agent-inspect 6.9.0 → 6.10.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 +6 -0
- package/README.md +1 -1
- package/docs/BUNDLES.md +31 -9
- package/docs/CI-ARTIFACTS.md +10 -1
- package/docs/CLI.md +11 -2
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-WQ5XMFH2.mjs → chunk-36IJ76LH.mjs} +1756 -149
- package/packages/cli/dist/chunk-36IJ76LH.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +10370 -8774
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +584 -712
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-C4EUHTER.mjs → src-YFN3Q3GP.mjs} +3 -3
- package/packages/cli/dist/{src-C4EUHTER.mjs.map → src-YFN3Q3GP.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +1703 -12
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +334 -3
- package/packages/core/dist/advanced.d.ts +334 -3
- package/packages/core/dist/advanced.mjs +1163 -10
- 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/diff.mjs +3 -477
- package/packages/core/dist/diff.mjs.map +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-36IJ76LH.mjs';
|
|
3
3
|
import { realpathSync, constants as constants$1, existsSync, readFileSync } from 'fs';
|
|
4
|
-
import
|
|
4
|
+
import path15 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.10.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 ? path15.resolve(resolvedOutput) : void 0;
|
|
2975
2975
|
if (outPath !== void 0) {
|
|
2976
|
-
await mkdir(
|
|
2976
|
+
await mkdir(path15.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: path15.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-YFN3Q3GP.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 ? path15.resolve(resolvedOutput) : void 0;
|
|
4261
3799
|
if (outPath !== void 0) {
|
|
4262
|
-
await mkdir(
|
|
3800
|
+
await mkdir(path15.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(path26) {
|
|
3927
|
+
const normalized = path26.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(path26, key) {
|
|
4577
4115
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
4578
|
-
return
|
|
4116
|
+
return path26 ? `${path26}.${key}` : key;
|
|
4579
4117
|
}
|
|
4580
|
-
return `${
|
|
4118
|
+
return `${path26 || "$"}[${JSON.stringify(key)}]`;
|
|
4581
4119
|
}
|
|
4582
|
-
function indexPath(
|
|
4583
|
-
return `${
|
|
4120
|
+
function indexPath(path26, index) {
|
|
4121
|
+
return `${path26 || "$"}[${index}]`;
|
|
4584
4122
|
}
|
|
4585
|
-
function makeFinding(
|
|
4586
|
-
return preview === void 0 ? { path:
|
|
4123
|
+
function makeFinding(path26, detector, action, matchKind, severity = "warning", preview) {
|
|
4124
|
+
return preview === void 0 ? { path: path26, detector, action, severity, matchKind } : { path: path26, 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, path26, depth, state) {
|
|
4656
4194
|
if (depth > this.#maxDepth) {
|
|
4657
4195
|
this.#recordFinding(
|
|
4658
4196
|
state,
|
|
4659
|
-
makeFinding(
|
|
4197
|
+
makeFinding(path26, "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(path26, `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: path26, 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
|
+
path26,
|
|
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(path26, 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(path26 === "$" ? "" : path26, 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 = path15.relative(parent, child);
|
|
4710
|
+
return relative === "" || !relative.startsWith("..") && !path15.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 = path15.resolve(inputPath);
|
|
4715
|
+
const outputAbs = path15.resolve(output2.trim());
|
|
4716
|
+
const inputDir = path15.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 = path15.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(path15.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, path26, 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: path26, 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: path26, 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: `${path26}.${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 = path15.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 = path15.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 path26 = 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}${path26 ? ` (${path26})` : ""}`);
|
|
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 = path15.join(path15.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 = path15.resolve(cwd);
|
|
6173
|
+
const workspaceDir = path15.join(projectRoot, WORKSPACE_DIR_NAME);
|
|
6636
6174
|
return {
|
|
6637
6175
|
projectRoot,
|
|
6638
6176
|
workspaceDir,
|
|
6639
|
-
manifestPath:
|
|
6177
|
+
manifestPath: path15.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 = path15.resolve(workspaceDir);
|
|
6182
|
+
const resolved = path15.resolve(base, relative);
|
|
6183
|
+
const rel = path15.relative(base, resolved);
|
|
6184
|
+
if (rel === "" || rel === "." || !rel.startsWith("..") && !path15.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() || path15.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(path15.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(path15.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 = path15.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: path15.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: path15.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
|
+
path15.join(location.workspaceDir, manifestRead.manifest.bundlesDir),
|
|
6488
|
+
path15.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: path15.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: path15.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: path15.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, path26) {
|
|
7069
|
+
return [{ runId: run.runId, ...path26 !== void 0 ? { path: path26 } : {} }];
|
|
7532
7070
|
}
|
|
7533
|
-
function evidenceForEvent(event,
|
|
7071
|
+
function evidenceForEvent(event, path26) {
|
|
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
|
+
...path26 !== void 0 ? { path: path26 } : {}
|
|
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, path26) {
|
|
7700
7238
|
const first = fields[0];
|
|
7701
|
-
return first === void 0 ? evidenceForRun(run,
|
|
7239
|
+
return first === void 0 ? evidenceForRun(run, path26) : 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 path26 = finding.evidence[0]?.path;
|
|
7617
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path26 ? ` (${path26})` : ""}`);
|
|
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 = path15.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 = path15.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 path26 = finding.evidence[0]?.path;
|
|
7811
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path26 ? ` (${path26})` : ""}`);
|
|
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 path26 = 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=${path26}; 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 path26 = 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}${path26 ? ` (${path26})` : ""}${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 = path15.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 (path15.isAbsolute(rawPath) || path15.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 = path15.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 = path15.resolve(outputDir, normalized);
|
|
8271
|
+
const relFromOutput = path15.relative(outputDir, absolutePath);
|
|
8272
|
+
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path15.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 : path15.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 path26 = finding.evidence[0]?.path ?? "(run)";
|
|
8388
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path26})`);
|
|
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 = path15.join(outputDir, relativePath);
|
|
8467
|
+
await mkdir(path15.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() !== "" ? path15.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(path15.dirname(path15.resolve(summaryTarget)), { recursive: true });
|
|
8625
|
+
await appendFile(path15.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: path15.resolve(summaryTarget) } : {},
|
|
8947
8645
|
note: NOTE
|
|
8948
8646
|
};
|
|
8949
|
-
await writeFile(
|
|
8647
|
+
await writeFile(path15.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 = path15.relative(location.workspaceDir, normalized);
|
|
8706
|
+
if (!rel.startsWith("..") && !path15.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
|
+
path15.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 = path15.dirname(outputDir);
|
|
9128
|
+
} else {
|
|
9129
|
+
await mkdir(outputDir, { recursive: true });
|
|
9130
|
+
htmlPath = path15.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(path15.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 = path15.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(path15.dirname(outPath), { recursive: true });
|
|
9156
|
+
await writeFile(outPath, content, "utf-8");
|
|
9157
|
+
}
|
|
9158
|
+
await writeFile(path15.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,44 @@ ${body}
|
|
|
9307
9196
|
</html>
|
|
9308
9197
|
`;
|
|
9309
9198
|
}
|
|
9310
|
-
function
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
if (
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
diagnostics.push({
|
|
9344
|
-
code: "artifact_path_escape",
|
|
9345
|
-
severity: "error",
|
|
9346
|
-
message: "Reporter artifact path must stay under the output directory.",
|
|
9347
|
-
target: rawPath
|
|
9348
|
-
});
|
|
9349
|
-
return { ok: false, outputDir, diagnostics };
|
|
9199
|
+
function writeJson5(value) {
|
|
9200
|
+
return `${JSON.stringify(value, null, 2)}
|
|
9201
|
+
`;
|
|
9202
|
+
}
|
|
9203
|
+
async function bundleVerifyCommand(targetPath, options = {}) {
|
|
9204
|
+
const root = path15.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
|
+
}
|
|
9350
9232
|
}
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path14.isAbsolute(relFromOutput)) {
|
|
9354
|
-
diagnostics.push({
|
|
9355
|
-
code: "artifact_path_escape",
|
|
9356
|
-
severity: "error",
|
|
9357
|
-
message: "Reporter artifact path resolved outside the output directory.",
|
|
9358
|
-
target: rawPath
|
|
9359
|
-
});
|
|
9360
|
-
return { ok: false, outputDir, diagnostics };
|
|
9233
|
+
if (!result.ok) {
|
|
9234
|
+
process.exitCode = 1;
|
|
9361
9235
|
}
|
|
9362
|
-
return {
|
|
9363
|
-
ok: true,
|
|
9364
|
-
outputDir,
|
|
9365
|
-
relativePath: normalized,
|
|
9366
|
-
absolutePath,
|
|
9367
|
-
diagnostics
|
|
9368
|
-
};
|
|
9369
9236
|
}
|
|
9370
|
-
|
|
9371
|
-
// packages/cli/src/ci-summary.ts
|
|
9372
9237
|
var NOTE2 = "Generated locally by AgentInspect from reporter artifact manifests. Trace contents are not embedded.";
|
|
9373
9238
|
var MAX_TEXT = 180;
|
|
9374
9239
|
var FRAMEWORKS = /* @__PURE__ */ new Set(["vitest", "jest", "manual"]);
|
|
@@ -9384,7 +9249,7 @@ function stable7(value) {
|
|
|
9384
9249
|
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable7(record[key])])
|
|
9385
9250
|
);
|
|
9386
9251
|
}
|
|
9387
|
-
function
|
|
9252
|
+
function writeJson6(value) {
|
|
9388
9253
|
return `${JSON.stringify(stable7(value), null, 2)}
|
|
9389
9254
|
`;
|
|
9390
9255
|
}
|
|
@@ -9496,23 +9361,23 @@ function readManifestDocument(value) {
|
|
|
9496
9361
|
};
|
|
9497
9362
|
}
|
|
9498
9363
|
function cwdRelative(filePath) {
|
|
9499
|
-
const relative =
|
|
9500
|
-
if (relative === "" || relative.startsWith("../") ||
|
|
9501
|
-
return
|
|
9364
|
+
const relative = path15.relative(process.cwd(), path15.resolve(filePath)).replace(/\\/g, "/");
|
|
9365
|
+
if (relative === "" || relative.startsWith("../") || path15.isAbsolute(relative)) {
|
|
9366
|
+
return path15.basename(filePath);
|
|
9502
9367
|
}
|
|
9503
9368
|
return relative;
|
|
9504
9369
|
}
|
|
9505
9370
|
async function readReporterManifest(filePath) {
|
|
9506
|
-
const absolute =
|
|
9371
|
+
const absolute = path15.resolve(filePath);
|
|
9507
9372
|
const raw = await readFile(absolute, "utf-8");
|
|
9508
9373
|
const document = readManifestDocument(JSON.parse(raw));
|
|
9509
9374
|
const manifest = document.manifest;
|
|
9510
9375
|
const results = manifest.results.map((result) => ({
|
|
9511
9376
|
testId: safeText(result.testId),
|
|
9512
9377
|
name: safeText(result.name),
|
|
9513
|
-
...result.file === void 0 ? {} : { file: safeText(
|
|
9378
|
+
...result.file === void 0 ? {} : { file: safeText(path15.basename(result.file)) },
|
|
9514
9379
|
status: result.status,
|
|
9515
|
-
...result.tracePath === void 0 ? {} : { tracePath: safeText(
|
|
9380
|
+
...result.tracePath === void 0 ? {} : { tracePath: safeText(path15.basename(result.tracePath)) },
|
|
9516
9381
|
artifacts: result.artifacts,
|
|
9517
9382
|
diagnostics: result.diagnostics
|
|
9518
9383
|
}));
|
|
@@ -9651,20 +9516,20 @@ async function ciSummaryCommand(manifestPaths, options = {}) {
|
|
|
9651
9516
|
return;
|
|
9652
9517
|
}
|
|
9653
9518
|
const markdown = renderMarkdown2(result);
|
|
9654
|
-
const outputPath = options.output !== void 0 && options.output.trim() !== "" ?
|
|
9519
|
+
const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path15.resolve(options.output.trim()) : void 0;
|
|
9655
9520
|
if (outputPath !== void 0) {
|
|
9656
|
-
await mkdir(
|
|
9521
|
+
await mkdir(path15.dirname(outputPath), { recursive: true });
|
|
9657
9522
|
await writeFile(outputPath, markdown, "utf-8");
|
|
9658
9523
|
}
|
|
9659
9524
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
9660
9525
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
9661
|
-
const summaryPath =
|
|
9662
|
-
await mkdir(
|
|
9526
|
+
const summaryPath = path15.resolve(summaryTarget);
|
|
9527
|
+
await mkdir(path15.dirname(summaryPath), { recursive: true });
|
|
9663
9528
|
await appendFile(summaryPath, `
|
|
9664
9529
|
${markdown}`, "utf-8");
|
|
9665
9530
|
}
|
|
9666
9531
|
if (options.json === true) {
|
|
9667
|
-
console.log(
|
|
9532
|
+
console.log(writeJson6(result).trimEnd());
|
|
9668
9533
|
} else if (outputPath !== void 0) {
|
|
9669
9534
|
console.log(`Wrote AgentInspect CI summary to ${outputPath}`);
|
|
9670
9535
|
console.log(`Status: ${result.status}`);
|
|
@@ -9807,7 +9672,7 @@ jobs:
|
|
|
9807
9672
|
}
|
|
9808
9673
|
async function planInit(options = {}) {
|
|
9809
9674
|
const framework = normalizeFramework(options.framework);
|
|
9810
|
-
const cwd =
|
|
9675
|
+
const cwd = path15.resolve(options.cwd ?? process.cwd());
|
|
9811
9676
|
const demoPath = framework === "custom" ? "examples/agent-inspect-demo.mjs" : `examples/agent-inspect-${framework}-demo.mjs`;
|
|
9812
9677
|
const candidates = [
|
|
9813
9678
|
{ rel: CONFIG_FILE, content: configTemplate(framework) },
|
|
@@ -9822,7 +9687,7 @@ async function planInit(options = {}) {
|
|
|
9822
9687
|
}
|
|
9823
9688
|
const files = [];
|
|
9824
9689
|
for (const candidate of candidates) {
|
|
9825
|
-
const abs =
|
|
9690
|
+
const abs = path15.join(cwd, candidate.rel);
|
|
9826
9691
|
try {
|
|
9827
9692
|
await access(abs);
|
|
9828
9693
|
files.push({
|
|
@@ -9843,12 +9708,12 @@ async function writePlannedFiles(plan, cwd, options) {
|
|
|
9843
9708
|
if (entry.action === "skip") {
|
|
9844
9709
|
continue;
|
|
9845
9710
|
}
|
|
9846
|
-
const abs =
|
|
9711
|
+
const abs = path15.join(cwd, entry.path);
|
|
9847
9712
|
if (options.dryRun) {
|
|
9848
9713
|
written.push(entry.path);
|
|
9849
9714
|
continue;
|
|
9850
9715
|
}
|
|
9851
|
-
await mkdir(
|
|
9716
|
+
await mkdir(path15.dirname(abs), { recursive: true });
|
|
9852
9717
|
const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate() : demoTemplate(plan.framework);
|
|
9853
9718
|
await writeFile(abs, content, "utf-8");
|
|
9854
9719
|
written.push(entry.path);
|
|
@@ -9856,7 +9721,7 @@ async function writePlannedFiles(plan, cwd, options) {
|
|
|
9856
9721
|
return written;
|
|
9857
9722
|
}
|
|
9858
9723
|
async function initCommand(options = {}) {
|
|
9859
|
-
const cwd =
|
|
9724
|
+
const cwd = path15.resolve(options.cwd ?? process.cwd());
|
|
9860
9725
|
try {
|
|
9861
9726
|
const plan = await planInit({ ...options, cwd });
|
|
9862
9727
|
const toWrite = plan.files.filter((file) => file.action === "create").map((f) => f.path);
|
|
@@ -9947,7 +9812,7 @@ function envCheck(name, optional = true) {
|
|
|
9947
9812
|
};
|
|
9948
9813
|
}
|
|
9949
9814
|
async function traceDirWritable(traceDir) {
|
|
9950
|
-
const resolved =
|
|
9815
|
+
const resolved = path15.resolve(traceDir);
|
|
9951
9816
|
try {
|
|
9952
9817
|
await mkdir(resolved, { recursive: true });
|
|
9953
9818
|
await access(resolved, constants.W_OK);
|
|
@@ -9968,10 +9833,10 @@ async function traceDirWritable(traceDir) {
|
|
|
9968
9833
|
}
|
|
9969
9834
|
}
|
|
9970
9835
|
function readPackageVersionNearEntry(entryPath, packageName) {
|
|
9971
|
-
let dir =
|
|
9972
|
-
const { root } =
|
|
9836
|
+
let dir = path15.dirname(path15.resolve(entryPath));
|
|
9837
|
+
const { root } = path15.parse(dir);
|
|
9973
9838
|
while (true) {
|
|
9974
|
-
const candidate =
|
|
9839
|
+
const candidate = path15.join(dir, "package.json");
|
|
9975
9840
|
if (existsSync(candidate)) {
|
|
9976
9841
|
try {
|
|
9977
9842
|
const pkg = JSON.parse(readFileSync(candidate, "utf8"));
|
|
@@ -9982,14 +9847,14 @@ function readPackageVersionNearEntry(entryPath, packageName) {
|
|
|
9982
9847
|
}
|
|
9983
9848
|
}
|
|
9984
9849
|
if (dir === root) break;
|
|
9985
|
-
const parent =
|
|
9850
|
+
const parent = path15.dirname(dir);
|
|
9986
9851
|
if (parent === dir) break;
|
|
9987
9852
|
dir = parent;
|
|
9988
9853
|
}
|
|
9989
9854
|
return void 0;
|
|
9990
9855
|
}
|
|
9991
9856
|
function resolveInstalledPackage(cwd, name) {
|
|
9992
|
-
const require2 = createRequire(
|
|
9857
|
+
const require2 = createRequire(path15.join(cwd, "package.json"));
|
|
9993
9858
|
try {
|
|
9994
9859
|
const entry = require2.resolve(name);
|
|
9995
9860
|
let version2;
|
|
@@ -10087,7 +9952,7 @@ function versionMismatchCheck(cwd) {
|
|
|
10087
9952
|
};
|
|
10088
9953
|
}
|
|
10089
9954
|
async function runDoctorChecks(options = {}) {
|
|
10090
|
-
const cwd =
|
|
9955
|
+
const cwd = path15.resolve(options.cwd ?? process2.cwd());
|
|
10091
9956
|
const traceDir = options.traceDir?.trim() || process2.env.AGENT_INSPECT_TRACE_DIR?.trim() || ".agent-inspect";
|
|
10092
9957
|
const checks2 = [
|
|
10093
9958
|
nodeVersionCheck(),
|
|
@@ -10327,7 +10192,7 @@ function parsePluginManifest(input3) {
|
|
|
10327
10192
|
};
|
|
10328
10193
|
}
|
|
10329
10194
|
async function readPluginManifestFile(packageDir) {
|
|
10330
|
-
const manifestPath =
|
|
10195
|
+
const manifestPath = path15.join(packageDir, PLUGIN_MANIFEST_FILENAME);
|
|
10331
10196
|
try {
|
|
10332
10197
|
const raw = await readFile(manifestPath, "utf8");
|
|
10333
10198
|
const parsed = parsePluginManifest(JSON.parse(raw));
|
|
@@ -10353,7 +10218,7 @@ async function indexIsStale(snapshot, traceDir) {
|
|
|
10353
10218
|
const files = await td.list();
|
|
10354
10219
|
for (const file of files) {
|
|
10355
10220
|
const stats = await td.getFileStats(file);
|
|
10356
|
-
if (stats.mtimeMs > builtMs) return true;
|
|
10221
|
+
if (Math.floor(stats.mtimeMs) > builtMs) return true;
|
|
10357
10222
|
}
|
|
10358
10223
|
return false;
|
|
10359
10224
|
}
|
|
@@ -10401,7 +10266,7 @@ function createTraceDirectoryIndexer() {
|
|
|
10401
10266
|
// packages/cli/src/plugins.ts
|
|
10402
10267
|
async function readPackageName(packageDir) {
|
|
10403
10268
|
try {
|
|
10404
|
-
const raw = await readFile(
|
|
10269
|
+
const raw = await readFile(path15.join(packageDir, "package.json"), "utf8");
|
|
10405
10270
|
const parsed = JSON.parse(raw);
|
|
10406
10271
|
return typeof parsed.name === "string" ? parsed.name : void 0;
|
|
10407
10272
|
} catch {
|
|
@@ -10409,7 +10274,7 @@ async function readPackageName(packageDir) {
|
|
|
10409
10274
|
}
|
|
10410
10275
|
}
|
|
10411
10276
|
async function discoverPlugins(cwd = process.cwd()) {
|
|
10412
|
-
const nodeModules =
|
|
10277
|
+
const nodeModules = path15.join(cwd, "node_modules");
|
|
10413
10278
|
const found = [];
|
|
10414
10279
|
let entries;
|
|
10415
10280
|
try {
|
|
@@ -10420,7 +10285,7 @@ async function discoverPlugins(cwd = process.cwd()) {
|
|
|
10420
10285
|
for (const entry of entries.sort()) {
|
|
10421
10286
|
if (entry.startsWith("@")) continue;
|
|
10422
10287
|
if (!isPluginPackageName(entry)) continue;
|
|
10423
|
-
const packageDir =
|
|
10288
|
+
const packageDir = path15.join(nodeModules, entry);
|
|
10424
10289
|
const manifestRead = await readPluginManifestFile(packageDir);
|
|
10425
10290
|
found.push({
|
|
10426
10291
|
packageName: entry,
|
|
@@ -10433,7 +10298,7 @@ async function discoverPlugins(cwd = process.cwd()) {
|
|
|
10433
10298
|
return found;
|
|
10434
10299
|
}
|
|
10435
10300
|
async function validatePluginPackage(packageRef, cwd = process.cwd()) {
|
|
10436
|
-
const packageDir =
|
|
10301
|
+
const packageDir = path15.isAbsolute(packageRef) ? packageRef : path15.join(cwd, "node_modules", packageRef);
|
|
10437
10302
|
const packageName = await readPackageName(packageDir) ?? packageRef;
|
|
10438
10303
|
const errors = [];
|
|
10439
10304
|
const warnings = [];
|
|
@@ -10509,7 +10374,7 @@ async function pluginsValidateCommand(packageRef, cwd) {
|
|
|
10509
10374
|
}
|
|
10510
10375
|
var INDEX_FILENAME = ".agent-inspect-index.json";
|
|
10511
10376
|
function traceIndexPath(traceDir) {
|
|
10512
|
-
return
|
|
10377
|
+
return path15.join(traceDir, INDEX_FILENAME);
|
|
10513
10378
|
}
|
|
10514
10379
|
function parseMaxEntries(raw) {
|
|
10515
10380
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
@@ -10622,7 +10487,7 @@ function isModuleNotFound4(e) {
|
|
|
10622
10487
|
}
|
|
10623
10488
|
async function loadIndexSqlite() {
|
|
10624
10489
|
try {
|
|
10625
|
-
return await import('./src-
|
|
10490
|
+
return await import('./src-YFN3Q3GP.mjs');
|
|
10626
10491
|
} catch (e) {
|
|
10627
10492
|
if (isModuleNotFound4(e)) {
|
|
10628
10493
|
console.error(
|
|
@@ -10652,7 +10517,7 @@ async function newestTraceMtimeMs2(traceDir) {
|
|
|
10652
10517
|
for (const file of files) {
|
|
10653
10518
|
if (!file.endsWith(".jsonl")) continue;
|
|
10654
10519
|
try {
|
|
10655
|
-
const s = await stat(
|
|
10520
|
+
const s = await stat(path15.join(traceDir, file));
|
|
10656
10521
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
10657
10522
|
} catch {
|
|
10658
10523
|
}
|
|
@@ -10904,11 +10769,11 @@ function printJson5(value) {
|
|
|
10904
10769
|
console.log(JSON.stringify(value, null, 2));
|
|
10905
10770
|
}
|
|
10906
10771
|
function resolveCwd(options) {
|
|
10907
|
-
return
|
|
10772
|
+
return path15.resolve(options.cwd ?? process.cwd());
|
|
10908
10773
|
}
|
|
10909
10774
|
async function suiteInitCommand(options = {}) {
|
|
10910
10775
|
const cwd = resolveCwd(options);
|
|
10911
|
-
const configPath =
|
|
10776
|
+
const configPath = path15.join(cwd, DEFAULT_CONFIG_FILENAME);
|
|
10912
10777
|
const template = options.template?.trim();
|
|
10913
10778
|
const suiteConfig = template !== void 0 && template !== "" ? resolveSuiteTemplate(template) : defaultSuiteConfigTemplate();
|
|
10914
10779
|
if (options.dryRun) {
|
|
@@ -11027,13 +10892,13 @@ async function suiteListCommand(options = {}) {
|
|
|
11027
10892
|
}
|
|
11028
10893
|
async function writeSuiteArtifact(result, configOutputDir, options) {
|
|
11029
10894
|
const cwd = resolveCwd(options);
|
|
11030
|
-
const outputDir =
|
|
10895
|
+
const outputDir = path15.resolve(
|
|
11031
10896
|
cwd,
|
|
11032
10897
|
options.output ?? configOutputDir ?? DEFAULT_SUITE_ARTIFACTS_DIR
|
|
11033
10898
|
);
|
|
11034
10899
|
await mkdir(outputDir, { recursive: true });
|
|
11035
10900
|
const stamp = result.startedAt.replace(/[:.]/g, "-");
|
|
11036
|
-
const filePath =
|
|
10901
|
+
const filePath = path15.join(outputDir, `${result.suiteName}-${stamp}.json`);
|
|
11037
10902
|
await writeFile(filePath, `${JSON.stringify(result, null, 2)}
|
|
11038
10903
|
`, "utf-8");
|
|
11039
10904
|
return filePath;
|
|
@@ -11091,7 +10956,7 @@ async function loadSuiteResultFromInput(options) {
|
|
|
11091
10956
|
if (options.input === void 0 || options.input.trim() === "") {
|
|
11092
10957
|
throw new Error("Pass --input <suite-run.json> from a prior suite run.");
|
|
11093
10958
|
}
|
|
11094
|
-
const inputPath =
|
|
10959
|
+
const inputPath = path15.resolve(cwd, options.input.trim());
|
|
11095
10960
|
const raw = await readFile(inputPath, "utf-8");
|
|
11096
10961
|
return JSON.parse(raw);
|
|
11097
10962
|
}
|
|
@@ -11125,9 +10990,9 @@ function normalizeMetrics(raw) {
|
|
|
11125
10990
|
}
|
|
11126
10991
|
async function writeArtifacts(result, outputDir) {
|
|
11127
10992
|
await mkdir(outputDir, { recursive: true });
|
|
11128
|
-
const jsonPath =
|
|
11129
|
-
const markdownPath =
|
|
11130
|
-
const htmlPath =
|
|
10993
|
+
const jsonPath = path15.join(outputDir, "cohort-results.json");
|
|
10994
|
+
const markdownPath = path15.join(outputDir, "cohort-summary.md");
|
|
10995
|
+
const htmlPath = path15.join(outputDir, "cohort-report.html");
|
|
11131
10996
|
await writeFile(jsonPath, `${renderCohortReport(result, { format: "json" })}
|
|
11132
10997
|
`, "utf-8");
|
|
11133
10998
|
await writeFile(
|
|
@@ -11155,7 +11020,7 @@ async function cohortCommand(options = {}) {
|
|
|
11155
11020
|
const format = options.format ?? (options.json ? "json" : "markdown");
|
|
11156
11021
|
let artifacts;
|
|
11157
11022
|
if (options.output !== void 0 && options.output.trim() !== "") {
|
|
11158
|
-
artifacts = await writeArtifacts(result,
|
|
11023
|
+
artifacts = await writeArtifacts(result, path15.resolve(options.output.trim()));
|
|
11159
11024
|
}
|
|
11160
11025
|
if (options.json || format === "json") {
|
|
11161
11026
|
console.log(
|
|
@@ -11225,11 +11090,11 @@ function parseDuration2(value) {
|
|
|
11225
11090
|
async function writeArtifacts2(result, outputDir) {
|
|
11226
11091
|
await mkdir(outputDir, { recursive: true });
|
|
11227
11092
|
const paths = {
|
|
11228
|
-
jsonPath:
|
|
11229
|
-
markdownPath:
|
|
11230
|
-
htmlPath:
|
|
11231
|
-
junitPath:
|
|
11232
|
-
githubPath:
|
|
11093
|
+
jsonPath: path15.join(outputDir, "gate-results.json"),
|
|
11094
|
+
markdownPath: path15.join(outputDir, "gate-summary.md"),
|
|
11095
|
+
htmlPath: path15.join(outputDir, "gate-report.html"),
|
|
11096
|
+
junitPath: path15.join(outputDir, "junit.xml"),
|
|
11097
|
+
githubPath: path15.join(outputDir, "github-step-summary.md")
|
|
11233
11098
|
};
|
|
11234
11099
|
await writeFile(
|
|
11235
11100
|
paths.jsonPath,
|
|
@@ -11297,7 +11162,7 @@ async function gateCommand(options = {}) {
|
|
|
11297
11162
|
const result = await runGate(runs, gateOptions);
|
|
11298
11163
|
let artifacts;
|
|
11299
11164
|
if (options.output !== void 0 && options.output.trim() !== "") {
|
|
11300
|
-
artifacts = await writeArtifacts2(result,
|
|
11165
|
+
artifacts = await writeArtifacts2(result, path15.resolve(options.output.trim()));
|
|
11301
11166
|
}
|
|
11302
11167
|
if (options.json || format === "json") {
|
|
11303
11168
|
console.log(JSON.stringify({ ...result, artifacts: artifacts ?? null }, null, 2));
|
|
@@ -11552,19 +11417,26 @@ function createCliProgram() {
|
|
|
11552
11417
|
"openinference-json",
|
|
11553
11418
|
"otlp-json"
|
|
11554
11419
|
])
|
|
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) => {
|
|
11420
|
+
).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
11421
|
runCommand(() => artifactsCommand(target, opts));
|
|
11557
11422
|
});
|
|
11558
|
-
program.command("bundle").description("Create
|
|
11423
|
+
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
11424
|
new Option("--profile <profile>", "redaction profile for exported copies").choices(["local", "share", "strict"]).default("share")
|
|
11560
11425
|
).addOption(
|
|
11561
11426
|
new Option(
|
|
11562
11427
|
"--redaction-profile <profile>",
|
|
11563
11428
|
"alias for --profile (canonical spelling)"
|
|
11564
11429
|
).choices(["local", "share", "strict"])
|
|
11565
|
-
).option("--out <path>", "output directory
|
|
11430
|
+
).option("--out <path>", "output directory, .html path, or .zip path").option("--output <path>", "alias for --out (canonical spelling)").addOption(
|
|
11431
|
+
new Option("--format <format>", "bundle output format").choices(["directory", "html", "zip"]).default("directory")
|
|
11432
|
+
).option("--allow-unsafe", "write bundle even when verify-safe reports UNSAFE").option("--json", "print deterministic JSON manifest").action((runId, opts) => {
|
|
11566
11433
|
runCommand(() => bundleCommand(runId, opts));
|
|
11567
11434
|
});
|
|
11435
|
+
bundleCmd.command("verify").description("Verify Evidence v2 integrity (manifest, hashes, presence)").argument("<path>", "evidence/bundle directory containing evidence.json").addOption(
|
|
11436
|
+
new Option("--unexpected <mode>", "policy for files not listed in the manifest").choices(["fail", "warn", "ignore"]).default("fail")
|
|
11437
|
+
).option("--json", "print deterministic JSON verify result").action((targetPath, opts) => {
|
|
11438
|
+
runCommand(() => bundleVerifyCommand(targetPath, opts));
|
|
11439
|
+
});
|
|
11568
11440
|
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
11441
|
runCommand(() => ciSummaryCommand(manifest, opts));
|
|
11570
11442
|
});
|
|
@@ -11838,9 +11710,9 @@ function isPrimaryModule() {
|
|
|
11838
11710
|
if (!entry) return false;
|
|
11839
11711
|
const selfPath = fileURLToPath(import.meta.url);
|
|
11840
11712
|
try {
|
|
11841
|
-
return realpathSync(
|
|
11713
|
+
return realpathSync(path15.resolve(entry)) === realpathSync(path15.resolve(selfPath));
|
|
11842
11714
|
} catch {
|
|
11843
|
-
return
|
|
11715
|
+
return path15.resolve(entry) === path15.resolve(selfPath);
|
|
11844
11716
|
}
|
|
11845
11717
|
}
|
|
11846
11718
|
if (isPrimaryModule()) {
|