@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.
@@ -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