@swarmify/agents-mcp 0.2.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/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/agents.d.ts +85 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +663 -0
- package/dist/agents.js.map +1 -0
- package/dist/api.d.ts +68 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +219 -0
- package/dist/api.js.map +1 -0
- package/dist/file_ops.d.ts +6 -0
- package/dist/file_ops.d.ts.map +1 -0
- package/dist/file_ops.js +59 -0
- package/dist/file_ops.js.map +1 -0
- package/dist/hello.d.ts +3 -0
- package/dist/hello.d.ts.map +1 -0
- package/dist/hello.js +13 -0
- package/dist/hello.js.map +1 -0
- package/dist/hello_world.d.ts +2 -0
- package/dist/hello_world.d.ts.map +1 -0
- package/dist/hello_world.js +12 -0
- package/dist/hello_world.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers.d.ts +5 -0
- package/dist/parsers.d.ts.map +1 -0
- package/dist/parsers.js +704 -0
- package/dist/parsers.js.map +1 -0
- package/dist/persistence.d.ts +10 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +103 -0
- package/dist/persistence.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +244 -0
- package/dist/server.js.map +1 -0
- package/dist/summarizer.d.ts +58 -0
- package/dist/summarizer.d.ts.map +1 -0
- package/dist/summarizer.js +766 -0
- package/dist/summarizer.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
import { extractFileOpsFromBash } from './file_ops.js';
|
|
2
|
+
function extractErrorFromRawEvents(events, maxChars = 500) {
|
|
3
|
+
const errorKeywords = ['error', 'Error', 'ERROR', 'failed', 'Failed', 'FAILED', 'exception', 'Exception'];
|
|
4
|
+
for (let i = events.length - 1; i >= Math.max(0, events.length - 20); i--) {
|
|
5
|
+
const event = events[i];
|
|
6
|
+
if (event.type === 'raw') {
|
|
7
|
+
const content = event.content || '';
|
|
8
|
+
if (typeof content === 'string') {
|
|
9
|
+
const contentLower = content.toLowerCase();
|
|
10
|
+
if (errorKeywords.some(keyword => contentLower.includes(keyword.toLowerCase()))) {
|
|
11
|
+
let errorMsg = content.trim();
|
|
12
|
+
if (errorMsg.length > maxChars) {
|
|
13
|
+
errorMsg = errorMsg.substring(0, maxChars - 3) + '...';
|
|
14
|
+
}
|
|
15
|
+
return errorMsg;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export const PRIORITY = {
|
|
23
|
+
critical: [
|
|
24
|
+
'error',
|
|
25
|
+
'result',
|
|
26
|
+
'file_write',
|
|
27
|
+
'file_delete',
|
|
28
|
+
'file_create',
|
|
29
|
+
],
|
|
30
|
+
important: [
|
|
31
|
+
'tool_use',
|
|
32
|
+
'bash',
|
|
33
|
+
'file_read',
|
|
34
|
+
'thinking',
|
|
35
|
+
'message',
|
|
36
|
+
],
|
|
37
|
+
verbose: [
|
|
38
|
+
'thinking_delta',
|
|
39
|
+
'message_delta',
|
|
40
|
+
'init',
|
|
41
|
+
'turn_start',
|
|
42
|
+
'user_message',
|
|
43
|
+
'raw',
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Collapse sequential events of the same type into summary entries.
|
|
48
|
+
* Returns a cleaner list of events suitable for output.
|
|
49
|
+
*/
|
|
50
|
+
export function collapseEvents(events, maxEvents = 20) {
|
|
51
|
+
if (events.length === 0)
|
|
52
|
+
return [];
|
|
53
|
+
const collapsed = [];
|
|
54
|
+
let i = 0;
|
|
55
|
+
while (i < events.length) {
|
|
56
|
+
const event = events[i];
|
|
57
|
+
const eventType = event.type || 'unknown';
|
|
58
|
+
// For thinking events, collapse sequential ones
|
|
59
|
+
if (eventType === 'thinking') {
|
|
60
|
+
let count = 1;
|
|
61
|
+
let lastContent = event.content || '';
|
|
62
|
+
let j = i + 1;
|
|
63
|
+
while (j < events.length && events[j].type === 'thinking') {
|
|
64
|
+
count++;
|
|
65
|
+
if (events[j].content) {
|
|
66
|
+
lastContent = events[j].content;
|
|
67
|
+
}
|
|
68
|
+
j++;
|
|
69
|
+
}
|
|
70
|
+
if (count > 1) {
|
|
71
|
+
collapsed.push({
|
|
72
|
+
type: 'thinking_summary',
|
|
73
|
+
count: count,
|
|
74
|
+
last_content: lastContent.length > 200 ? lastContent.slice(-200) : lastContent,
|
|
75
|
+
timestamp: event.timestamp,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
else if (event.content) {
|
|
79
|
+
collapsed.push(event);
|
|
80
|
+
}
|
|
81
|
+
i = j;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
// For message events, keep the last content
|
|
85
|
+
if (eventType === 'message') {
|
|
86
|
+
collapsed.push({
|
|
87
|
+
type: 'message',
|
|
88
|
+
content: event.content?.length > 500 ? event.content.slice(-500) : event.content,
|
|
89
|
+
complete: event.complete,
|
|
90
|
+
timestamp: event.timestamp,
|
|
91
|
+
});
|
|
92
|
+
i++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
// Keep tool events as-is but truncate large content
|
|
96
|
+
if (['bash', 'file_write', 'file_read', 'file_create', 'file_delete', 'tool_use'].includes(eventType)) {
|
|
97
|
+
const cleaned = { ...event };
|
|
98
|
+
if (cleaned.command && cleaned.command.length > 200) {
|
|
99
|
+
cleaned.command = cleaned.command.slice(0, 200) + '...';
|
|
100
|
+
}
|
|
101
|
+
collapsed.push(cleaned);
|
|
102
|
+
i++;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// Keep errors and results
|
|
106
|
+
if (['error', 'result'].includes(eventType)) {
|
|
107
|
+
collapsed.push(event);
|
|
108
|
+
i++;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// Skip other event types
|
|
112
|
+
i++;
|
|
113
|
+
}
|
|
114
|
+
// Return only the last N events
|
|
115
|
+
if (collapsed.length > maxEvents) {
|
|
116
|
+
return collapsed.slice(-maxEvents);
|
|
117
|
+
}
|
|
118
|
+
return collapsed;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get a breakdown of tool calls by type.
|
|
122
|
+
*/
|
|
123
|
+
export function getToolBreakdown(events) {
|
|
124
|
+
const breakdown = {};
|
|
125
|
+
for (const event of events) {
|
|
126
|
+
const eventType = event.type || '';
|
|
127
|
+
if (eventType === 'bash') {
|
|
128
|
+
breakdown['bash'] = (breakdown['bash'] || 0) + 1;
|
|
129
|
+
}
|
|
130
|
+
else if (eventType === 'file_write') {
|
|
131
|
+
breakdown['file_write'] = (breakdown['file_write'] || 0) + 1;
|
|
132
|
+
}
|
|
133
|
+
else if (eventType === 'file_read') {
|
|
134
|
+
breakdown['file_read'] = (breakdown['file_read'] || 0) + 1;
|
|
135
|
+
}
|
|
136
|
+
else if (eventType === 'file_create') {
|
|
137
|
+
breakdown['file_create'] = (breakdown['file_create'] || 0) + 1;
|
|
138
|
+
}
|
|
139
|
+
else if (eventType === 'file_delete') {
|
|
140
|
+
breakdown['file_delete'] = (breakdown['file_delete'] || 0) + 1;
|
|
141
|
+
}
|
|
142
|
+
else if (eventType === 'tool_use') {
|
|
143
|
+
const tool = event.tool || 'unknown';
|
|
144
|
+
breakdown[tool] = (breakdown[tool] || 0) + 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return breakdown;
|
|
148
|
+
}
|
|
149
|
+
export function groupAndFlattenEvents(events) {
|
|
150
|
+
if (events.length === 0)
|
|
151
|
+
return [];
|
|
152
|
+
const grouped = [];
|
|
153
|
+
let i = 0;
|
|
154
|
+
while (i < events.length) {
|
|
155
|
+
const event = events[i];
|
|
156
|
+
const eventType = event.type || 'unknown';
|
|
157
|
+
if (eventType === 'message' || eventType === 'thinking') {
|
|
158
|
+
let count = 1;
|
|
159
|
+
let combinedContent = event.content || '';
|
|
160
|
+
let j = i + 1;
|
|
161
|
+
while (j < events.length && events[j].type === eventType) {
|
|
162
|
+
count++;
|
|
163
|
+
if (events[j].content) {
|
|
164
|
+
combinedContent += (combinedContent ? '\n' : '') + events[j].content;
|
|
165
|
+
}
|
|
166
|
+
j++;
|
|
167
|
+
}
|
|
168
|
+
const flattened = {
|
|
169
|
+
type: eventType,
|
|
170
|
+
content: combinedContent.length > 1000 ? combinedContent.slice(-1000) : combinedContent,
|
|
171
|
+
};
|
|
172
|
+
if (count > 1) {
|
|
173
|
+
flattened.count = count;
|
|
174
|
+
}
|
|
175
|
+
grouped.push(flattened);
|
|
176
|
+
i = j;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (['file_write', 'file_create', 'file_read', 'file_delete'].includes(eventType)) {
|
|
180
|
+
const path = event.path || '';
|
|
181
|
+
if (!path) {
|
|
182
|
+
i++;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const pathGroup = {
|
|
186
|
+
type: eventType,
|
|
187
|
+
path: path,
|
|
188
|
+
count: 1,
|
|
189
|
+
};
|
|
190
|
+
let j = i + 1;
|
|
191
|
+
while (j < events.length && events[j].type === eventType && events[j].path === path) {
|
|
192
|
+
pathGroup.count++;
|
|
193
|
+
j++;
|
|
194
|
+
}
|
|
195
|
+
grouped.push(pathGroup);
|
|
196
|
+
i = j;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (eventType === 'bash') {
|
|
200
|
+
const command = event.command || '';
|
|
201
|
+
if (!command) {
|
|
202
|
+
i++;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const bashGroup = {
|
|
206
|
+
type: 'bash',
|
|
207
|
+
commands: [command],
|
|
208
|
+
count: 1,
|
|
209
|
+
};
|
|
210
|
+
let j = i + 1;
|
|
211
|
+
while (j < events.length && events[j].type === 'bash') {
|
|
212
|
+
bashGroup.commands.push(events[j].command || '');
|
|
213
|
+
bashGroup.count++;
|
|
214
|
+
j++;
|
|
215
|
+
}
|
|
216
|
+
if (bashGroup.commands.length > 5) {
|
|
217
|
+
bashGroup.commands = bashGroup.commands.slice(-5);
|
|
218
|
+
bashGroup.truncated = bashGroup.count - 5;
|
|
219
|
+
}
|
|
220
|
+
grouped.push(bashGroup);
|
|
221
|
+
i = j;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (eventType === 'tool_use') {
|
|
225
|
+
const flattened = {
|
|
226
|
+
type: 'tool_use',
|
|
227
|
+
tool: event.tool || 'unknown',
|
|
228
|
+
};
|
|
229
|
+
if (event.name)
|
|
230
|
+
flattened.name = event.name;
|
|
231
|
+
if (event.input) {
|
|
232
|
+
const inputStr = typeof event.input === 'string' ? event.input : JSON.stringify(event.input);
|
|
233
|
+
flattened.input = inputStr.length > 200 ? inputStr.slice(0, 200) + '...' : inputStr;
|
|
234
|
+
}
|
|
235
|
+
grouped.push(flattened);
|
|
236
|
+
i++;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (['error', 'result'].includes(eventType)) {
|
|
240
|
+
const flattened = {
|
|
241
|
+
type: eventType,
|
|
242
|
+
};
|
|
243
|
+
if (event.message)
|
|
244
|
+
flattened.message = event.message;
|
|
245
|
+
if (event.content)
|
|
246
|
+
flattened.content = event.content;
|
|
247
|
+
if (event.status)
|
|
248
|
+
flattened.status = event.status;
|
|
249
|
+
grouped.push(flattened);
|
|
250
|
+
i++;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
i++;
|
|
254
|
+
}
|
|
255
|
+
return grouped;
|
|
256
|
+
}
|
|
257
|
+
export class AgentSummary {
|
|
258
|
+
agentId;
|
|
259
|
+
agentType;
|
|
260
|
+
status;
|
|
261
|
+
duration = null;
|
|
262
|
+
filesModified = new Set();
|
|
263
|
+
filesCreated = new Set();
|
|
264
|
+
filesRead = new Set();
|
|
265
|
+
filesDeleted = new Set();
|
|
266
|
+
toolsUsed = new Set();
|
|
267
|
+
toolCallCount = 0;
|
|
268
|
+
bashCommands = [];
|
|
269
|
+
errors = [];
|
|
270
|
+
warnings = [];
|
|
271
|
+
finalMessage = null;
|
|
272
|
+
eventCount = 0;
|
|
273
|
+
lastActivity = null;
|
|
274
|
+
eventsCache = [];
|
|
275
|
+
constructor(agentId, agentType, status, duration = null, eventCount = 0) {
|
|
276
|
+
this.agentId = agentId;
|
|
277
|
+
this.agentType = agentType;
|
|
278
|
+
this.status = status;
|
|
279
|
+
this.duration = duration;
|
|
280
|
+
this.eventCount = eventCount;
|
|
281
|
+
}
|
|
282
|
+
toDict(detailLevel = 'standard') {
|
|
283
|
+
const base = {
|
|
284
|
+
agent_id: this.agentId,
|
|
285
|
+
agent_type: this.agentType,
|
|
286
|
+
status: this.status,
|
|
287
|
+
};
|
|
288
|
+
if (detailLevel === 'brief') {
|
|
289
|
+
return {
|
|
290
|
+
...base,
|
|
291
|
+
duration: this.duration,
|
|
292
|
+
tool_call_count: this.toolCallCount,
|
|
293
|
+
last_activity: this.lastActivity,
|
|
294
|
+
files_modified: Array.from(this.filesModified).slice(0, 5),
|
|
295
|
+
files_created: Array.from(this.filesCreated).slice(0, 5),
|
|
296
|
+
has_errors: this.errors.length > 0,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
else if (detailLevel === 'standard') {
|
|
300
|
+
return {
|
|
301
|
+
...base,
|
|
302
|
+
duration: this.duration,
|
|
303
|
+
files_modified: Array.from(this.filesModified),
|
|
304
|
+
files_created: Array.from(this.filesCreated),
|
|
305
|
+
tools_used: Array.from(this.toolsUsed),
|
|
306
|
+
tool_call_count: this.toolCallCount,
|
|
307
|
+
errors: this.errors.slice(0, 3),
|
|
308
|
+
final_message: this.truncate(this.finalMessage, 2000),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
return {
|
|
313
|
+
...base,
|
|
314
|
+
duration: this.duration,
|
|
315
|
+
files_modified: Array.from(this.filesModified),
|
|
316
|
+
files_created: Array.from(this.filesCreated),
|
|
317
|
+
files_read: Array.from(this.filesRead),
|
|
318
|
+
files_deleted: Array.from(this.filesDeleted),
|
|
319
|
+
tools_used: Array.from(this.toolsUsed),
|
|
320
|
+
tool_call_count: this.toolCallCount,
|
|
321
|
+
bash_commands: this.bashCommands.slice(-10),
|
|
322
|
+
errors: this.errors,
|
|
323
|
+
warnings: this.warnings,
|
|
324
|
+
final_message: this.finalMessage,
|
|
325
|
+
event_count: this.eventCount,
|
|
326
|
+
last_activity: this.lastActivity,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
truncate(text, maxLen) {
|
|
331
|
+
if (!text)
|
|
332
|
+
return null;
|
|
333
|
+
if (text.length <= maxLen)
|
|
334
|
+
return text;
|
|
335
|
+
return text.substring(0, maxLen - 3) + '...';
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
export function summarizeEvents(agentId, agentType, status, events, duration = null) {
|
|
339
|
+
const summary = new AgentSummary(agentId, agentType, status, duration, events.length);
|
|
340
|
+
summary.eventsCache = events;
|
|
341
|
+
summary.agentId = agentId;
|
|
342
|
+
summary.agentType = agentType;
|
|
343
|
+
summary.status = status;
|
|
344
|
+
for (const event of events) {
|
|
345
|
+
const eventType = event.type || 'unknown';
|
|
346
|
+
summary.lastActivity = eventType;
|
|
347
|
+
if (eventType === 'file_write') {
|
|
348
|
+
const path = event.path || '';
|
|
349
|
+
if (path) {
|
|
350
|
+
summary.filesModified.add(path);
|
|
351
|
+
summary.toolCallCount++;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else if (eventType === 'file_create') {
|
|
355
|
+
const path = event.path || '';
|
|
356
|
+
if (path) {
|
|
357
|
+
summary.filesCreated.add(path);
|
|
358
|
+
summary.toolCallCount++;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
else if (eventType === 'file_read') {
|
|
362
|
+
const path = event.path || '';
|
|
363
|
+
if (path) {
|
|
364
|
+
summary.filesRead.add(path);
|
|
365
|
+
summary.toolCallCount++;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else if (eventType === 'file_delete') {
|
|
369
|
+
const path = event.path || '';
|
|
370
|
+
if (path) {
|
|
371
|
+
summary.filesDeleted.add(path);
|
|
372
|
+
summary.toolCallCount++;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
else if (eventType === 'directory_list') {
|
|
376
|
+
summary.toolCallCount++;
|
|
377
|
+
}
|
|
378
|
+
else if (eventType === 'tool_use') {
|
|
379
|
+
const tool = event.tool || 'unknown';
|
|
380
|
+
summary.toolsUsed.add(tool);
|
|
381
|
+
summary.toolCallCount++;
|
|
382
|
+
}
|
|
383
|
+
else if (eventType === 'bash') {
|
|
384
|
+
const command = event.command || '';
|
|
385
|
+
summary.toolsUsed.add('bash');
|
|
386
|
+
if (command) {
|
|
387
|
+
summary.bashCommands.push(command);
|
|
388
|
+
const [filesRead, filesWritten, filesDeleted] = extractFileOpsFromBash(command);
|
|
389
|
+
for (const path of filesRead) {
|
|
390
|
+
summary.filesRead.add(path);
|
|
391
|
+
}
|
|
392
|
+
for (const path of filesWritten) {
|
|
393
|
+
summary.filesModified.add(path);
|
|
394
|
+
}
|
|
395
|
+
for (const path of filesDeleted) {
|
|
396
|
+
summary.filesDeleted.add(path);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
summary.toolCallCount++;
|
|
400
|
+
}
|
|
401
|
+
else if (eventType === 'message') {
|
|
402
|
+
const content = event.content || '';
|
|
403
|
+
if (content) {
|
|
404
|
+
summary.finalMessage = content;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
else if (eventType === 'error') {
|
|
408
|
+
let errorMsg = null;
|
|
409
|
+
for (const key of ['message', 'content', 'error', 'error_message', 'details']) {
|
|
410
|
+
if (event[key]) {
|
|
411
|
+
errorMsg = String(event[key]);
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (!errorMsg) {
|
|
416
|
+
errorMsg = extractErrorFromRawEvents(summary.eventsCache);
|
|
417
|
+
}
|
|
418
|
+
if (errorMsg) {
|
|
419
|
+
if (errorMsg.length > 500) {
|
|
420
|
+
errorMsg = errorMsg.substring(0, 497) + '...';
|
|
421
|
+
}
|
|
422
|
+
summary.errors.push(errorMsg);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else if (eventType === 'warning') {
|
|
426
|
+
const warningMsg = event.message || event.content || '';
|
|
427
|
+
if (warningMsg) {
|
|
428
|
+
summary.warnings.push(warningMsg);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else if (eventType === 'result') {
|
|
432
|
+
if (event.status === 'error') {
|
|
433
|
+
let errorMsg = null;
|
|
434
|
+
for (const key of ['message', 'error', 'error_message', 'error_details', 'details']) {
|
|
435
|
+
if (event[key]) {
|
|
436
|
+
errorMsg = String(event[key]);
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (!errorMsg) {
|
|
441
|
+
errorMsg = extractErrorFromRawEvents(summary.eventsCache);
|
|
442
|
+
}
|
|
443
|
+
if (errorMsg) {
|
|
444
|
+
if (errorMsg.length > 500) {
|
|
445
|
+
errorMsg = errorMsg.substring(0, 497) + '...';
|
|
446
|
+
}
|
|
447
|
+
summary.errors.push(errorMsg);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (!summary.duration && event.duration_ms) {
|
|
451
|
+
const durationMs = event.duration_ms;
|
|
452
|
+
const seconds = durationMs / 1000;
|
|
453
|
+
if (seconds < 60) {
|
|
454
|
+
if (seconds % 1 === 0) {
|
|
455
|
+
summary.duration = `${Math.floor(seconds)} seconds`;
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
summary.duration = `${seconds.toFixed(1)} seconds`;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
const minutes = seconds / 60;
|
|
463
|
+
summary.duration = `${minutes.toFixed(1)} minutes`;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return summary;
|
|
469
|
+
}
|
|
470
|
+
export function getDelta(agentId, agentType, status, events, since // Optional: ISO timestamp (string) or event index (number)
|
|
471
|
+
) {
|
|
472
|
+
// Filter events by timestamp (string) or index (number)
|
|
473
|
+
let newEvents;
|
|
474
|
+
let sinceEvent = 0;
|
|
475
|
+
if (since === undefined || since === null) {
|
|
476
|
+
// No filter - return all events
|
|
477
|
+
newEvents = events;
|
|
478
|
+
}
|
|
479
|
+
else if (typeof since === 'number') {
|
|
480
|
+
// Backward compatibility: event index
|
|
481
|
+
sinceEvent = since;
|
|
482
|
+
newEvents = events.slice(sinceEvent);
|
|
483
|
+
}
|
|
484
|
+
else if (typeof since === 'string') {
|
|
485
|
+
// New behavior: timestamp filtering
|
|
486
|
+
const sinceDate = new Date(since);
|
|
487
|
+
newEvents = events.filter((e) => {
|
|
488
|
+
if (!e.timestamp)
|
|
489
|
+
return false;
|
|
490
|
+
const eventDate = new Date(e.timestamp);
|
|
491
|
+
return eventDate > sinceDate;
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
newEvents = events;
|
|
496
|
+
}
|
|
497
|
+
if (newEvents.length === 0) {
|
|
498
|
+
return {
|
|
499
|
+
agent_id: agentId,
|
|
500
|
+
status: status,
|
|
501
|
+
since_event: sinceEvent, // For backward compatibility
|
|
502
|
+
new_events_count: 0,
|
|
503
|
+
has_changes: false,
|
|
504
|
+
new_files_created: [],
|
|
505
|
+
new_files_modified: [],
|
|
506
|
+
new_files_read: [],
|
|
507
|
+
new_files_deleted: [],
|
|
508
|
+
new_bash_commands: [],
|
|
509
|
+
new_messages: [],
|
|
510
|
+
new_tool_count: 0,
|
|
511
|
+
new_errors: [],
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
const summary = summarizeEvents(agentId, agentType, status, newEvents);
|
|
515
|
+
return {
|
|
516
|
+
agent_id: agentId,
|
|
517
|
+
agent_type: agentType,
|
|
518
|
+
status: status,
|
|
519
|
+
since_event: sinceEvent, // For backward compatibility
|
|
520
|
+
new_events_count: newEvents.length,
|
|
521
|
+
current_event_count: sinceEvent + newEvents.length, // For backward compatibility
|
|
522
|
+
has_changes: true,
|
|
523
|
+
new_files_created: Array.from(summary.filesCreated),
|
|
524
|
+
new_files_modified: Array.from(summary.filesModified),
|
|
525
|
+
new_files_read: Array.from(summary.filesRead),
|
|
526
|
+
new_files_deleted: Array.from(summary.filesDeleted),
|
|
527
|
+
new_bash_commands: summary.bashCommands.slice(-15),
|
|
528
|
+
new_messages: getLastMessages(newEvents, 5),
|
|
529
|
+
new_tool_count: summary.toolCallCount,
|
|
530
|
+
new_tool_calls: newEvents // For backward compatibility
|
|
531
|
+
.filter((e) => ['tool_use', 'bash', 'file_write'].includes(e.type))
|
|
532
|
+
.slice(-5)
|
|
533
|
+
.map((e) => `${e.tool || 'unknown'}: ${e.command || e.path || ''}`),
|
|
534
|
+
latest_message: summary.finalMessage, // For backward compatibility
|
|
535
|
+
new_errors: summary.errors,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
export function filterEventsByPriority(events, includeLevels = null) {
|
|
539
|
+
if (!includeLevels) {
|
|
540
|
+
includeLevels = ['critical', 'important'];
|
|
541
|
+
}
|
|
542
|
+
const allowedTypes = new Set();
|
|
543
|
+
for (const level of includeLevels) {
|
|
544
|
+
const types = PRIORITY[level] || [];
|
|
545
|
+
for (const type of types) {
|
|
546
|
+
allowedTypes.add(type);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return events.filter(e => allowedTypes.has(e.type));
|
|
550
|
+
}
|
|
551
|
+
export function getLastTool(events) {
|
|
552
|
+
if (events.length === 0)
|
|
553
|
+
return null;
|
|
554
|
+
const lastEvent = events[events.length - 1];
|
|
555
|
+
const eventType = lastEvent.type || '';
|
|
556
|
+
const validTypes = ['tool_use', 'bash', 'file_write', 'file_create', 'file_read', 'file_delete', 'message', 'error', 'result'];
|
|
557
|
+
if (validTypes.includes(eventType)) {
|
|
558
|
+
return eventType;
|
|
559
|
+
}
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
export function getToolUses(events) {
|
|
563
|
+
const toolUses = [];
|
|
564
|
+
for (const event of events) {
|
|
565
|
+
if (event.type === 'tool_use') {
|
|
566
|
+
const tool = event.tool || 'unknown';
|
|
567
|
+
const args = event.args || {};
|
|
568
|
+
toolUses.push({ tool, args });
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return toolUses;
|
|
572
|
+
}
|
|
573
|
+
export function getLastMessages(events, count = 3) {
|
|
574
|
+
const messages = [];
|
|
575
|
+
let currentBuffer = '';
|
|
576
|
+
let isCollecting = false;
|
|
577
|
+
for (const event of events) {
|
|
578
|
+
if (event.type === 'message') {
|
|
579
|
+
const content = event.content || '';
|
|
580
|
+
// For streaming events (delta=true), content fragments should be joined.
|
|
581
|
+
// We don't add newlines because these are likely parts of the same sentence/block.
|
|
582
|
+
currentBuffer += content;
|
|
583
|
+
isCollecting = true;
|
|
584
|
+
// If we hit an explicitly complete message, treat it as a boundary
|
|
585
|
+
if (event.complete) {
|
|
586
|
+
if (currentBuffer.trim()) {
|
|
587
|
+
messages.push(currentBuffer);
|
|
588
|
+
}
|
|
589
|
+
currentBuffer = '';
|
|
590
|
+
isCollecting = false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
// Any non-message event breaks the message stream
|
|
595
|
+
if (isCollecting) {
|
|
596
|
+
if (currentBuffer.trim()) {
|
|
597
|
+
messages.push(currentBuffer);
|
|
598
|
+
}
|
|
599
|
+
currentBuffer = '';
|
|
600
|
+
isCollecting = false;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// Handle any remaining buffer at the end
|
|
605
|
+
if (isCollecting && currentBuffer.trim()) {
|
|
606
|
+
messages.push(currentBuffer);
|
|
607
|
+
}
|
|
608
|
+
return messages.slice(-count);
|
|
609
|
+
}
|
|
610
|
+
export function getQuickStatus(agentId, agentType, status, events) {
|
|
611
|
+
const filesCreatedSet = new Set();
|
|
612
|
+
const filesModifiedSet = new Set();
|
|
613
|
+
const filesDeletedSet = new Set();
|
|
614
|
+
const filesReadSet = new Set();
|
|
615
|
+
let toolCount = 0;
|
|
616
|
+
let hasErrors = false;
|
|
617
|
+
const commands = [];
|
|
618
|
+
let lastMessage = null;
|
|
619
|
+
for (const event of events) {
|
|
620
|
+
const eventType = event.type || '';
|
|
621
|
+
if (eventType === 'message') {
|
|
622
|
+
const content = event.content || '';
|
|
623
|
+
if (content) {
|
|
624
|
+
lastMessage = content;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
else if (eventType === 'file_create') {
|
|
628
|
+
const path = event.path || '';
|
|
629
|
+
if (path) {
|
|
630
|
+
filesCreatedSet.add(path);
|
|
631
|
+
}
|
|
632
|
+
toolCount++;
|
|
633
|
+
}
|
|
634
|
+
else if (eventType === 'file_write') {
|
|
635
|
+
const path = event.path || '';
|
|
636
|
+
if (path) {
|
|
637
|
+
filesModifiedSet.add(path);
|
|
638
|
+
}
|
|
639
|
+
toolCount++;
|
|
640
|
+
}
|
|
641
|
+
else if (eventType === 'file_delete') {
|
|
642
|
+
const path = event.path || '';
|
|
643
|
+
if (path) {
|
|
644
|
+
filesDeletedSet.add(path);
|
|
645
|
+
}
|
|
646
|
+
toolCount++;
|
|
647
|
+
}
|
|
648
|
+
else if (eventType === 'file_read') {
|
|
649
|
+
const path = event.path || '';
|
|
650
|
+
if (path) {
|
|
651
|
+
filesReadSet.add(path);
|
|
652
|
+
}
|
|
653
|
+
toolCount++;
|
|
654
|
+
}
|
|
655
|
+
else if (eventType === 'bash') {
|
|
656
|
+
toolCount++;
|
|
657
|
+
const cmd = event.command || '';
|
|
658
|
+
if (cmd) {
|
|
659
|
+
commands.push(cmd.length > 100 ? cmd.substring(0, 97) + '...' : cmd);
|
|
660
|
+
const [filesRead, filesWritten, filesDeleted] = extractFileOpsFromBash(cmd);
|
|
661
|
+
for (const path of filesRead) {
|
|
662
|
+
filesReadSet.add(path);
|
|
663
|
+
}
|
|
664
|
+
for (const path of filesWritten) {
|
|
665
|
+
filesModifiedSet.add(path);
|
|
666
|
+
}
|
|
667
|
+
for (const path of filesDeleted) {
|
|
668
|
+
filesDeletedSet.add(path);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
else if (eventType === 'directory_list') {
|
|
673
|
+
toolCount++;
|
|
674
|
+
}
|
|
675
|
+
else if (['tool_use'].includes(eventType)) {
|
|
676
|
+
toolCount++;
|
|
677
|
+
}
|
|
678
|
+
else if (eventType === 'error' || (eventType === 'result' && event.status === 'error')) {
|
|
679
|
+
hasErrors = true;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return {
|
|
683
|
+
agent_id: agentId,
|
|
684
|
+
agent_type: agentType,
|
|
685
|
+
status: status,
|
|
686
|
+
files_created: filesCreatedSet.size,
|
|
687
|
+
files_modified: filesModifiedSet.size,
|
|
688
|
+
files_deleted: filesDeletedSet.size,
|
|
689
|
+
files_read: filesReadSet.size,
|
|
690
|
+
tool_count: toolCount,
|
|
691
|
+
last_commands: commands.slice(-3),
|
|
692
|
+
has_errors: hasErrors,
|
|
693
|
+
last_message: lastMessage,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
export function getStatusSummary(agentId, agentType, status, events, duration = null) {
|
|
697
|
+
if (events.length === 0) {
|
|
698
|
+
if (status === 'running') {
|
|
699
|
+
return 'Just started, no activity yet';
|
|
700
|
+
}
|
|
701
|
+
return 'No activity';
|
|
702
|
+
}
|
|
703
|
+
let fileCount = 0;
|
|
704
|
+
let bashCount = 0;
|
|
705
|
+
let toolCount = 0;
|
|
706
|
+
let hasErrors = false;
|
|
707
|
+
for (const event of events) {
|
|
708
|
+
const eventType = event.type || '';
|
|
709
|
+
if (['file_write', 'file_create', 'file_delete'].includes(eventType)) {
|
|
710
|
+
fileCount++;
|
|
711
|
+
}
|
|
712
|
+
else if (eventType === 'bash') {
|
|
713
|
+
bashCount++;
|
|
714
|
+
}
|
|
715
|
+
else if (['tool_use', 'file_read'].includes(eventType)) {
|
|
716
|
+
toolCount++;
|
|
717
|
+
}
|
|
718
|
+
else if (['error', 'result'].includes(eventType)) {
|
|
719
|
+
if (event.status === 'error') {
|
|
720
|
+
hasErrors = true;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
const totalTools = bashCount + toolCount;
|
|
725
|
+
const parts = [];
|
|
726
|
+
if (status === 'running') {
|
|
727
|
+
parts.push('Running');
|
|
728
|
+
}
|
|
729
|
+
else if (status === 'completed') {
|
|
730
|
+
if (hasErrors) {
|
|
731
|
+
parts.push('Completed with errors');
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
parts.push('Completed successfully');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
else if (status === 'failed') {
|
|
738
|
+
parts.push('Failed');
|
|
739
|
+
}
|
|
740
|
+
else if (status === 'stopped') {
|
|
741
|
+
parts.push('Stopped');
|
|
742
|
+
}
|
|
743
|
+
if (fileCount > 0) {
|
|
744
|
+
parts.push(`modified ${fileCount} file${fileCount !== 1 ? 's' : ''}`);
|
|
745
|
+
}
|
|
746
|
+
if (bashCount > 0) {
|
|
747
|
+
parts.push(`used bash ${bashCount} time${bashCount !== 1 ? 's' : ''}`);
|
|
748
|
+
}
|
|
749
|
+
if (toolCount > 0 && bashCount === 0) {
|
|
750
|
+
parts.push(`used ${toolCount} tool${toolCount !== 1 ? 's' : ''}`);
|
|
751
|
+
}
|
|
752
|
+
if (totalTools > 0 && bashCount > 0) {
|
|
753
|
+
parts.push(`used ${totalTools} tool${totalTools !== 1 ? 's' : ''}`);
|
|
754
|
+
}
|
|
755
|
+
if (hasErrors && status === 'running') {
|
|
756
|
+
parts.push('has errors');
|
|
757
|
+
}
|
|
758
|
+
if (parts.length === 0) {
|
|
759
|
+
if (status === 'running') {
|
|
760
|
+
return `Running, ${events.length} event${events.length !== 1 ? 's' : ''} so far`;
|
|
761
|
+
}
|
|
762
|
+
return status.charAt(0).toUpperCase() + status.slice(1);
|
|
763
|
+
}
|
|
764
|
+
return parts.join(', ');
|
|
765
|
+
}
|
|
766
|
+
//# sourceMappingURL=summarizer.js.map
|