diff --git a/codex-rs/core/src/openai_models/model_presets.rs b/codex-rs/core/src/openai_models/model_presets.rs index a01875310..da0048ce4 100644 --- a/codex-rs/core/src/openai_models/model_presets.rs +++ b/codex-rs/core/src/openai_models/model_presets.rs @@ -65,12 +65,7 @@ static PRESETS: Lazy> = Lazy::new(|| { }, ], is_default: false, - upgrade: Some(ModelUpgrade { - id: "gpt-5.2-codex".to_string(), - reasoning_effort_mapping: None, - migration_config_key: "gpt-5.2-codex".to_string(), - model_link: Some("https://openai.com/index/introducing-gpt-5-2-codex".to_string()), - }), + upgrade: Some(gpt_52_codex_upgrade()), show_in_picker: true, supported_in_api: true, }, @@ -92,12 +87,7 @@ static PRESETS: Lazy> = Lazy::new(|| { }, ], is_default: false, - upgrade: Some(ModelUpgrade { - id: "gpt-5.2-codex".to_string(), - reasoning_effort_mapping: None, - migration_config_key: "gpt-5.2-codex".to_string(), - model_link: Some("https://openai.com/index/introducing-gpt-5-2-codex".to_string()), - }), + upgrade: Some(gpt_52_codex_upgrade()), show_in_picker: true, supported_in_api: true, }, @@ -126,12 +116,7 @@ static PRESETS: Lazy> = Lazy::new(|| { }, ], is_default: false, - upgrade: Some(ModelUpgrade { - id: "gpt-5.2-codex".to_string(), - reasoning_effort_mapping: None, - migration_config_key: "gpt-5.2-codex".to_string(), - model_link: Some("https://openai.com/index/introducing-gpt-5-2-codex".to_string()), - }), + upgrade: Some(gpt_52_codex_upgrade()), show_in_picker: true, supported_in_api: true, }, @@ -157,12 +142,7 @@ static PRESETS: Lazy> = Lazy::new(|| { }, ], is_default: false, - upgrade: Some(ModelUpgrade { - id: "gpt-5.2-codex".to_string(), - reasoning_effort_mapping: None, - migration_config_key: "gpt-5.2-codex".to_string(), - model_link: Some("https://openai.com/index/introducing-gpt-5-2-codex".to_string()), - }), + upgrade: Some(gpt_52_codex_upgrade()), show_in_picker: false, supported_in_api: true, }, @@ -183,12 +163,7 @@ static PRESETS: Lazy> = Lazy::new(|| { }, ], is_default: false, - upgrade: Some(ModelUpgrade { - id: "gpt-5.1-codex-mini".to_string(), - reasoning_effort_mapping: None, - migration_config_key: HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG.to_string(), - model_link: Some("https://www.codex.com/models/caribou".to_string()), - }), + upgrade: Some(gpt_52_codex_upgrade()), show_in_picker: false, supported_in_api: true, }, @@ -214,12 +189,7 @@ static PRESETS: Lazy> = Lazy::new(|| { }, ], is_default: false, - upgrade: Some(ModelUpgrade { - id: "caribou".to_string(), - reasoning_effort_mapping: None, - migration_config_key: "gpt-5.2-codex".to_string(), - model_link: Some("https://openai.com/index/introducing-gpt-5-2-codex".to_string()), - }), + upgrade: Some(gpt_52_codex_upgrade()), show_in_picker: false, supported_in_api: true, }, @@ -248,12 +218,7 @@ static PRESETS: Lazy> = Lazy::new(|| { }, ], is_default: false, - upgrade: Some(ModelUpgrade { - id: "gpt-5.2-codex".to_string(), - reasoning_effort_mapping: None, - migration_config_key: "gpt-5.2-codex".to_string(), - model_link: Some("https://openai.com/index/introducing-gpt-5-2-codex".to_string()), - }), + upgrade: Some(gpt_52_codex_upgrade()), show_in_picker: false, supported_in_api: true, }, @@ -278,18 +243,26 @@ static PRESETS: Lazy> = Lazy::new(|| { }, ], is_default: false, - upgrade: Some(ModelUpgrade { - id: "caribou".to_string(), - reasoning_effort_mapping: None, - migration_config_key: "caribou".to_string(), - model_link: Some("https://www.codex.com/models/caribou".to_string()), - }), + upgrade: Some(gpt_52_codex_upgrade()), show_in_picker: false, supported_in_api: true, }, ] }); +fn gpt_52_codex_upgrade() -> ModelUpgrade { + ModelUpgrade { + id: "gpt-5.2-codex".to_string(), + reasoning_effort_mapping: None, + migration_config_key: "gpt-5.2-codex".to_string(), + model_link: Some("https://openai.com/index/introducing-gpt-5-2-codex".to_string()), + upgrade_copy: Some( + "Codex is now powered by gpt-5.2-codex, our latest frontier agentic coding model. It is smarter and faster than its predecessors and capable of long-running project-scale work." + .to_string(), + ), + } +} + pub(super) fn builtin_model_presets(_auth_mode: Option) -> Vec { PRESETS .iter() diff --git a/codex-rs/core/tests/suite/list_models.rs b/codex-rs/core/tests/suite/list_models.rs index 7db4cf76a..8cbcc063a 100644 --- a/codex-rs/core/tests/suite/list_models.rs +++ b/codex-rs/core/tests/suite/list_models.rs @@ -184,6 +184,10 @@ fn gpt52_codex_upgrade() -> codex_protocol::openai_models::ModelUpgrade { reasoning_effort_mapping: None, migration_config_key: "gpt-5.2-codex".to_string(), model_link: Some("https://openai.com/index/introducing-gpt-5-2-codex".to_string()), + upgrade_copy: Some( + "Codex is now powered by gpt-5.2-codex, our latest frontier agentic coding model. It is smarter and faster than its predecessors and capable of long-running project-scale work." + .to_string(), + ), } } diff --git a/codex-rs/protocol/src/openai_models.rs b/codex-rs/protocol/src/openai_models.rs index 3fc29f919..28b25bb60 100644 --- a/codex-rs/protocol/src/openai_models.rs +++ b/codex-rs/protocol/src/openai_models.rs @@ -53,6 +53,7 @@ pub struct ModelUpgrade { pub reasoning_effort_mapping: Option>, pub migration_config_key: String, pub model_link: Option, + pub upgrade_copy: Option, } /// Metadata describing a Codex-supported model. @@ -219,6 +220,7 @@ impl From for ModelPreset { migration_config_key: info.slug.clone(), // todo(aibrahim): add the model link here. model_link: None, + upgrade_copy: None, }), show_in_picker: info.visibility == ModelVisibility::List, supported_in_api: info.supported_in_api, diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 66e12f086..e6c17da3b 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -195,6 +195,7 @@ async fn handle_model_migration_prompt_if_needed( reasoning_effort_mapping, migration_config_key, model_link, + upgrade_copy, }) = upgrade { if migration_prompt_hidden(config, migration_config_key.as_str()) { @@ -227,6 +228,7 @@ async fn handle_model_migration_prompt_if_needed( model, &target_model, model_link.clone(), + upgrade_copy.clone(), heading_label, target_description, can_opt_out, @@ -1398,6 +1400,7 @@ mod tests { reasoning_effort_mapping: None, migration_config_key: HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG.to_string(), model_link: None, + upgrade_copy: None, }); available.retain(|preset| preset.model != "gpt-5-codex"); available.push(current.clone()); diff --git a/codex-rs/tui/src/model_migration.rs b/codex-rs/tui/src/model_migration.rs index 5140a7a38..cf2208109 100644 --- a/codex-rs/tui/src/model_migration.rs +++ b/codex-rs/tui/src/model_migration.rs @@ -59,26 +59,36 @@ pub(crate) fn migration_copy_for_models( current_model: &str, target_model: &str, model_link: Option, + migration_copy: Option, target_display_name: String, target_description: Option, can_opt_out: bool, ) -> ModelMigrationCopy { - let heading_text = Span::from(format!("Try {target_display_name}")).bold(); - let description_line = target_description - .filter(|desc| !desc.is_empty()) - .map(Line::from) - .unwrap_or_else(|| { - Line::from(format!( - "{target_display_name} is recommended for better performance and reliability." - )) - }); + let heading_text = Span::from(format!( + "Codex just got an upgrade. Introducing {target_display_name}." + )) + .bold(); + let description_line: Line<'static>; + if let Some(migration_copy) = &migration_copy { + description_line = Line::from(migration_copy.clone()); + } else { + description_line = target_description + .filter(|desc| !desc.is_empty()) + .map(Line::from) + .unwrap_or_else(|| { + Line::from(format!( + "{target_display_name} is recommended for better performance and reliability." + )) + }); + } - let mut content = vec![ - Line::from(format!( + let mut content = vec![]; + if migration_copy.is_none() { + content.push(Line::from(format!( "We recommend switching from {current_model} to {target_model}." - )), - Line::from(""), - ]; + ))); + content.push(Line::from("")); + } if let Some(model_link) = model_link { content.push(Line::from(vec![ @@ -364,6 +374,10 @@ mod tests { "gpt-5.1-codex-mini", "gpt-5.1-codex-max", None, + Some( + "Upgrade to gpt-5.2-codex for the latest and greatest agentic coding model." + .to_string(), + ), "gpt-5.1-codex-max".to_string(), Some("Codex-optimized flagship for deep and fast reasoning.".to_string()), true, @@ -391,6 +405,7 @@ mod tests { "gpt-5", "gpt-5.1", Some("https://www.codex.com/models/gpt-5.1".to_string()), + None, "gpt-5.1".to_string(), Some("Broad world knowledge with strong general reasoning.".to_string()), false, @@ -416,6 +431,7 @@ mod tests { "gpt-5-codex", "gpt-5.1-codex-max", Some("https://www.codex.com/models/gpt-5.1-codex-max".to_string()), + None, "gpt-5.1-codex-max".to_string(), Some("Codex-optimized flagship for deep and fast reasoning.".to_string()), false, @@ -441,6 +457,7 @@ mod tests { "gpt-5-codex-mini", "gpt-5.1-codex-mini", Some("https://www.codex.com/models/gpt-5.1-codex-mini".to_string()), + None, "gpt-5.1-codex-mini".to_string(), Some("Optimized for codex. Cheaper, faster, but less capable.".to_string()), false, @@ -462,6 +479,7 @@ mod tests { "gpt-old", "gpt-new", Some("https://www.codex.com/models/gpt-new".to_string()), + None, "gpt-new".to_string(), Some("Latest recommended model for better performance.".to_string()), true, @@ -489,6 +507,7 @@ mod tests { "gpt-old", "gpt-new", Some("https://www.codex.com/models/gpt-new".to_string()), + None, "gpt-new".to_string(), Some("Latest recommended model for better performance.".to_string()), true, diff --git a/codex-rs/tui/src/selection_list.rs b/codex-rs/tui/src/selection_list.rs index 481673543..25a6450fe 100644 --- a/codex-rs/tui/src/selection_list.rs +++ b/codex-rs/tui/src/selection_list.rs @@ -11,6 +11,15 @@ pub(crate) fn selection_option_row( index: usize, label: String, is_selected: bool, +) -> Box { + selection_option_row_with_dim(index, label, is_selected, false) +} + +pub(crate) fn selection_option_row_with_dim( + index: usize, + label: String, + is_selected: bool, + dim: bool, ) -> Box { let prefix = if is_selected { format!("› {}. ", index + 1) @@ -19,6 +28,8 @@ pub(crate) fn selection_option_row( }; let style = if is_selected { Style::default().cyan() + } else if dim { + Style::default().dim() } else { Style::default() }; diff --git a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt.snap b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt.snap index 702c9140f..313e80cbb 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt.snap @@ -3,12 +3,10 @@ source: tui/src/model_migration.rs expression: terminal.backend() --- -> Try gpt-5.1-codex-max +> Codex just got an upgrade. Introducing gpt-5.1-codex-max. - We recommend switching from gpt-5.1-codex-mini to - gpt-5.1-codex-max. - - Codex-optimized flagship for deep and fast reasoning. + Upgrade to gpt-5.2-codex for the latest and greatest + agentic coding model. You can continue using gpt-5.1-codex-mini if you prefer. diff --git a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex.snap b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex.snap index d71154c55..391eda72e 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex.snap @@ -3,7 +3,7 @@ source: tui/src/model_migration.rs expression: terminal.backend() --- -> Try gpt-5.1-codex-max +> Codex just got an upgrade. Introducing gpt-5.1-codex-max. We recommend switching from gpt-5-codex to gpt-5.1-codex-max. diff --git a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex_mini.snap b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex_mini.snap index 489bf7c97..67cc2603e 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex_mini.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex_mini.snap @@ -3,7 +3,7 @@ source: tui/src/model_migration.rs expression: terminal.backend() --- -> Try gpt-5.1-codex-mini +> Codex just got an upgrade. Introducing gpt-5.1-codex-mini. We recommend switching from gpt-5-codex-mini to gpt-5.1-codex-mini. diff --git a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_family.snap b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_family.snap index 5713c656b..47ba59aa7 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_family.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_family.snap @@ -3,7 +3,7 @@ source: tui/src/model_migration.rs expression: terminal.backend() --- -> Try gpt-5.1 +> Codex just got an upgrade. Introducing gpt-5.1. We recommend switching from gpt-5 to gpt-5.1. diff --git a/codex-rs/tui2/src/app.rs b/codex-rs/tui2/src/app.rs index 1275c1a33..25b9861ab 100644 --- a/codex-rs/tui2/src/app.rs +++ b/codex-rs/tui2/src/app.rs @@ -214,7 +214,7 @@ async fn handle_model_migration_prompt_if_needed( id: target_model, reasoning_effort_mapping, migration_config_key, - model_link: _, + .. }) = upgrade { if migration_prompt_hidden(config, migration_config_key.as_str()) {