agent-inspect 6.31.5 → 6.31.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +1 -1
  3. package/docs/AI-SDK-ADOPTION.md +62 -34
  4. package/docs/SAFE-TRACE-SHARING.md +1 -1
  5. package/package.json +1 -1
  6. package/packages/cli/dist/{chunk-LKEPABND.mjs → chunk-QV73NM25.mjs} +100 -31
  7. package/packages/cli/dist/chunk-QV73NM25.mjs.map +1 -0
  8. package/packages/cli/dist/index.cjs +474 -92
  9. package/packages/cli/dist/index.cjs.map +1 -1
  10. package/packages/cli/dist/index.mjs +379 -67
  11. package/packages/cli/dist/index.mjs.map +1 -1
  12. package/packages/cli/dist/{src-4WYF5RFI.mjs → src-KFOBXFPN.mjs} +3 -3
  13. package/packages/cli/dist/{src-4WYF5RFI.mjs.map → src-KFOBXFPN.mjs.map} +1 -1
  14. package/packages/core/dist/advanced.cjs +95 -23
  15. package/packages/core/dist/advanced.cjs.map +1 -1
  16. package/packages/core/dist/advanced.mjs +11 -11
  17. package/packages/core/dist/checks.cjs +2 -5
  18. package/packages/core/dist/checks.cjs.map +1 -1
  19. package/packages/core/dist/checks.mjs +3 -3
  20. package/packages/core/dist/{chunk-OSGNGRHF.mjs → chunk-3RV3JILQ.mjs} +34 -16
  21. package/packages/core/dist/chunk-3RV3JILQ.mjs.map +1 -0
  22. package/packages/core/dist/{chunk-NY4ILMXK.mjs → chunk-7YYS742B.mjs} +50 -13
  23. package/packages/core/dist/chunk-7YYS742B.mjs.map +1 -0
  24. package/packages/core/dist/{chunk-2KJR6X4V.mjs → chunk-DK3OW6UI.mjs} +3 -3
  25. package/packages/core/dist/chunk-DK3OW6UI.mjs.map +1 -0
  26. package/packages/core/dist/{chunk-PKDBFBJ7.mjs → chunk-LXYPM2ZO.mjs} +5 -8
  27. package/packages/core/dist/{chunk-PKDBFBJ7.mjs.map → chunk-LXYPM2ZO.mjs.map} +1 -1
  28. package/packages/core/dist/{chunk-INE5IWLN.mjs → chunk-N5PZAI6Q.mjs} +3 -3
  29. package/packages/core/dist/{chunk-INE5IWLN.mjs.map → chunk-N5PZAI6Q.mjs.map} +1 -1
  30. package/packages/core/dist/{chunk-Q5SLKV2Y.mjs → chunk-QLYUWKK7.mjs} +19 -2
  31. package/packages/core/dist/chunk-QLYUWKK7.mjs.map +1 -0
  32. package/packages/core/dist/{chunk-MTMTXTJL.mjs → chunk-VQUB3WRI.mjs} +4 -4
  33. package/packages/core/dist/{chunk-MTMTXTJL.mjs.map → chunk-VQUB3WRI.mjs.map} +1 -1
  34. package/packages/core/dist/exporters.cjs +253 -50
  35. package/packages/core/dist/exporters.cjs.map +1 -1
  36. package/packages/core/dist/exporters.mjs +208 -42
  37. package/packages/core/dist/exporters.mjs.map +1 -1
  38. package/packages/core/dist/index.cjs +1 -1
  39. package/packages/core/dist/index.cjs.map +1 -1
  40. package/packages/core/dist/index.mjs +4 -4
  41. package/packages/core/dist/persisted.cjs +17 -0
  42. package/packages/core/dist/persisted.cjs.map +1 -1
  43. package/packages/core/dist/persisted.mjs +2 -2
  44. package/packages/core/dist/readers.cjs +48 -13
  45. package/packages/core/dist/readers.cjs.map +1 -1
  46. package/packages/core/dist/readers.mjs +2 -2
  47. package/packages/core/dist/writers.cjs +1 -1
  48. package/packages/core/dist/writers.cjs.map +1 -1
  49. package/packages/core/dist/writers.mjs +1 -1
  50. package/packages/cli/dist/chunk-LKEPABND.mjs.map +0 -1
  51. package/packages/core/dist/chunk-2KJR6X4V.mjs.map +0 -1
  52. package/packages/core/dist/chunk-NY4ILMXK.mjs.map +0 -1
  53. package/packages/core/dist/chunk-OSGNGRHF.mjs.map +0 -1
  54. package/packages/core/dist/chunk-Q5SLKV2Y.mjs.map +0 -1
@@ -261,7 +261,7 @@ function stringContainsHighConfidenceCredential(value) {
261
261
  var KEY_VALUE_SECRET_PATTERN, HIGH_CONFIDENCE_CREDENTIAL_PATTERNS;
262
262
  var init_credential_value_patterns = __esm({
263
263
  "packages/core/src/safety/credential-value-patterns.ts"() {
264
- KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i;
264
+ KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?]))([^\s"'\\]{8,})/i;
265
265
  HIGH_CONFIDENCE_CREDENTIAL_PATTERNS = [
266
266
  /^(?:basic|bearer|digest|apikey)\s+[a-z0-9._~+/=-]+$/i,
267
267
  /\bbearer\s+[a-z0-9._~+/=-]{12,}\b/i,
@@ -2690,16 +2690,51 @@ function buildRunTimeline(events, options = {}) {
2690
2690
  const computeDepth = (stepId) => {
2691
2691
  const cached2 = depthCache.get(stepId);
2692
2692
  if (cached2 !== void 0) return cached2;
2693
- const node = steps.get(stepId);
2694
- if (!node) return 0;
2695
- const parent = node.parentId;
2696
- if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
2697
- depthCache.set(stepId, 0);
2698
- return 0;
2693
+ const ancestry = [];
2694
+ const ancestryIndexes = /* @__PURE__ */ new Map();
2695
+ let currentStepId = stepId;
2696
+ let resolvedDepth;
2697
+ while (resolvedDepth === void 0) {
2698
+ const cachedDepth = depthCache.get(currentStepId);
2699
+ if (cachedDepth !== void 0) {
2700
+ resolvedDepth = cachedDepth;
2701
+ continue;
2702
+ }
2703
+ const cycleStart = ancestryIndexes.get(currentStepId);
2704
+ if (cycleStart !== void 0) {
2705
+ const cycleStepIds = ancestry.splice(cycleStart);
2706
+ for (const cycleStepId of cycleStepIds) {
2707
+ depthCache.set(cycleStepId, ROOT_TIMELINE_DEPTH);
2708
+ }
2709
+ resolvedDepth = ROOT_TIMELINE_DEPTH;
2710
+ continue;
2711
+ }
2712
+ const currentStep = steps.get(currentStepId);
2713
+ if (!currentStep) {
2714
+ resolvedDepth = ROOT_TIMELINE_DEPTH;
2715
+ continue;
2716
+ }
2717
+ ancestryIndexes.set(currentStepId, ancestry.length);
2718
+ ancestry.push(currentStepId);
2719
+ const parentId = currentStep.parentId;
2720
+ if (typeof parentId !== "string" || parentId.trim() === "" || !steps.has(parentId)) {
2721
+ ancestry.pop();
2722
+ depthCache.set(currentStepId, ROOT_TIMELINE_DEPTH);
2723
+ resolvedDepth = ROOT_TIMELINE_DEPTH;
2724
+ continue;
2725
+ }
2726
+ currentStepId = parentId;
2727
+ }
2728
+ ancestry.reverse();
2729
+ for (const ancestorStepId of ancestry) {
2730
+ resolvedDepth = Math.min(MAX_TIMELINE_DEPTH, resolvedDepth + 1);
2731
+ depthCache.set(ancestorStepId, resolvedDepth);
2732
+ }
2733
+ const resolvedStepDepth = depthCache.get(stepId);
2734
+ if (resolvedStepDepth === void 0) {
2735
+ throw new Error(`Failed to resolve timeline depth for step "${stepId}"`);
2699
2736
  }
2700
- const d = Math.min(1e3, computeDepth(parent) + 1);
2701
- depthCache.set(stepId, d);
2702
- return d;
2737
+ return resolvedStepDepth;
2703
2738
  };
2704
2739
  const entries = [];
2705
2740
  for (const [stepId, s] of steps.entries()) {
@@ -2787,10 +2822,13 @@ function renderTimeline(timeline, options = {}) {
2787
2822
  }
2788
2823
  return lines.join("\n");
2789
2824
  }
2825
+ var ROOT_TIMELINE_DEPTH, MAX_TIMELINE_DEPTH;
2790
2826
  var init_timeline = __esm({
2791
2827
  "packages/core/src/timeline.ts"() {
2792
2828
  init_stats();
2793
2829
  init_utils();
2830
+ ROOT_TIMELINE_DEPTH = 0;
2831
+ MAX_TIMELINE_DEPTH = 1e3;
2794
2832
  }
2795
2833
  });
2796
2834
 
@@ -5848,9 +5886,6 @@ function limitFindings(findings, maxFindings) {
5848
5886
  if (maxFindings === void 0 || findings.length <= maxFindings) return findings;
5849
5887
  return findings.slice(0, Math.max(0, maxFindings));
5850
5888
  }
5851
- function hasRedactionMarker(value, markers) {
5852
- return markers.some((marker) => value.includes(marker)) || /^\[HASH:[A-Za-z0-9_-]+\]$/.test(value);
5853
- }
5854
5889
  function isFullyRedactedValue(value, markers = ["[REDACTED]", "[REDACTED:"]) {
5855
5890
  if (typeof value !== "string") return false;
5856
5891
  const trimmed = value.trim();
@@ -6837,7 +6872,7 @@ function createSafetyRedactionRule(options = {}) {
6837
6872
  for (const event of context.events) {
6838
6873
  for (const entry of eventValueEntries(event, { includeSummaries: true, includeError: true })) {
6839
6874
  if (!isSensitiveKey(entry.key ?? lastPathSegment(entry.path), sensitiveKeys)) continue;
6840
- if (typeof entry.value === "string" && hasRedactionMarker(entry.value, markers)) continue;
6875
+ if (isFullyRedactedValue(entry.value, markers)) continue;
6841
6876
  findings.push(
6842
6877
  failFinding(
6843
6878
  "safety.redaction",
@@ -7362,7 +7397,7 @@ var init_checks2 = __esm({
7362
7397
  // Keep in sync with packages/redact/src/key-value-secret.ts (KEY_VALUE_SECRET_PATTERN_SOURCE).
7363
7398
  {
7364
7399
  id: "key-value-secret",
7365
- pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i
7400
+ pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?]))([^\s"'\\]{8,})/i
7366
7401
  }
7367
7402
  ];
7368
7403
  }
@@ -12543,6 +12578,23 @@ function persistedInspectEventToInspectEvent(event) {
12543
12578
  if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
12544
12579
  out.durationMs = event.durationMs;
12545
12580
  }
12581
+ if (event.startedAt !== void 0) {
12582
+ const started = parseIsoToMs3(event.startedAt);
12583
+ if (!started.invalidTimestamp) {
12584
+ attrs.startedAtMs = started.ms;
12585
+ out.timestamp = started.ms;
12586
+ }
12587
+ }
12588
+ if (event.endedAt !== void 0) {
12589
+ const ended = parseIsoToMs3(event.endedAt);
12590
+ if (!ended.invalidTimestamp) {
12591
+ attrs.endedAtMs = ended.ms;
12592
+ if (out.durationMs === void 0 && typeof attrs.startedAtMs === "number" && ended.ms >= attrs.startedAtMs) {
12593
+ out.durationMs = ended.ms - attrs.startedAtMs;
12594
+ }
12595
+ }
12596
+ }
12597
+ out.attributes = compactAttributes3(attrs);
12546
12598
  return out;
12547
12599
  }
12548
12600
  function persistedInspectEventsToInspectEvents(events, options) {
@@ -13464,20 +13516,38 @@ function extractOtlpDocument(root) {
13464
13516
  function mapOtlpStatus(status) {
13465
13517
  if (!isRecord11(status)) return void 0;
13466
13518
  const rawCode = status.code;
13467
- if (typeof rawCode !== "string") return void 0;
13468
- switch (rawCode.toUpperCase()) {
13469
- case "STATUS_CODE_OK":
13470
- case "OK":
13471
- return "ok";
13472
- case "STATUS_CODE_ERROR":
13473
- case "ERROR":
13474
- return "error";
13475
- case "STATUS_CODE_UNSET":
13476
- case "UNSET":
13477
- return "unknown";
13478
- default:
13479
- return "unknown";
13519
+ if (typeof rawCode === "number" && Number.isFinite(rawCode)) {
13520
+ switch (rawCode) {
13521
+ case 0:
13522
+ return "unknown";
13523
+ case 1:
13524
+ return "ok";
13525
+ case 2:
13526
+ return "error";
13527
+ default:
13528
+ return "unknown";
13529
+ }
13530
+ }
13531
+ if (typeof rawCode === "string") {
13532
+ const trimmed = rawCode.trim();
13533
+ if (/^\d+$/.test(trimmed)) {
13534
+ return mapOtlpStatus({ code: Number(trimmed) });
13535
+ }
13536
+ switch (trimmed.toUpperCase()) {
13537
+ case "STATUS_CODE_OK":
13538
+ case "OK":
13539
+ return "ok";
13540
+ case "STATUS_CODE_ERROR":
13541
+ case "ERROR":
13542
+ return "error";
13543
+ case "STATUS_CODE_UNSET":
13544
+ case "UNSET":
13545
+ return "unknown";
13546
+ default:
13547
+ return "unknown";
13548
+ }
13480
13549
  }
13550
+ return void 0;
13481
13551
  }
13482
13552
  function readOtlpKind(attributes, pathPrefix) {
13483
13553
  const warnings = [];
@@ -16165,7 +16235,7 @@ var init_src = __esm({
16165
16235
  });
16166
16236
 
16167
16237
  // package.json
16168
- var version = "6.31.5";
16238
+ var version = "6.31.7";
16169
16239
 
16170
16240
  // packages/cli/src/list.ts
16171
16241
  init_advanced();
@@ -16514,7 +16584,7 @@ function mapReaderFormat(format) {
16514
16584
  function isMissingFileError(error) {
16515
16585
  return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
16516
16586
  }
16517
- async function readRunTraceEvents(runId, traceDir) {
16587
+ async function readRunPersistedEvents(runId, traceDir) {
16518
16588
  const filePath = getTraceFilePath(runId, traceDir);
16519
16589
  try {
16520
16590
  await promises.access(filePath);
@@ -16522,16 +16592,24 @@ async function readRunTraceEvents(runId, traceDir) {
16522
16592
  { type: "file", path: filePath },
16523
16593
  { format: "agent-inspect-jsonl" }
16524
16594
  );
16525
- const events = persistedInspectEventsToTraceEvents(result.events);
16526
16595
  return {
16527
- events,
16528
- format: events.length > 0 ? mapReaderFormat(result.format) : "empty"
16596
+ events: result.events,
16597
+ format: result.events.length > 0 ? mapReaderFormat(result.format) : "empty"
16529
16598
  };
16530
16599
  } catch (error) {
16531
16600
  if (isMissingFileError(error)) return void 0;
16532
16601
  throw error;
16533
16602
  }
16534
16603
  }
16604
+ async function readRunTraceEvents(runId, traceDir) {
16605
+ const persisted = await readRunPersistedEvents(runId, traceDir);
16606
+ if (persisted === void 0) return void 0;
16607
+ const events = persistedInspectEventsToTraceEvents(persisted.events);
16608
+ return {
16609
+ events,
16610
+ format: events.length > 0 ? persisted.format : "empty"
16611
+ };
16612
+ }
16535
16613
 
16536
16614
  // packages/cli/src/view.ts
16537
16615
  function isModuleNotFound(e) {
@@ -17975,6 +18053,8 @@ var CONNECTION_PROTOCOLS = /* @__PURE__ */ new Set([
17975
18053
  "amqps:"
17976
18054
  ]);
17977
18055
  var CONNECTION_URI = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
18056
+ var CONNECTION_USERINFO = /^(postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/([^@/?#]+)@(.+)$/i;
18057
+ var CONNECTION_HAS_USERINFO = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^@/?#]+@/i;
17978
18058
  var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17979
18059
  var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
17980
18060
  var NUMERIC_SEGMENT = /^\d{5,}$/;
@@ -17998,6 +18078,28 @@ function parseRedactableUrl(value) {
17998
18078
  return void 0;
17999
18079
  }
18000
18080
  }
18081
+ function redactConnectionUserinfoFallback(value, replacement) {
18082
+ const trimmed = value.trim();
18083
+ const match = CONNECTION_USERINFO.exec(trimmed);
18084
+ if (match) {
18085
+ const [, scheme, , rest] = match;
18086
+ if (scheme && rest && rest.length > 0) {
18087
+ return {
18088
+ value: `${scheme}://${rest}`,
18089
+ credentialRedacted: true,
18090
+ identifiersMasked: false
18091
+ };
18092
+ }
18093
+ }
18094
+ if (looksLikeConnectionUri(trimmed) && CONNECTION_HAS_USERINFO.test(trimmed)) {
18095
+ return {
18096
+ value: replacement,
18097
+ credentialRedacted: true,
18098
+ identifiersMasked: false
18099
+ };
18100
+ }
18101
+ return void 0;
18102
+ }
18001
18103
  function isHttpProtocol(protocol) {
18002
18104
  return protocol === "http:" || protocol === "https:";
18003
18105
  }
@@ -18039,9 +18141,11 @@ function redactUrlString(value, options) {
18039
18141
  identifiersMasked: false
18040
18142
  };
18041
18143
  if (!looksLikeUrlForRedaction(value)) return unchanged;
18042
- const url = parseRedactableUrl(value);
18043
- if (!url) return unchanged;
18044
18144
  const replacement = options.replacement ?? "[REDACTED]";
18145
+ const url = parseRedactableUrl(value);
18146
+ if (!url) {
18147
+ return redactConnectionUserinfoFallback(value, replacement) ?? unchanged;
18148
+ }
18045
18149
  const idPlaceholder = options.idPlaceholder ?? "[id]";
18046
18150
  const http = isHttpProtocol(url.protocol);
18047
18151
  let credentialRedacted = url.username.length > 0 || url.password.length > 0;
@@ -18737,6 +18841,10 @@ function exportOpenInference(tree, options) {
18737
18841
 
18738
18842
  // packages/core/src/exporters/otlp-json-exporter.ts
18739
18843
  init_helpers();
18844
+ var OTLP_STATUS_CODE_UNSET = 0;
18845
+ var OTLP_STATUS_CODE_OK = 1;
18846
+ var OTLP_STATUS_CODE_ERROR = 2;
18847
+ var OTLP_SPAN_KIND_INTERNAL = 1;
18740
18848
  function hexFrom2(seed, byteLen) {
18741
18849
  return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
18742
18850
  }
@@ -18749,7 +18857,19 @@ function stringAttr2(key, value) {
18749
18857
  return { key, value: { stringValue: value } };
18750
18858
  }
18751
18859
  function intAttr(key, value) {
18752
- return { key, value: { intValue: String(value) } };
18860
+ return { key, value: { intValue: String(Math.trunc(value)) } };
18861
+ }
18862
+ function doubleAttr(key, value) {
18863
+ return { key, value: { doubleValue: value } };
18864
+ }
18865
+ function boolAttr(key, value) {
18866
+ return { key, value: { boolValue: value } };
18867
+ }
18868
+ function numberAttr(key, value) {
18869
+ if (Number.isFinite(value) && !Number.isInteger(value)) {
18870
+ return doubleAttr(key, value);
18871
+ }
18872
+ return intAttr(key, value);
18753
18873
  }
18754
18874
  function genAiOperationName(kind) {
18755
18875
  switch (kind) {
@@ -18763,10 +18883,27 @@ function genAiOperationName(kind) {
18763
18883
  return void 0;
18764
18884
  }
18765
18885
  }
18886
+ function resolveSpanTimes(ev) {
18887
+ const startedAtMs = typeof ev.attributes?.startedAtMs === "number" && Number.isFinite(ev.attributes.startedAtMs) ? ev.attributes.startedAtMs : void 0;
18888
+ const endedAtMs = typeof ev.attributes?.endedAtMs === "number" && Number.isFinite(ev.attributes.endedAtMs) ? ev.attributes.endedAtMs : void 0;
18889
+ const startMs = startedAtMs ?? ev.timestamp;
18890
+ const startNs = String(unixNano2(startMs));
18891
+ if (endedAtMs !== void 0) {
18892
+ return { startNs, endNs: String(unixNano2(endedAtMs)) };
18893
+ }
18894
+ if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
18895
+ return {
18896
+ startNs,
18897
+ endNs: String(unixNano2(startMs) + unixNano2(ev.durationMs))
18898
+ };
18899
+ }
18900
+ return { startNs };
18901
+ }
18766
18902
  function exportOtlpJson(tree, options) {
18767
18903
  const warnings = [
18768
18904
  "OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
18769
- "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload."
18905
+ "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload.",
18906
+ "Status and span kind use OTLP numeric enums (StatusCode 0/1/2, SpanKind INTERNAL=1)."
18770
18907
  ];
18771
18908
  const traceId = hexFrom2(`trace:${tree.runId}`, 16);
18772
18909
  const includeAttributes = options?.includeAttributes ?? false;
@@ -18778,11 +18915,7 @@ function exportOtlpJson(tree, options) {
18778
18915
  const ev = n.event;
18779
18916
  const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
18780
18917
  const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
18781
- const startNs = String(unixNano2(ev.timestamp));
18782
- let endNs;
18783
- if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
18784
- endNs = String(unixNano2(ev.timestamp) + unixNano2(ev.durationMs));
18785
- }
18918
+ const { startNs, endNs } = resolveSpanTimes(ev);
18786
18919
  const attrs = [
18787
18920
  stringAttr2("agent_inspect.kind", ev.kind),
18788
18921
  stringAttr2("agent_inspect.confidence", ev.confidence),
@@ -18812,29 +18945,28 @@ function exportOtlpJson(tree, options) {
18812
18945
  if (includeAttributes && meta2 && typeof meta2 === "object") {
18813
18946
  for (const [k, v] of Object.entries(meta2)) {
18814
18947
  if (k === "tokens" || k === "model") continue;
18815
- if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
18816
- attrs.push(
18817
- stringAttr2(
18818
- `agent_inspect.preview.${k}`,
18819
- typeof v === "string" ? v.slice(0, maxLen) : String(v)
18820
- )
18821
- );
18948
+ if (typeof v === "string") {
18949
+ attrs.push(stringAttr2(`agent_inspect.preview.${k}`, v.slice(0, maxLen)));
18950
+ } else if (typeof v === "number" && Number.isFinite(v)) {
18951
+ attrs.push(numberAttr(`agent_inspect.preview.${k}`, v));
18952
+ } else if (typeof v === "boolean") {
18953
+ attrs.push(boolAttr(`agent_inspect.preview.${k}`, v));
18822
18954
  }
18823
18955
  }
18824
18956
  }
18825
- let statusCode = "STATUS_CODE_UNSET";
18957
+ let statusCode = OTLP_STATUS_CODE_UNSET;
18826
18958
  let statusMessage;
18827
18959
  if (ev.status === "error") {
18828
- statusCode = "STATUS_CODE_ERROR";
18960
+ statusCode = OTLP_STATUS_CODE_ERROR;
18829
18961
  statusMessage = meta2 && typeof meta2.error === "object" && meta2.error !== null ? String(meta2.error.message ?? "error").slice(0, maxLen) : "error";
18830
18962
  } else if (ev.status === "ok") {
18831
- statusCode = "STATUS_CODE_OK";
18963
+ statusCode = OTLP_STATUS_CODE_OK;
18832
18964
  }
18833
18965
  const spanJson = {
18834
18966
  traceId,
18835
18967
  spanId,
18836
18968
  name: ev.name,
18837
- kind: "SPAN_KIND_INTERNAL",
18969
+ kind: OTLP_SPAN_KIND_INTERNAL,
18838
18970
  startTimeUnixNano: startNs,
18839
18971
  attributes: attrs,
18840
18972
  status: {
@@ -18876,6 +19008,136 @@ function exportOtlpJson(tree, options) {
18876
19008
 
18877
19009
  // packages/core/src/exporters/validation.ts
18878
19010
  var EXPERIMENTAL = "Experimental compatibility export \u2014 verify against your target tooling before relying on it.";
19011
+ function pushPath(errors, path44, message) {
19012
+ errors.push(`${path44}: ${message}`);
19013
+ }
19014
+ function isRecord16(value) {
19015
+ return value !== null && typeof value === "object" && !Array.isArray(value);
19016
+ }
19017
+ function validateOtlpJson(content) {
19018
+ const errors = [];
19019
+ const warnings = [
19020
+ EXPERIMENTAL,
19021
+ "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation.",
19022
+ "protocol-valid \u2260 profile-complete \u2260 fixture-complete \u2014 nested path diagnostics report protocol shape only."
19023
+ ];
19024
+ let parsed;
19025
+ try {
19026
+ parsed = JSON.parse(content);
19027
+ } catch {
19028
+ pushPath(errors, "$", "OTLP JSON export is not valid JSON");
19029
+ return { ok: false, format: "otlp-json", errors, warnings };
19030
+ }
19031
+ if (!isRecord16(parsed)) {
19032
+ pushPath(errors, "$", "OTLP JSON export must be an object");
19033
+ return { ok: false, format: "otlp-json", errors, warnings };
19034
+ }
19035
+ if (!Array.isArray(parsed.resourceSpans)) {
19036
+ pushPath(errors, "$.resourceSpans", "must be an array");
19037
+ return { ok: false, format: "otlp-json", errors, warnings };
19038
+ }
19039
+ parsed.resourceSpans.forEach((resourceSpan, rsi) => {
19040
+ const rsPath = `$.resourceSpans[${rsi}]`;
19041
+ if (!isRecord16(resourceSpan)) {
19042
+ pushPath(errors, rsPath, "must be an object");
19043
+ return;
19044
+ }
19045
+ if (!Array.isArray(resourceSpan.scopeSpans)) {
19046
+ pushPath(errors, `${rsPath}.scopeSpans`, "must be an array");
19047
+ return;
19048
+ }
19049
+ resourceSpan.scopeSpans.forEach((scopeSpan, ssi) => {
19050
+ const ssPath = `${rsPath}.scopeSpans[${ssi}]`;
19051
+ if (!isRecord16(scopeSpan)) {
19052
+ pushPath(errors, ssPath, "must be an object");
19053
+ return;
19054
+ }
19055
+ if (!Array.isArray(scopeSpan.spans)) {
19056
+ pushPath(errors, `${ssPath}.spans`, "must be an array");
19057
+ return;
19058
+ }
19059
+ scopeSpan.spans.forEach((span, spi) => {
19060
+ const spanPath = `${ssPath}.spans[${spi}]`;
19061
+ if (!isRecord16(span)) {
19062
+ pushPath(errors, spanPath, "must be an object");
19063
+ return;
19064
+ }
19065
+ if (typeof span.traceId !== "string" || span.traceId.length === 0) {
19066
+ pushPath(errors, `${spanPath}.traceId`, "must be a non-empty string");
19067
+ }
19068
+ if (typeof span.spanId !== "string" || span.spanId.length === 0) {
19069
+ pushPath(errors, `${spanPath}.spanId`, "must be a non-empty string");
19070
+ }
19071
+ if (typeof span.name !== "string") {
19072
+ pushPath(errors, `${spanPath}.name`, "must be a string");
19073
+ }
19074
+ if (typeof span.startTimeUnixNano !== "string" || !/^\d+$/.test(span.startTimeUnixNano)) {
19075
+ pushPath(
19076
+ errors,
19077
+ `${spanPath}.startTimeUnixNano`,
19078
+ "must be a decimal-string 64-bit integer"
19079
+ );
19080
+ }
19081
+ if (span.endTimeUnixNano !== void 0 && (typeof span.endTimeUnixNano !== "string" || !/^\d+$/.test(span.endTimeUnixNano))) {
19082
+ pushPath(
19083
+ errors,
19084
+ `${spanPath}.endTimeUnixNano`,
19085
+ "must be a decimal-string 64-bit integer when present"
19086
+ );
19087
+ }
19088
+ if (typeof span.kind === "number") {
19089
+ if (!Number.isInteger(span.kind)) {
19090
+ pushPath(errors, `${spanPath}.kind`, "must be an integer SpanKind enum");
19091
+ }
19092
+ } else if (typeof span.kind === "string") {
19093
+ warnings.push(
19094
+ `${spanPath}.kind: string SpanKind enum is legacy; prefer numeric (INTERNAL=1)`
19095
+ );
19096
+ } else if (span.kind !== void 0) {
19097
+ pushPath(
19098
+ errors,
19099
+ `${spanPath}.kind`,
19100
+ "must be numeric SpanKind or historical string enum"
19101
+ );
19102
+ }
19103
+ if (!isRecord16(span.status)) {
19104
+ pushPath(errors, `${spanPath}.status`, "must be an object");
19105
+ } else {
19106
+ const code = span.status.code;
19107
+ if (typeof code === "number") {
19108
+ if (![0, 1, 2].includes(code)) {
19109
+ pushPath(
19110
+ errors,
19111
+ `${spanPath}.status.code`,
19112
+ "must be numeric StatusCode 0 (UNSET), 1 (OK), or 2 (ERROR)"
19113
+ );
19114
+ }
19115
+ } else if (typeof code === "string") {
19116
+ const upper = code.toUpperCase();
19117
+ if (upper !== "STATUS_CODE_UNSET" && upper !== "STATUS_CODE_OK" && upper !== "STATUS_CODE_ERROR" && upper !== "UNSET" && upper !== "OK" && upper !== "ERROR" && !/^[012]$/.test(code.trim())) {
19118
+ pushPath(
19119
+ errors,
19120
+ `${spanPath}.status.code`,
19121
+ "unrecognized StatusCode string"
19122
+ );
19123
+ } else {
19124
+ warnings.push(
19125
+ `${spanPath}.status.code: string StatusCode is legacy; prefer numeric 0/1/2`
19126
+ );
19127
+ }
19128
+ } else {
19129
+ pushPath(
19130
+ errors,
19131
+ `${spanPath}.status.code`,
19132
+ "must be numeric StatusCode 0/1/2 or historical string enum"
19133
+ );
19134
+ }
19135
+ }
19136
+ });
19137
+ });
19138
+ });
19139
+ return { ok: errors.length === 0, format: "otlp-json", errors, warnings };
19140
+ }
18879
19141
  function validateExportContent(format, content) {
18880
19142
  const errors = [];
18881
19143
  const warnings = [EXPERIMENTAL];
@@ -18921,25 +19183,7 @@ function validateExportContent(format, content) {
18921
19183
  return { ok: errors.length === 0, format, errors, warnings };
18922
19184
  }
18923
19185
  if (format === "otlp-json") {
18924
- let parsed;
18925
- try {
18926
- parsed = JSON.parse(content);
18927
- } catch {
18928
- errors.push("OTLP JSON export is not valid JSON");
18929
- return { ok: false, format, errors, warnings };
18930
- }
18931
- if (!parsed || typeof parsed !== "object") {
18932
- errors.push("OTLP JSON export must be an object");
18933
- return { ok: false, format, errors, warnings };
18934
- }
18935
- const o = parsed;
18936
- if (!Array.isArray(o.resourceSpans)) {
18937
- errors.push("OTLP JSON export must include resourceSpans array");
18938
- }
18939
- warnings.push(
18940
- "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation."
18941
- );
18942
- return { ok: errors.length === 0, format, errors, warnings };
19186
+ return validateOtlpJson(content);
18943
19187
  }
18944
19188
  errors.push(`Unsupported export format`);
18945
19189
  return { ok: false, format, errors, warnings };
@@ -19343,7 +19587,7 @@ async function exportCommand(runId, options = {}) {
19343
19587
  const traceDir = resolveTraceDir({ dir: options.dir });
19344
19588
  let events;
19345
19589
  try {
19346
- const result2 = await readRunTraceEvents(id, traceDir);
19590
+ const result2 = await readRunPersistedEvents(id, traceDir);
19347
19591
  events = result2?.events ?? [];
19348
19592
  } catch (e) {
19349
19593
  const msg = e instanceof Error ? e.message : String(e);
@@ -19359,7 +19603,11 @@ Trace directory: ${traceDir}`);
19359
19603
  }
19360
19604
  let tree;
19361
19605
  try {
19362
- tree = manualTraceEventsToRunTree(events);
19606
+ const trees = persistedInspectEventsToRunTrees(events);
19607
+ tree = trees.find((candidate) => candidate.runId === id) ?? trees[0];
19608
+ if (tree === void 0) {
19609
+ throw new Error("No run tree could be built from persisted events");
19610
+ }
19363
19611
  } catch (e) {
19364
19612
  const msg = e instanceof Error ? e.message : String(e);
19365
19613
  console.error(`[AgentInspect] export failed: ${msg}`);
@@ -19387,7 +19635,9 @@ Trace directory: ${traceDir}`);
19387
19635
  await promises.mkdir(path33__default.default.dirname(outPath), { recursive: true });
19388
19636
  await promises.writeFile(outPath, result.content, "utf-8");
19389
19637
  const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
19390
- console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
19638
+ console.error(
19639
+ `Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`
19640
+ );
19391
19641
  if (validation !== void 0 && !validation.ok) {
19392
19642
  console.error("Validation errors:", validation.errors.join("; "));
19393
19643
  }
@@ -19400,7 +19650,9 @@ Trace directory: ${traceDir}`);
19400
19650
  warnings: [...result.warnings, ...validation?.warnings ?? []],
19401
19651
  validation
19402
19652
  };
19403
- if (outPath === void 0) {
19653
+ if (outPath !== void 0) {
19654
+ payload.out = outPath;
19655
+ } else {
19404
19656
  payload.content = result.content;
19405
19657
  }
19406
19658
  console.log(JSON.stringify(payload, null, 2));
@@ -20302,7 +20554,7 @@ function isCredentialSensitiveKey2(key, sensitiveKeys) {
20302
20554
  }
20303
20555
 
20304
20556
  // packages/redact/src/key-value-secret.ts
20305
- var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})`;
20557
+ var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?]))([^\s"'\\]{8,})`;
20306
20558
  var KEY_VALUE_SECRET_PATTERN2 = new RegExp(
20307
20559
  KEY_VALUE_SECRET_PATTERN_SOURCE,
20308
20560
  "i"
@@ -20327,6 +20579,8 @@ var CONNECTION_PROTOCOLS2 = /* @__PURE__ */ new Set([
20327
20579
  "amqps:"
20328
20580
  ]);
20329
20581
  var CONNECTION_URI2 = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
20582
+ var CONNECTION_USERINFO2 = /^(postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/([^@/?#]+)@(.+)$/i;
20583
+ var CONNECTION_HAS_USERINFO2 = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^@/?#]+@/i;
20330
20584
  var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20331
20585
  var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
20332
20586
  var NUMERIC_SEGMENT2 = /^\d{5,}$/;
@@ -20350,6 +20604,28 @@ function parseRedactableUrl2(value) {
20350
20604
  return void 0;
20351
20605
  }
20352
20606
  }
20607
+ function redactConnectionUserinfoFallback2(value, replacement) {
20608
+ const trimmed = value.trim();
20609
+ const match = CONNECTION_USERINFO2.exec(trimmed);
20610
+ if (match) {
20611
+ const [, scheme, , rest] = match;
20612
+ if (scheme && rest && rest.length > 0) {
20613
+ return {
20614
+ value: `${scheme}://${rest}`,
20615
+ credentialRedacted: true,
20616
+ identifiersMasked: false
20617
+ };
20618
+ }
20619
+ }
20620
+ if (looksLikeConnectionUri2(trimmed) && CONNECTION_HAS_USERINFO2.test(trimmed)) {
20621
+ return {
20622
+ value: replacement,
20623
+ credentialRedacted: true,
20624
+ identifiersMasked: false
20625
+ };
20626
+ }
20627
+ return void 0;
20628
+ }
20353
20629
  function isHttpProtocol2(protocol) {
20354
20630
  return protocol === "http:" || protocol === "https:";
20355
20631
  }
@@ -20391,9 +20667,11 @@ function redactUrlString2(value, options) {
20391
20667
  identifiersMasked: false
20392
20668
  };
20393
20669
  if (!looksLikeUrlForRedaction2(value)) return unchanged;
20394
- const url = parseRedactableUrl2(value);
20395
- if (!url) return unchanged;
20396
20670
  const replacement = options.replacement ?? "[REDACTED]";
20671
+ const url = parseRedactableUrl2(value);
20672
+ if (!url) {
20673
+ return redactConnectionUserinfoFallback2(value, replacement) ?? unchanged;
20674
+ }
20397
20675
  const idPlaceholder = options.idPlaceholder ?? "[id]";
20398
20676
  const http = isHttpProtocol2(url.protocol);
20399
20677
  let credentialRedacted = url.username.length > 0 || url.password.length > 0;
@@ -20486,7 +20764,7 @@ var STRICT_PROFILE_EXTRA_KEYS2 = [
20486
20764
  "retrieval",
20487
20765
  "query"
20488
20766
  ];
20489
- function isRecord16(value) {
20767
+ function isRecord17(value) {
20490
20768
  return typeof value === "object" && value !== null && !Array.isArray(value);
20491
20769
  }
20492
20770
  function toKey2(key) {
@@ -20915,7 +21193,7 @@ var Redactor2 = class {
20915
21193
  });
20916
21194
  return out;
20917
21195
  }
20918
- if (isRecord16(value)) {
21196
+ if (isRecord17(value)) {
20919
21197
  if (state.seen.has(value)) return state.seen.get(value);
20920
21198
  const out = {};
20921
21199
  state.seen.set(value, out);
@@ -20950,7 +21228,7 @@ var REDACTION_POLICY_LIMITS = {
20950
21228
  };
20951
21229
  var ALLOWED_TOP_LEVEL = /* @__PURE__ */ new Set(["version", "extraKeys", "patterns"]);
20952
21230
  var ALLOWED_PATTERN_FIELDS = /* @__PURE__ */ new Set(["id", "type", "value", "severity"]);
20953
- function isRecord17(value) {
21231
+ function isRecord18(value) {
20954
21232
  return value !== null && typeof value === "object" && !Array.isArray(value);
20955
21233
  }
20956
21234
  function rejectRemotePolicyPath(policyPath) {
@@ -20990,7 +21268,7 @@ function rejectUnknownFields(record, allowed, label) {
20990
21268
  }
20991
21269
  function compilePatternDetector(input3, index) {
20992
21270
  const label = `patterns[${index}]`;
20993
- if (!isRecord17(input3)) throw new Error(`${label} must be an object.`);
21271
+ if (!isRecord18(input3)) throw new Error(`${label} must be an object.`);
20994
21272
  const typeEarly = input3.type;
20995
21273
  if (typeEarly === "typed") {
20996
21274
  throw new Error(
@@ -21045,7 +21323,7 @@ function compilePatternDetector(input3, index) {
21045
21323
  };
21046
21324
  }
21047
21325
  function compileRedactionPolicy(raw, policyPath) {
21048
- if (!isRecord17(raw)) {
21326
+ if (!isRecord18(raw)) {
21049
21327
  throw new Error(`Redaction policy must be a JSON object (${policyPath}).`);
21050
21328
  }
21051
21329
  rejectUnknownFields(raw, ALLOWED_TOP_LEVEL, "Redaction policy");
@@ -22081,7 +22359,7 @@ async function openCommand(input3, options = {}, stdin = process.stdin) {
22081
22359
 
22082
22360
  // packages/cli/src/migrate.ts
22083
22361
  init_advanced();
22084
- function isRecord18(value) {
22362
+ function isRecord19(value) {
22085
22363
  return typeof value === "object" && value !== null && !Array.isArray(value);
22086
22364
  }
22087
22365
  function parseTarget(value) {
@@ -22090,7 +22368,7 @@ function parseTarget(value) {
22090
22368
  throw new Error('Unsupported migration target. Use "--to 1.0".');
22091
22369
  }
22092
22370
  function formatOf(value) {
22093
- if (!isRecord18(value)) return "unknown";
22371
+ if (!isRecord19(value)) return "unknown";
22094
22372
  if (value.schemaVersion === "0.1") return "0.1";
22095
22373
  if (value.schemaVersion === "0.2") return "0.2";
22096
22374
  if (value.schemaVersion === "1.0") return "1.0";
@@ -28614,16 +28892,120 @@ function demoTemplate(framework) {
28614
28892
  switch (framework) {
28615
28893
  case "ai-sdk":
28616
28894
  return `/**
28617
- * AI SDK starter \u2014 metadata-only telemetry (no real model calls in this demo).
28618
- * Install: npm install agent-inspect @agent-inspect/ai-sdk ai
28895
+ * AI SDK starter \u2014 keyless MockLanguageModelV3 + @agent-inspect/ai-sdk telemetry.
28896
+ * Install: npm install agent-inspect @agent-inspect/ai-sdk ai@6.0.210 zod
28619
28897
  */
28620
- import { inspectRun, step } from "agent-inspect";
28898
+ import { generateText, stepCountIs, tool } from "ai";
28899
+ import { MockLanguageModelV3 } from "ai/test";
28900
+ import { agentInspect } from "@agent-inspect/ai-sdk";
28901
+ import { z } from "zod";
28902
+ import { readdirSync, readFileSync } from "node:fs";
28903
+ import path from "node:path";
28904
+
28905
+ const TRACE_DIR = ".agent-inspect";
28906
+ const ANSWER = "You have 2 orders";
28907
+ const CORRECT_RUN = "ai-sdk-demo-correct";
28908
+ const WRONG_RUN = "ai-sdk-demo-wrong";
28909
+
28910
+ const usage = {
28911
+ inputTokens: { total: 4, noCache: 4, cacheRead: 0, cacheWrite: 0 },
28912
+ outputTokens: { total: 3, text: 3, reasoning: 0 },
28913
+ };
28914
+
28915
+ const lookupOrders = tool({
28916
+ description: "Look up orders for a user",
28917
+ inputSchema: z.object({ userId: z.string() }),
28918
+ execute: async () => ({ orders: 2 }),
28919
+ });
28920
+
28921
+ const deleteOrders = tool({
28922
+ description: "Delete orders for a user (wrong path)",
28923
+ inputSchema: z.object({ userId: z.string() }),
28924
+ execute: async () => ({ orders: 2 }),
28925
+ });
28926
+
28927
+ async function runPath(runName, toolName) {
28928
+ const integration = agentInspect({
28929
+ traceDir: TRACE_DIR,
28930
+ runName,
28931
+ capture: "metadata-only",
28932
+ });
28933
+ let step = 0;
28934
+ try {
28935
+ const result = await generateText({
28936
+ model: new MockLanguageModelV3({
28937
+ provider: "fixture-provider",
28938
+ modelId: "fixture-tools",
28939
+ doGenerate: async () => {
28940
+ step += 1;
28941
+ if (step === 1) {
28942
+ return {
28943
+ content: [
28944
+ {
28945
+ type: "tool-call",
28946
+ toolCallId: \`call-\${toolName}\`,
28947
+ toolName,
28948
+ input: { userId: "u1" },
28949
+ },
28950
+ ],
28951
+ finishReason: { unified: "tool-calls", raw: "tool-calls" },
28952
+ usage,
28953
+ warnings: [],
28954
+ };
28955
+ }
28956
+ return {
28957
+ content: [{ type: "text", text: ANSWER }],
28958
+ finishReason: { unified: "stop", raw: "stop" },
28959
+ usage,
28960
+ warnings: [],
28961
+ };
28962
+ },
28963
+ }),
28964
+ tools: { lookup_orders: lookupOrders, delete_orders: deleteOrders },
28965
+ prompt: "How many orders do I have?",
28966
+ stopWhen: stepCountIs(5),
28967
+ experimental_telemetry: {
28968
+ isEnabled: true,
28969
+ recordInputs: false,
28970
+ recordOutputs: false,
28971
+ integrations: [integration],
28972
+ },
28973
+ });
28974
+ if (result.text !== ANSWER) {
28975
+ throw new Error(\`unexpected answer for \${runName}: \${result.text}\`);
28976
+ }
28977
+ } finally {
28978
+ await integration.flush();
28979
+ await integration.close();
28980
+ }
28981
+ }
28982
+
28983
+ function selectRunIdByName(runName) {
28984
+ const files = readdirSync(TRACE_DIR).filter((f) => f.endsWith(".jsonl"));
28985
+ const matches = [];
28986
+ for (const file of files) {
28987
+ const text = readFileSync(path.join(TRACE_DIR, file), "utf8");
28988
+ if (text.includes(\`"name":"\${runName}"\`) || text.includes(\`"name": "\${runName}"\`)) {
28989
+ matches.push(file.replace(/\\.jsonl$/, ""));
28990
+ }
28991
+ }
28992
+ if (matches.length !== 1) {
28993
+ throw new Error(
28994
+ \`expected exactly one run named \${runName}, found \${matches.length}\`,
28995
+ );
28996
+ }
28997
+ return matches[0];
28998
+ }
28621
28999
 
28622
29000
  async function main() {
28623
- await inspectRun("ai-sdk-demo", async () => {
28624
- await step.tool("mock-generate", async () => ({ text: "ok" }));
28625
- }, { traceDir: ".agent-inspect", silent: true });
29001
+ await runPath(CORRECT_RUN, "lookup_orders");
29002
+ await runPath(WRONG_RUN, "delete_orders");
29003
+ const correctId = selectRunIdByName(CORRECT_RUN);
29004
+ const wrongId = selectRunIdByName(WRONG_RUN);
28626
29005
  console.log("Trace written to .agent-inspect/");
29006
+ console.log(\`Correct path runId: \${correctId}\`);
29007
+ console.log(\`Wrong path runId: \${wrongId}\`);
29008
+ console.log(\`Next: npx agent-inspect check \${correctId} --required-tool lookup_orders\`);
28627
29009
  }
28628
29010
 
28629
29011
  main().catch((error) => {