@sema-agent/core 5.56.0 → 5.58.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 (208) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/agents/cascade.d.ts +1 -1
  3. package/dist/agents/cumulative-stats.d.ts +1 -1
  4. package/dist/agents/observer.d.ts +2 -2
  5. package/dist/agents/peer-admission.d.ts +1 -1
  6. package/dist/agents/retain-ledger.d.ts +2 -2
  7. package/dist/agents/roster-store.d.ts +8 -8
  8. package/dist/agents/send-message-tool.d.ts +13 -2
  9. package/dist/agents/send-message-tool.js +34 -12
  10. package/dist/agents/subagent-steps.d.ts +1 -1
  11. package/dist/agents/subagent.d.ts +13 -13
  12. package/dist/agents/team.d.ts +15 -6
  13. package/dist/agents/team.js +1 -0
  14. package/dist/agents/tool-filter.d.ts +2 -2
  15. package/dist/agents/verify.d.ts +1 -1
  16. package/dist/bench/metrics.d.ts +35 -35
  17. package/dist/brain/anthropic.js +15 -5
  18. package/dist/brain/circuit-breaker.js +2 -1
  19. package/dist/brain/degrading.d.ts +1 -1
  20. package/dist/brain/degrading.js +4 -1
  21. package/dist/brain/errors.d.ts +3 -3
  22. package/dist/brain/failover.js +16 -1
  23. package/dist/brain/open-responses.js +15 -5
  24. package/dist/brain/openai.js +16 -5
  25. package/dist/brain/reasoning.d.ts +2 -2
  26. package/dist/brain/repetition.d.ts +1 -1
  27. package/dist/brain/request-params.d.ts +30 -27
  28. package/dist/brain/request-params.js +1 -7
  29. package/dist/brain/route-adjudicator.d.ts +190 -0
  30. package/dist/brain/route-adjudicator.js +189 -0
  31. package/dist/brain/route-conformance.d.ts +55 -0
  32. package/dist/brain/route-conformance.js +136 -0
  33. package/dist/brain/routing.js +8 -3
  34. package/dist/brain/status-sink.d.ts +2 -2
  35. package/dist/brain/stream-shared.d.ts +1 -1
  36. package/dist/config/catalog.d.ts +5 -5
  37. package/dist/core/arg-summary.d.ts +4 -4
  38. package/dist/core/ask-class.d.ts +2 -2
  39. package/dist/core/ask-question.d.ts +1 -1
  40. package/dist/core/auto-compaction.d.ts +15 -15
  41. package/dist/core/auto-mode.d.ts +5 -5
  42. package/dist/core/background-agent-store.d.ts +20 -20
  43. package/dist/core/background-shell.d.ts +4 -4
  44. package/dist/core/checkpoint-store.d.ts +35 -27
  45. package/dist/core/context-edit.d.ts +1 -1
  46. package/dist/core/context-guard.d.ts +1 -1
  47. package/dist/core/exec-output-tail.d.ts +6 -6
  48. package/dist/core/file-snapshot-store.d.ts +8 -8
  49. package/dist/core/git-worktree-env.d.ts +3 -3
  50. package/dist/core/governance-codes.js +2 -0
  51. package/dist/core/hooks.d.ts +73 -33
  52. package/dist/core/hooks.js +87 -25
  53. package/dist/core/image-downsample.d.ts +1 -1
  54. package/dist/core/locked-config.d.ts +1 -1
  55. package/dist/core/lsp.d.ts +1 -1
  56. package/dist/core/mailbox-store.d.ts +1 -1
  57. package/dist/core/mcp.d.ts +3 -3
  58. package/dist/core/mcp.js +4 -4
  59. package/dist/core/memory-engine/consolidation-driver.d.ts +207 -0
  60. package/dist/core/memory-engine/consolidation-driver.js +378 -0
  61. package/dist/core/memory-engine/consolidation.d.ts +46 -2
  62. package/dist/core/memory-engine/consolidation.js +1 -0
  63. package/dist/core/memory-engine/data-plane.d.ts +1 -1
  64. package/dist/core/memory-engine/distiller.d.ts +550 -0
  65. package/dist/core/memory-engine/distiller.js +598 -0
  66. package/dist/core/memory-engine/dual-root.d.ts +1 -1
  67. package/dist/core/memory-engine/engine.d.ts +62 -8
  68. package/dist/core/memory-engine/engine.js +40 -4
  69. package/dist/core/memory-engine/file-backend.d.ts +1 -1
  70. package/dist/core/memory-engine/index.d.ts +4 -2
  71. package/dist/core/memory-engine/index.js +4 -2
  72. package/dist/core/memory-engine/origin-clearance.d.ts +1 -1
  73. package/dist/core/memory-engine/scope-contract.d.ts +4 -4
  74. package/dist/core/memory-engine/sync-client.d.ts +16 -16
  75. package/dist/core/memory-engine/sync.d.ts +4 -4
  76. package/dist/core/memory-recall.d.ts +1 -1
  77. package/dist/core/memory.d.ts +2 -2
  78. package/dist/core/permission-rule-consent.d.ts +230 -36
  79. package/dist/core/permission-rule-consent.js +258 -54
  80. package/dist/core/permission-rule-model.d.ts +295 -97
  81. package/dist/core/permission-rule-model.js +132 -41
  82. package/dist/core/permission-rules.d.ts +9 -9
  83. package/dist/core/remote-env.d.ts +8 -8
  84. package/dist/core/roles.d.ts +3 -3
  85. package/dist/core/roles.js +1 -0
  86. package/dist/core/runner/assemble-result.d.ts +2 -2
  87. package/dist/core/runner/compaction-call-options.d.ts +3 -3
  88. package/dist/core/runner/memory-consolidation-driver.d.ts +49 -0
  89. package/dist/core/runner/memory-consolidation-driver.js +60 -0
  90. package/dist/core/runner/memory-consolidation.d.ts +1 -1
  91. package/dist/core/runner/prepare-config-doors.d.ts +3 -3
  92. package/dist/core/runner/prepare-task.d.ts +21 -21
  93. package/dist/core/runner/prepare-task.js +54 -18
  94. package/dist/core/runner/prepare-workspace-restore.d.ts +2 -2
  95. package/dist/core/runner/runtask.d.ts +15 -12
  96. package/dist/core/runner/runtask.js +48 -0
  97. package/dist/core/runner/session-rule-policy.d.ts +1 -1
  98. package/dist/core/runner/teardown-bounded.d.ts +1 -1
  99. package/dist/core/runner/tool-disclosure.d.ts +2 -2
  100. package/dist/core/runner/turn-attachments.d.ts +11 -11
  101. package/dist/core/scheduler.d.ts +10 -5
  102. package/dist/core/secret-env.d.ts +1 -1
  103. package/dist/core/sensitive-path-policy.d.ts +1 -1
  104. package/dist/core/session-policy-store.d.ts +2 -2
  105. package/dist/core/session-reconcile.d.ts +2 -2
  106. package/dist/core/session-store.d.ts +3 -3
  107. package/dist/core/session.d.ts +1 -1
  108. package/dist/core/shutdown-debug.d.ts +2 -2
  109. package/dist/core/side-query.d.ts +14 -7
  110. package/dist/core/spec-contract.d.ts +1 -1
  111. package/dist/core/store-contracts/contract-harness.d.ts +2 -2
  112. package/dist/core/store-contracts/contract-kit-version.d.ts +2 -2
  113. package/dist/core/store-contracts/mailbox-store-contract.d.ts +1 -1
  114. package/dist/core/store-contracts/mailbox-store-contract.js +1 -1
  115. package/dist/core/task-notification.d.ts +5 -5
  116. package/dist/core/task-registry-agent.d.ts +12 -12
  117. package/dist/core/task-registry-monitor.d.ts +1 -1
  118. package/dist/core/task-registry-shared.d.ts +41 -41
  119. package/dist/core/task-registry.d.ts +12 -12
  120. package/dist/core/tool-detach.d.ts +2 -2
  121. package/dist/core/tool-errors.d.ts +3 -3
  122. package/dist/core/tool-policy.d.ts +55 -28
  123. package/dist/core/tool-result-budget.d.ts +1 -1
  124. package/dist/core/tool-result-store.d.ts +2 -2
  125. package/dist/core/tools.d.ts +1 -1
  126. package/dist/core/trace.d.ts +26 -23
  127. package/dist/core/types.d.ts +155 -70
  128. package/dist/core/untrusted-egress.d.ts +1 -1
  129. package/dist/core/untrusted-text.d.ts +7 -7
  130. package/dist/core/wiring-manifest.d.ts +5 -5
  131. package/dist/core/workflow-journal-store.d.ts +14 -14
  132. package/dist/core/workflow-run-store-contract.d.ts +1 -1
  133. package/dist/core/workflow-run-store-contract.js +1 -1
  134. package/dist/core/workflow-run-store.d.ts +4 -4
  135. package/dist/engine/compaction/compaction.d.ts +3 -3
  136. package/dist/engine/compaction/utils.d.ts +2 -2
  137. package/dist/engine/execution-env/kill-tree.d.ts +1 -1
  138. package/dist/engine/execution-env/node-execution-env.d.ts +8 -8
  139. package/dist/engine/harness/agent-harness.d.ts +6 -6
  140. package/dist/engine/harness/agent-harness.js +26 -1
  141. package/dist/engine/harness/messages.d.ts +1 -1
  142. package/dist/engine/harness/types.d.ts +15 -11
  143. package/dist/engine/llm/types.d.ts +79 -14
  144. package/dist/engine/loop/agent-loop.d.ts +3 -3
  145. package/dist/engine/loop/types.d.ts +4 -4
  146. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  147. package/dist/engine/session/import-validate.d.ts +1 -1
  148. package/dist/engine/session/log-digest.d.ts +1 -1
  149. package/dist/engine/session/memory-repo.d.ts +2 -2
  150. package/dist/engine/session/session.d.ts +4 -4
  151. package/dist/fixtures/index.d.ts +4 -4
  152. package/dist/index.d.ts +8 -4
  153. package/dist/index.js +5 -2
  154. package/dist/internal/llm.d.ts +1 -1
  155. package/dist/orchestration/goal.d.ts +1 -1
  156. package/dist/orchestration/run-spec.d.ts +1 -1
  157. package/dist/orchestration/run-workflow-tool.d.ts +12 -12
  158. package/dist/orchestration/workflow-governance.d.ts +4 -4
  159. package/dist/orchestration/workflow-observe.d.ts +1 -1
  160. package/dist/orchestration/workflow-script-runner.d.ts +1 -1
  161. package/dist/orchestration/workflow-script-store.d.ts +9 -9
  162. package/dist/orchestration/workflow-size-guideline.d.ts +1 -1
  163. package/dist/orchestration/workflow-types.d.ts +5 -5
  164. package/dist/orchestration/workflow.d.ts +10 -10
  165. package/dist/prompt-assembly/artifact-store.d.ts +1 -1
  166. package/dist/prompt-assembly/artifact.d.ts +1 -1
  167. package/dist/prompt-assembly/assemble.d.ts +1 -1
  168. package/dist/prompt-assembly/composer.d.ts +2 -2
  169. package/dist/prompt-assembly/epoch.d.ts +2 -2
  170. package/dist/prompt-assembly/event-registry.d.ts +1 -1
  171. package/dist/prompt-assembly/explain.d.ts +3 -3
  172. package/dist/prompt-assembly/tool-catalog.d.ts +1 -1
  173. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -4
  174. package/dist/prompt-assembly/types.d.ts +12 -12
  175. package/dist/prompts/coordinator.d.ts +1 -1
  176. package/dist/prompts/default.d.ts +10 -10
  177. package/dist/prompts/default.js +2 -0
  178. package/dist/prompts/simple-sections.d.ts +3 -3
  179. package/dist/prompts/supervisor.d.ts +2 -2
  180. package/dist/scenarios/full-body.d.ts +3 -3
  181. package/dist/scenarios/scenario-registry.d.ts +6 -2
  182. package/dist/scenarios/scenario-registry.js +4 -2
  183. package/dist/stores/cc/sidecar-transcript.d.ts +3 -3
  184. package/dist/stores/file/fs-atomic.d.ts +2 -2
  185. package/dist/stores/file/index.d.ts +1 -1
  186. package/dist/stores/file/session-store.d.ts +2 -2
  187. package/dist/stores/file/workflow-journal-store.d.ts +4 -4
  188. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  189. package/dist/tools/fs/encoding.d.ts +4 -4
  190. package/dist/tools/fs/fs-bash.d.ts +3 -3
  191. package/dist/tools/fs/fs-pdf.d.ts +1 -1
  192. package/dist/tools/fs/fs-shared.d.ts +6 -6
  193. package/dist/tools/fs/index.d.ts +2 -2
  194. package/dist/tools/fs/index.js +8 -1
  195. package/dist/tools/fs/notebook.d.ts +1 -1
  196. package/dist/tools/fs/pdf.d.ts +1 -1
  197. package/dist/tools/fs/read-deny.d.ts +1 -1
  198. package/dist/tools/fs/safety.d.ts +9 -9
  199. package/dist/tools/fs/search.d.ts +2 -2
  200. package/dist/tools/monitor.d.ts +3 -3
  201. package/dist/tools/scheduler-tools.js +28 -6
  202. package/dist/tools/task-list.d.ts +2 -2
  203. package/dist/tools/web.d.ts +19 -4
  204. package/dist/tools/web.js +8 -2
  205. package/dist/tools/worktree.d.ts +5 -5
  206. package/dist/tools/worktree.js +2 -2
  207. package/package.json +1 -1
  208. package/test/export-surface.snapshot.json +74 -3
@@ -71,7 +71,7 @@ export interface DeliveryDecision {
71
71
  }
72
72
  /**
73
73
  * design/95 §3.2 — the lifecycle status of a run, so INFRA noise never poisons the mode signal
74
- * (codex BLOCKER B4 / design/89 §3.3 Beatsep red line). The Beatsep task#2b 9% pass-rate was infra
74
+ * (design/89 §3.3 Beatsep red line). The Beatsep task#2b 9% pass-rate was infra
75
75
  * death (OOM / K8S passthrough / nested-root), NOT a mode signal — feeding such runs into the
76
76
  * numerator turns them into spurious DELIVERED-WRONG / zero datapoints and reproduces the very noise
77
77
  * the design claims to have isolated. `buildReport` SCORES only `scored`; `infra-failed` / `excluded`
@@ -94,7 +94,7 @@ export interface RunRecord {
94
94
  /** Repeat/seed index within the cell (design/89 §3.4 N-repeat). */
95
95
  seed?: number | string;
96
96
  /**
97
- * design/95 §3.2 / codex B4 — lifecycle status. Only "scored" runs reach the numerator / cost /
97
+ * design/95 §3.2 — lifecycle status. Only "scored" runs reach the numerator / cost /
98
98
  * Pareto. Absent ⇒ "scored" (back-compat). See {@link RunStatus}.
99
99
  */
100
100
  runStatus?: RunStatus;
@@ -313,7 +313,7 @@ export interface ArmCell {
313
313
  /** Avoided-loss rate = CORRECTLY-WITHHELD / n. The supervisor's §2.2.1 value, single-listed. */
314
314
  correctlyWithheldRate: number;
315
315
  /**
316
- * codex MINOR / design/95 M10 — withhold treated as a binary detector of would-be-wrong delivery
316
+ * design/95 M10 — withhold treated as a binary detector of would-be-wrong delivery
317
317
  * (positive = withheld; ground truth = would-be-wrong, established by the §2.4 counterfactual). Reported
318
318
  * as precision/recall — NOT just the raw correctly-withheld count — so an arm cannot look good by
319
319
  * withholding indiscriminately (high count, low precision) or by rarely withholding (high precision,
@@ -420,10 +420,10 @@ export declare function riskTransferDisclosure(cells: ArmCell[]): {
420
420
  };
421
421
  };
422
422
  /**
423
- * codex B2 — a paired-binary comparison of two arms on the SAME tasks/seeds. The two arms' truly-
423
+ * a paired-binary comparison of two arms on the SAME tasks/seeds. The two arms' truly-
424
424
  * correct flags are paired row-by-row (paired-seed, design/89 §3.4). For binary paired data the
425
425
  * RIGHT tests are McNemar (discordant pairs) and a paired-bootstrap difference interval — NOT two
426
- * independent proportions with "CI non-overlap" (that ignores the pairing and is under-powered, codex B2).
426
+ * independent proportions with "CI non-overlap" (that ignores the pairing and is under-powered).
427
427
  */
428
428
  export interface PairedBinaryComparison {
429
429
  /** Arm A (e.g. "sup") vs arm B (e.g. "solo"). pDiff = P(A) − P(B). */
@@ -440,12 +440,12 @@ export interface PairedBinaryComparison {
440
440
  * b+c large). */
441
441
  mcnemarP: number;
442
442
  /** Bootstrap percentile 95% CI for the paired difference (lo, hi) — a percentile CI on the seeded
443
- * paired-bootstrap distribution, NOT Newcombe's analytic interval (codex MAJOR-B). Crosses 0 ⇒ not significant. */
443
+ * paired-bootstrap distribution, NOT Newcombe's analytic interval. Crosses 0 ⇒ not significant. */
444
444
  ci95: [number, number];
445
445
  /** TRUE iff the difference is statistically significant at α=0.05 (CI excludes 0 AND McNemar p<0.05). */
446
446
  significant: boolean;
447
447
  /**
448
- * codex B2 — pre-registered Minimum Detectable Effect at the observed nPairs (the difference this
448
+ * pre-registered Minimum Detectable Effect at the observed nPairs (the difference this
449
449
  * comparison COULD have detected at 80% power). When |pDiff| is below this AND not significant, the
450
450
  * verdict is "not powered" — NOT "no difference". This is the field that stops "N≥15 is an assertion".
451
451
  */
@@ -456,8 +456,8 @@ export interface PairedBinaryComparison {
456
456
  verdict: "A-better" | "B-better" | "no-detectable-difference" | "not-powered";
457
457
  }
458
458
  /**
459
- * codex B2 — compare two arms' delivered truly-correct as PAIRED binary. Pairs runs by the COMPOSITE
460
- * `(taskId, seed)` key (codex MAJOR-A: a bare `seed` collides across tasks — the same repeat index
459
+ * compare two arms' delivered truly-correct as PAIRED binary. Pairs runs by the COMPOSITE
460
+ * `(taskId, seed)` key (a bare `seed` collides across tasks — the same repeat index
461
461
  * recurs per task — so cross-task input would overwrite pairs and contaminate the McNemar sample);
462
462
  * only rows where BOTH arms delivered (not withheld, both scored) form a pair — a
463
463
  * withheld run has no delivered binary to pair (it is scored in the withhold/avoided-loss axis, not
@@ -469,7 +469,7 @@ export declare function pairedBinaryCompare(scoredRuns: RunRecord[], armA: Arm,
469
469
  bootstrapIters?: number;
470
470
  }): PairedBinaryComparison;
471
471
  /**
472
- * codex B3 — the pre-registered C2 exchange rate(s): how many human-review SECONDS we are willing to
472
+ * the pre-registered C2 exchange rate(s): how many human-review SECONDS we are willing to
473
473
  * pay to buy one unit of supervisor value. Without these, "higher P / more withholds ⇒ worth it" is
474
474
  * unfalsifiable (the "helpful but too expensive" counter-thesis cannot be observed). Declared BEFORE
475
475
  * the run (design/95 §9), not fit after.
@@ -482,20 +482,20 @@ export interface C2Thresholds {
482
482
  }
483
483
  export interface ParetoVerdict {
484
484
  arm: Arm;
485
- /** Non-dominated on (C1, C2.sec, P) — necessary condition to be considered at all (codex B3). */
485
+ /** Non-dominated on (C1, C2.sec, P) — necessary condition to be considered at all. */
486
486
  nonDominated: boolean;
487
487
  /** The pre-registered exchange-rate check: does the arm's extra C2 buy enough avoided-loss / saved
488
488
  * decisions to clear the declared threshold? Undefined when the arm has no extra C2 over baseline. */
489
489
  clearsC2Threshold?: boolean;
490
490
  /**
491
- * codex B3 — the label. An arm may ONLY be "worth-it" when it is non-dominated AND clears the C2
491
+ * the label. An arm may ONLY be "worth-it" when it is non-dominated AND clears the C2
492
492
  * threshold. A non-dominated but threshold-failing arm is "quality-tradeoff" (higher quality, but the
493
493
  * cost is not bought back) — NEVER "worth-it". A dominated arm is "dominated".
494
494
  */
495
495
  label: "worth-it" | "quality-tradeoff" | "dominated";
496
496
  }
497
497
  /**
498
- * codex B3 — classify each arm against the SUP/TEAM-vs-baseline value question with a pre-registered
498
+ * classify each arm against the SUP/TEAM-vs-baseline value question with a pre-registered
499
499
  * exchange rate. `baselineArm` is the cost floor to compare extra C2 against (default "solo").
500
500
  * `avoidedWrong` / `savedDecisions` per arm come from the cells / campaign report.
501
501
  *
@@ -518,31 +518,31 @@ export declare function paretoValueVerdict(cells: ArmCell[], thresholds: C2Thres
518
518
  * design/95 §7.1 — V1 (免重复劳动): campaign-level human-decision delta. The ONLY authoritative V1
519
519
  * mechanism (design/95 reconcile, MAJOR#2): cross-task reuse of a decided strategy. Semantics:
520
520
  * - `soloHumanDecisions` is the V1 BASELINE — the operator's actual up-front decision count when
521
- * running solo across the campaign (codex M7: NOT 0, NOT synthetic — measured from real operator
521
+ * running solo across the campaign (NOT 0, NOT synthetic — measured from real operator
522
522
  * prep across runs). `0` is only valid when the campaign genuinely needed no human decision.
523
523
  * - `repeatedDecisionsSaved = soloHumanDecisions − supHumanDecisions` (may be negative = SUP cost
524
524
  * MORE human decisions; reported honestly, not floored).
525
525
  */
526
526
  export interface CampaignV1Saved {
527
527
  decisionKind: string;
528
- /** V1 baseline (codex M7): operator's measured up-front decisions when running SOLO. Provenance MUST
528
+ /** V1 baseline: operator's measured up-front decisions when running SOLO. Provenance MUST
529
529
  * be recorded in the stamp (real prep, not 0/synthetic). */
530
530
  soloHumanDecisions: number;
531
531
  /** SUP arm's actual human-review decision count for this decision kind. */
532
532
  supHumanDecisions: number;
533
533
  /** = soloHumanDecisions − supHumanDecisions. Positive ⇒ SUP saved repeated labour (V1 evidence). */
534
534
  repeatedDecisionsSaved: number;
535
- /** Whether the baseline is a real measurement vs absent (codex M7 honesty gate). */
535
+ /** Whether the baseline is a real measurement vs absent (honesty gate). */
536
536
  baselineProvenance: "measured-operator-baseline" | "absent-not-claimable";
537
537
  }
538
538
  /**
539
- * design/95 §7.2 — V2 (蓝图清晰度) THREE-arm ablation (codex BLOCKER#1 + MAJOR#3 / M8). All three are
539
+ * design/95 §7.2 — V2 (蓝图清晰度) THREE-arm ablation. All three are
540
540
  * paired on the SAME seeds (paired===true ⇒ the three pX arrays/values are over the same n seeds).
541
541
  * INVARIANT: `n` is the paired-seed count common to all three sub-arms; the three deltas are computed
542
542
  * over those n pairs.
543
543
  * - pSoloRaw — only the original brief (no blueprint baseline).
544
544
  * - pSoloSupGenerated — the SUP-arm-GENERATED blueprint (attributable to supervisor; gen cost charged).
545
- * - pSoloExperimenterIdeal — the experimenter "ideal" blueprint. codex M8: this is a CONSTRAINED upper
545
+ * - pSoloExperimenterIdeal — the experimenter "ideal" blueprint. this is a CONSTRAINED upper
546
546
  * bound on "better-prompt help", NOT "supervisor's mechanistic ceiling", UNLESS the ideal blueprint
547
547
  * was generated under the {@link IdealBlueprintConstraints} (run-time, visible-brief-only, no oracle
548
548
  * access, same info budget, leak-reviewed). `idealConstraintsSatisfied` records which it is.
@@ -557,16 +557,16 @@ export interface BlueprintAblationTriple {
557
557
  pSoloExperimenterIdeal: number;
558
558
  /** = pSoloSupGenerated − pSoloRaw (supervisor blueprint's real net value). */
559
559
  deltaSupVsRaw: number;
560
- /** = pSoloExperimenterIdeal − pSoloRaw. LABELLED per `idealConstraintsSatisfied` (codex M8). */
560
+ /** = pSoloExperimenterIdeal − pSoloRaw. LABELLED per `idealConstraintsSatisfied`. */
561
561
  deltaIdealVsRaw: number;
562
- /** codex M8 — when false, deltaIdealVsRaw is "manual-prompt upper bound", NOT "supervisor potential". */
562
+ /** when false, deltaIdealVsRaw is "manual-prompt upper bound", NOT "supervisor potential". */
563
563
  idealConstraintsSatisfied: boolean;
564
- /** codex M3 — blueprint generation cost charged to the sup-generated arm (budget-match red line). */
564
+ /** blueprint generation cost charged to the sup-generated arm (budget-match red line). */
565
565
  blueprintGenC1MicroUsd: number;
566
566
  blueprintGenC2Count: number;
567
567
  blueprintGenC2Sec: number;
568
568
  }
569
- /** codex M8 — the constraints under which an "ideal" blueprint may be read as a supervisor-mechanism
569
+ /** the constraints under which an "ideal" blueprint may be read as a supervisor-mechanism
570
570
  * upper bound rather than a generic "better prompt helps" result. Recorded per ablation. */
571
571
  export interface IdealBlueprintConstraints {
572
572
  generatedBeforeRun: boolean;
@@ -575,10 +575,10 @@ export interface IdealBlueprintConstraints {
575
575
  sameInfoBudget: boolean;
576
576
  solutionLeakReviewed: boolean;
577
577
  }
578
- /** True iff ALL ideal-blueprint constraints hold (codex M8 gate). */
578
+ /** True iff ALL ideal-blueprint constraints hold. */
579
579
  export declare function idealConstraintsSatisfied(c: IdealBlueprintConstraints): boolean;
580
580
  /**
581
- * design/95 §7.3 — V3 (无关性隔离) contamination probe (canary, mechanical, zero LLM-judge). codex M9:
581
+ * design/95 §7.3 — V3 (无关性隔离) contamination probe (canary, mechanical, zero LLM-judge).
582
582
  * the isolated-vs-shared P delta CONFOUNDS execution order / context size / worker count / budget; the
583
583
  * PRIMARY signal must be per-subgoal oracle failure + explicit wrong-use of a sibling artifact (the
584
584
  * canary leak), with the P delta kept as a DIAGNOSTIC only.
@@ -590,12 +590,12 @@ export interface IsolationContamination {
590
590
  isolatedSubgoalP: number[];
591
591
  /** Per-factor P under shared context. length === factorCount. */
592
592
  sharedSubgoalP: number[];
593
- /** PRIMARY mechanical signal (codex M9): cross-factor canary leak rate (explicit token bleed). */
593
+ /** PRIMARY mechanical signal: cross-factor canary leak rate (explicit token bleed). */
594
594
  canaryLeakRate: number;
595
- /** PRIMARY mechanical signal (codex M9): count of factors that failed their oracle AND demonstrably
595
+ /** PRIMARY mechanical signal: count of factors that failed their oracle AND demonstrably
596
596
  * used a sibling factor's artifact (the controlled-ablation harm signal). */
597
597
  wrongSiblingArtifactUses: number;
598
- /** DIAGNOSTIC ONLY (codex M9 confound): mean(isolated P) − mean(shared P). Not the primary signal. */
598
+ /** DIAGNOSTIC ONLY (confound): mean(isolated P) − mean(shared P). Not the primary signal. */
599
599
  contaminationRateDiag: number;
600
600
  /** Extra C1 the isolation cost (multi-worker / repeated context load). */
601
601
  isolationOverheadC1: number;
@@ -614,7 +614,7 @@ export interface CampaignReport {
614
614
  implementedAxes: ImplementedAxes;
615
615
  }
616
616
  /**
617
- * design/95 §2 / codex B1 — one heterogeneity CELL: a `(suiteVersion, taskId, archetype, valueDimension,
617
+ * design/95 §2 — one heterogeneity CELL: a `(suiteVersion, taskId, archetype, valueDimension,
618
618
  * arm)` group with its folded {@link ArmCell}. The decision surface is the per-cell vector — NOT the
619
619
  * arm mean (the Simpson's-paradox guard). `groupKey` is the stable join key.
620
620
  */
@@ -648,7 +648,7 @@ export interface ComparisonCell {
648
648
  pareto: ParetoResult;
649
649
  }
650
650
  /**
651
- * codex MINOR11 / council Q1 — capability metadata: which of the four §7/Pareto value axes are actually
651
+ * council Q1 — capability metadata: which of the four §7/Pareto value axes are actually
652
652
  * IMPLEMENTED in this report. Consumers must read this before claiming a V1/V2/V3 result; an unset axis
653
653
  * is NOT a null result, it is "not measured here". (The T1 structures above are types + pure helpers;
654
654
  * the report-level V1/V2/V3 fields are populated by the harness, not synthesized in `buildReport`.)
@@ -660,7 +660,7 @@ export interface ImplementedAxes {
660
660
  v3: boolean;
661
661
  }
662
662
  export interface ValueJudgmentReport {
663
- /** codex B1 — per-arm cells grouped by full heterogeneity key (incl. arm). Single-arm; use for cell
663
+ /** per-arm cells grouped by full heterogeneity key (incl. arm). Single-arm; use for cell
664
664
  * inspection / `pairedBinaryCompare` inputs. NOT directly a cross-arm verdict surface (each is one arm). */
665
665
  groupedCells: GroupedCell[];
666
666
  /**
@@ -671,7 +671,7 @@ export interface ValueJudgmentReport {
671
671
  */
672
672
  comparisons: ComparisonCell[];
673
673
  /**
674
- * codex B1 — suite-level rollup: per-arm cells folded over ALL scored runs. A WEIGHTED summary for
674
+ * suite-level rollup: per-arm cells folded over ALL scored runs. A WEIGHTED summary for
675
675
  * dashboards ONLY — NOT a decision surface (folding heterogeneous tasks can flip a verdict, Simpson).
676
676
  * Consumers MUST decide on `comparisons`; this is convenience aggregation.
677
677
  */
@@ -687,7 +687,7 @@ export interface ValueJudgmentReport {
687
687
  /** design/89 §3.4: cells with n<8 are directional-only, not verdict-grade. Flagged, not dropped. */
688
688
  directionalOnly: Arm[];
689
689
  /**
690
- * codex B4 — the exclusion ledger: runs dropped from scoring (infra-failed / excluded) with reasons.
690
+ * the exclusion ledger: runs dropped from scoring (infra-failed / excluded) with reasons.
691
691
  * A regression-grade invariant: these never reach `cells` / `pareto` (no infra noise in the verdict).
692
692
  */
693
693
  excluded: Array<{
@@ -696,16 +696,16 @@ export interface ValueJudgmentReport {
696
696
  seed?: number | string;
697
697
  reason: RunStatus;
698
698
  }>;
699
- /** codex MINOR11 — which value axes this report actually measured. */
699
+ /** which value axes this report actually measured. */
700
700
  implementedAxes: ImplementedAxes;
701
701
  }
702
702
  /**
703
- * Top-level (codex B1+B4): SCORE only `scored` runs (B4 — drop infra-failed/excluded with reasons),
703
+ * Top-level: SCORE only `scored` runs (B4 — drop infra-failed/excluded with reasons),
704
704
  * group by `(suiteVersion, taskId, archetype, valueDimension, arm)` (B1 — the per-cell decision surface,
705
705
  * Simpson's-paradox guard), fold each group, AND provide a suite-level per-arm rollup for dashboards
706
706
  * (explicitly NOT a decision surface). Deterministic; no model.
707
707
  *
708
- * `opts.implementedAxes` lets the harness declare which §7 axes it populated (codex MINOR11); default
708
+ * `opts.implementedAxes` lets the harness declare which §7 axes it populated; default
709
709
  * = only Pareto (the V1/V2/V3 structures are T1 net-new, not synthesized here).
710
710
  */
711
711
  export declare function buildReport(runs: RunRecord[], opts?: {
@@ -5,7 +5,8 @@ import { createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js"
5
5
  import { mintFallbackToolCallId } from "./tool-call-id.js";
6
6
  import { emitBrainTelemetry, reportReasoningWireFacts } from "./status-sink.js";
7
7
  import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, modelSupportsVision, sendableImages } from "./media-degrade.js";
8
- import { ANTHROPIC_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders, stripAuthHeaders, takeHeaderCasefold } from "./request-params.js";
8
+ import { ANTHROPIC_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders, takeHeaderCasefold } from "./request-params.js";
9
+ import { adjudicateModelRoute, applyRouteCredentialHeaders, createBrainRouteJudge, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
9
10
  import { MIN_THINKING_TOKENS, budgetCapSkipsThinking, declaredEffortLevels, reasoningBudgetShare, reasoningRequestCarried, resolveEffort } from "./reasoning.js";
10
11
  import { runStreamingBrain } from "./stream-engine.js";
11
12
  function thinkingBudget(maxTokens, share, fixed, hardCap = false) {
@@ -234,7 +235,17 @@ export function createAnthropicBrain(config = {}) {
234
235
  httpLabel: "anthropic",
235
236
  stallTimeouts: options?.stallTimeouts,
236
237
  buildRequest: (overrides) => {
237
- const apiKey = options?.apiKey ?? config.apiKey;
238
+ const routeCredential = resolveRouteCredential({
239
+ optionsApiKey: options?.apiKey,
240
+ optionsHeaders: options?.headers,
241
+ modelHeaders: model.headers,
242
+ configApiKey: config.apiKey,
243
+ configHeaders: config.headers,
244
+ });
245
+ const routeVerdict = adjudicateModelRoute(model, routeCredential, { baseUrl: config.baseUrl });
246
+ if (!routeVerdict.ok)
247
+ throw new BrainError("invalid_request", routeRefusalText(routeVerdict));
248
+ const apiKey = routeCredential.apiKey;
238
249
  const root = (model.baseUrl || config.baseUrl || "https://api.anthropic.com").replace(/\/+$/, "");
239
250
  const nonEmptyBlocks = context.systemBlocks?.filter((b) => b.text.length > 0);
240
251
  const system = nonEmptyBlocks?.length
@@ -302,8 +313,7 @@ export function createAnthropicBrain(config = {}) {
302
313
  betas.push("interleaved-thinking-2025-05-14");
303
314
  }
304
315
  const headers = mergeHeaders(model.headers, config.headers, options?.headers);
305
- if (options?.apiKey !== undefined)
306
- stripAuthHeaders(headers);
316
+ applyRouteCredentialHeaders(headers, routeCredential, { model: model.headers, options: options?.headers });
307
317
  if (betas.length > 0) {
308
318
  const existing = (takeHeaderCasefold(headers, "anthropic-beta") ?? "").split(",").map((b) => b.trim()).filter(Boolean);
309
319
  for (const beta of betas) {
@@ -680,5 +690,5 @@ export function createAnthropicBrain(config = {}) {
680
690
  },
681
691
  });
682
692
  };
683
- return { stream };
693
+ return { stream, adjudicateRoute: createBrainRouteJudge(config) };
684
694
  }
@@ -153,5 +153,6 @@ export function createCircuitBreakerBrain(inner, opts = {}) {
153
153
  })();
154
154
  return out;
155
155
  };
156
- return { stream };
156
+ const adjudicateRoute = (model, perModelAuth) => inner.adjudicateRoute?.(model, perModelAuth);
157
+ return { stream, adjudicateRoute };
157
158
  }
@@ -75,7 +75,7 @@ export declare function readDegradation(msg: {
75
75
  diagnostics?: AssistantMessageDiagnostic[];
76
76
  } | undefined): DegradationInfo | undefined;
77
77
  /**
78
- * design/131 (codex 131 审 A) — resilience-standdown pass-through: forward one brain's stream
78
+ * design/131 — resilience-standdown pass-through: forward one brain's stream
79
79
  * verbatim, but keep the decorator discipline that a REJECTING inner brain (throw instead of an
80
80
  * error event) still leaves a terminal on the returned stream — otherwise `result()` never
81
81
  * resolves and the harness hangs (the same class as the degradation-brain blocker).
@@ -144,6 +144,8 @@ export function createDegradingBrain(opts) {
144
144
  break;
145
145
  if (hop.model.id === failedModelId)
146
146
  continue;
147
+ if (hop.brain.adjudicateRoute?.(hop.model)?.ok === false)
148
+ continue;
147
149
  chainPath.push(hop.model.id);
148
150
  degradeInfo = {
149
151
  from: model.id,
@@ -182,5 +184,6 @@ export function createDegradingBrain(opts) {
182
184
  })();
183
185
  return out;
184
186
  };
185
- return { stream };
187
+ const adjudicateRoute = (model, perModelAuth) => opts.primary.adjudicateRoute?.(model, perModelAuth);
188
+ return { stream, adjudicateRoute };
186
189
  }
@@ -12,7 +12,7 @@ export declare const ZERO_USAGE: Usage;
12
12
  /** The single source of truth for brain error codes — the type, the `[code]` parser, and the prefix
13
13
  * stripper all derive from this one list, so adding a code can't silently diverge across call sites.
14
14
  *
15
- * The last three are IN-BAND terminal-output codes ([1086]③): stamped on errored assistant MESSAGES
15
+ * The last three are IN-BAND terminal-output codes: stamped on errored assistant MESSAGES
16
16
  * (never thrown as BrainError) so `TaskResult.errorCode` covers the error family end-to-end and
17
17
  * downstream consumers can bucket without string-matching. Deliberately behavior-neutral everywhere
18
18
  * else: the circuit breaker's default countCodes excludes them (model-output problems, not provider
@@ -27,7 +27,7 @@ export type BrainErrorCode = (typeof BRAIN_ERROR_CODES)[number];
27
27
  * tool-errors.ts must classify every member — a code added here but missed there folds to "unknown"). */
28
28
  export declare const ALL_BRAIN_ERROR_CODES: readonly BrainErrorCode[];
29
29
  /** The in-band terminal-output subset (see the list doc above). The circuit breaker collapses these
30
- * to the legacy `"http"` bucket before counting (codex [1086]③ 审): its taxonomy is TRANSPORT-level,
30
+ * to the legacy `"http"` bucket before counting: its taxonomy is TRANSPORT-level,
31
31
  * and before these codes existed the same failures reached it as the unprefixed→"http" fallback — a
32
32
  * custom `countCodes: ["http"]` policy must keep counting them exactly as it always did. */
33
33
  export declare const IN_BAND_OUTPUT_CODES: ReadonlySet<BrainErrorCode>;
@@ -65,7 +65,7 @@ export declare function stripErrorCodePrefix(errorMessage: string): string;
65
65
  */
66
66
  export type ConnectFailureClass = "deterministic" | "indeterminate";
67
67
  export declare function classifyConnectFailure(e: unknown): ConnectFailureClass;
68
- /** RB-386③ ([2090]) — render a transport-level error WITHOUT folding away its cause chain. undici's
68
+ /** RB-386③ — render a transport-level error WITHOUT folding away its cause chain. undici's
69
69
  * `fetch` rejects with `TypeError("fetch failed", { cause })` where the CAUSE carries the actually
70
70
  * actionable fact (`connect ECONNREFUSED …` / `getaddrinfo ENOTFOUND …` / `SocketError: other side
71
71
  * closed` with a `code`); `String(e)` / `e.message` alone collapsed ALL THREE network-failure shapes
@@ -82,5 +82,20 @@ export function createFailoverBrain(brains) {
82
82
  })();
83
83
  return out;
84
84
  };
85
- return { stream };
85
+ const adjudicateRoute = (model, perModelAuth) => {
86
+ let firstRefusal;
87
+ let sawUnjudged = false;
88
+ for (const b of brains) {
89
+ const verdict = b.adjudicateRoute?.(model, perModelAuth);
90
+ if (verdict === undefined) {
91
+ sawUnjudged = true;
92
+ continue;
93
+ }
94
+ if (verdict.ok)
95
+ return verdict;
96
+ firstRefusal ??= verdict;
97
+ }
98
+ return sawUnjudged ? undefined : firstRefusal;
99
+ };
100
+ return { stream, adjudicateRoute };
86
101
  }
@@ -5,7 +5,8 @@ import { createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js"
5
5
  import { mintFallbackToolCallId } from "./tool-call-id.js";
6
6
  import { emitBrainTelemetry } from "./status-sink.js";
7
7
  import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imagesOmittedNoVisionNote, modelSupportsVision, sendableImages } from "./media-degrade.js";
8
- import { OUTPUT_CAP_KEYS, RESPONSES_RESERVED, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders, stripAuthHeaders } from "./request-params.js";
8
+ import { OUTPUT_CAP_KEYS, RESPONSES_RESERVED, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders } from "./request-params.js";
9
+ import { adjudicateModelRoute, applyRouteCredentialHeaders, createBrainRouteJudge, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
9
10
  import { mintEffortWireValue, reasoningRequestCarried } from "./reasoning.js";
10
11
  import { runStreamingBrain } from "./stream-engine.js";
11
12
  const DEGENERATE_POLL_CHARS = 64;
@@ -228,7 +229,17 @@ export function createOpenResponsesBrain(config = {}) {
228
229
  httpLabel: "responses",
229
230
  stallTimeouts: options?.stallTimeouts,
230
231
  buildRequest: (overrides) => {
231
- const apiKey = options?.apiKey ?? config.apiKey;
232
+ const routeCredential = resolveRouteCredential({
233
+ optionsApiKey: options?.apiKey,
234
+ optionsHeaders: options?.headers,
235
+ modelHeaders: model.headers,
236
+ configApiKey: config.apiKey,
237
+ configHeaders: config.headers,
238
+ });
239
+ const routeVerdict = adjudicateModelRoute(model, routeCredential, { baseUrl: config.baseUrl });
240
+ if (!routeVerdict.ok)
241
+ throw new BrainError("invalid_request", routeRefusalText(routeVerdict));
242
+ const apiKey = routeCredential.apiKey;
232
243
  const root = (model.baseUrl || config.baseUrl || "").replace(/\/+$/, "");
233
244
  if (!root) {
234
245
  throw new Error("createOpenResponsesBrain: no baseUrl configured (set config.baseUrl or model.baseUrl)");
@@ -265,8 +276,7 @@ export function createOpenResponsesBrain(config = {}) {
265
276
  if (effort !== undefined)
266
277
  body.reasoning = { effort };
267
278
  const headers = mergeHeaders(model.headers, config.headers, options?.headers);
268
- if (options?.apiKey !== undefined)
269
- stripAuthHeaders(headers);
279
+ applyRouteCredentialHeaders(headers, routeCredential, { model: model.headers, options: options?.headers });
270
280
  lockHeader(headers, "content-type", "application/json");
271
281
  if (apiKey)
272
282
  headers["authorization"] = `Bearer ${apiKey}`;
@@ -738,5 +748,5 @@ export function createOpenResponsesBrain(config = {}) {
738
748
  },
739
749
  });
740
750
  };
741
- return { stream };
751
+ return { stream, adjudicateRoute: createBrainRouteJudge(config) };
742
752
  }
@@ -5,7 +5,9 @@ import { createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js"
5
5
  import { mintFallbackToolCallId } from "./tool-call-id.js";
6
6
  import { emitBrainTelemetry } from "./status-sink.js";
7
7
  import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imagesOmittedNoVisionNote, modelSupportsVision, sendableImages } from "./media-degrade.js";
8
- import { OPENAI_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders, stripAuthHeaders } from "./request-params.js";
8
+ import { OPENAI_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders } from "./request-params.js";
9
+ import { BrainError } from "./errors.js";
10
+ import { adjudicateModelRoute, applyRouteCredentialHeaders, createBrainRouteJudge, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
9
11
  import { mintEffortWireValue, reasoningRequestCarried } from "./reasoning.js";
10
12
  import { runStreamingBrain } from "./stream-engine.js";
11
13
  function closeToolCallAccum(acc) {
@@ -270,7 +272,17 @@ export function createOpenAIBrain(config = {}) {
270
272
  httpLabel: "gateway",
271
273
  stallTimeouts: options?.stallTimeouts,
272
274
  buildRequest: (overrides) => {
273
- const apiKey = options?.apiKey ?? config.apiKey;
275
+ const routeCredential = resolveRouteCredential({
276
+ optionsApiKey: options?.apiKey,
277
+ optionsHeaders: options?.headers,
278
+ modelHeaders: model.headers,
279
+ configApiKey: config.apiKey,
280
+ configHeaders: config.headers,
281
+ });
282
+ const routeVerdict = adjudicateModelRoute(model, routeCredential, { baseUrl: config.baseUrl });
283
+ if (!routeVerdict.ok)
284
+ throw new BrainError("invalid_request", routeRefusalText(routeVerdict));
285
+ const apiKey = routeCredential.apiKey;
274
286
  const root = (model.baseUrl || config.baseUrl || "").replace(/\/+$/, "");
275
287
  if (!root) {
276
288
  throw new Error("createOpenAIBrain: no baseUrl configured (set config.baseUrl or model.baseUrl)");
@@ -304,8 +316,7 @@ export function createOpenAIBrain(config = {}) {
304
316
  body.stop = options.stop;
305
317
  applyThinking(body, model, options?.reasoning);
306
318
  const headers = mergeHeaders(model.headers, config.headers, options?.headers);
307
- if (options?.apiKey !== undefined)
308
- stripAuthHeaders(headers);
319
+ applyRouteCredentialHeaders(headers, routeCredential, { model: model.headers, options: options?.headers });
309
320
  lockHeader(headers, "content-type", "application/json");
310
321
  if (apiKey)
311
322
  headers["authorization"] = `Bearer ${apiKey}`;
@@ -618,5 +629,5 @@ export function createOpenAIBrain(config = {}) {
618
629
  },
619
630
  });
620
631
  };
621
- return { stream };
632
+ return { stream, adjudicateRoute: createBrainRouteJudge(config) };
622
633
  }
@@ -92,7 +92,7 @@ export interface ReasoningResolution {
92
92
  * the cap-wins predicate below and the brain's budget-window math read ONE constant. */
93
93
  export declare const MIN_THINKING_TOKENS = 1024;
94
94
  /**
95
- * The anthropic BUDGET path's cap-wins arm (#346, single-sourced; design/119 #2 review codex H4):
95
+ * The anthropic BUDGET path's cap-wins arm (#346, single-sourced; design/119 #2 review):
96
96
  * a HARD per-request output cap (an engine-imposed override or the caller's explicit
97
97
  * `options.maxTokens` — the two lanes the brain refuses to raise) too small to host a legal thinking
98
98
  * budget (≥ {@link MIN_THINKING_TOKENS}) plus answer room means the CAP WINS and thinking is skipped
@@ -276,7 +276,7 @@ export interface ReasoningProfileFlags {
276
276
  }
277
277
  /**
278
278
  * design/96 §D (S4) — map a named {@link ReasoningTier} to its engine {@link ReasoningProfileFlags} (default
279
- * mapping). G2 (clay): the intensity→capability mapping is PROFILE-layer data — core gives this primitive +
279
+ * mapping). G2: the intensity→capability mapping is PROFILE-layer data — core gives this primitive +
280
280
  * a sane default; a profile/scenario layer calls it (and may override the table) to set a task's `thinking` +
281
281
  * prompt flags. Core never forces it. Returns a fresh COPY (callers may mutate).
282
282
  */
@@ -36,7 +36,7 @@ export declare const DEGENERATE_MESSAGE = "degenerate repetition detected \u2014
36
36
  * One detector hit — either the event that CUT a stream (`degenerate`) or a repetition that landed in
37
37
  * a detection window but was SPARED by a 2e1c161 structural allowance (code-line shape / divider run).
38
38
  * Spared events are the RSI raw material for judging whether the allowance is too wide or too narrow
39
- * (clay 2026-07-10: the detector must report counts AND the detected segment, not just a boolean).
39
+ * (2026-07-10: the detector must report counts AND the detected segment, not just a boolean).
40
40
  */
41
41
  export interface RepetitionEvent {
42
42
  /** Which rule's window the repetition landed in: rule 1 (`char-run`) or rule 2 (`unit-loop`). */
@@ -22,17 +22,16 @@ export declare function reservedFor(api: string): ReadonlySet<string>;
22
22
  * deployment with no `extraBody` is unaffected.
23
23
  */
24
24
  export declare function applyExtraBody(body: Record<string, unknown>, extraBody: Record<string, unknown> | undefined, reserved: ReadonlySet<string>): Record<string, unknown>;
25
- /**
26
- * Per-call auth REPLACES construction-time auth: drop every auth-bearing header
27
- * (case-insensitive `authorization` / `x-api-key`) from an already-merged header bag. Called by a
28
- * brain's buildRequest ONLY when a per-call `options.apiKey` is present the brain then re-emits
29
- * the credential in its own wire posture (anthropic `x-api-key`, openai `Bearer`), making the
30
- * per-call key the request's single credential. Case-insensitivity matters twice: a
31
- * construction-time `Authorization` (capital A) is what boot env tokens ship, and a case-variant
32
- * duplicate would otherwise ride the wire alongside the hard-locked lowercase form (fetch Headers
33
- * folds duplicates into one comma-joined value — broken auth both ways).
34
- */
35
- export declare function stripAuthHeaders(headers: Record<string, string>): void;
25
+ /** The auth-bearing header names (case-fold), single-sourced for the credential enforcement
26
+ * (`applyRouteCredentialHeaders`, route-adjudicator.ts the ONE authority over auth spelling: a
27
+ * per-model winner strips every carrier, any case, and re-asserts the winning bag's own),
28
+ * {@link mergeHeaders} (which EXEMPTS them see there), and the route credential resolver (which
29
+ * reads carrier PRESENCE per bag to classify a headers-borne credential). The former per-call
30
+ * strip helper that lived here (`stripAuthHeaders` "per-call auth replaces construction-time
31
+ * auth", triggered on `options.apiKey` presence) is retired: the three brains now run the
32
+ * route-credential resolution instead, whose per-model arm strips a strict superset of what the
33
+ * helper did. */
34
+ export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
36
35
  /**
37
36
  * #343 — the shared USER-HEADER merge layer (`model.headers` → construction `config.headers` →
38
37
  * per-call `options.headers`, later bag wins), CASE-FOLD deduplicated: HTTP header field names are
@@ -45,26 +44,29 @@ export declare function stripAuthHeaders(headers: Record<string, string>): void;
45
44
  * deployment — is byte-identical on the wire).
46
45
  *
47
46
  * EXEMPT: the auth carriers (`authorization` / `x-api-key`, any case) pass through with the exact
48
- * legacy spread semantics (same-spelling override only, no case-fold dedup), so that
49
- * {@link stripAuthHeaders} stays the ONE authority over auth spelling and this layer never becomes a
50
- * second, subtly different one.
47
+ * legacy spread semantics (same-spelling override only, no case-fold dedup), so that the credential
48
+ * enforcement (`applyRouteCredentialHeaders`, route-adjudicator.ts) stays the ONE authority over
49
+ * auth spelling and this layer never becomes a second, subtly different one.
51
50
  *
52
51
  * RE-RULED, because the exemption used to be justified by a reason that does not hold: the
53
52
  * note claimed it protected "the header-only ANTHROPIC_AUTH_TOKEN shape, which must survive under its
54
53
  * own capital-A spelling". Dedup would not endanger that shape — it keeps the WINNER'S spelling, and a
55
54
  * lone `Authorization` has nothing to be deduped against, so it survives either way; nor does the
56
- * per-call-replaces flow depend on the exemption, since {@link stripAuthHeaders} already deletes every
57
- * spelling present. Measured, not reasoned: `mergeHeaders({Authorization:A},{authorization:B})` keeps
58
- * BOTH, and the platform `Headers` fold sends `authorization: A, B`.
55
+ * credential-replaces flow depend on the exemption, since the enforcement already deletes every
56
+ * spelling present when a per-model credential wins. Measured, not reasoned:
57
+ * `mergeHeaders({Authorization:A},{authorization:B})` keeps BOTH, and the platform `Headers` fold
58
+ * sends `authorization: A, B`.
59
59
  *
60
- * STATED RESIDUAL (deliberately not fixed here): a deployment that spells the SAME auth carrier two
61
- * ways across two layers therefore ships both, comma-folded the very disease this function fixed
62
- * for every other header. It is held, not denied, on severity: no server accepts a comma-joined
63
- * credential, so the failure is a LOUD 401 attributable to the misconfiguration, whereas the
64
- * non-auth case this function exists for produced a silently WRONG value (`X-Tenant: a, b` neither
65
- * writer's, the later layer's documented override defeated). Tightening it changes which credential
66
- * reaches the wire, so it belongs in a window that discloses an auth-face behavior change, not in one
67
- * whose subject is the reasoning knob.
60
+ * STATED RESIDUAL (deliberately not fixed here), NARROWED by the credential enforcement: where a
61
+ * PER-MODEL credential wins (an options- or entry-bag carrier), every carrier is stripped and only
62
+ * the winning bag's re-asserted, so a cross-layer double spelling now folds to the winner. What
63
+ * remains is the DEPLOYMENT-CONFIG arm its bytes ride untouched by design so a config bag that
64
+ * itself spells the SAME auth carrier two ways still ships both, comma-folded. It is held, not
65
+ * denied, on severity: no server accepts a comma-joined credential, so the failure is a LOUD 401
66
+ * attributable to the misconfiguration, whereas the non-auth case this function exists for produced
67
+ * a silently WRONG value (`X-Tenant: a, b` — neither writer's, the later layer's documented
68
+ * override defeated). Tightening it changes which credential reaches the wire, so it belongs in a
69
+ * window that discloses an auth-face behavior change.
68
70
  */
69
71
  /**
70
72
  * #343 (review r4) — assign a STRUCTURAL locked header under its canonical lowercase name, deleting
@@ -72,8 +74,9 @@ export declare function stripAuthHeaders(headers: Record<string, string>): void;
72
74
  * the user-bag merge precisely so they "can NEVER be overridden" (council design/40) — but a valid
73
75
  * user bag carrying `Content-Type: text/plain` survived BESIDE the lowercase lock, and the platform
74
76
  * `Headers` fold turns the pair into `text/plain, application/json` on the wire: the lock decided
75
- * nothing. Auth carriers are deliberately NOT routed through here (per-call replacement + the
76
- * header-only boot flow are {@link stripAuthHeaders}' pinned jurisdiction).
77
+ * nothing. Auth carriers are deliberately NOT routed through here (credential replacement + the
78
+ * header-only boot flow are the pinned jurisdiction of `applyRouteCredentialHeaders`,
79
+ * route-adjudicator.ts).
77
80
  */
78
81
  export declare function lockHeader(headers: Record<string, string>, lowerName: string, value: string): void;
79
82
  /**
@@ -54,13 +54,7 @@ export function applyExtraBody(body, extraBody, reserved) {
54
54
  }
55
55
  return { ...passthrough, ...body };
56
56
  }
57
- const AUTH_CARRIER_NAMES = new Set(["authorization", "x-api-key"]);
58
- export function stripAuthHeaders(headers) {
59
- for (const k of Object.keys(headers)) {
60
- if (AUTH_CARRIER_NAMES.has(k.toLowerCase()))
61
- delete headers[k];
62
- }
63
- }
57
+ export const AUTH_CARRIER_NAMES = new Set(["authorization", "x-api-key"]);
64
58
  export function lockHeader(headers, lowerName, value) {
65
59
  for (const k of Object.keys(headers)) {
66
60
  if (k !== lowerName && k.toLowerCase() === lowerName)