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
This commit is contained in:
梧桐雨 2026-11-04 17:24:43 +08:00 • committed by GitHub
parent f3a64af07b
commit a63c4ec5c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 39 additions and 12 deletions

View file

@ -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,

View file

@ -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: [