agent-inspect 6.17.3 → 6.17.5

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 (40) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/docs/ADAPTER-CONFORMANCE.md +24 -0
  4. package/docs/ADAPTERS.md +3 -3
  5. package/docs/API.md +13 -7
  6. package/docs/CI-ARTIFACTS.md +2 -1
  7. package/docs/CLI.md +2 -0
  8. package/docs/LIMITATIONS.md +1 -0
  9. package/docs/OPENAI-AGENTS-LOCAL.md +14 -0
  10. package/docs/SAFE-TRACE-SHARING.md +1 -0
  11. package/docs/SELF-HOSTING.md +2 -0
  12. package/docs/STANDARDS.md +15 -0
  13. package/docs/SUPPORT-LEVELS.md +23 -0
  14. package/docs/TRACE-CONTRACTS.md +90 -0
  15. package/package.json +7 -3
  16. package/packages/cli/dist/{chunk-5GGYDIZD.mjs → chunk-GOHYSE7W.mjs} +93 -19
  17. package/packages/cli/dist/chunk-GOHYSE7W.mjs.map +1 -0
  18. package/packages/cli/dist/index.cjs +505 -88
  19. package/packages/cli/dist/index.cjs.map +1 -1
  20. package/packages/cli/dist/index.mjs +417 -74
  21. package/packages/cli/dist/index.mjs.map +1 -1
  22. package/packages/cli/dist/{src-IDMCWKRH.mjs → src-OYGG7QAZ.mjs} +3 -3
  23. package/packages/cli/dist/{src-IDMCWKRH.mjs.map → src-OYGG7QAZ.mjs.map} +1 -1
  24. package/packages/core/dist/advanced.cjs +86 -17
  25. package/packages/core/dist/advanced.cjs.map +1 -1
  26. package/packages/core/dist/advanced.d.cts +1 -1
  27. package/packages/core/dist/advanced.d.ts +1 -1
  28. package/packages/core/dist/advanced.mjs +15 -5
  29. package/packages/core/dist/advanced.mjs.map +1 -1
  30. package/packages/core/dist/checks.cjs +126 -28
  31. package/packages/core/dist/checks.cjs.map +1 -1
  32. package/packages/core/dist/checks.d.cts +28 -2
  33. package/packages/core/dist/checks.d.ts +28 -2
  34. package/packages/core/dist/checks.mjs +1 -1
  35. package/packages/core/dist/{chunk-UFP54T7F.mjs → chunk-DIZPIPY2.mjs} +128 -30
  36. package/packages/core/dist/chunk-DIZPIPY2.mjs.map +1 -0
  37. package/packages/core/dist/{index-Xk9X-yjY.d.cts → index-DWu54Y28.d.cts} +107 -122
  38. package/packages/core/dist/{index-BsCcOKxy.d.ts → index-DlwbVqEs.d.ts} +107 -122
  39. package/packages/cli/dist/chunk-5GGYDIZD.mjs.map +0 -1
  40. package/packages/core/dist/chunk-UFP54T7F.mjs.map +0 -1
@@ -118,8 +118,11 @@ function outcomesMatchingStatus(outcomes, statuses) {
118
118
  }
119
119
 
120
120
  // packages/core/src/safety/sensitive-key.ts
121
+ function keyHasExplicitSeparator(value) {
122
+ return /[_\-.]/.test(value);
123
+ }
121
124
  function normalizeSensitiveKey(value) {
122
- return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
125
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
123
126
  }
124
127
  var NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
125
128
  [
@@ -171,6 +174,7 @@ function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSIT
171
174
  const normalized = normalizeSensitiveKey(key);
172
175
  if (!normalized) return false;
173
176
  if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
177
+ const allowPrefixCompound = keyHasExplicitSeparator(key);
174
178
  for (const sensitive of sensitiveKeys) {
175
179
  const s = normalizeSensitiveKey(sensitive);
176
180
  if (!s) continue;
@@ -179,7 +183,8 @@ function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSIT
179
183
  continue;
180
184
  }
181
185
  if (normalized === s) return true;
182
- if (normalized.endsWith(`_${s}`) || normalized.startsWith(`${s}_`)) return true;
186
+ if (normalized.endsWith(`_${s}`)) return true;
187
+ if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
183
188
  }
184
189
  return false;
185
190
  }
@@ -668,9 +673,13 @@ function contractToRules(contract) {
668
673
  rules.push(createStructureIncompleteRule({ requireEndedAtForStarted: true }));
669
674
  }
670
675
  if (contract.tools) {
676
+ const order = contract.tools.requiredOrder ?? [];
671
677
  const required = [
672
- ...contract.tools.required ?? [],
673
- ...contract.tools.requiredTools ?? []
678
+ .../* @__PURE__ */ new Set([
679
+ ...contract.tools.required ?? [],
680
+ ...contract.tools.requiredTools ?? [],
681
+ ...order
682
+ ])
674
683
  ];
675
684
  const forbidden = [
676
685
  ...contract.tools.forbidden ?? [],
@@ -684,9 +693,14 @@ function contractToRules(contract) {
684
693
  ...contract.tools.maxCalls !== void 0 ? { maxCount: contract.tools.maxCalls } : {}
685
694
  })
686
695
  );
687
- const order = contract.tools.requiredOrder ?? [];
688
696
  for (let i = 0; i < order.length - 1; i += 1) {
689
- rules.push(createToolOrderingRule({ before: order[i], after: order[i + 1] }));
697
+ rules.push(
698
+ createToolOrderingRule({
699
+ before: order[i],
700
+ after: order[i + 1],
701
+ id: `contract.tool.order.${i}`
702
+ })
703
+ );
690
704
  }
691
705
  }
692
706
  if (contract.llm) {
@@ -846,10 +860,11 @@ function emptySummary() {
846
860
  passed: 0,
847
861
  failed: 0,
848
862
  warnings: 0,
849
- errors: 0
863
+ errors: 0,
864
+ rulesEvaluated: 0
850
865
  };
851
866
  }
852
- function errorResult(input, diagnostics, selectedRun) {
867
+ function errorResult(input, diagnostics, selectedRun, ruleExecutions = []) {
853
868
  return {
854
869
  ok: false,
855
870
  status: "error",
@@ -857,10 +872,12 @@ function errorResult(input, diagnostics, selectedRun) {
857
872
  ...selectedRun ? { runId: selectedRun.runId } : {},
858
873
  summary: {
859
874
  ...emptySummary(),
860
- errors: diagnostics.filter((item) => item.severity === "error").length
875
+ errors: diagnostics.filter((item) => item.severity === "error").length,
876
+ rulesEvaluated: ruleExecutions.length
861
877
  },
862
878
  findings: [],
863
- diagnostics: [...diagnostics]
879
+ diagnostics: [...diagnostics],
880
+ ruleExecutions: [...ruleExecutions]
864
881
  };
865
882
  }
866
883
  function flattenNodes(nodes) {
@@ -1016,7 +1033,7 @@ function normalizeFinding(rule, finding) {
1016
1033
  ...finding.action !== void 0 ? { action: finding.action } : {}
1017
1034
  };
1018
1035
  }
1019
- function summarize(findings, diagnostics) {
1036
+ function summarize(findings, diagnostics, rulesEvaluated) {
1020
1037
  return {
1021
1038
  passed: findings.filter((finding) => finding.status === "pass").length,
1022
1039
  failed: findings.filter(
@@ -1025,9 +1042,21 @@ function summarize(findings, diagnostics) {
1025
1042
  warnings: findings.filter(
1026
1043
  (finding) => finding.status === "warning" || finding.severity === "warning"
1027
1044
  ).length,
1028
- errors: diagnostics.filter((item) => item.severity === "error").length
1045
+ errors: diagnostics.filter((item) => item.severity === "error").length,
1046
+ rulesEvaluated
1029
1047
  };
1030
1048
  }
1049
+ function classifyRuleExecution(findings, threw) {
1050
+ if (findings.some((finding) => finding.status === "fail" && finding.severity === "error")) {
1051
+ return "fail";
1052
+ }
1053
+ if (findings.some(
1054
+ (finding) => finding.status === "warning" || finding.severity === "warning"
1055
+ )) {
1056
+ return "warning";
1057
+ }
1058
+ return "pass";
1059
+ }
1031
1060
  function stringAttr(event, keys) {
1032
1061
  for (const key of keys) {
1033
1062
  const value = event.attributes?.[key];
@@ -1124,6 +1153,9 @@ function finishedEvents(context, kind) {
1124
1153
  (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
1125
1154
  );
1126
1155
  }
1156
+ function toolInvocationEvents(context) {
1157
+ return semanticEvents(context).filter((event) => event.kind === "TOOL");
1158
+ }
1127
1159
  function firstIndexByName(events) {
1128
1160
  const index = /* @__PURE__ */ new Map();
1129
1161
  for (const [position, event] of events.entries()) {
@@ -1571,7 +1603,7 @@ function createToolUsageRule(options) {
1571
1603
  category: "tool",
1572
1604
  defaultSeverity: "error",
1573
1605
  evaluate(context) {
1574
- const tools = finishedEvents(context, "TOOL");
1606
+ const tools = toolInvocationEvents(context);
1575
1607
  const names = tools.map(toolName);
1576
1608
  const nameSet = new Set(names);
1577
1609
  const findings = [];
@@ -1612,8 +1644,9 @@ function createToolUsageRule(options) {
1612
1644
  };
1613
1645
  }
1614
1646
  function createToolOrderingRule(options) {
1647
+ const ruleId = options.id ?? "tool.order";
1615
1648
  return {
1616
- id: "tool.order",
1649
+ id: ruleId,
1617
1650
  category: "tool",
1618
1651
  defaultSeverity: "error",
1619
1652
  evaluate(context) {
@@ -1621,18 +1654,42 @@ function createToolOrderingRule(options) {
1621
1654
  const index = firstIndexByName(tools);
1622
1655
  const beforeIndex = index.get(options.before);
1623
1656
  const afterIndex = index.get(options.after);
1624
- if (beforeIndex === void 0 || afterIndex === void 0 || beforeIndex < afterIndex) {
1657
+ if (beforeIndex === void 0 || afterIndex === void 0) {
1625
1658
  return [];
1626
1659
  }
1627
- return [
1628
- failFinding(
1629
- "tool.order",
1630
- `Tool ${options.before} must appear before ${options.after}.`,
1631
- [eventEvidence(tools[beforeIndex]), eventEvidence(tools[afterIndex])],
1632
- { before: options.before, after: options.after },
1633
- tools.map(toolName)
1634
- )
1635
- ];
1660
+ if (beforeIndex >= afterIndex) {
1661
+ return [
1662
+ failFinding(
1663
+ ruleId,
1664
+ `Tool ${options.before} must appear before ${options.after}.`,
1665
+ [eventEvidence(tools[beforeIndex]), eventEvidence(tools[afterIndex])],
1666
+ { before: options.before, after: options.after },
1667
+ tools.map(toolName)
1668
+ )
1669
+ ];
1670
+ }
1671
+ const beforeEvent = tools[beforeIndex];
1672
+ const afterEvent = tools[afterIndex];
1673
+ const beforeEnd = eventEndMs(beforeEvent);
1674
+ const afterStart = eventStartMs(afterEvent);
1675
+ if (beforeEnd !== void 0 && afterStart !== void 0 && beforeEnd > afterStart) {
1676
+ return [
1677
+ {
1678
+ ruleId,
1679
+ severity: "warning",
1680
+ status: "warning",
1681
+ message: `Tool order ${options.before} before ${options.after} satisfied start/encounter order, but intervals overlap so causal completion before the next tool was not established.`,
1682
+ expected: { before: options.before, after: options.after, mode: "first-occurrence" },
1683
+ actual: {
1684
+ code: "tool.order.overlap",
1685
+ beforeEndedAt: beforeEvent.endedAt,
1686
+ afterStartedAt: afterEvent.startedAt ?? afterEvent.timestamp
1687
+ },
1688
+ evidence: [eventEvidence(beforeEvent), eventEvidence(afterEvent)]
1689
+ }
1690
+ ];
1691
+ }
1692
+ return [];
1636
1693
  }
1637
1694
  };
1638
1695
  }
@@ -2429,12 +2486,24 @@ function createBaselineRegressionRule(options) {
2429
2486
  }
2430
2487
  function createObservedOutcomeRule(options = {}) {
2431
2488
  const failOn = options.failOn ?? ["failed"];
2489
+ const requireAny = options.requireAny === true;
2432
2490
  return {
2433
2491
  id: "outcome.status",
2434
2492
  category: "run",
2435
2493
  defaultSeverity: "error",
2436
2494
  evaluate(context) {
2437
2495
  const outcomes = extractOutcomesFromPersistedEvents(context.events);
2496
+ if (requireAny && outcomes.length === 0) {
2497
+ return [
2498
+ failFinding(
2499
+ "outcome.status",
2500
+ "Expected at least one observed outcome.",
2501
+ runEvidence(context.selectedRun),
2502
+ { requireAny: true, expected: "at least one observed outcome" },
2503
+ { code: "outcome.missing", actual: 0 }
2504
+ )
2505
+ ];
2506
+ }
2438
2507
  const matching = outcomesMatchingStatus(outcomes, failOn);
2439
2508
  if (matching.length === 0) return [];
2440
2509
  return [
@@ -2470,6 +2539,18 @@ function runTraceChecks(input, options = {}) {
2470
2539
  if (rules.diagnostics.length > 0) {
2471
2540
  return errorResult(input, rules.diagnostics, selected.run);
2472
2541
  }
2542
+ if (rules.rules.length === 0) {
2543
+ return errorResult(
2544
+ input,
2545
+ [
2546
+ diagnostic(
2547
+ "AI_CHECK_NO_RULES_EVALUATED",
2548
+ "No trace check rules were evaluated. Configure at least one rule, contract, or CLI check option."
2549
+ )
2550
+ ],
2551
+ selected.run
2552
+ );
2553
+ }
2473
2554
  const facts = buildFacts(input, selected.run);
2474
2555
  const context = {
2475
2556
  ...facts,
@@ -2478,22 +2559,38 @@ function runTraceChecks(input, options = {}) {
2478
2559
  };
2479
2560
  const diagnostics = [];
2480
2561
  const findings = [];
2562
+ const ruleExecutions = [];
2481
2563
  for (const rule of rules.rules) {
2482
2564
  try {
2483
- findings.push(...rule.evaluate(context).map((finding) => normalizeFinding(rule, finding)));
2565
+ const ruleFindings = rule.evaluate(context).map((finding) => normalizeFinding(rule, finding));
2566
+ findings.push(...ruleFindings);
2567
+ ruleExecutions.push({
2568
+ ruleId: rule.id,
2569
+ category: rule.category,
2570
+ status: classifyRuleExecution(ruleFindings, false),
2571
+ findingCount: ruleFindings.length,
2572
+ ...selected.run ? { runId: selected.run.runId } : {}
2573
+ });
2484
2574
  } catch (error) {
2485
2575
  const message = error instanceof Error ? error.message : String(error);
2486
2576
  diagnostics.push(
2487
2577
  diagnostic("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
2488
2578
  );
2579
+ ruleExecutions.push({
2580
+ ruleId: rule.id,
2581
+ category: rule.category,
2582
+ status: "error",
2583
+ findingCount: 0,
2584
+ ...selected.run ? { runId: selected.run.runId } : {}
2585
+ });
2489
2586
  }
2490
2587
  }
2491
2588
  if (diagnostics.length > 0) {
2492
- return errorResult(input, diagnostics, selected.run);
2589
+ return errorResult(input, diagnostics, selected.run, ruleExecutions);
2493
2590
  }
2494
2591
  const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
2495
2592
  const sortedFindings = findings.sort(compareFindings(eventById));
2496
- const summary = summarize(sortedFindings, diagnostics);
2593
+ const summary = summarize(sortedFindings, diagnostics, ruleExecutions.length);
2497
2594
  const status = summary.failed > 0 ? "fail" : "pass";
2498
2595
  return {
2499
2596
  ok: status === "pass",
@@ -2502,7 +2599,8 @@ function runTraceChecks(input, options = {}) {
2502
2599
  ...selected.run ? { runId: selected.run.runId } : {},
2503
2600
  summary,
2504
2601
  findings: sortedFindings,
2505
- diagnostics
2602
+ diagnostics,
2603
+ ruleExecutions
2506
2604
  };
2507
2605
  }
2508
2606