fix(tui): clear pinned output on message_end to prevent duplicate display

The pinned "Latest Output" zone was only cleared at agent_end, but during
flows with form elicitation (e.g. discuss-phase), there is a gap between
message_end and agent_end where the agent waits for user input. During this
gap, the same content was visible in both the chat history and the pinned
zone. Clear the pinned zone at message_end when the assistant message is
finalized in the chat container.
This commit is contained in:
Jeremy 2026-04-11 17:41:50 -05:00
parent 65c42ba6dc
commit b488961609
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();