diff --git a/packages/coding-agent/changelog.d/fix-acp-async-result-terminal-correlation.md b/packages/coding-agent/changelog.d/fix-acp-async-result-terminal-correlation.md new file mode 100644 index 00000000000..450fa64ffa3 --- /dev/null +++ b/packages/coding-agent/changelog.d/fix-acp-async-result-terminal-correlation.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserve active ACP prompt correlation on async-result and trigger-turn continuations. diff --git a/packages/coding-agent/src/session/agent-session.ts b/packages/coding-agent/src/session/agent-session.ts index 75fb0717a02..4b633b3259e 100644 --- a/packages/coding-agent/src/session/agent-session.ts +++ b/packages/coding-agent/src/session/agent-session.ts @@ -5597,12 +5597,14 @@ export class AgentSession { void this.#queueFollowUpAfterReservation(message, displayText, { createDisplayEntry: false, trackExternalFollowUp: false, + sdkRunToken: this.#activeSdkRunToken, }).catch(error => { this.#settleDeliveredOwnedRegistrations([message]); logger.warn("Owned streaming follow-up was rejected", { error: String(error) }); }); }, injectIdle: async (messages, signal) => { + const sdkRunToken = this.#activeSdkRunToken; // Mandated boundary comment (corrected turn semantics): same origin // split as the streaming injector — an allowed owned-completion // delivery starts a fresh turn attempt/lineage and is not a @@ -5637,14 +5639,14 @@ export class AgentSession { await this.agent.prompt(first, { ...this.#managedFallbackPromptOptions(), onRunAccepted: (handle: AttemptRunHandle) => { - if (handle) this.#acceptSdkAttemptRun(handle); + if (handle) this.#acceptSdkAttemptRun(handle, sdkRunToken); }, }); } else { await this.agent.prompt(survivors, { ...this.#managedFallbackPromptOptions(), onRunAccepted: (handle: AttemptRunHandle) => { - if (handle) this.#acceptSdkAttemptRun(handle); + if (handle) this.#acceptSdkAttemptRun(handle, sdkRunToken); }, }); } @@ -15483,6 +15485,7 @@ export class AgentSession { forceOneAtATime: options?.followUpQueuePolicy === "sequential", createDisplayEntry: false, trackExternalFollowUp: false, + sdkRunToken: this.#activeSdkRunToken, }); this.#bindCustomDisplayEntry(appMessage, "followUp"); // The session can report streaming while no agent loop owns the queue diff --git a/packages/coding-agent/test/agent-session-concurrent.test.ts b/packages/coding-agent/test/agent-session-concurrent.test.ts index 39e7ecfdbe1..0eead1e7806 100644 --- a/packages/coding-agent/test/agent-session-concurrent.test.ts +++ b/packages/coding-agent/test/agent-session-concurrent.test.ts @@ -790,7 +790,6 @@ describe("AgentSession concurrent prompt guard", () => { await hiddenTurn; await firstPrompt; await session.waitForIdle(); - expect(callMessages).toHaveLength(2); expect( callMessages[1]?.some(message => {