From 1559dbc559798a1af8197cc9250944b5400ae63d Mon Sep 17 00:00:00 2001 From: Odin Glynn-Martin Date: Sun, 4 Oct 2026 21:07:20 +0100 Subject: [PATCH] fix(acp): apply queued session updates before ending a turn The ACP SDK starts each notification's handler without awaiting it but resolves responses synchronously, so a prompt response can settle while earlier session updates are still queued. A tool update applied after the turn's terminal published a running row that nothing closed. Drain the notification lane before terminalizing open rows and ending the turn. Co-Authored-By: Claude Opus 5.5 --- packages/plugin/src/server/acp-internal/connection.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/plugin/src/server/acp-internal/connection.ts b/packages/plugin/src/server/acp-internal/connection.ts index 99b539f7cb..fb96e4da16 100644 --- a/packages/plugin/src/server/acp-internal/connection.ts +++ b/packages/plugin/src/server/acp-internal/connection.ts @@ -596,10 +596,13 @@ class AcpRuntime { turnId, state: "started", }); + // The SDK dispatches notifications without awaiting them, so the prompt response can resolve + // while earlier session updates are still queued. Apply them before the turn ends. const settled = this.call( this.connection.prompt({ sessionId: this.nativeSessionId, prompt }), ).then( - (response): void => { + async (response): Promise => { + await this.drainNotifications(); const state = response.stopReason === "cancelled" ? "canceled" : "completed"; this.fallbackChunkIds.clear(); this.terminalizeTransientItems(state); @@ -611,7 +614,8 @@ class AcpRuntime { }); return undefined; }, - (error): void => { + async (error): Promise => { + await this.drainNotifications(); this.fallbackChunkIds.clear(); this.terminalizeTransientItems("failed"); this.emit({