From acbb32d19fd2cbdcc3cd6fe6af11ae1eab534209 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 8 Jan 2026 22:53:02 -0500 Subject: [PATCH] Subagents PR 1: Feature flag + tool skeleton (#46186) This PR adds the foundation for the subagents feature: - Add `SubagentsFeatureFlag` with staff-disabled default - Create `SubagentTool` struct with input schema for task/summary/context-low prompts - Register `SubagentTool` conditionally when feature flag is enabled - Tool returns stub message 'not yet implemented' for now Release Notes: - N/A --------- Co-authored-by: Amp Co-authored-by: Mikayla Maki Co-authored-by: Yara --- Cargo.lock | 1 + crates/agent/Cargo.toml | 1 + crates/agent/src/thread.rs | 9 +- crates/agent/src/tools.rs | 4 +- crates/agent/src/tools/subagent_tool.rs | 109 ++++++++++++++++++++++++ crates/feature_flags/src/flags.rs | 10 +++ 6 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 crates/agent/src/tools/subagent_tool.rs diff --git a/Cargo.lock b/Cargo.lock index b9c2c2ea507f5ece6e9be57e954db337c5a8f586..80d0f518ffd14f505e338b30f876a31ef9808a19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,6 +172,7 @@ dependencies = [ "editor", "env_logger 0.11.8", "eval_utils", + "feature_flags", "fs", "futures 0.3.31", "git", diff --git a/crates/agent/Cargo.toml b/crates/agent/Cargo.toml index 667033a1bb33ea0372b8a9d8b0bfb00b23f59347..54d1fc7ef3d1ab23ffdd903466bc4839de36429b 100644 --- a/crates/agent/Cargo.toml +++ b/crates/agent/Cargo.toml @@ -32,6 +32,7 @@ collections.workspace = true context_server.workspace = true db.workspace = true derive_more.workspace = true +feature_flags.workspace = true fs.workspace = true futures.workspace = true git.workspace = true diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs index 4ed1e537f0536c24e3b5ccd79e9d9fb052b293b2..9a579b12e4ca2b7d8724cad5b7987dcccb08b2ee 100644 --- a/crates/agent/src/thread.rs +++ b/crates/agent/src/thread.rs @@ -2,11 +2,12 @@ use crate::{ ContextServerRegistry, CopyPathTool, CreateDirectoryTool, DbLanguageModel, DbThread, DeletePathTool, DiagnosticsTool, EditFileTool, FetchTool, FindPathTool, GrepTool, ListDirectoryTool, MovePathTool, NowTool, OpenTool, ProjectSnapshot, ReadFileTool, - RestoreFileFromDiskTool, SaveFileTool, SystemPromptTemplate, Template, Templates, TerminalTool, - ThinkingTool, WebSearchTool, + RestoreFileFromDiskTool, SaveFileTool, SubagentTool, SystemPromptTemplate, Template, Templates, + TerminalTool, ThinkingTool, WebSearchTool, }; use acp_thread::{MentionUri, UserMessageId}; use action_log::ActionLog; +use feature_flags::{FeatureFlagAppExt as _, SubagentsFeatureFlag}; use agent_client_protocol as acp; use agent_settings::{ @@ -1028,6 +1029,10 @@ impl Thread { self.add_tool(TerminalTool::new(self.project.clone(), environment)); self.add_tool(ThinkingTool); self.add_tool(WebSearchTool); + + if cx.has_flag::() { + self.add_tool(SubagentTool::new()); + } } pub fn add_tool(&mut self, tool: T) { diff --git a/crates/agent/src/tools.rs b/crates/agent/src/tools.rs index 358903a32baa5ead9b073642015e6829501307a2..f2f33172824d007b8061beba5bec1aabe3c88644 100644 --- a/crates/agent/src/tools.rs +++ b/crates/agent/src/tools.rs @@ -14,7 +14,7 @@ mod open_tool; mod read_file_tool; mod restore_file_from_disk_tool; mod save_file_tool; - +mod subagent_tool; mod terminal_tool; mod thinking_tool; mod web_search_tool; @@ -38,7 +38,7 @@ pub use open_tool::*; pub use read_file_tool::*; pub use restore_file_from_disk_tool::*; pub use save_file_tool::*; - +pub use subagent_tool::*; pub use terminal_tool::*; pub use thinking_tool::*; pub use web_search_tool::*; diff --git a/crates/agent/src/tools/subagent_tool.rs b/crates/agent/src/tools/subagent_tool.rs new file mode 100644 index 0000000000000000000000000000000000000000..376bce844fbf67ab79bff4d7611cc7710defda13 --- /dev/null +++ b/crates/agent/src/tools/subagent_tool.rs @@ -0,0 +1,109 @@ +use agent_client_protocol as acp; +use anyhow::Result; +use gpui::{App, SharedString, Task}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +use crate::{AgentTool, ToolCallEventStream}; + +/// Spawns a subagent with its own context window to perform a delegated task. +/// +/// Use this tool when you need to: +/// - Perform research that would consume too many tokens in the main context +/// - Execute a complex subtask independently +/// - Run multiple parallel investigations +/// +/// You control what the subagent does by providing: +/// 1. A task prompt describing what the subagent should do +/// 2. A summary prompt that tells the subagent how to summarize its work when done +/// 3. A "context running out" prompt for when the subagent is low on tokens +/// +/// The subagent has access to the same tools you do. You can optionally restrict +/// which tools the subagent can use. +/// +/// IMPORTANT: +/// - Maximum 8 subagents can be spawned per turn +/// - Subagents cannot use tools you don't have access to +/// - If spawning multiple subagents that might write to the filesystem, provide +/// guidance on how to avoid conflicts (e.g., assign each to different directories) +/// - Instruct subagents to be concise in their summaries to conserve your context +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct SubagentToolInput { + /// Short label displayed in the UI while the subagent runs (e.g., "Researching alternatives") + pub label: String, + + /// The initial prompt that tells the subagent what task to perform. + /// Be specific about what you want the subagent to accomplish. + pub task_prompt: String, + + /// The prompt sent to the subagent when it completes its task, asking it + /// to summarize what it did and return results. This summary becomes the + /// tool result you receive. + /// + /// Example: "Summarize what you found, listing the top 3 alternatives with pros/cons." + pub summary_prompt: String, + + /// The prompt sent if the subagent is running low on context (25% remaining). + /// Should instruct it to stop and summarize progress so far, plus what's left undone. + /// + /// Example: "Context is running low. Stop and summarize your progress so far, + /// and list what remains to be investigated." + pub context_low_prompt: String, + + /// Optional: Maximum runtime in milliseconds. If exceeded, the subagent is + /// asked to summarize and return. No timeout by default. + #[serde(default)] + pub timeout_ms: Option, + + /// Optional: List of tool names the subagent is allowed to use. + /// If not provided, the subagent can use all tools available to the parent. + /// Tools listed here must be a subset of the parent's available tools. + #[serde(default)] + pub allowed_tools: Option>, +} + +pub struct SubagentTool; + +impl SubagentTool { + pub fn new() -> Self { + Self + } +} + +impl AgentTool for SubagentTool { + type Input = SubagentToolInput; + type Output = String; + + fn name() -> &'static str { + "subagent" + } + + fn kind() -> acp::ToolKind { + acp::ToolKind::Other + } + + fn initial_title( + &self, + input: Result, + _cx: &mut App, + ) -> SharedString { + match input { + Ok(input) => format!("Subagent: {}", input.label).into(), + Err(_) => "Subagent".into(), + } + } + + fn run( + self: Arc, + input: Self::Input, + event_stream: ToolCallEventStream, + _cx: &mut App, + ) -> Task> { + event_stream.update_fields( + acp::ToolCallUpdateFields::new() + .content(vec![format!("Starting subagent: {}", input.label).into()]), + ); + Task::ready(Ok("Subagent tool not yet implemented.".to_string())) + } +} diff --git a/crates/feature_flags/src/flags.rs b/crates/feature_flags/src/flags.rs index 6d170476b30cb520612232d02ca418a393a5311a..2fa896b9b480810cd04d01bb1f6d02a2761a689b 100644 --- a/crates/feature_flags/src/flags.rs +++ b/crates/feature_flags/src/flags.rs @@ -35,3 +35,13 @@ pub struct AgentSharingFeatureFlag; impl FeatureFlag for AgentSharingFeatureFlag { const NAME: &'static str = "agent-sharing"; } + +pub struct SubagentsFeatureFlag; + +impl FeatureFlag for SubagentsFeatureFlag { + const NAME: &'static str = "subagents"; + + fn enabled_for_staff() -> bool { + false + } +}