Skip to content

Commit a1f0cdb

Browse files
author
EC2 Default User
committed
fix: prevent interrupt mode from replaying previous assistant reply
When messages.queue.mode is 'interrupt', aborting an active run causes the previous turn's assistant reply to be re-sent to the user. Root cause: buildEmbeddedRunPayloads falls back to extractAssistantText(lastAssistant) when assistantTexts is empty. After an abort, assistantTexts is empty but lastAssistant still references the previous turn's message from session history. Fix: 1. Guard lastAssistant fallback: pass undefined when aborted and no new assistant text was generated (run.ts) 2. Skip blockReplyPipeline flush and payload delivery entirely when the run was aborted (agent-runner.ts) Fixes #50145
1 parent ffc1d54 commit a1f0cdb

2 files changed

Lines changed: 15 additions & 2 deletions

File tree

src/agents/pi-embedded-runner/run.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1605,7 +1605,8 @@ export async function runEmbeddedPiAgent(
16051605
const payloads = buildEmbeddedRunPayloads({
16061606
assistantTexts: attempt.assistantTexts,
16071607
toolMetas: attempt.toolMetas,
1608-
lastAssistant: attempt.lastAssistant,
1608+
lastAssistant:
1609+
aborted && attempt.assistantTexts.length === 0 ? undefined : attempt.lastAssistant,
16091610
lastToolError: attempt.lastToolError,
16101611
config: params.config,
16111612
sessionKey: params.sessionKey ?? params.sessionId,

src/auto-reply/reply/agent-runner.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,9 +406,14 @@ export async function runReplyAgent(params: {
406406
}
407407

408408
const payloadArray = runResult.payloads ?? [];
409+
const wasAborted = runResult.meta?.aborted === true;
409410

410411
if (blockReplyPipeline) {
411-
await blockReplyPipeline.flush({ force: true });
412+
// Skip flushing buffered block replies when the run was aborted —
413+
// the buffer may contain stale content from the interrupted turn.
414+
if (!wasAborted) {
415+
await blockReplyPipeline.flush({ force: true });
416+
}
412417
blockReplyPipeline.stop();
413418
}
414419
if (pendingToolTasks.size > 0) {
@@ -478,6 +483,13 @@ export async function runReplyAgent(params: {
478483
cliSessionId,
479484
});
480485

486+
// When the run was aborted (e.g. by interrupt queue mode), skip payload
487+
// delivery entirely. The aborted run's payloads may contain stale
488+
// `lastAssistant` fallback text from a previous turn. (#50145)
489+
if (wasAborted) {
490+
return finalizeWithFollowup(undefined, queueKey, runFollowupTurn);
491+
}
492+
481493
// Drain any late tool/block deliveries before deciding there's "nothing to send".
482494
// Otherwise, a late typing trigger (e.g. from a tool callback) can outlive the run and
483495
// keep the typing indicator stuck.

0 commit comments

Comments
 (0)