agent-inspect 6.8.0 → 6.9.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 (29) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -1
  3. package/docs/CLI.md +10 -3
  4. package/package.json +1 -1
  5. package/packages/cli/dist/{chunk-TO4VENHV.mjs → chunk-WQ5XMFH2.mjs} +109 -17
  6. package/packages/cli/dist/chunk-WQ5XMFH2.mjs.map +1 -0
  7. package/packages/cli/dist/index.cjs +357 -66
  8. package/packages/cli/dist/index.cjs.map +1 -1
  9. package/packages/cli/dist/index.mjs +252 -53
  10. package/packages/cli/dist/index.mjs.map +1 -1
  11. package/packages/cli/dist/{src-Z5W27YCW.mjs → src-C4EUHTER.mjs} +3 -3
  12. package/packages/cli/dist/{src-Z5W27YCW.mjs.map → src-C4EUHTER.mjs.map} +1 -1
  13. package/packages/core/dist/advanced.cjs +15 -6
  14. package/packages/core/dist/advanced.cjs.map +1 -1
  15. package/packages/core/dist/advanced.d.cts +4 -1
  16. package/packages/core/dist/advanced.d.ts +4 -1
  17. package/packages/core/dist/advanced.mjs +3 -2
  18. package/packages/core/dist/advanced.mjs.map +1 -1
  19. package/packages/core/dist/checks.cjs +105 -14
  20. package/packages/core/dist/checks.cjs.map +1 -1
  21. package/packages/core/dist/checks.d.cts +2 -2
  22. package/packages/core/dist/checks.d.ts +2 -2
  23. package/packages/core/dist/checks.mjs +1 -1
  24. package/packages/core/dist/{chunk-EQVXDGGU.mjs → chunk-QT6CQ2XA.mjs} +107 -16
  25. package/packages/core/dist/chunk-QT6CQ2XA.mjs.map +1 -0
  26. package/packages/core/dist/{index-BblJEdYZ.d.ts → index-BO7l0iAe.d.ts} +44 -1
  27. package/packages/core/dist/{index-DyKk7iR9.d.cts → index-mdFcxSOR.d.cts} +44 -1
  28. package/packages/cli/dist/chunk-TO4VENHV.mjs.map +0 -1
  29. package/packages/core/dist/chunk-EQVXDGGU.mjs.map +0 -1
@@ -1,5 +1,5 @@
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, 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-WQ5XMFH2.mjs';
3
3
  import { realpathSync, constants as constants$1, existsSync, readFileSync } from 'fs';
4
4
  import path14 from 'path';
5
5
  import { fileURLToPath, pathToFileURL } from 'url';
@@ -11,7 +11,7 @@ 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.9.0";
15
15
 
16
16
  // packages/cli/src/trace-dir-scale.ts
17
17
  var TRACE_COUNT_WARN = 1e3;
@@ -3808,7 +3808,7 @@ async function newestTraceMtimeMs(traceDir) {
3808
3808
  }
3809
3809
  async function loadSessionRuns(traceDir) {
3810
3810
  try {
3811
- const mod = await import('./src-Z5W27YCW.mjs');
3811
+ const mod = await import('./src-C4EUHTER.mjs');
3812
3812
  const dbPath = mod.resolveIndexDbPath(traceDir);
3813
3813
  const status = mod.indexStatus(dbPath);
3814
3814
  if (!status.healthy) {
@@ -4309,7 +4309,12 @@ var SHARE_PROFILE_EXTRA_KEYS = [
4309
4309
  "organizationId",
4310
4310
  "traceId",
4311
4311
  "spanId",
4312
- "parentSpanId"
4312
+ "parentSpanId",
4313
+ "currentTask",
4314
+ "task",
4315
+ "userInput",
4316
+ "requestText",
4317
+ "conversationText"
4313
4318
  ];
4314
4319
  var STRICT_PROFILE_EXTRA_KEYS = [
4315
4320
  "prompt",
@@ -4377,6 +4382,61 @@ function passesLuhn(value) {
4377
4382
  }
4378
4383
  return sum % 10 === 0;
4379
4384
  }
4385
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
4386
+ var EPOCH_MS_RE = /^1[0-9]{12}$/;
4387
+ var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
4388
+ function pathSuggestsNonCard(path25) {
4389
+ const normalized = path25.toLowerCase().replace(/[^a-z0-9._]/g, "");
4390
+ return /(^|\.)(tokenusage|usage)(\.|$)/.test(normalized) || /(startedat|endedat|durationms|timestamp|createdat|updatedat)(\.|$)/.test(normalized) || /(^|\.)(runid|traceid|spanid|eventid|sessionid|userid|parentid|requestid|correlationid)(\.|$)/.test(
4391
+ normalized
4392
+ ) || /(^|\.)ts(\.|$)/.test(normalized);
4393
+ }
4394
+ function isUuidLike(value) {
4395
+ return UUID_RE.test(value.trim());
4396
+ }
4397
+ function isPlausibleCardCandidate(candidate, fullValue) {
4398
+ const digits = digitsOnly(candidate);
4399
+ if (digits.length < 13 || digits.length > 19) return false;
4400
+ if (!passesLuhn(candidate)) return false;
4401
+ const trimmed = fullValue.trim();
4402
+ if (isUuidLike(trimmed)) return false;
4403
+ if (EPOCH_MS_RE.test(digits) && digitsOnly(trimmed) === digits && /^\d+$/.test(trimmed)) {
4404
+ return false;
4405
+ }
4406
+ const start = fullValue.indexOf(candidate);
4407
+ if (start >= 0) {
4408
+ const before = fullValue[start - 1];
4409
+ const after = fullValue[start + candidate.length];
4410
+ if (before && /[A-Za-z0-9_]/.test(before)) return false;
4411
+ if (after && /[A-Za-z0-9_]/.test(after)) return false;
4412
+ }
4413
+ return true;
4414
+ }
4415
+ function looksLikePathOrPackageOrUrl(value) {
4416
+ const trimmed = value.trim();
4417
+ if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(trimmed)) return true;
4418
+ if (/^webpack:/i.test(trimmed)) return true;
4419
+ if (/^@[A-Za-z0-9_.-]+\/[A-Za-z0-9_.@/-]+$/.test(trimmed)) return true;
4420
+ if (/^[A-Za-z]:[\\/]/.test(trimmed) || /^[/\\]/.test(trimmed)) return true;
4421
+ if (/[\\/]/.test(trimmed) && !/\s/.test(trimmed)) return true;
4422
+ return false;
4423
+ }
4424
+ function emailMatchIsPathAdjacent(value, index, length) {
4425
+ const before = value[index - 1];
4426
+ const after = value[index + length];
4427
+ return before === "/" || before === "\\" || after === "/" || after === "\\";
4428
+ }
4429
+ function valueContainsEmail(value) {
4430
+ if (looksLikePathOrPackageOrUrl(value)) return false;
4431
+ EMAIL_RE.lastIndex = 0;
4432
+ for (const match of value.matchAll(EMAIL_RE)) {
4433
+ const text = match[0] ?? "";
4434
+ const index = match.index ?? 0;
4435
+ if (emailMatchIsPathAdjacent(value, index, text.length)) continue;
4436
+ return true;
4437
+ }
4438
+ return false;
4439
+ }
4380
4440
  var credentialDetectors = [
4381
4441
  patternDetector({
4382
4442
  id: "value.authorizationHeader",
@@ -4424,10 +4484,12 @@ var credentialDetectors = [
4424
4484
  matchKind: "value",
4425
4485
  detect(input3) {
4426
4486
  if (typeof input3.value !== "string") return [];
4487
+ if (pathSuggestsNonCard(input3.path)) return [];
4488
+ if (isUuidLike(input3.value)) return [];
4427
4489
  const candidatePattern = /(?:\d[ -]?){13,19}/g;
4428
4490
  for (const match of input3.value.matchAll(candidatePattern)) {
4429
4491
  const candidate = match[0] ?? "";
4430
- if (passesLuhn(candidate)) {
4492
+ if (isPlausibleCardCandidate(candidate, input3.value)) {
4431
4493
  return [{ action: "replace", severity: "error", matchKind: "value" }];
4432
4494
  }
4433
4495
  }
@@ -4436,10 +4498,15 @@ var credentialDetectors = [
4436
4498
  }
4437
4499
  ];
4438
4500
  var identifierDetectors = [
4439
- patternDetector({
4501
+ {
4440
4502
  id: "value.email",
4441
- pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i
4442
- }),
4503
+ severity: "warning",
4504
+ matchKind: "value",
4505
+ detect(input3) {
4506
+ if (typeof input3.value !== "string") return [];
4507
+ return valueContainsEmail(input3.value) ? [{ action: "replace", severity: "warning", matchKind: "value" }] : [];
4508
+ }
4509
+ },
4443
4510
  patternDetector({
4444
4511
  id: "value.phone",
4445
4512
  pattern: /\b(?:\+\d{1,3}[\s.-]?)?(?:\(?\d{3}\)?[\s.-])\d{3}[\s.-]\d{4}\b/
@@ -4760,6 +4827,16 @@ function redactJsonlText(content, profile) {
4760
4827
  };
4761
4828
  }
4762
4829
  function redactDocument(content, profile) {
4830
+ const trimmed = content.trim();
4831
+ if (trimmed.startsWith("{")) {
4832
+ try {
4833
+ const parsed = JSON.parse(trimmed);
4834
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && ("schemaVersion" in parsed || "eventId" in parsed || "runId" in parsed)) {
4835
+ return redactJsonlText(content, profile);
4836
+ }
4837
+ } catch {
4838
+ }
4839
+ }
4763
4840
  try {
4764
4841
  return redactJsonText(content, profile);
4765
4842
  } catch {
@@ -8235,8 +8312,6 @@ async function evalCommand(target, options = {}, stdin = process.stdin) {
8235
8312
  else if (options.markdown) console.log(renderEvalMarkdown(result).trimEnd());
8236
8313
  else printHuman2(result);
8237
8314
  }
8238
-
8239
- // packages/cli/src/safety.ts
8240
8315
  var BEST_EFFORT_NOTE = "Best-effort local safety verification only; not a compliance, privacy, security, or regulatory certification.";
8241
8316
  var DEFAULT_MAX_STRING_LENGTH = 16384;
8242
8317
  var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
@@ -8294,7 +8369,7 @@ function resultFromParts(parts) {
8294
8369
  const diagnostics = [...parts.diagnostics ?? []];
8295
8370
  const warnings = [...parts.warnings ?? []];
8296
8371
  const unsupportedFields = [...parts.unsupportedFields ?? []];
8297
- const status = statusFrom(findings, diagnostics);
8372
+ const status = parts.status ?? statusFrom(findings, diagnostics);
8298
8373
  return {
8299
8374
  ok: status === "SAFE" || status === "SAFE WITH WARNINGS",
8300
8375
  command: parts.command,
@@ -8310,7 +8385,17 @@ function resultFromParts(parts) {
8310
8385
  diagnostics,
8311
8386
  warnings,
8312
8387
  unsupportedFields,
8313
- note: BEST_EFFORT_NOTE
8388
+ note: BEST_EFFORT_NOTE,
8389
+ ...parts.sourceAssessment !== void 0 ? { sourceAssessment: parts.sourceAssessment } : {},
8390
+ ...parts.artifactAssessment !== void 0 ? { artifactAssessment: parts.artifactAssessment } : {},
8391
+ ...parts.redactionSummary !== void 0 ? { redactionSummary: parts.redactionSummary } : {}
8392
+ };
8393
+ }
8394
+ function layerFromResult(result) {
8395
+ return {
8396
+ status: result.status,
8397
+ summary: result.summary,
8398
+ findings: result.findings
8314
8399
  };
8315
8400
  }
8316
8401
  function readErrorResult3(command, error) {
@@ -8374,6 +8459,21 @@ function flattenNodes(nodes) {
8374
8459
  function detectorSeverity(finding) {
8375
8460
  return finding.severity;
8376
8461
  }
8462
+ function classifyRedactionDetector(detector) {
8463
+ if (detector === "value.creditCard" || detector === "value.email" || detector === "value.phone") {
8464
+ return { category: "personal-data", confidence: "high" };
8465
+ }
8466
+ if (detector === "value.ipv4" || detector === "value.ipv6") {
8467
+ return { category: "identifier", confidence: "medium" };
8468
+ }
8469
+ 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"))) {
8470
+ return { category: "credential", confidence: "high" };
8471
+ }
8472
+ if (detector.startsWith("key.")) {
8473
+ return { category: "credential", confidence: "medium" };
8474
+ }
8475
+ return { category: "credential", confidence: "medium" };
8476
+ }
8377
8477
  function redactionDetectorFindings(read, runId) {
8378
8478
  const runs = runId === void 0 ? read.runs : read.runs.filter((run) => run.runId === runId);
8379
8479
  const out = [];
@@ -8384,6 +8484,7 @@ function redactionDetectorFindings(read, runId) {
8384
8484
  const result = redact(attrs, { profile: "share" });
8385
8485
  for (const finding of result.findings) {
8386
8486
  if (finding.action === "keep") continue;
8487
+ const taxonomy = classifyRedactionDetector(finding.detector);
8387
8488
  out.push({
8388
8489
  ruleId: "safety.redactDetector",
8389
8490
  severity: detectorSeverity(finding),
@@ -8401,7 +8502,11 @@ function redactionDetectorFindings(read, runId) {
8401
8502
  ...node.event.status !== void 0 ? { status: node.event.status } : {},
8402
8503
  path: `attributes.${finding.path.replace(/^\$\.?/, "")}`
8403
8504
  }
8404
- ]
8505
+ ],
8506
+ category: taxonomy.category,
8507
+ confidence: taxonomy.confidence,
8508
+ detector: finding.detector,
8509
+ action: finding.action
8405
8510
  });
8406
8511
  }
8407
8512
  }
@@ -8417,13 +8522,49 @@ function exitCodeFor3(result) {
8417
8522
  if (result.status === "UNSAFE") return 1;
8418
8523
  return 2;
8419
8524
  }
8420
- function printJson3(result) {
8421
- console.log(JSON.stringify(stable4(result), null, 2));
8525
+ function explainFinding(finding, blocksBundle) {
8526
+ const path25 = finding.evidence[0]?.path ?? "(unknown path)";
8527
+ const category = finding.category ?? "structure";
8528
+ const confidence = finding.confidence ?? "medium";
8529
+ const detector = finding.detector ?? finding.ruleId;
8530
+ const action = finding.action ?? "review";
8531
+ const redactionHint = category === "credential" || category === "personal-data" || category === "raw-content" || action.includes("redact") ? "Usually removable by share/strict redaction before bundling." : "May require omitting the field, lowering limits, or an explicit local override.";
8532
+ return [
8533
+ ` Matched: detector=${detector}; path=${path25}; category=${category}`,
8534
+ ` Why: ${finding.message}`,
8535
+ ` Confidence: ${confidence}`,
8536
+ ` Redaction: ${redactionHint}`,
8537
+ ` Override: configure a custom redaction/detector rule locally (see docs/SAFETY-POLICY.md); do not weaken defaults globally.`,
8538
+ ` Bundle gate: ${blocksBundle ? "blocks share-safe bundle unless --allow-unsafe" : "does not block by itself (warning/info)"}`
8539
+ ];
8540
+ }
8541
+ function findingExplanation(finding) {
8542
+ const blocks = finding.severity === "error" || finding.status === "fail";
8543
+ return {
8544
+ ruleId: finding.ruleId,
8545
+ detector: finding.detector ?? finding.ruleId,
8546
+ path: finding.evidence[0]?.path,
8547
+ category: finding.category,
8548
+ confidence: finding.confidence,
8549
+ action: finding.action,
8550
+ blocksBundle: blocks,
8551
+ // Never include matched secret/PII values.
8552
+ message: finding.message
8553
+ };
8422
8554
  }
8423
- function printHuman3(result) {
8555
+ function printHuman3(result, explain = false) {
8424
8556
  console.log(`Safety status: ${result.status}`);
8425
8557
  console.log(`Format: ${result.format}`);
8426
8558
  if (result.runId !== void 0) console.log(`Run: ${result.runId}`);
8559
+ if (result.sourceAssessment !== void 0 && result.artifactAssessment !== void 0) {
8560
+ console.log(`Source assessment: ${result.sourceAssessment.status}`);
8561
+ console.log(`Artifact assessment: ${result.artifactAssessment.status}`);
8562
+ if (result.redactionSummary !== void 0) {
8563
+ console.log(
8564
+ `Redaction: profile=${result.redactionSummary.profile}, findings=${result.redactionSummary.findings}`
8565
+ );
8566
+ }
8567
+ }
8427
8568
  console.log(
8428
8569
  `Summary: ${result.summary.findings} finding(s), ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
8429
8570
  );
@@ -8432,45 +8573,82 @@ function printHuman3(result) {
8432
8573
  }
8433
8574
  for (const finding of result.findings) {
8434
8575
  const path25 = finding.evidence[0]?.path;
8435
- console.log(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
8576
+ const taxonomy = finding.category !== void 0 || finding.confidence !== void 0 ? ` [${[finding.category, finding.confidence].filter(Boolean).join("/")}]` : "";
8577
+ console.log(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}${taxonomy}`);
8578
+ if (explain) {
8579
+ const blocks = finding.severity === "error" || finding.status === "fail";
8580
+ for (const line of explainFinding(finding, blocks)) {
8581
+ console.log(line);
8582
+ }
8583
+ }
8436
8584
  }
8437
8585
  console.log(`Note: ${result.note}`);
8438
8586
  }
8587
+ function printJson3(result, explain = false) {
8588
+ const payload = explain === true ? { ...result, explanations: result.findings.map(findingExplanation) } : result;
8589
+ console.log(JSON.stringify(stable4(payload), null, 2));
8590
+ }
8439
8591
  async function safetyCommand(command, target, options, stdin) {
8440
8592
  let result;
8441
8593
  try {
8442
- const rules = buildSafetyRules(options);
8443
8594
  const input3 = await inputFromTarget(target, options, stdin);
8444
8595
  const read = await openTrace(input3, {
8445
8596
  ...options.format !== void 0 ? { format: options.format } : {}
8446
8597
  });
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
8598
+ const source = assessOpenedTrace(read, {
8599
+ ...options,
8600
+ ...options.run !== void 0 ? { runId: options.run } : {}
8466
8601
  });
8602
+ if (command === "scan") {
8603
+ result = { ...source, command: "scan" };
8604
+ } else {
8605
+ const profile = options.redactionProfile ?? "share";
8606
+ const rawContent = input3.type === "string" ? input3.content : input3.type === "file" ? await readFile(input3.path, "utf-8") : void 0;
8607
+ if (rawContent === void 0) {
8608
+ result = {
8609
+ ...source,
8610
+ command: "verify-safe",
8611
+ sourceAssessment: layerFromResult(source)
8612
+ };
8613
+ } else {
8614
+ const redacted = redactTraceContent(rawContent, profile);
8615
+ const artifactRead = await openTrace(
8616
+ { type: "string", content: redacted.content },
8617
+ { format: options.format ?? "agent-inspect-jsonl" }
8618
+ );
8619
+ const artifact = assessOpenedTrace(artifactRead, {
8620
+ ...options,
8621
+ ...options.run !== void 0 ? { runId: options.run } : {}
8622
+ });
8623
+ const detectors = [
8624
+ ...new Set(redacted.findings.map((finding) => finding.detector))
8625
+ ].sort((a, b) => a.localeCompare(b));
8626
+ result = resultFromParts({
8627
+ command: "verify-safe",
8628
+ format: artifact.format,
8629
+ runId: artifact.runId ?? source.runId,
8630
+ findings: artifact.findings,
8631
+ diagnostics: artifact.diagnostics,
8632
+ warnings: artifact.warnings,
8633
+ unsupportedFields: artifact.unsupportedFields,
8634
+ status: artifact.status,
8635
+ sourceAssessment: layerFromResult(source),
8636
+ artifactAssessment: layerFromResult(artifact),
8637
+ redactionSummary: {
8638
+ profile,
8639
+ findings: redacted.findings.length,
8640
+ detectors
8641
+ }
8642
+ });
8643
+ }
8644
+ }
8467
8645
  } catch (error) {
8468
8646
  const message = error instanceof Error ? error.message : String(error);
8469
8647
  result = message.startsWith("--") ? invalidArgumentResult(command, error) : readErrorResult3(command, error);
8470
8648
  }
8471
8649
  process.exitCode = exitCodeFor3(result);
8472
- if (options.json) printJson3(result);
8473
- else printHuman3(result);
8650
+ if (options.json) printJson3(result, options.explain === true);
8651
+ else printHuman3(result, options.explain === true);
8474
8652
  }
8475
8653
  function scanCommand(target, options = {}, stdin = process.stdin) {
8476
8654
  return safetyCommand("scan", target, options, stdin);
@@ -8949,15 +9127,8 @@ async function bundleCommand(runIdArg, options = {}) {
8949
9127
  process.exitCode = 1;
8950
9128
  return;
8951
9129
  }
8952
- const safety = assessOpenedTrace(read, { run: runId });
8953
- const status = safetyStatusFromAssess(safety.status);
8954
- checkRuns.push({
8955
- runId,
8956
- status,
8957
- errors: safety.summary.errors,
8958
- warnings: safety.summary.warnings,
8959
- findings: safety.summary.findings
8960
- });
9130
+ const sourceSafety = assessOpenedTrace(read, { run: runId });
9131
+ const sourceStatus = safetyStatusFromAssess(sourceSafety.status);
8961
9132
  const redacted = redactTraceContent(rawContent, toReportProfile(profile));
8962
9133
  redactedJsonlByRun.set(runId, redacted.content);
8963
9134
  combinedJsonl += redacted.content.endsWith("\n") ? redacted.content : `${redacted.content}
@@ -8970,6 +9141,34 @@ async function bundleCommand(runIdArg, options = {}) {
8970
9141
  findings: redacted.findings.length,
8971
9142
  detectors
8972
9143
  });
9144
+ let artifactSafety = sourceSafety;
9145
+ try {
9146
+ const artifactRead = await openTrace(
9147
+ { type: "string", content: redacted.content },
9148
+ { format: "agent-inspect-jsonl" }
9149
+ );
9150
+ artifactSafety = assessOpenedTrace(artifactRead, { run: runId });
9151
+ } catch {
9152
+ artifactSafety = {
9153
+ ...sourceSafety,
9154
+ status: "UNKNOWN",
9155
+ ok: false,
9156
+ summary: {
9157
+ findings: sourceSafety.summary.findings,
9158
+ warnings: sourceSafety.summary.warnings,
9159
+ errors: Math.max(1, sourceSafety.summary.errors)
9160
+ }
9161
+ };
9162
+ }
9163
+ const status = safetyStatusFromAssess(artifactSafety.status);
9164
+ checkRuns.push({
9165
+ runId,
9166
+ status,
9167
+ sourceStatus,
9168
+ errors: artifactSafety.summary.errors,
9169
+ warnings: artifactSafety.summary.warnings,
9170
+ findings: artifactSafety.summary.findings
9171
+ });
8973
9172
  const selected = read.runs.find((run) => run.runId === runId);
8974
9173
  const runReport = exportRunTree(selected ?? read.runs[0], {
8975
9174
  format: "html",
@@ -8998,13 +9197,13 @@ async function bundleCommand(runIdArg, options = {}) {
8998
9197
  console.log(
8999
9198
  writeJson4({
9000
9199
  ok: false,
9001
- error: `Bundle safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`,
9200
+ error: `Bundle artifact safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`,
9002
9201
  checks: checks2
9003
9202
  }).trimEnd()
9004
9203
  );
9005
9204
  } else {
9006
9205
  console.error(
9007
- `[AgentInspect] bundle refused: safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`
9206
+ `[AgentInspect] bundle refused: artifact safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`
9008
9207
  );
9009
9208
  }
9010
9209
  process.exitCode = 1;
@@ -10423,7 +10622,7 @@ function isModuleNotFound4(e) {
10423
10622
  }
10424
10623
  async function loadIndexSqlite() {
10425
10624
  try {
10426
- return await import('./src-Z5W27YCW.mjs');
10625
+ return await import('./src-C4EUHTER.mjs');
10427
10626
  } catch (e) {
10428
10627
  if (isModuleNotFound4(e)) {
10429
10628
  console.error(
@@ -11335,7 +11534,7 @@ function createCliProgram() {
11335
11534
  "openinference-json",
11336
11535
  "otlp-json"
11337
11536
  ])
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) => {
11537
+ ).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
11538
  runCommand(() => scanCommand(target, opts));
11340
11539
  });
11341
11540
  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 +11543,7 @@ function createCliProgram() {
11344
11543
  "openinference-json",
11345
11544
  "otlp-json"
11346
11545
  ])
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) => {
11546
+ ).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
11547
  runCommand(() => verifySafeCommand(target, opts));
11349
11548
  });
11350
11549
  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(