@sema-agent/core 7.51.2 → 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 (363) hide show
  1. package/CHANGELOG.md +504 -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.js +6 -4
  12. package/dist/agents/tool-filter.d.ts +40 -13
  13. package/dist/agents/tool-filter.js +28 -14
  14. package/dist/bench/metrics.d.ts +1 -0
  15. package/dist/brain/anthropic.js +16 -12
  16. package/dist/brain/model-presets.d.ts +4 -5
  17. package/dist/brain/model-presets.js +4 -4
  18. package/dist/brain/open-responses.js +16 -12
  19. package/dist/brain/openai.js +16 -12
  20. package/dist/brain/request-params.d.ts +12 -33
  21. package/dist/brain/request-params.js +5 -2
  22. package/dist/brain/request-route.d.ts +9 -8
  23. package/dist/brain/request-route.js +1 -0
  24. package/dist/brain/route-adjudicator.d.ts +117 -47
  25. package/dist/brain/route-adjudicator.js +171 -42
  26. package/dist/brain/route-conformance.js +2 -2
  27. package/dist/brain/status-sink.d.ts +18 -0
  28. package/dist/brain/status-sink.js +7 -0
  29. package/dist/core/allow-entry.d.ts +40 -8
  30. package/dist/core/allow-entry.js +23 -16
  31. package/dist/core/auto-compaction.d.ts +23 -20
  32. package/dist/core/auto-compaction.js +37 -16
  33. package/dist/core/auto-promote.d.ts +2 -1
  34. package/dist/core/auto-promote.js +2 -2
  35. package/dist/core/caller-tool-name.d.ts +9 -0
  36. package/dist/core/caller-tool-name.js +13 -0
  37. package/dist/core/checkpoint-requirements.d.ts +7 -1
  38. package/dist/core/checkpoint-requirements.js +2 -0
  39. package/dist/core/checkpoint-store.d.ts +36 -12
  40. package/dist/core/checkpoint-store.js +1 -1
  41. package/dist/core/child-clamp-seats.d.ts +6 -0
  42. package/dist/core/child-clamp-seats.js +2 -1
  43. package/dist/core/credential-vocabulary.d.ts +33 -8
  44. package/dist/core/credential-vocabulary.js +32 -7
  45. package/dist/core/decision-receipt.d.ts +2 -2
  46. package/dist/core/delegation-frames.d.ts +9 -2
  47. package/dist/core/delegation-name.d.ts +18 -0
  48. package/dist/core/delegation-name.js +47 -0
  49. package/dist/core/engine-capabilities.d.ts +8 -1
  50. package/dist/core/engine-capabilities.js +1 -1
  51. package/dist/core/engine-notice.d.ts +36 -19
  52. package/dist/core/frontmatter-fence.d.ts +10 -0
  53. package/dist/core/frontmatter-fence.js +6 -0
  54. package/dist/core/gate-fold.js +5 -23
  55. package/dist/core/gate-lanes.js +356 -270
  56. package/dist/core/governance-codes.d.ts +9 -4
  57. package/dist/core/governance-codes.js +9 -3
  58. package/dist/core/hooks.d.ts +276 -734
  59. package/dist/core/hooks.js +97 -192
  60. package/dist/{tools/fs → core}/image-admission.d.ts +21 -3
  61. package/dist/{tools/fs → core}/image-admission.js +24 -6
  62. package/dist/core/mcp-connection-pool.d.ts +4 -1
  63. package/dist/core/mcp-connection-pool.js +7 -2
  64. package/dist/core/mcp-probe.d.ts +12 -9
  65. package/dist/core/mcp-probe.js +2 -2
  66. package/dist/core/mcp-server-spec.d.ts +16 -0
  67. package/dist/core/mcp.d.ts +11 -7
  68. package/dist/core/mcp.js +11 -5
  69. package/dist/core/memory-engine/data-plane.js +5 -1
  70. package/dist/core/memory-engine/distiller.js +16 -2
  71. package/dist/core/memory-engine/engine.d.ts +8 -11
  72. package/dist/core/memory-engine/engine.js +9 -5
  73. package/dist/core/memory-engine/frontmatter.d.ts +6 -0
  74. package/dist/core/memory-engine/frontmatter.js +19 -16
  75. package/dist/core/memory.d.ts +2 -2
  76. package/dist/core/mode-policy.d.ts +4 -0
  77. package/dist/core/mode-policy.js +7 -5
  78. package/dist/core/model-seat.d.ts +4 -1
  79. package/dist/core/mods/dollar.d.ts +109 -0
  80. package/dist/core/mods/dollar.js +154 -0
  81. package/dist/core/mods/host.d.ts +23 -0
  82. package/dist/core/mods/host.js +21 -0
  83. package/dist/core/mods/kernel/budget.d.ts +43 -0
  84. package/dist/core/mods/kernel/budget.js +83 -0
  85. package/dist/core/mods/kernel/dispatch.d.ts +100 -0
  86. package/dist/core/mods/kernel/dispatch.js +701 -0
  87. package/dist/core/mods/kernel/events.d.ts +242 -0
  88. package/dist/core/mods/kernel/events.js +210 -0
  89. package/dist/core/mods/kernel/index.d.ts +6 -0
  90. package/dist/core/mods/kernel/index.js +5 -0
  91. package/dist/core/mods/kernel/receive.d.ts +65 -0
  92. package/dist/core/mods/kernel/receive.js +123 -0
  93. package/dist/core/mods/kernel/registration.d.ts +131 -0
  94. package/dist/core/mods/kernel/registration.js +102 -0
  95. package/dist/core/mods/kernel/site-rules.d.ts +84 -0
  96. package/dist/core/mods/kernel/site-rules.js +104 -0
  97. package/dist/core/mods/stations.d.ts +350 -0
  98. package/dist/core/mods/stations.js +444 -0
  99. package/dist/core/own-property-receipt.js +11 -23
  100. package/dist/core/parent-spec-seat.d.ts +18 -7
  101. package/dist/core/parent-spec-seat.js +6 -2
  102. package/dist/core/path-identity.js +2 -1
  103. package/dist/core/permission-rule-syntax.d.ts +15 -0
  104. package/dist/core/permission-rule-syntax.js +24 -16
  105. package/dist/core/permission-rules.d.ts +7 -1
  106. package/dist/core/permission-rules.js +26 -15
  107. package/dist/core/prompt-section-seat.d.ts +15 -27
  108. package/dist/core/prompt-section-seat.js +73 -40
  109. package/dist/core/protocol-http-auth.d.ts +3 -1
  110. package/dist/core/protocol-http-auth.js +2 -0
  111. package/dist/core/reminder-mint.d.ts +2 -2
  112. package/dist/core/remote-env.d.ts +24 -12
  113. package/dist/core/remote-env.js +1 -0
  114. package/dist/core/resume-startup-cause.d.ts +16 -1
  115. package/dist/core/resume-startup-cause.js +4 -1
  116. package/dist/core/retired-keys.d.ts +4 -0
  117. package/dist/core/retired-keys.js +3 -0
  118. package/dist/core/runner/announce-once-ledger.d.ts +31 -1
  119. package/dist/core/runner/announce-once-ledger.js +22 -0
  120. package/dist/core/runner/assemble-result.d.ts +9 -0
  121. package/dist/core/runner/assemble-result.js +3 -1
  122. package/dist/core/runner/call-hook-stations.d.ts +23 -27
  123. package/dist/core/runner/call-hook-stations.js +58 -42
  124. package/dist/core/runner/caller-stop-gate.d.ts +25 -0
  125. package/dist/core/runner/caller-stop-gate.js +7 -0
  126. package/dist/core/runner/compaction-seams.d.ts +8 -22
  127. package/dist/core/runner/compaction-seams.js +28 -53
  128. package/dist/core/runner/construction-doors.js +11 -1
  129. package/dist/core/runner/contracts.d.ts +85 -83
  130. package/dist/core/runner/credential-origin-seat.d.ts +2 -0
  131. package/dist/core/runner/credential-origin-seat.js +19 -0
  132. package/dist/core/runner/extra-body-dropped-seat.d.ts +2 -6
  133. package/dist/core/runner/gate-exit.d.ts +47 -36
  134. package/dist/core/runner/gate-exit.js +8 -2
  135. package/dist/core/runner/image.js +1 -2
  136. package/dist/core/runner/inline-image.js +1 -1
  137. package/dist/core/runner/notice-seat-input.d.ts +11 -0
  138. package/dist/core/runner/notice-seat-input.js +1 -0
  139. package/dist/core/runner/person-path-rows.js +1 -0
  140. package/dist/core/runner/prepare-acquire-reconcile.js +5 -1
  141. package/dist/core/runner/prepare-ask-lane.d.ts +5 -7
  142. package/dist/core/runner/prepare-ask-lane.js +6 -15
  143. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
  144. package/dist/core/runner/prepare-caps-and-workflow.js +25 -8
  145. package/dist/core/runner/prepare-config-doors.d.ts +5 -0
  146. package/dist/core/runner/prepare-config-doors.js +9 -0
  147. package/dist/core/runner/prepare-defer-classify.js +3 -4
  148. package/dist/core/runner/prepare-gate-stations.d.ts +8 -11
  149. package/dist/core/runner/prepare-gate-stations.js +11 -10
  150. package/dist/core/runner/prepare-inherited-gate.d.ts +8 -12
  151. package/dist/core/runner/prepare-inherited-gate.js +8 -21
  152. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  153. package/dist/core/runner/prepare-park-ask.js +10 -14
  154. package/dist/core/runner/prepare-policy-chain.d.ts +17 -15
  155. package/dist/core/runner/prepare-policy-chain.js +95 -100
  156. package/dist/core/runner/prepare-project-context.js +7 -10
  157. package/dist/core/runner/prepare-prompt-assembly.d.ts +45 -40
  158. package/dist/core/runner/prepare-prompt-assembly.js +134 -101
  159. package/dist/core/runner/prepare-prompt-inputs.d.ts +18 -19
  160. package/dist/core/runner/prepare-prompt-inputs.js +5 -7
  161. package/dist/core/runner/prepare-protocol-tools.d.ts +6 -4
  162. package/dist/core/runner/prepare-protocol-tools.js +2 -2
  163. package/dist/core/runner/prepare-safety-scan.js +6 -10
  164. package/dist/core/runner/prepare-suspend-saga.d.ts +6 -0
  165. package/dist/core/runner/prepare-suspend-saga.js +7 -5
  166. package/dist/core/runner/prepare-task.js +33 -15
  167. package/dist/core/runner/prepare-turn-wiring.d.ts +13 -15
  168. package/dist/core/runner/prepare-turn-wiring.js +29 -27
  169. package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -6
  170. package/dist/core/runner/prepare-wiring-manifest.js +7 -7
  171. package/dist/core/runner/resume-apply.d.ts +5 -2
  172. package/dist/core/runner/resume-apply.js +24 -42
  173. package/dist/core/runner/resume-checkpoint-screen.d.ts +1 -0
  174. package/dist/core/runner/resume-checkpoint-screen.js +9 -1
  175. package/dist/core/runner/resume-preflight.js +38 -16
  176. package/dist/core/runner/retired-runner-deps.js +3 -1
  177. package/dist/core/runner/run-brain-sinks.d.ts +3 -2
  178. package/dist/core/runner/run-brain-sinks.js +5 -1
  179. package/dist/core/runner/run-compaction-machinery.d.ts +7 -1
  180. package/dist/core/runner/run-compaction-machinery.js +7 -33
  181. package/dist/core/runner/run-harness-handlers.js +4 -2
  182. package/dist/core/runner/run-identity-wiring.d.ts +3 -1
  183. package/dist/core/runner/run-identity-wiring.js +3 -2
  184. package/dist/core/runner/run-leg.js +62 -84
  185. package/dist/core/runner/run-notification-lane.js +1 -0
  186. package/dist/core/runner/run-recovery-lanes.d.ts +3 -0
  187. package/dist/core/runner/run-recovery-lanes.js +11 -10
  188. package/dist/core/runner/run-settle-and-teardown.js +9 -28
  189. package/dist/core/runner/run-stop-and-final-verify.js +18 -25
  190. package/dist/core/runner/run-terminal-adoption.d.ts +3 -0
  191. package/dist/core/runner/run-terminal-adoption.js +33 -22
  192. package/dist/core/runner/run-tool-mount-facts.js +5 -3
  193. package/dist/core/runner/run-turn-boundary.js +23 -34
  194. package/dist/core/runner/runtask.d.ts +6 -7
  195. package/dist/core/runner/runtask.js +14 -72
  196. package/dist/core/runner/session-rule-policy.d.ts +5 -5
  197. package/dist/core/runner/session-rule-policy.js +13 -8
  198. package/dist/core/runner/side-call-account.d.ts +68 -0
  199. package/dist/core/runner/side-call-account.js +61 -0
  200. package/dist/core/runner/stream-halt-verbs.js +12 -17
  201. package/dist/core/runner/stream-lifecycle-verbs.js +8 -5
  202. package/dist/core/runner/stream-steer-verb.d.ts +1 -1
  203. package/dist/core/runner/stream-steer-verb.js +20 -38
  204. package/dist/core/runner/sub-agent-deny-lists.d.ts +121 -22
  205. package/dist/core/runner/sub-agent-deny-lists.js +228 -71
  206. package/dist/core/runner/tool-end-body.d.ts +5 -6
  207. package/dist/core/runner/tool-end-body.js +6 -4
  208. package/dist/core/runner/tool-not-executed.d.ts +15 -0
  209. package/dist/core/runner/tool-not-executed.js +30 -0
  210. package/dist/core/runner/tool-occurrence.d.ts +8 -7
  211. package/dist/core/runner/tool-occurrence.js +5 -5
  212. package/dist/core/runner/tool-result-media-seat.d.ts +1 -1
  213. package/dist/core/runner-deps.d.ts +20 -28
  214. package/dist/core/safety-merge-corpus.js +18 -2
  215. package/dist/core/secret-env.d.ts +88 -20
  216. package/dist/core/secret-env.js +192 -38
  217. package/dist/core/session-policy-store.d.ts +52 -16
  218. package/dist/core/session-policy-store.js +118 -28
  219. package/dist/core/session-reconcile.d.ts +4 -0
  220. package/dist/core/session-reconcile.js +2 -1
  221. package/dist/core/shell-read-silence.js +3 -32
  222. package/dist/core/side-query.d.ts +5 -3
  223. package/dist/core/skill-grant.d.ts +3 -2
  224. package/dist/core/skill-grant.js +17 -9
  225. package/dist/core/skills-directory.d.ts +31 -11
  226. package/dist/core/skills-directory.js +655 -104
  227. package/dist/core/task-event.d.ts +13 -3
  228. package/dist/core/task-result.d.ts +8 -0
  229. package/dist/core/task-spec.d.ts +20 -19
  230. package/dist/core/task-stream.d.ts +14 -4
  231. package/dist/core/tighten-task-spec.d.ts +4 -3
  232. package/dist/core/tighten-task-spec.js +2 -4
  233. package/dist/core/tool-check-reading.d.ts +74 -0
  234. package/dist/core/tool-check-reading.js +80 -0
  235. package/dist/core/tool-policy.d.ts +59 -18
  236. package/dist/core/tool-policy.js +77 -12
  237. package/dist/core/tool-registry.d.ts +20 -0
  238. package/dist/core/tool-registry.js +4 -0
  239. package/dist/core/tool-spec.d.ts +10 -6
  240. package/dist/core/tools.js +2 -0
  241. package/dist/core/trace.d.ts +51 -32
  242. package/dist/core/unc-spelling.d.ts +10 -0
  243. package/dist/core/unc-spelling.js +3 -0
  244. package/dist/core/untrusted-egress.d.ts +8 -0
  245. package/dist/core/untrusted-egress.js +4 -1
  246. package/dist/core/user-attachment.d.ts +12 -2
  247. package/dist/core/user-attachment.js +17 -2
  248. package/dist/core/wiring-manifest.d.ts +14 -13
  249. package/dist/core/wiring-manifest.js +2 -2
  250. package/dist/engine/compaction/utils.js +1 -2
  251. package/dist/engine/execution-env/node-execution-env.d.ts +4 -3
  252. package/dist/engine/execution-env/symlink-chain.js +1 -1
  253. package/dist/engine/harness/agent-harness.d.ts +1 -1
  254. package/dist/engine/harness/agent-harness.js +3 -3
  255. package/dist/engine/harness/types.d.ts +17 -16
  256. package/dist/engine/harness/types.js +7 -1
  257. package/dist/engine/llm/types.d.ts +12 -7
  258. package/dist/engine/loop/agent-loop.js +1 -1
  259. package/dist/engine/loop/types.d.ts +1 -1
  260. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  261. package/dist/engine/session/import-validate.js +3 -10
  262. package/dist/engine/session/session.d.ts +4 -4
  263. package/dist/engine/session/session.js +4 -4
  264. package/dist/index.d.ts +20 -16
  265. package/dist/index.js +13 -11
  266. package/dist/internal/harness.d.ts +1 -1
  267. package/dist/internal/harness.js +1 -1
  268. package/dist/orchestration/run-spec.d.ts +4 -6
  269. package/dist/orchestration/run-spec.js +39 -63
  270. package/dist/orchestration/run-workflow-tool.js +1 -2
  271. package/dist/orchestration/workflow.d.ts +11 -3
  272. package/dist/orchestration/workflow.js +11 -2
  273. package/dist/prompt-assembly/artifact-store.d.ts +37 -56
  274. package/dist/prompt-assembly/artifact-store.js +31 -49
  275. package/dist/prompt-assembly/artifact.d.ts +89 -76
  276. package/dist/prompt-assembly/artifact.js +167 -146
  277. package/dist/prompt-assembly/assemble.d.ts +34 -29
  278. package/dist/prompt-assembly/assemble.js +33 -220
  279. package/dist/prompt-assembly/bands.d.ts +20 -0
  280. package/dist/prompt-assembly/bands.js +18 -0
  281. package/dist/prompt-assembly/catalog/constitution.d.ts +23 -0
  282. package/dist/prompt-assembly/catalog/constitution.js +69 -0
  283. package/dist/prompt-assembly/catalog/core-behavior.d.ts +9 -0
  284. package/dist/prompt-assembly/catalog/core-behavior.js +85 -0
  285. package/dist/prompt-assembly/catalog/index.d.ts +11 -0
  286. package/dist/prompt-assembly/catalog/index.js +59 -0
  287. package/dist/prompt-assembly/catalog/modes.d.ts +15 -0
  288. package/dist/prompt-assembly/catalog/modes.js +137 -0
  289. package/dist/prompt-assembly/catalog/role.d.ts +10 -0
  290. package/dist/prompt-assembly/catalog/role.js +157 -0
  291. package/dist/prompt-assembly/catalog/schema.d.ts +124 -0
  292. package/dist/prompt-assembly/catalog/schema.js +25 -0
  293. package/dist/prompt-assembly/catalog/sections.d.ts +9 -0
  294. package/dist/prompt-assembly/catalog/sections.js +98 -0
  295. package/dist/prompt-assembly/catalog/team.d.ts +4 -0
  296. package/dist/prompt-assembly/catalog/team.js +258 -0
  297. package/dist/prompt-assembly/composer.d.ts +24 -28
  298. package/dist/prompt-assembly/composer.js +124 -46
  299. package/dist/prompt-assembly/epoch.d.ts +89 -79
  300. package/dist/prompt-assembly/epoch.js +115 -142
  301. package/dist/prompt-assembly/explain.d.ts +58 -34
  302. package/dist/prompt-assembly/explain.js +21 -24
  303. package/dist/prompt-assembly/overlay.d.ts +127 -0
  304. package/dist/prompt-assembly/overlay.js +301 -0
  305. package/dist/prompt-assembly/renderers.d.ts +21 -0
  306. package/dist/prompt-assembly/renderers.js +39 -0
  307. package/dist/prompt-assembly/section-usage.d.ts +3 -3
  308. package/dist/prompt-assembly/section-usage.js +1 -1
  309. package/dist/prompt-assembly/select.d.ts +56 -0
  310. package/dist/prompt-assembly/select.js +69 -0
  311. package/dist/prompt-assembly/sugar.d.ts +31 -0
  312. package/dist/prompt-assembly/sugar.js +20 -0
  313. package/dist/prompt-assembly/turn-snapshot.d.ts +3 -2
  314. package/dist/prompt-assembly/types.d.ts +45 -148
  315. package/dist/prompts/coordinator.d.ts +2 -21
  316. package/dist/prompts/coordinator.js +3 -255
  317. package/dist/prompts/cowork.d.ts +27 -16
  318. package/dist/prompts/cowork.js +2 -23
  319. package/dist/prompts/default.d.ts +38 -500
  320. package/dist/prompts/default.js +20 -285
  321. package/dist/prompts/supervisor.d.ts +6 -71
  322. package/dist/prompts/supervisor.js +6 -117
  323. package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -5
  324. package/dist/tools/fs/dir-entries.d.ts +4 -2
  325. package/dist/tools/fs/dir-entries.js +16 -4
  326. package/dist/tools/fs/fs-bash.d.ts +1 -1
  327. package/dist/tools/fs/fs-bash.js +22 -11
  328. package/dist/tools/fs/fs-pdf.js +17 -13
  329. package/dist/tools/fs/fs-read.js +4 -3
  330. package/dist/tools/fs/fs-shared.d.ts +1 -10
  331. package/dist/tools/fs/fs-shared.js +3 -4
  332. package/dist/tools/fs/fs-write.js +2 -1
  333. package/dist/tools/fs/notebook.js +1 -1
  334. package/dist/tools/fs/pdf.d.ts +20 -10
  335. package/dist/tools/fs/pdf.js +19 -10
  336. package/dist/tools/fs/program-read-model.d.ts +68 -8
  337. package/dist/tools/fs/program-read-model.js +112 -33
  338. package/dist/tools/fs/program-read-model.rows.js +30 -10
  339. package/dist/tools/fs/read-deny-home.d.ts +25 -9
  340. package/dist/tools/fs/read-deny-home.js +28 -27
  341. package/dist/tools/fs/read-deny.d.ts +41 -11
  342. package/dist/tools/fs/read-deny.js +51 -5
  343. package/dist/tools/fs/safety.d.ts +4 -26
  344. package/dist/tools/fs/safety.js +10 -32
  345. package/dist/tools/fs/search.js +15 -10
  346. package/dist/tools/fs/shell-read-argv.d.ts +3 -2
  347. package/dist/tools/fs/shell-read-argv.js +118 -38
  348. package/dist/tools/fs/shell-read-establish.d.ts +5 -5
  349. package/dist/tools/fs/shell-read-establish.js +11 -61
  350. package/dist/tools/fs/shell-read-identity.js +6 -3
  351. package/dist/tools/fs/shell-read-judge.d.ts +13 -15
  352. package/dist/tools/fs/shell-read-judge.js +18 -4
  353. package/dist/tools/fs/shell-read-plan.d.ts +2 -2
  354. package/dist/tools/web.js +1 -1
  355. package/package.json +13 -1
  356. package/test/export-surface.snapshot.json +242 -99
  357. package/wire-changes/core@7.52.0.json +5859 -0
  358. package/dist/core/hook-seats.d.ts +0 -66
  359. package/dist/core/hook-seats.js +0 -37
  360. package/dist/prompt-assembly/packs/sema-default.d.ts +0 -2
  361. package/dist/prompt-assembly/packs/sema-default.js +0 -147
  362. package/dist/prompts/simple-sections.d.ts +0 -58
  363. package/dist/prompts/simple-sections.js +0 -43
@@ -1,11 +1,9 @@
1
- import { decisionText, describeThrown, receiveHookResult, isEngineRefusal, unaskedSeatAnswer } from "./tool-policy.js";
2
- import { asThrownError } from "./thrown-value.js";
3
- import { createSettlementLedger, engineFailClosedExit, hookDenyExit } from "./runner/gate-exit.js";
4
- import { traceHookCrash } from "./gate-crash-trace.js";
1
+ import { abortedSeatDeny, decisionText, describeThrown, isEngineRefusal, ownArgs, refuseOutOfContractDecision, unaskedSeatAnswer } from "./tool-policy.js";
2
+ import { createSettlementLedger, engineFailClosedExit, hookDenyExit, unownedArgsExit } from "./runner/gate-exit.js";
3
+ import { classicEventBase, dispatchClassic, ENGINE_CORE_ANSWER, engineCore, failedHandlerClause, judgedInputOf, observePermissionDenied, recordScreeningChain } from "./mods/stations.js";
5
4
  import { runGateFold } from "./gate-fold.js";
6
5
  import { runGateLanes } from "./gate-lanes.js";
7
6
  export { normalizeOrgGateVerdict, normalizePersistedRuleHit } from "./rule-lane-reads.js";
8
- export { cloneObserverInput } from "./runner/gate-exit.js";
9
7
  import { brandPolicyAskClass } from "./ask-class.js";
10
8
  import { isAskOrigin, screenAskClassMembers } from "./ask-origin.js";
11
9
  import { isAutoModeFallbackCause } from "./auto-mode.js";
@@ -13,92 +11,6 @@ import { inlineUntrusted } from "./untrusted-text.js";
13
11
  export { formatHookFeedback } from "./reminder-mint.js";
14
12
  import { createSafeNotifier } from "./safe-notify.js";
15
13
  import { wireOf } from "./tool-name-readings.js";
16
- export const DEFAULT_HOOK_TIMEOUT_MS = 600_000;
17
- export const MAX_HOOK_TIMEOUT_MS = 2_147_483_647;
18
- const POST_ABORT_GRACE_MS = 1_000;
19
- export function postAbortGraceMs(remainingMs) {
20
- return Math.max(0, Math.min(POST_ABORT_GRACE_MS, remainingMs));
21
- }
22
- const badHookTimeoutReported = new WeakSet();
23
- export function resolveHookTimeoutMs(supplied, report, owner) {
24
- if (supplied === undefined)
25
- return DEFAULT_HOOK_TIMEOUT_MS;
26
- if (Number.isFinite(supplied) && supplied >= 0 && supplied <= MAX_HOOK_TIMEOUT_MS)
27
- return supplied;
28
- if (report !== undefined && !(owner !== undefined && badHookTimeoutReported.has(owner))) {
29
- if (owner !== undefined)
30
- badHookTimeoutReported.add(owner);
31
- try {
32
- report(new Error(`Hooks.timeoutMs must be a non-negative finite number no greater than ${MAX_HOOK_TIMEOUT_MS} (got ${String(supplied)}) — ` +
33
- `every hook seat falls back to the ${DEFAULT_HOOK_TIMEOUT_MS}ms default`));
34
- }
35
- catch {
36
- }
37
- }
38
- return DEFAULT_HOOK_TIMEOUT_MS;
39
- }
40
- export async function runHookSeat(seat, bound, call) {
41
- void seat;
42
- const timeoutMs = resolveHookTimeoutMs(bound.timeoutMs, bound.onBadTimeout, bound.owner);
43
- const taskSignal = bound.signal;
44
- const controller = new AbortController();
45
- const pending = Promise.resolve(call(controller.signal));
46
- const startedAt = performance.now();
47
- return await new Promise((resolve, reject) => {
48
- let settled = false;
49
- let listening = false;
50
- let timer;
51
- const clearTimer = () => {
52
- if (timer !== undefined)
53
- clearTimeout(timer);
54
- timer = undefined;
55
- };
56
- const finish = (act) => {
57
- if (settled)
58
- return;
59
- settled = true;
60
- clearTimer();
61
- if (listening)
62
- taskSignal?.removeEventListener("abort", onAbort);
63
- act();
64
- };
65
- const expire = (cause) => finish(() => {
66
- void pending.then(undefined, () => {
67
- });
68
- resolve({ expired: true, cause });
69
- queueMicrotask(() => {
70
- try {
71
- controller.abort();
72
- }
73
- catch {
74
- }
75
- });
76
- });
77
- const collapseToGrace = () => {
78
- if (settled)
79
- return;
80
- clearTimer();
81
- timer = setTimeout(() => expire("aborted"), postAbortGraceMs(timeoutMs - (performance.now() - startedAt)));
82
- };
83
- const onAbort = () => (bound.abortEnds === true ? expire("aborted") : collapseToGrace());
84
- timer = setTimeout(() => expire("timeout"), timeoutMs);
85
- if (taskSignal !== undefined) {
86
- if (taskSignal.aborted) {
87
- onAbort();
88
- }
89
- else {
90
- taskSignal.addEventListener("abort", onAbort, { once: true });
91
- listening = true;
92
- }
93
- }
94
- pending.then((value) => finish(() => resolve({ expired: false, value })), (err) => finish(() => reject(asThrownError(err))));
95
- });
96
- }
97
- export function hookSeatExpiredError(seat, timeoutMs, cause, consequence) {
98
- return new Error(cause === "timeout"
99
- ? `the deployment's ${seat} hook did not answer within its ${timeoutMs}ms bound (Hooks.timeoutMs) — ${consequence}`
100
- : `the deployment's ${seat} hook had not answered when the task was cancelled — ${consequence}`);
101
- }
102
14
  const reflectApply = Reflect.apply;
103
15
  const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
104
16
  const getPrototypeOf = Object.getPrototypeOf;
@@ -174,54 +86,92 @@ export function mintHookInvocationIdentity(facts) {
174
86
  ...(facts.parentToolCallId !== undefined ? { parentToolCallId: facts.parentToolCallId } : {}),
175
87
  }));
176
88
  }
177
- function preToolUseCrashReason(subject, err) {
178
- const cause = inlineUntrusted(describeThrown(err), 200);
179
- return (`a PreToolUse hook crashed while screening ${subject}; the call was NOT executed (fail-closed). ` +
180
- `This is a failure of the deployment's hook, NOT of the tool itself — an identical retry reaches the ` +
181
- `same hook and fails the same way. Hook error: ${cause || "(no message)"}`);
89
+ function screenPreToolUseResult(r) {
90
+ if (r === undefined || r === null || r === false)
91
+ return r;
92
+ return refuseOutOfContractDecision(r, { reasonIsNonInput: true });
182
93
  }
183
- function preToolUseUnansweredReason(subject, timeoutMs, cause) {
184
- return cause === "timeout"
185
- ? `a PreToolUse hook did not answer within its ${timeoutMs}ms bound while screening ${subject}; the call was ` +
186
- `NOT executed (fail-closed). This is a failure of the deployment's hook, NOT of the tool itself — the ` +
187
- `screening face never returned a verdict, so nothing was decided about this call either way.`
188
- : `the task was cancelled while a PreToolUse hook was still screening ${subject}; the call was NOT executed ` +
189
- `(fail-closed). No verdict was reached — this is the run ending, not the tool failing.`;
94
+ function preToolUseAnswerViolation(answer) {
95
+ const r = screenPreToolUseResult(answer);
96
+ if (r === undefined || r === null || r === false || !isEngineRefusal(r))
97
+ return undefined;
98
+ return typeof r.message === "string" ? r.message : "the PreToolUse answer was refused by the engine";
190
99
  }
191
- function screenPreToolUseResult(r) {
192
- if (r === undefined)
100
+ function preToolUseRefusedReason(subject, failed) {
101
+ if (failed === undefined) {
102
+ return `the task was cancelled while a PreToolUse hook was still screening ${subject}; the call was NOT executed ` + `(fail-closed). No verdict was reached — this is the run ending, not the tool failing.`;
103
+ }
104
+ const tail = failed.cause === "threw"
105
+ ? ` This is a failure of the deployment's hook, NOT of the tool itself — an identical retry reaches the same hook and fails the same way.`
106
+ : failed.cause === "timeout"
107
+ ? ` This is a failure of the deployment's hook, NOT of the tool itself — the screening face never returned a verdict, so nothing was decided about this call either way.`
108
+ : "";
109
+ return `a PreToolUse hook ${failedHandlerClause(failed)} while screening ${subject}; the call was NOT executed (fail-closed).${tail}`;
110
+ }
111
+ export function createPreToolUseScreening(input) {
112
+ const bus = input.modBus;
113
+ if (bus === undefined || !bus.hasHandlers("classic.PreToolUse"))
193
114
  return undefined;
194
- return receiveHookResult(r);
115
+ const { toolCallId, toolName } = input.event;
116
+ const screenedName = wireOf("gate.preToolUse", input.event);
117
+ const eventOf = (args) => {
118
+ const cwd = input.trackedCwd?.();
119
+ return {
120
+ ...classicEventBase(input.identity, input.observedPermissionMode?.()),
121
+ tool: screenedName,
122
+ tool_use_id: toolCallId,
123
+ input: judgedInputOf(args),
124
+ ...(typeof cwd === "string" ? { cwd } : {}),
125
+ ...(input.hookEnv !== undefined ? { env: input.hookEnv } : {}),
126
+ };
127
+ };
128
+ return {
129
+ screen: async (args, subject) => {
130
+ let outcome;
131
+ try {
132
+ outcome = await dispatchClassic(bus, "classic.PreToolUse", eventOf(args), { ...(input.abortSignal !== undefined ? { signal: input.abortSignal } : {}), checkAnswer: preToolUseAnswerViolation });
133
+ }
134
+ catch (err) {
135
+ return { kind: "refused", reason: preToolUseRefusedReason(subject, { kind: "failed", registration: "engine", cause: "threw", reason: `could not raise the screening: ${describeThrown(err)}`, skipped: [] }) };
136
+ }
137
+ if (outcome === undefined || outcome.kind === "aborted")
138
+ return { kind: "refused", reason: preToolUseRefusedReason(subject, undefined) };
139
+ if (outcome.kind === "failed")
140
+ return { kind: "refused", reason: preToolUseRefusedReason(subject, outcome) };
141
+ const r = screenPreToolUseResult(outcome.answer);
142
+ return { kind: "answered", answer: r === null || r === false ? undefined : r, ...(outcome.answeredBy !== undefined ? { answeredBy: outcome.answeredBy } : {}) };
143
+ },
144
+ deniedReason: (answer) => decisionText(answer) ?? `tool "${toolName}" blocked by a PreToolUse hook`,
145
+ };
195
146
  }
196
- export function createPreToolUseConstraintPolicy(preToolUse, env, onCrash, timeoutMs) {
197
- return brandPolicyAskClass({
147
+ export function createPreToolUseConstraintPolicy(chain, env) {
148
+ const judges = (p) => !p.observe;
149
+ const minted = brandPolicyAskClass({
198
150
  check: async (req, signal) => {
151
+ if (!chain.hasHandlers("classic.PreToolUse", judges))
152
+ return { action: "allow" };
199
153
  const unasked = unaskedSeatAnswer(req, signal, { aborted: `the inherited PreToolUse screening of "${req.toolName}" was not consulted: the run or turn was already interrupted`, unreachable: `the inherited PreToolUse screening of "${req.toolName}" was not consulted: nobody can be asked about this call` }, "hook");
200
154
  if (unasked !== undefined)
201
155
  return unasked;
202
- let r;
203
- const boundMs = resolveHookTimeoutMs(timeoutMs, onCrash);
156
+ const subject = `this call to "${req.toolName}"`;
157
+ let outcome;
204
158
  try {
205
- const seat = await runHookSeat("preToolUse", { timeoutMs: boundMs }, (sig) => preToolUse(req.toolName, req.args, { toolCallId: req.toolCallId, toolName: req.toolName, signal: sig, ...(env !== undefined ? { env } : {}) }));
206
- if (seat.expired) {
207
- try {
208
- onCrash?.(hookSeatExpiredError("preToolUse", boundMs, seat.cause, `the inherited screening of "${req.toolName}" was refused fail-closed`));
209
- }
210
- catch {
211
- }
212
- return { action: "deny", message: preToolUseUnansweredReason(`this call to "${req.toolName}"`, boundMs, seat.cause), decisionReason: "hook" };
213
- }
214
- r = screenPreToolUseResult(seat.value);
159
+ outcome = await chain.dispatch("classic.PreToolUse", { tool: req.toolName, tool_use_id: req.toolCallId, input: judgedInputOf(req.args), ...(env !== undefined ? { env } : {}) }, {
160
+ bottom: engineCore,
161
+ participant: judges,
162
+ ...(signal !== undefined ? { signal } : {}),
163
+ checkAnswer: (a) => (a === ENGINE_CORE_ANSWER ? undefined : preToolUseAnswerViolation(a)),
164
+ });
215
165
  }
216
166
  catch (err) {
217
- try {
218
- onCrash?.(err);
219
- }
220
- catch {
221
- }
222
- return { action: "deny", message: preToolUseCrashReason(`this call to "${req.toolName}"`, err), decisionReason: "hook" };
167
+ return { action: "deny", message: preToolUseRefusedReason(subject, { kind: "failed", registration: "engine", cause: "threw", reason: `could not raise the screening: ${describeThrown(err)}`, skipped: [] }), decisionReason: "hook" };
223
168
  }
224
- if (r === undefined)
169
+ if (outcome.kind === "aborted")
170
+ return abortedSeatDeny(req, `the inherited PreToolUse screening of "${req.toolName}" ended when the run or turn was interrupted`, "hook");
171
+ if (outcome.kind === "failed")
172
+ return { action: "deny", message: preToolUseRefusedReason(subject, outcome), decisionReason: "hook" };
173
+ const r = screenPreToolUseResult(outcome.answer);
174
+ if (r === undefined || r === null || r === false)
225
175
  return { action: "allow" };
226
176
  if (r.action === "deny") {
227
177
  return { action: "deny", message: decisionText(r) ?? `tool "${req.toolName}" blocked by a PreToolUse hook`, decisionReason: "hook" };
@@ -239,6 +189,8 @@ export function createPreToolUseConstraintPolicy(preToolUse, env, onCrash, timeo
239
189
  return r.updatedInput !== undefined ? { action: "allow", updatedInput: r.updatedInput, ...stated } : { action: "allow", ...stated };
240
190
  },
241
191
  }, "hook");
192
+ recordScreeningChain(minted, chain);
193
+ return minted;
242
194
  }
243
195
  export function askCarryRowMembers(carry) {
244
196
  if (carry === undefined)
@@ -253,54 +205,21 @@ export function askCarryRowMembers(carry) {
253
205
  ...(classed !== undefined ? classed : {}),
254
206
  };
255
207
  }
256
- export function createPreToolUseScreening(preToolUse, input, hookSeatMs, notifier) {
257
- if (preToolUse === undefined)
258
- return undefined;
259
- const { toolCallId, toolName } = input.event;
260
- const screenedName = wireOf("gate.preToolUse", input.event);
261
- const hookCtx = (seatSignal) => ({
262
- toolCallId,
263
- toolName: screenedName,
264
- ...(seatSignal !== undefined ? { signal: seatSignal } : {}),
265
- ...(() => {
266
- const c = input.trackedCwd?.();
267
- return typeof c === "string" ? { cwd: c } : {};
268
- })(),
269
- ...(input.hookEnv !== undefined ? { env: input.hookEnv } : {}),
270
- ...(input.identity !== undefined ? { identity: input.identity } : {}),
271
- });
272
- const bound = { timeoutMs: hookSeatMs, ...(input.abortSignal !== undefined ? { signal: input.abortSignal } : {}), abortEnds: true };
273
- return {
274
- screen: (args) => runHookSeat("preToolUse", bound, (sig) => preToolUse(screenedName, args, hookCtx(sig))),
275
- screened: screenPreToolUseResult,
276
- crashReason: preToolUseCrashReason,
277
- unansweredReason: async (subject, cause) => {
278
- if (cause === "timeout")
279
- traceHookCrash(input, hookSeatExpiredError("preToolUse", hookSeatMs, cause, `${subject} was NOT executed (fail-closed) and the screening face was signalled to stop`), notifier);
280
- return preToolUseUnansweredReason(subject, hookSeatMs, cause);
281
- },
282
- deniedReason: (answer) => decisionText(answer) ?? `tool "${toolName}" blocked by a PreToolUse hook`,
283
- refusedReason: (answer) => String(answer.message ?? `the PreToolUse hook's result for "${toolName}" was refused by the engine`),
284
- };
285
- }
286
208
  export async function runToolGate(input) {
287
- const { event, preToolUse, adjudicate, resolveAsk, suspendAsk } = input;
209
+ const { event, adjudicate, resolveAsk, suspendAsk } = input;
288
210
  const { toolCallId, toolName } = event;
289
211
  const callSignal = input.callSignal;
290
212
  const modelInput = event.input;
213
+ const owned = ownArgs(modelInput);
214
+ const args = owned.ok ? owned.value : modelInput;
291
215
  const notifier = createSafeNotifier(input.onNotifyError !== undefined ? { onError: input.onNotifyError } : undefined);
292
- const hookSeatMs = resolveHookTimeoutMs(input.hookTimeoutMs, (err) => traceHookCrash(input, err, notifier));
293
- const observerBound = { timeoutMs: hookSeatMs, ...(input.abortSignal !== undefined ? { signal: input.abortSignal } : {}) };
294
- const notifyPermissionDeniedSeat = async (payload) => {
295
- if (input.permissionDenied === undefined)
296
- return;
297
- await notifier.notifyAsync(async () => {
298
- const seat = await runHookSeat("permissionDenied", observerBound, (sig) => input.permissionDenied?.({ ...payload, signal: sig }));
299
- if (seat.expired)
300
- throw hookSeatExpiredError("permissionDenied", hookSeatMs, seat.cause, "the deny observation was abandoned; the deny itself is unchanged");
301
- }, "toolGate.permissionDenied");
216
+ const notifyPermissionDeniedSeat = (payload) => {
217
+ const mode = input.observedPermissionMode?.();
218
+ const seen = mode !== undefined ? { ...payload, permissionMode: mode } : payload;
219
+ const onHostObserverFailure = (err) => void notifier.notify(() => { throw err; }, "toolGate.permissionDenied");
220
+ return observePermissionDenied({ legBus: input.modBus, hostObserver: input.permissionDenied, payload: seen, signal: input.abortSignal, onHostObserverFailure });
302
221
  };
303
- const screening = createPreToolUseScreening(preToolUse, input, hookSeatMs, notifier);
222
+ const screening = createPreToolUseScreening(input);
304
223
  const pass = {
305
224
  input,
306
225
  event,
@@ -315,8 +234,8 @@ export async function runToolGate(input) {
315
234
  notifyPermissionDenied: notifyPermissionDeniedSeat,
316
235
  screening,
317
236
  afterFold: runGateLanes,
318
- currentInput: modelInput,
319
- req: { toolName, args: modelInput, toolCallId, ...(() => { const f = input.toolFaceOf?.(toolName); return f !== undefined ? { face: f } : {}; })() },
237
+ currentInput: args,
238
+ req: Object.freeze({ toolName, args, toolCallId, ...(() => { const f = input.toolFaceOf?.(toolName); return f !== undefined ? { face: f } : {}; })() }),
320
239
  preToolContext: [],
321
240
  hookAsk: undefined,
322
241
  parkFailed: undefined,
@@ -326,29 +245,15 @@ export async function runToolGate(input) {
326
245
  tightenedBy: undefined,
327
246
  policyRewrite: undefined,
328
247
  };
248
+ if (!owned.ok)
249
+ return await unownedArgsExit(pass, owned.reason);
329
250
  if (screening !== undefined) {
330
- let r;
331
- let unanswered;
332
- try {
333
- const seat = await screening.screen(pass.currentInput);
334
- if (seat.expired)
335
- unanswered = seat.cause;
336
- else
337
- r = screening.screened(seat.value);
338
- }
339
- catch (err) {
340
- const reason = screening.crashReason(`this call to "${toolName}"`, err);
341
- traceHookCrash(input, err, notifier);
342
- return await engineFailClosedExit(pass, reason);
343
- }
344
- if (unanswered !== undefined) {
345
- const reason = await screening.unansweredReason(`this call to "${toolName}"`, unanswered);
346
- return await engineFailClosedExit(pass, reason);
251
+ const screened = await screening.screen(pass.currentInput, `this call to "${toolName}"`);
252
+ if (screened.kind === "refused") {
253
+ return await engineFailClosedExit(pass, screened.reason);
347
254
  }
255
+ const r = screened.answer;
348
256
  if (r) {
349
- if (isEngineRefusal(r)) {
350
- return await engineFailClosedExit(pass, screening.refusedReason(r));
351
- }
352
257
  if (r.additionalContext) {
353
258
  pass.preToolContext.push(r.additionalContext);
354
259
  }
@@ -362,7 +267,7 @@ export async function runToolGate(input) {
362
267
  pass.hookAsk = r;
363
268
  }
364
269
  if (r.action === "allow")
365
- pass.hookAllowed = true;
270
+ pass.hookAllowedBy = screened.answeredBy !== undefined ? [screened.answeredBy] : [];
366
271
  }
367
272
  }
368
273
  return await runGateFold(pass);
@@ -24,9 +24,27 @@
24
24
  * before the frame and the transcript read them. A block whose bytes the admission refuses becomes a text
25
25
  * block that names the tool, the refusal and the size — never the bytes, and never silently nothing.
26
26
  */
27
- import type { DocumentContent, ImageContent } from "../../internal/llm.js";
28
- import { type ImageDimensions, type ImageDownsampler } from "../../core/mcp.js";
29
- import { type MediaBlock } from "../../core/media-byte-cap.js";
27
+ import type { DocumentContent, ImageContent } from "../internal/llm.js";
28
+ import { type ImageDimensions, type ImageDownsampler } from "./image-downsample.js";
29
+ import { type MediaBlock } from "./media-byte-cap.js";
30
+ /** Byte cap on a Read-image WITHOUT a downsampler: an image file is returned AS a visual
31
+ * `ImageContent` block (base64) rather than refused. Bounded so a huge image cannot blow the request.
32
+ * This stat-time cap only bounds the READ; the accept/refuse decision is the base64 gate below (the
33
+ * provider limit is on base64 LENGTH, so the effective no-sharp accept bound is raw ≤ 3.75MB). */
34
+ export declare const MAX_IMAGE_READ_BYTES: number;
35
+ /** Byte cap on a Read-image WITH a downsampler: sharp can shrink a big photo to CC's 2000×2000/3.75MB
36
+ * target, so the smaller hard refusal does not apply (CC readImageWithTokenBudget reads the file
37
+ * unbounded; we keep an OOM sanity bound — deliberate delta, camera photos fit comfortably). */
38
+ export declare const MAX_IMAGE_DOWNSAMPLE_INPUT_BYTES: number;
39
+ /** Max raw size sent whole as a document block (CC `PDF_TARGET_RAW_SIZE`). */
40
+ export declare const PDF_TARGET_RAW_SIZE: number;
41
+ /** Verify the raw bytes actually ARE the image format the extension claims (magic-number sniff). Extension alone
42
+ * is forgeable: a `.png` holding text / an empty file / a truncated blob would otherwise become a malformed
43
+ * `ImageContent` that makes the NEXT provider request fail (400). Pure — no I/O. */
44
+ export declare function imageMagicMatches(bytes: Uint8Array, mimeType: string): boolean;
45
+ /** True when the bytes start with the `%PDF-` magic. Magic outranks extension both ways: a mis-named
46
+ * `.pdf` without it is refused, and a non-`.pdf` file carrying it is read AS a PDF. */
47
+ export declare function pdfMagicMatches(bytes: Uint8Array): boolean;
30
48
  /** The image types the content API accepts — the only ones a signature check can verify. */
31
49
  export declare const ADMITTED_IMAGE_MIME_TYPES: readonly ["image/png", "image/jpeg", "image/gif", "image/webp"];
32
50
  /** The raw-byte read cap for an image, with or without a downsampler. */
@@ -1,9 +1,27 @@
1
- import { MCP_IMAGE_MAX_BASE64 } from "../../core/mcp.js";
2
- import { claimedMediaType } from "../../core/media-byte-cap.js";
3
- import { readMember } from "../../core/thrown-value.js";
4
- import { imageMagicMatches } from "./safety.js";
5
- import { MAX_IMAGE_READ_BYTES, MAX_IMAGE_DOWNSAMPLE_INPUT_BYTES } from "./fs-shared.js";
6
- import { PDF_TARGET_RAW_SIZE, pdfMagicMatches } from "./pdf.js";
1
+ import { MCP_IMAGE_MAX_BASE64 } from "./image-downsample.js";
2
+ import { claimedMediaType } from "./media-byte-cap.js";
3
+ import { readMember } from "./thrown-value.js";
4
+ export const MAX_IMAGE_READ_BYTES = 5 * 1024 * 1024;
5
+ export const MAX_IMAGE_DOWNSAMPLE_INPUT_BYTES = 64 * 1024 * 1024;
6
+ export const PDF_TARGET_RAW_SIZE = 20 * 1024 * 1024;
7
+ export function imageMagicMatches(bytes, mimeType) {
8
+ const b = bytes;
9
+ switch (mimeType) {
10
+ case "image/png":
11
+ return b.length >= 8 && b[0] === 0x89 && b[1] === 0x50 && b[2] === 0x4e && b[3] === 0x47 && b[4] === 0x0d && b[5] === 0x0a && b[6] === 0x1a && b[7] === 0x0a;
12
+ case "image/jpeg":
13
+ return b.length >= 3 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff;
14
+ case "image/gif":
15
+ return b.length >= 6 && b[0] === 0x47 && b[1] === 0x49 && b[2] === 0x46 && b[3] === 0x38 && (b[4] === 0x37 || b[4] === 0x39) && b[5] === 0x61;
16
+ case "image/webp":
17
+ return b.length >= 12 && b[0] === 0x52 && b[1] === 0x49 && b[2] === 0x46 && b[3] === 0x46 && b[8] === 0x57 && b[9] === 0x45 && b[10] === 0x42 && b[11] === 0x50;
18
+ default:
19
+ return false;
20
+ }
21
+ }
22
+ export function pdfMagicMatches(bytes) {
23
+ return bytes.length >= 5 && bytes[0] === 0x25 && bytes[1] === 0x50 && bytes[2] === 0x44 && bytes[3] === 0x46 && bytes[4] === 0x2d;
24
+ }
7
25
  export const ADMITTED_IMAGE_MIME_TYPES = Object.freeze(["image/png", "image/jpeg", "image/gif", "image/webp"]);
8
26
  export function imageReadCap(downsampler) {
9
27
  return downsampler ? MAX_IMAGE_DOWNSAMPLE_INPUT_BYTES : MAX_IMAGE_READ_BYTES;
@@ -56,8 +56,11 @@ export declare class McpConnectionPool {
56
56
  * transport object that answers differently to two readers cannot split the dial from its key); every other transport member is read here.
57
57
  * The declaration's mount identity (`fingerprint`) is part of the key when declared: the revocation probe is asked with it, so two mounts the
58
58
  * host tells apart never share one connection (an undeclared fingerprint leaves the key as it always was).
59
+ * So is the startup bound the dial runs under (`startupMs`, the dial's one reading of {@link mcpStartupTimeoutMs}, threaded like `kind`):
60
+ * a connection is dialled under the declaration of the mount that dials it, so a mount never waits on, or shares, a dial bounded by
61
+ * another mount's clock. No bound in force (none declared, no environment seat) leaves the key as it always was.
59
62
  */
60
- static keyOf(spec: McpServerSpec, principal: string | undefined, kind: string | undefined): string;
63
+ static keyOf(spec: McpServerSpec, principal: string | undefined, kind: string | undefined, startupMs: number | undefined): string;
61
64
  /**
62
65
  * Lease the connection for `key`, dialling it with `dial` when none is live. `fresh` replaces the pool's current connection with a new
63
66
  * dial (a re-dial): the connection it replaces lives on until its other holders release it. Resolves `undefined` once the pool is
@@ -4,10 +4,15 @@ export class McpConnectionPool {
4
4
  dialing = new Map();
5
5
  generation = new Map();
6
6
  shut = false;
7
- static keyOf(spec, principal, kind) {
7
+ static keyOf(spec, principal, kind, startupMs) {
8
8
  const rest = Object.fromEntries(Object.keys(spec.transport).filter((k) => k !== "kind").map((k) => [k, Reflect.get(spec.transport, k)]));
9
9
  const fingerprint = spec.fingerprint;
10
- return canonicalize(fingerprint === undefined ? [spec.name, kind ?? null, rest, principal ?? null] : [spec.name, kind ?? null, rest, principal ?? null, fingerprint]);
10
+ const identity = [spec.name, kind ?? null, rest, principal ?? null];
11
+ if (fingerprint !== undefined)
12
+ identity.push(fingerprint);
13
+ if (startupMs !== undefined)
14
+ identity.push({ startupTimeoutMs: startupMs });
15
+ return canonicalize(identity);
11
16
  }
12
17
  async acquire(key, dial, opts) {
13
18
  if (this.shut)
@@ -38,15 +38,17 @@ export interface McpProbeOptions {
38
38
  * on a real leg — so a server that applies per-user rules answers the probe as it would answer the
39
39
  * task. */
40
40
  principal?: string;
41
- /** Per-server ceiling on the VERDICT, in whole milliseconds within `[1, MCP_ENV_MS_MAX]` (the ceiling
42
- * every env-supplied MCP bound keeps, because a larger delay is silently rewritten to 1 ms). When it
43
- * elapses, THAT server's row reads `failed` with `errorCode: "timeout"` and the walk moves on; nothing
44
- * throws. It does NOT end the dial — nothing here can — so the transport stays open until the dial
45
- * lands on its own; see the residue note on {@link probeMcpServers}.
41
+ /** An OVERALL ceiling on the VERDICT for each server, in whole milliseconds within `[1, MCP_ENV_MS_MAX]` (the
42
+ * ceiling every env-supplied MCP bound keeps, because a larger delay is silently rewritten to 1 ms). The
43
+ * caller's own choice, and it lives BESIDE the per-phase startup bounds the dial runs under — whichever
44
+ * arrives first decides. When it elapses, THAT server's row reads `failed` with `errorCode: "timeout"` and
45
+ * the walk moves on; nothing throws. It does NOT end the dial — nothing here can — so the transport stays
46
+ * open until the dial lands on its own; see the residue note on {@link probeMcpServers}.
46
47
  *
47
- * Default: the engine's own startup-phase bound ({@link mcpStartupTimeoutMs}, env `MCP_TIMEOUT`) when a
48
- * deployment set one; absent there, this module runs no clock and the answer for that server is
49
- * whatever the dial's own request budgets produce — which bound the REQUESTS, not the dial. */
48
+ * Default: none. The probe has no clock of its own: it dials through the entrance a task does, so each
49
+ * startup phase (the handshake, then the initial listing) carries that server's own bound from the one
50
+ * reader ({@link import("./mcp.js").mcpStartupTimeoutMs}: its `startupTimeoutMs`, else env `MCP_TIMEOUT`, else
51
+ * the SDK's 60 000 ms) — exactly what a task allows it. */
50
52
  timeoutMs?: number;
51
53
  /** Abort the whole probe. The call rejects with the signal's own reason (a bare `abort()` gives an
52
54
  * `AbortError`) AFTER every server it had opened is closed — see the residue contract on
@@ -102,7 +104,8 @@ export interface McpProbeOptions {
102
104
  * return, and NOT that the call itself is bounded. What bounds it, stated part by part rather than
103
105
  * summarised into a number that would be wrong:
104
106
  * · a dial has two REQUEST-BUDGET phases, each carrying the startup-phase bound ({@link
105
- * mcpStartupTimeoutMs}, env `MCP_TIMEOUT`, else the SDK's 60 000 ms default): the handshake, one
107
+ * import("./mcp.js").mcpStartupTimeoutMs}: the server's own `startupTimeoutMs`, else env `MCP_TIMEOUT`, else the SDK's
108
+ * 60 000 ms default): the handshake, one
106
109
  * request; and the initial tool listing, a PAGINATED WALK whose whole budget is that same number
107
110
  * (however many pages it takes — and no request at all when the peer advertises no tools). Two
108
111
  * budgets, so twice that, not once;
@@ -1,7 +1,7 @@
1
1
  import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { snapshotMcpEntries } from "./locked-config.js";
3
3
  import { classifyMcpFailure, livenessOf } from "./mcp-failure.js";
4
- import { MCP_ENV_MS_MAX, materializeMcpTools, mcpStartupTimeoutMs } from "./mcp.js";
4
+ import { MCP_ENV_MS_MAX, materializeMcpTools } from "./mcp.js";
5
5
  import { findNamespacePrefixCollision } from "./protocol-naming.js";
6
6
  import { MCP_NAMESPACE } from "./protocol-table.js";
7
7
  import { mcpManifestEntries } from "./runner/prepare-wiring-manifest.js";
@@ -62,7 +62,7 @@ function stoppedWaitingStatus(entry, ms) {
62
62
  }
63
63
  export async function probeMcpServers(specs, opts = {}) {
64
64
  const declared = snapshotMcpEntries(specs);
65
- const timeoutMs = wholeInRange("timeoutMs", opts.timeoutMs, 1, MCP_ENV_MS_MAX) ?? mcpStartupTimeoutMs();
65
+ const timeoutMs = wholeInRange("timeoutMs", opts.timeoutMs, 1, MCP_ENV_MS_MAX);
66
66
  const concurrency = wholeInRange("concurrency", opts.concurrency, 1, Number.MAX_SAFE_INTEGER) ?? 1;
67
67
  const onElicit = opts.onElicit ?? DECLINE_EVERY_ELICITATION;
68
68
  const signal = opts.signal;
@@ -148,6 +148,22 @@ export interface McpServerSpec {
148
148
  * (`config.mcp_fingerprint_invalid`).
149
149
  */
150
150
  fingerprint?: string;
151
+ /**
152
+ * THIS server's startup bound, in whole milliseconds: the handshake (connect + initialize) and the initial tool listing each carry it
153
+ * (the listing as one walk, however many pages). One knob, three seats, read by one function in this order — this member, else the
154
+ * process-level `MCP_TIMEOUT`, else the MCP SDK's own 60 000 ms — so a server that hangs at startup is cut on its own clock while its
155
+ * siblings keep the deployment's. It bounds only that startup phase: a call's own timeout (`MCP_TOOL_TIMEOUT`) is untouched.
156
+ *
157
+ * The range is the one every MCP millisecond bound keeps, 1 000 to 2 147 483 647 (a larger delay is silently rewritten to 1 ms by the
158
+ * timer, a smaller one is a unit slip). Unlike the environment seat — a string a process may clamp and announce — a declaration is typed
159
+ * data, so anything else (a string, a fraction, zero, a negative, NaN, `null`, a number outside the range) refuses THIS server at its
160
+ * dial (`config.mcp_startup_timeout_invalid`; siblings mount) rather than being clamped or read as the default.
161
+ *
162
+ * The bound a dial runs under (this member, else the environment seat) is part of a pooled connection's identity: mounts under one
163
+ * bound lease ONE connection, mounts under different bounds dial their own — so a mount is never held by another mount's clock.
164
+ * The no-run probe has no clock of its own: each startup phase carries this same bound (a caller's `timeoutMs` is an overall ceiling beside it).
165
+ */
166
+ startupTimeoutMs?: number;
151
167
  }
152
168
  type ToolAxes = NonNullable<McpServerSpec["toolAxes"]>;
153
169
  /**
@@ -1,4 +1,4 @@
1
- import { type McpInstructionsEntry, type PromptSectionSeat } from "./prompt-section-seat.js";
1
+ import { type McpInstructionsEntry, type PromptSectionDisposer } from "./prompt-section-seat.js";
2
2
  import { McpConnectionPool } from "./mcp-connection-pool.js";
3
3
  import { type McpDelivered, type McpFailureKind, type McpServerLiveness } from "./mcp-failure.js";
4
4
  import type { AgentTool } from "../internal/harness-types.js";
@@ -101,7 +101,7 @@ export interface MaterializedMcp {
101
101
  toolAxes: McpToolAxis[];
102
102
  /**
103
103
  * MCP server `instructions` (design/64 §17.3, CC `# MCP Server Instructions`): the optional guidance a
104
- * connected server returns from `initialize`, after the host's disposition (`PromptProvider.section`,
104
+ * connected server returns from `initialize`, after the host's disposition (the `prompt.section` station,
105
105
  * applied once at the dial: a dropped entry is absent, a replaced one carries `author: "host"`). The
106
106
  * Runner injects these into the (stable) system prompt. One entry per server whose entry survived.
107
107
  */
@@ -443,11 +443,15 @@ export declare const MCP_IDLE_TIMEOUT_HTTP_DEFAULT_MS: number;
443
443
  * `MCP_IDLE_TIMEOUT_HTTP`, else the CC-matched default). Exported for tests. */
444
444
  export declare function mcpIdleTimeoutMs(kind: "stdio" | "http"): number;
445
445
  /**
446
- * Startup-phase (connect + initial listTools) timeout override in ms (env `MCP_TIMEOUT`); undefined keeps
447
- * the SDK default. Startup deliberately stays bounded: a hung server at materialize time should fail open
448
- * into a skipped-server warning quickly, not stall task preparation indefinitely.
446
+ * Startup-phase (connect + initial listTools) timeout override in ms; undefined keeps the SDK default. Startup
447
+ * deliberately stays bounded: a hung server at materialize time should fail open into a skipped-server warning
448
+ * quickly, not stall task preparation indefinitely. THE one reader of the knob: with a `server`, that
449
+ * declaration's own `startupTimeoutMs` wins over env `MCP_TIMEOUT` (typed data held to the env knob's range
450
+ * [{@link MCP_ENV_MS_MIN}, {@link MCP_ENV_MS_MAX}], but REFUSED outside it — `config.mcp_startup_timeout_invalid`
451
+ * — where a process string is clamped and announced); without one, the process-level bound alone. The env value
452
+ * is resolved either way, so a bad one is announced even when a declaration shadows it.
449
453
  */
450
- export declare function mcpStartupTimeoutMs(): number | undefined;
454
+ export declare function mcpStartupTimeoutMs(server?: Pick<McpServerSpec, "name" | "startupTimeoutMs">): number | undefined;
451
455
  /** The protocol-range condition name for a JSON-RPC error code, or `undefined` for every other code
452
456
  * (see {@link MCP_SPEC_ERROR_CODE_NAMES}). */
453
457
  export declare function describeMcpSpecErrorCode(code: unknown): string | undefined;
@@ -605,7 +609,7 @@ reminderDisclosure?: {
605
609
  }, poolOpts?: {
606
610
  pool: McpConnectionPool;
607
611
  ownsPool?: boolean;
608
- }, promptSections?: PromptSectionSeat): Promise<MaterializedMcp>;
612
+ }, promptSections?: PromptSectionDisposer): Promise<MaterializedMcp>;
609
613
  /**
610
614
  * Fold the caller's AUTHORITATIVE per-tool override (design F: caller = trust root) over the server-hint axis.
611
615
  * Unlike server hints, the caller may RAISE or LOWER any axis: `effect` sets the repeat-safety class (lower to