-
Notifications
You must be signed in to change notification settings - Fork 2.6k
refactor: subagents as platform extension (enables subagents in code mode) #6160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
/goose trying to make |
|
Summary: This PR refactors the subagent tool from a special case in ✅ Highlights
🟢 Suggestions
Overall: This is well-architected. The move to a platform extension is the right call for consistency and enabling code_execution integration. The implementation follows project patterns and has good defensive checks. Approve once you're happy with the working_dir behavior. Review generated by goose |
|
/goose |
|
Summary: This PR refactors the subagent tool from a special case in 🟢 Suggestions
✅ Highlights
Review generated by goose |
| } | ||
|
|
||
| #[async_trait] | ||
| impl McpClientTrait for SubagentClient { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a thought (not feedback on this PR)
We could probably benefit from an extension construct that isn't necessarily an MCP extension. We're making good use of the platform extension construct, but having them "pretend" to be MCP servers results in us needing some odd code like this.
| let mut result = client | ||
| .lock() | ||
| .await | ||
| .call_tool_deferred(&tool_name, arguments, cancellation_token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of a new function, how about changing this so that McpClientTrait::call_tool always returns a notification stream, with the default impl calling .subscribe()?
I'm also seeing that all of the platform extensions just return a dummy channel for subscribe, but ToolCallResult has an Option for the notifcations stream, so it would make more sense to have the trait fn itself return an Option
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a good idea, but I don't necessarily want to do it in this PR.
…t renamed to DeferredToolCall
|
/goose |
PR #6160: refactor: subagents as platform extension (enables subagents in code mode)Summary: This PR successfully refactors the subagent tool from special-case handling in ✅ Highlights
🟢 Suggestions
Review generated by goose |
|
I think I need to make goose reviewer even more critical |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR refactors subagent handling into a first-class platform extension and generalizes tool execution to support deferred results, enabling subagent delegation from both regular tool calls and code-execution mode.
Changes:
- Introduces
DeferredToolCalland wires it through the agent, extension manager, router, and final-output tooling so extension tools can return futures plus optional notification streams. - Converts the subagent functionality into a
subagentplatform extension (SubagentClient) usingPlatformExtensionContext(now carryingsub_recipes) and a simplifiedTaskConfig, and auto-enables it when recipes include sub-recipes. - Fixes
code_execution’sread_moduleparameter name tomodule_path, updates tests accordingly, and aligns extension configuration enabling withPlatformExtensionDef::default_enabled.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
crates/goose/tests/agent.rs |
Updates extension manager tests to pass sub_recipes in PlatformExtensionContext, ensuring the test agent mirrors the new context shape. |
crates/goose/src/execution/manager.rs |
Ensures session-scoped ExtensionManager context now includes sub_recipes: Some(agent.sub_recipes()) so platform extensions can see recipe sub-recipes. |
crates/goose/src/config/extensions.rs |
Switches platform extension default enabled behavior to respect PlatformExtensionDef::default_enabled, allowing subagent/code-execution defaults to be controlled centrally. |
crates/goose/src/agents/tool_route_manager.rs |
Changes router search dispatch to return DeferredToolCall instead of the old wrapper type, aligning the router with the new deferred tool model. |
crates/goose/src/agents/tool_execution.rs |
Replaces the old ToolCallResult wrapper with DeferredToolCall (future + optional notification stream) and updates agent approval flow to consume it. |
crates/goose/src/agents/subagent_tool.rs |
Re-types handle_subagent_tool to produce DeferredToolCall while keeping validation and execution behavior the same. |
crates/goose/src/agents/subagent_task_config.rs |
Simplifies TaskConfig to hold only provider, extensions, and derived max_turns, dropping unused parent session/working-dir fields. |
crates/goose/src/agents/subagent_client.rs |
Adds the new SubagentClient MCP client exposing the subagent tool as a platform extension, enforcing no-subagents-from-subagents and wiring provider, extensions, sub-recipes, and working dir into handle_subagent_tool. |
crates/goose/src/agents/mod.rs |
Registers the new subagent_client module so the agent crate exports the subagent platform extension. |
crates/goose/src/agents/mcp_client.rs |
Extends McpClientTrait with a default call_tool_deferred that wraps call_tool into a DeferredToolCall, providing a unified async interface for extensions. |
crates/goose/src/agents/final_output_tool.rs |
Adapts final-output tool execution to return DeferredToolCall, keeping semantics while fitting the new tool execution abstraction. |
crates/goose/src/agents/extension_manager.rs |
Stores sub_recipes in the shared PlatformExtensionContext, exposes get_provider, and updates dispatch_tool_call to rely on call_tool_deferred and propagate notification streams cleanly. |
crates/goose/src/agents/extension.rs |
Adds the subagent platform extension definition and extends PlatformExtensionContext (with Default) to carry shared sub_recipes for platform clients. |
crates/goose/src/agents/code_execution_extension.rs |
Fixes handle_read_module to use module_path, updates instructions and tests, and simplifies tests to construct PlatformExtensionContext via Default. |
crates/goose/src/agents/agent.rs |
Switches sub-recipe storage to Arc<RwLock<_>>, exposes sub_recipes(), removes special-case subagent dispatch, adds ensure_subagent_for_recipes/enable_subagent_extension, and routes all tool calls through DeferredToolCall. |
crates/goose-server/src/routes/recipe_utils.rs |
After applying a recipe, calls agent.ensure_subagent_for_recipes() so recipe-driven sessions have the subagent extension enabled when appropriate. |
crates/goose-cli/src/session/builder.rs |
Passes sub_recipes into the CLI agent’s PlatformExtensionContext and calls ensure_subagent_for_recipes() so CLI sessions gain subagent support when recipes define sub-recipes. |
crates/goose/src/agents/agent.rs
Outdated
| pub async fn ensure_subagent_for_recipes(&self) { | ||
| let has_sub_recipes = !self.sub_recipes.read().await.is_empty(); | ||
| if has_sub_recipes { | ||
| if let Err(e) = self.enable_subagent_extension().await { | ||
| warn!("Failed to enable subagent extension for recipe: {}", e); | ||
| } | ||
| } | ||
| } |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ensure_subagent_for_recipes and enable_subagent_extension now control when the new subagent platform extension is auto-enabled, but there don’t appear to be tests exercising cases like recipes with/without sub_recipes, non-autonomous mode, and subagent sessions; adding targeted tests around these branches would help prevent regressions in when the subagent tool is available and ensure the safety gating continues to behave as intended.
| } | ||
| } | ||
|
|
||
| agent_ptr.ensure_subagent_for_recipes().await; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this makes me think that sub recipes should be part of the subagent platform extension itself, not the agent
at the least, I would say do this within the agent such that it isn't the caller's responsibility to always call this. But then you still have this situation where the Agent has sub_recipes, but they're non functional unless it's been configured with this extension
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. But I want to do that in a separate PR, potentially one for this overhaul #6202
Resolved merge conflicts: - Updated dispatch_tool_call to include session_id parameter - Updated handle_approved_and_denied_tools to include session_id parameter - Updated handle_approval_tool_requests to include session_id parameter - Fixed add_extension -> add_extension_with_working_dir calls - Fixed clippy warning for needless borrow in extension_manager.rs
|
/goose |
|
Summary: This PR moves the subagent tool from a special case in 🔴 Blocking Issues
🟡 Warnings
🟢 Suggestions
✅ Highlights
Review generated by goose |
|
/goose |
|
Summary: This PR refactors the subagent tool from special-case handling in 🟡 Warnings
🟢 Suggestions
✅ Highlights
Review generated by goose |
Moves the subagent tool from being a special case in
Agent::dispatch_tool_callto a proper platform extension, following the same pattern as todo, skills, chatrecall, and code_execution extensions.Motivation
code_execution- this change enables subagent delegation from JavaScript codeChanges
SubagentClientimplementingMcpClientTraitcall_tool_deferredtrait method allowing extensions to return deferred futures (with sensible default for sync tools)PlatformExtensionContextextended withworking_dirandsub_recipesTaskConfigsimplified (session/working_dir now obtained from context)sub_recipeschanged fromMutextoArc<RwLock>for shared read access"path"to"module_path"inhandle_read_modulefixes a pre-existing bug where the code looked for"path"but the schema definedmodule_path.