agent-inspect 1.5.0 → 1.7.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +14 -4
  3. package/docs/ADAPTER-CONFORMANCE.md +35 -0
  4. package/docs/ADAPTERS.md +79 -1
  5. package/docs/API.md +184 -10
  6. package/docs/ARCHITECTURE.md +4 -0
  7. package/docs/CLI.md +41 -12
  8. package/docs/KNOWN-ISSUES.md +11 -1
  9. package/docs/LIMITATIONS.md +19 -2
  10. package/docs/SCHEMA.md +17 -7
  11. package/package.json +23 -2
  12. package/packages/cli/dist/index.cjs +2449 -157
  13. package/packages/cli/dist/index.cjs.map +1 -1
  14. package/packages/cli/dist/index.mjs +2450 -158
  15. package/packages/cli/dist/index.mjs.map +1 -1
  16. package/packages/core/dist/advanced.cjs +839 -18
  17. package/packages/core/dist/advanced.cjs.map +1 -1
  18. package/packages/core/dist/advanced.d.cts +98 -3
  19. package/packages/core/dist/advanced.d.ts +98 -3
  20. package/packages/core/dist/advanced.mjs +7 -4
  21. package/packages/core/dist/chunk-57S5D6HR.mjs +655 -0
  22. package/packages/core/dist/chunk-57S5D6HR.mjs.map +1 -0
  23. package/packages/core/dist/chunk-6QSLZCBJ.mjs +743 -0
  24. package/packages/core/dist/chunk-6QSLZCBJ.mjs.map +1 -0
  25. package/packages/core/dist/chunk-6SZPTECC.mjs +342 -0
  26. package/packages/core/dist/chunk-6SZPTECC.mjs.map +1 -0
  27. package/packages/core/dist/{chunk-QX3ZMPUF.mjs → chunk-74XZ6N7Q.mjs} +13 -55
  28. package/packages/core/dist/chunk-74XZ6N7Q.mjs.map +1 -0
  29. package/packages/core/dist/{chunk-QPAU2TPA.mjs → chunk-HR7G62IE.mjs} +4 -4
  30. package/packages/core/dist/{chunk-QPAU2TPA.mjs.map → chunk-HR7G62IE.mjs.map} +1 -1
  31. package/packages/core/dist/chunk-S4YWKV4G.mjs +48 -0
  32. package/packages/core/dist/chunk-S4YWKV4G.mjs.map +1 -0
  33. package/packages/core/dist/chunk-TFLPUZ56.mjs +1571 -0
  34. package/packages/core/dist/chunk-TFLPUZ56.mjs.map +1 -0
  35. package/packages/core/dist/{chunk-Q6EPNB3V.mjs → chunk-TZISEVLQ.mjs} +34 -183
  36. package/packages/core/dist/chunk-TZISEVLQ.mjs.map +1 -0
  37. package/packages/core/dist/chunk-U2BGPESY.mjs +150 -0
  38. package/packages/core/dist/chunk-U2BGPESY.mjs.map +1 -0
  39. package/packages/core/dist/chunk-VTIB5MDK.mjs +304 -0
  40. package/packages/core/dist/chunk-VTIB5MDK.mjs.map +1 -0
  41. package/packages/core/dist/{chunk-5EMIZZXD.mjs → chunk-Y56BPA3B.mjs} +87 -4
  42. package/packages/core/dist/chunk-Y56BPA3B.mjs.map +1 -0
  43. package/packages/core/dist/diff.d.cts +3 -2
  44. package/packages/core/dist/diff.d.ts +3 -2
  45. package/packages/core/dist/exporters.cjs.map +1 -1
  46. package/packages/core/dist/exporters.d.cts +3 -2
  47. package/packages/core/dist/exporters.d.ts +3 -2
  48. package/packages/core/dist/exporters.mjs +2 -2
  49. package/packages/core/dist/index.cjs +2975 -229
  50. package/packages/core/dist/index.cjs.map +1 -1
  51. package/packages/core/dist/index.d.cts +27 -6
  52. package/packages/core/dist/index.d.ts +27 -6
  53. package/packages/core/dist/index.mjs +113 -60
  54. package/packages/core/dist/index.mjs.map +1 -1
  55. package/packages/core/dist/{log-config-BzGmDYum.d.cts → inspect-event-Des4JDHo.d.cts} +1 -31
  56. package/packages/core/dist/{log-config-BzGmDYum.d.ts → inspect-event-Des4JDHo.d.ts} +1 -31
  57. package/packages/core/dist/log-config-BnH8Ykcb.d.cts +33 -0
  58. package/packages/core/dist/log-config-C1GcJPIM.d.ts +33 -0
  59. package/packages/core/dist/logs.d.cts +3 -2
  60. package/packages/core/dist/logs.d.ts +3 -2
  61. package/packages/core/dist/logs.mjs +3 -3
  62. package/packages/core/dist/persisted-inspect-event-0kaRADsp.d.cts +56 -0
  63. package/packages/core/dist/persisted-inspect-event-DiFto0K2.d.ts +56 -0
  64. package/packages/core/dist/persisted.cjs +38 -40
  65. package/packages/core/dist/persisted.cjs.map +1 -1
  66. package/packages/core/dist/persisted.d.cts +6 -55
  67. package/packages/core/dist/persisted.d.ts +6 -55
  68. package/packages/core/dist/persisted.mjs +4 -2
  69. package/packages/core/dist/readers.cjs +2590 -0
  70. package/packages/core/dist/readers.cjs.map +1 -0
  71. package/packages/core/dist/readers.d.cts +80 -0
  72. package/packages/core/dist/readers.d.ts +80 -0
  73. package/packages/core/dist/readers.mjs +9 -0
  74. package/packages/core/dist/readers.mjs.map +1 -0
  75. package/packages/core/dist/{types-CNbheSdk.d.cts → types-DB8jB6Jg.d.cts} +7 -1
  76. package/packages/core/dist/{types-Bkt7LS01.d.ts → types-tSix7tfv.d.ts} +7 -1
  77. package/packages/core/dist/writers.cjs +997 -0
  78. package/packages/core/dist/writers.cjs.map +1 -0
  79. package/packages/core/dist/writers.d.cts +62 -0
  80. package/packages/core/dist/writers.d.ts +62 -0
  81. package/packages/core/dist/writers.mjs +9 -0
  82. package/packages/core/dist/writers.mjs.map +1 -0
  83. package/packages/core/dist/chunk-5EMIZZXD.mjs.map +0 -1
  84. package/packages/core/dist/chunk-Q6EPNB3V.mjs.map +0 -1
  85. package/packages/core/dist/chunk-QX3ZMPUF.mjs.map +0 -1
  86. package/packages/core/dist/chunk-XDBND27A.mjs +0 -975
  87. package/packages/core/dist/chunk-XDBND27A.mjs.map +0 -1
@@ -22,6 +22,9 @@ var os__default = /*#__PURE__*/_interopDefault(os);
22
22
  var process2__default = /*#__PURE__*/_interopDefault(process2);
23
23
  var tty__default = /*#__PURE__*/_interopDefault(tty);
24
24
 
25
+ // package.json
26
+ var version = "1.7.0";
27
+
25
28
  // packages/core/src/types.ts
26
29
  var STEP_TYPES = [
27
30
  "run",
@@ -144,6 +147,7 @@ function isPersistedTokenUsage(value) {
144
147
  if (!isOptionalNonNegativeNumber(value.input)) return false;
145
148
  if (!isOptionalNonNegativeNumber(value.output)) return false;
146
149
  if (!isOptionalNonNegativeNumber(value.total)) return false;
150
+ if (!isOptionalNonNegativeNumber(value.cached)) return false;
147
151
  return true;
148
152
  }
149
153
  function isPersistedTraceContext(value) {
@@ -189,8 +193,405 @@ function isPersistedInspectEvent(value) {
189
193
  return true;
190
194
  }
191
195
 
192
- // packages/core/src/persisted/to-trace-event.ts
196
+ // packages/core/src/correlation-metadata.ts
197
+ var TRACE_CORRELATION_KEYS = [
198
+ "correlationId",
199
+ "requestId",
200
+ "decisionId",
201
+ "groupId"
202
+ ];
203
+ function isNonEmptyString2(value) {
204
+ return typeof value === "string" && value.length > 0;
205
+ }
206
+ function extractCorrelationMetadata(record) {
207
+ if (!record) {
208
+ return void 0;
209
+ }
210
+ const out = {};
211
+ let found = false;
212
+ for (const key of TRACE_CORRELATION_KEYS) {
213
+ const value = record[key];
214
+ if (isNonEmptyString2(value)) {
215
+ out[key] = value;
216
+ found = true;
217
+ }
218
+ }
219
+ return found ? out : void 0;
220
+ }
221
+
222
+ // packages/core/src/persisted/token-usage.ts
223
+ function isRecord3(value) {
224
+ return typeof value === "object" && value !== null && !Array.isArray(value);
225
+ }
226
+ function nonNegativeFinite(value) {
227
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
228
+ }
229
+ function normalizeTokenUsage(value) {
230
+ if (!isRecord3(value)) return void 0;
231
+ const input3 = nonNegativeFinite(value.input);
232
+ const output2 = nonNegativeFinite(value.output);
233
+ const suppliedTotal = nonNegativeFinite(value.total);
234
+ const cached = nonNegativeFinite(value.cached);
235
+ const derivedTotal = input3 !== void 0 && output2 !== void 0 && Number.isFinite(input3 + output2) ? input3 + output2 : void 0;
236
+ const total = suppliedTotal ?? derivedTotal;
237
+ if (input3 === void 0 && output2 === void 0 && total === void 0 && cached === void 0) {
238
+ return void 0;
239
+ }
240
+ return {
241
+ ...input3 !== void 0 ? { input: input3 } : {},
242
+ ...output2 !== void 0 ? { output: output2 } : {},
243
+ ...total !== void 0 ? { total } : {},
244
+ ...cached !== void 0 ? { cached } : {}
245
+ };
246
+ }
247
+
248
+ // packages/core/src/persisted/from-trace-event.ts
249
+ function sanitizeIdPart(value) {
250
+ return value.replace(/[^a-zA-Z0-9_-]/g, "_");
251
+ }
252
+ function nodeIdForEvent(event) {
253
+ switch (event.event) {
254
+ case "run_started":
255
+ case "run_completed":
256
+ return event.runId;
257
+ case "step_started":
258
+ case "step_completed":
259
+ return event.stepId;
260
+ default:
261
+ return "unknown";
262
+ }
263
+ }
264
+ function createPersistedEventId(event, eventIndex) {
265
+ const runId = sanitizeIdPart(event.runId);
266
+ const ev = sanitizeIdPart(event.event);
267
+ const node = sanitizeIdPart(nodeIdForEvent(event));
268
+ return `manual:${runId}:${ev}:${node}:${eventIndex}`;
269
+ }
270
+ function toIsoTimestamp(ms) {
271
+ if (typeof ms !== "number" || !Number.isFinite(ms)) {
272
+ return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
273
+ }
274
+ return { iso: new Date(ms).toISOString(), invalidTimestamp: false };
275
+ }
276
+ function buildSource(options) {
277
+ return {
278
+ type: "manual",
279
+ name: options?.sourceName ?? "trace-event",
280
+ version: options?.sourceVersion ?? "0.1"
281
+ };
282
+ }
283
+ function mapStepTypeToInspectKind(type) {
284
+ switch (type) {
285
+ case "run":
286
+ return "RUN";
287
+ case "llm":
288
+ return "LLM";
289
+ case "tool":
290
+ return "TOOL";
291
+ case "decision":
292
+ return "DECISION";
293
+ case "logic":
294
+ case "state":
295
+ case "custom":
296
+ return "LOGIC";
297
+ default:
298
+ return "LOGIC";
299
+ }
300
+ }
301
+ function mapRunOrStepStatus(status) {
302
+ return status === "success" ? "ok" : "error";
303
+ }
304
+ function mapErrorInfo(error) {
305
+ if (!error?.message) {
306
+ return {};
307
+ }
308
+ const out = {
309
+ persisted: {
310
+ message: error.message,
311
+ name: "Error"
312
+ }
313
+ };
314
+ if (typeof error.stack === "string" && error.stack.length > 0) {
315
+ out.errorStack = error.stack;
316
+ }
317
+ return out;
318
+ }
319
+ function mapTokenUsageFromMetadata(metadata) {
320
+ return normalizeTokenUsage(metadata?.tokens);
321
+ }
322
+ function compactAttributes(entries) {
323
+ const out = {};
324
+ for (const [key, value] of Object.entries(entries)) {
325
+ if (value !== void 0) {
326
+ out[key] = value;
327
+ }
328
+ }
329
+ return Object.keys(out).length > 0 ? out : void 0;
330
+ }
331
+ function traceEventToPersistedInspectEvent(event, options) {
332
+ const eventIndex = options?.eventIndex ?? 0;
333
+ const eventId = createPersistedEventId(event, eventIndex);
334
+ const source = buildSource(options);
335
+ const tsMain = toIsoTimestamp(event.timestamp);
336
+ switch (event.event) {
337
+ case "run_started": {
338
+ const tsStart = toIsoTimestamp(event.startTime);
339
+ const correlation = extractCorrelationMetadata(event.metadata);
340
+ const attributes = compactAttributes({
341
+ legacyEvent: "run_started",
342
+ metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
343
+ correlationId: correlation?.correlationId,
344
+ requestId: correlation?.requestId,
345
+ decisionId: correlation?.decisionId,
346
+ groupId: correlation?.groupId,
347
+ invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
348
+ });
349
+ return {
350
+ schemaVersion: "0.2",
351
+ eventId,
352
+ runId: event.runId,
353
+ kind: "RUN",
354
+ name: event.name,
355
+ status: "running",
356
+ timestamp: tsMain.iso,
357
+ startedAt: tsStart.iso,
358
+ confidence: "explicit",
359
+ source,
360
+ attributes
361
+ };
362
+ }
363
+ case "run_completed": {
364
+ const tsEnd = toIsoTimestamp(event.endTime);
365
+ const { persisted: error, errorStack } = mapErrorInfo(event.error);
366
+ const attributes = compactAttributes({
367
+ legacyEvent: "run_completed",
368
+ errorStack,
369
+ invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
370
+ });
371
+ return {
372
+ schemaVersion: "0.2",
373
+ eventId,
374
+ runId: event.runId,
375
+ kind: "RUN",
376
+ name: "run",
377
+ status: mapRunOrStepStatus(event.status),
378
+ timestamp: tsMain.iso,
379
+ endedAt: tsEnd.iso,
380
+ durationMs: event.durationMs,
381
+ confidence: "explicit",
382
+ source,
383
+ attributes,
384
+ error
385
+ };
386
+ }
387
+ case "step_started": {
388
+ const tsStart = toIsoTimestamp(event.startTime);
389
+ const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
390
+ const attributes = compactAttributes({
391
+ legacyEvent: "step_started",
392
+ stepId: event.stepId,
393
+ stepType: event.type,
394
+ metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
395
+ invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
396
+ });
397
+ const out = {
398
+ schemaVersion: "0.2",
399
+ eventId,
400
+ runId: event.runId,
401
+ kind: mapStepTypeToInspectKind(event.type),
402
+ name: event.name,
403
+ status: "running",
404
+ timestamp: tsMain.iso,
405
+ startedAt: tsStart.iso,
406
+ confidence: "explicit",
407
+ source,
408
+ attributes
409
+ };
410
+ if (event.parentId !== void 0) {
411
+ out.parentId = event.parentId;
412
+ }
413
+ if (tokenUsage !== void 0) {
414
+ out.tokenUsage = tokenUsage;
415
+ }
416
+ return out;
417
+ }
418
+ case "step_completed": {
419
+ const tsEnd = toIsoTimestamp(event.endTime);
420
+ const { persisted: error, errorStack } = mapErrorInfo(event.error);
421
+ const attributes = compactAttributes({
422
+ legacyEvent: "step_completed",
423
+ stepId: event.stepId,
424
+ errorStack,
425
+ invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
426
+ });
427
+ return {
428
+ schemaVersion: "0.2",
429
+ eventId,
430
+ runId: event.runId,
431
+ kind: "LOGIC",
432
+ name: event.stepId,
433
+ status: mapRunOrStepStatus(event.status),
434
+ timestamp: tsMain.iso,
435
+ endedAt: tsEnd.iso,
436
+ durationMs: event.durationMs,
437
+ confidence: "explicit",
438
+ source,
439
+ attributes,
440
+ error
441
+ };
442
+ }
443
+ default: {
444
+ const _exhaustive = event;
445
+ throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
446
+ }
447
+ }
448
+ }
449
+ function traceEventsToPersistedInspectEvents(events, options) {
450
+ return events.map(
451
+ (event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
452
+ );
453
+ }
454
+
455
+ // packages/core/src/persisted/to-inspect-event.ts
456
+ function compactAttributes2(entries) {
457
+ const out = {};
458
+ for (const [key, value] of Object.entries(entries)) {
459
+ if (value !== void 0) {
460
+ out[key] = value;
461
+ }
462
+ }
463
+ return Object.keys(out).length > 0 ? out : void 0;
464
+ }
193
465
  function parseIsoToMs(iso) {
466
+ const parsed = Date.parse(iso);
467
+ if (!Number.isFinite(parsed)) {
468
+ return { ms: 0, invalidTimestamp: true };
469
+ }
470
+ return { ms: parsed, invalidTimestamp: false };
471
+ }
472
+ function mapPersistedSourceToInspect(event) {
473
+ const attrs = event.attributes ?? {};
474
+ const sourceName = event.source.name;
475
+ if (sourceName === "pino") {
476
+ return {
477
+ type: "pino",
478
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
479
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
480
+ };
481
+ }
482
+ if (sourceName === "winston") {
483
+ return {
484
+ type: "winston",
485
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
486
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
487
+ };
488
+ }
489
+ const mapType = (t) => {
490
+ switch (t) {
491
+ case "manual":
492
+ return "manual";
493
+ case "json-log":
494
+ return "json-log";
495
+ case "log4js":
496
+ return "log4js";
497
+ case "adapter":
498
+ case "ai-sdk":
499
+ case "otel":
500
+ return "adapter";
501
+ default:
502
+ return "json-log";
503
+ }
504
+ };
505
+ return {
506
+ type: mapType(event.source.type),
507
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
508
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
509
+ };
510
+ }
511
+ function buildInspectAttributes(event) {
512
+ const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
513
+ if (event.inputSummary !== void 0) {
514
+ attrs.inputSummary = event.inputSummary;
515
+ }
516
+ if (event.outputSummary !== void 0) {
517
+ attrs.outputSummary = event.outputSummary;
518
+ }
519
+ if (event.error) {
520
+ if (event.error.name !== void 0) {
521
+ attrs.errorName = event.error.name;
522
+ }
523
+ attrs.errorMessage = event.error.message;
524
+ if (event.error.code !== void 0) {
525
+ attrs.errorCode = event.error.code;
526
+ }
527
+ }
528
+ if (event.tokenUsage) {
529
+ attrs.tokens = { ...event.tokenUsage };
530
+ }
531
+ if (event.source.type === "ai-sdk" || event.source.type === "otel") {
532
+ attrs.originalSourceType = event.source.type;
533
+ }
534
+ if (event.source.name !== void 0) {
535
+ attrs.sourceName = event.source.name;
536
+ }
537
+ if (event.source.version !== void 0) {
538
+ attrs.sourceVersion = event.source.version;
539
+ }
540
+ return attrs;
541
+ }
542
+ function persistedInspectEventToInspectEvent(event) {
543
+ if (!isPersistedInspectEvent(event)) {
544
+ throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
545
+ }
546
+ const ts = parseIsoToMs(event.timestamp);
547
+ const attrs = buildInspectAttributes(event);
548
+ if (ts.invalidTimestamp) {
549
+ attrs.invalidTimestamp = true;
550
+ }
551
+ let status;
552
+ if (event.status === "running" || event.status === "ok" || event.status === "error") {
553
+ status = event.status;
554
+ } else if (event.status === "unknown") {
555
+ attrs.persistedStatus = "unknown";
556
+ }
557
+ const out = {
558
+ eventId: event.eventId,
559
+ runId: event.runId,
560
+ name: event.name,
561
+ kind: event.kind,
562
+ timestamp: ts.ms,
563
+ confidence: event.confidence,
564
+ source: mapPersistedSourceToInspect(event),
565
+ attributes: compactAttributes2(attrs)
566
+ };
567
+ if (event.parentId !== void 0) {
568
+ out.parentId = event.parentId;
569
+ }
570
+ if (status !== void 0) {
571
+ out.status = status;
572
+ }
573
+ if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
574
+ out.durationMs = event.durationMs;
575
+ }
576
+ return out;
577
+ }
578
+ function persistedInspectEventsToInspectEvents(events, options) {
579
+ const skipInvalid = options?.skipInvalid === true;
580
+ const out = [];
581
+ for (const event of events) {
582
+ if (!isPersistedInspectEvent(event)) {
583
+ if (skipInvalid) {
584
+ continue;
585
+ }
586
+ throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
587
+ }
588
+ out.push(persistedInspectEventToInspectEvent(event));
589
+ }
590
+ return out;
591
+ }
592
+
593
+ // packages/core/src/persisted/to-trace-event.ts
594
+ function parseIsoToMs2(iso) {
194
595
  const parsed = Date.parse(iso);
195
596
  return Number.isFinite(parsed) ? parsed : 0;
196
597
  }
@@ -232,10 +633,10 @@ function mapPersistedStatusToRunStatus(status) {
232
633
  return void 0;
233
634
  }
234
635
  }
235
- function mapPersistedError(error) {
636
+ function mapPersistedError(error, attributes) {
236
637
  if (!error?.message) return void 0;
237
638
  const out = { message: error.message };
238
- const stack = typeof error.name === "string" && error.name.length > 0 ? error.name : void 0;
639
+ const stack = typeof attributes?.errorStack === "string" && attributes.errorStack.length > 0 ? attributes.errorStack : void 0;
239
640
  if (stack) {
240
641
  out.stack = stack;
241
642
  }
@@ -249,7 +650,9 @@ function mapTokenUsageToMetadata(tokenUsage, attributes) {
249
650
  if (tokenUsage) {
250
651
  metadata.tokens = {
251
652
  ...tokenUsage.input !== void 0 ? { input: tokenUsage.input } : {},
252
- ...tokenUsage.output !== void 0 ? { output: tokenUsage.output } : {}
653
+ ...tokenUsage.output !== void 0 ? { output: tokenUsage.output } : {},
654
+ ...tokenUsage.total !== void 0 ? { total: tokenUsage.total } : {},
655
+ ...tokenUsage.cached !== void 0 ? { cached: tokenUsage.cached } : {}
253
656
  };
254
657
  }
255
658
  return Object.keys(metadata).length > 0 ? metadata : void 0;
@@ -288,9 +691,9 @@ function resolveStepType(event) {
288
691
  return mapInspectKindToStepType(event.kind);
289
692
  }
290
693
  function resolveTimes(event) {
291
- const timestamp = parseIsoToMs(event.timestamp);
292
- const startTime = event.startedAt !== void 0 ? parseIsoToMs(event.startedAt) : timestamp;
293
- let endTime = event.endedAt !== void 0 ? parseIsoToMs(event.endedAt) : timestamp;
694
+ const timestamp = parseIsoToMs2(event.timestamp);
695
+ const startTime = event.startedAt !== void 0 ? parseIsoToMs2(event.startedAt) : timestamp;
696
+ let endTime = event.endedAt !== void 0 ? parseIsoToMs2(event.endedAt) : timestamp;
294
697
  if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0 && event.endedAt === void 0) {
295
698
  endTime = startTime + event.durationMs;
296
699
  }
@@ -322,7 +725,7 @@ function fromLegacyRunCompleted(event) {
322
725
  endTime,
323
726
  durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
324
727
  };
325
- const error = mapPersistedError(event.error);
728
+ const error = mapPersistedError(event.error, event.attributes);
326
729
  if (error) out.error = error;
327
730
  return out;
328
731
  }
@@ -356,7 +759,7 @@ function fromLegacyStepCompleted(event) {
356
759
  endTime,
357
760
  durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
358
761
  };
359
- const error = mapPersistedError(event.error);
762
+ const error = mapPersistedError(event.error, event.attributes);
360
763
  if (error) out.error = error;
361
764
  return out;
362
765
  }
@@ -387,7 +790,7 @@ function fromNativeRun(event) {
387
790
  endTime,
388
791
  durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
389
792
  };
390
- const error = mapPersistedError(event.error);
793
+ const error = mapPersistedError(event.error, event.attributes);
391
794
  if (error) completed.error = error;
392
795
  out.push(completed);
393
796
  }
@@ -429,7 +832,7 @@ function fromNativeStep(event) {
429
832
  endTime,
430
833
  durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
431
834
  };
432
- const error = mapPersistedError(event.error);
835
+ const error = mapPersistedError(event.error, event.attributes);
433
836
  if (error) completed.error = error;
434
837
  out.push(completed);
435
838
  }
@@ -541,7 +944,15 @@ var TreeBuilder = class {
541
944
  return out;
542
945
  }
543
946
  };
544
- function isRecord3(v) {
947
+
948
+ // packages/core/src/persisted/tree-bridge.ts
949
+ function persistedInspectEventsToRunTrees(events, options) {
950
+ const inspectEvents = persistedInspectEventsToInspectEvents(events, {
951
+ skipInvalid: options?.skipInvalid
952
+ });
953
+ return new TreeBuilder().build(inspectEvents);
954
+ }
955
+ function isRecord4(v) {
545
956
  return typeof v === "object" && v !== null && !Array.isArray(v);
546
957
  }
547
958
  function isNonEmptyStringArray(v) {
@@ -553,7 +964,7 @@ function validateRedact(redact) {
553
964
  }
554
965
  for (const r of redact) {
555
966
  if (typeof r === "string") continue;
556
- if (!isRecord3(r)) {
967
+ if (!isRecord4(r)) {
557
968
  throw new Error("Invalid config: redact entries must be strings or objects");
558
969
  }
559
970
  if (typeof r.key !== "string" || r.key.trim() === "") {
@@ -572,7 +983,7 @@ function validateRedact(redact) {
572
983
  }
573
984
  }
574
985
  function validateMappings(mappings) {
575
- if (!isRecord3(mappings)) {
986
+ if (!isRecord4(mappings)) {
576
987
  throw new Error("Invalid config: mappings must be an object");
577
988
  }
578
989
  }
@@ -622,7 +1033,7 @@ async function loadLogIngestConfig(configPath) {
622
1033
  const msg = e instanceof Error ? e.message : String(e);
623
1034
  throw new Error(`Invalid JSON in config file: ${configPath} (${msg})`);
624
1035
  }
625
- if (!isRecord3(parsed)) {
1036
+ if (!isRecord4(parsed)) {
626
1037
  throw new Error("Invalid config: expected a JSON object at top-level");
627
1038
  }
628
1039
  const user = parsed;
@@ -659,7 +1070,7 @@ async function loadLogIngestConfig(configPath) {
659
1070
  }
660
1071
  return mergeLogIngestConfig(DEFAULT_LOG_INGEST_CONFIG, user);
661
1072
  }
662
- function isRecord4(v) {
1073
+ function isRecord5(v) {
663
1074
  return typeof v === "object" && v !== null && !Array.isArray(v);
664
1075
  }
665
1076
  var JsonLogParser = class {
@@ -684,7 +1095,7 @@ var JsonLogParser = class {
684
1095
  });
685
1096
  continue;
686
1097
  }
687
- if (!isRecord4(parsed)) {
1098
+ if (!isRecord5(parsed)) {
688
1099
  warnings.push({
689
1100
  code: "MALFORMED_JSON",
690
1101
  message: "JSON log line must be an object",
@@ -715,7 +1126,7 @@ var JsonLogParser = class {
715
1126
  return this.parseLines(lines, filePath);
716
1127
  }
717
1128
  };
718
- function isRecord5(v) {
1129
+ function isRecord6(v) {
719
1130
  return typeof v === "object" && v !== null && !Array.isArray(v);
720
1131
  }
721
1132
  function findLastJsonObjectSubstring(line) {
@@ -791,7 +1202,7 @@ var Log4jsParser = class {
791
1202
  });
792
1203
  continue;
793
1204
  }
794
- if (!isRecord5(parsed)) {
1205
+ if (!isRecord6(parsed)) {
795
1206
  warnings.push({
796
1207
  code: "UNSUPPORTED_LOG4JS_PAYLOAD",
797
1208
  message: "Embedded JSON payload must be an object",
@@ -869,7 +1280,7 @@ var DEFAULT_REDACT_KEYS = [
869
1280
  "secret",
870
1281
  "email"
871
1282
  ];
872
- function isRecord6(v) {
1283
+ function isRecord7(v) {
873
1284
  return typeof v === "object" && v !== null && !Array.isArray(v);
874
1285
  }
875
1286
  function toKey(s) {
@@ -942,7 +1353,7 @@ var Redactor = class {
942
1353
  if (Array.isArray(value)) {
943
1354
  return value.map((v) => this.#redactNested(v));
944
1355
  }
945
- if (isRecord6(value)) {
1356
+ if (isRecord7(value)) {
946
1357
  const out = {};
947
1358
  for (const [k, v] of Object.entries(value)) {
948
1359
  out[k] = this.redactValue(k, v);
@@ -1699,66 +2110,78 @@ function truncateStringForProfile(value, key, maxMetadataValueLength, maxPreview
1699
2110
  }
1700
2111
 
1701
2112
  // packages/core/src/read-trace.ts
1702
- function isRecord7(value) {
2113
+ function isRecord8(value) {
1703
2114
  return typeof value === "object" && value !== null && !Array.isArray(value);
1704
2115
  }
1705
2116
  function detectLineFormat(parsed) {
1706
- if (!isRecord7(parsed)) return "unknown";
2117
+ if (!isRecord8(parsed)) return "unknown";
1707
2118
  if (parsed.schemaVersion === "0.1") return "0.1";
1708
2119
  if (parsed.schemaVersion === "0.2") return "0.2";
1709
2120
  return "unknown";
1710
2121
  }
1711
2122
  function parseTraceJsonl(raw, options = {}) {
1712
2123
  const validate = options.validate ?? isTraceEvent;
2124
+ const emitWarning = (message) => {
2125
+ if (options.warnings !== false) warn(message);
2126
+ };
1713
2127
  const persisted = [];
1714
2128
  const traceEvents = [];
2129
+ const rows = [];
2130
+ let sourceEventCount = 0;
1715
2131
  let saw01 = false;
1716
2132
  let saw02 = false;
2133
+ let lineNumber = 0;
1717
2134
  for (const line of raw.split(/\r?\n/)) {
2135
+ lineNumber += 1;
1718
2136
  const trimmed = line.trim();
1719
2137
  if (trimmed === "") continue;
1720
2138
  let parsed;
1721
2139
  try {
1722
2140
  parsed = JSON.parse(trimmed);
1723
2141
  } catch {
1724
- warn("Skipped invalid JSON line in trace file");
2142
+ emitWarning("Skipped invalid JSON line in trace file");
1725
2143
  continue;
1726
2144
  }
1727
2145
  const format2 = detectLineFormat(parsed);
1728
2146
  if (format2 === "0.1") {
1729
2147
  saw01 = true;
1730
2148
  if (validate(parsed)) {
2149
+ sourceEventCount += 1;
1731
2150
  traceEvents.push(parsed);
2151
+ rows.push({ format: "0.1", event: parsed, sourceLine: lineNumber });
1732
2152
  } else {
1733
- warn("Skipped invalid trace event line in trace file");
2153
+ emitWarning("Skipped invalid trace event line in trace file");
1734
2154
  }
1735
2155
  continue;
1736
2156
  }
1737
2157
  if (format2 === "0.2") {
1738
2158
  saw02 = true;
1739
2159
  if (isPersistedInspectEvent(parsed)) {
2160
+ sourceEventCount += 1;
1740
2161
  persisted.push(parsed);
2162
+ rows.push({ format: "0.2", event: parsed, sourceLine: lineNumber });
2163
+ traceEvents.push(...persistedInspectEventToTraceEvents(parsed));
1741
2164
  } else {
1742
- warn("Skipped invalid persisted inspect event line in trace file");
2165
+ emitWarning("Skipped invalid persisted inspect event line in trace file");
1743
2166
  }
1744
2167
  continue;
1745
2168
  }
1746
- warn("Skipped trace line with unknown schemaVersion");
2169
+ emitWarning("Skipped trace line with unknown schemaVersion");
1747
2170
  }
1748
2171
  if (saw01 && saw02) {
1749
- warn("Trace file mixes schemaVersion 0.1 and 0.2 lines; normalizing all rows");
2172
+ emitWarning(
2173
+ "Trace file mixes schemaVersion 0.1 and 0.2 lines; normalizing all rows"
2174
+ );
1750
2175
  }
1751
- const converted = persisted.length > 0 ? persistedInspectEventsToTraceEvents(persisted) : [];
1752
- const events = [...traceEvents, ...converted];
1753
2176
  let format = "empty";
1754
2177
  if (saw01 && saw02) format = "mixed";
1755
2178
  else if (saw01) format = "0.1";
1756
2179
  else if (saw02) format = "0.2";
1757
- return { format, events, persisted };
2180
+ return { format, sourceEventCount, events: traceEvents, persisted, rows };
1758
2181
  }
1759
2182
 
1760
2183
  // packages/core/src/storage.ts
1761
- function isRecord8(value) {
2184
+ function isRecord9(value) {
1762
2185
  return typeof value === "object" && value !== null && !Array.isArray(value);
1763
2186
  }
1764
2187
  function nonEmptyString(value) {
@@ -1769,7 +2192,7 @@ function finiteNumber(value) {
1769
2192
  }
1770
2193
  function optionalErrorInfo(value) {
1771
2194
  if (value === void 0) return true;
1772
- if (!isRecord8(value)) return false;
2195
+ if (!isRecord9(value)) return false;
1773
2196
  if (typeof value.message !== "string") return false;
1774
2197
  if ("stack" in value && value.stack !== void 0) {
1775
2198
  if (typeof value.stack !== "string") return false;
@@ -1777,7 +2200,7 @@ function optionalErrorInfo(value) {
1777
2200
  return true;
1778
2201
  }
1779
2202
  function validateEvent(event) {
1780
- if (!isRecord8(event)) return false;
2203
+ if (!isRecord9(event)) return false;
1781
2204
  if (event.schemaVersion !== "0.1") return false;
1782
2205
  if (!finiteNumber(event.timestamp)) return false;
1783
2206
  if (typeof event.event !== "string") return false;
@@ -1786,7 +2209,7 @@ function validateEvent(event) {
1786
2209
  if (!nonEmptyString(event.runId) || !nonEmptyString(event.name) || !finiteNumber(event.startTime)) {
1787
2210
  return false;
1788
2211
  }
1789
- if (event.metadata !== void 0 && !isRecord8(event.metadata)) {
2212
+ if (event.metadata !== void 0 && !isRecord9(event.metadata)) {
1790
2213
  return false;
1791
2214
  }
1792
2215
  return true;
@@ -1801,7 +2224,7 @@ function validateEvent(event) {
1801
2224
  if (event.parentId !== void 0 && typeof event.parentId !== "string") {
1802
2225
  return false;
1803
2226
  }
1804
- if (event.metadata !== void 0 && !isRecord8(event.metadata)) {
2227
+ if (event.metadata !== void 0 && !isRecord9(event.metadata)) {
1805
2228
  return false;
1806
2229
  }
1807
2230
  return true;
@@ -1859,27 +2282,15 @@ async function writeTraceEvent(event, traceDir) {
1859
2282
  }
1860
2283
  warn("Failed to append trace event to fallback directory");
1861
2284
  }
1862
- async function readTraceFile(runId, traceDir) {
2285
+ async function readTraceEventsFromFile(filePath) {
1863
2286
  try {
1864
- const filePath = getTraceFilePath(runId, traceDir);
1865
- return await promises.readFile(filePath, "utf-8");
2287
+ const raw = await promises.readFile(filePath, "utf-8");
2288
+ return parseTraceJsonl(raw, { validate: validateEvent }).events;
1866
2289
  } catch (e) {
1867
2290
  if (e && typeof e === "object" && "code" in e && e.code === "ENOENT") {
1868
- return void 0;
1869
- }
1870
- warn("Unexpected error reading trace file", e);
1871
- return void 0;
1872
- }
1873
- }
1874
- async function readTraceEvents(runId, traceDir) {
1875
- try {
1876
- const raw = await readTraceFile(runId, traceDir);
1877
- if (raw === void 0) {
1878
2291
  return [];
1879
2292
  }
1880
- return parseTraceJsonl(raw, { validate: validateEvent }).events;
1881
- } catch (e) {
1882
- warn("Failed to read trace events", e);
2293
+ warn("Failed to read trace events from file", e);
1883
2294
  return [];
1884
2295
  }
1885
2296
  }
@@ -1888,7 +2299,7 @@ async function readTraceEvents(runId, traceDir) {
1888
2299
  var DEFAULT_MAX_METADATA_VALUE_LENGTH = 2e3;
1889
2300
  var DEFAULT_MAX_PREVIEW_LENGTH = 500;
1890
2301
  var DEFAULT_MAX_EVENT_BYTES = 65536;
1891
- function isRecord9(value) {
2302
+ function isRecord10(value) {
1892
2303
  return typeof value === "object" && value !== null && !Array.isArray(value);
1893
2304
  }
1894
2305
  function isPreviewKey2(key) {
@@ -1908,21 +2319,10 @@ function resolveTraceSafetyOptions(options) {
1908
2319
  {
1909
2320
  redactEnabled = true;
1910
2321
  }
1911
- const profile = options?.redactionProfile ?? "local";
2322
+ const profile = "local";
1912
2323
  const resolvedProfile = resolveRedactionProfile(profile);
1913
- const userMaxMetadata = "undefined" === "number" && Number.isFinite(options.maxMetadataValueLength) && options.maxMetadataValueLength >= 0 ? Math.floor(options.maxMetadataValueLength) : void 0;
1914
- const userMaxPreview = "undefined" === "number" && Number.isFinite(options.maxPreviewLength) && options.maxPreviewLength >= 0 ? Math.floor(options.maxPreviewLength) : void 0;
1915
- let maxMetadataValueLength = userMaxMetadata ?? DEFAULT_MAX_METADATA_VALUE_LENGTH;
1916
- let maxPreviewLength = userMaxPreview ?? DEFAULT_MAX_PREVIEW_LENGTH;
1917
- if (redactEnabled && profile !== "local") {
1918
- const capped = applyProfileMetadataCaps(
1919
- maxMetadataValueLength,
1920
- maxPreviewLength,
1921
- resolvedProfile
1922
- );
1923
- maxMetadataValueLength = capped.maxMetadataValueLength;
1924
- maxPreviewLength = capped.maxPreviewLength;
1925
- }
2324
+ let maxMetadataValueLength = DEFAULT_MAX_METADATA_VALUE_LENGTH;
2325
+ let maxPreviewLength = DEFAULT_MAX_PREVIEW_LENGTH;
1926
2326
  return {
1927
2327
  redactEnabled,
1928
2328
  redactionRules,
@@ -1930,11 +2330,26 @@ function resolveTraceSafetyOptions(options) {
1930
2330
  profileExtraKeys: redactEnabled ? resolvedProfile.extraKeys : [],
1931
2331
  maxMetadataValueLength,
1932
2332
  maxPreviewLength,
1933
- maxEventBytes: "undefined" === "number" && Number.isFinite(options.maxEventBytes) && options.maxEventBytes > 0 ? Math.floor(options.maxEventBytes) : DEFAULT_MAX_EVENT_BYTES
2333
+ maxEventBytes: DEFAULT_MAX_EVENT_BYTES
1934
2334
  };
1935
2335
  }
1936
2336
  function boundMetadataValue(key, value, opts, seen, depth) {
1937
2337
  if (depth > 32) return "[MaxDepth]";
2338
+ if (typeof value === "bigint") {
2339
+ return `${value.toString()}n`;
2340
+ }
2341
+ if (typeof value === "function") {
2342
+ return "[Function]";
2343
+ }
2344
+ if (typeof value === "symbol") {
2345
+ return "[Symbol]";
2346
+ }
2347
+ if (typeof value === "number" && !Number.isFinite(value)) {
2348
+ return String(value);
2349
+ }
2350
+ if (value === void 0) {
2351
+ return null;
2352
+ }
1938
2353
  if (value === null || typeof value !== "object") {
1939
2354
  if (typeof value === "string") {
1940
2355
  const max = isPreviewKey2(key) ? opts.maxPreviewLength : opts.maxMetadataValueLength;
@@ -1956,8 +2371,12 @@ function boundMetadataValue(key, value, opts, seen, depth) {
1956
2371
  }
1957
2372
  const record = value;
1958
2373
  const out = {};
1959
- for (const [k, v] of Object.entries(record)) {
1960
- out[k] = boundMetadataValue(k, v, opts, seen, depth + 1);
2374
+ try {
2375
+ for (const [k, v] of Object.entries(record)) {
2376
+ out[k] = boundMetadataValue(k, v, opts, seen, depth + 1);
2377
+ }
2378
+ } catch {
2379
+ return { truncated: true, reason: "metadataEnumerationFailed" };
1961
2380
  }
1962
2381
  return out;
1963
2382
  }
@@ -1971,16 +2390,25 @@ function redactMetadata(metadata, opts) {
1971
2390
  }
1972
2391
  function prepareMetadataForDisk(metadata, opts) {
1973
2392
  try {
1974
- const redacted = redactMetadata(metadata, opts);
1975
- const seen = /* @__PURE__ */ new WeakSet();
2393
+ const preBounded = boundMetadataValue(
2394
+ "metadata",
2395
+ metadata,
2396
+ opts,
2397
+ /* @__PURE__ */ new WeakSet(),
2398
+ 0
2399
+ );
2400
+ const redacted = redactMetadata(
2401
+ isRecord10(preBounded) ? preBounded : {},
2402
+ opts
2403
+ );
1976
2404
  const bounded = boundMetadataValue(
1977
2405
  "metadata",
1978
2406
  redacted,
1979
2407
  opts,
1980
- seen,
2408
+ /* @__PURE__ */ new WeakSet(),
1981
2409
  0
1982
2410
  );
1983
- return isRecord9(bounded) ? bounded : {};
2411
+ return isRecord10(bounded) ? bounded : {};
1984
2412
  } catch {
1985
2413
  return { truncated: true, reason: "metadataPreparationFailed" };
1986
2414
  }
@@ -2230,12 +2658,10 @@ var TraceDirectory = class {
2230
2658
  function isFiniteNumber2(v) {
2231
2659
  return typeof v === "number" && Number.isFinite(v);
2232
2660
  }
2233
- function safeParseJson(line) {
2234
- try {
2235
- return JSON.parse(line);
2236
- } catch {
2237
- return void 0;
2238
- }
2661
+ function parseIsoToMs3(value) {
2662
+ if (value === void 0) return void 0;
2663
+ const parsed = Date.parse(value);
2664
+ return Number.isFinite(parsed) ? parsed : void 0;
2239
2665
  }
2240
2666
  async function extractMetadata(filePath, _quickScan) {
2241
2667
  const stats = await promises.stat(filePath);
@@ -2244,8 +2670,7 @@ async function extractMetadata(filePath, _quickScan) {
2244
2670
  runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
2245
2671
  }
2246
2672
  const raw = await promises.readFile(filePath, "utf-8");
2247
- const lines = raw.split(/\r?\n/);
2248
- let eventCount = 0;
2673
+ const parsedTrace = parseTraceJsonl(raw, { warnings: false });
2249
2674
  let runId;
2250
2675
  let name;
2251
2676
  let startedAt;
@@ -2255,16 +2680,32 @@ async function extractMetadata(filePath, _quickScan) {
2255
2680
  let hasRunCompleted = false;
2256
2681
  let runCompletedStatus;
2257
2682
  let anyStepError = false;
2258
- let anyKnownEvent = false;
2259
- for (const line of lines) {
2260
- const trimmed = line.trim();
2261
- if (trimmed === "") continue;
2262
- const parsed = safeParseJson(trimmed);
2263
- if (!parsed) continue;
2264
- if (!isTraceEvent(parsed)) continue;
2265
- const e = parsed;
2266
- anyKnownEvent = true;
2267
- eventCount += 1;
2683
+ const anyKnownEvent = parsedTrace.sourceEventCount > 0;
2684
+ let persistedStatus;
2685
+ const persistedRun = parsedTrace.persisted.find(
2686
+ (event) => event.kind === "RUN"
2687
+ );
2688
+ if (persistedRun) {
2689
+ runId = persistedRun.runId;
2690
+ if (persistedRun.name.trim() !== "") {
2691
+ name = persistedRun.name;
2692
+ }
2693
+ startedAt = parseIsoToMs3(persistedRun.startedAt) ?? parseIsoToMs3(persistedRun.timestamp);
2694
+ endedAt = parseIsoToMs3(persistedRun.endedAt);
2695
+ if (isFiniteNumber2(persistedRun.durationMs)) {
2696
+ explicitDurationMs = persistedRun.durationMs;
2697
+ if (endedAt === void 0 && startedAt !== void 0) {
2698
+ endedAt = startedAt + persistedRun.durationMs;
2699
+ }
2700
+ }
2701
+ if (persistedRun.status === "ok") persistedStatus = "success";
2702
+ else if (persistedRun.status === "error") persistedStatus = "error";
2703
+ else if (persistedRun.status === "running") persistedStatus = "running";
2704
+ else if (persistedRun.status === "unknown") persistedStatus = "unknown";
2705
+ } else {
2706
+ runId = parsedTrace.persisted[0]?.runId;
2707
+ }
2708
+ for (const e of parsedTrace.events) {
2268
2709
  if (runId === void 0 && typeof e.runId === "string") {
2269
2710
  runId = e.runId;
2270
2711
  }
@@ -2301,6 +2742,8 @@ async function extractMetadata(filePath, _quickScan) {
2301
2742
  status = runCompletedStatus;
2302
2743
  } else if (anyStepError) {
2303
2744
  status = "error";
2745
+ } else if (persistedStatus !== void 0) {
2746
+ status = persistedStatus;
2304
2747
  } else if (hasRunStarted && !hasRunCompleted) {
2305
2748
  status = "running";
2306
2749
  } else if (anyKnownEvent) {
@@ -2316,12 +2759,15 @@ async function extractMetadata(filePath, _quickScan) {
2316
2759
  startedAt,
2317
2760
  endedAt,
2318
2761
  durationMs,
2319
- eventCount,
2762
+ eventCount: parsedTrace.sourceEventCount,
2320
2763
  filePath,
2321
2764
  fileSize: stats.size,
2322
2765
  createdAt: stats.birthtime
2323
2766
  };
2324
2767
  }
2768
+ function isNonNegativeFiniteNumber(value) {
2769
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
2770
+ }
2325
2771
  function buildRunSummary(events) {
2326
2772
  const started = events.find(
2327
2773
  (e) => e.event === "run_started"
@@ -2344,8 +2790,10 @@ function buildRunSummary(events) {
2344
2790
  name: s.name,
2345
2791
  status: "running",
2346
2792
  parentId: s.parentId,
2347
- tokensInput: typeof s.metadata?.tokens?.input === "number" ? s.metadata.tokens.input : void 0,
2348
- tokensOutput: typeof s.metadata?.tokens?.output === "number" ? s.metadata.tokens.output : void 0
2793
+ tokensInput: isNonNegativeFiniteNumber(s.metadata?.tokens?.input) ? s.metadata.tokens.input : void 0,
2794
+ tokensOutput: isNonNegativeFiniteNumber(s.metadata?.tokens?.output) ? s.metadata.tokens.output : void 0,
2795
+ tokensTotal: isNonNegativeFiniteNumber(s.metadata?.tokens?.total) ? s.metadata.tokens.total : void 0,
2796
+ tokensCached: isNonNegativeFiniteNumber(s.metadata?.tokens?.cached) ? s.metadata.tokens.cached : void 0
2349
2797
  });
2350
2798
  }
2351
2799
  }
@@ -2367,7 +2815,11 @@ function buildRunSummary(events) {
2367
2815
  let longestStep;
2368
2816
  let totalTokensInput = 0;
2369
2817
  let totalTokensOutput = 0;
2370
- let hasAnyTokens = false;
2818
+ let totalTokensTotal = 0;
2819
+ let totalTokensCached = 0;
2820
+ let tokenBearingSteps = 0;
2821
+ let stepsWithKnownTotal = 0;
2822
+ let hasCachedTokens = false;
2371
2823
  const depthCache = /* @__PURE__ */ new Map();
2372
2824
  const computeDepth = (stepId) => {
2373
2825
  const cached = depthCache.get(stepId);
@@ -2396,10 +2848,21 @@ function buildRunSummary(events) {
2396
2848
  longestStep = { name: s.name, durationMs: s.durationMs, type: s.type };
2397
2849
  }
2398
2850
  }
2399
- if (typeof s.tokensInput === "number" || typeof s.tokensOutput === "number") {
2400
- hasAnyTokens = true;
2401
- if (typeof s.tokensInput === "number") totalTokensInput += s.tokensInput;
2402
- if (typeof s.tokensOutput === "number") totalTokensOutput += s.tokensOutput;
2851
+ if (s.tokensInput !== void 0 || s.tokensOutput !== void 0 || s.tokensTotal !== void 0 || s.tokensCached !== void 0) {
2852
+ tokenBearingSteps += 1;
2853
+ if (s.tokensInput !== void 0) totalTokensInput += s.tokensInput;
2854
+ if (s.tokensOutput !== void 0) totalTokensOutput += s.tokensOutput;
2855
+ if (s.tokensTotal !== void 0) {
2856
+ totalTokensTotal += s.tokensTotal;
2857
+ stepsWithKnownTotal += 1;
2858
+ } else if (s.tokensInput !== void 0 && s.tokensOutput !== void 0) {
2859
+ totalTokensTotal += s.tokensInput + s.tokensOutput;
2860
+ stepsWithKnownTotal += 1;
2861
+ }
2862
+ if (s.tokensCached !== void 0) {
2863
+ totalTokensCached += s.tokensCached;
2864
+ hasCachedTokens = true;
2865
+ }
2403
2866
  }
2404
2867
  }
2405
2868
  const summary = {
@@ -2414,7 +2877,14 @@ function buildRunSummary(events) {
2414
2877
  errorSteps,
2415
2878
  maxDepth,
2416
2879
  ...longestStep ? { longestStep } : {},
2417
- ...hasAnyTokens ? { totalTokens: { input: totalTokensInput, output: totalTokensOutput } } : {}
2880
+ ...tokenBearingSteps > 0 ? {
2881
+ totalTokens: {
2882
+ input: totalTokensInput,
2883
+ output: totalTokensOutput,
2884
+ ...stepsWithKnownTotal === tokenBearingSteps ? { total: totalTokensTotal } : {},
2885
+ ...hasCachedTokens ? { cached: totalTokensCached } : {}
2886
+ }
2887
+ } : {}
2418
2888
  };
2419
2889
  return summary;
2420
2890
  }
@@ -2719,9 +3189,17 @@ function renderRunWhat(summary, options = {}) {
2719
3189
  `Status: ${summary.status} \xB7 Duration: ${duration} \xB7 Steps: ${summary.totalSteps} (${stepMixLine(summary)})`
2720
3190
  );
2721
3191
  if (summary.totalTokens) {
2722
- lines.push(
2723
- `Tokens: ${summary.totalTokens.input} in / ${summary.totalTokens.output} out`
2724
- );
3192
+ const tokenParts = [
3193
+ `${summary.totalTokens.input} in`,
3194
+ `${summary.totalTokens.output} out`
3195
+ ];
3196
+ if (summary.totalTokens.total !== void 0) {
3197
+ tokenParts.push(`${summary.totalTokens.total} total`);
3198
+ }
3199
+ if (summary.totalTokens.cached !== void 0) {
3200
+ tokenParts.push(`${summary.totalTokens.cached} cached`);
3201
+ }
3202
+ lines.push(`Tokens: ${tokenParts.join(" / ")}`);
2725
3203
  }
2726
3204
  if (showCorrelation && summary.correlation) {
2727
3205
  const parts = [];
@@ -2801,7 +3279,7 @@ function stableJson(value, pretty) {
2801
3279
  const sorted = sortKeysDeep(value);
2802
3280
  return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
2803
3281
  }
2804
- function compactAttributes(attrs, options) {
3282
+ function compactAttributes3(attrs, options) {
2805
3283
  if (attrs === void 0) return {};
2806
3284
  const maxLen = options?.maxLength ?? 500;
2807
3285
  const redacted = options?.redacted ?? true;
@@ -2946,7 +3424,7 @@ function exportHtml(tree, options) {
2946
3424
  attrsHtml += "<h2>Attributes (bounded)</h2>";
2947
3425
  for (const n of flat) {
2948
3426
  if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
2949
- const compact = compactAttributes(n.event.attributes, {
3427
+ const compact = compactAttributes3(n.event.attributes, {
2950
3428
  maxLength: maxLen,
2951
3429
  redacted
2952
3430
  });
@@ -3097,7 +3575,7 @@ function exportMarkdown(tree, options) {
3097
3575
  lines.push("");
3098
3576
  for (const n of flat) {
3099
3577
  if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
3100
- const compact = compactAttributes(n.event.attributes, {
3578
+ const compact = compactAttributes3(n.event.attributes, {
3101
3579
  maxLength: maxLen,
3102
3580
  redacted
3103
3581
  });
@@ -3260,7 +3738,7 @@ function manualTraceEventsToRunTree(events) {
3260
3738
  }
3261
3739
 
3262
3740
  // packages/core/src/exporters/redact-export.ts
3263
- function isRecord10(value) {
3741
+ function isRecord11(value) {
3264
3742
  return typeof value === "object" && value !== null && !Array.isArray(value);
3265
3743
  }
3266
3744
  function deepClone(value) {
@@ -3334,7 +3812,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
3334
3812
  0
3335
3813
  );
3336
3814
  const err = bounded.error;
3337
- if (isRecord10(err) && typeof err.message === "string") {
3815
+ if (isRecord11(err) && typeof err.message === "string") {
3338
3816
  bounded.error = {
3339
3817
  ...err,
3340
3818
  message: truncateStringForProfile(
@@ -3355,6 +3833,89 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
3355
3833
  }
3356
3834
  return bounded;
3357
3835
  }
3836
+ function redactErrorInfo(error, redactor, maxMetadataValueLength, maxPreviewLength) {
3837
+ if (error === void 0) return void 0;
3838
+ const record = redactEventAttributes(
3839
+ { error },
3840
+ redactor,
3841
+ maxMetadataValueLength,
3842
+ maxPreviewLength
3843
+ );
3844
+ const redacted = record?.error;
3845
+ if (!isRecord11(redacted) || typeof redacted.message !== "string") {
3846
+ return void 0;
3847
+ }
3848
+ return {
3849
+ message: redacted.message,
3850
+ ...typeof redacted.stack === "string" ? { stack: redacted.stack } : {}
3851
+ };
3852
+ }
3853
+ function redactTraceEventsForReport(events, options) {
3854
+ const profile = options?.redactionProfile ?? "local";
3855
+ if (profile === "local") {
3856
+ return deepClone(events);
3857
+ }
3858
+ const resolved = resolveRedactionProfile(profile);
3859
+ const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
3860
+ 2e3,
3861
+ 500,
3862
+ resolved
3863
+ );
3864
+ const redactor = new Redactor({ extraKeys: resolved.extraKeys });
3865
+ return events.map((event) => {
3866
+ switch (event.event) {
3867
+ case "run_started":
3868
+ return {
3869
+ ...event,
3870
+ name: truncateStringForProfile(
3871
+ event.name,
3872
+ "name",
3873
+ maxMetadataValueLength,
3874
+ maxPreviewLength
3875
+ ),
3876
+ ...event.metadata !== void 0 ? {
3877
+ metadata: redactEventAttributes(
3878
+ event.metadata,
3879
+ redactor,
3880
+ maxMetadataValueLength,
3881
+ maxPreviewLength
3882
+ )
3883
+ } : {}
3884
+ };
3885
+ case "step_started":
3886
+ return {
3887
+ ...event,
3888
+ name: truncateStringForProfile(
3889
+ event.name,
3890
+ "name",
3891
+ maxMetadataValueLength,
3892
+ maxPreviewLength
3893
+ ),
3894
+ ...event.metadata !== void 0 ? {
3895
+ metadata: redactEventAttributes(
3896
+ event.metadata,
3897
+ redactor,
3898
+ maxMetadataValueLength,
3899
+ maxPreviewLength
3900
+ )
3901
+ } : {}
3902
+ };
3903
+ case "run_completed":
3904
+ case "step_completed":
3905
+ return {
3906
+ ...event,
3907
+ ...event.error !== void 0 ? {
3908
+ error: redactErrorInfo(
3909
+ event.error,
3910
+ redactor,
3911
+ maxMetadataValueLength,
3912
+ maxPreviewLength
3913
+ )
3914
+ } : {}
3915
+ };
3916
+ }
3917
+ });
3918
+ }
3358
3919
  function redactRunTreeForExport(tree, options) {
3359
3920
  const profile = options?.redactionProfile ?? "local";
3360
3921
  if (profile === "local") {
@@ -3418,12 +3979,15 @@ footer{margin-top:2rem;font-size:0.85rem;color:#555}
3418
3979
  `.trim();
3419
3980
  function buildRunReport(events, options) {
3420
3981
  const profile = options.redactionProfile ?? "local";
3421
- const whatSummary = buildRunWhatSummary(events);
3982
+ const safeEvents = redactTraceEventsForReport(events, {
3983
+ redactionProfile: profile
3984
+ });
3985
+ const whatSummary = buildRunWhatSummary(safeEvents);
3422
3986
  const whatText = renderRunWhat(whatSummary, {
3423
3987
  correlation: options.correlation !== false
3424
3988
  });
3425
- const timelineText = renderTimeline(buildRunTimeline(events));
3426
- const tree = resolveTree(events, profile);
3989
+ const timelineText = renderTimeline(buildRunTimeline(safeEvents));
3990
+ const tree = resolveTree(safeEvents, profile);
3427
3991
  const exportOpts = {
3428
3992
  includeMetadata: false,
3429
3993
  includeAttributes: options.includeAttributes === true,
@@ -3504,6 +4068,8 @@ ${attrsSection}
3504
4068
  fileExtension: ".html"
3505
4069
  };
3506
4070
  }
4071
+
4072
+ // packages/core/src/stats.ts
3507
4073
  function percentile(sorted, p) {
3508
4074
  if (sorted.length === 0) return void 0;
3509
4075
  const idx = Math.min(
@@ -3514,19 +4080,10 @@ function percentile(sorted, p) {
3514
4080
  }
3515
4081
  async function readRunStartedMetadata(filePath) {
3516
4082
  try {
3517
- const raw = await promises.readFile(filePath, "utf-8");
3518
- for (const line of raw.split(/\r?\n/)) {
3519
- const trimmed = line.trim();
3520
- if (trimmed === "") continue;
3521
- let parsed;
3522
- try {
3523
- parsed = JSON.parse(trimmed);
3524
- } catch {
3525
- continue;
3526
- }
3527
- if (!isTraceEvent(parsed)) continue;
3528
- if (parsed.event !== "run_started") continue;
3529
- const rs = parsed;
4083
+ const events = await readTraceEventsFromFile(filePath);
4084
+ for (const event of events) {
4085
+ if (event.event !== "run_started") continue;
4086
+ const rs = event;
3530
4087
  if (rs.metadata && typeof rs.metadata === "object") {
3531
4088
  return rs.metadata;
3532
4089
  }
@@ -3585,7 +4142,7 @@ async function buildTraceStats(metas, options) {
3585
4142
  });
3586
4143
  }
3587
4144
  try {
3588
- const events = await readTraceEvents(m.runId, options.traceDir);
4145
+ const events = await readTraceEventsFromFile(m.filePath);
3589
4146
  if (events.length === 0) continue;
3590
4147
  const summary = buildRunSummary(events);
3591
4148
  totalSteps += summary.totalSteps;
@@ -3773,7 +4330,7 @@ async function searchTraces(metas, options) {
3773
4330
  if (options.status && m.status !== options.status) continue;
3774
4331
  let events = [];
3775
4332
  try {
3776
- events = await readTraceEvents(m.runId, options.traceDir);
4333
+ events = await readTraceEventsFromFile(m.filePath);
3777
4334
  } catch {
3778
4335
  continue;
3779
4336
  }
@@ -3909,7 +4466,7 @@ var KNOWN_EVENTS = /* @__PURE__ */ new Set([
3909
4466
  "step_started",
3910
4467
  "step_completed"
3911
4468
  ]);
3912
- function isRecord11(value) {
4469
+ function isRecord12(value) {
3913
4470
  return typeof value === "object" && value !== null && !Array.isArray(value);
3914
4471
  }
3915
4472
  function safeParse(line) {
@@ -3931,7 +4488,7 @@ async function isAgentInspectTrace(filePath) {
3931
4488
  if (trimmed === "") continue;
3932
4489
  const parsed = safeParse(trimmed);
3933
4490
  if (!parsed) continue;
3934
- if (!isRecord11(parsed)) continue;
4491
+ if (!isRecord12(parsed)) continue;
3935
4492
  checked += 1;
3936
4493
  if (isTraceEvent(parsed)) return true;
3937
4494
  const ev = parsed.event;
@@ -3946,9 +4503,1572 @@ async function isAgentInspectTrace(filePath) {
3946
4503
  return false;
3947
4504
  }
3948
4505
  }
3949
-
3950
- // packages/core/src/diff/comparable.ts
3951
- function extractOutputPreview(meta) {
4506
+ resolveTraceSafetyOptions();
4507
+ var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
4508
+ var MIN_DETECTION_CONFIDENCE = 0.5;
4509
+ var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
4510
+ var resolvedInputCache = /* @__PURE__ */ new WeakMap();
4511
+ var OPENINFERENCE_READER_FORMAT = "openinference-json";
4512
+ var OTLP_READER_FORMAT = "otlp-json";
4513
+ var OPENINFERENCE_SPAN_KEYS = /* @__PURE__ */ new Set([
4514
+ "trace_id",
4515
+ "traceId",
4516
+ "span_id",
4517
+ "spanId",
4518
+ "parent_span_id",
4519
+ "parentSpanId",
4520
+ "name",
4521
+ "start_time_unix_nano",
4522
+ "startTimeUnixNano",
4523
+ "end_time_unix_nano",
4524
+ "endTimeUnixNano",
4525
+ "start_time",
4526
+ "startTime",
4527
+ "end_time",
4528
+ "endTime",
4529
+ "attributes",
4530
+ "status",
4531
+ "kind",
4532
+ "span_kind",
4533
+ "spanKind"
4534
+ ]);
4535
+ var OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS = [
4536
+ "input.value",
4537
+ "output.value",
4538
+ "input.mime_type",
4539
+ "output.mime_type",
4540
+ "llm.input_messages",
4541
+ "llm.output_messages",
4542
+ "llm.prompts",
4543
+ "llm.completions",
4544
+ "retrieval.documents",
4545
+ "reranker.input_documents",
4546
+ "reranker.output_documents",
4547
+ "document.content",
4548
+ "gen_ai.prompt",
4549
+ "gen_ai.completion",
4550
+ "gen_ai.input.messages",
4551
+ "gen_ai.output.messages"
4552
+ ];
4553
+ var OTLP_SPAN_KEYS = /* @__PURE__ */ new Set([
4554
+ "traceId",
4555
+ "spanId",
4556
+ "parentSpanId",
4557
+ "name",
4558
+ "kind",
4559
+ "startTimeUnixNano",
4560
+ "endTimeUnixNano",
4561
+ "attributes",
4562
+ "events",
4563
+ "status",
4564
+ "droppedAttributesCount",
4565
+ "droppedEventsCount",
4566
+ "droppedLinksCount",
4567
+ "links",
4568
+ "flags"
4569
+ ]);
4570
+ var TraceReadError = class extends Error {
4571
+ code;
4572
+ warnings;
4573
+ constructor(code, message, warnings = []) {
4574
+ super(message);
4575
+ this.name = "TraceReadError";
4576
+ this.code = code;
4577
+ this.warnings = warnings;
4578
+ }
4579
+ };
4580
+ function normalizeCandidate(reader, candidate) {
4581
+ const confidence = Number.isFinite(candidate.confidence) ? Math.max(0, Math.min(1, candidate.confidence)) : 0;
4582
+ return {
4583
+ ...candidate,
4584
+ format: candidate.format || reader.format,
4585
+ confidence,
4586
+ readerName: candidate.readerName ?? reader.name
4587
+ };
4588
+ }
4589
+ function sortCandidates(candidates) {
4590
+ return [...candidates].sort((a, b) => {
4591
+ if (b.confidence !== a.confidence) return b.confidence - a.confidence;
4592
+ return a.format.localeCompare(b.format);
4593
+ });
4594
+ }
4595
+ function collectWarnings(candidates) {
4596
+ return candidates.flatMap((candidate) => candidate.warnings ?? []);
4597
+ }
4598
+ function dedupeWarnings(warnings) {
4599
+ const seen = /* @__PURE__ */ new Set();
4600
+ const out = [];
4601
+ for (const warning of warnings) {
4602
+ const key = [
4603
+ warning.code,
4604
+ warning.message,
4605
+ warning.severity ?? "",
4606
+ warning.sourceFile ?? "",
4607
+ warning.line ?? "",
4608
+ warning.field ?? ""
4609
+ ].join("\0");
4610
+ if (seen.has(key)) continue;
4611
+ seen.add(key);
4612
+ out.push(warning);
4613
+ }
4614
+ return out;
4615
+ }
4616
+ function attachSingleSourceFile(warnings, resolved) {
4617
+ if (resolved.sourceFiles.length !== 1) return [...warnings];
4618
+ const [sourceFile] = resolved.sourceFiles;
4619
+ return warnings.map((warning) => ({
4620
+ ...warning,
4621
+ sourceFile: warning.sourceFile ?? sourceFile
4622
+ }));
4623
+ }
4624
+ function findReaderByFormat(format, readers) {
4625
+ return readers.find((reader) => reader.format === format);
4626
+ }
4627
+ async function jsonlFilesInDirectory(dirPath) {
4628
+ const entries = await promises.readdir(dirPath, { withFileTypes: true });
4629
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
4630
+ }
4631
+ async function resolveInput(input3) {
4632
+ const cached = resolvedInputCache.get(input3);
4633
+ if (cached) return cached;
4634
+ const promise = resolveInputUncached(input3);
4635
+ resolvedInputCache.set(input3, promise);
4636
+ return promise;
4637
+ }
4638
+ function assertInputWithinBounds(content, sourceFile) {
4639
+ const bytes = Buffer.byteLength(content, "utf8");
4640
+ if (bytes <= DEFAULT_MAX_TRACE_INPUT_BYTES) return;
4641
+ throw new TraceReadError("unsupported_format", "Trace input exceeds the local reader size limit.", [
4642
+ {
4643
+ code: "input_too_large",
4644
+ message: `Trace input is ${bytes} bytes; max is ${DEFAULT_MAX_TRACE_INPUT_BYTES} bytes.`,
4645
+ severity: "error",
4646
+ ...sourceFile !== void 0 ? { sourceFile } : {}
4647
+ }
4648
+ ]);
4649
+ }
4650
+ async function resolveInputUncached(input3) {
4651
+ if (input3.type === "string") {
4652
+ assertInputWithinBounds(input3.content);
4653
+ return { content: input3.content, sourceFiles: [] };
4654
+ }
4655
+ if (input3.type === "buffer") {
4656
+ const content = input3.content.toString("utf-8");
4657
+ assertInputWithinBounds(content);
4658
+ return { content, sourceFiles: [] };
4659
+ }
4660
+ if (input3.type === "file") {
4661
+ const content = await promises.readFile(input3.path, "utf-8");
4662
+ assertInputWithinBounds(content, input3.path);
4663
+ return { content, sourceFiles: [input3.path] };
4664
+ }
4665
+ if (input3.type === "directory") {
4666
+ const files = await jsonlFilesInDirectory(input3.path);
4667
+ const parts = await Promise.all(
4668
+ files.map(async (file) => (await promises.readFile(file, "utf-8")).trimEnd())
4669
+ );
4670
+ const content = parts.filter((part) => part.trim() !== "").join("\n");
4671
+ assertInputWithinBounds(content, input3.path);
4672
+ return {
4673
+ content,
4674
+ sourceFiles: files
4675
+ };
4676
+ }
4677
+ return void 0;
4678
+ }
4679
+ function detectJsonlFormat(content) {
4680
+ let saw01 = false;
4681
+ let saw02 = false;
4682
+ let validRows = 0;
4683
+ let invalidJsonRows = 0;
4684
+ let unknownSchemaRows = 0;
4685
+ let firstInvalidJsonLine;
4686
+ let firstUnknownSchemaLine;
4687
+ let lineNumber = 0;
4688
+ for (const line of content.split(/\r?\n/)) {
4689
+ lineNumber += 1;
4690
+ const trimmed = line.trim();
4691
+ if (trimmed === "") continue;
4692
+ let parsed;
4693
+ try {
4694
+ parsed = JSON.parse(trimmed);
4695
+ } catch {
4696
+ invalidJsonRows += 1;
4697
+ firstInvalidJsonLine ??= lineNumber;
4698
+ continue;
4699
+ }
4700
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "schemaVersion" in parsed) {
4701
+ const version2 = parsed.schemaVersion;
4702
+ if (version2 === "0.1") {
4703
+ saw01 = true;
4704
+ validRows += 1;
4705
+ continue;
4706
+ }
4707
+ if (version2 === "0.2") {
4708
+ saw02 = true;
4709
+ validRows += 1;
4710
+ continue;
4711
+ }
4712
+ }
4713
+ unknownSchemaRows += 1;
4714
+ firstUnknownSchemaLine ??= lineNumber;
4715
+ }
4716
+ const warnings = [];
4717
+ if (invalidJsonRows > 0) {
4718
+ warnings.push({
4719
+ code: "invalid_jsonl_rows",
4720
+ message: `Skipped ${invalidJsonRows} invalid JSONL row(s) during format detection.`,
4721
+ severity: "warning",
4722
+ ...firstInvalidJsonLine !== void 0 ? { line: firstInvalidJsonLine } : {}
4723
+ });
4724
+ }
4725
+ if (unknownSchemaRows > 0) {
4726
+ warnings.push({
4727
+ code: "unknown_schema_rows",
4728
+ message: `Skipped ${unknownSchemaRows} row(s) with unknown schemaVersion during format detection.`,
4729
+ severity: "warning",
4730
+ ...firstUnknownSchemaLine !== void 0 ? { line: firstUnknownSchemaLine } : {}
4731
+ });
4732
+ }
4733
+ let format = "empty";
4734
+ if (saw01 && saw02) format = "mixed";
4735
+ else if (saw01) format = "0.1";
4736
+ else if (saw02) format = "0.2";
4737
+ return { format, validRows, warnings };
4738
+ }
4739
+ function agentInspectFormatLabel(format) {
4740
+ switch (format) {
4741
+ case "0.1":
4742
+ return "agent-inspect-v0.1-jsonl";
4743
+ case "0.2":
4744
+ return "agent-inspect-v0.2-jsonl";
4745
+ case "mixed":
4746
+ return "agent-inspect-mixed-jsonl";
4747
+ default:
4748
+ return "agent-inspect-jsonl";
4749
+ }
4750
+ }
4751
+ function persistedEventsForParsedTrace(parsed) {
4752
+ if (parsed.format === "0.2" && parsed.persisted.length > 0) {
4753
+ return [...parsed.persisted];
4754
+ }
4755
+ if (parsed.format === "mixed" && parsed.rows.length > 0) {
4756
+ return parsed.rows.map((row, index) => {
4757
+ if (row.format === "0.2") return row.event;
4758
+ return traceEventToPersistedInspectEvent(row.event, {
4759
+ eventIndex: index,
4760
+ sourceName: "agent-inspect-jsonl-reader"
4761
+ });
4762
+ });
4763
+ }
4764
+ return traceEventsToPersistedInspectEvents(parsed.events, {
4765
+ sourceName: "agent-inspect-jsonl-reader"
4766
+ });
4767
+ }
4768
+ function isRecord13(value) {
4769
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4770
+ }
4771
+ function isNonEmptyString3(value) {
4772
+ return typeof value === "string" && value.trim() !== "";
4773
+ }
4774
+ function readStringField(record, keys) {
4775
+ for (const key of keys) {
4776
+ const value = record[key];
4777
+ if (isNonEmptyString3(value)) return value;
4778
+ }
4779
+ return void 0;
4780
+ }
4781
+ function readRecordField(record, key) {
4782
+ const value = record[key];
4783
+ return isRecord13(value) ? value : void 0;
4784
+ }
4785
+ function parseJsonDocument(content) {
4786
+ return JSON.parse(content);
4787
+ }
4788
+ function looksLikeOpenInferenceSpan(value) {
4789
+ if (!isRecord13(value)) return false;
4790
+ const attributes = readRecordField(value, "attributes");
4791
+ return readStringField(value, ["trace_id", "traceId"]) !== void 0 && readStringField(value, ["span_id", "spanId"]) !== void 0 && (readStringField(value, ["name"]) !== void 0 || attributes?.["openinference.span.kind"] !== void 0);
4792
+ }
4793
+ function extractOpenInferenceDocument(root) {
4794
+ const warnings = [];
4795
+ const unsupportedFields = [];
4796
+ if (Array.isArray(root)) {
4797
+ const spans = root.filter(looksLikeOpenInferenceSpan);
4798
+ if (spans.length === 0) return void 0;
4799
+ if (spans.length !== root.length) {
4800
+ warnings.push({
4801
+ code: "openinference_skipped_items",
4802
+ message: "Skipped non-span item(s) in OpenInference span array.",
4803
+ severity: "warning"
4804
+ });
4805
+ }
4806
+ return {
4807
+ spans,
4808
+ confidence: 0.82,
4809
+ description: "OpenInference span array",
4810
+ warnings,
4811
+ unsupportedFields
4812
+ };
4813
+ }
4814
+ if (!isRecord13(root)) return void 0;
4815
+ const rootFormat = root.format;
4816
+ const rootCompatibility = root.compatibility;
4817
+ const version2 = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
4818
+ if (Array.isArray(root.spans)) {
4819
+ const spans = root.spans.filter(looksLikeOpenInferenceSpan);
4820
+ if (spans.length === 0 && (rootFormat === "openinference" || rootCompatibility === "openinference-compatible")) {
4821
+ warnings.push({
4822
+ code: "openinference_no_valid_spans",
4823
+ message: "OpenInference document did not contain any valid spans.",
4824
+ severity: "error"
4825
+ });
4826
+ return {
4827
+ spans,
4828
+ confidence: 0.7,
4829
+ description: "Malformed OpenInference document",
4830
+ version: version2,
4831
+ warnings,
4832
+ unsupportedFields
4833
+ };
4834
+ }
4835
+ if (spans.length === 0) return void 0;
4836
+ if (spans.length !== root.spans.length) {
4837
+ warnings.push({
4838
+ code: "openinference_skipped_spans",
4839
+ message: "Skipped invalid OpenInference span item(s).",
4840
+ severity: "warning"
4841
+ });
4842
+ }
4843
+ return {
4844
+ spans,
4845
+ confidence: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? 0.9 : 0.84,
4846
+ description: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? "OpenInference document" : "OpenInference spans document",
4847
+ version: version2,
4848
+ warnings,
4849
+ unsupportedFields
4850
+ };
4851
+ }
4852
+ if (Array.isArray(root.data)) {
4853
+ const spans = root.data.filter(looksLikeOpenInferenceSpan);
4854
+ if (spans.length === 0) return void 0;
4855
+ if (spans.length !== root.data.length) {
4856
+ warnings.push({
4857
+ code: "openinference_skipped_data_items",
4858
+ message: "Skipped non-span item(s) in OpenInference data array.",
4859
+ severity: "warning"
4860
+ });
4861
+ }
4862
+ return {
4863
+ spans,
4864
+ confidence: 0.8,
4865
+ description: "OpenInference data document",
4866
+ version: version2,
4867
+ warnings,
4868
+ unsupportedFields
4869
+ };
4870
+ }
4871
+ if (looksLikeOpenInferenceSpan(root)) {
4872
+ return {
4873
+ spans: [root],
4874
+ confidence: 0.76,
4875
+ description: "OpenInference single span",
4876
+ version: version2,
4877
+ warnings,
4878
+ unsupportedFields
4879
+ };
4880
+ }
4881
+ if (rootFormat === "openinference" || rootCompatibility === "openinference-compatible") {
4882
+ warnings.push({
4883
+ code: "openinference_missing_spans",
4884
+ message: "OpenInference document is missing a spans array.",
4885
+ severity: "error"
4886
+ });
4887
+ return {
4888
+ spans: [],
4889
+ confidence: 0.7,
4890
+ description: "Malformed OpenInference document",
4891
+ version: version2,
4892
+ warnings,
4893
+ unsupportedFields
4894
+ };
4895
+ }
4896
+ return void 0;
4897
+ }
4898
+ function parseUnixNanoToIso(value) {
4899
+ if (typeof value === "bigint" && value >= 0n) {
4900
+ return new Date(Number(value / 1000000n)).toISOString();
4901
+ }
4902
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
4903
+ return new Date(Math.floor(value / 1e6)).toISOString();
4904
+ }
4905
+ if (typeof value === "string" && /^\d+$/.test(value)) {
4906
+ return new Date(Number(BigInt(value) / 1000000n)).toISOString();
4907
+ }
4908
+ return void 0;
4909
+ }
4910
+ function parseIsoTime(value) {
4911
+ if (!isNonEmptyString3(value)) return void 0;
4912
+ const ms = Date.parse(value);
4913
+ if (!Number.isFinite(ms)) return void 0;
4914
+ return new Date(ms).toISOString();
4915
+ }
4916
+ function readOpenInferenceTimestamp(span, nanoKeys, isoKeys) {
4917
+ for (const key of nanoKeys) {
4918
+ const iso = parseUnixNanoToIso(span[key]);
4919
+ if (iso !== void 0) return iso;
4920
+ }
4921
+ for (const key of isoKeys) {
4922
+ const iso = parseIsoTime(span[key]);
4923
+ if (iso !== void 0) return iso;
4924
+ }
4925
+ return void 0;
4926
+ }
4927
+ function durationBetweenIso(startedAt, endedAt) {
4928
+ if (startedAt === void 0 || endedAt === void 0) return void 0;
4929
+ const startMs = Date.parse(startedAt);
4930
+ const endMs = Date.parse(endedAt);
4931
+ if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs < startMs) {
4932
+ return void 0;
4933
+ }
4934
+ return endMs - startMs;
4935
+ }
4936
+ function isSensitiveOpenInferenceAttribute(key) {
4937
+ return OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS.some(
4938
+ (sensitiveKey) => key === sensitiveKey || key.startsWith(`${sensitiveKey}.`) || key.endsWith(".message.content") || key.endsWith(".document.content")
4939
+ );
4940
+ }
4941
+ function summarizeAttributeValue(value) {
4942
+ if (typeof value === "string") {
4943
+ return { type: "string", length: value.length };
4944
+ }
4945
+ if (typeof value === "number") {
4946
+ return { type: "number", finite: Number.isFinite(value) };
4947
+ }
4948
+ if (typeof value === "boolean") {
4949
+ return { type: "boolean" };
4950
+ }
4951
+ if (Array.isArray(value)) {
4952
+ return { type: "array", length: value.length };
4953
+ }
4954
+ if (isRecord13(value)) {
4955
+ return { type: "object", keyCount: Object.keys(value).length };
4956
+ }
4957
+ if (value === null) {
4958
+ return { type: "null" };
4959
+ }
4960
+ return { type: typeof value };
4961
+ }
4962
+ function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
4963
+ const out = {};
4964
+ const warnings = [];
4965
+ const unsupportedFields = [];
4966
+ const summarizedKeys = [];
4967
+ for (const [key, value] of Object.entries(attributes)) {
4968
+ if (isSensitiveOpenInferenceAttribute(key)) {
4969
+ summarizedKeys.push(key);
4970
+ out[`${key}.summary`] = summarizeAttributeValue(value);
4971
+ unsupportedFields.push(`${pathPrefix}.attributes.${key}`);
4972
+ continue;
4973
+ }
4974
+ out[key] = value;
4975
+ }
4976
+ if (summarizedKeys.length > 0) {
4977
+ out["openinference.summarized_attributes"] = summarizedKeys;
4978
+ warnings.push({
4979
+ code: "openinference_sensitive_attribute_summarized",
4980
+ message: "OpenInference prompt/output/document attribute(s) were summarized instead of copied verbatim.",
4981
+ severity: "warning"
4982
+ });
4983
+ }
4984
+ return { attributes: out, warnings, unsupportedFields };
4985
+ }
4986
+ function mapOpenInferenceKind(span, attributes, pathPrefix) {
4987
+ const warnings = [];
4988
+ const agentInspectKind = attributes["agent_inspect.kind"];
4989
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
4990
+ return { kind: agentInspectKind, warnings };
4991
+ }
4992
+ const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
4993
+ const normalized = rawKind?.toUpperCase();
4994
+ switch (normalized) {
4995
+ case "LLM":
4996
+ return { kind: "LLM", warnings };
4997
+ case "TOOL":
4998
+ return { kind: "TOOL", warnings };
4999
+ case "CHAIN":
5000
+ return { kind: "CHAIN", warnings };
5001
+ case "RETRIEVER":
5002
+ return { kind: "RETRIEVER", warnings };
5003
+ case "AGENT":
5004
+ return { kind: "AGENT", warnings };
5005
+ case "EMBEDDING":
5006
+ warnings.push({
5007
+ code: "openinference_kind_semantic_loss",
5008
+ message: "OpenInference EMBEDDING span kind mapped to AgentInspect LLM.",
5009
+ severity: "warning",
5010
+ field: `${pathPrefix}.attributes.openinference.span.kind`
5011
+ });
5012
+ return { kind: "LLM", warnings };
5013
+ case "RERANKER":
5014
+ warnings.push({
5015
+ code: "openinference_kind_semantic_loss",
5016
+ message: "OpenInference RERANKER span kind mapped to AgentInspect RETRIEVER.",
5017
+ severity: "warning",
5018
+ field: `${pathPrefix}.attributes.openinference.span.kind`
5019
+ });
5020
+ return { kind: "RETRIEVER", warnings };
5021
+ case "UNKNOWN":
5022
+ case void 0:
5023
+ warnings.push({
5024
+ code: "openinference_kind_unknown",
5025
+ message: "OpenInference span kind was missing or unknown; mapped to AgentInspect LOGIC.",
5026
+ severity: "warning",
5027
+ field: `${pathPrefix}.attributes.openinference.span.kind`
5028
+ });
5029
+ return { kind: "LOGIC", warnings };
5030
+ default:
5031
+ warnings.push({
5032
+ code: "openinference_kind_unsupported",
5033
+ message: `Unsupported OpenInference span kind "${rawKind}" mapped to AgentInspect LOGIC.`,
5034
+ severity: "warning",
5035
+ field: `${pathPrefix}.attributes.openinference.span.kind`
5036
+ });
5037
+ return { kind: "LOGIC", warnings };
5038
+ }
5039
+ }
5040
+ function mapOpenInferenceStatus(status) {
5041
+ if (!isRecord13(status)) return void 0;
5042
+ const rawCode = status.code;
5043
+ if (typeof rawCode !== "string") return void 0;
5044
+ switch (rawCode.toUpperCase()) {
5045
+ case "OK":
5046
+ return "ok";
5047
+ case "ERROR":
5048
+ return "error";
5049
+ case "UNSET":
5050
+ return "unknown";
5051
+ default:
5052
+ return "unknown";
5053
+ }
5054
+ }
5055
+ function readOpenInferenceTokenUsage(attributes) {
5056
+ const prompt = attributes["llm.token_count.prompt"];
5057
+ const completion = attributes["llm.token_count.completion"];
5058
+ const total = attributes["llm.token_count.total"];
5059
+ const cached = attributes["llm.token_count.prompt_details.cache_read"];
5060
+ const usage = {};
5061
+ if (typeof prompt === "number" && Number.isFinite(prompt) && prompt >= 0) {
5062
+ usage.input = prompt;
5063
+ }
5064
+ if (typeof completion === "number" && Number.isFinite(completion) && completion >= 0) {
5065
+ usage.output = completion;
5066
+ }
5067
+ if (typeof total === "number" && Number.isFinite(total) && total >= 0) {
5068
+ usage.total = total;
5069
+ }
5070
+ if (typeof cached === "number" && Number.isFinite(cached) && cached >= 0) {
5071
+ usage.cached = cached;
5072
+ }
5073
+ if (usage.total === void 0 && usage.input !== void 0 && usage.output !== void 0) {
5074
+ usage.total = usage.input + usage.output;
5075
+ }
5076
+ return Object.keys(usage).length > 0 ? usage : void 0;
5077
+ }
5078
+ function readOpenInferenceConfidence(attributes) {
5079
+ const confidence = attributes["agent_inspect.confidence"];
5080
+ if (confidence === "explicit" || confidence === "correlated" || confidence === "heuristic" || confidence === "unknown") {
5081
+ return confidence;
5082
+ }
5083
+ return "correlated";
5084
+ }
5085
+ function mapOpenInferenceSpan(span, index, version2) {
5086
+ const pathPrefix = `spans[${index}]`;
5087
+ const warnings = [];
5088
+ const unsupportedFields = [];
5089
+ const rawAttributes = readRecordField(span, "attributes") ?? {};
5090
+ const sanitized = sanitizeOpenInferenceAttributes(rawAttributes, pathPrefix);
5091
+ warnings.push(...sanitized.warnings);
5092
+ unsupportedFields.push(...sanitized.unsupportedFields);
5093
+ const attributes = { ...sanitized.attributes };
5094
+ for (const [key, value] of Object.entries(span)) {
5095
+ if (OPENINFERENCE_SPAN_KEYS.has(key)) continue;
5096
+ unsupportedFields.push(`${pathPrefix}.${key}`);
5097
+ if (value === null || typeof value !== "object") {
5098
+ attributes[`openinference.${key}`] = value;
5099
+ } else {
5100
+ attributes[`openinference.${key}.summary`] = summarizeAttributeValue(value);
5101
+ warnings.push({
5102
+ code: "openinference_unsupported_field_summarized",
5103
+ message: `Unsupported OpenInference span field "${key}" was summarized.`,
5104
+ severity: "warning",
5105
+ field: `${pathPrefix}.${key}`
5106
+ });
5107
+ }
5108
+ }
5109
+ const traceId = readStringField(span, ["trace_id", "traceId"]) ?? `trace-${index}`;
5110
+ const spanId = readStringField(span, ["span_id", "spanId"]) ?? `span-${index}`;
5111
+ const parentSpanId = readStringField(span, ["parent_span_id", "parentSpanId"]);
5112
+ const name = readStringField(span, ["name"]) ?? spanId;
5113
+ const startedAt = readOpenInferenceTimestamp(
5114
+ span,
5115
+ ["start_time_unix_nano", "startTimeUnixNano"],
5116
+ ["start_time", "startTime"]
5117
+ );
5118
+ const endedAt = readOpenInferenceTimestamp(
5119
+ span,
5120
+ ["end_time_unix_nano", "endTimeUnixNano"],
5121
+ ["end_time", "endTime"]
5122
+ );
5123
+ const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
5124
+ if (startedAt === void 0) {
5125
+ warnings.push({
5126
+ code: "openinference_missing_start_time",
5127
+ message: "OpenInference span is missing a valid start time; using Unix epoch.",
5128
+ severity: "warning",
5129
+ field: `${pathPrefix}.start_time_unix_nano`
5130
+ });
5131
+ unsupportedFields.push(`${pathPrefix}.start_time_unix_nano`);
5132
+ }
5133
+ const { kind, warnings: kindWarnings } = mapOpenInferenceKind(
5134
+ span,
5135
+ rawAttributes,
5136
+ pathPrefix
5137
+ );
5138
+ warnings.push(...kindWarnings);
5139
+ const status = mapOpenInferenceStatus(span.status);
5140
+ const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
5141
+ const errorMessage = isRecord13(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
5142
+ const event = {
5143
+ schemaVersion: "0.2",
5144
+ eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
5145
+ runId: typeof rawAttributes["agent_inspect.run_id"] === "string" ? rawAttributes["agent_inspect.run_id"] : traceId,
5146
+ kind,
5147
+ name,
5148
+ timestamp,
5149
+ confidence: readOpenInferenceConfidence(rawAttributes),
5150
+ source: {
5151
+ type: "otel",
5152
+ name: "openinference",
5153
+ ...version2 !== void 0 ? { version: version2 } : {}
5154
+ },
5155
+ attributes,
5156
+ trace: {
5157
+ traceId,
5158
+ spanId,
5159
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
5160
+ }
5161
+ };
5162
+ if (status !== void 0) {
5163
+ event.status = status;
5164
+ }
5165
+ if (startedAt !== void 0) {
5166
+ event.startedAt = startedAt;
5167
+ }
5168
+ if (endedAt !== void 0) {
5169
+ event.endedAt = endedAt;
5170
+ }
5171
+ const durationMs = durationBetweenIso(startedAt, endedAt);
5172
+ if (durationMs !== void 0) {
5173
+ event.durationMs = durationMs;
5174
+ }
5175
+ if (tokenUsage !== void 0) {
5176
+ event.tokenUsage = tokenUsage;
5177
+ }
5178
+ if (status === "error") {
5179
+ event.error = {
5180
+ message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OpenInference span error"
5181
+ };
5182
+ }
5183
+ return {
5184
+ event,
5185
+ warnings,
5186
+ unsupportedFields,
5187
+ spanId,
5188
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
5189
+ };
5190
+ }
5191
+ function mapOpenInferenceEvents(document) {
5192
+ const mapped = document.spans.map(
5193
+ (span, index) => mapOpenInferenceSpan(span, index, document.version)
5194
+ );
5195
+ const spanIdToEventId = new Map(
5196
+ mapped.map((span) => [span.spanId, span.event.eventId])
5197
+ );
5198
+ for (const span of mapped) {
5199
+ if (span.parentSpanId === void 0) continue;
5200
+ span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
5201
+ }
5202
+ return {
5203
+ events: mapped.map((span) => span.event),
5204
+ warnings: mapped.flatMap((span) => span.warnings),
5205
+ unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
5206
+ };
5207
+ }
5208
+ var openInferenceJsonReader = {
5209
+ format: OPENINFERENCE_READER_FORMAT,
5210
+ name: "OpenInference JSON",
5211
+ async detect(input3) {
5212
+ const resolved = await resolveInput(input3);
5213
+ if (!resolved) return void 0;
5214
+ let parsed;
5215
+ try {
5216
+ parsed = parseJsonDocument(resolved.content);
5217
+ } catch {
5218
+ return void 0;
5219
+ }
5220
+ const document = extractOpenInferenceDocument(parsed);
5221
+ if (!document) return void 0;
5222
+ return {
5223
+ format: OPENINFERENCE_READER_FORMAT,
5224
+ confidence: document.confidence,
5225
+ readerName: "OpenInference JSON",
5226
+ description: document.description,
5227
+ warnings: attachSingleSourceFile(document.warnings, resolved)
5228
+ };
5229
+ },
5230
+ async read(input3) {
5231
+ const resolved = await resolveInput(input3);
5232
+ if (!resolved) {
5233
+ throw new TraceReadError(
5234
+ "unsupported_format",
5235
+ "OpenInference JSON reader requires file, string, or buffer input."
5236
+ );
5237
+ }
5238
+ let parsed;
5239
+ try {
5240
+ parsed = parseJsonDocument(resolved.content);
5241
+ } catch {
5242
+ throw new TraceReadError("unsupported_format", "OpenInference JSON input is not valid JSON.", [
5243
+ {
5244
+ code: "openinference_invalid_json",
5245
+ message: "OpenInference JSON reader could not parse the input as JSON.",
5246
+ severity: "error"
5247
+ }
5248
+ ]);
5249
+ }
5250
+ const document = extractOpenInferenceDocument(parsed);
5251
+ if (!document || document.spans.length === 0) {
5252
+ throw new TraceReadError(
5253
+ "unsupported_format",
5254
+ "No valid OpenInference spans found.",
5255
+ attachSingleSourceFile(
5256
+ document?.warnings ?? [
5257
+ {
5258
+ code: "openinference_no_valid_spans",
5259
+ message: "OpenInference JSON input did not contain valid spans.",
5260
+ severity: "error"
5261
+ }
5262
+ ],
5263
+ resolved
5264
+ )
5265
+ );
5266
+ }
5267
+ const mapped = mapOpenInferenceEvents(document);
5268
+ const warnings = attachSingleSourceFile(
5269
+ [...document.warnings, ...mapped.warnings],
5270
+ resolved
5271
+ );
5272
+ const unsupportedFields = [
5273
+ ...document.unsupportedFields,
5274
+ ...mapped.unsupportedFields
5275
+ ].sort((a, b) => a.localeCompare(b));
5276
+ return {
5277
+ format: OPENINFERENCE_READER_FORMAT,
5278
+ events: mapped.events,
5279
+ runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
5280
+ warnings,
5281
+ unsupportedFields,
5282
+ sourceFiles: resolved.sourceFiles
5283
+ };
5284
+ }
5285
+ };
5286
+ function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
5287
+ if (!isRecord13(value)) {
5288
+ unsupportedFields.push(field);
5289
+ warnings.push({
5290
+ code: "otlp_attribute_value_invalid",
5291
+ message: "OTLP attribute value was not an AnyValue object.",
5292
+ severity: "warning",
5293
+ field
5294
+ });
5295
+ return void 0;
5296
+ }
5297
+ if (typeof value.stringValue === "string") return value.stringValue;
5298
+ if (typeof value.boolValue === "boolean") return value.boolValue;
5299
+ if (typeof value.intValue === "number" && Number.isFinite(value.intValue)) {
5300
+ return value.intValue;
5301
+ }
5302
+ if (typeof value.intValue === "string" && value.intValue.trim() !== "") {
5303
+ const n = Number(value.intValue);
5304
+ if (Number.isFinite(n)) return n;
5305
+ }
5306
+ if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
5307
+ return value.doubleValue;
5308
+ }
5309
+ if (isRecord13(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
5310
+ return value.arrayValue.values.map(
5311
+ (item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
5312
+ );
5313
+ }
5314
+ if (isRecord13(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
5315
+ const out = {};
5316
+ for (const [index, item] of value.kvlistValue.values.entries()) {
5317
+ if (!isRecord13(item) || typeof item.key !== "string") {
5318
+ unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
5319
+ continue;
5320
+ }
5321
+ out[item.key] = parseOtlpAnyValue(
5322
+ item.value,
5323
+ `${field}.kvlistValue.values[${index}].value`,
5324
+ warnings,
5325
+ unsupportedFields
5326
+ );
5327
+ }
5328
+ return out;
5329
+ }
5330
+ if (typeof value.bytesValue === "string") {
5331
+ unsupportedFields.push(field);
5332
+ warnings.push({
5333
+ code: "otlp_bytes_value_summarized",
5334
+ message: "OTLP bytesValue attribute was summarized instead of decoded.",
5335
+ severity: "warning",
5336
+ field
5337
+ });
5338
+ return { type: "bytes", length: value.bytesValue.length };
5339
+ }
5340
+ unsupportedFields.push(field);
5341
+ warnings.push({
5342
+ code: "otlp_attribute_value_unsupported",
5343
+ message: "OTLP attribute value used an unsupported AnyValue shape.",
5344
+ severity: "warning",
5345
+ field
5346
+ });
5347
+ return void 0;
5348
+ }
5349
+ function parseOtlpAttributes(value, pathPrefix) {
5350
+ const attributes = {};
5351
+ const warnings = [];
5352
+ const unsupportedFields = [];
5353
+ if (value === void 0) {
5354
+ return { attributes, warnings, unsupportedFields };
5355
+ }
5356
+ if (!Array.isArray(value)) {
5357
+ unsupportedFields.push(pathPrefix);
5358
+ warnings.push({
5359
+ code: "otlp_attributes_invalid",
5360
+ message: "OTLP attributes field was not an array.",
5361
+ severity: "warning",
5362
+ field: pathPrefix
5363
+ });
5364
+ return { attributes, warnings, unsupportedFields };
5365
+ }
5366
+ for (const [index, item] of value.entries()) {
5367
+ const field = `${pathPrefix}[${index}]`;
5368
+ if (!isRecord13(item) || typeof item.key !== "string") {
5369
+ unsupportedFields.push(field);
5370
+ warnings.push({
5371
+ code: "otlp_attribute_invalid",
5372
+ message: "Skipped OTLP attribute without a string key.",
5373
+ severity: "warning",
5374
+ field
5375
+ });
5376
+ continue;
5377
+ }
5378
+ const parsed = parseOtlpAnyValue(
5379
+ item.value,
5380
+ `${field}.value`,
5381
+ warnings,
5382
+ unsupportedFields
5383
+ );
5384
+ if (parsed !== void 0) {
5385
+ attributes[item.key] = parsed;
5386
+ }
5387
+ }
5388
+ return { attributes, warnings, unsupportedFields };
5389
+ }
5390
+ function looksLikeOtlpSpan(value) {
5391
+ return isRecord13(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
5392
+ }
5393
+ function extractOtlpDocument(root) {
5394
+ if (!isRecord13(root) || !Array.isArray(root.resourceSpans)) return void 0;
5395
+ const spans = [];
5396
+ const warnings = [];
5397
+ const unsupportedFields = [];
5398
+ for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
5399
+ const resourcePath = `resourceSpans[${resourceIndex}]`;
5400
+ if (!isRecord13(resourceSpan)) {
5401
+ unsupportedFields.push(resourcePath);
5402
+ continue;
5403
+ }
5404
+ const resource = readRecordField(resourceSpan, "resource");
5405
+ const resourceParsed = parseOtlpAttributes(
5406
+ resource?.attributes,
5407
+ `${resourcePath}.resource.attributes`
5408
+ );
5409
+ warnings.push(...resourceParsed.warnings);
5410
+ unsupportedFields.push(...resourceParsed.unsupportedFields);
5411
+ if (!Array.isArray(resourceSpan.scopeSpans)) {
5412
+ unsupportedFields.push(`${resourcePath}.scopeSpans`);
5413
+ warnings.push({
5414
+ code: "otlp_scope_spans_missing",
5415
+ message: "OTLP resourceSpans entry did not contain a scopeSpans array.",
5416
+ severity: "warning",
5417
+ field: `${resourcePath}.scopeSpans`
5418
+ });
5419
+ continue;
5420
+ }
5421
+ for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
5422
+ const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
5423
+ if (!isRecord13(scopeSpan)) {
5424
+ unsupportedFields.push(scopePath);
5425
+ continue;
5426
+ }
5427
+ const scope = readRecordField(scopeSpan, "scope");
5428
+ const scopeParsed = parseOtlpAttributes(
5429
+ scope?.attributes,
5430
+ `${scopePath}.scope.attributes`
5431
+ );
5432
+ warnings.push(...scopeParsed.warnings);
5433
+ unsupportedFields.push(...scopeParsed.unsupportedFields);
5434
+ if (!Array.isArray(scopeSpan.spans)) {
5435
+ unsupportedFields.push(`${scopePath}.spans`);
5436
+ warnings.push({
5437
+ code: "otlp_spans_missing",
5438
+ message: "OTLP scopeSpans entry did not contain a spans array.",
5439
+ severity: "warning",
5440
+ field: `${scopePath}.spans`
5441
+ });
5442
+ continue;
5443
+ }
5444
+ for (const [spanIndex, span] of scopeSpan.spans.entries()) {
5445
+ const spanPath = `${scopePath}.spans[${spanIndex}]`;
5446
+ if (!looksLikeOtlpSpan(span)) {
5447
+ unsupportedFields.push(spanPath);
5448
+ warnings.push({
5449
+ code: "otlp_invalid_span",
5450
+ message: "Skipped OTLP span without required traceId, spanId, or name.",
5451
+ severity: "warning",
5452
+ field: spanPath
5453
+ });
5454
+ continue;
5455
+ }
5456
+ spans.push({
5457
+ span,
5458
+ resourceAttributes: resourceParsed.attributes,
5459
+ scopeAttributes: scopeParsed.attributes,
5460
+ scopeName: readStringField(scope ?? {}, ["name"]),
5461
+ scopeVersion: readStringField(scope ?? {}, ["version"]),
5462
+ pathPrefix: spanPath
5463
+ });
5464
+ }
5465
+ }
5466
+ }
5467
+ if (spans.length === 0) {
5468
+ warnings.push({
5469
+ code: "otlp_no_valid_spans",
5470
+ message: "OTLP JSON payload did not contain any valid spans.",
5471
+ severity: "error"
5472
+ });
5473
+ return {
5474
+ spans,
5475
+ confidence: 0.7,
5476
+ description: "Malformed OTLP JSON trace payload",
5477
+ warnings,
5478
+ unsupportedFields
5479
+ };
5480
+ }
5481
+ return {
5482
+ spans,
5483
+ confidence: 0.93,
5484
+ description: "OTLP JSON trace payload",
5485
+ warnings,
5486
+ unsupportedFields
5487
+ };
5488
+ }
5489
+ function mapOtlpStatus(status) {
5490
+ if (!isRecord13(status)) return void 0;
5491
+ const rawCode = status.code;
5492
+ if (typeof rawCode !== "string") return void 0;
5493
+ switch (rawCode.toUpperCase()) {
5494
+ case "STATUS_CODE_OK":
5495
+ case "OK":
5496
+ return "ok";
5497
+ case "STATUS_CODE_ERROR":
5498
+ case "ERROR":
5499
+ return "error";
5500
+ case "STATUS_CODE_UNSET":
5501
+ case "UNSET":
5502
+ return "unknown";
5503
+ default:
5504
+ return "unknown";
5505
+ }
5506
+ }
5507
+ function readOtlpKind(attributes, pathPrefix) {
5508
+ const warnings = [];
5509
+ const agentInspectKind = attributes["agent_inspect.kind"];
5510
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
5511
+ return { kind: agentInspectKind, warnings };
5512
+ }
5513
+ const operation = attributes["gen_ai.operation.name"];
5514
+ if (typeof operation === "string") {
5515
+ switch (operation) {
5516
+ case "generate_content":
5517
+ case "chat":
5518
+ return { kind: "LLM", warnings };
5519
+ case "execute_tool":
5520
+ return { kind: "TOOL", warnings };
5521
+ case "invoke_agent":
5522
+ return { kind: "AGENT", warnings };
5523
+ default:
5524
+ warnings.push({
5525
+ code: "otlp_gen_ai_operation_semantic_loss",
5526
+ message: `OTLP GenAI operation "${operation}" mapped to AgentInspect LOGIC.`,
5527
+ severity: "warning",
5528
+ field: `${pathPrefix}.attributes.gen_ai.operation.name`
5529
+ });
5530
+ return { kind: "LOGIC", warnings };
5531
+ }
5532
+ }
5533
+ warnings.push({
5534
+ code: "otlp_kind_unknown",
5535
+ message: "OTLP span had no AgentInspect kind or GenAI operation; mapped to LOGIC.",
5536
+ severity: "warning",
5537
+ field: `${pathPrefix}.attributes`
5538
+ });
5539
+ return { kind: "LOGIC", warnings };
5540
+ }
5541
+ function readOtlpTokenUsage(attributes) {
5542
+ const input3 = attributes["gen_ai.usage.input_tokens"];
5543
+ const output2 = attributes["gen_ai.usage.output_tokens"];
5544
+ const usage = {};
5545
+ if (typeof input3 === "number" && Number.isFinite(input3) && input3 >= 0) {
5546
+ usage.input = input3;
5547
+ }
5548
+ if (typeof output2 === "number" && Number.isFinite(output2) && output2 >= 0) {
5549
+ usage.output = output2;
5550
+ }
5551
+ if (usage.input !== void 0 && usage.output !== void 0) {
5552
+ usage.total = usage.input + usage.output;
5553
+ }
5554
+ return Object.keys(usage).length > 0 ? usage : void 0;
5555
+ }
5556
+ function readOtlpConfidence(attributes) {
5557
+ return readOpenInferenceConfidence(attributes);
5558
+ }
5559
+ function sanitizeOtlpAttributes(attributes, pathPrefix) {
5560
+ const ownerPath = pathPrefix.endsWith(".attributes") ? pathPrefix.slice(0, -".attributes".length) : pathPrefix;
5561
+ const sanitized = sanitizeOpenInferenceAttributes(attributes, ownerPath);
5562
+ return {
5563
+ ...sanitized,
5564
+ warnings: sanitized.warnings.map(
5565
+ (warning) => warning.code === "openinference_sensitive_attribute_summarized" ? {
5566
+ ...warning,
5567
+ code: "otlp_sensitive_attribute_summarized",
5568
+ message: "OTLP prompt/output/document attribute(s) were summarized instead of copied verbatim."
5569
+ } : warning
5570
+ )
5571
+ };
5572
+ }
5573
+ function mapOtlpEvents(value, pathPrefix) {
5574
+ const warnings = [];
5575
+ const unsupportedFields = [];
5576
+ if (value === void 0) return { warnings, unsupportedFields };
5577
+ if (!Array.isArray(value)) {
5578
+ unsupportedFields.push(pathPrefix);
5579
+ warnings.push({
5580
+ code: "otlp_events_invalid",
5581
+ message: "OTLP events field was not an array.",
5582
+ severity: "warning",
5583
+ field: pathPrefix
5584
+ });
5585
+ return { warnings, unsupportedFields };
5586
+ }
5587
+ const events = [];
5588
+ for (const [index, event] of value.entries()) {
5589
+ const eventPath = `${pathPrefix}[${index}]`;
5590
+ if (!isRecord13(event)) {
5591
+ unsupportedFields.push(eventPath);
5592
+ continue;
5593
+ }
5594
+ const parsedAttributes = parseOtlpAttributes(
5595
+ event.attributes,
5596
+ `${eventPath}.attributes`
5597
+ );
5598
+ warnings.push(...parsedAttributes.warnings);
5599
+ unsupportedFields.push(...parsedAttributes.unsupportedFields);
5600
+ const sanitized = sanitizeOtlpAttributes(
5601
+ parsedAttributes.attributes,
5602
+ `${eventPath}.attributes`
5603
+ );
5604
+ warnings.push(...sanitized.warnings);
5605
+ unsupportedFields.push(...sanitized.unsupportedFields);
5606
+ const out = {};
5607
+ const name = readStringField(event, ["name"]);
5608
+ if (name !== void 0) {
5609
+ out.name = name;
5610
+ }
5611
+ const timestamp = parseUnixNanoToIso(event.timeUnixNano);
5612
+ if (timestamp !== void 0) {
5613
+ out.timestamp = timestamp;
5614
+ } else if (event.timeUnixNano !== void 0) {
5615
+ unsupportedFields.push(`${eventPath}.timeUnixNano`);
5616
+ warnings.push({
5617
+ code: "otlp_event_timestamp_invalid",
5618
+ message: "OTLP event timeUnixNano could not be parsed.",
5619
+ severity: "warning",
5620
+ field: `${eventPath}.timeUnixNano`
5621
+ });
5622
+ }
5623
+ if (Object.keys(sanitized.attributes).length > 0) {
5624
+ out.attributes = sanitized.attributes;
5625
+ }
5626
+ events.push(out);
5627
+ }
5628
+ return {
5629
+ events: events.length > 0 ? events : void 0,
5630
+ warnings,
5631
+ unsupportedFields
5632
+ };
5633
+ }
5634
+ function mapOtlpSpan(context) {
5635
+ const { span, pathPrefix } = context;
5636
+ const warnings = [];
5637
+ const unsupportedFields = [];
5638
+ const parsedSpanAttributes = parseOtlpAttributes(
5639
+ span.attributes,
5640
+ `${pathPrefix}.attributes`
5641
+ );
5642
+ warnings.push(...parsedSpanAttributes.warnings);
5643
+ unsupportedFields.push(...parsedSpanAttributes.unsupportedFields);
5644
+ const sanitizedSpanAttributes = sanitizeOtlpAttributes(
5645
+ parsedSpanAttributes.attributes,
5646
+ `${pathPrefix}.attributes`
5647
+ );
5648
+ warnings.push(...sanitizedSpanAttributes.warnings);
5649
+ unsupportedFields.push(...sanitizedSpanAttributes.unsupportedFields);
5650
+ const attributes = {
5651
+ ...sanitizedSpanAttributes.attributes
5652
+ };
5653
+ for (const [key, value] of Object.entries(context.resourceAttributes)) {
5654
+ attributes[`resource.${key}`] = value;
5655
+ }
5656
+ for (const [key, value] of Object.entries(context.scopeAttributes)) {
5657
+ attributes[`scope.${key}`] = value;
5658
+ }
5659
+ if (context.scopeName !== void 0) {
5660
+ attributes["scope.name"] = context.scopeName;
5661
+ }
5662
+ if (context.scopeVersion !== void 0) {
5663
+ attributes["scope.version"] = context.scopeVersion;
5664
+ }
5665
+ for (const [key, value] of Object.entries(span)) {
5666
+ if (OTLP_SPAN_KEYS.has(key)) continue;
5667
+ unsupportedFields.push(`${pathPrefix}.${key}`);
5668
+ if (value === null || typeof value !== "object") {
5669
+ attributes[`otlp.${key}`] = value;
5670
+ } else {
5671
+ attributes[`otlp.${key}.summary`] = summarizeAttributeValue(value);
5672
+ warnings.push({
5673
+ code: "otlp_unsupported_field_summarized",
5674
+ message: `Unsupported OTLP span field "${key}" was summarized.`,
5675
+ severity: "warning",
5676
+ field: `${pathPrefix}.${key}`
5677
+ });
5678
+ }
5679
+ }
5680
+ for (const key of [
5681
+ "droppedAttributesCount",
5682
+ "droppedEventsCount",
5683
+ "droppedLinksCount",
5684
+ "links"
5685
+ ]) {
5686
+ if (span[key] !== void 0) {
5687
+ unsupportedFields.push(`${pathPrefix}.${key}`);
5688
+ warnings.push({
5689
+ code: "otlp_span_field_not_mapped",
5690
+ message: `OTLP span field "${key}" is not represented in AgentInspect events.`,
5691
+ severity: "warning",
5692
+ field: `${pathPrefix}.${key}`
5693
+ });
5694
+ }
5695
+ }
5696
+ const events = mapOtlpEvents(span.events, `${pathPrefix}.events`);
5697
+ warnings.push(...events.warnings);
5698
+ unsupportedFields.push(...events.unsupportedFields);
5699
+ if (events.events !== void 0) {
5700
+ attributes["otlp.events"] = events.events;
5701
+ }
5702
+ const traceId = readStringField(span, ["traceId"]) ?? "trace-unknown";
5703
+ const spanId = readStringField(span, ["spanId"]) ?? "span-unknown";
5704
+ const parentSpanId = readStringField(span, ["parentSpanId"]);
5705
+ const startedAt = readOpenInferenceTimestamp(
5706
+ span,
5707
+ ["startTimeUnixNano"],
5708
+ []
5709
+ );
5710
+ const endedAt = readOpenInferenceTimestamp(span, ["endTimeUnixNano"], []);
5711
+ const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
5712
+ if (startedAt === void 0) {
5713
+ unsupportedFields.push(`${pathPrefix}.startTimeUnixNano`);
5714
+ warnings.push({
5715
+ code: "otlp_missing_start_time",
5716
+ message: "OTLP span is missing a valid startTimeUnixNano; using Unix epoch.",
5717
+ severity: "warning",
5718
+ field: `${pathPrefix}.startTimeUnixNano`
5719
+ });
5720
+ }
5721
+ const { kind, warnings: kindWarnings } = readOtlpKind(
5722
+ parsedSpanAttributes.attributes,
5723
+ pathPrefix
5724
+ );
5725
+ warnings.push(...kindWarnings);
5726
+ const status = mapOtlpStatus(span.status);
5727
+ const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
5728
+ const errorMessage = isRecord13(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
5729
+ const event = {
5730
+ schemaVersion: "0.2",
5731
+ eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
5732
+ runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
5733
+ kind,
5734
+ name: readStringField(span, ["name"]) ?? spanId,
5735
+ timestamp,
5736
+ confidence: readOtlpConfidence(parsedSpanAttributes.attributes),
5737
+ source: {
5738
+ type: "otel",
5739
+ name: context.scopeName ?? (typeof context.resourceAttributes["service.name"] === "string" ? context.resourceAttributes["service.name"] : "otlp-json"),
5740
+ ...context.scopeVersion !== void 0 ? { version: context.scopeVersion } : {}
5741
+ },
5742
+ attributes,
5743
+ trace: {
5744
+ traceId,
5745
+ spanId,
5746
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
5747
+ }
5748
+ };
5749
+ if (status !== void 0) {
5750
+ event.status = status;
5751
+ }
5752
+ if (startedAt !== void 0) {
5753
+ event.startedAt = startedAt;
5754
+ }
5755
+ if (endedAt !== void 0) {
5756
+ event.endedAt = endedAt;
5757
+ }
5758
+ const durationMs = durationBetweenIso(startedAt, endedAt);
5759
+ if (durationMs !== void 0) {
5760
+ event.durationMs = durationMs;
5761
+ }
5762
+ if (tokenUsage !== void 0) {
5763
+ event.tokenUsage = tokenUsage;
5764
+ }
5765
+ if (status === "error") {
5766
+ event.error = {
5767
+ message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OTLP span error"
5768
+ };
5769
+ }
5770
+ return {
5771
+ event,
5772
+ warnings,
5773
+ unsupportedFields,
5774
+ spanId,
5775
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
5776
+ };
5777
+ }
5778
+ function mapOtlpEventsToPersisted(document) {
5779
+ const mapped = document.spans.map((span) => mapOtlpSpan(span));
5780
+ const spanIdToEventId = new Map(
5781
+ mapped.map((span) => [span.spanId, span.event.eventId])
5782
+ );
5783
+ for (const span of mapped) {
5784
+ if (span.parentSpanId === void 0) continue;
5785
+ span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
5786
+ }
5787
+ return {
5788
+ events: mapped.map((span) => span.event),
5789
+ warnings: mapped.flatMap((span) => span.warnings),
5790
+ unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
5791
+ };
5792
+ }
5793
+ var otlpJsonReader = {
5794
+ format: OTLP_READER_FORMAT,
5795
+ name: "OTLP JSON",
5796
+ async detect(input3) {
5797
+ const resolved = await resolveInput(input3);
5798
+ if (!resolved) return void 0;
5799
+ let parsed;
5800
+ try {
5801
+ parsed = parseJsonDocument(resolved.content);
5802
+ } catch {
5803
+ return void 0;
5804
+ }
5805
+ const document = extractOtlpDocument(parsed);
5806
+ if (!document) return void 0;
5807
+ return {
5808
+ format: OTLP_READER_FORMAT,
5809
+ confidence: document.confidence,
5810
+ readerName: "OTLP JSON",
5811
+ description: document.description,
5812
+ warnings: attachSingleSourceFile(document.warnings, resolved)
5813
+ };
5814
+ },
5815
+ async read(input3) {
5816
+ const resolved = await resolveInput(input3);
5817
+ if (!resolved) {
5818
+ throw new TraceReadError(
5819
+ "unsupported_format",
5820
+ "OTLP JSON reader requires file, string, or buffer input."
5821
+ );
5822
+ }
5823
+ let parsed;
5824
+ try {
5825
+ parsed = parseJsonDocument(resolved.content);
5826
+ } catch {
5827
+ throw new TraceReadError("unsupported_format", "OTLP JSON input is not valid JSON.", [
5828
+ {
5829
+ code: "otlp_invalid_json",
5830
+ message: "OTLP JSON reader could not parse the input as JSON.",
5831
+ severity: "error"
5832
+ }
5833
+ ]);
5834
+ }
5835
+ const document = extractOtlpDocument(parsed);
5836
+ if (!document || document.spans.length === 0) {
5837
+ throw new TraceReadError(
5838
+ "unsupported_format",
5839
+ "No valid OTLP spans found.",
5840
+ attachSingleSourceFile(
5841
+ document?.warnings ?? [
5842
+ {
5843
+ code: "otlp_no_valid_spans",
5844
+ message: "OTLP JSON input did not contain valid spans.",
5845
+ severity: "error"
5846
+ }
5847
+ ],
5848
+ resolved
5849
+ )
5850
+ );
5851
+ }
5852
+ const mapped = mapOtlpEventsToPersisted(document);
5853
+ const warnings = attachSingleSourceFile(
5854
+ [...document.warnings, ...mapped.warnings],
5855
+ resolved
5856
+ );
5857
+ const unsupportedFields = [
5858
+ ...document.unsupportedFields,
5859
+ ...mapped.unsupportedFields
5860
+ ].sort((a, b) => a.localeCompare(b));
5861
+ return {
5862
+ format: OTLP_READER_FORMAT,
5863
+ events: mapped.events,
5864
+ runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
5865
+ warnings,
5866
+ unsupportedFields,
5867
+ sourceFiles: resolved.sourceFiles
5868
+ };
5869
+ }
5870
+ };
5871
+ var agentInspectJsonlReader = {
5872
+ format: "agent-inspect-jsonl",
5873
+ name: "AgentInspect JSONL",
5874
+ async detect(input3) {
5875
+ const resolved = await resolveInput(input3);
5876
+ if (!resolved) return void 0;
5877
+ const detected = detectJsonlFormat(resolved.content);
5878
+ if (detected.validRows === 0 || detected.format === "empty") {
5879
+ return void 0;
5880
+ }
5881
+ return {
5882
+ format: "agent-inspect-jsonl",
5883
+ confidence: 0.95,
5884
+ readerName: "AgentInspect JSONL",
5885
+ description: agentInspectFormatLabel(detected.format),
5886
+ warnings: attachSingleSourceFile(detected.warnings, resolved)
5887
+ };
5888
+ },
5889
+ async read(input3) {
5890
+ const resolved = await resolveInput(input3);
5891
+ if (!resolved) {
5892
+ throw new Error("AgentInspect JSONL reader requires file, directory, string, or buffer input.");
5893
+ }
5894
+ const parsed = parseTraceJsonl(resolved.content, { warnings: false });
5895
+ if (parsed.sourceEventCount === 0) {
5896
+ throw new Error("No valid AgentInspect JSONL events found.");
5897
+ }
5898
+ const events = persistedEventsForParsedTrace(parsed);
5899
+ return {
5900
+ format: agentInspectFormatLabel(parsed.format),
5901
+ events,
5902
+ runs: persistedInspectEventsToRunTrees(events, { skipInvalid: true }),
5903
+ warnings: parsed.format === "mixed" ? attachSingleSourceFile(
5904
+ [
5905
+ {
5906
+ code: "mixed_agent_inspect_jsonl",
5907
+ message: "Trace input mixes schemaVersion 0.1 and 0.2 rows; events were normalized for reading.",
5908
+ severity: "warning"
5909
+ }
5910
+ ],
5911
+ resolved
5912
+ ) : [],
5913
+ unsupportedFields: [],
5914
+ sourceFiles: resolved.sourceFiles
5915
+ };
5916
+ }
5917
+ };
5918
+ var DEFAULT_TRACE_READERS = [
5919
+ agentInspectJsonlReader,
5920
+ openInferenceJsonReader,
5921
+ otlpJsonReader
5922
+ ];
5923
+ async function detectTraceFormat(input3, options = {}) {
5924
+ const readers = options.readers ?? DEFAULT_TRACE_READERS;
5925
+ if (options.format !== void 0) {
5926
+ const reader = findReaderByFormat(options.format, readers);
5927
+ if (!reader) {
5928
+ return {
5929
+ status: "unsupported",
5930
+ candidates: [],
5931
+ warnings: [
5932
+ {
5933
+ code: "unsupported_format",
5934
+ message: `No trace reader is registered for format "${options.format}".`,
5935
+ severity: "error"
5936
+ }
5937
+ ]
5938
+ };
5939
+ }
5940
+ return {
5941
+ status: "detected",
5942
+ format: reader.format,
5943
+ candidates: [
5944
+ {
5945
+ format: reader.format,
5946
+ confidence: 1,
5947
+ readerName: reader.name,
5948
+ description: "Explicit format override"
5949
+ }
5950
+ ],
5951
+ warnings: []
5952
+ };
5953
+ }
5954
+ const candidates = [];
5955
+ const warnings = [];
5956
+ for (const reader of readers) {
5957
+ try {
5958
+ const candidate = await reader.detect(input3);
5959
+ if (candidate !== void 0) {
5960
+ candidates.push(normalizeCandidate(reader, candidate));
5961
+ }
5962
+ } catch (error) {
5963
+ if (error instanceof TraceReadError) {
5964
+ warnings.push(...error.warnings);
5965
+ continue;
5966
+ }
5967
+ warnings.push({
5968
+ code: "reader_detect_failed",
5969
+ message: error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed during detection.`,
5970
+ severity: "warning"
5971
+ });
5972
+ }
5973
+ }
5974
+ const sorted = sortCandidates(
5975
+ candidates.filter((candidate) => candidate.confidence >= MIN_DETECTION_CONFIDENCE)
5976
+ );
5977
+ const candidateWarnings = collectWarnings(sorted);
5978
+ const lowConfidenceWarnings = candidates.length > sorted.length ? [
5979
+ {
5980
+ code: "low_confidence_candidates",
5981
+ message: `Ignored ${candidates.length - sorted.length} low-confidence format candidate(s).`,
5982
+ severity: "info"
5983
+ }
5984
+ ] : [];
5985
+ const allWarnings = dedupeWarnings([
5986
+ ...warnings,
5987
+ ...candidateWarnings,
5988
+ ...lowConfidenceWarnings
5989
+ ]);
5990
+ if (sorted.length === 0) {
5991
+ return {
5992
+ status: "unsupported",
5993
+ candidates: [],
5994
+ warnings: allWarnings
5995
+ };
5996
+ }
5997
+ const [best, second] = sorted;
5998
+ if (second !== void 0 && best.confidence - second.confidence <= AMBIGUOUS_CONFIDENCE_DELTA) {
5999
+ return {
6000
+ status: "ambiguous",
6001
+ candidates: sorted,
6002
+ warnings: [
6003
+ ...allWarnings,
6004
+ {
6005
+ code: "ambiguous_format_candidates",
6006
+ message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
6007
+ severity: "warning"
6008
+ }
6009
+ ]
6010
+ };
6011
+ }
6012
+ return {
6013
+ status: "detected",
6014
+ format: best.format,
6015
+ candidates: sorted,
6016
+ warnings: allWarnings
6017
+ };
6018
+ }
6019
+ async function readTrace(input3, options = {}) {
6020
+ const readers = options.readers ?? DEFAULT_TRACE_READERS;
6021
+ const detection = await detectTraceFormat(input3, options);
6022
+ if (detection.status === "unsupported" || detection.format === void 0) {
6023
+ throw new TraceReadError(
6024
+ "unsupported_format",
6025
+ "No trace reader could detect the input format.",
6026
+ detection.warnings
6027
+ );
6028
+ }
6029
+ if (detection.status === "ambiguous") {
6030
+ throw new TraceReadError(
6031
+ "ambiguous_format",
6032
+ "Multiple trace readers matched the input with equal confidence.",
6033
+ detection.warnings
6034
+ );
6035
+ }
6036
+ const reader = findReaderByFormat(detection.format, readers);
6037
+ if (!reader) {
6038
+ throw new TraceReadError(
6039
+ "unsupported_format",
6040
+ `No trace reader is registered for format "${detection.format}".`,
6041
+ detection.warnings
6042
+ );
6043
+ }
6044
+ try {
6045
+ const result = await reader.read(input3, { format: detection.format });
6046
+ return {
6047
+ ...result,
6048
+ format: result.format || detection.format,
6049
+ warnings: [...detection.warnings, ...result.warnings]
6050
+ };
6051
+ } catch (error) {
6052
+ if (error instanceof TraceReadError) {
6053
+ throw new TraceReadError(
6054
+ error.code,
6055
+ error.message,
6056
+ dedupeWarnings([...detection.warnings, ...error.warnings])
6057
+ );
6058
+ }
6059
+ throw new TraceReadError(
6060
+ "reader_failed",
6061
+ error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed.`,
6062
+ detection.warnings
6063
+ );
6064
+ }
6065
+ }
6066
+ function openTrace(input3, options = {}) {
6067
+ return readTrace(input3, options);
6068
+ }
6069
+
6070
+ // packages/core/src/diff/comparable.ts
6071
+ function extractOutputPreview(meta) {
3952
6072
  if (meta === void 0) return void 0;
3953
6073
  if ("outputPreview" in meta) return meta.outputPreview;
3954
6074
  if ("resultPreview" in meta) return meta.resultPreview;
@@ -4086,13 +6206,13 @@ function pairSteps(left, right) {
4086
6206
  return pairs;
4087
6207
  }
4088
6208
  function compareLeafSteps(L, R, segments, opts, out) {
4089
- const path8 = buildPath(segments);
6209
+ const path10 = buildPath(segments);
4090
6210
  if (L.name !== R.name) {
4091
6211
  out.push({
4092
6212
  kind: "structure",
4093
6213
  severity: "warning",
4094
6214
  message: "Step name differs",
4095
- path: path8,
6215
+ path: path10,
4096
6216
  left: L.name,
4097
6217
  right: R.name
4098
6218
  });
@@ -4102,7 +6222,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
4102
6222
  kind: "step-type",
4103
6223
  severity: "warning",
4104
6224
  message: "Step type differs",
4105
- path: path8,
6225
+ path: path10,
4106
6226
  left: L.type,
4107
6227
  right: R.type
4108
6228
  });
@@ -4112,7 +6232,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
4112
6232
  kind: "step-status",
4113
6233
  severity: "warning",
4114
6234
  message: "Step status differs",
4115
- path: path8,
6235
+ path: path10,
4116
6236
  left: L.status,
4117
6237
  right: R.status
4118
6238
  });
@@ -4124,7 +6244,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
4124
6244
  kind: "error",
4125
6245
  severity: "error",
4126
6246
  message: "Step error message differs",
4127
- path: path8,
6247
+ path: path10,
4128
6248
  left: le || void 0,
4129
6249
  right: re || void 0
4130
6250
  });
@@ -4142,7 +6262,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
4142
6262
  kind: "duration",
4143
6263
  severity: "info",
4144
6264
  message: "Step duration differs",
4145
- path: path8,
6265
+ path: path10,
4146
6266
  left: ld,
4147
6267
  right: rd
4148
6268
  });
@@ -4155,7 +6275,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
4155
6275
  kind: "metadata",
4156
6276
  severity: "info",
4157
6277
  message: "Step metadata differs",
4158
- path: path8,
6278
+ path: path10,
4159
6279
  left: L.metadata,
4160
6280
  right: R.metadata
4161
6281
  });
@@ -4167,7 +6287,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
4167
6287
  kind: "output",
4168
6288
  severity: "info",
4169
6289
  message: "Output preview differs",
4170
- path: path8,
6290
+ path: path10,
4171
6291
  left: L.outputPreview,
4172
6292
  right: R.outputPreview
4173
6293
  });
@@ -4605,10 +6725,10 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
4605
6725
  return 3;
4606
6726
  }
4607
6727
  if ("TERM_PROGRAM" in env) {
4608
- const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
6728
+ const version2 = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
4609
6729
  switch (env.TERM_PROGRAM) {
4610
6730
  case "iTerm.app": {
4611
- return version >= 3 ? 3 : 2;
6731
+ return version2 >= 3 ? 3 : 2;
4612
6732
  }
4613
6733
  case "Apple_Terminal": {
4614
6734
  return 2;
@@ -4817,11 +6937,11 @@ createChalk({ level: stderrColor ? stderrColor.level : 0 });
4817
6937
  var source_default = chalk;
4818
6938
 
4819
6939
  // packages/core/src/diff/renderer.ts
4820
- function formatPath(path8) {
4821
- if (path8 === void 0 || path8.path.length === 0) {
6940
+ function formatPath(path10) {
6941
+ if (path10 === void 0 || path10.path.length === 0) {
4822
6942
  return "(run)";
4823
6943
  }
4824
- return path8.path.map((s) => s.name).join(" > ");
6944
+ return path10.path.map((s) => s.name).join(" > ");
4825
6945
  }
4826
6946
  function formatValue(v, verbose) {
4827
6947
  if (v === void 0) return "(undefined)";
@@ -5728,18 +7848,38 @@ async function clean(options = {}) {
5728
7848
  process.exitCode = 1;
5729
7849
  }
5730
7850
  }
5731
-
5732
- // packages/cli/src/read-run.ts
5733
- async function readRunTraceEvents(runId, traceDir) {
5734
- const raw = await readTraceFile(runId, traceDir);
5735
- if (raw === void 0) {
5736
- return void 0;
7851
+ function mapReaderFormat(format) {
7852
+ switch (format) {
7853
+ case "agent-inspect-v0.1-jsonl":
7854
+ return "0.1";
7855
+ case "agent-inspect-v0.2-jsonl":
7856
+ return "0.2";
7857
+ case "agent-inspect-mixed-jsonl":
7858
+ return "mixed";
7859
+ default:
7860
+ return "empty";
5737
7861
  }
5738
- const parsed = parseTraceJsonl(raw);
5739
- if (parsed.format === "empty") {
5740
- return { events: [], format: "empty" };
7862
+ }
7863
+ function isMissingFileError(error) {
7864
+ return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
7865
+ }
7866
+ async function readRunTraceEvents(runId, traceDir) {
7867
+ const filePath = getTraceFilePath(runId, traceDir);
7868
+ try {
7869
+ await promises.access(filePath);
7870
+ const result = await openTrace(
7871
+ { type: "file", path: filePath },
7872
+ { format: "agent-inspect-jsonl" }
7873
+ );
7874
+ const events = persistedInspectEventsToTraceEvents(result.events);
7875
+ return {
7876
+ events,
7877
+ format: events.length > 0 ? mapReaderFormat(result.format) : "empty"
7878
+ };
7879
+ } catch (error) {
7880
+ if (isMissingFileError(error)) return void 0;
7881
+ throw error;
5741
7882
  }
5742
- return { events: parsed.events, format: parsed.format };
5743
7883
  }
5744
7884
 
5745
7885
  // packages/cli/src/view.ts
@@ -6810,6 +8950,149 @@ async function reportCommand(runId, options = {}) {
6810
8950
  console.log(result.content);
6811
8951
  }
6812
8952
  }
8953
+ async function readStdin(stdin) {
8954
+ stdin.setEncoding("utf8");
8955
+ let content = "";
8956
+ for await (const chunk of stdin) {
8957
+ content += typeof chunk === "string" ? chunk : String(chunk);
8958
+ }
8959
+ return content;
8960
+ }
8961
+ async function inputFromPathOrStdin(input3, stdin) {
8962
+ if (input3 === void 0 || input3 === "-") {
8963
+ return { type: "string", content: await readStdin(stdin) };
8964
+ }
8965
+ const stats = await promises.stat(input3);
8966
+ if (stats.isDirectory()) return { type: "directory", path: input3 };
8967
+ return { type: "file", path: input3 };
8968
+ }
8969
+ function printWarnings(result) {
8970
+ if (result.warnings.length === 0 && result.unsupportedFields.length === 0) {
8971
+ return;
8972
+ }
8973
+ console.log("");
8974
+ console.log("Diagnostics");
8975
+ for (const warning of result.warnings) {
8976
+ const where = [
8977
+ warning.sourceFile,
8978
+ warning.line !== void 0 ? `line ${warning.line}` : void 0,
8979
+ warning.field
8980
+ ].filter((value) => value !== void 0).join(" ");
8981
+ console.log(
8982
+ `- ${warning.code}: ${warning.message}${where !== "" ? ` (${where})` : ""}`
8983
+ );
8984
+ }
8985
+ for (const field of result.unsupportedFields) {
8986
+ console.log(`- unsupported: ${field}`);
8987
+ }
8988
+ }
8989
+ function printNode(node, depth) {
8990
+ const ev = node.event;
8991
+ const status = ev.status !== void 0 ? ` ${ev.status}` : "";
8992
+ const duration = ev.durationMs !== void 0 ? ` ${formatDuration2(ev.durationMs)}` : "";
8993
+ console.log(`${getIndent(depth)}${ev.kind.toLowerCase()}: ${ev.name}${status}${duration}`);
8994
+ for (const child of node.children) {
8995
+ printNode(child, depth + 1);
8996
+ }
8997
+ }
8998
+ function printRun(result, run) {
8999
+ console.log(`Format: ${result.format}`);
9000
+ console.log(`Run: ${run.runId}`);
9001
+ if (run.name !== void 0) console.log(`Name: ${run.name}`);
9002
+ if (run.status !== void 0) console.log(`Status: ${run.status}`);
9003
+ if (run.startedAt !== void 0) {
9004
+ console.log(`Started: ${formatTimestamp(run.startedAt)}`);
9005
+ }
9006
+ if (run.durationMs !== void 0) {
9007
+ console.log(`Duration: ${formatDuration2(run.durationMs)}`);
9008
+ }
9009
+ console.log(`Events: ${run.metadata.totalEvents}`);
9010
+ for (const node of run.children) {
9011
+ printNode(node, 0);
9012
+ }
9013
+ }
9014
+ function selectRun(result, runId) {
9015
+ if (runId !== void 0) {
9016
+ return result.runs.find((run) => run.runId === runId);
9017
+ }
9018
+ return result.runs.length === 1 ? result.runs[0] : void 0;
9019
+ }
9020
+ function printMultipleRuns(result) {
9021
+ console.error(
9022
+ `Trace contains ${result.runs.length} runs. Re-run with --run <run-id>.`
9023
+ );
9024
+ for (const run of result.runs) {
9025
+ const bits = [
9026
+ run.runId,
9027
+ run.name !== void 0 ? `name=${run.name}` : void 0,
9028
+ run.status !== void 0 ? `status=${run.status}` : void 0,
9029
+ `events=${run.metadata.totalEvents}`
9030
+ ].filter((value) => value !== void 0);
9031
+ console.error(`- ${bits.join(" ")}`);
9032
+ }
9033
+ }
9034
+ function writeJson(output2) {
9035
+ console.log(JSON.stringify(output2, null, 2));
9036
+ }
9037
+ async function openCommand(input3, options = {}, stdin = process.stdin) {
9038
+ try {
9039
+ const traceInput = await inputFromPathOrStdin(input3, stdin);
9040
+ const result = await openTrace(traceInput, {
9041
+ ...options.format !== void 0 ? { format: options.format } : {}
9042
+ });
9043
+ const selected = selectRun(result, options.run);
9044
+ if (selected === void 0) {
9045
+ process.exitCode = 1;
9046
+ const message = options.run !== void 0 ? `Run not found: ${options.run}` : `Trace contains ${result.runs.length} runs. Specify --run <run-id>.`;
9047
+ if (options.json) {
9048
+ writeJson({
9049
+ format: result.format,
9050
+ sourceFiles: result.sourceFiles,
9051
+ warnings: result.warnings,
9052
+ unsupportedFields: result.unsupportedFields,
9053
+ runs: result.runs,
9054
+ error: { message }
9055
+ });
9056
+ } else if (options.run !== void 0) {
9057
+ console.error(message);
9058
+ } else {
9059
+ printMultipleRuns(result);
9060
+ }
9061
+ return;
9062
+ }
9063
+ if (options.json) {
9064
+ writeJson({
9065
+ format: result.format,
9066
+ sourceFiles: result.sourceFiles,
9067
+ warnings: result.warnings,
9068
+ unsupportedFields: result.unsupportedFields,
9069
+ selectedRunId: selected.runId,
9070
+ runs: [selected]
9071
+ });
9072
+ return;
9073
+ }
9074
+ printRun(result, selected);
9075
+ if (options.diagnostics) {
9076
+ printWarnings(result);
9077
+ }
9078
+ } catch (error) {
9079
+ process.exitCode = 1;
9080
+ const message = error instanceof Error ? error.message : String(error);
9081
+ const code = error instanceof TraceReadError ? error.code : void 0;
9082
+ const warnings = error instanceof TraceReadError ? error.warnings : [];
9083
+ if (options.json) {
9084
+ writeJson({
9085
+ warnings,
9086
+ error: { ...code !== void 0 ? { code } : {}, message }
9087
+ });
9088
+ return;
9089
+ }
9090
+ console.error(message);
9091
+ if (options.diagnostics) {
9092
+ printWarnings({ warnings, unsupportedFields: [] });
9093
+ }
9094
+ }
9095
+ }
6813
9096
 
6814
9097
  // packages/cli/src/index.ts
6815
9098
  function runCommand(action) {
@@ -6820,7 +9103,7 @@ function runCommand(action) {
6820
9103
  });
6821
9104
  }
6822
9105
  function createCliProgram() {
6823
- const program = new commander.Command("agent-inspect").description("Local-first execution-tree debugger for AI agents").version("1.0.0");
9106
+ const program = new commander.Command("agent-inspect").description("Local-first execution-tree debugger for AI agents").version(version);
6824
9107
  program.command("list").description("List recent AgentInspect runs").option("--dir <path>", "trace directory").option("--limit <number>", "max runs to show (default 20, max 100)").addOption(
6825
9108
  new commander.Option("--status <status>", "filter by run status").choices([
6826
9109
  "running",
@@ -6903,6 +9186,15 @@ function createCliProgram() {
6903
9186
  ).action((runId, opts) => {
6904
9187
  runCommand(() => exportCommand(runId, opts));
6905
9188
  });
9189
+ program.command("open").description("Open any supported local trace through the reader pipeline").argument("[input]", "trace file, directory, or - for stdin").addOption(
9190
+ new commander.Option("--format <format>", "trace input format").choices([
9191
+ "agent-inspect-jsonl",
9192
+ "openinference-json",
9193
+ "otlp-json"
9194
+ ])
9195
+ ).option("--json", "print result as JSON").option("--diagnostics", "print reader warnings and unsupported fields").option("--run <run-id>", "select a run when the trace contains multiple runs").action((input3, opts) => {
9196
+ runCommand(() => openCommand(input3, opts));
9197
+ });
6906
9198
  program.command("diff").description("Compare two local AgentInspect JSONL traces (read-only)").argument("<left-run-id>", "first run id").argument("<right-run-id>", "second run id").option("--dir <path>", "trace directory").option("--json", "print diff result as JSON").option("--ignore-duration", "omit duration comparisons").option(
6907
9199
  "--duration-threshold <duration>",
6908
9200
  "ignore duration deltas at or below this (e.g. 500ms, 2s, 1m)"
@@ -6958,7 +9250,7 @@ function createCliProgram() {
6958
9250
  ).option("-o, --output <path>", "write report to file (creates parent dirs)").option("--json", "emit JSON wrapper (includes content when writing to stdout)").option("--include-attributes", "include bounded step attributes in tree section").option("--no-errors", "omit error details from tree section").option("--no-correlation", "omit correlation metadata from what section").addOption(
6959
9251
  new commander.Option(
6960
9252
  "--redaction-profile <profile>",
6961
- "redaction profile for tree section: local, share, strict (default: local)"
9253
+ "redaction profile for entire report: local, share, strict (default: local)"
6962
9254
  ).choices(["local", "share", "strict"])
6963
9255
  ).action((runId, opts) => {
6964
9256
  runCommand(() => reportCommand(runId, opts));