From 9028c9b63b6c3ed1737c168ae5e1548bab064668 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Tue, 14 Jul 2026 05:35:34 +0800 Subject: [PATCH] fix: contain ACP update predicate failures --- packages/support/acp-snapshot/src/launcher.ts | 13 +++++++++++-- packages/support/acp-snapshot/tests/harness.spec.ts | 4 ++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/support/acp-snapshot/src/launcher.ts b/packages/support/acp-snapshot/src/launcher.ts index 635a02ca35..975b1bb852 100644 --- a/packages/support/acp-snapshot/src/launcher.ts +++ b/packages/support/acp-snapshot/src/launcher.ts @@ -107,6 +107,7 @@ export function launchAcpTestAgent(options: AcpTestLaunchOptions): LaunchedAcpTe const updateWaiters: { match: (update: SessionNotification['update']) => boolean resolve: (update: SessionNotification['update']) => void + reject: (reason: unknown) => void }[] = [] const stream = ndJsonStream( Writable.toWeb(child.stdin) as WritableStream, @@ -119,7 +120,15 @@ export function launchAcpTestAgent(options: AcpTestLaunchOptions): LaunchedAcpTe const waiter = updateWaiters[index] /* v8 ignore next 1 -- index is bounded by the array length */ if (waiter === undefined) continue - if (!waiter.match(params.update)) continue + let matches: boolean + try { + matches = waiter.match(params.update) + } catch (error: unknown) { + updateWaiters.splice(index, 1) + waiter.reject(error) + continue + } + if (!matches) continue updateWaiters.splice(index, 1) waiter.resolve(params.update) } @@ -136,7 +145,7 @@ export function launchAcpTestAgent(options: AcpTestLaunchOptions): LaunchedAcpTe updates, rawStdout: () => Buffer.concat(rawBuffers).toString('utf8'), stderr: () => stderrChunks.join(''), - waitForUpdate: match => new Promise(resolve => updateWaiters.push({ match, resolve })), + waitForUpdate: match => new Promise((resolve, reject) => updateWaiters.push({ match, resolve, reject })), async close(signal?: NodeJS.Signals): Promise { if (child.exitCode !== null || child.signalCode !== null) return if (signal === undefined) child.stdin.end() diff --git a/packages/support/acp-snapshot/tests/harness.spec.ts b/packages/support/acp-snapshot/tests/harness.spec.ts index ec1ed5cf6f..db715db90c 100644 --- a/packages/support/acp-snapshot/tests/harness.spec.ts +++ b/packages/support/acp-snapshot/tests/harness.spec.ts @@ -57,7 +57,11 @@ describe('runScenario', () => { await launched.client.initialize({ protocolVersion: PROTOCOL_VERSION, clientCapabilities: {} }) const { sessionId } = await launched.client.newSession({ cwd: dir, mcpServers: [] }) const nextChunk = launched.waitForUpdate(update => update.sessionUpdate === 'agent_message_chunk') + const predicateFailure = new Error('predicate failed') + const failedPredicate = launched.waitForUpdate(() => { throw predicateFailure }) + .catch((error: unknown): unknown => error) await launched.client.prompt({ sessionId, prompt: [{ type: 'text', text: 'go' }] }) + expect(await failedPredicate).toBe(predicateFailure) expect((await nextChunk).sessionUpdate).toBe('agent_message_chunk') expect(launched.updates.some(update => update.sessionUpdate === 'agent_message_chunk')).toBe(true) expect(launched.rawStdout()).toContain('permission:{\\"outcome\\":\\"cancelled\\"}')