@agent-inspect/viewer 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.
package/dist/index.cjs CHANGED
@@ -2278,7 +2278,7 @@ function formatProgrammaticDiagnostic(code, detail) {
2278
2278
 
2279
2279
  // packages/core/src/safety/sensitive-key.ts
2280
2280
  function normalizeSensitiveKey(value) {
2281
- return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
2281
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
2282
2282
  }
2283
2283
  new Set(
2284
2284
  [
@@ -2629,10 +2629,11 @@ function emptySummary() {
2629
2629
  passed: 0,
2630
2630
  failed: 0,
2631
2631
  warnings: 0,
2632
- errors: 0
2632
+ errors: 0,
2633
+ rulesEvaluated: 0
2633
2634
  };
2634
2635
  }
2635
- function errorResult(input, diagnostics, selectedRun) {
2636
+ function errorResult(input, diagnostics, selectedRun, ruleExecutions = []) {
2636
2637
  return {
2637
2638
  ok: false,
2638
2639
  status: "error",
@@ -2640,10 +2641,12 @@ function errorResult(input, diagnostics, selectedRun) {
2640
2641
  ...selectedRun ? { runId: selectedRun.runId } : {},
2641
2642
  summary: {
2642
2643
  ...emptySummary(),
2643
- errors: diagnostics.filter((item) => item.severity === "error").length
2644
+ errors: diagnostics.filter((item) => item.severity === "error").length,
2645
+ rulesEvaluated: ruleExecutions.length
2644
2646
  },
2645
2647
  findings: [],
2646
- diagnostics: [...diagnostics]
2648
+ diagnostics: [...diagnostics],
2649
+ ruleExecutions: [...ruleExecutions]
2647
2650
  };
2648
2651
  }
2649
2652
  function flattenNodes(nodes) {
@@ -2799,7 +2802,7 @@ function normalizeFinding(rule, finding) {
2799
2802
  ...finding.action !== void 0 ? { action: finding.action } : {}
2800
2803
  };
2801
2804
  }
2802
- function summarize(findings, diagnostics) {
2805
+ function summarize(findings, diagnostics, rulesEvaluated) {
2803
2806
  return {
2804
2807
  passed: findings.filter((finding) => finding.status === "pass").length,
2805
2808
  failed: findings.filter(
@@ -2808,9 +2811,21 @@ function summarize(findings, diagnostics) {
2808
2811
  warnings: findings.filter(
2809
2812
  (finding) => finding.status === "warning" || finding.severity === "warning"
2810
2813
  ).length,
2811
- errors: diagnostics.filter((item) => item.severity === "error").length
2814
+ errors: diagnostics.filter((item) => item.severity === "error").length,
2815
+ rulesEvaluated
2812
2816
  };
2813
2817
  }
2818
+ function classifyRuleExecution(findings, threw) {
2819
+ if (findings.some((finding) => finding.status === "fail" && finding.severity === "error")) {
2820
+ return "fail";
2821
+ }
2822
+ if (findings.some(
2823
+ (finding) => finding.status === "warning" || finding.severity === "warning"
2824
+ )) {
2825
+ return "warning";
2826
+ }
2827
+ return "pass";
2828
+ }
2814
2829
  function stringAttr(event, keys) {
2815
2830
  for (const key of keys) {
2816
2831
  const value = event.attributes?.[key];
@@ -2887,9 +2902,12 @@ function llmFinishReason(event) {
2887
2902
  }
2888
2903
  function finishedEvents(context, kind) {
2889
2904
  return semanticEvents(context).filter(
2890
- (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
2905
+ (event) => (event.kind === kind) && event.status !== "running"
2891
2906
  );
2892
2907
  }
2908
+ function toolInvocationEvents(context) {
2909
+ return semanticEvents(context).filter((event) => event.kind === "TOOL");
2910
+ }
2893
2911
  function createRunStatusRule(options = {}) {
2894
2912
  const expected = options.expected ?? "ok";
2895
2913
  const allowIncomplete = options.allowIncomplete === true;
@@ -2955,7 +2973,7 @@ function createToolUsageRule(options) {
2955
2973
  category: "tool",
2956
2974
  defaultSeverity: "error",
2957
2975
  evaluate(context) {
2958
- const tools = finishedEvents(context, "TOOL");
2976
+ const tools = toolInvocationEvents(context);
2959
2977
  const names = tools.map(toolName);
2960
2978
  const nameSet = new Set(names);
2961
2979
  const findings = [];
@@ -3071,12 +3089,24 @@ function createLlmUsageRule(options) {
3071
3089
  }
3072
3090
  function createObservedOutcomeRule(options = {}) {
3073
3091
  const failOn = options.failOn ?? ["failed"];
3092
+ const requireAny = options.requireAny === true;
3074
3093
  return {
3075
3094
  id: "outcome.status",
3076
3095
  category: "run",
3077
3096
  defaultSeverity: "error",
3078
3097
  evaluate(context) {
3079
3098
  const outcomes = extractOutcomesFromPersistedEvents(context.events);
3099
+ if (requireAny && outcomes.length === 0) {
3100
+ return [
3101
+ failFinding(
3102
+ "outcome.status",
3103
+ "Expected at least one observed outcome.",
3104
+ runEvidence(context.selectedRun),
3105
+ { requireAny: true, expected: "at least one observed outcome" },
3106
+ { code: "outcome.missing", actual: 0 }
3107
+ )
3108
+ ];
3109
+ }
3080
3110
  const matching = outcomesMatchingStatus(outcomes, failOn);
3081
3111
  if (matching.length === 0) return [];
3082
3112
  return [
@@ -3112,6 +3142,18 @@ function runTraceChecks(input, options = {}) {
3112
3142
  if (rules.diagnostics.length > 0) {
3113
3143
  return errorResult(input, rules.diagnostics, selected.run);
3114
3144
  }
3145
+ if (rules.rules.length === 0) {
3146
+ return errorResult(
3147
+ input,
3148
+ [
3149
+ diagnostic3(
3150
+ "AI_CHECK_NO_RULES_EVALUATED",
3151
+ "No trace check rules were evaluated. Configure at least one rule, contract, or CLI check option."
3152
+ )
3153
+ ],
3154
+ selected.run
3155
+ );
3156
+ }
3115
3157
  const facts = buildFacts(input, selected.run);
3116
3158
  const context = {
3117
3159
  ...facts,
@@ -3120,22 +3162,38 @@ function runTraceChecks(input, options = {}) {
3120
3162
  };
3121
3163
  const diagnostics = [];
3122
3164
  const findings = [];
3165
+ const ruleExecutions = [];
3123
3166
  for (const rule of rules.rules) {
3124
3167
  try {
3125
- findings.push(...rule.evaluate(context).map((finding) => normalizeFinding(rule, finding)));
3168
+ const ruleFindings = rule.evaluate(context).map((finding) => normalizeFinding(rule, finding));
3169
+ findings.push(...ruleFindings);
3170
+ ruleExecutions.push({
3171
+ ruleId: rule.id,
3172
+ category: rule.category,
3173
+ status: classifyRuleExecution(ruleFindings, false),
3174
+ findingCount: ruleFindings.length,
3175
+ ...selected.run ? { runId: selected.run.runId } : {}
3176
+ });
3126
3177
  } catch (error) {
3127
3178
  const message = error instanceof Error ? error.message : String(error);
3128
3179
  diagnostics.push(
3129
3180
  diagnostic3("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
3130
3181
  );
3182
+ ruleExecutions.push({
3183
+ ruleId: rule.id,
3184
+ category: rule.category,
3185
+ status: "error",
3186
+ findingCount: 0,
3187
+ ...selected.run ? { runId: selected.run.runId } : {}
3188
+ });
3131
3189
  }
3132
3190
  }
3133
3191
  if (diagnostics.length > 0) {
3134
- return errorResult(input, diagnostics, selected.run);
3192
+ return errorResult(input, diagnostics, selected.run, ruleExecutions);
3135
3193
  }
3136
3194
  const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
3137
3195
  const sortedFindings = findings.sort(compareFindings(eventById));
3138
- const summary = summarize(sortedFindings, diagnostics);
3196
+ const summary = summarize(sortedFindings, diagnostics, ruleExecutions.length);
3139
3197
  const status = summary.failed > 0 ? "fail" : "pass";
3140
3198
  return {
3141
3199
  ok: status === "pass",
@@ -3144,7 +3202,8 @@ function runTraceChecks(input, options = {}) {
3144
3202
  ...selected.run ? { runId: selected.run.runId } : {},
3145
3203
  summary,
3146
3204
  findings: sortedFindings,
3147
- diagnostics
3205
+ diagnostics,
3206
+ ruleExecutions
3148
3207
  };
3149
3208
  }
3150
3209
 
@@ -5471,8 +5530,7 @@ async function runSuiteCase(suiteCase, config, options) {
5471
5530
  status: "pass",
5472
5531
  format: read.format,
5473
5532
  findings: [],
5474
- diagnostics: []
5475
- };
5533
+ diagnostics: []};
5476
5534
  const observationResult = validateExpectedObservations(suiteCase, read);
5477
5535
  const diagnostics = [
5478
5536
  ...checkResult.diagnostics.map(