mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user