agent-inspect 1.9.0 → 2.1.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 (100) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +27 -10
  3. package/docs/ADAPTER-CONFORMANCE.md +1 -1
  4. package/docs/ADAPTERS.md +4 -0
  5. package/docs/API.md +76 -35
  6. package/docs/CLI.md +129 -11
  7. package/docs/COMPARE.md +5 -4
  8. package/docs/EXPORTS.md +1 -1
  9. package/docs/GETTING-STARTED.md +26 -7
  10. package/docs/KNOWN-ISSUES.md +15 -3
  11. package/docs/LIMITATIONS.md +9 -7
  12. package/docs/MIGRATION.md +8 -8
  13. package/docs/SCHEMA.md +29 -12
  14. package/package.json +2 -2
  15. package/packages/cli/dist/index.cjs +9808 -8624
  16. package/packages/cli/dist/index.cjs.map +1 -1
  17. package/packages/cli/dist/index.mjs +9807 -8623
  18. package/packages/cli/dist/index.mjs.map +1 -1
  19. package/packages/core/dist/advanced.cjs +2047 -26
  20. package/packages/core/dist/advanced.cjs.map +1 -1
  21. package/packages/core/dist/advanced.d.cts +346 -196
  22. package/packages/core/dist/advanced.d.ts +346 -196
  23. package/packages/core/dist/advanced.mjs +628 -5
  24. package/packages/core/dist/advanced.mjs.map +1 -1
  25. package/packages/core/dist/checks.d.cts +1 -1
  26. package/packages/core/dist/checks.d.ts +1 -1
  27. package/packages/core/dist/chunk-2ZXEMWGK.mjs +549 -0
  28. package/packages/core/dist/chunk-2ZXEMWGK.mjs.map +1 -0
  29. package/packages/core/dist/{chunk-57S5D6HR.mjs → chunk-4JVGFNQH.mjs} +50 -6
  30. package/packages/core/dist/chunk-4JVGFNQH.mjs.map +1 -0
  31. package/packages/core/dist/{chunk-6QSLZCBJ.mjs → chunk-A4V2H3YU.mjs} +329 -163
  32. package/packages/core/dist/chunk-A4V2H3YU.mjs.map +1 -0
  33. package/packages/core/dist/{chunk-74XZ6N7Q.mjs → chunk-IZBJAZGF.mjs} +12 -4
  34. package/packages/core/dist/chunk-IZBJAZGF.mjs.map +1 -0
  35. package/packages/core/dist/{chunk-VTIB5MDK.mjs → chunk-NQZPZ2FN.mjs} +15 -10
  36. package/packages/core/dist/chunk-NQZPZ2FN.mjs.map +1 -0
  37. package/packages/core/dist/{chunk-TZISEVLQ.mjs → chunk-XUNJMAPD.mjs} +3 -3
  38. package/packages/core/dist/{chunk-TZISEVLQ.mjs.map → chunk-XUNJMAPD.mjs.map} +1 -1
  39. package/packages/core/dist/context-CSKnzpXR.d.cts +177 -0
  40. package/packages/core/dist/context-yv2VSDQF.d.ts +177 -0
  41. package/packages/core/dist/diff.d.cts +2 -2
  42. package/packages/core/dist/diff.d.ts +2 -2
  43. package/packages/core/dist/diff.mjs +473 -3
  44. package/packages/core/dist/diff.mjs.map +1 -1
  45. package/packages/core/dist/exporters.cjs +691 -0
  46. package/packages/core/dist/exporters.cjs.map +1 -1
  47. package/packages/core/dist/exporters.d.cts +24 -3
  48. package/packages/core/dist/exporters.d.ts +24 -3
  49. package/packages/core/dist/exporters.mjs +1113 -3
  50. package/packages/core/dist/exporters.mjs.map +1 -1
  51. package/packages/core/dist/index.cjs +1385 -8168
  52. package/packages/core/dist/index.cjs.map +1 -1
  53. package/packages/core/dist/index.d.cts +8 -350
  54. package/packages/core/dist/index.d.ts +8 -350
  55. package/packages/core/dist/index.mjs +18 -1478
  56. package/packages/core/dist/index.mjs.map +1 -1
  57. package/packages/core/dist/{log-config-DanPV3P9.d.cts → log-config-8aE5Kxtr.d.cts} +1 -1
  58. package/packages/core/dist/{log-config-BPHS4Sds.d.ts → log-config-DVimQJho.d.ts} +1 -1
  59. package/packages/core/dist/logs.d.cts +2 -2
  60. package/packages/core/dist/logs.d.ts +2 -2
  61. package/packages/core/dist/logs.mjs +785 -2
  62. package/packages/core/dist/logs.mjs.map +1 -1
  63. package/packages/core/dist/{persisted-inspect-event-Cw7TeYGr.d.ts → persisted-inspect-event-D-WpXeZX.d.ts} +14 -5
  64. package/packages/core/dist/{persisted-inspect-event-DHPfzUd8.d.cts → persisted-inspect-event-DHcHPUKv.d.cts} +14 -5
  65. package/packages/core/dist/persisted.cjs +11 -1
  66. package/packages/core/dist/persisted.cjs.map +1 -1
  67. package/packages/core/dist/persisted.d.cts +4 -4
  68. package/packages/core/dist/persisted.d.ts +4 -4
  69. package/packages/core/dist/persisted.mjs +150 -3
  70. package/packages/core/dist/persisted.mjs.map +1 -1
  71. package/packages/core/dist/readers.cjs +27 -10
  72. package/packages/core/dist/readers.cjs.map +1 -1
  73. package/packages/core/dist/readers.d.cts +1 -1
  74. package/packages/core/dist/readers.d.ts +1 -1
  75. package/packages/core/dist/readers.mjs +1580 -4
  76. package/packages/core/dist/readers.mjs.map +1 -1
  77. package/packages/core/dist/{types-Ap9uMdx_.d.ts → types-D-Y1kOU-.d.ts} +2 -2
  78. package/packages/core/dist/{types-B2-BU5CS.d.cts → types-DgMN3qow.d.cts} +2 -2
  79. package/packages/core/dist/writers.cjs +49 -3
  80. package/packages/core/dist/writers.cjs.map +1 -1
  81. package/packages/core/dist/writers.d.cts +1 -1
  82. package/packages/core/dist/writers.d.ts +1 -1
  83. package/packages/core/dist/writers.mjs +341 -4
  84. package/packages/core/dist/writers.mjs.map +1 -1
  85. package/packages/core/dist/chunk-57S5D6HR.mjs.map +0 -1
  86. package/packages/core/dist/chunk-6QSLZCBJ.mjs.map +0 -1
  87. package/packages/core/dist/chunk-6SZPTECC.mjs +0 -342
  88. package/packages/core/dist/chunk-6SZPTECC.mjs.map +0 -1
  89. package/packages/core/dist/chunk-74XZ6N7Q.mjs.map +0 -1
  90. package/packages/core/dist/chunk-HR7G62IE.mjs +0 -785
  91. package/packages/core/dist/chunk-HR7G62IE.mjs.map +0 -1
  92. package/packages/core/dist/chunk-TFLPUZ56.mjs +0 -1571
  93. package/packages/core/dist/chunk-TFLPUZ56.mjs.map +0 -1
  94. package/packages/core/dist/chunk-U2BGPESY.mjs +0 -150
  95. package/packages/core/dist/chunk-U2BGPESY.mjs.map +0 -1
  96. package/packages/core/dist/chunk-VTIB5MDK.mjs.map +0 -1
  97. package/packages/core/dist/chunk-Y56BPA3B.mjs +0 -990
  98. package/packages/core/dist/chunk-Y56BPA3B.mjs.map +0 -1
  99. package/packages/core/dist/chunk-YWAOOXLR.mjs +0 -475
  100. package/packages/core/dist/chunk-YWAOOXLR.mjs.map +0 -1
@@ -1,10 +1,15 @@
1
1
  'use strict';
2
2
 
3
3
  var crypto = require('crypto');
4
+ require('fs/promises');
5
+ var os = require('os');
6
+ var path = require('path');
4
7
 
5
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
9
 
7
10
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
11
+ var os__default = /*#__PURE__*/_interopDefault(os);
12
+ var path__default = /*#__PURE__*/_interopDefault(path);
8
13
 
9
14
  // packages/core/src/exporters/types.ts
10
15
  var EXPORT_PAYLOAD_VERSION = "0.1.2";
@@ -282,6 +287,89 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
282
287
  }
283
288
  return bounded;
284
289
  }
290
+ function redactErrorInfo(error, redactor, maxMetadataValueLength, maxPreviewLength) {
291
+ if (error === void 0) return void 0;
292
+ const record = redactEventAttributes(
293
+ { error },
294
+ redactor,
295
+ maxMetadataValueLength,
296
+ maxPreviewLength
297
+ );
298
+ const redacted = record?.error;
299
+ if (!isRecord2(redacted) || typeof redacted.message !== "string") {
300
+ return void 0;
301
+ }
302
+ return {
303
+ message: redacted.message,
304
+ ...typeof redacted.stack === "string" ? { stack: redacted.stack } : {}
305
+ };
306
+ }
307
+ function redactTraceEventsForReport(events, options) {
308
+ const profile = options?.redactionProfile ?? "local";
309
+ if (profile === "local") {
310
+ return deepClone(events);
311
+ }
312
+ const resolved = resolveRedactionProfile(profile);
313
+ const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
314
+ 2e3,
315
+ 500,
316
+ resolved
317
+ );
318
+ const redactor = new Redactor({ extraKeys: resolved.extraKeys });
319
+ return events.map((event) => {
320
+ switch (event.event) {
321
+ case "run_started":
322
+ return {
323
+ ...event,
324
+ name: truncateStringForProfile(
325
+ event.name,
326
+ "name",
327
+ maxMetadataValueLength,
328
+ maxPreviewLength
329
+ ),
330
+ ...event.metadata !== void 0 ? {
331
+ metadata: redactEventAttributes(
332
+ event.metadata,
333
+ redactor,
334
+ maxMetadataValueLength,
335
+ maxPreviewLength
336
+ )
337
+ } : {}
338
+ };
339
+ case "step_started":
340
+ return {
341
+ ...event,
342
+ name: truncateStringForProfile(
343
+ event.name,
344
+ "name",
345
+ maxMetadataValueLength,
346
+ maxPreviewLength
347
+ ),
348
+ ...event.metadata !== void 0 ? {
349
+ metadata: redactEventAttributes(
350
+ event.metadata,
351
+ redactor,
352
+ maxMetadataValueLength,
353
+ maxPreviewLength
354
+ )
355
+ } : {}
356
+ };
357
+ case "run_completed":
358
+ case "step_completed":
359
+ return {
360
+ ...event,
361
+ ...event.error !== void 0 ? {
362
+ error: redactErrorInfo(
363
+ event.error,
364
+ redactor,
365
+ maxMetadataValueLength,
366
+ maxPreviewLength
367
+ )
368
+ } : {}
369
+ };
370
+ }
371
+ });
372
+ }
285
373
  function redactRunTreeForExport(tree, options) {
286
374
  const profile = options?.redactionProfile ?? "local";
287
375
  if (profile === "local") {
@@ -1206,7 +1294,610 @@ function validateExport(result) {
1206
1294
  };
1207
1295
  }
1208
1296
 
1297
+ // packages/core/src/utils/duration.ts
1298
+ function formatDuration(ms) {
1299
+ if (!Number.isFinite(ms)) {
1300
+ return "0ms";
1301
+ }
1302
+ if (ms < 0) {
1303
+ throw new Error(`formatDuration: ms must be non-negative (got ${ms})`);
1304
+ }
1305
+ if (ms < 1e3) {
1306
+ return `${Math.floor(ms)}ms`;
1307
+ }
1308
+ if (ms < 6e4) {
1309
+ return `${(ms / 1e3).toFixed(2)}s`;
1310
+ }
1311
+ if (ms < 36e5) {
1312
+ return `${(ms / 6e4).toFixed(1)}m`;
1313
+ }
1314
+ return `${(ms / 36e5).toFixed(1)}h`;
1315
+ }
1316
+
1317
+ // packages/core/src/utils.ts
1318
+ var RUNS_DIR_NAME = "runs";
1319
+ path__default.default.join(
1320
+ os__default.default.tmpdir(),
1321
+ "agent-inspect",
1322
+ RUNS_DIR_NAME
1323
+ );
1324
+ function formatDuration2(ms) {
1325
+ return formatDuration(ms);
1326
+ }
1327
+ function formatTimestamp(timestamp) {
1328
+ if (!Number.isFinite(timestamp)) {
1329
+ return "Invalid date";
1330
+ }
1331
+ const d = new Date(timestamp);
1332
+ if (Number.isNaN(d.getTime())) {
1333
+ return "Invalid date";
1334
+ }
1335
+ const y = d.getFullYear();
1336
+ const mo = String(d.getMonth() + 1).padStart(2, "0");
1337
+ const day = String(d.getDate()).padStart(2, "0");
1338
+ const h = String(d.getHours()).padStart(2, "0");
1339
+ const min = String(d.getMinutes()).padStart(2, "0");
1340
+ const s = String(d.getSeconds()).padStart(2, "0");
1341
+ return `${y}-${mo}-${day} ${h}:${min}:${s}`;
1342
+ }
1343
+
1344
+ // packages/core/src/timeline.ts
1345
+ function finite(n) {
1346
+ return typeof n === "number" && Number.isFinite(n);
1347
+ }
1348
+ function pickStreamingMeta(metadata) {
1349
+ if (!metadata || typeof metadata !== "object") return void 0;
1350
+ const chunkCount = metadata.chunkCount;
1351
+ const streamDurationMs = metadata.streamDurationMs;
1352
+ const streamedCharCount = metadata.streamedCharCount;
1353
+ if (!finite(chunkCount) && !finite(streamDurationMs) && !finite(streamedCharCount)) {
1354
+ return void 0;
1355
+ }
1356
+ return {
1357
+ ...finite(chunkCount) ? { chunkCount } : {},
1358
+ ...finite(streamDurationMs) ? { streamDurationMs } : {},
1359
+ ...finite(streamedCharCount) ? { streamedCharCount } : {}
1360
+ };
1361
+ }
1362
+ function pickCorrelation(metadata) {
1363
+ if (!metadata || typeof metadata !== "object") return void 0;
1364
+ const out = {};
1365
+ for (const key of [
1366
+ "correlationId",
1367
+ "requestId",
1368
+ "decisionId",
1369
+ "groupId"
1370
+ ]) {
1371
+ const v = metadata[key];
1372
+ if (typeof v === "string" && v.trim() !== "") {
1373
+ out[key] = v;
1374
+ }
1375
+ }
1376
+ return Object.keys(out).length > 0 ? out : void 0;
1377
+ }
1378
+ function buildRunTimeline(events, options = {}) {
1379
+ const started = events.find(
1380
+ (e) => e.event === "run_started"
1381
+ );
1382
+ const completed = events.filter(
1383
+ (e) => e.event === "run_completed"
1384
+ );
1385
+ const lastCompleted = completed[completed.length - 1];
1386
+ const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
1387
+ const runStart = started && finite(started.startTime) ? started.startTime : started && finite(started.timestamp) ? started.timestamp : void 0;
1388
+ const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
1389
+ const steps = /* @__PURE__ */ new Map();
1390
+ for (const e of events) {
1391
+ if (e.event === "step_started") {
1392
+ const s = e;
1393
+ steps.set(s.stepId, {
1394
+ name: s.name,
1395
+ type: s.type,
1396
+ parentId: s.parentId,
1397
+ startedAt: finite(s.startTime) ? s.startTime : s.timestamp,
1398
+ status: "running",
1399
+ metadata: s.metadata
1400
+ });
1401
+ }
1402
+ }
1403
+ for (const e of events) {
1404
+ if (e.event !== "step_completed") continue;
1405
+ const c = e;
1406
+ const node = steps.get(c.stepId);
1407
+ if (!node) continue;
1408
+ node.status = c.status;
1409
+ if (finite(c.durationMs)) node.durationMs = c.durationMs;
1410
+ }
1411
+ const depthCache = /* @__PURE__ */ new Map();
1412
+ const computeDepth = (stepId) => {
1413
+ const cached = depthCache.get(stepId);
1414
+ if (cached !== void 0) return cached;
1415
+ const node = steps.get(stepId);
1416
+ if (!node) return 0;
1417
+ const parent = node.parentId;
1418
+ if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
1419
+ depthCache.set(stepId, 0);
1420
+ return 0;
1421
+ }
1422
+ const d = Math.min(1e3, computeDepth(parent) + 1);
1423
+ depthCache.set(stepId, d);
1424
+ return d;
1425
+ };
1426
+ const entries = [];
1427
+ for (const [stepId, s] of steps.entries()) {
1428
+ const offsetMs = runStart !== void 0 && finite(s.startedAt) ? Math.max(0, s.startedAt - runStart) : 0;
1429
+ entries.push({
1430
+ stepId,
1431
+ name: s.name,
1432
+ type: s.type,
1433
+ status: s.status,
1434
+ depth: computeDepth(stepId),
1435
+ startedAt: s.startedAt,
1436
+ offsetMs,
1437
+ durationMs: s.durationMs,
1438
+ isError: s.status === "error",
1439
+ streaming: pickStreamingMeta(s.metadata)
1440
+ });
1441
+ }
1442
+ entries.sort((a, b) => a.startedAt - b.startedAt);
1443
+ const slowTopN = options.slowTopN ?? 3;
1444
+ if (options.focus === "slow" && entries.length > 0) {
1445
+ const ranked = [...entries].filter((e) => finite(e.durationMs)).sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
1446
+ const slowIds = new Set(
1447
+ ranked.slice(0, slowTopN).map((e) => e.stepId)
1448
+ );
1449
+ for (const e of entries) {
1450
+ if (slowIds.has(e.stepId)) e.slow = true;
1451
+ }
1452
+ }
1453
+ return {
1454
+ runId,
1455
+ name: typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0,
1456
+ status,
1457
+ startedAt: runStart,
1458
+ endedAt: lastCompleted && finite(lastCompleted.endTime) ? lastCompleted.endTime : void 0,
1459
+ durationMs: lastCompleted && finite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0,
1460
+ correlation: pickCorrelation(
1461
+ started?.metadata
1462
+ ),
1463
+ entries
1464
+ };
1465
+ }
1466
+ function renderTimeline(timeline, options = {}) {
1467
+ const lines = [];
1468
+ lines.push(`Timeline: ${timeline.name ?? timeline.runId}`);
1469
+ lines.push(`Run ID: ${timeline.runId}`);
1470
+ lines.push(`Status: ${timeline.status}`);
1471
+ if (timeline.startedAt !== void 0) {
1472
+ lines.push(`Started: ${formatTimestamp(timeline.startedAt)}`);
1473
+ }
1474
+ if (timeline.durationMs !== void 0) {
1475
+ lines.push(`Duration: ${formatDuration2(timeline.durationMs)}`);
1476
+ }
1477
+ if (timeline.correlation) {
1478
+ const parts = Object.entries(timeline.correlation).filter(([, v]) => typeof v === "string").map(([k, v]) => `${k}=${v}`);
1479
+ if (parts.length > 0) {
1480
+ lines.push(`Correlation: ${parts.join(", ")}`);
1481
+ }
1482
+ }
1483
+ lines.push("");
1484
+ lines.push("Steps (chronological):");
1485
+ const show = timeline.entries.filter((e) => {
1486
+ if (options.focus === "slow") return e.slow === true;
1487
+ return true;
1488
+ });
1489
+ if (show.length === 0) {
1490
+ lines.push(
1491
+ options.focus === "slow" ? "(no steps with duration for slow focus)" : "(no steps)"
1492
+ );
1493
+ return lines.join("\n");
1494
+ }
1495
+ for (const e of show) {
1496
+ const prefix = e.slow ? "[slow] " : "";
1497
+ const typeTag = e.type === "llm" ? "llm" : e.type === "tool" ? "tool" : e.type;
1498
+ const dur = e.durationMs !== void 0 ? formatDuration2(e.durationMs) : "-";
1499
+ const err = e.isError ? " error" : "";
1500
+ const off = formatDuration2(e.offsetMs);
1501
+ let line = `${prefix}+${off} ${typeTag}:${e.name} (${dur})${err}`;
1502
+ if (e.streaming?.chunkCount !== void 0) {
1503
+ line += ` chunks=${e.streaming.chunkCount}`;
1504
+ }
1505
+ if (e.streaming?.streamDurationMs !== void 0) {
1506
+ line += ` stream=${formatDuration2(e.streaming.streamDurationMs)}`;
1507
+ }
1508
+ lines.push(line);
1509
+ }
1510
+ return lines.join("\n");
1511
+ }
1512
+ function isFiniteNumber(v) {
1513
+ return typeof v === "number" && Number.isFinite(v);
1514
+ }
1515
+ function isNonNegativeFiniteNumber(value) {
1516
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
1517
+ }
1518
+ function buildRunSummary(events) {
1519
+ const started = events.find(
1520
+ (e) => e.event === "run_started"
1521
+ );
1522
+ const completed = events.filter(
1523
+ (e) => e.event === "run_completed"
1524
+ );
1525
+ const lastCompleted = completed[completed.length - 1];
1526
+ const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
1527
+ const name = typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0;
1528
+ const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
1529
+ const durationMs = lastCompleted && isFiniteNumber(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
1530
+ started && isFiniteNumber(started.startTime) ? started.startTime : void 0;
1531
+ const steps = /* @__PURE__ */ new Map();
1532
+ for (const e of events) {
1533
+ if (e.event === "step_started") {
1534
+ const s = e;
1535
+ steps.set(s.stepId, {
1536
+ type: s.type,
1537
+ name: s.name,
1538
+ status: "running",
1539
+ parentId: s.parentId,
1540
+ tokensInput: isNonNegativeFiniteNumber(s.metadata?.tokens?.input) ? s.metadata.tokens.input : void 0,
1541
+ tokensOutput: isNonNegativeFiniteNumber(s.metadata?.tokens?.output) ? s.metadata.tokens.output : void 0,
1542
+ tokensTotal: isNonNegativeFiniteNumber(s.metadata?.tokens?.total) ? s.metadata.tokens.total : void 0,
1543
+ tokensCached: isNonNegativeFiniteNumber(s.metadata?.tokens?.cached) ? s.metadata.tokens.cached : void 0
1544
+ });
1545
+ }
1546
+ }
1547
+ for (const e of events) {
1548
+ if (e.event === "step_completed") {
1549
+ const c = e;
1550
+ const existing = steps.get(c.stepId);
1551
+ if (!existing) continue;
1552
+ existing.status = c.status;
1553
+ existing.durationMs = c.durationMs;
1554
+ }
1555
+ }
1556
+ let totalSteps = 0;
1557
+ let llmSteps = 0;
1558
+ let toolSteps = 0;
1559
+ let logicSteps = 0;
1560
+ let errorSteps = 0;
1561
+ let maxDepth = 0;
1562
+ let longestStep;
1563
+ let totalTokensInput = 0;
1564
+ let totalTokensOutput = 0;
1565
+ let totalTokensTotal = 0;
1566
+ let totalTokensCached = 0;
1567
+ let tokenBearingSteps = 0;
1568
+ let stepsWithKnownTotal = 0;
1569
+ let hasCachedTokens = false;
1570
+ const depthCache = /* @__PURE__ */ new Map();
1571
+ const computeDepth = (stepId) => {
1572
+ const cached = depthCache.get(stepId);
1573
+ if (cached !== void 0) return cached;
1574
+ const node = steps.get(stepId);
1575
+ if (!node) return 0;
1576
+ const parent = node.parentId;
1577
+ if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
1578
+ depthCache.set(stepId, 0);
1579
+ return 0;
1580
+ }
1581
+ const d = Math.min(1e3, computeDepth(parent) + 1);
1582
+ depthCache.set(stepId, d);
1583
+ return d;
1584
+ };
1585
+ for (const [id, s] of steps.entries()) {
1586
+ totalSteps += 1;
1587
+ if (s.type === "llm") llmSteps += 1;
1588
+ else if (s.type === "tool") toolSteps += 1;
1589
+ else logicSteps += 1;
1590
+ if (s.status === "error") errorSteps += 1;
1591
+ const depth = computeDepth(id);
1592
+ if (depth > maxDepth) maxDepth = depth;
1593
+ if (typeof s.durationMs === "number" && Number.isFinite(s.durationMs)) {
1594
+ if (!longestStep || s.durationMs > longestStep.durationMs) {
1595
+ longestStep = { name: s.name, durationMs: s.durationMs, type: s.type };
1596
+ }
1597
+ }
1598
+ if (s.tokensInput !== void 0 || s.tokensOutput !== void 0 || s.tokensTotal !== void 0 || s.tokensCached !== void 0) {
1599
+ tokenBearingSteps += 1;
1600
+ if (s.tokensInput !== void 0) totalTokensInput += s.tokensInput;
1601
+ if (s.tokensOutput !== void 0) totalTokensOutput += s.tokensOutput;
1602
+ if (s.tokensTotal !== void 0) {
1603
+ totalTokensTotal += s.tokensTotal;
1604
+ stepsWithKnownTotal += 1;
1605
+ } else if (s.tokensInput !== void 0 && s.tokensOutput !== void 0) {
1606
+ totalTokensTotal += s.tokensInput + s.tokensOutput;
1607
+ stepsWithKnownTotal += 1;
1608
+ }
1609
+ if (s.tokensCached !== void 0) {
1610
+ totalTokensCached += s.tokensCached;
1611
+ hasCachedTokens = true;
1612
+ }
1613
+ }
1614
+ }
1615
+ const summary = {
1616
+ runId,
1617
+ name,
1618
+ status,
1619
+ durationMs,
1620
+ totalSteps,
1621
+ llmSteps,
1622
+ toolSteps,
1623
+ logicSteps,
1624
+ errorSteps,
1625
+ maxDepth,
1626
+ ...longestStep ? { longestStep } : {},
1627
+ ...tokenBearingSteps > 0 ? {
1628
+ totalTokens: {
1629
+ input: totalTokensInput,
1630
+ output: totalTokensOutput,
1631
+ ...stepsWithKnownTotal === tokenBearingSteps ? { total: totalTokensTotal } : {},
1632
+ ...hasCachedTokens ? { cached: totalTokensCached } : {}
1633
+ }
1634
+ } : {}
1635
+ };
1636
+ return summary;
1637
+ }
1638
+
1639
+ // packages/core/src/what.ts
1640
+ function pickCorrelation2(metadata) {
1641
+ if (!metadata) return void 0;
1642
+ const out = {};
1643
+ for (const key of [
1644
+ "correlationId",
1645
+ "requestId",
1646
+ "decisionId",
1647
+ "groupId"
1648
+ ]) {
1649
+ const value = metadata[key];
1650
+ if (typeof value === "string" && value.trim() !== "") {
1651
+ out[key] = value;
1652
+ }
1653
+ }
1654
+ return Object.keys(out).length > 0 ? out : void 0;
1655
+ }
1656
+ function stepMixLine(summary) {
1657
+ const parts = [];
1658
+ if (summary.llmSteps > 0) parts.push(`${summary.llmSteps} LLM`);
1659
+ if (summary.toolSteps > 0) parts.push(`${summary.toolSteps} tool`);
1660
+ if (summary.logicSteps > 0) parts.push(`${summary.logicSteps} logic`);
1661
+ return parts.length > 0 ? parts.join(", ") : "none";
1662
+ }
1663
+ function outcomeLine(summary) {
1664
+ if (summary.status === "success") {
1665
+ return summary.errorSteps > 0 ? "Completed with step errors recorded." : "Completed successfully.";
1666
+ }
1667
+ if (summary.status === "error") {
1668
+ if (summary.failedStepNames.length > 0) {
1669
+ const names = summary.failedStepNames.slice(0, 3).join(", ");
1670
+ const suffix = summary.failedStepNames.length > 3 ? ` (+${summary.failedStepNames.length - 3} more)` : "";
1671
+ return `Failed at step(s): ${names}${suffix}.`;
1672
+ }
1673
+ if (summary.runErrorMessage) {
1674
+ return `Run failed: ${summary.runErrorMessage}`;
1675
+ }
1676
+ return "Run failed.";
1677
+ }
1678
+ if (summary.status === "running") {
1679
+ return "Run is still in progress (no run_completed).";
1680
+ }
1681
+ return "Outcome unknown \u2014 inspect events may be incomplete.";
1682
+ }
1683
+ function buildRunWhatSummary(events) {
1684
+ const base = buildRunSummary(events);
1685
+ const started = events.find(
1686
+ (e) => e.event === "run_started"
1687
+ );
1688
+ const completed = events.filter(
1689
+ (e) => e.event === "run_completed"
1690
+ );
1691
+ const lastCompleted = completed[completed.length - 1];
1692
+ const failedStepNames = [];
1693
+ const stepNames = /* @__PURE__ */ new Map();
1694
+ for (const e of events) {
1695
+ if (e.event === "step_started") {
1696
+ const s = e;
1697
+ stepNames.set(s.stepId, s.name);
1698
+ }
1699
+ }
1700
+ for (const e of events) {
1701
+ if (e.event === "step_completed") {
1702
+ const sc = e;
1703
+ if (sc.status === "error") {
1704
+ failedStepNames.push(stepNames.get(sc.stepId) ?? sc.stepId);
1705
+ }
1706
+ }
1707
+ }
1708
+ return {
1709
+ runId: base.runId,
1710
+ name: base.name,
1711
+ status: base.status,
1712
+ durationMs: base.durationMs,
1713
+ totalSteps: base.totalSteps,
1714
+ llmSteps: base.llmSteps,
1715
+ toolSteps: base.toolSteps,
1716
+ logicSteps: base.logicSteps,
1717
+ errorSteps: base.errorSteps,
1718
+ maxDepth: base.maxDepth,
1719
+ longestStep: base.longestStep,
1720
+ totalTokens: base.totalTokens,
1721
+ correlation: pickCorrelation2(started?.metadata),
1722
+ failedStepNames,
1723
+ runErrorMessage: lastCompleted?.error?.message
1724
+ };
1725
+ }
1726
+ function renderRunWhat(summary, options = {}) {
1727
+ const showCorrelation = options.correlation !== false;
1728
+ const lines = [];
1729
+ const label = summary.name ?? summary.runId;
1730
+ lines.push(`What: ${label}`);
1731
+ const duration = summary.durationMs !== void 0 ? formatDuration2(summary.durationMs) : "\u2014";
1732
+ lines.push(
1733
+ `Status: ${summary.status} \xB7 Duration: ${duration} \xB7 Steps: ${summary.totalSteps} (${stepMixLine(summary)})`
1734
+ );
1735
+ if (summary.totalTokens) {
1736
+ const tokenParts = [
1737
+ `${summary.totalTokens.input} in`,
1738
+ `${summary.totalTokens.output} out`
1739
+ ];
1740
+ if (summary.totalTokens.total !== void 0) {
1741
+ tokenParts.push(`${summary.totalTokens.total} total`);
1742
+ }
1743
+ if (summary.totalTokens.cached !== void 0) {
1744
+ tokenParts.push(`${summary.totalTokens.cached} cached`);
1745
+ }
1746
+ lines.push(`Tokens: ${tokenParts.join(" / ")}`);
1747
+ }
1748
+ if (showCorrelation && summary.correlation) {
1749
+ const parts = [];
1750
+ if (summary.correlation.correlationId) {
1751
+ parts.push(`correlationId=${summary.correlation.correlationId}`);
1752
+ }
1753
+ if (summary.correlation.requestId) {
1754
+ parts.push(`requestId=${summary.correlation.requestId}`);
1755
+ }
1756
+ if (summary.correlation.decisionId) {
1757
+ parts.push(`decisionId=${summary.correlation.decisionId}`);
1758
+ }
1759
+ if (summary.correlation.groupId) {
1760
+ parts.push(`groupId=${summary.correlation.groupId}`);
1761
+ }
1762
+ if (parts.length > 0) {
1763
+ lines.push(`Correlation: ${parts.join(", ")}`);
1764
+ }
1765
+ }
1766
+ lines.push(`Outcome: ${outcomeLine(summary)}`);
1767
+ if (summary.longestStep && summary.totalSteps > 0) {
1768
+ lines.push(
1769
+ `Slowest: ${summary.longestStep.name} (${formatDuration2(summary.longestStep.durationMs)}, ${summary.longestStep.type})`
1770
+ );
1771
+ }
1772
+ if (summary.maxDepth > 0) {
1773
+ lines.push(`Max depth: ${summary.maxDepth}`);
1774
+ }
1775
+ return lines.join("\n");
1776
+ }
1777
+
1778
+ // packages/core/src/report.ts
1779
+ function resolveTree(events, profile) {
1780
+ const tree = manualTraceEventsToRunTree(events);
1781
+ return profile === "local" ? tree : redactRunTreeForExport(tree, { redactionProfile: profile });
1782
+ }
1783
+ function tailMarkdownSection(content, heading) {
1784
+ const marker = `## ${heading}`;
1785
+ const idx = content.indexOf(marker);
1786
+ if (idx < 0) return "";
1787
+ return content.slice(idx).trimEnd();
1788
+ }
1789
+ function extractHtmlFragment(content, start, end) {
1790
+ const startIdx = content.indexOf(start);
1791
+ if (startIdx < 0) return "";
1792
+ const endIdx = content.indexOf(end, startIdx + start.length);
1793
+ if (endIdx < 0) return content.slice(startIdx);
1794
+ return content.slice(startIdx, endIdx);
1795
+ }
1796
+ var REPORT_HTML_CSS = `
1797
+ body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}
1798
+ h1{font-size:1.35rem}
1799
+ h2{font-size:1.1rem;margin-top:1.5rem}
1800
+ pre{white-space:pre-wrap;background:#f8f8f8;padding:0.75rem;overflow:auto;font-size:0.9rem}
1801
+ ul.tree{list-style:none;padding-left:1rem}
1802
+ ul.tree.nested{padding-left:1.25rem;border-left:1px solid #ddd;margin:0.25rem 0}
1803
+ .nm{font-weight:600}
1804
+ .meta{color:#555;font-size:0.9rem}
1805
+ footer{margin-top:2rem;font-size:0.85rem;color:#555}
1806
+ `.trim();
1807
+ function buildRunReport(events, options) {
1808
+ const profile = options.redactionProfile ?? "local";
1809
+ const safeEvents = redactTraceEventsForReport(events, {
1810
+ redactionProfile: profile
1811
+ });
1812
+ const whatSummary = buildRunWhatSummary(safeEvents);
1813
+ const whatText = renderRunWhat(whatSummary, {
1814
+ correlation: options.correlation !== false
1815
+ });
1816
+ const timelineText = renderTimeline(buildRunTimeline(safeEvents));
1817
+ const tree = resolveTree(safeEvents, profile);
1818
+ const exportOpts = {
1819
+ includeMetadata: false,
1820
+ includeAttributes: options.includeAttributes === true,
1821
+ includeErrors: options.includeErrors !== false,
1822
+ redacted: true,
1823
+ maxAttributeLength: 500
1824
+ };
1825
+ if (options.format === "markdown") {
1826
+ const treeMd = exportMarkdown(tree, exportOpts);
1827
+ const tail = tailMarkdownSection(treeMd.content, "Execution tree");
1828
+ const title2 = escapeMarkdown(whatSummary.name ?? whatSummary.runId);
1829
+ const lines = [
1830
+ `# AgentInspect Report: ${title2}`,
1831
+ "",
1832
+ "Generated locally by AgentInspect. Review for sensitive data before sharing.",
1833
+ "",
1834
+ "## What happened",
1835
+ "",
1836
+ "```text",
1837
+ whatText,
1838
+ "```",
1839
+ "",
1840
+ "## Timeline",
1841
+ "",
1842
+ "```text",
1843
+ timelineText,
1844
+ "```",
1845
+ ""
1846
+ ];
1847
+ if (tail) {
1848
+ lines.push(tail, "");
1849
+ } else {
1850
+ lines.push("## Execution tree", "", "(no steps)", "");
1851
+ }
1852
+ return {
1853
+ format: "markdown",
1854
+ content: lines.join("\n"),
1855
+ contentType: "text/markdown",
1856
+ fileExtension: ".md"
1857
+ };
1858
+ }
1859
+ const treeHtml = exportHtml(tree, exportOpts);
1860
+ const title = escapeHtml(whatSummary.name ?? whatSummary.runId);
1861
+ const treeSection = extractHtmlFragment(
1862
+ treeHtml.content,
1863
+ '<section class="tree">',
1864
+ "</section>"
1865
+ );
1866
+ const errorsSection = treeHtml.content.includes("<h2>Errors</h2>") ? extractHtmlFragment(treeHtml.content, "<h2>Errors</h2>", "<footer>") : "";
1867
+ const attrsSection = treeHtml.content.includes("<h2>Attributes (bounded)</h2>") ? extractHtmlFragment(
1868
+ treeHtml.content,
1869
+ "<h2>Attributes (bounded)</h2>",
1870
+ "<footer>"
1871
+ ) : "";
1872
+ const html = `<!doctype html>
1873
+ <html lang="en">
1874
+ <head>
1875
+ <meta charset="utf-8"/>
1876
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
1877
+ <title>AgentInspect Report: ${title}</title>
1878
+ <style>${REPORT_HTML_CSS}</style>
1879
+ </head>
1880
+ <body>
1881
+ <header><h1>AgentInspect Report: ${title}</h1></header>
1882
+ <p class="note">Generated locally by AgentInspect. Review for sensitive data before sharing.</p>
1883
+ <section class="what"><h2>What happened</h2><pre>${escapeHtml(whatText)}</pre></section>
1884
+ <section class="timeline"><h2>Timeline</h2><pre>${escapeHtml(timelineText)}</pre></section>
1885
+ ${treeSection || '<section class="tree"><h2>Execution tree</h2><p>No steps recorded.</p></section>'}
1886
+ ${errorsSection}
1887
+ ${attrsSection}
1888
+ <footer>Generated locally by AgentInspect. Review for sensitive data before sharing.</footer>
1889
+ </body>
1890
+ </html>`;
1891
+ return {
1892
+ format: "html",
1893
+ content: html,
1894
+ contentType: "text/html",
1895
+ fileExtension: ".html"
1896
+ };
1897
+ }
1898
+
1209
1899
  exports.EXPORT_PAYLOAD_VERSION = EXPORT_PAYLOAD_VERSION;
1900
+ exports.buildRunReport = buildRunReport;
1210
1901
  exports.compactAttributes = compactAttributes;
1211
1902
  exports.escapeHtml = escapeHtml;
1212
1903
  exports.escapeMarkdown = escapeMarkdown;