| author | |
| committer | |
| log | fb7260792fbbb84252213541a76dd61d52243bdf |
| tree | b569eb68b58f012b2cef951225ec7a6b11380751 |
| parent | d8a485ea7e93f4e498be3314c0094a3af675938f |
| signature | Signed by PGP key B5690EEEBB952194 |
2 files changed, 92 insertions(+), 4 deletions(-)
apps/server/src/provider/Layers/ClaudeAdapter.test.ts+79| ... | @@ -2384,6 +2384,85 @@ describe("ClaudeAdapterLive", () => { | ... | @@ -2384,6 +2384,85 @@ describe("ClaudeAdapterLive", () => { |
| 2384 | ); | 2384 | ); |
| 2385 | }); | 2385 | }); |
| 2386 | 2386 | ||
| 2387 | it.effect( | ||
| 2388 | "does not re-set the Claude model when the session already uses the same effective API model", | ||
| 2389 | () => { | ||
| 2390 | const harness = makeHarness(); | ||
| 2391 | return Effect.gen(function* () { | ||
| 2392 | const adapter = yield* ClaudeAdapter; | ||
| 2393 | const modelSelection = { | ||
| 2394 | provider: "claudeAgent" as const, | ||
| 2395 | model: "claude-opus-4-6", | ||
| 2396 | }; | ||
| 2397 | |||
| 2398 | const session = yield* adapter.startSession({ | ||
| 2399 | threadId: THREAD_ID, | ||
| 2400 | provider: "claudeAgent", | ||
| 2401 | modelSelection, | ||
| 2402 | runtimeMode: "full-access", | ||
| 2403 | }); | ||
| 2404 | |||
| 2405 | yield* adapter.sendTurn({ | ||
| 2406 | threadId: session.threadId, | ||
| 2407 | input: "hello", | ||
| 2408 | modelSelection, | ||
| 2409 | attachments: [], | ||
| 2410 | }); | ||
| 2411 | yield* adapter.sendTurn({ | ||
| 2412 | threadId: session.threadId, | ||
| 2413 | input: "hello again", | ||
| 2414 | modelSelection, | ||
| 2415 | attachments: [], | ||
| 2416 | }); | ||
| 2417 | |||
| 2418 | assert.deepEqual(harness.query.setModelCalls, []); | ||
| 2419 | }).pipe( | ||
| 2420 | Effect.provideService(Random.Random, makeDeterministicRandomService()), | ||
| 2421 | Effect.provide(harness.layer), | ||
| 2422 | ); | ||
| 2423 | }, | ||
| 2424 | ); | ||
| 2425 | |||
| 2426 | it.effect("re-sets the Claude model when the effective API model changes", () => { | ||
| 2427 | const harness = makeHarness(); | ||
| 2428 | return Effect.gen(function* () { | ||
| 2429 | const adapter = yield* ClaudeAdapter; | ||
| 2430 | |||
| 2431 | const session = yield* adapter.startSession({ | ||
| 2432 | threadId: THREAD_ID, | ||
| 2433 | provider: "claudeAgent", | ||
| 2434 | runtimeMode: "full-access", | ||
| 2435 | }); | ||
| 2436 | |||
| 2437 | yield* adapter.sendTurn({ | ||
| 2438 | threadId: session.threadId, | ||
| 2439 | input: "hello", | ||
| 2440 | modelSelection: { | ||
| 2441 | provider: "claudeAgent", | ||
| 2442 | model: "claude-opus-4-6", | ||
| 2443 | options: { | ||
| 2444 | contextWindow: "1m", | ||
| 2445 | }, | ||
| 2446 | }, | ||
| 2447 | attachments: [], | ||
| 2448 | }); | ||
| 2449 | yield* adapter.sendTurn({ | ||
| 2450 | threadId: session.threadId, | ||
| 2451 | input: "hello again", | ||
| 2452 | modelSelection: { | ||
| 2453 | provider: "claudeAgent", | ||
| 2454 | model: "claude-opus-4-6", | ||
| 2455 | }, | ||
| 2456 | attachments: [], | ||
| 2457 | }); | ||
| 2458 | |||
| 2459 | assert.deepEqual(harness.query.setModelCalls, ["claude-opus-4-6[1m]", "claude-opus-4-6"]); | ||
| 2460 | }).pipe( | ||
| 2461 | Effect.provideService(Random.Random, makeDeterministicRandomService()), | ||
| 2462 | Effect.provide(harness.layer), | ||
| 2463 | ); | ||
| 2464 | }); | ||
| 2465 | |||
| 2387 | it.effect("sets plan permission mode on sendTurn when interactionMode is plan", () => { | 2466 | it.effect("sets plan permission mode on sendTurn when interactionMode is plan", () => { |
| 2388 | const harness = makeHarness(); | 2467 | const harness = makeHarness(); |
| 2389 | return Effect.gen(function* () { | 2468 | return Effect.gen(function* () { |
apps/server/src/provider/Layers/ClaudeAdapter.ts+13-4| ... | @@ -148,6 +148,7 @@ interface ClaudeSessionContext { | ... | @@ -148,6 +148,7 @@ interface ClaudeSessionContext { |
| 148 | streamFiber: Fiber.Fiber<void, Error> | undefined; | 148 | streamFiber: Fiber.Fiber<void, Error> | undefined; |
| 149 | readonly startedAt: string; | 149 | readonly startedAt: string; |
| 150 | readonly basePermissionMode: PermissionMode | undefined; | 150 | readonly basePermissionMode: PermissionMode | undefined; |
| 151 | currentApiModelId: string | undefined; | ||
| 151 | resumeSessionId: string | undefined; | 152 | resumeSessionId: string | undefined; |
| 152 | readonly pendingApprovals: Map<ApprovalRequestId, PendingApproval>; | 153 | readonly pendingApprovals: Map<ApprovalRequestId, PendingApproval>; |
| 153 | readonly pendingUserInputs: Map<ApprovalRequestId, PendingUserInput>; | 154 | readonly pendingUserInputs: Map<ApprovalRequestId, PendingUserInput>; |
| ... | @@ -2809,6 +2810,7 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { | ... | @@ -2809,6 +2810,7 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { |
| 2809 | streamFiber: undefined, | 2810 | streamFiber: undefined, |
| 2810 | startedAt, | 2811 | startedAt, |
| 2811 | basePermissionMode: permissionMode, | 2812 | basePermissionMode: permissionMode, |
| 2813 | currentApiModelId: apiModelId, | ||
| 2812 | resumeSessionId: sessionId, | 2814 | resumeSessionId: sessionId, |
| 2813 | pendingApprovals, | 2815 | pendingApprovals, |
| 2814 | pendingUserInputs, | 2816 | pendingUserInputs, |
| ... | @@ -2898,10 +2900,17 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { | ... | @@ -2898,10 +2900,17 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { |
| 2898 | 2900 | ||
| 2899 | if (modelSelection?.model) { | 2901 | if (modelSelection?.model) { |
| 2900 | const apiModelId = resolveApiModelId(modelSelection); | 2902 | const apiModelId = resolveApiModelId(modelSelection); |
| 2901 | yield* Effect.tryPromise({ | 2903 | if (context.currentApiModelId !== apiModelId) { |
| 2902 | try: () => context.query.setModel(apiModelId), | 2904 | yield* Effect.tryPromise({ |
| 2903 | catch: (cause) => toRequestError(input.threadId, "turn/setModel", cause), | 2905 | try: () => context.query.setModel(apiModelId), |
| 2904 | }); | 2906 | catch: (cause) => toRequestError(input.threadId, "turn/setModel", cause), |
| 2907 | }); | ||
| 2908 | context.currentApiModelId = apiModelId; | ||
| 2909 | } | ||
| 2910 | context.session = { | ||
| 2911 | ...context.session, | ||
| 2912 | model: modelSelection.model, | ||
| 2913 | }; | ||
| 2905 | } | 2914 | } |
| 2906 | 2915 | ||
| 2907 | // Apply interaction mode by switching the SDK's permission mode. | 2916 | // Apply interaction mode by switching the SDK's permission mode. |