agent-inspect 6.31.32 → 6.31.34

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/TRACE-CONTRACTS.md +2 -0
  4. package/package.json +1 -1
  5. package/packages/cli/dist/{chunk-PEGM6T2X.mjs → chunk-6TXW53UR.mjs} +138 -36
  6. package/packages/cli/dist/chunk-6TXW53UR.mjs.map +1 -0
  7. package/packages/cli/dist/index.cjs +197 -50
  8. package/packages/cli/dist/index.cjs.map +1 -1
  9. package/packages/cli/dist/index.mjs +64 -19
  10. package/packages/cli/dist/index.mjs.map +1 -1
  11. package/packages/cli/dist/{src-CWUJTMRD.mjs → src-GCLC4DIC.mjs} +3 -3
  12. package/packages/cli/dist/{src-CWUJTMRD.mjs.map → src-GCLC4DIC.mjs.map} +1 -1
  13. package/packages/core/dist/advanced.cjs +79 -20
  14. package/packages/core/dist/advanced.cjs.map +1 -1
  15. package/packages/core/dist/advanced.mjs +9 -9
  16. package/packages/core/dist/checks.cjs +133 -32
  17. package/packages/core/dist/checks.cjs.map +1 -1
  18. package/packages/core/dist/checks.mjs +3 -3
  19. package/packages/core/dist/{chunk-6S666BF4.mjs → chunk-2SKAI5ZN.mjs} +3 -3
  20. package/packages/core/dist/{chunk-6S666BF4.mjs.map → chunk-2SKAI5ZN.mjs.map} +1 -1
  21. package/packages/core/dist/{chunk-FBPDF7HO.mjs → chunk-6F6KLGCR.mjs} +3 -3
  22. package/packages/core/dist/{chunk-FBPDF7HO.mjs.map → chunk-6F6KLGCR.mjs.map} +1 -1
  23. package/packages/core/dist/{chunk-6G67ZN72.mjs → chunk-CXC7L4PY.mjs} +136 -35
  24. package/packages/core/dist/chunk-CXC7L4PY.mjs.map +1 -0
  25. package/packages/core/dist/{chunk-RB6WANSW.mjs → chunk-PU3FF5AP.mjs} +5 -4
  26. package/packages/core/dist/chunk-PU3FF5AP.mjs.map +1 -0
  27. package/packages/core/dist/{chunk-VIEJJ5JB.mjs → chunk-X5RP2E2H.mjs} +4 -4
  28. package/packages/core/dist/{chunk-VIEJJ5JB.mjs.map → chunk-X5RP2E2H.mjs.map} +1 -1
  29. package/packages/core/dist/exporters.cjs +33 -9
  30. package/packages/core/dist/exporters.cjs.map +1 -1
  31. package/packages/core/dist/exporters.mjs +33 -10
  32. package/packages/core/dist/exporters.mjs.map +1 -1
  33. package/packages/core/dist/index.cjs +3 -2
  34. package/packages/core/dist/index.cjs.map +1 -1
  35. package/packages/core/dist/index.mjs +4 -4
  36. package/packages/core/dist/writers.cjs +3 -2
  37. package/packages/core/dist/writers.cjs.map +1 -1
  38. package/packages/core/dist/writers.mjs +1 -1
  39. package/packages/cli/dist/chunk-PEGM6T2X.mjs.map +0 -1
  40. package/packages/core/dist/chunk-6G67ZN72.mjs.map +0 -1
  41. package/packages/core/dist/chunk-RB6WANSW.mjs.map +0 -1
@@ -269,8 +269,9 @@ var init_credential_value_patterns = __esm({
269
269
  /\beyJ[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\b/,
270
270
  /\b(?:sk-(?:proj-)?[a-zA-Z0-9_-]{16,}|sk-ant-[a-zA-Z0-9_-]{16,}|AIza[0-9A-Za-z_-]{20,})\b/,
271
271
  // Stripe secret / restricted keys use underscores (not sk-). Keep in sync with
272
- // packages/redact/src/index.ts + packages/core/src/checks DEFAULT_SECRET_PATTERNS (#520).
273
- /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/,
272
+ // packages/redact/src/index.ts + packages/core/src/checks DEFAULT_SECRET_PATTERNS (#520/#528).
273
+ // Use non-alnum lookarounds so underscore-adjacent forms (acct_sk_live_…, …_eu) match.
274
+ /(?<![A-Za-z0-9])(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}(?![A-Za-z0-9])/,
274
275
  /\bxox[abposr]-[A-Za-z0-9-]{10,}\b/,
275
276
  /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20,}\b/,
276
277
  /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/,
@@ -5738,6 +5739,11 @@ function compareFindings(eventById) {
5738
5739
  if (STATUS_RANK[a.status] !== STATUS_RANK[b.status]) {
5739
5740
  return STATUS_RANK[a.status] - STATUS_RANK[b.status];
5740
5741
  }
5742
+ if (a.ruleId === "tool.usage") {
5743
+ const aHasEvent = a.evidence[0]?.eventId !== void 0;
5744
+ const bHasEvent = b.evidence[0]?.eventId !== void 0;
5745
+ if (aHasEvent !== bHasEvent) return aHasEvent ? -1 : 1;
5746
+ }
5741
5747
  const byRun = compareStrings(a.evidence[0]?.runId, b.evidence[0]?.runId);
5742
5748
  if (byRun !== 0) return byRun;
5743
5749
  const byTime = eventTimestamp(a, eventById).localeCompare(eventTimestamp(b, eventById));
@@ -5863,6 +5869,37 @@ function failFinding(ruleId, message, evidence, expected, actual, meta2) {
5863
5869
  function toolName(event) {
5864
5870
  return resolveCanonicalToolName(event);
5865
5871
  }
5872
+ function formatToolNameForMessage(name) {
5873
+ const details = [];
5874
+ for (let index = 0; index < name.length; ) {
5875
+ const codePoint = name.codePointAt(index);
5876
+ const width = codePoint > 65535 ? 2 : 1;
5877
+ if (codePoint > 127) {
5878
+ details.push(
5879
+ `U+${codePoint.toString(16).toUpperCase().padStart(4, "0")} at index ${index}`
5880
+ );
5881
+ }
5882
+ index += width;
5883
+ }
5884
+ if (details.length === 0) return name;
5885
+ return `"${name}" (contains ${details.join("; ")})`;
5886
+ }
5887
+ function describeRunningEvent(event) {
5888
+ if (event.kind === "TOOL") {
5889
+ return `tool ${formatToolNameForMessage(resolveCanonicalToolName(event))}`;
5890
+ }
5891
+ if (event.kind === "LLM") {
5892
+ return `llm ${stepEndpointName(event, "LLM")}`;
5893
+ }
5894
+ return `${event.kind} ${event.name}`;
5895
+ }
5896
+ function incompleteRunningMessage(prefix, running) {
5897
+ const labels = running.map(describeRunningEvent);
5898
+ if (labels.length === 1) {
5899
+ return `${prefix}: ${labels[0]} never completed.`;
5900
+ }
5901
+ return `${prefix}: ${labels.join("; ")}.`;
5902
+ }
5866
5903
  function stepEndpointName(event, kind) {
5867
5904
  if (kind === "TOOL") return resolveCanonicalToolName(event);
5868
5905
  for (const prefix of ["llm:", "generation:", "transcription:", "speech:"]) {
@@ -6204,10 +6241,13 @@ function createRunStatusRule(options = {}) {
6204
6241
  findings.push(
6205
6242
  failFinding(
6206
6243
  "run.status",
6207
- "Run contains incomplete running events.",
6244
+ incompleteRunningMessage("Run contains incomplete running events", running),
6208
6245
  running.map((event) => eventEvidence(event)),
6209
6246
  "no running events",
6210
- running.length
6247
+ {
6248
+ count: running.length,
6249
+ unfinished: running.map(describeRunningEvent)
6250
+ }
6211
6251
  )
6212
6252
  );
6213
6253
  }
@@ -6377,6 +6417,34 @@ function createToolUsageRule(options) {
6377
6417
  const names = tools.map(toolName);
6378
6418
  const nameSet = new Set(names);
6379
6419
  const findings = [];
6420
+ const forbidden = new Set(options.forbidden ?? []);
6421
+ const allowed = options.allowed?.length ? new Set(options.allowed) : void 0;
6422
+ for (const event of tools) {
6423
+ const name = toolName(event);
6424
+ const display = formatToolNameForMessage(name);
6425
+ if (forbidden.has(name)) {
6426
+ findings.push(
6427
+ failFinding(
6428
+ "tool.usage",
6429
+ `Forbidden tool ${display} appeared.`,
6430
+ [eventEvidence(event)],
6431
+ "tool absent",
6432
+ name
6433
+ )
6434
+ );
6435
+ }
6436
+ if (allowed && !allowed.has(name)) {
6437
+ findings.push(
6438
+ failFinding(
6439
+ "tool.usage",
6440
+ `Tool ${display} is not in the allowed tool set.`,
6441
+ [eventEvidence(event)],
6442
+ [...allowed].sort(),
6443
+ name
6444
+ )
6445
+ );
6446
+ }
6447
+ }
6380
6448
  for (const required of options.required ?? []) {
6381
6449
  if (!nameSet.has(required)) {
6382
6450
  const otherKinds = [
@@ -6394,7 +6462,7 @@ function createToolUsageRule(options) {
6394
6462
  findings.push(
6395
6463
  failFinding(
6396
6464
  "tool.usage",
6397
- `Required tool ${required} did not appear.${hint}`,
6465
+ `Required tool ${formatToolNameForMessage(required)} did not appear.${hint}`,
6398
6466
  runEvidence(context.selectedRun),
6399
6467
  required,
6400
6468
  names
@@ -6402,21 +6470,6 @@ function createToolUsageRule(options) {
6402
6470
  );
6403
6471
  }
6404
6472
  }
6405
- const forbidden = new Set(options.forbidden ?? []);
6406
- const allowed = options.allowed?.length ? new Set(options.allowed) : void 0;
6407
- for (const event of tools) {
6408
- const name = toolName(event);
6409
- if (forbidden.has(name)) {
6410
- findings.push(
6411
- failFinding("tool.usage", `Forbidden tool ${name} appeared.`, [eventEvidence(event)], "tool absent", name)
6412
- );
6413
- }
6414
- if (allowed && !allowed.has(name)) {
6415
- findings.push(
6416
- failFinding("tool.usage", `Tool ${name} is not in the allowed tool set.`, [eventEvidence(event)], [...allowed].sort(), name)
6417
- );
6418
- }
6419
- }
6420
6473
  if (options.minCount !== void 0 && tools.length < options.minCount) {
6421
6474
  findings.push(
6422
6475
  failFinding("tool.usage", `Tool count ${tools.length} was below minimum ${options.minCount}.`, runEvidence(context.selectedRun), { minCount: options.minCount }, tools.length)
@@ -6450,7 +6503,36 @@ function createStepOrderingRule(options) {
6450
6503
  const afterEvents = finishedEvents(context, options.after.kind).filter(
6451
6504
  (event) => stepEndpointName(event, options.after.kind) === options.after.name
6452
6505
  );
6453
- if (beforeEvents.length === 0 || afterEvents.length === 0) {
6506
+ const beforeRunning = semanticEvents(context).filter(
6507
+ (event) => event.kind === options.before.kind && event.status === "running" && stepEndpointName(event, options.before.kind) === options.before.name
6508
+ );
6509
+ const afterRunning = semanticEvents(context).filter(
6510
+ (event) => event.kind === options.after.kind && event.status === "running" && stepEndpointName(event, options.after.kind) === options.after.name
6511
+ );
6512
+ const afterStarted = afterEvents.length > 0 || afterRunning.length > 0;
6513
+ const afterEvidenceEvent = afterEvents[0] ?? afterRunning[0];
6514
+ if (beforeRunning.length > 0 && afterStarted && afterEvidenceEvent !== void 0) {
6515
+ const endpointExpected2 = bothTools ? { before: options.before.name, after: options.after.name } : { before: options.before, after: options.after };
6516
+ return [
6517
+ failFinding(
6518
+ ruleId,
6519
+ `${subject} ${beforeLabel} started but never finished before ${afterLabel} ran.`,
6520
+ [
6521
+ ...beforeRunning.map((event) => eventEvidence(event)),
6522
+ eventEvidence(afterEvidenceEvent)
6523
+ ],
6524
+ { ...endpointExpected2, mode },
6525
+ {
6526
+ code: bothTools ? "tool.order.interval-unresolved" : "step.order.interval-unresolved",
6527
+ unfinishedBefore: true,
6528
+ beforeStatus: "running",
6529
+ ...beforeEvents.length > 0 ? { finishedBeforeMasked: true } : {},
6530
+ ...afterEvents.length === 0 ? { afterStatus: "running" } : {}
6531
+ }
6532
+ )
6533
+ ];
6534
+ }
6535
+ if (beforeEvents.length === 0 || !afterStarted) {
6454
6536
  if (!requireEndpoints) return [];
6455
6537
  const findings = [];
6456
6538
  if (beforeEvents.length === 0) {
@@ -6474,7 +6556,7 @@ function createStepOrderingRule(options) {
6474
6556
  )
6475
6557
  );
6476
6558
  }
6477
- if (afterEvents.length === 0) {
6559
+ if (!afterStarted) {
6478
6560
  const otherKinds = otherKindsForStepName(
6479
6561
  context,
6480
6562
  options.after.kind,
@@ -6497,9 +6579,15 @@ function createStepOrderingRule(options) {
6497
6579
  }
6498
6580
  return findings;
6499
6581
  }
6500
- const encounter = finishedEvents(context).filter(
6501
- (event) => event.kind === options.before.kind && stepEndpointName(event, options.before.kind) === options.before.name || event.kind === options.after.kind && stepEndpointName(event, options.after.kind) === options.after.name
6502
- );
6582
+ const afterForOrder = afterEvents.length > 0 ? afterEvents : afterRunning;
6583
+ const encounter = [
6584
+ ...finishedEvents(context).filter(
6585
+ (event) => event.kind === options.before.kind && stepEndpointName(event, options.before.kind) === options.before.name || event.kind === options.after.kind && stepEndpointName(event, options.after.kind) === options.after.name
6586
+ ),
6587
+ ...afterEvents.length === 0 ? afterRunning.filter(
6588
+ (event) => !finishedEvents(context).some((finished) => finished.eventId === event.eventId)
6589
+ ) : []
6590
+ ];
6503
6591
  let beforeIndex;
6504
6592
  let afterIndex;
6505
6593
  for (const [position, event] of encounter.entries()) {
@@ -6530,7 +6618,7 @@ function createStepOrderingRule(options) {
6530
6618
  ];
6531
6619
  }
6532
6620
  const beforeEvent = beforeEvents[0];
6533
- const afterEvent = afterEvents[0];
6621
+ const afterEvent = afterForOrder[0];
6534
6622
  const beforeEnd = eventEndMs(beforeEvent);
6535
6623
  const afterStart = eventStartMs(afterEvent);
6536
6624
  const endpointExpected = bothTools ? { before: options.before.name, after: options.after.name } : { before: options.before, after: options.after };
@@ -6616,7 +6704,7 @@ function createStepOrderingRule(options) {
6616
6704
  latestBefore = { event, end };
6617
6705
  }
6618
6706
  }
6619
- for (const event of afterEvents) {
6707
+ for (const event of afterForOrder) {
6620
6708
  const start = eventStartMs(event);
6621
6709
  if (start === void 0) {
6622
6710
  missingAfterStarts += 1;
@@ -6645,7 +6733,7 @@ function createStepOrderingRule(options) {
6645
6733
  const tie = tiedEnd === tiedStart ? recordedTieOrder(
6646
6734
  context,
6647
6735
  beforeEvents.filter((event) => eventEndMs(event) === tiedEnd),
6648
- afterEvents.filter((event) => eventStartMs(event) === tiedStart)
6736
+ afterForOrder.filter((event) => eventStartMs(event) === tiedStart)
6649
6737
  ) : void 0;
6650
6738
  if (latestBefore.end > earliestAfter.start || tie !== void 0 && tie.inOrder !== true) {
6651
6739
  return [
@@ -6826,10 +6914,13 @@ function createStructureIncompleteRule(options = {}) {
6826
6914
  findings.push(
6827
6915
  failFinding(
6828
6916
  "structure.incomplete",
6829
- "Trace contains incomplete running events.",
6917
+ incompleteRunningMessage("Trace contains incomplete running events", running),
6830
6918
  running.map((event) => eventEvidence(event, "status")),
6831
6919
  "no running events",
6832
- running.length
6920
+ {
6921
+ count: running.length,
6922
+ unfinished: running.map(describeRunningEvent)
6923
+ }
6833
6924
  )
6834
6925
  );
6835
6926
  }
@@ -7607,10 +7698,11 @@ var init_checks2 = __esm({
7607
7698
  DEFAULT_SECRET_PATTERNS = [
7608
7699
  { id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
7609
7700
  { id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
7610
- // Stripe + Slack: keep in sync with packages/redact + credential-value-patterns (#520).
7701
+ // Stripe + Slack: keep in sync with packages/redact + credential-value-patterns (#520/#528).
7702
+ // Non-alnum lookarounds so underscore-adjacent forms (acct_sk_live_…, …_eu) match.
7611
7703
  {
7612
7704
  id: "stripe-key",
7613
- pattern: /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/
7705
+ pattern: /(?<![A-Za-z0-9])(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}(?![A-Za-z0-9])/
7614
7706
  },
7615
7707
  { id: "slack-token", pattern: /\bxox[abposr]-[A-Za-z0-9-]{10,}\b/ },
7616
7708
  { id: "aws-access-key", pattern: /AKIA[0-9A-Z]{16}/ },
@@ -9564,10 +9656,17 @@ function contractToRules(contract) {
9564
9656
  (event) => event.status === "running"
9565
9657
  );
9566
9658
  if (running.length > 0) {
9659
+ const labels = running.map((event) => {
9660
+ if (event.kind === "TOOL") {
9661
+ return `tool ${resolveCanonicalToolName(event)}`;
9662
+ }
9663
+ return `${event.kind} ${event.name}`;
9664
+ });
9665
+ const message = labels.length === 1 ? `Run contains incomplete running events: ${labels[0]} never completed.` : `Run contains incomplete running events: ${labels.join("; ")}.`;
9567
9666
  findings.push(
9568
9667
  contractFailFinding(
9569
9668
  "contract.run.allowedStatuses",
9570
- "Run contains incomplete running events.",
9669
+ message,
9571
9670
  running.map((event) => ({
9572
9671
  runId: event.runId,
9573
9672
  eventId: event.eventId,
@@ -9576,7 +9675,10 @@ function contractToRules(contract) {
9576
9675
  status: event.status
9577
9676
  })),
9578
9677
  "no running events",
9579
- running.length
9678
+ {
9679
+ count: running.length,
9680
+ unfinished: labels
9681
+ }
9580
9682
  )
9581
9683
  );
9582
9684
  }
@@ -17377,7 +17479,7 @@ var init_src = __esm({
17377
17479
  });
17378
17480
 
17379
17481
  // package.json
17380
- var version = "6.31.32";
17482
+ var version = "6.31.34";
17381
17483
 
17382
17484
  // packages/cli/src/list.ts
17383
17485
  init_advanced();
@@ -19605,11 +19707,25 @@ function attemptIdOf3(event) {
19605
19707
  }
19606
19708
  return void 0;
19607
19709
  }
19710
+ function operationIdOf(event) {
19711
+ const direct = event.attributes?.operationId;
19712
+ if (typeof direct === "string" && direct.trim() !== "") return direct;
19713
+ const meta2 = event.attributes?.metadata;
19714
+ if (meta2 && typeof meta2 === "object" && !Array.isArray(meta2)) {
19715
+ const nested = meta2.operationId;
19716
+ if (typeof nested === "string" && nested.trim() !== "") return nested;
19717
+ }
19718
+ return void 0;
19719
+ }
19608
19720
  function lifecycleKey(event) {
19609
19721
  if (event.kind === "RUN") return `run:${event.runId}`;
19610
19722
  const stepId = stepIdOf2(event);
19611
- if (stepId !== void 0) return `step:${stepId}`;
19612
- return `event:${event.eventId}`;
19723
+ if (stepId === void 0) return `event:${event.eventId}`;
19724
+ const attemptId = attemptIdOf3(event);
19725
+ if (attemptId !== void 0) return `step:${stepId}:attempt:${attemptId}`;
19726
+ const operationId = operationIdOf(event);
19727
+ if (operationId !== void 0) return `step:${stepId}:op:${operationId}`;
19728
+ return `step:${stepId}`;
19613
19729
  }
19614
19730
  function isTerminal(status) {
19615
19731
  return status === "ok" || status === "error";
@@ -19660,6 +19776,12 @@ function joinSiblingGroup(group) {
19660
19776
  const others = group.filter(
19661
19777
  (n) => !isTerminal(n.event.status) && n.event.status !== "running"
19662
19778
  );
19779
+ const operationIds = new Set(
19780
+ group.map((n) => operationIdOf(n.event)).filter((id) => id !== void 0)
19781
+ );
19782
+ if (operationIds.size > 1) {
19783
+ return group;
19784
+ }
19663
19785
  const terminalStatuses = new Set(terminals.map((n) => n.event.status));
19664
19786
  if (terminalStatuses.size > 1) {
19665
19787
  return group;
@@ -19671,19 +19793,22 @@ function joinSiblingGroup(group) {
19671
19793
  const out = [];
19672
19794
  for (const terminal of terminals) {
19673
19795
  const terminalAttempt = attemptIdOf3(terminal.event);
19796
+ const terminalOp = operationIdOf(terminal.event);
19674
19797
  const terminalTs = eventTime3(terminal.event);
19675
- const candidate = runnings.find((r) => {
19798
+ const candidates = runnings.filter((r) => {
19676
19799
  if (usedRunning.has(r.event.eventId)) return false;
19677
19800
  if (eventTime3(r.event) > terminalTs) return false;
19801
+ const runningOp = operationIdOf(r.event);
19802
+ if (terminalOp !== void 0 && runningOp !== void 0 && terminalOp !== runningOp) {
19803
+ return false;
19804
+ }
19678
19805
  const runningAttempt = attemptIdOf3(r.event);
19679
19806
  if (terminalAttempt !== void 0) {
19680
19807
  return runningAttempt === terminalAttempt;
19681
19808
  }
19682
- if (runningAttempt !== void 0) {
19683
- return true;
19684
- }
19685
- return true;
19809
+ return runningAttempt === void 0;
19686
19810
  });
19811
+ const candidate = candidates.length === 1 ? candidates[0] : void 0;
19687
19812
  if (candidate) {
19688
19813
  usedRunning.add(candidate.event.eventId);
19689
19814
  out.push(joinOnePair(candidate, terminal));
@@ -22573,10 +22698,11 @@ var credentialDetectors = [
22573
22698
  severity: "error"
22574
22699
  }),
22575
22700
  // Stripe secret/restricted keys (underscore form). Keep in sync with
22576
- // packages/core/src/safety/credential-value-patterns.ts + checks DEFAULT_SECRET_PATTERNS (#520).
22701
+ // packages/core/src/safety/credential-value-patterns.ts + checks DEFAULT_SECRET_PATTERNS (#520/#528).
22702
+ // Non-alnum lookarounds so underscore-adjacent forms (acct_sk_live_…, …_eu) match.
22577
22703
  patternDetector({
22578
22704
  id: "value.stripeSecretKey",
22579
- pattern: /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/,
22705
+ pattern: /(?<![A-Za-z0-9])(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}(?![A-Za-z0-9])/,
22580
22706
  severity: "error"
22581
22707
  }),
22582
22708
  patternDetector({
@@ -24357,6 +24483,21 @@ function stableJson2(value) {
24357
24483
  return `${JSON.stringify(value, null, 2)}
24358
24484
  `;
24359
24485
  }
24486
+ function alignCheckResultsAggregateStatus(checkResultsJson, assessmentStatus) {
24487
+ let parsed;
24488
+ try {
24489
+ parsed = JSON.parse(checkResultsJson);
24490
+ } catch {
24491
+ return stableJson2({
24492
+ aggregateStatus: assessmentStatus,
24493
+ raw: checkResultsJson
24494
+ });
24495
+ }
24496
+ return stableJson2({
24497
+ ...parsed,
24498
+ aggregateStatus: assessmentStatus
24499
+ });
24500
+ }
24360
24501
  async function writeLocalEvidence(input3) {
24361
24502
  const profile = input3.redactionProfile ?? "share";
24362
24503
  const format = input3.format ?? "directory";
@@ -24395,6 +24536,10 @@ async function writeLocalEvidence(input3) {
24395
24536
  format: "agent-inspect-jsonl"
24396
24537
  });
24397
24538
  const assessmentStatus = residualStatusToEvidenceSafe(residual.status);
24539
+ const checkResultsJson = alignCheckResultsAggregateStatus(
24540
+ input3.checkResultsJson,
24541
+ assessmentStatus
24542
+ );
24398
24543
  const evidencePackage = buildEvidenceCiPackage({
24399
24544
  generatorVersion: version,
24400
24545
  runIds: input3.runIds,
@@ -24402,7 +24547,7 @@ async function writeLocalEvidence(input3) {
24402
24547
  redactedTraceJsonl: combined,
24403
24548
  redactionProfile: profile,
24404
24549
  assessmentStatus,
24405
- checkResultsJson: input3.checkResultsJson,
24550
+ checkResultsJson,
24406
24551
  ...input3.summaryText !== void 0 ? { summaryText: input3.summaryText } : {},
24407
24552
  ...input3.contractPackage !== void 0 ? { contractPackage: input3.contractPackage } : {}
24408
24553
  });
@@ -24463,12 +24608,12 @@ async function writeLocalEvidence(input3) {
24463
24608
  return baseDir;
24464
24609
  }
24465
24610
  function checkResultToEvidenceJson(result, runIds) {
24466
- const status = result.status === "pass" ? "SAFE" : result.status === "fail" ? "UNSAFE" : "UNKNOWN";
24467
24611
  return stableJson2({
24468
- aggregateStatus: status,
24612
+ aggregateStatus: "UNKNOWN",
24613
+ checkStatus: result.status,
24469
24614
  runs: runIds.map((runId) => ({
24470
24615
  runId,
24471
- status,
24616
+ checkStatus: result.status,
24472
24617
  errors: result.summary.errors,
24473
24618
  warnings: result.summary.warnings,
24474
24619
  findings: result.findings.length,
@@ -32521,10 +32666,12 @@ async function gateCommand(options = {}) {
32521
32666
  runIds: runIds.length > 0 ? runIds : ["gate"],
32522
32667
  sourceContents,
32523
32668
  ...gateOptions.traceDir !== void 0 ? { dir: gateOptions.traceDir } : {},
32524
- failed,
32669
+ // aggregateStatus is filled from residual assessment before hashing (#529).
32670
+ // Gate pass/fail stays in `gate` / process.exitCode, not aggregateStatus.
32525
32671
  checkResultsJson: `${JSON.stringify(
32526
32672
  {
32527
- aggregateStatus: failed ? "UNSAFE" : "SAFE",
32673
+ aggregateStatus: "UNKNOWN",
32674
+ gateStatus: failed ? "fail" : "pass",
32528
32675
  gate: result
32529
32676
  },
32530
32677
  null,