Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions codex-rs/config/src/config_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,14 +678,23 @@ where
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)]
#[schemars(deny_unknown_fields)]
pub struct AgentsToml {
/// Maximum number of agent threads that can be open concurrently.
/// When unset, no limit is enforced.
#[schemars(range(min = 1))]
pub max_threads: Option<usize>,
/// Maximum nesting depth allowed for spawned agent threads.
/// Root sessions start at depth 0.
/// Whether multi-agent tools are enabled. Defaults to true.
/// An enabled `features.multi_agent_v2` setting takes precedence.
pub enabled: Option<bool>,
/// Maximum number of spawned agent threads that can be open concurrently per session.
/// When unset, the selected multi-agent backend uses its default.
#[serde(alias = "max_threads")]
#[schemars(range(min = 1))]
pub max_concurrent_threads_per_session: Option<usize>,
/// Maximum nesting depth for V1 agent threads. Ignored by V2.
pub max_depth: Option<i32>,
/// Reserved for controlling whether the spawn tool supports agent types.
/// Currently ignored.
pub support_agent_type: Option<bool>,
/// Reserved default model for spawned subagents. Currently ignored.
pub default_subagent_model: Option<String>,
/// Reserved default reasoning effort for spawned subagents. Currently ignored.
pub default_subagent_reasoning_effort: Option<ReasoningEffort>,
/// Default maximum runtime in seconds for agent job workers.
#[schemars(range(min = 1))]
pub job_max_runtime_seconds: Option<u64>,
Expand Down
17 changes: 12 additions & 5 deletions codex-rs/config/src/key_aliases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ struct ConfigKeyAlias {
canonical_key: &'static str,
}

const CONFIG_KEY_ALIASES: &[ConfigKeyAlias] = &[ConfigKeyAlias {
table_path: &["memories"],
legacy_key: "no_memories_if_mcp_or_web_search",
canonical_key: "disable_on_external_context",
}];
const CONFIG_KEY_ALIASES: &[ConfigKeyAlias] = &[
ConfigKeyAlias {
table_path: &["memories"],
legacy_key: "no_memories_if_mcp_or_web_search",
canonical_key: "disable_on_external_context",
},
ConfigKeyAlias {
table_path: &["agents"],
legacy_key: "max_threads",
canonical_key: "max_concurrent_threads_per_session",
},
];

pub(crate) fn normalize_key_aliases(path: &[String], table: &mut TomlMap<String, TomlValue>) {
for alias in CONFIG_KEY_ALIASES {
Expand Down
62 changes: 62 additions & 0 deletions codex-rs/config/src/merge_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::*;
use crate::config_toml::AgentsToml;
use crate::config_toml::ConfigToml;
use crate::types::MemoriesToml;
use pretty_assertions::assert_eq;
Expand Down Expand Up @@ -99,6 +100,67 @@ disable_on_external_context = true
assert_eq!(base, expected);
}

#[test]
fn merge_toml_values_normalizes_legacy_agents_key_across_layers() {
let mut base = parse_toml(
r#"
[agents]
max_threads = 4
"#,
);
let overlay = parse_toml(
r#"
[agents]
max_concurrent_threads_per_session = 7
"#,
);

merge_toml_values(&mut base, &overlay);

let expected = parse_toml(
r#"
[agents]
max_concurrent_threads_per_session = 7
"#,
);
assert_eq!(base, expected);

let config: ConfigToml = base.try_into().expect("merged config should deserialize");
assert_eq!(
config.agents,
Some(AgentsToml {
max_concurrent_threads_per_session: Some(7),
..Default::default()
})
);
}

#[test]
fn merge_toml_values_normalizes_legacy_agents_key_from_overlay() {
let mut base = parse_toml(
r#"
[agents]
max_concurrent_threads_per_session = 4
"#,
);
let overlay = parse_toml(
r#"
[agents]
max_threads = 7
"#,
);

merge_toml_values(&mut base, &overlay);

let expected = parse_toml(
r#"
[agents]
max_concurrent_threads_per_session = 7
"#,
);
assert_eq!(base, expected);
}

#[test]
fn merge_toml_values_normalizes_permission_network_domains_before_overlaying() {
let mut base = parse_toml(
Expand Down
33 changes: 26 additions & 7 deletions codex-rs/core/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@
"$ref": "#/definitions/AgentRoleToml"
},
"properties": {
"default_subagent_model": {
"description": "Reserved default model for spawned subagents. Currently ignored.",
"type": "string"
},
"default_subagent_reasoning_effort": {
"allOf": [
{
"$ref": "#/definitions/ReasoningEffort"
}
],
"description": "Reserved default reasoning effort for spawned subagents. Currently ignored."
},
"enabled": {
"description": "Whether multi-agent tools are enabled. Defaults to true. An enabled `features.multi_agent_v2` setting takes precedence.",
"type": "boolean"
},
"interrupt_message": {
"description": "Whether to record a model-visible message when an agent turn is interrupted. Defaults to true.",
"type": "boolean"
Expand All @@ -46,17 +62,20 @@
"minimum": 1.0,
"type": "integer"
},
"max_depth": {
"description": "Maximum nesting depth allowed for spawned agent threads. Root sessions start at depth 0.",
"format": "int32",
"max_concurrent_threads_per_session": {
"description": "Maximum number of spawned agent threads that can be open concurrently per session. When unset, the selected multi-agent backend uses its default.",
"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",
"minimum": 1.0,
"max_depth": {
"description": "Maximum nesting depth for V1 agent threads. Ignored by V2.",
"format": "int32",
"type": "integer"
},
"support_agent_type": {
"description": "Reserved for controlling whether the spawn tool supports agent types. Currently ignored.",
"type": "boolean"
}
},
"type": "object"
Expand Down
10 changes: 5 additions & 5 deletions codex-rs/core/src/agent/control_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1953,7 +1953,7 @@ async fn spawn_agent_fork_last_n_turns_strips_parent_usage_hints() {
}

#[tokio::test]
async fn spawn_agent_respects_max_threads_limit() {
async fn spawn_agent_respects_legacy_max_threads_alias() {
let max_threads = 1usize;
let (_home, config) = test_config_with_cli_overrides(vec![(
"agents.max_threads".to_string(),
Expand Down Expand Up @@ -2008,7 +2008,7 @@ async fn spawn_agent_respects_max_threads_limit() {
async fn spawn_agent_releases_slot_after_shutdown() {
let max_threads = 1usize;
let (_home, config) = test_config_with_cli_overrides(vec![(
"agents.max_threads".to_string(),
"agents.max_concurrent_threads_per_session".to_string(),
TomlValue::Integer(max_threads as i64),
)])
.await;
Expand Down Expand Up @@ -2051,7 +2051,7 @@ async fn spawn_agent_releases_slot_after_shutdown() {
async fn spawn_agent_limit_shared_across_clones() {
let max_threads = 1usize;
let (_home, config) = test_config_with_cli_overrides(vec![(
"agents.max_threads".to_string(),
"agents.max_concurrent_threads_per_session".to_string(),
TomlValue::Integer(max_threads as i64),
)])
.await;
Expand Down Expand Up @@ -2096,7 +2096,7 @@ async fn spawn_agent_limit_shared_across_clones() {
async fn resume_agent_respects_max_threads_limit() {
let max_threads = 1usize;
let (_home, config) = test_config_with_cli_overrides(vec![(
"agents.max_threads".to_string(),
"agents.max_concurrent_threads_per_session".to_string(),
TomlValue::Integer(max_threads as i64),
)])
.await;
Expand Down Expand Up @@ -2152,7 +2152,7 @@ async fn resume_agent_respects_max_threads_limit() {
async fn resume_agent_releases_slot_after_resume_failure() {
let max_threads = 1usize;
let (_home, config) = test_config_with_cli_overrides(vec![(
"agents.max_threads".to_string(),
"agents.max_concurrent_threads_per_session".to_string(),
TomlValue::Integer(max_threads as i64),
)])
.await;
Expand Down
Loading
Loading