Merge pull request #4009 from jeremymcs/fix/pinned-output-message-end-duplicate

fix(tui): clear pinned output on message_end to prevent duplicate display
This commit is contained in:
Jeremy McSpadden 2026-04-11 17:55:14 -05:00 committed by GitHub
commit 6bf76274a3
2 changed files with 59 additions and 0 deletions

View file

@ -386,6 +386,56 @@ test("chat-controller clears pinned zone when the agent turn ends", async () =>
assert.equal(host.pinnedMessageContainer.children.length, 0, "pinned zone should clear on agent_end");
});
test("chat-controller clears pinned zone when assistant message ends", async () => {
(globalThis as any)[Symbol.for("@gsd/pi-coding-agent:theme")] = {
fg: (_key: string, text: string) => text,
bg: (_key: string, text: string) => text,
bold: (text: string) => text,
italic: (text: string) => text,
truncate: (text: string) => text,
};
const host = createHost();
const toolCall = {
type: "toolCall",
id: "tool-msg-end-1",
name: "exec_command",
arguments: { cmd: "echo hi" },
};
await handleAgentEvent(host, { type: "message_start", message: makeAssistant([]) } as any);
host.getMarkdownThemeWithSettings = () => ({});
const msgContent = [{ type: "text", text: "Summary after tools." }, toolCall];
await handleAgentEvent(
host,
{
type: "message_update",
message: makeAssistant(msgContent),
assistantMessageEvent: {
type: "toolcall_end",
contentIndex: 1,
toolCall: {
...toolCall,
externalResult: {
content: [{ type: "text", text: "ok" }],
details: {},
isError: false,
},
},
partial: makeAssistant(msgContent),
},
} as any,
);
assert.ok(host.pinnedMessageContainer.children.length > 0, "pinned zone should be populated during streaming");
// End the assistant message (e.g. before form elicitation) — pinned zone should clear
await handleAgentEvent(host, { type: "message_end", message: makeAssistant(msgContent) } as any);
assert.equal(host.pinnedMessageContainer.children.length, 0, "pinned zone should clear on message_end to prevent duplicate display");
});
test("chat-controller does not pin when there are no tool calls", async () => {
(globalThis as any)[Symbol.for("@gsd/pi-coding-agent:theme")] = {
fg: (_key: string, text: string) => text,

View file

@ -380,6 +380,15 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
}
host.streamingComponent = undefined;
host.streamingMessage = undefined;
// Clear pinned output once the message is finalized in the chat
// container — prevents duplicate display when the agent continues
// (e.g. form elicitation) after the assistant message ends.
if (pinnedBorder) pinnedBorder.stopSpinner();
host.pinnedMessageContainer.clear();
lastPinnedText = "";
hasToolsInTurn = false;
pinnedBorder = undefined;
pinnedTextComponent = undefined;
host.footer.invalidate();
}
host.ui.requestRender();