agent-inspect 2.5.0 → 2.6.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.
@@ -2,7 +2,7 @@
2
2
  'use strict';
3
3
 
4
4
  var fs = require('fs');
5
- var path13 = require('path');
5
+ var path14 = require('path');
6
6
  var url = require('url');
7
7
  var commander = require('commander');
8
8
  var async_hooks = require('async_hooks');
@@ -12,18 +12,19 @@ var os = require('os');
12
12
  var process2 = require('process');
13
13
  var tty = require('tty');
14
14
  var readline = require('readline');
15
+ var http = require('http');
15
16
 
16
17
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
17
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
19
 
19
- var path13__default = /*#__PURE__*/_interopDefault(path13);
20
+ var path14__default = /*#__PURE__*/_interopDefault(path14);
20
21
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
21
22
  var os__default = /*#__PURE__*/_interopDefault(os);
22
23
  var process2__default = /*#__PURE__*/_interopDefault(process2);
23
24
  var tty__default = /*#__PURE__*/_interopDefault(tty);
24
25
 
25
26
  // package.json
26
- var version = "2.5.0";
27
+ var version = "2.6.0";
27
28
 
28
29
  // packages/core/src/correlation-metadata.ts
29
30
  var TRACE_CORRELATION_KEYS = [
@@ -755,7 +756,7 @@ function formatDuration(ms) {
755
756
  // packages/core/src/utils.ts
756
757
  var DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
757
758
  var RUNS_DIR_NAME = "runs";
758
- var FALLBACK_TRACE_DIR = path13__default.default.join(
759
+ var FALLBACK_TRACE_DIR = path14__default.default.join(
759
760
  os__default.default.tmpdir(),
760
761
  "agent-inspect",
761
762
  RUNS_DIR_NAME
@@ -790,7 +791,7 @@ function getDefaultTraceDir() {
790
791
  if (typeof home !== "string" || home.trim() === "") {
791
792
  return FALLBACK_TRACE_DIR;
792
793
  }
793
- return path13__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
794
+ return path14__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
794
795
  } catch {
795
796
  return FALLBACK_TRACE_DIR;
796
797
  }
@@ -798,11 +799,11 @@ function getDefaultTraceDir() {
798
799
  function getTraceFilePath(runId, traceDir) {
799
800
  const baseDir = traceDir ?? getDefaultTraceDir();
800
801
  let safeId = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
801
- safeId = path13__default.default.basename(safeId);
802
+ safeId = path14__default.default.basename(safeId);
802
803
  if (safeId === "" || safeId === "." || safeId === "..") {
803
804
  safeId = "run_unknown";
804
805
  }
805
- return path13__default.default.join(baseDir, `${safeId}.jsonl`);
806
+ return path14__default.default.join(baseDir, `${safeId}.jsonl`);
806
807
  }
807
808
  function formatError(error) {
808
809
  if (error instanceof Error) {
@@ -1564,7 +1565,7 @@ var TraceDirectory = class {
1564
1565
  this.#dir = resolveTraceDir(options);
1565
1566
  }
1566
1567
  getPath(filename) {
1567
- return filename ? path13__default.default.join(this.#dir, filename) : this.#dir;
1568
+ return filename ? path14__default.default.join(this.#dir, filename) : this.#dir;
1568
1569
  }
1569
1570
  async list() {
1570
1571
  try {
@@ -1591,7 +1592,7 @@ function parseIsoToMs2(value) {
1591
1592
  }
1592
1593
  async function extractMetadata(filePath, _quickScan) {
1593
1594
  const stats = await promises.stat(filePath);
1594
- let runIdFromFile = path13__default.default.basename(filePath);
1595
+ let runIdFromFile = path14__default.default.basename(filePath);
1595
1596
  if (runIdFromFile.endsWith(".jsonl")) {
1596
1597
  runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
1597
1598
  }
@@ -3069,12 +3070,12 @@ function buildCriticalPath(runs, handoffs) {
3069
3070
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
3070
3071
  );
3071
3072
  const ordered = [...runs].sort(compareRuns);
3072
- const path15 = [];
3073
+ const path16 = [];
3073
3074
  const visited = /* @__PURE__ */ new Set();
3074
3075
  const pushRun = (run, confidence, source) => {
3075
3076
  if (visited.has(run.runId)) return;
3076
3077
  visited.add(run.runId);
3077
- path15.push({
3078
+ path16.push({
3078
3079
  runId: run.runId,
3079
3080
  name: run.name,
3080
3081
  startedAt: run.startedAt,
@@ -3099,7 +3100,7 @@ function buildCriticalPath(runs, handoffs) {
3099
3100
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
3100
3101
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
3101
3102
  }
3102
- return path15;
3103
+ return path16;
3103
3104
  }
3104
3105
  function metaRunIdMatches(run, token, runById) {
3105
3106
  const meta = extractSessionWorkflowMetadata(run.metadata);
@@ -4037,7 +4038,7 @@ function findReaderByFormat(format, readers) {
4037
4038
  }
4038
4039
  async function jsonlFilesInDirectory(dirPath) {
4039
4040
  const entries = await promises.readdir(dirPath, { withFileTypes: true });
4040
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path13__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
4041
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path14__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
4041
4042
  }
4042
4043
  async function resolveInput(input3) {
4043
4044
  const cached = resolvedInputCache.get(input3);
@@ -8163,9 +8164,9 @@ Trace directory: ${traceDir}`);
8163
8164
  if (validation !== void 0 && !validation.ok) {
8164
8165
  process.exitCode = 1;
8165
8166
  }
8166
- const outPath = options.output !== void 0 && options.output.trim() !== "" ? path13__default.default.resolve(options.output.trim()) : void 0;
8167
+ const outPath = options.output !== void 0 && options.output.trim() !== "" ? path14__default.default.resolve(options.output.trim()) : void 0;
8167
8168
  if (outPath !== void 0) {
8168
- await promises.mkdir(path13__default.default.dirname(outPath), { recursive: true });
8169
+ await promises.mkdir(path14__default.default.dirname(outPath), { recursive: true });
8169
8170
  await promises.writeFile(outPath, result.content, "utf-8");
8170
8171
  const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
8171
8172
  console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
@@ -8342,13 +8343,13 @@ function pairSteps(left, right) {
8342
8343
  return pairs;
8343
8344
  }
8344
8345
  function compareLeafSteps(L, R, segments, opts, out) {
8345
- const path15 = buildPath(segments);
8346
+ const path16 = buildPath(segments);
8346
8347
  if (L.name !== R.name) {
8347
8348
  out.push({
8348
8349
  kind: "structure",
8349
8350
  severity: "warning",
8350
8351
  message: "Step name differs",
8351
- path: path15,
8352
+ path: path16,
8352
8353
  left: L.name,
8353
8354
  right: R.name
8354
8355
  });
@@ -8358,7 +8359,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8358
8359
  kind: "step-type",
8359
8360
  severity: "warning",
8360
8361
  message: "Step type differs",
8361
- path: path15,
8362
+ path: path16,
8362
8363
  left: L.type,
8363
8364
  right: R.type
8364
8365
  });
@@ -8368,7 +8369,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8368
8369
  kind: "step-status",
8369
8370
  severity: "warning",
8370
8371
  message: "Step status differs",
8371
- path: path15,
8372
+ path: path16,
8372
8373
  left: L.status,
8373
8374
  right: R.status
8374
8375
  });
@@ -8380,7 +8381,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8380
8381
  kind: "error",
8381
8382
  severity: "error",
8382
8383
  message: "Step error message differs",
8383
- path: path15,
8384
+ path: path16,
8384
8385
  left: le || void 0,
8385
8386
  right: re || void 0
8386
8387
  });
@@ -8398,7 +8399,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8398
8399
  kind: "duration",
8399
8400
  severity: "info",
8400
8401
  message: "Step duration differs",
8401
- path: path15,
8402
+ path: path16,
8402
8403
  left: ld,
8403
8404
  right: rd
8404
8405
  });
@@ -8411,7 +8412,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8411
8412
  kind: "metadata",
8412
8413
  severity: "info",
8413
8414
  message: "Step metadata differs",
8414
- path: path15,
8415
+ path: path16,
8415
8416
  left: L.metadata,
8416
8417
  right: R.metadata
8417
8418
  });
@@ -8423,7 +8424,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8423
8424
  kind: "output",
8424
8425
  severity: "info",
8425
8426
  message: "Output preview differs",
8426
- path: path15,
8427
+ path: path16,
8427
8428
  left: L.outputPreview,
8428
8429
  right: R.outputPreview
8429
8430
  });
@@ -8583,11 +8584,11 @@ function diffRuns(left, right, options) {
8583
8584
  }
8584
8585
 
8585
8586
  // packages/core/src/diff/renderer.ts
8586
- function formatPath(path15) {
8587
- if (path15 === void 0 || path15.path.length === 0) {
8587
+ function formatPath(path16) {
8588
+ if (path16 === void 0 || path16.path.length === 0) {
8588
8589
  return "(run)";
8589
8590
  }
8590
- return path15.path.map((s) => s.name).join(" > ");
8591
+ return path16.path.map((s) => s.name).join(" > ");
8591
8592
  }
8592
8593
  function formatValue(v, verbose) {
8593
8594
  if (v === void 0) return "(undefined)";
@@ -9216,9 +9217,9 @@ async function reportCommand(runId, options = {}) {
9216
9217
  redactionProfile,
9217
9218
  correlation: !options.noCorrelation
9218
9219
  });
9219
- const outPath = options.output !== void 0 && options.output.trim() !== "" ? path13__default.default.resolve(options.output.trim()) : void 0;
9220
+ const outPath = options.output !== void 0 && options.output.trim() !== "" ? path14__default.default.resolve(options.output.trim()) : void 0;
9220
9221
  if (outPath !== void 0) {
9221
- await promises.mkdir(path13__default.default.dirname(outPath), { recursive: true });
9222
+ await promises.mkdir(path14__default.default.dirname(outPath), { recursive: true });
9222
9223
  await promises.writeFile(outPath, result.content, "utf-8");
9223
9224
  console.log(`Wrote ${result.fileExtension} report to ${outPath}`);
9224
9225
  }
@@ -9465,17 +9466,17 @@ function applyRule(rule, value, replacement) {
9465
9466
  }
9466
9467
  return value;
9467
9468
  }
9468
- function childPath(path15, key) {
9469
+ function childPath(path16, key) {
9469
9470
  if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
9470
- return path15 ? `${path15}.${key}` : key;
9471
+ return path16 ? `${path16}.${key}` : key;
9471
9472
  }
9472
- return `${path15 || "$"}[${JSON.stringify(key)}]`;
9473
+ return `${path16 || "$"}[${JSON.stringify(key)}]`;
9473
9474
  }
9474
- function indexPath(path15, index) {
9475
- return `${path15 || "$"}[${index}]`;
9475
+ function indexPath(path16, index) {
9476
+ return `${path16 || "$"}[${index}]`;
9476
9477
  }
9477
- function makeFinding(path15, detector, action, matchKind, severity = "warning", preview) {
9478
- return preview === void 0 ? { path: path15, detector, action, severity, matchKind } : { path: path15, detector, action, severity, matchKind, preview };
9478
+ function makeFinding(path16, detector, action, matchKind, severity = "warning", preview) {
9479
+ return preview === void 0 ? { path: path16, detector, action, severity, matchKind } : { path: path16, detector, action, severity, matchKind, preview };
9479
9480
  }
9480
9481
  function createRedactionProfile(profile = "local") {
9481
9482
  switch (profile) {
@@ -9544,11 +9545,11 @@ var Redactor2 = class {
9544
9545
  #recordFinding(state, finding) {
9545
9546
  if (this.#collectFindings) state.findings.push(finding);
9546
9547
  }
9547
- #redactValue(value, key, path15, depth, state) {
9548
+ #redactValue(value, key, path16, depth, state) {
9548
9549
  if (depth > this.#maxDepth) {
9549
9550
  this.#recordFinding(
9550
9551
  state,
9551
- makeFinding(path15, "structure.maxDepth", "truncate", "value", "warning")
9552
+ makeFinding(path16, "structure.maxDepth", "truncate", "value", "warning")
9552
9553
  );
9553
9554
  return "[Truncated]";
9554
9555
  }
@@ -9557,19 +9558,19 @@ var Redactor2 = class {
9557
9558
  if (rule) {
9558
9559
  this.#recordFinding(
9559
9560
  state,
9560
- makeFinding(path15, `key.${rule.key}`, actionForRule(rule), "key", "warning")
9561
+ makeFinding(path16, `key.${rule.key}`, actionForRule(rule), "key", "warning")
9561
9562
  );
9562
9563
  return applyRule(rule, value, this.#replacement);
9563
9564
  }
9564
9565
  }
9565
9566
  for (const detector of this.#detectors) {
9566
- const detections = detector.detect({ path: path15, key, value });
9567
+ const detections = detector.detect({ path: path16, key, value });
9567
9568
  for (const detection of detections) {
9568
9569
  const action = detection.action ?? "replace";
9569
9570
  this.#recordFinding(
9570
9571
  state,
9571
9572
  makeFinding(
9572
- path15,
9573
+ path16,
9573
9574
  detector.id,
9574
9575
  action,
9575
9576
  detection.matchKind ?? detector.matchKind ?? "custom",
@@ -9587,7 +9588,7 @@ var Redactor2 = class {
9587
9588
  const out = [];
9588
9589
  state.seen.set(value, out);
9589
9590
  value.forEach((item, index) => {
9590
- out[index] = this.#redactValue(item, void 0, indexPath(path15, index), depth + 1, state);
9591
+ out[index] = this.#redactValue(item, void 0, indexPath(path16, index), depth + 1, state);
9591
9592
  });
9592
9593
  return out;
9593
9594
  }
@@ -9599,7 +9600,7 @@ var Redactor2 = class {
9599
9600
  out[entryKey] = this.#redactValue(
9600
9601
  entryValue,
9601
9602
  entryKey,
9602
- childPath(path15 === "$" ? "" : path15, entryKey),
9603
+ childPath(path16 === "$" ? "" : path16, entryKey),
9603
9604
  depth + 1,
9604
9605
  state
9605
9606
  );
@@ -10046,14 +10047,14 @@ function uniqueSorted(values) {
10046
10047
  return [...new Set(values)].sort();
10047
10048
  }
10048
10049
  function isWithinDirectory(child, parent) {
10049
- const relative = path13__default.default.relative(parent, child);
10050
- return relative === "" || !relative.startsWith("..") && !path13__default.default.isAbsolute(relative);
10050
+ const relative = path14__default.default.relative(parent, child);
10051
+ return relative === "" || !relative.startsWith("..") && !path14__default.default.isAbsolute(relative);
10051
10052
  }
10052
10053
  async function resolveOutputPath(inputPath, output2, force) {
10053
10054
  if (output2 === void 0 || output2.trim() === "") return void 0;
10054
- const inputAbs = path13__default.default.resolve(inputPath);
10055
- const outputAbs = path13__default.default.resolve(output2.trim());
10056
- const inputDir = path13__default.default.dirname(inputAbs);
10055
+ const inputAbs = path14__default.default.resolve(inputPath);
10056
+ const outputAbs = path14__default.default.resolve(output2.trim());
10057
+ const inputDir = path14__default.default.dirname(inputAbs);
10057
10058
  if (!isWithinDirectory(outputAbs, inputDir)) {
10058
10059
  throw new Error("Refusing to write migrated output outside the input directory.");
10059
10060
  }
@@ -10174,7 +10175,7 @@ async function migrateCommand(input3, options = {}) {
10174
10175
  process.exitCode = 1;
10175
10176
  return;
10176
10177
  }
10177
- const inputPath = path13__default.default.resolve(input3.trim());
10178
+ const inputPath = path14__default.default.resolve(input3.trim());
10178
10179
  const dryRun = options.dryRun === true;
10179
10180
  if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
10180
10181
  console.error("migrate requires --dry-run or --output <path>.");
@@ -10193,7 +10194,7 @@ async function migrateCommand(input3, options = {}) {
10193
10194
  );
10194
10195
  const result = await buildMigration(inputPath, outputPath);
10195
10196
  if (!dryRun && outputPath !== void 0) {
10196
- await promises.mkdir(path13__default.default.dirname(outputPath), { recursive: true });
10197
+ await promises.mkdir(path14__default.default.dirname(outputPath), { recursive: true });
10197
10198
  await promises.writeFile(outputPath, result.content, "utf-8");
10198
10199
  }
10199
10200
  printSummary2(result, dryRun);
@@ -10471,7 +10472,7 @@ function stripPrefix(name, prefixes) {
10471
10472
  }
10472
10473
  return name;
10473
10474
  }
10474
- function eventEvidence(event, path15) {
10475
+ function eventEvidence(event, path16) {
10475
10476
  return {
10476
10477
  runId: event.runId,
10477
10478
  eventId: event.eventId,
@@ -10481,7 +10482,7 @@ function eventEvidence(event, path15) {
10481
10482
  kind: event.kind,
10482
10483
  name: event.name,
10483
10484
  status: event.status,
10484
- ...path15 ? { path: path15 } : {}
10485
+ ...path16 ? { path: path16 } : {}
10485
10486
  };
10486
10487
  }
10487
10488
  function runEvidence(run) {
@@ -10544,9 +10545,9 @@ function eventEndMs(event) {
10544
10545
  function normalizedKey(value) {
10545
10546
  return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
10546
10547
  }
10547
- function lastPathSegment(path15) {
10548
- const parts = path15.split(".");
10549
- return parts[parts.length - 1] ?? path15;
10548
+ function lastPathSegment(path16) {
10549
+ const parts = path16.split(".");
10550
+ return parts[parts.length - 1] ?? path16;
10550
10551
  }
10551
10552
  function valueType(value) {
10552
10553
  if (Array.isArray(value)) return "array";
@@ -10560,12 +10561,12 @@ function serializedByteLength(value) {
10560
10561
  return void 0;
10561
10562
  }
10562
10563
  }
10563
- function pushValueEntries(entries, event, value, path15, key, depth = 0) {
10564
- entries.push({ event, path: path15, key, value });
10564
+ function pushValueEntries(entries, event, value, path16, key, depth = 0) {
10565
+ entries.push({ event, path: path16, key, value });
10565
10566
  if (depth >= 8) return;
10566
10567
  if (Array.isArray(value)) {
10567
10568
  for (const [index, item] of value.entries()) {
10568
- pushValueEntries(entries, event, item, `${path15}.${index}`, String(index), depth + 1);
10569
+ pushValueEntries(entries, event, item, `${path16}.${index}`, String(index), depth + 1);
10569
10570
  }
10570
10571
  return;
10571
10572
  }
@@ -10575,7 +10576,7 @@ function pushValueEntries(entries, event, value, path15, key, depth = 0) {
10575
10576
  entries,
10576
10577
  event,
10577
10578
  value[nestedKey],
10578
- `${path15}.${nestedKey}`,
10579
+ `${path16}.${nestedKey}`,
10579
10580
  nestedKey,
10580
10581
  depth + 1
10581
10582
  );
@@ -10656,9 +10657,9 @@ function eventDurationMs(event) {
10656
10657
  }
10657
10658
  function treeShape(nodes) {
10658
10659
  const lines = [];
10659
- const visit = (node, path15) => {
10660
- lines.push(`${path15}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
10661
- node.children.forEach((child, index) => visit(child, `${path15}.${index}`));
10660
+ const visit = (node, path16) => {
10661
+ lines.push(`${path16}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
10662
+ node.children.forEach((child, index) => visit(child, `${path16}.${index}`));
10662
10663
  };
10663
10664
  nodes.forEach((node, index) => visit(node, String(index)));
10664
10665
  return lines;
@@ -10707,9 +10708,9 @@ function retrievalShape(context) {
10707
10708
  function guardrailShape(context) {
10708
10709
  return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
10709
10710
  }
10710
- function firstEvidenceForKind(context, kind, path15) {
10711
+ function firstEvidenceForKind(context, kind, path16) {
10711
10712
  const event = context.events.find((candidate) => candidate.kind === kind);
10712
- return event ? [eventEvidence(event, path15)] : runEvidence(context.selectedRun);
10713
+ return event ? [eventEvidence(event, path16)] : runEvidence(context.selectedRun);
10713
10714
  }
10714
10715
  function baselineDiffFinding(message, evidence, expected, actual) {
10715
10716
  return failFinding("baseline.regression", message, evidence, expected, actual);
@@ -10957,13 +10958,13 @@ function createStructureCycleRule() {
10957
10958
  const seenCycles = /* @__PURE__ */ new Set();
10958
10959
  const findings = [];
10959
10960
  for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
10960
- const path15 = [];
10961
+ const path16 = [];
10961
10962
  const seenAt = /* @__PURE__ */ new Map();
10962
10963
  let current = event;
10963
10964
  while (current) {
10964
10965
  const existing = seenAt.get(current.eventId);
10965
10966
  if (existing !== void 0) {
10966
- const cycle = path15.slice(existing);
10967
+ const cycle = path16.slice(existing);
10967
10968
  const key = cycle.map((item) => item.eventId).sort().join("\0");
10968
10969
  if (!seenCycles.has(key)) {
10969
10970
  seenCycles.add(key);
@@ -10979,8 +10980,8 @@ function createStructureCycleRule() {
10979
10980
  }
10980
10981
  break;
10981
10982
  }
10982
- seenAt.set(current.eventId, path15.length);
10983
- path15.push(current);
10983
+ seenAt.set(current.eventId, path16.length);
10984
+ path16.push(current);
10984
10985
  current = current.parentId ? byId.get(current.parentId) : void 0;
10985
10986
  }
10986
10987
  }
@@ -11777,23 +11778,23 @@ function evaluatePromptInjection(text, options = {}) {
11777
11778
  }
11778
11779
  return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
11779
11780
  }
11780
- function validateSchemaField(value, field, path15, evidence) {
11781
+ function validateSchemaField(value, field, path16, evidence) {
11781
11782
  const ruleId = "guardrail.structured-output";
11782
11783
  if (field.type) {
11783
11784
  const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
11784
11785
  if (actual !== field.type) {
11785
- evidence.push({ ruleId, path: path15, preview: `expected ${field.type}, got ${actual}` });
11786
+ evidence.push({ ruleId, path: path16, preview: `expected ${field.type}, got ${actual}` });
11786
11787
  return;
11787
11788
  }
11788
11789
  }
11789
11790
  if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
11790
- evidence.push({ ruleId, path: path15, preview: "value not in enum" });
11791
+ evidence.push({ ruleId, path: path16, preview: "value not in enum" });
11791
11792
  }
11792
11793
  if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
11793
11794
  const record = value;
11794
11795
  for (const key of field.required) {
11795
11796
  if (!(key in record)) {
11796
- evidence.push({ ruleId, path: `${path15}.${key}`, preview: "missing required key" });
11797
+ evidence.push({ ruleId, path: `${path16}.${key}`, preview: "missing required key" });
11797
11798
  }
11798
11799
  }
11799
11800
  }
@@ -12120,7 +12121,7 @@ function asConfig(value) {
12120
12121
  }
12121
12122
  async function loadConfig(configPath) {
12122
12123
  if (configPath === void 0) return {};
12123
- const extension = path13__default.default.extname(configPath);
12124
+ const extension = path14__default.default.extname(configPath);
12124
12125
  if (TS_CONFIG_EXTENSIONS.has(extension)) {
12125
12126
  throw new Error(
12126
12127
  "TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
@@ -12129,7 +12130,7 @@ async function loadConfig(configPath) {
12129
12130
  if (!CONFIG_EXTENSIONS.has(extension)) {
12130
12131
  throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
12131
12132
  }
12132
- const absolute = path13__default.default.resolve(configPath);
12133
+ const absolute = path14__default.default.resolve(configPath);
12133
12134
  if (extension === ".json") {
12134
12135
  const raw = await promises.readFile(absolute, "utf-8");
12135
12136
  return asConfig(JSON.parse(raw));
@@ -12258,10 +12259,10 @@ function printHuman(result) {
12258
12259
  console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
12259
12260
  }
12260
12261
  for (const finding of result.findings) {
12261
- const path15 = finding.evidence[0]?.path;
12262
+ const path16 = finding.evidence[0]?.path;
12262
12263
  const run = finding.evidence[0]?.runId;
12263
12264
  const runPrefix = run ? `[${run}] ` : "";
12264
- console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path15 ? ` (${path15})` : ""}`);
12265
+ console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path16 ? ` (${path16})` : ""}`);
12265
12266
  }
12266
12267
  }
12267
12268
  function readErrorResult(error) {
@@ -12375,6 +12376,262 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
12375
12376
  else printHuman(result);
12376
12377
  }
12377
12378
 
12379
+ // packages/viewer/src/html.ts
12380
+ var viewerIndexHtml = `<!DOCTYPE html>
12381
+ <html lang="en">
12382
+ <head>
12383
+ <meta charset="utf-8" />
12384
+ <title>AgentInspect Viewer</title>
12385
+ <style>
12386
+ body { font-family: system-ui, sans-serif; margin: 1.5rem; line-height: 1.4; }
12387
+ h1 { font-size: 1.25rem; }
12388
+ pre { background: #f4f4f5; padding: 1rem; overflow: auto; max-height: 70vh; }
12389
+ a { color: #0b57d0; }
12390
+ .muted { color: #666; }
12391
+ </style>
12392
+ </head>
12393
+ <body>
12394
+ <h1>AgentInspect local viewer</h1>
12395
+ <p class="muted">Read-only. JSONL on disk remains canonical.</p>
12396
+ <p><a href="/api/health">/api/health</a> \xB7 <a href="/api/traces">/api/traces</a> \xB7 <a href="/api/sessions">/api/sessions</a></p>
12397
+ <pre id="out">Loading traces\u2026</pre>
12398
+ <script>
12399
+ fetch("/api/traces").then((r) => r.json()).then((data) => {
12400
+ document.getElementById("out").textContent = JSON.stringify(data, null, 2);
12401
+ }).catch((err) => {
12402
+ document.getElementById("out").textContent = String(err);
12403
+ });
12404
+ </script>
12405
+ </body>
12406
+ </html>
12407
+ `;
12408
+
12409
+ // packages/viewer/src/server.ts
12410
+ var DEFAULT_HOST = "127.0.0.1";
12411
+ var DEFAULT_PORT = 7337;
12412
+ var DEFAULT_MAX_EVENTS = 500;
12413
+ function sendJson(res, status, body) {
12414
+ const payload = JSON.stringify(body);
12415
+ res.writeHead(status, {
12416
+ "content-type": "application/json; charset=utf-8",
12417
+ "cache-control": "no-store"
12418
+ });
12419
+ res.end(payload);
12420
+ }
12421
+ function notFound(res, message) {
12422
+ sendJson(res, 404, { error: message });
12423
+ }
12424
+ function badRequest(res, message) {
12425
+ sendJson(res, 400, { error: message });
12426
+ }
12427
+ function decodeId(segment) {
12428
+ if (!segment) return "";
12429
+ try {
12430
+ return decodeURIComponent(segment);
12431
+ } catch {
12432
+ return segment;
12433
+ }
12434
+ }
12435
+ function boundedEvents(events, maxEvents) {
12436
+ if (events.length <= maxEvents) return [...events];
12437
+ return events.slice(0, maxEvents);
12438
+ }
12439
+ function createViewerServer(options = {}) {
12440
+ const traceDir = resolveTraceDir({ dir: options.traceDir });
12441
+ const host = options.host ?? DEFAULT_HOST;
12442
+ const port = options.port ?? DEFAULT_PORT;
12443
+ const maxEvents = options.maxEvents ?? DEFAULT_MAX_EVENTS;
12444
+ if (host === "0.0.0.0") {
12445
+ console.warn(
12446
+ "[AgentInspect viewer] Binding to 0.0.0.0 exposes traces on the network. Use 127.0.0.1 unless you accept that risk."
12447
+ );
12448
+ }
12449
+ const server = http.createServer(async (req, res) => {
12450
+ try {
12451
+ if (req.method !== "GET" && req.method !== "HEAD") {
12452
+ return badRequest(res, "Only GET is supported.");
12453
+ }
12454
+ const url = new URL(req.url ?? "/", `http://${host}:${port}`);
12455
+ const pathname = url.pathname;
12456
+ if (pathname === "/" || pathname === "/index.html") {
12457
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
12458
+ res.end(viewerIndexHtml);
12459
+ return;
12460
+ }
12461
+ if (pathname === "/api/health") {
12462
+ return sendJson(res, 200, {
12463
+ ok: true,
12464
+ readOnly: true,
12465
+ traceDir: path14__default.default.resolve(traceDir)
12466
+ });
12467
+ }
12468
+ const td = new TraceDirectory({ dir: traceDir });
12469
+ if (pathname === "/api/traces") {
12470
+ const files = await td.list();
12471
+ const metas = await loadTraceMetadataList(
12472
+ traceDir,
12473
+ files,
12474
+ (fileName) => td.getPath(fileName)
12475
+ );
12476
+ return sendJson(
12477
+ res,
12478
+ 200,
12479
+ metas.map((meta) => ({
12480
+ runId: meta.runId,
12481
+ name: meta.name,
12482
+ status: meta.status,
12483
+ file: path14__default.default.basename(meta.filePath),
12484
+ startedAt: meta.startedAt,
12485
+ durationMs: meta.durationMs
12486
+ }))
12487
+ );
12488
+ }
12489
+ if (pathname === "/api/sessions") {
12490
+ const files = await td.list();
12491
+ const metas = await loadTraceMetadataList(
12492
+ traceDir,
12493
+ files,
12494
+ (fileName) => td.getPath(fileName)
12495
+ );
12496
+ const runs = await loadSessionRunRecords(metas);
12497
+ const index = buildSessionIndex(runs, {
12498
+ correlateByGroupId: url.searchParams.get("correlateGroup") === "true"
12499
+ });
12500
+ return sendJson(res, 200, index);
12501
+ }
12502
+ const sessionMatch = pathname.match(/^\/api\/session\/([^/]+)$/);
12503
+ if (sessionMatch) {
12504
+ const sessionId = decodeId(sessionMatch[1]);
12505
+ const files = await td.list();
12506
+ const metas = await loadTraceMetadataList(
12507
+ traceDir,
12508
+ files,
12509
+ (fileName) => td.getPath(fileName)
12510
+ );
12511
+ const runs = await loadSessionRunRecords(metas);
12512
+ const index = buildSessionIndex(runs, {
12513
+ correlateByGroupId: url.searchParams.get("correlateGroup") === "true"
12514
+ });
12515
+ const session = index.sessions.find((item) => item.sessionId === sessionId);
12516
+ if (!session) return notFound(res, `Session not found: ${sessionId}`);
12517
+ return sendJson(res, 200, session);
12518
+ }
12519
+ const traceMatch = pathname.match(/^\/api\/trace\/([^/]+)$/);
12520
+ if (traceMatch) {
12521
+ const runId = decodeId(traceMatch[1]);
12522
+ const files = await td.list();
12523
+ const metas = await loadTraceMetadataList(
12524
+ traceDir,
12525
+ files,
12526
+ (fileName) => td.getPath(fileName)
12527
+ );
12528
+ const meta = metas.find((item) => item.runId === runId);
12529
+ if (!meta) return notFound(res, `Run not found: ${runId}`);
12530
+ const read = await openTrace({ type: "file", path: meta.filePath });
12531
+ const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
12532
+ return sendJson(res, 200, {
12533
+ runId,
12534
+ format: read.format,
12535
+ run,
12536
+ events: boundedEvents(read.events, maxEvents),
12537
+ warnings: read.warnings,
12538
+ truncated: read.events.length > maxEvents
12539
+ });
12540
+ }
12541
+ const timelineMatch = pathname.match(/^\/api\/trace\/([^/]+)\/timeline$/);
12542
+ if (timelineMatch) {
12543
+ const runId = decodeId(timelineMatch[1]);
12544
+ const files = await td.list();
12545
+ const metas = await loadTraceMetadataList(
12546
+ traceDir,
12547
+ files,
12548
+ (fileName) => td.getPath(fileName)
12549
+ );
12550
+ const meta = metas.find((item) => item.runId === runId);
12551
+ if (!meta) return notFound(res, `Run not found: ${runId}`);
12552
+ const read = await openTrace({ type: "file", path: meta.filePath });
12553
+ const legacyEvents = persistedInspectEventsToTraceEvents(
12554
+ boundedEvents(read.events, maxEvents)
12555
+ );
12556
+ const timeline = buildRunTimeline(legacyEvents, { focus: "all" });
12557
+ return sendJson(res, 200, { runId, timeline });
12558
+ }
12559
+ const checkMatch = pathname.match(/^\/api\/trace\/([^/]+)\/check$/);
12560
+ if (checkMatch) {
12561
+ const runId = decodeId(checkMatch[1]);
12562
+ const files = await td.list();
12563
+ const metas = await loadTraceMetadataList(
12564
+ traceDir,
12565
+ files,
12566
+ (fileName) => td.getPath(fileName)
12567
+ );
12568
+ const meta = metas.find((item) => item.runId === runId);
12569
+ if (!meta) return notFound(res, `Run not found: ${runId}`);
12570
+ const read = await openTrace({ type: "file", path: meta.filePath });
12571
+ const result = runTraceChecks(
12572
+ { read },
12573
+ { rules: [createRunStatusRule()], select: ["run.status"], runId }
12574
+ );
12575
+ return sendJson(res, 200, result);
12576
+ }
12577
+ return notFound(res, `Unknown route: ${pathname}`);
12578
+ } catch (error) {
12579
+ const message = error instanceof Error ? error.message : String(error);
12580
+ sendJson(res, 500, { error: message });
12581
+ }
12582
+ });
12583
+ return server;
12584
+ }
12585
+ function startViewerServer(options = {}) {
12586
+ const host = options.host ?? DEFAULT_HOST;
12587
+ const port = options.port ?? DEFAULT_PORT;
12588
+ const traceDir = resolveTraceDir({ dir: options.traceDir });
12589
+ const server = createViewerServer(options);
12590
+ return new Promise((resolve, reject) => {
12591
+ server.once("error", reject);
12592
+ server.listen(port, host, () => {
12593
+ const address = server.address();
12594
+ const resolvedPort = typeof address === "object" && address ? address.port : port;
12595
+ resolve({
12596
+ host,
12597
+ port: resolvedPort,
12598
+ traceDir: path14__default.default.resolve(traceDir),
12599
+ url: `http://${host}:${resolvedPort}`
12600
+ });
12601
+ });
12602
+ });
12603
+ }
12604
+
12605
+ // packages/cli/src/serve.ts
12606
+ function parsePort(value) {
12607
+ if (value === void 0) return void 0;
12608
+ const parsed = Number(value);
12609
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
12610
+ throw new Error("--port must be an integer between 1 and 65535.");
12611
+ }
12612
+ return parsed;
12613
+ }
12614
+ async function serveCommand(options = {}) {
12615
+ const port = parsePort(options.port);
12616
+ const host = options.host?.trim() || "127.0.0.1";
12617
+ const info = await startViewerServer({
12618
+ ...options.dir !== void 0 ? { traceDir: options.dir } : {},
12619
+ host,
12620
+ ...port !== void 0 ? { port } : {}
12621
+ });
12622
+ console.log(`AgentInspect viewer (read-only): ${info.url}`);
12623
+ console.log(`Trace directory: ${info.traceDir}`);
12624
+ if (options.open === true && host !== "127.0.0.1" && host !== "localhost") {
12625
+ console.warn("Skipping browser open for non-local host binding.");
12626
+ } else if (options.open === true) {
12627
+ const openMod = await import('child_process');
12628
+ openMod.exec(`open ${info.url}`, () => {
12629
+ });
12630
+ }
12631
+ await new Promise(() => {
12632
+ });
12633
+ }
12634
+
12378
12635
  // packages/eval/src/index.ts
12379
12636
  function isTraceReadResult(value) {
12380
12637
  return value !== null && typeof value === "object" && "runs" in value && "events" in value && "format" in value;
@@ -12543,10 +12800,10 @@ async function evalRun(input3, options = {}) {
12543
12800
  diagnostics: []
12544
12801
  };
12545
12802
  }
12546
- function evidenceForRun(run, path15) {
12547
- return [{ runId: run.runId, ...path15 !== void 0 ? { path: path15 } : {} }];
12803
+ function evidenceForRun(run, path16) {
12804
+ return [{ runId: run.runId, ...path16 !== void 0 ? { path: path16 } : {} }];
12548
12805
  }
12549
- function evidenceForEvent(event, path15) {
12806
+ function evidenceForEvent(event, path16) {
12550
12807
  return [
12551
12808
  {
12552
12809
  runId: event.runId,
@@ -12554,7 +12811,7 @@ function evidenceForEvent(event, path15) {
12554
12811
  ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
12555
12812
  kind: event.kind,
12556
12813
  name: event.name,
12557
- ...path15 !== void 0 ? { path: path15 } : {}
12814
+ ...path16 !== void 0 ? { path: path16 } : {}
12558
12815
  }
12559
12816
  ];
12560
12817
  }
@@ -12712,9 +12969,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
12712
12969
  function tokenize(text) {
12713
12970
  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));
12714
12971
  }
12715
- function firstEvidence(fields, run, path15) {
12972
+ function firstEvidence(fields, run, path16) {
12716
12973
  const first = fields[0];
12717
- return first === void 0 ? evidenceForRun(run, path15) : evidenceForEvent(first.node.event, first.path);
12974
+ return first === void 0 ? evidenceForRun(run, path16) : evidenceForEvent(first.node.event, first.path);
12718
12975
  }
12719
12976
  function collectSourceIds(nodes, keys) {
12720
12977
  const wanted = keySet(keys);
@@ -13091,8 +13348,8 @@ function renderEvalMarkdown(result) {
13091
13348
  if (result.findings.length > 0) {
13092
13349
  lines.push("", "## Findings");
13093
13350
  for (const finding of result.findings) {
13094
- const path15 = finding.evidence[0]?.path;
13095
- lines.push(`- ${finding.ruleId}: ${finding.message}${path15 ? ` (${path15})` : ""}`);
13351
+ const path16 = finding.evidence[0]?.path;
13352
+ lines.push(`- ${finding.ruleId}: ${finding.message}${path16 ? ` (${path16})` : ""}`);
13096
13353
  }
13097
13354
  }
13098
13355
  return `${lines.join("\n")}
@@ -13139,7 +13396,7 @@ function asConfig2(value) {
13139
13396
  }
13140
13397
  async function loadConfig2(configPath) {
13141
13398
  if (configPath === void 0) return {};
13142
- const extension = path13__default.default.extname(configPath);
13399
+ const extension = path14__default.default.extname(configPath);
13143
13400
  if (TS_CONFIG_EXTENSIONS2.has(extension)) {
13144
13401
  throw new Error(
13145
13402
  "TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
@@ -13148,7 +13405,7 @@ async function loadConfig2(configPath) {
13148
13405
  if (!CONFIG_EXTENSIONS2.has(extension)) {
13149
13406
  throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
13150
13407
  }
13151
- const absolute = path13__default.default.resolve(configPath);
13408
+ const absolute = path14__default.default.resolve(configPath);
13152
13409
  if (extension === ".json") {
13153
13410
  const raw = await promises.readFile(absolute, "utf-8");
13154
13411
  return asConfig2(JSON.parse(raw));
@@ -13285,8 +13542,8 @@ function printHuman2(result) {
13285
13542
  console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
13286
13543
  }
13287
13544
  for (const finding of result.findings) {
13288
- const path15 = finding.evidence[0]?.path;
13289
- console.log(`- ${finding.ruleId}: ${finding.message}${path15 ? ` (${path15})` : ""}`);
13545
+ const path16 = finding.evidence[0]?.path;
13546
+ console.log(`- ${finding.ruleId}: ${finding.message}${path16 ? ` (${path16})` : ""}`);
13290
13547
  }
13291
13548
  }
13292
13549
  function readErrorResult2(error) {
@@ -13524,8 +13781,8 @@ function printHuman3(result) {
13524
13781
  console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
13525
13782
  }
13526
13783
  for (const finding of result.findings) {
13527
- const path15 = finding.evidence[0]?.path;
13528
- console.log(`- ${finding.ruleId}: ${finding.message}${path15 ? ` (${path15})` : ""}`);
13784
+ const path16 = finding.evidence[0]?.path;
13785
+ console.log(`- ${finding.ruleId}: ${finding.message}${path16 ? ` (${path16})` : ""}`);
13529
13786
  }
13530
13787
  console.log(`Note: ${result.note}`);
13531
13788
  }
@@ -13644,8 +13901,8 @@ function renderCheckSection(result) {
13644
13901
  `Diagnostics: ${result.diagnostics.length}`
13645
13902
  ];
13646
13903
  for (const finding of result.findings.slice(0, 10)) {
13647
- const path15 = finding.evidence[0]?.path ?? "(run)";
13648
- lines.push(`- ${finding.ruleId}: ${finding.message} (${path15})`);
13904
+ const path16 = finding.evidence[0]?.path ?? "(run)";
13905
+ lines.push(`- ${finding.ruleId}: ${finding.message} (${path16})`);
13649
13906
  }
13650
13907
  for (const diagnostic4 of result.diagnostics.slice(0, 10)) {
13651
13908
  lines.push(`- ${diagnostic4.code}: ${diagnostic4.message}`);
@@ -13723,8 +13980,8 @@ function renderHtml(trace, check, diff) {
13723
13980
  `;
13724
13981
  }
13725
13982
  async function writeArtifact(outputDir, relativePath, content, files) {
13726
- const outPath = path13__default.default.join(outputDir, relativePath);
13727
- await promises.mkdir(path13__default.default.dirname(outPath), { recursive: true });
13983
+ const outPath = path14__default.default.join(outputDir, relativePath);
13984
+ await promises.mkdir(path14__default.default.dirname(outPath), { recursive: true });
13728
13985
  await promises.writeFile(outPath, content, "utf-8");
13729
13986
  files.push(relativePath);
13730
13987
  }
@@ -13740,7 +13997,7 @@ function manifestStatus(check, diff) {
13740
13997
  return "ok";
13741
13998
  }
13742
13999
  async function artifactsCommand(target, options = {}, stdin = process.stdin) {
13743
- const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path13__default.default.resolve(options.outputDir.trim()) : "";
14000
+ const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path14__default.default.resolve(options.outputDir.trim()) : "";
13744
14001
  if (outputDir === "") {
13745
14002
  console.error("--output-dir is required.");
13746
14003
  process.exitCode = 1;
@@ -13806,8 +14063,8 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
13806
14063
  await writeArtifact(outputDir, "report.html", renderHtml(trace, check, diff), files);
13807
14064
  const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
13808
14065
  if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
13809
- await promises.mkdir(path13__default.default.dirname(path13__default.default.resolve(summaryTarget)), { recursive: true });
13810
- await promises.appendFile(path13__default.default.resolve(summaryTarget), `
14066
+ await promises.mkdir(path14__default.default.dirname(path14__default.default.resolve(summaryTarget)), { recursive: true });
14067
+ await promises.appendFile(path14__default.default.resolve(summaryTarget), `
13811
14068
  ${renderMarkdown(trace, check, diff)}`, "utf-8");
13812
14069
  }
13813
14070
  const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
@@ -13826,10 +14083,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
13826
14083
  findings: diff?.findings.length ?? 0,
13827
14084
  diagnostics: diff?.diagnostics.length ?? 0
13828
14085
  },
13829
- ...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path13__default.default.resolve(summaryTarget) } : {},
14086
+ ...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path14__default.default.resolve(summaryTarget) } : {},
13830
14087
  note: NOTE
13831
14088
  };
13832
- await promises.writeFile(path13__default.default.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
14089
+ await promises.writeFile(path14__default.default.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
13833
14090
  if (options.json === true) {
13834
14091
  console.log(writeJson3(manifest).trimEnd());
13835
14092
  } else {
@@ -13841,7 +14098,7 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
13841
14098
  }
13842
14099
  }
13843
14100
  function validateReporterArtifactPath(options) {
13844
- const outputDir = path13__default.default.resolve(options.outputDir);
14101
+ const outputDir = path14__default.default.resolve(options.outputDir);
13845
14102
  const diagnostics = [];
13846
14103
  const rawPath = options.relativePath;
13847
14104
  if (rawPath.length === 0) {
@@ -13861,7 +14118,7 @@ function validateReporterArtifactPath(options) {
13861
14118
  });
13862
14119
  return { ok: false, outputDir, diagnostics };
13863
14120
  }
13864
- if (path13__default.default.isAbsolute(rawPath) || path13__default.default.win32.isAbsolute(rawPath)) {
14121
+ if (path14__default.default.isAbsolute(rawPath) || path14__default.default.win32.isAbsolute(rawPath)) {
13865
14122
  diagnostics.push({
13866
14123
  code: "artifact_path_absolute",
13867
14124
  severity: "error",
@@ -13870,7 +14127,7 @@ function validateReporterArtifactPath(options) {
13870
14127
  });
13871
14128
  return { ok: false, outputDir, diagnostics };
13872
14129
  }
13873
- const normalized = path13__default.default.posix.normalize(rawPath.replace(/\\/g, "/"));
14130
+ const normalized = path14__default.default.posix.normalize(rawPath.replace(/\\/g, "/"));
13874
14131
  const segments = normalized.split("/");
13875
14132
  if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
13876
14133
  diagnostics.push({
@@ -13881,9 +14138,9 @@ function validateReporterArtifactPath(options) {
13881
14138
  });
13882
14139
  return { ok: false, outputDir, diagnostics };
13883
14140
  }
13884
- const absolutePath = path13__default.default.resolve(outputDir, normalized);
13885
- const relFromOutput = path13__default.default.relative(outputDir, absolutePath);
13886
- if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path13__default.default.isAbsolute(relFromOutput)) {
14141
+ const absolutePath = path14__default.default.resolve(outputDir, normalized);
14142
+ const relFromOutput = path14__default.default.relative(outputDir, absolutePath);
14143
+ if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path14__default.default.isAbsolute(relFromOutput)) {
13887
14144
  diagnostics.push({
13888
14145
  code: "artifact_path_escape",
13889
14146
  severity: "error",
@@ -14029,23 +14286,23 @@ function readManifestDocument(value) {
14029
14286
  };
14030
14287
  }
14031
14288
  function cwdRelative(filePath) {
14032
- const relative = path13__default.default.relative(process.cwd(), path13__default.default.resolve(filePath)).replace(/\\/g, "/");
14033
- if (relative === "" || relative.startsWith("../") || path13__default.default.isAbsolute(relative)) {
14034
- return path13__default.default.basename(filePath);
14289
+ const relative = path14__default.default.relative(process.cwd(), path14__default.default.resolve(filePath)).replace(/\\/g, "/");
14290
+ if (relative === "" || relative.startsWith("../") || path14__default.default.isAbsolute(relative)) {
14291
+ return path14__default.default.basename(filePath);
14035
14292
  }
14036
14293
  return relative;
14037
14294
  }
14038
14295
  async function readReporterManifest(filePath) {
14039
- const absolute = path13__default.default.resolve(filePath);
14296
+ const absolute = path14__default.default.resolve(filePath);
14040
14297
  const raw = await promises.readFile(absolute, "utf-8");
14041
14298
  const document = readManifestDocument(JSON.parse(raw));
14042
14299
  const manifest = document.manifest;
14043
14300
  const results = manifest.results.map((result) => ({
14044
14301
  testId: safeText(result.testId),
14045
14302
  name: safeText(result.name),
14046
- ...result.file === void 0 ? {} : { file: safeText(path13__default.default.basename(result.file)) },
14303
+ ...result.file === void 0 ? {} : { file: safeText(path14__default.default.basename(result.file)) },
14047
14304
  status: result.status,
14048
- ...result.tracePath === void 0 ? {} : { tracePath: safeText(path13__default.default.basename(result.tracePath)) },
14305
+ ...result.tracePath === void 0 ? {} : { tracePath: safeText(path14__default.default.basename(result.tracePath)) },
14049
14306
  artifacts: result.artifacts,
14050
14307
  diagnostics: result.diagnostics
14051
14308
  }));
@@ -14184,15 +14441,15 @@ async function ciSummaryCommand(manifestPaths, options = {}) {
14184
14441
  return;
14185
14442
  }
14186
14443
  const markdown = renderMarkdown2(result);
14187
- const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path13__default.default.resolve(options.output.trim()) : void 0;
14444
+ const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path14__default.default.resolve(options.output.trim()) : void 0;
14188
14445
  if (outputPath !== void 0) {
14189
- await promises.mkdir(path13__default.default.dirname(outputPath), { recursive: true });
14446
+ await promises.mkdir(path14__default.default.dirname(outputPath), { recursive: true });
14190
14447
  await promises.writeFile(outputPath, markdown, "utf-8");
14191
14448
  }
14192
14449
  const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
14193
14450
  if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
14194
- const summaryPath = path13__default.default.resolve(summaryTarget);
14195
- await promises.mkdir(path13__default.default.dirname(summaryPath), { recursive: true });
14451
+ const summaryPath = path14__default.default.resolve(summaryTarget);
14452
+ await promises.mkdir(path14__default.default.dirname(summaryPath), { recursive: true });
14196
14453
  await promises.appendFile(summaryPath, `
14197
14454
  ${markdown}`, "utf-8");
14198
14455
  }
@@ -14344,6 +14601,9 @@ function createCliProgram() {
14344
14601
  ).action((target, opts) => {
14345
14602
  runCommand(() => checkCommand(target, opts));
14346
14603
  });
14604
+ program.command("serve").description("Start optional localhost read-only trace viewer").option("--dir <path>", "trace directory to serve").option("--host <host>", "bind host (default 127.0.0.1)", "127.0.0.1").option("--port <number>", "bind port (default 7337)", "7337").option("--open", "open browser locally when host is localhost").action((opts) => {
14605
+ runCommand(() => serveCommand(opts));
14606
+ });
14347
14607
  program.command("eval").description("Run deterministic local evals against a trace").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").option("--dir <path>", "trace directory for run-id lookup").addOption(
14348
14608
  new commander.Option("--format <format>", "trace input format").choices([
14349
14609
  "agent-inspect-jsonl",
@@ -14508,9 +14768,9 @@ function isPrimaryModule() {
14508
14768
  if (!entry) return false;
14509
14769
  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)));
14510
14770
  try {
14511
- return fs.realpathSync(path13__default.default.resolve(entry)) === fs.realpathSync(path13__default.default.resolve(selfPath));
14771
+ return fs.realpathSync(path14__default.default.resolve(entry)) === fs.realpathSync(path14__default.default.resolve(selfPath));
14512
14772
  } catch {
14513
- return path13__default.default.resolve(entry) === path13__default.default.resolve(selfPath);
14773
+ return path14__default.default.resolve(entry) === path14__default.default.resolve(selfPath);
14514
14774
  }
14515
14775
  }
14516
14776
  if (isPrimaryModule()) {