@kb-labs/agent-tracing 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/dist/index.js ADDED
@@ -0,0 +1,873 @@
1
+ import { appendFileSync, readFileSync, promises, mkdirSync, statSync } from 'fs';
2
+ import * as path3 from 'path';
3
+ import path3__default from 'path';
4
+ import * as fs2 from 'fs/promises';
5
+
6
+ // src/incremental-trace-writer.ts
7
+
8
+ // src/privacy-redactor.ts
9
+ var DEFAULT_SECRET_PATTERNS = [
10
+ // API keys
11
+ /sk-[A-Za-z0-9]{20,}/g,
12
+ // OpenAI API keys
13
+ /sk_live_[A-Za-z0-9]{24,}/g,
14
+ // Stripe API keys
15
+ /ghp_[A-Za-z0-9]{36}/g,
16
+ // GitHub personal access tokens
17
+ // Authentication tokens
18
+ /Bearer\s+[A-Za-z0-9\-._~+/]+=*/g,
19
+ /token[=:]\s*[A-Za-z0-9\-._~+/]+=*/gi,
20
+ // Passwords
21
+ /password[=:]\s*["']?[^"'\s]+["']?/gi,
22
+ /passwd[=:]\s*["']?[^"'\s]+["']?/gi,
23
+ /pwd[=:]\s*["']?[^"'\s]+["']?/gi,
24
+ // Connection strings
25
+ /mongodb(\+srv)?:\/\/[^@]+@[^\s]+/g,
26
+ /postgres(ql)?:\/\/[^@]+@[^\s]+/g,
27
+ /mysql:\/\/[^@]+@[^\s]+/g,
28
+ // Email addresses (PII)
29
+ /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g
30
+ // Note: Generic long alphanumeric pattern removed to avoid false positives
31
+ // (would match file hashes, UUIDs, legitimate data)
32
+ // Credit card numbers removed - too many false positives
33
+ ];
34
+ function redactSecretsFromString(str, patterns = DEFAULT_SECRET_PATTERNS) {
35
+ let result = str;
36
+ for (const pattern of patterns) {
37
+ result = result.replace(pattern, "[REDACTED]");
38
+ }
39
+ return result;
40
+ }
41
+ function redactPaths(str, replacements) {
42
+ let result = str;
43
+ for (const [absolute, relative] of Object.entries(replacements)) {
44
+ result = result.replace(new RegExp(absolute.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), relative);
45
+ }
46
+ return result;
47
+ }
48
+ function redactValue(value, config, depth = 0) {
49
+ if (depth > 10) {
50
+ return "[REDACTED:TOO_DEEP]";
51
+ }
52
+ if (value === null || value === void 0) {
53
+ return value;
54
+ }
55
+ if (typeof value === "string") {
56
+ let result = value;
57
+ if (config.redactSecrets) {
58
+ const patterns = config.secretPatterns.map((p) => new RegExp(p, "g"));
59
+ result = redactSecretsFromString(result, [...DEFAULT_SECRET_PATTERNS, ...patterns]);
60
+ }
61
+ if (config.redactPaths) {
62
+ result = redactPaths(result, config.pathReplacements);
63
+ }
64
+ return result === value ? value : result;
65
+ }
66
+ if (typeof value === "number" || typeof value === "boolean") {
67
+ return value;
68
+ }
69
+ if (Array.isArray(value)) {
70
+ let changed = false;
71
+ const result = value.map((item) => {
72
+ const redacted = redactValue(item, config, depth + 1);
73
+ if (redacted !== item) {
74
+ changed = true;
75
+ }
76
+ return redacted;
77
+ });
78
+ return changed ? result : value;
79
+ }
80
+ if (typeof value === "object") {
81
+ let changed = false;
82
+ const result = {};
83
+ for (const [key, val] of Object.entries(value)) {
84
+ const redacted = redactValue(val, config, depth + 1);
85
+ result[key] = redacted;
86
+ if (redacted !== val) {
87
+ changed = true;
88
+ }
89
+ }
90
+ return changed ? result : value;
91
+ }
92
+ return value;
93
+ }
94
+ function redactTraceEvent(event, config) {
95
+ if (!config.redactSecrets && !config.redactPaths) {
96
+ return event;
97
+ }
98
+ const eventStr = JSON.stringify(event);
99
+ const MAX_STRING_SIZE = 1e5;
100
+ const testStr = eventStr.length > MAX_STRING_SIZE ? eventStr.substring(0, MAX_STRING_SIZE) : eventStr;
101
+ let needsRedaction = false;
102
+ if (config.redactSecrets) {
103
+ const patterns = config.secretPatterns.map((p) => new RegExp(p, "g"));
104
+ needsRedaction = [...DEFAULT_SECRET_PATTERNS, ...patterns].some((p) => p.test(testStr));
105
+ }
106
+ if (!needsRedaction && config.redactPaths) {
107
+ needsRedaction = Object.keys(config.pathReplacements).some((path4) => testStr.includes(path4));
108
+ }
109
+ if (!needsRedaction) {
110
+ return event;
111
+ }
112
+ return redactValue(event, config, 0);
113
+ }
114
+ function createDefaultPrivacyConfig() {
115
+ return {
116
+ redactSecrets: true,
117
+ redactPaths: true,
118
+ secretPatterns: [],
119
+ pathReplacements: {
120
+ "/Users": "~",
121
+ "/home": "~",
122
+ "C:\\Users": "~"
123
+ }
124
+ };
125
+ }
126
+
127
+ // src/incremental-trace-writer.ts
128
+ var DEFAULT_TRACE_CONFIG = {
129
+ version: "1.0.0",
130
+ enabled: true,
131
+ level: "detailed",
132
+ incremental: {
133
+ enabled: true,
134
+ flushIntervalMs: 100,
135
+ maxBufferSize: 10,
136
+ format: "ndjson"
137
+ },
138
+ capture: {
139
+ prompts: true,
140
+ toolOutputs: true,
141
+ memorySnapshots: true,
142
+ decisions: true
143
+ },
144
+ retention: {
145
+ maxTraces: 30,
146
+ maxDays: 30,
147
+ cleanupOnFinalize: true,
148
+ archiveOlderThan: 7,
149
+ compressArchived: true
150
+ },
151
+ privacy: {
152
+ redactSecrets: true,
153
+ redactPaths: true,
154
+ secretPatterns: [
155
+ "sk-[a-zA-Z0-9]{20,}",
156
+ // OpenAI API keys
157
+ "Bearer\\s+[a-zA-Z0-9_-]+",
158
+ // Bearer tokens
159
+ `password['"]?\\s*[:=]\\s*['"][^'"]+(['"]})`,
160
+ // Passwords
161
+ `api[_-]?key['"]?\\s*[:=]\\s*['"][^'"]+['"]`
162
+ // Generic API keys
163
+ ],
164
+ pathReplacements: {
165
+ "/Users/": "~/",
166
+ "/home/": "~/",
167
+ "\\Users\\": "~\\"
168
+ }
169
+ },
170
+ storage: {
171
+ path: ".kb/traces/incremental",
172
+ indexPath: ".kb/traces/incremental"
173
+ }
174
+ };
175
+ var IncrementalTraceWriter = class {
176
+ seq = 0;
177
+ filepath;
178
+ indexPath;
179
+ config;
180
+ taskId;
181
+ startTime;
182
+ constructor(taskId, config = {}, outputDir) {
183
+ this.taskId = taskId;
184
+ this.config = { ...DEFAULT_TRACE_CONFIG, ...config };
185
+ this.startTime = (/* @__PURE__ */ new Date()).toISOString();
186
+ const dir = outputDir || this.config.storage.path;
187
+ this.filepath = path3__default.join(dir, `${taskId}.ndjson`);
188
+ this.indexPath = path3__default.join(dir, `${taskId}-index.json`);
189
+ this.ensureDirectoryExists(dir);
190
+ }
191
+ /**
192
+ * Record a trace entry — writes synchronously to disk.
193
+ *
194
+ * Every call = one appendFileSync. No buffering, no async, no lost events.
195
+ * If the agent crashes on iteration 5, you have all events from iterations 1-5.
196
+ */
197
+ trace(entry) {
198
+ try {
199
+ const seq = ++this.seq;
200
+ const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
201
+ const fullEntry = {
202
+ ...entry,
203
+ seq,
204
+ timestamp
205
+ };
206
+ const redacted = this.redact(fullEntry);
207
+ appendFileSync(this.filepath, JSON.stringify(redacted) + "\n", "utf-8");
208
+ } catch (error) {
209
+ console.error("[IncrementalTraceWriter] trace() error:", error);
210
+ }
211
+ }
212
+ /**
213
+ * Get all trace entries (reads from NDJSON file to avoid memory leak)
214
+ * Returns TraceEntry[] for backward compatibility, but actual format is DetailedTraceEntry[]
215
+ */
216
+ getEntries() {
217
+ try {
218
+ const content = readFileSync(this.filepath, "utf-8");
219
+ const lines = content.split("\n").filter(Boolean);
220
+ return lines.map((line) => JSON.parse(line));
221
+ } catch {
222
+ return [];
223
+ }
224
+ }
225
+ /**
226
+ * Save trace to file (backward compat — no-op, already written synchronously)
227
+ */
228
+ async save(_filePath) {
229
+ }
230
+ /**
231
+ * Clear all entries
232
+ */
233
+ clear() {
234
+ this.seq = 0;
235
+ }
236
+ /**
237
+ * Finalize trace (generate index, cleanup old traces)
238
+ */
239
+ async finalize() {
240
+ try {
241
+ await this.createIndex();
242
+ if (this.config.retention.cleanupOnFinalize) {
243
+ await this.cleanupOldTraces();
244
+ }
245
+ } catch (error) {
246
+ console.error("[IncrementalTraceWriter] finalize() error:", error);
247
+ }
248
+ }
249
+ /**
250
+ * Create index file for fast CLI queries
251
+ */
252
+ async createIndex() {
253
+ try {
254
+ const entries = this.getEntries();
255
+ if (entries.length === 0) {
256
+ console.warn("[IncrementalTraceWriter] No entries to index");
257
+ return;
258
+ }
259
+ const stats = this.calculateIndexStatistics(entries);
260
+ const index = {
261
+ version: "1.0.0",
262
+ taskId: this.taskId,
263
+ createdAt: this.startTime,
264
+ finalizedAt: (/* @__PURE__ */ new Date()).toISOString(),
265
+ summary: {
266
+ totalEvents: entries.length,
267
+ iterations: stats.iterations.size,
268
+ status: stats.errors > 0 ? "failed" : "success",
269
+ eventCounts: stats.eventCounts
270
+ },
271
+ timing: {
272
+ startedAt: this.startTime,
273
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
274
+ totalDurationMs: Date.now() - new Date(this.startTime).getTime()
275
+ },
276
+ cost: {
277
+ totalCost: stats.totalCost,
278
+ currency: "USD"
279
+ },
280
+ errors: stats.errors,
281
+ memory: stats.memory,
282
+ iterations: Array.from(stats.iterations.entries()).map(([iteration, iterStats]) => ({ iteration, ...iterStats })).sort((a, b) => a.iteration - b.iteration)
283
+ };
284
+ const indexDir = path3__default.dirname(this.indexPath);
285
+ this.ensureDirectoryExists(indexDir);
286
+ await promises.writeFile(this.indexPath, JSON.stringify(index, null, 2), "utf-8");
287
+ } catch (error) {
288
+ console.error("[IncrementalTraceWriter] createIndex() error:", error);
289
+ }
290
+ }
291
+ // ═══════════════════════════════════════════════════════════════════════
292
+ // Private Methods
293
+ // ═══════════════════════════════════════════════════════════════════════
294
+ /**
295
+ * Cleanup old traces (keep last N traces)
296
+ */
297
+ async cleanupOldTraces() {
298
+ try {
299
+ const dir = path3__default.dirname(this.filepath);
300
+ const files = await promises.readdir(dir);
301
+ const traceFiles = files.filter((f) => f.endsWith(".ndjson"));
302
+ if (traceFiles.length <= this.config.retention.maxTraces) {
303
+ return;
304
+ }
305
+ const filesWithStats = await Promise.all(
306
+ traceFiles.map(async (file) => {
307
+ const filepath = path3__default.join(dir, file);
308
+ const stats = await promises.stat(filepath);
309
+ return { file, mtime: stats.mtime.getTime(), filepath };
310
+ })
311
+ );
312
+ filesWithStats.sort((a, b) => b.mtime - a.mtime);
313
+ const toDelete = filesWithStats.slice(this.config.retention.maxTraces);
314
+ await Promise.allSettled(
315
+ toDelete.map(async ({ file, filepath }) => {
316
+ try {
317
+ await promises.unlink(filepath);
318
+ const indexFile = filepath.replace(".ndjson", "-index.json");
319
+ try {
320
+ await promises.unlink(indexFile);
321
+ } catch {
322
+ }
323
+ } catch (error) {
324
+ console.error(`[IncrementalTraceWriter] Failed to delete ${file}:`, error);
325
+ }
326
+ })
327
+ );
328
+ if (toDelete.length > 0) {
329
+ console.log(
330
+ `[IncrementalTraceWriter] Cleaned up ${toDelete.length} old traces (kept last ${this.config.retention.maxTraces})`
331
+ );
332
+ }
333
+ } catch (error) {
334
+ console.error("[IncrementalTraceWriter] cleanupOldTraces() error:", error);
335
+ }
336
+ }
337
+ /**
338
+ * Redact sensitive data from entry using optimized privacy-redactor
339
+ *
340
+ * Uses shallow clone optimization - only clones objects that need redaction,
341
+ * not the entire trace event tree. Returns original if no secrets found.
342
+ */
343
+ redact(entry) {
344
+ if (!this.config.privacy.redactSecrets && !this.config.privacy.redactPaths) {
345
+ return entry;
346
+ }
347
+ try {
348
+ return redactTraceEvent(entry, this.config.privacy);
349
+ } catch (error) {
350
+ console.warn("[IncrementalTraceWriter] redact() error:", error);
351
+ return entry;
352
+ }
353
+ }
354
+ /**
355
+ * Calculate index statistics from trace entries
356
+ */
357
+ calculateIndexStatistics(entries) {
358
+ const eventCounts = {};
359
+ const iterations = /* @__PURE__ */ new Map();
360
+ let totalCost = 0;
361
+ let errors = 0;
362
+ let totalFactsAdded = 0;
363
+ let totalArchiveStores = 0;
364
+ let summarizationRuns = 0;
365
+ let compressionRatioSum = 0;
366
+ let newFactRateSum = 0;
367
+ let lastFactSheetSize = 0;
368
+ let lastFactSheetTokens = 0;
369
+ let lastArchiveEntries = 0;
370
+ let lastArchiveUniqueFiles = 0;
371
+ for (const entry of entries) {
372
+ eventCounts[entry.type] = (eventCounts[entry.type] || 0) + 1;
373
+ if (entry.iteration !== void 0) {
374
+ if (!iterations.has(entry.iteration)) {
375
+ iterations.set(entry.iteration, { eventCount: 0, llmCalls: 0, toolCalls: 0 });
376
+ }
377
+ const iter = iterations.get(entry.iteration);
378
+ iter.eventCount++;
379
+ if (entry.type === "llm:end") {
380
+ iter.llmCalls++;
381
+ if ("cost" in entry && entry.cost) {
382
+ totalCost += entry.cost.totalCost || 0;
383
+ }
384
+ }
385
+ if (entry.type === "tool:end") {
386
+ iter.toolCalls++;
387
+ }
388
+ }
389
+ if (entry.type === "error:captured") {
390
+ errors++;
391
+ }
392
+ if (entry.type === "memory:fact_added") {
393
+ totalFactsAdded++;
394
+ if (entry.factSheetStats) {
395
+ lastFactSheetSize = entry.factSheetStats.totalFacts || 0;
396
+ lastFactSheetTokens = entry.factSheetStats.estimatedTokens || 0;
397
+ }
398
+ }
399
+ if (entry.type === "memory:archive_store") {
400
+ totalArchiveStores++;
401
+ if (entry.archiveStats) {
402
+ lastArchiveEntries = entry.archiveStats.totalEntries || 0;
403
+ lastArchiveUniqueFiles = entry.archiveStats.uniqueFiles || 0;
404
+ }
405
+ }
406
+ if (entry.type === "memory:summarization_result") {
407
+ summarizationRuns++;
408
+ if (entry.efficiency) {
409
+ compressionRatioSum += entry.efficiency.compressionRatio || 0;
410
+ newFactRateSum += entry.efficiency.newFactRate || 0;
411
+ }
412
+ }
413
+ }
414
+ const hasMemoryEvents = totalFactsAdded > 0 || totalArchiveStores > 0 || summarizationRuns > 0;
415
+ return {
416
+ eventCounts,
417
+ iterations,
418
+ totalCost,
419
+ errors,
420
+ memory: hasMemoryEvents ? {
421
+ totalFactsAdded,
422
+ totalArchiveStores,
423
+ summarizationRuns,
424
+ avgCompressionRatio: summarizationRuns > 0 ? compressionRatioSum / summarizationRuns : 0,
425
+ avgNewFactRate: summarizationRuns > 0 ? newFactRateSum / summarizationRuns : 0,
426
+ finalFactSheetSize: lastFactSheetSize,
427
+ finalFactSheetTokens: lastFactSheetTokens,
428
+ finalArchiveEntries: lastArchiveEntries,
429
+ finalArchiveUniqueFiles: lastArchiveUniqueFiles
430
+ } : void 0
431
+ };
432
+ }
433
+ /**
434
+ * Ensure directory exists
435
+ */
436
+ ensureDirectoryExists(dir) {
437
+ try {
438
+ mkdirSync(dir, { recursive: true });
439
+ } catch (error) {
440
+ try {
441
+ const stats = statSync(dir);
442
+ if (!stats.isDirectory()) {
443
+ throw new Error(`Path exists but is not a directory: ${dir}`);
444
+ }
445
+ } catch {
446
+ throw new Error(`Failed to create trace directory: ${dir}. Error: ${error}`);
447
+ }
448
+ }
449
+ }
450
+ };
451
+ var FileTracer = class {
452
+ entries = [];
453
+ taskId;
454
+ sessionId;
455
+ constructor(taskId, sessionId) {
456
+ this.taskId = taskId;
457
+ this.sessionId = sessionId;
458
+ }
459
+ /**
460
+ * Record a trace entry
461
+ */
462
+ trace(entry) {
463
+ this.entries.push(entry);
464
+ }
465
+ /**
466
+ * Get all trace entries
467
+ */
468
+ getEntries() {
469
+ return this.entries;
470
+ }
471
+ /**
472
+ * Save trace to file
473
+ */
474
+ async save(filePath) {
475
+ const dir = path3.dirname(filePath);
476
+ await fs2.mkdir(dir, { recursive: true });
477
+ const traceData = {
478
+ taskId: this.taskId,
479
+ sessionId: this.sessionId,
480
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
481
+ totalEntries: this.entries.length,
482
+ entries: this.entries
483
+ };
484
+ await fs2.writeFile(filePath, JSON.stringify(traceData, null, 2), "utf-8");
485
+ }
486
+ /**
487
+ * Clear all entries
488
+ */
489
+ clear() {
490
+ this.entries = [];
491
+ }
492
+ /**
493
+ * Get trace summary statistics
494
+ */
495
+ getSummary() {
496
+ const llmCalls = this.entries.filter((e) => e.type === "llm:call");
497
+ const toolCalls = this.entries.filter((e) => e.type === "tool:execution");
498
+ const getDuration = (e) => {
499
+ if (e.type === "llm:call" || e.type === "tool:execution") {
500
+ return e.timing?.durationMs ?? 0;
501
+ }
502
+ return 0;
503
+ };
504
+ const totalDuration = this.entries.reduce((sum, e) => sum + getDuration(e), 0);
505
+ const llmDuration = llmCalls.reduce((sum, e) => sum + getDuration(e), 0);
506
+ const toolDuration = toolCalls.reduce((sum, e) => sum + getDuration(e), 0);
507
+ return {
508
+ totalEntries: this.entries.length,
509
+ llmCalls: llmCalls.length,
510
+ toolCalls: toolCalls.length,
511
+ totalDuration,
512
+ avgLLMDuration: llmCalls.length > 0 ? llmDuration / llmCalls.length : 0,
513
+ avgToolDuration: toolCalls.length > 0 ? toolDuration / toolCalls.length : 0
514
+ };
515
+ }
516
+ };
517
+ var TRACE_DIR_RELATIVE = path3__default.join(".kb", "traces", "incremental");
518
+ var TASK_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
519
+ var MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024;
520
+ async function loadTrace(taskId, workingDir = process.cwd()) {
521
+ if (!taskId) {
522
+ return { ok: false, error: { kind: "invalid_task_id", message: "Missing required --task-id" } };
523
+ }
524
+ if (!TASK_ID_PATTERN.test(taskId)) {
525
+ return {
526
+ ok: false,
527
+ error: {
528
+ kind: "invalid_task_id",
529
+ message: "Task ID must contain only alphanumeric characters, hyphens, and underscores"
530
+ }
531
+ };
532
+ }
533
+ const traceDir = path3__default.join(workingDir, TRACE_DIR_RELATIVE);
534
+ const filePath = path3__default.join(traceDir, `${taskId}.ndjson`);
535
+ const resolvedFile = path3__default.resolve(filePath);
536
+ const resolvedDir = path3__default.resolve(traceDir);
537
+ if (!resolvedFile.startsWith(resolvedDir + path3__default.sep) && resolvedFile !== resolvedDir) {
538
+ return { ok: false, error: { kind: "invalid_task_id", message: "Path traversal detected" } };
539
+ }
540
+ let stat;
541
+ try {
542
+ stat = await promises.stat(filePath);
543
+ } catch {
544
+ return { ok: false, error: { kind: "not_found", taskId } };
545
+ }
546
+ if (stat.size > MAX_FILE_SIZE_BYTES) {
547
+ return { ok: false, error: { kind: "too_large", sizeBytes: stat.size } };
548
+ }
549
+ let content;
550
+ try {
551
+ content = await promises.readFile(filePath, "utf-8");
552
+ } catch (e) {
553
+ return { ok: false, error: { kind: "io_error", message: e.message } };
554
+ }
555
+ const events = [];
556
+ for (const line of content.split("\n")) {
557
+ if (!line.trim()) {
558
+ continue;
559
+ }
560
+ try {
561
+ events.push(JSON.parse(line));
562
+ } catch {
563
+ }
564
+ }
565
+ if (events.length === 0) {
566
+ return { ok: false, error: { kind: "empty", taskId } };
567
+ }
568
+ return { ok: true, events, taskId, filePath };
569
+ }
570
+ function formatTraceLoadError(error) {
571
+ switch (error.kind) {
572
+ case "invalid_task_id":
573
+ return `Invalid task ID: ${error.message}`;
574
+ case "not_found":
575
+ return `Trace not found: ${error.taskId}`;
576
+ case "too_large":
577
+ return `Trace file too large: ${Math.round(error.sizeBytes / 1024 / 1024)} MB (limit 100 MB)`;
578
+ case "empty":
579
+ return `Trace file is empty: ${error.taskId}`;
580
+ case "io_error":
581
+ return `IO error reading trace: ${error.message}`;
582
+ }
583
+ }
584
+
585
+ // src/trace-helpers.ts
586
+ function createIterationDetailEvent(params) {
587
+ return {
588
+ type: "iteration:detail",
589
+ iteration: params.iteration,
590
+ config: {
591
+ maxIterations: params.maxIterations,
592
+ mode: params.mode,
593
+ temperature: params.temperature
594
+ },
595
+ availableTools: {
596
+ total: params.availableTools.length,
597
+ tools: params.availableTools
598
+ },
599
+ context: {
600
+ messagesCount: params.messages.length,
601
+ totalTokens: params.totalTokens,
602
+ conversationSummary: params.messages.slice(-2).map((m) => `${m.role}: ${m.content?.substring(0, 100) || ""}`).join(" | ")
603
+ }
604
+ };
605
+ }
606
+ function createLLMCallEvent(params) {
607
+ const durationMs = params.endTime - params.startTime;
608
+ const inputCost = (params.response.usage?.promptTokens || 0) * 3e-6;
609
+ const outputCost = (params.response.usage?.completionTokens || 0) * 15e-6;
610
+ return {
611
+ type: "llm:call",
612
+ iteration: params.iteration,
613
+ request: {
614
+ model: params.model,
615
+ temperature: params.temperature,
616
+ maxTokens: params.maxTokens,
617
+ tools: params.tools
618
+ },
619
+ response: {
620
+ content: params.response.content || null,
621
+ toolCalls: params.response.toolCalls?.map((tc) => ({
622
+ id: tc.id,
623
+ name: tc.name,
624
+ input: tc.input
625
+ })),
626
+ stopReason: params.response.toolCalls && params.response.toolCalls.length > 0 ? "tool_use" : "end_turn",
627
+ usage: {
628
+ inputTokens: params.response.usage.promptTokens,
629
+ outputTokens: params.response.usage.completionTokens,
630
+ totalTokens: params.response.usage.promptTokens + params.response.usage.completionTokens
631
+ }
632
+ },
633
+ timing: {
634
+ startedAt: new Date(params.startTime).toISOString(),
635
+ completedAt: new Date(params.endTime).toISOString(),
636
+ durationMs
637
+ },
638
+ cost: {
639
+ inputCost,
640
+ outputCost,
641
+ totalCost: inputCost + outputCost,
642
+ currency: "USD"
643
+ }
644
+ };
645
+ }
646
+ function createToolExecutionEvent(params) {
647
+ const durationMs = params.endTime - params.startTime;
648
+ const resultStr = typeof params.output.result === "string" ? params.output.result : JSON.stringify(params.output.result || "");
649
+ const TRACE_OUTPUT_LIMIT = 5e4;
650
+ const truncated = resultStr.length > TRACE_OUTPUT_LIMIT;
651
+ return {
652
+ type: "tool:execution",
653
+ iteration: params.iteration,
654
+ tool: {
655
+ name: params.toolName,
656
+ callId: params.callId
657
+ },
658
+ input: params.input,
659
+ output: {
660
+ success: params.output.success,
661
+ result: truncated ? resultStr.substring(0, TRACE_OUTPUT_LIMIT) + `
662
+ ... (truncated from ${resultStr.length} chars)` : params.output.result,
663
+ error: params.output.error,
664
+ truncated,
665
+ originalLength: resultStr.length
666
+ },
667
+ timing: {
668
+ startedAt: new Date(params.startTime).toISOString(),
669
+ completedAt: new Date(params.endTime).toISOString(),
670
+ durationMs
671
+ },
672
+ metadata: params.metadata
673
+ };
674
+ }
675
+ function createMemorySnapshotEvent(params) {
676
+ return {
677
+ type: "memory:snapshot",
678
+ iteration: params.iteration,
679
+ sessionMemory: {
680
+ conversationHistory: params.conversationHistory,
681
+ userPreferences: params.userPreferences
682
+ },
683
+ sharedMemory: {
684
+ facts: params.facts,
685
+ findings: params.findings
686
+ },
687
+ executionMemory: {
688
+ filesRead: params.filesRead,
689
+ searchesMade: params.searchesMade,
690
+ toolsUsed: params.toolsUsed
691
+ }
692
+ };
693
+ }
694
+ function createDecisionPointEvent(params) {
695
+ return {
696
+ type: "decision:point",
697
+ iteration: params.iteration,
698
+ decision: params.decision,
699
+ toolSelection: params.toolSelection,
700
+ stoppingCondition: params.stoppingCondition
701
+ };
702
+ }
703
+ function createSynthesisForcedEvent(params) {
704
+ return {
705
+ type: "synthesis:forced",
706
+ iteration: params.iteration,
707
+ trigger: {
708
+ reason: params.reason,
709
+ lastIteration: params.lastIteration,
710
+ lastToolCall: params.lastToolCall
711
+ },
712
+ synthesisPrompt: params.synthesisPrompt,
713
+ synthesisResponse: params.synthesisResponse
714
+ };
715
+ }
716
+ function createErrorCapturedEvent(params) {
717
+ return {
718
+ type: "error:captured",
719
+ iteration: params.iteration,
720
+ error: {
721
+ message: params.error.message,
722
+ stack: params.error.stack || "",
723
+ code: params.error.code,
724
+ name: params.error.name
725
+ },
726
+ context: {
727
+ lastLLMCall: params.lastLLMCall,
728
+ lastToolCall: params.lastToolCall,
729
+ currentMessages: params.currentMessages.slice(-5).map((m) => ({
730
+ role: m.role === "tool" ? "assistant" : m.role,
731
+ contentPreview: m.content?.substring(0, 100) || ""
732
+ })),
733
+ memoryState: params.memoryState,
734
+ availableTools: params.availableTools
735
+ },
736
+ agentStack: params.agentStack
737
+ };
738
+ }
739
+ function createPromptDiffEvent(params) {
740
+ return {
741
+ type: "prompt:diff",
742
+ iteration: params.iteration,
743
+ diff: {
744
+ messagesAdded: params.messagesAdded,
745
+ messagesRemoved: params.messagesRemoved,
746
+ totalMessages: params.totalMessages,
747
+ changes: params.changes,
748
+ contextGrowth: {
749
+ tokensBefore: params.tokensBefore,
750
+ tokensAfter: params.tokensAfter,
751
+ delta: params.tokensAfter - params.tokensBefore
752
+ }
753
+ }
754
+ };
755
+ }
756
+ function createToolFilterEvent(params) {
757
+ return {
758
+ type: "tool:filter",
759
+ iteration: params.iteration,
760
+ filtering: {
761
+ before: {
762
+ totalTools: params.beforeTools.length,
763
+ tools: params.beforeTools
764
+ },
765
+ after: {
766
+ totalTools: params.afterTools.length,
767
+ tools: params.afterTools
768
+ },
769
+ filtered: params.filtered
770
+ }
771
+ };
772
+ }
773
+ function createContextTrimEvent(params) {
774
+ return {
775
+ type: "context:trim",
776
+ iteration: params.iteration,
777
+ trimming: {
778
+ trigger: params.trigger,
779
+ before: {
780
+ messageCount: params.messageCountBefore,
781
+ estimatedTokens: params.tokensBefore
782
+ },
783
+ after: {
784
+ messageCount: params.messageCountAfter,
785
+ estimatedTokens: params.tokensAfter
786
+ },
787
+ removed: {
788
+ messageCount: params.messagesRemoved,
789
+ tokensRemoved: params.tokensRemoved,
790
+ contentPreview: params.contentPreview
791
+ },
792
+ strategy: params.strategy
793
+ }
794
+ };
795
+ }
796
+ function createStoppingAnalysisEvent(params) {
797
+ return {
798
+ type: "stopping:analysis",
799
+ iteration: params.iteration,
800
+ conditions: params.conditions,
801
+ reasoning: params.reasoning,
802
+ metrics: {
803
+ iterationsUsed: params.iterationsUsed,
804
+ iterationsRemaining: params.iterationsRemaining,
805
+ timeElapsedMs: params.timeElapsedMs,
806
+ timeRemainingMs: params.timeRemainingMs,
807
+ toolCallsInLast3Iterations: params.toolCallsInLast3Iterations,
808
+ confidenceScore: params.confidenceScore
809
+ }
810
+ };
811
+ }
812
+ function createLLMValidationEvent(params) {
813
+ return {
814
+ type: "llm:validation",
815
+ iteration: params.iteration,
816
+ validation: {
817
+ stopReason: params.stopReason,
818
+ isValid: params.isValid,
819
+ checks: {
820
+ hasContent: params.hasContent,
821
+ hasToolCalls: params.hasToolCalls,
822
+ toolCallsValid: params.toolCallsValid,
823
+ jsonParseable: params.jsonParseable,
824
+ schemaValid: params.schemaValid
825
+ },
826
+ issues: params.issues
827
+ }
828
+ };
829
+ }
830
+ function createFactAddedEvent(params) {
831
+ return {
832
+ type: "memory:fact_added",
833
+ iteration: params.iteration,
834
+ fact: params.fact,
835
+ factSheetStats: params.factSheetStats
836
+ };
837
+ }
838
+ function createArchiveStoreEvent(params) {
839
+ return {
840
+ type: "memory:archive_store",
841
+ iteration: params.iteration,
842
+ entry: params.entry,
843
+ archiveStats: params.archiveStats
844
+ };
845
+ }
846
+ function createSummarizationLLMCallEvent(params) {
847
+ return {
848
+ type: "memory:summarization_llm_call",
849
+ iteration: params.iteration,
850
+ prompt: params.prompt,
851
+ rawResponse: params.rawResponse,
852
+ parseSuccess: params.parseSuccess,
853
+ parseError: params.parseError,
854
+ timing: {
855
+ durationMs: params.durationMs,
856
+ outputTokens: params.outputTokens
857
+ }
858
+ };
859
+ }
860
+ function createSummarizationResultEvent(params) {
861
+ return {
862
+ type: "memory:summarization_result",
863
+ iteration: params.iteration,
864
+ input: params.input,
865
+ output: params.output,
866
+ delta: params.delta,
867
+ efficiency: params.efficiency
868
+ };
869
+ }
870
+
871
+ export { DEFAULT_TRACE_CONFIG, FileTracer, IncrementalTraceWriter, TRACE_DIR_RELATIVE, createArchiveStoreEvent, createContextTrimEvent, createDecisionPointEvent, createDefaultPrivacyConfig, createErrorCapturedEvent, createFactAddedEvent, createIterationDetailEvent, createLLMCallEvent, createLLMValidationEvent, createMemorySnapshotEvent, createPromptDiffEvent, createStoppingAnalysisEvent, createSummarizationLLMCallEvent, createSummarizationResultEvent, createSynthesisForcedEvent, createToolExecutionEvent, createToolFilterEvent, formatTraceLoadError, loadTrace, redactPaths, redactSecretsFromString, redactTraceEvent, redactValue };
872
+ //# sourceMappingURL=index.js.map
873
+ //# sourceMappingURL=index.js.map