Subagents PR 1: Feature flag + tool skeleton (#46186)

Richard Feldman , Amp , Mikayla Maki , and Yara created

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 <amp@ampcode.com>
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
Co-authored-by: Yara <yara@zed.dev>

Change summary

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(-)

Detailed changes

Cargo.lock 🔗

@@ -172,6 +172,7 @@ dependencies = [
  "editor",
  "env_logger 0.11.8",
  "eval_utils",
+ "feature_flags",
  "fs",
  "futures 0.3.31",
  "git",

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

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::<SubagentsFeatureFlag>() {
+            self.add_tool(SubagentTool::new());
+        }
     }
 
     pub fn add_tool<T: AgentTool>(&mut self, tool: T) {

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::*;

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<u64>,
+
+    /// 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<Vec<String>>,
+}
+
+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<Self::Input, serde_json::Value>,
+        _cx: &mut App,
+    ) -> SharedString {
+        match input {
+            Ok(input) => format!("Subagent: {}", input.label).into(),
+            Err(_) => "Subagent".into(),
+        }
+    }
+
+    fn run(
+        self: Arc<Self>,
+        input: Self::Input,
+        event_stream: ToolCallEventStream,
+        _cx: &mut App,
+    ) -> Task<Result<String>> {
+        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()))
+    }
+}

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
+    }
+}