mirror of
https://github.com/openai/codex.git
synced 2026-11-06 20:20:34 +00:00
Part 1
This commit is contained in:
+388
-346
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -67,7 +67,6 @@ use codex_core::git_info::current_branch_name;
|
||||
use codex_core::git_info::get_git_repo_root;
|
||||
use codex_core::git_info::local_git_branches;
|
||||
use codex_core::mcp::McpManager;
|
||||
use codex_core::models_manager::manager::ModelsManager;
|
||||
use codex_core::plugins::PluginsManager;
|
||||
use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME;
|
||||
use codex_core::skills::model::SkillMetadata;
|
||||
@@ -274,7 +273,7 @@ use self::interrupts::InterruptManager;
|
||||
mod agent;
|
||||
use self::agent::spawn_agent;
|
||||
use self::agent::spawn_agent_from_existing;
|
||||
pub(crate) use self::agent::spawn_op_forwarder;
|
||||
pub(crate) use self::agent::spawn_app_event_forwarder;
|
||||
mod session_header;
|
||||
use self::session_header::SessionHeader;
|
||||
mod skills;
|
||||
@@ -476,7 +475,8 @@ pub(crate) struct ChatWidgetInit {
|
||||
pub(crate) initial_user_message: Option<UserMessage>,
|
||||
pub(crate) enhanced_keys_supported: bool,
|
||||
pub(crate) auth_manager: Arc<AuthManager>,
|
||||
pub(crate) models_manager: Arc<ModelsManager>,
|
||||
pub(crate) available_models: Vec<ModelPreset>,
|
||||
pub(crate) available_collaboration_modes: Vec<CollaborationModeMask>,
|
||||
pub(crate) feedback: codex_feedback::CodexFeedback,
|
||||
pub(crate) is_first_run: bool,
|
||||
pub(crate) feedback_audience: FeedbackAudience,
|
||||
@@ -654,7 +654,8 @@ pub(crate) struct ChatWidget {
|
||||
/// The currently active collaboration mask, if any.
|
||||
active_collaboration_mask: Option<CollaborationModeMask>,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
models_manager: Arc<ModelsManager>,
|
||||
available_models: Vec<ModelPreset>,
|
||||
available_collaboration_modes: Vec<CollaborationModeMask>,
|
||||
session_telemetry: SessionTelemetry,
|
||||
session_header: SessionHeader,
|
||||
initial_user_message: Option<UserMessage>,
|
||||
@@ -1813,7 +1814,8 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn open_plan_implementation_prompt(&mut self) {
|
||||
let default_mask = collaboration_modes::default_mode_mask(self.models_manager.as_ref());
|
||||
let default_mask =
|
||||
collaboration_modes::default_mode_mask(&self.available_collaboration_modes);
|
||||
let (implement_actions, implement_disabled_reason) = match default_mask {
|
||||
Some(mask) => {
|
||||
let user_text = PLAN_IMPLEMENTATION_CODING_MESSAGE.to_string();
|
||||
@@ -3519,7 +3521,8 @@ impl ChatWidget {
|
||||
initial_user_message,
|
||||
enhanced_keys_supported,
|
||||
auth_manager,
|
||||
models_manager,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
feedback,
|
||||
is_first_run,
|
||||
feedback_audience,
|
||||
@@ -3540,8 +3543,11 @@ impl ChatWidget {
|
||||
let model_for_header = model
|
||||
.clone()
|
||||
.unwrap_or_else(|| DEFAULT_MODEL_DISPLAY_NAME.to_string());
|
||||
let active_collaboration_mask =
|
||||
Self::initial_collaboration_mask(&config, models_manager.as_ref(), model_override);
|
||||
let active_collaboration_mask = Self::initial_collaboration_mask(
|
||||
&config,
|
||||
&available_collaboration_modes,
|
||||
model_override,
|
||||
);
|
||||
let header_model = active_collaboration_mask
|
||||
.as_ref()
|
||||
.and_then(|mask| mask.model.clone())
|
||||
@@ -3584,7 +3590,8 @@ impl ChatWidget {
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
auth_manager,
|
||||
models_manager,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
session_telemetry,
|
||||
session_header: SessionHeader::new(header_model),
|
||||
initial_user_message,
|
||||
@@ -3706,7 +3713,8 @@ impl ChatWidget {
|
||||
initial_user_message,
|
||||
enhanced_keys_supported,
|
||||
auth_manager,
|
||||
models_manager,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
feedback,
|
||||
is_first_run,
|
||||
feedback_audience,
|
||||
@@ -3726,8 +3734,11 @@ impl ChatWidget {
|
||||
let model_for_header = model
|
||||
.clone()
|
||||
.unwrap_or_else(|| DEFAULT_MODEL_DISPLAY_NAME.to_string());
|
||||
let active_collaboration_mask =
|
||||
Self::initial_collaboration_mask(&config, models_manager.as_ref(), model_override);
|
||||
let active_collaboration_mask = Self::initial_collaboration_mask(
|
||||
&config,
|
||||
&available_collaboration_modes,
|
||||
model_override,
|
||||
);
|
||||
let header_model = active_collaboration_mask
|
||||
.as_ref()
|
||||
.and_then(|mask| mask.model.clone())
|
||||
@@ -3770,7 +3781,8 @@ impl ChatWidget {
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
auth_manager,
|
||||
models_manager,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
session_telemetry,
|
||||
session_header: SessionHeader::new(header_model),
|
||||
initial_user_message,
|
||||
@@ -3884,7 +3896,8 @@ impl ChatWidget {
|
||||
initial_user_message,
|
||||
enhanced_keys_supported,
|
||||
auth_manager,
|
||||
models_manager,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
feedback,
|
||||
is_first_run: _,
|
||||
feedback_audience,
|
||||
@@ -3902,8 +3915,11 @@ impl ChatWidget {
|
||||
let header_model = model
|
||||
.clone()
|
||||
.unwrap_or_else(|| session_configured.model.clone());
|
||||
let active_collaboration_mask =
|
||||
Self::initial_collaboration_mask(&config, models_manager.as_ref(), model_override);
|
||||
let active_collaboration_mask = Self::initial_collaboration_mask(
|
||||
&config,
|
||||
&available_collaboration_modes,
|
||||
model_override,
|
||||
);
|
||||
let header_model = active_collaboration_mask
|
||||
.as_ref()
|
||||
.and_then(|mask| mask.model.clone())
|
||||
@@ -3948,7 +3964,8 @@ impl ChatWidget {
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
auth_manager,
|
||||
models_manager,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
session_telemetry,
|
||||
session_header: SessionHeader::new(header_model),
|
||||
initial_user_message,
|
||||
@@ -4393,7 +4410,9 @@ impl ChatWidget {
|
||||
);
|
||||
return;
|
||||
}
|
||||
if let Some(mask) = collaboration_modes::plan_mask(self.models_manager.as_ref()) {
|
||||
if let Some(mask) =
|
||||
collaboration_modes::plan_mask(&self.available_collaboration_modes)
|
||||
{
|
||||
self.set_collaboration_mask(mask);
|
||||
} else {
|
||||
self.add_info_message("Plan mode unavailable right now.".to_string(), None);
|
||||
@@ -6106,8 +6125,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn lower_cost_preset(&self) -> Option<ModelPreset> {
|
||||
let models = self.models_manager.try_list_models().ok()?;
|
||||
models
|
||||
self.available_models
|
||||
.iter()
|
||||
.find(|preset| preset.show_in_picker && preset.model == NUDGE_MODEL_SLUG)
|
||||
.cloned()
|
||||
@@ -6225,16 +6243,11 @@ impl ChatWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
let presets: Vec<ModelPreset> = match self.models_manager.try_list_models() {
|
||||
Ok(models) => models,
|
||||
Err(_) => {
|
||||
self.add_info_message(
|
||||
"Models are being updated; please try /model again in a moment.".to_string(),
|
||||
None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let presets = self.available_models.clone();
|
||||
if presets.is_empty() {
|
||||
self.add_info_message("No models are available right now.".to_string(), None);
|
||||
return;
|
||||
}
|
||||
self.open_model_popup_with_presets(presets);
|
||||
}
|
||||
|
||||
@@ -6645,7 +6658,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
pub(crate) fn open_collaboration_modes_popup(&mut self) {
|
||||
let presets = collaboration_modes::presets_for_tui(self.models_manager.as_ref());
|
||||
let presets = collaboration_modes::presets_for_tui(&self.available_collaboration_modes);
|
||||
if presets.is_empty() {
|
||||
self.add_info_message(
|
||||
"No collaboration modes are available right now.".to_string(),
|
||||
@@ -6659,7 +6672,7 @@ impl ChatWidget {
|
||||
.as_ref()
|
||||
.and_then(|mask| mask.mode)
|
||||
.or_else(|| {
|
||||
collaboration_modes::default_mask(self.models_manager.as_ref())
|
||||
collaboration_modes::default_mask(&self.available_collaboration_modes)
|
||||
.and_then(|mask| mask.mode)
|
||||
});
|
||||
let items: Vec<SelectionItem> = presets
|
||||
@@ -6748,25 +6761,25 @@ impl ChatWidget {
|
||||
None => "the selected reasoning".to_string(),
|
||||
};
|
||||
let plan_only_description = format!("Always use {reasoning_phrase} in Plan mode.");
|
||||
let plan_reasoning_source = if let Some(plan_override) =
|
||||
self.config.plan_mode_reasoning_effort
|
||||
{
|
||||
format!(
|
||||
"user-chosen Plan override ({})",
|
||||
Self::reasoning_effort_label(plan_override).to_lowercase()
|
||||
)
|
||||
} else if let Some(plan_mask) = collaboration_modes::plan_mask(self.models_manager.as_ref())
|
||||
{
|
||||
match plan_mask.reasoning_effort.flatten() {
|
||||
Some(plan_effort) => format!(
|
||||
"built-in Plan default ({})",
|
||||
Self::reasoning_effort_label(plan_effort).to_lowercase()
|
||||
),
|
||||
None => "built-in Plan default (no reasoning)".to_string(),
|
||||
}
|
||||
} else {
|
||||
"built-in Plan default".to_string()
|
||||
};
|
||||
let plan_reasoning_source =
|
||||
if let Some(plan_override) = self.config.plan_mode_reasoning_effort {
|
||||
format!(
|
||||
"user-chosen Plan override ({})",
|
||||
Self::reasoning_effort_label(plan_override).to_lowercase()
|
||||
)
|
||||
} else if let Some(plan_mask) =
|
||||
collaboration_modes::plan_mask(&self.available_collaboration_modes)
|
||||
{
|
||||
match plan_mask.reasoning_effort.flatten() {
|
||||
Some(plan_effort) => format!(
|
||||
"built-in Plan default ({})",
|
||||
Self::reasoning_effort_label(plan_effort).to_lowercase()
|
||||
),
|
||||
None => "built-in Plan default (no reasoning)".to_string(),
|
||||
}
|
||||
} else {
|
||||
"built-in Plan default".to_string()
|
||||
};
|
||||
let all_modes_description = format!(
|
||||
"Set the global default reasoning level and the Plan mode override. This replaces the current {plan_reasoning_source}."
|
||||
);
|
||||
@@ -7901,7 +7914,7 @@ impl ChatWidget {
|
||||
if let Some(effort) = effort {
|
||||
mask.reasoning_effort = Some(Some(effort));
|
||||
} else if let Some(plan_mask) =
|
||||
collaboration_modes::plan_mask(self.models_manager.as_ref())
|
||||
collaboration_modes::plan_mask(&self.available_collaboration_modes)
|
||||
{
|
||||
mask.reasoning_effort = plan_mask.reasoning_effort;
|
||||
}
|
||||
@@ -8042,15 +8055,10 @@ impl ChatWidget {
|
||||
|
||||
fn current_model_supports_personality(&self) -> bool {
|
||||
let model = self.current_model();
|
||||
self.models_manager
|
||||
.try_list_models()
|
||||
.ok()
|
||||
.and_then(|models| {
|
||||
models
|
||||
.into_iter()
|
||||
.find(|preset| preset.model == model)
|
||||
.map(|preset| preset.supports_personality)
|
||||
})
|
||||
self.available_models
|
||||
.iter()
|
||||
.find(|preset| preset.model == model)
|
||||
.map(|preset| preset.supports_personality)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
@@ -8060,15 +8068,10 @@ impl ChatWidget {
|
||||
/// failures do not hard-block user input in the UI.
|
||||
fn current_model_supports_images(&self) -> bool {
|
||||
let model = self.current_model();
|
||||
self.models_manager
|
||||
.try_list_models()
|
||||
.ok()
|
||||
.and_then(|models| {
|
||||
models
|
||||
.into_iter()
|
||||
.find(|preset| preset.model == model)
|
||||
.map(|preset| preset.input_modalities.contains(&InputModality::Image))
|
||||
})
|
||||
self.available_models
|
||||
.iter()
|
||||
.find(|preset| preset.model == model)
|
||||
.map(|preset| preset.input_modalities.contains(&InputModality::Image))
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
@@ -8108,10 +8111,10 @@ impl ChatWidget {
|
||||
|
||||
fn initial_collaboration_mask(
|
||||
_config: &Config,
|
||||
models_manager: &ModelsManager,
|
||||
available_collaboration_modes: &[CollaborationModeMask],
|
||||
model_override: Option<&str>,
|
||||
) -> Option<CollaborationModeMask> {
|
||||
let mut mask = collaboration_modes::default_mask(models_manager)?;
|
||||
let mut mask = collaboration_modes::default_mask(available_collaboration_modes)?;
|
||||
if let Some(model_override) = model_override {
|
||||
mask.model = Some(model_override.to_string());
|
||||
}
|
||||
@@ -8211,7 +8214,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
if let Some(next_mask) = collaboration_modes::next_mask(
|
||||
self.models_manager.as_ref(),
|
||||
&self.available_collaboration_modes,
|
||||
self.active_collaboration_mask.as_ref(),
|
||||
) {
|
||||
self.set_collaboration_mask(next_mask);
|
||||
|
||||
@@ -147,3 +147,19 @@ pub(crate) fn spawn_op_forwarder(thread: std::sync::Arc<CodexThread>) -> Unbound
|
||||
|
||||
codex_op_tx
|
||||
}
|
||||
|
||||
/// Spawn an op forwarder that routes all widget submissions back onto the app event bus.
|
||||
///
|
||||
/// The app layer owns app-server request translation during the migration, so the chat widget
|
||||
/// should no longer need a direct thread handle for normal runtime operation.
|
||||
pub(crate) fn spawn_app_event_forwarder(app_event_tx: AppEventSender) -> UnboundedSender<Op> {
|
||||
let (codex_op_tx, mut codex_op_rx) = unbounded_channel::<Op>();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(op) = codex_op_rx.recv().await {
|
||||
app_event_tx.send(AppEvent::CodexOp(op));
|
||||
}
|
||||
});
|
||||
|
||||
codex_op_tx
|
||||
}
|
||||
|
||||
@@ -1310,7 +1310,7 @@ async fn interrupted_turn_restore_keeps_active_mode_for_resubmission() {
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.models_manager.as_ref())
|
||||
let plan_mask = collaboration_modes::plan_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected plan collaboration mode");
|
||||
let expected_mode = plan_mask
|
||||
.mode
|
||||
@@ -1761,7 +1761,13 @@ async fn helpers_are_available_and_do_not_panic() {
|
||||
initial_user_message: None,
|
||||
enhanced_keys_supported: false,
|
||||
auth_manager,
|
||||
models_manager: thread_manager.get_models_manager(),
|
||||
available_models: thread_manager
|
||||
.get_models_manager()
|
||||
.try_list_models()
|
||||
.expect("test models should be available"),
|
||||
available_collaboration_modes: thread_manager
|
||||
.get_models_manager()
|
||||
.list_collaboration_modes(),
|
||||
feedback: codex_feedback::CodexFeedback::new(),
|
||||
is_first_run: true,
|
||||
feedback_audience: FeedbackAudience::External,
|
||||
@@ -1791,6 +1797,17 @@ fn test_session_telemetry(config: &Config, model: &str) -> SessionTelemetry {
|
||||
)
|
||||
}
|
||||
|
||||
fn available_chatwidget_catalog(
|
||||
models_manager: &ModelsManager,
|
||||
) -> (Vec<ModelPreset>, Vec<CollaborationModeMask>) {
|
||||
(
|
||||
models_manager
|
||||
.try_list_models()
|
||||
.expect("test models should be available"),
|
||||
models_manager.list_collaboration_modes(),
|
||||
)
|
||||
}
|
||||
|
||||
// --- Helpers for tests that need direct construction and event draining ---
|
||||
async fn make_chatwidget_manual(
|
||||
model_override: Option<&str>,
|
||||
@@ -1831,6 +1848,8 @@ async fn make_chatwidget_manual(
|
||||
None,
|
||||
CollaborationModesConfig::default(),
|
||||
));
|
||||
let (available_models, available_collaboration_modes) =
|
||||
available_chatwidget_catalog(models_manager.as_ref());
|
||||
let reasoning_effort = None;
|
||||
let base_mode = CollaborationMode {
|
||||
mode: ModeKind::Default,
|
||||
@@ -1841,7 +1860,8 @@ async fn make_chatwidget_manual(
|
||||
},
|
||||
};
|
||||
let current_collaboration_mode = base_mode;
|
||||
let active_collaboration_mask = collaboration_modes::default_mask(models_manager.as_ref());
|
||||
let active_collaboration_mask =
|
||||
collaboration_modes::default_mask(&available_collaboration_modes);
|
||||
let mut widget = ChatWidget {
|
||||
app_event_tx,
|
||||
codex_op_tx: op_tx,
|
||||
@@ -1852,7 +1872,8 @@ async fn make_chatwidget_manual(
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
auth_manager,
|
||||
models_manager,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
session_telemetry,
|
||||
session_header: SessionHeader::new(resolved_model.clone()),
|
||||
initial_user_message: None,
|
||||
@@ -1970,12 +1991,16 @@ pub(crate) fn set_chatgpt_auth(chat: &mut ChatWidget) {
|
||||
chat.auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
|
||||
);
|
||||
chat.models_manager = Arc::new(ModelsManager::new(
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
chat.config.codex_home.clone(),
|
||||
chat.auth_manager.clone(),
|
||||
None,
|
||||
CollaborationModesConfig::default(),
|
||||
));
|
||||
let (available_models, available_collaboration_modes) =
|
||||
available_chatwidget_catalog(models_manager.as_ref());
|
||||
chat.available_models = available_models;
|
||||
chat.available_collaboration_modes = available_collaboration_modes;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -2477,7 +2502,7 @@ async fn submit_user_message_with_mode_sets_coding_collaboration_mode() {
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
|
||||
let default_mode = collaboration_modes::default_mode_mask(chat.models_manager.as_ref())
|
||||
let default_mode = collaboration_modes::default_mode_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected default collaboration mode");
|
||||
chat.submit_user_message_with_mode("Implement the plan.".to_string(), default_mode);
|
||||
|
||||
@@ -2502,7 +2527,7 @@ async fn reasoning_selection_in_plan_mode_opens_scope_prompt_event() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.models_manager.as_ref())
|
||||
let plan_mask = collaboration_modes::plan_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected plan collaboration mode");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
let _ = drain_insert_history(&mut rx);
|
||||
@@ -2529,7 +2554,7 @@ async fn reasoning_selection_in_plan_mode_without_effort_change_does_not_open_sc
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.models_manager.as_ref())
|
||||
let plan_mask = collaboration_modes::plan_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected plan collaboration mode");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
let _ = drain_insert_history(&mut rx);
|
||||
@@ -2564,7 +2589,7 @@ async fn reasoning_selection_in_plan_mode_matching_plan_effort_but_different_glo
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.models_manager.as_ref())
|
||||
let plan_mask = collaboration_modes::plan_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected plan collaboration mode");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
let _ = drain_insert_history(&mut rx);
|
||||
@@ -2597,7 +2622,7 @@ async fn plan_mode_reasoning_override_is_marked_current_in_reasoning_popup() {
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
chat.set_plan_mode_reasoning_effort(Some(ReasoningEffortConfig::Low));
|
||||
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.models_manager.as_ref())
|
||||
let plan_mask = collaboration_modes::plan_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected plan collaboration mode");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -2617,7 +2642,7 @@ async fn reasoning_selection_in_plan_mode_model_switch_does_not_open_scope_promp
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.models_manager.as_ref())
|
||||
let plan_mask = collaboration_modes::plan_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected plan collaboration mode");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
let _ = drain_insert_history(&mut rx);
|
||||
@@ -2883,12 +2908,12 @@ async fn submit_user_message_with_mode_errors_when_mode_changes_during_running_t
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
chat.on_task_started();
|
||||
|
||||
let default_mode = collaboration_modes::default_mask(chat.models_manager.as_ref())
|
||||
let default_mode = collaboration_modes::default_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected default collaboration mode");
|
||||
chat.submit_user_message_with_mode("Implement the plan.".to_string(), default_mode);
|
||||
|
||||
@@ -2912,7 +2937,7 @@ async fn submit_user_message_with_mode_allows_same_mode_during_running_turn() {
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask.clone());
|
||||
chat.on_task_started();
|
||||
@@ -2943,11 +2968,11 @@ async fn submit_user_message_with_mode_submits_when_plan_stream_is_not_active()
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
let default_mode = collaboration_modes::default_mask(chat.models_manager.as_ref())
|
||||
let default_mode = collaboration_modes::default_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected default collaboration mode");
|
||||
let expected_mode = default_mode
|
||||
.mode
|
||||
@@ -2973,7 +2998,7 @@ async fn plan_implementation_popup_skips_replayed_turn_complete() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -2994,7 +3019,7 @@ async fn plan_implementation_popup_shows_once_when_replay_precedes_live_turn_com
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -3072,7 +3097,7 @@ async fn plan_implementation_popup_skips_when_messages_queued() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
chat.bottom_pane.set_task_running(true);
|
||||
@@ -3092,7 +3117,7 @@ async fn plan_implementation_popup_skips_without_proposed_plan() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -3118,7 +3143,7 @@ async fn plan_implementation_popup_shows_after_proposed_plan_output() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -3139,7 +3164,7 @@ async fn plan_implementation_popup_skips_when_steer_follows_proposed_plan() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
@@ -3181,7 +3206,7 @@ async fn plan_implementation_popup_shows_after_new_plan_follows_steer() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
@@ -3230,7 +3255,7 @@ async fn plan_implementation_popup_skips_when_rate_limit_prompt_pending() {
|
||||
);
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -3634,11 +3659,7 @@ fn active_blob(chat: &ChatWidget) -> String {
|
||||
}
|
||||
|
||||
fn get_available_model(chat: &ChatWidget, model: &str) -> ModelPreset {
|
||||
let models = chat
|
||||
.models_manager
|
||||
.try_list_models()
|
||||
.expect("models lock available");
|
||||
models
|
||||
chat.available_models
|
||||
.iter()
|
||||
.find(|&preset| preset.model == model)
|
||||
.cloned()
|
||||
@@ -3902,7 +3923,7 @@ async fn plan_completion_restores_status_indicator_after_streaming_plan_output()
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -3997,7 +4018,7 @@ async fn steer_enter_queues_while_plan_stream_is_active() {
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
chat.on_task_started();
|
||||
@@ -4712,7 +4733,7 @@ async fn enter_submits_when_plan_stream_is_not_active() {
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
chat.on_task_started();
|
||||
@@ -5417,7 +5438,7 @@ async fn mode_switch_surfaces_model_change_notification_when_effective_model_cha
|
||||
let default_model = chat.current_model().to_string();
|
||||
|
||||
let mut plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mode");
|
||||
plan_mask.model = Some("gpt-5.1-codex-mini".to_string());
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
@@ -5432,7 +5453,7 @@ async fn mode_switch_surfaces_model_change_notification_when_effective_model_cha
|
||||
"expected Plan-mode model switch notice, got: {plan_messages:?}"
|
||||
);
|
||||
|
||||
let default_mask = collaboration_modes::default_mask(chat.models_manager.as_ref())
|
||||
let default_mask = collaboration_modes::default_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected default collaboration mode");
|
||||
chat.set_collaboration_mask(default_mask);
|
||||
|
||||
@@ -5455,7 +5476,7 @@ async fn mode_switch_surfaces_reasoning_change_notification_when_model_stays_sam
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.models_manager.as_ref())
|
||||
let plan_mask = collaboration_modes::plan_mask(&chat.available_collaboration_modes)
|
||||
.expect("expected plan collaboration mode");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -5621,7 +5642,13 @@ async fn collaboration_modes_defaults_to_code_on_startup() {
|
||||
initial_user_message: None,
|
||||
enhanced_keys_supported: false,
|
||||
auth_manager,
|
||||
models_manager: thread_manager.get_models_manager(),
|
||||
available_models: thread_manager
|
||||
.get_models_manager()
|
||||
.try_list_models()
|
||||
.expect("test models should be available"),
|
||||
available_collaboration_modes: thread_manager
|
||||
.get_models_manager()
|
||||
.list_collaboration_modes(),
|
||||
feedback: codex_feedback::CodexFeedback::new(),
|
||||
is_first_run: true,
|
||||
feedback_audience: FeedbackAudience::External,
|
||||
@@ -5671,7 +5698,13 @@ async fn experimental_mode_plan_is_ignored_on_startup() {
|
||||
initial_user_message: None,
|
||||
enhanced_keys_supported: false,
|
||||
auth_manager,
|
||||
models_manager: thread_manager.get_models_manager(),
|
||||
available_models: thread_manager
|
||||
.get_models_manager()
|
||||
.try_list_models()
|
||||
.expect("test models should be available"),
|
||||
available_collaboration_modes: thread_manager
|
||||
.get_models_manager()
|
||||
.list_collaboration_modes(),
|
||||
feedback: codex_feedback::CodexFeedback::new(),
|
||||
is_first_run: true,
|
||||
feedback_audience: FeedbackAudience::External,
|
||||
@@ -5691,7 +5724,7 @@ async fn set_model_updates_active_collaboration_mask() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -5706,7 +5739,7 @@ async fn set_reasoning_effort_updates_active_collaboration_mask() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
@@ -5725,7 +5758,7 @@ async fn set_reasoning_effort_does_not_override_active_plan_override() {
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
chat.set_plan_mode_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
use codex_core::models_manager::manager::ModelsManager;
|
||||
use codex_protocol::config_types::CollaborationModeMask;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
|
||||
fn filtered_presets(models_manager: &ModelsManager) -> Vec<CollaborationModeMask> {
|
||||
models_manager
|
||||
.list_collaboration_modes()
|
||||
.into_iter()
|
||||
.filter(|mask| mask.mode.is_some_and(ModeKind::is_tui_visible))
|
||||
fn filtered_presets(collaboration_modes: &[CollaborationModeMask]) -> Vec<CollaborationModeMask> {
|
||||
collaboration_modes
|
||||
.iter()
|
||||
.filter(|&mask| mask.mode.is_some_and(ModeKind::is_tui_visible))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn presets_for_tui(models_manager: &ModelsManager) -> Vec<CollaborationModeMask> {
|
||||
filtered_presets(models_manager)
|
||||
pub(crate) fn presets_for_tui(
|
||||
collaboration_modes: &[CollaborationModeMask],
|
||||
) -> Vec<CollaborationModeMask> {
|
||||
filtered_presets(collaboration_modes)
|
||||
}
|
||||
|
||||
pub(crate) fn default_mask(models_manager: &ModelsManager) -> Option<CollaborationModeMask> {
|
||||
let presets = filtered_presets(models_manager);
|
||||
pub(crate) fn default_mask(
|
||||
collaboration_modes: &[CollaborationModeMask],
|
||||
) -> Option<CollaborationModeMask> {
|
||||
let presets = filtered_presets(collaboration_modes);
|
||||
presets
|
||||
.iter()
|
||||
.find(|mask| mask.mode == Some(ModeKind::Default))
|
||||
@@ -24,23 +27,23 @@ pub(crate) fn default_mask(models_manager: &ModelsManager) -> Option<Collaborati
|
||||
}
|
||||
|
||||
pub(crate) fn mask_for_kind(
|
||||
models_manager: &ModelsManager,
|
||||
collaboration_modes: &[CollaborationModeMask],
|
||||
kind: ModeKind,
|
||||
) -> Option<CollaborationModeMask> {
|
||||
if !kind.is_tui_visible() {
|
||||
return None;
|
||||
}
|
||||
filtered_presets(models_manager)
|
||||
filtered_presets(collaboration_modes)
|
||||
.into_iter()
|
||||
.find(|mask| mask.mode == Some(kind))
|
||||
}
|
||||
|
||||
/// Cycle to the next collaboration mode preset in list order.
|
||||
pub(crate) fn next_mask(
|
||||
models_manager: &ModelsManager,
|
||||
collaboration_modes: &[CollaborationModeMask],
|
||||
current: Option<&CollaborationModeMask>,
|
||||
) -> Option<CollaborationModeMask> {
|
||||
let presets = filtered_presets(models_manager);
|
||||
let presets = filtered_presets(collaboration_modes);
|
||||
if presets.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -52,10 +55,14 @@ pub(crate) fn next_mask(
|
||||
presets.get(next_index).cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn default_mode_mask(models_manager: &ModelsManager) -> Option<CollaborationModeMask> {
|
||||
mask_for_kind(models_manager, ModeKind::Default)
|
||||
pub(crate) fn default_mode_mask(
|
||||
collaboration_modes: &[CollaborationModeMask],
|
||||
) -> Option<CollaborationModeMask> {
|
||||
mask_for_kind(collaboration_modes, ModeKind::Default)
|
||||
}
|
||||
|
||||
pub(crate) fn plan_mask(models_manager: &ModelsManager) -> Option<CollaborationModeMask> {
|
||||
mask_for_kind(models_manager, ModeKind::Plan)
|
||||
pub(crate) fn plan_mask(
|
||||
collaboration_modes: &[CollaborationModeMask],
|
||||
) -> Option<CollaborationModeMask> {
|
||||
mask_for_kind(collaboration_modes, ModeKind::Plan)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user