Configure multi_agent_v2 spawn agent hints (#17071)

Allow multi_agent_v2 features to have its own temporary configuration
under `[features.multi_agent_v2]`

```
[features.multi_agent_v2]
enabled = true
usage_hint_enabled = false
usage_hint_text = "Custom delegation guidance."
hide_spawn_agent_metadata = true
```

Absent `usage_hint_text` means use the default hint.

```
[features]
multi_agent_v2 = true
```

still works as the boolean shorthand.
This commit is contained in:
pakrym-oai
2026-04-08 08:42:18 -07:00
committed by GitHub
parent 2250fdd54a
commit 4c07dd4d25
18 changed files with 501 additions and 62 deletions

View File

@@ -11,6 +11,8 @@ pub struct SpawnAgentToolOptions<'a> {
pub available_models: &'a [ModelPreset],
pub agent_type_description: String,
pub hide_agent_type_model_reasoning: bool,
pub include_usage_hint: bool,
pub usage_hint_text: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -35,6 +37,8 @@ pub fn create_spawn_agent_tool_v1(options: SpawnAgentToolOptions<'_>) -> ToolSpe
description: spawn_agent_tool_description(
available_models_description.as_deref(),
return_value_description,
options.include_usage_hint,
options.usage_hint_text,
),
strict: false,
defer_loading: None,
@@ -68,6 +72,8 @@ pub fn create_spawn_agent_tool_v2(options: SpawnAgentToolOptions<'_>) -> ToolSpe
description: spawn_agent_tool_description(
available_models_description.as_deref(),
return_value_description,
options.include_usage_hint,
options.usage_hint_text,
),
strict: false,
defer_loading: None,
@@ -580,20 +586,40 @@ fn hide_spawn_agent_metadata_options(properties: &mut BTreeMap<String, JsonSchem
fn spawn_agent_tool_description(
available_models_description: Option<&str>,
return_value_description: &str,
include_usage_hint: bool,
usage_hint_text: Option<String>,
) -> String {
let agent_role_guidance = available_models_description
.map(|description| {
format!(
"Agent-role guidance below only helps choose which agent to use after spawning is already authorized; it never authorizes spawning by itself.\n{description}"
)
let agent_role_guidance = available_models_description.unwrap_or_default();
let tool_description = format!(
r#"
{agent_role_guidance}
Spawn a sub-agent for a well-scoped task. {return_value_description}"#
);
if !include_usage_hint {
return tool_description;
}
if let Some(usage_hint_text) = usage_hint_text {
return format!(
r#"
{tool_description}
{usage_hint_text}"#
);
}
let agent_role_usage_hint = available_models_description
.map(|_| {
"Agent-role guidance below only helps choose which agent to use after spawning is already authorized; it never authorizes spawning by itself."
})
.unwrap_or_default();
format!(
r#"
Only use `spawn_agent` if and only if the user explicitly asks for sub-agents, delegation, or parallel agent work.
Requests for depth, thoroughness, research, investigation, or detailed codebase analysis do not count as permission to spawn.
{agent_role_guidance}
Spawn a sub-agent for a well-scoped task. {return_value_description} This spawn_agent tool provides you access to smaller but more efficient sub-agents. A mini model can solve many tasks faster than the main model. You should follow the rules and guidelines below to use this tool.
{tool_description}
This spawn_agent tool provides you access to smaller but more efficient sub-agents. A mini model can solve many tasks faster than the main model. You should follow the rules and guidelines below to use this tool.
Only use `spawn_agent` if and only if the user explicitly asks for sub-agents, delegation, or parallel agent work.
Requests for depth, thoroughness, research, investigation, or detailed codebase analysis do not count as permission to spawn.
{agent_role_usage_hint}
### When to delegate vs. do the subtask yourself
- First, quickly analyze the overall user task and form a succinct high-level plan. Identify which tasks are immediate blockers on the critical path, and which tasks are sidecar tasks that are needed but can run in parallel without blocking the next local step. As part of that plan, explicitly decide what immediate task you should do locally right now. Do this planning step before delegating to agents so you do not hand off the immediate blocking task to a submodel and then waste time waiting on it.

View File

@@ -38,6 +38,8 @@ fn spawn_agent_tool_v2_requires_task_name_and_lists_visible_models() {
],
agent_type_description: "role help".to_string(),
hide_agent_type_model_reasoning: false,
include_usage_hint: true,
usage_hint_text: None,
});
let ToolSpec::Function(ResponsesApiTool {
@@ -84,6 +86,8 @@ fn spawn_agent_tool_v1_keeps_legacy_fork_context_field() {
available_models: &[],
agent_type_description: "role help".to_string(),
hide_agent_type_model_reasoning: false,
include_usage_hint: true,
usage_hint_text: None,
});
let ToolSpec::Function(ResponsesApiTool { parameters, .. }) = tool else {

View File

@@ -105,6 +105,8 @@ pub struct ToolsConfig {
pub collab_tools: bool,
pub multi_agent_v2: bool,
pub hide_spawn_agent_metadata: bool,
pub spawn_agent_usage_hint: bool,
pub spawn_agent_usage_hint_text: Option<String>,
pub default_mode_request_user_input: bool,
pub experimental_supported_tools: Vec<String>,
pub agent_jobs_tools: bool,
@@ -141,7 +143,6 @@ impl ToolsConfig {
include_js_repl && features.enabled(Feature::JsReplToolsOnly);
let include_collab_tools = features.enabled(Feature::Collab);
let include_multi_agent_v2 = features.enabled(Feature::MultiAgentV2);
let hide_spawn_agent_metadata = features.enabled(Feature::DebugHideSpawnAgentMetadata);
let include_agent_jobs = features.enabled(Feature::SpawnCsv);
let include_default_mode_request_user_input =
features.enabled(Feature::DefaultModeRequestUserInput);
@@ -219,7 +220,9 @@ impl ToolsConfig {
can_request_original_image_detail: include_original_image_detail,
collab_tools: include_collab_tools,
multi_agent_v2: include_multi_agent_v2,
hide_spawn_agent_metadata,
hide_spawn_agent_metadata: false,
spawn_agent_usage_hint: true,
spawn_agent_usage_hint_text: None,
default_mode_request_user_input: include_default_mode_request_user_input,
experimental_supported_tools: model_info.experimental_supported_tools.clone(),
agent_jobs_tools: include_agent_jobs,
@@ -233,6 +236,24 @@ impl ToolsConfig {
self
}
pub fn with_spawn_agent_usage_hint(mut self, spawn_agent_usage_hint: bool) -> Self {
self.spawn_agent_usage_hint = spawn_agent_usage_hint;
self
}
pub fn with_spawn_agent_usage_hint_text(
mut self,
spawn_agent_usage_hint_text: Option<String>,
) -> Self {
self.spawn_agent_usage_hint_text = spawn_agent_usage_hint_text;
self
}
pub fn with_hide_spawn_agent_metadata(mut self, hide_spawn_agent_metadata: bool) -> Self {
self.hide_spawn_agent_metadata = hide_spawn_agent_metadata;
self
}
pub fn with_allow_login_shell(mut self, allow_login_shell: bool) -> Self {
self.allow_login_shell = allow_login_shell;
self

View File

@@ -376,6 +376,8 @@ pub fn build_tool_registry_plan(
available_models: &config.available_models,
agent_type_description,
hide_agent_type_model_reasoning: config.hide_spawn_agent_metadata,
include_usage_hint: config.spawn_agent_usage_hint,
usage_hint_text: config.spawn_agent_usage_hint_text.clone(),
}),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
@@ -419,6 +421,8 @@ pub fn build_tool_registry_plan(
available_models: &config.available_models,
agent_type_description,
hide_agent_type_model_reasoning: config.hide_spawn_agent_metadata,
include_usage_hint: config.spawn_agent_usage_hint,
usage_hint_text: config.spawn_agent_usage_hint_text.clone(),
}),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,

View File

@@ -1940,6 +1940,8 @@ fn spawn_agent_tool_options(config: &ToolsConfig) -> SpawnAgentToolOptions<'_> {
available_models: &config.available_models,
agent_type_description: agent_type_description(config, DEFAULT_AGENT_TYPE_DESCRIPTION),
hide_agent_type_model_reasoning: config.hide_spawn_agent_metadata,
include_usage_hint: config.spawn_agent_usage_hint,
usage_hint_text: config.spawn_agent_usage_hint_text.clone(),
}
}