agent-inspect 5.1.0 → 5.3.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 +55 -0
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-TSIQUIPF.mjs → chunk-4LKKB4Z5.mjs} +416 -28
- package/packages/cli/dist/chunk-4LKKB4Z5.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +2830 -2072
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +2430 -2103
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-2SZWGKMM.mjs → src-4SGS3Q2D.mjs} +3 -3
- package/packages/cli/dist/{src-2SZWGKMM.mjs.map → src-4SGS3Q2D.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +415 -9
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +54 -4
- package/packages/core/dist/advanced.d.ts +54 -4
- package/packages/core/dist/advanced.mjs +405 -8
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.d.cts +1 -1
- package/packages/core/dist/checks.d.ts +1 -1
- package/packages/core/dist/{context-Cp8NRbBX.d.ts → context-BJ5UQLDi.d.ts} +1 -1
- package/packages/core/dist/{context-BbfkMYjz.d.cts → context-DyKbkmd4.d.cts} +1 -1
- package/packages/core/dist/index.d.cts +2 -2
- package/packages/core/dist/index.d.ts +2 -2
- package/packages/core/dist/{types-CvsKGweN.d.cts → types-J3Xtuf9x.d.cts} +13 -1
- package/packages/core/dist/{types-CvsKGweN.d.ts → types-J3Xtuf9x.d.ts} +13 -1
- package/packages/cli/dist/chunk-TSIQUIPF.mjs.map +0 -1
|
@@ -4086,9 +4086,9 @@ async function searchTraces(metas, options) {
|
|
|
4086
4086
|
}
|
|
4087
4087
|
const limit = options.limit ?? 50;
|
|
4088
4088
|
const sessionId = options.session?.trim();
|
|
4089
|
-
const
|
|
4089
|
+
const observationStatus2 = parseObservationFilter(options.observation);
|
|
4090
4090
|
const hasContentFilter = Boolean(
|
|
4091
|
-
options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter ||
|
|
4091
|
+
options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus2
|
|
4092
4092
|
);
|
|
4093
4093
|
const results = [];
|
|
4094
4094
|
const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
|
|
@@ -4133,9 +4133,9 @@ async function searchTraces(metas, options) {
|
|
|
4133
4133
|
statusFilter: options.status
|
|
4134
4134
|
});
|
|
4135
4135
|
results.push(...stepMatches);
|
|
4136
|
-
if (
|
|
4136
|
+
if (observationStatus2) {
|
|
4137
4137
|
const outcomes = extractOutcomesFromTraceEvents(events);
|
|
4138
|
-
const matched = outcomes.filter((outcome) => outcome.status ===
|
|
4138
|
+
const matched = outcomes.filter((outcome) => outcome.status === observationStatus2);
|
|
4139
4139
|
for (const outcome of matched) {
|
|
4140
4140
|
results.push({
|
|
4141
4141
|
runId: m.runId,
|
|
@@ -4971,12 +4971,12 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
4971
4971
|
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
4972
4972
|
);
|
|
4973
4973
|
const ordered = [...runs].sort(compareRuns);
|
|
4974
|
-
const
|
|
4974
|
+
const path12 = [];
|
|
4975
4975
|
const visited = /* @__PURE__ */ new Set();
|
|
4976
4976
|
const pushRun = (run, confidence, source) => {
|
|
4977
4977
|
if (visited.has(run.runId)) return;
|
|
4978
4978
|
visited.add(run.runId);
|
|
4979
|
-
|
|
4979
|
+
path12.push({
|
|
4980
4980
|
runId: run.runId,
|
|
4981
4981
|
name: run.name,
|
|
4982
4982
|
startedAt: run.startedAt,
|
|
@@ -5001,7 +5001,7 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
5001
5001
|
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
5002
5002
|
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
5003
5003
|
}
|
|
5004
|
-
return
|
|
5004
|
+
return path12;
|
|
5005
5005
|
}
|
|
5006
5006
|
function metaRunIdMatches(run, token, runById) {
|
|
5007
5007
|
const meta = extractSessionWorkflowMetadata(run.metadata);
|
|
@@ -5824,7 +5824,7 @@ function stripPrefix(name, prefixes) {
|
|
|
5824
5824
|
}
|
|
5825
5825
|
return name;
|
|
5826
5826
|
}
|
|
5827
|
-
function eventEvidence(event,
|
|
5827
|
+
function eventEvidence(event, path12) {
|
|
5828
5828
|
return {
|
|
5829
5829
|
runId: event.runId,
|
|
5830
5830
|
eventId: event.eventId,
|
|
@@ -5834,7 +5834,7 @@ function eventEvidence(event, path11) {
|
|
|
5834
5834
|
kind: event.kind,
|
|
5835
5835
|
name: event.name,
|
|
5836
5836
|
status: event.status,
|
|
5837
|
-
...
|
|
5837
|
+
...path12 ? { path: path12 } : {}
|
|
5838
5838
|
};
|
|
5839
5839
|
}
|
|
5840
5840
|
function runEvidence(run) {
|
|
@@ -8967,6 +8967,402 @@ function renderCohortReport(result, options = {}) {
|
|
|
8967
8967
|
return renderCohortSummaryMarkdown(result);
|
|
8968
8968
|
}
|
|
8969
8969
|
|
|
8970
|
+
// packages/core/src/gate/parse.ts
|
|
8971
|
+
function parseGateList(value) {
|
|
8972
|
+
if (value === void 0 || value.trim() === "") return [];
|
|
8973
|
+
return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
8974
|
+
}
|
|
8975
|
+
function parseGateNumber(value, label) {
|
|
8976
|
+
if (value === void 0 || value.trim() === "") return void 0;
|
|
8977
|
+
const parsed = Number(value);
|
|
8978
|
+
if (!Number.isFinite(parsed)) {
|
|
8979
|
+
throw new Error(`Invalid ${label}: ${value}`);
|
|
8980
|
+
}
|
|
8981
|
+
return parsed;
|
|
8982
|
+
}
|
|
8983
|
+
|
|
8984
|
+
// packages/core/src/gate/evaluate.ts
|
|
8985
|
+
function percentile3(values, p) {
|
|
8986
|
+
if (values.length === 0) return void 0;
|
|
8987
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
8988
|
+
const idx = Math.min(
|
|
8989
|
+
sorted.length - 1,
|
|
8990
|
+
Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
|
|
8991
|
+
);
|
|
8992
|
+
return sorted[idx];
|
|
8993
|
+
}
|
|
8994
|
+
function hasThresholds(options) {
|
|
8995
|
+
return options.maxErrorRate !== void 0 || options.maxP95DurationMs !== void 0 || (options.forbidTools?.length ?? 0) > 0 || (options.requireObservations?.length ?? 0) > 0;
|
|
8996
|
+
}
|
|
8997
|
+
function gateHasThresholds(options) {
|
|
8998
|
+
return hasThresholds(options);
|
|
8999
|
+
}
|
|
9000
|
+
async function loadRunMetrics(runs) {
|
|
9001
|
+
const metrics = [];
|
|
9002
|
+
for (const run of runs) {
|
|
9003
|
+
if (run.filePath === void 0) continue;
|
|
9004
|
+
metrics.push(
|
|
9005
|
+
await computeCohortRunMetrics({
|
|
9006
|
+
runId: run.runId,
|
|
9007
|
+
filePath: run.filePath,
|
|
9008
|
+
metadata: run.metadata,
|
|
9009
|
+
status: run.status,
|
|
9010
|
+
durationMs: run.durationMs,
|
|
9011
|
+
groupKey: "all"
|
|
9012
|
+
})
|
|
9013
|
+
);
|
|
9014
|
+
}
|
|
9015
|
+
return metrics;
|
|
9016
|
+
}
|
|
9017
|
+
async function observationStatus(filePath, name) {
|
|
9018
|
+
const events = await readTraceEventsFromFile(filePath);
|
|
9019
|
+
const outcomes = extractOutcomesFromTraceEvents(events);
|
|
9020
|
+
const match = outcomes.find((item) => item.name === name);
|
|
9021
|
+
if (!match) return "missing";
|
|
9022
|
+
return match.status === "passed" ? "passed" : "failed";
|
|
9023
|
+
}
|
|
9024
|
+
async function evaluateGateThresholds(runs, options) {
|
|
9025
|
+
const checks = [];
|
|
9026
|
+
const readErrors = [];
|
|
9027
|
+
if (!hasThresholds(options)) {
|
|
9028
|
+
return { checks, readErrors };
|
|
9029
|
+
}
|
|
9030
|
+
if (runs.length === 0) {
|
|
9031
|
+
readErrors.push("No trace runs found in the gate directory.");
|
|
9032
|
+
return { checks, readErrors };
|
|
9033
|
+
}
|
|
9034
|
+
let runMetrics;
|
|
9035
|
+
try {
|
|
9036
|
+
runMetrics = await loadRunMetrics(runs);
|
|
9037
|
+
} catch (error) {
|
|
9038
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9039
|
+
readErrors.push(message);
|
|
9040
|
+
return { checks, readErrors };
|
|
9041
|
+
}
|
|
9042
|
+
if (options.maxErrorRate !== void 0) {
|
|
9043
|
+
const errors = runMetrics.filter((run) => run.error).length;
|
|
9044
|
+
const actual = runMetrics.length > 0 ? errors / runMetrics.length * 100 : 0;
|
|
9045
|
+
const ok = actual <= options.maxErrorRate;
|
|
9046
|
+
checks.push({
|
|
9047
|
+
id: "maxErrorRate",
|
|
9048
|
+
name: "Max error rate",
|
|
9049
|
+
ok,
|
|
9050
|
+
expected: options.maxErrorRate,
|
|
9051
|
+
actual: Math.round(actual * 10) / 10,
|
|
9052
|
+
message: ok ? `Error rate ${actual.toFixed(1)}% within limit ${options.maxErrorRate}%` : `Error rate ${actual.toFixed(1)}% exceeds limit ${options.maxErrorRate}%`
|
|
9053
|
+
});
|
|
9054
|
+
}
|
|
9055
|
+
if (options.maxP95DurationMs !== void 0) {
|
|
9056
|
+
const durations = runMetrics.map((run) => run.durationMs).filter((value) => typeof value === "number");
|
|
9057
|
+
const actual = percentile3(durations, 95);
|
|
9058
|
+
const ok = actual !== void 0 && actual <= options.maxP95DurationMs;
|
|
9059
|
+
checks.push({
|
|
9060
|
+
id: "maxP95Duration",
|
|
9061
|
+
name: "Max p95 duration (ms)",
|
|
9062
|
+
ok,
|
|
9063
|
+
expected: options.maxP95DurationMs,
|
|
9064
|
+
actual: actual ?? "n/a",
|
|
9065
|
+
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`
|
|
9066
|
+
});
|
|
9067
|
+
}
|
|
9068
|
+
for (const tool of options.forbidTools ?? []) {
|
|
9069
|
+
let violated = false;
|
|
9070
|
+
for (const run of runMetrics) {
|
|
9071
|
+
const used = run.toolChoices.includes(tool) || run.toolOrdering.includes(tool);
|
|
9072
|
+
if (used) {
|
|
9073
|
+
violated = true;
|
|
9074
|
+
checks.push({
|
|
9075
|
+
id: "forbidTool",
|
|
9076
|
+
name: `Forbid tool: ${tool}`,
|
|
9077
|
+
ok: false,
|
|
9078
|
+
expected: `not used`,
|
|
9079
|
+
actual: "used",
|
|
9080
|
+
runId: run.runId,
|
|
9081
|
+
message: `Forbidden tool "${tool}" used in run ${run.runId}`
|
|
9082
|
+
});
|
|
9083
|
+
}
|
|
9084
|
+
}
|
|
9085
|
+
if (!violated) {
|
|
9086
|
+
checks.push({
|
|
9087
|
+
id: "forbidTool",
|
|
9088
|
+
name: `Forbid tool: ${tool}`,
|
|
9089
|
+
ok: true,
|
|
9090
|
+
message: `Forbidden tool "${tool}" not used`
|
|
9091
|
+
});
|
|
9092
|
+
}
|
|
9093
|
+
}
|
|
9094
|
+
for (const observation of options.requireObservations ?? []) {
|
|
9095
|
+
for (const run of runs) {
|
|
9096
|
+
if (run.filePath === void 0) continue;
|
|
9097
|
+
try {
|
|
9098
|
+
const status = await observationStatus(run.filePath, observation);
|
|
9099
|
+
const ok = status === "passed";
|
|
9100
|
+
checks.push({
|
|
9101
|
+
id: "requireObservation",
|
|
9102
|
+
name: `Require observation: ${observation}`,
|
|
9103
|
+
ok,
|
|
9104
|
+
expected: "passed",
|
|
9105
|
+
actual: status,
|
|
9106
|
+
runId: run.runId,
|
|
9107
|
+
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}`
|
|
9108
|
+
});
|
|
9109
|
+
} catch (error) {
|
|
9110
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9111
|
+
readErrors.push(`Run ${run.runId}: ${message}`);
|
|
9112
|
+
}
|
|
9113
|
+
}
|
|
9114
|
+
}
|
|
9115
|
+
return { checks, readErrors };
|
|
9116
|
+
}
|
|
9117
|
+
function checksFromSuiteResult(suiteResult) {
|
|
9118
|
+
const checks = [
|
|
9119
|
+
{
|
|
9120
|
+
id: "suite",
|
|
9121
|
+
name: `Suite: ${suiteResult.suiteName}`,
|
|
9122
|
+
ok: suiteResult.ok,
|
|
9123
|
+
message: suiteResult.ok ? `Suite passed (${suiteResult.summary.passed} cases)` : `Suite failed (${suiteResult.summary.failed} failed, ${suiteResult.summary.errors} errors)`
|
|
9124
|
+
}
|
|
9125
|
+
];
|
|
9126
|
+
for (const suiteCase of suiteResult.cases) {
|
|
9127
|
+
if (suiteCase.status === "pass") continue;
|
|
9128
|
+
checks.push({
|
|
9129
|
+
id: "suite",
|
|
9130
|
+
name: `Case: ${suiteCase.id}`,
|
|
9131
|
+
ok: false,
|
|
9132
|
+
message: suiteCase.message ?? `Case status: ${suiteCase.status}`
|
|
9133
|
+
});
|
|
9134
|
+
}
|
|
9135
|
+
return checks;
|
|
9136
|
+
}
|
|
9137
|
+
function resolveExitCode(input) {
|
|
9138
|
+
if (input.configError) return 2;
|
|
9139
|
+
if (input.readError) return 3;
|
|
9140
|
+
if (!input.ok) return 1;
|
|
9141
|
+
return 0;
|
|
9142
|
+
}
|
|
9143
|
+
function validateOptions(options) {
|
|
9144
|
+
const errors = [];
|
|
9145
|
+
const hasSuite = options.suitePath !== void 0 && options.suitePath.trim() !== "";
|
|
9146
|
+
const hasThresholds2 = gateHasThresholds(options);
|
|
9147
|
+
if (!hasSuite && !hasThresholds2) {
|
|
9148
|
+
errors.push(
|
|
9149
|
+
"No gate rules specified. Pass --suite or at least one threshold flag."
|
|
9150
|
+
);
|
|
9151
|
+
}
|
|
9152
|
+
if (hasThresholds2 && (options.traceDir === void 0 || options.traceDir.trim() === "")) {
|
|
9153
|
+
if (!hasSuite) {
|
|
9154
|
+
errors.push("Threshold flags require --dir <trace-directory>.");
|
|
9155
|
+
}
|
|
9156
|
+
}
|
|
9157
|
+
if (options.maxErrorRate !== void 0 && options.maxErrorRate < 0) {
|
|
9158
|
+
errors.push("--max-error-rate must be a non-negative percentage.");
|
|
9159
|
+
}
|
|
9160
|
+
if (options.maxP95DurationMs !== void 0 && options.maxP95DurationMs < 0) {
|
|
9161
|
+
errors.push("--max-p95-duration must be a non-negative millisecond value.");
|
|
9162
|
+
}
|
|
9163
|
+
return errors;
|
|
9164
|
+
}
|
|
9165
|
+
function isConfigLoadError(error) {
|
|
9166
|
+
if (!(error instanceof Error)) return false;
|
|
9167
|
+
const ext = path__default.default.extname(error.message);
|
|
9168
|
+
if (error.message.includes("Unsupported suite config extension")) return true;
|
|
9169
|
+
if (error.message.includes("TypeScript suite configs require")) return true;
|
|
9170
|
+
if (error.message.includes("No suite config found")) return true;
|
|
9171
|
+
if (error.message.includes("AI_SUITE_CONFIG")) return true;
|
|
9172
|
+
if (ext === ".ts" || ext === ".mts" || ext === ".cts") return true;
|
|
9173
|
+
return "diagnostics" in error;
|
|
9174
|
+
}
|
|
9175
|
+
async function runGate(runs, options) {
|
|
9176
|
+
const diagnostics = [];
|
|
9177
|
+
const checks = [];
|
|
9178
|
+
const validationErrors = validateOptions(options);
|
|
9179
|
+
if (validationErrors.length > 0) {
|
|
9180
|
+
return {
|
|
9181
|
+
ok: false,
|
|
9182
|
+
exitCode: 2,
|
|
9183
|
+
runCount: 0,
|
|
9184
|
+
checks,
|
|
9185
|
+
diagnostics: validationErrors
|
|
9186
|
+
};
|
|
9187
|
+
}
|
|
9188
|
+
let traceDir = options.traceDir?.trim();
|
|
9189
|
+
let suiteResult;
|
|
9190
|
+
if (options.suitePath !== void 0 && options.suitePath.trim() !== "") {
|
|
9191
|
+
try {
|
|
9192
|
+
suiteResult = await runSuite({
|
|
9193
|
+
configPath: options.suitePath,
|
|
9194
|
+
cwd: options.cwd
|
|
9195
|
+
});
|
|
9196
|
+
traceDir = traceDir ?? suiteResult.tracesDir;
|
|
9197
|
+
checks.push(...checksFromSuiteResult(suiteResult));
|
|
9198
|
+
} catch (error) {
|
|
9199
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9200
|
+
diagnostics.push(message);
|
|
9201
|
+
return {
|
|
9202
|
+
ok: false,
|
|
9203
|
+
exitCode: isConfigLoadError(error) ? 2 : 3,
|
|
9204
|
+
traceDir,
|
|
9205
|
+
suitePath: options.suitePath,
|
|
9206
|
+
runCount: 0,
|
|
9207
|
+
checks,
|
|
9208
|
+
diagnostics
|
|
9209
|
+
};
|
|
9210
|
+
}
|
|
9211
|
+
}
|
|
9212
|
+
if (gateHasThresholds(options)) {
|
|
9213
|
+
const thresholdDir = traceDir;
|
|
9214
|
+
if (thresholdDir === void 0 || thresholdDir.trim() === "") {
|
|
9215
|
+
return {
|
|
9216
|
+
ok: false,
|
|
9217
|
+
exitCode: 2,
|
|
9218
|
+
traceDir,
|
|
9219
|
+
suitePath: options.suitePath,
|
|
9220
|
+
runCount: runs.length,
|
|
9221
|
+
checks,
|
|
9222
|
+
diagnostics: ["Threshold evaluation requires a trace directory."],
|
|
9223
|
+
...suiteResult !== void 0 ? { suiteResult } : {}
|
|
9224
|
+
};
|
|
9225
|
+
}
|
|
9226
|
+
const thresholdRuns = runs.length > 0 ? runs : [];
|
|
9227
|
+
const { checks: thresholdChecks, readErrors } = await evaluateGateThresholds(
|
|
9228
|
+
thresholdRuns,
|
|
9229
|
+
options
|
|
9230
|
+
);
|
|
9231
|
+
checks.push(...thresholdChecks);
|
|
9232
|
+
diagnostics.push(...readErrors);
|
|
9233
|
+
if (readErrors.length > 0) {
|
|
9234
|
+
const ok2 = checks.length > 0 && checks.every((item) => item.ok);
|
|
9235
|
+
return {
|
|
9236
|
+
ok: ok2,
|
|
9237
|
+
exitCode: resolveExitCode({
|
|
9238
|
+
ok: ok2,
|
|
9239
|
+
configError: false,
|
|
9240
|
+
readError: true
|
|
9241
|
+
}),
|
|
9242
|
+
traceDir: thresholdDir,
|
|
9243
|
+
suitePath: options.suitePath,
|
|
9244
|
+
runCount: thresholdRuns.length,
|
|
9245
|
+
checks,
|
|
9246
|
+
diagnostics,
|
|
9247
|
+
...suiteResult !== void 0 ? { suiteResult } : {}
|
|
9248
|
+
};
|
|
9249
|
+
}
|
|
9250
|
+
}
|
|
9251
|
+
const ok = checks.length > 0 && checks.every((item) => item.ok);
|
|
9252
|
+
return {
|
|
9253
|
+
ok,
|
|
9254
|
+
exitCode: resolveExitCode({ ok, configError: false, readError: false }),
|
|
9255
|
+
traceDir,
|
|
9256
|
+
suitePath: options.suitePath,
|
|
9257
|
+
runCount: runs.length,
|
|
9258
|
+
checks,
|
|
9259
|
+
diagnostics,
|
|
9260
|
+
...suiteResult !== void 0 ? { suiteResult } : {}
|
|
9261
|
+
};
|
|
9262
|
+
}
|
|
9263
|
+
|
|
9264
|
+
// packages/core/src/gate/render.ts
|
|
9265
|
+
function escapeXml(value) {
|
|
9266
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
9267
|
+
}
|
|
9268
|
+
function renderGateSummaryMarkdown(result) {
|
|
9269
|
+
const lines = [];
|
|
9270
|
+
lines.push("# AgentInspect gate");
|
|
9271
|
+
lines.push("");
|
|
9272
|
+
lines.push(`Status: **${result.ok ? "PASS" : "FAIL"}** (exit ${result.exitCode})`);
|
|
9273
|
+
if (result.traceDir !== void 0) {
|
|
9274
|
+
lines.push(`Trace directory: \`${result.traceDir}\``);
|
|
9275
|
+
}
|
|
9276
|
+
if (result.suitePath !== void 0) {
|
|
9277
|
+
lines.push(`Suite config: \`${result.suitePath}\``);
|
|
9278
|
+
}
|
|
9279
|
+
lines.push(`Runs evaluated: ${result.runCount}`);
|
|
9280
|
+
lines.push("");
|
|
9281
|
+
if (result.diagnostics.length > 0) {
|
|
9282
|
+
lines.push("## Diagnostics");
|
|
9283
|
+
for (const item of result.diagnostics) lines.push(`- ${item}`);
|
|
9284
|
+
lines.push("");
|
|
9285
|
+
}
|
|
9286
|
+
lines.push("## Checks");
|
|
9287
|
+
for (const check of result.checks) {
|
|
9288
|
+
const flag = check.ok ? "PASS" : "FAIL";
|
|
9289
|
+
lines.push(`- [${flag}] ${check.name}: ${check.message}`);
|
|
9290
|
+
}
|
|
9291
|
+
lines.push("");
|
|
9292
|
+
return lines.join("\n").trimEnd();
|
|
9293
|
+
}
|
|
9294
|
+
function renderGateGithubStepSummary(result) {
|
|
9295
|
+
const lines = [];
|
|
9296
|
+
lines.push(`## AgentInspect gate: ${result.ok ? "PASS" : "FAIL"}`);
|
|
9297
|
+
lines.push("");
|
|
9298
|
+
lines.push("| Check | Status | Details |");
|
|
9299
|
+
lines.push("| --- | --- | --- |");
|
|
9300
|
+
for (const check of result.checks) {
|
|
9301
|
+
lines.push(
|
|
9302
|
+
`| ${check.name} | ${check.ok ? "pass" : "fail"} | ${check.message.replace(/\|/g, "/")} |`
|
|
9303
|
+
);
|
|
9304
|
+
}
|
|
9305
|
+
if (result.diagnostics.length > 0) {
|
|
9306
|
+
lines.push("");
|
|
9307
|
+
lines.push("**Diagnostics**");
|
|
9308
|
+
for (const item of result.diagnostics) lines.push(`- ${item}`);
|
|
9309
|
+
}
|
|
9310
|
+
return lines.join("\n").trimEnd();
|
|
9311
|
+
}
|
|
9312
|
+
function renderGateReportHtml(result) {
|
|
9313
|
+
const rows = result.checks.map(
|
|
9314
|
+
(check) => `<tr><td>${escapeHtml(check.name)}</td><td>${check.ok ? "PASS" : "FAIL"}</td><td>${escapeHtml(check.message)}</td></tr>`
|
|
9315
|
+
).join("");
|
|
9316
|
+
return `<!DOCTYPE html>
|
|
9317
|
+
<html lang="en">
|
|
9318
|
+
<head>
|
|
9319
|
+
<meta charset="utf-8" />
|
|
9320
|
+
<title>Gate report</title>
|
|
9321
|
+
<style>
|
|
9322
|
+
body { font-family: system-ui, sans-serif; margin: 2rem; }
|
|
9323
|
+
table { border-collapse: collapse; width: 100%; }
|
|
9324
|
+
th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
|
|
9325
|
+
th { background: #f6f6f6; }
|
|
9326
|
+
</style>
|
|
9327
|
+
</head>
|
|
9328
|
+
<body>
|
|
9329
|
+
<h1>AgentInspect gate</h1>
|
|
9330
|
+
<p>Status: <strong>${result.ok ? "PASS" : "FAIL"}</strong> (exit ${result.exitCode})</p>
|
|
9331
|
+
<h2>Checks</h2>
|
|
9332
|
+
<table>
|
|
9333
|
+
<thead><tr><th>Check</th><th>Status</th><th>Details</th></tr></thead>
|
|
9334
|
+
<tbody>${rows}</tbody>
|
|
9335
|
+
</table>
|
|
9336
|
+
</body>
|
|
9337
|
+
</html>`;
|
|
9338
|
+
}
|
|
9339
|
+
function renderGateJUnit(result) {
|
|
9340
|
+
const failures = result.checks.filter((check) => !check.ok).length;
|
|
9341
|
+
const tests = result.checks.length;
|
|
9342
|
+
const cases = result.checks.map((check) => {
|
|
9343
|
+
if (check.ok) {
|
|
9344
|
+
return ` <testcase name="${escapeXml(check.name)}" classname="gate" />`;
|
|
9345
|
+
}
|
|
9346
|
+
return ` <testcase name="${escapeXml(check.name)}" classname="gate">
|
|
9347
|
+
<failure message="${escapeXml(check.message)}">${escapeXml(check.message)}</failure>
|
|
9348
|
+
</testcase>`;
|
|
9349
|
+
}).join("\n");
|
|
9350
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
9351
|
+
<testsuites tests="${tests}" failures="${failures}" errors="0" time="0">
|
|
9352
|
+
<testsuite name="agent-inspect-gate" tests="${tests}" failures="${failures}" errors="0" time="0">
|
|
9353
|
+
${cases}
|
|
9354
|
+
</testsuite>
|
|
9355
|
+
</testsuites>`;
|
|
9356
|
+
}
|
|
9357
|
+
function renderGateReport(result, options = {}) {
|
|
9358
|
+
const format = options.format ?? "markdown";
|
|
9359
|
+
if (format === "json") return JSON.stringify(result, null, 2);
|
|
9360
|
+
if (format === "html") return renderGateReportHtml(result);
|
|
9361
|
+
if (format === "junit") return renderGateJUnit(result);
|
|
9362
|
+
if (format === "github") return renderGateGithubStepSummary(result);
|
|
9363
|
+
return renderGateSummaryMarkdown(result);
|
|
9364
|
+
}
|
|
9365
|
+
|
|
8970
9366
|
// packages/core/src/inspect-run.ts
|
|
8971
9367
|
function normalizeRunName(name) {
|
|
8972
9368
|
if (typeof name !== "string" || name.trim() === "") {
|
|
@@ -9138,6 +9534,8 @@ exports.enrichSessionRunRecord = enrichSessionRunRecord;
|
|
|
9138
9534
|
exports.enrichSessionSummary = enrichSessionSummary;
|
|
9139
9535
|
exports.ensureTraceDir = ensureTraceDir;
|
|
9140
9536
|
exports.extractMetadata = extractMetadata;
|
|
9537
|
+
exports.extractOutcomesFromPersistedEvents = extractOutcomesFromPersistedEvents;
|
|
9538
|
+
exports.extractOutcomesFromTraceEvents = extractOutcomesFromTraceEvents;
|
|
9141
9539
|
exports.extractSessionWorkflowMetadata = extractSessionWorkflowMetadata;
|
|
9142
9540
|
exports.filterMetasBySessionScope = filterMetasBySessionScope;
|
|
9143
9541
|
exports.filterTraces = filterTraces;
|
|
@@ -9145,6 +9543,7 @@ exports.formatDuration = formatDuration2;
|
|
|
9145
9543
|
exports.formatError = formatError;
|
|
9146
9544
|
exports.formatTerminalName = formatTerminalName;
|
|
9147
9545
|
exports.formatTimestamp = formatTimestamp;
|
|
9546
|
+
exports.gateHasThresholds = gateHasThresholds;
|
|
9148
9547
|
exports.getCurrentContext = getCurrentContext;
|
|
9149
9548
|
exports.getCurrentCorrelationMetadata = getCurrentCorrelationMetadata;
|
|
9150
9549
|
exports.getCurrentDepth = getCurrentDepth;
|
|
@@ -9177,6 +9576,8 @@ exports.normalizeSuiteConfig = normalizeSuiteConfig;
|
|
|
9177
9576
|
exports.parseCohortMetricList = parseCohortMetricList;
|
|
9178
9577
|
exports.parseDuration = parseDuration;
|
|
9179
9578
|
exports.parseDurationFilter = parseDurationFilter;
|
|
9579
|
+
exports.parseGateList = parseGateList;
|
|
9580
|
+
exports.parseGateNumber = parseGateNumber;
|
|
9180
9581
|
exports.parseGroupBySpec = parseGroupBySpec;
|
|
9181
9582
|
exports.parseTraceJsonl = parseTraceJsonl;
|
|
9182
9583
|
exports.prepareMetadataForDisk = prepareMetadataForDisk;
|
|
@@ -9193,6 +9594,10 @@ exports.renderActivitySummaryHuman = renderActivitySummaryHuman;
|
|
|
9193
9594
|
exports.renderCohortReport = renderCohortReport;
|
|
9194
9595
|
exports.renderCohortSummaryMarkdown = renderCohortSummaryMarkdown;
|
|
9195
9596
|
exports.renderErrorLine = renderErrorLine;
|
|
9597
|
+
exports.renderGateGithubStepSummary = renderGateGithubStepSummary;
|
|
9598
|
+
exports.renderGateJUnit = renderGateJUnit;
|
|
9599
|
+
exports.renderGateReport = renderGateReport;
|
|
9600
|
+
exports.renderGateSummaryMarkdown = renderGateSummaryMarkdown;
|
|
9196
9601
|
exports.renderRunSummary = renderRunSummary;
|
|
9197
9602
|
exports.renderRunWhat = renderRunWhat;
|
|
9198
9603
|
exports.renderStepLine = renderStepLine;
|
|
@@ -9206,6 +9611,7 @@ exports.resolveSuiteCaseTrace = resolveSuiteCaseTrace;
|
|
|
9206
9611
|
exports.resolveSuiteConfigPath = resolveSuiteConfigPath;
|
|
9207
9612
|
exports.resolveTraceDir = resolveTraceDir;
|
|
9208
9613
|
exports.resolveTraceSafetyOptions = resolveTraceSafetyOptions;
|
|
9614
|
+
exports.runGate = runGate;
|
|
9209
9615
|
exports.runSuite = runSuite;
|
|
9210
9616
|
exports.runWithContext = runWithContext;
|
|
9211
9617
|
exports.runWithStepContext = runWithStepContext;
|