Files
codex/codex-rs/tui/src/managed_new_thread_defaults.rs
T
Alex Ryan aff3e0db94 Preserve selected profile settings over managed new-thread defaults (#44693)
## Why

Managed new-thread defaults could overwrite model, reasoning effort, and service tier settings from an explicitly selected profile.

## What changed

- Treat effective profile settings as explicit launch choices when applying managed defaults. A profile setting for either `model` or `model_reasoning_effort` opts out of both managed values; `service_tier` remains independent.
- Count a profile setting only when it supplies the highest-precedence active value, so settings shadowed by project configuration do not block defaults.

## Testing

Add regression coverage for profile precedence at startup, session replacement, and background task creation, plus custom-provider profiles, service tiers, unrelated profile settings, and project settings that shadow profiles.

GitOrigin-RevId: 98f036b8b7059bab8598283600dbc6967c71d3e3
2026-09-11 00:59:27 +00:00

52 lines
2.2 KiB
Rust

use crate::app::has_launch_setting;
use crate::legacy_core::config::Config;
use crate::legacy_core::config::ConfigOverrides;
use codex_app_server_protocol::NewThreadModelDefaults;
use codex_protocol::config_types::ServiceTier;
use toml::Value as TomlValue;
pub(crate) fn apply_managed_new_thread_defaults(
config: &mut Config,
defaults: Option<&NewThreadModelDefaults>,
cli_kv_overrides: &[(String, TomlValue)],
harness_overrides: &ConfigOverrides,
) {
let Some(defaults) = defaults else {
return;
};
// Managed values are defaults rather than enforcement. Preserve explicit launch choices from
// dedicated flags such as `-m` (`harness_overrides`), generic `-c key=value` settings
// (`cli_kv_overrides`), and explicitly selected profiles.
// Model and reasoning effort are a compatibility-sensitive pair, so an explicit override of
// either opts out of both managed values. For example, `codex -m gpt-5.4` keeps that model and
// its existing/default effort, while `-c model_reasoning_effort=low` does not switch to the
// managed model. Service tier remains independent and is resolved against the selected model
// before the thread starts.
let has_explicit_model_settings = harness_overrides.model.is_some()
|| has_launch_setting(config, cli_kv_overrides, "model")
|| has_launch_setting(config, cli_kv_overrides, "model_reasoning_effort");
if !has_explicit_model_settings && let Some(model) = defaults.model.as_ref() {
config.model = Some(model.clone());
}
if !has_explicit_model_settings
&& let Some(reasoning_effort) = defaults.model_reasoning_effort.as_ref()
{
config.model_reasoning_effort = Some(reasoning_effort.clone());
}
if harness_overrides.service_tier.is_none()
&& !has_launch_setting(config, cli_kv_overrides, "service_tier")
&& let Some(service_tier) = defaults.service_tier.as_ref()
{
config.service_tier = Some(
ServiceTier::from_request_value(service_tier)
.map(|tier| tier.request_value().to_string())
.unwrap_or_else(|| service_tier.clone()),
);
}
}
#[cfg(test)]
#[path = "managed_new_thread_defaults_tests.rs"]
mod tests;