agent-inspect 6.8.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 +12 -0
- package/README.md +1 -1
- package/docs/BUNDLES.md +31 -9
- package/docs/CI-ARTIFACTS.md +10 -1
- package/docs/CLI.md +20 -4
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-TO4VENHV.mjs → chunk-36IJ76LH.mjs} +1857 -158
- package/packages/cli/dist/chunk-36IJ76LH.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +10764 -8877
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +825 -754
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-Z5W27YCW.mjs → src-YFN3Q3GP.mjs} +3 -3
- package/packages/cli/dist/{src-Z5W27YCW.mjs.map → src-YFN3Q3GP.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +1718 -18
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +338 -4
- package/packages/core/dist/advanced.d.ts +338 -4
- package/packages/core/dist/advanced.mjs +1166 -12
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.cjs +105 -14
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.d.cts +2 -2
- package/packages/core/dist/checks.d.ts +2 -2
- package/packages/core/dist/checks.mjs +1 -1
- package/packages/core/dist/chunk-F7STQ5JF.mjs +479 -0
- package/packages/core/dist/chunk-F7STQ5JF.mjs.map +1 -0
- package/packages/core/dist/{chunk-EQVXDGGU.mjs → chunk-QT6CQ2XA.mjs} +107 -16
- package/packages/core/dist/chunk-QT6CQ2XA.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/{index-BblJEdYZ.d.ts → index-BO7l0iAe.d.ts} +44 -1
- package/packages/core/dist/{index-DyKk7iR9.d.cts → index-mdFcxSOR.d.cts} +44 -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-TO4VENHV.mjs.map +0 -1
- package/packages/core/dist/chunk-EQVXDGGU.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
|
}
|
|
@@ -4309,7 +3847,12 @@ var SHARE_PROFILE_EXTRA_KEYS = [
|
|
|
4309
3847
|
"organizationId",
|
|
4310
3848
|
"traceId",
|
|
4311
3849
|
"spanId",
|
|
4312
|
-
"parentSpanId"
|
|
3850
|
+
"parentSpanId",
|
|
3851
|
+
"currentTask",
|
|
3852
|
+
"task",
|
|
3853
|
+
"userInput",
|
|
3854
|
+
"requestText",
|
|
3855
|
+
"conversationText"
|
|
4313
3856
|
];
|
|
4314
3857
|
var STRICT_PROFILE_EXTRA_KEYS = [
|
|
4315
3858
|
"prompt",
|
|
@@ -4377,6 +3920,61 @@ function passesLuhn(value) {
|
|
|
4377
3920
|
}
|
|
4378
3921
|
return sum % 10 === 0;
|
|
4379
3922
|
}
|
|
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;
|
|
3924
|
+
var EPOCH_MS_RE = /^1[0-9]{12}$/;
|
|
3925
|
+
var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
|
|
3926
|
+
function pathSuggestsNonCard(path26) {
|
|
3927
|
+
const normalized = path26.toLowerCase().replace(/[^a-z0-9._]/g, "");
|
|
3928
|
+
return /(^|\.)(tokenusage|usage)(\.|$)/.test(normalized) || /(startedat|endedat|durationms|timestamp|createdat|updatedat)(\.|$)/.test(normalized) || /(^|\.)(runid|traceid|spanid|eventid|sessionid|userid|parentid|requestid|correlationid)(\.|$)/.test(
|
|
3929
|
+
normalized
|
|
3930
|
+
) || /(^|\.)ts(\.|$)/.test(normalized);
|
|
3931
|
+
}
|
|
3932
|
+
function isUuidLike(value) {
|
|
3933
|
+
return UUID_RE.test(value.trim());
|
|
3934
|
+
}
|
|
3935
|
+
function isPlausibleCardCandidate(candidate, fullValue) {
|
|
3936
|
+
const digits = digitsOnly(candidate);
|
|
3937
|
+
if (digits.length < 13 || digits.length > 19) return false;
|
|
3938
|
+
if (!passesLuhn(candidate)) return false;
|
|
3939
|
+
const trimmed = fullValue.trim();
|
|
3940
|
+
if (isUuidLike(trimmed)) return false;
|
|
3941
|
+
if (EPOCH_MS_RE.test(digits) && digitsOnly(trimmed) === digits && /^\d+$/.test(trimmed)) {
|
|
3942
|
+
return false;
|
|
3943
|
+
}
|
|
3944
|
+
const start = fullValue.indexOf(candidate);
|
|
3945
|
+
if (start >= 0) {
|
|
3946
|
+
const before = fullValue[start - 1];
|
|
3947
|
+
const after = fullValue[start + candidate.length];
|
|
3948
|
+
if (before && /[A-Za-z0-9_]/.test(before)) return false;
|
|
3949
|
+
if (after && /[A-Za-z0-9_]/.test(after)) return false;
|
|
3950
|
+
}
|
|
3951
|
+
return true;
|
|
3952
|
+
}
|
|
3953
|
+
function looksLikePathOrPackageOrUrl(value) {
|
|
3954
|
+
const trimmed = value.trim();
|
|
3955
|
+
if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(trimmed)) return true;
|
|
3956
|
+
if (/^webpack:/i.test(trimmed)) return true;
|
|
3957
|
+
if (/^@[A-Za-z0-9_.-]+\/[A-Za-z0-9_.@/-]+$/.test(trimmed)) return true;
|
|
3958
|
+
if (/^[A-Za-z]:[\\/]/.test(trimmed) || /^[/\\]/.test(trimmed)) return true;
|
|
3959
|
+
if (/[\\/]/.test(trimmed) && !/\s/.test(trimmed)) return true;
|
|
3960
|
+
return false;
|
|
3961
|
+
}
|
|
3962
|
+
function emailMatchIsPathAdjacent(value, index, length) {
|
|
3963
|
+
const before = value[index - 1];
|
|
3964
|
+
const after = value[index + length];
|
|
3965
|
+
return before === "/" || before === "\\" || after === "/" || after === "\\";
|
|
3966
|
+
}
|
|
3967
|
+
function valueContainsEmail(value) {
|
|
3968
|
+
if (looksLikePathOrPackageOrUrl(value)) return false;
|
|
3969
|
+
EMAIL_RE.lastIndex = 0;
|
|
3970
|
+
for (const match of value.matchAll(EMAIL_RE)) {
|
|
3971
|
+
const text = match[0] ?? "";
|
|
3972
|
+
const index = match.index ?? 0;
|
|
3973
|
+
if (emailMatchIsPathAdjacent(value, index, text.length)) continue;
|
|
3974
|
+
return true;
|
|
3975
|
+
}
|
|
3976
|
+
return false;
|
|
3977
|
+
}
|
|
4380
3978
|
var credentialDetectors = [
|
|
4381
3979
|
patternDetector({
|
|
4382
3980
|
id: "value.authorizationHeader",
|
|
@@ -4424,10 +4022,12 @@ var credentialDetectors = [
|
|
|
4424
4022
|
matchKind: "value",
|
|
4425
4023
|
detect(input3) {
|
|
4426
4024
|
if (typeof input3.value !== "string") return [];
|
|
4025
|
+
if (pathSuggestsNonCard(input3.path)) return [];
|
|
4026
|
+
if (isUuidLike(input3.value)) return [];
|
|
4427
4027
|
const candidatePattern = /(?:\d[ -]?){13,19}/g;
|
|
4428
4028
|
for (const match of input3.value.matchAll(candidatePattern)) {
|
|
4429
4029
|
const candidate = match[0] ?? "";
|
|
4430
|
-
if (
|
|
4030
|
+
if (isPlausibleCardCandidate(candidate, input3.value)) {
|
|
4431
4031
|
return [{ action: "replace", severity: "error", matchKind: "value" }];
|
|
4432
4032
|
}
|
|
4433
4033
|
}
|
|
@@ -4436,10 +4036,15 @@ var credentialDetectors = [
|
|
|
4436
4036
|
}
|
|
4437
4037
|
];
|
|
4438
4038
|
var identifierDetectors = [
|
|
4439
|
-
|
|
4039
|
+
{
|
|
4440
4040
|
id: "value.email",
|
|
4441
|
-
|
|
4442
|
-
|
|
4041
|
+
severity: "warning",
|
|
4042
|
+
matchKind: "value",
|
|
4043
|
+
detect(input3) {
|
|
4044
|
+
if (typeof input3.value !== "string") return [];
|
|
4045
|
+
return valueContainsEmail(input3.value) ? [{ action: "replace", severity: "warning", matchKind: "value" }] : [];
|
|
4046
|
+
}
|
|
4047
|
+
},
|
|
4443
4048
|
patternDetector({
|
|
4444
4049
|
id: "value.phone",
|
|
4445
4050
|
pattern: /\b(?:\+\d{1,3}[\s.-]?)?(?:\(?\d{3}\)?[\s.-])\d{3}[\s.-]\d{4}\b/
|
|
@@ -4506,17 +4111,17 @@ function applyRule(rule, value, replacement) {
|
|
|
4506
4111
|
}
|
|
4507
4112
|
return value;
|
|
4508
4113
|
}
|
|
4509
|
-
function childPath(
|
|
4114
|
+
function childPath(path26, key) {
|
|
4510
4115
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
4511
|
-
return
|
|
4116
|
+
return path26 ? `${path26}.${key}` : key;
|
|
4512
4117
|
}
|
|
4513
|
-
return `${
|
|
4118
|
+
return `${path26 || "$"}[${JSON.stringify(key)}]`;
|
|
4514
4119
|
}
|
|
4515
|
-
function indexPath(
|
|
4516
|
-
return `${
|
|
4120
|
+
function indexPath(path26, index) {
|
|
4121
|
+
return `${path26 || "$"}[${index}]`;
|
|
4517
4122
|
}
|
|
4518
|
-
function makeFinding(
|
|
4519
|
-
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 };
|
|
4520
4125
|
}
|
|
4521
4126
|
function createRedactionProfile(profile = "local") {
|
|
4522
4127
|
switch (profile) {
|
|
@@ -4585,11 +4190,11 @@ var Redactor2 = class {
|
|
|
4585
4190
|
#recordFinding(state, finding) {
|
|
4586
4191
|
if (this.#collectFindings) state.findings.push(finding);
|
|
4587
4192
|
}
|
|
4588
|
-
#redactValue(value, key,
|
|
4193
|
+
#redactValue(value, key, path26, depth, state) {
|
|
4589
4194
|
if (depth > this.#maxDepth) {
|
|
4590
4195
|
this.#recordFinding(
|
|
4591
4196
|
state,
|
|
4592
|
-
makeFinding(
|
|
4197
|
+
makeFinding(path26, "structure.maxDepth", "truncate", "value", "warning")
|
|
4593
4198
|
);
|
|
4594
4199
|
return "[Truncated]";
|
|
4595
4200
|
}
|
|
@@ -4598,19 +4203,19 @@ var Redactor2 = class {
|
|
|
4598
4203
|
if (rule) {
|
|
4599
4204
|
this.#recordFinding(
|
|
4600
4205
|
state,
|
|
4601
|
-
makeFinding(
|
|
4206
|
+
makeFinding(path26, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
4602
4207
|
);
|
|
4603
4208
|
return applyRule(rule, value, this.#replacement);
|
|
4604
4209
|
}
|
|
4605
4210
|
}
|
|
4606
4211
|
for (const detector of this.#detectors) {
|
|
4607
|
-
const detections = detector.detect({ path:
|
|
4212
|
+
const detections = detector.detect({ path: path26, key, value });
|
|
4608
4213
|
for (const detection of detections) {
|
|
4609
4214
|
const action = detection.action ?? "replace";
|
|
4610
4215
|
this.#recordFinding(
|
|
4611
4216
|
state,
|
|
4612
4217
|
makeFinding(
|
|
4613
|
-
|
|
4218
|
+
path26,
|
|
4614
4219
|
detector.id,
|
|
4615
4220
|
action,
|
|
4616
4221
|
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
@@ -4628,7 +4233,7 @@ var Redactor2 = class {
|
|
|
4628
4233
|
const out = [];
|
|
4629
4234
|
state.seen.set(value, out);
|
|
4630
4235
|
value.forEach((item, index) => {
|
|
4631
|
-
out[index] = this.#redactValue(item, void 0, indexPath(
|
|
4236
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path26, index), depth + 1, state);
|
|
4632
4237
|
});
|
|
4633
4238
|
return out;
|
|
4634
4239
|
}
|
|
@@ -4640,7 +4245,7 @@ var Redactor2 = class {
|
|
|
4640
4245
|
out[entryKey] = this.#redactValue(
|
|
4641
4246
|
entryValue,
|
|
4642
4247
|
entryKey,
|
|
4643
|
-
childPath(
|
|
4248
|
+
childPath(path26 === "$" ? "" : path26, entryKey),
|
|
4644
4249
|
depth + 1,
|
|
4645
4250
|
state
|
|
4646
4251
|
);
|
|
@@ -4760,6 +4365,16 @@ function redactJsonlText(content, profile) {
|
|
|
4760
4365
|
};
|
|
4761
4366
|
}
|
|
4762
4367
|
function redactDocument(content, profile) {
|
|
4368
|
+
const trimmed = content.trim();
|
|
4369
|
+
if (trimmed.startsWith("{")) {
|
|
4370
|
+
try {
|
|
4371
|
+
const parsed = JSON.parse(trimmed);
|
|
4372
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && ("schemaVersion" in parsed || "eventId" in parsed || "runId" in parsed)) {
|
|
4373
|
+
return redactJsonlText(content, profile);
|
|
4374
|
+
}
|
|
4375
|
+
} catch {
|
|
4376
|
+
}
|
|
4377
|
+
}
|
|
4763
4378
|
try {
|
|
4764
4379
|
return redactJsonText(content, profile);
|
|
4765
4380
|
} catch {
|
|
@@ -5091,14 +4706,14 @@ function uniqueSorted(values) {
|
|
|
5091
4706
|
return [...new Set(values)].sort();
|
|
5092
4707
|
}
|
|
5093
4708
|
function isWithinDirectory(child, parent) {
|
|
5094
|
-
const relative =
|
|
5095
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
4709
|
+
const relative = path15.relative(parent, child);
|
|
4710
|
+
return relative === "" || !relative.startsWith("..") && !path15.isAbsolute(relative);
|
|
5096
4711
|
}
|
|
5097
4712
|
async function resolveOutputPath(inputPath, output2, force) {
|
|
5098
4713
|
if (output2 === void 0 || output2.trim() === "") return void 0;
|
|
5099
|
-
const inputAbs =
|
|
5100
|
-
const outputAbs =
|
|
5101
|
-
const inputDir =
|
|
4714
|
+
const inputAbs = path15.resolve(inputPath);
|
|
4715
|
+
const outputAbs = path15.resolve(output2.trim());
|
|
4716
|
+
const inputDir = path15.dirname(inputAbs);
|
|
5102
4717
|
if (!isWithinDirectory(outputAbs, inputDir)) {
|
|
5103
4718
|
throw new Error("Refusing to write migrated output outside the input directory.");
|
|
5104
4719
|
}
|
|
@@ -5219,7 +4834,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
5219
4834
|
process.exitCode = 1;
|
|
5220
4835
|
return;
|
|
5221
4836
|
}
|
|
5222
|
-
const inputPath =
|
|
4837
|
+
const inputPath = path15.resolve(input3.trim());
|
|
5223
4838
|
const dryRun = options.dryRun === true;
|
|
5224
4839
|
if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
|
|
5225
4840
|
console.error("migrate requires --dry-run or --output <path>.");
|
|
@@ -5238,7 +4853,7 @@ async function migrateCommand(input3, options = {}) {
|
|
|
5238
4853
|
);
|
|
5239
4854
|
const result = await buildMigration(inputPath, outputPath);
|
|
5240
4855
|
if (!dryRun && outputPath !== void 0) {
|
|
5241
|
-
await mkdir(
|
|
4856
|
+
await mkdir(path15.dirname(outputPath), { recursive: true });
|
|
5242
4857
|
await writeFile(outputPath, result.content, "utf-8");
|
|
5243
4858
|
}
|
|
5244
4859
|
printSummary2(result, dryRun);
|
|
@@ -5568,23 +5183,23 @@ function evaluatePromptInjection(text, options = {}) {
|
|
|
5568
5183
|
}
|
|
5569
5184
|
return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
|
|
5570
5185
|
}
|
|
5571
|
-
function validateSchemaField(value, field,
|
|
5186
|
+
function validateSchemaField(value, field, path26, evidence) {
|
|
5572
5187
|
const ruleId = "guardrail.structured-output";
|
|
5573
5188
|
if (field.type) {
|
|
5574
5189
|
const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
5575
5190
|
if (actual !== field.type) {
|
|
5576
|
-
evidence.push({ ruleId, path:
|
|
5191
|
+
evidence.push({ ruleId, path: path26, preview: `expected ${field.type}, got ${actual}` });
|
|
5577
5192
|
return;
|
|
5578
5193
|
}
|
|
5579
5194
|
}
|
|
5580
5195
|
if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
|
|
5581
|
-
evidence.push({ ruleId, path:
|
|
5196
|
+
evidence.push({ ruleId, path: path26, preview: "value not in enum" });
|
|
5582
5197
|
}
|
|
5583
5198
|
if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
|
|
5584
5199
|
const record = value;
|
|
5585
5200
|
for (const key of field.required) {
|
|
5586
5201
|
if (!(key in record)) {
|
|
5587
|
-
evidence.push({ ruleId, path: `${
|
|
5202
|
+
evidence.push({ ruleId, path: `${path26}.${key}`, preview: "missing required key" });
|
|
5588
5203
|
}
|
|
5589
5204
|
}
|
|
5590
5205
|
}
|
|
@@ -5911,7 +5526,7 @@ function asConfig(value) {
|
|
|
5911
5526
|
}
|
|
5912
5527
|
async function loadConfig(configPath) {
|
|
5913
5528
|
if (configPath === void 0) return {};
|
|
5914
|
-
const extension =
|
|
5529
|
+
const extension = path15.extname(configPath);
|
|
5915
5530
|
if (TS_CONFIG_EXTENSIONS.has(extension)) {
|
|
5916
5531
|
throw new Error(
|
|
5917
5532
|
"TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -5920,7 +5535,7 @@ async function loadConfig(configPath) {
|
|
|
5920
5535
|
if (!CONFIG_EXTENSIONS.has(extension)) {
|
|
5921
5536
|
throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
|
|
5922
5537
|
}
|
|
5923
|
-
const absolute =
|
|
5538
|
+
const absolute = path15.resolve(configPath);
|
|
5924
5539
|
if (extension === ".json") {
|
|
5925
5540
|
const raw = await readFile(absolute, "utf-8");
|
|
5926
5541
|
return asConfig(JSON.parse(raw));
|
|
@@ -6096,10 +5711,10 @@ function printHuman(result) {
|
|
|
6096
5711
|
console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
6097
5712
|
}
|
|
6098
5713
|
for (const finding of result.findings) {
|
|
6099
|
-
const
|
|
5714
|
+
const path26 = finding.evidence[0]?.path;
|
|
6100
5715
|
const run = finding.evidence[0]?.runId;
|
|
6101
5716
|
const runPrefix = run ? `[${run}] ` : "";
|
|
6102
|
-
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${
|
|
5717
|
+
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path26 ? ` (${path26})` : ""}`);
|
|
6103
5718
|
}
|
|
6104
5719
|
}
|
|
6105
5720
|
function readErrorResult(error) {
|
|
@@ -6361,7 +5976,7 @@ async function loadSuiteViewerData(options) {
|
|
|
6361
5976
|
for (const suiteCase of result.cases) {
|
|
6362
5977
|
cases.push(await enrichCase(suiteCase, baselinePath));
|
|
6363
5978
|
}
|
|
6364
|
-
const artifactsDir =
|
|
5979
|
+
const artifactsDir = path15.join(path15.dirname(result.configPath), ".agent-inspect/suite-runs");
|
|
6365
5980
|
return {
|
|
6366
5981
|
suiteName: result.suiteName,
|
|
6367
5982
|
configPath: result.configPath,
|
|
@@ -6554,19 +6169,19 @@ function serializeWorkspaceManifest(manifest) {
|
|
|
6554
6169
|
}
|
|
6555
6170
|
var INDEX_DIR_NAME = "index";
|
|
6556
6171
|
function resolveWorkspaceLocation(cwd = process.cwd()) {
|
|
6557
|
-
const projectRoot =
|
|
6558
|
-
const workspaceDir =
|
|
6172
|
+
const projectRoot = path15.resolve(cwd);
|
|
6173
|
+
const workspaceDir = path15.join(projectRoot, WORKSPACE_DIR_NAME);
|
|
6559
6174
|
return {
|
|
6560
6175
|
projectRoot,
|
|
6561
6176
|
workspaceDir,
|
|
6562
|
-
manifestPath:
|
|
6177
|
+
manifestPath: path15.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
|
|
6563
6178
|
};
|
|
6564
6179
|
}
|
|
6565
6180
|
function resolveInsideWorkspace(workspaceDir, relative) {
|
|
6566
|
-
const base =
|
|
6567
|
-
const resolved =
|
|
6568
|
-
const rel =
|
|
6569
|
-
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)) {
|
|
6570
6185
|
return resolved;
|
|
6571
6186
|
}
|
|
6572
6187
|
throw new Error(
|
|
@@ -6635,7 +6250,7 @@ async function createWorkspace(options = {}) {
|
|
|
6635
6250
|
created = false;
|
|
6636
6251
|
adopted = true;
|
|
6637
6252
|
} else {
|
|
6638
|
-
const project = options.project?.trim() ||
|
|
6253
|
+
const project = options.project?.trim() || path15.basename(location.projectRoot) || "workspace";
|
|
6639
6254
|
const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
|
|
6640
6255
|
manifest = createDefaultWorkspaceManifest({
|
|
6641
6256
|
project,
|
|
@@ -6774,7 +6389,7 @@ async function doctorWorkspace(location) {
|
|
|
6774
6389
|
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
6775
6390
|
for (const file of await listJsonl(abs)) {
|
|
6776
6391
|
try {
|
|
6777
|
-
const s = await stat(
|
|
6392
|
+
const s = await stat(path15.join(abs, file));
|
|
6778
6393
|
newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
|
|
6779
6394
|
} catch {
|
|
6780
6395
|
checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
|
|
@@ -6822,7 +6437,7 @@ async function cleanWorkspace(location, manifest, options = {}) {
|
|
|
6822
6437
|
const relPath = `${rel}/${entry}`;
|
|
6823
6438
|
removed.push(relPath);
|
|
6824
6439
|
if (!dryRun) {
|
|
6825
|
-
await rm(
|
|
6440
|
+
await rm(path15.join(abs, entry), { recursive: true, force: true });
|
|
6826
6441
|
}
|
|
6827
6442
|
}
|
|
6828
6443
|
}
|
|
@@ -6831,7 +6446,7 @@ async function cleanWorkspace(location, manifest, options = {}) {
|
|
|
6831
6446
|
|
|
6832
6447
|
// packages/viewer/src/workspace-data.ts
|
|
6833
6448
|
async function loadWorkspaceViewerData(options) {
|
|
6834
|
-
const cwd =
|
|
6449
|
+
const cwd = path15.resolve(options.cwd ?? process.cwd());
|
|
6835
6450
|
const location = resolveWorkspaceLocation(cwd);
|
|
6836
6451
|
const manifestRead = await readWorkspaceManifestFile(location);
|
|
6837
6452
|
if (!manifestRead.ok || manifestRead.manifest === void 0) {
|
|
@@ -6844,7 +6459,7 @@ async function loadWorkspaceViewerData(options) {
|
|
|
6844
6459
|
const runs = [];
|
|
6845
6460
|
for (const rel of manifestRead.manifest.traceDirs) {
|
|
6846
6461
|
const traceDir = resolveTraceDir({
|
|
6847
|
-
dir:
|
|
6462
|
+
dir: path15.join(location.workspaceDir, rel)
|
|
6848
6463
|
});
|
|
6849
6464
|
const td = new TraceDirectory({ dir: traceDir });
|
|
6850
6465
|
const files = await td.list();
|
|
@@ -6858,7 +6473,7 @@ async function loadWorkspaceViewerData(options) {
|
|
|
6858
6473
|
runId: meta.runId,
|
|
6859
6474
|
...meta.name !== void 0 ? { name: meta.name } : {},
|
|
6860
6475
|
status: meta.status,
|
|
6861
|
-
file:
|
|
6476
|
+
file: path15.basename(meta.filePath)
|
|
6862
6477
|
});
|
|
6863
6478
|
}
|
|
6864
6479
|
}
|
|
@@ -6869,8 +6484,8 @@ async function loadWorkspaceViewerData(options) {
|
|
|
6869
6484
|
doctor,
|
|
6870
6485
|
runs,
|
|
6871
6486
|
bundleDirs: [
|
|
6872
|
-
|
|
6873
|
-
|
|
6487
|
+
path15.join(location.workspaceDir, manifestRead.manifest.bundlesDir),
|
|
6488
|
+
path15.join(location.workspaceDir, manifestRead.manifest.artifactsDir)
|
|
6874
6489
|
]
|
|
6875
6490
|
};
|
|
6876
6491
|
}
|
|
@@ -6933,7 +6548,7 @@ function createViewerServer(options = {}) {
|
|
|
6933
6548
|
ok: true,
|
|
6934
6549
|
readOnly: true,
|
|
6935
6550
|
mode,
|
|
6936
|
-
traceDir:
|
|
6551
|
+
traceDir: path15.resolve(traceDir)
|
|
6937
6552
|
});
|
|
6938
6553
|
}
|
|
6939
6554
|
if (pathname === "/api/suite" && mode === "suite") {
|
|
@@ -6962,7 +6577,7 @@ function createViewerServer(options = {}) {
|
|
|
6962
6577
|
runId: meta.runId,
|
|
6963
6578
|
name: meta.name,
|
|
6964
6579
|
status: meta.status,
|
|
6965
|
-
file:
|
|
6580
|
+
file: path15.basename(meta.filePath),
|
|
6966
6581
|
startedAt: meta.startedAt,
|
|
6967
6582
|
durationMs: meta.durationMs
|
|
6968
6583
|
}))
|
|
@@ -7079,7 +6694,7 @@ function startViewerServer(options = {}) {
|
|
|
7079
6694
|
resolve({
|
|
7080
6695
|
host,
|
|
7081
6696
|
port: resolvedPort,
|
|
7082
|
-
traceDir:
|
|
6697
|
+
traceDir: path15.resolve(traceDir),
|
|
7083
6698
|
url: `http://${host}:${resolvedPort}/${modeQuery}`,
|
|
7084
6699
|
mode
|
|
7085
6700
|
});
|
|
@@ -7450,10 +7065,10 @@ async function evalRun(input3, options = {}) {
|
|
|
7450
7065
|
diagnostics: []
|
|
7451
7066
|
};
|
|
7452
7067
|
}
|
|
7453
|
-
function evidenceForRun(run,
|
|
7454
|
-
return [{ runId: run.runId, ...
|
|
7068
|
+
function evidenceForRun(run, path26) {
|
|
7069
|
+
return [{ runId: run.runId, ...path26 !== void 0 ? { path: path26 } : {} }];
|
|
7455
7070
|
}
|
|
7456
|
-
function evidenceForEvent(event,
|
|
7071
|
+
function evidenceForEvent(event, path26) {
|
|
7457
7072
|
return [
|
|
7458
7073
|
{
|
|
7459
7074
|
runId: event.runId,
|
|
@@ -7461,7 +7076,7 @@ function evidenceForEvent(event, path25) {
|
|
|
7461
7076
|
...event.parentId !== void 0 ? { parentId: event.parentId } : {},
|
|
7462
7077
|
kind: event.kind,
|
|
7463
7078
|
name: event.name,
|
|
7464
|
-
...
|
|
7079
|
+
...path26 !== void 0 ? { path: path26 } : {}
|
|
7465
7080
|
}
|
|
7466
7081
|
];
|
|
7467
7082
|
}
|
|
@@ -7619,9 +7234,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
|
|
|
7619
7234
|
function tokenize(text) {
|
|
7620
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));
|
|
7621
7236
|
}
|
|
7622
|
-
function firstEvidence(fields, run,
|
|
7237
|
+
function firstEvidence(fields, run, path26) {
|
|
7623
7238
|
const first = fields[0];
|
|
7624
|
-
return first === void 0 ? evidenceForRun(run,
|
|
7239
|
+
return first === void 0 ? evidenceForRun(run, path26) : evidenceForEvent(first.node.event, first.path);
|
|
7625
7240
|
}
|
|
7626
7241
|
function collectSourceIds(nodes, keys) {
|
|
7627
7242
|
const wanted = keySet(keys);
|
|
@@ -7998,8 +7613,8 @@ function renderEvalMarkdown(result) {
|
|
|
7998
7613
|
if (result.findings.length > 0) {
|
|
7999
7614
|
lines.push("", "## Findings");
|
|
8000
7615
|
for (const finding of result.findings) {
|
|
8001
|
-
const
|
|
8002
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
7616
|
+
const path26 = finding.evidence[0]?.path;
|
|
7617
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path26 ? ` (${path26})` : ""}`);
|
|
8003
7618
|
}
|
|
8004
7619
|
}
|
|
8005
7620
|
return `${lines.join("\n")}
|
|
@@ -8046,7 +7661,7 @@ function asConfig2(value) {
|
|
|
8046
7661
|
}
|
|
8047
7662
|
async function loadConfig2(configPath) {
|
|
8048
7663
|
if (configPath === void 0) return {};
|
|
8049
|
-
const extension =
|
|
7664
|
+
const extension = path15.extname(configPath);
|
|
8050
7665
|
if (TS_CONFIG_EXTENSIONS2.has(extension)) {
|
|
8051
7666
|
throw new Error(
|
|
8052
7667
|
"TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
@@ -8055,7 +7670,7 @@ async function loadConfig2(configPath) {
|
|
|
8055
7670
|
if (!CONFIG_EXTENSIONS2.has(extension)) {
|
|
8056
7671
|
throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
|
|
8057
7672
|
}
|
|
8058
|
-
const absolute =
|
|
7673
|
+
const absolute = path15.resolve(configPath);
|
|
8059
7674
|
if (extension === ".json") {
|
|
8060
7675
|
const raw = await readFile(absolute, "utf-8");
|
|
8061
7676
|
return asConfig2(JSON.parse(raw));
|
|
@@ -8192,8 +7807,8 @@ function printHuman2(result) {
|
|
|
8192
7807
|
console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
8193
7808
|
}
|
|
8194
7809
|
for (const finding of result.findings) {
|
|
8195
|
-
const
|
|
8196
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
7810
|
+
const path26 = finding.evidence[0]?.path;
|
|
7811
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path26 ? ` (${path26})` : ""}`);
|
|
8197
7812
|
}
|
|
8198
7813
|
}
|
|
8199
7814
|
function readErrorResult2(error) {
|
|
@@ -8235,8 +7850,6 @@ async function evalCommand(target, options = {}, stdin = process.stdin) {
|
|
|
8235
7850
|
else if (options.markdown) console.log(renderEvalMarkdown(result).trimEnd());
|
|
8236
7851
|
else printHuman2(result);
|
|
8237
7852
|
}
|
|
8238
|
-
|
|
8239
|
-
// packages/cli/src/safety.ts
|
|
8240
7853
|
var BEST_EFFORT_NOTE = "Best-effort local safety verification only; not a compliance, privacy, security, or regulatory certification.";
|
|
8241
7854
|
var DEFAULT_MAX_STRING_LENGTH = 16384;
|
|
8242
7855
|
var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
|
|
@@ -8294,7 +7907,7 @@ function resultFromParts(parts) {
|
|
|
8294
7907
|
const diagnostics = [...parts.diagnostics ?? []];
|
|
8295
7908
|
const warnings = [...parts.warnings ?? []];
|
|
8296
7909
|
const unsupportedFields = [...parts.unsupportedFields ?? []];
|
|
8297
|
-
const status = statusFrom(findings, diagnostics);
|
|
7910
|
+
const status = parts.status ?? statusFrom(findings, diagnostics);
|
|
8298
7911
|
return {
|
|
8299
7912
|
ok: status === "SAFE" || status === "SAFE WITH WARNINGS",
|
|
8300
7913
|
command: parts.command,
|
|
@@ -8310,7 +7923,17 @@ function resultFromParts(parts) {
|
|
|
8310
7923
|
diagnostics,
|
|
8311
7924
|
warnings,
|
|
8312
7925
|
unsupportedFields,
|
|
8313
|
-
note: BEST_EFFORT_NOTE
|
|
7926
|
+
note: BEST_EFFORT_NOTE,
|
|
7927
|
+
...parts.sourceAssessment !== void 0 ? { sourceAssessment: parts.sourceAssessment } : {},
|
|
7928
|
+
...parts.artifactAssessment !== void 0 ? { artifactAssessment: parts.artifactAssessment } : {},
|
|
7929
|
+
...parts.redactionSummary !== void 0 ? { redactionSummary: parts.redactionSummary } : {}
|
|
7930
|
+
};
|
|
7931
|
+
}
|
|
7932
|
+
function layerFromResult(result) {
|
|
7933
|
+
return {
|
|
7934
|
+
status: result.status,
|
|
7935
|
+
summary: result.summary,
|
|
7936
|
+
findings: result.findings
|
|
8314
7937
|
};
|
|
8315
7938
|
}
|
|
8316
7939
|
function readErrorResult3(command, error) {
|
|
@@ -8374,6 +7997,21 @@ function flattenNodes(nodes) {
|
|
|
8374
7997
|
function detectorSeverity(finding) {
|
|
8375
7998
|
return finding.severity;
|
|
8376
7999
|
}
|
|
8000
|
+
function classifyRedactionDetector(detector) {
|
|
8001
|
+
if (detector === "value.creditCard" || detector === "value.email" || detector === "value.phone") {
|
|
8002
|
+
return { category: "personal-data", confidence: "high" };
|
|
8003
|
+
}
|
|
8004
|
+
if (detector === "value.ipv4" || detector === "value.ipv6") {
|
|
8005
|
+
return { category: "identifier", confidence: "medium" };
|
|
8006
|
+
}
|
|
8007
|
+
if (detector.startsWith("value.") && (detector.includes("Token") || detector.includes("Key") || detector.includes("jwt") || detector.includes("authorization") || detector.includes("bearer") || detector.includes("cookie") || detector.includes("privateKey") || detector.includes("github") || detector.includes("aws") || detector.includes("provider"))) {
|
|
8008
|
+
return { category: "credential", confidence: "high" };
|
|
8009
|
+
}
|
|
8010
|
+
if (detector.startsWith("key.")) {
|
|
8011
|
+
return { category: "credential", confidence: "medium" };
|
|
8012
|
+
}
|
|
8013
|
+
return { category: "credential", confidence: "medium" };
|
|
8014
|
+
}
|
|
8377
8015
|
function redactionDetectorFindings(read, runId) {
|
|
8378
8016
|
const runs = runId === void 0 ? read.runs : read.runs.filter((run) => run.runId === runId);
|
|
8379
8017
|
const out = [];
|
|
@@ -8384,6 +8022,7 @@ function redactionDetectorFindings(read, runId) {
|
|
|
8384
8022
|
const result = redact(attrs, { profile: "share" });
|
|
8385
8023
|
for (const finding of result.findings) {
|
|
8386
8024
|
if (finding.action === "keep") continue;
|
|
8025
|
+
const taxonomy = classifyRedactionDetector(finding.detector);
|
|
8387
8026
|
out.push({
|
|
8388
8027
|
ruleId: "safety.redactDetector",
|
|
8389
8028
|
severity: detectorSeverity(finding),
|
|
@@ -8401,7 +8040,11 @@ function redactionDetectorFindings(read, runId) {
|
|
|
8401
8040
|
...node.event.status !== void 0 ? { status: node.event.status } : {},
|
|
8402
8041
|
path: `attributes.${finding.path.replace(/^\$\.?/, "")}`
|
|
8403
8042
|
}
|
|
8404
|
-
]
|
|
8043
|
+
],
|
|
8044
|
+
category: taxonomy.category,
|
|
8045
|
+
confidence: taxonomy.confidence,
|
|
8046
|
+
detector: finding.detector,
|
|
8047
|
+
action: finding.action
|
|
8405
8048
|
});
|
|
8406
8049
|
}
|
|
8407
8050
|
}
|
|
@@ -8417,13 +8060,49 @@ function exitCodeFor3(result) {
|
|
|
8417
8060
|
if (result.status === "UNSAFE") return 1;
|
|
8418
8061
|
return 2;
|
|
8419
8062
|
}
|
|
8420
|
-
function
|
|
8421
|
-
|
|
8063
|
+
function explainFinding(finding, blocksBundle) {
|
|
8064
|
+
const path26 = finding.evidence[0]?.path ?? "(unknown path)";
|
|
8065
|
+
const category = finding.category ?? "structure";
|
|
8066
|
+
const confidence = finding.confidence ?? "medium";
|
|
8067
|
+
const detector = finding.detector ?? finding.ruleId;
|
|
8068
|
+
const action = finding.action ?? "review";
|
|
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.";
|
|
8070
|
+
return [
|
|
8071
|
+
` Matched: detector=${detector}; path=${path26}; category=${category}`,
|
|
8072
|
+
` Why: ${finding.message}`,
|
|
8073
|
+
` Confidence: ${confidence}`,
|
|
8074
|
+
` Redaction: ${redactionHint}`,
|
|
8075
|
+
` Override: configure a custom redaction/detector rule locally (see docs/SAFETY-POLICY.md); do not weaken defaults globally.`,
|
|
8076
|
+
` Bundle gate: ${blocksBundle ? "blocks share-safe bundle unless --allow-unsafe" : "does not block by itself (warning/info)"}`
|
|
8077
|
+
];
|
|
8078
|
+
}
|
|
8079
|
+
function findingExplanation(finding) {
|
|
8080
|
+
const blocks = finding.severity === "error" || finding.status === "fail";
|
|
8081
|
+
return {
|
|
8082
|
+
ruleId: finding.ruleId,
|
|
8083
|
+
detector: finding.detector ?? finding.ruleId,
|
|
8084
|
+
path: finding.evidence[0]?.path,
|
|
8085
|
+
category: finding.category,
|
|
8086
|
+
confidence: finding.confidence,
|
|
8087
|
+
action: finding.action,
|
|
8088
|
+
blocksBundle: blocks,
|
|
8089
|
+
// Never include matched secret/PII values.
|
|
8090
|
+
message: finding.message
|
|
8091
|
+
};
|
|
8422
8092
|
}
|
|
8423
|
-
function printHuman3(result) {
|
|
8093
|
+
function printHuman3(result, explain = false) {
|
|
8424
8094
|
console.log(`Safety status: ${result.status}`);
|
|
8425
8095
|
console.log(`Format: ${result.format}`);
|
|
8426
8096
|
if (result.runId !== void 0) console.log(`Run: ${result.runId}`);
|
|
8097
|
+
if (result.sourceAssessment !== void 0 && result.artifactAssessment !== void 0) {
|
|
8098
|
+
console.log(`Source assessment: ${result.sourceAssessment.status}`);
|
|
8099
|
+
console.log(`Artifact assessment: ${result.artifactAssessment.status}`);
|
|
8100
|
+
if (result.redactionSummary !== void 0) {
|
|
8101
|
+
console.log(
|
|
8102
|
+
`Redaction: profile=${result.redactionSummary.profile}, findings=${result.redactionSummary.findings}`
|
|
8103
|
+
);
|
|
8104
|
+
}
|
|
8105
|
+
}
|
|
8427
8106
|
console.log(
|
|
8428
8107
|
`Summary: ${result.summary.findings} finding(s), ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
|
|
8429
8108
|
);
|
|
@@ -8431,46 +8110,83 @@ function printHuman3(result) {
|
|
|
8431
8110
|
console.log(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
8432
8111
|
}
|
|
8433
8112
|
for (const finding of result.findings) {
|
|
8434
|
-
const
|
|
8435
|
-
|
|
8113
|
+
const path26 = finding.evidence[0]?.path;
|
|
8114
|
+
const taxonomy = finding.category !== void 0 || finding.confidence !== void 0 ? ` [${[finding.category, finding.confidence].filter(Boolean).join("/")}]` : "";
|
|
8115
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path26 ? ` (${path26})` : ""}${taxonomy}`);
|
|
8116
|
+
if (explain) {
|
|
8117
|
+
const blocks = finding.severity === "error" || finding.status === "fail";
|
|
8118
|
+
for (const line of explainFinding(finding, blocks)) {
|
|
8119
|
+
console.log(line);
|
|
8120
|
+
}
|
|
8121
|
+
}
|
|
8436
8122
|
}
|
|
8437
8123
|
console.log(`Note: ${result.note}`);
|
|
8438
8124
|
}
|
|
8125
|
+
function printJson3(result, explain = false) {
|
|
8126
|
+
const payload = explain === true ? { ...result, explanations: result.findings.map(findingExplanation) } : result;
|
|
8127
|
+
console.log(JSON.stringify(stable4(payload), null, 2));
|
|
8128
|
+
}
|
|
8439
8129
|
async function safetyCommand(command, target, options, stdin) {
|
|
8440
8130
|
let result;
|
|
8441
8131
|
try {
|
|
8442
|
-
const rules = buildSafetyRules(options);
|
|
8443
8132
|
const input3 = await inputFromTarget(target, options, stdin);
|
|
8444
8133
|
const read = await openTrace(input3, {
|
|
8445
8134
|
...options.format !== void 0 ? { format: options.format } : {}
|
|
8446
8135
|
});
|
|
8447
|
-
const
|
|
8448
|
-
|
|
8449
|
-
{
|
|
8450
|
-
rules,
|
|
8451
|
-
...options.run !== void 0 ? { runId: options.run } : {}
|
|
8452
|
-
}
|
|
8453
|
-
);
|
|
8454
|
-
const detectorFindings = checkResult.diagnostics.length === 0 ? redactionDetectorFindings(read, checkResult.runId) : [];
|
|
8455
|
-
result = resultFromParts({
|
|
8456
|
-
command,
|
|
8457
|
-
format: checkResult.format,
|
|
8458
|
-
runId: checkResult.runId,
|
|
8459
|
-
findings: [...checkResult.findings, ...detectorFindings],
|
|
8460
|
-
diagnostics: [
|
|
8461
|
-
...checkResult.diagnostics.map(diagnosticFromCheck),
|
|
8462
|
-
...warningDiagnostics(read.warnings, read.unsupportedFields)
|
|
8463
|
-
],
|
|
8464
|
-
warnings: read.warnings,
|
|
8465
|
-
unsupportedFields: read.unsupportedFields
|
|
8136
|
+
const source = assessOpenedTrace(read, {
|
|
8137
|
+
...options,
|
|
8138
|
+
...options.run !== void 0 ? { runId: options.run } : {}
|
|
8466
8139
|
});
|
|
8140
|
+
if (command === "scan") {
|
|
8141
|
+
result = { ...source, command: "scan" };
|
|
8142
|
+
} else {
|
|
8143
|
+
const profile = options.redactionProfile ?? "share";
|
|
8144
|
+
const rawContent = input3.type === "string" ? input3.content : input3.type === "file" ? await readFile(input3.path, "utf-8") : void 0;
|
|
8145
|
+
if (rawContent === void 0) {
|
|
8146
|
+
result = {
|
|
8147
|
+
...source,
|
|
8148
|
+
command: "verify-safe",
|
|
8149
|
+
sourceAssessment: layerFromResult(source)
|
|
8150
|
+
};
|
|
8151
|
+
} else {
|
|
8152
|
+
const redacted = redactTraceContent(rawContent, profile);
|
|
8153
|
+
const artifactRead = await openTrace(
|
|
8154
|
+
{ type: "string", content: redacted.content },
|
|
8155
|
+
{ format: options.format ?? "agent-inspect-jsonl" }
|
|
8156
|
+
);
|
|
8157
|
+
const artifact = assessOpenedTrace(artifactRead, {
|
|
8158
|
+
...options,
|
|
8159
|
+
...options.run !== void 0 ? { runId: options.run } : {}
|
|
8160
|
+
});
|
|
8161
|
+
const detectors = [
|
|
8162
|
+
...new Set(redacted.findings.map((finding) => finding.detector))
|
|
8163
|
+
].sort((a, b) => a.localeCompare(b));
|
|
8164
|
+
result = resultFromParts({
|
|
8165
|
+
command: "verify-safe",
|
|
8166
|
+
format: artifact.format,
|
|
8167
|
+
runId: artifact.runId ?? source.runId,
|
|
8168
|
+
findings: artifact.findings,
|
|
8169
|
+
diagnostics: artifact.diagnostics,
|
|
8170
|
+
warnings: artifact.warnings,
|
|
8171
|
+
unsupportedFields: artifact.unsupportedFields,
|
|
8172
|
+
status: artifact.status,
|
|
8173
|
+
sourceAssessment: layerFromResult(source),
|
|
8174
|
+
artifactAssessment: layerFromResult(artifact),
|
|
8175
|
+
redactionSummary: {
|
|
8176
|
+
profile,
|
|
8177
|
+
findings: redacted.findings.length,
|
|
8178
|
+
detectors
|
|
8179
|
+
}
|
|
8180
|
+
});
|
|
8181
|
+
}
|
|
8182
|
+
}
|
|
8467
8183
|
} catch (error) {
|
|
8468
8184
|
const message = error instanceof Error ? error.message : String(error);
|
|
8469
8185
|
result = message.startsWith("--") ? invalidArgumentResult(command, error) : readErrorResult3(command, error);
|
|
8470
8186
|
}
|
|
8471
8187
|
process.exitCode = exitCodeFor3(result);
|
|
8472
|
-
if (options.json) printJson3(result);
|
|
8473
|
-
else printHuman3(result);
|
|
8188
|
+
if (options.json) printJson3(result, options.explain === true);
|
|
8189
|
+
else printHuman3(result, options.explain === true);
|
|
8474
8190
|
}
|
|
8475
8191
|
function scanCommand(target, options = {}, stdin = process.stdin) {
|
|
8476
8192
|
return safetyCommand("scan", target, options, stdin);
|
|
@@ -8510,6 +8226,91 @@ function messageStartsWithDash(error) {
|
|
|
8510
8226
|
const message = error instanceof Error ? error.message : String(error);
|
|
8511
8227
|
return message.startsWith("--");
|
|
8512
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
|
|
8513
8314
|
var NOTE = "Generated locally by AgentInspect. Artifacts are best-effort summaries, not compliance or security certification.";
|
|
8514
8315
|
var SAFETY_RULES = [
|
|
8515
8316
|
createSafetyRawContentRule(),
|
|
@@ -8583,8 +8384,8 @@ function renderCheckSection(result) {
|
|
|
8583
8384
|
`Diagnostics: ${result.diagnostics.length}`
|
|
8584
8385
|
];
|
|
8585
8386
|
for (const finding of result.findings.slice(0, 10)) {
|
|
8586
|
-
const
|
|
8587
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
8387
|
+
const path26 = finding.evidence[0]?.path ?? "(run)";
|
|
8388
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path26})`);
|
|
8588
8389
|
}
|
|
8589
8390
|
for (const diagnostic3 of result.diagnostics.slice(0, 10)) {
|
|
8590
8391
|
lines.push(`- ${diagnostic3.code}: ${diagnostic3.message}`);
|
|
@@ -8662,8 +8463,8 @@ function renderHtml(trace, check, diff) {
|
|
|
8662
8463
|
`;
|
|
8663
8464
|
}
|
|
8664
8465
|
async function writeArtifact(outputDir, relativePath, content, files) {
|
|
8665
|
-
const outPath =
|
|
8666
|
-
await mkdir(
|
|
8466
|
+
const outPath = path15.join(outputDir, relativePath);
|
|
8467
|
+
await mkdir(path15.dirname(outPath), { recursive: true });
|
|
8667
8468
|
await writeFile(outPath, content, "utf-8");
|
|
8668
8469
|
files.push(relativePath);
|
|
8669
8470
|
}
|
|
@@ -8678,8 +8479,21 @@ function manifestStatus(check, diff) {
|
|
|
8678
8479
|
if (check.summary.warnings > 0 || (diff?.summary.warnings ?? 0) > 0) return "warning";
|
|
8679
8480
|
return "ok";
|
|
8680
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
|
+
}
|
|
8681
8495
|
async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
8682
|
-
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ?
|
|
8496
|
+
const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path15.resolve(options.outputDir.trim()) : "";
|
|
8683
8497
|
if (outputDir === "") {
|
|
8684
8498
|
console.error("--output-dir is required.");
|
|
8685
8499
|
process.exitCode = 1;
|
|
@@ -8743,15 +8557,77 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
|
|
|
8743
8557
|
);
|
|
8744
8558
|
await writeArtifact(outputDir, "summary.md", renderMarkdown(trace, check, diff), files);
|
|
8745
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
|
+
}
|
|
8746
8622
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
8747
8623
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
8748
|
-
await mkdir(
|
|
8749
|
-
await appendFile(
|
|
8624
|
+
await mkdir(path15.dirname(path15.resolve(summaryTarget)), { recursive: true });
|
|
8625
|
+
await appendFile(path15.resolve(summaryTarget), `
|
|
8750
8626
|
${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
8751
8627
|
}
|
|
8752
8628
|
const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
|
|
8753
8629
|
const manifest = {
|
|
8754
|
-
status
|
|
8630
|
+
status,
|
|
8755
8631
|
outputDir,
|
|
8756
8632
|
files: manifestFiles,
|
|
8757
8633
|
trace,
|
|
@@ -8765,10 +8641,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
|
|
|
8765
8641
|
findings: diff?.findings.length ?? 0,
|
|
8766
8642
|
diagnostics: diff?.diagnostics.length ?? 0
|
|
8767
8643
|
},
|
|
8768
|
-
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary:
|
|
8644
|
+
...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path15.resolve(summaryTarget) } : {},
|
|
8769
8645
|
note: NOTE
|
|
8770
8646
|
};
|
|
8771
|
-
await writeFile(
|
|
8647
|
+
await writeFile(path15.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
|
|
8772
8648
|
if (options.json === true) {
|
|
8773
8649
|
console.log(writeJson3(manifest).trimEnd());
|
|
8774
8650
|
} else {
|
|
@@ -8810,16 +8686,24 @@ function safetyStatusFromAssess(status) {
|
|
|
8810
8686
|
}
|
|
8811
8687
|
return "UNKNOWN";
|
|
8812
8688
|
}
|
|
8813
|
-
|
|
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) {
|
|
8814
8696
|
const out = resolveOutputOption(options);
|
|
8815
8697
|
if (out !== void 0) {
|
|
8816
|
-
const normalized = normalizeBundleOutputPath(out
|
|
8698
|
+
const normalized = normalizeBundleOutputPath(out, {
|
|
8699
|
+
preserveZipExtension: format === "zip"
|
|
8700
|
+
});
|
|
8817
8701
|
try {
|
|
8818
8702
|
const location = resolveWorkspaceLocation(cwd);
|
|
8819
8703
|
const manifest = await readWorkspaceManifestFile(location);
|
|
8820
8704
|
if (manifest.ok && manifest.manifest) {
|
|
8821
|
-
const rel =
|
|
8822
|
-
if (!rel.startsWith("..") && !
|
|
8705
|
+
const rel = path15.relative(location.workspaceDir, normalized);
|
|
8706
|
+
if (!rel.startsWith("..") && !path15.isAbsolute(rel)) {
|
|
8823
8707
|
return resolveInsideWorkspace(location.workspaceDir, rel);
|
|
8824
8708
|
}
|
|
8825
8709
|
}
|
|
@@ -8833,20 +8717,21 @@ async function resolveOutputDir(options, runIds, cwd) {
|
|
|
8833
8717
|
if (manifest.ok && manifest.manifest) {
|
|
8834
8718
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8835
8719
|
const label = runIds.length === 1 ? sanitizeBundleRunId(runIds[0]) : `multi-${runIds.length}`;
|
|
8836
|
-
|
|
8720
|
+
const base2 = resolveInsideWorkspace(
|
|
8837
8721
|
location.workspaceDir,
|
|
8838
|
-
|
|
8722
|
+
path15.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
|
|
8839
8723
|
);
|
|
8724
|
+
return format === "zip" ? `${base2}.zip` : base2;
|
|
8840
8725
|
}
|
|
8841
8726
|
} catch {
|
|
8842
8727
|
}
|
|
8843
|
-
|
|
8728
|
+
const base = defaultBundleOutputPath(runIds);
|
|
8729
|
+
return format === "zip" ? `${base}.zip` : base;
|
|
8844
8730
|
}
|
|
8845
|
-
|
|
8846
|
-
const
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
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);
|
|
8850
8735
|
}
|
|
8851
8736
|
function renderBundleIndexHtml(parts) {
|
|
8852
8737
|
const links = parts.runIds.map(
|
|
@@ -8884,6 +8769,7 @@ function extractHtmlBody(content) {
|
|
|
8884
8769
|
}
|
|
8885
8770
|
async function bundleCommand(runIdArg, options = {}) {
|
|
8886
8771
|
const profile = parseBundleProfile(resolveRedactionProfileOption(options));
|
|
8772
|
+
const format = parseBundleFormat(options.format);
|
|
8887
8773
|
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
8888
8774
|
const cwd = process.cwd();
|
|
8889
8775
|
let resolveResult;
|
|
@@ -8909,12 +8795,18 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
8909
8795
|
process.exitCode = 1;
|
|
8910
8796
|
return;
|
|
8911
8797
|
}
|
|
8912
|
-
const outputDir = await resolveOutputDir(options, resolveResult.runIds, cwd);
|
|
8798
|
+
const outputDir = await resolveOutputDir(options, resolveResult.runIds, cwd, format);
|
|
8913
8799
|
const files = [];
|
|
8800
|
+
const packaged = /* @__PURE__ */ new Map();
|
|
8914
8801
|
const checkRuns = [];
|
|
8915
8802
|
const redactionRuns = [];
|
|
8916
8803
|
const htmlByRun = /* @__PURE__ */ new Map();
|
|
8917
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();
|
|
8918
8810
|
let combinedJsonl = "";
|
|
8919
8811
|
for (const runId of resolveResult.runIds) {
|
|
8920
8812
|
const tracePath = getTraceFilePath(runId, traceDir);
|
|
@@ -8949,15 +8841,16 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
8949
8841
|
process.exitCode = 1;
|
|
8950
8842
|
return;
|
|
8951
8843
|
}
|
|
8952
|
-
const
|
|
8953
|
-
const
|
|
8954
|
-
|
|
8844
|
+
const sourceSafety = assessOpenedTrace(read, { run: runId });
|
|
8845
|
+
const sourceStatus = safetyStatusFromAssess(sourceSafety.status);
|
|
8846
|
+
sourceHashes.push({
|
|
8955
8847
|
runId,
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
warnings: safety.summary.warnings,
|
|
8959
|
-
findings: safety.summary.findings
|
|
8848
|
+
algorithm: "sha256",
|
|
8849
|
+
hash: sha256Hex(rawContent)
|
|
8960
8850
|
});
|
|
8851
|
+
for (const version2 of collectTraceSchemaVersions(rawContent)) {
|
|
8852
|
+
schemaVersions.add(version2);
|
|
8853
|
+
}
|
|
8961
8854
|
const redacted = redactTraceContent(rawContent, toReportProfile(profile));
|
|
8962
8855
|
redactedJsonlByRun.set(runId, redacted.content);
|
|
8963
8856
|
combinedJsonl += redacted.content.endsWith("\n") ? redacted.content : `${redacted.content}
|
|
@@ -8970,6 +8863,58 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
8970
8863
|
findings: redacted.findings.length,
|
|
8971
8864
|
detectors
|
|
8972
8865
|
});
|
|
8866
|
+
let artifactSafety = sourceSafety;
|
|
8867
|
+
let artifactTree;
|
|
8868
|
+
try {
|
|
8869
|
+
const artifactRead = await openTrace(
|
|
8870
|
+
{ type: "string", content: redacted.content },
|
|
8871
|
+
{ format: "agent-inspect-jsonl" }
|
|
8872
|
+
);
|
|
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
|
+
);
|
|
8879
|
+
} catch {
|
|
8880
|
+
artifactSafety = {
|
|
8881
|
+
...sourceSafety,
|
|
8882
|
+
status: "UNKNOWN",
|
|
8883
|
+
ok: false,
|
|
8884
|
+
summary: {
|
|
8885
|
+
findings: sourceSafety.summary.findings,
|
|
8886
|
+
warnings: sourceSafety.summary.warnings,
|
|
8887
|
+
errors: Math.max(1, sourceSafety.summary.errors)
|
|
8888
|
+
}
|
|
8889
|
+
};
|
|
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
|
+
}
|
|
8909
|
+
const status = safetyStatusFromAssess(artifactSafety.status);
|
|
8910
|
+
checkRuns.push({
|
|
8911
|
+
runId,
|
|
8912
|
+
status,
|
|
8913
|
+
sourceStatus,
|
|
8914
|
+
errors: artifactSafety.summary.errors,
|
|
8915
|
+
warnings: artifactSafety.summary.warnings,
|
|
8916
|
+
findings: artifactSafety.summary.findings
|
|
8917
|
+
});
|
|
8973
8918
|
const selected = read.runs.find((run) => run.runId === runId);
|
|
8974
8919
|
const runReport = exportRunTree(selected ?? read.runs[0], {
|
|
8975
8920
|
format: "html",
|
|
@@ -8998,13 +8943,13 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
8998
8943
|
console.log(
|
|
8999
8944
|
writeJson4({
|
|
9000
8945
|
ok: false,
|
|
9001
|
-
error: `Bundle safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`,
|
|
8946
|
+
error: `Bundle artifact safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`,
|
|
9002
8947
|
checks: checks2
|
|
9003
8948
|
}).trimEnd()
|
|
9004
8949
|
);
|
|
9005
8950
|
} else {
|
|
9006
8951
|
console.error(
|
|
9007
|
-
`[AgentInspect] bundle refused: safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`
|
|
8952
|
+
`[AgentInspect] bundle refused: artifact safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`
|
|
9008
8953
|
);
|
|
9009
8954
|
}
|
|
9010
8955
|
process.exitCode = 1;
|
|
@@ -9015,80 +8960,223 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
9015
8960
|
totalFindings: redactionRuns.reduce((sum, run) => sum + run.findings, 0),
|
|
9016
8961
|
runs: redactionRuns
|
|
9017
8962
|
};
|
|
9018
|
-
await mkdir(outputDir, { recursive: true });
|
|
9019
8963
|
for (const runId of resolveResult.runIds) {
|
|
9020
8964
|
const jsonl = redactedJsonlByRun.get(runId) ?? "";
|
|
9021
|
-
|
|
9022
|
-
outputDir,
|
|
8965
|
+
stageBundleFile(
|
|
9023
8966
|
bundleRunAssetRelativePath(runId, ".jsonl"),
|
|
9024
8967
|
jsonl,
|
|
9025
|
-
files
|
|
8968
|
+
files,
|
|
8969
|
+
packaged
|
|
9026
8970
|
);
|
|
9027
8971
|
const html = htmlByRun.get(runId) ?? "";
|
|
9028
|
-
|
|
9029
|
-
outputDir,
|
|
8972
|
+
stageBundleFile(
|
|
9030
8973
|
bundleRunAssetRelativePath(runId, ".html"),
|
|
9031
8974
|
runReportWrap(html, runId),
|
|
9032
|
-
files
|
|
8975
|
+
files,
|
|
8976
|
+
packaged
|
|
9033
8977
|
);
|
|
9034
8978
|
}
|
|
9035
8979
|
const primaryRunId = resolveResult.runIds[0];
|
|
9036
8980
|
const traceHtml = resolveResult.runIds.length === 1 ? runReportWrap(htmlByRun.get(primaryRunId) ?? "", primaryRunId) : renderBundleIndexHtml({ runIds: resolveResult.runIds, reports: htmlByRun });
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
"check-results.json",
|
|
9042
|
-
writeJson4(checks2),
|
|
9043
|
-
files
|
|
9044
|
-
);
|
|
9045
|
-
await writeBundleFile(
|
|
9046
|
-
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(
|
|
9047
8985
|
"eval-results.json",
|
|
9048
8986
|
writeJson4(buildPlaceholderArtifact()),
|
|
9049
|
-
files
|
|
8987
|
+
files,
|
|
8988
|
+
packaged
|
|
9050
8989
|
);
|
|
9051
|
-
|
|
9052
|
-
outputDir,
|
|
8990
|
+
stageBundleFile(
|
|
9053
8991
|
"redaction-report.json",
|
|
9054
8992
|
writeJson4(redactionReport),
|
|
9055
|
-
files
|
|
8993
|
+
files,
|
|
8994
|
+
packaged
|
|
9056
8995
|
);
|
|
9057
|
-
|
|
9058
|
-
outputDir,
|
|
8996
|
+
stageBundleFile(
|
|
9059
8997
|
"performance-summary.json",
|
|
9060
8998
|
writeJson4(buildPlaceholderArtifact()),
|
|
9061
|
-
files
|
|
8999
|
+
files,
|
|
9000
|
+
packaged
|
|
9062
9001
|
);
|
|
9002
|
+
const summaryPath = "summary.md";
|
|
9003
|
+
const metadataPath = "metadata.json";
|
|
9004
|
+
const aggregateSourceStatus = aggregateBundleSafeStatus(
|
|
9005
|
+
checkRuns.map((run) => run.sourceStatus ?? run.status)
|
|
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));
|
|
9063
9014
|
const metadata = buildBundleMetadata({
|
|
9064
9015
|
agentInspectVersion: version,
|
|
9065
9016
|
profile,
|
|
9066
9017
|
resolve: resolveResult,
|
|
9067
9018
|
checks: checks2,
|
|
9068
|
-
files:
|
|
9019
|
+
files: metadataFileList
|
|
9069
9020
|
});
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
9073
|
-
|
|
9074
|
-
|
|
9075
|
-
|
|
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 })
|
|
9076
9093
|
);
|
|
9077
|
-
|
|
9078
|
-
|
|
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
|
+
}
|
|
9079
9164
|
if (options.json) {
|
|
9080
9165
|
console.log(
|
|
9081
9166
|
writeJson4({
|
|
9082
9167
|
ok: true,
|
|
9083
|
-
|
|
9168
|
+
format,
|
|
9169
|
+
outputDir: outputPath,
|
|
9084
9170
|
metadata,
|
|
9171
|
+
evidence: format === "html" ? JSON.parse(evidenceJson) : evidence,
|
|
9085
9172
|
checks: checks2,
|
|
9086
9173
|
redaction: redactionReport
|
|
9087
9174
|
}).trimEnd()
|
|
9088
9175
|
);
|
|
9089
9176
|
return;
|
|
9090
9177
|
}
|
|
9091
|
-
console.log(`Bundle written to ${
|
|
9178
|
+
console.log(`Bundle written to ${outputPath}`);
|
|
9179
|
+
console.log(`Format: ${format}`);
|
|
9092
9180
|
console.log(`Safe status: ${metadata.safeStatus}`);
|
|
9093
9181
|
console.log(`Runs: ${resolveResult.runIds.join(", ")}`);
|
|
9094
9182
|
console.log(`Files: ${metadata.files.length}`);
|
|
@@ -9108,68 +9196,44 @@ ${body}
|
|
|
9108
9196
|
</html>
|
|
9109
9197
|
`;
|
|
9110
9198
|
}
|
|
9111
|
-
function
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
if (
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
diagnostics.push({
|
|
9145
|
-
code: "artifact_path_escape",
|
|
9146
|
-
severity: "error",
|
|
9147
|
-
message: "Reporter artifact path must stay under the output directory.",
|
|
9148
|
-
target: rawPath
|
|
9149
|
-
});
|
|
9150
|
-
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
|
+
}
|
|
9151
9232
|
}
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path14.isAbsolute(relFromOutput)) {
|
|
9155
|
-
diagnostics.push({
|
|
9156
|
-
code: "artifact_path_escape",
|
|
9157
|
-
severity: "error",
|
|
9158
|
-
message: "Reporter artifact path resolved outside the output directory.",
|
|
9159
|
-
target: rawPath
|
|
9160
|
-
});
|
|
9161
|
-
return { ok: false, outputDir, diagnostics };
|
|
9233
|
+
if (!result.ok) {
|
|
9234
|
+
process.exitCode = 1;
|
|
9162
9235
|
}
|
|
9163
|
-
return {
|
|
9164
|
-
ok: true,
|
|
9165
|
-
outputDir,
|
|
9166
|
-
relativePath: normalized,
|
|
9167
|
-
absolutePath,
|
|
9168
|
-
diagnostics
|
|
9169
|
-
};
|
|
9170
9236
|
}
|
|
9171
|
-
|
|
9172
|
-
// packages/cli/src/ci-summary.ts
|
|
9173
9237
|
var NOTE2 = "Generated locally by AgentInspect from reporter artifact manifests. Trace contents are not embedded.";
|
|
9174
9238
|
var MAX_TEXT = 180;
|
|
9175
9239
|
var FRAMEWORKS = /* @__PURE__ */ new Set(["vitest", "jest", "manual"]);
|
|
@@ -9185,7 +9249,7 @@ function stable7(value) {
|
|
|
9185
9249
|
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable7(record[key])])
|
|
9186
9250
|
);
|
|
9187
9251
|
}
|
|
9188
|
-
function
|
|
9252
|
+
function writeJson6(value) {
|
|
9189
9253
|
return `${JSON.stringify(stable7(value), null, 2)}
|
|
9190
9254
|
`;
|
|
9191
9255
|
}
|
|
@@ -9297,23 +9361,23 @@ function readManifestDocument(value) {
|
|
|
9297
9361
|
};
|
|
9298
9362
|
}
|
|
9299
9363
|
function cwdRelative(filePath) {
|
|
9300
|
-
const relative =
|
|
9301
|
-
if (relative === "" || relative.startsWith("../") ||
|
|
9302
|
-
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);
|
|
9303
9367
|
}
|
|
9304
9368
|
return relative;
|
|
9305
9369
|
}
|
|
9306
9370
|
async function readReporterManifest(filePath) {
|
|
9307
|
-
const absolute =
|
|
9371
|
+
const absolute = path15.resolve(filePath);
|
|
9308
9372
|
const raw = await readFile(absolute, "utf-8");
|
|
9309
9373
|
const document = readManifestDocument(JSON.parse(raw));
|
|
9310
9374
|
const manifest = document.manifest;
|
|
9311
9375
|
const results = manifest.results.map((result) => ({
|
|
9312
9376
|
testId: safeText(result.testId),
|
|
9313
9377
|
name: safeText(result.name),
|
|
9314
|
-
...result.file === void 0 ? {} : { file: safeText(
|
|
9378
|
+
...result.file === void 0 ? {} : { file: safeText(path15.basename(result.file)) },
|
|
9315
9379
|
status: result.status,
|
|
9316
|
-
...result.tracePath === void 0 ? {} : { tracePath: safeText(
|
|
9380
|
+
...result.tracePath === void 0 ? {} : { tracePath: safeText(path15.basename(result.tracePath)) },
|
|
9317
9381
|
artifacts: result.artifacts,
|
|
9318
9382
|
diagnostics: result.diagnostics
|
|
9319
9383
|
}));
|
|
@@ -9452,20 +9516,20 @@ async function ciSummaryCommand(manifestPaths, options = {}) {
|
|
|
9452
9516
|
return;
|
|
9453
9517
|
}
|
|
9454
9518
|
const markdown = renderMarkdown2(result);
|
|
9455
|
-
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;
|
|
9456
9520
|
if (outputPath !== void 0) {
|
|
9457
|
-
await mkdir(
|
|
9521
|
+
await mkdir(path15.dirname(outputPath), { recursive: true });
|
|
9458
9522
|
await writeFile(outputPath, markdown, "utf-8");
|
|
9459
9523
|
}
|
|
9460
9524
|
const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
|
|
9461
9525
|
if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
|
|
9462
|
-
const summaryPath =
|
|
9463
|
-
await mkdir(
|
|
9526
|
+
const summaryPath = path15.resolve(summaryTarget);
|
|
9527
|
+
await mkdir(path15.dirname(summaryPath), { recursive: true });
|
|
9464
9528
|
await appendFile(summaryPath, `
|
|
9465
9529
|
${markdown}`, "utf-8");
|
|
9466
9530
|
}
|
|
9467
9531
|
if (options.json === true) {
|
|
9468
|
-
console.log(
|
|
9532
|
+
console.log(writeJson6(result).trimEnd());
|
|
9469
9533
|
} else if (outputPath !== void 0) {
|
|
9470
9534
|
console.log(`Wrote AgentInspect CI summary to ${outputPath}`);
|
|
9471
9535
|
console.log(`Status: ${result.status}`);
|
|
@@ -9608,7 +9672,7 @@ jobs:
|
|
|
9608
9672
|
}
|
|
9609
9673
|
async function planInit(options = {}) {
|
|
9610
9674
|
const framework = normalizeFramework(options.framework);
|
|
9611
|
-
const cwd =
|
|
9675
|
+
const cwd = path15.resolve(options.cwd ?? process.cwd());
|
|
9612
9676
|
const demoPath = framework === "custom" ? "examples/agent-inspect-demo.mjs" : `examples/agent-inspect-${framework}-demo.mjs`;
|
|
9613
9677
|
const candidates = [
|
|
9614
9678
|
{ rel: CONFIG_FILE, content: configTemplate(framework) },
|
|
@@ -9623,7 +9687,7 @@ async function planInit(options = {}) {
|
|
|
9623
9687
|
}
|
|
9624
9688
|
const files = [];
|
|
9625
9689
|
for (const candidate of candidates) {
|
|
9626
|
-
const abs =
|
|
9690
|
+
const abs = path15.join(cwd, candidate.rel);
|
|
9627
9691
|
try {
|
|
9628
9692
|
await access(abs);
|
|
9629
9693
|
files.push({
|
|
@@ -9644,12 +9708,12 @@ async function writePlannedFiles(plan, cwd, options) {
|
|
|
9644
9708
|
if (entry.action === "skip") {
|
|
9645
9709
|
continue;
|
|
9646
9710
|
}
|
|
9647
|
-
const abs =
|
|
9711
|
+
const abs = path15.join(cwd, entry.path);
|
|
9648
9712
|
if (options.dryRun) {
|
|
9649
9713
|
written.push(entry.path);
|
|
9650
9714
|
continue;
|
|
9651
9715
|
}
|
|
9652
|
-
await mkdir(
|
|
9716
|
+
await mkdir(path15.dirname(abs), { recursive: true });
|
|
9653
9717
|
const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate() : demoTemplate(plan.framework);
|
|
9654
9718
|
await writeFile(abs, content, "utf-8");
|
|
9655
9719
|
written.push(entry.path);
|
|
@@ -9657,7 +9721,7 @@ async function writePlannedFiles(plan, cwd, options) {
|
|
|
9657
9721
|
return written;
|
|
9658
9722
|
}
|
|
9659
9723
|
async function initCommand(options = {}) {
|
|
9660
|
-
const cwd =
|
|
9724
|
+
const cwd = path15.resolve(options.cwd ?? process.cwd());
|
|
9661
9725
|
try {
|
|
9662
9726
|
const plan = await planInit({ ...options, cwd });
|
|
9663
9727
|
const toWrite = plan.files.filter((file) => file.action === "create").map((f) => f.path);
|
|
@@ -9748,7 +9812,7 @@ function envCheck(name, optional = true) {
|
|
|
9748
9812
|
};
|
|
9749
9813
|
}
|
|
9750
9814
|
async function traceDirWritable(traceDir) {
|
|
9751
|
-
const resolved =
|
|
9815
|
+
const resolved = path15.resolve(traceDir);
|
|
9752
9816
|
try {
|
|
9753
9817
|
await mkdir(resolved, { recursive: true });
|
|
9754
9818
|
await access(resolved, constants.W_OK);
|
|
@@ -9769,10 +9833,10 @@ async function traceDirWritable(traceDir) {
|
|
|
9769
9833
|
}
|
|
9770
9834
|
}
|
|
9771
9835
|
function readPackageVersionNearEntry(entryPath, packageName) {
|
|
9772
|
-
let dir =
|
|
9773
|
-
const { root } =
|
|
9836
|
+
let dir = path15.dirname(path15.resolve(entryPath));
|
|
9837
|
+
const { root } = path15.parse(dir);
|
|
9774
9838
|
while (true) {
|
|
9775
|
-
const candidate =
|
|
9839
|
+
const candidate = path15.join(dir, "package.json");
|
|
9776
9840
|
if (existsSync(candidate)) {
|
|
9777
9841
|
try {
|
|
9778
9842
|
const pkg = JSON.parse(readFileSync(candidate, "utf8"));
|
|
@@ -9783,14 +9847,14 @@ function readPackageVersionNearEntry(entryPath, packageName) {
|
|
|
9783
9847
|
}
|
|
9784
9848
|
}
|
|
9785
9849
|
if (dir === root) break;
|
|
9786
|
-
const parent =
|
|
9850
|
+
const parent = path15.dirname(dir);
|
|
9787
9851
|
if (parent === dir) break;
|
|
9788
9852
|
dir = parent;
|
|
9789
9853
|
}
|
|
9790
9854
|
return void 0;
|
|
9791
9855
|
}
|
|
9792
9856
|
function resolveInstalledPackage(cwd, name) {
|
|
9793
|
-
const require2 = createRequire(
|
|
9857
|
+
const require2 = createRequire(path15.join(cwd, "package.json"));
|
|
9794
9858
|
try {
|
|
9795
9859
|
const entry = require2.resolve(name);
|
|
9796
9860
|
let version2;
|
|
@@ -9888,7 +9952,7 @@ function versionMismatchCheck(cwd) {
|
|
|
9888
9952
|
};
|
|
9889
9953
|
}
|
|
9890
9954
|
async function runDoctorChecks(options = {}) {
|
|
9891
|
-
const cwd =
|
|
9955
|
+
const cwd = path15.resolve(options.cwd ?? process2.cwd());
|
|
9892
9956
|
const traceDir = options.traceDir?.trim() || process2.env.AGENT_INSPECT_TRACE_DIR?.trim() || ".agent-inspect";
|
|
9893
9957
|
const checks2 = [
|
|
9894
9958
|
nodeVersionCheck(),
|
|
@@ -10128,7 +10192,7 @@ function parsePluginManifest(input3) {
|
|
|
10128
10192
|
};
|
|
10129
10193
|
}
|
|
10130
10194
|
async function readPluginManifestFile(packageDir) {
|
|
10131
|
-
const manifestPath =
|
|
10195
|
+
const manifestPath = path15.join(packageDir, PLUGIN_MANIFEST_FILENAME);
|
|
10132
10196
|
try {
|
|
10133
10197
|
const raw = await readFile(manifestPath, "utf8");
|
|
10134
10198
|
const parsed = parsePluginManifest(JSON.parse(raw));
|
|
@@ -10154,7 +10218,7 @@ async function indexIsStale(snapshot, traceDir) {
|
|
|
10154
10218
|
const files = await td.list();
|
|
10155
10219
|
for (const file of files) {
|
|
10156
10220
|
const stats = await td.getFileStats(file);
|
|
10157
|
-
if (stats.mtimeMs > builtMs) return true;
|
|
10221
|
+
if (Math.floor(stats.mtimeMs) > builtMs) return true;
|
|
10158
10222
|
}
|
|
10159
10223
|
return false;
|
|
10160
10224
|
}
|
|
@@ -10202,7 +10266,7 @@ function createTraceDirectoryIndexer() {
|
|
|
10202
10266
|
// packages/cli/src/plugins.ts
|
|
10203
10267
|
async function readPackageName(packageDir) {
|
|
10204
10268
|
try {
|
|
10205
|
-
const raw = await readFile(
|
|
10269
|
+
const raw = await readFile(path15.join(packageDir, "package.json"), "utf8");
|
|
10206
10270
|
const parsed = JSON.parse(raw);
|
|
10207
10271
|
return typeof parsed.name === "string" ? parsed.name : void 0;
|
|
10208
10272
|
} catch {
|
|
@@ -10210,7 +10274,7 @@ async function readPackageName(packageDir) {
|
|
|
10210
10274
|
}
|
|
10211
10275
|
}
|
|
10212
10276
|
async function discoverPlugins(cwd = process.cwd()) {
|
|
10213
|
-
const nodeModules =
|
|
10277
|
+
const nodeModules = path15.join(cwd, "node_modules");
|
|
10214
10278
|
const found = [];
|
|
10215
10279
|
let entries;
|
|
10216
10280
|
try {
|
|
@@ -10221,7 +10285,7 @@ async function discoverPlugins(cwd = process.cwd()) {
|
|
|
10221
10285
|
for (const entry of entries.sort()) {
|
|
10222
10286
|
if (entry.startsWith("@")) continue;
|
|
10223
10287
|
if (!isPluginPackageName(entry)) continue;
|
|
10224
|
-
const packageDir =
|
|
10288
|
+
const packageDir = path15.join(nodeModules, entry);
|
|
10225
10289
|
const manifestRead = await readPluginManifestFile(packageDir);
|
|
10226
10290
|
found.push({
|
|
10227
10291
|
packageName: entry,
|
|
@@ -10234,7 +10298,7 @@ async function discoverPlugins(cwd = process.cwd()) {
|
|
|
10234
10298
|
return found;
|
|
10235
10299
|
}
|
|
10236
10300
|
async function validatePluginPackage(packageRef, cwd = process.cwd()) {
|
|
10237
|
-
const packageDir =
|
|
10301
|
+
const packageDir = path15.isAbsolute(packageRef) ? packageRef : path15.join(cwd, "node_modules", packageRef);
|
|
10238
10302
|
const packageName = await readPackageName(packageDir) ?? packageRef;
|
|
10239
10303
|
const errors = [];
|
|
10240
10304
|
const warnings = [];
|
|
@@ -10310,7 +10374,7 @@ async function pluginsValidateCommand(packageRef, cwd) {
|
|
|
10310
10374
|
}
|
|
10311
10375
|
var INDEX_FILENAME = ".agent-inspect-index.json";
|
|
10312
10376
|
function traceIndexPath(traceDir) {
|
|
10313
|
-
return
|
|
10377
|
+
return path15.join(traceDir, INDEX_FILENAME);
|
|
10314
10378
|
}
|
|
10315
10379
|
function parseMaxEntries(raw) {
|
|
10316
10380
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
@@ -10423,7 +10487,7 @@ function isModuleNotFound4(e) {
|
|
|
10423
10487
|
}
|
|
10424
10488
|
async function loadIndexSqlite() {
|
|
10425
10489
|
try {
|
|
10426
|
-
return await import('./src-
|
|
10490
|
+
return await import('./src-YFN3Q3GP.mjs');
|
|
10427
10491
|
} catch (e) {
|
|
10428
10492
|
if (isModuleNotFound4(e)) {
|
|
10429
10493
|
console.error(
|
|
@@ -10453,7 +10517,7 @@ async function newestTraceMtimeMs2(traceDir) {
|
|
|
10453
10517
|
for (const file of files) {
|
|
10454
10518
|
if (!file.endsWith(".jsonl")) continue;
|
|
10455
10519
|
try {
|
|
10456
|
-
const s = await stat(
|
|
10520
|
+
const s = await stat(path15.join(traceDir, file));
|
|
10457
10521
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
10458
10522
|
} catch {
|
|
10459
10523
|
}
|
|
@@ -10705,11 +10769,11 @@ function printJson5(value) {
|
|
|
10705
10769
|
console.log(JSON.stringify(value, null, 2));
|
|
10706
10770
|
}
|
|
10707
10771
|
function resolveCwd(options) {
|
|
10708
|
-
return
|
|
10772
|
+
return path15.resolve(options.cwd ?? process.cwd());
|
|
10709
10773
|
}
|
|
10710
10774
|
async function suiteInitCommand(options = {}) {
|
|
10711
10775
|
const cwd = resolveCwd(options);
|
|
10712
|
-
const configPath =
|
|
10776
|
+
const configPath = path15.join(cwd, DEFAULT_CONFIG_FILENAME);
|
|
10713
10777
|
const template = options.template?.trim();
|
|
10714
10778
|
const suiteConfig = template !== void 0 && template !== "" ? resolveSuiteTemplate(template) : defaultSuiteConfigTemplate();
|
|
10715
10779
|
if (options.dryRun) {
|
|
@@ -10828,13 +10892,13 @@ async function suiteListCommand(options = {}) {
|
|
|
10828
10892
|
}
|
|
10829
10893
|
async function writeSuiteArtifact(result, configOutputDir, options) {
|
|
10830
10894
|
const cwd = resolveCwd(options);
|
|
10831
|
-
const outputDir =
|
|
10895
|
+
const outputDir = path15.resolve(
|
|
10832
10896
|
cwd,
|
|
10833
10897
|
options.output ?? configOutputDir ?? DEFAULT_SUITE_ARTIFACTS_DIR
|
|
10834
10898
|
);
|
|
10835
10899
|
await mkdir(outputDir, { recursive: true });
|
|
10836
10900
|
const stamp = result.startedAt.replace(/[:.]/g, "-");
|
|
10837
|
-
const filePath =
|
|
10901
|
+
const filePath = path15.join(outputDir, `${result.suiteName}-${stamp}.json`);
|
|
10838
10902
|
await writeFile(filePath, `${JSON.stringify(result, null, 2)}
|
|
10839
10903
|
`, "utf-8");
|
|
10840
10904
|
return filePath;
|
|
@@ -10892,7 +10956,7 @@ async function loadSuiteResultFromInput(options) {
|
|
|
10892
10956
|
if (options.input === void 0 || options.input.trim() === "") {
|
|
10893
10957
|
throw new Error("Pass --input <suite-run.json> from a prior suite run.");
|
|
10894
10958
|
}
|
|
10895
|
-
const inputPath =
|
|
10959
|
+
const inputPath = path15.resolve(cwd, options.input.trim());
|
|
10896
10960
|
const raw = await readFile(inputPath, "utf-8");
|
|
10897
10961
|
return JSON.parse(raw);
|
|
10898
10962
|
}
|
|
@@ -10926,9 +10990,9 @@ function normalizeMetrics(raw) {
|
|
|
10926
10990
|
}
|
|
10927
10991
|
async function writeArtifacts(result, outputDir) {
|
|
10928
10992
|
await mkdir(outputDir, { recursive: true });
|
|
10929
|
-
const jsonPath =
|
|
10930
|
-
const markdownPath =
|
|
10931
|
-
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");
|
|
10932
10996
|
await writeFile(jsonPath, `${renderCohortReport(result, { format: "json" })}
|
|
10933
10997
|
`, "utf-8");
|
|
10934
10998
|
await writeFile(
|
|
@@ -10956,7 +11020,7 @@ async function cohortCommand(options = {}) {
|
|
|
10956
11020
|
const format = options.format ?? (options.json ? "json" : "markdown");
|
|
10957
11021
|
let artifacts;
|
|
10958
11022
|
if (options.output !== void 0 && options.output.trim() !== "") {
|
|
10959
|
-
artifacts = await writeArtifacts(result,
|
|
11023
|
+
artifacts = await writeArtifacts(result, path15.resolve(options.output.trim()));
|
|
10960
11024
|
}
|
|
10961
11025
|
if (options.json || format === "json") {
|
|
10962
11026
|
console.log(
|
|
@@ -11026,11 +11090,11 @@ function parseDuration2(value) {
|
|
|
11026
11090
|
async function writeArtifacts2(result, outputDir) {
|
|
11027
11091
|
await mkdir(outputDir, { recursive: true });
|
|
11028
11092
|
const paths = {
|
|
11029
|
-
jsonPath:
|
|
11030
|
-
markdownPath:
|
|
11031
|
-
htmlPath:
|
|
11032
|
-
junitPath:
|
|
11033
|
-
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")
|
|
11034
11098
|
};
|
|
11035
11099
|
await writeFile(
|
|
11036
11100
|
paths.jsonPath,
|
|
@@ -11098,7 +11162,7 @@ async function gateCommand(options = {}) {
|
|
|
11098
11162
|
const result = await runGate(runs, gateOptions);
|
|
11099
11163
|
let artifacts;
|
|
11100
11164
|
if (options.output !== void 0 && options.output.trim() !== "") {
|
|
11101
|
-
artifacts = await writeArtifacts2(result,
|
|
11165
|
+
artifacts = await writeArtifacts2(result, path15.resolve(options.output.trim()));
|
|
11102
11166
|
}
|
|
11103
11167
|
if (options.json || format === "json") {
|
|
11104
11168
|
console.log(JSON.stringify({ ...result, artifacts: artifacts ?? null }, null, 2));
|
|
@@ -11335,7 +11399,7 @@ function createCliProgram() {
|
|
|
11335
11399
|
"openinference-json",
|
|
11336
11400
|
"otlp-json"
|
|
11337
11401
|
])
|
|
11338
|
-
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
|
|
11402
|
+
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--explain", "explain each finding (path, confidence, redaction, override, bundle gate)").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
|
|
11339
11403
|
runCommand(() => scanCommand(target, opts));
|
|
11340
11404
|
});
|
|
11341
11405
|
program.command("verify-safe").description("Best-effort local trace safety verification").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").option("--dir <path>", "trace directory for run-id lookup").addOption(
|
|
@@ -11344,7 +11408,7 @@ function createCliProgram() {
|
|
|
11344
11408
|
"openinference-json",
|
|
11345
11409
|
"otlp-json"
|
|
11346
11410
|
])
|
|
11347
|
-
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
|
|
11411
|
+
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--explain", "explain each finding (path, confidence, redaction, override, bundle gate)").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
|
|
11348
11412
|
runCommand(() => verifySafeCommand(target, opts));
|
|
11349
11413
|
});
|
|
11350
11414
|
program.command("artifacts").description("Create safe local CI trace artifacts").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").requiredOption("--output-dir <path>", "directory for generated artifacts").option("--dir <path>", "trace directory for run-id lookup").addOption(
|
|
@@ -11353,19 +11417,26 @@ function createCliProgram() {
|
|
|
11353
11417
|
"openinference-json",
|
|
11354
11418
|
"otlp-json"
|
|
11355
11419
|
])
|
|
11356
|
-
).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) => {
|
|
11357
11421
|
runCommand(() => artifactsCommand(target, opts));
|
|
11358
11422
|
});
|
|
11359
|
-
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(
|
|
11360
11424
|
new Option("--profile <profile>", "redaction profile for exported copies").choices(["local", "share", "strict"]).default("share")
|
|
11361
11425
|
).addOption(
|
|
11362
11426
|
new Option(
|
|
11363
11427
|
"--redaction-profile <profile>",
|
|
11364
11428
|
"alias for --profile (canonical spelling)"
|
|
11365
11429
|
).choices(["local", "share", "strict"])
|
|
11366
|
-
).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) => {
|
|
11367
11433
|
runCommand(() => bundleCommand(runId, opts));
|
|
11368
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
|
+
});
|
|
11369
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) => {
|
|
11370
11441
|
runCommand(() => ciSummaryCommand(manifest, opts));
|
|
11371
11442
|
});
|
|
@@ -11639,9 +11710,9 @@ function isPrimaryModule() {
|
|
|
11639
11710
|
if (!entry) return false;
|
|
11640
11711
|
const selfPath = fileURLToPath(import.meta.url);
|
|
11641
11712
|
try {
|
|
11642
|
-
return realpathSync(
|
|
11713
|
+
return realpathSync(path15.resolve(entry)) === realpathSync(path15.resolve(selfPath));
|
|
11643
11714
|
} catch {
|
|
11644
|
-
return
|
|
11715
|
+
return path15.resolve(entry) === path15.resolve(selfPath);
|
|
11645
11716
|
}
|
|
11646
11717
|
}
|
|
11647
11718
|
if (isPrimaryModule()) {
|