agent-inspect 6.9.0 → 6.11.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/README.md +1 -1
- package/docs/BUNDLES.md +31 -9
- package/docs/CI-ARTIFACTS.md +10 -1
- package/docs/CLI.md +22 -2
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-WQ5XMFH2.mjs → chunk-XTAA733P.mjs} +1756 -149
- package/packages/cli/dist/chunk-XTAA733P.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +10634 -8880
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +729 -708
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-C4EUHTER.mjs → src-G7PM24W2.mjs} +3 -3
- package/packages/cli/dist/{src-C4EUHTER.mjs.map → src-G7PM24W2.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +1846 -12
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +373 -4
- package/packages/core/dist/advanced.d.ts +373 -4
- package/packages/core/dist/advanced.mjs +1307 -12
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/chunk-F7STQ5JF.mjs +479 -0
- package/packages/core/dist/chunk-F7STQ5JF.mjs.map +1 -0
- package/packages/core/dist/{chunk-KDDU2R5P.mjs → chunk-LFWZEO2L.mjs} +2 -2
- package/packages/core/dist/{chunk-KDDU2R5P.mjs.map → chunk-LFWZEO2L.mjs.map} +1 -1
- package/packages/core/dist/diff.mjs +3 -477
- package/packages/core/dist/diff.mjs.map +1 -1
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.mjs +1 -1
- package/packages/core/dist/reporters.cjs +25 -0
- package/packages/core/dist/reporters.cjs.map +1 -1
- package/packages/core/dist/reporters.d.cts +16 -2
- package/packages/core/dist/reporters.d.ts +16 -2
- package/packages/core/dist/reporters.mjs +24 -1
- package/packages/core/dist/reporters.mjs.map +1 -1
- package/packages/cli/dist/chunk-WQ5XMFH2.mjs.map +0 -1
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, enrichSessionRunRecord, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords,
|
|
2
|
+
import { createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, resolveBundleRunIds, getTraceFilePath, openTrace, sha256Hex, collectTraceSchemaVersions, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceHtmlShell, buildEvidenceProvenanceViewHtml, EVIDENCE_FORMAT_VERSION, inferEvidenceFileRole, buildEvidenceSafetyViewHtml, buildEvidenceDiffViewHtml, buildEvidenceCircuitViewHtml, buildEvidenceOutcomesViewHtml, buildEvidenceContractsViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceCausalFailureViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceTreeViewHtml, buildEvidenceManifest, serializeEvidenceManifest, buildZipArchive, assertBundlePathContained, enrichSessionRunRecord, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, runTraceChecks, assertEvidenceRelativePath, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, TraceReadError, resolveRedactionProfile, applyProfileMetadataCaps, Redactor, flattenTree, escapeHtml, safeString, compactAttributes, stableJson, escapeMarkdown, truncateStringForProfile, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, isAgentInspectTrace, filterMetasBySessionScope, aggregateSessionCheckResults, createBaselineRegressionRule, buildEvidenceCiPackage, verifyEvidenceDirectory, diffTraceEvents, renderRunDiff, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, resolveSuiteTemplate, defaultSuiteConfigTemplate, loadSuiteConfig, validateSuiteConfig, runSuite, renderSuiteReport, analyzeCohort, renderCohortReport, gateHasThresholds, runGate, renderGateReport, persistedInspectEventsToTraceEvents, renderStepLine, renderErrorLine, getIndent, TreeBuilder, zeroKinds, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, diffRuns, manualTraceEventsToComparableRun } from './chunk-XTAA733P.mjs';
|
|
3
3
|
import { realpathSync, constants as constants$1, existsSync, readFileSync } from 'fs';
|
|
4
|
-
import
|
|
4
|
+
import path16 from 'path';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
6
6
|
import { Command, Option } from 'commander';
|
|
7
|
-
import {
|
|
7
|
+
import { readFile, stat, mkdir, writeFile, unlink, appendFile, rm, access, open, readdir, constants } from 'fs/promises';
|
|
8
8
|
import process2, { stdin, stdout } from 'process';
|
|
9
9
|
import crypto, { createHash } from 'crypto';
|
|
10
10
|
import { createServer } from 'http';
|
|
11
11
|
import { createRequire } from 'module';
|
|
12
12
|
|
|
13
13
|
// package.json
|
|
14
|
-
var version = "6.
|
|
14
|
+
var version = "6.11.0";
|
|
15
15
|
|
|
16
16
|
// packages/cli/src/trace-dir-scale.ts
|
|
17
17
|
var TRACE_COUNT_WARN = 1e3;
|
|
@@ -2971,9 +2971,9 @@ Trace directory: ${traceDir}`);
|
|
|
2971
2971
|
process.exitCode = 1;
|
|
2972
2972
|
}
|
|
2973
2973
|
const resolvedOutput = resolveOutputOption(options);
|
|
2974
|
-
const outPath = resolvedOutput !== void 0 ?
|
|
2974
|
+
const outPath = resolvedOutput !== void 0 ? path16.resolve(resolvedOutput) : void 0;
|
|
2975
2975
|
if (outPath !== void 0) {
|
|
2976
|
-
await mkdir(
|
|
2976
|
+
await mkdir(path16.dirname(outPath), { recursive: true });
|
|
2977
2977
|
await writeFile(outPath, result.content, "utf-8");
|
|
2978
2978
|
const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
|
|
2979
2979
|
console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
|
|
@@ -3011,471 +3011,6 @@ Trace directory: ${traceDir}`);
|
|
|
3011
3011
|
}
|
|
3012
3012
|
}
|
|
3013
3013
|
|
|
3014
|
-
// packages/core/src/diff/comparable.ts
|
|
3015
|
-
function extractOutputPreview(meta) {
|
|
3016
|
-
if (meta === void 0) return void 0;
|
|
3017
|
-
if ("outputPreview" in meta) return meta.outputPreview;
|
|
3018
|
-
if ("resultPreview" in meta) return meta.resultPreview;
|
|
3019
|
-
return void 0;
|
|
3020
|
-
}
|
|
3021
|
-
function mapStepStatus2(s) {
|
|
3022
|
-
if (s === void 0) return "running";
|
|
3023
|
-
return s;
|
|
3024
|
-
}
|
|
3025
|
-
function manualTraceEventsToComparableRun(events) {
|
|
3026
|
-
const started = events.find((e) => e.event === "run_started");
|
|
3027
|
-
if (!started || started.event !== "run_started") {
|
|
3028
|
-
throw new Error("Invalid trace: missing run_started");
|
|
3029
|
-
}
|
|
3030
|
-
const rs = started;
|
|
3031
|
-
const runId = rs.runId;
|
|
3032
|
-
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
3033
|
-
const lastCompleted = completedAll[completedAll.length - 1];
|
|
3034
|
-
let runStatus;
|
|
3035
|
-
if (lastCompleted === void 0) runStatus = "running";
|
|
3036
|
-
else runStatus = lastCompleted.status;
|
|
3037
|
-
const durationMs2 = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
3038
|
-
const steps = /* @__PURE__ */ new Map();
|
|
3039
|
-
let order = 0;
|
|
3040
|
-
for (const e of events) {
|
|
3041
|
-
if (e.event !== "step_started") continue;
|
|
3042
|
-
const s = e;
|
|
3043
|
-
const meta = s.metadata ? { ...s.metadata } : void 0;
|
|
3044
|
-
steps.set(s.stepId, {
|
|
3045
|
-
id: s.stepId,
|
|
3046
|
-
parentId: s.parentId,
|
|
3047
|
-
name: s.name,
|
|
3048
|
-
type: s.type,
|
|
3049
|
-
order: order++,
|
|
3050
|
-
timestamp: s.timestamp,
|
|
3051
|
-
metadata: meta
|
|
3052
|
-
});
|
|
3053
|
-
}
|
|
3054
|
-
for (const e of events) {
|
|
3055
|
-
if (e.event !== "step_completed") continue;
|
|
3056
|
-
const acc = steps.get(e.stepId);
|
|
3057
|
-
if (!acc) continue;
|
|
3058
|
-
acc.status = e.status;
|
|
3059
|
-
acc.durationMs = e.durationMs;
|
|
3060
|
-
if (e.error?.message) acc.errorMsg = e.error.message;
|
|
3061
|
-
const extra = e;
|
|
3062
|
-
if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
|
|
3063
|
-
acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
|
|
3064
|
-
}
|
|
3065
|
-
}
|
|
3066
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
3067
|
-
for (const acc of steps.values()) {
|
|
3068
|
-
let meta = acc.metadata ? { ...acc.metadata } : void 0;
|
|
3069
|
-
if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
|
|
3070
|
-
meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
|
|
3071
|
-
}
|
|
3072
|
-
const outputPreview = extractOutputPreview(meta);
|
|
3073
|
-
if (meta !== void 0 && ("outputPreview" in meta || "resultPreview" in meta)) {
|
|
3074
|
-
delete meta.outputPreview;
|
|
3075
|
-
delete meta.resultPreview;
|
|
3076
|
-
}
|
|
3077
|
-
const sc = {
|
|
3078
|
-
id: acc.id,
|
|
3079
|
-
name: acc.name,
|
|
3080
|
-
type: acc.type,
|
|
3081
|
-
status: mapStepStatus2(acc.status),
|
|
3082
|
-
durationMs: acc.durationMs,
|
|
3083
|
-
error: acc.errorMsg,
|
|
3084
|
-
metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
|
|
3085
|
-
outputPreview,
|
|
3086
|
-
children: []
|
|
3087
|
-
};
|
|
3088
|
-
nodes.set(acc.id, sc);
|
|
3089
|
-
}
|
|
3090
|
-
const roots = [];
|
|
3091
|
-
const sortByOrder = (a, b) => {
|
|
3092
|
-
const oa = steps.get(a.id)?.order ?? 0;
|
|
3093
|
-
const ob = steps.get(b.id)?.order ?? 0;
|
|
3094
|
-
return oa - ob;
|
|
3095
|
-
};
|
|
3096
|
-
for (const acc of steps.values()) {
|
|
3097
|
-
const node = nodes.get(acc.id);
|
|
3098
|
-
if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
|
|
3099
|
-
nodes.get(acc.parentId).children.push(node);
|
|
3100
|
-
} else {
|
|
3101
|
-
roots.push(node);
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
|
-
roots.sort(sortByOrder);
|
|
3105
|
-
for (const n of nodes.values()) {
|
|
3106
|
-
n.children.sort(sortByOrder);
|
|
3107
|
-
}
|
|
3108
|
-
return {
|
|
3109
|
-
runId,
|
|
3110
|
-
name: rs.name,
|
|
3111
|
-
status: runStatus,
|
|
3112
|
-
durationMs: durationMs2,
|
|
3113
|
-
steps: roots
|
|
3114
|
-
};
|
|
3115
|
-
}
|
|
3116
|
-
|
|
3117
|
-
// packages/core/src/diff/engine.ts
|
|
3118
|
-
var DEFAULT_THRESHOLD_MS = 0;
|
|
3119
|
-
function pathSeg(step, index) {
|
|
3120
|
-
return { index, name: step.name, stepId: step.id };
|
|
3121
|
-
}
|
|
3122
|
-
function buildPath(segments) {
|
|
3123
|
-
return { path: [...segments] };
|
|
3124
|
-
}
|
|
3125
|
-
function pairSteps(left, right) {
|
|
3126
|
-
const usedRight = /* @__PURE__ */ new Set();
|
|
3127
|
-
const pairs = [];
|
|
3128
|
-
for (let i = 0; i < left.length; i++) {
|
|
3129
|
-
const L = left[i];
|
|
3130
|
-
let R = right.find((r) => !usedRight.has(r.id) && r.id === L.id);
|
|
3131
|
-
if (R === void 0 && i < right.length && !usedRight.has(right[i].id)) {
|
|
3132
|
-
const cand = right[i];
|
|
3133
|
-
if (cand.name === L.name && (cand.type ?? "") === (L.type ?? "")) {
|
|
3134
|
-
R = cand;
|
|
3135
|
-
}
|
|
3136
|
-
}
|
|
3137
|
-
if (R === void 0) {
|
|
3138
|
-
R = right.find(
|
|
3139
|
-
(r) => !usedRight.has(r.id) && r.name === L.name && (r.type ?? "") === (L.type ?? "")
|
|
3140
|
-
);
|
|
3141
|
-
}
|
|
3142
|
-
if (R !== void 0) {
|
|
3143
|
-
usedRight.add(R.id);
|
|
3144
|
-
pairs.push([L, R]);
|
|
3145
|
-
} else {
|
|
3146
|
-
pairs.push([L, void 0]);
|
|
3147
|
-
}
|
|
3148
|
-
}
|
|
3149
|
-
for (const R of right) {
|
|
3150
|
-
if (!usedRight.has(R.id)) {
|
|
3151
|
-
pairs.push([void 0, R]);
|
|
3152
|
-
}
|
|
3153
|
-
}
|
|
3154
|
-
return pairs;
|
|
3155
|
-
}
|
|
3156
|
-
function compareLeafSteps(L, R, segments, opts, out) {
|
|
3157
|
-
const path25 = buildPath(segments);
|
|
3158
|
-
if (L.name !== R.name) {
|
|
3159
|
-
out.push({
|
|
3160
|
-
kind: "structure",
|
|
3161
|
-
severity: "warning",
|
|
3162
|
-
message: "Step name differs",
|
|
3163
|
-
path: path25,
|
|
3164
|
-
left: L.name,
|
|
3165
|
-
right: R.name
|
|
3166
|
-
});
|
|
3167
|
-
}
|
|
3168
|
-
if ((L.type ?? "") !== (R.type ?? "")) {
|
|
3169
|
-
out.push({
|
|
3170
|
-
kind: "step-type",
|
|
3171
|
-
severity: "warning",
|
|
3172
|
-
message: "Step type differs",
|
|
3173
|
-
path: path25,
|
|
3174
|
-
left: L.type,
|
|
3175
|
-
right: R.type
|
|
3176
|
-
});
|
|
3177
|
-
}
|
|
3178
|
-
if ((L.status ?? "") !== (R.status ?? "")) {
|
|
3179
|
-
out.push({
|
|
3180
|
-
kind: "step-status",
|
|
3181
|
-
severity: "warning",
|
|
3182
|
-
message: "Step status differs",
|
|
3183
|
-
path: path25,
|
|
3184
|
-
left: L.status,
|
|
3185
|
-
right: R.status
|
|
3186
|
-
});
|
|
3187
|
-
}
|
|
3188
|
-
const le = L.error ?? "";
|
|
3189
|
-
const re = R.error ?? "";
|
|
3190
|
-
if (le !== re) {
|
|
3191
|
-
out.push({
|
|
3192
|
-
kind: "error",
|
|
3193
|
-
severity: "error",
|
|
3194
|
-
message: "Step error message differs",
|
|
3195
|
-
path: path25,
|
|
3196
|
-
left: le || void 0,
|
|
3197
|
-
right: re || void 0
|
|
3198
|
-
});
|
|
3199
|
-
}
|
|
3200
|
-
if (!opts.ignoreDuration) {
|
|
3201
|
-
const ld = L.durationMs;
|
|
3202
|
-
const rd = R.durationMs;
|
|
3203
|
-
const th = opts.durationThresholdMs;
|
|
3204
|
-
let differs = false;
|
|
3205
|
-
if (ld === void 0 && rd === void 0) differs = false;
|
|
3206
|
-
else if (ld === void 0 || rd === void 0) differs = true;
|
|
3207
|
-
else differs = Math.abs(ld - rd) > th;
|
|
3208
|
-
if (differs) {
|
|
3209
|
-
out.push({
|
|
3210
|
-
kind: "duration",
|
|
3211
|
-
severity: "info",
|
|
3212
|
-
message: "Step duration differs",
|
|
3213
|
-
path: path25,
|
|
3214
|
-
left: ld,
|
|
3215
|
-
right: rd
|
|
3216
|
-
});
|
|
3217
|
-
}
|
|
3218
|
-
}
|
|
3219
|
-
const lm = stableJson(L.metadata ?? {});
|
|
3220
|
-
const rm3 = stableJson(R.metadata ?? {});
|
|
3221
|
-
if (lm !== rm3) {
|
|
3222
|
-
out.push({
|
|
3223
|
-
kind: "metadata",
|
|
3224
|
-
severity: "info",
|
|
3225
|
-
message: "Step metadata differs",
|
|
3226
|
-
path: path25,
|
|
3227
|
-
left: L.metadata,
|
|
3228
|
-
right: R.metadata
|
|
3229
|
-
});
|
|
3230
|
-
}
|
|
3231
|
-
const lo = stableJson(L.outputPreview ?? null);
|
|
3232
|
-
const ro = stableJson(R.outputPreview ?? null);
|
|
3233
|
-
if (lo !== ro) {
|
|
3234
|
-
out.push({
|
|
3235
|
-
kind: "output",
|
|
3236
|
-
severity: "info",
|
|
3237
|
-
message: "Output preview differs",
|
|
3238
|
-
path: path25,
|
|
3239
|
-
left: L.outputPreview,
|
|
3240
|
-
right: R.outputPreview
|
|
3241
|
-
});
|
|
3242
|
-
}
|
|
3243
|
-
}
|
|
3244
|
-
function compareRecursive(L, R, segments, opts, out) {
|
|
3245
|
-
compareLeafSteps(L, R, segments, opts, out);
|
|
3246
|
-
const pairs = pairSteps(L.children, R.children);
|
|
3247
|
-
let ci = 0;
|
|
3248
|
-
for (const [lch, rch] of pairs) {
|
|
3249
|
-
if (lch !== void 0 && rch !== void 0) {
|
|
3250
|
-
compareRecursive(lch, rch, [...segments, pathSeg(lch, ci)], opts, out);
|
|
3251
|
-
} else if (lch !== void 0) {
|
|
3252
|
-
out.push({
|
|
3253
|
-
kind: "step-removed",
|
|
3254
|
-
severity: "warning",
|
|
3255
|
-
message: `Step only in left run: ${lch.name}`,
|
|
3256
|
-
path: buildPath([...segments, pathSeg(lch, ci)]),
|
|
3257
|
-
left: lch.id,
|
|
3258
|
-
right: void 0
|
|
3259
|
-
});
|
|
3260
|
-
} else if (rch !== void 0) {
|
|
3261
|
-
out.push({
|
|
3262
|
-
kind: "step-added",
|
|
3263
|
-
severity: "warning",
|
|
3264
|
-
message: `Step only in right run: ${rch.name}`,
|
|
3265
|
-
path: buildPath([...segments, pathSeg(rch, ci)]),
|
|
3266
|
-
left: void 0,
|
|
3267
|
-
right: rch.id
|
|
3268
|
-
});
|
|
3269
|
-
}
|
|
3270
|
-
ci += 1;
|
|
3271
|
-
}
|
|
3272
|
-
}
|
|
3273
|
-
function mergeDiffDefaults(options) {
|
|
3274
|
-
return {
|
|
3275
|
-
ignoreDuration: options?.ignoreDuration ?? false,
|
|
3276
|
-
durationThresholdMs: options?.durationThresholdMs !== void 0 ? options.durationThresholdMs : DEFAULT_THRESHOLD_MS,
|
|
3277
|
-
focus: options?.focus ?? "all",
|
|
3278
|
-
check: options?.check ?? "all"
|
|
3279
|
-
};
|
|
3280
|
-
}
|
|
3281
|
-
function kindMatchesFilter(kind, merged) {
|
|
3282
|
-
const { focus, check } = merged;
|
|
3283
|
-
if (check !== "all") {
|
|
3284
|
-
if (check === "structure") {
|
|
3285
|
-
if (!["step-added", "step-removed", "structure", "step-type"].includes(kind)) return false;
|
|
3286
|
-
} else if (check === "outputs") {
|
|
3287
|
-
if (!["metadata", "output"].includes(kind)) return false;
|
|
3288
|
-
} else if (check === "errors") {
|
|
3289
|
-
if (!["run-status", "step-status", "error"].includes(kind)) return false;
|
|
3290
|
-
} else if (check === "timing") {
|
|
3291
|
-
if (kind !== "duration") return false;
|
|
3292
|
-
}
|
|
3293
|
-
}
|
|
3294
|
-
if (focus !== "all") {
|
|
3295
|
-
if (focus === "errors") {
|
|
3296
|
-
if (!["run-status", "step-status", "error"].includes(kind)) return false;
|
|
3297
|
-
} else if (focus === "structure") {
|
|
3298
|
-
if (!["step-added", "step-removed", "structure", "step-type"].includes(kind)) return false;
|
|
3299
|
-
} else if (focus === "outputs") {
|
|
3300
|
-
if (!["metadata", "output"].includes(kind)) return false;
|
|
3301
|
-
}
|
|
3302
|
-
}
|
|
3303
|
-
return true;
|
|
3304
|
-
}
|
|
3305
|
-
function diffRuns(left, right, options) {
|
|
3306
|
-
const merged = mergeDiffDefaults(options);
|
|
3307
|
-
const opts = {
|
|
3308
|
-
ignoreDuration: merged.ignoreDuration,
|
|
3309
|
-
durationThresholdMs: merged.durationThresholdMs
|
|
3310
|
-
};
|
|
3311
|
-
const raw = [];
|
|
3312
|
-
if ((left.status ?? "") !== (right.status ?? "")) {
|
|
3313
|
-
raw.push({
|
|
3314
|
-
kind: "run-status",
|
|
3315
|
-
severity: "warning",
|
|
3316
|
-
message: "Run completion status differs",
|
|
3317
|
-
left: left.status,
|
|
3318
|
-
right: right.status
|
|
3319
|
-
});
|
|
3320
|
-
}
|
|
3321
|
-
if (!merged.ignoreDuration) {
|
|
3322
|
-
const ld = left.durationMs;
|
|
3323
|
-
const rd = right.durationMs;
|
|
3324
|
-
const th = merged.durationThresholdMs;
|
|
3325
|
-
let differs = false;
|
|
3326
|
-
if (ld === void 0 && rd === void 0) differs = false;
|
|
3327
|
-
else if (ld === void 0 || rd === void 0) differs = true;
|
|
3328
|
-
else differs = Math.abs(ld - rd) > th;
|
|
3329
|
-
if (differs) {
|
|
3330
|
-
raw.push({
|
|
3331
|
-
kind: "duration",
|
|
3332
|
-
severity: "info",
|
|
3333
|
-
message: "Run duration differs",
|
|
3334
|
-
left: ld,
|
|
3335
|
-
right: rd
|
|
3336
|
-
});
|
|
3337
|
-
}
|
|
3338
|
-
}
|
|
3339
|
-
const pairs = pairSteps(left.steps, right.steps);
|
|
3340
|
-
let idx = 0;
|
|
3341
|
-
for (const [ls, rs] of pairs) {
|
|
3342
|
-
if (ls !== void 0 && rs !== void 0) {
|
|
3343
|
-
compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
|
|
3344
|
-
idx += 1;
|
|
3345
|
-
} else if (ls !== void 0) {
|
|
3346
|
-
raw.push({
|
|
3347
|
-
kind: "step-removed",
|
|
3348
|
-
severity: "warning",
|
|
3349
|
-
message: `Step only in left run: ${ls.name}`,
|
|
3350
|
-
path: buildPath([pathSeg(ls, idx)]),
|
|
3351
|
-
left: ls.id,
|
|
3352
|
-
right: void 0
|
|
3353
|
-
});
|
|
3354
|
-
idx += 1;
|
|
3355
|
-
} else if (rs !== void 0) {
|
|
3356
|
-
raw.push({
|
|
3357
|
-
kind: "step-added",
|
|
3358
|
-
severity: "warning",
|
|
3359
|
-
message: `Step only in right run: ${rs.name}`,
|
|
3360
|
-
path: buildPath([pathSeg(rs, idx)]),
|
|
3361
|
-
left: void 0,
|
|
3362
|
-
right: rs.id
|
|
3363
|
-
});
|
|
3364
|
-
idx += 1;
|
|
3365
|
-
}
|
|
3366
|
-
}
|
|
3367
|
-
const differences = raw.filter((d) => kindMatchesFilter(d.kind, merged));
|
|
3368
|
-
let errors = 0;
|
|
3369
|
-
let warnings = 0;
|
|
3370
|
-
let info = 0;
|
|
3371
|
-
for (const d of differences) {
|
|
3372
|
-
if (d.severity === "error") errors += 1;
|
|
3373
|
-
else if (d.severity === "warning") warnings += 1;
|
|
3374
|
-
else info += 1;
|
|
3375
|
-
}
|
|
3376
|
-
const firstVisible = differences[0];
|
|
3377
|
-
const firstDivergence = firstVisible !== void 0 ? {
|
|
3378
|
-
kind: "first-divergence",
|
|
3379
|
-
severity: firstVisible.severity,
|
|
3380
|
-
message: `First divergence: ${firstVisible.message}`,
|
|
3381
|
-
path: firstVisible.path,
|
|
3382
|
-
left: firstVisible.left,
|
|
3383
|
-
right: firstVisible.right
|
|
3384
|
-
} : void 0;
|
|
3385
|
-
const summary = {
|
|
3386
|
-
leftRunId: left.runId,
|
|
3387
|
-
rightRunId: right.runId,
|
|
3388
|
-
totalDifferences: differences.length,
|
|
3389
|
-
errors,
|
|
3390
|
-
warnings,
|
|
3391
|
-
info,
|
|
3392
|
-
firstDivergence
|
|
3393
|
-
};
|
|
3394
|
-
return { summary, differences };
|
|
3395
|
-
}
|
|
3396
|
-
|
|
3397
|
-
// packages/core/src/diff/renderer.ts
|
|
3398
|
-
function formatPath(path25) {
|
|
3399
|
-
if (path25 === void 0 || path25.path.length === 0) {
|
|
3400
|
-
return "(run)";
|
|
3401
|
-
}
|
|
3402
|
-
return path25.path.map((s) => s.name).join(" > ");
|
|
3403
|
-
}
|
|
3404
|
-
function formatValue(v, verbose) {
|
|
3405
|
-
if (v === void 0) return "(undefined)";
|
|
3406
|
-
if (typeof v === "string") return v;
|
|
3407
|
-
if (typeof v === "number" || typeof v === "boolean") return String(v);
|
|
3408
|
-
const s = JSON.stringify(v);
|
|
3409
|
-
if (verbose || s.length <= 120) return s;
|
|
3410
|
-
return `${s.slice(0, 117)}...`;
|
|
3411
|
-
}
|
|
3412
|
-
function renderRunDiff(result, options) {
|
|
3413
|
-
const verbose = options?.verbose === true;
|
|
3414
|
-
const sev = (s, level) => {
|
|
3415
|
-
return s;
|
|
3416
|
-
};
|
|
3417
|
-
const lines = [];
|
|
3418
|
-
const { summary } = result;
|
|
3419
|
-
lines.push("Run diff");
|
|
3420
|
-
lines.push(`Left: ${summary.leftRunId}`);
|
|
3421
|
-
lines.push(`Right: ${summary.rightRunId}`);
|
|
3422
|
-
lines.push("");
|
|
3423
|
-
lines.push("Summary:");
|
|
3424
|
-
lines.push(` Differences: ${summary.totalDifferences}`);
|
|
3425
|
-
lines.push(` Errors: ${summary.errors}`);
|
|
3426
|
-
lines.push(` Warnings: ${summary.warnings}`);
|
|
3427
|
-
lines.push(` Info: ${summary.info}`);
|
|
3428
|
-
lines.push("");
|
|
3429
|
-
const fd = summary.firstDivergence;
|
|
3430
|
-
const firstKind = result.differences[0]?.kind;
|
|
3431
|
-
if (fd !== void 0) {
|
|
3432
|
-
lines.push("First divergence:");
|
|
3433
|
-
const where = formatPath(fd.path);
|
|
3434
|
-
const displayKind = firstKind ?? fd.kind;
|
|
3435
|
-
lines.push(` ${displayKind} at ${where}`);
|
|
3436
|
-
if (fd.left !== void 0 || fd.right !== void 0) {
|
|
3437
|
-
lines.push(` left: ${formatValue(fd.left, verbose)}`);
|
|
3438
|
-
lines.push(` right: ${formatValue(fd.right, verbose)}`);
|
|
3439
|
-
}
|
|
3440
|
-
lines.push("");
|
|
3441
|
-
}
|
|
3442
|
-
lines.push("Differences:");
|
|
3443
|
-
if (result.differences.length === 0) {
|
|
3444
|
-
lines.push(" (none)");
|
|
3445
|
-
return lines.join("\n");
|
|
3446
|
-
}
|
|
3447
|
-
const showSides = (kind) => verbose || [
|
|
3448
|
-
"run-status",
|
|
3449
|
-
"step-status",
|
|
3450
|
-
"error",
|
|
3451
|
-
"duration",
|
|
3452
|
-
"step-type",
|
|
3453
|
-
"structure",
|
|
3454
|
-
"step-added",
|
|
3455
|
-
"step-removed"
|
|
3456
|
-
].includes(kind);
|
|
3457
|
-
for (const d of result.differences) {
|
|
3458
|
-
const tag = sev(`[${d.severity}]`, d.severity);
|
|
3459
|
-
const pathStr = d.path !== void 0 ? ` ${formatPath(d.path)}` : "";
|
|
3460
|
-
lines.push(` ${tag} ${d.kind}${pathStr}`);
|
|
3461
|
-
lines.push(` ${d.message}`);
|
|
3462
|
-
if (d.left !== void 0 || d.right !== void 0) {
|
|
3463
|
-
if (showSides(d.kind)) {
|
|
3464
|
-
lines.push(` left: ${formatValue(d.left, verbose)}`);
|
|
3465
|
-
lines.push(` right: ${formatValue(d.right, verbose)}`);
|
|
3466
|
-
}
|
|
3467
|
-
}
|
|
3468
|
-
}
|
|
3469
|
-
return lines.join("\n");
|
|
3470
|
-
}
|
|
3471
|
-
|
|
3472
|
-
// packages/core/src/diff/index.ts
|
|
3473
|
-
function diffTraceEvents(leftEvents, rightEvents, options) {
|
|
3474
|
-
const left = manualTraceEventsToComparableRun(leftEvents);
|
|
3475
|
-
const right = manualTraceEventsToComparableRun(rightEvents);
|
|
3476
|
-
return diffRuns(left, right, options);
|
|
3477
|
-
}
|
|
3478
|
-
|
|
3479
3014
|
// packages/cli/src/diff.ts
|
|
3480
3015
|
function parseFocus(s) {
|
|
3481
3016
|
const v = (s ?? "all").trim().toLowerCase();
|
|
@@ -3576,7 +3111,10 @@ Trace directory: ${traceDir}`
|
|
|
3576
3111
|
}
|
|
3577
3112
|
console.log(
|
|
3578
3113
|
renderRunDiff(result, {
|
|
3579
|
-
verbose: options.verbose === true
|
|
3114
|
+
verbose: options.verbose === true,
|
|
3115
|
+
color: false,
|
|
3116
|
+
json: false
|
|
3117
|
+
})
|
|
3580
3118
|
);
|
|
3581
3119
|
}
|
|
3582
3120
|
|
|
@@ -3779,7 +3317,7 @@ function indexedToMetadata(row, traceDir) {
|
|
|
3779
3317
|
endedAt: row.endedAt ?? void 0,
|
|
3780
3318
|
durationMs: row.durationMs ?? void 0,
|
|
3781
3319
|
eventCount: 0,
|
|
3782
|
-
filePath:
|
|
3320
|
+
filePath: path16.join(traceDir, row.file),
|
|
3783
3321
|
fileSize: 0,
|
|
3784
3322
|
createdAt: new Date(row.mtimeMs)
|
|
3785
3323
|
};
|
|
@@ -3808,7 +3346,7 @@ async function newestTraceMtimeMs(traceDir) {
|
|
|
3808
3346
|
}
|
|
3809
3347
|
async function loadSessionRuns(traceDir) {
|
|
3810
3348
|
try {
|
|
3811
|
-
const mod = await import('./src-
|
|
3349
|
+
const mod = await import('./src-G7PM24W2.mjs');
|
|
3812
3350
|
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
3813
3351
|
const status = mod.indexStatus(dbPath);
|
|
3814
3352
|
if (!status.healthy) {
|
|
@@ -4257,9 +3795,9 @@ async function reportCommand(runId, options = {}) {
|
|
|
4257
3795
|
...options.section ? { section: options.section } : {}
|
|
4258
3796
|
});
|
|
4259
3797
|
const resolvedOutput = resolveOutputOption(options);
|
|
4260
|
-
const outPath = resolvedOutput !== void 0 ?
|
|
3798
|
+
const outPath = resolvedOutput !== void 0 ? path16.resolve(resolvedOutput) : void 0;
|
|
4261
3799
|
if (outPath !== void 0) {
|
|
4262
|
-
await mkdir(
|
|
3800
|
+
await mkdir(path16.dirname(outPath), { recursive: true });
|
|
4263
3801
|
await writeFile(outPath, result.content, "utf-8");
|
|
4264
3802
|
console.log(`Wrote ${result.fileExtension} report to ${outPath}`);
|
|
4265
3803
|
}
|
|
@@ -4385,8 +3923,8 @@ function passesLuhn(value) {
|
|
|
4385
3923
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4386
3924
|
var EPOCH_MS_RE = /^1[0-9]{12}$/;
|
|
4387
3925
|
var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
|
|
4388
|
-
function pathSuggestsNonCard(
|
|
4389
|
-
const normalized =
|
|
3926
|
+
function pathSuggestsNonCard(path27) {
|
|
3927
|
+
const normalized = path27.toLowerCase().replace(/[^a-z0-9._]/g, "");
|
|
4390
3928
|
return /(^|\.)(tokenusage|usage)(\.|$)/.test(normalized) || /(startedat|endedat|durationms|timestamp|createdat|updatedat)(\.|$)/.test(normalized) || /(^|\.)(runid|traceid|spanid|eventid|sessionid|userid|parentid|requestid|correlationid)(\.|$)/.test(
|
|
4391
3929
|
normalized
|
|
4392
3930
|
) || /(^|\.)ts(\.|$)/.test(normalized);
|
|
@@ -4573,17 +4111,17 @@ function applyRule(rule, value, replacement) {
|
|
|
4573
4111
|
}
|
|
4574
4112
|
return value;
|
|
4575
4113
|
}
|
|
4576
|
-
function childPath(
|
|
4114
|
+
function childPath(path27, key) {
|
|
4577
4115
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
4578
|
-
return
|
|
4116
|
+
return path27 ? `${path27}.${key}` : key;
|
|
4579
4117
|
}
|
|
4580
|
-
return `${
|
|
4118
|
+
return `${path27 || "$"}[${JSON.stringify(key)}]`;
|
|
4581
4119
|
}
|
|
4582
|
-
function indexPath(
|
|
4583
|
-
return `${
|
|
4120
|
+
function indexPath(path27, index) {
|
|
4121
|
+
return `${path27 || "$"}[${index}]`;
|
|
4584
4122
|
}
|
|
4585
|
-
function makeFinding(
|
|
4586
|
-
return preview === void 0 ? { path:
|
|
4123
|
+
function makeFinding(path27, detector, action, matchKind, severity = "warning", preview) {
|
|
4124
|
+
return preview === void 0 ? { path: path27, detector, action, severity, matchKind } : { path: path27, detector, action, severity, matchKind, preview };
|
|
4587
4125
|
}
|
|
4588
4126
|
function createRedactionProfile(profile = "local") {
|
|
4589
4127
|
switch (profile) {
|
|
@@ -4652,11 +4190,11 @@ var Redactor2 = class {
|
|
|
4652
4190
|
#recordFinding(state, finding) {
|
|
4653
4191
|
if (this.#collectFindings) state.findings.push(finding);
|
|
4654
4192
|
}
|
|
4655
|
-
#redactValue(value, key,
|
|
4193
|
+
#redactValue(value, key, path27, depth, state) {
|
|
4656
4194
|
if (depth > this.#maxDepth) {
|
|
4657
4195
|
this.#recordFinding(
|
|
4658
4196
|
state,
|
|
4659
|
-
makeFinding(
|
|
4197
|
+
makeFinding(path27, "structure.maxDepth", "truncate", "value", "warning")
|
|
4660
4198
|
);
|
|
4661
4199
|
return "[Truncated]";
|
|
4662
4200
|
}
|
|
@@ -4665,19 +4203,19 @@ var Redactor2 = class {
|
|
|
4665
4203
|
if (rule) {
|
|
4666
4204
|
this.#recordFinding(
|
|
4667
4205
|
state,
|
|
4668
|
-
makeFinding(
|
|
4206
|
+
makeFinding(path27, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
4669
4207
|
);
|
|
4670
4208
|
return applyRule(rule, value, this.#replacement);
|
|
4671
4209
|
}
|
|
4672
4210
|
}
|
|
4673
4211
|
for (const detector of this.#detectors) {
|
|
4674
|
-
const detections = detector.detect({ path:
|
|
4212
|
+
const detections = detector.detect({ path: path27, key, value });
|
|
4675
4213
|
for (const detection of detections) {
|
|
4676
4214
|
const action = detection.action ?? "replace";
|
|
4677
4215
|
this.#recordFinding(
|
|
4678
4216
|
state,
|
|
4679
4217
|
makeFinding(
|
|
4680
|
-
|
|
4218
|
+
path27,
|
|
4681
4219
|
detector.id,
|
|
4682
4220
|
action,
|
|
4683
4221
|
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
@@ -4695,7 +4233,7 @@ var Redactor2 = class {
|
|
|
4695
4233
|
const out = [];
|
|
4696
4234
|
state.seen.set(value, out);
|
|
4697
4235
|
value.forEach((item, index) => {
|
|
4698
|
-
out[index] = this.#redactValue(item, void 0, indexPath(
|
|
4236
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path27, index), depth + 1, state);
|
|
4699
4237
|
});
|
|
4700
4238
|
return out;
|
|
4701
4239
|
}
|
|
@@ -4707,7 +4245,7 @@ var Redactor2 = class {
|
|
|
4707
4245
|
out[entryKey] = this.#redactValue(
|
|
4708
4246
|
entryValue,
|
|
4709
4247
|
entryKey,
|
|
4710
|
-
childPath(
|
|
4248
|
+
childPath(path27 === "$" ? "" : path27, entryKey),
|
|
4711
4249
|
depth + 1,
|
|
4712
4250
|
state
|
|
4713
4251
|
);
|
|
@@ -5168,14 +4706,14 @@ function uniqueSorted(values) {
|
|
|
5168
4706
|
return [...new Set(values)].sort();
|
|
5169
4707
|
}
|
|
5170
4708
|
function isWithinDirectory(child, parent) {
|
|
5171
|
-
const relative =
|
|
5172
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
4709
|
+
const relative = path16.relative(parent, child);
|
|
4710
|
+
return relative === "" || !relative.startsWith("..") && !path16.isAbsolute(relative);
|
|
5173
4711
|
}
|
|
5174
4712
|
async function resolveOutputPath(inputPath, output2, force) {
|
|
5175
4713
|
if (output2 === void 0 || output2.trim() === "") return void 0;
|
|
5176
|
-
const inputAbs =
|
|
5177
|
-
const outputAbs =
|
|
5178
|
-
const inputDir =
|
|
4714
|
+
const inputAbs = path16.resolve(inputPath);
|
|
4715
|
+
const outputAbs = path16.resolve(output2.trim());
|
|
4716
|
+
const inputDir = path16.dirname(inputAbs);
|
|
5179
4717
|
if (!isWithinDirectory(outputAbs, inputDir)) {
|
|
5180
4718
|
throw new Error("Refusing to write migrated output outside the input directory.");
|
|
5181
4719
|
}
|
|
@@ -5296,7 +4834,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
5296
4834
|
process.exitCode = 1;
|
|
5297
4835
|
return;
|
|
5298
4836
|
}
|
|
5299
|
-
const inputPath =
|
|
4837
|
+
const inputPath = path16.resolve(input3.trim());
|
|
5300
4838
|
const dryRun = options.dryRun === true;
|
|
5301
4839
|
if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
|
|
5302
4840
|
console.error("migrate requires --dry-run or --output <path>.");
|
|
@@ -5315,7 +4853,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
5315
4853
|
);
|
|
5316
4854
|
const result = await buildMigration(inputPath, outputPath);
|
|
5317
4855
|
if (!dryRun && outputPath !== void 0) {
|
|
5318
|
-
await mkdir(
|
|
4856
|
+
await mkdir(path16.dirname(outputPath), { recursive: true });
|
|
5319
4857
|
await writeFile(outputPath, result.content, "utf-8");
|
|
5320
4858
|
}
|
|
5321
4859
|
printSummary2(result, dryRun);
|
|
@@ -5645,23 +5183,23 @@ function evaluatePromptInjection(text, options = {}) {
|
|
|
5645
5183
|
}
|
|
5646
5184
|
return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
|
|
5647
5185
|
}
|
|
5648
|
-
function validateSchemaField(value, field,
|
|
5186
|
+
function validateSchemaField(value, field, path27, evidence) {
|
|
5649
5187
|
const ruleId = "guardrail.structured-output";
|
|
5650
5188
|
if (field.type) {
|
|
5651
5189
|
const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
5652
5190
|
if (actual !== field.type) {
|
|
5653
|
-
evidence.push({ ruleId, path:
|
|
5191
|
+
evidence.push({ ruleId, path: path27, preview: `expected ${field.type}, got ${actual}` });
|
|
5654
5192
|
return;
|
|
5655
5193
|
}
|
|
5656
5194
|
}
|
|
5657
5195
|
if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
|
|
5658
|
-
evidence.push({ ruleId, path:
|
|
5196
|
+
evidence.push({ ruleId, path: path27, preview: "value not in enum" });
|
|
5659
5197
|
}
|
|
5660
5198
|
if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
|
|
5661
5199
|
const record = value;
|
|
5662
5200
|
for (const key of field.required) {
|
|
5663
5201
|
if (!(key in record)) {
|
|
5664
|
-
evidence.push({ ruleId, path: `${
|
|
5202
|
+
evidence.push({ ruleId, path: `${path27}.${key}`, preview: "missing required key" });
|
|
5665
5203
|
}
|
|
5666
5204
|
}
|
|
5667
5205
|
}
|
|
@@ -5988,7 +5526,7 @@ function asConfig(value) {
|
|
|
5988
5526
|
}
|
|
5989
5527
|
async function loadConfig(configPath) {
|
|
5990
5528
|
if (configPath === void 0) return {};
|
|
5991
|
-
const extension =
|
|
5529
|
+
const extension = path16.extname(configPath);
|
|
5992
5530
|
if (TS_CONFIG_EXTENSIONS.has(extension)) {
|
|
5993
5531
|
throw new Error(
|
|
5994
5532
|
"TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -5997,7 +5535,7 @@ async function loadConfig(configPath) {
|
|
|
5997
5535
|
if (!CONFIG_EXTENSIONS.has(extension)) {
|
|
5998
5536
|
throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
|
|
5999
5537
|
}
|
|
6000
|
-
const absolute =
|
|
5538
|
+
const absolute = path16.resolve(configPath);
|
|
6001
5539
|
if (extension === ".json") {
|
|
6002
5540
|
const raw = await readFile(absolute, "utf-8");
|
|
6003
5541
|
return asConfig(JSON.parse(raw));
|
|
@@ -6173,10 +5711,10 @@ function printHuman(result) {
|
|
|
6173
5711
|
console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
6174
5712
|
}
|
|
6175
5713
|
for (const finding of result.findings) {
|
|
6176
|
-
const
|
|
5714
|
+
const path27 = finding.evidence[0]?.path;
|
|
6177
5715
|
const run = finding.evidence[0]?.runId;
|
|
6178
5716
|
const runPrefix = run ? `[${run}] ` : "";
|
|
6179
|
-
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${
|
|
5717
|
+
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path27 ? ` (${path27})` : ""}`);
|
|
6180
5718
|
}
|
|
6181
5719
|
}
|
|
6182
5720
|
function readErrorResult(error) {
|
|
@@ -6438,7 +5976,7 @@ async function loadSuiteViewerData(options) {
|
|
|
6438
5976
|
for (const suiteCase of result.cases) {
|
|
6439
5977
|
cases.push(await enrichCase(suiteCase, baselinePath));
|
|
6440
5978
|
}
|
|
6441
|
-
const artifactsDir =
|
|
5979
|
+
const artifactsDir = path16.join(path16.dirname(result.configPath), ".agent-inspect/suite-runs");
|
|
6442
5980
|
return {
|
|
6443
5981
|
suiteName: result.suiteName,
|
|
6444
5982
|
configPath: result.configPath,
|
|
@@ -6631,19 +6169,19 @@ function serializeWorkspaceManifest(manifest) {
|
|
|
6631
6169
|
}
|
|
6632
6170
|
var INDEX_DIR_NAME = "index";
|
|
6633
6171
|
function resolveWorkspaceLocation(cwd = process.cwd()) {
|
|
6634
|
-
const projectRoot =
|
|
6635
|
-
const workspaceDir =
|
|
6172
|
+
const projectRoot = path16.resolve(cwd);
|
|
6173
|
+
const workspaceDir = path16.join(projectRoot, WORKSPACE_DIR_NAME);
|
|
6636
6174
|
return {
|
|
6637
6175
|
projectRoot,
|
|
6638
6176
|
workspaceDir,
|
|
6639
|
-
manifestPath:
|
|
6177
|
+
manifestPath: path16.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
|
|
6640
6178
|
};
|
|
6641
6179
|
}
|
|
6642
6180
|
function resolveInsideWorkspace(workspaceDir, relative) {
|
|
6643
|
-
const base =
|
|
6644
|
-
const resolved =
|
|
6645
|
-
const rel =
|
|
6646
|
-
if (rel === "" || rel === "." || !rel.startsWith("..") && !
|
|
6181
|
+
const base = path16.resolve(workspaceDir);
|
|
6182
|
+
const resolved = path16.resolve(base, relative);
|
|
6183
|
+
const rel = path16.relative(base, resolved);
|
|
6184
|
+
if (rel === "" || rel === "." || !rel.startsWith("..") && !path16.isAbsolute(rel)) {
|
|
6647
6185
|
return resolved;
|
|
6648
6186
|
}
|
|
6649
6187
|
throw new Error(
|
|
@@ -6712,7 +6250,7 @@ async function createWorkspace(options = {}) {
|
|
|
6712
6250
|
created = false;
|
|
6713
6251
|
adopted = true;
|
|
6714
6252
|
} else {
|
|
6715
|
-
const project = options.project?.trim() ||
|
|
6253
|
+
const project = options.project?.trim() || path16.basename(location.projectRoot) || "workspace";
|
|
6716
6254
|
const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
|
|
6717
6255
|
manifest = createDefaultWorkspaceManifest({
|
|
6718
6256
|
project,
|
|
@@ -6851,7 +6389,7 @@ async function doctorWorkspace(location) {
|
|
|
6851
6389
|
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
6852
6390
|
for (const file of await listJsonl(abs)) {
|
|
6853
6391
|
try {
|
|
6854
|
-
const s = await stat(
|
|
6392
|
+
const s = await stat(path16.join(abs, file));
|
|
6855
6393
|
newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
|
|
6856
6394
|
} catch {
|
|
6857
6395
|
checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
|
|
@@ -6899,7 +6437,7 @@ async function cleanWorkspace(location, manifest, options = {}) {
|
|
|
6899
6437
|
const relPath = `${rel}/${entry}`;
|
|
6900
6438
|
removed.push(relPath);
|
|
6901
6439
|
if (!dryRun) {
|
|
6902
|
-
await rm(
|
|
6440
|
+
await rm(path16.join(abs, entry), { recursive: true, force: true });
|
|
6903
6441
|
}
|
|
6904
6442
|
}
|
|
6905
6443
|
}
|
|
@@ -6908,7 +6446,7 @@ async function cleanWorkspace(location, manifest, options = {}) {
|
|
|
6908
6446
|
|
|
6909
6447
|
// packages/viewer/src/workspace-data.ts
|
|
6910
6448
|
async function loadWorkspaceViewerData(options) {
|
|
6911
|
-
const cwd =
|
|
6449
|
+
const cwd = path16.resolve(options.cwd ?? process.cwd());
|
|
6912
6450
|
const location = resolveWorkspaceLocation(cwd);
|
|
6913
6451
|
const manifestRead = await readWorkspaceManifestFile(location);
|
|
6914
6452
|
if (!manifestRead.ok || manifestRead.manifest === void 0) {
|
|
@@ -6921,7 +6459,7 @@ async function loadWorkspaceViewerData(options) {
|
|
|
6921
6459
|
const runs = [];
|
|
6922
6460
|
for (const rel of manifestRead.manifest.traceDirs) {
|
|
6923
6461
|
const traceDir = resolveTraceDir({
|
|
6924
|
-
dir:
|
|
6462
|
+
dir: path16.join(location.workspaceDir, rel)
|
|
6925
6463
|
});
|
|
6926
6464
|
const td = new TraceDirectory({ dir: traceDir });
|
|
6927
6465
|
const files = await td.list();
|
|
@@ -6935,7 +6473,7 @@ async function loadWorkspaceViewerData(options) {
|
|
|
6935
6473
|
runId: meta.runId,
|
|
6936
6474
|
...meta.name !== void 0 ? { name: meta.name } : {},
|
|
6937
6475
|
status: meta.status,
|
|
6938
|
-
file:
|
|
6476
|
+
file: path16.basename(meta.filePath)
|
|
6939
6477
|
});
|
|
6940
6478
|
}
|
|
6941
6479
|
}
|
|
@@ -6946,8 +6484,8 @@ async function loadWorkspaceViewerData(options) {
|
|
|
6946
6484
|
doctor,
|
|
6947
6485
|
runs,
|
|
6948
6486
|
bundleDirs: [
|
|
6949
|
-
|
|
6950
|
-
|
|
6487
|
+
path16.join(location.workspaceDir, manifestRead.manifest.bundlesDir),
|
|
6488
|
+
path16.join(location.workspaceDir, manifestRead.manifest.artifactsDir)
|
|
6951
6489
|
]
|
|
6952
6490
|
};
|
|
6953
6491
|
}
|
|
@@ -7010,7 +6548,7 @@ function createViewerServer(options = {}) {
|
|
|
7010
6548
|
ok: true,
|
|
7011
6549
|
readOnly: true,
|
|
7012
6550
|
mode,
|
|
7013
|
-
traceDir:
|
|
6551
|
+
traceDir: path16.resolve(traceDir)
|
|
7014
6552
|
});
|
|
7015
6553
|
}
|
|
7016
6554
|
if (pathname === "/api/suite" && mode === "suite") {
|
|
@@ -7039,7 +6577,7 @@ function createViewerServer(options = {}) {
|
|
|
7039
6577
|
runId: meta.runId,
|
|
7040
6578
|
name: meta.name,
|
|
7041
6579
|
status: meta.status,
|
|
7042
|
-
file:
|
|
6580
|
+
file: path16.basename(meta.filePath),
|
|
7043
6581
|
startedAt: meta.startedAt,
|
|
7044
6582
|
durationMs: meta.durationMs
|
|
7045
6583
|
}))
|
|
@@ -7156,7 +6694,7 @@ function startViewerServer(options = {}) {
|
|
|
7156
6694
|
resolve({
|
|
7157
6695
|
host,
|
|
7158
6696
|
port: resolvedPort,
|
|
7159
|
-
traceDir:
|
|
6697
|
+
traceDir: path16.resolve(traceDir),
|
|
7160
6698
|
url: `http://${host}:${resolvedPort}/${modeQuery}`,
|
|
7161
6699
|
mode
|
|
7162
6700
|
});
|
|
@@ -7527,10 +7065,10 @@ async function evalRun(input3, options = {}) {
|
|
|
7527
7065
|
diagnostics: []
|
|
7528
7066
|
};
|
|
7529
7067
|
}
|
|
7530
|
-
function evidenceForRun(run,
|
|
7531
|
-
return [{ runId: run.runId, ...
|
|
7068
|
+
function evidenceForRun(run, path27) {
|
|
7069
|
+
return [{ runId: run.runId, ...path27 !== void 0 ? { path: path27 } : {} }];
|
|
7532
7070
|
}
|
|
7533
|
-
function evidenceForEvent(event,
|
|
7071
|
+
function evidenceForEvent(event, path27) {
|
|
7534
7072
|
return [
|
|
7535
7073
|
{
|
|
7536
7074
|
runId: event.runId,
|
|
@@ -7538,7 +7076,7 @@ function evidenceForEvent(event, path25) {
|
|
|
7538
7076
|
...event.parentId !== void 0 ? { parentId: event.parentId } : {},
|
|
7539
7077
|
kind: event.kind,
|
|
7540
7078
|
name: event.name,
|
|
7541
|
-
...
|
|
7079
|
+
...path27 !== void 0 ? { path: path27 } : {}
|
|
7542
7080
|
}
|
|
7543
7081
|
];
|
|
7544
7082
|
}
|
|
@@ -7696,9 +7234,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
|
|
|
7696
7234
|
function tokenize(text) {
|
|
7697
7235
|
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));
|
|
7698
7236
|
}
|
|
7699
|
-
function firstEvidence(fields, run,
|
|
7237
|
+
function firstEvidence(fields, run, path27) {
|
|
7700
7238
|
const first = fields[0];
|
|
7701
|
-
return first === void 0 ? evidenceForRun(run,
|
|
7239
|
+
return first === void 0 ? evidenceForRun(run, path27) : evidenceForEvent(first.node.event, first.path);
|
|
7702
7240
|
}
|
|
7703
7241
|
function collectSourceIds(nodes, keys) {
|
|
7704
7242
|
const wanted = keySet(keys);
|
|
@@ -8075,8 +7613,8 @@ function renderEvalMarkdown(result) {
|
|
|
8075
7613
|
if (result.findings.length > 0) {
|
|
8076
7614
|
lines.push("", "## Findings");
|
|
8077
7615
|
for (const finding of result.findings) {
|
|
8078
|
-
const
|
|
8079
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
7616
|
+
const path27 = finding.evidence[0]?.path;
|
|
7617
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path27 ? ` (${path27})` : ""}`);
|
|
8080
7618
|
}
|
|
8081
7619
|
}
|
|
8082
7620
|
return `${lines.join("\n")}
|
|
@@ -8123,7 +7661,7 @@ function asConfig2(value) {
|
|
|
8123
7661
|
}
|
|
8124
7662
|
async function loadConfig2(configPath) {
|
|
8125
7663
|
if (configPath === void 0) return {};
|
|
8126
|
-
const extension =
|
|
7664
|
+
const extension = path16.extname(configPath);
|
|
8127
7665
|
if (TS_CONFIG_EXTENSIONS2.has(extension)) {
|
|
8128
7666
|
throw new Error(
|
|
8129
7667
|
"TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -8132,7 +7670,7 @@ async function loadConfig2(configPath) {
|
|
|
8132
7670
|
if (!CONFIG_EXTENSIONS2.has(extension)) {
|
|
8133
7671
|
throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
|
|
8134
7672
|
}
|
|
8135
|
-
const absolute =
|
|
7673
|
+
const absolute = path16.resolve(configPath);
|
|
8136
7674
|
if (extension === ".json") {
|
|
8137
7675
|
const raw = await readFile(absolute, "utf-8");
|
|
8138
7676
|
return asConfig2(JSON.parse(raw));
|
|
@@ -8269,8 +7807,8 @@ function printHuman2(result) {
|
|
|
8269
7807
|
console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
8270
7808
|
}
|
|
8271
7809
|
for (const finding of result.findings) {
|
|
8272
|
-
const
|
|
8273
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
7810
|
+
const path27 = finding.evidence[0]?.path;
|
|
7811
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path27 ? ` (${path27})` : ""}`);
|
|
8274
7812
|
}
|
|
8275
7813
|
}
|
|
8276
7814
|
function readErrorResult2(error) {
|
|
@@ -8523,14 +8061,14 @@ function exitCodeFor3(result) {
|
|
|
8523
8061
|
return 2;
|
|
8524
8062
|
}
|
|
8525
8063
|
function explainFinding(finding, blocksBundle) {
|
|
8526
|
-
const
|
|
8064
|
+
const path27 = finding.evidence[0]?.path ?? "(unknown path)";
|
|
8527
8065
|
const category = finding.category ?? "structure";
|
|
8528
8066
|
const confidence = finding.confidence ?? "medium";
|
|
8529
8067
|
const detector = finding.detector ?? finding.ruleId;
|
|
8530
8068
|
const action = finding.action ?? "review";
|
|
8531
8069
|
const redactionHint = category === "credential" || category === "personal-data" || category === "raw-content" || action.includes("redact") ? "Usually removable by share/strict redaction before bundling." : "May require omitting the field, lowering limits, or an explicit local override.";
|
|
8532
8070
|
return [
|
|
8533
|
-
` Matched: detector=${detector}; path=${
|
|
8071
|
+
` Matched: detector=${detector}; path=${path27}; category=${category}`,
|
|
8534
8072
|
` Why: ${finding.message}`,
|
|
8535
8073
|
` Confidence: ${confidence}`,
|
|
8536
8074
|
` Redaction: ${redactionHint}`,
|
|
@@ -8572,9 +8110,9 @@ function printHuman3(result, explain = false) {
|
|
|
8572
8110
|
console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
8573
8111
|
}
|
|
8574
8112
|
for (const finding of result.findings) {
|
|
8575
|
-
const
|
|
8113
|
+
const path27 = finding.evidence[0]?.path;
|
|
8576
8114
|
const taxonomy = finding.category !== void 0 || finding.confidence !== void 0 ? ` [${[finding.category, finding.confidence].filter(Boolean).join("/")}]` : "";
|
|
8577
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
8115
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path27 ? ` (${path27})` : ""}${taxonomy}`);
|
|
8578
8116
|
if (explain) {
|
|
8579
8117
|
const blocks = finding.severity === "error" || finding.status === "fail";
|
|
8580
8118
|
for (const line of explainFinding(finding, blocks)) {
|
|
@@ -8688,6 +8226,91 @@ function messageStartsWithDash(error) {
|
|
|
8688
8226
|
const message = error instanceof Error ? error.message : String(error);
|
|
8689
8227
|
return message.startsWith("--");
|
|
8690
8228
|
}
|
|
8229
|
+
function validateReporterArtifactPath(options) {
|
|
8230
|
+
const outputDir = path16.resolve(options.outputDir);
|
|
8231
|
+
const diagnostics = [];
|
|
8232
|
+
const rawPath = options.relativePath;
|
|
8233
|
+
if (rawPath.length === 0) {
|
|
8234
|
+
diagnostics.push({
|
|
8235
|
+
code: "artifact_path_empty",
|
|
8236
|
+
severity: "error",
|
|
8237
|
+
message: "Reporter artifact path must not be empty."
|
|
8238
|
+
});
|
|
8239
|
+
return { ok: false, outputDir, diagnostics };
|
|
8240
|
+
}
|
|
8241
|
+
if (rawPath.includes("\0")) {
|
|
8242
|
+
diagnostics.push({
|
|
8243
|
+
code: "invalid_artifact_path",
|
|
8244
|
+
severity: "error",
|
|
8245
|
+
message: "Reporter artifact path must not contain null bytes.",
|
|
8246
|
+
target: rawPath
|
|
8247
|
+
});
|
|
8248
|
+
return { ok: false, outputDir, diagnostics };
|
|
8249
|
+
}
|
|
8250
|
+
if (path16.isAbsolute(rawPath) || path16.win32.isAbsolute(rawPath)) {
|
|
8251
|
+
diagnostics.push({
|
|
8252
|
+
code: "artifact_path_absolute",
|
|
8253
|
+
severity: "error",
|
|
8254
|
+
message: "Reporter artifact path must be relative.",
|
|
8255
|
+
target: rawPath
|
|
8256
|
+
});
|
|
8257
|
+
return { ok: false, outputDir, diagnostics };
|
|
8258
|
+
}
|
|
8259
|
+
const normalized = path16.posix.normalize(rawPath.replace(/\\/g, "/"));
|
|
8260
|
+
const segments = normalized.split("/");
|
|
8261
|
+
if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
|
|
8262
|
+
diagnostics.push({
|
|
8263
|
+
code: "artifact_path_escape",
|
|
8264
|
+
severity: "error",
|
|
8265
|
+
message: "Reporter artifact path must stay under the output directory.",
|
|
8266
|
+
target: rawPath
|
|
8267
|
+
});
|
|
8268
|
+
return { ok: false, outputDir, diagnostics };
|
|
8269
|
+
}
|
|
8270
|
+
const absolutePath = path16.resolve(outputDir, normalized);
|
|
8271
|
+
const relFromOutput = path16.relative(outputDir, absolutePath);
|
|
8272
|
+
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path16.isAbsolute(relFromOutput)) {
|
|
8273
|
+
diagnostics.push({
|
|
8274
|
+
code: "artifact_path_escape",
|
|
8275
|
+
severity: "error",
|
|
8276
|
+
message: "Reporter artifact path resolved outside the output directory.",
|
|
8277
|
+
target: rawPath
|
|
8278
|
+
});
|
|
8279
|
+
return { ok: false, outputDir, diagnostics };
|
|
8280
|
+
}
|
|
8281
|
+
return {
|
|
8282
|
+
ok: true,
|
|
8283
|
+
outputDir,
|
|
8284
|
+
relativePath: normalized,
|
|
8285
|
+
absolutePath,
|
|
8286
|
+
diagnostics
|
|
8287
|
+
};
|
|
8288
|
+
}
|
|
8289
|
+
var EVIDENCE_CI_ARTIFACT_FILES = [
|
|
8290
|
+
"evidence.html",
|
|
8291
|
+
"evidence.json",
|
|
8292
|
+
"check-results.json",
|
|
8293
|
+
"trace.jsonl"
|
|
8294
|
+
];
|
|
8295
|
+
function createEvidenceCiArtifacts(options) {
|
|
8296
|
+
const profile = options.redactionProfile;
|
|
8297
|
+
const prefix = options.relativeDir !== void 0 && options.relativeDir.trim() !== "" ? options.relativeDir.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "") : "";
|
|
8298
|
+
const join = (name) => prefix === "" ? name : path16.posix.join(prefix, name);
|
|
8299
|
+
const formatFor = (name) => {
|
|
8300
|
+
if (name.endsWith(".html")) return "html";
|
|
8301
|
+
if (name.endsWith(".jsonl")) return "jsonl";
|
|
8302
|
+
return "json";
|
|
8303
|
+
};
|
|
8304
|
+
return EVIDENCE_CI_ARTIFACT_FILES.map((name) => ({
|
|
8305
|
+
kind: "evidence",
|
|
8306
|
+
path: join(name),
|
|
8307
|
+
format: formatFor(name),
|
|
8308
|
+
redactionProfile: profile,
|
|
8309
|
+
title: `Evidence v2 ${name}`
|
|
8310
|
+
}));
|
|
8311
|
+
}
|
|
8312
|
+
|
|
8313
|
+
// packages/cli/src/artifacts.ts
|
|
8691
8314
|
var NOTE = "Generated locally by AgentInspect. Artifacts are best-effort summaries, not compliance or security certification.";
|
|
8692
8315
|
var SAFETY_RULES = [
|
|
8693
8316
|
createSafetyRawContentRule(),
|
|
@@ -8761,8 +8384,8 @@ function renderCheckSection(result) {
|
|
|
8761
8384
|
`Diagnostics: ${result.diagnostics.length}`
|
|
8762
8385
|
];
|
|
8763
8386
|
for (const finding of result.findings.slice(0, 10)) {
|
|
8764
|
-
const
|
|
8765
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
8387
|
+
const path27 = finding.evidence[0]?.path ?? "(run)";
|
|
8388
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path27})`);
|
|
8766
8389
|
}
|
|
8767
8390
|
for (const diagnostic3 of result.diagnostics.slice(0, 10)) {
|
|
8768
8391
|
lines.push(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
@@ -8840,8 +8463,8 @@ function renderHtml(trace, check, diff) {
|
|
|
8840
8463
|
`;
|
|
8841
8464
|
}
|
|
8842
8465
|
async function writeArtifact(outputDir, relativePath, content, files) {
|
|
8843
|
-
const outPath =
|
|
8844
|
-
await mkdir(
|
|
8466
|
+
const outPath = path16.join(outputDir, relativePath);
|
|
8467
|
+
await mkdir(path16.dirname(outPath), { recursive: true });
|
|
8845
8468
|
await writeFile(outPath, content, "utf-8");
|
|
8846
8469
|
files.push(relativePath);
|
|
8847
8470
|
}
|
|
@@ -8856,8 +8479,21 @@ function manifestStatus(check, diff) {
|
|
|
8856
8479
|
if (check.summary.warnings > 0 || (diff?.summary.warnings ?? 0) > 0) return "warning";
|
|
8857
8480
|
return "ok";
|
|
8858
8481
|
}
|
|
8482
|
+
function toEvidenceStatus(status) {
|
|
8483
|
+
if (status === "ok") return "SAFE";
|
|
8484
|
+
if (status === "warning") return "SAFE WITH WARNINGS";
|
|
8485
|
+
if (status === "unsafe" || status === "regression") return "UNSAFE";
|
|
8486
|
+
return "UNKNOWN";
|
|
8487
|
+
}
|
|
8488
|
+
function shouldWriteEvidence(options, status) {
|
|
8489
|
+
if (options.alwaysEvidence === true) return true;
|
|
8490
|
+
if (options.evidence === false) return false;
|
|
8491
|
+
const onFailureOnly = options.onFailureOnly !== false;
|
|
8492
|
+
if (!onFailureOnly) return true;
|
|
8493
|
+
return status === "unsafe" || status === "regression" || status === "unknown" || status === "warning";
|
|
8494
|
+
}
|
|
8859
8495
|
async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
8860
|
-
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ?
|
|
8496
|
+
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path16.resolve(options.outputDir.trim()) : "";
|
|
8861
8497
|
if (outputDir === "") {
|
|
8862
8498
|
console.error("--output-dir is required.");
|
|
8863
8499
|
process.exitCode = 1;
|
|
@@ -8921,15 +8557,77 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
|
8921
8557
|
);
|
|
8922
8558
|
await writeArtifact(outputDir, "summary.md", renderMarkdown(trace, check, diff), files);
|
|
8923
8559
|
await writeArtifact(outputDir, "report.html", renderHtml(trace, check, diff), files);
|
|
8560
|
+
const status = manifestStatus(check, diff);
|
|
8561
|
+
if (shouldWriteEvidence(options, status)) {
|
|
8562
|
+
try {
|
|
8563
|
+
const runIds = selectedRun !== void 0 ? [selectedRun.runId] : read.runs.map((run) => run.runId);
|
|
8564
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
8565
|
+
const sourceContents = /* @__PURE__ */ new Map();
|
|
8566
|
+
let combined = "";
|
|
8567
|
+
for (const runId of runIds) {
|
|
8568
|
+
const tracePath = getTraceFilePath(runId, traceDir);
|
|
8569
|
+
let raw = "";
|
|
8570
|
+
try {
|
|
8571
|
+
raw = await readFile(tracePath, "utf-8");
|
|
8572
|
+
} catch {
|
|
8573
|
+
raw = `${read.events.filter((event) => event.runId === runId).map((event) => JSON.stringify(event)).join("\n")}
|
|
8574
|
+
`;
|
|
8575
|
+
}
|
|
8576
|
+
sourceContents.set(runId, raw);
|
|
8577
|
+
const redacted = redactTraceContent(raw, "strict");
|
|
8578
|
+
combined += redacted.content.endsWith("\n") ? redacted.content : `${redacted.content}
|
|
8579
|
+
`;
|
|
8580
|
+
}
|
|
8581
|
+
const checkResultsJson = writeJson3({
|
|
8582
|
+
aggregateStatus: toEvidenceStatus(status),
|
|
8583
|
+
runs: runIds.map((runId) => ({
|
|
8584
|
+
runId,
|
|
8585
|
+
status: toEvidenceStatus(status),
|
|
8586
|
+
errors: check.summary.errors,
|
|
8587
|
+
warnings: check.summary.warnings,
|
|
8588
|
+
findings: check.findings.length
|
|
8589
|
+
}))
|
|
8590
|
+
});
|
|
8591
|
+
const evidencePackage = buildEvidenceCiPackage({
|
|
8592
|
+
generatorVersion: version,
|
|
8593
|
+
runIds,
|
|
8594
|
+
sourceContents,
|
|
8595
|
+
redactedTraceJsonl: combined,
|
|
8596
|
+
redactionProfile: "strict",
|
|
8597
|
+
assessmentStatus: toEvidenceStatus(status),
|
|
8598
|
+
checkResultsJson,
|
|
8599
|
+
summaryText: renderMarkdown(trace, check, diff)
|
|
8600
|
+
});
|
|
8601
|
+
await writeArtifact(outputDir, "evidence.html", evidencePackage["evidence.html"], files);
|
|
8602
|
+
await writeArtifact(outputDir, "evidence.json", evidencePackage["evidence.json"], files);
|
|
8603
|
+
await writeArtifact(
|
|
8604
|
+
outputDir,
|
|
8605
|
+
"check-results.json",
|
|
8606
|
+
evidencePackage["check-results.json"],
|
|
8607
|
+
files
|
|
8608
|
+
);
|
|
8609
|
+
await writeArtifact(outputDir, "trace.jsonl", evidencePackage["trace.jsonl"], files);
|
|
8610
|
+
const expected = createEvidenceCiArtifacts({ redactionProfile: "strict" }).map((a) => a.path);
|
|
8611
|
+
for (const name of expected) {
|
|
8612
|
+
if (!files.includes(name)) {
|
|
8613
|
+
throw new Error(`Evidence CI package missing expected file: ${name}`);
|
|
8614
|
+
}
|
|
8615
|
+
}
|
|
8616
|
+
} catch (error) {
|
|
8617
|
+
console.error(
|
|
8618
|
+
`[AgentInspect] evidence package skipped: ${error instanceof Error ? error.message : String(error)}`
|
|
8619
|
+
);
|
|
8620
|
+
}
|
|
8621
|
+
}
|
|
8924
8622
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
8925
8623
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
8926
|
-
await mkdir(
|
|
8927
|
-
await appendFile(
|
|
8624
|
+
await mkdir(path16.dirname(path16.resolve(summaryTarget)), { recursive: true });
|
|
8625
|
+
await appendFile(path16.resolve(summaryTarget), `
|
|
8928
8626
|
${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
8929
8627
|
}
|
|
8930
8628
|
const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
|
|
8931
8629
|
const manifest = {
|
|
8932
|
-
status
|
|
8630
|
+
status,
|
|
8933
8631
|
outputDir,
|
|
8934
8632
|
files: manifestFiles,
|
|
8935
8633
|
trace,
|
|
@@ -8943,10 +8641,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
8943
8641
|
findings: diff?.findings.length ?? 0,
|
|
8944
8642
|
diagnostics: diff?.diagnostics.length ?? 0
|
|
8945
8643
|
},
|
|
8946
|
-
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary:
|
|
8644
|
+
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path16.resolve(summaryTarget) } : {},
|
|
8947
8645
|
note: NOTE
|
|
8948
8646
|
};
|
|
8949
|
-
await writeFile(
|
|
8647
|
+
await writeFile(path16.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
|
|
8950
8648
|
if (options.json === true) {
|
|
8951
8649
|
console.log(writeJson3(manifest).trimEnd());
|
|
8952
8650
|
} else {
|
|
@@ -8988,16 +8686,24 @@ function safetyStatusFromAssess(status) {
|
|
|
8988
8686
|
}
|
|
8989
8687
|
return "UNKNOWN";
|
|
8990
8688
|
}
|
|
8991
|
-
|
|
8689
|
+
function parseBundleFormat(value) {
|
|
8690
|
+
if (value === void 0 || value === "directory" || value === "html" || value === "zip") {
|
|
8691
|
+
return value ?? "directory";
|
|
8692
|
+
}
|
|
8693
|
+
throw new Error(`Unsupported --format "${value}". Use directory, html, or zip.`);
|
|
8694
|
+
}
|
|
8695
|
+
async function resolveOutputDir(options, runIds, cwd, format) {
|
|
8992
8696
|
const out = resolveOutputOption(options);
|
|
8993
8697
|
if (out !== void 0) {
|
|
8994
|
-
const normalized = normalizeBundleOutputPath(out
|
|
8698
|
+
const normalized = normalizeBundleOutputPath(out, {
|
|
8699
|
+
preserveZipExtension: format === "zip"
|
|
8700
|
+
});
|
|
8995
8701
|
try {
|
|
8996
8702
|
const location = resolveWorkspaceLocation(cwd);
|
|
8997
8703
|
const manifest = await readWorkspaceManifestFile(location);
|
|
8998
8704
|
if (manifest.ok && manifest.manifest) {
|
|
8999
|
-
const rel =
|
|
9000
|
-
if (!rel.startsWith("..") && !
|
|
8705
|
+
const rel = path16.relative(location.workspaceDir, normalized);
|
|
8706
|
+
if (!rel.startsWith("..") && !path16.isAbsolute(rel)) {
|
|
9001
8707
|
return resolveInsideWorkspace(location.workspaceDir, rel);
|
|
9002
8708
|
}
|
|
9003
8709
|
}
|
|
@@ -9011,20 +8717,21 @@ async function resolveOutputDir(options, runIds, cwd) {
|
|
|
9011
8717
|
if (manifest.ok && manifest.manifest) {
|
|
9012
8718
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
9013
8719
|
const label = runIds.length === 1 ? sanitizeBundleRunId(runIds[0]) : `multi-${runIds.length}`;
|
|
9014
|
-
|
|
8720
|
+
const base2 = resolveInsideWorkspace(
|
|
9015
8721
|
location.workspaceDir,
|
|
9016
|
-
|
|
8722
|
+
path16.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
|
|
9017
8723
|
);
|
|
8724
|
+
return format === "zip" ? `${base2}.zip` : base2;
|
|
9018
8725
|
}
|
|
9019
8726
|
} catch {
|
|
9020
8727
|
}
|
|
9021
|
-
|
|
8728
|
+
const base = defaultBundleOutputPath(runIds);
|
|
8729
|
+
return format === "zip" ? `${base}.zip` : base;
|
|
9022
8730
|
}
|
|
9023
|
-
|
|
9024
|
-
const
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
files.push(relativePath);
|
|
8731
|
+
function stageBundleFile(relativePath, content, files, packaged) {
|
|
8732
|
+
const safe = assertEvidenceRelativePath(relativePath);
|
|
8733
|
+
if (!files.includes(safe)) files.push(safe);
|
|
8734
|
+
packaged.set(safe, content);
|
|
9028
8735
|
}
|
|
9029
8736
|
function renderBundleIndexHtml(parts) {
|
|
9030
8737
|
const links = parts.runIds.map(
|
|
@@ -9062,6 +8769,7 @@ function extractHtmlBody(content) {
|
|
|
9062
8769
|
}
|
|
9063
8770
|
async function bundleCommand(runIdArg, options = {}) {
|
|
9064
8771
|
const profile = parseBundleProfile(resolveRedactionProfileOption(options));
|
|
8772
|
+
const format = parseBundleFormat(options.format);
|
|
9065
8773
|
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
9066
8774
|
const cwd = process.cwd();
|
|
9067
8775
|
let resolveResult;
|
|
@@ -9087,12 +8795,18 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
9087
8795
|
process.exitCode = 1;
|
|
9088
8796
|
return;
|
|
9089
8797
|
}
|
|
9090
|
-
const outputDir = await resolveOutputDir(options, resolveResult.runIds, cwd);
|
|
8798
|
+
const outputDir = await resolveOutputDir(options, resolveResult.runIds, cwd, format);
|
|
9091
8799
|
const files = [];
|
|
8800
|
+
const packaged = /* @__PURE__ */ new Map();
|
|
9092
8801
|
const checkRuns = [];
|
|
9093
8802
|
const redactionRuns = [];
|
|
9094
8803
|
const htmlByRun = /* @__PURE__ */ new Map();
|
|
9095
8804
|
const redactedJsonlByRun = /* @__PURE__ */ new Map();
|
|
8805
|
+
const artifactTrees = [];
|
|
8806
|
+
const artifactEventsByRun = /* @__PURE__ */ new Map();
|
|
8807
|
+
const findingSummaries = [];
|
|
8808
|
+
const sourceHashes = [];
|
|
8809
|
+
const schemaVersions = /* @__PURE__ */ new Set();
|
|
9096
8810
|
let combinedJsonl = "";
|
|
9097
8811
|
for (const runId of resolveResult.runIds) {
|
|
9098
8812
|
const tracePath = getTraceFilePath(runId, traceDir);
|
|
@@ -9129,6 +8843,14 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
9129
8843
|
}
|
|
9130
8844
|
const sourceSafety = assessOpenedTrace(read, { run: runId });
|
|
9131
8845
|
const sourceStatus = safetyStatusFromAssess(sourceSafety.status);
|
|
8846
|
+
sourceHashes.push({
|
|
8847
|
+
runId,
|
|
8848
|
+
algorithm: "sha256",
|
|
8849
|
+
hash: sha256Hex(rawContent)
|
|
8850
|
+
});
|
|
8851
|
+
for (const version2 of collectTraceSchemaVersions(rawContent)) {
|
|
8852
|
+
schemaVersions.add(version2);
|
|
8853
|
+
}
|
|
9132
8854
|
const redacted = redactTraceContent(rawContent, toReportProfile(profile));
|
|
9133
8855
|
redactedJsonlByRun.set(runId, redacted.content);
|
|
9134
8856
|
combinedJsonl += redacted.content.endsWith("\n") ? redacted.content : `${redacted.content}
|
|
@@ -9142,12 +8864,18 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
9142
8864
|
detectors
|
|
9143
8865
|
});
|
|
9144
8866
|
let artifactSafety = sourceSafety;
|
|
8867
|
+
let artifactTree;
|
|
9145
8868
|
try {
|
|
9146
8869
|
const artifactRead = await openTrace(
|
|
9147
8870
|
{ type: "string", content: redacted.content },
|
|
9148
8871
|
{ format: "agent-inspect-jsonl" }
|
|
9149
8872
|
);
|
|
9150
8873
|
artifactSafety = assessOpenedTrace(artifactRead, { run: runId });
|
|
8874
|
+
artifactTree = artifactRead.runs.find((run) => run.runId === runId) ?? artifactRead.runs[0];
|
|
8875
|
+
artifactEventsByRun.set(
|
|
8876
|
+
runId,
|
|
8877
|
+
artifactRead.events.filter((event) => event.runId === runId)
|
|
8878
|
+
);
|
|
9151
8879
|
} catch {
|
|
9152
8880
|
artifactSafety = {
|
|
9153
8881
|
...sourceSafety,
|
|
@@ -9160,6 +8888,24 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
9160
8888
|
}
|
|
9161
8889
|
};
|
|
9162
8890
|
}
|
|
8891
|
+
for (const finding of artifactSafety.findings) {
|
|
8892
|
+
findingSummaries.push({
|
|
8893
|
+
runId,
|
|
8894
|
+
ruleId: finding.ruleId,
|
|
8895
|
+
severity: finding.severity,
|
|
8896
|
+
message: finding.message,
|
|
8897
|
+
...finding.category !== void 0 ? { category: finding.category } : {},
|
|
8898
|
+
...finding.detector !== void 0 ? { detector: finding.detector } : {},
|
|
8899
|
+
...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
|
|
8900
|
+
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
8901
|
+
});
|
|
8902
|
+
}
|
|
8903
|
+
if (artifactTree) {
|
|
8904
|
+
artifactTrees.push(artifactTree);
|
|
8905
|
+
} else {
|
|
8906
|
+
const selected2 = read.runs.find((run) => run.runId === runId);
|
|
8907
|
+
if (selected2) artifactTrees.push(selected2);
|
|
8908
|
+
}
|
|
9163
8909
|
const status = safetyStatusFromAssess(artifactSafety.status);
|
|
9164
8910
|
checkRuns.push({
|
|
9165
8911
|
runId,
|
|
@@ -9214,80 +8960,223 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
9214
8960
|
totalFindings: redactionRuns.reduce((sum, run) => sum + run.findings, 0),
|
|
9215
8961
|
runs: redactionRuns
|
|
9216
8962
|
};
|
|
9217
|
-
await mkdir(outputDir, { recursive: true });
|
|
9218
8963
|
for (const runId of resolveResult.runIds) {
|
|
9219
8964
|
const jsonl = redactedJsonlByRun.get(runId) ?? "";
|
|
9220
|
-
|
|
9221
|
-
outputDir,
|
|
8965
|
+
stageBundleFile(
|
|
9222
8966
|
bundleRunAssetRelativePath(runId, ".jsonl"),
|
|
9223
8967
|
jsonl,
|
|
9224
|
-
files
|
|
8968
|
+
files,
|
|
8969
|
+
packaged
|
|
9225
8970
|
);
|
|
9226
8971
|
const html = htmlByRun.get(runId) ?? "";
|
|
9227
|
-
|
|
9228
|
-
outputDir,
|
|
8972
|
+
stageBundleFile(
|
|
9229
8973
|
bundleRunAssetRelativePath(runId, ".html"),
|
|
9230
8974
|
runReportWrap(html, runId),
|
|
9231
|
-
files
|
|
8975
|
+
files,
|
|
8976
|
+
packaged
|
|
9232
8977
|
);
|
|
9233
8978
|
}
|
|
9234
8979
|
const primaryRunId = resolveResult.runIds[0];
|
|
9235
8980
|
const traceHtml = resolveResult.runIds.length === 1 ? runReportWrap(htmlByRun.get(primaryRunId) ?? "", primaryRunId) : renderBundleIndexHtml({ runIds: resolveResult.runIds, reports: htmlByRun });
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
"check-results.json",
|
|
9241
|
-
writeJson4(checks2),
|
|
9242
|
-
files
|
|
9243
|
-
);
|
|
9244
|
-
await writeBundleFile(
|
|
9245
|
-
outputDir,
|
|
8981
|
+
stageBundleFile("trace.jsonl", combinedJsonl, files, packaged);
|
|
8982
|
+
stageBundleFile("trace.html", traceHtml, files, packaged);
|
|
8983
|
+
stageBundleFile("check-results.json", writeJson4(checks2), files, packaged);
|
|
8984
|
+
stageBundleFile(
|
|
9246
8985
|
"eval-results.json",
|
|
9247
8986
|
writeJson4(buildPlaceholderArtifact()),
|
|
9248
|
-
files
|
|
8987
|
+
files,
|
|
8988
|
+
packaged
|
|
9249
8989
|
);
|
|
9250
|
-
|
|
9251
|
-
outputDir,
|
|
8990
|
+
stageBundleFile(
|
|
9252
8991
|
"redaction-report.json",
|
|
9253
8992
|
writeJson4(redactionReport),
|
|
9254
|
-
files
|
|
8993
|
+
files,
|
|
8994
|
+
packaged
|
|
9255
8995
|
);
|
|
9256
|
-
|
|
9257
|
-
outputDir,
|
|
8996
|
+
stageBundleFile(
|
|
9258
8997
|
"performance-summary.json",
|
|
9259
8998
|
writeJson4(buildPlaceholderArtifact()),
|
|
9260
|
-
files
|
|
8999
|
+
files,
|
|
9000
|
+
packaged
|
|
9001
|
+
);
|
|
9002
|
+
const summaryPath = "summary.md";
|
|
9003
|
+
const metadataPath = "metadata.json";
|
|
9004
|
+
const aggregateSourceStatus = aggregateBundleSafeStatus(
|
|
9005
|
+
checkRuns.map((run) => run.sourceStatus ?? run.status)
|
|
9261
9006
|
);
|
|
9007
|
+
const metadataFileList = [
|
|
9008
|
+
...files,
|
|
9009
|
+
summaryPath,
|
|
9010
|
+
EVIDENCE_HTML_FILENAME,
|
|
9011
|
+
metadataPath,
|
|
9012
|
+
EVIDENCE_MANIFEST_FILENAME
|
|
9013
|
+
].filter((name, index, all) => all.indexOf(name) === index).sort((a, b) => a.localeCompare(b));
|
|
9262
9014
|
const metadata = buildBundleMetadata({
|
|
9263
9015
|
agentInspectVersion: version,
|
|
9264
9016
|
profile,
|
|
9265
9017
|
resolve: resolveResult,
|
|
9266
9018
|
checks: checks2,
|
|
9267
|
-
files:
|
|
9019
|
+
files: metadataFileList
|
|
9268
9020
|
});
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9021
|
+
const summaryMd = buildBundleSummaryMarkdown({
|
|
9022
|
+
metadata,
|
|
9023
|
+
checks: checks2,
|
|
9024
|
+
redaction: redactionReport
|
|
9025
|
+
});
|
|
9026
|
+
const evidenceHtml = buildEvidenceHtmlShell({
|
|
9027
|
+
title: "AgentInspect evidence",
|
|
9028
|
+
runIds: resolveResult.runIds,
|
|
9029
|
+
assessmentStatus: checks2.aggregateStatus,
|
|
9030
|
+
sourceStatus: aggregateSourceStatus,
|
|
9031
|
+
redactionProfile: profile,
|
|
9032
|
+
verificationPolicy: profile,
|
|
9033
|
+
generatorName: "agent-inspect",
|
|
9034
|
+
generatorVersion: version,
|
|
9035
|
+
createdAt: metadata.createdAt,
|
|
9036
|
+
summaryText: summaryMd,
|
|
9037
|
+
checkSummary: {
|
|
9038
|
+
aggregateStatus: checks2.aggregateStatus,
|
|
9039
|
+
runs: checkRuns
|
|
9040
|
+
},
|
|
9041
|
+
viewBodies: {
|
|
9042
|
+
tree: buildEvidenceTreeViewHtml(artifactTrees),
|
|
9043
|
+
timeline: buildEvidenceTimelineViewHtml(artifactTrees),
|
|
9044
|
+
causal: buildEvidenceCausalFailureViewHtml(artifactTrees),
|
|
9045
|
+
"tools-llm": buildEvidenceToolsLlmViewHtml(artifactTrees),
|
|
9046
|
+
contracts: buildEvidenceContractsViewHtml({
|
|
9047
|
+
aggregateStatus: checks2.aggregateStatus,
|
|
9048
|
+
runs: checkRuns,
|
|
9049
|
+
findingSummaries
|
|
9050
|
+
}),
|
|
9051
|
+
outcomes: buildEvidenceOutcomesViewHtml(
|
|
9052
|
+
resolveResult.runIds.map((id) => ({
|
|
9053
|
+
runId: id,
|
|
9054
|
+
events: artifactEventsByRun.get(id) ?? []
|
|
9055
|
+
}))
|
|
9056
|
+
),
|
|
9057
|
+
circuit: buildEvidenceCircuitViewHtml(),
|
|
9058
|
+
diff: resolveResult.runIds.length >= 2 ? buildEvidenceDiffViewHtml({
|
|
9059
|
+
leftRunId: resolveResult.runIds[0],
|
|
9060
|
+
rightRunId: resolveResult.runIds[1],
|
|
9061
|
+
leftEvents: artifactEventsByRun.get(resolveResult.runIds[0]) ?? [],
|
|
9062
|
+
rightEvents: artifactEventsByRun.get(resolveResult.runIds[1]) ?? []
|
|
9063
|
+
}) : buildEvidenceDiffViewHtml(),
|
|
9064
|
+
safety: buildEvidenceSafetyViewHtml({
|
|
9065
|
+
artifactStatus: checks2.aggregateStatus,
|
|
9066
|
+
sourceStatus: aggregateSourceStatus,
|
|
9067
|
+
redactionProfile: profile,
|
|
9068
|
+
verificationPolicy: profile,
|
|
9069
|
+
redaction: redactionReport,
|
|
9070
|
+
findingSummaries
|
|
9071
|
+
}),
|
|
9072
|
+
provenance: buildEvidenceProvenanceViewHtml({
|
|
9073
|
+
generatorName: "agent-inspect",
|
|
9074
|
+
generatorVersion: version,
|
|
9075
|
+
evidenceFormatVersion: EVIDENCE_FORMAT_VERSION,
|
|
9076
|
+
createdAt: metadata.createdAt,
|
|
9077
|
+
runIds: resolveResult.runIds,
|
|
9078
|
+
traceSchemaVersions: [...schemaVersions].sort((a, b) => a.localeCompare(b)),
|
|
9079
|
+
sourceHashes,
|
|
9080
|
+
packagedFiles: metadataFileList.filter((name) => name !== EVIDENCE_MANIFEST_FILENAME).map((name) => ({ path: name, role: inferEvidenceFileRole(name) }))
|
|
9081
|
+
})
|
|
9082
|
+
}
|
|
9083
|
+
});
|
|
9084
|
+
stageBundleFile(EVIDENCE_HTML_FILENAME, evidenceHtml, files, packaged);
|
|
9085
|
+
stageBundleFile(summaryPath, summaryMd, files, packaged);
|
|
9086
|
+
const metadataJson = writeJson4(metadata);
|
|
9087
|
+
packaged.set(metadataPath, metadataJson);
|
|
9088
|
+
if (!files.includes(metadataPath)) {
|
|
9089
|
+
files.push(metadataPath);
|
|
9090
|
+
}
|
|
9091
|
+
const evidenceFiles = [...packaged.entries()].map(
|
|
9092
|
+
([relativePath, content]) => ({ path: relativePath, content })
|
|
9275
9093
|
);
|
|
9276
|
-
|
|
9277
|
-
|
|
9094
|
+
const evidence = buildEvidenceManifest({
|
|
9095
|
+
generatorVersion: version,
|
|
9096
|
+
runIds: resolveResult.runIds,
|
|
9097
|
+
traceSchemaVersions: [...schemaVersions].sort((a, b) => a.localeCompare(b)),
|
|
9098
|
+
sourceHashes,
|
|
9099
|
+
redactionProfile: profile,
|
|
9100
|
+
verificationPolicy: profile,
|
|
9101
|
+
assessmentStatus: checks2.aggregateStatus,
|
|
9102
|
+
sourceStatus: aggregateSourceStatus,
|
|
9103
|
+
files: evidenceFiles,
|
|
9104
|
+
createdAt: metadata.createdAt
|
|
9105
|
+
});
|
|
9106
|
+
let evidenceJson = serializeEvidenceManifest(evidence);
|
|
9107
|
+
let outputPath = outputDir;
|
|
9108
|
+
if (format === "html") {
|
|
9109
|
+
const htmlContent = packaged.get(EVIDENCE_HTML_FILENAME) ?? evidenceHtml;
|
|
9110
|
+
const htmlEvidence = buildEvidenceManifest({
|
|
9111
|
+
generatorVersion: version,
|
|
9112
|
+
runIds: resolveResult.runIds,
|
|
9113
|
+
traceSchemaVersions: [...schemaVersions].sort((a, b) => a.localeCompare(b)),
|
|
9114
|
+
sourceHashes,
|
|
9115
|
+
redactionProfile: profile,
|
|
9116
|
+
verificationPolicy: profile,
|
|
9117
|
+
assessmentStatus: checks2.aggregateStatus,
|
|
9118
|
+
sourceStatus: aggregateSourceStatus,
|
|
9119
|
+
files: [{ path: EVIDENCE_HTML_FILENAME, content: htmlContent }],
|
|
9120
|
+
createdAt: metadata.createdAt
|
|
9121
|
+
});
|
|
9122
|
+
evidenceJson = serializeEvidenceManifest(htmlEvidence);
|
|
9123
|
+
let htmlPath = outputDir;
|
|
9124
|
+
let sidecarDir = outputDir;
|
|
9125
|
+
if (outputDir.toLowerCase().endsWith(".html")) {
|
|
9126
|
+
htmlPath = outputDir;
|
|
9127
|
+
sidecarDir = path16.dirname(outputDir);
|
|
9128
|
+
} else {
|
|
9129
|
+
await mkdir(outputDir, { recursive: true });
|
|
9130
|
+
htmlPath = path16.join(outputDir, EVIDENCE_HTML_FILENAME);
|
|
9131
|
+
sidecarDir = outputDir;
|
|
9132
|
+
}
|
|
9133
|
+
await mkdir(sidecarDir, { recursive: true });
|
|
9134
|
+
await writeFile(htmlPath, htmlContent, "utf-8");
|
|
9135
|
+
await writeFile(path16.join(sidecarDir, EVIDENCE_MANIFEST_FILENAME), evidenceJson, "utf-8");
|
|
9136
|
+
outputPath = htmlPath;
|
|
9137
|
+
} else if (format === "zip") {
|
|
9138
|
+
const zipPath = outputDir.toLowerCase().endsWith(".zip") ? outputDir : `${outputDir}.zip`;
|
|
9139
|
+
const zipParent = path16.dirname(zipPath);
|
|
9140
|
+
await mkdir(zipParent, { recursive: true });
|
|
9141
|
+
const entries = [
|
|
9142
|
+
...[...packaged.entries()].map(([relativePath, content]) => ({
|
|
9143
|
+
path: relativePath,
|
|
9144
|
+
content
|
|
9145
|
+
})),
|
|
9146
|
+
{ path: EVIDENCE_MANIFEST_FILENAME, content: evidenceJson }
|
|
9147
|
+
];
|
|
9148
|
+
const archive = buildZipArchive(entries);
|
|
9149
|
+
await writeFile(zipPath, archive);
|
|
9150
|
+
outputPath = zipPath;
|
|
9151
|
+
} else {
|
|
9152
|
+
await mkdir(outputDir, { recursive: true });
|
|
9153
|
+
for (const [relativePath, content] of packaged.entries()) {
|
|
9154
|
+
const outPath = assertBundlePathContained(outputDir, relativePath);
|
|
9155
|
+
await mkdir(path16.dirname(outPath), { recursive: true });
|
|
9156
|
+
await writeFile(outPath, content, "utf-8");
|
|
9157
|
+
}
|
|
9158
|
+
await writeFile(path16.join(outputDir, EVIDENCE_MANIFEST_FILENAME), evidenceJson, "utf-8");
|
|
9159
|
+
if (!files.includes(EVIDENCE_MANIFEST_FILENAME)) {
|
|
9160
|
+
files.push(EVIDENCE_MANIFEST_FILENAME);
|
|
9161
|
+
}
|
|
9162
|
+
outputPath = outputDir;
|
|
9163
|
+
}
|
|
9278
9164
|
if (options.json) {
|
|
9279
9165
|
console.log(
|
|
9280
9166
|
writeJson4({
|
|
9281
9167
|
ok: true,
|
|
9282
|
-
|
|
9168
|
+
format,
|
|
9169
|
+
outputDir: outputPath,
|
|
9283
9170
|
metadata,
|
|
9171
|
+
evidence: format === "html" ? JSON.parse(evidenceJson) : evidence,
|
|
9284
9172
|
checks: checks2,
|
|
9285
9173
|
redaction: redactionReport
|
|
9286
9174
|
}).trimEnd()
|
|
9287
9175
|
);
|
|
9288
9176
|
return;
|
|
9289
9177
|
}
|
|
9290
|
-
console.log(`Bundle written to ${
|
|
9178
|
+
console.log(`Bundle written to ${outputPath}`);
|
|
9179
|
+
console.log(`Format: ${format}`);
|
|
9291
9180
|
console.log(`Safe status: ${metadata.safeStatus}`);
|
|
9292
9181
|
console.log(`Runs: ${resolveResult.runIds.join(", ")}`);
|
|
9293
9182
|
console.log(`Files: ${metadata.files.length}`);
|
|
@@ -9307,68 +9196,179 @@ ${body}
|
|
|
9307
9196
|
</html>
|
|
9308
9197
|
`;
|
|
9309
9198
|
}
|
|
9310
|
-
function
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9199
|
+
function writeJson5(value) {
|
|
9200
|
+
return `${JSON.stringify(value, null, 2)}
|
|
9201
|
+
`;
|
|
9202
|
+
}
|
|
9203
|
+
async function bundleVerifyCommand(targetPath, options = {}) {
|
|
9204
|
+
const root = path16.resolve(targetPath.trim() || ".");
|
|
9205
|
+
const result = await verifyEvidenceDirectory(root, {
|
|
9206
|
+
unexpectedFiles: options.unexpected ?? "fail"
|
|
9207
|
+
});
|
|
9208
|
+
if (options.json) {
|
|
9209
|
+
console.log(
|
|
9210
|
+
writeJson5({
|
|
9211
|
+
ok: result.ok,
|
|
9212
|
+
status: result.status,
|
|
9213
|
+
root: result.root,
|
|
9214
|
+
checkedFiles: result.checkedFiles,
|
|
9215
|
+
issues: result.issues,
|
|
9216
|
+
assessment: result.manifest?.assessment,
|
|
9217
|
+
generator: result.manifest?.generator
|
|
9218
|
+
}).trimEnd()
|
|
9219
|
+
);
|
|
9220
|
+
} else if (result.ok) {
|
|
9221
|
+
console.log(`Evidence verify: pass (${result.checkedFiles} file(s) checked)`);
|
|
9222
|
+
console.log(`Root: ${result.root}`);
|
|
9223
|
+
if (result.manifest?.assessment?.status) {
|
|
9224
|
+
console.log(`Assessment: ${result.manifest.assessment.status}`);
|
|
9225
|
+
}
|
|
9226
|
+
} else {
|
|
9227
|
+
console.error(`Evidence verify: fail (${result.issues.length} issue(s))`);
|
|
9228
|
+
console.error(`Root: ${result.root}`);
|
|
9229
|
+
for (const issue of result.issues) {
|
|
9230
|
+
console.error(`- [${issue.severity}] ${issue.code}: ${issue.message}`);
|
|
9231
|
+
}
|
|
9321
9232
|
}
|
|
9322
|
-
if (
|
|
9323
|
-
|
|
9324
|
-
code: "invalid_artifact_path",
|
|
9325
|
-
severity: "error",
|
|
9326
|
-
message: "Reporter artifact path must not contain null bytes.",
|
|
9327
|
-
target: rawPath
|
|
9328
|
-
});
|
|
9329
|
-
return { ok: false, outputDir, diagnostics };
|
|
9233
|
+
if (!result.ok) {
|
|
9234
|
+
process.exitCode = 1;
|
|
9330
9235
|
}
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9236
|
+
}
|
|
9237
|
+
var CLIENTS = [
|
|
9238
|
+
"cursor",
|
|
9239
|
+
"claude-code",
|
|
9240
|
+
"codex",
|
|
9241
|
+
"gemini"
|
|
9242
|
+
];
|
|
9243
|
+
function isMcpConfigureClient(value) {
|
|
9244
|
+
return CLIENTS.includes(value);
|
|
9245
|
+
}
|
|
9246
|
+
function buildServerBlock(traceDir) {
|
|
9247
|
+
return {
|
|
9248
|
+
command: "npx",
|
|
9249
|
+
args: ["-y", "@agent-inspect/mcp-server", "--dir", traceDir],
|
|
9250
|
+
env: {
|
|
9251
|
+
AGENT_INSPECT_TRACE_DIR: traceDir,
|
|
9252
|
+
AGENT_INSPECT_MCP_REDACTION_PROFILE: "share"
|
|
9253
|
+
}
|
|
9254
|
+
};
|
|
9255
|
+
}
|
|
9256
|
+
function buildMcpClientConfig(client, traceDir) {
|
|
9257
|
+
const server = buildServerBlock(traceDir);
|
|
9258
|
+
switch (client) {
|
|
9259
|
+
case "cursor":
|
|
9260
|
+
return {
|
|
9261
|
+
mcpServers: {
|
|
9262
|
+
"agent-inspect": server
|
|
9263
|
+
}
|
|
9264
|
+
};
|
|
9265
|
+
case "claude-code":
|
|
9266
|
+
return {
|
|
9267
|
+
mcpServers: {
|
|
9268
|
+
"agent-inspect": server
|
|
9269
|
+
}
|
|
9270
|
+
};
|
|
9271
|
+
case "codex":
|
|
9272
|
+
return {
|
|
9273
|
+
mcp_servers: {
|
|
9274
|
+
"agent-inspect": {
|
|
9275
|
+
command: server.command,
|
|
9276
|
+
args: server.args,
|
|
9277
|
+
env: server.env
|
|
9278
|
+
}
|
|
9279
|
+
}
|
|
9280
|
+
};
|
|
9281
|
+
case "gemini":
|
|
9282
|
+
return {
|
|
9283
|
+
mcpServers: {
|
|
9284
|
+
"agent-inspect": server
|
|
9285
|
+
}
|
|
9286
|
+
};
|
|
9287
|
+
default: {
|
|
9288
|
+
const _exhaustive = client;
|
|
9289
|
+
return _exhaustive;
|
|
9290
|
+
}
|
|
9339
9291
|
}
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
if (
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9292
|
+
}
|
|
9293
|
+
function resolveTargetPath(client, projectLocal) {
|
|
9294
|
+
if (projectLocal) {
|
|
9295
|
+
switch (client) {
|
|
9296
|
+
case "cursor":
|
|
9297
|
+
return path16.join(".cursor", "mcp.json");
|
|
9298
|
+
case "claude-code":
|
|
9299
|
+
return path16.join(".mcp.json");
|
|
9300
|
+
case "codex":
|
|
9301
|
+
return path16.join(".codex", "config.toml.json");
|
|
9302
|
+
case "gemini":
|
|
9303
|
+
return path16.join(".gemini", "settings.json");
|
|
9304
|
+
}
|
|
9350
9305
|
}
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
return { ok: false, outputDir, diagnostics };
|
|
9306
|
+
switch (client) {
|
|
9307
|
+
case "cursor":
|
|
9308
|
+
return path16.join("~", ".cursor", "mcp.json");
|
|
9309
|
+
case "claude-code":
|
|
9310
|
+
return path16.join("~", ".claude.json");
|
|
9311
|
+
case "codex":
|
|
9312
|
+
return path16.join("~", ".codex", "config.toml");
|
|
9313
|
+
case "gemini":
|
|
9314
|
+
return path16.join("~", ".gemini", "settings.json");
|
|
9361
9315
|
}
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9316
|
+
}
|
|
9317
|
+
async function mcpConfigureCommand(options) {
|
|
9318
|
+
if (!isMcpConfigureClient(options.client)) {
|
|
9319
|
+
throw new Error(
|
|
9320
|
+
`Unsupported client "${String(options.client)}". Use: ${CLIENTS.join(", ")}`
|
|
9321
|
+
);
|
|
9322
|
+
}
|
|
9323
|
+
const traceDir = options.dir?.trim() || ".agent-inspect";
|
|
9324
|
+
const projectLocal = Boolean(options.projectLocal);
|
|
9325
|
+
const write = Boolean(options.write);
|
|
9326
|
+
const yes = Boolean(options.yes);
|
|
9327
|
+
const dryRun = !(write && yes && projectLocal);
|
|
9328
|
+
const config = buildMcpClientConfig(options.client, traceDir);
|
|
9329
|
+
const targetPath = resolveTargetPath(options.client, projectLocal);
|
|
9330
|
+
const notes = [
|
|
9331
|
+
"Read-only MCP server; no network upload; share redaction by default.",
|
|
9332
|
+
"Dry-run by default. Pass --project-local --write --yes to write a project file.",
|
|
9333
|
+
"User-level paths are never auto-written by this command.",
|
|
9334
|
+
"Remove the agent-inspect server block to uninstall."
|
|
9335
|
+
];
|
|
9336
|
+
let wrote = false;
|
|
9337
|
+
if (!dryRun && projectLocal) {
|
|
9338
|
+
const abs = path16.resolve(targetPath);
|
|
9339
|
+
await mkdir(path16.dirname(abs), { recursive: true });
|
|
9340
|
+
await writeFile(abs, `${JSON.stringify(config, null, 2)}
|
|
9341
|
+
`, "utf8");
|
|
9342
|
+
wrote = true;
|
|
9343
|
+
}
|
|
9344
|
+
const result = {
|
|
9345
|
+
client: options.client,
|
|
9346
|
+
dryRun,
|
|
9347
|
+
wrote,
|
|
9348
|
+
targetPath,
|
|
9349
|
+
config,
|
|
9350
|
+
notes
|
|
9368
9351
|
};
|
|
9352
|
+
if (options.json) {
|
|
9353
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
9354
|
+
`);
|
|
9355
|
+
} else {
|
|
9356
|
+
process.stdout.write(
|
|
9357
|
+
[
|
|
9358
|
+
`Client: ${result.client}`,
|
|
9359
|
+
`Mode: ${result.dryRun ? "dry-run" : "write"}`,
|
|
9360
|
+
`Target: ${result.targetPath}`,
|
|
9361
|
+
`Wrote: ${result.wrote}`,
|
|
9362
|
+
"",
|
|
9363
|
+
JSON.stringify(result.config, null, 2),
|
|
9364
|
+
"",
|
|
9365
|
+
...result.notes.map((note) => `- ${note}`),
|
|
9366
|
+
""
|
|
9367
|
+
].join("\n")
|
|
9368
|
+
);
|
|
9369
|
+
}
|
|
9370
|
+
return result;
|
|
9369
9371
|
}
|
|
9370
|
-
|
|
9371
|
-
// packages/cli/src/ci-summary.ts
|
|
9372
9372
|
var NOTE2 = "Generated locally by AgentInspect from reporter artifact manifests. Trace contents are not embedded.";
|
|
9373
9373
|
var MAX_TEXT = 180;
|
|
9374
9374
|
var FRAMEWORKS = /* @__PURE__ */ new Set(["vitest", "jest", "manual"]);
|
|
@@ -9384,7 +9384,7 @@ function stable7(value) {
|
|
|
9384
9384
|
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable7(record[key])])
|
|
9385
9385
|
);
|
|
9386
9386
|
}
|
|
9387
|
-
function
|
|
9387
|
+
function writeJson6(value) {
|
|
9388
9388
|
return `${JSON.stringify(stable7(value), null, 2)}
|
|
9389
9389
|
`;
|
|
9390
9390
|
}
|
|
@@ -9496,23 +9496,23 @@ function readManifestDocument(value) {
|
|
|
9496
9496
|
};
|
|
9497
9497
|
}
|
|
9498
9498
|
function cwdRelative(filePath) {
|
|
9499
|
-
const relative =
|
|
9500
|
-
if (relative === "" || relative.startsWith("../") ||
|
|
9501
|
-
return
|
|
9499
|
+
const relative = path16.relative(process.cwd(), path16.resolve(filePath)).replace(/\\/g, "/");
|
|
9500
|
+
if (relative === "" || relative.startsWith("../") || path16.isAbsolute(relative)) {
|
|
9501
|
+
return path16.basename(filePath);
|
|
9502
9502
|
}
|
|
9503
9503
|
return relative;
|
|
9504
9504
|
}
|
|
9505
9505
|
async function readReporterManifest(filePath) {
|
|
9506
|
-
const absolute =
|
|
9506
|
+
const absolute = path16.resolve(filePath);
|
|
9507
9507
|
const raw = await readFile(absolute, "utf-8");
|
|
9508
9508
|
const document = readManifestDocument(JSON.parse(raw));
|
|
9509
9509
|
const manifest = document.manifest;
|
|
9510
9510
|
const results = manifest.results.map((result) => ({
|
|
9511
9511
|
testId: safeText(result.testId),
|
|
9512
9512
|
name: safeText(result.name),
|
|
9513
|
-
...result.file === void 0 ? {} : { file: safeText(
|
|
9513
|
+
...result.file === void 0 ? {} : { file: safeText(path16.basename(result.file)) },
|
|
9514
9514
|
status: result.status,
|
|
9515
|
-
...result.tracePath === void 0 ? {} : { tracePath: safeText(
|
|
9515
|
+
...result.tracePath === void 0 ? {} : { tracePath: safeText(path16.basename(result.tracePath)) },
|
|
9516
9516
|
artifacts: result.artifacts,
|
|
9517
9517
|
diagnostics: result.diagnostics
|
|
9518
9518
|
}));
|
|
@@ -9651,20 +9651,20 @@ async function ciSummaryCommand(manifestPaths, options = {}) {
|
|
|
9651
9651
|
return;
|
|
9652
9652
|
}
|
|
9653
9653
|
const markdown = renderMarkdown2(result);
|
|
9654
|
-
const outputPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
9654
|
+
const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path16.resolve(options.output.trim()) : void 0;
|
|
9655
9655
|
if (outputPath !== void 0) {
|
|
9656
|
-
await mkdir(
|
|
9656
|
+
await mkdir(path16.dirname(outputPath), { recursive: true });
|
|
9657
9657
|
await writeFile(outputPath, markdown, "utf-8");
|
|
9658
9658
|
}
|
|
9659
9659
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
9660
9660
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
9661
|
-
const summaryPath =
|
|
9662
|
-
await mkdir(
|
|
9661
|
+
const summaryPath = path16.resolve(summaryTarget);
|
|
9662
|
+
await mkdir(path16.dirname(summaryPath), { recursive: true });
|
|
9663
9663
|
await appendFile(summaryPath, `
|
|
9664
9664
|
${markdown}`, "utf-8");
|
|
9665
9665
|
}
|
|
9666
9666
|
if (options.json === true) {
|
|
9667
|
-
console.log(
|
|
9667
|
+
console.log(writeJson6(result).trimEnd());
|
|
9668
9668
|
} else if (outputPath !== void 0) {
|
|
9669
9669
|
console.log(`Wrote AgentInspect CI summary to ${outputPath}`);
|
|
9670
9670
|
console.log(`Status: ${result.status}`);
|
|
@@ -9807,7 +9807,7 @@ jobs:
|
|
|
9807
9807
|
}
|
|
9808
9808
|
async function planInit(options = {}) {
|
|
9809
9809
|
const framework = normalizeFramework(options.framework);
|
|
9810
|
-
const cwd =
|
|
9810
|
+
const cwd = path16.resolve(options.cwd ?? process.cwd());
|
|
9811
9811
|
const demoPath = framework === "custom" ? "examples/agent-inspect-demo.mjs" : `examples/agent-inspect-${framework}-demo.mjs`;
|
|
9812
9812
|
const candidates = [
|
|
9813
9813
|
{ rel: CONFIG_FILE, content: configTemplate(framework) },
|
|
@@ -9822,7 +9822,7 @@ async function planInit(options = {}) {
|
|
|
9822
9822
|
}
|
|
9823
9823
|
const files = [];
|
|
9824
9824
|
for (const candidate of candidates) {
|
|
9825
|
-
const abs =
|
|
9825
|
+
const abs = path16.join(cwd, candidate.rel);
|
|
9826
9826
|
try {
|
|
9827
9827
|
await access(abs);
|
|
9828
9828
|
files.push({
|
|
@@ -9843,12 +9843,12 @@ async function writePlannedFiles(plan, cwd, options) {
|
|
|
9843
9843
|
if (entry.action === "skip") {
|
|
9844
9844
|
continue;
|
|
9845
9845
|
}
|
|
9846
|
-
const abs =
|
|
9846
|
+
const abs = path16.join(cwd, entry.path);
|
|
9847
9847
|
if (options.dryRun) {
|
|
9848
9848
|
written.push(entry.path);
|
|
9849
9849
|
continue;
|
|
9850
9850
|
}
|
|
9851
|
-
await mkdir(
|
|
9851
|
+
await mkdir(path16.dirname(abs), { recursive: true });
|
|
9852
9852
|
const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate() : demoTemplate(plan.framework);
|
|
9853
9853
|
await writeFile(abs, content, "utf-8");
|
|
9854
9854
|
written.push(entry.path);
|
|
@@ -9856,7 +9856,7 @@ async function writePlannedFiles(plan, cwd, options) {
|
|
|
9856
9856
|
return written;
|
|
9857
9857
|
}
|
|
9858
9858
|
async function initCommand(options = {}) {
|
|
9859
|
-
const cwd =
|
|
9859
|
+
const cwd = path16.resolve(options.cwd ?? process.cwd());
|
|
9860
9860
|
try {
|
|
9861
9861
|
const plan = await planInit({ ...options, cwd });
|
|
9862
9862
|
const toWrite = plan.files.filter((file) => file.action === "create").map((f) => f.path);
|
|
@@ -9947,7 +9947,7 @@ function envCheck(name, optional = true) {
|
|
|
9947
9947
|
};
|
|
9948
9948
|
}
|
|
9949
9949
|
async function traceDirWritable(traceDir) {
|
|
9950
|
-
const resolved =
|
|
9950
|
+
const resolved = path16.resolve(traceDir);
|
|
9951
9951
|
try {
|
|
9952
9952
|
await mkdir(resolved, { recursive: true });
|
|
9953
9953
|
await access(resolved, constants.W_OK);
|
|
@@ -9968,10 +9968,10 @@ async function traceDirWritable(traceDir) {
|
|
|
9968
9968
|
}
|
|
9969
9969
|
}
|
|
9970
9970
|
function readPackageVersionNearEntry(entryPath, packageName) {
|
|
9971
|
-
let dir =
|
|
9972
|
-
const { root } =
|
|
9971
|
+
let dir = path16.dirname(path16.resolve(entryPath));
|
|
9972
|
+
const { root } = path16.parse(dir);
|
|
9973
9973
|
while (true) {
|
|
9974
|
-
const candidate =
|
|
9974
|
+
const candidate = path16.join(dir, "package.json");
|
|
9975
9975
|
if (existsSync(candidate)) {
|
|
9976
9976
|
try {
|
|
9977
9977
|
const pkg = JSON.parse(readFileSync(candidate, "utf8"));
|
|
@@ -9982,14 +9982,14 @@ function readPackageVersionNearEntry(entryPath, packageName) {
|
|
|
9982
9982
|
}
|
|
9983
9983
|
}
|
|
9984
9984
|
if (dir === root) break;
|
|
9985
|
-
const parent =
|
|
9985
|
+
const parent = path16.dirname(dir);
|
|
9986
9986
|
if (parent === dir) break;
|
|
9987
9987
|
dir = parent;
|
|
9988
9988
|
}
|
|
9989
9989
|
return void 0;
|
|
9990
9990
|
}
|
|
9991
9991
|
function resolveInstalledPackage(cwd, name) {
|
|
9992
|
-
const require2 = createRequire(
|
|
9992
|
+
const require2 = createRequire(path16.join(cwd, "package.json"));
|
|
9993
9993
|
try {
|
|
9994
9994
|
const entry = require2.resolve(name);
|
|
9995
9995
|
let version2;
|
|
@@ -10087,7 +10087,7 @@ function versionMismatchCheck(cwd) {
|
|
|
10087
10087
|
};
|
|
10088
10088
|
}
|
|
10089
10089
|
async function runDoctorChecks(options = {}) {
|
|
10090
|
-
const cwd =
|
|
10090
|
+
const cwd = path16.resolve(options.cwd ?? process2.cwd());
|
|
10091
10091
|
const traceDir = options.traceDir?.trim() || process2.env.AGENT_INSPECT_TRACE_DIR?.trim() || ".agent-inspect";
|
|
10092
10092
|
const checks2 = [
|
|
10093
10093
|
nodeVersionCheck(),
|
|
@@ -10327,7 +10327,7 @@ function parsePluginManifest(input3) {
|
|
|
10327
10327
|
};
|
|
10328
10328
|
}
|
|
10329
10329
|
async function readPluginManifestFile(packageDir) {
|
|
10330
|
-
const manifestPath =
|
|
10330
|
+
const manifestPath = path16.join(packageDir, PLUGIN_MANIFEST_FILENAME);
|
|
10331
10331
|
try {
|
|
10332
10332
|
const raw = await readFile(manifestPath, "utf8");
|
|
10333
10333
|
const parsed = parsePluginManifest(JSON.parse(raw));
|
|
@@ -10353,7 +10353,7 @@ async function indexIsStale(snapshot, traceDir) {
|
|
|
10353
10353
|
const files = await td.list();
|
|
10354
10354
|
for (const file of files) {
|
|
10355
10355
|
const stats = await td.getFileStats(file);
|
|
10356
|
-
if (stats.mtimeMs > builtMs) return true;
|
|
10356
|
+
if (Math.floor(stats.mtimeMs) > builtMs) return true;
|
|
10357
10357
|
}
|
|
10358
10358
|
return false;
|
|
10359
10359
|
}
|
|
@@ -10401,7 +10401,7 @@ function createTraceDirectoryIndexer() {
|
|
|
10401
10401
|
// packages/cli/src/plugins.ts
|
|
10402
10402
|
async function readPackageName(packageDir) {
|
|
10403
10403
|
try {
|
|
10404
|
-
const raw = await readFile(
|
|
10404
|
+
const raw = await readFile(path16.join(packageDir, "package.json"), "utf8");
|
|
10405
10405
|
const parsed = JSON.parse(raw);
|
|
10406
10406
|
return typeof parsed.name === "string" ? parsed.name : void 0;
|
|
10407
10407
|
} catch {
|
|
@@ -10409,7 +10409,7 @@ async function readPackageName(packageDir) {
|
|
|
10409
10409
|
}
|
|
10410
10410
|
}
|
|
10411
10411
|
async function discoverPlugins(cwd = process.cwd()) {
|
|
10412
|
-
const nodeModules =
|
|
10412
|
+
const nodeModules = path16.join(cwd, "node_modules");
|
|
10413
10413
|
const found = [];
|
|
10414
10414
|
let entries;
|
|
10415
10415
|
try {
|
|
@@ -10420,7 +10420,7 @@ async function discoverPlugins(cwd = process.cwd()) {
|
|
|
10420
10420
|
for (const entry of entries.sort()) {
|
|
10421
10421
|
if (entry.startsWith("@")) continue;
|
|
10422
10422
|
if (!isPluginPackageName(entry)) continue;
|
|
10423
|
-
const packageDir =
|
|
10423
|
+
const packageDir = path16.join(nodeModules, entry);
|
|
10424
10424
|
const manifestRead = await readPluginManifestFile(packageDir);
|
|
10425
10425
|
found.push({
|
|
10426
10426
|
packageName: entry,
|
|
@@ -10433,7 +10433,7 @@ async function discoverPlugins(cwd = process.cwd()) {
|
|
|
10433
10433
|
return found;
|
|
10434
10434
|
}
|
|
10435
10435
|
async function validatePluginPackage(packageRef, cwd = process.cwd()) {
|
|
10436
|
-
const packageDir =
|
|
10436
|
+
const packageDir = path16.isAbsolute(packageRef) ? packageRef : path16.join(cwd, "node_modules", packageRef);
|
|
10437
10437
|
const packageName = await readPackageName(packageDir) ?? packageRef;
|
|
10438
10438
|
const errors = [];
|
|
10439
10439
|
const warnings = [];
|
|
@@ -10509,7 +10509,7 @@ async function pluginsValidateCommand(packageRef, cwd) {
|
|
|
10509
10509
|
}
|
|
10510
10510
|
var INDEX_FILENAME = ".agent-inspect-index.json";
|
|
10511
10511
|
function traceIndexPath(traceDir) {
|
|
10512
|
-
return
|
|
10512
|
+
return path16.join(traceDir, INDEX_FILENAME);
|
|
10513
10513
|
}
|
|
10514
10514
|
function parseMaxEntries(raw) {
|
|
10515
10515
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
@@ -10622,7 +10622,7 @@ function isModuleNotFound4(e) {
|
|
|
10622
10622
|
}
|
|
10623
10623
|
async function loadIndexSqlite() {
|
|
10624
10624
|
try {
|
|
10625
|
-
return await import('./src-
|
|
10625
|
+
return await import('./src-G7PM24W2.mjs');
|
|
10626
10626
|
} catch (e) {
|
|
10627
10627
|
if (isModuleNotFound4(e)) {
|
|
10628
10628
|
console.error(
|
|
@@ -10652,7 +10652,7 @@ async function newestTraceMtimeMs2(traceDir) {
|
|
|
10652
10652
|
for (const file of files) {
|
|
10653
10653
|
if (!file.endsWith(".jsonl")) continue;
|
|
10654
10654
|
try {
|
|
10655
|
-
const s = await stat(
|
|
10655
|
+
const s = await stat(path16.join(traceDir, file));
|
|
10656
10656
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
10657
10657
|
} catch {
|
|
10658
10658
|
}
|
|
@@ -10904,11 +10904,11 @@ function printJson5(value) {
|
|
|
10904
10904
|
console.log(JSON.stringify(value, null, 2));
|
|
10905
10905
|
}
|
|
10906
10906
|
function resolveCwd(options) {
|
|
10907
|
-
return
|
|
10907
|
+
return path16.resolve(options.cwd ?? process.cwd());
|
|
10908
10908
|
}
|
|
10909
10909
|
async function suiteInitCommand(options = {}) {
|
|
10910
10910
|
const cwd = resolveCwd(options);
|
|
10911
|
-
const configPath =
|
|
10911
|
+
const configPath = path16.join(cwd, DEFAULT_CONFIG_FILENAME);
|
|
10912
10912
|
const template = options.template?.trim();
|
|
10913
10913
|
const suiteConfig = template !== void 0 && template !== "" ? resolveSuiteTemplate(template) : defaultSuiteConfigTemplate();
|
|
10914
10914
|
if (options.dryRun) {
|
|
@@ -11027,13 +11027,13 @@ async function suiteListCommand(options = {}) {
|
|
|
11027
11027
|
}
|
|
11028
11028
|
async function writeSuiteArtifact(result, configOutputDir, options) {
|
|
11029
11029
|
const cwd = resolveCwd(options);
|
|
11030
|
-
const outputDir =
|
|
11030
|
+
const outputDir = path16.resolve(
|
|
11031
11031
|
cwd,
|
|
11032
11032
|
options.output ?? configOutputDir ?? DEFAULT_SUITE_ARTIFACTS_DIR
|
|
11033
11033
|
);
|
|
11034
11034
|
await mkdir(outputDir, { recursive: true });
|
|
11035
11035
|
const stamp = result.startedAt.replace(/[:.]/g, "-");
|
|
11036
|
-
const filePath =
|
|
11036
|
+
const filePath = path16.join(outputDir, `${result.suiteName}-${stamp}.json`);
|
|
11037
11037
|
await writeFile(filePath, `${JSON.stringify(result, null, 2)}
|
|
11038
11038
|
`, "utf-8");
|
|
11039
11039
|
return filePath;
|
|
@@ -11091,7 +11091,7 @@ async function loadSuiteResultFromInput(options) {
|
|
|
11091
11091
|
if (options.input === void 0 || options.input.trim() === "") {
|
|
11092
11092
|
throw new Error("Pass --input <suite-run.json> from a prior suite run.");
|
|
11093
11093
|
}
|
|
11094
|
-
const inputPath =
|
|
11094
|
+
const inputPath = path16.resolve(cwd, options.input.trim());
|
|
11095
11095
|
const raw = await readFile(inputPath, "utf-8");
|
|
11096
11096
|
return JSON.parse(raw);
|
|
11097
11097
|
}
|
|
@@ -11125,9 +11125,9 @@ function normalizeMetrics(raw) {
|
|
|
11125
11125
|
}
|
|
11126
11126
|
async function writeArtifacts(result, outputDir) {
|
|
11127
11127
|
await mkdir(outputDir, { recursive: true });
|
|
11128
|
-
const jsonPath =
|
|
11129
|
-
const markdownPath =
|
|
11130
|
-
const htmlPath =
|
|
11128
|
+
const jsonPath = path16.join(outputDir, "cohort-results.json");
|
|
11129
|
+
const markdownPath = path16.join(outputDir, "cohort-summary.md");
|
|
11130
|
+
const htmlPath = path16.join(outputDir, "cohort-report.html");
|
|
11131
11131
|
await writeFile(jsonPath, `${renderCohortReport(result, { format: "json" })}
|
|
11132
11132
|
`, "utf-8");
|
|
11133
11133
|
await writeFile(
|
|
@@ -11155,7 +11155,7 @@ async function cohortCommand(options = {}) {
|
|
|
11155
11155
|
const format = options.format ?? (options.json ? "json" : "markdown");
|
|
11156
11156
|
let artifacts;
|
|
11157
11157
|
if (options.output !== void 0 && options.output.trim() !== "") {
|
|
11158
|
-
artifacts = await writeArtifacts(result,
|
|
11158
|
+
artifacts = await writeArtifacts(result, path16.resolve(options.output.trim()));
|
|
11159
11159
|
}
|
|
11160
11160
|
if (options.json || format === "json") {
|
|
11161
11161
|
console.log(
|
|
@@ -11225,11 +11225,11 @@ function parseDuration2(value) {
|
|
|
11225
11225
|
async function writeArtifacts2(result, outputDir) {
|
|
11226
11226
|
await mkdir(outputDir, { recursive: true });
|
|
11227
11227
|
const paths = {
|
|
11228
|
-
jsonPath:
|
|
11229
|
-
markdownPath:
|
|
11230
|
-
htmlPath:
|
|
11231
|
-
junitPath:
|
|
11232
|
-
githubPath:
|
|
11228
|
+
jsonPath: path16.join(outputDir, "gate-results.json"),
|
|
11229
|
+
markdownPath: path16.join(outputDir, "gate-summary.md"),
|
|
11230
|
+
htmlPath: path16.join(outputDir, "gate-report.html"),
|
|
11231
|
+
junitPath: path16.join(outputDir, "junit.xml"),
|
|
11232
|
+
githubPath: path16.join(outputDir, "github-step-summary.md")
|
|
11233
11233
|
};
|
|
11234
11234
|
await writeFile(
|
|
11235
11235
|
paths.jsonPath,
|
|
@@ -11297,7 +11297,7 @@ async function gateCommand(options = {}) {
|
|
|
11297
11297
|
const result = await runGate(runs, gateOptions);
|
|
11298
11298
|
let artifacts;
|
|
11299
11299
|
if (options.output !== void 0 && options.output.trim() !== "") {
|
|
11300
|
-
artifacts = await writeArtifacts2(result,
|
|
11300
|
+
artifacts = await writeArtifacts2(result, path16.resolve(options.output.trim()));
|
|
11301
11301
|
}
|
|
11302
11302
|
if (options.json || format === "json") {
|
|
11303
11303
|
console.log(JSON.stringify({ ...result, artifacts: artifacts ?? null }, null, 2));
|
|
@@ -11552,19 +11552,26 @@ function createCliProgram() {
|
|
|
11552
11552
|
"openinference-json",
|
|
11553
11553
|
"otlp-json"
|
|
11554
11554
|
])
|
|
11555
|
-
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--baseline <trace-path-or-run-id>", "optional baseline trace for diff artifacts").option("--baseline-run <run-id>", "select a run from the baseline trace").option("--github-summary <path>", "append a safe summary to this file, e.g. GITHUB_STEP_SUMMARY").option("--json", "print deterministic JSON manifest").action((target, opts) => {
|
|
11555
|
+
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--baseline <trace-path-or-run-id>", "optional baseline trace for diff artifacts").option("--baseline-run <run-id>", "select a run from the baseline trace").option("--github-summary <path>", "append a safe summary to this file, e.g. GITHUB_STEP_SUMMARY").option("--evidence", "write Evidence v2 package on failure (default behavior when omitted)").option("--no-evidence", "skip Evidence v2 package").option("--always-evidence", "write Evidence v2 package even when checks pass").option("--json", "print deterministic JSON manifest").action((target, opts) => {
|
|
11556
11556
|
runCommand(() => artifactsCommand(target, opts));
|
|
11557
11557
|
});
|
|
11558
|
-
program.command("bundle").description("Create
|
|
11558
|
+
const bundleCmd = program.command("bundle").description("Create or verify share-safe offline evidence bundles").argument("[run-id]", "run id to bundle (optional with --session or --since)").option("--dir <path>", "trace directory for run/session lookup").option("--session <session-id>", "bundle all runs in a session").option("--since <duration>", "bundle runs with activity since a duration (e.g. 24h)").addOption(
|
|
11559
11559
|
new Option("--profile <profile>", "redaction profile for exported copies").choices(["local", "share", "strict"]).default("share")
|
|
11560
11560
|
).addOption(
|
|
11561
11561
|
new Option(
|
|
11562
11562
|
"--redaction-profile <profile>",
|
|
11563
11563
|
"alias for --profile (canonical spelling)"
|
|
11564
11564
|
).choices(["local", "share", "strict"])
|
|
11565
|
-
).option("--out <path>", "output directory
|
|
11565
|
+
).option("--out <path>", "output directory, .html path, or .zip path").option("--output <path>", "alias for --out (canonical spelling)").addOption(
|
|
11566
|
+
new Option("--format <format>", "bundle output format").choices(["directory", "html", "zip"]).default("directory")
|
|
11567
|
+
).option("--allow-unsafe", "write bundle even when verify-safe reports UNSAFE").option("--json", "print deterministic JSON manifest").action((runId, opts) => {
|
|
11566
11568
|
runCommand(() => bundleCommand(runId, opts));
|
|
11567
11569
|
});
|
|
11570
|
+
bundleCmd.command("verify").description("Verify Evidence v2 integrity (manifest, hashes, presence)").argument("<path>", "evidence/bundle directory containing evidence.json").addOption(
|
|
11571
|
+
new Option("--unexpected <mode>", "policy for files not listed in the manifest").choices(["fail", "warn", "ignore"]).default("fail")
|
|
11572
|
+
).option("--json", "print deterministic JSON verify result").action((targetPath, opts) => {
|
|
11573
|
+
runCommand(() => bundleVerifyCommand(targetPath, opts));
|
|
11574
|
+
});
|
|
11568
11575
|
program.command("ci-summary").description("Summarize local reporter artifact manifests for CI").argument("<manifest...>", "reporter artifact manifest JSON files").option("-o, --output <path>", "write Markdown summary to a local file").option("--github-summary <path>", "append Markdown summary to this local file, e.g. GITHUB_STEP_SUMMARY").option("--json", "print deterministic JSON summary").action((manifest, opts) => {
|
|
11569
11576
|
runCommand(() => ciSummaryCommand(manifest, opts));
|
|
11570
11577
|
});
|
|
@@ -11822,6 +11829,20 @@ function createCliProgram() {
|
|
|
11822
11829
|
).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) => {
|
|
11823
11830
|
runCommand(() => cohortCommand(opts));
|
|
11824
11831
|
});
|
|
11832
|
+
const mcpCmd = program.command("mcp").description("Configure local coding-agent MCP clients (read-only; dry-run by default)");
|
|
11833
|
+
mcpCmd.command("configure").description("Print or write MCP client config for Cursor/Claude/Codex/Gemini").requiredOption(
|
|
11834
|
+
"--client <name>",
|
|
11835
|
+
"cursor | claude-code | codex | gemini"
|
|
11836
|
+
).option("--dir <path>", "trace directory for the MCP server", ".agent-inspect").option("--project-local", "target a project-local config path").option("--write", "write config (requires --project-local --yes)").option("--yes", "confirm write").option("--json", "print deterministic JSON result").action((opts) => {
|
|
11837
|
+
runCommand(async () => {
|
|
11838
|
+
if (!isMcpConfigureClient(opts.client)) {
|
|
11839
|
+
throw new Error(
|
|
11840
|
+
`Unsupported --client "${opts.client}". Use: cursor, claude-code, codex, gemini`
|
|
11841
|
+
);
|
|
11842
|
+
}
|
|
11843
|
+
await mcpConfigureCommand({ ...opts, client: opts.client });
|
|
11844
|
+
});
|
|
11845
|
+
});
|
|
11825
11846
|
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(
|
|
11826
11847
|
"--require-observation <name>",
|
|
11827
11848
|
"required passed observation (repeatable or comma-separated)"
|
|
@@ -11838,9 +11859,9 @@ function isPrimaryModule() {
|
|
|
11838
11859
|
if (!entry) return false;
|
|
11839
11860
|
const selfPath = fileURLToPath(import.meta.url);
|
|
11840
11861
|
try {
|
|
11841
|
-
return realpathSync(
|
|
11862
|
+
return realpathSync(path16.resolve(entry)) === realpathSync(path16.resolve(selfPath));
|
|
11842
11863
|
} catch {
|
|
11843
|
-
return
|
|
11864
|
+
return path16.resolve(entry) === path16.resolve(selfPath);
|
|
11844
11865
|
}
|
|
11845
11866
|
}
|
|
11846
11867
|
if (isPrimaryModule()) {
|