From 37fd9259e00ab6d41611df03f40f571ce2323654 Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 23:02:03 +0200 Subject: [PATCH] fix(server): keep a provider-side model switch across reload A model_changed event from the provider updated runtimeInfo.model but left config.model at the creation-time value. Reload and daemon restart resume from config.model, so the resumed session was switched back to the old model. model_changed now writes config.model, as thinking_option_changed already does for config.thinkingOptionId. Co-authored-by: Edo Sulaiman --- .../src/server/agent/agent-manager.test.ts | 36 +++++++++++++++++++ .../server/src/server/agent/agent-manager.ts | 1 + 2 files changed, 37 insertions(+) diff --git a/packages/server/src/server/agent/agent-manager.test.ts b/packages/server/src/server/agent/agent-manager.test.ts index 0af62655ae..720fc961a1 100644 --- a/packages/server/src/server/agent/agent-manager.test.ts +++ b/packages/server/src/server/agent/agent-manager.test.ts @@ -5066,6 +5066,42 @@ test("session config drift events update state through the stream channel", asyn expect(streams.map((event) => event.type)).toEqual([]); }); +test("provider-side model switch survives reload and daemon restart", async () => { + const workdir = mkdtempSync(join(tmpdir(), "agent-manager-provider-model-switch-")); + const storage = new AgentStorage(join(workdir, "agents"), logger); + let capturedSession: TestAgentSession | null = null; + class ModelSwitchClient extends TestAgentClient { + override async createSession(config: AgentSessionConfig): Promise { + capturedSession = new TestAgentSession(config); + return capturedSession; + } + } + const client = new ModelSwitchClient(); + const manager = new AgentManager({ + clients: { codex: client }, + registry: storage, + logger, + idFactory: () => "00000000-0000-4000-8000-000000000134", + }); + + const snapshot = await manager.createAgent( + { provider: "codex", cwd: workdir, model: "gpt-5.2-codex" }, + undefined, + { workspaceId: undefined }, + ); + capturedSession!.pushEvent({ + type: "model_changed", + provider: "codex", + runtimeInfo: { provider: "codex", sessionId: capturedSession!.id, model: "gpt-5.4" }, + }); + await manager.flush(); + + await manager.reloadAgentSession(snapshot.id); + expect(client.resumeOverrides.at(-1)?.model).toBe("gpt-5.4"); + expect((await storage.get(snapshot.id))?.config?.model).toBe("gpt-5.4"); + rmSync(workdir, { recursive: true, force: true }); +}); + test("setLabels merges and persists labels", async () => { const workdir = mkdtempSync(join(tmpdir(), "agent-manager-set-labels-")); const storagePath = join(workdir, "agents"); diff --git a/packages/server/src/server/agent/agent-manager.ts b/packages/server/src/server/agent/agent-manager.ts index 061c00e2ca..9bd5ee1e71 100644 --- a/packages/server/src/server/agent/agent-manager.ts +++ b/packages/server/src/server/agent/agent-manager.ts @@ -4310,6 +4310,7 @@ export class AgentManager { return undefined; case "model_changed": agent.runtimeInfo = event.runtimeInfo; + agent.config.model = event.runtimeInfo.model ?? agent.config.model; if (!agent.persistence && event.runtimeInfo.sessionId) { agent.persistence = attachPersistenceCwd( { provider: agent.provider, sessionId: event.runtimeInfo.sessionId },