From d1f14e31a7000e7361599e057dc401077a4b5a05 Mon Sep 17 00:00:00 2001 From: jif Date: Tue, 4 Aug 2026 14:56:16 +0000 Subject: [PATCH] Preserve model providers when reloading v2 agents (#36906) ## Why Reloading an unloaded v2 agent could inherit the model provider from the agent that triggered the reload, even though the worker's model was restored from its persisted thread state. ## What changed - Restore both the model and model provider from the stored thread when reloading a v2 agent. - Return an invalid-request error if the stored provider is no longer present in the configured provider map. ## Testing Extend the v2 agent reload test to trigger the reload with a different sender provider and verify that the worker retains its stored provider. GitOrigin-RevId: 6401d9f0d1c116e9954e3bd193d016f04da5f223 --- codex-rs/core/src/agent/control/spawn.rs | 13 ++++++++++++ codex-rs/core/src/agent/control_tests.rs | 27 +++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/codex-rs/core/src/agent/control/spawn.rs b/codex-rs/core/src/agent/control/spawn.rs index 4d057a42b5..8f4f2d62a3 100644 --- a/codex-rs/core/src/agent/control/spawn.rs +++ b/codex-rs/core/src/agent/control/spawn.rs @@ -269,6 +269,7 @@ impl AgentControl { }) .await?; let stored_model = stored_thread.model.clone(); + let stored_model_provider = stored_thread.model_provider.clone(); let stored_source = stored_thread.source.clone(); let stored_parent_thread_id = stored_thread.parent_thread_id; let history = load_agent_model_context(&state, thread_id, stored_thread.history_mode) @@ -324,6 +325,18 @@ impl AgentControl { if let Some(model) = stored_model { config.model = Some(model); } + if config.model_provider_id != stored_model_provider { + config.model_provider = config + .model_providers + .get(&stored_model_provider) + .cloned() + .ok_or_else(|| { + CodexErr::InvalidRequest(format!( + "Model provider `{stored_model_provider}` not found" + )) + })?; + config.model_provider_id = stored_model_provider; + } let residency_slot = self .reserve_v2_residency_slot(&state, &config, Some(thread_id)) .await?; diff --git a/codex-rs/core/src/agent/control_tests.rs b/codex-rs/core/src/agent/control_tests.rs index 75ad1e7786..db48f9bdce 100644 --- a/codex-rs/core/src/agent/control_tests.rs +++ b/codex-rs/core/src/agent/control_tests.rs @@ -713,9 +713,17 @@ async fn ensure_v2_agent_loaded_reloads_registered_unloaded_agent() { Ok(_) => panic!("expected thread to be removed"), } + let mut sender_config = harness.config.clone(); + sender_config.model_provider_id = "ollama".to_string(); + sender_config.model_provider = sender_config + .model_providers + .get("ollama") + .cloned() + .expect("ollama provider should be configured"); + harness .control - .ensure_v2_agent_loaded(harness.config.clone(), spawned_agent.thread_id) + .ensure_v2_agent_loaded(sender_config, spawned_agent.thread_id) .await .expect("known v2 agent should reload"); let reloaded_child = harness @@ -728,6 +736,23 @@ async fn ensure_v2_agent_loaded_reloads_registered_unloaded_agent() { "gpt-5.6-luna", "residency reload must preserve the worker model instead of inheriting its parent model", ); + assert_eq!( + ( + reloaded_child.config_snapshot().await.model_provider_id, + reloaded_child + .session + .new_default_turn() + .await + .provider + .info() + .clone(), + ), + ( + stored_child.model_provider, + harness.config.model_provider.clone() + ), + "residency reload must preserve the worker provider instead of inheriting its sender's provider", + ); let communication = InterAgentCommunication::new( AgentPath::root(),