@sema-agent/core 7.55.0 → 7.56.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 +513 -0
  2. package/dist/agents/cascade.js +1 -1
  3. package/dist/agents/cc-peer-directory.js +1 -0
  4. package/dist/agents/child-model-seat.d.ts +0 -11
  5. package/dist/agents/child-model-seat.js +0 -3
  6. package/dist/agents/list-agents-tool.d.ts +3 -1
  7. package/dist/agents/list-agents-tool.js +7 -7
  8. package/dist/agents/peer-directory.d.ts +22 -4
  9. package/dist/agents/peer-directory.js +24 -2
  10. package/dist/agents/peer-session-drain.js +2 -2
  11. package/dist/agents/repair-loop.js +2 -2
  12. package/dist/agents/send-message-tool.js +4 -4
  13. package/dist/agents/subagent.d.ts +6 -4
  14. package/dist/agents/subagent.js +64 -60
  15. package/dist/agents/tool-filter.d.ts +7 -5
  16. package/dist/agents/tool-filter.js +12 -9
  17. package/dist/agents/verify.d.ts +3 -2
  18. package/dist/agents/verify.js +3 -3
  19. package/dist/brain/anthropic.js +30 -73
  20. package/dist/brain/media-degrade.d.ts +18 -7
  21. package/dist/brain/media-degrade.js +13 -2
  22. package/dist/brain/model-capability.d.ts +37 -0
  23. package/dist/brain/model-capability.js +34 -0
  24. package/dist/brain/model-presets.d.ts +2 -8
  25. package/dist/brain/model-presets.js +1 -6
  26. package/dist/brain/open-responses.js +13 -26
  27. package/dist/brain/openai.js +14 -87
  28. package/dist/brain/reasoning-request.d.ts +96 -0
  29. package/dist/brain/reasoning-request.js +174 -0
  30. package/dist/brain/reasoning.d.ts +11 -11
  31. package/dist/brain/reasoning.js +4 -3
  32. package/dist/brain/request-params.d.ts +7 -4
  33. package/dist/brain/request-params.js +7 -4
  34. package/dist/brain/status-sink.d.ts +20 -0
  35. package/dist/brain/status-sink.js +7 -0
  36. package/dist/core/a2a.d.ts +2 -1
  37. package/dist/core/a2a.js +19 -13
  38. package/dist/core/agent-definition.d.ts +10 -0
  39. package/dist/core/allow-entry.d.ts +20 -13
  40. package/dist/core/allow-entry.js +26 -22
  41. package/dist/core/allow-rule-shadow.d.ts +3 -3
  42. package/dist/core/ask-origin.d.ts +64 -18
  43. package/dist/core/ask-origin.js +63 -23
  44. package/dist/core/ask-question.d.ts +51 -8
  45. package/dist/core/ask-question.js +3 -4
  46. package/dist/core/ask-unresolvable-notice.d.ts +8 -3
  47. package/dist/core/ask-unresolvable-notice.js +16 -2
  48. package/dist/core/checkpoint-generation.d.ts +53 -0
  49. package/dist/core/checkpoint-generation.js +35 -0
  50. package/dist/core/checkpoint-queries.d.ts +180 -0
  51. package/dist/core/checkpoint-queries.js +272 -0
  52. package/dist/core/checkpoint-requirements.d.ts +13 -1
  53. package/dist/core/checkpoint-requirements.js +4 -0
  54. package/dist/core/checkpoint-store.d.ts +88 -24
  55. package/dist/core/checkpoint-store.js +24 -9
  56. package/dist/core/cross-boundary-taint.d.ts +6 -5
  57. package/dist/core/cross-boundary-taint.js +1 -3
  58. package/dist/core/decision-receipt.d.ts +37 -3
  59. package/dist/core/decision-receipt.js +17 -17
  60. package/dist/core/delegation-not-started.d.ts +6 -0
  61. package/dist/core/delegation-not-started.js +17 -0
  62. package/dist/core/engine-attestation.d.ts +93 -0
  63. package/dist/core/engine-attestation.js +70 -0
  64. package/dist/core/engine-capabilities.d.ts +4 -1
  65. package/dist/core/engine-capabilities.js +1 -1
  66. package/dist/core/engine-notice.d.ts +30 -11
  67. package/dist/core/file-history-store.d.ts +90 -14
  68. package/dist/core/file-history-store.js +23 -15
  69. package/dist/core/gate-fold.d.ts +1 -1
  70. package/dist/core/gate-fold.js +22 -10
  71. package/dist/core/gate-lanes.js +73 -64
  72. package/dist/core/gate-outcome.d.ts +26 -5
  73. package/dist/core/gate-outcome.js +26 -0
  74. package/dist/core/governance-codes.d.ts +5 -1
  75. package/dist/core/governance-codes.js +6 -0
  76. package/dist/core/hooks.d.ts +17 -16
  77. package/dist/core/hooks.js +3 -1
  78. package/dist/core/host-floor.d.ts +128 -0
  79. package/dist/core/host-floor.js +206 -0
  80. package/dist/core/host-target.d.ts +38 -3
  81. package/dist/core/host-target.js +38 -5
  82. package/dist/core/mcp.d.ts +5 -1
  83. package/dist/core/mcp.js +67 -41
  84. package/dist/core/memory-engine/delegation-settlement.js +15 -24
  85. package/dist/core/memory-engine/engine.js +4 -4
  86. package/dist/core/memory-engine/layout.d.ts +60 -8
  87. package/dist/core/memory-engine/layout.js +65 -6
  88. package/dist/core/model-seat.d.ts +10 -3
  89. package/dist/core/mods/classic-events.d.ts +67 -0
  90. package/dist/core/mods/classic-events.js +1 -0
  91. package/dist/core/mods/dollar.d.ts +13 -0
  92. package/dist/core/mods/extension-call.d.ts +31 -0
  93. package/dist/core/mods/extension-call.js +43 -0
  94. package/dist/core/mods/kernel/dispatch.d.ts +20 -3
  95. package/dist/core/mods/kernel/dispatch.js +140 -69
  96. package/dist/core/mods/kernel/events.d.ts +47 -11
  97. package/dist/core/mods/kernel/events.js +66 -42
  98. package/dist/core/mods/kernel/index.d.ts +4 -4
  99. package/dist/core/mods/kernel/index.js +2 -2
  100. package/dist/core/mods/kernel/receive.d.ts +39 -5
  101. package/dist/core/mods/kernel/receive.js +290 -17
  102. package/dist/core/mods/kernel/registration.d.ts +33 -2
  103. package/dist/core/mods/kernel/registration.js +6 -7
  104. package/dist/core/mods/kernel/site-rules.d.ts +44 -16
  105. package/dist/core/mods/kernel/site-rules.js +24 -42
  106. package/dist/core/mods/spec-seats.d.ts +69 -0
  107. package/dist/core/mods/spec-seats.js +114 -0
  108. package/dist/core/mods/stations.d.ts +28 -8
  109. package/dist/core/mods/stations.js +29 -13
  110. package/dist/core/mods/tool-call.d.ts +79 -0
  111. package/dist/core/mods/tool-call.js +152 -0
  112. package/dist/core/namespaced-rule-name.d.ts +32 -38
  113. package/dist/core/namespaced-rule-name.js +29 -36
  114. package/dist/core/parent-spec-seat.d.ts +6 -1
  115. package/dist/core/parent-spec-seat.js +2 -0
  116. package/dist/core/park-selfcheck.js +1 -0
  117. package/dist/core/permission-rule-consent.d.ts +19 -0
  118. package/dist/core/permission-rule-consent.js +116 -26
  119. package/dist/core/permission-rule-model.d.ts +14 -3
  120. package/dist/core/permission-rule-model.js +39 -24
  121. package/dist/core/permission-rule-org.d.ts +6 -2
  122. package/dist/core/permission-rule-org.js +7 -3
  123. package/dist/core/permission-rule-provider.d.ts +5 -2
  124. package/dist/core/permission-rule-provider.js +41 -11
  125. package/dist/core/permission-rule-store.d.ts +3 -2
  126. package/dist/core/permission-rule-store.js +11 -2
  127. package/dist/core/permission-rule-sync.js +3 -2
  128. package/dist/core/permission-rule-syntax.d.ts +5 -4
  129. package/dist/core/permission-rule-syntax.js +2 -2
  130. package/dist/core/permission-rules.d.ts +4 -4
  131. package/dist/core/permission-rules.js +11 -20
  132. package/dist/core/persisted-rule-arms.d.ts +2 -2
  133. package/dist/core/persisted-rule-arms.js +10 -12
  134. package/dist/core/principal-name.d.ts +20 -0
  135. package/dist/core/principal-name.js +22 -0
  136. package/dist/core/prompt-section-seat.js +4 -6
  137. package/dist/core/protocol-http-auth.d.ts +26 -5
  138. package/dist/core/protocol-http-auth.js +137 -4
  139. package/dist/core/retired-keys.d.ts +16 -0
  140. package/dist/core/retired-keys.js +16 -4
  141. package/dist/core/rewrite-rejudge.d.ts +60 -0
  142. package/dist/core/rewrite-rejudge.js +36 -0
  143. package/dist/core/roles.d.ts +28 -1
  144. package/dist/core/roles.js +31 -19
  145. package/dist/core/rule-lane-reads.d.ts +20 -15
  146. package/dist/core/rule-lane-reads.js +45 -17
  147. package/dist/core/rule-owner.d.ts +3 -3
  148. package/dist/core/rule-owner.js +3 -2
  149. package/dist/core/runner/announce-once-ledger.d.ts +14 -0
  150. package/dist/core/runner/announce-once-ledger.js +5 -0
  151. package/dist/core/runner/assemble-result.d.ts +1 -0
  152. package/dist/core/runner/assemble-result.js +1 -1
  153. package/dist/core/runner/call-hook-stations.d.ts +3 -2
  154. package/dist/core/runner/caller-roster.d.ts +16 -4
  155. package/dist/core/runner/caller-roster.js +33 -6
  156. package/dist/core/runner/capability-withheld-seat.d.ts +5 -0
  157. package/dist/core/runner/capability-withheld-seat.js +24 -0
  158. package/dist/core/runner/classifier-ladder.d.ts +7 -9
  159. package/dist/core/runner/classifier-ladder.js +3 -3
  160. package/dist/core/runner/construction-doors.js +3 -0
  161. package/dist/core/runner/contracts.d.ts +28 -25
  162. package/dist/core/runner/cross-boundary-lane.d.ts +17 -13
  163. package/dist/core/runner/cross-boundary-lane.js +40 -27
  164. package/dist/core/runner/denial-limit-arms.d.ts +2 -2
  165. package/dist/core/runner/denial-limit-arms.js +1 -1
  166. package/dist/core/runner/file-history-request.d.ts +18 -0
  167. package/dist/core/runner/file-history-request.js +21 -13
  168. package/dist/core/runner/file-history-restore.d.ts +45 -0
  169. package/dist/core/runner/file-history-restore.js +46 -0
  170. package/dist/core/runner/gate-exit.d.ts +3 -3
  171. package/dist/core/runner/gate-exit.js +4 -3
  172. package/dist/core/runner/image.d.ts +7 -17
  173. package/dist/core/runner/image.js +9 -60
  174. package/dist/core/runner/permission-rule-lanes.d.ts +4 -4
  175. package/dist/core/runner/permission-rule-lanes.js +26 -10
  176. package/dist/core/runner/person-path-rows.d.ts +12 -9
  177. package/dist/core/runner/person-path-rows.js +18 -7
  178. package/dist/core/runner/prepare-ask-lane.d.ts +2 -2
  179. package/dist/core/runner/prepare-ask-lane.js +5 -2
  180. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -6
  181. package/dist/core/runner/prepare-caps-and-workflow.js +4 -3
  182. package/dist/core/runner/prepare-config-doors.d.ts +29 -38
  183. package/dist/core/runner/prepare-config-doors.js +42 -25
  184. package/dist/core/runner/prepare-defer-classify.d.ts +4 -3
  185. package/dist/core/runner/prepare-defer-classify.js +7 -8
  186. package/dist/core/runner/prepare-file-history.d.ts +48 -8
  187. package/dist/core/runner/prepare-file-history.js +7 -39
  188. package/dist/core/runner/prepare-gate-stations.d.ts +1 -1
  189. package/dist/core/runner/prepare-gate-stations.js +10 -1
  190. package/dist/core/runner/prepare-hands-readface.d.ts +2 -8
  191. package/dist/core/runner/prepare-hands-readface.js +15 -14
  192. package/dist/core/runner/prepare-inherited-gate.d.ts +6 -3
  193. package/dist/core/runner/prepare-inherited-gate.js +5 -3
  194. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  195. package/dist/core/runner/prepare-park-ask.js +5 -5
  196. package/dist/core/runner/prepare-policy-chain.d.ts +6 -3
  197. package/dist/core/runner/prepare-policy-chain.js +65 -72
  198. package/dist/core/runner/prepare-prompt-assembly.d.ts +5 -7
  199. package/dist/core/runner/prepare-prompt-assembly.js +10 -8
  200. package/dist/core/runner/prepare-prompt-inputs.d.ts +11 -9
  201. package/dist/core/runner/prepare-prompt-inputs.js +3 -5
  202. package/dist/core/runner/prepare-protocol-tools.d.ts +2 -0
  203. package/dist/core/runner/prepare-protocol-tools.js +3 -3
  204. package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
  205. package/dist/core/runner/prepare-safety-scan.js +0 -17
  206. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
  207. package/dist/core/runner/prepare-suspend-saga.js +5 -6
  208. package/dist/core/runner/prepare-task.d.ts +1 -1
  209. package/dist/core/runner/prepare-task.js +29 -20
  210. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +7 -4
  211. package/dist/core/runner/prepare-tool-disclosure-mount.js +20 -16
  212. package/dist/core/runner/prepare-turn-wiring.d.ts +0 -4
  213. package/dist/core/runner/prepare-turn-wiring.js +2 -7
  214. package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -2
  215. package/dist/core/runner/prepare-wiring-manifest.js +3 -3
  216. package/dist/core/runner/resume-admission.js +1 -1
  217. package/dist/core/runner/resume-apply.js +11 -12
  218. package/dist/core/runner/resume-checkpoint-screen.js +3 -0
  219. package/dist/core/runner/resume-claim.d.ts +4 -2
  220. package/dist/core/runner/resume-claim.js +15 -8
  221. package/dist/core/runner/retired-runner-deps.js +1 -0
  222. package/dist/core/runner/rewind-files.d.ts +15 -0
  223. package/dist/core/runner/rewind-files.js +132 -0
  224. package/dist/core/runner/run-attachment-seats.js +2 -1
  225. package/dist/core/runner/run-brain-sinks.d.ts +4 -1
  226. package/dist/core/runner/run-brain-sinks.js +4 -2
  227. package/dist/core/runner/run-harness-handlers.js +12 -4
  228. package/dist/core/runner/run-leg.d.ts +1 -1
  229. package/dist/core/runner/run-leg.js +30 -18
  230. package/dist/core/runner/run-settle-and-teardown.js +2 -1
  231. package/dist/core/runner/run-turn-boundary.js +1 -10
  232. package/dist/core/runner/runtask.d.ts +3 -0
  233. package/dist/core/runner/runtask.js +8 -9
  234. package/dist/core/runner/session-read-file-states.d.ts +15 -0
  235. package/dist/core/runner/session-read-file-states.js +28 -0
  236. package/dist/core/runner/session-rule-policy.js +4 -3
  237. package/dist/core/runner/shell-env-withheld.d.ts +33 -0
  238. package/dist/core/runner/shell-env-withheld.js +51 -0
  239. package/dist/core/runner/skills-seat.js +6 -2
  240. package/dist/core/runner/stream-settle-backstop.js +1 -1
  241. package/dist/core/runner/sub-agent-deny-lists.js +4 -7
  242. package/dist/core/runner/synthetic-tools.d.ts +6 -0
  243. package/dist/core/runner/synthetic-tools.js +4 -1
  244. package/dist/core/runner/tool-disclosure.d.ts +10 -12
  245. package/dist/core/runner/tool-disclosure.js +1 -3
  246. package/dist/core/runner/tool-end-body.d.ts +1 -1
  247. package/dist/core/runner/tool-end-body.js +10 -9
  248. package/dist/core/runner/tool-not-executed.d.ts +5 -0
  249. package/dist/core/runner/tool-not-executed.js +11 -0
  250. package/dist/core/runner/tool-occurrence.d.ts +8 -0
  251. package/dist/core/runner/tool-occurrence.js +7 -0
  252. package/dist/core/runner/turn-attachments.d.ts +33 -15
  253. package/dist/core/runner/turn-attachments.js +62 -37
  254. package/dist/core/runner-deps.d.ts +5 -5
  255. package/dist/core/safety-axis-vocab.d.ts +6 -4
  256. package/dist/core/safety-axis-vocab.js +6 -5
  257. package/dist/core/safety-merge-corpus.js +11 -3
  258. package/dist/core/secret-env.d.ts +26 -3
  259. package/dist/core/secret-env.js +44 -8
  260. package/dist/core/select-model.js +3 -3
  261. package/dist/core/session-policy-store.d.ts +27 -15
  262. package/dist/core/session-policy-store.js +156 -36
  263. package/dist/core/session-store.d.ts +29 -10
  264. package/dist/core/session-store.js +51 -19
  265. package/dist/core/session.d.ts +82 -16
  266. package/dist/core/session.js +53 -0
  267. package/dist/core/shell-read-silence.d.ts +11 -12
  268. package/dist/core/shell-read-silence.js +9 -5
  269. package/dist/core/shell-run-candidates.js +3 -1
  270. package/dist/core/shell-wrapper-projection.js +1 -1
  271. package/dist/core/shell-wrapper-table.d.ts +14 -7
  272. package/dist/core/shell-wrapper-table.js +5 -4
  273. package/dist/core/side-query.js +10 -1
  274. package/dist/core/skill-grant.d.ts +7 -2
  275. package/dist/core/skill-grant.js +5 -0
  276. package/dist/core/skills-directory.js +6 -2
  277. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +11 -1
  278. package/dist/core/store-contracts/checkpoint-store-contract.js +140 -2
  279. package/dist/core/store-contracts/file-history-store-contract.js +31 -1
  280. package/dist/core/store-contracts/peer-directory-contract.d.ts +7 -2
  281. package/dist/core/store-contracts/peer-directory-contract.js +14 -2
  282. package/dist/core/store-contracts/session-repo-contract.js +51 -0
  283. package/dist/core/store-contracts/session-store-contract.d.ts +2 -1
  284. package/dist/core/store-contracts/session-store-contract.js +150 -14
  285. package/dist/core/stub-env.d.ts +2 -0
  286. package/dist/core/stub-env.js +1 -0
  287. package/dist/core/synthetic-read-verdict.js +3 -3
  288. package/dist/core/task-event.d.ts +32 -4
  289. package/dist/core/task-notification.d.ts +16 -12
  290. package/dist/core/task-registry.d.ts +9 -0
  291. package/dist/core/task-registry.js +11 -1
  292. package/dist/core/task-result.d.ts +18 -0
  293. package/dist/core/task-spec.d.ts +18 -13
  294. package/dist/core/tighten-task-spec.js +2 -1
  295. package/dist/core/tool-catalog-entries.d.ts +17 -0
  296. package/dist/core/tool-catalog-entries.js +13 -2
  297. package/dist/core/tool-check-reading.d.ts +11 -0
  298. package/dist/core/tool-check-reading.js +11 -6
  299. package/dist/core/tool-model-gate.d.ts +2 -2
  300. package/dist/core/tool-name-aliases.d.ts +16 -16
  301. package/dist/core/tool-name-aliases.js +13 -19
  302. package/dist/core/tool-name-readings.d.ts +5 -35
  303. package/dist/core/tool-name-readings.js +1 -9
  304. package/dist/core/tool-policy.d.ts +68 -198
  305. package/dist/core/tool-policy.js +149 -198
  306. package/dist/core/tool-result-store.js +3 -0
  307. package/dist/core/tool-roster.d.ts +14 -19
  308. package/dist/core/tool-roster.js +11 -44
  309. package/dist/core/tool-spec-members.js +7 -2
  310. package/dist/core/tool-spec.d.ts +19 -11
  311. package/dist/core/tools.js +49 -9
  312. package/dist/core/trace.d.ts +2 -2
  313. package/dist/core/types.d.ts +1 -1
  314. package/dist/core/user-attachment.d.ts +8 -1
  315. package/dist/core/user-attachment.js +64 -4
  316. package/dist/core/wiring-manifest.d.ts +9 -0
  317. package/dist/core/wiring-manifest.js +1 -0
  318. package/dist/core/workflow-journal-store.js +1 -1
  319. package/dist/engine/compaction/compaction.js +3 -2
  320. package/dist/engine/execution-env/node-execution-env.d.ts +7 -3
  321. package/dist/engine/execution-env/node-execution-env.js +11 -2
  322. package/dist/engine/harness/agent-harness.d.ts +25 -1
  323. package/dist/engine/harness/agent-harness.js +54 -7
  324. package/dist/engine/harness/types.d.ts +29 -2
  325. package/dist/engine/llm/types.d.ts +28 -11
  326. package/dist/engine/loop/agent-loop.d.ts +22 -0
  327. package/dist/engine/loop/agent-loop.js +16 -26
  328. package/dist/engine/loop/types.d.ts +16 -6
  329. package/dist/engine/session/memory-repo.d.ts +9 -2
  330. package/dist/engine/session/memory-repo.js +11 -1
  331. package/dist/engine/session/repo-utils.d.ts +6 -5
  332. package/dist/engine/session/repo-utils.js +14 -6
  333. package/dist/index.d.ts +22 -10
  334. package/dist/index.js +15 -7
  335. package/dist/internal/harness-types.d.ts +1 -1
  336. package/dist/internal/llm.d.ts +1 -1
  337. package/dist/orchestration/run-workflow-tool.d.ts +6 -4
  338. package/dist/orchestration/run-workflow-tool.js +5 -3
  339. package/dist/orchestration/workflow.d.ts +3 -0
  340. package/dist/orchestration/workflow.js +36 -18
  341. package/dist/prompt-assembly/artifact.d.ts +2 -2
  342. package/dist/prompt-assembly/artifact.js +5 -3
  343. package/dist/prompt-assembly/assemble.d.ts +3 -2
  344. package/dist/prompt-assembly/assemble.js +1 -1
  345. package/dist/prompt-assembly/catalog/schema.d.ts +43 -12
  346. package/dist/prompt-assembly/catalog/schema.js +34 -2
  347. package/dist/prompt-assembly/catalog/sections.js +0 -1
  348. package/dist/prompt-assembly/composer.d.ts +3 -1
  349. package/dist/prompt-assembly/composer.js +1 -1
  350. package/dist/prompt-assembly/event-registry.js +1 -0
  351. package/dist/prompt-assembly/overlay.js +3 -3
  352. package/dist/prompt-assembly/renderers.d.ts +1 -1
  353. package/dist/prompt-assembly/renderers.js +0 -3
  354. package/dist/prompt-assembly/role-base.d.ts +15 -0
  355. package/dist/prompt-assembly/role-base.js +9 -0
  356. package/dist/prompt-assembly/select.d.ts +39 -14
  357. package/dist/prompt-assembly/select.js +47 -16
  358. package/dist/prompt-assembly/sugar.d.ts +17 -11
  359. package/dist/prompt-assembly/sugar.js +8 -3
  360. package/dist/prompt-assembly/tool-catalog.d.ts +37 -3
  361. package/dist/prompt-assembly/tool-catalog.js +114 -4
  362. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -10
  363. package/dist/prompt-assembly/turn-snapshot.js +0 -3
  364. package/dist/prompt-assembly/types.d.ts +0 -7
  365. package/dist/scenarios/full-body.d.ts +11 -11
  366. package/dist/scenarios/full-body.js +4 -3
  367. package/dist/shell/lex.js +10 -1
  368. package/dist/stores/file/adoption/adopt.js +4 -3
  369. package/dist/stores/file/adoption/marker.js +3 -2
  370. package/dist/stores/file/checkpoint-store.d.ts +21 -0
  371. package/dist/stores/file/checkpoint-store.js +38 -6
  372. package/dist/stores/file/file-history-store.js +6 -7
  373. package/dist/stores/file/index.js +2 -1
  374. package/dist/stores/file/permission-rule-adopt.js +4 -3
  375. package/dist/stores/file/permission-rule-store.js +11 -5
  376. package/dist/stores/file/session-store.d.ts +29 -6
  377. package/dist/stores/file/session-store.js +42 -50
  378. package/dist/tools/artifact/artifact-tool.js +3 -2
  379. package/dist/tools/fs/bash-program-position.d.ts +19 -2
  380. package/dist/tools/fs/bash-program-position.js +56 -9
  381. package/dist/tools/fs/fs-bash.js +5 -5
  382. package/dist/tools/fs/fs-glob.d.ts +86 -0
  383. package/dist/tools/fs/fs-glob.js +226 -0
  384. package/dist/tools/fs/fs-read.js +14 -14
  385. package/dist/tools/fs/fs-search-tools.js +7 -7
  386. package/dist/tools/fs/pattern-kill-guard.d.ts +22 -0
  387. package/dist/tools/fs/pattern-kill-guard.js +397 -0
  388. package/dist/tools/fs/pdf.d.ts +9 -6
  389. package/dist/tools/fs/pdf.js +3 -3
  390. package/dist/tools/fs/program-read-model.d.ts +57 -7
  391. package/dist/tools/fs/program-read-model.js +11 -1
  392. package/dist/tools/fs/program-read-model.rows.js +78 -21
  393. package/dist/tools/fs/read-deny-home.js +3 -2
  394. package/dist/tools/fs/removal-targets.d.ts +17 -7
  395. package/dist/tools/fs/removal-targets.js +47 -20
  396. package/dist/tools/fs/search.d.ts +10 -0
  397. package/dist/tools/fs/search.js +20 -26
  398. package/dist/tools/fs/shell-read-argv.js +89 -12
  399. package/dist/tools/fs/shell-read-bindings.d.ts +31 -0
  400. package/dist/tools/fs/shell-read-bindings.js +41 -0
  401. package/dist/tools/fs/shell-read-check.js +6 -19
  402. package/dist/tools/fs/shell-read-environment.d.ts +5 -1
  403. package/dist/tools/fs/shell-read-environment.js +3 -2
  404. package/dist/tools/fs/shell-read-establish.d.ts +15 -0
  405. package/dist/tools/fs/shell-read-establish.js +19 -20
  406. package/dist/tools/fs/shell-read-plan.js +24 -16
  407. package/dist/tools/fs/shell-read-start.d.ts +6 -1
  408. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -1
  409. package/dist/tools/fs/shell-read-write-targets.js +2 -3
  410. package/dist/tools/fs/shell-reentry-text.d.ts +9 -0
  411. package/dist/tools/fs/shell-reentry-text.js +40 -2
  412. package/dist/tools/todo.js +63 -63
  413. package/dist/tools/web.d.ts +19 -14
  414. package/dist/tools/web.js +38 -55
  415. package/package.json +1 -1
  416. package/test/export-surface.snapshot.json +143 -5
  417. package/wire-changes/core@7.56.0.json +3891 -0
  418. package/wire-changes/mods-kernel@7.56.0.json +303 -0
@@ -1,6 +1,7 @@
1
1
  import { heldBy } from "./tool-name-readings.js";
2
- import { isNamespacedCoveringRuleName, namespacedRuleNameCovers, ruleIssue } from "./permission-rules.js";
3
- import { parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js";
2
+ import { ruleIssue } from "./permission-rules.js";
3
+ import { isCoveringRuleName, ruleNameCovers } from "./namespaced-rule-name.js";
4
+ import { ruleSpellingOf } from "./permission-rule-syntax.js";
4
5
  import { judgeRuleToolName, splitNamespacedName } from "./tool-name-aliases.js";
5
6
  import { catalogToolNames, isShellFaceToolName } from "./tool-registry.js";
6
7
  import { ALLOW_RULE_MEMBERS } from "./allow-rule-shadow.js";
@@ -58,7 +59,7 @@ export function readRuleEntry(raw, behavior, roster) {
58
59
  if (parsed === undefined)
59
60
  return inert("grammar");
60
61
  const name = parsed.toolName;
61
- if (name === "*")
62
+ if (name === "*" && behavior === "allow")
62
63
  return inert("wildcard_all");
63
64
  if (roster !== undefined) {
64
65
  const stale = toolNameReading(name, roster, behavior);
@@ -66,14 +67,10 @@ export function readRuleEntry(raw, behavior, roster) {
66
67
  return stale;
67
68
  }
68
69
  if (ruleContentOf(parsed) === undefined) {
69
- const split = splitNamespacedName(name);
70
- if (split !== undefined) {
71
- if (split.peer.includes("*"))
72
- return isNamespacedCoveringRuleName(name, behavior) ? row({ kind: "namespace", pattern: name }) : inert("namespace_wildcard");
73
- if (isNamespacedCoveringRuleName(name))
74
- return row({ kind: "namespace", pattern: name });
75
- return row({ kind: "tool", name });
76
- }
70
+ if (isCoveringRuleName(name, behavior))
71
+ return row({ kind: "covering", pattern: name });
72
+ if (name.includes("*"))
73
+ return inert(splitNamespacedName(name) !== undefined ? "namespace_wildcard" : "grammar");
77
74
  return row(isShellFaceToolName(name) ? { kind: "shell", name } : { kind: "tool", name });
78
75
  }
79
76
  const grammar = ruleToolGrammarOf(name);
@@ -94,7 +91,7 @@ export function compiledEntryName(compiled) {
94
91
  case "tool":
95
92
  case "shell":
96
93
  return compiled.name;
97
- case "namespace":
94
+ case "covering":
98
95
  return compiled.pattern;
99
96
  default:
100
97
  return compiled.rule.tool;
@@ -104,11 +101,10 @@ function inertReasonOf(code) {
104
101
  return code === "unsupported.tool" ? "content_unsupported" : "grammar";
105
102
  }
106
103
  export function compileAllowList(entries, behavior, roster) {
107
- const voided = behavior === "allow" && entries.some(namesWholeRoster);
108
104
  const rows = [];
109
105
  const inert = [];
110
106
  for (const entry of entries) {
111
- const read = typeof entry !== "string" || !voided ? readRuleEntry(entry, behavior, roster) : { kind: "inert", reason: "wildcard_all" };
107
+ const read = readRuleEntry(entry, behavior, roster);
112
108
  if (read.kind === "row")
113
109
  rows.push({ entry, compiled: read.compiled });
114
110
  else {
@@ -119,9 +115,6 @@ export function compileAllowList(entries, behavior, roster) {
119
115
  }
120
116
  return { rows, inert };
121
117
  }
122
- function namesWholeRoster(entry) {
123
- return typeof entry === "string" && entry === entry.trim() && parsePermissionRule(entry).toolName === "*";
124
- }
125
118
  function answersTo(req, has) {
126
119
  return heldBy("allowEntry.names", { has }, req);
127
120
  }
@@ -160,7 +153,7 @@ export function autoStripped(compiled, call) {
160
153
  return call !== undefined && answersTo(call, isShellFaceToolName);
161
154
  case "command":
162
155
  return compiled.rule.match === "pattern" ? commandPatternMayRun(compiled.rule.command, AUTO_STRIPPED_PROGRAMS) : shellContentStripped(shellRuleContent(compiled.rule));
163
- case "namespace":
156
+ case "covering":
164
157
  case "path":
165
158
  case "name":
166
159
  case "domain":
@@ -176,7 +169,7 @@ export function autoStrippedRule(rule, call) {
176
169
  }
177
170
  const NO_LEADS = new Map();
178
171
  function nameRowReaches(compiled, req) {
179
- const covers = compiled.kind === "namespace" ? (n) => namespacedRuleNameCovers(compiled.pattern, n) : compiled.kind === "tool" || compiled.kind === "shell" ? (n) => n === compiled.name : undefined;
172
+ const covers = compiled.kind === "covering" ? (n) => ruleNameCovers(compiled.pattern, n) : compiled.kind === "tool" || compiled.kind === "shell" ? (n) => n === compiled.name : undefined;
180
173
  return covers !== undefined && answersTo(req, covers);
181
174
  }
182
175
  function commandOf(req) {
@@ -348,6 +341,16 @@ export function allowRuleShadowOf(member, entries) {
348
341
  const shown = entries.slice(0, SHADOW_ENTRIES_MAX).map((e) => inlineUntrusted(e, SHADOW_ENTRY_MAX_CHARS));
349
342
  return { member, entries: shown, ...(entries.length > shown.length ? { omitted: entries.length - shown.length } : {}) };
350
343
  }
344
+ export function withAllowRuleShadow(list, shadow) {
345
+ const at = list.findIndex((s) => s.member === shadow.member);
346
+ if (at < 0)
347
+ return [...list, shadow];
348
+ const held = list[at];
349
+ const entries = [...held.entries, ...shadow.entries];
350
+ const shown = entries.slice(0, SHADOW_ENTRIES_MAX);
351
+ const omitted = (held.omitted ?? 0) + (shadow.omitted ?? 0) + (entries.length - shown.length);
352
+ return list.map((s, i) => (i === at ? { member: held.member, entries: shown, ...(omitted > 0 ? { omitted } : {}) } : s));
353
+ }
351
354
  const MEMBER_SET = new Set(ALLOW_RULE_MEMBERS);
352
355
  export function readAllowRuleShadows(value) {
353
356
  try {
@@ -432,10 +435,11 @@ export function allowRuleShadowSentence(shadows, ask) {
432
435
  }
433
436
  export function cardDisclosureOf(decision, fallbackMessage) {
434
437
  const base = decision.message ?? fallbackMessage;
438
+ const outlet = decision.editRewritten !== undefined ? { editRewritten: decision.editRewritten } : {};
435
439
  const shadows = readAllowRuleShadows(decision.allowRuleShadowed);
436
440
  if (shadows === undefined)
437
- return { message: base };
438
- return { message: `${base} (${allowRuleShadowSentence(shadows, decision)})`, allowRuleShadowed: shadows };
441
+ return { message: base, ...outlet };
442
+ return { message: `${base} (${allowRuleShadowSentence(shadows, decision)})`, allowRuleShadowed: shadows, ...outlet };
439
443
  }
440
444
  export const ALLOWED_TOOL_NOT_GRANTED_CODE = "permission.allowed_tool_not_granted";
441
445
  const NOTICE_ENTRIES_SHOWN = 5;
@@ -486,7 +490,7 @@ export function allowedToolsNotGrantedNotice(inert, sessionId) {
486
490
  return {
487
491
  code: ALLOWED_TOOL_NOT_GRANTED_CODE,
488
492
  message: `${inert.length} allowed-tools entr${inert.length === 1 ? "y grants" : "ies grant"} or refuse${inert.length === 1 ? "s" : ""} nothing — ${shown.join("; ")}${rest > 0 ? `; and ${rest} more` : ""}. ` +
489
- "A list holding `*` voids the list, `mcp__*` names no server, a rule the grammar refuses (`grammar`) or one on a tool with no such rule form (`content_unsupported`: an allow by path is an `Edit(…)` or `Read(…)` rule) grants nothing, and a tool name must be spelled as the tool is mounted; name the tools (or `mcp__<server>`) instead." +
493
+ "On an allow list a `*` outside a protocol name's tool segment (`*`, `Re*d`, `mcp__*`) names no scope and grants nothing — the entries beside it still grant — and a rule the grammar refuses (`grammar`) or one on a tool with no such rule form (`content_unsupported`: an allow by path is an `Edit(…)` or `Read(…)` rule) grants nothing; a tool name must be spelled as the tool is mounted; name the tools (or `mcp__<server>`) instead." +
490
494
  (refusing ? " Such an entry in a disallowed-tools list keeps its Skill from activating: a refusal that reads as nothing is not dropped." : ""),
491
495
  detail: { ...(sessionId !== undefined ? { sessionId } : {}), entries: inert.map((e) => ({ ...e, entry: noticeRuleText(e.entry) })) },
492
496
  };
@@ -8,9 +8,9 @@
8
8
  * `TaskSpec.allowedTools`. The closed set {@link AllowRuleShadow.member} names. */
9
9
  export declare const ALLOW_RULE_MEMBERS: readonly ["hook", "deployment", "persisted", "skill", "request"];
10
10
  export type AllowRuleMember = (typeof ALLOW_RULE_MEMBERS)[number];
11
- /** One allow-layer member that matched a call and could not clear its ask: the member, and the entries it matched as their
12
- * source spells them (sanitized and capped at the mint; a hook names none — its word is about the call, not an entry).
13
- * `omitted` counts entries beyond the cap, never dropped silently. */
11
+ /** One allow-layer member that matched a call and could not clear its ask — one record per member: the member, and the
12
+ * entries it matched as their source spells them (sanitized and capped at the mint; the hook member names the registrations
13
+ * that spoke). `omitted` counts entries beyond the cap, never dropped silently. */
14
14
  export interface AllowRuleShadow {
15
15
  readonly member: AllowRuleMember;
16
16
  readonly entries: readonly string[];
@@ -25,6 +25,7 @@
25
25
  * the ask). Both stay where they are and are consumed beside the origin, not folded into it.
26
26
  */
27
27
  import type { PermissionResult } from "./tool-policy.js";
28
+ import { type EngineAttestation } from "./engine-attestation.js";
28
29
  import type { ContainmentFace } from "../tools/fs/containment-face.js";
29
30
  /**
30
31
  * The closed set, in AUTHORITY order — the order {@link askOriginOf} resolves a fold in when several
@@ -93,6 +94,12 @@ export type AssertAllKeysHandled<T extends never> = T;
93
94
  * itself. Every member is a plain boolean/word read ONCE by the gate, so the derivation is a pure
94
95
  * function that a test can drive arm by arm. */
95
96
  export interface AskOriginFacts {
97
+ /** The call these facts are about — the one an engine record on the decision must be bound to for the derivation to read it
98
+ * ({@link import("./engine-attestation.js").attestationOf}): a record handed back for another call reads as absent. */
99
+ readonly call: {
100
+ readonly toolCallId: string;
101
+ readonly toolName: string;
102
+ };
96
103
  /** The call is on the reserved question tool. */
97
104
  readonly contentQuestion: boolean;
98
105
  /** The call is marked unresolvable by an ancestor (see the gate's `isMarkedUnresolvable` seat). */
@@ -148,7 +155,7 @@ export declare function askOriginOf(decision: Extract<PermissionResult, {
148
155
  * word, so a site that stamped without it would be a second, quieter answer to one question.
149
156
  * The same stamp writes the ask's CLASS members (`askClass` / `classes`, {@link askClassMembersOf} over the
150
157
  * `classification` the caller derived from this decision under these facts): the word and the class leave the
151
- * gate on one object, and a value a policy put on either is replaced.
158
+ * gate on one object. No policy states either (the decision receipt refuses a stamp member).
152
159
  */
153
160
  export declare function stampAskOrigin<T extends Extract<PermissionResult, {
154
161
  action: "ask";
@@ -237,10 +244,14 @@ export interface RuleClearanceFacts {
237
244
  /** The ask is being resolved at an ANCESTOR's frozen approver (the three inherited-lane mint sites
238
245
  * pass it literally) — this task's rule lane never adjudicates it. */
239
246
  ancestorResolved?: boolean;
240
- /** The classes of the contributors the policy fold carried under this ask (see {@link import("./tool-policy.js").PermissionResult});
241
- * a member whose row no rule may clear keeps the question a person's. */
242
- foldedAskClasses?: readonly AskClass[];
247
+ /** The classes of the contributors the policy fold carried under this ask — the decision's engine record's
248
+ * ({@link EngineAttestation.foldedClasses}), filled in by the builder of these facts ({@link withFoldedOf}); a member whose row
249
+ * no rule may clear keeps the question a person's. */
250
+ foldedClasses?: readonly AskClass[];
243
251
  }
252
+ /** `facts` with the folded contributors' classes the decision's record carries for its call — the one way a builder of
253
+ * {@link RuleClearanceFacts} fills them (a decision carries no such member: the record is their one carrier). */
254
+ export declare function withFoldedOf<F extends RuleClearanceFacts>(facts: F, record: EngineAttestation | undefined): F;
244
255
  /** The ask FACE's mandate members — the bit and its table word, spread at the four mint stations and the
245
256
  * park row. The stations hand it the SAME facts object they hand the rule-offer factory, so the card's word
246
257
  * and these members are readings of one derivation and cannot drift. The tool SEAT is not re-judged here:
@@ -265,10 +276,11 @@ export declare function withProbeMandate<D extends Extract<PermissionResult, {
265
276
  }>>(decision: D, account: string | undefined): D;
266
277
  /** The clearance facts of an ask the engine did not construct (a policy's verdict), each member read ONCE into
267
278
  * an engine-owned value — for a station that keeps the answer past its own side effects, where the verdict's
268
- * accessors must not run again. The members a verdict may carry that narrow what may clear it. */
279
+ * accessors must not run again. The members a verdict may carry that narrow what may clear it, and the folded classes its
280
+ * record carries for `call`. */
269
281
  export declare function ownedClearanceFacts(d: Extract<PermissionResult, {
270
282
  action: "ask";
271
- }>): RuleClearanceFacts;
283
+ }>, call: AskOriginFacts["call"]): RuleClearanceFacts;
272
284
  /**
273
285
  * Which origins IMPLY the `requiresRealApproval` bit, and WHERE that implication is verified — the
274
286
  * consistency table {@link askOriginOf} reads. An ask of a must-carry origin exists so that no automatic
@@ -813,16 +825,22 @@ export declare function askClassMembersOf(c: AskClassification): AskClassMembers
813
825
  * first member in {@link ASK_CLASSES} order. The copy returned is fresh and frozen, never the caller's array. */
814
826
  export declare function screenAskClassMembers(askClass: unknown, classes: unknown): AskClassMembers | undefined;
815
827
  /**
816
- * The DECISION half of the class derivation — what ONE ask says about itself through its own members, read
817
- * per contributing ask at the policy fold so a member the fold does not copy (a guard mark, the unreadable-rule
818
- * fact) is not lost behind the ask that won the slot. The bare-bit rules are applied over THIS ask's own set:
819
- * `requiresRealApproval` with no explaining class is `policy_rra`; a self-declared `mandated` with no other
820
- * class is `operator_forced` (a deployment probe's own mandate, read conservatively). Returns the specific
821
- * classes only — an empty set is "nothing but hesitation", which the caller reads as `ordinary`.
828
+ * The OWN half of the class derivation — what ONE ask says about itself through its own members, and the integrity guards
829
+ * its engine record carries for its call (`record`, read by the caller through {@link attestationOf}): the guards are engine
830
+ * findings, never a member, so a policy cannot move a real-approval question into a guard's class. The bare-bit rules are
831
+ * applied over THIS ask's own set: `requiresRealApproval` with no explaining class is `policy_rra`; a self-declared
832
+ * `mandated` with no other class is `operator_forced` (a deployment probe's own mandate, read conservatively). Returns the
833
+ * specific classes only — an empty set is "nothing but hesitation", which the caller reads as `ordinary`.
822
834
  */
835
+ export declare function ownAskClassesOf(d: Extract<PermissionResult, {
836
+ action: "ask";
837
+ }>, record: EngineAttestation | undefined): Set<AskClass>;
838
+ /** The DECISION's classes as the policy fold reads a contributing ask: its own ({@link ownAskClassesOf}) and the classes of the
839
+ * asks a fold already collapsed into it ({@link EngineAttestation.foldedClasses}) — both off its record for `call`, so a
840
+ * fact the fold does not copy is not lost behind the ask that won the slot. */
823
841
  export declare function decisionAskClassesOf(d: Extract<PermissionResult, {
824
842
  action: "ask";
825
- }>): Set<AskClass>;
843
+ }>, call: AskOriginFacts["call"]): Set<AskClass>;
826
844
  /** Fold a class set onto one row: `bypassImmune` and `hookAskFloor` by OR (any immune member keeps the ask; any floored
827
845
  * member floors a hook's ask), the three permissive switches by AND (one refusing member withholds the lane). An empty
828
846
  * set is `ordinary`. The two faces the ask was raised under enter HERE and nowhere else, so every reader of the folded row
@@ -865,9 +883,36 @@ export declare function dontAskRefusalMessage(input: {
865
883
  * asked (`approvalReach: "none"`), whatever is wired. Absent in the input reads as `absent` (the posture the function was
866
884
  * written for). */
867
885
  export type HeadlessApproverSeat = "absent" | "unavailable" | "declared_none";
886
+ /** The kinds of way out a refusal carries as DATA: `approver` — a person who can answer becomes reachable (the sentence's approver clause);
887
+ * `permission_mode` — a mode that releases the ask with nobody answering it (`bypassPermissions`, unless the deployment's caps deny it:
888
+ * a denied bypass folds to `default`). A host renders a kind or leaves it unrendered; the sentence a user reads never teaches the release. */
889
+ export declare const APPROVAL_REMEDY_KINDS: readonly ["approver", "permission_mode"];
890
+ export type ApprovalRemedyKind = (typeof APPROVAL_REMEDY_KINDS)[number];
891
+ /** Membership test for {@link ApprovalRemedyKind} — the word rides a notice detail across process boundaries. */
892
+ export declare function isApprovalRemedyKind(v: unknown): v is ApprovalRemedyKind;
893
+ /** One way out, discriminated by `kind` ({@link APPROVAL_REMEDY_KINDS}). `permission_mode` names the mode (`value`: `bypassPermissions`, the one
894
+ * mode that releases an ask a person did not answer) and where the release is disclosed (`disclosedOn`: the allowed disposition's `released`
895
+ * member on the call's `tool_end.gate`) — the disclosure a host shows beside the offer. */
896
+ export type ApprovalRemedy = {
897
+ readonly kind: "approver";
898
+ } | {
899
+ readonly kind: "permission_mode";
900
+ readonly value: "bypassPermissions";
901
+ readonly disclosedOn: "gate.disposition.released";
902
+ };
903
+ /** One {@link ApprovalRemedy} read from `v`, as a fresh plain object holding only the fields its kind owns — or `undefined` when `v` is not exactly
904
+ * one (a kind outside the closed set, a member missing, a member beyond the kind's own, any field inherited rather than own). Each field is read
905
+ * once; the notice mint copies through this and refuses what it cannot read. */
906
+ export declare function readApprovalRemedy(v: unknown): ApprovalRemedy | undefined;
907
+ /** The headless remedy: `sentence` — the user-facing way out, which names the approver and never offers the bypass release — and `remedies` — every way out as data, the approver first. */
908
+ export interface HeadlessApprovalRemedy {
909
+ readonly sentence: string;
910
+ readonly remedies: readonly ApprovalRemedy[];
911
+ }
868
912
  /**
869
- * The ONE sentence a headless auto-deny (an ask that reached its final deny because no approver is wired) adds
870
- * after its refusal: the caller-side change that would have let this call run. Read off the effective mode, the
913
+ * The ONE answer a headless auto-deny (an ask that reached its final deny because no approver is wired) adds
914
+ * after its refusal: the caller-side change that would have let this call run, as a sentence (`sentence`) and as the
915
+ * ways out in data (`remedies`, {@link ApprovalRemedy}: the approver in every arm). Read off the effective mode, the
871
916
  * ask's folded class row and whether the call is a covered write (the mode write gate's own question):
872
917
  * · a class no permission mode releases (`bypassImmune`) — only a person can answer it: wire an approver;
873
918
  * · the plan mode's own question (`mode`) — leave plan (steer the mode, or approve the plan) or wire an approver;
@@ -875,11 +920,12 @@ export type HeadlessApproverSeat = "absent" | "unavailable" | "declared_none";
875
920
  * directories through without asking;
876
921
  * · the same ask under `acceptEdits` — the target lies outside the working directories: add its directory to
877
922
  * `additionalDirectories`;
878
- * · any other releasable ask — `bypassPermissions` releases it (disclosed on the call).
923
+ * · any other releasable ask, and a tool that states no permission of its own — `bypassPermissions` releases it (disclosed on the call):
924
+ * the sentence does not say so; `remedies` carries it as a `permission_mode` member for a host that chooses to offer it.
879
925
  * Every arm also names the approver, in the clause of the seat's own state ({@link HeadlessApproverSeat}): none wired ⇒
880
926
  * wire one; one wired that answered `unavailable` for this ask ⇒ make a person reachable through it (a wired approver is
881
927
  * never told to be wired); the run declared nobody can be asked ⇒ run it where a person can be, or park it. The refusal's tool text and the `delegation.ask_unresolvable` notice both carry this sentence —
882
- * they call this function, neither spells it.
928
+ * they call this function, neither spells it — and the notice carries `remedies` beside it.
883
929
  * Under `dontAsk` (the mode answers no ask: {@link dontAskRefusalMessage} carries this arm) the one question is whether an allow
884
930
  * rule may clear the ask — the folded row's `ruleMayClear`, the allow layer's own reading: yes ⇒ add an allow rule covering the
885
931
  * call, in the tool's rule shape (`ruleShape`, the caller's reading of the call's grammar — never a rule minted for the call), or
@@ -894,7 +940,7 @@ export declare function headlessApprovalRemedy(input: {
894
940
  };
895
941
  readonly approverSeat?: HeadlessApproverSeat;
896
942
  readonly ruleShape?: string;
897
- }): string;
943
+ }): HeadlessApprovalRemedy;
898
944
  /** The reference implementation's sentence for a `cp` / `mv` the mode will not answer because it carries option
899
945
  * words — the approval card's message under `acceptEdits` and the head of the headless remedy. */
900
946
  export declare function fileCommandOptionsSentence(program: "cp" | "mv"): string;
@@ -1,3 +1,4 @@
1
+ import { attestationOf } from "./engine-attestation.js";
1
2
  import { isShellFaceToolName } from "./tool-registry.js";
2
3
  export const ASK_ORIGINS = Object.freeze(["content_question", "plan_review", "ancestor_marked", "org_unavailable", "org_rule", "rule_store_unavailable", "hook", "ask_rule", "denial_limit_fallback", "shell_gate_tighten", "safety_tighten", "policy"]);
3
4
  const ASK_ORIGIN_SET = new Set(ASK_ORIGINS);
@@ -15,8 +16,8 @@ const TIGHTEN_ONLY_ORIGINS = new Set(["shell_gate_tighten", "safety_tighten"]);
15
16
  export function stampAskOrigin(decision, facts, classification) {
16
17
  const origin = askOriginOf(decision, facts);
17
18
  const classed = askClassMembersOf(classification);
18
- const { mandate: _selfDeclared, ...unstamped } = decision;
19
- const clearance = ruleClearanceOf(unstamped, facts.mandateMarks);
19
+ const { mandate: _earlierStamp, ...unstamped } = decision;
20
+ const clearance = ruleClearanceOf(withFoldedOf(unstamped, attestationOf(decision, facts.call)), facts.mandateMarks);
20
21
  const mandated = { ...unstamped, ...(clearance.mandated ? { mandated: true } : {}), ...(clearance.mandate !== undefined ? { mandate: clearance.mandate } : {}), ...(facts.mandateMarks.requiresUserInteraction === true ? { requiresRealApproval: true } : {}) };
21
22
  if (mandated.probeReadRootCandidate === undefined || TIGHTEN_ONLY_ORIGINS.has(origin))
22
23
  return { ...mandated, origin, ...classed };
@@ -26,7 +27,7 @@ export function stampAskOrigin(decision, facts, classification) {
26
27
  export const RULE_OFFERS_ABSENCES = Object.freeze(["mandated", "allow_outranked", "ask_rule_matched", "lane_cannot_speak"]);
27
28
  export function ruleClearanceOf(ask, marks) {
28
29
  const marksWord = ask?.crossBoundaryMandated === true ? "cross_boundary_taint" : persistedRuleMandateOf({ ...marks, probeMandated: ask?.probeMandated === true });
29
- const foldedDoor = (ask?.foldedAskClasses ?? []).some((c) => !MODE_ARBITRATION[c].ruleMayClear);
30
+ const foldedDoor = (ask?.foldedClasses ?? []).some((c) => !MODE_ARBITRATION[c].ruleMayClear);
30
31
  const doorOutsideTable = ask?.requiresRealApproval === true || ask?.decisionReason === "hook" || ask?.decisionReason === "mode" || foldedDoor || ask?.authoredBy === "deployment" || ask?.inheritedUnresolved === true || ask?.ancestorResolved === true;
31
32
  const mandated = ask?.mandated === true || doorOutsideTable || marksWord !== undefined;
32
33
  const stampedWord = ask?.mandated === true && isPersistedRuleMandate(ask.mandate) ? ask.mandate : undefined;
@@ -52,6 +53,10 @@ export function ruleClearanceOf(ask, marks) {
52
53
  })();
53
54
  return { ...(word !== undefined ? { word } : {}), mandated, ...(mandate !== undefined ? { mandate } : {}) };
54
55
  }
56
+ export function withFoldedOf(facts, record) {
57
+ const folded = record?.foldedClasses;
58
+ return folded !== undefined && folded.length > 0 ? { ...facts, foldedClasses: [...folded] } : facts;
59
+ }
55
60
  export function askMandateMember(ask) {
56
61
  const { mandated, mandate } = ruleClearanceOf(ask, {});
57
62
  const authored = ask?.authoredBy === "deployment" ? { authoredBy: "deployment" } : {};
@@ -63,18 +68,17 @@ export function withProbeMandate(decision, account) {
63
68
  const { probeReason: _arrivedWith, ...rest } = decision;
64
69
  return { ...rest, probeMandated: true, ...(account !== undefined ? { probeReason: account } : {}) };
65
70
  }
66
- export function ownedClearanceFacts(d) {
67
- const { mandated, requiresRealApproval, decisionReason, matchedAskRule, probeMandated, crossBoundaryMandated, authoredBy, foldedAskClasses } = d;
68
- return {
71
+ export function ownedClearanceFacts(d, call) {
72
+ const { mandated, requiresRealApproval, decisionReason, matchedAskRule, probeMandated, crossBoundaryMandated, authoredBy } = d;
73
+ return withFoldedOf({
69
74
  ...(crossBoundaryMandated === true ? { crossBoundaryMandated } : {}),
70
75
  ...(mandated === true ? { mandated } : {}),
71
- ...(Array.isArray(foldedAskClasses) && foldedAskClasses.length > 0 ? { foldedAskClasses: [...foldedAskClasses] } : {}),
72
76
  ...(authoredBy === "deployment" ? { authoredBy } : {}),
73
77
  ...(requiresRealApproval === true ? { requiresRealApproval } : {}),
74
78
  ...(decisionReason !== undefined ? { decisionReason } : {}),
75
79
  ...(typeof matchedAskRule === "string" ? { matchedAskRule } : {}),
76
80
  ...(probeMandated === true ? { probeMandated } : {}),
77
- };
81
+ }, attestationOf(d, call));
78
82
  }
79
83
  function originLadder(decision, facts) {
80
84
  if (facts.contentQuestion)
@@ -282,9 +286,9 @@ export function screenAskClassMembers(askClass, classes) {
282
286
  return undefined;
283
287
  return { askClass, classes: Object.freeze(ordered) };
284
288
  }
285
- export function decisionAskClassesOf(d) {
289
+ export function ownAskClassesOf(d, record) {
286
290
  const out = new Set();
287
- for (const guard of d.guards ?? [])
291
+ for (const guard of record?.guards ?? [])
288
292
  out.add(guard);
289
293
  if (d.ruleUnreadable !== undefined)
290
294
  out.add("rule_unreadable");
@@ -308,7 +312,12 @@ export function decisionAskClassesOf(d) {
308
312
  out.add("policy_rra");
309
313
  if (out.size === 0 && d.mandated === true)
310
314
  out.add("operator_forced");
311
- for (const c of d.foldedAskClasses ?? [])
315
+ return out;
316
+ }
317
+ export function decisionAskClassesOf(d, call) {
318
+ const record = attestationOf(d, call);
319
+ const out = ownAskClassesOf(d, record);
320
+ for (const c of record?.foldedClasses ?? [])
312
321
  out.add(c);
313
322
  return out;
314
323
  }
@@ -328,7 +337,8 @@ export function foldArbitrationRow(classes, readFace, askFace) {
328
337
  return { bypassImmune, classifierEligible, sandboxAdmission, ruleMayClear, hookAskFloor };
329
338
  }
330
339
  export function classifyAsk(d, facts) {
331
- const classes = decisionAskClassesOf({ ...d, foldedAskClasses: undefined });
340
+ const record = attestationOf(d, facts.call);
341
+ const classes = ownAskClassesOf(d, record);
332
342
  classes.delete("policy_rra");
333
343
  if (facts.contentQuestion)
334
344
  classes.add("content_question");
@@ -365,7 +375,7 @@ export function classifyAsk(d, facts) {
365
375
  classes.add(c);
366
376
  if (d.requiresRealApproval === true && ![...classes].some((c) => RRA_EXPLAINERS.has(c)))
367
377
  classes.add("policy_rra");
368
- for (const c of d.foldedAskClasses ?? [])
378
+ for (const c of record?.foldedClasses ?? [])
369
379
  classes.add(c);
370
380
  if (classes.size === 0 && d.mandated === true)
371
381
  classes.add("operator_forced");
@@ -379,15 +389,45 @@ export function headlessDenialMessage(toolName, askMessage) {
379
389
  }
380
390
  const REFUSED_CAPABILITY_GUIDANCE = "IMPORTANT: you may try to accomplish this with other tools that would naturally be used for it (for example `head` instead of `cat`), but do not work around this denial in malicious ways — for example, do not use your ability to run tests to run non-test actions; only work around it in reasonable ways that keep to the intent behind it. If you believe this capability is essential to complete the user's request, STOP and explain to the user what you were trying to do and why you need this permission, and let the user decide how to proceed.";
381
391
  export function dontAskRefusalMessage(input) {
382
- const remedy = headlessApprovalRemedy({ mode: "dontAsk", classification: input.classification, coveredWrite: input.coveredWrite, ruleShape: input.ruleShape });
392
+ const remedy = headlessApprovalRemedy({ mode: "dontAsk", classification: input.classification, coveredWrite: input.coveredWrite, ruleShape: input.ruleShape }).sentence;
383
393
  return `Permission to use ${input.toolName} has been denied because this run is in the dontAsk permission mode (this call needed approval: ${input.classification.askClass}). ${REFUSED_CAPABILITY_GUIDANCE} To let a call like this run: ${remedy}.`;
384
394
  }
395
+ export const APPROVAL_REMEDY_KINDS = Object.freeze(["approver", "permission_mode"]);
396
+ const APPROVAL_REMEDY_KIND_SET = new Set(APPROVAL_REMEDY_KINDS);
397
+ export function isApprovalRemedyKind(v) {
398
+ return APPROVAL_REMEDY_KIND_SET.has(v);
399
+ }
400
+ export function readApprovalRemedy(v) {
401
+ if (typeof v !== "object" || v === null || Array.isArray(v))
402
+ return undefined;
403
+ const keys = Object.keys(v);
404
+ const field = (k) => (keys.includes(k) ? v[k] : undefined);
405
+ const kind = field("kind");
406
+ if (!isApprovalRemedyKind(kind))
407
+ return undefined;
408
+ switch (kind) {
409
+ case "approver":
410
+ return keys.length === 1 ? { kind } : undefined;
411
+ case "permission_mode": {
412
+ const value = field("value");
413
+ const disclosedOn = field("disclosedOn");
414
+ return keys.length === 3 && value === "bypassPermissions" && disclosedOn === "gate.disposition.released" ? { kind, value, disclosedOn } : undefined;
415
+ }
416
+ default: {
417
+ const _exhaustive = kind;
418
+ return _exhaustive;
419
+ }
420
+ }
421
+ }
422
+ const APPROVER_REMEDY = Object.freeze({ kind: "approver" });
423
+ const RELEASE_REMEDY = Object.freeze({ kind: "permission_mode", value: "bypassPermissions", disclosedOn: "gate.disposition.released" });
385
424
  export function headlessApprovalRemedy(input) {
425
+ const way = (sentence, release = false) => ({ sentence, remedies: Object.freeze(release ? [APPROVER_REMEDY, RELEASE_REMEDY] : [APPROVER_REMEDY]) });
386
426
  if (input.mode === "dontAsk") {
387
427
  const c = input.classification;
388
- return c?.row.ruleMayClear === true
428
+ return way(c?.row.ruleMayClear === true
389
429
  ? `an allow rule covering this call answers it under dontAsk — add one${input.ruleShape !== undefined ? ` in this tool's rule shape (\`${input.ruleShape}\`)` : ""} to the allow rules (a settings file's \`permissions.allow\`, or the request's or the deployment's allow list), or run under a mode with an approver`
390
- : `no allow rule answers this ask${c !== undefined ? ` (${c.askClass})` : ""} — only a person can: run under a mode with an approver, or change the call`;
430
+ : `no allow rule answers this ask${c !== undefined ? ` (${c.askClass})` : ""} — only a person can: run under a mode with an approver, or change the call`);
391
431
  }
392
432
  const approver = input.approverSeat === "unavailable"
393
433
  ? "make a person reachable through the approver that is wired (it answered `unavailable` for this ask)"
@@ -396,21 +436,21 @@ export function headlessApprovalRemedy(input) {
396
436
  : "wire an approver (`onAsk`) to put it to a person";
397
437
  const c = input.classification;
398
438
  if (input.mode === "acceptEdits" && input.unplacedOptions !== undefined && (c === undefined || (c.classes.size === 1 && c.classes.has("ordinary"))))
399
- return `${fileCommandOptionsSentence(input.unplacedOptions.program)} Spell the ${input.unplacedOptions.program} without option words for the mode to answer it, or ${approver}`;
439
+ return way(`${fileCommandOptionsSentence(input.unplacedOptions.program)} Spell the ${input.unplacedOptions.program} without option words for the mode to answer it, or ${approver}`);
400
440
  if (c !== undefined && bypassImmuneOf(c))
401
- return `no permission mode releases this ask (${c.askClass}): ${approver}, or change the call`;
441
+ return way(`no permission mode releases this ask (${c.askClass}): ${approver}, or change the call`);
402
442
  if (input.mode === "plan" && c?.classes.has("mode") === true)
403
- return `the plan permission mode asks before every write and every call to a tool the engine does not define that is not read-only — leave plan mode (\`setPermissionMode\` to another mode, or approve the plan) to run it, or ${approver}`;
443
+ return way(`the plan permission mode asks before every write and every call to a tool the engine does not define that is not read-only — leave plan mode (\`setPermissionMode\` to another mode, or approve the plan) to run it, or ${approver}`);
404
444
  if (c !== undefined && c.classes.size === 1 && c.classes.has("unstated"))
405
- return `no allow entry, stored allow rule or hook granted this tool, and it states no permission of its own — grant it with an allow entry for its name (\`RunnerDeps.allowedTools\` / \`TaskSpec.allowedTools\`), run with \`permissionMode: "bypassPermissions"\`, or ${approver}`;
445
+ return way(`no allow entry, stored allow rule or hook granted this tool, and it states no permission of its own — grant it with an allow entry for its name (\`RunnerDeps.allowedTools\` / \`TaskSpec.allowedTools\`), or ${approver}`, true);
406
446
  const onlyOrdinary = c === undefined || (c.classes.size === 1 && c.classes.has("ordinary"));
407
447
  if (input.coveredWrite && onlyOrdinary) {
408
448
  if (input.mode === "acceptEdits")
409
- return `under \`permissionMode: "acceptEdits"\` the mode asks only for a write outside the working directories — if this target is outside them, add its directory to \`additionalDirectories\`; otherwise (or besides) ${approver}`;
449
+ return way(`under \`permissionMode: "acceptEdits"\` the mode asks only for a write outside the working directories — if this target is outside them, add its directory to \`additionalDirectories\`; otherwise (or besides) ${approver}`);
410
450
  if (input.mode === undefined || input.mode === "default" || input.mode === "auto")
411
- return `the mode's write gate asks every write under \`default\`: \`permissionMode: "acceptEdits"\` lets writes inside the working directories (the task root and \`additionalDirectories\`) through without asking (a question another policy raised still stands), or ${approver}`;
451
+ return way(`the mode's write gate asks every write under \`default\`: \`permissionMode: "acceptEdits"\` lets writes inside the working directories (the task root and \`additionalDirectories\`) through without asking (a question another policy raised still stands), or ${approver}`);
412
452
  }
413
- return `run with \`permissionMode: "bypassPermissions"\` to release this ask (the release is disclosed on the call), or ${approver}`;
453
+ return way(approver, true);
414
454
  }
415
455
  export function fileCommandOptionsSentence(program) {
416
456
  return `${program} with flags requires manual approval to ensure path safety: the mode cannot automatically validate ${program} commands that use flags, as some flags like --target-directory=PATH can bypass path validation.`;
@@ -3,15 +3,23 @@ import type { ToolPolicy } from "./tool-policy.js";
3
3
  /** The reserved tool name for AskUserQuestion — shared so the durable-resume path (runtask) can recognize it
4
4
  * as a content-ask whose SHOWN questions must not be rewritten by an operator `updatedInput` (design/80 D-F).
5
5
  *
6
- * RESERVED means the whole engine keys content-ask semantics on this name: a policy `ask` on it is NEVER
7
- * routed to a synchronous PERMISSION approver — on the main gate, the degraded fall-through, and the
8
- * inherited ancestor wrappers alike (a permission frame's allow/deny cannot carry an ANSWER; CC parity:
9
- * AskUserQuestion never raises a permission card). What it DOES reach, in this order, is the run's own
10
- * content-ask channel: a LIVE question face answers it in-stream (the gate routes the question, not a
11
- * verdict), and only with no live face — or with the durable gate forced — does it park durably or answer
12
- * the typed refusal. Permission-GATING this tool stays unsupported by design; content-ROUTING it is the
13
- * supported path. A caller-supplied tool reusing this name inherits the same routing; don't. */
6
+ * The engine mounts its own question tool under this name, and the gate's content-ask semantics belong to THAT tool,
7
+ * read off the call's face ({@link isEngineQuestionFace}), never off this name: a policy `ask` on it is NEVER routed to
8
+ * a synchronous PERMISSION approver (a permission frame's allow/deny cannot carry an ANSWER; CC parity: AskUserQuestion
9
+ * never raises a permission card). What it DOES reach, in this order, is the run's own content-ask channel: a LIVE
10
+ * question face answers it in-stream (the gate routes the question, not a verdict), and only with no live face — or
11
+ * with the durable gate forced — does it park durably or answer the typed refusal. Permission-GATING this tool stays
12
+ * unsupported by design; content-ROUTING it is the supported path. A caller-supplied tool reusing this name is an
13
+ * ordinary caller tool to the gate, its park decline, an ancestor's settlement and a person's edit of its parked call — asked, cleared by an allow rule,
14
+ * released by a mode or refused like any other; the approval card a call carries is still chosen by this name. */
14
15
  export declare const ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
16
+ /** "Is this call the engine's own question tool?" — the catalog's one mount judge ({@link engineMountedAs}) for the
17
+ * question definition: every station that treats a call as a content ask (the gate's content-question class and route,
18
+ * the park's sync-first decline, the ancestor adapters' question float, {@link createDurableQuestionPolicy}) reads this answer. */
19
+ export declare const isEngineQuestionFace: (face: {
20
+ readonly engineMounted?: true;
21
+ readonly contractId: string;
22
+ } | undefined) => boolean;
15
23
  /**
16
24
  * AskUserQuestion (design/64 §5). A model-callable tool that lets the agent ask the USER a structured
17
25
  * multiple-choice question (which approach? which of these?) — distinct from the design/37 permission `ask`
@@ -278,6 +286,41 @@ export interface AskUserQuestionToolOptions {
278
286
  * would let a LATER question inherit the claim that an operator approved it. */
279
287
  redeemedApprovalQuestionsHash?: string;
280
288
  }
289
+ /**
290
+ * Durable ask (design/64 TC-5.4 — ABOVE CC, the disconnected-human case): instead of the headless
291
+ * "proceed with your best judgment" default, an AskUserQuestion **suspends the task durably** and the
292
+ * answer resumes it — built ENTIRELY from existing primitives, no new gate kind.
293
+ *
294
+ * **The park is the fallback, not the mandate.** An adjudicated question parks durably UNLESS this run
295
+ * has a live question face (see {@link isLiveQuestionFace}), in which case the engine routes the question
296
+ * to that face and the answer arrives in-stream — same turn, no checkpoint, no rebuild. The park is what
297
+ * happens when nobody is there: no live face, the face affirming nobody is reachable, or a control-plane
298
+ * `forceDurableGate` that mandates the durable breakpoint regardless. A deployment that wired a live face
299
+ * AND wants every question parked anyway states that with `forceDurableGate` (or by not wiring the face).
300
+ *
301
+ * 1. Run the task with `durableApproval` + a durable `CheckpointStore` (+ durable session/offload
302
+ * stores) and `combinePolicies(createDurableQuestionPolicy(), yourPolicy)`. The policy adjudicates
303
+ * the AskUserQuestion call `ask`; in durable mode that suspends BEFORE the tool executes —
304
+ * `onQuestion` is never called on the suspend side, so mount the tool with
305
+ * {@link QUESTION_AWAITS_RESUME} when no live human is connected.
306
+ * 2. The HITL surface reads the questions from `checkpoint.pendingAction.args.questions` and collects
307
+ * the human's {@link QuestionAnswer} out-of-band (hours/days later, any replica).
308
+ * 3. `runner.resume(token, { gate: "policy_ask", decision: "allow", boundCallId:
309
+ * checkpoint.pendingAction.toolCallId }, { ...taskConfig, onQuestion: async () => answer })` — the resume
310
+ * re-mounts the tool with the answer closure; the pending call executes against it and the model
311
+ * continues with "The user answered: …". `boundCallId` (design/80 D-1) must name the pending call the
312
+ * human answered, or the resume is rejected (`checkpoint.invalid_outcome`).
313
+ *
314
+ * A deployment that wants the CC-style threshold form ("wait N minutes for a live human, then go
315
+ * durable") implements it inside its own policy: return `allow` while a human is connected (the live
316
+ * `onQuestion` answers synchronously) and `ask` when none is — the sync-vs-durable choice is exactly
317
+ * one policy decision. Composing this policy's answer is how such a policy keeps the one judge (an `ask`
318
+ * from here means the call is the engine's question tool).
319
+ *
320
+ * The call it asks about is the ENGINE's question tool, read off the request's face ({@link isEngineQuestionFace}):
321
+ * a caller's tool that took the name is not asked about here, and neither is a bare request (no face — a host
322
+ * driving a policy outside a Runner stamps the face itself, `toolCallFaceOf(roster, name)`).
323
+ */
281
324
  export declare function createDurableQuestionPolicy(): ToolPolicy;
282
325
  /**
283
326
  * Placeholder {@link OnQuestion} that mounts the AskUserQuestion tool on a worker with NO live human
@@ -3,10 +3,10 @@ import { uuidv7 } from "../engine/session/uuid.js";
3
3
  import { boundInputHashOf, capturedJsonOf } from "./canonical-json.js";
4
4
  import { defineTool, errorResult } from "./tools.js";
5
5
  import { delimitUntrusted, inlineUntrusted } from "./untrusted-text.js";
6
- import { mountRuleFace, toolFace } from "./tool-catalog-entries.js";
6
+ import { engineMountedAs, mountRuleFace, toolFace } from "./tool-catalog-entries.js";
7
7
  import { thrownText } from "./thrown-value.js";
8
- import { heldBy } from "./tool-name-readings.js";
9
8
  export const ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
9
+ export const isEngineQuestionFace = engineMountedAs(["ask-user-question"]);
10
10
  export function classifyQuestionOutcome(outcome) {
11
11
  let unavailable;
12
12
  let answers;
@@ -69,11 +69,10 @@ export function validateAskQuestions(questions) {
69
69
  }
70
70
  return undefined;
71
71
  }
72
- const QUESTION_TOOL_NAMES = new Set([ASK_USER_QUESTION_TOOL_NAME]);
73
72
  export function createDurableQuestionPolicy() {
74
73
  return {
75
74
  check(req) {
76
- if (!heldBy("policy.durableQuestion", QUESTION_TOOL_NAMES, req)) {
75
+ if (!isEngineQuestionFace(req.face)) {
77
76
  return { action: "allow" };
78
77
  }
79
78
  return { action: "ask", message: "AskUserQuestion: awaiting a human answer (durable)" };
@@ -28,7 +28,7 @@
28
28
  * is pure.
29
29
  */
30
30
  import type { EngineNotice } from "./engine-notice.js";
31
- import type { AssertAllKeysHandled, HeadlessApproverSeat } from "./ask-origin.js";
31
+ import { type ApprovalRemedy, type AssertAllKeysHandled, type HeadlessApproverSeat } from "./ask-origin.js";
32
32
  /** WHY the ask could not be resolved — the closed set (see the module note for each word). */
33
33
  export declare const ASK_UNRESOLVABLE_CAUSES: readonly ["approver_unavailable", "no_approver_wired", "mandate_unreconstructible"];
34
34
  export type AskUnresolvableCause = (typeof ASK_UNRESOLVABLE_CAUSES)[number];
@@ -52,6 +52,10 @@ export interface AskUnresolvableFacts {
52
52
  * for arguments no approval can be bound to, the binding's own sentence, which is that text — the remedy's approver half would
53
53
  * not let them run) — carried verbatim, never re-composed here. */
54
54
  readonly remedy?: string;
55
+ /** The ways out of that same refusal as data ({@link ApprovalRemedy}) — the approver, and the permission mode where the refusal could be
56
+ * released by one — for a host that chooses which to offer. Rides exactly when {@link remedy} is the headless remedy's sentence
57
+ * (`headlessApprovalRemedy`); a binding's own sentence, which has no way out to offer, carries none. */
58
+ readonly remedies?: readonly ApprovalRemedy[];
55
59
  }
56
60
  /**
57
61
  * Which causes take the caller-side remedy (`headlessApprovalRemedy`, ask-origin.ts), and in which seat's clause — the table IS
@@ -82,8 +86,9 @@ export declare function askUnresolvableRemedySeat(cause: AskUnresolvableCause):
82
86
  * an empty session or tool name, or a cause outside the closed set, is refused at the mint with a
83
87
  * `TypeError` rather than shaped into a notice that would route nowhere or say nothing.
84
88
  *
85
- * `detail: { sessionId, toolName, toolCallId, cause, parkLaneExisted, remedy? }` — `remedy` rides the causes that say nobody
86
- * could be asked ({@link askUnresolvableRemedySeat}: the sentence the refusal's tool text carries); `sessionId` is lifted onto the typed key by {@link import("./engine-notice.js").deliverEngineNotice}, the one throat.
89
+ * `detail: { sessionId, toolName, toolCallId, cause, parkLaneExisted, remedy?, remedies? }` — `remedy` rides the causes that say nobody
90
+ * could be asked ({@link askUnresolvableRemedySeat}: the sentence the refusal's tool text carries) and `remedies` beside it, the same
91
+ * way out as data ({@link ApprovalRemedy}); `sessionId` is lifted onto the typed key by {@link import("./engine-notice.js").deliverEngineNotice}, the one throat.
87
92
  */
88
93
  export declare function askUnresolvableNotice(facts: AskUnresolvableFacts): EngineNotice;
89
94
  export {};