feat(core): force forked agents to inherit parent model

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Friel
2026-03-27 16:29:37 -07:00
parent 6c36e7d688
commit 1b4579f4a4
3 changed files with 120 additions and 18 deletions

View File

@@ -59,16 +59,21 @@ impl ToolHandler for Handler {
.into(),
)
.await;
let mut config =
build_agent_spawn_config(&session.get_base_instructions().await, turn.as_ref())?;
apply_requested_spawn_agent_model_overrides(
&session,
let mut config = build_agent_spawn_config(
session.get_base_instructions().await.as_ref(),
turn.as_ref(),
&mut config,
args.model.as_deref(),
args.reasoning_effort,
)
.await?;
)?;
if !args.fork_context {
apply_requested_spawn_agent_model_overrides(
&session,
turn.as_ref(),
&mut config,
args.model.as_deref(),
args.reasoning_effort,
)
.await?;
}
apply_role_to_config(&mut config, role_name)
.await
.map_err(FunctionCallError::RespondToModel)?;

View File

@@ -366,6 +366,98 @@ async fn spawn_agent_uses_explorer_role_and_preserves_approval_policy() {
assert_eq!(snapshot.model_provider_id, "ollama");
}
#[tokio::test]
async fn spawn_agent_fork_context_ignores_child_model_overrides() {
let (mut session, turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
session.conversation_id = root.thread_id;
let expected_model = turn.model_info.slug.clone();
let expected_reasoning_effort = turn.reasoning_effort;
let output = SpawnAgentHandler
.handle(invocation(
Arc::new(session),
Arc::new(turn),
"spawn_agent",
function_payload(json!({
"message": "inspect this repo",
"model": "not-a-real-model",
"reasoning_effort": "low",
"fork_context": true
})),
))
.await
.expect("spawn_agent should succeed");
let (content, _) = expect_text_output(output);
let result: serde_json::Value =
serde_json::from_str(&content).expect("spawn_agent result should be json");
let agent_id = parse_agent_id(
result["agent_id"]
.as_str()
.expect("spawn_agent result should include agent_id"),
);
let snapshot = manager
.get_thread(agent_id)
.await
.expect("spawned agent thread should exist")
.config_snapshot()
.await;
assert_eq!(snapshot.model, expected_model);
assert_eq!(snapshot.reasoning_effort, expected_reasoning_effort);
}
#[tokio::test]
async fn multi_agent_v2_spawn_fork_context_ignores_child_model_overrides() {
let (mut session, turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
session.conversation_id = root.thread_id;
let expected_model = turn.model_info.slug.clone();
let expected_reasoning_effort = turn.reasoning_effort;
let output = SpawnAgentHandlerV2
.handle(invocation(
Arc::new(session),
Arc::new(turn),
"spawn_agent",
function_payload(json!({
"message": "inspect this repo",
"model": "not-a-real-model",
"reasoning_effort": "low",
"fork_context": true
})),
))
.await
.expect("spawn_agent should succeed");
let (content, _) = expect_text_output(output);
let result: serde_json::Value =
serde_json::from_str(&content).expect("spawn_agent result should be json");
let agent_id = parse_agent_id(
result["agent_id"]
.as_str()
.expect("spawn_agent result should include agent_id"),
);
let snapshot = manager
.get_thread(agent_id)
.await
.expect("spawned agent thread should exist")
.config_snapshot()
.await;
assert_eq!(snapshot.model, expected_model);
assert_eq!(snapshot.reasoning_effort, expected_reasoning_effort);
}
#[tokio::test]
async fn spawn_agent_returns_agent_id_without_task_name() {
let (mut session, turn) = make_session_and_context().await;

View File

@@ -69,16 +69,21 @@ impl ToolHandler for Handler {
.into(),
)
.await;
let mut config =
build_agent_spawn_config(&session.get_base_instructions().await, turn.as_ref())?;
apply_requested_spawn_agent_model_overrides(
&session,
let mut config = build_agent_spawn_config(
session.get_base_instructions().await.as_ref(),
turn.as_ref(),
&mut config,
args.model.as_deref(),
args.reasoning_effort,
)
.await?;
)?;
if fork_mode.is_none() {
apply_requested_spawn_agent_model_overrides(
&session,
turn.as_ref(),
&mut config,
args.model.as_deref(),
args.reasoning_effort,
)
.await?;
}
apply_role_to_config(&mut config, role_name)
.await
.map_err(FunctionCallError::RespondToModel)?;