@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.
- package/dist/adapters/claude.d.ts +2 -0
- package/dist/adapters/claude.js +43 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex-cloud.d.ts +4 -0
- package/dist/adapters/codex-cloud.js +80 -0
- package/dist/adapters/codex-cloud.js.map +1 -0
- package/dist/adapters/codex-local.d.ts +17 -0
- package/dist/adapters/codex-local.js +180 -0
- package/dist/adapters/codex-local.js.map +1 -0
- package/dist/adapters/cursor-cloud.d.ts +2 -0
- package/dist/adapters/cursor-cloud.js +54 -0
- package/dist/adapters/cursor-cloud.js.map +1 -0
- package/dist/adapters/cursor-local.d.ts +2 -0
- package/dist/adapters/cursor-local.js +37 -0
- package/dist/adapters/cursor-local.js.map +1 -0
- package/dist/adapters/helpers.d.ts +15 -0
- package/dist/adapters/helpers.js +33 -0
- package/dist/adapters/helpers.js.map +1 -0
- package/dist/adapters/hermes.d.ts +2 -0
- package/dist/adapters/hermes.js +41 -0
- package/dist/adapters/hermes.js.map +1 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.js +37 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/openclaw.d.ts +2 -0
- package/dist/adapters/openclaw.js +41 -0
- package/dist/adapters/openclaw.js.map +1 -0
- package/dist/adapters/presence.d.ts +2 -0
- package/dist/adapters/presence.js +36 -0
- package/dist/adapters/presence.js.map +1 -0
- package/dist/adapters/static-source.d.ts +15 -0
- package/dist/adapters/static-source.js +41 -0
- package/dist/adapters/static-source.js.map +1 -0
- package/dist/adapters/types.d.ts +44 -0
- package/dist/adapters/types.js +3 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/app-server.d.ts +94 -0
- package/dist/app-server.js +382 -0
- package/dist/app-server.js.map +1 -0
- package/dist/app-settings.d.ts +52 -0
- package/dist/app-settings.js +268 -0
- package/dist/app-settings.js.map +1 -0
- package/dist/appearance.d.ts +6 -0
- package/dist/appearance.js +84 -0
- package/dist/appearance.js.map +1 -0
- package/dist/aseprite.d.ts +4 -0
- package/dist/aseprite.js +42 -0
- package/dist/aseprite.js.map +1 -0
- package/dist/claude-agent-sdk.d.ts +44 -0
- package/dist/claude-agent-sdk.js +347 -0
- package/dist/claude-agent-sdk.js.map +1 -0
- package/dist/claude-home-cache.d.ts +61 -0
- package/dist/claude-home-cache.js +438 -0
- package/dist/claude-home-cache.js.map +1 -0
- package/dist/claude.d.ts +154 -0
- package/dist/claude.js +3495 -0
- package/dist/claude.js.map +1 -0
- package/dist/cloud.d.ts +2 -0
- package/dist/cloud.js +25 -0
- package/dist/cloud.js.map +1 -0
- package/dist/codex-command.d.ts +25 -0
- package/dist/codex-command.js +235 -0
- package/dist/codex-command.js.map +1 -0
- package/dist/codex-session-files.d.ts +8 -0
- package/dist/codex-session-files.js +509 -0
- package/dist/codex-session-files.js.map +1 -0
- package/dist/codex-thread-query.d.ts +14 -0
- package/dist/codex-thread-query.js +82 -0
- package/dist/codex-thread-query.js.map +1 -0
- package/dist/cursor-cloud-data.d.ts +34 -0
- package/dist/cursor-cloud-data.js +344 -0
- package/dist/cursor-cloud-data.js.map +1 -0
- package/dist/cursor-lib/local-discovery.d.ts +17 -0
- package/dist/cursor-lib/local-discovery.js +251 -0
- package/dist/cursor-lib/local-discovery.js.map +1 -0
- package/dist/cursor-lib/shared.d.ts +5 -0
- package/dist/cursor-lib/shared.js +88 -0
- package/dist/cursor-lib/shared.js.map +1 -0
- package/dist/cursor.d.ts +8 -0
- package/dist/cursor.js +1584 -0
- package/dist/cursor.js.map +1 -0
- package/dist/domain/codex-turn-semantics.d.ts +4 -0
- package/dist/domain/codex-turn-semantics.js +49 -0
- package/dist/domain/codex-turn-semantics.js.map +1 -0
- package/dist/domain/workload-policy.d.ts +8 -0
- package/dist/domain/workload-policy.js +268 -0
- package/dist/domain/workload-policy.js.map +1 -0
- package/dist/domain/workspace-activity.d.ts +10 -0
- package/dist/domain/workspace-activity.js +372 -0
- package/dist/domain/workspace-activity.js.map +1 -0
- package/dist/goal.d.ts +11 -0
- package/dist/goal.js +76 -0
- package/dist/goal.js.map +1 -0
- package/dist/hermes-hook-install.d.ts +12 -0
- package/dist/hermes-hook-install.js +335 -0
- package/dist/hermes-hook-install.js.map +1 -0
- package/dist/hermes.d.ts +97 -0
- package/dist/hermes.js +2446 -0
- package/dist/hermes.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/live-monitor-lib/events.d.ts +43 -0
- package/dist/live-monitor-lib/events.js +1463 -0
- package/dist/live-monitor-lib/events.js.map +1 -0
- package/dist/live-monitor-lib/rollout-hooks.d.ts +13 -0
- package/dist/live-monitor-lib/rollout-hooks.js +253 -0
- package/dist/live-monitor-lib/rollout-hooks.js.map +1 -0
- package/dist/live-monitor.d.ts +102 -0
- package/dist/live-monitor.js +1461 -0
- package/dist/live-monitor.js.map +1 -0
- package/dist/local-thread-selection.d.ts +2 -0
- package/dist/local-thread-selection.js +58 -0
- package/dist/local-thread-selection.js.map +1 -0
- package/dist/openclaw.d.ts +41 -0
- package/dist/openclaw.js +715 -0
- package/dist/openclaw.js.map +1 -0
- package/dist/presence.d.ts +9 -0
- package/dist/presence.js +108 -0
- package/dist/presence.js.map +1 -0
- package/dist/project-identity.d.ts +7 -0
- package/dist/project-identity.js +162 -0
- package/dist/project-identity.js.map +1 -0
- package/dist/project-paths.d.ts +23 -0
- package/dist/project-paths.js +394 -0
- package/dist/project-paths.js.map +1 -0
- package/dist/project-storage.d.ts +7 -0
- package/dist/project-storage.js +79 -0
- package/dist/project-storage.js.map +1 -0
- package/dist/room-config.d.ts +21 -0
- package/dist/room-config.js +230 -0
- package/dist/room-config.js.map +1 -0
- package/dist/services/project-discovery.d.ts +1 -0
- package/dist/services/project-discovery.js +16 -0
- package/dist/services/project-discovery.js.map +1 -0
- package/dist/services/project-live-monitor.d.ts +1 -0
- package/dist/services/project-live-monitor.js +6 -0
- package/dist/services/project-live-monitor.js.map +1 -0
- package/dist/services/project-snapshot-coordinator.d.ts +49 -0
- package/dist/services/project-snapshot-coordinator.js +152 -0
- package/dist/services/project-snapshot-coordinator.js.map +1 -0
- package/dist/services/refresh-scheduler.d.ts +8 -0
- package/dist/services/refresh-scheduler.js +44 -0
- package/dist/services/refresh-scheduler.js.map +1 -0
- package/dist/services/snapshot-assembler.d.ts +8 -0
- package/dist/services/snapshot-assembler.js +167 -0
- package/dist/services/snapshot-assembler.js.map +1 -0
- package/dist/services/thread-store.d.ts +4 -0
- package/dist/services/thread-store.js +8 -0
- package/dist/services/thread-store.js.map +1 -0
- package/dist/snapshot-lib/activity-summary.d.ts +3 -0
- package/dist/snapshot-lib/activity-summary.js +220 -0
- package/dist/snapshot-lib/activity-summary.js.map +1 -0
- package/dist/snapshot-lib/dashboard-builder.d.ts +21 -0
- package/dist/snapshot-lib/dashboard-builder.js +146 -0
- package/dist/snapshot-lib/dashboard-builder.js.map +1 -0
- package/dist/snapshot-lib/thread-summary.d.ts +27 -0
- package/dist/snapshot-lib/thread-summary.js +953 -0
- package/dist/snapshot-lib/thread-summary.js.map +1 -0
- package/dist/snapshot.d.ts +2 -0
- package/dist/snapshot.js +21 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/types.d.ts +368 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/json.d.ts +4 -0
- package/dist/utils/json.js +29 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/text.d.ts +3 -0
- package/dist/utils/text.js +26 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/web-search.d.ts +1 -0
- package/dist/web-search.js +52 -0
- package/dist/web-search.js.map +1 -0
- package/dist/workload.d.ts +1 -0
- package/dist/workload.js +12 -0
- package/dist/workload.js.map +1 -0
- 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
|