@lov3kaizen/agentsea-debugger 0.5.1
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 +364 -0
- package/dist/Recorder-Dc9qR2V2.d.ts +138 -0
- package/dist/ReplayEngine-Dyyqy5bw.d.ts +54 -0
- package/dist/analysis/index.d.ts +4 -0
- package/dist/analysis/index.js +1840 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/diff-CLShBdWe.d.ts +24 -0
- package/dist/index-1W27DYJt.d.ts +366 -0
- package/dist/index-DRrKPSo9.d.ts +233 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +6232 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/agentsea/index.d.ts +8 -0
- package/dist/integrations/agentsea/index.js +5826 -0
- package/dist/integrations/agentsea/index.js.map +1 -0
- package/dist/recording/index.d.ts +91 -0
- package/dist/recording/index.js +1207 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording.types-Ck7pbikw.d.ts +281 -0
- package/dist/replay/index.d.ts +80 -0
- package/dist/replay/index.js +1112 -0
- package/dist/replay/index.js.map +1 -0
- package/dist/replay.types-C9hJizI-.d.ts +76 -0
- package/dist/storage/index.d.ts +135 -0
- package/dist/storage/index.js +588 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/visualization/index.d.ts +123 -0
- package/dist/visualization/index.js +789 -0
- package/dist/visualization/index.js.map +1 -0
- package/dist/visualization.types-dWjGE037.d.ts +98 -0
- package/package.json +99 -0
|
@@ -0,0 +1,1207 @@
|
|
|
1
|
+
import { EventEmitter } from 'eventemitter3';
|
|
2
|
+
import { nanoid } from 'nanoid';
|
|
3
|
+
|
|
4
|
+
// src/recording/Recorder.ts
|
|
5
|
+
function generateId(prefix = "") {
|
|
6
|
+
const id = nanoid(12);
|
|
7
|
+
return prefix ? `${prefix}_${id}` : id;
|
|
8
|
+
}
|
|
9
|
+
function now() {
|
|
10
|
+
return Date.now();
|
|
11
|
+
}
|
|
12
|
+
function deepClone(obj) {
|
|
13
|
+
if (obj === null || typeof obj !== "object") {
|
|
14
|
+
return obj;
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(obj)) {
|
|
17
|
+
return obj.map((item) => deepClone(item));
|
|
18
|
+
}
|
|
19
|
+
if (obj instanceof Date) {
|
|
20
|
+
return new Date(obj.getTime());
|
|
21
|
+
}
|
|
22
|
+
if (obj instanceof Map) {
|
|
23
|
+
const clonedMap = /* @__PURE__ */ new Map();
|
|
24
|
+
obj.forEach((value, key) => {
|
|
25
|
+
clonedMap.set(deepClone(key), deepClone(value));
|
|
26
|
+
});
|
|
27
|
+
return clonedMap;
|
|
28
|
+
}
|
|
29
|
+
if (obj instanceof Set) {
|
|
30
|
+
const clonedSet = /* @__PURE__ */ new Set();
|
|
31
|
+
obj.forEach((value) => {
|
|
32
|
+
clonedSet.add(deepClone(value));
|
|
33
|
+
});
|
|
34
|
+
return clonedSet;
|
|
35
|
+
}
|
|
36
|
+
const clonedObj = {};
|
|
37
|
+
for (const key in obj) {
|
|
38
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
39
|
+
clonedObj[key] = deepClone(
|
|
40
|
+
obj[key]
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return clonedObj;
|
|
45
|
+
}
|
|
46
|
+
function safeStringify(obj, indent = 0) {
|
|
47
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
48
|
+
return JSON.stringify(
|
|
49
|
+
obj,
|
|
50
|
+
(key, value) => {
|
|
51
|
+
if (typeof value === "object" && value !== null) {
|
|
52
|
+
if (seen.has(value)) {
|
|
53
|
+
return "[Circular]";
|
|
54
|
+
}
|
|
55
|
+
seen.add(value);
|
|
56
|
+
}
|
|
57
|
+
if (value instanceof Error) {
|
|
58
|
+
return {
|
|
59
|
+
name: value.name,
|
|
60
|
+
message: value.message,
|
|
61
|
+
stack: value.stack
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (value instanceof Map) {
|
|
65
|
+
return Object.fromEntries(value);
|
|
66
|
+
}
|
|
67
|
+
if (value instanceof Set) {
|
|
68
|
+
return Array.from(value);
|
|
69
|
+
}
|
|
70
|
+
if (typeof value === "function") {
|
|
71
|
+
return `[Function: ${value.name || "anonymous"}]`;
|
|
72
|
+
}
|
|
73
|
+
if (typeof value === "bigint") {
|
|
74
|
+
return value.toString();
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
},
|
|
78
|
+
indent
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
function formatDuration(ms) {
|
|
82
|
+
if (ms < 1e3) {
|
|
83
|
+
return `${ms}ms`;
|
|
84
|
+
}
|
|
85
|
+
const seconds = Math.floor(ms / 1e3);
|
|
86
|
+
const minutes = Math.floor(seconds / 60);
|
|
87
|
+
const hours = Math.floor(minutes / 60);
|
|
88
|
+
if (hours > 0) {
|
|
89
|
+
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
90
|
+
}
|
|
91
|
+
if (minutes > 0) {
|
|
92
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
93
|
+
}
|
|
94
|
+
return `${seconds}s`;
|
|
95
|
+
}
|
|
96
|
+
function estimateSize(obj) {
|
|
97
|
+
return safeStringify(obj).length * 2;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/utils/diff.ts
|
|
101
|
+
function diff(lhs, rhs, path = []) {
|
|
102
|
+
const differences = [];
|
|
103
|
+
if (lhs === rhs) {
|
|
104
|
+
return differences;
|
|
105
|
+
}
|
|
106
|
+
if (lhs === null || lhs === void 0) {
|
|
107
|
+
if (rhs !== null && rhs !== void 0) {
|
|
108
|
+
differences.push({ path, kind: "N", rhs });
|
|
109
|
+
}
|
|
110
|
+
return differences;
|
|
111
|
+
}
|
|
112
|
+
if (rhs === null || rhs === void 0) {
|
|
113
|
+
differences.push({ path, kind: "D", lhs });
|
|
114
|
+
return differences;
|
|
115
|
+
}
|
|
116
|
+
const lhsType = typeof lhs;
|
|
117
|
+
const rhsType = typeof rhs;
|
|
118
|
+
if (lhsType !== rhsType) {
|
|
119
|
+
differences.push({ path, kind: "E", lhs, rhs });
|
|
120
|
+
return differences;
|
|
121
|
+
}
|
|
122
|
+
if (lhsType !== "object") {
|
|
123
|
+
if (lhs !== rhs) {
|
|
124
|
+
differences.push({ path, kind: "E", lhs, rhs });
|
|
125
|
+
}
|
|
126
|
+
return differences;
|
|
127
|
+
}
|
|
128
|
+
if (Array.isArray(lhs) && Array.isArray(rhs)) {
|
|
129
|
+
const maxLen = Math.max(lhs.length, rhs.length);
|
|
130
|
+
for (let i = 0; i < maxLen; i++) {
|
|
131
|
+
if (i >= lhs.length) {
|
|
132
|
+
differences.push({
|
|
133
|
+
path,
|
|
134
|
+
kind: "A",
|
|
135
|
+
index: i,
|
|
136
|
+
item: { path: [...path, String(i)], kind: "N", rhs: rhs[i] }
|
|
137
|
+
});
|
|
138
|
+
} else if (i >= rhs.length) {
|
|
139
|
+
differences.push({
|
|
140
|
+
path,
|
|
141
|
+
kind: "A",
|
|
142
|
+
index: i,
|
|
143
|
+
item: { path: [...path, String(i)], kind: "D", lhs: lhs[i] }
|
|
144
|
+
});
|
|
145
|
+
} else {
|
|
146
|
+
const itemDiffs = diff(lhs[i], rhs[i], [...path, String(i)]);
|
|
147
|
+
differences.push(...itemDiffs);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return differences;
|
|
151
|
+
}
|
|
152
|
+
if (typeof lhs === "object" && typeof rhs === "object") {
|
|
153
|
+
const lhsObj = lhs;
|
|
154
|
+
const rhsObj = rhs;
|
|
155
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(lhsObj), ...Object.keys(rhsObj)]);
|
|
156
|
+
for (const key of allKeys) {
|
|
157
|
+
const keyPath = [...path, key];
|
|
158
|
+
if (!(key in lhsObj)) {
|
|
159
|
+
differences.push({ path: keyPath, kind: "N", rhs: rhsObj[key] });
|
|
160
|
+
} else if (!(key in rhsObj)) {
|
|
161
|
+
differences.push({ path: keyPath, kind: "D", lhs: lhsObj[key] });
|
|
162
|
+
} else {
|
|
163
|
+
const propDiffs = diff(lhsObj[key], rhsObj[key], keyPath);
|
|
164
|
+
differences.push(...propDiffs);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return differences;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/recording/Snapshot.ts
|
|
172
|
+
var DEFAULT_OPTIONS = {
|
|
173
|
+
maxSnapshots: 100,
|
|
174
|
+
useDiffs: true,
|
|
175
|
+
compressionLevel: 0
|
|
176
|
+
};
|
|
177
|
+
var SnapshotManager = class {
|
|
178
|
+
snapshots = /* @__PURE__ */ new Map();
|
|
179
|
+
snapshotOrder = [];
|
|
180
|
+
options;
|
|
181
|
+
fullSnapshotInterval = 10;
|
|
182
|
+
constructor(options) {
|
|
183
|
+
this.options = {
|
|
184
|
+
...DEFAULT_OPTIONS,
|
|
185
|
+
...options
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Create a snapshot
|
|
190
|
+
*/
|
|
191
|
+
create(state, stepIndex) {
|
|
192
|
+
const id = generateId("snap");
|
|
193
|
+
const timestamp = now();
|
|
194
|
+
const shouldBeFull = !this.options.useDiffs || this.snapshotOrder.length === 0 || this.snapshotOrder.length % this.fullSnapshotInterval === 0;
|
|
195
|
+
let snapshot;
|
|
196
|
+
if (shouldBeFull) {
|
|
197
|
+
snapshot = {
|
|
198
|
+
id,
|
|
199
|
+
timestamp,
|
|
200
|
+
stepIndex,
|
|
201
|
+
state: deepClone(state),
|
|
202
|
+
size: estimateSize(state),
|
|
203
|
+
isFull: true
|
|
204
|
+
};
|
|
205
|
+
} else {
|
|
206
|
+
const previousId = this.snapshotOrder[this.snapshotOrder.length - 1];
|
|
207
|
+
const previous = this.snapshots.get(previousId);
|
|
208
|
+
if (previous) {
|
|
209
|
+
const stateDiff = diff(previous.state, state);
|
|
210
|
+
snapshot = {
|
|
211
|
+
id,
|
|
212
|
+
timestamp,
|
|
213
|
+
stepIndex,
|
|
214
|
+
state: deepClone(state),
|
|
215
|
+
size: estimateSize(state),
|
|
216
|
+
isFull: false,
|
|
217
|
+
diff: stateDiff,
|
|
218
|
+
previousId
|
|
219
|
+
};
|
|
220
|
+
} else {
|
|
221
|
+
snapshot = {
|
|
222
|
+
id,
|
|
223
|
+
timestamp,
|
|
224
|
+
stepIndex,
|
|
225
|
+
state: deepClone(state),
|
|
226
|
+
size: estimateSize(state),
|
|
227
|
+
isFull: true
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
this.snapshots.set(id, snapshot);
|
|
232
|
+
this.snapshotOrder.push(id);
|
|
233
|
+
this.enforceLimit();
|
|
234
|
+
return snapshot;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get a snapshot by ID
|
|
238
|
+
*/
|
|
239
|
+
get(id) {
|
|
240
|
+
return this.snapshots.get(id);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get all snapshots
|
|
244
|
+
*/
|
|
245
|
+
getAll() {
|
|
246
|
+
return this.snapshotOrder.map((id) => this.snapshots.get(id));
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get snapshot at or before a step
|
|
250
|
+
*/
|
|
251
|
+
getAtStep(stepIndex) {
|
|
252
|
+
let closestSnapshot;
|
|
253
|
+
for (const id of this.snapshotOrder) {
|
|
254
|
+
const snapshot = this.snapshots.get(id);
|
|
255
|
+
if (snapshot && snapshot.stepIndex <= stepIndex) {
|
|
256
|
+
closestSnapshot = snapshot;
|
|
257
|
+
} else {
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return closestSnapshot;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Get snapshot after a step
|
|
265
|
+
*/
|
|
266
|
+
getAfterStep(stepIndex) {
|
|
267
|
+
for (const id of this.snapshotOrder) {
|
|
268
|
+
const snapshot = this.snapshots.get(id);
|
|
269
|
+
if (snapshot && snapshot.stepIndex > stepIndex) {
|
|
270
|
+
return snapshot;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return void 0;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get snapshots in a range
|
|
277
|
+
*/
|
|
278
|
+
getInRange(startStep, endStep) {
|
|
279
|
+
return this.getAll().filter(
|
|
280
|
+
(snap) => snap.stepIndex >= startStep && snap.stepIndex <= endStep
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Restore state from a snapshot
|
|
285
|
+
*/
|
|
286
|
+
restore(id) {
|
|
287
|
+
const snapshot = this.snapshots.get(id);
|
|
288
|
+
if (!snapshot) {
|
|
289
|
+
return void 0;
|
|
290
|
+
}
|
|
291
|
+
return deepClone(snapshot.state);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get the latest snapshot
|
|
295
|
+
*/
|
|
296
|
+
getLatest() {
|
|
297
|
+
if (this.snapshotOrder.length === 0) {
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
const latestId = this.snapshotOrder[this.snapshotOrder.length - 1];
|
|
301
|
+
return this.snapshots.get(latestId);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Get the first snapshot
|
|
305
|
+
*/
|
|
306
|
+
getFirst() {
|
|
307
|
+
if (this.snapshotOrder.length === 0) {
|
|
308
|
+
return void 0;
|
|
309
|
+
}
|
|
310
|
+
const firstId = this.snapshotOrder[0];
|
|
311
|
+
return this.snapshots.get(firstId);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Delete a snapshot
|
|
315
|
+
*/
|
|
316
|
+
delete(id) {
|
|
317
|
+
const existed = this.snapshots.delete(id);
|
|
318
|
+
if (existed) {
|
|
319
|
+
this.snapshotOrder = this.snapshotOrder.filter((i) => i !== id);
|
|
320
|
+
}
|
|
321
|
+
return existed;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Clear all snapshots
|
|
325
|
+
*/
|
|
326
|
+
clear() {
|
|
327
|
+
this.snapshots.clear();
|
|
328
|
+
this.snapshotOrder = [];
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Get snapshot count
|
|
332
|
+
*/
|
|
333
|
+
get count() {
|
|
334
|
+
return this.snapshots.size;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Get total size of all snapshots
|
|
338
|
+
*/
|
|
339
|
+
getTotalSize() {
|
|
340
|
+
let total = 0;
|
|
341
|
+
for (const snapshot of this.snapshots.values()) {
|
|
342
|
+
total += snapshot.size ?? 0;
|
|
343
|
+
}
|
|
344
|
+
return total;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Compact snapshots by merging diffs
|
|
348
|
+
*/
|
|
349
|
+
compact() {
|
|
350
|
+
const fullSnapshots = this.getAll().filter(
|
|
351
|
+
(snap) => snap.isFull
|
|
352
|
+
);
|
|
353
|
+
this.clear();
|
|
354
|
+
for (const snapshot of fullSnapshots) {
|
|
355
|
+
this.snapshots.set(snapshot.id, snapshot);
|
|
356
|
+
this.snapshotOrder.push(snapshot.id);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Compare two snapshots
|
|
361
|
+
*/
|
|
362
|
+
compare(id1, id2) {
|
|
363
|
+
const snap1 = this.snapshots.get(id1);
|
|
364
|
+
const snap2 = this.snapshots.get(id2);
|
|
365
|
+
if (!snap1 || !snap2) {
|
|
366
|
+
return void 0;
|
|
367
|
+
}
|
|
368
|
+
return diff(snap1.state, snap2.state);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Export snapshots
|
|
372
|
+
*/
|
|
373
|
+
export() {
|
|
374
|
+
return this.getAll().map((snap) => ({
|
|
375
|
+
id: snap.id,
|
|
376
|
+
timestamp: snap.timestamp,
|
|
377
|
+
stepIndex: snap.stepIndex,
|
|
378
|
+
state: snap.state,
|
|
379
|
+
size: snap.size
|
|
380
|
+
}));
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Import snapshots
|
|
384
|
+
*/
|
|
385
|
+
import(snapshots) {
|
|
386
|
+
this.clear();
|
|
387
|
+
for (const snapshot of snapshots) {
|
|
388
|
+
const incremental = {
|
|
389
|
+
...snapshot,
|
|
390
|
+
isFull: true
|
|
391
|
+
};
|
|
392
|
+
this.snapshots.set(snapshot.id, incremental);
|
|
393
|
+
this.snapshotOrder.push(snapshot.id);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Enforce max snapshots limit
|
|
398
|
+
*/
|
|
399
|
+
enforceLimit() {
|
|
400
|
+
while (this.snapshotOrder.length > this.options.maxSnapshots) {
|
|
401
|
+
const oldestId = this.snapshotOrder.shift();
|
|
402
|
+
if (oldestId) {
|
|
403
|
+
this.snapshots.delete(oldestId);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
function createSnapshotManager(options) {
|
|
409
|
+
return new SnapshotManager(options);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/recording/Checkpoint.ts
|
|
413
|
+
var CheckpointManager = class {
|
|
414
|
+
checkpoints = /* @__PURE__ */ new Map();
|
|
415
|
+
checkpointOrder = [];
|
|
416
|
+
/**
|
|
417
|
+
* Create a checkpoint
|
|
418
|
+
*/
|
|
419
|
+
create(options) {
|
|
420
|
+
const checkpoint = {
|
|
421
|
+
id: generateId("cp"),
|
|
422
|
+
recordingId: options.recordingId,
|
|
423
|
+
name: options.name,
|
|
424
|
+
description: options.description,
|
|
425
|
+
stepIndex: options.stepIndex,
|
|
426
|
+
timestamp: now(),
|
|
427
|
+
state: deepClone(options.state),
|
|
428
|
+
automatic: options.automatic ?? false
|
|
429
|
+
};
|
|
430
|
+
this.checkpoints.set(checkpoint.id, checkpoint);
|
|
431
|
+
this.checkpointOrder.push(checkpoint.id);
|
|
432
|
+
return checkpoint;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Get a checkpoint by ID
|
|
436
|
+
*/
|
|
437
|
+
get(id) {
|
|
438
|
+
return this.checkpoints.get(id);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Get checkpoint by name
|
|
442
|
+
*/
|
|
443
|
+
getByName(name) {
|
|
444
|
+
for (const checkpoint of this.checkpoints.values()) {
|
|
445
|
+
if (checkpoint.name === name) {
|
|
446
|
+
return checkpoint;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return void 0;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Get all checkpoints
|
|
453
|
+
*/
|
|
454
|
+
getAll() {
|
|
455
|
+
return this.checkpointOrder.map((id) => this.checkpoints.get(id));
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Get checkpoints matching filter
|
|
459
|
+
*/
|
|
460
|
+
filter(options) {
|
|
461
|
+
return this.getAll().filter((cp) => {
|
|
462
|
+
if (options.namePattern) {
|
|
463
|
+
if (typeof options.namePattern === "string") {
|
|
464
|
+
if (!cp.name.includes(options.namePattern)) {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
} else if (!options.namePattern.test(cp.name)) {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (options.stepRange) {
|
|
472
|
+
if (options.stepRange.min !== void 0 && cp.stepIndex < options.stepRange.min) {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
if (options.stepRange.max !== void 0 && cp.stepIndex > options.stepRange.max) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (options.timeRange) {
|
|
480
|
+
if (options.timeRange.after !== void 0 && cp.timestamp < options.timeRange.after) {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
if (options.timeRange.before !== void 0 && cp.timestamp > options.timeRange.before) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (options.automatic !== void 0 && cp.automatic !== options.automatic) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
return true;
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Get checkpoint at or before a step
|
|
495
|
+
*/
|
|
496
|
+
getAtStep(stepIndex) {
|
|
497
|
+
let closestCheckpoint;
|
|
498
|
+
for (const id of this.checkpointOrder) {
|
|
499
|
+
const checkpoint = this.checkpoints.get(id);
|
|
500
|
+
if (checkpoint && checkpoint.stepIndex <= stepIndex) {
|
|
501
|
+
closestCheckpoint = checkpoint;
|
|
502
|
+
} else {
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return closestCheckpoint;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Get checkpoint after a step
|
|
510
|
+
*/
|
|
511
|
+
getAfterStep(stepIndex) {
|
|
512
|
+
for (const id of this.checkpointOrder) {
|
|
513
|
+
const checkpoint = this.checkpoints.get(id);
|
|
514
|
+
if (checkpoint && checkpoint.stepIndex > stepIndex) {
|
|
515
|
+
return checkpoint;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return void 0;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Get checkpoints in step range
|
|
522
|
+
*/
|
|
523
|
+
getInRange(startStep, endStep) {
|
|
524
|
+
return this.getAll().filter(
|
|
525
|
+
(cp) => cp.stepIndex >= startStep && cp.stepIndex <= endStep
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Get state from checkpoint
|
|
530
|
+
*/
|
|
531
|
+
getState(id) {
|
|
532
|
+
const checkpoint = this.checkpoints.get(id);
|
|
533
|
+
if (!checkpoint) {
|
|
534
|
+
return void 0;
|
|
535
|
+
}
|
|
536
|
+
return deepClone(checkpoint.state);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Update checkpoint
|
|
540
|
+
*/
|
|
541
|
+
update(id, updates) {
|
|
542
|
+
const checkpoint = this.checkpoints.get(id);
|
|
543
|
+
if (!checkpoint) {
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
if (updates.name !== void 0) {
|
|
547
|
+
checkpoint.name = updates.name;
|
|
548
|
+
}
|
|
549
|
+
if (updates.description !== void 0) {
|
|
550
|
+
checkpoint.description = updates.description;
|
|
551
|
+
}
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Delete a checkpoint
|
|
556
|
+
*/
|
|
557
|
+
delete(id) {
|
|
558
|
+
const existed = this.checkpoints.delete(id);
|
|
559
|
+
if (existed) {
|
|
560
|
+
this.checkpointOrder = this.checkpointOrder.filter((i) => i !== id);
|
|
561
|
+
}
|
|
562
|
+
return existed;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Clear all checkpoints
|
|
566
|
+
*/
|
|
567
|
+
clear() {
|
|
568
|
+
this.checkpoints.clear();
|
|
569
|
+
this.checkpointOrder = [];
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Get checkpoint count
|
|
573
|
+
*/
|
|
574
|
+
get count() {
|
|
575
|
+
return this.checkpoints.size;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Get the latest checkpoint
|
|
579
|
+
*/
|
|
580
|
+
getLatest() {
|
|
581
|
+
if (this.checkpointOrder.length === 0) {
|
|
582
|
+
return void 0;
|
|
583
|
+
}
|
|
584
|
+
const latestId = this.checkpointOrder[this.checkpointOrder.length - 1];
|
|
585
|
+
return this.checkpoints.get(latestId);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Get the first checkpoint
|
|
589
|
+
*/
|
|
590
|
+
getFirst() {
|
|
591
|
+
if (this.checkpointOrder.length === 0) {
|
|
592
|
+
return void 0;
|
|
593
|
+
}
|
|
594
|
+
const firstId = this.checkpointOrder[0];
|
|
595
|
+
return this.checkpoints.get(firstId);
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Get manual checkpoints only
|
|
599
|
+
*/
|
|
600
|
+
getManual() {
|
|
601
|
+
return this.filter({ automatic: false });
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Get automatic checkpoints only
|
|
605
|
+
*/
|
|
606
|
+
getAutomatic() {
|
|
607
|
+
return this.filter({ automatic: true });
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Export checkpoints
|
|
611
|
+
*/
|
|
612
|
+
export() {
|
|
613
|
+
return this.getAll().map((cp) => ({
|
|
614
|
+
id: cp.id,
|
|
615
|
+
recordingId: cp.recordingId,
|
|
616
|
+
name: cp.name,
|
|
617
|
+
description: cp.description,
|
|
618
|
+
stepIndex: cp.stepIndex,
|
|
619
|
+
timestamp: cp.timestamp,
|
|
620
|
+
state: cp.state,
|
|
621
|
+
automatic: cp.automatic
|
|
622
|
+
}));
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Import checkpoints
|
|
626
|
+
*/
|
|
627
|
+
import(checkpoints) {
|
|
628
|
+
this.clear();
|
|
629
|
+
for (const checkpoint of checkpoints) {
|
|
630
|
+
this.checkpoints.set(checkpoint.id, deepClone(checkpoint));
|
|
631
|
+
this.checkpointOrder.push(checkpoint.id);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
function createCheckpointManager() {
|
|
636
|
+
return new CheckpointManager();
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// src/recording/Timeline.ts
|
|
640
|
+
var Timeline = class {
|
|
641
|
+
events = /* @__PURE__ */ new Map();
|
|
642
|
+
eventOrder = [];
|
|
643
|
+
markers = /* @__PURE__ */ new Map();
|
|
644
|
+
segments = /* @__PURE__ */ new Map();
|
|
645
|
+
/**
|
|
646
|
+
* Add an event to the timeline
|
|
647
|
+
*/
|
|
648
|
+
addEvent(options) {
|
|
649
|
+
const event = {
|
|
650
|
+
id: options.id ?? generateId("evt"),
|
|
651
|
+
type: options.type,
|
|
652
|
+
timestamp: options.timestamp ?? now(),
|
|
653
|
+
stepIndex: options.stepIndex,
|
|
654
|
+
summary: options.description ?? "",
|
|
655
|
+
description: options.description,
|
|
656
|
+
durationMs: options.durationMs,
|
|
657
|
+
metadata: options.metadata
|
|
658
|
+
};
|
|
659
|
+
this.events.set(event.id, event);
|
|
660
|
+
this.eventOrder.push(event.id);
|
|
661
|
+
return event;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Get an event by ID
|
|
665
|
+
*/
|
|
666
|
+
getEvent(id) {
|
|
667
|
+
return this.events.get(id);
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Get all events
|
|
671
|
+
*/
|
|
672
|
+
getEvents() {
|
|
673
|
+
return this.eventOrder.map((id) => this.events.get(id));
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Get events matching filter
|
|
677
|
+
*/
|
|
678
|
+
filterEvents(options) {
|
|
679
|
+
return this.getEvents().filter((event) => {
|
|
680
|
+
if (options.types && !options.types.includes(event.type)) {
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
if (options.stepRange) {
|
|
684
|
+
if (options.stepRange.min !== void 0 && event.stepIndex < options.stepRange.min) {
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
if (options.stepRange.max !== void 0 && event.stepIndex > options.stepRange.max) {
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
if (options.timeRange) {
|
|
692
|
+
if (options.timeRange.after !== void 0 && event.timestamp < options.timeRange.after) {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
if (options.timeRange.before !== void 0 && event.timestamp > options.timeRange.before) {
|
|
696
|
+
return false;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (options.search) {
|
|
700
|
+
const searchLower = options.search.toLowerCase();
|
|
701
|
+
const description = event.description ?? event.summary ?? "";
|
|
702
|
+
if (!description.toLowerCase().includes(searchLower)) {
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return true;
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Get events in step range
|
|
711
|
+
*/
|
|
712
|
+
getEventsInRange(startStep, endStep) {
|
|
713
|
+
return this.filterEvents({
|
|
714
|
+
stepRange: { min: startStep, max: endStep }
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Get events by type
|
|
719
|
+
*/
|
|
720
|
+
getEventsByType(type) {
|
|
721
|
+
return this.filterEvents({ types: [type] });
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Get event at step
|
|
725
|
+
*/
|
|
726
|
+
getEventAtStep(stepIndex) {
|
|
727
|
+
return this.getEvents().find((e) => e.stepIndex === stepIndex);
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Add a marker
|
|
731
|
+
*/
|
|
732
|
+
addMarker(options) {
|
|
733
|
+
const marker = {
|
|
734
|
+
id: generateId("mark"),
|
|
735
|
+
name: options.name,
|
|
736
|
+
timestamp: options.timestamp ?? now(),
|
|
737
|
+
stepIndex: options.stepIndex,
|
|
738
|
+
color: options.color,
|
|
739
|
+
description: options.description
|
|
740
|
+
};
|
|
741
|
+
this.markers.set(marker.id, marker);
|
|
742
|
+
return marker;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Get all markers
|
|
746
|
+
*/
|
|
747
|
+
getMarkers() {
|
|
748
|
+
return Array.from(this.markers.values());
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Get marker at step
|
|
752
|
+
*/
|
|
753
|
+
getMarkerAtStep(stepIndex) {
|
|
754
|
+
for (const marker of this.markers.values()) {
|
|
755
|
+
if (marker.stepIndex === stepIndex) {
|
|
756
|
+
return marker;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return void 0;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Remove a marker
|
|
763
|
+
*/
|
|
764
|
+
removeMarker(id) {
|
|
765
|
+
return this.markers.delete(id);
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Add a segment
|
|
769
|
+
*/
|
|
770
|
+
addSegment(options) {
|
|
771
|
+
const segment = {
|
|
772
|
+
id: generateId("seg"),
|
|
773
|
+
...options
|
|
774
|
+
};
|
|
775
|
+
this.segments.set(segment.id, segment);
|
|
776
|
+
return segment;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Get all segments
|
|
780
|
+
*/
|
|
781
|
+
getSegments() {
|
|
782
|
+
return Array.from(this.segments.values());
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Get segment containing step
|
|
786
|
+
*/
|
|
787
|
+
getSegmentForStep(stepIndex) {
|
|
788
|
+
for (const segment of this.segments.values()) {
|
|
789
|
+
if (stepIndex >= segment.startStep && stepIndex <= segment.endStep) {
|
|
790
|
+
return segment;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return void 0;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Remove a segment
|
|
797
|
+
*/
|
|
798
|
+
removeSegment(id) {
|
|
799
|
+
return this.segments.delete(id);
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Get timeline statistics
|
|
803
|
+
*/
|
|
804
|
+
getStats() {
|
|
805
|
+
const events = this.getEvents();
|
|
806
|
+
const eventsByType = {};
|
|
807
|
+
let totalDuration = 0;
|
|
808
|
+
for (const event of events) {
|
|
809
|
+
eventsByType[event.type] = (eventsByType[event.type] ?? 0) + 1;
|
|
810
|
+
if (event.durationMs) {
|
|
811
|
+
totalDuration += event.durationMs;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return {
|
|
815
|
+
totalEvents: events.length,
|
|
816
|
+
eventsByType,
|
|
817
|
+
totalDurationMs: totalDuration,
|
|
818
|
+
avgEventDurationMs: events.length > 0 ? totalDuration / events.length : 0,
|
|
819
|
+
firstEventTime: events.length > 0 ? events[0].timestamp : void 0,
|
|
820
|
+
lastEventTime: events.length > 0 ? events[events.length - 1].timestamp : void 0
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Get duration between two steps
|
|
825
|
+
*/
|
|
826
|
+
getDurationBetween(startStep, endStep) {
|
|
827
|
+
const startEvent = this.getEventAtStep(startStep);
|
|
828
|
+
const endEvent = this.getEventAtStep(endStep);
|
|
829
|
+
if (!startEvent || !endEvent) {
|
|
830
|
+
return 0;
|
|
831
|
+
}
|
|
832
|
+
return endEvent.timestamp - startEvent.timestamp;
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Format timeline as text
|
|
836
|
+
*/
|
|
837
|
+
format() {
|
|
838
|
+
const lines = [];
|
|
839
|
+
const events = this.getEvents();
|
|
840
|
+
for (const event of events) {
|
|
841
|
+
const duration = event.durationMs ? ` (${formatDuration(event.durationMs)})` : "";
|
|
842
|
+
lines.push(
|
|
843
|
+
`[${event.stepIndex}] ${event.type}: ${event.description}${duration}`
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
return lines.join("\n");
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Clear the timeline
|
|
850
|
+
*/
|
|
851
|
+
clear() {
|
|
852
|
+
this.events.clear();
|
|
853
|
+
this.eventOrder = [];
|
|
854
|
+
this.markers.clear();
|
|
855
|
+
this.segments.clear();
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Get event count
|
|
859
|
+
*/
|
|
860
|
+
get count() {
|
|
861
|
+
return this.events.size;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Export timeline data
|
|
865
|
+
*/
|
|
866
|
+
export() {
|
|
867
|
+
return {
|
|
868
|
+
events: this.getEvents(),
|
|
869
|
+
markers: this.getMarkers(),
|
|
870
|
+
segments: this.getSegments()
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Import timeline data
|
|
875
|
+
*/
|
|
876
|
+
import(data) {
|
|
877
|
+
this.clear();
|
|
878
|
+
if (data.events) {
|
|
879
|
+
for (const event of data.events) {
|
|
880
|
+
this.events.set(event.id, event);
|
|
881
|
+
this.eventOrder.push(event.id);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (data.markers) {
|
|
885
|
+
for (const marker of data.markers) {
|
|
886
|
+
this.markers.set(marker.id, marker);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if (data.segments) {
|
|
890
|
+
for (const segment of data.segments) {
|
|
891
|
+
this.segments.set(segment.id, segment);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
function createTimeline() {
|
|
897
|
+
return new Timeline();
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// src/recording/Recorder.ts
|
|
901
|
+
var DEFAULT_CONFIG = {
|
|
902
|
+
includePrompts: true,
|
|
903
|
+
includeResponses: true,
|
|
904
|
+
includeToolCalls: true,
|
|
905
|
+
includeMemory: true,
|
|
906
|
+
includeMetadata: true,
|
|
907
|
+
compression: false,
|
|
908
|
+
maxSizeBytes: 100 * 1024 * 1024,
|
|
909
|
+
// 100MB
|
|
910
|
+
autoSnapshot: false,
|
|
911
|
+
snapshotInterval: 0,
|
|
912
|
+
maxRecordings: 1e3,
|
|
913
|
+
retentionDays: 30,
|
|
914
|
+
checkpointInterval: 0,
|
|
915
|
+
includeEmbeddings: false,
|
|
916
|
+
storage: void 0
|
|
917
|
+
};
|
|
918
|
+
var Recorder = class extends EventEmitter {
|
|
919
|
+
config;
|
|
920
|
+
state = "idle";
|
|
921
|
+
recordingId;
|
|
922
|
+
agentId;
|
|
923
|
+
agentName;
|
|
924
|
+
steps = [];
|
|
925
|
+
startedAt = 0;
|
|
926
|
+
initialState;
|
|
927
|
+
currentState;
|
|
928
|
+
snapshots;
|
|
929
|
+
checkpoints;
|
|
930
|
+
timeline;
|
|
931
|
+
storage;
|
|
932
|
+
estimatedSize = 0;
|
|
933
|
+
constructor(config, storage) {
|
|
934
|
+
super();
|
|
935
|
+
this.config = {
|
|
936
|
+
...DEFAULT_CONFIG,
|
|
937
|
+
...config
|
|
938
|
+
};
|
|
939
|
+
this.storage = storage;
|
|
940
|
+
this.snapshots = new SnapshotManager();
|
|
941
|
+
this.checkpoints = new CheckpointManager();
|
|
942
|
+
this.timeline = new Timeline();
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Start recording
|
|
946
|
+
*/
|
|
947
|
+
start(agentId, initialState, agentName) {
|
|
948
|
+
if (this.state !== "idle") {
|
|
949
|
+
throw new Error(`Cannot start recording in state: ${this.state}`);
|
|
950
|
+
}
|
|
951
|
+
this.recordingId = generateId("rec");
|
|
952
|
+
this.agentId = agentId;
|
|
953
|
+
this.agentName = agentName ?? initialState.agentName;
|
|
954
|
+
this.initialState = deepClone(initialState);
|
|
955
|
+
this.currentState = deepClone(initialState);
|
|
956
|
+
this.steps = [];
|
|
957
|
+
this.startedAt = now();
|
|
958
|
+
this.estimatedSize = 0;
|
|
959
|
+
this.state = "recording";
|
|
960
|
+
this.snapshots = new SnapshotManager();
|
|
961
|
+
this.checkpoints = new CheckpointManager();
|
|
962
|
+
this.timeline = new Timeline();
|
|
963
|
+
this.snapshots.create(initialState, 0);
|
|
964
|
+
this.emit("recording:started", this.recordingId);
|
|
965
|
+
return this.recordingId;
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Stop recording
|
|
969
|
+
*/
|
|
970
|
+
stop() {
|
|
971
|
+
if (this.state !== "recording" && this.state !== "paused") {
|
|
972
|
+
throw new Error(`Cannot stop recording in state: ${this.state}`);
|
|
973
|
+
}
|
|
974
|
+
const recording = this.buildRecording();
|
|
975
|
+
this.state = "stopped";
|
|
976
|
+
this.emit("recording:stopped", recording);
|
|
977
|
+
return recording;
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Pause recording
|
|
981
|
+
*/
|
|
982
|
+
pause() {
|
|
983
|
+
if (this.state !== "recording") {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
this.state = "paused";
|
|
987
|
+
this.emit("recording:paused");
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Resume recording
|
|
991
|
+
*/
|
|
992
|
+
resume() {
|
|
993
|
+
if (this.state !== "paused") {
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
this.state = "recording";
|
|
997
|
+
this.emit("recording:resumed");
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Record a step
|
|
1001
|
+
*/
|
|
1002
|
+
recordStep(step, state) {
|
|
1003
|
+
if (this.state !== "recording") {
|
|
1004
|
+
return false;
|
|
1005
|
+
}
|
|
1006
|
+
const stepSize = estimateSize(step);
|
|
1007
|
+
if (this.estimatedSize + stepSize > this.config.maxSizeBytes) {
|
|
1008
|
+
this.emit("error", new Error("Recording size limit exceeded"));
|
|
1009
|
+
return false;
|
|
1010
|
+
}
|
|
1011
|
+
const filteredStep = this.filterStep(step);
|
|
1012
|
+
if (!filteredStep) {
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
this.steps.push(filteredStep);
|
|
1016
|
+
this.currentState = deepClone(state);
|
|
1017
|
+
this.estimatedSize += stepSize;
|
|
1018
|
+
this.timeline.addEvent({
|
|
1019
|
+
id: generateId("evt"),
|
|
1020
|
+
type: filteredStep.type,
|
|
1021
|
+
timestamp: filteredStep.timestamp,
|
|
1022
|
+
stepIndex: filteredStep.index,
|
|
1023
|
+
description: this.getStepDescription(filteredStep)
|
|
1024
|
+
});
|
|
1025
|
+
if (this.config.autoSnapshot && this.config.snapshotInterval > 0 && this.steps.length % this.config.snapshotInterval === 0) {
|
|
1026
|
+
const snapshot = this.snapshots.create(state, filteredStep.index);
|
|
1027
|
+
this.emit("snapshot:created", snapshot);
|
|
1028
|
+
}
|
|
1029
|
+
this.emit("step:recorded", filteredStep);
|
|
1030
|
+
return true;
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Create a checkpoint
|
|
1034
|
+
*/
|
|
1035
|
+
createCheckpoint(name, description) {
|
|
1036
|
+
if (this.state !== "recording" && this.state !== "paused") {
|
|
1037
|
+
return void 0;
|
|
1038
|
+
}
|
|
1039
|
+
if (!this.recordingId || !this.currentState) {
|
|
1040
|
+
return void 0;
|
|
1041
|
+
}
|
|
1042
|
+
const stepIndex = this.steps.length > 0 ? this.steps[this.steps.length - 1].index : -1;
|
|
1043
|
+
const checkpoint = this.checkpoints.create({
|
|
1044
|
+
recordingId: this.recordingId,
|
|
1045
|
+
name,
|
|
1046
|
+
description,
|
|
1047
|
+
stepIndex,
|
|
1048
|
+
state: this.currentState
|
|
1049
|
+
});
|
|
1050
|
+
this.snapshots.create(this.currentState, stepIndex);
|
|
1051
|
+
this.emit("checkpoint:created", checkpoint);
|
|
1052
|
+
return checkpoint;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Get current recording state
|
|
1056
|
+
*/
|
|
1057
|
+
getState() {
|
|
1058
|
+
return this.state;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Get recording ID
|
|
1062
|
+
*/
|
|
1063
|
+
getRecordingId() {
|
|
1064
|
+
return this.recordingId;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Get steps count
|
|
1068
|
+
*/
|
|
1069
|
+
getStepsCount() {
|
|
1070
|
+
return this.steps.length;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Get estimated size
|
|
1074
|
+
*/
|
|
1075
|
+
getEstimatedSize() {
|
|
1076
|
+
return this.estimatedSize;
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Get timeline
|
|
1080
|
+
*/
|
|
1081
|
+
getTimeline() {
|
|
1082
|
+
return this.timeline;
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Get snapshots
|
|
1086
|
+
*/
|
|
1087
|
+
getSnapshots() {
|
|
1088
|
+
return this.snapshots.getAll();
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Get checkpoints
|
|
1092
|
+
*/
|
|
1093
|
+
getCheckpoints() {
|
|
1094
|
+
return this.checkpoints.getAll();
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Save recording to storage
|
|
1098
|
+
*/
|
|
1099
|
+
async save() {
|
|
1100
|
+
if (!this.storage) {
|
|
1101
|
+
throw new Error("No storage adapter configured");
|
|
1102
|
+
}
|
|
1103
|
+
const recording = this.buildRecording();
|
|
1104
|
+
await this.storage.save(recording);
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Filter step based on configuration
|
|
1108
|
+
*/
|
|
1109
|
+
filterStep(step) {
|
|
1110
|
+
const filtered = { ...step };
|
|
1111
|
+
if (!this.config.includePrompts && step.type === "prompt") {
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1114
|
+
if (!this.config.includeResponses && step.type === "response") {
|
|
1115
|
+
return null;
|
|
1116
|
+
}
|
|
1117
|
+
if (!this.config.includeToolCalls && (step.type === "tool-call" || step.type === "tool-result")) {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
if (!this.config.includeMetadata) {
|
|
1121
|
+
delete filtered.metadata;
|
|
1122
|
+
}
|
|
1123
|
+
return filtered;
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Get step description for timeline
|
|
1127
|
+
*/
|
|
1128
|
+
getStepDescription(step) {
|
|
1129
|
+
switch (step.type) {
|
|
1130
|
+
case "input":
|
|
1131
|
+
return "User input received";
|
|
1132
|
+
case "prompt":
|
|
1133
|
+
return "Prompt sent to model";
|
|
1134
|
+
case "response":
|
|
1135
|
+
return "Response received from model";
|
|
1136
|
+
case "tool-call":
|
|
1137
|
+
return `Tool called: ${step.toolCall?.name ?? "unknown"}`;
|
|
1138
|
+
case "tool-result":
|
|
1139
|
+
return `Tool result: ${step.toolCall?.success ? "success" : "failed"}`;
|
|
1140
|
+
case "decision":
|
|
1141
|
+
return `Decision made: ${step.decision?.chosen.description ?? "unknown"}`;
|
|
1142
|
+
case "error":
|
|
1143
|
+
return `Error: ${step.error?.message ?? "unknown"}`;
|
|
1144
|
+
default:
|
|
1145
|
+
return `Step: ${step.type}`;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Build the recording object
|
|
1150
|
+
*/
|
|
1151
|
+
buildRecording() {
|
|
1152
|
+
const endedAt = now();
|
|
1153
|
+
const toolCalls = this.steps.filter((s) => s.toolCall).map((s) => s.toolCall);
|
|
1154
|
+
const decisions = this.steps.filter((s) => s.decision).map((s) => s.decision);
|
|
1155
|
+
const tokenUsage = this.steps.reduce(
|
|
1156
|
+
(acc, step) => {
|
|
1157
|
+
if (step.tokenUsage) {
|
|
1158
|
+
acc.prompt += step.tokenUsage.prompt;
|
|
1159
|
+
acc.completion += step.tokenUsage.completion;
|
|
1160
|
+
acc.total += step.tokenUsage.total;
|
|
1161
|
+
}
|
|
1162
|
+
return acc;
|
|
1163
|
+
},
|
|
1164
|
+
{ prompt: 0, completion: 0, total: 0 }
|
|
1165
|
+
);
|
|
1166
|
+
const hasErrors = this.steps.some((s) => s.error);
|
|
1167
|
+
return {
|
|
1168
|
+
id: this.recordingId,
|
|
1169
|
+
agentId: this.agentId,
|
|
1170
|
+
agentName: this.agentName ?? "Unknown",
|
|
1171
|
+
status: hasErrors ? "failed" : "completed",
|
|
1172
|
+
startedAt: this.startedAt,
|
|
1173
|
+
endedAt,
|
|
1174
|
+
durationMs: endedAt - this.startedAt,
|
|
1175
|
+
steps: this.steps,
|
|
1176
|
+
toolCalls,
|
|
1177
|
+
decisions,
|
|
1178
|
+
checkpoints: this.checkpoints.getAll(),
|
|
1179
|
+
initialState: this.initialState,
|
|
1180
|
+
finalState: this.currentState,
|
|
1181
|
+
tokenUsage,
|
|
1182
|
+
version: "1.0.0",
|
|
1183
|
+
metadata: this.config.includeMetadata ? this.buildMetadata() : void 0
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Build recording metadata
|
|
1188
|
+
*/
|
|
1189
|
+
buildMetadata() {
|
|
1190
|
+
return {
|
|
1191
|
+
totalSteps: this.steps.length,
|
|
1192
|
+
totalToolCalls: this.steps.filter((s) => s.type === "tool-call").length,
|
|
1193
|
+
totalDecisions: this.steps.filter((s) => s.type === "decision").length,
|
|
1194
|
+
hasErrors: this.steps.some((s) => s.error),
|
|
1195
|
+
compressionUsed: this.config.compression,
|
|
1196
|
+
recordingVersion: "1.0.0",
|
|
1197
|
+
tags: []
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
function createRecorder(config, storage) {
|
|
1202
|
+
return new Recorder(config, storage);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
export { CheckpointManager, Recorder, SnapshotManager, Timeline, createCheckpointManager, createRecorder, createSnapshotManager, createTimeline };
|
|
1206
|
+
//# sourceMappingURL=index.js.map
|
|
1207
|
+
//# sourceMappingURL=index.js.map
|