From 15b8cde2a4f63bb510d46c88d3ddd7e286bee203 Mon Sep 17 00:00:00 2001 From: jif-oai Date: Tue, 21 Apr 2026 21:54:58 +0100 Subject: [PATCH] chore: default multi-agent v2 fork to all (#18873) Default sub-agents v2 to `all` for the fork mode --- codex-rs/core/src/agent/control.rs | 8 ++++---- .../src/tools/handlers/multi_agents_common.rs | 2 +- .../src/tools/handlers/multi_agents_tests.rs | 15 +++++++-------- .../tools/handlers/multi_agents_v2/spawn.rs | 6 ++---- .../core/tests/suite/subagent_notifications.rs | 18 ++++++++++++------ 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/codex-rs/core/src/agent/control.rs b/codex-rs/core/src/agent/control.rs index 8004f6e6e..3c8b2cf3e 100644 --- a/codex-rs/core/src/agent/control.rs +++ b/codex-rs/core/src/agent/control.rs @@ -117,10 +117,10 @@ fn keep_forked_rollout_item(item: &RolloutItem) -> bool { | ResponseItem::Other, ) => false, RolloutItem::SessionState(_) => false, - RolloutItem::Compacted(_) - | RolloutItem::EventMsg(_) - | RolloutItem::SessionMeta(_) - | RolloutItem::TurnContext(_) => true, + // A forked child gets its own runtime config, including spawned-agent + // instructions, so it must establish a fresh context diff baseline. + RolloutItem::TurnContext(_) => false, + RolloutItem::Compacted(_) | RolloutItem::EventMsg(_) | RolloutItem::SessionMeta(_) => true, } } diff --git a/codex-rs/core/src/tools/handlers/multi_agents_common.rs b/codex-rs/core/src/tools/handlers/multi_agents_common.rs index 396496a35..5efe4e22c 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_common.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_common.rs @@ -243,7 +243,7 @@ pub(crate) fn reject_full_fork_spawn_overrides( ) -> Result<(), FunctionCallError> { if agent_type.is_some() || model.is_some() || reasoning_effort.is_some() { return Err(FunctionCallError::RespondToModel( - "Full-history forked agents inherit the parent agent type, model, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without fork_context/fork_turns=all.".to_string(), + "Full-history forked agents inherit the parent agent type, model, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without a full-history fork.".to_string(), )); } Ok(()) diff --git a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs index c46e98bce..199b1d037 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs @@ -431,7 +431,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, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without fork_context/fork_turns=all.".to_string(), + "Full-history forked agents inherit the parent agent type, model, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without a full-history fork.".to_string(), ) ); } @@ -465,7 +465,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, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without fork_context/fork_turns=all.".to_string(), + "Full-history forked agents inherit the parent agent type, model, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without a full-history fork.".to_string(), ) ); } @@ -509,13 +509,13 @@ 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, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without fork_context/fork_turns=all.".to_string(), + "Full-history forked agents inherit the parent agent type, model, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without a full-history fork.".to_string(), ) ); } #[tokio::test] -async fn multi_agent_v2_spawn_fork_turns_rejects_child_model_overrides() { +async fn multi_agent_v2_spawn_defaults_to_full_fork_and_rejects_child_model_overrides() { let (mut session, mut turn) = make_session_and_context().await; let manager = thread_manager(); let root = manager @@ -540,17 +540,16 @@ async fn multi_agent_v2_spawn_fork_turns_rejects_child_model_overrides() { "message": "inspect this repo", "task_name": "fork_context_v2", "model": "gpt-5-child-override", - "reasoning_effort": "low", - "fork_turns": "all" + "reasoning_effort": "low" })), )) .await - .expect_err("forked spawn should reject child model overrides"); + .expect_err("default full fork should reject child model overrides"); assert_eq!( err, FunctionCallError::RespondToModel( - "Full-history forked agents inherit the parent agent type, model, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without fork_context/fork_turns=all.".to_string(), + "Full-history forked agents inherit the parent agent type, model, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without a full-history fork.".to_string(), ) ); } diff --git a/codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs b/codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs index 6395a286c..fabb01b7d 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs @@ -244,14 +244,12 @@ impl SpawnAgentArgs { )); } - let Some(fork_turns) = self + let fork_turns = self .fork_turns .as_deref() .map(str::trim) .filter(|fork_turns| !fork_turns.is_empty()) - else { - return Ok(None); - }; + .unwrap_or("all"); if fork_turns.eq_ignore_ascii_case("none") { return Ok(None); diff --git a/codex-rs/core/tests/suite/subagent_notifications.rs b/codex-rs/core/tests/suite/subagent_notifications.rs index 04e418bb6..f12c68427 100644 --- a/codex-rs/core/tests/suite/subagent_notifications.rs +++ b/codex-rs/core/tests/suite/subagent_notifications.rs @@ -447,7 +447,7 @@ async fn spawned_multi_agent_v2_child_receives_xml_tagged_developer_context() -> let _child_request_log = mount_sse_once_match( &server, |req: &wiremock::Request| { - body_contains(req, CHILD_PROMPT) && !body_contains(req, SPAWN_CALL_ID) + body_contains(req, CHILD_PROMPT) && body_contains(req, "") }, sse(vec![ ev_response_created("resp-child-1"), @@ -458,7 +458,11 @@ async fn spawned_multi_agent_v2_child_receives_xml_tagged_developer_context() -> let _turn1_followup = mount_sse_once_match( &server, - |req: &wiremock::Request| body_contains(req, SPAWN_CALL_ID), + |req: &wiremock::Request| { + body_contains(req, "function_call_output") + && body_contains(req, "/root/worker") + && !body_contains(req, "") + }, sse(vec![ ev_response_created("resp-turn1-2"), ev_assistant_message("msg-turn1-2", "parent done"), @@ -493,7 +497,6 @@ async fn spawned_multi_agent_v2_child_receives_xml_tagged_developer_context() -> body_contains(request, CHILD_PROMPT) && body_contains(request, "") && body_contains(request, SPAWNED_AGENT_DEVELOPER_INSTRUCTIONS) - && !body_contains(request, SPAWN_CALL_ID) }) { break request; @@ -540,7 +543,7 @@ async fn skills_toggle_skips_instructions_for_parent_and_spawned_child() -> Resu let _child_request_log = mount_sse_once_match( &server, |req: &wiremock::Request| { - body_contains(req, CHILD_PROMPT) && !body_contains(req, SPAWN_CALL_ID) + body_contains(req, CHILD_PROMPT) && body_contains(req, "") }, sse(vec![ ev_response_created("resp-child-1"), @@ -551,7 +554,11 @@ async fn skills_toggle_skips_instructions_for_parent_and_spawned_child() -> Resu let _turn1_followup = mount_sse_once_match( &server, - |req: &wiremock::Request| body_contains(req, SPAWN_CALL_ID), + |req: &wiremock::Request| { + body_contains(req, "function_call_output") + && body_contains(req, "/root/worker") + && !body_contains(req, "") + }, sse(vec![ ev_response_created("resp-turn1-2"), ev_assistant_message("msg-turn1-2", "parent done"), @@ -594,7 +601,6 @@ async fn skills_toggle_skips_instructions_for_parent_and_spawned_child() -> Resu .find(|request| { body_contains(request, CHILD_PROMPT) && body_contains(request, "") - && !body_contains(request, SPAWN_CALL_ID) }) { break request;