agent-inspect 6.17.4 → 6.17.6

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 (33) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/docs/ADAPTERS.md +3 -3
  4. package/docs/API.md +13 -7
  5. package/docs/CLI.md +2 -0
  6. package/docs/LIMITATIONS.md +1 -0
  7. package/docs/TRACE-CONTRACTS.md +90 -0
  8. package/package.json +8 -5
  9. package/packages/cli/dist/{chunk-YX6RFLV5.mjs → chunk-GOHYSE7W.mjs} +86 -17
  10. package/packages/cli/dist/chunk-GOHYSE7W.mjs.map +1 -0
  11. package/packages/cli/dist/index.cjs +522 -81
  12. package/packages/cli/dist/index.cjs.map +1 -1
  13. package/packages/cli/dist/index.mjs +441 -69
  14. package/packages/cli/dist/index.mjs.map +1 -1
  15. package/packages/cli/dist/{src-X6SLMBNH.mjs → src-OYGG7QAZ.mjs} +3 -3
  16. package/packages/cli/dist/{src-X6SLMBNH.mjs.map → src-OYGG7QAZ.mjs.map} +1 -1
  17. package/packages/core/dist/advanced.cjs +85 -16
  18. package/packages/core/dist/advanced.cjs.map +1 -1
  19. package/packages/core/dist/advanced.d.cts +1 -1
  20. package/packages/core/dist/advanced.d.ts +1 -1
  21. package/packages/core/dist/advanced.mjs +15 -5
  22. package/packages/core/dist/advanced.mjs.map +1 -1
  23. package/packages/core/dist/checks.cjs +119 -26
  24. package/packages/core/dist/checks.cjs.map +1 -1
  25. package/packages/core/dist/checks.d.cts +28 -2
  26. package/packages/core/dist/checks.d.ts +28 -2
  27. package/packages/core/dist/checks.mjs +1 -1
  28. package/packages/core/dist/{chunk-HN377P23.mjs → chunk-DIZPIPY2.mjs} +121 -28
  29. package/packages/core/dist/chunk-DIZPIPY2.mjs.map +1 -0
  30. package/packages/core/dist/{index-Xk9X-yjY.d.cts → index-DWu54Y28.d.cts} +107 -122
  31. package/packages/core/dist/{index-BsCcOKxy.d.ts → index-DlwbVqEs.d.ts} +107 -122
  32. package/packages/cli/dist/chunk-YX6RFLV5.mjs.map +0 -1
  33. package/packages/core/dist/chunk-HN377P23.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { 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, truncateStringForProfile, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, isAgentInspectTrace, filterMetasBySessionScope, 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, renderErrorLine, getIndent, TreeBuilder, zeroKinds, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, diffRuns, manualTraceEventsToComparableRun } from './chunk-YX6RFLV5.mjs';
2
+ import { 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, truncateStringForProfile, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, isAgentInspectTrace, filterMetasBySessionScope, 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, renderErrorLine, getIndent, TreeBuilder, zeroKinds, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, diffRuns, manualTraceEventsToComparableRun } from './chunk-GOHYSE7W.mjs';
3
3
  import { realpathSync, constants as constants$1, existsSync, readFileSync } from 'fs';
4
4
  import path18 from 'path';
5
5
  import { fileURLToPath, pathToFileURL } from 'url';
@@ -7,12 +7,13 @@ import { Command, Option } from 'commander';
7
7
  import { readFile, stat, mkdir, writeFile, unlink, appendFile, access, rm, open, readdir, constants } from 'fs/promises';
8
8
  import process2, { stdin, stdout } from 'process';
9
9
  import crypto, { createHash } from 'crypto';
10
+ import { execFile, spawn } from 'child_process';
11
+ import { promisify } from 'util';
10
12
  import { createServer } from 'http';
11
- import { spawn } from 'child_process';
12
13
  import { createRequire } from 'module';
13
14
 
14
15
  // package.json
15
- var version = "6.17.4";
16
+ var version = "6.17.6";
16
17
 
17
18
  // packages/cli/src/trace-dir-scale.ts
18
19
  var TRACE_COUNT_WARN = 1e3;
@@ -1540,6 +1541,11 @@ async function followFile(filePath, options, onLine, shouldStop) {
1540
1541
  const msg = e instanceof Error ? e.message : String(e);
1541
1542
  throw new Error(`Failed to stat file: ${filePath} (${msg})`);
1542
1543
  }
1544
+ if (next.size < pos) {
1545
+ pos = 0;
1546
+ carry = "";
1547
+ lineNumber = 0;
1548
+ }
1543
1549
  if (next.size > pos) {
1544
1550
  const fh = await open(filePath, "r");
1545
1551
  try {
@@ -3347,7 +3353,7 @@ async function newestTraceMtimeMs(traceDir) {
3347
3353
  }
3348
3354
  async function loadSessionRuns(traceDir) {
3349
3355
  try {
3350
- const mod = await import('./src-X6SLMBNH.mjs');
3356
+ const mod = await import('./src-OYGG7QAZ.mjs');
3351
3357
  const dbPath = mod.resolveIndexDbPath(traceDir);
3352
3358
  const status = mod.indexStatus(dbPath);
3353
3359
  if (!status.healthy) {
@@ -5143,8 +5149,11 @@ function checkResultToEvidenceJson(result, runIds) {
5143
5149
  status,
5144
5150
  errors: result.summary.errors,
5145
5151
  warnings: result.summary.warnings,
5146
- findings: result.findings.length
5152
+ findings: result.findings.length,
5153
+ rulesEvaluated: result.summary.rulesEvaluated ?? 0
5147
5154
  })),
5155
+ rulesEvaluated: result.summary.rulesEvaluated ?? 0,
5156
+ evaluatedRuleIds: (result.ruleExecutions ?? []).map((item) => item.ruleId),
5148
5157
  findings: result.findings,
5149
5158
  diagnostics: result.diagnostics
5150
5159
  });
@@ -5769,13 +5778,16 @@ function mergeSafetyExtensions(result, read, options) {
5769
5778
  passed,
5770
5779
  failed,
5771
5780
  warnings,
5772
- errors: result.summary.errors
5781
+ errors: result.summary.errors,
5782
+ rulesEvaluated: result.summary.rulesEvaluated ?? 0
5773
5783
  },
5774
- findings
5784
+ findings,
5785
+ ruleExecutions: result.ruleExecutions ?? []
5775
5786
  };
5776
5787
  }
5777
5788
 
5778
5789
  // packages/cli/src/check.ts
5790
+ var execFileAsync = promisify(execFile);
5779
5791
  var DEFAULT_SELECT = ["run.status"];
5780
5792
  function uniqueSelectIds(ids) {
5781
5793
  const seen = /* @__PURE__ */ new Set();
@@ -5891,10 +5903,12 @@ function errorResult(code, message, format = "unknown") {
5891
5903
  passed: 0,
5892
5904
  failed: 0,
5893
5905
  warnings: 0,
5894
- errors: 1
5906
+ errors: 1,
5907
+ rulesEvaluated: 0
5895
5908
  },
5896
5909
  findings: [],
5897
- diagnostics
5910
+ diagnostics,
5911
+ ruleExecutions: []
5898
5912
  };
5899
5913
  }
5900
5914
  function parseNumber(value, label) {
@@ -5912,36 +5926,368 @@ function asStringArray(value) {
5912
5926
  }
5913
5927
  return value;
5914
5928
  }
5915
- function asConfig(value) {
5929
+ var CHECKS_KEYS = /* @__PURE__ */ new Set(["select", "run", "tool", "llm", "structure", "safety"]);
5930
+ var RUN_KEYS = /* @__PURE__ */ new Set(["expected", "allowIncomplete", "maxDurationMs", "maxDepth"]);
5931
+ var TOOL_KEYS = /* @__PURE__ */ new Set(["required", "forbidden", "allowed", "minCount", "maxCount"]);
5932
+ var LLM_KEYS = /* @__PURE__ */ new Set([
5933
+ "allowedModels",
5934
+ "allowedProviders",
5935
+ "finishReasons",
5936
+ "maxCalls",
5937
+ "maxTotalTokens",
5938
+ "maxInputTokens",
5939
+ "maxOutputTokens",
5940
+ "maxCachedTokens"
5941
+ ]);
5942
+ var STRUCTURE_KEYS = /* @__PURE__ */ new Set([
5943
+ "minConfidence",
5944
+ "requireParentBeforeChild",
5945
+ "requireTraceParentSpan",
5946
+ "orphan",
5947
+ "cycle",
5948
+ "maxChildren",
5949
+ "maxConcurrent"
5950
+ ]);
5951
+ var SAFETY_KEYS = /* @__PURE__ */ new Set([
5952
+ "redaction",
5953
+ "rawContent",
5954
+ "secretPattern",
5955
+ "maxStringLength",
5956
+ "maxArrayLength",
5957
+ "maxObjectKeys",
5958
+ "maxSerializedBytes",
5959
+ "maxFindings"
5960
+ ]);
5961
+ var RUN_EXPECTED = /* @__PURE__ */ new Set(["ok", "error", "running"]);
5962
+ var CONFIDENCE_VALUES = /* @__PURE__ */ new Set(["exact", "high", "medium", "low", "inferred"]);
5963
+ var CheckConfigError = class extends Error {
5964
+ code;
5965
+ constructor(code, message) {
5966
+ super(message);
5967
+ this.name = "CheckConfigError";
5968
+ this.code = code;
5969
+ }
5970
+ };
5971
+ function closestKey(unknown, known) {
5972
+ const lower = unknown.toLowerCase();
5973
+ let best;
5974
+ let bestDistance = Number.POSITIVE_INFINITY;
5975
+ for (const candidate of known) {
5976
+ if (candidate.toLowerCase() === lower) return candidate;
5977
+ let distance = Math.abs(candidate.length - unknown.length);
5978
+ const max = Math.max(candidate.length, unknown.length);
5979
+ for (let i = 0; i < Math.min(candidate.length, unknown.length); i += 1) {
5980
+ if (candidate[i].toLowerCase() !== unknown[i].toLowerCase()) distance += 1;
5981
+ }
5982
+ if (distance < bestDistance && distance <= Math.max(2, Math.floor(max / 3))) {
5983
+ bestDistance = distance;
5984
+ best = candidate;
5985
+ }
5986
+ }
5987
+ return best;
5988
+ }
5989
+ function rejectUnknownKeys(record, allowed, pathPrefix) {
5990
+ for (const key of Object.keys(record)) {
5991
+ if (allowed.has(key)) continue;
5992
+ const suggestion = closestKey(key, [...allowed]);
5993
+ const hint = suggestion ? ` Did you mean "${suggestion}"?` : "";
5994
+ throw new CheckConfigError(
5995
+ "AI_CHECK_CONFIG_UNKNOWN_KEY",
5996
+ `Unknown check config key "${pathPrefix}.${key}".${hint}`
5997
+ );
5998
+ }
5999
+ }
6000
+ function requireObject(value, pathPrefix) {
6001
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
6002
+ throw new CheckConfigError(
6003
+ "AI_CHECK_CONFIG_INVALID_VALUE",
6004
+ `${pathPrefix} must be an object.`
6005
+ );
6006
+ }
6007
+ return value;
6008
+ }
6009
+ function requireStringArray(value, pathPrefix) {
6010
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.trim() === "")) {
6011
+ throw new CheckConfigError(
6012
+ "AI_CHECK_CONFIG_INVALID_VALUE",
6013
+ `${pathPrefix} must be an array of non-empty strings.`
6014
+ );
6015
+ }
6016
+ return value;
6017
+ }
6018
+ function requireNonNegativeNumber(value, pathPrefix) {
6019
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
6020
+ throw new CheckConfigError(
6021
+ "AI_CHECK_CONFIG_INVALID_VALUE",
6022
+ `${pathPrefix} must be a finite non-negative number.`
6023
+ );
6024
+ }
6025
+ return value;
6026
+ }
6027
+ function requireBoolean(value, pathPrefix) {
6028
+ if (typeof value !== "boolean") {
6029
+ throw new CheckConfigError(
6030
+ "AI_CHECK_CONFIG_INVALID_VALUE",
6031
+ `${pathPrefix} must be a boolean.`
6032
+ );
6033
+ }
6034
+ return value;
6035
+ }
6036
+ function parseRunSection(value) {
6037
+ const record = requireObject(value, "checks.run");
6038
+ rejectUnknownKeys(record, RUN_KEYS, "checks.run");
6039
+ const out = {};
6040
+ if (record.expected !== void 0) {
6041
+ if (typeof record.expected !== "string" || !RUN_EXPECTED.has(record.expected)) {
6042
+ throw new CheckConfigError(
6043
+ "AI_CHECK_CONFIG_INVALID_VALUE",
6044
+ `checks.run.expected must be one of: ok, error, running.`
6045
+ );
6046
+ }
6047
+ out.expected = record.expected;
6048
+ }
6049
+ if (record.allowIncomplete !== void 0) {
6050
+ out.allowIncomplete = requireBoolean(record.allowIncomplete, "checks.run.allowIncomplete");
6051
+ }
6052
+ if (record.maxDurationMs !== void 0) {
6053
+ out.maxDurationMs = requireNonNegativeNumber(record.maxDurationMs, "checks.run.maxDurationMs");
6054
+ }
6055
+ if (record.maxDepth !== void 0) {
6056
+ out.maxDepth = requireNonNegativeNumber(record.maxDepth, "checks.run.maxDepth");
6057
+ }
6058
+ return out;
6059
+ }
6060
+ function parseToolSection(value) {
6061
+ const record = requireObject(value, "checks.tool");
6062
+ rejectUnknownKeys(record, TOOL_KEYS, "checks.tool");
6063
+ const out = {};
6064
+ if (record.required !== void 0) out.required = requireStringArray(record.required, "checks.tool.required");
6065
+ if (record.forbidden !== void 0) out.forbidden = requireStringArray(record.forbidden, "checks.tool.forbidden");
6066
+ if (record.allowed !== void 0) out.allowed = requireStringArray(record.allowed, "checks.tool.allowed");
6067
+ if (record.minCount !== void 0) out.minCount = requireNonNegativeNumber(record.minCount, "checks.tool.minCount");
6068
+ if (record.maxCount !== void 0) out.maxCount = requireNonNegativeNumber(record.maxCount, "checks.tool.maxCount");
6069
+ return out;
6070
+ }
6071
+ function parseLlmSection(value) {
6072
+ const record = requireObject(value, "checks.llm");
6073
+ rejectUnknownKeys(record, LLM_KEYS, "checks.llm");
6074
+ const out = {};
6075
+ if (record.allowedModels !== void 0) {
6076
+ out.allowedModels = requireStringArray(record.allowedModels, "checks.llm.allowedModels");
6077
+ }
6078
+ if (record.allowedProviders !== void 0) {
6079
+ out.allowedProviders = requireStringArray(record.allowedProviders, "checks.llm.allowedProviders");
6080
+ }
6081
+ if (record.finishReasons !== void 0) {
6082
+ out.finishReasons = requireStringArray(record.finishReasons, "checks.llm.finishReasons");
6083
+ }
6084
+ if (record.maxCalls !== void 0) out.maxCalls = requireNonNegativeNumber(record.maxCalls, "checks.llm.maxCalls");
6085
+ if (record.maxTotalTokens !== void 0) {
6086
+ out.maxTotalTokens = requireNonNegativeNumber(record.maxTotalTokens, "checks.llm.maxTotalTokens");
6087
+ }
6088
+ if (record.maxInputTokens !== void 0) {
6089
+ out.maxInputTokens = requireNonNegativeNumber(record.maxInputTokens, "checks.llm.maxInputTokens");
6090
+ }
6091
+ if (record.maxOutputTokens !== void 0) {
6092
+ out.maxOutputTokens = requireNonNegativeNumber(record.maxOutputTokens, "checks.llm.maxOutputTokens");
6093
+ }
6094
+ if (record.maxCachedTokens !== void 0) {
6095
+ out.maxCachedTokens = requireNonNegativeNumber(record.maxCachedTokens, "checks.llm.maxCachedTokens");
6096
+ }
6097
+ return out;
6098
+ }
6099
+ function parseStructureSection(value) {
6100
+ const record = requireObject(value, "checks.structure");
6101
+ rejectUnknownKeys(record, STRUCTURE_KEYS, "checks.structure");
6102
+ const out = {};
6103
+ if (record.minConfidence !== void 0) {
6104
+ if (typeof record.minConfidence !== "string" || !CONFIDENCE_VALUES.has(record.minConfidence)) {
6105
+ throw new CheckConfigError(
6106
+ "AI_CHECK_CONFIG_INVALID_VALUE",
6107
+ `checks.structure.minConfidence must be one of: exact, high, medium, low, inferred.`
6108
+ );
6109
+ }
6110
+ out.minConfidence = record.minConfidence;
6111
+ }
6112
+ if (record.requireParentBeforeChild !== void 0) {
6113
+ out.requireParentBeforeChild = requireBoolean(
6114
+ record.requireParentBeforeChild,
6115
+ "checks.structure.requireParentBeforeChild"
6116
+ );
6117
+ }
6118
+ if (record.requireTraceParentSpan !== void 0) {
6119
+ out.requireTraceParentSpan = requireBoolean(
6120
+ record.requireTraceParentSpan,
6121
+ "checks.structure.requireTraceParentSpan"
6122
+ );
6123
+ }
6124
+ if (record.orphan !== void 0) out.orphan = requireBoolean(record.orphan, "checks.structure.orphan");
6125
+ if (record.cycle !== void 0) out.cycle = requireBoolean(record.cycle, "checks.structure.cycle");
6126
+ if (record.maxChildren !== void 0) {
6127
+ out.maxChildren = requireNonNegativeNumber(record.maxChildren, "checks.structure.maxChildren");
6128
+ }
6129
+ if (record.maxConcurrent !== void 0) {
6130
+ out.maxConcurrent = requireNonNegativeNumber(record.maxConcurrent, "checks.structure.maxConcurrent");
6131
+ }
6132
+ return out;
6133
+ }
6134
+ function parseSafetySection(value) {
6135
+ const record = requireObject(value, "checks.safety");
6136
+ rejectUnknownKeys(record, SAFETY_KEYS, "checks.safety");
6137
+ const out = {};
6138
+ if (record.redaction !== void 0) out.redaction = requireBoolean(record.redaction, "checks.safety.redaction");
6139
+ if (record.rawContent !== void 0) out.rawContent = requireBoolean(record.rawContent, "checks.safety.rawContent");
6140
+ if (record.secretPattern !== void 0) {
6141
+ out.secretPattern = requireBoolean(record.secretPattern, "checks.safety.secretPattern");
6142
+ }
6143
+ if (record.maxStringLength !== void 0) {
6144
+ out.maxStringLength = requireNonNegativeNumber(record.maxStringLength, "checks.safety.maxStringLength");
6145
+ }
6146
+ if (record.maxArrayLength !== void 0) {
6147
+ out.maxArrayLength = requireNonNegativeNumber(record.maxArrayLength, "checks.safety.maxArrayLength");
6148
+ }
6149
+ if (record.maxObjectKeys !== void 0) {
6150
+ out.maxObjectKeys = requireNonNegativeNumber(record.maxObjectKeys, "checks.safety.maxObjectKeys");
6151
+ }
6152
+ if (record.maxSerializedBytes !== void 0) {
6153
+ out.maxSerializedBytes = requireNonNegativeNumber(
6154
+ record.maxSerializedBytes,
6155
+ "checks.safety.maxSerializedBytes"
6156
+ );
6157
+ }
6158
+ if (record.maxFindings !== void 0) {
6159
+ out.maxFindings = requireNonNegativeNumber(record.maxFindings, "checks.safety.maxFindings");
6160
+ }
6161
+ return out;
6162
+ }
6163
+ function sectionHasEffect(section) {
6164
+ if (section === void 0) return false;
6165
+ return Object.keys(section).length > 0;
6166
+ }
6167
+ function checkConfigHasEffect(config) {
6168
+ const checks2 = config.checks;
6169
+ if (checks2 === void 0) return false;
6170
+ if ((checks2.select?.length ?? 0) > 0) return true;
6171
+ if (sectionHasEffect(checks2.run)) return true;
6172
+ if (sectionHasEffect(checks2.tool)) return true;
6173
+ if (sectionHasEffect(checks2.llm)) return true;
6174
+ if (sectionHasEffect(checks2.structure)) return true;
6175
+ if (sectionHasEffect(checks2.safety)) return true;
6176
+ return false;
6177
+ }
6178
+ function parseCheckConfig(value) {
5916
6179
  if (value === void 0 || value === null) return {};
5917
6180
  if (typeof value !== "object" || Array.isArray(value)) {
5918
- throw new Error("Config must export an object.");
6181
+ throw new CheckConfigError("AI_CHECK_CONFIG_INVALID_VALUE", "Config must export an object.");
5919
6182
  }
5920
- return value;
6183
+ const root = value;
6184
+ if ("contract" in root) {
6185
+ throw new CheckConfigError(
6186
+ "AI_CHECK_CONFIG_UNKNOWN_KEY",
6187
+ 'Top-level "contract" is not supported by `agent-inspect check --config`. Use the TraceContract TypeScript API (`defineTraceContract` / `evaluateTraceContract` from `agent-inspect/checks`), or express tool/run/llm rules under `checks`.'
6188
+ );
6189
+ }
6190
+ const rootKeys = Object.keys(root);
6191
+ for (const key of rootKeys) {
6192
+ if (key === "checks") continue;
6193
+ const suggestion = closestKey(key, ["checks"]);
6194
+ const hint = suggestion ? ` Did you mean "${suggestion}"?` : "";
6195
+ throw new CheckConfigError(
6196
+ "AI_CHECK_CONFIG_UNKNOWN_KEY",
6197
+ `Unknown top-level check config key "${key}". Allowed: checks.${hint}`
6198
+ );
6199
+ }
6200
+ if (!("checks" in root)) return {};
6201
+ const checksRecord = requireObject(root.checks, "checks");
6202
+ rejectUnknownKeys(checksRecord, CHECKS_KEYS, "checks");
6203
+ const checks2 = {};
6204
+ if (checksRecord.select !== void 0) {
6205
+ checks2.select = requireStringArray(checksRecord.select, "checks.select");
6206
+ }
6207
+ if (checksRecord.run !== void 0) checks2.run = parseRunSection(checksRecord.run);
6208
+ if (checksRecord.tool !== void 0) checks2.tool = parseToolSection(checksRecord.tool);
6209
+ if (checksRecord.llm !== void 0) checks2.llm = parseLlmSection(checksRecord.llm);
6210
+ if (checksRecord.structure !== void 0) {
6211
+ checks2.structure = parseStructureSection(checksRecord.structure);
6212
+ }
6213
+ if (checksRecord.safety !== void 0) checks2.safety = parseSafetySection(checksRecord.safety);
6214
+ return { checks: checks2 };
5921
6215
  }
5922
6216
  async function loadConfig(configPath) {
5923
6217
  if (configPath === void 0) return {};
5924
6218
  const extension = path18.extname(configPath);
5925
6219
  if (TS_CONFIG_EXTENSIONS.has(extension)) {
5926
- throw new Error(
6220
+ throw new CheckConfigError(
6221
+ "AI_CHECK_CONFIG_LOAD_FAILED",
5927
6222
  "TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
5928
6223
  );
5929
6224
  }
5930
6225
  if (!CONFIG_EXTENSIONS.has(extension)) {
5931
- throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
6226
+ throw new CheckConfigError(
6227
+ "AI_CHECK_CONFIG_LOAD_FAILED",
6228
+ "Unsupported check config extension. Use .json, .js, .mjs, or .cjs."
6229
+ );
5932
6230
  }
5933
6231
  const absolute = path18.resolve(configPath);
5934
- if (extension === ".json") {
5935
- const raw = await readFile(absolute, "utf-8");
5936
- return asConfig(JSON.parse(raw));
6232
+ try {
6233
+ if (extension === ".json") {
6234
+ const raw = await readFile(absolute, "utf-8");
6235
+ return parseCheckConfig(JSON.parse(raw));
6236
+ }
6237
+ return parseCheckConfig(await importJsCheckConfig(absolute));
6238
+ } catch (error) {
6239
+ if (error instanceof CheckConfigError) throw error;
6240
+ const message = error instanceof Error ? error.message : String(error);
6241
+ throw new CheckConfigError(
6242
+ "AI_CHECK_CONFIG_LOAD_FAILED",
6243
+ `Failed to load check config: ${message}`
6244
+ );
6245
+ }
6246
+ }
6247
+ async function importJsCheckConfig(absolute) {
6248
+ const href = pathToFileURL(absolute).href;
6249
+ if (!process.env.VITEST) {
6250
+ const mod = await import(
6251
+ /* @vite-ignore */
6252
+ href
6253
+ );
6254
+ return "default" in mod ? mod.default : mod;
6255
+ }
6256
+ const script = `
6257
+ const mod = await import(${JSON.stringify(href)});
6258
+ const value = "default" in mod ? mod.default : mod;
6259
+ process.stdout.write(JSON.stringify({ ok: true, value }));
6260
+ `;
6261
+ const env = { ...process.env };
6262
+ delete env.NODE_OPTIONS;
6263
+ delete env.VITEST;
6264
+ delete env.VITEST_WORKER_ID;
6265
+ const { stdout, stderr } = await execFileAsync(
6266
+ process.execPath,
6267
+ ["--input-type=module", "--eval", script],
6268
+ {
6269
+ encoding: "utf8",
6270
+ maxBuffer: 4 * 1024 * 1024,
6271
+ env
6272
+ }
6273
+ );
6274
+ try {
6275
+ const parsed = JSON.parse(stdout);
6276
+ if (parsed.ok !== true) {
6277
+ throw new Error(stderr.trim() || "Check config module did not return a value");
6278
+ }
6279
+ return parsed.value;
6280
+ } catch (error) {
6281
+ if (stderr.trim()) {
6282
+ throw new Error(stderr.trim());
6283
+ }
6284
+ throw error instanceof Error ? error : new Error(String(error));
5937
6285
  }
5938
- const mod = await import(pathToFileURL(absolute).href);
5939
- return asConfig("default" in mod ? mod.default : mod);
5940
6286
  }
5941
6287
  function normalizeConfig(config) {
5942
6288
  if (config.checks === void 0) return {};
5943
6289
  if (typeof config.checks !== "object" || Array.isArray(config.checks)) {
5944
- throw new Error("checks config must be an object.");
6290
+ throw new CheckConfigError("AI_CHECK_CONFIG_INVALID_VALUE", "checks config must be an object.");
5945
6291
  }
5946
6292
  return config.checks;
5947
6293
  }
@@ -6019,7 +6365,7 @@ function buildRules(config, options, presetSelect = []) {
6019
6365
  }
6020
6366
  throw new Error(`unsupported status "${value}"`);
6021
6367
  });
6022
- rules.push(createObservedOutcomeRule({ failOn: statuses }));
6368
+ rules.push(createObservedOutcomeRule({ failOn: statuses, requireAny: true }));
6023
6369
  } catch (error) {
6024
6370
  const message = error instanceof Error ? error.message : String(error);
6025
6371
  diagnostics.push(
@@ -6091,7 +6437,7 @@ function exitCodeFor(result) {
6091
6437
  return 3;
6092
6438
  }
6093
6439
  if (codes.some(
6094
- (code) => code === "AI_CHECK_INVALID_ARGUMENTS" || code === "AI_CHECK_INVALID_CONFIG" || code === "AI_CHECK_CONFIG_LOAD_FAILED" || code === "AI_CHECK_RUN_SELECTION_REQUIRED"
6440
+ (code) => code === "AI_CHECK_INVALID_ARGUMENTS" || code === "AI_CHECK_INVALID_CONFIG" || code === "AI_CHECK_CONFIG_LOAD_FAILED" || code === "AI_CHECK_CONFIG_UNKNOWN_KEY" || code === "AI_CHECK_CONFIG_INVALID_VALUE" || code === "AI_CHECK_CONFIG_NO_EFFECTIVE_RULES" || code === "AI_CHECK_NO_RULES_EVALUATED" || code === "AI_CHECK_RUN_SELECTION_REQUIRED"
6095
6441
  )) {
6096
6442
  return 2;
6097
6443
  }
@@ -6158,6 +6504,7 @@ function printHuman(result, options = {}) {
6158
6504
  console.log(
6159
6505
  `Summary: ${result.summary.failed} failed, ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
6160
6506
  );
6507
+ console.log(`Rules evaluated: ${result.summary.rulesEvaluated ?? result.ruleExecutions?.length ?? 0}`);
6161
6508
  for (const diagnostic3 of result.diagnostics) {
6162
6509
  console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
6163
6510
  }
@@ -6189,6 +6536,16 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
6189
6536
  const useSessionScope = Boolean(sessionId || groupId);
6190
6537
  try {
6191
6538
  let config = await loadConfig(options.config);
6539
+ if (options.config !== void 0 && !checkConfigHasEffect(config)) {
6540
+ result = errorResult(
6541
+ "AI_CHECK_CONFIG_NO_EFFECTIVE_RULES",
6542
+ "Explicit --config has no effective check rules. Configure checks.select, checks.run, checks.tool, checks.llm, checks.structure, or checks.safety."
6543
+ );
6544
+ if (options.json) printJson(result);
6545
+ else printHuman(result, options);
6546
+ process.exitCode = exitCodeFor(result);
6547
+ return;
6548
+ }
6192
6549
  let effectiveOptions = options;
6193
6550
  const resolved = resolvePreset(options.preset, {
6194
6551
  hasToolRules: hasToolRulesConfigured(config, options)
@@ -6308,12 +6665,13 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
6308
6665
  }
6309
6666
  } catch (error) {
6310
6667
  if (phase === "config") {
6311
- const message = error instanceof Error ? error.message : String(error);
6312
- const code = message.startsWith("--") || message.includes("Unknown --preset") ? "AI_CHECK_INVALID_ARGUMENTS" : error instanceof SyntaxError || message.includes("Unsupported check config extension") || message.includes("TypeScript check configs") || message.includes("Config must") || message.includes("checks config") || message.includes("Expected an array") ? "AI_CHECK_INVALID_CONFIG" : "AI_CHECK_CONFIG_LOAD_FAILED";
6313
- result = errorResult(
6314
- code,
6315
- message
6316
- );
6668
+ if (error instanceof CheckConfigError) {
6669
+ result = errorResult(error.code, error.message);
6670
+ } else {
6671
+ const message = error instanceof Error ? error.message : String(error);
6672
+ const code = message.startsWith("--") || message.includes("Unknown --preset") ? "AI_CHECK_INVALID_ARGUMENTS" : error instanceof SyntaxError || message.includes("Unsupported check config extension") || message.includes("TypeScript check configs") || message.includes("Config must") || message.includes("checks config") || message.includes("Expected an array") ? "AI_CHECK_INVALID_CONFIG" : "AI_CHECK_CONFIG_LOAD_FAILED";
6673
+ result = errorResult(code, message);
6674
+ }
6317
6675
  } else {
6318
6676
  result = readErrorResult(error);
6319
6677
  }
@@ -7235,6 +7593,42 @@ function startViewerServer(options = {}) {
7235
7593
  });
7236
7594
  });
7237
7595
  }
7596
+ function openInDefaultBrowser(target) {
7597
+ const platform = process.platform;
7598
+ const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
7599
+ const args = platform === "win32" ? ["/c", "start", "", target] : [target];
7600
+ return new Promise((resolve) => {
7601
+ try {
7602
+ const child = spawn(command, args, {
7603
+ detached: true,
7604
+ stdio: "ignore",
7605
+ windowsHide: platform === "win32"
7606
+ });
7607
+ child.once("spawn", () => {
7608
+ try {
7609
+ child.unref();
7610
+ resolve({ ok: true });
7611
+ } catch (error) {
7612
+ resolve({
7613
+ ok: false,
7614
+ detail: error instanceof Error ? error.message : String(error)
7615
+ });
7616
+ }
7617
+ });
7618
+ child.once("error", (error) => {
7619
+ resolve({
7620
+ ok: false,
7621
+ detail: error instanceof Error ? error.message : String(error)
7622
+ });
7623
+ });
7624
+ } catch (error) {
7625
+ resolve({
7626
+ ok: false,
7627
+ detail: error instanceof Error ? error.message : String(error)
7628
+ });
7629
+ }
7630
+ });
7631
+ }
7238
7632
 
7239
7633
  // packages/cli/src/serve.ts
7240
7634
  function parsePort(value) {
@@ -7264,9 +7658,11 @@ async function serveCommand(options = {}) {
7264
7658
  if (options.open === true && host !== "127.0.0.1" && host !== "localhost") {
7265
7659
  console.warn("Skipping browser open for non-local host binding.");
7266
7660
  } else if (options.open === true) {
7267
- const openMod = await import('child_process');
7268
- openMod.exec(`open ${info.url}`, () => {
7269
- });
7661
+ const opened = await openInDefaultBrowser(info.url);
7662
+ if (!opened.ok) {
7663
+ console.warn("Viewer started, but the browser could not be opened automatically.");
7664
+ console.warn(`Open manually: ${info.url}`);
7665
+ }
7270
7666
  }
7271
7667
  await new Promise(() => {
7272
7668
  });
@@ -7425,9 +7821,11 @@ async function studioCommand(options = {}) {
7425
7821
  if (options.open === true && info.host !== "127.0.0.1" && info.host !== "localhost") {
7426
7822
  console.warn("Skipping browser open for non-local host binding.");
7427
7823
  } else if (options.open === true) {
7428
- const openMod = await import('child_process');
7429
- openMod.exec(`open ${info.url}`, () => {
7430
- });
7824
+ const opened = await openInDefaultBrowser(info.url);
7825
+ if (!opened.ok) {
7826
+ console.warn("Studio started, but the browser could not be opened automatically.");
7827
+ console.warn(`Open manually: ${info.url}`);
7828
+ }
7431
7829
  }
7432
7830
  await new Promise(() => {
7433
7831
  });
@@ -8186,7 +8584,7 @@ function asStringArray2(value, label) {
8186
8584
  }
8187
8585
  return value;
8188
8586
  }
8189
- function asConfig2(value) {
8587
+ function asConfig(value) {
8190
8588
  if (value === void 0 || value === null) return {};
8191
8589
  if (typeof value !== "object" || Array.isArray(value)) {
8192
8590
  throw new Error("Config must export an object.");
@@ -8207,10 +8605,10 @@ async function loadConfig2(configPath) {
8207
8605
  const absolute = path18.resolve(configPath);
8208
8606
  if (extension === ".json") {
8209
8607
  const raw = await readFile(absolute, "utf-8");
8210
- return asConfig2(JSON.parse(raw));
8608
+ return asConfig(JSON.parse(raw));
8211
8609
  }
8212
8610
  const mod = await import(pathToFileURL(absolute).href);
8213
- return asConfig2("default" in mod ? mod.default : mod);
8611
+ return asConfig("default" in mod ? mod.default : mod);
8214
8612
  }
8215
8613
  function normalizeConfig2(config) {
8216
8614
  if (config.eval === void 0) return {};
@@ -9715,33 +10113,6 @@ async function resolveHtmlPath(root) {
9715
10113
  `No evidence.html found under ${root}. Pass a bundle directory or .html path.`
9716
10114
  );
9717
10115
  }
9718
- function openLocalFile(filePath) {
9719
- const fileUrl = pathToFileURL(filePath).href;
9720
- const platform = process.platform;
9721
- const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
9722
- const args = platform === "darwin" ? [filePath] : platform === "win32" ? ["/c", "start", "", filePath] : [filePath];
9723
- return new Promise((resolve) => {
9724
- try {
9725
- const child = spawn(command, args, {
9726
- detached: true,
9727
- stdio: "ignore"
9728
- });
9729
- child.on("error", (error) => {
9730
- resolve({
9731
- ok: false,
9732
- detail: `${error instanceof Error ? error.message : String(error)}; open manually: ${fileUrl}`
9733
- });
9734
- });
9735
- child.unref();
9736
- resolve({ ok: true, detail: fileUrl });
9737
- } catch (error) {
9738
- resolve({
9739
- ok: false,
9740
- detail: `${error instanceof Error ? error.message : String(error)}; open manually: ${fileUrl}`
9741
- });
9742
- }
9743
- });
9744
- }
9745
10116
  async function bundleOpenCommand(targetPath, options = {}) {
9746
10117
  const root = path18.resolve(targetPath.trim() || ".");
9747
10118
  try {
@@ -9811,23 +10182,24 @@ async function bundleOpenCommand(targetPath, options = {}) {
9811
10182
  await readFile(path18.join(path18.dirname(htmlPath), "evidence.json"), "utf-8");
9812
10183
  } catch {
9813
10184
  }
9814
- const opened = await openLocalFile(htmlPath);
10185
+ const fileUrl = pathToFileURL(htmlPath).href;
10186
+ const opened = await openInDefaultBrowser(fileUrl);
9815
10187
  if (options.json) {
9816
10188
  console.log(
9817
10189
  writeJson6({
9818
10190
  ok: opened.ok,
9819
10191
  path: htmlPath,
9820
10192
  opened: opened.ok,
9821
- detail: opened.detail
10193
+ detail: opened.ok ? fileUrl : `${opened.detail ?? "unknown error"}; open manually: ${fileUrl}`
9822
10194
  }).trimEnd()
9823
10195
  );
9824
10196
  } else if (opened.ok) {
9825
10197
  console.log(`Opened local Evidence: ${htmlPath}`);
9826
- console.log(`URL: ${opened.detail}`);
10198
+ console.log(`URL: ${fileUrl}`);
9827
10199
  } else {
9828
10200
  console.error(`[AgentInspect] Could not open browser automatically.`);
9829
10201
  console.error(`Open this file locally: ${htmlPath}`);
9830
- console.error(opened.detail);
10202
+ console.error(`${opened.detail ?? "unknown error"}; open manually: ${fileUrl}`);
9831
10203
  }
9832
10204
  if (!opened.ok) {
9833
10205
  process.exitCode = 0;
@@ -11260,7 +11632,7 @@ function isModuleNotFound4(e) {
11260
11632
  }
11261
11633
  async function loadIndexSqlite() {
11262
11634
  try {
11263
- return await import('./src-X6SLMBNH.mjs');
11635
+ return await import('./src-OYGG7QAZ.mjs');
11264
11636
  } catch (e) {
11265
11637
  if (isModuleNotFound4(e)) {
11266
11638
  console.error(