From 694d8d45bd3d440fa4f4cbf22667ac6c17a13758 Mon Sep 17 00:00:00 2001 From: andrewgu-oai Date: Thu, 24 Sep 2026 20:14:22 +0000 Subject: [PATCH] Remove GPT-5.4 from bundled catalogs and preserve migration prompts (#47932) ## Why Saved model selections can outlive their catalog entries. Removing GPT-5.4 needs to preserve a migration path without applying it to other providers that may still support the same model slug. ## What changed - Remove `gpt-5.4` from the bundled catalog and `openai.gpt-5.4` from the Bedrock catalogs, including GovCloud. - Add fallback migration prompts to GPT-6 Sol for saved GPT-5.4 selections on OpenAI and Amazon Bedrock. Scope fallback migrations, including the existing GPT-5.4 Mini migration, to their owning providers while honoring catalog metadata. - Update tests that depend on bundled GPT-5.4 metadata to use GPT-5.5 or explicit model overrides. ## Testing Extend migration tests to cover catalog metadata precedence, acknowledged targets, missing or hidden replacements, and preserving the provider while saving the replacement model and its default reasoning effort. Update Bedrock catalog assertions and picker snapshots. GitOrigin-RevId: f739795da423b9622db2a416be6c52b01b8e7a71 --- .../app-server/tests/suite/v2/analytics.rs | 4 +- .../multi_agent_v2_developer_instructions.rs | 4 +- .../tests/suite/v2/safety_check_downgrade.rs | 2 +- .../app-server/tests/suite/v2/thread_start.rs | 4 +- codex-rs/core/src/guardian/tests.rs | 8 +- codex-rs/core/tests/suite/apply_patch_cli.rs | 18 +- codex-rs/core/tests/suite/approvals.rs | 2 +- codex-rs/core/tests/suite/client.rs | 2 +- codex-rs/core/tests/suite/compact.rs | 2 +- codex-rs/core/tests/suite/guardian_review.rs | 2 +- codex-rs/core/tests/suite/hooks.rs | 4 +- .../core/tests/suite/mcp_turn_metadata.rs | 4 +- .../core/tests/suite/model_visible_layout.rs | 8 +- codex-rs/core/tests/suite/models_cache_ttl.rs | 4 +- codex-rs/core/tests/suite/network_approval.rs | 4 +- codex-rs/core/tests/suite/otel.rs | 2 +- codex-rs/core/tests/suite/personality.rs | 17 +- codex-rs/core/tests/suite/rmcp_client.rs | 2 +- codex-rs/core/tests/suite/truncation.rs | 8 +- codex-rs/core/tests/suite/unified_exec.rs | 4 +- codex-rs/core/tests/suite/view_image.rs | 12 +- codex-rs/core/tests/suite/web_search.rs | 8 +- codex-rs/model-provider-info/src/lib.rs | 1 - .../src/amazon_bedrock/catalog.rs | 21 +- codex-rs/model-provider/src/provider.rs | 2 - codex-rs/models-manager/models.json | 112 --------- codex-rs/tui/src/app/startup_prompts.rs | 29 ++- codex-rs/tui/src/app/tests/model_catalog.rs | 235 ++++++++++++------ .../src/app/tests/startup_defaults_tests.rs | 1 + codex-rs/tui/src/app_server_session.rs | 4 +- ...idget__tests__bedrock_govcloud_models.snap | 1 - ...twidget__tests__bedrock_mantle_models.snap | 1 - ..._line_goal_active_token_budget_footer.snap | 2 +- ...tus_line_goal_complete_elapsed_footer.snap | 2 +- ...ts__terminal_title_model_display_name.snap | 2 +- .../src/chatwidget/tests/status_and_layout.rs | 6 +- ...ver_session__tests__resumed_plan_mode.snap | 2 +- 37 files changed, 251 insertions(+), 295 deletions(-) diff --git a/codex-rs/app-server/tests/suite/v2/analytics.rs b/codex-rs/app-server/tests/suite/v2/analytics.rs index c6ca49bb68..560cb26cf7 100644 --- a/codex-rs/app-server/tests/suite/v2/analytics.rs +++ b/codex-rs/app-server/tests/suite/v2/analytics.rs @@ -808,8 +808,8 @@ async fn assert_plugin_measurement_analytics(remote: bool, background: bool) -> let model = bundled_models_response()? .models .into_iter() - .find(|model| model.slug == "gpt-5.4") - .expect("bundled gpt-5.4 model"); + .find(|model| model.slug == "gpt-5.5") + .expect("bundled gpt-5.5 model"); let models = [ ("initial-model", ReasoningEffort::Low), ("invoking-model", ReasoningEffort::High), diff --git a/codex-rs/app-server/tests/suite/v2/multi_agent_v2_developer_instructions.rs b/codex-rs/app-server/tests/suite/v2/multi_agent_v2_developer_instructions.rs index d4e0c71e43..cab6b5253c 100644 --- a/codex-rs/app-server/tests/suite/v2/multi_agent_v2_developer_instructions.rs +++ b/codex-rs/app-server/tests/suite/v2/multi_agent_v2_developer_instructions.rs @@ -635,7 +635,7 @@ async fn cold_resume_preserves_effective_developer_instructions_for_worker( )); } MockResponsesConfig::new(&server.uri()) - .with_model("gpt-5.4") + .with_model("gpt-5.5") .with_root_config(&format!( "developer_instructions = {PARENT_INSTRUCTIONS:?}\nmodel_reasoning_effort = \"high\"" )) @@ -650,7 +650,7 @@ async fn cold_resume_preserves_effective_developer_instructions_for_worker( .await?; let ThreadStartResponse { thread, .. } = app_server .start_thread(ThreadStartParams { - model: Some("gpt-5.4".to_string()), + model: Some("gpt-5.5".to_string()), history_mode: Some(history_mode), ..Default::default() }) diff --git a/codex-rs/app-server/tests/suite/v2/safety_check_downgrade.rs b/codex-rs/app-server/tests/suite/v2/safety_check_downgrade.rs index ece47b4cc9..0ac5cdb2b7 100644 --- a/codex-rs/app-server/tests/suite/v2/safety_check_downgrade.rs +++ b/codex-rs/app-server/tests/suite/v2/safety_check_downgrade.rs @@ -27,7 +27,7 @@ use tokio::time::timeout; use wiremock::ResponseTemplate; const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); -const REQUESTED_MODEL: &str = "gpt-5.4"; +const REQUESTED_MODEL: &str = "gpt-5.5"; const SERVER_MODEL: &str = "gpt-5.3-codex"; const TRUSTED_ACCESS_FOR_CYBER_VERIFICATION: &str = "trusted_access_for_cyber"; const CYBER_POLICY_MESSAGE: &str = diff --git a/codex-rs/app-server/tests/suite/v2/thread_start.rs b/codex-rs/app-server/tests/suite/v2/thread_start.rs index 49aeb90d11..405da40168 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_start.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_start.rs @@ -256,7 +256,7 @@ async fn thread_start_provider_model_fallback_uses_bedrock_static_catalog() -> R } let supported_with_fallback = start_thread_with_model( &mut mcp, - "openai.gpt-5.4", + "openai.gpt-5.5", /*allow_provider_model_fallback*/ true, ) .await?; @@ -273,7 +273,7 @@ async fn thread_start_provider_model_fallback_uses_bedrock_static_catalog() -> R supported_with_fallback.model, unsupported_without_fallback.model, ], - vec!["openai.gpt-6-sol", "openai.gpt-5.4", "gpt-5.4-mini"] + vec!["openai.gpt-6-sol", "openai.gpt-5.5", "gpt-5.4-mini"] ); Ok(()) } diff --git a/codex-rs/core/src/guardian/tests.rs b/codex-rs/core/src/guardian/tests.rs index 6b8a676187..9f53c78676 100644 --- a/codex-rs/core/src/guardian/tests.rs +++ b/codex-rs/core/src/guardian/tests.rs @@ -31,7 +31,7 @@ use codex_guardian_context::ConversationTranscriptEntryKind; use codex_guardian_reviewer::guardian_output_contract_prompt; use codex_history::RolloutItem; use codex_model_provider::create_model_provider; -use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_4_MODEL_ID; +use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_5_MODEL_ID; use codex_model_provider_info::AMAZON_BEDROCK_PROVIDER_ID; use codex_model_provider_info::ModelProviderInfo; use codex_model_provider_info::OPENAI_PROVIDER_ID; @@ -4087,7 +4087,7 @@ async fn guardian_review_session_config_allows_pinned_disabled_feature() { } #[tokio::test] -async fn guardian_review_session_config_keeps_bedrock_provider_for_bedrock_gpt_5_4() { +async fn guardian_review_session_config_keeps_bedrock_provider_for_bedrock_gpt_5_5() { let mut parent_config = test_config().await; parent_config.model_provider_id = AMAZON_BEDROCK_PROVIDER_ID.to_string(); parent_config.model_provider = @@ -4096,7 +4096,7 @@ async fn guardian_review_session_config_keeps_bedrock_provider_for_bedrock_gpt_5 let guardian_config = build_guardian_review_session_config_for_test( crate::guardian::test_host::build_reviewer_config(&parent_config).expect("reviewer config"), /*live_network_config*/ None, - AMAZON_BEDROCK_GPT_5_4_MODEL_ID, + AMAZON_BEDROCK_GPT_5_5_MODEL_ID, Some(ReasoningEffort::Low), ReasoningSummary::default(), /*personality*/ None, @@ -4115,7 +4115,7 @@ async fn guardian_review_session_config_keeps_bedrock_provider_for_bedrock_gpt_5 guardian_config.model_provider, ), ( - Some(AMAZON_BEDROCK_GPT_5_4_MODEL_ID.to_string()), + Some(AMAZON_BEDROCK_GPT_5_5_MODEL_ID.to_string()), AMAZON_BEDROCK_PROVIDER_ID.to_string(), expected_model_provider, ) diff --git a/codex-rs/core/tests/suite/apply_patch_cli.rs b/codex-rs/core/tests/suite/apply_patch_cli.rs index aba4a3b3c8..23886c46e7 100644 --- a/codex-rs/core/tests/suite/apply_patch_cli.rs +++ b/codex-rs/core/tests/suite/apply_patch_cli.rs @@ -492,7 +492,7 @@ async fn apply_patch_cli_uses_codex_self_exe_with_linux_sandbox_helper_alias() - async fn apply_patch_cli_multiple_operations_integration() -> Result<()> { skip_if_no_network!(Ok(())); - let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.4")).await?; + let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.5")).await?; // Seed workspace state harness.write_file("modify.txt", "line1\nline2\n").await?; @@ -1014,7 +1014,7 @@ async fn intercepted_apply_patch_updates_absolute_target_after_turn_cwd_is_remov "the default Windows restricted-token test backend cannot enforce denied reads" ); - let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.4")).await?; + let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.5")).await?; let test = harness.test(); let workspace = test.workspace_path_uri("")?; let original_cwd = test.workspace_path_uri("policy-cwd")?; @@ -1165,7 +1165,7 @@ async fn escalated_patch_rejects_symlink_swapped_after_approval_request() -> Res fs::write(work.join("file.txt"), "original\n")?; fs::write(&outside, "original\n")?; let harness = apply_patch_harness_with(move |builder| { - builder.with_model("gpt-5.4").with_config(move |config| { + builder.with_model("gpt-5.5").with_config(move |config| { config.cwd = work.try_into().expect("absolute workspace"); config.workspace_roots = vec![config.cwd.clone()]; config @@ -1545,7 +1545,7 @@ async fn apply_patch_exec_command_heredoc_with_cd_updates_relative_workdir() -> skip_if_wine_exec!(Ok(()), "uses a POSIX shell heredoc and cd command"); skip_if_no_network!(Ok(())); - let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.4")).await?; + let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.5")).await?; // Prepare a file inside a subdir; update it via cd && apply_patch heredoc form. harness.write_file("sub/in_sub.txt", "before\n").await?; @@ -1585,7 +1585,7 @@ async fn apply_patch_cli_can_use_exec_command_output_as_patch_input() -> Result< ); let harness = - apply_patch_harness_with(|builder| builder.with_model("gpt-5.4").with_windows_cmd_shell()) + apply_patch_harness_with(|builder| builder.with_model("gpt-5.5").with_windows_cmd_shell()) .await?; let source_contents = "line1\nnaïve café\nline3\n"; @@ -1829,7 +1829,7 @@ async fn apply_patch_exec_command_heredoc_with_cd_emits_turn_diff() -> Result<() skip_if_wine_exec!(Ok(()), "uses a POSIX shell heredoc and cd command"); skip_if_no_network!(Ok(())); - let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.4")).await?; + let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.5")).await?; let test = harness.test(); let codex = test.codex.clone(); @@ -1895,7 +1895,7 @@ async fn apply_patch_turn_diff_paths_stay_repo_relative_when_session_cwd_is_nest let harness = apply_patch_harness_with(|builder| { builder - .with_model("gpt-5.4") + .with_model("gpt-5.5") .with_config(|config| { config.cwd = config.cwd.join("subdir"); }) @@ -1987,7 +1987,7 @@ async fn apply_patch_turn_diff_skips_git_root_when_feature_is_enabled( let harness = apply_patch_harness_with(|builder| { builder - .with_model("gpt-5.4") + .with_model("gpt-5.5") .with_config(move |config| { config.cwd = config.cwd.join("subdir"); if cwd_relative_turn_diffs { @@ -2081,7 +2081,7 @@ async fn apply_patch_exec_command_failure_propagates_error_and_skips_diff() -> R skip_if_wine_exec!(Ok(()), "uses a POSIX shell heredoc"); skip_if_no_network!(Ok(())); - let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.4")).await?; + let harness = apply_patch_harness_with(|builder| builder.with_model("gpt-5.5")).await?; let test = harness.test(); let codex = test.codex.clone(); diff --git a/codex-rs/core/tests/suite/approvals.rs b/codex-rs/core/tests/suite/approvals.rs index 105f706fc5..fd229e37d7 100644 --- a/codex-rs/core/tests/suite/approvals.rs +++ b/codex-rs/core/tests/suite/approvals.rs @@ -2153,7 +2153,7 @@ async fn approving_apply_patch_for_session_skips_future_prompts_for_same_file() let sandbox_policy_for_config = sandbox_policy.clone(); let mut builder = test_codex() - .with_model("gpt-5.4") + .with_model("gpt-5.5") .with_config(move |config| { config.permissions.approval_policy = Constrained::allow_any(approval_policy); config diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index e8591fddc9..a260ce8805 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -1372,7 +1372,7 @@ async fn resume_replays_image_tool_outputs_with_detail() { .await; let codex_home = Arc::new(TempDir::new().unwrap()); - let mut builder = test_codex().with_model("gpt-5.4"); + let mut builder = test_codex().with_model("gpt-5.5"); let test = builder .resume(&server, codex_home, session_path.clone()) .await diff --git a/codex-rs/core/tests/suite/compact.rs b/codex-rs/core/tests/suite/compact.rs index 76d8354a32..786b8d93d0 100644 --- a/codex-rs/core/tests/suite/compact.rs +++ b/codex-rs/core/tests/suite/compact.rs @@ -5183,7 +5183,7 @@ async fn snapshot_request_shape_pre_turn_compaction_strips_incoming_model_switch let model_provider = non_openai_model_provider(&server); let test = test_codex() .with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing()) - .with_model(previous_model) + .with_model_info_override(previous_model, |_| {}) .with_config(move |config| { config.update_plan_enabled = true; config.model_provider = model_provider; diff --git a/codex-rs/core/tests/suite/guardian_review.rs b/codex-rs/core/tests/suite/guardian_review.rs index b15a8a9aff..c69d0ffb29 100644 --- a/codex-rs/core/tests/suite/guardian_review.rs +++ b/codex-rs/core/tests/suite/guardian_review.rs @@ -1485,7 +1485,7 @@ async fn guardian_session_is_reused_for_consecutive_tool_reviews_without_prewarm let mut extensions = ExtensionRegistryBuilder::::new(); extensions.tool_lifecycle_contributor(lifecycle_recorder.clone()); let mut builder = test_codex() - .with_model("gpt-5.4") + .with_model_info_override("gpt-5.4", |_| {}) .with_extensions(Arc::new(extensions.build())) .with_config(move |config| { let secret_file = config.cwd.join("guardian-secret.txt"); diff --git a/codex-rs/core/tests/suite/hooks.rs b/codex-rs/core/tests/suite/hooks.rs index de752ac5e8..02d9dffeae 100644 --- a/codex-rs/core/tests/suite/hooks.rs +++ b/codex-rs/core/tests/suite/hooks.rs @@ -3651,8 +3651,8 @@ async fn pre_tool_use_hook_model_tracks_step_after_a_turn_update() -> Result<()> .expect("bundled models should parse") .models .into_iter() - .find(|model| model.slug == "gpt-5.4") - .expect("bundled gpt-5.4 model"); + .find(|model| model.slug == "gpt-5.5") + .expect("bundled gpt-5.5 model"); let models = [model_a, model_b] .into_iter() .map(|slug| { diff --git a/codex-rs/core/tests/suite/mcp_turn_metadata.rs b/codex-rs/core/tests/suite/mcp_turn_metadata.rs index 75b3ba1dfc..2c4343e389 100644 --- a/codex-rs/core/tests/suite/mcp_turn_metadata.rs +++ b/codex-rs/core/tests/suite/mcp_turn_metadata.rs @@ -475,8 +475,8 @@ fn attribution_models(model_slugs: [&str; 2]) -> Vec Result<()> { let mut builder = test_codex() .with_extensions(skills_extensions()) - .with_model("gpt-5.4") + .with_model("gpt-5.5") .with_config(|config| { config.update_plan_enabled = true; }); @@ -399,7 +399,7 @@ async fn snapshot_model_visible_layout_cwd_change_refreshes_agents() -> Result<( let mut builder = test_codex() .with_config(|config| config.update_plan_enabled = true) .with_extensions(skills_extensions()) - .with_model("gpt-5.4"); + .with_model("gpt-5.5"); let test = builder.build(&server).await?; let cwd_one = test.cwd_path().join("agents_one"); let cwd_two = test.cwd_path().join("agents_two"); @@ -546,9 +546,9 @@ async fn snapshot_model_visible_layout_resume_with_personality_change() -> Resul let mut resume_builder = test_codex() .with_extensions(skills_extensions()) + .with_model_info_override("gpt-5.4", |_| {}) .with_config(|config| { config.update_plan_enabled = true; - config.model = Some("gpt-5.4".to_string()); config.personality = Some(Personality::Pragmatic); }); let resumed = resume_builder.restart(&server, &initial).await?; @@ -654,9 +654,9 @@ async fn snapshot_model_visible_layout_resume_override_matches_rollout_model() - let mut resume_builder = test_codex() .with_extensions(skills_extensions()) + .with_model_info_override("gpt-5.4", |_| {}) .with_config(|config| { config.update_plan_enabled = true; - config.model = Some("gpt-5.4".to_string()); }); let resumed = resume_builder.restart(&server, &initial).await?; let resume_override_cwd = resumed.cwd_path().join(PRETURN_CONTEXT_DIFF_CWD); diff --git a/codex-rs/core/tests/suite/models_cache_ttl.rs b/codex-rs/core/tests/suite/models_cache_ttl.rs index 4a3cf558c1..2ffb1b80bd 100644 --- a/codex-rs/core/tests/suite/models_cache_ttl.rs +++ b/codex-rs/core/tests/suite/models_cache_ttl.rs @@ -65,7 +65,7 @@ async fn guardian_reused_reviewer_avoids_stale_catalog_lookup() -> Result<()> { models: bundled .models .into_iter() - .filter(|model| ["gpt-5.4", "codex-auto-review"].contains(&model.slug.as_str())) + .filter(|model| ["gpt-5.5", "codex-auto-review"].contains(&model.slug.as_str())) .collect(), }; assert_eq!(catalog.models.len(), 2); @@ -74,7 +74,7 @@ async fn guardian_reused_reviewer_avoids_stale_catalog_lookup() -> Result<()> { let mut builder = test_codex() .with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing()) - .with_model("gpt-5.4"); + .with_model("gpt-5.5"); builder = builder.with_config(|config| { config.permissions.approval_policy = Constrained::allow_any(AskForApproval::OnRequest); config.approvals_reviewer = ApprovalsReviewer::AutoReview; diff --git a/codex-rs/core/tests/suite/network_approval.rs b/codex-rs/core/tests/suite/network_approval.rs index 985bdb9a36..95fec8a8a2 100644 --- a/codex-rs/core/tests/suite/network_approval.rs +++ b/codex-rs/core/tests/suite/network_approval.rs @@ -3025,8 +3025,8 @@ pub(super) fn guardian_parent_catalog() -> ModelsResponse { .expect("bundled model catalog") .models .into_iter() - .find(|model| model.slug == "gpt-5.4") - .expect("gpt-5.4 in bundled catalog"); + .find(|model| model.slug == "gpt-5.5") + .expect("gpt-5.5 in bundled catalog"); // Keep safety settings compatible so active publication can switch A to B. ModelsResponse { models: ["guardian-parent-a", "guardian-parent-b"] diff --git a/codex-rs/core/tests/suite/otel.rs b/codex-rs/core/tests/suite/otel.rs index 64b8790f3d..386312a2bf 100644 --- a/codex-rs/core/tests/suite/otel.rs +++ b/codex-rs/core/tests/suite/otel.rs @@ -122,7 +122,7 @@ async fn responses_api_emits_api_request_event() { let response_mock = mount_sse_once(&server, sse(vec![ev_completed("done")])).await; let TestCodex { codex, .. } = test_codex() - .with_model("gpt-5.4") + .with_model("gpt-5.5") .with_config(|config| { config.service_tier = Some(ServiceTier::Fast.request_value().to_string()); config.model_reasoning_effort = Some(ReasoningEffort::High); diff --git a/codex-rs/core/tests/suite/personality.rs b/codex-rs/core/tests/suite/personality.rs index b8e1985882..a1872d46ba 100644 --- a/codex-rs/core/tests/suite/personality.rs +++ b/codex-rs/core/tests/suite/personality.rs @@ -22,8 +22,6 @@ use core_test_support::wait_for_event; use pretty_assertions::assert_eq; use test_case::test_case; -const LOCAL_FRIENDLY_TEMPLATE: &str = - "You optimize for team morale and being a supportive teammate as much as code quality."; const BUNDLED_FRIENDLY_TEMPLATE: &str = "You have a vivid inner life as Codex:"; const CUSTOM_INSTRUCTIONS: &str = "Custom instructions\n# Personality\nThis must remain\n## Writing Style\nThis must also remain\n# General\nGeneral instructions"; @@ -225,13 +223,8 @@ async fn default_instructions_are_friendly_without_config_toml() -> anyhow::Resu Ok(()) } -#[test_case("gpt-5.4", LOCAL_FRIENDLY_TEMPLATE; "gpt_5_4")] -#[test_case("gpt-5.5", BUNDLED_FRIENDLY_TEMPLATE; "gpt_5_5")] #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn fixed_friendly_personality_ignores_pragmatic_update( - model: &str, - friendly_template: &str, -) -> anyhow::Result<()> { +async fn fixed_friendly_personality_ignores_pragmatic_update() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; @@ -240,7 +233,7 @@ async fn fixed_friendly_personality_ignores_pragmatic_update( vec![sse_completed("resp-1"), sse_completed("resp-2")], ) .await; - let mut builder = test_codex().with_model(model).with_config(|config| { + let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| { config.personality = Some(Personality::Friendly); }); let test = builder.build_with_auto_env(&server).await?; @@ -259,7 +252,11 @@ async fn fixed_friendly_personality_ignores_pragmatic_update( let requests = responses.requests(); assert_eq!(requests.len(), 2); - assert!(requests[0].instructions_text().contains(friendly_template)); + assert!( + requests[0] + .instructions_text() + .contains(BUNDLED_FRIENDLY_TEMPLATE) + ); assert_eq!( requests[1].instructions_text(), requests[0].instructions_text() diff --git a/codex-rs/core/tests/suite/rmcp_client.rs b/codex-rs/core/tests/suite/rmcp_client.rs index 0fca7d0ea5..91d3e59c1d 100644 --- a/codex-rs/core/tests/suite/rmcp_client.rs +++ b/codex-rs/core/tests/suite/rmcp_client.rs @@ -3118,7 +3118,7 @@ async fn stdio_image_responses_preserve_original_detail_metadata() -> anyhow::Re let rmcp_test_server_bin = remote_aware_stdio_server_bin()?; let fixture = test_codex() - .with_model("gpt-5.4") + .with_model("gpt-5.5") .with_config(move |config| { insert_mcp_server( config, diff --git a/codex-rs/core/tests/suite/truncation.rs b/codex-rs/core/tests/suite/truncation.rs index 59c722abc3..4fee8dc355 100644 --- a/codex-rs/core/tests/suite/truncation.rs +++ b/codex-rs/core/tests/suite/truncation.rs @@ -219,7 +219,7 @@ async fn tool_call_output_exceeds_limit_truncated_for_model() -> Result<()> { let server = start_mock_server().await; // Use a model that exposes the exec_command tool. - let mut builder = test_codex().with_model("gpt-5.4"); + let mut builder = test_codex().with_model("gpt-5.5"); let fixture = builder.build(&server).await?; let call_id = "shell-too-large"; @@ -302,7 +302,7 @@ async fn tool_call_output_truncated_only_once() -> Result<()> { let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4"); + let mut builder = test_codex().with_model("gpt-5.5"); let fixture = builder.build(&server).await?; let call_id = "shell-single-truncation"; let command = if cfg!(windows) { @@ -593,7 +593,7 @@ async fn token_policy_marker_reports_tokens() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| { + let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| { config.tool_output_token_limit = Some(50); // small budget to force truncation }); let fixture = builder.build(&server).await?; @@ -699,7 +699,7 @@ async fn exec_command_output_not_truncated_with_custom_limit() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| { + let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| { config.tool_output_token_limit = Some(50_000); // ample budget }); let fixture = builder.build(&server).await?; diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index b17be39003..3d95f61190 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -1972,7 +1972,7 @@ async fn exec_command_clamps_model_requested_max_output_tokens_to_policy() -> Re let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| { + let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| { config.tool_output_token_limit = Some(50); }); let test = builder.build_with_auto_env(&server).await?; @@ -2031,7 +2031,7 @@ async fn write_stdin_clamps_model_requested_max_output_tokens_to_policy() -> Res let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| { + let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| { config.tool_output_token_limit = Some(50); }); let test = builder.build_with_auto_env(&server).await?; diff --git a/codex-rs/core/tests/suite/view_image.rs b/codex-rs/core/tests/suite/view_image.rs index 62479ce0e6..ea6c401141 100644 --- a/codex-rs/core/tests/suite/view_image.rs +++ b/codex-rs/core/tests/suite/view_image.rs @@ -839,12 +839,12 @@ async fn view_image_routes_to_selected_remote_environment() -> anyhow::Result<() } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn view_image_tool_can_preserve_original_resolution_when_requested_on_gpt5_4() +async fn view_image_tool_can_preserve_original_resolution_when_requested_on_gpt5_5() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4"); + let mut builder = test_codex().with_model("gpt-5.5"); let test = builder.build_with_auto_env(&server).await?; let TestCodex { codex, @@ -935,7 +935,7 @@ async fn view_image_unified_budget_hides_detail_but_accepts_legacy_hints() -> an skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| { + let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| { let _ = config.features.enable(Feature::UnifiedImageBudget); }); let test = builder.build_with_auto_env(&server).await?; @@ -1011,7 +1011,7 @@ async fn view_image_tool_errors_clearly_for_unsupported_detail_values() -> anyho skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4"); + let mut builder = test_codex().with_model("gpt-5.5"); let test = builder.build_with_auto_env(&server).await?; let TestCodex { codex, @@ -1089,7 +1089,7 @@ async fn view_image_tool_treats_null_detail_as_omitted() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4"); + let mut builder = test_codex().with_model("gpt-5.5"); let test = builder.build_with_auto_env(&server).await?; let TestCodex { codex, @@ -1292,7 +1292,7 @@ async fn view_image_tool_does_not_force_original_resolution_with_capability_only skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let mut builder = test_codex().with_model("gpt-5.4"); + let mut builder = test_codex().with_model("gpt-5.5"); let test = builder.build_with_auto_env(&server).await?; let TestCodex { codex, diff --git a/codex-rs/core/tests/suite/web_search.rs b/codex-rs/core/tests/suite/web_search.rs index d21417bae0..8f5f5af143 100644 --- a/codex-rs/core/tests/suite/web_search.rs +++ b/codex-rs/core/tests/suite/web_search.rs @@ -4,7 +4,7 @@ use codex_config::test_support::CloudConfigBundleFixture; use codex_features::Feature; use codex_login::CodexAuth; use codex_login::auth::BedrockApiKeyAuth; -use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_4_MODEL_ID; +use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_5_MODEL_ID; use codex_model_provider_info::AMAZON_BEDROCK_PROVIDER_ID; use codex_model_provider_info::AMAZON_BEDROCK_RUNTIME_PROVIDER_ID; use codex_model_provider_info::ModelProviderInfo; @@ -106,8 +106,8 @@ async fn amazon_bedrock_web_search_uses_text_only_hosted_tools() { }); let mut builder = test_codex().with_auth(auth); builder = match model_catalog { - ModelCatalog::BuiltIn => builder.with_model(AMAZON_BEDROCK_GPT_5_4_MODEL_ID), - ModelCatalog::Configured => builder.with_model_info_override("gpt-5.4", |model_info| { + ModelCatalog::BuiltIn => builder.with_model(AMAZON_BEDROCK_GPT_5_5_MODEL_ID), + ModelCatalog::Configured => builder.with_model_info_override("gpt-5.5", |model_info| { model_info.web_search_tool_type = WebSearchToolType::TextAndImage; }), }; @@ -232,7 +232,7 @@ async fn amazon_bedrock_web_search_is_disabled_when_managed_requirements_prohibi }); let mut builder = test_codex() .with_auth(auth) - .with_model(AMAZON_BEDROCK_GPT_5_4_MODEL_ID) + .with_model(AMAZON_BEDROCK_GPT_5_5_MODEL_ID) .with_cloud_config_bundle( CloudConfigBundleFixture::loader_with_enterprise_requirement(format!( r#"allowed_web_search_modes = ["{allowed_mode}"]"# diff --git a/codex-rs/model-provider-info/src/lib.rs b/codex-rs/model-provider-info/src/lib.rs index 2e35b4b4c3..eef9b2129c 100644 --- a/codex-rs/model-provider-info/src/lib.rs +++ b/codex-rs/model-provider-info/src/lib.rs @@ -80,7 +80,6 @@ pub const AMAZON_BEDROCK_PROVIDER_ID: &str = "amazon-bedrock"; const AMAZON_BEDROCK_RUNTIME_PROVIDER_NAME: &str = "Amazon Bedrock Runtime"; pub const AMAZON_BEDROCK_RUNTIME_PROVIDER_ID: &str = "amazon-bedrock-runtime"; pub const AMAZON_BEDROCK_GPT_5_5_MODEL_ID: &str = "openai.gpt-5.5"; -pub const AMAZON_BEDROCK_GPT_5_4_MODEL_ID: &str = "openai.gpt-5.4"; pub const AMAZON_BEDROCK_GPT_5_6_SOL_MODEL_ID: &str = "openai.gpt-5.6-sol"; pub const AMAZON_BEDROCK_GPT_6_SOL_MODEL_ID: &str = "openai.gpt-6-sol"; pub const AMAZON_BEDROCK_GPT_6_LUNA_MODEL_ID: &str = "openai.gpt-6-luna"; diff --git a/codex-rs/model-provider/src/amazon_bedrock/catalog.rs b/codex-rs/model-provider/src/amazon_bedrock/catalog.rs index f7727363fc..003bbbe761 100644 --- a/codex-rs/model-provider/src/amazon_bedrock/catalog.rs +++ b/codex-rs/model-provider/src/amazon_bedrock/catalog.rs @@ -1,4 +1,3 @@ -use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_4_MODEL_ID; use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_5_MODEL_ID; use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_6_LUNA_MODEL_ID; use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_6_SOL_MODEL_ID; @@ -22,7 +21,6 @@ const GPT_6_SOL_OPENAI_MODEL_ID: &str = "gpt-6-sol"; const GPT_6_LUNA_OPENAI_MODEL_ID: &str = "gpt-6-luna"; const GPT_6_ASTRA_OPENAI_MODEL_ID: &str = "gpt-6-astra"; const GPT_5_5_OPENAI_MODEL_ID: &str = "gpt-5.5"; -const GPT_5_4_OPENAI_MODEL_ID: &str = "gpt-5.4"; pub(crate) fn static_model_catalog() -> ModelsResponse { normalize_bedrock_catalog(ModelsResponse { @@ -69,12 +67,6 @@ pub(crate) fn static_model_catalog() -> ModelsResponse { "GPT-5.5", /*priority*/ 6, ), - gpt_5_bedrock_model( - GPT_5_4_OPENAI_MODEL_ID, - AMAZON_BEDROCK_GPT_5_4_MODEL_ID, - "GPT-5.4", - /*priority*/ 7, - ), ], }) } @@ -84,9 +76,7 @@ pub(super) fn static_gov_model_catalog() -> ModelsResponse { catalog.models.retain(|model| { matches!( model.slug.as_str(), - AMAZON_BEDROCK_GPT_5_6_TERRA_MODEL_ID - | AMAZON_BEDROCK_GPT_5_6_LUNA_MODEL_ID - | AMAZON_BEDROCK_GPT_5_4_MODEL_ID + AMAZON_BEDROCK_GPT_5_6_TERRA_MODEL_ID | AMAZON_BEDROCK_GPT_5_6_LUNA_MODEL_ID ) }); catalog @@ -178,7 +168,6 @@ mod tests { AMAZON_BEDROCK_GPT_5_6_TERRA_MODEL_ID, AMAZON_BEDROCK_GPT_5_6_LUNA_MODEL_ID, AMAZON_BEDROCK_GPT_5_5_MODEL_ID, - AMAZON_BEDROCK_GPT_5_4_MODEL_ID, ] ); } @@ -241,19 +230,13 @@ mod tests { Some(GPT_5_BEDROCK_CONTEXT_WINDOW), WebSearchToolType::Text, ), - ( - AMAZON_BEDROCK_GPT_5_4_MODEL_ID, - Some(GPT_5_BEDROCK_CONTEXT_WINDOW), - Some(GPT_5_BEDROCK_CONTEXT_WINDOW), - WebSearchToolType::Text, - ), ] ); } #[test] fn configured_bedrock_catalogs_normalize_unsupported_model_capabilities() { - let model = bundled_openai_model(GPT_5_4_OPENAI_MODEL_ID); + let model = bundled_openai_model(GPT_5_5_OPENAI_MODEL_ID); let mut expected = model.clone(); expected.additional_speed_tiers.clear(); expected.service_tiers.clear(); diff --git a/codex-rs/model-provider/src/provider.rs b/codex-rs/model-provider/src/provider.rs index acb7de1a75..94bd3a835b 100644 --- a/codex-rs/model-provider/src/provider.rs +++ b/codex-rs/model-provider/src/provider.rs @@ -1302,7 +1302,6 @@ printf '%s\n' '{"AccessKeyId":"exported","SecretAccessKey":"secret"}' ("openai.gpt-5.6-terra", "GPT-5.6 Terra"), ("openai.gpt-5.6-luna", "GPT-5.6 Luna"), ("openai.gpt-5.5", "GPT-5.5"), - ("openai.gpt-5.4", "GPT-5.4"), ] ); @@ -1325,7 +1324,6 @@ printf '%s\n' '{"AccessKeyId":"exported","SecretAccessKey":"secret"}' "openai.gpt-5.6-terra", "openai.gpt-5.6-luna", "openai.gpt-5.5", - "openai.gpt-5.4", ] ); diff --git a/codex-rs/models-manager/models.json b/codex-rs/models-manager/models.json index 9bdae36ad0..29942914e8 100644 --- a/codex-rs/models-manager/models.json +++ b/codex-rs/models-manager/models.json @@ -1294,118 +1294,6 @@ "supports_reasoning_summary_parameter": true, "supports_reasoning_summaries": true }, - { - "slug": "gpt-5.4", - "prefer_websockets": true, - "support_verbosity": true, - "default_verbosity": "low", - "apply_patch_tool_type": "freeform", - "web_search_tool_type": "text_and_image", - "input_modalities": [ - "text", - "image" - ], - "supports_image_detail_original": true, - "truncation_policy": { - "mode": "tokens", - "limit": 10000 - }, - "supports_parallel_tool_calls": true, - "tool_mode": null, - "multi_agent_version": null, - "use_responses_lite": false, - "include_skills_usage_instructions": true, - "include_apps_usage_instructions": true, - "include_plugin_usage_instructions": true, - "node_repl_auto_review_required": false, - "node_repl_disabled": false, - "auto_review_model_override": null, - "model_specialty": null, - "context_window": 272000, - "max_context_window": 1000000, - "auto_compact_token_limit": null, - "comp_hash": "2911", - "default_reasoning_summary": "none", - "display_name": "GPT-5.4", - "description": "Strong model for everyday coding.", - "default_reasoning_level": "medium", - "supported_reasoning_levels": [ - { - "effort": "low", - "description": "Fast responses with lighter reasoning" - }, - { - "effort": "medium", - "description": "Balances speed and reasoning depth for everyday tasks" - }, - { - "effort": "high", - "description": "Greater reasoning depth for complex problems" - }, - { - "effort": "xhigh", - "description": "Extra high reasoning depth for complex problems" - } - ], - "shell_type": "unified_exec", - "visibility": "hide", - "minimal_client_version": "0.98.0", - "supported_in_api": true, - "availability_nux": null, - "upgrade": { - "model": "gpt-6-sol", - "migration_markdown": "GPT-5.4 is no longer available\n\nCodex now uses GPT-6 Sol in place of GPT-5.4. Switch to GPT-6 Sol to continue.\n", - "retirement_at": "2026-08-31T19:00:00Z" - }, - "priority": 16, - "model_messages": { - "instructions_template": "You are Codex, a coding agent based on GPT-5. You and the user share the same workspace and collaborate to achieve the user's goals.\n\n# Personality\n\nYou optimize for team morale and being a supportive teammate as much as code quality. You are consistent, reliable, and kind. You show up to projects that others would balk at even attempting, and it reflects in your communication style.\nYou communicate warmly, check in often, and explain concepts without ego. You excel at pairing, onboarding, and unblocking others. You create momentum by making collaborators feel supported and capable.\n\n## Values\nYou are guided by these core values:\n* Empathy: Interprets empathy as meeting people where they are - adjusting explanations, pacing, and tone to maximize understanding and confidence.\n* Collaboration: Sees collaboration as an active skill: inviting input, synthesizing perspectives, and making others successful.\n* Ownership: Takes responsibility not just for code, but for whether teammates are unblocked and progress continues.\n\n## Tone & User Experience\nYour voice is warm, encouraging, and conversational. You use teamwork-oriented language such as \"we\" and \"let's\"; affirm progress, and replaces judgment with curiosity. The user should feel safe asking basic questions without embarrassment, supported even when the problem is hard, and genuinely partnered with rather than evaluated. Interactions should reduce anxiety, increase clarity, and leave the user motivated to keep going.\n\nYou are a patient and enjoyable collaborator: unflappable when others might get frustrated, while being an enjoyable, easy-going personality to work with. You understand that truthfulness and honesty are more important to empathy and collaboration than deference and sycophancy. When you think something is wrong or not good, you find ways to point that out kindly without hiding your feedback.\n\nYou never make the user work for you. You can ask clarifying questions only when they are substantial. Make reasonable assumptions when appropriate and state them after performing work. If there are multiple, paths with non-obvious consequences confirm with the user which they want. Avoid open-ended questions, and prefer a list of options when possible.\n\n## Escalation\nYou escalate gently and deliberately when decisions have non-obvious consequences or hidden risk. Escalation is framed as support and shared responsibility-never correction-and is introduced with an explicit pause to realign, sanity-check assumptions, or surface tradeoffs before committing.\n\n# General\nAs an expert coding agent, your primary focus is writing code, answering questions, and helping the user complete their task in the current environment. You build context by examining the codebase first without making assumptions or jumping to conclusions. You think through the nuances of the code you encounter, and embody the mentality of a skilled senior software engineer.\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Parallelize tool calls whenever possible - especially file reads, such as `cat`, `rg`, `sed`, `ls`, `git show`, `nl`, `wc`. Use `multi_tool_use.parallel` to parallelize tool calls and only this. Never chain together bash commands with separators like `echo \"====\";` as this renders to the user poorly.\n\n## Editing constraints\n\n- Default to ASCII when editing or creating files. Only introduce non-ASCII or other Unicode characters when there is a clear justification and the file already uses them.\n- Add succinct code comments that explain what is going on if code is not self-explanatory. You should not add comments like \"Assigns the value to the variable\", but a brief comment might be useful ahead of a complex code block that the user would otherwise have to spend time parsing out. Usage of these comments should be rare.\n- Always use apply_patch for manual code edits. Do not use cat or any other commands when creating or editing files. Formatting commands or bulk edits don't need to be done with apply_patch.\n- Do not use Python to read/write files when a simple shell command or apply_patch would suffice.\n- You may be in a dirty git worktree.\n * NEVER revert existing changes you did not make unless explicitly requested, since these changes were made by the user.\n * If asked to make a commit or code edits and there are unrelated changes to your work or changes that you didn't make in those files, don't revert those changes.\n * If the changes are in files you've touched recently, you should read carefully and understand how you can work with the changes rather than reverting them.\n * If the changes are in unrelated files, just ignore them and don't revert them.\n- Do not amend a commit unless explicitly requested to do so.\n- While you are working, you might notice unexpected changes that you didn't make. It's likely the user made them, or were autogenerated. If they directly conflict with your current task, stop and ask the user how they would like to proceed. Otherwise, focus on the task at hand.\n- **NEVER** use destructive commands like `git reset --hard` or `git checkout --` unless specifically requested or approved by the user.\n- You struggle using the git interactive console. **ALWAYS** prefer using non-interactive git commands.\n\n## Special user requests\n\n- If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as `date`), you should do so.\n- If the user asks for a \"review\", default to a code review mindset: prioritise identifying bugs, risks, behavioural regressions, and missing tests. Findings must be the primary focus of the response - keep summaries or overviews brief and only after enumerating the issues. Present findings first (ordered by severity with file/line references), follow with open questions or assumptions, and offer a change-summary only as a secondary detail. If no findings are discovered, state that explicitly and mention any residual risks or testing gaps.\n\n## Autonomy and persistence\nPersist until the task is fully handled end-to-end within the current turn whenever feasible: do not stop at analysis or partial fixes; carry changes through implementation, verification, and a clear explanation of outcomes unless the user explicitly pauses or redirects you.\n\nUnless the user explicitly asks for a plan, asks a question about the code, is brainstorming potential solutions, or some other intent that makes it clear that code should not be written, assume the user wants you to make code changes or run tools to solve the user's problem. In these cases, it's bad to output your proposed solution in a message, you should go ahead and actually implement the change. If you encounter challenges or blockers, you should attempt to resolve them yourself.\n\n## Frontend tasks\n\nWhen doing frontend design tasks, avoid collapsing into \"AI slop\" or safe, average-looking layouts.\nAim for interfaces that feel intentional, bold, and a bit surprising.\n- Typography: Use expressive, purposeful fonts and avoid default stacks (Inter, Roboto, Arial, system).\n- Color & Look: Choose a clear visual direction; define CSS variables; avoid purple-on-white defaults. No purple bias or dark mode bias.\n- Motion: Use a few meaningful animations (page-load, staggered reveals) instead of generic micro-motions.\n- Background: Don't rely on flat, single-color backgrounds; use gradients, shapes, or subtle patterns to build atmosphere.\n- Ensure the page loads properly on both desktop and mobile\n- For React code, prefer modern patterns including useEffectEvent, startTransition, and useDeferredValue when appropriate if used by the team. Do not add useMemo/useCallback by default unless already used; follow the repo's React Compiler guidance.\n- Overall: Avoid boilerplate layouts and interchangeable UI patterns. Vary themes, type families, and visual languages across outputs.\n\nException: If working within an existing website or design system, preserve the established patterns, structure, and visual language.\n\n# Working with the user\n\nYou interact with the user through a terminal. You have 2 ways of communicating with the users:\n- Share intermediary updates in `commentary` channel. \n- After you have completed all your work, send a message to the `final` channel.\nYou are producing plain text that will later be styled by the program you run in. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value. Follow the formatting rules exactly.\n\n## Formatting rules\n\n- You may format with GitHub-flavored Markdown.\n- Structure your answer if necessary, the complexity of the answer should match the task. If the task is simple, your answer should be a one-liner. Order sections from general to specific to supporting.\n- Never use nested bullets. Keep lists flat (single level). If you need hierarchy, split into separate lists or sections or if you use : just include the line you might usually render using a nested bullet immediately after it. For numbered lists, only use the `1. 2. 3.` style markers (with a period), never `1)`.\n- Headers are optional, only use them when you think they are necessary. If you do use them, use short Title Case (1-3 words) wrapped in **…**. Don't add a blank line.\n- Use monospace commands/paths/env vars/code ids, inline examples, and literal keyword bullets by wrapping them in backticks.\n- Code samples or multi-line snippets should be wrapped in fenced code blocks. Include an info string as often as possible.\n- When referencing a real local file, prefer a clickable markdown link.\n * Clickable file links should look like [app.py](/abs/path/app.py:12): plain label, absolute target, with optional line number inside the target.\n * If a file path has spaces, wrap the target in angle brackets: [My Report.md]().\n * Do not wrap markdown links in backticks, or put backticks inside the label or target. This confuses the markdown renderer.\n * Do not use URIs like file://, vscode://, or https:// for file links.\n * Do not provide ranges of lines.\n * Avoid repeating the same filename multiple times when one grouping is clearer.\n- Don’t use emojis or em dashes unless explicitly instructed.\n\n## Final answer instructions\n\nAlways favor conciseness in your final answer - you should usually avoid long-winded explanations and focus only on the most important details. For casual chit-chat, just chat. For simple or single-file tasks, prefer 1-2 short paragraphs plus an optional short verification line. Do not default to bullets. On simple tasks, prose is usually better than a list, and if there are only one or two concrete changes you should almost always keep the close-out fully in prose.\n\nOn larger tasks, use at most 2-3 high-level sections when helpful. Each section can be a short paragraph or a few flat bullets. Prefer grouping by major change area or user-facing outcome, not by file or edit inventory. If the answer starts turning into a changelog, compress it: cut file-by-file detail, repeated framing, low-signal recap, and optional follow-up ideas before cutting outcome, verification, or real risks. Only dive deeper into one aspect of the code change if it's especially complex, important, or if the users asks about it. This also holds true for PR explanations, codebase walkthroughs, or architectural decisions: provide a high-level walkthrough unless specifically asked and cap answers at 2-3 sections.\n\nRequirements for your final answer:\n- Prefer short paragraphs by default.\n- When explaining something, optimize for fast, high-level comprehension rather than completeness-by-default.\n- Use lists only when the content is inherently list-shaped: enumerating distinct items, steps, options, categories, comparisons, ideas. Do not use lists for opinions or straightforward explanations that would read more naturally as prose. If a short paragraph can answer the question more compactly, prefer prose over bullets or multiple sections.\n- Do not turn simple explanations into outlines or taxonomies unless the user asks for depth. If a list is used, each bullet should be a complete standalone point.\n- Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements (“Done —”, “Got it”, “Great question, ”, \"You're right to call that out\") or framing phrases.\n- The user does not see command execution outputs. When asked to show the output of a command (e.g. `git show`), relay the important details in your answer or summarize the key lines so the user understands the result.\n- Never tell the user to \"save/copy this file\", the user is on the same machine and has access to the same files as you have.\n- If the user asks for a code explanation, include code references as appropriate.\n- If you weren't able to do something, for example run tests, tell the user.\n- Never use nested bullets. Keep lists flat (single level). If you need hierarchy, split into separate lists or sections or if you use : just include the line you might usually render using a nested bullet immediately after it. For numbered lists, only use the `1. 2. 3.` style markers (with a period), never `1)`.\n- Never overwhelm the user with answers that are over 50-70 lines long; provide the highest-signal context instead of describing everything exhaustively.\n\n## Intermediary updates \n\n- Intermediary updates go to the `commentary` channel.\n- User updates are short updates while you are working, they are NOT final answers.\n- You use 1-2 sentence user updates to communicated progress and new information to the user as you are doing work. \n- Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements (“Done —”, “Got it”, “Great question, ”) or framing phrases.\n- Before exploring or doing substantial work, you start with a user update acknowledging the request and explaining your first step. You should include your understanding of the user request and explain what you will do. Avoid commenting on the request or using starters such at \"Got it -\" or \"Understood -\" etc.\n- You provide user updates frequently, every 30s.\n- When exploring, e.g. searching, reading files you provide user updates as you go, explaining what context you are gathering and what you've learned. Vary your sentence structure when providing these updates to avoid sounding repetitive - in particular, don't start each sentence the same way.\n- When working for a while, keep updates informative and varied, but stay concise.\n- After you have sufficient context, and the work is substantial you provide a longer plan (this is the only user update that may be longer than 2 sentences and can contain formatting).\n- Before performing file edits of any kind, you provide updates explaining what edits you are making.\n- As you are thinking, you very frequently provide updates even if not taking any actions, informing the user of your progress. You interrupt your thinking and send multiple updates in a row if thinking for more than 100 words.\n- Tone of your updates MUST match your personality.\n", - "instructions_variables": null, - "approvals": null, - "collaboration_modes": null, - "auto_review": null, - "multi_agent": null, - "permissions": null, - "token_budget": null, - "guardian_v2": null - }, - "experimental_supported_tools": [], - "available_in_plans": [ - "business", - "edu", - "edu_plus", - "edu_pro", - "education", - "enterprise", - "enterprise_cbp_automation", - "enterprise_cbp_usage_based", - "finserv", - "go", - "hc", - "plus", - "pro", - "prolite", - "quorum", - "sci", - "self_serve_business_prolite", - "self_serve_business_usage_based", - "team" - ], - "supports_search_tool": true, - "default_service_tier": null, - "service_tiers": [ - { - "id": "priority", - "name": "Fast", - "description": "1.5x speed, increased usage" - } - ], - "additional_speed_tiers": [ - "fast" - ], - "supports_reasoning_summary_parameter": true, - "supports_reasoning_summaries": true - }, { "slug": "codex-auto-review", "prefer_websockets": true, diff --git a/codex-rs/tui/src/app/startup_prompts.rs b/codex-rs/tui/src/app/startup_prompts.rs index aa6abc56ac..08f8668c58 100644 --- a/codex-rs/tui/src/app/startup_prompts.rs +++ b/codex-rs/tui/src/app/startup_prompts.rs @@ -5,6 +5,8 @@ use super::*; use codex_config::ConfigLayerSource; +use codex_model_provider_info::AMAZON_BEDROCK_PROVIDER_ID; +use codex_model_provider_info::OPENAI_PROVIDER_ID; use std::collections::HashSet; use std::path::PathBuf; @@ -111,6 +113,7 @@ pub(super) fn emit_system_bwrap_warning(app_event_tx: &AppEventSender, config: & } pub(super) fn model_upgrade_for_migration( + model_provider_id: &str, model: &str, available_models: &[ModelPreset], ) -> Option { @@ -118,24 +121,37 @@ pub(super) fn model_upgrade_for_migration( return preset.upgrade.clone(); } - // Saved selections can outlive their catalog entries. Keep only their migration metadata. - let (target_model, current_name, target_name) = match model { - "gpt-5.4-mini" => ("gpt-6-luna", "GPT-5.4 Mini", "GPT-6 Luna"), + // Saved selections can outlive their catalog entries. Scope fallback migration metadata + // to the owning provider, since other providers may still support the same model slug. + let (target_model, current_name, target_name) = match (model_provider_id, model) { + (OPENAI_PROVIDER_ID, "gpt-5.4") => ("gpt-6-sol", "GPT-5.4", "GPT-6 Sol"), + (AMAZON_BEDROCK_PROVIDER_ID, "openai.gpt-5.4") => ( + "openai.gpt-6-sol", + "GPT-5.4 on Amazon Bedrock", + "GPT-6 Sol on Amazon Bedrock", + ), + (OPENAI_PROVIDER_ID, "gpt-5.4-mini") => ("gpt-6-luna", "GPT-5.4 Mini", "GPT-6 Luna"), _ => return None, }; + let availability = if model == "openai.gpt-5.4" { + "no longer offered in Codex" + } else { + "no longer available" + }; Some(ModelUpgrade { id: target_model.to_string(), migration_config_key: model.to_string(), model_link: None, upgrade_copy: None, migration_markdown: Some(format!( - "{current_name} is no longer available\n\nCodex now uses {target_name} in place of {current_name}. Switch to {target_name} to continue.\n" + "{current_name} is {availability}\n\nCodex now uses {target_name} in place of {current_name}. Switch to {target_name} to continue.\n" )), retirement_at: None, }) } pub(super) fn should_show_model_migration_prompt( + model_provider_id: &str, current_model: &str, target_model: &str, seen_migrations: &BTreeMap, @@ -158,7 +174,7 @@ pub(super) fn should_show_model_migration_prompt( return false; } - if model_upgrade_for_migration(current_model, available_models) + if model_upgrade_for_migration(model_provider_id, current_model, available_models) .is_some_and(|upgrade| upgrade.id == target_model) { return true; @@ -298,7 +314,7 @@ pub(super) async fn handle_model_migration_prompt_if_needed( app_event_tx: &AppEventSender, available_models: &[ModelPreset], ) -> std::io::Result> { - let upgrade = model_upgrade_for_migration(model, available_models); + let upgrade = model_upgrade_for_migration(&config.model_provider_id, model, available_models); if let Some(ModelUpgrade { id: target_model, @@ -315,6 +331,7 @@ pub(super) async fn handle_model_migration_prompt_if_needed( let target_model = target_model.to_string(); if !should_show_model_migration_prompt( + &config.model_provider_id, model, &target_model, &local_settings.notices.model_migrations, diff --git a/codex-rs/tui/src/app/tests/model_catalog.rs b/codex-rs/tui/src/app/tests/model_catalog.rs index 626aae8e22..f4f19c5352 100644 --- a/codex-rs/tui/src/app/tests/model_catalog.rs +++ b/codex-rs/tui/src/app/tests/model_catalog.rs @@ -1,6 +1,8 @@ use super::*; use assert_matches::assert_matches; use codex_config::types::ModelAvailabilityNuxConfig; +use codex_model_provider::create_model_provider; +use codex_model_provider_info::ModelProviderInfo; use codex_protocol::config_types::ServiceTier; use codex_protocol::openai_models::ModelAvailabilityNux; use pretty_assertions::assert_eq; @@ -10,6 +12,16 @@ fn all_model_presets() -> Vec { crate::test_support::TEST_MODEL_PRESETS.clone() } +fn bedrock_model_presets() -> Vec { + create_model_provider( + ModelProviderInfo::create_amazon_bedrock_provider(/*aws*/ None), + /*auth_manager*/ None, + ) + .models_manager_without_cache(/*config_model_catalog*/ None) + .try_list_models() + .expect("Bedrock model catalog") +} + #[tokio::test] async fn model_picker_refresh_updates_app_catalog_from_app_server() -> Result<()> { let (mut app, mut rx, _op_rx) = make_test_app_with_channels().await; @@ -175,40 +187,43 @@ async fn model_migration_prompt_only_shows_for_deprecated_models() { let seen = BTreeMap::new(); let presets = model_presets_with_test_upgrades(); assert!(should_show_model_migration_prompt( - "gpt-5.2", "gpt-5.5", &seen, &presets + "openai", "gpt-5.2", "gpt-5.5", &seen, &presets )); assert!(should_show_model_migration_prompt( + "openai", "gpt-5.4", "gpt-6-sol", &seen, &presets )); assert!(!should_show_model_migration_prompt( - "gpt-5.4", "gpt-5.4", &seen, &presets + "openai", "gpt-5.4", "gpt-5.4", &seen, &presets )); } #[test] fn retired_model_migration_respects_catalog_metadata() { - let mut presets = model_presets_with_test_upgrades(); - let current = presets - .iter_mut() - .find(|preset| preset.model == "gpt-5.2") - .expect("test preset"); - current.id = "gpt-5.4-mini".to_string(); - current.model = "gpt-5.4-mini".to_string(); - let expected = current.upgrade.clone(); - assert_eq!( - model_upgrade_for_migration("gpt-5.4-mini", &presets), - expected - ); + for model in ["gpt-5.4-mini", "gpt-5.4", "openai.gpt-5.4"] { + let mut presets = model_presets_with_test_upgrades(); + let current = presets + .iter_mut() + .find(|preset| preset.model == "gpt-5.2") + .expect("test preset"); + current.id = model.to_string(); + current.model = model.to_string(); + let expected = current.upgrade.clone(); + assert_eq!( + model_upgrade_for_migration("openai", model, &presets), + expected + ); - presets - .iter_mut() - .find(|preset| preset.model == "gpt-5.4-mini") - .expect("catalog preset") - .upgrade = None; - assert_eq!(model_upgrade_for_migration("gpt-5.4-mini", &presets), None); + presets + .iter_mut() + .find(|preset| preset.model == model) + .expect("catalog preset") + .upgrade = None; + assert_eq!(model_upgrade_for_migration("openai", model, &presets), None); + } } #[test] @@ -366,30 +381,39 @@ async fn prepare_startup_tooltip_override_persists_model_availability_nux_count( #[tokio::test] async fn accepted_model_migration_persists_target_default_reasoning_effort() -> Result<()> { - let presets = all_model_presets(); let mut migration_copies = Vec::new(); - for (model, replacement) in [ - ("gpt-5.4", "gpt-6-sol"), - ("gpt-5.4-mini", "gpt-6-luna"), - ("gpt-5.5", "gpt-6-sol"), - ("gpt-5.6-luna", "gpt-6-luna"), - ("gpt-5.6-terra", "gpt-6-sol"), - ("gpt-5.6-sol", "gpt-6-sol"), + for (model, replacement, provider_id, presets) in [ + ("gpt-5.4", "gpt-6-sol", "openai", all_model_presets()), + ("gpt-5.4-mini", "gpt-6-luna", "openai", all_model_presets()), + ("gpt-5.5", "gpt-6-sol", "openai", all_model_presets()), + ("gpt-5.6-luna", "gpt-6-luna", "openai", all_model_presets()), + ("gpt-5.6-terra", "gpt-6-sol", "openai", all_model_presets()), + ("gpt-5.6-sol", "gpt-6-sol", "openai", all_model_presets()), + ( + "openai.gpt-5.4", + "openai.gpt-6-sol", + "amazon-bedrock", + bedrock_model_presets(), + ), ] { let codex_home = tempdir()?; std::fs::write( codex_home.path().join("config.toml"), - format!("model = \"{model}\"\nmodel_reasoning_effort = \"xhigh\"\n"), + format!( + "model = \"{model}\"\nmodel_reasoning_effort = \"xhigh\"\nmodel_provider = \"{provider_id}\"\n" + ), )?; let mut config = ConfigBuilder::default() .codex_home(codex_home.path().to_path_buf()) .build() .await?; let current_model = config.model.clone().expect("saved model"); - let upgrade = model_upgrade_for_migration(¤t_model, &presets) + let provider = config.model_provider.clone(); + let upgrade = model_upgrade_for_migration(provider_id, ¤t_model, &presets) .expect("catalog selection should have an upgrade"); assert_eq!(upgrade.id, replacement); assert!(should_show_model_migration_prompt( + provider_id, ¤t_model, &upgrade.id, &BTreeMap::new(), @@ -421,8 +445,20 @@ async fn accepted_model_migration_persists_target_default_reasoning_effort() -> target_effort.clone(), ); - assert_eq!(config.model.as_deref(), Some(replacement)); - assert_eq!(config.model_reasoning_effort, Some(target_effort.clone())); + assert_eq!( + ( + config.model.as_deref(), + config.model_reasoning_effort.as_ref(), + config.model_provider_id.as_str(), + &config.model_provider, + ), + ( + Some(replacement), + Some(&target_effort), + provider_id, + &provider + ) + ); let acknowledged = rx.try_recv().expect("acknowledged event"); assert_matches!( @@ -474,65 +510,103 @@ async fn accepted_model_migration_persists_target_default_reasoning_effort() -> Meet GPT-6 Sol Our latest Sol is more intelligent and more efficient so your usage limits go further. This model is a great daily driver for complex tasks, especially coding. + + GPT-5.4 on Amazon Bedrock is no longer offered in Codex + + Codex now uses GPT-6 Sol on Amazon Bedrock in place of GPT-5.4 on Amazon Bedrock. Switch to GPT-6 Sol on Amazon Bedrock to continue. "); Ok(()) } #[tokio::test] async fn model_migration_prompt_respects_hide_flag_and_self_target() { - let presets = all_model_presets(); - let mut seen = BTreeMap::new(); - // A previously acknowledged intermediate upgrade must not hide the new target. - seen.insert("gpt-5.4-mini".to_string(), "gpt-5.6-luna".to_string()); - assert!(should_show_model_migration_prompt( - "gpt-5.4-mini", - "gpt-6-luna", - &seen, - &presets - )); - seen.insert("gpt-5.4-mini".to_string(), "gpt-6-luna".to_string()); - assert!(!should_show_model_migration_prompt( - "gpt-5.4-mini", - "gpt-6-luna", - &seen, - &presets - )); - assert!(!should_show_model_migration_prompt( - "gpt-6-luna", - "gpt-6-luna", - &seen, - &presets - )); + for (model, previous_target, target, presets) in [ + ( + "gpt-5.4-mini", + "gpt-5.6-luna", + "gpt-6-luna", + all_model_presets(), + ), + ("gpt-5.4", "gpt-5.6-terra", "gpt-6-sol", all_model_presets()), + ( + "openai.gpt-5.4", + "openai.gpt-5.6-terra", + "openai.gpt-6-sol", + bedrock_model_presets(), + ), + ] { + let provider_id = if model == "openai.gpt-5.4" { + "amazon-bedrock" + } else { + "openai" + }; + // A previously acknowledged intermediate upgrade must not hide the new target. + let mut seen = BTreeMap::from([(model.to_string(), previous_target.to_string())]); + assert!(should_show_model_migration_prompt( + provider_id, + model, + target, + &seen, + &presets + )); + seen.insert(model.to_string(), target.to_string()); + assert!(!should_show_model_migration_prompt( + provider_id, + model, + target, + &seen, + &presets + )); + assert!(!should_show_model_migration_prompt( + provider_id, + target, + target, + &seen, + &presets + )); + } } #[tokio::test] async fn model_migration_prompt_skips_when_target_missing_or_hidden() { - let mut available = all_model_presets(); - available.retain(|preset| preset.model != "gpt-6-luna"); + for (model, target_model, mut available) in [ + ("gpt-5.4-mini", "gpt-6-luna", all_model_presets()), + ("gpt-5.4", "gpt-6-sol", all_model_presets()), + ( + "openai.gpt-5.4", + "openai.gpt-6-sol", + bedrock_model_presets(), + ), + ] { + let provider_id = if model == "openai.gpt-5.4" { + "amazon-bedrock" + } else { + "openai" + }; + let target = available + .iter_mut() + .find(|preset| preset.model == target_model) + .expect("target preset present"); + target.show_in_picker = false; + assert!(!should_show_model_migration_prompt( + provider_id, + model, + target_model, + &BTreeMap::new(), + &available, + )); + assert!(target_preset_for_upgrade(&available, target_model).is_none()); - assert!(!should_show_model_migration_prompt( - "gpt-5.4-mini", - "gpt-6-luna", - &BTreeMap::new(), - &available, - )); - - assert!(target_preset_for_upgrade(&available, "gpt-6-luna").is_none()); - - let mut with_hidden_target = all_model_presets(); - let target = with_hidden_target - .iter_mut() - .find(|preset| preset.model == "gpt-6-luna") - .expect("target preset present"); - target.show_in_picker = false; - - assert!(!should_show_model_migration_prompt( - "gpt-5.4-mini", - "gpt-6-luna", - &BTreeMap::new(), - &with_hidden_target, - )); - assert!(target_preset_for_upgrade(&with_hidden_target, "gpt-6-luna").is_none()); + available.retain(|preset| preset.model != target_model); + assert!(!should_show_model_migration_prompt( + provider_id, + model, + target_model, + &BTreeMap::new(), + &available, + )); + assert!(target_preset_for_upgrade(&available, target_model).is_none()); + } } #[tokio::test] @@ -564,6 +638,7 @@ async fn model_migration_prompt_shows_for_hidden_model() { .show_in_picker = true; assert!( should_show_model_migration_prompt( + "openai", ¤t.model, &upgrade.id, &config.notices.model_migrations, diff --git a/codex-rs/tui/src/app/tests/startup_defaults_tests.rs b/codex-rs/tui/src/app/tests/startup_defaults_tests.rs index 4c789caf5e..5e2409e78e 100644 --- a/codex-rs/tui/src/app/tests/startup_defaults_tests.rs +++ b/codex-rs/tui/src/app/tests/startup_defaults_tests.rs @@ -90,6 +90,7 @@ async fn cli_fork_omits_implicit_model_and_effort() -> Result<()> { }); assert!( crate::app::startup_prompts::should_show_model_migration_prompt( + "openai", "gpt-5.5", "server-model", &Default::default(), diff --git a/codex-rs/tui/src/app_server_session.rs b/codex-rs/tui/src/app_server_session.rs index 13b4ed0a11..3bfea5b4c4 100644 --- a/codex-rs/tui/src/app_server_session.rs +++ b/codex-rs/tui/src/app_server_session.rs @@ -3989,7 +3989,7 @@ mod tests { duration_ms: None, }], }, - model: "gpt-5.4".to_string(), + model: "gpt-5.5".to_string(), model_provider: "openai".to_string(), service_tier: None, cwd: test_path_buf("/tmp/project").abs(), @@ -4011,7 +4011,7 @@ mod tests { collaboration_mode: Some(codex_protocol::config_types::CollaborationMode { mode: codex_protocol::config_types::ModeKind::Plan, settings: codex_protocol::config_types::Settings { - model: "gpt-5.4".to_string(), + model: "gpt-5.5".to_string(), reasoning_effort: None, developer_instructions: Some("Keep planning".to_string()), }, diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_govcloud_models.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_govcloud_models.snap index 49ac7903d2..f9f6fdfcbe 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_govcloud_models.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_govcloud_models.snap @@ -7,6 +7,5 @@ expression: "render_bottom_popup(&chat, 100)" › 1. GPT-5.6 Terra (current) Older balanced model for straightforward work. 2. GPT-5.6 Luna Older fast and efficient model. - 3. GPT-5.4 Strong model for everyday coding. enter select · esc back diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_mantle_models.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_mantle_models.snap index 139fc6707d..be68bca08c 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_mantle_models.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_mantle_models.snap @@ -12,6 +12,5 @@ expression: "render_bottom_popup(&chat, 100)" 5. GPT-5.6 Terra Older balanced model for straightforward work. 6. GPT-5.6 Luna Older fast and efficient model. 7. GPT-5.5 Legacy coding model. - 8. GPT-5.4 Strong model for everyday coding. enter select · esc back diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_goal_active_token_budget_footer.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_goal_active_token_budget_footer.snap index ace4815899..d7fddce123 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_goal_active_token_budget_footer.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_goal_active_token_budget_footer.snap @@ -6,4 +6,4 @@ expression: normalized_backend_snapshot(terminal.backend()) " " "› Ask Codex to do anything " " " -" GPT-5.4 Pursuing goal (40K / 50K) " +" GPT-5.5 Pursuing goal (40K / 50K) " diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_goal_complete_elapsed_footer.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_goal_complete_elapsed_footer.snap index 63cda5d1b1..10e1d6409b 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_goal_complete_elapsed_footer.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_goal_complete_elapsed_footer.snap @@ -6,4 +6,4 @@ expression: normalized_backend_snapshot(terminal.backend()) " " "› Ask Codex to do anything " " " -" GPT-5.4 Goal achieved (2d 23h 42m) " +" GPT-5.5 Goal achieved (2d 23h 42m) " diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__terminal_title_model_display_name.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__terminal_title_model_display_name.snap index 808735b45d..4b9c59bdaa 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__terminal_title_model_display_name.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__terminal_title_model_display_name.snap @@ -2,4 +2,4 @@ source: tui/src/chatwidget/tests/status_and_layout.rs expression: "chat.last_terminal_title.as_deref().expect(\"terminal title\")" --- -GPT-5.4 +GPT-5.5 diff --git a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs index 5517e30070..c3aa1ba65d 100644 --- a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs +++ b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs @@ -4023,7 +4023,7 @@ async fn status_line_model_with_reasoning_includes_fast_for_fast_capable_models( #[tokio::test] async fn terminal_title_model_updates_on_model_change_without_manual_refresh() { - let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await; + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await; chat.local_settings.tui.terminal_title = Some(vec!["model".to_string()]); chat.refresh_terminal_title(); @@ -4228,7 +4228,7 @@ async fn status_line_goal_active_token_budget_footer_snapshot() { use ratatui::Terminal; use ratatui::backend::TestBackend; - let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await; + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await; chat.set_feature_enabled(Feature::Goals, /*enabled*/ true); chat.show_welcome_banner = false; chat.local_settings.tui.status_line = Some(vec!["model-name".to_string()]); @@ -4265,7 +4265,7 @@ async fn status_line_goal_complete_elapsed_footer_snapshot() { use ratatui::Terminal; use ratatui::backend::TestBackend; - let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await; + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await; chat.set_feature_enabled(Feature::Goals, /*enabled*/ true); chat.show_welcome_banner = false; chat.local_settings.tui.status_line = Some(vec!["model-name".to_string()]); diff --git a/codex-rs/tui/src/snapshots/codex_tui__app_server_session__tests__resumed_plan_mode.snap b/codex-rs/tui/src/snapshots/codex_tui__app_server_session__tests__resumed_plan_mode.snap index fa1f7e7374..cf58c4def2 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__app_server_session__tests__resumed_plan_mode.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__app_server_session__tests__resumed_plan_mode.snap @@ -4,4 +4,4 @@ expression: "crate::chatwidget::tests::helpers::render_bottom_popup(&chat, 40)" --- › Ask Codex to do anything - GPT-5.4 default · /tmp/pr… Plan mode + GPT-5.5 default · /tmp/pr… Plan mode