diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..ec54f3f3a097 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +crates/goose/src/agents/snapshots/*.snap linguist-language=Text diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs index fdcb2b2eccd7..6d2978e25159 100644 --- a/crates/goose/src/agents/agent.rs +++ b/crates/goose/src/agents/agent.rs @@ -1341,6 +1341,8 @@ impl Agent { let extensions_info = self.extension_manager.get_extensions_info().await; tracing::debug!("Retrieved {} extensions info", extensions_info.len()); + let (extension_count, tool_count) = + self.extension_manager.get_extension_and_tool_counts().await; // Get model name from provider let provider = self.provider().await.map_err(|e| { @@ -1352,15 +1354,12 @@ impl Agent { tracing::debug!("Using model: {}", model_name); let prompt_manager = self.prompt_manager.lock().await; - let system_prompt = prompt_manager.build_system_prompt( - extensions_info, - self.frontend_instructions.lock().await.clone(), - self.extension_manager - .suggest_disable_extensions_prompt() - .await, - model_name, - false, - ); + let system_prompt = prompt_manager + .builder(model_name) + .with_extensions(extensions_info.into_iter()) + .with_frontend_instructions(self.frontend_instructions.lock().await.clone()) + .with_extension_and_tool_counts(extension_count, tool_count) + .build(); let recipe_prompt = prompt_manager.get_recipe_prompt().await; let tools = self @@ -1584,8 +1583,7 @@ mod tests { ); let prompt_manager = agent.prompt_manager.lock().await; - let system_prompt = - prompt_manager.build_system_prompt(vec![], None, Value::Null, "gpt-4o", false); + let system_prompt = prompt_manager.builder("gpt-4o").build(); let final_output_tool_ref = agent.final_output_tool.lock().await; let final_output_tool_system_prompt = diff --git a/crates/goose/src/agents/extension_manager.rs b/crates/goose/src/agents/extension_manager.rs index 6d5bf085bb7f..7e81552f6c2c 100644 --- a/crates/goose/src/agents/extension_manager.rs +++ b/crates/goose/src/agents/extension_manager.rs @@ -549,7 +549,7 @@ impl ExtensionManager { Ok(()) } - pub async fn suggest_disable_extensions_prompt(&self) -> Value { + pub async fn get_extension_and_tool_counts(&self) -> (usize, usize) { let enabled_extensions_count = self.extensions.lock().await.len(); let total_tools = self @@ -558,27 +558,7 @@ impl ExtensionManager { .map(|tools| tools.len()) .unwrap_or(0); - // Check if either condition is met - const MIN_EXTENSIONS: usize = 5; - const MIN_TOOLS: usize = 50; - - if enabled_extensions_count > MIN_EXTENSIONS || total_tools > MIN_TOOLS { - Value::String(format!( - "The user currently has enabled {} extensions with a total of {} tools. \ - Since this exceeds the recommended limits ({} extensions or {} tools), \ - you should ask the user if they would like to disable some extensions for this session.\n\n\ - Use the search_available_extensions tool to find extensions available to disable. \ - You should only disable extensions found from the search_available_extensions tool. \ - List all the extensions available to disable in the response. \ - Explain that minimizing extensions helps with the recall of the correct tools to use.", - enabled_extensions_count, - total_tools, - MIN_EXTENSIONS, - MIN_TOOLS, - )) - } else { - Value::String(String::new()) // Empty string if under limits - } + (enabled_extensions_count, total_tools) } pub async fn list_extensions(&self) -> ExtensionResult> { diff --git a/crates/goose/src/agents/prompt_manager.rs b/crates/goose/src/agents/prompt_manager.rs index c72268bb83a4..133624d12ad9 100644 --- a/crates/goose/src/agents/prompt_manager.rs +++ b/crates/goose/src/agents/prompt_manager.rs @@ -1,7 +1,9 @@ #[cfg(test)] use chrono::DateTime; use chrono::Utc; +use serde::Serialize; use serde_json::Value; +use std::borrow::Cow; use std::collections::HashMap; use crate::agents::extension::ExtensionInfo; @@ -9,6 +11,9 @@ use crate::agents::recipe_tools::dynamic_task_tools::should_enabled_subagents; use crate::agents::router_tools::llm_search_tool_prompt; use crate::{config::Config, prompt_template, utils::sanitize_unicode_tags}; +const MAX_EXTENSIONS: usize = 5; +const MAX_TOOLS: usize = 50; + pub struct PromptManager { system_prompt_override: Option, system_prompt_extras: Vec, @@ -21,50 +26,68 @@ impl Default for PromptManager { } } -impl PromptManager { - pub fn new() -> Self { - PromptManager { - system_prompt_override: None, - system_prompt_extras: Vec::new(), - // Use the fixed current date time so that prompt cache can be used. - // Filtering to an hour to balance user time accuracy and multi session prompt cache hits. - current_date_timestamp: Utc::now().format("%Y-%m-%d %H:00").to_string(), - } +#[derive(Serialize)] +struct SystemPromptContext { + extensions: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + tool_selection_strategy: Option, + current_date_time: String, + #[serde(skip_serializing_if = "Option::is_none")] + extension_tool_limits: Option<(usize, usize)>, + goose_mode: String, + is_autonomous: bool, + enable_subagents: bool, + max_extensions: usize, + max_tools: usize, +} + +pub struct SystemPromptBuilder<'a, M> { + model_name: String, + manager: &'a M, + + extensions_info: Vec, + frontend_instructions: Option, + extension_tool_count: Option<(usize, usize)>, + router_enabled: bool, +} + +impl<'a> SystemPromptBuilder<'a, PromptManager> { + pub fn with_extension(mut self, extension: ExtensionInfo) -> Self { + self.extensions_info.push(extension); + self } - #[cfg(test)] - pub fn with_timestamp(dt: DateTime) -> Self { - PromptManager { - system_prompt_override: None, - system_prompt_extras: Vec::new(), - // Use the fixed current date time so that prompt cache can be used. - current_date_timestamp: dt.format("%Y-%m-%d %H:%M:%S").to_string(), + pub fn with_extensions(mut self, extensions: impl Iterator) -> Self { + for extension in extensions { + self.extensions_info.push(extension); } + self } - /// Add an additional instruction to the system prompt - pub fn add_system_prompt_extra(&mut self, instruction: String) { - self.system_prompt_extras.push(instruction); + pub fn with_frontend_instructions(mut self, frontend_instructions: Option) -> Self { + self.frontend_instructions = frontend_instructions; + self } - /// Override the system prompt with custom text - pub fn set_system_prompt_override(&mut self, template: String) { - self.system_prompt_override = Some(template); + pub fn with_extension_and_tool_counts( + mut self, + extension_count: usize, + tool_count: usize, + ) -> Self { + self.extension_tool_count = Some((extension_count, tool_count)); + self } - pub fn build_system_prompt( - &self, - extensions_info: Vec, - frontend_instructions: Option, - suggest_disable_extensions_prompt: Value, - model_name: &str, - router_enabled: bool, - ) -> String { - let mut context: HashMap<&str, Value> = HashMap::new(); - let mut extensions_info = extensions_info.clone(); + pub fn with_router_enabled(mut self, enabled: bool) -> Self { + self.router_enabled = enabled; + self + } + + pub fn build(self) -> String { + let mut extensions_info = self.extensions_info; // Add frontend instructions to extensions_info to simplify json rendering - if let Some(frontend_instructions) = frontend_instructions { + if let Some(frontend_instructions) = self.frontend_instructions { extensions_info.push(ExtensionInfo::new( "frontend", &frontend_instructions, @@ -82,38 +105,28 @@ impl PromptManager { }) .collect(); - context.insert( - "extensions", - serde_json::to_value(sanitized_extensions_info).unwrap(), - ); - - if router_enabled { - context.insert( - "tool_selection_strategy", - Value::String(llm_search_tool_prompt()), - ); - } - - context.insert( - "current_date_time", - Value::String(self.current_date_timestamp.clone()), - ); - - // Add the suggestion about disabling extensions if flag is true - context.insert( - "suggest_disable", - Value::String(suggest_disable_extensions_prompt.to_string()), - ); - let config = Config::global(); - let goose_mode = config.get_param("GOOSE_MODE").unwrap_or("auto".to_string()); - context.insert("goose_mode", Value::String(goose_mode.clone())); - context.insert( - "enable_subagents", - Value::Bool(should_enabled_subagents(model_name)), - ); - - let base_prompt = if let Some(override_prompt) = &self.system_prompt_override { + let goose_mode = config + .get_param("GOOSE_MODE") + .unwrap_or_else(|_| Cow::from("auto")); + + let extension_tool_limits = self + .extension_tool_count + .filter(|(extensions, tools)| *extensions > MAX_EXTENSIONS || *tools > MAX_TOOLS); + + let context = SystemPromptContext { + extensions: sanitized_extensions_info, + tool_selection_strategy: self.router_enabled.then(llm_search_tool_prompt), + current_date_time: self.manager.current_date_timestamp.clone(), + extension_tool_limits, + goose_mode: goose_mode.to_string(), + is_autonomous: goose_mode == "auto", + enable_subagents: should_enabled_subagents(self.model_name.as_str()), + max_extensions: MAX_EXTENSIONS, + max_tools: MAX_TOOLS, + }; + + let base_prompt = if let Some(override_prompt) = &self.manager.system_prompt_override { let sanitized_override_prompt = sanitize_unicode_tags(override_prompt); prompt_template::render_inline_once(&sanitized_override_prompt, &context) } else { @@ -123,7 +136,7 @@ impl PromptManager { "You are a general-purpose AI agent called goose, created by Block".to_string() }); - let mut system_prompt_extras = self.system_prompt_extras.clone(); + let mut system_prompt_extras = self.manager.system_prompt_extras.clone(); if goose_mode == "chat" { system_prompt_extras.push( "Right now you are in the chat only mode, no access to any tool use and system." @@ -146,6 +159,49 @@ impl PromptManager { ) } } +} + +impl PromptManager { + pub fn new() -> Self { + PromptManager { + system_prompt_override: None, + system_prompt_extras: Vec::new(), + // Use the fixed current date time so that prompt cache can be used. + // Filtering to an hour to balance user time accuracy and multi session prompt cache hits. + current_date_timestamp: Utc::now().format("%Y-%m-%d %H:00").to_string(), + } + } + + #[cfg(test)] + pub fn with_timestamp(dt: DateTime) -> Self { + PromptManager { + system_prompt_override: None, + system_prompt_extras: Vec::new(), + current_date_timestamp: dt.format("%Y-%m-%d %H:%M:%S").to_string(), + } + } + + /// Add an additional instruction to the system prompt + pub fn add_system_prompt_extra(&mut self, instruction: String) { + self.system_prompt_extras.push(instruction); + } + + /// Override the system prompt with custom text + pub fn set_system_prompt_override(&mut self, template: String) { + self.system_prompt_override = Some(template); + } + + pub fn builder<'a>(&'a self, model_name: &str) -> SystemPromptBuilder<'a, Self> { + SystemPromptBuilder { + model_name: model_name.to_string(), + manager: self, + + extensions_info: vec![], + frontend_instructions: None, + extension_tool_count: None, + router_enabled: false, + } + } pub async fn get_recipe_prompt(&self) -> String { let context: HashMap<&str, Value> = HashMap::new(); @@ -166,13 +222,7 @@ mod tests { let malicious_override = "System prompt\u{E0041}\u{E0042}\u{E0043}with hidden text"; manager.set_system_prompt_override(malicious_override.to_string()); - let result = manager.build_system_prompt( - vec![], - None, - Value::String("".to_string()), - "gpt-4o", - false, - ); + let result = manager.builder("gpt-4o").build(); assert!(!result.contains('\u{E0041}')); assert!(!result.contains('\u{E0042}')); @@ -187,13 +237,7 @@ mod tests { let malicious_extra = "Extra instruction\u{E0041}\u{E0042}\u{E0043}hidden"; manager.add_system_prompt_extra(malicious_extra.to_string()); - let result = manager.build_system_prompt( - vec![], - None, - Value::String("".to_string()), - "gpt-4o", - false, - ); + let result = manager.builder("gpt-4o").build(); assert!(!result.contains('\u{E0041}')); assert!(!result.contains('\u{E0042}')); @@ -209,13 +253,7 @@ mod tests { manager.add_system_prompt_extra("Second\u{E0042}instruction".to_string()); manager.add_system_prompt_extra("Third\u{E0043}instruction".to_string()); - let result = manager.build_system_prompt( - vec![], - None, - Value::String("".to_string()), - "gpt-4o", - false, - ); + let result = manager.builder("gpt-4o").build(); assert!(!result.contains('\u{E0041}')); assert!(!result.contains('\u{E0042}')); @@ -231,13 +269,7 @@ mod tests { let legitimate_unicode = "Instruction with δΈ–η•Œ and 🌍 emojis"; manager.add_system_prompt_extra(legitimate_unicode.to_string()); - let result = manager.build_system_prompt( - vec![], - None, - Value::String("".to_string()), - "gpt-4o", - false, - ); + let result = manager.builder("gpt-4o").build(); assert!(result.contains("δΈ–η•Œ")); assert!(result.contains("🌍")); @@ -254,13 +286,10 @@ mod tests { false, ); - let result = manager.build_system_prompt( - vec![malicious_extension_info], - None, - Value::String("".to_string()), - "gpt-4o", - false, - ); + let result = manager + .builder("gpt-4o") + .with_extension(malicious_extension_info) + .build(); assert!(!result.contains('\u{E0041}')); assert!(!result.contains('\u{E0042}')); @@ -273,13 +302,7 @@ mod tests { fn test_basic() { let manager = PromptManager::with_timestamp(DateTime::::from_timestamp(0, 0).unwrap()); - let system_prompt = manager.build_system_prompt( - vec![], - None, - Value::String("".to_string()), - "gpt-4o", - false, - ); + let system_prompt = manager.builder("gpt-4o").build(); assert_snapshot!(system_prompt) } @@ -288,17 +311,38 @@ mod tests { fn test_one_extension() { let manager = PromptManager::with_timestamp(DateTime::::from_timestamp(0, 0).unwrap()); - let system_prompt = manager.build_system_prompt( - vec![ExtensionInfo::new( + let system_prompt = manager + .builder("gpt-4o") + .with_extension(ExtensionInfo::new( "test", "how to use this extension", true, - )], - None, - Value::String("".to_string()), - "gpt-4o", - true, - ); + )) + .with_router_enabled(true) + .build(); + + assert_snapshot!(system_prompt) + } + + #[test] + fn test_typical_setup() { + let manager = PromptManager::with_timestamp(DateTime::::from_timestamp(0, 0).unwrap()); + + let system_prompt = manager + .builder("gpt-4o") + .with_extension(ExtensionInfo::new( + "extension_A", + "", + true, + )) + .with_extension(ExtensionInfo::new( + "extension_B", + "", + false, + )) + .with_router_enabled(true) + .with_extension_and_tool_counts(MAX_EXTENSIONS + 1, MAX_TOOLS + 1) + .build(); assert_snapshot!(system_prompt) } diff --git a/crates/goose/src/agents/reply_parts.rs b/crates/goose/src/agents/reply_parts.rs index bc443d83746a..7d54e60fb045 100644 --- a/crates/goose/src/agents/reply_parts.rs +++ b/crates/goose/src/agents/reply_parts.rs @@ -67,6 +67,8 @@ impl Agent { // Prepare system prompt let extensions_info = self.extension_manager.get_extensions_info().await; + let (extension_count, tool_count) = + self.extension_manager.get_extension_and_tool_counts().await; // Get model name from provider let provider = self.provider().await?; @@ -74,15 +76,13 @@ impl Agent { let model_name = &model_config.model_name; let prompt_manager = self.prompt_manager.lock().await; - let mut system_prompt = prompt_manager.build_system_prompt( - extensions_info, - self.frontend_instructions.lock().await.clone(), - self.extension_manager - .suggest_disable_extensions_prompt() - .await, - model_name, - router_enabled, - ); + let mut system_prompt = prompt_manager + .builder(model_name) + .with_extensions(extensions_info.into_iter()) + .with_frontend_instructions(self.frontend_instructions.lock().await.clone()) + .with_extension_and_tool_counts(extension_count, tool_count) + .with_router_enabled(router_enabled) + .build(); // Handle toolshim if enabled let mut toolshim_tools = vec![]; diff --git a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__basic.snap b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__basic.snap index 768a225dcb54..4acf9b188401 100644 --- a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__basic.snap +++ b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__basic.snap @@ -25,20 +25,11 @@ extension_name. You should only enable extensions found from the search_availabl If Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load new ones. - No extensions are defined. You should let the user know that they should add extensions. -# Suggestion - -"" - - - - - # sub agents Execute self contained tasks where step-by-step visibility is not important through subagents. @@ -50,7 +41,6 @@ Execute self contained tasks where step-by-step visibility is not important thro - Provide all needed context β€” subagents cannot see your context - Use extension filters to limit resource access - Use return_last_only when only a summary or simple answer is required β€” inform subagent of this choice. - # Response Guidelines diff --git a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__one_extension.snap b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__one_extension.snap index 7bf2feff30b6..bb5479e3e553 100644 --- a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__one_extension.snap +++ b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__one_extension.snap @@ -25,33 +25,20 @@ extension_name. You should only enable extensions found from the search_availabl If Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load new ones. - Because you dynamically load extensions, your conversation history may refer to interactions with extensions that are not currently active. The currently active extensions are below. Each of these extensions provides tools that are in your tool specification. - ## test - test supports resources, you can use platform__read_resource, and platform__list_resources on this extension. - ### Instructions how to use this extension - - - - -# Suggestion - -"" - - # LLM Tool Selection Instructions Important: the user has opted to dynamically enable tools, so although an extension could be enabled, \ please invoke the llm search tool to actually retrieve the most relevant tools to use according to the user's messages. @@ -67,7 +54,6 @@ how to use this extension - list_resources - # sub agents Execute self contained tasks where step-by-step visibility is not important through subagents. @@ -79,7 +65,6 @@ Execute self contained tasks where step-by-step visibility is not important thro - Provide all needed context β€” subagents cannot see your context - Use extension filters to limit resource access - Use return_last_only when only a summary or simple answer is required β€” inform subagent of this choice. - # Response Guidelines diff --git a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__typical_setup.snap b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__typical_setup.snap new file mode 100644 index 000000000000..c28476fe5c38 --- /dev/null +++ b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__typical_setup.snap @@ -0,0 +1,93 @@ +--- +source: crates/goose/src/agents/prompt_manager.rs +expression: system_prompt +--- +You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal. +goose is being developed as an open-source software project. + +The current date is 1970-01-01 00:00:00. + +goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o, +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. + +# Extensions + +Extensions allow other applications to provide context to goose. Extensions connect goose to different data sources and +tools. +You are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level +problems using the tools in these extensions, and can interact with multiple at once. + +If the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional +extensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the +extension_name. You should only enable extensions found from the search_available_extensions tool. +If Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load +new ones. + +Because you dynamically load extensions, your conversation history may refer +to interactions with extensions that are not currently active. The currently +active extensions are below. Each of these extensions provides tools that are +in your tool specification. + + +## extension_A + +extension_A supports resources, you can use platform__read_resource, +and platform__list_resources on this extension. +### Instructions + +## extension_B + +### Instructions + + +# Suggestion + +The user currently has enabled 6 extensions with a total of 51 tools. +Since this exceeds the recommended limits (5 extensions or 50 tools), +you should ask the user if they would like to disable some extensions for this session. + +Use the search_available_extensions tool to find extensions available to disable. +You should only disable extensions found from the search_available_extensions tool. +List all the extensions available to disable in the response. +Explain that minimizing extensions helps with the recall of the correct tools to use. + +# LLM Tool Selection Instructions + Important: the user has opted to dynamically enable tools, so although an extension could be enabled, \ + please invoke the llm search tool to actually retrieve the most relevant tools to use according to the user's messages. + For example, if the user has 3 extensions enabled, but they are asking for a tool to read a pdf file, \ + you would invoke the llm_search tool to find the most relevant read pdf tool. + By dynamically enabling tools, you (goose) as the agent save context window space and allow the user to dynamically retrieve the most relevant tools. + Be sure to format a query packed with relevant keywords to search for the most relevant tools. + In addition to the extension names available to you, you also have platform extension tools available to you. + The platform extension contains the following tools: + - search_available_extensions + - manage_extensions + - read_resource + - list_resources + + +# sub agents + +Execute self contained tasks where step-by-step visibility is not important through subagents. + +- Delegate via `dynamic_task__create_task` for: result-only operations, parallelizable work, multi-part requests, + verification, exploration +- Parallel subagents for multiple operations, single subagents for independent work +- Explore solutions in parallel β€” launch parallel subagents with different approaches (if non-interfering) +- Provide all needed context β€” subagents cannot see your context +- Use extension filters to limit resource access +- Use return_last_only when only a summary or simple answer is required β€” inform subagent of this choice. + +# Response Guidelines + +- Use Markdown formatting for all responses. +- Follow best practices for Markdown, including: + - Using headers for organization. + - Bullet points for lists. + - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic + links using angle brackets (e.g., ). +- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the + language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting. +- Ensure clarity, conciseness, and proper formatting to enhance readability and usability. diff --git a/crates/goose/src/prompt_template.rs b/crates/goose/src/prompt_template.rs index bf7bd99660b0..1bdafebb5bce 100644 --- a/crates/goose/src/prompt_template.rs +++ b/crates/goose/src/prompt_template.rs @@ -16,6 +16,8 @@ static CORE_PROMPTS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/src/prompts"); /// - *Not* used for extension prompts (which are ephemeral). static GLOBAL_ENV: Lazy>>> = Lazy::new(|| { let mut env = Environment::new(); + env.set_trim_blocks(true); + env.set_lstrip_blocks(true); // Pre-load all core templates from the embedded dir. for file in CORE_PROMPTS_DIR.files() { diff --git a/crates/goose/src/prompts/system.md b/crates/goose/src/prompts/system.md index 5a0c55cf947d..1501f28623b7 100644 --- a/crates/goose/src/prompts/system.md +++ b/crates/goose/src/prompts/system.md @@ -43,11 +43,19 @@ and platform__list_resources on this extension. No extensions are defined. You should let the user know that they should add extensions. {% endif %} -{% if suggest_disable is defined %} - +{% if extension_tool_limits is defined %} +{% with (extension_count, tool_count) = extension_tool_limits %} # Suggestion -{{suggest_disable}} +The user currently has enabled {{extension_count}} extensions with a total of {{tool_count}} tools. +Since this exceeds the recommended limits ({{max_extensions}} extensions or {{max_tools}} tools), +you should ask the user if they would like to disable some extensions for this session. + +Use the search_available_extensions tool to find extensions available to disable. +You should only disable extensions found from the search_available_extensions tool. +List all the extensions available to disable in the response. +Explain that minimizing extensions helps with the recall of the correct tools to use. +{% endwith %} {% endif %} {{tool_selection_strategy}}