From a358e264a541fd0fac7afc1ebf4401ee0cede308 Mon Sep 17 00:00:00 2001 From: Wendy Tang Date: Wed, 23 Jul 2025 13:08:17 -0700 Subject: [PATCH] feat: subagent independent extension manager (#3596) --- crates/goose/src/agents/agent.rs | 4 +- crates/goose/src/agents/subagent.rs | 45 ++++++++++++++----- .../goose/src/agents/subagent_task_config.rs | 12 +---- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs index c0e83fb8..0742ba33 100644 --- a/crates/goose/src/agents/agent.rs +++ b/crates/goose/src/agents/agent.rs @@ -344,9 +344,7 @@ impl Agent { let provider = self.provider().await.ok(); let mcp_tx = self.mcp_tx.lock().await.clone(); - let task_config = - TaskConfig::new(provider, Some(Arc::clone(&self.extension_manager)), mcp_tx); - + let task_config = TaskConfig::new(provider, mcp_tx); subagent_execute_task_tool::run_tasks( tool_call.arguments.clone(), task_config, diff --git a/crates/goose/src/agents/subagent.rs b/crates/goose/src/agents/subagent.rs index 0992d47f..d4150f2f 100644 --- a/crates/goose/src/agents/subagent.rs +++ b/crates/goose/src/agents/subagent.rs @@ -1,5 +1,7 @@ use crate::{ - agents::{Agent, TaskConfig}, + agents::extension::ExtensionConfig, + agents::{extension_manager::ExtensionManager, Agent, TaskConfig}, + config::ExtensionConfigManager, message::{Message, MessageContent, ToolRequest}, prompt_template::render_global_file, providers::errors::ProviderError, @@ -43,6 +45,7 @@ pub struct SubAgent { pub config: TaskConfig, pub turn_count: Arc>, pub created_at: DateTime, + pub extension_manager: Arc>, } impl SubAgent { @@ -53,6 +56,29 @@ impl SubAgent { ) -> Result<(Arc, tokio::task::JoinHandle<()>), anyhow::Error> { debug!("Creating new subagent with id: {}", task_config.id); + // Create a new extension manager for this subagent + let mut extension_manager = ExtensionManager::new(); + + // Add extensions based on task_type: + // 1. If executing dynamic task (task_type = 'text_instruction'), default to using all enabled extensions + // 2. (TODO) If executing a sub-recipe task, only use recipe extensions + + // Get all enabled extensions from config + let enabled_extensions = ExtensionConfigManager::get_all() + .unwrap_or_default() + .into_iter() + .filter(|ext| ext.enabled) + .map(|ext| ext.config) + .collect::>(); + + // Add enabled extensions to the subagent's extension manager + for extension in enabled_extensions { + if let Err(e) = extension_manager.add_extension(extension).await { + debug!("Failed to add extension to subagent: {}", e); + // Continue with other extensions even if one fails + } + } + let subagent = Arc::new(SubAgent { id: task_config.id.clone(), conversation: Arc::new(Mutex::new(Vec::new())), @@ -60,6 +86,7 @@ impl SubAgent { config: task_config, turn_count: Arc::new(Mutex::new(0)), created_at: Utc::now(), + extension_manager: Arc::new(RwLock::new(extension_manager)), }); // Send initial MCP notification @@ -169,19 +196,13 @@ impl SubAgent { self.send_mcp_notification("message_processing", &format!("Processing: {}", message)) .await; - // Get provider and extension manager from task config + // Get provider from task config let provider = self .config .provider .as_ref() .ok_or_else(|| anyhow!("No provider configured for subagent"))?; - let extension_manager = self - .config - .extension_manager - .as_ref() - .ok_or_else(|| anyhow!("No extension manager configured for subagent"))?; - // Check if we've exceeded max turns { let turn_count = *self.turn_count.lock().await; @@ -220,8 +241,9 @@ impl SubAgent { // Get the current conversation for context let mut messages = self.get_conversation().await; - // Get tools based on whether we're using a recipe or inheriting from parent - let tools: Vec = extension_manager + // Get tools from the subagent's own extension manager + let tools: Vec = self + .extension_manager .read() .await .get_prefixed_tools(None) @@ -292,7 +314,8 @@ impl SubAgent { .await; // Handle platform tools or dispatch to extension manager - let tool_result = match extension_manager + let tool_result = match self + .extension_manager .read() .await .dispatch_tool_call(tool_call.clone()) diff --git a/crates/goose/src/agents/subagent_task_config.rs b/crates/goose/src/agents/subagent_task_config.rs index 282bc7a7..81bcd999 100644 --- a/crates/goose/src/agents/subagent_task_config.rs +++ b/crates/goose/src/agents/subagent_task_config.rs @@ -1,9 +1,8 @@ -use crate::agents::extension_manager::ExtensionManager; use crate::providers::base::Provider; use rmcp::model::JsonRpcMessage; use std::fmt; use std::sync::Arc; -use tokio::sync::{mpsc, RwLock}; +use tokio::sync::mpsc; use uuid::Uuid; /// Configuration for task execution with all necessary dependencies @@ -11,7 +10,6 @@ use uuid::Uuid; pub struct TaskConfig { pub id: String, pub provider: Option>, - pub extension_manager: Option>>, pub mcp_tx: mpsc::Sender, pub max_turns: Option, } @@ -21,7 +19,6 @@ impl fmt::Debug for TaskConfig { f.debug_struct("TaskConfig") .field("id", &self.id) .field("provider", &"") - .field("extension_manager", &"") .field("max_turns", &self.max_turns) .finish() } @@ -29,15 +26,10 @@ impl fmt::Debug for TaskConfig { impl TaskConfig { /// Create a new TaskConfig with all required dependencies - pub fn new( - provider: Option>, - extension_manager: Option>>, - mcp_tx: mpsc::Sender, - ) -> Self { + pub fn new(provider: Option>, mcp_tx: mpsc::Sender) -> Self { Self { id: Uuid::new_v4().to_string(), provider, - extension_manager, mcp_tx, max_turns: Some(10), }