agent-inspect 6.17.8 → 6.18.0

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.
@@ -4,7 +4,7 @@
4
4
  var crypto = require('crypto');
5
5
  var promises = require('fs/promises');
6
6
  var os = require('os');
7
- var path32 = require('path');
7
+ var path33 = require('path');
8
8
  var async_hooks = require('async_hooks');
9
9
  var process3 = require('process');
10
10
  var tty = require('tty');
@@ -22,7 +22,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
22
 
23
23
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
24
24
  var os__default = /*#__PURE__*/_interopDefault(os);
25
- var path32__default = /*#__PURE__*/_interopDefault(path32);
25
+ var path33__default = /*#__PURE__*/_interopDefault(path33);
26
26
  var process3__default = /*#__PURE__*/_interopDefault(process3);
27
27
  var tty__default = /*#__PURE__*/_interopDefault(tty);
28
28
 
@@ -866,7 +866,7 @@ function getDefaultTraceDir() {
866
866
  if (typeof home !== "string" || home.trim() === "") {
867
867
  return FALLBACK_TRACE_DIR;
868
868
  }
869
- return path32__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
869
+ return path33__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
870
870
  } catch {
871
871
  return FALLBACK_TRACE_DIR;
872
872
  }
@@ -874,11 +874,11 @@ function getDefaultTraceDir() {
874
874
  function getTraceFilePath(runId, traceDir) {
875
875
  const baseDir = traceDir ?? getDefaultTraceDir();
876
876
  let safeId = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
877
- safeId = path32__default.default.basename(safeId);
877
+ safeId = path33__default.default.basename(safeId);
878
878
  if (safeId === "" || safeId === "." || safeId === "..") {
879
879
  safeId = "run_unknown";
880
880
  }
881
- return path32__default.default.join(baseDir, `${safeId}.jsonl`);
881
+ return path33__default.default.join(baseDir, `${safeId}.jsonl`);
882
882
  }
883
883
  function formatError(error) {
884
884
  if (error instanceof Error) {
@@ -935,7 +935,7 @@ var init_utils = __esm({
935
935
  init_duration();
936
936
  DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
937
937
  RUNS_DIR_NAME = "runs";
938
- FALLBACK_TRACE_DIR = path32__default.default.join(
938
+ FALLBACK_TRACE_DIR = path33__default.default.join(
939
939
  os__default.default.tmpdir(),
940
940
  "agent-inspect",
941
941
  RUNS_DIR_NAME
@@ -1306,6 +1306,14 @@ var init_inspector = __esm({
1306
1306
  }
1307
1307
  });
1308
1308
 
1309
+ // packages/core/src/adapters/preview-capture.ts
1310
+ var init_preview_capture = __esm({
1311
+ "packages/core/src/adapters/preview-capture.ts"() {
1312
+ init_redactor();
1313
+ init_redaction_profiles();
1314
+ }
1315
+ });
1316
+
1309
1317
  // node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
1310
1318
  function assembleStyles() {
1311
1319
  const codes = /* @__PURE__ */ new Map();
@@ -1901,7 +1909,7 @@ var init_trace_directory = __esm({
1901
1909
  this.#dir = resolveTraceDir(options);
1902
1910
  }
1903
1911
  getPath(filename) {
1904
- return filename ? path32__default.default.join(this.#dir, filename) : this.#dir;
1912
+ return filename ? path33__default.default.join(this.#dir, filename) : this.#dir;
1905
1913
  }
1906
1914
  async list() {
1907
1915
  try {
@@ -1930,7 +1938,7 @@ function parseIsoToMs2(value) {
1930
1938
  }
1931
1939
  async function extractMetadata(filePath, _quickScan) {
1932
1940
  const stats = await promises.stat(filePath);
1933
- let runIdFromFile = path32__default.default.basename(filePath);
1941
+ let runIdFromFile = path33__default.default.basename(filePath);
1934
1942
  if (runIdFromFile.endsWith(".jsonl")) {
1935
1943
  runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
1936
1944
  }
@@ -3800,12 +3808,12 @@ function buildCriticalPath(runs, handoffs) {
3800
3808
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
3801
3809
  );
3802
3810
  const ordered = [...runs].sort(compareRuns);
3803
- const path43 = [];
3811
+ const path44 = [];
3804
3812
  const visited = /* @__PURE__ */ new Set();
3805
3813
  const pushRun = (run, confidence, source) => {
3806
3814
  if (visited.has(run.runId)) return;
3807
3815
  visited.add(run.runId);
3808
- path43.push({
3816
+ path44.push({
3809
3817
  runId: run.runId,
3810
3818
  name: run.name,
3811
3819
  startedAt: run.startedAt,
@@ -3830,7 +3838,7 @@ function buildCriticalPath(runs, handoffs) {
3830
3838
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
3831
3839
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
3832
3840
  }
3833
- return path43;
3841
+ return path44;
3834
3842
  }
3835
3843
  function metaRunIdMatches(run, token, runById) {
3836
3844
  const meta2 = extractSessionWorkflowMetadata(run.metadata);
@@ -4159,7 +4167,7 @@ var init_summary = __esm({
4159
4167
  });
4160
4168
  function sanitizeBundleRunId(runId) {
4161
4169
  let safe = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
4162
- safe = path32__default.default.basename(safe);
4170
+ safe = path33__default.default.basename(safe);
4163
4171
  safe = safe.replace(/[^a-zA-Z0-9._-]+/g, "_");
4164
4172
  if (safe === "" || safe === "." || safe === "..") {
4165
4173
  safe = "run_unknown";
@@ -4169,12 +4177,12 @@ function sanitizeBundleRunId(runId) {
4169
4177
  function bundleRunAssetRelativePath(runId, extension) {
4170
4178
  const safe = sanitizeBundleRunId(runId);
4171
4179
  const ext = extension.startsWith(".") ? extension : `.${extension}`;
4172
- return path32__default.default.posix.join("assets", "runs", `${safe}${ext}`);
4180
+ return path33__default.default.posix.join("assets", "runs", `${safe}${ext}`);
4173
4181
  }
4174
4182
  function assertBundlePathContained(outputDir, relativePath) {
4175
- const base = path32__default.default.resolve(outputDir);
4176
- const resolved = path32__default.default.resolve(base, relativePath);
4177
- if (resolved !== base && !resolved.startsWith(base + path32__default.default.sep)) {
4183
+ const base = path33__default.default.resolve(outputDir);
4184
+ const resolved = path33__default.default.resolve(base, relativePath);
4185
+ if (resolved !== base && !resolved.startsWith(base + path33__default.default.sep)) {
4178
4186
  throw new Error(`Bundle path escapes output directory: ${relativePath}`);
4179
4187
  }
4180
4188
  return resolved;
@@ -4184,7 +4192,7 @@ function normalizeBundleOutputPath(out, options) {
4184
4192
  if (trimmed === "") {
4185
4193
  throw new Error("--out requires a non-empty path.");
4186
4194
  }
4187
- const resolved = path32__default.default.resolve(trimmed);
4195
+ const resolved = path33__default.default.resolve(trimmed);
4188
4196
  if (options?.preserveZipExtension !== true && resolved.toLowerCase().endsWith(".zip")) {
4189
4197
  return resolved.slice(0, -4);
4190
4198
  }
@@ -4193,7 +4201,7 @@ function normalizeBundleOutputPath(out, options) {
4193
4201
  function defaultBundleOutputPath(runIds) {
4194
4202
  const label = runIds.length === 1 ? sanitizeBundleRunId(runIds[0]) : `multi-${runIds.length}`;
4195
4203
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4196
- return path32__default.default.resolve(`agent-inspect-bundle-${label}-${stamp}`);
4204
+ return path33__default.default.resolve(`agent-inspect-bundle-${label}-${stamp}`);
4197
4205
  }
4198
4206
  var init_paths = __esm({
4199
4207
  "packages/core/src/bundle/paths.ts"() {
@@ -4256,7 +4264,7 @@ function assertEvidenceRelativePath(relativePath) {
4256
4264
  throw new Error("Evidence file path must be a non-empty relative path.");
4257
4265
  }
4258
4266
  const trimmed = relativePath.trim().replaceAll("\\", "/");
4259
- if (path32__default.default.isAbsolute(trimmed) || trimmed.startsWith("/")) {
4267
+ if (path33__default.default.isAbsolute(trimmed) || trimmed.startsWith("/")) {
4260
4268
  throw new Error(`Evidence file path must be relative: ${relativePath}`);
4261
4269
  }
4262
4270
  const parts = trimmed.split("/").filter((part) => part !== "");
@@ -5123,13 +5131,13 @@ function pairSteps(left, right) {
5123
5131
  return pairs;
5124
5132
  }
5125
5133
  function compareLeafSteps(L, R, segments, opts, out) {
5126
- const path43 = buildPath(segments);
5134
+ const path44 = buildPath(segments);
5127
5135
  if (L.name !== R.name) {
5128
5136
  out.push({
5129
5137
  kind: "structure",
5130
5138
  severity: "warning",
5131
5139
  message: "Step name differs",
5132
- path: path43,
5140
+ path: path44,
5133
5141
  left: L.name,
5134
5142
  right: R.name
5135
5143
  });
@@ -5139,7 +5147,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
5139
5147
  kind: "step-type",
5140
5148
  severity: "warning",
5141
5149
  message: "Step type differs",
5142
- path: path43,
5150
+ path: path44,
5143
5151
  left: L.type,
5144
5152
  right: R.type
5145
5153
  });
@@ -5149,7 +5157,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
5149
5157
  kind: "step-status",
5150
5158
  severity: "warning",
5151
5159
  message: "Step status differs",
5152
- path: path43,
5160
+ path: path44,
5153
5161
  left: L.status,
5154
5162
  right: R.status
5155
5163
  });
@@ -5161,7 +5169,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
5161
5169
  kind: "error",
5162
5170
  severity: "error",
5163
5171
  message: "Step error message differs",
5164
- path: path43,
5172
+ path: path44,
5165
5173
  left: le || void 0,
5166
5174
  right: re || void 0
5167
5175
  });
@@ -5179,7 +5187,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
5179
5187
  kind: "duration",
5180
5188
  severity: "info",
5181
5189
  message: "Step duration differs",
5182
- path: path43,
5190
+ path: path44,
5183
5191
  left: ld,
5184
5192
  right: rd
5185
5193
  });
@@ -5192,7 +5200,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
5192
5200
  kind: "metadata",
5193
5201
  severity: "info",
5194
5202
  message: "Step metadata differs",
5195
- path: path43,
5203
+ path: path44,
5196
5204
  left: L.metadata,
5197
5205
  right: R.metadata
5198
5206
  });
@@ -5204,7 +5212,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
5204
5212
  kind: "output",
5205
5213
  severity: "info",
5206
5214
  message: "Output preview differs",
5207
- path: path43,
5215
+ path: path44,
5208
5216
  left: L.outputPreview,
5209
5217
  right: R.outputPreview
5210
5218
  });
@@ -5371,11 +5379,11 @@ var init_engine = __esm({
5371
5379
  });
5372
5380
 
5373
5381
  // packages/core/src/diff/renderer.ts
5374
- function formatPath(path43) {
5375
- if (path43 === void 0 || path43.path.length === 0) {
5382
+ function formatPath(path44) {
5383
+ if (path44 === void 0 || path44.path.length === 0) {
5376
5384
  return "(run)";
5377
5385
  }
5378
- return path43.path.map((s) => s.name).join(" > ");
5386
+ return path44.path.map((s) => s.name).join(" > ");
5379
5387
  }
5380
5388
  function formatValue(v, verbose) {
5381
5389
  if (v === void 0) return "(undefined)";
@@ -5755,11 +5763,11 @@ async function listFilesRecursive(root) {
5755
5763
  async function walk(dir) {
5756
5764
  const entries = await promises.readdir(dir, { withFileTypes: true });
5757
5765
  for (const entry of entries) {
5758
- const abs = path32__default.default.join(dir, entry.name);
5766
+ const abs = path33__default.default.join(dir, entry.name);
5759
5767
  if (entry.isDirectory()) {
5760
5768
  await walk(abs);
5761
5769
  } else if (entry.isFile()) {
5762
- const rel = path32__default.default.relative(root, abs).split(path32__default.default.sep).join("/");
5770
+ const rel = path33__default.default.relative(root, abs).split(path33__default.default.sep).join("/");
5763
5771
  out.push(rel);
5764
5772
  }
5765
5773
  }
@@ -5769,7 +5777,7 @@ async function listFilesRecursive(root) {
5769
5777
  }
5770
5778
  async function verifyEvidenceDirectory(rootPath, options = {}) {
5771
5779
  const unexpectedMode = options.unexpectedFiles ?? "fail";
5772
- const root = path32__default.default.resolve(rootPath);
5780
+ const root = path33__default.default.resolve(rootPath);
5773
5781
  const issues = [];
5774
5782
  let rootStat;
5775
5783
  try {
@@ -5799,7 +5807,7 @@ async function verifyEvidenceDirectory(rootPath, options = {}) {
5799
5807
  checkedFiles: 0
5800
5808
  };
5801
5809
  }
5802
- const manifestPath = path32__default.default.join(root, EVIDENCE_MANIFEST_FILENAME);
5810
+ const manifestPath = path33__default.default.join(root, EVIDENCE_MANIFEST_FILENAME);
5803
5811
  let manifestText;
5804
5812
  try {
5805
5813
  manifestText = await promises.readFile(manifestPath, "utf-8");
@@ -5885,7 +5893,7 @@ async function verifyEvidenceDirectory(rootPath, options = {}) {
5885
5893
  continue;
5886
5894
  }
5887
5895
  listed.add(rel);
5888
- const abs = path32__default.default.join(root, ...rel.split("/"));
5896
+ const abs = path33__default.default.join(root, ...rel.split("/"));
5889
5897
  let bytes;
5890
5898
  try {
5891
5899
  bytes = await promises.readFile(abs);
@@ -6200,7 +6208,7 @@ function normalizeSuiteConfig(value) {
6200
6208
  }
6201
6209
  async function validateSuiteConfig(config, options) {
6202
6210
  const diagnostics = [];
6203
- const tracesDir = path32__default.default.resolve(options.configDir, config.traces);
6211
+ const tracesDir = path33__default.default.resolve(options.configDir, config.traces);
6204
6212
  try {
6205
6213
  await promises.access(tracesDir);
6206
6214
  } catch {
@@ -6210,7 +6218,7 @@ async function validateSuiteConfig(config, options) {
6210
6218
  }
6211
6219
  for (const suiteCase of config.cases) {
6212
6220
  if (suiteCase.input !== void 0) {
6213
- const inputPath = path32__default.default.resolve(options.configDir, suiteCase.input);
6221
+ const inputPath = path33__default.default.resolve(options.configDir, suiteCase.input);
6214
6222
  try {
6215
6223
  await promises.access(inputPath);
6216
6224
  } catch {
@@ -6243,12 +6251,12 @@ async function fileExists(filePath) {
6243
6251
  }
6244
6252
  }
6245
6253
  async function resolveSuiteConfigPath(options = {}) {
6246
- const cwd = path32__default.default.resolve(options.cwd ?? process.cwd());
6254
+ const cwd = path33__default.default.resolve(options.cwd ?? process.cwd());
6247
6255
  if (options.configPath !== void 0 && options.configPath.trim() !== "") {
6248
- return path32__default.default.resolve(cwd, options.configPath.trim());
6256
+ return path33__default.default.resolve(cwd, options.configPath.trim());
6249
6257
  }
6250
6258
  for (const name of DEFAULT_SUITE_CONFIG_NAMES) {
6251
- const candidate = path32__default.default.join(cwd, name);
6259
+ const candidate = path33__default.default.join(cwd, name);
6252
6260
  if (await fileExists(candidate)) return candidate;
6253
6261
  }
6254
6262
  throw new Error(
@@ -6265,7 +6273,7 @@ async function loadSuiteConfig(options = {}) {
6265
6273
  diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
6266
6274
  });
6267
6275
  }
6268
- const extension = path32__default.default.extname(configPath);
6276
+ const extension = path33__default.default.extname(configPath);
6269
6277
  if (TS_CONFIG_EXTENSIONS.has(extension)) {
6270
6278
  const message = "TypeScript suite configs require an explicit precompiled JavaScript config or future --config-loader support.";
6271
6279
  throw Object.assign(new Error(message), {
@@ -6290,7 +6298,7 @@ async function loadSuiteConfig(options = {}) {
6290
6298
  return {
6291
6299
  config,
6292
6300
  configPath,
6293
- configDir: path32__default.default.dirname(configPath)
6301
+ configDir: path33__default.default.dirname(configPath)
6294
6302
  };
6295
6303
  } catch (error) {
6296
6304
  const message = error instanceof Error ? error.message : String(error);
@@ -6340,7 +6348,7 @@ async function exists(filePath) {
6340
6348
  }
6341
6349
  async function resolveSuiteCaseTrace(suiteCase, options) {
6342
6350
  if (suiteCase.trace !== void 0) {
6343
- const tracePath = path32__default.default.resolve(options.configDir, suiteCase.trace);
6351
+ const tracePath = path33__default.default.resolve(options.configDir, suiteCase.trace);
6344
6352
  if (await exists(tracePath)) {
6345
6353
  return { caseId: suiteCase.id, tracePath, missing: false };
6346
6354
  }
@@ -6356,7 +6364,7 @@ async function resolveSuiteCaseTrace(suiteCase, options) {
6356
6364
  if (await exists(directPath)) {
6357
6365
  return { caseId: suiteCase.id, tracePath: directPath, runId: runKey, missing: false };
6358
6366
  }
6359
- const nestedPath = path32__default.default.join(options.tracesDir, `${path32__default.default.basename(runKey)}.jsonl`);
6367
+ const nestedPath = path33__default.default.join(options.tracesDir, `${path33__default.default.basename(runKey)}.jsonl`);
6360
6368
  if (await exists(nestedPath)) {
6361
6369
  return { caseId: suiteCase.id, tracePath: nestedPath, runId: runKey, missing: false };
6362
6370
  }
@@ -7106,7 +7114,7 @@ function stripPrefix(name, prefixes) {
7106
7114
  }
7107
7115
  return name;
7108
7116
  }
7109
- function eventEvidence(event, path43) {
7117
+ function eventEvidence(event, path44) {
7110
7118
  return {
7111
7119
  runId: event.runId,
7112
7120
  eventId: event.eventId,
@@ -7116,7 +7124,7 @@ function eventEvidence(event, path43) {
7116
7124
  kind: event.kind,
7117
7125
  name: event.name,
7118
7126
  status: event.status,
7119
- ...path43 ? { path: path43 } : {}
7127
+ ...path44 ? { path: path44 } : {}
7120
7128
  };
7121
7129
  }
7122
7130
  function runEvidence(run) {
@@ -7192,9 +7200,9 @@ function eventEndMs(event) {
7192
7200
  function normalizedKey(value) {
7193
7201
  return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
7194
7202
  }
7195
- function lastPathSegment(path43) {
7196
- const parts = path43.split(".");
7197
- return parts[parts.length - 1] ?? path43;
7203
+ function lastPathSegment(path44) {
7204
+ const parts = path44.split(".");
7205
+ return parts[parts.length - 1] ?? path44;
7198
7206
  }
7199
7207
  function valueType(value) {
7200
7208
  if (Array.isArray(value)) return "array";
@@ -7208,12 +7216,12 @@ function serializedByteLength(value) {
7208
7216
  return void 0;
7209
7217
  }
7210
7218
  }
7211
- function pushValueEntries(entries, event, value, path43, key, depth = 0) {
7212
- entries.push({ event, path: path43, key, value });
7219
+ function pushValueEntries(entries, event, value, path44, key, depth = 0) {
7220
+ entries.push({ event, path: path44, key, value });
7213
7221
  if (depth >= 8) return;
7214
7222
  if (Array.isArray(value)) {
7215
7223
  for (const [index, item] of value.entries()) {
7216
- pushValueEntries(entries, event, item, `${path43}.${index}`, String(index), depth + 1);
7224
+ pushValueEntries(entries, event, item, `${path44}.${index}`, String(index), depth + 1);
7217
7225
  }
7218
7226
  return;
7219
7227
  }
@@ -7223,7 +7231,7 @@ function pushValueEntries(entries, event, value, path43, key, depth = 0) {
7223
7231
  entries,
7224
7232
  event,
7225
7233
  value[nestedKey],
7226
- `${path43}.${nestedKey}`,
7234
+ `${path44}.${nestedKey}`,
7227
7235
  nestedKey,
7228
7236
  depth + 1
7229
7237
  );
@@ -7262,18 +7270,18 @@ function isRawContentKey(key, forbiddenKeys) {
7262
7270
  const normalized = normalizedKey(key);
7263
7271
  return forbiddenKeys.some((forbidden) => normalized === normalizedKey(forbidden));
7264
7272
  }
7265
- function isSafeRawContentMetricPath(path43, key, safePathPrefixes) {
7266
- const leaf = normalizedKey(key ?? lastPathSegment(path43));
7273
+ function isSafeRawContentMetricPath(path44, key, safePathPrefixes) {
7274
+ const leaf = normalizedKey(key ?? lastPathSegment(path44));
7267
7275
  if (!SAFE_USAGE_LEAF_KEYS.has(leaf)) return false;
7268
- const parts = path43.split(".").filter(Boolean);
7276
+ const parts = path44.split(".").filter(Boolean);
7269
7277
  if (parts.length < 2) return false;
7270
7278
  const parent = parts[parts.length - 2] ?? "";
7271
7279
  const parentNorm = normalizedKey(parent);
7272
7280
  return safePathPrefixes.some((prefix) => parentNorm === normalizedKey(prefix));
7273
7281
  }
7274
- function isRawContentPath(path43, key, forbiddenKeys, safePathPrefixes) {
7275
- if (isSafeRawContentMetricPath(path43, key, safePathPrefixes)) return false;
7276
- return isRawContentKey(key ?? lastPathSegment(path43), forbiddenKeys);
7282
+ function isRawContentPath(path44, key, forbiddenKeys, safePathPrefixes) {
7283
+ if (isSafeRawContentMetricPath(path44, key, safePathPrefixes)) return false;
7284
+ return isRawContentKey(key ?? lastPathSegment(path44), forbiddenKeys);
7277
7285
  }
7278
7286
  function parentMarkedUnresolved(event) {
7279
7287
  if (booleanAttr(event, [
@@ -7312,9 +7320,9 @@ function eventDurationMs(event) {
7312
7320
  }
7313
7321
  function treeShape(nodes) {
7314
7322
  const lines = [];
7315
- const visit = (node, path43) => {
7316
- lines.push(`${path43}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
7317
- node.children.forEach((child, index) => visit(child, `${path43}.${index}`));
7323
+ const visit = (node, path44) => {
7324
+ lines.push(`${path44}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
7325
+ node.children.forEach((child, index) => visit(child, `${path44}.${index}`));
7318
7326
  };
7319
7327
  nodes.forEach((node, index) => visit(node, String(index)));
7320
7328
  return lines;
@@ -7363,9 +7371,9 @@ function retrievalShape(context) {
7363
7371
  function guardrailShape(context) {
7364
7372
  return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
7365
7373
  }
7366
- function firstEvidenceForKind(context, kind, path43) {
7374
+ function firstEvidenceForKind(context, kind, path44) {
7367
7375
  const event = semanticEvents(context).find((candidate) => candidate.kind === kind);
7368
- return event ? [eventEvidence(event, path43)] : runEvidence(context.selectedRun);
7376
+ return event ? [eventEvidence(event, path44)] : runEvidence(context.selectedRun);
7369
7377
  }
7370
7378
  function baselineDiffFinding(message, evidence, expected, actual) {
7371
7379
  return failFinding("baseline.regression", message, evidence, expected, actual);
@@ -7715,13 +7723,13 @@ function createStructureCycleRule() {
7715
7723
  const seenCycles = /* @__PURE__ */ new Set();
7716
7724
  const findings = [];
7717
7725
  for (const event of [...semanticEvents(context)].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
7718
- const path43 = [];
7726
+ const path44 = [];
7719
7727
  const seenAt = /* @__PURE__ */ new Map();
7720
7728
  let current = event;
7721
7729
  while (current) {
7722
7730
  const existing = seenAt.get(current.eventId);
7723
7731
  if (existing !== void 0) {
7724
- const cycle = path43.slice(existing);
7732
+ const cycle = path44.slice(existing);
7725
7733
  const key = cycle.map((item) => item.eventId).sort().join("\0");
7726
7734
  if (!seenCycles.has(key)) {
7727
7735
  seenCycles.add(key);
@@ -7737,8 +7745,8 @@ function createStructureCycleRule() {
7737
7745
  }
7738
7746
  break;
7739
7747
  }
7740
- seenAt.set(current.eventId, path43.length);
7741
- path43.push(current);
7748
+ seenAt.set(current.eventId, path44.length);
7749
+ path44.push(current);
7742
7750
  current = current.parentId ? byId.get(current.parentId) : void 0;
7743
7751
  }
7744
7752
  }
@@ -9117,7 +9125,7 @@ function findReaderByFormat(format, readers) {
9117
9125
  }
9118
9126
  async function jsonlFilesInDirectory(dirPath) {
9119
9127
  const entries = await promises.readdir(dirPath, { withFileTypes: true });
9120
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path32__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
9128
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path33__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
9121
9129
  }
9122
9130
  async function resolveInput(input3) {
9123
9131
  const cached2 = resolvedInputCache.get(input3);
@@ -10824,7 +10832,7 @@ async function runSuiteCase(suiteCase, config, options) {
10824
10832
  async function runSuite(options = {}) {
10825
10833
  const startedAt = new Date(options.nowMs ?? Date.now()).toISOString();
10826
10834
  const { config, configPath, configDir } = await loadSuiteConfig(options);
10827
- const tracesDir = path32__default.default.resolve(configDir, config.traces);
10835
+ const tracesDir = path33__default.default.resolve(configDir, config.traces);
10828
10836
  const cases = [];
10829
10837
  const diagnostics = [];
10830
10838
  for (const suiteCase of config.cases) {
@@ -11851,7 +11859,7 @@ function validateOptions(options) {
11851
11859
  }
11852
11860
  function isConfigLoadError(error) {
11853
11861
  if (!(error instanceof Error)) return false;
11854
- const ext = path32__default.default.extname(error.message);
11862
+ const ext = path33__default.default.extname(error.message);
11855
11863
  if (error.message.includes("Unsupported suite config extension")) return true;
11856
11864
  if (error.message.includes("TypeScript suite configs require")) return true;
11857
11865
  if (error.message.includes("No suite config found")) return true;
@@ -12098,6 +12106,7 @@ var init_advanced = __esm({
12098
12106
  init_inspector();
12099
12107
  init_inspector_runtime();
12100
12108
  init_trace_event_safety();
12109
+ init_preview_capture();
12101
12110
  init_terminal();
12102
12111
  init_utils();
12103
12112
  init_types();
@@ -12245,8 +12254,8 @@ var init_load_sqlite = __esm({
12245
12254
  }
12246
12255
  });
12247
12256
  function resolveIndexDbPath(traceDir, dbPath) {
12248
- if (dbPath && dbPath.trim() !== "") return path32__default.default.resolve(dbPath);
12249
- return path32__default.default.join(path32__default.default.resolve(traceDir), INDEX_DB_FILENAME);
12257
+ if (dbPath && dbPath.trim() !== "") return path33__default.default.resolve(dbPath);
12258
+ return path33__default.default.join(path33__default.default.resolve(traceDir), INDEX_DB_FILENAME);
12250
12259
  }
12251
12260
  function str(value) {
12252
12261
  return typeof value === "string" && value !== "" ? value : null;
@@ -12338,7 +12347,7 @@ async function buildIndex(options = {}) {
12338
12347
  warnings.push(`index.unreadable: ${file}`);
12339
12348
  }
12340
12349
  }
12341
- await promises.mkdir(path32__default.default.dirname(dbPath), { recursive: true });
12350
+ await promises.mkdir(path33__default.default.dirname(dbPath), { recursive: true });
12342
12351
  await promises.rm(dbPath, { force: true });
12343
12352
  const Sqlite = loadBetterSqlite3();
12344
12353
  const db = new Sqlite(dbPath);
@@ -12586,7 +12595,7 @@ var init_src = __esm({
12586
12595
  });
12587
12596
 
12588
12597
  // package.json
12589
- var version = "6.17.8";
12598
+ var version = "6.18.0";
12590
12599
 
12591
12600
  // packages/cli/src/list.ts
12592
12601
  init_advanced();
@@ -13084,6 +13093,16 @@ function filterErrorEvents(events) {
13084
13093
  return false;
13085
13094
  });
13086
13095
  }
13096
+ function pruneErrorTree(nodes) {
13097
+ const pruned = [];
13098
+ for (const node of nodes) {
13099
+ const children = pruneErrorTree(node.children);
13100
+ if (node.status === "error" || children.length > 0) {
13101
+ pruned.push({ ...node, children });
13102
+ }
13103
+ }
13104
+ return pruned;
13105
+ }
13087
13106
  async function view(runId, options = {}) {
13088
13107
  try {
13089
13108
  const id = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "";
@@ -13156,11 +13175,44 @@ async function view(runId, options = {}) {
13156
13175
  const errEvents = filterErrorEvents(events);
13157
13176
  if (options.json) {
13158
13177
  console.log(JSON.stringify(errEvents, null, 2));
13159
- } else if (errEvents.length === 0) {
13178
+ return;
13179
+ }
13180
+ if (errEvents.length === 0) {
13160
13181
  console.log("No errors found in trace");
13182
+ return;
13183
+ }
13184
+ const started2 = events.find(
13185
+ (e) => e.event === "run_started"
13186
+ );
13187
+ const completed2 = events.filter(
13188
+ (e) => e.event === "run_completed"
13189
+ );
13190
+ const last2 = completed2[completed2.length - 1];
13191
+ const status2 = last2 ? last2.status : "running";
13192
+ const durationLine2 = last2 !== void 0 && Number.isFinite(last2.durationMs) ? formatDuration2(last2.durationMs) : "-";
13193
+ const startedTs2 = started2 !== void 0 && Number.isFinite(started2.startTime) ? started2.startTime : started2?.timestamp;
13194
+ const startedLabel2 = startedTs2 !== void 0 ? formatTimestamp(startedTs2) : "-";
13195
+ console.log(`AgentInspect Run: ${started2?.name ?? id}`);
13196
+ console.log(`ID: ${id}`);
13197
+ console.log(`Status: ${status2}`);
13198
+ console.log(`Duration: ${durationLine2}`);
13199
+ console.log(`Started: ${startedLabel2}`);
13200
+ console.log("");
13201
+ console.log("Error Tree:");
13202
+ const pruned = pruneErrorTree(buildStepTree(events));
13203
+ if (pruned.length === 0) {
13204
+ if (last2?.status === "error") {
13205
+ console.log(
13206
+ renderErrorLine(
13207
+ last2.error ?? { message: "Run completed with error status" },
13208
+ 0
13209
+ )
13210
+ );
13211
+ } else {
13212
+ console.log("No error steps recorded");
13213
+ }
13161
13214
  } else {
13162
- console.log("Error events");
13163
- console.log(JSON.stringify(errEvents, null, 2));
13215
+ printStepTree(pruned, 0, options.verbose === true);
13164
13216
  }
13165
13217
  return;
13166
13218
  }
@@ -15615,9 +15667,9 @@ Trace directory: ${traceDir}`);
15615
15667
  process.exitCode = 1;
15616
15668
  }
15617
15669
  const resolvedOutput = resolveOutputOption(options);
15618
- const outPath = resolvedOutput !== void 0 ? path32__default.default.resolve(resolvedOutput) : void 0;
15670
+ const outPath = resolvedOutput !== void 0 ? path33__default.default.resolve(resolvedOutput) : void 0;
15619
15671
  if (outPath !== void 0) {
15620
- await promises.mkdir(path32__default.default.dirname(outPath), { recursive: true });
15672
+ await promises.mkdir(path33__default.default.dirname(outPath), { recursive: true });
15621
15673
  await promises.writeFile(outPath, result.content, "utf-8");
15622
15674
  const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
15623
15675
  console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
@@ -15976,7 +16028,7 @@ function indexedToMetadata(row, traceDir) {
15976
16028
  endedAt: row.endedAt ?? void 0,
15977
16029
  durationMs: row.durationMs ?? void 0,
15978
16030
  eventCount: 0,
15979
- filePath: path32__default.default.join(traceDir, row.file),
16031
+ filePath: path33__default.default.join(traceDir, row.file),
15980
16032
  fileSize: 0,
15981
16033
  createdAt: new Date(row.mtimeMs)
15982
16034
  };
@@ -16458,9 +16510,9 @@ async function reportCommand(runId, options = {}) {
16458
16510
  ...options.section ? { section: options.section } : {}
16459
16511
  });
16460
16512
  const resolvedOutput = resolveOutputOption(options);
16461
- const outPath = resolvedOutput !== void 0 ? path32__default.default.resolve(resolvedOutput) : void 0;
16513
+ const outPath = resolvedOutput !== void 0 ? path33__default.default.resolve(resolvedOutput) : void 0;
16462
16514
  if (outPath !== void 0) {
16463
- await promises.mkdir(path32__default.default.dirname(outPath), { recursive: true });
16515
+ await promises.mkdir(path33__default.default.dirname(outPath), { recursive: true });
16464
16516
  await promises.writeFile(outPath, result.content, "utf-8");
16465
16517
  console.log(`Wrote ${result.fileExtension} report to ${outPath}`);
16466
16518
  }
@@ -16658,8 +16710,8 @@ function passesLuhn(value) {
16658
16710
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
16659
16711
  var EPOCH_MS_RE = /^1[0-9]{12}$/;
16660
16712
  var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
16661
- function pathSuggestsNonCard(path43) {
16662
- const normalized = path43.toLowerCase().replace(/[^a-z0-9._]/g, "");
16713
+ function pathSuggestsNonCard(path44) {
16714
+ const normalized = path44.toLowerCase().replace(/[^a-z0-9._]/g, "");
16663
16715
  return /(^|\.)(tokenusage|usage)(\.|$)/.test(normalized) || /(startedat|endedat|durationms|timestamp|createdat|updatedat)(\.|$)/.test(normalized) || /(^|\.)(runid|traceid|spanid|eventid|sessionid|userid|parentid|requestid|correlationid)(\.|$)/.test(
16664
16716
  normalized
16665
16717
  ) || /(^|\.)ts(\.|$)/.test(normalized);
@@ -16855,17 +16907,17 @@ function applyRule(rule, value, replacement) {
16855
16907
  }
16856
16908
  return value;
16857
16909
  }
16858
- function childPath(path43, key) {
16910
+ function childPath(path44, key) {
16859
16911
  if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
16860
- return path43 ? `${path43}.${key}` : key;
16912
+ return path44 ? `${path44}.${key}` : key;
16861
16913
  }
16862
- return `${path43 || "$"}[${JSON.stringify(key)}]`;
16914
+ return `${path44 || "$"}[${JSON.stringify(key)}]`;
16863
16915
  }
16864
- function indexPath(path43, index) {
16865
- return `${path43 || "$"}[${index}]`;
16916
+ function indexPath(path44, index) {
16917
+ return `${path44 || "$"}[${index}]`;
16866
16918
  }
16867
- function makeFinding(path43, detector, action, matchKind, severity = "warning", preview) {
16868
- return preview === void 0 ? { path: path43, detector, action, severity, matchKind } : { path: path43, detector, action, severity, matchKind, preview };
16919
+ function makeFinding(path44, detector, action, matchKind, severity = "warning", preview) {
16920
+ return preview === void 0 ? { path: path44, detector, action, severity, matchKind } : { path: path44, detector, action, severity, matchKind, preview };
16869
16921
  }
16870
16922
  function createRedactionProfile(profile = "local") {
16871
16923
  switch (profile) {
@@ -16934,11 +16986,11 @@ var Redactor2 = class {
16934
16986
  #recordFinding(state, finding) {
16935
16987
  if (this.#collectFindings) state.findings.push(finding);
16936
16988
  }
16937
- #redactValue(value, key, path43, depth, state) {
16989
+ #redactValue(value, key, path44, depth, state) {
16938
16990
  if (depth > this.#maxDepth) {
16939
16991
  this.#recordFinding(
16940
16992
  state,
16941
- makeFinding(path43, "structure.maxDepth", "truncate", "value", "warning")
16993
+ makeFinding(path44, "structure.maxDepth", "truncate", "value", "warning")
16942
16994
  );
16943
16995
  return "[Truncated]";
16944
16996
  }
@@ -16947,19 +16999,19 @@ var Redactor2 = class {
16947
16999
  if (rule) {
16948
17000
  this.#recordFinding(
16949
17001
  state,
16950
- makeFinding(path43, `key.${rule.key}`, actionForRule(rule), "key", "warning")
17002
+ makeFinding(path44, `key.${rule.key}`, actionForRule(rule), "key", "warning")
16951
17003
  );
16952
17004
  return applyRule(rule, value, this.#replacement);
16953
17005
  }
16954
17006
  }
16955
17007
  for (const detector of this.#detectors) {
16956
- const detections = detector.detect({ path: path43, key, value });
17008
+ const detections = detector.detect({ path: path44, key, value });
16957
17009
  for (const detection of detections) {
16958
17010
  const action = detection.action ?? "replace";
16959
17011
  this.#recordFinding(
16960
17012
  state,
16961
17013
  makeFinding(
16962
- path43,
17014
+ path44,
16963
17015
  detector.id,
16964
17016
  action,
16965
17017
  detection.matchKind ?? detector.matchKind ?? "custom",
@@ -16977,7 +17029,7 @@ var Redactor2 = class {
16977
17029
  const out = [];
16978
17030
  state.seen.set(value, out);
16979
17031
  value.forEach((item, index) => {
16980
- out[index] = this.#redactValue(item, void 0, indexPath(path43, index), depth + 1, state);
17032
+ out[index] = this.#redactValue(item, void 0, indexPath(path44, index), depth + 1, state);
16981
17033
  });
16982
17034
  return out;
16983
17035
  }
@@ -16989,7 +17041,7 @@ var Redactor2 = class {
16989
17041
  out[entryKey] = this.#redactValue(
16990
17042
  entryValue,
16991
17043
  entryKey,
16992
- childPath(path43 === "$" ? "" : path43, entryKey),
17044
+ childPath(path44 === "$" ? "" : path44, entryKey),
16993
17045
  depth + 1,
16994
17046
  state
16995
17047
  );
@@ -17005,6 +17057,233 @@ function createRedactor(options) {
17005
17057
  function redact(value, options) {
17006
17058
  return createRedactor(options).redact(value);
17007
17059
  }
17060
+ var REDACTION_POLICY_LIMITS = {
17061
+ maxExtraKeys: 64,
17062
+ maxKeyLength: 64,
17063
+ maxPatterns: 32,
17064
+ maxPatternLength: 128,
17065
+ maxPatternIdLength: 64,
17066
+ maxTypedQuantifier: 64
17067
+ };
17068
+ var SAFE_TYPED_PATTERN = /^[A-Za-z0-9_@./:=<>\-[\]()|+*?{},\\\s^$]+$/;
17069
+ function isRecord16(value) {
17070
+ return value !== null && typeof value === "object" && !Array.isArray(value);
17071
+ }
17072
+ function escapeRegExp(value) {
17073
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17074
+ }
17075
+ function rejectRemotePolicyPath(policyPath) {
17076
+ const trimmed = policyPath.trim();
17077
+ if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed)) {
17078
+ throw new Error(
17079
+ `--policy must be a local JSON file path (got a URL-like value). Remote policy fetch is not supported.`
17080
+ );
17081
+ }
17082
+ }
17083
+ function parseSeverity(value, label) {
17084
+ if (value === void 0) return "error";
17085
+ if (value === "info" || value === "warning" || value === "error") return value;
17086
+ throw new Error(`${label} severity must be info, warning, or error.`);
17087
+ }
17088
+ function validateExtraKey(key, index) {
17089
+ const label = `extraKeys[${index}]`;
17090
+ if (key.length === 0) throw new Error(`${label} must be a non-empty string.`);
17091
+ if (key.length > REDACTION_POLICY_LIMITS.maxKeyLength) {
17092
+ throw new Error(
17093
+ `${label} exceeds max length ${REDACTION_POLICY_LIMITS.maxKeyLength}.`
17094
+ );
17095
+ }
17096
+ if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(key)) {
17097
+ throw new Error(
17098
+ `${label} must be an identifier-like key (letters, digits, _, ., -).`
17099
+ );
17100
+ }
17101
+ return key;
17102
+ }
17103
+ function assertBoundedTypedPattern(pattern, label) {
17104
+ if (pattern.length === 0) throw new Error(`${label} must be non-empty.`);
17105
+ if (pattern.length > REDACTION_POLICY_LIMITS.maxPatternLength) {
17106
+ throw new Error(
17107
+ `${label} exceeds max length ${REDACTION_POLICY_LIMITS.maxPatternLength}.`
17108
+ );
17109
+ }
17110
+ if (!SAFE_TYPED_PATTERN.test(pattern)) {
17111
+ throw new Error(
17112
+ `${label} contains unsupported characters for bounded typed patterns.`
17113
+ );
17114
+ }
17115
+ if (/\(\?/.test(pattern) || /\\[1-9]/.test(pattern)) {
17116
+ throw new Error(
17117
+ `${label} rejects lookaround, backreferences, and nested quantifiers.`
17118
+ );
17119
+ }
17120
+ if (/\([^)]*[+*{][^)]*\)[+*{]/.test(pattern) || /\[[^\]]*[+*{][^\]]*\][+*{]/.test(pattern)) {
17121
+ throw new Error(`${label} rejects nested quantifiers.`);
17122
+ }
17123
+ if (/\.[*+]/.test(pattern) || /[*+]\{/.test(pattern)) {
17124
+ throw new Error(`${label} rejects unbounded .* / .+ style quantifiers.`);
17125
+ }
17126
+ for (const match of pattern.matchAll(/\{(\d+)(?:,(\d*))?\}/g)) {
17127
+ const min = Number(match[1]);
17128
+ const maxRaw = match[2];
17129
+ const max = maxRaw === void 0 || maxRaw === "" ? min : Number(maxRaw);
17130
+ if (!Number.isFinite(min) || !Number.isFinite(max) || min > REDACTION_POLICY_LIMITS.maxTypedQuantifier || max > REDACTION_POLICY_LIMITS.maxTypedQuantifier) {
17131
+ throw new Error(
17132
+ `${label} quantifiers must be <= ${REDACTION_POLICY_LIMITS.maxTypedQuantifier}.`
17133
+ );
17134
+ }
17135
+ }
17136
+ }
17137
+ function compilePatternDetector(input3, index) {
17138
+ const label = `patterns[${index}]`;
17139
+ if (!isRecord16(input3)) throw new Error(`${label} must be an object.`);
17140
+ const idRaw = input3.id;
17141
+ if (typeof idRaw !== "string" || idRaw.trim() === "") {
17142
+ throw new Error(`${label}.id must be a non-empty string.`);
17143
+ }
17144
+ const id = idRaw.trim();
17145
+ if (id.length > REDACTION_POLICY_LIMITS.maxPatternIdLength) {
17146
+ throw new Error(
17147
+ `${label}.id exceeds max length ${REDACTION_POLICY_LIMITS.maxPatternIdLength}.`
17148
+ );
17149
+ }
17150
+ if (!/^[A-Za-z][A-Za-z0-9._-]*$/.test(id)) {
17151
+ throw new Error(`${label}.id must be an identifier-like detector id.`);
17152
+ }
17153
+ const type = input3.type;
17154
+ if (type !== "literal" && type !== "prefix" && type !== "typed") {
17155
+ throw new Error(`${label}.type must be literal, prefix, or typed.`);
17156
+ }
17157
+ const severity = parseSeverity(input3.severity, label);
17158
+ let source;
17159
+ if (type === "typed") {
17160
+ if (typeof input3.pattern !== "string") {
17161
+ throw new Error(`${label}.pattern must be a string for typed patterns.`);
17162
+ }
17163
+ assertBoundedTypedPattern(input3.pattern, `${label}.pattern`);
17164
+ source = input3.pattern;
17165
+ } else {
17166
+ if (typeof input3.value !== "string") {
17167
+ throw new Error(`${label}.value must be a string for ${type} patterns.`);
17168
+ }
17169
+ if (input3.value.length === 0) {
17170
+ throw new Error(`${label}.value must be non-empty.`);
17171
+ }
17172
+ if (input3.value.length > REDACTION_POLICY_LIMITS.maxPatternLength) {
17173
+ throw new Error(
17174
+ `${label}.value exceeds max length ${REDACTION_POLICY_LIMITS.maxPatternLength}.`
17175
+ );
17176
+ }
17177
+ const escaped = escapeRegExp(input3.value);
17178
+ source = type === "prefix" ? `^${escaped}` : escaped;
17179
+ }
17180
+ let regex;
17181
+ try {
17182
+ regex = new RegExp(source);
17183
+ } catch (error) {
17184
+ throw new Error(
17185
+ `${label} failed to compile: ${error instanceof Error ? error.message : String(error)}`
17186
+ );
17187
+ }
17188
+ return {
17189
+ id: `policy.${id}`,
17190
+ severity,
17191
+ matchKind: "custom",
17192
+ detect({ value }) {
17193
+ if (typeof value !== "string") return [];
17194
+ regex.lastIndex = 0;
17195
+ return regex.test(value) ? [{ action: "replace", severity, matchKind: "custom" }] : [];
17196
+ }
17197
+ };
17198
+ }
17199
+ function compileRedactionPolicy(raw, policyPath) {
17200
+ if (!isRecord16(raw)) {
17201
+ throw new Error(`Redaction policy must be a JSON object (${policyPath}).`);
17202
+ }
17203
+ const diagnostics = [];
17204
+ if (raw.version !== void 0 && raw.version !== 1) {
17205
+ throw new Error(`Unsupported redaction policy version (supported: 1).`);
17206
+ }
17207
+ const extraKeysRaw = raw.extraKeys;
17208
+ const extraKeys = [];
17209
+ if (extraKeysRaw !== void 0) {
17210
+ if (!Array.isArray(extraKeysRaw)) {
17211
+ throw new Error(`extraKeys must be an array of strings.`);
17212
+ }
17213
+ if (extraKeysRaw.length > REDACTION_POLICY_LIMITS.maxExtraKeys) {
17214
+ throw new Error(
17215
+ `extraKeys exceeds max count ${REDACTION_POLICY_LIMITS.maxExtraKeys}.`
17216
+ );
17217
+ }
17218
+ for (let i = 0; i < extraKeysRaw.length; i += 1) {
17219
+ const key = extraKeysRaw[i];
17220
+ if (typeof key !== "string") {
17221
+ throw new Error(`extraKeys[${i}] must be a string.`);
17222
+ }
17223
+ extraKeys.push(validateExtraKey(key, i));
17224
+ }
17225
+ }
17226
+ const patternsRaw = raw.patterns;
17227
+ const detectors = [];
17228
+ if (patternsRaw !== void 0) {
17229
+ if (!Array.isArray(patternsRaw)) {
17230
+ throw new Error(`patterns must be an array.`);
17231
+ }
17232
+ if (patternsRaw.length > REDACTION_POLICY_LIMITS.maxPatterns) {
17233
+ throw new Error(
17234
+ `patterns exceeds max count ${REDACTION_POLICY_LIMITS.maxPatterns}.`
17235
+ );
17236
+ }
17237
+ const seenIds = /* @__PURE__ */ new Set();
17238
+ for (let i = 0; i < patternsRaw.length; i += 1) {
17239
+ const detector = compilePatternDetector(
17240
+ patternsRaw[i],
17241
+ i
17242
+ );
17243
+ if (seenIds.has(detector.id)) {
17244
+ throw new Error(`Duplicate pattern id "${detector.id}".`);
17245
+ }
17246
+ seenIds.add(detector.id);
17247
+ detectors.push(detector);
17248
+ }
17249
+ }
17250
+ if (extraKeys.length === 0 && detectors.length === 0) {
17251
+ diagnostics.push({
17252
+ code: "AI_POLICY_EMPTY",
17253
+ message: "Redaction policy contained no extraKeys or patterns."
17254
+ });
17255
+ }
17256
+ return {
17257
+ path: policyPath,
17258
+ extraKeys,
17259
+ detectors,
17260
+ diagnostics
17261
+ };
17262
+ }
17263
+ async function loadRedactionPolicy(policyPath) {
17264
+ rejectRemotePolicyPath(policyPath);
17265
+ const resolved = path33__default.default.resolve(policyPath);
17266
+ let text;
17267
+ try {
17268
+ text = await promises.readFile(resolved, "utf-8");
17269
+ } catch (error) {
17270
+ throw new Error(
17271
+ `Failed to read --policy file "${resolved}": ${error instanceof Error ? error.message : String(error)}`
17272
+ );
17273
+ }
17274
+ let parsed;
17275
+ try {
17276
+ parsed = JSON.parse(text);
17277
+ } catch (error) {
17278
+ throw new Error(
17279
+ `Invalid JSON in --policy file "${resolved}": ${error instanceof Error ? error.message : String(error)}`
17280
+ );
17281
+ }
17282
+ return compileRedactionPolicy(parsed, resolved);
17283
+ }
17284
+
17285
+ // packages/core/src/entries/checks.ts
17286
+ init_checks2();
17008
17287
 
17009
17288
  // packages/cli/src/trace-input.ts
17010
17289
  init_advanced();
@@ -17036,12 +17315,19 @@ async function inputFromTarget(target, options, stdin) {
17036
17315
  return { type: "file", path: runPath };
17037
17316
  }
17038
17317
 
17039
- // packages/cli/src/redact.ts
17040
- function parseRedactionProfile3(value) {
17041
- if (value === void 0 || value === "local" || value === "share" || value === "strict") {
17042
- return value ?? "share";
17318
+ // packages/cli/src/safety.ts
17319
+ var BEST_EFFORT_NOTE = "Best-effort local safety verification only; not a compliance, privacy, security, or regulatory certification.";
17320
+ var DEFAULT_MAX_STRING_LENGTH = 16384;
17321
+ var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
17322
+ var DEFAULT_MAX_OBJECT_KEYS = 200;
17323
+ var DEFAULT_MAX_SERIALIZED_BYTES = 128 * 1024;
17324
+ function parseLimit3(value, label) {
17325
+ if (value === void 0) return void 0;
17326
+ const parsed = Number(value);
17327
+ if (!Number.isFinite(parsed) || parsed < 0) {
17328
+ throw new Error(`${label} must be a non-negative number.`);
17043
17329
  }
17044
- throw new Error(`Unsupported --profile "${value}". Use local, share, or strict.`);
17330
+ return parsed;
17045
17331
  }
17046
17332
  function stable2(value) {
17047
17333
  if (Array.isArray(value)) return value.map(stable2);
@@ -17051,103 +17337,585 @@ function stable2(value) {
17051
17337
  Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable2(record[key])])
17052
17338
  );
17053
17339
  }
17054
- function isMissingFileError3(error) {
17055
- return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
17340
+ function safetyDiagnostic(code, message, severity = "error") {
17341
+ return { code, message, severity };
17056
17342
  }
17057
- async function contentFromTarget(target, options, stdin) {
17058
- if (target === "-") {
17059
- return { content: await readStdin(stdin), source: "stdin" };
17060
- }
17061
- try {
17062
- const stats2 = await promises.stat(target);
17063
- if (stats2.isDirectory()) {
17064
- throw new Error("redact requires a trace file, JSON file, stdin, or run id.");
17065
- }
17066
- return { content: await promises.readFile(target, "utf-8"), source: target };
17067
- } catch (error) {
17068
- if (!isMissingFileError3(error)) throw error;
17069
- }
17070
- const runPath = getTraceFilePath(target, resolveTraceDir({ dir: options.dir }));
17071
- const stats = await promises.stat(runPath);
17072
- if (stats.isDirectory()) {
17073
- throw new Error("redact requires a trace file, JSON file, stdin, or run id.");
17074
- }
17075
- return { content: await promises.readFile(runPath, "utf-8"), source: runPath };
17343
+ function warningDiagnostics(warnings, unsupportedFields) {
17344
+ return [
17345
+ ...warnings.map(
17346
+ (warning) => safetyDiagnostic(
17347
+ warning.code,
17348
+ warning.message,
17349
+ warning.severity === "error" ? "error" : "warning"
17350
+ )
17351
+ ),
17352
+ ...unsupportedFields.map(
17353
+ (field) => safetyDiagnostic(
17354
+ "unsupported_field",
17355
+ `Reader reported unsupported field: ${field}`,
17356
+ "warning"
17357
+ )
17358
+ )
17359
+ ];
17076
17360
  }
17077
- function redactJsonText(content, profile) {
17078
- const parsed = JSON.parse(content);
17079
- const result = redact(parsed, { profile });
17361
+ function diagnosticFromCheck(item) {
17362
+ return safetyDiagnostic(item.code, item.message, item.severity);
17363
+ }
17364
+ function statusFrom(findings, diagnostics) {
17365
+ if (diagnostics.some((item) => item.severity === "error")) return "UNKNOWN";
17366
+ if (findings.some((item) => item.severity === "error")) return "UNSAFE";
17367
+ if (diagnostics.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
17368
+ if (findings.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
17369
+ return "SAFE";
17370
+ }
17371
+ function resultFromParts(parts) {
17372
+ const findings = [...parts.findings ?? []];
17373
+ const diagnostics = [...parts.diagnostics ?? []];
17374
+ const warnings = [...parts.warnings ?? []];
17375
+ const unsupportedFields = [...parts.unsupportedFields ?? []];
17376
+ const status = parts.status ?? statusFrom(findings, diagnostics);
17080
17377
  return {
17081
- content: `${JSON.stringify(result.value, null, 2)}
17082
- `,
17083
- findings: result.findings
17378
+ ok: status === "SAFE" || status === "SAFE WITH WARNINGS",
17379
+ command: parts.command,
17380
+ status,
17381
+ format: parts.format,
17382
+ ...parts.runId !== void 0 ? { runId: parts.runId } : {},
17383
+ summary: {
17384
+ findings: findings.length,
17385
+ warnings: diagnostics.filter((item) => item.severity === "warning").length + findings.filter((item) => item.severity === "warning").length,
17386
+ errors: diagnostics.filter((item) => item.severity === "error").length + findings.filter((item) => item.severity === "error").length
17387
+ },
17388
+ findings,
17389
+ diagnostics,
17390
+ warnings,
17391
+ unsupportedFields,
17392
+ note: BEST_EFFORT_NOTE,
17393
+ ...parts.sourceAssessment !== void 0 ? { sourceAssessment: parts.sourceAssessment } : {},
17394
+ ...parts.artifactAssessment !== void 0 ? { artifactAssessment: parts.artifactAssessment } : {},
17395
+ ...parts.redactionSummary !== void 0 ? { redactionSummary: parts.redactionSummary } : {}
17084
17396
  };
17085
17397
  }
17086
- function redactJsonlText(content, profile) {
17087
- const lines = content.split(/\r?\n/);
17088
- const out = [];
17089
- const findings = [];
17090
- for (let index = 0; index < lines.length; index += 1) {
17091
- const line = lines[index] ?? "";
17092
- if (line.trim() === "") continue;
17093
- let parsed;
17094
- try {
17095
- parsed = JSON.parse(line);
17096
- } catch {
17097
- throw new Error(`Input is not valid JSON or JSONL at line ${index + 1}.`);
17098
- }
17099
- const result = redact(parsed, { profile });
17100
- out.push(JSON.stringify(result.value));
17101
- findings.push(
17102
- ...result.findings.map((finding) => ({
17103
- ...finding,
17104
- path: `line:${index + 1}:${finding.path}`
17105
- }))
17106
- );
17107
- }
17398
+ function layerFromResult(result) {
17108
17399
  return {
17109
- content: out.length === 0 ? "" : `${out.join("\n")}
17110
- `,
17111
- findings
17400
+ status: result.status,
17401
+ summary: result.summary,
17402
+ findings: result.findings
17112
17403
  };
17113
17404
  }
17114
- function redactDocument(content, profile) {
17115
- const trimmed = content.trim();
17116
- if (trimmed.startsWith("{")) {
17117
- try {
17118
- const parsed = JSON.parse(trimmed);
17119
- if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && ("schemaVersion" in parsed || "eventId" in parsed || "runId" in parsed)) {
17120
- return redactJsonlText(content, profile);
17121
- }
17122
- } catch {
17123
- }
17405
+ function readErrorResult(command, error) {
17406
+ if (error instanceof TraceReadError) {
17407
+ const code = error.code === "unsupported_format" ? "AI_SAFETY_UNSUPPORTED_FORMAT" : error.code === "ambiguous_format" ? "AI_SAFETY_AMBIGUOUS_FORMAT" : "AI_SAFETY_TRACE_UNREADABLE";
17408
+ return resultFromParts({
17409
+ command,
17410
+ format: "unknown",
17411
+ diagnostics: [safetyDiagnostic(code, error.message)],
17412
+ warnings: error.warnings
17413
+ });
17124
17414
  }
17415
+ return resultFromParts({
17416
+ command,
17417
+ format: "unknown",
17418
+ diagnostics: [
17419
+ safetyDiagnostic(
17420
+ "AI_SAFETY_TRACE_UNREADABLE",
17421
+ error instanceof Error ? error.message : String(error)
17422
+ )
17423
+ ]
17424
+ });
17425
+ }
17426
+ function invalidArgumentResult(command, error) {
17427
+ return resultFromParts({
17428
+ command,
17429
+ format: "unknown",
17430
+ diagnostics: [
17431
+ safetyDiagnostic(
17432
+ "AI_SAFETY_INVALID_ARGUMENTS",
17433
+ error instanceof Error ? error.message : String(error)
17434
+ )
17435
+ ]
17436
+ });
17437
+ }
17438
+ function buildSafetyRules(options) {
17439
+ const maxStringLength2 = parseLimit3(options.maxStringLength, "--max-string-length") ?? DEFAULT_MAX_STRING_LENGTH;
17440
+ const maxArrayLength = parseLimit3(options.maxArrayLength, "--max-array-length") ?? DEFAULT_MAX_ARRAY_LENGTH;
17441
+ const maxObjectKeys = parseLimit3(options.maxObjectKeys, "--max-object-keys") ?? DEFAULT_MAX_OBJECT_KEYS;
17442
+ const maxSerializedBytes = parseLimit3(options.maxSerializedBytes, "--max-serialized-bytes") ?? DEFAULT_MAX_SERIALIZED_BYTES;
17443
+ return [
17444
+ createSafetyRawContentRule(),
17445
+ createSafetyRedactionRule(),
17446
+ createSafetySecretPatternRule(),
17447
+ createSafetyOversizedAttributeRule({
17448
+ maxStringLength: maxStringLength2,
17449
+ maxArrayLength,
17450
+ maxObjectKeys,
17451
+ maxSerializedBytes
17452
+ })
17453
+ ];
17454
+ }
17455
+ function flattenNodes2(nodes) {
17456
+ return nodes.flatMap((node) => [
17457
+ node,
17458
+ ...flattenNodes2(
17459
+ node.children
17460
+ )
17461
+ ]);
17462
+ }
17463
+ function detectorSeverity(finding) {
17464
+ return finding.severity;
17465
+ }
17466
+ function classifyRedactionDetector(detector) {
17467
+ if (detector === "value.creditCard" || detector === "value.email" || detector === "value.phone") {
17468
+ return { category: "personal-data", confidence: "high" };
17469
+ }
17470
+ if (detector === "value.ipv4" || detector === "value.ipv6") {
17471
+ return { category: "identifier", confidence: "medium" };
17472
+ }
17473
+ if (detector.startsWith("value.") && (detector.includes("Token") || detector.includes("Key") || detector.includes("jwt") || detector.includes("authorization") || detector.includes("bearer") || detector.includes("cookie") || detector.includes("privateKey") || detector.includes("github") || detector.includes("aws") || detector.includes("provider"))) {
17474
+ return { category: "credential", confidence: "high" };
17475
+ }
17476
+ if (detector.startsWith("key.")) {
17477
+ return { category: "credential", confidence: "medium" };
17478
+ }
17479
+ return { category: "credential", confidence: "medium" };
17480
+ }
17481
+ function redactionOptionsFromPolicy(policy) {
17482
+ if (policy === void 0) return {};
17483
+ return {
17484
+ ...policy.extraKeys.length > 0 ? { extraKeys: [...policy.extraKeys] } : {},
17485
+ ...policy.detectors.length > 0 ? { detectors: [...policy.detectors] } : {}
17486
+ };
17487
+ }
17488
+ function redactionDetectorFindings(read, runId, policy) {
17489
+ const runs = runId === void 0 ? read.runs : read.runs.filter((run) => run.runId === runId);
17490
+ const out = [];
17491
+ const policyOptions = redactionOptionsFromPolicy(policy);
17492
+ for (const run of runs) {
17493
+ for (const node of flattenNodes2(run.children)) {
17494
+ const attrs = node.event.attributes;
17495
+ if (attrs === void 0) continue;
17496
+ const result = createRedactor({
17497
+ profile: "share",
17498
+ ...policyOptions
17499
+ }).redact(attrs);
17500
+ for (const finding of result.findings) {
17501
+ if (finding.action === "keep") continue;
17502
+ const taxonomy = classifyRedactionDetector(finding.detector);
17503
+ out.push({
17504
+ ruleId: "safety.redactDetector",
17505
+ severity: detectorSeverity(finding),
17506
+ status: finding.severity === "error" ? "fail" : "warning",
17507
+ message: `Redaction detector ${finding.detector} matched ${finding.matchKind} at ${finding.path}.`,
17508
+ expected: "redacted trace content",
17509
+ actual: finding.detector,
17510
+ evidence: [
17511
+ {
17512
+ runId: node.event.runId,
17513
+ eventId: node.event.eventId,
17514
+ ...node.event.parentId !== void 0 ? { parentId: node.event.parentId } : {},
17515
+ kind: node.event.kind,
17516
+ name: node.event.name,
17517
+ ...node.event.status !== void 0 ? { status: node.event.status } : {},
17518
+ path: `attributes.${finding.path.replace(/^\$\.?/, "")}`
17519
+ }
17520
+ ],
17521
+ category: taxonomy.category,
17522
+ confidence: taxonomy.confidence,
17523
+ detector: finding.detector,
17524
+ action: finding.action
17525
+ });
17526
+ }
17527
+ }
17528
+ }
17529
+ return out.sort((a, b) => {
17530
+ const aEvidence = a.evidence[0];
17531
+ const bEvidence = b.evidence[0];
17532
+ return (aEvidence?.runId ?? "").localeCompare(bEvidence?.runId ?? "") || (aEvidence?.eventId ?? "").localeCompare(bEvidence?.eventId ?? "") || (aEvidence?.path ?? "").localeCompare(bEvidence?.path ?? "") || a.message.localeCompare(b.message);
17533
+ });
17534
+ }
17535
+ function exitCodeFor(result) {
17536
+ if (result.status === "SAFE" || result.status === "SAFE WITH WARNINGS") return 0;
17537
+ if (result.status === "UNSAFE") return 1;
17538
+ return 2;
17539
+ }
17540
+ function toResidualStatus(status) {
17541
+ if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
17542
+ return status;
17543
+ }
17544
+ function toResidualSafetyAssessment(result) {
17545
+ const codes = [
17546
+ .../* @__PURE__ */ new Set([
17547
+ ...result.diagnostics.map((item) => item.code),
17548
+ ...result.findings.map((item) => item.ruleId)
17549
+ ])
17550
+ ].sort((a, b) => a.localeCompare(b));
17551
+ return {
17552
+ status: toResidualStatus(result.status),
17553
+ findings: result.summary.findings,
17554
+ warnings: result.summary.warnings,
17555
+ errors: result.summary.errors,
17556
+ codes,
17557
+ note: BEST_EFFORT_NOTE
17558
+ };
17559
+ }
17560
+ async function assessResidualFromContent(content, options = {}) {
17561
+ try {
17562
+ const policy = options.compiledPolicy ?? (options.policy !== void 0 ? await loadRedactionPolicy(options.policy) : void 0);
17563
+ const read = await openTrace(
17564
+ { type: "string", content },
17565
+ { format: options.format ?? "agent-inspect-jsonl" }
17566
+ );
17567
+ const result = assessOpenedTrace(read, {
17568
+ ...options,
17569
+ ...policy !== void 0 ? { compiledPolicy: policy } : {}
17570
+ });
17571
+ return toResidualSafetyAssessment(result);
17572
+ } catch (error) {
17573
+ if (error instanceof TraceReadError) {
17574
+ return toResidualSafetyAssessment(readErrorResult("verify-safe", error));
17575
+ }
17576
+ const message = error instanceof Error ? error.message : String(error);
17577
+ return {
17578
+ status: "UNKNOWN",
17579
+ findings: 0,
17580
+ warnings: 0,
17581
+ errors: 1,
17582
+ codes: ["AI_SAFETY_TRACE_UNREADABLE"],
17583
+ note: `${BEST_EFFORT_NOTE} Residual assessment requires a supported AgentInspect trace; ${message}`
17584
+ };
17585
+ }
17586
+ }
17587
+ function explainFinding(finding, blocksBundle) {
17588
+ const path44 = finding.evidence[0]?.path ?? "(unknown path)";
17589
+ const category = finding.category ?? "structure";
17590
+ const confidence = finding.confidence ?? "medium";
17591
+ const detector = finding.detector ?? finding.ruleId;
17592
+ const action = finding.action ?? "review";
17593
+ const redactionHint = category === "credential" || category === "personal-data" || category === "raw-content" || action.includes("redact") ? "Usually removable by share/strict redaction before bundling." : "May require omitting the field, lowering limits, or an explicit local override.";
17594
+ return [
17595
+ ` Matched: detector=${detector}; path=${path44}; category=${category}`,
17596
+ ` Why: ${finding.message}`,
17597
+ ` Confidence: ${confidence}`,
17598
+ ` Redaction: ${redactionHint}`,
17599
+ ` Override: configure a custom redaction/detector rule locally (see docs/SAFETY-POLICY.md); do not weaken defaults globally.`,
17600
+ ` Bundle gate: ${blocksBundle ? "blocks share-safe bundle unless --allow-unsafe" : "does not block by itself (warning/info)"}`
17601
+ ];
17602
+ }
17603
+ function findingExplanation(finding) {
17604
+ const blocks = finding.severity === "error" || finding.status === "fail";
17605
+ return {
17606
+ ruleId: finding.ruleId,
17607
+ detector: finding.detector ?? finding.ruleId,
17608
+ path: finding.evidence[0]?.path,
17609
+ category: finding.category,
17610
+ confidence: finding.confidence,
17611
+ action: finding.action,
17612
+ blocksBundle: blocks,
17613
+ // Never include matched secret/PII values.
17614
+ message: finding.message
17615
+ };
17616
+ }
17617
+ function printHuman(result, explain = false) {
17618
+ console.log(`Safety status: ${result.status}`);
17619
+ console.log(`Format: ${result.format}`);
17620
+ if (result.runId !== void 0) console.log(`Run: ${result.runId}`);
17621
+ if (result.sourceAssessment !== void 0 && result.artifactAssessment !== void 0) {
17622
+ console.log(`Source assessment: ${result.sourceAssessment.status}`);
17623
+ console.log(`Artifact assessment: ${result.artifactAssessment.status}`);
17624
+ if (result.redactionSummary !== void 0) {
17625
+ console.log(
17626
+ `Redaction: profile=${result.redactionSummary.profile}, findings=${result.redactionSummary.findings}`
17627
+ );
17628
+ }
17629
+ }
17630
+ console.log(
17631
+ `Summary: ${result.summary.findings} finding(s), ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
17632
+ );
17633
+ for (const diagnostic7 of result.diagnostics) {
17634
+ console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
17635
+ }
17636
+ for (const finding of result.findings) {
17637
+ const path44 = finding.evidence[0]?.path;
17638
+ const taxonomy = finding.category !== void 0 || finding.confidence !== void 0 ? ` [${[finding.category, finding.confidence].filter(Boolean).join("/")}]` : "";
17639
+ console.log(`- ${finding.ruleId}: ${finding.message}${path44 ? ` (${path44})` : ""}${taxonomy}`);
17640
+ if (explain) {
17641
+ const blocks = finding.severity === "error" || finding.status === "fail";
17642
+ for (const line of explainFinding(finding, blocks)) {
17643
+ console.log(line);
17644
+ }
17645
+ }
17646
+ }
17647
+ console.log(`Note: ${result.note}`);
17648
+ }
17649
+ function printJson(result, explain = false) {
17650
+ const payload = explain === true ? { ...result, explanations: result.findings.map(findingExplanation) } : result;
17651
+ console.log(JSON.stringify(stable2(payload), null, 2));
17652
+ }
17653
+ async function safetyCommand(command, target, options, stdin) {
17654
+ let result;
17655
+ try {
17656
+ const policy = options.compiledPolicy ?? (options.policy !== void 0 ? await loadRedactionPolicy(options.policy) : void 0);
17657
+ const optionsWithPolicy = {
17658
+ ...options,
17659
+ ...policy !== void 0 ? { compiledPolicy: policy } : {}
17660
+ };
17661
+ const input3 = await inputFromTarget(target, optionsWithPolicy, stdin);
17662
+ const read = await openTrace(input3, {
17663
+ ...options.format !== void 0 ? { format: options.format } : {}
17664
+ });
17665
+ const source = assessOpenedTrace(read, {
17666
+ ...optionsWithPolicy,
17667
+ ...options.run !== void 0 ? { runId: options.run } : {}
17668
+ });
17669
+ if (command === "scan") {
17670
+ result = { ...source, command: "scan" };
17671
+ } else {
17672
+ const profile = options.redactionProfile ?? "share";
17673
+ const rawContent = input3.type === "string" ? input3.content : input3.type === "file" ? await promises.readFile(input3.path, "utf-8") : void 0;
17674
+ if (rawContent === void 0) {
17675
+ result = {
17676
+ ...source,
17677
+ command: "verify-safe",
17678
+ sourceAssessment: layerFromResult(source)
17679
+ };
17680
+ } else {
17681
+ const redacted = redactTraceContent(rawContent, profile, policy);
17682
+ const artifactRead = await openTrace(
17683
+ { type: "string", content: redacted.content },
17684
+ { format: options.format ?? "agent-inspect-jsonl" }
17685
+ );
17686
+ const artifact = assessOpenedTrace(artifactRead, {
17687
+ ...optionsWithPolicy,
17688
+ ...options.run !== void 0 ? { runId: options.run } : {}
17689
+ });
17690
+ const detectors = [
17691
+ ...new Set(redacted.findings.map((finding) => finding.detector))
17692
+ ].sort((a, b) => a.localeCompare(b));
17693
+ result = resultFromParts({
17694
+ command: "verify-safe",
17695
+ format: artifact.format,
17696
+ runId: artifact.runId ?? source.runId,
17697
+ findings: artifact.findings,
17698
+ diagnostics: artifact.diagnostics,
17699
+ warnings: artifact.warnings,
17700
+ unsupportedFields: artifact.unsupportedFields,
17701
+ status: artifact.status,
17702
+ sourceAssessment: layerFromResult(source),
17703
+ artifactAssessment: layerFromResult(artifact),
17704
+ redactionSummary: {
17705
+ profile,
17706
+ findings: redacted.findings.length,
17707
+ detectors
17708
+ }
17709
+ });
17710
+ }
17711
+ }
17712
+ } catch (error) {
17713
+ const message = error instanceof Error ? error.message : String(error);
17714
+ result = message.startsWith("--") || message.includes("--policy") ? invalidArgumentResult(command, error) : readErrorResult(command, error);
17715
+ }
17716
+ process.exitCode = exitCodeFor(result);
17717
+ if (options.json) printJson(result, options.explain === true);
17718
+ else printHuman(result, options.explain === true);
17719
+ }
17720
+ function scanCommand(target, options = {}, stdin = process.stdin) {
17721
+ return safetyCommand("scan", target, options, stdin);
17722
+ }
17723
+ function verifySafeCommand(target, options = {}, stdin = process.stdin) {
17724
+ return safetyCommand("verify-safe", target, options, stdin);
17725
+ }
17726
+ function assessOpenedTrace(read, options = {}) {
17727
+ try {
17728
+ const rules = buildSafetyRules(options);
17729
+ const checkResult = runTraceChecks(
17730
+ { read },
17731
+ {
17732
+ rules,
17733
+ ...options.runId !== void 0 ? { runId: options.runId } : {},
17734
+ ...options.run !== void 0 ? { runId: options.run } : {}
17735
+ }
17736
+ );
17737
+ const detectorFindings = checkResult.diagnostics.length === 0 ? redactionDetectorFindings(read, checkResult.runId, options.compiledPolicy) : [];
17738
+ return resultFromParts({
17739
+ command: "verify-safe",
17740
+ format: checkResult.format,
17741
+ runId: checkResult.runId,
17742
+ findings: [...checkResult.findings, ...detectorFindings],
17743
+ diagnostics: [
17744
+ ...checkResult.diagnostics.map(diagnosticFromCheck),
17745
+ ...warningDiagnostics(read.warnings, read.unsupportedFields)
17746
+ ],
17747
+ warnings: read.warnings,
17748
+ unsupportedFields: read.unsupportedFields
17749
+ });
17750
+ } catch (error) {
17751
+ return messageStartsWithDash(error) ? invalidArgumentResult("verify-safe", error) : readErrorResult("verify-safe", error);
17752
+ }
17753
+ }
17754
+ function messageStartsWithDash(error) {
17755
+ const message = error instanceof Error ? error.message : String(error);
17756
+ return message.startsWith("--");
17757
+ }
17758
+
17759
+ // packages/cli/src/redact.ts
17760
+ function parseRedactionProfile3(value) {
17761
+ if (value === void 0 || value === "local" || value === "share" || value === "strict") {
17762
+ return value ?? "share";
17763
+ }
17764
+ throw new Error(`Unsupported --profile "${value}". Use local, share, or strict.`);
17765
+ }
17766
+ function stable3(value) {
17767
+ if (Array.isArray(value)) return value.map(stable3);
17768
+ if (value === null || typeof value !== "object") return value;
17769
+ const record = value;
17770
+ return Object.fromEntries(
17771
+ Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable3(record[key])])
17772
+ );
17773
+ }
17774
+ function isMissingFileError3(error) {
17775
+ return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
17776
+ }
17777
+ async function contentFromTarget(target, options, stdin) {
17778
+ if (target === "-") {
17779
+ return { content: await readStdin(stdin), source: "stdin" };
17780
+ }
17781
+ try {
17782
+ const stats2 = await promises.stat(target);
17783
+ if (stats2.isDirectory()) {
17784
+ throw new Error("redact requires a trace file, JSON file, stdin, or run id.");
17785
+ }
17786
+ return { content: await promises.readFile(target, "utf-8"), source: target };
17787
+ } catch (error) {
17788
+ if (!isMissingFileError3(error)) throw error;
17789
+ }
17790
+ const runPath = getTraceFilePath(target, resolveTraceDir({ dir: options.dir }));
17791
+ const stats = await promises.stat(runPath);
17792
+ if (stats.isDirectory()) {
17793
+ throw new Error("redact requires a trace file, JSON file, stdin, or run id.");
17794
+ }
17795
+ return { content: await promises.readFile(runPath, "utf-8"), source: runPath };
17796
+ }
17797
+ function applyRedact(value, profile, policy) {
17798
+ const result = createRedactor({
17799
+ profile,
17800
+ ...policy?.extraKeys.length ? { extraKeys: [...policy.extraKeys] } : {},
17801
+ ...policy?.detectors.length ? { detectors: [...policy.detectors] } : {}
17802
+ }).redact(value);
17803
+ return { value: result.value, findings: result.findings };
17804
+ }
17805
+ function redactJsonText(content, profile, policy) {
17806
+ const parsed = JSON.parse(content);
17807
+ const result = applyRedact(parsed, profile, policy);
17808
+ return {
17809
+ content: `${JSON.stringify(result.value, null, 2)}
17810
+ `,
17811
+ findings: result.findings
17812
+ };
17813
+ }
17814
+ function redactJsonlText(content, profile, policy) {
17815
+ const lines = content.split(/\r?\n/);
17816
+ const out = [];
17817
+ const findings = [];
17818
+ for (let index = 0; index < lines.length; index += 1) {
17819
+ const line = lines[index] ?? "";
17820
+ if (line.trim() === "") continue;
17821
+ let parsed;
17822
+ try {
17823
+ parsed = JSON.parse(line);
17824
+ } catch {
17825
+ throw new Error(`Input is not valid JSON or JSONL at line ${index + 1}.`);
17826
+ }
17827
+ const result = applyRedact(parsed, profile, policy);
17828
+ out.push(JSON.stringify(result.value));
17829
+ findings.push(
17830
+ ...result.findings.map((finding) => ({
17831
+ ...finding,
17832
+ path: `line:${index + 1}:${finding.path}`
17833
+ }))
17834
+ );
17835
+ }
17836
+ return {
17837
+ content: out.length === 0 ? "" : `${out.join("\n")}
17838
+ `,
17839
+ findings
17840
+ };
17841
+ }
17842
+ function redactDocument(content, profile, policy) {
17843
+ const trimmed = content.trim();
17844
+ if (trimmed.startsWith("{")) {
17845
+ try {
17846
+ const parsed = JSON.parse(trimmed);
17847
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && ("schemaVersion" in parsed || "eventId" in parsed || "runId" in parsed)) {
17848
+ return redactJsonlText(content, profile, policy);
17849
+ }
17850
+ } catch {
17851
+ }
17852
+ }
17853
+ try {
17854
+ return redactJsonText(content, profile, policy);
17855
+ } catch {
17856
+ return redactJsonlText(content, profile, policy);
17857
+ }
17858
+ }
17859
+ function redactTraceContent(content, profile, policy) {
17860
+ return redactDocument(content, profile, policy);
17861
+ }
17862
+ function looksLikeAgentInspectTrace(content) {
17863
+ const trimmed = content.trim();
17864
+ if (!trimmed.startsWith("{")) return false;
17125
17865
  try {
17126
- return redactJsonText(content, profile);
17866
+ const firstLine = trimmed.split(/\r?\n/).find((line) => line.trim() !== "") ?? trimmed;
17867
+ const parsed = JSON.parse(firstLine);
17868
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && ("schemaVersion" in parsed || "eventId" in parsed || "runId" in parsed);
17127
17869
  } catch {
17128
- return redactJsonlText(content, profile);
17870
+ return false;
17129
17871
  }
17130
17872
  }
17131
- function redactTraceContent(content, profile) {
17132
- return redactDocument(content, profile);
17873
+ function printResidualWarning(assessment, sourceLooksLikeTrace) {
17874
+ if (assessment.status === "SAFE") return;
17875
+ if (assessment.status === "UNKNOWN" && !sourceLooksLikeTrace) return;
17876
+ const codes = assessment.codes.length > 0 ? ` codes=${assessment.codes.join(",")}` : "";
17877
+ console.error(
17878
+ `Residual safety: ${assessment.status} (findings=${assessment.findings}, warnings=${assessment.warnings}, errors=${assessment.errors})${codes}. Redact does not certify safe sharing; run verify-safe before publishing.`
17879
+ );
17880
+ }
17881
+ function residualExitCode(assessment) {
17882
+ if (assessment.status === "UNSAFE") return 1;
17883
+ if (assessment.status === "UNKNOWN") return 2;
17884
+ return 0;
17133
17885
  }
17134
17886
  async function redactCommand(target, options = {}, stdin = process.stdin) {
17135
17887
  const profile = parseRedactionProfile3(resolveRedactionProfileOption(options));
17888
+ const policy = options.policy !== void 0 ? await loadRedactionPolicy(options.policy) : void 0;
17136
17889
  const source = await contentFromTarget(target, options, stdin);
17137
- const redacted = redactDocument(source.content, profile);
17890
+ const redacted = redactDocument(source.content, profile, policy);
17138
17891
  const outputPath = resolveOutputOption(options);
17892
+ const residualAssessment = await assessResidualFromContent(redacted.content, {
17893
+ compiledPolicy: policy
17894
+ });
17139
17895
  if (outputPath !== void 0) {
17140
17896
  await promises.writeFile(outputPath, redacted.content, "utf-8");
17141
17897
  }
17898
+ if (options.failOnResidual === true) {
17899
+ process.exitCode = residualExitCode(residualAssessment);
17900
+ }
17142
17901
  if (options.json) {
17143
17902
  console.log(
17144
17903
  JSON.stringify(
17145
- stable2({
17904
+ stable3({
17146
17905
  ok: true,
17147
17906
  profile,
17148
17907
  source: source.source,
17149
17908
  output: outputPath,
17150
17909
  findings: redacted.findings,
17910
+ residualAssessment,
17911
+ ...policy !== void 0 ? {
17912
+ policy: {
17913
+ path: policy.path,
17914
+ extraKeys: policy.extraKeys.length,
17915
+ patterns: policy.detectors.length,
17916
+ diagnostics: policy.diagnostics
17917
+ }
17918
+ } : {},
17151
17919
  content: outputPath === void 0 ? redacted.content : void 0
17152
17920
  }),
17153
17921
  null,
@@ -17156,6 +17924,7 @@ async function redactCommand(target, options = {}, stdin = process.stdin) {
17156
17924
  );
17157
17925
  return;
17158
17926
  }
17927
+ printResidualWarning(residualAssessment, looksLikeAgentInspectTrace(redacted.content));
17159
17928
  if (outputPath === void 0) {
17160
17929
  process.stdout.write(redacted.content);
17161
17930
  }
@@ -17438,7 +18207,7 @@ async function openCommand(input3, options = {}, stdin = process.stdin) {
17438
18207
 
17439
18208
  // packages/cli/src/migrate.ts
17440
18209
  init_advanced();
17441
- function isRecord16(value) {
18210
+ function isRecord17(value) {
17442
18211
  return typeof value === "object" && value !== null && !Array.isArray(value);
17443
18212
  }
17444
18213
  function parseTarget(value) {
@@ -17447,7 +18216,7 @@ function parseTarget(value) {
17447
18216
  throw new Error('Unsupported migration target. Use "--to 1.0".');
17448
18217
  }
17449
18218
  function formatOf(value) {
17450
- if (!isRecord16(value)) return "unknown";
18219
+ if (!isRecord17(value)) return "unknown";
17451
18220
  if (value.schemaVersion === "0.1") return "0.1";
17452
18221
  if (value.schemaVersion === "0.2") return "0.2";
17453
18222
  if (value.schemaVersion === "1.0") return "1.0";
@@ -17460,14 +18229,14 @@ function uniqueSorted(values) {
17460
18229
  return [...new Set(values)].sort();
17461
18230
  }
17462
18231
  function isWithinDirectory(child, parent) {
17463
- const relative = path32__default.default.relative(parent, child);
17464
- return relative === "" || !relative.startsWith("..") && !path32__default.default.isAbsolute(relative);
18232
+ const relative = path33__default.default.relative(parent, child);
18233
+ return relative === "" || !relative.startsWith("..") && !path33__default.default.isAbsolute(relative);
17465
18234
  }
17466
18235
  async function resolveOutputPath(inputPath, output2, force) {
17467
18236
  if (output2 === void 0 || output2.trim() === "") return void 0;
17468
- const inputAbs = path32__default.default.resolve(inputPath);
17469
- const outputAbs = path32__default.default.resolve(output2.trim());
17470
- const inputDir = path32__default.default.dirname(inputAbs);
18237
+ const inputAbs = path33__default.default.resolve(inputPath);
18238
+ const outputAbs = path33__default.default.resolve(output2.trim());
18239
+ const inputDir = path33__default.default.dirname(inputAbs);
17471
18240
  if (!isWithinDirectory(outputAbs, inputDir)) {
17472
18241
  throw new Error("Refusing to write migrated output outside the input directory.");
17473
18242
  }
@@ -17588,7 +18357,7 @@ async function migrateCommand(input3, options = {}) {
17588
18357
  process.exitCode = 1;
17589
18358
  return;
17590
18359
  }
17591
- const inputPath = path32__default.default.resolve(input3.trim());
18360
+ const inputPath = path33__default.default.resolve(input3.trim());
17592
18361
  const dryRun = options.dryRun === true;
17593
18362
  if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
17594
18363
  console.error("migrate requires --dry-run or --output <path>.");
@@ -17607,7 +18376,7 @@ async function migrateCommand(input3, options = {}) {
17607
18376
  );
17608
18377
  const result = await buildMigration(inputPath, outputPath);
17609
18378
  if (!dryRun && outputPath !== void 0) {
17610
- await promises.mkdir(path32__default.default.dirname(outputPath), { recursive: true });
18379
+ await promises.mkdir(path33__default.default.dirname(outputPath), { recursive: true });
17611
18380
  await promises.writeFile(outputPath, result.content, "utf-8");
17612
18381
  }
17613
18382
  printSummary2(result, dryRun);
@@ -17616,16 +18385,13 @@ async function migrateCommand(input3, options = {}) {
17616
18385
  console.error(`[AgentInspect] migrate failed: ${msg}`);
17617
18386
  process.exitCode = 1;
17618
18387
  }
17619
- }
17620
- init_advanced();
17621
-
17622
- // packages/core/src/entries/checks.ts
17623
- init_checks2();
18388
+ }
18389
+ init_advanced();
17624
18390
 
17625
18391
  // packages/cli/src/evidence-on.ts
17626
18392
  init_advanced();
17627
18393
  function validateReporterArtifactPath(options) {
17628
- const outputDir = path32__default.default.resolve(options.outputDir);
18394
+ const outputDir = path33__default.default.resolve(options.outputDir);
17629
18395
  const diagnostics = [];
17630
18396
  const rawPath = options.relativePath;
17631
18397
  if (rawPath.length === 0) {
@@ -17645,7 +18411,7 @@ function validateReporterArtifactPath(options) {
17645
18411
  });
17646
18412
  return { ok: false, outputDir, diagnostics };
17647
18413
  }
17648
- if (path32__default.default.isAbsolute(rawPath) || path32__default.default.win32.isAbsolute(rawPath)) {
18414
+ if (path33__default.default.isAbsolute(rawPath) || path33__default.default.win32.isAbsolute(rawPath)) {
17649
18415
  diagnostics.push({
17650
18416
  code: "artifact_path_absolute",
17651
18417
  severity: "error",
@@ -17654,7 +18420,7 @@ function validateReporterArtifactPath(options) {
17654
18420
  });
17655
18421
  return { ok: false, outputDir, diagnostics };
17656
18422
  }
17657
- const normalized = path32__default.default.posix.normalize(rawPath.replace(/\\/g, "/"));
18423
+ const normalized = path33__default.default.posix.normalize(rawPath.replace(/\\/g, "/"));
17658
18424
  const segments = normalized.split("/");
17659
18425
  if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
17660
18426
  diagnostics.push({
@@ -17665,9 +18431,9 @@ function validateReporterArtifactPath(options) {
17665
18431
  });
17666
18432
  return { ok: false, outputDir, diagnostics };
17667
18433
  }
17668
- const absolutePath = path32__default.default.resolve(outputDir, normalized);
17669
- const relFromOutput = path32__default.default.relative(outputDir, absolutePath);
17670
- if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path32__default.default.isAbsolute(relFromOutput)) {
18434
+ const absolutePath = path33__default.default.resolve(outputDir, normalized);
18435
+ const relFromOutput = path33__default.default.relative(outputDir, absolutePath);
18436
+ if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path33__default.default.isAbsolute(relFromOutput)) {
17671
18437
  diagnostics.push({
17672
18438
  code: "artifact_path_escape",
17673
18439
  severity: "error",
@@ -17693,7 +18459,7 @@ var EVIDENCE_CI_ARTIFACT_FILES = [
17693
18459
  function createEvidenceCiArtifacts(options) {
17694
18460
  const profile = options.redactionProfile ?? "share";
17695
18461
  const prefix = options.relativeDir !== void 0 && options.relativeDir.trim() !== "" ? options.relativeDir.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "") : "";
17696
- const join = (name) => prefix === "" ? name : path32__default.default.posix.join(prefix, name);
18462
+ const join = (name) => prefix === "" ? name : path33__default.default.posix.join(prefix, name);
17697
18463
  const formatFor = (name) => {
17698
18464
  if (name.endsWith(".html")) return "html";
17699
18465
  if (name.endsWith(".jsonl")) return "jsonl";
@@ -17742,9 +18508,9 @@ function stableJson2(value) {
17742
18508
  async function writeLocalEvidence(input3) {
17743
18509
  const profile = input3.redactionProfile ?? "share";
17744
18510
  const format = input3.format ?? "directory";
17745
- const baseDir = path32__default.default.resolve(input3.outputDir);
18511
+ const baseDir = path33__default.default.resolve(input3.outputDir);
17746
18512
  await promises.mkdir(
17747
- format === "zip" && baseDir.toLowerCase().endsWith(".zip") ? path32__default.default.dirname(baseDir) : baseDir,
18513
+ format === "zip" && baseDir.toLowerCase().endsWith(".zip") ? path33__default.default.dirname(baseDir) : baseDir,
17748
18514
  { recursive: true }
17749
18515
  );
17750
18516
  const sourceContents = /* @__PURE__ */ new Map();
@@ -17800,7 +18566,7 @@ async function writeLocalEvidence(input3) {
17800
18566
  }
17801
18567
  if (format === "zip") {
17802
18568
  const zipPath = baseDir.toLowerCase().endsWith(".zip") ? baseDir : `${baseDir}.zip`;
17803
- await promises.mkdir(path32__default.default.dirname(zipPath), { recursive: true });
18569
+ await promises.mkdir(path33__default.default.dirname(zipPath), { recursive: true });
17804
18570
  const archive = buildZipArchive(
17805
18571
  files.map(([relativePath, content]) => ({
17806
18572
  path: relativePath,
@@ -17815,16 +18581,16 @@ async function writeLocalEvidence(input3) {
17815
18581
  let sidecarDir = baseDir;
17816
18582
  if (baseDir.toLowerCase().endsWith(".html")) {
17817
18583
  htmlPath = baseDir;
17818
- sidecarDir = path32__default.default.dirname(baseDir);
18584
+ sidecarDir = path33__default.default.dirname(baseDir);
17819
18585
  } else {
17820
18586
  await promises.mkdir(baseDir, { recursive: true });
17821
- htmlPath = path32__default.default.join(baseDir, "evidence.html");
18587
+ htmlPath = path33__default.default.join(baseDir, "evidence.html");
17822
18588
  sidecarDir = baseDir;
17823
18589
  }
17824
18590
  await promises.mkdir(sidecarDir, { recursive: true });
17825
18591
  await promises.writeFile(htmlPath, evidencePackage["evidence.html"], "utf-8");
17826
18592
  await promises.writeFile(
17827
- path32__default.default.join(sidecarDir, "evidence.json"),
18593
+ path33__default.default.join(sidecarDir, "evidence.json"),
17828
18594
  evidencePackage["evidence.json"],
17829
18595
  "utf-8"
17830
18596
  );
@@ -17832,7 +18598,7 @@ async function writeLocalEvidence(input3) {
17832
18598
  }
17833
18599
  await promises.mkdir(baseDir, { recursive: true });
17834
18600
  for (const [name, content] of files) {
17835
- await promises.writeFile(path32__default.default.join(baseDir, name), content, "utf-8");
18601
+ await promises.writeFile(path33__default.default.join(baseDir, name), content, "utf-8");
17836
18602
  }
17837
18603
  return baseDir;
17838
18604
  }
@@ -17856,11 +18622,11 @@ function checkResultToEvidenceJson(result, runIds) {
17856
18622
  }
17857
18623
  function defaultEvidenceDir(label) {
17858
18624
  const safe = label.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 80) || "evidence";
17859
- return path32__default.default.join(".agent-inspect", "evidence", safe);
18625
+ return path33__default.default.join(".agent-inspect", "evidence", safe);
17860
18626
  }
17861
18627
  function resolveEvidenceOutputDir(evidenceDir, label) {
17862
18628
  if (evidenceDir !== void 0 && evidenceDir.trim() !== "") {
17863
- return path32__default.default.resolve(evidenceDir.trim());
18629
+ return path33__default.default.resolve(evidenceDir.trim());
17864
18630
  }
17865
18631
  return defaultEvidenceDir(label);
17866
18632
  }
@@ -18184,23 +18950,23 @@ function evaluatePromptInjection(text, options = {}) {
18184
18950
  }
18185
18951
  return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
18186
18952
  }
18187
- function validateSchemaField(value, field, path43, evidence) {
18953
+ function validateSchemaField(value, field, path44, evidence) {
18188
18954
  const ruleId = "guardrail.structured-output";
18189
18955
  if (field.type) {
18190
18956
  const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
18191
18957
  if (actual !== field.type) {
18192
- evidence.push({ ruleId, path: path43, preview: `expected ${field.type}, got ${actual}` });
18958
+ evidence.push({ ruleId, path: path44, preview: `expected ${field.type}, got ${actual}` });
18193
18959
  return;
18194
18960
  }
18195
18961
  }
18196
18962
  if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
18197
- evidence.push({ ruleId, path: path43, preview: "value not in enum" });
18963
+ evidence.push({ ruleId, path: path44, preview: "value not in enum" });
18198
18964
  }
18199
18965
  if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
18200
18966
  const record = value;
18201
18967
  for (const key of field.required) {
18202
18968
  if (!(key in record)) {
18203
- evidence.push({ ruleId, path: `${path43}.${key}`, preview: "missing required key" });
18969
+ evidence.push({ ruleId, path: `${path44}.${key}`, preview: "missing required key" });
18204
18970
  }
18205
18971
  }
18206
18972
  }
@@ -18929,7 +19695,7 @@ function parseCheckConfig(value) {
18929
19695
  }
18930
19696
  async function loadConfig(configPath) {
18931
19697
  if (configPath === void 0) return {};
18932
- const extension = path32__default.default.extname(configPath);
19698
+ const extension = path33__default.default.extname(configPath);
18933
19699
  if (TS_CONFIG_EXTENSIONS2.has(extension)) {
18934
19700
  throw new CheckConfigError(
18935
19701
  "AI_CHECK_CONFIG_LOAD_FAILED",
@@ -18942,7 +19708,7 @@ async function loadConfig(configPath) {
18942
19708
  "Unsupported check config extension. Use .json, .js, .mjs, or .cjs."
18943
19709
  );
18944
19710
  }
18945
- const absolute = path32__default.default.resolve(configPath);
19711
+ const absolute = path33__default.default.resolve(configPath);
18946
19712
  try {
18947
19713
  if (extension === ".json") {
18948
19714
  const raw = await promises.readFile(absolute, "utf-8");
@@ -19136,7 +19902,7 @@ function buildRules(config, options, presetSelect = []) {
19136
19902
  diagnostics
19137
19903
  };
19138
19904
  }
19139
- function exitCodeFor(result) {
19905
+ function exitCodeFor2(result) {
19140
19906
  if (result.status === "pass") return 0;
19141
19907
  if (result.status === "fail") return 1;
19142
19908
  const codes = result.diagnostics.map((item) => item.code);
@@ -19157,16 +19923,16 @@ function exitCodeFor(result) {
19157
19923
  }
19158
19924
  return 1;
19159
19925
  }
19160
- function stable3(value) {
19161
- if (Array.isArray(value)) return value.map(stable3);
19926
+ function stable4(value) {
19927
+ if (Array.isArray(value)) return value.map(stable4);
19162
19928
  if (value === null || typeof value !== "object") return value;
19163
19929
  const record = value;
19164
19930
  return Object.fromEntries(
19165
- Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable3(record[key])])
19931
+ Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable4(record[key])])
19166
19932
  );
19167
19933
  }
19168
- function printJson(result) {
19169
- console.log(JSON.stringify(stable3(result), null, 2));
19934
+ function printJson2(result) {
19935
+ console.log(JSON.stringify(stable4(result), null, 2));
19170
19936
  }
19171
19937
  function isSafetyFinding(ruleId) {
19172
19938
  return ruleId.startsWith("safety.") || ruleId.startsWith("guardrail.") || ruleId.includes("pii") || ruleId.includes("secret");
@@ -19203,7 +19969,7 @@ function printPresetClassSummary(result, preset) {
19203
19969
  `Share safety: ${hasSafetyFindings || result.status === "fail" ? "FAIL" : result.status === "pass" ? "PASS" : "FAIL"}`
19204
19970
  );
19205
19971
  }
19206
- function printHuman(result, options = {}) {
19972
+ function printHuman2(result, options = {}) {
19207
19973
  const scoped = result;
19208
19974
  if (scoped.scopeLabel) {
19209
19975
  console.log(`Scope: ${scoped.scopeKind} ${scoped.scopeLabel}`);
@@ -19223,13 +19989,13 @@ function printHuman(result, options = {}) {
19223
19989
  console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
19224
19990
  }
19225
19991
  for (const finding of result.findings) {
19226
- const path43 = finding.evidence[0]?.path;
19992
+ const path44 = finding.evidence[0]?.path;
19227
19993
  const run = finding.evidence[0]?.runId;
19228
19994
  const runPrefix = run ? `[${run}] ` : "";
19229
- console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path43 ? ` (${path43})` : ""}`);
19995
+ console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path44 ? ` (${path44})` : ""}`);
19230
19996
  }
19231
19997
  }
19232
- function readErrorResult(error) {
19998
+ function readErrorResult2(error) {
19233
19999
  if (error instanceof TraceReadError) {
19234
20000
  const code = error.code === "unsupported_format" ? "AI_CHECK_UNSUPPORTED_FORMAT" : error.code === "ambiguous_format" ? "AI_CHECK_AMBIGUOUS_FORMAT" : "AI_CHECK_TRACE_UNREADABLE";
19235
20001
  return errorResult2(code, error.message);
@@ -19256,9 +20022,9 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
19256
20022
  "AI_CHECK_CONFIG_NO_EFFECTIVE_RULES",
19257
20023
  "Explicit --config has no effective check rules. Configure checks.select, checks.run, checks.tool, checks.llm, checks.structure, or checks.safety."
19258
20024
  );
19259
- if (options.json) printJson(result);
19260
- else printHuman(result, options);
19261
- process.exitCode = exitCodeFor(result);
20025
+ if (options.json) printJson2(result);
20026
+ else printHuman2(result, options);
20027
+ process.exitCode = exitCodeFor2(result);
19262
20028
  return;
19263
20029
  }
19264
20030
  let effectiveOptions = options;
@@ -19388,10 +20154,10 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
19388
20154
  result = errorResult2(code, message);
19389
20155
  }
19390
20156
  } else {
19391
- result = readErrorResult(error);
20157
+ result = readErrorResult2(error);
19392
20158
  }
19393
20159
  }
19394
- process.exitCode = exitCodeFor(result);
20160
+ process.exitCode = exitCodeFor2(result);
19395
20161
  const failed = result.status !== "pass";
19396
20162
  if (shouldEmitEvidence(options.evidenceOn, failed)) {
19397
20163
  try {
@@ -19431,8 +20197,8 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
19431
20197
  );
19432
20198
  }
19433
20199
  }
19434
- if (options.json) printJson(result);
19435
- else printHuman(result, options);
20200
+ if (options.json) printJson2(result);
20201
+ else printHuman2(result, options);
19436
20202
  }
19437
20203
 
19438
20204
  // packages/viewer/src/server.ts
@@ -19589,7 +20355,7 @@ async function loadSuiteViewerData(options) {
19589
20355
  for (const suiteCase of result.cases) {
19590
20356
  cases.push(await enrichCase(suiteCase, baselinePath));
19591
20357
  }
19592
- const artifactsDir = path32__default.default.join(path32__default.default.dirname(result.configPath), ".agent-inspect/suite-runs");
20358
+ const artifactsDir = path33__default.default.join(path33__default.default.dirname(result.configPath), ".agent-inspect/suite-runs");
19593
20359
  return {
19594
20360
  suiteName: result.suiteName,
19595
20361
  configPath: result.configPath,
@@ -19785,19 +20551,19 @@ function serializeWorkspaceManifest(manifest) {
19785
20551
  }
19786
20552
  var INDEX_DIR_NAME = "index";
19787
20553
  function resolveWorkspaceLocation(cwd = process.cwd()) {
19788
- const projectRoot = path32__default.default.resolve(cwd);
19789
- const workspaceDir = path32__default.default.join(projectRoot, WORKSPACE_DIR_NAME);
20554
+ const projectRoot = path33__default.default.resolve(cwd);
20555
+ const workspaceDir = path33__default.default.join(projectRoot, WORKSPACE_DIR_NAME);
19790
20556
  return {
19791
20557
  projectRoot,
19792
20558
  workspaceDir,
19793
- manifestPath: path32__default.default.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
20559
+ manifestPath: path33__default.default.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
19794
20560
  };
19795
20561
  }
19796
20562
  function resolveInsideWorkspace(workspaceDir, relative) {
19797
- const base = path32__default.default.resolve(workspaceDir);
19798
- const resolved = path32__default.default.resolve(base, relative);
19799
- const rel = path32__default.default.relative(base, resolved);
19800
- if (rel === "" || rel === "." || !rel.startsWith("..") && !path32__default.default.isAbsolute(rel)) {
20563
+ const base = path33__default.default.resolve(workspaceDir);
20564
+ const resolved = path33__default.default.resolve(base, relative);
20565
+ const rel = path33__default.default.relative(base, resolved);
20566
+ if (rel === "" || rel === "." || !rel.startsWith("..") && !path33__default.default.isAbsolute(rel)) {
19801
20567
  return resolved;
19802
20568
  }
19803
20569
  throw new Error(
@@ -19866,7 +20632,7 @@ async function createWorkspace(options = {}) {
19866
20632
  created = false;
19867
20633
  adopted = true;
19868
20634
  } else {
19869
- const project = options.project?.trim() || path32__default.default.basename(location.projectRoot) || "workspace";
20635
+ const project = options.project?.trim() || path33__default.default.basename(location.projectRoot) || "workspace";
19870
20636
  const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
19871
20637
  manifest = createDefaultWorkspaceManifest({
19872
20638
  project,
@@ -20005,7 +20771,7 @@ async function doctorWorkspace(location) {
20005
20771
  const abs = resolveInsideWorkspace(location.workspaceDir, rel);
20006
20772
  for (const file of await listJsonl(abs)) {
20007
20773
  try {
20008
- const s = await promises.stat(path32__default.default.join(abs, file));
20774
+ const s = await promises.stat(path33__default.default.join(abs, file));
20009
20775
  newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
20010
20776
  } catch {
20011
20777
  checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
@@ -20053,7 +20819,7 @@ async function cleanWorkspace(location, manifest, options = {}) {
20053
20819
  const relPath = `${rel}/${entry}`;
20054
20820
  removed.push(relPath);
20055
20821
  if (!dryRun) {
20056
- await promises.rm(path32__default.default.join(abs, entry), { recursive: true, force: true });
20822
+ await promises.rm(path33__default.default.join(abs, entry), { recursive: true, force: true });
20057
20823
  }
20058
20824
  }
20059
20825
  }
@@ -20062,7 +20828,7 @@ async function cleanWorkspace(location, manifest, options = {}) {
20062
20828
 
20063
20829
  // packages/viewer/src/workspace-data.ts
20064
20830
  async function loadWorkspaceViewerData(options) {
20065
- const cwd = path32__default.default.resolve(options.cwd ?? process.cwd());
20831
+ const cwd = path33__default.default.resolve(options.cwd ?? process.cwd());
20066
20832
  const location = resolveWorkspaceLocation(cwd);
20067
20833
  const manifestRead = await readWorkspaceManifestFile(location);
20068
20834
  if (!manifestRead.ok || manifestRead.manifest === void 0) {
@@ -20075,7 +20841,7 @@ async function loadWorkspaceViewerData(options) {
20075
20841
  const runs = [];
20076
20842
  for (const rel of manifestRead.manifest.traceDirs) {
20077
20843
  const traceDir = resolveTraceDir({
20078
- dir: path32__default.default.join(location.workspaceDir, rel)
20844
+ dir: path33__default.default.join(location.workspaceDir, rel)
20079
20845
  });
20080
20846
  const td = new TraceDirectory({ dir: traceDir });
20081
20847
  const files = await td.list();
@@ -20089,7 +20855,7 @@ async function loadWorkspaceViewerData(options) {
20089
20855
  runId: meta2.runId,
20090
20856
  ...meta2.name !== void 0 ? { name: meta2.name } : {},
20091
20857
  status: meta2.status,
20092
- file: path32__default.default.basename(meta2.filePath)
20858
+ file: path33__default.default.basename(meta2.filePath)
20093
20859
  });
20094
20860
  }
20095
20861
  }
@@ -20100,8 +20866,8 @@ async function loadWorkspaceViewerData(options) {
20100
20866
  doctor,
20101
20867
  runs,
20102
20868
  bundleDirs: [
20103
- path32__default.default.join(location.workspaceDir, manifestRead.manifest.bundlesDir),
20104
- path32__default.default.join(location.workspaceDir, manifestRead.manifest.artifactsDir)
20869
+ path33__default.default.join(location.workspaceDir, manifestRead.manifest.bundlesDir),
20870
+ path33__default.default.join(location.workspaceDir, manifestRead.manifest.artifactsDir)
20105
20871
  ]
20106
20872
  };
20107
20873
  }
@@ -20164,7 +20930,7 @@ function createViewerServer(options = {}) {
20164
20930
  ok: true,
20165
20931
  readOnly: true,
20166
20932
  mode,
20167
- traceDir: path32__default.default.resolve(traceDir)
20933
+ traceDir: path33__default.default.resolve(traceDir)
20168
20934
  });
20169
20935
  }
20170
20936
  if (pathname === "/api/suite" && mode === "suite") {
@@ -20193,7 +20959,7 @@ function createViewerServer(options = {}) {
20193
20959
  runId: meta2.runId,
20194
20960
  name: meta2.name,
20195
20961
  status: meta2.status,
20196
- file: path32__default.default.basename(meta2.filePath),
20962
+ file: path33__default.default.basename(meta2.filePath),
20197
20963
  startedAt: meta2.startedAt,
20198
20964
  durationMs: meta2.durationMs
20199
20965
  }))
@@ -20310,7 +21076,7 @@ function startViewerServer(options = {}) {
20310
21076
  resolve({
20311
21077
  host,
20312
21078
  port: resolvedPort,
20313
- traceDir: path32__default.default.resolve(traceDir),
21079
+ traceDir: path33__default.default.resolve(traceDir),
20314
21080
  url: `http://${host}:${resolvedPort}/${modeQuery}`,
20315
21081
  mode
20316
21082
  });
@@ -20721,10 +21487,10 @@ async function evalRun(input3, options = {}) {
20721
21487
  diagnostics: []
20722
21488
  };
20723
21489
  }
20724
- function evidenceForRun(run, path43) {
20725
- return [{ runId: run.runId, ...path43 !== void 0 ? { path: path43 } : {} }];
21490
+ function evidenceForRun(run, path44) {
21491
+ return [{ runId: run.runId, ...path44 !== void 0 ? { path: path44 } : {} }];
20726
21492
  }
20727
- function evidenceForEvent(event, path43) {
21493
+ function evidenceForEvent(event, path44) {
20728
21494
  return [
20729
21495
  {
20730
21496
  runId: event.runId,
@@ -20732,7 +21498,7 @@ function evidenceForEvent(event, path43) {
20732
21498
  ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
20733
21499
  kind: event.kind,
20734
21500
  name: event.name,
20735
- ...path43 !== void 0 ? { path: path43 } : {}
21501
+ ...path44 !== void 0 ? { path: path44 } : {}
20736
21502
  }
20737
21503
  ];
20738
21504
  }
@@ -20890,9 +21656,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
20890
21656
  function tokenize(text) {
20891
21657
  return [...text.toLowerCase().matchAll(/[a-z0-9][a-z0-9'-]{2,}/g)].map((match) => match[0].replace(/^['-]+|['-]+$/g, "")).filter((token) => token.length > 2 && !STOP_WORDS.has(token));
20892
21658
  }
20893
- function firstEvidence(fields, run, path43) {
21659
+ function firstEvidence(fields, run, path44) {
20894
21660
  const first = fields[0];
20895
- return first === void 0 ? evidenceForRun(run, path43) : evidenceForEvent(first.node.event, first.path);
21661
+ return first === void 0 ? evidenceForRun(run, path44) : evidenceForEvent(first.node.event, first.path);
20896
21662
  }
20897
21663
  function collectSourceIds(nodes, keys) {
20898
21664
  const wanted = keySet(keys);
@@ -21245,273 +22011,55 @@ var checks = {
21245
22011
  "Answer contained banned unsupported-answer phrasing.",
21246
22012
  firstEvidence(answers, context.run, "attributes.answer"),
21247
22013
  { bannedPhraseCount: banned.length },
21248
- { matchedPhraseCount: matches.length }
21249
- )
21250
- ] : [];
21251
- });
21252
- }
21253
- };
21254
- function renderEvalMarkdown(result) {
21255
- const lines = [
21256
- `# AgentInspect Eval`,
21257
- "",
21258
- `Status: ${result.status}`,
21259
- `Format: ${result.format}`,
21260
- ...result.runId !== void 0 ? [`Run: ${result.runId}`] : [],
21261
- `Summary: ${result.summary.passed} passed, ${result.summary.failed} failed, ${result.summary.warnings} warnings, ${result.summary.errors} errors`
21262
- ];
21263
- if (result.diagnostics.length > 0) {
21264
- lines.push("", "## Diagnostics");
21265
- for (const diagnostic7 of result.diagnostics) {
21266
- lines.push(`- ${diagnostic7.code}: ${diagnostic7.message}`);
21267
- }
21268
- }
21269
- if (result.findings.length > 0) {
21270
- lines.push("", "## Findings");
21271
- for (const finding of result.findings) {
21272
- const path43 = finding.evidence[0]?.path;
21273
- lines.push(`- ${finding.ruleId}: ${finding.message}${path43 ? ` (${path43})` : ""}`);
21274
- }
21275
- }
21276
- return `${lines.join("\n")}
21277
- `;
21278
- }
21279
-
21280
- // packages/cli/src/eval.ts
21281
- var CONFIG_EXTENSIONS3 = /* @__PURE__ */ new Set([".json", ".js", ".mjs", ".cjs"]);
21282
- var TS_CONFIG_EXTENSIONS3 = /* @__PURE__ */ new Set([".ts", ".mts", ".cts"]);
21283
- function diagnostic6(code, message, severity = "error") {
21284
- return { code, message, severity };
21285
- }
21286
- function errorResult4(code, message, format = "unknown") {
21287
- return {
21288
- ok: false,
21289
- status: "error",
21290
- format,
21291
- summary: { passed: 0, failed: 0, warnings: 0, errors: 1 },
21292
- findings: [],
21293
- diagnostics: [diagnostic6(code, message)]
21294
- };
21295
- }
21296
- function parseNumber2(value, label) {
21297
- if (value === void 0) return void 0;
21298
- const parsed = Number(value);
21299
- if (!Number.isFinite(parsed) || parsed < 0) {
21300
- throw new Error(`${label} must be a non-negative number.`);
21301
- }
21302
- return parsed;
21303
- }
21304
- function asStringArray3(value, label) {
21305
- if (value === void 0) return void 0;
21306
- if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
21307
- throw new Error(`${label} must be an array of strings.`);
21308
- }
21309
- return value;
21310
- }
21311
- function asConfig(value) {
21312
- if (value === void 0 || value === null) return {};
21313
- if (typeof value !== "object" || Array.isArray(value)) {
21314
- throw new Error("Config must export an object.");
21315
- }
21316
- return value;
21317
- }
21318
- async function loadConfig2(configPath) {
21319
- if (configPath === void 0) return {};
21320
- const extension = path32__default.default.extname(configPath);
21321
- if (TS_CONFIG_EXTENSIONS3.has(extension)) {
21322
- throw new Error(
21323
- "TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
21324
- );
21325
- }
21326
- if (!CONFIG_EXTENSIONS3.has(extension)) {
21327
- throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
21328
- }
21329
- const absolute = path32__default.default.resolve(configPath);
21330
- if (extension === ".json") {
21331
- const raw = await promises.readFile(absolute, "utf-8");
21332
- return asConfig(JSON.parse(raw));
21333
- }
21334
- const mod = await import(url.pathToFileURL(absolute).href);
21335
- return asConfig("default" in mod ? mod.default : mod);
21336
- }
21337
- function normalizeConfig2(config) {
21338
- if (config.eval === void 0) return {};
21339
- if (typeof config.eval !== "object" || Array.isArray(config.eval)) {
21340
- throw new Error("eval config must be an object.");
21341
- }
21342
- return config.eval;
21343
- }
21344
- function maybeAdd(rules, value, makeRule) {
21345
- if (value !== void 0) rules.push(makeRule(value));
21346
- }
21347
- function optionObject(value) {
21348
- if (value === void 0 || value === false) return void 0;
21349
- if (value === true) return {};
21350
- if (typeof value !== "object" || Array.isArray(value)) {
21351
- throw new Error("Eval heuristic config must be a boolean or object.");
21352
- }
21353
- return value;
21354
- }
21355
- function sourceIdsFromConfig(value) {
21356
- if (value === void 0) return void 0;
21357
- if (Array.isArray(value)) return { ids: asStringArray3(value, "eval.requiredSourceIds") ?? [] };
21358
- if (typeof value !== "object" || Array.isArray(value)) {
21359
- throw new Error("eval.requiredSourceIds must be an array or object.");
21360
- }
21361
- const ids = asStringArray3(value.ids, "eval.requiredSourceIds.ids") ?? [];
21362
- return { ids, options: { sourceIdKeys: value.sourceIdKeys } };
21363
- }
21364
- function buildRules2(config, options) {
21365
- const evalConfig = normalizeConfig2(config);
21366
- const rules = [];
21367
- if (evalConfig.requireSuccess || options.requireSuccess) rules.push(checks.requireSuccess());
21368
- const requiredTools = [
21369
- ...asStringArray3(evalConfig.requiredTools, "eval.requiredTools") ?? [],
21370
- ...options.requiredTool ?? []
21371
- ];
21372
- if (requiredTools.length > 0) rules.push(checks.requiredTools(requiredTools));
21373
- const forbiddenTools = [
21374
- ...asStringArray3(evalConfig.forbiddenTools, "eval.forbiddenTools") ?? [],
21375
- ...options.forbidTool ?? [],
21376
- ...options.forbiddenTool ?? []
21377
- ];
21378
- if (forbiddenTools.length > 0) rules.push(checks.forbiddenTools(forbiddenTools));
21379
- const maxDurationMs = parseNumber2(options.maxDurationMs, "--max-duration-ms") ?? evalConfig.maxDurationMs;
21380
- maybeAdd(rules, maxDurationMs, checks.maxDurationMs);
21381
- const maxDepth = parseNumber2(options.maxDepth, "--max-depth") ?? evalConfig.maxDepth;
21382
- maybeAdd(rules, maxDepth, checks.maxDepth);
21383
- const maxRetries = parseNumber2(options.maxRetries, "--max-retries") ?? evalConfig.maxRetries;
21384
- maybeAdd(rules, maxRetries, checks.maxRetries);
21385
- const maxTotalTokens = parseNumber2(options.maxTotalTokens, "--max-total-tokens") ?? evalConfig.maxTotalTokens;
21386
- maybeAdd(rules, maxTotalTokens, checks.maxTotalTokens);
21387
- if (evalConfig.requiredRetrievalBeforeGeneration || options.requireRetrievalBeforeGeneration) {
21388
- rules.push(checks.requiredRetrievalBeforeGeneration());
21389
- }
21390
- const requiredDecisionMetadata = [
21391
- ...asStringArray3(
21392
- evalConfig.requiredDecisionMetadata,
21393
- "eval.requiredDecisionMetadata"
21394
- ) ?? [],
21395
- ...options.requiredDecisionMetadata ?? []
21396
- ];
21397
- if (requiredDecisionMetadata.length > 0) {
21398
- rules.push(checks.requiredDecisionMetadata(requiredDecisionMetadata));
21399
- }
21400
- const contextOverlap = optionObject(evalConfig.contextOverlap);
21401
- const minOverlap = parseNumber2(options.minContextOverlap, "--min-context-overlap");
21402
- const minSharedTerms = parseNumber2(options.minSharedTerms, "--min-shared-terms");
21403
- if (contextOverlap !== void 0 || options.contextOverlap || minOverlap !== void 0 || minSharedTerms !== void 0) {
21404
- rules.push(checks.contextOverlap({ ...contextOverlap, minOverlap, minSharedTerms }));
21405
- }
21406
- const quoteOverlap = optionObject(evalConfig.quoteOverlap);
21407
- if (quoteOverlap !== void 0 || options.quoteOverlap) {
21408
- rules.push(checks.quoteOverlap(quoteOverlap));
21409
- }
21410
- const citationPresence = optionObject(evalConfig.citationPresence);
21411
- if (citationPresence !== void 0 || options.citationPresence) {
21412
- rules.push(checks.citationPresence(citationPresence));
21413
- }
21414
- const sourceIds = sourceIdsFromConfig(evalConfig.requiredSourceIds);
21415
- const requiredSourceIds = [...sourceIds?.ids ?? [], ...options.requiredSourceId ?? []];
21416
- if (requiredSourceIds.length > 0) {
21417
- rules.push(checks.requiredSourceIds(requiredSourceIds, sourceIds?.options));
21418
- }
21419
- const cliAnswerLength = {
21420
- minCharacters: parseNumber2(options.minAnswerCharacters, "--min-answer-characters"),
21421
- maxCharacters: parseNumber2(options.maxAnswerCharacters, "--max-answer-characters"),
21422
- minWords: parseNumber2(options.minAnswerWords, "--min-answer-words"),
21423
- maxWords: parseNumber2(options.maxAnswerWords, "--max-answer-words")
21424
- };
21425
- const hasCliAnswerLength = Object.values(cliAnswerLength).some((value) => value !== void 0);
21426
- if (evalConfig.answerLengthBounds !== void 0 || hasCliAnswerLength) {
21427
- rules.push(checks.answerLengthBounds({ ...evalConfig.answerLengthBounds, ...cliAnswerLength }));
21428
- }
21429
- const bannedPhrases = [
21430
- ...asStringArray3(
21431
- evalConfig.bannedUnsupportedPhrases,
21432
- "eval.bannedUnsupportedPhrases"
21433
- ) ?? [],
21434
- ...options.bannedPhrase ?? []
21435
- ];
21436
- if (bannedPhrases.length > 0) rules.push(checks.bannedUnsupportedPhrases(bannedPhrases));
21437
- return rules.length > 0 ? rules : void 0;
21438
- }
21439
- function exitCodeFor2(result) {
21440
- if (result.status === "pass") return 0;
21441
- if (result.status === "fail") return 1;
21442
- return 2;
21443
- }
21444
- function stable4(value) {
21445
- if (Array.isArray(value)) return value.map(stable4);
21446
- if (value === null || typeof value !== "object") return value;
21447
- const record = value;
21448
- return Object.fromEntries(
21449
- Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable4(record[key])])
21450
- );
21451
- }
21452
- function printJson2(result) {
21453
- console.log(JSON.stringify(stable4(result), null, 2));
21454
- }
21455
- function printHuman2(result) {
21456
- console.log(`Eval status: ${result.status}`);
21457
- console.log(`Format: ${result.format}`);
21458
- if (result.runId !== void 0) console.log(`Run: ${result.runId}`);
21459
- console.log(
21460
- `Summary: ${result.summary.failed} failed, ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
21461
- );
21462
- for (const diagnostic7 of result.diagnostics) {
21463
- console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
21464
- }
21465
- for (const finding of result.findings) {
21466
- const path43 = finding.evidence[0]?.path;
21467
- console.log(`- ${finding.ruleId}: ${finding.message}${path43 ? ` (${path43})` : ""}`);
21468
- }
21469
- }
21470
- function readErrorResult2(error) {
21471
- if (error instanceof TraceReadError) {
21472
- const code = error.code === "unsupported_format" ? "AI_EVAL_UNSUPPORTED_FORMAT" : error.code === "ambiguous_format" ? "AI_EVAL_AMBIGUOUS_FORMAT" : "AI_EVAL_TRACE_UNREADABLE";
21473
- return errorResult4(code, error.message);
21474
- }
21475
- return errorResult4(
21476
- "AI_EVAL_TRACE_UNREADABLE",
21477
- error instanceof Error ? error.message : String(error)
21478
- );
21479
- }
21480
- async function evalCommand(target, options = {}, stdin = process.stdin) {
21481
- let result;
21482
- let phase = "config";
21483
- try {
21484
- const config = await loadConfig2(options.config);
21485
- const rules = buildRules2(config, options);
21486
- phase = "read";
21487
- const input3 = await inputFromTarget(target, options, stdin);
21488
- const read = await openTrace(input3, {
21489
- ...options.format !== void 0 ? { format: options.format } : {}
21490
- });
21491
- result = await evalRun(read, {
21492
- ...rules !== void 0 ? { checks: rules } : {},
21493
- ...options.run !== void 0 ? { runId: options.run } : {}
22014
+ { matchedPhraseCount: matches.length }
22015
+ )
22016
+ ] : [];
21494
22017
  });
21495
- } catch (error) {
21496
- if (phase === "config") {
21497
- const message = error instanceof Error ? error.message : String(error);
21498
- const code = message.startsWith("--") ? "AI_EVAL_INVALID_ARGUMENTS" : error instanceof SyntaxError || message.includes("Unsupported eval config extension") || message.includes("TypeScript eval configs") || message.includes("Config must") || message.includes("eval config") || message.includes("must be an array") ? "AI_EVAL_INVALID_CONFIG" : "AI_EVAL_CONFIG_LOAD_FAILED";
21499
- result = errorResult4(code, message);
21500
- } else {
21501
- result = readErrorResult2(error);
22018
+ }
22019
+ };
22020
+ function renderEvalMarkdown(result) {
22021
+ const lines = [
22022
+ `# AgentInspect Eval`,
22023
+ "",
22024
+ `Status: ${result.status}`,
22025
+ `Format: ${result.format}`,
22026
+ ...result.runId !== void 0 ? [`Run: ${result.runId}`] : [],
22027
+ `Summary: ${result.summary.passed} passed, ${result.summary.failed} failed, ${result.summary.warnings} warnings, ${result.summary.errors} errors`
22028
+ ];
22029
+ if (result.diagnostics.length > 0) {
22030
+ lines.push("", "## Diagnostics");
22031
+ for (const diagnostic7 of result.diagnostics) {
22032
+ lines.push(`- ${diagnostic7.code}: ${diagnostic7.message}`);
21502
22033
  }
21503
22034
  }
21504
- process.exitCode = exitCodeFor2(result);
21505
- if (options.json) printJson2(result);
21506
- else if (options.markdown) console.log(renderEvalMarkdown(result).trimEnd());
21507
- else printHuman2(result);
22035
+ if (result.findings.length > 0) {
22036
+ lines.push("", "## Findings");
22037
+ for (const finding of result.findings) {
22038
+ const path44 = finding.evidence[0]?.path;
22039
+ lines.push(`- ${finding.ruleId}: ${finding.message}${path44 ? ` (${path44})` : ""}`);
22040
+ }
22041
+ }
22042
+ return `${lines.join("\n")}
22043
+ `;
21508
22044
  }
21509
- var BEST_EFFORT_NOTE = "Best-effort local safety verification only; not a compliance, privacy, security, or regulatory certification.";
21510
- var DEFAULT_MAX_STRING_LENGTH = 16384;
21511
- var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
21512
- var DEFAULT_MAX_OBJECT_KEYS = 200;
21513
- var DEFAULT_MAX_SERIALIZED_BYTES = 128 * 1024;
21514
- function parseLimit3(value, label) {
22045
+
22046
+ // packages/cli/src/eval.ts
22047
+ var CONFIG_EXTENSIONS3 = /* @__PURE__ */ new Set([".json", ".js", ".mjs", ".cjs"]);
22048
+ var TS_CONFIG_EXTENSIONS3 = /* @__PURE__ */ new Set([".ts", ".mts", ".cts"]);
22049
+ function diagnostic6(code, message, severity = "error") {
22050
+ return { code, message, severity };
22051
+ }
22052
+ function errorResult4(code, message, format = "unknown") {
22053
+ return {
22054
+ ok: false,
22055
+ status: "error",
22056
+ format,
22057
+ summary: { passed: 0, failed: 0, warnings: 0, errors: 1 },
22058
+ findings: [],
22059
+ diagnostics: [diagnostic6(code, message)]
22060
+ };
22061
+ }
22062
+ function parseNumber2(value, label) {
21515
22063
  if (value === void 0) return void 0;
21516
22064
  const parsed = Number(value);
21517
22065
  if (!Number.isFinite(parsed) || parsed < 0) {
@@ -21519,368 +22067,210 @@ function parseLimit3(value, label) {
21519
22067
  }
21520
22068
  return parsed;
21521
22069
  }
21522
- function stable5(value) {
21523
- if (Array.isArray(value)) return value.map(stable5);
21524
- if (value === null || typeof value !== "object") return value;
21525
- const record = value;
21526
- return Object.fromEntries(
21527
- Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable5(record[key])])
21528
- );
21529
- }
21530
- function safetyDiagnostic(code, message, severity = "error") {
21531
- return { code, message, severity };
21532
- }
21533
- function warningDiagnostics(warnings, unsupportedFields) {
21534
- return [
21535
- ...warnings.map(
21536
- (warning) => safetyDiagnostic(
21537
- warning.code,
21538
- warning.message,
21539
- warning.severity === "error" ? "error" : "warning"
21540
- )
21541
- ),
21542
- ...unsupportedFields.map(
21543
- (field) => safetyDiagnostic(
21544
- "unsupported_field",
21545
- `Reader reported unsupported field: ${field}`,
21546
- "warning"
21547
- )
21548
- )
21549
- ];
22070
+ function asStringArray3(value, label) {
22071
+ if (value === void 0) return void 0;
22072
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
22073
+ throw new Error(`${label} must be an array of strings.`);
22074
+ }
22075
+ return value;
21550
22076
  }
21551
- function diagnosticFromCheck(item) {
21552
- return safetyDiagnostic(item.code, item.message, item.severity);
22077
+ function asConfig(value) {
22078
+ if (value === void 0 || value === null) return {};
22079
+ if (typeof value !== "object" || Array.isArray(value)) {
22080
+ throw new Error("Config must export an object.");
22081
+ }
22082
+ return value;
21553
22083
  }
21554
- function statusFrom(findings, diagnostics) {
21555
- if (diagnostics.some((item) => item.severity === "error")) return "UNKNOWN";
21556
- if (findings.some((item) => item.severity === "error")) return "UNSAFE";
21557
- if (diagnostics.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
21558
- if (findings.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
21559
- return "SAFE";
22084
+ async function loadConfig2(configPath) {
22085
+ if (configPath === void 0) return {};
22086
+ const extension = path33__default.default.extname(configPath);
22087
+ if (TS_CONFIG_EXTENSIONS3.has(extension)) {
22088
+ throw new Error(
22089
+ "TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
22090
+ );
22091
+ }
22092
+ if (!CONFIG_EXTENSIONS3.has(extension)) {
22093
+ throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
22094
+ }
22095
+ const absolute = path33__default.default.resolve(configPath);
22096
+ if (extension === ".json") {
22097
+ const raw = await promises.readFile(absolute, "utf-8");
22098
+ return asConfig(JSON.parse(raw));
22099
+ }
22100
+ const mod = await import(url.pathToFileURL(absolute).href);
22101
+ return asConfig("default" in mod ? mod.default : mod);
21560
22102
  }
21561
- function resultFromParts(parts) {
21562
- const findings = [...parts.findings ?? []];
21563
- const diagnostics = [...parts.diagnostics ?? []];
21564
- const warnings = [...parts.warnings ?? []];
21565
- const unsupportedFields = [...parts.unsupportedFields ?? []];
21566
- const status = parts.status ?? statusFrom(findings, diagnostics);
21567
- return {
21568
- ok: status === "SAFE" || status === "SAFE WITH WARNINGS",
21569
- command: parts.command,
21570
- status,
21571
- format: parts.format,
21572
- ...parts.runId !== void 0 ? { runId: parts.runId } : {},
21573
- summary: {
21574
- findings: findings.length,
21575
- warnings: diagnostics.filter((item) => item.severity === "warning").length + findings.filter((item) => item.severity === "warning").length,
21576
- errors: diagnostics.filter((item) => item.severity === "error").length + findings.filter((item) => item.severity === "error").length
21577
- },
21578
- findings,
21579
- diagnostics,
21580
- warnings,
21581
- unsupportedFields,
21582
- note: BEST_EFFORT_NOTE,
21583
- ...parts.sourceAssessment !== void 0 ? { sourceAssessment: parts.sourceAssessment } : {},
21584
- ...parts.artifactAssessment !== void 0 ? { artifactAssessment: parts.artifactAssessment } : {},
21585
- ...parts.redactionSummary !== void 0 ? { redactionSummary: parts.redactionSummary } : {}
21586
- };
22103
+ function normalizeConfig2(config) {
22104
+ if (config.eval === void 0) return {};
22105
+ if (typeof config.eval !== "object" || Array.isArray(config.eval)) {
22106
+ throw new Error("eval config must be an object.");
22107
+ }
22108
+ return config.eval;
21587
22109
  }
21588
- function layerFromResult(result) {
21589
- return {
21590
- status: result.status,
21591
- summary: result.summary,
21592
- findings: result.findings
21593
- };
22110
+ function maybeAdd(rules, value, makeRule) {
22111
+ if (value !== void 0) rules.push(makeRule(value));
21594
22112
  }
21595
- function readErrorResult3(command, error) {
21596
- if (error instanceof TraceReadError) {
21597
- const code = error.code === "unsupported_format" ? "AI_SAFETY_UNSUPPORTED_FORMAT" : error.code === "ambiguous_format" ? "AI_SAFETY_AMBIGUOUS_FORMAT" : "AI_SAFETY_TRACE_UNREADABLE";
21598
- return resultFromParts({
21599
- command,
21600
- format: "unknown",
21601
- diagnostics: [safetyDiagnostic(code, error.message)],
21602
- warnings: error.warnings
21603
- });
22113
+ function optionObject(value) {
22114
+ if (value === void 0 || value === false) return void 0;
22115
+ if (value === true) return {};
22116
+ if (typeof value !== "object" || Array.isArray(value)) {
22117
+ throw new Error("Eval heuristic config must be a boolean or object.");
21604
22118
  }
21605
- return resultFromParts({
21606
- command,
21607
- format: "unknown",
21608
- diagnostics: [
21609
- safetyDiagnostic(
21610
- "AI_SAFETY_TRACE_UNREADABLE",
21611
- error instanceof Error ? error.message : String(error)
21612
- )
21613
- ]
21614
- });
22119
+ return value;
21615
22120
  }
21616
- function invalidArgumentResult(command, error) {
21617
- return resultFromParts({
21618
- command,
21619
- format: "unknown",
21620
- diagnostics: [
21621
- safetyDiagnostic(
21622
- "AI_SAFETY_INVALID_ARGUMENTS",
21623
- error instanceof Error ? error.message : String(error)
21624
- )
21625
- ]
21626
- });
22121
+ function sourceIdsFromConfig(value) {
22122
+ if (value === void 0) return void 0;
22123
+ if (Array.isArray(value)) return { ids: asStringArray3(value, "eval.requiredSourceIds") ?? [] };
22124
+ if (typeof value !== "object" || Array.isArray(value)) {
22125
+ throw new Error("eval.requiredSourceIds must be an array or object.");
22126
+ }
22127
+ const ids = asStringArray3(value.ids, "eval.requiredSourceIds.ids") ?? [];
22128
+ return { ids, options: { sourceIdKeys: value.sourceIdKeys } };
21627
22129
  }
21628
- function buildSafetyRules(options) {
21629
- const maxStringLength2 = parseLimit3(options.maxStringLength, "--max-string-length") ?? DEFAULT_MAX_STRING_LENGTH;
21630
- const maxArrayLength = parseLimit3(options.maxArrayLength, "--max-array-length") ?? DEFAULT_MAX_ARRAY_LENGTH;
21631
- const maxObjectKeys = parseLimit3(options.maxObjectKeys, "--max-object-keys") ?? DEFAULT_MAX_OBJECT_KEYS;
21632
- const maxSerializedBytes = parseLimit3(options.maxSerializedBytes, "--max-serialized-bytes") ?? DEFAULT_MAX_SERIALIZED_BYTES;
21633
- return [
21634
- createSafetyRawContentRule(),
21635
- createSafetyRedactionRule(),
21636
- createSafetySecretPatternRule(),
21637
- createSafetyOversizedAttributeRule({
21638
- maxStringLength: maxStringLength2,
21639
- maxArrayLength,
21640
- maxObjectKeys,
21641
- maxSerializedBytes
21642
- })
22130
+ function buildRules2(config, options) {
22131
+ const evalConfig = normalizeConfig2(config);
22132
+ const rules = [];
22133
+ if (evalConfig.requireSuccess || options.requireSuccess) rules.push(checks.requireSuccess());
22134
+ const requiredTools = [
22135
+ ...asStringArray3(evalConfig.requiredTools, "eval.requiredTools") ?? [],
22136
+ ...options.requiredTool ?? []
21643
22137
  ];
21644
- }
21645
- function flattenNodes2(nodes) {
21646
- return nodes.flatMap((node) => [
21647
- node,
21648
- ...flattenNodes2(
21649
- node.children
21650
- )
21651
- ]);
21652
- }
21653
- function detectorSeverity(finding) {
21654
- return finding.severity;
21655
- }
21656
- function classifyRedactionDetector(detector) {
21657
- if (detector === "value.creditCard" || detector === "value.email" || detector === "value.phone") {
21658
- return { category: "personal-data", confidence: "high" };
22138
+ if (requiredTools.length > 0) rules.push(checks.requiredTools(requiredTools));
22139
+ const forbiddenTools = [
22140
+ ...asStringArray3(evalConfig.forbiddenTools, "eval.forbiddenTools") ?? [],
22141
+ ...options.forbidTool ?? [],
22142
+ ...options.forbiddenTool ?? []
22143
+ ];
22144
+ if (forbiddenTools.length > 0) rules.push(checks.forbiddenTools(forbiddenTools));
22145
+ const maxDurationMs = parseNumber2(options.maxDurationMs, "--max-duration-ms") ?? evalConfig.maxDurationMs;
22146
+ maybeAdd(rules, maxDurationMs, checks.maxDurationMs);
22147
+ const maxDepth = parseNumber2(options.maxDepth, "--max-depth") ?? evalConfig.maxDepth;
22148
+ maybeAdd(rules, maxDepth, checks.maxDepth);
22149
+ const maxRetries = parseNumber2(options.maxRetries, "--max-retries") ?? evalConfig.maxRetries;
22150
+ maybeAdd(rules, maxRetries, checks.maxRetries);
22151
+ const maxTotalTokens = parseNumber2(options.maxTotalTokens, "--max-total-tokens") ?? evalConfig.maxTotalTokens;
22152
+ maybeAdd(rules, maxTotalTokens, checks.maxTotalTokens);
22153
+ if (evalConfig.requiredRetrievalBeforeGeneration || options.requireRetrievalBeforeGeneration) {
22154
+ rules.push(checks.requiredRetrievalBeforeGeneration());
21659
22155
  }
21660
- if (detector === "value.ipv4" || detector === "value.ipv6") {
21661
- return { category: "identifier", confidence: "medium" };
22156
+ const requiredDecisionMetadata = [
22157
+ ...asStringArray3(
22158
+ evalConfig.requiredDecisionMetadata,
22159
+ "eval.requiredDecisionMetadata"
22160
+ ) ?? [],
22161
+ ...options.requiredDecisionMetadata ?? []
22162
+ ];
22163
+ if (requiredDecisionMetadata.length > 0) {
22164
+ rules.push(checks.requiredDecisionMetadata(requiredDecisionMetadata));
21662
22165
  }
21663
- if (detector.startsWith("value.") && (detector.includes("Token") || detector.includes("Key") || detector.includes("jwt") || detector.includes("authorization") || detector.includes("bearer") || detector.includes("cookie") || detector.includes("privateKey") || detector.includes("github") || detector.includes("aws") || detector.includes("provider"))) {
21664
- return { category: "credential", confidence: "high" };
22166
+ const contextOverlap = optionObject(evalConfig.contextOverlap);
22167
+ const minOverlap = parseNumber2(options.minContextOverlap, "--min-context-overlap");
22168
+ const minSharedTerms = parseNumber2(options.minSharedTerms, "--min-shared-terms");
22169
+ if (contextOverlap !== void 0 || options.contextOverlap || minOverlap !== void 0 || minSharedTerms !== void 0) {
22170
+ rules.push(checks.contextOverlap({ ...contextOverlap, minOverlap, minSharedTerms }));
21665
22171
  }
21666
- if (detector.startsWith("key.")) {
21667
- return { category: "credential", confidence: "medium" };
22172
+ const quoteOverlap = optionObject(evalConfig.quoteOverlap);
22173
+ if (quoteOverlap !== void 0 || options.quoteOverlap) {
22174
+ rules.push(checks.quoteOverlap(quoteOverlap));
21668
22175
  }
21669
- return { category: "credential", confidence: "medium" };
21670
- }
21671
- function redactionDetectorFindings(read, runId) {
21672
- const runs = runId === void 0 ? read.runs : read.runs.filter((run) => run.runId === runId);
21673
- const out = [];
21674
- for (const run of runs) {
21675
- for (const node of flattenNodes2(run.children)) {
21676
- const attrs = node.event.attributes;
21677
- if (attrs === void 0) continue;
21678
- const result = redact(attrs, { profile: "share" });
21679
- for (const finding of result.findings) {
21680
- if (finding.action === "keep") continue;
21681
- const taxonomy = classifyRedactionDetector(finding.detector);
21682
- out.push({
21683
- ruleId: "safety.redactDetector",
21684
- severity: detectorSeverity(finding),
21685
- status: finding.severity === "error" ? "fail" : "warning",
21686
- message: `Redaction detector ${finding.detector} matched ${finding.matchKind} at ${finding.path}.`,
21687
- expected: "redacted trace content",
21688
- actual: finding.detector,
21689
- evidence: [
21690
- {
21691
- runId: node.event.runId,
21692
- eventId: node.event.eventId,
21693
- ...node.event.parentId !== void 0 ? { parentId: node.event.parentId } : {},
21694
- kind: node.event.kind,
21695
- name: node.event.name,
21696
- ...node.event.status !== void 0 ? { status: node.event.status } : {},
21697
- path: `attributes.${finding.path.replace(/^\$\.?/, "")}`
21698
- }
21699
- ],
21700
- category: taxonomy.category,
21701
- confidence: taxonomy.confidence,
21702
- detector: finding.detector,
21703
- action: finding.action
21704
- });
21705
- }
21706
- }
22176
+ const citationPresence = optionObject(evalConfig.citationPresence);
22177
+ if (citationPresence !== void 0 || options.citationPresence) {
22178
+ rules.push(checks.citationPresence(citationPresence));
21707
22179
  }
21708
- return out.sort((a, b) => {
21709
- const aEvidence = a.evidence[0];
21710
- const bEvidence = b.evidence[0];
21711
- return (aEvidence?.runId ?? "").localeCompare(bEvidence?.runId ?? "") || (aEvidence?.eventId ?? "").localeCompare(bEvidence?.eventId ?? "") || (aEvidence?.path ?? "").localeCompare(bEvidence?.path ?? "") || a.message.localeCompare(b.message);
21712
- });
22180
+ const sourceIds = sourceIdsFromConfig(evalConfig.requiredSourceIds);
22181
+ const requiredSourceIds = [...sourceIds?.ids ?? [], ...options.requiredSourceId ?? []];
22182
+ if (requiredSourceIds.length > 0) {
22183
+ rules.push(checks.requiredSourceIds(requiredSourceIds, sourceIds?.options));
22184
+ }
22185
+ const cliAnswerLength = {
22186
+ minCharacters: parseNumber2(options.minAnswerCharacters, "--min-answer-characters"),
22187
+ maxCharacters: parseNumber2(options.maxAnswerCharacters, "--max-answer-characters"),
22188
+ minWords: parseNumber2(options.minAnswerWords, "--min-answer-words"),
22189
+ maxWords: parseNumber2(options.maxAnswerWords, "--max-answer-words")
22190
+ };
22191
+ const hasCliAnswerLength = Object.values(cliAnswerLength).some((value) => value !== void 0);
22192
+ if (evalConfig.answerLengthBounds !== void 0 || hasCliAnswerLength) {
22193
+ rules.push(checks.answerLengthBounds({ ...evalConfig.answerLengthBounds, ...cliAnswerLength }));
22194
+ }
22195
+ const bannedPhrases = [
22196
+ ...asStringArray3(
22197
+ evalConfig.bannedUnsupportedPhrases,
22198
+ "eval.bannedUnsupportedPhrases"
22199
+ ) ?? [],
22200
+ ...options.bannedPhrase ?? []
22201
+ ];
22202
+ if (bannedPhrases.length > 0) rules.push(checks.bannedUnsupportedPhrases(bannedPhrases));
22203
+ return rules.length > 0 ? rules : void 0;
21713
22204
  }
21714
22205
  function exitCodeFor3(result) {
21715
- if (result.status === "SAFE" || result.status === "SAFE WITH WARNINGS") return 0;
21716
- if (result.status === "UNSAFE") return 1;
22206
+ if (result.status === "pass") return 0;
22207
+ if (result.status === "fail") return 1;
21717
22208
  return 2;
21718
22209
  }
21719
- function explainFinding(finding, blocksBundle) {
21720
- const path43 = finding.evidence[0]?.path ?? "(unknown path)";
21721
- const category = finding.category ?? "structure";
21722
- const confidence = finding.confidence ?? "medium";
21723
- const detector = finding.detector ?? finding.ruleId;
21724
- const action = finding.action ?? "review";
21725
- const redactionHint = category === "credential" || category === "personal-data" || category === "raw-content" || action.includes("redact") ? "Usually removable by share/strict redaction before bundling." : "May require omitting the field, lowering limits, or an explicit local override.";
21726
- return [
21727
- ` Matched: detector=${detector}; path=${path43}; category=${category}`,
21728
- ` Why: ${finding.message}`,
21729
- ` Confidence: ${confidence}`,
21730
- ` Redaction: ${redactionHint}`,
21731
- ` Override: configure a custom redaction/detector rule locally (see docs/SAFETY-POLICY.md); do not weaken defaults globally.`,
21732
- ` Bundle gate: ${blocksBundle ? "blocks share-safe bundle unless --allow-unsafe" : "does not block by itself (warning/info)"}`
21733
- ];
22210
+ function stable5(value) {
22211
+ if (Array.isArray(value)) return value.map(stable5);
22212
+ if (value === null || typeof value !== "object") return value;
22213
+ const record = value;
22214
+ return Object.fromEntries(
22215
+ Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable5(record[key])])
22216
+ );
21734
22217
  }
21735
- function findingExplanation(finding) {
21736
- const blocks = finding.severity === "error" || finding.status === "fail";
21737
- return {
21738
- ruleId: finding.ruleId,
21739
- detector: finding.detector ?? finding.ruleId,
21740
- path: finding.evidence[0]?.path,
21741
- category: finding.category,
21742
- confidence: finding.confidence,
21743
- action: finding.action,
21744
- blocksBundle: blocks,
21745
- // Never include matched secret/PII values.
21746
- message: finding.message
21747
- };
22218
+ function printJson3(result) {
22219
+ console.log(JSON.stringify(stable5(result), null, 2));
21748
22220
  }
21749
- function printHuman3(result, explain = false) {
21750
- console.log(`Safety status: ${result.status}`);
22221
+ function printHuman3(result) {
22222
+ console.log(`Eval status: ${result.status}`);
21751
22223
  console.log(`Format: ${result.format}`);
21752
22224
  if (result.runId !== void 0) console.log(`Run: ${result.runId}`);
21753
- if (result.sourceAssessment !== void 0 && result.artifactAssessment !== void 0) {
21754
- console.log(`Source assessment: ${result.sourceAssessment.status}`);
21755
- console.log(`Artifact assessment: ${result.artifactAssessment.status}`);
21756
- if (result.redactionSummary !== void 0) {
21757
- console.log(
21758
- `Redaction: profile=${result.redactionSummary.profile}, findings=${result.redactionSummary.findings}`
21759
- );
21760
- }
21761
- }
21762
22225
  console.log(
21763
- `Summary: ${result.summary.findings} finding(s), ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
22226
+ `Summary: ${result.summary.failed} failed, ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
21764
22227
  );
21765
22228
  for (const diagnostic7 of result.diagnostics) {
21766
22229
  console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
21767
22230
  }
21768
22231
  for (const finding of result.findings) {
21769
- const path43 = finding.evidence[0]?.path;
21770
- const taxonomy = finding.category !== void 0 || finding.confidence !== void 0 ? ` [${[finding.category, finding.confidence].filter(Boolean).join("/")}]` : "";
21771
- console.log(`- ${finding.ruleId}: ${finding.message}${path43 ? ` (${path43})` : ""}${taxonomy}`);
21772
- if (explain) {
21773
- const blocks = finding.severity === "error" || finding.status === "fail";
21774
- for (const line of explainFinding(finding, blocks)) {
21775
- console.log(line);
21776
- }
21777
- }
22232
+ const path44 = finding.evidence[0]?.path;
22233
+ console.log(`- ${finding.ruleId}: ${finding.message}${path44 ? ` (${path44})` : ""}`);
21778
22234
  }
21779
- console.log(`Note: ${result.note}`);
21780
22235
  }
21781
- function printJson3(result, explain = false) {
21782
- const payload = explain === true ? { ...result, explanations: result.findings.map(findingExplanation) } : result;
21783
- console.log(JSON.stringify(stable5(payload), null, 2));
22236
+ function readErrorResult3(error) {
22237
+ if (error instanceof TraceReadError) {
22238
+ const code = error.code === "unsupported_format" ? "AI_EVAL_UNSUPPORTED_FORMAT" : error.code === "ambiguous_format" ? "AI_EVAL_AMBIGUOUS_FORMAT" : "AI_EVAL_TRACE_UNREADABLE";
22239
+ return errorResult4(code, error.message);
22240
+ }
22241
+ return errorResult4(
22242
+ "AI_EVAL_TRACE_UNREADABLE",
22243
+ error instanceof Error ? error.message : String(error)
22244
+ );
21784
22245
  }
21785
- async function safetyCommand(command, target, options, stdin) {
22246
+ async function evalCommand(target, options = {}, stdin = process.stdin) {
21786
22247
  let result;
22248
+ let phase = "config";
21787
22249
  try {
22250
+ const config = await loadConfig2(options.config);
22251
+ const rules = buildRules2(config, options);
22252
+ phase = "read";
21788
22253
  const input3 = await inputFromTarget(target, options, stdin);
21789
22254
  const read = await openTrace(input3, {
21790
22255
  ...options.format !== void 0 ? { format: options.format } : {}
21791
22256
  });
21792
- const source = assessOpenedTrace(read, {
21793
- ...options,
22257
+ result = await evalRun(read, {
22258
+ ...rules !== void 0 ? { checks: rules } : {},
21794
22259
  ...options.run !== void 0 ? { runId: options.run } : {}
21795
22260
  });
21796
- if (command === "scan") {
21797
- result = { ...source, command: "scan" };
22261
+ } catch (error) {
22262
+ if (phase === "config") {
22263
+ const message = error instanceof Error ? error.message : String(error);
22264
+ const code = message.startsWith("--") ? "AI_EVAL_INVALID_ARGUMENTS" : error instanceof SyntaxError || message.includes("Unsupported eval config extension") || message.includes("TypeScript eval configs") || message.includes("Config must") || message.includes("eval config") || message.includes("must be an array") ? "AI_EVAL_INVALID_CONFIG" : "AI_EVAL_CONFIG_LOAD_FAILED";
22265
+ result = errorResult4(code, message);
21798
22266
  } else {
21799
- const profile = options.redactionProfile ?? "share";
21800
- const rawContent = input3.type === "string" ? input3.content : input3.type === "file" ? await promises.readFile(input3.path, "utf-8") : void 0;
21801
- if (rawContent === void 0) {
21802
- result = {
21803
- ...source,
21804
- command: "verify-safe",
21805
- sourceAssessment: layerFromResult(source)
21806
- };
21807
- } else {
21808
- const redacted = redactTraceContent(rawContent, profile);
21809
- const artifactRead = await openTrace(
21810
- { type: "string", content: redacted.content },
21811
- { format: options.format ?? "agent-inspect-jsonl" }
21812
- );
21813
- const artifact = assessOpenedTrace(artifactRead, {
21814
- ...options,
21815
- ...options.run !== void 0 ? { runId: options.run } : {}
21816
- });
21817
- const detectors = [
21818
- ...new Set(redacted.findings.map((finding) => finding.detector))
21819
- ].sort((a, b) => a.localeCompare(b));
21820
- result = resultFromParts({
21821
- command: "verify-safe",
21822
- format: artifact.format,
21823
- runId: artifact.runId ?? source.runId,
21824
- findings: artifact.findings,
21825
- diagnostics: artifact.diagnostics,
21826
- warnings: artifact.warnings,
21827
- unsupportedFields: artifact.unsupportedFields,
21828
- status: artifact.status,
21829
- sourceAssessment: layerFromResult(source),
21830
- artifactAssessment: layerFromResult(artifact),
21831
- redactionSummary: {
21832
- profile,
21833
- findings: redacted.findings.length,
21834
- detectors
21835
- }
21836
- });
21837
- }
22267
+ result = readErrorResult3(error);
21838
22268
  }
21839
- } catch (error) {
21840
- const message = error instanceof Error ? error.message : String(error);
21841
- result = message.startsWith("--") ? invalidArgumentResult(command, error) : readErrorResult3(command, error);
21842
22269
  }
21843
22270
  process.exitCode = exitCodeFor3(result);
21844
- if (options.json) printJson3(result, options.explain === true);
21845
- else printHuman3(result, options.explain === true);
21846
- }
21847
- function scanCommand(target, options = {}, stdin = process.stdin) {
21848
- return safetyCommand("scan", target, options, stdin);
21849
- }
21850
- function verifySafeCommand(target, options = {}, stdin = process.stdin) {
21851
- return safetyCommand("verify-safe", target, options, stdin);
21852
- }
21853
- function assessOpenedTrace(read, options = {}) {
21854
- try {
21855
- const rules = buildSafetyRules(options);
21856
- const checkResult = runTraceChecks(
21857
- { read },
21858
- {
21859
- rules,
21860
- ...options.runId !== void 0 ? { runId: options.runId } : {},
21861
- ...options.run !== void 0 ? { runId: options.run } : {}
21862
- }
21863
- );
21864
- const detectorFindings = checkResult.diagnostics.length === 0 ? redactionDetectorFindings(read, checkResult.runId) : [];
21865
- return resultFromParts({
21866
- command: "verify-safe",
21867
- format: checkResult.format,
21868
- runId: checkResult.runId,
21869
- findings: [...checkResult.findings, ...detectorFindings],
21870
- diagnostics: [
21871
- ...checkResult.diagnostics.map(diagnosticFromCheck),
21872
- ...warningDiagnostics(read.warnings, read.unsupportedFields)
21873
- ],
21874
- warnings: read.warnings,
21875
- unsupportedFields: read.unsupportedFields
21876
- });
21877
- } catch (error) {
21878
- return messageStartsWithDash(error) ? invalidArgumentResult("verify-safe", error) : readErrorResult3("verify-safe", error);
21879
- }
21880
- }
21881
- function messageStartsWithDash(error) {
21882
- const message = error instanceof Error ? error.message : String(error);
21883
- return message.startsWith("--");
22271
+ if (options.json) printJson3(result);
22272
+ else if (options.markdown) console.log(renderEvalMarkdown(result).trimEnd());
22273
+ else printHuman3(result);
21884
22274
  }
21885
22275
  init_advanced();
21886
22276
  var NOTE = "Generated locally by AgentInspect. Artifacts are best-effort summaries, not compliance or security certification.";
@@ -21956,8 +22346,8 @@ function renderCheckSection(result) {
21956
22346
  `Diagnostics: ${result.diagnostics.length}`
21957
22347
  ];
21958
22348
  for (const finding of result.findings.slice(0, 10)) {
21959
- const path43 = finding.evidence[0]?.path ?? "(run)";
21960
- lines.push(`- ${finding.ruleId}: ${finding.message} (${path43})`);
22349
+ const path44 = finding.evidence[0]?.path ?? "(run)";
22350
+ lines.push(`- ${finding.ruleId}: ${finding.message} (${path44})`);
21961
22351
  }
21962
22352
  for (const diagnostic7 of result.diagnostics.slice(0, 10)) {
21963
22353
  lines.push(`- ${diagnostic7.code}: ${diagnostic7.message}`);
@@ -22035,8 +22425,8 @@ function renderHtml(trace, check, diff) {
22035
22425
  `;
22036
22426
  }
22037
22427
  async function writeArtifact(outputDir, relativePath, content, files) {
22038
- const outPath = path32__default.default.join(outputDir, relativePath);
22039
- await promises.mkdir(path32__default.default.dirname(outPath), { recursive: true });
22428
+ const outPath = path33__default.default.join(outputDir, relativePath);
22429
+ await promises.mkdir(path33__default.default.dirname(outPath), { recursive: true });
22040
22430
  await promises.writeFile(outPath, content, "utf-8");
22041
22431
  files.push(relativePath);
22042
22432
  }
@@ -22065,7 +22455,7 @@ function shouldWriteEvidence(options, status) {
22065
22455
  return status === "unsafe" || status === "regression" || status === "unknown" || status === "warning";
22066
22456
  }
22067
22457
  async function artifactsCommand(target, options = {}, stdin = process.stdin) {
22068
- const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path32__default.default.resolve(options.outputDir.trim()) : "";
22458
+ const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path33__default.default.resolve(options.outputDir.trim()) : "";
22069
22459
  if (outputDir === "") {
22070
22460
  console.error("--output-dir is required.");
22071
22461
  process.exitCode = 1;
@@ -22205,8 +22595,8 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
22205
22595
  }
22206
22596
  const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
22207
22597
  if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
22208
- await promises.mkdir(path32__default.default.dirname(path32__default.default.resolve(summaryTarget)), { recursive: true });
22209
- await promises.appendFile(path32__default.default.resolve(summaryTarget), `
22598
+ await promises.mkdir(path33__default.default.dirname(path33__default.default.resolve(summaryTarget)), { recursive: true });
22599
+ await promises.appendFile(path33__default.default.resolve(summaryTarget), `
22210
22600
  ${renderMarkdown(trace, check, diff)}`, "utf-8");
22211
22601
  }
22212
22602
  const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
@@ -22225,10 +22615,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
22225
22615
  findings: diff?.findings.length ?? 0,
22226
22616
  diagnostics: diff?.diagnostics.length ?? 0
22227
22617
  },
22228
- ...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path32__default.default.resolve(summaryTarget) } : {},
22618
+ ...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path33__default.default.resolve(summaryTarget) } : {},
22229
22619
  note: NOTE
22230
22620
  };
22231
- await promises.writeFile(path32__default.default.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
22621
+ await promises.writeFile(path33__default.default.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
22232
22622
  if (options.json === true) {
22233
22623
  console.log(writeJson3(manifest).trimEnd());
22234
22624
  } else {
@@ -22289,8 +22679,8 @@ async function resolveOutputDir(options, runIds, cwd, format) {
22289
22679
  const location = resolveWorkspaceLocation(cwd);
22290
22680
  const manifest = await readWorkspaceManifestFile(location);
22291
22681
  if (manifest.ok && manifest.manifest) {
22292
- const rel = path32__default.default.relative(location.workspaceDir, normalized);
22293
- if (!rel.startsWith("..") && !path32__default.default.isAbsolute(rel)) {
22682
+ const rel = path33__default.default.relative(location.workspaceDir, normalized);
22683
+ if (!rel.startsWith("..") && !path33__default.default.isAbsolute(rel)) {
22294
22684
  return resolveInsideWorkspace(location.workspaceDir, rel);
22295
22685
  }
22296
22686
  }
@@ -22306,7 +22696,7 @@ async function resolveOutputDir(options, runIds, cwd, format) {
22306
22696
  const label = runIds.length === 1 ? sanitizeBundleRunId(runIds[0]) : `multi-${runIds.length}`;
22307
22697
  const base2 = resolveInsideWorkspace(
22308
22698
  location.workspaceDir,
22309
- path32__default.default.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
22699
+ path33__default.default.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
22310
22700
  );
22311
22701
  return format === "zip" ? `${base2}.zip` : base2;
22312
22702
  }
@@ -22711,19 +23101,19 @@ async function bundleCommand(runIdArg, options = {}) {
22711
23101
  let sidecarDir = outputDir;
22712
23102
  if (outputDir.toLowerCase().endsWith(".html")) {
22713
23103
  htmlPath = outputDir;
22714
- sidecarDir = path32__default.default.dirname(outputDir);
23104
+ sidecarDir = path33__default.default.dirname(outputDir);
22715
23105
  } else {
22716
23106
  await promises.mkdir(outputDir, { recursive: true });
22717
- htmlPath = path32__default.default.join(outputDir, EVIDENCE_HTML_FILENAME);
23107
+ htmlPath = path33__default.default.join(outputDir, EVIDENCE_HTML_FILENAME);
22718
23108
  sidecarDir = outputDir;
22719
23109
  }
22720
23110
  await promises.mkdir(sidecarDir, { recursive: true });
22721
23111
  await promises.writeFile(htmlPath, htmlContent, "utf-8");
22722
- await promises.writeFile(path32__default.default.join(sidecarDir, EVIDENCE_MANIFEST_FILENAME), evidenceJson, "utf-8");
23112
+ await promises.writeFile(path33__default.default.join(sidecarDir, EVIDENCE_MANIFEST_FILENAME), evidenceJson, "utf-8");
22723
23113
  outputPath = htmlPath;
22724
23114
  } else if (format === "zip") {
22725
23115
  const zipPath = outputDir.toLowerCase().endsWith(".zip") ? outputDir : `${outputDir}.zip`;
22726
- const zipParent = path32__default.default.dirname(zipPath);
23116
+ const zipParent = path33__default.default.dirname(zipPath);
22727
23117
  await promises.mkdir(zipParent, { recursive: true });
22728
23118
  const entries = [
22729
23119
  ...[...packaged.entries()].map(([relativePath, content]) => ({
@@ -22739,10 +23129,10 @@ async function bundleCommand(runIdArg, options = {}) {
22739
23129
  await promises.mkdir(outputDir, { recursive: true });
22740
23130
  for (const [relativePath, content] of packaged.entries()) {
22741
23131
  const outPath = assertBundlePathContained(outputDir, relativePath);
22742
- await promises.mkdir(path32__default.default.dirname(outPath), { recursive: true });
23132
+ await promises.mkdir(path33__default.default.dirname(outPath), { recursive: true });
22743
23133
  await promises.writeFile(outPath, content, "utf-8");
22744
23134
  }
22745
- await promises.writeFile(path32__default.default.join(outputDir, EVIDENCE_MANIFEST_FILENAME), evidenceJson, "utf-8");
23135
+ await promises.writeFile(path33__default.default.join(outputDir, EVIDENCE_MANIFEST_FILENAME), evidenceJson, "utf-8");
22746
23136
  if (!files.includes(EVIDENCE_MANIFEST_FILENAME)) {
22747
23137
  files.push(EVIDENCE_MANIFEST_FILENAME);
22748
23138
  }
@@ -22791,7 +23181,7 @@ function writeJson5(value) {
22791
23181
  `;
22792
23182
  }
22793
23183
  async function bundleVerifyCommand(targetPath, options = {}) {
22794
- const root = path32__default.default.resolve(targetPath.trim() || ".");
23184
+ const root = path33__default.default.resolve(targetPath.trim() || ".");
22795
23185
  const result = await verifyEvidenceDirectory(root, {
22796
23186
  unexpectedFiles: options.unexpected ?? "fail"
22797
23187
  });
@@ -22833,7 +23223,7 @@ function writeJson6(value) {
22833
23223
  }
22834
23224
  async function resolveHtmlPath(root) {
22835
23225
  const candidates = [
22836
- path32__default.default.join(root, "evidence.html"),
23226
+ path33__default.default.join(root, "evidence.html"),
22837
23227
  root.toLowerCase().endsWith(".html") ? root : ""
22838
23228
  ].filter(Boolean);
22839
23229
  for (const candidate of candidates) {
@@ -22848,7 +23238,7 @@ async function resolveHtmlPath(root) {
22848
23238
  );
22849
23239
  }
22850
23240
  async function bundleOpenCommand(targetPath, options = {}) {
22851
- const root = path32__default.default.resolve(targetPath.trim() || ".");
23241
+ const root = path33__default.default.resolve(targetPath.trim() || ".");
22852
23242
  try {
22853
23243
  await promises.access(root);
22854
23244
  } catch {
@@ -22865,7 +23255,7 @@ async function bundleOpenCommand(targetPath, options = {}) {
22865
23255
  try {
22866
23256
  const info = await promises.stat(root);
22867
23257
  if (info.isFile() && root.toLowerCase().endsWith(".html")) {
22868
- verifyRoot = path32__default.default.dirname(root);
23258
+ verifyRoot = path33__default.default.dirname(root);
22869
23259
  }
22870
23260
  } catch {
22871
23261
  }
@@ -22897,7 +23287,7 @@ async function bundleOpenCommand(targetPath, options = {}) {
22897
23287
  let htmlPath;
22898
23288
  try {
22899
23289
  htmlPath = await resolveHtmlPath(
22900
- root.toLowerCase().endsWith(".html") ? path32__default.default.dirname(root) : root
23290
+ root.toLowerCase().endsWith(".html") ? path33__default.default.dirname(root) : root
22901
23291
  );
22902
23292
  if (root.toLowerCase().endsWith(".html")) {
22903
23293
  htmlPath = root;
@@ -22913,7 +23303,7 @@ async function bundleOpenCommand(targetPath, options = {}) {
22913
23303
  return;
22914
23304
  }
22915
23305
  try {
22916
- await promises.readFile(path32__default.default.join(path32__default.default.dirname(htmlPath), "evidence.json"), "utf-8");
23306
+ await promises.readFile(path33__default.default.join(path33__default.default.dirname(htmlPath), "evidence.json"), "utf-8");
22917
23307
  } catch {
22918
23308
  }
22919
23309
  const fileUrl = url.pathToFileURL(htmlPath).href;
@@ -22999,24 +23389,24 @@ function resolveTargetPath(client, projectLocal) {
22999
23389
  if (projectLocal) {
23000
23390
  switch (client) {
23001
23391
  case "cursor":
23002
- return path32__default.default.join(".cursor", "mcp.json");
23392
+ return path33__default.default.join(".cursor", "mcp.json");
23003
23393
  case "claude-code":
23004
- return path32__default.default.join(".mcp.json");
23394
+ return path33__default.default.join(".mcp.json");
23005
23395
  case "codex":
23006
- return path32__default.default.join(".codex", "config.toml.json");
23396
+ return path33__default.default.join(".codex", "config.toml.json");
23007
23397
  case "gemini":
23008
- return path32__default.default.join(".gemini", "settings.json");
23398
+ return path33__default.default.join(".gemini", "settings.json");
23009
23399
  }
23010
23400
  }
23011
23401
  switch (client) {
23012
23402
  case "cursor":
23013
- return path32__default.default.join("~", ".cursor", "mcp.json");
23403
+ return path33__default.default.join("~", ".cursor", "mcp.json");
23014
23404
  case "claude-code":
23015
- return path32__default.default.join("~", ".claude.json");
23405
+ return path33__default.default.join("~", ".claude.json");
23016
23406
  case "codex":
23017
- return path32__default.default.join("~", ".codex", "config.toml");
23407
+ return path33__default.default.join("~", ".codex", "config.toml");
23018
23408
  case "gemini":
23019
- return path32__default.default.join("~", ".gemini", "settings.json");
23409
+ return path33__default.default.join("~", ".gemini", "settings.json");
23020
23410
  }
23021
23411
  }
23022
23412
  async function mcpConfigureCommand(options) {
@@ -23040,8 +23430,8 @@ async function mcpConfigureCommand(options) {
23040
23430
  ];
23041
23431
  let wrote = false;
23042
23432
  if (!dryRun && projectLocal) {
23043
- const abs = path32__default.default.resolve(targetPath);
23044
- await promises.mkdir(path32__default.default.dirname(abs), { recursive: true });
23433
+ const abs = path33__default.default.resolve(targetPath);
23434
+ await promises.mkdir(path33__default.default.dirname(abs), { recursive: true });
23045
23435
  await promises.writeFile(abs, `${JSON.stringify(config, null, 2)}
23046
23436
  `, "utf8");
23047
23437
  wrote = true;
@@ -23201,23 +23591,23 @@ function readManifestDocument(value) {
23201
23591
  };
23202
23592
  }
23203
23593
  function cwdRelative(filePath) {
23204
- const relative = path32__default.default.relative(process.cwd(), path32__default.default.resolve(filePath)).replace(/\\/g, "/");
23205
- if (relative === "" || relative.startsWith("../") || path32__default.default.isAbsolute(relative)) {
23206
- return path32__default.default.basename(filePath);
23594
+ const relative = path33__default.default.relative(process.cwd(), path33__default.default.resolve(filePath)).replace(/\\/g, "/");
23595
+ if (relative === "" || relative.startsWith("../") || path33__default.default.isAbsolute(relative)) {
23596
+ return path33__default.default.basename(filePath);
23207
23597
  }
23208
23598
  return relative;
23209
23599
  }
23210
23600
  async function readReporterManifest(filePath) {
23211
- const absolute = path32__default.default.resolve(filePath);
23601
+ const absolute = path33__default.default.resolve(filePath);
23212
23602
  const raw = await promises.readFile(absolute, "utf-8");
23213
23603
  const document = readManifestDocument(JSON.parse(raw));
23214
23604
  const manifest = document.manifest;
23215
23605
  const results = manifest.results.map((result) => ({
23216
23606
  testId: safeText(result.testId),
23217
23607
  name: safeText(result.name),
23218
- ...result.file === void 0 ? {} : { file: safeText(path32__default.default.basename(result.file)) },
23608
+ ...result.file === void 0 ? {} : { file: safeText(path33__default.default.basename(result.file)) },
23219
23609
  status: result.status,
23220
- ...result.tracePath === void 0 ? {} : { tracePath: safeText(path32__default.default.basename(result.tracePath)) },
23610
+ ...result.tracePath === void 0 ? {} : { tracePath: safeText(path33__default.default.basename(result.tracePath)) },
23221
23611
  artifacts: result.artifacts,
23222
23612
  diagnostics: result.diagnostics
23223
23613
  }));
@@ -23356,15 +23746,15 @@ async function ciSummaryCommand(manifestPaths, options = {}) {
23356
23746
  return;
23357
23747
  }
23358
23748
  const markdown = renderMarkdown2(result);
23359
- const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path32__default.default.resolve(options.output.trim()) : void 0;
23749
+ const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path33__default.default.resolve(options.output.trim()) : void 0;
23360
23750
  if (outputPath !== void 0) {
23361
- await promises.mkdir(path32__default.default.dirname(outputPath), { recursive: true });
23751
+ await promises.mkdir(path33__default.default.dirname(outputPath), { recursive: true });
23362
23752
  await promises.writeFile(outputPath, markdown, "utf-8");
23363
23753
  }
23364
23754
  const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
23365
23755
  if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
23366
- const summaryPath = path32__default.default.resolve(summaryTarget);
23367
- await promises.mkdir(path32__default.default.dirname(summaryPath), { recursive: true });
23756
+ const summaryPath = path33__default.default.resolve(summaryTarget);
23757
+ await promises.mkdir(path33__default.default.dirname(summaryPath), { recursive: true });
23368
23758
  await promises.appendFile(summaryPath, `
23369
23759
  ${markdown}`, "utf-8");
23370
23760
  }
@@ -23549,7 +23939,7 @@ jobs:
23549
23939
  }
23550
23940
  async function planInit(options = {}) {
23551
23941
  const framework = normalizeFramework(options.framework);
23552
- const cwd = path32__default.default.resolve(options.cwd ?? process.cwd());
23942
+ const cwd = path33__default.default.resolve(options.cwd ?? process.cwd());
23553
23943
  const demoPath = framework === "custom" ? "examples/agent-inspect-demo.mjs" : `examples/agent-inspect-${framework}-demo.mjs`;
23554
23944
  const candidates = [
23555
23945
  { rel: CONFIG_FILE, content: configTemplate(framework) },
@@ -23564,7 +23954,7 @@ async function planInit(options = {}) {
23564
23954
  }
23565
23955
  const files = [];
23566
23956
  for (const candidate of candidates) {
23567
- const abs = path32__default.default.join(cwd, candidate.rel);
23957
+ const abs = path33__default.default.join(cwd, candidate.rel);
23568
23958
  try {
23569
23959
  await promises.access(abs);
23570
23960
  files.push({
@@ -23585,12 +23975,12 @@ async function writePlannedFiles(plan, cwd, options) {
23585
23975
  if (entry.action === "skip") {
23586
23976
  continue;
23587
23977
  }
23588
- const abs = path32__default.default.join(cwd, entry.path);
23978
+ const abs = path33__default.default.join(cwd, entry.path);
23589
23979
  if (options.dryRun) {
23590
23980
  written.push(entry.path);
23591
23981
  continue;
23592
23982
  }
23593
- await promises.mkdir(path32__default.default.dirname(abs), { recursive: true });
23983
+ await promises.mkdir(path33__default.default.dirname(abs), { recursive: true });
23594
23984
  const demoPath = plan.framework === "custom" ? "examples/agent-inspect-demo.mjs" : `examples/agent-inspect-${plan.framework}-demo.mjs`;
23595
23985
  const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate(demoPath) : demoTemplate(plan.framework);
23596
23986
  await promises.writeFile(abs, content, "utf-8");
@@ -23599,7 +23989,7 @@ async function writePlannedFiles(plan, cwd, options) {
23599
23989
  return written;
23600
23990
  }
23601
23991
  async function initCommand(options = {}) {
23602
- const cwd = path32__default.default.resolve(options.cwd ?? process.cwd());
23992
+ const cwd = path33__default.default.resolve(options.cwd ?? process.cwd());
23603
23993
  try {
23604
23994
  const plan = await planInit({ ...options, cwd });
23605
23995
  const toWrite = plan.files.filter((file) => file.action === "create").map((f) => f.path);
@@ -23694,7 +24084,7 @@ function envCheck(name, optional = true) {
23694
24084
  };
23695
24085
  }
23696
24086
  async function traceDirWritable(traceDir) {
23697
- const resolved = path32__default.default.resolve(traceDir);
24087
+ const resolved = path33__default.default.resolve(traceDir);
23698
24088
  try {
23699
24089
  await promises.mkdir(resolved, { recursive: true });
23700
24090
  await promises.access(resolved, promises.constants.W_OK);
@@ -23715,10 +24105,10 @@ async function traceDirWritable(traceDir) {
23715
24105
  }
23716
24106
  }
23717
24107
  function readPackageVersionNearEntry(entryPath, packageName) {
23718
- let dir = path32__default.default.dirname(path32__default.default.resolve(entryPath));
23719
- const { root } = path32__default.default.parse(dir);
24108
+ let dir = path33__default.default.dirname(path33__default.default.resolve(entryPath));
24109
+ const { root } = path33__default.default.parse(dir);
23720
24110
  while (true) {
23721
- const candidate = path32__default.default.join(dir, "package.json");
24111
+ const candidate = path33__default.default.join(dir, "package.json");
23722
24112
  if (fs.existsSync(candidate)) {
23723
24113
  try {
23724
24114
  const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
@@ -23729,14 +24119,14 @@ function readPackageVersionNearEntry(entryPath, packageName) {
23729
24119
  }
23730
24120
  }
23731
24121
  if (dir === root) break;
23732
- const parent = path32__default.default.dirname(dir);
24122
+ const parent = path33__default.default.dirname(dir);
23733
24123
  if (parent === dir) break;
23734
24124
  dir = parent;
23735
24125
  }
23736
24126
  return void 0;
23737
24127
  }
23738
24128
  function resolveInstalledPackage(cwd, name) {
23739
- const require2 = module$1.createRequire(path32__default.default.join(cwd, "package.json"));
24129
+ const require2 = module$1.createRequire(path33__default.default.join(cwd, "package.json"));
23740
24130
  try {
23741
24131
  const entry = require2.resolve(name);
23742
24132
  let version2;
@@ -23834,7 +24224,7 @@ function versionMismatchCheck(cwd) {
23834
24224
  };
23835
24225
  }
23836
24226
  async function runDoctorChecks(options = {}) {
23837
- const cwd = path32__default.default.resolve(options.cwd ?? process3__default.default.cwd());
24227
+ const cwd = path33__default.default.resolve(options.cwd ?? process3__default.default.cwd());
23838
24228
  const traceDir = options.traceDir?.trim() || process3__default.default.env.AGENT_INSPECT_TRACE_DIR?.trim() || ".agent-inspect";
23839
24229
  const checks2 = [
23840
24230
  nodeVersionCheck(),
@@ -24074,7 +24464,7 @@ function parsePluginManifest(input3) {
24074
24464
  };
24075
24465
  }
24076
24466
  async function readPluginManifestFile(packageDir) {
24077
- const manifestPath = path32__default.default.join(packageDir, PLUGIN_MANIFEST_FILENAME);
24467
+ const manifestPath = path33__default.default.join(packageDir, PLUGIN_MANIFEST_FILENAME);
24078
24468
  try {
24079
24469
  const raw = await promises.readFile(manifestPath, "utf8");
24080
24470
  const parsed = parsePluginManifest(JSON.parse(raw));
@@ -24149,7 +24539,7 @@ function createTraceDirectoryIndexer() {
24149
24539
  // packages/cli/src/plugins.ts
24150
24540
  async function readPackageName(packageDir) {
24151
24541
  try {
24152
- const raw = await promises.readFile(path32__default.default.join(packageDir, "package.json"), "utf8");
24542
+ const raw = await promises.readFile(path33__default.default.join(packageDir, "package.json"), "utf8");
24153
24543
  const parsed = JSON.parse(raw);
24154
24544
  return typeof parsed.name === "string" ? parsed.name : void 0;
24155
24545
  } catch {
@@ -24157,7 +24547,7 @@ async function readPackageName(packageDir) {
24157
24547
  }
24158
24548
  }
24159
24549
  async function discoverPlugins(cwd = process.cwd()) {
24160
- const nodeModules = path32__default.default.join(cwd, "node_modules");
24550
+ const nodeModules = path33__default.default.join(cwd, "node_modules");
24161
24551
  const found = [];
24162
24552
  let entries;
24163
24553
  try {
@@ -24168,7 +24558,7 @@ async function discoverPlugins(cwd = process.cwd()) {
24168
24558
  for (const entry of entries.sort()) {
24169
24559
  if (entry.startsWith("@")) continue;
24170
24560
  if (!isPluginPackageName(entry)) continue;
24171
- const packageDir = path32__default.default.join(nodeModules, entry);
24561
+ const packageDir = path33__default.default.join(nodeModules, entry);
24172
24562
  const manifestRead = await readPluginManifestFile(packageDir);
24173
24563
  found.push({
24174
24564
  packageName: entry,
@@ -24181,7 +24571,7 @@ async function discoverPlugins(cwd = process.cwd()) {
24181
24571
  return found;
24182
24572
  }
24183
24573
  async function validatePluginPackage(packageRef, cwd = process.cwd()) {
24184
- const packageDir = path32__default.default.isAbsolute(packageRef) ? packageRef : path32__default.default.join(cwd, "node_modules", packageRef);
24574
+ const packageDir = path33__default.default.isAbsolute(packageRef) ? packageRef : path33__default.default.join(cwd, "node_modules", packageRef);
24185
24575
  const packageName = await readPackageName(packageDir) ?? packageRef;
24186
24576
  const errors = [];
24187
24577
  const warnings = [];
@@ -24258,7 +24648,7 @@ async function pluginsValidateCommand(packageRef, cwd) {
24258
24648
  init_advanced();
24259
24649
  var INDEX_FILENAME = ".agent-inspect-index.json";
24260
24650
  function traceIndexPath(traceDir) {
24261
- return path32__default.default.join(traceDir, INDEX_FILENAME);
24651
+ return path33__default.default.join(traceDir, INDEX_FILENAME);
24262
24652
  }
24263
24653
  function parseMaxEntries(raw) {
24264
24654
  if (raw === void 0 || raw.trim() === "") return void 0;
@@ -24404,7 +24794,7 @@ async function newestTraceMtimeMs2(traceDir) {
24404
24794
  for (const file of files) {
24405
24795
  if (!file.endsWith(".jsonl")) continue;
24406
24796
  try {
24407
- const s = await promises.stat(path32__default.default.join(traceDir, file));
24797
+ const s = await promises.stat(path33__default.default.join(traceDir, file));
24408
24798
  if (s.mtimeMs > newest) newest = s.mtimeMs;
24409
24799
  } catch {
24410
24800
  }
@@ -24659,11 +25049,11 @@ function printJson5(value) {
24659
25049
  console.log(JSON.stringify(value, null, 2));
24660
25050
  }
24661
25051
  function resolveCwd(options) {
24662
- return path32__default.default.resolve(options.cwd ?? process.cwd());
25052
+ return path33__default.default.resolve(options.cwd ?? process.cwd());
24663
25053
  }
24664
25054
  async function suiteInitCommand(options = {}) {
24665
25055
  const cwd = resolveCwd(options);
24666
- const configPath = path32__default.default.join(cwd, DEFAULT_CONFIG_FILENAME);
25056
+ const configPath = path33__default.default.join(cwd, DEFAULT_CONFIG_FILENAME);
24667
25057
  const template = options.template?.trim();
24668
25058
  const suiteConfig = template !== void 0 && template !== "" ? resolveSuiteTemplate(template) : defaultSuiteConfigTemplate();
24669
25059
  if (options.dryRun) {
@@ -24782,13 +25172,13 @@ async function suiteListCommand(options = {}) {
24782
25172
  }
24783
25173
  async function writeSuiteArtifact(result, configOutputDir, options) {
24784
25174
  const cwd = resolveCwd(options);
24785
- const outputDir = path32__default.default.resolve(
25175
+ const outputDir = path33__default.default.resolve(
24786
25176
  cwd,
24787
25177
  options.output ?? configOutputDir ?? DEFAULT_SUITE_ARTIFACTS_DIR
24788
25178
  );
24789
25179
  await promises.mkdir(outputDir, { recursive: true });
24790
25180
  const stamp = result.startedAt.replace(/[:.]/g, "-");
24791
- const filePath = path32__default.default.join(outputDir, `${result.suiteName}-${stamp}.json`);
25181
+ const filePath = path33__default.default.join(outputDir, `${result.suiteName}-${stamp}.json`);
24792
25182
  await promises.writeFile(filePath, `${JSON.stringify(result, null, 2)}
24793
25183
  `, "utf-8");
24794
25184
  return filePath;
@@ -24846,7 +25236,7 @@ async function loadSuiteResultFromInput(options) {
24846
25236
  if (options.input === void 0 || options.input.trim() === "") {
24847
25237
  throw new Error("Pass --input <suite-run.json> from a prior suite run.");
24848
25238
  }
24849
- const inputPath = path32__default.default.resolve(cwd, options.input.trim());
25239
+ const inputPath = path33__default.default.resolve(cwd, options.input.trim());
24850
25240
  const raw = await promises.readFile(inputPath, "utf-8");
24851
25241
  return JSON.parse(raw);
24852
25242
  }
@@ -24889,9 +25279,9 @@ function normalizeMetrics2(raw) {
24889
25279
  }
24890
25280
  async function writeArtifacts(result, outputDir) {
24891
25281
  await promises.mkdir(outputDir, { recursive: true });
24892
- const jsonPath = path32__default.default.join(outputDir, "cohort-results.json");
24893
- const markdownPath = path32__default.default.join(outputDir, "cohort-summary.md");
24894
- const htmlPath = path32__default.default.join(outputDir, "cohort-report.html");
25282
+ const jsonPath = path33__default.default.join(outputDir, "cohort-results.json");
25283
+ const markdownPath = path33__default.default.join(outputDir, "cohort-summary.md");
25284
+ const htmlPath = path33__default.default.join(outputDir, "cohort-report.html");
24895
25285
  await promises.writeFile(jsonPath, `${renderCohortReport(result, { format: "json" })}
24896
25286
  `, "utf-8");
24897
25287
  await promises.writeFile(
@@ -24920,7 +25310,7 @@ async function cohortCommand(options = {}) {
24920
25310
  const format = options.format ?? (options.json ? "json" : "markdown");
24921
25311
  let artifacts;
24922
25312
  if (options.output !== void 0 && options.output.trim() !== "") {
24923
- artifacts = await writeArtifacts(result, path32__default.default.resolve(options.output.trim()));
25313
+ artifacts = await writeArtifacts(result, path33__default.default.resolve(options.output.trim()));
24924
25314
  }
24925
25315
  if (options.json || format === "json") {
24926
25316
  console.log(
@@ -24993,11 +25383,11 @@ function parseDuration2(value) {
24993
25383
  async function writeArtifacts2(result, outputDir) {
24994
25384
  await promises.mkdir(outputDir, { recursive: true });
24995
25385
  const paths = {
24996
- jsonPath: path32__default.default.join(outputDir, "gate-results.json"),
24997
- markdownPath: path32__default.default.join(outputDir, "gate-summary.md"),
24998
- htmlPath: path32__default.default.join(outputDir, "gate-report.html"),
24999
- junitPath: path32__default.default.join(outputDir, "junit.xml"),
25000
- githubPath: path32__default.default.join(outputDir, "github-step-summary.md")
25386
+ jsonPath: path33__default.default.join(outputDir, "gate-results.json"),
25387
+ markdownPath: path33__default.default.join(outputDir, "gate-summary.md"),
25388
+ htmlPath: path33__default.default.join(outputDir, "gate-report.html"),
25389
+ junitPath: path33__default.default.join(outputDir, "junit.xml"),
25390
+ githubPath: path33__default.default.join(outputDir, "github-step-summary.md")
25001
25391
  };
25002
25392
  await promises.writeFile(
25003
25393
  paths.jsonPath,
@@ -25065,7 +25455,7 @@ async function gateCommand(options = {}) {
25065
25455
  const result = await runGate(runs, gateOptions);
25066
25456
  let artifacts;
25067
25457
  if (options.output !== void 0 && options.output.trim() !== "") {
25068
- artifacts = await writeArtifacts2(result, path32__default.default.resolve(options.output.trim()));
25458
+ artifacts = await writeArtifacts2(result, path33__default.default.resolve(options.output.trim()));
25069
25459
  }
25070
25460
  const failed = !result.ok;
25071
25461
  if (shouldEmitEvidence(options.evidenceOn, failed)) {
@@ -25372,7 +25762,7 @@ function createCliProgram() {
25372
25762
  "openinference-json",
25373
25763
  "otlp-json"
25374
25764
  ])
25375
- ).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--explain", "explain each finding (path, confidence, redaction, override, bundle gate)").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
25765
+ ).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--explain", "explain each finding (path, confidence, redaction, override, bundle gate)").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").option("--policy <path>", "local JSON redaction policy (extraKeys + bounded patterns)").action((target, opts) => {
25376
25766
  runCommand(() => scanCommand(target, opts));
25377
25767
  });
25378
25768
  program.command("verify-safe").description("Best-effort local trace safety verification").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").option("--dir <path>", "trace directory for run-id lookup").addOption(
@@ -25381,7 +25771,7 @@ function createCliProgram() {
25381
25771
  "openinference-json",
25382
25772
  "otlp-json"
25383
25773
  ])
25384
- ).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--explain", "explain each finding (path, confidence, redaction, override, bundle gate)").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
25774
+ ).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--explain", "explain each finding (path, confidence, redaction, override, bundle gate)").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").option("--policy <path>", "local JSON redaction policy (extraKeys + bounded patterns)").action((target, opts) => {
25385
25775
  runCommand(() => verifySafeCommand(target, opts));
25386
25776
  });
25387
25777
  program.command("artifacts").description("Create safe local CI trace artifacts").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").requiredOption("--output-dir <path>", "directory for generated artifacts").option("--dir <path>", "trace directory for run-id lookup").addOption(
@@ -25541,7 +25931,10 @@ function createCliProgram() {
25541
25931
  "--redaction-profile <profile>",
25542
25932
  "alias for --profile (canonical spelling)"
25543
25933
  ).choices(["local", "share", "strict"])
25544
- ).option("-o, --output <path>", "write redacted content to a file").option("--out <path>", "alias for --output").option("--json", "print deterministic JSON wrapper with findings").action((target, opts) => {
25934
+ ).option("-o, --output <path>", "write redacted content to a file").option("--out <path>", "alias for --output").option("--json", "print deterministic JSON wrapper with findings").option("--policy <path>", "local JSON redaction policy (extraKeys + bounded patterns)").option(
25935
+ "--fail-on-residual",
25936
+ "exit non-zero when residual safety is UNSAFE or UNKNOWN (opt-in)"
25937
+ ).action((target, opts) => {
25545
25938
  runCommand(() => redactCommand(target, opts));
25546
25939
  });
25547
25940
  program.command("explain").description("Explain a local trace with deterministic facts (no provider calls)").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").option("--dir <path>", "trace directory for run-id lookup").addOption(
@@ -25723,9 +26116,9 @@ function isPrimaryModule() {
25723
26116
  if (!entry) return false;
25724
26117
  const selfPath = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
25725
26118
  try {
25726
- return fs.realpathSync(path32__default.default.resolve(entry)) === fs.realpathSync(path32__default.default.resolve(selfPath));
26119
+ return fs.realpathSync(path33__default.default.resolve(entry)) === fs.realpathSync(path33__default.default.resolve(selfPath));
25727
26120
  } catch {
25728
- return path32__default.default.resolve(entry) === path32__default.default.resolve(selfPath);
26121
+ return path33__default.default.resolve(entry) === path33__default.default.resolve(selfPath);
25729
26122
  }
25730
26123
  }
25731
26124
  if (isPrimaryModule()) {