From 4fafe8854a7ab3fd4fb40b6427975155a1ca0a05 Mon Sep 17 00:00:00 2001 From: Andrey Bragin Date: Tue, 13 Oct 2026 15:11:34 +0200 Subject: [PATCH 1/4] test: stabilize e2e approval tests Switch the default e2e model to gpt-6-luna: gpt-5.2 is no longer in the Codex catalog, so it ran on fallback metadata without the apply_patch tool and edited files through the shell instead. Ask for escalated permissions up front in the shell approval prompts so the model does not run the command sandboxed and skip the approval. --- .../CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts | 7 +++++-- src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts b/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts index d3d59f4ca..013bfc3df 100644 --- a/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts +++ b/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts @@ -42,9 +42,12 @@ describeE2E("E2E shell approval tests", () => { async function promptShellCommandTwice( expectedStopReasons: ["end_turn" | "cancelled", "end_turn" | "cancelled"], ): Promise { + // Without the escalation hint the model may run the command sandboxed, hit the + // read-only filesystem and end the turn without ever requesting approval. + const escalate = "It writes outside the sandbox, so request escalated permissions on the first attempt."; const prompts = [ - `Use your shell tool to run exactly \`${command}\`.`, - `Use your shell tool to run exactly the same command again: \`${command}\`.`, + `Use your shell tool to run exactly \`${command}\`. ${escalate}`, + `Use your shell tool to run exactly the same command again: \`${command}\`. ${escalate}`, ]; for (const [index, text] of prompts.entries()) { const response = await fixture.connection.prompt({ diff --git a/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts b/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts index 42b947eb3..8bcf676ad 100644 --- a/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts +++ b/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts @@ -9,7 +9,7 @@ import {removeDirectoryWithRetry, writeCodexHomeConfig} from "../../acp-test-uti import type {PermissionResponder} from "./permission-responders"; import type {LegacyNewSessionResponse} from "../../../AcpExtensions"; -export const DEFAULT_TEST_MODEL_ID = ModelId.create("gpt-5.2", "none"); +export const DEFAULT_TEST_MODEL_ID = ModelId.create("gpt-6-luna", "low"); export const OTHER_TEST_MODEL_ID = ModelId.create("gpt-5.5", "low"); export interface TestSkill { From c08de9514df509f65f7ad598c2e1cbcadaff923a Mon Sep 17 00:00:00 2001 From: Andrey Bragin Date: Tue, 13 Oct 2026 15:18:34 +0200 Subject: [PATCH 2/4] test: harden read-only shell prompts and shell cancellation e2e Reuse the escalation hint for read-only shell tests that expect an approval, and wait for the execute tool call before cancelling so the interrupt does not race Codex registering the process. --- .../e2e/acp-e2e-shell-approval.test.ts | 23 +++++++++++-------- .../e2e/spawned-agent-fixture.ts | 18 +++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts b/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts index 013bfc3df..6de6de885 100644 --- a/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts +++ b/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts @@ -18,6 +18,9 @@ import { const FIRST_FILE_NAME = "approval-first.txt"; const SECOND_FILE_NAME = "approval-second.txt"; +// Without this hint the model may run the command sandboxed, hit the read-only +// filesystem and end the turn without ever requesting approval. +const ESCALATE = "It writes outside the sandbox, so request escalated permissions on the first attempt."; describeE2E("E2E shell approval tests", () => { let fixture: SpawnedAgentFixture; @@ -42,12 +45,9 @@ describeE2E("E2E shell approval tests", () => { async function promptShellCommandTwice( expectedStopReasons: ["end_turn" | "cancelled", "end_turn" | "cancelled"], ): Promise { - // Without the escalation hint the model may run the command sandboxed, hit the - // read-only filesystem and end the turn without ever requesting approval. - const escalate = "It writes outside the sandbox, so request escalated permissions on the first attempt."; const prompts = [ - `Use your shell tool to run exactly \`${command}\`. ${escalate}`, - `Use your shell tool to run exactly the same command again: \`${command}\`. ${escalate}`, + `Use your shell tool to run exactly \`${command}\`. ${ESCALATE}`, + `Use your shell tool to run exactly the same command again: \`${command}\`. ${ESCALATE}`, ]; for (const [index, text] of prompts.entries()) { const response = await fixture.connection.prompt({ @@ -108,7 +108,7 @@ describeE2E("E2E read-only mode shell permission tests", () => { it("requests permission for a command that writes inside the workspace", async () => { fixture.setPermissionResponder(createPermissionResponder("execute", ApprovalOptionId.AllowOnce)); - const sessionId = await writeToFile(fixture, path.join(fixture.workspaceDir, generateFileNameForTest())); + const sessionId = await writeToFile(fixture, path.join(fixture.workspaceDir, generateFileNameForTest()), ESCALATE); expect(fixture.readPermissionRequests(sessionId, "execute").length).toBeGreaterThanOrEqual(1); expect(fixture.readPermissionRequests(sessionId, "edit")).toHaveLength(0); @@ -144,7 +144,7 @@ describeE2E("E2E read-only mode shell permission tests", () => { it("requests permission for a command that writes outside the workspace", async () => { const dir = createDirOutsideWorkspace(fixture); fixture.setPermissionResponder(createPermissionResponder("execute", ApprovalOptionId.AllowOnce)); - const sessionId = await writeToFile(fixture, path.join(dir, generateFileNameForTest())); + const sessionId = await writeToFile(fixture, path.join(dir, generateFileNameForTest()), ESCALATE); expectPermissionRequests(fixture, sessionId, {execute: 1, edit: 0}); }); }); @@ -219,6 +219,11 @@ describeE2E("E2E shell cancellation tests", () => { return parsed; }, {timeout: 10_000}); expect(isProcessRunning(pid)).toBe(true); + // The process can start before Codex tracks it in the turn; an interrupt in that + // window leaves it running. A client only cancels what it has seen, so wait for it. + await vi.waitFor(() => { + expect(fixture?.readToolCalls(sessionId, "execute")).not.toHaveLength(0); + }, {timeout: 10_000}); await fixture.connection.cancel({sessionId}); expect((await promptResponse).stopReason).toBe("cancelled"); @@ -228,7 +233,7 @@ describeE2E("E2E shell cancellation tests", () => { }); }); -async function writeToFile(fixture: SpawnedAgentFixture, filePath: string): Promise { +async function writeToFile(fixture: SpawnedAgentFixture, filePath: string, hint?: string): Promise { const content = "hello from e2e"; const command = `printf '${content}' > '${filePath}'`; const sessionId = (await fixture.createSession()).sessionId; @@ -236,7 +241,7 @@ async function writeToFile(fixture: SpawnedAgentFixture, filePath: string): Prom sessionId, prompt: [{ type: "text", - text: `Use your shell tool to run exactly \`${command}\`. Do not modify files any other way.`, + text: `Use your shell tool to run exactly \`${command}\`. Do not modify files any other way.${hint ? ` ${hint}` : ""}`, }], }); expectEndTurn(response); diff --git a/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts b/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts index 8bcf676ad..df40312b2 100644 --- a/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts +++ b/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts @@ -38,6 +38,7 @@ export interface SpawnedAgentFixture { sessionId: string, toolCallKind: acp.ToolKind, ): acp.RequestPermissionRequest[]; + readToolCalls(sessionId: string, toolCallKind: acp.ToolKind): acp.ToolCall[]; dispose(): Promise; } @@ -107,6 +108,7 @@ class RecordingClient implements acp.Client { private readonly textBySessionId = new Map(); private readonly availableCommandsBySessionId = new Map(); private readonly permissionRequestsBySessionId = new Map(); + private readonly toolCallsBySessionId = new Map(); private permissionResponder: PermissionResponder = () => ({ outcome: {outcome: "cancelled"}, }); @@ -131,6 +133,13 @@ class RecordingClient implements acp.Client { return; } + if (params.update.sessionUpdate === "tool_call") { + const toolCalls = this.toolCallsBySessionId.get(params.sessionId) ?? []; + toolCalls.push(params.update); + this.toolCallsBySessionId.set(params.sessionId, toolCalls); + return; + } + if (params.update.sessionUpdate !== "agent_message_chunk" || params.update.content.type !== "text") { return; } @@ -154,6 +163,11 @@ class RecordingClient implements acp.Client { const requests = this.permissionRequestsBySessionId.get(sessionId) ?? []; return requests.filter((request) => request.toolCall.kind === toolCallKind); } + + readToolCalls(sessionId: string, toolCallKind: acp.ToolKind): acp.ToolCall[] { + const toolCalls = this.toolCallsBySessionId.get(sessionId) ?? []; + return toolCalls.filter((toolCall) => toolCall.kind === toolCallKind); + } } class SpawnedAgentFixtureImpl implements SpawnedAgentFixture { @@ -261,6 +275,10 @@ class SpawnedAgentFixtureImpl implements SpawnedAgentFixture { return this.client.readPermissionRequests(sessionId, toolCallKind); } + readToolCalls(sessionId: string, toolCallKind: acp.ToolKind): acp.ToolCall[] { + return this.client.readToolCalls(sessionId, toolCallKind); + } + async dispose(): Promise { if (this.disposed) { return; From 33c7db064ac5596d022e544315ccdf4d988f9519 Mon Sep 17 00:00:00 2001 From: Andrey Bragin Date: Tue, 13 Oct 2026 15:28:50 +0200 Subject: [PATCH 3/4] test: stop a cancelled shell command through the async task e2e Codex keeps a running unified exec command as a background terminal when the turn is interrupted. Assert that cancel ends the turn, then stop the command with _session/async_task/stop and check it exits. --- .../e2e/acp-e2e-shell-approval.test.ts | 28 +++++++++++++++---- .../CodexACPAgent/e2e/acp-e2e-test-utils.ts | 14 +++++++--- .../e2e/spawned-agent-fixture.ts | 20 +++++++++++++ 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts b/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts index 6de6de885..344593159 100644 --- a/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts +++ b/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import {afterEach, beforeEach, expect, it, onTestFinished, vi} from "vitest"; import {AgentMode} from "../../../AgentMode"; +import {ASYNC_TASK_STOP_METHOD} from "../../../async-tasks/AsyncTaskExtension"; import {ApprovalOptionId} from "../../../permissions/option-ids"; import { createAuthenticatedFixture, @@ -201,8 +202,10 @@ describeE2E("E2E shell cancellation tests", () => { } } - it("cancels a running shell command", async () => { - fixture = await createAuthenticatedFixture(AgentMode.AgentFullAccess); + // Codex keeps a running command as a background terminal when the turn is interrupted, + // so cancel ends the turn and the async task stop request ends the process. + it("cancels the turn of a running shell command and stops it as an async task", async () => { + fixture = await createAuthenticatedFixture(AgentMode.AgentFullAccess, undefined, ["asyncTasks"]); const sessionId = (await fixture.createSession()).sessionId; const pidFilePath = path.join(fixture.workspaceDir, "cancel-command.pid"); const command = `/bin/sh -c 'echo $$ > "${pidFilePath}"; exec sleep 100'`; @@ -218,15 +221,28 @@ describeE2E("E2E shell cancellation tests", () => { expect(parsed).toBeGreaterThan(0); return parsed; }, {timeout: 10_000}); + onTestFinished(() => { + if (isProcessRunning(pid)) process.kill(pid, "SIGKILL"); + }); expect(isProcessRunning(pid)).toBe(true); - // The process can start before Codex tracks it in the turn; an interrupt in that - // window leaves it running. A client only cancels what it has seen, so wait for it. - await vi.waitFor(() => { - expect(fixture?.readToolCalls(sessionId, "execute")).not.toHaveLength(0); + // The process can start before Codex tracks it in the turn, so wait until the client sees it. + const toolCallId = await vi.waitFor(() => { + const [toolCall] = fixture?.readToolCalls(sessionId, "execute") ?? []; + expect(toolCall).toBeDefined(); + return toolCall!.toolCallId; }, {timeout: 10_000}); await fixture.connection.cancel({sessionId}); expect((await promptResponse).stopReason).toBe("cancelled"); + const asyncTaskId = await vi.waitFor(() => { + const task = fixture?.readAsyncTasks(sessionId).find(task => task.toolCallId === toolCallId); + expect(task).toBeDefined(); + return task!.asyncTaskId; + }, {timeout: 10_000}); + expect(isProcessRunning(pid)).toBe(true); + + await expect(fixture.connection.extMethod(ASYNC_TASK_STOP_METHOD, {sessionId, asyncTaskId})) + .resolves.toEqual({stopped: true}); await vi.waitFor(() => { expect(isProcessRunning(pid)).toBe(false); }, {timeout: 5_000}); diff --git a/src/__tests__/CodexACPAgent/e2e/acp-e2e-test-utils.ts b/src/__tests__/CodexACPAgent/e2e/acp-e2e-test-utils.ts index 35bb5f3a3..caf256962 100644 --- a/src/__tests__/CodexACPAgent/e2e/acp-e2e-test-utils.ts +++ b/src/__tests__/CodexACPAgent/e2e/acp-e2e-test-utils.ts @@ -47,7 +47,11 @@ export function expectNoPermissionRequests(fixture: SpawnedAgentFixture, session expectPermissionRequests(fixture, sessionId, { edit: 0, execute: 0 }); } -export async function createAuthenticatedFixture(initialMode?: AgentMode, mcpServers?: acp.McpServerStdio[]): Promise { +export async function createAuthenticatedFixture( + initialMode?: AgentMode, + mcpServers?: acp.McpServerStdio[], + airCapabilities?: string[], +): Promise { const apiKey = requireLiveApiKey(); const extraEnv = initialMode ? {INITIAL_AGENT_MODE: initialMode.id} : undefined; return await createSpawnedFixture(async (connection, authMethods) => { @@ -68,7 +72,7 @@ export async function createAuthenticatedFixture(initialMode?: AgentMode, mcpSer if (authenticationStatus["type"] !== "api-key") { throw new Error(`Unexpected authentication status: ${JSON.stringify(authenticationStatus)}`); } - }, extraEnv, mcpServers); + }, extraEnv, mcpServers, airCapabilities); } export async function createGatewayFixture( @@ -97,7 +101,7 @@ export async function createGatewayFixture( }); } -function buildClientCapabilities(): acp.ClientCapabilities { +function buildClientCapabilities(airCapabilities?: string[]): acp.ClientCapabilities { return { fs: { readTextFile: true, @@ -111,6 +115,7 @@ function buildClientCapabilities(): acp.ClientCapabilities { }, _meta: { "terminal-auth": true, + ...(airCapabilities ? {jetbrains: {air: {version: 1, capabilities: airCapabilities}}} : {}), }, }; } @@ -121,11 +126,12 @@ async function createSpawnedFixture( authenticate: Authenticator, extraEnv?: NodeJS.ProcessEnv, mcpServers?: acp.McpServerStdio[], + airCapabilities?: string[], ): Promise { return await createSpawnedAgentFixture(async (connection) => { const initializeResponse = await connection.initialize({ protocolVersion: acp.PROTOCOL_VERSION, - clientCapabilities: buildClientCapabilities(), + clientCapabilities: buildClientCapabilities(airCapabilities), clientInfo: { name: "vitest", version: "1.0.0", diff --git a/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts b/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts index df40312b2..d49edf97a 100644 --- a/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts +++ b/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts @@ -8,6 +8,7 @@ import {ModelId} from "../../../ModelId"; import {removeDirectoryWithRetry, writeCodexHomeConfig} from "../../acp-test-utils"; import type {PermissionResponder} from "./permission-responders"; import type {LegacyNewSessionResponse} from "../../../AcpExtensions"; +import type {AsyncTaskSpawnedUpdate} from "../../../async-tasks/AcpAsyncTasks"; export const DEFAULT_TEST_MODEL_ID = ModelId.create("gpt-6-luna", "low"); export const OTHER_TEST_MODEL_ID = ModelId.create("gpt-5.5", "low"); @@ -39,6 +40,7 @@ export interface SpawnedAgentFixture { toolCallKind: acp.ToolKind, ): acp.RequestPermissionRequest[]; readToolCalls(sessionId: string, toolCallKind: acp.ToolKind): acp.ToolCall[]; + readAsyncTasks(sessionId: string): AsyncTaskSpawnedUpdate[]; dispose(): Promise; } @@ -109,6 +111,7 @@ class RecordingClient implements acp.Client { private readonly availableCommandsBySessionId = new Map(); private readonly permissionRequestsBySessionId = new Map(); private readonly toolCallsBySessionId = new Map(); + private readonly asyncTasksBySessionId = new Map(); private permissionResponder: PermissionResponder = () => ({ outcome: {outcome: "cancelled"}, }); @@ -128,6 +131,15 @@ class RecordingClient implements acp.Client { } async sessionUpdate(params: acp.SessionNotification): Promise { + // The SDK schema does not know the AIR async task updates. + const update: {sessionUpdate: string} = params.update; + if (update.sessionUpdate === "async_task_spawned") { + const asyncTasks = this.asyncTasksBySessionId.get(params.sessionId) ?? []; + asyncTasks.push(update as AsyncTaskSpawnedUpdate); + this.asyncTasksBySessionId.set(params.sessionId, asyncTasks); + return; + } + if (params.update.sessionUpdate === "available_commands_update") { this.availableCommandsBySessionId.set(params.sessionId, params.update.availableCommands); return; @@ -168,6 +180,10 @@ class RecordingClient implements acp.Client { const toolCalls = this.toolCallsBySessionId.get(sessionId) ?? []; return toolCalls.filter((toolCall) => toolCall.kind === toolCallKind); } + + readAsyncTasks(sessionId: string): AsyncTaskSpawnedUpdate[] { + return this.asyncTasksBySessionId.get(sessionId) ?? []; + } } class SpawnedAgentFixtureImpl implements SpawnedAgentFixture { @@ -279,6 +295,10 @@ class SpawnedAgentFixtureImpl implements SpawnedAgentFixture { return this.client.readToolCalls(sessionId, toolCallKind); } + readAsyncTasks(sessionId: string): AsyncTaskSpawnedUpdate[] { + return this.client.readAsyncTasks(sessionId); + } + async dispose(): Promise { if (this.disposed) { return; From c88eb8a1bd4023c38f94b248270b57e7898dd453 Mon Sep 17 00:00:00 2001 From: Andrey Bragin Date: Tue, 13 Oct 2026 15:35:25 +0200 Subject: [PATCH 4/4] test: stop the cancelled command by its tool call id The SDK client rejects the async_task_spawned update, so the test cannot read the task id from it. A root command's async task id is its tool call id, so retry the stop request with it until the task is published. --- .../e2e/acp-e2e-shell-approval.test.ts | 16 ++++++++------- .../e2e/spawned-agent-fixture.ts | 20 ------------------- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts b/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts index 344593159..24608b02e 100644 --- a/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts +++ b/src/__tests__/CodexACPAgent/e2e/acp-e2e-shell-approval.test.ts @@ -234,15 +234,17 @@ describeE2E("E2E shell cancellation tests", () => { await fixture.connection.cancel({sessionId}); expect((await promptResponse).stopReason).toBe("cancelled"); - const asyncTaskId = await vi.waitFor(() => { - const task = fixture?.readAsyncTasks(sessionId).find(task => task.toolCallId === toolCallId); - expect(task).toBeDefined(); - return task!.asyncTaskId; - }, {timeout: 10_000}); expect(isProcessRunning(pid)).toBe(true); - await expect(fixture.connection.extMethod(ASYNC_TASK_STOP_METHOD, {sessionId, asyncTaskId})) - .resolves.toEqual({stopped: true}); + // The SDK client rejects the `async_task_spawned` update it has no schema for, but a root + // command's async task id is its tool call id. Stop fails until the adapter publishes the task. + await vi.waitFor(async () => { + const response = await fixture?.connection.extMethod(ASYNC_TASK_STOP_METHOD, { + sessionId, + asyncTaskId: toolCallId, + }); + expect(response).toEqual({stopped: true}); + }, {timeout: 10_000}); await vi.waitFor(() => { expect(isProcessRunning(pid)).toBe(false); }, {timeout: 5_000}); diff --git a/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts b/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts index d49edf97a..df40312b2 100644 --- a/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts +++ b/src/__tests__/CodexACPAgent/e2e/spawned-agent-fixture.ts @@ -8,7 +8,6 @@ import {ModelId} from "../../../ModelId"; import {removeDirectoryWithRetry, writeCodexHomeConfig} from "../../acp-test-utils"; import type {PermissionResponder} from "./permission-responders"; import type {LegacyNewSessionResponse} from "../../../AcpExtensions"; -import type {AsyncTaskSpawnedUpdate} from "../../../async-tasks/AcpAsyncTasks"; export const DEFAULT_TEST_MODEL_ID = ModelId.create("gpt-6-luna", "low"); export const OTHER_TEST_MODEL_ID = ModelId.create("gpt-5.5", "low"); @@ -40,7 +39,6 @@ export interface SpawnedAgentFixture { toolCallKind: acp.ToolKind, ): acp.RequestPermissionRequest[]; readToolCalls(sessionId: string, toolCallKind: acp.ToolKind): acp.ToolCall[]; - readAsyncTasks(sessionId: string): AsyncTaskSpawnedUpdate[]; dispose(): Promise; } @@ -111,7 +109,6 @@ class RecordingClient implements acp.Client { private readonly availableCommandsBySessionId = new Map(); private readonly permissionRequestsBySessionId = new Map(); private readonly toolCallsBySessionId = new Map(); - private readonly asyncTasksBySessionId = new Map(); private permissionResponder: PermissionResponder = () => ({ outcome: {outcome: "cancelled"}, }); @@ -131,15 +128,6 @@ class RecordingClient implements acp.Client { } async sessionUpdate(params: acp.SessionNotification): Promise { - // The SDK schema does not know the AIR async task updates. - const update: {sessionUpdate: string} = params.update; - if (update.sessionUpdate === "async_task_spawned") { - const asyncTasks = this.asyncTasksBySessionId.get(params.sessionId) ?? []; - asyncTasks.push(update as AsyncTaskSpawnedUpdate); - this.asyncTasksBySessionId.set(params.sessionId, asyncTasks); - return; - } - if (params.update.sessionUpdate === "available_commands_update") { this.availableCommandsBySessionId.set(params.sessionId, params.update.availableCommands); return; @@ -180,10 +168,6 @@ class RecordingClient implements acp.Client { const toolCalls = this.toolCallsBySessionId.get(sessionId) ?? []; return toolCalls.filter((toolCall) => toolCall.kind === toolCallKind); } - - readAsyncTasks(sessionId: string): AsyncTaskSpawnedUpdate[] { - return this.asyncTasksBySessionId.get(sessionId) ?? []; - } } class SpawnedAgentFixtureImpl implements SpawnedAgentFixture { @@ -295,10 +279,6 @@ class SpawnedAgentFixtureImpl implements SpawnedAgentFixture { return this.client.readToolCalls(sessionId, toolCallKind); } - readAsyncTasks(sessionId: string): AsyncTaskSpawnedUpdate[] { - return this.client.readAsyncTasks(sessionId); - } - async dispose(): Promise { if (this.disposed) { return;