fix(webui): tighten turn completion handling
Keep the new turn-end signal scoped to WebSocket clients, preserve pending tool-call state across trailing tool result rows, and drop the accidental npm lockfile from the Bun-based WebUI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
Xubin Ren
co-authored by
Cursor
parent
be83525f99
commit
96da6d8190
Generated
-6020
File diff suppressed because it is too large
Load Diff
@@ -84,7 +84,7 @@ export function useSessionHistory(key: string | null): {
|
||||
messages: UIMessage[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
/** ``true`` when the last persisted message has ``tool_calls`` but no
|
||||
/** ``true`` when the last persisted assistant turn has ``tool_calls`` but no
|
||||
* final text yet — the model was still processing when the page loaded. */
|
||||
hasPendingToolCalls: boolean;
|
||||
} {
|
||||
@@ -153,9 +153,11 @@ export function useSessionHistory(key: string | null): {
|
||||
},
|
||||
];
|
||||
});
|
||||
// Check if the last persisted message has tool_calls but no final
|
||||
// text yet — the model was still processing when the page loaded.
|
||||
const lastRaw = body.messages[body.messages.length - 1];
|
||||
// Tool result rows can trail the assistant tool-call row while the turn
|
||||
// is still running, so check the last conversational row.
|
||||
const lastRaw = [...body.messages]
|
||||
.reverse()
|
||||
.find((m) => m.role === "user" || m.role === "assistant");
|
||||
const hasPending =
|
||||
lastRaw?.role === "assistant" &&
|
||||
Array.isArray(lastRaw.tool_calls) &&
|
||||
|
||||
@@ -194,6 +194,36 @@ describe("useSessions", () => {
|
||||
expect(result.current.hasPendingToolCalls).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps pending when tool result rows trail assistant tool calls", async () => {
|
||||
vi.mocked(api.fetchSessionMessages).mockResolvedValue({
|
||||
key: "websocket:chat-pending-tool-result",
|
||||
created_at: "2026-04-20T10:00:00Z",
|
||||
updated_at: "2026-04-20T10:05:00Z",
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: "Using 1 tool",
|
||||
timestamp: "2026-04-20T10:00:01Z",
|
||||
tool_calls: [{ id: "call-1" }],
|
||||
},
|
||||
{
|
||||
role: "tool",
|
||||
content: "tool output",
|
||||
timestamp: "2026-04-20T10:00:02Z",
|
||||
tool_call_id: "call-1",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useSessionHistory("websocket:chat-pending-tool-result"), {
|
||||
wrapper: wrap(fakeClient()),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.loading).toBe(false));
|
||||
|
||||
expect(result.current.hasPendingToolCalls).toBe(true);
|
||||
});
|
||||
|
||||
it("does not flag history as pending once the assistant turn has no tool calls", async () => {
|
||||
vi.mocked(api.fetchSessionMessages).mockResolvedValue({
|
||||
key: "websocket:chat-done",
|
||||
|
||||
Reference in New Issue
Block a user