agent-inspect 2.1.0 → 2.2.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 +16 -0
- package/README.md +13 -3
- package/docs/API.md +29 -4
- package/docs/CLI.md +34 -7
- package/package.json +11 -1
- package/packages/cli/dist/index.cjs +461 -93
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +460 -92
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/core/dist/reporters.cjs +185 -0
- package/packages/core/dist/reporters.cjs.map +1 -0
- package/packages/core/dist/reporters.d.cts +91 -0
- package/packages/core/dist/reporters.d.ts +91 -0
- package/packages/core/dist/reporters.mjs +175 -0
- package/packages/core/dist/reporters.mjs.map +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { realpathSync, createReadStream } from 'fs';
|
|
3
|
-
import
|
|
3
|
+
import path13 from 'path';
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
5
5
|
import { Command, Option } from 'commander';
|
|
6
6
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
@@ -12,7 +12,7 @@ import tty from 'tty';
|
|
|
12
12
|
import { createInterface } from 'readline';
|
|
13
13
|
|
|
14
14
|
// package.json
|
|
15
|
-
var version = "2.
|
|
15
|
+
var version = "2.2.0";
|
|
16
16
|
|
|
17
17
|
// packages/core/src/correlation-metadata.ts
|
|
18
18
|
var TRACE_CORRELATION_KEYS = [
|
|
@@ -744,7 +744,7 @@ function formatDuration(ms) {
|
|
|
744
744
|
// packages/core/src/utils.ts
|
|
745
745
|
var DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
|
|
746
746
|
var RUNS_DIR_NAME = "runs";
|
|
747
|
-
var FALLBACK_TRACE_DIR =
|
|
747
|
+
var FALLBACK_TRACE_DIR = path13.join(
|
|
748
748
|
os.tmpdir(),
|
|
749
749
|
"agent-inspect",
|
|
750
750
|
RUNS_DIR_NAME
|
|
@@ -779,7 +779,7 @@ function getDefaultTraceDir() {
|
|
|
779
779
|
if (typeof home !== "string" || home.trim() === "") {
|
|
780
780
|
return FALLBACK_TRACE_DIR;
|
|
781
781
|
}
|
|
782
|
-
return
|
|
782
|
+
return path13.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
|
|
783
783
|
} catch {
|
|
784
784
|
return FALLBACK_TRACE_DIR;
|
|
785
785
|
}
|
|
@@ -787,11 +787,11 @@ function getDefaultTraceDir() {
|
|
|
787
787
|
function getTraceFilePath(runId, traceDir) {
|
|
788
788
|
const baseDir = traceDir ?? getDefaultTraceDir();
|
|
789
789
|
let safeId = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
|
|
790
|
-
safeId =
|
|
790
|
+
safeId = path13.basename(safeId);
|
|
791
791
|
if (safeId === "" || safeId === "." || safeId === "..") {
|
|
792
792
|
safeId = "run_unknown";
|
|
793
793
|
}
|
|
794
|
-
return
|
|
794
|
+
return path13.join(baseDir, `${safeId}.jsonl`);
|
|
795
795
|
}
|
|
796
796
|
function formatError(error) {
|
|
797
797
|
if (error instanceof Error) {
|
|
@@ -1553,7 +1553,7 @@ var TraceDirectory = class {
|
|
|
1553
1553
|
this.#dir = resolveTraceDir(options);
|
|
1554
1554
|
}
|
|
1555
1555
|
getPath(filename) {
|
|
1556
|
-
return filename ?
|
|
1556
|
+
return filename ? path13.join(this.#dir, filename) : this.#dir;
|
|
1557
1557
|
}
|
|
1558
1558
|
async list() {
|
|
1559
1559
|
try {
|
|
@@ -1580,7 +1580,7 @@ function parseIsoToMs2(value) {
|
|
|
1580
1580
|
}
|
|
1581
1581
|
async function extractMetadata(filePath, _quickScan) {
|
|
1582
1582
|
const stats = await stat(filePath);
|
|
1583
|
-
let runIdFromFile =
|
|
1583
|
+
let runIdFromFile = path13.basename(filePath);
|
|
1584
1584
|
if (runIdFromFile.endsWith(".jsonl")) {
|
|
1585
1585
|
runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
|
|
1586
1586
|
}
|
|
@@ -3551,7 +3551,7 @@ function findReaderByFormat(format, readers) {
|
|
|
3551
3551
|
}
|
|
3552
3552
|
async function jsonlFilesInDirectory(dirPath) {
|
|
3553
3553
|
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
3554
|
-
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) =>
|
|
3554
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path13.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
3555
3555
|
}
|
|
3556
3556
|
async function resolveInput(input3) {
|
|
3557
3557
|
const cached = resolvedInputCache.get(input3);
|
|
@@ -7677,9 +7677,9 @@ Trace directory: ${traceDir}`);
|
|
|
7677
7677
|
if (validation !== void 0 && !validation.ok) {
|
|
7678
7678
|
process.exitCode = 1;
|
|
7679
7679
|
}
|
|
7680
|
-
const outPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
7680
|
+
const outPath = options.output !== void 0 && options.output.trim() !== "" ? path13.resolve(options.output.trim()) : void 0;
|
|
7681
7681
|
if (outPath !== void 0) {
|
|
7682
|
-
await mkdir(
|
|
7682
|
+
await mkdir(path13.dirname(outPath), { recursive: true });
|
|
7683
7683
|
await writeFile(outPath, result.content, "utf-8");
|
|
7684
7684
|
const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
|
|
7685
7685
|
console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
|
|
@@ -7856,13 +7856,13 @@ function pairSteps(left, right) {
|
|
|
7856
7856
|
return pairs;
|
|
7857
7857
|
}
|
|
7858
7858
|
function compareLeafSteps(L, R, segments, opts, out) {
|
|
7859
|
-
const
|
|
7859
|
+
const path15 = buildPath(segments);
|
|
7860
7860
|
if (L.name !== R.name) {
|
|
7861
7861
|
out.push({
|
|
7862
7862
|
kind: "structure",
|
|
7863
7863
|
severity: "warning",
|
|
7864
7864
|
message: "Step name differs",
|
|
7865
|
-
path:
|
|
7865
|
+
path: path15,
|
|
7866
7866
|
left: L.name,
|
|
7867
7867
|
right: R.name
|
|
7868
7868
|
});
|
|
@@ -7872,7 +7872,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
7872
7872
|
kind: "step-type",
|
|
7873
7873
|
severity: "warning",
|
|
7874
7874
|
message: "Step type differs",
|
|
7875
|
-
path:
|
|
7875
|
+
path: path15,
|
|
7876
7876
|
left: L.type,
|
|
7877
7877
|
right: R.type
|
|
7878
7878
|
});
|
|
@@ -7882,7 +7882,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
7882
7882
|
kind: "step-status",
|
|
7883
7883
|
severity: "warning",
|
|
7884
7884
|
message: "Step status differs",
|
|
7885
|
-
path:
|
|
7885
|
+
path: path15,
|
|
7886
7886
|
left: L.status,
|
|
7887
7887
|
right: R.status
|
|
7888
7888
|
});
|
|
@@ -7894,7 +7894,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
7894
7894
|
kind: "error",
|
|
7895
7895
|
severity: "error",
|
|
7896
7896
|
message: "Step error message differs",
|
|
7897
|
-
path:
|
|
7897
|
+
path: path15,
|
|
7898
7898
|
left: le || void 0,
|
|
7899
7899
|
right: re || void 0
|
|
7900
7900
|
});
|
|
@@ -7912,7 +7912,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
7912
7912
|
kind: "duration",
|
|
7913
7913
|
severity: "info",
|
|
7914
7914
|
message: "Step duration differs",
|
|
7915
|
-
path:
|
|
7915
|
+
path: path15,
|
|
7916
7916
|
left: ld,
|
|
7917
7917
|
right: rd
|
|
7918
7918
|
});
|
|
@@ -7925,7 +7925,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
7925
7925
|
kind: "metadata",
|
|
7926
7926
|
severity: "info",
|
|
7927
7927
|
message: "Step metadata differs",
|
|
7928
|
-
path:
|
|
7928
|
+
path: path15,
|
|
7929
7929
|
left: L.metadata,
|
|
7930
7930
|
right: R.metadata
|
|
7931
7931
|
});
|
|
@@ -7937,7 +7937,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
7937
7937
|
kind: "output",
|
|
7938
7938
|
severity: "info",
|
|
7939
7939
|
message: "Output preview differs",
|
|
7940
|
-
path:
|
|
7940
|
+
path: path15,
|
|
7941
7941
|
left: L.outputPreview,
|
|
7942
7942
|
right: R.outputPreview
|
|
7943
7943
|
});
|
|
@@ -8097,11 +8097,11 @@ function diffRuns(left, right, options) {
|
|
|
8097
8097
|
}
|
|
8098
8098
|
|
|
8099
8099
|
// packages/core/src/diff/renderer.ts
|
|
8100
|
-
function formatPath(
|
|
8101
|
-
if (
|
|
8100
|
+
function formatPath(path15) {
|
|
8101
|
+
if (path15 === void 0 || path15.path.length === 0) {
|
|
8102
8102
|
return "(run)";
|
|
8103
8103
|
}
|
|
8104
|
-
return
|
|
8104
|
+
return path15.path.map((s) => s.name).join(" > ");
|
|
8105
8105
|
}
|
|
8106
8106
|
function formatValue(v, verbose) {
|
|
8107
8107
|
if (v === void 0) return "(undefined)";
|
|
@@ -8532,9 +8532,9 @@ async function reportCommand(runId, options = {}) {
|
|
|
8532
8532
|
redactionProfile,
|
|
8533
8533
|
correlation: !options.noCorrelation
|
|
8534
8534
|
});
|
|
8535
|
-
const outPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
8535
|
+
const outPath = options.output !== void 0 && options.output.trim() !== "" ? path13.resolve(options.output.trim()) : void 0;
|
|
8536
8536
|
if (outPath !== void 0) {
|
|
8537
|
-
await mkdir(
|
|
8537
|
+
await mkdir(path13.dirname(outPath), { recursive: true });
|
|
8538
8538
|
await writeFile(outPath, result.content, "utf-8");
|
|
8539
8539
|
console.log(`Wrote ${result.fileExtension} report to ${outPath}`);
|
|
8540
8540
|
}
|
|
@@ -8781,17 +8781,17 @@ function applyRule(rule, value, replacement) {
|
|
|
8781
8781
|
}
|
|
8782
8782
|
return value;
|
|
8783
8783
|
}
|
|
8784
|
-
function childPath(
|
|
8784
|
+
function childPath(path15, key) {
|
|
8785
8785
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
8786
|
-
return
|
|
8786
|
+
return path15 ? `${path15}.${key}` : key;
|
|
8787
8787
|
}
|
|
8788
|
-
return `${
|
|
8788
|
+
return `${path15 || "$"}[${JSON.stringify(key)}]`;
|
|
8789
8789
|
}
|
|
8790
|
-
function indexPath(
|
|
8791
|
-
return `${
|
|
8790
|
+
function indexPath(path15, index) {
|
|
8791
|
+
return `${path15 || "$"}[${index}]`;
|
|
8792
8792
|
}
|
|
8793
|
-
function makeFinding(
|
|
8794
|
-
return preview === void 0 ? { path:
|
|
8793
|
+
function makeFinding(path15, detector, action, matchKind, severity = "warning", preview) {
|
|
8794
|
+
return preview === void 0 ? { path: path15, detector, action, severity, matchKind } : { path: path15, detector, action, severity, matchKind, preview };
|
|
8795
8795
|
}
|
|
8796
8796
|
function createRedactionProfile(profile = "local") {
|
|
8797
8797
|
switch (profile) {
|
|
@@ -8860,11 +8860,11 @@ var Redactor2 = class {
|
|
|
8860
8860
|
#recordFinding(state, finding) {
|
|
8861
8861
|
if (this.#collectFindings) state.findings.push(finding);
|
|
8862
8862
|
}
|
|
8863
|
-
#redactValue(value, key,
|
|
8863
|
+
#redactValue(value, key, path15, depth, state) {
|
|
8864
8864
|
if (depth > this.#maxDepth) {
|
|
8865
8865
|
this.#recordFinding(
|
|
8866
8866
|
state,
|
|
8867
|
-
makeFinding(
|
|
8867
|
+
makeFinding(path15, "structure.maxDepth", "truncate", "value", "warning")
|
|
8868
8868
|
);
|
|
8869
8869
|
return "[Truncated]";
|
|
8870
8870
|
}
|
|
@@ -8873,19 +8873,19 @@ var Redactor2 = class {
|
|
|
8873
8873
|
if (rule) {
|
|
8874
8874
|
this.#recordFinding(
|
|
8875
8875
|
state,
|
|
8876
|
-
makeFinding(
|
|
8876
|
+
makeFinding(path15, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
8877
8877
|
);
|
|
8878
8878
|
return applyRule(rule, value, this.#replacement);
|
|
8879
8879
|
}
|
|
8880
8880
|
}
|
|
8881
8881
|
for (const detector of this.#detectors) {
|
|
8882
|
-
const detections = detector.detect({ path:
|
|
8882
|
+
const detections = detector.detect({ path: path15, key, value });
|
|
8883
8883
|
for (const detection of detections) {
|
|
8884
8884
|
const action = detection.action ?? "replace";
|
|
8885
8885
|
this.#recordFinding(
|
|
8886
8886
|
state,
|
|
8887
8887
|
makeFinding(
|
|
8888
|
-
|
|
8888
|
+
path15,
|
|
8889
8889
|
detector.id,
|
|
8890
8890
|
action,
|
|
8891
8891
|
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
@@ -8903,7 +8903,7 @@ var Redactor2 = class {
|
|
|
8903
8903
|
const out = [];
|
|
8904
8904
|
state.seen.set(value, out);
|
|
8905
8905
|
value.forEach((item, index) => {
|
|
8906
|
-
out[index] = this.#redactValue(item, void 0, indexPath(
|
|
8906
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path15, index), depth + 1, state);
|
|
8907
8907
|
});
|
|
8908
8908
|
return out;
|
|
8909
8909
|
}
|
|
@@ -8915,7 +8915,7 @@ var Redactor2 = class {
|
|
|
8915
8915
|
out[entryKey] = this.#redactValue(
|
|
8916
8916
|
entryValue,
|
|
8917
8917
|
entryKey,
|
|
8918
|
-
childPath(
|
|
8918
|
+
childPath(path15 === "$" ? "" : path15, entryKey),
|
|
8919
8919
|
depth + 1,
|
|
8920
8920
|
state
|
|
8921
8921
|
);
|
|
@@ -9362,14 +9362,14 @@ function uniqueSorted(values) {
|
|
|
9362
9362
|
return [...new Set(values)].sort();
|
|
9363
9363
|
}
|
|
9364
9364
|
function isWithinDirectory(child, parent) {
|
|
9365
|
-
const relative =
|
|
9366
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
9365
|
+
const relative = path13.relative(parent, child);
|
|
9366
|
+
return relative === "" || !relative.startsWith("..") && !path13.isAbsolute(relative);
|
|
9367
9367
|
}
|
|
9368
9368
|
async function resolveOutputPath(inputPath, output2, force) {
|
|
9369
9369
|
if (output2 === void 0 || output2.trim() === "") return void 0;
|
|
9370
|
-
const inputAbs =
|
|
9371
|
-
const outputAbs =
|
|
9372
|
-
const inputDir =
|
|
9370
|
+
const inputAbs = path13.resolve(inputPath);
|
|
9371
|
+
const outputAbs = path13.resolve(output2.trim());
|
|
9372
|
+
const inputDir = path13.dirname(inputAbs);
|
|
9373
9373
|
if (!isWithinDirectory(outputAbs, inputDir)) {
|
|
9374
9374
|
throw new Error("Refusing to write migrated output outside the input directory.");
|
|
9375
9375
|
}
|
|
@@ -9490,7 +9490,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
9490
9490
|
process.exitCode = 1;
|
|
9491
9491
|
return;
|
|
9492
9492
|
}
|
|
9493
|
-
const inputPath =
|
|
9493
|
+
const inputPath = path13.resolve(input3.trim());
|
|
9494
9494
|
const dryRun = options.dryRun === true;
|
|
9495
9495
|
if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
|
|
9496
9496
|
console.error("migrate requires --dry-run or --output <path>.");
|
|
@@ -9509,7 +9509,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
9509
9509
|
);
|
|
9510
9510
|
const result = await buildMigration(inputPath, outputPath);
|
|
9511
9511
|
if (!dryRun && outputPath !== void 0) {
|
|
9512
|
-
await mkdir(
|
|
9512
|
+
await mkdir(path13.dirname(outputPath), { recursive: true });
|
|
9513
9513
|
await writeFile(outputPath, result.content, "utf-8");
|
|
9514
9514
|
}
|
|
9515
9515
|
printSummary2(result, dryRun);
|
|
@@ -9787,7 +9787,7 @@ function stripPrefix(name, prefixes) {
|
|
|
9787
9787
|
}
|
|
9788
9788
|
return name;
|
|
9789
9789
|
}
|
|
9790
|
-
function eventEvidence(event,
|
|
9790
|
+
function eventEvidence(event, path15) {
|
|
9791
9791
|
return {
|
|
9792
9792
|
runId: event.runId,
|
|
9793
9793
|
eventId: event.eventId,
|
|
@@ -9797,7 +9797,7 @@ function eventEvidence(event, path13) {
|
|
|
9797
9797
|
kind: event.kind,
|
|
9798
9798
|
name: event.name,
|
|
9799
9799
|
status: event.status,
|
|
9800
|
-
...
|
|
9800
|
+
...path15 ? { path: path15 } : {}
|
|
9801
9801
|
};
|
|
9802
9802
|
}
|
|
9803
9803
|
function runEvidence(run) {
|
|
@@ -9860,9 +9860,9 @@ function eventEndMs(event) {
|
|
|
9860
9860
|
function normalizedKey(value) {
|
|
9861
9861
|
return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
9862
9862
|
}
|
|
9863
|
-
function lastPathSegment(
|
|
9864
|
-
const parts =
|
|
9865
|
-
return parts[parts.length - 1] ??
|
|
9863
|
+
function lastPathSegment(path15) {
|
|
9864
|
+
const parts = path15.split(".");
|
|
9865
|
+
return parts[parts.length - 1] ?? path15;
|
|
9866
9866
|
}
|
|
9867
9867
|
function valueType(value) {
|
|
9868
9868
|
if (Array.isArray(value)) return "array";
|
|
@@ -9876,12 +9876,12 @@ function serializedByteLength(value) {
|
|
|
9876
9876
|
return void 0;
|
|
9877
9877
|
}
|
|
9878
9878
|
}
|
|
9879
|
-
function pushValueEntries(entries, event, value,
|
|
9880
|
-
entries.push({ event, path:
|
|
9879
|
+
function pushValueEntries(entries, event, value, path15, key, depth = 0) {
|
|
9880
|
+
entries.push({ event, path: path15, key, value });
|
|
9881
9881
|
if (depth >= 8) return;
|
|
9882
9882
|
if (Array.isArray(value)) {
|
|
9883
9883
|
for (const [index, item] of value.entries()) {
|
|
9884
|
-
pushValueEntries(entries, event, item, `${
|
|
9884
|
+
pushValueEntries(entries, event, item, `${path15}.${index}`, String(index), depth + 1);
|
|
9885
9885
|
}
|
|
9886
9886
|
return;
|
|
9887
9887
|
}
|
|
@@ -9891,7 +9891,7 @@ function pushValueEntries(entries, event, value, path13, key, depth = 0) {
|
|
|
9891
9891
|
entries,
|
|
9892
9892
|
event,
|
|
9893
9893
|
value[nestedKey],
|
|
9894
|
-
`${
|
|
9894
|
+
`${path15}.${nestedKey}`,
|
|
9895
9895
|
nestedKey,
|
|
9896
9896
|
depth + 1
|
|
9897
9897
|
);
|
|
@@ -9972,9 +9972,9 @@ function eventDurationMs(event) {
|
|
|
9972
9972
|
}
|
|
9973
9973
|
function treeShape(nodes) {
|
|
9974
9974
|
const lines = [];
|
|
9975
|
-
const visit = (node,
|
|
9976
|
-
lines.push(`${
|
|
9977
|
-
node.children.forEach((child, index) => visit(child, `${
|
|
9975
|
+
const visit = (node, path15) => {
|
|
9976
|
+
lines.push(`${path15}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
|
|
9977
|
+
node.children.forEach((child, index) => visit(child, `${path15}.${index}`));
|
|
9978
9978
|
};
|
|
9979
9979
|
nodes.forEach((node, index) => visit(node, String(index)));
|
|
9980
9980
|
return lines;
|
|
@@ -10023,9 +10023,9 @@ function retrievalShape(context) {
|
|
|
10023
10023
|
function guardrailShape(context) {
|
|
10024
10024
|
return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
|
|
10025
10025
|
}
|
|
10026
|
-
function firstEvidenceForKind(context, kind,
|
|
10026
|
+
function firstEvidenceForKind(context, kind, path15) {
|
|
10027
10027
|
const event = context.events.find((candidate) => candidate.kind === kind);
|
|
10028
|
-
return event ? [eventEvidence(event,
|
|
10028
|
+
return event ? [eventEvidence(event, path15)] : runEvidence(context.selectedRun);
|
|
10029
10029
|
}
|
|
10030
10030
|
function baselineDiffFinding(message, evidence, expected, actual) {
|
|
10031
10031
|
return failFinding("baseline.regression", message, evidence, expected, actual);
|
|
@@ -10273,13 +10273,13 @@ function createStructureCycleRule() {
|
|
|
10273
10273
|
const seenCycles = /* @__PURE__ */ new Set();
|
|
10274
10274
|
const findings = [];
|
|
10275
10275
|
for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
|
|
10276
|
-
const
|
|
10276
|
+
const path15 = [];
|
|
10277
10277
|
const seenAt = /* @__PURE__ */ new Map();
|
|
10278
10278
|
let current = event;
|
|
10279
10279
|
while (current) {
|
|
10280
10280
|
const existing = seenAt.get(current.eventId);
|
|
10281
10281
|
if (existing !== void 0) {
|
|
10282
|
-
const cycle =
|
|
10282
|
+
const cycle = path15.slice(existing);
|
|
10283
10283
|
const key = cycle.map((item) => item.eventId).sort().join("\0");
|
|
10284
10284
|
if (!seenCycles.has(key)) {
|
|
10285
10285
|
seenCycles.add(key);
|
|
@@ -10295,8 +10295,8 @@ function createStructureCycleRule() {
|
|
|
10295
10295
|
}
|
|
10296
10296
|
break;
|
|
10297
10297
|
}
|
|
10298
|
-
seenAt.set(current.eventId,
|
|
10299
|
-
|
|
10298
|
+
seenAt.set(current.eventId, path15.length);
|
|
10299
|
+
path15.push(current);
|
|
10300
10300
|
current = current.parentId ? byId.get(current.parentId) : void 0;
|
|
10301
10301
|
}
|
|
10302
10302
|
}
|
|
@@ -10821,7 +10821,7 @@ function asConfig(value) {
|
|
|
10821
10821
|
}
|
|
10822
10822
|
async function loadConfig(configPath) {
|
|
10823
10823
|
if (configPath === void 0) return {};
|
|
10824
|
-
const extension =
|
|
10824
|
+
const extension = path13.extname(configPath);
|
|
10825
10825
|
if (TS_CONFIG_EXTENSIONS.has(extension)) {
|
|
10826
10826
|
throw new Error(
|
|
10827
10827
|
"TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -10830,7 +10830,7 @@ async function loadConfig(configPath) {
|
|
|
10830
10830
|
if (!CONFIG_EXTENSIONS.has(extension)) {
|
|
10831
10831
|
throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
|
|
10832
10832
|
}
|
|
10833
|
-
const absolute =
|
|
10833
|
+
const absolute = path13.resolve(configPath);
|
|
10834
10834
|
if (extension === ".json") {
|
|
10835
10835
|
const raw = await readFile(absolute, "utf-8");
|
|
10836
10836
|
return asConfig(JSON.parse(raw));
|
|
@@ -10952,8 +10952,8 @@ function printHuman(result) {
|
|
|
10952
10952
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
10953
10953
|
}
|
|
10954
10954
|
for (const finding of result.findings) {
|
|
10955
|
-
const
|
|
10956
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
10955
|
+
const path15 = finding.evidence[0]?.path;
|
|
10956
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path15 ? ` (${path15})` : ""}`);
|
|
10957
10957
|
}
|
|
10958
10958
|
}
|
|
10959
10959
|
function readErrorResult(error) {
|
|
@@ -11175,10 +11175,10 @@ async function evalRun(input3, options = {}) {
|
|
|
11175
11175
|
diagnostics: []
|
|
11176
11176
|
};
|
|
11177
11177
|
}
|
|
11178
|
-
function evidenceForRun(run,
|
|
11179
|
-
return [{ runId: run.runId, ...
|
|
11178
|
+
function evidenceForRun(run, path15) {
|
|
11179
|
+
return [{ runId: run.runId, ...path15 !== void 0 ? { path: path15 } : {} }];
|
|
11180
11180
|
}
|
|
11181
|
-
function evidenceForEvent(event,
|
|
11181
|
+
function evidenceForEvent(event, path15) {
|
|
11182
11182
|
return [
|
|
11183
11183
|
{
|
|
11184
11184
|
runId: event.runId,
|
|
@@ -11186,7 +11186,7 @@ function evidenceForEvent(event, path13) {
|
|
|
11186
11186
|
...event.parentId !== void 0 ? { parentId: event.parentId } : {},
|
|
11187
11187
|
kind: event.kind,
|
|
11188
11188
|
name: event.name,
|
|
11189
|
-
...
|
|
11189
|
+
...path15 !== void 0 ? { path: path15 } : {}
|
|
11190
11190
|
}
|
|
11191
11191
|
];
|
|
11192
11192
|
}
|
|
@@ -11344,9 +11344,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
|
|
|
11344
11344
|
function tokenize(text) {
|
|
11345
11345
|
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));
|
|
11346
11346
|
}
|
|
11347
|
-
function firstEvidence(fields, run,
|
|
11347
|
+
function firstEvidence(fields, run, path15) {
|
|
11348
11348
|
const first = fields[0];
|
|
11349
|
-
return first === void 0 ? evidenceForRun(run,
|
|
11349
|
+
return first === void 0 ? evidenceForRun(run, path15) : evidenceForEvent(first.node.event, first.path);
|
|
11350
11350
|
}
|
|
11351
11351
|
function collectSourceIds(nodes, keys) {
|
|
11352
11352
|
const wanted = keySet(keys);
|
|
@@ -11723,8 +11723,8 @@ function renderEvalMarkdown(result) {
|
|
|
11723
11723
|
if (result.findings.length > 0) {
|
|
11724
11724
|
lines.push("", "## Findings");
|
|
11725
11725
|
for (const finding of result.findings) {
|
|
11726
|
-
const
|
|
11727
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
11726
|
+
const path15 = finding.evidence[0]?.path;
|
|
11727
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path15 ? ` (${path15})` : ""}`);
|
|
11728
11728
|
}
|
|
11729
11729
|
}
|
|
11730
11730
|
return `${lines.join("\n")}
|
|
@@ -11771,7 +11771,7 @@ function asConfig2(value) {
|
|
|
11771
11771
|
}
|
|
11772
11772
|
async function loadConfig2(configPath) {
|
|
11773
11773
|
if (configPath === void 0) return {};
|
|
11774
|
-
const extension =
|
|
11774
|
+
const extension = path13.extname(configPath);
|
|
11775
11775
|
if (TS_CONFIG_EXTENSIONS2.has(extension)) {
|
|
11776
11776
|
throw new Error(
|
|
11777
11777
|
"TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -11780,7 +11780,7 @@ async function loadConfig2(configPath) {
|
|
|
11780
11780
|
if (!CONFIG_EXTENSIONS2.has(extension)) {
|
|
11781
11781
|
throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
|
|
11782
11782
|
}
|
|
11783
|
-
const absolute =
|
|
11783
|
+
const absolute = path13.resolve(configPath);
|
|
11784
11784
|
if (extension === ".json") {
|
|
11785
11785
|
const raw = await readFile(absolute, "utf-8");
|
|
11786
11786
|
return asConfig2(JSON.parse(raw));
|
|
@@ -11917,8 +11917,8 @@ function printHuman2(result) {
|
|
|
11917
11917
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
11918
11918
|
}
|
|
11919
11919
|
for (const finding of result.findings) {
|
|
11920
|
-
const
|
|
11921
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
11920
|
+
const path15 = finding.evidence[0]?.path;
|
|
11921
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path15 ? ` (${path15})` : ""}`);
|
|
11922
11922
|
}
|
|
11923
11923
|
}
|
|
11924
11924
|
function readErrorResult2(error) {
|
|
@@ -12156,8 +12156,8 @@ function printHuman3(result) {
|
|
|
12156
12156
|
console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
12157
12157
|
}
|
|
12158
12158
|
for (const finding of result.findings) {
|
|
12159
|
-
const
|
|
12160
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
12159
|
+
const path15 = finding.evidence[0]?.path;
|
|
12160
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path15 ? ` (${path15})` : ""}`);
|
|
12161
12161
|
}
|
|
12162
12162
|
console.log(`Note: ${result.note}`);
|
|
12163
12163
|
}
|
|
@@ -12276,8 +12276,8 @@ function renderCheckSection(result) {
|
|
|
12276
12276
|
`Diagnostics: ${result.diagnostics.length}`
|
|
12277
12277
|
];
|
|
12278
12278
|
for (const finding of result.findings.slice(0, 10)) {
|
|
12279
|
-
const
|
|
12280
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
12279
|
+
const path15 = finding.evidence[0]?.path ?? "(run)";
|
|
12280
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path15})`);
|
|
12281
12281
|
}
|
|
12282
12282
|
for (const diagnostic4 of result.diagnostics.slice(0, 10)) {
|
|
12283
12283
|
lines.push(`- ${diagnostic4.code}: ${diagnostic4.message}`);
|
|
@@ -12355,8 +12355,8 @@ function renderHtml(trace, check, diff) {
|
|
|
12355
12355
|
`;
|
|
12356
12356
|
}
|
|
12357
12357
|
async function writeArtifact(outputDir, relativePath, content, files) {
|
|
12358
|
-
const outPath =
|
|
12359
|
-
await mkdir(
|
|
12358
|
+
const outPath = path13.join(outputDir, relativePath);
|
|
12359
|
+
await mkdir(path13.dirname(outPath), { recursive: true });
|
|
12360
12360
|
await writeFile(outPath, content, "utf-8");
|
|
12361
12361
|
files.push(relativePath);
|
|
12362
12362
|
}
|
|
@@ -12372,7 +12372,7 @@ function manifestStatus(check, diff) {
|
|
|
12372
12372
|
return "ok";
|
|
12373
12373
|
}
|
|
12374
12374
|
async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
12375
|
-
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ?
|
|
12375
|
+
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path13.resolve(options.outputDir.trim()) : "";
|
|
12376
12376
|
if (outputDir === "") {
|
|
12377
12377
|
console.error("--output-dir is required.");
|
|
12378
12378
|
process.exitCode = 1;
|
|
@@ -12438,8 +12438,8 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
|
12438
12438
|
await writeArtifact(outputDir, "report.html", renderHtml(trace, check, diff), files);
|
|
12439
12439
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
12440
12440
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
12441
|
-
await mkdir(
|
|
12442
|
-
await appendFile(
|
|
12441
|
+
await mkdir(path13.dirname(path13.resolve(summaryTarget)), { recursive: true });
|
|
12442
|
+
await appendFile(path13.resolve(summaryTarget), `
|
|
12443
12443
|
${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
12444
12444
|
}
|
|
12445
12445
|
const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
|
|
@@ -12458,10 +12458,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
12458
12458
|
findings: diff?.findings.length ?? 0,
|
|
12459
12459
|
diagnostics: diff?.diagnostics.length ?? 0
|
|
12460
12460
|
},
|
|
12461
|
-
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary:
|
|
12461
|
+
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path13.resolve(summaryTarget) } : {},
|
|
12462
12462
|
note: NOTE
|
|
12463
12463
|
};
|
|
12464
|
-
await writeFile(
|
|
12464
|
+
await writeFile(path13.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
|
|
12465
12465
|
if (options.json === true) {
|
|
12466
12466
|
console.log(writeJson3(manifest).trimEnd());
|
|
12467
12467
|
} else {
|
|
@@ -12472,6 +12472,371 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
12472
12472
|
}
|
|
12473
12473
|
}
|
|
12474
12474
|
}
|
|
12475
|
+
function validateReporterArtifactPath(options) {
|
|
12476
|
+
const outputDir = path13.resolve(options.outputDir);
|
|
12477
|
+
const diagnostics = [];
|
|
12478
|
+
const rawPath = options.relativePath;
|
|
12479
|
+
if (rawPath.length === 0) {
|
|
12480
|
+
diagnostics.push({
|
|
12481
|
+
code: "artifact_path_empty",
|
|
12482
|
+
severity: "error",
|
|
12483
|
+
message: "Reporter artifact path must not be empty."
|
|
12484
|
+
});
|
|
12485
|
+
return { ok: false, outputDir, diagnostics };
|
|
12486
|
+
}
|
|
12487
|
+
if (rawPath.includes("\0")) {
|
|
12488
|
+
diagnostics.push({
|
|
12489
|
+
code: "invalid_artifact_path",
|
|
12490
|
+
severity: "error",
|
|
12491
|
+
message: "Reporter artifact path must not contain null bytes.",
|
|
12492
|
+
target: rawPath
|
|
12493
|
+
});
|
|
12494
|
+
return { ok: false, outputDir, diagnostics };
|
|
12495
|
+
}
|
|
12496
|
+
if (path13.isAbsolute(rawPath) || path13.win32.isAbsolute(rawPath)) {
|
|
12497
|
+
diagnostics.push({
|
|
12498
|
+
code: "artifact_path_absolute",
|
|
12499
|
+
severity: "error",
|
|
12500
|
+
message: "Reporter artifact path must be relative.",
|
|
12501
|
+
target: rawPath
|
|
12502
|
+
});
|
|
12503
|
+
return { ok: false, outputDir, diagnostics };
|
|
12504
|
+
}
|
|
12505
|
+
const normalized = path13.posix.normalize(rawPath.replace(/\\/g, "/"));
|
|
12506
|
+
const segments = normalized.split("/");
|
|
12507
|
+
if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
|
|
12508
|
+
diagnostics.push({
|
|
12509
|
+
code: "artifact_path_escape",
|
|
12510
|
+
severity: "error",
|
|
12511
|
+
message: "Reporter artifact path must stay under the output directory.",
|
|
12512
|
+
target: rawPath
|
|
12513
|
+
});
|
|
12514
|
+
return { ok: false, outputDir, diagnostics };
|
|
12515
|
+
}
|
|
12516
|
+
const absolutePath = path13.resolve(outputDir, normalized);
|
|
12517
|
+
const relFromOutput = path13.relative(outputDir, absolutePath);
|
|
12518
|
+
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path13.isAbsolute(relFromOutput)) {
|
|
12519
|
+
diagnostics.push({
|
|
12520
|
+
code: "artifact_path_escape",
|
|
12521
|
+
severity: "error",
|
|
12522
|
+
message: "Reporter artifact path resolved outside the output directory.",
|
|
12523
|
+
target: rawPath
|
|
12524
|
+
});
|
|
12525
|
+
return { ok: false, outputDir, diagnostics };
|
|
12526
|
+
}
|
|
12527
|
+
return {
|
|
12528
|
+
ok: true,
|
|
12529
|
+
outputDir,
|
|
12530
|
+
relativePath: normalized,
|
|
12531
|
+
absolutePath,
|
|
12532
|
+
diagnostics
|
|
12533
|
+
};
|
|
12534
|
+
}
|
|
12535
|
+
|
|
12536
|
+
// packages/cli/src/ci-summary.ts
|
|
12537
|
+
var NOTE2 = "Generated locally by AgentInspect from reporter artifact manifests. Trace contents are not embedded.";
|
|
12538
|
+
var MAX_TEXT = 180;
|
|
12539
|
+
var FRAMEWORKS = /* @__PURE__ */ new Set(["vitest", "jest", "manual"]);
|
|
12540
|
+
var STATUSES = /* @__PURE__ */ new Set(["passed", "failed", "skipped", "todo"]);
|
|
12541
|
+
var ARTIFACT_KINDS = /* @__PURE__ */ new Set(["trace", "report", "eval", "redaction", "summary"]);
|
|
12542
|
+
var ARTIFACT_FORMATS = /* @__PURE__ */ new Set(["json", "jsonl", "md", "html"]);
|
|
12543
|
+
var REDACTION_PROFILES = /* @__PURE__ */ new Set(["local", "share", "strict"]);
|
|
12544
|
+
function stable6(value) {
|
|
12545
|
+
if (Array.isArray(value)) return value.map(stable6);
|
|
12546
|
+
if (value === null || typeof value !== "object") return value;
|
|
12547
|
+
const record = value;
|
|
12548
|
+
return Object.fromEntries(
|
|
12549
|
+
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable6(record[key])])
|
|
12550
|
+
);
|
|
12551
|
+
}
|
|
12552
|
+
function writeJson4(value) {
|
|
12553
|
+
return `${JSON.stringify(stable6(value), null, 2)}
|
|
12554
|
+
`;
|
|
12555
|
+
}
|
|
12556
|
+
function isObject(value) {
|
|
12557
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12558
|
+
}
|
|
12559
|
+
function readString(value, label) {
|
|
12560
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
12561
|
+
throw new Error(`${label} must be a non-empty string.`);
|
|
12562
|
+
}
|
|
12563
|
+
return safeText(value);
|
|
12564
|
+
}
|
|
12565
|
+
function readOptionalString(value) {
|
|
12566
|
+
return typeof value === "string" && value.trim() !== "" ? safeText(value) : void 0;
|
|
12567
|
+
}
|
|
12568
|
+
function readFramework(value) {
|
|
12569
|
+
const framework = readString(value, "manifest.framework");
|
|
12570
|
+
if (!FRAMEWORKS.has(framework)) {
|
|
12571
|
+
throw new Error(`Unsupported reporter framework: ${framework}.`);
|
|
12572
|
+
}
|
|
12573
|
+
return framework;
|
|
12574
|
+
}
|
|
12575
|
+
function readStatus(value) {
|
|
12576
|
+
const status = readString(value, "result.status");
|
|
12577
|
+
if (!STATUSES.has(status)) {
|
|
12578
|
+
throw new Error(`Unsupported reporter test status: ${status}.`);
|
|
12579
|
+
}
|
|
12580
|
+
return status;
|
|
12581
|
+
}
|
|
12582
|
+
function readArtifact(value, index) {
|
|
12583
|
+
if (!isObject(value)) throw new Error(`manifest.artifacts[${index}] must be an object.`);
|
|
12584
|
+
const kind = readString(value.kind, `manifest.artifacts[${index}].kind`);
|
|
12585
|
+
const format = readString(value.format, `manifest.artifacts[${index}].format`);
|
|
12586
|
+
const redactionProfile = readString(
|
|
12587
|
+
value.redactionProfile,
|
|
12588
|
+
`manifest.artifacts[${index}].redactionProfile`
|
|
12589
|
+
);
|
|
12590
|
+
if (!ARTIFACT_KINDS.has(kind)) throw new Error(`Unsupported artifact kind: ${kind}.`);
|
|
12591
|
+
if (!ARTIFACT_FORMATS.has(format)) throw new Error(`Unsupported artifact format: ${format}.`);
|
|
12592
|
+
if (!REDACTION_PROFILES.has(redactionProfile)) {
|
|
12593
|
+
throw new Error(`Unsupported artifact redaction profile: ${redactionProfile}.`);
|
|
12594
|
+
}
|
|
12595
|
+
const artifactPath = readString(value.path, `manifest.artifacts[${index}].path`);
|
|
12596
|
+
const pathCheck = validateReporterArtifactPath({
|
|
12597
|
+
outputDir: process.cwd(),
|
|
12598
|
+
relativePath: artifactPath
|
|
12599
|
+
});
|
|
12600
|
+
if (!pathCheck.ok || pathCheck.relativePath === void 0) {
|
|
12601
|
+
throw new Error(`Unsafe reporter artifact path: ${artifactPath}.`);
|
|
12602
|
+
}
|
|
12603
|
+
return {
|
|
12604
|
+
kind,
|
|
12605
|
+
path: pathCheck.relativePath,
|
|
12606
|
+
format,
|
|
12607
|
+
redactionProfile
|
|
12608
|
+
};
|
|
12609
|
+
}
|
|
12610
|
+
function readArtifacts(value, label) {
|
|
12611
|
+
if (value === void 0) return [];
|
|
12612
|
+
if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
|
|
12613
|
+
return value.map((item, index) => readArtifact(item, index));
|
|
12614
|
+
}
|
|
12615
|
+
function readDiagnosticsCount(value, label) {
|
|
12616
|
+
if (value === void 0) return 0;
|
|
12617
|
+
if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
|
|
12618
|
+
return value.length;
|
|
12619
|
+
}
|
|
12620
|
+
function readResult(value, index) {
|
|
12621
|
+
if (!isObject(value)) throw new Error(`manifest.results[${index}] must be an object.`);
|
|
12622
|
+
const file = readOptionalString(value.file);
|
|
12623
|
+
const tracePath = readOptionalString(value.tracePath);
|
|
12624
|
+
return {
|
|
12625
|
+
testId: readString(value.testId, `manifest.results[${index}].testId`),
|
|
12626
|
+
name: readString(value.name, `manifest.results[${index}].name`),
|
|
12627
|
+
...file === void 0 ? {} : { file },
|
|
12628
|
+
status: readStatus(value.status),
|
|
12629
|
+
...tracePath === void 0 ? {} : { tracePath },
|
|
12630
|
+
artifacts: readArtifacts(value.artifacts, `manifest.results[${index}].artifacts`),
|
|
12631
|
+
diagnostics: readDiagnosticsCount(
|
|
12632
|
+
value.diagnostics,
|
|
12633
|
+
`manifest.results[${index}].diagnostics`
|
|
12634
|
+
)
|
|
12635
|
+
};
|
|
12636
|
+
}
|
|
12637
|
+
function readManifestDocument(value) {
|
|
12638
|
+
if (!isObject(value)) throw new Error("Reporter manifest file must contain a JSON object.");
|
|
12639
|
+
const candidate = isObject(value.manifest) ? value.manifest : value;
|
|
12640
|
+
if (!isObject(candidate)) throw new Error("Reporter manifest must be a JSON object.");
|
|
12641
|
+
const schemaVersion = readString(candidate.schemaVersion, "manifest.schemaVersion");
|
|
12642
|
+
if (schemaVersion !== "0.1") {
|
|
12643
|
+
throw new Error(`Unsupported reporter manifest schemaVersion: ${schemaVersion}.`);
|
|
12644
|
+
}
|
|
12645
|
+
if (!Array.isArray(candidate.results)) {
|
|
12646
|
+
throw new Error("manifest.results must be an array.");
|
|
12647
|
+
}
|
|
12648
|
+
const artifacts = readArtifacts(candidate.artifacts, "manifest.artifacts");
|
|
12649
|
+
const results = candidate.results.map((item, index) => readResult(item, index));
|
|
12650
|
+
const diagnostics = readDiagnosticsCount(candidate.diagnostics, "manifest.diagnostics");
|
|
12651
|
+
const manifest = {
|
|
12652
|
+
framework: readFramework(candidate.framework),
|
|
12653
|
+
generatedAt: readString(candidate.generatedAt, "manifest.generatedAt"),
|
|
12654
|
+
results,
|
|
12655
|
+
artifacts
|
|
12656
|
+
};
|
|
12657
|
+
return {
|
|
12658
|
+
packageName: readOptionalString(value.package),
|
|
12659
|
+
manifest,
|
|
12660
|
+
diagnostics
|
|
12661
|
+
};
|
|
12662
|
+
}
|
|
12663
|
+
function cwdRelative(filePath) {
|
|
12664
|
+
const relative = path13.relative(process.cwd(), path13.resolve(filePath)).replace(/\\/g, "/");
|
|
12665
|
+
if (relative === "" || relative.startsWith("../") || path13.isAbsolute(relative)) {
|
|
12666
|
+
return path13.basename(filePath);
|
|
12667
|
+
}
|
|
12668
|
+
return relative;
|
|
12669
|
+
}
|
|
12670
|
+
async function readReporterManifest(filePath) {
|
|
12671
|
+
const absolute = path13.resolve(filePath);
|
|
12672
|
+
const raw = await readFile(absolute, "utf-8");
|
|
12673
|
+
const document = readManifestDocument(JSON.parse(raw));
|
|
12674
|
+
const manifest = document.manifest;
|
|
12675
|
+
const results = manifest.results.map((result) => ({
|
|
12676
|
+
testId: safeText(result.testId),
|
|
12677
|
+
name: safeText(result.name),
|
|
12678
|
+
...result.file === void 0 ? {} : { file: safeText(path13.basename(result.file)) },
|
|
12679
|
+
status: result.status,
|
|
12680
|
+
...result.tracePath === void 0 ? {} : { tracePath: safeText(path13.basename(result.tracePath)) },
|
|
12681
|
+
artifacts: result.artifacts,
|
|
12682
|
+
diagnostics: result.diagnostics
|
|
12683
|
+
}));
|
|
12684
|
+
return {
|
|
12685
|
+
...document.packageName === void 0 ? {} : { packageName: document.packageName },
|
|
12686
|
+
manifestFile: cwdRelative(absolute),
|
|
12687
|
+
framework: manifest.framework,
|
|
12688
|
+
generatedAt: manifest.generatedAt,
|
|
12689
|
+
results,
|
|
12690
|
+
artifacts: manifest.artifacts,
|
|
12691
|
+
diagnostics: document.diagnostics
|
|
12692
|
+
};
|
|
12693
|
+
}
|
|
12694
|
+
function summarize3(manifests) {
|
|
12695
|
+
const summary = {
|
|
12696
|
+
manifests: manifests.length,
|
|
12697
|
+
tests: 0,
|
|
12698
|
+
failed: 0,
|
|
12699
|
+
passed: 0,
|
|
12700
|
+
skipped: 0,
|
|
12701
|
+
todo: 0,
|
|
12702
|
+
artifacts: 0,
|
|
12703
|
+
diagnostics: 0
|
|
12704
|
+
};
|
|
12705
|
+
for (const manifest of manifests) {
|
|
12706
|
+
summary.artifacts += manifest.artifacts.length;
|
|
12707
|
+
summary.diagnostics += manifest.diagnostics;
|
|
12708
|
+
for (const result of manifest.results) {
|
|
12709
|
+
summary.tests += 1;
|
|
12710
|
+
if (result.status === "failed") summary.failed += 1;
|
|
12711
|
+
else if (result.status === "passed") summary.passed += 1;
|
|
12712
|
+
else if (result.status === "skipped") summary.skipped += 1;
|
|
12713
|
+
else summary.todo += 1;
|
|
12714
|
+
summary.artifacts += result.artifacts.length;
|
|
12715
|
+
summary.diagnostics += result.diagnostics;
|
|
12716
|
+
}
|
|
12717
|
+
}
|
|
12718
|
+
return {
|
|
12719
|
+
status: summary.failed > 0 ? "failed" : summary.diagnostics > 0 ? "warning" : "ok",
|
|
12720
|
+
manifests,
|
|
12721
|
+
summary,
|
|
12722
|
+
note: NOTE2
|
|
12723
|
+
};
|
|
12724
|
+
}
|
|
12725
|
+
function markdownCell(value) {
|
|
12726
|
+
return safeText(String(value ?? "unknown")).replaceAll("|", "\\|").replace(/\r?\n/g, " ");
|
|
12727
|
+
}
|
|
12728
|
+
function renderMarkdown2(result) {
|
|
12729
|
+
const lines = [
|
|
12730
|
+
"# AgentInspect CI Summary",
|
|
12731
|
+
"",
|
|
12732
|
+
NOTE2,
|
|
12733
|
+
"",
|
|
12734
|
+
"| Field | Value |",
|
|
12735
|
+
"| --- | --- |",
|
|
12736
|
+
`| Status | ${result.status} |`,
|
|
12737
|
+
`| Manifests | ${result.summary.manifests} |`,
|
|
12738
|
+
`| Tests | ${result.summary.tests} |`,
|
|
12739
|
+
`| Failed | ${result.summary.failed} |`,
|
|
12740
|
+
`| Passed | ${result.summary.passed} |`,
|
|
12741
|
+
`| Skipped | ${result.summary.skipped} |`,
|
|
12742
|
+
`| Todo | ${result.summary.todo} |`,
|
|
12743
|
+
`| Artifacts | ${result.summary.artifacts} |`,
|
|
12744
|
+
`| Diagnostics | ${result.summary.diagnostics} |`,
|
|
12745
|
+
"",
|
|
12746
|
+
"## Tests",
|
|
12747
|
+
"",
|
|
12748
|
+
"| Framework | Status | Test | File | Trace | Artifacts |",
|
|
12749
|
+
"| --- | --- | --- | --- | --- | --- |"
|
|
12750
|
+
];
|
|
12751
|
+
const rows = result.manifests.flatMap(
|
|
12752
|
+
(manifest) => manifest.results.map((test) => ({
|
|
12753
|
+
framework: manifest.framework,
|
|
12754
|
+
test
|
|
12755
|
+
}))
|
|
12756
|
+
);
|
|
12757
|
+
if (rows.length === 0) {
|
|
12758
|
+
lines.push("| unknown | unknown | No tests found | unknown | unknown | 0 |");
|
|
12759
|
+
} else {
|
|
12760
|
+
for (const row of rows) {
|
|
12761
|
+
lines.push(
|
|
12762
|
+
`| ${markdownCell(row.framework)} | ${markdownCell(row.test.status)} | ${markdownCell(row.test.name)} | ${markdownCell(row.test.file)} | ${markdownCell(row.test.tracePath)} | ${row.test.artifacts.length} |`
|
|
12763
|
+
);
|
|
12764
|
+
}
|
|
12765
|
+
}
|
|
12766
|
+
lines.push("", "## Manifests", "", "| Framework | File | Generated | Artifacts |", "| --- | --- | --- | --- |");
|
|
12767
|
+
for (const manifest of result.manifests) {
|
|
12768
|
+
lines.push(
|
|
12769
|
+
`| ${markdownCell(manifest.framework)} | ${markdownCell(manifest.manifestFile)} | ${markdownCell(manifest.generatedAt)} | ${manifest.artifacts.length} |`
|
|
12770
|
+
);
|
|
12771
|
+
}
|
|
12772
|
+
lines.push("", "## Artifacts", "", "| Framework | Kind | Path | Format | Profile |", "| --- | --- | --- | --- | --- |");
|
|
12773
|
+
const artifacts = result.manifests.flatMap(
|
|
12774
|
+
(manifest) => manifest.artifacts.map((artifact) => ({ framework: manifest.framework, artifact }))
|
|
12775
|
+
);
|
|
12776
|
+
if (artifacts.length === 0) {
|
|
12777
|
+
lines.push("| unknown | unknown | No artifacts found | unknown | unknown |");
|
|
12778
|
+
} else {
|
|
12779
|
+
for (const row of artifacts) {
|
|
12780
|
+
lines.push(
|
|
12781
|
+
`| ${markdownCell(row.framework)} | ${markdownCell(row.artifact.kind)} | ${markdownCell(row.artifact.path)} | ${markdownCell(row.artifact.format)} | ${markdownCell(row.artifact.redactionProfile)} |`
|
|
12782
|
+
);
|
|
12783
|
+
}
|
|
12784
|
+
}
|
|
12785
|
+
lines.push("");
|
|
12786
|
+
return `${lines.join("\n")}
|
|
12787
|
+
`;
|
|
12788
|
+
}
|
|
12789
|
+
function safeText(value) {
|
|
12790
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
12791
|
+
const redacted = compact.replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(
|
|
12792
|
+
/\b(?:api[_-]?key|authorization|token|secret|password)\s*[:=]\s*[^,\s]+/gi,
|
|
12793
|
+
"$1=[REDACTED]"
|
|
12794
|
+
);
|
|
12795
|
+
if (redacted.length <= MAX_TEXT) return redacted;
|
|
12796
|
+
return `${redacted.slice(0, MAX_TEXT - 12)}...[truncated]`;
|
|
12797
|
+
}
|
|
12798
|
+
async function ciSummaryCommand(manifestPaths, options = {}) {
|
|
12799
|
+
if (manifestPaths.length === 0) {
|
|
12800
|
+
console.error("At least one reporter manifest path is required.");
|
|
12801
|
+
process.exitCode = 1;
|
|
12802
|
+
return;
|
|
12803
|
+
}
|
|
12804
|
+
let result;
|
|
12805
|
+
try {
|
|
12806
|
+
const manifests = [];
|
|
12807
|
+
for (const manifestPath of manifestPaths) {
|
|
12808
|
+
manifests.push(await readReporterManifest(manifestPath));
|
|
12809
|
+
}
|
|
12810
|
+
manifests.sort((a, b) => a.manifestFile.localeCompare(b.manifestFile));
|
|
12811
|
+
result = summarize3(manifests);
|
|
12812
|
+
} catch (error) {
|
|
12813
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12814
|
+
console.error(`[AgentInspect] ci-summary failed: ${message}`);
|
|
12815
|
+
process.exitCode = 1;
|
|
12816
|
+
return;
|
|
12817
|
+
}
|
|
12818
|
+
const markdown = renderMarkdown2(result);
|
|
12819
|
+
const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path13.resolve(options.output.trim()) : void 0;
|
|
12820
|
+
if (outputPath !== void 0) {
|
|
12821
|
+
await mkdir(path13.dirname(outputPath), { recursive: true });
|
|
12822
|
+
await writeFile(outputPath, markdown, "utf-8");
|
|
12823
|
+
}
|
|
12824
|
+
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
12825
|
+
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
12826
|
+
const summaryPath = path13.resolve(summaryTarget);
|
|
12827
|
+
await mkdir(path13.dirname(summaryPath), { recursive: true });
|
|
12828
|
+
await appendFile(summaryPath, `
|
|
12829
|
+
${markdown}`, "utf-8");
|
|
12830
|
+
}
|
|
12831
|
+
if (options.json === true) {
|
|
12832
|
+
console.log(writeJson4(result).trimEnd());
|
|
12833
|
+
} else if (outputPath !== void 0) {
|
|
12834
|
+
console.log(`Wrote AgentInspect CI summary to ${outputPath}`);
|
|
12835
|
+
console.log(`Status: ${result.status}`);
|
|
12836
|
+
} else {
|
|
12837
|
+
console.log(markdown.trimEnd());
|
|
12838
|
+
}
|
|
12839
|
+
}
|
|
12475
12840
|
|
|
12476
12841
|
// packages/cli/src/index.ts
|
|
12477
12842
|
function runCommand(action) {
|
|
@@ -12657,6 +13022,9 @@ function createCliProgram() {
|
|
|
12657
13022
|
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--baseline <trace-path-or-run-id>", "optional baseline trace for diff artifacts").option("--baseline-run <run-id>", "select a run from the baseline trace").option("--github-summary <path>", "append a safe summary to this file, e.g. GITHUB_STEP_SUMMARY").option("--json", "print deterministic JSON manifest").action((target, opts) => {
|
|
12658
13023
|
runCommand(() => artifactsCommand(target, opts));
|
|
12659
13024
|
});
|
|
13025
|
+
program.command("ci-summary").description("Summarize local reporter artifact manifests for CI").argument("<manifest...>", "reporter artifact manifest JSON files").option("-o, --output <path>", "write Markdown summary to a local file").option("--github-summary <path>", "append Markdown summary to this local file, e.g. GITHUB_STEP_SUMMARY").option("--json", "print deterministic JSON summary").action((manifest, opts) => {
|
|
13026
|
+
runCommand(() => ciSummaryCommand(manifest, opts));
|
|
13027
|
+
});
|
|
12660
13028
|
program.command("diff").description("Compare two local AgentInspect JSONL traces (read-only)").argument("<left-run-id>", "first run id").argument("<right-run-id>", "second run id").option("--dir <path>", "trace directory").option("--json", "print diff result as JSON").option("--ignore-duration", "omit duration comparisons").option(
|
|
12661
13029
|
"--duration-threshold <duration>",
|
|
12662
13030
|
"ignore duration deltas at or below this (e.g. 500ms, 2s, 1m)"
|
|
@@ -12749,9 +13117,9 @@ function isPrimaryModule() {
|
|
|
12749
13117
|
if (!entry) return false;
|
|
12750
13118
|
const selfPath = fileURLToPath(import.meta.url);
|
|
12751
13119
|
try {
|
|
12752
|
-
return realpathSync(
|
|
13120
|
+
return realpathSync(path13.resolve(entry)) === realpathSync(path13.resolve(selfPath));
|
|
12753
13121
|
} catch {
|
|
12754
|
-
return
|
|
13122
|
+
return path13.resolve(entry) === path13.resolve(selfPath);
|
|
12755
13123
|
}
|
|
12756
13124
|
}
|
|
12757
13125
|
if (isPrimaryModule()) {
|