Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 63 additions & 3 deletions apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2621,6 +2621,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
readonly close?: (sdkMessages: Queue.Queue<SDKMessage>) => Effect.Effect<void>;
readonly interrupt?: Effect.Effect<void>;
readonly setPermissionMode?: ClaudeAdapterV2.ClaudeAgentSdkQuerySession["setPermissionMode"];
readonly applyFlagSettings?: ClaudeAdapterV2.ClaudeAgentSdkQuerySession["applyFlagSettings"];
readonly stopTask?: (
taskId: string,
) => Effect.Effect<void, ClaudeAdapterV2.ClaudeAgentSdkQueryRunnerError>;
Expand Down Expand Up @@ -2768,7 +2769,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
applyFlagSettings: (settings) =>
Effect.sync(() => {
liveSelectionChanges.push(settings);
}),
}).pipe(
Effect.andThen(() => options?.applyFlagSettings?.(settings) ?? Effect.void),
),
setPermissionMode: (mode) =>
Effect.sync(() => {
permissionModeChanges.push(mode);
Expand Down Expand Up @@ -4267,7 +4270,6 @@ describe("ClaudeAdapterV2 background wake turns", () => {
yield* harness.runtime.startTurn(turn(4, { ...normal, model: "claude-sonnet-4-6" }));
yield* settle;
assert.deepEqual(harness.liveSelectionChanges, [
{ model: "claude-opus-5-5" },
{ effortLevel: "xhigh", fastMode: true, ultracode: true },
{ model: "claude-sonnet-4-6" },
{ effortLevel: "high", fastMode: null, ultracode: null },
Expand All @@ -4292,7 +4294,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
}
assert.strictEqual(harness.getOpenedOptions(), originalOptions);
assert.lengthOf(harness.offeredMessages, 4);
assert.lengthOf(harness.liveSelectionChanges, 4);
assert.lengthOf(harness.liveSelectionChanges, 3);
assert.isTrue(yield* harness.hasPendingBackgroundWork);
}).pipe(
Effect.provide(
Expand All @@ -4302,6 +4304,64 @@ describe("ClaudeAdapterV2 background wake turns", () => {
),
);

it.effect("re-sends the model on the turn after a live selection change fails", () =>
Effect.scoped(
Effect.gen(function* () {
let failFlags = true;
const harness = yield* makeWakeHarnessWithOptions({
applyFlagSettings: () =>
Effect.suspend(() => {
if (!failFlags) return Effect.void;
failFlags = false;
return Effect.fail(
new ClaudeAdapterV2.ClaudeAgentSdkQueryRunnerError({
method: "applyFlagSettings",
cause: new Error("control request failed"),
}),
);
}),
});
const now = yield* DateTime.now;
const opus = {
instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
model: "claude-opus-5-5",
} satisfies ModelSelection;
const turn = (ordinal: number, modelSelection: ModelSelection) =>
makeClaudeTestTurnInput({
threadId: harness.threadId,
providerThread: harness.providerThread,
now,
attemptId: RunAttemptId.make(`attempt-failed-live-change:${ordinal}`),
text: `Request ${ordinal}`,
attachments: [],
providerTurnOrdinal: ordinal,
modelSelection,
});
yield* harness.runtime.startTurn(turn(1, opus));
yield* harness.offerAndWait(turnOneResult);
yield* Queue.take(harness.terminalReceipts);

const failed = yield* harness.runtime
.startTurn(turn(2, { ...opus, model: "claude-sonnet-4-6" }))
.pipe(Effect.result);
assert.equal(failed._tag, "Failure");

// The process may now run Sonnet, so returning to Opus must send it.
yield* harness.runtime.startTurn(turn(3, opus));
assert.deepEqual(harness.liveSelectionChanges, [
{ model: "claude-sonnet-4-6" },
{ effortLevel: "high", fastMode: null, ultracode: null },
{ model: "claude-opus-5-5" },
{ effortLevel: "medium", fastMode: false, ultracode: null },
]);
}).pipe(
Effect.provide(
Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer),
),
),
),
);

it.effect.each(["completed", "interrupted"] as const)(
"projects Claude thinking blocks when %s",
(status) =>
Expand Down
8 changes: 7 additions & 1 deletion apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9659,7 +9659,13 @@ export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* (
existing.openedPermissionMode === queryPolicy.permissionMode)
) {
const live = existing;
yield* live.query.setModel(compiledSelection.apiModelId).pipe(
// Preserve launcher-selected models unless the model changed or a failed update left it unknown.
yield* (
live.selection.queryIdentity === "" ||
live.selection.apiModelId !== compiledSelection.apiModelId
? live.query.setModel(compiledSelection.apiModelId)
: Effect.void
).pipe(
Effect.andThen(
live.query.applyFlagSettings(claudeLiveSelectionFlagSettings(compiledSelection)),
),
Expand Down
Loading