Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4ca12c7
Persist extension config so we can resume recipe sessions w/ extensions
wpfleger96 Aug 25, 2025
fb06fb8
Clarify comment and consolidate redundant print statements
wpfleger96 Aug 26, 2025
8013c7f
Extract get_enabled() method for ExtensionConfigManager type to avoid…
wpfleger96 Aug 26, 2025
7d56e05
Build extension metadata object and persist in the same write as the …
wpfleger96 Aug 29, 2025
3240ed9
Simplify ExtensionConfigManager struct into free functions
wpfleger96 Aug 29, 2025
086cb88
Simplify pattern matching per PR feedback
wpfleger96 Aug 29, 2025
988a450
Merge remote-tracking branch 'upstream/main' into wpfleger/restore-ex…
wpfleger96 Aug 29, 2025
e2ccdf5
Cruft cleanup
wpfleger96 Aug 29, 2025
9bacd60
Merge branch 'main' into wpfleger/restore-extensions-on-resume
wpfleger96 Sep 30, 2025
f24eb00
Cleanup after addressing merge conflicts
wpfleger96 Oct 1, 2025
973dd98
Botched merge conflict resolution, this shouldn't have been recreated
wpfleger96 Oct 1, 2025
37bda81
Unnecessary tests
wpfleger96 Oct 1, 2025
5a52420
Extension persistence needs to happen after dynamic extensions are lo…
wpfleger96 Oct 1, 2025
9aa981f
Remove unnecessary comments
wpfleger96 Oct 2, 2025
5e19a07
Simplify save_extension_state function and remove unnecessary Option/…
wpfleger96 Oct 2, 2025
84347c3
Remove trivial LLM tests
wpfleger96 Oct 2, 2025
6c40fac
Check if extensions were uninstalled in between exiting and resuming …
wpfleger96 Oct 2, 2025
3dc1f3b
Merge branch 'main' into wpfleger/restore-extensions-on-resume
wpfleger96 Oct 2, 2025
18b35c8
nit picky cargo fmt
wpfleger96 Oct 2, 2025
194dc2b
Merge branch 'main' into wpfleger/restore-extensions-on-resume
wpfleger96 Oct 3, 2025
86beccd
Merge branch 'main' into wpfleger/restore-extensions-on-resume
wpfleger96 Oct 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions crates/goose-cli/src/commands/acp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use agent_client_protocol::{
};
use anyhow::Result;
use goose::agents::Agent;
use goose::config::{Config, ExtensionConfigManager};
use goose::config::{get_all_extensions, Config};
use goose::conversation::message::{Message, MessageContent};
use goose::conversation::Conversation;
use goose::providers::create;
Expand Down Expand Up @@ -124,8 +124,7 @@ impl GooseAcpAgent {
agent.update_provider(provider.clone()).await?;

// Load and add extensions just like the normal CLI
let extensions_to_run: Vec<_> = ExtensionConfigManager::get_all()
.map_err(|e| anyhow::anyhow!("Failed to load extensions: {}", e))?
let extensions_to_run: Vec<_> = get_all_extensions()
.into_iter()
.filter(|ext| ext.enabled)
.map(|ext| ext.config)
Expand Down
78 changes: 34 additions & 44 deletions crates/goose-cli/src/commands/configure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ use goose::agents::platform_tools::{
use goose::agents::Agent;
use goose::agents::{extension::Envs, ExtensionConfig};
use goose::config::custom_providers::CustomProviderConfig;
use goose::config::extensions::name_to_key;
use goose::config::permission::PermissionLevel;
use goose::config::{
Config, ConfigError, ExperimentManager, ExtensionConfigManager, ExtensionEntry,
PermissionManager,
use goose::config::extensions::{
get_all_extension_names, get_all_extensions, get_enabled_extensions, get_extension_by_name,
name_to_key, remove_extension, set_extension, set_extension_enabled,
};
use goose::config::permission::PermissionLevel;
use goose::config::{Config, ConfigError, ExperimentManager, ExtensionEntry, PermissionManager};
use goose::conversation::message::Message;
use goose::model::ModelConfig;
use goose::providers::{create, providers};
Expand Down Expand Up @@ -105,10 +105,10 @@ pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
);
// Since we are setting up for the first time, we'll also enable the developer system
// This operation is best-effort and errors are ignored
ExtensionConfigManager::set(ExtensionEntry {
set_extension(ExtensionEntry {
enabled: true,
config: ExtensionConfig::default(),
})?;
});
}
Ok(false) => {
let _ = config.clear();
Expand Down Expand Up @@ -641,7 +641,7 @@ pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> {
/// Configure extensions that can be used with goose
/// Dialog for toggling which extensions are enabled/disabled
pub fn toggle_extensions_dialog() -> Result<(), Box<dyn Error>> {
let extensions = ExtensionConfigManager::get_all()?;
let extensions = get_all_extensions();

if extensions.is_empty() {
cliclack::outro(
Expand Down Expand Up @@ -682,10 +682,10 @@ pub fn toggle_extensions_dialog() -> Result<(), Box<dyn Error>> {

// Update enabled status for each extension
for name in extension_status.iter().map(|(name, _)| name) {
ExtensionConfigManager::set_enabled(
set_extension_enabled(
&name_to_key(name),
selected.iter().any(|s| s.as_str() == name),
)?;
);
}

cliclack::outro("Extension settings updated successfully")?;
Expand Down Expand Up @@ -768,7 +768,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
.map(|(_, name, desc)| (name.to_string(), desc.to_string()))
.unwrap_or_else(|| (extension.clone(), extension.clone()));

ExtensionConfigManager::set(ExtensionEntry {
set_extension(ExtensionEntry {
enabled: true,
config: ExtensionConfig::Builtin {
name: extension.clone(),
Expand All @@ -778,12 +778,12 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
description,
available_tools: Vec::new(),
},
})?;
});

cliclack::outro(format!("Enabled {} extension", style(extension).green()))?;
}
"stdio" => {
let extensions = ExtensionConfigManager::get_all_names()?;
let extensions = get_all_extension_names();
let name: String = cliclack::input("What would you like to call this extension?")
.placeholder("my-extension")
.validate(move |input: &String| {
Expand Down Expand Up @@ -866,7 +866,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
}
}

ExtensionConfigManager::set(ExtensionEntry {
set_extension(ExtensionEntry {
enabled: true,
config: ExtensionConfig::Stdio {
name: name.clone(),
Expand All @@ -879,12 +879,12 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
bundled: None,
available_tools: Vec::new(),
},
})?;
});

cliclack::outro(format!("Added {} extension", style(name).green()))?;
}
"sse" => {
let extensions = ExtensionConfigManager::get_all_names()?;
let extensions = get_all_extension_names();
let name: String = cliclack::input("What would you like to call this extension?")
.placeholder("my-remote-extension")
.validate(move |input: &String| {
Expand Down Expand Up @@ -962,7 +962,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
}
}

ExtensionConfigManager::set(ExtensionEntry {
set_extension(ExtensionEntry {
enabled: true,
config: ExtensionConfig::Sse {
name: name.clone(),
Expand All @@ -974,12 +974,12 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
bundled: None,
available_tools: Vec::new(),
},
})?;
});

cliclack::outro(format!("Added {} extension", style(name).green()))?;
}
"streamable_http" => {
let extensions = ExtensionConfigManager::get_all_names()?;
let extensions = get_all_extension_names();
let name: String = cliclack::input("What would you like to call this extension?")
.placeholder("my-remote-extension")
.validate(move |input: &String| {
Expand Down Expand Up @@ -1082,7 +1082,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
}
}

ExtensionConfigManager::set(ExtensionEntry {
set_extension(ExtensionEntry {
enabled: true,
config: ExtensionConfig::StreamableHttp {
name: name.clone(),
Expand All @@ -1095,7 +1095,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
bundled: None,
available_tools: Vec::new(),
},
})?;
});

cliclack::outro(format!("Added {} extension", style(name).green()))?;
}
Expand All @@ -1106,7 +1106,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
}

pub fn remove_extension_dialog() -> Result<(), Box<dyn Error>> {
let extensions = ExtensionConfigManager::get_all()?;
let extensions = get_all_extensions();

// Create a list of extension names and their enabled status
let mut extension_status: Vec<(String, bool)> = extensions
Expand Down Expand Up @@ -1151,7 +1151,7 @@ pub fn remove_extension_dialog() -> Result<(), Box<dyn Error>> {
.interact()?;

for name in selected {
ExtensionConfigManager::remove(&name_to_key(name))?;
remove_extension(&name_to_key(name));
let mut permission_manager = PermissionManager::default();
permission_manager.remove_extension(&name_to_key(name));
cliclack::outro(format!("Removed {} extension", style(name).green()))?;
Expand Down Expand Up @@ -1386,11 +1386,9 @@ pub fn toggle_experiments_dialog() -> Result<(), Box<dyn Error>> {
}

pub async fn configure_tool_permissions_dialog() -> Result<(), Box<dyn Error>> {
let mut extensions: Vec<String> = ExtensionConfigManager::get_all()
.unwrap_or_default()
let mut extensions: Vec<String> = get_enabled_extensions()
.into_iter()
.filter(|ext| ext.enabled)
.map(|ext| ext.config.name().clone())
.map(|ext| ext.name().clone())
.collect();
extensions.push("platform".to_string());

Expand Down Expand Up @@ -1423,7 +1421,7 @@ pub async fn configure_tool_permissions_dialog() -> Result<(), Box<dyn Error>> {
let agent = Agent::new();
let new_provider = create(&provider_name, model_config)?;
agent.update_provider(new_provider).await?;
if let Ok(Some(config)) = ExtensionConfigManager::get_config_by_name(&selected_extension_name) {
if let Some(config) = get_extension_by_name(&selected_extension_name) {
agent
.add_extension(config.clone())
.await
Expand Down Expand Up @@ -1706,13 +1704,13 @@ pub async fn handle_openrouter_auth() -> Result<(), Box<dyn Error>> {
println!("✓ Configuration test passed!");

// Enable the developer extension by default if not already enabled
let entries = ExtensionConfigManager::get_all()?;
let entries = get_all_extensions();
let has_developer = entries
.iter()
.any(|e| e.config.name() == "developer" && e.enabled);

if !has_developer {
match ExtensionConfigManager::set(ExtensionEntry {
set_extension(ExtensionEntry {
enabled: true,
config: ExtensionConfig::Builtin {
name: "developer".to_string(),
Expand All @@ -1724,12 +1722,8 @@ pub async fn handle_openrouter_auth() -> Result<(), Box<dyn Error>> {
description: "Developer extension".to_string(),
available_tools: Vec::new(),
},
}) {
Ok(_) => println!("✓ Developer extension enabled"),
Err(e) => {
eprintln!("⚠️ Failed to enable developer extension: {}", e)
}
}
});
println!("✓ Developer extension enabled");
}

cliclack::outro("OpenRouter setup complete! You can now use goose.")?;
Expand Down Expand Up @@ -1809,13 +1803,13 @@ pub async fn handle_tetrate_auth() -> Result<(), Box<dyn Error>> {
println!("✓ Configuration test passed!");

// Enable the developer extension by default if not already enabled
let entries = ExtensionConfigManager::get_all()?;
let entries = get_all_extensions();
let has_developer = entries
.iter()
.any(|e| e.config.name() == "developer" && e.enabled);

if !has_developer {
match ExtensionConfigManager::set(ExtensionEntry {
set_extension(ExtensionEntry {
enabled: true,
config: ExtensionConfig::Builtin {
name: "developer".to_string(),
Expand All @@ -1827,12 +1821,8 @@ pub async fn handle_tetrate_auth() -> Result<(), Box<dyn Error>> {
description: "Developer extension".to_string(),
available_tools: Vec::new(),
},
}) {
Ok(_) => println!("✓ Developer extension enabled"),
Err(e) => {
eprintln!("⚠️ Failed to enable developer extension: {}", e)
}
}
});
println!("✓ Developer extension enabled");
}

cliclack::outro("Tetrate Agent Router Service setup complete! You can now use goose.")?;
Expand Down
14 changes: 4 additions & 10 deletions crates/goose-cli/src/commands/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,16 +164,10 @@ pub async fn handle_web(
agent.update_provider(provider).await?;

// Load and enable extensions from config
let extensions = goose::config::ExtensionConfigManager::get_all()?;
for ext_config in extensions {
if ext_config.enabled {
if let Err(e) = agent.add_extension(ext_config.config.clone()).await {
eprintln!(
"Warning: Failed to load extension {}: {}",
ext_config.config.name(),
e
);
}
let enabled_configs = goose::config::get_enabled_extensions();
for config in enabled_configs {
if let Err(e) = agent.add_extension(config.clone()).await {
eprintln!("Warning: Failed to load extension {}: {}", config.name(), e);
}
}

Expand Down
Loading
Loading