authorgravatar for saphid@gmail.comAlex Southwell <saphid@gmail.com> 2026-10-13 15:17:52+11:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-13 04:17:52+00:00
log540a3e89ed15a78129ff6bc91f561abddba8f6d8
tree0b0edf04a6b9bdaa291b2fcf129a9073459a43a3
parent5070a83a3248c537d3baaf063e719f179c49bb52
signature Signed by PGP key B5690EEEBB952194

perf(web): collect active tool calls without array shifts (#18853)

Co-authored-by: SAPHID <saphid@users.noreply.github.com> Co-authored-by: GPT-6.1 Sol <noreply@openai.com>

2 files changed, 53 insertions(+), 1 deletions(-)

apps/web/src/components/chat/MessagesTimeline.active-tools.test.ts created+51
...@@ -0,0 +1,51 @@
1import { RunId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3import { deriveMessagesTimelineRows, liveWorkEntryLabel } from "./MessagesTimeline.logic";
4
5describe("long active tool groups", () => {
6 it.each([false, true])(
7 "preserves chronological order and the latest activity, expanded=%s",
8 (expanded) => {
9 const runId = RunId.make("long-active-run");
10 const createdAt = "2026-10-12T00:00:00Z";
11 const timelineEntries = Array.from({ length: 1_000 }, (_, index) => ({
12 id: `work-entry-${index}`,
13 kind: "work" as const,
14 createdAt,
15 entry: {
16 id: `work-${index}`,
17 createdAt,
18 runId,
19 label: `Tool ${index}`,
20 tone: index % 2 === 0 ? ("thinking" as const) : ("tool" as const),
21 itemType: index % 2 === 0 ? ("reasoning" as const) : ("command_execution" as const),
22 ...(index % 2 === 0 ? { detail: `Thought ${index}` } : {}),
23 toolLifecycleStatus: index === 999 ? ("inProgress" as const) : ("completed" as const),
24 },
25 }));
26 const rows = deriveMessagesTimelineRows({
27 timelineEntries,
28 isWorking: true,
29 runningRunId: runId,
30 activeTurnStartedAt: createdAt,
31 turnDiffSummaries: [],
32 supportsConversationRollback: false,
33 expandedWorkGroupIds: new Set(expanded ? ["work-group:work-entry-0"] : []),
34 });
35 const live = rows.find((row) => row.kind === "work-live");
36 expect(live).toBeDefined();
37 expect(live?.entry.id).toBe("work-999");
38 expect(live && liveWorkEntryLabel(live.entry, undefined, live.active)).toContain("999");
39 expect(live?.thought?.id).toBe("work-998");
40 expect(live?.groupedEntries.map(({ id }) => id)).toEqual(
41 timelineEntries.map(({ entry }) => entry.id),
42 );
43 expect(live?.groupId).toBe("work-group:work-entry-0");
44 const details = rows.filter((row) => row.kind === "work");
45 expect(details).toHaveLength(expanded ? 1 : 0);
46 if (expanded) {
47 expect(details[0]?.groupedEntries).toEqual(live?.groupedEntries);
48 }
49 },
50 );
51});
apps/web/src/components/chat/MessagesTimeline.logic.ts+2-1
...@@ -1504,9 +1504,10 @@ export function deriveMessagesTimelineRows(input: {...@@ -1504,9 +1504,10 @@ export function deriveMessagesTimelineRows(input: {
1504 } else if ((entry.attempt?.id ?? null) !== tailAttemptId) {1504 } else if ((entry.attempt?.id ?? null) !== tailAttemptId) {
1505 break;1505 break;
1506 }1506 }
1507 activeToolEntries.unshift(entry);1507 activeToolEntries.push(entry);
1508 }1508 }
1509 }1509 }
1510 activeToolEntries.reverse();
1510 const visibleActiveToolEntries = activeToolEntries.filter((entry) =>1511 const visibleActiveToolEntries = activeToolEntries.filter((entry) =>
1511 workEntryIsVisibleInGroup(entry.entry, true),1512 workEntryIsVisibleInGroup(entry.entry, true),
1512 );1513 );