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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { realpathSync, createReadStream } from 'fs';
|
|
2
|
+
import { realpathSync, createReadStream, constants as constants$1 } from 'fs';
|
|
3
3
|
import path14 from 'path';
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
5
5
|
import { Command, Option } from 'commander';
|
|
@@ -14,7 +14,7 @@ import { createServer } from 'http';
|
|
|
14
14
|
import { createRequire } from 'module';
|
|
15
15
|
|
|
16
16
|
// package.json
|
|
17
|
-
var version = "
|
|
17
|
+
var version = "4.0.0";
|
|
18
18
|
|
|
19
19
|
// packages/core/src/correlation-metadata.ts
|
|
20
20
|
var TRACE_CORRELATION_KEYS = [
|
|
@@ -3060,12 +3060,12 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3060
3060
|
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
3061
3061
|
);
|
|
3062
3062
|
const ordered = [...runs].sort(compareRuns);
|
|
3063
|
-
const
|
|
3063
|
+
const path20 = [];
|
|
3064
3064
|
const visited = /* @__PURE__ */ new Set();
|
|
3065
3065
|
const pushRun = (run, confidence, source) => {
|
|
3066
3066
|
if (visited.has(run.runId)) return;
|
|
3067
3067
|
visited.add(run.runId);
|
|
3068
|
-
|
|
3068
|
+
path20.push({
|
|
3069
3069
|
runId: run.runId,
|
|
3070
3070
|
name: run.name,
|
|
3071
3071
|
startedAt: run.startedAt,
|
|
@@ -3090,7 +3090,7 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3090
3090
|
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
3091
3091
|
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
3092
3092
|
}
|
|
3093
|
-
return
|
|
3093
|
+
return path20;
|
|
3094
3094
|
}
|
|
3095
3095
|
function metaRunIdMatches(run, token, runById) {
|
|
3096
3096
|
const meta = extractSessionWorkflowMetadata(run.metadata);
|
|
@@ -3326,8 +3326,7 @@ async function list(options = {}) {
|
|
|
3326
3326
|
const filtered = filterTraces(metas, {
|
|
3327
3327
|
status: options.status,
|
|
3328
3328
|
name: options.name,
|
|
3329
|
-
since: options.since
|
|
3330
|
-
limit
|
|
3329
|
+
since: options.since
|
|
3331
3330
|
});
|
|
3332
3331
|
const shown = filtered.slice(0, limit);
|
|
3333
3332
|
if (options.json) {
|
|
@@ -8101,7 +8100,10 @@ function buildRunReport(events, options) {
|
|
|
8101
8100
|
'<section class="tree">',
|
|
8102
8101
|
"</section>"
|
|
8103
8102
|
);
|
|
8104
|
-
const
|
|
8103
|
+
const errorsSectionEnd = treeHtml.content.includes(
|
|
8104
|
+
"<h2>Attributes (bounded)</h2>"
|
|
8105
|
+
) ? "<h2>Attributes (bounded)</h2>" : "<footer>";
|
|
8106
|
+
const errorsSection = treeHtml.content.includes("<h2>Errors</h2>") ? extractHtmlFragment(treeHtml.content, "<h2>Errors</h2>", errorsSectionEnd) : "";
|
|
8105
8107
|
const attrsSection = treeHtml.content.includes("<h2>Attributes (bounded)</h2>") ? extractHtmlFragment(
|
|
8106
8108
|
treeHtml.content,
|
|
8107
8109
|
"<h2>Attributes (bounded)</h2>",
|
|
@@ -8391,13 +8393,13 @@ function pairSteps(left, right) {
|
|
|
8391
8393
|
return pairs;
|
|
8392
8394
|
}
|
|
8393
8395
|
function compareLeafSteps(L, R, segments, opts, out) {
|
|
8394
|
-
const
|
|
8396
|
+
const path20 = buildPath(segments);
|
|
8395
8397
|
if (L.name !== R.name) {
|
|
8396
8398
|
out.push({
|
|
8397
8399
|
kind: "structure",
|
|
8398
8400
|
severity: "warning",
|
|
8399
8401
|
message: "Step name differs",
|
|
8400
|
-
path:
|
|
8402
|
+
path: path20,
|
|
8401
8403
|
left: L.name,
|
|
8402
8404
|
right: R.name
|
|
8403
8405
|
});
|
|
@@ -8407,7 +8409,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8407
8409
|
kind: "step-type",
|
|
8408
8410
|
severity: "warning",
|
|
8409
8411
|
message: "Step type differs",
|
|
8410
|
-
path:
|
|
8412
|
+
path: path20,
|
|
8411
8413
|
left: L.type,
|
|
8412
8414
|
right: R.type
|
|
8413
8415
|
});
|
|
@@ -8417,7 +8419,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8417
8419
|
kind: "step-status",
|
|
8418
8420
|
severity: "warning",
|
|
8419
8421
|
message: "Step status differs",
|
|
8420
|
-
path:
|
|
8422
|
+
path: path20,
|
|
8421
8423
|
left: L.status,
|
|
8422
8424
|
right: R.status
|
|
8423
8425
|
});
|
|
@@ -8429,7 +8431,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8429
8431
|
kind: "error",
|
|
8430
8432
|
severity: "error",
|
|
8431
8433
|
message: "Step error message differs",
|
|
8432
|
-
path:
|
|
8434
|
+
path: path20,
|
|
8433
8435
|
left: le || void 0,
|
|
8434
8436
|
right: re || void 0
|
|
8435
8437
|
});
|
|
@@ -8447,20 +8449,20 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8447
8449
|
kind: "duration",
|
|
8448
8450
|
severity: "info",
|
|
8449
8451
|
message: "Step duration differs",
|
|
8450
|
-
path:
|
|
8452
|
+
path: path20,
|
|
8451
8453
|
left: ld,
|
|
8452
8454
|
right: rd
|
|
8453
8455
|
});
|
|
8454
8456
|
}
|
|
8455
8457
|
}
|
|
8456
8458
|
const lm = stableJson(L.metadata ?? {});
|
|
8457
|
-
const
|
|
8458
|
-
if (lm !==
|
|
8459
|
+
const rm3 = stableJson(R.metadata ?? {});
|
|
8460
|
+
if (lm !== rm3) {
|
|
8459
8461
|
out.push({
|
|
8460
8462
|
kind: "metadata",
|
|
8461
8463
|
severity: "info",
|
|
8462
8464
|
message: "Step metadata differs",
|
|
8463
|
-
path:
|
|
8465
|
+
path: path20,
|
|
8464
8466
|
left: L.metadata,
|
|
8465
8467
|
right: R.metadata
|
|
8466
8468
|
});
|
|
@@ -8472,7 +8474,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8472
8474
|
kind: "output",
|
|
8473
8475
|
severity: "info",
|
|
8474
8476
|
message: "Output preview differs",
|
|
8475
|
-
path:
|
|
8477
|
+
path: path20,
|
|
8476
8478
|
left: L.outputPreview,
|
|
8477
8479
|
right: R.outputPreview
|
|
8478
8480
|
});
|
|
@@ -8632,11 +8634,11 @@ function diffRuns(left, right, options) {
|
|
|
8632
8634
|
}
|
|
8633
8635
|
|
|
8634
8636
|
// packages/core/src/diff/renderer.ts
|
|
8635
|
-
function formatPath(
|
|
8636
|
-
if (
|
|
8637
|
+
function formatPath(path20) {
|
|
8638
|
+
if (path20 === void 0 || path20.path.length === 0) {
|
|
8637
8639
|
return "(run)";
|
|
8638
8640
|
}
|
|
8639
|
-
return
|
|
8641
|
+
return path20.path.map((s) => s.name).join(" > ");
|
|
8640
8642
|
}
|
|
8641
8643
|
function formatValue(v, verbose) {
|
|
8642
8644
|
if (v === void 0) return "(undefined)";
|
|
@@ -9518,17 +9520,17 @@ function applyRule(rule, value, replacement) {
|
|
|
9518
9520
|
}
|
|
9519
9521
|
return value;
|
|
9520
9522
|
}
|
|
9521
|
-
function childPath(
|
|
9523
|
+
function childPath(path20, key) {
|
|
9522
9524
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
9523
|
-
return
|
|
9525
|
+
return path20 ? `${path20}.${key}` : key;
|
|
9524
9526
|
}
|
|
9525
|
-
return `${
|
|
9527
|
+
return `${path20 || "$"}[${JSON.stringify(key)}]`;
|
|
9526
9528
|
}
|
|
9527
|
-
function indexPath(
|
|
9528
|
-
return `${
|
|
9529
|
+
function indexPath(path20, index) {
|
|
9530
|
+
return `${path20 || "$"}[${index}]`;
|
|
9529
9531
|
}
|
|
9530
|
-
function makeFinding(
|
|
9531
|
-
return preview === void 0 ? { path:
|
|
9532
|
+
function makeFinding(path20, detector, action, matchKind, severity = "warning", preview) {
|
|
9533
|
+
return preview === void 0 ? { path: path20, detector, action, severity, matchKind } : { path: path20, detector, action, severity, matchKind, preview };
|
|
9532
9534
|
}
|
|
9533
9535
|
function createRedactionProfile(profile = "local") {
|
|
9534
9536
|
switch (profile) {
|
|
@@ -9597,11 +9599,11 @@ var Redactor2 = class {
|
|
|
9597
9599
|
#recordFinding(state, finding) {
|
|
9598
9600
|
if (this.#collectFindings) state.findings.push(finding);
|
|
9599
9601
|
}
|
|
9600
|
-
#redactValue(value, key,
|
|
9602
|
+
#redactValue(value, key, path20, depth, state) {
|
|
9601
9603
|
if (depth > this.#maxDepth) {
|
|
9602
9604
|
this.#recordFinding(
|
|
9603
9605
|
state,
|
|
9604
|
-
makeFinding(
|
|
9606
|
+
makeFinding(path20, "structure.maxDepth", "truncate", "value", "warning")
|
|
9605
9607
|
);
|
|
9606
9608
|
return "[Truncated]";
|
|
9607
9609
|
}
|
|
@@ -9610,19 +9612,19 @@ var Redactor2 = class {
|
|
|
9610
9612
|
if (rule) {
|
|
9611
9613
|
this.#recordFinding(
|
|
9612
9614
|
state,
|
|
9613
|
-
makeFinding(
|
|
9615
|
+
makeFinding(path20, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
9614
9616
|
);
|
|
9615
9617
|
return applyRule(rule, value, this.#replacement);
|
|
9616
9618
|
}
|
|
9617
9619
|
}
|
|
9618
9620
|
for (const detector of this.#detectors) {
|
|
9619
|
-
const detections = detector.detect({ path:
|
|
9621
|
+
const detections = detector.detect({ path: path20, key, value });
|
|
9620
9622
|
for (const detection of detections) {
|
|
9621
9623
|
const action = detection.action ?? "replace";
|
|
9622
9624
|
this.#recordFinding(
|
|
9623
9625
|
state,
|
|
9624
9626
|
makeFinding(
|
|
9625
|
-
|
|
9627
|
+
path20,
|
|
9626
9628
|
detector.id,
|
|
9627
9629
|
action,
|
|
9628
9630
|
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
@@ -9640,7 +9642,7 @@ var Redactor2 = class {
|
|
|
9640
9642
|
const out = [];
|
|
9641
9643
|
state.seen.set(value, out);
|
|
9642
9644
|
value.forEach((item, index) => {
|
|
9643
|
-
out[index] = this.#redactValue(item, void 0, indexPath(
|
|
9645
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path20, index), depth + 1, state);
|
|
9644
9646
|
});
|
|
9645
9647
|
return out;
|
|
9646
9648
|
}
|
|
@@ -9652,7 +9654,7 @@ var Redactor2 = class {
|
|
|
9652
9654
|
out[entryKey] = this.#redactValue(
|
|
9653
9655
|
entryValue,
|
|
9654
9656
|
entryKey,
|
|
9655
|
-
childPath(
|
|
9657
|
+
childPath(path20 === "$" ? "" : path20, entryKey),
|
|
9656
9658
|
depth + 1,
|
|
9657
9659
|
state
|
|
9658
9660
|
);
|
|
@@ -10524,7 +10526,7 @@ function stripPrefix(name, prefixes) {
|
|
|
10524
10526
|
}
|
|
10525
10527
|
return name;
|
|
10526
10528
|
}
|
|
10527
|
-
function eventEvidence(event,
|
|
10529
|
+
function eventEvidence(event, path20) {
|
|
10528
10530
|
return {
|
|
10529
10531
|
runId: event.runId,
|
|
10530
10532
|
eventId: event.eventId,
|
|
@@ -10534,7 +10536,7 @@ function eventEvidence(event, path19) {
|
|
|
10534
10536
|
kind: event.kind,
|
|
10535
10537
|
name: event.name,
|
|
10536
10538
|
status: event.status,
|
|
10537
|
-
...
|
|
10539
|
+
...path20 ? { path: path20 } : {}
|
|
10538
10540
|
};
|
|
10539
10541
|
}
|
|
10540
10542
|
function runEvidence(run) {
|
|
@@ -10597,9 +10599,9 @@ function eventEndMs(event) {
|
|
|
10597
10599
|
function normalizedKey(value) {
|
|
10598
10600
|
return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
10599
10601
|
}
|
|
10600
|
-
function lastPathSegment(
|
|
10601
|
-
const parts =
|
|
10602
|
-
return parts[parts.length - 1] ??
|
|
10602
|
+
function lastPathSegment(path20) {
|
|
10603
|
+
const parts = path20.split(".");
|
|
10604
|
+
return parts[parts.length - 1] ?? path20;
|
|
10603
10605
|
}
|
|
10604
10606
|
function valueType(value) {
|
|
10605
10607
|
if (Array.isArray(value)) return "array";
|
|
@@ -10613,12 +10615,12 @@ function serializedByteLength(value) {
|
|
|
10613
10615
|
return void 0;
|
|
10614
10616
|
}
|
|
10615
10617
|
}
|
|
10616
|
-
function pushValueEntries(entries, event, value,
|
|
10617
|
-
entries.push({ event, path:
|
|
10618
|
+
function pushValueEntries(entries, event, value, path20, key, depth = 0) {
|
|
10619
|
+
entries.push({ event, path: path20, key, value });
|
|
10618
10620
|
if (depth >= 8) return;
|
|
10619
10621
|
if (Array.isArray(value)) {
|
|
10620
10622
|
for (const [index, item] of value.entries()) {
|
|
10621
|
-
pushValueEntries(entries, event, item, `${
|
|
10623
|
+
pushValueEntries(entries, event, item, `${path20}.${index}`, String(index), depth + 1);
|
|
10622
10624
|
}
|
|
10623
10625
|
return;
|
|
10624
10626
|
}
|
|
@@ -10628,7 +10630,7 @@ function pushValueEntries(entries, event, value, path19, key, depth = 0) {
|
|
|
10628
10630
|
entries,
|
|
10629
10631
|
event,
|
|
10630
10632
|
value[nestedKey],
|
|
10631
|
-
`${
|
|
10633
|
+
`${path20}.${nestedKey}`,
|
|
10632
10634
|
nestedKey,
|
|
10633
10635
|
depth + 1
|
|
10634
10636
|
);
|
|
@@ -10709,9 +10711,9 @@ function eventDurationMs(event) {
|
|
|
10709
10711
|
}
|
|
10710
10712
|
function treeShape(nodes) {
|
|
10711
10713
|
const lines = [];
|
|
10712
|
-
const visit = (node,
|
|
10713
|
-
lines.push(`${
|
|
10714
|
-
node.children.forEach((child, index) => visit(child, `${
|
|
10714
|
+
const visit = (node, path20) => {
|
|
10715
|
+
lines.push(`${path20}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
|
|
10716
|
+
node.children.forEach((child, index) => visit(child, `${path20}.${index}`));
|
|
10715
10717
|
};
|
|
10716
10718
|
nodes.forEach((node, index) => visit(node, String(index)));
|
|
10717
10719
|
return lines;
|
|
@@ -10760,9 +10762,9 @@ function retrievalShape(context) {
|
|
|
10760
10762
|
function guardrailShape(context) {
|
|
10761
10763
|
return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
|
|
10762
10764
|
}
|
|
10763
|
-
function firstEvidenceForKind(context, kind,
|
|
10765
|
+
function firstEvidenceForKind(context, kind, path20) {
|
|
10764
10766
|
const event = context.events.find((candidate) => candidate.kind === kind);
|
|
10765
|
-
return event ? [eventEvidence(event,
|
|
10767
|
+
return event ? [eventEvidence(event, path20)] : runEvidence(context.selectedRun);
|
|
10766
10768
|
}
|
|
10767
10769
|
function baselineDiffFinding(message, evidence, expected, actual) {
|
|
10768
10770
|
return failFinding("baseline.regression", message, evidence, expected, actual);
|
|
@@ -11112,13 +11114,13 @@ function createStructureCycleRule() {
|
|
|
11112
11114
|
const seenCycles = /* @__PURE__ */ new Set();
|
|
11113
11115
|
const findings = [];
|
|
11114
11116
|
for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
|
|
11115
|
-
const
|
|
11117
|
+
const path20 = [];
|
|
11116
11118
|
const seenAt = /* @__PURE__ */ new Map();
|
|
11117
11119
|
let current = event;
|
|
11118
11120
|
while (current) {
|
|
11119
11121
|
const existing = seenAt.get(current.eventId);
|
|
11120
11122
|
if (existing !== void 0) {
|
|
11121
|
-
const cycle =
|
|
11123
|
+
const cycle = path20.slice(existing);
|
|
11122
11124
|
const key = cycle.map((item) => item.eventId).sort().join("\0");
|
|
11123
11125
|
if (!seenCycles.has(key)) {
|
|
11124
11126
|
seenCycles.add(key);
|
|
@@ -11134,8 +11136,8 @@ function createStructureCycleRule() {
|
|
|
11134
11136
|
}
|
|
11135
11137
|
break;
|
|
11136
11138
|
}
|
|
11137
|
-
seenAt.set(current.eventId,
|
|
11138
|
-
|
|
11139
|
+
seenAt.set(current.eventId, path20.length);
|
|
11140
|
+
path20.push(current);
|
|
11139
11141
|
current = current.parentId ? byId.get(current.parentId) : void 0;
|
|
11140
11142
|
}
|
|
11141
11143
|
}
|
|
@@ -11932,23 +11934,23 @@ function evaluatePromptInjection(text, options = {}) {
|
|
|
11932
11934
|
}
|
|
11933
11935
|
return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
|
|
11934
11936
|
}
|
|
11935
|
-
function validateSchemaField(value, field,
|
|
11937
|
+
function validateSchemaField(value, field, path20, evidence) {
|
|
11936
11938
|
const ruleId = "guardrail.structured-output";
|
|
11937
11939
|
if (field.type) {
|
|
11938
11940
|
const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
11939
11941
|
if (actual !== field.type) {
|
|
11940
|
-
evidence.push({ ruleId, path:
|
|
11942
|
+
evidence.push({ ruleId, path: path20, preview: `expected ${field.type}, got ${actual}` });
|
|
11941
11943
|
return;
|
|
11942
11944
|
}
|
|
11943
11945
|
}
|
|
11944
11946
|
if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
|
|
11945
|
-
evidence.push({ ruleId, path:
|
|
11947
|
+
evidence.push({ ruleId, path: path20, preview: "value not in enum" });
|
|
11946
11948
|
}
|
|
11947
11949
|
if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
|
|
11948
11950
|
const record = value;
|
|
11949
11951
|
for (const key of field.required) {
|
|
11950
11952
|
if (!(key in record)) {
|
|
11951
|
-
evidence.push({ ruleId, path: `${
|
|
11953
|
+
evidence.push({ ruleId, path: `${path20}.${key}`, preview: "missing required key" });
|
|
11952
11954
|
}
|
|
11953
11955
|
}
|
|
11954
11956
|
}
|
|
@@ -12433,10 +12435,10 @@ function printHuman(result) {
|
|
|
12433
12435
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
12434
12436
|
}
|
|
12435
12437
|
for (const finding of result.findings) {
|
|
12436
|
-
const
|
|
12438
|
+
const path20 = finding.evidence[0]?.path;
|
|
12437
12439
|
const run = finding.evidence[0]?.runId;
|
|
12438
12440
|
const runPrefix = run ? `[${run}] ` : "";
|
|
12439
|
-
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${
|
|
12441
|
+
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path20 ? ` (${path20})` : ""}`);
|
|
12440
12442
|
}
|
|
12441
12443
|
}
|
|
12442
12444
|
function readErrorResult(error) {
|
|
@@ -12974,10 +12976,10 @@ async function evalRun(input3, options = {}) {
|
|
|
12974
12976
|
diagnostics: []
|
|
12975
12977
|
};
|
|
12976
12978
|
}
|
|
12977
|
-
function evidenceForRun(run,
|
|
12978
|
-
return [{ runId: run.runId, ...
|
|
12979
|
+
function evidenceForRun(run, path20) {
|
|
12980
|
+
return [{ runId: run.runId, ...path20 !== void 0 ? { path: path20 } : {} }];
|
|
12979
12981
|
}
|
|
12980
|
-
function evidenceForEvent(event,
|
|
12982
|
+
function evidenceForEvent(event, path20) {
|
|
12981
12983
|
return [
|
|
12982
12984
|
{
|
|
12983
12985
|
runId: event.runId,
|
|
@@ -12985,7 +12987,7 @@ function evidenceForEvent(event, path19) {
|
|
|
12985
12987
|
...event.parentId !== void 0 ? { parentId: event.parentId } : {},
|
|
12986
12988
|
kind: event.kind,
|
|
12987
12989
|
name: event.name,
|
|
12988
|
-
...
|
|
12990
|
+
...path20 !== void 0 ? { path: path20 } : {}
|
|
12989
12991
|
}
|
|
12990
12992
|
];
|
|
12991
12993
|
}
|
|
@@ -13143,9 +13145,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
|
|
|
13143
13145
|
function tokenize(text) {
|
|
13144
13146
|
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));
|
|
13145
13147
|
}
|
|
13146
|
-
function firstEvidence(fields, run,
|
|
13148
|
+
function firstEvidence(fields, run, path20) {
|
|
13147
13149
|
const first = fields[0];
|
|
13148
|
-
return first === void 0 ? evidenceForRun(run,
|
|
13150
|
+
return first === void 0 ? evidenceForRun(run, path20) : evidenceForEvent(first.node.event, first.path);
|
|
13149
13151
|
}
|
|
13150
13152
|
function collectSourceIds(nodes, keys) {
|
|
13151
13153
|
const wanted = keySet(keys);
|
|
@@ -13522,8 +13524,8 @@ function renderEvalMarkdown(result) {
|
|
|
13522
13524
|
if (result.findings.length > 0) {
|
|
13523
13525
|
lines.push("", "## Findings");
|
|
13524
13526
|
for (const finding of result.findings) {
|
|
13525
|
-
const
|
|
13526
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
13527
|
+
const path20 = finding.evidence[0]?.path;
|
|
13528
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path20 ? ` (${path20})` : ""}`);
|
|
13527
13529
|
}
|
|
13528
13530
|
}
|
|
13529
13531
|
return `${lines.join("\n")}
|
|
@@ -13716,8 +13718,8 @@ function printHuman2(result) {
|
|
|
13716
13718
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
13717
13719
|
}
|
|
13718
13720
|
for (const finding of result.findings) {
|
|
13719
|
-
const
|
|
13720
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
13721
|
+
const path20 = finding.evidence[0]?.path;
|
|
13722
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path20 ? ` (${path20})` : ""}`);
|
|
13721
13723
|
}
|
|
13722
13724
|
}
|
|
13723
13725
|
function readErrorResult2(error) {
|
|
@@ -13955,8 +13957,8 @@ function printHuman3(result) {
|
|
|
13955
13957
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
13956
13958
|
}
|
|
13957
13959
|
for (const finding of result.findings) {
|
|
13958
|
-
const
|
|
13959
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
13960
|
+
const path20 = finding.evidence[0]?.path;
|
|
13961
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path20 ? ` (${path20})` : ""}`);
|
|
13960
13962
|
}
|
|
13961
13963
|
console.log(`Note: ${result.note}`);
|
|
13962
13964
|
}
|
|
@@ -14075,8 +14077,8 @@ function renderCheckSection(result) {
|
|
|
14075
14077
|
`Diagnostics: ${result.diagnostics.length}`
|
|
14076
14078
|
];
|
|
14077
14079
|
for (const finding of result.findings.slice(0, 10)) {
|
|
14078
|
-
const
|
|
14079
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
14080
|
+
const path20 = finding.evidence[0]?.path ?? "(run)";
|
|
14081
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path20})`);
|
|
14080
14082
|
}
|
|
14081
14083
|
for (const diagnostic4 of result.diagnostics.slice(0, 10)) {
|
|
14082
14084
|
lines.push(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
@@ -15246,6 +15248,608 @@ async function indexCleanCommand(options = {}) {
|
|
|
15246
15248
|
}
|
|
15247
15249
|
}
|
|
15248
15250
|
|
|
15251
|
+
// packages/core/src/workspace/types.ts
|
|
15252
|
+
var WORKSPACE_SCHEMA_VERSION = "1.0";
|
|
15253
|
+
var WORKSPACE_DIR_NAME = ".agent-inspect";
|
|
15254
|
+
var WORKSPACE_MANIFEST_FILENAME = "workspace.json";
|
|
15255
|
+
|
|
15256
|
+
// packages/core/src/workspace/manifest.ts
|
|
15257
|
+
var DEFAULT_WORKSPACE_LAYOUT = {
|
|
15258
|
+
traceDirs: ["runs"],
|
|
15259
|
+
reportsDir: "reports",
|
|
15260
|
+
artifactsDir: "artifacts",
|
|
15261
|
+
bundlesDir: "bundles",
|
|
15262
|
+
notesDir: "notes"
|
|
15263
|
+
};
|
|
15264
|
+
var DEFAULT_REDACTION_PROFILE = "share";
|
|
15265
|
+
var REDACTION_PROFILES2 = [
|
|
15266
|
+
"local",
|
|
15267
|
+
"share",
|
|
15268
|
+
"strict"
|
|
15269
|
+
];
|
|
15270
|
+
var INDEX_TYPES = ["none", "sqlite", "custom"];
|
|
15271
|
+
var MAX_WORKSPACE_MANIFEST_BYTES = 64 * 1024;
|
|
15272
|
+
function createDefaultWorkspaceManifest(options) {
|
|
15273
|
+
const project = typeof options.project === "string" ? options.project.trim() : "";
|
|
15274
|
+
const index = {
|
|
15275
|
+
enabled: options.index?.enabled ?? false,
|
|
15276
|
+
type: options.index?.type ?? "none",
|
|
15277
|
+
...options.index?.path !== void 0 ? { path: options.index.path } : {}
|
|
15278
|
+
};
|
|
15279
|
+
return {
|
|
15280
|
+
schemaVersion: WORKSPACE_SCHEMA_VERSION,
|
|
15281
|
+
project,
|
|
15282
|
+
createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
15283
|
+
traceDirs: options.traceDirs ? [...options.traceDirs] : [...DEFAULT_WORKSPACE_LAYOUT.traceDirs],
|
|
15284
|
+
reportsDir: options.reportsDir ?? DEFAULT_WORKSPACE_LAYOUT.reportsDir,
|
|
15285
|
+
artifactsDir: options.artifactsDir ?? DEFAULT_WORKSPACE_LAYOUT.artifactsDir,
|
|
15286
|
+
bundlesDir: options.bundlesDir ?? DEFAULT_WORKSPACE_LAYOUT.bundlesDir,
|
|
15287
|
+
notesDir: options.notesDir ?? DEFAULT_WORKSPACE_LAYOUT.notesDir,
|
|
15288
|
+
redactionProfile: options.redactionProfile ?? DEFAULT_REDACTION_PROFILE,
|
|
15289
|
+
index
|
|
15290
|
+
};
|
|
15291
|
+
}
|
|
15292
|
+
function isPlainObject(value) {
|
|
15293
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15294
|
+
}
|
|
15295
|
+
function isSafeRelativeWorkspacePath(p) {
|
|
15296
|
+
if (typeof p !== "string") return false;
|
|
15297
|
+
const trimmed = p.trim();
|
|
15298
|
+
if (trimmed === "") return false;
|
|
15299
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("\\")) return false;
|
|
15300
|
+
if (/^[a-zA-Z]:/.test(trimmed)) return false;
|
|
15301
|
+
const segments = trimmed.split(/[/\\]+/);
|
|
15302
|
+
return !segments.some((seg) => seg === "..");
|
|
15303
|
+
}
|
|
15304
|
+
function validateDirField(value, field, errors) {
|
|
15305
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
15306
|
+
errors.push(`${field} must be a non-empty string`);
|
|
15307
|
+
return;
|
|
15308
|
+
}
|
|
15309
|
+
if (!isSafeRelativeWorkspacePath(value)) {
|
|
15310
|
+
errors.push(
|
|
15311
|
+
`${field} must be a relative path inside the workspace (no absolute paths or ".." traversal)`
|
|
15312
|
+
);
|
|
15313
|
+
}
|
|
15314
|
+
}
|
|
15315
|
+
function validateIndex(value, errors) {
|
|
15316
|
+
if (!isPlainObject(value)) {
|
|
15317
|
+
errors.push("index must be an object");
|
|
15318
|
+
return void 0;
|
|
15319
|
+
}
|
|
15320
|
+
if (typeof value.enabled !== "boolean") {
|
|
15321
|
+
errors.push("index.enabled must be a boolean");
|
|
15322
|
+
}
|
|
15323
|
+
if (!INDEX_TYPES.includes(value.type)) {
|
|
15324
|
+
errors.push(`index.type must be one of: ${INDEX_TYPES.join(", ")}`);
|
|
15325
|
+
}
|
|
15326
|
+
if (value.path !== void 0 && !isSafeRelativeWorkspacePath(value.path)) {
|
|
15327
|
+
errors.push(
|
|
15328
|
+
'index.path must be a relative path inside the workspace (no absolute paths or ".." traversal)'
|
|
15329
|
+
);
|
|
15330
|
+
}
|
|
15331
|
+
if (errors.length > 0) return void 0;
|
|
15332
|
+
return {
|
|
15333
|
+
enabled: value.enabled,
|
|
15334
|
+
type: value.type,
|
|
15335
|
+
...value.path !== void 0 ? { path: value.path } : {}
|
|
15336
|
+
};
|
|
15337
|
+
}
|
|
15338
|
+
function validateWorkspaceManifest(input3) {
|
|
15339
|
+
const errors = [];
|
|
15340
|
+
const warnings = [];
|
|
15341
|
+
if (!isPlainObject(input3)) {
|
|
15342
|
+
return { ok: false, errors: ["manifest must be an object"], warnings };
|
|
15343
|
+
}
|
|
15344
|
+
if (input3.schemaVersion !== WORKSPACE_SCHEMA_VERSION) {
|
|
15345
|
+
errors.push(
|
|
15346
|
+
`schemaVersion must be "${WORKSPACE_SCHEMA_VERSION}" (received ${JSON.stringify(
|
|
15347
|
+
input3.schemaVersion
|
|
15348
|
+
)})`
|
|
15349
|
+
);
|
|
15350
|
+
}
|
|
15351
|
+
if (typeof input3.project !== "string" || input3.project.trim() === "") {
|
|
15352
|
+
errors.push("project must be a non-empty string");
|
|
15353
|
+
}
|
|
15354
|
+
if (typeof input3.createdAt !== "string" || input3.createdAt.trim() === "") {
|
|
15355
|
+
errors.push("createdAt must be a non-empty ISO-8601 string");
|
|
15356
|
+
} else if (Number.isNaN(Date.parse(input3.createdAt))) {
|
|
15357
|
+
errors.push("createdAt must be a valid ISO-8601 date string");
|
|
15358
|
+
}
|
|
15359
|
+
if (!Array.isArray(input3.traceDirs) || input3.traceDirs.length === 0) {
|
|
15360
|
+
errors.push("traceDirs must be a non-empty array");
|
|
15361
|
+
} else {
|
|
15362
|
+
input3.traceDirs.forEach((dir, i) => {
|
|
15363
|
+
if (typeof dir !== "string" || dir.trim() === "") {
|
|
15364
|
+
errors.push(`traceDirs[${i}] must be a non-empty string`);
|
|
15365
|
+
} else if (!isSafeRelativeWorkspacePath(dir)) {
|
|
15366
|
+
errors.push(
|
|
15367
|
+
`traceDirs[${i}] must be a relative path inside the workspace (no absolute paths or ".." traversal)`
|
|
15368
|
+
);
|
|
15369
|
+
}
|
|
15370
|
+
});
|
|
15371
|
+
}
|
|
15372
|
+
validateDirField(input3.reportsDir, "reportsDir", errors);
|
|
15373
|
+
validateDirField(input3.artifactsDir, "artifactsDir", errors);
|
|
15374
|
+
validateDirField(input3.bundlesDir, "bundlesDir", errors);
|
|
15375
|
+
validateDirField(input3.notesDir, "notesDir", errors);
|
|
15376
|
+
if (!REDACTION_PROFILES2.includes(input3.redactionProfile)) {
|
|
15377
|
+
errors.push(`redactionProfile must be one of: ${REDACTION_PROFILES2.join(", ")}`);
|
|
15378
|
+
}
|
|
15379
|
+
const indexErrors = [];
|
|
15380
|
+
const index = validateIndex(input3.index, indexErrors);
|
|
15381
|
+
errors.push(...indexErrors);
|
|
15382
|
+
if (index && index.type !== "none" && !index.enabled) {
|
|
15383
|
+
warnings.push(`index.type is "${index.type}" but index.enabled is false`);
|
|
15384
|
+
}
|
|
15385
|
+
if (errors.length > 0 || index === void 0) {
|
|
15386
|
+
return { ok: false, errors, warnings };
|
|
15387
|
+
}
|
|
15388
|
+
const manifest = {
|
|
15389
|
+
schemaVersion: WORKSPACE_SCHEMA_VERSION,
|
|
15390
|
+
project: input3.project.trim(),
|
|
15391
|
+
createdAt: input3.createdAt,
|
|
15392
|
+
traceDirs: input3.traceDirs.map((d) => d.trim()),
|
|
15393
|
+
reportsDir: input3.reportsDir.trim(),
|
|
15394
|
+
artifactsDir: input3.artifactsDir.trim(),
|
|
15395
|
+
bundlesDir: input3.bundlesDir.trim(),
|
|
15396
|
+
notesDir: input3.notesDir.trim(),
|
|
15397
|
+
redactionProfile: input3.redactionProfile,
|
|
15398
|
+
index
|
|
15399
|
+
};
|
|
15400
|
+
return { ok: true, manifest, errors, warnings };
|
|
15401
|
+
}
|
|
15402
|
+
function parseWorkspaceManifest(json) {
|
|
15403
|
+
const warnings = [];
|
|
15404
|
+
if (typeof json !== "string") {
|
|
15405
|
+
return { ok: false, errors: ["manifest input must be a string"], warnings };
|
|
15406
|
+
}
|
|
15407
|
+
if (json.length > MAX_WORKSPACE_MANIFEST_BYTES) {
|
|
15408
|
+
return {
|
|
15409
|
+
ok: false,
|
|
15410
|
+
errors: [
|
|
15411
|
+
`manifest exceeds maximum size of ${MAX_WORKSPACE_MANIFEST_BYTES} bytes`
|
|
15412
|
+
],
|
|
15413
|
+
warnings
|
|
15414
|
+
};
|
|
15415
|
+
}
|
|
15416
|
+
let parsed;
|
|
15417
|
+
try {
|
|
15418
|
+
parsed = JSON.parse(json);
|
|
15419
|
+
} catch {
|
|
15420
|
+
return { ok: false, errors: ["manifest is not valid JSON"], warnings };
|
|
15421
|
+
}
|
|
15422
|
+
return validateWorkspaceManifest(parsed);
|
|
15423
|
+
}
|
|
15424
|
+
function serializeWorkspaceManifest(manifest) {
|
|
15425
|
+
return `${JSON.stringify(manifest, null, 2)}
|
|
15426
|
+
`;
|
|
15427
|
+
}
|
|
15428
|
+
var INDEX_DIR_NAME = "index";
|
|
15429
|
+
function resolveWorkspaceLocation(cwd = process.cwd()) {
|
|
15430
|
+
const projectRoot = path14.resolve(cwd);
|
|
15431
|
+
const workspaceDir = path14.join(projectRoot, WORKSPACE_DIR_NAME);
|
|
15432
|
+
return {
|
|
15433
|
+
projectRoot,
|
|
15434
|
+
workspaceDir,
|
|
15435
|
+
manifestPath: path14.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
|
|
15436
|
+
};
|
|
15437
|
+
}
|
|
15438
|
+
function resolveInsideWorkspace(workspaceDir, relative) {
|
|
15439
|
+
const base = path14.resolve(workspaceDir);
|
|
15440
|
+
const resolved = path14.resolve(base, relative);
|
|
15441
|
+
const rel = path14.relative(base, resolved);
|
|
15442
|
+
if (rel === "" || rel === "." || !rel.startsWith("..") && !path14.isAbsolute(rel)) {
|
|
15443
|
+
return resolved;
|
|
15444
|
+
}
|
|
15445
|
+
throw new Error(
|
|
15446
|
+
`Workspace path "${relative}" resolves outside the workspace directory`
|
|
15447
|
+
);
|
|
15448
|
+
}
|
|
15449
|
+
async function pathExists(p) {
|
|
15450
|
+
try {
|
|
15451
|
+
await access(p);
|
|
15452
|
+
return true;
|
|
15453
|
+
} catch {
|
|
15454
|
+
return false;
|
|
15455
|
+
}
|
|
15456
|
+
}
|
|
15457
|
+
async function isWritable(p) {
|
|
15458
|
+
try {
|
|
15459
|
+
await access(p, constants$1.W_OK);
|
|
15460
|
+
return true;
|
|
15461
|
+
} catch {
|
|
15462
|
+
return false;
|
|
15463
|
+
}
|
|
15464
|
+
}
|
|
15465
|
+
async function listJsonl(dir) {
|
|
15466
|
+
try {
|
|
15467
|
+
const entries = await readdir(dir);
|
|
15468
|
+
return entries.filter((f) => f.endsWith(".jsonl"));
|
|
15469
|
+
} catch {
|
|
15470
|
+
return [];
|
|
15471
|
+
}
|
|
15472
|
+
}
|
|
15473
|
+
async function countFiles(dir) {
|
|
15474
|
+
try {
|
|
15475
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
15476
|
+
return entries.filter((e) => e.isFile()).length;
|
|
15477
|
+
} catch {
|
|
15478
|
+
return 0;
|
|
15479
|
+
}
|
|
15480
|
+
}
|
|
15481
|
+
async function readWorkspaceManifestFile(location) {
|
|
15482
|
+
let raw;
|
|
15483
|
+
try {
|
|
15484
|
+
raw = await readFile(location.manifestPath, "utf-8");
|
|
15485
|
+
} catch {
|
|
15486
|
+
return { exists: false, ok: false, errors: ["workspace.json not found"], warnings: [] };
|
|
15487
|
+
}
|
|
15488
|
+
const parsed = parseWorkspaceManifest(raw);
|
|
15489
|
+
return {
|
|
15490
|
+
exists: true,
|
|
15491
|
+
ok: parsed.ok,
|
|
15492
|
+
...parsed.manifest ? { manifest: parsed.manifest } : {},
|
|
15493
|
+
errors: parsed.errors,
|
|
15494
|
+
warnings: parsed.warnings
|
|
15495
|
+
};
|
|
15496
|
+
}
|
|
15497
|
+
async function createWorkspace(options = {}) {
|
|
15498
|
+
const location = resolveWorkspaceLocation(options.cwd);
|
|
15499
|
+
const dryRun = options.dryRun === true;
|
|
15500
|
+
const existing = await readWorkspaceManifestFile(location);
|
|
15501
|
+
const topLevelTraces = await listJsonl(location.workspaceDir);
|
|
15502
|
+
const detectedExistingTraces = topLevelTraces.length > 0;
|
|
15503
|
+
let manifest;
|
|
15504
|
+
let created;
|
|
15505
|
+
let adopted;
|
|
15506
|
+
if (existing.exists && existing.ok && existing.manifest) {
|
|
15507
|
+
manifest = existing.manifest;
|
|
15508
|
+
created = false;
|
|
15509
|
+
adopted = true;
|
|
15510
|
+
} else {
|
|
15511
|
+
const project = options.project?.trim() || path14.basename(location.projectRoot) || "workspace";
|
|
15512
|
+
const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
|
|
15513
|
+
manifest = createDefaultWorkspaceManifest({
|
|
15514
|
+
project,
|
|
15515
|
+
traceDirs,
|
|
15516
|
+
...options.redactionProfile ? { redactionProfile: options.redactionProfile } : {}
|
|
15517
|
+
});
|
|
15518
|
+
created = true;
|
|
15519
|
+
adopted = detectedExistingTraces || existing.exists && !existing.ok;
|
|
15520
|
+
}
|
|
15521
|
+
const relDirs = uniqueDirs([
|
|
15522
|
+
...manifest.traceDirs.filter((d) => d !== "."),
|
|
15523
|
+
manifest.reportsDir,
|
|
15524
|
+
manifest.artifactsDir,
|
|
15525
|
+
manifest.bundlesDir,
|
|
15526
|
+
manifest.notesDir,
|
|
15527
|
+
INDEX_DIR_NAME
|
|
15528
|
+
]);
|
|
15529
|
+
const createdDirs = [];
|
|
15530
|
+
for (const rel of relDirs) {
|
|
15531
|
+
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
15532
|
+
if (await pathExists(abs)) continue;
|
|
15533
|
+
createdDirs.push(rel);
|
|
15534
|
+
if (!dryRun) await mkdir(abs, { recursive: true });
|
|
15535
|
+
}
|
|
15536
|
+
if (!dryRun && created) {
|
|
15537
|
+
await mkdir(location.workspaceDir, { recursive: true });
|
|
15538
|
+
await writeFile(location.manifestPath, serializeWorkspaceManifest(manifest), "utf-8");
|
|
15539
|
+
}
|
|
15540
|
+
return {
|
|
15541
|
+
location,
|
|
15542
|
+
manifest,
|
|
15543
|
+
created,
|
|
15544
|
+
adopted,
|
|
15545
|
+
createdDirs,
|
|
15546
|
+
detectedExistingTraces,
|
|
15547
|
+
dryRun
|
|
15548
|
+
};
|
|
15549
|
+
}
|
|
15550
|
+
function uniqueDirs(dirs) {
|
|
15551
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15552
|
+
const out = [];
|
|
15553
|
+
for (const d of dirs) {
|
|
15554
|
+
const t = d.trim();
|
|
15555
|
+
if (t === "" || t === "." || seen.has(t)) continue;
|
|
15556
|
+
seen.add(t);
|
|
15557
|
+
out.push(t);
|
|
15558
|
+
}
|
|
15559
|
+
return out;
|
|
15560
|
+
}
|
|
15561
|
+
async function getWorkspaceStatus(location, manifest) {
|
|
15562
|
+
let traceFiles = 0;
|
|
15563
|
+
for (const rel of manifest.traceDirs) {
|
|
15564
|
+
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
15565
|
+
traceFiles += (await listJsonl(abs)).length;
|
|
15566
|
+
}
|
|
15567
|
+
const reports = await countFiles(
|
|
15568
|
+
resolveInsideWorkspace(location.workspaceDir, manifest.reportsDir)
|
|
15569
|
+
);
|
|
15570
|
+
const artifacts = await countFiles(
|
|
15571
|
+
resolveInsideWorkspace(location.workspaceDir, manifest.artifactsDir)
|
|
15572
|
+
);
|
|
15573
|
+
const bundles = await countFiles(
|
|
15574
|
+
resolveInsideWorkspace(location.workspaceDir, manifest.bundlesDir)
|
|
15575
|
+
);
|
|
15576
|
+
const notes = await countFiles(
|
|
15577
|
+
resolveInsideWorkspace(location.workspaceDir, manifest.notesDir)
|
|
15578
|
+
);
|
|
15579
|
+
const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
|
|
15580
|
+
return {
|
|
15581
|
+
project: manifest.project,
|
|
15582
|
+
traceFiles,
|
|
15583
|
+
reports,
|
|
15584
|
+
artifacts,
|
|
15585
|
+
bundles,
|
|
15586
|
+
notes,
|
|
15587
|
+
index: {
|
|
15588
|
+
enabled: manifest.index.enabled,
|
|
15589
|
+
type: manifest.index.type,
|
|
15590
|
+
exists: await pathExists(indexPath2)
|
|
15591
|
+
}
|
|
15592
|
+
};
|
|
15593
|
+
}
|
|
15594
|
+
async function doctorWorkspace(location) {
|
|
15595
|
+
const checks2 = [];
|
|
15596
|
+
const manifestResult = await readWorkspaceManifestFile(location);
|
|
15597
|
+
if (!manifestResult.exists) {
|
|
15598
|
+
checks2.push({
|
|
15599
|
+
id: "manifest",
|
|
15600
|
+
status: "fail",
|
|
15601
|
+
message: "workspace.json not found (run `agent-inspect workspace init`)"
|
|
15602
|
+
});
|
|
15603
|
+
return { ok: false, checks: checks2 };
|
|
15604
|
+
}
|
|
15605
|
+
if (!manifestResult.ok || !manifestResult.manifest) {
|
|
15606
|
+
checks2.push({
|
|
15607
|
+
id: "manifest",
|
|
15608
|
+
status: "fail",
|
|
15609
|
+
message: `workspace.json is invalid: ${manifestResult.errors.join("; ")}`
|
|
15610
|
+
});
|
|
15611
|
+
return { ok: false, checks: checks2 };
|
|
15612
|
+
}
|
|
15613
|
+
const manifest = manifestResult.manifest;
|
|
15614
|
+
checks2.push({ id: "manifest", status: "pass", message: "workspace.json is valid" });
|
|
15615
|
+
for (const warning of manifestResult.warnings) {
|
|
15616
|
+
checks2.push({ id: "manifest-warning", status: "warn", message: warning });
|
|
15617
|
+
}
|
|
15618
|
+
const dirFields = [
|
|
15619
|
+
...manifest.traceDirs.filter((d) => d !== ".").map((d, i) => [`traceDir[${i}]`, d]),
|
|
15620
|
+
["reportsDir", manifest.reportsDir],
|
|
15621
|
+
["artifactsDir", manifest.artifactsDir],
|
|
15622
|
+
["bundlesDir", manifest.bundlesDir],
|
|
15623
|
+
["notesDir", manifest.notesDir]
|
|
15624
|
+
];
|
|
15625
|
+
for (const [id, rel] of dirFields) {
|
|
15626
|
+
let abs;
|
|
15627
|
+
try {
|
|
15628
|
+
abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
15629
|
+
} catch (error) {
|
|
15630
|
+
checks2.push({
|
|
15631
|
+
id,
|
|
15632
|
+
status: "fail",
|
|
15633
|
+
message: error instanceof Error ? error.message : String(error)
|
|
15634
|
+
});
|
|
15635
|
+
continue;
|
|
15636
|
+
}
|
|
15637
|
+
if (!await pathExists(abs)) {
|
|
15638
|
+
checks2.push({ id, status: "warn", message: `${rel}/ does not exist yet` });
|
|
15639
|
+
} else if (!await isWritable(abs)) {
|
|
15640
|
+
checks2.push({ id, status: "fail", message: `${rel}/ is not writable` });
|
|
15641
|
+
} else {
|
|
15642
|
+
checks2.push({ id, status: "pass", message: `${rel}/ is present and writable` });
|
|
15643
|
+
}
|
|
15644
|
+
}
|
|
15645
|
+
let newestTraceMtime = 0;
|
|
15646
|
+
for (const rel of manifest.traceDirs) {
|
|
15647
|
+
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
15648
|
+
for (const file of await listJsonl(abs)) {
|
|
15649
|
+
try {
|
|
15650
|
+
const s = await stat(path14.join(abs, file));
|
|
15651
|
+
newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
|
|
15652
|
+
} catch {
|
|
15653
|
+
checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
|
|
15654
|
+
}
|
|
15655
|
+
}
|
|
15656
|
+
}
|
|
15657
|
+
if (manifest.index.enabled) {
|
|
15658
|
+
const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
|
|
15659
|
+
if (!await pathExists(indexPath2)) {
|
|
15660
|
+
checks2.push({ id: "index", status: "warn", message: "index enabled but not built" });
|
|
15661
|
+
} else {
|
|
15662
|
+
try {
|
|
15663
|
+
const s = await stat(indexPath2);
|
|
15664
|
+
if (newestTraceMtime > s.mtimeMs) {
|
|
15665
|
+
checks2.push({ id: "index", status: "warn", message: "index is stale (traces are newer)" });
|
|
15666
|
+
} else {
|
|
15667
|
+
checks2.push({ id: "index", status: "pass", message: "index is present" });
|
|
15668
|
+
}
|
|
15669
|
+
} catch {
|
|
15670
|
+
checks2.push({ id: "index", status: "warn", message: "cannot stat index" });
|
|
15671
|
+
}
|
|
15672
|
+
}
|
|
15673
|
+
}
|
|
15674
|
+
const ok = !checks2.some((c) => c.status === "fail");
|
|
15675
|
+
return { ok, checks: checks2 };
|
|
15676
|
+
}
|
|
15677
|
+
async function cleanWorkspace(location, manifest, options = {}) {
|
|
15678
|
+
const dryRun = options.confirm !== true;
|
|
15679
|
+
const targets = uniqueDirs([
|
|
15680
|
+
manifest.reportsDir,
|
|
15681
|
+
manifest.artifactsDir,
|
|
15682
|
+
manifest.bundlesDir,
|
|
15683
|
+
manifest.index.path ?? INDEX_DIR_NAME
|
|
15684
|
+
]);
|
|
15685
|
+
const removed = [];
|
|
15686
|
+
for (const rel of targets) {
|
|
15687
|
+
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
15688
|
+
let entries;
|
|
15689
|
+
try {
|
|
15690
|
+
entries = await readdir(abs);
|
|
15691
|
+
} catch {
|
|
15692
|
+
continue;
|
|
15693
|
+
}
|
|
15694
|
+
for (const entry of entries) {
|
|
15695
|
+
const relPath = `${rel}/${entry}`;
|
|
15696
|
+
removed.push(relPath);
|
|
15697
|
+
if (!dryRun) {
|
|
15698
|
+
await rm(path14.join(abs, entry), { recursive: true, force: true });
|
|
15699
|
+
}
|
|
15700
|
+
}
|
|
15701
|
+
}
|
|
15702
|
+
return { dryRun, removed };
|
|
15703
|
+
}
|
|
15704
|
+
|
|
15705
|
+
// packages/cli/src/workspace.ts
|
|
15706
|
+
function printJson4(payload) {
|
|
15707
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
15708
|
+
}
|
|
15709
|
+
function fail3(json, message) {
|
|
15710
|
+
if (json) {
|
|
15711
|
+
printJson4({ ok: false, error: message });
|
|
15712
|
+
} else {
|
|
15713
|
+
console.error(`[AgentInspect] workspace: ${message}`);
|
|
15714
|
+
}
|
|
15715
|
+
process.exitCode = 1;
|
|
15716
|
+
}
|
|
15717
|
+
async function workspaceInitCommand(options = {}) {
|
|
15718
|
+
try {
|
|
15719
|
+
const result = await createWorkspace({
|
|
15720
|
+
...options.cwd ? { cwd: options.cwd } : {},
|
|
15721
|
+
...options.project ? { project: options.project } : {},
|
|
15722
|
+
...options.redactionProfile ? { redactionProfile: options.redactionProfile } : {},
|
|
15723
|
+
dryRun: options.dryRun === true
|
|
15724
|
+
});
|
|
15725
|
+
if (options.json) {
|
|
15726
|
+
printJson4({
|
|
15727
|
+
ok: true,
|
|
15728
|
+
version,
|
|
15729
|
+
dryRun: result.dryRun,
|
|
15730
|
+
created: result.created,
|
|
15731
|
+
adopted: result.adopted,
|
|
15732
|
+
detectedExistingTraces: result.detectedExistingTraces,
|
|
15733
|
+
workspaceDir: result.location.workspaceDir,
|
|
15734
|
+
manifestPath: result.location.manifestPath,
|
|
15735
|
+
createdDirs: result.createdDirs,
|
|
15736
|
+
project: result.manifest.project
|
|
15737
|
+
});
|
|
15738
|
+
return;
|
|
15739
|
+
}
|
|
15740
|
+
console.log("AgentInspect workspace");
|
|
15741
|
+
console.log(`Project: ${result.manifest.project}`);
|
|
15742
|
+
console.log(`Directory: ${result.location.workspaceDir}`);
|
|
15743
|
+
if (result.dryRun) {
|
|
15744
|
+
console.log("Dry run \u2014 would create:");
|
|
15745
|
+
for (const dir of result.createdDirs) console.log(`- ${dir}/`);
|
|
15746
|
+
if (result.created) console.log(`- ${result.location.manifestPath}`);
|
|
15747
|
+
return;
|
|
15748
|
+
}
|
|
15749
|
+
if (result.adopted && !result.created) {
|
|
15750
|
+
console.log("Adopted existing workspace.json (left unchanged).");
|
|
15751
|
+
} else if (result.created) {
|
|
15752
|
+
console.log("Created workspace.json");
|
|
15753
|
+
}
|
|
15754
|
+
if (result.detectedExistingTraces) {
|
|
15755
|
+
console.log("Detected existing traces \u2014 preserved without changes.");
|
|
15756
|
+
}
|
|
15757
|
+
for (const dir of result.createdDirs) console.log(`Created ${dir}/`);
|
|
15758
|
+
console.log("\nNext: `agent-inspect workspace status`");
|
|
15759
|
+
} catch (error) {
|
|
15760
|
+
fail3(options.json, error instanceof Error ? error.message : String(error));
|
|
15761
|
+
}
|
|
15762
|
+
}
|
|
15763
|
+
async function workspaceStatusCommand(options = {}) {
|
|
15764
|
+
const location = resolveWorkspaceLocation(options.cwd);
|
|
15765
|
+
const manifestResult = await readWorkspaceManifestFile(location);
|
|
15766
|
+
if (!manifestResult.ok || !manifestResult.manifest) {
|
|
15767
|
+
fail3(
|
|
15768
|
+
options.json,
|
|
15769
|
+
manifestResult.exists ? `invalid workspace.json: ${manifestResult.errors.join("; ")}` : "no workspace found (run `agent-inspect workspace init`)"
|
|
15770
|
+
);
|
|
15771
|
+
return;
|
|
15772
|
+
}
|
|
15773
|
+
try {
|
|
15774
|
+
const status = await getWorkspaceStatus(location, manifestResult.manifest);
|
|
15775
|
+
if (options.json) {
|
|
15776
|
+
printJson4({ ok: true, ...status });
|
|
15777
|
+
return;
|
|
15778
|
+
}
|
|
15779
|
+
console.log(`Project: ${status.project}`);
|
|
15780
|
+
console.log(`Traces: ${status.traceFiles}`);
|
|
15781
|
+
console.log(`Reports: ${status.reports}`);
|
|
15782
|
+
console.log(`Artifacts: ${status.artifacts}`);
|
|
15783
|
+
console.log(`Bundles: ${status.bundles}`);
|
|
15784
|
+
console.log(`Notes: ${status.notes}`);
|
|
15785
|
+
console.log(
|
|
15786
|
+
`Index: ${status.index.enabled ? status.index.type : "disabled"}${status.index.exists ? " (present)" : ""}`
|
|
15787
|
+
);
|
|
15788
|
+
} catch (error) {
|
|
15789
|
+
fail3(options.json, error instanceof Error ? error.message : String(error));
|
|
15790
|
+
}
|
|
15791
|
+
}
|
|
15792
|
+
async function workspaceDoctorCommand(options = {}) {
|
|
15793
|
+
const location = resolveWorkspaceLocation(options.cwd);
|
|
15794
|
+
const result = await doctorWorkspace(location);
|
|
15795
|
+
if (options.json) {
|
|
15796
|
+
printJson4({ ok: result.ok, checks: result.checks });
|
|
15797
|
+
} else {
|
|
15798
|
+
console.log("AgentInspect workspace doctor");
|
|
15799
|
+
for (const check of result.checks) {
|
|
15800
|
+
const mark = check.status === "pass" ? "\u2713" : check.status === "warn" ? "!" : "\u2717";
|
|
15801
|
+
console.log(`${mark} ${check.id}: ${check.message}`);
|
|
15802
|
+
}
|
|
15803
|
+
console.log(result.ok ? "\nWorkspace looks healthy." : "\nWorkspace has problems.");
|
|
15804
|
+
}
|
|
15805
|
+
if (!result.ok) process.exitCode = 1;
|
|
15806
|
+
}
|
|
15807
|
+
async function workspaceCleanCommand(options = {}) {
|
|
15808
|
+
const location = resolveWorkspaceLocation(options.cwd);
|
|
15809
|
+
const manifestResult = await readWorkspaceManifestFile(location);
|
|
15810
|
+
if (!manifestResult.ok || !manifestResult.manifest) {
|
|
15811
|
+
fail3(
|
|
15812
|
+
options.json,
|
|
15813
|
+
manifestResult.exists ? `invalid workspace.json: ${manifestResult.errors.join("; ")}` : "no workspace found (run `agent-inspect workspace init`)"
|
|
15814
|
+
);
|
|
15815
|
+
return;
|
|
15816
|
+
}
|
|
15817
|
+
try {
|
|
15818
|
+
const result = await cleanWorkspace(location, manifestResult.manifest, {
|
|
15819
|
+
confirm: options.yes === true
|
|
15820
|
+
});
|
|
15821
|
+
if (options.json) {
|
|
15822
|
+
printJson4({ ok: true, dryRun: result.dryRun, removed: result.removed });
|
|
15823
|
+
return;
|
|
15824
|
+
}
|
|
15825
|
+
if (result.dryRun) {
|
|
15826
|
+
console.log("Dry run \u2014 would remove (traces are never deleted):");
|
|
15827
|
+
if (result.removed.length === 0) console.log("- (nothing)");
|
|
15828
|
+
for (const item of result.removed) console.log(`- ${item}`);
|
|
15829
|
+
console.log("\nRe-run with --yes to delete.");
|
|
15830
|
+
return;
|
|
15831
|
+
}
|
|
15832
|
+
console.log("Removed generated workspace content (traces preserved):");
|
|
15833
|
+
if (result.removed.length === 0) console.log("- (nothing)");
|
|
15834
|
+
for (const item of result.removed) console.log(`- ${item}`);
|
|
15835
|
+
} catch (error) {
|
|
15836
|
+
fail3(options.json, error instanceof Error ? error.message : String(error));
|
|
15837
|
+
}
|
|
15838
|
+
}
|
|
15839
|
+
async function workspacePathCommand(options = {}) {
|
|
15840
|
+
const location = resolveWorkspaceLocation(options.cwd);
|
|
15841
|
+
if (options.json) {
|
|
15842
|
+
printJson4({
|
|
15843
|
+
ok: true,
|
|
15844
|
+
projectRoot: location.projectRoot,
|
|
15845
|
+
workspaceDir: location.workspaceDir,
|
|
15846
|
+
manifestPath: location.manifestPath
|
|
15847
|
+
});
|
|
15848
|
+
return;
|
|
15849
|
+
}
|
|
15850
|
+
console.log(location.workspaceDir);
|
|
15851
|
+
}
|
|
15852
|
+
|
|
15249
15853
|
// packages/cli/src/index.ts
|
|
15250
15854
|
function runCommand(action) {
|
|
15251
15855
|
void action().catch((error) => {
|
|
@@ -15577,6 +16181,28 @@ function createCliProgram() {
|
|
|
15577
16181
|
indexCmd.command("clean").description("Remove the local index file").option("--dir <path>", "trace directory").option("--json", "print JSON result").action((opts) => {
|
|
15578
16182
|
runCommand(() => indexCleanCommand(opts));
|
|
15579
16183
|
});
|
|
16184
|
+
const workspaceCmd = program.command("workspace").description("Manage a project-local AgentInspect workspace (.agent-inspect)");
|
|
16185
|
+
workspaceCmd.command("init").description("Create or adopt a local workspace (never deletes traces)").option("--project <name>", "project name (default: directory name)").addOption(
|
|
16186
|
+
new Option("--redaction-profile <profile>", "default redaction posture").choices([
|
|
16187
|
+
"local",
|
|
16188
|
+
"share",
|
|
16189
|
+
"strict"
|
|
16190
|
+
])
|
|
16191
|
+
).option("--dry-run", "print planned changes without writing").option("--json", "print deterministic JSON result").action((opts) => {
|
|
16192
|
+
runCommand(() => workspaceInitCommand(opts));
|
|
16193
|
+
});
|
|
16194
|
+
workspaceCmd.command("status").description("Show workspace counts and index status (read-only)").option("--json", "print deterministic JSON result").action((opts) => {
|
|
16195
|
+
runCommand(() => workspaceStatusCommand(opts));
|
|
16196
|
+
});
|
|
16197
|
+
workspaceCmd.command("doctor").description("Validate workspace config, permissions, and index freshness").option("--json", "print deterministic JSON result").action((opts) => {
|
|
16198
|
+
runCommand(() => workspaceDoctorCommand(opts));
|
|
16199
|
+
});
|
|
16200
|
+
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) => {
|
|
16201
|
+
runCommand(() => workspaceCleanCommand(opts));
|
|
16202
|
+
});
|
|
16203
|
+
workspaceCmd.command("path").description("Print resolved workspace paths").option("--json", "print deterministic JSON result").action((opts) => {
|
|
16204
|
+
runCommand(() => workspacePathCommand(opts));
|
|
16205
|
+
});
|
|
15580
16206
|
return program;
|
|
15581
16207
|
}
|
|
15582
16208
|
function isPrimaryModule() {
|