fix(ui): ignore malformed tool stream entries (#111145)

Prevent malformed transient tool-stream entries from aborting Control UI chat reconciliation while preserving valid tool message order and rendering.
This commit is contained in:
Jason (Json)
2026-07-18 22:21:11 -06:00
committed by GitHub
parent 16e967eb7f
commit 060b30f23a
2 changed files with 23 additions and 1 deletions
+20
View File
@@ -1870,6 +1870,26 @@ describe("buildCachedChatItems", () => {
expect(messageRecord(groupAt(groups, 0)).content).toBe("still visible");
});
it("does not expose malformed tool stream entries to message rendering", () => {
const items = buildCachedChatItems(
createProps({
toolMessages: [
null,
undefined,
{
role: "assistant",
content: [{ type: "toolcall", name: "heartbeat_respond", arguments: {} }],
timestamp: 1,
},
],
}),
);
const groups = items.filter((item) => item.kind === "group");
expect(groups).toHaveLength(1);
expect(messageRecord(groupAt(groups, 0)).role).toBe("assistant");
});
it("does not collapse duplicate text messages separated by another message", () => {
const groups = messageGroups({
messages: [
+3 -1
View File
@@ -1187,7 +1187,9 @@ function buildChatItems(props: BuildChatItemsProps): Array<ChatItem | MessageGro
const history = (Array.isArray(props.messages) ? props.messages : []).filter(
(message) => !isAssistantHeartbeatAckForDisplay(message),
);
const tools = Array.isArray(props.toolMessages) ? props.toolMessages : [];
const tools = Array.isArray(props.toolMessages)
? props.toolMessages.filter((message) => asRecord(message) !== null)
: [];
const historyKeys = buildMessageKeys(history);
const toolKeys = buildMessageKeys(tools, history.length);
const liftedCanvasSources = tools.flatMap((message, index) => {