feat: drop spawned-agent context instructions (#19127)

## Why

MultiAgentV2 children should not receive an extra model-visible
developer fragment just because they were spawned. The parent/configured
developer instructions should carry through normally, but the dedicated
`<spawned_agent_context>` block is no longer desired.

## What changed

- Removed the `SpawnAgentInstructions` context fragment and its
`<spawned_agent_context>` wrapper.
- Stopped appending spawned-agent instructions in
`codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs`.
- Updated subagent notification coverage to assert inherited parent
developer instructions without expecting the spawned-agent wrapper.

## Verification

- `cargo test -p codex-core --test all
spawned_multi_agent_v2_child_inherits_parent_developer_context --
--nocapture`
- `cargo test -p codex-core --test all
skills_toggle_skips_instructions_for_parent_and_spawned_child --
--nocapture`
- `cargo test -p codex-core --test all subagent_notifications --
--nocapture`
This commit is contained in:
jif-oai
2026-04-23 18:54:45 +02:00
committed by GitHub
parent e18bfeec91
commit a2f868c9d6
4 changed files with 7 additions and 54 deletions

View File

@@ -37,7 +37,6 @@ const REQUESTED_MODEL: &str = "gpt-5.4";
const REQUESTED_REASONING_EFFORT: ReasoningEffort = ReasoningEffort::Low;
const ROLE_MODEL: &str = "gpt-5.4";
const ROLE_REASONING_EFFORT: ReasoningEffort = ReasoningEffort::High;
const SPAWNED_AGENT_DEVELOPER_INSTRUCTIONS: &str = "You are a newly spawned agent in a team of agents collaborating to complete a task. You can spawn sub-agents to handle subtasks, and those sub-agents can spawn their own sub-agents. You are responsible for returning the response to your assigned task in the final channel. When you give your response, the contents of your response in the final channel will be immediately delivered back to your parent agent. The prior conversation history was forked from your parent agent. Treat the next user message as your assigned task, and use the forked history only as background context.";
fn body_contains(req: &wiremock::Request, text: &str) -> bool {
let is_zstd = req
@@ -425,7 +424,7 @@ async fn spawn_agent_requested_model_and_reasoning_override_inherited_settings_w
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn spawned_multi_agent_v2_child_receives_xml_tagged_developer_context() -> Result<()> {
async fn spawned_multi_agent_v2_child_inherits_parent_developer_context() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
@@ -446,9 +445,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, "<spawned_agent_context>")
},
|req: &wiremock::Request| body_contains(req, CHILD_PROMPT),
sse(vec![
ev_response_created("resp-child-1"),
ev_completed("resp-child-1"),
@@ -459,9 +456,7 @@ 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, "function_call_output")
&& body_contains(req, "/root/worker")
&& !body_contains(req, "<spawned_agent_context>")
body_contains(req, "function_call_output") && body_contains(req, "/root/worker")
},
sse(vec![
ev_response_created("resp-turn1-2"),
@@ -494,9 +489,7 @@ async fn spawned_multi_agent_v2_child_receives_xml_tagged_developer_context() ->
.unwrap_or_default()
.into_iter()
.find(|request| {
body_contains(request, CHILD_PROMPT)
&& body_contains(request, "<spawned_agent_context>")
&& body_contains(request, SPAWNED_AGENT_DEVELOPER_INSTRUCTIONS)
body_contains(request, CHILD_PROMPT) && !body_contains(request, SPAWN_CALL_ID)
})
{
break request;
@@ -510,11 +503,6 @@ async fn spawned_multi_agent_v2_child_receives_xml_tagged_developer_context() ->
&child_request,
"Parent developer instructions."
));
assert!(body_contains(&child_request, "<spawned_agent_context>"));
assert!(body_contains(
&child_request,
SPAWNED_AGENT_DEVELOPER_INSTRUCTIONS
));
assert!(body_contains(&child_request, CHILD_PROMPT));
Ok(())
@@ -542,9 +530,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, "<spawned_agent_context>")
},
|req: &wiremock::Request| body_contains(req, CHILD_PROMPT),
sse(vec![
ev_response_created("resp-child-1"),
ev_completed("resp-child-1"),
@@ -555,9 +541,7 @@ 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, "function_call_output")
&& body_contains(req, "/root/worker")
&& !body_contains(req, "<spawned_agent_context>")
body_contains(req, "function_call_output") && body_contains(req, "/root/worker")
},
sse(vec![
ev_response_created("resp-turn1-2"),
@@ -599,8 +583,7 @@ async fn skills_toggle_skips_instructions_for_parent_and_spawned_child() -> Resu
.unwrap_or_default()
.into_iter()
.find(|request| {
body_contains(request, CHILD_PROMPT)
&& body_contains(request, "<spawned_agent_context>")
body_contains(request, CHILD_PROMPT) && !body_contains(request, SPAWN_CALL_ID)
})
{
break request;