@agents-tower/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/adapters/claude.d.ts +2 -0
  2. package/dist/adapters/claude.js +43 -0
  3. package/dist/adapters/claude.js.map +1 -0
  4. package/dist/adapters/codex-cloud.d.ts +4 -0
  5. package/dist/adapters/codex-cloud.js +80 -0
  6. package/dist/adapters/codex-cloud.js.map +1 -0
  7. package/dist/adapters/codex-local.d.ts +17 -0
  8. package/dist/adapters/codex-local.js +180 -0
  9. package/dist/adapters/codex-local.js.map +1 -0
  10. package/dist/adapters/cursor-cloud.d.ts +2 -0
  11. package/dist/adapters/cursor-cloud.js +54 -0
  12. package/dist/adapters/cursor-cloud.js.map +1 -0
  13. package/dist/adapters/cursor-local.d.ts +2 -0
  14. package/dist/adapters/cursor-local.js +37 -0
  15. package/dist/adapters/cursor-local.js.map +1 -0
  16. package/dist/adapters/helpers.d.ts +15 -0
  17. package/dist/adapters/helpers.js +33 -0
  18. package/dist/adapters/helpers.js.map +1 -0
  19. package/dist/adapters/hermes.d.ts +2 -0
  20. package/dist/adapters/hermes.js +41 -0
  21. package/dist/adapters/hermes.js.map +1 -0
  22. package/dist/adapters/index.d.ts +2 -0
  23. package/dist/adapters/index.js +37 -0
  24. package/dist/adapters/index.js.map +1 -0
  25. package/dist/adapters/openclaw.d.ts +2 -0
  26. package/dist/adapters/openclaw.js +41 -0
  27. package/dist/adapters/openclaw.js.map +1 -0
  28. package/dist/adapters/presence.d.ts +2 -0
  29. package/dist/adapters/presence.js +36 -0
  30. package/dist/adapters/presence.js.map +1 -0
  31. package/dist/adapters/static-source.d.ts +15 -0
  32. package/dist/adapters/static-source.js +41 -0
  33. package/dist/adapters/static-source.js.map +1 -0
  34. package/dist/adapters/types.d.ts +44 -0
  35. package/dist/adapters/types.js +3 -0
  36. package/dist/adapters/types.js.map +1 -0
  37. package/dist/app-server.d.ts +94 -0
  38. package/dist/app-server.js +382 -0
  39. package/dist/app-server.js.map +1 -0
  40. package/dist/app-settings.d.ts +52 -0
  41. package/dist/app-settings.js +268 -0
  42. package/dist/app-settings.js.map +1 -0
  43. package/dist/appearance.d.ts +6 -0
  44. package/dist/appearance.js +84 -0
  45. package/dist/appearance.js.map +1 -0
  46. package/dist/aseprite.d.ts +4 -0
  47. package/dist/aseprite.js +42 -0
  48. package/dist/aseprite.js.map +1 -0
  49. package/dist/claude-agent-sdk.d.ts +44 -0
  50. package/dist/claude-agent-sdk.js +347 -0
  51. package/dist/claude-agent-sdk.js.map +1 -0
  52. package/dist/claude-home-cache.d.ts +61 -0
  53. package/dist/claude-home-cache.js +438 -0
  54. package/dist/claude-home-cache.js.map +1 -0
  55. package/dist/claude.d.ts +154 -0
  56. package/dist/claude.js +3495 -0
  57. package/dist/claude.js.map +1 -0
  58. package/dist/cloud.d.ts +2 -0
  59. package/dist/cloud.js +25 -0
  60. package/dist/cloud.js.map +1 -0
  61. package/dist/codex-command.d.ts +25 -0
  62. package/dist/codex-command.js +235 -0
  63. package/dist/codex-command.js.map +1 -0
  64. package/dist/codex-session-files.d.ts +8 -0
  65. package/dist/codex-session-files.js +509 -0
  66. package/dist/codex-session-files.js.map +1 -0
  67. package/dist/codex-thread-query.d.ts +14 -0
  68. package/dist/codex-thread-query.js +82 -0
  69. package/dist/codex-thread-query.js.map +1 -0
  70. package/dist/cursor-cloud-data.d.ts +34 -0
  71. package/dist/cursor-cloud-data.js +344 -0
  72. package/dist/cursor-cloud-data.js.map +1 -0
  73. package/dist/cursor-lib/local-discovery.d.ts +17 -0
  74. package/dist/cursor-lib/local-discovery.js +251 -0
  75. package/dist/cursor-lib/local-discovery.js.map +1 -0
  76. package/dist/cursor-lib/shared.d.ts +5 -0
  77. package/dist/cursor-lib/shared.js +88 -0
  78. package/dist/cursor-lib/shared.js.map +1 -0
  79. package/dist/cursor.d.ts +8 -0
  80. package/dist/cursor.js +1584 -0
  81. package/dist/cursor.js.map +1 -0
  82. package/dist/domain/codex-turn-semantics.d.ts +4 -0
  83. package/dist/domain/codex-turn-semantics.js +49 -0
  84. package/dist/domain/codex-turn-semantics.js.map +1 -0
  85. package/dist/domain/workload-policy.d.ts +8 -0
  86. package/dist/domain/workload-policy.js +268 -0
  87. package/dist/domain/workload-policy.js.map +1 -0
  88. package/dist/domain/workspace-activity.d.ts +10 -0
  89. package/dist/domain/workspace-activity.js +372 -0
  90. package/dist/domain/workspace-activity.js.map +1 -0
  91. package/dist/goal.d.ts +11 -0
  92. package/dist/goal.js +76 -0
  93. package/dist/goal.js.map +1 -0
  94. package/dist/hermes-hook-install.d.ts +12 -0
  95. package/dist/hermes-hook-install.js +335 -0
  96. package/dist/hermes-hook-install.js.map +1 -0
  97. package/dist/hermes.d.ts +97 -0
  98. package/dist/hermes.js +2446 -0
  99. package/dist/hermes.js.map +1 -0
  100. package/dist/index.d.ts +28 -0
  101. package/dist/index.js +47 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/live-monitor-lib/events.d.ts +43 -0
  104. package/dist/live-monitor-lib/events.js +1463 -0
  105. package/dist/live-monitor-lib/events.js.map +1 -0
  106. package/dist/live-monitor-lib/rollout-hooks.d.ts +13 -0
  107. package/dist/live-monitor-lib/rollout-hooks.js +253 -0
  108. package/dist/live-monitor-lib/rollout-hooks.js.map +1 -0
  109. package/dist/live-monitor.d.ts +102 -0
  110. package/dist/live-monitor.js +1461 -0
  111. package/dist/live-monitor.js.map +1 -0
  112. package/dist/local-thread-selection.d.ts +2 -0
  113. package/dist/local-thread-selection.js +58 -0
  114. package/dist/local-thread-selection.js.map +1 -0
  115. package/dist/openclaw.d.ts +41 -0
  116. package/dist/openclaw.js +715 -0
  117. package/dist/openclaw.js.map +1 -0
  118. package/dist/presence.d.ts +9 -0
  119. package/dist/presence.js +108 -0
  120. package/dist/presence.js.map +1 -0
  121. package/dist/project-identity.d.ts +7 -0
  122. package/dist/project-identity.js +162 -0
  123. package/dist/project-identity.js.map +1 -0
  124. package/dist/project-paths.d.ts +23 -0
  125. package/dist/project-paths.js +394 -0
  126. package/dist/project-paths.js.map +1 -0
  127. package/dist/project-storage.d.ts +7 -0
  128. package/dist/project-storage.js +79 -0
  129. package/dist/project-storage.js.map +1 -0
  130. package/dist/room-config.d.ts +21 -0
  131. package/dist/room-config.js +230 -0
  132. package/dist/room-config.js.map +1 -0
  133. package/dist/services/project-discovery.d.ts +1 -0
  134. package/dist/services/project-discovery.js +16 -0
  135. package/dist/services/project-discovery.js.map +1 -0
  136. package/dist/services/project-live-monitor.d.ts +1 -0
  137. package/dist/services/project-live-monitor.js +6 -0
  138. package/dist/services/project-live-monitor.js.map +1 -0
  139. package/dist/services/project-snapshot-coordinator.d.ts +49 -0
  140. package/dist/services/project-snapshot-coordinator.js +152 -0
  141. package/dist/services/project-snapshot-coordinator.js.map +1 -0
  142. package/dist/services/refresh-scheduler.d.ts +8 -0
  143. package/dist/services/refresh-scheduler.js +44 -0
  144. package/dist/services/refresh-scheduler.js.map +1 -0
  145. package/dist/services/snapshot-assembler.d.ts +8 -0
  146. package/dist/services/snapshot-assembler.js +167 -0
  147. package/dist/services/snapshot-assembler.js.map +1 -0
  148. package/dist/services/thread-store.d.ts +4 -0
  149. package/dist/services/thread-store.js +8 -0
  150. package/dist/services/thread-store.js.map +1 -0
  151. package/dist/snapshot-lib/activity-summary.d.ts +3 -0
  152. package/dist/snapshot-lib/activity-summary.js +220 -0
  153. package/dist/snapshot-lib/activity-summary.js.map +1 -0
  154. package/dist/snapshot-lib/dashboard-builder.d.ts +21 -0
  155. package/dist/snapshot-lib/dashboard-builder.js +146 -0
  156. package/dist/snapshot-lib/dashboard-builder.js.map +1 -0
  157. package/dist/snapshot-lib/thread-summary.d.ts +27 -0
  158. package/dist/snapshot-lib/thread-summary.js +953 -0
  159. package/dist/snapshot-lib/thread-summary.js.map +1 -0
  160. package/dist/snapshot.d.ts +2 -0
  161. package/dist/snapshot.js +21 -0
  162. package/dist/snapshot.js.map +1 -0
  163. package/dist/types.d.ts +368 -0
  164. package/dist/types.js +3 -0
  165. package/dist/types.js.map +1 -0
  166. package/dist/utils/json.d.ts +4 -0
  167. package/dist/utils/json.js +29 -0
  168. package/dist/utils/json.js.map +1 -0
  169. package/dist/utils/text.d.ts +3 -0
  170. package/dist/utils/text.js +26 -0
  171. package/dist/utils/text.js.map +1 -0
  172. package/dist/web-search.d.ts +1 -0
  173. package/dist/web-search.js +52 -0
  174. package/dist/web-search.js.map +1 -0
  175. package/dist/workload.d.ts +1 -0
  176. package/dist/workload.js +12 -0
  177. package/dist/workload.js.map +1 -0
  178. package/package.json +19 -0
@@ -0,0 +1,953 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatGoalCommandLabel = formatGoalCommandLabel;
4
+ exports.pickThreadLabel = pickThreadLabel;
5
+ exports.latestAgentMessageForThread = latestAgentMessageForThread;
6
+ exports.syncSummaryWithLatestThreadMessage = syncSummaryWithLatestThreadMessage;
7
+ exports.parseThreadSourceMeta = parseThreadSourceMeta;
8
+ exports.parentThreadIdForThread = parentThreadIdForThread;
9
+ exports.isStaleActiveSubagentThread = isStaleActiveSubagentThread;
10
+ exports.inferThreadAgentRole = inferThreadAgentRole;
11
+ exports.isOngoingThread = isOngoingThread;
12
+ exports.summariseThread = summariseThread;
13
+ exports.buildActivityEventFromDashboardEvent = buildActivityEventFromDashboardEvent;
14
+ exports.applyRecentActivityEvent = applyRecentActivityEvent;
15
+ const text_1 = require("../utils/text");
16
+ const web_search_1 = require("../web-search");
17
+ const codex_turn_semantics_1 = require("../domain/codex-turn-semantics");
18
+ const DONE_WINDOW_MS = 15 * 60 * 1000;
19
+ const USER_PROMPT_ACTIVE_WINDOW_MS = 30 * 1000;
20
+ const EVENT_ACTIVITY_WINDOW_MS = 90 * 1000;
21
+ function isFreshActivityEvent(event, threadUpdatedAtMs, nowMs = Date.now()) {
22
+ const createdAtMs = Date.parse(event.createdAt);
23
+ return Number.isFinite(createdAtMs)
24
+ && createdAtMs >= threadUpdatedAtMs - EVENT_ACTIVITY_WINDOW_MS
25
+ && createdAtMs >= nowMs - EVENT_ACTIVITY_WINDOW_MS;
26
+ }
27
+ function recentActivityEventPriority(event) {
28
+ if (event.kind === "subagent") {
29
+ return 48;
30
+ }
31
+ if (event.kind === "message") {
32
+ if (event.method === "item/completed" && event.itemType === "agentMessage") {
33
+ return 40;
34
+ }
35
+ if (event.method === "item/agentMessage/delta") {
36
+ return 35;
37
+ }
38
+ if (event.method === "item/started" && event.itemType === "agentMessage") {
39
+ return 30;
40
+ }
41
+ if (event.method === "thread/read/agentMessage") {
42
+ return 10;
43
+ }
44
+ return 20;
45
+ }
46
+ if (event.kind === "fileChange") {
47
+ return 50;
48
+ }
49
+ if (event.kind === "command") {
50
+ return 45;
51
+ }
52
+ return 0;
53
+ }
54
+ function compareRecentActivityEvents(left, right) {
55
+ const priorityDelta = recentActivityEventPriority(right) - recentActivityEventPriority(left);
56
+ if (priorityDelta !== 0) {
57
+ return priorityDelta;
58
+ }
59
+ return right.createdAt.localeCompare(left.createdAt);
60
+ }
61
+ function threadTurns(thread) {
62
+ return Array.isArray(thread.turns) ? thread.turns : [];
63
+ }
64
+ function asRecord(value) {
65
+ return typeof value === "object" && value ? value : null;
66
+ }
67
+ function sourceRecord(thread) {
68
+ return asRecord(thread.source);
69
+ }
70
+ function sourceStringValue(record, key) {
71
+ const value = record?.[key];
72
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
73
+ }
74
+ function sourceKindForThread(thread) {
75
+ if (typeof thread.source === "string") {
76
+ return thread.source;
77
+ }
78
+ const source = sourceRecord(thread);
79
+ return sourceStringValue(source, "sourceKind")
80
+ ?? sourceStringValue(source, "source_kind")
81
+ ?? sourceStringValue(source, "kind")
82
+ ?? sourceStringValue(source, "type")
83
+ ?? "unknown";
84
+ }
85
+ function subAgentSourceForThread(thread) {
86
+ const source = sourceRecord(thread);
87
+ return source
88
+ ? source.subAgent
89
+ ?? source.subagent
90
+ ?? source.sub_agent
91
+ ?? source.subAgentThreadSpawn
92
+ ?? source.subagentThreadSpawn
93
+ ?? source.sub_agent_thread_spawn
94
+ ?? null
95
+ : null;
96
+ }
97
+ function recordHasThreadSpawnMetadata(record) {
98
+ return Boolean(record
99
+ && (sourceStringValue(record, "parent_thread_id")
100
+ || sourceStringValue(record, "parentThreadId")
101
+ || sourceStringValue(record, "agent_nickname")
102
+ || sourceStringValue(record, "agentNickname")
103
+ || sourceStringValue(record, "agent_path")
104
+ || sourceStringValue(record, "agentPath")
105
+ || sourceStringValue(record, "agent_role")
106
+ || sourceStringValue(record, "agentRole")
107
+ || sourceStringValue(record, "agent_type")
108
+ || sourceStringValue(record, "agentType")
109
+ || typeof record.depth === "number"));
110
+ }
111
+ function threadSpawnSourceForThread(thread) {
112
+ const source = sourceRecord(thread);
113
+ const sourceThreadSpawn = asRecord(source?.thread_spawn) ?? asRecord(source?.threadSpawn);
114
+ if (sourceThreadSpawn) {
115
+ return sourceThreadSpawn;
116
+ }
117
+ if (recordHasThreadSpawnMetadata(source)) {
118
+ return source;
119
+ }
120
+ const subAgentSource = subAgentSourceForThread(thread);
121
+ const subAgentRecord = asRecord(subAgentSource);
122
+ const threadSpawn = asRecord(subAgentRecord?.thread_spawn) ?? asRecord(subAgentRecord?.threadSpawn);
123
+ if (threadSpawn) {
124
+ return threadSpawn;
125
+ }
126
+ if (recordHasThreadSpawnMetadata(subAgentRecord)) {
127
+ return subAgentRecord;
128
+ }
129
+ const threadRecord = thread;
130
+ return recordHasThreadSpawnMetadata(threadRecord) ? threadRecord : null;
131
+ }
132
+ function sourceAgentNickname(thread) {
133
+ const threadSpawn = threadSpawnSourceForThread(thread);
134
+ return sourceStringValue(threadSpawn, "agent_nickname") ?? sourceStringValue(threadSpawn, "agentNickname");
135
+ }
136
+ function sourceAgentRole(thread) {
137
+ const threadSpawn = threadSpawnSourceForThread(thread);
138
+ return sourceStringValue(threadSpawn, "agent_role")
139
+ ?? sourceStringValue(threadSpawn, "agentRole")
140
+ ?? sourceStringValue(threadSpawn, "agent_type")
141
+ ?? sourceStringValue(threadSpawn, "agentType");
142
+ }
143
+ function sourceAgentPath(thread) {
144
+ const threadSpawn = threadSpawnSourceForThread(thread);
145
+ return sourceStringValue(threadSpawn, "agent_path") ?? sourceStringValue(threadSpawn, "agentPath");
146
+ }
147
+ function formatGoalCommandLabel(value) {
148
+ return value.replace(/(^|[\s(\x5B\x7B<"'])\/goal(?=$|[\s)\]\x7D,.!?:;"'>])/g, "$1🎯");
149
+ }
150
+ function pickThreadLabel(thread) {
151
+ const agentNickname = thread.agentNickname ?? sourceAgentNickname(thread);
152
+ const agentPath = sourceAgentPath(thread);
153
+ const agentPathName = agentPath?.split("/").filter(Boolean).at(-1) ?? null;
154
+ const label = (agentNickname ??
155
+ agentPathName ??
156
+ thread.name ??
157
+ (0, text_1.shortenText)(thread.preview || thread.id, 42));
158
+ return formatGoalCommandLabel(label);
159
+ }
160
+ function extractStringArray(value, key) {
161
+ if (!Array.isArray(value)) {
162
+ return [];
163
+ }
164
+ return value
165
+ .map((entry) => (typeof entry === "object" && entry && typeof entry[key] === "string"
166
+ ? entry[key]
167
+ : null))
168
+ .filter((entry) => Boolean(entry));
169
+ }
170
+ function extractNumberValue(value, ...keys) {
171
+ if (typeof value !== "object" || !value) {
172
+ return undefined;
173
+ }
174
+ const record = value;
175
+ for (const key of keys) {
176
+ const candidate = record[key];
177
+ if (typeof candidate === "number" && Number.isFinite(candidate)) {
178
+ return candidate;
179
+ }
180
+ }
181
+ return undefined;
182
+ }
183
+ function extractReceiverThreadIds(item) {
184
+ const receiverThreadIds = item.receiverThreadIds ?? item.receiver_thread_ids;
185
+ if (Array.isArray(receiverThreadIds)) {
186
+ return receiverThreadIds.filter((entry) => typeof entry === "string" && entry.length > 0);
187
+ }
188
+ const receiverThreadId = item.receiverThreadId ?? item.receiver_thread_id;
189
+ return typeof receiverThreadId === "string" && receiverThreadId.length > 0 ? [receiverThreadId] : [];
190
+ }
191
+ function normalizeCollabToolName(tool) {
192
+ const normalized = tool.replace(/[_\s-]+/g, "").toLowerCase();
193
+ if (normalized === "spawnagent" || normalized === "spawnagents") {
194
+ return "spawn";
195
+ }
196
+ if (normalized === "waitagent" || normalized === "wait") {
197
+ return "wait";
198
+ }
199
+ if (normalized === "sendinput" || normalized === "sendmessage" || normalized === "followuptask") {
200
+ return "send";
201
+ }
202
+ if (normalized === "closeagent") {
203
+ return "close";
204
+ }
205
+ if (normalized === "resumeagent") {
206
+ return "resume";
207
+ }
208
+ return normalized;
209
+ }
210
+ function collabToolDetail(item) {
211
+ const rawTool = typeof item.tool === "string" ? item.tool : "subagent";
212
+ const receiverCount = extractReceiverThreadIds(item).length;
213
+ switch (normalizeCollabToolName(rawTool)) {
214
+ case "spawn":
215
+ return receiverCount > 0
216
+ ? `Spawning ${receiverCount} subagent${receiverCount === 1 ? "" : "s"}`
217
+ : "Spawning subagent";
218
+ case "wait":
219
+ return receiverCount > 1 ? `Waiting on ${receiverCount} subagents` : "Waiting on subagents";
220
+ case "send":
221
+ return receiverCount > 1 ? `Messaging ${receiverCount} subagents` : "Messaging subagent";
222
+ case "close":
223
+ return receiverCount > 1 ? `Closing ${receiverCount} subagents` : "Closing subagent";
224
+ case "resume":
225
+ return receiverCount > 1 ? `Resuming ${receiverCount} subagents` : "Resuming subagent";
226
+ default:
227
+ return rawTool;
228
+ }
229
+ }
230
+ function summarizeFileChange(item) {
231
+ const primaryChange = Array.isArray(item.changes)
232
+ ? item.changes.find((entry) => typeof entry === "object" && entry && typeof entry.path === "string")
233
+ : undefined;
234
+ const paths = extractStringArray(item.changes, "path");
235
+ const primaryPath = paths[0] ?? null;
236
+ const changeKind = typeof primaryChange?.kind === "string" ? primaryChange.kind : "edit";
237
+ const action = changeKind === "create" ? "created"
238
+ : changeKind === "delete" ? "deleted"
239
+ : changeKind === "move" || changeKind === "rename" ? "moved"
240
+ : "edited";
241
+ return {
242
+ action,
243
+ path: primaryPath,
244
+ title: primaryPath ? `${changeKind} ${primaryPath}` : "Editing files",
245
+ isImage: Boolean(primaryPath && /\.(png|jpe?g|gif|webp|svg|bmp)$/i.test(primaryPath)),
246
+ linesAdded: extractNumberValue(primaryChange, "linesAdded", "lines_added", "added") ?? extractNumberValue(item, "linesAdded", "lines_added"),
247
+ linesRemoved: extractNumberValue(primaryChange, "linesRemoved", "lines_removed", "removed") ?? extractNumberValue(item, "linesRemoved", "lines_removed"),
248
+ paths
249
+ };
250
+ }
251
+ function extractTextContent(value) {
252
+ if (!Array.isArray(value)) {
253
+ return [];
254
+ }
255
+ return value
256
+ .map((entry) => {
257
+ if (typeof entry === "object"
258
+ && entry
259
+ && typeof entry.text === "string") {
260
+ return entry.text;
261
+ }
262
+ return null;
263
+ })
264
+ .filter((entry) => Boolean(entry));
265
+ }
266
+ function extractPathsFromText(text) {
267
+ const matches = text.match(/(?:[A-Za-z]:[\\/][^\s`'"]+|\/[^\s`'"]+)/g) ?? [];
268
+ return Array.from(new Set(matches));
269
+ }
270
+ function summarizePlanItem(item) {
271
+ const explanation = typeof item.explanation === "string" ? item.explanation : null;
272
+ if ((0, text_1.isMeaningfulText)(explanation)) {
273
+ return (0, text_1.shortenText)(explanation, 88);
274
+ }
275
+ const summary = typeof item.summary === "string" ? item.summary : null;
276
+ if ((0, text_1.isMeaningfulText)(summary)) {
277
+ return (0, text_1.shortenText)(summary, 88);
278
+ }
279
+ const text = typeof item.text === "string" ? item.text : null;
280
+ if ((0, text_1.isMeaningfulText)(text)) {
281
+ return (0, text_1.shortenText)(text, 88);
282
+ }
283
+ const plan = Array.isArray(item.plan) ? item.plan : [];
284
+ const entries = plan
285
+ .map((entry) => (typeof entry === "object" && entry ? entry : null))
286
+ .filter((entry) => Boolean(entry))
287
+ .map((entry) => ({
288
+ step: typeof entry.step === "string" ? entry.step : null,
289
+ status: typeof entry.status === "string" ? entry.status : null
290
+ }))
291
+ .filter((entry) => (0, text_1.isMeaningfulText)(entry.step));
292
+ const current = entries.find((entry) => entry.status === "inProgress")
293
+ ?? entries.find((entry) => entry.status === "pending")
294
+ ?? entries.at(-1)
295
+ ?? null;
296
+ if (!current?.step) {
297
+ return "Planning";
298
+ }
299
+ if (current.status === "inProgress") {
300
+ return (0, text_1.shortenText)(`Planning: ${current.step}`, 88);
301
+ }
302
+ if (current.status === "pending") {
303
+ return (0, text_1.shortenText)(`Queued: ${current.step}`, 88);
304
+ }
305
+ return (0, text_1.shortenText)(current.step, 88);
306
+ }
307
+ function summarizeFailedItemDetail(item, fallback) {
308
+ const errorRecord = typeof item.error === "object" && item.error ? item.error : null;
309
+ const errorMessage = (errorRecord && typeof errorRecord.message === "string" ? errorRecord.message : null)
310
+ || (errorRecord && typeof errorRecord.text === "string" ? errorRecord.text : null)
311
+ || (typeof item.error_message === "string" ? item.error_message : null)
312
+ || (typeof item.reason === "string" ? item.reason : null)
313
+ || (typeof item.message === "string" ? item.message : null);
314
+ if ((0, text_1.isMeaningfulText)(errorMessage)) {
315
+ return (0, text_1.shortenText)(errorMessage, 88);
316
+ }
317
+ return fallback;
318
+ }
319
+ function describeAgentMessage(item) {
320
+ const candidateText = typeof item.text === "string" ? item.text : null;
321
+ const text = (0, text_1.isMeaningfulText)(candidateText)
322
+ ? candidateText
323
+ : "Responding";
324
+ return {
325
+ detail: (0, text_1.shortenText)(text, 88),
326
+ paths: extractPathsFromText(text)
327
+ };
328
+ }
329
+ function latestAgentMessageForThread(thread) {
330
+ const turns = threadTurns(thread);
331
+ for (const turn of [...turns].reverse()) {
332
+ for (const item of [...turn.items].reverse()) {
333
+ if (item.type !== "agentMessage" || typeof item.text !== "string") {
334
+ continue;
335
+ }
336
+ const text = (0, text_1.shortenText)(item.text, 240);
337
+ if ((0, text_1.isMeaningfulText)(text)) {
338
+ return text;
339
+ }
340
+ }
341
+ }
342
+ if (turns.length === 0) {
343
+ const preview = (0, text_1.shortenText)(thread.preview || "", 240);
344
+ return (0, text_1.isMeaningfulText)(preview) ? preview : null;
345
+ }
346
+ return null;
347
+ }
348
+ function syncSummaryWithLatestThreadMessage(thread, summary, recentEvents = []) {
349
+ const latestMessage = latestAgentMessageForThread(thread);
350
+ if (!latestMessage || summary.activityEvent?.type !== "agentMessage") {
351
+ return { latestMessage, summary };
352
+ }
353
+ if (summary.detail === latestMessage && summary.activityEvent.title === latestMessage) {
354
+ return { latestMessage, summary };
355
+ }
356
+ const latestRecentMessageEvent = recentEvents
357
+ .filter((event) => event.threadId === thread.id && event.kind === "message")
358
+ .sort(compareRecentActivityEvents)[0] ?? null;
359
+ const latestRecentMessageCreatedAtMs = latestRecentMessageEvent
360
+ ? Date.parse(latestRecentMessageEvent.createdAt)
361
+ : Number.NaN;
362
+ const summaryUsesRecentMessage = latestRecentMessageEvent
363
+ && (summary.detail === latestRecentMessageEvent.detail
364
+ || summary.activityEvent.title === latestRecentMessageEvent.detail);
365
+ if (summaryUsesRecentMessage
366
+ && Number.isFinite(latestRecentMessageCreatedAtMs)
367
+ && latestRecentMessageCreatedAtMs > thread.updatedAt * 1000) {
368
+ return { latestMessage, summary };
369
+ }
370
+ return {
371
+ latestMessage,
372
+ summary: {
373
+ ...summary,
374
+ detail: latestMessage,
375
+ activityEvent: {
376
+ ...summary.activityEvent,
377
+ title: latestMessage
378
+ }
379
+ }
380
+ };
381
+ }
382
+ function extractPromptRole(text) {
383
+ if (typeof text !== "string" || text.length === 0) {
384
+ return null;
385
+ }
386
+ const explicitRoleMatch = text.match(/(?:^|\n)\s*Role:\s*([A-Za-z][A-Za-z0-9_-]*)/i);
387
+ if (explicitRoleMatch) {
388
+ return explicitRoleMatch[1].trim().toLowerCase();
389
+ }
390
+ const impliedRoleMatch = text.match(/\bAct as (?:an?|the)?\s*([A-Za-z][A-Za-z0-9_-]*)\s+subagent\b/i);
391
+ return impliedRoleMatch ? impliedRoleMatch[1].trim().toLowerCase() : null;
392
+ }
393
+ function parseThreadSourceMeta(thread) {
394
+ const subAgentSource = subAgentSourceForThread(thread);
395
+ const threadSpawn = threadSpawnSourceForThread(thread);
396
+ const sourceKind = sourceKindForThread(thread);
397
+ if (typeof threadSpawn === "object" && threadSpawn) {
398
+ return {
399
+ sourceKind: "subAgent",
400
+ parentThreadId: typeof threadSpawn.parent_thread_id === "string"
401
+ ? threadSpawn.parent_thread_id
402
+ : typeof threadSpawn.parentThreadId === "string"
403
+ ? threadSpawn.parentThreadId
404
+ : null,
405
+ depth: typeof threadSpawn.depth === "number"
406
+ ? threadSpawn.depth
407
+ : 1,
408
+ agentNickname: sourceAgentNickname(thread),
409
+ agentRole: sourceAgentRole(thread)
410
+ };
411
+ }
412
+ if (typeof thread.source === "string") {
413
+ return {
414
+ sourceKind,
415
+ parentThreadId: null,
416
+ depth: sourceKind.startsWith("subAgent") ? 1 : 0,
417
+ agentNickname: null,
418
+ agentRole: null
419
+ };
420
+ }
421
+ if (subAgentSource === "review" || subAgentSource === "compact") {
422
+ return {
423
+ sourceKind: subAgentSource === "review" ? "subAgentReview" : "subAgentCompact",
424
+ parentThreadId: null,
425
+ depth: 1,
426
+ agentNickname: null,
427
+ agentRole: null
428
+ };
429
+ }
430
+ if (subAgentSource === "memory_consolidation" || typeof subAgentSource === "object") {
431
+ return {
432
+ sourceKind: "subAgentOther",
433
+ parentThreadId: null,
434
+ depth: 1,
435
+ agentNickname: null,
436
+ agentRole: null
437
+ };
438
+ }
439
+ return {
440
+ sourceKind,
441
+ parentThreadId: null,
442
+ depth: 0,
443
+ agentNickname: null,
444
+ agentRole: null
445
+ };
446
+ }
447
+ function parentThreadIdForThread(thread) {
448
+ return parseThreadSourceMeta(thread).parentThreadId;
449
+ }
450
+ const ACTIVE_SUBAGENT_THREAD_WINDOW_MS = 20 * 60 * 1000;
451
+ function isStaleActiveSubagentThread(thread, nowMs = Date.now()) {
452
+ if (thread.status.type !== "active" || !parentThreadIdForThread(thread)) {
453
+ return false;
454
+ }
455
+ if (threadTurns(thread).some((turn) => turn.status === "inProgress")) {
456
+ return false;
457
+ }
458
+ const updatedAtMs = thread.updatedAt * 1000;
459
+ return Number.isFinite(updatedAtMs) && nowMs - updatedAtMs > ACTIVE_SUBAGENT_THREAD_WINDOW_MS;
460
+ }
461
+ function inferThreadAgentRole(thread, sourceKind) {
462
+ if (sourceKind === "subAgent") {
463
+ const previewRole = extractPromptRole(thread.preview);
464
+ if (previewRole && previewRole !== "default") {
465
+ return previewRole;
466
+ }
467
+ const userMessageRole = [...threadTurns(thread)]
468
+ .reverse()
469
+ .flatMap((turn) => [...turn.items].reverse())
470
+ .find((item) => item.type === "userMessage");
471
+ const extractedUserRole = userMessageRole
472
+ ? extractPromptRole(extractTextContent(userMessageRole.content)[0] ?? null)
473
+ : null;
474
+ if (extractedUserRole && extractedUserRole !== "default") {
475
+ return extractedUserRole;
476
+ }
477
+ }
478
+ return thread.agentRole ?? sourceAgentRole(thread);
479
+ }
480
+ const FRESH_SPAWNED_THREAD_WINDOW_MS = 2 * 60 * 1000;
481
+ // Just-sent desktop prompts can appear as notLoaded/no-turn rows before hydration catches up.
482
+ const FRESH_NOT_LOADED_THREAD_UPDATE_WINDOW_MS = 8 * 1000;
483
+ const QUIET_LIVE_THREAD_WINDOW_MS = 3 * 60 * 1000;
484
+ function isFreshSpawnedDetachedThread(thread) {
485
+ if (thread.status.type !== "notLoaded") {
486
+ return false;
487
+ }
488
+ if (threadTurns(thread).length > 0) {
489
+ return false;
490
+ }
491
+ if (thread.source !== "exec" && typeof thread.source !== "object") {
492
+ return false;
493
+ }
494
+ const createdAtMs = thread.createdAt * 1000;
495
+ if (!Number.isFinite(createdAtMs)) {
496
+ return false;
497
+ }
498
+ return Date.now() - createdAtMs <= FRESH_SPAWNED_THREAD_WINDOW_MS;
499
+ }
500
+ function isFreshNotLoadedUnhydratedThread(thread) {
501
+ if (thread.status.type !== "notLoaded") {
502
+ return false;
503
+ }
504
+ if (threadTurns(thread).length > 0) {
505
+ return false;
506
+ }
507
+ const updatedAtMs = thread.updatedAt * 1000;
508
+ if (!Number.isFinite(updatedAtMs)) {
509
+ return false;
510
+ }
511
+ return Date.now() - updatedAtMs <= FRESH_NOT_LOADED_THREAD_UPDATE_WINDOW_MS;
512
+ }
513
+ function isFreshNotLoadedNonFinalWorkThread(thread, lastTurn = threadTurns(thread).at(-1), nowMs = Date.now()) {
514
+ if (thread.status.type !== "notLoaded" || !lastTurn) {
515
+ return false;
516
+ }
517
+ if (parentThreadIdForThread(thread)) {
518
+ return false;
519
+ }
520
+ if (lastTurn.status === "failed" || (0, codex_turn_semantics_1.turnHasFinalAnswer)(lastTurn)) {
521
+ return false;
522
+ }
523
+ const updatedAtMs = thread.updatedAt * 1000;
524
+ if (!Number.isFinite(updatedAtMs) || nowMs - updatedAtMs > QUIET_LIVE_THREAD_WINDOW_MS) {
525
+ return false;
526
+ }
527
+ return lastTurn.status === "inProgress" || (0, codex_turn_semantics_1.turnHasNonFinalWorkSignal)(lastTurn);
528
+ }
529
+ function isOngoingThread(thread) {
530
+ if (thread.status.type === "active") {
531
+ const lastTurn = threadTurns(thread).at(-1);
532
+ if (lastTurn && (0, codex_turn_semantics_1.turnHasFinalAnswer)(lastTurn)) {
533
+ return false;
534
+ }
535
+ return !isStaleActiveSubagentThread(thread);
536
+ }
537
+ if (isFreshSpawnedDetachedThread(thread) || isFreshNotLoadedUnhydratedThread(thread)) {
538
+ return true;
539
+ }
540
+ const turns = threadTurns(thread);
541
+ const lastTurn = turns.at(-1);
542
+ return Boolean(lastTurn
543
+ && (lastTurn.status === "inProgress"
544
+ || isFreshNotLoadedNonFinalWorkThread(thread, lastTurn)));
545
+ }
546
+ function selectRelevantItem(items) {
547
+ const priority = [
548
+ "fileChange",
549
+ "commandExecution",
550
+ "dynamicToolCall",
551
+ "mcpToolCall",
552
+ "webSearch",
553
+ "imageView",
554
+ "collabToolCall",
555
+ "collabAgentToolCall",
556
+ "enteredReviewMode",
557
+ "exitedReviewMode",
558
+ "plan",
559
+ "reasoning",
560
+ "contextCompaction",
561
+ "agentMessage",
562
+ "userMessage"
563
+ ];
564
+ for (const type of priority) {
565
+ const match = [...items].reverse().find((item) => item.type === type);
566
+ if (match) {
567
+ return match;
568
+ }
569
+ }
570
+ return items.at(-1) ?? null;
571
+ }
572
+ function summariseThread(thread) {
573
+ const ageMs = Date.now() - thread.updatedAt * 1000;
574
+ const settledRecentState = ageMs <= DONE_WINDOW_MS ? "done" : "idle";
575
+ if (thread.status.type === "systemError") {
576
+ return {
577
+ state: "blocked",
578
+ detail: "System error",
579
+ paths: [thread.cwd],
580
+ activityEvent: null
581
+ };
582
+ }
583
+ if (thread.status.type === "active" && thread.status.activeFlags?.includes("waitingOnApproval")) {
584
+ return {
585
+ state: "blocked",
586
+ detail: "Waiting on approval",
587
+ paths: [thread.cwd],
588
+ activityEvent: null
589
+ };
590
+ }
591
+ if (thread.status.type === "active" && thread.status.activeFlags?.includes("waitingOnUserInput")) {
592
+ return {
593
+ state: "waiting",
594
+ detail: "Waiting on user input",
595
+ paths: [thread.cwd],
596
+ activityEvent: null
597
+ };
598
+ }
599
+ const turns = threadTurns(thread);
600
+ const lastTurn = turns.at(-1);
601
+ if (!lastTurn) {
602
+ const preview = (0, text_1.shortenText)(thread.preview || "", 88);
603
+ const freshSpawnedDetached = isFreshSpawnedDetachedThread(thread);
604
+ const freshNotLoadedUnhydrated = isFreshNotLoadedUnhydratedThread(thread);
605
+ const recentState = ageMs <= DONE_WINDOW_MS ? "done" : "idle";
606
+ return {
607
+ state: thread.status.type === "active" || freshSpawnedDetached || freshNotLoadedUnhydrated ? "planning"
608
+ : recentState,
609
+ detail: preview
610
+ || (thread.status.type === "active" || freshSpawnedDetached || freshNotLoadedUnhydrated
611
+ ? "No turns yet"
612
+ : recentState === "done"
613
+ ? "Finished recently"
614
+ : "Idle"),
615
+ paths: [thread.cwd],
616
+ activityEvent: null
617
+ };
618
+ }
619
+ const interruptedWithoutFinalAnswer = lastTurn.status === "interrupted" && !(0, codex_turn_semantics_1.turnHasFinalAnswer)(lastTurn);
620
+ const freshNotLoadedNonFinalWork = isFreshNotLoadedNonFinalWorkThread(thread, lastTurn);
621
+ const activeTopLevelWithoutFinalAnswer = thread.status.type === "active"
622
+ && !parentThreadIdForThread(thread)
623
+ && !(0, codex_turn_semantics_1.turnHasFinalAnswer)(lastTurn);
624
+ const treatAsInProgress = lastTurn.status === "inProgress"
625
+ || freshNotLoadedNonFinalWork
626
+ || activeTopLevelWithoutFinalAnswer;
627
+ if (lastTurn.status === "failed") {
628
+ return {
629
+ state: "blocked",
630
+ detail: lastTurn.error?.message ?? "Turn failed",
631
+ paths: [thread.cwd],
632
+ activityEvent: null
633
+ };
634
+ }
635
+ const item = selectRelevantItem(lastTurn.items);
636
+ if (!item) {
637
+ return {
638
+ state: treatAsInProgress ? "planning"
639
+ : interruptedWithoutFinalAnswer ? (ageMs <= DONE_WINDOW_MS ? "done" : "idle")
640
+ : "idle",
641
+ detail: treatAsInProgress ? "Planning"
642
+ : interruptedWithoutFinalAnswer ? (ageMs <= DONE_WINDOW_MS ? "Interrupted" : "Idle")
643
+ : "Idle",
644
+ paths: [thread.cwd],
645
+ activityEvent: null
646
+ };
647
+ }
648
+ switch (item.type) {
649
+ case "fileChange": {
650
+ const change = summarizeFileChange(item);
651
+ const status = typeof item.status === "string" ? item.status : "inProgress";
652
+ const state = status === "failed" || status === "declined" ? "blocked"
653
+ : !treatAsInProgress && status === "completed" ? settledRecentState
654
+ : "editing";
655
+ const editingDetail = change.path ? `Editing ${change.path}` : "Editing files";
656
+ return {
657
+ state,
658
+ detail: state === "blocked" ? summarizeFailedItemDetail(item, editingDetail)
659
+ : state === "done" || state === "idle"
660
+ ? change.path ? `Edited ${change.path}` : "Edited files"
661
+ : editingDetail,
662
+ paths: change.paths.length > 0 ? change.paths : [thread.cwd],
663
+ activityEvent: {
664
+ type: "fileChange",
665
+ action: change.action,
666
+ path: change.path,
667
+ title: change.title,
668
+ isImage: change.isImage,
669
+ linesAdded: change.linesAdded,
670
+ linesRemoved: change.linesRemoved
671
+ }
672
+ };
673
+ }
674
+ case "commandExecution": {
675
+ const command = typeof item.command === "string" ? item.command : "Command";
676
+ const cwd = typeof item.cwd === "string" ? item.cwd : thread.cwd;
677
+ const status = typeof item.status === "string" ? item.status : "inProgress";
678
+ if (status === "failed" || status === "declined") {
679
+ return {
680
+ state: "blocked",
681
+ detail: summarizeFailedItemDetail(item, command),
682
+ paths: [cwd],
683
+ activityEvent: {
684
+ type: "commandExecution",
685
+ action: "ran",
686
+ path: cwd,
687
+ title: command,
688
+ isImage: false
689
+ }
690
+ };
691
+ }
692
+ return {
693
+ state: !treatAsInProgress && status === "completed"
694
+ ? settledRecentState
695
+ : (0, text_1.looksLikeValidationCommand)(command) ? "validating" : "running",
696
+ detail: command,
697
+ paths: [cwd],
698
+ activityEvent: {
699
+ type: "commandExecution",
700
+ action: "ran",
701
+ path: cwd,
702
+ title: command,
703
+ isImage: false
704
+ }
705
+ };
706
+ }
707
+ case "webSearch": {
708
+ const query = (0, web_search_1.summarizeWebSearch)(item);
709
+ return {
710
+ state: "scanning",
711
+ detail: query,
712
+ paths: [thread.cwd],
713
+ activityEvent: {
714
+ type: "webSearch",
715
+ action: "updated",
716
+ path: thread.cwd,
717
+ title: query,
718
+ isImage: false
719
+ }
720
+ };
721
+ }
722
+ case "dynamicToolCall":
723
+ case "mcpToolCall": {
724
+ const server = typeof item.server === "string" ? item.server : "mcp";
725
+ const tool = typeof item.tool === "string" ? item.tool : server;
726
+ const status = typeof item.status === "string" ? item.status : "inProgress";
727
+ return {
728
+ state: status === "failed" || status === "declined" ? "blocked"
729
+ : !treatAsInProgress && status === "completed" ? settledRecentState
730
+ : "planning",
731
+ detail: status === "failed" || status === "declined" ? summarizeFailedItemDetail(item, tool) : tool,
732
+ paths: [thread.cwd],
733
+ activityEvent: {
734
+ type: item.type,
735
+ action: "updated",
736
+ path: thread.cwd,
737
+ title: tool,
738
+ isImage: false
739
+ }
740
+ };
741
+ }
742
+ case "collabToolCall":
743
+ case "collabAgentToolCall": {
744
+ const detail = collabToolDetail(item);
745
+ const status = typeof item.status === "string" ? item.status : "inProgress";
746
+ return {
747
+ state: status === "failed" || status === "declined" ? "blocked"
748
+ : !treatAsInProgress && status === "completed" ? settledRecentState
749
+ : "delegating",
750
+ detail: status === "failed" || status === "declined" ? summarizeFailedItemDetail(item, detail) : detail,
751
+ paths: [thread.cwd],
752
+ activityEvent: {
753
+ type: item.type,
754
+ action: "updated",
755
+ path: thread.cwd,
756
+ title: detail,
757
+ isImage: false
758
+ }
759
+ };
760
+ }
761
+ case "plan":
762
+ return {
763
+ state: !treatAsInProgress && item.status === "completed" ? settledRecentState : "planning",
764
+ detail: summarizePlanItem(item),
765
+ paths: [thread.cwd],
766
+ activityEvent: {
767
+ type: "plan",
768
+ action: "updated",
769
+ path: thread.cwd,
770
+ title: summarizePlanItem(item),
771
+ isImage: false
772
+ }
773
+ };
774
+ case "reasoning": {
775
+ const text = typeof item.summary === "string" ? item.summary : typeof item.text === "string" ? item.text : "Reasoning";
776
+ return {
777
+ state: !treatAsInProgress && item.status === "completed" ? settledRecentState : "thinking",
778
+ detail: (0, text_1.shortenText)(text, 88),
779
+ paths: [thread.cwd],
780
+ activityEvent: null
781
+ };
782
+ }
783
+ case "contextCompaction":
784
+ return {
785
+ state: treatAsInProgress ? "thinking" : settledRecentState,
786
+ detail: treatAsInProgress ? "Compacting context" : "Compacted context",
787
+ paths: [thread.cwd],
788
+ activityEvent: null
789
+ };
790
+ case "agentMessage": {
791
+ const message = describeAgentMessage(item);
792
+ const interruptedCommentary = interruptedWithoutFinalAnswer
793
+ && (item.phase === "commentary" || item.phase === "assistant_response");
794
+ return {
795
+ state: treatAsInProgress || (interruptedCommentary && ageMs <= DONE_WINDOW_MS) ? "thinking"
796
+ : interruptedCommentary || ageMs <= DONE_WINDOW_MS ? "done"
797
+ : "idle",
798
+ detail: message.detail,
799
+ paths: message.paths.length > 0 ? message.paths : [thread.cwd],
800
+ activityEvent: {
801
+ type: "agentMessage",
802
+ action: "said",
803
+ path: message.paths[0] ?? thread.cwd,
804
+ title: message.detail,
805
+ isImage: false
806
+ }
807
+ };
808
+ }
809
+ case "userMessage": {
810
+ const text = typeof item.text === "string" ? (0, text_1.shortenText)(item.text, 88) : "Prompt";
811
+ return {
812
+ state: treatAsInProgress || ageMs <= USER_PROMPT_ACTIVE_WINDOW_MS ? "planning"
813
+ : ageMs <= DONE_WINDOW_MS ? "done"
814
+ : "idle",
815
+ detail: text,
816
+ paths: extractPathsFromText(text).length > 0 ? extractPathsFromText(text) : [thread.cwd],
817
+ activityEvent: {
818
+ type: "userMessage",
819
+ action: "said",
820
+ path: thread.cwd,
821
+ title: text,
822
+ isImage: false
823
+ }
824
+ };
825
+ }
826
+ default:
827
+ return {
828
+ state: treatAsInProgress ? "planning"
829
+ : interruptedWithoutFinalAnswer ? (ageMs <= DONE_WINDOW_MS ? "done" : "idle")
830
+ : ageMs <= DONE_WINDOW_MS ? "done"
831
+ : "idle",
832
+ detail: treatAsInProgress ? "Planning"
833
+ : interruptedWithoutFinalAnswer ? (ageMs <= DONE_WINDOW_MS ? "Interrupted" : "Idle")
834
+ : ageMs <= DONE_WINDOW_MS ? "Finished recently"
835
+ : "Idle",
836
+ paths: [thread.cwd],
837
+ activityEvent: null
838
+ };
839
+ }
840
+ }
841
+ function buildActivityEventFromDashboardEvent(event) {
842
+ switch (event.kind) {
843
+ case "fileChange":
844
+ return {
845
+ type: "fileChange",
846
+ action: event.action ?? "edited",
847
+ path: event.path,
848
+ title: event.title,
849
+ isImage: Boolean(event.isImage),
850
+ linesAdded: event.linesAdded,
851
+ linesRemoved: event.linesRemoved
852
+ };
853
+ case "command":
854
+ return {
855
+ type: "commandExecution",
856
+ action: "ran",
857
+ path: event.path,
858
+ title: event.command ?? event.detail ?? event.title,
859
+ isImage: false
860
+ };
861
+ case "subagent":
862
+ return {
863
+ type: "collabAgentToolCall",
864
+ action: "updated",
865
+ path: event.path,
866
+ title: event.detail || event.title,
867
+ isImage: false
868
+ };
869
+ case "message":
870
+ return {
871
+ type: "agentMessage",
872
+ action: "said",
873
+ path: event.path,
874
+ title: event.detail || event.title,
875
+ isImage: false
876
+ };
877
+ default:
878
+ return null;
879
+ }
880
+ }
881
+ function applyRecentActivityEvent(thread, summary, recentEvents) {
882
+ if (recentEvents.length === 0) {
883
+ return summary;
884
+ }
885
+ const updatedAtMs = thread.updatedAt * 1000;
886
+ const preferredEvent = recentEvents
887
+ .filter((event) => {
888
+ if (event.threadId !== thread.id) {
889
+ return false;
890
+ }
891
+ if (!["fileChange", "command", "subagent", "message"].includes(event.kind)) {
892
+ return false;
893
+ }
894
+ return isFreshActivityEvent(event, updatedAtMs);
895
+ })
896
+ .sort(compareRecentActivityEvents)
897
+ .find((event) => event.kind === "fileChange"
898
+ || event.kind === "command"
899
+ || event.kind === "subagent"
900
+ || event.kind === "message");
901
+ if (!preferredEvent) {
902
+ return summary;
903
+ }
904
+ if (summary.state === "waiting" || summary.state === "blocked") {
905
+ return summary;
906
+ }
907
+ const activityEvent = buildActivityEventFromDashboardEvent(preferredEvent);
908
+ if (!activityEvent) {
909
+ return summary;
910
+ }
911
+ const nextPaths = preferredEvent.path
912
+ ? Array.from(new Set([preferredEvent.path, ...summary.paths.filter(Boolean)]))
913
+ : summary.paths;
914
+ if (preferredEvent.kind === "message") {
915
+ return {
916
+ state: summary.state,
917
+ detail: preferredEvent.detail || summary.detail,
918
+ paths: nextPaths,
919
+ activityEvent
920
+ };
921
+ }
922
+ if (preferredEvent.kind === "command" && (summary.state === "done" || summary.state === "idle")) {
923
+ return summary;
924
+ }
925
+ if (preferredEvent.kind === "subagent") {
926
+ return {
927
+ state: preferredEvent.phase === "failed" ? "blocked" : "delegating",
928
+ detail: preferredEvent.detail || preferredEvent.title,
929
+ paths: nextPaths,
930
+ activityEvent
931
+ };
932
+ }
933
+ const commandText = preferredEvent.command ?? preferredEvent.detail ?? preferredEvent.title;
934
+ const nextState = preferredEvent.kind === "fileChange"
935
+ ? (preferredEvent.phase === "failed" ? "blocked"
936
+ : preferredEvent.phase === "started" || preferredEvent.phase === "updated" ? "editing"
937
+ : summary.state)
938
+ : preferredEvent.kind === "command"
939
+ ? (preferredEvent.phase === "failed" ? "blocked"
940
+ : preferredEvent.phase === "started" || preferredEvent.phase === "updated"
941
+ ? ((0, text_1.looksLikeValidationCommand)(commandText) ? "validating" : "running")
942
+ : summary.state)
943
+ : summary.state;
944
+ return {
945
+ state: nextState,
946
+ detail: preferredEvent.kind === "fileChange"
947
+ ? preferredEvent.path ? `Edited ${preferredEvent.path}` : preferredEvent.title
948
+ : commandText,
949
+ paths: nextPaths,
950
+ activityEvent
951
+ };
952
+ }
953
+ //# sourceMappingURL=thread-summary.js.map