@agent-inspect/viewer 6.17.4 → 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.mjs CHANGED
@@ -2622,10 +2622,11 @@ function emptySummary() {
2622
2622
  passed: 0,
2623
2623
  failed: 0,
2624
2624
  warnings: 0,
2625
- errors: 0
2625
+ errors: 0,
2626
+ rulesEvaluated: 0
2626
2627
  };
2627
2628
  }
2628
- function errorResult(input, diagnostics, selectedRun) {
2629
+ function errorResult(input, diagnostics, selectedRun, ruleExecutions = []) {
2629
2630
  return {
2630
2631
  ok: false,
2631
2632
  status: "error",
@@ -2633,10 +2634,12 @@ function errorResult(input, diagnostics, selectedRun) {
2633
2634
  ...selectedRun ? { runId: selectedRun.runId } : {},
2634
2635
  summary: {
2635
2636
  ...emptySummary(),
2636
- errors: diagnostics.filter((item) => item.severity === "error").length
2637
+ errors: diagnostics.filter((item) => item.severity === "error").length,
2638
+ rulesEvaluated: ruleExecutions.length
2637
2639
  },
2638
2640
  findings: [],
2639
- diagnostics: [...diagnostics]
2641
+ diagnostics: [...diagnostics],
2642
+ ruleExecutions: [...ruleExecutions]
2640
2643
  };
2641
2644
  }
2642
2645
  function flattenNodes(nodes) {
@@ -2792,7 +2795,7 @@ function normalizeFinding(rule, finding) {
2792
2795
  ...finding.action !== void 0 ? { action: finding.action } : {}
2793
2796
  };
2794
2797
  }
2795
- function summarize(findings, diagnostics) {
2798
+ function summarize(findings, diagnostics, rulesEvaluated) {
2796
2799
  return {
2797
2800
  passed: findings.filter((finding) => finding.status === "pass").length,
2798
2801
  failed: findings.filter(
@@ -2801,9 +2804,21 @@ function summarize(findings, diagnostics) {
2801
2804
  warnings: findings.filter(
2802
2805
  (finding) => finding.status === "warning" || finding.severity === "warning"
2803
2806
  ).length,
2804
- errors: diagnostics.filter((item) => item.severity === "error").length
2807
+ errors: diagnostics.filter((item) => item.severity === "error").length,
2808
+ rulesEvaluated
2805
2809
  };
2806
2810
  }
2811
+ function classifyRuleExecution(findings, threw) {
2812
+ if (findings.some((finding) => finding.status === "fail" && finding.severity === "error")) {
2813
+ return "fail";
2814
+ }
2815
+ if (findings.some(
2816
+ (finding) => finding.status === "warning" || finding.severity === "warning"
2817
+ )) {
2818
+ return "warning";
2819
+ }
2820
+ return "pass";
2821
+ }
2807
2822
  function stringAttr(event, keys) {
2808
2823
  for (const key of keys) {
2809
2824
  const value = event.attributes?.[key];
@@ -2880,9 +2895,12 @@ function llmFinishReason(event) {
2880
2895
  }
2881
2896
  function finishedEvents(context, kind) {
2882
2897
  return semanticEvents(context).filter(
2883
- (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
2898
+ (event) => (event.kind === kind) && event.status !== "running"
2884
2899
  );
2885
2900
  }
2901
+ function toolInvocationEvents(context) {
2902
+ return semanticEvents(context).filter((event) => event.kind === "TOOL");
2903
+ }
2886
2904
  function createRunStatusRule(options = {}) {
2887
2905
  const expected = options.expected ?? "ok";
2888
2906
  const allowIncomplete = options.allowIncomplete === true;
@@ -2948,7 +2966,7 @@ function createToolUsageRule(options) {
2948
2966
  category: "tool",
2949
2967
  defaultSeverity: "error",
2950
2968
  evaluate(context) {
2951
- const tools = finishedEvents(context, "TOOL");
2969
+ const tools = toolInvocationEvents(context);
2952
2970
  const names = tools.map(toolName);
2953
2971
  const nameSet = new Set(names);
2954
2972
  const findings = [];
@@ -3064,12 +3082,24 @@ function createLlmUsageRule(options) {
3064
3082
  }
3065
3083
  function createObservedOutcomeRule(options = {}) {
3066
3084
  const failOn = options.failOn ?? ["failed"];
3085
+ const requireAny = options.requireAny === true;
3067
3086
  return {
3068
3087
  id: "outcome.status",
3069
3088
  category: "run",
3070
3089
  defaultSeverity: "error",
3071
3090
  evaluate(context) {
3072
3091
  const outcomes = extractOutcomesFromPersistedEvents(context.events);
3092
+ if (requireAny && outcomes.length === 0) {
3093
+ return [
3094
+ failFinding(
3095
+ "outcome.status",
3096
+ "Expected at least one observed outcome.",
3097
+ runEvidence(context.selectedRun),
3098
+ { requireAny: true, expected: "at least one observed outcome" },
3099
+ { code: "outcome.missing", actual: 0 }
3100
+ )
3101
+ ];
3102
+ }
3073
3103
  const matching = outcomesMatchingStatus(outcomes, failOn);
3074
3104
  if (matching.length === 0) return [];
3075
3105
  return [
@@ -3105,6 +3135,18 @@ function runTraceChecks(input, options = {}) {
3105
3135
  if (rules.diagnostics.length > 0) {
3106
3136
  return errorResult(input, rules.diagnostics, selected.run);
3107
3137
  }
3138
+ if (rules.rules.length === 0) {
3139
+ return errorResult(
3140
+ input,
3141
+ [
3142
+ diagnostic3(
3143
+ "AI_CHECK_NO_RULES_EVALUATED",
3144
+ "No trace check rules were evaluated. Configure at least one rule, contract, or CLI check option."
3145
+ )
3146
+ ],
3147
+ selected.run
3148
+ );
3149
+ }
3108
3150
  const facts = buildFacts(input, selected.run);
3109
3151
  const context = {
3110
3152
  ...facts,
@@ -3113,22 +3155,38 @@ function runTraceChecks(input, options = {}) {
3113
3155
  };
3114
3156
  const diagnostics = [];
3115
3157
  const findings = [];
3158
+ const ruleExecutions = [];
3116
3159
  for (const rule of rules.rules) {
3117
3160
  try {
3118
- findings.push(...rule.evaluate(context).map((finding) => normalizeFinding(rule, finding)));
3161
+ const ruleFindings = rule.evaluate(context).map((finding) => normalizeFinding(rule, finding));
3162
+ findings.push(...ruleFindings);
3163
+ ruleExecutions.push({
3164
+ ruleId: rule.id,
3165
+ category: rule.category,
3166
+ status: classifyRuleExecution(ruleFindings, false),
3167
+ findingCount: ruleFindings.length,
3168
+ ...selected.run ? { runId: selected.run.runId } : {}
3169
+ });
3119
3170
  } catch (error) {
3120
3171
  const message = error instanceof Error ? error.message : String(error);
3121
3172
  diagnostics.push(
3122
3173
  diagnostic3("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
3123
3174
  );
3175
+ ruleExecutions.push({
3176
+ ruleId: rule.id,
3177
+ category: rule.category,
3178
+ status: "error",
3179
+ findingCount: 0,
3180
+ ...selected.run ? { runId: selected.run.runId } : {}
3181
+ });
3124
3182
  }
3125
3183
  }
3126
3184
  if (diagnostics.length > 0) {
3127
- return errorResult(input, diagnostics, selected.run);
3185
+ return errorResult(input, diagnostics, selected.run, ruleExecutions);
3128
3186
  }
3129
3187
  const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
3130
3188
  const sortedFindings = findings.sort(compareFindings(eventById));
3131
- const summary = summarize(sortedFindings, diagnostics);
3189
+ const summary = summarize(sortedFindings, diagnostics, ruleExecutions.length);
3132
3190
  const status = summary.failed > 0 ? "fail" : "pass";
3133
3191
  return {
3134
3192
  ok: status === "pass",
@@ -3137,7 +3195,8 @@ function runTraceChecks(input, options = {}) {
3137
3195
  ...selected.run ? { runId: selected.run.runId } : {},
3138
3196
  summary,
3139
3197
  findings: sortedFindings,
3140
- diagnostics
3198
+ diagnostics,
3199
+ ruleExecutions
3141
3200
  };
3142
3201
  }
3143
3202
 
@@ -5464,8 +5523,7 @@ async function runSuiteCase(suiteCase, config, options) {
5464
5523
  status: "pass",
5465
5524
  format: read.format,
5466
5525
  findings: [],
5467
- diagnostics: []
5468
- };
5526
+ diagnostics: []};
5469
5527
  const observationResult = validateExpectedObservations(suiteCase, read);
5470
5528
  const diagnostics = [
5471
5529
  ...checkResult.diagnostics.map(