Remove legacy ModelInfo and merge it with ModelFamily (#7748)

This is a step towards removing the need to know `model` when
constructing config. We firstly don't need to know `model_info` and just
respect if the user has already set it. Next step, we don't need to know
`model` unless the user explicitly set it in `config.toml`
This commit is contained in:
Ahmed Ibrahim
2025-12-08 15:29:37 -08:00
committed by GitHub
parent ac5fa6baf8
commit 382f047a10
10 changed files with 205 additions and 225 deletions

View File

@@ -98,7 +98,7 @@ fn snapshot(percent: f64) -> RateLimitSnapshot {
#[test]
fn resumed_initial_messages_render_history() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
let conversation_id = ConversationId::new();
let rollout_file = NamedTempFile::new().unwrap();
@@ -154,7 +154,7 @@ fn resumed_initial_messages_render_history() {
/// Entering review mode uses the hint provided by the review request.
#[test]
fn entered_review_mode_uses_request_hint() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "review-start".into(),
@@ -175,7 +175,7 @@ fn entered_review_mode_uses_request_hint() {
/// Entering review mode renders the current changes banner when requested.
#[test]
fn entered_review_mode_defaults_to_current_changes_banner() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "review-start".into(),
@@ -195,7 +195,7 @@ fn entered_review_mode_defaults_to_current_changes_banner() {
/// the closing banner while clearing review mode state.
#[test]
fn exited_review_mode_emits_results_and_finishes() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
let review = ReviewOutputEvent {
findings: vec![ReviewFinding {
@@ -229,7 +229,7 @@ fn exited_review_mode_emits_results_and_finishes() {
/// Exiting review restores the pre-review context window indicator.
#[test]
fn review_restores_context_window_indicator() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
let context_window = 13_000;
let pre_review_tokens = 12_700; // ~30% remaining after subtracting baseline.
@@ -278,7 +278,7 @@ fn review_restores_context_window_indicator() {
/// Receiving a TokenCount event without usage clears the context indicator.
#[test]
fn token_count_none_resets_context_indicator() {
let (mut chat, _rx, _ops) = make_chatwidget_manual();
let (mut chat, _rx, _ops) = make_chatwidget_manual(None);
let context_window = 13_000;
let pre_compact_tokens = 12_700;
@@ -304,7 +304,7 @@ fn token_count_none_resets_context_indicator() {
#[test]
fn context_indicator_shows_used_tokens_when_window_unknown() {
let (mut chat, _rx, _ops) = make_chatwidget_manual();
let (mut chat, _rx, _ops) = make_chatwidget_manual(Some("unknown-model"));
chat.config.model_context_window = None;
let auto_compact_limit = 200_000;
@@ -371,7 +371,9 @@ async fn helpers_are_available_and_do_not_panic() {
}
// --- Helpers for tests that need direct construction and event draining ---
fn make_chatwidget_manual() -> (
fn make_chatwidget_manual(
model_override: Option<&str>,
) -> (
ChatWidget,
tokio::sync::mpsc::UnboundedReceiver<AppEvent>,
tokio::sync::mpsc::UnboundedReceiver<Op>,
@@ -379,7 +381,10 @@ fn make_chatwidget_manual() -> (
let (tx_raw, rx) = unbounded_channel::<AppEvent>();
let app_event_tx = AppEventSender::new(tx_raw);
let (op_tx, op_rx) = unbounded_channel::<Op>();
let cfg = test_config();
let mut cfg = test_config();
if let Some(model) = model_override {
cfg.model = model.to_string();
}
let bottom = BottomPane::new(BottomPaneParams {
app_event_tx: app_event_tx.clone(),
frame_requester: FrameRequester::test_dummy(),
@@ -447,7 +452,7 @@ pub(crate) fn make_chatwidget_manual_with_sender() -> (
tokio::sync::mpsc::UnboundedReceiver<AppEvent>,
tokio::sync::mpsc::UnboundedReceiver<Op>,
) {
let (widget, rx, op_rx) = make_chatwidget_manual();
let (widget, rx, op_rx) = make_chatwidget_manual(None);
let app_event_tx = widget.app_event_tx.clone();
(widget, app_event_tx, rx, op_rx)
}
@@ -543,7 +548,7 @@ fn test_rate_limit_warnings_monthly() {
#[test]
fn rate_limit_snapshot_keeps_prior_credits_when_missing_from_headers() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.on_rate_limit_snapshot(Some(RateLimitSnapshot {
primary: None,
@@ -592,7 +597,7 @@ fn rate_limit_snapshot_keeps_prior_credits_when_missing_from_headers() {
#[test]
fn rate_limit_snapshot_updates_and_retains_plan_type() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.on_rate_limit_snapshot(Some(RateLimitSnapshot {
primary: Some(RateLimitWindow {
@@ -645,7 +650,7 @@ fn rate_limit_snapshot_updates_and_retains_plan_type() {
#[test]
fn rate_limit_switch_prompt_skips_when_on_lower_cost_model() {
let (mut chat, _, _) = make_chatwidget_manual();
let (mut chat, _, _) = make_chatwidget_manual(None);
chat.auth_manager =
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
chat.config.model = NUDGE_MODEL_SLUG.to_string();
@@ -661,7 +666,7 @@ fn rate_limit_switch_prompt_skips_when_on_lower_cost_model() {
#[test]
fn rate_limit_switch_prompt_shows_once_per_session() {
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
let (mut chat, _, _) = make_chatwidget_manual();
let (mut chat, _, _) = make_chatwidget_manual(None);
chat.config.model = "gpt-5".to_string();
chat.auth_manager = AuthManager::from_auth_for_testing(auth);
@@ -686,7 +691,7 @@ fn rate_limit_switch_prompt_shows_once_per_session() {
#[test]
fn rate_limit_switch_prompt_respects_hidden_notice() {
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
let (mut chat, _, _) = make_chatwidget_manual();
let (mut chat, _, _) = make_chatwidget_manual(None);
chat.config.model = "gpt-5".to_string();
chat.auth_manager = AuthManager::from_auth_for_testing(auth);
chat.config.notices.hide_rate_limit_model_nudge = Some(true);
@@ -702,7 +707,7 @@ fn rate_limit_switch_prompt_respects_hidden_notice() {
#[test]
fn rate_limit_switch_prompt_defers_until_task_complete() {
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
let (mut chat, _, _) = make_chatwidget_manual();
let (mut chat, _, _) = make_chatwidget_manual(None);
chat.config.model = "gpt-5".to_string();
chat.auth_manager = AuthManager::from_auth_for_testing(auth);
@@ -723,7 +728,7 @@ fn rate_limit_switch_prompt_defers_until_task_complete() {
#[test]
fn rate_limit_switch_prompt_popup_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.auth_manager =
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
chat.config.model = "gpt-5".to_string();
@@ -739,7 +744,7 @@ fn rate_limit_switch_prompt_popup_snapshot() {
#[test]
fn exec_approval_emits_proposed_command_and_decision_history() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Trigger an exec approval request with a short, single-line command
let ev = ExecApprovalRequestEvent {
@@ -784,7 +789,7 @@ fn exec_approval_emits_proposed_command_and_decision_history() {
#[test]
fn exec_approval_decision_truncates_multiline_and_long_commands() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Multiline command: modal should show full command, history records decision only
let ev_multi = ExecApprovalRequestEvent {
@@ -969,7 +974,7 @@ fn get_available_model(chat: &ChatWidget, model: &str) -> ModelPreset {
#[test]
fn empty_enter_during_task_does_not_queue() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Simulate running task so submissions would normally be queued.
chat.bottom_pane.set_task_running(true);
@@ -983,7 +988,7 @@ fn empty_enter_during_task_does_not_queue() {
#[test]
fn alt_up_edits_most_recent_queued_message() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Simulate a running task so messages would normally be queued.
chat.bottom_pane.set_task_running(true);
@@ -1016,7 +1021,7 @@ fn alt_up_edits_most_recent_queued_message() {
/// is queued repeatedly.
#[test]
fn enqueueing_history_prompt_multiple_times_is_stable() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Submit an initial prompt to seed history.
chat.bottom_pane.set_composer_text("repeat me".to_string());
@@ -1042,7 +1047,7 @@ fn enqueueing_history_prompt_multiple_times_is_stable() {
#[test]
fn streaming_final_answer_keeps_task_running_state() {
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual();
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None);
chat.on_task_started();
chat.on_agent_message_delta("Final answer line\n".to_string());
@@ -1072,7 +1077,7 @@ fn streaming_final_answer_keeps_task_running_state() {
#[test]
fn ctrl_c_shutdown_ignores_caps_lock() {
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual();
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None);
chat.handle_key_event(KeyEvent::new(KeyCode::Char('C'), KeyModifiers::CONTROL));
@@ -1084,7 +1089,7 @@ fn ctrl_c_shutdown_ignores_caps_lock() {
#[test]
fn ctrl_c_cleared_prompt_is_recoverable_via_history() {
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual();
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None);
chat.bottom_pane.insert_str("draft message ");
chat.bottom_pane
@@ -1118,7 +1123,7 @@ fn ctrl_c_cleared_prompt_is_recoverable_via_history() {
#[test]
fn exec_history_cell_shows_working_then_completed() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Begin command
let begin = begin_exec(&mut chat, "call-1", "echo done");
@@ -1148,7 +1153,7 @@ fn exec_history_cell_shows_working_then_completed() {
#[test]
fn exec_history_cell_shows_working_then_failed() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Begin command
let begin = begin_exec(&mut chat, "call-2", "false");
@@ -1172,7 +1177,7 @@ fn exec_history_cell_shows_working_then_failed() {
#[test]
fn exec_history_shows_unified_exec_startup_commands() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
let begin = begin_exec_with_source(
&mut chat,
@@ -1200,7 +1205,7 @@ fn exec_history_shows_unified_exec_startup_commands() {
/// OpenReviewCustomPrompt to the app event channel.
#[test]
fn review_popup_custom_prompt_action_sends_event() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Open the preset selection popup
chat.open_review_popup();
@@ -1225,7 +1230,7 @@ fn review_popup_custom_prompt_action_sends_event() {
#[test]
fn slash_init_skips_when_project_doc_exists() {
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None);
let tempdir = tempdir().unwrap();
let existing_path = tempdir.path().join(DEFAULT_PROJECT_DOC_FILENAME);
std::fs::write(&existing_path, "existing instructions").unwrap();
@@ -1257,7 +1262,7 @@ fn slash_init_skips_when_project_doc_exists() {
#[test]
fn slash_quit_requests_exit() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.dispatch_command(SlashCommand::Quit);
@@ -1266,7 +1271,7 @@ fn slash_quit_requests_exit() {
#[test]
fn slash_exit_requests_exit() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.dispatch_command(SlashCommand::Exit);
@@ -1275,7 +1280,7 @@ fn slash_exit_requests_exit() {
#[test]
fn slash_resume_opens_picker() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.dispatch_command(SlashCommand::Resume);
@@ -1284,7 +1289,7 @@ fn slash_resume_opens_picker() {
#[test]
fn slash_undo_sends_op() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.dispatch_command(SlashCommand::Undo);
@@ -1296,7 +1301,7 @@ fn slash_undo_sends_op() {
#[test]
fn slash_rollout_displays_current_path() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
let rollout_path = PathBuf::from("/tmp/codex-test-rollout.jsonl");
chat.current_rollout_path = Some(rollout_path.clone());
@@ -1313,7 +1318,7 @@ fn slash_rollout_displays_current_path() {
#[test]
fn slash_rollout_handles_missing_path() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.dispatch_command(SlashCommand::Rollout);
@@ -1332,7 +1337,7 @@ fn slash_rollout_handles_missing_path() {
#[test]
fn undo_success_events_render_info_messages() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "turn-1".to_string(),
@@ -1369,7 +1374,7 @@ fn undo_success_events_render_info_messages() {
#[test]
fn undo_failure_events_render_error_message() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "turn-2".to_string(),
@@ -1404,7 +1409,7 @@ fn undo_failure_events_render_error_message() {
#[test]
fn undo_started_hides_interrupt_hint() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "turn-hint".to_string(),
@@ -1424,7 +1429,7 @@ fn undo_started_hides_interrupt_hint() {
/// The commit picker shows only commit subjects (no timestamps).
#[test]
fn review_commit_picker_shows_subjects_without_timestamps() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Open the Review presets parent popup.
chat.open_review_popup();
@@ -1486,7 +1491,7 @@ fn review_commit_picker_shows_subjects_without_timestamps() {
/// and uses the same text for the user-facing hint.
#[test]
fn custom_prompt_submit_sends_review_op() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.show_review_custom_prompt();
// Paste prompt text via ChatWidget handler, then submit
@@ -1514,7 +1519,7 @@ fn custom_prompt_submit_sends_review_op() {
/// Hitting Enter on an empty custom prompt view does not submit.
#[test]
fn custom_prompt_enter_empty_does_not_send() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.show_review_custom_prompt();
// Enter without any text
@@ -1526,7 +1531,7 @@ fn custom_prompt_enter_empty_does_not_send() {
#[test]
fn view_image_tool_call_adds_history_cell() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
let image_path = chat.config.cwd.join("example.png");
chat.handle_codex_event(Event {
@@ -1547,7 +1552,7 @@ fn view_image_tool_call_adds_history_cell() {
// marker (replacing the spinner) and flushes it into history.
#[test]
fn interrupt_exec_marks_failed_snapshot() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Begin a long-running command so we have an active exec cell with a spinner.
begin_exec(&mut chat, "call-int", "sleep 1");
@@ -1576,7 +1581,7 @@ fn interrupt_exec_marks_failed_snapshot() {
// suggesting the user to tell the model what to do differently and to use /feedback.
#[test]
fn interrupted_turn_error_message_snapshot() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Simulate an in-progress task so the widget is in a running state.
chat.handle_codex_event(Event {
@@ -1607,7 +1612,7 @@ fn interrupted_turn_error_message_snapshot() {
/// parent popup, pressing Esc again dismisses all panels (back to normal mode).
#[test]
fn review_custom_prompt_escape_navigates_back_then_dismisses() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Open the Review presets parent popup.
chat.open_review_popup();
@@ -1642,7 +1647,7 @@ fn review_custom_prompt_escape_navigates_back_then_dismisses() {
/// parent popup, pressing Esc again dismisses all panels (back to normal mode).
#[tokio::test]
async fn review_branch_picker_escape_navigates_back_then_dismisses() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Open the Review presets parent popup.
chat.open_review_popup();
@@ -1729,7 +1734,7 @@ fn render_bottom_popup(chat: &ChatWidget, width: u16) -> String {
#[test]
fn model_selection_popup_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.config.model = "gpt-5-codex".to_string();
chat.open_model_popup();
@@ -1740,7 +1745,7 @@ fn model_selection_popup_snapshot() {
#[test]
fn approvals_selection_popup_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.config.notices.hide_full_access_warning = None;
chat.open_approvals_popup();
@@ -1779,7 +1784,7 @@ fn preset_matching_ignores_extra_writable_roots() {
#[test]
fn full_access_confirmation_popup_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
let preset = builtin_approval_presets()
.into_iter()
@@ -1794,7 +1799,7 @@ fn full_access_confirmation_popup_snapshot() {
#[cfg(target_os = "windows")]
#[test]
fn windows_auto_mode_prompt_requests_enabling_sandbox_feature() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
let preset = builtin_approval_presets()
.into_iter()
@@ -1812,7 +1817,7 @@ fn windows_auto_mode_prompt_requests_enabling_sandbox_feature() {
#[cfg(target_os = "windows")]
#[test]
fn startup_prompts_for_windows_sandbox_when_agent_requested() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
set_windows_sandbox_enabled(false);
chat.config.forced_auto_mode_downgraded_on_windows = true;
@@ -1834,7 +1839,7 @@ fn startup_prompts_for_windows_sandbox_when_agent_requested() {
#[test]
fn model_reasoning_selection_popup_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
set_chatgpt_auth(&mut chat);
chat.config.model = "gpt-5.1-codex-max".to_string();
@@ -1849,7 +1854,7 @@ fn model_reasoning_selection_popup_snapshot() {
#[test]
fn model_reasoning_selection_popup_extra_high_warning_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
set_chatgpt_auth(&mut chat);
chat.config.model = "gpt-5.1-codex-max".to_string();
@@ -1864,7 +1869,7 @@ fn model_reasoning_selection_popup_extra_high_warning_snapshot() {
#[test]
fn reasoning_popup_shows_extra_high_with_space() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
set_chatgpt_auth(&mut chat);
chat.config.model = "gpt-5.1-codex-max".to_string();
@@ -1885,7 +1890,7 @@ fn reasoning_popup_shows_extra_high_with_space() {
#[test]
fn single_reasoning_option_skips_selection() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
let single_effort = vec![ReasoningEffortPreset {
effort: ReasoningEffortConfig::High,
@@ -1925,7 +1930,7 @@ fn single_reasoning_option_skips_selection() {
#[test]
fn feedback_selection_popup_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Open the feedback category selection popup via slash command.
chat.dispatch_command(SlashCommand::Feedback);
@@ -1936,7 +1941,7 @@ fn feedback_selection_popup_snapshot() {
#[test]
fn feedback_upload_consent_popup_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Open the consent popup directly for a chosen category.
chat.open_feedback_consent(crate::app_event::FeedbackCategory::Bug);
@@ -1947,7 +1952,7 @@ fn feedback_upload_consent_popup_snapshot() {
#[test]
fn reasoning_popup_escape_returns_to_model_popup() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.config.model = "gpt-5.1".to_string();
chat.open_model_popup();
@@ -1967,7 +1972,7 @@ fn reasoning_popup_escape_returns_to_model_popup() {
#[test]
fn exec_history_extends_previous_when_consecutive() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// 1) Start "ls -la" (List)
let begin_ls = begin_exec(&mut chat, "call-ls", "ls -la");
@@ -1998,7 +2003,7 @@ fn exec_history_extends_previous_when_consecutive() {
#[test]
fn user_shell_command_renders_output_not_exploring() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
let begin_ls = begin_exec_with_source(
&mut chat,
@@ -2021,7 +2026,7 @@ fn user_shell_command_renders_output_not_exploring() {
#[test]
fn disabled_slash_command_while_task_running_snapshot() {
// Build a chat widget and simulate an active task
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.bottom_pane.set_task_running(true);
// Dispatch a command that is unavailable while a task runs (e.g., /model)
@@ -2045,7 +2050,7 @@ fn disabled_slash_command_while_task_running_snapshot() {
#[test]
fn approval_modal_exec_snapshot() {
// Build a chat widget with manual channels to avoid spawning the agent.
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Ensure policy allows surfacing approvals explicitly (not strictly required for direct event).
chat.config.approval_policy = AskForApproval::OnRequest;
// Inject an exec approval request to display the approval modal.
@@ -2100,7 +2105,7 @@ fn approval_modal_exec_snapshot() {
// Ensures spacing looks correct when no reason text is provided.
#[test]
fn approval_modal_exec_without_reason_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.config.approval_policy = AskForApproval::OnRequest;
let ev = ExecApprovalRequestEvent {
@@ -2139,7 +2144,7 @@ fn approval_modal_exec_without_reason_snapshot() {
// Snapshot test: patch approval modal
#[test]
fn approval_modal_patch_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.config.approval_policy = AskForApproval::OnRequest;
// Build a small changeset and a reason/grant_root to exercise the prompt text.
@@ -2178,7 +2183,7 @@ fn approval_modal_patch_snapshot() {
#[test]
fn interrupt_restores_queued_messages_into_composer() {
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None);
// Simulate a running task to enable queuing of user inputs.
chat.bottom_pane.set_task_running(true);
@@ -2217,7 +2222,7 @@ fn interrupt_restores_queued_messages_into_composer() {
#[test]
fn interrupt_prepends_queued_messages_before_existing_composer_text() {
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None);
chat.bottom_pane.set_task_running(true);
chat.bottom_pane
@@ -2255,7 +2260,7 @@ fn interrupt_prepends_queued_messages_before_existing_composer_text() {
fn ui_snapshots_small_heights_idle() {
use ratatui::Terminal;
use ratatui::backend::TestBackend;
let (chat, _rx, _op_rx) = make_chatwidget_manual();
let (chat, _rx, _op_rx) = make_chatwidget_manual(None);
for h in [1u16, 2, 3] {
let name = format!("chat_small_idle_h{h}");
let mut terminal = Terminal::new(TestBackend::new(40, h)).expect("create terminal");
@@ -2272,7 +2277,7 @@ fn ui_snapshots_small_heights_idle() {
fn ui_snapshots_small_heights_task_running() {
use ratatui::Terminal;
use ratatui::backend::TestBackend;
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Activate status line
chat.handle_codex_event(Event {
id: "task-1".into(),
@@ -2303,7 +2308,7 @@ fn ui_snapshots_small_heights_task_running() {
fn status_widget_and_approval_modal_snapshot() {
use codex_core::protocol::ExecApprovalRequestEvent;
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Begin a running task so the status indicator would be active.
chat.handle_codex_event(Event {
id: "task-1".into(),
@@ -2356,7 +2361,7 @@ fn status_widget_and_approval_modal_snapshot() {
// Ensures the VT100 rendering of the status indicator is stable when active.
#[test]
fn status_widget_active_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Activate the status indicator by simulating a task start.
chat.handle_codex_event(Event {
id: "task-1".into(),
@@ -2383,7 +2388,7 @@ fn status_widget_active_snapshot() {
#[test]
fn background_event_updates_status_header() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "bg-1".into(),
@@ -2399,7 +2404,7 @@ fn background_event_updates_status_header() {
#[test]
fn apply_patch_events_emit_history_cells() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// 1) Approval request -> proposed patch summary cell
let mut changes = HashMap::new();
@@ -2497,7 +2502,7 @@ fn apply_patch_events_emit_history_cells() {
#[test]
fn apply_patch_manual_approval_adjusts_header() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
let mut proposed_changes = HashMap::new();
proposed_changes.insert(
@@ -2546,7 +2551,7 @@ fn apply_patch_manual_approval_adjusts_header() {
#[test]
fn apply_patch_manual_flow_snapshot() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
let mut proposed_changes = HashMap::new();
proposed_changes.insert(
@@ -2599,7 +2604,7 @@ fn apply_patch_manual_flow_snapshot() {
#[test]
fn apply_patch_approval_sends_op_with_submission_id() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Simulate receiving an approval request with a distinct submission id and call id
let mut changes = HashMap::new();
changes.insert(
@@ -2638,7 +2643,7 @@ fn apply_patch_approval_sends_op_with_submission_id() {
#[test]
fn apply_patch_full_flow_integration_like() {
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None);
// 1) Backend requests approval
let mut changes = HashMap::new();
@@ -2716,7 +2721,7 @@ fn apply_patch_full_flow_integration_like() {
#[test]
fn apply_patch_untrusted_shows_approval_modal() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
// Ensure approval policy is untrusted (OnRequest)
chat.config.approval_policy = AskForApproval::OnRequest;
@@ -2761,7 +2766,7 @@ fn apply_patch_untrusted_shows_approval_modal() {
#[test]
fn apply_patch_request_shows_diff_summary() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Ensure we are in OnRequest so an approval is surfaced
chat.config.approval_policy = AskForApproval::OnRequest;
@@ -2827,7 +2832,7 @@ fn apply_patch_request_shows_diff_summary() {
#[test]
fn plan_update_renders_history_cell() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
let update = UpdatePlanArgs {
explanation: Some("Adapting plan".to_string()),
plan: vec![
@@ -2863,7 +2868,7 @@ fn plan_update_renders_history_cell() {
#[test]
fn stream_error_updates_status_indicator() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.bottom_pane.set_task_running(true);
let msg = "Reconnecting... 2/5";
chat.handle_codex_event(Event {
@@ -2888,7 +2893,7 @@ fn stream_error_updates_status_indicator() {
#[test]
fn warning_event_adds_warning_history_cell() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "sub-1".into(),
msg: EventMsg::Warning(WarningEvent {
@@ -2907,7 +2912,7 @@ fn warning_event_adds_warning_history_cell() {
#[test]
fn stream_recovery_restores_previous_status_header() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "task".into(),
msg: EventMsg::TaskStarted(TaskStartedEvent {
@@ -2940,7 +2945,7 @@ fn stream_recovery_restores_previous_status_header() {
#[test]
fn multiple_agent_messages_in_single_turn_emit_multiple_headers() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Begin turn
chat.handle_codex_event(Event {
@@ -2994,7 +2999,7 @@ fn multiple_agent_messages_in_single_turn_emit_multiple_headers() {
#[test]
fn final_reasoning_then_message_without_deltas_are_rendered() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// No deltas; only final reasoning followed by final message.
chat.handle_codex_event(Event {
@@ -3021,7 +3026,7 @@ fn final_reasoning_then_message_without_deltas_are_rendered() {
#[test]
fn deltas_then_same_final_message_are_rendered_snapshot() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Stream some reasoning deltas first.
chat.handle_codex_event(Event {
@@ -3085,7 +3090,7 @@ fn deltas_then_same_final_message_are_rendered_snapshot() {
// then the exec block, another blank line, the status line, a blank line, and the composer.
#[test]
fn chatwidget_exec_and_status_layout_vt100_snapshot() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "t1".into(),
msg: EventMsg::AgentMessage(AgentMessageEvent { message: "Im going to search the repo for where “Change Approved” is rendered to update that view.".into() }),
@@ -3177,7 +3182,7 @@ fn chatwidget_exec_and_status_layout_vt100_snapshot() {
// E2E vt100 snapshot for complex markdown with indented and nested fenced code blocks
#[test]
fn chatwidget_markdown_code_blocks_vt100_snapshot() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
// Simulate a final agent message via streaming deltas instead of a single message
@@ -3268,7 +3273,7 @@ printf 'fenced within fenced\n'
#[test]
fn chatwidget_tall() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
chat.handle_codex_event(Event {
id: "t1".into(),
msg: EventMsg::TaskStarted(TaskStartedEvent {