@tutti-os/agent-activity-core 0.0.3
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/LICENSE +202 -0
- package/README.md +169 -0
- package/dist/index.d.ts +296 -0
- package/dist/index.js +1083 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1083 @@
|
|
|
1
|
+
// src/merge.ts
|
|
2
|
+
function mergeAgentActivityMessages(currentMessages, incomingMessages) {
|
|
3
|
+
if (incomingMessages.length === 0) {
|
|
4
|
+
return [...currentMessages];
|
|
5
|
+
}
|
|
6
|
+
const byMessageId = /* @__PURE__ */ new Map();
|
|
7
|
+
for (const message of currentMessages) {
|
|
8
|
+
byMessageId.set(message.messageId, cloneAgentActivityMessage(message));
|
|
9
|
+
}
|
|
10
|
+
for (const incoming of incomingMessages) {
|
|
11
|
+
const existing = byMessageId.get(incoming.messageId);
|
|
12
|
+
if (!existing || shouldReplaceAgentActivityMessage(existing, incoming)) {
|
|
13
|
+
byMessageId.set(
|
|
14
|
+
incoming.messageId,
|
|
15
|
+
existing ? mergeAgentActivityMessage(existing, incoming) : cloneAgentActivityMessage(incoming)
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return [...byMessageId.values()].sort(compareAgentActivityMessages);
|
|
20
|
+
}
|
|
21
|
+
function compareAgentActivityMessages(left, right) {
|
|
22
|
+
return left.version - right.version || (left.id ?? 0) - (right.id ?? 0) || left.messageId.localeCompare(right.messageId);
|
|
23
|
+
}
|
|
24
|
+
function latestAgentActivityMessageVersion(messages) {
|
|
25
|
+
return messages.reduce(
|
|
26
|
+
(latestVersion, message) => Math.max(latestVersion, message.version),
|
|
27
|
+
0
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function areAgentActivityMessageArraysEqual(left, right) {
|
|
31
|
+
if (left.length !== right.length) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
35
|
+
if (!areAgentActivityMessagesEqual(left[index], right[index])) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
function areAgentActivityMessagesEqual(left, right) {
|
|
42
|
+
return left.workspaceId === right.workspaceId && left.agentSessionId === right.agentSessionId && left.messageId === right.messageId && Object.is(left.id, right.id) && left.version === right.version && Object.is(left.turnId, right.turnId) && left.role === right.role && left.kind === right.kind && Object.is(left.status, right.status) && Object.is(left.occurredAtUnixMs, right.occurredAtUnixMs) && Object.is(left.startedAtUnixMs, right.startedAtUnixMs) && Object.is(left.completedAtUnixMs, right.completedAtUnixMs) && areJsonLikeValuesEqual(left.payload, right.payload);
|
|
43
|
+
}
|
|
44
|
+
function cloneAgentActivityMessage(message) {
|
|
45
|
+
return {
|
|
46
|
+
...message,
|
|
47
|
+
payload: { ...message.payload }
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function mergeAgentActivityMessage(existing, incoming) {
|
|
51
|
+
return {
|
|
52
|
+
...existing,
|
|
53
|
+
...incoming,
|
|
54
|
+
payload: {
|
|
55
|
+
...existing.payload,
|
|
56
|
+
...incoming.payload
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function shouldReplaceAgentActivityMessage(existing, incoming) {
|
|
61
|
+
if (incoming.version !== existing.version) {
|
|
62
|
+
return incoming.version > existing.version;
|
|
63
|
+
}
|
|
64
|
+
return (incoming.id ?? 0) >= (existing.id ?? 0);
|
|
65
|
+
}
|
|
66
|
+
function areJsonLikeValuesEqual(left, right) {
|
|
67
|
+
if (Object.is(left, right)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
71
|
+
if (!Array.isArray(left) || !Array.isArray(right)) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (left.length !== right.length) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
78
|
+
if (!areJsonLikeValuesEqual(left[index], right[index])) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
const leftRecord = recordValue(left);
|
|
85
|
+
const rightRecord = recordValue(right);
|
|
86
|
+
if (!leftRecord || !rightRecord) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const keys = /* @__PURE__ */ new Set([
|
|
90
|
+
...Object.keys(leftRecord),
|
|
91
|
+
...Object.keys(rightRecord)
|
|
92
|
+
]);
|
|
93
|
+
for (const key of keys) {
|
|
94
|
+
if (!areJsonLikeValuesEqual(leftRecord[key], rightRecord[key])) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
function recordValue(value) {
|
|
101
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/controller.ts
|
|
105
|
+
function createAgentActivityController({
|
|
106
|
+
adapter,
|
|
107
|
+
autoRetainSessionEvents = true,
|
|
108
|
+
workspaceId
|
|
109
|
+
}) {
|
|
110
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
111
|
+
const activeMessageSyncs = /* @__PURE__ */ new Map();
|
|
112
|
+
const activeComposerOptionsLoads = /* @__PURE__ */ new Map();
|
|
113
|
+
const composerOptionsLoadVersions = /* @__PURE__ */ new Map();
|
|
114
|
+
const autoRetainedStreamReleases = /* @__PURE__ */ new Map();
|
|
115
|
+
const retainedStreams = /* @__PURE__ */ new Map();
|
|
116
|
+
let snapshot = createEmptyAgentActivitySnapshot(workspaceId);
|
|
117
|
+
const emit = () => {
|
|
118
|
+
for (const listener of listeners) {
|
|
119
|
+
listener(snapshot);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const updateSnapshot = (updater) => {
|
|
123
|
+
const nextSnapshot = updater(snapshot);
|
|
124
|
+
if (nextSnapshot === snapshot) {
|
|
125
|
+
return snapshot;
|
|
126
|
+
}
|
|
127
|
+
snapshot = cloneAgentActivitySnapshot(nextSnapshot);
|
|
128
|
+
emit();
|
|
129
|
+
return snapshot;
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
getSnapshot: () => snapshot,
|
|
133
|
+
subscribe(listener) {
|
|
134
|
+
listeners.add(listener);
|
|
135
|
+
listener(snapshot);
|
|
136
|
+
return () => {
|
|
137
|
+
listeners.delete(listener);
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
async load(signal) {
|
|
141
|
+
const response = await adapter.listSessions({ workspaceId, signal });
|
|
142
|
+
const nextSessions = response.sessions;
|
|
143
|
+
const nextPresences = response.presences ?? [];
|
|
144
|
+
const nextSnapshot = updateSnapshot((current) => {
|
|
145
|
+
if (areShallowObjectArraysEqual(current.sessions, nextSessions) && areShallowObjectArraysEqual(current.presences, nextPresences)) {
|
|
146
|
+
return current;
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
...current,
|
|
150
|
+
presences: nextPresences,
|
|
151
|
+
sessions: nextSessions
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
if (autoRetainSessionEvents) {
|
|
155
|
+
reconcileAutoRetainedSessionStreams(nextSnapshot.sessions, signal);
|
|
156
|
+
}
|
|
157
|
+
return nextSnapshot;
|
|
158
|
+
},
|
|
159
|
+
async loadComposerOptions(input) {
|
|
160
|
+
const provider = input.provider.trim();
|
|
161
|
+
if (!provider) {
|
|
162
|
+
throw new Error("Agent composer options provider is required.");
|
|
163
|
+
}
|
|
164
|
+
if (!input.force) {
|
|
165
|
+
const cached = snapshot.composerOptionsByProvider?.[provider];
|
|
166
|
+
if (cached) {
|
|
167
|
+
return cloneAgentActivityComposerOptions(cached);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const existingLoad = activeComposerOptionsLoads.get(provider);
|
|
171
|
+
if (existingLoad && !input.force) {
|
|
172
|
+
return existingLoad.then(cloneAgentActivityComposerOptions);
|
|
173
|
+
}
|
|
174
|
+
const loadVersion = (composerOptionsLoadVersions.get(provider) ?? 0) + 1;
|
|
175
|
+
composerOptionsLoadVersions.set(provider, loadVersion);
|
|
176
|
+
const load = adapter.loadComposerOptions({
|
|
177
|
+
workspaceId,
|
|
178
|
+
provider,
|
|
179
|
+
cwd: input.cwd,
|
|
180
|
+
settings: input.settings,
|
|
181
|
+
signal: input.signal
|
|
182
|
+
}).then((options) => {
|
|
183
|
+
const normalizedOptions = cloneAgentActivityComposerOptions({
|
|
184
|
+
...options,
|
|
185
|
+
provider,
|
|
186
|
+
loadedAtUnixMs: options.loadedAtUnixMs || Date.now()
|
|
187
|
+
});
|
|
188
|
+
if (composerOptionsLoadVersions.get(provider) !== loadVersion) {
|
|
189
|
+
return cloneAgentActivityComposerOptions(normalizedOptions);
|
|
190
|
+
}
|
|
191
|
+
updateSnapshot((current) => {
|
|
192
|
+
const currentOptions = current.composerOptionsByProvider?.[provider];
|
|
193
|
+
if (currentOptions && areComposerOptionsEqual(currentOptions, normalizedOptions)) {
|
|
194
|
+
return current;
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
...current,
|
|
198
|
+
composerOptionsByProvider: {
|
|
199
|
+
...current.composerOptionsByProvider,
|
|
200
|
+
[provider]: normalizedOptions
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
return cloneAgentActivityComposerOptions(normalizedOptions);
|
|
205
|
+
}).finally(() => {
|
|
206
|
+
if (activeComposerOptionsLoads.get(provider) === load) {
|
|
207
|
+
activeComposerOptionsLoads.delete(provider);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
activeComposerOptionsLoads.set(provider, load);
|
|
211
|
+
return load.then(cloneAgentActivityComposerOptions);
|
|
212
|
+
},
|
|
213
|
+
async listSessionMessages({
|
|
214
|
+
agentSessionId,
|
|
215
|
+
afterVersion,
|
|
216
|
+
beforeVersion,
|
|
217
|
+
limit,
|
|
218
|
+
order,
|
|
219
|
+
signal
|
|
220
|
+
}) {
|
|
221
|
+
const response = await adapter.listSessionMessages({
|
|
222
|
+
workspaceId,
|
|
223
|
+
agentSessionId,
|
|
224
|
+
afterVersion,
|
|
225
|
+
beforeVersion,
|
|
226
|
+
limit,
|
|
227
|
+
order,
|
|
228
|
+
signal
|
|
229
|
+
});
|
|
230
|
+
updateSnapshot(
|
|
231
|
+
(current) => mergeSnapshotMessages(current, agentSessionId, response.messages)
|
|
232
|
+
);
|
|
233
|
+
return {
|
|
234
|
+
...response,
|
|
235
|
+
messages: response.messages.map((message) => ({
|
|
236
|
+
...message,
|
|
237
|
+
payload: { ...message.payload }
|
|
238
|
+
}))
|
|
239
|
+
};
|
|
240
|
+
},
|
|
241
|
+
retainSessionEvents: retainSessionEventsImpl,
|
|
242
|
+
removeSession(agentSessionId) {
|
|
243
|
+
updateSnapshot(
|
|
244
|
+
(current) => removeSnapshotSession(current, agentSessionId)
|
|
245
|
+
);
|
|
246
|
+
},
|
|
247
|
+
upsertSession(session) {
|
|
248
|
+
if (session.workspaceId && session.workspaceId !== snapshot.workspaceId) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
updateSnapshot((current) => upsertSnapshotSession(current, session));
|
|
252
|
+
},
|
|
253
|
+
applyActivityUpdatedEvent(event) {
|
|
254
|
+
const result = applyActivityUpdatedEvent(snapshot, event);
|
|
255
|
+
if (result.snapshot !== snapshot) {
|
|
256
|
+
snapshot = result.snapshot;
|
|
257
|
+
emit();
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
applied: result.applied,
|
|
261
|
+
messages: result.messages.map(cloneAgentActivityMessage),
|
|
262
|
+
session: result.session ? { ...result.session } : null,
|
|
263
|
+
statePatch: result.statePatch ? cloneAgentActivityStatePatch(result.statePatch) : null
|
|
264
|
+
};
|
|
265
|
+
},
|
|
266
|
+
applySessionEvent(event) {
|
|
267
|
+
updateSnapshot((current) => applySessionEvent(current, event));
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
function retainSessionEventsImpl({
|
|
271
|
+
agentSessionId,
|
|
272
|
+
afterVersion,
|
|
273
|
+
onRetainFailed,
|
|
274
|
+
onError
|
|
275
|
+
}) {
|
|
276
|
+
const normalizedAgentSessionId = agentSessionId.trim();
|
|
277
|
+
if (!normalizedAgentSessionId) {
|
|
278
|
+
return () => {
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const existing = retainedStreams.get(normalizedAgentSessionId);
|
|
282
|
+
if (existing) {
|
|
283
|
+
existing.refCount += 1;
|
|
284
|
+
return createRetainedStreamRelease(normalizedAgentSessionId);
|
|
285
|
+
}
|
|
286
|
+
const abortController = new AbortController();
|
|
287
|
+
const stream = {
|
|
288
|
+
abortController,
|
|
289
|
+
refCount: 1,
|
|
290
|
+
unsubscribe: null
|
|
291
|
+
};
|
|
292
|
+
retainedStreams.set(normalizedAgentSessionId, stream);
|
|
293
|
+
const cachedMessages = snapshot.sessionMessagesById[normalizedAgentSessionId] ?? [];
|
|
294
|
+
const streamAfterVersion = afterVersion ?? latestAgentActivityMessageVersion(cachedMessages);
|
|
295
|
+
void adapter.subscribeSessionEvents({
|
|
296
|
+
workspaceId,
|
|
297
|
+
agentSessionId: normalizedAgentSessionId,
|
|
298
|
+
afterVersion: streamAfterVersion,
|
|
299
|
+
signal: abortController.signal,
|
|
300
|
+
onEvent(event) {
|
|
301
|
+
if (!abortController.signal.aborted) {
|
|
302
|
+
updateSnapshot((current) => applySessionEvent(current, event));
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
onError
|
|
306
|
+
}).then((unsubscribe) => {
|
|
307
|
+
const retained = retainedStreams.get(normalizedAgentSessionId);
|
|
308
|
+
if (!retained || retained.abortController.signal.aborted) {
|
|
309
|
+
unsubscribe();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
retained.unsubscribe = unsubscribe;
|
|
313
|
+
}).catch((error) => {
|
|
314
|
+
if (!abortController.signal.aborted) {
|
|
315
|
+
onError?.(error);
|
|
316
|
+
}
|
|
317
|
+
if (retainedStreams.get(normalizedAgentSessionId) === stream) {
|
|
318
|
+
retainedStreams.delete(normalizedAgentSessionId);
|
|
319
|
+
}
|
|
320
|
+
onRetainFailed?.();
|
|
321
|
+
abortController.abort();
|
|
322
|
+
stream.unsubscribe?.();
|
|
323
|
+
});
|
|
324
|
+
return createRetainedStreamRelease(normalizedAgentSessionId);
|
|
325
|
+
}
|
|
326
|
+
function reconcileAutoRetainedSessionStreams(sessions, signal) {
|
|
327
|
+
const activeSessionIds = new Set(
|
|
328
|
+
sessions.filter(shouldAutoRetainSessionEvents).map((session) => session.agentSessionId.trim()).filter(Boolean)
|
|
329
|
+
);
|
|
330
|
+
for (const [agentSessionId, release] of autoRetainedStreamReleases) {
|
|
331
|
+
if (!activeSessionIds.has(agentSessionId)) {
|
|
332
|
+
release();
|
|
333
|
+
autoRetainedStreamReleases.delete(agentSessionId);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
for (const agentSessionId of activeSessionIds) {
|
|
337
|
+
if (!autoRetainedStreamReleases.has(agentSessionId)) {
|
|
338
|
+
autoRetainedStreamReleases.set(
|
|
339
|
+
agentSessionId,
|
|
340
|
+
retainSessionEventsImpl({
|
|
341
|
+
agentSessionId,
|
|
342
|
+
onRetainFailed() {
|
|
343
|
+
autoRetainedStreamReleases.delete(agentSessionId);
|
|
344
|
+
}
|
|
345
|
+
})
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
syncSessionMessages(agentSessionId, signal);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function syncSessionMessages(agentSessionId, signal) {
|
|
352
|
+
if (activeMessageSyncs.has(agentSessionId)) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const cachedMessages = snapshot.sessionMessagesById[agentSessionId] ?? [];
|
|
356
|
+
const afterVersion = latestAgentActivityMessageVersion(cachedMessages);
|
|
357
|
+
const sync = adapter.listSessionMessages({
|
|
358
|
+
workspaceId,
|
|
359
|
+
agentSessionId,
|
|
360
|
+
afterVersion,
|
|
361
|
+
signal
|
|
362
|
+
}).then((response) => {
|
|
363
|
+
if (signal?.aborted) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
updateSnapshot(
|
|
367
|
+
(current) => mergeSnapshotMessages(current, agentSessionId, response.messages)
|
|
368
|
+
);
|
|
369
|
+
}).catch(() => {
|
|
370
|
+
}).finally(() => {
|
|
371
|
+
if (activeMessageSyncs.get(agentSessionId) === sync) {
|
|
372
|
+
activeMessageSyncs.delete(agentSessionId);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
activeMessageSyncs.set(agentSessionId, sync);
|
|
376
|
+
}
|
|
377
|
+
function createRetainedStreamRelease(agentSessionId) {
|
|
378
|
+
let released = false;
|
|
379
|
+
return () => {
|
|
380
|
+
if (released) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
released = true;
|
|
384
|
+
releaseRetainedStream(agentSessionId);
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function releaseRetainedStream(agentSessionId) {
|
|
388
|
+
const stream = retainedStreams.get(agentSessionId);
|
|
389
|
+
if (!stream) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
stream.refCount -= 1;
|
|
393
|
+
if (stream.refCount > 0) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
retainedStreams.delete(agentSessionId);
|
|
397
|
+
stream.abortController.abort();
|
|
398
|
+
stream.unsubscribe?.();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function createEmptyAgentActivitySnapshot(workspaceId) {
|
|
402
|
+
return {
|
|
403
|
+
workspaceId,
|
|
404
|
+
sessions: [],
|
|
405
|
+
presences: [],
|
|
406
|
+
sessionMessagesById: {},
|
|
407
|
+
composerOptionsByProvider: {}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function cloneAgentActivitySnapshot(snapshot) {
|
|
411
|
+
return {
|
|
412
|
+
workspaceId: snapshot.workspaceId,
|
|
413
|
+
sessions: snapshot.sessions.map((session) => ({ ...session })),
|
|
414
|
+
presences: snapshot.presences.map((presence) => ({ ...presence })),
|
|
415
|
+
composerOptionsByProvider: Object.fromEntries(
|
|
416
|
+
Object.entries(snapshot.composerOptionsByProvider ?? {}).map(
|
|
417
|
+
([provider, options]) => [
|
|
418
|
+
provider,
|
|
419
|
+
cloneAgentActivityComposerOptions(options)
|
|
420
|
+
]
|
|
421
|
+
)
|
|
422
|
+
),
|
|
423
|
+
sessionMessagesById: Object.fromEntries(
|
|
424
|
+
Object.entries(snapshot.sessionMessagesById).map(
|
|
425
|
+
([agentSessionId, messages]) => [
|
|
426
|
+
agentSessionId,
|
|
427
|
+
messages.map((message) => ({
|
|
428
|
+
...message,
|
|
429
|
+
payload: { ...message.payload }
|
|
430
|
+
}))
|
|
431
|
+
]
|
|
432
|
+
)
|
|
433
|
+
)
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function cloneAgentActivityComposerOptions(options) {
|
|
437
|
+
return {
|
|
438
|
+
provider: options.provider,
|
|
439
|
+
models: options.models.map((option) => ({ ...option })),
|
|
440
|
+
reasoningEfforts: options.reasoningEfforts.map((option) => ({
|
|
441
|
+
...option
|
|
442
|
+
})),
|
|
443
|
+
permissionConfig: options.permissionConfig ? {
|
|
444
|
+
configurable: options.permissionConfig.configurable,
|
|
445
|
+
defaultValue: options.permissionConfig.defaultValue ?? null,
|
|
446
|
+
modes: options.permissionConfig.modes.map((mode) => ({ ...mode }))
|
|
447
|
+
} : options.permissionConfig ?? null,
|
|
448
|
+
runtimeContext: cloneJSONRecord(options.runtimeContext),
|
|
449
|
+
skills: options.skills.map((skill) => ({ ...skill })),
|
|
450
|
+
loadedAtUnixMs: options.loadedAtUnixMs
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function areComposerOptionsEqual(left, right) {
|
|
454
|
+
const { loadedAtUnixMs: _leftLoadedAtUnixMs, ...leftComparable } = left;
|
|
455
|
+
const { loadedAtUnixMs: _rightLoadedAtUnixMs, ...rightComparable } = right;
|
|
456
|
+
return JSON.stringify(leftComparable) === JSON.stringify(rightComparable);
|
|
457
|
+
}
|
|
458
|
+
function cloneJSONRecord(value) {
|
|
459
|
+
if (value === void 0) {
|
|
460
|
+
return value;
|
|
461
|
+
}
|
|
462
|
+
return cloneJSONValue(value);
|
|
463
|
+
}
|
|
464
|
+
function cloneJSONValue(value) {
|
|
465
|
+
if (Array.isArray(value)) {
|
|
466
|
+
return value.map(cloneJSONValue);
|
|
467
|
+
}
|
|
468
|
+
if (value !== null && typeof value === "object") {
|
|
469
|
+
return Object.fromEntries(
|
|
470
|
+
Object.entries(value).map(([key, entry]) => [
|
|
471
|
+
key,
|
|
472
|
+
cloneJSONValue(entry)
|
|
473
|
+
])
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
return value;
|
|
477
|
+
}
|
|
478
|
+
function areShallowObjectArraysEqual(left, right) {
|
|
479
|
+
if (left.length !== right.length) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
483
|
+
if (!areShallowObjectsEqual(left[index], right[index])) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
function areShallowObjectsEqual(left, right) {
|
|
490
|
+
const leftRecord = left;
|
|
491
|
+
const rightRecord = right;
|
|
492
|
+
const keys = /* @__PURE__ */ new Set([
|
|
493
|
+
...Object.keys(leftRecord),
|
|
494
|
+
...Object.keys(rightRecord)
|
|
495
|
+
]);
|
|
496
|
+
for (const key of keys) {
|
|
497
|
+
if (!Object.is(leftRecord[key], rightRecord[key])) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
function applyActivityUpdatedEvent(snapshot, event) {
|
|
504
|
+
if (event.workspaceId && event.workspaceId !== snapshot.workspaceId) {
|
|
505
|
+
return emptyActivityUpdatedApplyResult(snapshot);
|
|
506
|
+
}
|
|
507
|
+
const workspaceId = event.workspaceId || snapshot.workspaceId;
|
|
508
|
+
const agentSessionId = event.agentSessionId.trim();
|
|
509
|
+
if (!agentSessionId) {
|
|
510
|
+
return emptyActivityUpdatedApplyResult(snapshot);
|
|
511
|
+
}
|
|
512
|
+
if (event.eventType === "message_update") {
|
|
513
|
+
return applyActivityUpdatedMessages(snapshot, {
|
|
514
|
+
agentSessionId,
|
|
515
|
+
data: event.data,
|
|
516
|
+
workspaceId
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
if (event.eventType === "state_patch") {
|
|
520
|
+
return applyActivityUpdatedStatePatch(snapshot, {
|
|
521
|
+
agentSessionId,
|
|
522
|
+
data: event.data,
|
|
523
|
+
workspaceId
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
return emptyActivityUpdatedApplyResult(snapshot);
|
|
527
|
+
}
|
|
528
|
+
function applyActivityUpdatedMessages(snapshot, input) {
|
|
529
|
+
const inlineMessages = inlineMessagesFromActivityUpdateData(input.data);
|
|
530
|
+
if (inlineMessages.length === 0) {
|
|
531
|
+
return emptyActivityUpdatedApplyResult(snapshot);
|
|
532
|
+
}
|
|
533
|
+
const sessionMessages = inlineMessages.filter(
|
|
534
|
+
(message) => inlineMessageBelongsToSession(message, input.agentSessionId)
|
|
535
|
+
);
|
|
536
|
+
if (sessionMessages.length === 0) {
|
|
537
|
+
return emptyActivityUpdatedApplyResult(snapshot);
|
|
538
|
+
}
|
|
539
|
+
const messages = sessionMessages.map(
|
|
540
|
+
(message) => agentActivityMessageFromInlineMessage({
|
|
541
|
+
agentSessionId: input.agentSessionId,
|
|
542
|
+
message,
|
|
543
|
+
workspaceId: input.workspaceId
|
|
544
|
+
})
|
|
545
|
+
);
|
|
546
|
+
const nextSnapshot = mergeSnapshotMessages(
|
|
547
|
+
snapshot,
|
|
548
|
+
input.agentSessionId,
|
|
549
|
+
messages
|
|
550
|
+
);
|
|
551
|
+
if (nextSnapshot === snapshot) {
|
|
552
|
+
return {
|
|
553
|
+
applied: true,
|
|
554
|
+
messages: [],
|
|
555
|
+
session: null,
|
|
556
|
+
snapshot,
|
|
557
|
+
statePatch: null
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
applied: true,
|
|
562
|
+
messages,
|
|
563
|
+
session: null,
|
|
564
|
+
snapshot: nextSnapshot,
|
|
565
|
+
statePatch: null
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
function applyActivityUpdatedStatePatch(snapshot, input) {
|
|
569
|
+
const statePatch = inlineStatePatchFromActivityUpdateData(input.data);
|
|
570
|
+
if (!statePatch || statePatch.agentSessionId !== input.agentSessionId) {
|
|
571
|
+
return emptyActivityUpdatedApplyResult(snapshot);
|
|
572
|
+
}
|
|
573
|
+
const existingSession = snapshot.sessions.find(
|
|
574
|
+
(session2) => session2.agentSessionId === input.agentSessionId
|
|
575
|
+
) ?? null;
|
|
576
|
+
if (!existingSession || isStaleStatePatch(existingSession, statePatch)) {
|
|
577
|
+
return emptyActivityUpdatedApplyResult(snapshot);
|
|
578
|
+
}
|
|
579
|
+
const session = agentActivitySessionFromInlineStatePatch({
|
|
580
|
+
existingSession,
|
|
581
|
+
patch: statePatch,
|
|
582
|
+
workspaceId: input.workspaceId
|
|
583
|
+
});
|
|
584
|
+
return {
|
|
585
|
+
applied: true,
|
|
586
|
+
messages: [],
|
|
587
|
+
session,
|
|
588
|
+
snapshot: upsertSnapshotSession(snapshot, session),
|
|
589
|
+
statePatch
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
function emptyActivityUpdatedApplyResult(snapshot) {
|
|
593
|
+
return {
|
|
594
|
+
applied: false,
|
|
595
|
+
messages: [],
|
|
596
|
+
session: null,
|
|
597
|
+
snapshot,
|
|
598
|
+
statePatch: null
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function applySessionEvent(snapshot, event) {
|
|
602
|
+
if (event.workspaceId && event.workspaceId !== snapshot.workspaceId) {
|
|
603
|
+
return snapshot;
|
|
604
|
+
}
|
|
605
|
+
const data = recordValue2(event.data) ?? {};
|
|
606
|
+
if (event.eventType === "message_update") {
|
|
607
|
+
const message = messageFromEvent(event, data);
|
|
608
|
+
return message ? mergeSnapshotMessages(snapshot, message.agentSessionId, [message]) : snapshot;
|
|
609
|
+
}
|
|
610
|
+
if (event.eventType === "session_update") {
|
|
611
|
+
const session = sessionFromEvent(snapshot.workspaceId, event, data);
|
|
612
|
+
return session ? upsertSnapshotSession(snapshot, session) : snapshot;
|
|
613
|
+
}
|
|
614
|
+
return snapshot;
|
|
615
|
+
}
|
|
616
|
+
function mergeSnapshotMessages(snapshot, agentSessionId, messages) {
|
|
617
|
+
const normalizedAgentSessionId = agentSessionId.trim();
|
|
618
|
+
if (!normalizedAgentSessionId || messages.length === 0) {
|
|
619
|
+
return snapshot;
|
|
620
|
+
}
|
|
621
|
+
const currentMessages = snapshot.sessionMessagesById[normalizedAgentSessionId] ?? [];
|
|
622
|
+
const mergedMessages = mergeAgentActivityMessages(currentMessages, messages);
|
|
623
|
+
if (areAgentActivityMessageArraysEqual(currentMessages, mergedMessages)) {
|
|
624
|
+
return snapshot;
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
...snapshot,
|
|
628
|
+
sessionMessagesById: {
|
|
629
|
+
...snapshot.sessionMessagesById,
|
|
630
|
+
[normalizedAgentSessionId]: mergedMessages
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
function upsertSnapshotSession(snapshot, session) {
|
|
635
|
+
const index = snapshot.sessions.findIndex(
|
|
636
|
+
(item) => item.agentSessionId === session.agentSessionId
|
|
637
|
+
);
|
|
638
|
+
if (index < 0) {
|
|
639
|
+
return {
|
|
640
|
+
...snapshot,
|
|
641
|
+
sessions: [...snapshot.sessions, session]
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
const sessions = [...snapshot.sessions];
|
|
645
|
+
sessions[index] = session;
|
|
646
|
+
return {
|
|
647
|
+
...snapshot,
|
|
648
|
+
sessions
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function removeSnapshotSession(snapshot, agentSessionId) {
|
|
652
|
+
const normalizedAgentSessionId = agentSessionId.trim();
|
|
653
|
+
if (!normalizedAgentSessionId) {
|
|
654
|
+
return snapshot;
|
|
655
|
+
}
|
|
656
|
+
const sessions = snapshot.sessions.filter(
|
|
657
|
+
(session) => session.agentSessionId !== normalizedAgentSessionId
|
|
658
|
+
);
|
|
659
|
+
if (sessions.length === snapshot.sessions.length && !snapshot.sessionMessagesById[normalizedAgentSessionId]) {
|
|
660
|
+
return snapshot;
|
|
661
|
+
}
|
|
662
|
+
const sessionMessagesById = { ...snapshot.sessionMessagesById };
|
|
663
|
+
delete sessionMessagesById[normalizedAgentSessionId];
|
|
664
|
+
return {
|
|
665
|
+
...snapshot,
|
|
666
|
+
sessions,
|
|
667
|
+
sessionMessagesById
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function shouldAutoRetainSessionEvents(session) {
|
|
671
|
+
if (!session.agentSessionId.trim()) {
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
switch (session.status.trim()) {
|
|
675
|
+
case "canceled":
|
|
676
|
+
case "completed":
|
|
677
|
+
case "failed":
|
|
678
|
+
return false;
|
|
679
|
+
default:
|
|
680
|
+
return true;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
function messageFromEvent(event, data) {
|
|
684
|
+
const source = recordValue2(data.message) ?? data;
|
|
685
|
+
const agentSessionId = stringValue(source.agentSessionId) || event.agentSessionId;
|
|
686
|
+
const messageId = stringValue(source.messageId);
|
|
687
|
+
const role = stringValue(source.role);
|
|
688
|
+
const kind = stringValue(source.kind);
|
|
689
|
+
if (!agentSessionId || !messageId || !role || !kind) {
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
workspaceId: stringValue(source.workspaceId) || event.workspaceId,
|
|
694
|
+
agentSessionId,
|
|
695
|
+
messageId,
|
|
696
|
+
id: numberValue(source.id),
|
|
697
|
+
version: numberValue(source.version) ?? 0,
|
|
698
|
+
turnId: nullableStringValue(source.turnId),
|
|
699
|
+
role,
|
|
700
|
+
kind,
|
|
701
|
+
status: nullableStringValue(source.status),
|
|
702
|
+
payload: recordValue2(source.payload) ?? {},
|
|
703
|
+
occurredAtUnixMs: numberValue(source.occurredAtUnixMs),
|
|
704
|
+
startedAtUnixMs: numberValue(source.startedAtUnixMs),
|
|
705
|
+
completedAtUnixMs: numberValue(source.completedAtUnixMs)
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function sessionFromEvent(workspaceId, event, data) {
|
|
709
|
+
const source = recordValue2(data.session) ?? data;
|
|
710
|
+
const agentSessionId = stringValue(source.agentSessionId) || event.agentSessionId;
|
|
711
|
+
if (!agentSessionId) {
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
workspaceId: stringValue(source.workspaceId) || workspaceId,
|
|
716
|
+
agentSessionId,
|
|
717
|
+
provider: stringValue(source.provider),
|
|
718
|
+
providerSessionId: nullableStringValue(source.providerSessionId),
|
|
719
|
+
model: nullableStringValue(source.model),
|
|
720
|
+
cwd: stringValue(source.cwd),
|
|
721
|
+
title: stringValue(source.title),
|
|
722
|
+
status: stringValue(source.status) || "unknown",
|
|
723
|
+
resumable: booleanValue(source.resumable),
|
|
724
|
+
currentPhase: nullableStringValue(source.currentPhase),
|
|
725
|
+
lastError: nullableStringValue(source.lastError),
|
|
726
|
+
messageVersion: numberValue(source.messageVersion),
|
|
727
|
+
lastEventUnixMs: numberValue(source.lastEventUnixMs),
|
|
728
|
+
startedAtUnixMs: numberValue(source.startedAtUnixMs),
|
|
729
|
+
endedAtUnixMs: numberValue(source.endedAtUnixMs),
|
|
730
|
+
createdAtUnixMs: numberValue(source.createdAtUnixMs),
|
|
731
|
+
updatedAtUnixMs: numberValue(source.updatedAtUnixMs)
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function inlineMessagesFromActivityUpdateData(data) {
|
|
735
|
+
const source = recordValue2(data);
|
|
736
|
+
const messages = Array.isArray(source?.messages) ? source.messages : [];
|
|
737
|
+
return messages.flatMap((message) => {
|
|
738
|
+
const record = recordValue2(message);
|
|
739
|
+
return record ? [record] : [];
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
function inlineMessageBelongsToSession(message, agentSessionId) {
|
|
743
|
+
const messageAgentSessionId = stringValue(message.agentSessionId);
|
|
744
|
+
return !messageAgentSessionId || messageAgentSessionId === agentSessionId;
|
|
745
|
+
}
|
|
746
|
+
function agentActivityMessageFromInlineMessage(input) {
|
|
747
|
+
return {
|
|
748
|
+
workspaceId: stringValue(input.message.workspaceId) || input.workspaceId,
|
|
749
|
+
agentSessionId: stringValue(input.message.agentSessionId) || input.agentSessionId,
|
|
750
|
+
messageId: stringValue(input.message.messageId),
|
|
751
|
+
id: numberValue(input.message.id),
|
|
752
|
+
version: numberValue(input.message.version) ?? 0,
|
|
753
|
+
turnId: nullableStringValue(input.message.turnId),
|
|
754
|
+
role: stringValue(input.message.role),
|
|
755
|
+
kind: stringValue(input.message.kind),
|
|
756
|
+
status: nullableStringValue(input.message.status),
|
|
757
|
+
payload: recordValue2(input.message.payload) ?? {},
|
|
758
|
+
occurredAtUnixMs: numberValue(input.message.occurredAtUnixMs),
|
|
759
|
+
startedAtUnixMs: numberValue(input.message.startedAtUnixMs),
|
|
760
|
+
completedAtUnixMs: numberValue(input.message.completedAtUnixMs)
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
function inlineStatePatchFromActivityUpdateData(data) {
|
|
764
|
+
const source = recordValue2(data);
|
|
765
|
+
const agentSessionId = stringValue(source?.agentSessionId);
|
|
766
|
+
if (!source || !agentSessionId) {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
const turn = recordValue2(source.turn);
|
|
770
|
+
return {
|
|
771
|
+
agentSessionId,
|
|
772
|
+
currentPhase: stringValue(source.currentPhase) || void 0,
|
|
773
|
+
cwd: stringValue(source.cwd) || void 0,
|
|
774
|
+
lastError: stringValue(source.lastError) || void 0,
|
|
775
|
+
lastEventUnixMs: numberValue(source.lastEventUnixMs),
|
|
776
|
+
lifecycleStatus: stringValue(source.lifecycleStatus) || void 0,
|
|
777
|
+
model: stringValue(source.model) || void 0,
|
|
778
|
+
occurredAtUnixMs: numberValue(source.occurredAtUnixMs),
|
|
779
|
+
provider: stringValue(source.provider) || void 0,
|
|
780
|
+
providerSessionId: stringValue(source.providerSessionId) || void 0,
|
|
781
|
+
startedAtUnixMs: numberValue(source.startedAtUnixMs),
|
|
782
|
+
endedAtUnixMs: numberValue(source.endedAtUnixMs),
|
|
783
|
+
title: stringValue(source.title) || void 0,
|
|
784
|
+
turn: turn ? {
|
|
785
|
+
completedAtUnixMs: numberValue(turn.completedAtUnixMs),
|
|
786
|
+
fileChanges: turn.fileChanges,
|
|
787
|
+
outcome: stringValue(turn.outcome) || void 0,
|
|
788
|
+
phase: stringValue(turn.phase) || void 0,
|
|
789
|
+
startedAtUnixMs: numberValue(turn.startedAtUnixMs),
|
|
790
|
+
turnId: stringValue(turn.turnId)
|
|
791
|
+
} : void 0,
|
|
792
|
+
workspaceId: stringValue(source.workspaceId) || void 0
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
function isStaleStatePatch(session, patch) {
|
|
796
|
+
const nextTime = patch.lastEventUnixMs ?? patch.occurredAtUnixMs;
|
|
797
|
+
const currentTime = session.lastEventUnixMs ?? session.updatedAtUnixMs;
|
|
798
|
+
return typeof nextTime === "number" && typeof currentTime === "number" && nextTime < currentTime;
|
|
799
|
+
}
|
|
800
|
+
function agentActivitySessionFromInlineStatePatch(input) {
|
|
801
|
+
return {
|
|
802
|
+
...input.existingSession,
|
|
803
|
+
workspaceId: input.patch.workspaceId ?? input.workspaceId,
|
|
804
|
+
agentSessionId: input.patch.agentSessionId,
|
|
805
|
+
provider: input.patch.provider ?? input.existingSession.provider,
|
|
806
|
+
providerSessionId: input.patch.providerSessionId ?? input.existingSession.providerSessionId,
|
|
807
|
+
model: input.patch.model ?? input.existingSession.model,
|
|
808
|
+
cwd: input.patch.cwd ?? input.existingSession.cwd,
|
|
809
|
+
title: input.patch.title ?? input.existingSession.title,
|
|
810
|
+
status: input.patch.lifecycleStatus ?? input.existingSession.status,
|
|
811
|
+
currentPhase: input.patch.currentPhase ?? input.patch.turn?.phase ?? input.existingSession.currentPhase,
|
|
812
|
+
lastError: input.patch.lastError ?? input.existingSession.lastError,
|
|
813
|
+
lastEventUnixMs: input.patch.lastEventUnixMs ?? input.patch.occurredAtUnixMs ?? input.existingSession.lastEventUnixMs,
|
|
814
|
+
startedAtUnixMs: input.patch.startedAtUnixMs ?? input.existingSession.startedAtUnixMs,
|
|
815
|
+
endedAtUnixMs: input.patch.endedAtUnixMs ?? input.existingSession.endedAtUnixMs,
|
|
816
|
+
updatedAtUnixMs: input.patch.occurredAtUnixMs ?? input.patch.lastEventUnixMs ?? input.existingSession.updatedAtUnixMs
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
function cloneAgentActivityStatePatch(statePatch) {
|
|
820
|
+
return {
|
|
821
|
+
...statePatch,
|
|
822
|
+
turn: statePatch.turn ? { ...statePatch.turn } : void 0
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
function recordValue2(value) {
|
|
826
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
827
|
+
}
|
|
828
|
+
function stringValue(value) {
|
|
829
|
+
return typeof value === "string" ? value.trim() : "";
|
|
830
|
+
}
|
|
831
|
+
function nullableStringValue(value) {
|
|
832
|
+
return typeof value === "string" ? value : value === null ? null : void 0;
|
|
833
|
+
}
|
|
834
|
+
function numberValue(value) {
|
|
835
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
836
|
+
}
|
|
837
|
+
function booleanValue(value) {
|
|
838
|
+
return typeof value === "boolean" ? value : void 0;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// src/selectors.ts
|
|
842
|
+
var terminalMessageStatuses = /* @__PURE__ */ new Set([
|
|
843
|
+
"completed",
|
|
844
|
+
"canceled",
|
|
845
|
+
"failed",
|
|
846
|
+
"rejected",
|
|
847
|
+
"answered",
|
|
848
|
+
"resolved"
|
|
849
|
+
]);
|
|
850
|
+
function resolveAgentActivityPromptImagesSupported(input) {
|
|
851
|
+
return promptImagesSupportedFromRuntimeContext(input.sessionRuntimeContext) ?? promptImagesSupportedFromRuntimeContext(
|
|
852
|
+
input.composerOptions?.runtimeContext
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
function selectNeedsAttentionCount(snapshot) {
|
|
856
|
+
return selectNeedsAttentionItems(snapshot).length;
|
|
857
|
+
}
|
|
858
|
+
function selectSessionDisplayStatuses(snapshot) {
|
|
859
|
+
const needsAttentionSessionIds = new Set(
|
|
860
|
+
selectNeedsAttentionItems(snapshot).map((item) => item.agentSessionId)
|
|
861
|
+
);
|
|
862
|
+
return new Map(
|
|
863
|
+
snapshot.sessions.map((session) => [
|
|
864
|
+
session.agentSessionId,
|
|
865
|
+
normalizeAgentActivityDisplayStatus(session.status, {
|
|
866
|
+
currentPhase: session.currentPhase,
|
|
867
|
+
needsAttention: needsAttentionSessionIds.has(session.agentSessionId)
|
|
868
|
+
})
|
|
869
|
+
])
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
function normalizeAgentActivityDisplayStatus(status, options = {}) {
|
|
873
|
+
if (options.needsAttention) {
|
|
874
|
+
return "waiting";
|
|
875
|
+
}
|
|
876
|
+
const normalizedStatus = normalizeStatus(status);
|
|
877
|
+
const normalizedCurrentPhase = normalizeStatus(options.currentPhase);
|
|
878
|
+
switch (normalizedStatus) {
|
|
879
|
+
case "completed":
|
|
880
|
+
case "done":
|
|
881
|
+
case "success":
|
|
882
|
+
case "succeeded":
|
|
883
|
+
return "completed";
|
|
884
|
+
case "canceled":
|
|
885
|
+
case "cancelled":
|
|
886
|
+
return "canceled";
|
|
887
|
+
case "error":
|
|
888
|
+
case "failed":
|
|
889
|
+
return "failed";
|
|
890
|
+
default:
|
|
891
|
+
break;
|
|
892
|
+
}
|
|
893
|
+
switch (normalizedCurrentPhase) {
|
|
894
|
+
case "completed":
|
|
895
|
+
case "done":
|
|
896
|
+
case "success":
|
|
897
|
+
case "succeeded":
|
|
898
|
+
return "completed";
|
|
899
|
+
case "canceled":
|
|
900
|
+
case "cancelled":
|
|
901
|
+
return "canceled";
|
|
902
|
+
case "error":
|
|
903
|
+
case "failed":
|
|
904
|
+
return "failed";
|
|
905
|
+
case "awaiting_approval":
|
|
906
|
+
case "waiting":
|
|
907
|
+
case "waiting_approval":
|
|
908
|
+
case "waiting_input":
|
|
909
|
+
return "waiting";
|
|
910
|
+
case "running":
|
|
911
|
+
case "streaming":
|
|
912
|
+
case "working":
|
|
913
|
+
return "working";
|
|
914
|
+
default:
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
switch (normalizedStatus) {
|
|
918
|
+
case "running":
|
|
919
|
+
case "streaming":
|
|
920
|
+
case "working":
|
|
921
|
+
return "working";
|
|
922
|
+
case "awaiting_approval":
|
|
923
|
+
case "waiting":
|
|
924
|
+
case "waiting_approval":
|
|
925
|
+
case "waiting_input":
|
|
926
|
+
return "waiting";
|
|
927
|
+
case "idle":
|
|
928
|
+
case "ready":
|
|
929
|
+
default:
|
|
930
|
+
return "idle";
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
function selectNeedsAttentionItems(snapshot) {
|
|
934
|
+
const sessionsById = new Map(
|
|
935
|
+
snapshot.sessions.map((session) => [session.agentSessionId, session])
|
|
936
|
+
);
|
|
937
|
+
const items = [];
|
|
938
|
+
for (const [agentSessionId, messages] of Object.entries(
|
|
939
|
+
snapshot.sessionMessagesById
|
|
940
|
+
)) {
|
|
941
|
+
const session = sessionsById.get(agentSessionId);
|
|
942
|
+
for (const message of messages) {
|
|
943
|
+
const kind = needsAttentionKindForMessage(message);
|
|
944
|
+
if (!kind) {
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
947
|
+
items.push(
|
|
948
|
+
needsAttentionItemFromMessage(snapshot, message, kind, session)
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return items.sort(
|
|
953
|
+
(left, right) => right.occurredAtUnixMs - left.occurredAtUnixMs || left.id.localeCompare(right.id)
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
function needsAttentionKindForMessage(message) {
|
|
957
|
+
if (isTerminalMessageStatus(message.status)) {
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
const kind = normalizeKind(message.kind);
|
|
961
|
+
const payloadType = normalizeMetadataValue(message.payload.type);
|
|
962
|
+
const action = normalizeMetadataValue(message.payload.action);
|
|
963
|
+
const requestType = normalizeMetadataValue(message.payload.requestType);
|
|
964
|
+
const callType = normalizeMetadataValue(message.payload.callType);
|
|
965
|
+
const toolName = normalizeMetadataValue(message.payload.toolName);
|
|
966
|
+
const name = normalizeMetadataValue(message.payload.name);
|
|
967
|
+
const status = normalizeStatus(message.status);
|
|
968
|
+
const payloadStatus = normalizeMetadataValue(message.payload.status);
|
|
969
|
+
if (includesAny(
|
|
970
|
+
[
|
|
971
|
+
kind,
|
|
972
|
+
payloadType,
|
|
973
|
+
requestType,
|
|
974
|
+
callType,
|
|
975
|
+
toolName,
|
|
976
|
+
name,
|
|
977
|
+
status,
|
|
978
|
+
payloadStatus
|
|
979
|
+
].join(" "),
|
|
980
|
+
["permission", "approval"]
|
|
981
|
+
)) {
|
|
982
|
+
return "permission";
|
|
983
|
+
}
|
|
984
|
+
if (includesAny(
|
|
985
|
+
[
|
|
986
|
+
kind,
|
|
987
|
+
payloadType,
|
|
988
|
+
action,
|
|
989
|
+
callType,
|
|
990
|
+
toolName,
|
|
991
|
+
name,
|
|
992
|
+
status,
|
|
993
|
+
payloadStatus
|
|
994
|
+
].join(" "),
|
|
995
|
+
["ask_user", "ask-user", "askuserquestion", "question"]
|
|
996
|
+
)) {
|
|
997
|
+
return "question";
|
|
998
|
+
}
|
|
999
|
+
if (includesAny([kind, payloadType, action, toolName, name].join(" "), [
|
|
1000
|
+
"constraint"
|
|
1001
|
+
])) {
|
|
1002
|
+
return "constraint";
|
|
1003
|
+
}
|
|
1004
|
+
if (isWaitingStatus(status, payloadStatus) && (message.role === "assistant" || message.role === "system")) {
|
|
1005
|
+
return "other";
|
|
1006
|
+
}
|
|
1007
|
+
return null;
|
|
1008
|
+
}
|
|
1009
|
+
function needsAttentionItemFromMessage(snapshot, message, kind, session) {
|
|
1010
|
+
return {
|
|
1011
|
+
id: `${message.agentSessionId}:${message.messageId}`,
|
|
1012
|
+
workspaceId: message.workspaceId || snapshot.workspaceId,
|
|
1013
|
+
agentSessionId: message.agentSessionId,
|
|
1014
|
+
provider: session?.provider ?? "",
|
|
1015
|
+
title: session?.title ?? "",
|
|
1016
|
+
cwd: session?.cwd ?? "",
|
|
1017
|
+
kind,
|
|
1018
|
+
summary: messageSummary(message),
|
|
1019
|
+
occurredAtUnixMs: message.occurredAtUnixMs ?? message.startedAtUnixMs ?? message.completedAtUnixMs ?? session?.updatedAtUnixMs ?? session?.lastEventUnixMs ?? 0
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
function messageSummary(message) {
|
|
1023
|
+
return stringValue2(message.payload.summary) || stringValue2(message.payload.title) || stringValue2(message.payload.content) || stringValue2(message.payload.text) || message.kind;
|
|
1024
|
+
}
|
|
1025
|
+
function isTerminalMessageStatus(status) {
|
|
1026
|
+
return terminalMessageStatuses.has(normalizeStatus(status));
|
|
1027
|
+
}
|
|
1028
|
+
function normalizeStatus(status) {
|
|
1029
|
+
return status?.trim().toLowerCase() ?? "";
|
|
1030
|
+
}
|
|
1031
|
+
function isWaitingStatus(...values) {
|
|
1032
|
+
return values.some((value) => {
|
|
1033
|
+
const normalized = value.trim().toLowerCase();
|
|
1034
|
+
return normalized === "waiting" || normalized.startsWith("waiting_");
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
function normalizeKind(kind) {
|
|
1038
|
+
return kind.trim().toLowerCase();
|
|
1039
|
+
}
|
|
1040
|
+
function normalizeMetadataValue(value) {
|
|
1041
|
+
return stringValue2(value).toLowerCase();
|
|
1042
|
+
}
|
|
1043
|
+
function includesAny(value, needles) {
|
|
1044
|
+
return needles.some((needle) => value.includes(needle));
|
|
1045
|
+
}
|
|
1046
|
+
function stringValue2(value) {
|
|
1047
|
+
return typeof value === "string" ? value.trim() : "";
|
|
1048
|
+
}
|
|
1049
|
+
function promptImagesSupportedFromRuntimeContext(runtimeContext) {
|
|
1050
|
+
const promptCapabilities = recordValue3(runtimeContext?.promptCapabilities);
|
|
1051
|
+
const value = promptCapabilities?.image;
|
|
1052
|
+
if (typeof value === "boolean") {
|
|
1053
|
+
return value;
|
|
1054
|
+
}
|
|
1055
|
+
if (typeof value === "string") {
|
|
1056
|
+
const normalized = value.trim().toLowerCase();
|
|
1057
|
+
if (normalized === "true") {
|
|
1058
|
+
return true;
|
|
1059
|
+
}
|
|
1060
|
+
if (normalized === "false") {
|
|
1061
|
+
return false;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
function recordValue3(value) {
|
|
1067
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1068
|
+
}
|
|
1069
|
+
export {
|
|
1070
|
+
cloneAgentActivityMessage,
|
|
1071
|
+
cloneAgentActivitySnapshot,
|
|
1072
|
+
compareAgentActivityMessages,
|
|
1073
|
+
createAgentActivityController,
|
|
1074
|
+
createEmptyAgentActivitySnapshot,
|
|
1075
|
+
latestAgentActivityMessageVersion,
|
|
1076
|
+
mergeAgentActivityMessages,
|
|
1077
|
+
normalizeAgentActivityDisplayStatus,
|
|
1078
|
+
resolveAgentActivityPromptImagesSupported,
|
|
1079
|
+
selectNeedsAttentionCount,
|
|
1080
|
+
selectNeedsAttentionItems,
|
|
1081
|
+
selectSessionDisplayStatuses
|
|
1082
|
+
};
|
|
1083
|
+
//# sourceMappingURL=index.js.map
|