@agent-inspect/mcp-server 4.3.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -7,6 +7,7 @@ import os from 'os';
7
7
  import 'nanoid';
8
8
  import 'chalk';
9
9
  import 'fs';
10
+ import 'url';
10
11
 
11
12
  // packages/mcp-server/src/index.ts
12
13
 
@@ -70,6 +71,9 @@ function isTraceEvent(value) {
70
71
  case "step_completed": {
71
72
  return typeof value.runId === "string" && typeof value.stepId === "string" && (value.status === "success" || value.status === "error") && typeof value.endTime === "number" && typeof value.durationMs === "number";
72
73
  }
74
+ case "outcome_observed": {
75
+ return typeof value.runId === "string" && typeof value.outcomeId === "string" && typeof value.name === "string" && typeof value.expectation === "string" && (value.status === "passed" || value.status === "failed" || value.status === "unknown" || value.status === "skipped") && typeof value.observedAt === "number";
76
+ }
73
77
  default:
74
78
  return false;
75
79
  }
@@ -87,7 +91,8 @@ var INSPECT_KINDS = [
87
91
  "RESULT",
88
92
  "ERROR",
89
93
  "LOGIC",
90
- "LOG"
94
+ "LOG",
95
+ "OUTCOME"
91
96
  ];
92
97
  var ATTRIBUTION_CONFIDENCES = [
93
98
  "explicit",
@@ -379,6 +384,32 @@ function fromLegacyStepCompleted(event) {
379
384
  if (error) out.error = error;
380
385
  return out;
381
386
  }
387
+ function fromLegacyOutcomeObserved(event) {
388
+ const attrs = event.attributes ?? {};
389
+ const observedAtRaw = attrs.observedAt;
390
+ const observedAt = typeof observedAtRaw === "string" ? Date.parse(observedAtRaw) : typeof observedAtRaw === "number" && Number.isFinite(observedAtRaw) ? observedAtRaw : resolveTimes(event).timestamp;
391
+ const status = attrs.outcomeStatus;
392
+ const out = {
393
+ schemaVersion: "0.1",
394
+ event: "outcome_observed",
395
+ timestamp: observedAt,
396
+ runId: event.runId,
397
+ outcomeId: typeof attrs.outcomeId === "string" ? attrs.outcomeId : event.eventId,
398
+ name: event.name,
399
+ expectation: typeof attrs.expectation === "string" ? attrs.expectation : event.name,
400
+ status: status === "passed" || status === "failed" || status === "unknown" || status === "skipped" ? status : "unknown",
401
+ observedAt
402
+ };
403
+ if (event.parentId !== void 0) out.parentId = event.parentId;
404
+ if (typeof attrs.method === "string") out.method = attrs.method;
405
+ if (attrs.actual !== void 0) out.actual = attrs.actual;
406
+ if (event.outputSummary !== void 0) out.actual = event.outputSummary;
407
+ if (attrs.evidence !== void 0) out.evidence = attrs.evidence;
408
+ return out;
409
+ }
410
+ function fromNativeOutcome(event) {
411
+ return [fromLegacyOutcomeObserved(event)];
412
+ }
382
413
  function fromNativeRun(event) {
383
414
  const { timestamp, startTime, endTime } = resolveTimes(event);
384
415
  const runStatus = mapPersistedStatusToRunStatus(event.status);
@@ -466,9 +497,13 @@ function persistedInspectEventToTraceEvents(event) {
466
497
  if (legacyEvent === "run_completed") return [fromLegacyRunCompleted(event)];
467
498
  if (legacyEvent === "step_started") return [fromLegacyStepStarted(event)];
468
499
  if (legacyEvent === "step_completed") return [fromLegacyStepCompleted(event)];
500
+ if (legacyEvent === "outcome_observed") return [fromLegacyOutcomeObserved(event)];
469
501
  if (event.kind === "RUN") {
470
502
  return fromNativeRun(event);
471
503
  }
504
+ if (event.kind === "OUTCOME") {
505
+ return fromNativeOutcome(event);
506
+ }
472
507
  return fromNativeStep(event);
473
508
  }
474
509
  function persistedInspectEventsToTraceEvents(events, options) {
@@ -703,6 +738,9 @@ function validateEvent(event) {
703
738
  case "step_completed": {
704
739
  return nonEmptyString(event.runId) && nonEmptyString(event.stepId) && (event.status === "success" || event.status === "error") && finiteNumber(event.endTime) && finiteNumber(event.durationMs) && optionalErrorInfo(event.error);
705
740
  }
741
+ case "outcome_observed": {
742
+ return nonEmptyString(event.runId) && nonEmptyString(event.outcomeId) && nonEmptyString(event.name) && nonEmptyString(event.expectation) && (event.status === "passed" || event.status === "failed" || event.status === "unknown" || event.status === "skipped") && finiteNumber(event.observedAt);
743
+ }
706
744
  default:
707
745
  return false;
708
746
  }
@@ -722,6 +760,55 @@ async function readTraceEventsFromFile(filePath) {
722
760
 
723
761
  // packages/core/src/context.ts
724
762
  new AsyncLocalStorage();
763
+
764
+ // packages/core/src/outcomes/types.ts
765
+ var OBSERVED_OUTCOME_STATUSES = [
766
+ "passed",
767
+ "failed",
768
+ "unknown",
769
+ "skipped"
770
+ ];
771
+ var OUTCOME_LEGACY_EVENT = "outcome_observed";
772
+
773
+ // packages/core/src/outcomes/validate.ts
774
+ function parseObservedOutcomeStatus(value) {
775
+ const trimmed = value.trim().toLowerCase();
776
+ if (OBSERVED_OUTCOME_STATUSES.includes(trimmed)) {
777
+ return trimmed;
778
+ }
779
+ throw new Error(
780
+ `Unsupported observation status "${value}". Use passed, failed, unknown, or skipped.`
781
+ );
782
+ }
783
+
784
+ // packages/core/src/outcomes/extract.ts
785
+ function fromOutcomeObservedEvent(event) {
786
+ return {
787
+ outcomeId: event.outcomeId,
788
+ runId: event.runId,
789
+ ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
790
+ name: event.name,
791
+ expectation: event.expectation,
792
+ status: event.status,
793
+ ...event.method !== void 0 ? { method: event.method } : {},
794
+ ...event.actual !== void 0 ? { actual: event.actual } : {},
795
+ ...event.evidence !== void 0 ? { evidence: event.evidence } : {},
796
+ observedAt: event.observedAt
797
+ };
798
+ }
799
+ function extractOutcomesFromTraceEvents(events) {
800
+ const out = [];
801
+ for (const event of events) {
802
+ if (event.event === OUTCOME_LEGACY_EVENT) {
803
+ out.push(fromOutcomeObservedEvent(event));
804
+ }
805
+ }
806
+ return out.sort((a, b) => a.observedAt - b.observedAt || a.name.localeCompare(b.name));
807
+ }
808
+ function parseObservationFilter(value) {
809
+ if (value === void 0 || value.trim() === "") return void 0;
810
+ return parseObservedOutcomeStatus(value);
811
+ }
725
812
  function resolveTraceDir(options = {}) {
726
813
  if (typeof options.dir === "string" && options.dir.trim() !== "") {
727
814
  return options.dir.trim();
@@ -1069,8 +1156,9 @@ async function searchTraces(metas, options) {
1069
1156
  }
1070
1157
  const limit = options.limit;
1071
1158
  const sessionId = options.session?.trim();
1159
+ const observationStatus = parseObservationFilter(options.observation);
1072
1160
  const hasContentFilter = Boolean(
1073
- options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter
1161
+ options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus
1074
1162
  );
1075
1163
  const results = [];
1076
1164
  const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
@@ -1115,6 +1203,22 @@ async function searchTraces(metas, options) {
1115
1203
  statusFilter: options.status
1116
1204
  });
1117
1205
  results.push(...stepMatches);
1206
+ if (observationStatus) {
1207
+ const outcomes = extractOutcomesFromTraceEvents(events);
1208
+ const matched = outcomes.filter((outcome) => outcome.status === observationStatus);
1209
+ for (const outcome of matched) {
1210
+ results.push({
1211
+ runId: m.runId,
1212
+ runName: m.name,
1213
+ runStatus: m.status,
1214
+ stepName: outcome.name,
1215
+ timestamp: outcome.observedAt,
1216
+ matchReason: `observation status=${outcome.status}`,
1217
+ matchedFields: ["outcome.status", "outcome.name"],
1218
+ filePath: m.filePath
1219
+ });
1220
+ }
1221
+ }
1118
1222
  }
1119
1223
  results.sort((a, b) => {
1120
1224
  const ta = a.timestamp ?? 0;
@@ -1421,7 +1525,7 @@ function summarize(findings, diagnostics) {
1421
1525
  errors: diagnostics.filter((item) => item.severity === "error").length
1422
1526
  };
1423
1527
  }
1424
- function eventEvidence(event, path8) {
1528
+ function eventEvidence(event, path12) {
1425
1529
  return {
1426
1530
  runId: event.runId,
1427
1531
  eventId: event.eventId,
@@ -1532,926 +1636,474 @@ function runTraceChecks(input, options = {}) {
1532
1636
  };
1533
1637
  }
1534
1638
 
1535
- // packages/core/src/diff/comparable.ts
1536
- function extractOutputPreview(meta) {
1537
- if (meta === void 0) return void 0;
1538
- if ("outputPreview" in meta) return meta.outputPreview;
1539
- if ("resultPreview" in meta) return meta.resultPreview;
1540
- return void 0;
1639
+ // packages/core/src/persisted/token-usage.ts
1640
+ function isRecord5(value) {
1641
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1541
1642
  }
1542
- function mapStepStatus(s) {
1543
- if (s === void 0) return "running";
1544
- return s;
1643
+ function nonNegativeFinite(value) {
1644
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1545
1645
  }
1546
- function manualTraceEventsToComparableRun(events) {
1547
- const started = events.find((e) => e.event === "run_started");
1548
- if (!started || started.event !== "run_started") {
1549
- throw new Error("Invalid trace: missing run_started");
1550
- }
1551
- const rs = started;
1552
- const runId = rs.runId;
1553
- const completedAll = events.filter((e) => e.event === "run_completed");
1554
- const lastCompleted = completedAll[completedAll.length - 1];
1555
- let runStatus;
1556
- if (lastCompleted === void 0) runStatus = "running";
1557
- else runStatus = lastCompleted.status;
1558
- const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
1559
- const steps = /* @__PURE__ */ new Map();
1560
- let order = 0;
1561
- for (const e of events) {
1562
- if (e.event !== "step_started") continue;
1563
- const s = e;
1564
- const meta = s.metadata ? { ...s.metadata } : void 0;
1565
- steps.set(s.stepId, {
1566
- id: s.stepId,
1567
- parentId: s.parentId,
1568
- name: s.name,
1569
- type: s.type,
1570
- order: order++,
1571
- timestamp: s.timestamp,
1572
- metadata: meta
1573
- });
1574
- }
1575
- for (const e of events) {
1576
- if (e.event !== "step_completed") continue;
1577
- const acc = steps.get(e.stepId);
1578
- if (!acc) continue;
1579
- acc.status = e.status;
1580
- acc.durationMs = e.durationMs;
1581
- if (e.error?.message) acc.errorMsg = e.error.message;
1582
- const extra = e;
1583
- if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
1584
- acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
1585
- }
1586
- }
1587
- const nodes = /* @__PURE__ */ new Map();
1588
- for (const acc of steps.values()) {
1589
- let meta = acc.metadata ? { ...acc.metadata } : void 0;
1590
- if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
1591
- meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
1592
- }
1593
- const outputPreview = extractOutputPreview(meta);
1594
- const sc = {
1595
- id: acc.id,
1596
- name: acc.name,
1597
- type: acc.type,
1598
- status: mapStepStatus(acc.status),
1599
- durationMs: acc.durationMs,
1600
- error: acc.errorMsg,
1601
- metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
1602
- outputPreview,
1603
- children: []
1604
- };
1605
- nodes.set(acc.id, sc);
1606
- }
1607
- const roots = [];
1608
- const sortByOrder = (a, b) => {
1609
- const oa = steps.get(a.id)?.order ?? 0;
1610
- const ob = steps.get(b.id)?.order ?? 0;
1611
- return oa - ob;
1612
- };
1613
- for (const acc of steps.values()) {
1614
- const node = nodes.get(acc.id);
1615
- if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
1616
- nodes.get(acc.parentId).children.push(node);
1617
- } else {
1618
- roots.push(node);
1619
- }
1620
- }
1621
- roots.sort(sortByOrder);
1622
- for (const n of nodes.values()) {
1623
- n.children.sort(sortByOrder);
1646
+ function normalizeTokenUsage(value) {
1647
+ if (!isRecord5(value)) return void 0;
1648
+ const input = nonNegativeFinite(value.input);
1649
+ const output = nonNegativeFinite(value.output);
1650
+ const suppliedTotal = nonNegativeFinite(value.total);
1651
+ const cached = nonNegativeFinite(value.cached);
1652
+ const derivedTotal = input !== void 0 && output !== void 0 && Number.isFinite(input + output) ? input + output : void 0;
1653
+ const total = suppliedTotal ?? derivedTotal;
1654
+ if (input === void 0 && output === void 0 && total === void 0 && cached === void 0) {
1655
+ return void 0;
1624
1656
  }
1625
1657
  return {
1626
- runId,
1627
- name: rs.name,
1628
- status: runStatus,
1629
- durationMs,
1630
- steps: roots
1658
+ ...input !== void 0 ? { input } : {},
1659
+ ...output !== void 0 ? { output } : {},
1660
+ ...total !== void 0 ? { total } : {},
1661
+ ...cached !== void 0 ? { cached } : {}
1631
1662
  };
1632
1663
  }
1633
1664
 
1634
- // packages/core/src/exporters/helpers.ts
1635
- var REDACT_SUBSTRINGS = [
1636
- "authorization",
1637
- "cookie",
1638
- "token",
1639
- "apikey",
1640
- "password",
1641
- "secret",
1642
- "email"
1643
- ];
1644
- function shouldRedactKey(key) {
1645
- const k = key.toLowerCase();
1646
- for (const s of REDACT_SUBSTRINGS) {
1647
- if (k.includes(s)) return true;
1665
+ // packages/core/src/persisted/from-trace-event.ts
1666
+ function sanitizeIdPart(value) {
1667
+ return value.replace(/[^a-zA-Z0-9_-]/g, "_");
1668
+ }
1669
+ function nodeIdForEvent(event) {
1670
+ switch (event.event) {
1671
+ case "run_started":
1672
+ case "run_completed":
1673
+ return event.runId;
1674
+ case "step_started":
1675
+ case "step_completed":
1676
+ return event.stepId;
1677
+ case "outcome_observed":
1678
+ return event.outcomeId;
1679
+ default:
1680
+ return "unknown";
1648
1681
  }
1649
- return false;
1650
1682
  }
1651
- function safeString(value, maxLength) {
1652
- if (value === null || value === void 0) return "";
1653
- let s;
1654
- if (typeof value === "string") s = value;
1655
- else if (typeof value === "number" || typeof value === "boolean") s = String(value);
1656
- else s = stableJson(value, false);
1657
- if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
1658
- return `${s.slice(0, maxLength)}\u2026`;
1683
+ function createPersistedEventId(event, eventIndex) {
1684
+ const runId = sanitizeIdPart(event.runId);
1685
+ const ev = sanitizeIdPart(event.event);
1686
+ const node = sanitizeIdPart(nodeIdForEvent(event));
1687
+ return `manual:${runId}:${ev}:${node}:${eventIndex}`;
1688
+ }
1689
+ function toIsoTimestamp(ms) {
1690
+ if (typeof ms !== "number" || !Number.isFinite(ms)) {
1691
+ return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
1659
1692
  }
1660
- return s;
1693
+ return { iso: new Date(ms).toISOString(), invalidTimestamp: false };
1661
1694
  }
1662
- function escapeMarkdown(value) {
1663
- return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
1695
+ function buildSource(options) {
1696
+ return {
1697
+ type: "manual",
1698
+ name: options?.sourceName ?? "trace-event",
1699
+ version: options?.sourceVersion ?? "0.1"
1700
+ };
1664
1701
  }
1665
- function sortKeysDeep(input) {
1666
- if (input === null || typeof input !== "object") return input;
1667
- if (Array.isArray(input)) return input.map(sortKeysDeep);
1668
- const o = input;
1669
- const out = {};
1670
- for (const k of Object.keys(o).sort()) {
1671
- out[k] = sortKeysDeep(o[k]);
1702
+ function mapStepTypeToInspectKind(type) {
1703
+ switch (type) {
1704
+ case "run":
1705
+ return "RUN";
1706
+ case "llm":
1707
+ return "LLM";
1708
+ case "tool":
1709
+ return "TOOL";
1710
+ case "decision":
1711
+ return "DECISION";
1712
+ case "logic":
1713
+ case "state":
1714
+ case "custom":
1715
+ return "LOGIC";
1716
+ default:
1717
+ return "LOGIC";
1672
1718
  }
1673
- return out;
1674
1719
  }
1675
- function stableJson(value, pretty) {
1676
- const sorted = sortKeysDeep(value);
1677
- return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
1720
+ function mapRunOrStepStatus(status) {
1721
+ return status === "success" ? "ok" : "error";
1678
1722
  }
1679
- function compactAttributes(attrs, options) {
1680
- if (attrs === void 0) return {};
1681
- const maxLen = options?.maxLength ?? 500;
1682
- const out = {};
1683
- for (const key of Object.keys(attrs).sort()) {
1684
- if (shouldRedactKey(key)) {
1685
- out[key] = "[REDACTED]";
1686
- continue;
1723
+ function mapErrorInfo(error) {
1724
+ if (!error?.message) {
1725
+ return {};
1726
+ }
1727
+ const out = {
1728
+ persisted: {
1729
+ message: error.message,
1730
+ name: "Error"
1687
1731
  }
1688
- const v = attrs[key];
1689
- out[key] = compactValue(v, maxLen);
1732
+ };
1733
+ if (typeof error.stack === "string" && error.stack.length > 0) {
1734
+ out.errorStack = error.stack;
1690
1735
  }
1691
1736
  return out;
1692
1737
  }
1693
- function compactValue(value, maxLen, redacted) {
1694
- if (value === null || typeof value !== "object") {
1695
- return typeof value === "string" ? safeString(value, maxLen) : value;
1696
- }
1697
- if (Array.isArray(value)) {
1698
- const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen));
1699
- if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
1700
- return arr;
1701
- }
1702
- const o = value;
1703
- const inner = {};
1704
- for (const k of Object.keys(o)) {
1705
- if (shouldRedactKey(k)) inner[k] = "[REDACTED]";
1706
- else inner[k] = compactValue(o[k], maxLen);
1707
- }
1708
- return inner;
1738
+ function mapTokenUsageFromMetadata(metadata) {
1739
+ return normalizeTokenUsage(metadata?.tokens);
1709
1740
  }
1710
- function flattenTree(tree) {
1711
- const out = [];
1712
- function walk(nodes) {
1713
- for (const n of nodes) {
1714
- out.push(n);
1715
- if (n.children.length > 0) walk(n.children);
1741
+ function compactAttributes(entries) {
1742
+ const out = {};
1743
+ for (const [key, value] of Object.entries(entries)) {
1744
+ if (value !== void 0) {
1745
+ out[key] = value;
1716
1746
  }
1717
1747
  }
1718
- walk(tree.children);
1719
- return out;
1720
- }
1721
-
1722
- // packages/core/src/diff/engine.ts
1723
- var DEFAULT_THRESHOLD_MS = 0;
1724
- function pathSeg(step, index) {
1725
- return { index, name: step.name, stepId: step.id };
1726
- }
1727
- function buildPath(segments) {
1728
- return { path: [...segments] };
1748
+ return Object.keys(out).length > 0 ? out : void 0;
1729
1749
  }
1730
- function pairSteps(left, right) {
1731
- const usedRight = /* @__PURE__ */ new Set();
1732
- const pairs = [];
1733
- for (let i = 0; i < left.length; i++) {
1734
- const L = left[i];
1735
- let R = right.find((r) => !usedRight.has(r.id) && r.id === L.id);
1736
- if (R === void 0 && i < right.length && !usedRight.has(right[i].id)) {
1737
- const cand = right[i];
1738
- if (cand.name === L.name && (cand.type ?? "") === (L.type ?? "")) {
1739
- R = cand;
1750
+ function traceEventToPersistedInspectEvent(event, options) {
1751
+ const eventIndex = options?.eventIndex ?? 0;
1752
+ const eventId = createPersistedEventId(event, eventIndex);
1753
+ const source = buildSource(options);
1754
+ const tsMain = toIsoTimestamp(event.timestamp);
1755
+ switch (event.event) {
1756
+ case "run_started": {
1757
+ const tsStart = toIsoTimestamp(event.startTime);
1758
+ const correlation = extractCorrelationMetadata(event.metadata);
1759
+ const attributes = compactAttributes({
1760
+ legacyEvent: "run_started",
1761
+ metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
1762
+ correlationId: correlation?.correlationId,
1763
+ requestId: correlation?.requestId,
1764
+ decisionId: correlation?.decisionId,
1765
+ groupId: correlation?.groupId,
1766
+ invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
1767
+ });
1768
+ return {
1769
+ schemaVersion: "0.2",
1770
+ eventId,
1771
+ runId: event.runId,
1772
+ kind: "RUN",
1773
+ name: event.name,
1774
+ status: "running",
1775
+ timestamp: tsMain.iso,
1776
+ startedAt: tsStart.iso,
1777
+ confidence: "explicit",
1778
+ source,
1779
+ attributes
1780
+ };
1781
+ }
1782
+ case "run_completed": {
1783
+ const tsEnd = toIsoTimestamp(event.endTime);
1784
+ const { persisted: error, errorStack } = mapErrorInfo(event.error);
1785
+ const attributes = compactAttributes({
1786
+ legacyEvent: "run_completed",
1787
+ errorStack,
1788
+ invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
1789
+ });
1790
+ return {
1791
+ schemaVersion: "0.2",
1792
+ eventId,
1793
+ runId: event.runId,
1794
+ kind: "RUN",
1795
+ name: "run",
1796
+ status: mapRunOrStepStatus(event.status),
1797
+ timestamp: tsMain.iso,
1798
+ endedAt: tsEnd.iso,
1799
+ durationMs: event.durationMs,
1800
+ confidence: "explicit",
1801
+ source,
1802
+ attributes,
1803
+ error
1804
+ };
1805
+ }
1806
+ case "step_started": {
1807
+ const tsStart = toIsoTimestamp(event.startTime);
1808
+ const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
1809
+ const attributes = compactAttributes({
1810
+ legacyEvent: "step_started",
1811
+ stepId: event.stepId,
1812
+ stepType: event.type,
1813
+ metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
1814
+ invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
1815
+ });
1816
+ const out = {
1817
+ schemaVersion: "0.2",
1818
+ eventId,
1819
+ runId: event.runId,
1820
+ kind: mapStepTypeToInspectKind(event.type),
1821
+ name: event.name,
1822
+ status: "running",
1823
+ timestamp: tsMain.iso,
1824
+ startedAt: tsStart.iso,
1825
+ confidence: "explicit",
1826
+ source,
1827
+ attributes
1828
+ };
1829
+ if (event.parentId !== void 0) {
1830
+ out.parentId = event.parentId;
1831
+ }
1832
+ if (tokenUsage !== void 0) {
1833
+ out.tokenUsage = tokenUsage;
1740
1834
  }
1835
+ return out;
1741
1836
  }
1742
- if (R === void 0) {
1743
- R = right.find(
1744
- (r) => !usedRight.has(r.id) && r.name === L.name && (r.type ?? "") === (L.type ?? "")
1745
- );
1837
+ case "step_completed": {
1838
+ const tsEnd = toIsoTimestamp(event.endTime);
1839
+ const { persisted: error, errorStack } = mapErrorInfo(event.error);
1840
+ const attributes = compactAttributes({
1841
+ legacyEvent: "step_completed",
1842
+ stepId: event.stepId,
1843
+ errorStack,
1844
+ invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
1845
+ });
1846
+ return {
1847
+ schemaVersion: "0.2",
1848
+ eventId,
1849
+ runId: event.runId,
1850
+ kind: "LOGIC",
1851
+ name: event.stepId,
1852
+ status: mapRunOrStepStatus(event.status),
1853
+ timestamp: tsMain.iso,
1854
+ endedAt: tsEnd.iso,
1855
+ durationMs: event.durationMs,
1856
+ confidence: "explicit",
1857
+ source,
1858
+ attributes,
1859
+ error
1860
+ };
1746
1861
  }
1747
- if (R !== void 0) {
1748
- usedRight.add(R.id);
1749
- pairs.push([L, R]);
1750
- } else {
1751
- pairs.push([L, void 0]);
1862
+ case "outcome_observed": {
1863
+ const tsObserved = toIsoTimestamp(event.observedAt);
1864
+ const attributes = compactAttributes({
1865
+ legacyEvent: "outcome_observed",
1866
+ outcomeId: event.outcomeId,
1867
+ outcomeStatus: event.status,
1868
+ expectation: event.expectation,
1869
+ method: event.method,
1870
+ actual: event.actual,
1871
+ evidence: event.evidence,
1872
+ observedAt: tsObserved.iso,
1873
+ invalidTimestamp: tsMain.invalidTimestamp || tsObserved.invalidTimestamp ? true : void 0
1874
+ });
1875
+ const out = {
1876
+ schemaVersion: "0.2",
1877
+ eventId,
1878
+ runId: event.runId,
1879
+ kind: "OUTCOME",
1880
+ name: event.name,
1881
+ status: event.status === "failed" ? "error" : "ok",
1882
+ timestamp: tsMain.iso,
1883
+ confidence: "explicit",
1884
+ source,
1885
+ attributes
1886
+ };
1887
+ if (event.parentId !== void 0) {
1888
+ out.parentId = event.parentId;
1889
+ }
1890
+ if (event.actual !== void 0) {
1891
+ out.outputSummary = event.actual;
1892
+ }
1893
+ return out;
1752
1894
  }
1753
- }
1754
- for (const R of right) {
1755
- if (!usedRight.has(R.id)) {
1756
- pairs.push([void 0, R]);
1895
+ default: {
1896
+ const _exhaustive = event;
1897
+ throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
1757
1898
  }
1758
1899
  }
1759
- return pairs;
1760
1900
  }
1761
- function compareLeafSteps(L, R, segments, opts, out) {
1762
- const path8 = buildPath(segments);
1763
- if (L.name !== R.name) {
1764
- out.push({
1765
- kind: "structure",
1766
- severity: "warning",
1767
- message: "Step name differs",
1768
- path: path8,
1769
- left: L.name,
1770
- right: R.name
1771
- });
1772
- }
1773
- if ((L.type ?? "") !== (R.type ?? "")) {
1774
- out.push({
1775
- kind: "step-type",
1776
- severity: "warning",
1777
- message: "Step type differs",
1778
- path: path8,
1779
- left: L.type,
1780
- right: R.type
1781
- });
1782
- }
1783
- if ((L.status ?? "") !== (R.status ?? "")) {
1784
- out.push({
1785
- kind: "step-status",
1786
- severity: "warning",
1787
- message: "Step status differs",
1788
- path: path8,
1789
- left: L.status,
1790
- right: R.status
1791
- });
1901
+ function traceEventsToPersistedInspectEvents(events, options) {
1902
+ return events.map(
1903
+ (event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
1904
+ );
1905
+ }
1906
+
1907
+ // packages/core/src/logs/tree-builder.ts
1908
+ function inc(map, key) {
1909
+ map[key] = (map[key] ?? 0) + 1;
1910
+ }
1911
+ function computeRunStatus(events) {
1912
+ let hasRunning = false;
1913
+ for (const e of events) {
1914
+ if (e.status === "error") return "error";
1915
+ if (e.status === "running") hasRunning = true;
1792
1916
  }
1793
- const le = L.error ?? "";
1794
- const re = R.error ?? "";
1795
- if (le !== re) {
1796
- out.push({
1797
- kind: "error",
1798
- severity: "error",
1799
- message: "Step error message differs",
1800
- path: path8,
1801
- left: le || void 0,
1802
- right: re || void 0
1803
- });
1917
+ if (hasRunning) return "running";
1918
+ return "ok";
1919
+ }
1920
+ var TreeBuilder = class {
1921
+ constructor(options) {
1922
+ void options?.config;
1804
1923
  }
1805
- if (!opts.ignoreDuration) {
1806
- const ld = L.durationMs;
1807
- const rd = R.durationMs;
1808
- const th = opts.durationThresholdMs;
1809
- let differs = false;
1810
- if (ld === void 0 && rd === void 0) differs = false;
1811
- else if (ld === void 0 || rd === void 0) differs = true;
1812
- else differs = Math.abs(ld - rd) > th;
1813
- if (differs) {
1814
- out.push({
1815
- kind: "duration",
1816
- severity: "info",
1817
- message: "Step duration differs",
1818
- path: path8,
1819
- left: ld,
1820
- right: rd
1821
- });
1924
+ build(events) {
1925
+ const byRun = /* @__PURE__ */ new Map();
1926
+ for (const e of events) {
1927
+ if (!byRun.has(e.runId)) byRun.set(e.runId, []);
1928
+ byRun.get(e.runId).push(e);
1822
1929
  }
1823
- }
1824
- const lm = stableJson(L.metadata ?? {});
1825
- const rm = stableJson(R.metadata ?? {});
1826
- if (lm !== rm) {
1827
- out.push({
1828
- kind: "metadata",
1829
- severity: "info",
1830
- message: "Step metadata differs",
1831
- path: path8,
1832
- left: L.metadata,
1833
- right: R.metadata
1834
- });
1835
- }
1836
- const lo = stableJson(L.outputPreview ?? null);
1837
- const ro = stableJson(R.outputPreview ?? null);
1838
- if (lo !== ro) {
1839
- out.push({
1840
- kind: "output",
1841
- severity: "info",
1842
- message: "Output preview differs",
1843
- path: path8,
1844
- left: L.outputPreview,
1845
- right: R.outputPreview
1846
- });
1847
- }
1848
- }
1849
- function compareRecursive(L, R, segments, opts, out) {
1850
- compareLeafSteps(L, R, segments, opts, out);
1851
- const pairs = pairSteps(L.children, R.children);
1852
- let ci = 0;
1853
- for (const [lch, rch] of pairs) {
1854
- if (lch !== void 0 && rch !== void 0) {
1855
- compareRecursive(lch, rch, [...segments, pathSeg(lch, ci)], opts, out);
1856
- } else if (lch !== void 0) {
1857
- out.push({
1858
- kind: "step-removed",
1859
- severity: "warning",
1860
- message: `Step only in left run: ${lch.name}`,
1861
- path: buildPath([...segments, pathSeg(lch, ci)]),
1862
- left: lch.id,
1863
- right: void 0
1864
- });
1865
- } else if (rch !== void 0) {
1930
+ const out = [];
1931
+ for (const [runId, runEvents] of byRun.entries()) {
1932
+ const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
1933
+ const nodes = /* @__PURE__ */ new Map();
1934
+ for (const e of sorted) {
1935
+ nodes.set(e.eventId, { event: e, children: [], depth: 0 });
1936
+ }
1937
+ const roots = [];
1938
+ for (const node of nodes.values()) {
1939
+ const parentId = node.event.parentId;
1940
+ if (parentId && nodes.has(parentId)) {
1941
+ nodes.get(parentId).children.push(node);
1942
+ } else {
1943
+ roots.push(node);
1944
+ }
1945
+ }
1946
+ const assignDepth = (n, depth) => {
1947
+ n.depth = depth;
1948
+ for (const c of n.children) assignDepth(c, depth + 1);
1949
+ };
1950
+ for (const r of roots) assignDepth(r, 0);
1951
+ const confidenceBreakdown = {
1952
+ explicit: 0,
1953
+ correlated: 0,
1954
+ heuristic: 0,
1955
+ unknown: 0
1956
+ };
1957
+ const kinds = {};
1958
+ for (const e of sorted) {
1959
+ inc(confidenceBreakdown, e.confidence);
1960
+ kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
1961
+ }
1962
+ const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
1963
+ const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
1964
+ const status = computeRunStatus(sorted);
1965
+ const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
1966
+ const name = sorted.find((e) => e.kind === "RUN")?.name;
1866
1967
  out.push({
1867
- kind: "step-added",
1868
- severity: "warning",
1869
- message: `Step only in right run: ${rch.name}`,
1870
- path: buildPath([...segments, pathSeg(rch, ci)]),
1871
- left: void 0,
1872
- right: rch.id
1968
+ runId,
1969
+ name,
1970
+ status,
1971
+ startedAt,
1972
+ endedAt: status === "running" ? void 0 : endedAt,
1973
+ durationMs,
1974
+ children: roots,
1975
+ metadata: {
1976
+ totalEvents: sorted.length,
1977
+ confidenceBreakdown,
1978
+ kinds
1979
+ }
1873
1980
  });
1874
1981
  }
1875
- ci += 1;
1982
+ out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
1983
+ return out;
1984
+ }
1985
+ };
1986
+
1987
+ // packages/core/src/persisted/to-inspect-event.ts
1988
+ function compactAttributes2(entries) {
1989
+ const out = {};
1990
+ for (const [key, value] of Object.entries(entries)) {
1991
+ if (value !== void 0) {
1992
+ out[key] = value;
1993
+ }
1876
1994
  }
1995
+ return Object.keys(out).length > 0 ? out : void 0;
1877
1996
  }
1878
- function mergeDiffDefaults(options) {
1997
+ function parseIsoToMs3(iso) {
1998
+ const parsed = Date.parse(iso);
1999
+ if (!Number.isFinite(parsed)) {
2000
+ return { ms: 0, invalidTimestamp: true };
2001
+ }
2002
+ return { ms: parsed, invalidTimestamp: false };
2003
+ }
2004
+ function mapPersistedSourceToInspect(event) {
2005
+ const attrs = event.attributes ?? {};
2006
+ const sourceName = event.source.name;
2007
+ if (sourceName === "pino") {
2008
+ return {
2009
+ type: "pino",
2010
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
2011
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
2012
+ };
2013
+ }
2014
+ if (sourceName === "winston") {
2015
+ return {
2016
+ type: "winston",
2017
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
2018
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
2019
+ };
2020
+ }
2021
+ const mapType = (t) => {
2022
+ switch (t) {
2023
+ case "manual":
2024
+ return "manual";
2025
+ case "json-log":
2026
+ return "json-log";
2027
+ case "log4js":
2028
+ return "log4js";
2029
+ case "adapter":
2030
+ case "ai-sdk":
2031
+ case "otel":
2032
+ return "adapter";
2033
+ default:
2034
+ return "json-log";
2035
+ }
2036
+ };
1879
2037
  return {
1880
- ignoreDuration: false,
1881
- durationThresholdMs: DEFAULT_THRESHOLD_MS,
1882
- focus: "all",
1883
- check: "all"
2038
+ type: mapType(event.source.type),
2039
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
2040
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
1884
2041
  };
1885
2042
  }
1886
- function kindMatchesFilter(kind, merged) {
1887
- return true;
1888
- }
1889
- function diffRuns(left, right, options) {
1890
- const merged = mergeDiffDefaults();
1891
- const opts = {
1892
- ignoreDuration: merged.ignoreDuration,
1893
- durationThresholdMs: merged.durationThresholdMs
1894
- };
1895
- const raw = [];
1896
- if ((left.status ?? "") !== (right.status ?? "")) {
1897
- raw.push({
1898
- kind: "run-status",
1899
- severity: "warning",
1900
- message: "Run completion status differs",
1901
- left: left.status,
1902
- right: right.status
1903
- });
2043
+ function buildInspectAttributes(event) {
2044
+ const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
2045
+ if (event.inputSummary !== void 0) {
2046
+ attrs.inputSummary = event.inputSummary;
1904
2047
  }
1905
- {
1906
- const ld = left.durationMs;
1907
- const rd = right.durationMs;
1908
- const th = merged.durationThresholdMs;
1909
- let differs = false;
1910
- if (ld === void 0 && rd === void 0) differs = false;
1911
- else if (ld === void 0 || rd === void 0) differs = true;
1912
- else differs = Math.abs(ld - rd) > th;
1913
- if (differs) {
1914
- raw.push({
1915
- kind: "duration",
1916
- severity: "info",
1917
- message: "Run duration differs",
1918
- left: ld,
1919
- right: rd
1920
- });
1921
- }
2048
+ if (event.outputSummary !== void 0) {
2049
+ attrs.outputSummary = event.outputSummary;
1922
2050
  }
1923
- const pairs = pairSteps(left.steps, right.steps);
1924
- let idx = 0;
1925
- for (const [ls, rs] of pairs) {
1926
- if (ls !== void 0 && rs !== void 0) {
1927
- compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
1928
- idx += 1;
1929
- } else if (ls !== void 0) {
1930
- raw.push({
1931
- kind: "step-removed",
1932
- severity: "warning",
1933
- message: `Step only in left run: ${ls.name}`,
1934
- path: buildPath([pathSeg(ls, idx)]),
1935
- left: ls.id,
1936
- right: void 0
1937
- });
1938
- idx += 1;
1939
- } else if (rs !== void 0) {
1940
- raw.push({
1941
- kind: "step-added",
1942
- severity: "warning",
1943
- message: `Step only in right run: ${rs.name}`,
1944
- path: buildPath([pathSeg(rs, idx)]),
1945
- left: void 0,
1946
- right: rs.id
1947
- });
1948
- idx += 1;
2051
+ if (event.error) {
2052
+ if (event.error.name !== void 0) {
2053
+ attrs.errorName = event.error.name;
2054
+ }
2055
+ attrs.errorMessage = event.error.message;
2056
+ if (event.error.code !== void 0) {
2057
+ attrs.errorCode = event.error.code;
1949
2058
  }
1950
2059
  }
1951
- const differences = raw.filter((d) => kindMatchesFilter(d.kind));
1952
- let errors = 0;
1953
- let warnings = 0;
1954
- let info = 0;
1955
- for (const d of differences) {
1956
- if (d.severity === "error") errors += 1;
1957
- else if (d.severity === "warning") warnings += 1;
1958
- else info += 1;
2060
+ if (event.tokenUsage) {
2061
+ attrs.tokens = { ...event.tokenUsage };
1959
2062
  }
1960
- const firstVisible = differences[0];
1961
- const firstDivergence = firstVisible !== void 0 ? {
1962
- kind: "first-divergence",
1963
- severity: firstVisible.severity,
1964
- message: `First divergence: ${firstVisible.message}`,
1965
- path: firstVisible.path,
1966
- left: firstVisible.left,
1967
- right: firstVisible.right
1968
- } : void 0;
1969
- const summary = {
1970
- leftRunId: left.runId,
1971
- rightRunId: right.runId,
1972
- totalDifferences: differences.length,
1973
- errors,
1974
- warnings,
1975
- info,
1976
- firstDivergence
1977
- };
1978
- return { summary, differences };
1979
- }
1980
-
1981
- // packages/core/src/exporters/markdown-exporter.ts
1982
- function renderTreeAscii(nodes, indent = "") {
1983
- const lines = [];
1984
- for (let i = 0; i < nodes.length; i++) {
1985
- const n = nodes[i];
1986
- const last = i === nodes.length - 1;
1987
- const branch = last ? "\u2514\u2500 " : "\u251C\u2500 ";
1988
- const ev = n.event;
1989
- const status = ev.status ?? "?";
1990
- const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
1991
- lines.push(`${indent}${branch}${escapeMarkdown(ev.name)} [${ev.kind}] ${status} (${dur})`);
1992
- const nextIndent = indent + (last ? " " : "\u2502 ");
1993
- if (n.children.length > 0) {
1994
- const childStr = renderTreeAscii(n.children, nextIndent);
1995
- if (childStr.length > 0) lines.push(childStr);
1996
- }
1997
- }
1998
- return lines.join("\n");
1999
- }
2000
- function exportMarkdown(tree, options) {
2001
- const warnings = [];
2002
- const includeMetadata = options?.includeMetadata ?? true;
2003
- const includeAttributes = options?.includeAttributes ?? false;
2004
- const includeErrors = options?.includeErrors ?? true;
2005
- const maxLen = options?.maxAttributeLength ?? 500;
2006
- const titleName = tree.name ?? tree.runId;
2007
- const lines = [];
2008
- lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
2009
- lines.push("");
2010
- lines.push("Generated locally by AgentInspect. Review for sensitive data before sharing.");
2011
- lines.push("");
2012
- if (includeMetadata) {
2013
- lines.push("## Summary");
2014
- lines.push("");
2015
- lines.push(`- **runId**: ${escapeMarkdown(tree.runId)}`);
2016
- if (tree.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(tree.name)}`);
2017
- lines.push(`- **status**: ${escapeMarkdown(String(tree.status ?? "unknown"))}`);
2018
- lines.push(
2019
- `- **durationMs**: ${tree.durationMs !== void 0 ? escapeMarkdown(String(tree.durationMs)) : "-"}`
2020
- );
2021
- lines.push(
2022
- `- **startedAt**: ${tree.startedAt !== void 0 ? escapeMarkdown(String(tree.startedAt)) : "-"}`
2023
- );
2024
- lines.push(
2025
- `- **endedAt**: ${tree.endedAt !== void 0 ? escapeMarkdown(String(tree.endedAt)) : "-"}`
2026
- );
2027
- lines.push(`- **totalEvents**: ${tree.metadata.totalEvents}`);
2028
- lines.push("");
2029
- lines.push("### Confidence breakdown");
2030
- lines.push("");
2031
- lines.push("| bucket | count |");
2032
- lines.push("| --- | --- |");
2033
- for (const k of Object.keys(tree.metadata.confidenceBreakdown).sort()) {
2034
- const key = k;
2035
- lines.push(
2036
- `| ${escapeMarkdown(key)} | ${tree.metadata.confidenceBreakdown[key]} |`
2037
- );
2038
- }
2039
- lines.push("");
2040
- lines.push("### Kind breakdown");
2041
- lines.push("");
2042
- lines.push("| kind | count |");
2043
- lines.push("| --- | --- |");
2044
- for (const k of Object.keys(tree.metadata.kinds).sort()) {
2045
- const key = k;
2046
- const c = tree.metadata.kinds[key];
2047
- if (c > 0) lines.push(`| ${escapeMarkdown(key)} | ${c} |`);
2048
- }
2049
- lines.push("");
2063
+ if (event.source.type === "ai-sdk" || event.source.type === "otel") {
2064
+ attrs.originalSourceType = event.source.type;
2050
2065
  }
2051
- lines.push("## Execution tree");
2052
- lines.push("");
2053
- lines.push("```text");
2054
- lines.push(
2055
- tree.children.length > 0 ? renderTreeAscii(tree.children) : "(no steps)"
2056
- );
2057
- lines.push("```");
2058
- lines.push("");
2059
- const flat = flattenTree(tree);
2060
- const errors = flat.filter((n) => n.event.status === "error");
2061
- if (includeErrors && errors.length > 0) {
2062
- lines.push("## Errors");
2063
- lines.push("");
2064
- for (const n of errors) {
2065
- const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
2066
- n.event.attributes.error.message,
2067
- maxLen
2068
- ) : "";
2069
- lines.push(
2070
- `- **${escapeMarkdown(n.event.name)}** (${escapeMarkdown(n.event.eventId)}): ${escapeMarkdown(msg || "error")}`
2071
- );
2072
- }
2073
- lines.push("");
2066
+ if (event.source.name !== void 0) {
2067
+ attrs.sourceName = event.source.name;
2074
2068
  }
2075
- if (includeAttributes) {
2076
- lines.push("## Attributes (bounded)");
2077
- lines.push("");
2078
- for (const n of flat) {
2079
- if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
2080
- const compact = compactAttributes(n.event.attributes, {
2081
- maxLength: maxLen});
2082
- lines.push(`### ${escapeMarkdown(n.event.name)}`);
2083
- lines.push("");
2084
- lines.push("```json");
2085
- lines.push(stableJson(compact, true));
2086
- lines.push("```");
2087
- lines.push("");
2088
- }
2089
- warnings.push(
2090
- "Attributes may still contain sensitive data; review exports before sharing."
2091
- );
2069
+ if (event.source.version !== void 0) {
2070
+ attrs.sourceVersion = event.source.version;
2092
2071
  }
2093
- return {
2094
- format: "markdown",
2095
- content: lines.join("\n"),
2096
- contentType: "text/markdown",
2097
- fileExtension: ".md",
2098
- warnings
2099
- };
2100
- }
2101
-
2102
- // packages/core/src/persisted/token-usage.ts
2103
- function isRecord5(value) {
2104
- return typeof value === "object" && value !== null && !Array.isArray(value);
2105
- }
2106
- function nonNegativeFinite(value) {
2107
- return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
2072
+ return attrs;
2108
2073
  }
2109
- function normalizeTokenUsage(value) {
2110
- if (!isRecord5(value)) return void 0;
2111
- const input = nonNegativeFinite(value.input);
2112
- const output = nonNegativeFinite(value.output);
2113
- const suppliedTotal = nonNegativeFinite(value.total);
2114
- const cached = nonNegativeFinite(value.cached);
2115
- const derivedTotal = input !== void 0 && output !== void 0 && Number.isFinite(input + output) ? input + output : void 0;
2116
- const total = suppliedTotal ?? derivedTotal;
2117
- if (input === void 0 && output === void 0 && total === void 0 && cached === void 0) {
2118
- return void 0;
2074
+ function persistedInspectEventToInspectEvent(event) {
2075
+ if (!isPersistedInspectEvent(event)) {
2076
+ throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
2119
2077
  }
2120
- return {
2121
- ...input !== void 0 ? { input } : {},
2122
- ...output !== void 0 ? { output } : {},
2123
- ...total !== void 0 ? { total } : {},
2124
- ...cached !== void 0 ? { cached } : {}
2125
- };
2126
- }
2127
-
2128
- // packages/core/src/persisted/from-trace-event.ts
2129
- function sanitizeIdPart(value) {
2130
- return value.replace(/[^a-zA-Z0-9_-]/g, "_");
2131
- }
2132
- function nodeIdForEvent(event) {
2133
- switch (event.event) {
2134
- case "run_started":
2135
- case "run_completed":
2136
- return event.runId;
2137
- case "step_started":
2138
- case "step_completed":
2139
- return event.stepId;
2140
- default:
2141
- return "unknown";
2078
+ const ts = parseIsoToMs3(event.timestamp);
2079
+ const attrs = buildInspectAttributes(event);
2080
+ if (ts.invalidTimestamp) {
2081
+ attrs.invalidTimestamp = true;
2142
2082
  }
2143
- }
2144
- function createPersistedEventId(event, eventIndex) {
2145
- const runId = sanitizeIdPart(event.runId);
2146
- const ev = sanitizeIdPart(event.event);
2147
- const node = sanitizeIdPart(nodeIdForEvent(event));
2148
- return `manual:${runId}:${ev}:${node}:${eventIndex}`;
2149
- }
2150
- function toIsoTimestamp(ms) {
2151
- if (typeof ms !== "number" || !Number.isFinite(ms)) {
2152
- return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
2083
+ let status;
2084
+ if (event.status === "running" || event.status === "ok" || event.status === "error") {
2085
+ status = event.status;
2086
+ } else if (event.status === "unknown") {
2087
+ attrs.persistedStatus = "unknown";
2153
2088
  }
2154
- return { iso: new Date(ms).toISOString(), invalidTimestamp: false };
2155
- }
2156
- function buildSource(options) {
2157
- return {
2158
- type: "manual",
2159
- name: options?.sourceName ?? "trace-event",
2160
- version: options?.sourceVersion ?? "0.1"
2089
+ const out = {
2090
+ eventId: event.eventId,
2091
+ runId: event.runId,
2092
+ name: event.name,
2093
+ kind: event.kind,
2094
+ timestamp: ts.ms,
2095
+ confidence: event.confidence,
2096
+ source: mapPersistedSourceToInspect(event),
2097
+ attributes: compactAttributes2(attrs)
2161
2098
  };
2162
- }
2163
- function mapStepTypeToInspectKind(type) {
2164
- switch (type) {
2165
- case "run":
2166
- return "RUN";
2167
- case "llm":
2168
- return "LLM";
2169
- case "tool":
2170
- return "TOOL";
2171
- case "decision":
2172
- return "DECISION";
2173
- case "logic":
2174
- case "state":
2175
- case "custom":
2176
- return "LOGIC";
2177
- default:
2178
- return "LOGIC";
2099
+ if (event.parentId !== void 0) {
2100
+ out.parentId = event.parentId;
2179
2101
  }
2180
- }
2181
- function mapRunOrStepStatus(status) {
2182
- return status === "success" ? "ok" : "error";
2183
- }
2184
- function mapErrorInfo(error) {
2185
- if (!error?.message) {
2186
- return {};
2102
+ if (status !== void 0) {
2103
+ out.status = status;
2187
2104
  }
2188
- const out = {
2189
- persisted: {
2190
- message: error.message,
2191
- name: "Error"
2192
- }
2193
- };
2194
- if (typeof error.stack === "string" && error.stack.length > 0) {
2195
- out.errorStack = error.stack;
2196
- }
2197
- return out;
2198
- }
2199
- function mapTokenUsageFromMetadata(metadata) {
2200
- return normalizeTokenUsage(metadata?.tokens);
2201
- }
2202
- function compactAttributes2(entries) {
2203
- const out = {};
2204
- for (const [key, value] of Object.entries(entries)) {
2205
- if (value !== void 0) {
2206
- out[key] = value;
2207
- }
2208
- }
2209
- return Object.keys(out).length > 0 ? out : void 0;
2210
- }
2211
- function traceEventToPersistedInspectEvent(event, options) {
2212
- const eventIndex = options?.eventIndex ?? 0;
2213
- const eventId = createPersistedEventId(event, eventIndex);
2214
- const source = buildSource(options);
2215
- const tsMain = toIsoTimestamp(event.timestamp);
2216
- switch (event.event) {
2217
- case "run_started": {
2218
- const tsStart = toIsoTimestamp(event.startTime);
2219
- const correlation = extractCorrelationMetadata(event.metadata);
2220
- const attributes = compactAttributes2({
2221
- legacyEvent: "run_started",
2222
- metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
2223
- correlationId: correlation?.correlationId,
2224
- requestId: correlation?.requestId,
2225
- decisionId: correlation?.decisionId,
2226
- groupId: correlation?.groupId,
2227
- invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
2228
- });
2229
- return {
2230
- schemaVersion: "0.2",
2231
- eventId,
2232
- runId: event.runId,
2233
- kind: "RUN",
2234
- name: event.name,
2235
- status: "running",
2236
- timestamp: tsMain.iso,
2237
- startedAt: tsStart.iso,
2238
- confidence: "explicit",
2239
- source,
2240
- attributes
2241
- };
2242
- }
2243
- case "run_completed": {
2244
- const tsEnd = toIsoTimestamp(event.endTime);
2245
- const { persisted: error, errorStack } = mapErrorInfo(event.error);
2246
- const attributes = compactAttributes2({
2247
- legacyEvent: "run_completed",
2248
- errorStack,
2249
- invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
2250
- });
2251
- return {
2252
- schemaVersion: "0.2",
2253
- eventId,
2254
- runId: event.runId,
2255
- kind: "RUN",
2256
- name: "run",
2257
- status: mapRunOrStepStatus(event.status),
2258
- timestamp: tsMain.iso,
2259
- endedAt: tsEnd.iso,
2260
- durationMs: event.durationMs,
2261
- confidence: "explicit",
2262
- source,
2263
- attributes,
2264
- error
2265
- };
2266
- }
2267
- case "step_started": {
2268
- const tsStart = toIsoTimestamp(event.startTime);
2269
- const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
2270
- const attributes = compactAttributes2({
2271
- legacyEvent: "step_started",
2272
- stepId: event.stepId,
2273
- stepType: event.type,
2274
- metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
2275
- invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
2276
- });
2277
- const out = {
2278
- schemaVersion: "0.2",
2279
- eventId,
2280
- runId: event.runId,
2281
- kind: mapStepTypeToInspectKind(event.type),
2282
- name: event.name,
2283
- status: "running",
2284
- timestamp: tsMain.iso,
2285
- startedAt: tsStart.iso,
2286
- confidence: "explicit",
2287
- source,
2288
- attributes
2289
- };
2290
- if (event.parentId !== void 0) {
2291
- out.parentId = event.parentId;
2292
- }
2293
- if (tokenUsage !== void 0) {
2294
- out.tokenUsage = tokenUsage;
2295
- }
2296
- return out;
2297
- }
2298
- case "step_completed": {
2299
- const tsEnd = toIsoTimestamp(event.endTime);
2300
- const { persisted: error, errorStack } = mapErrorInfo(event.error);
2301
- const attributes = compactAttributes2({
2302
- legacyEvent: "step_completed",
2303
- stepId: event.stepId,
2304
- errorStack,
2305
- invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
2306
- });
2307
- return {
2308
- schemaVersion: "0.2",
2309
- eventId,
2310
- runId: event.runId,
2311
- kind: "LOGIC",
2312
- name: event.stepId,
2313
- status: mapRunOrStepStatus(event.status),
2314
- timestamp: tsMain.iso,
2315
- endedAt: tsEnd.iso,
2316
- durationMs: event.durationMs,
2317
- confidence: "explicit",
2318
- source,
2319
- attributes,
2320
- error
2321
- };
2322
- }
2323
- default: {
2324
- const _exhaustive = event;
2325
- throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
2326
- }
2327
- }
2328
- }
2329
- function traceEventsToPersistedInspectEvents(events, options) {
2330
- return events.map(
2331
- (event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
2332
- );
2333
- }
2334
-
2335
- // packages/core/src/persisted/to-inspect-event.ts
2336
- function compactAttributes3(entries) {
2337
- const out = {};
2338
- for (const [key, value] of Object.entries(entries)) {
2339
- if (value !== void 0) {
2340
- out[key] = value;
2341
- }
2342
- }
2343
- return Object.keys(out).length > 0 ? out : void 0;
2344
- }
2345
- function parseIsoToMs3(iso) {
2346
- const parsed = Date.parse(iso);
2347
- if (!Number.isFinite(parsed)) {
2348
- return { ms: 0, invalidTimestamp: true };
2349
- }
2350
- return { ms: parsed, invalidTimestamp: false };
2351
- }
2352
- function mapPersistedSourceToInspect(event) {
2353
- const attrs = event.attributes ?? {};
2354
- const sourceName = event.source.name;
2355
- if (sourceName === "pino") {
2356
- return {
2357
- type: "pino",
2358
- file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
2359
- line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
2360
- };
2361
- }
2362
- if (sourceName === "winston") {
2363
- return {
2364
- type: "winston",
2365
- file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
2366
- line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
2367
- };
2368
- }
2369
- const mapType = (t) => {
2370
- switch (t) {
2371
- case "manual":
2372
- return "manual";
2373
- case "json-log":
2374
- return "json-log";
2375
- case "log4js":
2376
- return "log4js";
2377
- case "adapter":
2378
- case "ai-sdk":
2379
- case "otel":
2380
- return "adapter";
2381
- default:
2382
- return "json-log";
2383
- }
2384
- };
2385
- return {
2386
- type: mapType(event.source.type),
2387
- file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
2388
- line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
2389
- };
2390
- }
2391
- function buildInspectAttributes(event) {
2392
- const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
2393
- if (event.inputSummary !== void 0) {
2394
- attrs.inputSummary = event.inputSummary;
2395
- }
2396
- if (event.outputSummary !== void 0) {
2397
- attrs.outputSummary = event.outputSummary;
2398
- }
2399
- if (event.error) {
2400
- if (event.error.name !== void 0) {
2401
- attrs.errorName = event.error.name;
2402
- }
2403
- attrs.errorMessage = event.error.message;
2404
- if (event.error.code !== void 0) {
2405
- attrs.errorCode = event.error.code;
2406
- }
2407
- }
2408
- if (event.tokenUsage) {
2409
- attrs.tokens = { ...event.tokenUsage };
2410
- }
2411
- if (event.source.type === "ai-sdk" || event.source.type === "otel") {
2412
- attrs.originalSourceType = event.source.type;
2413
- }
2414
- if (event.source.name !== void 0) {
2415
- attrs.sourceName = event.source.name;
2416
- }
2417
- if (event.source.version !== void 0) {
2418
- attrs.sourceVersion = event.source.version;
2419
- }
2420
- return attrs;
2421
- }
2422
- function persistedInspectEventToInspectEvent(event) {
2423
- if (!isPersistedInspectEvent(event)) {
2424
- throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
2425
- }
2426
- const ts = parseIsoToMs3(event.timestamp);
2427
- const attrs = buildInspectAttributes(event);
2428
- if (ts.invalidTimestamp) {
2429
- attrs.invalidTimestamp = true;
2430
- }
2431
- let status;
2432
- if (event.status === "running" || event.status === "ok" || event.status === "error") {
2433
- status = event.status;
2434
- } else if (event.status === "unknown") {
2435
- attrs.persistedStatus = "unknown";
2436
- }
2437
- const out = {
2438
- eventId: event.eventId,
2439
- runId: event.runId,
2440
- name: event.name,
2441
- kind: event.kind,
2442
- timestamp: ts.ms,
2443
- confidence: event.confidence,
2444
- source: mapPersistedSourceToInspect(event),
2445
- attributes: compactAttributes3(attrs)
2446
- };
2447
- if (event.parentId !== void 0) {
2448
- out.parentId = event.parentId;
2449
- }
2450
- if (status !== void 0) {
2451
- out.status = status;
2452
- }
2453
- if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
2454
- out.durationMs = event.durationMs;
2105
+ if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
2106
+ out.durationMs = event.durationMs;
2455
2107
  }
2456
2108
  return out;
2457
2109
  }
@@ -2470,93 +2122,15 @@ function persistedInspectEventsToInspectEvents(events, options) {
2470
2122
  return out;
2471
2123
  }
2472
2124
 
2473
- // packages/core/src/logs/tree-builder.ts
2474
- function inc(map, key) {
2475
- map[key] = (map[key] ?? 0) + 1;
2125
+ // packages/core/src/persisted/tree-bridge.ts
2126
+ function persistedInspectEventsToRunTrees(events, options) {
2127
+ const inspectEvents = persistedInspectEventsToInspectEvents(events, {
2128
+ skipInvalid: options?.skipInvalid
2129
+ });
2130
+ return new TreeBuilder().build(inspectEvents);
2476
2131
  }
2477
- function computeRunStatus(events) {
2478
- let hasRunning = false;
2479
- for (const e of events) {
2480
- if (e.status === "error") return "error";
2481
- if (e.status === "running") hasRunning = true;
2482
- }
2483
- if (hasRunning) return "running";
2484
- return "ok";
2485
- }
2486
- var TreeBuilder = class {
2487
- constructor(options) {
2488
- void options?.config;
2489
- }
2490
- build(events) {
2491
- const byRun = /* @__PURE__ */ new Map();
2492
- for (const e of events) {
2493
- if (!byRun.has(e.runId)) byRun.set(e.runId, []);
2494
- byRun.get(e.runId).push(e);
2495
- }
2496
- const out = [];
2497
- for (const [runId, runEvents] of byRun.entries()) {
2498
- const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
2499
- const nodes = /* @__PURE__ */ new Map();
2500
- for (const e of sorted) {
2501
- nodes.set(e.eventId, { event: e, children: [], depth: 0 });
2502
- }
2503
- const roots = [];
2504
- for (const node of nodes.values()) {
2505
- const parentId = node.event.parentId;
2506
- if (parentId && nodes.has(parentId)) {
2507
- nodes.get(parentId).children.push(node);
2508
- } else {
2509
- roots.push(node);
2510
- }
2511
- }
2512
- const assignDepth = (n, depth) => {
2513
- n.depth = depth;
2514
- for (const c of n.children) assignDepth(c, depth + 1);
2515
- };
2516
- for (const r of roots) assignDepth(r, 0);
2517
- const confidenceBreakdown = {
2518
- explicit: 0,
2519
- correlated: 0,
2520
- heuristic: 0,
2521
- unknown: 0
2522
- };
2523
- const kinds = {};
2524
- for (const e of sorted) {
2525
- inc(confidenceBreakdown, e.confidence);
2526
- kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
2527
- }
2528
- const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
2529
- const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
2530
- const status = computeRunStatus(sorted);
2531
- const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
2532
- const name = sorted.find((e) => e.kind === "RUN")?.name;
2533
- out.push({
2534
- runId,
2535
- name,
2536
- status,
2537
- startedAt,
2538
- endedAt: status === "running" ? void 0 : endedAt,
2539
- durationMs,
2540
- children: roots,
2541
- metadata: {
2542
- totalEvents: sorted.length,
2543
- confidenceBreakdown,
2544
- kinds
2545
- }
2546
- });
2547
- }
2548
- out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
2549
- return out;
2550
- }
2551
- };
2552
2132
 
2553
- // packages/core/src/persisted/tree-bridge.ts
2554
- function persistedInspectEventsToRunTrees(events, options) {
2555
- const inspectEvents = persistedInspectEventsToInspectEvents(events, {
2556
- skipInvalid: options?.skipInvalid
2557
- });
2558
- return new TreeBuilder().build(inspectEvents);
2559
- }
2133
+ // packages/core/src/readers/index.ts
2560
2134
  var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
2561
2135
  var MIN_DETECTION_CONFIDENCE = 0.5;
2562
2136
  var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
@@ -3049,7 +2623,7 @@ function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
3049
2623
  function mapOpenInferenceKind(span, attributes, pathPrefix) {
3050
2624
  const warnings = [];
3051
2625
  const agentInspectKind = attributes["agent_inspect.kind"];
3052
- if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
2626
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG" || agentInspectKind === "OUTCOME") {
3053
2627
  return { kind: agentInspectKind, warnings };
3054
2628
  }
3055
2629
  const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
@@ -3570,7 +3144,7 @@ function mapOtlpStatus(status) {
3570
3144
  function readOtlpKind(attributes, pathPrefix) {
3571
3145
  const warnings = [];
3572
3146
  const agentInspectKind = attributes["agent_inspect.kind"];
3573
- if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
3147
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG" || agentInspectKind === "OUTCOME") {
3574
3148
  return { kind: agentInspectKind, warnings };
3575
3149
  }
3576
3150
  const operation = attributes["gen_ai.operation.name"];
@@ -4034,100 +3608,667 @@ async function detectTraceFormat(input, options = {}) {
4034
3608
  });
4035
3609
  }
4036
3610
  }
4037
- const sorted = sortCandidates(
4038
- candidates.filter((candidate) => candidate.confidence >= MIN_DETECTION_CONFIDENCE)
4039
- );
4040
- const candidateWarnings = collectWarnings(sorted);
4041
- const lowConfidenceWarnings = candidates.length > sorted.length ? [
4042
- {
4043
- code: "low_confidence_candidates",
4044
- message: `Ignored ${candidates.length - sorted.length} low-confidence format candidate(s).`,
4045
- severity: "info"
3611
+ const sorted = sortCandidates(
3612
+ candidates.filter((candidate) => candidate.confidence >= MIN_DETECTION_CONFIDENCE)
3613
+ );
3614
+ const candidateWarnings = collectWarnings(sorted);
3615
+ const lowConfidenceWarnings = candidates.length > sorted.length ? [
3616
+ {
3617
+ code: "low_confidence_candidates",
3618
+ message: `Ignored ${candidates.length - sorted.length} low-confidence format candidate(s).`,
3619
+ severity: "info"
3620
+ }
3621
+ ] : [];
3622
+ const allWarnings = dedupeWarnings([
3623
+ ...warnings,
3624
+ ...candidateWarnings,
3625
+ ...lowConfidenceWarnings
3626
+ ]);
3627
+ if (sorted.length === 0) {
3628
+ return {
3629
+ status: "unsupported",
3630
+ candidates: [],
3631
+ warnings: allWarnings
3632
+ };
3633
+ }
3634
+ const [best, second] = sorted;
3635
+ if (second !== void 0 && best.confidence - second.confidence <= AMBIGUOUS_CONFIDENCE_DELTA) {
3636
+ return {
3637
+ status: "ambiguous",
3638
+ candidates: sorted,
3639
+ warnings: [
3640
+ ...allWarnings,
3641
+ {
3642
+ code: "ambiguous_format_candidates",
3643
+ message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
3644
+ severity: "warning"
3645
+ }
3646
+ ]
3647
+ };
3648
+ }
3649
+ return {
3650
+ status: "detected",
3651
+ format: best.format,
3652
+ candidates: sorted,
3653
+ warnings: allWarnings
3654
+ };
3655
+ }
3656
+ async function readTrace(input, options = {}) {
3657
+ const readers = options.readers ?? DEFAULT_TRACE_READERS;
3658
+ const detection = await detectTraceFormat(input, options);
3659
+ if (detection.status === "unsupported" || detection.format === void 0) {
3660
+ throw new TraceReadError(
3661
+ "unsupported_format",
3662
+ "No trace reader could detect the input format.",
3663
+ detection.warnings
3664
+ );
3665
+ }
3666
+ if (detection.status === "ambiguous") {
3667
+ throw new TraceReadError(
3668
+ "ambiguous_format",
3669
+ "Multiple trace readers matched the input with equal confidence.",
3670
+ detection.warnings
3671
+ );
3672
+ }
3673
+ const reader = findReaderByFormat(detection.format, readers);
3674
+ if (!reader) {
3675
+ throw new TraceReadError(
3676
+ "unsupported_format",
3677
+ `No trace reader is registered for format "${detection.format}".`,
3678
+ detection.warnings
3679
+ );
3680
+ }
3681
+ try {
3682
+ const result = await reader.read(input, { format: detection.format });
3683
+ return {
3684
+ ...result,
3685
+ format: result.format || detection.format,
3686
+ warnings: [...detection.warnings, ...result.warnings]
3687
+ };
3688
+ } catch (error) {
3689
+ if (error instanceof TraceReadError) {
3690
+ throw new TraceReadError(
3691
+ error.code,
3692
+ error.message,
3693
+ dedupeWarnings([...detection.warnings, ...error.warnings])
3694
+ );
3695
+ }
3696
+ throw new TraceReadError(
3697
+ "reader_failed",
3698
+ error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed.`,
3699
+ detection.warnings
3700
+ );
3701
+ }
3702
+ }
3703
+ function openTrace(input, options = {}) {
3704
+ return readTrace(input, options);
3705
+ }
3706
+
3707
+ // packages/core/src/diff/comparable.ts
3708
+ function extractOutputPreview(meta) {
3709
+ if (meta === void 0) return void 0;
3710
+ if ("outputPreview" in meta) return meta.outputPreview;
3711
+ if ("resultPreview" in meta) return meta.resultPreview;
3712
+ return void 0;
3713
+ }
3714
+ function mapStepStatus(s) {
3715
+ if (s === void 0) return "running";
3716
+ return s;
3717
+ }
3718
+ function manualTraceEventsToComparableRun(events) {
3719
+ const started = events.find((e) => e.event === "run_started");
3720
+ if (!started || started.event !== "run_started") {
3721
+ throw new Error("Invalid trace: missing run_started");
3722
+ }
3723
+ const rs = started;
3724
+ const runId = rs.runId;
3725
+ const completedAll = events.filter((e) => e.event === "run_completed");
3726
+ const lastCompleted = completedAll[completedAll.length - 1];
3727
+ let runStatus;
3728
+ if (lastCompleted === void 0) runStatus = "running";
3729
+ else runStatus = lastCompleted.status;
3730
+ const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
3731
+ const steps = /* @__PURE__ */ new Map();
3732
+ let order = 0;
3733
+ for (const e of events) {
3734
+ if (e.event !== "step_started") continue;
3735
+ const s = e;
3736
+ const meta = s.metadata ? { ...s.metadata } : void 0;
3737
+ steps.set(s.stepId, {
3738
+ id: s.stepId,
3739
+ parentId: s.parentId,
3740
+ name: s.name,
3741
+ type: s.type,
3742
+ order: order++,
3743
+ timestamp: s.timestamp,
3744
+ metadata: meta
3745
+ });
3746
+ }
3747
+ for (const e of events) {
3748
+ if (e.event !== "step_completed") continue;
3749
+ const acc = steps.get(e.stepId);
3750
+ if (!acc) continue;
3751
+ acc.status = e.status;
3752
+ acc.durationMs = e.durationMs;
3753
+ if (e.error?.message) acc.errorMsg = e.error.message;
3754
+ const extra = e;
3755
+ if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
3756
+ acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
3757
+ }
3758
+ }
3759
+ const nodes = /* @__PURE__ */ new Map();
3760
+ for (const acc of steps.values()) {
3761
+ let meta = acc.metadata ? { ...acc.metadata } : void 0;
3762
+ if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
3763
+ meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
3764
+ }
3765
+ const outputPreview = extractOutputPreview(meta);
3766
+ const sc = {
3767
+ id: acc.id,
3768
+ name: acc.name,
3769
+ type: acc.type,
3770
+ status: mapStepStatus(acc.status),
3771
+ durationMs: acc.durationMs,
3772
+ error: acc.errorMsg,
3773
+ metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
3774
+ outputPreview,
3775
+ children: []
3776
+ };
3777
+ nodes.set(acc.id, sc);
3778
+ }
3779
+ const roots = [];
3780
+ const sortByOrder = (a, b) => {
3781
+ const oa = steps.get(a.id)?.order ?? 0;
3782
+ const ob = steps.get(b.id)?.order ?? 0;
3783
+ return oa - ob;
3784
+ };
3785
+ for (const acc of steps.values()) {
3786
+ const node = nodes.get(acc.id);
3787
+ if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
3788
+ nodes.get(acc.parentId).children.push(node);
3789
+ } else {
3790
+ roots.push(node);
3791
+ }
3792
+ }
3793
+ roots.sort(sortByOrder);
3794
+ for (const n of nodes.values()) {
3795
+ n.children.sort(sortByOrder);
3796
+ }
3797
+ return {
3798
+ runId,
3799
+ name: rs.name,
3800
+ status: runStatus,
3801
+ durationMs,
3802
+ steps: roots
3803
+ };
3804
+ }
3805
+
3806
+ // packages/core/src/exporters/helpers.ts
3807
+ var REDACT_SUBSTRINGS = [
3808
+ "authorization",
3809
+ "cookie",
3810
+ "token",
3811
+ "apikey",
3812
+ "password",
3813
+ "secret",
3814
+ "email"
3815
+ ];
3816
+ function shouldRedactKey(key) {
3817
+ const k = key.toLowerCase();
3818
+ for (const s of REDACT_SUBSTRINGS) {
3819
+ if (k.includes(s)) return true;
3820
+ }
3821
+ return false;
3822
+ }
3823
+ function safeString(value, maxLength) {
3824
+ if (value === null || value === void 0) return "";
3825
+ let s;
3826
+ if (typeof value === "string") s = value;
3827
+ else if (typeof value === "number" || typeof value === "boolean") s = String(value);
3828
+ else s = stableJson(value, false);
3829
+ if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
3830
+ return `${s.slice(0, maxLength)}\u2026`;
3831
+ }
3832
+ return s;
3833
+ }
3834
+ function escapeMarkdown(value) {
3835
+ return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
3836
+ }
3837
+ function sortKeysDeep(input) {
3838
+ if (input === null || typeof input !== "object") return input;
3839
+ if (Array.isArray(input)) return input.map(sortKeysDeep);
3840
+ const o = input;
3841
+ const out = {};
3842
+ for (const k of Object.keys(o).sort()) {
3843
+ out[k] = sortKeysDeep(o[k]);
3844
+ }
3845
+ return out;
3846
+ }
3847
+ function stableJson(value, pretty) {
3848
+ const sorted = sortKeysDeep(value);
3849
+ return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
3850
+ }
3851
+ function compactAttributes3(attrs, options) {
3852
+ if (attrs === void 0) return {};
3853
+ const maxLen = options?.maxLength ?? 500;
3854
+ const out = {};
3855
+ for (const key of Object.keys(attrs).sort()) {
3856
+ if (shouldRedactKey(key)) {
3857
+ out[key] = "[REDACTED]";
3858
+ continue;
3859
+ }
3860
+ const v = attrs[key];
3861
+ out[key] = compactValue(v, maxLen);
3862
+ }
3863
+ return out;
3864
+ }
3865
+ function compactValue(value, maxLen, redacted) {
3866
+ if (value === null || typeof value !== "object") {
3867
+ return typeof value === "string" ? safeString(value, maxLen) : value;
3868
+ }
3869
+ if (Array.isArray(value)) {
3870
+ const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen));
3871
+ if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
3872
+ return arr;
3873
+ }
3874
+ const o = value;
3875
+ const inner = {};
3876
+ for (const k of Object.keys(o)) {
3877
+ if (shouldRedactKey(k)) inner[k] = "[REDACTED]";
3878
+ else inner[k] = compactValue(o[k], maxLen);
3879
+ }
3880
+ return inner;
3881
+ }
3882
+ function flattenTree(tree) {
3883
+ const out = [];
3884
+ function walk(nodes) {
3885
+ for (const n of nodes) {
3886
+ out.push(n);
3887
+ if (n.children.length > 0) walk(n.children);
3888
+ }
3889
+ }
3890
+ walk(tree.children);
3891
+ return out;
3892
+ }
3893
+
3894
+ // packages/core/src/diff/engine.ts
3895
+ var DEFAULT_THRESHOLD_MS = 0;
3896
+ function pathSeg(step, index) {
3897
+ return { index, name: step.name, stepId: step.id };
3898
+ }
3899
+ function buildPath(segments) {
3900
+ return { path: [...segments] };
3901
+ }
3902
+ function pairSteps(left, right) {
3903
+ const usedRight = /* @__PURE__ */ new Set();
3904
+ const pairs = [];
3905
+ for (let i = 0; i < left.length; i++) {
3906
+ const L = left[i];
3907
+ let R = right.find((r) => !usedRight.has(r.id) && r.id === L.id);
3908
+ if (R === void 0 && i < right.length && !usedRight.has(right[i].id)) {
3909
+ const cand = right[i];
3910
+ if (cand.name === L.name && (cand.type ?? "") === (L.type ?? "")) {
3911
+ R = cand;
3912
+ }
3913
+ }
3914
+ if (R === void 0) {
3915
+ R = right.find(
3916
+ (r) => !usedRight.has(r.id) && r.name === L.name && (r.type ?? "") === (L.type ?? "")
3917
+ );
3918
+ }
3919
+ if (R !== void 0) {
3920
+ usedRight.add(R.id);
3921
+ pairs.push([L, R]);
3922
+ } else {
3923
+ pairs.push([L, void 0]);
3924
+ }
3925
+ }
3926
+ for (const R of right) {
3927
+ if (!usedRight.has(R.id)) {
3928
+ pairs.push([void 0, R]);
3929
+ }
3930
+ }
3931
+ return pairs;
3932
+ }
3933
+ function compareLeafSteps(L, R, segments, opts, out) {
3934
+ const path12 = buildPath(segments);
3935
+ if (L.name !== R.name) {
3936
+ out.push({
3937
+ kind: "structure",
3938
+ severity: "warning",
3939
+ message: "Step name differs",
3940
+ path: path12,
3941
+ left: L.name,
3942
+ right: R.name
3943
+ });
3944
+ }
3945
+ if ((L.type ?? "") !== (R.type ?? "")) {
3946
+ out.push({
3947
+ kind: "step-type",
3948
+ severity: "warning",
3949
+ message: "Step type differs",
3950
+ path: path12,
3951
+ left: L.type,
3952
+ right: R.type
3953
+ });
3954
+ }
3955
+ if ((L.status ?? "") !== (R.status ?? "")) {
3956
+ out.push({
3957
+ kind: "step-status",
3958
+ severity: "warning",
3959
+ message: "Step status differs",
3960
+ path: path12,
3961
+ left: L.status,
3962
+ right: R.status
3963
+ });
3964
+ }
3965
+ const le = L.error ?? "";
3966
+ const re = R.error ?? "";
3967
+ if (le !== re) {
3968
+ out.push({
3969
+ kind: "error",
3970
+ severity: "error",
3971
+ message: "Step error message differs",
3972
+ path: path12,
3973
+ left: le || void 0,
3974
+ right: re || void 0
3975
+ });
3976
+ }
3977
+ if (!opts.ignoreDuration) {
3978
+ const ld = L.durationMs;
3979
+ const rd = R.durationMs;
3980
+ const th = opts.durationThresholdMs;
3981
+ let differs = false;
3982
+ if (ld === void 0 && rd === void 0) differs = false;
3983
+ else if (ld === void 0 || rd === void 0) differs = true;
3984
+ else differs = Math.abs(ld - rd) > th;
3985
+ if (differs) {
3986
+ out.push({
3987
+ kind: "duration",
3988
+ severity: "info",
3989
+ message: "Step duration differs",
3990
+ path: path12,
3991
+ left: ld,
3992
+ right: rd
3993
+ });
3994
+ }
3995
+ }
3996
+ const lm = stableJson(L.metadata ?? {});
3997
+ const rm = stableJson(R.metadata ?? {});
3998
+ if (lm !== rm) {
3999
+ out.push({
4000
+ kind: "metadata",
4001
+ severity: "info",
4002
+ message: "Step metadata differs",
4003
+ path: path12,
4004
+ left: L.metadata,
4005
+ right: R.metadata
4006
+ });
4007
+ }
4008
+ const lo = stableJson(L.outputPreview ?? null);
4009
+ const ro = stableJson(R.outputPreview ?? null);
4010
+ if (lo !== ro) {
4011
+ out.push({
4012
+ kind: "output",
4013
+ severity: "info",
4014
+ message: "Output preview differs",
4015
+ path: path12,
4016
+ left: L.outputPreview,
4017
+ right: R.outputPreview
4018
+ });
4019
+ }
4020
+ }
4021
+ function compareRecursive(L, R, segments, opts, out) {
4022
+ compareLeafSteps(L, R, segments, opts, out);
4023
+ const pairs = pairSteps(L.children, R.children);
4024
+ let ci = 0;
4025
+ for (const [lch, rch] of pairs) {
4026
+ if (lch !== void 0 && rch !== void 0) {
4027
+ compareRecursive(lch, rch, [...segments, pathSeg(lch, ci)], opts, out);
4028
+ } else if (lch !== void 0) {
4029
+ out.push({
4030
+ kind: "step-removed",
4031
+ severity: "warning",
4032
+ message: `Step only in left run: ${lch.name}`,
4033
+ path: buildPath([...segments, pathSeg(lch, ci)]),
4034
+ left: lch.id,
4035
+ right: void 0
4036
+ });
4037
+ } else if (rch !== void 0) {
4038
+ out.push({
4039
+ kind: "step-added",
4040
+ severity: "warning",
4041
+ message: `Step only in right run: ${rch.name}`,
4042
+ path: buildPath([...segments, pathSeg(rch, ci)]),
4043
+ left: void 0,
4044
+ right: rch.id
4045
+ });
4046
+ }
4047
+ ci += 1;
4048
+ }
4049
+ }
4050
+ function mergeDiffDefaults(options) {
4051
+ return {
4052
+ ignoreDuration: false,
4053
+ durationThresholdMs: DEFAULT_THRESHOLD_MS,
4054
+ focus: "all",
4055
+ check: "all"
4056
+ };
4057
+ }
4058
+ function kindMatchesFilter(kind, merged) {
4059
+ return true;
4060
+ }
4061
+ function diffRuns(left, right, options) {
4062
+ const merged = mergeDiffDefaults();
4063
+ const opts = {
4064
+ ignoreDuration: merged.ignoreDuration,
4065
+ durationThresholdMs: merged.durationThresholdMs
4066
+ };
4067
+ const raw = [];
4068
+ if ((left.status ?? "") !== (right.status ?? "")) {
4069
+ raw.push({
4070
+ kind: "run-status",
4071
+ severity: "warning",
4072
+ message: "Run completion status differs",
4073
+ left: left.status,
4074
+ right: right.status
4075
+ });
4076
+ }
4077
+ {
4078
+ const ld = left.durationMs;
4079
+ const rd = right.durationMs;
4080
+ const th = merged.durationThresholdMs;
4081
+ let differs = false;
4082
+ if (ld === void 0 && rd === void 0) differs = false;
4083
+ else if (ld === void 0 || rd === void 0) differs = true;
4084
+ else differs = Math.abs(ld - rd) > th;
4085
+ if (differs) {
4086
+ raw.push({
4087
+ kind: "duration",
4088
+ severity: "info",
4089
+ message: "Run duration differs",
4090
+ left: ld,
4091
+ right: rd
4092
+ });
4093
+ }
4094
+ }
4095
+ const pairs = pairSteps(left.steps, right.steps);
4096
+ let idx = 0;
4097
+ for (const [ls, rs] of pairs) {
4098
+ if (ls !== void 0 && rs !== void 0) {
4099
+ compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
4100
+ idx += 1;
4101
+ } else if (ls !== void 0) {
4102
+ raw.push({
4103
+ kind: "step-removed",
4104
+ severity: "warning",
4105
+ message: `Step only in left run: ${ls.name}`,
4106
+ path: buildPath([pathSeg(ls, idx)]),
4107
+ left: ls.id,
4108
+ right: void 0
4109
+ });
4110
+ idx += 1;
4111
+ } else if (rs !== void 0) {
4112
+ raw.push({
4113
+ kind: "step-added",
4114
+ severity: "warning",
4115
+ message: `Step only in right run: ${rs.name}`,
4116
+ path: buildPath([pathSeg(rs, idx)]),
4117
+ left: void 0,
4118
+ right: rs.id
4119
+ });
4120
+ idx += 1;
4046
4121
  }
4047
- ] : [];
4048
- const allWarnings = dedupeWarnings([
4049
- ...warnings,
4050
- ...candidateWarnings,
4051
- ...lowConfidenceWarnings
4052
- ]);
4053
- if (sorted.length === 0) {
4054
- return {
4055
- status: "unsupported",
4056
- candidates: [],
4057
- warnings: allWarnings
4058
- };
4059
4122
  }
4060
- const [best, second] = sorted;
4061
- if (second !== void 0 && best.confidence - second.confidence <= AMBIGUOUS_CONFIDENCE_DELTA) {
4062
- return {
4063
- status: "ambiguous",
4064
- candidates: sorted,
4065
- warnings: [
4066
- ...allWarnings,
4067
- {
4068
- code: "ambiguous_format_candidates",
4069
- message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
4070
- severity: "warning"
4071
- }
4072
- ]
4073
- };
4123
+ const differences = raw.filter((d) => kindMatchesFilter(d.kind));
4124
+ let errors = 0;
4125
+ let warnings = 0;
4126
+ let info = 0;
4127
+ for (const d of differences) {
4128
+ if (d.severity === "error") errors += 1;
4129
+ else if (d.severity === "warning") warnings += 1;
4130
+ else info += 1;
4074
4131
  }
4075
- return {
4076
- status: "detected",
4077
- format: best.format,
4078
- candidates: sorted,
4079
- warnings: allWarnings
4132
+ const firstVisible = differences[0];
4133
+ const firstDivergence = firstVisible !== void 0 ? {
4134
+ kind: "first-divergence",
4135
+ severity: firstVisible.severity,
4136
+ message: `First divergence: ${firstVisible.message}`,
4137
+ path: firstVisible.path,
4138
+ left: firstVisible.left,
4139
+ right: firstVisible.right
4140
+ } : void 0;
4141
+ const summary = {
4142
+ leftRunId: left.runId,
4143
+ rightRunId: right.runId,
4144
+ totalDifferences: differences.length,
4145
+ errors,
4146
+ warnings,
4147
+ info,
4148
+ firstDivergence
4080
4149
  };
4150
+ return { summary, differences };
4081
4151
  }
4082
- async function readTrace(input, options = {}) {
4083
- const readers = options.readers ?? DEFAULT_TRACE_READERS;
4084
- const detection = await detectTraceFormat(input, options);
4085
- if (detection.status === "unsupported" || detection.format === void 0) {
4086
- throw new TraceReadError(
4087
- "unsupported_format",
4088
- "No trace reader could detect the input format.",
4089
- detection.warnings
4090
- );
4152
+
4153
+ // packages/core/src/exporters/markdown-exporter.ts
4154
+ function renderTreeAscii(nodes, indent = "") {
4155
+ const lines = [];
4156
+ for (let i = 0; i < nodes.length; i++) {
4157
+ const n = nodes[i];
4158
+ const last = i === nodes.length - 1;
4159
+ const branch = last ? "\u2514\u2500 " : "\u251C\u2500 ";
4160
+ const ev = n.event;
4161
+ const status = ev.status ?? "?";
4162
+ const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
4163
+ lines.push(`${indent}${branch}${escapeMarkdown(ev.name)} [${ev.kind}] ${status} (${dur})`);
4164
+ const nextIndent = indent + (last ? " " : "\u2502 ");
4165
+ if (n.children.length > 0) {
4166
+ const childStr = renderTreeAscii(n.children, nextIndent);
4167
+ if (childStr.length > 0) lines.push(childStr);
4168
+ }
4091
4169
  }
4092
- if (detection.status === "ambiguous") {
4093
- throw new TraceReadError(
4094
- "ambiguous_format",
4095
- "Multiple trace readers matched the input with equal confidence.",
4096
- detection.warnings
4170
+ return lines.join("\n");
4171
+ }
4172
+ function exportMarkdown(tree, options) {
4173
+ const warnings = [];
4174
+ const includeMetadata = options?.includeMetadata ?? true;
4175
+ const includeAttributes = options?.includeAttributes ?? false;
4176
+ const includeErrors = options?.includeErrors ?? true;
4177
+ const maxLen = options?.maxAttributeLength ?? 500;
4178
+ const titleName = tree.name ?? tree.runId;
4179
+ const lines = [];
4180
+ lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
4181
+ lines.push("");
4182
+ lines.push("Generated locally by AgentInspect. Review for sensitive data before sharing.");
4183
+ lines.push("");
4184
+ if (includeMetadata) {
4185
+ lines.push("## Summary");
4186
+ lines.push("");
4187
+ lines.push(`- **runId**: ${escapeMarkdown(tree.runId)}`);
4188
+ if (tree.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(tree.name)}`);
4189
+ lines.push(`- **status**: ${escapeMarkdown(String(tree.status ?? "unknown"))}`);
4190
+ lines.push(
4191
+ `- **durationMs**: ${tree.durationMs !== void 0 ? escapeMarkdown(String(tree.durationMs)) : "-"}`
4097
4192
  );
4098
- }
4099
- const reader = findReaderByFormat(detection.format, readers);
4100
- if (!reader) {
4101
- throw new TraceReadError(
4102
- "unsupported_format",
4103
- `No trace reader is registered for format "${detection.format}".`,
4104
- detection.warnings
4193
+ lines.push(
4194
+ `- **startedAt**: ${tree.startedAt !== void 0 ? escapeMarkdown(String(tree.startedAt)) : "-"}`
4195
+ );
4196
+ lines.push(
4197
+ `- **endedAt**: ${tree.endedAt !== void 0 ? escapeMarkdown(String(tree.endedAt)) : "-"}`
4105
4198
  );
4199
+ lines.push(`- **totalEvents**: ${tree.metadata.totalEvents}`);
4200
+ lines.push("");
4201
+ lines.push("### Confidence breakdown");
4202
+ lines.push("");
4203
+ lines.push("| bucket | count |");
4204
+ lines.push("| --- | --- |");
4205
+ for (const k of Object.keys(tree.metadata.confidenceBreakdown).sort()) {
4206
+ const key = k;
4207
+ lines.push(
4208
+ `| ${escapeMarkdown(key)} | ${tree.metadata.confidenceBreakdown[key]} |`
4209
+ );
4210
+ }
4211
+ lines.push("");
4212
+ lines.push("### Kind breakdown");
4213
+ lines.push("");
4214
+ lines.push("| kind | count |");
4215
+ lines.push("| --- | --- |");
4216
+ for (const k of Object.keys(tree.metadata.kinds).sort()) {
4217
+ const key = k;
4218
+ const c = tree.metadata.kinds[key];
4219
+ if (c > 0) lines.push(`| ${escapeMarkdown(key)} | ${c} |`);
4220
+ }
4221
+ lines.push("");
4106
4222
  }
4107
- try {
4108
- const result = await reader.read(input, { format: detection.format });
4109
- return {
4110
- ...result,
4111
- format: result.format || detection.format,
4112
- warnings: [...detection.warnings, ...result.warnings]
4113
- };
4114
- } catch (error) {
4115
- if (error instanceof TraceReadError) {
4116
- throw new TraceReadError(
4117
- error.code,
4118
- error.message,
4119
- dedupeWarnings([...detection.warnings, ...error.warnings])
4223
+ lines.push("## Execution tree");
4224
+ lines.push("");
4225
+ lines.push("```text");
4226
+ lines.push(
4227
+ tree.children.length > 0 ? renderTreeAscii(tree.children) : "(no steps)"
4228
+ );
4229
+ lines.push("```");
4230
+ lines.push("");
4231
+ const flat = flattenTree(tree);
4232
+ const errors = flat.filter((n) => n.event.status === "error");
4233
+ if (includeErrors && errors.length > 0) {
4234
+ lines.push("## Errors");
4235
+ lines.push("");
4236
+ for (const n of errors) {
4237
+ const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
4238
+ n.event.attributes.error.message,
4239
+ maxLen
4240
+ ) : "";
4241
+ lines.push(
4242
+ `- **${escapeMarkdown(n.event.name)}** (${escapeMarkdown(n.event.eventId)}): ${escapeMarkdown(msg || "error")}`
4120
4243
  );
4121
4244
  }
4122
- throw new TraceReadError(
4123
- "reader_failed",
4124
- error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed.`,
4125
- detection.warnings
4245
+ lines.push("");
4246
+ }
4247
+ if (includeAttributes) {
4248
+ lines.push("## Attributes (bounded)");
4249
+ lines.push("");
4250
+ for (const n of flat) {
4251
+ if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
4252
+ const compact = compactAttributes3(n.event.attributes, {
4253
+ maxLength: maxLen});
4254
+ lines.push(`### ${escapeMarkdown(n.event.name)}`);
4255
+ lines.push("");
4256
+ lines.push("```json");
4257
+ lines.push(stableJson(compact, true));
4258
+ lines.push("```");
4259
+ lines.push("");
4260
+ }
4261
+ warnings.push(
4262
+ "Attributes may still contain sensitive data; review exports before sharing."
4126
4263
  );
4127
4264
  }
4128
- }
4129
- function openTrace(input, options = {}) {
4130
- return readTrace(input, options);
4265
+ return {
4266
+ format: "markdown",
4267
+ content: lines.join("\n"),
4268
+ contentType: "text/markdown",
4269
+ fileExtension: ".md",
4270
+ warnings
4271
+ };
4131
4272
  }
4132
4273
 
4133
4274
  // packages/mcp-server/src/tools.ts