From c37f7434badbc50a4bee80148952a98f4dca36b4 Mon Sep 17 00:00:00 2001 From: jif-oai Date: Thu, 30 Apr 2026 10:23:31 +0200 Subject: [PATCH] Gate multi-agent v2 tools independently of collab (#20246) ## Why `multi_agents_v2` is meant to be independently gated from the older `collab` feature. The tool registry still treated the collaboration-style agent tools as `collab`-only, so enabling `multi_agents_v2` without `collab` omitted the v2 agent tools. Review and guardian sub-sessions also need to keep agent spawning disabled even when the outer session has `multi_agents_v2` enabled. ## What changed - Include the collab-backed agent tools when either `multi_agents_v2` or `collab` is enabled. - Explicitly disable `multi_agents_v2` for review and guardian review sub-sessions, matching the existing `spawn_csv` and `collab` restrictions. - Add a registry test that enables `multi_agents_v2`, disables `collab`, and verifies the v2 agent tools are present while legacy `send_input` and `resume_agent` remain hidden. ## Testing - Added `test_build_specs_multi_agent_v2_does_not_require_collab_feature`. --- codex-rs/core/src/guardian/review_session.rs | 1 + codex-rs/core/src/tasks/review.rs | 1 + codex-rs/tools/src/tool_config.rs | 2 +- .../tools/src/tool_registry_plan_tests.rs | 40 +++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/codex-rs/core/src/guardian/review_session.rs b/codex-rs/core/src/guardian/review_session.rs index e95eda02b..6fd50219d 100644 --- a/codex-rs/core/src/guardian/review_session.rs +++ b/codex-rs/core/src/guardian/review_session.rs @@ -931,6 +931,7 @@ pub(crate) fn build_guardian_review_session_config( for feature in [ Feature::SpawnCsv, Feature::Collab, + Feature::MultiAgentV2, Feature::CodexHooks, Feature::Apps, Feature::Plugins, diff --git a/codex-rs/core/src/tasks/review.rs b/codex-rs/core/src/tasks/review.rs index 2f81b9275..c81c011f4 100644 --- a/codex-rs/core/src/tasks/review.rs +++ b/codex-rs/core/src/tasks/review.rs @@ -110,6 +110,7 @@ async fn start_review_conversation( } let _ = sub_agent_config.features.disable(Feature::SpawnCsv); let _ = sub_agent_config.features.disable(Feature::Collab); + let _ = sub_agent_config.features.disable(Feature::MultiAgentV2); // Set explicit review rubric for the sub-agent sub_agent_config.base_instructions = Some(crate::REVIEW_PROMPT.to_string()); diff --git a/codex-rs/tools/src/tool_config.rs b/codex-rs/tools/src/tool_config.rs index 8c4111041..32ee9e1e5 100644 --- a/codex-rs/tools/src/tool_config.rs +++ b/codex-rs/tools/src/tool_config.rs @@ -143,9 +143,9 @@ impl ToolsConfig { let include_apply_patch_tool = features.enabled(Feature::ApplyPatchFreeform); let include_code_mode = features.enabled(Feature::CodeMode); let include_code_mode_only = include_code_mode && features.enabled(Feature::CodeModeOnly); - let include_collab_tools = features.enabled(Feature::Collab); let include_goal_tools = features.enabled(Feature::Goals); let include_multi_agent_v2 = features.enabled(Feature::MultiAgentV2); + let include_collab_tools = include_multi_agent_v2 || features.enabled(Feature::Collab); let include_agent_jobs = features.enabled(Feature::SpawnCsv); let include_search_tool = model_info.supports_search_tool && features.enabled(Feature::ToolSearch); diff --git a/codex-rs/tools/src/tool_registry_plan_tests.rs b/codex-rs/tools/src/tool_registry_plan_tests.rs index d206a53b3..9564c3eb8 100644 --- a/codex-rs/tools/src/tool_registry_plan_tests.rs +++ b/codex-rs/tools/src/tool_registry_plan_tests.rs @@ -385,6 +385,46 @@ fn test_build_specs_multi_agent_v2_uses_task_names_and_hides_resume() { assert_lacks_tool_name(&tools, "resume_agent"); } +#[test] +fn test_build_specs_multi_agent_v2_does_not_require_collab_feature() { + let model_info = model_info(); + let mut features = Features::with_defaults(); + features.disable(Feature::Collab); + features.enable(Feature::MultiAgentV2); + assert!(!features.enabled(Feature::Collab)); + let available_models = Vec::new(); + let tools_config = ToolsConfig::new(&ToolsConfigParams { + model_info: &model_info, + available_models: &available_models, + features: &features, + image_generation_tool_auth_allowed: true, + web_search_mode: Some(WebSearchMode::Cached), + session_source: SessionSource::Cli, + permission_profile: &PermissionProfile::Disabled, + windows_sandbox_level: WindowsSandboxLevel::Disabled, + }); + let (tools, _) = build_specs( + &tools_config, + /*mcp_tools*/ None, + /*deferred_mcp_tools*/ None, + &[], + ); + + assert_contains_tool_names( + &tools, + &[ + "spawn_agent", + "send_message", + "followup_task", + "wait_agent", + "close_agent", + "list_agents", + ], + ); + assert_lacks_tool_name(&tools, "send_input"); + assert_lacks_tool_name(&tools, "resume_agent"); +} + #[test] fn test_build_specs_enable_fanout_enables_agent_jobs_and_collab_tools() { let model_info = model_info();