mirror of
https://github.com/openai/codex.git
synced 2026-11-12 21:20:34 +00:00
Preserve thread model settings across resume and revert (#55923)
## Why Resuming a thread can replace saved model settings with global defaults. A selected service tier can also be lost when the current model does not support it. ## What changed - Restore saved service tier, reasoning summary, and personality on resume unless the caller explicitly overrides them, respecting thread ownership of settings snapshots. - Retain the selected service tier for persistence, settings notifications, and resume comparisons while filtering the effective tier against the current model's capabilities. - Carry the runtime model, service tier, and personality into thread revert reconstruction. - Persist initial thread settings before user input during MCP startup so an interrupted first turn retains its settings checkpoint. ## Testing Extend regression coverage for cold resume and explicit overrides, unsupported service tiers, settings preservation during revert, and settings checkpoints before user input when MCP startup is interrupted. GitOrigin-RevId: e5ec012a5678048c5eedd443e524f38c142bf9fd
This commit is contained in:
@@ -5,6 +5,7 @@ mod daemon_continuation;
|
||||
mod daemon_snapshot;
|
||||
|
||||
use super::persisted_resume_settings::PersistedResumeSettings;
|
||||
use super::persisted_resume_settings::latest_persisted_model_settings;
|
||||
use super::persisted_resume_settings::latest_persisted_resume_settings;
|
||||
use super::thread_enrichment::enrich_loaded_threads;
|
||||
use super::thread_fork_goal::inherit_thread_goal_snapshot;
|
||||
@@ -120,6 +121,7 @@ struct ThreadRevertRuntimeSnapshot {
|
||||
fn collect_resume_override_mismatches(
|
||||
request: &ThreadResumeParams,
|
||||
config_snapshot: &ThreadConfigSnapshot,
|
||||
selected_service_tier: Option<&str>,
|
||||
) -> Vec<String> {
|
||||
let mut mismatch_details = Vec::new();
|
||||
|
||||
@@ -140,7 +142,7 @@ fn collect_resume_override_mismatches(
|
||||
));
|
||||
}
|
||||
if let Some(requested_service_tier) = request.service_tier.as_ref()
|
||||
&& requested_service_tier != &config_snapshot.service_tier
|
||||
&& Some(requested_service_tier.as_deref().unwrap_or("default")) != selected_service_tier
|
||||
{
|
||||
mismatch_details.push(format!(
|
||||
"service_tier requested={requested_service_tier:?} active={:?}",
|
||||
@@ -2405,10 +2407,17 @@ impl ThreadRequestProcessor {
|
||||
app_server_client_version: Option<String>,
|
||||
) -> Result<ThreadRevertResponse, JSONRPCErrorError> {
|
||||
let ThreadRevertRuntimeSnapshot {
|
||||
config,
|
||||
mut config,
|
||||
settings,
|
||||
client_mcp_extensions,
|
||||
} = runtime_snapshot;
|
||||
if let Some(collaboration_mode) = settings.collaboration_mode.as_ref() {
|
||||
config.model = Some(collaboration_mode.model().to_string());
|
||||
}
|
||||
if let Some(service_tier) = settings.service_tier.as_ref() {
|
||||
config.service_tier.clone_from(service_tier);
|
||||
}
|
||||
config.personality = settings.personality;
|
||||
let thread_id_string = thread_id.to_string();
|
||||
let stored_thread = self
|
||||
.read_stored_thread_for_resume(
|
||||
@@ -4186,6 +4195,17 @@ impl ThreadRequestProcessor {
|
||||
}
|
||||
let has_explicit_model_resume_override =
|
||||
has_model_resume_override(request_overrides.as_ref(), &typesafe_overrides);
|
||||
let persisted_model_settings = latest_persisted_model_settings(&thread_history);
|
||||
let has_override = |key| {
|
||||
request_overrides
|
||||
.as_ref()
|
||||
.is_some_and(|overrides| overrides.contains_key(key))
|
||||
};
|
||||
let restore_service_tier =
|
||||
typesafe_overrides.service_tier.is_none() && !has_override("service_tier");
|
||||
let restore_summary = !has_override("model_reasoning_summary");
|
||||
let restore_personality =
|
||||
typesafe_overrides.personality.is_none() && !has_override("personality");
|
||||
typesafe_overrides.persisted_permission_profile = history_settings
|
||||
.filter(|settings| {
|
||||
settings.active_permission_profile.is_none()
|
||||
@@ -4249,7 +4269,17 @@ impl ThreadRequestProcessor {
|
||||
if clear_reasoning_effort {
|
||||
config.model_reasoning_effort = None;
|
||||
}
|
||||
|
||||
if let Some(settings) = persisted_model_settings {
|
||||
if restore_service_tier {
|
||||
config.service_tier.clone_from(&settings.service_tier);
|
||||
}
|
||||
if restore_summary {
|
||||
config.model_reasoning_summary = settings.reasoning_summary;
|
||||
}
|
||||
if restore_personality {
|
||||
config.personality = settings.personality;
|
||||
}
|
||||
}
|
||||
let response_history = thread_history.clone();
|
||||
|
||||
match self
|
||||
@@ -4616,8 +4646,13 @@ impl ThreadRequestProcessor {
|
||||
active_path.display()
|
||||
)));
|
||||
}
|
||||
let config_snapshot = existing_thread.config_snapshot().await;
|
||||
let mismatch_details = collect_resume_override_mismatches(params, &config_snapshot);
|
||||
let (config_snapshot, selected_service_tier) =
|
||||
existing_thread.resume_config_snapshot().await;
|
||||
let mismatch_details = collect_resume_override_mismatches(
|
||||
params,
|
||||
&config_snapshot,
|
||||
selected_service_tier.as_deref(),
|
||||
);
|
||||
if !mismatch_details.is_empty() {
|
||||
let has_subscribers = !self
|
||||
.thread_state_manager
|
||||
|
||||
Reference in New Issue
Block a user