Files
codex/codex-rs/tui/src/managed_new_thread_defaults.rs
T
hefuc-oai cf36c688b3 [codex] Use managed defaults for TUI threads (#30147)
## Why

#29683 exposes managed defaults for new-thread model settings through
`configRequirements/read` without applying them server-wide. The TUI is
an app-server client, so it should explicitly consume those defaults
when it creates a fresh thread.

This lets plain `codex` start on the managed model while preserving the
existing ability to change model settings within the thread.

## What changed

- Read `requirements.models.newThread` during TUI app-server bootstrap.
- Apply the managed model, reasoning effort, and service tier to the
initial fresh thread and subsequent `/new` or `/clear` threads.
- Keep explicit launch overrides above the managed defaults.
- Normalize the managed `fast` service tier to the `priority` request
value.
- Leave resumed and forked threads unchanged.

The application logic lives in a small TUI-only module; app-server
`thread/start` behavior remains unchanged for other clients.

## User experience

- Plain `codex` starts with the managed new-thread settings.
- A user can still change settings with `/model` or the existing
service-tier controls.
- Starting another fresh thread reapplies the managed defaults.
- Explicit launch choices such as `codex -m <model>` continue to win.

## Validation

- `just test -p codex-tui managed_new_thread_defaults`
- `just fix -p codex-tui`

Depends on #29683.
2026-06-26 19:27:31 +00:00

52 lines
2.2 KiB
Rust

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`) and generic `-c key=value` settings
// (`cli_kv_overrides`), then fill only the fields that were not selected for this invocation.
// 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_cli_override = |key: &str| cli_kv_overrides.iter().any(|(path, _value)| path == key);
let has_explicit_model_settings = harness_overrides.model.is_some()
|| has_cli_override("model")
|| has_cli_override("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_cli_override("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;