From c7a018b893f136f0ccbca579dd182dc514e41779 Mon Sep 17 00:00:00 2001 From: Harshit Agarwal Date: Fri, 27 Mar 2026 23:02:41 +0530 Subject: [PATCH] fix(claude): avoid resetting the Claude model on every turn --- .../src/provider/Layers/ClaudeAdapter.test.ts | 79 +++++++++++++++++++ .../src/provider/Layers/ClaudeAdapter.ts | 17 +++- 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts index a10a40629c..acb82709f7 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts @@ -2384,6 +2384,85 @@ describe("ClaudeAdapterLive", () => { ); }); + it.effect( + "does not re-set the Claude model when the session already uses the same effective API model", + () => { + const harness = makeHarness(); + return Effect.gen(function* () { + const adapter = yield* ClaudeAdapter; + const modelSelection = { + provider: "claudeAgent" as const, + model: "claude-opus-4-6", + }; + + const session = yield* adapter.startSession({ + threadId: THREAD_ID, + provider: "claudeAgent", + modelSelection, + runtimeMode: "full-access", + }); + + yield* adapter.sendTurn({ + threadId: session.threadId, + input: "hello", + modelSelection, + attachments: [], + }); + yield* adapter.sendTurn({ + threadId: session.threadId, + input: "hello again", + modelSelection, + attachments: [], + }); + + assert.deepEqual(harness.query.setModelCalls, []); + }).pipe( + Effect.provideService(Random.Random, makeDeterministicRandomService()), + Effect.provide(harness.layer), + ); + }, + ); + + it.effect("re-sets the Claude model when the effective API model changes", () => { + const harness = makeHarness(); + return Effect.gen(function* () { + const adapter = yield* ClaudeAdapter; + + const session = yield* adapter.startSession({ + threadId: THREAD_ID, + provider: "claudeAgent", + runtimeMode: "full-access", + }); + + yield* adapter.sendTurn({ + threadId: session.threadId, + input: "hello", + modelSelection: { + provider: "claudeAgent", + model: "claude-opus-4-6", + options: { + contextWindow: "1m", + }, + }, + attachments: [], + }); + yield* adapter.sendTurn({ + threadId: session.threadId, + input: "hello again", + modelSelection: { + provider: "claudeAgent", + model: "claude-opus-4-6", + }, + attachments: [], + }); + + assert.deepEqual(harness.query.setModelCalls, ["claude-opus-4-6[1m]", "claude-opus-4-6"]); + }).pipe( + Effect.provideService(Random.Random, makeDeterministicRandomService()), + Effect.provide(harness.layer), + ); + }); + it.effect("sets plan permission mode on sendTurn when interactionMode is plan", () => { const harness = makeHarness(); return Effect.gen(function* () { diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.ts b/apps/server/src/provider/Layers/ClaudeAdapter.ts index e7602ea5c4..5e4e98747a 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.ts @@ -148,6 +148,7 @@ interface ClaudeSessionContext { streamFiber: Fiber.Fiber | undefined; readonly startedAt: string; readonly basePermissionMode: PermissionMode | undefined; + currentApiModelId: string | undefined; resumeSessionId: string | undefined; readonly pendingApprovals: Map; readonly pendingUserInputs: Map; @@ -2809,6 +2810,7 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { streamFiber: undefined, startedAt, basePermissionMode: permissionMode, + currentApiModelId: apiModelId, resumeSessionId: sessionId, pendingApprovals, pendingUserInputs, @@ -2898,10 +2900,17 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { if (modelSelection?.model) { const apiModelId = resolveApiModelId(modelSelection); - yield* Effect.tryPromise({ - try: () => context.query.setModel(apiModelId), - catch: (cause) => toRequestError(input.threadId, "turn/setModel", cause), - }); + if (context.currentApiModelId !== apiModelId) { + yield* Effect.tryPromise({ + try: () => context.query.setModel(apiModelId), + catch: (cause) => toRequestError(input.threadId, "turn/setModel", cause), + }); + context.currentApiModelId = apiModelId; + } + context.session = { + ...context.session, + model: modelSelection.model, + }; } // Apply interaction mode by switching the SDK's permission mode.