From 2da9e1b7f27404449fd9a5ee86959cb34b25dde4 Mon Sep 17 00:00:00 2001 From: Friel Date: Mon, 30 Mar 2026 00:39:45 +0000 Subject: [PATCH 1/2] feat(core): remove spawn_mode from agent roles --- codex-rs/core/src/agent/control_tests.rs | 1 - codex-rs/core/src/agent/role.rs | 15 --------------- codex-rs/core/src/agent/role_tests.rs | 16 ---------------- codex-rs/core/src/codex_tests.rs | 1 - codex-rs/core/src/config/agent_roles.rs | 3 --- codex-rs/core/src/config/config_tests.rs | 5 ----- codex-rs/core/src/config/mod.rs | 13 ------------- .../core/tests/suite/subagent_notifications.rs | 2 -- 8 files changed, 56 deletions(-) diff --git a/codex-rs/core/src/agent/control_tests.rs b/codex-rs/core/src/agent/control_tests.rs index e7e700d71b..a34f2c82c3 100644 --- a/codex-rs/core/src/agent/control_tests.rs +++ b/codex-rs/core/src/agent/control_tests.rs @@ -1552,7 +1552,6 @@ async fn spawn_thread_subagent_uses_role_specific_nickname_candidates() { description: Some("Research role".to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: Some(vec!["Atlas".to_string()]), fork_context: None, diff --git a/codex-rs/core/src/agent/role.rs b/codex-rs/core/src/agent/role.rs index cde3e3c064..b84382ae3c 100644 --- a/codex-rs/core/src/agent/role.rs +++ b/codex-rs/core/src/agent/role.rs @@ -7,7 +7,6 @@ //! which role to use; the multi-agent tool handler owns that orchestration. use crate::config::AgentRoleConfig; -use crate::config::AgentRoleSpawnMode; use crate::config::Config; use crate::config::ConfigOverrides; use crate::config::agent_roles::parse_agent_role_file_contents; @@ -28,16 +27,6 @@ use toml::Value as TomlValue; pub const DEFAULT_ROLE_NAME: &str = "default"; const AGENT_TYPE_UNAVAILABLE_ERROR: &str = "agent type is currently not available"; -pub(crate) fn default_spawn_mode_for_role( - config: &Config, - role_name: Option<&str>, -) -> AgentRoleSpawnMode { - let role_name = role_name.unwrap_or(DEFAULT_ROLE_NAME); - resolve_role_config(config, role_name) - .and_then(|role| role.spawn_mode) - .unwrap_or_default() -} - pub(crate) fn watchdog_interval_for_role(config: &Config, role_name: Option<&str>) -> Option { let role_name = role_name.unwrap_or(DEFAULT_ROLE_NAME); resolve_role_config(config, role_name).and_then(|role| role.watchdog_interval_s) @@ -384,7 +373,6 @@ mod built_in { description: Some("Default agent.".to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: Some(true), @@ -402,7 +390,6 @@ Rules: - Reuse existing explorers for related questions."#.to_string()), model: None, config_file: Some("explorer.toml".to_string().parse().unwrap_or_default()), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: Some(true), @@ -421,7 +408,6 @@ Rules: - Always tell workers they are **not alone in the codebase**, and they should not revert the edits made by others, and they should adjust their implementation to accommodate the changes made by others. This is important because there may be multiple workers making changes in parallel, and they need to be aware of each other's work to avoid conflicts and ensure a cohesive final product."#.to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: Some(true), @@ -438,7 +424,6 @@ Rules: - Close the watchdog handle only when it is no longer needed or when replacing it with a new watchdog."#.to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: Some(crate::config::DEFAULT_WATCHDOG_INTERVAL_S), nickname_candidates: None, fork_context: Some(true), diff --git a/codex-rs/core/src/agent/role_tests.rs b/codex-rs/core/src/agent/role_tests.rs index a2fb057e7b..b7e1bc5cab 100644 --- a/codex-rs/core/src/agent/role_tests.rs +++ b/codex-rs/core/src/agent/role_tests.rs @@ -96,7 +96,6 @@ async fn apply_role_returns_unavailable_for_missing_user_role_file() { description: None, model: None, config_file: Some(PathBuf::from("/path/does/not/exist.toml")), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -120,7 +119,6 @@ async fn apply_role_returns_unavailable_for_invalid_user_role_toml() { description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -155,7 +153,6 @@ model = "role-model" description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -190,7 +187,6 @@ async fn apply_role_preserves_unspecified_keys() { description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -253,7 +249,6 @@ model_provider = "test-provider" description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -311,7 +306,6 @@ model_verbosity = "high" description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -381,7 +375,6 @@ model_provider = "role-provider" description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -443,7 +436,6 @@ model_provider = "base-provider" description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -511,7 +503,6 @@ model_reasoning_effort = "high" description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -559,7 +550,6 @@ writable_roots = ["./sandbox-root"] description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -625,7 +615,6 @@ async fn apply_role_takes_precedence_over_existing_session_flags_for_same_key() description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -672,7 +661,6 @@ enabled = false description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -721,7 +709,6 @@ fn spawn_tool_spec_build_deduplicates_user_defined_built_in_roles() { description: Some("user override".to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -746,7 +733,6 @@ fn spawn_tool_spec_lists_user_defined_roles_before_built_ins() { description: Some("first".to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -777,7 +763,6 @@ fn spawn_tool_spec_marks_role_locked_model_and_reasoning_effort() { description: Some("Research carefully.".to_string()), model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -806,7 +791,6 @@ fn spawn_tool_spec_marks_role_locked_reasoning_effort_only() { description: Some("Review carefully.".to_string()), model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, diff --git a/codex-rs/core/src/codex_tests.rs b/codex-rs/core/src/codex_tests.rs index 83f3459901..571e75053b 100644 --- a/codex-rs/core/src/codex_tests.rs +++ b/codex-rs/core/src/codex_tests.rs @@ -2369,7 +2369,6 @@ enabled = false description: None, model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, diff --git a/codex-rs/core/src/config/agent_roles.rs b/codex-rs/core/src/config/agent_roles.rs index 19d1484c92..dbbb2f4386 100644 --- a/codex-rs/core/src/config/agent_roles.rs +++ b/codex-rs/core/src/config/agent_roles.rs @@ -154,7 +154,6 @@ fn merge_missing_role_fields(role: &mut AgentRoleConfig, fallback: &AgentRoleCon role.description = role.description.clone().or(fallback.description.clone()); role.model = role.model.clone().or(fallback.model.clone()); role.config_file = role.config_file.clone().or(fallback.config_file.clone()); - role.spawn_mode = role.spawn_mode.or(fallback.spawn_mode); role.watchdog_interval_s = role.watchdog_interval_s.or(fallback.watchdog_interval_s); role.nickname_candidates = role .nickname_candidates @@ -194,7 +193,6 @@ fn agent_role_config_from_toml( description, model: role.model.clone(), config_file, - spawn_mode: role.spawn_mode, watchdog_interval_s: role.watchdog_interval_s, nickname_candidates, fork_context: role.fork_context, @@ -488,7 +486,6 @@ fn discover_agent_roles_in_dir( description: parsed_file.description, model: None, config_file: Some(agent_file), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: parsed_file.nickname_candidates, fork_context: None, diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index d706bc328b..773f8ad3bb 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -3220,7 +3220,6 @@ fn load_config_rejects_missing_agent_role_config_file() -> std::io::Result<()> { description: Some("Research role".to_string()), model: None, config_file: Some(missing_path.abs()), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -4090,7 +4089,6 @@ fn load_config_normalizes_agent_role_nickname_candidates() -> std::io::Result<() description: Some("Research role".to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: Some(vec![ " Hypatia ".to_string(), @@ -4136,7 +4134,6 @@ fn load_config_rejects_empty_agent_role_nickname_candidates() -> std::io::Result description: Some("Research role".to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: Some(Vec::new()), fork_context: None, @@ -4176,7 +4173,6 @@ fn load_config_rejects_duplicate_agent_role_nickname_candidates() -> std::io::Re description: Some("Research role".to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: Some(vec!["Hypatia".to_string(), " Hypatia ".to_string()]), fork_context: None, @@ -4216,7 +4212,6 @@ fn load_config_rejects_unsafe_agent_role_nickname_candidates() -> std::io::Resul description: Some("Research role".to_string()), model: None, config_file: None, - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: Some(vec!["Agent ".to_string()]), fork_context: None, diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 8cd59fa659..8d4c9a4c8b 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1577,14 +1577,6 @@ pub struct AgentsToml { pub roles: BTreeMap, } -#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum AgentRoleSpawnMode { - #[default] - Spawn, - Fork, -} - #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct AgentRoleConfig { /// Human-facing role documentation used in spawn tool guidance. @@ -1594,8 +1586,6 @@ pub struct AgentRoleConfig { pub model: Option, /// Path to a role-specific config layer. pub config_file: Option, - /// Optional default spawn mode when `spawn_agent` omits `spawn_mode`. - pub spawn_mode: Option, /// Optional watchdog interval for roles that should spawn as idle-time watchdog handles. pub watchdog_interval_s: Option, /// Candidate nicknames for agents spawned with this role. @@ -1618,9 +1608,6 @@ pub struct AgentRoleToml { /// Relative paths are resolved relative to the `config.toml` that defines them. pub config_file: Option, - /// Optional default spawn mode when `spawn_agent` omits `spawn_mode`. - pub spawn_mode: Option, - /// Optional watchdog interval in seconds for roles that should behave as watchdogs. #[schemars(range(min = 1))] pub watchdog_interval_s: Option, diff --git a/codex-rs/core/tests/suite/subagent_notifications.rs b/codex-rs/core/tests/suite/subagent_notifications.rs index 12e7b10219..f6cb352bf9 100644 --- a/codex-rs/core/tests/suite/subagent_notifications.rs +++ b/codex-rs/core/tests/suite/subagent_notifications.rs @@ -486,7 +486,6 @@ async fn spawn_agent_role_overrides_requested_model_and_reasoning_settings() -> description: Some("Custom role".to_string()), model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, @@ -538,7 +537,6 @@ async fn spawn_agent_tool_description_mentions_role_locked_settings() -> Result< description: Some("Custom role".to_string()), model: None, config_file: Some(role_path), - spawn_mode: None, watchdog_interval_s: None, nickname_candidates: None, fork_context: None, From b2acee09382cf3d59a95e9962ee0cbc135d8d5e8 Mon Sep 17 00:00:00 2001 From: Friel Date: Mon, 30 Mar 2026 08:45:50 +0000 Subject: [PATCH 2/2] ci: bump workflow timeouts --- .github/workflows/rust-ci.yml | 4 ++-- .github/workflows/sdk.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index a655b3071e..444b79ee74 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -137,10 +137,10 @@ jobs: include: - name: Linux runner: ubuntu-24.04 - timeout_minutes: 30 + timeout_minutes: 45 - name: macOS runner: macos-15-xlarge - timeout_minutes: 30 + timeout_minutes: 45 - name: Windows runner: windows-x64 timeout_minutes: 30 diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index 45c983ac1e..87adc96283 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -10,7 +10,7 @@ jobs: runs-on: group: codex-runners labels: codex-linux-x64 - timeout-minutes: 10 + timeout-minutes: 20 steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6