agent-inspect 5.0.0 → 5.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 +12 -0
- package/docs/CLI.md +65 -0
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-TB45H3QT.mjs → chunk-SJ5R2XBE.mjs} +1043 -31
- package/packages/cli/dist/chunk-SJ5R2XBE.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +1543 -312
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +276 -164
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-ZRFSYML6.mjs → src-PK22BBMH.mjs} +3 -3
- package/packages/cli/dist/{src-ZRFSYML6.mjs.map → src-PK22BBMH.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +944 -12
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +134 -1
- package/packages/core/dist/advanced.d.ts +134 -1
- package/packages/core/dist/advanced.mjs +921 -8
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/cli/dist/chunk-TB45H3QT.mjs.map +0 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
var crypto = require('crypto');
|
|
5
5
|
var promises = require('fs/promises');
|
|
6
6
|
var os = require('os');
|
|
7
|
-
var
|
|
7
|
+
var path24 = require('path');
|
|
8
8
|
var async_hooks = require('async_hooks');
|
|
9
9
|
var process3 = require('process');
|
|
10
10
|
var tty = require('tty');
|
|
@@ -20,7 +20,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
20
20
|
|
|
21
21
|
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
22
22
|
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
23
|
-
var
|
|
23
|
+
var path24__default = /*#__PURE__*/_interopDefault(path24);
|
|
24
24
|
var process3__default = /*#__PURE__*/_interopDefault(process3);
|
|
25
25
|
var tty__default = /*#__PURE__*/_interopDefault(tty);
|
|
26
26
|
|
|
@@ -893,7 +893,7 @@ function getDefaultTraceDir() {
|
|
|
893
893
|
if (typeof home !== "string" || home.trim() === "") {
|
|
894
894
|
return FALLBACK_TRACE_DIR;
|
|
895
895
|
}
|
|
896
|
-
return
|
|
896
|
+
return path24__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
|
|
897
897
|
} catch {
|
|
898
898
|
return FALLBACK_TRACE_DIR;
|
|
899
899
|
}
|
|
@@ -901,11 +901,11 @@ function getDefaultTraceDir() {
|
|
|
901
901
|
function getTraceFilePath(runId, traceDir) {
|
|
902
902
|
const baseDir = traceDir ?? getDefaultTraceDir();
|
|
903
903
|
let safeId = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
|
|
904
|
-
safeId =
|
|
904
|
+
safeId = path24__default.default.basename(safeId);
|
|
905
905
|
if (safeId === "" || safeId === "." || safeId === "..") {
|
|
906
906
|
safeId = "run_unknown";
|
|
907
907
|
}
|
|
908
|
-
return
|
|
908
|
+
return path24__default.default.join(baseDir, `${safeId}.jsonl`);
|
|
909
909
|
}
|
|
910
910
|
function formatError(error) {
|
|
911
911
|
if (error instanceof Error) {
|
|
@@ -962,7 +962,7 @@ var init_utils = __esm({
|
|
|
962
962
|
init_duration();
|
|
963
963
|
DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
|
|
964
964
|
RUNS_DIR_NAME = "runs";
|
|
965
|
-
FALLBACK_TRACE_DIR =
|
|
965
|
+
FALLBACK_TRACE_DIR = path24__default.default.join(
|
|
966
966
|
os__default.default.tmpdir(),
|
|
967
967
|
"agent-inspect",
|
|
968
968
|
RUNS_DIR_NAME
|
|
@@ -1928,7 +1928,7 @@ var init_trace_directory = __esm({
|
|
|
1928
1928
|
this.#dir = resolveTraceDir(options);
|
|
1929
1929
|
}
|
|
1930
1930
|
getPath(filename) {
|
|
1931
|
-
return filename ?
|
|
1931
|
+
return filename ? path24__default.default.join(this.#dir, filename) : this.#dir;
|
|
1932
1932
|
}
|
|
1933
1933
|
async list() {
|
|
1934
1934
|
try {
|
|
@@ -1957,7 +1957,7 @@ function parseIsoToMs2(value) {
|
|
|
1957
1957
|
}
|
|
1958
1958
|
async function extractMetadata(filePath, _quickScan) {
|
|
1959
1959
|
const stats = await promises.stat(filePath);
|
|
1960
|
-
let runIdFromFile =
|
|
1960
|
+
let runIdFromFile = path24__default.default.basename(filePath);
|
|
1961
1961
|
if (runIdFromFile.endsWith(".jsonl")) {
|
|
1962
1962
|
runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
|
|
1963
1963
|
}
|
|
@@ -2100,7 +2100,7 @@ function buildRunSummary(events) {
|
|
|
2100
2100
|
}
|
|
2101
2101
|
let totalSteps = 0;
|
|
2102
2102
|
let llmSteps = 0;
|
|
2103
|
-
let
|
|
2103
|
+
let toolSteps2 = 0;
|
|
2104
2104
|
let logicSteps = 0;
|
|
2105
2105
|
let errorSteps = 0;
|
|
2106
2106
|
let maxDepth = 0;
|
|
@@ -2130,7 +2130,7 @@ function buildRunSummary(events) {
|
|
|
2130
2130
|
for (const [id, s] of steps.entries()) {
|
|
2131
2131
|
totalSteps += 1;
|
|
2132
2132
|
if (s.type === "llm") llmSteps += 1;
|
|
2133
|
-
else if (s.type === "tool")
|
|
2133
|
+
else if (s.type === "tool") toolSteps2 += 1;
|
|
2134
2134
|
else logicSteps += 1;
|
|
2135
2135
|
if (s.status === "error") errorSteps += 1;
|
|
2136
2136
|
const depth = computeDepth(id);
|
|
@@ -2164,7 +2164,7 @@ function buildRunSummary(events) {
|
|
|
2164
2164
|
durationMs: durationMs2,
|
|
2165
2165
|
totalSteps,
|
|
2166
2166
|
llmSteps,
|
|
2167
|
-
toolSteps,
|
|
2167
|
+
toolSteps: toolSteps2,
|
|
2168
2168
|
logicSteps,
|
|
2169
2169
|
errorSteps,
|
|
2170
2170
|
maxDepth,
|
|
@@ -2942,9 +2942,9 @@ async function searchTraces(metas, options) {
|
|
|
2942
2942
|
}
|
|
2943
2943
|
const limit = options.limit ?? 50;
|
|
2944
2944
|
const sessionId = options.session?.trim();
|
|
2945
|
-
const
|
|
2945
|
+
const observationStatus2 = parseObservationFilter(options.observation);
|
|
2946
2946
|
const hasContentFilter = Boolean(
|
|
2947
|
-
options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter ||
|
|
2947
|
+
options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus2
|
|
2948
2948
|
);
|
|
2949
2949
|
const results = [];
|
|
2950
2950
|
const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
|
|
@@ -2989,9 +2989,9 @@ async function searchTraces(metas, options) {
|
|
|
2989
2989
|
statusFilter: options.status
|
|
2990
2990
|
});
|
|
2991
2991
|
results.push(...stepMatches);
|
|
2992
|
-
if (
|
|
2992
|
+
if (observationStatus2) {
|
|
2993
2993
|
const outcomes = extractOutcomesFromTraceEvents(events);
|
|
2994
|
-
const matched = outcomes.filter((outcome) => outcome.status ===
|
|
2994
|
+
const matched = outcomes.filter((outcome) => outcome.status === observationStatus2);
|
|
2995
2995
|
for (const outcome of matched) {
|
|
2996
2996
|
results.push({
|
|
2997
2997
|
runId: m.runId,
|
|
@@ -3786,12 +3786,12 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3786
3786
|
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
3787
3787
|
);
|
|
3788
3788
|
const ordered = [...runs].sort(compareRuns);
|
|
3789
|
-
const
|
|
3789
|
+
const path33 = [];
|
|
3790
3790
|
const visited = /* @__PURE__ */ new Set();
|
|
3791
3791
|
const pushRun = (run, confidence, source) => {
|
|
3792
3792
|
if (visited.has(run.runId)) return;
|
|
3793
3793
|
visited.add(run.runId);
|
|
3794
|
-
|
|
3794
|
+
path33.push({
|
|
3795
3795
|
runId: run.runId,
|
|
3796
3796
|
name: run.name,
|
|
3797
3797
|
startedAt: run.startedAt,
|
|
@@ -3816,7 +3816,7 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3816
3816
|
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
3817
3817
|
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
3818
3818
|
}
|
|
3819
|
-
return
|
|
3819
|
+
return path33;
|
|
3820
3820
|
}
|
|
3821
3821
|
function metaRunIdMatches(run, token, runById) {
|
|
3822
3822
|
const meta2 = extractSessionWorkflowMetadata(run.metadata);
|
|
@@ -4147,7 +4147,7 @@ function normalizeBundleOutputPath(out) {
|
|
|
4147
4147
|
if (trimmed === "") {
|
|
4148
4148
|
throw new Error("--out requires a non-empty path.");
|
|
4149
4149
|
}
|
|
4150
|
-
const resolved =
|
|
4150
|
+
const resolved = path24__default.default.resolve(trimmed);
|
|
4151
4151
|
if (resolved.toLowerCase().endsWith(".zip")) {
|
|
4152
4152
|
return resolved.slice(0, -4);
|
|
4153
4153
|
}
|
|
@@ -4156,7 +4156,7 @@ function normalizeBundleOutputPath(out) {
|
|
|
4156
4156
|
function defaultBundleOutputPath(runIds) {
|
|
4157
4157
|
const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
|
|
4158
4158
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4159
|
-
return
|
|
4159
|
+
return path24__default.default.resolve(`agent-inspect-bundle-${label}-${stamp}`);
|
|
4160
4160
|
}
|
|
4161
4161
|
var init_paths = __esm({
|
|
4162
4162
|
"packages/core/src/bundle/paths.ts"() {
|
|
@@ -4330,7 +4330,7 @@ function normalizeSuiteConfig(value) {
|
|
|
4330
4330
|
}
|
|
4331
4331
|
async function validateSuiteConfig(config, options) {
|
|
4332
4332
|
const diagnostics = [];
|
|
4333
|
-
const tracesDir =
|
|
4333
|
+
const tracesDir = path24__default.default.resolve(options.configDir, config.traces);
|
|
4334
4334
|
try {
|
|
4335
4335
|
await promises.access(tracesDir);
|
|
4336
4336
|
} catch {
|
|
@@ -4340,7 +4340,7 @@ async function validateSuiteConfig(config, options) {
|
|
|
4340
4340
|
}
|
|
4341
4341
|
for (const suiteCase of config.cases) {
|
|
4342
4342
|
if (suiteCase.input !== void 0) {
|
|
4343
|
-
const inputPath =
|
|
4343
|
+
const inputPath = path24__default.default.resolve(options.configDir, suiteCase.input);
|
|
4344
4344
|
try {
|
|
4345
4345
|
await promises.access(inputPath);
|
|
4346
4346
|
} catch {
|
|
@@ -4373,12 +4373,12 @@ async function fileExists(filePath) {
|
|
|
4373
4373
|
}
|
|
4374
4374
|
}
|
|
4375
4375
|
async function resolveSuiteConfigPath(options = {}) {
|
|
4376
|
-
const cwd =
|
|
4376
|
+
const cwd = path24__default.default.resolve(options.cwd ?? process.cwd());
|
|
4377
4377
|
if (options.configPath !== void 0 && options.configPath.trim() !== "") {
|
|
4378
|
-
return
|
|
4378
|
+
return path24__default.default.resolve(cwd, options.configPath.trim());
|
|
4379
4379
|
}
|
|
4380
4380
|
for (const name of DEFAULT_SUITE_CONFIG_NAMES) {
|
|
4381
|
-
const candidate =
|
|
4381
|
+
const candidate = path24__default.default.join(cwd, name);
|
|
4382
4382
|
if (await fileExists(candidate)) return candidate;
|
|
4383
4383
|
}
|
|
4384
4384
|
throw new Error(
|
|
@@ -4395,7 +4395,7 @@ async function loadSuiteConfig(options = {}) {
|
|
|
4395
4395
|
diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
|
|
4396
4396
|
});
|
|
4397
4397
|
}
|
|
4398
|
-
const extension =
|
|
4398
|
+
const extension = path24__default.default.extname(configPath);
|
|
4399
4399
|
if (TS_CONFIG_EXTENSIONS.has(extension)) {
|
|
4400
4400
|
const message = "TypeScript suite configs require an explicit precompiled JavaScript config or future --config-loader support.";
|
|
4401
4401
|
throw Object.assign(new Error(message), {
|
|
@@ -4420,7 +4420,7 @@ async function loadSuiteConfig(options = {}) {
|
|
|
4420
4420
|
return {
|
|
4421
4421
|
config,
|
|
4422
4422
|
configPath,
|
|
4423
|
-
configDir:
|
|
4423
|
+
configDir: path24__default.default.dirname(configPath)
|
|
4424
4424
|
};
|
|
4425
4425
|
} catch (error) {
|
|
4426
4426
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -4470,7 +4470,7 @@ async function exists(filePath) {
|
|
|
4470
4470
|
}
|
|
4471
4471
|
async function resolveSuiteCaseTrace(suiteCase, options) {
|
|
4472
4472
|
if (suiteCase.trace !== void 0) {
|
|
4473
|
-
const tracePath =
|
|
4473
|
+
const tracePath = path24__default.default.resolve(options.configDir, suiteCase.trace);
|
|
4474
4474
|
if (await exists(tracePath)) {
|
|
4475
4475
|
return { caseId: suiteCase.id, tracePath, missing: false };
|
|
4476
4476
|
}
|
|
@@ -4486,7 +4486,7 @@ async function resolveSuiteCaseTrace(suiteCase, options) {
|
|
|
4486
4486
|
if (await exists(directPath)) {
|
|
4487
4487
|
return { caseId: suiteCase.id, tracePath: directPath, runId: runKey, missing: false };
|
|
4488
4488
|
}
|
|
4489
|
-
const nestedPath =
|
|
4489
|
+
const nestedPath = path24__default.default.join(options.tracesDir, `${path24__default.default.basename(runKey)}.jsonl`);
|
|
4490
4490
|
if (await exists(nestedPath)) {
|
|
4491
4491
|
return { caseId: suiteCase.id, tracePath: nestedPath, runId: runKey, missing: false };
|
|
4492
4492
|
}
|
|
@@ -4716,7 +4716,7 @@ function stripPrefix(name, prefixes) {
|
|
|
4716
4716
|
}
|
|
4717
4717
|
return name;
|
|
4718
4718
|
}
|
|
4719
|
-
function eventEvidence(event,
|
|
4719
|
+
function eventEvidence(event, path33) {
|
|
4720
4720
|
return {
|
|
4721
4721
|
runId: event.runId,
|
|
4722
4722
|
eventId: event.eventId,
|
|
@@ -4726,7 +4726,7 @@ function eventEvidence(event, path30) {
|
|
|
4726
4726
|
kind: event.kind,
|
|
4727
4727
|
name: event.name,
|
|
4728
4728
|
status: event.status,
|
|
4729
|
-
...
|
|
4729
|
+
...path33 ? { path: path33 } : {}
|
|
4730
4730
|
};
|
|
4731
4731
|
}
|
|
4732
4732
|
function runEvidence(run) {
|
|
@@ -4789,9 +4789,9 @@ function eventEndMs(event) {
|
|
|
4789
4789
|
function normalizedKey(value) {
|
|
4790
4790
|
return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
4791
4791
|
}
|
|
4792
|
-
function lastPathSegment(
|
|
4793
|
-
const parts =
|
|
4794
|
-
return parts[parts.length - 1] ??
|
|
4792
|
+
function lastPathSegment(path33) {
|
|
4793
|
+
const parts = path33.split(".");
|
|
4794
|
+
return parts[parts.length - 1] ?? path33;
|
|
4795
4795
|
}
|
|
4796
4796
|
function valueType(value) {
|
|
4797
4797
|
if (Array.isArray(value)) return "array";
|
|
@@ -4805,12 +4805,12 @@ function serializedByteLength(value) {
|
|
|
4805
4805
|
return void 0;
|
|
4806
4806
|
}
|
|
4807
4807
|
}
|
|
4808
|
-
function pushValueEntries(entries, event, value,
|
|
4809
|
-
entries.push({ event, path:
|
|
4808
|
+
function pushValueEntries(entries, event, value, path33, key, depth = 0) {
|
|
4809
|
+
entries.push({ event, path: path33, key, value });
|
|
4810
4810
|
if (depth >= 8) return;
|
|
4811
4811
|
if (Array.isArray(value)) {
|
|
4812
4812
|
for (const [index, item] of value.entries()) {
|
|
4813
|
-
pushValueEntries(entries, event, item, `${
|
|
4813
|
+
pushValueEntries(entries, event, item, `${path33}.${index}`, String(index), depth + 1);
|
|
4814
4814
|
}
|
|
4815
4815
|
return;
|
|
4816
4816
|
}
|
|
@@ -4820,7 +4820,7 @@ function pushValueEntries(entries, event, value, path30, key, depth = 0) {
|
|
|
4820
4820
|
entries,
|
|
4821
4821
|
event,
|
|
4822
4822
|
value[nestedKey],
|
|
4823
|
-
`${
|
|
4823
|
+
`${path33}.${nestedKey}`,
|
|
4824
4824
|
nestedKey,
|
|
4825
4825
|
depth + 1
|
|
4826
4826
|
);
|
|
@@ -4901,9 +4901,9 @@ function eventDurationMs(event) {
|
|
|
4901
4901
|
}
|
|
4902
4902
|
function treeShape(nodes) {
|
|
4903
4903
|
const lines = [];
|
|
4904
|
-
const visit = (node,
|
|
4905
|
-
lines.push(`${
|
|
4906
|
-
node.children.forEach((child, index) => visit(child, `${
|
|
4904
|
+
const visit = (node, path33) => {
|
|
4905
|
+
lines.push(`${path33}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
|
|
4906
|
+
node.children.forEach((child, index) => visit(child, `${path33}.${index}`));
|
|
4907
4907
|
};
|
|
4908
4908
|
nodes.forEach((node, index) => visit(node, String(index)));
|
|
4909
4909
|
return lines;
|
|
@@ -4952,9 +4952,9 @@ function retrievalShape(context) {
|
|
|
4952
4952
|
function guardrailShape(context) {
|
|
4953
4953
|
return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
|
|
4954
4954
|
}
|
|
4955
|
-
function firstEvidenceForKind(context, kind,
|
|
4955
|
+
function firstEvidenceForKind(context, kind, path33) {
|
|
4956
4956
|
const event = context.events.find((candidate) => candidate.kind === kind);
|
|
4957
|
-
return event ? [eventEvidence(event,
|
|
4957
|
+
return event ? [eventEvidence(event, path33)] : runEvidence(context.selectedRun);
|
|
4958
4958
|
}
|
|
4959
4959
|
function baselineDiffFinding(message, evidence, expected, actual) {
|
|
4960
4960
|
return failFinding("baseline.regression", message, evidence, expected, actual);
|
|
@@ -5304,13 +5304,13 @@ function createStructureCycleRule() {
|
|
|
5304
5304
|
const seenCycles = /* @__PURE__ */ new Set();
|
|
5305
5305
|
const findings = [];
|
|
5306
5306
|
for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
|
|
5307
|
-
const
|
|
5307
|
+
const path33 = [];
|
|
5308
5308
|
const seenAt = /* @__PURE__ */ new Map();
|
|
5309
5309
|
let current = event;
|
|
5310
5310
|
while (current) {
|
|
5311
5311
|
const existing = seenAt.get(current.eventId);
|
|
5312
5312
|
if (existing !== void 0) {
|
|
5313
|
-
const cycle =
|
|
5313
|
+
const cycle = path33.slice(existing);
|
|
5314
5314
|
const key = cycle.map((item) => item.eventId).sort().join("\0");
|
|
5315
5315
|
if (!seenCycles.has(key)) {
|
|
5316
5316
|
seenCycles.add(key);
|
|
@@ -5326,8 +5326,8 @@ function createStructureCycleRule() {
|
|
|
5326
5326
|
}
|
|
5327
5327
|
break;
|
|
5328
5328
|
}
|
|
5329
|
-
seenAt.set(current.eventId,
|
|
5330
|
-
|
|
5329
|
+
seenAt.set(current.eventId, path33.length);
|
|
5330
|
+
path33.push(current);
|
|
5331
5331
|
current = current.parentId ? byId.get(current.parentId) : void 0;
|
|
5332
5332
|
}
|
|
5333
5333
|
}
|
|
@@ -6468,7 +6468,7 @@ function findReaderByFormat(format, readers) {
|
|
|
6468
6468
|
}
|
|
6469
6469
|
async function jsonlFilesInDirectory(dirPath) {
|
|
6470
6470
|
const entries = await promises.readdir(dirPath, { withFileTypes: true });
|
|
6471
|
-
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) =>
|
|
6471
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path24__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
6472
6472
|
}
|
|
6473
6473
|
async function resolveInput(input3) {
|
|
6474
6474
|
const cached = resolvedInputCache.get(input3);
|
|
@@ -8167,7 +8167,7 @@ async function runSuiteCase(suiteCase, config, options) {
|
|
|
8167
8167
|
async function runSuite(options = {}) {
|
|
8168
8168
|
const startedAt = new Date(options.nowMs ?? Date.now()).toISOString();
|
|
8169
8169
|
const { config, configPath, configDir } = await loadSuiteConfig(options);
|
|
8170
|
-
const tracesDir =
|
|
8170
|
+
const tracesDir = path24__default.default.resolve(configDir, config.traces);
|
|
8171
8171
|
const cases = [];
|
|
8172
8172
|
const diagnostics = [];
|
|
8173
8173
|
for (const suiteCase of config.cases) {
|
|
@@ -8275,6 +8275,1102 @@ var init_suite = __esm({
|
|
|
8275
8275
|
}
|
|
8276
8276
|
});
|
|
8277
8277
|
|
|
8278
|
+
// packages/core/src/cohort/types.ts
|
|
8279
|
+
var COHORT_METRIC_IDS;
|
|
8280
|
+
var init_types5 = __esm({
|
|
8281
|
+
"packages/core/src/cohort/types.ts"() {
|
|
8282
|
+
COHORT_METRIC_IDS = [
|
|
8283
|
+
"errorRate",
|
|
8284
|
+
"duration",
|
|
8285
|
+
"toolChoice",
|
|
8286
|
+
"toolOrdering",
|
|
8287
|
+
"llmCallCount",
|
|
8288
|
+
"tokenUsage",
|
|
8289
|
+
"retryCount",
|
|
8290
|
+
"observationFailure",
|
|
8291
|
+
"guardrailFailure",
|
|
8292
|
+
"circuitViolation",
|
|
8293
|
+
"redactionWarning"
|
|
8294
|
+
];
|
|
8295
|
+
}
|
|
8296
|
+
});
|
|
8297
|
+
|
|
8298
|
+
// packages/core/src/cohort/compare.ts
|
|
8299
|
+
function compareNumber(metric, label, baseline, candidate, higherIsWorse = true) {
|
|
8300
|
+
if (baseline === void 0 && candidate === void 0) return void 0;
|
|
8301
|
+
const delta = baseline !== void 0 && candidate !== void 0 ? candidate - baseline : void 0;
|
|
8302
|
+
const regression = delta !== void 0 && (higherIsWorse && delta > 0 || !higherIsWorse && delta < 0);
|
|
8303
|
+
return {
|
|
8304
|
+
metric,
|
|
8305
|
+
baseline,
|
|
8306
|
+
candidate,
|
|
8307
|
+
delta,
|
|
8308
|
+
regression,
|
|
8309
|
+
message: `${label}: baseline=${baseline ?? "n/a"} candidate=${candidate ?? "n/a"}${delta !== void 0 ? ` (delta ${delta})` : ""}`
|
|
8310
|
+
};
|
|
8311
|
+
}
|
|
8312
|
+
function pickAggregate(groups, cohortLabel, groupKey) {
|
|
8313
|
+
return groups.find(
|
|
8314
|
+
(group) => group.cohortLabel === cohortLabel && (groupKey === void 0 || group.groupKey === groupKey)
|
|
8315
|
+
);
|
|
8316
|
+
}
|
|
8317
|
+
function compareCohortAggregates(groups, options) {
|
|
8318
|
+
const baselineAgg = pickAggregate(groups, options.baseline, options.groupKey);
|
|
8319
|
+
const candidateAgg = pickAggregate(groups, options.candidate, options.groupKey);
|
|
8320
|
+
const comparisons = [];
|
|
8321
|
+
for (const metric of options.metrics) {
|
|
8322
|
+
switch (metric) {
|
|
8323
|
+
case "errorRate": {
|
|
8324
|
+
const item = compareNumber(
|
|
8325
|
+
metric,
|
|
8326
|
+
"Error rate",
|
|
8327
|
+
baselineAgg?.errorRate,
|
|
8328
|
+
candidateAgg?.errorRate
|
|
8329
|
+
);
|
|
8330
|
+
if (item) comparisons.push(item);
|
|
8331
|
+
break;
|
|
8332
|
+
}
|
|
8333
|
+
case "duration": {
|
|
8334
|
+
const item = compareNumber(
|
|
8335
|
+
metric,
|
|
8336
|
+
"Average duration (ms)",
|
|
8337
|
+
baselineAgg?.avgDurationMs,
|
|
8338
|
+
candidateAgg?.avgDurationMs
|
|
8339
|
+
);
|
|
8340
|
+
if (item) comparisons.push(item);
|
|
8341
|
+
break;
|
|
8342
|
+
}
|
|
8343
|
+
case "llmCallCount": {
|
|
8344
|
+
const item = compareNumber(
|
|
8345
|
+
metric,
|
|
8346
|
+
"Average LLM calls",
|
|
8347
|
+
baselineAgg?.avgLlmCallCount,
|
|
8348
|
+
candidateAgg?.avgLlmCallCount
|
|
8349
|
+
);
|
|
8350
|
+
if (item) comparisons.push(item);
|
|
8351
|
+
break;
|
|
8352
|
+
}
|
|
8353
|
+
case "tokenUsage": {
|
|
8354
|
+
const item = compareNumber(
|
|
8355
|
+
metric,
|
|
8356
|
+
"Average token usage",
|
|
8357
|
+
baselineAgg?.avgTokenUsage,
|
|
8358
|
+
candidateAgg?.avgTokenUsage
|
|
8359
|
+
);
|
|
8360
|
+
if (item) comparisons.push(item);
|
|
8361
|
+
break;
|
|
8362
|
+
}
|
|
8363
|
+
case "retryCount": {
|
|
8364
|
+
const item = compareNumber(
|
|
8365
|
+
metric,
|
|
8366
|
+
"Average retries",
|
|
8367
|
+
baselineAgg?.avgRetryCount,
|
|
8368
|
+
candidateAgg?.avgRetryCount
|
|
8369
|
+
);
|
|
8370
|
+
if (item) comparisons.push(item);
|
|
8371
|
+
break;
|
|
8372
|
+
}
|
|
8373
|
+
case "observationFailure": {
|
|
8374
|
+
const item = compareNumber(
|
|
8375
|
+
metric,
|
|
8376
|
+
"Observation failure rate",
|
|
8377
|
+
baselineAgg?.observationFailureRate,
|
|
8378
|
+
candidateAgg?.observationFailureRate
|
|
8379
|
+
);
|
|
8380
|
+
if (item) comparisons.push(item);
|
|
8381
|
+
break;
|
|
8382
|
+
}
|
|
8383
|
+
case "toolChoice": {
|
|
8384
|
+
const baselineValue = baselineAgg?.dominantToolChoice;
|
|
8385
|
+
const candidateValue = candidateAgg?.dominantToolChoice;
|
|
8386
|
+
comparisons.push({
|
|
8387
|
+
metric,
|
|
8388
|
+
baseline: baselineValue,
|
|
8389
|
+
candidate: candidateValue,
|
|
8390
|
+
delta: baselineValue === candidateValue ? "same" : "changed",
|
|
8391
|
+
regression: baselineValue !== candidateValue,
|
|
8392
|
+
message: `Tool choice: baseline=${baselineValue ?? "n/a"} candidate=${candidateValue ?? "n/a"}`
|
|
8393
|
+
});
|
|
8394
|
+
break;
|
|
8395
|
+
}
|
|
8396
|
+
case "toolOrdering": {
|
|
8397
|
+
const baselineValue = baselineAgg?.toolOrderingSignature;
|
|
8398
|
+
const candidateValue = candidateAgg?.toolOrderingSignature;
|
|
8399
|
+
comparisons.push({
|
|
8400
|
+
metric,
|
|
8401
|
+
baseline: baselineValue,
|
|
8402
|
+
candidate: candidateValue,
|
|
8403
|
+
delta: baselineValue === candidateValue ? "same" : "changed",
|
|
8404
|
+
regression: baselineValue !== candidateValue,
|
|
8405
|
+
message: `Tool ordering: baseline=${baselineValue ?? "n/a"} candidate=${candidateValue ?? "n/a"}`
|
|
8406
|
+
});
|
|
8407
|
+
break;
|
|
8408
|
+
}
|
|
8409
|
+
case "guardrailFailure": {
|
|
8410
|
+
const item = compareNumber(
|
|
8411
|
+
metric,
|
|
8412
|
+
"Guardrail failures",
|
|
8413
|
+
baselineAgg?.avgGuardrailFailures,
|
|
8414
|
+
candidateAgg?.avgGuardrailFailures
|
|
8415
|
+
);
|
|
8416
|
+
if (item) comparisons.push(item);
|
|
8417
|
+
break;
|
|
8418
|
+
}
|
|
8419
|
+
case "circuitViolation": {
|
|
8420
|
+
const item = compareNumber(
|
|
8421
|
+
metric,
|
|
8422
|
+
"Circuit violations",
|
|
8423
|
+
baselineAgg?.avgCircuitViolations,
|
|
8424
|
+
candidateAgg?.avgCircuitViolations
|
|
8425
|
+
);
|
|
8426
|
+
if (item) comparisons.push(item);
|
|
8427
|
+
break;
|
|
8428
|
+
}
|
|
8429
|
+
case "redactionWarning": {
|
|
8430
|
+
const item = compareNumber(
|
|
8431
|
+
metric,
|
|
8432
|
+
"Redaction warnings",
|
|
8433
|
+
baselineAgg?.avgRedactionWarnings,
|
|
8434
|
+
candidateAgg?.avgRedactionWarnings
|
|
8435
|
+
);
|
|
8436
|
+
if (item) comparisons.push(item);
|
|
8437
|
+
break;
|
|
8438
|
+
}
|
|
8439
|
+
}
|
|
8440
|
+
}
|
|
8441
|
+
return comparisons;
|
|
8442
|
+
}
|
|
8443
|
+
var init_compare = __esm({
|
|
8444
|
+
"packages/core/src/cohort/compare.ts"() {
|
|
8445
|
+
}
|
|
8446
|
+
});
|
|
8447
|
+
|
|
8448
|
+
// packages/core/src/cohort/grouping.ts
|
|
8449
|
+
function parseCohortMetricList(value) {
|
|
8450
|
+
if (value === void 0 || value.trim() === "") return [];
|
|
8451
|
+
return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
8452
|
+
}
|
|
8453
|
+
function parseGroupBySpec(groupBy) {
|
|
8454
|
+
const raw = (groupBy ?? "model").trim();
|
|
8455
|
+
if (raw === "model") return { kind: "model" };
|
|
8456
|
+
if (raw === "session") return { kind: "session" };
|
|
8457
|
+
if (raw === "group") return { kind: "group" };
|
|
8458
|
+
if (raw.startsWith("metadata.")) {
|
|
8459
|
+
const metadataKey = raw.slice("metadata.".length).trim();
|
|
8460
|
+
if (metadataKey === "") throw new Error("metadata group-by requires a key.");
|
|
8461
|
+
return { kind: "metadata", metadataKey };
|
|
8462
|
+
}
|
|
8463
|
+
throw new Error(`Unsupported --group-by value: ${raw}`);
|
|
8464
|
+
}
|
|
8465
|
+
function metadataString(metadata, key) {
|
|
8466
|
+
const value = metadata?.[key];
|
|
8467
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
8468
|
+
}
|
|
8469
|
+
function resolveRunGroupKey(run, groupBy) {
|
|
8470
|
+
const metadata = run.metadata ?? {};
|
|
8471
|
+
switch (groupBy.kind) {
|
|
8472
|
+
case "model":
|
|
8473
|
+
return metadataString(metadata, "model") ?? "unknown";
|
|
8474
|
+
case "session":
|
|
8475
|
+
return extractSessionWorkflowMetadata(metadata)?.sessionId ?? metadataString(metadata, "sessionId") ?? "__unscoped__";
|
|
8476
|
+
case "group":
|
|
8477
|
+
return extractSessionWorkflowMetadata(metadata)?.groupId ?? metadataString(metadata, "groupId") ?? "__unscoped__";
|
|
8478
|
+
case "metadata":
|
|
8479
|
+
return metadataString(metadata, groupBy.metadataKey) ?? "__missing__";
|
|
8480
|
+
default:
|
|
8481
|
+
return "unknown";
|
|
8482
|
+
}
|
|
8483
|
+
}
|
|
8484
|
+
function resolveCohortLabel(run, cohortKey, baseline, candidate) {
|
|
8485
|
+
const label = metadataString(run.metadata, cohortKey);
|
|
8486
|
+
if (label === void 0) return void 0;
|
|
8487
|
+
if (baseline !== void 0 && label === baseline) return baseline;
|
|
8488
|
+
if (candidate !== void 0 && label === candidate) return candidate;
|
|
8489
|
+
if (baseline === void 0 && candidate === void 0) return label;
|
|
8490
|
+
return void 0;
|
|
8491
|
+
}
|
|
8492
|
+
function filterRunsForCohort(runs, options) {
|
|
8493
|
+
const warnings = [];
|
|
8494
|
+
if (options.baseline === void 0 && options.candidate === void 0) {
|
|
8495
|
+
return { runs: [...runs], warnings };
|
|
8496
|
+
}
|
|
8497
|
+
const selected = [];
|
|
8498
|
+
for (const run of runs) {
|
|
8499
|
+
const label = resolveCohortLabel(
|
|
8500
|
+
run,
|
|
8501
|
+
options.cohortKey,
|
|
8502
|
+
options.baseline,
|
|
8503
|
+
options.candidate
|
|
8504
|
+
);
|
|
8505
|
+
if (label !== void 0) selected.push(run);
|
|
8506
|
+
}
|
|
8507
|
+
if (selected.length === 0) {
|
|
8508
|
+
warnings.push(
|
|
8509
|
+
`No runs matched baseline/candidate labels on metadata.${options.cohortKey}.`
|
|
8510
|
+
);
|
|
8511
|
+
}
|
|
8512
|
+
return { runs: selected, warnings };
|
|
8513
|
+
}
|
|
8514
|
+
var init_grouping = __esm({
|
|
8515
|
+
"packages/core/src/cohort/grouping.ts"() {
|
|
8516
|
+
init_metadata();
|
|
8517
|
+
}
|
|
8518
|
+
});
|
|
8519
|
+
|
|
8520
|
+
// packages/core/src/cohort/metrics.ts
|
|
8521
|
+
function asNumber(value) {
|
|
8522
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
8523
|
+
}
|
|
8524
|
+
function toolSteps(events) {
|
|
8525
|
+
const ordering = [];
|
|
8526
|
+
const choices = /* @__PURE__ */ new Set();
|
|
8527
|
+
const sorted = [...events].filter((event) => event.event === "step_started").sort((a, b) => a.timestamp - b.timestamp);
|
|
8528
|
+
for (const event of sorted) {
|
|
8529
|
+
const step = event;
|
|
8530
|
+
if (step.type !== "tool") continue;
|
|
8531
|
+
const name = typeof step.metadata?.toolName === "string" ? step.metadata.toolName : step.name;
|
|
8532
|
+
ordering.push(name);
|
|
8533
|
+
choices.add(name);
|
|
8534
|
+
}
|
|
8535
|
+
return { choices: [...choices].sort(), ordering };
|
|
8536
|
+
}
|
|
8537
|
+
async function computeCohortRunMetrics(input3) {
|
|
8538
|
+
const events = await readTraceEventsFromFile(input3.filePath);
|
|
8539
|
+
const summary = buildRunSummary(events);
|
|
8540
|
+
const tools = toolSteps(events);
|
|
8541
|
+
const outcomes = extractOutcomesFromTraceEvents(events);
|
|
8542
|
+
const observationFailures = outcomes.filter((item) => item.status === "failed").length;
|
|
8543
|
+
const metadata = input3.metadata ?? {};
|
|
8544
|
+
const retryCount2 = asNumber(metadata.attempt) !== void 0 && asNumber(metadata.attempt) > 1 ? asNumber(metadata.attempt) - 1 : typeof metadata.retryOf === "string" ? 1 : 0;
|
|
8545
|
+
return {
|
|
8546
|
+
runId: input3.runId,
|
|
8547
|
+
...input3.cohortLabel !== void 0 ? { cohortLabel: input3.cohortLabel } : {},
|
|
8548
|
+
groupKey: input3.groupKey,
|
|
8549
|
+
status: summary.status,
|
|
8550
|
+
error: summary.status === "error",
|
|
8551
|
+
durationMs: summary.durationMs ?? input3.durationMs,
|
|
8552
|
+
llmCallCount: summary.llmSteps,
|
|
8553
|
+
tokenUsageTotal: summary.totalTokens?.total,
|
|
8554
|
+
retryCount: retryCount2,
|
|
8555
|
+
observationFailures,
|
|
8556
|
+
guardrailFailures: asNumber(metadata.guardrailFailures) ?? 0,
|
|
8557
|
+
circuitViolations: asNumber(metadata.circuitViolations) ?? 0,
|
|
8558
|
+
redactionWarnings: asNumber(metadata.redactionWarnings) ?? 0,
|
|
8559
|
+
toolChoices: tools.choices,
|
|
8560
|
+
toolOrdering: tools.ordering
|
|
8561
|
+
};
|
|
8562
|
+
}
|
|
8563
|
+
function percentile2(values, p) {
|
|
8564
|
+
if (values.length === 0) return void 0;
|
|
8565
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
8566
|
+
const idx = Math.min(
|
|
8567
|
+
sorted.length - 1,
|
|
8568
|
+
Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
|
|
8569
|
+
);
|
|
8570
|
+
return sorted[idx];
|
|
8571
|
+
}
|
|
8572
|
+
function dominantToolChoice(runs) {
|
|
8573
|
+
const counts = /* @__PURE__ */ new Map();
|
|
8574
|
+
for (const run of runs) {
|
|
8575
|
+
const signature = run.toolChoices.join(",");
|
|
8576
|
+
if (signature === "") continue;
|
|
8577
|
+
counts.set(signature, (counts.get(signature) ?? 0) + 1);
|
|
8578
|
+
}
|
|
8579
|
+
let best;
|
|
8580
|
+
let bestCount = 0;
|
|
8581
|
+
for (const [key, count] of counts) {
|
|
8582
|
+
if (count > bestCount) {
|
|
8583
|
+
best = key;
|
|
8584
|
+
bestCount = count;
|
|
8585
|
+
}
|
|
8586
|
+
}
|
|
8587
|
+
return best;
|
|
8588
|
+
}
|
|
8589
|
+
function orderingSignature(runs) {
|
|
8590
|
+
const counts = /* @__PURE__ */ new Map();
|
|
8591
|
+
for (const run of runs) {
|
|
8592
|
+
const signature = run.toolOrdering.join(">");
|
|
8593
|
+
if (signature === "") continue;
|
|
8594
|
+
counts.set(signature, (counts.get(signature) ?? 0) + 1);
|
|
8595
|
+
}
|
|
8596
|
+
let best;
|
|
8597
|
+
let bestCount = 0;
|
|
8598
|
+
for (const [key, count] of counts) {
|
|
8599
|
+
if (count > bestCount) {
|
|
8600
|
+
best = key;
|
|
8601
|
+
bestCount = count;
|
|
8602
|
+
}
|
|
8603
|
+
}
|
|
8604
|
+
return best;
|
|
8605
|
+
}
|
|
8606
|
+
function aggregateCohortMetrics(runs, groupKey, cohortLabel) {
|
|
8607
|
+
const durations = runs.map((run) => run.durationMs).filter((value) => typeof value === "number");
|
|
8608
|
+
const tokenValues = runs.map((run) => run.tokenUsageTotal).filter((value) => typeof value === "number");
|
|
8609
|
+
const errors = runs.filter((run) => run.error).length;
|
|
8610
|
+
const observationFailures = runs.reduce((sum, run) => sum + run.observationFailures, 0);
|
|
8611
|
+
return {
|
|
8612
|
+
groupKey,
|
|
8613
|
+
...cohortLabel !== void 0 ? { cohortLabel } : {},
|
|
8614
|
+
runCount: runs.length,
|
|
8615
|
+
errorRate: runs.length > 0 ? errors / runs.length : 0,
|
|
8616
|
+
avgDurationMs: durations.length > 0 ? durations.reduce((sum, value) => sum + value, 0) / durations.length : void 0,
|
|
8617
|
+
p95DurationMs: percentile2(durations, 95),
|
|
8618
|
+
avgLlmCallCount: runs.length > 0 ? runs.reduce((sum, run) => sum + run.llmCallCount, 0) / runs.length : 0,
|
|
8619
|
+
avgTokenUsage: tokenValues.length > 0 ? tokenValues.reduce((sum, value) => sum + value, 0) / tokenValues.length : void 0,
|
|
8620
|
+
avgRetryCount: runs.length > 0 ? runs.reduce((sum, run) => sum + run.retryCount, 0) / runs.length : 0,
|
|
8621
|
+
observationFailureRate: runs.length > 0 ? observationFailures / runs.length : 0,
|
|
8622
|
+
avgGuardrailFailures: runs.length > 0 ? runs.reduce((sum, run) => sum + run.guardrailFailures, 0) / runs.length : 0,
|
|
8623
|
+
avgCircuitViolations: runs.length > 0 ? runs.reduce((sum, run) => sum + run.circuitViolations, 0) / runs.length : 0,
|
|
8624
|
+
avgRedactionWarnings: runs.length > 0 ? runs.reduce((sum, run) => sum + run.redactionWarnings, 0) / runs.length : 0,
|
|
8625
|
+
dominantToolChoice: dominantToolChoice(runs),
|
|
8626
|
+
toolOrderingSignature: orderingSignature(runs)
|
|
8627
|
+
};
|
|
8628
|
+
}
|
|
8629
|
+
var init_metrics = __esm({
|
|
8630
|
+
"packages/core/src/cohort/metrics.ts"() {
|
|
8631
|
+
init_extract();
|
|
8632
|
+
init_trace_metadata();
|
|
8633
|
+
init_storage();
|
|
8634
|
+
}
|
|
8635
|
+
});
|
|
8636
|
+
|
|
8637
|
+
// packages/core/src/cohort/analyze.ts
|
|
8638
|
+
function normalizeMetrics(metrics) {
|
|
8639
|
+
if (metrics === void 0 || metrics.length === 0) return [...DEFAULT_METRICS];
|
|
8640
|
+
const allowed = new Set(COHORT_METRIC_IDS);
|
|
8641
|
+
return metrics.filter((metric) => allowed.has(metric));
|
|
8642
|
+
}
|
|
8643
|
+
async function analyzeCohort(runsInput, options) {
|
|
8644
|
+
const cohortKey = options.cohortKey ?? "cohort";
|
|
8645
|
+
const groupBySpec = parseGroupBySpec(options.groupBy);
|
|
8646
|
+
const metrics = normalizeMetrics(options.metrics);
|
|
8647
|
+
const { runs: filteredRuns, warnings } = filterRunsForCohort(runsInput, {
|
|
8648
|
+
cohortKey,
|
|
8649
|
+
baseline: options.baseline,
|
|
8650
|
+
candidate: options.candidate
|
|
8651
|
+
});
|
|
8652
|
+
const runMetrics = [];
|
|
8653
|
+
for (const run of filteredRuns) {
|
|
8654
|
+
if (run.filePath === void 0) continue;
|
|
8655
|
+
const cohortLabel = resolveCohortLabel(
|
|
8656
|
+
run,
|
|
8657
|
+
cohortKey,
|
|
8658
|
+
options.baseline,
|
|
8659
|
+
options.candidate
|
|
8660
|
+
);
|
|
8661
|
+
runMetrics.push(
|
|
8662
|
+
await computeCohortRunMetrics({
|
|
8663
|
+
runId: run.runId,
|
|
8664
|
+
filePath: run.filePath,
|
|
8665
|
+
metadata: run.metadata,
|
|
8666
|
+
status: run.status,
|
|
8667
|
+
durationMs: run.durationMs,
|
|
8668
|
+
groupKey: resolveRunGroupKey(run, groupBySpec),
|
|
8669
|
+
cohortLabel
|
|
8670
|
+
})
|
|
8671
|
+
);
|
|
8672
|
+
}
|
|
8673
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
8674
|
+
for (const run of runMetrics) {
|
|
8675
|
+
const key = `${run.cohortLabel ?? "*"}::${run.groupKey}`;
|
|
8676
|
+
const bucket = groupMap.get(key) ?? [];
|
|
8677
|
+
bucket.push(run);
|
|
8678
|
+
groupMap.set(key, bucket);
|
|
8679
|
+
}
|
|
8680
|
+
const groups = [...groupMap.entries()].sort(([a], [b]) => a.localeCompare(b)).map(
|
|
8681
|
+
([, bucket]) => aggregateCohortMetrics(
|
|
8682
|
+
bucket,
|
|
8683
|
+
bucket[0].groupKey,
|
|
8684
|
+
bucket[0]?.cohortLabel
|
|
8685
|
+
)
|
|
8686
|
+
);
|
|
8687
|
+
const comparisons = options.baseline !== void 0 && options.candidate !== void 0 ? (() => {
|
|
8688
|
+
const groupKeys = [
|
|
8689
|
+
...new Set(groups.map((group) => group.groupKey))
|
|
8690
|
+
].sort((a, b) => a.localeCompare(b));
|
|
8691
|
+
const items = [];
|
|
8692
|
+
for (const groupKey of groupKeys) {
|
|
8693
|
+
items.push(
|
|
8694
|
+
...compareCohortAggregates(groups, {
|
|
8695
|
+
baseline: options.baseline,
|
|
8696
|
+
candidate: options.candidate,
|
|
8697
|
+
metrics,
|
|
8698
|
+
groupKey
|
|
8699
|
+
})
|
|
8700
|
+
);
|
|
8701
|
+
}
|
|
8702
|
+
return items;
|
|
8703
|
+
})() : [];
|
|
8704
|
+
const regression = comparisons.some((item) => item.regression);
|
|
8705
|
+
return {
|
|
8706
|
+
ok: !regression,
|
|
8707
|
+
traceDir: options.traceDir,
|
|
8708
|
+
...options.baseline !== void 0 ? { baseline: options.baseline } : {},
|
|
8709
|
+
...options.candidate !== void 0 ? { candidate: options.candidate } : {},
|
|
8710
|
+
cohortKey,
|
|
8711
|
+
groupBy: options.groupBy ?? "model",
|
|
8712
|
+
metrics,
|
|
8713
|
+
groups,
|
|
8714
|
+
comparisons,
|
|
8715
|
+
runs: runMetrics,
|
|
8716
|
+
warnings
|
|
8717
|
+
};
|
|
8718
|
+
}
|
|
8719
|
+
var DEFAULT_METRICS;
|
|
8720
|
+
var init_analyze = __esm({
|
|
8721
|
+
"packages/core/src/cohort/analyze.ts"() {
|
|
8722
|
+
init_compare();
|
|
8723
|
+
init_grouping();
|
|
8724
|
+
init_metrics();
|
|
8725
|
+
init_types5();
|
|
8726
|
+
DEFAULT_METRICS = [
|
|
8727
|
+
"errorRate",
|
|
8728
|
+
"duration",
|
|
8729
|
+
"toolChoice",
|
|
8730
|
+
"observationFailure"
|
|
8731
|
+
];
|
|
8732
|
+
}
|
|
8733
|
+
});
|
|
8734
|
+
|
|
8735
|
+
// packages/core/src/exporters/helpers.ts
|
|
8736
|
+
function shouldRedactKey(key) {
|
|
8737
|
+
const k = key.toLowerCase();
|
|
8738
|
+
for (const s of REDACT_SUBSTRINGS) {
|
|
8739
|
+
if (k.includes(s)) return true;
|
|
8740
|
+
}
|
|
8741
|
+
return false;
|
|
8742
|
+
}
|
|
8743
|
+
function safeString(value, maxLength) {
|
|
8744
|
+
if (value === null || value === void 0) return "";
|
|
8745
|
+
let s;
|
|
8746
|
+
if (typeof value === "string") s = value;
|
|
8747
|
+
else if (typeof value === "number" || typeof value === "boolean") s = String(value);
|
|
8748
|
+
else s = stableJson(value, false);
|
|
8749
|
+
if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
|
|
8750
|
+
return `${s.slice(0, maxLength)}\u2026`;
|
|
8751
|
+
}
|
|
8752
|
+
return s;
|
|
8753
|
+
}
|
|
8754
|
+
function escapeMarkdown(value) {
|
|
8755
|
+
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
8756
|
+
}
|
|
8757
|
+
function escapeHtml(value) {
|
|
8758
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
8759
|
+
}
|
|
8760
|
+
function sortKeysDeep(input3) {
|
|
8761
|
+
if (input3 === null || typeof input3 !== "object") return input3;
|
|
8762
|
+
if (Array.isArray(input3)) return input3.map(sortKeysDeep);
|
|
8763
|
+
const o = input3;
|
|
8764
|
+
const out = {};
|
|
8765
|
+
for (const k of Object.keys(o).sort()) {
|
|
8766
|
+
out[k] = sortKeysDeep(o[k]);
|
|
8767
|
+
}
|
|
8768
|
+
return out;
|
|
8769
|
+
}
|
|
8770
|
+
function stableJson(value, pretty) {
|
|
8771
|
+
const sorted = sortKeysDeep(value);
|
|
8772
|
+
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
8773
|
+
}
|
|
8774
|
+
function compactAttributes3(attrs, options) {
|
|
8775
|
+
if (attrs === void 0) return {};
|
|
8776
|
+
const maxLen = options?.maxLength ?? 500;
|
|
8777
|
+
const redacted = options?.redacted ?? true;
|
|
8778
|
+
const out = {};
|
|
8779
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
8780
|
+
if (redacted && shouldRedactKey(key)) {
|
|
8781
|
+
out[key] = "[REDACTED]";
|
|
8782
|
+
continue;
|
|
8783
|
+
}
|
|
8784
|
+
const v = attrs[key];
|
|
8785
|
+
out[key] = compactValue(v, maxLen, redacted);
|
|
8786
|
+
}
|
|
8787
|
+
return out;
|
|
8788
|
+
}
|
|
8789
|
+
function compactValue(value, maxLen, redacted) {
|
|
8790
|
+
if (value === null || typeof value !== "object") {
|
|
8791
|
+
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
8792
|
+
}
|
|
8793
|
+
if (Array.isArray(value)) {
|
|
8794
|
+
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
|
|
8795
|
+
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
8796
|
+
return arr;
|
|
8797
|
+
}
|
|
8798
|
+
const o = value;
|
|
8799
|
+
const inner = {};
|
|
8800
|
+
for (const k of Object.keys(o)) {
|
|
8801
|
+
if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
8802
|
+
else inner[k] = compactValue(o[k], maxLen, redacted);
|
|
8803
|
+
}
|
|
8804
|
+
return inner;
|
|
8805
|
+
}
|
|
8806
|
+
function flattenTree(tree) {
|
|
8807
|
+
const out = [];
|
|
8808
|
+
function walk(nodes) {
|
|
8809
|
+
for (const n of nodes) {
|
|
8810
|
+
out.push(n);
|
|
8811
|
+
if (n.children.length > 0) walk(n.children);
|
|
8812
|
+
}
|
|
8813
|
+
}
|
|
8814
|
+
walk(tree.children);
|
|
8815
|
+
return out;
|
|
8816
|
+
}
|
|
8817
|
+
function zeroKinds() {
|
|
8818
|
+
return {
|
|
8819
|
+
RUN: 0,
|
|
8820
|
+
AGENT: 0,
|
|
8821
|
+
LLM: 0,
|
|
8822
|
+
TOOL: 0,
|
|
8823
|
+
CHAIN: 0,
|
|
8824
|
+
RETRIEVER: 0,
|
|
8825
|
+
DECISION: 0,
|
|
8826
|
+
RESULT: 0,
|
|
8827
|
+
ERROR: 0,
|
|
8828
|
+
LOGIC: 0,
|
|
8829
|
+
LOG: 0,
|
|
8830
|
+
OUTCOME: 0
|
|
8831
|
+
};
|
|
8832
|
+
}
|
|
8833
|
+
var REDACT_SUBSTRINGS;
|
|
8834
|
+
var init_helpers = __esm({
|
|
8835
|
+
"packages/core/src/exporters/helpers.ts"() {
|
|
8836
|
+
REDACT_SUBSTRINGS = [
|
|
8837
|
+
"authorization",
|
|
8838
|
+
"cookie",
|
|
8839
|
+
"token",
|
|
8840
|
+
"apikey",
|
|
8841
|
+
"password",
|
|
8842
|
+
"secret",
|
|
8843
|
+
"email"
|
|
8844
|
+
];
|
|
8845
|
+
}
|
|
8846
|
+
});
|
|
8847
|
+
|
|
8848
|
+
// packages/core/src/cohort/render.ts
|
|
8849
|
+
function formatRate(value) {
|
|
8850
|
+
if (value === void 0) return "n/a";
|
|
8851
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
8852
|
+
}
|
|
8853
|
+
function renderCohortSummaryMarkdown(result) {
|
|
8854
|
+
const lines = [];
|
|
8855
|
+
lines.push("# Cohort analysis");
|
|
8856
|
+
lines.push("");
|
|
8857
|
+
lines.push(`Trace directory: \`${result.traceDir}\``);
|
|
8858
|
+
lines.push(`Group by: \`${result.groupBy}\``);
|
|
8859
|
+
if (result.baseline !== void 0 && result.candidate !== void 0) {
|
|
8860
|
+
lines.push(
|
|
8861
|
+
`Baseline/Candidate key: \`${result.cohortKey}\` (${result.baseline} vs ${result.candidate})`
|
|
8862
|
+
);
|
|
8863
|
+
}
|
|
8864
|
+
lines.push(`Status: **${result.ok ? "PASS" : "REGRESSION"}**`);
|
|
8865
|
+
lines.push("");
|
|
8866
|
+
if (result.warnings.length > 0) {
|
|
8867
|
+
lines.push("## Warnings");
|
|
8868
|
+
for (const warning of result.warnings) lines.push(`- ${warning}`);
|
|
8869
|
+
lines.push("");
|
|
8870
|
+
}
|
|
8871
|
+
lines.push("## Groups");
|
|
8872
|
+
for (const group of result.groups) {
|
|
8873
|
+
lines.push(
|
|
8874
|
+
`### ${group.cohortLabel ?? "all"} / ${group.groupKey} (${group.runCount} runs)`
|
|
8875
|
+
);
|
|
8876
|
+
lines.push(`- Error rate: ${formatRate(group.errorRate)}`);
|
|
8877
|
+
if (group.avgDurationMs !== void 0) {
|
|
8878
|
+
lines.push(`- Avg duration: ${Math.round(group.avgDurationMs)} ms`);
|
|
8879
|
+
}
|
|
8880
|
+
if (group.dominantToolChoice !== void 0) {
|
|
8881
|
+
lines.push(`- Dominant tools: ${group.dominantToolChoice}`);
|
|
8882
|
+
}
|
|
8883
|
+
lines.push(
|
|
8884
|
+
`- Observation failure rate: ${formatRate(group.observationFailureRate)}`
|
|
8885
|
+
);
|
|
8886
|
+
lines.push("");
|
|
8887
|
+
}
|
|
8888
|
+
if (result.comparisons.length > 0) {
|
|
8889
|
+
lines.push("## Comparisons");
|
|
8890
|
+
for (const comparison of result.comparisons) {
|
|
8891
|
+
const flag = comparison.regression ? " **REGRESSION**" : "";
|
|
8892
|
+
lines.push(`- ${comparison.message}${flag}`);
|
|
8893
|
+
}
|
|
8894
|
+
lines.push("");
|
|
8895
|
+
}
|
|
8896
|
+
return lines.join("\n").trimEnd();
|
|
8897
|
+
}
|
|
8898
|
+
function renderCohortReportHtml(result) {
|
|
8899
|
+
const rows = result.groups.map(
|
|
8900
|
+
(group) => `<tr><td>${escapeHtml(group.cohortLabel ?? "all")}</td><td>${escapeHtml(group.groupKey)}</td><td>${group.runCount}</td><td>${escapeHtml(formatRate(group.errorRate))}</td><td>${group.avgDurationMs !== void 0 ? Math.round(group.avgDurationMs) : "n/a"}</td></tr>`
|
|
8901
|
+
).join("");
|
|
8902
|
+
const comparisons = result.comparisons.map(
|
|
8903
|
+
(item) => `<li>${escapeHtml(item.message)}${item.regression ? " <strong>REGRESSION</strong>" : ""}</li>`
|
|
8904
|
+
).join("");
|
|
8905
|
+
return `<!DOCTYPE html>
|
|
8906
|
+
<html lang="en">
|
|
8907
|
+
<head>
|
|
8908
|
+
<meta charset="utf-8" />
|
|
8909
|
+
<title>Cohort report</title>
|
|
8910
|
+
<style>
|
|
8911
|
+
body { font-family: system-ui, sans-serif; margin: 2rem; }
|
|
8912
|
+
table { border-collapse: collapse; width: 100%; }
|
|
8913
|
+
th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
|
|
8914
|
+
th { background: #f6f6f6; }
|
|
8915
|
+
</style>
|
|
8916
|
+
</head>
|
|
8917
|
+
<body>
|
|
8918
|
+
<h1>Cohort analysis</h1>
|
|
8919
|
+
<p>Status: <strong>${result.ok ? "PASS" : "REGRESSION"}</strong></p>
|
|
8920
|
+
<p>Trace directory: <code>${escapeHtml(result.traceDir)}</code></p>
|
|
8921
|
+
<h2>Groups</h2>
|
|
8922
|
+
<table>
|
|
8923
|
+
<thead><tr><th>Cohort</th><th>Group</th><th>Runs</th><th>Error rate</th><th>Avg duration (ms)</th></tr></thead>
|
|
8924
|
+
<tbody>${rows}</tbody>
|
|
8925
|
+
</table>
|
|
8926
|
+
${result.comparisons.length > 0 ? `<h2>Comparisons</h2><ul>${comparisons}</ul>` : ""}
|
|
8927
|
+
</body>
|
|
8928
|
+
</html>`;
|
|
8929
|
+
}
|
|
8930
|
+
function renderCohortReport(result, options = {}) {
|
|
8931
|
+
const format = options.format ?? "markdown";
|
|
8932
|
+
if (format === "json") return JSON.stringify(result, null, 2);
|
|
8933
|
+
if (format === "html") return renderCohortReportHtml(result);
|
|
8934
|
+
return renderCohortSummaryMarkdown(result);
|
|
8935
|
+
}
|
|
8936
|
+
var init_render2 = __esm({
|
|
8937
|
+
"packages/core/src/cohort/render.ts"() {
|
|
8938
|
+
init_helpers();
|
|
8939
|
+
}
|
|
8940
|
+
});
|
|
8941
|
+
|
|
8942
|
+
// packages/core/src/cohort/index.ts
|
|
8943
|
+
var init_cohort2 = __esm({
|
|
8944
|
+
"packages/core/src/cohort/index.ts"() {
|
|
8945
|
+
init_types5();
|
|
8946
|
+
init_analyze();
|
|
8947
|
+
init_compare();
|
|
8948
|
+
init_grouping();
|
|
8949
|
+
init_metrics();
|
|
8950
|
+
init_render2();
|
|
8951
|
+
}
|
|
8952
|
+
});
|
|
8953
|
+
|
|
8954
|
+
// packages/core/src/gate/parse.ts
|
|
8955
|
+
function parseGateList(value) {
|
|
8956
|
+
if (value === void 0 || value.trim() === "") return [];
|
|
8957
|
+
return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
8958
|
+
}
|
|
8959
|
+
var init_parse = __esm({
|
|
8960
|
+
"packages/core/src/gate/parse.ts"() {
|
|
8961
|
+
}
|
|
8962
|
+
});
|
|
8963
|
+
|
|
8964
|
+
// packages/core/src/gate/evaluate.ts
|
|
8965
|
+
function percentile3(values, p) {
|
|
8966
|
+
if (values.length === 0) return void 0;
|
|
8967
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
8968
|
+
const idx = Math.min(
|
|
8969
|
+
sorted.length - 1,
|
|
8970
|
+
Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
|
|
8971
|
+
);
|
|
8972
|
+
return sorted[idx];
|
|
8973
|
+
}
|
|
8974
|
+
function hasThresholds(options) {
|
|
8975
|
+
return options.maxErrorRate !== void 0 || options.maxP95DurationMs !== void 0 || (options.forbidTools?.length ?? 0) > 0 || (options.requireObservations?.length ?? 0) > 0;
|
|
8976
|
+
}
|
|
8977
|
+
function gateHasThresholds(options) {
|
|
8978
|
+
return hasThresholds(options);
|
|
8979
|
+
}
|
|
8980
|
+
async function loadRunMetrics(runs) {
|
|
8981
|
+
const metrics = [];
|
|
8982
|
+
for (const run of runs) {
|
|
8983
|
+
if (run.filePath === void 0) continue;
|
|
8984
|
+
metrics.push(
|
|
8985
|
+
await computeCohortRunMetrics({
|
|
8986
|
+
runId: run.runId,
|
|
8987
|
+
filePath: run.filePath,
|
|
8988
|
+
metadata: run.metadata,
|
|
8989
|
+
status: run.status,
|
|
8990
|
+
durationMs: run.durationMs,
|
|
8991
|
+
groupKey: "all"
|
|
8992
|
+
})
|
|
8993
|
+
);
|
|
8994
|
+
}
|
|
8995
|
+
return metrics;
|
|
8996
|
+
}
|
|
8997
|
+
async function observationStatus(filePath, name) {
|
|
8998
|
+
const events = await readTraceEventsFromFile(filePath);
|
|
8999
|
+
const outcomes = extractOutcomesFromTraceEvents(events);
|
|
9000
|
+
const match = outcomes.find((item) => item.name === name);
|
|
9001
|
+
if (!match) return "missing";
|
|
9002
|
+
return match.status === "passed" ? "passed" : "failed";
|
|
9003
|
+
}
|
|
9004
|
+
async function evaluateGateThresholds(runs, options) {
|
|
9005
|
+
const checks2 = [];
|
|
9006
|
+
const readErrors = [];
|
|
9007
|
+
if (!hasThresholds(options)) {
|
|
9008
|
+
return { checks: checks2, readErrors };
|
|
9009
|
+
}
|
|
9010
|
+
if (runs.length === 0) {
|
|
9011
|
+
readErrors.push("No trace runs found in the gate directory.");
|
|
9012
|
+
return { checks: checks2, readErrors };
|
|
9013
|
+
}
|
|
9014
|
+
let runMetrics;
|
|
9015
|
+
try {
|
|
9016
|
+
runMetrics = await loadRunMetrics(runs);
|
|
9017
|
+
} catch (error) {
|
|
9018
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9019
|
+
readErrors.push(message);
|
|
9020
|
+
return { checks: checks2, readErrors };
|
|
9021
|
+
}
|
|
9022
|
+
if (options.maxErrorRate !== void 0) {
|
|
9023
|
+
const errors = runMetrics.filter((run) => run.error).length;
|
|
9024
|
+
const actual = runMetrics.length > 0 ? errors / runMetrics.length * 100 : 0;
|
|
9025
|
+
const ok = actual <= options.maxErrorRate;
|
|
9026
|
+
checks2.push({
|
|
9027
|
+
id: "maxErrorRate",
|
|
9028
|
+
name: "Max error rate",
|
|
9029
|
+
ok,
|
|
9030
|
+
expected: options.maxErrorRate,
|
|
9031
|
+
actual: Math.round(actual * 10) / 10,
|
|
9032
|
+
message: ok ? `Error rate ${actual.toFixed(1)}% within limit ${options.maxErrorRate}%` : `Error rate ${actual.toFixed(1)}% exceeds limit ${options.maxErrorRate}%`
|
|
9033
|
+
});
|
|
9034
|
+
}
|
|
9035
|
+
if (options.maxP95DurationMs !== void 0) {
|
|
9036
|
+
const durations = runMetrics.map((run) => run.durationMs).filter((value) => typeof value === "number");
|
|
9037
|
+
const actual = percentile3(durations, 95);
|
|
9038
|
+
const ok = actual !== void 0 && actual <= options.maxP95DurationMs;
|
|
9039
|
+
checks2.push({
|
|
9040
|
+
id: "maxP95Duration",
|
|
9041
|
+
name: "Max p95 duration (ms)",
|
|
9042
|
+
ok,
|
|
9043
|
+
expected: options.maxP95DurationMs,
|
|
9044
|
+
actual: actual ?? "n/a",
|
|
9045
|
+
message: actual === void 0 ? "No duration samples available for p95 check." : ok ? `P95 duration ${Math.round(actual)} ms within limit ${options.maxP95DurationMs} ms` : `P95 duration ${Math.round(actual)} ms exceeds limit ${options.maxP95DurationMs} ms`
|
|
9046
|
+
});
|
|
9047
|
+
}
|
|
9048
|
+
for (const tool of options.forbidTools ?? []) {
|
|
9049
|
+
let violated = false;
|
|
9050
|
+
for (const run of runMetrics) {
|
|
9051
|
+
const used = run.toolChoices.includes(tool) || run.toolOrdering.includes(tool);
|
|
9052
|
+
if (used) {
|
|
9053
|
+
violated = true;
|
|
9054
|
+
checks2.push({
|
|
9055
|
+
id: "forbidTool",
|
|
9056
|
+
name: `Forbid tool: ${tool}`,
|
|
9057
|
+
ok: false,
|
|
9058
|
+
expected: `not used`,
|
|
9059
|
+
actual: "used",
|
|
9060
|
+
runId: run.runId,
|
|
9061
|
+
message: `Forbidden tool "${tool}" used in run ${run.runId}`
|
|
9062
|
+
});
|
|
9063
|
+
}
|
|
9064
|
+
}
|
|
9065
|
+
if (!violated) {
|
|
9066
|
+
checks2.push({
|
|
9067
|
+
id: "forbidTool",
|
|
9068
|
+
name: `Forbid tool: ${tool}`,
|
|
9069
|
+
ok: true,
|
|
9070
|
+
message: `Forbidden tool "${tool}" not used`
|
|
9071
|
+
});
|
|
9072
|
+
}
|
|
9073
|
+
}
|
|
9074
|
+
for (const observation of options.requireObservations ?? []) {
|
|
9075
|
+
for (const run of runs) {
|
|
9076
|
+
if (run.filePath === void 0) continue;
|
|
9077
|
+
try {
|
|
9078
|
+
const status = await observationStatus(run.filePath, observation);
|
|
9079
|
+
const ok = status === "passed";
|
|
9080
|
+
checks2.push({
|
|
9081
|
+
id: "requireObservation",
|
|
9082
|
+
name: `Require observation: ${observation}`,
|
|
9083
|
+
ok,
|
|
9084
|
+
expected: "passed",
|
|
9085
|
+
actual: status,
|
|
9086
|
+
runId: run.runId,
|
|
9087
|
+
message: ok ? `Observation "${observation}" passed in run ${run.runId}` : status === "missing" ? `Observation "${observation}" missing in run ${run.runId}` : `Observation "${observation}" failed in run ${run.runId}`
|
|
9088
|
+
});
|
|
9089
|
+
} catch (error) {
|
|
9090
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9091
|
+
readErrors.push(`Run ${run.runId}: ${message}`);
|
|
9092
|
+
}
|
|
9093
|
+
}
|
|
9094
|
+
}
|
|
9095
|
+
return { checks: checks2, readErrors };
|
|
9096
|
+
}
|
|
9097
|
+
function checksFromSuiteResult(suiteResult) {
|
|
9098
|
+
const checks2 = [
|
|
9099
|
+
{
|
|
9100
|
+
id: "suite",
|
|
9101
|
+
name: `Suite: ${suiteResult.suiteName}`,
|
|
9102
|
+
ok: suiteResult.ok,
|
|
9103
|
+
message: suiteResult.ok ? `Suite passed (${suiteResult.summary.passed} cases)` : `Suite failed (${suiteResult.summary.failed} failed, ${suiteResult.summary.errors} errors)`
|
|
9104
|
+
}
|
|
9105
|
+
];
|
|
9106
|
+
for (const suiteCase of suiteResult.cases) {
|
|
9107
|
+
if (suiteCase.status === "pass") continue;
|
|
9108
|
+
checks2.push({
|
|
9109
|
+
id: "suite",
|
|
9110
|
+
name: `Case: ${suiteCase.id}`,
|
|
9111
|
+
ok: false,
|
|
9112
|
+
message: suiteCase.message ?? `Case status: ${suiteCase.status}`
|
|
9113
|
+
});
|
|
9114
|
+
}
|
|
9115
|
+
return checks2;
|
|
9116
|
+
}
|
|
9117
|
+
var init_evaluate = __esm({
|
|
9118
|
+
"packages/core/src/gate/evaluate.ts"() {
|
|
9119
|
+
init_extract();
|
|
9120
|
+
init_storage();
|
|
9121
|
+
init_metrics();
|
|
9122
|
+
}
|
|
9123
|
+
});
|
|
9124
|
+
function resolveExitCode(input3) {
|
|
9125
|
+
if (input3.configError) return 2;
|
|
9126
|
+
if (input3.readError) return 3;
|
|
9127
|
+
if (!input3.ok) return 1;
|
|
9128
|
+
return 0;
|
|
9129
|
+
}
|
|
9130
|
+
function validateOptions(options) {
|
|
9131
|
+
const errors = [];
|
|
9132
|
+
const hasSuite = options.suitePath !== void 0 && options.suitePath.trim() !== "";
|
|
9133
|
+
const hasThresholds2 = gateHasThresholds(options);
|
|
9134
|
+
if (!hasSuite && !hasThresholds2) {
|
|
9135
|
+
errors.push(
|
|
9136
|
+
"No gate rules specified. Pass --suite or at least one threshold flag."
|
|
9137
|
+
);
|
|
9138
|
+
}
|
|
9139
|
+
if (hasThresholds2 && (options.traceDir === void 0 || options.traceDir.trim() === "")) {
|
|
9140
|
+
if (!hasSuite) {
|
|
9141
|
+
errors.push("Threshold flags require --dir <trace-directory>.");
|
|
9142
|
+
}
|
|
9143
|
+
}
|
|
9144
|
+
if (options.maxErrorRate !== void 0 && options.maxErrorRate < 0) {
|
|
9145
|
+
errors.push("--max-error-rate must be a non-negative percentage.");
|
|
9146
|
+
}
|
|
9147
|
+
if (options.maxP95DurationMs !== void 0 && options.maxP95DurationMs < 0) {
|
|
9148
|
+
errors.push("--max-p95-duration must be a non-negative millisecond value.");
|
|
9149
|
+
}
|
|
9150
|
+
return errors;
|
|
9151
|
+
}
|
|
9152
|
+
function isConfigLoadError(error) {
|
|
9153
|
+
if (!(error instanceof Error)) return false;
|
|
9154
|
+
const ext = path24__default.default.extname(error.message);
|
|
9155
|
+
if (error.message.includes("Unsupported suite config extension")) return true;
|
|
9156
|
+
if (error.message.includes("TypeScript suite configs require")) return true;
|
|
9157
|
+
if (error.message.includes("No suite config found")) return true;
|
|
9158
|
+
if (error.message.includes("AI_SUITE_CONFIG")) return true;
|
|
9159
|
+
if (ext === ".ts" || ext === ".mts" || ext === ".cts") return true;
|
|
9160
|
+
return "diagnostics" in error;
|
|
9161
|
+
}
|
|
9162
|
+
async function runGate(runs, options) {
|
|
9163
|
+
const diagnostics = [];
|
|
9164
|
+
const checks2 = [];
|
|
9165
|
+
const validationErrors = validateOptions(options);
|
|
9166
|
+
if (validationErrors.length > 0) {
|
|
9167
|
+
return {
|
|
9168
|
+
ok: false,
|
|
9169
|
+
exitCode: 2,
|
|
9170
|
+
runCount: 0,
|
|
9171
|
+
checks: checks2,
|
|
9172
|
+
diagnostics: validationErrors
|
|
9173
|
+
};
|
|
9174
|
+
}
|
|
9175
|
+
let traceDir = options.traceDir?.trim();
|
|
9176
|
+
let suiteResult;
|
|
9177
|
+
if (options.suitePath !== void 0 && options.suitePath.trim() !== "") {
|
|
9178
|
+
try {
|
|
9179
|
+
suiteResult = await runSuite({
|
|
9180
|
+
configPath: options.suitePath,
|
|
9181
|
+
cwd: options.cwd
|
|
9182
|
+
});
|
|
9183
|
+
traceDir = traceDir ?? suiteResult.tracesDir;
|
|
9184
|
+
checks2.push(...checksFromSuiteResult(suiteResult));
|
|
9185
|
+
} catch (error) {
|
|
9186
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9187
|
+
diagnostics.push(message);
|
|
9188
|
+
return {
|
|
9189
|
+
ok: false,
|
|
9190
|
+
exitCode: isConfigLoadError(error) ? 2 : 3,
|
|
9191
|
+
traceDir,
|
|
9192
|
+
suitePath: options.suitePath,
|
|
9193
|
+
runCount: 0,
|
|
9194
|
+
checks: checks2,
|
|
9195
|
+
diagnostics
|
|
9196
|
+
};
|
|
9197
|
+
}
|
|
9198
|
+
}
|
|
9199
|
+
if (gateHasThresholds(options)) {
|
|
9200
|
+
const thresholdDir = traceDir;
|
|
9201
|
+
if (thresholdDir === void 0 || thresholdDir.trim() === "") {
|
|
9202
|
+
return {
|
|
9203
|
+
ok: false,
|
|
9204
|
+
exitCode: 2,
|
|
9205
|
+
traceDir,
|
|
9206
|
+
suitePath: options.suitePath,
|
|
9207
|
+
runCount: runs.length,
|
|
9208
|
+
checks: checks2,
|
|
9209
|
+
diagnostics: ["Threshold evaluation requires a trace directory."],
|
|
9210
|
+
...suiteResult !== void 0 ? { suiteResult } : {}
|
|
9211
|
+
};
|
|
9212
|
+
}
|
|
9213
|
+
const thresholdRuns = runs.length > 0 ? runs : [];
|
|
9214
|
+
const { checks: thresholdChecks, readErrors } = await evaluateGateThresholds(
|
|
9215
|
+
thresholdRuns,
|
|
9216
|
+
options
|
|
9217
|
+
);
|
|
9218
|
+
checks2.push(...thresholdChecks);
|
|
9219
|
+
diagnostics.push(...readErrors);
|
|
9220
|
+
if (readErrors.length > 0) {
|
|
9221
|
+
const ok2 = checks2.length > 0 && checks2.every((item) => item.ok);
|
|
9222
|
+
return {
|
|
9223
|
+
ok: ok2,
|
|
9224
|
+
exitCode: resolveExitCode({
|
|
9225
|
+
ok: ok2,
|
|
9226
|
+
configError: false,
|
|
9227
|
+
readError: true
|
|
9228
|
+
}),
|
|
9229
|
+
traceDir: thresholdDir,
|
|
9230
|
+
suitePath: options.suitePath,
|
|
9231
|
+
runCount: thresholdRuns.length,
|
|
9232
|
+
checks: checks2,
|
|
9233
|
+
diagnostics,
|
|
9234
|
+
...suiteResult !== void 0 ? { suiteResult } : {}
|
|
9235
|
+
};
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
const ok = checks2.length > 0 && checks2.every((item) => item.ok);
|
|
9239
|
+
return {
|
|
9240
|
+
ok,
|
|
9241
|
+
exitCode: resolveExitCode({ ok, configError: false, readError: false }),
|
|
9242
|
+
traceDir,
|
|
9243
|
+
suitePath: options.suitePath,
|
|
9244
|
+
runCount: runs.length,
|
|
9245
|
+
checks: checks2,
|
|
9246
|
+
diagnostics,
|
|
9247
|
+
...suiteResult !== void 0 ? { suiteResult } : {}
|
|
9248
|
+
};
|
|
9249
|
+
}
|
|
9250
|
+
var init_run2 = __esm({
|
|
9251
|
+
"packages/core/src/gate/run.ts"() {
|
|
9252
|
+
init_run();
|
|
9253
|
+
init_evaluate();
|
|
9254
|
+
}
|
|
9255
|
+
});
|
|
9256
|
+
|
|
9257
|
+
// packages/core/src/gate/render.ts
|
|
9258
|
+
function escapeXml(value) {
|
|
9259
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
9260
|
+
}
|
|
9261
|
+
function renderGateSummaryMarkdown(result) {
|
|
9262
|
+
const lines = [];
|
|
9263
|
+
lines.push("# AgentInspect gate");
|
|
9264
|
+
lines.push("");
|
|
9265
|
+
lines.push(`Status: **${result.ok ? "PASS" : "FAIL"}** (exit ${result.exitCode})`);
|
|
9266
|
+
if (result.traceDir !== void 0) {
|
|
9267
|
+
lines.push(`Trace directory: \`${result.traceDir}\``);
|
|
9268
|
+
}
|
|
9269
|
+
if (result.suitePath !== void 0) {
|
|
9270
|
+
lines.push(`Suite config: \`${result.suitePath}\``);
|
|
9271
|
+
}
|
|
9272
|
+
lines.push(`Runs evaluated: ${result.runCount}`);
|
|
9273
|
+
lines.push("");
|
|
9274
|
+
if (result.diagnostics.length > 0) {
|
|
9275
|
+
lines.push("## Diagnostics");
|
|
9276
|
+
for (const item of result.diagnostics) lines.push(`- ${item}`);
|
|
9277
|
+
lines.push("");
|
|
9278
|
+
}
|
|
9279
|
+
lines.push("## Checks");
|
|
9280
|
+
for (const check of result.checks) {
|
|
9281
|
+
const flag = check.ok ? "PASS" : "FAIL";
|
|
9282
|
+
lines.push(`- [${flag}] ${check.name}: ${check.message}`);
|
|
9283
|
+
}
|
|
9284
|
+
lines.push("");
|
|
9285
|
+
return lines.join("\n").trimEnd();
|
|
9286
|
+
}
|
|
9287
|
+
function renderGateGithubStepSummary(result) {
|
|
9288
|
+
const lines = [];
|
|
9289
|
+
lines.push(`## AgentInspect gate: ${result.ok ? "PASS" : "FAIL"}`);
|
|
9290
|
+
lines.push("");
|
|
9291
|
+
lines.push("| Check | Status | Details |");
|
|
9292
|
+
lines.push("| --- | --- | --- |");
|
|
9293
|
+
for (const check of result.checks) {
|
|
9294
|
+
lines.push(
|
|
9295
|
+
`| ${check.name} | ${check.ok ? "pass" : "fail"} | ${check.message.replace(/\|/g, "/")} |`
|
|
9296
|
+
);
|
|
9297
|
+
}
|
|
9298
|
+
if (result.diagnostics.length > 0) {
|
|
9299
|
+
lines.push("");
|
|
9300
|
+
lines.push("**Diagnostics**");
|
|
9301
|
+
for (const item of result.diagnostics) lines.push(`- ${item}`);
|
|
9302
|
+
}
|
|
9303
|
+
return lines.join("\n").trimEnd();
|
|
9304
|
+
}
|
|
9305
|
+
function renderGateReportHtml(result) {
|
|
9306
|
+
const rows = result.checks.map(
|
|
9307
|
+
(check) => `<tr><td>${escapeHtml(check.name)}</td><td>${check.ok ? "PASS" : "FAIL"}</td><td>${escapeHtml(check.message)}</td></tr>`
|
|
9308
|
+
).join("");
|
|
9309
|
+
return `<!DOCTYPE html>
|
|
9310
|
+
<html lang="en">
|
|
9311
|
+
<head>
|
|
9312
|
+
<meta charset="utf-8" />
|
|
9313
|
+
<title>Gate report</title>
|
|
9314
|
+
<style>
|
|
9315
|
+
body { font-family: system-ui, sans-serif; margin: 2rem; }
|
|
9316
|
+
table { border-collapse: collapse; width: 100%; }
|
|
9317
|
+
th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
|
|
9318
|
+
th { background: #f6f6f6; }
|
|
9319
|
+
</style>
|
|
9320
|
+
</head>
|
|
9321
|
+
<body>
|
|
9322
|
+
<h1>AgentInspect gate</h1>
|
|
9323
|
+
<p>Status: <strong>${result.ok ? "PASS" : "FAIL"}</strong> (exit ${result.exitCode})</p>
|
|
9324
|
+
<h2>Checks</h2>
|
|
9325
|
+
<table>
|
|
9326
|
+
<thead><tr><th>Check</th><th>Status</th><th>Details</th></tr></thead>
|
|
9327
|
+
<tbody>${rows}</tbody>
|
|
9328
|
+
</table>
|
|
9329
|
+
</body>
|
|
9330
|
+
</html>`;
|
|
9331
|
+
}
|
|
9332
|
+
function renderGateJUnit(result) {
|
|
9333
|
+
const failures = result.checks.filter((check) => !check.ok).length;
|
|
9334
|
+
const tests = result.checks.length;
|
|
9335
|
+
const cases = result.checks.map((check) => {
|
|
9336
|
+
if (check.ok) {
|
|
9337
|
+
return ` <testcase name="${escapeXml(check.name)}" classname="gate" />`;
|
|
9338
|
+
}
|
|
9339
|
+
return ` <testcase name="${escapeXml(check.name)}" classname="gate">
|
|
9340
|
+
<failure message="${escapeXml(check.message)}">${escapeXml(check.message)}</failure>
|
|
9341
|
+
</testcase>`;
|
|
9342
|
+
}).join("\n");
|
|
9343
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
9344
|
+
<testsuites tests="${tests}" failures="${failures}" errors="0" time="0">
|
|
9345
|
+
<testsuite name="agent-inspect-gate" tests="${tests}" failures="${failures}" errors="0" time="0">
|
|
9346
|
+
${cases}
|
|
9347
|
+
</testsuite>
|
|
9348
|
+
</testsuites>`;
|
|
9349
|
+
}
|
|
9350
|
+
function renderGateReport(result, options = {}) {
|
|
9351
|
+
const format = options.format ?? "markdown";
|
|
9352
|
+
if (format === "json") return JSON.stringify(result, null, 2);
|
|
9353
|
+
if (format === "html") return renderGateReportHtml(result);
|
|
9354
|
+
if (format === "junit") return renderGateJUnit(result);
|
|
9355
|
+
if (format === "github") return renderGateGithubStepSummary(result);
|
|
9356
|
+
return renderGateSummaryMarkdown(result);
|
|
9357
|
+
}
|
|
9358
|
+
var init_render3 = __esm({
|
|
9359
|
+
"packages/core/src/gate/render.ts"() {
|
|
9360
|
+
init_helpers();
|
|
9361
|
+
}
|
|
9362
|
+
});
|
|
9363
|
+
|
|
9364
|
+
// packages/core/src/gate/index.ts
|
|
9365
|
+
var init_gate = __esm({
|
|
9366
|
+
"packages/core/src/gate/index.ts"() {
|
|
9367
|
+
init_parse();
|
|
9368
|
+
init_evaluate();
|
|
9369
|
+
init_run2();
|
|
9370
|
+
init_render3();
|
|
9371
|
+
}
|
|
9372
|
+
});
|
|
9373
|
+
|
|
8278
9374
|
// packages/core/src/inspect-run.ts
|
|
8279
9375
|
var init_inspect_run = __esm({
|
|
8280
9376
|
"packages/core/src/inspect-run.ts"() {
|
|
@@ -8320,13 +9416,15 @@ var init_advanced = __esm({
|
|
|
8320
9416
|
init_duration();
|
|
8321
9417
|
init_bundle();
|
|
8322
9418
|
init_suite();
|
|
9419
|
+
init_cohort2();
|
|
9420
|
+
init_gate();
|
|
8323
9421
|
init_maybe_inspect_run();
|
|
8324
9422
|
}
|
|
8325
9423
|
});
|
|
8326
9424
|
|
|
8327
9425
|
// packages/index-sqlite/src/types.ts
|
|
8328
9426
|
var INDEX_SCHEMA_VERSION, INDEX_DB_FILENAME;
|
|
8329
|
-
var
|
|
9427
|
+
var init_types6 = __esm({
|
|
8330
9428
|
"packages/index-sqlite/src/types.ts"() {
|
|
8331
9429
|
INDEX_SCHEMA_VERSION = "1";
|
|
8332
9430
|
INDEX_DB_FILENAME = "trace-index.sqlite";
|
|
@@ -8384,19 +9482,19 @@ var require_file_uri_to_path = __commonJS({
|
|
|
8384
9482
|
var rest = decodeURI(uri.substring(7));
|
|
8385
9483
|
var firstSlash = rest.indexOf("/");
|
|
8386
9484
|
var host = rest.substring(0, firstSlash);
|
|
8387
|
-
var
|
|
9485
|
+
var path33 = rest.substring(firstSlash + 1);
|
|
8388
9486
|
if ("localhost" == host) host = "";
|
|
8389
9487
|
if (host) {
|
|
8390
9488
|
host = sep + sep + host;
|
|
8391
9489
|
}
|
|
8392
|
-
|
|
9490
|
+
path33 = path33.replace(/^(.+)\|/, "$1:");
|
|
8393
9491
|
if (sep == "\\") {
|
|
8394
|
-
|
|
9492
|
+
path33 = path33.replace(/\//g, "\\");
|
|
8395
9493
|
}
|
|
8396
|
-
if (/^.+\:/.test(
|
|
8397
|
-
|
|
9494
|
+
if (/^.+\:/.test(path33)) ; else {
|
|
9495
|
+
path33 = sep + path33;
|
|
8398
9496
|
}
|
|
8399
|
-
return host +
|
|
9497
|
+
return host + path33;
|
|
8400
9498
|
}
|
|
8401
9499
|
}
|
|
8402
9500
|
});
|
|
@@ -8405,18 +9503,18 @@ var require_file_uri_to_path = __commonJS({
|
|
|
8405
9503
|
var require_bindings = __commonJS({
|
|
8406
9504
|
"node_modules/.pnpm/bindings@1.5.0/node_modules/bindings/bindings.js"(exports$1, module) {
|
|
8407
9505
|
var fs = __require("fs");
|
|
8408
|
-
var
|
|
9506
|
+
var path33 = __require("path");
|
|
8409
9507
|
var fileURLToPath2 = require_file_uri_to_path();
|
|
8410
|
-
var join =
|
|
8411
|
-
var dirname =
|
|
8412
|
-
var exists2 = fs.accessSync && function(
|
|
9508
|
+
var join = path33.join;
|
|
9509
|
+
var dirname = path33.dirname;
|
|
9510
|
+
var exists2 = fs.accessSync && function(path34) {
|
|
8413
9511
|
try {
|
|
8414
|
-
fs.accessSync(
|
|
9512
|
+
fs.accessSync(path34);
|
|
8415
9513
|
} catch (e) {
|
|
8416
9514
|
return false;
|
|
8417
9515
|
}
|
|
8418
9516
|
return true;
|
|
8419
|
-
} || fs.existsSync ||
|
|
9517
|
+
} || fs.existsSync || path33.existsSync;
|
|
8420
9518
|
var defaults = {
|
|
8421
9519
|
arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
|
|
8422
9520
|
compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
|
|
@@ -8461,7 +9559,7 @@ var require_bindings = __commonJS({
|
|
|
8461
9559
|
if (!opts.module_root) {
|
|
8462
9560
|
opts.module_root = exports$1.getRoot(exports$1.getFileName());
|
|
8463
9561
|
}
|
|
8464
|
-
if (
|
|
9562
|
+
if (path33.extname(opts.bindings) != ".node") {
|
|
8465
9563
|
opts.bindings += ".node";
|
|
8466
9564
|
}
|
|
8467
9565
|
var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
@@ -8696,7 +9794,7 @@ var require_pragma = __commonJS({
|
|
|
8696
9794
|
var require_backup = __commonJS({
|
|
8697
9795
|
"node_modules/.pnpm/better-sqlite3@11.10.0/node_modules/better-sqlite3/lib/methods/backup.js"(exports$1, module) {
|
|
8698
9796
|
var fs = __require("fs");
|
|
8699
|
-
var
|
|
9797
|
+
var path33 = __require("path");
|
|
8700
9798
|
var { promisify } = __require("util");
|
|
8701
9799
|
var { cppdb } = require_util();
|
|
8702
9800
|
var fsAccess = promisify(fs.access);
|
|
@@ -8712,7 +9810,7 @@ var require_backup = __commonJS({
|
|
|
8712
9810
|
if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
|
|
8713
9811
|
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
8714
9812
|
if (handler != null && typeof handler !== "function") throw new TypeError('Expected the "progress" option to be a function');
|
|
8715
|
-
await fsAccess(
|
|
9813
|
+
await fsAccess(path33.dirname(filename)).catch(() => {
|
|
8716
9814
|
throw new TypeError("Cannot save backup because the directory does not exist");
|
|
8717
9815
|
});
|
|
8718
9816
|
const isNewFile = await fsAccess(filename).then(() => false, () => true);
|
|
@@ -9012,7 +10110,7 @@ var require_inspect = __commonJS({
|
|
|
9012
10110
|
var require_database = __commonJS({
|
|
9013
10111
|
"node_modules/.pnpm/better-sqlite3@11.10.0/node_modules/better-sqlite3/lib/database.js"(exports$1, module) {
|
|
9014
10112
|
var fs = __require("fs");
|
|
9015
|
-
var
|
|
10113
|
+
var path33 = __require("path");
|
|
9016
10114
|
var util = require_util();
|
|
9017
10115
|
var SqliteError = require_sqlite_error();
|
|
9018
10116
|
var DEFAULT_ADDON;
|
|
@@ -9048,7 +10146,7 @@ var require_database = __commonJS({
|
|
|
9048
10146
|
addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
|
|
9049
10147
|
} else if (typeof nativeBinding === "string") {
|
|
9050
10148
|
const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
9051
|
-
addon = requireFunc(
|
|
10149
|
+
addon = requireFunc(path33.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
|
|
9052
10150
|
} else {
|
|
9053
10151
|
addon = nativeBinding;
|
|
9054
10152
|
}
|
|
@@ -9056,7 +10154,7 @@ var require_database = __commonJS({
|
|
|
9056
10154
|
addon.setErrorConstructor(SqliteError);
|
|
9057
10155
|
addon.isInitialized = true;
|
|
9058
10156
|
}
|
|
9059
|
-
if (!anonymous && !fs.existsSync(
|
|
10157
|
+
if (!anonymous && !fs.existsSync(path33.dirname(filename))) {
|
|
9060
10158
|
throw new TypeError("Cannot open database because the directory does not exist");
|
|
9061
10159
|
}
|
|
9062
10160
|
Object.defineProperties(this, {
|
|
@@ -9104,7 +10202,7 @@ function metaDefaults(sourceDir, fileCount) {
|
|
|
9104
10202
|
var INDEX_SCHEMA_SQL, DERIVE_SESSIONS_SQL, META_KEYS;
|
|
9105
10203
|
var init_schema = __esm({
|
|
9106
10204
|
"packages/index-sqlite/src/schema.ts"() {
|
|
9107
|
-
|
|
10205
|
+
init_types6();
|
|
9108
10206
|
INDEX_SCHEMA_SQL = `
|
|
9109
10207
|
DROP TABLE IF EXISTS meta;
|
|
9110
10208
|
DROP TABLE IF EXISTS errors;
|
|
@@ -9183,8 +10281,8 @@ GROUP BY session_id;
|
|
|
9183
10281
|
}
|
|
9184
10282
|
});
|
|
9185
10283
|
function resolveIndexDbPath(traceDir, dbPath) {
|
|
9186
|
-
if (dbPath && dbPath.trim() !== "") return
|
|
9187
|
-
return
|
|
10284
|
+
if (dbPath && dbPath.trim() !== "") return path24__default.default.resolve(dbPath);
|
|
10285
|
+
return path24__default.default.join(path24__default.default.resolve(traceDir), INDEX_DB_FILENAME);
|
|
9188
10286
|
}
|
|
9189
10287
|
function str(value) {
|
|
9190
10288
|
return typeof value === "string" && value !== "" ? value : null;
|
|
@@ -9276,7 +10374,7 @@ async function buildIndex(options = {}) {
|
|
|
9276
10374
|
warnings.push(`index.unreadable: ${file}`);
|
|
9277
10375
|
}
|
|
9278
10376
|
}
|
|
9279
|
-
await promises.mkdir(
|
|
10377
|
+
await promises.mkdir(path24__default.default.dirname(dbPath), { recursive: true });
|
|
9280
10378
|
await promises.rm(dbPath, { force: true });
|
|
9281
10379
|
const db = new import_better_sqlite3.default(dbPath);
|
|
9282
10380
|
let runCount = 0;
|
|
@@ -9357,7 +10455,7 @@ var init_builder = __esm({
|
|
|
9357
10455
|
import_better_sqlite3 = __toESM(require_lib());
|
|
9358
10456
|
init_advanced();
|
|
9359
10457
|
init_schema();
|
|
9360
|
-
|
|
10458
|
+
init_types6();
|
|
9361
10459
|
DEFAULT_MAX_RUNS = 1e4;
|
|
9362
10460
|
rebuildIndex = buildIndex;
|
|
9363
10461
|
}
|
|
@@ -9507,14 +10605,14 @@ __export(src_exports, {
|
|
|
9507
10605
|
});
|
|
9508
10606
|
var init_src = __esm({
|
|
9509
10607
|
"packages/index-sqlite/src/index.ts"() {
|
|
9510
|
-
|
|
10608
|
+
init_types6();
|
|
9511
10609
|
init_builder();
|
|
9512
10610
|
init_query();
|
|
9513
10611
|
}
|
|
9514
10612
|
});
|
|
9515
10613
|
|
|
9516
10614
|
// package.json
|
|
9517
|
-
var version = "5.
|
|
10615
|
+
var version = "5.2.0";
|
|
9518
10616
|
|
|
9519
10617
|
// packages/cli/src/list.ts
|
|
9520
10618
|
init_advanced();
|
|
@@ -10459,7 +11557,7 @@ init_nanoid();
|
|
|
10459
11557
|
function isFiniteNumber2(v) {
|
|
10460
11558
|
return typeof v === "number" && Number.isFinite(v);
|
|
10461
11559
|
}
|
|
10462
|
-
function
|
|
11560
|
+
function safeString2(v) {
|
|
10463
11561
|
if (typeof v !== "string") return void 0;
|
|
10464
11562
|
const t = v.trim();
|
|
10465
11563
|
return t === "" ? void 0 : t;
|
|
@@ -10509,7 +11607,7 @@ var EventNormalizer = class {
|
|
|
10509
11607
|
let runId;
|
|
10510
11608
|
for (const k of cfg.runIdKeys) {
|
|
10511
11609
|
const v = raw[k];
|
|
10512
|
-
const s =
|
|
11610
|
+
const s = safeString2(v);
|
|
10513
11611
|
if (s) {
|
|
10514
11612
|
runId = s;
|
|
10515
11613
|
break;
|
|
@@ -10525,7 +11623,7 @@ var EventNormalizer = class {
|
|
|
10525
11623
|
}
|
|
10526
11624
|
};
|
|
10527
11625
|
}
|
|
10528
|
-
const eventName =
|
|
11626
|
+
const eventName = safeString2(raw[cfg.eventKey]);
|
|
10529
11627
|
if (!eventName) {
|
|
10530
11628
|
return {
|
|
10531
11629
|
warning: {
|
|
@@ -10543,7 +11641,7 @@ var EventNormalizer = class {
|
|
|
10543
11641
|
const timestamp = parsedTs ?? Date.now();
|
|
10544
11642
|
const timestampMissing = parsedTs === void 0;
|
|
10545
11643
|
const parentIdKey = cfg.parentIdKey;
|
|
10546
|
-
const parentId = parentIdKey ?
|
|
11644
|
+
const parentId = parentIdKey ? safeString2(raw[parentIdKey]) : void 0;
|
|
10547
11645
|
let durationMs2;
|
|
10548
11646
|
const durationKey = cfg.durationKey;
|
|
10549
11647
|
if (durationKey) {
|
|
@@ -10554,7 +11652,7 @@ var EventNormalizer = class {
|
|
|
10554
11652
|
}
|
|
10555
11653
|
let status;
|
|
10556
11654
|
const statusKey = cfg.statusKey;
|
|
10557
|
-
const statusRaw = statusKey ?
|
|
11655
|
+
const statusRaw = statusKey ? safeString2(raw[statusKey]) : void 0;
|
|
10558
11656
|
if (statusRaw === "running" || statusRaw === "ok" || statusRaw === "error") {
|
|
10559
11657
|
status = statusRaw;
|
|
10560
11658
|
} else if (mapping?.status) {
|
|
@@ -11498,115 +12596,8 @@ function redactRunTreeForExport(tree, options) {
|
|
|
11498
12596
|
return clone;
|
|
11499
12597
|
}
|
|
11500
12598
|
|
|
11501
|
-
// packages/core/src/exporters/helpers.ts
|
|
11502
|
-
var REDACT_SUBSTRINGS = [
|
|
11503
|
-
"authorization",
|
|
11504
|
-
"cookie",
|
|
11505
|
-
"token",
|
|
11506
|
-
"apikey",
|
|
11507
|
-
"password",
|
|
11508
|
-
"secret",
|
|
11509
|
-
"email"
|
|
11510
|
-
];
|
|
11511
|
-
function shouldRedactKey(key) {
|
|
11512
|
-
const k = key.toLowerCase();
|
|
11513
|
-
for (const s of REDACT_SUBSTRINGS) {
|
|
11514
|
-
if (k.includes(s)) return true;
|
|
11515
|
-
}
|
|
11516
|
-
return false;
|
|
11517
|
-
}
|
|
11518
|
-
function safeString2(value, maxLength) {
|
|
11519
|
-
if (value === null || value === void 0) return "";
|
|
11520
|
-
let s;
|
|
11521
|
-
if (typeof value === "string") s = value;
|
|
11522
|
-
else if (typeof value === "number" || typeof value === "boolean") s = String(value);
|
|
11523
|
-
else s = stableJson(value, false);
|
|
11524
|
-
if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
|
|
11525
|
-
return `${s.slice(0, maxLength)}\u2026`;
|
|
11526
|
-
}
|
|
11527
|
-
return s;
|
|
11528
|
-
}
|
|
11529
|
-
function escapeMarkdown(value) {
|
|
11530
|
-
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
11531
|
-
}
|
|
11532
|
-
function escapeHtml(value) {
|
|
11533
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
11534
|
-
}
|
|
11535
|
-
function sortKeysDeep(input3) {
|
|
11536
|
-
if (input3 === null || typeof input3 !== "object") return input3;
|
|
11537
|
-
if (Array.isArray(input3)) return input3.map(sortKeysDeep);
|
|
11538
|
-
const o = input3;
|
|
11539
|
-
const out = {};
|
|
11540
|
-
for (const k of Object.keys(o).sort()) {
|
|
11541
|
-
out[k] = sortKeysDeep(o[k]);
|
|
11542
|
-
}
|
|
11543
|
-
return out;
|
|
11544
|
-
}
|
|
11545
|
-
function stableJson(value, pretty) {
|
|
11546
|
-
const sorted = sortKeysDeep(value);
|
|
11547
|
-
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
11548
|
-
}
|
|
11549
|
-
function compactAttributes3(attrs, options) {
|
|
11550
|
-
if (attrs === void 0) return {};
|
|
11551
|
-
const maxLen = options?.maxLength ?? 500;
|
|
11552
|
-
const redacted = options?.redacted ?? true;
|
|
11553
|
-
const out = {};
|
|
11554
|
-
for (const key of Object.keys(attrs).sort()) {
|
|
11555
|
-
if (redacted && shouldRedactKey(key)) {
|
|
11556
|
-
out[key] = "[REDACTED]";
|
|
11557
|
-
continue;
|
|
11558
|
-
}
|
|
11559
|
-
const v = attrs[key];
|
|
11560
|
-
out[key] = compactValue(v, maxLen, redacted);
|
|
11561
|
-
}
|
|
11562
|
-
return out;
|
|
11563
|
-
}
|
|
11564
|
-
function compactValue(value, maxLen, redacted) {
|
|
11565
|
-
if (value === null || typeof value !== "object") {
|
|
11566
|
-
return typeof value === "string" ? safeString2(value, maxLen) : value;
|
|
11567
|
-
}
|
|
11568
|
-
if (Array.isArray(value)) {
|
|
11569
|
-
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
|
|
11570
|
-
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
11571
|
-
return arr;
|
|
11572
|
-
}
|
|
11573
|
-
const o = value;
|
|
11574
|
-
const inner = {};
|
|
11575
|
-
for (const k of Object.keys(o)) {
|
|
11576
|
-
if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
11577
|
-
else inner[k] = compactValue(o[k], maxLen, redacted);
|
|
11578
|
-
}
|
|
11579
|
-
return inner;
|
|
11580
|
-
}
|
|
11581
|
-
function flattenTree(tree) {
|
|
11582
|
-
const out = [];
|
|
11583
|
-
function walk(nodes) {
|
|
11584
|
-
for (const n of nodes) {
|
|
11585
|
-
out.push(n);
|
|
11586
|
-
if (n.children.length > 0) walk(n.children);
|
|
11587
|
-
}
|
|
11588
|
-
}
|
|
11589
|
-
walk(tree.children);
|
|
11590
|
-
return out;
|
|
11591
|
-
}
|
|
11592
|
-
function zeroKinds() {
|
|
11593
|
-
return {
|
|
11594
|
-
RUN: 0,
|
|
11595
|
-
AGENT: 0,
|
|
11596
|
-
LLM: 0,
|
|
11597
|
-
TOOL: 0,
|
|
11598
|
-
CHAIN: 0,
|
|
11599
|
-
RETRIEVER: 0,
|
|
11600
|
-
DECISION: 0,
|
|
11601
|
-
RESULT: 0,
|
|
11602
|
-
ERROR: 0,
|
|
11603
|
-
LOGIC: 0,
|
|
11604
|
-
LOG: 0,
|
|
11605
|
-
OUTCOME: 0
|
|
11606
|
-
};
|
|
11607
|
-
}
|
|
11608
|
-
|
|
11609
12599
|
// packages/core/src/exporters/html-exporter.ts
|
|
12600
|
+
init_helpers();
|
|
11610
12601
|
function renderTreeHtml(nodes, ulClass = "tree") {
|
|
11611
12602
|
if (nodes.length === 0) return "";
|
|
11612
12603
|
const parts = [`<ul class="${ulClass}">`];
|
|
@@ -11679,7 +12670,7 @@ function exportHtml(tree, options) {
|
|
|
11679
12670
|
if (includeErrors && errors.length > 0) {
|
|
11680
12671
|
errorsHtml += "<h2>Errors</h2><ul>";
|
|
11681
12672
|
for (const n of errors) {
|
|
11682
|
-
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ?
|
|
12673
|
+
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
|
|
11683
12674
|
n.event.attributes.error.message,
|
|
11684
12675
|
maxLen
|
|
11685
12676
|
) : "";
|
|
@@ -11744,6 +12735,7 @@ ${attrsHtml}
|
|
|
11744
12735
|
}
|
|
11745
12736
|
|
|
11746
12737
|
// packages/core/src/exporters/markdown-exporter.ts
|
|
12738
|
+
init_helpers();
|
|
11747
12739
|
function renderTreeAscii(nodes, indent = "") {
|
|
11748
12740
|
const lines = [];
|
|
11749
12741
|
for (let i = 0; i < nodes.length; i++) {
|
|
@@ -11828,7 +12820,7 @@ function exportMarkdown(tree, options) {
|
|
|
11828
12820
|
lines.push("## Errors");
|
|
11829
12821
|
lines.push("");
|
|
11830
12822
|
for (const n of errors) {
|
|
11831
|
-
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ?
|
|
12823
|
+
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
|
|
11832
12824
|
n.event.attributes.error.message,
|
|
11833
12825
|
maxLen
|
|
11834
12826
|
) : "";
|
|
@@ -11866,6 +12858,7 @@ function exportMarkdown(tree, options) {
|
|
|
11866
12858
|
warnings
|
|
11867
12859
|
};
|
|
11868
12860
|
}
|
|
12861
|
+
init_helpers();
|
|
11869
12862
|
function hexFrom(seed, byteLen) {
|
|
11870
12863
|
return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
11871
12864
|
}
|
|
@@ -11992,6 +12985,9 @@ function exportOpenInference(tree, options) {
|
|
|
11992
12985
|
warnings
|
|
11993
12986
|
};
|
|
11994
12987
|
}
|
|
12988
|
+
|
|
12989
|
+
// packages/core/src/exporters/otlp-json-exporter.ts
|
|
12990
|
+
init_helpers();
|
|
11995
12991
|
function hexFrom2(seed, byteLen) {
|
|
11996
12992
|
return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
11997
12993
|
}
|
|
@@ -12195,7 +13191,11 @@ function validateExportContent(format, content) {
|
|
|
12195
13191
|
return { ok: false, format, errors, warnings };
|
|
12196
13192
|
}
|
|
12197
13193
|
|
|
13194
|
+
// packages/core/src/exporters/index.ts
|
|
13195
|
+
init_helpers();
|
|
13196
|
+
|
|
12198
13197
|
// packages/core/src/exporters/manual-trace-adapter.ts
|
|
13198
|
+
init_helpers();
|
|
12199
13199
|
function stepTypeToInspectKind(t) {
|
|
12200
13200
|
switch (t) {
|
|
12201
13201
|
case "llm":
|
|
@@ -12375,6 +13375,7 @@ function validateExport(result) {
|
|
|
12375
13375
|
}
|
|
12376
13376
|
|
|
12377
13377
|
// packages/core/src/report.ts
|
|
13378
|
+
init_helpers();
|
|
12378
13379
|
init_timeline();
|
|
12379
13380
|
init_what();
|
|
12380
13381
|
init_outcomes();
|
|
@@ -12531,6 +13532,9 @@ ${attrsSection}
|
|
|
12531
13532
|
};
|
|
12532
13533
|
}
|
|
12533
13534
|
|
|
13535
|
+
// packages/core/src/entries/exporters.ts
|
|
13536
|
+
init_helpers();
|
|
13537
|
+
|
|
12534
13538
|
// packages/cli/src/export.ts
|
|
12535
13539
|
function parseRedactionProfile(s) {
|
|
12536
13540
|
const v = (s ?? "local").trim().toLowerCase();
|
|
@@ -12609,9 +13613,9 @@ Trace directory: ${traceDir}`);
|
|
|
12609
13613
|
if (validation !== void 0 && !validation.ok) {
|
|
12610
13614
|
process.exitCode = 1;
|
|
12611
13615
|
}
|
|
12612
|
-
const outPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
13616
|
+
const outPath = options.output !== void 0 && options.output.trim() !== "" ? path24__default.default.resolve(options.output.trim()) : void 0;
|
|
12613
13617
|
if (outPath !== void 0) {
|
|
12614
|
-
await promises.mkdir(
|
|
13618
|
+
await promises.mkdir(path24__default.default.dirname(outPath), { recursive: true });
|
|
12615
13619
|
await promises.writeFile(outPath, result.content, "utf-8");
|
|
12616
13620
|
const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
|
|
12617
13621
|
console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
|
|
@@ -12752,6 +13756,7 @@ function manualTraceEventsToComparableRun(events) {
|
|
|
12752
13756
|
}
|
|
12753
13757
|
|
|
12754
13758
|
// packages/core/src/diff/engine.ts
|
|
13759
|
+
init_helpers();
|
|
12755
13760
|
var DEFAULT_THRESHOLD_MS = 0;
|
|
12756
13761
|
function pathSeg(step, index) {
|
|
12757
13762
|
return { index, name: step.name, stepId: step.id };
|
|
@@ -12791,13 +13796,13 @@ function pairSteps(left, right) {
|
|
|
12791
13796
|
return pairs;
|
|
12792
13797
|
}
|
|
12793
13798
|
function compareLeafSteps(L, R, segments, opts, out) {
|
|
12794
|
-
const
|
|
13799
|
+
const path33 = buildPath(segments);
|
|
12795
13800
|
if (L.name !== R.name) {
|
|
12796
13801
|
out.push({
|
|
12797
13802
|
kind: "structure",
|
|
12798
13803
|
severity: "warning",
|
|
12799
13804
|
message: "Step name differs",
|
|
12800
|
-
path:
|
|
13805
|
+
path: path33,
|
|
12801
13806
|
left: L.name,
|
|
12802
13807
|
right: R.name
|
|
12803
13808
|
});
|
|
@@ -12807,7 +13812,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12807
13812
|
kind: "step-type",
|
|
12808
13813
|
severity: "warning",
|
|
12809
13814
|
message: "Step type differs",
|
|
12810
|
-
path:
|
|
13815
|
+
path: path33,
|
|
12811
13816
|
left: L.type,
|
|
12812
13817
|
right: R.type
|
|
12813
13818
|
});
|
|
@@ -12817,7 +13822,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12817
13822
|
kind: "step-status",
|
|
12818
13823
|
severity: "warning",
|
|
12819
13824
|
message: "Step status differs",
|
|
12820
|
-
path:
|
|
13825
|
+
path: path33,
|
|
12821
13826
|
left: L.status,
|
|
12822
13827
|
right: R.status
|
|
12823
13828
|
});
|
|
@@ -12829,7 +13834,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12829
13834
|
kind: "error",
|
|
12830
13835
|
severity: "error",
|
|
12831
13836
|
message: "Step error message differs",
|
|
12832
|
-
path:
|
|
13837
|
+
path: path33,
|
|
12833
13838
|
left: le || void 0,
|
|
12834
13839
|
right: re || void 0
|
|
12835
13840
|
});
|
|
@@ -12847,7 +13852,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12847
13852
|
kind: "duration",
|
|
12848
13853
|
severity: "info",
|
|
12849
13854
|
message: "Step duration differs",
|
|
12850
|
-
path:
|
|
13855
|
+
path: path33,
|
|
12851
13856
|
left: ld,
|
|
12852
13857
|
right: rd
|
|
12853
13858
|
});
|
|
@@ -12860,7 +13865,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12860
13865
|
kind: "metadata",
|
|
12861
13866
|
severity: "info",
|
|
12862
13867
|
message: "Step metadata differs",
|
|
12863
|
-
path:
|
|
13868
|
+
path: path33,
|
|
12864
13869
|
left: L.metadata,
|
|
12865
13870
|
right: R.metadata
|
|
12866
13871
|
});
|
|
@@ -12872,7 +13877,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12872
13877
|
kind: "output",
|
|
12873
13878
|
severity: "info",
|
|
12874
13879
|
message: "Output preview differs",
|
|
12875
|
-
path:
|
|
13880
|
+
path: path33,
|
|
12876
13881
|
left: L.outputPreview,
|
|
12877
13882
|
right: R.outputPreview
|
|
12878
13883
|
});
|
|
@@ -13033,11 +14038,11 @@ function diffRuns(left, right, options) {
|
|
|
13033
14038
|
|
|
13034
14039
|
// packages/core/src/diff/renderer.ts
|
|
13035
14040
|
init_source();
|
|
13036
|
-
function formatPath(
|
|
13037
|
-
if (
|
|
14041
|
+
function formatPath(path33) {
|
|
14042
|
+
if (path33 === void 0 || path33.path.length === 0) {
|
|
13038
14043
|
return "(run)";
|
|
13039
14044
|
}
|
|
13040
|
-
return
|
|
14045
|
+
return path33.path.map((s) => s.name).join(" > ");
|
|
13041
14046
|
}
|
|
13042
14047
|
function formatValue(v, verbose) {
|
|
13043
14048
|
if (v === void 0) return "(undefined)";
|
|
@@ -13425,7 +14430,7 @@ function indexedToMetadata(row, traceDir) {
|
|
|
13425
14430
|
endedAt: row.endedAt ?? void 0,
|
|
13426
14431
|
durationMs: row.durationMs ?? void 0,
|
|
13427
14432
|
eventCount: 0,
|
|
13428
|
-
filePath:
|
|
14433
|
+
filePath: path24__default.default.join(traceDir, row.file),
|
|
13429
14434
|
fileSize: 0,
|
|
13430
14435
|
createdAt: new Date(row.mtimeMs)
|
|
13431
14436
|
};
|
|
@@ -13906,9 +14911,9 @@ async function reportCommand(runId, options = {}) {
|
|
|
13906
14911
|
correlation: !options.noCorrelation,
|
|
13907
14912
|
...options.section ? { section: options.section } : {}
|
|
13908
14913
|
});
|
|
13909
|
-
const outPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
14914
|
+
const outPath = options.output !== void 0 && options.output.trim() !== "" ? path24__default.default.resolve(options.output.trim()) : void 0;
|
|
13910
14915
|
if (outPath !== void 0) {
|
|
13911
|
-
await promises.mkdir(
|
|
14916
|
+
await promises.mkdir(path24__default.default.dirname(outPath), { recursive: true });
|
|
13912
14917
|
await promises.writeFile(outPath, result.content, "utf-8");
|
|
13913
14918
|
console.log(`Wrote ${result.fileExtension} report to ${outPath}`);
|
|
13914
14919
|
}
|
|
@@ -14158,17 +15163,17 @@ function applyRule(rule, value, replacement) {
|
|
|
14158
15163
|
}
|
|
14159
15164
|
return value;
|
|
14160
15165
|
}
|
|
14161
|
-
function childPath(
|
|
15166
|
+
function childPath(path33, key) {
|
|
14162
15167
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
14163
|
-
return
|
|
15168
|
+
return path33 ? `${path33}.${key}` : key;
|
|
14164
15169
|
}
|
|
14165
|
-
return `${
|
|
15170
|
+
return `${path33 || "$"}[${JSON.stringify(key)}]`;
|
|
14166
15171
|
}
|
|
14167
|
-
function indexPath(
|
|
14168
|
-
return `${
|
|
15172
|
+
function indexPath(path33, index) {
|
|
15173
|
+
return `${path33 || "$"}[${index}]`;
|
|
14169
15174
|
}
|
|
14170
|
-
function makeFinding(
|
|
14171
|
-
return preview === void 0 ? { path:
|
|
15175
|
+
function makeFinding(path33, detector, action, matchKind, severity = "warning", preview) {
|
|
15176
|
+
return preview === void 0 ? { path: path33, detector, action, severity, matchKind } : { path: path33, detector, action, severity, matchKind, preview };
|
|
14172
15177
|
}
|
|
14173
15178
|
function createRedactionProfile(profile = "local") {
|
|
14174
15179
|
switch (profile) {
|
|
@@ -14237,11 +15242,11 @@ var Redactor2 = class {
|
|
|
14237
15242
|
#recordFinding(state, finding) {
|
|
14238
15243
|
if (this.#collectFindings) state.findings.push(finding);
|
|
14239
15244
|
}
|
|
14240
|
-
#redactValue(value, key,
|
|
15245
|
+
#redactValue(value, key, path33, depth, state) {
|
|
14241
15246
|
if (depth > this.#maxDepth) {
|
|
14242
15247
|
this.#recordFinding(
|
|
14243
15248
|
state,
|
|
14244
|
-
makeFinding(
|
|
15249
|
+
makeFinding(path33, "structure.maxDepth", "truncate", "value", "warning")
|
|
14245
15250
|
);
|
|
14246
15251
|
return "[Truncated]";
|
|
14247
15252
|
}
|
|
@@ -14250,19 +15255,19 @@ var Redactor2 = class {
|
|
|
14250
15255
|
if (rule) {
|
|
14251
15256
|
this.#recordFinding(
|
|
14252
15257
|
state,
|
|
14253
|
-
makeFinding(
|
|
15258
|
+
makeFinding(path33, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
14254
15259
|
);
|
|
14255
15260
|
return applyRule(rule, value, this.#replacement);
|
|
14256
15261
|
}
|
|
14257
15262
|
}
|
|
14258
15263
|
for (const detector of this.#detectors) {
|
|
14259
|
-
const detections = detector.detect({ path:
|
|
15264
|
+
const detections = detector.detect({ path: path33, key, value });
|
|
14260
15265
|
for (const detection of detections) {
|
|
14261
15266
|
const action = detection.action ?? "replace";
|
|
14262
15267
|
this.#recordFinding(
|
|
14263
15268
|
state,
|
|
14264
15269
|
makeFinding(
|
|
14265
|
-
|
|
15270
|
+
path33,
|
|
14266
15271
|
detector.id,
|
|
14267
15272
|
action,
|
|
14268
15273
|
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
@@ -14280,7 +15285,7 @@ var Redactor2 = class {
|
|
|
14280
15285
|
const out = [];
|
|
14281
15286
|
state.seen.set(value, out);
|
|
14282
15287
|
value.forEach((item, index) => {
|
|
14283
|
-
out[index] = this.#redactValue(item, void 0, indexPath(
|
|
15288
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path33, index), depth + 1, state);
|
|
14284
15289
|
});
|
|
14285
15290
|
return out;
|
|
14286
15291
|
}
|
|
@@ -14292,7 +15297,7 @@ var Redactor2 = class {
|
|
|
14292
15297
|
out[entryKey] = this.#redactValue(
|
|
14293
15298
|
entryValue,
|
|
14294
15299
|
entryKey,
|
|
14295
|
-
childPath(
|
|
15300
|
+
childPath(path33 === "$" ? "" : path33, entryKey),
|
|
14296
15301
|
depth + 1,
|
|
14297
15302
|
state
|
|
14298
15303
|
);
|
|
@@ -14752,14 +15757,14 @@ function uniqueSorted(values) {
|
|
|
14752
15757
|
return [...new Set(values)].sort();
|
|
14753
15758
|
}
|
|
14754
15759
|
function isWithinDirectory(child, parent) {
|
|
14755
|
-
const relative =
|
|
14756
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
15760
|
+
const relative = path24__default.default.relative(parent, child);
|
|
15761
|
+
return relative === "" || !relative.startsWith("..") && !path24__default.default.isAbsolute(relative);
|
|
14757
15762
|
}
|
|
14758
15763
|
async function resolveOutputPath(inputPath, output2, force) {
|
|
14759
15764
|
if (output2 === void 0 || output2.trim() === "") return void 0;
|
|
14760
|
-
const inputAbs =
|
|
14761
|
-
const outputAbs =
|
|
14762
|
-
const inputDir =
|
|
15765
|
+
const inputAbs = path24__default.default.resolve(inputPath);
|
|
15766
|
+
const outputAbs = path24__default.default.resolve(output2.trim());
|
|
15767
|
+
const inputDir = path24__default.default.dirname(inputAbs);
|
|
14763
15768
|
if (!isWithinDirectory(outputAbs, inputDir)) {
|
|
14764
15769
|
throw new Error("Refusing to write migrated output outside the input directory.");
|
|
14765
15770
|
}
|
|
@@ -14880,7 +15885,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
14880
15885
|
process.exitCode = 1;
|
|
14881
15886
|
return;
|
|
14882
15887
|
}
|
|
14883
|
-
const inputPath =
|
|
15888
|
+
const inputPath = path24__default.default.resolve(input3.trim());
|
|
14884
15889
|
const dryRun = options.dryRun === true;
|
|
14885
15890
|
if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
|
|
14886
15891
|
console.error("migrate requires --dry-run or --output <path>.");
|
|
@@ -14899,7 +15904,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
14899
15904
|
);
|
|
14900
15905
|
const result = await buildMigration(inputPath, outputPath);
|
|
14901
15906
|
if (!dryRun && outputPath !== void 0) {
|
|
14902
|
-
await promises.mkdir(
|
|
15907
|
+
await promises.mkdir(path24__default.default.dirname(outputPath), { recursive: true });
|
|
14903
15908
|
await promises.writeFile(outputPath, result.content, "utf-8");
|
|
14904
15909
|
}
|
|
14905
15910
|
printSummary2(result, dryRun);
|
|
@@ -15233,23 +16238,23 @@ function evaluatePromptInjection(text, options = {}) {
|
|
|
15233
16238
|
}
|
|
15234
16239
|
return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
|
|
15235
16240
|
}
|
|
15236
|
-
function validateSchemaField(value, field,
|
|
16241
|
+
function validateSchemaField(value, field, path33, evidence) {
|
|
15237
16242
|
const ruleId = "guardrail.structured-output";
|
|
15238
16243
|
if (field.type) {
|
|
15239
16244
|
const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
15240
16245
|
if (actual !== field.type) {
|
|
15241
|
-
evidence.push({ ruleId, path:
|
|
16246
|
+
evidence.push({ ruleId, path: path33, preview: `expected ${field.type}, got ${actual}` });
|
|
15242
16247
|
return;
|
|
15243
16248
|
}
|
|
15244
16249
|
}
|
|
15245
16250
|
if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
|
|
15246
|
-
evidence.push({ ruleId, path:
|
|
16251
|
+
evidence.push({ ruleId, path: path33, preview: "value not in enum" });
|
|
15247
16252
|
}
|
|
15248
16253
|
if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
|
|
15249
16254
|
const record = value;
|
|
15250
16255
|
for (const key of field.required) {
|
|
15251
16256
|
if (!(key in record)) {
|
|
15252
|
-
evidence.push({ ruleId, path: `${
|
|
16257
|
+
evidence.push({ ruleId, path: `${path33}.${key}`, preview: "missing required key" });
|
|
15253
16258
|
}
|
|
15254
16259
|
}
|
|
15255
16260
|
}
|
|
@@ -15576,7 +16581,7 @@ function asConfig(value) {
|
|
|
15576
16581
|
}
|
|
15577
16582
|
async function loadConfig(configPath) {
|
|
15578
16583
|
if (configPath === void 0) return {};
|
|
15579
|
-
const extension =
|
|
16584
|
+
const extension = path24__default.default.extname(configPath);
|
|
15580
16585
|
if (TS_CONFIG_EXTENSIONS2.has(extension)) {
|
|
15581
16586
|
throw new Error(
|
|
15582
16587
|
"TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -15585,7 +16590,7 @@ async function loadConfig(configPath) {
|
|
|
15585
16590
|
if (!CONFIG_EXTENSIONS2.has(extension)) {
|
|
15586
16591
|
throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
|
|
15587
16592
|
}
|
|
15588
|
-
const absolute =
|
|
16593
|
+
const absolute = path24__default.default.resolve(configPath);
|
|
15589
16594
|
if (extension === ".json") {
|
|
15590
16595
|
const raw = await promises.readFile(absolute, "utf-8");
|
|
15591
16596
|
return asConfig(JSON.parse(raw));
|
|
@@ -15750,10 +16755,10 @@ function printHuman(result) {
|
|
|
15750
16755
|
console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
|
|
15751
16756
|
}
|
|
15752
16757
|
for (const finding of result.findings) {
|
|
15753
|
-
const
|
|
16758
|
+
const path33 = finding.evidence[0]?.path;
|
|
15754
16759
|
const run = finding.evidence[0]?.runId;
|
|
15755
16760
|
const runPrefix = run ? `[${run}] ` : "";
|
|
15756
|
-
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${
|
|
16761
|
+
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path33 ? ` (${path33})` : ""}`);
|
|
15757
16762
|
}
|
|
15758
16763
|
}
|
|
15759
16764
|
function readErrorResult(error) {
|
|
@@ -15956,7 +16961,7 @@ function createViewerServer(options = {}) {
|
|
|
15956
16961
|
return sendJson(res, 200, {
|
|
15957
16962
|
ok: true,
|
|
15958
16963
|
readOnly: true,
|
|
15959
|
-
traceDir:
|
|
16964
|
+
traceDir: path24__default.default.resolve(traceDir)
|
|
15960
16965
|
});
|
|
15961
16966
|
}
|
|
15962
16967
|
const td = new TraceDirectory({ dir: traceDir });
|
|
@@ -15974,7 +16979,7 @@ function createViewerServer(options = {}) {
|
|
|
15974
16979
|
runId: meta2.runId,
|
|
15975
16980
|
name: meta2.name,
|
|
15976
16981
|
status: meta2.status,
|
|
15977
|
-
file:
|
|
16982
|
+
file: path24__default.default.basename(meta2.filePath),
|
|
15978
16983
|
startedAt: meta2.startedAt,
|
|
15979
16984
|
durationMs: meta2.durationMs
|
|
15980
16985
|
}))
|
|
@@ -16089,7 +17094,7 @@ function startViewerServer(options = {}) {
|
|
|
16089
17094
|
resolve({
|
|
16090
17095
|
host,
|
|
16091
17096
|
port: resolvedPort,
|
|
16092
|
-
traceDir:
|
|
17097
|
+
traceDir: path24__default.default.resolve(traceDir),
|
|
16093
17098
|
url: `http://${host}:${resolvedPort}`
|
|
16094
17099
|
});
|
|
16095
17100
|
});
|
|
@@ -16294,10 +17299,10 @@ async function evalRun(input3, options = {}) {
|
|
|
16294
17299
|
diagnostics: []
|
|
16295
17300
|
};
|
|
16296
17301
|
}
|
|
16297
|
-
function evidenceForRun(run,
|
|
16298
|
-
return [{ runId: run.runId, ...
|
|
17302
|
+
function evidenceForRun(run, path33) {
|
|
17303
|
+
return [{ runId: run.runId, ...path33 !== void 0 ? { path: path33 } : {} }];
|
|
16299
17304
|
}
|
|
16300
|
-
function evidenceForEvent(event,
|
|
17305
|
+
function evidenceForEvent(event, path33) {
|
|
16301
17306
|
return [
|
|
16302
17307
|
{
|
|
16303
17308
|
runId: event.runId,
|
|
@@ -16305,7 +17310,7 @@ function evidenceForEvent(event, path30) {
|
|
|
16305
17310
|
...event.parentId !== void 0 ? { parentId: event.parentId } : {},
|
|
16306
17311
|
kind: event.kind,
|
|
16307
17312
|
name: event.name,
|
|
16308
|
-
...
|
|
17313
|
+
...path33 !== void 0 ? { path: path33 } : {}
|
|
16309
17314
|
}
|
|
16310
17315
|
];
|
|
16311
17316
|
}
|
|
@@ -16463,9 +17468,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
|
|
|
16463
17468
|
function tokenize(text) {
|
|
16464
17469
|
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));
|
|
16465
17470
|
}
|
|
16466
|
-
function firstEvidence(fields, run,
|
|
17471
|
+
function firstEvidence(fields, run, path33) {
|
|
16467
17472
|
const first = fields[0];
|
|
16468
|
-
return first === void 0 ? evidenceForRun(run,
|
|
17473
|
+
return first === void 0 ? evidenceForRun(run, path33) : evidenceForEvent(first.node.event, first.path);
|
|
16469
17474
|
}
|
|
16470
17475
|
function collectSourceIds(nodes, keys) {
|
|
16471
17476
|
const wanted = keySet(keys);
|
|
@@ -16842,8 +17847,8 @@ function renderEvalMarkdown(result) {
|
|
|
16842
17847
|
if (result.findings.length > 0) {
|
|
16843
17848
|
lines.push("", "## Findings");
|
|
16844
17849
|
for (const finding of result.findings) {
|
|
16845
|
-
const
|
|
16846
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
17850
|
+
const path33 = finding.evidence[0]?.path;
|
|
17851
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path33 ? ` (${path33})` : ""}`);
|
|
16847
17852
|
}
|
|
16848
17853
|
}
|
|
16849
17854
|
return `${lines.join("\n")}
|
|
@@ -16890,7 +17895,7 @@ function asConfig2(value) {
|
|
|
16890
17895
|
}
|
|
16891
17896
|
async function loadConfig2(configPath) {
|
|
16892
17897
|
if (configPath === void 0) return {};
|
|
16893
|
-
const extension =
|
|
17898
|
+
const extension = path24__default.default.extname(configPath);
|
|
16894
17899
|
if (TS_CONFIG_EXTENSIONS3.has(extension)) {
|
|
16895
17900
|
throw new Error(
|
|
16896
17901
|
"TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -16899,7 +17904,7 @@ async function loadConfig2(configPath) {
|
|
|
16899
17904
|
if (!CONFIG_EXTENSIONS3.has(extension)) {
|
|
16900
17905
|
throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
|
|
16901
17906
|
}
|
|
16902
|
-
const absolute =
|
|
17907
|
+
const absolute = path24__default.default.resolve(configPath);
|
|
16903
17908
|
if (extension === ".json") {
|
|
16904
17909
|
const raw = await promises.readFile(absolute, "utf-8");
|
|
16905
17910
|
return asConfig2(JSON.parse(raw));
|
|
@@ -17036,8 +18041,8 @@ function printHuman2(result) {
|
|
|
17036
18041
|
console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
|
|
17037
18042
|
}
|
|
17038
18043
|
for (const finding of result.findings) {
|
|
17039
|
-
const
|
|
17040
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
18044
|
+
const path33 = finding.evidence[0]?.path;
|
|
18045
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path33 ? ` (${path33})` : ""}`);
|
|
17041
18046
|
}
|
|
17042
18047
|
}
|
|
17043
18048
|
function readErrorResult2(error) {
|
|
@@ -17275,8 +18280,8 @@ function printHuman3(result) {
|
|
|
17275
18280
|
console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
|
|
17276
18281
|
}
|
|
17277
18282
|
for (const finding of result.findings) {
|
|
17278
|
-
const
|
|
17279
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
18283
|
+
const path33 = finding.evidence[0]?.path;
|
|
18284
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path33 ? ` (${path33})` : ""}`);
|
|
17280
18285
|
}
|
|
17281
18286
|
console.log(`Note: ${result.note}`);
|
|
17282
18287
|
}
|
|
@@ -17427,8 +18432,8 @@ function renderCheckSection(result) {
|
|
|
17427
18432
|
`Diagnostics: ${result.diagnostics.length}`
|
|
17428
18433
|
];
|
|
17429
18434
|
for (const finding of result.findings.slice(0, 10)) {
|
|
17430
|
-
const
|
|
17431
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
18435
|
+
const path33 = finding.evidence[0]?.path ?? "(run)";
|
|
18436
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path33})`);
|
|
17432
18437
|
}
|
|
17433
18438
|
for (const diagnostic7 of result.diagnostics.slice(0, 10)) {
|
|
17434
18439
|
lines.push(`- ${diagnostic7.code}: ${diagnostic7.message}`);
|
|
@@ -17506,8 +18511,8 @@ function renderHtml(trace, check, diff) {
|
|
|
17506
18511
|
`;
|
|
17507
18512
|
}
|
|
17508
18513
|
async function writeArtifact(outputDir, relativePath, content, files) {
|
|
17509
|
-
const outPath =
|
|
17510
|
-
await promises.mkdir(
|
|
18514
|
+
const outPath = path24__default.default.join(outputDir, relativePath);
|
|
18515
|
+
await promises.mkdir(path24__default.default.dirname(outPath), { recursive: true });
|
|
17511
18516
|
await promises.writeFile(outPath, content, "utf-8");
|
|
17512
18517
|
files.push(relativePath);
|
|
17513
18518
|
}
|
|
@@ -17523,7 +18528,7 @@ function manifestStatus(check, diff) {
|
|
|
17523
18528
|
return "ok";
|
|
17524
18529
|
}
|
|
17525
18530
|
async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
17526
|
-
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ?
|
|
18531
|
+
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path24__default.default.resolve(options.outputDir.trim()) : "";
|
|
17527
18532
|
if (outputDir === "") {
|
|
17528
18533
|
console.error("--output-dir is required.");
|
|
17529
18534
|
process.exitCode = 1;
|
|
@@ -17589,8 +18594,8 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
|
17589
18594
|
await writeArtifact(outputDir, "report.html", renderHtml(trace, check, diff), files);
|
|
17590
18595
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
17591
18596
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
17592
|
-
await promises.mkdir(
|
|
17593
|
-
await promises.appendFile(
|
|
18597
|
+
await promises.mkdir(path24__default.default.dirname(path24__default.default.resolve(summaryTarget)), { recursive: true });
|
|
18598
|
+
await promises.appendFile(path24__default.default.resolve(summaryTarget), `
|
|
17594
18599
|
${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
17595
18600
|
}
|
|
17596
18601
|
const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
|
|
@@ -17609,10 +18614,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
17609
18614
|
findings: diff?.findings.length ?? 0,
|
|
17610
18615
|
diagnostics: diff?.diagnostics.length ?? 0
|
|
17611
18616
|
},
|
|
17612
|
-
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary:
|
|
18617
|
+
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path24__default.default.resolve(summaryTarget) } : {},
|
|
17613
18618
|
note: NOTE
|
|
17614
18619
|
};
|
|
17615
|
-
await promises.writeFile(
|
|
18620
|
+
await promises.writeFile(path24__default.default.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
|
|
17616
18621
|
if (options.json === true) {
|
|
17617
18622
|
console.log(writeJson3(manifest).trimEnd());
|
|
17618
18623
|
} else {
|
|
@@ -17806,19 +18811,19 @@ function serializeWorkspaceManifest(manifest) {
|
|
|
17806
18811
|
}
|
|
17807
18812
|
var INDEX_DIR_NAME = "index";
|
|
17808
18813
|
function resolveWorkspaceLocation(cwd = process.cwd()) {
|
|
17809
|
-
const projectRoot =
|
|
17810
|
-
const workspaceDir =
|
|
18814
|
+
const projectRoot = path24__default.default.resolve(cwd);
|
|
18815
|
+
const workspaceDir = path24__default.default.join(projectRoot, WORKSPACE_DIR_NAME);
|
|
17811
18816
|
return {
|
|
17812
18817
|
projectRoot,
|
|
17813
18818
|
workspaceDir,
|
|
17814
|
-
manifestPath:
|
|
18819
|
+
manifestPath: path24__default.default.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
|
|
17815
18820
|
};
|
|
17816
18821
|
}
|
|
17817
18822
|
function resolveInsideWorkspace(workspaceDir, relative) {
|
|
17818
|
-
const base =
|
|
17819
|
-
const resolved =
|
|
17820
|
-
const rel =
|
|
17821
|
-
if (rel === "" || rel === "." || !rel.startsWith("..") && !
|
|
18823
|
+
const base = path24__default.default.resolve(workspaceDir);
|
|
18824
|
+
const resolved = path24__default.default.resolve(base, relative);
|
|
18825
|
+
const rel = path24__default.default.relative(base, resolved);
|
|
18826
|
+
if (rel === "" || rel === "." || !rel.startsWith("..") && !path24__default.default.isAbsolute(rel)) {
|
|
17822
18827
|
return resolved;
|
|
17823
18828
|
}
|
|
17824
18829
|
throw new Error(
|
|
@@ -17887,7 +18892,7 @@ async function createWorkspace(options = {}) {
|
|
|
17887
18892
|
created = false;
|
|
17888
18893
|
adopted = true;
|
|
17889
18894
|
} else {
|
|
17890
|
-
const project = options.project?.trim() ||
|
|
18895
|
+
const project = options.project?.trim() || path24__default.default.basename(location.projectRoot) || "workspace";
|
|
17891
18896
|
const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
|
|
17892
18897
|
manifest = createDefaultWorkspaceManifest({
|
|
17893
18898
|
project,
|
|
@@ -18026,7 +19031,7 @@ async function doctorWorkspace(location) {
|
|
|
18026
19031
|
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
18027
19032
|
for (const file of await listJsonl(abs)) {
|
|
18028
19033
|
try {
|
|
18029
|
-
const s = await promises.stat(
|
|
19034
|
+
const s = await promises.stat(path24__default.default.join(abs, file));
|
|
18030
19035
|
newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
|
|
18031
19036
|
} catch {
|
|
18032
19037
|
checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
|
|
@@ -18074,7 +19079,7 @@ async function cleanWorkspace(location, manifest, options = {}) {
|
|
|
18074
19079
|
const relPath = `${rel}/${entry}`;
|
|
18075
19080
|
removed.push(relPath);
|
|
18076
19081
|
if (!dryRun) {
|
|
18077
|
-
await promises.rm(
|
|
19082
|
+
await promises.rm(path24__default.default.join(abs, entry), { recursive: true, force: true });
|
|
18078
19083
|
}
|
|
18079
19084
|
}
|
|
18080
19085
|
}
|
|
@@ -18120,8 +19125,8 @@ async function resolveOutputDir(options, runIds, cwd) {
|
|
|
18120
19125
|
const location = resolveWorkspaceLocation(cwd);
|
|
18121
19126
|
const manifest = await readWorkspaceManifestFile(location);
|
|
18122
19127
|
if (manifest.ok && manifest.manifest) {
|
|
18123
|
-
const rel =
|
|
18124
|
-
if (!rel.startsWith("..") && !
|
|
19128
|
+
const rel = path24__default.default.relative(location.workspaceDir, normalized);
|
|
19129
|
+
if (!rel.startsWith("..") && !path24__default.default.isAbsolute(rel)) {
|
|
18125
19130
|
return resolveInsideWorkspace(location.workspaceDir, rel);
|
|
18126
19131
|
}
|
|
18127
19132
|
}
|
|
@@ -18137,7 +19142,7 @@ async function resolveOutputDir(options, runIds, cwd) {
|
|
|
18137
19142
|
const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
|
|
18138
19143
|
return resolveInsideWorkspace(
|
|
18139
19144
|
location.workspaceDir,
|
|
18140
|
-
|
|
19145
|
+
path24__default.default.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
|
|
18141
19146
|
);
|
|
18142
19147
|
}
|
|
18143
19148
|
} catch {
|
|
@@ -18145,8 +19150,8 @@ async function resolveOutputDir(options, runIds, cwd) {
|
|
|
18145
19150
|
return defaultBundleOutputPath(runIds);
|
|
18146
19151
|
}
|
|
18147
19152
|
async function writeBundleFile(outputDir, relativePath, content, files) {
|
|
18148
|
-
const outPath =
|
|
18149
|
-
await promises.mkdir(
|
|
19153
|
+
const outPath = path24__default.default.join(outputDir, relativePath);
|
|
19154
|
+
await promises.mkdir(path24__default.default.dirname(outPath), { recursive: true });
|
|
18150
19155
|
await promises.writeFile(outPath, content, "utf-8");
|
|
18151
19156
|
files.push(relativePath);
|
|
18152
19157
|
}
|
|
@@ -18372,7 +19377,7 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
18372
19377
|
files
|
|
18373
19378
|
);
|
|
18374
19379
|
metadata.files = [...files].sort((a, b) => a.localeCompare(b));
|
|
18375
|
-
await promises.writeFile(
|
|
19380
|
+
await promises.writeFile(path24__default.default.join(outputDir, "metadata.json"), writeJson4(metadata), "utf-8");
|
|
18376
19381
|
if (options.json) {
|
|
18377
19382
|
console.log(
|
|
18378
19383
|
writeJson4({
|
|
@@ -18406,7 +19411,7 @@ ${body}
|
|
|
18406
19411
|
`;
|
|
18407
19412
|
}
|
|
18408
19413
|
function validateReporterArtifactPath(options) {
|
|
18409
|
-
const outputDir =
|
|
19414
|
+
const outputDir = path24__default.default.resolve(options.outputDir);
|
|
18410
19415
|
const diagnostics = [];
|
|
18411
19416
|
const rawPath = options.relativePath;
|
|
18412
19417
|
if (rawPath.length === 0) {
|
|
@@ -18426,7 +19431,7 @@ function validateReporterArtifactPath(options) {
|
|
|
18426
19431
|
});
|
|
18427
19432
|
return { ok: false, outputDir, diagnostics };
|
|
18428
19433
|
}
|
|
18429
|
-
if (
|
|
19434
|
+
if (path24__default.default.isAbsolute(rawPath) || path24__default.default.win32.isAbsolute(rawPath)) {
|
|
18430
19435
|
diagnostics.push({
|
|
18431
19436
|
code: "artifact_path_absolute",
|
|
18432
19437
|
severity: "error",
|
|
@@ -18435,7 +19440,7 @@ function validateReporterArtifactPath(options) {
|
|
|
18435
19440
|
});
|
|
18436
19441
|
return { ok: false, outputDir, diagnostics };
|
|
18437
19442
|
}
|
|
18438
|
-
const normalized =
|
|
19443
|
+
const normalized = path24__default.default.posix.normalize(rawPath.replace(/\\/g, "/"));
|
|
18439
19444
|
const segments = normalized.split("/");
|
|
18440
19445
|
if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
|
|
18441
19446
|
diagnostics.push({
|
|
@@ -18446,9 +19451,9 @@ function validateReporterArtifactPath(options) {
|
|
|
18446
19451
|
});
|
|
18447
19452
|
return { ok: false, outputDir, diagnostics };
|
|
18448
19453
|
}
|
|
18449
|
-
const absolutePath =
|
|
18450
|
-
const relFromOutput =
|
|
18451
|
-
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") ||
|
|
19454
|
+
const absolutePath = path24__default.default.resolve(outputDir, normalized);
|
|
19455
|
+
const relFromOutput = path24__default.default.relative(outputDir, absolutePath);
|
|
19456
|
+
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path24__default.default.isAbsolute(relFromOutput)) {
|
|
18452
19457
|
diagnostics.push({
|
|
18453
19458
|
code: "artifact_path_escape",
|
|
18454
19459
|
severity: "error",
|
|
@@ -18594,23 +19599,23 @@ function readManifestDocument(value) {
|
|
|
18594
19599
|
};
|
|
18595
19600
|
}
|
|
18596
19601
|
function cwdRelative(filePath) {
|
|
18597
|
-
const relative =
|
|
18598
|
-
if (relative === "" || relative.startsWith("../") ||
|
|
18599
|
-
return
|
|
19602
|
+
const relative = path24__default.default.relative(process.cwd(), path24__default.default.resolve(filePath)).replace(/\\/g, "/");
|
|
19603
|
+
if (relative === "" || relative.startsWith("../") || path24__default.default.isAbsolute(relative)) {
|
|
19604
|
+
return path24__default.default.basename(filePath);
|
|
18600
19605
|
}
|
|
18601
19606
|
return relative;
|
|
18602
19607
|
}
|
|
18603
19608
|
async function readReporterManifest(filePath) {
|
|
18604
|
-
const absolute =
|
|
19609
|
+
const absolute = path24__default.default.resolve(filePath);
|
|
18605
19610
|
const raw = await promises.readFile(absolute, "utf-8");
|
|
18606
19611
|
const document = readManifestDocument(JSON.parse(raw));
|
|
18607
19612
|
const manifest = document.manifest;
|
|
18608
19613
|
const results = manifest.results.map((result) => ({
|
|
18609
19614
|
testId: safeText(result.testId),
|
|
18610
19615
|
name: safeText(result.name),
|
|
18611
|
-
...result.file === void 0 ? {} : { file: safeText(
|
|
19616
|
+
...result.file === void 0 ? {} : { file: safeText(path24__default.default.basename(result.file)) },
|
|
18612
19617
|
status: result.status,
|
|
18613
|
-
...result.tracePath === void 0 ? {} : { tracePath: safeText(
|
|
19618
|
+
...result.tracePath === void 0 ? {} : { tracePath: safeText(path24__default.default.basename(result.tracePath)) },
|
|
18614
19619
|
artifacts: result.artifacts,
|
|
18615
19620
|
diagnostics: result.diagnostics
|
|
18616
19621
|
}));
|
|
@@ -18749,15 +19754,15 @@ async function ciSummaryCommand(manifestPaths, options = {}) {
|
|
|
18749
19754
|
return;
|
|
18750
19755
|
}
|
|
18751
19756
|
const markdown = renderMarkdown2(result);
|
|
18752
|
-
const outputPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
19757
|
+
const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path24__default.default.resolve(options.output.trim()) : void 0;
|
|
18753
19758
|
if (outputPath !== void 0) {
|
|
18754
|
-
await promises.mkdir(
|
|
19759
|
+
await promises.mkdir(path24__default.default.dirname(outputPath), { recursive: true });
|
|
18755
19760
|
await promises.writeFile(outputPath, markdown, "utf-8");
|
|
18756
19761
|
}
|
|
18757
19762
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
18758
19763
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
18759
|
-
const summaryPath =
|
|
18760
|
-
await promises.mkdir(
|
|
19764
|
+
const summaryPath = path24__default.default.resolve(summaryTarget);
|
|
19765
|
+
await promises.mkdir(path24__default.default.dirname(summaryPath), { recursive: true });
|
|
18761
19766
|
await promises.appendFile(summaryPath, `
|
|
18762
19767
|
${markdown}`, "utf-8");
|
|
18763
19768
|
}
|
|
@@ -18905,8 +19910,8 @@ jobs:
|
|
|
18905
19910
|
}
|
|
18906
19911
|
async function planInit(options = {}) {
|
|
18907
19912
|
const framework = normalizeFramework(options.framework);
|
|
18908
|
-
const cwd =
|
|
18909
|
-
const demoPath = framework === "custom" ?
|
|
19913
|
+
const cwd = path24__default.default.resolve(options.cwd ?? process.cwd());
|
|
19914
|
+
const demoPath = framework === "custom" ? path24__default.default.join("examples", "agent-inspect-demo.mjs") : path24__default.default.join("examples", `agent-inspect-${framework}-demo.mjs`);
|
|
18910
19915
|
const candidates = [
|
|
18911
19916
|
{ rel: CONFIG_FILE, content: configTemplate(framework) },
|
|
18912
19917
|
{ rel: GITKEEP, content: "" },
|
|
@@ -18920,7 +19925,7 @@ async function planInit(options = {}) {
|
|
|
18920
19925
|
}
|
|
18921
19926
|
const files = [];
|
|
18922
19927
|
for (const candidate of candidates) {
|
|
18923
|
-
const abs =
|
|
19928
|
+
const abs = path24__default.default.join(cwd, candidate.rel);
|
|
18924
19929
|
try {
|
|
18925
19930
|
await promises.access(abs);
|
|
18926
19931
|
files.push({
|
|
@@ -18941,12 +19946,12 @@ async function writePlannedFiles(plan, cwd, options) {
|
|
|
18941
19946
|
if (entry.action === "skip") {
|
|
18942
19947
|
continue;
|
|
18943
19948
|
}
|
|
18944
|
-
const abs =
|
|
19949
|
+
const abs = path24__default.default.join(cwd, entry.path);
|
|
18945
19950
|
if (options.dryRun) {
|
|
18946
19951
|
written.push(entry.path);
|
|
18947
19952
|
continue;
|
|
18948
19953
|
}
|
|
18949
|
-
await promises.mkdir(
|
|
19954
|
+
await promises.mkdir(path24__default.default.dirname(abs), { recursive: true });
|
|
18950
19955
|
const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate() : demoTemplate(plan.framework);
|
|
18951
19956
|
await promises.writeFile(abs, content, "utf-8");
|
|
18952
19957
|
written.push(entry.path);
|
|
@@ -18954,7 +19959,7 @@ async function writePlannedFiles(plan, cwd, options) {
|
|
|
18954
19959
|
return written;
|
|
18955
19960
|
}
|
|
18956
19961
|
async function initCommand(options = {}) {
|
|
18957
|
-
const cwd =
|
|
19962
|
+
const cwd = path24__default.default.resolve(options.cwd ?? process.cwd());
|
|
18958
19963
|
try {
|
|
18959
19964
|
const plan = await planInit({ ...options, cwd });
|
|
18960
19965
|
const toWrite = plan.files.filter((file) => file.action === "create").map((f) => f.path);
|
|
@@ -19045,7 +20050,7 @@ function envCheck(name, optional = true) {
|
|
|
19045
20050
|
};
|
|
19046
20051
|
}
|
|
19047
20052
|
async function traceDirWritable(traceDir) {
|
|
19048
|
-
const resolved =
|
|
20053
|
+
const resolved = path24__default.default.resolve(traceDir);
|
|
19049
20054
|
try {
|
|
19050
20055
|
await promises.mkdir(resolved, { recursive: true });
|
|
19051
20056
|
await promises.access(resolved, promises.constants.W_OK);
|
|
@@ -19066,7 +20071,7 @@ async function traceDirWritable(traceDir) {
|
|
|
19066
20071
|
}
|
|
19067
20072
|
}
|
|
19068
20073
|
function resolvePackage(cwd, name) {
|
|
19069
|
-
const require2 = module$1.createRequire(
|
|
20074
|
+
const require2 = module$1.createRequire(path24__default.default.join(cwd, "package.json"));
|
|
19070
20075
|
try {
|
|
19071
20076
|
const pkgPath = require2.resolve(`${name}/package.json`);
|
|
19072
20077
|
const pkg = require2(pkgPath);
|
|
@@ -19077,7 +20082,7 @@ function resolvePackage(cwd, name) {
|
|
|
19077
20082
|
}
|
|
19078
20083
|
function importSmoke(cwd) {
|
|
19079
20084
|
const results = [];
|
|
19080
|
-
const require2 = module$1.createRequire(
|
|
20085
|
+
const require2 = module$1.createRequire(path24__default.default.join(cwd, "package.json"));
|
|
19081
20086
|
try {
|
|
19082
20087
|
require2.resolve("agent-inspect");
|
|
19083
20088
|
results.push({
|
|
@@ -19160,7 +20165,7 @@ function versionMismatchCheck(cwd) {
|
|
|
19160
20165
|
};
|
|
19161
20166
|
}
|
|
19162
20167
|
async function runDoctorChecks(options = {}) {
|
|
19163
|
-
const cwd =
|
|
20168
|
+
const cwd = path24__default.default.resolve(options.cwd ?? process3__default.default.cwd());
|
|
19164
20169
|
const traceDir = options.traceDir?.trim() || process3__default.default.env.AGENT_INSPECT_TRACE_DIR?.trim() || ".agent-inspect";
|
|
19165
20170
|
const checks2 = [
|
|
19166
20171
|
nodeVersionCheck(),
|
|
@@ -19274,7 +20279,7 @@ function createTraceDirectoryIndexer() {
|
|
|
19274
20279
|
init_advanced();
|
|
19275
20280
|
var INDEX_FILENAME = ".agent-inspect-index.json";
|
|
19276
20281
|
function traceIndexPath(traceDir) {
|
|
19277
|
-
return
|
|
20282
|
+
return path24__default.default.join(traceDir, INDEX_FILENAME);
|
|
19278
20283
|
}
|
|
19279
20284
|
function parseMaxEntries(raw) {
|
|
19280
20285
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
@@ -19420,7 +20425,7 @@ async function newestTraceMtimeMs2(traceDir) {
|
|
|
19420
20425
|
for (const file of files) {
|
|
19421
20426
|
if (!file.endsWith(".jsonl")) continue;
|
|
19422
20427
|
try {
|
|
19423
|
-
const s = await promises.stat(
|
|
20428
|
+
const s = await promises.stat(path24__default.default.join(traceDir, file));
|
|
19424
20429
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
19425
20430
|
} catch {
|
|
19426
20431
|
}
|
|
@@ -19675,11 +20680,11 @@ function printJson5(value) {
|
|
|
19675
20680
|
console.log(JSON.stringify(value, null, 2));
|
|
19676
20681
|
}
|
|
19677
20682
|
function resolveCwd(options) {
|
|
19678
|
-
return
|
|
20683
|
+
return path24__default.default.resolve(options.cwd ?? process.cwd());
|
|
19679
20684
|
}
|
|
19680
20685
|
async function suiteInitCommand(options = {}) {
|
|
19681
20686
|
const cwd = resolveCwd(options);
|
|
19682
|
-
const configPath =
|
|
20687
|
+
const configPath = path24__default.default.join(cwd, DEFAULT_CONFIG_FILENAME);
|
|
19683
20688
|
const template = defaultSuiteConfigTemplate();
|
|
19684
20689
|
if (options.dryRun) {
|
|
19685
20690
|
if (options.json) {
|
|
@@ -19789,13 +20794,13 @@ async function suiteListCommand(options = {}) {
|
|
|
19789
20794
|
}
|
|
19790
20795
|
async function writeSuiteArtifact(result, configOutputDir, options) {
|
|
19791
20796
|
const cwd = resolveCwd(options);
|
|
19792
|
-
const outputDir =
|
|
20797
|
+
const outputDir = path24__default.default.resolve(
|
|
19793
20798
|
cwd,
|
|
19794
20799
|
options.output ?? configOutputDir ?? DEFAULT_SUITE_ARTIFACTS_DIR
|
|
19795
20800
|
);
|
|
19796
20801
|
await promises.mkdir(outputDir, { recursive: true });
|
|
19797
20802
|
const stamp = result.startedAt.replace(/[:.]/g, "-");
|
|
19798
|
-
const filePath =
|
|
20803
|
+
const filePath = path24__default.default.join(outputDir, `${result.suiteName}-${stamp}.json`);
|
|
19799
20804
|
await promises.writeFile(filePath, `${JSON.stringify(result, null, 2)}
|
|
19800
20805
|
`, "utf-8");
|
|
19801
20806
|
return filePath;
|
|
@@ -19853,7 +20858,7 @@ async function loadSuiteResultFromInput(options) {
|
|
|
19853
20858
|
if (options.input === void 0 || options.input.trim() === "") {
|
|
19854
20859
|
throw new Error("Pass --input <suite-run.json> from a prior suite run.");
|
|
19855
20860
|
}
|
|
19856
|
-
const inputPath =
|
|
20861
|
+
const inputPath = path24__default.default.resolve(cwd, options.input.trim());
|
|
19857
20862
|
const raw = await promises.readFile(inputPath, "utf-8");
|
|
19858
20863
|
return JSON.parse(raw);
|
|
19859
20864
|
}
|
|
@@ -19878,6 +20883,217 @@ async function suiteReportCommand(options = {}) {
|
|
|
19878
20883
|
}
|
|
19879
20884
|
}
|
|
19880
20885
|
|
|
20886
|
+
// packages/cli/src/cohort.ts
|
|
20887
|
+
init_advanced();
|
|
20888
|
+
function normalizeMetrics2(raw) {
|
|
20889
|
+
const parsed = parseCohortMetricList(raw);
|
|
20890
|
+
if (parsed.length === 0) return void 0;
|
|
20891
|
+
const allowed = new Set(COHORT_METRIC_IDS);
|
|
20892
|
+
return parsed.filter(
|
|
20893
|
+
(item) => allowed.has(item)
|
|
20894
|
+
);
|
|
20895
|
+
}
|
|
20896
|
+
async function writeArtifacts(result, outputDir) {
|
|
20897
|
+
await promises.mkdir(outputDir, { recursive: true });
|
|
20898
|
+
const jsonPath = path24__default.default.join(outputDir, "cohort-results.json");
|
|
20899
|
+
const markdownPath = path24__default.default.join(outputDir, "cohort-summary.md");
|
|
20900
|
+
const htmlPath = path24__default.default.join(outputDir, "cohort-report.html");
|
|
20901
|
+
await promises.writeFile(jsonPath, `${renderCohortReport(result, { format: "json" })}
|
|
20902
|
+
`, "utf-8");
|
|
20903
|
+
await promises.writeFile(
|
|
20904
|
+
markdownPath,
|
|
20905
|
+
`${renderCohortReport(result, { format: "markdown" })}
|
|
20906
|
+
`,
|
|
20907
|
+
"utf-8"
|
|
20908
|
+
);
|
|
20909
|
+
await promises.writeFile(htmlPath, `${renderCohortReport(result, { format: "html" })}
|
|
20910
|
+
`, "utf-8");
|
|
20911
|
+
return { jsonPath, markdownPath, htmlPath };
|
|
20912
|
+
}
|
|
20913
|
+
async function cohortCommand(options = {}) {
|
|
20914
|
+
try {
|
|
20915
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
20916
|
+
const { runs } = await loadSessionRuns(traceDir);
|
|
20917
|
+
const result = await analyzeCohort(runs, {
|
|
20918
|
+
traceDir,
|
|
20919
|
+
baseline: options.baseline,
|
|
20920
|
+
candidate: options.candidate,
|
|
20921
|
+
cohortKey: options.cohortKey,
|
|
20922
|
+
groupBy: options.groupBy,
|
|
20923
|
+
metrics: normalizeMetrics2(options.metric)
|
|
20924
|
+
});
|
|
20925
|
+
const format = options.format ?? (options.json ? "json" : "markdown");
|
|
20926
|
+
let artifacts;
|
|
20927
|
+
if (options.output !== void 0 && options.output.trim() !== "") {
|
|
20928
|
+
artifacts = await writeArtifacts(result, path24__default.default.resolve(options.output.trim()));
|
|
20929
|
+
}
|
|
20930
|
+
if (options.json || format === "json") {
|
|
20931
|
+
console.log(
|
|
20932
|
+
JSON.stringify(
|
|
20933
|
+
{
|
|
20934
|
+
...result,
|
|
20935
|
+
artifacts: artifacts ?? null
|
|
20936
|
+
},
|
|
20937
|
+
null,
|
|
20938
|
+
2
|
|
20939
|
+
)
|
|
20940
|
+
);
|
|
20941
|
+
} else if (format === "html") {
|
|
20942
|
+
console.log(renderCohortReport(result, { format: "html" }));
|
|
20943
|
+
if (artifacts !== void 0) {
|
|
20944
|
+
console.error(`Artifacts written to ${options.output}`);
|
|
20945
|
+
}
|
|
20946
|
+
} else {
|
|
20947
|
+
console.log(renderCohortReport(result, { format: "markdown" }));
|
|
20948
|
+
if (artifacts !== void 0) {
|
|
20949
|
+
console.log("");
|
|
20950
|
+
console.log(`Artifacts:`);
|
|
20951
|
+
console.log(`- ${artifacts.jsonPath}`);
|
|
20952
|
+
console.log(`- ${artifacts.markdownPath}`);
|
|
20953
|
+
console.log(`- ${artifacts.htmlPath}`);
|
|
20954
|
+
}
|
|
20955
|
+
}
|
|
20956
|
+
if (!result.ok) process.exitCode = 1;
|
|
20957
|
+
} catch (error) {
|
|
20958
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20959
|
+
if (options.json) {
|
|
20960
|
+
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
20961
|
+
} else {
|
|
20962
|
+
console.error(`[AgentInspect] cohort failed: ${message}`);
|
|
20963
|
+
}
|
|
20964
|
+
process.exitCode = 1;
|
|
20965
|
+
}
|
|
20966
|
+
}
|
|
20967
|
+
|
|
20968
|
+
// packages/cli/src/gate.ts
|
|
20969
|
+
init_advanced();
|
|
20970
|
+
var SUPPORTED_FORMATS = /* @__PURE__ */ new Set(["markdown", "json", "html", "junit", "github"]);
|
|
20971
|
+
function collectList(value) {
|
|
20972
|
+
if (value === void 0) return [];
|
|
20973
|
+
const items = Array.isArray(value) ? value : [value];
|
|
20974
|
+
return items.flatMap((item) => parseGateList(item));
|
|
20975
|
+
}
|
|
20976
|
+
function parseRate(value) {
|
|
20977
|
+
if (value === void 0 || value.trim() === "") return void 0;
|
|
20978
|
+
const parsed = Number(value);
|
|
20979
|
+
if (!Number.isFinite(parsed)) {
|
|
20980
|
+
throw new Error(`Invalid --max-error-rate: ${value}`);
|
|
20981
|
+
}
|
|
20982
|
+
return parsed;
|
|
20983
|
+
}
|
|
20984
|
+
function parseDuration2(value) {
|
|
20985
|
+
if (value === void 0 || value.trim() === "") return void 0;
|
|
20986
|
+
const parsed = Number(value);
|
|
20987
|
+
if (!Number.isFinite(parsed)) {
|
|
20988
|
+
throw new Error(`Invalid --max-p95-duration: ${value}`);
|
|
20989
|
+
}
|
|
20990
|
+
return parsed;
|
|
20991
|
+
}
|
|
20992
|
+
async function writeArtifacts2(result, outputDir) {
|
|
20993
|
+
await promises.mkdir(outputDir, { recursive: true });
|
|
20994
|
+
const paths = {
|
|
20995
|
+
jsonPath: path24__default.default.join(outputDir, "gate-results.json"),
|
|
20996
|
+
markdownPath: path24__default.default.join(outputDir, "gate-summary.md"),
|
|
20997
|
+
htmlPath: path24__default.default.join(outputDir, "gate-report.html"),
|
|
20998
|
+
junitPath: path24__default.default.join(outputDir, "junit.xml"),
|
|
20999
|
+
githubPath: path24__default.default.join(outputDir, "github-step-summary.md")
|
|
21000
|
+
};
|
|
21001
|
+
await promises.writeFile(
|
|
21002
|
+
paths.jsonPath,
|
|
21003
|
+
`${renderGateReport(result, { format: "json" })}
|
|
21004
|
+
`,
|
|
21005
|
+
"utf-8"
|
|
21006
|
+
);
|
|
21007
|
+
await promises.writeFile(
|
|
21008
|
+
paths.markdownPath,
|
|
21009
|
+
`${renderGateReport(result, { format: "markdown" })}
|
|
21010
|
+
`,
|
|
21011
|
+
"utf-8"
|
|
21012
|
+
);
|
|
21013
|
+
await promises.writeFile(
|
|
21014
|
+
paths.htmlPath,
|
|
21015
|
+
`${renderGateReport(result, { format: "html" })}
|
|
21016
|
+
`,
|
|
21017
|
+
"utf-8"
|
|
21018
|
+
);
|
|
21019
|
+
await promises.writeFile(
|
|
21020
|
+
paths.junitPath,
|
|
21021
|
+
`${renderGateReport(result, { format: "junit" })}
|
|
21022
|
+
`,
|
|
21023
|
+
"utf-8"
|
|
21024
|
+
);
|
|
21025
|
+
await promises.writeFile(
|
|
21026
|
+
paths.githubPath,
|
|
21027
|
+
`${renderGateReport(result, { format: "github" })}
|
|
21028
|
+
`,
|
|
21029
|
+
"utf-8"
|
|
21030
|
+
);
|
|
21031
|
+
return paths;
|
|
21032
|
+
}
|
|
21033
|
+
async function gateCommand(options = {}) {
|
|
21034
|
+
try {
|
|
21035
|
+
const format = options.format ?? (options.json ? "json" : "markdown");
|
|
21036
|
+
if (!SUPPORTED_FORMATS.has(format)) {
|
|
21037
|
+
if (options.json) {
|
|
21038
|
+
console.log(
|
|
21039
|
+
JSON.stringify({ ok: false, exitCode: 4, error: `Unsupported format: ${format}` }, null, 2)
|
|
21040
|
+
);
|
|
21041
|
+
} else {
|
|
21042
|
+
console.error(`[AgentInspect] Unsupported format: ${format}`);
|
|
21043
|
+
}
|
|
21044
|
+
process.exitCode = 4;
|
|
21045
|
+
return;
|
|
21046
|
+
}
|
|
21047
|
+
const gateOptions = {
|
|
21048
|
+
...options.dir !== void 0 ? { traceDir: resolveTraceDir({ dir: options.dir }) } : {},
|
|
21049
|
+
...options.suite !== void 0 ? { suitePath: options.suite } : {},
|
|
21050
|
+
...options.cwd !== void 0 ? { cwd: options.cwd } : {},
|
|
21051
|
+
...parseRate(options.maxErrorRate) !== void 0 ? { maxErrorRate: parseRate(options.maxErrorRate) } : {},
|
|
21052
|
+
...parseDuration2(options.maxP95Duration) !== void 0 ? { maxP95DurationMs: parseDuration2(options.maxP95Duration) } : {},
|
|
21053
|
+
forbidTools: collectList(options.forbidTool),
|
|
21054
|
+
requireObservations: collectList(options.requireObservation)
|
|
21055
|
+
};
|
|
21056
|
+
let runs = [];
|
|
21057
|
+
const needsRuns = gateHasThresholds(gateOptions) || gateOptions.traceDir !== void 0 && gateOptions.suitePath === void 0;
|
|
21058
|
+
if (needsRuns) {
|
|
21059
|
+
const traceDir = gateOptions.traceDir ?? resolveTraceDir({ dir: options.dir });
|
|
21060
|
+
const loaded = await loadSessionRuns(traceDir);
|
|
21061
|
+
runs = loaded.runs;
|
|
21062
|
+
gateOptions.traceDir = traceDir;
|
|
21063
|
+
}
|
|
21064
|
+
const result = await runGate(runs, gateOptions);
|
|
21065
|
+
let artifacts;
|
|
21066
|
+
if (options.output !== void 0 && options.output.trim() !== "") {
|
|
21067
|
+
artifacts = await writeArtifacts2(result, path24__default.default.resolve(options.output.trim()));
|
|
21068
|
+
}
|
|
21069
|
+
if (options.json || format === "json") {
|
|
21070
|
+
console.log(JSON.stringify({ ...result, artifacts: artifacts ?? null }, null, 2));
|
|
21071
|
+
} else if (format === "html") {
|
|
21072
|
+
console.log(renderGateReport(result, { format: "html" }));
|
|
21073
|
+
} else if (format === "junit") {
|
|
21074
|
+
console.log(renderGateReport(result, { format: "junit" }));
|
|
21075
|
+
} else if (format === "github") {
|
|
21076
|
+
console.log(renderGateReport(result, { format: "github" }));
|
|
21077
|
+
} else {
|
|
21078
|
+
console.log(renderGateReport(result, { format: "markdown" }));
|
|
21079
|
+
if (artifacts !== void 0) {
|
|
21080
|
+
console.log("");
|
|
21081
|
+
console.log("Artifacts:");
|
|
21082
|
+
for (const value of Object.values(artifacts)) console.log(`- ${value}`);
|
|
21083
|
+
}
|
|
21084
|
+
}
|
|
21085
|
+
process.exitCode = result.exitCode;
|
|
21086
|
+
} catch (error) {
|
|
21087
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21088
|
+
if (options.json) {
|
|
21089
|
+
console.log(JSON.stringify({ ok: false, exitCode: 2, error: message }, null, 2));
|
|
21090
|
+
} else {
|
|
21091
|
+
console.error(`[AgentInspect] gate failed: ${message}`);
|
|
21092
|
+
}
|
|
21093
|
+
process.exitCode = 2;
|
|
21094
|
+
}
|
|
21095
|
+
}
|
|
21096
|
+
|
|
19881
21097
|
// packages/cli/src/index.ts
|
|
19882
21098
|
function runCommand(action) {
|
|
19883
21099
|
void action().catch((error) => {
|
|
@@ -20311,6 +21527,21 @@ function createCliProgram() {
|
|
|
20311
21527
|
suiteCmd.command("report").description("Render a report from a prior suite run artifact").option("--input <path>", "suite run JSON artifact").option("--format <format>", "markdown or json", "markdown").option("--json", "print JSON wrapper").action((opts) => {
|
|
20312
21528
|
runCommand(() => suiteReportCommand(opts));
|
|
20313
21529
|
});
|
|
21530
|
+
program.command("cohort").description("Compare baseline/candidate trace cohorts for regressions (v5.1+)").option("--dir <path>", "trace directory").option("--baseline <label>", "baseline cohort label (metadata key)").option("--candidate <label>", "candidate cohort label (metadata key)").option("--cohort-key <key>", "metadata key for cohort labels", "cohort").option("--group-by <spec>", "model | session | group | metadata.<key>", "model").option(
|
|
21531
|
+
"--metric <list>",
|
|
21532
|
+
"comma-separated metrics (errorRate,duration,toolChoice,...)"
|
|
21533
|
+
).option("--format <format>", "markdown, json, or html", "markdown").option("-o, --output <dir>", "write cohort-results.json, cohort-summary.md, cohort-report.html").option("--json", "print deterministic JSON result").action((opts) => {
|
|
21534
|
+
runCommand(() => cohortCommand(opts));
|
|
21535
|
+
});
|
|
21536
|
+
program.command("gate").description("Run deterministic CI quality gates over traces or suites (v5.2+)").option("--dir <path>", "trace directory for threshold checks").option("--suite <path>", "suite config path").option("--max-error-rate <percent>", "maximum allowed error rate percentage").option("--max-p95-duration <ms>", "maximum allowed p95 run duration in ms").option("--forbid-tool <name>", "forbidden tool name (repeatable or comma-separated)").option(
|
|
21537
|
+
"--require-observation <name>",
|
|
21538
|
+
"required passed observation (repeatable or comma-separated)"
|
|
21539
|
+
).option("--format <format>", "markdown, json, html, junit, or github", "markdown").option(
|
|
21540
|
+
"-o, --output <dir>",
|
|
21541
|
+
"write gate-results.json, gate-summary.md, gate-report.html, junit.xml, github-step-summary.md"
|
|
21542
|
+
).option("--json", "print deterministic JSON result").action((opts) => {
|
|
21543
|
+
runCommand(() => gateCommand(opts));
|
|
21544
|
+
});
|
|
20314
21545
|
return program;
|
|
20315
21546
|
}
|
|
20316
21547
|
function isPrimaryModule() {
|
|
@@ -20318,9 +21549,9 @@ function isPrimaryModule() {
|
|
|
20318
21549
|
if (!entry) return false;
|
|
20319
21550
|
const selfPath = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
20320
21551
|
try {
|
|
20321
|
-
return fs.realpathSync(
|
|
21552
|
+
return fs.realpathSync(path24__default.default.resolve(entry)) === fs.realpathSync(path24__default.default.resolve(selfPath));
|
|
20322
21553
|
} catch {
|
|
20323
|
-
return
|
|
21554
|
+
return path24__default.default.resolve(entry) === path24__default.default.resolve(selfPath);
|
|
20324
21555
|
}
|
|
20325
21556
|
}
|
|
20326
21557
|
if (isPrimaryModule()) {
|