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
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
163 changes: 0 additions & 163 deletions apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
31 changes: 0 additions & 31 deletions apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ReturnType<typeof buildApprovalRequestArtifacts>>,
) {
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<CanUseTool>[0],
toolInput: Parameters<CanUseTool>[1],
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down
Loading