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.
- package/CHANGELOG.md +14 -0
- package/README.md +4 -4
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CLI.md +21 -0
- package/docs/COMPARE.md +35 -14
- package/docs/SAFE-TRACE-SHARING.md +1 -0
- package/docs/TECHNICAL-GUIDE.md +9 -7
- package/docs/WORKSPACE.md +96 -0
- package/docs/assets/readme-product-loop.svg +12 -17
- package/package.json +12 -1
- package/packages/cli/dist/index.cjs +698 -72
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +699 -73
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/core/dist/exporters.cjs +4 -1
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.mjs +4 -1
- package/packages/core/dist/exporters.mjs.map +1 -1
- package/packages/core/dist/workspace.cjs +483 -0
- package/packages/core/dist/workspace.cjs.map +1 -0
- package/packages/core/dist/workspace.d.cts +220 -0
- package/packages/core/dist/workspace.d.ts +220 -0
- package/packages/core/dist/workspace.mjs +461 -0
- package/packages/core/dist/workspace.mjs.map +1 -0
|
@@ -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 = "
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
8469
|
-
if (lm !==
|
|
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:
|
|
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:
|
|
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(
|
|
8647
|
-
if (
|
|
8648
|
+
function formatPath(path20) {
|
|
8649
|
+
if (path20 === void 0 || path20.path.length === 0) {
|
|
8648
8650
|
return "(run)";
|
|
8649
8651
|
}
|
|
8650
|
-
return
|
|
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(
|
|
9534
|
+
function childPath(path20, key) {
|
|
9533
9535
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
9534
|
-
return
|
|
9536
|
+
return path20 ? `${path20}.${key}` : key;
|
|
9535
9537
|
}
|
|
9536
|
-
return `${
|
|
9538
|
+
return `${path20 || "$"}[${JSON.stringify(key)}]`;
|
|
9537
9539
|
}
|
|
9538
|
-
function indexPath(
|
|
9539
|
-
return `${
|
|
9540
|
+
function indexPath(path20, index) {
|
|
9541
|
+
return `${path20 || "$"}[${index}]`;
|
|
9540
9542
|
}
|
|
9541
|
-
function makeFinding(
|
|
9542
|
-
return preview === void 0 ? { path:
|
|
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,
|
|
9613
|
+
#redactValue(value, key, path20, depth, state) {
|
|
9612
9614
|
if (depth > this.#maxDepth) {
|
|
9613
9615
|
this.#recordFinding(
|
|
9614
9616
|
state,
|
|
9615
|
-
makeFinding(
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
-
...
|
|
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(
|
|
10612
|
-
const parts =
|
|
10613
|
-
return parts[parts.length - 1] ??
|
|
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,
|
|
10628
|
-
entries.push({ event, path:
|
|
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, `${
|
|
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
|
-
`${
|
|
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,
|
|
10724
|
-
lines.push(`${
|
|
10725
|
-
node.children.forEach((child, index) => visit(child, `${
|
|
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,
|
|
10776
|
+
function firstEvidenceForKind(context, kind, path20) {
|
|
10775
10777
|
const event = context.events.find((candidate) => candidate.kind === kind);
|
|
10776
|
-
return event ? [eventEvidence(event,
|
|
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
|
|
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 =
|
|
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,
|
|
11149
|
-
|
|
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,
|
|
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:
|
|
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:
|
|
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: `${
|
|
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
|
|
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}${
|
|
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,
|
|
12989
|
-
return [{ runId: run.runId, ...
|
|
12990
|
+
function evidenceForRun(run, path20) {
|
|
12991
|
+
return [{ runId: run.runId, ...path20 !== void 0 ? { path: path20 } : {} }];
|
|
12990
12992
|
}
|
|
12991
|
-
function evidenceForEvent(event,
|
|
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
|
-
...
|
|
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,
|
|
13159
|
+
function firstEvidence(fields, run, path20) {
|
|
13158
13160
|
const first = fields[0];
|
|
13159
|
-
return first === void 0 ? evidenceForRun(run,
|
|
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
|
|
13537
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
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
|
|
13731
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
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
|
|
13970
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
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
|
|
14090
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
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() {
|