From a63c4ec5c89ce30f8f144c2c1eba7842ffab974b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A7=E6=A1=90=E9=9B=A8?= <147830929+wutongyuonce@users.noreply.github.com> Date: Wed, 4 Nov 2026 17:24:43 +0800 Subject: [PATCH] fix(coding-agent): clear delivered image-only queue entries (#8612) ## Summary - remove delivered queue entries even when the user message has no text - keep image-only steering and follow-up pending counts in sync with the agent queue - cover image-only steering delivery with a regression test ## Testing - `npm run check` - `./test.sh` Fixes #8581 --- .../coding-agent/src/core/agent-session.ts | 22 +++++++------- .../test/suite/agent-session-queue.test.ts | 29 +++++++++++++++++++ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index 0f5aa1105..e4f53004b 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -1141,19 +1141,17 @@ export class AgentSession { if (event.type === "message_start" && event.message.role === "user") { this._overflowRecoveryAttempted = false; const messageText = contentText(event.message.content, ""); - if (messageText) { - // Check steering queue first - const steeringIndex = this._steeringMessages.indexOf(messageText); - if (steeringIndex !== -1) { - this._steeringMessages.splice(steeringIndex, 1); + // Check steering queue first + const steeringIndex = this._steeringMessages.indexOf(messageText); + if (steeringIndex !== -1) { + this._steeringMessages.splice(steeringIndex, 1); + this._emitQueueUpdate(); + } else { + // Check follow-up queue + const followUpIndex = this._followUpMessages.indexOf(messageText); + if (followUpIndex !== -1) { + this._followUpMessages.splice(followUpIndex, 1); this._emitQueueUpdate(); - } else { - // Check follow-up queue - const followUpIndex = this._followUpMessages.indexOf(messageText); - if (followUpIndex !== -1) { - this._followUpMessages.splice(followUpIndex, 1); - this._emitQueueUpdate(); - } } } // Normalize queued images at delivery, before events and persistence see the message, diff --git a/packages/coding-agent/test/suite/agent-session-queue.test.ts b/packages/coding-agent/test/suite/agent-session-queue.test.ts index d198cbd2d..39a4470d2 100644 --- a/packages/coding-agent/test/suite/agent-session-queue.test.ts +++ b/packages/coding-agent/test/suite/agent-session-queue.test.ts @@ -437,6 +437,35 @@ describe("AgentSession queue characterization", () => { expect(harness.session.pendingMessageCount).toBe(0); }); + it("removes an image-only steering message after delivery", async () => { + const waiting = await createWaitingHarness(); + const { harness, waitForToolStart, promptPromise, releaseToolExecution } = waiting; + harnesses.push(harness); + let sawImage = false; + + harness.setResponses([ + fauxAssistantMessage(fauxToolCall("wait", {}), { stopReason: "toolUse" }), + (context) => { + const user = [...context.messages].reverse().find((message) => message.role === "user"); + sawImage = + user?.role === "user" && + typeof user.content !== "string" && + user.content.some((part) => part.type === "image"); + return fauxAssistantMessage("done"); + }, + ]); + + await waitForToolStart; + await harness.session.steer("", [{ type: "image", mimeType: "image/png", data: "ZmFrZQ==" }]); + expect(harness.session.pendingMessageCount).toBe(1); + releaseToolExecution(); + await promptPromise; + + expect(sawImage).toBe(true); + expect(harness.session.agent.hasQueuedMessages()).toBe(false); + expect(harness.session.pendingMessageCount).toBe(0); + }); + it("throws when queueing an extension command with steer", async () => { const harness = await createHarness({ extensionFactories: [