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