| ... | ... | @@ -98,15 +98,22 @@ export interface WorkLogEntry { |
| 98 | 98 | }; |
| 99 | 99 | } |
| 100 | 100 | |
| 101 | const workLogCollapseKey = Symbol(); |
| 102 | |
| 101 | 103 | interface DerivedWorkLogEntry extends WorkLogEntry { |
| 102 | | activityKind: OrchestrationThreadActivity["kind"]; |
| 103 | | collapseKey?: string; |
| 104 | sourceActivityKind: OrchestrationThreadActivity["kind"]; |
| 105 | [workLogCollapseKey]?: string; |
| 104 | 106 | toolCallId?: string; |
| 105 | 107 | isWorkflowCoordinator?: boolean; |
| 106 | 108 | /** Shell/monitor/plan tasks: ordinary work-log rows, never spawn CTAs. */ |
| 107 | 109 | isBackgroundTask?: boolean; |
| 108 | 110 | } |
| 109 | 111 | |
| 112 | const derivedWorkLogEntryByActivity = new WeakMap< |
| 113 | OrchestrationThreadActivity, |
| 114 | DerivedWorkLogEntry |
| 115 | >(); |
| 116 | |
| 110 | 117 | export interface PendingApproval { |
| 111 | 118 | requestId: ApprovalRequestId; |
| 112 | 119 | requestKind: "command" | "file-read" | "file-change"; |
| ... | ... | @@ -854,10 +861,7 @@ export function deriveWorkLogEntries( |
| 854 | 861 | if (isAgentInternalActivity(activity)) continue; |
| 855 | 862 | entries.push(toDerivedWorkLogEntry(activity)); |
| 856 | 863 | } |
| 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); |
| 861 | 865 | } |
| 862 | 866 | |
| 863 | 867 | function isPlanBoundaryToolActivity(activity: OrchestrationThreadActivity): boolean { |
| ... | ... | @@ -892,6 +896,10 @@ function extractWorkLogToolLifecycleStatus( |
| 892 | 896 | } |
| 893 | 897 | |
| 894 | 898 | function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWorkLogEntry { |
| 899 | const cachedEntry = derivedWorkLogEntryByActivity.get(activity); |
| 900 | if (cachedEntry) { |
| 901 | return cachedEntry; |
| 902 | } |
| 895 | 903 | const payload = |
| 896 | 904 | activity.payload && typeof activity.payload === "object" |
| 897 | 905 | ? (activity.payload as Record<string, unknown>) |
| ... | ... | @@ -935,7 +943,7 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo |
| 935 | 943 | : activity.tone === "approval" |
| 936 | 944 | ? "info" |
| 937 | 945 | : activity.tone, |
| 938 | | activityKind: activity.kind, |
| 946 | sourceActivityKind: activity.kind, |
| 939 | 947 | }; |
| 940 | 948 | const itemType = extractWorkLogItemType(payload); |
| 941 | 949 | const requestKind = extractWorkLogRequestKind(payload); |
| ... | ... | @@ -994,8 +1002,9 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo |
| 994 | 1002 | } |
| 995 | 1003 | const collapseKey = deriveToolLifecycleCollapseKey(entry); |
| 996 | 1004 | if (collapseKey) { |
| 997 | | entry.collapseKey = collapseKey; |
| 1005 | entry[workLogCollapseKey] = collapseKey; |
| 998 | 1006 | } |
| 1007 | derivedWorkLogEntryByActivity.set(activity, entry); |
| 999 | 1008 | return entry; |
| 1000 | 1009 | } |
| 1001 | 1010 | |
| ... | ... | @@ -1026,7 +1035,10 @@ function agentSpawnGroupKey(entry: DerivedWorkLogEntry): string { |
| 1026 | 1035 | } |
| 1027 | 1036 | |
| 1028 | 1037 | function 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 | ) { |
| 1030 | 1042 | return undefined; |
| 1031 | 1043 | } |
| 1032 | 1044 | return entry.toolCallId ? `tool:${entry.turnId ?? "no-turn"}:${entry.toolCallId}` : undefined; |
| ... | ... | @@ -1053,9 +1065,9 @@ function collapseDerivedWorkLogEntries( |
| 1053 | 1065 | const isTaskRow = |
| 1054 | 1066 | entry.taskId !== undefined && |
| 1055 | 1067 | !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"); |
| 1059 | 1071 | if (isTaskRow && entry.taskId !== undefined) { |
| 1060 | 1072 | const rememberedKey = groupKeyByTaskId.get(entry.taskId); |
| 1061 | 1073 | const groupKey = rememberedKey ?? agentSpawnGroupKey(entry); |
| ... | ... | @@ -1131,19 +1143,25 @@ function shouldCollapseToolLifecycleEntries( |
| 1131 | 1143 | previous: DerivedWorkLogEntry, |
| 1132 | 1144 | next: DerivedWorkLogEntry, |
| 1133 | 1145 | ): boolean { |
| 1134 | | if (previous.activityKind !== "tool.updated" && previous.activityKind !== "tool.completed") { |
| 1146 | if ( |
| 1147 | previous.sourceActivityKind !== "tool.updated" && |
| 1148 | previous.sourceActivityKind !== "tool.completed" |
| 1149 | ) { |
| 1135 | 1150 | return false; |
| 1136 | 1151 | } |
| 1137 | | if (next.activityKind !== "tool.updated" && next.activityKind !== "tool.completed") { |
| 1152 | if (next.sourceActivityKind !== "tool.updated" && next.sourceActivityKind !== "tool.completed") { |
| 1138 | 1153 | return false; |
| 1139 | 1154 | } |
| 1140 | 1155 | if (previous.turnId !== next.turnId) { |
| 1141 | 1156 | return false; |
| 1142 | 1157 | } |
| 1143 | | if (previous.activityKind === "tool.completed") { |
| 1158 | if (previous.sourceActivityKind === "tool.completed") { |
| 1144 | 1159 | return false; |
| 1145 | 1160 | } |
| 1146 | | if (previous.collapseKey !== undefined && previous.collapseKey === next.collapseKey) { |
| 1161 | if ( |
| 1162 | previous[workLogCollapseKey] !== undefined && |
| 1163 | previous[workLogCollapseKey] === next[workLogCollapseKey] |
| 1164 | ) { |
| 1147 | 1165 | return true; |
| 1148 | 1166 | } |
| 1149 | 1167 | return ( |
| ... | ... | @@ -1166,7 +1184,7 @@ function mergeDerivedWorkLogEntries( |
| 1166 | 1184 | const toolTitle = next.toolTitle ?? previous.toolTitle; |
| 1167 | 1185 | const itemType = next.itemType ?? previous.itemType; |
| 1168 | 1186 | const requestKind = next.requestKind ?? previous.requestKind; |
| 1169 | | const collapseKey = next.collapseKey ?? previous.collapseKey; |
| 1187 | const collapseKey = next[workLogCollapseKey] ?? previous[workLogCollapseKey]; |
| 1170 | 1188 | const toolCallId = next.toolCallId ?? previous.toolCallId; |
| 1171 | 1189 | const toolLifecycleStatus = next.toolLifecycleStatus ?? previous.toolLifecycleStatus; |
| 1172 | 1190 | const toolData = next.toolData ?? previous.toolData; |
| ... | ... | @@ -1180,7 +1198,7 @@ function mergeDerivedWorkLogEntries( |
| 1180 | 1198 | ...(toolTitle ? { toolTitle } : {}), |
| 1181 | 1199 | ...(itemType ? { itemType } : {}), |
| 1182 | 1200 | ...(requestKind ? { requestKind } : {}), |
| 1183 | | ...(collapseKey ? { collapseKey } : {}), |
| 1201 | ...(collapseKey ? { [workLogCollapseKey]: collapseKey } : {}), |
| 1184 | 1202 | ...(toolCallId ? { toolCallId } : {}), |
| 1185 | 1203 | ...(toolLifecycleStatus !== undefined ? { toolLifecycleStatus } : {}), |
| 1186 | 1204 | ...(toolData !== undefined ? { toolData } : {}), |
| ... | ... | @@ -1203,11 +1221,14 @@ function deriveToolLifecycleCollapseKey(entry: DerivedWorkLogEntry): string | un |
| 1203 | 1221 | // progress ticks fold into it, the terminal row wins the label. |
| 1204 | 1222 | if ( |
| 1205 | 1223 | entry.taskId && |
| 1206 | | (entry.activityKind === "task.progress" || entry.activityKind === "task.completed") |
| 1224 | (entry.sourceActivityKind === "task.progress" || entry.sourceActivityKind === "task.completed") |
| 1207 | 1225 | ) { |
| 1208 | 1226 | return `task${entry.taskId}`; |
| 1209 | 1227 | } |
| 1210 | | if (entry.activityKind !== "tool.updated" && entry.activityKind !== "tool.completed") { |
| 1228 | if ( |
| 1229 | entry.sourceActivityKind !== "tool.updated" && |
| 1230 | entry.sourceActivityKind !== "tool.completed" |
| 1231 | ) { |
| 1211 | 1232 | return undefined; |
| 1212 | 1233 | } |
| 1213 | 1234 | if (entry.toolCallId) { |