agent-inspect 6.31.6 → 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 (35) hide show
  1. package/CHANGELOG.md +7 -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-QV73NM25.mjs} +97 -25
  6. package/packages/cli/dist/chunk-QV73NM25.mjs.map +1 -0
  7. package/packages/cli/dist/index.cjs +414 -81
  8. package/packages/cli/dist/index.cjs.map +1 -1
  9. package/packages/cli/dist/index.mjs +322 -62
  10. package/packages/cli/dist/index.mjs.map +1 -1
  11. package/packages/cli/dist/{src-H22KNWAZ.mjs → src-KFOBXFPN.mjs} +3 -3
  12. package/packages/cli/dist/{src-H22KNWAZ.mjs.map → src-KFOBXFPN.mjs.map} +1 -1
  13. package/packages/core/dist/advanced.cjs +94 -22
  14. package/packages/core/dist/advanced.cjs.map +1 -1
  15. package/packages/core/dist/advanced.mjs +4 -4
  16. package/packages/core/dist/{chunk-OSGNGRHF.mjs → chunk-3RV3JILQ.mjs} +34 -16
  17. package/packages/core/dist/chunk-3RV3JILQ.mjs.map +1 -0
  18. package/packages/core/dist/{chunk-EXGQLMVB.mjs → chunk-7YYS742B.mjs} +49 -12
  19. package/packages/core/dist/chunk-7YYS742B.mjs.map +1 -0
  20. package/packages/core/dist/{chunk-Q5SLKV2Y.mjs → chunk-QLYUWKK7.mjs} +19 -2
  21. package/packages/core/dist/chunk-QLYUWKK7.mjs.map +1 -0
  22. package/packages/core/dist/exporters.cjs +224 -47
  23. package/packages/core/dist/exporters.cjs.map +1 -1
  24. package/packages/core/dist/exporters.mjs +178 -38
  25. package/packages/core/dist/exporters.mjs.map +1 -1
  26. package/packages/core/dist/persisted.cjs +17 -0
  27. package/packages/core/dist/persisted.cjs.map +1 -1
  28. package/packages/core/dist/persisted.mjs +2 -2
  29. package/packages/core/dist/readers.cjs +48 -13
  30. package/packages/core/dist/readers.cjs.map +1 -1
  31. package/packages/core/dist/readers.mjs +2 -2
  32. package/packages/cli/dist/chunk-YXDNWHNJ.mjs.map +0 -1
  33. package/packages/core/dist/chunk-EXGQLMVB.mjs.map +0 -1
  34. package/packages/core/dist/chunk-OSGNGRHF.mjs.map +0 -1
  35. package/packages/core/dist/chunk-Q5SLKV2Y.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { ATTRIBUTION_CONFIDENCES, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, resolveBundleRunIds, getTraceFilePath, openTrace, sha256Hex, collectTraceSchemaVersions, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceHtmlShell, buildEvidenceProvenanceViewHtml, EVIDENCE_FORMAT_VERSION, inferEvidenceFileRole, buildEvidenceSafetyViewHtml, buildEvidenceDiffViewHtml, buildEvidenceCircuitViewHtml, buildEvidenceOutcomesViewHtml, buildEvidenceContractsViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceCausalFailureViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceTreeViewHtml, buildEvidenceManifest, serializeEvidenceManifest, buildZipArchive, assertBundlePathContained, enrichSessionRunRecord, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, runTraceChecks, assertEvidenceRelativePath, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, TraceReadError, resolveRedactionProfile, applyProfileMetadataCaps, Redactor, flattenTree, escapeHtml, safeString, compactAttributes, stableJson, escapeMarkdown, DEFAULT_REDACT_KEYS, truncateStringForProfile, stringContainsHighConfidenceCredential, isCredentialSensitiveKey, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, renderErrorLine, isAgentInspectTrace, defineTraceContract, filterMetasBySessionScope, evaluateTraceContract, aggregateSessionCheckResults, createBaselineRegressionRule, summarizeSemanticParity, buildEvidenceCiPackage, verifyEvidenceDirectory, diffTraceEvents, renderRunDiff, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, formatStepLabel, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, resolveSuiteTemplate, defaultSuiteConfigTemplate, loadSuiteConfig, validateSuiteConfig, runSuite, renderSuiteReport, analyzeCohort, renderCohortReport, gateHasThresholds, runGate, renderGateReport, persistedInspectEventsToTraceEvents, renderStepLine, getIndent, TreeBuilder, zeroKinds, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, projectLogicalEvents, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, isAttributionConfidence, diffRuns, manualTraceEventsToComparableRun } from './chunk-YXDNWHNJ.mjs';
2
+ import { ATTRIBUTION_CONFIDENCES, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, resolveBundleRunIds, getTraceFilePath, openTrace, sha256Hex, collectTraceSchemaVersions, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceHtmlShell, buildEvidenceProvenanceViewHtml, EVIDENCE_FORMAT_VERSION, inferEvidenceFileRole, buildEvidenceSafetyViewHtml, buildEvidenceDiffViewHtml, buildEvidenceCircuitViewHtml, buildEvidenceOutcomesViewHtml, buildEvidenceContractsViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceCausalFailureViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceTreeViewHtml, buildEvidenceManifest, serializeEvidenceManifest, buildZipArchive, assertBundlePathContained, enrichSessionRunRecord, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, runTraceChecks, assertEvidenceRelativePath, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, TraceReadError, resolveRedactionProfile, applyProfileMetadataCaps, Redactor, flattenTree, escapeHtml, safeString, compactAttributes, stableJson, escapeMarkdown, DEFAULT_REDACT_KEYS, truncateStringForProfile, stringContainsHighConfidenceCredential, isCredentialSensitiveKey, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, renderErrorLine, isAgentInspectTrace, persistedInspectEventsToRunTrees, defineTraceContract, filterMetasBySessionScope, evaluateTraceContract, aggregateSessionCheckResults, createBaselineRegressionRule, summarizeSemanticParity, buildEvidenceCiPackage, verifyEvidenceDirectory, diffTraceEvents, renderRunDiff, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, formatStepLabel, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, resolveSuiteTemplate, defaultSuiteConfigTemplate, loadSuiteConfig, validateSuiteConfig, runSuite, renderSuiteReport, analyzeCohort, renderCohortReport, gateHasThresholds, runGate, renderGateReport, persistedInspectEventsToTraceEvents, renderStepLine, getIndent, TreeBuilder, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, projectLogicalEvents, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, isAttributionConfidence, zeroKinds, diffRuns, manualTraceEventsToComparableRun } from './chunk-QV73NM25.mjs';
3
3
  import { realpathSync, constants as constants$1, existsSync, readFileSync } from 'fs';
4
4
  import path19 from 'path';
5
5
  import { fileURLToPath, pathToFileURL } from 'url';
@@ -14,7 +14,7 @@ import { createServer } from 'http';
14
14
  import { createRequire } from 'module';
15
15
 
16
16
  // package.json
17
- var version = "6.31.6";
17
+ var version = "6.31.7";
18
18
 
19
19
  // packages/cli/src/trace-dir-scale.ts
20
20
  var TRACE_COUNT_WARN = 1e3;
@@ -333,7 +333,7 @@ function mapReaderFormat(format) {
333
333
  function isMissingFileError(error) {
334
334
  return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
335
335
  }
336
- async function readRunTraceEvents(runId, traceDir) {
336
+ async function readRunPersistedEvents(runId, traceDir) {
337
337
  const filePath = getTraceFilePath(runId, traceDir);
338
338
  try {
339
339
  await access(filePath);
@@ -341,16 +341,24 @@ async function readRunTraceEvents(runId, traceDir) {
341
341
  { type: "file", path: filePath },
342
342
  { format: "agent-inspect-jsonl" }
343
343
  );
344
- const events = persistedInspectEventsToTraceEvents(result.events);
345
344
  return {
346
- events,
347
- format: events.length > 0 ? mapReaderFormat(result.format) : "empty"
345
+ events: result.events,
346
+ format: result.events.length > 0 ? mapReaderFormat(result.format) : "empty"
348
347
  };
349
348
  } catch (error) {
350
349
  if (isMissingFileError(error)) return void 0;
351
350
  throw error;
352
351
  }
353
352
  }
353
+ async function readRunTraceEvents(runId, traceDir) {
354
+ const persisted = await readRunPersistedEvents(runId, traceDir);
355
+ if (persisted === void 0) return void 0;
356
+ const events = persistedInspectEventsToTraceEvents(persisted.events);
357
+ return {
358
+ events,
359
+ format: events.length > 0 ? persisted.format : "empty"
360
+ };
361
+ }
354
362
 
355
363
  // packages/cli/src/view.ts
356
364
  function isModuleNotFound(e) {
@@ -2556,6 +2564,10 @@ function exportOpenInference(tree, options) {
2556
2564
  warnings
2557
2565
  };
2558
2566
  }
2567
+ var OTLP_STATUS_CODE_UNSET = 0;
2568
+ var OTLP_STATUS_CODE_OK = 1;
2569
+ var OTLP_STATUS_CODE_ERROR = 2;
2570
+ var OTLP_SPAN_KIND_INTERNAL = 1;
2559
2571
  function hexFrom2(seed, byteLen) {
2560
2572
  return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
2561
2573
  }
@@ -2568,7 +2580,19 @@ function stringAttr(key, value) {
2568
2580
  return { key, value: { stringValue: value } };
2569
2581
  }
2570
2582
  function intAttr(key, value) {
2571
- return { key, value: { intValue: String(value) } };
2583
+ return { key, value: { intValue: String(Math.trunc(value)) } };
2584
+ }
2585
+ function doubleAttr(key, value) {
2586
+ return { key, value: { doubleValue: value } };
2587
+ }
2588
+ function boolAttr(key, value) {
2589
+ return { key, value: { boolValue: value } };
2590
+ }
2591
+ function numberAttr(key, value) {
2592
+ if (Number.isFinite(value) && !Number.isInteger(value)) {
2593
+ return doubleAttr(key, value);
2594
+ }
2595
+ return intAttr(key, value);
2572
2596
  }
2573
2597
  function genAiOperationName(kind) {
2574
2598
  switch (kind) {
@@ -2582,10 +2606,27 @@ function genAiOperationName(kind) {
2582
2606
  return void 0;
2583
2607
  }
2584
2608
  }
2609
+ function resolveSpanTimes(ev) {
2610
+ const startedAtMs = typeof ev.attributes?.startedAtMs === "number" && Number.isFinite(ev.attributes.startedAtMs) ? ev.attributes.startedAtMs : void 0;
2611
+ const endedAtMs = typeof ev.attributes?.endedAtMs === "number" && Number.isFinite(ev.attributes.endedAtMs) ? ev.attributes.endedAtMs : void 0;
2612
+ const startMs = startedAtMs ?? ev.timestamp;
2613
+ const startNs = String(unixNano2(startMs));
2614
+ if (endedAtMs !== void 0) {
2615
+ return { startNs, endNs: String(unixNano2(endedAtMs)) };
2616
+ }
2617
+ if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
2618
+ return {
2619
+ startNs,
2620
+ endNs: String(unixNano2(startMs) + unixNano2(ev.durationMs))
2621
+ };
2622
+ }
2623
+ return { startNs };
2624
+ }
2585
2625
  function exportOtlpJson(tree, options) {
2586
2626
  const warnings = [
2587
2627
  "OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
2588
- "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload."
2628
+ "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload.",
2629
+ "Status and span kind use OTLP numeric enums (StatusCode 0/1/2, SpanKind INTERNAL=1)."
2589
2630
  ];
2590
2631
  const traceId = hexFrom2(`trace:${tree.runId}`, 16);
2591
2632
  const includeAttributes = options?.includeAttributes ?? false;
@@ -2597,11 +2638,7 @@ function exportOtlpJson(tree, options) {
2597
2638
  const ev = n.event;
2598
2639
  const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
2599
2640
  const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
2600
- const startNs = String(unixNano2(ev.timestamp));
2601
- let endNs;
2602
- if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
2603
- endNs = String(unixNano2(ev.timestamp) + unixNano2(ev.durationMs));
2604
- }
2641
+ const { startNs, endNs } = resolveSpanTimes(ev);
2605
2642
  const attrs = [
2606
2643
  stringAttr("agent_inspect.kind", ev.kind),
2607
2644
  stringAttr("agent_inspect.confidence", ev.confidence),
@@ -2631,29 +2668,28 @@ function exportOtlpJson(tree, options) {
2631
2668
  if (includeAttributes && meta && typeof meta === "object") {
2632
2669
  for (const [k, v] of Object.entries(meta)) {
2633
2670
  if (k === "tokens" || k === "model") continue;
2634
- if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
2635
- attrs.push(
2636
- stringAttr(
2637
- `agent_inspect.preview.${k}`,
2638
- typeof v === "string" ? v.slice(0, maxLen) : String(v)
2639
- )
2640
- );
2671
+ if (typeof v === "string") {
2672
+ attrs.push(stringAttr(`agent_inspect.preview.${k}`, v.slice(0, maxLen)));
2673
+ } else if (typeof v === "number" && Number.isFinite(v)) {
2674
+ attrs.push(numberAttr(`agent_inspect.preview.${k}`, v));
2675
+ } else if (typeof v === "boolean") {
2676
+ attrs.push(boolAttr(`agent_inspect.preview.${k}`, v));
2641
2677
  }
2642
2678
  }
2643
2679
  }
2644
- let statusCode = "STATUS_CODE_UNSET";
2680
+ let statusCode = OTLP_STATUS_CODE_UNSET;
2645
2681
  let statusMessage;
2646
2682
  if (ev.status === "error") {
2647
- statusCode = "STATUS_CODE_ERROR";
2683
+ statusCode = OTLP_STATUS_CODE_ERROR;
2648
2684
  statusMessage = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error").slice(0, maxLen) : "error";
2649
2685
  } else if (ev.status === "ok") {
2650
- statusCode = "STATUS_CODE_OK";
2686
+ statusCode = OTLP_STATUS_CODE_OK;
2651
2687
  }
2652
2688
  const spanJson = {
2653
2689
  traceId,
2654
2690
  spanId,
2655
2691
  name: ev.name,
2656
- kind: "SPAN_KIND_INTERNAL",
2692
+ kind: OTLP_SPAN_KIND_INTERNAL,
2657
2693
  startTimeUnixNano: startNs,
2658
2694
  attributes: attrs,
2659
2695
  status: {
@@ -2695,6 +2731,136 @@ function exportOtlpJson(tree, options) {
2695
2731
 
2696
2732
  // packages/core/src/exporters/validation.ts
2697
2733
  var EXPERIMENTAL = "Experimental compatibility export \u2014 verify against your target tooling before relying on it.";
2734
+ function pushPath(errors, path30, message) {
2735
+ errors.push(`${path30}: ${message}`);
2736
+ }
2737
+ function isRecord5(value) {
2738
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2739
+ }
2740
+ function validateOtlpJson(content) {
2741
+ const errors = [];
2742
+ const warnings = [
2743
+ EXPERIMENTAL,
2744
+ "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation.",
2745
+ "protocol-valid \u2260 profile-complete \u2260 fixture-complete \u2014 nested path diagnostics report protocol shape only."
2746
+ ];
2747
+ let parsed;
2748
+ try {
2749
+ parsed = JSON.parse(content);
2750
+ } catch {
2751
+ pushPath(errors, "$", "OTLP JSON export is not valid JSON");
2752
+ return { ok: false, format: "otlp-json", errors, warnings };
2753
+ }
2754
+ if (!isRecord5(parsed)) {
2755
+ pushPath(errors, "$", "OTLP JSON export must be an object");
2756
+ return { ok: false, format: "otlp-json", errors, warnings };
2757
+ }
2758
+ if (!Array.isArray(parsed.resourceSpans)) {
2759
+ pushPath(errors, "$.resourceSpans", "must be an array");
2760
+ return { ok: false, format: "otlp-json", errors, warnings };
2761
+ }
2762
+ parsed.resourceSpans.forEach((resourceSpan, rsi) => {
2763
+ const rsPath = `$.resourceSpans[${rsi}]`;
2764
+ if (!isRecord5(resourceSpan)) {
2765
+ pushPath(errors, rsPath, "must be an object");
2766
+ return;
2767
+ }
2768
+ if (!Array.isArray(resourceSpan.scopeSpans)) {
2769
+ pushPath(errors, `${rsPath}.scopeSpans`, "must be an array");
2770
+ return;
2771
+ }
2772
+ resourceSpan.scopeSpans.forEach((scopeSpan, ssi) => {
2773
+ const ssPath = `${rsPath}.scopeSpans[${ssi}]`;
2774
+ if (!isRecord5(scopeSpan)) {
2775
+ pushPath(errors, ssPath, "must be an object");
2776
+ return;
2777
+ }
2778
+ if (!Array.isArray(scopeSpan.spans)) {
2779
+ pushPath(errors, `${ssPath}.spans`, "must be an array");
2780
+ return;
2781
+ }
2782
+ scopeSpan.spans.forEach((span, spi) => {
2783
+ const spanPath = `${ssPath}.spans[${spi}]`;
2784
+ if (!isRecord5(span)) {
2785
+ pushPath(errors, spanPath, "must be an object");
2786
+ return;
2787
+ }
2788
+ if (typeof span.traceId !== "string" || span.traceId.length === 0) {
2789
+ pushPath(errors, `${spanPath}.traceId`, "must be a non-empty string");
2790
+ }
2791
+ if (typeof span.spanId !== "string" || span.spanId.length === 0) {
2792
+ pushPath(errors, `${spanPath}.spanId`, "must be a non-empty string");
2793
+ }
2794
+ if (typeof span.name !== "string") {
2795
+ pushPath(errors, `${spanPath}.name`, "must be a string");
2796
+ }
2797
+ if (typeof span.startTimeUnixNano !== "string" || !/^\d+$/.test(span.startTimeUnixNano)) {
2798
+ pushPath(
2799
+ errors,
2800
+ `${spanPath}.startTimeUnixNano`,
2801
+ "must be a decimal-string 64-bit integer"
2802
+ );
2803
+ }
2804
+ if (span.endTimeUnixNano !== void 0 && (typeof span.endTimeUnixNano !== "string" || !/^\d+$/.test(span.endTimeUnixNano))) {
2805
+ pushPath(
2806
+ errors,
2807
+ `${spanPath}.endTimeUnixNano`,
2808
+ "must be a decimal-string 64-bit integer when present"
2809
+ );
2810
+ }
2811
+ if (typeof span.kind === "number") {
2812
+ if (!Number.isInteger(span.kind)) {
2813
+ pushPath(errors, `${spanPath}.kind`, "must be an integer SpanKind enum");
2814
+ }
2815
+ } else if (typeof span.kind === "string") {
2816
+ warnings.push(
2817
+ `${spanPath}.kind: string SpanKind enum is legacy; prefer numeric (INTERNAL=1)`
2818
+ );
2819
+ } else if (span.kind !== void 0) {
2820
+ pushPath(
2821
+ errors,
2822
+ `${spanPath}.kind`,
2823
+ "must be numeric SpanKind or historical string enum"
2824
+ );
2825
+ }
2826
+ if (!isRecord5(span.status)) {
2827
+ pushPath(errors, `${spanPath}.status`, "must be an object");
2828
+ } else {
2829
+ const code = span.status.code;
2830
+ if (typeof code === "number") {
2831
+ if (![0, 1, 2].includes(code)) {
2832
+ pushPath(
2833
+ errors,
2834
+ `${spanPath}.status.code`,
2835
+ "must be numeric StatusCode 0 (UNSET), 1 (OK), or 2 (ERROR)"
2836
+ );
2837
+ }
2838
+ } else if (typeof code === "string") {
2839
+ const upper = code.toUpperCase();
2840
+ if (upper !== "STATUS_CODE_UNSET" && upper !== "STATUS_CODE_OK" && upper !== "STATUS_CODE_ERROR" && upper !== "UNSET" && upper !== "OK" && upper !== "ERROR" && !/^[012]$/.test(code.trim())) {
2841
+ pushPath(
2842
+ errors,
2843
+ `${spanPath}.status.code`,
2844
+ "unrecognized StatusCode string"
2845
+ );
2846
+ } else {
2847
+ warnings.push(
2848
+ `${spanPath}.status.code: string StatusCode is legacy; prefer numeric 0/1/2`
2849
+ );
2850
+ }
2851
+ } else {
2852
+ pushPath(
2853
+ errors,
2854
+ `${spanPath}.status.code`,
2855
+ "must be numeric StatusCode 0/1/2 or historical string enum"
2856
+ );
2857
+ }
2858
+ }
2859
+ });
2860
+ });
2861
+ });
2862
+ return { ok: errors.length === 0, format: "otlp-json", errors, warnings };
2863
+ }
2698
2864
  function validateExportContent(format, content) {
2699
2865
  const errors = [];
2700
2866
  const warnings = [EXPERIMENTAL];
@@ -2740,25 +2906,7 @@ function validateExportContent(format, content) {
2740
2906
  return { ok: errors.length === 0, format, errors, warnings };
2741
2907
  }
2742
2908
  if (format === "otlp-json") {
2743
- let parsed;
2744
- try {
2745
- parsed = JSON.parse(content);
2746
- } catch {
2747
- errors.push("OTLP JSON export is not valid JSON");
2748
- return { ok: false, format, errors, warnings };
2749
- }
2750
- if (!parsed || typeof parsed !== "object") {
2751
- errors.push("OTLP JSON export must be an object");
2752
- return { ok: false, format, errors, warnings };
2753
- }
2754
- const o = parsed;
2755
- if (!Array.isArray(o.resourceSpans)) {
2756
- errors.push("OTLP JSON export must include resourceSpans array");
2757
- }
2758
- warnings.push(
2759
- "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation."
2760
- );
2761
- return { ok: errors.length === 0, format, errors, warnings };
2909
+ return validateOtlpJson(content);
2762
2910
  }
2763
2911
  errors.push(`Unsupported export format`);
2764
2912
  return { ok: false, format, errors, warnings };
@@ -3151,7 +3299,7 @@ async function exportCommand(runId, options = {}) {
3151
3299
  const traceDir = resolveTraceDir({ dir: options.dir });
3152
3300
  let events;
3153
3301
  try {
3154
- const result2 = await readRunTraceEvents(id, traceDir);
3302
+ const result2 = await readRunPersistedEvents(id, traceDir);
3155
3303
  events = result2?.events ?? [];
3156
3304
  } catch (e) {
3157
3305
  const msg = e instanceof Error ? e.message : String(e);
@@ -3167,7 +3315,11 @@ Trace directory: ${traceDir}`);
3167
3315
  }
3168
3316
  let tree;
3169
3317
  try {
3170
- tree = manualTraceEventsToRunTree(events);
3318
+ const trees = persistedInspectEventsToRunTrees(events);
3319
+ tree = trees.find((candidate) => candidate.runId === id) ?? trees[0];
3320
+ if (tree === void 0) {
3321
+ throw new Error("No run tree could be built from persisted events");
3322
+ }
3171
3323
  } catch (e) {
3172
3324
  const msg = e instanceof Error ? e.message : String(e);
3173
3325
  console.error(`[AgentInspect] export failed: ${msg}`);
@@ -3195,7 +3347,9 @@ Trace directory: ${traceDir}`);
3195
3347
  await mkdir(path19.dirname(outPath), { recursive: true });
3196
3348
  await writeFile(outPath, result.content, "utf-8");
3197
3349
  const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
3198
- console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
3350
+ console.error(
3351
+ `Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`
3352
+ );
3199
3353
  if (validation !== void 0 && !validation.ok) {
3200
3354
  console.error("Validation errors:", validation.errors.join("; "));
3201
3355
  }
@@ -3208,7 +3362,9 @@ Trace directory: ${traceDir}`);
3208
3362
  warnings: [...result.warnings, ...validation?.warnings ?? []],
3209
3363
  validation
3210
3364
  };
3211
- if (outPath === void 0) {
3365
+ if (outPath !== void 0) {
3366
+ payload.out = outPath;
3367
+ } else {
3212
3368
  payload.content = result.content;
3213
3369
  }
3214
3370
  console.log(JSON.stringify(payload, null, 2));
@@ -3565,7 +3721,7 @@ async function newestTraceMtimeMs(traceDir) {
3565
3721
  }
3566
3722
  async function loadSessionRuns(traceDir) {
3567
3723
  try {
3568
- const mod = await import('./src-H22KNWAZ.mjs');
3724
+ const mod = await import('./src-KFOBXFPN.mjs');
3569
3725
  const dbPath = mod.resolveIndexDbPath(traceDir);
3570
3726
  const status = mod.indexStatus(dbPath);
3571
3727
  if (!status.healthy) {
@@ -4298,7 +4454,7 @@ var STRICT_PROFILE_EXTRA_KEYS = [
4298
4454
  "retrieval",
4299
4455
  "query"
4300
4456
  ];
4301
- function isRecord5(value) {
4457
+ function isRecord6(value) {
4302
4458
  return typeof value === "object" && value !== null && !Array.isArray(value);
4303
4459
  }
4304
4460
  function toKey(key) {
@@ -4727,7 +4883,7 @@ var Redactor2 = class {
4727
4883
  });
4728
4884
  return out;
4729
4885
  }
4730
- if (isRecord5(value)) {
4886
+ if (isRecord6(value)) {
4731
4887
  if (state.seen.has(value)) return state.seen.get(value);
4732
4888
  const out = {};
4733
4889
  state.seen.set(value, out);
@@ -4762,7 +4918,7 @@ var REDACTION_POLICY_LIMITS = {
4762
4918
  };
4763
4919
  var ALLOWED_TOP_LEVEL = /* @__PURE__ */ new Set(["version", "extraKeys", "patterns"]);
4764
4920
  var ALLOWED_PATTERN_FIELDS = /* @__PURE__ */ new Set(["id", "type", "value", "severity"]);
4765
- function isRecord6(value) {
4921
+ function isRecord7(value) {
4766
4922
  return value !== null && typeof value === "object" && !Array.isArray(value);
4767
4923
  }
4768
4924
  function rejectRemotePolicyPath(policyPath) {
@@ -4802,7 +4958,7 @@ function rejectUnknownFields(record, allowed, label) {
4802
4958
  }
4803
4959
  function compilePatternDetector(input3, index) {
4804
4960
  const label = `patterns[${index}]`;
4805
- if (!isRecord6(input3)) throw new Error(`${label} must be an object.`);
4961
+ if (!isRecord7(input3)) throw new Error(`${label} must be an object.`);
4806
4962
  const typeEarly = input3.type;
4807
4963
  if (typeEarly === "typed") {
4808
4964
  throw new Error(
@@ -4857,7 +5013,7 @@ function compilePatternDetector(input3, index) {
4857
5013
  };
4858
5014
  }
4859
5015
  function compileRedactionPolicy(raw, policyPath) {
4860
- if (!isRecord6(raw)) {
5016
+ if (!isRecord7(raw)) {
4861
5017
  throw new Error(`Redaction policy must be a JSON object (${policyPath}).`);
4862
5018
  }
4863
5019
  rejectUnknownFields(raw, ALLOWED_TOP_LEVEL, "Redaction policy");
@@ -5879,7 +6035,7 @@ async function openCommand(input3, options = {}, stdin = process.stdin) {
5879
6035
  }
5880
6036
  }
5881
6037
  }
5882
- function isRecord7(value) {
6038
+ function isRecord8(value) {
5883
6039
  return typeof value === "object" && value !== null && !Array.isArray(value);
5884
6040
  }
5885
6041
  function parseTarget(value) {
@@ -5888,7 +6044,7 @@ function parseTarget(value) {
5888
6044
  throw new Error('Unsupported migration target. Use "--to 1.0".');
5889
6045
  }
5890
6046
  function formatOf(value) {
5891
- if (!isRecord7(value)) return "unknown";
6047
+ if (!isRecord8(value)) return "unknown";
5892
6048
  if (value.schemaVersion === "0.1") return "0.1";
5893
6049
  if (value.schemaVersion === "0.2") return "0.2";
5894
6050
  if (value.schemaVersion === "1.0") return "1.0";
@@ -12389,16 +12545,120 @@ function demoTemplate(framework) {
12389
12545
  switch (framework) {
12390
12546
  case "ai-sdk":
12391
12547
  return `/**
12392
- * AI SDK starter \u2014 metadata-only telemetry (no real model calls in this demo).
12393
- * Install: npm install agent-inspect @agent-inspect/ai-sdk ai
12548
+ * AI SDK starter \u2014 keyless MockLanguageModelV3 + @agent-inspect/ai-sdk telemetry.
12549
+ * Install: npm install agent-inspect @agent-inspect/ai-sdk ai@6.0.210 zod
12394
12550
  */
12395
- import { inspectRun, step } from "agent-inspect";
12551
+ import { generateText, stepCountIs, tool } from "ai";
12552
+ import { MockLanguageModelV3 } from "ai/test";
12553
+ import { agentInspect } from "@agent-inspect/ai-sdk";
12554
+ import { z } from "zod";
12555
+ import { readdirSync, readFileSync } from "node:fs";
12556
+ import path from "node:path";
12557
+
12558
+ const TRACE_DIR = ".agent-inspect";
12559
+ const ANSWER = "You have 2 orders";
12560
+ const CORRECT_RUN = "ai-sdk-demo-correct";
12561
+ const WRONG_RUN = "ai-sdk-demo-wrong";
12562
+
12563
+ const usage = {
12564
+ inputTokens: { total: 4, noCache: 4, cacheRead: 0, cacheWrite: 0 },
12565
+ outputTokens: { total: 3, text: 3, reasoning: 0 },
12566
+ };
12567
+
12568
+ const lookupOrders = tool({
12569
+ description: "Look up orders for a user",
12570
+ inputSchema: z.object({ userId: z.string() }),
12571
+ execute: async () => ({ orders: 2 }),
12572
+ });
12573
+
12574
+ const deleteOrders = tool({
12575
+ description: "Delete orders for a user (wrong path)",
12576
+ inputSchema: z.object({ userId: z.string() }),
12577
+ execute: async () => ({ orders: 2 }),
12578
+ });
12579
+
12580
+ async function runPath(runName, toolName) {
12581
+ const integration = agentInspect({
12582
+ traceDir: TRACE_DIR,
12583
+ runName,
12584
+ capture: "metadata-only",
12585
+ });
12586
+ let step = 0;
12587
+ try {
12588
+ const result = await generateText({
12589
+ model: new MockLanguageModelV3({
12590
+ provider: "fixture-provider",
12591
+ modelId: "fixture-tools",
12592
+ doGenerate: async () => {
12593
+ step += 1;
12594
+ if (step === 1) {
12595
+ return {
12596
+ content: [
12597
+ {
12598
+ type: "tool-call",
12599
+ toolCallId: \`call-\${toolName}\`,
12600
+ toolName,
12601
+ input: { userId: "u1" },
12602
+ },
12603
+ ],
12604
+ finishReason: { unified: "tool-calls", raw: "tool-calls" },
12605
+ usage,
12606
+ warnings: [],
12607
+ };
12608
+ }
12609
+ return {
12610
+ content: [{ type: "text", text: ANSWER }],
12611
+ finishReason: { unified: "stop", raw: "stop" },
12612
+ usage,
12613
+ warnings: [],
12614
+ };
12615
+ },
12616
+ }),
12617
+ tools: { lookup_orders: lookupOrders, delete_orders: deleteOrders },
12618
+ prompt: "How many orders do I have?",
12619
+ stopWhen: stepCountIs(5),
12620
+ experimental_telemetry: {
12621
+ isEnabled: true,
12622
+ recordInputs: false,
12623
+ recordOutputs: false,
12624
+ integrations: [integration],
12625
+ },
12626
+ });
12627
+ if (result.text !== ANSWER) {
12628
+ throw new Error(\`unexpected answer for \${runName}: \${result.text}\`);
12629
+ }
12630
+ } finally {
12631
+ await integration.flush();
12632
+ await integration.close();
12633
+ }
12634
+ }
12635
+
12636
+ function selectRunIdByName(runName) {
12637
+ const files = readdirSync(TRACE_DIR).filter((f) => f.endsWith(".jsonl"));
12638
+ const matches = [];
12639
+ for (const file of files) {
12640
+ const text = readFileSync(path.join(TRACE_DIR, file), "utf8");
12641
+ if (text.includes(\`"name":"\${runName}"\`) || text.includes(\`"name": "\${runName}"\`)) {
12642
+ matches.push(file.replace(/\\.jsonl$/, ""));
12643
+ }
12644
+ }
12645
+ if (matches.length !== 1) {
12646
+ throw new Error(
12647
+ \`expected exactly one run named \${runName}, found \${matches.length}\`,
12648
+ );
12649
+ }
12650
+ return matches[0];
12651
+ }
12396
12652
 
12397
12653
  async function main() {
12398
- await inspectRun("ai-sdk-demo", async () => {
12399
- await step.tool("mock-generate", async () => ({ text: "ok" }));
12400
- }, { traceDir: ".agent-inspect", silent: true });
12654
+ await runPath(CORRECT_RUN, "lookup_orders");
12655
+ await runPath(WRONG_RUN, "delete_orders");
12656
+ const correctId = selectRunIdByName(CORRECT_RUN);
12657
+ const wrongId = selectRunIdByName(WRONG_RUN);
12401
12658
  console.log("Trace written to .agent-inspect/");
12659
+ console.log(\`Correct path runId: \${correctId}\`);
12660
+ console.log(\`Wrong path runId: \${wrongId}\`);
12661
+ console.log(\`Next: npx agent-inspect check \${correctId} --required-tool lookup_orders\`);
12402
12662
  }
12403
12663
 
12404
12664
  main().catch((error) => {
@@ -13391,7 +13651,7 @@ function isModuleNotFound4(e) {
13391
13651
  }
13392
13652
  async function loadIndexSqlite() {
13393
13653
  try {
13394
- return await import('./src-H22KNWAZ.mjs');
13654
+ return await import('./src-KFOBXFPN.mjs');
13395
13655
  } catch (e) {
13396
13656
  if (isModuleNotFound4(e)) {
13397
13657
  console.error(