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
@@ -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-LKEPABND.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.5";
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) {
@@ -1775,6 +1783,8 @@ var CONNECTION_PROTOCOLS = /* @__PURE__ */ new Set([
1775
1783
  "amqps:"
1776
1784
  ]);
1777
1785
  var CONNECTION_URI = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
1786
+ var CONNECTION_USERINFO = /^(postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/([^@/?#]+)@(.+)$/i;
1787
+ var CONNECTION_HAS_USERINFO = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^@/?#]+@/i;
1778
1788
  var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1779
1789
  var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
1780
1790
  var NUMERIC_SEGMENT = /^\d{5,}$/;
@@ -1798,6 +1808,28 @@ function parseRedactableUrl(value) {
1798
1808
  return void 0;
1799
1809
  }
1800
1810
  }
1811
+ function redactConnectionUserinfoFallback(value, replacement) {
1812
+ const trimmed = value.trim();
1813
+ const match = CONNECTION_USERINFO.exec(trimmed);
1814
+ if (match) {
1815
+ const [, scheme, , rest] = match;
1816
+ if (scheme && rest && rest.length > 0) {
1817
+ return {
1818
+ value: `${scheme}://${rest}`,
1819
+ credentialRedacted: true,
1820
+ identifiersMasked: false
1821
+ };
1822
+ }
1823
+ }
1824
+ if (looksLikeConnectionUri(trimmed) && CONNECTION_HAS_USERINFO.test(trimmed)) {
1825
+ return {
1826
+ value: replacement,
1827
+ credentialRedacted: true,
1828
+ identifiersMasked: false
1829
+ };
1830
+ }
1831
+ return void 0;
1832
+ }
1801
1833
  function isHttpProtocol(protocol) {
1802
1834
  return protocol === "http:" || protocol === "https:";
1803
1835
  }
@@ -1839,9 +1871,11 @@ function redactUrlString(value, options) {
1839
1871
  identifiersMasked: false
1840
1872
  };
1841
1873
  if (!looksLikeUrlForRedaction(value)) return unchanged;
1842
- const url = parseRedactableUrl(value);
1843
- if (!url) return unchanged;
1844
1874
  const replacement = options.replacement ?? "[REDACTED]";
1875
+ const url = parseRedactableUrl(value);
1876
+ if (!url) {
1877
+ return redactConnectionUserinfoFallback(value, replacement) ?? unchanged;
1878
+ }
1845
1879
  const idPlaceholder = options.idPlaceholder ?? "[id]";
1846
1880
  const http = isHttpProtocol(url.protocol);
1847
1881
  let credentialRedacted = url.username.length > 0 || url.password.length > 0;
@@ -2530,6 +2564,10 @@ function exportOpenInference(tree, options) {
2530
2564
  warnings
2531
2565
  };
2532
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;
2533
2571
  function hexFrom2(seed, byteLen) {
2534
2572
  return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
2535
2573
  }
@@ -2542,7 +2580,19 @@ function stringAttr(key, value) {
2542
2580
  return { key, value: { stringValue: value } };
2543
2581
  }
2544
2582
  function intAttr(key, value) {
2545
- 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);
2546
2596
  }
2547
2597
  function genAiOperationName(kind) {
2548
2598
  switch (kind) {
@@ -2556,10 +2606,27 @@ function genAiOperationName(kind) {
2556
2606
  return void 0;
2557
2607
  }
2558
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
+ }
2559
2625
  function exportOtlpJson(tree, options) {
2560
2626
  const warnings = [
2561
2627
  "OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
2562
- "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)."
2563
2630
  ];
2564
2631
  const traceId = hexFrom2(`trace:${tree.runId}`, 16);
2565
2632
  const includeAttributes = options?.includeAttributes ?? false;
@@ -2571,11 +2638,7 @@ function exportOtlpJson(tree, options) {
2571
2638
  const ev = n.event;
2572
2639
  const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
2573
2640
  const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
2574
- const startNs = String(unixNano2(ev.timestamp));
2575
- let endNs;
2576
- if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
2577
- endNs = String(unixNano2(ev.timestamp) + unixNano2(ev.durationMs));
2578
- }
2641
+ const { startNs, endNs } = resolveSpanTimes(ev);
2579
2642
  const attrs = [
2580
2643
  stringAttr("agent_inspect.kind", ev.kind),
2581
2644
  stringAttr("agent_inspect.confidence", ev.confidence),
@@ -2605,29 +2668,28 @@ function exportOtlpJson(tree, options) {
2605
2668
  if (includeAttributes && meta && typeof meta === "object") {
2606
2669
  for (const [k, v] of Object.entries(meta)) {
2607
2670
  if (k === "tokens" || k === "model") continue;
2608
- if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
2609
- attrs.push(
2610
- stringAttr(
2611
- `agent_inspect.preview.${k}`,
2612
- typeof v === "string" ? v.slice(0, maxLen) : String(v)
2613
- )
2614
- );
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));
2615
2677
  }
2616
2678
  }
2617
2679
  }
2618
- let statusCode = "STATUS_CODE_UNSET";
2680
+ let statusCode = OTLP_STATUS_CODE_UNSET;
2619
2681
  let statusMessage;
2620
2682
  if (ev.status === "error") {
2621
- statusCode = "STATUS_CODE_ERROR";
2683
+ statusCode = OTLP_STATUS_CODE_ERROR;
2622
2684
  statusMessage = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error").slice(0, maxLen) : "error";
2623
2685
  } else if (ev.status === "ok") {
2624
- statusCode = "STATUS_CODE_OK";
2686
+ statusCode = OTLP_STATUS_CODE_OK;
2625
2687
  }
2626
2688
  const spanJson = {
2627
2689
  traceId,
2628
2690
  spanId,
2629
2691
  name: ev.name,
2630
- kind: "SPAN_KIND_INTERNAL",
2692
+ kind: OTLP_SPAN_KIND_INTERNAL,
2631
2693
  startTimeUnixNano: startNs,
2632
2694
  attributes: attrs,
2633
2695
  status: {
@@ -2669,6 +2731,136 @@ function exportOtlpJson(tree, options) {
2669
2731
 
2670
2732
  // packages/core/src/exporters/validation.ts
2671
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
+ }
2672
2864
  function validateExportContent(format, content) {
2673
2865
  const errors = [];
2674
2866
  const warnings = [EXPERIMENTAL];
@@ -2714,25 +2906,7 @@ function validateExportContent(format, content) {
2714
2906
  return { ok: errors.length === 0, format, errors, warnings };
2715
2907
  }
2716
2908
  if (format === "otlp-json") {
2717
- let parsed;
2718
- try {
2719
- parsed = JSON.parse(content);
2720
- } catch {
2721
- errors.push("OTLP JSON export is not valid JSON");
2722
- return { ok: false, format, errors, warnings };
2723
- }
2724
- if (!parsed || typeof parsed !== "object") {
2725
- errors.push("OTLP JSON export must be an object");
2726
- return { ok: false, format, errors, warnings };
2727
- }
2728
- const o = parsed;
2729
- if (!Array.isArray(o.resourceSpans)) {
2730
- errors.push("OTLP JSON export must include resourceSpans array");
2731
- }
2732
- warnings.push(
2733
- "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation."
2734
- );
2735
- return { ok: errors.length === 0, format, errors, warnings };
2909
+ return validateOtlpJson(content);
2736
2910
  }
2737
2911
  errors.push(`Unsupported export format`);
2738
2912
  return { ok: false, format, errors, warnings };
@@ -3125,7 +3299,7 @@ async function exportCommand(runId, options = {}) {
3125
3299
  const traceDir = resolveTraceDir({ dir: options.dir });
3126
3300
  let events;
3127
3301
  try {
3128
- const result2 = await readRunTraceEvents(id, traceDir);
3302
+ const result2 = await readRunPersistedEvents(id, traceDir);
3129
3303
  events = result2?.events ?? [];
3130
3304
  } catch (e) {
3131
3305
  const msg = e instanceof Error ? e.message : String(e);
@@ -3141,7 +3315,11 @@ Trace directory: ${traceDir}`);
3141
3315
  }
3142
3316
  let tree;
3143
3317
  try {
3144
- 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
+ }
3145
3323
  } catch (e) {
3146
3324
  const msg = e instanceof Error ? e.message : String(e);
3147
3325
  console.error(`[AgentInspect] export failed: ${msg}`);
@@ -3169,7 +3347,9 @@ Trace directory: ${traceDir}`);
3169
3347
  await mkdir(path19.dirname(outPath), { recursive: true });
3170
3348
  await writeFile(outPath, result.content, "utf-8");
3171
3349
  const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
3172
- console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
3350
+ console.error(
3351
+ `Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`
3352
+ );
3173
3353
  if (validation !== void 0 && !validation.ok) {
3174
3354
  console.error("Validation errors:", validation.errors.join("; "));
3175
3355
  }
@@ -3182,7 +3362,9 @@ Trace directory: ${traceDir}`);
3182
3362
  warnings: [...result.warnings, ...validation?.warnings ?? []],
3183
3363
  validation
3184
3364
  };
3185
- if (outPath === void 0) {
3365
+ if (outPath !== void 0) {
3366
+ payload.out = outPath;
3367
+ } else {
3186
3368
  payload.content = result.content;
3187
3369
  }
3188
3370
  console.log(JSON.stringify(payload, null, 2));
@@ -3539,7 +3721,7 @@ async function newestTraceMtimeMs(traceDir) {
3539
3721
  }
3540
3722
  async function loadSessionRuns(traceDir) {
3541
3723
  try {
3542
- const mod = await import('./src-4WYF5RFI.mjs');
3724
+ const mod = await import('./src-KFOBXFPN.mjs');
3543
3725
  const dbPath = mod.resolveIndexDbPath(traceDir);
3544
3726
  const status = mod.indexStatus(dbPath);
3545
3727
  if (!status.healthy) {
@@ -4062,7 +4244,7 @@ function isCredentialSensitiveKey2(key, sensitiveKeys) {
4062
4244
  }
4063
4245
 
4064
4246
  // packages/redact/src/key-value-secret.ts
4065
- 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,})`;
4247
+ 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,})`;
4066
4248
  var KEY_VALUE_SECRET_PATTERN = new RegExp(
4067
4249
  KEY_VALUE_SECRET_PATTERN_SOURCE,
4068
4250
  "i"
@@ -4087,6 +4269,8 @@ var CONNECTION_PROTOCOLS2 = /* @__PURE__ */ new Set([
4087
4269
  "amqps:"
4088
4270
  ]);
4089
4271
  var CONNECTION_URI2 = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
4272
+ var CONNECTION_USERINFO2 = /^(postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/([^@/?#]+)@(.+)$/i;
4273
+ var CONNECTION_HAS_USERINFO2 = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^@/?#]+@/i;
4090
4274
  var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4091
4275
  var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
4092
4276
  var NUMERIC_SEGMENT2 = /^\d{5,}$/;
@@ -4110,6 +4294,28 @@ function parseRedactableUrl2(value) {
4110
4294
  return void 0;
4111
4295
  }
4112
4296
  }
4297
+ function redactConnectionUserinfoFallback2(value, replacement) {
4298
+ const trimmed = value.trim();
4299
+ const match = CONNECTION_USERINFO2.exec(trimmed);
4300
+ if (match) {
4301
+ const [, scheme, , rest] = match;
4302
+ if (scheme && rest && rest.length > 0) {
4303
+ return {
4304
+ value: `${scheme}://${rest}`,
4305
+ credentialRedacted: true,
4306
+ identifiersMasked: false
4307
+ };
4308
+ }
4309
+ }
4310
+ if (looksLikeConnectionUri2(trimmed) && CONNECTION_HAS_USERINFO2.test(trimmed)) {
4311
+ return {
4312
+ value: replacement,
4313
+ credentialRedacted: true,
4314
+ identifiersMasked: false
4315
+ };
4316
+ }
4317
+ return void 0;
4318
+ }
4113
4319
  function isHttpProtocol2(protocol) {
4114
4320
  return protocol === "http:" || protocol === "https:";
4115
4321
  }
@@ -4151,9 +4357,11 @@ function redactUrlString2(value, options) {
4151
4357
  identifiersMasked: false
4152
4358
  };
4153
4359
  if (!looksLikeUrlForRedaction2(value)) return unchanged;
4154
- const url = parseRedactableUrl2(value);
4155
- if (!url) return unchanged;
4156
4360
  const replacement = options.replacement ?? "[REDACTED]";
4361
+ const url = parseRedactableUrl2(value);
4362
+ if (!url) {
4363
+ return redactConnectionUserinfoFallback2(value, replacement) ?? unchanged;
4364
+ }
4157
4365
  const idPlaceholder = options.idPlaceholder ?? "[id]";
4158
4366
  const http = isHttpProtocol2(url.protocol);
4159
4367
  let credentialRedacted = url.username.length > 0 || url.password.length > 0;
@@ -4246,7 +4454,7 @@ var STRICT_PROFILE_EXTRA_KEYS = [
4246
4454
  "retrieval",
4247
4455
  "query"
4248
4456
  ];
4249
- function isRecord5(value) {
4457
+ function isRecord6(value) {
4250
4458
  return typeof value === "object" && value !== null && !Array.isArray(value);
4251
4459
  }
4252
4460
  function toKey(key) {
@@ -4675,7 +4883,7 @@ var Redactor2 = class {
4675
4883
  });
4676
4884
  return out;
4677
4885
  }
4678
- if (isRecord5(value)) {
4886
+ if (isRecord6(value)) {
4679
4887
  if (state.seen.has(value)) return state.seen.get(value);
4680
4888
  const out = {};
4681
4889
  state.seen.set(value, out);
@@ -4710,7 +4918,7 @@ var REDACTION_POLICY_LIMITS = {
4710
4918
  };
4711
4919
  var ALLOWED_TOP_LEVEL = /* @__PURE__ */ new Set(["version", "extraKeys", "patterns"]);
4712
4920
  var ALLOWED_PATTERN_FIELDS = /* @__PURE__ */ new Set(["id", "type", "value", "severity"]);
4713
- function isRecord6(value) {
4921
+ function isRecord7(value) {
4714
4922
  return value !== null && typeof value === "object" && !Array.isArray(value);
4715
4923
  }
4716
4924
  function rejectRemotePolicyPath(policyPath) {
@@ -4750,7 +4958,7 @@ function rejectUnknownFields(record, allowed, label) {
4750
4958
  }
4751
4959
  function compilePatternDetector(input3, index) {
4752
4960
  const label = `patterns[${index}]`;
4753
- if (!isRecord6(input3)) throw new Error(`${label} must be an object.`);
4961
+ if (!isRecord7(input3)) throw new Error(`${label} must be an object.`);
4754
4962
  const typeEarly = input3.type;
4755
4963
  if (typeEarly === "typed") {
4756
4964
  throw new Error(
@@ -4805,7 +5013,7 @@ function compilePatternDetector(input3, index) {
4805
5013
  };
4806
5014
  }
4807
5015
  function compileRedactionPolicy(raw, policyPath) {
4808
- if (!isRecord6(raw)) {
5016
+ if (!isRecord7(raw)) {
4809
5017
  throw new Error(`Redaction policy must be a JSON object (${policyPath}).`);
4810
5018
  }
4811
5019
  rejectUnknownFields(raw, ALLOWED_TOP_LEVEL, "Redaction policy");
@@ -5827,7 +6035,7 @@ async function openCommand(input3, options = {}, stdin = process.stdin) {
5827
6035
  }
5828
6036
  }
5829
6037
  }
5830
- function isRecord7(value) {
6038
+ function isRecord8(value) {
5831
6039
  return typeof value === "object" && value !== null && !Array.isArray(value);
5832
6040
  }
5833
6041
  function parseTarget(value) {
@@ -5836,7 +6044,7 @@ function parseTarget(value) {
5836
6044
  throw new Error('Unsupported migration target. Use "--to 1.0".');
5837
6045
  }
5838
6046
  function formatOf(value) {
5839
- if (!isRecord7(value)) return "unknown";
6047
+ if (!isRecord8(value)) return "unknown";
5840
6048
  if (value.schemaVersion === "0.1") return "0.1";
5841
6049
  if (value.schemaVersion === "0.2") return "0.2";
5842
6050
  if (value.schemaVersion === "1.0") return "1.0";
@@ -12337,16 +12545,120 @@ function demoTemplate(framework) {
12337
12545
  switch (framework) {
12338
12546
  case "ai-sdk":
12339
12547
  return `/**
12340
- * AI SDK starter \u2014 metadata-only telemetry (no real model calls in this demo).
12341
- * 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
12342
12550
  */
12343
- 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
+ }
12344
12652
 
12345
12653
  async function main() {
12346
- await inspectRun("ai-sdk-demo", async () => {
12347
- await step.tool("mock-generate", async () => ({ text: "ok" }));
12348
- }, { 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);
12349
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\`);
12350
12662
  }
12351
12663
 
12352
12664
  main().catch((error) => {
@@ -13339,7 +13651,7 @@ function isModuleNotFound4(e) {
13339
13651
  }
13340
13652
  async function loadIndexSqlite() {
13341
13653
  try {
13342
- return await import('./src-4WYF5RFI.mjs');
13654
+ return await import('./src-KFOBXFPN.mjs');
13343
13655
  } catch (e) {
13344
13656
  if (isModuleNotFound4(e)) {
13345
13657
  console.error(