@sema-agent/core 7.51.1 → 7.52.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 (372) hide show
  1. package/CHANGELOG.md +550 -2
  2. package/dist/agents/agent-transcript-tool.d.ts +1 -1
  3. package/dist/agents/cascade.js +4 -0
  4. package/dist/agents/cumulative-stats.d.ts +3 -0
  5. package/dist/agents/cumulative-stats.js +3 -0
  6. package/dist/agents/list-agents-tool.d.ts +1 -1
  7. package/dist/agents/read-inbox-tool.d.ts +1 -1
  8. package/dist/agents/repair-loop.js +4 -0
  9. package/dist/agents/send-message-tool.d.ts +11 -16
  10. package/dist/agents/send-message-tool.js +11 -13
  11. package/dist/agents/subagent.d.ts +5 -0
  12. package/dist/agents/subagent.js +7 -4
  13. package/dist/agents/tool-filter.d.ts +40 -13
  14. package/dist/agents/tool-filter.js +28 -14
  15. package/dist/bench/metrics.d.ts +1 -0
  16. package/dist/brain/anthropic.js +16 -12
  17. package/dist/brain/model-presets.d.ts +4 -5
  18. package/dist/brain/model-presets.js +4 -4
  19. package/dist/brain/open-responses.js +16 -12
  20. package/dist/brain/openai.js +16 -12
  21. package/dist/brain/request-params.d.ts +12 -33
  22. package/dist/brain/request-params.js +5 -2
  23. package/dist/brain/request-route.d.ts +9 -8
  24. package/dist/brain/request-route.js +1 -0
  25. package/dist/brain/route-adjudicator.d.ts +117 -47
  26. package/dist/brain/route-adjudicator.js +171 -42
  27. package/dist/brain/route-conformance.js +2 -2
  28. package/dist/brain/status-sink.d.ts +18 -0
  29. package/dist/brain/status-sink.js +7 -0
  30. package/dist/core/allow-entry.d.ts +42 -8
  31. package/dist/core/allow-entry.js +24 -17
  32. package/dist/core/auto-compaction.d.ts +23 -20
  33. package/dist/core/auto-compaction.js +37 -16
  34. package/dist/core/auto-promote.d.ts +2 -1
  35. package/dist/core/auto-promote.js +2 -2
  36. package/dist/core/caller-tool-name.d.ts +9 -0
  37. package/dist/core/caller-tool-name.js +13 -0
  38. package/dist/core/checkpoint-requirements.d.ts +7 -1
  39. package/dist/core/checkpoint-requirements.js +2 -0
  40. package/dist/core/checkpoint-store.d.ts +36 -12
  41. package/dist/core/checkpoint-store.js +1 -1
  42. package/dist/core/child-clamp-seats.d.ts +6 -0
  43. package/dist/core/child-clamp-seats.js +2 -1
  44. package/dist/core/credential-vocabulary.d.ts +33 -8
  45. package/dist/core/credential-vocabulary.js +32 -7
  46. package/dist/core/decision-receipt.d.ts +2 -2
  47. package/dist/core/delegation-frames.d.ts +9 -2
  48. package/dist/core/delegation-name.d.ts +18 -0
  49. package/dist/core/delegation-name.js +47 -0
  50. package/dist/core/engine-capabilities.d.ts +8 -1
  51. package/dist/core/engine-capabilities.js +1 -1
  52. package/dist/core/engine-notice.d.ts +36 -19
  53. package/dist/core/frontmatter-fence.d.ts +10 -0
  54. package/dist/core/frontmatter-fence.js +6 -0
  55. package/dist/core/gate-fold.js +5 -23
  56. package/dist/core/gate-lanes.js +356 -270
  57. package/dist/core/governance-codes.d.ts +9 -4
  58. package/dist/core/governance-codes.js +9 -3
  59. package/dist/core/hooks.d.ts +276 -734
  60. package/dist/core/hooks.js +97 -192
  61. package/dist/{tools/fs → core}/image-admission.d.ts +21 -3
  62. package/dist/{tools/fs → core}/image-admission.js +24 -6
  63. package/dist/core/locked-config.d.ts +26 -5
  64. package/dist/core/locked-config.js +31 -6
  65. package/dist/core/mcp-connection-pool.d.ts +4 -1
  66. package/dist/core/mcp-connection-pool.js +7 -2
  67. package/dist/core/mcp-probe.d.ts +12 -9
  68. package/dist/core/mcp-probe.js +2 -2
  69. package/dist/core/mcp-server-spec.d.ts +53 -7
  70. package/dist/core/mcp-server-spec.js +84 -1
  71. package/dist/core/mcp.d.ts +11 -7
  72. package/dist/core/mcp.js +27 -9
  73. package/dist/core/memory-engine/data-plane.js +5 -1
  74. package/dist/core/memory-engine/distiller.js +16 -2
  75. package/dist/core/memory-engine/engine.d.ts +8 -11
  76. package/dist/core/memory-engine/engine.js +9 -5
  77. package/dist/core/memory-engine/frontmatter.d.ts +6 -0
  78. package/dist/core/memory-engine/frontmatter.js +19 -16
  79. package/dist/core/memory.d.ts +2 -2
  80. package/dist/core/mode-policy.d.ts +4 -0
  81. package/dist/core/mode-policy.js +7 -5
  82. package/dist/core/model-seat.d.ts +4 -1
  83. package/dist/core/mods/dollar.d.ts +109 -0
  84. package/dist/core/mods/dollar.js +154 -0
  85. package/dist/core/mods/host.d.ts +23 -0
  86. package/dist/core/mods/host.js +21 -0
  87. package/dist/core/mods/kernel/budget.d.ts +43 -0
  88. package/dist/core/mods/kernel/budget.js +83 -0
  89. package/dist/core/mods/kernel/dispatch.d.ts +100 -0
  90. package/dist/core/mods/kernel/dispatch.js +701 -0
  91. package/dist/core/mods/kernel/events.d.ts +242 -0
  92. package/dist/core/mods/kernel/events.js +210 -0
  93. package/dist/core/mods/kernel/index.d.ts +6 -0
  94. package/dist/core/mods/kernel/index.js +5 -0
  95. package/dist/core/mods/kernel/receive.d.ts +65 -0
  96. package/dist/core/mods/kernel/receive.js +123 -0
  97. package/dist/core/mods/kernel/registration.d.ts +131 -0
  98. package/dist/core/mods/kernel/registration.js +102 -0
  99. package/dist/core/mods/kernel/site-rules.d.ts +84 -0
  100. package/dist/core/mods/kernel/site-rules.js +104 -0
  101. package/dist/core/mods/stations.d.ts +350 -0
  102. package/dist/core/mods/stations.js +444 -0
  103. package/dist/core/own-property-receipt.js +11 -23
  104. package/dist/core/parent-spec-seat.d.ts +18 -7
  105. package/dist/core/parent-spec-seat.js +6 -2
  106. package/dist/core/path-identity.js +2 -1
  107. package/dist/core/permission-rule-syntax.d.ts +15 -0
  108. package/dist/core/permission-rule-syntax.js +24 -16
  109. package/dist/core/permission-rules.d.ts +7 -1
  110. package/dist/core/permission-rules.js +26 -15
  111. package/dist/core/prompt-section-seat.d.ts +15 -27
  112. package/dist/core/prompt-section-seat.js +73 -40
  113. package/dist/core/protocol-http-auth.d.ts +3 -1
  114. package/dist/core/protocol-http-auth.js +2 -0
  115. package/dist/core/reminder-mint.d.ts +2 -2
  116. package/dist/core/remote-env.d.ts +24 -12
  117. package/dist/core/remote-env.js +1 -0
  118. package/dist/core/resume-startup-cause.d.ts +16 -1
  119. package/dist/core/resume-startup-cause.js +4 -1
  120. package/dist/core/retired-keys.d.ts +4 -0
  121. package/dist/core/retired-keys.js +3 -0
  122. package/dist/core/runner/announce-once-ledger.d.ts +31 -1
  123. package/dist/core/runner/announce-once-ledger.js +22 -0
  124. package/dist/core/runner/assemble-result.d.ts +9 -0
  125. package/dist/core/runner/assemble-result.js +3 -1
  126. package/dist/core/runner/call-hook-stations.d.ts +23 -27
  127. package/dist/core/runner/call-hook-stations.js +58 -42
  128. package/dist/core/runner/caller-stop-gate.d.ts +25 -0
  129. package/dist/core/runner/caller-stop-gate.js +7 -0
  130. package/dist/core/runner/compaction-seams.d.ts +8 -22
  131. package/dist/core/runner/compaction-seams.js +28 -53
  132. package/dist/core/runner/construction-doors.js +11 -1
  133. package/dist/core/runner/contracts.d.ts +85 -83
  134. package/dist/core/runner/credential-origin-seat.d.ts +2 -0
  135. package/dist/core/runner/credential-origin-seat.js +19 -0
  136. package/dist/core/runner/extra-body-dropped-seat.d.ts +2 -6
  137. package/dist/core/runner/gate-exit.d.ts +47 -36
  138. package/dist/core/runner/gate-exit.js +8 -2
  139. package/dist/core/runner/image.js +1 -2
  140. package/dist/core/runner/inline-image.js +1 -1
  141. package/dist/core/runner/notice-seat-input.d.ts +11 -0
  142. package/dist/core/runner/notice-seat-input.js +1 -0
  143. package/dist/core/runner/person-path-rows.js +1 -0
  144. package/dist/core/runner/prepare-acquire-reconcile.js +5 -1
  145. package/dist/core/runner/prepare-ask-lane.d.ts +5 -7
  146. package/dist/core/runner/prepare-ask-lane.js +6 -15
  147. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
  148. package/dist/core/runner/prepare-caps-and-workflow.js +25 -8
  149. package/dist/core/runner/prepare-config-doors.d.ts +5 -0
  150. package/dist/core/runner/prepare-config-doors.js +9 -0
  151. package/dist/core/runner/prepare-defer-classify.js +3 -4
  152. package/dist/core/runner/prepare-gate-stations.d.ts +8 -11
  153. package/dist/core/runner/prepare-gate-stations.js +11 -10
  154. package/dist/core/runner/prepare-inherited-gate.d.ts +8 -12
  155. package/dist/core/runner/prepare-inherited-gate.js +8 -21
  156. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  157. package/dist/core/runner/prepare-park-ask.js +10 -14
  158. package/dist/core/runner/prepare-policy-chain.d.ts +18 -16
  159. package/dist/core/runner/prepare-policy-chain.js +98 -101
  160. package/dist/core/runner/prepare-project-context.js +7 -10
  161. package/dist/core/runner/prepare-prompt-assembly.d.ts +45 -40
  162. package/dist/core/runner/prepare-prompt-assembly.js +134 -101
  163. package/dist/core/runner/prepare-prompt-inputs.d.ts +18 -19
  164. package/dist/core/runner/prepare-prompt-inputs.js +5 -7
  165. package/dist/core/runner/prepare-protocol-tools.d.ts +6 -4
  166. package/dist/core/runner/prepare-protocol-tools.js +2 -2
  167. package/dist/core/runner/prepare-safety-scan.js +6 -10
  168. package/dist/core/runner/prepare-suspend-saga.d.ts +6 -0
  169. package/dist/core/runner/prepare-suspend-saga.js +7 -5
  170. package/dist/core/runner/prepare-task.js +33 -15
  171. package/dist/core/runner/prepare-turn-wiring.d.ts +13 -15
  172. package/dist/core/runner/prepare-turn-wiring.js +29 -27
  173. package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -6
  174. package/dist/core/runner/prepare-wiring-manifest.js +7 -7
  175. package/dist/core/runner/resume-apply.d.ts +5 -2
  176. package/dist/core/runner/resume-apply.js +24 -42
  177. package/dist/core/runner/resume-checkpoint-screen.d.ts +1 -0
  178. package/dist/core/runner/resume-checkpoint-screen.js +9 -1
  179. package/dist/core/runner/resume-preflight.js +38 -16
  180. package/dist/core/runner/retired-runner-deps.js +3 -1
  181. package/dist/core/runner/run-brain-sinks.d.ts +3 -2
  182. package/dist/core/runner/run-brain-sinks.js +5 -1
  183. package/dist/core/runner/run-compaction-machinery.d.ts +7 -1
  184. package/dist/core/runner/run-compaction-machinery.js +7 -33
  185. package/dist/core/runner/run-harness-handlers.js +4 -2
  186. package/dist/core/runner/run-identity-wiring.d.ts +3 -1
  187. package/dist/core/runner/run-identity-wiring.js +3 -2
  188. package/dist/core/runner/run-leg.js +62 -84
  189. package/dist/core/runner/run-notification-lane.js +1 -0
  190. package/dist/core/runner/run-recovery-lanes.d.ts +3 -0
  191. package/dist/core/runner/run-recovery-lanes.js +11 -10
  192. package/dist/core/runner/run-settle-and-teardown.js +9 -28
  193. package/dist/core/runner/run-stop-and-final-verify.js +18 -25
  194. package/dist/core/runner/run-terminal-adoption.d.ts +3 -0
  195. package/dist/core/runner/run-terminal-adoption.js +33 -22
  196. package/dist/core/runner/run-tool-mount-facts.js +5 -3
  197. package/dist/core/runner/run-turn-boundary.js +23 -34
  198. package/dist/core/runner/runtask.d.ts +6 -7
  199. package/dist/core/runner/runtask.js +14 -72
  200. package/dist/core/runner/session-rule-policy.d.ts +6 -4
  201. package/dist/core/runner/session-rule-policy.js +17 -10
  202. package/dist/core/runner/side-call-account.d.ts +68 -0
  203. package/dist/core/runner/side-call-account.js +61 -0
  204. package/dist/core/runner/stream-halt-verbs.js +12 -17
  205. package/dist/core/runner/stream-lifecycle-verbs.js +8 -5
  206. package/dist/core/runner/stream-steer-verb.d.ts +1 -1
  207. package/dist/core/runner/stream-steer-verb.js +20 -38
  208. package/dist/core/runner/sub-agent-deny-lists.d.ts +121 -22
  209. package/dist/core/runner/sub-agent-deny-lists.js +228 -71
  210. package/dist/core/runner/tool-end-body.d.ts +5 -6
  211. package/dist/core/runner/tool-end-body.js +6 -4
  212. package/dist/core/runner/tool-not-executed.d.ts +15 -0
  213. package/dist/core/runner/tool-not-executed.js +30 -0
  214. package/dist/core/runner/tool-occurrence.d.ts +8 -7
  215. package/dist/core/runner/tool-occurrence.js +5 -5
  216. package/dist/core/runner/tool-result-media-seat.d.ts +1 -1
  217. package/dist/core/runner-deps.d.ts +20 -28
  218. package/dist/core/safety-merge-corpus.js +18 -2
  219. package/dist/core/secret-env.d.ts +93 -20
  220. package/dist/core/secret-env.js +192 -38
  221. package/dist/core/session-policy-store.d.ts +93 -17
  222. package/dist/core/session-policy-store.js +178 -25
  223. package/dist/core/session-reconcile.d.ts +4 -0
  224. package/dist/core/session-reconcile.js +2 -1
  225. package/dist/core/shell-read-silence.js +3 -32
  226. package/dist/core/side-query.d.ts +5 -3
  227. package/dist/core/skill-grant.d.ts +3 -2
  228. package/dist/core/skill-grant.js +17 -9
  229. package/dist/core/skills-directory.d.ts +31 -11
  230. package/dist/core/skills-directory.js +655 -104
  231. package/dist/core/task-event.d.ts +13 -3
  232. package/dist/core/task-result.d.ts +8 -0
  233. package/dist/core/task-spec.d.ts +20 -19
  234. package/dist/core/task-stream.d.ts +14 -4
  235. package/dist/core/tighten-task-spec.d.ts +4 -3
  236. package/dist/core/tighten-task-spec.js +2 -4
  237. package/dist/core/tool-check-reading.d.ts +74 -0
  238. package/dist/core/tool-check-reading.js +80 -0
  239. package/dist/core/tool-policy.d.ts +59 -18
  240. package/dist/core/tool-policy.js +77 -12
  241. package/dist/core/tool-registry.d.ts +20 -0
  242. package/dist/core/tool-registry.js +4 -0
  243. package/dist/core/tool-spec.d.ts +10 -6
  244. package/dist/core/tools.js +2 -0
  245. package/dist/core/trace.d.ts +51 -32
  246. package/dist/core/types.d.ts +2 -1
  247. package/dist/core/types.js +1 -0
  248. package/dist/core/unc-spelling.d.ts +10 -0
  249. package/dist/core/unc-spelling.js +3 -0
  250. package/dist/core/untrusted-egress.d.ts +8 -0
  251. package/dist/core/untrusted-egress.js +4 -1
  252. package/dist/core/user-attachment.d.ts +12 -2
  253. package/dist/core/user-attachment.js +17 -2
  254. package/dist/core/wiring-manifest.d.ts +14 -13
  255. package/dist/core/wiring-manifest.js +2 -2
  256. package/dist/engine/compaction/utils.js +1 -2
  257. package/dist/engine/execution-env/node-execution-env.d.ts +4 -3
  258. package/dist/engine/execution-env/symlink-chain.js +1 -1
  259. package/dist/engine/harness/agent-harness.d.ts +1 -1
  260. package/dist/engine/harness/agent-harness.js +3 -3
  261. package/dist/engine/harness/types.d.ts +17 -16
  262. package/dist/engine/harness/types.js +7 -1
  263. package/dist/engine/llm/types.d.ts +12 -7
  264. package/dist/engine/loop/agent-loop.js +1 -1
  265. package/dist/engine/loop/types.d.ts +1 -1
  266. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  267. package/dist/engine/session/import-validate.js +3 -10
  268. package/dist/engine/session/session.d.ts +4 -4
  269. package/dist/engine/session/session.js +4 -4
  270. package/dist/index.d.ts +24 -17
  271. package/dist/index.js +15 -12
  272. package/dist/internal/harness.d.ts +1 -1
  273. package/dist/internal/harness.js +1 -1
  274. package/dist/orchestration/run-spec.d.ts +4 -6
  275. package/dist/orchestration/run-spec.js +39 -63
  276. package/dist/orchestration/run-workflow-tool.js +1 -2
  277. package/dist/orchestration/workflow.d.ts +11 -3
  278. package/dist/orchestration/workflow.js +11 -2
  279. package/dist/prompt-assembly/artifact-store.d.ts +37 -56
  280. package/dist/prompt-assembly/artifact-store.js +31 -49
  281. package/dist/prompt-assembly/artifact.d.ts +89 -76
  282. package/dist/prompt-assembly/artifact.js +167 -146
  283. package/dist/prompt-assembly/assemble.d.ts +34 -29
  284. package/dist/prompt-assembly/assemble.js +33 -220
  285. package/dist/prompt-assembly/bands.d.ts +20 -0
  286. package/dist/prompt-assembly/bands.js +18 -0
  287. package/dist/prompt-assembly/catalog/constitution.d.ts +23 -0
  288. package/dist/prompt-assembly/catalog/constitution.js +69 -0
  289. package/dist/prompt-assembly/catalog/core-behavior.d.ts +9 -0
  290. package/dist/prompt-assembly/catalog/core-behavior.js +85 -0
  291. package/dist/prompt-assembly/catalog/index.d.ts +11 -0
  292. package/dist/prompt-assembly/catalog/index.js +59 -0
  293. package/dist/prompt-assembly/catalog/modes.d.ts +15 -0
  294. package/dist/prompt-assembly/catalog/modes.js +137 -0
  295. package/dist/prompt-assembly/catalog/role.d.ts +10 -0
  296. package/dist/prompt-assembly/catalog/role.js +157 -0
  297. package/dist/prompt-assembly/catalog/schema.d.ts +124 -0
  298. package/dist/prompt-assembly/catalog/schema.js +25 -0
  299. package/dist/prompt-assembly/catalog/sections.d.ts +9 -0
  300. package/dist/prompt-assembly/catalog/sections.js +98 -0
  301. package/dist/prompt-assembly/catalog/team.d.ts +4 -0
  302. package/dist/prompt-assembly/catalog/team.js +258 -0
  303. package/dist/prompt-assembly/composer.d.ts +24 -28
  304. package/dist/prompt-assembly/composer.js +124 -46
  305. package/dist/prompt-assembly/epoch.d.ts +89 -79
  306. package/dist/prompt-assembly/epoch.js +115 -142
  307. package/dist/prompt-assembly/explain.d.ts +58 -34
  308. package/dist/prompt-assembly/explain.js +21 -24
  309. package/dist/prompt-assembly/overlay.d.ts +127 -0
  310. package/dist/prompt-assembly/overlay.js +301 -0
  311. package/dist/prompt-assembly/renderers.d.ts +21 -0
  312. package/dist/prompt-assembly/renderers.js +39 -0
  313. package/dist/prompt-assembly/section-usage.d.ts +3 -3
  314. package/dist/prompt-assembly/section-usage.js +1 -1
  315. package/dist/prompt-assembly/select.d.ts +56 -0
  316. package/dist/prompt-assembly/select.js +69 -0
  317. package/dist/prompt-assembly/sugar.d.ts +31 -0
  318. package/dist/prompt-assembly/sugar.js +20 -0
  319. package/dist/prompt-assembly/turn-snapshot.d.ts +3 -2
  320. package/dist/prompt-assembly/types.d.ts +45 -148
  321. package/dist/prompts/coordinator.d.ts +2 -21
  322. package/dist/prompts/coordinator.js +3 -255
  323. package/dist/prompts/cowork.d.ts +27 -16
  324. package/dist/prompts/cowork.js +2 -23
  325. package/dist/prompts/default.d.ts +38 -500
  326. package/dist/prompts/default.js +20 -285
  327. package/dist/prompts/supervisor.d.ts +6 -71
  328. package/dist/prompts/supervisor.js +6 -117
  329. package/dist/stores/file/session-policy-store.d.ts +3 -3
  330. package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -5
  331. package/dist/tools/fs/dir-entries.d.ts +4 -2
  332. package/dist/tools/fs/dir-entries.js +16 -4
  333. package/dist/tools/fs/fs-bash.d.ts +1 -1
  334. package/dist/tools/fs/fs-bash.js +22 -11
  335. package/dist/tools/fs/fs-pdf.js +17 -13
  336. package/dist/tools/fs/fs-read.js +4 -3
  337. package/dist/tools/fs/fs-shared.d.ts +1 -10
  338. package/dist/tools/fs/fs-shared.js +3 -4
  339. package/dist/tools/fs/fs-write.js +2 -1
  340. package/dist/tools/fs/notebook.js +1 -1
  341. package/dist/tools/fs/pdf.d.ts +20 -10
  342. package/dist/tools/fs/pdf.js +19 -10
  343. package/dist/tools/fs/program-read-model.d.ts +68 -8
  344. package/dist/tools/fs/program-read-model.js +112 -33
  345. package/dist/tools/fs/program-read-model.rows.js +30 -10
  346. package/dist/tools/fs/read-deny-home.d.ts +25 -9
  347. package/dist/tools/fs/read-deny-home.js +28 -27
  348. package/dist/tools/fs/read-deny.d.ts +41 -11
  349. package/dist/tools/fs/read-deny.js +51 -5
  350. package/dist/tools/fs/safety.d.ts +4 -26
  351. package/dist/tools/fs/safety.js +10 -32
  352. package/dist/tools/fs/search.js +15 -10
  353. package/dist/tools/fs/shell-read-argv.d.ts +3 -2
  354. package/dist/tools/fs/shell-read-argv.js +118 -38
  355. package/dist/tools/fs/shell-read-establish.d.ts +5 -5
  356. package/dist/tools/fs/shell-read-establish.js +11 -61
  357. package/dist/tools/fs/shell-read-identity.js +6 -3
  358. package/dist/tools/fs/shell-read-judge.d.ts +13 -15
  359. package/dist/tools/fs/shell-read-judge.js +18 -4
  360. package/dist/tools/fs/shell-read-plan.d.ts +2 -2
  361. package/dist/tools/web.js +1 -1
  362. package/package.json +13 -1
  363. package/test/export-surface.snapshot.json +256 -99
  364. package/wire-changes/core@7.51.1.json +5 -5
  365. package/wire-changes/core@7.51.2.json +293 -0
  366. package/wire-changes/core@7.52.0.json +5859 -0
  367. package/dist/core/hook-seats.d.ts +0 -66
  368. package/dist/core/hook-seats.js +0 -37
  369. package/dist/prompt-assembly/packs/sema-default.d.ts +0 -2
  370. package/dist/prompt-assembly/packs/sema-default.js +0 -147
  371. package/dist/prompts/simple-sections.d.ts +0 -58
  372. package/dist/prompts/simple-sections.js +0 -43
@@ -5,13 +5,14 @@ import { STREAM_TORN_WARNING, createRepetitionPoll, modelOutputInvalidOf, noteRe
5
5
  import { admitToolCallId, providerResultIds, providerToolCallId, responseToolCallIds } 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 } from "./request-params.js";
9
- import { adjudicateModelRoute, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
8
+ import { OUTPUT_CAP_KEYS, RESPONSES_RESERVED, applyExtraBody, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders } from "./request-params.js";
9
+ import { admitRouteRequest, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential } from "./route-adjudicator.js";
10
10
  import { requestRouteOf } from "./request-route.js";
11
11
  import { declaredOffSpelling, mintEffortWireValue, reasoningRequestCarried } from "./reasoning.js";
12
12
  import { runStreamingBrain } from "./stream-engine.js";
13
13
  import { refuseRetiredTimeoutKnobs } from "./timeout.js";
14
14
  import { resolveFetch } from "./transport.js";
15
+ const OPEN_RESPONSES_KEY_HEADER = "authorization";
15
16
  const DEGENERATE_POLL_CHARS = 64;
16
17
  const MALFORMED_SAMPLE_CHARS = 160;
17
18
  const MALFORMED_ARGS_CHARS = 200;
@@ -239,20 +240,22 @@ export function createOpenResponsesBrain(given = {}) {
239
240
  maxRetries: options?.maxRetries,
240
241
  stallTimeouts: options?.stallTimeouts,
241
242
  buildRequest: (overrides) => {
242
- const { root, origin: rootOrigin } = resolveRequestRootOf(model.baseUrl, config.baseUrl);
243
+ const routed = { id: model.id, baseUrl: model.baseUrl };
244
+ const { root, origin: rootOrigin } = resolveRequestRootOf(routed.baseUrl, config.baseUrl);
243
245
  if (!root) {
244
246
  throw new Error("createOpenResponsesBrain: no baseUrl configured (set config.baseUrl or model.baseUrl)");
245
247
  }
248
+ const modelHeaders = headerBagOnce(model.headers);
249
+ const optionsHeaders = headerBagOnce(options?.headers);
246
250
  const routeCredential = resolveRouteCredential({
247
251
  optionsApiKey: options?.apiKey,
248
- optionsHeaders: options?.headers,
249
- modelHeaders: model.headers,
252
+ optionsHeaders,
253
+ modelHeaders,
250
254
  configApiKey: config.apiKey,
251
255
  configHeaders: config.headers,
256
+ keyHeader: OPEN_RESPONSES_KEY_HEADER,
252
257
  });
253
- const routeVerdict = adjudicateModelRoute(model, routeCredential, { baseUrl: config.baseUrl });
254
- if (!routeVerdict.ok)
255
- throw new BrainError("invalid_request", routeRefusalText(routeVerdict));
258
+ const routeAdmission = admitRouteRequest(routed, routeCredential, { baseUrl: config.baseUrl });
256
259
  const apiKey = routeCredential.apiKey;
257
260
  if (options?.stop !== undefined && options.stop.length > 0) {
258
261
  throw new Error("createOpenResponsesBrain: options.stop is not expressible in the Open Responses request (the wire form has no stop-sequence field); " +
@@ -285,11 +288,11 @@ export function createOpenResponsesBrain(given = {}) {
285
288
  }
286
289
  if (effort !== undefined)
287
290
  body.reasoning = { effort };
288
- const headers = mergeHeaders(model.headers, config.headers, options?.headers);
289
- applyRouteCredentialHeaders(headers, routeCredential, { model: model.headers, options: options?.headers });
291
+ const headers = mergeHeaders(modelHeaders, config.headers, optionsHeaders);
292
+ applyRouteCredentialHeaders(headers, routeCredential);
290
293
  lockHeader(headers, "content-type", "application/json");
291
294
  if (apiKey)
292
- headers["authorization"] = `Bearer ${apiKey}`;
295
+ lockHeader(headers, OPEN_RESPONSES_KEY_HEADER, `Bearer ${apiKey}`);
293
296
  const { body: wire, dropped: extraBodyDropped } = applyExtraBody(body, model.extraBody, RESPONSES_RESERVED);
294
297
  return {
295
298
  url: joinRequestUrl(root, "/responses"),
@@ -300,6 +303,7 @@ export function createOpenResponsesBrain(given = {}) {
300
303
  outputCapTokens: effectiveOutputCap(wire, OUTPUT_CAP_KEYS.responses),
301
304
  capDerivedPaths: OUTPUT_CAP_KEYS.responses,
302
305
  onCommitted: () => {
306
+ routeAdmission();
303
307
  sentMaxTokens = builtMaxTokens;
304
308
  sentMaxTokensLane = builtMaxTokensLane;
305
309
  sentEffort = effort;
@@ -773,5 +777,5 @@ export function createOpenResponsesBrain(given = {}) {
773
777
  },
774
778
  });
775
779
  };
776
- return { stream, adjudicateRoute: createBrainRouteJudge(config) };
780
+ return { stream, adjudicateRoute: createBrainRouteJudge({ ...config, keyHeader: OPEN_RESPONSES_KEY_HEADER }) };
777
781
  }
@@ -5,15 +5,16 @@ import { STREAM_TORN_WARNING, createRepetitionPoll, createStreamEndLedger, echoF
5
5
  import { admitToolCallId, providerResultIds, providerToolCallId, responseToolCallIds } 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 } from "./request-params.js";
8
+ import { OPENAI_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders } from "./request-params.js";
9
9
  import { BrainError, providerErrorWord } from "./errors.js";
10
10
  import { inBandProviderError } from "./provider-dialect.js";
11
- import { adjudicateModelRoute, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
11
+ import { admitRouteRequest, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential } from "./route-adjudicator.js";
12
12
  import { requestRouteOf } from "./request-route.js";
13
13
  import { declaredOffSpelling, mintEffortWireValue, reasoningRequestCarried } from "./reasoning.js";
14
14
  import { runStreamingBrain } from "./stream-engine.js";
15
15
  import { refuseRetiredTimeoutKnobs } from "./timeout.js";
16
16
  import { resolveFetch } from "./transport.js";
17
+ const OPENAI_KEY_HEADER = "authorization";
17
18
  function closeToolCallAccum(acc, responseIds, closed) {
18
19
  if (acc.closedTc)
19
20
  return acc.closedTc;
@@ -342,20 +343,22 @@ export function createOpenAIBrain(given = {}) {
342
343
  maxRetries: options?.maxRetries,
343
344
  stallTimeouts: options?.stallTimeouts,
344
345
  buildRequest: (overrides) => {
345
- const { root, origin: rootOrigin } = resolveRequestRootOf(model.baseUrl, config.baseUrl);
346
+ const routed = { id: model.id, baseUrl: model.baseUrl };
347
+ const { root, origin: rootOrigin } = resolveRequestRootOf(routed.baseUrl, config.baseUrl);
346
348
  if (!root) {
347
349
  throw new Error("createOpenAIBrain: no baseUrl configured (set config.baseUrl or model.baseUrl)");
348
350
  }
351
+ const modelHeaders = headerBagOnce(model.headers);
352
+ const optionsHeaders = headerBagOnce(options?.headers);
349
353
  const routeCredential = resolveRouteCredential({
350
354
  optionsApiKey: options?.apiKey,
351
- optionsHeaders: options?.headers,
352
- modelHeaders: model.headers,
355
+ optionsHeaders,
356
+ modelHeaders,
353
357
  configApiKey: config.apiKey,
354
358
  configHeaders: config.headers,
359
+ keyHeader: OPENAI_KEY_HEADER,
355
360
  });
356
- const routeVerdict = adjudicateModelRoute(model, routeCredential, { baseUrl: config.baseUrl });
357
- if (!routeVerdict.ok)
358
- throw new BrainError("invalid_request", routeRefusalText(routeVerdict));
361
+ const routeAdmission = admitRouteRequest(routed, routeCredential, { baseUrl: config.baseUrl });
359
362
  const apiKey = routeCredential.apiKey;
360
363
  const body = {
361
364
  model: model.id,
@@ -385,11 +388,11 @@ export function createOpenAIBrain(given = {}) {
385
388
  if (options?.stop)
386
389
  body.stop = options.stop;
387
390
  applyThinking(body, model, options?.reasoning);
388
- const headers = mergeHeaders(model.headers, config.headers, options?.headers);
389
- applyRouteCredentialHeaders(headers, routeCredential, { model: model.headers, options: options?.headers });
391
+ const headers = mergeHeaders(modelHeaders, config.headers, optionsHeaders);
392
+ applyRouteCredentialHeaders(headers, routeCredential);
390
393
  lockHeader(headers, "content-type", "application/json");
391
394
  if (apiKey)
392
- headers["authorization"] = `Bearer ${apiKey}`;
395
+ lockHeader(headers, OPENAI_KEY_HEADER, `Bearer ${apiKey}`);
393
396
  const { body: wire, dropped: extraBodyDropped } = applyExtraBody(body, model.extraBody, OPENAI_RESERVED);
394
397
  return {
395
398
  url: joinRequestUrl(root, "/chat/completions"),
@@ -400,6 +403,7 @@ export function createOpenAIBrain(given = {}) {
400
403
  outputCapTokens: effectiveOutputCap(wire, OUTPUT_CAP_KEYS.openai),
401
404
  capDerivedPaths: [mtField],
402
405
  onCommitted: () => {
406
+ routeAdmission();
403
407
  sentMaxTokens = builtMaxTokens;
404
408
  sentMaxTokensLane = builtMaxTokensLane;
405
409
  },
@@ -749,5 +753,5 @@ export function createOpenAIBrain(given = {}) {
749
753
  },
750
754
  });
751
755
  };
752
- return { stream, adjudicateRoute: createBrainRouteJudge(config) };
756
+ return { stream, adjudicateRoute: createBrainRouteJudge({ ...config, keyHeader: OPENAI_KEY_HEADER }) };
753
757
  }
@@ -1,19 +1,3 @@
1
- /**
2
- * Request-parameter passthrough (design/40). A deployment can set official provider params it wants but
3
- * core doesn't model as first-class fields (OpenAI `frequency_penalty`/`presence_penalty`/`top_p`/`seed`/
4
- * `logit_bias`; Anthropic `top_k`/`top_p`/`stop_sequences`; …) via `Model.extraBody`, and core merges them
5
- * into the brain's request body. Anchored at the **Model** level (not per-turn) so the serialized body
6
- * stays byte-stable across turns → the prefix cache holds (design/9/31).
7
- *
8
- * Symmetric: each brain owns its structural + tuning keys (`reserved`) and ALWAYS wins — `extraBody` can
9
- * only supply the long tail, never clobber `messages`/`tools`/`temperature`/etc. Stripping is the safety
10
- * enforcement (the brains are standalone-usable), and a strip is never silent: {@link applyExtraBody}
11
- * RETURNS the keys it stripped, the brain reports them on the request it built
12
- * (`SSERequest.extraBodyDropped`), the stream engine reports them to the run's sink when it commits that
13
- * request (`reportExtraBodyDropped`), and the Runner announces them (`route.extra_body_reserved_dropped`).
14
- * One reading, at the one place the strip happens — so an `extraBody` a host brain swapped in for a single
15
- * request, or one on the compaction model, is said the same way the task's own model spec is.
16
- */
17
1
  /** OpenAI request-body keys core owns; `extraBody` cannot override these. */
18
2
  export declare const OPENAI_RESERVED: ReadonlySet<string>;
19
3
  /** Anthropic request-body keys core owns; `extraBody` cannot override these. (Note the asymmetry vs
@@ -43,16 +27,6 @@ export declare function applyExtraBody(body: Record<string, unknown>, extraBody:
43
27
  body: Record<string, unknown>;
44
28
  dropped: readonly string[];
45
29
  };
46
- /** The auth-bearing header names (case-fold), single-sourced for the credential enforcement
47
- * (`applyRouteCredentialHeaders`, route-adjudicator.ts — the ONE authority over auth spelling: a
48
- * per-model winner strips every carrier, any case, and re-asserts the winning bag's own),
49
- * {@link mergeHeaders} (which EXEMPTS them — see there), and the route credential resolver (which
50
- * reads carrier PRESENCE per bag to classify a headers-borne credential). The former per-call
51
- * strip helper that lived here (`stripAuthHeaders` — "per-call auth replaces construction-time
52
- * auth", triggered on `options.apiKey` presence) is retired: the three brains now run the
53
- * route-credential resolution instead, whose per-model arm strips a strict superset of what the
54
- * helper did. */
55
- export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
56
30
  /**
57
31
  * #343 — the shared USER-HEADER merge layer (`model.headers` → construction `config.headers` →
58
32
  * per-call `options.headers`, later bag wins), CASE-FOLD deduplicated: HTTP header field names are
@@ -62,10 +36,11 @@ export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
62
36
  * override silently defeated. A later bag's entry replaces an earlier case-variant instead; the
63
37
  * WINNER'S spelling and value survive (a single-spelling config is byte-identical on the wire).
64
38
  *
65
- * EXEMPT: the auth carriers (`authorization` / `x-api-key`, any case) pass through with plain
66
- * spread semantics (same-spelling override only, no case-fold dedup), so that the credential
67
- * enforcement (`applyRouteCredentialHeaders`, route-adjudicator.ts) stays the ONE authority over
68
- * auth spelling and this layer never becomes a second, subtly different one.
39
+ * EXEMPT: the credential headers ({@link isCredentialHeaderName}, the engine's one reading of which
40
+ * header carries a credential: `authorization`, `x-api-key`, `api-key`, `cookie`, …, any case) pass
41
+ * through with plain spread semantics (same-spelling override only, no case-fold dedup), so that the
42
+ * credential enforcement (`applyRouteCredentialHeaders`, route-adjudicator.ts) stays the ONE authority
43
+ * over credential spelling and this layer never becomes a second, subtly different one.
69
44
  *
70
45
  * The exemption is NOT what keeps a header-only `ANTHROPIC_AUTH_TOKEN` alive: dedup keeps the
71
46
  * winner's spelling, a lone `Authorization` has nothing to dedup against, and the credential
@@ -90,9 +65,9 @@ export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
90
65
  * user-bag merge precisely so they "can NEVER be overridden" (council design/40) — and without the
91
66
  * delete a user bag carrying `Content-Type: text/plain` survives BESIDE the lowercase lock, the
92
67
  * platform `Headers` fold turning the pair into `text/plain, application/json` on the wire: a lock
93
- * that decides nothing. Auth carriers are deliberately NOT routed through here (credential replacement + the
94
- * header-only boot flow are the pinned jurisdiction of `applyRouteCredentialHeaders`,
95
- * route-adjudicator.ts).
68
+ * that decides nothing. WHICH credential headers ride is the jurisdiction of `applyRouteCredentialHeaders`
69
+ * (route-adjudicator.ts); the key header a brain writes after it (`authorization` / `x-api-key`, from the
70
+ * apiKey knob) is a lock like any other — one value per name, whatever spelling a bag carried.
96
71
  */
97
72
  export declare function lockHeader(headers: Record<string, string>, lowerName: string, value: string): void;
98
73
  /**
@@ -103,6 +78,10 @@ export declare function lockHeader(headers: Record<string, string>, lowerName: s
103
78
  * losing the user's betas from the merge AND double-sending the header).
104
79
  */
105
80
  export declare function takeHeaderCasefold(headers: Record<string, string>, lowerName: string): string | undefined;
81
+ /** A caller's header bag read ONCE, into a plain copy of its own enumerable entries — what a request build hands both
82
+ * the route law (which credential rides, and its identity) and the merge (what goes on the wire), so a bag whose getters
83
+ * or keys answer differently on a second read cannot be judged as one bag and sent as another. */
84
+ export declare function headerBagOnce(bag: Readonly<Record<string, string>> | undefined): Record<string, string> | undefined;
106
85
  export declare function mergeHeaders(...bags: Array<Record<string, string> | undefined>): Record<string, string>;
107
86
  /**
108
87
  * The output-cap key(s) each lane's wire form uses. A lane's set is exactly the keys THAT lane's
@@ -1,3 +1,4 @@
1
+ import { isCredentialHeaderName } from "../core/secret-env.js";
1
2
  export const OPENAI_RESERVED = new Set([
2
3
  "model",
3
4
  "messages",
@@ -52,7 +53,6 @@ export function applyExtraBody(body, extraBody, reserved) {
52
53
  }
53
54
  return { body: { ...passthrough, ...body }, dropped: dropped.length === 0 ? NO_KEYS : dropped };
54
55
  }
55
- export const AUTH_CARRIER_NAMES = new Set(["authorization", "x-api-key"]);
56
56
  export function lockHeader(headers, lowerName, value) {
57
57
  for (const k of Object.keys(headers)) {
58
58
  if (k !== lowerName && k.toLowerCase() === lowerName)
@@ -70,6 +70,9 @@ export function takeHeaderCasefold(headers, lowerName) {
70
70
  }
71
71
  return undefined;
72
72
  }
73
+ export function headerBagOnce(bag) {
74
+ return bag === undefined ? undefined : { ...bag };
75
+ }
73
76
  export function mergeHeaders(...bags) {
74
77
  const out = {};
75
78
  const spellingByFold = new Map();
@@ -78,7 +81,7 @@ export function mergeHeaders(...bags) {
78
81
  continue;
79
82
  for (const [name, value] of Object.entries(bag)) {
80
83
  const fold = name.toLowerCase();
81
- if (AUTH_CARRIER_NAMES.has(fold)) {
84
+ if (isCredentialHeaderName(name)) {
82
85
  out[name] = value;
83
86
  continue;
84
87
  }
@@ -13,8 +13,8 @@
13
13
  import type { RouteCredential } from "../internal/llm.js";
14
14
  /** Where the credential a request would carry came from.
15
15
  * - `"per-model"` — resolved FOR this model: a per-call key/headers from the per-model auth hook
16
- * (`getApiKeyAndHeaders`), or an auth-bearing header declared on `Model.headers`. Paired by
17
- * construction (the resolver was asked about THIS entry), so the adjudicator never refuses it.
16
+ * (`getApiKeyAndHeaders`), or a credential header declared on `Model.headers`. Bound to the
17
+ * origin of the first request sent carrying it; refused only on its way to another origin.
18
18
  * - `"deployment-config"` — the brain construction-time fallback (`config.apiKey` / an auth header
19
19
  * in `config.headers`). Paired only with the deployment's own root (`config.baseUrl`, else the lane's default).
20
20
  * - `"absent"` — no credential anywhere. Legal for keyless gateways; refused only where an entry
@@ -40,8 +40,9 @@ export type RequestRootOrigin = (typeof REQUEST_ROOT_ORIGINS)[number];
40
40
  * - `credentialSource` — {@link RouteCredentialSource}.
41
41
  * - `carrier` — how the credential rode ({@link RouteCarrier}); absent exactly when `credentialSource` is `"absent"`.
42
42
  * - `carrierBag` — the header bag whose auth header rode ({@link RouteCarrierBag}); present exactly when one did:
43
- * with carrier `"header"`, and beside carrier `"apiKey"` in the deployment shape that sets both `config.apiKey`
44
- * and an auth header in `config.headers` (both ride).
43
+ * with carrier `"header"`, and beside carrier `"apiKey"` when one source sets both — the per-model hook's answer
44
+ * (`apiKey` and an auth header in its `headers`) or the deployment (`config.apiKey` and an auth header in
45
+ * `config.headers`); both ride.
45
46
  * - `root` — which knob named the request's root ({@link RequestRootOrigin}).
46
47
  * The legal shapes are exactly the rows of {@link ROUTE_CREDENTIAL_PAIRING}.
47
48
  */
@@ -62,16 +63,16 @@ export interface RouteCredentialPairingRow {
62
63
  /** The knob(s) the credential is read from, in this engine's own spelling (`getApiKeyAndHeaders` is the per-model
63
64
  * auth hook, whose answer reaches the brain as the per-call options). Empty for `"absent"`. */
64
65
  readonly credentialKnobs: readonly string[];
65
- /** The root knob the credential is paired with: `"Model.baseUrl"` for a per-model credential (paired by
66
- * construction — it was resolved for this entry), `"config.baseUrl"` for the deployment credential (paired only
66
+ /** The root knob the credential is paired with: `"Model.baseUrl"` for a per-model credential (paired with the
67
+ * ORIGIN that knob named the first time the credential was judged), `"config.baseUrl"` for the deployment credential (paired only
67
68
  * when the request targets that root — or, undeclared, the lane's own default endpoint). Absent for
68
69
  * `"absent"`. */
69
70
  readonly pairsWith?: "Model.baseUrl" | "config.baseUrl";
70
71
  }
71
72
  /**
72
73
  * The pairing law as a table — one row per route shape a request can carry, in resolution precedence (an earlier
73
- * row's source wins when several are configured; the three deployment rows are one rank, the deployment credential
74
- * judged as one). A consumer naming "which key was that" reads the row a {@link RequestRoute} lands on (same
74
+ * row's source wins when several are configured; one rank per source — the first three rows are the per-model hook's
75
+ * answer, the three deployment rows the deployment config, each source's credential judged as one). A consumer naming "which key was that" reads the row a {@link RequestRoute} lands on (same
75
76
  * `credentialSource` / `carrier` / `carrierBag`), never a sentence. The verdicts the law gives these shapes are the
76
77
  * conformance corpus's (`ROUTE_ADJUDICATION_CONFORMANCE_CORPUS`).
77
78
  */
@@ -14,6 +14,7 @@ export function requestRouteOf(credential, root) {
14
14
  }
15
15
  export const ROUTE_CREDENTIAL_PAIRING = Object.freeze([
16
16
  { credentialSource: "per-model", carrier: "apiKey", credentialKnobs: ["getApiKeyAndHeaders().apiKey"], pairsWith: "Model.baseUrl" },
17
+ { credentialSource: "per-model", carrier: "apiKey", carrierBag: "options", credentialKnobs: ["getApiKeyAndHeaders().apiKey", "getApiKeyAndHeaders().headers"], pairsWith: "Model.baseUrl" },
17
18
  { credentialSource: "per-model", carrier: "header", carrierBag: "options", credentialKnobs: ["getApiKeyAndHeaders().headers"], pairsWith: "Model.baseUrl" },
18
19
  { credentialSource: "per-model", carrier: "header", carrierBag: "model", credentialKnobs: ["Model.headers"], pairsWith: "Model.baseUrl" },
19
20
  { credentialSource: "deployment-config", carrier: "apiKey", credentialKnobs: ["config.apiKey"], pairsWith: "config.baseUrl" },
@@ -11,10 +11,19 @@
11
11
  * external consumers (a server annotating a model roster, a conformance suite) import so there is
12
12
  * exactly one implementation of the law.
13
13
  *
14
- * The judgment, by credential source (see {@link RouteCredentialSource}):
15
- * - `per-model` → OK always. A per-model credential was resolved FOR this entry (auth hook or an
16
- * auth header on `Model.headers`); it is paired by construction, so the per-model lane pays no
17
- * new cost.
14
+ * The judgment, by credential source (see {@link RouteCredentialSource}). ONE law for both credential
15
+ * sources — **a credential has exactly one root** — differing only in where that root comes from:
16
+ * - `per-model` → a per-model credential (auth hook or a credential header on `Model.headers`) is bound to the
17
+ * ORIGIN (scheme + host + port) of the first request the engine SENT carrying it, in the engine's one
18
+ * credential-origin table ({@link credentialOriginTable}) — written only by a brain's request gate when the request
19
+ * goes out ({@link admitRouteRequest}), read by every judgment; a request carrying it to another origin is
20
+ * refused (`route.credential_origin_mismatch`), a request to another path of the same origin is not. An
21
+ * entry whose `baseUrl` moves to another host while its credential stays the same therefore sends
22
+ * nothing: the credential belongs to the host it was first sent to, and a NEW credential (the entry's
23
+ * credential reference changed with its address) is a new binding. The binding lasts for the process. Before any
24
+ * origin is read, a per-model credential is ONE value per header: a request whose per-call `apiKey` is set beside a
25
+ * header of the very name its lane writes that key in carries two values for one header, and is refused
26
+ * (`route.credential_answer_conflict`) wherever it is going.
18
27
  * - `deployment-config` → a deployment credential has exactly ONE root: the one its brain sends to when
19
28
  * an entry names none — `config.baseUrl`, else the lane's own default endpoint
20
29
  * (`RoutePairingConfig.laneDefault`). A request to that root is `"paired"`; a request to any other
@@ -32,13 +41,18 @@
32
41
  * Bad-value loudness (garbage / contradiction / absence / misplacement):
33
42
  * - garbage — non-string URL/credential inputs throw a `TypeError` here (a programmer error must
34
43
  * not be laundered into a polite verdict); whitespace-only URLs normalize to "" (undeclared).
35
- * - contradiction — the torn pair refuses with `route.credential_mismatch` + both halves named.
44
+ * - contradiction — the torn pair refuses with `route.credential_mismatch` + both halves named; a bound
45
+ * per-model credential sent elsewhere refuses with `route.credential_origin_mismatch` + both origins named; a
46
+ * per-call key beside its own header refuses with `route.credential_answer_conflict` + both header names named.
47
+ * Each is a VERDICT, one shape for every caller of the law ({@link adjudicateModelRoute}, which throws on garbage
48
+ * alone): the request gate, a pre-flight, the read face and a failover chain read the same refusal.
36
49
  * - absence — `route.credential_missing` with the fix hint, exactly where absence is a hole.
37
50
  * - misplacement — a credential in the URL, or a non-http(s) root: {@link assertServableRequestRoot}.
38
51
  */
39
52
  import type { Model } from "../internal/llm.js";
40
53
  import type { RouteAdjudication, RouteCredential, RoutePairingConfig, RoutePairingPosture } from "../internal/llm.js";
41
54
  import type { Brain, EngineNotice } from "../core/types.js";
55
+ import { BrainError } from "./errors.js";
42
56
  import type { RequestRootOrigin } from "./request-route.js";
43
57
  /** A base URL's ROUTE KEY — the value the pairing law COMPARES, folded to WIRE equivalence: the
44
58
  * halves the wire itself treats as case-insensitive (scheme and host, RFC 9110) fold, the scheme's
@@ -119,25 +133,39 @@ export declare function resolveRequestRootOf(modelBaseUrl: string | undefined, c
119
133
  root: string;
120
134
  origin: RequestRootOrigin;
121
135
  };
122
- /** Whether a header bag carries an auth-bearing header (any case). */
136
+ /** Whether a header bag carries a credential header (any case) — the engine's one reading of which header carries a
137
+ * credential ({@link isCredentialHeaderName}: `Authorization`, `X-Api-Key`, Azure's `api-key`, `x-goog-api-key`,
138
+ * `Cookie`, …), the same reading the withheld-value intake keeps. */
123
139
  export declare function hasAuthCarrier(headers: Record<string, string> | undefined): boolean;
140
+ /** Test seam: empties the process's credential-origin table — every per-model credential is unbound again, as at a
141
+ * process start. Never called by production code (a binding lasts as long as the process; restarting rebinds). A test
142
+ * suite that sends one fixture credential to two local mock upstreams (two `listen(0)` ports are two origins) calls it
143
+ * between cases, or gives each case its own fixture credential. */
144
+ export declare function __resetCredentialOriginBindings(): void;
124
145
  /**
125
146
  * Resolve WHICH credential a request would carry, tagged with its source — the input half of the
126
- * adjudication. Precedence (most-specific wins):
127
- * 1. `options.apiKey` (per-call, from the per-model auth hook — presence, not truthiness: an
128
- * empty string is a present credential that fails closed at emit),
129
- * 2. an auth carrier in `options.headers` (per-call headers-borne). The bag MUST be per-model
130
- * sourced (the per-model auth hook's headers) — that provenance is what licenses the
131
- * "paired by construction" verdict below. A caller that folds run-wide headers into the
132
- * per-call options must strip auth carriers first (the harness merge does), or a deployment
147
+ * adjudication. One rank per SOURCE, most-specific wins; within a rank the apiKey knob and the rank's
148
+ * credential headers are ONE credential (they ride together and are bound together):
149
+ * 1. the per-model auth hook's answer — `options.apiKey` (presence, not truthiness: an empty string
150
+ * is a present credential that fails closed at emit — no key header, no fallback to the
151
+ * deployment's) and/or a credential header ({@link hasAuthCarrier}) in `options.headers` (a
152
+ * gateway's own `Helicone-Auth` beside the key, or the key spelled as a header — not both: an `apiKey`
153
+ * beside a header of the very name the lane writes the key in (`keyHeader`) is resolved all the same, with
154
+ * that conflict recorded beside it for the law to refuse). The bag MUST be
155
+ * per-model sourced (the hook's headers) — that provenance is what makes it a credential of THIS
156
+ * entry, bound to its own origin below. A caller that folds run-wide headers into the per-call
157
+ * options must strip credential headers first (the harness merge does), or a deployment
133
158
  * credential would ride here and be judged per-model,
134
- * 3. an auth carrier in `model.headers` (the ENTRY's own declared credential),
135
- * 4. the deployment config (`config.apiKey` and/or an auth carrier in `config.headers` —
136
- * deliberately ONE rank, not two: a config carrying BOTH forms is the documented gateway
137
- * dual-auth escape shape, and both ride exactly as they always have; the pairing gate judges
159
+ * 2. a credential header in `model.headers` (the ENTRY's own declared credential),
160
+ * 3. the deployment config (`config.apiKey` and/or a credential header in `config.headers` — the
161
+ * documented gateway dual-auth shape, both riding as they always have; the pairing gate judges
138
162
  * the pair as one deployment credential),
139
- * 5. absent.
140
- * 1–3 are `per-model`; 4 is `deployment-config`.
163
+ * 4. absent.
164
+ * 1–2 are `per-model`; 3 is `deployment-config`. A per-model credential that carries a secret leaves
165
+ * here with its identity beside it (a digest, held by this module and never a member of the returned
166
+ * object), which {@link adjudicateModelRoute} reads against the origin table and {@link admitRouteRequest}
167
+ * binds when its request is sent. Resolution does not judge the route: what it found — an identity, a key beside
168
+ * its own header — is the law's to rule on, in one verdict shape for every caller.
141
169
  */
142
170
  export declare function resolveRouteCredential(input: {
143
171
  optionsApiKey?: string;
@@ -145,10 +173,23 @@ export declare function resolveRouteCredential(input: {
145
173
  modelHeaders?: Record<string, string>;
146
174
  configApiKey?: string;
147
175
  configHeaders?: Record<string, string>;
176
+ /** The header the caller's lane writes a per-call `apiKey` in (`authorization`, `x-api-key`), when it has one. Per-call
177
+ * options that carry an `apiKey` and a header of that name (any case) are two values for one header: the credential
178
+ * is resolved with that conflict beside it, and the law refuses it (`route.credential_answer_conflict`) rather than
179
+ * let the key win. Optional like every input here: a caller that does not know its lane leaves it out, and then no
180
+ * conflict is recorded and none is refused. */
181
+ keyHeader?: string;
148
182
  }): RouteCredential;
149
183
  /**
150
- * The pairing law (module note above). Pure and deterministic: same inputs, same verdict — safe to
151
- * call both at a resolution seat (pre-flight) and again inside a brain's request build.
184
+ * The pairing law (module note above). A READ: it judges against the engine's credential-origin table and writes
185
+ * nothing, so a pre-flight, the read face ({@link routePairingStatus}) and any consumer may call it freely — same
186
+ * inputs and same table, same verdict. A per-model credential {@link resolveRouteCredential} resolved with a key beside
187
+ * its own header is refused first (`route.credential_answer_conflict`, both header names in the fix — wherever the
188
+ * request goes, since the request itself is contradictory); otherwise it is judged against the origin it is bound to
189
+ * (refused when that is another one); one not bound yet, or built by hand (no resolution facts — the published corpus
190
+ * is such input), is servable. Every refusal is a verdict: the law throws on garbage alone. The table is written by one
191
+ * seat only: a first-party brain's request gate ({@link admitRouteRequest}), when the engine commits the request to be
192
+ * sent. The verdict reports nothing either: the request gate's refusal is {@link routeRefusalError}.
152
193
  */
153
194
  export declare function adjudicateModelRoute(model: Pick<Model, "id" | "baseUrl">, credential: RouteCredential, config: RoutePairingConfig): RouteAdjudication;
154
195
  /** Render a refusal as the one-line detail text the brains put in their thrown error (the machine
@@ -156,43 +197,77 @@ export declare function adjudicateModelRoute(model: Pick<Model, "id" | "baseUrl"
156
197
  export declare function routeRefusalText(verdict: Extract<RouteAdjudication, {
157
198
  ok: false;
158
199
  }>): string;
200
+ /** The request gate's refusal — the ONE error a first-party brain throws when the pairing law refuses the request it is
201
+ * building ({@link admitRouteRequest}). A credential-origin refusal is also reported to the run it happens in (the run
202
+ * announces `route.credential_origin_mismatch`): here, at the request that is refused, and not in the law, whose other
203
+ * callers only advise (a pre-flight, the read face) and leave the outcome to the seat that asked. */
204
+ export declare function routeRefusalError(verdict: Extract<RouteAdjudication, {
205
+ ok: false;
206
+ }>): BrainError;
159
207
  /**
160
- * Enforce "the resolved credential is the request's ONLY credential" on an already-merged header
161
- * bag — the per-call-auth-replaces rule, generalized to the per-model sources:
162
- * - PER-MODEL apiKey-form winner → every auth carrier is stripped; the brain then emits the key in
163
- * its own wire posture, the single credential on the wire (the pre-existing per-call rule).
164
- * - PER-MODEL headers-borne winner (per-call or entry bag) → carriers are stripped and the winning
165
- * bag's own carriers re-asserted (an entry-declared credential must survive a same- or
166
- * variant-case deployment spelling that the plain merge order would have let win).
167
- * - DEPLOYMENT-CONFIG winner → untouched. The historical bytes stand exactly: a header-only config
168
- * keeps its own spelling, and a config carrying BOTH the apiKey knob and an auth header is the
169
- * documented gateway dual-auth escape shape — the knob's hard-lock write plus the surviving
170
- * header, both on the wire, as they always were.
171
- * - absent → untouched (no carriers exist).
208
+ * THE request gate of a first-party brain — the one seat that writes the credential-origin table. Judged while the
209
+ * request is built ({@link adjudicateModelRoute}, the same law the pre-flight and the read face read): a refusal is
210
+ * thrown here ({@link routeRefusalError}) before the body is built, and binds nothing — every refusal the law rules,
211
+ * a key beside its own header included, leaves as that one error (`<code>: <fix>`); the gate rules nothing of its
212
+ * own. An admitted request hands back its
213
+ * ADMISSION, which the brain runs from `SSERequest.onCommitted` — the engine's one point at which THIS request is the one
214
+ * that is sent: the law judged again and, admitted, the per-model credential bound to the request's origin, in ONE
215
+ * synchronous step; refused (the credential went out to another origin since the build — caller code runs while a body
216
+ * is built), the same refusal is thrown there, before anything is sent. A request built speculatively and declined (the
217
+ * context-overflow recovery's candidate), or one whose build fails after the gate, never reaches its admission and binds
218
+ * nothing. A credential is bound to the origin it was first SENT to, never to one it was only judged for. The request's
219
+ * halves (the entry's id and root, the brain's root and lane default) are read ONCE, here: both judgments and the binding
220
+ * are of the request this build is sending, whatever the caller's objects say by the time it is committed.
172
221
  */
173
- export declare function applyRouteCredentialHeaders(merged: Record<string, string>, credential: RouteCredential, bags: {
174
- model?: Record<string, string>;
175
- options?: Record<string, string>;
176
- }): void;
222
+ export declare function admitRouteRequest(model: Pick<Model, "id" | "baseUrl">, credential: RouteCredential, config: RoutePairingConfig): () => void;
177
223
  /**
178
- * The first-party brains' `adjudicateRoute` face, closed over the brain's construction config —
179
- * EXACTLY the resolution + judgment the brain's own request build runs (single source, two call
180
- * sites), so a pre-flight verdict and the request-time verdict can never disagree.
224
+ * Enforce "the resolved credential is the request's ONLY credential" on an already-merged header
225
+ * bag — one source's credential rides, the other sources' credential headers do not:
226
+ * - PER-MODEL winner → every credential header ({@link isCredentialHeaderName}) is stripped and the
227
+ * winning bag's own credential headers written back — exactly the ones {@link resolveRouteCredential}
228
+ * read and hashed ({@link ResolvedCredentialFacts}; the caller's bag is not read again): the hook's
229
+ * answer (`options`) or the entry (`model`). The hook's answer is one credential — beside its apiKey,
230
+ * which the brain then writes in its lane's key header (a request that also holds a header of that name
231
+ * never gets here in a first-party brain: its request gate refused it first, `route.credential_answer_conflict`,
232
+ * so the lock supersedes nothing the answer carried), its other credential headers ride (a gateway's `Helicone-Auth`); an
233
+ * entry's credential survives a same- or variant-case deployment spelling that the plain merge order
234
+ * would have let win. A credential header from any OTHER bag (the entry's under a hook answer, the
235
+ * deployment's under either) never rides beside a per-model credential. Whether a header is stripped
236
+ * is decided by its SOURCE, not its name.
237
+ * - DEPLOYMENT-CONFIG winner → untouched. A header-only config keeps its own spelling, and a config
238
+ * carrying BOTH the apiKey knob and an auth header is the documented gateway dual-auth shape — the
239
+ * knob's lock write plus the surviving header, both on the wire.
240
+ * - absent → untouched (no carriers exist).
181
241
  */
242
+ export declare function applyRouteCredentialHeaders(merged: Record<string, string>, credential: RouteCredential): void;
182
243
  /** A brain's construction config, received ONCE by the engine's receipt law for caller objects
183
244
  * ({@link receiveOwnProperties}: a plain object, every own member read once): the request gate and the pre-flight
184
245
  * judge both read this frozen copy, so a config object mutated after construction moves neither and the two cannot
185
246
  * disagree about the root a credential is paired with. The header bag is a caller object too: it is received by the
186
- * same law from that one reading into a frozen bag of the copy's own. */
247
+ * same law from that one reading into a frozen bag of the copy's own. The copy's credential — `apiKey` and the
248
+ * credential headers — is a credential the engine now holds: no child spawned on this host inherits a variable whose
249
+ * value equals it (`holdOwnCredentialValues`). */
187
250
  export declare function brainConfigSnapshot<T extends {
251
+ apiKey?: string;
188
252
  headers?: Record<string, string>;
189
253
  }>(given: T, subject: string): Readonly<T>;
254
+ /**
255
+ * The first-party brains' `adjudicateRoute` face, closed over the brain's construction config —
256
+ * EXACTLY the resolution + judgment the brain's own request build runs (single source, two call
257
+ * sites), so a pre-flight verdict and the request-time verdict agree on the same inputs. A refusal is
258
+ * always a verdict here, never a throw. The pre-flight sees the
259
+ * per-model auth it is handed, nothing later: a header a provider-request hook adds afterwards reaches
260
+ * the request build alone, which judges it by the same law.
261
+ */
190
262
  export declare function createBrainRouteJudge(config: {
191
263
  apiKey?: string;
192
264
  baseUrl?: string;
193
265
  headers?: Record<string, string>;
194
266
  /** The lane's own default endpoint (the brain's constant), when it has one. */
195
267
  laneDefault?: string;
268
+ /** The header the lane writes a per-call apiKey in ({@link resolveRouteCredential}'s `keyHeader`): the brain's own
269
+ * constant, so the judge refuses the requests its request build refuses. */
270
+ keyHeader?: string;
196
271
  }): (model: Model, perModelAuth?: {
197
272
  apiKey?: string;
198
273
  headers?: Record<string, string>;
@@ -203,7 +278,7 @@ export declare function createBrainRouteJudge(config: {
203
278
  * `judge` is a brain's optional `adjudicateRoute` face; absent ⇒ `"unknown"` (a custom brain core
204
279
  * cannot see into — never guessed at).
205
280
  */
206
- export type RoutePairingStatus = `ok:${RoutePairingPosture}` | `broken:${"credential_mismatch" | "credential_missing"}` | "unknown";
281
+ export type RoutePairingStatus = `ok:${RoutePairingPosture}` | `broken:${"credential_mismatch" | "credential_missing" | "credential_origin_mismatch" | "credential_answer_conflict"}` | "unknown";
207
282
  export declare function routePairingStatus(brain: {
208
283
  adjudicateRoute?: (model: Model, perModelAuth?: {
209
284
  apiKey?: string;
@@ -294,8 +369,3 @@ export declare function unroutableSeatNotice(args: {
294
369
  sessionId?: string;
295
370
  runId?: string;
296
371
  }): EngineNotice;
297
- /** The auth carriers of a header bag, canonicalized (case-fold name → value, sorted) — the
298
- * Model-visible credential fingerprint used by catalog-change disclosure (a same-name entry whose
299
- * URL moved while this fingerprint did not is the "address changed, credential reference did not"
300
- * shape worth announcing). */
301
- export declare function authCarrierFingerprint(headers: Record<string, string> | undefined): string;