Set multi-agent v2 dogfood defaults (#25266)

## Summary
- default multi-agent v2 to direct-model-only tools so code mode does
not wrap subagent tools
- add default root/subagent team prompts aligned with dogfood training
assumptions
- tighten spawn-agent model override wording to prefer the inherited
model by default

## Tests
- just fmt
- just test -p codex-core
spawn_agent_description_lists_visible_models_and_reasoning_efforts
- just test -p codex-core
multi_agent_v2_default_session_thread_cap_counts_root
- just test -p codex-rollout-trace
- just fix -p codex-core
- just fix -p codex-rollout-trace

Note: a broad just test -p codex-core run was attempted locally, but
this sandbox produced unrelated environment failures around
sandbox-exec, missing test_stdio_server, and realtime timeouts.
This commit is contained in:
jif-oai
2026-06-01 10:24:46 +02:00
committed by GitHub
Unverified
parent cf0911076f
commit 8d49394feb
8 changed files with 201 additions and 15 deletions
+1
View File
@@ -475,6 +475,7 @@ impl AgentControl {
}
if preserve_reference_context_item
&& config.features.enabled(Feature::MultiAgentV2)
&& config.multi_agent_v2.usage_hint_enabled
&& let Some(subagent_usage_hint_text) =
config.multi_agent_v2.subagent_usage_hint_text.clone()
&& let Some(subagent_usage_hint_message) =
+52
View File
@@ -717,6 +717,53 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
"full-history forked child should preserve the parent diff baseline"
);
let mut disabled_hint_child_config = harness.config.clone();
let _ = disabled_hint_child_config
.features
.enable(Feature::MultiAgentV2);
disabled_hint_child_config.multi_agent_v2.usage_hint_enabled = false;
disabled_hint_child_config
.multi_agent_v2
.subagent_usage_hint_text = Some("Disabled child subagent guidance.".to_string());
let disabled_hint_child_thread_id = harness
.control
.spawn_agent_with_metadata(
disabled_hint_child_config,
text_input("child task without hints"),
Some(SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
parent_thread_id,
depth: 1,
agent_path: None,
agent_nickname: None,
agent_role: None,
})),
SpawnAgentOptions {
fork_parent_spawn_call_id: Some(parent_spawn_call_id.clone()),
fork_mode: Some(SpawnAgentForkMode::FullHistory),
..Default::default()
},
)
.await
.expect("forked spawn should honor disabled usage hints")
.thread_id;
let disabled_hint_child_thread = harness
.manager
.get_thread(disabled_hint_child_thread_id)
.await
.expect("disabled-hint child thread should be registered");
let disabled_hint_history = disabled_hint_child_thread
.codex
.session
.clone_history()
.await;
assert!(
!history_contains_text(
disabled_hint_history.raw_items(),
"Disabled child subagent guidance.",
),
"full-history forked child should not add subagent guidance when usage hints are disabled"
);
let expected = (
child_thread_id,
Op::UserInput {
@@ -743,6 +790,11 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
.shutdown_live_agent(child_thread_id)
.await
.expect("child shutdown should submit");
let _ = harness
.control
.shutdown_live_agent(disabled_hint_child_thread_id)
.await
.expect("disabled-hint child shutdown should submit");
let _ = parent_thread
.submit(Op::Shutdown {})
.await
+49 -1
View File
@@ -9846,7 +9846,55 @@ enabled = true
assert_eq!(config.multi_agent_v2.max_wait_timeout_ms, 3_600_000);
assert_eq!(config.multi_agent_v2.default_wait_timeout_ms, 30_000);
assert_eq!(config.agent_max_threads, Some(3));
assert!(!config.multi_agent_v2.non_code_mode_only);
assert_eq!(
config.multi_agent_v2.root_agent_usage_hint_text.as_deref(),
Some(DEFAULT_MULTI_AGENT_V2_ROOT_AGENT_USAGE_HINT_TEXT)
);
assert!(
!config
.multi_agent_v2
.root_agent_usage_hint_text
.as_deref()
.unwrap_or_default()
.contains("maximum concurrency"),
);
assert_eq!(
config.multi_agent_v2.subagent_usage_hint_text.as_deref(),
Some(DEFAULT_MULTI_AGENT_V2_SUBAGENT_USAGE_HINT_TEXT)
);
assert!(
!config
.multi_agent_v2
.subagent_usage_hint_text
.as_deref()
.unwrap_or_default()
.contains("maximum concurrency"),
);
assert!(config.multi_agent_v2.non_code_mode_only);
Ok(())
}
#[tokio::test]
async fn multi_agent_v2_empty_usage_hint_overrides_clear_default_hints() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
r#"[features.multi_agent_v2]
enabled = true
root_agent_usage_hint_text = ""
subagent_usage_hint_text = ""
"#,
)?;
let config = ConfigBuilder::without_managed_config_for_tests()
.codex_home(codex_home.path().to_path_buf())
.fallback_cwd(Some(codex_home.path().to_path_buf()))
.build()
.await?;
assert_eq!(config.multi_agent_v2.root_agent_usage_hint_text, None);
assert_eq!(config.multi_agent_v2.subagent_usage_hint_text, None);
Ok(())
}
+64 -11
View File
@@ -189,6 +189,44 @@ pub(crate) const DEFAULT_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION: usiz
pub(crate) const DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS: i64 = 10_000;
pub(crate) const DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS: i64 = 3600 * 1000;
pub(crate) const DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS: i64 = 30_000;
const DEFAULT_MULTI_AGENT_V2_ROOT_AGENT_USAGE_HINT_TEXT: &str = r#"You are `/root`, the primary agent in a team of agents collaborating to fulfill the user's goals.
At the start of your turn, you are the active agent.
You can spawn sub-agents to handle subtasks, and those sub-agents can spawn their own sub-agents.
All agents in the team, including the agents that you can assign tasks to, are equally intelligent and capable, and have access to the same set of tools.
You can use `spawn_agent` to create a new agent, `assign_task` to give an existing agent a new task and trigger a turn, and `send_message` to pass a message to a running agent without triggering a turn.
Child agents can also spawn their own sub-agents.
You can decide how much context you want to propagate to your sub-agents with the `fork_turns` parameter.
You will receive messages in the analysis channel in the form:
```
Message Type: MESSAGE | FINAL_ANSWER
Sender: <author>
Payload:
<payload text>
```
They may be addressed as to=/root
"#;
const DEFAULT_MULTI_AGENT_V2_SUBAGENT_USAGE_HINT_TEXT: &str = r#"You are an 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. All agents in the team, including the agents that you can assign tasks to, are equally intelligent and capable, and have access to the same set of tools.
You can use `spawn_agent` to create a new agent, `assign_task` to give an existing agent a new task and trigger a turn, and `send_message` to pass a message to a running agent.
Child agents can also spawn their own sub-agents.
When you provide a response in the final channel, that content is immediately delivered back to your parent agent.
You will receive messages in the analysis channel in the form:
```
Message Type: NEW_TASK | MESSAGE | FINAL_ANSWER
Task name: <recipient> # only for NEW_TASK -- this determines your identity
Sender: <author>
Payload:
<payload text>
```
You may also see them addressed as to=/root/..., which indicates your identity is /root/...
"#;
pub(crate) const HARD_MIN_MULTI_AGENT_V2_TIMEOUT_MS: i64 = 0;
pub(crate) const HARD_MAX_MULTI_AGENT_V2_TIMEOUT_MS: i64 =
DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS;
@@ -1019,11 +1057,15 @@ impl Default for MultiAgentV2Config {
default_wait_timeout_ms: DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS,
usage_hint_enabled: true,
usage_hint_text: None,
root_agent_usage_hint_text: None,
subagent_usage_hint_text: None,
root_agent_usage_hint_text: Some(
DEFAULT_MULTI_AGENT_V2_ROOT_AGENT_USAGE_HINT_TEXT.to_string(),
),
subagent_usage_hint_text: Some(
DEFAULT_MULTI_AGENT_V2_SUBAGENT_USAGE_HINT_TEXT.to_string(),
),
tool_namespace: None,
hide_spawn_agent_metadata: false,
non_code_mode_only: false,
non_code_mode_only: true,
}
}
}
@@ -2239,14 +2281,14 @@ fn resolve_multi_agent_v2_config(config_toml: &ConfigToml) -> MultiAgentV2Config
.and_then(|config| config.usage_hint_text.as_ref())
.cloned()
.or(default.usage_hint_text);
let root_agent_usage_hint_text = base
.and_then(|config| config.root_agent_usage_hint_text.as_ref())
.cloned()
.or(default.root_agent_usage_hint_text);
let subagent_usage_hint_text = base
.and_then(|config| config.subagent_usage_hint_text.as_ref())
.cloned()
.or(default.subagent_usage_hint_text);
let root_agent_usage_hint_text = resolve_optional_prompt_text(
base.map(|config| &config.root_agent_usage_hint_text),
default.root_agent_usage_hint_text,
);
let subagent_usage_hint_text = resolve_optional_prompt_text(
base.map(|config| &config.subagent_usage_hint_text),
default.subagent_usage_hint_text,
);
let tool_namespace = base
.and_then(|config| config.tool_namespace.as_ref())
.cloned()
@@ -2287,6 +2329,17 @@ fn resolve_terminal_resize_reflow_config(config_toml: &ConfigToml) -> TerminalRe
}
}
fn resolve_optional_prompt_text(
configured: Option<&Option<String>>,
default: Option<String>,
) -> Option<String> {
match configured {
Some(Some(value)) if value.is_empty() => None,
Some(Some(value)) => Some(value.clone()),
Some(None) | None => default,
}
}
fn multi_agent_v2_toml_config(features: Option<&FeaturesToml>) -> Option<&MultiAgentV2ConfigToml> {
match features?.multi_agent_v2.as_ref()? {
FeatureToml::Enabled(_) => None,
@@ -12,6 +12,10 @@ pub(super) fn usage_hint_text<'a>(
}
let multi_agent_v2 = &turn_context.config.multi_agent_v2;
if !multi_agent_v2.usage_hint_enabled {
return None;
}
match session_source {
SessionSource::SubAgent(SubAgentSource::ThreadSpawn { .. }) => {
multi_agent_v2.subagent_usage_hint_text.as_deref()
+28
View File
@@ -7185,6 +7185,34 @@ async fn build_initial_context_omits_multi_agent_v2_usage_hints_when_feature_dis
);
}
#[tokio::test]
async fn build_initial_context_omits_multi_agent_v2_usage_hints_when_hint_disabled() {
let (session, turn_context, _rx_event) = make_session_and_context_with_auth_and_config_and_rx(
CodexAuth::from_api_key("Test API Key"),
Vec::new(),
|config| {
let _ = config.features.enable(Feature::MultiAgentV2);
config.multi_agent_v2.usage_hint_enabled = false;
config.multi_agent_v2.root_agent_usage_hint_text = Some("Root guidance.".to_string());
config.multi_agent_v2.subagent_usage_hint_text = Some("Subagent guidance.".to_string());
},
)
.await;
let initial_context = session.build_initial_context(turn_context.as_ref()).await;
let developer_messages = developer_message_texts(&initial_context);
assert!(
!developer_messages.iter().any(|message| {
matches!(
message.as_slice(),
["Root guidance."] | ["Subagent guidance."]
)
}),
"did not expect multi-agent v2 usage hint developer messages, got {developer_messages:?}"
);
}
#[tokio::test]
async fn build_initial_context_omits_default_image_save_location_with_image_history() {
let (session, turn_context) = make_session_and_context().await;
@@ -11,9 +11,9 @@ use std::collections::BTreeMap;
pub const MULTI_AGENT_V1_NAMESPACE: &str = "multi_agent_v1";
const MULTI_AGENT_V1_NAMESPACE_DESCRIPTION: &str = "Tools for spawning and managing sub-agents.";
const SPAWN_AGENT_INHERITED_MODEL_GUIDANCE: &str = "Spawned agents inherit your current model by default. If provided, `model` specifies the model to use for the spawned agent.";
const SPAWN_AGENT_INHERITED_MODEL_GUIDANCE: &str = "Spawned agents inherit your current model by default. Omit `model` to use that preferred default; set `model` only when an explicit override is needed.";
const SPAWN_AGENT_MODEL_OVERRIDE_DESCRIPTION: &str =
"Model override for the new agent. Omit to inherit the parent model.";
"Model override for the new agent. Omit unless an explicit override is needed.";
const SPAWN_AGENT_SERVICE_TIER_OVERRIDE_DESCRIPTION: &str =
"Service tier override for the new agent. Omit unless explicitly requested.";
const MAX_MODEL_OVERRIDES_IN_SPAWN_AGENT_DESCRIPTION: usize = 5;
@@ -184,7 +184,7 @@ async fn spawn_agent_description_lists_visible_models_and_reasoning_efforts() ->
);
assert!(
description.contains(
"Spawned agents inherit your current model by default. If provided, `model` specifies the model to use for the spawned agent."
"Spawned agents inherit your current model by default. Omit `model` to use that preferred default; set `model` only when an explicit override is needed."
),
"expected inherited-model guidance in spawn_agent description: {description:?}"
);