diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index a897e5a8f6..b08726ea7b 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -40,7 +40,6 @@ pub(crate) struct ToolsConfig { pub js_repl_enabled: bool, pub js_repl_tools_only: bool, pub collab_tools: bool, - pub collaboration_modes_tools: bool, pub experimental_supported_tools: Vec, } @@ -62,7 +61,6 @@ 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_search_tool = features.enabled(Feature::Apps); let shell_type = if !features.enabled(Feature::ShellTool) { @@ -98,7 +96,6 @@ impl ToolsConfig { js_repl_enabled: include_js_repl, js_repl_tools_only: include_js_repl_tools_only, collab_tools: include_collab_tools, - collaboration_modes_tools: include_collaboration_modes_tools, experimental_supported_tools: model_info.experimental_supported_tools.clone(), } } @@ -1484,10 +1481,8 @@ pub(crate) fn build_specs( builder.register_handler("js_repl_reset", js_repl_reset_handler); } - if config.collaboration_modes_tools { - builder.push_spec(create_request_user_input_tool()); - builder.register_handler("request_user_input", request_user_input_handler); - } + builder.push_spec(create_request_user_input_tool()); + builder.register_handler("request_user_input", request_user_input_handler); if config.search_tool && let Some(app_tools) = app_tools @@ -1874,7 +1869,7 @@ mod tests { } #[test] - fn request_user_input_requires_collaboration_modes_feature() { + fn request_user_input_is_available_even_if_collaboration_modes_disabled() { let config = test_config(); let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config); @@ -1887,8 +1882,8 @@ mod tests { }); 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" + tools.iter().any(|t| t.spec.name() == "request_user_input"), + "request_user_input should remain enabled even when collaboration_modes is disabled" ); features.enable(Feature::CollaborationModes); diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index dcbee999bc..cf4171c75e 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -2699,9 +2699,7 @@ impl ChatWidget { .as_ref() .is_some_and(|items| !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(); #[cfg(target_os = "windows")] widget.bottom_pane.set_windows_degraded_sandbox_active( @@ -2866,9 +2864,7 @@ impl ChatWidget { .as_ref() .is_some_and(|items| !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 @@ -3022,9 +3018,7 @@ impl ChatWidget { .as_ref() .is_some_and(|items| !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(); #[cfg(target_os = "windows")] widget.bottom_pane.set_windows_degraded_sandbox_active( @@ -6027,22 +6021,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(); } @@ -6198,7 +6176,7 @@ impl ChatWidget { } fn collaboration_modes_enabled(&self) -> bool { - self.config.features.enabled(Feature::CollaborationModes) + true } fn initial_collaboration_mask( @@ -6206,9 +6184,6 @@ impl ChatWidget { models_manager: &ModelsManager, model_override: Option<&str>, ) -> Option { - if !config.features.enabled(Feature::CollaborationModes) { - return None; - } let mut mask = match config.experimental_mode { Some(kind) => collaboration_modes::mask_for_kind(models_manager, kind)?, None => collaboration_modes::default_mask(models_manager)?, diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index f069db9160..bc840112ea 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -1575,11 +1575,12 @@ 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(); let models_manager = Arc::new(ModelsManager::new(codex_home, auth_manager.clone())); + let active_collaboration_mask = collaboration_modes::default_mask(models_manager.as_ref()); let reasoning_effort = None; let base_mode = CollaborationMode { mode: ModeKind::Default, @@ -1598,7 +1599,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, @@ -3552,17 +3553,11 @@ 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_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); @@ -3815,10 +3810,9 @@ async fn set_reasoning_effort_updates_active_collaboration_mask() { } #[tokio::test] -async fn collab_mode_is_sent_after_enabling() { +async fn collab_mode_is_sent_on_submit() { let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None).await; chat.thread_id = Some(ThreadId::new()); - chat.set_feature_enabled(Feature::CollaborationModes, true); chat.bottom_pane .set_composer_text("hello".to_string(), Vec::new(), Vec::new()); @@ -3840,26 +3834,13 @@ async fn collab_mode_is_sent_after_enabling() { } #[tokio::test] -async fn collab_mode_toggle_on_applies_default_preset() { +async fn collab_mode_flag_toggle_does_not_disable_default_preset() { let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None).await; chat.thread_id = Some(ThreadId::new()); + chat.set_feature_enabled(Feature::CollaborationModes, false); 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("while disabled".to_string(), Vec::new(), Vec::new()); chat.handle_key_event(KeyEvent::from(KeyCode::Enter)); match next_submit_op(&mut op_rx) { Op::UserTurn { @@ -3875,9 +3856,6 @@ async fn collab_mode_toggle_on_applies_default_preset() { panic!("expected Op::UserTurn with default collaboration_mode, got {other:?}") } } - - assert_eq!(chat.active_collaboration_mode_kind(), ModeKind::Default); - assert_eq!(chat.current_collaboration_mode().mode, ModeKind::Default); } #[tokio::test]