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.
Files changed (41) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/docs/BUNDLES.md +31 -9
  4. package/docs/CI-ARTIFACTS.md +10 -1
  5. package/docs/CLI.md +20 -4
  6. package/package.json +1 -1
  7. package/packages/cli/dist/{chunk-TO4VENHV.mjs → chunk-36IJ76LH.mjs} +1857 -158
  8. package/packages/cli/dist/chunk-36IJ76LH.mjs.map +1 -0
  9. package/packages/cli/dist/index.cjs +10764 -8877
  10. package/packages/cli/dist/index.cjs.map +1 -1
  11. package/packages/cli/dist/index.mjs +825 -754
  12. package/packages/cli/dist/index.mjs.map +1 -1
  13. package/packages/cli/dist/{src-Z5W27YCW.mjs → src-YFN3Q3GP.mjs} +3 -3
  14. package/packages/cli/dist/{src-Z5W27YCW.mjs.map → src-YFN3Q3GP.mjs.map} +1 -1
  15. package/packages/core/dist/advanced.cjs +1718 -18
  16. package/packages/core/dist/advanced.cjs.map +1 -1
  17. package/packages/core/dist/advanced.d.cts +338 -4
  18. package/packages/core/dist/advanced.d.ts +338 -4
  19. package/packages/core/dist/advanced.mjs +1166 -12
  20. package/packages/core/dist/advanced.mjs.map +1 -1
  21. package/packages/core/dist/checks.cjs +105 -14
  22. package/packages/core/dist/checks.cjs.map +1 -1
  23. package/packages/core/dist/checks.d.cts +2 -2
  24. package/packages/core/dist/checks.d.ts +2 -2
  25. package/packages/core/dist/checks.mjs +1 -1
  26. package/packages/core/dist/chunk-F7STQ5JF.mjs +479 -0
  27. package/packages/core/dist/chunk-F7STQ5JF.mjs.map +1 -0
  28. package/packages/core/dist/{chunk-EQVXDGGU.mjs → chunk-QT6CQ2XA.mjs} +107 -16
  29. package/packages/core/dist/chunk-QT6CQ2XA.mjs.map +1 -0
  30. package/packages/core/dist/diff.mjs +3 -477
  31. package/packages/core/dist/diff.mjs.map +1 -1
  32. package/packages/core/dist/{index-BblJEdYZ.d.ts → index-BO7l0iAe.d.ts} +44 -1
  33. package/packages/core/dist/{index-DyKk7iR9.d.cts → index-mdFcxSOR.d.cts} +44 -1
  34. package/packages/core/dist/reporters.cjs +25 -0
  35. package/packages/core/dist/reporters.cjs.map +1 -1
  36. package/packages/core/dist/reporters.d.cts +16 -2
  37. package/packages/core/dist/reporters.d.ts +16 -2
  38. package/packages/core/dist/reporters.mjs +24 -1
  39. package/packages/core/dist/reporters.mjs.map +1 -1
  40. package/packages/cli/dist/chunk-TO4VENHV.mjs.map +0 -1
  41. 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, extractMetadata, filterTraces, truncateName, getTraceFilePath, buildRunSummary, formatDuration, formatTimestamp, isAgentInspectTrace, openTrace, TraceReadError, filterMetasBySessionScope, runTraceChecks, aggregateSessionCheckResults, createBaselineRegressionRule, resolveBundleRunIds, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, buildBundleMetadata, buildBundleSummaryMarkdown, 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, Redactor, TreeBuilder, zeroKinds, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, assertBundlePathContained, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, escapeMarkdown, escapeHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, resolveRedactionProfile, applyProfileMetadataCaps, flattenTree, safeString, compactAttributes, stableJson, truncateStringForProfile } from './chunk-TO4VENHV.mjs';
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 path14 from 'path';
4
+ import path15 from 'path';
5
5
  import { fileURLToPath, pathToFileURL } from 'url';
6
6
  import { Command, Option } from 'commander';
7
- import { unlink, stat, mkdir, writeFile, appendFile, readFile, rm, access, open, readdir, constants } from 'fs/promises';
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.8.0";
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 ? path14.resolve(resolvedOutput) : void 0;
2974
+ const outPath = resolvedOutput !== void 0 ? path15.resolve(resolvedOutput) : void 0;
2975
2975
  if (outPath !== void 0) {
2976
- await mkdir(path14.dirname(outPath), { recursive: true });
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: path14.join(traceDir, row.file),
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-Z5W27YCW.mjs');
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 ? path14.resolve(resolvedOutput) : void 0;
3798
+ const outPath = resolvedOutput !== void 0 ? path15.resolve(resolvedOutput) : void 0;
4261
3799
  if (outPath !== void 0) {
4262
- await mkdir(path14.dirname(outPath), { recursive: true });
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 (passesLuhn(candidate)) {
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
- patternDetector({
4039
+ {
4440
4040
  id: "value.email",
4441
- pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i
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(path25, key) {
4114
+ function childPath(path26, key) {
4510
4115
  if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
4511
- return path25 ? `${path25}.${key}` : key;
4116
+ return path26 ? `${path26}.${key}` : key;
4512
4117
  }
4513
- return `${path25 || "$"}[${JSON.stringify(key)}]`;
4118
+ return `${path26 || "$"}[${JSON.stringify(key)}]`;
4514
4119
  }
4515
- function indexPath(path25, index) {
4516
- return `${path25 || "$"}[${index}]`;
4120
+ function indexPath(path26, index) {
4121
+ return `${path26 || "$"}[${index}]`;
4517
4122
  }
4518
- function makeFinding(path25, detector, action, matchKind, severity = "warning", preview) {
4519
- return preview === void 0 ? { path: path25, detector, action, severity, matchKind } : { path: path25, detector, action, severity, matchKind, preview };
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, path25, depth, state) {
4193
+ #redactValue(value, key, path26, depth, state) {
4589
4194
  if (depth > this.#maxDepth) {
4590
4195
  this.#recordFinding(
4591
4196
  state,
4592
- makeFinding(path25, "structure.maxDepth", "truncate", "value", "warning")
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(path25, `key.${rule.key}`, actionForRule(rule), "key", "warning")
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: path25, key, value });
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
- path25,
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(path25, index), depth + 1, state);
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(path25 === "$" ? "" : path25, entryKey),
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 = path14.relative(parent, child);
5095
- return relative === "" || !relative.startsWith("..") && !path14.isAbsolute(relative);
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 = path14.resolve(inputPath);
5100
- const outputAbs = path14.resolve(output2.trim());
5101
- const inputDir = path14.dirname(inputAbs);
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 = path14.resolve(input3.trim());
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(path14.dirname(outputPath), { recursive: true });
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, path25, evidence) {
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: path25, preview: `expected ${field.type}, got ${actual}` });
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: path25, preview: "value not in enum" });
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: `${path25}.${key}`, preview: "missing required key" });
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 = path14.extname(configPath);
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 = path14.resolve(configPath);
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 path25 = finding.evidence[0]?.path;
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}${path25 ? ` (${path25})` : ""}`);
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 = path14.join(path14.dirname(result.configPath), ".agent-inspect/suite-runs");
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 = path14.resolve(cwd);
6558
- const workspaceDir = path14.join(projectRoot, WORKSPACE_DIR_NAME);
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: path14.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
6177
+ manifestPath: path15.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
6563
6178
  };
6564
6179
  }
6565
6180
  function resolveInsideWorkspace(workspaceDir, relative) {
6566
- const base = path14.resolve(workspaceDir);
6567
- const resolved = path14.resolve(base, relative);
6568
- const rel = path14.relative(base, resolved);
6569
- if (rel === "" || rel === "." || !rel.startsWith("..") && !path14.isAbsolute(rel)) {
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() || path14.basename(location.projectRoot) || "workspace";
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(path14.join(abs, file));
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(path14.join(abs, entry), { recursive: true, force: true });
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 = path14.resolve(options.cwd ?? process.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: path14.join(location.workspaceDir, rel)
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: path14.basename(meta.filePath)
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
- path14.join(location.workspaceDir, manifestRead.manifest.bundlesDir),
6873
- path14.join(location.workspaceDir, manifestRead.manifest.artifactsDir)
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: path14.resolve(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: path14.basename(meta.filePath),
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: path14.resolve(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, path25) {
7454
- return [{ runId: run.runId, ...path25 !== void 0 ? { path: path25 } : {} }];
7068
+ function evidenceForRun(run, path26) {
7069
+ return [{ runId: run.runId, ...path26 !== void 0 ? { path: path26 } : {} }];
7455
7070
  }
7456
- function evidenceForEvent(event, path25) {
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
- ...path25 !== void 0 ? { path: path25 } : {}
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, path25) {
7237
+ function firstEvidence(fields, run, path26) {
7623
7238
  const first = fields[0];
7624
- return first === void 0 ? evidenceForRun(run, path25) : evidenceForEvent(first.node.event, first.path);
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 path25 = finding.evidence[0]?.path;
8002
- lines.push(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
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 = path14.extname(configPath);
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 = path14.resolve(configPath);
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 path25 = finding.evidence[0]?.path;
8196
- console.log(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
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 printJson3(result) {
8421
- console.log(JSON.stringify(stable4(result), null, 2));
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 path25 = finding.evidence[0]?.path;
8435
- console.log(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
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 checkResult = runTraceChecks(
8448
- { read },
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 path25 = finding.evidence[0]?.path ?? "(run)";
8587
- lines.push(`- ${finding.ruleId}: ${finding.message} (${path25})`);
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 = path14.join(outputDir, relativePath);
8666
- await mkdir(path14.dirname(outPath), { recursive: true });
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() !== "" ? path14.resolve(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(path14.dirname(path14.resolve(summaryTarget)), { recursive: true });
8749
- await appendFile(path14.resolve(summaryTarget), `
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: manifestStatus(check, diff),
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: path14.resolve(summaryTarget) } : {},
8644
+ ...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path15.resolve(summaryTarget) } : {},
8769
8645
  note: NOTE
8770
8646
  };
8771
- await writeFile(path14.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
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
- async function resolveOutputDir(options, runIds, cwd) {
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 = path14.relative(location.workspaceDir, normalized);
8822
- if (!rel.startsWith("..") && !path14.isAbsolute(rel)) {
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
- return resolveInsideWorkspace(
8720
+ const base2 = resolveInsideWorkspace(
8837
8721
  location.workspaceDir,
8838
- path14.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
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
- return defaultBundleOutputPath(runIds);
8728
+ const base = defaultBundleOutputPath(runIds);
8729
+ return format === "zip" ? `${base}.zip` : base;
8844
8730
  }
8845
- async function writeBundleFile(outputDir, relativePath, content, files) {
8846
- const outPath = assertBundlePathContained(outputDir, relativePath);
8847
- await mkdir(path14.dirname(outPath), { recursive: true });
8848
- await writeFile(outPath, content, "utf-8");
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 safety = assessOpenedTrace(read, { run: runId });
8953
- const status = safetyStatusFromAssess(safety.status);
8954
- checkRuns.push({
8844
+ const sourceSafety = assessOpenedTrace(read, { run: runId });
8845
+ const sourceStatus = safetyStatusFromAssess(sourceSafety.status);
8846
+ sourceHashes.push({
8955
8847
  runId,
8956
- status,
8957
- errors: safety.summary.errors,
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
- await writeBundleFile(
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
- await writeBundleFile(
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
- await writeBundleFile(outputDir, "trace.jsonl", combinedJsonl, files);
9038
- await writeBundleFile(outputDir, "trace.html", traceHtml, files);
9039
- await writeBundleFile(
9040
- outputDir,
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
- await writeBundleFile(
9052
- outputDir,
8990
+ stageBundleFile(
9053
8991
  "redaction-report.json",
9054
8992
  writeJson4(redactionReport),
9055
- files
8993
+ files,
8994
+ packaged
9056
8995
  );
9057
- await writeBundleFile(
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: [...files]
9019
+ files: metadataFileList
9069
9020
  });
9070
- await writeBundleFile(outputDir, "metadata.json", writeJson4(metadata), files);
9071
- await writeBundleFile(
9072
- outputDir,
9073
- "summary.md",
9074
- buildBundleSummaryMarkdown({ metadata, checks: checks2, redaction: redactionReport }),
9075
- files
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
- metadata.files = [...files].sort((a, b) => a.localeCompare(b));
9078
- await writeFile(path14.join(outputDir, "metadata.json"), writeJson4(metadata), "utf-8");
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
- outputDir,
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 ${outputDir}`);
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 validateReporterArtifactPath(options) {
9112
- const outputDir = path14.resolve(options.outputDir);
9113
- const diagnostics = [];
9114
- const rawPath = options.relativePath;
9115
- if (rawPath.length === 0) {
9116
- diagnostics.push({
9117
- code: "artifact_path_empty",
9118
- severity: "error",
9119
- message: "Reporter artifact path must not be empty."
9120
- });
9121
- return { ok: false, outputDir, diagnostics };
9122
- }
9123
- if (rawPath.includes("\0")) {
9124
- diagnostics.push({
9125
- code: "invalid_artifact_path",
9126
- severity: "error",
9127
- message: "Reporter artifact path must not contain null bytes.",
9128
- target: rawPath
9129
- });
9130
- return { ok: false, outputDir, diagnostics };
9131
- }
9132
- if (path14.isAbsolute(rawPath) || path14.win32.isAbsolute(rawPath)) {
9133
- diagnostics.push({
9134
- code: "artifact_path_absolute",
9135
- severity: "error",
9136
- message: "Reporter artifact path must be relative.",
9137
- target: rawPath
9138
- });
9139
- return { ok: false, outputDir, diagnostics };
9140
- }
9141
- const normalized = path14.posix.normalize(rawPath.replace(/\\/g, "/"));
9142
- const segments = normalized.split("/");
9143
- if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
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
- const absolutePath = path14.resolve(outputDir, normalized);
9153
- const relFromOutput = path14.relative(outputDir, absolutePath);
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 writeJson5(value) {
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 = path14.relative(process.cwd(), path14.resolve(filePath)).replace(/\\/g, "/");
9301
- if (relative === "" || relative.startsWith("../") || path14.isAbsolute(relative)) {
9302
- return path14.basename(filePath);
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 = path14.resolve(filePath);
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(path14.basename(result.file)) },
9378
+ ...result.file === void 0 ? {} : { file: safeText(path15.basename(result.file)) },
9315
9379
  status: result.status,
9316
- ...result.tracePath === void 0 ? {} : { tracePath: safeText(path14.basename(result.tracePath)) },
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() !== "" ? path14.resolve(options.output.trim()) : void 0;
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(path14.dirname(outputPath), { recursive: true });
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 = path14.resolve(summaryTarget);
9463
- await mkdir(path14.dirname(summaryPath), { recursive: true });
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(writeJson5(result).trimEnd());
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 = path14.resolve(options.cwd ?? process.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 = path14.join(cwd, candidate.rel);
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 = path14.join(cwd, entry.path);
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(path14.dirname(abs), { recursive: true });
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 = path14.resolve(options.cwd ?? process.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 = path14.resolve(traceDir);
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 = path14.dirname(path14.resolve(entryPath));
9773
- const { root } = path14.parse(dir);
9836
+ let dir = path15.dirname(path15.resolve(entryPath));
9837
+ const { root } = path15.parse(dir);
9774
9838
  while (true) {
9775
- const candidate = path14.join(dir, "package.json");
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 = path14.dirname(dir);
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(path14.join(cwd, "package.json"));
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 = path14.resolve(options.cwd ?? process2.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 = path14.join(packageDir, PLUGIN_MANIFEST_FILENAME);
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(path14.join(packageDir, "package.json"), "utf8");
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 = path14.join(cwd, "node_modules");
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 = path14.join(nodeModules, entry);
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 = path14.isAbsolute(packageRef) ? packageRef : path14.join(cwd, "node_modules", packageRef);
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 path14.join(traceDir, INDEX_FILENAME);
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-Z5W27YCW.mjs');
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(path14.join(traceDir, file));
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 path14.resolve(options.cwd ?? process.cwd());
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 = path14.join(cwd, DEFAULT_CONFIG_FILENAME);
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 = path14.resolve(
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 = path14.join(outputDir, `${result.suiteName}-${stamp}.json`);
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 = path14.resolve(cwd, options.input.trim());
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 = path14.join(outputDir, "cohort-results.json");
10930
- const markdownPath = path14.join(outputDir, "cohort-summary.md");
10931
- const htmlPath = path14.join(outputDir, "cohort-report.html");
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, path14.resolve(options.output.trim()));
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: path14.join(outputDir, "gate-results.json"),
11030
- markdownPath: path14.join(outputDir, "gate-summary.md"),
11031
- htmlPath: path14.join(outputDir, "gate-report.html"),
11032
- junitPath: path14.join(outputDir, "junit.xml"),
11033
- githubPath: path14.join(outputDir, "github-step-summary.md")
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, path14.resolve(options.output.trim()));
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 a share-safe offline trace bundle (local folder)").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(
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 (folder; .zip suffix is stripped)").option("--output <path>", "alias for --out (canonical spelling)").option("--allow-unsafe", "write bundle even when verify-safe reports UNSAFE").option("--json", "print deterministic JSON manifest").action((runId, opts) => {
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(path14.resolve(entry)) === realpathSync(path14.resolve(selfPath));
11713
+ return realpathSync(path15.resolve(entry)) === realpathSync(path15.resolve(selfPath));
11643
11714
  } catch {
11644
- return path14.resolve(entry) === path14.resolve(selfPath);
11715
+ return path15.resolve(entry) === path15.resolve(selfPath);
11645
11716
  }
11646
11717
  }
11647
11718
  if (isPrimaryModule()) {