From 6e60f724bcd18e722086e646f9d7e4f19214a9f1 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Mon, 23 Feb 2026 09:06:08 -0800 Subject: [PATCH] remove feature flag collaboration modes (#12028) All code should go in the direction that steer is enabled --------- Co-authored-by: Codex --- codex-rs/core/src/features.rs | 10 ++++---- codex-rs/core/src/tools/spec.rs | 30 +----------------------- codex-rs/tui/src/chatwidget.rs | 35 ++++------------------------ codex-rs/tui/src/chatwidget/tests.rs | 32 +++++-------------------- 4 files changed, 17 insertions(+), 90 deletions(-) diff --git a/codex-rs/core/src/features.rs b/codex-rs/core/src/features.rs index 4b3f0b9de..1f7805a90 100644 --- a/codex-rs/core/src/features.rs +++ b/codex-rs/core/src/features.rs @@ -134,6 +134,7 @@ pub enum Feature { /// Steer feature flag - when enabled, Enter submits immediately instead of queuing. Steer, /// Enable collaboration modes (Plan, Default). + /// Kept for config backward compatibility; behavior is always collaboration-modes-enabled. CollaborationModes, /// Enable personality selection in the TUI. Personality, @@ -617,7 +618,7 @@ pub const FEATURES: &[FeatureSpec] = &[ FeatureSpec { id: Feature::CollaborationModes, key: "collaboration_modes", - stage: Stage::Stable, + stage: Stage::Removed, default_enabled: true, }, FeatureSpec { @@ -728,10 +729,9 @@ mod tests { fn default_enabled_features_are_stable() { for spec in FEATURES { if spec.default_enabled { - assert_eq!( - spec.stage, - Stage::Stable, - "feature `{}` is enabled by default but is not stable ({:?})", + assert!( + matches!(spec.stage, Stage::Stable | Stage::Removed), + "feature `{}` is enabled by default but is not stable/removed ({:?})", spec.key, spec.stage ); diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index 84a69c3ad..60bd8bb03 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -65,7 +65,7 @@ impl ToolsConfig { let include_js_repl_tools_only = include_js_repl && features.enabled(Feature::JsReplToolsOnly); let include_collab_tools = features.enabled(Feature::Collab); - let include_collaboration_modes_tools = features.enabled(Feature::CollaborationModes); + let include_collaboration_modes_tools = true; let include_search_tool = features.enabled(Feature::Apps); let shell_type = if !features.enabled(Feature::ShellTool) { @@ -1887,34 +1887,6 @@ mod tests { ); } - #[test] - fn request_user_input_requires_collaboration_modes_feature() { - let config = test_config(); - let model_info = - ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config); - let mut features = Features::with_defaults(); - features.disable(Feature::CollaborationModes); - let tools_config = ToolsConfig::new(&ToolsConfigParams { - model_info: &model_info, - features: &features, - web_search_mode: Some(WebSearchMode::Cached), - }); - let (tools, _) = build_specs(&tools_config, None, None, &[]).build(); - assert!( - !tools.iter().any(|t| t.spec.name() == "request_user_input"), - "request_user_input should be disabled when collaboration_modes feature is off" - ); - - features.enable(Feature::CollaborationModes); - let tools_config = ToolsConfig::new(&ToolsConfigParams { - model_info: &model_info, - features: &features, - web_search_mode: Some(WebSearchMode::Cached), - }); - let (tools, _) = build_specs(&tools_config, None, None, &[]).build(); - assert_contains_tool_names(&tools, &["request_user_input"]); - } - #[test] fn js_repl_requires_feature_flag() { let config = test_config(); diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 64757eaa2..7ba3a9505 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -2831,9 +2831,7 @@ impl ChatWidget { widget .bottom_pane .set_status_line_enabled(!widget.configured_status_line_items().is_empty()); - widget.bottom_pane.set_collaboration_modes_enabled( - widget.config.features.enabled(Feature::CollaborationModes), - ); + widget.bottom_pane.set_collaboration_modes_enabled(true); widget.sync_personality_command_enabled(); widget .bottom_pane @@ -3001,9 +2999,7 @@ impl ChatWidget { widget .bottom_pane .set_status_line_enabled(!widget.configured_status_line_items().is_empty()); - widget.bottom_pane.set_collaboration_modes_enabled( - widget.config.features.enabled(Feature::CollaborationModes), - ); + widget.bottom_pane.set_collaboration_modes_enabled(true); widget.sync_personality_command_enabled(); widget .bottom_pane @@ -3160,9 +3156,7 @@ impl ChatWidget { widget .bottom_pane .set_status_line_enabled(!widget.configured_status_line_items().is_empty()); - widget.bottom_pane.set_collaboration_modes_enabled( - widget.config.features.enabled(Feature::CollaborationModes), - ); + widget.bottom_pane.set_collaboration_modes_enabled(true); widget.sync_personality_command_enabled(); widget .bottom_pane @@ -6347,22 +6341,6 @@ impl ChatWidget { if feature == Feature::Steer { self.bottom_pane.set_steer_enabled(enabled); } - if feature == Feature::CollaborationModes { - self.bottom_pane.set_collaboration_modes_enabled(enabled); - let settings = self.current_collaboration_mode.settings.clone(); - self.current_collaboration_mode = CollaborationMode { - mode: ModeKind::Default, - settings, - }; - self.active_collaboration_mask = if enabled { - collaboration_modes::default_mask(self.models_manager.as_ref()) - } else { - None - }; - self.update_collaboration_mode_indicator(); - self.refresh_model_display(); - self.request_redraw(); - } if feature == Feature::Personality { self.sync_personality_command_enabled(); } @@ -6541,17 +6519,14 @@ impl ChatWidget { } fn collaboration_modes_enabled(&self) -> bool { - self.config.features.enabled(Feature::CollaborationModes) + true } fn initial_collaboration_mask( - config: &Config, + _config: &Config, models_manager: &ModelsManager, model_override: Option<&str>, ) -> Option { - if !config.features.enabled(Feature::CollaborationModes) { - return None; - } let mut mask = collaboration_modes::default_mask(models_manager)?; if let Some(model_override) = model_override { mask.model = Some(model_override.to_string()); diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 0f537581e..1e0487edd 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -1628,7 +1628,7 @@ async fn make_chatwidget_manual( skills: None, }); bottom.set_steer_enabled(true); - bottom.set_collaboration_modes_enabled(cfg.features.enabled(Feature::CollaborationModes)); + bottom.set_collaboration_modes_enabled(true); let auth_manager = codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("test")); let codex_home = cfg.codex_home.clone(); @@ -1643,6 +1643,7 @@ async fn make_chatwidget_manual( }, }; let current_collaboration_mode = base_mode; + let active_collaboration_mask = collaboration_modes::default_mask(models_manager.as_ref()); let mut widget = ChatWidget { app_event_tx, codex_op_tx: op_tx, @@ -1651,7 +1652,7 @@ async fn make_chatwidget_manual( active_cell_revision: 0, config: cfg, current_collaboration_mode, - active_collaboration_mask: None, + active_collaboration_mask, auth_manager, models_manager, otel_manager, @@ -4055,17 +4056,10 @@ async fn slash_init_skips_when_project_doc_exists() { } #[tokio::test] -async fn collab_mode_shift_tab_cycles_only_when_enabled_and_idle() { +async fn collab_mode_shift_tab_cycles_only_when_idle() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await; - chat.set_feature_enabled(Feature::CollaborationModes, false); let initial = chat.current_collaboration_mode().clone(); - chat.handle_key_event(KeyEvent::from(KeyCode::BackTab)); - assert_eq!(chat.current_collaboration_mode(), &initial); - assert_eq!(chat.active_collaboration_mode_kind(), ModeKind::Default); - - chat.set_feature_enabled(Feature::CollaborationModes, true); - chat.handle_key_event(KeyEvent::from(KeyCode::BackTab)); assert_eq!(chat.active_collaboration_mode_kind(), ModeKind::Plan); assert_eq!(chat.current_collaboration_mode(), &initial); @@ -4430,26 +4424,12 @@ async fn collab_mode_is_sent_after_enabling() { } #[tokio::test] -async fn collab_mode_toggle_on_applies_default_preset() { +async fn collab_mode_applies_default_preset() { let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None).await; chat.thread_id = Some(ThreadId::new()); chat.bottom_pane - .set_composer_text("before toggle".to_string(), Vec::new(), Vec::new()); - chat.handle_key_event(KeyEvent::from(KeyCode::Enter)); - match next_submit_op(&mut op_rx) { - Op::UserTurn { - collaboration_mode: None, - personality: Some(Personality::Pragmatic), - .. - } => {} - other => panic!("expected Op::UserTurn without collaboration_mode, got {other:?}"), - } - - chat.set_feature_enabled(Feature::CollaborationModes, true); - - chat.bottom_pane - .set_composer_text("after toggle".to_string(), Vec::new(), Vec::new()); + .set_composer_text("hello".to_string(), Vec::new(), Vec::new()); chat.handle_key_event(KeyEvent::from(KeyCode::Enter)); match next_submit_op(&mut op_rx) { Op::UserTurn {