@sema-agent/core 7.55.0 → 7.57.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 (502) hide show
  1. package/CHANGELOG.md +1054 -0
  2. package/LICENSE +1 -1
  3. package/dist/agents/cascade.js +1 -1
  4. package/dist/agents/cc-peer-directory.js +1 -0
  5. package/dist/agents/child-model-seat.d.ts +0 -11
  6. package/dist/agents/child-model-seat.js +0 -3
  7. package/dist/agents/list-agents-tool.d.ts +3 -1
  8. package/dist/agents/list-agents-tool.js +7 -7
  9. package/dist/agents/org-delivery-gate.js +3 -4
  10. package/dist/agents/peer-directory.d.ts +22 -4
  11. package/dist/agents/peer-directory.js +24 -2
  12. package/dist/agents/peer-held-queue.d.ts +69 -49
  13. package/dist/agents/peer-held-queue.js +255 -106
  14. package/dist/agents/peer-session-drain.js +65 -45
  15. package/dist/agents/repair-loop.js +2 -2
  16. package/dist/agents/send-message-tool.js +7 -6
  17. package/dist/agents/subagent.d.ts +6 -4
  18. package/dist/agents/subagent.js +72 -68
  19. package/dist/agents/tool-filter.d.ts +7 -5
  20. package/dist/agents/tool-filter.js +12 -9
  21. package/dist/agents/verify.d.ts +3 -2
  22. package/dist/agents/verify.js +3 -3
  23. package/dist/brain/anthropic.js +45 -80
  24. package/dist/brain/media-degrade.d.ts +18 -7
  25. package/dist/brain/media-degrade.js +13 -2
  26. package/dist/brain/model-capability.d.ts +37 -0
  27. package/dist/brain/model-capability.js +34 -0
  28. package/dist/brain/model-presets.d.ts +2 -8
  29. package/dist/brain/model-presets.js +1 -6
  30. package/dist/brain/open-responses.js +23 -34
  31. package/dist/brain/openai.js +25 -96
  32. package/dist/brain/provider-dialect.d.ts +3 -2
  33. package/dist/brain/provider-dialect.js +2 -2
  34. package/dist/brain/reasoning-request.d.ts +96 -0
  35. package/dist/brain/reasoning-request.js +174 -0
  36. package/dist/brain/reasoning.d.ts +11 -11
  37. package/dist/brain/reasoning.js +4 -3
  38. package/dist/brain/request-params.d.ts +13 -4
  39. package/dist/brain/request-params.js +21 -5
  40. package/dist/brain/status-sink.d.ts +20 -0
  41. package/dist/brain/status-sink.js +7 -0
  42. package/dist/brain/stream-engine.d.ts +9 -0
  43. package/dist/brain/stream-engine.js +24 -10
  44. package/dist/brain/stream-shared.d.ts +5 -4
  45. package/dist/brain/stream-shared.js +4 -4
  46. package/dist/core/a2a.d.ts +2 -1
  47. package/dist/core/a2a.js +19 -13
  48. package/dist/core/agent-definition.d.ts +10 -0
  49. package/dist/core/allow-entry.d.ts +23 -23
  50. package/dist/core/allow-entry.js +42 -46
  51. package/dist/core/allow-rule-shadow.d.ts +3 -3
  52. package/dist/core/approval-object.d.ts +21 -5
  53. package/dist/core/approval-object.js +44 -17
  54. package/dist/core/ask-origin.d.ts +66 -20
  55. package/dist/core/ask-origin.js +63 -23
  56. package/dist/core/ask-question.d.ts +51 -8
  57. package/dist/core/ask-question.js +3 -4
  58. package/dist/core/ask-unresolvable-notice.d.ts +8 -3
  59. package/dist/core/ask-unresolvable-notice.js +16 -2
  60. package/dist/core/auto-compaction.d.ts +71 -6
  61. package/dist/core/auto-compaction.js +42 -26
  62. package/dist/core/background-agent-store.d.ts +6 -2
  63. package/dist/core/background-agent-store.js +1 -1
  64. package/dist/core/checkpoint-generation.d.ts +53 -0
  65. package/dist/core/checkpoint-generation.js +35 -0
  66. package/dist/core/checkpoint-queries.d.ts +180 -0
  67. package/dist/core/checkpoint-queries.js +272 -0
  68. package/dist/core/checkpoint-requirements.d.ts +19 -1
  69. package/dist/core/checkpoint-requirements.js +6 -0
  70. package/dist/core/checkpoint-store.d.ts +105 -53
  71. package/dist/core/checkpoint-store.js +43 -20
  72. package/dist/core/cross-boundary-taint.d.ts +6 -5
  73. package/dist/core/cross-boundary-taint.js +5 -5
  74. package/dist/core/decision-receipt.d.ts +37 -3
  75. package/dist/core/decision-receipt.js +17 -17
  76. package/dist/core/delegation-not-started.d.ts +6 -0
  77. package/dist/core/delegation-not-started.js +17 -0
  78. package/dist/core/engine-attestation.d.ts +93 -0
  79. package/dist/core/engine-attestation.js +70 -0
  80. package/dist/core/engine-capabilities.d.ts +4 -1
  81. package/dist/core/engine-capabilities.js +1 -1
  82. package/dist/core/engine-notice.d.ts +32 -12
  83. package/dist/core/engine-notice.js +6 -0
  84. package/dist/core/file-history-store.d.ts +104 -22
  85. package/dist/core/file-history-store.js +37 -19
  86. package/dist/core/gate-fold.d.ts +1 -1
  87. package/dist/core/gate-fold.js +22 -10
  88. package/dist/core/gate-lanes.js +73 -65
  89. package/dist/core/gate-outcome.d.ts +26 -5
  90. package/dist/core/gate-outcome.js +26 -0
  91. package/dist/core/governance-codes.d.ts +5 -1
  92. package/dist/core/governance-codes.js +6 -0
  93. package/dist/core/hooks.d.ts +19 -18
  94. package/dist/core/hooks.js +3 -1
  95. package/dist/core/host-floor.d.ts +128 -0
  96. package/dist/core/host-floor.js +206 -0
  97. package/dist/core/host-target.d.ts +38 -3
  98. package/dist/core/host-target.js +38 -5
  99. package/dist/core/mailbox-store.d.ts +62 -1
  100. package/dist/core/mailbox-store.js +59 -0
  101. package/dist/core/mcp-name-judge.d.ts +46 -0
  102. package/dist/core/mcp-name-judge.js +61 -0
  103. package/dist/core/mcp-probe.d.ts +1 -1
  104. package/dist/core/mcp-probe.js +2 -5
  105. package/dist/core/mcp.d.ts +5 -1
  106. package/dist/core/mcp.js +70 -64
  107. package/dist/core/memory-engine/consolidation-driver.js +4 -3
  108. package/dist/core/memory-engine/delegation-settlement.js +15 -24
  109. package/dist/core/memory-engine/distiller.js +26 -17
  110. package/dist/core/memory-engine/engine.js +4 -4
  111. package/dist/core/memory-engine/layout.d.ts +60 -8
  112. package/dist/core/memory-engine/layout.js +70 -8
  113. package/dist/core/model-seat.d.ts +10 -3
  114. package/dist/core/mods/classic-events.d.ts +67 -0
  115. package/dist/core/mods/classic-events.js +1 -0
  116. package/dist/core/mods/dollar.d.ts +13 -0
  117. package/dist/core/mods/extension-call.d.ts +31 -0
  118. package/dist/core/mods/extension-call.js +43 -0
  119. package/dist/core/mods/kernel/dispatch.d.ts +20 -3
  120. package/dist/core/mods/kernel/dispatch.js +140 -69
  121. package/dist/core/mods/kernel/events.d.ts +47 -11
  122. package/dist/core/mods/kernel/events.js +66 -42
  123. package/dist/core/mods/kernel/index.d.ts +4 -4
  124. package/dist/core/mods/kernel/index.js +2 -2
  125. package/dist/core/mods/kernel/receive.d.ts +39 -5
  126. package/dist/core/mods/kernel/receive.js +290 -17
  127. package/dist/core/mods/kernel/registration.d.ts +33 -2
  128. package/dist/core/mods/kernel/registration.js +6 -7
  129. package/dist/core/mods/kernel/site-rules.d.ts +44 -16
  130. package/dist/core/mods/kernel/site-rules.js +24 -42
  131. package/dist/core/mods/spec-seats.d.ts +69 -0
  132. package/dist/core/mods/spec-seats.js +114 -0
  133. package/dist/core/mods/stations.d.ts +28 -8
  134. package/dist/core/mods/stations.js +29 -13
  135. package/dist/core/mods/tool-call.d.ts +79 -0
  136. package/dist/core/mods/tool-call.js +152 -0
  137. package/dist/core/namespaced-rule-name.d.ts +32 -38
  138. package/dist/core/namespaced-rule-name.js +29 -36
  139. package/dist/core/parent-spec-seat.d.ts +6 -1
  140. package/dist/core/parent-spec-seat.js +2 -0
  141. package/dist/core/park-selfcheck.js +3 -0
  142. package/dist/core/permission-rule-consent.d.ts +24 -3
  143. package/dist/core/permission-rule-consent.js +136 -41
  144. package/dist/core/permission-rule-model.d.ts +57 -5
  145. package/dist/core/permission-rule-model.js +90 -31
  146. package/dist/core/permission-rule-org.d.ts +6 -2
  147. package/dist/core/permission-rule-org.js +8 -4
  148. package/dist/core/permission-rule-provider.d.ts +5 -2
  149. package/dist/core/permission-rule-provider.js +41 -11
  150. package/dist/core/permission-rule-store.d.ts +3 -2
  151. package/dist/core/permission-rule-store.js +11 -2
  152. package/dist/core/permission-rule-sync.js +3 -2
  153. package/dist/core/permission-rule-syntax.d.ts +5 -4
  154. package/dist/core/permission-rule-syntax.js +2 -2
  155. package/dist/core/permission-rules.d.ts +4 -4
  156. package/dist/core/permission-rules.js +11 -20
  157. package/dist/core/persisted-rule-arms.d.ts +2 -2
  158. package/dist/core/persisted-rule-arms.js +10 -12
  159. package/dist/core/principal-name.d.ts +20 -0
  160. package/dist/core/principal-name.js +22 -0
  161. package/dist/core/prompt-section-seat.js +4 -6
  162. package/dist/core/protocol-http-auth.d.ts +40 -5
  163. package/dist/core/protocol-http-auth.js +168 -4
  164. package/dist/core/protocol-naming.d.ts +20 -0
  165. package/dist/core/protocol-naming.js +20 -3
  166. package/dist/core/read-only-shell.d.ts +20 -5
  167. package/dist/core/read-only-shell.js +29 -14
  168. package/dist/core/remote-env.d.ts +37 -1
  169. package/dist/core/remote-env.js +11 -0
  170. package/dist/core/resume-startup-cause.d.ts +24 -2
  171. package/dist/core/resume-startup-cause.js +20 -1
  172. package/dist/core/retired-keys.d.ts +16 -0
  173. package/dist/core/retired-keys.js +16 -4
  174. package/dist/core/rewrite-rejudge.d.ts +60 -0
  175. package/dist/core/rewrite-rejudge.js +36 -0
  176. package/dist/core/roles.d.ts +28 -1
  177. package/dist/core/roles.js +81 -26
  178. package/dist/core/rule-lane-reads.d.ts +20 -15
  179. package/dist/core/rule-lane-reads.js +45 -17
  180. package/dist/core/rule-owner.d.ts +3 -3
  181. package/dist/core/rule-owner.js +3 -2
  182. package/dist/core/runner/announce-once-ledger.d.ts +14 -0
  183. package/dist/core/runner/announce-once-ledger.js +5 -0
  184. package/dist/core/runner/assemble-result.d.ts +1 -0
  185. package/dist/core/runner/assemble-result.js +1 -1
  186. package/dist/core/runner/call-hook-stations.d.ts +3 -2
  187. package/dist/core/runner/caller-roster.d.ts +16 -4
  188. package/dist/core/runner/caller-roster.js +33 -6
  189. package/dist/core/runner/capability-withheld-seat.d.ts +5 -0
  190. package/dist/core/runner/capability-withheld-seat.js +24 -0
  191. package/dist/core/runner/classifier-ladder.d.ts +7 -9
  192. package/dist/core/runner/classifier-ladder.js +3 -3
  193. package/dist/core/runner/compact-session.d.ts +10 -0
  194. package/dist/core/runner/compact-session.js +202 -0
  195. package/dist/core/runner/compaction-call-options.d.ts +2 -2
  196. package/dist/core/runner/compaction-seams.d.ts +21 -1
  197. package/dist/core/runner/compaction-seams.js +11 -8
  198. package/dist/core/runner/construction-doors.js +3 -0
  199. package/dist/core/runner/contracts.d.ts +50 -40
  200. package/dist/core/runner/cross-boundary-lane.d.ts +17 -13
  201. package/dist/core/runner/cross-boundary-lane.js +40 -27
  202. package/dist/core/runner/denial-limit-arms.d.ts +2 -2
  203. package/dist/core/runner/denial-limit-arms.js +1 -1
  204. package/dist/core/runner/derived-route-fallback.d.ts +13 -0
  205. package/dist/core/runner/derived-route-fallback.js +6 -0
  206. package/dist/core/runner/env-capability-census.d.ts +3 -2
  207. package/dist/core/runner/env-capability-census.js +9 -3
  208. package/dist/core/runner/file-history-request.d.ts +18 -0
  209. package/dist/core/runner/file-history-request.js +21 -13
  210. package/dist/core/runner/file-history-restore.d.ts +45 -0
  211. package/dist/core/runner/file-history-restore.js +46 -0
  212. package/dist/core/runner/gate-exit.d.ts +3 -3
  213. package/dist/core/runner/gate-exit.js +4 -3
  214. package/dist/core/runner/image.d.ts +7 -17
  215. package/dist/core/runner/image.js +9 -60
  216. package/dist/core/runner/permission-rule-lanes.d.ts +11 -11
  217. package/dist/core/runner/permission-rule-lanes.js +39 -17
  218. package/dist/core/runner/person-path-rows.d.ts +12 -9
  219. package/dist/core/runner/person-path-rows.js +18 -7
  220. package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -0
  221. package/dist/core/runner/prepare-acquire-reconcile.js +2 -1
  222. package/dist/core/runner/prepare-ask-lane.d.ts +2 -2
  223. package/dist/core/runner/prepare-ask-lane.js +5 -2
  224. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -6
  225. package/dist/core/runner/prepare-caps-and-workflow.js +4 -3
  226. package/dist/core/runner/prepare-config-doors.d.ts +35 -38
  227. package/dist/core/runner/prepare-config-doors.js +46 -28
  228. package/dist/core/runner/prepare-defer-classify.d.ts +4 -3
  229. package/dist/core/runner/prepare-defer-classify.js +7 -8
  230. package/dist/core/runner/prepare-file-history.d.ts +48 -8
  231. package/dist/core/runner/prepare-file-history.js +7 -39
  232. package/dist/core/runner/prepare-gate-stations.d.ts +1 -1
  233. package/dist/core/runner/prepare-gate-stations.js +10 -1
  234. package/dist/core/runner/prepare-hands-readface.d.ts +5 -9
  235. package/dist/core/runner/prepare-hands-readface.js +17 -14
  236. package/dist/core/runner/prepare-inherited-gate.d.ts +6 -3
  237. package/dist/core/runner/prepare-inherited-gate.js +5 -3
  238. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  239. package/dist/core/runner/prepare-park-ask.d.ts +2 -0
  240. package/dist/core/runner/prepare-park-ask.js +7 -7
  241. package/dist/core/runner/prepare-policy-chain.d.ts +6 -3
  242. package/dist/core/runner/prepare-policy-chain.js +67 -76
  243. package/dist/core/runner/prepare-prompt-assembly.d.ts +5 -7
  244. package/dist/core/runner/prepare-prompt-assembly.js +10 -8
  245. package/dist/core/runner/prepare-prompt-inputs.d.ts +11 -9
  246. package/dist/core/runner/prepare-prompt-inputs.js +3 -5
  247. package/dist/core/runner/prepare-protocol-tools.d.ts +2 -0
  248. package/dist/core/runner/prepare-protocol-tools.js +3 -3
  249. package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
  250. package/dist/core/runner/prepare-safety-scan.js +0 -17
  251. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
  252. package/dist/core/runner/prepare-suspend-saga.js +5 -6
  253. package/dist/core/runner/prepare-task.d.ts +5 -2
  254. package/dist/core/runner/prepare-task.js +48 -43
  255. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +20 -4
  256. package/dist/core/runner/prepare-tool-disclosure-mount.js +33 -23
  257. package/dist/core/runner/prepare-turn-wiring.d.ts +0 -4
  258. package/dist/core/runner/prepare-turn-wiring.js +2 -7
  259. package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -2
  260. package/dist/core/runner/prepare-wiring-manifest.js +3 -3
  261. package/dist/core/runner/resume-admission.js +1 -1
  262. package/dist/core/runner/resume-apply.js +11 -12
  263. package/dist/core/runner/resume-checkpoint-screen.js +3 -0
  264. package/dist/core/runner/resume-claim.d.ts +4 -2
  265. package/dist/core/runner/resume-claim.js +15 -8
  266. package/dist/core/runner/retired-runner-deps.js +1 -0
  267. package/dist/core/runner/rewind-files.d.ts +12 -0
  268. package/dist/core/runner/rewind-files.js +90 -0
  269. package/dist/core/runner/run-attachment-seats.js +2 -1
  270. package/dist/core/runner/run-brain-sinks.d.ts +4 -1
  271. package/dist/core/runner/run-brain-sinks.js +4 -2
  272. package/dist/core/runner/run-harness-handlers.js +12 -4
  273. package/dist/core/runner/run-leg.d.ts +1 -1
  274. package/dist/core/runner/run-leg.js +40 -39
  275. package/dist/core/runner/run-notification-lane.d.ts +2 -0
  276. package/dist/core/runner/run-notification-lane.js +2 -2
  277. package/dist/core/runner/run-recovery-lanes.js +3 -4
  278. package/dist/core/runner/run-settle-and-teardown.js +7 -12
  279. package/dist/core/runner/run-terminal-adoption.js +3 -4
  280. package/dist/core/runner/run-turn-boundary.js +4 -15
  281. package/dist/core/runner/runtask.d.ts +10 -0
  282. package/dist/core/runner/runtask.js +35 -42
  283. package/dist/core/runner/send-message-continuation.js +1 -1
  284. package/dist/core/runner/session-read-file-states.d.ts +15 -0
  285. package/dist/core/runner/session-read-file-states.js +28 -0
  286. package/dist/core/runner/session-rule-policy.js +4 -3
  287. package/dist/core/runner/session-verb-doors.d.ts +28 -0
  288. package/dist/core/runner/session-verb-doors.js +48 -0
  289. package/dist/core/runner/session-write-ledger.d.ts +30 -0
  290. package/dist/core/runner/session-write-ledger.js +101 -0
  291. package/dist/core/runner/shell-env-withheld.d.ts +33 -0
  292. package/dist/core/runner/shell-env-withheld.js +51 -0
  293. package/dist/core/runner/skills-seat.js +6 -2
  294. package/dist/core/runner/stream-settle-backstop.js +12 -15
  295. package/dist/core/runner/sub-agent-deny-lists.d.ts +25 -3
  296. package/dist/core/runner/sub-agent-deny-lists.js +25 -17
  297. package/dist/core/runner/synthetic-tools.d.ts +6 -0
  298. package/dist/core/runner/synthetic-tools.js +4 -1
  299. package/dist/core/runner/teardown-bounded.d.ts +1 -0
  300. package/dist/core/runner/teardown-bounded.js +1 -0
  301. package/dist/core/runner/tool-disclosure.d.ts +10 -12
  302. package/dist/core/runner/tool-disclosure.js +1 -3
  303. package/dist/core/runner/tool-end-body.d.ts +1 -1
  304. package/dist/core/runner/tool-end-body.js +10 -9
  305. package/dist/core/runner/tool-not-executed.d.ts +5 -0
  306. package/dist/core/runner/tool-not-executed.js +11 -0
  307. package/dist/core/runner/tool-occurrence.d.ts +8 -0
  308. package/dist/core/runner/tool-occurrence.js +7 -0
  309. package/dist/core/runner/turn-attachments.d.ts +33 -15
  310. package/dist/core/runner/turn-attachments.js +62 -37
  311. package/dist/core/runner-deps.d.ts +7 -7
  312. package/dist/core/safety-axis-vocab.d.ts +6 -4
  313. package/dist/core/safety-axis-vocab.js +6 -5
  314. package/dist/core/safety-merge-corpus.js +11 -3
  315. package/dist/core/secret-env.d.ts +30 -3
  316. package/dist/core/secret-env.js +47 -8
  317. package/dist/core/select-model.js +3 -3
  318. package/dist/core/session-policy-store.d.ts +32 -15
  319. package/dist/core/session-policy-store.js +156 -36
  320. package/dist/core/session-store.d.ts +29 -10
  321. package/dist/core/session-store.js +51 -19
  322. package/dist/core/session.d.ts +82 -16
  323. package/dist/core/session.js +53 -0
  324. package/dist/core/shell-read-silence.d.ts +11 -12
  325. package/dist/core/shell-read-silence.js +9 -5
  326. package/dist/core/shell-run-candidates.js +3 -1
  327. package/dist/core/shell-wrapper-projection.d.ts +1 -1
  328. package/dist/core/shell-wrapper-projection.js +1 -1
  329. package/dist/core/shell-wrapper-table.d.ts +14 -7
  330. package/dist/core/shell-wrapper-table.js +5 -4
  331. package/dist/core/side-query.js +10 -1
  332. package/dist/core/skill-grant.d.ts +7 -2
  333. package/dist/core/skill-grant.js +5 -0
  334. package/dist/core/skills-directory.js +6 -2
  335. package/dist/core/store-contracts/background-agent-store-contract.js +1 -0
  336. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +11 -1
  337. package/dist/core/store-contracts/checkpoint-store-contract.js +141 -2
  338. package/dist/core/store-contracts/file-history-store-contract.js +40 -1
  339. package/dist/core/store-contracts/mailbox-store-contract.js +49 -1
  340. package/dist/core/store-contracts/peer-directory-contract.d.ts +7 -2
  341. package/dist/core/store-contracts/peer-directory-contract.js +14 -2
  342. package/dist/core/store-contracts/session-repo-contract.js +51 -0
  343. package/dist/core/store-contracts/session-store-contract.d.ts +2 -1
  344. package/dist/core/store-contracts/session-store-contract.js +150 -14
  345. package/dist/core/stub-env.d.ts +2 -0
  346. package/dist/core/stub-env.js +1 -0
  347. package/dist/core/synthetic-read-verdict.js +3 -3
  348. package/dist/core/task-event.d.ts +32 -4
  349. package/dist/core/task-notification.d.ts +16 -12
  350. package/dist/core/task-registry-agent.d.ts +8 -4
  351. package/dist/core/task-registry-agent.js +14 -8
  352. package/dist/core/task-registry-shared.d.ts +35 -6
  353. package/dist/core/task-registry-shared.js +11 -0
  354. package/dist/core/task-registry.d.ts +10 -1
  355. package/dist/core/task-registry.js +12 -2
  356. package/dist/core/task-result.d.ts +18 -0
  357. package/dist/core/task-spec.d.ts +18 -13
  358. package/dist/core/tighten-task-spec.js +2 -1
  359. package/dist/core/tool-catalog-entries.d.ts +23 -0
  360. package/dist/core/tool-catalog-entries.js +14 -2
  361. package/dist/core/tool-check-reading.d.ts +11 -0
  362. package/dist/core/tool-check-reading.js +11 -6
  363. package/dist/core/tool-model-gate.d.ts +2 -2
  364. package/dist/core/tool-name-aliases.d.ts +48 -18
  365. package/dist/core/tool-name-aliases.js +36 -19
  366. package/dist/core/tool-name-readings.d.ts +6 -32
  367. package/dist/core/tool-name-readings.js +2 -9
  368. package/dist/core/tool-policy.d.ts +78 -204
  369. package/dist/core/tool-policy.js +152 -204
  370. package/dist/core/tool-result-store.js +3 -0
  371. package/dist/core/tool-roster.d.ts +14 -19
  372. package/dist/core/tool-roster.js +11 -44
  373. package/dist/core/tool-spec-members.js +7 -2
  374. package/dist/core/tool-spec.d.ts +19 -11
  375. package/dist/core/tools.js +49 -9
  376. package/dist/core/trace.d.ts +6 -3
  377. package/dist/core/trace.js +2 -1
  378. package/dist/core/types.d.ts +1 -1
  379. package/dist/core/untrusted-egress.d.ts +20 -0
  380. package/dist/core/untrusted-egress.js +77 -3
  381. package/dist/core/user-attachment.d.ts +8 -1
  382. package/dist/core/user-attachment.js +64 -4
  383. package/dist/core/wiring-manifest.d.ts +9 -0
  384. package/dist/core/wiring-manifest.js +1 -0
  385. package/dist/core/workflow-journal-store.js +1 -1
  386. package/dist/engine/compaction/compaction.js +3 -2
  387. package/dist/engine/execution-env/node-execution-env.d.ts +7 -3
  388. package/dist/engine/execution-env/node-execution-env.js +11 -2
  389. package/dist/engine/harness/agent-harness.d.ts +32 -6
  390. package/dist/engine/harness/agent-harness.js +65 -9
  391. package/dist/engine/harness/types.d.ts +29 -2
  392. package/dist/engine/llm/types.d.ts +28 -11
  393. package/dist/engine/loop/agent-loop.d.ts +23 -4
  394. package/dist/engine/loop/agent-loop.js +42 -30
  395. package/dist/engine/loop/types.d.ts +16 -6
  396. package/dist/engine/session/memory-repo.d.ts +9 -2
  397. package/dist/engine/session/memory-repo.js +11 -1
  398. package/dist/engine/session/repo-utils.d.ts +6 -5
  399. package/dist/engine/session/repo-utils.js +14 -6
  400. package/dist/index.d.ts +34 -17
  401. package/dist/index.js +22 -11
  402. package/dist/internal/harness-types.d.ts +1 -1
  403. package/dist/internal/harness.d.ts +2 -1
  404. package/dist/internal/harness.js +1 -0
  405. package/dist/internal/llm.d.ts +1 -1
  406. package/dist/orchestration/run-workflow-tool.d.ts +6 -4
  407. package/dist/orchestration/run-workflow-tool.js +5 -3
  408. package/dist/orchestration/workflow.d.ts +3 -0
  409. package/dist/orchestration/workflow.js +36 -18
  410. package/dist/primitives/credential-ref.d.ts +89 -0
  411. package/dist/primitives/credential-ref.js +125 -0
  412. package/dist/primitives/index.d.ts +2 -0
  413. package/dist/primitives/index.js +1 -0
  414. package/dist/prompt-assembly/artifact.d.ts +2 -2
  415. package/dist/prompt-assembly/artifact.js +5 -3
  416. package/dist/prompt-assembly/assemble.d.ts +3 -2
  417. package/dist/prompt-assembly/assemble.js +1 -1
  418. package/dist/prompt-assembly/catalog/schema.d.ts +43 -12
  419. package/dist/prompt-assembly/catalog/schema.js +34 -2
  420. package/dist/prompt-assembly/catalog/sections.js +0 -1
  421. package/dist/prompt-assembly/composer.d.ts +3 -1
  422. package/dist/prompt-assembly/composer.js +1 -1
  423. package/dist/prompt-assembly/event-registry.js +1 -0
  424. package/dist/prompt-assembly/overlay.js +3 -3
  425. package/dist/prompt-assembly/renderers.d.ts +1 -1
  426. package/dist/prompt-assembly/renderers.js +0 -3
  427. package/dist/prompt-assembly/role-base.d.ts +15 -0
  428. package/dist/prompt-assembly/role-base.js +9 -0
  429. package/dist/prompt-assembly/select.d.ts +39 -14
  430. package/dist/prompt-assembly/select.js +47 -16
  431. package/dist/prompt-assembly/sugar.d.ts +17 -11
  432. package/dist/prompt-assembly/sugar.js +8 -3
  433. package/dist/prompt-assembly/tool-catalog.d.ts +37 -3
  434. package/dist/prompt-assembly/tool-catalog.js +114 -4
  435. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -10
  436. package/dist/prompt-assembly/turn-snapshot.js +0 -3
  437. package/dist/prompt-assembly/types.d.ts +0 -7
  438. package/dist/scenarios/full-body.d.ts +11 -11
  439. package/dist/scenarios/full-body.js +4 -3
  440. package/dist/shell/lex.js +10 -1
  441. package/dist/stores/file/adoption/adopt.js +4 -3
  442. package/dist/stores/file/adoption/marker.js +3 -2
  443. package/dist/stores/file/checkpoint-store.d.ts +21 -4
  444. package/dist/stores/file/checkpoint-store.js +38 -7
  445. package/dist/stores/file/file-history-store.js +18 -11
  446. package/dist/stores/file/index.js +2 -1
  447. package/dist/stores/file/mailbox-store.d.ts +2 -1
  448. package/dist/stores/file/mailbox-store.js +26 -1
  449. package/dist/stores/file/permission-rule-adopt.js +4 -3
  450. package/dist/stores/file/permission-rule-store.js +11 -5
  451. package/dist/stores/file/session-store.d.ts +29 -6
  452. package/dist/stores/file/session-store.js +42 -50
  453. package/dist/tools/artifact/artifact-tool.js +3 -2
  454. package/dist/tools/fs/bash-program-position.d.ts +19 -2
  455. package/dist/tools/fs/bash-program-position.js +56 -9
  456. package/dist/tools/fs/bash-readonly-classifier.d.ts +15 -19
  457. package/dist/tools/fs/bash-readonly-classifier.js +5 -10
  458. package/dist/tools/fs/fs-bash.js +15 -8
  459. package/dist/tools/fs/fs-glob.d.ts +86 -0
  460. package/dist/tools/fs/fs-glob.js +226 -0
  461. package/dist/tools/fs/fs-read.js +14 -14
  462. package/dist/tools/fs/fs-search-tools.js +7 -7
  463. package/dist/tools/fs/pattern-kill-guard.d.ts +23 -0
  464. package/dist/tools/fs/pattern-kill-guard.js +429 -0
  465. package/dist/tools/fs/pdf.d.ts +9 -6
  466. package/dist/tools/fs/pdf.js +3 -3
  467. package/dist/tools/fs/program-read-model.d.ts +57 -7
  468. package/dist/tools/fs/program-read-model.js +11 -1
  469. package/dist/tools/fs/program-read-model.rows.js +78 -21
  470. package/dist/tools/fs/read-deny-home.js +3 -2
  471. package/dist/tools/fs/removal-targets.d.ts +17 -7
  472. package/dist/tools/fs/removal-targets.js +47 -20
  473. package/dist/tools/fs/search.d.ts +10 -0
  474. package/dist/tools/fs/search.js +20 -26
  475. package/dist/tools/fs/shell-read-argv.js +89 -12
  476. package/dist/tools/fs/shell-read-bindings.d.ts +31 -0
  477. package/dist/tools/fs/shell-read-bindings.js +41 -0
  478. package/dist/tools/fs/shell-read-check.d.ts +10 -0
  479. package/dist/tools/fs/shell-read-check.js +13 -20
  480. package/dist/tools/fs/shell-read-environment.d.ts +5 -1
  481. package/dist/tools/fs/shell-read-environment.js +3 -2
  482. package/dist/tools/fs/shell-read-establish.d.ts +15 -0
  483. package/dist/tools/fs/shell-read-establish.js +19 -20
  484. package/dist/tools/fs/shell-read-identity.d.ts +12 -9
  485. package/dist/tools/fs/shell-read-identity.js +9 -4
  486. package/dist/tools/fs/shell-read-plan.js +24 -16
  487. package/dist/tools/fs/shell-read-start.d.ts +6 -1
  488. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -1
  489. package/dist/tools/fs/shell-read-write-targets.js +2 -3
  490. package/dist/tools/fs/shell-reentry-text.d.ts +9 -0
  491. package/dist/tools/fs/shell-reentry-text.js +40 -2
  492. package/dist/tools/todo.js +63 -63
  493. package/dist/tools/web.d.ts +19 -14
  494. package/dist/tools/web.js +46 -61
  495. package/package.json +1 -1
  496. package/test/export-surface.snapshot.json +221 -7
  497. package/wire-changes/core-stores-sql@7.57.0.json +29 -0
  498. package/wire-changes/core@7.56.0.json +3891 -0
  499. package/wire-changes/core@7.57.0.json +1487 -0
  500. package/wire-changes/mods-kernel@7.56.0.json +303 -0
  501. package/wire-changes/mods-kernel@7.57.0.json +29 -0
  502. package/wire-changes/primitives@7.57.0.json +245 -0
@@ -1,11 +1,111 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { escapeForDisclosure, hasUnrenderableCharacters, parseRuleText, ruleAdmitsCommand, ruleBreadthWarningsOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForHost, suggestRuleForName, suggestRulesForCommand, EDITED_RULE_BREADTH_WARNING_CODES, RULE_OFFER_KINDS, UNCOVERED_SEGMENT_REASONS, RULE_BEHAVIORS, RULE_BEHAVIOR_PRECEDENCE, isRuleBehavior, } from "./permission-rule-model.js";
2
+ import { COMMAND_RULE_TOOL, cardRuleFormOf, escapeForDisclosure, hasUnrenderableCharacters, parseRuleText, ruleAdmitsCommand, ruleBreadthWarningsOf, segmentCoverageOf, suggestRuleForHost, suggestRuleForName, suggestRuleForTool, suggestRulesForCommand, EDITED_RULE_BREADTH_WARNING_CODES, RULE_OFFER_KINDS, UNCOVERED_SEGMENT_REASONS, RULE_BEHAVIORS, RULE_BEHAVIOR_PRECEDENCE, isRuleBehavior, } from "./permission-rule-model.js";
3
3
  import { readRuleOwner, sameRuleOwner } from "./rule-owner.js";
4
+ import { principalNameOf, readPrincipal } from "./principal-name.js";
5
+ import { isPlainRecord } from "./mods/kernel/receive.js";
4
6
  import { effectiveOrThrow } from "./permission-rule-provider.js";
5
7
  import { errText, isValidConsentScope, landRuleAdd, sameScope, writerOf } from "./permission-rule-store.js";
6
8
  const isUncoveredSegmentReason = (v) => UNCOVERED_SEGMENT_REASONS.some((word) => v === word);
7
9
  export const RULE_APPROVAL_KINDS = Object.freeze(["card", "import", "starter"]);
8
10
  export const RULE_APPROVAL_STATES = Object.freeze(["pending", "approved", "redeemed"]);
11
+ const RECORD_OPTION_MEMBERS = { single: ["kind", "candidate"], batch: ["kind", "candidates", "segments", "uncoveredSegments", "uncoveredDetail"] };
12
+ export function readRuleOffer2(raw) {
13
+ if (!isPlainRecord(raw))
14
+ return { problem: "malformed" };
15
+ const kind = raw.kind;
16
+ if (typeof kind !== "string")
17
+ return { problem: "malformed" };
18
+ const word = RULE_OFFER_KINDS.find((k) => k === kind);
19
+ if (word === undefined)
20
+ return { problem: "unknown_word" };
21
+ const declared = RECORD_OPTION_MEMBERS[word];
22
+ if (Object.keys(raw).some((name) => !declared.includes(name)))
23
+ return { problem: "malformed" };
24
+ switch (word) {
25
+ case "single": {
26
+ const candidate = raw.candidate;
27
+ return isCandidateIndex(candidate) ? { offer: { kind: "single", candidate } } : { problem: "malformed" };
28
+ }
29
+ case "batch": {
30
+ const candidates = indexListOf(raw.candidates);
31
+ if (candidates === undefined || candidates.length === 0)
32
+ return { problem: "malformed" };
33
+ const segments = raw.segments;
34
+ const uncoveredSegments = raw.uncoveredSegments;
35
+ const detail = raw.uncoveredDetail;
36
+ const segmentList = segments === undefined ? undefined : stringListOf(segments);
37
+ if (segments !== undefined && segmentList === undefined)
38
+ return { problem: "malformed" };
39
+ if (uncoveredSegments !== undefined && !isCandidateIndex(uncoveredSegments))
40
+ return { problem: "malformed" };
41
+ const rows = detail === undefined ? undefined : reasonRowsOf(detail);
42
+ if (rows !== undefined && "problem" in rows)
43
+ return rows;
44
+ return {
45
+ offer: {
46
+ kind: "batch",
47
+ candidates,
48
+ ...(segmentList !== undefined ? { segments: segmentList } : {}),
49
+ ...(uncoveredSegments !== undefined ? { uncoveredSegments } : {}),
50
+ ...(rows !== undefined ? { uncoveredDetail: rows.rows } : {}),
51
+ },
52
+ };
53
+ }
54
+ default: {
55
+ const unhandled = word;
56
+ return unhandled;
57
+ }
58
+ }
59
+ }
60
+ function isCandidateIndex(value) {
61
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
62
+ }
63
+ function indexListOf(value) {
64
+ if (!Array.isArray(value))
65
+ return undefined;
66
+ const length = value.length;
67
+ const out = [];
68
+ for (let i = 0; i < length; i++) {
69
+ const item = value[i];
70
+ if (!isCandidateIndex(item))
71
+ return undefined;
72
+ out.push(item);
73
+ }
74
+ return out;
75
+ }
76
+ function stringListOf(value) {
77
+ if (!Array.isArray(value))
78
+ return undefined;
79
+ const length = value.length;
80
+ const out = [];
81
+ for (let i = 0; i < length; i++) {
82
+ const item = value[i];
83
+ if (typeof item !== "string")
84
+ return undefined;
85
+ out.push(item);
86
+ }
87
+ return out;
88
+ }
89
+ function reasonRowsOf(value) {
90
+ if (!Array.isArray(value))
91
+ return { problem: "malformed" };
92
+ const length = value.length;
93
+ const rows = [];
94
+ for (let i = 0; i < length; i++) {
95
+ const row = value[i];
96
+ if (!isPlainRecord(row) || Object.keys(row).some((name) => name !== "segment" && name !== "reason"))
97
+ return { problem: "malformed" };
98
+ const segment = row.segment;
99
+ const reason = row.reason;
100
+ if (typeof segment !== "string" || typeof reason !== "string")
101
+ return { problem: "malformed" };
102
+ const word = UNCOVERED_SEGMENT_REASONS.find((r) => r === reason);
103
+ if (word === undefined)
104
+ return { problem: "unknown_word" };
105
+ rows.push({ segment, reason: word });
106
+ }
107
+ return { rows };
108
+ }
9
109
  export const RULE_APPROVAL_RECORD_SCHEMA = 4;
10
110
  export class InMemoryRuleApprovalRecordStore {
11
111
  rows = new Map();
@@ -48,25 +148,25 @@ function mintId(deps, prefix) {
48
148
  return `${prefix}_${Date.now().toString(36)}_${randomBytes(9).toString("base64url")}`;
49
149
  }
50
150
  function requirePrincipal(principal, entry) {
51
- const owner = readRuleOwner({ kind: "principal", principal });
52
- if (owner?.kind !== "principal") {
151
+ const name = principalNameOf(principal);
152
+ if (name === undefined) {
53
153
  throw new Error(`${entry} requires a verified principal — refusing to create an approval record without one`);
54
154
  }
55
- return owner.principal;
155
+ return name;
56
156
  }
57
157
  function resolveCallerOwner(principal, owner, entry) {
58
158
  if (owner !== undefined) {
59
159
  const given = readRuleOwner(owner);
60
160
  if (given === undefined) {
61
- throw new Error(`${entry} requires a verified principal or the local owner — the owner given is neither (a principal owner names a non-blank principal)`);
161
+ throw new Error(`${entry} requires a verified principal or the local owner — the owner given is neither (a principal owner names a principal name)`);
62
162
  }
63
163
  if (given.kind === "principal") {
64
- if (typeof principal === "string" && principal !== "" && principal !== given.principal) {
164
+ if (readPrincipal(principal).kind !== "absent" && principal !== given.principal) {
65
165
  throw new Error(`${entry}: contradictory identity — principal "${principal}" and owner principal "${given.principal}" disagree`);
66
166
  }
67
167
  return given;
68
168
  }
69
- if (typeof principal === "string" && principal !== "") {
169
+ if (readPrincipal(principal).kind !== "absent") {
70
170
  throw new Error(`${entry}: contradictory identity — a principal and a local-owner were both given; a bucket has one owner`);
71
171
  }
72
172
  return given;
@@ -114,19 +214,17 @@ function approvalRecordDamageOf(rec) {
114
214
  if (!Array.isArray(rec.offers) || rec.offers.length === 0)
115
215
  return "the record carries no offers";
116
216
  const claimed = new Set();
117
- for (const [at, offer] of rec.offers.entries()) {
118
- if (typeof offer !== "object" || offer === null)
119
- return `offer ${at} is not an offer object`;
120
- if (!RULE_OFFER_KINDS.some((word) => offer.kind === word))
121
- return `offer ${at} has an unknown kind`;
122
- if (offer.kind === "batch" && (!Array.isArray(offer.candidates) || offer.candidates.length === 0)) {
123
- return `batch offer ${at} has no members`;
217
+ for (const [at, stored] of rec.offers.entries()) {
218
+ const read = readRuleOffer2(stored);
219
+ if ("problem" in read) {
220
+ return `offer ${at} is not a record option this engine reads (${read.problem}): an option is an object whose kind is ${RULE_OFFER_KINDS.join(" or ")}, whose candidate indices are non-negative integers (a batch with no members is not one), whose reason rows are segment/reason rows from the closed reason set, and which carries no other member`;
124
221
  }
222
+ const offer = read.offer;
125
223
  const members = offerMembersOf(offer);
126
224
  const seen = new Set();
127
225
  for (const member of members) {
128
- if (!Number.isInteger(member) || member < 0 || rec.candidates[member] === undefined) {
129
- return `offer ${at} references candidate ${escapeForDisclosure(member)}, which the record does not carry`;
226
+ if (rec.candidates[member] === undefined) {
227
+ return `offer ${at} references candidate ${member}, which the record does not carry`;
130
228
  }
131
229
  const cand = rec.candidates[member];
132
230
  if (typeof cand !== "object" || cand === null || typeof cand.rule !== "string") {
@@ -138,6 +236,12 @@ function approvalRecordDamageOf(rec) {
138
236
  if (rec.kind === "card" && cand.behavior !== "allow") {
139
237
  return `card offer ${at} references candidate ${member}, a ${cand.behavior} rule — a card mints allow candidates only`;
140
238
  }
239
+ if (rec.kind === "card") {
240
+ const read = parseRuleText(cand.rule, cand.behavior);
241
+ if ("rule" in read && read.rule.match === "tool" && (typeof rec.command === "string" || cardRuleFormOf(read.rule.tool) !== "tool")) {
242
+ return `card offer ${at} references candidate ${member}, the whole-tool rule "${escapeForDisclosure(read.rule.rule)}" — the engine mints one only on the whole-tool card of a tool with no rule grammar`;
243
+ }
244
+ }
141
245
  if (!isValidConsentScope(cand.scope)) {
142
246
  return `offer ${at} references candidate ${member}, whose scope is not a consent-face scope (global, project, or session)`;
143
247
  }
@@ -150,29 +254,21 @@ function approvalRecordDamageOf(rec) {
150
254
  }
151
255
  if (offer.kind === "batch") {
152
256
  if (rec.kind === "card") {
153
- if (!Array.isArray(offer.segments) || offer.segments.length !== offer.candidates.length || !offer.segments.every((s) => typeof s === "string")) {
257
+ if (offer.segments === undefined || offer.segments.length !== offer.candidates.length) {
154
258
  return `card batch offer ${at} must carry exactly one source segment per member`;
155
259
  }
156
- if (!Number.isInteger(offer.uncoveredSegments) || offer.uncoveredSegments < 0) {
260
+ if (offer.uncoveredSegments === undefined) {
157
261
  return `card batch offer ${at} must carry a non-negative uncovered-segment count`;
158
262
  }
159
263
  const detail = offer.uncoveredDetail;
160
- if (!Array.isArray(detail))
264
+ if (detail === undefined)
161
265
  return `card batch offer ${at} must carry its uncovered-segment reason rows`;
162
266
  if (detail.length !== offer.uncoveredSegments) {
163
267
  return `card batch offer ${at} carries ${detail.length} reason rows for an uncovered-segment count of ${offer.uncoveredSegments} — one fact, two seats, and they must agree`;
164
268
  }
165
- for (const [ri, row] of detail.entries()) {
166
- if (typeof row !== "object" ||
167
- row === null ||
168
- typeof row.segment !== "string" ||
169
- !UNCOVERED_SEGMENT_REASONS.some((word) => row.reason === word)) {
170
- return `card batch offer ${at} reason row ${ri} is not a segment/reason row from the closed reason set`;
171
- }
172
- }
173
269
  if (detail.length > 0) {
174
270
  const rows = detail;
175
- const table = typeof rec.command === "string" ? segmentCoverageOf(rec.command, { persisted: [] }, { tool: CARD_RULE_TOOL, cwd: undefined }) : undefined;
271
+ const table = typeof rec.command === "string" ? segmentCoverageOf(rec.command, { persisted: [] }, { tool: COMMAND_RULE_TOOL, cwd: undefined }) : undefined;
176
272
  if (table === undefined) {
177
273
  return `card batch offer ${at} carries reason rows but the record's command cannot back them — a row without a readable source command is a row nobody can audit`;
178
274
  }
@@ -289,10 +385,10 @@ export async function prepareCardApproval(opts) {
289
385
  e.code = "config.invalid_argument";
290
386
  throw e;
291
387
  }
292
- const grammar = ruleToolGrammarOf(opts.toolName);
293
- const oneRuleCard = grammar === "name" || grammar === "domain" ? grammar : undefined;
294
- if (opts.toolName !== CARD_RULE_TOOL && oneRuleCard === undefined)
388
+ const form = cardRuleFormOf(opts.toolName);
389
+ if (form === undefined)
295
390
  return undefined;
391
+ const oneRuleCard = form === "command" ? undefined : form;
296
392
  const scope = (() => {
297
393
  if (opts.scope !== undefined) {
298
394
  if (!isValidConsentScope(opts.scope)) {
@@ -319,14 +415,14 @@ export async function prepareCardApproval(opts) {
319
415
  try {
320
416
  const view = await effectiveOrThrow(storeForOwner(opts.deps.provider, owner), opts.sessionId !== undefined ? { sessionId: opts.sessionId } : undefined);
321
417
  const table = view.rules;
322
- coverage = segmentCoverageOf(opts.command, { persisted: table }, { tool: CARD_RULE_TOOL, cwd: opts.cwd, sessionId: opts.sessionId, ...(opts.execCwd !== undefined ? { execCwd: opts.execCwd } : {}) });
418
+ coverage = segmentCoverageOf(opts.command, { persisted: table }, { tool: COMMAND_RULE_TOOL, cwd: opts.cwd, sessionId: opts.sessionId, ...(opts.execCwd !== undefined ? { execCwd: opts.execCwd } : {}) });
323
419
  }
324
420
  catch {
325
421
  coverage = undefined;
326
422
  }
327
423
  }
328
424
  const suggested = oneRuleCard !== undefined
329
- ? [oneRuleCard === "name" ? suggestRuleForName(opts.toolName, opts.command) : suggestRuleForHost(opts.toolName, opts.command)].filter((o) => o !== undefined)
425
+ ? [oneRuleCard === "name" ? suggestRuleForName(opts.toolName, opts.command) : oneRuleCard === "domain" ? suggestRuleForHost(opts.toolName, opts.command) : opts.command === "" ? suggestRuleForTool(opts.toolName) : undefined].filter((o) => o !== undefined)
330
426
  : suggestRulesForCommand(opts.command, {
331
427
  ...(coverage !== undefined ? { coverage } : {}),
332
428
  scope,
@@ -378,7 +474,6 @@ export async function prepareCardApproval(opts) {
378
474
  candidates,
379
475
  };
380
476
  }
381
- const CARD_RULE_TOOL = "Bash";
382
477
  export async function confirmRuleApproval(opts) {
383
478
  const caller = resolveCallerOwner(opts.principal, opts.owner, "confirmRuleApproval");
384
479
  const no = (reason, detail) => ({
@@ -456,6 +551,9 @@ function checkEditedRuleText(text, command) {
456
551
  const parsed = parseSubmittedSpelling(text);
457
552
  if ("reject" in parsed)
458
553
  return { ok: false, code: parsed.reject.code, message: parsed.reject.message };
554
+ if (parsed.rule.match === "tool") {
555
+ return { ok: false, message: `the edited rule "${parsed.rule.rule}" names the whole tool — a card's edit names a command rule, and a standing yes to every command is not one a card mints` };
556
+ }
459
557
  if (!ruleAdmitsCommand(parsed.rule, command)) {
460
558
  return {
461
559
  ok: false,
@@ -748,18 +846,15 @@ export async function prepareCcImport(opts) {
748
846
  skipped.push({ rule: String(entry), reason: `settings ${behavior} entry is not a string` });
749
847
  continue;
750
848
  }
751
- if (!/^[A-Za-z][A-Za-z0-9_]*\(.*\)$/.test(entry)) {
752
- const reason = /^[A-Za-z][A-Za-z0-9_-]*$/.test(entry)
753
- ? "unsupported.form: a bare tool-name entry is a name-set item, not a command rule — it stays in the settings file, unimported"
754
- : "unsupported.form: not a command rule spelling this version imports — it stays in the settings file, unimported";
755
- skipped.push({ rule: entry, reason });
756
- continue;
757
- }
758
849
  const parsed = parseRuleText(entry, behavior);
759
850
  if ("reject" in parsed) {
760
851
  skipped.push({ rule: entry, reason: `${parsed.reject.code}: ${parsed.reject.message}` });
761
852
  continue;
762
853
  }
854
+ if (parsed.rule.match === "tool") {
855
+ skipped.push({ rule: entry, reason: "unsupported.form: a bare tool-name entry is a name-set item the host carries from the settings layer's own lists — it stays in the settings file, unimported" });
856
+ continue;
857
+ }
763
858
  if (candidates.some((c) => c.behavior === behavior && c.rule === parsed.rule.rule && sameScope(c.scope, scope)))
764
859
  continue;
765
860
  candidates.push({ behavior, rule: parsed.rule.rule, scope });
@@ -77,6 +77,7 @@ import { type DenyFace } from "../tools/fs/read-deny.js";
77
77
  import type { AssertAllKeysHandled, RuleOffersAbsence } from "./ask-origin.js";
78
78
  import { type ToolCallLike } from "./tool-registry.js";
79
79
  import { type HostReading } from "./host-target.js";
80
+ import { type NamedCall } from "./tool-name-readings.js";
80
81
  import { type PathDenialEvidence, type PathFamily, type PathIdentity } from "../tools/fs/safety.js";
81
82
  import { type TargetViews } from "./path-rule-binding.js";
82
83
  /**
@@ -284,6 +285,18 @@ export declare const DOMAIN_RULE_MARKER = "domain:";
284
285
  */
285
286
  export type RuleGrammar = "command" | "path" | "name" | "domain";
286
287
  export declare function ruleToolGrammarOf(toolName: string): RuleGrammar | undefined;
288
+ /**
289
+ * THE rule a tool's approval card mints a "don't ask again" option in — one answer, read by the offer factory and the consent door
290
+ * alike: `command` for the command rule word (the shell card's command rules), `name` / `domain` for a tool of those grammars (the one
291
+ * exact value rule), `tool` for a call no rule word speaks for under any name it answers to ({@link laneToolNamesOf} empty: the
292
+ * whole-tool rule, the reference's fallback card), and `undefined` for the rest — a path tool (no path offer is minted), a shell-face
293
+ * sibling of the command word (its commands are `Bash(…)` rules), and a tool that answers to one of those under an alias it declares
294
+ * (a caller's shell wearing `Bash`: a one-click whole-tool yes on it would be a licence for every program): its card names that it has
295
+ * no rule to offer. `toolName` is the wire name the card mints on; `call` the call's names (absent: the wire name alone — the
296
+ * consent door, which holds no face).
297
+ */
298
+ export type CardRuleForm = "command" | "name" | "domain" | "tool";
299
+ export declare function cardRuleFormOf(toolName: string, call?: NamedCall): CardRuleForm | undefined;
287
300
  /** The parameter a shell-face tool's rule content names as one of the tool's own scalar arguments (`Monitor(description:*)`,
288
301
  * `Bash(timeout:5)`), or undefined — the content is then a command spelling. */
289
302
  export declare function shellRuleParamOf(tool: string, content: string): string | undefined;
@@ -311,11 +324,11 @@ export declare function nameRuleSubjectOf(toolName: string, args: unknown): stri
311
324
  * domain tool's its own name — so a stored `Bash(…)` or `Edit(…)` row reaches a tool that answers to that name under another wire
312
325
  * name, as a policy's rule does. Every lane that judges a call by a rule's word reads it here: the policy compiler, the
313
326
  * person's stored rows, the org partition, the allow lists. Empty ⇒ no grammar speaks for the call. */
314
- export declare function laneToolNamesOf(req: Pick<import("./tool-policy.js").ToolCallRequest, "toolName" | "face">): readonly string[];
327
+ export declare function laneToolNamesOf(req: NamedCall): readonly string[];
315
328
  /** The match words a persisted rule is stored with, spelled once: {@link PersistedRuleMatch} is derived from this
316
329
  * tuple (each form is documented there). A store backend that checks a stored row's `match` reads this value
317
330
  * rather than keeping its own list. */
318
- export declare const PERSISTED_RULE_MATCHES: readonly ["exact", "prefix", "wildcard", "pattern", "subpath", "path", "name", "name_prefix", "host", "subdomain"];
331
+ export declare const PERSISTED_RULE_MATCHES: readonly ["exact", "prefix", "wildcard", "pattern", "subpath", "path", "name", "name_prefix", "host", "subdomain", "tool"];
319
332
  /**
320
333
  * Match forms, one family per grammar (`prefix` and `wildcard` are one predicate):
321
334
  * · `exact` — admits the one string the rule spells.
@@ -347,6 +360,8 @@ export declare const PERSISTED_RULE_MATCHES: readonly ["exact", "prefix", "wildc
347
360
  * · `name` / `name_prefix` — the NAME grammar ({@link parseNameRuleContent}).
348
361
  * · `host` / `subdomain` — the DOMAIN grammar ({@link parseDomainRuleContent}): one exact host, or every
349
362
  * host below one domain.
363
+ * · `tool` — the WHOLE-TOOL form, a rule with no content (`SendUserFile`, `Bash`, `mcp__srv__get_issue`), on any tool:
364
+ * `command` is empty and the rule reaches every call that answers to the name it spells ({@link wholeToolRuleReaches}).
350
365
  */
351
366
  export type PersistedRuleMatch = (typeof PERSISTED_RULE_MATCHES)[number];
352
367
  /**
@@ -460,6 +475,9 @@ export type RuleRejectCode = "invalid.grammar" | "invalid.empty_command" | "inva
460
475
  export interface RuleReject {
461
476
  code: RuleRejectCode;
462
477
  message: string;
478
+ /** For a text that is no rule spelling only because of white space (a tab, a no-break space included) around it or before its `(`:
479
+ * the rule spelling it is a blank away from ({@link respelledRuleTextOf}) — named in `message`, never read in its place. */
480
+ respelling?: string;
463
481
  }
464
482
  /** The parsed, canonical shape of one rule text under one behavior — everything but the scope and the
465
483
  * provenance. The behavior is the parser's INPUT (it decides which validator floor applies), carried
@@ -718,6 +736,13 @@ export declare function ruleContentOf(parsed: ParsedPermissionRule): string | un
718
736
  * most wants. The tightening behaviors therefore admit the interpreter-headed prefix form and change
719
737
  * nothing else; the allow floor is unchanged.
720
738
  */
739
+ /**
740
+ * The rule spelling a text the rule syntax cannot read is a blank away from: the text with the white space (tabs included) around
741
+ * it and before its first `(` taken off, when that reads as a rule under `behavior` — its canonical text — else `undefined`. A text
742
+ * holding an invisible format character never gets here (its one caller refuses it first: `invalid.control_chars`, or `invalid.too_long`). Only what a refusal SAYS: a rule text is read as written ({@link parseRuleText}), so the way out is named, never
743
+ * taken (a door that reads the respelled text would be a second reading of the same text).
744
+ */
745
+ export declare function respelledRuleTextOf(text: string, behavior: RuleBehavior): string | undefined;
721
746
  export declare function parseRuleText(text: string, behavior: RuleBehavior): {
722
747
  rule: ParsedRule;
723
748
  } | {
@@ -745,8 +770,8 @@ export type NameRuleReach = "reached" | "not_reached" | "unread";
745
770
  export declare function nameRuleReach(rule: Pick<PersistedRule, "tool" | "behavior" | "match" | "command">, name: string, values: readonly string[] | undefined): NameRuleReach;
746
771
  /** Why a call is unread against a mounted row's rule ({@link nameRuleReach} `unread`) — the sentence every reader carries. */
747
772
  export declare function nameValuesUnreadReason(tool: string): string;
748
- /** Does a domain rule reach this host (as `hostOfUrl` reads it)? `host`: equal; `subdomain`: a name ending in
749
- * `.<domain>` with at least one label before it and no empty label. A rule of any other family reaches no host. */
773
+ /** Does a domain rule reach this host (as `hostOfUrl` reads it)? The pattern's own reach ({@link hostPatternAdmits}); a rule of
774
+ * any other family reaches no host. */
750
775
  export declare function domainRuleAdmits(rule: Pick<PersistedRule, "match" | "command">, host: string): boolean;
751
776
  /**
752
777
  * The closed set of BASES a path-form pattern resolves against — facts of the CALL (its execution
@@ -1255,7 +1280,8 @@ export interface CallHosts {
1255
1280
  * that does not parse, is not http(s), carries credentials, or names a host that does not read back as itself) is REFUSED by the
1256
1281
  * tool's first eligible deny row, whatever host that row names, and otherwise ASKED by its first eligible ask row in the
1257
1282
  * fail-closed form (`unreadable`, never a matched ask); no allow row reaches it, and with no deny or ask row it is reached by
1258
- * nothing.
1283
+ * nothing. A stored row whose text the rule reader reads as no rule reaches this walk from the person's rule lane only as a deny
1284
+ * or an ask (its fields still tighten); the lane answers a call none of the rows refuses as one it cannot judge.
1259
1285
  */
1260
1286
  export declare function adjudicatePersistedDomainRules(rules: readonly PersistedRule[], call: {
1261
1287
  tool: string;
@@ -1264,6 +1290,32 @@ export declare function adjudicatePersistedDomainRules(rules: readonly Persisted
1264
1290
  sessionId?: string;
1265
1291
  scopeIdentity?: ScopeRootIdentity;
1266
1292
  }): PersistedRuleVerdict | undefined;
1293
+ /**
1294
+ * THE predicate of a whole-tool rule (`match: "tool"`) over one call: the rule reaches the call iff the call answers to the name it
1295
+ * spells, under every name the call answers to (the model's spelling, the wire name, the aliases that resolve to it) — the
1296
+ * reference's tool-wide matcher. Every reader of a rule with no content asks it: an entry list's whole-tool row and a person's stored
1297
+ * whole-tool row alike.
1298
+ */
1299
+ export declare function wholeToolRuleReaches(tool: string, call: NamedCall): boolean;
1300
+ /**
1301
+ * The whole-tool sibling of {@link adjudicatePersistedRules}: the same precedence walk (deny > ask > allow) over the whole-tool rows
1302
+ * (`match: "tool"`) that reach the call ({@link wholeToolRuleReaches}) and are eligible under their own tool ({@link eligiblePersisted},
1303
+ * the one predicate); the first behavior with such a row decides, the first table-order row of it is the answer.
1304
+ */
1305
+ export declare function adjudicatePersistedToolRules(rules: readonly PersistedRule[], call: {
1306
+ request: NamedCall;
1307
+ cwd: string | undefined;
1308
+ sessionId?: string;
1309
+ scopeIdentity?: ScopeRootIdentity;
1310
+ }): PersistedRuleVerdict | undefined;
1311
+ /**
1312
+ * The ONE offer the card of a tool with no rule grammar carries ({@link cardRuleFormOf} `tool`): the whole-tool rule on its name (the
1313
+ * reference's fallback card, "don't ask again for <tool>"), or undefined when the name has no such rule (the round-trip law: the bare
1314
+ * name, parsed back as an allow, must be the whole-tool rule on that very name).
1315
+ */
1316
+ export declare function suggestRuleForTool(tool: string): Extract<RuleOffer, {
1317
+ kind: "single";
1318
+ }> | undefined;
1267
1319
  /**
1268
1320
  * The ONE offer a host tool's approval card carries: the exact rule for the host the call reaches (`WebFetch(domain:<host>)`, the
1269
1321
  * reference's "don't ask again for <host>" row), or undefined when no rule spells exactly that host (the round-trip law: the
@@ -2,13 +2,15 @@ import { homedir } from "node:os";
2
2
  import { shellWordsOf } from "../shell/shell-words.js";
3
3
  import { lexShell } from "../shell/lex.js";
4
4
  import { readProgramPosition } from "../tools/fs/bash-program-position.js";
5
- import { parsePermissionRule } from "./permission-rule-syntax.js";
5
+ import { isNameableToolName, parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js";
6
+ import { isCoveringRuleName } from "./namespaced-rule-name.js";
7
+ import { toolNameFaultOf } from "./tool-name-aliases.js";
6
8
  import { compileReadDeny } from "../tools/fs/read-deny.js";
7
9
  import { carriesShellRedirection, parseLeadingCommandName, splitShellCompoundSegments } from "../tools/fs/bash-readonly-classifier.js";
8
10
  import { INLINE_TRUNCATION_MARK, inlineUntrusted } from "./untrusted-text.js";
9
- import { catalogRuleFaceOf, hostTargetOf, isShellFaceToolName, pathTargetOf, protectivePathTargetOf } from "./tool-registry.js";
10
- import { HOST_UNREADABLE_SENTENCE, hostOfSpelling, isAddressHost } from "./host-target.js";
11
- import { heldValues } from "./tool-name-readings.js";
11
+ import { catalogRuleFaceOf, catalogToolNames, hostTargetOf, isShellFaceToolName, pathTargetOf, protectivePathTargetOf } from "./tool-registry.js";
12
+ import { HOST_UNREADABLE_SENTENCE, hostPatternAdmits, readHostPattern, spelledHostPattern } from "./host-target.js";
13
+ import { heldBy, heldValues } from "./tool-name-readings.js";
12
14
  import { carriesDotSegment, isAbsoluteForFamily, isIdentityDir, isPathIdentity, isShellRootedSpellingUnmapped, isUncIdentity, pathFamilyOf, pathIdentityOf } from "../tools/fs/safety.js";
13
15
  import { bindPathRules, judgeRuleRows, rowsWhereTheyLead } from "./path-rule-binding.js";
14
16
  import { readShellCommand } from "./shell-run-candidates.js";
@@ -135,6 +137,14 @@ export function ruleToolGrammarOf(toolName) {
135
137
  return "domain";
136
138
  return pathTargetOf(toolName) !== undefined ? "path" : undefined;
137
139
  }
140
+ export function cardRuleFormOf(toolName, call = { toolName }) {
141
+ if (toolName === COMMAND_RULE_TOOL)
142
+ return "command";
143
+ const grammar = ruleToolGrammarOf(toolName);
144
+ if (grammar === "name" || grammar === "domain")
145
+ return grammar;
146
+ return grammar === undefined && laneToolNamesOf(call).length === 0 && laneToolNamesOf({ toolName }).length === 0 ? "tool" : undefined;
147
+ }
138
148
  export function shellRuleParamOf(tool, content) {
139
149
  const colon = content.indexOf(":");
140
150
  if (colon <= 0)
@@ -172,7 +182,7 @@ function ruleWordOf(name) {
172
182
  return COMMAND_RULE_TOOL;
173
183
  return ruleToolGrammarOf(name) !== undefined ? name : undefined;
174
184
  }
175
- export const PERSISTED_RULE_MATCHES = Object.freeze(["exact", "prefix", "wildcard", "pattern", "subpath", "path", "name", "name_prefix", "host", "subdomain"]);
185
+ export const PERSISTED_RULE_MATCHES = Object.freeze(["exact", "prefix", "wildcard", "pattern", "subpath", "path", "name", "name_prefix", "host", "subdomain", "tool"]);
176
186
  function isCommandMatch(match) {
177
187
  return match === "exact" || match === "prefix" || match === "wildcard" || match === "pattern";
178
188
  }
@@ -406,6 +416,28 @@ export function ruleContentOf(parsed) {
406
416
  const content = parsed.ruleContent;
407
417
  return content === undefined || (parsed.toolName === COMMAND_RULE_TOOL && foldSpacing(content) === "*") ? undefined : content;
408
418
  }
419
+ export function respelledRuleTextOf(text, behavior) {
420
+ const outer = text.trim();
421
+ const open = outer.indexOf("(");
422
+ const candidate = open < 0 ? outer : outer.slice(0, open).replace(/\s+$/u, "") + outer.slice(open);
423
+ if (candidate === text || ruleSpellingOf(candidate) === undefined)
424
+ return undefined;
425
+ const read = parseRuleText(candidate, behavior);
426
+ return "rule" in read ? read.rule.rule : undefined;
427
+ }
428
+ function parseWholeToolRule(text, tool, behavior) {
429
+ if (!isNameableToolName(tool) || isCoveringRuleName(tool, behavior)) {
430
+ return reject("invalid.grammar", `"${text}" names a set of tools — a stored rule names one tool, or one tool's content; a set of tools is a list entry's form`);
431
+ }
432
+ const fault = toolNameFaultOf(tool, catalogToolNames(), behavior);
433
+ if (fault === undefined)
434
+ return { rule: { behavior, rule: tool, tool, match: "tool", command: "" } };
435
+ const why = fault.reason === "near_miss"
436
+ ? `is another spelling of "${fault.currentName}", which the engine's tool catalog names — a rule names its tool exactly as spelled, so this one would reach no tool`
437
+ : `names no tool under that name${fault.guidance !== undefined ? ` — ${fault.guidance}` : ""}`;
438
+ const write = fault.currentName !== undefined && !why.includes(`"${fault.currentName}"`) ? `; write "${fault.currentName}"` : "";
439
+ return reject("unsupported.tool", `"${text}" ${why}${write}`);
440
+ }
409
441
  export function parseRuleText(text, behavior) {
410
442
  if (!isRuleBehavior(behavior)) {
411
443
  return reject("invalid.grammar", `rule "${text}" was parsed under behavior ${escapeForDisclosure(behavior)}, which is not one of ${RULE_BEHAVIORS.join("/")}`);
@@ -417,11 +449,20 @@ export function parseRuleText(text, behavior) {
417
449
  if (CONTROL_CHARS_RE.test(text)) {
418
450
  return reject("invalid.control_chars", "rule text contains control characters (C0/C1, tab excepted), which cannot be part of a readable command");
419
451
  }
420
- const parsed = parsePermissionRule(text);
421
- const ruleContent = ruleContentOf(parsed);
422
- if (ruleContent === undefined) {
423
- return reject("invalid.grammar", `"${text}" is not a Tool(content) rule — a bare tool name claims the whole tool and is not a command rule`);
452
+ const parsed = ruleSpellingOf(text);
453
+ if (parsed === undefined) {
454
+ const respelling = respelledRuleTextOf(text, behavior);
455
+ return {
456
+ reject: {
457
+ code: "invalid.grammar",
458
+ message: `"${text}" is not a rule spelling — a rule is the tool's name exactly as spelled, then its content in parentheses (no blank, tab or invisible character around the rule or before the "("), and the ")" ends it${respelling !== undefined ? `; spelled as a rule it is "${respelling}"` : ""}`,
459
+ ...(respelling !== undefined ? { respelling } : {}),
460
+ },
461
+ };
424
462
  }
463
+ const ruleContent = ruleContentOf(parsed);
464
+ if (ruleContent === undefined)
465
+ return parseWholeToolRule(text, parsed.toolName, behavior);
425
466
  if (parsed.toolName !== COMMAND_RULE_TOOL) {
426
467
  const grammar = ruleToolGrammarOf(parsed.toolName);
427
468
  if (grammar === undefined) {
@@ -491,6 +532,8 @@ export function parseRuleText(text, behavior) {
491
532
  return { rule: { behavior, rule: formatRuleText(command, match, COMMAND_RULE_TOOL), tool: COMMAND_RULE_TOOL, match, command } };
492
533
  }
493
534
  export function formatRuleText(command, match, tool) {
535
+ if (match === "tool")
536
+ return tool;
494
537
  if (match === "subpath")
495
538
  return `${tool}(//${command.startsWith("/") ? command.slice(1) : command}/**)`;
496
539
  if (match === "path")
@@ -498,7 +541,7 @@ export function formatRuleText(command, match, tool) {
498
541
  if (match === "name")
499
542
  return `${tool}(${keyedValueParamOf(tool) !== undefined ? `${keyedValueParamOf(tool)}:` : ""}${command})`;
500
543
  if (match === "host" || match === "subdomain")
501
- return `${tool}(domain:${match === "subdomain" ? "*." : ""}${command})`;
544
+ return `${tool}(domain:${spelledHostPattern({ match, host: command })})`;
502
545
  return `${tool}(${command}${match === "prefix" || match === "name_prefix" ? ":*" : match === "wildcard" ? " *" : ""})`;
503
546
  }
504
547
  function parseNameRuleContent(text, tool, content, behavior, verifyCanonical = true) {
@@ -606,30 +649,26 @@ function parseDomainRuleContent(text, tool, content, behavior) {
606
649
  if (!content.startsWith(DOMAIN_RULE_MARKER)) {
607
650
  return reject("invalid.grammar", `a ${tool} rule names a host: ${tool}(domain:<host>) for one, ${tool}(domain:*.<domain>) for every host below a domain ("${text}")`);
608
651
  }
609
- const spelled = content.slice(DOMAIN_RULE_MARKER.length);
610
- if (spelled === "*")
611
- return reject("unsupported.wildcard", `"${text}" names every host — the whole tool is the bare ${tool} entry`);
612
- const below = spelled.startsWith("*.");
613
- const body = below ? spelled.slice(2) : spelled;
614
- if (body.includes("*")) {
615
- return reject("unsupported.wildcard", `a \`*\` in "${text}" other than a leading \`*.\` has no rule form — a ${tool} rule names one host, or every host below one domain with \`*.<domain>\``);
616
- }
617
- const host = hostOfSpelling(body);
618
- if (host === undefined || hostOfSpelling(host) !== host) {
619
- return reject("invalid.grammar", `"${text}" does not name a host alone — write the host as a URL carries it, with no scheme, path, port or credentials`);
652
+ const read = readHostPattern(content.slice(DOMAIN_RULE_MARKER.length));
653
+ if ("problem" in read) {
654
+ switch (read.problem) {
655
+ case "every_host":
656
+ return reject("unsupported.wildcard", `"${text}" names every host — the whole tool is the bare ${tool} entry`);
657
+ case "inner_wildcard":
658
+ return reject("unsupported.wildcard", `a \`*\` in "${text}" other than a leading \`*.\` has no rule form — a ${tool} rule names one host, or every host below one domain with \`*.<domain>\``);
659
+ case "empty_label":
660
+ return reject("invalid.grammar", `"${text}" names a host with an empty label (a leading or doubled dot) — write the host itself, and every host below a domain as \`*.<domain>\``);
661
+ case "not_a_host":
662
+ return reject("invalid.grammar", `"${text}" does not name a host alone — write the host as a URL carries it, with no scheme, path, port or credentials`);
663
+ case "below_address":
664
+ return reject("invalid.grammar", `"${text}" names hosts below an address, which has none — write the address itself`);
665
+ }
620
666
  }
621
- if (below && isAddressHost(host))
622
- return reject("invalid.grammar", `"${text}" names hosts below an address, which has none — write the address itself`);
623
- const match = below ? "subdomain" : "host";
667
+ const { match, host } = read.pattern;
624
668
  return { rule: { behavior, rule: formatRuleText(host, match, tool), tool, match, command: host } };
625
669
  }
626
670
  export function domainRuleAdmits(rule, host) {
627
- if (rule.match === "host")
628
- return host === rule.command;
629
- if (rule.match !== "subdomain" || isAddressHost(host) || !host.endsWith(`.${rule.command}`))
630
- return false;
631
- const labels = host.slice(0, -(rule.command.length + 1));
632
- return labels !== "" && !labels.split(".").includes("");
671
+ return (rule.match === "host" || rule.match === "subdomain") && hostPatternAdmits({ match: rule.match, host: rule.command }, host);
633
672
  }
634
673
  function parsePathRuleContent(text, tool, content, behavior) {
635
674
  if (behavior === "allow" && PATH_RULE_FACES[tool]?.allow === undefined) {
@@ -1303,7 +1342,7 @@ export function commandCoverageOf(rules, call) {
1303
1342
  return covering.length > 0 ? { covering } : { uncovered: undefined };
1304
1343
  }
1305
1344
  function readsOnlyAsSegment(segment, lineObjection, whole) {
1306
- return lineObjection === undefined && !movesShellCursor(segment) && readOnlyShellVerdict(segment, undefined, undefined, () => lineDescriptorsOfText(whole)).readOnly;
1345
+ return lineObjection === undefined && !movesShellCursor(segment) && readOnlyShellVerdict(segment, { bound: () => lineDescriptorsOfText(whole) }).readOnly;
1307
1346
  }
1308
1347
  function firstEligibleDirectoryRule(rules, directory, call) {
1309
1348
  for (const rule of rules) {
@@ -1419,6 +1458,26 @@ export function adjudicatePersistedDomainRules(rules, call) {
1419
1458
  }
1420
1459
  return undefined;
1421
1460
  }
1461
+ export function wholeToolRuleReaches(tool, call) {
1462
+ return heldBy("policy.persistedToolRule", { has: (name) => name === tool }, call);
1463
+ }
1464
+ export function adjudicatePersistedToolRules(rules, call) {
1465
+ const at = { cwd: call.cwd, ...(call.sessionId !== undefined ? { sessionId: call.sessionId } : {}), ...(call.scopeIdentity !== undefined ? { scopeIdentity: call.scopeIdentity } : {}) };
1466
+ for (const behavior of RULE_BEHAVIORS_BY_PRECEDENCE) {
1467
+ const row = rules.find((rule) => rule.match === "tool" && wholeToolRuleReaches(rule.tool, call.request) && eligiblePersisted(rule, { ...at, tool: rule.tool }, behavior));
1468
+ if (row !== undefined)
1469
+ return { behavior, rules: [row] };
1470
+ }
1471
+ return undefined;
1472
+ }
1473
+ export function suggestRuleForTool(tool) {
1474
+ if (cardRuleFormOf(tool) !== "tool")
1475
+ return undefined;
1476
+ const parsed = parseRuleText(formatRuleText("", "tool", tool), "allow");
1477
+ if (!("rule" in parsed) || parsed.rule.match !== "tool" || parsed.rule.tool !== tool)
1478
+ return undefined;
1479
+ return { kind: "single", rule: parsed.rule.rule, match: "tool", command: "" };
1480
+ }
1422
1481
  export function suggestRuleForHost(tool, host) {
1423
1482
  if (ruleToolGrammarOf(tool) !== "domain")
1424
1483
  return undefined;
@@ -224,6 +224,9 @@ export interface UnreadableOrgRow {
224
224
  readonly rule: string;
225
225
  readonly code: RuleRejectCode;
226
226
  readonly detail?: string;
227
+ /** The rule spelling a row that is no rule spelling only because of white space (a tab, a no-break space) is a blank away from — the
228
+ * text to republish it as; the row itself is still read as unreadable (one spelling reader, fail-closed). */
229
+ readonly respelling?: string;
227
230
  readonly behavior: "deny" | "ask";
228
231
  readonly reach: OrgRowReach;
229
232
  }
@@ -327,8 +330,9 @@ export declare function unenforceableOrgRules(rules: readonly OrgPermissionRule[
327
330
  rule: string;
328
331
  code: RuleRejectCode;
329
332
  }>;
330
- /** The reason an unreadable row's verdict carries — its place in the snapshot, its code, and the repair. */
331
- export declare function unreadableOrgRowReason(row: Pick<UnreadableOrgRow, "index" | "code" | "detail">): string;
333
+ /** The reason an unreadable row's verdict carries — its place in the snapshot, its code, and the repair: for a row that is no rule
334
+ * spelling only because of white space (a tab, a no-break space), its text and the spelling to republish it as. */
335
+ export declare function unreadableOrgRowReason(row: Pick<UnreadableOrgRow, "index" | "code" | "detail" | "rule" | "respelling">): string;
332
336
  /** The ONE clause for an org verdict that carries an `unreadable` reason — a rule the call could not be read against, or
333
337
  * a row this layer cannot read — on a refusal and on a question alike. */
334
338
  export declare function orgUnreadableClause(reason: string): string;