agent-inspect 6.3.0 → 6.4.1

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 (59) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -2
  3. package/package.json +2 -2
  4. package/packages/cli/dist/{chunk-YS2I722C.mjs → chunk-M6PC5KUT.mjs} +78 -31
  5. package/packages/cli/dist/chunk-M6PC5KUT.mjs.map +1 -0
  6. package/packages/cli/dist/index.cjs +153 -42
  7. package/packages/cli/dist/index.cjs.map +1 -1
  8. package/packages/cli/dist/index.mjs +88 -36
  9. package/packages/cli/dist/index.mjs.map +1 -1
  10. package/packages/cli/dist/{src-FVNE44UA.mjs → src-27LC2BB3.mjs} +8 -5
  11. package/packages/cli/dist/src-27LC2BB3.mjs.map +1 -0
  12. package/packages/core/dist/advanced.cjs +91 -35
  13. package/packages/core/dist/advanced.cjs.map +1 -1
  14. package/packages/core/dist/advanced.d.cts +36 -12
  15. package/packages/core/dist/advanced.d.ts +36 -12
  16. package/packages/core/dist/advanced.mjs +77 -24
  17. package/packages/core/dist/advanced.mjs.map +1 -1
  18. package/packages/core/dist/checks.cjs +113 -0
  19. package/packages/core/dist/checks.cjs.map +1 -1
  20. package/packages/core/dist/checks.d.cts +40 -599
  21. package/packages/core/dist/checks.d.ts +40 -599
  22. package/packages/core/dist/checks.mjs +1 -1
  23. package/packages/core/dist/{chunk-QLYOCCEF.mjs → chunk-EWHN6NA4.mjs} +114 -3
  24. package/packages/core/dist/chunk-EWHN6NA4.mjs.map +1 -0
  25. package/packages/core/dist/{context-DyKbkmd4.d.cts → context-BHAgRjOe.d.cts} +4 -4
  26. package/packages/core/dist/{context-BJ5UQLDi.d.ts → context-Blyjb5my.d.ts} +4 -4
  27. package/packages/core/dist/diff.d.cts +3 -3
  28. package/packages/core/dist/diff.d.ts +3 -3
  29. package/packages/core/dist/exporters.cjs +75 -0
  30. package/packages/core/dist/exporters.cjs.map +1 -1
  31. package/packages/core/dist/exporters.d.cts +29 -4
  32. package/packages/core/dist/exporters.d.ts +29 -4
  33. package/packages/core/dist/exporters.mjs +71 -1
  34. package/packages/core/dist/exporters.mjs.map +1 -1
  35. package/packages/core/dist/index-BblJEdYZ.d.ts +618 -0
  36. package/packages/core/dist/index-DyKk7iR9.d.cts +618 -0
  37. package/packages/core/dist/index.d.cts +7 -7
  38. package/packages/core/dist/index.d.ts +7 -7
  39. package/packages/core/dist/{inspect-event-IqwOHbGo.d.cts → inspect-event-DVg84S4v.d.cts} +1 -1
  40. package/packages/core/dist/{inspect-event-IqwOHbGo.d.ts → inspect-event-DVg84S4v.d.ts} +1 -1
  41. package/packages/core/dist/{log-config-CW8w5YQ9.d.cts → log-config-BG1WmWf2.d.cts} +1 -1
  42. package/packages/core/dist/{log-config-SRA4xTp8.d.ts → log-config-Ck0aT_Ou.d.ts} +1 -1
  43. package/packages/core/dist/logs.d.cts +3 -3
  44. package/packages/core/dist/logs.d.ts +3 -3
  45. package/packages/core/dist/{persisted-inspect-event-DruwqARZ.d.ts → persisted-inspect-event-ChE7pGb7.d.ts} +1 -1
  46. package/packages/core/dist/{persisted-inspect-event-Gd-W8vB7.d.cts → persisted-inspect-event-d-mSP8kd.d.cts} +1 -1
  47. package/packages/core/dist/persisted.d.cts +5 -5
  48. package/packages/core/dist/persisted.d.ts +5 -5
  49. package/packages/core/dist/readers.d.cts +2 -2
  50. package/packages/core/dist/readers.d.ts +2 -2
  51. package/packages/core/dist/{types-J3Xtuf9x.d.cts → types-B562HgN_.d.cts} +1 -1
  52. package/packages/core/dist/{types-J3Xtuf9x.d.ts → types-B562HgN_.d.ts} +1 -1
  53. package/packages/core/dist/{types-B1X87dUW.d.cts → types-BLX3tiOW.d.cts} +1 -1
  54. package/packages/core/dist/{types-BVkjSd0q.d.ts → types-MuwXSJ4b.d.ts} +1 -1
  55. package/packages/core/dist/writers.d.cts +2 -2
  56. package/packages/core/dist/writers.d.ts +2 -2
  57. package/packages/cli/dist/chunk-YS2I722C.mjs.map +0 -1
  58. package/packages/cli/dist/src-FVNE44UA.mjs.map +0 -1
  59. package/packages/core/dist/chunk-QLYOCCEF.mjs.map +0 -1
@@ -4142,6 +4142,28 @@ var init_summary = __esm({
4142
4142
  "packages/core/src/bundle/summary.ts"() {
4143
4143
  }
4144
4144
  });
4145
+ function sanitizeBundleRunId(runId) {
4146
+ let safe = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
4147
+ safe = path26__default.default.basename(safe);
4148
+ safe = safe.replace(/[^a-zA-Z0-9._-]+/g, "_");
4149
+ if (safe === "" || safe === "." || safe === "..") {
4150
+ safe = "run_unknown";
4151
+ }
4152
+ return safe;
4153
+ }
4154
+ function bundleRunAssetRelativePath(runId, extension) {
4155
+ const safe = sanitizeBundleRunId(runId);
4156
+ const ext = extension.startsWith(".") ? extension : `.${extension}`;
4157
+ return path26__default.default.posix.join("assets", "runs", `${safe}${ext}`);
4158
+ }
4159
+ function assertBundlePathContained(outputDir, relativePath) {
4160
+ const base = path26__default.default.resolve(outputDir);
4161
+ const resolved = path26__default.default.resolve(base, relativePath);
4162
+ if (resolved !== base && !resolved.startsWith(base + path26__default.default.sep)) {
4163
+ throw new Error(`Bundle path escapes output directory: ${relativePath}`);
4164
+ }
4165
+ return resolved;
4166
+ }
4145
4167
  function normalizeBundleOutputPath(out) {
4146
4168
  const trimmed = out.trim();
4147
4169
  if (trimmed === "") {
@@ -4154,7 +4176,7 @@ function normalizeBundleOutputPath(out) {
4154
4176
  return resolved;
4155
4177
  }
4156
4178
  function defaultBundleOutputPath(runIds) {
4157
- const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
4179
+ const label = runIds.length === 1 ? sanitizeBundleRunId(runIds[0]) : `multi-${runIds.length}`;
4158
4180
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4159
4181
  return path26__default.default.resolve(`agent-inspect-bundle-${label}-${stamp}`);
4160
4182
  }
@@ -4503,6 +4525,14 @@ var init_resolve2 = __esm({
4503
4525
  }
4504
4526
  });
4505
4527
 
4528
+ // packages/core/src/checks/contract.ts
4529
+ var init_contract = __esm({
4530
+ "packages/core/src/checks/contract.ts"() {
4531
+ init_checks2();
4532
+ init_outcomes();
4533
+ }
4534
+ });
4535
+
4506
4536
  // packages/core/src/checks/index.ts
4507
4537
  function compareStrings(a, b) {
4508
4538
  return (a ?? "").localeCompare(b ?? "");
@@ -5842,6 +5872,7 @@ var SEVERITY_RANK, STATUS_RANK, CONFIDENCE_RANK, DEFAULT_SENSITIVE_KEYS, DEFAULT
5842
5872
  var init_checks2 = __esm({
5843
5873
  "packages/core/src/checks/index.ts"() {
5844
5874
  init_outcomes();
5875
+ init_contract();
5845
5876
  SEVERITY_RANK = {
5846
5877
  error: 0,
5847
5878
  warning: 1,
@@ -8185,8 +8216,9 @@ async function runSuite(options = {}) {
8185
8216
  skipped: cases.filter((item) => item.status === "skipped").length
8186
8217
  };
8187
8218
  const finishedAt = new Date(options.nowMs ?? Date.now()).toISOString();
8188
- const ok = summary.failed === 0 && summary.errors === 0;
8189
- const status = summary.errors > 0 ? "error" : summary.failed > 0 || !ok ? "fail" : "pass";
8219
+ const allSkipped = cases.length > 0 && summary.skipped === cases.length;
8220
+ const ok = summary.failed === 0 && summary.errors === 0 && !allSkipped;
8221
+ const status = summary.errors > 0 ? "error" : allSkipped ? "fail" : summary.failed > 0 || !ok ? "fail" : "pass";
8190
8222
  return {
8191
8223
  ok,
8192
8224
  status,
@@ -8411,10 +8443,14 @@ var init_types5 = __esm({
8411
8443
  });
8412
8444
 
8413
8445
  // packages/core/src/cohort/compare.ts
8414
- function compareNumber(metric, label, baseline, candidate, higherIsWorse = true) {
8446
+ function compareNumber(metric, label, baseline, candidate, higherIsWorse = true, maxRelativeDelta) {
8415
8447
  if (baseline === void 0 && candidate === void 0) return void 0;
8416
8448
  const delta = baseline !== void 0 && candidate !== void 0 ? candidate - baseline : void 0;
8417
- const regression = delta !== void 0 && (higherIsWorse && delta > 0 || !higherIsWorse && delta < 0);
8449
+ let regression = delta !== void 0 && (higherIsWorse && delta > 0 || !higherIsWorse && delta < 0);
8450
+ if (regression && maxRelativeDelta !== void 0 && baseline !== void 0 && baseline !== 0 && delta !== void 0) {
8451
+ const relative = Math.abs(delta / baseline);
8452
+ if (relative <= maxRelativeDelta) regression = false;
8453
+ }
8418
8454
  return {
8419
8455
  metric,
8420
8456
  baseline,
@@ -8433,6 +8469,7 @@ function compareCohortAggregates(groups, options) {
8433
8469
  const baselineAgg = pickAggregate(groups, options.baseline, options.groupKey);
8434
8470
  const candidateAgg = pickAggregate(groups, options.candidate, options.groupKey);
8435
8471
  const comparisons = [];
8472
+ const tolerance = options.maxRelativeDelta;
8436
8473
  for (const metric of options.metrics) {
8437
8474
  switch (metric) {
8438
8475
  case "errorRate": {
@@ -8440,7 +8477,9 @@ function compareCohortAggregates(groups, options) {
8440
8477
  metric,
8441
8478
  "Error rate",
8442
8479
  baselineAgg?.errorRate,
8443
- candidateAgg?.errorRate
8480
+ candidateAgg?.errorRate,
8481
+ true,
8482
+ tolerance
8444
8483
  );
8445
8484
  if (item) comparisons.push(item);
8446
8485
  break;
@@ -8804,13 +8843,27 @@ async function analyzeCohort(runsInput, options) {
8804
8843
  ...new Set(groups.map((group) => group.groupKey))
8805
8844
  ].sort((a, b) => a.localeCompare(b));
8806
8845
  const items = [];
8846
+ const minSample = options.tolerance?.minSampleSize ?? 1;
8807
8847
  for (const groupKey of groupKeys) {
8848
+ const baselineGroup = groups.find(
8849
+ (group) => group.groupKey === groupKey && group.cohortLabel === options.baseline
8850
+ );
8851
+ const candidateGroup = groups.find(
8852
+ (group) => group.groupKey === groupKey && group.cohortLabel === options.candidate
8853
+ );
8854
+ if (baselineGroup && baselineGroup.runCount < minSample || candidateGroup && candidateGroup.runCount < minSample) {
8855
+ warnings.push(
8856
+ `Insufficient sample for group ${groupKey}: need at least ${minSample} run(s) per cohort.`
8857
+ );
8858
+ continue;
8859
+ }
8808
8860
  items.push(
8809
8861
  ...compareCohortAggregates(groups, {
8810
8862
  baseline: options.baseline,
8811
8863
  candidate: options.candidate,
8812
8864
  metrics,
8813
- groupKey
8865
+ groupKey,
8866
+ maxRelativeDelta: options.tolerance?.maxRelativeDelta
8814
8867
  })
8815
8868
  );
8816
8869
  }
@@ -9259,6 +9312,9 @@ function validateOptions(options) {
9259
9312
  if (options.maxErrorRate !== void 0 && options.maxErrorRate < 0) {
9260
9313
  errors.push("--max-error-rate must be a non-negative percentage.");
9261
9314
  }
9315
+ if (options.maxErrorRate !== void 0 && options.maxErrorRate > 100) {
9316
+ errors.push("--max-error-rate must be at most 100.");
9317
+ }
9262
9318
  if (options.maxP95DurationMs !== void 0 && options.maxP95DurationMs < 0) {
9263
9319
  errors.push("--max-p95-duration must be a non-negative millisecond value.");
9264
9320
  }
@@ -10620,15 +10676,18 @@ function indexStatus(dbPath) {
10620
10676
  try {
10621
10677
  const runs = db.prepare(`SELECT COUNT(*) AS c FROM runs`).get().c;
10622
10678
  const steps = db.prepare(`SELECT COUNT(*) AS c FROM steps`).get().c;
10679
+ const builtAt = meta(db, META_KEYS.builtAt);
10680
+ const refreshStatus = runs === 0 ? "fallback-scan" : builtAt ? "complete" : "partial";
10623
10681
  return {
10624
10682
  dbPath,
10625
10683
  exists: true,
10626
10684
  healthy: true,
10627
- builtAt: meta(db, META_KEYS.builtAt),
10685
+ builtAt,
10628
10686
  sourceDir: meta(db, META_KEYS.sourceDir),
10629
10687
  schemaVersion: meta(db, META_KEYS.schemaVersion),
10630
10688
  runs,
10631
- steps
10689
+ steps,
10690
+ refreshStatus
10632
10691
  };
10633
10692
  } finally {
10634
10693
  db.close();
@@ -10729,7 +10788,7 @@ var init_src = __esm({
10729
10788
  });
10730
10789
 
10731
10790
  // package.json
10732
- var version = "6.3.0";
10791
+ var version = "6.4.1";
10733
10792
 
10734
10793
  // packages/cli/src/list.ts
10735
10794
  init_advanced();
@@ -17021,27 +17080,36 @@ var viewerIndexHtml = `<!DOCTYPE html>
17021
17080
  const params = new URLSearchParams(location.search);
17022
17081
  const mode = params.get("mode") || "traces";
17023
17082
 
17083
+ function escapeHtml(value) {
17084
+ return String(value ?? "")
17085
+ .replaceAll("&", "&amp;")
17086
+ .replaceAll("<", "&lt;")
17087
+ .replaceAll(">", "&gt;")
17088
+ .replaceAll('"', "&quot;")
17089
+ .replaceAll("'", "&#39;");
17090
+ }
17091
+
17024
17092
  function renderSuite(data) {
17025
17093
  const rows = data.cases.map((c) =>
17026
- '<tr><td>' + c.id + '</td><td class="' + c.status + '">' + c.status +
17027
- '</td><td>' + (c.message || '') + '</td><td>' + c.toolPath.join(' \u2192 ') +
17028
- '</td><td>' + c.observations.map((o) => o.name + ':' + o.status).join(', ') + '</td></tr>'
17094
+ '<tr><td>' + escapeHtml(c.id) + '</td><td class="' + escapeHtml(c.status) + '">' + escapeHtml(c.status) +
17095
+ '</td><td>' + escapeHtml(c.message || '') + '</td><td>' + escapeHtml(c.toolPath.join(' \u2192 ')) +
17096
+ '</td><td>' + escapeHtml(c.observations.map((o) => o.name + ':' + o.status).join(', ')) + '</td></tr>'
17029
17097
  ).join('');
17030
17098
  const failed = data.cases.filter((c) => c.status !== 'pass');
17031
17099
  const detail = failed.map((c) => {
17032
- let html = '<h3>Case ' + c.id + '</h3><ul>';
17033
- if (c.failureDiff) html += '<li>Diff errors: ' + c.failureDiff.summary.errors + '</li>';
17034
- if (c.timeline) html += '<li>Timeline steps: ' + c.timeline.entries.length + '</li>';
17035
- html += '<li>Diagnostics: ' + c.diagnostics.map((d) => d.message).join('; ') + '</li></ul>';
17100
+ let html = '<h3>Case ' + escapeHtml(c.id) + '</h3><ul>';
17101
+ if (c.failureDiff) html += '<li>Diff errors: ' + escapeHtml(c.failureDiff.summary.errors) + '</li>';
17102
+ if (c.timeline) html += '<li>Timeline steps: ' + escapeHtml(c.timeline.entries.length) + '</li>';
17103
+ html += '<li>Diagnostics: ' + escapeHtml(c.diagnostics.map((d) => d.message).join('; ')) + '</li></ul>';
17036
17104
  return html;
17037
17105
  }).join('');
17038
- return '<section><h2>Suite: ' + data.suiteName + ' (' + data.status + ')</h2>' +
17039
- '<p>Passed ' + data.summary.passed + ', failed ' + data.summary.failed + '</p>' +
17106
+ return '<section><h2>Suite: ' + escapeHtml(data.suiteName) + ' (' + escapeHtml(data.status) + ')</h2>' +
17107
+ '<p>Passed ' + escapeHtml(data.summary.passed) + ', failed ' + escapeHtml(data.summary.failed) + '</p>' +
17040
17108
  '<table><thead><tr><th>Case</th><th>Status</th><th>Message</th><th>Tool path</th><th>Observations</th></tr></thead><tbody>' +
17041
17109
  rows + '</tbody></table>' +
17042
17110
  '<section><h2>Failure detail</h2>' + (detail || '<p class="pass">No failures</p>') + '</section>' +
17043
- '<section><h2>CI artifacts</h2><p>' + (data.ciArtifactsDir || 'n/a') + '</p></section>' +
17044
- '<section><h2>Bundle export</h2><p>' + data.bundleExportHint + '</p></section>';
17111
+ '<section><h2>CI artifacts</h2><p>' + escapeHtml(data.ciArtifactsDir || 'n/a') + '</p></section>' +
17112
+ '<section><h2>Bundle export</h2><p>' + escapeHtml(data.bundleExportHint) + '</p></section>';
17045
17113
  }
17046
17114
 
17047
17115
  async function load() {
@@ -17057,8 +17125,8 @@ var viewerIndexHtml = `<!DOCTYPE html>
17057
17125
  if (mode === "workspace") {
17058
17126
  nav.innerHTML = '<a href="/api/workspace">/api/workspace</a>';
17059
17127
  const data = await fetch("/api/workspace").then((r) => r.json());
17060
- content.innerHTML = '<section><h2>Workspace: ' + (data.project || 'workspace') + '</h2>' +
17061
- '<p>Runs: ' + data.runs.length + '</p><pre>' + JSON.stringify(data, null, 2) + '</pre></section>';
17128
+ content.innerHTML = '<section><h2>Workspace: ' + escapeHtml(data.project || 'workspace') + '</h2>' +
17129
+ '<p>Runs: ' + escapeHtml(data.runs.length) + '</p><pre>' + escapeHtml(JSON.stringify(data, null, 2)) + '</pre></section>';
17062
17130
  return;
17063
17131
  }
17064
17132
  nav.innerHTML = '<a href="/api/traces">/api/traces</a> \xB7 <a href="/api/sessions">/api/sessions</a>';
@@ -19612,7 +19680,7 @@ async function resolveOutputDir(options, runIds, cwd) {
19612
19680
  const manifest = await readWorkspaceManifestFile(location);
19613
19681
  if (manifest.ok && manifest.manifest) {
19614
19682
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
19615
- const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
19683
+ const label = runIds.length === 1 ? sanitizeBundleRunId(runIds[0]) : `multi-${runIds.length}`;
19616
19684
  return resolveInsideWorkspace(
19617
19685
  location.workspaceDir,
19618
19686
  path26__default.default.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
@@ -19623,14 +19691,14 @@ async function resolveOutputDir(options, runIds, cwd) {
19623
19691
  return defaultBundleOutputPath(runIds);
19624
19692
  }
19625
19693
  async function writeBundleFile(outputDir, relativePath, content, files) {
19626
- const outPath = path26__default.default.join(outputDir, relativePath);
19694
+ const outPath = assertBundlePathContained(outputDir, relativePath);
19627
19695
  await promises.mkdir(path26__default.default.dirname(outPath), { recursive: true });
19628
19696
  await promises.writeFile(outPath, content, "utf-8");
19629
19697
  files.push(relativePath);
19630
19698
  }
19631
19699
  function renderBundleIndexHtml(parts) {
19632
19700
  const links = parts.runIds.map(
19633
- (runId) => `<li><a href="assets/runs/${escapeHtml3(runId)}.html">${escapeHtml3(runId)}</a></li>`
19701
+ (runId) => `<li><a href="${escapeHtml3(bundleRunAssetRelativePath(runId, ".html"))}">${escapeHtml3(runId)}</a></li>`
19634
19702
  ).join("");
19635
19703
  const sections = parts.runIds.map((runId) => {
19636
19704
  const html = parts.reports.get(runId) ?? "";
@@ -19798,11 +19866,16 @@ async function bundleCommand(runIdArg, options = {}) {
19798
19866
  await promises.mkdir(outputDir, { recursive: true });
19799
19867
  for (const runId of resolveResult.runIds) {
19800
19868
  const jsonl = redactedJsonlByRun.get(runId) ?? "";
19801
- await writeBundleFile(outputDir, `assets/runs/${runId}.jsonl`, jsonl, files);
19869
+ await writeBundleFile(
19870
+ outputDir,
19871
+ bundleRunAssetRelativePath(runId, ".jsonl"),
19872
+ jsonl,
19873
+ files
19874
+ );
19802
19875
  const html = htmlByRun.get(runId) ?? "";
19803
19876
  await writeBundleFile(
19804
19877
  outputDir,
19805
- `assets/runs/${runId}.html`,
19878
+ bundleRunAssetRelativePath(runId, ".html"),
19806
19879
  runReportWrap(html, runId),
19807
19880
  files
19808
19881
  );
@@ -20339,7 +20412,7 @@ main().catch((error) => {
20339
20412
  return `import { observe } from "agent-inspect";
20340
20413
 
20341
20414
  class DemoAgent {
20342
- async run(input: { question: string }) {
20415
+ async run(input) {
20343
20416
  return { answer: \`Echo: \${input.question}\` };
20344
20417
  }
20345
20418
  }
@@ -20760,6 +20833,23 @@ var PLUGIN_NAME_PREFIXES = [
20760
20833
  "agent-inspect-check-",
20761
20834
  "agent-inspect-importer-"
20762
20835
  ];
20836
+ var CAPTURE_MODES = /* @__PURE__ */ new Set(["metadata-only", "preview", "full"]);
20837
+ function parseStrictBoolean(value, label, errors) {
20838
+ if (value === void 0) return void 0;
20839
+ if (value === true) return true;
20840
+ if (value === false) return false;
20841
+ errors.push(`${label} must be a boolean true or false, not ${JSON.stringify(value)}`);
20842
+ return void 0;
20843
+ }
20844
+ function parseCaptureMode(value, errors) {
20845
+ if (value === void 0) return void 0;
20846
+ const mode = String(value);
20847
+ if (!CAPTURE_MODES.has(mode)) {
20848
+ errors.push(`privacy.captureMode must be one of: metadata-only, preview, full`);
20849
+ return void 0;
20850
+ }
20851
+ return mode;
20852
+ }
20763
20853
  function isPlainObject2(value) {
20764
20854
  return typeof value === "object" && value !== null && !Array.isArray(value);
20765
20855
  }
@@ -20808,20 +20898,35 @@ function parsePluginManifest(input3) {
20808
20898
  errors.push("privacy must be an object");
20809
20899
  } else {
20810
20900
  privacy = {};
20811
- if (input3.privacy.captureMode !== void 0) {
20812
- privacy.captureMode = String(input3.privacy.captureMode);
20813
- }
20814
- if (input3.privacy.networkAllowed !== void 0) {
20815
- privacy.networkAllowed = Boolean(input3.privacy.networkAllowed);
20816
- }
20817
- if (input3.privacy.uploadAllowed !== void 0) {
20818
- privacy.uploadAllowed = Boolean(input3.privacy.uploadAllowed);
20819
- }
20820
- if (input3.privacy.redactionDocumented !== void 0) {
20821
- privacy.redactionDocumented = Boolean(input3.privacy.redactionDocumented);
20901
+ const captureMode = parseCaptureMode(input3.privacy.captureMode, errors);
20902
+ if (captureMode !== void 0) privacy.captureMode = captureMode;
20903
+ const networkAllowed = parseStrictBoolean(
20904
+ input3.privacy.networkAllowed,
20905
+ "privacy.networkAllowed",
20906
+ errors
20907
+ );
20908
+ if (networkAllowed !== void 0) privacy.networkAllowed = networkAllowed;
20909
+ const uploadAllowed = parseStrictBoolean(
20910
+ input3.privacy.uploadAllowed,
20911
+ "privacy.uploadAllowed",
20912
+ errors
20913
+ );
20914
+ if (uploadAllowed !== void 0) privacy.uploadAllowed = uploadAllowed;
20915
+ const redactionDocumented = parseStrictBoolean(
20916
+ input3.privacy.redactionDocumented,
20917
+ "privacy.redactionDocumented",
20918
+ errors
20919
+ );
20920
+ if (redactionDocumented !== void 0) {
20921
+ privacy.redactionDocumented = redactionDocumented;
20822
20922
  }
20823
- if (input3.privacy.frameworkDepsPackageScoped !== void 0) {
20824
- privacy.frameworkDepsPackageScoped = Boolean(input3.privacy.frameworkDepsPackageScoped);
20923
+ const frameworkDepsPackageScoped = parseStrictBoolean(
20924
+ input3.privacy.frameworkDepsPackageScoped,
20925
+ "privacy.frameworkDepsPackageScoped",
20926
+ errors
20927
+ );
20928
+ if (frameworkDepsPackageScoped !== void 0) {
20929
+ privacy.frameworkDepsPackageScoped = frameworkDepsPackageScoped;
20825
20930
  }
20826
20931
  if (privacy.networkAllowed === true || privacy.uploadAllowed === true) {
20827
20932
  warnings.push("plugin declares network or upload \u2014 review before install");
@@ -21739,6 +21844,12 @@ function parseRate(value) {
21739
21844
  if (!Number.isFinite(parsed)) {
21740
21845
  throw new Error(`Invalid --max-error-rate: ${value}`);
21741
21846
  }
21847
+ if (parsed < 0) {
21848
+ throw new Error("--max-error-rate must be a non-negative percentage.");
21849
+ }
21850
+ if (parsed > 100) {
21851
+ throw new Error("--max-error-rate must be at most 100.");
21852
+ }
21742
21853
  return parsed;
21743
21854
  }
21744
21855
  function parseDuration2(value) {