From d6d39fe0cf463892a26e031be2b9d53b46e49ce8 Mon Sep 17 00:00:00 2001 From: Ari Webb Date: Tue, 3 Mar 2026 12:52:00 -0800 Subject: [PATCH] fix: persist /model selection for default conversations (#1239) Co-authored-by: Claude Opus 4.6 --- src/cli/App.tsx | 20 ++++++++++++++++--- .../agent/model-preset-refresh.wiring.test.ts | 7 +++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/cli/App.tsx b/src/cli/App.tsx index b0e1f2a..c279cb6 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -3269,8 +3269,12 @@ export default function App({ const syncConversationModel = async () => { // "default" is a virtual sentinel for the agent's primary message history, // not a real conversation object — skip the API call. + // If the user just switched models via /model, honour the local override + // until the next agent state refresh brings back the updated model. if (conversationId === "default") { - applyAgentModelLocally(); + if (!hasConversationModelOverrideRef.current) { + applyAgentModelLocally(); + } return; } @@ -11014,8 +11018,10 @@ ${SYSTEM_REMINDER_CLOSE} phase: "running", }); - // "default" is a virtual sentinel, not a real conversation object — - // skip the API call and fall through with undefined model_settings. + // Persist model change to the backend. + // For real conversations, update the conversation-scoped override. + // For "default" (virtual sentinel with no real conversation object), + // update the agent itself so the model sticks across messages. let conversationModelSettings: | AgentState["model_settings"] | null @@ -11034,6 +11040,14 @@ ${SYSTEM_REMINDER_CLOSE} model_settings?: AgentState["model_settings"] | null; } ).model_settings; + } else { + const { updateAgentLLMConfig } = await import("../agent/modify"); + const updatedAgent = await updateAgentLLMConfig( + agentId, + modelHandle, + model.updateArgs, + ); + conversationModelSettings = updatedAgent.model_settings; } // The API may not echo reasoning_effort back, so populate it from diff --git a/src/tests/agent/model-preset-refresh.wiring.test.ts b/src/tests/agent/model-preset-refresh.wiring.test.ts index 5402b64..3b5252b 100644 --- a/src/tests/agent/model-preset-refresh.wiring.test.ts +++ b/src/tests/agent/model-preset-refresh.wiring.test.ts @@ -71,7 +71,7 @@ describe("model preset refresh wiring", () => { expect(updateSegment).not.toContain("client.agents.update("); }); - test("/model handler updates conversation model (not agent model)", () => { + test("/model handler updates conversation model and falls back to agent for default", () => { const path = fileURLToPath(new URL("../../cli/App.tsx", import.meta.url)); const source = readFileSync(path, "utf-8"); @@ -86,7 +86,10 @@ describe("model preset refresh wiring", () => { expect(segment).toContain("updateConversationLLMConfig("); expect(segment).toContain("conversationIdRef.current"); - expect(segment).not.toContain("updateAgentLLMConfig("); + // For the "default" virtual conversation (no real conversation object), + // the handler falls back to updating the agent directly. + expect(segment).toContain("updateAgentLLMConfig("); + expect(segment).toContain('conversationIdRef.current !== "default"'); }); test("App defines helper to carry over active conversation model", () => {