agent-inspect 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +31 -0
- package/SECURITY.md +18 -1
- package/docs/ADAPTERS.md +41 -0
- package/docs/CLI.md +18 -1
- package/package.json +2 -2
- package/packages/cli/dist/index.cjs +376 -116
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +375 -115
- package/packages/cli/dist/index.mjs.map +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { realpathSync, createReadStream } from 'fs';
|
|
3
|
-
import
|
|
3
|
+
import path14 from 'path';
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
5
5
|
import { Command, Option } from 'commander';
|
|
6
6
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
@@ -10,9 +10,10 @@ import os from 'os';
|
|
|
10
10
|
import process2, { stdin, stdout } from 'process';
|
|
11
11
|
import tty from 'tty';
|
|
12
12
|
import { createInterface } from 'readline';
|
|
13
|
+
import { createServer } from 'http';
|
|
13
14
|
|
|
14
15
|
// package.json
|
|
15
|
-
var version = "2.
|
|
16
|
+
var version = "2.6.0";
|
|
16
17
|
|
|
17
18
|
// packages/core/src/correlation-metadata.ts
|
|
18
19
|
var TRACE_CORRELATION_KEYS = [
|
|
@@ -744,7 +745,7 @@ function formatDuration(ms) {
|
|
|
744
745
|
// packages/core/src/utils.ts
|
|
745
746
|
var DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
|
|
746
747
|
var RUNS_DIR_NAME = "runs";
|
|
747
|
-
var FALLBACK_TRACE_DIR =
|
|
748
|
+
var FALLBACK_TRACE_DIR = path14.join(
|
|
748
749
|
os.tmpdir(),
|
|
749
750
|
"agent-inspect",
|
|
750
751
|
RUNS_DIR_NAME
|
|
@@ -779,7 +780,7 @@ function getDefaultTraceDir() {
|
|
|
779
780
|
if (typeof home !== "string" || home.trim() === "") {
|
|
780
781
|
return FALLBACK_TRACE_DIR;
|
|
781
782
|
}
|
|
782
|
-
return
|
|
783
|
+
return path14.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
|
|
783
784
|
} catch {
|
|
784
785
|
return FALLBACK_TRACE_DIR;
|
|
785
786
|
}
|
|
@@ -787,11 +788,11 @@ function getDefaultTraceDir() {
|
|
|
787
788
|
function getTraceFilePath(runId, traceDir) {
|
|
788
789
|
const baseDir = traceDir ?? getDefaultTraceDir();
|
|
789
790
|
let safeId = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
|
|
790
|
-
safeId =
|
|
791
|
+
safeId = path14.basename(safeId);
|
|
791
792
|
if (safeId === "" || safeId === "." || safeId === "..") {
|
|
792
793
|
safeId = "run_unknown";
|
|
793
794
|
}
|
|
794
|
-
return
|
|
795
|
+
return path14.join(baseDir, `${safeId}.jsonl`);
|
|
795
796
|
}
|
|
796
797
|
function formatError(error) {
|
|
797
798
|
if (error instanceof Error) {
|
|
@@ -1553,7 +1554,7 @@ var TraceDirectory = class {
|
|
|
1553
1554
|
this.#dir = resolveTraceDir(options);
|
|
1554
1555
|
}
|
|
1555
1556
|
getPath(filename) {
|
|
1556
|
-
return filename ?
|
|
1557
|
+
return filename ? path14.join(this.#dir, filename) : this.#dir;
|
|
1557
1558
|
}
|
|
1558
1559
|
async list() {
|
|
1559
1560
|
try {
|
|
@@ -1580,7 +1581,7 @@ function parseIsoToMs2(value) {
|
|
|
1580
1581
|
}
|
|
1581
1582
|
async function extractMetadata(filePath, _quickScan) {
|
|
1582
1583
|
const stats = await stat(filePath);
|
|
1583
|
-
let runIdFromFile =
|
|
1584
|
+
let runIdFromFile = path14.basename(filePath);
|
|
1584
1585
|
if (runIdFromFile.endsWith(".jsonl")) {
|
|
1585
1586
|
runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
|
|
1586
1587
|
}
|
|
@@ -3058,12 +3059,12 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3058
3059
|
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
3059
3060
|
);
|
|
3060
3061
|
const ordered = [...runs].sort(compareRuns);
|
|
3061
|
-
const
|
|
3062
|
+
const path16 = [];
|
|
3062
3063
|
const visited = /* @__PURE__ */ new Set();
|
|
3063
3064
|
const pushRun = (run, confidence, source) => {
|
|
3064
3065
|
if (visited.has(run.runId)) return;
|
|
3065
3066
|
visited.add(run.runId);
|
|
3066
|
-
|
|
3067
|
+
path16.push({
|
|
3067
3068
|
runId: run.runId,
|
|
3068
3069
|
name: run.name,
|
|
3069
3070
|
startedAt: run.startedAt,
|
|
@@ -3088,7 +3089,7 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3088
3089
|
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
3089
3090
|
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
3090
3091
|
}
|
|
3091
|
-
return
|
|
3092
|
+
return path16;
|
|
3092
3093
|
}
|
|
3093
3094
|
function metaRunIdMatches(run, token, runById) {
|
|
3094
3095
|
const meta = extractSessionWorkflowMetadata(run.metadata);
|
|
@@ -4026,7 +4027,7 @@ function findReaderByFormat(format, readers) {
|
|
|
4026
4027
|
}
|
|
4027
4028
|
async function jsonlFilesInDirectory(dirPath) {
|
|
4028
4029
|
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
4029
|
-
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) =>
|
|
4030
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path14.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
4030
4031
|
}
|
|
4031
4032
|
async function resolveInput(input3) {
|
|
4032
4033
|
const cached = resolvedInputCache.get(input3);
|
|
@@ -8152,9 +8153,9 @@ Trace directory: ${traceDir}`);
|
|
|
8152
8153
|
if (validation !== void 0 && !validation.ok) {
|
|
8153
8154
|
process.exitCode = 1;
|
|
8154
8155
|
}
|
|
8155
|
-
const outPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
8156
|
+
const outPath = options.output !== void 0 && options.output.trim() !== "" ? path14.resolve(options.output.trim()) : void 0;
|
|
8156
8157
|
if (outPath !== void 0) {
|
|
8157
|
-
await mkdir(
|
|
8158
|
+
await mkdir(path14.dirname(outPath), { recursive: true });
|
|
8158
8159
|
await writeFile(outPath, result.content, "utf-8");
|
|
8159
8160
|
const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
|
|
8160
8161
|
console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
|
|
@@ -8331,13 +8332,13 @@ function pairSteps(left, right) {
|
|
|
8331
8332
|
return pairs;
|
|
8332
8333
|
}
|
|
8333
8334
|
function compareLeafSteps(L, R, segments, opts, out) {
|
|
8334
|
-
const
|
|
8335
|
+
const path16 = buildPath(segments);
|
|
8335
8336
|
if (L.name !== R.name) {
|
|
8336
8337
|
out.push({
|
|
8337
8338
|
kind: "structure",
|
|
8338
8339
|
severity: "warning",
|
|
8339
8340
|
message: "Step name differs",
|
|
8340
|
-
path:
|
|
8341
|
+
path: path16,
|
|
8341
8342
|
left: L.name,
|
|
8342
8343
|
right: R.name
|
|
8343
8344
|
});
|
|
@@ -8347,7 +8348,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8347
8348
|
kind: "step-type",
|
|
8348
8349
|
severity: "warning",
|
|
8349
8350
|
message: "Step type differs",
|
|
8350
|
-
path:
|
|
8351
|
+
path: path16,
|
|
8351
8352
|
left: L.type,
|
|
8352
8353
|
right: R.type
|
|
8353
8354
|
});
|
|
@@ -8357,7 +8358,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8357
8358
|
kind: "step-status",
|
|
8358
8359
|
severity: "warning",
|
|
8359
8360
|
message: "Step status differs",
|
|
8360
|
-
path:
|
|
8361
|
+
path: path16,
|
|
8361
8362
|
left: L.status,
|
|
8362
8363
|
right: R.status
|
|
8363
8364
|
});
|
|
@@ -8369,7 +8370,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8369
8370
|
kind: "error",
|
|
8370
8371
|
severity: "error",
|
|
8371
8372
|
message: "Step error message differs",
|
|
8372
|
-
path:
|
|
8373
|
+
path: path16,
|
|
8373
8374
|
left: le || void 0,
|
|
8374
8375
|
right: re || void 0
|
|
8375
8376
|
});
|
|
@@ -8387,7 +8388,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8387
8388
|
kind: "duration",
|
|
8388
8389
|
severity: "info",
|
|
8389
8390
|
message: "Step duration differs",
|
|
8390
|
-
path:
|
|
8391
|
+
path: path16,
|
|
8391
8392
|
left: ld,
|
|
8392
8393
|
right: rd
|
|
8393
8394
|
});
|
|
@@ -8400,7 +8401,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8400
8401
|
kind: "metadata",
|
|
8401
8402
|
severity: "info",
|
|
8402
8403
|
message: "Step metadata differs",
|
|
8403
|
-
path:
|
|
8404
|
+
path: path16,
|
|
8404
8405
|
left: L.metadata,
|
|
8405
8406
|
right: R.metadata
|
|
8406
8407
|
});
|
|
@@ -8412,7 +8413,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
8412
8413
|
kind: "output",
|
|
8413
8414
|
severity: "info",
|
|
8414
8415
|
message: "Output preview differs",
|
|
8415
|
-
path:
|
|
8416
|
+
path: path16,
|
|
8416
8417
|
left: L.outputPreview,
|
|
8417
8418
|
right: R.outputPreview
|
|
8418
8419
|
});
|
|
@@ -8572,11 +8573,11 @@ function diffRuns(left, right, options) {
|
|
|
8572
8573
|
}
|
|
8573
8574
|
|
|
8574
8575
|
// packages/core/src/diff/renderer.ts
|
|
8575
|
-
function formatPath(
|
|
8576
|
-
if (
|
|
8576
|
+
function formatPath(path16) {
|
|
8577
|
+
if (path16 === void 0 || path16.path.length === 0) {
|
|
8577
8578
|
return "(run)";
|
|
8578
8579
|
}
|
|
8579
|
-
return
|
|
8580
|
+
return path16.path.map((s) => s.name).join(" > ");
|
|
8580
8581
|
}
|
|
8581
8582
|
function formatValue(v, verbose) {
|
|
8582
8583
|
if (v === void 0) return "(undefined)";
|
|
@@ -9205,9 +9206,9 @@ async function reportCommand(runId, options = {}) {
|
|
|
9205
9206
|
redactionProfile,
|
|
9206
9207
|
correlation: !options.noCorrelation
|
|
9207
9208
|
});
|
|
9208
|
-
const outPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
9209
|
+
const outPath = options.output !== void 0 && options.output.trim() !== "" ? path14.resolve(options.output.trim()) : void 0;
|
|
9209
9210
|
if (outPath !== void 0) {
|
|
9210
|
-
await mkdir(
|
|
9211
|
+
await mkdir(path14.dirname(outPath), { recursive: true });
|
|
9211
9212
|
await writeFile(outPath, result.content, "utf-8");
|
|
9212
9213
|
console.log(`Wrote ${result.fileExtension} report to ${outPath}`);
|
|
9213
9214
|
}
|
|
@@ -9454,17 +9455,17 @@ function applyRule(rule, value, replacement) {
|
|
|
9454
9455
|
}
|
|
9455
9456
|
return value;
|
|
9456
9457
|
}
|
|
9457
|
-
function childPath(
|
|
9458
|
+
function childPath(path16, key) {
|
|
9458
9459
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
9459
|
-
return
|
|
9460
|
+
return path16 ? `${path16}.${key}` : key;
|
|
9460
9461
|
}
|
|
9461
|
-
return `${
|
|
9462
|
+
return `${path16 || "$"}[${JSON.stringify(key)}]`;
|
|
9462
9463
|
}
|
|
9463
|
-
function indexPath(
|
|
9464
|
-
return `${
|
|
9464
|
+
function indexPath(path16, index) {
|
|
9465
|
+
return `${path16 || "$"}[${index}]`;
|
|
9465
9466
|
}
|
|
9466
|
-
function makeFinding(
|
|
9467
|
-
return preview === void 0 ? { path:
|
|
9467
|
+
function makeFinding(path16, detector, action, matchKind, severity = "warning", preview) {
|
|
9468
|
+
return preview === void 0 ? { path: path16, detector, action, severity, matchKind } : { path: path16, detector, action, severity, matchKind, preview };
|
|
9468
9469
|
}
|
|
9469
9470
|
function createRedactionProfile(profile = "local") {
|
|
9470
9471
|
switch (profile) {
|
|
@@ -9533,11 +9534,11 @@ var Redactor2 = class {
|
|
|
9533
9534
|
#recordFinding(state, finding) {
|
|
9534
9535
|
if (this.#collectFindings) state.findings.push(finding);
|
|
9535
9536
|
}
|
|
9536
|
-
#redactValue(value, key,
|
|
9537
|
+
#redactValue(value, key, path16, depth, state) {
|
|
9537
9538
|
if (depth > this.#maxDepth) {
|
|
9538
9539
|
this.#recordFinding(
|
|
9539
9540
|
state,
|
|
9540
|
-
makeFinding(
|
|
9541
|
+
makeFinding(path16, "structure.maxDepth", "truncate", "value", "warning")
|
|
9541
9542
|
);
|
|
9542
9543
|
return "[Truncated]";
|
|
9543
9544
|
}
|
|
@@ -9546,19 +9547,19 @@ var Redactor2 = class {
|
|
|
9546
9547
|
if (rule) {
|
|
9547
9548
|
this.#recordFinding(
|
|
9548
9549
|
state,
|
|
9549
|
-
makeFinding(
|
|
9550
|
+
makeFinding(path16, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
9550
9551
|
);
|
|
9551
9552
|
return applyRule(rule, value, this.#replacement);
|
|
9552
9553
|
}
|
|
9553
9554
|
}
|
|
9554
9555
|
for (const detector of this.#detectors) {
|
|
9555
|
-
const detections = detector.detect({ path:
|
|
9556
|
+
const detections = detector.detect({ path: path16, key, value });
|
|
9556
9557
|
for (const detection of detections) {
|
|
9557
9558
|
const action = detection.action ?? "replace";
|
|
9558
9559
|
this.#recordFinding(
|
|
9559
9560
|
state,
|
|
9560
9561
|
makeFinding(
|
|
9561
|
-
|
|
9562
|
+
path16,
|
|
9562
9563
|
detector.id,
|
|
9563
9564
|
action,
|
|
9564
9565
|
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
@@ -9576,7 +9577,7 @@ var Redactor2 = class {
|
|
|
9576
9577
|
const out = [];
|
|
9577
9578
|
state.seen.set(value, out);
|
|
9578
9579
|
value.forEach((item, index) => {
|
|
9579
|
-
out[index] = this.#redactValue(item, void 0, indexPath(
|
|
9580
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path16, index), depth + 1, state);
|
|
9580
9581
|
});
|
|
9581
9582
|
return out;
|
|
9582
9583
|
}
|
|
@@ -9588,7 +9589,7 @@ var Redactor2 = class {
|
|
|
9588
9589
|
out[entryKey] = this.#redactValue(
|
|
9589
9590
|
entryValue,
|
|
9590
9591
|
entryKey,
|
|
9591
|
-
childPath(
|
|
9592
|
+
childPath(path16 === "$" ? "" : path16, entryKey),
|
|
9592
9593
|
depth + 1,
|
|
9593
9594
|
state
|
|
9594
9595
|
);
|
|
@@ -10035,14 +10036,14 @@ function uniqueSorted(values) {
|
|
|
10035
10036
|
return [...new Set(values)].sort();
|
|
10036
10037
|
}
|
|
10037
10038
|
function isWithinDirectory(child, parent) {
|
|
10038
|
-
const relative =
|
|
10039
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
10039
|
+
const relative = path14.relative(parent, child);
|
|
10040
|
+
return relative === "" || !relative.startsWith("..") && !path14.isAbsolute(relative);
|
|
10040
10041
|
}
|
|
10041
10042
|
async function resolveOutputPath(inputPath, output2, force) {
|
|
10042
10043
|
if (output2 === void 0 || output2.trim() === "") return void 0;
|
|
10043
|
-
const inputAbs =
|
|
10044
|
-
const outputAbs =
|
|
10045
|
-
const inputDir =
|
|
10044
|
+
const inputAbs = path14.resolve(inputPath);
|
|
10045
|
+
const outputAbs = path14.resolve(output2.trim());
|
|
10046
|
+
const inputDir = path14.dirname(inputAbs);
|
|
10046
10047
|
if (!isWithinDirectory(outputAbs, inputDir)) {
|
|
10047
10048
|
throw new Error("Refusing to write migrated output outside the input directory.");
|
|
10048
10049
|
}
|
|
@@ -10163,7 +10164,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
10163
10164
|
process.exitCode = 1;
|
|
10164
10165
|
return;
|
|
10165
10166
|
}
|
|
10166
|
-
const inputPath =
|
|
10167
|
+
const inputPath = path14.resolve(input3.trim());
|
|
10167
10168
|
const dryRun = options.dryRun === true;
|
|
10168
10169
|
if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
|
|
10169
10170
|
console.error("migrate requires --dry-run or --output <path>.");
|
|
@@ -10182,7 +10183,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
10182
10183
|
);
|
|
10183
10184
|
const result = await buildMigration(inputPath, outputPath);
|
|
10184
10185
|
if (!dryRun && outputPath !== void 0) {
|
|
10185
|
-
await mkdir(
|
|
10186
|
+
await mkdir(path14.dirname(outputPath), { recursive: true });
|
|
10186
10187
|
await writeFile(outputPath, result.content, "utf-8");
|
|
10187
10188
|
}
|
|
10188
10189
|
printSummary2(result, dryRun);
|
|
@@ -10460,7 +10461,7 @@ function stripPrefix(name, prefixes) {
|
|
|
10460
10461
|
}
|
|
10461
10462
|
return name;
|
|
10462
10463
|
}
|
|
10463
|
-
function eventEvidence(event,
|
|
10464
|
+
function eventEvidence(event, path16) {
|
|
10464
10465
|
return {
|
|
10465
10466
|
runId: event.runId,
|
|
10466
10467
|
eventId: event.eventId,
|
|
@@ -10470,7 +10471,7 @@ function eventEvidence(event, path15) {
|
|
|
10470
10471
|
kind: event.kind,
|
|
10471
10472
|
name: event.name,
|
|
10472
10473
|
status: event.status,
|
|
10473
|
-
...
|
|
10474
|
+
...path16 ? { path: path16 } : {}
|
|
10474
10475
|
};
|
|
10475
10476
|
}
|
|
10476
10477
|
function runEvidence(run) {
|
|
@@ -10533,9 +10534,9 @@ function eventEndMs(event) {
|
|
|
10533
10534
|
function normalizedKey(value) {
|
|
10534
10535
|
return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
10535
10536
|
}
|
|
10536
|
-
function lastPathSegment(
|
|
10537
|
-
const parts =
|
|
10538
|
-
return parts[parts.length - 1] ??
|
|
10537
|
+
function lastPathSegment(path16) {
|
|
10538
|
+
const parts = path16.split(".");
|
|
10539
|
+
return parts[parts.length - 1] ?? path16;
|
|
10539
10540
|
}
|
|
10540
10541
|
function valueType(value) {
|
|
10541
10542
|
if (Array.isArray(value)) return "array";
|
|
@@ -10549,12 +10550,12 @@ function serializedByteLength(value) {
|
|
|
10549
10550
|
return void 0;
|
|
10550
10551
|
}
|
|
10551
10552
|
}
|
|
10552
|
-
function pushValueEntries(entries, event, value,
|
|
10553
|
-
entries.push({ event, path:
|
|
10553
|
+
function pushValueEntries(entries, event, value, path16, key, depth = 0) {
|
|
10554
|
+
entries.push({ event, path: path16, key, value });
|
|
10554
10555
|
if (depth >= 8) return;
|
|
10555
10556
|
if (Array.isArray(value)) {
|
|
10556
10557
|
for (const [index, item] of value.entries()) {
|
|
10557
|
-
pushValueEntries(entries, event, item, `${
|
|
10558
|
+
pushValueEntries(entries, event, item, `${path16}.${index}`, String(index), depth + 1);
|
|
10558
10559
|
}
|
|
10559
10560
|
return;
|
|
10560
10561
|
}
|
|
@@ -10564,7 +10565,7 @@ function pushValueEntries(entries, event, value, path15, key, depth = 0) {
|
|
|
10564
10565
|
entries,
|
|
10565
10566
|
event,
|
|
10566
10567
|
value[nestedKey],
|
|
10567
|
-
`${
|
|
10568
|
+
`${path16}.${nestedKey}`,
|
|
10568
10569
|
nestedKey,
|
|
10569
10570
|
depth + 1
|
|
10570
10571
|
);
|
|
@@ -10645,9 +10646,9 @@ function eventDurationMs(event) {
|
|
|
10645
10646
|
}
|
|
10646
10647
|
function treeShape(nodes) {
|
|
10647
10648
|
const lines = [];
|
|
10648
|
-
const visit = (node,
|
|
10649
|
-
lines.push(`${
|
|
10650
|
-
node.children.forEach((child, index) => visit(child, `${
|
|
10649
|
+
const visit = (node, path16) => {
|
|
10650
|
+
lines.push(`${path16}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
|
|
10651
|
+
node.children.forEach((child, index) => visit(child, `${path16}.${index}`));
|
|
10651
10652
|
};
|
|
10652
10653
|
nodes.forEach((node, index) => visit(node, String(index)));
|
|
10653
10654
|
return lines;
|
|
@@ -10696,9 +10697,9 @@ function retrievalShape(context) {
|
|
|
10696
10697
|
function guardrailShape(context) {
|
|
10697
10698
|
return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
|
|
10698
10699
|
}
|
|
10699
|
-
function firstEvidenceForKind(context, kind,
|
|
10700
|
+
function firstEvidenceForKind(context, kind, path16) {
|
|
10700
10701
|
const event = context.events.find((candidate) => candidate.kind === kind);
|
|
10701
|
-
return event ? [eventEvidence(event,
|
|
10702
|
+
return event ? [eventEvidence(event, path16)] : runEvidence(context.selectedRun);
|
|
10702
10703
|
}
|
|
10703
10704
|
function baselineDiffFinding(message, evidence, expected, actual) {
|
|
10704
10705
|
return failFinding("baseline.regression", message, evidence, expected, actual);
|
|
@@ -10946,13 +10947,13 @@ function createStructureCycleRule() {
|
|
|
10946
10947
|
const seenCycles = /* @__PURE__ */ new Set();
|
|
10947
10948
|
const findings = [];
|
|
10948
10949
|
for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
|
|
10949
|
-
const
|
|
10950
|
+
const path16 = [];
|
|
10950
10951
|
const seenAt = /* @__PURE__ */ new Map();
|
|
10951
10952
|
let current = event;
|
|
10952
10953
|
while (current) {
|
|
10953
10954
|
const existing = seenAt.get(current.eventId);
|
|
10954
10955
|
if (existing !== void 0) {
|
|
10955
|
-
const cycle =
|
|
10956
|
+
const cycle = path16.slice(existing);
|
|
10956
10957
|
const key = cycle.map((item) => item.eventId).sort().join("\0");
|
|
10957
10958
|
if (!seenCycles.has(key)) {
|
|
10958
10959
|
seenCycles.add(key);
|
|
@@ -10968,8 +10969,8 @@ function createStructureCycleRule() {
|
|
|
10968
10969
|
}
|
|
10969
10970
|
break;
|
|
10970
10971
|
}
|
|
10971
|
-
seenAt.set(current.eventId,
|
|
10972
|
-
|
|
10972
|
+
seenAt.set(current.eventId, path16.length);
|
|
10973
|
+
path16.push(current);
|
|
10973
10974
|
current = current.parentId ? byId.get(current.parentId) : void 0;
|
|
10974
10975
|
}
|
|
10975
10976
|
}
|
|
@@ -11766,23 +11767,23 @@ function evaluatePromptInjection(text, options = {}) {
|
|
|
11766
11767
|
}
|
|
11767
11768
|
return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
|
|
11768
11769
|
}
|
|
11769
|
-
function validateSchemaField(value, field,
|
|
11770
|
+
function validateSchemaField(value, field, path16, evidence) {
|
|
11770
11771
|
const ruleId = "guardrail.structured-output";
|
|
11771
11772
|
if (field.type) {
|
|
11772
11773
|
const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
11773
11774
|
if (actual !== field.type) {
|
|
11774
|
-
evidence.push({ ruleId, path:
|
|
11775
|
+
evidence.push({ ruleId, path: path16, preview: `expected ${field.type}, got ${actual}` });
|
|
11775
11776
|
return;
|
|
11776
11777
|
}
|
|
11777
11778
|
}
|
|
11778
11779
|
if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
|
|
11779
|
-
evidence.push({ ruleId, path:
|
|
11780
|
+
evidence.push({ ruleId, path: path16, preview: "value not in enum" });
|
|
11780
11781
|
}
|
|
11781
11782
|
if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
|
|
11782
11783
|
const record = value;
|
|
11783
11784
|
for (const key of field.required) {
|
|
11784
11785
|
if (!(key in record)) {
|
|
11785
|
-
evidence.push({ ruleId, path: `${
|
|
11786
|
+
evidence.push({ ruleId, path: `${path16}.${key}`, preview: "missing required key" });
|
|
11786
11787
|
}
|
|
11787
11788
|
}
|
|
11788
11789
|
}
|
|
@@ -12109,7 +12110,7 @@ function asConfig(value) {
|
|
|
12109
12110
|
}
|
|
12110
12111
|
async function loadConfig(configPath) {
|
|
12111
12112
|
if (configPath === void 0) return {};
|
|
12112
|
-
const extension =
|
|
12113
|
+
const extension = path14.extname(configPath);
|
|
12113
12114
|
if (TS_CONFIG_EXTENSIONS.has(extension)) {
|
|
12114
12115
|
throw new Error(
|
|
12115
12116
|
"TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -12118,7 +12119,7 @@ async function loadConfig(configPath) {
|
|
|
12118
12119
|
if (!CONFIG_EXTENSIONS.has(extension)) {
|
|
12119
12120
|
throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
|
|
12120
12121
|
}
|
|
12121
|
-
const absolute =
|
|
12122
|
+
const absolute = path14.resolve(configPath);
|
|
12122
12123
|
if (extension === ".json") {
|
|
12123
12124
|
const raw = await readFile(absolute, "utf-8");
|
|
12124
12125
|
return asConfig(JSON.parse(raw));
|
|
@@ -12247,10 +12248,10 @@ function printHuman(result) {
|
|
|
12247
12248
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
12248
12249
|
}
|
|
12249
12250
|
for (const finding of result.findings) {
|
|
12250
|
-
const
|
|
12251
|
+
const path16 = finding.evidence[0]?.path;
|
|
12251
12252
|
const run = finding.evidence[0]?.runId;
|
|
12252
12253
|
const runPrefix = run ? `[${run}] ` : "";
|
|
12253
|
-
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${
|
|
12254
|
+
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path16 ? ` (${path16})` : ""}`);
|
|
12254
12255
|
}
|
|
12255
12256
|
}
|
|
12256
12257
|
function readErrorResult(error) {
|
|
@@ -12364,6 +12365,262 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
|
|
|
12364
12365
|
else printHuman(result);
|
|
12365
12366
|
}
|
|
12366
12367
|
|
|
12368
|
+
// packages/viewer/src/html.ts
|
|
12369
|
+
var viewerIndexHtml = `<!DOCTYPE html>
|
|
12370
|
+
<html lang="en">
|
|
12371
|
+
<head>
|
|
12372
|
+
<meta charset="utf-8" />
|
|
12373
|
+
<title>AgentInspect Viewer</title>
|
|
12374
|
+
<style>
|
|
12375
|
+
body { font-family: system-ui, sans-serif; margin: 1.5rem; line-height: 1.4; }
|
|
12376
|
+
h1 { font-size: 1.25rem; }
|
|
12377
|
+
pre { background: #f4f4f5; padding: 1rem; overflow: auto; max-height: 70vh; }
|
|
12378
|
+
a { color: #0b57d0; }
|
|
12379
|
+
.muted { color: #666; }
|
|
12380
|
+
</style>
|
|
12381
|
+
</head>
|
|
12382
|
+
<body>
|
|
12383
|
+
<h1>AgentInspect local viewer</h1>
|
|
12384
|
+
<p class="muted">Read-only. JSONL on disk remains canonical.</p>
|
|
12385
|
+
<p><a href="/api/health">/api/health</a> \xB7 <a href="/api/traces">/api/traces</a> \xB7 <a href="/api/sessions">/api/sessions</a></p>
|
|
12386
|
+
<pre id="out">Loading traces\u2026</pre>
|
|
12387
|
+
<script>
|
|
12388
|
+
fetch("/api/traces").then((r) => r.json()).then((data) => {
|
|
12389
|
+
document.getElementById("out").textContent = JSON.stringify(data, null, 2);
|
|
12390
|
+
}).catch((err) => {
|
|
12391
|
+
document.getElementById("out").textContent = String(err);
|
|
12392
|
+
});
|
|
12393
|
+
</script>
|
|
12394
|
+
</body>
|
|
12395
|
+
</html>
|
|
12396
|
+
`;
|
|
12397
|
+
|
|
12398
|
+
// packages/viewer/src/server.ts
|
|
12399
|
+
var DEFAULT_HOST = "127.0.0.1";
|
|
12400
|
+
var DEFAULT_PORT = 7337;
|
|
12401
|
+
var DEFAULT_MAX_EVENTS = 500;
|
|
12402
|
+
function sendJson(res, status, body) {
|
|
12403
|
+
const payload = JSON.stringify(body);
|
|
12404
|
+
res.writeHead(status, {
|
|
12405
|
+
"content-type": "application/json; charset=utf-8",
|
|
12406
|
+
"cache-control": "no-store"
|
|
12407
|
+
});
|
|
12408
|
+
res.end(payload);
|
|
12409
|
+
}
|
|
12410
|
+
function notFound(res, message) {
|
|
12411
|
+
sendJson(res, 404, { error: message });
|
|
12412
|
+
}
|
|
12413
|
+
function badRequest(res, message) {
|
|
12414
|
+
sendJson(res, 400, { error: message });
|
|
12415
|
+
}
|
|
12416
|
+
function decodeId(segment) {
|
|
12417
|
+
if (!segment) return "";
|
|
12418
|
+
try {
|
|
12419
|
+
return decodeURIComponent(segment);
|
|
12420
|
+
} catch {
|
|
12421
|
+
return segment;
|
|
12422
|
+
}
|
|
12423
|
+
}
|
|
12424
|
+
function boundedEvents(events, maxEvents) {
|
|
12425
|
+
if (events.length <= maxEvents) return [...events];
|
|
12426
|
+
return events.slice(0, maxEvents);
|
|
12427
|
+
}
|
|
12428
|
+
function createViewerServer(options = {}) {
|
|
12429
|
+
const traceDir = resolveTraceDir({ dir: options.traceDir });
|
|
12430
|
+
const host = options.host ?? DEFAULT_HOST;
|
|
12431
|
+
const port = options.port ?? DEFAULT_PORT;
|
|
12432
|
+
const maxEvents = options.maxEvents ?? DEFAULT_MAX_EVENTS;
|
|
12433
|
+
if (host === "0.0.0.0") {
|
|
12434
|
+
console.warn(
|
|
12435
|
+
"[AgentInspect viewer] Binding to 0.0.0.0 exposes traces on the network. Use 127.0.0.1 unless you accept that risk."
|
|
12436
|
+
);
|
|
12437
|
+
}
|
|
12438
|
+
const server = createServer(async (req, res) => {
|
|
12439
|
+
try {
|
|
12440
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
12441
|
+
return badRequest(res, "Only GET is supported.");
|
|
12442
|
+
}
|
|
12443
|
+
const url = new URL(req.url ?? "/", `http://${host}:${port}`);
|
|
12444
|
+
const pathname = url.pathname;
|
|
12445
|
+
if (pathname === "/" || pathname === "/index.html") {
|
|
12446
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
12447
|
+
res.end(viewerIndexHtml);
|
|
12448
|
+
return;
|
|
12449
|
+
}
|
|
12450
|
+
if (pathname === "/api/health") {
|
|
12451
|
+
return sendJson(res, 200, {
|
|
12452
|
+
ok: true,
|
|
12453
|
+
readOnly: true,
|
|
12454
|
+
traceDir: path14.resolve(traceDir)
|
|
12455
|
+
});
|
|
12456
|
+
}
|
|
12457
|
+
const td = new TraceDirectory({ dir: traceDir });
|
|
12458
|
+
if (pathname === "/api/traces") {
|
|
12459
|
+
const files = await td.list();
|
|
12460
|
+
const metas = await loadTraceMetadataList(
|
|
12461
|
+
traceDir,
|
|
12462
|
+
files,
|
|
12463
|
+
(fileName) => td.getPath(fileName)
|
|
12464
|
+
);
|
|
12465
|
+
return sendJson(
|
|
12466
|
+
res,
|
|
12467
|
+
200,
|
|
12468
|
+
metas.map((meta) => ({
|
|
12469
|
+
runId: meta.runId,
|
|
12470
|
+
name: meta.name,
|
|
12471
|
+
status: meta.status,
|
|
12472
|
+
file: path14.basename(meta.filePath),
|
|
12473
|
+
startedAt: meta.startedAt,
|
|
12474
|
+
durationMs: meta.durationMs
|
|
12475
|
+
}))
|
|
12476
|
+
);
|
|
12477
|
+
}
|
|
12478
|
+
if (pathname === "/api/sessions") {
|
|
12479
|
+
const files = await td.list();
|
|
12480
|
+
const metas = await loadTraceMetadataList(
|
|
12481
|
+
traceDir,
|
|
12482
|
+
files,
|
|
12483
|
+
(fileName) => td.getPath(fileName)
|
|
12484
|
+
);
|
|
12485
|
+
const runs = await loadSessionRunRecords(metas);
|
|
12486
|
+
const index = buildSessionIndex(runs, {
|
|
12487
|
+
correlateByGroupId: url.searchParams.get("correlateGroup") === "true"
|
|
12488
|
+
});
|
|
12489
|
+
return sendJson(res, 200, index);
|
|
12490
|
+
}
|
|
12491
|
+
const sessionMatch = pathname.match(/^\/api\/session\/([^/]+)$/);
|
|
12492
|
+
if (sessionMatch) {
|
|
12493
|
+
const sessionId = decodeId(sessionMatch[1]);
|
|
12494
|
+
const files = await td.list();
|
|
12495
|
+
const metas = await loadTraceMetadataList(
|
|
12496
|
+
traceDir,
|
|
12497
|
+
files,
|
|
12498
|
+
(fileName) => td.getPath(fileName)
|
|
12499
|
+
);
|
|
12500
|
+
const runs = await loadSessionRunRecords(metas);
|
|
12501
|
+
const index = buildSessionIndex(runs, {
|
|
12502
|
+
correlateByGroupId: url.searchParams.get("correlateGroup") === "true"
|
|
12503
|
+
});
|
|
12504
|
+
const session = index.sessions.find((item) => item.sessionId === sessionId);
|
|
12505
|
+
if (!session) return notFound(res, `Session not found: ${sessionId}`);
|
|
12506
|
+
return sendJson(res, 200, session);
|
|
12507
|
+
}
|
|
12508
|
+
const traceMatch = pathname.match(/^\/api\/trace\/([^/]+)$/);
|
|
12509
|
+
if (traceMatch) {
|
|
12510
|
+
const runId = decodeId(traceMatch[1]);
|
|
12511
|
+
const files = await td.list();
|
|
12512
|
+
const metas = await loadTraceMetadataList(
|
|
12513
|
+
traceDir,
|
|
12514
|
+
files,
|
|
12515
|
+
(fileName) => td.getPath(fileName)
|
|
12516
|
+
);
|
|
12517
|
+
const meta = metas.find((item) => item.runId === runId);
|
|
12518
|
+
if (!meta) return notFound(res, `Run not found: ${runId}`);
|
|
12519
|
+
const read = await openTrace({ type: "file", path: meta.filePath });
|
|
12520
|
+
const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
|
|
12521
|
+
return sendJson(res, 200, {
|
|
12522
|
+
runId,
|
|
12523
|
+
format: read.format,
|
|
12524
|
+
run,
|
|
12525
|
+
events: boundedEvents(read.events, maxEvents),
|
|
12526
|
+
warnings: read.warnings,
|
|
12527
|
+
truncated: read.events.length > maxEvents
|
|
12528
|
+
});
|
|
12529
|
+
}
|
|
12530
|
+
const timelineMatch = pathname.match(/^\/api\/trace\/([^/]+)\/timeline$/);
|
|
12531
|
+
if (timelineMatch) {
|
|
12532
|
+
const runId = decodeId(timelineMatch[1]);
|
|
12533
|
+
const files = await td.list();
|
|
12534
|
+
const metas = await loadTraceMetadataList(
|
|
12535
|
+
traceDir,
|
|
12536
|
+
files,
|
|
12537
|
+
(fileName) => td.getPath(fileName)
|
|
12538
|
+
);
|
|
12539
|
+
const meta = metas.find((item) => item.runId === runId);
|
|
12540
|
+
if (!meta) return notFound(res, `Run not found: ${runId}`);
|
|
12541
|
+
const read = await openTrace({ type: "file", path: meta.filePath });
|
|
12542
|
+
const legacyEvents = persistedInspectEventsToTraceEvents(
|
|
12543
|
+
boundedEvents(read.events, maxEvents)
|
|
12544
|
+
);
|
|
12545
|
+
const timeline = buildRunTimeline(legacyEvents, { focus: "all" });
|
|
12546
|
+
return sendJson(res, 200, { runId, timeline });
|
|
12547
|
+
}
|
|
12548
|
+
const checkMatch = pathname.match(/^\/api\/trace\/([^/]+)\/check$/);
|
|
12549
|
+
if (checkMatch) {
|
|
12550
|
+
const runId = decodeId(checkMatch[1]);
|
|
12551
|
+
const files = await td.list();
|
|
12552
|
+
const metas = await loadTraceMetadataList(
|
|
12553
|
+
traceDir,
|
|
12554
|
+
files,
|
|
12555
|
+
(fileName) => td.getPath(fileName)
|
|
12556
|
+
);
|
|
12557
|
+
const meta = metas.find((item) => item.runId === runId);
|
|
12558
|
+
if (!meta) return notFound(res, `Run not found: ${runId}`);
|
|
12559
|
+
const read = await openTrace({ type: "file", path: meta.filePath });
|
|
12560
|
+
const result = runTraceChecks(
|
|
12561
|
+
{ read },
|
|
12562
|
+
{ rules: [createRunStatusRule()], select: ["run.status"], runId }
|
|
12563
|
+
);
|
|
12564
|
+
return sendJson(res, 200, result);
|
|
12565
|
+
}
|
|
12566
|
+
return notFound(res, `Unknown route: ${pathname}`);
|
|
12567
|
+
} catch (error) {
|
|
12568
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12569
|
+
sendJson(res, 500, { error: message });
|
|
12570
|
+
}
|
|
12571
|
+
});
|
|
12572
|
+
return server;
|
|
12573
|
+
}
|
|
12574
|
+
function startViewerServer(options = {}) {
|
|
12575
|
+
const host = options.host ?? DEFAULT_HOST;
|
|
12576
|
+
const port = options.port ?? DEFAULT_PORT;
|
|
12577
|
+
const traceDir = resolveTraceDir({ dir: options.traceDir });
|
|
12578
|
+
const server = createViewerServer(options);
|
|
12579
|
+
return new Promise((resolve, reject) => {
|
|
12580
|
+
server.once("error", reject);
|
|
12581
|
+
server.listen(port, host, () => {
|
|
12582
|
+
const address = server.address();
|
|
12583
|
+
const resolvedPort = typeof address === "object" && address ? address.port : port;
|
|
12584
|
+
resolve({
|
|
12585
|
+
host,
|
|
12586
|
+
port: resolvedPort,
|
|
12587
|
+
traceDir: path14.resolve(traceDir),
|
|
12588
|
+
url: `http://${host}:${resolvedPort}`
|
|
12589
|
+
});
|
|
12590
|
+
});
|
|
12591
|
+
});
|
|
12592
|
+
}
|
|
12593
|
+
|
|
12594
|
+
// packages/cli/src/serve.ts
|
|
12595
|
+
function parsePort(value) {
|
|
12596
|
+
if (value === void 0) return void 0;
|
|
12597
|
+
const parsed = Number(value);
|
|
12598
|
+
if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
|
|
12599
|
+
throw new Error("--port must be an integer between 1 and 65535.");
|
|
12600
|
+
}
|
|
12601
|
+
return parsed;
|
|
12602
|
+
}
|
|
12603
|
+
async function serveCommand(options = {}) {
|
|
12604
|
+
const port = parsePort(options.port);
|
|
12605
|
+
const host = options.host?.trim() || "127.0.0.1";
|
|
12606
|
+
const info = await startViewerServer({
|
|
12607
|
+
...options.dir !== void 0 ? { traceDir: options.dir } : {},
|
|
12608
|
+
host,
|
|
12609
|
+
...port !== void 0 ? { port } : {}
|
|
12610
|
+
});
|
|
12611
|
+
console.log(`AgentInspect viewer (read-only): ${info.url}`);
|
|
12612
|
+
console.log(`Trace directory: ${info.traceDir}`);
|
|
12613
|
+
if (options.open === true && host !== "127.0.0.1" && host !== "localhost") {
|
|
12614
|
+
console.warn("Skipping browser open for non-local host binding.");
|
|
12615
|
+
} else if (options.open === true) {
|
|
12616
|
+
const openMod = await import('child_process');
|
|
12617
|
+
openMod.exec(`open ${info.url}`, () => {
|
|
12618
|
+
});
|
|
12619
|
+
}
|
|
12620
|
+
await new Promise(() => {
|
|
12621
|
+
});
|
|
12622
|
+
}
|
|
12623
|
+
|
|
12367
12624
|
// packages/eval/src/index.ts
|
|
12368
12625
|
function isTraceReadResult(value) {
|
|
12369
12626
|
return value !== null && typeof value === "object" && "runs" in value && "events" in value && "format" in value;
|
|
@@ -12532,10 +12789,10 @@ async function evalRun(input3, options = {}) {
|
|
|
12532
12789
|
diagnostics: []
|
|
12533
12790
|
};
|
|
12534
12791
|
}
|
|
12535
|
-
function evidenceForRun(run,
|
|
12536
|
-
return [{ runId: run.runId, ...
|
|
12792
|
+
function evidenceForRun(run, path16) {
|
|
12793
|
+
return [{ runId: run.runId, ...path16 !== void 0 ? { path: path16 } : {} }];
|
|
12537
12794
|
}
|
|
12538
|
-
function evidenceForEvent(event,
|
|
12795
|
+
function evidenceForEvent(event, path16) {
|
|
12539
12796
|
return [
|
|
12540
12797
|
{
|
|
12541
12798
|
runId: event.runId,
|
|
@@ -12543,7 +12800,7 @@ function evidenceForEvent(event, path15) {
|
|
|
12543
12800
|
...event.parentId !== void 0 ? { parentId: event.parentId } : {},
|
|
12544
12801
|
kind: event.kind,
|
|
12545
12802
|
name: event.name,
|
|
12546
|
-
...
|
|
12803
|
+
...path16 !== void 0 ? { path: path16 } : {}
|
|
12547
12804
|
}
|
|
12548
12805
|
];
|
|
12549
12806
|
}
|
|
@@ -12701,9 +12958,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
|
|
|
12701
12958
|
function tokenize(text) {
|
|
12702
12959
|
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));
|
|
12703
12960
|
}
|
|
12704
|
-
function firstEvidence(fields, run,
|
|
12961
|
+
function firstEvidence(fields, run, path16) {
|
|
12705
12962
|
const first = fields[0];
|
|
12706
|
-
return first === void 0 ? evidenceForRun(run,
|
|
12963
|
+
return first === void 0 ? evidenceForRun(run, path16) : evidenceForEvent(first.node.event, first.path);
|
|
12707
12964
|
}
|
|
12708
12965
|
function collectSourceIds(nodes, keys) {
|
|
12709
12966
|
const wanted = keySet(keys);
|
|
@@ -13080,8 +13337,8 @@ function renderEvalMarkdown(result) {
|
|
|
13080
13337
|
if (result.findings.length > 0) {
|
|
13081
13338
|
lines.push("", "## Findings");
|
|
13082
13339
|
for (const finding of result.findings) {
|
|
13083
|
-
const
|
|
13084
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
13340
|
+
const path16 = finding.evidence[0]?.path;
|
|
13341
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path16 ? ` (${path16})` : ""}`);
|
|
13085
13342
|
}
|
|
13086
13343
|
}
|
|
13087
13344
|
return `${lines.join("\n")}
|
|
@@ -13128,7 +13385,7 @@ function asConfig2(value) {
|
|
|
13128
13385
|
}
|
|
13129
13386
|
async function loadConfig2(configPath) {
|
|
13130
13387
|
if (configPath === void 0) return {};
|
|
13131
|
-
const extension =
|
|
13388
|
+
const extension = path14.extname(configPath);
|
|
13132
13389
|
if (TS_CONFIG_EXTENSIONS2.has(extension)) {
|
|
13133
13390
|
throw new Error(
|
|
13134
13391
|
"TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -13137,7 +13394,7 @@ async function loadConfig2(configPath) {
|
|
|
13137
13394
|
if (!CONFIG_EXTENSIONS2.has(extension)) {
|
|
13138
13395
|
throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
|
|
13139
13396
|
}
|
|
13140
|
-
const absolute =
|
|
13397
|
+
const absolute = path14.resolve(configPath);
|
|
13141
13398
|
if (extension === ".json") {
|
|
13142
13399
|
const raw = await readFile(absolute, "utf-8");
|
|
13143
13400
|
return asConfig2(JSON.parse(raw));
|
|
@@ -13274,8 +13531,8 @@ function printHuman2(result) {
|
|
|
13274
13531
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
13275
13532
|
}
|
|
13276
13533
|
for (const finding of result.findings) {
|
|
13277
|
-
const
|
|
13278
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
13534
|
+
const path16 = finding.evidence[0]?.path;
|
|
13535
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path16 ? ` (${path16})` : ""}`);
|
|
13279
13536
|
}
|
|
13280
13537
|
}
|
|
13281
13538
|
function readErrorResult2(error) {
|
|
@@ -13513,8 +13770,8 @@ function printHuman3(result) {
|
|
|
13513
13770
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
13514
13771
|
}
|
|
13515
13772
|
for (const finding of result.findings) {
|
|
13516
|
-
const
|
|
13517
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
13773
|
+
const path16 = finding.evidence[0]?.path;
|
|
13774
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path16 ? ` (${path16})` : ""}`);
|
|
13518
13775
|
}
|
|
13519
13776
|
console.log(`Note: ${result.note}`);
|
|
13520
13777
|
}
|
|
@@ -13633,8 +13890,8 @@ function renderCheckSection(result) {
|
|
|
13633
13890
|
`Diagnostics: ${result.diagnostics.length}`
|
|
13634
13891
|
];
|
|
13635
13892
|
for (const finding of result.findings.slice(0, 10)) {
|
|
13636
|
-
const
|
|
13637
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
13893
|
+
const path16 = finding.evidence[0]?.path ?? "(run)";
|
|
13894
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path16})`);
|
|
13638
13895
|
}
|
|
13639
13896
|
for (const diagnostic4 of result.diagnostics.slice(0, 10)) {
|
|
13640
13897
|
lines.push(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
@@ -13712,8 +13969,8 @@ function renderHtml(trace, check, diff) {
|
|
|
13712
13969
|
`;
|
|
13713
13970
|
}
|
|
13714
13971
|
async function writeArtifact(outputDir, relativePath, content, files) {
|
|
13715
|
-
const outPath =
|
|
13716
|
-
await mkdir(
|
|
13972
|
+
const outPath = path14.join(outputDir, relativePath);
|
|
13973
|
+
await mkdir(path14.dirname(outPath), { recursive: true });
|
|
13717
13974
|
await writeFile(outPath, content, "utf-8");
|
|
13718
13975
|
files.push(relativePath);
|
|
13719
13976
|
}
|
|
@@ -13729,7 +13986,7 @@ function manifestStatus(check, diff) {
|
|
|
13729
13986
|
return "ok";
|
|
13730
13987
|
}
|
|
13731
13988
|
async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
13732
|
-
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ?
|
|
13989
|
+
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path14.resolve(options.outputDir.trim()) : "";
|
|
13733
13990
|
if (outputDir === "") {
|
|
13734
13991
|
console.error("--output-dir is required.");
|
|
13735
13992
|
process.exitCode = 1;
|
|
@@ -13795,8 +14052,8 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
|
13795
14052
|
await writeArtifact(outputDir, "report.html", renderHtml(trace, check, diff), files);
|
|
13796
14053
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
13797
14054
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
13798
|
-
await mkdir(
|
|
13799
|
-
await appendFile(
|
|
14055
|
+
await mkdir(path14.dirname(path14.resolve(summaryTarget)), { recursive: true });
|
|
14056
|
+
await appendFile(path14.resolve(summaryTarget), `
|
|
13800
14057
|
${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
13801
14058
|
}
|
|
13802
14059
|
const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
|
|
@@ -13815,10 +14072,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
13815
14072
|
findings: diff?.findings.length ?? 0,
|
|
13816
14073
|
diagnostics: diff?.diagnostics.length ?? 0
|
|
13817
14074
|
},
|
|
13818
|
-
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary:
|
|
14075
|
+
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path14.resolve(summaryTarget) } : {},
|
|
13819
14076
|
note: NOTE
|
|
13820
14077
|
};
|
|
13821
|
-
await writeFile(
|
|
14078
|
+
await writeFile(path14.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
|
|
13822
14079
|
if (options.json === true) {
|
|
13823
14080
|
console.log(writeJson3(manifest).trimEnd());
|
|
13824
14081
|
} else {
|
|
@@ -13830,7 +14087,7 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
13830
14087
|
}
|
|
13831
14088
|
}
|
|
13832
14089
|
function validateReporterArtifactPath(options) {
|
|
13833
|
-
const outputDir =
|
|
14090
|
+
const outputDir = path14.resolve(options.outputDir);
|
|
13834
14091
|
const diagnostics = [];
|
|
13835
14092
|
const rawPath = options.relativePath;
|
|
13836
14093
|
if (rawPath.length === 0) {
|
|
@@ -13850,7 +14107,7 @@ function validateReporterArtifactPath(options) {
|
|
|
13850
14107
|
});
|
|
13851
14108
|
return { ok: false, outputDir, diagnostics };
|
|
13852
14109
|
}
|
|
13853
|
-
if (
|
|
14110
|
+
if (path14.isAbsolute(rawPath) || path14.win32.isAbsolute(rawPath)) {
|
|
13854
14111
|
diagnostics.push({
|
|
13855
14112
|
code: "artifact_path_absolute",
|
|
13856
14113
|
severity: "error",
|
|
@@ -13859,7 +14116,7 @@ function validateReporterArtifactPath(options) {
|
|
|
13859
14116
|
});
|
|
13860
14117
|
return { ok: false, outputDir, diagnostics };
|
|
13861
14118
|
}
|
|
13862
|
-
const normalized =
|
|
14119
|
+
const normalized = path14.posix.normalize(rawPath.replace(/\\/g, "/"));
|
|
13863
14120
|
const segments = normalized.split("/");
|
|
13864
14121
|
if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
|
|
13865
14122
|
diagnostics.push({
|
|
@@ -13870,9 +14127,9 @@ function validateReporterArtifactPath(options) {
|
|
|
13870
14127
|
});
|
|
13871
14128
|
return { ok: false, outputDir, diagnostics };
|
|
13872
14129
|
}
|
|
13873
|
-
const absolutePath =
|
|
13874
|
-
const relFromOutput =
|
|
13875
|
-
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") ||
|
|
14130
|
+
const absolutePath = path14.resolve(outputDir, normalized);
|
|
14131
|
+
const relFromOutput = path14.relative(outputDir, absolutePath);
|
|
14132
|
+
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path14.isAbsolute(relFromOutput)) {
|
|
13876
14133
|
diagnostics.push({
|
|
13877
14134
|
code: "artifact_path_escape",
|
|
13878
14135
|
severity: "error",
|
|
@@ -14018,23 +14275,23 @@ function readManifestDocument(value) {
|
|
|
14018
14275
|
};
|
|
14019
14276
|
}
|
|
14020
14277
|
function cwdRelative(filePath) {
|
|
14021
|
-
const relative =
|
|
14022
|
-
if (relative === "" || relative.startsWith("../") ||
|
|
14023
|
-
return
|
|
14278
|
+
const relative = path14.relative(process.cwd(), path14.resolve(filePath)).replace(/\\/g, "/");
|
|
14279
|
+
if (relative === "" || relative.startsWith("../") || path14.isAbsolute(relative)) {
|
|
14280
|
+
return path14.basename(filePath);
|
|
14024
14281
|
}
|
|
14025
14282
|
return relative;
|
|
14026
14283
|
}
|
|
14027
14284
|
async function readReporterManifest(filePath) {
|
|
14028
|
-
const absolute =
|
|
14285
|
+
const absolute = path14.resolve(filePath);
|
|
14029
14286
|
const raw = await readFile(absolute, "utf-8");
|
|
14030
14287
|
const document = readManifestDocument(JSON.parse(raw));
|
|
14031
14288
|
const manifest = document.manifest;
|
|
14032
14289
|
const results = manifest.results.map((result) => ({
|
|
14033
14290
|
testId: safeText(result.testId),
|
|
14034
14291
|
name: safeText(result.name),
|
|
14035
|
-
...result.file === void 0 ? {} : { file: safeText(
|
|
14292
|
+
...result.file === void 0 ? {} : { file: safeText(path14.basename(result.file)) },
|
|
14036
14293
|
status: result.status,
|
|
14037
|
-
...result.tracePath === void 0 ? {} : { tracePath: safeText(
|
|
14294
|
+
...result.tracePath === void 0 ? {} : { tracePath: safeText(path14.basename(result.tracePath)) },
|
|
14038
14295
|
artifacts: result.artifacts,
|
|
14039
14296
|
diagnostics: result.diagnostics
|
|
14040
14297
|
}));
|
|
@@ -14173,15 +14430,15 @@ async function ciSummaryCommand(manifestPaths, options = {}) {
|
|
|
14173
14430
|
return;
|
|
14174
14431
|
}
|
|
14175
14432
|
const markdown = renderMarkdown2(result);
|
|
14176
|
-
const outputPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
14433
|
+
const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path14.resolve(options.output.trim()) : void 0;
|
|
14177
14434
|
if (outputPath !== void 0) {
|
|
14178
|
-
await mkdir(
|
|
14435
|
+
await mkdir(path14.dirname(outputPath), { recursive: true });
|
|
14179
14436
|
await writeFile(outputPath, markdown, "utf-8");
|
|
14180
14437
|
}
|
|
14181
14438
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
14182
14439
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
14183
|
-
const summaryPath =
|
|
14184
|
-
await mkdir(
|
|
14440
|
+
const summaryPath = path14.resolve(summaryTarget);
|
|
14441
|
+
await mkdir(path14.dirname(summaryPath), { recursive: true });
|
|
14185
14442
|
await appendFile(summaryPath, `
|
|
14186
14443
|
${markdown}`, "utf-8");
|
|
14187
14444
|
}
|
|
@@ -14333,6 +14590,9 @@ function createCliProgram() {
|
|
|
14333
14590
|
).action((target, opts) => {
|
|
14334
14591
|
runCommand(() => checkCommand(target, opts));
|
|
14335
14592
|
});
|
|
14593
|
+
program.command("serve").description("Start optional localhost read-only trace viewer").option("--dir <path>", "trace directory to serve").option("--host <host>", "bind host (default 127.0.0.1)", "127.0.0.1").option("--port <number>", "bind port (default 7337)", "7337").option("--open", "open browser locally when host is localhost").action((opts) => {
|
|
14594
|
+
runCommand(() => serveCommand(opts));
|
|
14595
|
+
});
|
|
14336
14596
|
program.command("eval").description("Run deterministic local evals against a trace").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").option("--dir <path>", "trace directory for run-id lookup").addOption(
|
|
14337
14597
|
new Option("--format <format>", "trace input format").choices([
|
|
14338
14598
|
"agent-inspect-jsonl",
|
|
@@ -14497,9 +14757,9 @@ function isPrimaryModule() {
|
|
|
14497
14757
|
if (!entry) return false;
|
|
14498
14758
|
const selfPath = fileURLToPath(import.meta.url);
|
|
14499
14759
|
try {
|
|
14500
|
-
return realpathSync(
|
|
14760
|
+
return realpathSync(path14.resolve(entry)) === realpathSync(path14.resolve(selfPath));
|
|
14501
14761
|
} catch {
|
|
14502
|
-
return
|
|
14762
|
+
return path14.resolve(entry) === path14.resolve(selfPath);
|
|
14503
14763
|
}
|
|
14504
14764
|
}
|
|
14505
14765
|
if (isPrimaryModule()) {
|