mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
Add spawned agent skills-message toggle
Support configuring whether spawned agents receive the skills developer message through agents.spawn.inject_skills_message and per-call spawn_agent overrides. Full-history forks continue to inherit the parent setting, and request construction now omits skills instructions when disabled. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -565,6 +565,9 @@ pub struct AgentSpawnToml {
|
||||
/// MCP server names to start in spawned agents. When unset, spawned agents inherit all
|
||||
/// effective MCP servers. An empty list disables MCP servers for spawned agents.
|
||||
pub mcp_servers: Option<Vec<String>>,
|
||||
|
||||
/// Whether to inject the skills developer message in spawned agents.
|
||||
pub inject_skills_message: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)]
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
"AgentSpawnToml": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"inject_skills_message": {
|
||||
"description": "Whether to inject the skills developer message in spawned agents.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mcp_servers": {
|
||||
"description": "MCP server names to start in spawned agents. When unset, spawned agents inherit all effective MCP servers. An empty list disables MCP servers for spawned agents.",
|
||||
"items": {
|
||||
|
||||
@@ -4837,6 +4837,7 @@ async fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
|
||||
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
multi_agent_v2: MultiAgentV2Config::default(),
|
||||
inject_skills_message: true,
|
||||
features: Features::with_defaults().into(),
|
||||
suppress_unstable_features_warning: false,
|
||||
active_profile: Some("o3".to_string()),
|
||||
@@ -4989,6 +4990,7 @@ async fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
multi_agent_v2: MultiAgentV2Config::default(),
|
||||
inject_skills_message: true,
|
||||
features: Features::with_defaults().into(),
|
||||
suppress_unstable_features_warning: false,
|
||||
active_profile: Some("gpt3".to_string()),
|
||||
@@ -5139,6 +5141,7 @@ async fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
|
||||
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
multi_agent_v2: MultiAgentV2Config::default(),
|
||||
inject_skills_message: true,
|
||||
features: Features::with_defaults().into(),
|
||||
suppress_unstable_features_warning: false,
|
||||
active_profile: Some("zdr".to_string()),
|
||||
@@ -5274,6 +5277,7 @@ async fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
|
||||
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
multi_agent_v2: MultiAgentV2Config::default(),
|
||||
inject_skills_message: true,
|
||||
features: Features::with_defaults().into(),
|
||||
suppress_unstable_features_warning: false,
|
||||
active_profile: Some("gpt5".to_string()),
|
||||
@@ -6529,6 +6533,7 @@ async fn agents_spawn_config_sets_child_defaults_only() -> std::io::Result<()> {
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[agents.spawn]
|
||||
mcp_servers = ["docs", " linear ", "docs"]
|
||||
inject_skills_message = false
|
||||
"#,
|
||||
)?;
|
||||
|
||||
@@ -6542,9 +6547,11 @@ mcp_servers = ["docs", " linear ", "docs"]
|
||||
config.agent_spawn,
|
||||
AgentSpawnConfig {
|
||||
mcp_servers: Some(vec!["docs".to_string(), "linear".to_string()]),
|
||||
inject_skills_message: false,
|
||||
}
|
||||
);
|
||||
assert_eq!(config.mcp_server_allowlist, None);
|
||||
assert!(config.inject_skills_message);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -560,6 +560,9 @@ pub struct Config {
|
||||
/// Settings specific to the task-path-based multi-agent tool surface.
|
||||
pub multi_agent_v2: MultiAgentV2Config,
|
||||
|
||||
/// Whether to inject the `<skills_instructions>` developer block for this session.
|
||||
pub inject_skills_message: bool,
|
||||
|
||||
/// Centralized feature flags; source of truth for feature gating.
|
||||
pub features: ManagedFeatures,
|
||||
|
||||
@@ -614,11 +617,15 @@ pub struct MultiAgentV2Config {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AgentSpawnConfig {
|
||||
pub mcp_servers: Option<Vec<String>>,
|
||||
pub inject_skills_message: bool,
|
||||
}
|
||||
|
||||
impl Default for AgentSpawnConfig {
|
||||
fn default() -> Self {
|
||||
Self { mcp_servers: None }
|
||||
Self {
|
||||
mcp_servers: None,
|
||||
inject_skills_message: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1427,8 +1434,9 @@ fn resolve_multi_agent_v2_config(
|
||||
}
|
||||
|
||||
fn resolve_agent_spawn_config(spawn: Option<&AgentSpawnToml>) -> std::io::Result<AgentSpawnConfig> {
|
||||
let default = AgentSpawnConfig::default();
|
||||
let Some(spawn) = spawn else {
|
||||
return Ok(AgentSpawnConfig { mcp_servers: None });
|
||||
return Ok(default);
|
||||
};
|
||||
|
||||
let mcp_servers = spawn
|
||||
@@ -1436,7 +1444,12 @@ fn resolve_agent_spawn_config(spawn: Option<&AgentSpawnToml>) -> std::io::Result
|
||||
.as_ref()
|
||||
.map(|servers| normalize_mcp_server_allowlist(servers, "agents.spawn.mcp_servers"))
|
||||
.transpose()?;
|
||||
Ok(AgentSpawnConfig { mcp_servers })
|
||||
Ok(AgentSpawnConfig {
|
||||
mcp_servers,
|
||||
inject_skills_message: spawn
|
||||
.inject_skills_message
|
||||
.unwrap_or(default.inject_skills_message),
|
||||
})
|
||||
}
|
||||
|
||||
fn normalize_mcp_server_allowlist(
|
||||
@@ -2309,6 +2322,7 @@ impl Config {
|
||||
background_terminal_max_timeout,
|
||||
ghost_snapshot,
|
||||
multi_agent_v2,
|
||||
inject_skills_message: true,
|
||||
features,
|
||||
suppress_unstable_features_warning: cfg
|
||||
.suppress_unstable_features_warning
|
||||
|
||||
@@ -2411,12 +2411,14 @@ impl Session {
|
||||
developer_sections.push(apps_section);
|
||||
}
|
||||
}
|
||||
let implicit_skills = turn_context
|
||||
.turn_skills
|
||||
.outcome
|
||||
.allowed_skills_for_implicit_invocation();
|
||||
if let Some(skills_section) = render_skills_section(&implicit_skills) {
|
||||
developer_sections.push(skills_section);
|
||||
if turn_context.config.inject_skills_message {
|
||||
let implicit_skills = turn_context
|
||||
.turn_skills
|
||||
.outcome
|
||||
.allowed_skills_for_implicit_invocation();
|
||||
if let Some(skills_section) = render_skills_section(&implicit_skills) {
|
||||
developer_sections.push(skills_section);
|
||||
}
|
||||
}
|
||||
let loaded_plugins = self
|
||||
.services
|
||||
|
||||
@@ -66,6 +66,7 @@ impl ToolHandler for Handler {
|
||||
args.model.as_deref(),
|
||||
args.reasoning_effort,
|
||||
args.mcp_servers.as_deref(),
|
||||
args.inject_skills_message,
|
||||
)?;
|
||||
} else {
|
||||
apply_requested_spawn_agent_model_overrides(
|
||||
@@ -83,6 +84,7 @@ impl ToolHandler for Handler {
|
||||
&session,
|
||||
&mut config,
|
||||
args.mcp_servers,
|
||||
args.inject_skills_message,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -190,6 +192,7 @@ struct SpawnAgentArgs {
|
||||
model: Option<String>,
|
||||
reasoning_effort: Option<ReasoningEffort>,
|
||||
mcp_servers: Option<Vec<String>>,
|
||||
inject_skills_message: Option<bool>,
|
||||
#[serde(default)]
|
||||
fork_context: bool,
|
||||
}
|
||||
|
||||
@@ -243,14 +243,16 @@ pub(crate) fn reject_full_fork_spawn_overrides(
|
||||
model: Option<&str>,
|
||||
reasoning_effort: Option<ReasoningEffort>,
|
||||
mcp_servers: Option<&[String]>,
|
||||
inject_skills_message: Option<bool>,
|
||||
) -> Result<(), FunctionCallError> {
|
||||
if agent_type.is_some()
|
||||
|| model.is_some()
|
||||
|| reasoning_effort.is_some()
|
||||
|| mcp_servers.is_some()
|
||||
|| inject_skills_message.is_some()
|
||||
{
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, and MCP servers; omit agent_type, model, reasoning_effort, and mcp_servers, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, MCP servers, and skills-message setting; omit agent_type, model, reasoning_effort, mcp_servers, and inject_skills_message, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
@@ -260,6 +262,7 @@ pub(crate) async fn apply_spawn_agent_capability_overrides(
|
||||
session: &Session,
|
||||
config: &mut Config,
|
||||
requested_mcp_servers: Option<Vec<String>>,
|
||||
requested_inject_skills_message: Option<bool>,
|
||||
) -> Result<(), FunctionCallError> {
|
||||
let mcp_server_allowlist = match requested_mcp_servers {
|
||||
Some(servers) => Some(normalize_spawn_mcp_server_allowlist(
|
||||
@@ -274,6 +277,8 @@ pub(crate) async fn apply_spawn_agent_capability_overrides(
|
||||
}
|
||||
|
||||
config.mcp_server_allowlist = mcp_server_allowlist;
|
||||
config.inject_skills_message =
|
||||
requested_inject_skills_message.unwrap_or(config.agent_spawn.inject_skills_message);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ async fn spawn_agent_uses_explorer_role_and_preserves_approval_policy() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn spawn_agent_applies_per_call_mcp_settings() {
|
||||
async fn spawn_agent_applies_per_call_mcp_and_skills_settings() {
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct SpawnAgentResult {
|
||||
agent_id: String,
|
||||
@@ -462,7 +462,8 @@ async fn spawn_agent_applies_per_call_mcp_settings() {
|
||||
"spawn_agent",
|
||||
function_payload(json!({
|
||||
"message": "inspect this repo",
|
||||
"mcp_servers": [" docs ", "docs"]
|
||||
"mcp_servers": [" docs ", "docs"],
|
||||
"inject_skills_message": false
|
||||
})),
|
||||
))
|
||||
.await
|
||||
@@ -484,6 +485,7 @@ async fn spawn_agent_applies_per_call_mcp_settings() {
|
||||
child_config.mcp_server_allowlist,
|
||||
Some(vec!["docs".to_string()])
|
||||
);
|
||||
assert!(!child_config.inject_skills_message);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -514,6 +516,7 @@ enabled = false
|
||||
|
||||
[agents.spawn]
|
||||
mcp_servers = ["linear"]
|
||||
inject_skills_message = false
|
||||
"#,
|
||||
)
|
||||
.await
|
||||
@@ -551,7 +554,8 @@ mcp_servers = ["linear"]
|
||||
function_payload(json!({
|
||||
"message": "inspect this repo",
|
||||
"agent_type": role_name,
|
||||
"mcp_servers": ["docs"]
|
||||
"mcp_servers": ["docs"],
|
||||
"inject_skills_message": true
|
||||
})),
|
||||
))
|
||||
.await
|
||||
@@ -576,6 +580,7 @@ mcp_servers = ["linear"]
|
||||
child_config.mcp_server_allowlist,
|
||||
Some(vec!["docs".to_string()])
|
||||
);
|
||||
assert!(child_config.inject_skills_message);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -606,6 +611,7 @@ enabled = false
|
||||
|
||||
[agents.spawn]
|
||||
mcp_servers = ["linear"]
|
||||
inject_skills_message = false
|
||||
"#,
|
||||
)
|
||||
.await
|
||||
@@ -659,6 +665,7 @@ mcp_servers = ["linear"]
|
||||
child_config.mcp_server_allowlist,
|
||||
Some(vec!["linear".to_string()])
|
||||
);
|
||||
assert!(!child_config.inject_skills_message);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -712,7 +719,8 @@ async fn spawn_agent_fork_context_rejects_capability_overrides() {
|
||||
function_payload(json!({
|
||||
"message": "inspect this repo",
|
||||
"fork_context": true,
|
||||
"mcp_servers": []
|
||||
"mcp_servers": [],
|
||||
"inject_skills_message": false
|
||||
})),
|
||||
))
|
||||
.await
|
||||
@@ -721,7 +729,7 @@ async fn spawn_agent_fork_context_rejects_capability_overrides() {
|
||||
assert_eq!(
|
||||
err,
|
||||
FunctionCallError::RespondToModel(
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, and MCP servers; omit agent_type, model, reasoning_effort, and mcp_servers, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, MCP servers, and skills-message setting; omit agent_type, model, reasoning_effort, mcp_servers, and inject_skills_message, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -754,7 +762,7 @@ async fn spawn_agent_fork_context_rejects_agent_type_override() {
|
||||
assert_eq!(
|
||||
err,
|
||||
FunctionCallError::RespondToModel(
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, and MCP servers; omit agent_type, model, reasoning_effort, and mcp_servers, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, MCP servers, and skills-message setting; omit agent_type, model, reasoning_effort, mcp_servers, and inject_skills_message, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -788,7 +796,7 @@ async fn spawn_agent_fork_context_rejects_child_model_overrides() {
|
||||
assert_eq!(
|
||||
err,
|
||||
FunctionCallError::RespondToModel(
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, and MCP servers; omit agent_type, model, reasoning_effort, and mcp_servers, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, MCP servers, and skills-message setting; omit agent_type, model, reasoning_effort, mcp_servers, and inject_skills_message, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -832,7 +840,7 @@ async fn multi_agent_v2_spawn_fork_turns_all_rejects_agent_type_override() {
|
||||
assert_eq!(
|
||||
err,
|
||||
FunctionCallError::RespondToModel(
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, and MCP servers; omit agent_type, model, reasoning_effort, and mcp_servers, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, MCP servers, and skills-message setting; omit agent_type, model, reasoning_effort, mcp_servers, and inject_skills_message, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -873,7 +881,7 @@ async fn multi_agent_v2_spawn_fork_turns_rejects_child_model_overrides() {
|
||||
assert_eq!(
|
||||
err,
|
||||
FunctionCallError::RespondToModel(
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, and MCP servers; omit agent_type, model, reasoning_effort, and mcp_servers, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
"Full-history forked agents inherit the parent agent type, model, reasoning effort, MCP servers, and skills-message setting; omit agent_type, model, reasoning_effort, mcp_servers, and inject_skills_message, or spawn without fork_context/fork_turns=all.".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1154,7 +1162,7 @@ async fn multi_agent_v2_spawn_returns_path_and_send_message_accepts_relative_pat
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_spawn_applies_per_call_mcp_settings() {
|
||||
async fn multi_agent_v2_spawn_applies_per_call_mcp_and_skills_settings() {
|
||||
let (mut session, mut turn) = make_session_and_context().await;
|
||||
let manager = thread_manager();
|
||||
let root = manager
|
||||
@@ -1189,7 +1197,8 @@ async fn multi_agent_v2_spawn_applies_per_call_mcp_settings() {
|
||||
function_payload(json!({
|
||||
"message": "inspect this repo",
|
||||
"task_name": "test_process",
|
||||
"mcp_servers": ["docs"]
|
||||
"mcp_servers": ["docs"],
|
||||
"inject_skills_message": false
|
||||
})),
|
||||
))
|
||||
.await
|
||||
@@ -1213,6 +1222,7 @@ async fn multi_agent_v2_spawn_applies_per_call_mcp_settings() {
|
||||
child_config.mcp_server_allowlist,
|
||||
Some(vec!["docs".to_string()])
|
||||
);
|
||||
assert!(!child_config.inject_skills_message);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -76,6 +76,7 @@ impl ToolHandler for Handler {
|
||||
args.model.as_deref(),
|
||||
args.reasoning_effort,
|
||||
args.mcp_servers.as_deref(),
|
||||
args.inject_skills_message,
|
||||
)?;
|
||||
} else {
|
||||
apply_requested_spawn_agent_model_overrides(
|
||||
@@ -93,6 +94,7 @@ impl ToolHandler for Handler {
|
||||
&session,
|
||||
&mut config,
|
||||
args.mcp_servers,
|
||||
args.inject_skills_message,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -242,6 +244,7 @@ struct SpawnAgentArgs {
|
||||
model: Option<String>,
|
||||
reasoning_effort: Option<ReasoningEffort>,
|
||||
mcp_servers: Option<Vec<String>>,
|
||||
inject_skills_message: Option<bool>,
|
||||
fork_turns: Option<String>,
|
||||
fork_context: Option<bool>,
|
||||
}
|
||||
|
||||
@@ -1480,6 +1480,67 @@ async fn skills_append_to_developer_message() {
|
||||
let _codex_home_guard = codex_home;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn inject_skills_message_false_suppresses_developer_skills_section() {
|
||||
skip_if_no_network!();
|
||||
let server = MockServer::start().await;
|
||||
|
||||
let resp_mock = mount_sse_once(
|
||||
&server,
|
||||
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
|
||||
)
|
||||
.await;
|
||||
|
||||
let codex_home = Arc::new(TempDir::new().unwrap());
|
||||
let skill_dir = codex_home.path().join("skills/demo");
|
||||
std::fs::create_dir_all(&skill_dir).expect("create skill dir");
|
||||
std::fs::write(
|
||||
skill_dir.join("SKILL.md"),
|
||||
"---\nname: demo\ndescription: build charts\n---\n\n# body\n",
|
||||
)
|
||||
.expect("write skill");
|
||||
|
||||
let codex_home_path = codex_home.path().to_path_buf();
|
||||
let codex = test_codex()
|
||||
.with_home(codex_home.clone())
|
||||
.with_auth(CodexAuth::from_api_key("Test API Key"))
|
||||
.with_config(move |config| {
|
||||
config.cwd = codex_home_path.abs();
|
||||
config.inject_skills_message = false;
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
.expect("create new conversation")
|
||||
.codex;
|
||||
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
responsesapi_client_metadata: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
||||
|
||||
let request = resp_mock.single_request();
|
||||
let developer_messages = request.message_input_texts("developer");
|
||||
let developer_text = developer_messages.join("\n\n");
|
||||
assert!(
|
||||
!developer_text.contains("## Skills"),
|
||||
"did not expect skills section: {developer_messages:?}"
|
||||
);
|
||||
assert!(
|
||||
!developer_text.contains("demo: build charts"),
|
||||
"did not expect skill summary: {developer_messages:?}"
|
||||
);
|
||||
let _codex_home_guard = codex_home;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn includes_configured_effort_in_request() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
@@ -548,6 +548,13 @@ fn spawn_agent_common_properties_v1(agent_type_description: &str) -> BTreeMap<St
|
||||
),
|
||||
),
|
||||
),
|
||||
(
|
||||
"inject_skills_message".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Optional override for whether the new agent receives the skills developer message."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -592,6 +599,13 @@ fn spawn_agent_common_properties_v2(agent_type_description: &str) -> BTreeMap<St
|
||||
),
|
||||
),
|
||||
),
|
||||
(
|
||||
"inject_skills_message".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Optional override for whether the new agent receives the skills developer message."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ fn spawn_agent_tool_v2_requires_task_name_and_lists_visible_models() {
|
||||
assert!(properties.contains_key("message"));
|
||||
assert!(properties.contains_key("fork_turns"));
|
||||
assert!(properties.contains_key("mcp_servers"));
|
||||
assert!(properties.contains_key("inject_skills_message"));
|
||||
assert!(!properties.contains_key("items"));
|
||||
assert!(!properties.contains_key("fork_context"));
|
||||
assert_eq!(
|
||||
@@ -107,6 +108,7 @@ fn spawn_agent_tool_v1_keeps_legacy_fork_context_field() {
|
||||
|
||||
assert!(properties.contains_key("fork_context"));
|
||||
assert!(properties.contains_key("mcp_servers"));
|
||||
assert!(properties.contains_key("inject_skills_message"));
|
||||
assert!(!properties.contains_key("fork_turns"));
|
||||
}
|
||||
|
||||
|
||||
@@ -184,6 +184,7 @@ fn test_build_specs_collab_tools_enabled() {
|
||||
let (properties, _) = expect_object_schema(parameters);
|
||||
assert!(properties.contains_key("fork_context"));
|
||||
assert!(properties.contains_key("mcp_servers"));
|
||||
assert!(properties.contains_key("inject_skills_message"));
|
||||
assert!(!properties.contains_key("fork_turns"));
|
||||
}
|
||||
|
||||
@@ -237,6 +238,7 @@ fn test_build_specs_multi_agent_v2_uses_task_names_and_hides_resume() {
|
||||
assert!(properties.contains_key("message"));
|
||||
assert!(properties.contains_key("fork_turns"));
|
||||
assert!(properties.contains_key("mcp_servers"));
|
||||
assert!(properties.contains_key("inject_skills_message"));
|
||||
assert!(!properties.contains_key("items"));
|
||||
assert!(!properties.contains_key("fork_context"));
|
||||
assert_eq!(
|
||||
|
||||
+7
-6
@@ -42,21 +42,22 @@ default_tools_approval_mode = "approve"
|
||||
approval_mode = "prompt"
|
||||
```
|
||||
|
||||
## Spawned agent MCP defaults
|
||||
## Spawned agent defaults
|
||||
|
||||
Use `[agents.spawn]` to set MCP defaults for newly spawned agents. `mcp_servers`
|
||||
Use `[agents.spawn]` to set defaults for newly spawned agents. `mcp_servers`
|
||||
is an optional allowlist of MCP server names; omit it to inherit all effective
|
||||
MCP servers, or set it to an empty list to disable MCP servers in spawned
|
||||
agents.
|
||||
agents. `inject_skills_message` controls whether spawned agents receive the
|
||||
skills developer message.
|
||||
|
||||
```toml
|
||||
[agents.spawn]
|
||||
mcp_servers = ["docs"]
|
||||
inject_skills_message = false
|
||||
```
|
||||
|
||||
The `spawn_agent` tool can override this setting per spawned agent. Full
|
||||
history forks inherit the parent session MCP settings and reject these
|
||||
overrides.
|
||||
The `spawn_agent` tool can override either setting per spawned agent. Full
|
||||
history forks inherit the parent session settings and reject these overrides.
|
||||
|
||||
## Apps (Connectors)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user