diff --git a/crates/goose/src/agents/extension_manager.rs b/crates/goose/src/agents/extension_manager.rs index 4736c92a31..e169506cce 100644 --- a/crates/goose/src/agents/extension_manager.rs +++ b/crates/goose/src/agents/extension_manager.rs @@ -26,6 +26,7 @@ use tokio_stream::wrappers::ReceiverStream; use tokio_util::sync::CancellationToken; use tracing::{error, warn}; +use super::code_execution_extension::EXTENSION_NAME as CODE_EXECUTION_NAME; use super::extension::{ ExtensionConfig, ExtensionError, ExtensionInfo, ExtensionResult, PlatformExtensionContext, ToolInfo, PLATFORM_EXTENSIONS, @@ -598,12 +599,25 @@ impl ExtensionManager { .insert(name, Extension::new(config, client, info, temp_dir)); } - /// Get extensions info + /// Get extensions info for building the system prompt. + /// When code_execution is enabled, it routes tools from other extensions through its + /// execute_code interface, so those extensions should not appear as separate sections + /// in the system prompt (their instructions would be irrelevant/confusing). pub async fn get_extensions_info(&self) -> Vec { - self.extensions - .lock() - .await + let extensions = self.extensions.lock().await; + let code_exec_enabled = extensions.contains_key(CODE_EXECUTION_NAME); + + extensions .iter() + .filter(|(name, _)| { + if code_exec_enabled { + // When code_execution is enabled, only show code_execution itself + // Other extensions' tools are accessed via code_execution's execute_code + name.as_str() == CODE_EXECUTION_NAME + } else { + true + } + }) .map(|(name, ext)| { ExtensionInfo::new( name, diff --git a/crates/goose/src/agents/prompt_manager.rs b/crates/goose/src/agents/prompt_manager.rs index af31418acd..b1a17e9d67 100644 --- a/crates/goose/src/agents/prompt_manager.rs +++ b/crates/goose/src/agents/prompt_manager.rs @@ -44,6 +44,7 @@ struct SystemPromptContext { enable_subagents: bool, max_extensions: usize, max_tools: usize, + code_execution_mode: bool, } pub struct SystemPromptBuilder<'a, M> { @@ -138,6 +139,11 @@ impl<'a> SystemPromptBuilder<'a, PromptManager> { }) .collect(); + // Detect code_execution mode: when enabled, only code_execution extension is passed + let code_execution_mode = sanitized_extensions_info + .iter() + .any(|ext| ext.name == "code_execution"); + let config = Config::global(); let goose_mode = config.get_goose_mode().unwrap_or(GooseMode::Auto); @@ -155,6 +161,7 @@ impl<'a> SystemPromptBuilder<'a, PromptManager> { enable_subagents: should_enabled_subagents(self.model_name.as_str()), max_extensions: MAX_EXTENSIONS, max_tools: MAX_TOOLS, + code_execution_mode, }; let base_prompt = if let Some(override_prompt) = &self.manager.system_prompt_override { diff --git a/crates/goose/src/prompts/system.md b/crates/goose/src/prompts/system.md index 795934e235..e9743240da 100644 --- a/crates/goose/src/prompts/system.md +++ b/crates/goose/src/prompts/system.md @@ -5,6 +5,7 @@ goose uses LLM providers with tool calling capability. You can be used with diff claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc). These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10 months prior to the current date. +{% if not code_execution_mode %} # Extensions @@ -40,6 +41,7 @@ and platform__list_resources on this extension. {% else %} No extensions are defined. You should let the user know that they should add extensions. {% endif %} +{% endif %} {% if extension_tool_limits is defined %} {% with (extension_count, tool_count) = extension_tool_limits %}