diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 649f52e549..a4b2c491f9 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -163,6 +163,13 @@ pub(crate) struct CompactConversationRequestSettings { pub(crate) service_tier: Option, } +fn reasoning_effort_for_request(effort: ReasoningEffortConfig) -> ReasoningEffortConfig { + match effort { + ReasoningEffortConfig::Ultra => ReasoningEffortConfig::Custom("max".to_string()), + effort => effort, + } +} + fn session_telemetry_for_request( session_telemetry: &SessionTelemetry, request: &ResponsesApiRequest, @@ -665,11 +672,13 @@ impl ModelClient { let payload = ApiMemorySummarizeInput { model: model_info.slug.clone(), raw_memories, - reasoning: effort.map(|effort| Reasoning { - effort: Some(effort), - summary: None, - context: None, - }), + reasoning: effort + .map(reasoning_effort_for_request) + .map(|effort| Reasoning { + effort: Some(effort), + summary: None, + context: None, + }), }; client @@ -768,7 +777,9 @@ impl ModelClient { ) -> Option { if model_info.supports_reasoning_summaries { Some(Reasoning { - effort: effort.or_else(|| model_info.default_reasoning_level.clone()), + effort: effort + .or_else(|| model_info.default_reasoning_level.clone()) + .map(reasoning_effort_for_request), summary: if summary == ReasoningSummaryConfig::None { None } else { diff --git a/codex-rs/core/src/client_tests.rs b/codex-rs/core/src/client_tests.rs index 78dc324c43..26b3fa1589 100644 --- a/codex-rs/core/src/client_tests.rs +++ b/codex-rs/core/src/client_tests.rs @@ -31,6 +31,7 @@ use codex_protocol::auth::AuthMode; use codex_protocol::models::ContentItem; use codex_protocol::models::ResponseItem; use codex_protocol::openai_models::ModelInfo; +use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::protocol::InternalSessionSource; use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::SubAgentSource; @@ -154,6 +155,20 @@ fn test_session_telemetry() -> SessionTelemetry { ) } +#[test] +fn ultra_reasoning_uses_max_for_requests() { + assert_eq!( + ( + super::reasoning_effort_for_request(ReasoningEffort::Ultra), + super::reasoning_effort_for_request(ReasoningEffort::High), + ), + ( + ReasoningEffort::Custom("max".to_string()), + ReasoningEffort::High, + ) + ); +} + #[derive(Default)] struct TagCollectorVisitor { tags: BTreeMap, diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index ac79dea390..f60c031a88 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1461,6 +1461,7 @@ impl Config { tool_output_token_limit: self.tool_output_token_limit, base_instructions: self.base_instructions.clone(), personality_enabled: self.features.enabled(Feature::Personality), + ultra_reasoning_enabled: self.features.enabled(Feature::MultiAgentMode), model_supports_reasoning_summaries: self.model_supports_reasoning_summaries, model_catalog: self.model_catalog.clone(), } diff --git a/codex-rs/core/src/guardian/review.rs b/codex-rs/core/src/guardian/review.rs index 4756cc1fc4..f9ca83c185 100644 --- a/codex-rs/core/src/guardian/review.rs +++ b/codex-rs/core/src/guardian/review.rs @@ -5,6 +5,7 @@ use codex_analytics::GuardianReviewFailureReason; use codex_analytics::GuardianReviewTerminalStatus; use codex_analytics::GuardianReviewTrackContext; use codex_analytics::GuardianReviewedAction; +use codex_features::Feature; use codex_protocol::config_types::ApprovalsReviewer; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::CodexErrorInfo; @@ -684,11 +685,14 @@ pub(super) async fn guardian_review_session_config( Some(network_proxy) => Some(network_proxy.proxy().current_cfg().await?), None => None, }; - let available_models = session + let mut available_models = session .services .models_manager .list_models(codex_models_manager::manager::RefreshStrategy::Offline) .await; + if !turn.config.features.enabled(Feature::MultiAgentMode) { + codex_models_manager::model_presets::hide_ultra_reasoning_effort(&mut available_models); + } let default_review_model_id = turn.provider.approval_review_preferred_model(); let preferred_reasoning_effort = |supports_low: bool, fallback| { if supports_low { diff --git a/codex-rs/core/src/guardian/tests.rs b/codex-rs/core/src/guardian/tests.rs index bf6a478896..0bd044b836 100644 --- a/codex-rs/core/src/guardian/tests.rs +++ b/codex-rs/core/src/guardian/tests.rs @@ -6,6 +6,7 @@ use crate::config::ManagedFeatures; use crate::config::NetworkProxySpec; use crate::config::test_config; use crate::guardian::approval_request::guardian_request_target_item_id; +use crate::guardian::review::guardian_review_session_config; use crate::session::session::Session; use crate::session::turn_context::TurnContext; use crate::test_support; @@ -36,6 +37,7 @@ use codex_protocol::models::PermissionProfile; use codex_protocol::models::ResponseItem; use codex_protocol::openai_models::ModelsResponse; use codex_protocol::openai_models::ReasoningEffort; +use codex_protocol::openai_models::ReasoningEffortPreset; use codex_protocol::permissions::FileSystemAccessMode; use codex_protocol::permissions::FileSystemPath; use codex_protocol::permissions::FileSystemSandboxEntry; @@ -1420,6 +1422,44 @@ enum GuardianTestCatalog { ParentOnly, } +#[tokio::test] +async fn guardian_review_does_not_select_ultra_when_feature_is_disabled() -> anyhow::Result<()> { + let server = start_mock_server().await; + let (mut session, turn) = guardian_test_session_and_turn(&server).await; + assert!(!turn.config.features.enabled(Feature::MultiAgentMode)); + + let mut review_model = turn.model_info.clone(); + review_model.slug = turn.provider.approval_review_preferred_model().to_string(); + review_model.default_reasoning_level = Some(ReasoningEffort::Ultra); + review_model.supported_reasoning_levels = vec![ + ReasoningEffortPreset { + effort: ReasoningEffort::High, + description: "High".to_string(), + }, + ReasoningEffortPreset { + effort: ReasoningEffort::Ultra, + description: "Ultra".to_string(), + }, + ]; + let models_manager = StaticModelsManager::new( + Some(Arc::clone(&session.services.auth_manager)), + ModelsResponse { + models: vec![review_model], + }, + ); + Arc::get_mut(&mut session) + .expect("session should be unique") + .services + .models_manager = Arc::new(models_manager); + + let guardian_config = guardian_review_session_config(&session, &turn).await?; + assert_eq!( + guardian_config.spawn_config.model_reasoning_effort, + Some(ReasoningEffort::High) + ); + Ok(()) +} + async fn guardian_request_model_for_auto_review( auto_review_model_override: Option, catalog: GuardianTestCatalog, diff --git a/codex-rs/core/src/session/review.rs b/codex-rs/core/src/session/review.rs index c55afbf141..0f1eb45d9b 100644 --- a/codex-rs/core/src/session/review.rs +++ b/codex-rs/core/src/session/review.rs @@ -24,11 +24,14 @@ pub(super) async fn spawn_review_thread( let _ = review_features.disable(Feature::WebSearchCached); let _ = review_features.disable(Feature::Goals); let review_web_search_mode = WebSearchMode::Disabled; - let available_models = sess + let mut available_models = sess .services .models_manager .list_models(RefreshStrategy::OnlineIfUncached) .await; + if !review_features.enabled(Feature::MultiAgentMode) { + codex_models_manager::model_presets::hide_ultra_reasoning_effort(&mut available_models); + } let unified_exec_shell_mode = UnifiedExecShellMode::for_session( codex_tools::unified_exec_feature_mode_for_features(review_features.get()), crate::tools::tool_user_shell_type(sess.services.user_shell.as_ref()), diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index da526d4911..99d53775a8 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -6,6 +6,7 @@ use codex_core_skills::HostSkillsSnapshot; use codex_file_system::FileSystemSandboxContext; use codex_model_provider::SharedModelProvider; use codex_model_provider::create_model_provider; +use codex_models_manager::model_presets::hide_ultra_reasoning_effort; use codex_protocol::SessionId; use codex_protocol::ThreadId; use codex_protocol::models::AdditionalPermissionProfile; @@ -246,9 +247,12 @@ impl TurnContext { Some(reasoning_effort.clone()), /*developer_instructions*/ None, ); - let available_models = models_manager + let mut available_models = models_manager .list_models(RefreshStrategy::OnlineIfUncached) .await; + if !config.features.enabled(Feature::MultiAgentMode) { + hide_ultra_reasoning_effort(&mut available_models); + } Self { sub_id: self.sub_id.clone(), @@ -506,7 +510,10 @@ impl Session { let auth_manager_for_context = auth_manager.clone(); let provider_for_context = create_model_provider(provider, auth_manager); let session_telemetry_for_context = session_telemetry; - let available_models = models_manager.try_list_models().unwrap_or_default(); + let mut available_models = models_manager.try_list_models().unwrap_or_default(); + if !per_turn_config.features.enabled(Feature::MultiAgentMode) { + hide_ultra_reasoning_effort(&mut available_models); + } let unified_exec_shell_mode = UnifiedExecShellMode::for_session( codex_tools::unified_exec_feature_mode_for_features(per_turn_config.features.get()), crate::tools::tool_user_shell_type(user_shell), diff --git a/codex-rs/core/tests/suite/spawn_agent_description.rs b/codex-rs/core/tests/suite/spawn_agent_description.rs index fb493c1b14..ec336459d4 100644 --- a/codex-rs/core/tests/suite/spawn_agent_description.rs +++ b/codex-rs/core/tests/suite/spawn_agent_description.rs @@ -130,6 +130,10 @@ async fn spawn_agent_description_lists_visible_models_and_reasoning_efforts() -> effort: ReasoningEffort::High, description: "Deep dive".to_string(), }, + ReasoningEffortPreset { + effort: ReasoningEffort::Ultra, + description: "Maximum reasoning with proactive delegation".to_string(), + }, ], vec![ModelServiceTier { id: "priority".to_string(), @@ -167,6 +171,10 @@ async fn spawn_agent_description_lists_visible_models_and_reasoning_efforts() -> .features .enable(Feature::Collab) .expect("test config should allow feature update"); + config + .features + .enable(Feature::MultiAgentMode) + .expect("test config should allow feature update"); config.multi_agent_v2.hide_spawn_agent_metadata = false; }); let test = builder.build(&server).await?; @@ -200,7 +208,7 @@ async fn spawn_agent_description_lists_visible_models_and_reasoning_efforts() -> "expected model override usage guidance in spawn_agent description: {description:?}" ); assert!( - description.contains("Reasoning efforts: low, medium (default), high."), + description.contains("Reasoning efforts: low, medium (default), high, ultra."), "expected default reasoning effort in spawn_agent description: {description:?}" ); assert!( diff --git a/codex-rs/features/src/lib.rs b/codex-rs/features/src/lib.rs index 9d3d3e1b4a..be84d80ca0 100644 --- a/codex-rs/features/src/lib.rs +++ b/codex-rs/features/src/lib.rs @@ -145,7 +145,7 @@ pub enum Feature { Collab, /// Enable task-path-based multi-agent routing. MultiAgentV2, - /// Removed compatibility flag retained as a no-op. + /// Enable Ultra reasoning effort and its proactive multi-agent behavior. MultiAgentMode, /// Enable CSV-backed agent job tools. SpawnCsv, @@ -1036,7 +1036,7 @@ pub const FEATURES: &[FeatureSpec] = &[ FeatureSpec { id: Feature::MultiAgentMode, key: "multi_agent_mode", - stage: Stage::Removed, + stage: Stage::UnderDevelopment, default_enabled: false, }, FeatureSpec { diff --git a/codex-rs/models-manager/src/config.rs b/codex-rs/models-manager/src/config.rs index b64add40fc..a4bf4c0af8 100644 --- a/codex-rs/models-manager/src/config.rs +++ b/codex-rs/models-manager/src/config.rs @@ -7,6 +7,7 @@ pub struct ModelsManagerConfig { pub tool_output_token_limit: Option, pub base_instructions: Option, pub personality_enabled: bool, + pub ultra_reasoning_enabled: bool, pub model_supports_reasoning_summaries: Option, pub model_catalog: Option, } diff --git a/codex-rs/models-manager/src/model_info.rs b/codex-rs/models-manager/src/model_info.rs index 566953dfc2..0e382569ce 100644 --- a/codex-rs/models-manager/src/model_info.rs +++ b/codex-rs/models-manager/src/model_info.rs @@ -4,6 +4,7 @@ use codex_protocol::openai_models::ModelInfo; use codex_protocol::openai_models::ModelInstructionsVariables; use codex_protocol::openai_models::ModelMessages; use codex_protocol::openai_models::ModelVisibility; +use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::openai_models::TruncationMode; use codex_protocol::openai_models::TruncationPolicyConfig; use codex_protocol::openai_models::WebSearchToolType; @@ -21,6 +22,17 @@ const LOCAL_PRAGMATIC_TEMPLATE: &str = "You are a deeply pragmatic, effective so const PERSONALITY_PLACEHOLDER: &str = "{{ personality }}"; pub fn with_config_overrides(mut model: ModelInfo, config: &ModelsManagerConfig) -> ModelInfo { + if !config.ultra_reasoning_enabled { + model + .supported_reasoning_levels + .retain(|preset| preset.effort != ReasoningEffort::Ultra); + if model.default_reasoning_level == Some(ReasoningEffort::Ultra) { + model.default_reasoning_level = model + .supported_reasoning_levels + .last() + .map(|preset| preset.effort.clone()); + } + } if let Some(supports_reasoning_summaries) = config.model_supports_reasoning_summaries && supports_reasoning_summaries { diff --git a/codex-rs/models-manager/src/model_info_tests.rs b/codex-rs/models-manager/src/model_info_tests.rs index 70ad3da8df..dfa11af1c2 100644 --- a/codex-rs/models-manager/src/model_info_tests.rs +++ b/codex-rs/models-manager/src/model_info_tests.rs @@ -1,7 +1,42 @@ use super::*; use crate::ModelsManagerConfig; +use codex_protocol::openai_models::ReasoningEffortPreset; use pretty_assertions::assert_eq; +#[test] +fn ultra_reasoning_requires_feature() { + let mut model = model_info_from_slug("unknown-model"); + model.default_reasoning_level = Some(ReasoningEffort::Ultra); + model.supported_reasoning_levels = vec![ + ReasoningEffortPreset { + effort: ReasoningEffort::Low, + description: "Low".to_string(), + }, + ReasoningEffortPreset { + effort: ReasoningEffort::Ultra, + description: "Ultra".to_string(), + }, + ]; + let mut expected_without_ultra = model.clone(); + expected_without_ultra.default_reasoning_level = Some(ReasoningEffort::Low); + expected_without_ultra.supported_reasoning_levels.pop(); + + assert_eq!( + with_config_overrides(model.clone(), &ModelsManagerConfig::default()), + expected_without_ultra + ); + assert_eq!( + with_config_overrides( + model.clone(), + &ModelsManagerConfig { + ultra_reasoning_enabled: true, + ..Default::default() + }, + ), + model + ); +} + #[test] fn reasoning_summaries_override_true_enables_support() { let model = model_info_from_slug("unknown-model"); diff --git a/codex-rs/models-manager/src/model_presets.rs b/codex-rs/models-manager/src/model_presets.rs index 0039b52eaa..ebbc9842aa 100644 --- a/codex-rs/models-manager/src/model_presets.rs +++ b/codex-rs/models-manager/src/model_presets.rs @@ -1,6 +1,25 @@ +use codex_protocol::openai_models::ModelPreset; +use codex_protocol::openai_models::ReasoningEffort; + /// Legacy notice keys kept for config compatibility with older migration prompts. /// /// Hardcoded model presets were removed; model listings are now derived from the active catalog. pub const HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG: &str = "hide_gpt5_1_migration_prompt"; pub const HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG: &str = "hide_gpt-5.1-codex-max_migration_prompt"; + +/// Removes the gated Ultra effort from model presets exposed to clients or tools. +pub fn hide_ultra_reasoning_effort(models: &mut [ModelPreset]) { + for model in models { + model + .supported_reasoning_efforts + .retain(|preset| preset.effort != ReasoningEffort::Ultra); + if model.default_reasoning_effort == ReasoningEffort::Ultra { + model.default_reasoning_effort = model + .supported_reasoning_efforts + .last() + .map(|preset| preset.effort.clone()) + .unwrap_or(ReasoningEffort::None); + } + } +} diff --git a/codex-rs/protocol/src/openai_models.rs b/codex-rs/protocol/src/openai_models.rs index 5b95fee29d..f697a8107f 100644 --- a/codex-rs/protocol/src/openai_models.rs +++ b/codex-rs/protocol/src/openai_models.rs @@ -45,6 +45,7 @@ pub enum ReasoningEffort { Medium, High, XHigh, + Ultra, /// A model-defined effort value that this client does not know yet. Custom(String), } @@ -59,6 +60,7 @@ impl ReasoningEffort { Self::Medium => "medium", Self::High => "high", Self::XHigh => "xhigh", + Self::Ultra => "ultra", Self::Custom(effort) => effort, } } @@ -123,6 +125,7 @@ impl FromStr for ReasoningEffort { "medium" => Ok(Self::Medium), "high" => Ok(Self::High), "xhigh" => Ok(Self::XHigh), + "ultra" => Ok(Self::Ultra), "" => Err("reasoning_effort must not be empty".to_string()), effort => Ok(Self::Custom(effort.to_string())), } @@ -701,20 +704,25 @@ mod tests { let deserialized = from_str::(r#""max""#) .expect("custom reasoning effort should deserialize"); let serialized = to_string(&custom).expect("custom reasoning effort should serialize"); + let serialized_ultra = to_string(&ReasoningEffort::Ultra).expect("Ultra should serialize"); assert_eq!( ( "high".parse(), + "ultra".parse(), "max".parse(), deserialized, serialized, + serialized_ultra, custom.to_string(), ), ( Ok(ReasoningEffort::High), + Ok(ReasoningEffort::Ultra), Ok(custom.clone()), custom, r#""max""#.to_string(), + r#""ultra""#.to_string(), "max".to_string(), ) ); diff --git a/codex-rs/tui/src/app_server_session.rs b/codex-rs/tui/src/app_server_session.rs index 7181d1a560..870a41c565 100644 --- a/codex-rs/tui/src/app_server_session.rs +++ b/codex-rs/tui/src/app_server_session.rs @@ -107,6 +107,7 @@ use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::TurnSteerParams; use codex_app_server_protocol::TurnSteerResponse; use codex_app_server_protocol::UserInput; +use codex_features::Feature; use codex_otel::TelemetryAuthMode; use codex_protocol::ThreadId; use codex_protocol::approvals::GuardianAssessmentEvent; @@ -118,6 +119,7 @@ use codex_protocol::openai_models::ModelAvailabilityNux; use codex_protocol::openai_models::ModelPreset; use codex_protocol::openai_models::ModelServiceTier; use codex_protocol::openai_models::ModelUpgrade; +use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::openai_models::ReasoningEffortPreset; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_path_uri::PathUri; @@ -278,7 +280,9 @@ impl AppServerSession { let available_models = models .data .into_iter() - .map(model_preset_from_api_model) + .map(|model| { + model_preset_from_api_model(model, config.features.enabled(Feature::MultiAgentMode)) + }) .collect::>(); let default_model = config .model @@ -1189,7 +1193,7 @@ pub(crate) fn status_account_display_from_auth_mode( } } -fn model_preset_from_api_model(model: ApiModel) -> ModelPreset { +fn model_preset_from_api_model(model: ApiModel, ultra_reasoning_enabled: bool) -> ModelPreset { let upgrade = model.upgrade.map(|upgrade_id| { let upgrade_info = model.upgrade_info.clone(); ModelUpgrade { @@ -1205,7 +1209,7 @@ fn model_preset_from_api_model(model: ApiModel) -> ModelPreset { } }); - ModelPreset { + let mut preset = ModelPreset { id: model.id, model: model.model, display_name: model.display_name, @@ -1240,7 +1244,24 @@ fn model_preset_from_api_model(model: ApiModel) -> ModelPreset { // `model/list` already returns models filtered for the active client/auth context. supported_in_api: true, input_modalities: model.input_modalities, + }; + if ultra_reasoning_enabled { + preset + .supported_reasoning_efforts + .sort_by_key(|preset| preset.effort == ReasoningEffort::Ultra); + } else { + preset + .supported_reasoning_efforts + .retain(|preset| preset.effort != ReasoningEffort::Ultra); + if preset.default_reasoning_effort == ReasoningEffort::Ultra { + preset.default_reasoning_effort = preset + .supported_reasoning_efforts + .last() + .map(|preset| preset.effort.clone()) + .unwrap_or(ReasoningEffort::None); + } } + preset } fn approvals_reviewer_override_from_config( @@ -1747,7 +1768,6 @@ mod tests { use codex_app_server_protocol::ThreadStatus; use codex_app_server_protocol::Turn; use codex_app_server_protocol::TurnStatus; - use codex_features::Feature; use codex_protocol::config_types::Personality; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::config_types::ServiceTier; @@ -1756,7 +1776,6 @@ mod tests { use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_READ_ONLY; use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE; use codex_protocol::models::ManagedFileSystemPermissions; - use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::permissions::FileSystemAccessMode; use codex_protocol::permissions::FileSystemPath; use codex_protocol::permissions::FileSystemSandboxEntry; @@ -1793,6 +1812,83 @@ mod tests { } } + #[test] + fn model_preset_filters_ultra_for_tui_when_feature_is_disabled() { + let api_model = ApiModel { + id: "model-id".to_string(), + model: "model-slug".to_string(), + upgrade: None, + upgrade_info: None, + availability_nux: None, + display_name: "Model".to_string(), + description: "Description".to_string(), + hidden: false, + supported_reasoning_efforts: vec![ + codex_app_server_protocol::ReasoningEffortOption { + reasoning_effort: ReasoningEffort::Ultra, + description: "Ultra".to_string(), + }, + codex_app_server_protocol::ReasoningEffortOption { + reasoning_effort: ReasoningEffort::Low, + description: "Low".to_string(), + }, + codex_app_server_protocol::ReasoningEffortOption { + reasoning_effort: ReasoningEffort::XHigh, + description: "Extra high".to_string(), + }, + ], + default_reasoning_effort: ReasoningEffort::Ultra, + input_modalities: codex_protocol::openai_models::default_input_modalities(), + supports_personality: false, + additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), + default_service_tier: None, + is_default: true, + }; + + let filtered = + model_preset_from_api_model(api_model.clone(), /*ultra_reasoning_enabled*/ false); + let unfiltered = + model_preset_from_api_model(api_model, /*ultra_reasoning_enabled*/ true); + + assert_eq!( + ( + filtered.default_reasoning_effort, + filtered.supported_reasoning_efforts, + unfiltered.default_reasoning_effort, + unfiltered.supported_reasoning_efforts, + ), + ( + ReasoningEffort::XHigh, + vec![ + ReasoningEffortPreset { + effort: ReasoningEffort::Low, + description: "Low".to_string(), + }, + ReasoningEffortPreset { + effort: ReasoningEffort::XHigh, + description: "Extra high".to_string(), + }, + ], + ReasoningEffort::Ultra, + vec![ + ReasoningEffortPreset { + effort: ReasoningEffort::Low, + description: "Low".to_string(), + }, + ReasoningEffortPreset { + effort: ReasoningEffort::XHigh, + description: "Extra high".to_string(), + }, + ReasoningEffortPreset { + effort: ReasoningEffort::Ultra, + description: "Ultra".to_string(), + }, + ], + ) + ); + } + #[test] fn app_server_rate_limit_snapshots_deduplicates_top_level_limit_from_map() { let response = GetAccountRateLimitsResponse { diff --git a/codex-rs/tui/src/chatwidget/model_popups.rs b/codex-rs/tui/src/chatwidget/model_popups.rs index 90fd6c2cc3..3042ff6c71 100644 --- a/codex-rs/tui/src/chatwidget/model_popups.rs +++ b/codex-rs/tui/src/chatwidget/model_popups.rs @@ -505,6 +505,7 @@ impl ChatWidget { ReasoningEffortConfig::Medium => "Medium".to_string(), ReasoningEffortConfig::High => "High".to_string(), ReasoningEffortConfig::XHigh => "Extra high".to_string(), + ReasoningEffortConfig::Ultra => "Ultra".to_string(), ReasoningEffortConfig::Custom(value) => value.clone(), } } diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap index a436136570..19e39ca439 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap @@ -6,7 +6,7 @@ expression: popup 1. Low Fast responses with lighter reasoning 2. Medium (default) Balances speed and reasoning depth for everyday tasks - 3. max Maximum available reasoning + 3. Ultra Maximum reasoning with proactive delegation › 4. High (current) Greater reasoning depth for complex problems 5. Extra high Extra high reasoning depth for complex problems diff --git a/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs b/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs index a8053dc5a3..95c67e0794 100644 --- a/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs +++ b/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs @@ -3018,8 +3018,8 @@ async fn model_reasoning_selection_popup_snapshot() { preset.supported_reasoning_efforts.insert( 2, ReasoningEffortPreset { - effort: ReasoningEffortConfig::Custom("max".to_string()), - description: "Maximum available reasoning".to_string(), + effort: ReasoningEffortConfig::Ultra, + description: "Maximum reasoning with proactive delegation".to_string(), }, ); chat.open_reasoning_popup(preset);