agent-inspect 5.0.0 → 5.2.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 path23 = require('path');
7
+ var path24 = require('path');
8
8
  var async_hooks = require('async_hooks');
9
9
  var process3 = require('process');
10
10
  var tty = require('tty');
@@ -20,7 +20,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
20
20
 
21
21
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
22
22
  var os__default = /*#__PURE__*/_interopDefault(os);
23
- var path23__default = /*#__PURE__*/_interopDefault(path23);
23
+ var path24__default = /*#__PURE__*/_interopDefault(path24);
24
24
  var process3__default = /*#__PURE__*/_interopDefault(process3);
25
25
  var tty__default = /*#__PURE__*/_interopDefault(tty);
26
26
 
@@ -893,7 +893,7 @@ function getDefaultTraceDir() {
893
893
  if (typeof home !== "string" || home.trim() === "") {
894
894
  return FALLBACK_TRACE_DIR;
895
895
  }
896
- return path23__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
896
+ return path24__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
897
897
  } catch {
898
898
  return FALLBACK_TRACE_DIR;
899
899
  }
@@ -901,11 +901,11 @@ function getDefaultTraceDir() {
901
901
  function getTraceFilePath(runId, traceDir) {
902
902
  const baseDir = traceDir ?? getDefaultTraceDir();
903
903
  let safeId = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
904
- safeId = path23__default.default.basename(safeId);
904
+ safeId = path24__default.default.basename(safeId);
905
905
  if (safeId === "" || safeId === "." || safeId === "..") {
906
906
  safeId = "run_unknown";
907
907
  }
908
- return path23__default.default.join(baseDir, `${safeId}.jsonl`);
908
+ return path24__default.default.join(baseDir, `${safeId}.jsonl`);
909
909
  }
910
910
  function formatError(error) {
911
911
  if (error instanceof Error) {
@@ -962,7 +962,7 @@ var init_utils = __esm({
962
962
  init_duration();
963
963
  DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
964
964
  RUNS_DIR_NAME = "runs";
965
- FALLBACK_TRACE_DIR = path23__default.default.join(
965
+ FALLBACK_TRACE_DIR = path24__default.default.join(
966
966
  os__default.default.tmpdir(),
967
967
  "agent-inspect",
968
968
  RUNS_DIR_NAME
@@ -1928,7 +1928,7 @@ var init_trace_directory = __esm({
1928
1928
  this.#dir = resolveTraceDir(options);
1929
1929
  }
1930
1930
  getPath(filename) {
1931
- return filename ? path23__default.default.join(this.#dir, filename) : this.#dir;
1931
+ return filename ? path24__default.default.join(this.#dir, filename) : this.#dir;
1932
1932
  }
1933
1933
  async list() {
1934
1934
  try {
@@ -1957,7 +1957,7 @@ function parseIsoToMs2(value) {
1957
1957
  }
1958
1958
  async function extractMetadata(filePath, _quickScan) {
1959
1959
  const stats = await promises.stat(filePath);
1960
- let runIdFromFile = path23__default.default.basename(filePath);
1960
+ let runIdFromFile = path24__default.default.basename(filePath);
1961
1961
  if (runIdFromFile.endsWith(".jsonl")) {
1962
1962
  runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
1963
1963
  }
@@ -2100,7 +2100,7 @@ function buildRunSummary(events) {
2100
2100
  }
2101
2101
  let totalSteps = 0;
2102
2102
  let llmSteps = 0;
2103
- let toolSteps = 0;
2103
+ let toolSteps2 = 0;
2104
2104
  let logicSteps = 0;
2105
2105
  let errorSteps = 0;
2106
2106
  let maxDepth = 0;
@@ -2130,7 +2130,7 @@ function buildRunSummary(events) {
2130
2130
  for (const [id, s] of steps.entries()) {
2131
2131
  totalSteps += 1;
2132
2132
  if (s.type === "llm") llmSteps += 1;
2133
- else if (s.type === "tool") toolSteps += 1;
2133
+ else if (s.type === "tool") toolSteps2 += 1;
2134
2134
  else logicSteps += 1;
2135
2135
  if (s.status === "error") errorSteps += 1;
2136
2136
  const depth = computeDepth(id);
@@ -2164,7 +2164,7 @@ function buildRunSummary(events) {
2164
2164
  durationMs: durationMs2,
2165
2165
  totalSteps,
2166
2166
  llmSteps,
2167
- toolSteps,
2167
+ toolSteps: toolSteps2,
2168
2168
  logicSteps,
2169
2169
  errorSteps,
2170
2170
  maxDepth,
@@ -2942,9 +2942,9 @@ async function searchTraces(metas, options) {
2942
2942
  }
2943
2943
  const limit = options.limit ?? 50;
2944
2944
  const sessionId = options.session?.trim();
2945
- const observationStatus = parseObservationFilter(options.observation);
2945
+ const observationStatus2 = parseObservationFilter(options.observation);
2946
2946
  const hasContentFilter = Boolean(
2947
- options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus
2947
+ options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus2
2948
2948
  );
2949
2949
  const results = [];
2950
2950
  const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
@@ -2989,9 +2989,9 @@ async function searchTraces(metas, options) {
2989
2989
  statusFilter: options.status
2990
2990
  });
2991
2991
  results.push(...stepMatches);
2992
- if (observationStatus) {
2992
+ if (observationStatus2) {
2993
2993
  const outcomes = extractOutcomesFromTraceEvents(events);
2994
- const matched = outcomes.filter((outcome) => outcome.status === observationStatus);
2994
+ const matched = outcomes.filter((outcome) => outcome.status === observationStatus2);
2995
2995
  for (const outcome of matched) {
2996
2996
  results.push({
2997
2997
  runId: m.runId,
@@ -3786,12 +3786,12 @@ function buildCriticalPath(runs, handoffs) {
3786
3786
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
3787
3787
  );
3788
3788
  const ordered = [...runs].sort(compareRuns);
3789
- const path30 = [];
3789
+ const path33 = [];
3790
3790
  const visited = /* @__PURE__ */ new Set();
3791
3791
  const pushRun = (run, confidence, source) => {
3792
3792
  if (visited.has(run.runId)) return;
3793
3793
  visited.add(run.runId);
3794
- path30.push({
3794
+ path33.push({
3795
3795
  runId: run.runId,
3796
3796
  name: run.name,
3797
3797
  startedAt: run.startedAt,
@@ -3816,7 +3816,7 @@ function buildCriticalPath(runs, handoffs) {
3816
3816
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
3817
3817
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
3818
3818
  }
3819
- return path30;
3819
+ return path33;
3820
3820
  }
3821
3821
  function metaRunIdMatches(run, token, runById) {
3822
3822
  const meta2 = extractSessionWorkflowMetadata(run.metadata);
@@ -4147,7 +4147,7 @@ function normalizeBundleOutputPath(out) {
4147
4147
  if (trimmed === "") {
4148
4148
  throw new Error("--out requires a non-empty path.");
4149
4149
  }
4150
- const resolved = path23__default.default.resolve(trimmed);
4150
+ const resolved = path24__default.default.resolve(trimmed);
4151
4151
  if (resolved.toLowerCase().endsWith(".zip")) {
4152
4152
  return resolved.slice(0, -4);
4153
4153
  }
@@ -4156,7 +4156,7 @@ function normalizeBundleOutputPath(out) {
4156
4156
  function defaultBundleOutputPath(runIds) {
4157
4157
  const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
4158
4158
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4159
- return path23__default.default.resolve(`agent-inspect-bundle-${label}-${stamp}`);
4159
+ return path24__default.default.resolve(`agent-inspect-bundle-${label}-${stamp}`);
4160
4160
  }
4161
4161
  var init_paths = __esm({
4162
4162
  "packages/core/src/bundle/paths.ts"() {
@@ -4330,7 +4330,7 @@ function normalizeSuiteConfig(value) {
4330
4330
  }
4331
4331
  async function validateSuiteConfig(config, options) {
4332
4332
  const diagnostics = [];
4333
- const tracesDir = path23__default.default.resolve(options.configDir, config.traces);
4333
+ const tracesDir = path24__default.default.resolve(options.configDir, config.traces);
4334
4334
  try {
4335
4335
  await promises.access(tracesDir);
4336
4336
  } catch {
@@ -4340,7 +4340,7 @@ async function validateSuiteConfig(config, options) {
4340
4340
  }
4341
4341
  for (const suiteCase of config.cases) {
4342
4342
  if (suiteCase.input !== void 0) {
4343
- const inputPath = path23__default.default.resolve(options.configDir, suiteCase.input);
4343
+ const inputPath = path24__default.default.resolve(options.configDir, suiteCase.input);
4344
4344
  try {
4345
4345
  await promises.access(inputPath);
4346
4346
  } catch {
@@ -4373,12 +4373,12 @@ async function fileExists(filePath) {
4373
4373
  }
4374
4374
  }
4375
4375
  async function resolveSuiteConfigPath(options = {}) {
4376
- const cwd = path23__default.default.resolve(options.cwd ?? process.cwd());
4376
+ const cwd = path24__default.default.resolve(options.cwd ?? process.cwd());
4377
4377
  if (options.configPath !== void 0 && options.configPath.trim() !== "") {
4378
- return path23__default.default.resolve(cwd, options.configPath.trim());
4378
+ return path24__default.default.resolve(cwd, options.configPath.trim());
4379
4379
  }
4380
4380
  for (const name of DEFAULT_SUITE_CONFIG_NAMES) {
4381
- const candidate = path23__default.default.join(cwd, name);
4381
+ const candidate = path24__default.default.join(cwd, name);
4382
4382
  if (await fileExists(candidate)) return candidate;
4383
4383
  }
4384
4384
  throw new Error(
@@ -4395,7 +4395,7 @@ async function loadSuiteConfig(options = {}) {
4395
4395
  diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
4396
4396
  });
4397
4397
  }
4398
- const extension = path23__default.default.extname(configPath);
4398
+ const extension = path24__default.default.extname(configPath);
4399
4399
  if (TS_CONFIG_EXTENSIONS.has(extension)) {
4400
4400
  const message = "TypeScript suite configs require an explicit precompiled JavaScript config or future --config-loader support.";
4401
4401
  throw Object.assign(new Error(message), {
@@ -4420,7 +4420,7 @@ async function loadSuiteConfig(options = {}) {
4420
4420
  return {
4421
4421
  config,
4422
4422
  configPath,
4423
- configDir: path23__default.default.dirname(configPath)
4423
+ configDir: path24__default.default.dirname(configPath)
4424
4424
  };
4425
4425
  } catch (error) {
4426
4426
  const message = error instanceof Error ? error.message : String(error);
@@ -4470,7 +4470,7 @@ async function exists(filePath) {
4470
4470
  }
4471
4471
  async function resolveSuiteCaseTrace(suiteCase, options) {
4472
4472
  if (suiteCase.trace !== void 0) {
4473
- const tracePath = path23__default.default.resolve(options.configDir, suiteCase.trace);
4473
+ const tracePath = path24__default.default.resolve(options.configDir, suiteCase.trace);
4474
4474
  if (await exists(tracePath)) {
4475
4475
  return { caseId: suiteCase.id, tracePath, missing: false };
4476
4476
  }
@@ -4486,7 +4486,7 @@ async function resolveSuiteCaseTrace(suiteCase, options) {
4486
4486
  if (await exists(directPath)) {
4487
4487
  return { caseId: suiteCase.id, tracePath: directPath, runId: runKey, missing: false };
4488
4488
  }
4489
- const nestedPath = path23__default.default.join(options.tracesDir, `${path23__default.default.basename(runKey)}.jsonl`);
4489
+ const nestedPath = path24__default.default.join(options.tracesDir, `${path24__default.default.basename(runKey)}.jsonl`);
4490
4490
  if (await exists(nestedPath)) {
4491
4491
  return { caseId: suiteCase.id, tracePath: nestedPath, runId: runKey, missing: false };
4492
4492
  }
@@ -4716,7 +4716,7 @@ function stripPrefix(name, prefixes) {
4716
4716
  }
4717
4717
  return name;
4718
4718
  }
4719
- function eventEvidence(event, path30) {
4719
+ function eventEvidence(event, path33) {
4720
4720
  return {
4721
4721
  runId: event.runId,
4722
4722
  eventId: event.eventId,
@@ -4726,7 +4726,7 @@ function eventEvidence(event, path30) {
4726
4726
  kind: event.kind,
4727
4727
  name: event.name,
4728
4728
  status: event.status,
4729
- ...path30 ? { path: path30 } : {}
4729
+ ...path33 ? { path: path33 } : {}
4730
4730
  };
4731
4731
  }
4732
4732
  function runEvidence(run) {
@@ -4789,9 +4789,9 @@ function eventEndMs(event) {
4789
4789
  function normalizedKey(value) {
4790
4790
  return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
4791
4791
  }
4792
- function lastPathSegment(path30) {
4793
- const parts = path30.split(".");
4794
- return parts[parts.length - 1] ?? path30;
4792
+ function lastPathSegment(path33) {
4793
+ const parts = path33.split(".");
4794
+ return parts[parts.length - 1] ?? path33;
4795
4795
  }
4796
4796
  function valueType(value) {
4797
4797
  if (Array.isArray(value)) return "array";
@@ -4805,12 +4805,12 @@ function serializedByteLength(value) {
4805
4805
  return void 0;
4806
4806
  }
4807
4807
  }
4808
- function pushValueEntries(entries, event, value, path30, key, depth = 0) {
4809
- entries.push({ event, path: path30, key, value });
4808
+ function pushValueEntries(entries, event, value, path33, key, depth = 0) {
4809
+ entries.push({ event, path: path33, key, value });
4810
4810
  if (depth >= 8) return;
4811
4811
  if (Array.isArray(value)) {
4812
4812
  for (const [index, item] of value.entries()) {
4813
- pushValueEntries(entries, event, item, `${path30}.${index}`, String(index), depth + 1);
4813
+ pushValueEntries(entries, event, item, `${path33}.${index}`, String(index), depth + 1);
4814
4814
  }
4815
4815
  return;
4816
4816
  }
@@ -4820,7 +4820,7 @@ function pushValueEntries(entries, event, value, path30, key, depth = 0) {
4820
4820
  entries,
4821
4821
  event,
4822
4822
  value[nestedKey],
4823
- `${path30}.${nestedKey}`,
4823
+ `${path33}.${nestedKey}`,
4824
4824
  nestedKey,
4825
4825
  depth + 1
4826
4826
  );
@@ -4901,9 +4901,9 @@ function eventDurationMs(event) {
4901
4901
  }
4902
4902
  function treeShape(nodes) {
4903
4903
  const lines = [];
4904
- const visit = (node, path30) => {
4905
- lines.push(`${path30}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
4906
- node.children.forEach((child, index) => visit(child, `${path30}.${index}`));
4904
+ const visit = (node, path33) => {
4905
+ lines.push(`${path33}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
4906
+ node.children.forEach((child, index) => visit(child, `${path33}.${index}`));
4907
4907
  };
4908
4908
  nodes.forEach((node, index) => visit(node, String(index)));
4909
4909
  return lines;
@@ -4952,9 +4952,9 @@ function retrievalShape(context) {
4952
4952
  function guardrailShape(context) {
4953
4953
  return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
4954
4954
  }
4955
- function firstEvidenceForKind(context, kind, path30) {
4955
+ function firstEvidenceForKind(context, kind, path33) {
4956
4956
  const event = context.events.find((candidate) => candidate.kind === kind);
4957
- return event ? [eventEvidence(event, path30)] : runEvidence(context.selectedRun);
4957
+ return event ? [eventEvidence(event, path33)] : runEvidence(context.selectedRun);
4958
4958
  }
4959
4959
  function baselineDiffFinding(message, evidence, expected, actual) {
4960
4960
  return failFinding("baseline.regression", message, evidence, expected, actual);
@@ -5304,13 +5304,13 @@ function createStructureCycleRule() {
5304
5304
  const seenCycles = /* @__PURE__ */ new Set();
5305
5305
  const findings = [];
5306
5306
  for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
5307
- const path30 = [];
5307
+ const path33 = [];
5308
5308
  const seenAt = /* @__PURE__ */ new Map();
5309
5309
  let current = event;
5310
5310
  while (current) {
5311
5311
  const existing = seenAt.get(current.eventId);
5312
5312
  if (existing !== void 0) {
5313
- const cycle = path30.slice(existing);
5313
+ const cycle = path33.slice(existing);
5314
5314
  const key = cycle.map((item) => item.eventId).sort().join("\0");
5315
5315
  if (!seenCycles.has(key)) {
5316
5316
  seenCycles.add(key);
@@ -5326,8 +5326,8 @@ function createStructureCycleRule() {
5326
5326
  }
5327
5327
  break;
5328
5328
  }
5329
- seenAt.set(current.eventId, path30.length);
5330
- path30.push(current);
5329
+ seenAt.set(current.eventId, path33.length);
5330
+ path33.push(current);
5331
5331
  current = current.parentId ? byId.get(current.parentId) : void 0;
5332
5332
  }
5333
5333
  }
@@ -6468,7 +6468,7 @@ function findReaderByFormat(format, readers) {
6468
6468
  }
6469
6469
  async function jsonlFilesInDirectory(dirPath) {
6470
6470
  const entries = await promises.readdir(dirPath, { withFileTypes: true });
6471
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path23__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
6471
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path24__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
6472
6472
  }
6473
6473
  async function resolveInput(input3) {
6474
6474
  const cached = resolvedInputCache.get(input3);
@@ -8167,7 +8167,7 @@ async function runSuiteCase(suiteCase, config, options) {
8167
8167
  async function runSuite(options = {}) {
8168
8168
  const startedAt = new Date(options.nowMs ?? Date.now()).toISOString();
8169
8169
  const { config, configPath, configDir } = await loadSuiteConfig(options);
8170
- const tracesDir = path23__default.default.resolve(configDir, config.traces);
8170
+ const tracesDir = path24__default.default.resolve(configDir, config.traces);
8171
8171
  const cases = [];
8172
8172
  const diagnostics = [];
8173
8173
  for (const suiteCase of config.cases) {
@@ -8275,6 +8275,1102 @@ var init_suite = __esm({
8275
8275
  }
8276
8276
  });
8277
8277
 
8278
+ // packages/core/src/cohort/types.ts
8279
+ var COHORT_METRIC_IDS;
8280
+ var init_types5 = __esm({
8281
+ "packages/core/src/cohort/types.ts"() {
8282
+ COHORT_METRIC_IDS = [
8283
+ "errorRate",
8284
+ "duration",
8285
+ "toolChoice",
8286
+ "toolOrdering",
8287
+ "llmCallCount",
8288
+ "tokenUsage",
8289
+ "retryCount",
8290
+ "observationFailure",
8291
+ "guardrailFailure",
8292
+ "circuitViolation",
8293
+ "redactionWarning"
8294
+ ];
8295
+ }
8296
+ });
8297
+
8298
+ // packages/core/src/cohort/compare.ts
8299
+ function compareNumber(metric, label, baseline, candidate, higherIsWorse = true) {
8300
+ if (baseline === void 0 && candidate === void 0) return void 0;
8301
+ const delta = baseline !== void 0 && candidate !== void 0 ? candidate - baseline : void 0;
8302
+ const regression = delta !== void 0 && (higherIsWorse && delta > 0 || !higherIsWorse && delta < 0);
8303
+ return {
8304
+ metric,
8305
+ baseline,
8306
+ candidate,
8307
+ delta,
8308
+ regression,
8309
+ message: `${label}: baseline=${baseline ?? "n/a"} candidate=${candidate ?? "n/a"}${delta !== void 0 ? ` (delta ${delta})` : ""}`
8310
+ };
8311
+ }
8312
+ function pickAggregate(groups, cohortLabel, groupKey) {
8313
+ return groups.find(
8314
+ (group) => group.cohortLabel === cohortLabel && (groupKey === void 0 || group.groupKey === groupKey)
8315
+ );
8316
+ }
8317
+ function compareCohortAggregates(groups, options) {
8318
+ const baselineAgg = pickAggregate(groups, options.baseline, options.groupKey);
8319
+ const candidateAgg = pickAggregate(groups, options.candidate, options.groupKey);
8320
+ const comparisons = [];
8321
+ for (const metric of options.metrics) {
8322
+ switch (metric) {
8323
+ case "errorRate": {
8324
+ const item = compareNumber(
8325
+ metric,
8326
+ "Error rate",
8327
+ baselineAgg?.errorRate,
8328
+ candidateAgg?.errorRate
8329
+ );
8330
+ if (item) comparisons.push(item);
8331
+ break;
8332
+ }
8333
+ case "duration": {
8334
+ const item = compareNumber(
8335
+ metric,
8336
+ "Average duration (ms)",
8337
+ baselineAgg?.avgDurationMs,
8338
+ candidateAgg?.avgDurationMs
8339
+ );
8340
+ if (item) comparisons.push(item);
8341
+ break;
8342
+ }
8343
+ case "llmCallCount": {
8344
+ const item = compareNumber(
8345
+ metric,
8346
+ "Average LLM calls",
8347
+ baselineAgg?.avgLlmCallCount,
8348
+ candidateAgg?.avgLlmCallCount
8349
+ );
8350
+ if (item) comparisons.push(item);
8351
+ break;
8352
+ }
8353
+ case "tokenUsage": {
8354
+ const item = compareNumber(
8355
+ metric,
8356
+ "Average token usage",
8357
+ baselineAgg?.avgTokenUsage,
8358
+ candidateAgg?.avgTokenUsage
8359
+ );
8360
+ if (item) comparisons.push(item);
8361
+ break;
8362
+ }
8363
+ case "retryCount": {
8364
+ const item = compareNumber(
8365
+ metric,
8366
+ "Average retries",
8367
+ baselineAgg?.avgRetryCount,
8368
+ candidateAgg?.avgRetryCount
8369
+ );
8370
+ if (item) comparisons.push(item);
8371
+ break;
8372
+ }
8373
+ case "observationFailure": {
8374
+ const item = compareNumber(
8375
+ metric,
8376
+ "Observation failure rate",
8377
+ baselineAgg?.observationFailureRate,
8378
+ candidateAgg?.observationFailureRate
8379
+ );
8380
+ if (item) comparisons.push(item);
8381
+ break;
8382
+ }
8383
+ case "toolChoice": {
8384
+ const baselineValue = baselineAgg?.dominantToolChoice;
8385
+ const candidateValue = candidateAgg?.dominantToolChoice;
8386
+ comparisons.push({
8387
+ metric,
8388
+ baseline: baselineValue,
8389
+ candidate: candidateValue,
8390
+ delta: baselineValue === candidateValue ? "same" : "changed",
8391
+ regression: baselineValue !== candidateValue,
8392
+ message: `Tool choice: baseline=${baselineValue ?? "n/a"} candidate=${candidateValue ?? "n/a"}`
8393
+ });
8394
+ break;
8395
+ }
8396
+ case "toolOrdering": {
8397
+ const baselineValue = baselineAgg?.toolOrderingSignature;
8398
+ const candidateValue = candidateAgg?.toolOrderingSignature;
8399
+ comparisons.push({
8400
+ metric,
8401
+ baseline: baselineValue,
8402
+ candidate: candidateValue,
8403
+ delta: baselineValue === candidateValue ? "same" : "changed",
8404
+ regression: baselineValue !== candidateValue,
8405
+ message: `Tool ordering: baseline=${baselineValue ?? "n/a"} candidate=${candidateValue ?? "n/a"}`
8406
+ });
8407
+ break;
8408
+ }
8409
+ case "guardrailFailure": {
8410
+ const item = compareNumber(
8411
+ metric,
8412
+ "Guardrail failures",
8413
+ baselineAgg?.avgGuardrailFailures,
8414
+ candidateAgg?.avgGuardrailFailures
8415
+ );
8416
+ if (item) comparisons.push(item);
8417
+ break;
8418
+ }
8419
+ case "circuitViolation": {
8420
+ const item = compareNumber(
8421
+ metric,
8422
+ "Circuit violations",
8423
+ baselineAgg?.avgCircuitViolations,
8424
+ candidateAgg?.avgCircuitViolations
8425
+ );
8426
+ if (item) comparisons.push(item);
8427
+ break;
8428
+ }
8429
+ case "redactionWarning": {
8430
+ const item = compareNumber(
8431
+ metric,
8432
+ "Redaction warnings",
8433
+ baselineAgg?.avgRedactionWarnings,
8434
+ candidateAgg?.avgRedactionWarnings
8435
+ );
8436
+ if (item) comparisons.push(item);
8437
+ break;
8438
+ }
8439
+ }
8440
+ }
8441
+ return comparisons;
8442
+ }
8443
+ var init_compare = __esm({
8444
+ "packages/core/src/cohort/compare.ts"() {
8445
+ }
8446
+ });
8447
+
8448
+ // packages/core/src/cohort/grouping.ts
8449
+ function parseCohortMetricList(value) {
8450
+ if (value === void 0 || value.trim() === "") return [];
8451
+ return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
8452
+ }
8453
+ function parseGroupBySpec(groupBy) {
8454
+ const raw = (groupBy ?? "model").trim();
8455
+ if (raw === "model") return { kind: "model" };
8456
+ if (raw === "session") return { kind: "session" };
8457
+ if (raw === "group") return { kind: "group" };
8458
+ if (raw.startsWith("metadata.")) {
8459
+ const metadataKey = raw.slice("metadata.".length).trim();
8460
+ if (metadataKey === "") throw new Error("metadata group-by requires a key.");
8461
+ return { kind: "metadata", metadataKey };
8462
+ }
8463
+ throw new Error(`Unsupported --group-by value: ${raw}`);
8464
+ }
8465
+ function metadataString(metadata, key) {
8466
+ const value = metadata?.[key];
8467
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
8468
+ }
8469
+ function resolveRunGroupKey(run, groupBy) {
8470
+ const metadata = run.metadata ?? {};
8471
+ switch (groupBy.kind) {
8472
+ case "model":
8473
+ return metadataString(metadata, "model") ?? "unknown";
8474
+ case "session":
8475
+ return extractSessionWorkflowMetadata(metadata)?.sessionId ?? metadataString(metadata, "sessionId") ?? "__unscoped__";
8476
+ case "group":
8477
+ return extractSessionWorkflowMetadata(metadata)?.groupId ?? metadataString(metadata, "groupId") ?? "__unscoped__";
8478
+ case "metadata":
8479
+ return metadataString(metadata, groupBy.metadataKey) ?? "__missing__";
8480
+ default:
8481
+ return "unknown";
8482
+ }
8483
+ }
8484
+ function resolveCohortLabel(run, cohortKey, baseline, candidate) {
8485
+ const label = metadataString(run.metadata, cohortKey);
8486
+ if (label === void 0) return void 0;
8487
+ if (baseline !== void 0 && label === baseline) return baseline;
8488
+ if (candidate !== void 0 && label === candidate) return candidate;
8489
+ if (baseline === void 0 && candidate === void 0) return label;
8490
+ return void 0;
8491
+ }
8492
+ function filterRunsForCohort(runs, options) {
8493
+ const warnings = [];
8494
+ if (options.baseline === void 0 && options.candidate === void 0) {
8495
+ return { runs: [...runs], warnings };
8496
+ }
8497
+ const selected = [];
8498
+ for (const run of runs) {
8499
+ const label = resolveCohortLabel(
8500
+ run,
8501
+ options.cohortKey,
8502
+ options.baseline,
8503
+ options.candidate
8504
+ );
8505
+ if (label !== void 0) selected.push(run);
8506
+ }
8507
+ if (selected.length === 0) {
8508
+ warnings.push(
8509
+ `No runs matched baseline/candidate labels on metadata.${options.cohortKey}.`
8510
+ );
8511
+ }
8512
+ return { runs: selected, warnings };
8513
+ }
8514
+ var init_grouping = __esm({
8515
+ "packages/core/src/cohort/grouping.ts"() {
8516
+ init_metadata();
8517
+ }
8518
+ });
8519
+
8520
+ // packages/core/src/cohort/metrics.ts
8521
+ function asNumber(value) {
8522
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8523
+ }
8524
+ function toolSteps(events) {
8525
+ const ordering = [];
8526
+ const choices = /* @__PURE__ */ new Set();
8527
+ const sorted = [...events].filter((event) => event.event === "step_started").sort((a, b) => a.timestamp - b.timestamp);
8528
+ for (const event of sorted) {
8529
+ const step = event;
8530
+ if (step.type !== "tool") continue;
8531
+ const name = typeof step.metadata?.toolName === "string" ? step.metadata.toolName : step.name;
8532
+ ordering.push(name);
8533
+ choices.add(name);
8534
+ }
8535
+ return { choices: [...choices].sort(), ordering };
8536
+ }
8537
+ async function computeCohortRunMetrics(input3) {
8538
+ const events = await readTraceEventsFromFile(input3.filePath);
8539
+ const summary = buildRunSummary(events);
8540
+ const tools = toolSteps(events);
8541
+ const outcomes = extractOutcomesFromTraceEvents(events);
8542
+ const observationFailures = outcomes.filter((item) => item.status === "failed").length;
8543
+ const metadata = input3.metadata ?? {};
8544
+ const retryCount2 = asNumber(metadata.attempt) !== void 0 && asNumber(metadata.attempt) > 1 ? asNumber(metadata.attempt) - 1 : typeof metadata.retryOf === "string" ? 1 : 0;
8545
+ return {
8546
+ runId: input3.runId,
8547
+ ...input3.cohortLabel !== void 0 ? { cohortLabel: input3.cohortLabel } : {},
8548
+ groupKey: input3.groupKey,
8549
+ status: summary.status,
8550
+ error: summary.status === "error",
8551
+ durationMs: summary.durationMs ?? input3.durationMs,
8552
+ llmCallCount: summary.llmSteps,
8553
+ tokenUsageTotal: summary.totalTokens?.total,
8554
+ retryCount: retryCount2,
8555
+ observationFailures,
8556
+ guardrailFailures: asNumber(metadata.guardrailFailures) ?? 0,
8557
+ circuitViolations: asNumber(metadata.circuitViolations) ?? 0,
8558
+ redactionWarnings: asNumber(metadata.redactionWarnings) ?? 0,
8559
+ toolChoices: tools.choices,
8560
+ toolOrdering: tools.ordering
8561
+ };
8562
+ }
8563
+ function percentile2(values, p) {
8564
+ if (values.length === 0) return void 0;
8565
+ const sorted = [...values].sort((a, b) => a - b);
8566
+ const idx = Math.min(
8567
+ sorted.length - 1,
8568
+ Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
8569
+ );
8570
+ return sorted[idx];
8571
+ }
8572
+ function dominantToolChoice(runs) {
8573
+ const counts = /* @__PURE__ */ new Map();
8574
+ for (const run of runs) {
8575
+ const signature = run.toolChoices.join(",");
8576
+ if (signature === "") continue;
8577
+ counts.set(signature, (counts.get(signature) ?? 0) + 1);
8578
+ }
8579
+ let best;
8580
+ let bestCount = 0;
8581
+ for (const [key, count] of counts) {
8582
+ if (count > bestCount) {
8583
+ best = key;
8584
+ bestCount = count;
8585
+ }
8586
+ }
8587
+ return best;
8588
+ }
8589
+ function orderingSignature(runs) {
8590
+ const counts = /* @__PURE__ */ new Map();
8591
+ for (const run of runs) {
8592
+ const signature = run.toolOrdering.join(">");
8593
+ if (signature === "") continue;
8594
+ counts.set(signature, (counts.get(signature) ?? 0) + 1);
8595
+ }
8596
+ let best;
8597
+ let bestCount = 0;
8598
+ for (const [key, count] of counts) {
8599
+ if (count > bestCount) {
8600
+ best = key;
8601
+ bestCount = count;
8602
+ }
8603
+ }
8604
+ return best;
8605
+ }
8606
+ function aggregateCohortMetrics(runs, groupKey, cohortLabel) {
8607
+ const durations = runs.map((run) => run.durationMs).filter((value) => typeof value === "number");
8608
+ const tokenValues = runs.map((run) => run.tokenUsageTotal).filter((value) => typeof value === "number");
8609
+ const errors = runs.filter((run) => run.error).length;
8610
+ const observationFailures = runs.reduce((sum, run) => sum + run.observationFailures, 0);
8611
+ return {
8612
+ groupKey,
8613
+ ...cohortLabel !== void 0 ? { cohortLabel } : {},
8614
+ runCount: runs.length,
8615
+ errorRate: runs.length > 0 ? errors / runs.length : 0,
8616
+ avgDurationMs: durations.length > 0 ? durations.reduce((sum, value) => sum + value, 0) / durations.length : void 0,
8617
+ p95DurationMs: percentile2(durations, 95),
8618
+ avgLlmCallCount: runs.length > 0 ? runs.reduce((sum, run) => sum + run.llmCallCount, 0) / runs.length : 0,
8619
+ avgTokenUsage: tokenValues.length > 0 ? tokenValues.reduce((sum, value) => sum + value, 0) / tokenValues.length : void 0,
8620
+ avgRetryCount: runs.length > 0 ? runs.reduce((sum, run) => sum + run.retryCount, 0) / runs.length : 0,
8621
+ observationFailureRate: runs.length > 0 ? observationFailures / runs.length : 0,
8622
+ avgGuardrailFailures: runs.length > 0 ? runs.reduce((sum, run) => sum + run.guardrailFailures, 0) / runs.length : 0,
8623
+ avgCircuitViolations: runs.length > 0 ? runs.reduce((sum, run) => sum + run.circuitViolations, 0) / runs.length : 0,
8624
+ avgRedactionWarnings: runs.length > 0 ? runs.reduce((sum, run) => sum + run.redactionWarnings, 0) / runs.length : 0,
8625
+ dominantToolChoice: dominantToolChoice(runs),
8626
+ toolOrderingSignature: orderingSignature(runs)
8627
+ };
8628
+ }
8629
+ var init_metrics = __esm({
8630
+ "packages/core/src/cohort/metrics.ts"() {
8631
+ init_extract();
8632
+ init_trace_metadata();
8633
+ init_storage();
8634
+ }
8635
+ });
8636
+
8637
+ // packages/core/src/cohort/analyze.ts
8638
+ function normalizeMetrics(metrics) {
8639
+ if (metrics === void 0 || metrics.length === 0) return [...DEFAULT_METRICS];
8640
+ const allowed = new Set(COHORT_METRIC_IDS);
8641
+ return metrics.filter((metric) => allowed.has(metric));
8642
+ }
8643
+ async function analyzeCohort(runsInput, options) {
8644
+ const cohortKey = options.cohortKey ?? "cohort";
8645
+ const groupBySpec = parseGroupBySpec(options.groupBy);
8646
+ const metrics = normalizeMetrics(options.metrics);
8647
+ const { runs: filteredRuns, warnings } = filterRunsForCohort(runsInput, {
8648
+ cohortKey,
8649
+ baseline: options.baseline,
8650
+ candidate: options.candidate
8651
+ });
8652
+ const runMetrics = [];
8653
+ for (const run of filteredRuns) {
8654
+ if (run.filePath === void 0) continue;
8655
+ const cohortLabel = resolveCohortLabel(
8656
+ run,
8657
+ cohortKey,
8658
+ options.baseline,
8659
+ options.candidate
8660
+ );
8661
+ runMetrics.push(
8662
+ await computeCohortRunMetrics({
8663
+ runId: run.runId,
8664
+ filePath: run.filePath,
8665
+ metadata: run.metadata,
8666
+ status: run.status,
8667
+ durationMs: run.durationMs,
8668
+ groupKey: resolveRunGroupKey(run, groupBySpec),
8669
+ cohortLabel
8670
+ })
8671
+ );
8672
+ }
8673
+ const groupMap = /* @__PURE__ */ new Map();
8674
+ for (const run of runMetrics) {
8675
+ const key = `${run.cohortLabel ?? "*"}::${run.groupKey}`;
8676
+ const bucket = groupMap.get(key) ?? [];
8677
+ bucket.push(run);
8678
+ groupMap.set(key, bucket);
8679
+ }
8680
+ const groups = [...groupMap.entries()].sort(([a], [b]) => a.localeCompare(b)).map(
8681
+ ([, bucket]) => aggregateCohortMetrics(
8682
+ bucket,
8683
+ bucket[0].groupKey,
8684
+ bucket[0]?.cohortLabel
8685
+ )
8686
+ );
8687
+ const comparisons = options.baseline !== void 0 && options.candidate !== void 0 ? (() => {
8688
+ const groupKeys = [
8689
+ ...new Set(groups.map((group) => group.groupKey))
8690
+ ].sort((a, b) => a.localeCompare(b));
8691
+ const items = [];
8692
+ for (const groupKey of groupKeys) {
8693
+ items.push(
8694
+ ...compareCohortAggregates(groups, {
8695
+ baseline: options.baseline,
8696
+ candidate: options.candidate,
8697
+ metrics,
8698
+ groupKey
8699
+ })
8700
+ );
8701
+ }
8702
+ return items;
8703
+ })() : [];
8704
+ const regression = comparisons.some((item) => item.regression);
8705
+ return {
8706
+ ok: !regression,
8707
+ traceDir: options.traceDir,
8708
+ ...options.baseline !== void 0 ? { baseline: options.baseline } : {},
8709
+ ...options.candidate !== void 0 ? { candidate: options.candidate } : {},
8710
+ cohortKey,
8711
+ groupBy: options.groupBy ?? "model",
8712
+ metrics,
8713
+ groups,
8714
+ comparisons,
8715
+ runs: runMetrics,
8716
+ warnings
8717
+ };
8718
+ }
8719
+ var DEFAULT_METRICS;
8720
+ var init_analyze = __esm({
8721
+ "packages/core/src/cohort/analyze.ts"() {
8722
+ init_compare();
8723
+ init_grouping();
8724
+ init_metrics();
8725
+ init_types5();
8726
+ DEFAULT_METRICS = [
8727
+ "errorRate",
8728
+ "duration",
8729
+ "toolChoice",
8730
+ "observationFailure"
8731
+ ];
8732
+ }
8733
+ });
8734
+
8735
+ // packages/core/src/exporters/helpers.ts
8736
+ function shouldRedactKey(key) {
8737
+ const k = key.toLowerCase();
8738
+ for (const s of REDACT_SUBSTRINGS) {
8739
+ if (k.includes(s)) return true;
8740
+ }
8741
+ return false;
8742
+ }
8743
+ function safeString(value, maxLength) {
8744
+ if (value === null || value === void 0) return "";
8745
+ let s;
8746
+ if (typeof value === "string") s = value;
8747
+ else if (typeof value === "number" || typeof value === "boolean") s = String(value);
8748
+ else s = stableJson(value, false);
8749
+ if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
8750
+ return `${s.slice(0, maxLength)}\u2026`;
8751
+ }
8752
+ return s;
8753
+ }
8754
+ function escapeMarkdown(value) {
8755
+ return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
8756
+ }
8757
+ function escapeHtml(value) {
8758
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
8759
+ }
8760
+ function sortKeysDeep(input3) {
8761
+ if (input3 === null || typeof input3 !== "object") return input3;
8762
+ if (Array.isArray(input3)) return input3.map(sortKeysDeep);
8763
+ const o = input3;
8764
+ const out = {};
8765
+ for (const k of Object.keys(o).sort()) {
8766
+ out[k] = sortKeysDeep(o[k]);
8767
+ }
8768
+ return out;
8769
+ }
8770
+ function stableJson(value, pretty) {
8771
+ const sorted = sortKeysDeep(value);
8772
+ return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
8773
+ }
8774
+ function compactAttributes3(attrs, options) {
8775
+ if (attrs === void 0) return {};
8776
+ const maxLen = options?.maxLength ?? 500;
8777
+ const redacted = options?.redacted ?? true;
8778
+ const out = {};
8779
+ for (const key of Object.keys(attrs).sort()) {
8780
+ if (redacted && shouldRedactKey(key)) {
8781
+ out[key] = "[REDACTED]";
8782
+ continue;
8783
+ }
8784
+ const v = attrs[key];
8785
+ out[key] = compactValue(v, maxLen, redacted);
8786
+ }
8787
+ return out;
8788
+ }
8789
+ function compactValue(value, maxLen, redacted) {
8790
+ if (value === null || typeof value !== "object") {
8791
+ return typeof value === "string" ? safeString(value, maxLen) : value;
8792
+ }
8793
+ if (Array.isArray(value)) {
8794
+ const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
8795
+ if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
8796
+ return arr;
8797
+ }
8798
+ const o = value;
8799
+ const inner = {};
8800
+ for (const k of Object.keys(o)) {
8801
+ if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
8802
+ else inner[k] = compactValue(o[k], maxLen, redacted);
8803
+ }
8804
+ return inner;
8805
+ }
8806
+ function flattenTree(tree) {
8807
+ const out = [];
8808
+ function walk(nodes) {
8809
+ for (const n of nodes) {
8810
+ out.push(n);
8811
+ if (n.children.length > 0) walk(n.children);
8812
+ }
8813
+ }
8814
+ walk(tree.children);
8815
+ return out;
8816
+ }
8817
+ function zeroKinds() {
8818
+ return {
8819
+ RUN: 0,
8820
+ AGENT: 0,
8821
+ LLM: 0,
8822
+ TOOL: 0,
8823
+ CHAIN: 0,
8824
+ RETRIEVER: 0,
8825
+ DECISION: 0,
8826
+ RESULT: 0,
8827
+ ERROR: 0,
8828
+ LOGIC: 0,
8829
+ LOG: 0,
8830
+ OUTCOME: 0
8831
+ };
8832
+ }
8833
+ var REDACT_SUBSTRINGS;
8834
+ var init_helpers = __esm({
8835
+ "packages/core/src/exporters/helpers.ts"() {
8836
+ REDACT_SUBSTRINGS = [
8837
+ "authorization",
8838
+ "cookie",
8839
+ "token",
8840
+ "apikey",
8841
+ "password",
8842
+ "secret",
8843
+ "email"
8844
+ ];
8845
+ }
8846
+ });
8847
+
8848
+ // packages/core/src/cohort/render.ts
8849
+ function formatRate(value) {
8850
+ if (value === void 0) return "n/a";
8851
+ return `${(value * 100).toFixed(1)}%`;
8852
+ }
8853
+ function renderCohortSummaryMarkdown(result) {
8854
+ const lines = [];
8855
+ lines.push("# Cohort analysis");
8856
+ lines.push("");
8857
+ lines.push(`Trace directory: \`${result.traceDir}\``);
8858
+ lines.push(`Group by: \`${result.groupBy}\``);
8859
+ if (result.baseline !== void 0 && result.candidate !== void 0) {
8860
+ lines.push(
8861
+ `Baseline/Candidate key: \`${result.cohortKey}\` (${result.baseline} vs ${result.candidate})`
8862
+ );
8863
+ }
8864
+ lines.push(`Status: **${result.ok ? "PASS" : "REGRESSION"}**`);
8865
+ lines.push("");
8866
+ if (result.warnings.length > 0) {
8867
+ lines.push("## Warnings");
8868
+ for (const warning of result.warnings) lines.push(`- ${warning}`);
8869
+ lines.push("");
8870
+ }
8871
+ lines.push("## Groups");
8872
+ for (const group of result.groups) {
8873
+ lines.push(
8874
+ `### ${group.cohortLabel ?? "all"} / ${group.groupKey} (${group.runCount} runs)`
8875
+ );
8876
+ lines.push(`- Error rate: ${formatRate(group.errorRate)}`);
8877
+ if (group.avgDurationMs !== void 0) {
8878
+ lines.push(`- Avg duration: ${Math.round(group.avgDurationMs)} ms`);
8879
+ }
8880
+ if (group.dominantToolChoice !== void 0) {
8881
+ lines.push(`- Dominant tools: ${group.dominantToolChoice}`);
8882
+ }
8883
+ lines.push(
8884
+ `- Observation failure rate: ${formatRate(group.observationFailureRate)}`
8885
+ );
8886
+ lines.push("");
8887
+ }
8888
+ if (result.comparisons.length > 0) {
8889
+ lines.push("## Comparisons");
8890
+ for (const comparison of result.comparisons) {
8891
+ const flag = comparison.regression ? " **REGRESSION**" : "";
8892
+ lines.push(`- ${comparison.message}${flag}`);
8893
+ }
8894
+ lines.push("");
8895
+ }
8896
+ return lines.join("\n").trimEnd();
8897
+ }
8898
+ function renderCohortReportHtml(result) {
8899
+ const rows = result.groups.map(
8900
+ (group) => `<tr><td>${escapeHtml(group.cohortLabel ?? "all")}</td><td>${escapeHtml(group.groupKey)}</td><td>${group.runCount}</td><td>${escapeHtml(formatRate(group.errorRate))}</td><td>${group.avgDurationMs !== void 0 ? Math.round(group.avgDurationMs) : "n/a"}</td></tr>`
8901
+ ).join("");
8902
+ const comparisons = result.comparisons.map(
8903
+ (item) => `<li>${escapeHtml(item.message)}${item.regression ? " <strong>REGRESSION</strong>" : ""}</li>`
8904
+ ).join("");
8905
+ return `<!DOCTYPE html>
8906
+ <html lang="en">
8907
+ <head>
8908
+ <meta charset="utf-8" />
8909
+ <title>Cohort report</title>
8910
+ <style>
8911
+ body { font-family: system-ui, sans-serif; margin: 2rem; }
8912
+ table { border-collapse: collapse; width: 100%; }
8913
+ th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
8914
+ th { background: #f6f6f6; }
8915
+ </style>
8916
+ </head>
8917
+ <body>
8918
+ <h1>Cohort analysis</h1>
8919
+ <p>Status: <strong>${result.ok ? "PASS" : "REGRESSION"}</strong></p>
8920
+ <p>Trace directory: <code>${escapeHtml(result.traceDir)}</code></p>
8921
+ <h2>Groups</h2>
8922
+ <table>
8923
+ <thead><tr><th>Cohort</th><th>Group</th><th>Runs</th><th>Error rate</th><th>Avg duration (ms)</th></tr></thead>
8924
+ <tbody>${rows}</tbody>
8925
+ </table>
8926
+ ${result.comparisons.length > 0 ? `<h2>Comparisons</h2><ul>${comparisons}</ul>` : ""}
8927
+ </body>
8928
+ </html>`;
8929
+ }
8930
+ function renderCohortReport(result, options = {}) {
8931
+ const format = options.format ?? "markdown";
8932
+ if (format === "json") return JSON.stringify(result, null, 2);
8933
+ if (format === "html") return renderCohortReportHtml(result);
8934
+ return renderCohortSummaryMarkdown(result);
8935
+ }
8936
+ var init_render2 = __esm({
8937
+ "packages/core/src/cohort/render.ts"() {
8938
+ init_helpers();
8939
+ }
8940
+ });
8941
+
8942
+ // packages/core/src/cohort/index.ts
8943
+ var init_cohort2 = __esm({
8944
+ "packages/core/src/cohort/index.ts"() {
8945
+ init_types5();
8946
+ init_analyze();
8947
+ init_compare();
8948
+ init_grouping();
8949
+ init_metrics();
8950
+ init_render2();
8951
+ }
8952
+ });
8953
+
8954
+ // packages/core/src/gate/parse.ts
8955
+ function parseGateList(value) {
8956
+ if (value === void 0 || value.trim() === "") return [];
8957
+ return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
8958
+ }
8959
+ var init_parse = __esm({
8960
+ "packages/core/src/gate/parse.ts"() {
8961
+ }
8962
+ });
8963
+
8964
+ // packages/core/src/gate/evaluate.ts
8965
+ function percentile3(values, p) {
8966
+ if (values.length === 0) return void 0;
8967
+ const sorted = [...values].sort((a, b) => a - b);
8968
+ const idx = Math.min(
8969
+ sorted.length - 1,
8970
+ Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
8971
+ );
8972
+ return sorted[idx];
8973
+ }
8974
+ function hasThresholds(options) {
8975
+ return options.maxErrorRate !== void 0 || options.maxP95DurationMs !== void 0 || (options.forbidTools?.length ?? 0) > 0 || (options.requireObservations?.length ?? 0) > 0;
8976
+ }
8977
+ function gateHasThresholds(options) {
8978
+ return hasThresholds(options);
8979
+ }
8980
+ async function loadRunMetrics(runs) {
8981
+ const metrics = [];
8982
+ for (const run of runs) {
8983
+ if (run.filePath === void 0) continue;
8984
+ metrics.push(
8985
+ await computeCohortRunMetrics({
8986
+ runId: run.runId,
8987
+ filePath: run.filePath,
8988
+ metadata: run.metadata,
8989
+ status: run.status,
8990
+ durationMs: run.durationMs,
8991
+ groupKey: "all"
8992
+ })
8993
+ );
8994
+ }
8995
+ return metrics;
8996
+ }
8997
+ async function observationStatus(filePath, name) {
8998
+ const events = await readTraceEventsFromFile(filePath);
8999
+ const outcomes = extractOutcomesFromTraceEvents(events);
9000
+ const match = outcomes.find((item) => item.name === name);
9001
+ if (!match) return "missing";
9002
+ return match.status === "passed" ? "passed" : "failed";
9003
+ }
9004
+ async function evaluateGateThresholds(runs, options) {
9005
+ const checks2 = [];
9006
+ const readErrors = [];
9007
+ if (!hasThresholds(options)) {
9008
+ return { checks: checks2, readErrors };
9009
+ }
9010
+ if (runs.length === 0) {
9011
+ readErrors.push("No trace runs found in the gate directory.");
9012
+ return { checks: checks2, readErrors };
9013
+ }
9014
+ let runMetrics;
9015
+ try {
9016
+ runMetrics = await loadRunMetrics(runs);
9017
+ } catch (error) {
9018
+ const message = error instanceof Error ? error.message : String(error);
9019
+ readErrors.push(message);
9020
+ return { checks: checks2, readErrors };
9021
+ }
9022
+ if (options.maxErrorRate !== void 0) {
9023
+ const errors = runMetrics.filter((run) => run.error).length;
9024
+ const actual = runMetrics.length > 0 ? errors / runMetrics.length * 100 : 0;
9025
+ const ok = actual <= options.maxErrorRate;
9026
+ checks2.push({
9027
+ id: "maxErrorRate",
9028
+ name: "Max error rate",
9029
+ ok,
9030
+ expected: options.maxErrorRate,
9031
+ actual: Math.round(actual * 10) / 10,
9032
+ message: ok ? `Error rate ${actual.toFixed(1)}% within limit ${options.maxErrorRate}%` : `Error rate ${actual.toFixed(1)}% exceeds limit ${options.maxErrorRate}%`
9033
+ });
9034
+ }
9035
+ if (options.maxP95DurationMs !== void 0) {
9036
+ const durations = runMetrics.map((run) => run.durationMs).filter((value) => typeof value === "number");
9037
+ const actual = percentile3(durations, 95);
9038
+ const ok = actual !== void 0 && actual <= options.maxP95DurationMs;
9039
+ checks2.push({
9040
+ id: "maxP95Duration",
9041
+ name: "Max p95 duration (ms)",
9042
+ ok,
9043
+ expected: options.maxP95DurationMs,
9044
+ actual: actual ?? "n/a",
9045
+ message: actual === void 0 ? "No duration samples available for p95 check." : ok ? `P95 duration ${Math.round(actual)} ms within limit ${options.maxP95DurationMs} ms` : `P95 duration ${Math.round(actual)} ms exceeds limit ${options.maxP95DurationMs} ms`
9046
+ });
9047
+ }
9048
+ for (const tool of options.forbidTools ?? []) {
9049
+ let violated = false;
9050
+ for (const run of runMetrics) {
9051
+ const used = run.toolChoices.includes(tool) || run.toolOrdering.includes(tool);
9052
+ if (used) {
9053
+ violated = true;
9054
+ checks2.push({
9055
+ id: "forbidTool",
9056
+ name: `Forbid tool: ${tool}`,
9057
+ ok: false,
9058
+ expected: `not used`,
9059
+ actual: "used",
9060
+ runId: run.runId,
9061
+ message: `Forbidden tool "${tool}" used in run ${run.runId}`
9062
+ });
9063
+ }
9064
+ }
9065
+ if (!violated) {
9066
+ checks2.push({
9067
+ id: "forbidTool",
9068
+ name: `Forbid tool: ${tool}`,
9069
+ ok: true,
9070
+ message: `Forbidden tool "${tool}" not used`
9071
+ });
9072
+ }
9073
+ }
9074
+ for (const observation of options.requireObservations ?? []) {
9075
+ for (const run of runs) {
9076
+ if (run.filePath === void 0) continue;
9077
+ try {
9078
+ const status = await observationStatus(run.filePath, observation);
9079
+ const ok = status === "passed";
9080
+ checks2.push({
9081
+ id: "requireObservation",
9082
+ name: `Require observation: ${observation}`,
9083
+ ok,
9084
+ expected: "passed",
9085
+ actual: status,
9086
+ runId: run.runId,
9087
+ message: ok ? `Observation "${observation}" passed in run ${run.runId}` : status === "missing" ? `Observation "${observation}" missing in run ${run.runId}` : `Observation "${observation}" failed in run ${run.runId}`
9088
+ });
9089
+ } catch (error) {
9090
+ const message = error instanceof Error ? error.message : String(error);
9091
+ readErrors.push(`Run ${run.runId}: ${message}`);
9092
+ }
9093
+ }
9094
+ }
9095
+ return { checks: checks2, readErrors };
9096
+ }
9097
+ function checksFromSuiteResult(suiteResult) {
9098
+ const checks2 = [
9099
+ {
9100
+ id: "suite",
9101
+ name: `Suite: ${suiteResult.suiteName}`,
9102
+ ok: suiteResult.ok,
9103
+ message: suiteResult.ok ? `Suite passed (${suiteResult.summary.passed} cases)` : `Suite failed (${suiteResult.summary.failed} failed, ${suiteResult.summary.errors} errors)`
9104
+ }
9105
+ ];
9106
+ for (const suiteCase of suiteResult.cases) {
9107
+ if (suiteCase.status === "pass") continue;
9108
+ checks2.push({
9109
+ id: "suite",
9110
+ name: `Case: ${suiteCase.id}`,
9111
+ ok: false,
9112
+ message: suiteCase.message ?? `Case status: ${suiteCase.status}`
9113
+ });
9114
+ }
9115
+ return checks2;
9116
+ }
9117
+ var init_evaluate = __esm({
9118
+ "packages/core/src/gate/evaluate.ts"() {
9119
+ init_extract();
9120
+ init_storage();
9121
+ init_metrics();
9122
+ }
9123
+ });
9124
+ function resolveExitCode(input3) {
9125
+ if (input3.configError) return 2;
9126
+ if (input3.readError) return 3;
9127
+ if (!input3.ok) return 1;
9128
+ return 0;
9129
+ }
9130
+ function validateOptions(options) {
9131
+ const errors = [];
9132
+ const hasSuite = options.suitePath !== void 0 && options.suitePath.trim() !== "";
9133
+ const hasThresholds2 = gateHasThresholds(options);
9134
+ if (!hasSuite && !hasThresholds2) {
9135
+ errors.push(
9136
+ "No gate rules specified. Pass --suite or at least one threshold flag."
9137
+ );
9138
+ }
9139
+ if (hasThresholds2 && (options.traceDir === void 0 || options.traceDir.trim() === "")) {
9140
+ if (!hasSuite) {
9141
+ errors.push("Threshold flags require --dir <trace-directory>.");
9142
+ }
9143
+ }
9144
+ if (options.maxErrorRate !== void 0 && options.maxErrorRate < 0) {
9145
+ errors.push("--max-error-rate must be a non-negative percentage.");
9146
+ }
9147
+ if (options.maxP95DurationMs !== void 0 && options.maxP95DurationMs < 0) {
9148
+ errors.push("--max-p95-duration must be a non-negative millisecond value.");
9149
+ }
9150
+ return errors;
9151
+ }
9152
+ function isConfigLoadError(error) {
9153
+ if (!(error instanceof Error)) return false;
9154
+ const ext = path24__default.default.extname(error.message);
9155
+ if (error.message.includes("Unsupported suite config extension")) return true;
9156
+ if (error.message.includes("TypeScript suite configs require")) return true;
9157
+ if (error.message.includes("No suite config found")) return true;
9158
+ if (error.message.includes("AI_SUITE_CONFIG")) return true;
9159
+ if (ext === ".ts" || ext === ".mts" || ext === ".cts") return true;
9160
+ return "diagnostics" in error;
9161
+ }
9162
+ async function runGate(runs, options) {
9163
+ const diagnostics = [];
9164
+ const checks2 = [];
9165
+ const validationErrors = validateOptions(options);
9166
+ if (validationErrors.length > 0) {
9167
+ return {
9168
+ ok: false,
9169
+ exitCode: 2,
9170
+ runCount: 0,
9171
+ checks: checks2,
9172
+ diagnostics: validationErrors
9173
+ };
9174
+ }
9175
+ let traceDir = options.traceDir?.trim();
9176
+ let suiteResult;
9177
+ if (options.suitePath !== void 0 && options.suitePath.trim() !== "") {
9178
+ try {
9179
+ suiteResult = await runSuite({
9180
+ configPath: options.suitePath,
9181
+ cwd: options.cwd
9182
+ });
9183
+ traceDir = traceDir ?? suiteResult.tracesDir;
9184
+ checks2.push(...checksFromSuiteResult(suiteResult));
9185
+ } catch (error) {
9186
+ const message = error instanceof Error ? error.message : String(error);
9187
+ diagnostics.push(message);
9188
+ return {
9189
+ ok: false,
9190
+ exitCode: isConfigLoadError(error) ? 2 : 3,
9191
+ traceDir,
9192
+ suitePath: options.suitePath,
9193
+ runCount: 0,
9194
+ checks: checks2,
9195
+ diagnostics
9196
+ };
9197
+ }
9198
+ }
9199
+ if (gateHasThresholds(options)) {
9200
+ const thresholdDir = traceDir;
9201
+ if (thresholdDir === void 0 || thresholdDir.trim() === "") {
9202
+ return {
9203
+ ok: false,
9204
+ exitCode: 2,
9205
+ traceDir,
9206
+ suitePath: options.suitePath,
9207
+ runCount: runs.length,
9208
+ checks: checks2,
9209
+ diagnostics: ["Threshold evaluation requires a trace directory."],
9210
+ ...suiteResult !== void 0 ? { suiteResult } : {}
9211
+ };
9212
+ }
9213
+ const thresholdRuns = runs.length > 0 ? runs : [];
9214
+ const { checks: thresholdChecks, readErrors } = await evaluateGateThresholds(
9215
+ thresholdRuns,
9216
+ options
9217
+ );
9218
+ checks2.push(...thresholdChecks);
9219
+ diagnostics.push(...readErrors);
9220
+ if (readErrors.length > 0) {
9221
+ const ok2 = checks2.length > 0 && checks2.every((item) => item.ok);
9222
+ return {
9223
+ ok: ok2,
9224
+ exitCode: resolveExitCode({
9225
+ ok: ok2,
9226
+ configError: false,
9227
+ readError: true
9228
+ }),
9229
+ traceDir: thresholdDir,
9230
+ suitePath: options.suitePath,
9231
+ runCount: thresholdRuns.length,
9232
+ checks: checks2,
9233
+ diagnostics,
9234
+ ...suiteResult !== void 0 ? { suiteResult } : {}
9235
+ };
9236
+ }
9237
+ }
9238
+ const ok = checks2.length > 0 && checks2.every((item) => item.ok);
9239
+ return {
9240
+ ok,
9241
+ exitCode: resolveExitCode({ ok, configError: false, readError: false }),
9242
+ traceDir,
9243
+ suitePath: options.suitePath,
9244
+ runCount: runs.length,
9245
+ checks: checks2,
9246
+ diagnostics,
9247
+ ...suiteResult !== void 0 ? { suiteResult } : {}
9248
+ };
9249
+ }
9250
+ var init_run2 = __esm({
9251
+ "packages/core/src/gate/run.ts"() {
9252
+ init_run();
9253
+ init_evaluate();
9254
+ }
9255
+ });
9256
+
9257
+ // packages/core/src/gate/render.ts
9258
+ function escapeXml(value) {
9259
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
9260
+ }
9261
+ function renderGateSummaryMarkdown(result) {
9262
+ const lines = [];
9263
+ lines.push("# AgentInspect gate");
9264
+ lines.push("");
9265
+ lines.push(`Status: **${result.ok ? "PASS" : "FAIL"}** (exit ${result.exitCode})`);
9266
+ if (result.traceDir !== void 0) {
9267
+ lines.push(`Trace directory: \`${result.traceDir}\``);
9268
+ }
9269
+ if (result.suitePath !== void 0) {
9270
+ lines.push(`Suite config: \`${result.suitePath}\``);
9271
+ }
9272
+ lines.push(`Runs evaluated: ${result.runCount}`);
9273
+ lines.push("");
9274
+ if (result.diagnostics.length > 0) {
9275
+ lines.push("## Diagnostics");
9276
+ for (const item of result.diagnostics) lines.push(`- ${item}`);
9277
+ lines.push("");
9278
+ }
9279
+ lines.push("## Checks");
9280
+ for (const check of result.checks) {
9281
+ const flag = check.ok ? "PASS" : "FAIL";
9282
+ lines.push(`- [${flag}] ${check.name}: ${check.message}`);
9283
+ }
9284
+ lines.push("");
9285
+ return lines.join("\n").trimEnd();
9286
+ }
9287
+ function renderGateGithubStepSummary(result) {
9288
+ const lines = [];
9289
+ lines.push(`## AgentInspect gate: ${result.ok ? "PASS" : "FAIL"}`);
9290
+ lines.push("");
9291
+ lines.push("| Check | Status | Details |");
9292
+ lines.push("| --- | --- | --- |");
9293
+ for (const check of result.checks) {
9294
+ lines.push(
9295
+ `| ${check.name} | ${check.ok ? "pass" : "fail"} | ${check.message.replace(/\|/g, "/")} |`
9296
+ );
9297
+ }
9298
+ if (result.diagnostics.length > 0) {
9299
+ lines.push("");
9300
+ lines.push("**Diagnostics**");
9301
+ for (const item of result.diagnostics) lines.push(`- ${item}`);
9302
+ }
9303
+ return lines.join("\n").trimEnd();
9304
+ }
9305
+ function renderGateReportHtml(result) {
9306
+ const rows = result.checks.map(
9307
+ (check) => `<tr><td>${escapeHtml(check.name)}</td><td>${check.ok ? "PASS" : "FAIL"}</td><td>${escapeHtml(check.message)}</td></tr>`
9308
+ ).join("");
9309
+ return `<!DOCTYPE html>
9310
+ <html lang="en">
9311
+ <head>
9312
+ <meta charset="utf-8" />
9313
+ <title>Gate report</title>
9314
+ <style>
9315
+ body { font-family: system-ui, sans-serif; margin: 2rem; }
9316
+ table { border-collapse: collapse; width: 100%; }
9317
+ th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
9318
+ th { background: #f6f6f6; }
9319
+ </style>
9320
+ </head>
9321
+ <body>
9322
+ <h1>AgentInspect gate</h1>
9323
+ <p>Status: <strong>${result.ok ? "PASS" : "FAIL"}</strong> (exit ${result.exitCode})</p>
9324
+ <h2>Checks</h2>
9325
+ <table>
9326
+ <thead><tr><th>Check</th><th>Status</th><th>Details</th></tr></thead>
9327
+ <tbody>${rows}</tbody>
9328
+ </table>
9329
+ </body>
9330
+ </html>`;
9331
+ }
9332
+ function renderGateJUnit(result) {
9333
+ const failures = result.checks.filter((check) => !check.ok).length;
9334
+ const tests = result.checks.length;
9335
+ const cases = result.checks.map((check) => {
9336
+ if (check.ok) {
9337
+ return ` <testcase name="${escapeXml(check.name)}" classname="gate" />`;
9338
+ }
9339
+ return ` <testcase name="${escapeXml(check.name)}" classname="gate">
9340
+ <failure message="${escapeXml(check.message)}">${escapeXml(check.message)}</failure>
9341
+ </testcase>`;
9342
+ }).join("\n");
9343
+ return `<?xml version="1.0" encoding="UTF-8"?>
9344
+ <testsuites tests="${tests}" failures="${failures}" errors="0" time="0">
9345
+ <testsuite name="agent-inspect-gate" tests="${tests}" failures="${failures}" errors="0" time="0">
9346
+ ${cases}
9347
+ </testsuite>
9348
+ </testsuites>`;
9349
+ }
9350
+ function renderGateReport(result, options = {}) {
9351
+ const format = options.format ?? "markdown";
9352
+ if (format === "json") return JSON.stringify(result, null, 2);
9353
+ if (format === "html") return renderGateReportHtml(result);
9354
+ if (format === "junit") return renderGateJUnit(result);
9355
+ if (format === "github") return renderGateGithubStepSummary(result);
9356
+ return renderGateSummaryMarkdown(result);
9357
+ }
9358
+ var init_render3 = __esm({
9359
+ "packages/core/src/gate/render.ts"() {
9360
+ init_helpers();
9361
+ }
9362
+ });
9363
+
9364
+ // packages/core/src/gate/index.ts
9365
+ var init_gate = __esm({
9366
+ "packages/core/src/gate/index.ts"() {
9367
+ init_parse();
9368
+ init_evaluate();
9369
+ init_run2();
9370
+ init_render3();
9371
+ }
9372
+ });
9373
+
8278
9374
  // packages/core/src/inspect-run.ts
8279
9375
  var init_inspect_run = __esm({
8280
9376
  "packages/core/src/inspect-run.ts"() {
@@ -8320,13 +9416,15 @@ var init_advanced = __esm({
8320
9416
  init_duration();
8321
9417
  init_bundle();
8322
9418
  init_suite();
9419
+ init_cohort2();
9420
+ init_gate();
8323
9421
  init_maybe_inspect_run();
8324
9422
  }
8325
9423
  });
8326
9424
 
8327
9425
  // packages/index-sqlite/src/types.ts
8328
9426
  var INDEX_SCHEMA_VERSION, INDEX_DB_FILENAME;
8329
- var init_types5 = __esm({
9427
+ var init_types6 = __esm({
8330
9428
  "packages/index-sqlite/src/types.ts"() {
8331
9429
  INDEX_SCHEMA_VERSION = "1";
8332
9430
  INDEX_DB_FILENAME = "trace-index.sqlite";
@@ -8384,19 +9482,19 @@ var require_file_uri_to_path = __commonJS({
8384
9482
  var rest = decodeURI(uri.substring(7));
8385
9483
  var firstSlash = rest.indexOf("/");
8386
9484
  var host = rest.substring(0, firstSlash);
8387
- var path30 = rest.substring(firstSlash + 1);
9485
+ var path33 = rest.substring(firstSlash + 1);
8388
9486
  if ("localhost" == host) host = "";
8389
9487
  if (host) {
8390
9488
  host = sep + sep + host;
8391
9489
  }
8392
- path30 = path30.replace(/^(.+)\|/, "$1:");
9490
+ path33 = path33.replace(/^(.+)\|/, "$1:");
8393
9491
  if (sep == "\\") {
8394
- path30 = path30.replace(/\//g, "\\");
9492
+ path33 = path33.replace(/\//g, "\\");
8395
9493
  }
8396
- if (/^.+\:/.test(path30)) ; else {
8397
- path30 = sep + path30;
9494
+ if (/^.+\:/.test(path33)) ; else {
9495
+ path33 = sep + path33;
8398
9496
  }
8399
- return host + path30;
9497
+ return host + path33;
8400
9498
  }
8401
9499
  }
8402
9500
  });
@@ -8405,18 +9503,18 @@ var require_file_uri_to_path = __commonJS({
8405
9503
  var require_bindings = __commonJS({
8406
9504
  "node_modules/.pnpm/bindings@1.5.0/node_modules/bindings/bindings.js"(exports$1, module) {
8407
9505
  var fs = __require("fs");
8408
- var path30 = __require("path");
9506
+ var path33 = __require("path");
8409
9507
  var fileURLToPath2 = require_file_uri_to_path();
8410
- var join = path30.join;
8411
- var dirname = path30.dirname;
8412
- var exists2 = fs.accessSync && function(path31) {
9508
+ var join = path33.join;
9509
+ var dirname = path33.dirname;
9510
+ var exists2 = fs.accessSync && function(path34) {
8413
9511
  try {
8414
- fs.accessSync(path31);
9512
+ fs.accessSync(path34);
8415
9513
  } catch (e) {
8416
9514
  return false;
8417
9515
  }
8418
9516
  return true;
8419
- } || fs.existsSync || path30.existsSync;
9517
+ } || fs.existsSync || path33.existsSync;
8420
9518
  var defaults = {
8421
9519
  arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
8422
9520
  compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
@@ -8461,7 +9559,7 @@ var require_bindings = __commonJS({
8461
9559
  if (!opts.module_root) {
8462
9560
  opts.module_root = exports$1.getRoot(exports$1.getFileName());
8463
9561
  }
8464
- if (path30.extname(opts.bindings) != ".node") {
9562
+ if (path33.extname(opts.bindings) != ".node") {
8465
9563
  opts.bindings += ".node";
8466
9564
  }
8467
9565
  var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
@@ -8696,7 +9794,7 @@ var require_pragma = __commonJS({
8696
9794
  var require_backup = __commonJS({
8697
9795
  "node_modules/.pnpm/better-sqlite3@11.10.0/node_modules/better-sqlite3/lib/methods/backup.js"(exports$1, module) {
8698
9796
  var fs = __require("fs");
8699
- var path30 = __require("path");
9797
+ var path33 = __require("path");
8700
9798
  var { promisify } = __require("util");
8701
9799
  var { cppdb } = require_util();
8702
9800
  var fsAccess = promisify(fs.access);
@@ -8712,7 +9810,7 @@ var require_backup = __commonJS({
8712
9810
  if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
8713
9811
  if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
8714
9812
  if (handler != null && typeof handler !== "function") throw new TypeError('Expected the "progress" option to be a function');
8715
- await fsAccess(path30.dirname(filename)).catch(() => {
9813
+ await fsAccess(path33.dirname(filename)).catch(() => {
8716
9814
  throw new TypeError("Cannot save backup because the directory does not exist");
8717
9815
  });
8718
9816
  const isNewFile = await fsAccess(filename).then(() => false, () => true);
@@ -9012,7 +10110,7 @@ var require_inspect = __commonJS({
9012
10110
  var require_database = __commonJS({
9013
10111
  "node_modules/.pnpm/better-sqlite3@11.10.0/node_modules/better-sqlite3/lib/database.js"(exports$1, module) {
9014
10112
  var fs = __require("fs");
9015
- var path30 = __require("path");
10113
+ var path33 = __require("path");
9016
10114
  var util = require_util();
9017
10115
  var SqliteError = require_sqlite_error();
9018
10116
  var DEFAULT_ADDON;
@@ -9048,7 +10146,7 @@ var require_database = __commonJS({
9048
10146
  addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
9049
10147
  } else if (typeof nativeBinding === "string") {
9050
10148
  const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : __require;
9051
- addon = requireFunc(path30.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
10149
+ addon = requireFunc(path33.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
9052
10150
  } else {
9053
10151
  addon = nativeBinding;
9054
10152
  }
@@ -9056,7 +10154,7 @@ var require_database = __commonJS({
9056
10154
  addon.setErrorConstructor(SqliteError);
9057
10155
  addon.isInitialized = true;
9058
10156
  }
9059
- if (!anonymous && !fs.existsSync(path30.dirname(filename))) {
10157
+ if (!anonymous && !fs.existsSync(path33.dirname(filename))) {
9060
10158
  throw new TypeError("Cannot open database because the directory does not exist");
9061
10159
  }
9062
10160
  Object.defineProperties(this, {
@@ -9104,7 +10202,7 @@ function metaDefaults(sourceDir, fileCount) {
9104
10202
  var INDEX_SCHEMA_SQL, DERIVE_SESSIONS_SQL, META_KEYS;
9105
10203
  var init_schema = __esm({
9106
10204
  "packages/index-sqlite/src/schema.ts"() {
9107
- init_types5();
10205
+ init_types6();
9108
10206
  INDEX_SCHEMA_SQL = `
9109
10207
  DROP TABLE IF EXISTS meta;
9110
10208
  DROP TABLE IF EXISTS errors;
@@ -9183,8 +10281,8 @@ GROUP BY session_id;
9183
10281
  }
9184
10282
  });
9185
10283
  function resolveIndexDbPath(traceDir, dbPath) {
9186
- if (dbPath && dbPath.trim() !== "") return path23__default.default.resolve(dbPath);
9187
- return path23__default.default.join(path23__default.default.resolve(traceDir), INDEX_DB_FILENAME);
10284
+ if (dbPath && dbPath.trim() !== "") return path24__default.default.resolve(dbPath);
10285
+ return path24__default.default.join(path24__default.default.resolve(traceDir), INDEX_DB_FILENAME);
9188
10286
  }
9189
10287
  function str(value) {
9190
10288
  return typeof value === "string" && value !== "" ? value : null;
@@ -9276,7 +10374,7 @@ async function buildIndex(options = {}) {
9276
10374
  warnings.push(`index.unreadable: ${file}`);
9277
10375
  }
9278
10376
  }
9279
- await promises.mkdir(path23__default.default.dirname(dbPath), { recursive: true });
10377
+ await promises.mkdir(path24__default.default.dirname(dbPath), { recursive: true });
9280
10378
  await promises.rm(dbPath, { force: true });
9281
10379
  const db = new import_better_sqlite3.default(dbPath);
9282
10380
  let runCount = 0;
@@ -9357,7 +10455,7 @@ var init_builder = __esm({
9357
10455
  import_better_sqlite3 = __toESM(require_lib());
9358
10456
  init_advanced();
9359
10457
  init_schema();
9360
- init_types5();
10458
+ init_types6();
9361
10459
  DEFAULT_MAX_RUNS = 1e4;
9362
10460
  rebuildIndex = buildIndex;
9363
10461
  }
@@ -9507,14 +10605,14 @@ __export(src_exports, {
9507
10605
  });
9508
10606
  var init_src = __esm({
9509
10607
  "packages/index-sqlite/src/index.ts"() {
9510
- init_types5();
10608
+ init_types6();
9511
10609
  init_builder();
9512
10610
  init_query();
9513
10611
  }
9514
10612
  });
9515
10613
 
9516
10614
  // package.json
9517
- var version = "5.0.0";
10615
+ var version = "5.2.0";
9518
10616
 
9519
10617
  // packages/cli/src/list.ts
9520
10618
  init_advanced();
@@ -10459,7 +11557,7 @@ init_nanoid();
10459
11557
  function isFiniteNumber2(v) {
10460
11558
  return typeof v === "number" && Number.isFinite(v);
10461
11559
  }
10462
- function safeString(v) {
11560
+ function safeString2(v) {
10463
11561
  if (typeof v !== "string") return void 0;
10464
11562
  const t = v.trim();
10465
11563
  return t === "" ? void 0 : t;
@@ -10509,7 +11607,7 @@ var EventNormalizer = class {
10509
11607
  let runId;
10510
11608
  for (const k of cfg.runIdKeys) {
10511
11609
  const v = raw[k];
10512
- const s = safeString(v);
11610
+ const s = safeString2(v);
10513
11611
  if (s) {
10514
11612
  runId = s;
10515
11613
  break;
@@ -10525,7 +11623,7 @@ var EventNormalizer = class {
10525
11623
  }
10526
11624
  };
10527
11625
  }
10528
- const eventName = safeString(raw[cfg.eventKey]);
11626
+ const eventName = safeString2(raw[cfg.eventKey]);
10529
11627
  if (!eventName) {
10530
11628
  return {
10531
11629
  warning: {
@@ -10543,7 +11641,7 @@ var EventNormalizer = class {
10543
11641
  const timestamp = parsedTs ?? Date.now();
10544
11642
  const timestampMissing = parsedTs === void 0;
10545
11643
  const parentIdKey = cfg.parentIdKey;
10546
- const parentId = parentIdKey ? safeString(raw[parentIdKey]) : void 0;
11644
+ const parentId = parentIdKey ? safeString2(raw[parentIdKey]) : void 0;
10547
11645
  let durationMs2;
10548
11646
  const durationKey = cfg.durationKey;
10549
11647
  if (durationKey) {
@@ -10554,7 +11652,7 @@ var EventNormalizer = class {
10554
11652
  }
10555
11653
  let status;
10556
11654
  const statusKey = cfg.statusKey;
10557
- const statusRaw = statusKey ? safeString(raw[statusKey]) : void 0;
11655
+ const statusRaw = statusKey ? safeString2(raw[statusKey]) : void 0;
10558
11656
  if (statusRaw === "running" || statusRaw === "ok" || statusRaw === "error") {
10559
11657
  status = statusRaw;
10560
11658
  } else if (mapping?.status) {
@@ -11498,115 +12596,8 @@ function redactRunTreeForExport(tree, options) {
11498
12596
  return clone;
11499
12597
  }
11500
12598
 
11501
- // packages/core/src/exporters/helpers.ts
11502
- var REDACT_SUBSTRINGS = [
11503
- "authorization",
11504
- "cookie",
11505
- "token",
11506
- "apikey",
11507
- "password",
11508
- "secret",
11509
- "email"
11510
- ];
11511
- function shouldRedactKey(key) {
11512
- const k = key.toLowerCase();
11513
- for (const s of REDACT_SUBSTRINGS) {
11514
- if (k.includes(s)) return true;
11515
- }
11516
- return false;
11517
- }
11518
- function safeString2(value, maxLength) {
11519
- if (value === null || value === void 0) return "";
11520
- let s;
11521
- if (typeof value === "string") s = value;
11522
- else if (typeof value === "number" || typeof value === "boolean") s = String(value);
11523
- else s = stableJson(value, false);
11524
- if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
11525
- return `${s.slice(0, maxLength)}\u2026`;
11526
- }
11527
- return s;
11528
- }
11529
- function escapeMarkdown(value) {
11530
- return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
11531
- }
11532
- function escapeHtml(value) {
11533
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
11534
- }
11535
- function sortKeysDeep(input3) {
11536
- if (input3 === null || typeof input3 !== "object") return input3;
11537
- if (Array.isArray(input3)) return input3.map(sortKeysDeep);
11538
- const o = input3;
11539
- const out = {};
11540
- for (const k of Object.keys(o).sort()) {
11541
- out[k] = sortKeysDeep(o[k]);
11542
- }
11543
- return out;
11544
- }
11545
- function stableJson(value, pretty) {
11546
- const sorted = sortKeysDeep(value);
11547
- return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
11548
- }
11549
- function compactAttributes3(attrs, options) {
11550
- if (attrs === void 0) return {};
11551
- const maxLen = options?.maxLength ?? 500;
11552
- const redacted = options?.redacted ?? true;
11553
- const out = {};
11554
- for (const key of Object.keys(attrs).sort()) {
11555
- if (redacted && shouldRedactKey(key)) {
11556
- out[key] = "[REDACTED]";
11557
- continue;
11558
- }
11559
- const v = attrs[key];
11560
- out[key] = compactValue(v, maxLen, redacted);
11561
- }
11562
- return out;
11563
- }
11564
- function compactValue(value, maxLen, redacted) {
11565
- if (value === null || typeof value !== "object") {
11566
- return typeof value === "string" ? safeString2(value, maxLen) : value;
11567
- }
11568
- if (Array.isArray(value)) {
11569
- const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
11570
- if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
11571
- return arr;
11572
- }
11573
- const o = value;
11574
- const inner = {};
11575
- for (const k of Object.keys(o)) {
11576
- if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
11577
- else inner[k] = compactValue(o[k], maxLen, redacted);
11578
- }
11579
- return inner;
11580
- }
11581
- function flattenTree(tree) {
11582
- const out = [];
11583
- function walk(nodes) {
11584
- for (const n of nodes) {
11585
- out.push(n);
11586
- if (n.children.length > 0) walk(n.children);
11587
- }
11588
- }
11589
- walk(tree.children);
11590
- return out;
11591
- }
11592
- function zeroKinds() {
11593
- return {
11594
- RUN: 0,
11595
- AGENT: 0,
11596
- LLM: 0,
11597
- TOOL: 0,
11598
- CHAIN: 0,
11599
- RETRIEVER: 0,
11600
- DECISION: 0,
11601
- RESULT: 0,
11602
- ERROR: 0,
11603
- LOGIC: 0,
11604
- LOG: 0,
11605
- OUTCOME: 0
11606
- };
11607
- }
11608
-
11609
12599
  // packages/core/src/exporters/html-exporter.ts
12600
+ init_helpers();
11610
12601
  function renderTreeHtml(nodes, ulClass = "tree") {
11611
12602
  if (nodes.length === 0) return "";
11612
12603
  const parts = [`<ul class="${ulClass}">`];
@@ -11679,7 +12670,7 @@ function exportHtml(tree, options) {
11679
12670
  if (includeErrors && errors.length > 0) {
11680
12671
  errorsHtml += "<h2>Errors</h2><ul>";
11681
12672
  for (const n of errors) {
11682
- const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString2(
12673
+ const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
11683
12674
  n.event.attributes.error.message,
11684
12675
  maxLen
11685
12676
  ) : "";
@@ -11744,6 +12735,7 @@ ${attrsHtml}
11744
12735
  }
11745
12736
 
11746
12737
  // packages/core/src/exporters/markdown-exporter.ts
12738
+ init_helpers();
11747
12739
  function renderTreeAscii(nodes, indent = "") {
11748
12740
  const lines = [];
11749
12741
  for (let i = 0; i < nodes.length; i++) {
@@ -11828,7 +12820,7 @@ function exportMarkdown(tree, options) {
11828
12820
  lines.push("## Errors");
11829
12821
  lines.push("");
11830
12822
  for (const n of errors) {
11831
- const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString2(
12823
+ const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
11832
12824
  n.event.attributes.error.message,
11833
12825
  maxLen
11834
12826
  ) : "";
@@ -11866,6 +12858,7 @@ function exportMarkdown(tree, options) {
11866
12858
  warnings
11867
12859
  };
11868
12860
  }
12861
+ init_helpers();
11869
12862
  function hexFrom(seed, byteLen) {
11870
12863
  return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
11871
12864
  }
@@ -11992,6 +12985,9 @@ function exportOpenInference(tree, options) {
11992
12985
  warnings
11993
12986
  };
11994
12987
  }
12988
+
12989
+ // packages/core/src/exporters/otlp-json-exporter.ts
12990
+ init_helpers();
11995
12991
  function hexFrom2(seed, byteLen) {
11996
12992
  return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
11997
12993
  }
@@ -12195,7 +13191,11 @@ function validateExportContent(format, content) {
12195
13191
  return { ok: false, format, errors, warnings };
12196
13192
  }
12197
13193
 
13194
+ // packages/core/src/exporters/index.ts
13195
+ init_helpers();
13196
+
12198
13197
  // packages/core/src/exporters/manual-trace-adapter.ts
13198
+ init_helpers();
12199
13199
  function stepTypeToInspectKind(t) {
12200
13200
  switch (t) {
12201
13201
  case "llm":
@@ -12375,6 +13375,7 @@ function validateExport(result) {
12375
13375
  }
12376
13376
 
12377
13377
  // packages/core/src/report.ts
13378
+ init_helpers();
12378
13379
  init_timeline();
12379
13380
  init_what();
12380
13381
  init_outcomes();
@@ -12531,6 +13532,9 @@ ${attrsSection}
12531
13532
  };
12532
13533
  }
12533
13534
 
13535
+ // packages/core/src/entries/exporters.ts
13536
+ init_helpers();
13537
+
12534
13538
  // packages/cli/src/export.ts
12535
13539
  function parseRedactionProfile(s) {
12536
13540
  const v = (s ?? "local").trim().toLowerCase();
@@ -12609,9 +13613,9 @@ Trace directory: ${traceDir}`);
12609
13613
  if (validation !== void 0 && !validation.ok) {
12610
13614
  process.exitCode = 1;
12611
13615
  }
12612
- const outPath = options.output !== void 0 && options.output.trim() !== "" ? path23__default.default.resolve(options.output.trim()) : void 0;
13616
+ const outPath = options.output !== void 0 && options.output.trim() !== "" ? path24__default.default.resolve(options.output.trim()) : void 0;
12613
13617
  if (outPath !== void 0) {
12614
- await promises.mkdir(path23__default.default.dirname(outPath), { recursive: true });
13618
+ await promises.mkdir(path24__default.default.dirname(outPath), { recursive: true });
12615
13619
  await promises.writeFile(outPath, result.content, "utf-8");
12616
13620
  const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
12617
13621
  console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
@@ -12752,6 +13756,7 @@ function manualTraceEventsToComparableRun(events) {
12752
13756
  }
12753
13757
 
12754
13758
  // packages/core/src/diff/engine.ts
13759
+ init_helpers();
12755
13760
  var DEFAULT_THRESHOLD_MS = 0;
12756
13761
  function pathSeg(step, index) {
12757
13762
  return { index, name: step.name, stepId: step.id };
@@ -12791,13 +13796,13 @@ function pairSteps(left, right) {
12791
13796
  return pairs;
12792
13797
  }
12793
13798
  function compareLeafSteps(L, R, segments, opts, out) {
12794
- const path30 = buildPath(segments);
13799
+ const path33 = buildPath(segments);
12795
13800
  if (L.name !== R.name) {
12796
13801
  out.push({
12797
13802
  kind: "structure",
12798
13803
  severity: "warning",
12799
13804
  message: "Step name differs",
12800
- path: path30,
13805
+ path: path33,
12801
13806
  left: L.name,
12802
13807
  right: R.name
12803
13808
  });
@@ -12807,7 +13812,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
12807
13812
  kind: "step-type",
12808
13813
  severity: "warning",
12809
13814
  message: "Step type differs",
12810
- path: path30,
13815
+ path: path33,
12811
13816
  left: L.type,
12812
13817
  right: R.type
12813
13818
  });
@@ -12817,7 +13822,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
12817
13822
  kind: "step-status",
12818
13823
  severity: "warning",
12819
13824
  message: "Step status differs",
12820
- path: path30,
13825
+ path: path33,
12821
13826
  left: L.status,
12822
13827
  right: R.status
12823
13828
  });
@@ -12829,7 +13834,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
12829
13834
  kind: "error",
12830
13835
  severity: "error",
12831
13836
  message: "Step error message differs",
12832
- path: path30,
13837
+ path: path33,
12833
13838
  left: le || void 0,
12834
13839
  right: re || void 0
12835
13840
  });
@@ -12847,7 +13852,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
12847
13852
  kind: "duration",
12848
13853
  severity: "info",
12849
13854
  message: "Step duration differs",
12850
- path: path30,
13855
+ path: path33,
12851
13856
  left: ld,
12852
13857
  right: rd
12853
13858
  });
@@ -12860,7 +13865,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
12860
13865
  kind: "metadata",
12861
13866
  severity: "info",
12862
13867
  message: "Step metadata differs",
12863
- path: path30,
13868
+ path: path33,
12864
13869
  left: L.metadata,
12865
13870
  right: R.metadata
12866
13871
  });
@@ -12872,7 +13877,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
12872
13877
  kind: "output",
12873
13878
  severity: "info",
12874
13879
  message: "Output preview differs",
12875
- path: path30,
13880
+ path: path33,
12876
13881
  left: L.outputPreview,
12877
13882
  right: R.outputPreview
12878
13883
  });
@@ -13033,11 +14038,11 @@ function diffRuns(left, right, options) {
13033
14038
 
13034
14039
  // packages/core/src/diff/renderer.ts
13035
14040
  init_source();
13036
- function formatPath(path30) {
13037
- if (path30 === void 0 || path30.path.length === 0) {
14041
+ function formatPath(path33) {
14042
+ if (path33 === void 0 || path33.path.length === 0) {
13038
14043
  return "(run)";
13039
14044
  }
13040
- return path30.path.map((s) => s.name).join(" > ");
14045
+ return path33.path.map((s) => s.name).join(" > ");
13041
14046
  }
13042
14047
  function formatValue(v, verbose) {
13043
14048
  if (v === void 0) return "(undefined)";
@@ -13425,7 +14430,7 @@ function indexedToMetadata(row, traceDir) {
13425
14430
  endedAt: row.endedAt ?? void 0,
13426
14431
  durationMs: row.durationMs ?? void 0,
13427
14432
  eventCount: 0,
13428
- filePath: path23__default.default.join(traceDir, row.file),
14433
+ filePath: path24__default.default.join(traceDir, row.file),
13429
14434
  fileSize: 0,
13430
14435
  createdAt: new Date(row.mtimeMs)
13431
14436
  };
@@ -13906,9 +14911,9 @@ async function reportCommand(runId, options = {}) {
13906
14911
  correlation: !options.noCorrelation,
13907
14912
  ...options.section ? { section: options.section } : {}
13908
14913
  });
13909
- const outPath = options.output !== void 0 && options.output.trim() !== "" ? path23__default.default.resolve(options.output.trim()) : void 0;
14914
+ const outPath = options.output !== void 0 && options.output.trim() !== "" ? path24__default.default.resolve(options.output.trim()) : void 0;
13910
14915
  if (outPath !== void 0) {
13911
- await promises.mkdir(path23__default.default.dirname(outPath), { recursive: true });
14916
+ await promises.mkdir(path24__default.default.dirname(outPath), { recursive: true });
13912
14917
  await promises.writeFile(outPath, result.content, "utf-8");
13913
14918
  console.log(`Wrote ${result.fileExtension} report to ${outPath}`);
13914
14919
  }
@@ -14158,17 +15163,17 @@ function applyRule(rule, value, replacement) {
14158
15163
  }
14159
15164
  return value;
14160
15165
  }
14161
- function childPath(path30, key) {
15166
+ function childPath(path33, key) {
14162
15167
  if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
14163
- return path30 ? `${path30}.${key}` : key;
15168
+ return path33 ? `${path33}.${key}` : key;
14164
15169
  }
14165
- return `${path30 || "$"}[${JSON.stringify(key)}]`;
15170
+ return `${path33 || "$"}[${JSON.stringify(key)}]`;
14166
15171
  }
14167
- function indexPath(path30, index) {
14168
- return `${path30 || "$"}[${index}]`;
15172
+ function indexPath(path33, index) {
15173
+ return `${path33 || "$"}[${index}]`;
14169
15174
  }
14170
- function makeFinding(path30, detector, action, matchKind, severity = "warning", preview) {
14171
- return preview === void 0 ? { path: path30, detector, action, severity, matchKind } : { path: path30, detector, action, severity, matchKind, preview };
15175
+ function makeFinding(path33, detector, action, matchKind, severity = "warning", preview) {
15176
+ return preview === void 0 ? { path: path33, detector, action, severity, matchKind } : { path: path33, detector, action, severity, matchKind, preview };
14172
15177
  }
14173
15178
  function createRedactionProfile(profile = "local") {
14174
15179
  switch (profile) {
@@ -14237,11 +15242,11 @@ var Redactor2 = class {
14237
15242
  #recordFinding(state, finding) {
14238
15243
  if (this.#collectFindings) state.findings.push(finding);
14239
15244
  }
14240
- #redactValue(value, key, path30, depth, state) {
15245
+ #redactValue(value, key, path33, depth, state) {
14241
15246
  if (depth > this.#maxDepth) {
14242
15247
  this.#recordFinding(
14243
15248
  state,
14244
- makeFinding(path30, "structure.maxDepth", "truncate", "value", "warning")
15249
+ makeFinding(path33, "structure.maxDepth", "truncate", "value", "warning")
14245
15250
  );
14246
15251
  return "[Truncated]";
14247
15252
  }
@@ -14250,19 +15255,19 @@ var Redactor2 = class {
14250
15255
  if (rule) {
14251
15256
  this.#recordFinding(
14252
15257
  state,
14253
- makeFinding(path30, `key.${rule.key}`, actionForRule(rule), "key", "warning")
15258
+ makeFinding(path33, `key.${rule.key}`, actionForRule(rule), "key", "warning")
14254
15259
  );
14255
15260
  return applyRule(rule, value, this.#replacement);
14256
15261
  }
14257
15262
  }
14258
15263
  for (const detector of this.#detectors) {
14259
- const detections = detector.detect({ path: path30, key, value });
15264
+ const detections = detector.detect({ path: path33, key, value });
14260
15265
  for (const detection of detections) {
14261
15266
  const action = detection.action ?? "replace";
14262
15267
  this.#recordFinding(
14263
15268
  state,
14264
15269
  makeFinding(
14265
- path30,
15270
+ path33,
14266
15271
  detector.id,
14267
15272
  action,
14268
15273
  detection.matchKind ?? detector.matchKind ?? "custom",
@@ -14280,7 +15285,7 @@ var Redactor2 = class {
14280
15285
  const out = [];
14281
15286
  state.seen.set(value, out);
14282
15287
  value.forEach((item, index) => {
14283
- out[index] = this.#redactValue(item, void 0, indexPath(path30, index), depth + 1, state);
15288
+ out[index] = this.#redactValue(item, void 0, indexPath(path33, index), depth + 1, state);
14284
15289
  });
14285
15290
  return out;
14286
15291
  }
@@ -14292,7 +15297,7 @@ var Redactor2 = class {
14292
15297
  out[entryKey] = this.#redactValue(
14293
15298
  entryValue,
14294
15299
  entryKey,
14295
- childPath(path30 === "$" ? "" : path30, entryKey),
15300
+ childPath(path33 === "$" ? "" : path33, entryKey),
14296
15301
  depth + 1,
14297
15302
  state
14298
15303
  );
@@ -14752,14 +15757,14 @@ function uniqueSorted(values) {
14752
15757
  return [...new Set(values)].sort();
14753
15758
  }
14754
15759
  function isWithinDirectory(child, parent) {
14755
- const relative = path23__default.default.relative(parent, child);
14756
- return relative === "" || !relative.startsWith("..") && !path23__default.default.isAbsolute(relative);
15760
+ const relative = path24__default.default.relative(parent, child);
15761
+ return relative === "" || !relative.startsWith("..") && !path24__default.default.isAbsolute(relative);
14757
15762
  }
14758
15763
  async function resolveOutputPath(inputPath, output2, force) {
14759
15764
  if (output2 === void 0 || output2.trim() === "") return void 0;
14760
- const inputAbs = path23__default.default.resolve(inputPath);
14761
- const outputAbs = path23__default.default.resolve(output2.trim());
14762
- const inputDir = path23__default.default.dirname(inputAbs);
15765
+ const inputAbs = path24__default.default.resolve(inputPath);
15766
+ const outputAbs = path24__default.default.resolve(output2.trim());
15767
+ const inputDir = path24__default.default.dirname(inputAbs);
14763
15768
  if (!isWithinDirectory(outputAbs, inputDir)) {
14764
15769
  throw new Error("Refusing to write migrated output outside the input directory.");
14765
15770
  }
@@ -14880,7 +15885,7 @@ async function migrateCommand(input3, options = {}) {
14880
15885
  process.exitCode = 1;
14881
15886
  return;
14882
15887
  }
14883
- const inputPath = path23__default.default.resolve(input3.trim());
15888
+ const inputPath = path24__default.default.resolve(input3.trim());
14884
15889
  const dryRun = options.dryRun === true;
14885
15890
  if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
14886
15891
  console.error("migrate requires --dry-run or --output <path>.");
@@ -14899,7 +15904,7 @@ async function migrateCommand(input3, options = {}) {
14899
15904
  );
14900
15905
  const result = await buildMigration(inputPath, outputPath);
14901
15906
  if (!dryRun && outputPath !== void 0) {
14902
- await promises.mkdir(path23__default.default.dirname(outputPath), { recursive: true });
15907
+ await promises.mkdir(path24__default.default.dirname(outputPath), { recursive: true });
14903
15908
  await promises.writeFile(outputPath, result.content, "utf-8");
14904
15909
  }
14905
15910
  printSummary2(result, dryRun);
@@ -15233,23 +16238,23 @@ function evaluatePromptInjection(text, options = {}) {
15233
16238
  }
15234
16239
  return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
15235
16240
  }
15236
- function validateSchemaField(value, field, path30, evidence) {
16241
+ function validateSchemaField(value, field, path33, evidence) {
15237
16242
  const ruleId = "guardrail.structured-output";
15238
16243
  if (field.type) {
15239
16244
  const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
15240
16245
  if (actual !== field.type) {
15241
- evidence.push({ ruleId, path: path30, preview: `expected ${field.type}, got ${actual}` });
16246
+ evidence.push({ ruleId, path: path33, preview: `expected ${field.type}, got ${actual}` });
15242
16247
  return;
15243
16248
  }
15244
16249
  }
15245
16250
  if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
15246
- evidence.push({ ruleId, path: path30, preview: "value not in enum" });
16251
+ evidence.push({ ruleId, path: path33, preview: "value not in enum" });
15247
16252
  }
15248
16253
  if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
15249
16254
  const record = value;
15250
16255
  for (const key of field.required) {
15251
16256
  if (!(key in record)) {
15252
- evidence.push({ ruleId, path: `${path30}.${key}`, preview: "missing required key" });
16257
+ evidence.push({ ruleId, path: `${path33}.${key}`, preview: "missing required key" });
15253
16258
  }
15254
16259
  }
15255
16260
  }
@@ -15576,7 +16581,7 @@ function asConfig(value) {
15576
16581
  }
15577
16582
  async function loadConfig(configPath) {
15578
16583
  if (configPath === void 0) return {};
15579
- const extension = path23__default.default.extname(configPath);
16584
+ const extension = path24__default.default.extname(configPath);
15580
16585
  if (TS_CONFIG_EXTENSIONS2.has(extension)) {
15581
16586
  throw new Error(
15582
16587
  "TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
@@ -15585,7 +16590,7 @@ async function loadConfig(configPath) {
15585
16590
  if (!CONFIG_EXTENSIONS2.has(extension)) {
15586
16591
  throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
15587
16592
  }
15588
- const absolute = path23__default.default.resolve(configPath);
16593
+ const absolute = path24__default.default.resolve(configPath);
15589
16594
  if (extension === ".json") {
15590
16595
  const raw = await promises.readFile(absolute, "utf-8");
15591
16596
  return asConfig(JSON.parse(raw));
@@ -15750,10 +16755,10 @@ function printHuman(result) {
15750
16755
  console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
15751
16756
  }
15752
16757
  for (const finding of result.findings) {
15753
- const path30 = finding.evidence[0]?.path;
16758
+ const path33 = finding.evidence[0]?.path;
15754
16759
  const run = finding.evidence[0]?.runId;
15755
16760
  const runPrefix = run ? `[${run}] ` : "";
15756
- console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path30 ? ` (${path30})` : ""}`);
16761
+ console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path33 ? ` (${path33})` : ""}`);
15757
16762
  }
15758
16763
  }
15759
16764
  function readErrorResult(error) {
@@ -15956,7 +16961,7 @@ function createViewerServer(options = {}) {
15956
16961
  return sendJson(res, 200, {
15957
16962
  ok: true,
15958
16963
  readOnly: true,
15959
- traceDir: path23__default.default.resolve(traceDir)
16964
+ traceDir: path24__default.default.resolve(traceDir)
15960
16965
  });
15961
16966
  }
15962
16967
  const td = new TraceDirectory({ dir: traceDir });
@@ -15974,7 +16979,7 @@ function createViewerServer(options = {}) {
15974
16979
  runId: meta2.runId,
15975
16980
  name: meta2.name,
15976
16981
  status: meta2.status,
15977
- file: path23__default.default.basename(meta2.filePath),
16982
+ file: path24__default.default.basename(meta2.filePath),
15978
16983
  startedAt: meta2.startedAt,
15979
16984
  durationMs: meta2.durationMs
15980
16985
  }))
@@ -16089,7 +17094,7 @@ function startViewerServer(options = {}) {
16089
17094
  resolve({
16090
17095
  host,
16091
17096
  port: resolvedPort,
16092
- traceDir: path23__default.default.resolve(traceDir),
17097
+ traceDir: path24__default.default.resolve(traceDir),
16093
17098
  url: `http://${host}:${resolvedPort}`
16094
17099
  });
16095
17100
  });
@@ -16294,10 +17299,10 @@ async function evalRun(input3, options = {}) {
16294
17299
  diagnostics: []
16295
17300
  };
16296
17301
  }
16297
- function evidenceForRun(run, path30) {
16298
- return [{ runId: run.runId, ...path30 !== void 0 ? { path: path30 } : {} }];
17302
+ function evidenceForRun(run, path33) {
17303
+ return [{ runId: run.runId, ...path33 !== void 0 ? { path: path33 } : {} }];
16299
17304
  }
16300
- function evidenceForEvent(event, path30) {
17305
+ function evidenceForEvent(event, path33) {
16301
17306
  return [
16302
17307
  {
16303
17308
  runId: event.runId,
@@ -16305,7 +17310,7 @@ function evidenceForEvent(event, path30) {
16305
17310
  ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
16306
17311
  kind: event.kind,
16307
17312
  name: event.name,
16308
- ...path30 !== void 0 ? { path: path30 } : {}
17313
+ ...path33 !== void 0 ? { path: path33 } : {}
16309
17314
  }
16310
17315
  ];
16311
17316
  }
@@ -16463,9 +17468,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
16463
17468
  function tokenize(text) {
16464
17469
  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));
16465
17470
  }
16466
- function firstEvidence(fields, run, path30) {
17471
+ function firstEvidence(fields, run, path33) {
16467
17472
  const first = fields[0];
16468
- return first === void 0 ? evidenceForRun(run, path30) : evidenceForEvent(first.node.event, first.path);
17473
+ return first === void 0 ? evidenceForRun(run, path33) : evidenceForEvent(first.node.event, first.path);
16469
17474
  }
16470
17475
  function collectSourceIds(nodes, keys) {
16471
17476
  const wanted = keySet(keys);
@@ -16842,8 +17847,8 @@ function renderEvalMarkdown(result) {
16842
17847
  if (result.findings.length > 0) {
16843
17848
  lines.push("", "## Findings");
16844
17849
  for (const finding of result.findings) {
16845
- const path30 = finding.evidence[0]?.path;
16846
- lines.push(`- ${finding.ruleId}: ${finding.message}${path30 ? ` (${path30})` : ""}`);
17850
+ const path33 = finding.evidence[0]?.path;
17851
+ lines.push(`- ${finding.ruleId}: ${finding.message}${path33 ? ` (${path33})` : ""}`);
16847
17852
  }
16848
17853
  }
16849
17854
  return `${lines.join("\n")}
@@ -16890,7 +17895,7 @@ function asConfig2(value) {
16890
17895
  }
16891
17896
  async function loadConfig2(configPath) {
16892
17897
  if (configPath === void 0) return {};
16893
- const extension = path23__default.default.extname(configPath);
17898
+ const extension = path24__default.default.extname(configPath);
16894
17899
  if (TS_CONFIG_EXTENSIONS3.has(extension)) {
16895
17900
  throw new Error(
16896
17901
  "TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
@@ -16899,7 +17904,7 @@ async function loadConfig2(configPath) {
16899
17904
  if (!CONFIG_EXTENSIONS3.has(extension)) {
16900
17905
  throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
16901
17906
  }
16902
- const absolute = path23__default.default.resolve(configPath);
17907
+ const absolute = path24__default.default.resolve(configPath);
16903
17908
  if (extension === ".json") {
16904
17909
  const raw = await promises.readFile(absolute, "utf-8");
16905
17910
  return asConfig2(JSON.parse(raw));
@@ -17036,8 +18041,8 @@ function printHuman2(result) {
17036
18041
  console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
17037
18042
  }
17038
18043
  for (const finding of result.findings) {
17039
- const path30 = finding.evidence[0]?.path;
17040
- console.log(`- ${finding.ruleId}: ${finding.message}${path30 ? ` (${path30})` : ""}`);
18044
+ const path33 = finding.evidence[0]?.path;
18045
+ console.log(`- ${finding.ruleId}: ${finding.message}${path33 ? ` (${path33})` : ""}`);
17041
18046
  }
17042
18047
  }
17043
18048
  function readErrorResult2(error) {
@@ -17275,8 +18280,8 @@ function printHuman3(result) {
17275
18280
  console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
17276
18281
  }
17277
18282
  for (const finding of result.findings) {
17278
- const path30 = finding.evidence[0]?.path;
17279
- console.log(`- ${finding.ruleId}: ${finding.message}${path30 ? ` (${path30})` : ""}`);
18283
+ const path33 = finding.evidence[0]?.path;
18284
+ console.log(`- ${finding.ruleId}: ${finding.message}${path33 ? ` (${path33})` : ""}`);
17280
18285
  }
17281
18286
  console.log(`Note: ${result.note}`);
17282
18287
  }
@@ -17427,8 +18432,8 @@ function renderCheckSection(result) {
17427
18432
  `Diagnostics: ${result.diagnostics.length}`
17428
18433
  ];
17429
18434
  for (const finding of result.findings.slice(0, 10)) {
17430
- const path30 = finding.evidence[0]?.path ?? "(run)";
17431
- lines.push(`- ${finding.ruleId}: ${finding.message} (${path30})`);
18435
+ const path33 = finding.evidence[0]?.path ?? "(run)";
18436
+ lines.push(`- ${finding.ruleId}: ${finding.message} (${path33})`);
17432
18437
  }
17433
18438
  for (const diagnostic7 of result.diagnostics.slice(0, 10)) {
17434
18439
  lines.push(`- ${diagnostic7.code}: ${diagnostic7.message}`);
@@ -17506,8 +18511,8 @@ function renderHtml(trace, check, diff) {
17506
18511
  `;
17507
18512
  }
17508
18513
  async function writeArtifact(outputDir, relativePath, content, files) {
17509
- const outPath = path23__default.default.join(outputDir, relativePath);
17510
- await promises.mkdir(path23__default.default.dirname(outPath), { recursive: true });
18514
+ const outPath = path24__default.default.join(outputDir, relativePath);
18515
+ await promises.mkdir(path24__default.default.dirname(outPath), { recursive: true });
17511
18516
  await promises.writeFile(outPath, content, "utf-8");
17512
18517
  files.push(relativePath);
17513
18518
  }
@@ -17523,7 +18528,7 @@ function manifestStatus(check, diff) {
17523
18528
  return "ok";
17524
18529
  }
17525
18530
  async function artifactsCommand(target, options = {}, stdin = process.stdin) {
17526
- const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path23__default.default.resolve(options.outputDir.trim()) : "";
18531
+ const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path24__default.default.resolve(options.outputDir.trim()) : "";
17527
18532
  if (outputDir === "") {
17528
18533
  console.error("--output-dir is required.");
17529
18534
  process.exitCode = 1;
@@ -17589,8 +18594,8 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
17589
18594
  await writeArtifact(outputDir, "report.html", renderHtml(trace, check, diff), files);
17590
18595
  const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
17591
18596
  if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
17592
- await promises.mkdir(path23__default.default.dirname(path23__default.default.resolve(summaryTarget)), { recursive: true });
17593
- await promises.appendFile(path23__default.default.resolve(summaryTarget), `
18597
+ await promises.mkdir(path24__default.default.dirname(path24__default.default.resolve(summaryTarget)), { recursive: true });
18598
+ await promises.appendFile(path24__default.default.resolve(summaryTarget), `
17594
18599
  ${renderMarkdown(trace, check, diff)}`, "utf-8");
17595
18600
  }
17596
18601
  const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
@@ -17609,10 +18614,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
17609
18614
  findings: diff?.findings.length ?? 0,
17610
18615
  diagnostics: diff?.diagnostics.length ?? 0
17611
18616
  },
17612
- ...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path23__default.default.resolve(summaryTarget) } : {},
18617
+ ...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path24__default.default.resolve(summaryTarget) } : {},
17613
18618
  note: NOTE
17614
18619
  };
17615
- await promises.writeFile(path23__default.default.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
18620
+ await promises.writeFile(path24__default.default.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
17616
18621
  if (options.json === true) {
17617
18622
  console.log(writeJson3(manifest).trimEnd());
17618
18623
  } else {
@@ -17806,19 +18811,19 @@ function serializeWorkspaceManifest(manifest) {
17806
18811
  }
17807
18812
  var INDEX_DIR_NAME = "index";
17808
18813
  function resolveWorkspaceLocation(cwd = process.cwd()) {
17809
- const projectRoot = path23__default.default.resolve(cwd);
17810
- const workspaceDir = path23__default.default.join(projectRoot, WORKSPACE_DIR_NAME);
18814
+ const projectRoot = path24__default.default.resolve(cwd);
18815
+ const workspaceDir = path24__default.default.join(projectRoot, WORKSPACE_DIR_NAME);
17811
18816
  return {
17812
18817
  projectRoot,
17813
18818
  workspaceDir,
17814
- manifestPath: path23__default.default.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
18819
+ manifestPath: path24__default.default.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
17815
18820
  };
17816
18821
  }
17817
18822
  function resolveInsideWorkspace(workspaceDir, relative) {
17818
- const base = path23__default.default.resolve(workspaceDir);
17819
- const resolved = path23__default.default.resolve(base, relative);
17820
- const rel = path23__default.default.relative(base, resolved);
17821
- if (rel === "" || rel === "." || !rel.startsWith("..") && !path23__default.default.isAbsolute(rel)) {
18823
+ const base = path24__default.default.resolve(workspaceDir);
18824
+ const resolved = path24__default.default.resolve(base, relative);
18825
+ const rel = path24__default.default.relative(base, resolved);
18826
+ if (rel === "" || rel === "." || !rel.startsWith("..") && !path24__default.default.isAbsolute(rel)) {
17822
18827
  return resolved;
17823
18828
  }
17824
18829
  throw new Error(
@@ -17887,7 +18892,7 @@ async function createWorkspace(options = {}) {
17887
18892
  created = false;
17888
18893
  adopted = true;
17889
18894
  } else {
17890
- const project = options.project?.trim() || path23__default.default.basename(location.projectRoot) || "workspace";
18895
+ const project = options.project?.trim() || path24__default.default.basename(location.projectRoot) || "workspace";
17891
18896
  const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
17892
18897
  manifest = createDefaultWorkspaceManifest({
17893
18898
  project,
@@ -18026,7 +19031,7 @@ async function doctorWorkspace(location) {
18026
19031
  const abs = resolveInsideWorkspace(location.workspaceDir, rel);
18027
19032
  for (const file of await listJsonl(abs)) {
18028
19033
  try {
18029
- const s = await promises.stat(path23__default.default.join(abs, file));
19034
+ const s = await promises.stat(path24__default.default.join(abs, file));
18030
19035
  newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
18031
19036
  } catch {
18032
19037
  checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
@@ -18074,7 +19079,7 @@ async function cleanWorkspace(location, manifest, options = {}) {
18074
19079
  const relPath = `${rel}/${entry}`;
18075
19080
  removed.push(relPath);
18076
19081
  if (!dryRun) {
18077
- await promises.rm(path23__default.default.join(abs, entry), { recursive: true, force: true });
19082
+ await promises.rm(path24__default.default.join(abs, entry), { recursive: true, force: true });
18078
19083
  }
18079
19084
  }
18080
19085
  }
@@ -18120,8 +19125,8 @@ async function resolveOutputDir(options, runIds, cwd) {
18120
19125
  const location = resolveWorkspaceLocation(cwd);
18121
19126
  const manifest = await readWorkspaceManifestFile(location);
18122
19127
  if (manifest.ok && manifest.manifest) {
18123
- const rel = path23__default.default.relative(location.workspaceDir, normalized);
18124
- if (!rel.startsWith("..") && !path23__default.default.isAbsolute(rel)) {
19128
+ const rel = path24__default.default.relative(location.workspaceDir, normalized);
19129
+ if (!rel.startsWith("..") && !path24__default.default.isAbsolute(rel)) {
18125
19130
  return resolveInsideWorkspace(location.workspaceDir, rel);
18126
19131
  }
18127
19132
  }
@@ -18137,7 +19142,7 @@ async function resolveOutputDir(options, runIds, cwd) {
18137
19142
  const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
18138
19143
  return resolveInsideWorkspace(
18139
19144
  location.workspaceDir,
18140
- path23__default.default.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
19145
+ path24__default.default.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
18141
19146
  );
18142
19147
  }
18143
19148
  } catch {
@@ -18145,8 +19150,8 @@ async function resolveOutputDir(options, runIds, cwd) {
18145
19150
  return defaultBundleOutputPath(runIds);
18146
19151
  }
18147
19152
  async function writeBundleFile(outputDir, relativePath, content, files) {
18148
- const outPath = path23__default.default.join(outputDir, relativePath);
18149
- await promises.mkdir(path23__default.default.dirname(outPath), { recursive: true });
19153
+ const outPath = path24__default.default.join(outputDir, relativePath);
19154
+ await promises.mkdir(path24__default.default.dirname(outPath), { recursive: true });
18150
19155
  await promises.writeFile(outPath, content, "utf-8");
18151
19156
  files.push(relativePath);
18152
19157
  }
@@ -18372,7 +19377,7 @@ async function bundleCommand(runIdArg, options = {}) {
18372
19377
  files
18373
19378
  );
18374
19379
  metadata.files = [...files].sort((a, b) => a.localeCompare(b));
18375
- await promises.writeFile(path23__default.default.join(outputDir, "metadata.json"), writeJson4(metadata), "utf-8");
19380
+ await promises.writeFile(path24__default.default.join(outputDir, "metadata.json"), writeJson4(metadata), "utf-8");
18376
19381
  if (options.json) {
18377
19382
  console.log(
18378
19383
  writeJson4({
@@ -18406,7 +19411,7 @@ ${body}
18406
19411
  `;
18407
19412
  }
18408
19413
  function validateReporterArtifactPath(options) {
18409
- const outputDir = path23__default.default.resolve(options.outputDir);
19414
+ const outputDir = path24__default.default.resolve(options.outputDir);
18410
19415
  const diagnostics = [];
18411
19416
  const rawPath = options.relativePath;
18412
19417
  if (rawPath.length === 0) {
@@ -18426,7 +19431,7 @@ function validateReporterArtifactPath(options) {
18426
19431
  });
18427
19432
  return { ok: false, outputDir, diagnostics };
18428
19433
  }
18429
- if (path23__default.default.isAbsolute(rawPath) || path23__default.default.win32.isAbsolute(rawPath)) {
19434
+ if (path24__default.default.isAbsolute(rawPath) || path24__default.default.win32.isAbsolute(rawPath)) {
18430
19435
  diagnostics.push({
18431
19436
  code: "artifact_path_absolute",
18432
19437
  severity: "error",
@@ -18435,7 +19440,7 @@ function validateReporterArtifactPath(options) {
18435
19440
  });
18436
19441
  return { ok: false, outputDir, diagnostics };
18437
19442
  }
18438
- const normalized = path23__default.default.posix.normalize(rawPath.replace(/\\/g, "/"));
19443
+ const normalized = path24__default.default.posix.normalize(rawPath.replace(/\\/g, "/"));
18439
19444
  const segments = normalized.split("/");
18440
19445
  if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
18441
19446
  diagnostics.push({
@@ -18446,9 +19451,9 @@ function validateReporterArtifactPath(options) {
18446
19451
  });
18447
19452
  return { ok: false, outputDir, diagnostics };
18448
19453
  }
18449
- const absolutePath = path23__default.default.resolve(outputDir, normalized);
18450
- const relFromOutput = path23__default.default.relative(outputDir, absolutePath);
18451
- if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path23__default.default.isAbsolute(relFromOutput)) {
19454
+ const absolutePath = path24__default.default.resolve(outputDir, normalized);
19455
+ const relFromOutput = path24__default.default.relative(outputDir, absolutePath);
19456
+ if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path24__default.default.isAbsolute(relFromOutput)) {
18452
19457
  diagnostics.push({
18453
19458
  code: "artifact_path_escape",
18454
19459
  severity: "error",
@@ -18594,23 +19599,23 @@ function readManifestDocument(value) {
18594
19599
  };
18595
19600
  }
18596
19601
  function cwdRelative(filePath) {
18597
- const relative = path23__default.default.relative(process.cwd(), path23__default.default.resolve(filePath)).replace(/\\/g, "/");
18598
- if (relative === "" || relative.startsWith("../") || path23__default.default.isAbsolute(relative)) {
18599
- return path23__default.default.basename(filePath);
19602
+ const relative = path24__default.default.relative(process.cwd(), path24__default.default.resolve(filePath)).replace(/\\/g, "/");
19603
+ if (relative === "" || relative.startsWith("../") || path24__default.default.isAbsolute(relative)) {
19604
+ return path24__default.default.basename(filePath);
18600
19605
  }
18601
19606
  return relative;
18602
19607
  }
18603
19608
  async function readReporterManifest(filePath) {
18604
- const absolute = path23__default.default.resolve(filePath);
19609
+ const absolute = path24__default.default.resolve(filePath);
18605
19610
  const raw = await promises.readFile(absolute, "utf-8");
18606
19611
  const document = readManifestDocument(JSON.parse(raw));
18607
19612
  const manifest = document.manifest;
18608
19613
  const results = manifest.results.map((result) => ({
18609
19614
  testId: safeText(result.testId),
18610
19615
  name: safeText(result.name),
18611
- ...result.file === void 0 ? {} : { file: safeText(path23__default.default.basename(result.file)) },
19616
+ ...result.file === void 0 ? {} : { file: safeText(path24__default.default.basename(result.file)) },
18612
19617
  status: result.status,
18613
- ...result.tracePath === void 0 ? {} : { tracePath: safeText(path23__default.default.basename(result.tracePath)) },
19618
+ ...result.tracePath === void 0 ? {} : { tracePath: safeText(path24__default.default.basename(result.tracePath)) },
18614
19619
  artifacts: result.artifacts,
18615
19620
  diagnostics: result.diagnostics
18616
19621
  }));
@@ -18749,15 +19754,15 @@ async function ciSummaryCommand(manifestPaths, options = {}) {
18749
19754
  return;
18750
19755
  }
18751
19756
  const markdown = renderMarkdown2(result);
18752
- const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path23__default.default.resolve(options.output.trim()) : void 0;
19757
+ const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path24__default.default.resolve(options.output.trim()) : void 0;
18753
19758
  if (outputPath !== void 0) {
18754
- await promises.mkdir(path23__default.default.dirname(outputPath), { recursive: true });
19759
+ await promises.mkdir(path24__default.default.dirname(outputPath), { recursive: true });
18755
19760
  await promises.writeFile(outputPath, markdown, "utf-8");
18756
19761
  }
18757
19762
  const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
18758
19763
  if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
18759
- const summaryPath = path23__default.default.resolve(summaryTarget);
18760
- await promises.mkdir(path23__default.default.dirname(summaryPath), { recursive: true });
19764
+ const summaryPath = path24__default.default.resolve(summaryTarget);
19765
+ await promises.mkdir(path24__default.default.dirname(summaryPath), { recursive: true });
18761
19766
  await promises.appendFile(summaryPath, `
18762
19767
  ${markdown}`, "utf-8");
18763
19768
  }
@@ -18905,8 +19910,8 @@ jobs:
18905
19910
  }
18906
19911
  async function planInit(options = {}) {
18907
19912
  const framework = normalizeFramework(options.framework);
18908
- const cwd = path23__default.default.resolve(options.cwd ?? process.cwd());
18909
- const demoPath = framework === "custom" ? path23__default.default.join("examples", "agent-inspect-demo.mjs") : path23__default.default.join("examples", `agent-inspect-${framework}-demo.mjs`);
19913
+ const cwd = path24__default.default.resolve(options.cwd ?? process.cwd());
19914
+ const demoPath = framework === "custom" ? path24__default.default.join("examples", "agent-inspect-demo.mjs") : path24__default.default.join("examples", `agent-inspect-${framework}-demo.mjs`);
18910
19915
  const candidates = [
18911
19916
  { rel: CONFIG_FILE, content: configTemplate(framework) },
18912
19917
  { rel: GITKEEP, content: "" },
@@ -18920,7 +19925,7 @@ async function planInit(options = {}) {
18920
19925
  }
18921
19926
  const files = [];
18922
19927
  for (const candidate of candidates) {
18923
- const abs = path23__default.default.join(cwd, candidate.rel);
19928
+ const abs = path24__default.default.join(cwd, candidate.rel);
18924
19929
  try {
18925
19930
  await promises.access(abs);
18926
19931
  files.push({
@@ -18941,12 +19946,12 @@ async function writePlannedFiles(plan, cwd, options) {
18941
19946
  if (entry.action === "skip") {
18942
19947
  continue;
18943
19948
  }
18944
- const abs = path23__default.default.join(cwd, entry.path);
19949
+ const abs = path24__default.default.join(cwd, entry.path);
18945
19950
  if (options.dryRun) {
18946
19951
  written.push(entry.path);
18947
19952
  continue;
18948
19953
  }
18949
- await promises.mkdir(path23__default.default.dirname(abs), { recursive: true });
19954
+ await promises.mkdir(path24__default.default.dirname(abs), { recursive: true });
18950
19955
  const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate() : demoTemplate(plan.framework);
18951
19956
  await promises.writeFile(abs, content, "utf-8");
18952
19957
  written.push(entry.path);
@@ -18954,7 +19959,7 @@ async function writePlannedFiles(plan, cwd, options) {
18954
19959
  return written;
18955
19960
  }
18956
19961
  async function initCommand(options = {}) {
18957
- const cwd = path23__default.default.resolve(options.cwd ?? process.cwd());
19962
+ const cwd = path24__default.default.resolve(options.cwd ?? process.cwd());
18958
19963
  try {
18959
19964
  const plan = await planInit({ ...options, cwd });
18960
19965
  const toWrite = plan.files.filter((file) => file.action === "create").map((f) => f.path);
@@ -19045,7 +20050,7 @@ function envCheck(name, optional = true) {
19045
20050
  };
19046
20051
  }
19047
20052
  async function traceDirWritable(traceDir) {
19048
- const resolved = path23__default.default.resolve(traceDir);
20053
+ const resolved = path24__default.default.resolve(traceDir);
19049
20054
  try {
19050
20055
  await promises.mkdir(resolved, { recursive: true });
19051
20056
  await promises.access(resolved, promises.constants.W_OK);
@@ -19066,7 +20071,7 @@ async function traceDirWritable(traceDir) {
19066
20071
  }
19067
20072
  }
19068
20073
  function resolvePackage(cwd, name) {
19069
- const require2 = module$1.createRequire(path23__default.default.join(cwd, "package.json"));
20074
+ const require2 = module$1.createRequire(path24__default.default.join(cwd, "package.json"));
19070
20075
  try {
19071
20076
  const pkgPath = require2.resolve(`${name}/package.json`);
19072
20077
  const pkg = require2(pkgPath);
@@ -19077,7 +20082,7 @@ function resolvePackage(cwd, name) {
19077
20082
  }
19078
20083
  function importSmoke(cwd) {
19079
20084
  const results = [];
19080
- const require2 = module$1.createRequire(path23__default.default.join(cwd, "package.json"));
20085
+ const require2 = module$1.createRequire(path24__default.default.join(cwd, "package.json"));
19081
20086
  try {
19082
20087
  require2.resolve("agent-inspect");
19083
20088
  results.push({
@@ -19160,7 +20165,7 @@ function versionMismatchCheck(cwd) {
19160
20165
  };
19161
20166
  }
19162
20167
  async function runDoctorChecks(options = {}) {
19163
- const cwd = path23__default.default.resolve(options.cwd ?? process3__default.default.cwd());
20168
+ const cwd = path24__default.default.resolve(options.cwd ?? process3__default.default.cwd());
19164
20169
  const traceDir = options.traceDir?.trim() || process3__default.default.env.AGENT_INSPECT_TRACE_DIR?.trim() || ".agent-inspect";
19165
20170
  const checks2 = [
19166
20171
  nodeVersionCheck(),
@@ -19274,7 +20279,7 @@ function createTraceDirectoryIndexer() {
19274
20279
  init_advanced();
19275
20280
  var INDEX_FILENAME = ".agent-inspect-index.json";
19276
20281
  function traceIndexPath(traceDir) {
19277
- return path23__default.default.join(traceDir, INDEX_FILENAME);
20282
+ return path24__default.default.join(traceDir, INDEX_FILENAME);
19278
20283
  }
19279
20284
  function parseMaxEntries(raw) {
19280
20285
  if (raw === void 0 || raw.trim() === "") return void 0;
@@ -19420,7 +20425,7 @@ async function newestTraceMtimeMs2(traceDir) {
19420
20425
  for (const file of files) {
19421
20426
  if (!file.endsWith(".jsonl")) continue;
19422
20427
  try {
19423
- const s = await promises.stat(path23__default.default.join(traceDir, file));
20428
+ const s = await promises.stat(path24__default.default.join(traceDir, file));
19424
20429
  if (s.mtimeMs > newest) newest = s.mtimeMs;
19425
20430
  } catch {
19426
20431
  }
@@ -19675,11 +20680,11 @@ function printJson5(value) {
19675
20680
  console.log(JSON.stringify(value, null, 2));
19676
20681
  }
19677
20682
  function resolveCwd(options) {
19678
- return path23__default.default.resolve(options.cwd ?? process.cwd());
20683
+ return path24__default.default.resolve(options.cwd ?? process.cwd());
19679
20684
  }
19680
20685
  async function suiteInitCommand(options = {}) {
19681
20686
  const cwd = resolveCwd(options);
19682
- const configPath = path23__default.default.join(cwd, DEFAULT_CONFIG_FILENAME);
20687
+ const configPath = path24__default.default.join(cwd, DEFAULT_CONFIG_FILENAME);
19683
20688
  const template = defaultSuiteConfigTemplate();
19684
20689
  if (options.dryRun) {
19685
20690
  if (options.json) {
@@ -19789,13 +20794,13 @@ async function suiteListCommand(options = {}) {
19789
20794
  }
19790
20795
  async function writeSuiteArtifact(result, configOutputDir, options) {
19791
20796
  const cwd = resolveCwd(options);
19792
- const outputDir = path23__default.default.resolve(
20797
+ const outputDir = path24__default.default.resolve(
19793
20798
  cwd,
19794
20799
  options.output ?? configOutputDir ?? DEFAULT_SUITE_ARTIFACTS_DIR
19795
20800
  );
19796
20801
  await promises.mkdir(outputDir, { recursive: true });
19797
20802
  const stamp = result.startedAt.replace(/[:.]/g, "-");
19798
- const filePath = path23__default.default.join(outputDir, `${result.suiteName}-${stamp}.json`);
20803
+ const filePath = path24__default.default.join(outputDir, `${result.suiteName}-${stamp}.json`);
19799
20804
  await promises.writeFile(filePath, `${JSON.stringify(result, null, 2)}
19800
20805
  `, "utf-8");
19801
20806
  return filePath;
@@ -19853,7 +20858,7 @@ async function loadSuiteResultFromInput(options) {
19853
20858
  if (options.input === void 0 || options.input.trim() === "") {
19854
20859
  throw new Error("Pass --input <suite-run.json> from a prior suite run.");
19855
20860
  }
19856
- const inputPath = path23__default.default.resolve(cwd, options.input.trim());
20861
+ const inputPath = path24__default.default.resolve(cwd, options.input.trim());
19857
20862
  const raw = await promises.readFile(inputPath, "utf-8");
19858
20863
  return JSON.parse(raw);
19859
20864
  }
@@ -19878,6 +20883,217 @@ async function suiteReportCommand(options = {}) {
19878
20883
  }
19879
20884
  }
19880
20885
 
20886
+ // packages/cli/src/cohort.ts
20887
+ init_advanced();
20888
+ function normalizeMetrics2(raw) {
20889
+ const parsed = parseCohortMetricList(raw);
20890
+ if (parsed.length === 0) return void 0;
20891
+ const allowed = new Set(COHORT_METRIC_IDS);
20892
+ return parsed.filter(
20893
+ (item) => allowed.has(item)
20894
+ );
20895
+ }
20896
+ async function writeArtifacts(result, outputDir) {
20897
+ await promises.mkdir(outputDir, { recursive: true });
20898
+ const jsonPath = path24__default.default.join(outputDir, "cohort-results.json");
20899
+ const markdownPath = path24__default.default.join(outputDir, "cohort-summary.md");
20900
+ const htmlPath = path24__default.default.join(outputDir, "cohort-report.html");
20901
+ await promises.writeFile(jsonPath, `${renderCohortReport(result, { format: "json" })}
20902
+ `, "utf-8");
20903
+ await promises.writeFile(
20904
+ markdownPath,
20905
+ `${renderCohortReport(result, { format: "markdown" })}
20906
+ `,
20907
+ "utf-8"
20908
+ );
20909
+ await promises.writeFile(htmlPath, `${renderCohortReport(result, { format: "html" })}
20910
+ `, "utf-8");
20911
+ return { jsonPath, markdownPath, htmlPath };
20912
+ }
20913
+ async function cohortCommand(options = {}) {
20914
+ try {
20915
+ const traceDir = resolveTraceDir({ dir: options.dir });
20916
+ const { runs } = await loadSessionRuns(traceDir);
20917
+ const result = await analyzeCohort(runs, {
20918
+ traceDir,
20919
+ baseline: options.baseline,
20920
+ candidate: options.candidate,
20921
+ cohortKey: options.cohortKey,
20922
+ groupBy: options.groupBy,
20923
+ metrics: normalizeMetrics2(options.metric)
20924
+ });
20925
+ const format = options.format ?? (options.json ? "json" : "markdown");
20926
+ let artifacts;
20927
+ if (options.output !== void 0 && options.output.trim() !== "") {
20928
+ artifacts = await writeArtifacts(result, path24__default.default.resolve(options.output.trim()));
20929
+ }
20930
+ if (options.json || format === "json") {
20931
+ console.log(
20932
+ JSON.stringify(
20933
+ {
20934
+ ...result,
20935
+ artifacts: artifacts ?? null
20936
+ },
20937
+ null,
20938
+ 2
20939
+ )
20940
+ );
20941
+ } else if (format === "html") {
20942
+ console.log(renderCohortReport(result, { format: "html" }));
20943
+ if (artifacts !== void 0) {
20944
+ console.error(`Artifacts written to ${options.output}`);
20945
+ }
20946
+ } else {
20947
+ console.log(renderCohortReport(result, { format: "markdown" }));
20948
+ if (artifacts !== void 0) {
20949
+ console.log("");
20950
+ console.log(`Artifacts:`);
20951
+ console.log(`- ${artifacts.jsonPath}`);
20952
+ console.log(`- ${artifacts.markdownPath}`);
20953
+ console.log(`- ${artifacts.htmlPath}`);
20954
+ }
20955
+ }
20956
+ if (!result.ok) process.exitCode = 1;
20957
+ } catch (error) {
20958
+ const message = error instanceof Error ? error.message : String(error);
20959
+ if (options.json) {
20960
+ console.log(JSON.stringify({ ok: false, error: message }, null, 2));
20961
+ } else {
20962
+ console.error(`[AgentInspect] cohort failed: ${message}`);
20963
+ }
20964
+ process.exitCode = 1;
20965
+ }
20966
+ }
20967
+
20968
+ // packages/cli/src/gate.ts
20969
+ init_advanced();
20970
+ var SUPPORTED_FORMATS = /* @__PURE__ */ new Set(["markdown", "json", "html", "junit", "github"]);
20971
+ function collectList(value) {
20972
+ if (value === void 0) return [];
20973
+ const items = Array.isArray(value) ? value : [value];
20974
+ return items.flatMap((item) => parseGateList(item));
20975
+ }
20976
+ function parseRate(value) {
20977
+ if (value === void 0 || value.trim() === "") return void 0;
20978
+ const parsed = Number(value);
20979
+ if (!Number.isFinite(parsed)) {
20980
+ throw new Error(`Invalid --max-error-rate: ${value}`);
20981
+ }
20982
+ return parsed;
20983
+ }
20984
+ function parseDuration2(value) {
20985
+ if (value === void 0 || value.trim() === "") return void 0;
20986
+ const parsed = Number(value);
20987
+ if (!Number.isFinite(parsed)) {
20988
+ throw new Error(`Invalid --max-p95-duration: ${value}`);
20989
+ }
20990
+ return parsed;
20991
+ }
20992
+ async function writeArtifacts2(result, outputDir) {
20993
+ await promises.mkdir(outputDir, { recursive: true });
20994
+ const paths = {
20995
+ jsonPath: path24__default.default.join(outputDir, "gate-results.json"),
20996
+ markdownPath: path24__default.default.join(outputDir, "gate-summary.md"),
20997
+ htmlPath: path24__default.default.join(outputDir, "gate-report.html"),
20998
+ junitPath: path24__default.default.join(outputDir, "junit.xml"),
20999
+ githubPath: path24__default.default.join(outputDir, "github-step-summary.md")
21000
+ };
21001
+ await promises.writeFile(
21002
+ paths.jsonPath,
21003
+ `${renderGateReport(result, { format: "json" })}
21004
+ `,
21005
+ "utf-8"
21006
+ );
21007
+ await promises.writeFile(
21008
+ paths.markdownPath,
21009
+ `${renderGateReport(result, { format: "markdown" })}
21010
+ `,
21011
+ "utf-8"
21012
+ );
21013
+ await promises.writeFile(
21014
+ paths.htmlPath,
21015
+ `${renderGateReport(result, { format: "html" })}
21016
+ `,
21017
+ "utf-8"
21018
+ );
21019
+ await promises.writeFile(
21020
+ paths.junitPath,
21021
+ `${renderGateReport(result, { format: "junit" })}
21022
+ `,
21023
+ "utf-8"
21024
+ );
21025
+ await promises.writeFile(
21026
+ paths.githubPath,
21027
+ `${renderGateReport(result, { format: "github" })}
21028
+ `,
21029
+ "utf-8"
21030
+ );
21031
+ return paths;
21032
+ }
21033
+ async function gateCommand(options = {}) {
21034
+ try {
21035
+ const format = options.format ?? (options.json ? "json" : "markdown");
21036
+ if (!SUPPORTED_FORMATS.has(format)) {
21037
+ if (options.json) {
21038
+ console.log(
21039
+ JSON.stringify({ ok: false, exitCode: 4, error: `Unsupported format: ${format}` }, null, 2)
21040
+ );
21041
+ } else {
21042
+ console.error(`[AgentInspect] Unsupported format: ${format}`);
21043
+ }
21044
+ process.exitCode = 4;
21045
+ return;
21046
+ }
21047
+ const gateOptions = {
21048
+ ...options.dir !== void 0 ? { traceDir: resolveTraceDir({ dir: options.dir }) } : {},
21049
+ ...options.suite !== void 0 ? { suitePath: options.suite } : {},
21050
+ ...options.cwd !== void 0 ? { cwd: options.cwd } : {},
21051
+ ...parseRate(options.maxErrorRate) !== void 0 ? { maxErrorRate: parseRate(options.maxErrorRate) } : {},
21052
+ ...parseDuration2(options.maxP95Duration) !== void 0 ? { maxP95DurationMs: parseDuration2(options.maxP95Duration) } : {},
21053
+ forbidTools: collectList(options.forbidTool),
21054
+ requireObservations: collectList(options.requireObservation)
21055
+ };
21056
+ let runs = [];
21057
+ const needsRuns = gateHasThresholds(gateOptions) || gateOptions.traceDir !== void 0 && gateOptions.suitePath === void 0;
21058
+ if (needsRuns) {
21059
+ const traceDir = gateOptions.traceDir ?? resolveTraceDir({ dir: options.dir });
21060
+ const loaded = await loadSessionRuns(traceDir);
21061
+ runs = loaded.runs;
21062
+ gateOptions.traceDir = traceDir;
21063
+ }
21064
+ const result = await runGate(runs, gateOptions);
21065
+ let artifacts;
21066
+ if (options.output !== void 0 && options.output.trim() !== "") {
21067
+ artifacts = await writeArtifacts2(result, path24__default.default.resolve(options.output.trim()));
21068
+ }
21069
+ if (options.json || format === "json") {
21070
+ console.log(JSON.stringify({ ...result, artifacts: artifacts ?? null }, null, 2));
21071
+ } else if (format === "html") {
21072
+ console.log(renderGateReport(result, { format: "html" }));
21073
+ } else if (format === "junit") {
21074
+ console.log(renderGateReport(result, { format: "junit" }));
21075
+ } else if (format === "github") {
21076
+ console.log(renderGateReport(result, { format: "github" }));
21077
+ } else {
21078
+ console.log(renderGateReport(result, { format: "markdown" }));
21079
+ if (artifacts !== void 0) {
21080
+ console.log("");
21081
+ console.log("Artifacts:");
21082
+ for (const value of Object.values(artifacts)) console.log(`- ${value}`);
21083
+ }
21084
+ }
21085
+ process.exitCode = result.exitCode;
21086
+ } catch (error) {
21087
+ const message = error instanceof Error ? error.message : String(error);
21088
+ if (options.json) {
21089
+ console.log(JSON.stringify({ ok: false, exitCode: 2, error: message }, null, 2));
21090
+ } else {
21091
+ console.error(`[AgentInspect] gate failed: ${message}`);
21092
+ }
21093
+ process.exitCode = 2;
21094
+ }
21095
+ }
21096
+
19881
21097
  // packages/cli/src/index.ts
19882
21098
  function runCommand(action) {
19883
21099
  void action().catch((error) => {
@@ -20311,6 +21527,21 @@ function createCliProgram() {
20311
21527
  suiteCmd.command("report").description("Render a report from a prior suite run artifact").option("--input <path>", "suite run JSON artifact").option("--format <format>", "markdown or json", "markdown").option("--json", "print JSON wrapper").action((opts) => {
20312
21528
  runCommand(() => suiteReportCommand(opts));
20313
21529
  });
21530
+ program.command("cohort").description("Compare baseline/candidate trace cohorts for regressions (v5.1+)").option("--dir <path>", "trace directory").option("--baseline <label>", "baseline cohort label (metadata key)").option("--candidate <label>", "candidate cohort label (metadata key)").option("--cohort-key <key>", "metadata key for cohort labels", "cohort").option("--group-by <spec>", "model | session | group | metadata.<key>", "model").option(
21531
+ "--metric <list>",
21532
+ "comma-separated metrics (errorRate,duration,toolChoice,...)"
21533
+ ).option("--format <format>", "markdown, json, or html", "markdown").option("-o, --output <dir>", "write cohort-results.json, cohort-summary.md, cohort-report.html").option("--json", "print deterministic JSON result").action((opts) => {
21534
+ runCommand(() => cohortCommand(opts));
21535
+ });
21536
+ program.command("gate").description("Run deterministic CI quality gates over traces or suites (v5.2+)").option("--dir <path>", "trace directory for threshold checks").option("--suite <path>", "suite config path").option("--max-error-rate <percent>", "maximum allowed error rate percentage").option("--max-p95-duration <ms>", "maximum allowed p95 run duration in ms").option("--forbid-tool <name>", "forbidden tool name (repeatable or comma-separated)").option(
21537
+ "--require-observation <name>",
21538
+ "required passed observation (repeatable or comma-separated)"
21539
+ ).option("--format <format>", "markdown, json, html, junit, or github", "markdown").option(
21540
+ "-o, --output <dir>",
21541
+ "write gate-results.json, gate-summary.md, gate-report.html, junit.xml, github-step-summary.md"
21542
+ ).option("--json", "print deterministic JSON result").action((opts) => {
21543
+ runCommand(() => gateCommand(opts));
21544
+ });
20314
21545
  return program;
20315
21546
  }
20316
21547
  function isPrimaryModule() {
@@ -20318,9 +21549,9 @@ function isPrimaryModule() {
20318
21549
  if (!entry) return false;
20319
21550
  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)));
20320
21551
  try {
20321
- return fs.realpathSync(path23__default.default.resolve(entry)) === fs.realpathSync(path23__default.default.resolve(selfPath));
21552
+ return fs.realpathSync(path24__default.default.resolve(entry)) === fs.realpathSync(path24__default.default.resolve(selfPath));
20322
21553
  } catch {
20323
- return path23__default.default.resolve(entry) === path23__default.default.resolve(selfPath);
21554
+ return path24__default.default.resolve(entry) === path24__default.default.resolve(selfPath);
20324
21555
  }
20325
21556
  }
20326
21557
  if (isPrimaryModule()) {