@sema-agent/core 7.48.0 → 7.49.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 (418) hide show
  1. package/CHANGELOG.md +418 -0
  2. package/README.md +2 -2
  3. package/dist/agents/cascade.d.ts +2 -5
  4. package/dist/agents/child-failure.d.ts +20 -0
  5. package/dist/agents/child-failure.js +28 -0
  6. package/dist/agents/cross-session-ref.d.ts +11 -3
  7. package/dist/agents/cross-session-ref.js +6 -0
  8. package/dist/agents/cumulative-stats.d.ts +12 -15
  9. package/dist/agents/cumulative-stats.js +1 -1
  10. package/dist/agents/extra-tool-names.js +1 -1
  11. package/dist/agents/inherited-tool-pool.d.ts +17 -13
  12. package/dist/agents/inherited-tool-pool.js +4 -3
  13. package/dist/agents/peer-session-drain.d.ts +3 -2
  14. package/dist/agents/peer-session-drain.js +5 -4
  15. package/dist/agents/send-message-tool.js +2 -9
  16. package/dist/agents/subagent.d.ts +10 -29
  17. package/dist/agents/subagent.js +65 -53
  18. package/dist/agents/suspend-guard.d.ts +1 -1
  19. package/dist/agents/tool-filter.d.ts +76 -65
  20. package/dist/agents/tool-filter.js +122 -92
  21. package/dist/agents/verify.d.ts +4 -7
  22. package/dist/agents/verify.js +6 -7
  23. package/dist/bench/metrics.d.ts +1 -2
  24. package/dist/brain/anthropic.js +133 -79
  25. package/dist/brain/errors.d.ts +53 -6
  26. package/dist/brain/errors.js +22 -2
  27. package/dist/brain/input-too-long.d.ts +40 -19
  28. package/dist/brain/input-too-long.js +3 -16
  29. package/dist/brain/open-responses.js +19 -10
  30. package/dist/brain/openai.js +123 -32
  31. package/dist/brain/provider-dialect.d.ts +214 -0
  32. package/dist/brain/provider-dialect.js +186 -0
  33. package/dist/brain/request-params.d.ts +27 -8
  34. package/dist/brain/request-params.js +7 -12
  35. package/dist/brain/request-route.d.ts +84 -0
  36. package/dist/brain/request-route.js +41 -0
  37. package/dist/brain/retry.d.ts +8 -0
  38. package/dist/brain/retry.js +10 -4
  39. package/dist/brain/route-adjudicator.d.ts +7 -0
  40. package/dist/brain/route-adjudicator.js +5 -1
  41. package/dist/brain/status-sink.d.ts +20 -0
  42. package/dist/brain/status-sink.js +11 -0
  43. package/dist/brain/stream-engine.d.ts +52 -1
  44. package/dist/brain/stream-engine.js +81 -24
  45. package/dist/brain/stream-shared.d.ts +57 -11
  46. package/dist/brain/stream-shared.js +28 -2
  47. package/dist/core/a2a.d.ts +1 -0
  48. package/dist/core/agent-config-dir.js +3 -2
  49. package/dist/core/allow-entry.d.ts +21 -12
  50. package/dist/core/allow-entry.js +38 -31
  51. package/dist/core/arg-summary.d.ts +17 -24
  52. package/dist/core/arg-summary.js +169 -68
  53. package/dist/core/ask-origin.d.ts +42 -24
  54. package/dist/core/ask-origin.js +18 -13
  55. package/dist/core/auto-mode-prompt-assets.d.ts +2 -2
  56. package/dist/core/auto-mode-prompt-assets.js +1 -1
  57. package/dist/core/auto-mode-prompt.d.ts +11 -1
  58. package/dist/core/auto-mode-prompt.js +13 -10
  59. package/dist/core/auto-mode-rebuild.d.ts +7 -0
  60. package/dist/core/auto-mode-rebuild.js +4 -1
  61. package/dist/core/canonical-json.d.ts +3 -0
  62. package/dist/core/canonical-json.js +2 -1
  63. package/dist/core/checkpoint-requirements.d.ts +892 -0
  64. package/dist/core/checkpoint-requirements.js +333 -0
  65. package/dist/core/checkpoint-store.d.ts +94 -65
  66. package/dist/core/checkpoint-store.js +14 -26
  67. package/dist/core/child-clamp-seats.d.ts +228 -69
  68. package/dist/core/child-clamp-seats.js +211 -17
  69. package/dist/core/classifier-unavailable-arm.d.ts +5 -1
  70. package/dist/core/classifier-unavailable-arm.js +3 -3
  71. package/dist/core/compliance.d.ts +3 -0
  72. package/dist/core/compliance.js +9 -4
  73. package/dist/core/credential-vocabulary.d.ts +140 -0
  74. package/dist/core/credential-vocabulary.js +131 -0
  75. package/dist/core/decision-receipt.js +1 -1
  76. package/dist/core/effective-path-target.d.ts +73 -16
  77. package/dist/core/effective-path-target.js +63 -33
  78. package/dist/core/engine-notice.d.ts +32 -7
  79. package/dist/core/file-history-store.js +2 -2
  80. package/dist/core/fs-write-gate-policy.d.ts +4 -0
  81. package/dist/core/fs-write-gate-policy.js +22 -5
  82. package/dist/core/gate-bounded-await.d.ts +28 -13
  83. package/dist/core/gate-bounded-await.js +41 -4
  84. package/dist/core/gate-fold.d.ts +1 -1
  85. package/dist/core/gate-fold.js +24 -25
  86. package/dist/core/gate-lanes.js +16 -15
  87. package/dist/core/gate-outcome.d.ts +20 -5
  88. package/dist/core/gate-outcome.js +5 -0
  89. package/dist/core/governance-codes.d.ts +11 -4
  90. package/dist/core/governance-codes.js +14 -3
  91. package/dist/core/hooks.d.ts +4 -0
  92. package/dist/core/locked-config.d.ts +8 -0
  93. package/dist/core/locked-config.js +14 -2
  94. package/dist/core/lsp.js +3 -5
  95. package/dist/core/mailbox-store.d.ts +3 -4
  96. package/dist/core/mcp-connection-pool.d.ts +75 -0
  97. package/dist/core/mcp-connection-pool.js +128 -0
  98. package/dist/core/mcp-failure.d.ts +2 -1
  99. package/dist/core/mcp-failure.js +13 -2
  100. package/dist/core/mcp.d.ts +4 -0
  101. package/dist/core/mcp.js +70 -43
  102. package/dist/core/memory-engine/content-origin.js +2 -14
  103. package/dist/core/memory-engine/engine.d.ts +32 -22
  104. package/dist/core/memory-engine/engine.js +103 -55
  105. package/dist/core/memory-engine/file-backend.d.ts +13 -30
  106. package/dist/core/memory-engine/file-backend.js +37 -252
  107. package/dist/core/memory-engine/index.d.ts +1 -1
  108. package/dist/core/memory-engine/index.js +1 -1
  109. package/dist/core/memory-engine/layout.d.ts +139 -9
  110. package/dist/core/memory-engine/layout.js +350 -7
  111. package/dist/core/memory-engine/scope-contract.d.ts +10 -0
  112. package/dist/core/memory-engine/scope-contract.js +8 -0
  113. package/dist/core/memory-engine/session-incarnation.d.ts +14 -4
  114. package/dist/core/memory-engine/session-incarnation.js +5 -2
  115. package/dist/core/memory-engine/types.d.ts +10 -1
  116. package/dist/core/memory.js +5 -17
  117. package/dist/core/model-seat.d.ts +4 -4
  118. package/dist/core/model-seat.js +1 -1
  119. package/dist/core/mounted-mcp.d.ts +57 -0
  120. package/dist/core/mounted-mcp.js +19 -0
  121. package/dist/core/namespaced-rule-name.d.ts +29 -0
  122. package/dist/core/namespaced-rule-name.js +52 -0
  123. package/dist/core/park-selfcheck.d.ts +1 -1
  124. package/dist/core/park-selfcheck.js +16 -6
  125. package/dist/core/path-guard-rows.d.ts +99 -0
  126. package/dist/core/path-guard-rows.js +45 -0
  127. package/dist/core/path-identity.d.ts +22 -16
  128. package/dist/core/path-identity.js +10 -10
  129. package/dist/core/path-rule-binding.d.ts +2 -1
  130. package/dist/core/path-rule-binding.js +3 -1
  131. package/dist/core/pause-registry.d.ts +16 -2
  132. package/dist/core/pause-registry.js +6 -0
  133. package/dist/core/permission-rule-consent.js +0 -7
  134. package/dist/core/permission-rule-model.d.ts +68 -24
  135. package/dist/core/permission-rule-model.js +105 -24
  136. package/dist/core/permission-rule-store.js +2 -2
  137. package/dist/core/permission-rules.d.ts +3 -30
  138. package/dist/core/permission-rules.js +4 -54
  139. package/dist/core/read-root-candidate.d.ts +1 -3
  140. package/dist/core/read-root-candidate.js +4 -15
  141. package/dist/core/real-approval.d.ts +4 -3
  142. package/dist/core/retired-keys.d.ts +13 -0
  143. package/dist/core/retired-keys.js +14 -0
  144. package/dist/core/roles.d.ts +2 -3
  145. package/dist/core/roles.js +15 -6
  146. package/dist/core/runner/assemble-result.d.ts +0 -7
  147. package/dist/core/runner/assemble-result.js +19 -31
  148. package/dist/core/runner/caller-roster.d.ts +78 -0
  149. package/dist/core/runner/caller-roster.js +219 -0
  150. package/dist/core/runner/checkpoint-scope.d.ts +16 -16
  151. package/dist/core/runner/checkpoint-scope.js +2 -2
  152. package/dist/core/runner/child-tree.js +2 -2
  153. package/dist/core/runner/contracts.d.ts +68 -35
  154. package/dist/core/runner/decide-continuation.d.ts +51 -3
  155. package/dist/core/runner/decide-continuation.js +76 -21
  156. package/dist/core/runner/denial-limit-arms.d.ts +17 -5
  157. package/dist/core/runner/denial-limit-arms.js +11 -9
  158. package/dist/core/runner/extra-body-dropped-seat.d.ts +8 -0
  159. package/dist/core/runner/extra-body-dropped-seat.js +22 -0
  160. package/dist/core/runner/finish-reason-seat.d.ts +9 -0
  161. package/dist/core/runner/finish-reason-seat.js +40 -0
  162. package/dist/core/runner/gate-exit.d.ts +44 -32
  163. package/dist/core/runner/gate-exit.js +112 -25
  164. package/dist/core/runner/inherited-ask-grants.js +6 -4
  165. package/dist/core/runner/mcp-redial.d.ts +3 -3
  166. package/dist/core/runner/mcp-redial.js +3 -4
  167. package/dist/core/runner/permission-rule-lanes.d.ts +1 -2
  168. package/dist/core/runner/permission-rule-lanes.js +5 -4
  169. package/dist/core/runner/prepare-artifact.d.ts +4 -0
  170. package/dist/core/runner/prepare-artifact.js +2 -1
  171. package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
  172. package/dist/core/runner/prepare-ask-lane.js +12 -6
  173. package/dist/core/runner/prepare-boundary-parks.d.ts +5 -8
  174. package/dist/core/runner/prepare-boundary-parks.js +4 -7
  175. package/dist/core/runner/prepare-caps-and-workflow.d.ts +11 -15
  176. package/dist/core/runner/prepare-caps-and-workflow.js +19 -27
  177. package/dist/core/runner/prepare-config-doors.d.ts +9 -9
  178. package/dist/core/runner/prepare-config-doors.js +10 -5
  179. package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
  180. package/dist/core/runner/prepare-defer-classify.js +5 -3
  181. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
  182. package/dist/core/runner/prepare-delegation-surface.js +3 -3
  183. package/dist/core/runner/prepare-gate-stations.d.ts +4 -4
  184. package/dist/core/runner/prepare-gate-stations.js +9 -7
  185. package/dist/core/runner/prepare-hands-readface.d.ts +7 -7
  186. package/dist/core/runner/prepare-hands-readface.js +7 -7
  187. package/dist/core/runner/prepare-inherited-gate.d.ts +7 -6
  188. package/dist/core/runner/prepare-inherited-gate.js +14 -15
  189. package/dist/core/runner/prepare-listings.d.ts +7 -24
  190. package/dist/core/runner/prepare-listings.js +4 -5
  191. package/dist/core/runner/prepare-memory-engine-session.d.ts +2 -0
  192. package/dist/core/runner/prepare-memory-engine-session.js +4 -3
  193. package/dist/core/runner/prepare-memory.d.ts +6 -0
  194. package/dist/core/runner/prepare-memory.js +13 -13
  195. package/dist/core/runner/prepare-offload-wrappers.d.ts +5 -0
  196. package/dist/core/runner/prepare-offload-wrappers.js +3 -3
  197. package/dist/core/runner/prepare-park-ask.d.ts +7 -8
  198. package/dist/core/runner/prepare-park-ask.js +17 -10
  199. package/dist/core/runner/prepare-policy-chain.js +24 -44
  200. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  201. package/dist/core/runner/prepare-project-context.js +3 -4
  202. package/dist/core/runner/prepare-protocol-tools.d.ts +9 -3
  203. package/dist/core/runner/prepare-protocol-tools.js +3 -4
  204. package/dist/core/runner/prepare-run-refs.d.ts +3 -0
  205. package/dist/core/runner/prepare-run-refs.js +2 -1
  206. package/dist/core/runner/prepare-safety-scan.d.ts +4 -8
  207. package/dist/core/runner/prepare-safety-scan.js +1 -2
  208. package/dist/core/runner/prepare-suspend-saga.d.ts +7 -1
  209. package/dist/core/runner/prepare-suspend-saga.js +28 -12
  210. package/dist/core/runner/prepare-task.d.ts +1 -1
  211. package/dist/core/runner/prepare-task.js +67 -56
  212. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -10
  213. package/dist/core/runner/prepare-wiring-manifest.js +5 -8
  214. package/dist/core/runner/resume-apply.js +7 -14
  215. package/dist/core/runner/resume-checkpoint-screen.js +11 -4
  216. package/dist/core/runner/resume-internals-and-config.js +2 -2
  217. package/dist/core/runner/resume-policy-outcome.js +5 -32
  218. package/dist/core/runner/resume-review-outcome.js +4 -1
  219. package/dist/core/runner/retired-runner-deps.d.ts +6 -0
  220. package/dist/core/runner/retired-runner-deps.js +10 -1
  221. package/dist/core/runner/run-brain-sinks.d.ts +4 -0
  222. package/dist/core/runner/run-brain-sinks.js +5 -2
  223. package/dist/core/runner/run-harness-handlers.js +27 -34
  224. package/dist/core/runner/run-leg.d.ts +0 -2
  225. package/dist/core/runner/run-leg.js +32 -33
  226. package/dist/core/runner/run-terminal-adoption.js +4 -3
  227. package/dist/core/runner/runtask.js +8 -8
  228. package/dist/core/runner/session-rule-policy.js +5 -5
  229. package/dist/core/runner/stream-settle-backstop.js +7 -4
  230. package/dist/core/runner/sub-agent-deny-lists.d.ts +51 -12
  231. package/dist/core/runner/sub-agent-deny-lists.js +111 -80
  232. package/dist/core/runner/terminal-account.d.ts +48 -0
  233. package/dist/core/runner/terminal-account.js +186 -0
  234. package/dist/core/runner/tool-end-body.d.ts +54 -17
  235. package/dist/core/runner/tool-end-body.js +27 -2
  236. package/dist/core/runner/tool-face-overlay.d.ts +3 -1
  237. package/dist/core/runner/tool-face-overlay.js +6 -4
  238. package/dist/core/runner/tool-occurrence.d.ts +106 -0
  239. package/dist/core/runner/tool-occurrence.js +123 -0
  240. package/dist/core/runner/tool-progress-lane.d.ts +16 -8
  241. package/dist/core/runner/tool-progress-lane.js +25 -34
  242. package/dist/core/runner/turn-attachments.d.ts +1 -1
  243. package/dist/core/runner/write-protection-station.d.ts +21 -9
  244. package/dist/core/runner/write-protection-station.js +20 -17
  245. package/dist/core/runner-deps.d.ts +12 -22
  246. package/dist/core/secret-env.d.ts +16 -5
  247. package/dist/core/secret-env.js +23 -63
  248. package/dist/core/sensitive-path-policy.d.ts +19 -18
  249. package/dist/core/sensitive-path-policy.js +73 -49
  250. package/dist/core/session-reconcile.d.ts +3 -0
  251. package/dist/core/session-reconcile.js +5 -4
  252. package/dist/core/session-store.d.ts +5 -0
  253. package/dist/core/session-store.js +12 -0
  254. package/dist/core/session.d.ts +13 -0
  255. package/dist/core/shared-memory/normalize.d.ts +17 -4
  256. package/dist/core/shared-memory/normalize.js +7 -7
  257. package/dist/core/shell-lexer.js +5 -4
  258. package/dist/core/skill-grant.js +1 -1
  259. package/dist/core/store-contracts/checkpoint-store-contract.js +13 -0
  260. package/dist/core/store-contracts/mailbox-store-contract.d.ts +7 -14
  261. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +27 -0
  262. package/dist/core/store-contracts/session-store-incarnation-contract.js +92 -0
  263. package/dist/core/stub-env.d.ts +2 -0
  264. package/dist/core/stub-env.js +1 -0
  265. package/dist/core/synthetic-read-verdict.js +2 -2
  266. package/dist/core/task-event.d.ts +59 -5
  267. package/dist/core/task-result.d.ts +1 -1
  268. package/dist/core/task-spec.d.ts +42 -9
  269. package/dist/core/terminal-cause.d.ts +94 -11
  270. package/dist/core/tighten-task-spec.d.ts +5 -4
  271. package/dist/core/tighten-task-spec.js +20 -25
  272. package/dist/core/tool-catalog-entries.d.ts +5 -7
  273. package/dist/core/tool-catalog-entries.js +14 -15
  274. package/dist/core/tool-face.d.ts +47 -0
  275. package/dist/core/tool-face.js +5 -0
  276. package/dist/core/tool-model-gate.d.ts +5 -5
  277. package/dist/core/tool-name-aliases.d.ts +26 -2
  278. package/dist/core/tool-name-aliases.js +13 -0
  279. package/dist/core/tool-name-list.d.ts +30 -0
  280. package/dist/core/tool-name-list.js +55 -0
  281. package/dist/core/tool-name-readings.d.ts +1 -9
  282. package/dist/core/tool-name-readings.js +1 -3
  283. package/dist/core/tool-policy.d.ts +195 -85
  284. package/dist/core/tool-policy.js +165 -380
  285. package/dist/core/tool-registry.d.ts +44 -1
  286. package/dist/core/tool-registry.js +26 -0
  287. package/dist/core/tool-roster.d.ts +19 -1
  288. package/dist/core/tool-roster.js +21 -2
  289. package/dist/core/tool-spec-members.d.ts +45 -0
  290. package/dist/core/tool-spec-members.js +169 -0
  291. package/dist/core/tool-spec.d.ts +46 -8
  292. package/dist/core/tools.d.ts +6 -2
  293. package/dist/core/tools.js +8 -4
  294. package/dist/core/untrusted-egress.js +29 -26
  295. package/dist/core/untrusted-text.d.ts +13 -10
  296. package/dist/core/untrusted-text.js +6 -44
  297. package/dist/core/workflow-journal-store.d.ts +4 -1
  298. package/dist/core/write-protect.d.ts +65 -29
  299. package/dist/core/write-protect.js +235 -177
  300. package/dist/engine/compaction/compaction.d.ts +2 -2
  301. package/dist/engine/execution-env/node-execution-env.d.ts +5 -23
  302. package/dist/engine/execution-env/node-execution-env.js +16 -38
  303. package/dist/engine/execution-env/symlink-chain.d.ts +23 -0
  304. package/dist/engine/execution-env/symlink-chain.js +32 -0
  305. package/dist/engine/harness/types.d.ts +7 -0
  306. package/dist/engine/llm/tool-turns.d.ts +3 -0
  307. package/dist/engine/llm/tool-turns.js +1 -0
  308. package/dist/engine/llm/types.d.ts +59 -23
  309. package/dist/engine/loop/agent-loop.js +16 -2
  310. package/dist/engine/session/memory-repo.js +11 -3
  311. package/dist/engine/session/repo-utils.d.ts +8 -0
  312. package/dist/engine/session/repo-utils.js +13 -0
  313. package/dist/index.d.ts +20 -23
  314. package/dist/index.js +17 -20
  315. package/dist/internal/harness.d.ts +1 -1
  316. package/dist/internal/harness.js +1 -1
  317. package/dist/internal/llm.d.ts +2 -2
  318. package/dist/internal/llm.js +1 -1
  319. package/dist/orchestration/governance-baseline-validity.js +1 -1
  320. package/dist/orchestration/run-spec.js +3 -3
  321. package/dist/orchestration/run-workflow-tool.d.ts +16 -35
  322. package/dist/orchestration/run-workflow-tool.js +38 -78
  323. package/dist/orchestration/workflow-governance.d.ts +21 -25
  324. package/dist/orchestration/workflow-governance.js +38 -78
  325. package/dist/orchestration/workflow-primitives.d.ts +7 -22
  326. package/dist/orchestration/workflow-primitives.js +5 -12
  327. package/dist/orchestration/workflow-types.d.ts +1 -1
  328. package/dist/orchestration/workflow.d.ts +61 -11
  329. package/dist/orchestration/workflow.js +189 -117
  330. package/dist/scenarios/full-body.d.ts +1 -1
  331. package/dist/shell/lex.d.ts +0 -21
  332. package/dist/shell/lex.js +250 -68
  333. package/dist/shell/shell-words.d.ts +35 -0
  334. package/dist/shell/shell-words.js +65 -0
  335. package/dist/stores/file/checkpoint-store.d.ts +5 -5
  336. package/dist/stores/file/checkpoint-store.js +5 -2
  337. package/dist/stores/file/index.d.ts +0 -8
  338. package/dist/stores/file/index.js +0 -12
  339. package/dist/stores/file/session-store.d.ts +3 -0
  340. package/dist/stores/file/session-store.js +44 -19
  341. package/dist/tools/artifact/artifact-tool.js +2 -0
  342. package/dist/tools/fs/bash-program-position.d.ts +10 -3
  343. package/dist/tools/fs/bash-program-position.js +21 -9
  344. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  345. package/dist/tools/fs/bash-readonly-classifier.js +8 -7
  346. package/dist/tools/fs/containment-face.d.ts +4 -0
  347. package/dist/tools/fs/containment-face.js +3 -0
  348. package/dist/tools/fs/fs-shared.d.ts +10 -0
  349. package/dist/tools/fs/fs-shared.js +18 -0
  350. package/dist/tools/fs/fs-write.js +4 -2
  351. package/dist/tools/fs/index.d.ts +1 -1
  352. package/dist/tools/fs/program-read-model.d.ts +39 -18
  353. package/dist/tools/fs/program-read-model.js +29 -19
  354. package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
  355. package/dist/tools/fs/program-read-model.rows.js +5 -5
  356. package/dist/tools/fs/read-deny-home.d.ts +33 -47
  357. package/dist/tools/fs/read-deny-home.js +35 -49
  358. package/dist/tools/fs/read-deny.d.ts +23 -22
  359. package/dist/tools/fs/read-deny.js +114 -89
  360. package/dist/tools/fs/removal-targets.d.ts +56 -23
  361. package/dist/tools/fs/removal-targets.js +65 -87
  362. package/dist/tools/fs/safety.d.ts +29 -22
  363. package/dist/tools/fs/safety.js +17 -11
  364. package/dist/tools/fs/search.js +10 -8
  365. package/dist/tools/fs/shell-read-argv.js +1 -1
  366. package/dist/tools/fs/shell-read-bindings.d.ts +169 -37
  367. package/dist/tools/fs/shell-read-bindings.js +324 -86
  368. package/dist/tools/fs/shell-read-builtins.d.ts +35 -0
  369. package/dist/tools/fs/shell-read-builtins.js +43 -0
  370. package/dist/tools/fs/shell-read-check.d.ts +15 -1
  371. package/dist/tools/fs/shell-read-check.js +51 -42
  372. package/dist/tools/fs/shell-read-establish.js +6 -6
  373. package/dist/tools/fs/shell-read-functions.d.ts +22 -0
  374. package/dist/tools/fs/shell-read-functions.js +29 -0
  375. package/dist/tools/fs/shell-read-judge.d.ts +48 -18
  376. package/dist/tools/fs/shell-read-judge.js +33 -4
  377. package/dist/tools/fs/shell-read-plan.d.ts +12 -6
  378. package/dist/tools/fs/shell-read-plan.js +118 -113
  379. package/dist/tools/fs/shell-read-start.d.ts +29 -6
  380. package/dist/tools/fs/shell-read-start.js +16 -9
  381. package/dist/tools/fs/shell-read-word.js +4 -0
  382. package/dist/tools/fs/shell-read-workdir.d.ts +156 -0
  383. package/dist/tools/fs/shell-read-workdir.js +281 -0
  384. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -2
  385. package/dist/tools/fs/shell-read-write-targets.js +14 -5
  386. package/dist/tools/fs/shell-reentry-text.d.ts +4 -6
  387. package/dist/tools/fs/shell-reentry-text.js +3 -11
  388. package/dist/tools/task-list.d.ts +1 -2
  389. package/dist/tools/web.js +1 -1
  390. package/package.json +4 -1
  391. package/test/export-surface.snapshot.json +107 -143
  392. package/wire-changes/core@7.49.0.json +4765 -0
  393. package/dist/agents/teacher.d.ts +0 -159
  394. package/dist/agents/teacher.js +0 -577
  395. package/dist/agents/team.d.ts +0 -226
  396. package/dist/agents/team.js +0 -257
  397. package/dist/core/strategy-store.d.ts +0 -238
  398. package/dist/core/strategy-store.js +0 -253
  399. package/dist/orchestration/builtin-workflows.d.ts +0 -133
  400. package/dist/orchestration/builtin-workflows.js +0 -159
  401. package/dist/scenarios/env.d.ts +0 -40
  402. package/dist/scenarios/env.js +0 -39
  403. package/dist/scenarios/scenario-registry.d.ts +0 -110
  404. package/dist/scenarios/scenario-registry.js +0 -140
  405. package/dist/scenarios/teacher-quickstart.d.ts +0 -40
  406. package/dist/scenarios/teacher-quickstart.js +0 -12
  407. package/dist/stores/cc/lockfile.d.ts +0 -10
  408. package/dist/stores/cc/lockfile.js +0 -98
  409. package/dist/stores/cc/mailbox-store.d.ts +0 -20
  410. package/dist/stores/cc/mailbox-store.js +0 -255
  411. package/dist/stores/cc/roster-adapter.d.ts +0 -12
  412. package/dist/stores/cc/roster-adapter.js +0 -117
  413. package/dist/stores/cc/sidecar-transcript.d.ts +0 -97
  414. package/dist/stores/cc/sidecar-transcript.js +0 -264
  415. package/dist/stores/cc/task-list-store.d.ts +0 -2
  416. package/dist/stores/cc/task-list-store.js +0 -125
  417. package/dist/stores/file/strategy-store.d.ts +0 -97
  418. package/dist/stores/file/strategy-store.js +0 -341
@@ -1,4 +1,4 @@
1
- import { describeThrown, engineSettlementOf, refuseOutOfContractDecision } from "./tool-policy.js";
1
+ import { attestationOf, describeThrown, refuseOutOfContractDecision } from "./tool-policy.js";
2
2
  import { skillRowRefusal } from "./skill-grant.js";
3
3
  import { applyPersistedTightening } from "./persisted-rule-arms.js";
4
4
  import { readOrgRuleLane, readPersistedRuleLane } from "./rule-lane-reads.js";
@@ -62,7 +62,7 @@ export function createSyntheticReadJudge(layers) {
62
62
  }
63
63
  if (read.action !== "deny")
64
64
  return undefined;
65
- if (engineSettlementOf(d, req) !== undefined) {
65
+ if (attestationOf(read, req)?.settlement !== undefined) {
66
66
  cannot(layer, "approval_seat", "the policy would ask an approver, who is never consulted for a visibility probe");
67
67
  return undefined;
68
68
  }
@@ -9,6 +9,7 @@
9
9
  import type { ActorAssertion } from "../internal/llm.js";
10
10
  import type { TaskResult } from "./task-result.js";
11
11
  import type { TaskNotificationPayload } from "./task-notification.js";
12
+ import type { ProviderCondition, ProviderConditionSource } from "../brain/provider-dialect.js";
12
13
  /**
13
14
  * Identity fields carried by every CONTENT event of a task stream (design/99 §E2 — lightweight message
14
15
  * identity). Both are OPTIONAL and ADDITIVE; a consumer that ignores them is unchanged.
@@ -198,6 +199,25 @@ export interface BrainStatus {
198
199
  * `control_request_progress` frame, where CC itself makes the key optional.
199
200
  */
200
201
  errorStatus?: number;
202
+ /**
203
+ * What the provider said about the account or its capacity on the response being retried — one word of
204
+ * `PROVIDER_CONDITIONS`, judged off the provider dialect table keyed by where the request went
205
+ * (`brain/provider-dialect.ts`). Fills the gap {@link errorStatus} states: {@link errClass} `rate_limit`
206
+ * buckets a 429 that clears on its own with one the provider pinned on the account. Only the RETRYABLE words
207
+ * ride a retry frame (`throttled` / `overloaded` / `unknown`): a response judged `quota_exhausted` or
208
+ * `stop_other` is not retried — its word rides the terminal (`terminal.detail.providerCondition`) — unless
209
+ * the provider's own `x-should-retry: true` sent it back into the ladder, in which case the frame states the
210
+ * word the response was judged by.
211
+ *
212
+ * PRESENCE — exactly the connect-ladder backoff frames that carry {@link errorStatus}: a response named the
213
+ * failure. ABSENT on a transport failure, a stall, a mid-stream tear, the output-cap re-send (a re-shaped
214
+ * request, not a provider condition), `circuit_open`, and the terminal `recovered` / `gave_up` frames. A
215
+ * consumer reads absence as "not judged" — the same reading as `unknown`: back off, do not claim a quota.
216
+ */
217
+ providerCondition?: ProviderCondition;
218
+ /** Which evidence {@link providerCondition} rests on (`unified_header` / `provider_code` / `status`). Present
219
+ * exactly when it is. */
220
+ providerConditionSource?: ProviderConditionSource;
201
221
  }
202
222
  /**
203
223
  * design/97 CORE-8 (③): one lightweight TOOL-ACTIVITY beat surfaced from a running task, for a per-agent live
@@ -282,9 +302,19 @@ export type TaskEvent = ({
282
302
  } & TaskEventIdentity) | ({
283
303
  type: "tool_start";
284
304
  toolCallId: string;
285
- toolName: string; /** {@link ToolSpec.label} when distinct from `toolName` (design/71 liveness); omitted otherwise. */
305
+ toolName: string;
306
+ /** {@link ToolSpec.label} when distinct from `toolName`; omitted otherwise. */
286
307
  label?: string;
287
308
  args: unknown;
309
+ /**
310
+ * @contract tool_start.recovered — `true` on a start frame the engine minted at the moment it CLOSED a call
311
+ * whose start never went out on this stream: an interrupted run's reconcile closing a call the loop never
312
+ * reached, or a resumed process closing a call a dead process had announced. Every `tool_end` is preceded by
313
+ * a `tool_start` with the same id on the same stream — this frame is how that holds for those closes. `args`
314
+ * is the call's input as the transcript recorded it. It asserts NOTHING about execution (whether the call
315
+ * ran is the paired `tool_end.notExecuted`); absent on every start the loop announced itself.
316
+ */
317
+ recovered?: true;
288
318
  } & TaskEventIdentity) | ({
289
319
  /**
290
320
  * A LIVENESS TICK for a tool call that is still running. Emitted on the run's own queue between this
@@ -477,17 +507,41 @@ export type TaskEvent = ({
477
507
  * denied by which LAYER), `settlement` (how the ask this pass consumed ENDED — a person's yes or
478
508
  * no with the channel's attribution and, on a bare "no", no note; a window that elapsed and whose;
479
509
  * nobody reachable; the wait aborted…) and `origin` (who ASKED). It is the SAME object the
480
- * `permissionDenied` observer received and the engine's per-call sideband recorded — one mint, three
510
+ * `permissionDenied` observer received and the call's occurrence record holds — one mint, three
481
511
  * faces — so a renderer never branches on prose to tell "a person refused" from "the window closed".
482
512
  *
483
513
  * WHERE IT COMES FROM — never inferred here: the gate mints it at its exit, the decide lane mints it
484
514
  * from the host's decision facts joined with the row's persisted origin.
485
515
  *
486
- * ABSENT on every frame of a call the gate never saw (a tool call on an unarmed gate, a deferred
487
- * re-issue, a reconcile-recovered orphan). A gated call's frame ALWAYS carries it — an absence is
488
- * not a fact about the call, and a consumer must not read one out of it.
516
+ * THE LATEST RECORD: a station that refuses the call AFTER its gate exit (the shell read admission that
517
+ * re-establishes what the gate judged and finds it moved, a resumed leg's re-checks) adds one more
518
+ * record, minted as a veto — the refusing layer is the disposition, an approval already given stays on
519
+ * the settlement — and this member is that record. A call refused after its gate allowed it therefore
520
+ * reads `denied`, never `allowed` beside an error, and {@link isError} is `true` whatever the tool's own
521
+ * result said.
522
+ *
523
+ * ABSENT on every frame of a call no gate exit on this stream saw (a tool call on an unarmed gate, a
524
+ * deferred re-issue, a call a reconcile closed that never reached a gate here). A gated call's frame
525
+ * ALWAYS carries it — an absence is not a fact about the call, and a consumer must not read one out of it.
489
526
  */
490
527
  gate?: import("./gate-outcome.js").GateOutcome;
528
+ /**
529
+ * @contract tool_end.wireName — on a REFUSED call's frame only (`gate.disposition.kind === "denied"`): the name of
530
+ * the tool the call dispatched to on the live roster — the name the gate judged and the deny observer's `toolName`
531
+ * — where {@link toolName} is the model's spelling (an alias, a retired name the roster still answers to). A
532
+ * refusals list keyed on the tool reads this member; absent on every other frame, and on a refused frame whose
533
+ * station did not know the roster name.
534
+ */
535
+ wireName?: string;
536
+ /**
537
+ * @contract tool_end.notExecuted — `true` when the engine has POSITIVE evidence this call never executed:
538
+ * the loop closed it without running it (a sibling settled by a halted batch, a call a turn interrupt
539
+ * reached before it started) or an interrupted run's reconcile closed a call this run never started.
540
+ * ABSENT means only that no such evidence exists — never "it ran": a call closed with its outcome unknown
541
+ * (a reconcile at the next process start, a call interrupted mid-execution) carries no mark, because
542
+ * telling a consumer it did not run could invite a replay of a write that already took effect.
543
+ */
544
+ notExecuted?: true;
491
545
  /**
492
546
  * design/99 §E1 — `true` when {@link output} was SIZE-bounded by core (the full body exceeded the cap and
493
547
  * was degraded to a truncated string). Lets a consumer detect truncation programmatically instead of
@@ -11,7 +11,7 @@ import type { NestedUsage } from "./tool-spec.js";
11
11
  * `"suspended"` (design/45) is a **non-terminal** outcome: the task hit a durable tool-policy gate
12
12
  * (`ask` with a `CheckpointStore` wired) and persisted a checkpoint instead of finishing — resume it
13
13
  * with `runner.resume(checkpointToken, outcome)`. v1 contract: it is only safe at the **top-level
14
- * `runTask` boundary**; the orchestrators (cascade/teacher/verify/team) map an unexpected `suspended`
14
+ * `runTask` boundary**; the orchestrators (cascade/verify/repair-loop) map an unexpected `suspended`
15
15
  * to `failed` + `errorCode:"unexpected.suspended"` and pass the token up (§11 Q6, hard boundary).
16
16
  *
17
17
  * `"needs_review"` (design/76 §2.5, dry-run / shadow; design/80 D-B, plan-gate) is also a **non-terminal,
@@ -426,15 +426,34 @@ export interface TaskSpec {
426
426
  */
427
427
  tools?: ToolSpec[];
428
428
  /**
429
- * per-scenario tool ROSTER (true unmount, NOT a permission gate): wire names listed here
430
- * are removed from the mounted tool set AFTER assembly — their schemas never reach `tools[]`
431
- * (unlike a policy deny, which gates calls but still ships the schema bytes every request —
432
- * measured at zero token savings). Exact wire-name match (aliases resolve to their
433
- * canonical wire name at mount, so list the canonical name). Unknown names are ignored (a
434
- * center-distributed roster may cover tools this deployment doesn't mount). The injected
435
- * ToolSearch cannot be excluded (it mounts after this filter, only when deferred tools exist).
436
- * ⚠️ Excluding structural tools (submit_output / ReportBlocked) can strand the task — the caller
437
- * owns that risk, same trust level as assembling `spec.tools` itself.
429
+ * The run's tool ALLOWLIST — the availability selector (true unmount, NOT a permission gate): when set, the run may use only the
430
+ * tools it names, out of everything the engine would otherwise mount for it (the caller's tools, the hands, the engine's own
431
+ * tools, MCP and A2A tools, scenario and leg tools); the rest never reach `tools[]`. Omitted ⇒ the default set (a delegated
432
+ * child: whatever its parent could use). `[]` ⇒ no tools. An entry `"*"` ⇒ no allowlist on this layer (still bounded by the
433
+ * ceiling a spawning run handed down — a child's `"*"` never widens its parent).
434
+ *
435
+ * Entries are read like {@link excludeTools}: a wire name or an alias it declares, `mcp__srv` / `mcp__srv__*` for every tool
436
+ * of a server (including tools it lists later), a glob over the tool segment. The engine resolves the list against its OWN
437
+ * roster at prepare — a host passes the names it was given and never computes a complement. A name no tool answers to, a
438
+ * protocol-wide `mcp__*`, or an entry that is not a string is REFUSED at prepare (`config.tool_face_control`). The same
439
+ * compiled selector is applied on every later roster change (an MCP refresh or re-dial, the deferred-search lane): an exact name
440
+ * admits no new tool. Deferral is reached through ToolSearch, so a list that leaves it out defers nothing.
441
+ *
442
+ * A tool a leg relies on (a delegated child's ReportBlocked, a teammate's SendMessage, a goal iteration's DeclareDone, an
443
+ * observer's report tool — the catalog's protocol column) is not an allowlist's to name; a denylist that reaches one on such a
444
+ * leg is refused at prepare.
445
+ */
446
+ allowTools?: string[];
447
+ /**
448
+ * per-scenario tool ROSTER removal (true unmount, NOT a permission gate): the tools this list names are removed from the
449
+ * mounted tool set AFTER assembly — their schemas never reach `tools[]` (unlike a policy deny, which gates calls but still
450
+ * ships the schema bytes every request — measured at zero token savings). The DENY half of the run's tool selector, read
451
+ * exactly as a sub-agent's denied tools are: a wire name or an alias it declares; `Tool(content)` names the whole `Tool`;
452
+ * `mcp__srv` / `mcp__srv__*` / a glob over the tool segment for a server's tools, `mcp__*` for every MCP tool (including tools
453
+ * a server lists later). A name no tool answers to, `"*"`, or an entry that is not a string is REFUSED at prepare
454
+ * (`config.tool_face_control`) — a misspelt exclusion guards nothing. A deny wins over {@link allowTools}.
455
+ * ⚠️ A list that reaches a tool this kind of leg relies on (see {@link allowTools}) is refused; excluding a host-requested
456
+ * structural tool (the structured-output tool) can still strand the task — the caller owns that risk.
438
457
  */
439
458
  excludeTools?: string[];
440
459
  /**
@@ -854,6 +873,20 @@ export interface TaskSpec {
854
873
  * (#123). The write faces are untouched.
855
874
  */
856
875
  readDenyPatterns?: readonly import("../tools/fs/read-deny.js").ReadDenyEntry[];
876
+ /**
877
+ * TASK-layer rows ADDED to the write-protection table ({@link readDenyPatterns}' law): the run's table is the deployment's
878
+ * ({@link RunnerDeps.writeProtectedPaths}) UNION these; `[]` ≡ absent; no task-layer value removes a deployment row. A
879
+ * covered write is the judge's mandated ask (`write_protection`), as for a deployment row. Bad shapes refuse at prepare
880
+ * (`config.invalid_argument`); rides every engine-built child spec add-only.
881
+ */
882
+ writeProtectedAdditions?: readonly import("./write-protect.js").WriteProtectedEntry[];
883
+ /**
884
+ * TASK-layer denies ADDED to the compliance posture ({@link readDenyPatterns}' law): the run's deny set is the deployment's
885
+ * ({@link RunnerDeps.compliancePostureResolver}) UNION these; `[]` ≡ absent; nothing here removes a deployment deny. A hit
886
+ * is refused exactly as a deployment deny is. Members outside the closed capability set refuse at prepare
887
+ * (`config.invalid_argument`) — never a silently dropped deny; rides every engine-built child spec add-only.
888
+ */
889
+ complianceAdditionalDenies?: readonly import("./compliance.js").ComplianceCapability[];
857
890
  /**
858
891
  * design/199 件A — the TASK layer's read-face containment declaration
859
892
  * ({@link import("../tools/fs/containment-face.js").ContainmentFace}). "roots" pins this task (and, via the
@@ -9,6 +9,9 @@
9
9
  * `runner/terminal-projection.ts` — the one cause→plane derivation.
10
10
  */
11
11
  import type { AssertAllKeysHandled } from "./ask-origin.js";
12
+ import type { ProviderCondition, ProviderConditionSource } from "../brain/provider-dialect.js";
13
+ import type { RequestRoute } from "../brain/request-route.js";
14
+ import type { ModelOutputInvalidCause, OutputLimit } from "../internal/llm.js";
12
15
  /**
13
16
  * WHY a run ended — ONE tagged cause, and the ONLY terminal record a {@link TaskResult} carries. The run
14
17
  * loop's terminal arbitration used to decide a `status` word plus seven optional plane fields one by one
@@ -32,6 +35,14 @@ import type { AssertAllKeysHandled } from "./ask-origin.js";
32
35
  */
33
36
  export type TerminalCause = {
34
37
  kind: "completed";
38
+ /**
39
+ * A machine-readable account beside a completion, for the one shape that completes yet may be short:
40
+ * `finish_reason_unknown` — the final turn's provider ended it with a stream-end word the brain does not read
41
+ * (`AssistantMessage.finishReasonUnknown`); the turn was kept as the model left it, and the run announced the
42
+ * word (`route.finish_reason_unknown`). PRESENCE: only when the final assistant message carried that seat and the
43
+ * run completed on it; every other completion leaves it absent.
44
+ */
45
+ detail?: TerminalCompletedDetail;
35
46
  } | {
36
47
  kind: "failed";
37
48
  /**
@@ -69,14 +80,16 @@ export type TerminalCause = {
69
80
  /** Human-readable failure text. */
70
81
  message?: string;
71
82
  /**
72
- * A machine-readable account of the failure beside `code`, for the one shape a consumer offers its own way out
73
- * of: `provider_rejected` — the provider REFUSED the request as sent (a 4xx answer) and that refusal named this
74
- * result. `status` is the refusal's HTTP status (`TaskResult.apiFailure.status`, the same value);
75
- * `code` the provider's own word for it from its error envelope (`AssistantMessage.apiErrorCode`), when it
76
- * stated one. PRESENCE: only on that terminal — a run whose higher-ranked cause named the result, a 5xx, a
77
- * connect / stream failure, an in-band error inside a 200 and every non-provider failure leave it absent.
78
- * A 401 / 403 (credentials) and a 429 (rate limit) are refusals too: branch on `status` (or on `code` above)
79
- * to tell a request a consumer may change and resend from one it may not.
83
+ * A machine-readable account of the failure beside `code` — the fact the failure's mint point stated, projected
84
+ * once ({@link TerminalFailureDetail}; branch on `detail.kind`, never on `message`):
85
+ * - `provider_rejected` — the provider REFUSED the request as sent (a 4xx answer) and that refusal named this
86
+ * result. `status` is the refusal's HTTP status (`TaskResult.apiFailure.status`, the same value); `code` the
87
+ * provider's own word (`AssistantMessage.apiErrorCode`); `route` the request's route. A run whose
88
+ * higher-ranked cause named the result, a 5xx, a connect / stream failure and an in-band error inside a 200
89
+ * carry none. A 401 / 403 (credentials) and a 429 (rate limit) are refusals too: branch on `status`.
90
+ * - `coded` — the run threw a coded error (this `code`) whose throw site stated a structured `detail`.
91
+ * - `model_output_invalid` — the model's final turn failed because its tool calls could not be executed.
92
+ * PRESENCE: only when the arm that named this result has such a fact to project; absent otherwise.
80
93
  */
81
94
  detail?: TerminalFailureDetail;
82
95
  /**
@@ -144,14 +157,84 @@ export type TerminalCause = {
144
157
  */
145
158
  restoreMode?: "snapshot" | "park_only";
146
159
  };
147
- /** The machine-readable account a `failed` cause may carry ({@link TerminalCause}). `kind` is closed; one member. */
148
- export interface TerminalFailureDetail {
160
+ /** The machine-readable account a `completed` cause may carry ({@link TerminalCause}). `kind` is closed; one member. */
161
+ export interface TerminalCompletedDetail {
162
+ kind: "finish_reason_unknown";
163
+ /** The provider's stream-end word, in the provider echo shape (`?` when it had bytes outside it). */
164
+ finishReasonUnknown: string;
165
+ }
166
+ /**
167
+ * The machine-readable account a `failed` cause may carry ({@link TerminalCause}) — the ONE projection of a fact the
168
+ * failure's mint point already stated, carried as it was stated (a missing fact stays missing; nothing is re-derived
169
+ * from the message). `kind` is closed:
170
+ * - `provider_rejected` — the provider REFUSED the request as sent (a 4xx answer) and that refusal named the result:
171
+ * the terminal message's refusal account (`apiErrorStatus` / `apiErrorCode` / the provider-condition verdict / the
172
+ * request's `apiErrorRoute`).
173
+ * - `coded` — a failure THROWN with a machine code whose throw site stated a structured `detail` beside it (e.g. a
174
+ * `CheckpointError`'s `{ reason }` on `resume.parent_constraint_mismatch`): that detail, as a JSON-plain snapshot.
175
+ * `terminal.code` is the same error's code.
176
+ * - `model_output_invalid` — the model's turn failed because its tool calls could not be executed: the terminal
177
+ * message's `modelOutputInvalid` account.
178
+ * New members join with a minor; a consumer that branches on `kind` keeps a default arm.
179
+ */
180
+ export type TerminalFailureDetail = {
149
181
  kind: "provider_rejected";
150
182
  /** The refusal's HTTP status, 400–499. */
151
183
  status: number;
152
184
  /** The provider's own word for the refusal (its error envelope's `error.code`, else `error.type`), when stated. */
153
185
  code?: string;
154
- }
186
+ /**
187
+ * What the refusal says about the account — the engine's closed word for it (`PROVIDER_CONDITIONS`), judged off
188
+ * the provider dialect table by where the request went: `quota_exhausted` (stop; someone must act or a window
189
+ * must reset), `throttled` / `overloaded` / `unknown` (a retryable condition the retry budget ran out on, or the
190
+ * provider's own `x-should-retry: false` stopped), `stop_other` (credentials, model access, the request itself).
191
+ * The terminal's reading of the same verdict the call's retry frames carried. Present whenever the terminal
192
+ * message states one (`AssistantMessage.providerCondition`); a host-minted message that did not judge leaves it
193
+ * absent.
194
+ */
195
+ providerCondition?: ProviderCondition;
196
+ /** Which evidence {@link providerCondition} rests on. Present exactly when it is. */
197
+ providerConditionSource?: ProviderConditionSource;
198
+ /** The terminal message's `providerResetAtMs`, carried beside a `quota_exhausted` condition. */
199
+ providerResetAtMs?: number;
200
+ /**
201
+ * The route the refused request took (`AssistantMessage.apiErrorRoute`): where its credential came from, how it
202
+ * rode, which knob named its root — the knob words of `ROUTE_CREDENTIAL_PAIRING`, never a key value or a URL.
203
+ * Present whenever the terminal message states one (the first-party brains always do); a host-minted message that
204
+ * states none leaves it absent.
205
+ */
206
+ route?: RequestRoute;
207
+ } | {
208
+ kind: "coded";
209
+ /**
210
+ * The thrown error's own `detail`, as its throw site stated it — a snapshot of its JSON-plain members (strings,
211
+ * finite numbers, booleans, null, and arrays / plain objects of those, bounded; a member of any other kind is left
212
+ * out; bytes — an `ArrayBuffer` view, an index-keyed object — are never a record). A member under a credential
213
+ * name (the env-credential judge's names: `password`, `apiKey`, `token` …) stands as `"[redacted]"`, unread; every
214
+ * other string leaf passes the secret-tier redaction (credential shapes masked; a path is kept — it rides this
215
+ * structured seat, never `message`); a member whose key is not JSON-safe (visible ASCII 1–128, nothing the
216
+ * redaction would mask) is left out. Read off the same link of the error's `cause` chain whose `code` became
217
+ * `terminal.code`. PRESENCE: only when that link carried a plain-object `detail` with at least one such member — a
218
+ * code thrown without one (or a higher-ranked terminal that named the result) leaves the whole account absent.
219
+ */
220
+ detail: CodedFailureDetail;
221
+ } | {
222
+ kind: "model_output_invalid";
223
+ /** Which drop cause failed the turn (`MODEL_OUTPUT_INVALID_CAUSES`: `truncated` — arguments cut before they
224
+ * parsed; `unnamed` — a call with no tool name). The same words `task.tool_turn_incomplete` announces. */
225
+ cause: ModelOutputInvalidCause;
226
+ /** With `truncated`: the output-cap facts the stream reported (the cap stop, the counted output, the request's
227
+ * cap) — what the brain's sentence words as its cap clause. */
228
+ outputLimit?: OutputLimit;
229
+ };
230
+ /** A JSON-plain value as a `coded` account carries it. */
231
+ export type CodedFailureDetailValue = string | number | boolean | null | readonly CodedFailureDetailValue[] | {
232
+ readonly [key: string]: CodedFailureDetailValue;
233
+ };
234
+ /** The `coded` account's body: the thrown error's `detail` members ({@link TerminalFailureDetail}). */
235
+ export type CodedFailureDetail = {
236
+ readonly [key: string]: CodedFailureDetailValue;
237
+ };
155
238
  /**
156
239
  * The endings of a `userPromptSubmit` hook that keep a prompt from being submitted — the closed set of
157
240
  * {@link TerminalBlockedDetail.cause} words:
@@ -11,10 +11,11 @@ export declare class TaskSpecTightenError extends Error {
11
11
  * - **`toolPolicy`** — both set ⇒ `combinePolicies(base, override)` (deny-wins; the base's denials survive,
12
12
  * the override can only ADD denials/asks). Either alone ⇒ that one. This is the central fix: a blind spread
13
13
  * would drop the base policy entirely.
14
- * - **`handsReadOnly`** — `true` is stricter. `base:true` + `override:false` ⇒ **throw** (loosening). Otherwise
15
- * the stricter (`true` if either is true) wins.
16
- * - **`approvalReach`** — `"none"` (nobody can be asked) is the only word; a base declaration survives an override that
17
- * omits it or writes `undefined`.
14
+ * - **every row of the clamp table** (`CLAMP_SET`: `handsReadOnly`, `interactiveTools`, `approvalReach`, the two
15
+ * containment faces, `readDenyPatterns`, `oneShot`, `memoryPersistenceCapable`, `fileHistory`) — a base carrying the
16
+ * row's restricting value meeting an override that states a different one ⇒ **throw** (loosening); otherwise the
17
+ * restricting value wins if either side carries it, an add-only list unions, and an override writing `undefined`
18
+ * erases nothing.
18
19
  * - **`shellGate`** — one value (`"always"`, the operator floor). Either side set ⇒ the floor stands; it cannot
19
20
  * be loosened because absence is the only other state and an override cannot unset a base value.
20
21
  * - **`onAsk`** — no safe automatic merge (the approver and the policy must be coordinated). Both set to
@@ -1,5 +1,6 @@
1
1
  import { combinePolicies } from "./tool-policy.js";
2
2
  import { clampPermissionMode } from "./ask-origin.js";
3
+ import { CLAMP_SEATS, composeClampSeat, loosensClampSeat } from "./child-clamp-seats.js";
3
4
  export class TaskSpecTightenError extends Error {
4
5
  constructor(message) {
5
6
  super(message);
@@ -21,34 +22,28 @@ export function tightenTaskSpec(base, overrides) {
21
22
  merged[field] = [...new Set([...(b ?? []), ...(o ?? [])])];
22
23
  }
23
24
  }
24
- for (const faceKey of ["readFace", "writeFace"]) {
25
- if (base[faceKey] === "roots" && overrides[faceKey] === "open") {
26
- throw new TaskSpecTightenError(`tightenTaskSpec: cannot loosen ${faceKey} from "roots" to "open" (override may only tighten)`);
27
- }
28
- if (base[faceKey] !== undefined || overrides[faceKey] !== undefined) {
29
- merged[faceKey] = base[faceKey] === "roots" || overrides[faceKey] === "roots" ? "roots" : (overrides[faceKey] ?? base[faceKey]);
25
+ {
26
+ const b = base.allowTools;
27
+ const o = overrides.allowTools;
28
+ if (b !== undefined && o !== undefined && !b.includes("*")) {
29
+ const wider = o.filter((entry) => !b.includes(entry));
30
+ if (wider.length > 0)
31
+ throw new TaskSpecTightenError(`tightenTaskSpec: cannot widen allowTools beyond the base list (override names ${JSON.stringify(wider)}; the override may only narrow)`);
30
32
  }
33
+ const kept = o ?? b;
34
+ if (kept !== undefined)
35
+ merged.allowTools = [...kept];
36
+ else
37
+ delete merged.allowTools;
31
38
  }
32
- if (base.readDenyPatterns !== undefined || overrides.readDenyPatterns !== undefined) {
33
- const seen = new Set();
34
- const union = [];
35
- for (const e of [...(base.readDenyPatterns ?? []), ...(overrides.readDenyPatterns ?? [])]) {
36
- const key = typeof e === "string" ? `i:${e}` : `${e.caseSensitive === true ? "s" : "i"}:${e.pattern}`;
37
- if (seen.has(key))
38
- continue;
39
- seen.add(key);
40
- union.push(e);
39
+ for (const seat of CLAMP_SEATS) {
40
+ const b = base[seat];
41
+ const o = overrides[seat];
42
+ if (loosensClampSeat(seat, b, o)) {
43
+ throw new TaskSpecTightenError(`tightenTaskSpec: cannot loosen ${seat} from ${JSON.stringify(b)} to ${JSON.stringify(o)} (override may only tighten)`);
41
44
  }
42
- merged.readDenyPatterns = union;
43
- }
44
- if (base.handsReadOnly === true && overrides.handsReadOnly === false) {
45
- throw new TaskSpecTightenError("tightenTaskSpec: cannot loosen handsReadOnly from true to false (override may only tighten)");
46
- }
47
- if (base.handsReadOnly !== undefined || overrides.handsReadOnly !== undefined) {
48
- merged.handsReadOnly = base.handsReadOnly === true || overrides.handsReadOnly === true;
49
- }
50
- if (base.approvalReach !== undefined || overrides.approvalReach !== undefined) {
51
- merged.approvalReach = base.approvalReach === "none" || overrides.approvalReach === "none" ? "none" : (overrides.approvalReach ?? base.approvalReach);
45
+ if (b !== undefined || o !== undefined)
46
+ Object.assign(merged, { [seat]: composeClampSeat(seat, b, o) });
52
47
  }
53
48
  if (base.permissionMode !== undefined && overrides.permissionMode !== undefined) {
54
49
  const { effective, clamped } = clampPermissionMode(overrides.permissionMode, base.permissionMode);
@@ -55,16 +55,14 @@ export declare const TOOL_FACES: Readonly<Record<string, ToolFaceDeclaration>>;
55
55
  */
56
56
  export declare const ENGINE_BUILTIN_TOOL_NAMES: readonly string[];
57
57
  /**
58
- * The engine's HAND tools, once each, with the other spellings they answer to: the catalog rows mounted by `hands` — what a run
59
- * mounts for itself over an execution environment (the file and shell band, the worktree pair, the band's background-shell
60
- * pair). A delegated child's tool-name lists govern these beside the tools the delegation tool hands it, so the spawn reads
61
- * this set rather than a list of its own.
58
+ * EVERY tool the catalog describes, once per wire name, with the other spellings it answers to — the engine's side of the one
59
+ * universe a delegated child's tool-name lists govern (beside the tools the delegation tool hands it and the protocol tools
60
+ * its spawning run mounts). The spawn reads this set rather than a list of its own: a definition a catalog row adds later is
61
+ * governed the day it lands, whatever band mounts it.
62
62
  */
63
- export declare const HAND_TOOLS: ReadonlyArray<{
63
+ export declare const ENGINE_TOOLS: ReadonlyArray<{
64
64
  readonly name: string;
65
65
  readonly aliases?: readonly string[];
66
66
  }>;
67
- /** The wire names of {@link HAND_TOOLS}. */
68
- export declare const HAND_TOOL_NAMES: readonly string[];
69
67
  /** Is this name one the engine mounts itself ({@link ENGINE_BUILTIN_TOOL_NAMES})? A caller tool, an MCP tool or a skill tool is not. */
70
68
  export declare function isEngineBuiltinToolName(name: string): boolean;
@@ -110,14 +110,14 @@ export const TOOL_CATALOG_ENTRIES = [
110
110
  { id: "task-output", name: "TaskOutput", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-output"], face: { family: "delegate", effect: "read", contract: TASK_OUTPUT_CONTRACT } },
111
111
  { id: "task-stop", name: "TaskStop", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-stop"], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
112
112
  { id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start", "readonly_out_of_root", "read_unestablished"], face: { family: "other", effect: "write", contract: c("core.monitor@1"), ruleFace: shellRuleFace(["command", "description", "timeout_ms", "persistent"]), offloadThresholdChars: 10_000 } },
113
- { id: "send-message", name: "SendMessage", source: "builtin", definedIn: "src/agents/send-message-tool.ts", mountedBy: ["delegation", "hostInternals", "peerLane"], cards: ["send-message"], face: { family: "protocol", effect: "write", contract: c("core.send_message@1") } },
113
+ { id: "send-message", name: "SendMessage", protocol: ["teammate"], source: "builtin", definedIn: "src/agents/send-message-tool.ts", mountedBy: ["delegation", "hostInternals", "peerLane"], cards: ["send-message"], face: { family: "protocol", effect: "write", contract: c("core.send_message@1") } },
114
114
  { id: "agent-transcript", name: "AgentTranscript", source: "builtin", definedIn: "src/agents/agent-transcript-tool.ts", mountedBy: ["delegation"], cards: ["agent-transcript"], face: { family: "delegate", effect: "read", contract: c("core.agent_transcript@1") } },
115
115
  { id: "list-agents", name: "ListAgents", source: "builtin", definedIn: "src/agents/list-agents-tool.ts", mountedBy: ["delegation", "peerLane"], cards: ["list-agents"], face: { family: "protocol", effect: "read", aliases: ["ListPeers"], contract: c("core.list_agents@1") } },
116
116
  { id: "read-inbox", name: "ReadInbox", source: "builtin", definedIn: "src/agents/read-inbox-tool.ts", mountedBy: ["peerLane"], cards: ["read-inbox"], face: { family: "protocol", effect: "read", contract: c("core.read_inbox@1"), offload: false, budgetExempt: true } },
117
117
  { id: "workflow", name: "Workflow", source: "builtin", definedIn: "src/orchestration/run-workflow-tool.ts", mountedBy: ["workflow"], cards: ["workflow-run"], face: { family: "delegate", effect: "write", contract: c("core.workflow@1") } },
118
- { id: "report-blocked", name: "ReportBlocked", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["always"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.report_blocked@1") } },
118
+ { id: "report-blocked", name: "ReportBlocked", protocol: ["delegated"], source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["always"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.report_blocked@1") } },
119
119
  { id: "report-findings", name: "ReportFindings", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["always"], cards: ["report-findings"], face: { family: "scaffold", effect: "read", contract: c("core.report_findings@1") } },
120
- { id: "structured-output", name: "StructuredOutput", source: "synthetic", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.structured_output@1") } },
120
+ { id: "structured-output", name: "StructuredOutput", protocol: ["structured"], source: "synthetic", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.structured_output@1") } },
121
121
  { id: "skill", name: "Skill", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.skill@1") } },
122
122
  { id: "read-tool-result", name: "ReadToolResult", source: "builtin", definedIn: "src/core/tool-result-store.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.read_tool_result@1"), budgetExempt: true } },
123
123
  { id: "tool-search", name: "ToolSearch", source: "builtin", definedIn: "src/core/runner/tool-disclosure.ts", mountedBy: ["optional"], cards: ["tool-search"], face: { family: "scaffold", effect: "read", contract: c("core.tool_search@1") } },
@@ -130,9 +130,9 @@ export const TOOL_CATALOG_ENTRIES = [
130
130
  { id: "memory-index", name: "memory_index", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contentOrigin: "local", contract: c("core.memory_index@1", "2"), offload: false } },
131
131
  { id: "memory-list", name: "memory_list", source: "builtin", definedIn: "src/core/shared-memory/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contract: c("core.memory_list@1"), offload: false } },
132
132
  { id: "memory-read", name: "memory_read", source: "builtin", definedIn: "src/core/shared-memory/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contract: c("core.memory_read@1"), offload: false } },
133
- { id: "cron-create", name: "CronCreate", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-create"], face: { family: "other", effect: "write", contract: c("core.cron_create@1") } },
134
- { id: "cron-delete", name: "CronDelete", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-delete"], face: { family: "other", effect: "idempotent", contract: c("core.cron_delete@1") } },
135
- { id: "cron-list", name: "CronList", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-list"], face: { family: "other", effect: "read", contract: c("core.cron_list@1") } },
133
+ { id: "cron-create", name: "CronCreate", protocol: ["teammate"], source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-create"], face: { family: "other", effect: "write", contract: c("core.cron_create@1") } },
134
+ { id: "cron-delete", name: "CronDelete", protocol: ["teammate"], source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-delete"], face: { family: "other", effect: "idempotent", contract: c("core.cron_delete@1") } },
135
+ { id: "cron-list", name: "CronList", protocol: ["teammate"], source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-list"], face: { family: "other", effect: "read", contract: c("core.cron_list@1") } },
136
136
  { id: "schedule-wakeup", name: "ScheduleWakeup", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["schedule-wakeup"], face: { family: "other", effect: "write", contract: c("core.schedule_wakeup@1") } },
137
137
  {
138
138
  id: "web-fetch", name: "WebFetch", source: "builtin", definedIn: "src/tools/web.ts", mountedBy: ["scenario"], cards: ["web-fetch"],
@@ -143,13 +143,13 @@ export const TOOL_CATALOG_ENTRIES = [
143
143
  face: { family: "web", effect: "read", contentOrigin: "external", contract: c("core.web_search@1"), ruleFace: { primaryParams: ["query"] }, renderHints: { summaryParams: ["query"] }, compactable: true },
144
144
  },
145
145
  { id: "todo-write", name: TODO_WRITE_TOOL_NAME, source: "builtin", definedIn: "src/tools/todo.ts", mountedBy: ["scenario"], cards: ["todo"], modelGate: "task-scaffold", face: { family: "plan", effect: "idempotent", contract: c("core.todo_write@1") } },
146
- { id: "task-create", name: TASK_CREATE_TOOL_NAME, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "write", contract: c("core.task_create@1") } },
147
- { id: "task-get", name: TASK_GET_TOOL_NAME, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "read", contract: c("core.task_get@1") } },
148
- { id: "task-update", name: TASK_UPDATE_TOOL_NAME, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "idempotent", contract: c("core.task_update@1") } },
149
- { id: "task-list", name: TASK_LIST_TOOL_NAME, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task-list"], modelGate: "task-scaffold", face: { family: "plan", effect: "read", contract: c("core.task_list@1") } },
146
+ { id: "task-create", name: TASK_CREATE_TOOL_NAME, protocol: ["teammate"], protocolUnderCeiling: true, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "write", contract: c("core.task_create@1") } },
147
+ { id: "task-get", name: TASK_GET_TOOL_NAME, protocol: ["teammate"], protocolUnderCeiling: true, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "read", contract: c("core.task_get@1") } },
148
+ { id: "task-update", name: TASK_UPDATE_TOOL_NAME, protocol: ["teammate"], protocolUnderCeiling: true, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "idempotent", contract: c("core.task_update@1") } },
149
+ { id: "task-list", name: TASK_LIST_TOOL_NAME, protocol: ["teammate"], protocolUnderCeiling: true, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task-list"], modelGate: "task-scaffold", face: { family: "plan", effect: "read", contract: c("core.task_list@1") } },
150
150
  { id: "agent", name: "Agent", source: "builtin", definedIn: "src/agents/subagent.ts", mountedBy: ["scenario"], cards: ["agent", "fork"], face: { family: "delegate", contentOrigin: "local", contract: c("core.agent@1") } },
151
- { id: "observer-report", name: "ObserverReport", source: "builtin", definedIn: "src/agents/observer.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", effect: "write", contract: c("core.observer_report@1") } },
152
- { id: "declare-done", name: "DeclareDone", source: "builtin", definedIn: "src/orchestration/goal.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", effect: "read" } },
151
+ { id: "observer-report", name: "ObserverReport", protocol: ["observer"], source: "builtin", definedIn: "src/agents/observer.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", effect: "write", contract: c("core.observer_report@1") } },
152
+ { id: "declare-done", name: "DeclareDone", protocol: ["goal"], source: "builtin", definedIn: "src/orchestration/goal.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", effect: "read" } },
153
153
  ];
154
154
  export const ENGINE_CARDS = Object.freeze({
155
155
  mcp: "src/core/mcp.ts",
@@ -182,11 +182,10 @@ function deepFreeze(value) {
182
182
  deepFreeze(TOOL_CATALOG_ENTRIES);
183
183
  export const TOOL_FACES = Object.freeze(Object.fromEntries(TOOL_CATALOG_ENTRIES.map((e) => [e.id, e.face])));
184
184
  export const ENGINE_BUILTIN_TOOL_NAMES = Object.freeze([...new Set(TOOL_CATALOG_ENTRIES.filter((e) => e.source === "builtin").map((e) => e.name))]);
185
- export const HAND_TOOLS = Object.freeze([...new Set(TOOL_CATALOG_ENTRIES.filter((e) => e.mountedBy.includes("hands")).map((e) => e.name))].map((name) => {
186
- const aliases = [...new Set(TOOL_CATALOG_ENTRIES.filter((e) => e.name === name && e.mountedBy.includes("hands")).flatMap((e) => e.face.aliases ?? []))];
185
+ export const ENGINE_TOOLS = Object.freeze([...new Set(TOOL_CATALOG_ENTRIES.map((e) => e.name))].map((name) => {
186
+ const aliases = [...new Set(TOOL_CATALOG_ENTRIES.filter((e) => e.name === name).flatMap((e) => e.face.aliases ?? []))];
187
187
  return Object.freeze(aliases.length > 0 ? { name, aliases: Object.freeze(aliases) } : { name });
188
188
  }));
189
- export const HAND_TOOL_NAMES = Object.freeze(HAND_TOOLS.map((t) => t.name));
190
189
  export function isEngineBuiltinToolName(name) {
191
190
  return ENGINE_BUILTIN_TOOL_NAMES.includes(name);
192
191
  }
@@ -42,6 +42,12 @@ import type { AssertAllKeysHandled } from "./ask-origin.js";
42
42
  * mounting the card (design/388 §2.5 boundary ①). */
43
43
  export declare const TOOL_APPROVAL_CARDS: readonly ["file-edit", "file-write", "notebook-edit", "shell", "fetch", "plan", "question", "generic"];
44
44
  export type ToolApprovalCard = (typeof TOOL_APPROVAL_CARDS)[number];
45
+ /** Membership in the closed set — the one screen a stored or declared card word passes before it is read. */
46
+ export declare function isToolApprovalCard(word: unknown): word is ToolApprovalCard;
47
+ /** The cards only an engine-defined tool owns: a caller or protocol tool cannot declare one, and the card is minted
48
+ * from the engine's own judgment of the call, never from a row's declaration. */
49
+ export declare const RESERVED_APPROVAL_CARDS: readonly ["question", "plan"];
50
+ export type ReservedApprovalCard = (typeof RESERVED_APPROVAL_CARDS)[number];
45
51
  /** How a path-targeting tool touches its target. `read` never enters a write-confinement face. */
46
52
  export declare const TOOL_PATH_ACCESSES: readonly ["read", "create", "edit"];
47
53
  export type ToolPathAccess = (typeof TOOL_PATH_ACCESSES)[number];
@@ -182,6 +188,7 @@ export interface ToolRenderHints {
182
188
  activity?: string;
183
189
  summaryParams?: readonly string[];
184
190
  resultCards?: readonly string[];
191
+ /** The card family this tool asks under; absent reads `generic`. `question` / `plan` are the engine's own tools' cards — a declaration of either is refused. */
185
192
  approvalCard?: ToolApprovalCard;
186
193
  }
187
194
  /**
@@ -217,6 +224,39 @@ export interface ToolFaceDeclaration {
217
224
  compactable?: true;
218
225
  budgetExempt?: true;
219
226
  }
227
+ /**
228
+ * The kinds of leg a run can be, as far as which tools a leg itself relies on: a delegated child (every lane a delegation
229
+ * tool, a workflow or a team spawns), a NAMED delegated child (a teammate), an iteration of a goal loop, an observer sidecar,
230
+ * a run with an output schema (it finishes by submitting through the structured-output tool). A catalog row's
231
+ * {@link ToolCatalogEntry.protocol} names the kinds whose legs carry that tool as part of their protocol.
232
+ */
233
+ export declare const TOOL_LEG_KINDS: readonly ["delegated", "teammate", "goal", "observer", "structured"];
234
+ export type ToolLegKind = (typeof TOOL_LEG_KINDS)[number];
235
+ /**
236
+ * One layer of a tool selector, as data: an allowed-tools list (absent = no allowlist on this layer) and a denied-tools list,
237
+ * each a frozen copy of the entries a list was written with. A run's own layer is its `allowTools` and `excludeTools`; the
238
+ * layers above it are the ones the run that spawned it was bounded by, plus that run's own.
239
+ */
240
+ export interface ToolSelectorLayer {
241
+ readonly allow?: readonly string[];
242
+ readonly deny: readonly string[];
243
+ }
244
+ /**
245
+ * The compiled tool selector of one run — the one answer to "is this tool available on this run", read at the roster valve
246
+ * and on every later roster change (an MCP refresh or re-dial, the deferred-search lane), and handed down as the ceiling of
247
+ * every run this one spawns (`layers`). A tool is admitted when it is a protocol tool of this run's leg kind, or when every
248
+ * layer admits it: the layer's allowlist (if any) reaches it and its denylist does not.
249
+ */
250
+ export interface ToolSelector {
251
+ readonly layers: readonly ToolSelectorLayer[];
252
+ admits(tool: {
253
+ readonly name: string;
254
+ readonly aliases?: readonly string[];
255
+ }): boolean;
256
+ /** Can ANY tool of the protocol peer whose minted prefix is `prefix` (`mcp__srv__`) be admitted — read off the lists alone,
257
+ * never off what the peer lists now, so a tool the peer lists later is judged by the same answer. */
258
+ admitsPeer(prefix: string): boolean;
259
+ }
220
260
  /**
221
261
  * One row of the static tool catalog (design/388 §2.1 "静态目录"). Keyed by DEFINITION identity, not by
222
262
  * name: two definitions may share a wire name (the full and the read-only shell both answer to `Bash`),
@@ -240,6 +280,13 @@ export interface ToolCatalogEntry {
240
280
  face: ToolFaceDeclaration;
241
281
  /** A `modelGate` class the default-bundle assembler stamps on this definition's default arm. */
242
282
  modelGate?: string;
283
+ /** The leg kinds whose legs rely on this tool as part of their protocol: on such a leg the tool's availability is not a
284
+ * selector's to decide (an allowlist need not name it), and a denylist that reaches it there is a contradiction the run
285
+ * refuses at prepare. */
286
+ protocol?: readonly ToolLegKind[];
287
+ /** A protocol row the leg shares with the run that spawned it (the team task-list family a teammate shares with its leader): on its
288
+ * leg the leg's OWN lists do not decide it, but the ceiling still does — the leg holds it exactly when its spawner may. */
289
+ protocolUnderCeiling?: true;
243
290
  }
244
291
  /**
245
292
  * The band-membership table over the mount tags: a host derives its hand band as the definitions listed under a