@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,63 @@
1
- import { deniedProtocolTools, delegationListWhere, listReaches, notAListSentence, toolListsDigest, notAStringSentence, readAllowedToolList, readDeniedEntry, readDeniedToolList } from "../../agents/tool-filter.js";
1
+ import { compileToolSelector, deniedProtocolTools, delegationListWhere, isCanonicalToolName, isToolSelectorLayer, listReaches, notAListSentence, toolListsDigest, notAStringSentence, readAllowedToolList, readDeniedEntry, readDeniedToolList } from "../../agents/tool-filter.js";
2
2
  import { readToolNameList, valueTypeLabel } from "../tool-name-list.js";
3
- import { compileAllowList, inertEntryPhrase, renderListEntry } from "../allow-entry.js";
3
+ import { compileAllowList, inertEntryPhrase, renderListEntry, rootFacingToolNames } from "../allow-entry.js";
4
4
  import { inlineUntrusted } from "../untrusted-text.js";
5
5
  import { ENGINE_TOOLS } from "../tool-catalog-entries.js";
6
+ import { callerToolNameProblem } from "../caller-tool-name.js";
6
7
  import { mintNamespacePrefix } from "../protocol-naming.js";
7
8
  import { PROTOCOL_TABLE, protocolOf } from "../protocol-table.js";
8
- import { splitNamespacedName } from "../tool-name-aliases.js";
9
+ import { mountedToolNames, splitNamespacedName } from "../tool-name-aliases.js";
10
+ import { projectionToolNames } from "../tool-policy.js";
11
+ import { declaredDelegationNameOf } from "../delegation-name.js";
12
+ export function renamedDelegationMountSentence(...rosters) {
13
+ for (const tools of rosters) {
14
+ if (!Array.isArray(tools))
15
+ continue;
16
+ for (const t of tools) {
17
+ const declared = declaredDelegationNameOf(t);
18
+ if (declared === undefined)
19
+ continue;
20
+ try {
21
+ const mounted = t.name;
22
+ if (declared === mounted)
23
+ continue;
24
+ return `The delegation tool declared as "${inlineUntrusted(declared, 80)}" is mounted under the name "${inlineUntrusted(String(mounted), 80)}": one tool would answer to two names (the agent definitions' tool lists, the copy a child is handed and the tool's prompt text speak the declared one; the roster and the model's call speak the mounted one). Mount it under its declared name, or declare the name you mount it under (\`createSubagentTool({ name })\`).`;
25
+ }
26
+ catch {
27
+ }
28
+ }
29
+ }
30
+ return undefined;
31
+ }
32
+ export function refuseRenamedDelegationMounts(...rosters) {
33
+ const sentence = renamedDelegationMountSentence(...rosters);
34
+ if (sentence !== undefined)
35
+ throw refused(sentence, "config.tool_face_control", "renamed_mount");
36
+ }
37
+ function poolOf(tools) {
38
+ return { tools, names: mountedToolNames([...tools.map((t) => ({ name: t.name })), ...tools.flatMap((t) => t.agentToolPool ?? [])]), complete: tools.every((t) => t.agentToolPool !== undefined) };
39
+ }
40
+ export function delegationPoolReading(callerTools, selector) {
41
+ return poolOf(callerTools.filter((t) => t.agentToolFaces !== undefined && selector.admits(t)));
42
+ }
43
+ function peersOf(known, declaredPeers) {
44
+ const peers = new Set(PROTOCOL_TABLE.flatMap((ns) => (declaredPeers[ns.id] ?? []).map((p) => mintNamespacePrefix(ns, p.name))));
45
+ for (const name of known) {
46
+ const ns = protocolOf(name);
47
+ const split = ns !== undefined ? splitNamespacedName(name) : undefined;
48
+ if (split !== undefined && split.peer !== "")
49
+ peers.add(mintNamespacePrefix(split.ns, split.peer));
50
+ }
51
+ return peers;
52
+ }
53
+ function rootFacingUniverse(mounted, pool, declaredPeers) {
54
+ const known = rootFacingToolNames([...mounted, ...pool.names]);
55
+ return { known, peers: peersOf(known, declaredPeers), complete: true, carrierOf: (name) => (mounted.has(name) ? "run" : pool.names.has(name) ? "pool" : "catalog") };
56
+ }
57
+ function childFacingUniverse(pool, declaredPeers) {
58
+ const known = new Set([...mountedToolNames(ENGINE_TOOLS), ...pool.names]);
59
+ return { known, peers: peersOf(known, declaredPeers), complete: pool.complete, carrierOf: (name) => (pool.names.has(name) ? "pool" : "catalog") };
60
+ }
9
61
  const DENY_LIST_NAMES_ONLY = "To block by path or command, write a deny rule in the caller's permission policy: a tool-name list only takes tool names (it narrows which tools are mounted).";
10
62
  function universeCarries(reach, entry, universe) {
11
63
  for (const name of universe.known)
@@ -23,34 +75,46 @@ function refused(message, code, reason) {
23
75
  return e;
24
76
  }
25
77
  const LIST_WORD = { allowed: "allowed tools", denied: "denied tools" };
78
+ const NEAR_MISS_EFFECT = {
79
+ allowed: "it admits nothing, so the tool it was written for is not granted",
80
+ denied: "it hides nothing, so the tool it was written for stays available to the agent",
81
+ };
82
+ function nearMissRefusal(where, side, flaw, code) {
83
+ return refused(`${where} declares tool ${inertEntryPhrase(flaw)} in its ${LIST_WORD[side]}, a spelling that reaches no tool a run mounts — ${NEAR_MISS_EFFECT[side]}. Write the name as the tool is mounted.`, code, "near_miss");
84
+ }
26
85
  function grammarRefusal(where, side, flaw, code) {
27
86
  return refused(`${where} declares tool ${inertEntryPhrase(flaw)} in its ${LIST_WORD[side]}, a spelling no tool name can have — fix the entry. ${DENY_LIST_NAMES_ONLY}`, code, "unknown_name");
28
87
  }
88
+ function readableToolNameList(where, side, value, code) {
89
+ if (value === undefined || value === null)
90
+ return [];
91
+ const list = readToolNameList(value);
92
+ if (list.kind !== "entries")
93
+ throw refused(notAListSentence(where, side, list), code, "not_a_list");
94
+ return list.entries.map((entry, index) => {
95
+ const read = readDeniedEntry(entry);
96
+ if (read.kind === "not_a_string")
97
+ throw refused(notAStringSentence(where, side, index, read.type), code, "not_a_string");
98
+ if (read.kind === "unreadable")
99
+ throw grammarRefusal(where, side, { entry: read.entry, reason: "grammar" }, code);
100
+ if (side === "denied" && read.name === "*") {
101
+ throw refused(`${where} declares ${renderListEntry(entry)} in its ${LIST_WORD[side]}: a denied list has no wildcard — the entry would hide nothing. To give the agent no tools, write an empty allowed list.`, code, "unsupported_pattern");
102
+ }
103
+ return { entry: entry, name: read.name, content: read.content };
104
+ });
105
+ }
29
106
  const KEPT_ENTRY = {
30
107
  allowed: "it grants nothing today and stays in force for a tool of that name mounted later",
31
108
  denied: "it denies nothing today and stays in force for a tool of that name mounted later",
32
109
  };
110
+ const KEPT_ENTRY_UNLISTED_POOL = "so it is kept here: the pool this delegation tool hands its child cannot be listed, and the spawned child judges the entry against its own roster (another spelling of a tool it carries refuses the child before it starts; an exact name takes effect; a name nothing answers to there stays in force for a tool of that name mounted later)";
33
111
  const PEER_WORD = { mcp: "MCP server", a2a: "A2A peer" };
34
112
  function judgeToolNameList(where, side, value, universe, advise, code) {
35
- if (value === undefined || value === null)
36
- return;
37
- const list = readToolNameList(value);
38
- if (list.kind !== "entries")
39
- throw refused(notAListSentence(where, side, list), code, "not_a_list");
40
- let index = -1;
41
- for (const entry of list.entries) {
42
- index += 1;
43
- if (typeof entry !== "string")
44
- throw refused(notAStringSentence(where, side, index, valueTypeLabel(entry)), code, "not_a_string");
45
- if (entry === "*") {
46
- if (side === "allowed")
47
- continue;
48
- throw refused(`${where} declares "*" in its ${LIST_WORD[side]}: a denied list has no wildcard — the entry would hide nothing. To give the agent no tools, write an empty allowed list.`, code, "unsupported_pattern");
49
- }
50
- const read = readDeniedEntry(entry);
51
- const toolName = read.kind === "name" ? read.name : entry;
52
- const name = side === "denied" ? toolName : entry;
53
- const content = side === "denied" && read.kind === "name" && read.content;
113
+ for (const { entry, name: toolName, content: written } of readableToolNameList(where, side, value, code)) {
114
+ if (toolName === "*" && !written)
115
+ continue;
116
+ const name = side === "denied" || !written ? toolName : entry;
117
+ const content = side === "denied" && written;
54
118
  const reach = side === "denied" ? readDeniedToolList([entry]) : (readAllowedToolList([entry]) ?? readDeniedToolList([]));
55
119
  const split = splitNamespacedName(name);
56
120
  if (split !== undefined && split.peer === "*" && split.tool === undefined) {
@@ -59,9 +123,9 @@ function judgeToolNameList(where, side, value, universe, advise, code) {
59
123
  throw refused(`${where} declares ${renderListEntry(entry)} in its ${LIST_WORD[side]}: an allowed list has no protocol-wide form — name a peer (\`${split.ns.prefix}<peer>\`) or its tools.`, code, "unsupported_pattern");
60
124
  }
61
125
  const { inert } = compileAllowList([toolName], "deny", universe.known);
62
- const flaw = inert[0] === undefined ? undefined : { ...inert[0], entry };
63
- if (flaw !== undefined && flaw.reason === "grammar")
64
- throw grammarRefusal(where, side, flaw, code);
126
+ const flaw = inert[0] === undefined ? undefined : { ...inert[0], entry, ...(inert[0].reason === "near_miss" && inert[0].currentName !== undefined ? { carriedBy: universe.carrierOf(inert[0].currentName) } : {}) };
127
+ if (universe.complete && flaw?.currentName !== undefined)
128
+ throw nearMissRefusal(where, side, flaw, code);
65
129
  if (toolName !== name) {
66
130
  advise(new Error(`${where} declares ${renderListEntry(entry)} in its allowed tools: an allowed list names tools, not content, so this entry grants nothing — name the tool "${inlineUntrusted(toolName, 80)}" to grant it, or move the content rule to a denied list or a permission rule.`));
67
131
  continue;
@@ -73,29 +137,117 @@ function judgeToolNameList(where, side, value, universe, advise, code) {
73
137
  }
74
138
  const phrase = flaw !== undefined ? inertEntryPhrase(flaw) : renderListEntry(entry);
75
139
  const undeclared = split !== undefined && split.peer !== "" && !split.peer.includes("*") && !universe.peers.has(mintNamespacePrefix(split.ns, split.peer));
76
- const why = undeclared ? `no ${PEER_WORD[split.ns.id]} named "${inlineUntrusted(split.peer, 80)}" is declared for this task` : "no tool of this task answers to that name";
77
- advise(new Error(`${where} declares tool ${phrase} in its ${LIST_WORD[side]}: ${why}, so ${KEPT_ENTRY[side]}. If the name is misspelt, fix the entry.`));
140
+ const why = undeclared ? `no ${PEER_WORD[split.ns.id]} named "${inlineUntrusted(split.peer, 80)}" is declared for this task` : universe.complete ? "no tool of this task answers to that name" : "no tool this run can list answers to that name";
141
+ advise(new Error(`${where} declares tool ${phrase} in its ${LIST_WORD[side]}: ${why}, ${universe.complete ? `so ${KEPT_ENTRY[side]}` : KEPT_ENTRY_UNLISTED_POOL}. If the name is misspelt, fix the entry.`));
78
142
  }
79
143
  }
80
144
  export function deniedToolNamesOf(where, value) {
81
- const list = readToolNameList(value);
82
- if (list.kind !== "entries")
83
- throw refused(notAListSentence(where, "denied", list), "config.agent.unknown_tool", "not_a_list");
84
- return list.entries.map((entry, index) => {
85
- if (entry === "*")
86
- return entry;
87
- const read = readDeniedEntry(entry);
88
- if (read.kind === "not_a_string")
89
- throw refused(notAStringSentence(where, "denied", index, read.type), "config.agent.unknown_tool", "not_a_string");
90
- const flaw = compileAllowList([read.name], "deny").inert[0];
91
- if (flaw !== undefined && flaw.reason === "grammar")
92
- throw grammarRefusal(where, "denied", { ...flaw, entry: read.name }, "config.agent.unknown_tool");
93
- return read.name;
94
- });
145
+ return readableToolNameList(where, "denied", value, "config.agent.unknown_tool").map((read) => read.name);
146
+ }
147
+ function inheritedListsWhere(ceilings, i) {
148
+ const up = ceilings - 1 - i;
149
+ return up === 0 ? "the lists of the run this leg was spawned under" : `the lists of the run ${up} spawn${up === 1 ? "" : "s"} above the run this leg was spawned under`;
150
+ }
151
+ function readSelectorLayer(layer, layerWhere, listWhere, recorded) {
152
+ if (!isToolSelectorLayer(layer)) {
153
+ throw refused(`${layerWhere} are a value that is ${valueTypeLabel(layer)}, not a pair of tool-name lists (an allowed and a denied list) — the leg does not start. Fix the layer.`, "config.tool_face_control", "not_a_list");
154
+ }
155
+ for (const side of ["allowed", "denied"]) {
156
+ const where = listWhere(side);
157
+ const entries = readableToolNameList(where, side, side === "allowed" ? layer.allow : layer.deny, "config.tool_face_control");
158
+ const written = recorded ? entries.find((read) => !isCanonicalToolName(read.entry)) : undefined;
159
+ if (written !== undefined) {
160
+ throw refused(`${where} declares ${renderListEntry(written.entry)} in its ${LIST_WORD[side]}: a row records each tool name in its bare spelling (\`Read\`, never \`Read()\`, \`Read(*)\` or \`Read(…)\`), and this entry was recorded by an engine that read the list otherwise — it is read here neither as that engine read it nor as this one would`, "config.tool_face_control", "unsupported_pattern");
161
+ }
162
+ }
163
+ }
164
+ function ownToolListsFlaw(value) {
165
+ try {
166
+ if (typeof value !== "object" || value === null || Array.isArray(value))
167
+ return `a value that is ${valueTypeLabel(value)}, not a tool name with an agent definition name and the digest of the lists it wrote`;
168
+ const { toolName, definition, lists } = value;
169
+ if (typeof toolName !== "string" || toolName.length === 0)
170
+ return `a tool whose name is ${typeof toolName === "string" ? "empty" : `a value that is ${valueTypeLabel(toolName)}`}, not a tool name`;
171
+ const problem = callerToolNameProblem(toolName);
172
+ if (problem !== undefined)
173
+ return `a tool whose name is not one a caller tool can carry (${problem})`;
174
+ if (definition !== undefined && typeof definition !== "string")
175
+ return `an agent definition whose name is a value that is ${valueTypeLabel(definition)}, not a name`;
176
+ if (definition === "")
177
+ return "an agent definition whose name is empty, not a name";
178
+ if (lists === undefined)
179
+ return "a record with no digest of the lists it wrote";
180
+ if (typeof lists !== "string")
181
+ return `a digest of the lists it wrote that is a value that is ${valueTypeLabel(lists)}, not a digest`;
182
+ if (lists.length === 0)
183
+ return "a digest of the lists it wrote that is empty, not a digest";
184
+ return undefined;
185
+ }
186
+ catch {
187
+ return "a value that cannot be read";
188
+ }
189
+ }
190
+ export function persistedToolNamesRefusal(ceiling, chain, ownLists) {
191
+ const pending = "refused before the claim (the checkpoint stays pending): finish it on a worker of the engine that parked it, or expire it and run the work again";
192
+ const ownFlaw = ownLists === undefined ? undefined : ownToolListsFlaw(ownLists);
193
+ if (ownFlaw !== undefined)
194
+ return { message: `this leg's checkpoint row records the delegation tool that wrote its own lists as ${ownFlaw} — ${pending}`, reason: "invalid_record" };
195
+ if (ceiling !== undefined) {
196
+ const layers = readToolNameList(ceiling);
197
+ if (layers.kind !== "entries")
198
+ return { message: `the ceiling this leg's checkpoint row recorded is a value that is ${layers.kind === "not_a_list" ? layers.type : "a list that cannot be read"}, not a list of layers — ${pending}`, reason: "not_a_list" };
199
+ try {
200
+ layers.entries.forEach((layer, i) => readSelectorLayer(layer, inheritedListsWhere(layers.entries.length, i), () => inheritedListsWhere(layers.entries.length, i), true));
201
+ }
202
+ catch (e) {
203
+ return { message: `${e.message} — as this leg's checkpoint row recorded it; ${pending}`, reason: e.detail.reason };
204
+ }
205
+ }
206
+ if (!Array.isArray(chain))
207
+ return undefined;
208
+ const flaw = (() => {
209
+ for (const [layer, entry] of chain.entries()) {
210
+ if (typeof entry !== "object" || entry === null || "opaque" in entry || !Array.isArray(entry.components))
211
+ continue;
212
+ for (const c of entry.components) {
213
+ if (typeof c !== "object" || c === null)
214
+ continue;
215
+ const { member, list } = projectionToolNames(c);
216
+ const where = `layer ${layer + 1}'s ${c.kind === "tool_deny" || c.kind === "tool_allowlist" || c.kind === "shell_command" ? `\`${c.kind}\`` : "component"} ${member}`;
217
+ const read = readToolNameList(list);
218
+ if (read.kind !== "entries")
219
+ return { what: `${where} as a value that is ${read.kind === "not_a_list" ? read.type : "a list that cannot be read"}, not a list of tool names`, reason: "not_a_list" };
220
+ const at = read.entries.findIndex((name) => !isCanonicalToolName(name));
221
+ if (at < 0)
222
+ continue;
223
+ const name = read.entries[at];
224
+ return typeof name === "string" ? { what: `${renderListEntry(name)} in ${where}`, reason: "unsupported_pattern" } : { what: `in ${where}, as entry ${at + 1}, a value that is ${valueTypeLabel(name)}`, reason: "not_a_string" };
225
+ }
226
+ }
227
+ return undefined;
228
+ })();
229
+ return flaw === undefined
230
+ ? undefined
231
+ : { reason: flaw.reason, message: `the checkpoint's frozen inherited constraint chain holds ${flaw.what}: a row records each tool name in its bare spelling (\`Read\`, never \`Read()\`, \`Read(*)\` or \`Read(…)\`), and this entry was recorded by a reading that is not the rule syntax's (an earlier engine's name set, or a policy that reads its lists as written) — it is replayed neither as that reading nor as this engine's; ${pending} (with the policy naming the tool itself)` };
232
+ }
233
+ export function refusePersistedToolNames(ceiling, chain, ownLists) {
234
+ const refusal = persistedToolNamesRefusal(ceiling, chain, ownLists);
235
+ if (refusal !== undefined)
236
+ throw refused(refusal.message, "config.tool_face_control", refusal.reason);
95
237
  }
96
238
  export function boundToolListsJudgment(evidence, own) {
97
239
  return evidence !== undefined && evidence === toolListsDigest(own.allow, own.exclude) ? evidence : undefined;
98
240
  }
241
+ export function boundOwnToolLists(record, own) {
242
+ if (record === undefined)
243
+ return undefined;
244
+ const flaw = ownToolListsFlaw(record);
245
+ if (flaw !== undefined) {
246
+ throw refused(`this leg's internals (RunInternals.ownToolLists) record the delegation tool that wrote its own lists as ${flaw} — the leg does not start. Only the delegation lane writes that record: leave it out.`, "config.tool_face_control", "invalid_record");
247
+ }
248
+ const held = record;
249
+ return held.lists === toolListsDigest(own.allow, own.exclude) ? held : undefined;
250
+ }
99
251
  export function judgeRunToolLists(input) {
100
252
  const { spec, tools, onceLedger, sessionId } = input;
101
253
  const advise = (err) => {
@@ -105,6 +257,13 @@ export function judgeRunToolLists(input) {
105
257
  catch {
106
258
  }
107
259
  };
260
+ const ownWhere = input.ownBy !== undefined ? delegationListWhere(input.ownBy.toolName, input.ownBy.definition) : undefined;
261
+ const ceilings = input.layers.length - 1;
262
+ input.layers.forEach((layer, i) => {
263
+ const own = i === ceilings;
264
+ const inherited = own ? undefined : inheritedListsWhere(ceilings, i);
265
+ readSelectorLayer(layer, inherited ?? "this run's own tool lists", (side) => inherited ?? ownWhere ?? (side === "allowed" ? "TaskSpec.allowTools" : "TaskSpec.excludeTools"), false);
266
+ });
108
267
  const denied = deniedProtocolTools(input.layers, input.legs);
109
268
  if (denied.length > 0) {
110
269
  const names = [...new Set(denied.map((h) => `"${h.name}"`))];
@@ -113,43 +272,28 @@ export function judgeRunToolLists(input) {
113
272
  const hits = denied.filter((h) => h.own === own).map((h) => `"${h.name}"`);
114
273
  return hits.length > 0 ? [`${whose} denies ${one ? "it" : hits.join(", ")}`] : [];
115
274
  };
116
- const by = [...deniedBy(true, input.ownWhere ?? "this run's own excludeTools"), ...deniedBy(false, "an inherited list (the spawning run's excludeTools, or one it inherits)")];
275
+ const by = [...deniedBy(true, ownWhere ?? "this run's own excludeTools"), ...deniedBy(false, "an inherited list (the spawning run's excludeTools, or one it inherits)")];
117
276
  throw refused(`This kind of leg (${input.legs.join(", ")}) relies on ${names.join(", ")}, but ${by.join(" and ")} — the leg cannot work without ${one ? "it" : "them"}. Remove the entry from the list that names it.`, "config.tool_face_control", "protocol_denied");
118
277
  }
119
- const delegationTools = (spec.tools ?? []).filter((t) => t.agentToolFaces !== undefined);
120
- const known = new Set();
121
- const add = (t) => {
122
- known.add(t.name);
123
- for (const a of t.aliases ?? [])
124
- known.add(a);
125
- };
126
- for (const t of tools)
127
- add(t);
128
- for (const t of ENGINE_TOOLS)
129
- add(t);
130
- for (const t of spec.tools ?? [])
131
- add(t);
132
- for (const t of delegationTools)
133
- for (const p of t.agentToolPool ?? [])
134
- add(p);
135
- const peers = new Set(PROTOCOL_TABLE.flatMap((ns) => (input.declaredPeers[ns.id] ?? []).map((p) => mintNamespacePrefix(ns, p.name))));
136
- for (const name of known) {
137
- const ns = protocolOf(name);
138
- const split = ns !== undefined ? splitNamespacedName(name) : undefined;
139
- if (split !== undefined && split.peer !== "")
140
- peers.add(mintNamespacePrefix(split.ns, split.peer));
141
- }
142
- const universe = { known, peers };
278
+ const pool = delegationPoolReading(spec.tools ?? [], compileToolSelector(input.layers, input.legs));
279
+ const mounted = mountedToolNames([...tools, ...(spec.tools ?? [])]);
143
280
  if (!input.judgedUpstream) {
144
- judgeToolNameList(input.ownWhere ?? "TaskSpec.allowTools", "allowed", input.own.allow, universe, advise, "config.tool_face_control");
145
- judgeToolNameList(input.ownWhere ?? "TaskSpec.excludeTools", "denied", input.own.exclude, universe, advise, "config.tool_face_control");
281
+ const root = rootFacingUniverse(input.ownBy !== undefined ? new Set([...mounted, input.ownBy.toolName]) : mounted, pool, input.declaredPeers);
282
+ const own = [["allowed", input.own.allow], ["denied", input.own.exclude]];
283
+ const tell = input.toldUpstream === true ? () => undefined : advise;
284
+ for (const [side, value] of own)
285
+ judgeToolNameList(ownWhere ?? (side === "allowed" ? "TaskSpec.allowTools" : "TaskSpec.excludeTools"), side, value, root, tell, "config.tool_face_control");
146
286
  }
287
+ const children = new Map(pool.tools.map((t) => [t, childFacingUniverse(poolOf([t]), input.declaredPeers)]));
288
+ const taskTargets = pool.tools.length > 0 ? [...children.values()] : [childFacingUniverse(poolOf([]), input.declaredPeers)];
147
289
  for (const def of spec.agents ?? []) {
148
- judgeToolNameList(`TaskSpec.agents: agent "${def.name}"`, "allowed", def.allowTools, universe, advise, "config.agent.unknown_tool");
149
- judgeToolNameList(`TaskSpec.agents: agent "${def.name}"`, "denied", def.denyTools, universe, advise, "config.agent.unknown_tool");
290
+ for (const universe of taskTargets) {
291
+ judgeToolNameList(`TaskSpec.agents: agent "${def.name}"`, "allowed", def.allowTools, universe, advise, "config.agent.unknown_tool");
292
+ judgeToolNameList(`TaskSpec.agents: agent "${def.name}"`, "denied", def.denyTools, universe, advise, "config.agent.unknown_tool");
293
+ }
150
294
  }
151
295
  const replaced = new Set((spec.agents ?? []).map((d) => d.name));
152
- for (const t of delegationTools) {
296
+ for (const [t, universe] of children) {
153
297
  for (const face of t.agentToolFaces ?? []) {
154
298
  if (face.authored === true && !replaced.has(face.name)) {
155
299
  const faceWhere = delegationListWhere(t.name, face.name);
@@ -161,3 +305,16 @@ export function judgeRunToolLists(input) {
161
305
  judgeToolNameList(delegationListWhere(t.name, undefined), "denied", t.agentToolLevelDenyTools, universe, advise, "config.agent.unknown_tool");
162
306
  }
163
307
  }
308
+ export function judgeHostToolCeiling(layers, agent, above = []) {
309
+ const where = (i) => `RunWorkflowOptions.toolCeiling, layer ${i + 1}`;
310
+ layers.forEach((layer, i) => readSelectorLayer(layer, where(i), () => where(i), false));
311
+ const callerTools = agent.tools ?? [];
312
+ const own = { ...(agent.allowTools != null ? { allow: agent.allowTools } : {}), deny: agent.excludeTools ?? [] };
313
+ const pool = delegationPoolReading(callerTools, compileToolSelector([...above, ...layers, own], ["delegated"]));
314
+ const universe = rootFacingUniverse(mountedToolNames(callerTools), pool, { mcp: undefined, a2a: undefined });
315
+ const unannounced = () => undefined;
316
+ layers.forEach((layer, i) => {
317
+ judgeToolNameList(where(i), "allowed", layer.allow, universe, unannounced, "config.tool_face_control");
318
+ judgeToolNameList(where(i), "denied", layer.deny, universe, unannounced, "config.tool_face_control");
319
+ });
320
+ }
@@ -17,7 +17,7 @@ import type { GateOutcome } from "../gate-outcome.js";
17
17
  import type { TaskEvent, TaskEventIdentity } from "../types.js";
18
18
  import type { OccurrenceStart, ToolOccurrenceTable } from "./tool-occurrence.js";
19
19
  import { type McpDelivered } from "../mcp-failure.js";
20
- import type { RecoveredOrphan } from "../session-reconcile.js";
20
+ import { type RecoveredOrphan } from "../session-reconcile.js";
21
21
  import { type StructuredProjector } from "./tool-output-projection.js";
22
22
  import type { WriteFamily } from "./turn-attachments.js";
23
23
  /** Derive the thin `response` digest from a harness tool result: model-facing text
@@ -77,16 +77,15 @@ export interface OccurrenceEnd {
77
77
  /** The harness result (or the exact text the closing station records). */
78
78
  result: unknown;
79
79
  isError: boolean;
80
- /** POSITIVE evidence the call never executed — the loop's own mark on a call it closed without running it, or the
81
- * reconcile's never-started class. Absent = no such evidence, never "it ran" (see `tool_end.notExecuted`). */
80
+ /** The CLOSING STATION's own POSITIVE evidence the call never executed — the loop's mark (a halted sibling may well have been let go by the gate) or the reconcile's never-started class; the engine's refusal is read off the occurrence. Absent = no such evidence, never "it ran". */
82
81
  notExecuted?: true;
83
82
  /** The call is closed by an interruption rather than a result of its own. */
84
83
  interrupted?: true;
85
84
  /** See {@link toolEndBodyFrom}'s parameter of the same name. */
86
85
  gatedCallIdOfRun?: string;
87
86
  }
88
- /** What the end projection decided, for the readers that ride beside the frame (the trace row, the row's record). */
89
- export interface SettledOccurrence {
87
+ /** What the end projection decided, for the readers that ride beside the frame (the trace row, the row's record, the rows' `notExecuted`). */
88
+ export interface SettledOccurrence extends Pick<OccurrenceEnd, "notExecuted"> {
90
89
  /** The latest gate record — the call's disposition (absent when no gate exit saw it). */
91
90
  gate?: GateOutcome;
92
91
  /** The tool's own error bit, or a refusal recorded after the gate: the engine's record wins over the tool's word. */
@@ -96,7 +95,7 @@ export interface SettledOccurrence {
96
95
  * The END projection — the ONE mint of `tool_end`, for every station that closes a call: the live loop's
97
96
  * `tool_execution_end`, the resumed leg's resolution, an interrupted run's reconcile and the reconcile replayed at
98
97
  * the next process start. It reads the occurrence and nothing else: `gate` is its latest gate record, `isError` is
99
- * the tool's bit or the refusal that record names, and `notExecuted` rides only on the station's positive evidence.
98
+ * the tool's bit or the refusal that record names, and `notExecuted` rides on positive evidence only: the station's own or `refusedBeforeRelease` (returned for the rows).
100
99
  *
101
100
  * A call whose start never went out on this stream (a call the loop never reached, a call announced by a dead
102
101
  * process) gets its start minted here first, marked `recovered` and carrying the input the closing station holds —
@@ -1,6 +1,8 @@
1
1
  import {} from "../../internal/harness.js";
2
2
  import { readMember } from "../thrown-value.js";
3
+ import { refusedBeforeRelease } from "./tool-not-executed.js";
3
4
  import { MCP_DELIVERY_VERDICTS } from "../mcp-failure.js";
5
+ import { closeNeverExecuted } from "../session-reconcile.js";
4
6
  import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
5
7
  import { toolOutputFrom } from "./tool-output-projection.js";
6
8
  import { TASK_CREATE_TOOL_NAME, TASK_UPDATE_TOOL_NAME, TODO_WRITE_TOOL_NAME } from "../tool-catalog-entries.js";
@@ -63,7 +65,7 @@ export function emitSettled(table, start, end, frames) {
63
65
  if (!occ.startEmitted)
64
66
  frames.push(startFrameOf(occ, true));
65
67
  const gate = occ.gateRounds.at(-1);
66
- const isError = end.isError || gate?.disposition.kind === "denied";
68
+ const isError = end.isError || gate?.disposition.kind === "denied", notExecuted = end.notExecuted === true || refusedBeforeRelease(occ);
67
69
  table.close(occ.toolCallId, end.interrupted === true ? "interrupted" : "settled");
68
70
  frames.push({
69
71
  type: "tool_end",
@@ -73,12 +75,12 @@ export function emitSettled(table, start, end, frames) {
73
75
  isError,
74
76
  ...toolEndBodyFrom(end.result, isError, frames, gate, end.gatedCallIdOfRun),
75
77
  ...(gate?.disposition.kind === "denied" && occ.wireName !== undefined ? { wireName: occ.wireName } : {}),
76
- ...(end.notExecuted === true ? { notExecuted: true } : {}),
78
+ ...(notExecuted ? { notExecuted: true } : {}),
77
79
  });
78
- return { ...(gate !== undefined ? { gate } : {}), isError };
80
+ return { ...(gate !== undefined ? { gate } : {}), isError, ...(notExecuted ? { notExecuted: true } : {}) };
79
81
  }
80
82
  export function reconciledEnd(orphan) {
81
- return { result: { content: orphan.text, details: { code: orphan.errorKind } }, isError: true, interrupted: true, ...(orphan.errorKind === "interrupted_never_started" ? { notExecuted: true } : {}) };
83
+ return { result: { content: orphan.text, details: { code: orphan.errorKind } }, isError: true, interrupted: true, ...(closeNeverExecuted(orphan.errorKind) ? { notExecuted: true } : {}) };
82
84
  }
83
85
  export function writeFamilyOfCanonical(name) {
84
86
  if (name === TASK_CREATE_TOOL_NAME || name === TASK_UPDATE_TOOL_NAME)
@@ -0,0 +1,15 @@
1
+ import type { ToolOccurrence } from "./tool-occurrence.js";
2
+ /** The engine's own judgment refused this call and it was never let go to an executor: the latest gate record is a
3
+ * refusal and the life never left `announced` — positive evidence the call did not run (a refusal recorded AFTER the
4
+ * release, by a station inside the execution, is not). A call whose start this stream never carried (a recovered
5
+ * one, closed by a station that knows only its own class) is not read here. */
6
+ export declare function refusedBeforeRelease(occ: Pick<ToolOccurrence, "life" | "gateRounds" | "startEmitted">): boolean;
7
+ /** `details` as a persisted `toolResult` row carries it: `notExecuted` is the ENGINE's key — the settlement's fact when it holds,
8
+ * absent otherwise, whatever a tool wrote under that name. Returns `details` itself unless the key has to move; never mutates it. */
9
+ export declare function rowDetailsOf(details: unknown, notExecuted: boolean): unknown;
10
+ /** A row the resumed leg mints (`toolResultMsg`) with the settlement's fact on it. */
11
+ export declare function withRowNotExecuted<T extends object>(row: T, notExecuted: boolean): T;
12
+ /** Write the end projection's one `notExecuted` onto the result the live loop persists as the call's row: the loop builds that message
13
+ * from this very result right after the end event. The frame's card was projected from the previous `details` first and keeps its
14
+ * members (a row mark is never part of a card). `Reflect.set`: a result that refuses the write keeps its row as it was. */
15
+ export declare function stampRowNotExecuted(result: unknown, notExecuted: boolean): void;
@@ -0,0 +1,30 @@
1
+ import { readOwnMembers } from "../thrown-value.js";
2
+ export function refusedBeforeRelease(occ) {
3
+ return occ.life === "announced" && occ.startEmitted && occ.gateRounds.at(-1)?.disposition.kind === "denied";
4
+ }
5
+ export function rowDetailsOf(details, notExecuted) {
6
+ const carries = (() => { try {
7
+ return details !== null && typeof details === "object" && !Array.isArray(details) && Object.keys(details).includes("notExecuted");
8
+ }
9
+ catch {
10
+ return false;
11
+ } })();
12
+ if (!notExecuted && !carries)
13
+ return details;
14
+ const bag = { ...readOwnMembers(details) };
15
+ if (notExecuted)
16
+ bag["notExecuted"] = true;
17
+ else
18
+ delete bag["notExecuted"];
19
+ return bag;
20
+ }
21
+ export function withRowNotExecuted(row, notExecuted) {
22
+ return notExecuted ? { ...row, details: rowDetailsOf(undefined, true) } : row;
23
+ }
24
+ export function stampRowNotExecuted(result, notExecuted) {
25
+ if (result === null || typeof result !== "object")
26
+ return;
27
+ const next = rowDetailsOf(result.details, notExecuted);
28
+ if (next !== result.details)
29
+ Reflect.set(result, "details", next);
30
+ }
@@ -6,10 +6,11 @@
6
6
  * `tool_progress` heartbeat (`tool-progress-lane.ts`), the trace row's `ok`, and the resumed leg's
7
7
  * `executionOutcome` filing. The record has three parts:
8
8
  *
9
- * - the EXECUTION LIFE, monotonic: `announced` (the start frame is out) → `executing` (the engine started the
10
- * tool body itself — the resumed leg; on a live leg the loop starts the body and the runner does not observe
11
- * that moment, so a live call stays `announced`) → `settled` or `interrupted`. Leaving a terminal state is an
12
- * engine defect and throws;
9
+ * - the EXECUTION LIFE, monotonic: `announced` (the start frame is out) → `released` (the engine let the call go
10
+ * to its executor: the resumed leg right before it runs the tool body itself, a live leg at the gate's allow
11
+ * exit — the loop starts the body after it and the runner does not observe that moment) → `settled` or
12
+ * `interrupted`. A call that closes still `announced` was never let go: whatever the tool's body did, this call
13
+ * did not do it (`refusedBeforeRelease`, `tool-not-executed.ts`). Leaving a terminal state is an engine defect and throws;
13
14
  * - the WAIT ON A PERSON, re-entrant: every ask put to the person's seat for this call counts one entry and one
14
15
  * exit (several rounds of one gate pass, a confirmation asked during the execution) — a count, never a bit,
15
16
  * because the waits nest and overlap;
@@ -28,7 +29,7 @@ import type { DeniedBy, GateOutcome } from "../gate-outcome.js";
28
29
  * host without `performance` falls back to the wall clock and inherits its rollback behaviour. */
29
30
  export declare const occurrenceClock: () => number;
30
31
  /** Where a call's execution life stands — see the module header for the order. */
31
- export type OccurrenceLife = "announced" | "executing" | "settled" | "interrupted";
32
+ export type OccurrenceLife = "announced" | "released" | "settled" | "interrupted";
32
33
  /** What opens an occurrence: the facts its start frame carries. */
33
34
  export interface OccurrenceStart {
34
35
  readonly toolCallId: string;
@@ -78,8 +79,8 @@ export interface ToolOccurrenceTable {
78
79
  ensure(start: OccurrenceStart): ToolOccurrence;
79
80
  /** The call's start frame is out. */
80
81
  noteStartEmitted(toolCallId: string): void;
81
- /** The engine starts the tool body. */
82
- execute(toolCallId: string): void;
82
+ /** The engine lets the call go to its executor — the one transition into `released`, whichever leg makes it. */
83
+ release(toolCallId: string): void;
83
84
  /** A gate exit for this call: its record and the input it decided on (frozen by the gate's exit). */
84
85
  gateRound(toolCallId: string, outcome: GateOutcome, finalInput?: unknown): void;
85
86
  /** A station after the gate refused the call: one more gate record, minted as a veto of whatever the latest one
@@ -1,6 +1,6 @@
1
1
  import { mintGateOutcome } from "./gate-exit.js";
2
2
  export const occurrenceClock = () => (typeof performance !== "undefined" ? performance.now() : Date.now());
3
- const TERMINAL = { announced: false, executing: false, settled: true, interrupted: true };
3
+ const TERMINAL = { announced: false, released: false, settled: true, interrupted: true };
4
4
  export function createToolOccurrenceTable() {
5
5
  const byId = new Map();
6
6
  const attempts = new Map();
@@ -77,11 +77,11 @@ export function createToolOccurrenceTable() {
77
77
  noteStartEmitted(toolCallId) {
78
78
  held(toolCallId, "a start frame").startEmitted = true;
79
79
  },
80
- execute(toolCallId) {
81
- const occ = held(toolCallId, "execute");
80
+ release(toolCallId) {
81
+ const occ = held(toolCallId, "release");
82
82
  if (occ.life !== "announced")
83
- throw new Error(`tool occurrence: call "${toolCallId}" (attempt ${occ.attempt}) cannot start executing from "${occ.life}"`);
84
- occ.life = "executing";
83
+ throw new Error(`tool occurrence: call "${toolCallId}" (attempt ${occ.attempt}) cannot be released from "${occ.life}"`);
84
+ occ.life = "released";
85
85
  },
86
86
  gateRound(toolCallId, outcome, finalInput) {
87
87
  const occ = held(toolCallId, "a gate round");
@@ -6,7 +6,7 @@
6
6
  * closed-set words, counts, the call's identity and a media type only when it can be one — never the bytes.
7
7
  */
8
8
  import { type EngineNotice } from "../engine-notice.js";
9
- import type { ToolResultMediaRefusal } from "../../tools/fs/image-admission.js";
9
+ import type { ToolResultMediaRefusal } from "../image-admission.js";
10
10
  /** The run's attribution pair, as every per-run notice carries it. */
11
11
  export interface ToolResultMediaSeatIds {
12
12
  sessionId: string;