From 115ba41b941e88e0423c050cee905e32e6e8d46c Mon Sep 17 00:00:00 2001 From: Renfei Zhou Date: Mon, 2 Nov 2026 04:05:38 +0000 Subject: [PATCH 1/2] fix(fork): resolve upstream merge 91f66f88de ClaudeAdapterV2.ts: upstream #27320 closes withdrawn approval and question cards with its own cancelWithdrawnRequest, the same fix the fork carried as cancelRuntimeRequest. Take upstream's helper at both call sites and drop the fork's copy and its duplicate adapter test; upstream's test covers both the approval and the question case. The fork's EventSink and ingestor guards stay. Co-Authored-By: Claude Opus 5.5 --- .../Adapters/ClaudeAdapterV2.test.ts | 163 ------------------ .../Adapters/ClaudeAdapterV2.ts | 31 ---- 2 files changed, 194 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index 71a178f3ffd0e..1b1161f74ff04 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -1223,169 +1223,6 @@ describe("ClaudeAdapterV2 approval cancellation", () => { assert.deepEqual(result, { decision: "cancel" }); }), ); - - it.effect.each([ - { name: "command approval", toolName: "Bash", toolInput: { command: "rm -rf build" } }, - { - name: "user question", - toolName: "AskUserQuestion", - toolInput: { - questions: [ - { - header: "Target", - question: "Deploy where?", - options: [ - { label: "Production", description: "Deploy to production." }, - { label: "Staging", description: "Deploy to staging." }, - ], - multiSelect: false, - }, - ], - }, - }, - ])("closes the $name when the SDK aborts it", ({ toolName, toolInput }) => - Effect.scoped( - Effect.gen(function* () { - const fileSystem = yield* FileSystem.FileSystem; - let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; - const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ - instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, - settings: DEFAULT_CLAUDE_SETTINGS, - environment: {}, - attachmentsDir: yield* fileSystem.makeTempDirectoryScoped({ - prefix: "t3-claude-aborted-request-", - }), - fileSystem, - path: yield* Path.Path, - crypto: yield* Crypto.Crypto, - idAllocator: yield* IdAllocator.IdAllocatorV2, - queryRunner: { - allocateSessionId: Effect.succeed("native-thread-claude-aborted-request"), - open: (input) => - Effect.sync(() => { - openedOptions = input.options; - return { - messages: Stream.never, - offer: () => Effect.void, - setModel: () => Effect.void, - applyFlagSettings: () => Effect.void, - setPermissionMode: () => Effect.void, - backgroundTasks: () => Effect.succeed(false), - stopTask: () => Effect.void, - interrupt: Effect.void, - close: Effect.void, - }; - }), - forkSession: () => Effect.die("unused"), - subagentLaunchToolUseId: () => Effect.succeed(null), - assertComplete: Effect.void, - }, - }); - const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ - runtimeMode: "approval-required", - interactionMode: "default", - cwd: "/workspace", - }); - const threadId = ThreadId.make("thread-claude-aborted-request"); - const runtime = yield* adapter.openSession({ - threadId, - providerSessionId: ProviderSessionId.make("provider-session-claude-aborted-request"), - modelSelection: CLAUDE_TEST_MODEL_SELECTION, - runtimePolicy, - }); - const providerThread = yield* runtime.ensureThread({ - threadId, - modelSelection: CLAUDE_TEST_MODEL_SELECTION, - runtimePolicy, - }); - yield* runtime.startTurn( - makeClaudeTestTurnInput({ - threadId, - providerThread, - now: yield* DateTime.now, - attemptId: RunAttemptId.make("attempt-claude-aborted-request"), - text: "Clean the build.", - attachments: [], - runtimePolicy, - }), - ); - const canUseTool = openedOptions?.canUseTool; - assert.isFunction(canUseTool); - - const opened = yield* runtime.events.pipe( - Stream.filter((event) => event.type === "runtime_request.updated"), - Stream.runHead, - Effect.forkScoped, - ); - // A PermissionRequest hook that answers first makes the SDK abort this callback. - // @effect-diagnostics-next-line abortControllerInEffect:off - The test plays the SDK and aborts the callback itself. - const controller = new AbortController(); - const result = yield* Effect.promise(() => - canUseTool!(toolName, toolInput, { - signal: controller.signal, - toolUseID: "tool-aborted-request", - requestId: "request-aborted-request", - }), - ).pipe(Effect.forkScoped); - const openedEvent = yield* Fiber.join(opened); - if (Option.isNone(openedEvent) || openedEvent.value.type !== "runtime_request.updated") { - return assert.fail("the request never opened"); - } - const request = openedEvent.value.runtimeRequest; - assert.equal(request.status, "pending"); - - controller.abort(); - assert.equal((yield* Fiber.join(result))?.behavior, "deny"); - // The event queue never ends, so a later plan marks where the abort's updates must be. - const marker = yield* Effect.promise(() => - canUseTool!( - "ExitPlanMode", - { plan: "# Marker" }, - { - signal: new AbortController().signal, - toolUseID: "tool-marker", - requestId: "request-marker", - }, - ), - ); - assert.equal(marker?.behavior, "deny"); - const afterAbort = yield* runtime.events.pipe( - Stream.takeUntil((event) => event.type === "plan.updated"), - Stream.runCollect, - ); - - const closedRequest = afterAbort.findLast( - (event) => - event.type === "runtime_request.updated" && event.runtimeRequest.id === request.id, - ); - assert.deepInclude( - closedRequest?.type === "runtime_request.updated" ? closedRequest.runtimeRequest : {}, - { status: "cancelled" }, - ); - const closedNode = afterAbort.findLast( - (event) => event.type === "node.updated" && event.node.id === request.nodeId, - ); - assert.equal( - closedNode?.type === "node.updated" ? closedNode.node.status : null, - "cancelled", - ); - const closedItem = afterAbort.findLast( - (event) => - event.type === "turn_item.updated" && - "requestId" in event.turnItem && - event.turnItem.requestId === request.id, - ); - assert.equal( - closedItem?.type === "turn_item.updated" ? closedItem.turnItem.status : null, - "cancelled", - ); - }).pipe( - Effect.provide( - Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), - ), - ), - ), - ); }); describe("ClaudeAdapterV2 MCP elicitation acceptance", () => { diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 79324ca76fa03..a1ca4f8570d3c 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -9711,29 +9711,6 @@ export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* ( yield* handleRoutedSdkMessage(input); }); - // No user response will close a request the SDK aborted, such as one a hook answered. - const cancelRuntimeRequest = Effect.fn("ClaudeAdapterV2.cancelRuntimeRequest")(function* ( - artifacts: Effect.Success>, - ) { - const now = yield* DateTime.now; - const cancelled = { status: "cancelled", completedAt: now } as const; - yield* emitProviderEvent({ - type: "node.updated", - driver: CLAUDE_PROVIDER, - node: { ...artifacts.node, ...cancelled }, - }); - yield* emitProviderEvent({ - type: "runtime_request.updated", - driver: CLAUDE_PROVIDER, - runtimeRequest: { ...artifacts.request, status: "cancelled", resolvedAt: now }, - }); - yield* emitProviderEvent({ - type: "turn_item.updated", - driver: CLAUDE_PROVIDER, - turnItem: { ...artifacts.turnItem, ...cancelled, updatedAt: now }, - }); - }); - const canUseToolEffect = Effect.fn("ClaudeAdapterV2.canUseTool")(function* ( toolName: Parameters[0], toolInput: Parameters[1], @@ -9844,11 +9821,7 @@ export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* ( }), ), ); -<<<<<<< origin/main - if (!(yield* Deferred.isDone(answers))) yield* cancelRuntimeRequest(artifacts); -======= if (!(yield* Deferred.isDone(answers))) yield* cancelWithdrawnRequest(artifacts); ->>>>>>> upstream/main return callbackOptions.signal.aborted ? ({ behavior: "deny", @@ -9989,11 +9962,7 @@ export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* ( }), ), ); -<<<<<<< origin/main - if (!(yield* Deferred.isDone(decision))) yield* cancelRuntimeRequest(artifacts); -======= if (!(yield* Deferred.isDone(decision))) yield* cancelWithdrawnRequest(artifacts); ->>>>>>> upstream/main return permissionResultFromDecision({ toolName, From 51c3efbe289685c2fee7dc3089d8816a19c0b580 Mon Sep 17 00:00:00 2001 From: Renfei Zhou Date: Mon, 2 Nov 2026 04:06:27 +0000 Subject: [PATCH 2/2] test(fork): pass profileDirectory in upstream's new Antigravity adapter test The fork makes profileDirectory a required Antigravity adapter option. Co-Authored-By: Claude Opus 5.5 --- .../src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts index 4d5d57c549773..6d5624cfe31c0 100644 --- a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts @@ -1100,6 +1100,7 @@ describe("AntigravityAdapterV2 runtime mode ceiling", () => { const instanceId = ProviderInstanceId.make("antigravity-ceiling-test"); const adapter = yield* makeAntigravityAdapterV2({ instanceId, + profileDirectory: unusedProfileDirectory, selfInvocation: yield* resolveSelfInvocation(), makeRuntime: (input) => makeAntigravityAcpRuntime({