agent-inspect 6.31.6 → 6.31.8

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 (46) 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/package.json +1 -1
  5. package/packages/cli/dist/{chunk-YXDNWHNJ.mjs → chunk-6FE47XSF.mjs} +119 -34
  6. package/packages/cli/dist/chunk-6FE47XSF.mjs.map +1 -0
  7. package/packages/cli/dist/index.cjs +569 -102
  8. package/packages/cli/dist/index.cjs.map +1 -1
  9. package/packages/cli/dist/index.mjs +454 -74
  10. package/packages/cli/dist/index.mjs.map +1 -1
  11. package/packages/cli/dist/{src-H22KNWAZ.mjs → src-NXAYHG34.mjs} +3 -3
  12. package/packages/cli/dist/{src-H22KNWAZ.mjs.map → src-NXAYHG34.mjs.map} +1 -1
  13. package/packages/core/dist/advanced.cjs +116 -31
  14. package/packages/core/dist/advanced.cjs.map +1 -1
  15. package/packages/core/dist/advanced.mjs +7 -7
  16. package/packages/core/dist/checks.mjs +2 -2
  17. package/packages/core/dist/{chunk-LXYPM2ZO.mjs → chunk-2CCRUI42.mjs} +19 -318
  18. package/packages/core/dist/chunk-2CCRUI42.mjs.map +1 -0
  19. package/packages/core/dist/{chunk-Q5SLKV2Y.mjs → chunk-2IRRUNIN.mjs} +28 -5
  20. package/packages/core/dist/chunk-2IRRUNIN.mjs.map +1 -0
  21. package/packages/core/dist/{chunk-EXGQLMVB.mjs → chunk-7YYS742B.mjs} +49 -12
  22. package/packages/core/dist/chunk-7YYS742B.mjs.map +1 -0
  23. package/packages/core/dist/{chunk-OSGNGRHF.mjs → chunk-IJJSPJXV.mjs} +51 -25
  24. package/packages/core/dist/chunk-IJJSPJXV.mjs.map +1 -0
  25. package/packages/core/dist/chunk-L5WLVBW4.mjs +355 -0
  26. package/packages/core/dist/chunk-L5WLVBW4.mjs.map +1 -0
  27. package/packages/core/dist/exporters.cjs +327 -58
  28. package/packages/core/dist/exporters.cjs.map +1 -1
  29. package/packages/core/dist/exporters.mjs +281 -49
  30. package/packages/core/dist/exporters.mjs.map +1 -1
  31. package/packages/core/dist/persisted.cjs +361 -9
  32. package/packages/core/dist/persisted.cjs.map +1 -1
  33. package/packages/core/dist/persisted.d.cts +8 -1
  34. package/packages/core/dist/persisted.d.ts +8 -1
  35. package/packages/core/dist/persisted.mjs +3 -2
  36. package/packages/core/dist/persisted.mjs.map +1 -1
  37. package/packages/core/dist/readers.cjs +425 -100
  38. package/packages/core/dist/readers.cjs.map +1 -1
  39. package/packages/core/dist/readers.mjs +3 -3
  40. package/packages/cli/dist/chunk-YXDNWHNJ.mjs.map +0 -1
  41. package/packages/core/dist/chunk-EXGQLMVB.mjs.map +0 -1
  42. package/packages/core/dist/chunk-FWK3Q3OJ.mjs +0 -56
  43. package/packages/core/dist/chunk-FWK3Q3OJ.mjs.map +0 -1
  44. package/packages/core/dist/chunk-LXYPM2ZO.mjs.map +0 -1
  45. package/packages/core/dist/chunk-OSGNGRHF.mjs.map +0 -1
  46. package/packages/core/dist/chunk-Q5SLKV2Y.mjs.map +0 -1
@@ -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;
2699
2727
  }
2700
- const d = Math.min(1e3, computeDepth(parent) + 1);
2701
- depthCache.set(stepId, d);
2702
- return d;
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}"`);
2736
+ }
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
 
@@ -12540,6 +12578,23 @@ function persistedInspectEventToInspectEvent(event) {
12540
12578
  if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
12541
12579
  out.durationMs = event.durationMs;
12542
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);
12543
12598
  return out;
12544
12599
  }
12545
12600
  function persistedInspectEventsToInspectEvents(events, options) {
@@ -12563,14 +12618,20 @@ var init_to_inspect_event = __esm({
12563
12618
  });
12564
12619
 
12565
12620
  // packages/core/src/persisted/tree-bridge.ts
12621
+ function logicalToPersisted(event) {
12622
+ const { sourceEventIds: _sourceEventIds, projection: _projection, ...rest } = event;
12623
+ return rest;
12624
+ }
12566
12625
  function persistedInspectEventsToRunTrees(events, options) {
12567
- const inspectEvents = persistedInspectEventsToInspectEvents(events, {
12626
+ const sourceEvents = options?.coalesceLifecycle === true ? projectLogicalEvents(events).logicalEvents.map(logicalToPersisted) : events;
12627
+ const inspectEvents = persistedInspectEventsToInspectEvents(sourceEvents, {
12568
12628
  skipInvalid: options?.skipInvalid
12569
12629
  });
12570
12630
  return new TreeBuilder().build(inspectEvents);
12571
12631
  }
12572
12632
  var init_tree_bridge = __esm({
12573
12633
  "packages/core/src/persisted/tree-bridge.ts"() {
12634
+ init_logical_events();
12574
12635
  init_tree_builder();
12575
12636
  init_from_trace_event();
12576
12637
  init_to_inspect_event();
@@ -13191,7 +13252,7 @@ function mapOpenInferenceSpan(span, index, version2) {
13191
13252
  const errorMessage = isRecord11(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
13192
13253
  const event = {
13193
13254
  schemaVersion: "0.2",
13194
- eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
13255
+ eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : `${traceId}:${spanId}`,
13195
13256
  runId: typeof rawAttributes["agent_inspect.run_id"] === "string" ? rawAttributes["agent_inspect.run_id"] : traceId,
13196
13257
  kind,
13197
13258
  name,
@@ -13242,12 +13303,16 @@ function mapOpenInferenceEvents(document) {
13242
13303
  const mapped = document.spans.map(
13243
13304
  (span, index) => mapOpenInferenceSpan(span, index, document.version)
13244
13305
  );
13245
- const spanIdToEventId = new Map(
13246
- mapped.map((span) => [span.spanId, span.event.eventId])
13306
+ const spanKeyToEventId = new Map(
13307
+ mapped.map((span) => {
13308
+ const traceId = typeof span.event.trace?.traceId === "string" ? span.event.trace.traceId : "";
13309
+ return [`${traceId}:${span.spanId}`, span.event.eventId];
13310
+ })
13247
13311
  );
13248
13312
  for (const span of mapped) {
13249
13313
  if (span.parentSpanId === void 0) continue;
13250
- span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
13314
+ const traceId = typeof span.event.trace?.traceId === "string" ? span.event.trace.traceId : "";
13315
+ span.event.parentId = spanKeyToEventId.get(`${traceId}:${span.parentSpanId}`) ?? span.parentSpanId;
13251
13316
  }
13252
13317
  return {
13253
13318
  events: mapped.map((span) => span.event),
@@ -13461,20 +13526,38 @@ function extractOtlpDocument(root) {
13461
13526
  function mapOtlpStatus(status) {
13462
13527
  if (!isRecord11(status)) return void 0;
13463
13528
  const rawCode = status.code;
13464
- if (typeof rawCode !== "string") return void 0;
13465
- switch (rawCode.toUpperCase()) {
13466
- case "STATUS_CODE_OK":
13467
- case "OK":
13468
- return "ok";
13469
- case "STATUS_CODE_ERROR":
13470
- case "ERROR":
13471
- return "error";
13472
- case "STATUS_CODE_UNSET":
13473
- case "UNSET":
13474
- return "unknown";
13475
- default:
13476
- return "unknown";
13529
+ if (typeof rawCode === "number" && Number.isFinite(rawCode)) {
13530
+ switch (rawCode) {
13531
+ case 0:
13532
+ return "unknown";
13533
+ case 1:
13534
+ return "ok";
13535
+ case 2:
13536
+ return "error";
13537
+ default:
13538
+ return "unknown";
13539
+ }
13540
+ }
13541
+ if (typeof rawCode === "string") {
13542
+ const trimmed = rawCode.trim();
13543
+ if (/^\d+$/.test(trimmed)) {
13544
+ return mapOtlpStatus({ code: Number(trimmed) });
13545
+ }
13546
+ switch (trimmed.toUpperCase()) {
13547
+ case "STATUS_CODE_OK":
13548
+ case "OK":
13549
+ return "ok";
13550
+ case "STATUS_CODE_ERROR":
13551
+ case "ERROR":
13552
+ return "error";
13553
+ case "STATUS_CODE_UNSET":
13554
+ case "UNSET":
13555
+ return "unknown";
13556
+ default:
13557
+ return "unknown";
13558
+ }
13477
13559
  }
13560
+ return void 0;
13478
13561
  }
13479
13562
  function readOtlpKind(attributes, pathPrefix) {
13480
13563
  const warnings = [];
@@ -13700,7 +13783,7 @@ function mapOtlpSpan(context) {
13700
13783
  const errorMessage = isRecord11(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
13701
13784
  const event = {
13702
13785
  schemaVersion: "0.2",
13703
- eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
13786
+ eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : `${traceId}:${spanId}`,
13704
13787
  runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
13705
13788
  kind,
13706
13789
  name: readStringField(span, ["name"]) ?? spanId,
@@ -13749,12 +13832,16 @@ function mapOtlpSpan(context) {
13749
13832
  }
13750
13833
  function mapOtlpEventsToPersisted(document) {
13751
13834
  const mapped = document.spans.map((span) => mapOtlpSpan(span));
13752
- const spanIdToEventId = new Map(
13753
- mapped.map((span) => [span.spanId, span.event.eventId])
13835
+ const spanKeyToEventId = new Map(
13836
+ mapped.map((span) => {
13837
+ const traceId = typeof span.event.trace?.traceId === "string" ? span.event.trace.traceId : "";
13838
+ return [`${traceId}:${span.spanId}`, span.event.eventId];
13839
+ })
13754
13840
  );
13755
13841
  for (const span of mapped) {
13756
13842
  if (span.parentSpanId === void 0) continue;
13757
- span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
13843
+ const traceId = typeof span.event.trace?.traceId === "string" ? span.event.trace.traceId : "";
13844
+ span.event.parentId = spanKeyToEventId.get(`${traceId}:${span.parentSpanId}`) ?? span.parentSpanId;
13758
13845
  }
13759
13846
  return {
13760
13847
  events: mapped.map((span) => span.event),
@@ -16162,7 +16249,7 @@ var init_src = __esm({
16162
16249
  });
16163
16250
 
16164
16251
  // package.json
16165
- var version = "6.31.6";
16252
+ var version = "6.31.8";
16166
16253
 
16167
16254
  // packages/cli/src/list.ts
16168
16255
  init_advanced();
@@ -16511,7 +16598,7 @@ function mapReaderFormat(format) {
16511
16598
  function isMissingFileError(error) {
16512
16599
  return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
16513
16600
  }
16514
- async function readRunTraceEvents(runId, traceDir) {
16601
+ async function readRunPersistedEvents(runId, traceDir) {
16515
16602
  const filePath = getTraceFilePath(runId, traceDir);
16516
16603
  try {
16517
16604
  await promises.access(filePath);
@@ -16519,16 +16606,24 @@ async function readRunTraceEvents(runId, traceDir) {
16519
16606
  { type: "file", path: filePath },
16520
16607
  { format: "agent-inspect-jsonl" }
16521
16608
  );
16522
- const events = persistedInspectEventsToTraceEvents(result.events);
16523
16609
  return {
16524
- events,
16525
- format: events.length > 0 ? mapReaderFormat(result.format) : "empty"
16610
+ events: result.events,
16611
+ format: result.events.length > 0 ? mapReaderFormat(result.format) : "empty"
16526
16612
  };
16527
16613
  } catch (error) {
16528
16614
  if (isMissingFileError(error)) return void 0;
16529
16615
  throw error;
16530
16616
  }
16531
16617
  }
16618
+ async function readRunTraceEvents(runId, traceDir) {
16619
+ const persisted = await readRunPersistedEvents(runId, traceDir);
16620
+ if (persisted === void 0) return void 0;
16621
+ const events = persistedInspectEventsToTraceEvents(persisted.events);
16622
+ return {
16623
+ events,
16624
+ format: events.length > 0 ? persisted.format : "empty"
16625
+ };
16626
+ }
16532
16627
 
16533
16628
  // packages/cli/src/view.ts
16534
16629
  function isModuleNotFound(e) {
@@ -18689,11 +18784,13 @@ function exportOpenInference(tree, options) {
18689
18784
  endNs = startNs + unixNano(ev.durationMs);
18690
18785
  }
18691
18786
  const { openInferenceKind } = mapInspectKindToOI(ev.kind, warnings);
18787
+ const meta2 = ev.attributes;
18788
+ const originalSource = typeof meta2?.originalSourceType === "string" && meta2.originalSourceType.trim() !== "" ? meta2.originalSourceType.trim() : ev.source.type;
18692
18789
  const attrs = {
18693
18790
  "openinference.span.kind": openInferenceKind,
18694
18791
  "agent_inspect.kind": ev.kind,
18695
18792
  "agent_inspect.confidence": ev.confidence,
18696
- "agent_inspect.source.type": ev.source.type,
18793
+ "agent_inspect.source.type": originalSource,
18697
18794
  "agent_inspect.run_id": tree.runId,
18698
18795
  "agent_inspect.event_id": ev.eventId,
18699
18796
  "agent_inspect.status": ev.status ?? "unset"
@@ -18701,9 +18798,9 @@ function exportOpenInference(tree, options) {
18701
18798
  if (ev.durationMs !== void 0) {
18702
18799
  attrs["agent_inspect.duration_ms"] = ev.durationMs;
18703
18800
  }
18704
- const meta2 = ev.attributes;
18705
- if (meta2?.model !== void 0 && typeof meta2.model === "string") {
18706
- attrs["llm.model_name"] = meta2.model;
18801
+ const requestModel = (typeof meta2?.model === "string" && meta2.model.trim() !== "" ? meta2.model : void 0) ?? (typeof meta2?.modelId === "string" && meta2.modelId.trim() !== "" ? meta2.modelId : void 0);
18802
+ if (requestModel !== void 0) {
18803
+ attrs["llm.model_name"] = requestModel;
18707
18804
  }
18708
18805
  const tokens = meta2?.tokens;
18709
18806
  if (tokens && typeof tokens === "object" && tokens !== null) {
@@ -18713,8 +18810,9 @@ function exportOpenInference(tree, options) {
18713
18810
  if (typeof outp === "number") attrs["llm.token_count.completion"] = outp;
18714
18811
  }
18715
18812
  if (includeAttributes && meta2 && typeof meta2 === "object") {
18813
+ const skipKeys = /* @__PURE__ */ new Set(["tokens", "model", "modelId", "originalSourceType"]);
18716
18814
  for (const [k, v] of Object.entries(meta2)) {
18717
- if (k === "tokens" || k === "model") continue;
18815
+ if (skipKeys.has(k)) continue;
18718
18816
  if (v !== void 0 && v !== null && typeof v !== "object") {
18719
18817
  attrs[`agent_inspect.preview.${k}`] = typeof v === "string" ? v.slice(0, maxLen) : v;
18720
18818
  }
@@ -18760,6 +18858,10 @@ function exportOpenInference(tree, options) {
18760
18858
 
18761
18859
  // packages/core/src/exporters/otlp-json-exporter.ts
18762
18860
  init_helpers();
18861
+ var OTLP_STATUS_CODE_UNSET = 0;
18862
+ var OTLP_STATUS_CODE_OK = 1;
18863
+ var OTLP_STATUS_CODE_ERROR = 2;
18864
+ var OTLP_SPAN_KIND_INTERNAL = 1;
18763
18865
  function hexFrom2(seed, byteLen) {
18764
18866
  return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
18765
18867
  }
@@ -18772,7 +18874,19 @@ function stringAttr2(key, value) {
18772
18874
  return { key, value: { stringValue: value } };
18773
18875
  }
18774
18876
  function intAttr(key, value) {
18775
- return { key, value: { intValue: String(value) } };
18877
+ return { key, value: { intValue: String(Math.trunc(value)) } };
18878
+ }
18879
+ function doubleAttr(key, value) {
18880
+ return { key, value: { doubleValue: value } };
18881
+ }
18882
+ function boolAttr(key, value) {
18883
+ return { key, value: { boolValue: value } };
18884
+ }
18885
+ function numberAttr(key, value) {
18886
+ if (Number.isFinite(value) && !Number.isInteger(value)) {
18887
+ return doubleAttr(key, value);
18888
+ }
18889
+ return intAttr(key, value);
18776
18890
  }
18777
18891
  function genAiOperationName(kind) {
18778
18892
  switch (kind) {
@@ -18786,10 +18900,27 @@ function genAiOperationName(kind) {
18786
18900
  return void 0;
18787
18901
  }
18788
18902
  }
18903
+ function resolveSpanTimes(ev) {
18904
+ const startedAtMs = typeof ev.attributes?.startedAtMs === "number" && Number.isFinite(ev.attributes.startedAtMs) ? ev.attributes.startedAtMs : void 0;
18905
+ const endedAtMs = typeof ev.attributes?.endedAtMs === "number" && Number.isFinite(ev.attributes.endedAtMs) ? ev.attributes.endedAtMs : void 0;
18906
+ const startMs = startedAtMs ?? ev.timestamp;
18907
+ const startNs = String(unixNano2(startMs));
18908
+ if (endedAtMs !== void 0) {
18909
+ return { startNs, endNs: String(unixNano2(endedAtMs)) };
18910
+ }
18911
+ if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
18912
+ return {
18913
+ startNs,
18914
+ endNs: String(unixNano2(startMs) + unixNano2(ev.durationMs))
18915
+ };
18916
+ }
18917
+ return { startNs };
18918
+ }
18789
18919
  function exportOtlpJson(tree, options) {
18790
18920
  const warnings = [
18791
18921
  "OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
18792
- "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload."
18922
+ "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload.",
18923
+ "Status and span kind use OTLP numeric enums (StatusCode 0/1/2, SpanKind INTERNAL=1)."
18793
18924
  ];
18794
18925
  const traceId = hexFrom2(`trace:${tree.runId}`, 16);
18795
18926
  const includeAttributes = options?.includeAttributes ?? false;
@@ -18801,29 +18932,34 @@ function exportOtlpJson(tree, options) {
18801
18932
  const ev = n.event;
18802
18933
  const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
18803
18934
  const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
18804
- const startNs = String(unixNano2(ev.timestamp));
18805
- let endNs;
18806
- if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
18807
- endNs = String(unixNano2(ev.timestamp) + unixNano2(ev.durationMs));
18808
- }
18935
+ const { startNs, endNs } = resolveSpanTimes(ev);
18936
+ const meta2 = ev.attributes;
18937
+ const originalSource = typeof meta2?.originalSourceType === "string" && meta2.originalSourceType.trim() !== "" ? meta2.originalSourceType.trim() : ev.source.type;
18809
18938
  const attrs = [
18810
18939
  stringAttr2("agent_inspect.kind", ev.kind),
18811
18940
  stringAttr2("agent_inspect.confidence", ev.confidence),
18812
- stringAttr2("agent_inspect.source.type", ev.source.type),
18941
+ stringAttr2("agent_inspect.source.type", originalSource),
18813
18942
  stringAttr2("agent_inspect.run_id", tree.runId),
18814
18943
  stringAttr2("agent_inspect.event_id", ev.eventId),
18815
18944
  stringAttr2("agent_inspect.status", ev.status ?? "unset")
18816
18945
  ];
18817
18946
  if (ev.durationMs !== void 0) {
18818
- attrs.push(intAttr("agent_inspect.duration_ms", ev.durationMs));
18947
+ attrs.push(numberAttr("agent_inspect.duration_ms", ev.durationMs));
18819
18948
  }
18820
18949
  const op = genAiOperationName(ev.kind);
18821
18950
  if (op !== void 0) {
18822
18951
  attrs.push(stringAttr2("gen_ai.operation.name", op));
18823
18952
  }
18824
- const meta2 = ev.attributes;
18825
- if (meta2?.model !== void 0 && typeof meta2.model === "string") {
18826
- attrs.push(stringAttr2("gen_ai.request.model", meta2.model.slice(0, maxLen)));
18953
+ const requestModel = (typeof meta2?.model === "string" && meta2.model.trim() !== "" ? meta2.model : void 0) ?? (typeof meta2?.modelId === "string" && meta2.modelId.trim() !== "" ? meta2.modelId : void 0);
18954
+ if (requestModel !== void 0) {
18955
+ attrs.push(stringAttr2("gen_ai.request.model", requestModel.slice(0, maxLen)));
18956
+ }
18957
+ const responseModel = typeof meta2?.responseModelId === "string" && meta2.responseModelId.trim() !== "" ? meta2.responseModelId : typeof meta2?.responseModel === "string" && meta2.responseModel.trim() !== "" ? meta2.responseModel : void 0;
18958
+ if (responseModel !== void 0) {
18959
+ attrs.push(stringAttr2("gen_ai.response.model", responseModel.slice(0, maxLen)));
18960
+ }
18961
+ if (typeof meta2?.provider === "string" && meta2.provider.trim() !== "") {
18962
+ attrs.push(stringAttr2("gen_ai.provider.name", meta2.provider.slice(0, maxLen)));
18827
18963
  }
18828
18964
  const tokens = meta2?.tokens;
18829
18965
  if (tokens && typeof tokens === "object" && tokens !== null) {
@@ -18833,31 +18969,39 @@ function exportOtlpJson(tree, options) {
18833
18969
  if (typeof outp === "number") attrs.push(intAttr("gen_ai.usage.output_tokens", outp));
18834
18970
  }
18835
18971
  if (includeAttributes && meta2 && typeof meta2 === "object") {
18972
+ const skipKeys = /* @__PURE__ */ new Set([
18973
+ "tokens",
18974
+ "model",
18975
+ "modelId",
18976
+ "responseModel",
18977
+ "responseModelId",
18978
+ "provider",
18979
+ "originalSourceType"
18980
+ ]);
18836
18981
  for (const [k, v] of Object.entries(meta2)) {
18837
- if (k === "tokens" || k === "model") continue;
18838
- if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
18839
- attrs.push(
18840
- stringAttr2(
18841
- `agent_inspect.preview.${k}`,
18842
- typeof v === "string" ? v.slice(0, maxLen) : String(v)
18843
- )
18844
- );
18982
+ if (skipKeys.has(k)) continue;
18983
+ if (typeof v === "string") {
18984
+ attrs.push(stringAttr2(`agent_inspect.preview.${k}`, v.slice(0, maxLen)));
18985
+ } else if (typeof v === "number" && Number.isFinite(v)) {
18986
+ attrs.push(numberAttr(`agent_inspect.preview.${k}`, v));
18987
+ } else if (typeof v === "boolean") {
18988
+ attrs.push(boolAttr(`agent_inspect.preview.${k}`, v));
18845
18989
  }
18846
18990
  }
18847
18991
  }
18848
- let statusCode = "STATUS_CODE_UNSET";
18992
+ let statusCode = OTLP_STATUS_CODE_UNSET;
18849
18993
  let statusMessage;
18850
18994
  if (ev.status === "error") {
18851
- statusCode = "STATUS_CODE_ERROR";
18995
+ statusCode = OTLP_STATUS_CODE_ERROR;
18852
18996
  statusMessage = meta2 && typeof meta2.error === "object" && meta2.error !== null ? String(meta2.error.message ?? "error").slice(0, maxLen) : "error";
18853
18997
  } else if (ev.status === "ok") {
18854
- statusCode = "STATUS_CODE_OK";
18998
+ statusCode = OTLP_STATUS_CODE_OK;
18855
18999
  }
18856
19000
  const spanJson = {
18857
19001
  traceId,
18858
19002
  spanId,
18859
19003
  name: ev.name,
18860
- kind: "SPAN_KIND_INTERNAL",
19004
+ kind: OTLP_SPAN_KIND_INTERNAL,
18861
19005
  startTimeUnixNano: startNs,
18862
19006
  attributes: attrs,
18863
19007
  status: {
@@ -18899,6 +19043,207 @@ function exportOtlpJson(tree, options) {
18899
19043
 
18900
19044
  // packages/core/src/exporters/validation.ts
18901
19045
  var EXPERIMENTAL = "Experimental compatibility export \u2014 verify against your target tooling before relying on it.";
19046
+ function pushPath(errors, path44, message) {
19047
+ errors.push(`${path44}: ${message}`);
19048
+ }
19049
+ function isRecord16(value) {
19050
+ return value !== null && typeof value === "object" && !Array.isArray(value);
19051
+ }
19052
+ function isHexId(value, byteLen) {
19053
+ return typeof value === "string" && new RegExp(`^[0-9a-fA-F]{${byteLen * 2}}$`).test(value);
19054
+ }
19055
+ function validateAnyValue(errors, path44, value) {
19056
+ if (!isRecord16(value)) {
19057
+ pushPath(errors, path44, "must be an AnyValue object");
19058
+ return;
19059
+ }
19060
+ const variants = [
19061
+ "stringValue",
19062
+ "boolValue",
19063
+ "intValue",
19064
+ "doubleValue",
19065
+ "bytesValue",
19066
+ "arrayValue",
19067
+ "kvlistValue"
19068
+ ].filter((key) => value[key] !== void 0);
19069
+ if (variants.length !== 1) {
19070
+ pushPath(
19071
+ errors,
19072
+ path44,
19073
+ `AnyValue must set exactly one variant (found ${variants.length}: ${variants.join(",")})`
19074
+ );
19075
+ }
19076
+ if (value.intValue !== void 0) {
19077
+ if (typeof value.intValue !== "string" || !/^-?\d+$/.test(value.intValue)) {
19078
+ pushPath(errors, `${path44}.intValue`, "must be a decimal integer string");
19079
+ }
19080
+ }
19081
+ }
19082
+ function validateOtlpJson(content) {
19083
+ const errors = [];
19084
+ const warnings = [
19085
+ EXPERIMENTAL,
19086
+ "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation.",
19087
+ "producer-profile checks IDs, timing, and AnyValue shape; historical string enums are reader-compat warnings only."
19088
+ ];
19089
+ let parsed;
19090
+ try {
19091
+ parsed = JSON.parse(content);
19092
+ } catch {
19093
+ pushPath(errors, "$", "OTLP JSON export is not valid JSON");
19094
+ return { ok: false, format: "otlp-json", errors, warnings };
19095
+ }
19096
+ if (!isRecord16(parsed)) {
19097
+ pushPath(errors, "$", "OTLP JSON export must be an object");
19098
+ return { ok: false, format: "otlp-json", errors, warnings };
19099
+ }
19100
+ if (!Array.isArray(parsed.resourceSpans)) {
19101
+ pushPath(errors, "$.resourceSpans", "must be an array");
19102
+ return { ok: false, format: "otlp-json", errors, warnings };
19103
+ }
19104
+ parsed.resourceSpans.forEach((resourceSpan, rsi) => {
19105
+ const rsPath = `$.resourceSpans[${rsi}]`;
19106
+ if (!isRecord16(resourceSpan)) {
19107
+ pushPath(errors, rsPath, "must be an object");
19108
+ return;
19109
+ }
19110
+ if (!Array.isArray(resourceSpan.scopeSpans)) {
19111
+ pushPath(errors, `${rsPath}.scopeSpans`, "must be an array");
19112
+ return;
19113
+ }
19114
+ resourceSpan.scopeSpans.forEach((scopeSpan, ssi) => {
19115
+ const ssPath = `${rsPath}.scopeSpans[${ssi}]`;
19116
+ if (!isRecord16(scopeSpan)) {
19117
+ pushPath(errors, ssPath, "must be an object");
19118
+ return;
19119
+ }
19120
+ if (!Array.isArray(scopeSpan.spans)) {
19121
+ pushPath(errors, `${ssPath}.spans`, "must be an array");
19122
+ return;
19123
+ }
19124
+ scopeSpan.spans.forEach((span, spi) => {
19125
+ const spanPath = `${ssPath}.spans[${spi}]`;
19126
+ if (!isRecord16(span)) {
19127
+ pushPath(errors, spanPath, "must be an object");
19128
+ return;
19129
+ }
19130
+ if (!isHexId(span.traceId, 16)) {
19131
+ pushPath(
19132
+ errors,
19133
+ `${spanPath}.traceId`,
19134
+ "must be a 32-char hex trace id (16 bytes)"
19135
+ );
19136
+ }
19137
+ if (!isHexId(span.spanId, 8)) {
19138
+ pushPath(
19139
+ errors,
19140
+ `${spanPath}.spanId`,
19141
+ "must be a 16-char hex span id (8 bytes)"
19142
+ );
19143
+ }
19144
+ if (span.parentSpanId !== void 0 && !isHexId(span.parentSpanId, 8)) {
19145
+ pushPath(
19146
+ errors,
19147
+ `${spanPath}.parentSpanId`,
19148
+ "must be a 16-char hex span id when present"
19149
+ );
19150
+ }
19151
+ if (typeof span.name !== "string") {
19152
+ pushPath(errors, `${spanPath}.name`, "must be a string");
19153
+ }
19154
+ if (typeof span.startTimeUnixNano !== "string" || !/^\d+$/.test(span.startTimeUnixNano)) {
19155
+ pushPath(
19156
+ errors,
19157
+ `${spanPath}.startTimeUnixNano`,
19158
+ "must be a decimal-string 64-bit integer"
19159
+ );
19160
+ }
19161
+ if (span.endTimeUnixNano !== void 0 && (typeof span.endTimeUnixNano !== "string" || !/^\d+$/.test(span.endTimeUnixNano))) {
19162
+ pushPath(
19163
+ errors,
19164
+ `${spanPath}.endTimeUnixNano`,
19165
+ "must be a decimal-string 64-bit integer when present"
19166
+ );
19167
+ }
19168
+ if (typeof span.startTimeUnixNano === "string" && /^\d+$/.test(span.startTimeUnixNano) && typeof span.endTimeUnixNano === "string" && /^\d+$/.test(span.endTimeUnixNano)) {
19169
+ try {
19170
+ if (BigInt(span.endTimeUnixNano) < BigInt(span.startTimeUnixNano)) {
19171
+ pushPath(
19172
+ errors,
19173
+ `${spanPath}.endTimeUnixNano`,
19174
+ "must be >= startTimeUnixNano"
19175
+ );
19176
+ }
19177
+ } catch {
19178
+ pushPath(errors, `${spanPath}.endTimeUnixNano`, "out of integer range");
19179
+ }
19180
+ }
19181
+ if (Array.isArray(span.attributes)) {
19182
+ span.attributes.forEach((attr, ai) => {
19183
+ const attrPath = `${spanPath}.attributes[${ai}]`;
19184
+ if (!isRecord16(attr)) {
19185
+ pushPath(errors, attrPath, "must be an object");
19186
+ return;
19187
+ }
19188
+ if (typeof attr.key !== "string") {
19189
+ pushPath(errors, `${attrPath}.key`, "must be a string");
19190
+ }
19191
+ validateAnyValue(errors, `${attrPath}.value`, attr.value);
19192
+ });
19193
+ }
19194
+ if (typeof span.kind === "number") {
19195
+ if (!Number.isInteger(span.kind)) {
19196
+ pushPath(errors, `${spanPath}.kind`, "must be an integer SpanKind enum");
19197
+ }
19198
+ } else if (typeof span.kind === "string") {
19199
+ warnings.push(
19200
+ `${spanPath}.kind: string SpanKind is historical reader-compat only; producer wire format uses numeric enums`
19201
+ );
19202
+ } else if (span.kind !== void 0) {
19203
+ pushPath(
19204
+ errors,
19205
+ `${spanPath}.kind`,
19206
+ "must be numeric SpanKind or historical string enum"
19207
+ );
19208
+ }
19209
+ if (!isRecord16(span.status)) {
19210
+ pushPath(errors, `${spanPath}.status`, "must be an object");
19211
+ } else {
19212
+ const code = span.status.code;
19213
+ if (typeof code === "number") {
19214
+ if (![0, 1, 2].includes(code)) {
19215
+ pushPath(
19216
+ errors,
19217
+ `${spanPath}.status.code`,
19218
+ "must be numeric StatusCode 0 (UNSET), 1 (OK), or 2 (ERROR)"
19219
+ );
19220
+ }
19221
+ } else if (typeof code === "string") {
19222
+ const upper = code.toUpperCase();
19223
+ if (upper !== "STATUS_CODE_UNSET" && upper !== "STATUS_CODE_OK" && upper !== "STATUS_CODE_ERROR" && upper !== "UNSET" && upper !== "OK" && upper !== "ERROR" && !/^[012]$/.test(code.trim())) {
19224
+ pushPath(
19225
+ errors,
19226
+ `${spanPath}.status.code`,
19227
+ "unrecognized StatusCode string"
19228
+ );
19229
+ } else {
19230
+ warnings.push(
19231
+ `${spanPath}.status.code: string StatusCode is historical reader-compat only; producer wire format uses numeric 0/1/2`
19232
+ );
19233
+ }
19234
+ } else {
19235
+ pushPath(
19236
+ errors,
19237
+ `${spanPath}.status.code`,
19238
+ "must be numeric StatusCode 0/1/2 or historical string enum"
19239
+ );
19240
+ }
19241
+ }
19242
+ });
19243
+ });
19244
+ });
19245
+ return { ok: errors.length === 0, format: "otlp-json", errors, warnings };
19246
+ }
18902
19247
  function validateExportContent(format, content) {
18903
19248
  const errors = [];
18904
19249
  const warnings = [EXPERIMENTAL];
@@ -18944,25 +19289,7 @@ function validateExportContent(format, content) {
18944
19289
  return { ok: errors.length === 0, format, errors, warnings };
18945
19290
  }
18946
19291
  if (format === "otlp-json") {
18947
- let parsed;
18948
- try {
18949
- parsed = JSON.parse(content);
18950
- } catch {
18951
- errors.push("OTLP JSON export is not valid JSON");
18952
- return { ok: false, format, errors, warnings };
18953
- }
18954
- if (!parsed || typeof parsed !== "object") {
18955
- errors.push("OTLP JSON export must be an object");
18956
- return { ok: false, format, errors, warnings };
18957
- }
18958
- const o = parsed;
18959
- if (!Array.isArray(o.resourceSpans)) {
18960
- errors.push("OTLP JSON export must include resourceSpans array");
18961
- }
18962
- warnings.push(
18963
- "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation."
18964
- );
18965
- return { ok: errors.length === 0, format, errors, warnings };
19292
+ return validateOtlpJson(content);
18966
19293
  }
18967
19294
  errors.push(`Unsupported export format`);
18968
19295
  return { ok: false, format, errors, warnings };
@@ -19366,7 +19693,7 @@ async function exportCommand(runId, options = {}) {
19366
19693
  const traceDir = resolveTraceDir({ dir: options.dir });
19367
19694
  let events;
19368
19695
  try {
19369
- const result2 = await readRunTraceEvents(id, traceDir);
19696
+ const result2 = await readRunPersistedEvents(id, traceDir);
19370
19697
  events = result2?.events ?? [];
19371
19698
  } catch (e) {
19372
19699
  const msg = e instanceof Error ? e.message : String(e);
@@ -19382,7 +19709,13 @@ Trace directory: ${traceDir}`);
19382
19709
  }
19383
19710
  let tree;
19384
19711
  try {
19385
- tree = manualTraceEventsToRunTree(events);
19712
+ const trees = persistedInspectEventsToRunTrees(events, {
19713
+ coalesceLifecycle: true
19714
+ });
19715
+ tree = trees.find((candidate) => candidate.runId === id) ?? trees[0];
19716
+ if (tree === void 0) {
19717
+ throw new Error("No run tree could be built from persisted events");
19718
+ }
19386
19719
  } catch (e) {
19387
19720
  const msg = e instanceof Error ? e.message : String(e);
19388
19721
  console.error(`[AgentInspect] export failed: ${msg}`);
@@ -19410,7 +19743,9 @@ Trace directory: ${traceDir}`);
19410
19743
  await promises.mkdir(path33__default.default.dirname(outPath), { recursive: true });
19411
19744
  await promises.writeFile(outPath, result.content, "utf-8");
19412
19745
  const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
19413
- console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
19746
+ console.error(
19747
+ `Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`
19748
+ );
19414
19749
  if (validation !== void 0 && !validation.ok) {
19415
19750
  console.error("Validation errors:", validation.errors.join("; "));
19416
19751
  }
@@ -19423,7 +19758,9 @@ Trace directory: ${traceDir}`);
19423
19758
  warnings: [...result.warnings, ...validation?.warnings ?? []],
19424
19759
  validation
19425
19760
  };
19426
- if (outPath === void 0) {
19761
+ if (outPath !== void 0) {
19762
+ payload.out = outPath;
19763
+ } else {
19427
19764
  payload.content = result.content;
19428
19765
  }
19429
19766
  console.log(JSON.stringify(payload, null, 2));
@@ -20535,7 +20872,7 @@ var STRICT_PROFILE_EXTRA_KEYS2 = [
20535
20872
  "retrieval",
20536
20873
  "query"
20537
20874
  ];
20538
- function isRecord16(value) {
20875
+ function isRecord17(value) {
20539
20876
  return typeof value === "object" && value !== null && !Array.isArray(value);
20540
20877
  }
20541
20878
  function toKey2(key) {
@@ -20964,7 +21301,7 @@ var Redactor2 = class {
20964
21301
  });
20965
21302
  return out;
20966
21303
  }
20967
- if (isRecord16(value)) {
21304
+ if (isRecord17(value)) {
20968
21305
  if (state.seen.has(value)) return state.seen.get(value);
20969
21306
  const out = {};
20970
21307
  state.seen.set(value, out);
@@ -20999,7 +21336,7 @@ var REDACTION_POLICY_LIMITS = {
20999
21336
  };
21000
21337
  var ALLOWED_TOP_LEVEL = /* @__PURE__ */ new Set(["version", "extraKeys", "patterns"]);
21001
21338
  var ALLOWED_PATTERN_FIELDS = /* @__PURE__ */ new Set(["id", "type", "value", "severity"]);
21002
- function isRecord17(value) {
21339
+ function isRecord18(value) {
21003
21340
  return value !== null && typeof value === "object" && !Array.isArray(value);
21004
21341
  }
21005
21342
  function rejectRemotePolicyPath(policyPath) {
@@ -21039,7 +21376,7 @@ function rejectUnknownFields(record, allowed, label) {
21039
21376
  }
21040
21377
  function compilePatternDetector(input3, index) {
21041
21378
  const label = `patterns[${index}]`;
21042
- if (!isRecord17(input3)) throw new Error(`${label} must be an object.`);
21379
+ if (!isRecord18(input3)) throw new Error(`${label} must be an object.`);
21043
21380
  const typeEarly = input3.type;
21044
21381
  if (typeEarly === "typed") {
21045
21382
  throw new Error(
@@ -21094,7 +21431,7 @@ function compilePatternDetector(input3, index) {
21094
21431
  };
21095
21432
  }
21096
21433
  function compileRedactionPolicy(raw, policyPath) {
21097
- if (!isRecord17(raw)) {
21434
+ if (!isRecord18(raw)) {
21098
21435
  throw new Error(`Redaction policy must be a JSON object (${policyPath}).`);
21099
21436
  }
21100
21437
  rejectUnknownFields(raw, ALLOWED_TOP_LEVEL, "Redaction policy");
@@ -22130,7 +22467,7 @@ async function openCommand(input3, options = {}, stdin = process.stdin) {
22130
22467
 
22131
22468
  // packages/cli/src/migrate.ts
22132
22469
  init_advanced();
22133
- function isRecord18(value) {
22470
+ function isRecord19(value) {
22134
22471
  return typeof value === "object" && value !== null && !Array.isArray(value);
22135
22472
  }
22136
22473
  function parseTarget(value) {
@@ -22139,7 +22476,7 @@ function parseTarget(value) {
22139
22476
  throw new Error('Unsupported migration target. Use "--to 1.0".');
22140
22477
  }
22141
22478
  function formatOf(value) {
22142
- if (!isRecord18(value)) return "unknown";
22479
+ if (!isRecord19(value)) return "unknown";
22143
22480
  if (value.schemaVersion === "0.1") return "0.1";
22144
22481
  if (value.schemaVersion === "0.2") return "0.2";
22145
22482
  if (value.schemaVersion === "1.0") return "1.0";
@@ -28663,16 +29000,146 @@ function demoTemplate(framework) {
28663
29000
  switch (framework) {
28664
29001
  case "ai-sdk":
28665
29002
  return `/**
28666
- * AI SDK starter \u2014 metadata-only telemetry (no real model calls in this demo).
28667
- * Install: npm install agent-inspect @agent-inspect/ai-sdk ai
29003
+ * AI SDK starter \u2014 keyless MockLanguageModelV3 + @agent-inspect/ai-sdk telemetry.
29004
+ * Install: npm install agent-inspect @agent-inspect/ai-sdk ai@6.0.210 zod
28668
29005
  */
28669
- import { inspectRun, step } from "agent-inspect";
29006
+ import { generateText, stepCountIs, tool } from "ai";
29007
+ import { MockLanguageModelV3 } from "ai/test";
29008
+ import { agentInspect } from "@agent-inspect/ai-sdk";
29009
+ import { z } from "zod";
29010
+ import { mkdirSync, readdirSync, readFileSync, rmSync } from "node:fs";
29011
+ import path from "node:path";
29012
+ import { randomUUID } from "node:crypto";
29013
+
29014
+ const SESSION = randomUUID().slice(0, 8);
29015
+ const TRACE_DIR = path.join(".agent-inspect", \`demo-\${SESSION}\`);
29016
+ const ANSWER = "You have 2 orders";
29017
+ const CORRECT_RUN = \`ai-sdk-demo-correct-\${SESSION}\`;
29018
+ const WRONG_RUN = \`ai-sdk-demo-wrong-\${SESSION}\`;
29019
+
29020
+ const usage = {
29021
+ inputTokens: { total: 4, noCache: 4, cacheRead: 0, cacheWrite: 0 },
29022
+ outputTokens: { total: 3, text: 3, reasoning: 0 },
29023
+ };
29024
+
29025
+ const lookupOrders = tool({
29026
+ description: "Look up orders for a user",
29027
+ inputSchema: z.object({ userId: z.string() }),
29028
+ execute: async () => ({ orders: 2 }),
29029
+ });
29030
+
29031
+ const deleteOrders = tool({
29032
+ description: "Delete orders for a user (wrong path)",
29033
+ inputSchema: z.object({ userId: z.string() }),
29034
+ execute: async () => ({ orders: 2 }),
29035
+ });
29036
+
29037
+ async function runPath(runName, toolName) {
29038
+ const integration = agentInspect({
29039
+ traceDir: TRACE_DIR,
29040
+ runName,
29041
+ capture: "metadata-only",
29042
+ });
29043
+ let step = 0;
29044
+ try {
29045
+ const result = await generateText({
29046
+ model: new MockLanguageModelV3({
29047
+ provider: "fixture-provider",
29048
+ modelId: "fixture-tools",
29049
+ doGenerate: async () => {
29050
+ step += 1;
29051
+ if (step === 1) {
29052
+ return {
29053
+ content: [
29054
+ {
29055
+ type: "tool-call",
29056
+ toolCallId: \`call-\${toolName}\`,
29057
+ toolName,
29058
+ input: JSON.stringify({ userId: "u1" }),
29059
+ },
29060
+ ],
29061
+ finishReason: { unified: "tool-calls", raw: "tool-calls" },
29062
+ usage,
29063
+ warnings: [],
29064
+ };
29065
+ }
29066
+ return {
29067
+ content: [{ type: "text", text: ANSWER }],
29068
+ finishReason: { unified: "stop", raw: "stop" },
29069
+ usage,
29070
+ warnings: [],
29071
+ };
29072
+ },
29073
+ }),
29074
+ tools: { lookup_orders: lookupOrders, delete_orders: deleteOrders },
29075
+ prompt: "How many orders do I have?",
29076
+ stopWhen: stepCountIs(5),
29077
+ experimental_telemetry: {
29078
+ isEnabled: true,
29079
+ recordInputs: false,
29080
+ recordOutputs: false,
29081
+ integrations: [integration],
29082
+ },
29083
+ });
29084
+ if (result.text !== ANSWER) {
29085
+ throw new Error(\`unexpected answer for \${runName}: \${result.text}\`);
29086
+ }
29087
+ } finally {
29088
+ await integration.flush();
29089
+ await integration.close();
29090
+ }
29091
+ }
29092
+
29093
+ function selectRunIdByName(runName) {
29094
+ const files = readdirSync(TRACE_DIR).filter((f) => f.endsWith(".jsonl"));
29095
+ const matches = [];
29096
+ for (const file of files) {
29097
+ const text = readFileSync(path.join(TRACE_DIR, file), "utf8");
29098
+ if (text.includes(\`"name":"\${runName}"\`) || text.includes(\`"name": "\${runName}"\`)) {
29099
+ matches.push(file.replace(/\\.jsonl$/, ""));
29100
+ }
29101
+ }
29102
+ if (matches.length !== 1) {
29103
+ throw new Error(
29104
+ \`expected exactly one run named \${runName}, found \${matches.length}\`,
29105
+ );
29106
+ }
29107
+ return matches[0];
29108
+ }
29109
+
29110
+ function assertToolExecuted(runId, requiredTool) {
29111
+ const text = readFileSync(path.join(TRACE_DIR, \`\${runId}.jsonl\`), "utf8");
29112
+ const toolHits = text
29113
+ .split("\\n")
29114
+ .filter(Boolean)
29115
+ .map((line) => JSON.parse(line))
29116
+ .filter(
29117
+ (ev) =>
29118
+ (ev.kind === "TOOL" || ev.attributes?.stepType === "tool") &&
29119
+ (ev.name === requiredTool || ev.attributes?.toolName === requiredTool),
29120
+ );
29121
+ if (toolHits.length === 0) {
29122
+ throw new Error(\`expected TOOL events for \${requiredTool} in \${runId}\`);
29123
+ }
29124
+ }
28670
29125
 
28671
29126
  async function main() {
28672
- await inspectRun("ai-sdk-demo", async () => {
28673
- await step.tool("mock-generate", async () => ({ text: "ok" }));
28674
- }, { traceDir: ".agent-inspect", silent: true });
28675
- console.log("Trace written to .agent-inspect/");
29127
+ mkdirSync(TRACE_DIR, { recursive: true });
29128
+ try {
29129
+ await runPath(CORRECT_RUN, "lookup_orders");
29130
+ await runPath(WRONG_RUN, "delete_orders");
29131
+ const correctId = selectRunIdByName(CORRECT_RUN);
29132
+ const wrongId = selectRunIdByName(WRONG_RUN);
29133
+ assertToolExecuted(correctId, "lookup_orders");
29134
+ assertToolExecuted(wrongId, "delete_orders");
29135
+ console.log(\`Trace written to \${TRACE_DIR}/\`);
29136
+ console.log(\`Correct path runId: \${correctId}\`);
29137
+ console.log(\`Wrong path runId: \${wrongId}\`);
29138
+ console.log(\`Next: npx agent-inspect check \${correctId} --dir \${TRACE_DIR} --required-tool lookup_orders\`);
29139
+ } catch (error) {
29140
+ rmSync(TRACE_DIR, { recursive: true, force: true });
29141
+ throw error;
29142
+ }
28676
29143
  }
28677
29144
 
28678
29145
  main().catch((error) => {