authorgravatar for 62337003+Bil0000@users.noreply.github.comBilal Bakr <62337003+Bil0000@users.noreply.github.com> 2026-08-23 23:53:54+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-23 16:53:54-04:00
logfa219001dc2f14cfd9c7774c2c03c153359144be
treedfa4b18ad380f9599aef5b9e158a6a8bb8ebe04a
parent5427ca056270ad49ac88431ac1fb8bb54dd7b91b
signature Signed by PGP key B5690EEEBB952194

perf(web): reuse work log rows during streaming (#8006)


2 files changed, 102 insertions(+), 20 deletions(-)

apps/web/src/session-logic.test.ts+61
......@@ -2205,3 +2205,64 @@ describe("rerun workflows", () => {
22052205 expect(spawnRows.map((row) => row.turnId)).toEqual(["turn-1", "turn-2"]);
22062206 });
22072207});
2208
2209describe("session activity performance", () => {
2210 it("reuses entries for unchanged activities", () => {
2211 const activities = ["status", "diff", "log"].map((command, index) =>
2212 makeActivity({
2213 id: `stable-tool-${index}`,
2214 kind: "tool.completed",
2215 sequence: index,
2216 payload: {
2217 itemType: "command_execution",
2218 data: { toolCallId: `stable-tool-${index}`, item: { command: ["git", command] } },
2219 },
2220 }),
2221 );
2222
2223 const initialEntries = deriveWorkLogEntries(activities.slice(0, 2));
2224 const appendedEntries = deriveWorkLogEntries(activities);
2225 expect(appendedEntries[0]).toBe(initialEntries[0]);
2226 expect(appendedEntries[1]).toBe(initialEntries[1]);
2227 });
2228
2229 it("updates 20,000 ordered tool activities within 100 ms", () => {
2230 const activities = Array.from({ length: 20_000 }, (_, index) =>
2231 makeActivity({
2232 id: `benchmark-tool-${index}`,
2233 createdAt: new Date(1_700_000_000_000 + index).toISOString(),
2234 kind: "tool.completed",
2235 summary: "Ran command",
2236 sequence: index,
2237 payload: {
2238 itemType: "command_execution",
2239 title: "Ran command",
2240 data: {
2241 toolCallId: `benchmark-tool-${index}`,
2242 item: { command: ["git", "status"] },
2243 },
2244 },
2245 }),
2246 );
2247 deriveWorkLogEntries(activities);
2248 const updatedActivities = [
2249 ...activities,
2250 makeActivity({
2251 id: "benchmark-tool-appended",
2252 createdAt: new Date(1_700_000_000_000 + activities.length).toISOString(),
2253 kind: "tool.completed",
2254 summary: "Ran command",
2255 sequence: activities.length,
2256 payload: {
2257 itemType: "command_execution",
2258 title: "Ran command",
2259 data: { toolCallId: "benchmark-tool-appended", item: { command: ["git", "diff"] } },
2260 },
2261 }),
2262 ];
2263
2264 const startedAt = performance.now();
2265 expect(deriveWorkLogEntries(updatedActivities)).toHaveLength(20_001);
2266 expect(performance.now() - startedAt).toBeLessThan(100);
2267 });
2268});
apps/web/src/session-logic.ts+41-20
......@@ -98,15 +98,22 @@ export interface WorkLogEntry {
9898 };
9999}
100100
101const workLogCollapseKey = Symbol();
102
101103interface DerivedWorkLogEntry extends WorkLogEntry {
102 activityKind: OrchestrationThreadActivity["kind"];
103 collapseKey?: string;
104 sourceActivityKind: OrchestrationThreadActivity["kind"];
105 [workLogCollapseKey]?: string;
104106 toolCallId?: string;
105107 isWorkflowCoordinator?: boolean;
106108 /** Shell/monitor/plan tasks: ordinary work-log rows, never spawn CTAs. */
107109 isBackgroundTask?: boolean;
108110}
109111
112const derivedWorkLogEntryByActivity = new WeakMap<
113 OrchestrationThreadActivity,
114 DerivedWorkLogEntry
115>();
116
110117export interface PendingApproval {
111118 requestId: ApprovalRequestId;
112119 requestKind: "command" | "file-read" | "file-change";
......@@ -854,10 +861,7 @@ export function deriveWorkLogEntries(
854861 if (isAgentInternalActivity(activity)) continue;
855862 entries.push(toDerivedWorkLogEntry(activity));
856863 }
857 return collapseDerivedWorkLogEntries(entries).map((entry) => {
858 const { activityKind, collapseKey: _collapseKey, ...rest } = entry;
859 return Object.assign(rest, { sourceActivityKind: activityKind });
860 });
864 return collapseDerivedWorkLogEntries(entries);
861865}
862866
863867function isPlanBoundaryToolActivity(activity: OrchestrationThreadActivity): boolean {
......@@ -892,6 +896,10 @@ function extractWorkLogToolLifecycleStatus(
892896}
893897
894898function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWorkLogEntry {
899 const cachedEntry = derivedWorkLogEntryByActivity.get(activity);
900 if (cachedEntry) {
901 return cachedEntry;
902 }
895903 const payload =
896904 activity.payload && typeof activity.payload === "object"
897905 ? (activity.payload as Record<string, unknown>)
......@@ -935,7 +943,7 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo
935943 : activity.tone === "approval"
936944 ? "info"
937945 : activity.tone,
938 activityKind: activity.kind,
946 sourceActivityKind: activity.kind,
939947 };
940948 const itemType = extractWorkLogItemType(payload);
941949 const requestKind = extractWorkLogRequestKind(payload);
......@@ -994,8 +1002,9 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo
9941002 }
9951003 const collapseKey = deriveToolLifecycleCollapseKey(entry);
9961004 if (collapseKey) {
997 entry.collapseKey = collapseKey;
1005 entry[workLogCollapseKey] = collapseKey;
9981006 }
1007 derivedWorkLogEntryByActivity.set(activity, entry);
9991008 return entry;
10001009}
10011010
......@@ -1026,7 +1035,10 @@ function agentSpawnGroupKey(entry: DerivedWorkLogEntry): string {
10261035}
10271036
10281037function toolLifecycleCollapseMapKey(entry: DerivedWorkLogEntry): string | undefined {
1029 if (entry.activityKind !== "tool.updated" && entry.activityKind !== "tool.completed") {
1038 if (
1039 entry.sourceActivityKind !== "tool.updated" &&
1040 entry.sourceActivityKind !== "tool.completed"
1041 ) {
10301042 return undefined;
10311043 }
10321044 return entry.toolCallId ? `tool:${entry.turnId ?? "no-turn"}:${entry.toolCallId}` : undefined;
......@@ -1053,9 +1065,9 @@ function collapseDerivedWorkLogEntries(
10531065 const isTaskRow =
10541066 entry.taskId !== undefined &&
10551067 !entry.isBackgroundTask &&
1056 (entry.activityKind === "task.started" ||
1057 entry.activityKind === "task.progress" ||
1058 entry.activityKind === "task.completed");
1068 (entry.sourceActivityKind === "task.started" ||
1069 entry.sourceActivityKind === "task.progress" ||
1070 entry.sourceActivityKind === "task.completed");
10591071 if (isTaskRow && entry.taskId !== undefined) {
10601072 const rememberedKey = groupKeyByTaskId.get(entry.taskId);
10611073 const groupKey = rememberedKey ?? agentSpawnGroupKey(entry);
......@@ -1131,19 +1143,25 @@ function shouldCollapseToolLifecycleEntries(
11311143 previous: DerivedWorkLogEntry,
11321144 next: DerivedWorkLogEntry,
11331145): boolean {
1134 if (previous.activityKind !== "tool.updated" && previous.activityKind !== "tool.completed") {
1146 if (
1147 previous.sourceActivityKind !== "tool.updated" &&
1148 previous.sourceActivityKind !== "tool.completed"
1149 ) {
11351150 return false;
11361151 }
1137 if (next.activityKind !== "tool.updated" && next.activityKind !== "tool.completed") {
1152 if (next.sourceActivityKind !== "tool.updated" && next.sourceActivityKind !== "tool.completed") {
11381153 return false;
11391154 }
11401155 if (previous.turnId !== next.turnId) {
11411156 return false;
11421157 }
1143 if (previous.activityKind === "tool.completed") {
1158 if (previous.sourceActivityKind === "tool.completed") {
11441159 return false;
11451160 }
1146 if (previous.collapseKey !== undefined && previous.collapseKey === next.collapseKey) {
1161 if (
1162 previous[workLogCollapseKey] !== undefined &&
1163 previous[workLogCollapseKey] === next[workLogCollapseKey]
1164 ) {
11471165 return true;
11481166 }
11491167 return (
......@@ -1166,7 +1184,7 @@ function mergeDerivedWorkLogEntries(
11661184 const toolTitle = next.toolTitle ?? previous.toolTitle;
11671185 const itemType = next.itemType ?? previous.itemType;
11681186 const requestKind = next.requestKind ?? previous.requestKind;
1169 const collapseKey = next.collapseKey ?? previous.collapseKey;
1187 const collapseKey = next[workLogCollapseKey] ?? previous[workLogCollapseKey];
11701188 const toolCallId = next.toolCallId ?? previous.toolCallId;
11711189 const toolLifecycleStatus = next.toolLifecycleStatus ?? previous.toolLifecycleStatus;
11721190 const toolData = next.toolData ?? previous.toolData;
......@@ -1180,7 +1198,7 @@ function mergeDerivedWorkLogEntries(
11801198 ...(toolTitle ? { toolTitle } : {}),
11811199 ...(itemType ? { itemType } : {}),
11821200 ...(requestKind ? { requestKind } : {}),
1183 ...(collapseKey ? { collapseKey } : {}),
1201 ...(collapseKey ? { [workLogCollapseKey]: collapseKey } : {}),
11841202 ...(toolCallId ? { toolCallId } : {}),
11851203 ...(toolLifecycleStatus !== undefined ? { toolLifecycleStatus } : {}),
11861204 ...(toolData !== undefined ? { toolData } : {}),
......@@ -1203,11 +1221,14 @@ function deriveToolLifecycleCollapseKey(entry: DerivedWorkLogEntry): string | un
12031221 // progress ticks fold into it, the terminal row wins the label.
12041222 if (
12051223 entry.taskId &&
1206 (entry.activityKind === "task.progress" || entry.activityKind === "task.completed")
1224 (entry.sourceActivityKind === "task.progress" || entry.sourceActivityKind === "task.completed")
12071225 ) {
12081226 return `task${entry.taskId}`;
12091227 }
1210 if (entry.activityKind !== "tool.updated" && entry.activityKind !== "tool.completed") {
1228 if (
1229 entry.sourceActivityKind !== "tool.updated" &&
1230 entry.sourceActivityKind !== "tool.completed"
1231 ) {
12111232 return undefined;
12121233 }
12131234 if (entry.toolCallId) {