fix(webui): split reasoning at tool trace boundaries
Live rendering merged reasoning chunks by scanning backward to the latest assistant row. That fixed late reasoning, but the scan skipped trace rows, so reasoning after a tool call crossed the Used tools block and attached to the previous assistant iteration. Refresh looked correct because persisted history reconstructs assistant/tool boundaries. Treat trace rows as hard phase boundaries, just like user messages. A reasoning_delta after Used tools now starts a fresh assistant placeholder, so live rendering matches replay: Thinking -> Used tools -> Thinking -> Used tools / answer. Add a regression for reasoning_delta -> reasoning_end -> tool_hint -> reasoning_delta. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -276,6 +276,45 @@ describe("useNanobotStream", () => {
|
||||
expect(result.current.messages[2].reasoningStreaming).toBe(true);
|
||||
});
|
||||
|
||||
it("does not attach reasoning across a tool trace boundary", () => {
|
||||
const fake = fakeClient();
|
||||
const { result } = renderHook(() => useNanobotStream("chat-r7", EMPTY_MESSAGES), {
|
||||
wrapper: wrap(fake.client),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
fake.emit("chat-r7", {
|
||||
event: "reasoning_delta",
|
||||
chat_id: "chat-r7",
|
||||
text: "First reasoning.",
|
||||
});
|
||||
fake.emit("chat-r7", { event: "reasoning_end", chat_id: "chat-r7" });
|
||||
fake.emit("chat-r7", {
|
||||
event: "message",
|
||||
chat_id: "chat-r7",
|
||||
text: "web_search({\"query\":\"OpenClaw\"})",
|
||||
kind: "tool_hint",
|
||||
});
|
||||
fake.emit("chat-r7", {
|
||||
event: "reasoning_delta",
|
||||
chat_id: "chat-r7",
|
||||
text: "Second reasoning.",
|
||||
});
|
||||
});
|
||||
|
||||
expect(result.current.messages).toHaveLength(3);
|
||||
expect(result.current.messages.map((m) => m.kind ?? "message")).toEqual([
|
||||
"message",
|
||||
"trace",
|
||||
"message",
|
||||
]);
|
||||
expect(result.current.messages[0].reasoning).toBe("First reasoning.");
|
||||
expect(result.current.messages[1].traces).toEqual([
|
||||
"web_search({\"query\":\"OpenClaw\"})",
|
||||
]);
|
||||
expect(result.current.messages[2].reasoning).toBe("Second reasoning.");
|
||||
});
|
||||
|
||||
it("attaches assistant media_urls to complete messages", () => {
|
||||
const fake = fakeClient();
|
||||
const { result } = renderHook(() => useNanobotStream("chat-m", EMPTY_MESSAGES), {
|
||||
|
||||
Reference in New Issue
Block a user