agent-inspect 3.5.4 → 4.0.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.
@@ -25,7 +25,7 @@ var process3__default = /*#__PURE__*/_interopDefault(process3);
25
25
  var tty__default = /*#__PURE__*/_interopDefault(tty);
26
26
 
27
27
  // package.json
28
- var version = "3.5.4";
28
+ var version = "4.0.0";
29
29
 
30
30
  // packages/core/src/correlation-metadata.ts
31
31
  var TRACE_CORRELATION_KEYS = [
@@ -3071,12 +3071,12 @@ function buildCriticalPath(runs, handoffs) {
3071
3071
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
3072
3072
  );
3073
3073
  const ordered = [...runs].sort(compareRuns);
3074
- const path19 = [];
3074
+ const path20 = [];
3075
3075
  const visited = /* @__PURE__ */ new Set();
3076
3076
  const pushRun = (run, confidence, source) => {
3077
3077
  if (visited.has(run.runId)) return;
3078
3078
  visited.add(run.runId);
3079
- path19.push({
3079
+ path20.push({
3080
3080
  runId: run.runId,
3081
3081
  name: run.name,
3082
3082
  startedAt: run.startedAt,
@@ -3101,7 +3101,7 @@ function buildCriticalPath(runs, handoffs) {
3101
3101
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
3102
3102
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
3103
3103
  }
3104
- return path19;
3104
+ return path20;
3105
3105
  }
3106
3106
  function metaRunIdMatches(run, token, runById) {
3107
3107
  const meta = extractSessionWorkflowMetadata(run.metadata);
@@ -3337,8 +3337,7 @@ async function list(options = {}) {
3337
3337
  const filtered = filterTraces(metas, {
3338
3338
  status: options.status,
3339
3339
  name: options.name,
3340
- since: options.since,
3341
- limit
3340
+ since: options.since
3342
3341
  });
3343
3342
  const shown = filtered.slice(0, limit);
3344
3343
  if (options.json) {
@@ -8112,7 +8111,10 @@ function buildRunReport(events, options) {
8112
8111
  '<section class="tree">',
8113
8112
  "</section>"
8114
8113
  );
8115
- const errorsSection = treeHtml.content.includes("<h2>Errors</h2>") ? extractHtmlFragment(treeHtml.content, "<h2>Errors</h2>", "<footer>") : "";
8114
+ const errorsSectionEnd = treeHtml.content.includes(
8115
+ "<h2>Attributes (bounded)</h2>"
8116
+ ) ? "<h2>Attributes (bounded)</h2>" : "<footer>";
8117
+ const errorsSection = treeHtml.content.includes("<h2>Errors</h2>") ? extractHtmlFragment(treeHtml.content, "<h2>Errors</h2>", errorsSectionEnd) : "";
8116
8118
  const attrsSection = treeHtml.content.includes("<h2>Attributes (bounded)</h2>") ? extractHtmlFragment(
8117
8119
  treeHtml.content,
8118
8120
  "<h2>Attributes (bounded)</h2>",
@@ -8402,13 +8404,13 @@ function pairSteps(left, right) {
8402
8404
  return pairs;
8403
8405
  }
8404
8406
  function compareLeafSteps(L, R, segments, opts, out) {
8405
- const path19 = buildPath(segments);
8407
+ const path20 = buildPath(segments);
8406
8408
  if (L.name !== R.name) {
8407
8409
  out.push({
8408
8410
  kind: "structure",
8409
8411
  severity: "warning",
8410
8412
  message: "Step name differs",
8411
- path: path19,
8413
+ path: path20,
8412
8414
  left: L.name,
8413
8415
  right: R.name
8414
8416
  });
@@ -8418,7 +8420,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8418
8420
  kind: "step-type",
8419
8421
  severity: "warning",
8420
8422
  message: "Step type differs",
8421
- path: path19,
8423
+ path: path20,
8422
8424
  left: L.type,
8423
8425
  right: R.type
8424
8426
  });
@@ -8428,7 +8430,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8428
8430
  kind: "step-status",
8429
8431
  severity: "warning",
8430
8432
  message: "Step status differs",
8431
- path: path19,
8433
+ path: path20,
8432
8434
  left: L.status,
8433
8435
  right: R.status
8434
8436
  });
@@ -8440,7 +8442,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8440
8442
  kind: "error",
8441
8443
  severity: "error",
8442
8444
  message: "Step error message differs",
8443
- path: path19,
8445
+ path: path20,
8444
8446
  left: le || void 0,
8445
8447
  right: re || void 0
8446
8448
  });
@@ -8458,20 +8460,20 @@ function compareLeafSteps(L, R, segments, opts, out) {
8458
8460
  kind: "duration",
8459
8461
  severity: "info",
8460
8462
  message: "Step duration differs",
8461
- path: path19,
8463
+ path: path20,
8462
8464
  left: ld,
8463
8465
  right: rd
8464
8466
  });
8465
8467
  }
8466
8468
  }
8467
8469
  const lm = stableJson(L.metadata ?? {});
8468
- const rm2 = stableJson(R.metadata ?? {});
8469
- if (lm !== rm2) {
8470
+ const rm3 = stableJson(R.metadata ?? {});
8471
+ if (lm !== rm3) {
8470
8472
  out.push({
8471
8473
  kind: "metadata",
8472
8474
  severity: "info",
8473
8475
  message: "Step metadata differs",
8474
- path: path19,
8476
+ path: path20,
8475
8477
  left: L.metadata,
8476
8478
  right: R.metadata
8477
8479
  });
@@ -8483,7 +8485,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
8483
8485
  kind: "output",
8484
8486
  severity: "info",
8485
8487
  message: "Output preview differs",
8486
- path: path19,
8488
+ path: path20,
8487
8489
  left: L.outputPreview,
8488
8490
  right: R.outputPreview
8489
8491
  });
@@ -8643,11 +8645,11 @@ function diffRuns(left, right, options) {
8643
8645
  }
8644
8646
 
8645
8647
  // packages/core/src/diff/renderer.ts
8646
- function formatPath(path19) {
8647
- if (path19 === void 0 || path19.path.length === 0) {
8648
+ function formatPath(path20) {
8649
+ if (path20 === void 0 || path20.path.length === 0) {
8648
8650
  return "(run)";
8649
8651
  }
8650
- return path19.path.map((s) => s.name).join(" > ");
8652
+ return path20.path.map((s) => s.name).join(" > ");
8651
8653
  }
8652
8654
  function formatValue(v, verbose) {
8653
8655
  if (v === void 0) return "(undefined)";
@@ -9529,17 +9531,17 @@ function applyRule(rule, value, replacement) {
9529
9531
  }
9530
9532
  return value;
9531
9533
  }
9532
- function childPath(path19, key) {
9534
+ function childPath(path20, key) {
9533
9535
  if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
9534
- return path19 ? `${path19}.${key}` : key;
9536
+ return path20 ? `${path20}.${key}` : key;
9535
9537
  }
9536
- return `${path19 || "$"}[${JSON.stringify(key)}]`;
9538
+ return `${path20 || "$"}[${JSON.stringify(key)}]`;
9537
9539
  }
9538
- function indexPath(path19, index) {
9539
- return `${path19 || "$"}[${index}]`;
9540
+ function indexPath(path20, index) {
9541
+ return `${path20 || "$"}[${index}]`;
9540
9542
  }
9541
- function makeFinding(path19, detector, action, matchKind, severity = "warning", preview) {
9542
- return preview === void 0 ? { path: path19, detector, action, severity, matchKind } : { path: path19, detector, action, severity, matchKind, preview };
9543
+ function makeFinding(path20, detector, action, matchKind, severity = "warning", preview) {
9544
+ return preview === void 0 ? { path: path20, detector, action, severity, matchKind } : { path: path20, detector, action, severity, matchKind, preview };
9543
9545
  }
9544
9546
  function createRedactionProfile(profile = "local") {
9545
9547
  switch (profile) {
@@ -9608,11 +9610,11 @@ var Redactor2 = class {
9608
9610
  #recordFinding(state, finding) {
9609
9611
  if (this.#collectFindings) state.findings.push(finding);
9610
9612
  }
9611
- #redactValue(value, key, path19, depth, state) {
9613
+ #redactValue(value, key, path20, depth, state) {
9612
9614
  if (depth > this.#maxDepth) {
9613
9615
  this.#recordFinding(
9614
9616
  state,
9615
- makeFinding(path19, "structure.maxDepth", "truncate", "value", "warning")
9617
+ makeFinding(path20, "structure.maxDepth", "truncate", "value", "warning")
9616
9618
  );
9617
9619
  return "[Truncated]";
9618
9620
  }
@@ -9621,19 +9623,19 @@ var Redactor2 = class {
9621
9623
  if (rule) {
9622
9624
  this.#recordFinding(
9623
9625
  state,
9624
- makeFinding(path19, `key.${rule.key}`, actionForRule(rule), "key", "warning")
9626
+ makeFinding(path20, `key.${rule.key}`, actionForRule(rule), "key", "warning")
9625
9627
  );
9626
9628
  return applyRule(rule, value, this.#replacement);
9627
9629
  }
9628
9630
  }
9629
9631
  for (const detector of this.#detectors) {
9630
- const detections = detector.detect({ path: path19, key, value });
9632
+ const detections = detector.detect({ path: path20, key, value });
9631
9633
  for (const detection of detections) {
9632
9634
  const action = detection.action ?? "replace";
9633
9635
  this.#recordFinding(
9634
9636
  state,
9635
9637
  makeFinding(
9636
- path19,
9638
+ path20,
9637
9639
  detector.id,
9638
9640
  action,
9639
9641
  detection.matchKind ?? detector.matchKind ?? "custom",
@@ -9651,7 +9653,7 @@ var Redactor2 = class {
9651
9653
  const out = [];
9652
9654
  state.seen.set(value, out);
9653
9655
  value.forEach((item, index) => {
9654
- out[index] = this.#redactValue(item, void 0, indexPath(path19, index), depth + 1, state);
9656
+ out[index] = this.#redactValue(item, void 0, indexPath(path20, index), depth + 1, state);
9655
9657
  });
9656
9658
  return out;
9657
9659
  }
@@ -9663,7 +9665,7 @@ var Redactor2 = class {
9663
9665
  out[entryKey] = this.#redactValue(
9664
9666
  entryValue,
9665
9667
  entryKey,
9666
- childPath(path19 === "$" ? "" : path19, entryKey),
9668
+ childPath(path20 === "$" ? "" : path20, entryKey),
9667
9669
  depth + 1,
9668
9670
  state
9669
9671
  );
@@ -10535,7 +10537,7 @@ function stripPrefix(name, prefixes) {
10535
10537
  }
10536
10538
  return name;
10537
10539
  }
10538
- function eventEvidence(event, path19) {
10540
+ function eventEvidence(event, path20) {
10539
10541
  return {
10540
10542
  runId: event.runId,
10541
10543
  eventId: event.eventId,
@@ -10545,7 +10547,7 @@ function eventEvidence(event, path19) {
10545
10547
  kind: event.kind,
10546
10548
  name: event.name,
10547
10549
  status: event.status,
10548
- ...path19 ? { path: path19 } : {}
10550
+ ...path20 ? { path: path20 } : {}
10549
10551
  };
10550
10552
  }
10551
10553
  function runEvidence(run) {
@@ -10608,9 +10610,9 @@ function eventEndMs(event) {
10608
10610
  function normalizedKey(value) {
10609
10611
  return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
10610
10612
  }
10611
- function lastPathSegment(path19) {
10612
- const parts = path19.split(".");
10613
- return parts[parts.length - 1] ?? path19;
10613
+ function lastPathSegment(path20) {
10614
+ const parts = path20.split(".");
10615
+ return parts[parts.length - 1] ?? path20;
10614
10616
  }
10615
10617
  function valueType(value) {
10616
10618
  if (Array.isArray(value)) return "array";
@@ -10624,12 +10626,12 @@ function serializedByteLength(value) {
10624
10626
  return void 0;
10625
10627
  }
10626
10628
  }
10627
- function pushValueEntries(entries, event, value, path19, key, depth = 0) {
10628
- entries.push({ event, path: path19, key, value });
10629
+ function pushValueEntries(entries, event, value, path20, key, depth = 0) {
10630
+ entries.push({ event, path: path20, key, value });
10629
10631
  if (depth >= 8) return;
10630
10632
  if (Array.isArray(value)) {
10631
10633
  for (const [index, item] of value.entries()) {
10632
- pushValueEntries(entries, event, item, `${path19}.${index}`, String(index), depth + 1);
10634
+ pushValueEntries(entries, event, item, `${path20}.${index}`, String(index), depth + 1);
10633
10635
  }
10634
10636
  return;
10635
10637
  }
@@ -10639,7 +10641,7 @@ function pushValueEntries(entries, event, value, path19, key, depth = 0) {
10639
10641
  entries,
10640
10642
  event,
10641
10643
  value[nestedKey],
10642
- `${path19}.${nestedKey}`,
10644
+ `${path20}.${nestedKey}`,
10643
10645
  nestedKey,
10644
10646
  depth + 1
10645
10647
  );
@@ -10720,9 +10722,9 @@ function eventDurationMs(event) {
10720
10722
  }
10721
10723
  function treeShape(nodes) {
10722
10724
  const lines = [];
10723
- const visit = (node, path19) => {
10724
- lines.push(`${path19}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
10725
- node.children.forEach((child, index) => visit(child, `${path19}.${index}`));
10725
+ const visit = (node, path20) => {
10726
+ lines.push(`${path20}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
10727
+ node.children.forEach((child, index) => visit(child, `${path20}.${index}`));
10726
10728
  };
10727
10729
  nodes.forEach((node, index) => visit(node, String(index)));
10728
10730
  return lines;
@@ -10771,9 +10773,9 @@ function retrievalShape(context) {
10771
10773
  function guardrailShape(context) {
10772
10774
  return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
10773
10775
  }
10774
- function firstEvidenceForKind(context, kind, path19) {
10776
+ function firstEvidenceForKind(context, kind, path20) {
10775
10777
  const event = context.events.find((candidate) => candidate.kind === kind);
10776
- return event ? [eventEvidence(event, path19)] : runEvidence(context.selectedRun);
10778
+ return event ? [eventEvidence(event, path20)] : runEvidence(context.selectedRun);
10777
10779
  }
10778
10780
  function baselineDiffFinding(message, evidence, expected, actual) {
10779
10781
  return failFinding("baseline.regression", message, evidence, expected, actual);
@@ -11123,13 +11125,13 @@ function createStructureCycleRule() {
11123
11125
  const seenCycles = /* @__PURE__ */ new Set();
11124
11126
  const findings = [];
11125
11127
  for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
11126
- const path19 = [];
11128
+ const path20 = [];
11127
11129
  const seenAt = /* @__PURE__ */ new Map();
11128
11130
  let current = event;
11129
11131
  while (current) {
11130
11132
  const existing = seenAt.get(current.eventId);
11131
11133
  if (existing !== void 0) {
11132
- const cycle = path19.slice(existing);
11134
+ const cycle = path20.slice(existing);
11133
11135
  const key = cycle.map((item) => item.eventId).sort().join("\0");
11134
11136
  if (!seenCycles.has(key)) {
11135
11137
  seenCycles.add(key);
@@ -11145,8 +11147,8 @@ function createStructureCycleRule() {
11145
11147
  }
11146
11148
  break;
11147
11149
  }
11148
- seenAt.set(current.eventId, path19.length);
11149
- path19.push(current);
11150
+ seenAt.set(current.eventId, path20.length);
11151
+ path20.push(current);
11150
11152
  current = current.parentId ? byId.get(current.parentId) : void 0;
11151
11153
  }
11152
11154
  }
@@ -11943,23 +11945,23 @@ function evaluatePromptInjection(text, options = {}) {
11943
11945
  }
11944
11946
  return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
11945
11947
  }
11946
- function validateSchemaField(value, field, path19, evidence) {
11948
+ function validateSchemaField(value, field, path20, evidence) {
11947
11949
  const ruleId = "guardrail.structured-output";
11948
11950
  if (field.type) {
11949
11951
  const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
11950
11952
  if (actual !== field.type) {
11951
- evidence.push({ ruleId, path: path19, preview: `expected ${field.type}, got ${actual}` });
11953
+ evidence.push({ ruleId, path: path20, preview: `expected ${field.type}, got ${actual}` });
11952
11954
  return;
11953
11955
  }
11954
11956
  }
11955
11957
  if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
11956
- evidence.push({ ruleId, path: path19, preview: "value not in enum" });
11958
+ evidence.push({ ruleId, path: path20, preview: "value not in enum" });
11957
11959
  }
11958
11960
  if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
11959
11961
  const record = value;
11960
11962
  for (const key of field.required) {
11961
11963
  if (!(key in record)) {
11962
- evidence.push({ ruleId, path: `${path19}.${key}`, preview: "missing required key" });
11964
+ evidence.push({ ruleId, path: `${path20}.${key}`, preview: "missing required key" });
11963
11965
  }
11964
11966
  }
11965
11967
  }
@@ -12444,10 +12446,10 @@ function printHuman(result) {
12444
12446
  console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
12445
12447
  }
12446
12448
  for (const finding of result.findings) {
12447
- const path19 = finding.evidence[0]?.path;
12449
+ const path20 = finding.evidence[0]?.path;
12448
12450
  const run = finding.evidence[0]?.runId;
12449
12451
  const runPrefix = run ? `[${run}] ` : "";
12450
- console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path19 ? ` (${path19})` : ""}`);
12452
+ console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path20 ? ` (${path20})` : ""}`);
12451
12453
  }
12452
12454
  }
12453
12455
  function readErrorResult(error) {
@@ -12985,10 +12987,10 @@ async function evalRun(input3, options = {}) {
12985
12987
  diagnostics: []
12986
12988
  };
12987
12989
  }
12988
- function evidenceForRun(run, path19) {
12989
- return [{ runId: run.runId, ...path19 !== void 0 ? { path: path19 } : {} }];
12990
+ function evidenceForRun(run, path20) {
12991
+ return [{ runId: run.runId, ...path20 !== void 0 ? { path: path20 } : {} }];
12990
12992
  }
12991
- function evidenceForEvent(event, path19) {
12993
+ function evidenceForEvent(event, path20) {
12992
12994
  return [
12993
12995
  {
12994
12996
  runId: event.runId,
@@ -12996,7 +12998,7 @@ function evidenceForEvent(event, path19) {
12996
12998
  ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
12997
12999
  kind: event.kind,
12998
13000
  name: event.name,
12999
- ...path19 !== void 0 ? { path: path19 } : {}
13001
+ ...path20 !== void 0 ? { path: path20 } : {}
13000
13002
  }
13001
13003
  ];
13002
13004
  }
@@ -13154,9 +13156,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
13154
13156
  function tokenize(text) {
13155
13157
  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));
13156
13158
  }
13157
- function firstEvidence(fields, run, path19) {
13159
+ function firstEvidence(fields, run, path20) {
13158
13160
  const first = fields[0];
13159
- return first === void 0 ? evidenceForRun(run, path19) : evidenceForEvent(first.node.event, first.path);
13161
+ return first === void 0 ? evidenceForRun(run, path20) : evidenceForEvent(first.node.event, first.path);
13160
13162
  }
13161
13163
  function collectSourceIds(nodes, keys) {
13162
13164
  const wanted = keySet(keys);
@@ -13533,8 +13535,8 @@ function renderEvalMarkdown(result) {
13533
13535
  if (result.findings.length > 0) {
13534
13536
  lines.push("", "## Findings");
13535
13537
  for (const finding of result.findings) {
13536
- const path19 = finding.evidence[0]?.path;
13537
- lines.push(`- ${finding.ruleId}: ${finding.message}${path19 ? ` (${path19})` : ""}`);
13538
+ const path20 = finding.evidence[0]?.path;
13539
+ lines.push(`- ${finding.ruleId}: ${finding.message}${path20 ? ` (${path20})` : ""}`);
13538
13540
  }
13539
13541
  }
13540
13542
  return `${lines.join("\n")}
@@ -13727,8 +13729,8 @@ function printHuman2(result) {
13727
13729
  console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
13728
13730
  }
13729
13731
  for (const finding of result.findings) {
13730
- const path19 = finding.evidence[0]?.path;
13731
- console.log(`- ${finding.ruleId}: ${finding.message}${path19 ? ` (${path19})` : ""}`);
13732
+ const path20 = finding.evidence[0]?.path;
13733
+ console.log(`- ${finding.ruleId}: ${finding.message}${path20 ? ` (${path20})` : ""}`);
13732
13734
  }
13733
13735
  }
13734
13736
  function readErrorResult2(error) {
@@ -13966,8 +13968,8 @@ function printHuman3(result) {
13966
13968
  console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
13967
13969
  }
13968
13970
  for (const finding of result.findings) {
13969
- const path19 = finding.evidence[0]?.path;
13970
- console.log(`- ${finding.ruleId}: ${finding.message}${path19 ? ` (${path19})` : ""}`);
13971
+ const path20 = finding.evidence[0]?.path;
13972
+ console.log(`- ${finding.ruleId}: ${finding.message}${path20 ? ` (${path20})` : ""}`);
13971
13973
  }
13972
13974
  console.log(`Note: ${result.note}`);
13973
13975
  }
@@ -14086,8 +14088,8 @@ function renderCheckSection(result) {
14086
14088
  `Diagnostics: ${result.diagnostics.length}`
14087
14089
  ];
14088
14090
  for (const finding of result.findings.slice(0, 10)) {
14089
- const path19 = finding.evidence[0]?.path ?? "(run)";
14090
- lines.push(`- ${finding.ruleId}: ${finding.message} (${path19})`);
14091
+ const path20 = finding.evidence[0]?.path ?? "(run)";
14092
+ lines.push(`- ${finding.ruleId}: ${finding.message} (${path20})`);
14091
14093
  }
14092
14094
  for (const diagnostic4 of result.diagnostics.slice(0, 10)) {
14093
14095
  lines.push(`- ${diagnostic4.code}: ${diagnostic4.message}`);
@@ -15257,6 +15259,608 @@ async function indexCleanCommand(options = {}) {
15257
15259
  }
15258
15260
  }
15259
15261
 
15262
+ // packages/core/src/workspace/types.ts
15263
+ var WORKSPACE_SCHEMA_VERSION = "1.0";
15264
+ var WORKSPACE_DIR_NAME = ".agent-inspect";
15265
+ var WORKSPACE_MANIFEST_FILENAME = "workspace.json";
15266
+
15267
+ // packages/core/src/workspace/manifest.ts
15268
+ var DEFAULT_WORKSPACE_LAYOUT = {
15269
+ traceDirs: ["runs"],
15270
+ reportsDir: "reports",
15271
+ artifactsDir: "artifacts",
15272
+ bundlesDir: "bundles",
15273
+ notesDir: "notes"
15274
+ };
15275
+ var DEFAULT_REDACTION_PROFILE = "share";
15276
+ var REDACTION_PROFILES2 = [
15277
+ "local",
15278
+ "share",
15279
+ "strict"
15280
+ ];
15281
+ var INDEX_TYPES = ["none", "sqlite", "custom"];
15282
+ var MAX_WORKSPACE_MANIFEST_BYTES = 64 * 1024;
15283
+ function createDefaultWorkspaceManifest(options) {
15284
+ const project = typeof options.project === "string" ? options.project.trim() : "";
15285
+ const index = {
15286
+ enabled: options.index?.enabled ?? false,
15287
+ type: options.index?.type ?? "none",
15288
+ ...options.index?.path !== void 0 ? { path: options.index.path } : {}
15289
+ };
15290
+ return {
15291
+ schemaVersion: WORKSPACE_SCHEMA_VERSION,
15292
+ project,
15293
+ createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
15294
+ traceDirs: options.traceDirs ? [...options.traceDirs] : [...DEFAULT_WORKSPACE_LAYOUT.traceDirs],
15295
+ reportsDir: options.reportsDir ?? DEFAULT_WORKSPACE_LAYOUT.reportsDir,
15296
+ artifactsDir: options.artifactsDir ?? DEFAULT_WORKSPACE_LAYOUT.artifactsDir,
15297
+ bundlesDir: options.bundlesDir ?? DEFAULT_WORKSPACE_LAYOUT.bundlesDir,
15298
+ notesDir: options.notesDir ?? DEFAULT_WORKSPACE_LAYOUT.notesDir,
15299
+ redactionProfile: options.redactionProfile ?? DEFAULT_REDACTION_PROFILE,
15300
+ index
15301
+ };
15302
+ }
15303
+ function isPlainObject(value) {
15304
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15305
+ }
15306
+ function isSafeRelativeWorkspacePath(p) {
15307
+ if (typeof p !== "string") return false;
15308
+ const trimmed = p.trim();
15309
+ if (trimmed === "") return false;
15310
+ if (trimmed.startsWith("/") || trimmed.startsWith("\\")) return false;
15311
+ if (/^[a-zA-Z]:/.test(trimmed)) return false;
15312
+ const segments = trimmed.split(/[/\\]+/);
15313
+ return !segments.some((seg) => seg === "..");
15314
+ }
15315
+ function validateDirField(value, field, errors) {
15316
+ if (typeof value !== "string" || value.trim() === "") {
15317
+ errors.push(`${field} must be a non-empty string`);
15318
+ return;
15319
+ }
15320
+ if (!isSafeRelativeWorkspacePath(value)) {
15321
+ errors.push(
15322
+ `${field} must be a relative path inside the workspace (no absolute paths or ".." traversal)`
15323
+ );
15324
+ }
15325
+ }
15326
+ function validateIndex(value, errors) {
15327
+ if (!isPlainObject(value)) {
15328
+ errors.push("index must be an object");
15329
+ return void 0;
15330
+ }
15331
+ if (typeof value.enabled !== "boolean") {
15332
+ errors.push("index.enabled must be a boolean");
15333
+ }
15334
+ if (!INDEX_TYPES.includes(value.type)) {
15335
+ errors.push(`index.type must be one of: ${INDEX_TYPES.join(", ")}`);
15336
+ }
15337
+ if (value.path !== void 0 && !isSafeRelativeWorkspacePath(value.path)) {
15338
+ errors.push(
15339
+ 'index.path must be a relative path inside the workspace (no absolute paths or ".." traversal)'
15340
+ );
15341
+ }
15342
+ if (errors.length > 0) return void 0;
15343
+ return {
15344
+ enabled: value.enabled,
15345
+ type: value.type,
15346
+ ...value.path !== void 0 ? { path: value.path } : {}
15347
+ };
15348
+ }
15349
+ function validateWorkspaceManifest(input3) {
15350
+ const errors = [];
15351
+ const warnings = [];
15352
+ if (!isPlainObject(input3)) {
15353
+ return { ok: false, errors: ["manifest must be an object"], warnings };
15354
+ }
15355
+ if (input3.schemaVersion !== WORKSPACE_SCHEMA_VERSION) {
15356
+ errors.push(
15357
+ `schemaVersion must be "${WORKSPACE_SCHEMA_VERSION}" (received ${JSON.stringify(
15358
+ input3.schemaVersion
15359
+ )})`
15360
+ );
15361
+ }
15362
+ if (typeof input3.project !== "string" || input3.project.trim() === "") {
15363
+ errors.push("project must be a non-empty string");
15364
+ }
15365
+ if (typeof input3.createdAt !== "string" || input3.createdAt.trim() === "") {
15366
+ errors.push("createdAt must be a non-empty ISO-8601 string");
15367
+ } else if (Number.isNaN(Date.parse(input3.createdAt))) {
15368
+ errors.push("createdAt must be a valid ISO-8601 date string");
15369
+ }
15370
+ if (!Array.isArray(input3.traceDirs) || input3.traceDirs.length === 0) {
15371
+ errors.push("traceDirs must be a non-empty array");
15372
+ } else {
15373
+ input3.traceDirs.forEach((dir, i) => {
15374
+ if (typeof dir !== "string" || dir.trim() === "") {
15375
+ errors.push(`traceDirs[${i}] must be a non-empty string`);
15376
+ } else if (!isSafeRelativeWorkspacePath(dir)) {
15377
+ errors.push(
15378
+ `traceDirs[${i}] must be a relative path inside the workspace (no absolute paths or ".." traversal)`
15379
+ );
15380
+ }
15381
+ });
15382
+ }
15383
+ validateDirField(input3.reportsDir, "reportsDir", errors);
15384
+ validateDirField(input3.artifactsDir, "artifactsDir", errors);
15385
+ validateDirField(input3.bundlesDir, "bundlesDir", errors);
15386
+ validateDirField(input3.notesDir, "notesDir", errors);
15387
+ if (!REDACTION_PROFILES2.includes(input3.redactionProfile)) {
15388
+ errors.push(`redactionProfile must be one of: ${REDACTION_PROFILES2.join(", ")}`);
15389
+ }
15390
+ const indexErrors = [];
15391
+ const index = validateIndex(input3.index, indexErrors);
15392
+ errors.push(...indexErrors);
15393
+ if (index && index.type !== "none" && !index.enabled) {
15394
+ warnings.push(`index.type is "${index.type}" but index.enabled is false`);
15395
+ }
15396
+ if (errors.length > 0 || index === void 0) {
15397
+ return { ok: false, errors, warnings };
15398
+ }
15399
+ const manifest = {
15400
+ schemaVersion: WORKSPACE_SCHEMA_VERSION,
15401
+ project: input3.project.trim(),
15402
+ createdAt: input3.createdAt,
15403
+ traceDirs: input3.traceDirs.map((d) => d.trim()),
15404
+ reportsDir: input3.reportsDir.trim(),
15405
+ artifactsDir: input3.artifactsDir.trim(),
15406
+ bundlesDir: input3.bundlesDir.trim(),
15407
+ notesDir: input3.notesDir.trim(),
15408
+ redactionProfile: input3.redactionProfile,
15409
+ index
15410
+ };
15411
+ return { ok: true, manifest, errors, warnings };
15412
+ }
15413
+ function parseWorkspaceManifest(json) {
15414
+ const warnings = [];
15415
+ if (typeof json !== "string") {
15416
+ return { ok: false, errors: ["manifest input must be a string"], warnings };
15417
+ }
15418
+ if (json.length > MAX_WORKSPACE_MANIFEST_BYTES) {
15419
+ return {
15420
+ ok: false,
15421
+ errors: [
15422
+ `manifest exceeds maximum size of ${MAX_WORKSPACE_MANIFEST_BYTES} bytes`
15423
+ ],
15424
+ warnings
15425
+ };
15426
+ }
15427
+ let parsed;
15428
+ try {
15429
+ parsed = JSON.parse(json);
15430
+ } catch {
15431
+ return { ok: false, errors: ["manifest is not valid JSON"], warnings };
15432
+ }
15433
+ return validateWorkspaceManifest(parsed);
15434
+ }
15435
+ function serializeWorkspaceManifest(manifest) {
15436
+ return `${JSON.stringify(manifest, null, 2)}
15437
+ `;
15438
+ }
15439
+ var INDEX_DIR_NAME = "index";
15440
+ function resolveWorkspaceLocation(cwd = process.cwd()) {
15441
+ const projectRoot = path14__default.default.resolve(cwd);
15442
+ const workspaceDir = path14__default.default.join(projectRoot, WORKSPACE_DIR_NAME);
15443
+ return {
15444
+ projectRoot,
15445
+ workspaceDir,
15446
+ manifestPath: path14__default.default.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
15447
+ };
15448
+ }
15449
+ function resolveInsideWorkspace(workspaceDir, relative) {
15450
+ const base = path14__default.default.resolve(workspaceDir);
15451
+ const resolved = path14__default.default.resolve(base, relative);
15452
+ const rel = path14__default.default.relative(base, resolved);
15453
+ if (rel === "" || rel === "." || !rel.startsWith("..") && !path14__default.default.isAbsolute(rel)) {
15454
+ return resolved;
15455
+ }
15456
+ throw new Error(
15457
+ `Workspace path "${relative}" resolves outside the workspace directory`
15458
+ );
15459
+ }
15460
+ async function pathExists(p) {
15461
+ try {
15462
+ await promises.access(p);
15463
+ return true;
15464
+ } catch {
15465
+ return false;
15466
+ }
15467
+ }
15468
+ async function isWritable(p) {
15469
+ try {
15470
+ await promises.access(p, fs.constants.W_OK);
15471
+ return true;
15472
+ } catch {
15473
+ return false;
15474
+ }
15475
+ }
15476
+ async function listJsonl(dir) {
15477
+ try {
15478
+ const entries = await promises.readdir(dir);
15479
+ return entries.filter((f) => f.endsWith(".jsonl"));
15480
+ } catch {
15481
+ return [];
15482
+ }
15483
+ }
15484
+ async function countFiles(dir) {
15485
+ try {
15486
+ const entries = await promises.readdir(dir, { withFileTypes: true });
15487
+ return entries.filter((e) => e.isFile()).length;
15488
+ } catch {
15489
+ return 0;
15490
+ }
15491
+ }
15492
+ async function readWorkspaceManifestFile(location) {
15493
+ let raw;
15494
+ try {
15495
+ raw = await promises.readFile(location.manifestPath, "utf-8");
15496
+ } catch {
15497
+ return { exists: false, ok: false, errors: ["workspace.json not found"], warnings: [] };
15498
+ }
15499
+ const parsed = parseWorkspaceManifest(raw);
15500
+ return {
15501
+ exists: true,
15502
+ ok: parsed.ok,
15503
+ ...parsed.manifest ? { manifest: parsed.manifest } : {},
15504
+ errors: parsed.errors,
15505
+ warnings: parsed.warnings
15506
+ };
15507
+ }
15508
+ async function createWorkspace(options = {}) {
15509
+ const location = resolveWorkspaceLocation(options.cwd);
15510
+ const dryRun = options.dryRun === true;
15511
+ const existing = await readWorkspaceManifestFile(location);
15512
+ const topLevelTraces = await listJsonl(location.workspaceDir);
15513
+ const detectedExistingTraces = topLevelTraces.length > 0;
15514
+ let manifest;
15515
+ let created;
15516
+ let adopted;
15517
+ if (existing.exists && existing.ok && existing.manifest) {
15518
+ manifest = existing.manifest;
15519
+ created = false;
15520
+ adopted = true;
15521
+ } else {
15522
+ const project = options.project?.trim() || path14__default.default.basename(location.projectRoot) || "workspace";
15523
+ const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
15524
+ manifest = createDefaultWorkspaceManifest({
15525
+ project,
15526
+ traceDirs,
15527
+ ...options.redactionProfile ? { redactionProfile: options.redactionProfile } : {}
15528
+ });
15529
+ created = true;
15530
+ adopted = detectedExistingTraces || existing.exists && !existing.ok;
15531
+ }
15532
+ const relDirs = uniqueDirs([
15533
+ ...manifest.traceDirs.filter((d) => d !== "."),
15534
+ manifest.reportsDir,
15535
+ manifest.artifactsDir,
15536
+ manifest.bundlesDir,
15537
+ manifest.notesDir,
15538
+ INDEX_DIR_NAME
15539
+ ]);
15540
+ const createdDirs = [];
15541
+ for (const rel of relDirs) {
15542
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
15543
+ if (await pathExists(abs)) continue;
15544
+ createdDirs.push(rel);
15545
+ if (!dryRun) await promises.mkdir(abs, { recursive: true });
15546
+ }
15547
+ if (!dryRun && created) {
15548
+ await promises.mkdir(location.workspaceDir, { recursive: true });
15549
+ await promises.writeFile(location.manifestPath, serializeWorkspaceManifest(manifest), "utf-8");
15550
+ }
15551
+ return {
15552
+ location,
15553
+ manifest,
15554
+ created,
15555
+ adopted,
15556
+ createdDirs,
15557
+ detectedExistingTraces,
15558
+ dryRun
15559
+ };
15560
+ }
15561
+ function uniqueDirs(dirs) {
15562
+ const seen = /* @__PURE__ */ new Set();
15563
+ const out = [];
15564
+ for (const d of dirs) {
15565
+ const t = d.trim();
15566
+ if (t === "" || t === "." || seen.has(t)) continue;
15567
+ seen.add(t);
15568
+ out.push(t);
15569
+ }
15570
+ return out;
15571
+ }
15572
+ async function getWorkspaceStatus(location, manifest) {
15573
+ let traceFiles = 0;
15574
+ for (const rel of manifest.traceDirs) {
15575
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
15576
+ traceFiles += (await listJsonl(abs)).length;
15577
+ }
15578
+ const reports = await countFiles(
15579
+ resolveInsideWorkspace(location.workspaceDir, manifest.reportsDir)
15580
+ );
15581
+ const artifacts = await countFiles(
15582
+ resolveInsideWorkspace(location.workspaceDir, manifest.artifactsDir)
15583
+ );
15584
+ const bundles = await countFiles(
15585
+ resolveInsideWorkspace(location.workspaceDir, manifest.bundlesDir)
15586
+ );
15587
+ const notes = await countFiles(
15588
+ resolveInsideWorkspace(location.workspaceDir, manifest.notesDir)
15589
+ );
15590
+ const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
15591
+ return {
15592
+ project: manifest.project,
15593
+ traceFiles,
15594
+ reports,
15595
+ artifacts,
15596
+ bundles,
15597
+ notes,
15598
+ index: {
15599
+ enabled: manifest.index.enabled,
15600
+ type: manifest.index.type,
15601
+ exists: await pathExists(indexPath2)
15602
+ }
15603
+ };
15604
+ }
15605
+ async function doctorWorkspace(location) {
15606
+ const checks2 = [];
15607
+ const manifestResult = await readWorkspaceManifestFile(location);
15608
+ if (!manifestResult.exists) {
15609
+ checks2.push({
15610
+ id: "manifest",
15611
+ status: "fail",
15612
+ message: "workspace.json not found (run `agent-inspect workspace init`)"
15613
+ });
15614
+ return { ok: false, checks: checks2 };
15615
+ }
15616
+ if (!manifestResult.ok || !manifestResult.manifest) {
15617
+ checks2.push({
15618
+ id: "manifest",
15619
+ status: "fail",
15620
+ message: `workspace.json is invalid: ${manifestResult.errors.join("; ")}`
15621
+ });
15622
+ return { ok: false, checks: checks2 };
15623
+ }
15624
+ const manifest = manifestResult.manifest;
15625
+ checks2.push({ id: "manifest", status: "pass", message: "workspace.json is valid" });
15626
+ for (const warning of manifestResult.warnings) {
15627
+ checks2.push({ id: "manifest-warning", status: "warn", message: warning });
15628
+ }
15629
+ const dirFields = [
15630
+ ...manifest.traceDirs.filter((d) => d !== ".").map((d, i) => [`traceDir[${i}]`, d]),
15631
+ ["reportsDir", manifest.reportsDir],
15632
+ ["artifactsDir", manifest.artifactsDir],
15633
+ ["bundlesDir", manifest.bundlesDir],
15634
+ ["notesDir", manifest.notesDir]
15635
+ ];
15636
+ for (const [id, rel] of dirFields) {
15637
+ let abs;
15638
+ try {
15639
+ abs = resolveInsideWorkspace(location.workspaceDir, rel);
15640
+ } catch (error) {
15641
+ checks2.push({
15642
+ id,
15643
+ status: "fail",
15644
+ message: error instanceof Error ? error.message : String(error)
15645
+ });
15646
+ continue;
15647
+ }
15648
+ if (!await pathExists(abs)) {
15649
+ checks2.push({ id, status: "warn", message: `${rel}/ does not exist yet` });
15650
+ } else if (!await isWritable(abs)) {
15651
+ checks2.push({ id, status: "fail", message: `${rel}/ is not writable` });
15652
+ } else {
15653
+ checks2.push({ id, status: "pass", message: `${rel}/ is present and writable` });
15654
+ }
15655
+ }
15656
+ let newestTraceMtime = 0;
15657
+ for (const rel of manifest.traceDirs) {
15658
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
15659
+ for (const file of await listJsonl(abs)) {
15660
+ try {
15661
+ const s = await promises.stat(path14__default.default.join(abs, file));
15662
+ newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
15663
+ } catch {
15664
+ checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
15665
+ }
15666
+ }
15667
+ }
15668
+ if (manifest.index.enabled) {
15669
+ const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
15670
+ if (!await pathExists(indexPath2)) {
15671
+ checks2.push({ id: "index", status: "warn", message: "index enabled but not built" });
15672
+ } else {
15673
+ try {
15674
+ const s = await promises.stat(indexPath2);
15675
+ if (newestTraceMtime > s.mtimeMs) {
15676
+ checks2.push({ id: "index", status: "warn", message: "index is stale (traces are newer)" });
15677
+ } else {
15678
+ checks2.push({ id: "index", status: "pass", message: "index is present" });
15679
+ }
15680
+ } catch {
15681
+ checks2.push({ id: "index", status: "warn", message: "cannot stat index" });
15682
+ }
15683
+ }
15684
+ }
15685
+ const ok = !checks2.some((c) => c.status === "fail");
15686
+ return { ok, checks: checks2 };
15687
+ }
15688
+ async function cleanWorkspace(location, manifest, options = {}) {
15689
+ const dryRun = options.confirm !== true;
15690
+ const targets = uniqueDirs([
15691
+ manifest.reportsDir,
15692
+ manifest.artifactsDir,
15693
+ manifest.bundlesDir,
15694
+ manifest.index.path ?? INDEX_DIR_NAME
15695
+ ]);
15696
+ const removed = [];
15697
+ for (const rel of targets) {
15698
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
15699
+ let entries;
15700
+ try {
15701
+ entries = await promises.readdir(abs);
15702
+ } catch {
15703
+ continue;
15704
+ }
15705
+ for (const entry of entries) {
15706
+ const relPath = `${rel}/${entry}`;
15707
+ removed.push(relPath);
15708
+ if (!dryRun) {
15709
+ await promises.rm(path14__default.default.join(abs, entry), { recursive: true, force: true });
15710
+ }
15711
+ }
15712
+ }
15713
+ return { dryRun, removed };
15714
+ }
15715
+
15716
+ // packages/cli/src/workspace.ts
15717
+ function printJson4(payload) {
15718
+ console.log(JSON.stringify(payload, null, 2));
15719
+ }
15720
+ function fail3(json, message) {
15721
+ if (json) {
15722
+ printJson4({ ok: false, error: message });
15723
+ } else {
15724
+ console.error(`[AgentInspect] workspace: ${message}`);
15725
+ }
15726
+ process.exitCode = 1;
15727
+ }
15728
+ async function workspaceInitCommand(options = {}) {
15729
+ try {
15730
+ const result = await createWorkspace({
15731
+ ...options.cwd ? { cwd: options.cwd } : {},
15732
+ ...options.project ? { project: options.project } : {},
15733
+ ...options.redactionProfile ? { redactionProfile: options.redactionProfile } : {},
15734
+ dryRun: options.dryRun === true
15735
+ });
15736
+ if (options.json) {
15737
+ printJson4({
15738
+ ok: true,
15739
+ version,
15740
+ dryRun: result.dryRun,
15741
+ created: result.created,
15742
+ adopted: result.adopted,
15743
+ detectedExistingTraces: result.detectedExistingTraces,
15744
+ workspaceDir: result.location.workspaceDir,
15745
+ manifestPath: result.location.manifestPath,
15746
+ createdDirs: result.createdDirs,
15747
+ project: result.manifest.project
15748
+ });
15749
+ return;
15750
+ }
15751
+ console.log("AgentInspect workspace");
15752
+ console.log(`Project: ${result.manifest.project}`);
15753
+ console.log(`Directory: ${result.location.workspaceDir}`);
15754
+ if (result.dryRun) {
15755
+ console.log("Dry run \u2014 would create:");
15756
+ for (const dir of result.createdDirs) console.log(`- ${dir}/`);
15757
+ if (result.created) console.log(`- ${result.location.manifestPath}`);
15758
+ return;
15759
+ }
15760
+ if (result.adopted && !result.created) {
15761
+ console.log("Adopted existing workspace.json (left unchanged).");
15762
+ } else if (result.created) {
15763
+ console.log("Created workspace.json");
15764
+ }
15765
+ if (result.detectedExistingTraces) {
15766
+ console.log("Detected existing traces \u2014 preserved without changes.");
15767
+ }
15768
+ for (const dir of result.createdDirs) console.log(`Created ${dir}/`);
15769
+ console.log("\nNext: `agent-inspect workspace status`");
15770
+ } catch (error) {
15771
+ fail3(options.json, error instanceof Error ? error.message : String(error));
15772
+ }
15773
+ }
15774
+ async function workspaceStatusCommand(options = {}) {
15775
+ const location = resolveWorkspaceLocation(options.cwd);
15776
+ const manifestResult = await readWorkspaceManifestFile(location);
15777
+ if (!manifestResult.ok || !manifestResult.manifest) {
15778
+ fail3(
15779
+ options.json,
15780
+ manifestResult.exists ? `invalid workspace.json: ${manifestResult.errors.join("; ")}` : "no workspace found (run `agent-inspect workspace init`)"
15781
+ );
15782
+ return;
15783
+ }
15784
+ try {
15785
+ const status = await getWorkspaceStatus(location, manifestResult.manifest);
15786
+ if (options.json) {
15787
+ printJson4({ ok: true, ...status });
15788
+ return;
15789
+ }
15790
+ console.log(`Project: ${status.project}`);
15791
+ console.log(`Traces: ${status.traceFiles}`);
15792
+ console.log(`Reports: ${status.reports}`);
15793
+ console.log(`Artifacts: ${status.artifacts}`);
15794
+ console.log(`Bundles: ${status.bundles}`);
15795
+ console.log(`Notes: ${status.notes}`);
15796
+ console.log(
15797
+ `Index: ${status.index.enabled ? status.index.type : "disabled"}${status.index.exists ? " (present)" : ""}`
15798
+ );
15799
+ } catch (error) {
15800
+ fail3(options.json, error instanceof Error ? error.message : String(error));
15801
+ }
15802
+ }
15803
+ async function workspaceDoctorCommand(options = {}) {
15804
+ const location = resolveWorkspaceLocation(options.cwd);
15805
+ const result = await doctorWorkspace(location);
15806
+ if (options.json) {
15807
+ printJson4({ ok: result.ok, checks: result.checks });
15808
+ } else {
15809
+ console.log("AgentInspect workspace doctor");
15810
+ for (const check of result.checks) {
15811
+ const mark = check.status === "pass" ? "\u2713" : check.status === "warn" ? "!" : "\u2717";
15812
+ console.log(`${mark} ${check.id}: ${check.message}`);
15813
+ }
15814
+ console.log(result.ok ? "\nWorkspace looks healthy." : "\nWorkspace has problems.");
15815
+ }
15816
+ if (!result.ok) process.exitCode = 1;
15817
+ }
15818
+ async function workspaceCleanCommand(options = {}) {
15819
+ const location = resolveWorkspaceLocation(options.cwd);
15820
+ const manifestResult = await readWorkspaceManifestFile(location);
15821
+ if (!manifestResult.ok || !manifestResult.manifest) {
15822
+ fail3(
15823
+ options.json,
15824
+ manifestResult.exists ? `invalid workspace.json: ${manifestResult.errors.join("; ")}` : "no workspace found (run `agent-inspect workspace init`)"
15825
+ );
15826
+ return;
15827
+ }
15828
+ try {
15829
+ const result = await cleanWorkspace(location, manifestResult.manifest, {
15830
+ confirm: options.yes === true
15831
+ });
15832
+ if (options.json) {
15833
+ printJson4({ ok: true, dryRun: result.dryRun, removed: result.removed });
15834
+ return;
15835
+ }
15836
+ if (result.dryRun) {
15837
+ console.log("Dry run \u2014 would remove (traces are never deleted):");
15838
+ if (result.removed.length === 0) console.log("- (nothing)");
15839
+ for (const item of result.removed) console.log(`- ${item}`);
15840
+ console.log("\nRe-run with --yes to delete.");
15841
+ return;
15842
+ }
15843
+ console.log("Removed generated workspace content (traces preserved):");
15844
+ if (result.removed.length === 0) console.log("- (nothing)");
15845
+ for (const item of result.removed) console.log(`- ${item}`);
15846
+ } catch (error) {
15847
+ fail3(options.json, error instanceof Error ? error.message : String(error));
15848
+ }
15849
+ }
15850
+ async function workspacePathCommand(options = {}) {
15851
+ const location = resolveWorkspaceLocation(options.cwd);
15852
+ if (options.json) {
15853
+ printJson4({
15854
+ ok: true,
15855
+ projectRoot: location.projectRoot,
15856
+ workspaceDir: location.workspaceDir,
15857
+ manifestPath: location.manifestPath
15858
+ });
15859
+ return;
15860
+ }
15861
+ console.log(location.workspaceDir);
15862
+ }
15863
+
15260
15864
  // packages/cli/src/index.ts
15261
15865
  function runCommand(action) {
15262
15866
  void action().catch((error) => {
@@ -15588,6 +16192,28 @@ function createCliProgram() {
15588
16192
  indexCmd.command("clean").description("Remove the local index file").option("--dir <path>", "trace directory").option("--json", "print JSON result").action((opts) => {
15589
16193
  runCommand(() => indexCleanCommand(opts));
15590
16194
  });
16195
+ const workspaceCmd = program.command("workspace").description("Manage a project-local AgentInspect workspace (.agent-inspect)");
16196
+ workspaceCmd.command("init").description("Create or adopt a local workspace (never deletes traces)").option("--project <name>", "project name (default: directory name)").addOption(
16197
+ new commander.Option("--redaction-profile <profile>", "default redaction posture").choices([
16198
+ "local",
16199
+ "share",
16200
+ "strict"
16201
+ ])
16202
+ ).option("--dry-run", "print planned changes without writing").option("--json", "print deterministic JSON result").action((opts) => {
16203
+ runCommand(() => workspaceInitCommand(opts));
16204
+ });
16205
+ workspaceCmd.command("status").description("Show workspace counts and index status (read-only)").option("--json", "print deterministic JSON result").action((opts) => {
16206
+ runCommand(() => workspaceStatusCommand(opts));
16207
+ });
16208
+ workspaceCmd.command("doctor").description("Validate workspace config, permissions, and index freshness").option("--json", "print deterministic JSON result").action((opts) => {
16209
+ runCommand(() => workspaceDoctorCommand(opts));
16210
+ });
16211
+ workspaceCmd.command("clean").description("Remove generated content (reports/artifacts/bundles/index); traces are never deleted").option("--yes", "actually delete (default is a dry-run)").option("--json", "print deterministic JSON result").action((opts) => {
16212
+ runCommand(() => workspaceCleanCommand(opts));
16213
+ });
16214
+ workspaceCmd.command("path").description("Print resolved workspace paths").option("--json", "print deterministic JSON result").action((opts) => {
16215
+ runCommand(() => workspacePathCommand(opts));
16216
+ });
15591
16217
  return program;
15592
16218
  }
15593
16219
  function isPrimaryModule() {