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
@@ -13,8 +13,9 @@ var readline = require('readline');
13
13
  var url = require('url');
14
14
  var module$1 = require('module');
15
15
  var commander = require('commander');
16
- var http = require('http');
17
16
  var child_process = require('child_process');
17
+ var util = require('util');
18
+ var http = require('http');
18
19
 
19
20
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
20
21
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -3565,14 +3566,15 @@ var init_cohort = __esm({
3565
3566
 
3566
3567
  // packages/core/src/sessions/checks.ts
3567
3568
  function emptySummary() {
3568
- return { passed: 0, failed: 0, warnings: 0, errors: 0 };
3569
+ return { passed: 0, failed: 0, warnings: 0, errors: 0, rulesEvaluated: 0 };
3569
3570
  }
3570
3571
  function mergeSummary(target, source) {
3571
3572
  return {
3572
3573
  passed: target.passed + source.passed,
3573
3574
  failed: target.failed + source.failed,
3574
3575
  warnings: target.warnings + source.warnings,
3575
- errors: target.errors + source.errors
3576
+ errors: target.errors + source.errors,
3577
+ rulesEvaluated: target.rulesEvaluated + source.rulesEvaluated
3576
3578
  };
3577
3579
  }
3578
3580
  function sessionDiagnostic(code, message) {
@@ -3596,6 +3598,7 @@ function aggregateSessionCheckResults(perRun, scope) {
3596
3598
  `${scope.scopeKind} not found: ${scope.scopeLabel}`
3597
3599
  )
3598
3600
  ],
3601
+ ruleExecutions: [],
3599
3602
  ...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
3600
3603
  };
3601
3604
  }
@@ -3616,17 +3619,20 @@ function aggregateSessionCheckResults(perRun, scope) {
3616
3619
  `No readable traces in ${scope.scopeKind}: ${scope.scopeLabel}`
3617
3620
  )
3618
3621
  ],
3622
+ ruleExecutions: [],
3619
3623
  ...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
3620
3624
  };
3621
3625
  }
3622
3626
  let summary = emptySummary();
3623
3627
  const findings = [];
3624
3628
  const diagnostics = [];
3629
+ const ruleExecutions = [];
3625
3630
  const runResults = [];
3626
3631
  for (const result of perRun) {
3627
3632
  summary = mergeSummary(summary, result.summary);
3628
3633
  findings.push(...result.findings);
3629
3634
  diagnostics.push(...result.diagnostics);
3635
+ ruleExecutions.push(...result.ruleExecutions ?? []);
3630
3636
  if (result.runId) {
3631
3637
  runResults.push({ runId: result.runId, status: result.status });
3632
3638
  }
@@ -3639,6 +3645,11 @@ function aggregateSessionCheckResults(perRun, scope) {
3639
3645
  if (runCmp !== 0) return runCmp;
3640
3646
  return a.ruleId.localeCompare(b.ruleId);
3641
3647
  });
3648
+ ruleExecutions.sort((a, b) => {
3649
+ const runCmp = (a.runId ?? "").localeCompare(b.runId ?? "");
3650
+ if (runCmp !== 0) return runCmp;
3651
+ return a.ruleId.localeCompare(b.ruleId);
3652
+ });
3642
3653
  const hasErrors = diagnostics.some((item) => item.severity === "error");
3643
3654
  const status = hasErrors ? "error" : summary.failed > 0 ? "fail" : "pass";
3644
3655
  return {
@@ -3652,6 +3663,7 @@ function aggregateSessionCheckResults(perRun, scope) {
3652
3663
  summary,
3653
3664
  findings,
3654
3665
  diagnostics,
3666
+ ruleExecutions,
3655
3667
  ...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
3656
3668
  };
3657
3669
  }
@@ -6857,10 +6869,11 @@ function emptySummary2() {
6857
6869
  passed: 0,
6858
6870
  failed: 0,
6859
6871
  warnings: 0,
6860
- errors: 0
6872
+ errors: 0,
6873
+ rulesEvaluated: 0
6861
6874
  };
6862
6875
  }
6863
- function errorResult(input3, diagnostics, selectedRun) {
6876
+ function errorResult(input3, diagnostics, selectedRun, ruleExecutions = []) {
6864
6877
  return {
6865
6878
  ok: false,
6866
6879
  status: "error",
@@ -6868,10 +6881,12 @@ function errorResult(input3, diagnostics, selectedRun) {
6868
6881
  ...selectedRun ? { runId: selectedRun.runId } : {},
6869
6882
  summary: {
6870
6883
  ...emptySummary2(),
6871
- errors: diagnostics.filter((item) => item.severity === "error").length
6884
+ errors: diagnostics.filter((item) => item.severity === "error").length,
6885
+ rulesEvaluated: ruleExecutions.length
6872
6886
  },
6873
6887
  findings: [],
6874
- diagnostics: [...diagnostics]
6888
+ diagnostics: [...diagnostics],
6889
+ ruleExecutions: [...ruleExecutions]
6875
6890
  };
6876
6891
  }
6877
6892
  function flattenNodes(nodes) {
@@ -7027,7 +7042,7 @@ function normalizeFinding(rule, finding) {
7027
7042
  ...finding.action !== void 0 ? { action: finding.action } : {}
7028
7043
  };
7029
7044
  }
7030
- function summarize(findings, diagnostics) {
7045
+ function summarize(findings, diagnostics, rulesEvaluated) {
7031
7046
  return {
7032
7047
  passed: findings.filter((finding) => finding.status === "pass").length,
7033
7048
  failed: findings.filter(
@@ -7036,9 +7051,21 @@ function summarize(findings, diagnostics) {
7036
7051
  warnings: findings.filter(
7037
7052
  (finding) => finding.status === "warning" || finding.severity === "warning"
7038
7053
  ).length,
7039
- errors: diagnostics.filter((item) => item.severity === "error").length
7054
+ errors: diagnostics.filter((item) => item.severity === "error").length,
7055
+ rulesEvaluated
7040
7056
  };
7041
7057
  }
7058
+ function classifyRuleExecution(findings, threw) {
7059
+ if (findings.some((finding) => finding.status === "fail" && finding.severity === "error")) {
7060
+ return "fail";
7061
+ }
7062
+ if (findings.some(
7063
+ (finding) => finding.status === "warning" || finding.severity === "warning"
7064
+ )) {
7065
+ return "warning";
7066
+ }
7067
+ return "pass";
7068
+ }
7042
7069
  function stringAttr(event, keys) {
7043
7070
  for (const key of keys) {
7044
7071
  const value = event.attributes?.[key];
@@ -7135,6 +7162,9 @@ function finishedEvents(context, kind) {
7135
7162
  (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
7136
7163
  );
7137
7164
  }
7165
+ function toolInvocationEvents(context) {
7166
+ return semanticEvents(context).filter((event) => event.kind === "TOOL");
7167
+ }
7138
7168
  function isRecord8(value) {
7139
7169
  return typeof value === "object" && value !== null && !Array.isArray(value);
7140
7170
  }
@@ -7535,7 +7565,7 @@ function createToolUsageRule(options) {
7535
7565
  category: "tool",
7536
7566
  defaultSeverity: "error",
7537
7567
  evaluate(context) {
7538
- const tools = finishedEvents(context, "TOOL");
7568
+ const tools = toolInvocationEvents(context);
7539
7569
  const names = tools.map(toolName);
7540
7570
  const nameSet = new Set(names);
7541
7571
  const findings = [];
@@ -8185,12 +8215,24 @@ function createBaselineRegressionRule(options) {
8185
8215
  }
8186
8216
  function createObservedOutcomeRule(options = {}) {
8187
8217
  const failOn = options.failOn ?? ["failed"];
8218
+ const requireAny = options.requireAny === true;
8188
8219
  return {
8189
8220
  id: "outcome.status",
8190
8221
  category: "run",
8191
8222
  defaultSeverity: "error",
8192
8223
  evaluate(context) {
8193
8224
  const outcomes = extractOutcomesFromPersistedEvents(context.events);
8225
+ if (requireAny && outcomes.length === 0) {
8226
+ return [
8227
+ failFinding(
8228
+ "outcome.status",
8229
+ "Expected at least one observed outcome.",
8230
+ runEvidence(context.selectedRun),
8231
+ { requireAny: true, expected: "at least one observed outcome" },
8232
+ { code: "outcome.missing", actual: 0 }
8233
+ )
8234
+ ];
8235
+ }
8194
8236
  const matching = outcomesMatchingStatus(outcomes, failOn);
8195
8237
  if (matching.length === 0) return [];
8196
8238
  return [
@@ -8226,6 +8268,18 @@ function runTraceChecks(input3, options = {}) {
8226
8268
  if (rules.diagnostics.length > 0) {
8227
8269
  return errorResult(input3, rules.diagnostics, selected.run);
8228
8270
  }
8271
+ if (rules.rules.length === 0) {
8272
+ return errorResult(
8273
+ input3,
8274
+ [
8275
+ diagnostic3(
8276
+ "AI_CHECK_NO_RULES_EVALUATED",
8277
+ "No trace check rules were evaluated. Configure at least one rule, contract, or CLI check option."
8278
+ )
8279
+ ],
8280
+ selected.run
8281
+ );
8282
+ }
8229
8283
  const facts = buildFacts2(input3, selected.run);
8230
8284
  const context = {
8231
8285
  ...facts,
@@ -8234,22 +8288,38 @@ function runTraceChecks(input3, options = {}) {
8234
8288
  };
8235
8289
  const diagnostics = [];
8236
8290
  const findings = [];
8291
+ const ruleExecutions = [];
8237
8292
  for (const rule of rules.rules) {
8238
8293
  try {
8239
- findings.push(...rule.evaluate(context).map((finding) => normalizeFinding(rule, finding)));
8294
+ const ruleFindings = rule.evaluate(context).map((finding) => normalizeFinding(rule, finding));
8295
+ findings.push(...ruleFindings);
8296
+ ruleExecutions.push({
8297
+ ruleId: rule.id,
8298
+ category: rule.category,
8299
+ status: classifyRuleExecution(ruleFindings, false),
8300
+ findingCount: ruleFindings.length,
8301
+ ...selected.run ? { runId: selected.run.runId } : {}
8302
+ });
8240
8303
  } catch (error) {
8241
8304
  const message = error instanceof Error ? error.message : String(error);
8242
8305
  diagnostics.push(
8243
8306
  diagnostic3("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
8244
8307
  );
8308
+ ruleExecutions.push({
8309
+ ruleId: rule.id,
8310
+ category: rule.category,
8311
+ status: "error",
8312
+ findingCount: 0,
8313
+ ...selected.run ? { runId: selected.run.runId } : {}
8314
+ });
8245
8315
  }
8246
8316
  }
8247
8317
  if (diagnostics.length > 0) {
8248
- return errorResult(input3, diagnostics, selected.run);
8318
+ return errorResult(input3, diagnostics, selected.run, ruleExecutions);
8249
8319
  }
8250
8320
  const eventById = new Map(input3.read.events.map((event) => [event.eventId, event]));
8251
8321
  const sortedFindings = findings.sort(compareFindings(eventById));
8252
- const summary = summarize(sortedFindings, diagnostics);
8322
+ const summary = summarize(sortedFindings, diagnostics, ruleExecutions.length);
8253
8323
  const status = summary.failed > 0 ? "fail" : "pass";
8254
8324
  return {
8255
8325
  ok: status === "pass",
@@ -8258,7 +8328,8 @@ function runTraceChecks(input3, options = {}) {
8258
8328
  ...selected.run ? { runId: selected.run.runId } : {},
8259
8329
  summary,
8260
8330
  findings: sortedFindings,
8261
- diagnostics
8331
+ diagnostics,
8332
+ ruleExecutions
8262
8333
  };
8263
8334
  }
8264
8335
  var SEVERITY_RANK, STATUS_RANK, CONFIDENCE_RANK, DEFAULT_SENSITIVE_KEYS, DEFAULT_RAW_CONTENT_KEYS, DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES, SAFE_USAGE_LEAF_KEYS, DEFAULT_SECRET_PATTERNS;
@@ -10707,8 +10778,7 @@ async function runSuiteCase(suiteCase, config, options) {
10707
10778
  status: "pass",
10708
10779
  format: read.format,
10709
10780
  findings: [],
10710
- diagnostics: []
10711
- };
10781
+ diagnostics: []};
10712
10782
  const observationResult = validateExpectedObservations(suiteCase, read);
10713
10783
  const diagnostics = [
10714
10784
  ...checkResult.diagnostics.map(
@@ -12511,7 +12581,7 @@ var init_src = __esm({
12511
12581
  });
12512
12582
 
12513
12583
  // package.json
12514
- var version = "6.17.4";
12584
+ var version = "6.17.6";
12515
12585
 
12516
12586
  // packages/cli/src/list.ts
12517
12587
  init_advanced();
@@ -14080,6 +14150,11 @@ async function followFile(filePath, options, onLine, shouldStop) {
14080
14150
  const msg = e instanceof Error ? e.message : String(e);
14081
14151
  throw new Error(`Failed to stat file: ${filePath} (${msg})`);
14082
14152
  }
14153
+ if (next.size < pos) {
14154
+ pos = 0;
14155
+ carry = "";
14156
+ lineNumber = 0;
14157
+ }
14083
14158
  if (next.size > pos) {
14084
14159
  const fh = await promises.open(filePath, "r");
14085
14160
  try {
@@ -17744,8 +17819,11 @@ function checkResultToEvidenceJson(result, runIds) {
17744
17819
  status,
17745
17820
  errors: result.summary.errors,
17746
17821
  warnings: result.summary.warnings,
17747
- findings: result.findings.length
17822
+ findings: result.findings.length,
17823
+ rulesEvaluated: result.summary.rulesEvaluated ?? 0
17748
17824
  })),
17825
+ rulesEvaluated: result.summary.rulesEvaluated ?? 0,
17826
+ evaluatedRuleIds: (result.ruleExecutions ?? []).map((item) => item.ruleId),
17749
17827
  findings: result.findings,
17750
17828
  diagnostics: result.diagnostics
17751
17829
  });
@@ -18370,13 +18448,16 @@ function mergeSafetyExtensions(result, read, options) {
18370
18448
  passed,
18371
18449
  failed,
18372
18450
  warnings,
18373
- errors: result.summary.errors
18451
+ errors: result.summary.errors,
18452
+ rulesEvaluated: result.summary.rulesEvaluated ?? 0
18374
18453
  },
18375
- findings
18454
+ findings,
18455
+ ruleExecutions: result.ruleExecutions ?? []
18376
18456
  };
18377
18457
  }
18378
18458
 
18379
18459
  // packages/cli/src/check.ts
18460
+ var execFileAsync = util.promisify(child_process.execFile);
18380
18461
  var DEFAULT_SELECT = ["run.status"];
18381
18462
  function uniqueSelectIds(ids) {
18382
18463
  const seen = /* @__PURE__ */ new Set();
@@ -18492,10 +18573,12 @@ function errorResult2(code, message, format = "unknown") {
18492
18573
  passed: 0,
18493
18574
  failed: 0,
18494
18575
  warnings: 0,
18495
- errors: 1
18576
+ errors: 1,
18577
+ rulesEvaluated: 0
18496
18578
  },
18497
18579
  findings: [],
18498
- diagnostics
18580
+ diagnostics,
18581
+ ruleExecutions: []
18499
18582
  };
18500
18583
  }
18501
18584
  function parseNumber(value, label) {
@@ -18513,36 +18596,368 @@ function asStringArray2(value) {
18513
18596
  }
18514
18597
  return value;
18515
18598
  }
18516
- function asConfig(value) {
18599
+ var CHECKS_KEYS = /* @__PURE__ */ new Set(["select", "run", "tool", "llm", "structure", "safety"]);
18600
+ var RUN_KEYS = /* @__PURE__ */ new Set(["expected", "allowIncomplete", "maxDurationMs", "maxDepth"]);
18601
+ var TOOL_KEYS = /* @__PURE__ */ new Set(["required", "forbidden", "allowed", "minCount", "maxCount"]);
18602
+ var LLM_KEYS = /* @__PURE__ */ new Set([
18603
+ "allowedModels",
18604
+ "allowedProviders",
18605
+ "finishReasons",
18606
+ "maxCalls",
18607
+ "maxTotalTokens",
18608
+ "maxInputTokens",
18609
+ "maxOutputTokens",
18610
+ "maxCachedTokens"
18611
+ ]);
18612
+ var STRUCTURE_KEYS = /* @__PURE__ */ new Set([
18613
+ "minConfidence",
18614
+ "requireParentBeforeChild",
18615
+ "requireTraceParentSpan",
18616
+ "orphan",
18617
+ "cycle",
18618
+ "maxChildren",
18619
+ "maxConcurrent"
18620
+ ]);
18621
+ var SAFETY_KEYS = /* @__PURE__ */ new Set([
18622
+ "redaction",
18623
+ "rawContent",
18624
+ "secretPattern",
18625
+ "maxStringLength",
18626
+ "maxArrayLength",
18627
+ "maxObjectKeys",
18628
+ "maxSerializedBytes",
18629
+ "maxFindings"
18630
+ ]);
18631
+ var RUN_EXPECTED = /* @__PURE__ */ new Set(["ok", "error", "running"]);
18632
+ var CONFIDENCE_VALUES = /* @__PURE__ */ new Set(["exact", "high", "medium", "low", "inferred"]);
18633
+ var CheckConfigError = class extends Error {
18634
+ code;
18635
+ constructor(code, message) {
18636
+ super(message);
18637
+ this.name = "CheckConfigError";
18638
+ this.code = code;
18639
+ }
18640
+ };
18641
+ function closestKey(unknown, known) {
18642
+ const lower = unknown.toLowerCase();
18643
+ let best;
18644
+ let bestDistance = Number.POSITIVE_INFINITY;
18645
+ for (const candidate of known) {
18646
+ if (candidate.toLowerCase() === lower) return candidate;
18647
+ let distance = Math.abs(candidate.length - unknown.length);
18648
+ const max = Math.max(candidate.length, unknown.length);
18649
+ for (let i = 0; i < Math.min(candidate.length, unknown.length); i += 1) {
18650
+ if (candidate[i].toLowerCase() !== unknown[i].toLowerCase()) distance += 1;
18651
+ }
18652
+ if (distance < bestDistance && distance <= Math.max(2, Math.floor(max / 3))) {
18653
+ bestDistance = distance;
18654
+ best = candidate;
18655
+ }
18656
+ }
18657
+ return best;
18658
+ }
18659
+ function rejectUnknownKeys(record, allowed, pathPrefix) {
18660
+ for (const key of Object.keys(record)) {
18661
+ if (allowed.has(key)) continue;
18662
+ const suggestion = closestKey(key, [...allowed]);
18663
+ const hint = suggestion ? ` Did you mean "${suggestion}"?` : "";
18664
+ throw new CheckConfigError(
18665
+ "AI_CHECK_CONFIG_UNKNOWN_KEY",
18666
+ `Unknown check config key "${pathPrefix}.${key}".${hint}`
18667
+ );
18668
+ }
18669
+ }
18670
+ function requireObject(value, pathPrefix) {
18671
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
18672
+ throw new CheckConfigError(
18673
+ "AI_CHECK_CONFIG_INVALID_VALUE",
18674
+ `${pathPrefix} must be an object.`
18675
+ );
18676
+ }
18677
+ return value;
18678
+ }
18679
+ function requireStringArray(value, pathPrefix) {
18680
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.trim() === "")) {
18681
+ throw new CheckConfigError(
18682
+ "AI_CHECK_CONFIG_INVALID_VALUE",
18683
+ `${pathPrefix} must be an array of non-empty strings.`
18684
+ );
18685
+ }
18686
+ return value;
18687
+ }
18688
+ function requireNonNegativeNumber(value, pathPrefix) {
18689
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
18690
+ throw new CheckConfigError(
18691
+ "AI_CHECK_CONFIG_INVALID_VALUE",
18692
+ `${pathPrefix} must be a finite non-negative number.`
18693
+ );
18694
+ }
18695
+ return value;
18696
+ }
18697
+ function requireBoolean(value, pathPrefix) {
18698
+ if (typeof value !== "boolean") {
18699
+ throw new CheckConfigError(
18700
+ "AI_CHECK_CONFIG_INVALID_VALUE",
18701
+ `${pathPrefix} must be a boolean.`
18702
+ );
18703
+ }
18704
+ return value;
18705
+ }
18706
+ function parseRunSection(value) {
18707
+ const record = requireObject(value, "checks.run");
18708
+ rejectUnknownKeys(record, RUN_KEYS, "checks.run");
18709
+ const out = {};
18710
+ if (record.expected !== void 0) {
18711
+ if (typeof record.expected !== "string" || !RUN_EXPECTED.has(record.expected)) {
18712
+ throw new CheckConfigError(
18713
+ "AI_CHECK_CONFIG_INVALID_VALUE",
18714
+ `checks.run.expected must be one of: ok, error, running.`
18715
+ );
18716
+ }
18717
+ out.expected = record.expected;
18718
+ }
18719
+ if (record.allowIncomplete !== void 0) {
18720
+ out.allowIncomplete = requireBoolean(record.allowIncomplete, "checks.run.allowIncomplete");
18721
+ }
18722
+ if (record.maxDurationMs !== void 0) {
18723
+ out.maxDurationMs = requireNonNegativeNumber(record.maxDurationMs, "checks.run.maxDurationMs");
18724
+ }
18725
+ if (record.maxDepth !== void 0) {
18726
+ out.maxDepth = requireNonNegativeNumber(record.maxDepth, "checks.run.maxDepth");
18727
+ }
18728
+ return out;
18729
+ }
18730
+ function parseToolSection(value) {
18731
+ const record = requireObject(value, "checks.tool");
18732
+ rejectUnknownKeys(record, TOOL_KEYS, "checks.tool");
18733
+ const out = {};
18734
+ if (record.required !== void 0) out.required = requireStringArray(record.required, "checks.tool.required");
18735
+ if (record.forbidden !== void 0) out.forbidden = requireStringArray(record.forbidden, "checks.tool.forbidden");
18736
+ if (record.allowed !== void 0) out.allowed = requireStringArray(record.allowed, "checks.tool.allowed");
18737
+ if (record.minCount !== void 0) out.minCount = requireNonNegativeNumber(record.minCount, "checks.tool.minCount");
18738
+ if (record.maxCount !== void 0) out.maxCount = requireNonNegativeNumber(record.maxCount, "checks.tool.maxCount");
18739
+ return out;
18740
+ }
18741
+ function parseLlmSection(value) {
18742
+ const record = requireObject(value, "checks.llm");
18743
+ rejectUnknownKeys(record, LLM_KEYS, "checks.llm");
18744
+ const out = {};
18745
+ if (record.allowedModels !== void 0) {
18746
+ out.allowedModels = requireStringArray(record.allowedModels, "checks.llm.allowedModels");
18747
+ }
18748
+ if (record.allowedProviders !== void 0) {
18749
+ out.allowedProviders = requireStringArray(record.allowedProviders, "checks.llm.allowedProviders");
18750
+ }
18751
+ if (record.finishReasons !== void 0) {
18752
+ out.finishReasons = requireStringArray(record.finishReasons, "checks.llm.finishReasons");
18753
+ }
18754
+ if (record.maxCalls !== void 0) out.maxCalls = requireNonNegativeNumber(record.maxCalls, "checks.llm.maxCalls");
18755
+ if (record.maxTotalTokens !== void 0) {
18756
+ out.maxTotalTokens = requireNonNegativeNumber(record.maxTotalTokens, "checks.llm.maxTotalTokens");
18757
+ }
18758
+ if (record.maxInputTokens !== void 0) {
18759
+ out.maxInputTokens = requireNonNegativeNumber(record.maxInputTokens, "checks.llm.maxInputTokens");
18760
+ }
18761
+ if (record.maxOutputTokens !== void 0) {
18762
+ out.maxOutputTokens = requireNonNegativeNumber(record.maxOutputTokens, "checks.llm.maxOutputTokens");
18763
+ }
18764
+ if (record.maxCachedTokens !== void 0) {
18765
+ out.maxCachedTokens = requireNonNegativeNumber(record.maxCachedTokens, "checks.llm.maxCachedTokens");
18766
+ }
18767
+ return out;
18768
+ }
18769
+ function parseStructureSection(value) {
18770
+ const record = requireObject(value, "checks.structure");
18771
+ rejectUnknownKeys(record, STRUCTURE_KEYS, "checks.structure");
18772
+ const out = {};
18773
+ if (record.minConfidence !== void 0) {
18774
+ if (typeof record.minConfidence !== "string" || !CONFIDENCE_VALUES.has(record.minConfidence)) {
18775
+ throw new CheckConfigError(
18776
+ "AI_CHECK_CONFIG_INVALID_VALUE",
18777
+ `checks.structure.minConfidence must be one of: exact, high, medium, low, inferred.`
18778
+ );
18779
+ }
18780
+ out.minConfidence = record.minConfidence;
18781
+ }
18782
+ if (record.requireParentBeforeChild !== void 0) {
18783
+ out.requireParentBeforeChild = requireBoolean(
18784
+ record.requireParentBeforeChild,
18785
+ "checks.structure.requireParentBeforeChild"
18786
+ );
18787
+ }
18788
+ if (record.requireTraceParentSpan !== void 0) {
18789
+ out.requireTraceParentSpan = requireBoolean(
18790
+ record.requireTraceParentSpan,
18791
+ "checks.structure.requireTraceParentSpan"
18792
+ );
18793
+ }
18794
+ if (record.orphan !== void 0) out.orphan = requireBoolean(record.orphan, "checks.structure.orphan");
18795
+ if (record.cycle !== void 0) out.cycle = requireBoolean(record.cycle, "checks.structure.cycle");
18796
+ if (record.maxChildren !== void 0) {
18797
+ out.maxChildren = requireNonNegativeNumber(record.maxChildren, "checks.structure.maxChildren");
18798
+ }
18799
+ if (record.maxConcurrent !== void 0) {
18800
+ out.maxConcurrent = requireNonNegativeNumber(record.maxConcurrent, "checks.structure.maxConcurrent");
18801
+ }
18802
+ return out;
18803
+ }
18804
+ function parseSafetySection(value) {
18805
+ const record = requireObject(value, "checks.safety");
18806
+ rejectUnknownKeys(record, SAFETY_KEYS, "checks.safety");
18807
+ const out = {};
18808
+ if (record.redaction !== void 0) out.redaction = requireBoolean(record.redaction, "checks.safety.redaction");
18809
+ if (record.rawContent !== void 0) out.rawContent = requireBoolean(record.rawContent, "checks.safety.rawContent");
18810
+ if (record.secretPattern !== void 0) {
18811
+ out.secretPattern = requireBoolean(record.secretPattern, "checks.safety.secretPattern");
18812
+ }
18813
+ if (record.maxStringLength !== void 0) {
18814
+ out.maxStringLength = requireNonNegativeNumber(record.maxStringLength, "checks.safety.maxStringLength");
18815
+ }
18816
+ if (record.maxArrayLength !== void 0) {
18817
+ out.maxArrayLength = requireNonNegativeNumber(record.maxArrayLength, "checks.safety.maxArrayLength");
18818
+ }
18819
+ if (record.maxObjectKeys !== void 0) {
18820
+ out.maxObjectKeys = requireNonNegativeNumber(record.maxObjectKeys, "checks.safety.maxObjectKeys");
18821
+ }
18822
+ if (record.maxSerializedBytes !== void 0) {
18823
+ out.maxSerializedBytes = requireNonNegativeNumber(
18824
+ record.maxSerializedBytes,
18825
+ "checks.safety.maxSerializedBytes"
18826
+ );
18827
+ }
18828
+ if (record.maxFindings !== void 0) {
18829
+ out.maxFindings = requireNonNegativeNumber(record.maxFindings, "checks.safety.maxFindings");
18830
+ }
18831
+ return out;
18832
+ }
18833
+ function sectionHasEffect(section) {
18834
+ if (section === void 0) return false;
18835
+ return Object.keys(section).length > 0;
18836
+ }
18837
+ function checkConfigHasEffect(config) {
18838
+ const checks2 = config.checks;
18839
+ if (checks2 === void 0) return false;
18840
+ if ((checks2.select?.length ?? 0) > 0) return true;
18841
+ if (sectionHasEffect(checks2.run)) return true;
18842
+ if (sectionHasEffect(checks2.tool)) return true;
18843
+ if (sectionHasEffect(checks2.llm)) return true;
18844
+ if (sectionHasEffect(checks2.structure)) return true;
18845
+ if (sectionHasEffect(checks2.safety)) return true;
18846
+ return false;
18847
+ }
18848
+ function parseCheckConfig(value) {
18517
18849
  if (value === void 0 || value === null) return {};
18518
18850
  if (typeof value !== "object" || Array.isArray(value)) {
18519
- throw new Error("Config must export an object.");
18851
+ throw new CheckConfigError("AI_CHECK_CONFIG_INVALID_VALUE", "Config must export an object.");
18520
18852
  }
18521
- return value;
18853
+ const root = value;
18854
+ if ("contract" in root) {
18855
+ throw new CheckConfigError(
18856
+ "AI_CHECK_CONFIG_UNKNOWN_KEY",
18857
+ '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`.'
18858
+ );
18859
+ }
18860
+ const rootKeys = Object.keys(root);
18861
+ for (const key of rootKeys) {
18862
+ if (key === "checks") continue;
18863
+ const suggestion = closestKey(key, ["checks"]);
18864
+ const hint = suggestion ? ` Did you mean "${suggestion}"?` : "";
18865
+ throw new CheckConfigError(
18866
+ "AI_CHECK_CONFIG_UNKNOWN_KEY",
18867
+ `Unknown top-level check config key "${key}". Allowed: checks.${hint}`
18868
+ );
18869
+ }
18870
+ if (!("checks" in root)) return {};
18871
+ const checksRecord = requireObject(root.checks, "checks");
18872
+ rejectUnknownKeys(checksRecord, CHECKS_KEYS, "checks");
18873
+ const checks2 = {};
18874
+ if (checksRecord.select !== void 0) {
18875
+ checks2.select = requireStringArray(checksRecord.select, "checks.select");
18876
+ }
18877
+ if (checksRecord.run !== void 0) checks2.run = parseRunSection(checksRecord.run);
18878
+ if (checksRecord.tool !== void 0) checks2.tool = parseToolSection(checksRecord.tool);
18879
+ if (checksRecord.llm !== void 0) checks2.llm = parseLlmSection(checksRecord.llm);
18880
+ if (checksRecord.structure !== void 0) {
18881
+ checks2.structure = parseStructureSection(checksRecord.structure);
18882
+ }
18883
+ if (checksRecord.safety !== void 0) checks2.safety = parseSafetySection(checksRecord.safety);
18884
+ return { checks: checks2 };
18522
18885
  }
18523
18886
  async function loadConfig(configPath) {
18524
18887
  if (configPath === void 0) return {};
18525
18888
  const extension = path32__default.default.extname(configPath);
18526
18889
  if (TS_CONFIG_EXTENSIONS2.has(extension)) {
18527
- throw new Error(
18890
+ throw new CheckConfigError(
18891
+ "AI_CHECK_CONFIG_LOAD_FAILED",
18528
18892
  "TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
18529
18893
  );
18530
18894
  }
18531
18895
  if (!CONFIG_EXTENSIONS2.has(extension)) {
18532
- throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
18896
+ throw new CheckConfigError(
18897
+ "AI_CHECK_CONFIG_LOAD_FAILED",
18898
+ "Unsupported check config extension. Use .json, .js, .mjs, or .cjs."
18899
+ );
18533
18900
  }
18534
18901
  const absolute = path32__default.default.resolve(configPath);
18535
- if (extension === ".json") {
18536
- const raw = await promises.readFile(absolute, "utf-8");
18537
- return asConfig(JSON.parse(raw));
18902
+ try {
18903
+ if (extension === ".json") {
18904
+ const raw = await promises.readFile(absolute, "utf-8");
18905
+ return parseCheckConfig(JSON.parse(raw));
18906
+ }
18907
+ return parseCheckConfig(await importJsCheckConfig(absolute));
18908
+ } catch (error) {
18909
+ if (error instanceof CheckConfigError) throw error;
18910
+ const message = error instanceof Error ? error.message : String(error);
18911
+ throw new CheckConfigError(
18912
+ "AI_CHECK_CONFIG_LOAD_FAILED",
18913
+ `Failed to load check config: ${message}`
18914
+ );
18915
+ }
18916
+ }
18917
+ async function importJsCheckConfig(absolute) {
18918
+ const href = url.pathToFileURL(absolute).href;
18919
+ if (!process.env.VITEST) {
18920
+ const mod = await import(
18921
+ /* @vite-ignore */
18922
+ href
18923
+ );
18924
+ return "default" in mod ? mod.default : mod;
18925
+ }
18926
+ const script = `
18927
+ const mod = await import(${JSON.stringify(href)});
18928
+ const value = "default" in mod ? mod.default : mod;
18929
+ process.stdout.write(JSON.stringify({ ok: true, value }));
18930
+ `;
18931
+ const env2 = { ...process.env };
18932
+ delete env2.NODE_OPTIONS;
18933
+ delete env2.VITEST;
18934
+ delete env2.VITEST_WORKER_ID;
18935
+ const { stdout, stderr } = await execFileAsync(
18936
+ process.execPath,
18937
+ ["--input-type=module", "--eval", script],
18938
+ {
18939
+ encoding: "utf8",
18940
+ maxBuffer: 4 * 1024 * 1024,
18941
+ env: env2
18942
+ }
18943
+ );
18944
+ try {
18945
+ const parsed = JSON.parse(stdout);
18946
+ if (parsed.ok !== true) {
18947
+ throw new Error(stderr.trim() || "Check config module did not return a value");
18948
+ }
18949
+ return parsed.value;
18950
+ } catch (error) {
18951
+ if (stderr.trim()) {
18952
+ throw new Error(stderr.trim());
18953
+ }
18954
+ throw error instanceof Error ? error : new Error(String(error));
18538
18955
  }
18539
- const mod = await import(url.pathToFileURL(absolute).href);
18540
- return asConfig("default" in mod ? mod.default : mod);
18541
18956
  }
18542
18957
  function normalizeConfig(config) {
18543
18958
  if (config.checks === void 0) return {};
18544
18959
  if (typeof config.checks !== "object" || Array.isArray(config.checks)) {
18545
- throw new Error("checks config must be an object.");
18960
+ throw new CheckConfigError("AI_CHECK_CONFIG_INVALID_VALUE", "checks config must be an object.");
18546
18961
  }
18547
18962
  return config.checks;
18548
18963
  }
@@ -18620,7 +19035,7 @@ function buildRules(config, options, presetSelect = []) {
18620
19035
  }
18621
19036
  throw new Error(`unsupported status "${value}"`);
18622
19037
  });
18623
- rules.push(createObservedOutcomeRule({ failOn: statuses }));
19038
+ rules.push(createObservedOutcomeRule({ failOn: statuses, requireAny: true }));
18624
19039
  } catch (error) {
18625
19040
  const message = error instanceof Error ? error.message : String(error);
18626
19041
  diagnostics.push(
@@ -18692,7 +19107,7 @@ function exitCodeFor(result) {
18692
19107
  return 3;
18693
19108
  }
18694
19109
  if (codes.some(
18695
- (code) => code === "AI_CHECK_INVALID_ARGUMENTS" || code === "AI_CHECK_INVALID_CONFIG" || code === "AI_CHECK_CONFIG_LOAD_FAILED" || code === "AI_CHECK_RUN_SELECTION_REQUIRED"
19110
+ (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"
18696
19111
  )) {
18697
19112
  return 2;
18698
19113
  }
@@ -18759,6 +19174,7 @@ function printHuman(result, options = {}) {
18759
19174
  console.log(
18760
19175
  `Summary: ${result.summary.failed} failed, ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
18761
19176
  );
19177
+ console.log(`Rules evaluated: ${result.summary.rulesEvaluated ?? result.ruleExecutions?.length ?? 0}`);
18762
19178
  for (const diagnostic7 of result.diagnostics) {
18763
19179
  console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
18764
19180
  }
@@ -18790,6 +19206,16 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
18790
19206
  const useSessionScope = Boolean(sessionId || groupId);
18791
19207
  try {
18792
19208
  let config = await loadConfig(options.config);
19209
+ if (options.config !== void 0 && !checkConfigHasEffect(config)) {
19210
+ result = errorResult2(
19211
+ "AI_CHECK_CONFIG_NO_EFFECTIVE_RULES",
19212
+ "Explicit --config has no effective check rules. Configure checks.select, checks.run, checks.tool, checks.llm, checks.structure, or checks.safety."
19213
+ );
19214
+ if (options.json) printJson(result);
19215
+ else printHuman(result, options);
19216
+ process.exitCode = exitCodeFor(result);
19217
+ return;
19218
+ }
18793
19219
  let effectiveOptions = options;
18794
19220
  const resolved = resolvePreset(options.preset, {
18795
19221
  hasToolRules: hasToolRulesConfigured(config, options)
@@ -18909,12 +19335,13 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
18909
19335
  }
18910
19336
  } catch (error) {
18911
19337
  if (phase === "config") {
18912
- const message = error instanceof Error ? error.message : String(error);
18913
- 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";
18914
- result = errorResult2(
18915
- code,
18916
- message
18917
- );
19338
+ if (error instanceof CheckConfigError) {
19339
+ result = errorResult2(error.code, error.message);
19340
+ } else {
19341
+ const message = error instanceof Error ? error.message : String(error);
19342
+ 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";
19343
+ result = errorResult2(code, message);
19344
+ }
18918
19345
  } else {
18919
19346
  result = readErrorResult(error);
18920
19347
  }
@@ -19845,6 +20272,42 @@ function startViewerServer(options = {}) {
19845
20272
  });
19846
20273
  });
19847
20274
  }
20275
+ function openInDefaultBrowser(target) {
20276
+ const platform = process.platform;
20277
+ const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
20278
+ const args = platform === "win32" ? ["/c", "start", "", target] : [target];
20279
+ return new Promise((resolve) => {
20280
+ try {
20281
+ const child = child_process.spawn(command, args, {
20282
+ detached: true,
20283
+ stdio: "ignore",
20284
+ windowsHide: platform === "win32"
20285
+ });
20286
+ child.once("spawn", () => {
20287
+ try {
20288
+ child.unref();
20289
+ resolve({ ok: true });
20290
+ } catch (error) {
20291
+ resolve({
20292
+ ok: false,
20293
+ detail: error instanceof Error ? error.message : String(error)
20294
+ });
20295
+ }
20296
+ });
20297
+ child.once("error", (error) => {
20298
+ resolve({
20299
+ ok: false,
20300
+ detail: error instanceof Error ? error.message : String(error)
20301
+ });
20302
+ });
20303
+ } catch (error) {
20304
+ resolve({
20305
+ ok: false,
20306
+ detail: error instanceof Error ? error.message : String(error)
20307
+ });
20308
+ }
20309
+ });
20310
+ }
19848
20311
 
19849
20312
  // packages/cli/src/serve.ts
19850
20313
  function parsePort(value) {
@@ -19874,9 +20337,11 @@ async function serveCommand(options = {}) {
19874
20337
  if (options.open === true && host !== "127.0.0.1" && host !== "localhost") {
19875
20338
  console.warn("Skipping browser open for non-local host binding.");
19876
20339
  } else if (options.open === true) {
19877
- const openMod = await import('child_process');
19878
- openMod.exec(`open ${info.url}`, () => {
19879
- });
20340
+ const opened = await openInDefaultBrowser(info.url);
20341
+ if (!opened.ok) {
20342
+ console.warn("Viewer started, but the browser could not be opened automatically.");
20343
+ console.warn(`Open manually: ${info.url}`);
20344
+ }
19880
20345
  }
19881
20346
  await new Promise(() => {
19882
20347
  });
@@ -20035,9 +20500,11 @@ async function studioCommand(options = {}) {
20035
20500
  if (options.open === true && info.host !== "127.0.0.1" && info.host !== "localhost") {
20036
20501
  console.warn("Skipping browser open for non-local host binding.");
20037
20502
  } else if (options.open === true) {
20038
- const openMod = await import('child_process');
20039
- openMod.exec(`open ${info.url}`, () => {
20040
- });
20503
+ const opened = await openInDefaultBrowser(info.url);
20504
+ if (!opened.ok) {
20505
+ console.warn("Studio started, but the browser could not be opened automatically.");
20506
+ console.warn(`Open manually: ${info.url}`);
20507
+ }
20041
20508
  }
20042
20509
  await new Promise(() => {
20043
20510
  });
@@ -20796,7 +21263,7 @@ function asStringArray3(value, label) {
20796
21263
  }
20797
21264
  return value;
20798
21265
  }
20799
- function asConfig2(value) {
21266
+ function asConfig(value) {
20800
21267
  if (value === void 0 || value === null) return {};
20801
21268
  if (typeof value !== "object" || Array.isArray(value)) {
20802
21269
  throw new Error("Config must export an object.");
@@ -20817,10 +21284,10 @@ async function loadConfig2(configPath) {
20817
21284
  const absolute = path32__default.default.resolve(configPath);
20818
21285
  if (extension === ".json") {
20819
21286
  const raw = await promises.readFile(absolute, "utf-8");
20820
- return asConfig2(JSON.parse(raw));
21287
+ return asConfig(JSON.parse(raw));
20821
21288
  }
20822
21289
  const mod = await import(url.pathToFileURL(absolute).href);
20823
- return asConfig2("default" in mod ? mod.default : mod);
21290
+ return asConfig("default" in mod ? mod.default : mod);
20824
21291
  }
20825
21292
  function normalizeConfig2(config) {
20826
21293
  if (config.eval === void 0) return {};
@@ -22335,33 +22802,6 @@ async function resolveHtmlPath(root) {
22335
22802
  `No evidence.html found under ${root}. Pass a bundle directory or .html path.`
22336
22803
  );
22337
22804
  }
22338
- function openLocalFile(filePath) {
22339
- const fileUrl = url.pathToFileURL(filePath).href;
22340
- const platform = process.platform;
22341
- const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
22342
- const args = platform === "darwin" ? [filePath] : platform === "win32" ? ["/c", "start", "", filePath] : [filePath];
22343
- return new Promise((resolve) => {
22344
- try {
22345
- const child = child_process.spawn(command, args, {
22346
- detached: true,
22347
- stdio: "ignore"
22348
- });
22349
- child.on("error", (error) => {
22350
- resolve({
22351
- ok: false,
22352
- detail: `${error instanceof Error ? error.message : String(error)}; open manually: ${fileUrl}`
22353
- });
22354
- });
22355
- child.unref();
22356
- resolve({ ok: true, detail: fileUrl });
22357
- } catch (error) {
22358
- resolve({
22359
- ok: false,
22360
- detail: `${error instanceof Error ? error.message : String(error)}; open manually: ${fileUrl}`
22361
- });
22362
- }
22363
- });
22364
- }
22365
22805
  async function bundleOpenCommand(targetPath, options = {}) {
22366
22806
  const root = path32__default.default.resolve(targetPath.trim() || ".");
22367
22807
  try {
@@ -22431,23 +22871,24 @@ async function bundleOpenCommand(targetPath, options = {}) {
22431
22871
  await promises.readFile(path32__default.default.join(path32__default.default.dirname(htmlPath), "evidence.json"), "utf-8");
22432
22872
  } catch {
22433
22873
  }
22434
- const opened = await openLocalFile(htmlPath);
22874
+ const fileUrl = url.pathToFileURL(htmlPath).href;
22875
+ const opened = await openInDefaultBrowser(fileUrl);
22435
22876
  if (options.json) {
22436
22877
  console.log(
22437
22878
  writeJson6({
22438
22879
  ok: opened.ok,
22439
22880
  path: htmlPath,
22440
22881
  opened: opened.ok,
22441
- detail: opened.detail
22882
+ detail: opened.ok ? fileUrl : `${opened.detail ?? "unknown error"}; open manually: ${fileUrl}`
22442
22883
  }).trimEnd()
22443
22884
  );
22444
22885
  } else if (opened.ok) {
22445
22886
  console.log(`Opened local Evidence: ${htmlPath}`);
22446
- console.log(`URL: ${opened.detail}`);
22887
+ console.log(`URL: ${fileUrl}`);
22447
22888
  } else {
22448
22889
  console.error(`[AgentInspect] Could not open browser automatically.`);
22449
22890
  console.error(`Open this file locally: ${htmlPath}`);
22450
- console.error(opened.detail);
22891
+ console.error(`${opened.detail ?? "unknown error"}; open manually: ${fileUrl}`);
22451
22892
  }
22452
22893
  if (!opened.ok) {
22453
22894
  process.exitCode = 0;