chore: unify max depth parameter (#12770)

Users were confused
This commit is contained in:
jif-oai
2026-02-25 15:20:24 +00:00
committed by GitHub
parent bccce0d75f
commit 01f25a7b96
6 changed files with 15 additions and 70 deletions

View File

@@ -41,12 +41,6 @@
"minimum": 1.0,
"type": "integer"
},
"max_spawn_depth": {
"description": "Maximum depth for thread-spawned subagents.",
"format": "uint",
"minimum": 1.0,
"type": "integer"
},
"max_threads": {
"description": "Maximum number of agent threads that can be open concurrently. When unset, no limit is enforced.",
"format": "uint",

View File

@@ -1,4 +1,3 @@
use crate::config::DEFAULT_AGENT_MAX_SPAWN_DEPTH;
use crate::error::CodexErr;
use crate::error::Result;
use codex_protocol::ThreadId;
@@ -43,10 +42,6 @@ pub(crate) fn next_thread_spawn_depth(session_source: &SessionSource) -> i32 {
session_depth(session_source).saturating_add(1)
}
pub(crate) fn max_thread_spawn_depth(max_depth: Option<usize>) -> i32 {
let max_depth = max_depth.or(DEFAULT_AGENT_MAX_SPAWN_DEPTH).unwrap_or(1);
i32::try_from(max_depth).unwrap_or(i32::MAX)
}
pub(crate) fn exceeds_thread_spawn_depth_limit(depth: i32, max_depth: i32) -> bool {
depth > max_depth
}

View File

@@ -6,6 +6,5 @@ pub(crate) mod status;
pub(crate) use codex_protocol::protocol::AgentStatus;
pub(crate) use control::AgentControl;
pub(crate) use guards::exceeds_thread_spawn_depth_limit;
pub(crate) use guards::max_thread_spawn_depth;
pub(crate) use guards::next_thread_spawn_depth;
pub(crate) use status::agent_status_from_event;

View File

@@ -115,7 +115,6 @@ pub use codex_git::GhostSnapshotConfig;
/// the context window.
pub(crate) const PROJECT_DOC_MAX_BYTES: usize = 32 * 1024; // 32 KiB
pub(crate) const DEFAULT_AGENT_MAX_THREADS: Option<usize> = Some(6);
pub(crate) const DEFAULT_AGENT_MAX_SPAWN_DEPTH: Option<usize> = Some(2);
pub(crate) const DEFAULT_AGENT_MAX_DEPTH: i32 = 1;
pub(crate) const DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS: Option<u64> = None;
@@ -357,8 +356,6 @@ pub struct Config {
/// Maximum number of agent threads that can be open concurrently.
pub agent_max_threads: Option<usize>,
/// Maximum depth for thread-spawned subagents.
pub agent_max_spawn_depth: Option<usize>,
/// Maximum runtime in seconds for agent job workers before they are failed.
pub agent_job_max_runtime_seconds: Option<u64>,
@@ -1340,9 +1337,6 @@ pub struct AgentsToml {
/// When unset, no limit is enforced.
#[schemars(range(min = 1))]
pub max_threads: Option<usize>,
/// Maximum depth for thread-spawned subagents.
#[schemars(range(min = 1))]
pub max_spawn_depth: Option<usize>,
/// Maximum nesting depth allowed for spawned agent threads.
/// Root sessions start at depth 0.
#[schemars(range(min = 1))]
@@ -1865,25 +1859,6 @@ impl Config {
})
.transpose()?
.unwrap_or_default();
let agent_max_spawn_depth = cfg
.agents
.as_ref()
.and_then(|agents| agents.max_spawn_depth)
.or(DEFAULT_AGENT_MAX_SPAWN_DEPTH);
if agent_max_spawn_depth == Some(0) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"agents.max_spawn_depth must be at least 1",
));
}
if let Some(max_spawn_depth) = agent_max_spawn_depth
&& max_spawn_depth > i32::MAX as usize
{
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"agents.max_spawn_depth must fit within a 32-bit signed integer",
));
}
let agent_job_max_runtime_seconds = cfg
.agents
.as_ref()
@@ -2149,7 +2124,6 @@ impl Config {
agent_max_depth,
agent_roles,
memories: cfg.memories.unwrap_or_default().into(),
agent_max_spawn_depth,
agent_job_max_runtime_seconds,
codex_home,
sqlite_home,
@@ -4487,7 +4461,6 @@ model = "gpt-5.1-codex"
let cfg = ConfigToml {
agents: Some(AgentsToml {
max_threads: None,
max_spawn_depth: None,
max_depth: None,
job_max_runtime_seconds: None,
roles: BTreeMap::from([(
@@ -4763,7 +4736,6 @@ model_verbosity = "high"
agent_max_depth: DEFAULT_AGENT_MAX_DEPTH,
agent_roles: BTreeMap::new(),
memories: MemoriesConfig::default(),
agent_max_spawn_depth: DEFAULT_AGENT_MAX_SPAWN_DEPTH,
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
codex_home: fixture.codex_home(),
sqlite_home: fixture.codex_home(),
@@ -4890,7 +4862,6 @@ model_verbosity = "high"
agent_max_depth: DEFAULT_AGENT_MAX_DEPTH,
agent_roles: BTreeMap::new(),
memories: MemoriesConfig::default(),
agent_max_spawn_depth: DEFAULT_AGENT_MAX_SPAWN_DEPTH,
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
codex_home: fixture.codex_home(),
sqlite_home: fixture.codex_home(),
@@ -5015,7 +4986,6 @@ model_verbosity = "high"
agent_max_depth: DEFAULT_AGENT_MAX_DEPTH,
agent_roles: BTreeMap::new(),
memories: MemoriesConfig::default(),
agent_max_spawn_depth: DEFAULT_AGENT_MAX_SPAWN_DEPTH,
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
codex_home: fixture.codex_home(),
sqlite_home: fixture.codex_home(),
@@ -5126,7 +5096,6 @@ model_verbosity = "high"
agent_max_depth: DEFAULT_AGENT_MAX_DEPTH,
agent_roles: BTreeMap::new(),
memories: MemoriesConfig::default(),
agent_max_spawn_depth: DEFAULT_AGENT_MAX_SPAWN_DEPTH,
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
codex_home: fixture.codex_home(),
sqlite_home: fixture.codex_home(),

View File

@@ -1,5 +1,4 @@
use crate::agent::exceeds_thread_spawn_depth_limit;
use crate::agent::max_thread_spawn_depth;
use crate::agent::next_thread_spawn_depth;
use crate::agent::status::is_final;
use crate::codex::Session;
@@ -531,7 +530,7 @@ async fn build_runner_options(
) -> Result<JobRunnerOptions, FunctionCallError> {
let session_source = turn.session_source.clone();
let child_depth = next_thread_spawn_depth(&session_source);
let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth);
let max_depth = turn.config.agent_max_depth;
if exceeds_thread_spawn_depth_limit(child_depth, max_depth) {
return Err(FunctionCallError::RespondToModel(
"agent depth limit reached; this session cannot spawn more subagents".to_string(),
@@ -540,7 +539,7 @@ async fn build_runner_options(
let max_concurrency =
normalize_concurrency(requested_concurrency, turn.config.agent_max_threads);
let base_instructions = session.get_base_instructions().await;
let spawn_config = build_agent_spawn_config(&base_instructions, turn.as_ref(), child_depth)?;
let spawn_config = build_agent_spawn_config(&base_instructions, turn.as_ref())?;
Ok(JobRunnerOptions {
max_concurrency,
spawn_config,

View File

@@ -1,6 +1,5 @@
use crate::agent::AgentStatus;
use crate::agent::exceeds_thread_spawn_depth_limit;
use crate::agent::max_thread_spawn_depth;
use crate::codex::Session;
use crate::codex::TurnContext;
use crate::config::Config;
@@ -96,7 +95,6 @@ mod spawn {
use crate::agent::role::apply_role_to_config;
use crate::agent::exceeds_thread_spawn_depth_limit;
use crate::agent::max_thread_spawn_depth;
use crate::agent::next_thread_spawn_depth;
use std::sync::Arc;
@@ -129,7 +127,7 @@ mod spawn {
let prompt = input_preview(&input_items);
let session_source = turn.session_source.clone();
let child_depth = next_thread_spawn_depth(&session_source);
let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth);
let max_depth = turn.config.agent_max_depth;
if exceeds_thread_spawn_depth_limit(child_depth, max_depth) {
return Err(FunctionCallError::RespondToModel(
"Agent depth limit reached. Solve the task yourself.".to_string(),
@@ -146,11 +144,8 @@ mod spawn {
.into(),
)
.await;
let mut config = build_agent_spawn_config(
&session.get_base_instructions().await,
turn.as_ref(),
child_depth,
)?;
let mut config =
build_agent_spawn_config(&session.get_base_instructions().await, turn.as_ref())?;
apply_role_to_config(&mut config, role_name)
.await
.map_err(FunctionCallError::RespondToModel)?;
@@ -346,7 +341,7 @@ mod resume_agent {
.await
.unwrap_or((None, None));
let child_depth = next_thread_spawn_depth(&turn.session_source);
let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth);
let max_depth = turn.config.agent_max_depth;
if exceeds_thread_spawn_depth_limit(child_depth, max_depth) {
return Err(FunctionCallError::RespondToModel(
"Agent depth limit reached. Solve the task yourself.".to_string(),
@@ -896,9 +891,8 @@ fn input_preview(items: &[UserInput]) -> String {
pub(crate) fn build_agent_spawn_config(
base_instructions: &BaseInstructions,
turn: &TurnContext,
child_depth: i32,
) -> Result<Config, FunctionCallError> {
let mut config = build_agent_shared_config(turn, child_depth)?;
let mut config = build_agent_shared_config(turn)?;
config.base_instructions = Some(base_instructions.text.clone());
Ok(config)
}
@@ -907,16 +901,14 @@ fn build_agent_resume_config(
turn: &TurnContext,
child_depth: i32,
) -> Result<Config, FunctionCallError> {
let mut config = build_agent_shared_config(turn, child_depth)?;
let mut config = build_agent_shared_config(turn)?;
apply_spawn_agent_overrides(&mut config, child_depth);
// For resume, keep base instructions sourced from rollout/session metadata.
config.base_instructions = None;
Ok(config)
}
fn build_agent_shared_config(
turn: &TurnContext,
child_depth: i32,
) -> Result<Config, FunctionCallError> {
fn build_agent_shared_config(turn: &TurnContext) -> Result<Config, FunctionCallError> {
let base_config = turn.config.clone();
let mut config = (*base_config).clone();
config.model = Some(turn.model_info.slug.clone());
@@ -926,7 +918,6 @@ fn build_agent_shared_config(
config.developer_instructions = turn.developer_instructions.clone();
config.compact_prompt = turn.compact_prompt.clone();
apply_spawn_agent_runtime_overrides(&mut config, turn)?;
apply_spawn_agent_overrides(&mut config, child_depth);
Ok(config)
}
@@ -956,8 +947,7 @@ fn apply_spawn_agent_runtime_overrides(
}
fn apply_spawn_agent_overrides(config: &mut Config, child_depth: i32) {
let max_depth = max_thread_spawn_depth(config.agent_max_spawn_depth);
if exceeds_thread_spawn_depth_limit(child_depth + 1, max_depth) {
if child_depth >= config.agent_max_depth {
config.features.disable(Feature::Collab);
}
}
@@ -968,7 +958,6 @@ mod tests {
use crate::AuthManager;
use crate::CodexAuth;
use crate::ThreadManager;
use crate::agent::max_thread_spawn_depth;
use crate::built_in_model_providers;
use crate::codex::make_session_and_context;
use crate::config::DEFAULT_AGENT_MAX_DEPTH;
@@ -1273,7 +1262,7 @@ mod tests {
let manager = thread_manager();
session.services.agent_control = manager.agent_control();
let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth);
let max_depth = turn.config.agent_max_depth;
turn.session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
parent_thread_id: session.conversation_id,
depth: max_depth,
@@ -1704,7 +1693,7 @@ mod tests {
let manager = thread_manager();
session.services.agent_control = manager.agent_control();
let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth);
let max_depth = turn.config.agent_max_depth;
turn.session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
parent_thread_id: session.conversation_id,
depth: max_depth,
@@ -2051,7 +2040,7 @@ mod tests {
.set(AskForApproval::OnRequest)
.expect("approval policy set");
let config = build_agent_spawn_config(&base_instructions, &turn, 0).expect("spawn config");
let config = build_agent_spawn_config(&base_instructions, &turn).expect("spawn config");
let mut expected = (*turn.config).clone();
expected.base_instructions = Some(base_instructions.text);
expected.model = Some(turn.model_info.slug.clone());
@@ -2087,7 +2076,7 @@ mod tests {
text: "base".to_string(),
};
let config = build_agent_spawn_config(&base_instructions, &turn, 0).expect("spawn config");
let config = build_agent_spawn_config(&base_instructions, &turn).expect("spawn config");
assert_eq!(config.user_instructions, base_config.user_instructions);
}