@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
@@ -460,6 +460,9 @@ export type RuleRejectCode = "invalid.grammar" | "invalid.empty_command" | "inva
460
460
  export interface RuleReject {
461
461
  code: RuleRejectCode;
462
462
  message: string;
463
+ /** 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 `(`:
464
+ * the rule spelling it is a blank away from ({@link respelledRuleTextOf}) — named in `message`, never read in its place. */
465
+ respelling?: string;
463
466
  }
464
467
  /** The parsed, canonical shape of one rule text under one behavior — everything but the scope and the
465
468
  * provenance. The behavior is the parser's INPUT (it decides which validator floor applies), carried
@@ -718,6 +721,13 @@ export declare function ruleContentOf(parsed: ParsedPermissionRule): string | un
718
721
  * most wants. The tightening behaviors therefore admit the interpreter-headed prefix form and change
719
722
  * nothing else; the allow floor is unchanged.
720
723
  */
724
+ /**
725
+ * The rule spelling a text the rule syntax cannot read is a blank away from: the text with the white space (tabs included) around
726
+ * it and before its first `(` taken off, when that reads as a rule under `behavior` — its canonical text — else `undefined`. A text
727
+ * 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
728
+ * taken (a door that reads the respelled text would be a second reading of the same text).
729
+ */
730
+ export declare function respelledRuleTextOf(text: string, behavior: RuleBehavior): string | undefined;
721
731
  export declare function parseRuleText(text: string, behavior: RuleBehavior): {
722
732
  rule: ParsedRule;
723
733
  } | {
@@ -745,8 +755,8 @@ export type NameRuleReach = "reached" | "not_reached" | "unread";
745
755
  export declare function nameRuleReach(rule: Pick<PersistedRule, "tool" | "behavior" | "match" | "command">, name: string, values: readonly string[] | undefined): NameRuleReach;
746
756
  /** Why a call is unread against a mounted row's rule ({@link nameRuleReach} `unread`) — the sentence every reader carries. */
747
757
  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. */
758
+ /** Does a domain rule reach this host (as `hostOfUrl` reads it)? The pattern's own reach ({@link hostPatternAdmits}); a rule of
759
+ * any other family reaches no host. */
750
760
  export declare function domainRuleAdmits(rule: Pick<PersistedRule, "match" | "command">, host: string): boolean;
751
761
  /**
752
762
  * The closed set of BASES a path-form pattern resolves against — facts of the CALL (its execution
@@ -1255,7 +1265,8 @@ export interface CallHosts {
1255
1265
  * 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
1266
  * tool's first eligible deny row, whatever host that row names, and otherwise ASKED by its first eligible ask row in the
1257
1267
  * 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.
1268
+ * 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
1269
+ * or an ask (its fields still tighten); the lane answers a call none of the rows refuses as one it cannot judge.
1259
1270
  */
1260
1271
  export declare function adjudicatePersistedDomainRules(rules: readonly PersistedRule[], call: {
1261
1272
  tool: string;
@@ -2,12 +2,12 @@ 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 { parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js";
6
6
  import { compileReadDeny } from "../tools/fs/read-deny.js";
7
7
  import { carriesShellRedirection, parseLeadingCommandName, splitShellCompoundSegments } from "../tools/fs/bash-readonly-classifier.js";
8
8
  import { INLINE_TRUNCATION_MARK, inlineUntrusted } from "./untrusted-text.js";
9
9
  import { catalogRuleFaceOf, hostTargetOf, isShellFaceToolName, pathTargetOf, protectivePathTargetOf } from "./tool-registry.js";
10
- import { HOST_UNREADABLE_SENTENCE, hostOfSpelling, isAddressHost } from "./host-target.js";
10
+ import { HOST_UNREADABLE_SENTENCE, hostPatternAdmits, readHostPattern, spelledHostPattern } from "./host-target.js";
11
11
  import { heldValues } from "./tool-name-readings.js";
12
12
  import { carriesDotSegment, isAbsoluteForFamily, isIdentityDir, isPathIdentity, isShellRootedSpellingUnmapped, isUncIdentity, pathFamilyOf, pathIdentityOf } from "../tools/fs/safety.js";
13
13
  import { bindPathRules, judgeRuleRows, rowsWhereTheyLead } from "./path-rule-binding.js";
@@ -406,6 +406,15 @@ export function ruleContentOf(parsed) {
406
406
  const content = parsed.ruleContent;
407
407
  return content === undefined || (parsed.toolName === COMMAND_RULE_TOOL && foldSpacing(content) === "*") ? undefined : content;
408
408
  }
409
+ export function respelledRuleTextOf(text, behavior) {
410
+ const outer = text.trim();
411
+ const open = outer.indexOf("(");
412
+ const candidate = open < 0 ? outer : outer.slice(0, open).replace(/\s+$/u, "") + outer.slice(open);
413
+ if (candidate === text || ruleSpellingOf(candidate) === undefined)
414
+ return undefined;
415
+ const read = parseRuleText(candidate, behavior);
416
+ return "rule" in read ? read.rule.rule : undefined;
417
+ }
409
418
  export function parseRuleText(text, behavior) {
410
419
  if (!isRuleBehavior(behavior)) {
411
420
  return reject("invalid.grammar", `rule "${text}" was parsed under behavior ${escapeForDisclosure(behavior)}, which is not one of ${RULE_BEHAVIORS.join("/")}`);
@@ -417,7 +426,17 @@ export function parseRuleText(text, behavior) {
417
426
  if (CONTROL_CHARS_RE.test(text)) {
418
427
  return reject("invalid.control_chars", "rule text contains control characters (C0/C1, tab excepted), which cannot be part of a readable command");
419
428
  }
420
- const parsed = parsePermissionRule(text);
429
+ const parsed = ruleSpellingOf(text);
430
+ if (parsed === undefined) {
431
+ const respelling = respelledRuleTextOf(text, behavior);
432
+ return {
433
+ reject: {
434
+ code: "invalid.grammar",
435
+ 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}"` : ""}`,
436
+ ...(respelling !== undefined ? { respelling } : {}),
437
+ },
438
+ };
439
+ }
421
440
  const ruleContent = ruleContentOf(parsed);
422
441
  if (ruleContent === undefined) {
423
442
  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`);
@@ -498,7 +517,7 @@ export function formatRuleText(command, match, tool) {
498
517
  if (match === "name")
499
518
  return `${tool}(${keyedValueParamOf(tool) !== undefined ? `${keyedValueParamOf(tool)}:` : ""}${command})`;
500
519
  if (match === "host" || match === "subdomain")
501
- return `${tool}(domain:${match === "subdomain" ? "*." : ""}${command})`;
520
+ return `${tool}(domain:${spelledHostPattern({ match, host: command })})`;
502
521
  return `${tool}(${command}${match === "prefix" || match === "name_prefix" ? ":*" : match === "wildcard" ? " *" : ""})`;
503
522
  }
504
523
  function parseNameRuleContent(text, tool, content, behavior, verifyCanonical = true) {
@@ -606,30 +625,26 @@ function parseDomainRuleContent(text, tool, content, behavior) {
606
625
  if (!content.startsWith(DOMAIN_RULE_MARKER)) {
607
626
  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
627
  }
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`);
628
+ const read = readHostPattern(content.slice(DOMAIN_RULE_MARKER.length));
629
+ if ("problem" in read) {
630
+ switch (read.problem) {
631
+ case "every_host":
632
+ return reject("unsupported.wildcard", `"${text}" names every host — the whole tool is the bare ${tool} entry`);
633
+ case "inner_wildcard":
634
+ 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>\``);
635
+ case "empty_label":
636
+ 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>\``);
637
+ case "not_a_host":
638
+ 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`);
639
+ case "below_address":
640
+ return reject("invalid.grammar", `"${text}" names hosts below an address, which has none — write the address itself`);
641
+ }
620
642
  }
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";
643
+ const { match, host } = read.pattern;
624
644
  return { rule: { behavior, rule: formatRuleText(host, match, tool), tool, match, command: host } };
625
645
  }
626
646
  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("");
647
+ return (rule.match === "host" || rule.match === "subdomain") && hostPatternAdmits({ match: rule.match, host: rule.command }, host);
633
648
  }
634
649
  function parsePathRuleContent(text, tool, content, behavior) {
635
650
  if (behavior === "allow" && PATH_RULE_FACES[tool]?.allow === 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;
@@ -284,12 +284,13 @@ function orgStateCopyOf(state) {
284
284
  function readOrgRow(row, index) {
285
285
  const { rule: text, behavior: said } = orgRowPolicyOf(row);
286
286
  const behavior = said === "ask" ? "ask" : "deny";
287
- const unreadable = (code, detail) => ({
287
+ const unreadable = (code, detail, respelling) => ({
288
288
  kind: "unreadable",
289
289
  index,
290
290
  rule: typeof text === "string" ? text : text === undefined ? "<unreadable>" : escapeForDisclosure(text),
291
291
  code,
292
292
  ...(detail !== undefined ? { detail } : {}),
293
+ ...(respelling !== undefined ? { respelling } : {}),
293
294
  behavior,
294
295
  reach: reachOfRowHead(text, behavior),
295
296
  });
@@ -299,7 +300,7 @@ function readOrgRow(row, index) {
299
300
  return unreadable("invalid.grammar", said === undefined ? "no behavior" : `behavior "${escapeForDisclosure(said)}" is not deny or ask`);
300
301
  const parsed = parseRuleText(text, said);
301
302
  if ("reject" in parsed)
302
- return unreadable(parsed.reject.code);
303
+ return unreadable(parsed.reject.code, undefined, parsed.reject.respelling);
303
304
  if (parsed.rule.tool !== COMMAND_RULE_TOOL)
304
305
  return unreadable("unsupported.tool");
305
306
  return { kind: "read", index, rule: text, behavior: said, parsed: parsed.rule };
@@ -355,6 +356,9 @@ function readListedOrgRules(rules) {
355
356
  return reading;
356
357
  }
357
358
  export function unreadableOrgRowReason(row) {
359
+ if (row.respelling !== undefined) {
360
+ return `row ${row.index} of the organization snapshot, "${escapeForDisclosure(row.rule)}", is not a rule spelling (${row.code}); republish it as "${escapeForDisclosure(row.respelling)}"`;
361
+ }
358
362
  return `row ${row.index} of the organization snapshot is not a rule this layer reads (${row.code}${row.detail !== undefined ? `: ${row.detail}` : ""}); republish it in the shell tool's command grammar`;
359
363
  }
360
364
  export function orgUnreadableClause(reason) {
@@ -365,7 +369,7 @@ export function describeUnreadableOrgRows(unreadable, revision) {
365
369
  return undefined;
366
370
  const named = unreadable
367
371
  .slice(0, DISCLOSED_UNREADABLE_ROWS)
368
- .map((u) => `row ${u.index} "${escapeForDisclosure(u.rule)}" (${u.code}${u.detail !== undefined ? `: ${u.detail}` : ""})`)
372
+ .map((u) => `row ${u.index} "${escapeForDisclosure(u.rule)}" (${u.code}${u.detail !== undefined ? `: ${u.detail}` : ""}${u.respelling !== undefined ? `; republish it as "${escapeForDisclosure(u.respelling)}"` : ""})`)
369
373
  .join(", ");
370
374
  const more = unreadable.length - DISCLOSED_UNREADABLE_ROWS;
371
375
  return (`the org snapshot${revision !== undefined ? ` (revision ${escapeForDisclosure(revision)})` : ""} is served DEGRADED: ${unreadable.length} row(s) this layer cannot read — ${named}${more > 0 ? ` and ${more} more` : ""}; ` +
@@ -36,7 +36,7 @@
36
36
  */
37
37
  import type { PersistedRule, RuleScope } from "./permission-rule-model.js";
38
38
  import { type DurableRulePartitionProvider, type QuarantinedRuleAdd } from "./permission-rule-store.js";
39
- import type { RuleOwner } from "./rule-owner.js";
39
+ import { type RuleOwner } from "./rule-owner.js";
40
40
  import { type OrgRulePartitionConfig, type OrgRuleResolution } from "./permission-rule-org.js";
41
41
  import type { SessionRulePartition } from "./permission-rule-session.js";
42
42
  import type { StoreDurability, StoreFidelity } from "./checkpoint-store.js";
@@ -146,7 +146,10 @@ export interface PermissionRuleStoreProvider {
146
146
  * provider: an absent principal resolves to zero personal rules, and a provider that forgot it would hand one person's
147
147
  * standing approvals to an anonymous task. The ONE exception is an explicitly declared LOCAL-OWNER deployment, where "no
148
148
  * principal" is the typed absence of identity — the machine owner's own bucket (a provider with no such face resolves
149
- * the anonymous store; {@link createPermissionRuleLanes} refuses that declaration loudly).
149
+ * the anonymous store; {@link createPermissionRuleLanes} refuses that declaration loudly). "No principal" is ABSENCE only —
150
+ * `undefined`, and the empty string the engine has always spelled the same ({@link readPrincipal}). A principal that was provided
151
+ * and is not a name (white space, an invisible or control character) is a bad value and is refused here: read as absence it
152
+ * would be the machine owner's bucket on a local-owner deployment.
150
153
  */
151
154
  export declare function ownerStoreOf(provider: PermissionRuleStoreProvider, principal: string | undefined, localOwnerDeclared: boolean): PermissionRuleStore;
152
155
  /** What {@link createPermissionRuleStoreProvider} composes. */
@@ -1,17 +1,39 @@
1
- import { escapeForDisclosure } from "./permission-rule-model.js";
1
+ import { escapeForDisclosure, isRuleBehavior, parseRuleText } from "./permission-rule-model.js";
2
2
  import { PERMISSION_RULE_WRITER, errText, isValidDurableScope, normalizePersistedRule, sameRuleIdentity, writerOf, } from "./permission-rule-store.js";
3
+ import { readRuleOwner } from "./rule-owner.js";
4
+ import { readPrincipal } from "./principal-name.js";
3
5
  import { UNGOVERNED_ORG_RESOLUTION, compileOrgShadowPredicate, createOrgRulePartition } from "./permission-rule-org.js";
4
6
  export function ruleSourceOf(scope) {
5
7
  return scope.kind === "global" ? "user" : scope.kind;
6
8
  }
7
9
  export function ownerStoreOf(provider, principal, localOwnerDeclared) {
8
- const anonymous = principal === undefined || principal === "";
9
- return anonymous && localOwnerDeclared && provider.forLocalOwner !== undefined ? provider.forLocalOwner() : provider.forPrincipal(anonymous ? undefined : principal);
10
+ const reading = readPrincipal(principal);
11
+ if (reading.kind === "invalid") {
12
+ throw new Error(`the principal given is not a principal name (got ${reading.fault}) — refusing to resolve a bucket for it; a provided value that names nobody is not "no principal"`);
13
+ }
14
+ if (reading.kind === "name")
15
+ return provider.forPrincipal(reading.name);
16
+ return localOwnerDeclared && provider.forLocalOwner !== undefined ? provider.forLocalOwner() : provider.forPrincipal(undefined);
10
17
  }
11
18
  function usableSessionId(sessionId) {
12
19
  return typeof sessionId === "string" && sessionId !== "" ? sessionId : undefined;
13
20
  }
14
21
  const EMPTY_DURABLE = { rules: [], tombstones: [], rev: 0 };
22
+ function durableReadOf(settled) {
23
+ if (!settled.ok)
24
+ return settled;
25
+ for (const [kind, rows] of [["rule", settled.v.rules], ["tombstone", settled.v.tombstones]]) {
26
+ const at = rows.findIndex((r) => !isRuleBehavior(r?.behavior));
27
+ if (at < 0)
28
+ continue;
29
+ const row = rows[at];
30
+ return {
31
+ ok: false,
32
+ e: new Error(`the persisted rule partition served a ${kind} row (#${at}, ${escapeForDisclosure(row?.rule)}) whose behavior word ${escapeForDisclosure(row?.behavior)} is outside deny/ask/allow — whether it refuses a call is unknown, so the partition is read as one that could not be read`),
33
+ };
34
+ }
35
+ return settled;
36
+ }
15
37
  export function createPermissionRuleStoreProvider(cfg) {
16
38
  if (cfg === null || typeof cfg !== "object") {
17
39
  throw new Error("createPermissionRuleStoreProvider takes a config object naming the partitions to compose");
@@ -61,7 +83,7 @@ class UnifiedPermissionRuleStore {
61
83
  const disclosures = [];
62
84
  const [orgSettled, durableSettled, sessionRows] = await Promise.all([
63
85
  this.org !== undefined ? this.org.resolve() : Promise.resolve({ ...UNGOVERNED_ORG_RESOLUTION, rules: [], disclosures: [] }),
64
- this.durable !== undefined ? this.durable.list().then((v) => ({ ok: true, v }), (e) => ({ ok: false, e })) : Promise.resolve({ ok: true, v: EMPTY_DURABLE }),
86
+ this.durable !== undefined ? this.durable.list().then((v) => durableReadOf({ ok: true, v }), (e) => ({ ok: false, e })) : Promise.resolve({ ok: true, v: EMPTY_DURABLE }),
65
87
  this.readSessionRows(usableSessionId(query?.sessionId), disclosures),
66
88
  ]);
67
89
  const orgResolution = orgSettled;
@@ -106,7 +128,7 @@ class UnifiedPermissionRuleStore {
106
128
  const served = structuredClone(await this.session.read(sessionId));
107
129
  const rows = served.filter((r) => {
108
130
  const scope = r?.scope;
109
- return scope?.kind === "session" && scope.sessionId === sessionId && !("reject" in normalizePersistedRule(r));
131
+ return scope?.kind === "session" && scope.sessionId === sessionId && (!("reject" in normalizePersistedRule(r)) || ((r.behavior === "deny" || r.behavior === "ask") && "reject" in parseRuleText(r.rule, r.behavior)));
110
132
  });
111
133
  if (rows.length < served.length) {
112
134
  disclosures.push(`the session rule partition served ${served.length - rows.length} row(s) that are not canonical session rows of this session — dropped, not adjudicated`);
@@ -157,8 +179,17 @@ export async function effectivePermissionRules(opts) {
157
179
  return await resolveOwnerStore(opts).effective(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : undefined);
158
180
  }
159
181
  export function resolveOwnerStore(opts) {
160
- if (opts.owner !== undefined && opts.owner.kind === "local-owner") {
161
- if (opts.principal !== undefined) {
182
+ const owner = opts.owner === undefined ? undefined : readRuleOwner(opts.owner);
183
+ if (opts.owner !== undefined && owner === undefined) {
184
+ throw new Error("the owner given is neither a principal nor the local owner (a principal owner names a principal name) — refusing to resolve a bucket nobody can name");
185
+ }
186
+ const given = readPrincipal(opts.principal);
187
+ if (given.kind === "invalid") {
188
+ throw new Error(`the principal given is not a principal name (got ${given.fault}) — pass a verified principal, the local owner, or neither; a provided value that names nobody is not "no principal"`);
189
+ }
190
+ const principal = given.kind === "name" ? given.name : undefined;
191
+ if (owner?.kind === "local-owner") {
192
+ if (principal !== undefined) {
162
193
  throw new Error("pass either a principal or a local-owner, not both — a bucket has one owner");
163
194
  }
164
195
  const store = opts.provider.forLocalOwner?.();
@@ -167,9 +198,8 @@ export function resolveOwnerStore(opts) {
167
198
  }
168
199
  return store;
169
200
  }
170
- if (opts.owner !== undefined && opts.owner.kind === "principal" && opts.principal !== undefined && opts.owner.principal !== opts.principal) {
171
- throw new Error(`contradictory identity: principal "${escapeForDisclosure(opts.principal)}" and owner principal "${escapeForDisclosure(opts.owner.principal)}" disagree`);
201
+ if (owner?.kind === "principal" && principal !== undefined && owner.principal !== principal) {
202
+ throw new Error(`contradictory identity: principal "${escapeForDisclosure(principal)}" and owner principal "${escapeForDisclosure(owner.principal)}" disagree`);
172
203
  }
173
- const principal = opts.owner?.kind === "principal" ? opts.owner.principal : opts.principal;
174
- return opts.provider.forPrincipal(principal);
204
+ return opts.provider.forPrincipal(owner?.kind === "principal" ? owner.principal : principal);
175
205
  }
@@ -45,7 +45,7 @@ import type { PersistedRule, RuleAdd, RuleDot, RuleScope, RuleTombstone } from "
45
45
  import { type RuleBehavior } from "./permission-rule-model.js";
46
46
  import type { RuleSyncDropReason, RuleQuarantineReason } from "./governance-codes.js";
47
47
  import type { StoreDurability, StoreFidelity } from "./checkpoint-store.js";
48
- import type { RuleOwner } from "./rule-owner.js";
48
+ import { type RuleOwner } from "./rule-owner.js";
49
49
  /** One store read: the rules as persisted, their tombstones, and the revision they were read at. */
50
50
  export interface StoredRules {
51
51
  /** Rules with their FULL add sets — tombstoned dots already removed by the backend on read. */
@@ -93,7 +93,8 @@ export interface DurableRulePartition {
93
93
  /** Declared serialization fidelity, same doctrine as the other store families. */
94
94
  readonly fidelity?: StoreFidelity;
95
95
  }
96
- /** Resolves one owner's durable partition. `undefined` ⇒ a partition that reports zero rules. This is
96
+ /** Resolves one owner's durable partition. `undefined` — or anything that is not a principal name
97
+ * (`principalNameOf`: the empty and the blank string included) ⇒ a partition that reports zero rules. This is
97
98
  * the BACKEND contract (file, in-memory, an out-of-repo SQL twin); the engine never consumes it
98
99
  * directly — `createPermissionRuleStoreProvider({ durable })` composes it into the unified store. */
99
100
  export interface DurableRulePartitionProvider {
@@ -1,6 +1,7 @@
1
1
  import { randomBytes } from "node:crypto";
2
2
  import { canonicalize } from "./canonical-json.js";
3
3
  import { escapeForDisclosure, isRuleBehavior, isRuleLive, parseRuleText } from "./permission-rule-model.js";
4
+ import { readRuleOwner } from "./rule-owner.js";
4
5
  import { thrownText } from "./thrown-value.js";
5
6
  import { validatePath } from "./effective-path-target.js";
6
7
  export const PERMISSION_RULES_LATER_GRAMMAR_CODE = "permission_rules.later_grammar";
@@ -510,6 +511,10 @@ async function readRuleLiveness(store, identity) {
510
511
  }
511
512
  }
512
513
  const REMOVE_MAX_ATTEMPTS = 8;
514
+ const NO_TARGET_OWNER = "the principal given is neither a principal name (a string with a visible character, no control or separator character, and no format character but a joiner) nor an owner (a principal owner names a principal name; the local owner is { kind: \"local-owner\" })";
515
+ function persistedRuleTargetOwner(target) {
516
+ return readRuleOwner(typeof target === "string" ? { kind: "principal", principal: target } : target);
517
+ }
513
518
  export async function removePersistedRule(opts) {
514
519
  const behavior = opts.behavior;
515
520
  const rule = opts.rule;
@@ -519,7 +524,9 @@ export async function removePersistedRule(opts) {
519
524
  return { status: "failed", error: `removePersistedRule: behavior ${escapeForDisclosure(behavior)} is not one of ${["deny", "ask", "allow"].join("/")} — refusing to name a rule identity nothing can carry`, committed: "no" };
520
525
  }
521
526
  const identity = { behavior, rule, scope };
522
- const owner = typeof target === "string" ? { kind: "principal", principal: target } : structuredClone(target);
527
+ const owner = persistedRuleTargetOwner(target);
528
+ if (owner === undefined)
529
+ return { status: "failed", error: `removePersistedRule: ${NO_TARGET_OWNER} — refusing to remove from a bucket nobody can name`, committed: "no" };
523
530
  const provider = opts.provider;
524
531
  if (scope?.kind === "session") {
525
532
  return {
@@ -686,7 +693,9 @@ export async function addPersistedRule(opts) {
686
693
  if ("reject" in parsed)
687
694
  return { status: "failed", error: `addPersistedRule: ${parsed.reject.code}: ${parsed.reject.message}`, committed: "no" };
688
695
  const identity = { behavior, rule: parsed.rule.rule, scope };
689
- const owner = typeof target === "string" ? { kind: "principal", principal: target } : structuredClone(target);
696
+ const owner = persistedRuleTargetOwner(target);
697
+ if (owner === undefined)
698
+ return { status: "failed", error: `addPersistedRule: ${NO_TARGET_OWNER} — refusing to write into a bucket nobody can name`, committed: "no" };
690
699
  const provider = opts.provider;
691
700
  if (owner.kind === "local-owner" && provider.forLocalOwner === undefined) {
692
701
  return { status: "failed", error: "this provider has no local-owner bucket (forLocalOwner is not implemented) — a local-owner rule cannot be added through it", committed: "no" };
@@ -1,4 +1,5 @@
1
1
  import { escapeForDisclosure, isRuleBehavior, parseRuleText, RULE_ADD_ORIGINS } from "./permission-rule-model.js";
2
+ import { principalNameOf } from "./principal-name.js";
2
3
  import { PERMISSION_RULES_LATER_GRAMMAR_CODE, applyTombstones, errText, isProjectScopeRoot, joinFrontiers, joinRuleStates, normalizePersistedRule, readRuleWriteOutcome, ruleSyncVector, sameRuleIdentity, screenRuleSyncState, writerOf, } from "./permission-rule-store.js";
3
4
  import { emitTrace } from "./trace.js";
4
5
  import { SESSION_RULES_GRAMMAR, readRuleGrammarStamp } from "./session-policy-store.js";
@@ -23,8 +24,8 @@ export async function syncPermissionRules(opts) {
23
24
  throw new Error(`contradictory identity: principal "${escapeForDisclosure(principal)}" and owner principal "${escapeForDisclosure(owner.principal)}" disagree`);
24
25
  }
25
26
  }
26
- if (typeof principal !== "string" || principal === "") {
27
- throw new Error("syncPermissionRules requires a verified principal — an unauthenticated deployment has no cloud bucket to sync");
27
+ if (principalNameOf(principal) === undefined) {
28
+ throw new Error("syncPermissionRules requires a verified principal — an unauthenticated deployment has no cloud bucket to sync (a blank name, or one holding a control, separator or invisible format character, is not a principal)");
28
29
  }
29
30
  const traceClock = opts.now ?? Date.now;
30
31
  const disclose = (result) => {
@@ -29,10 +29,11 @@ export declare function lastIndexOfUnescaped(s: string, ch: string): number;
29
29
  */
30
30
  export declare function parsePermissionRule(rule: string): ParsedPermissionRule;
31
31
  /**
32
- * Can a rule name this tool name — is it the rule syntax's name head ({@link RULE_TOOL_NAME}) and not the lone `*`? The
33
- * lone `*` is the wildcard (every tool), not a name: a tool called `*` could never be granted or refused on its own. The
34
- * mount contract for every name a caller tool answers to (its name and each alias), so each one is a name every list can
35
- * write; a protocol-minted name (`<ns>__<peer>__<tool>`, segments in `[A-Za-z0-9_-]`) is always one.
32
+ * Can a rule name this tool name — is it the rule syntax's name head ({@link RULE_TOOL_NAME}) with no `*` in it? A `*` makes a
33
+ * spelling a glob over names, not a name: a tool whose name held one would be reached by every rule whose glob matches it and
34
+ * could never be named on its own (the reference's mount spelling holds no `*` either). The mount contract for every name a
35
+ * caller tool answers to (its name and each alias), so each one is a name every list can write; a protocol-minted name
36
+ * (`<ns>__<peer>__<tool>`, segments in `[A-Za-z0-9_-]`) is always one.
36
37
  */
37
38
  export declare function isNameableToolName(name: string): boolean;
38
39
  /**
@@ -36,9 +36,9 @@ export function parsePermissionRule(rule) {
36
36
  return { toolName: name.trim() };
37
37
  return { toolName: name.trim(), ruleContent: unescapeRuleContent(content) };
38
38
  }
39
- const RULE_TOOL_NAME = /^(?:\*|[A-Za-z0-9_](?:[A-Za-z0-9_.:*-]*[A-Za-z0-9_*-])?)$/;
39
+ const RULE_TOOL_NAME = /^[A-Za-z0-9_*](?:[A-Za-z0-9_.:*-]*[A-Za-z0-9_*-])?$/;
40
40
  export function isNameableToolName(name) {
41
- return typeof name === "string" && name !== "*" && RULE_TOOL_NAME.test(name);
41
+ return typeof name === "string" && !name.includes("*") && RULE_TOOL_NAME.test(name);
42
42
  }
43
43
  export function ruleSpellingOf(rule) {
44
44
  if (rule !== rule.trim())
@@ -90,8 +90,8 @@ export interface PermissionRuleIssue {
90
90
  /** A rule the rule syntax reads no rule in (`ruleSpellingOf`) whose name is the reason: blanks around the rule, a
91
91
  * character outside a tool name's spelling (a variation selector, a filler, a zero-width space), a blank or an invisible
92
92
  * character before the `(`, a leading `-`. Refused — the lenient reading would deny a tool under another spelling, or none.
93
- * Also a rule whose tool name the syntax reads as the wildcard `*` (`*`, `*()`, `*(*)`): a rule reaches the tool it
94
- * names, and `*` names none. */
93
+ * (A `*` in a tool name is no such spelling: a deny or ask rule reads it as a glob over the whole name, `*` alone as every
94
+ * tool.) */
95
95
  | "invalid.tool_name" | "invalid.param_split" | "invalid.primary_field" | "invalid.cap_exceeded"
96
96
  /** An ALLOW rule (any spelling): a policy fold says deny / ask only — "this does not need asking" is the allow
97
97
  * layer's (`TaskSpec.allowedTools`, `RunnerDeps.allowedTools`). One issue per allow rule. */
@@ -206,8 +206,8 @@ export interface PermissionRulePolicyOptions {
206
206
  */
207
207
  pathBases?: PathRuleBases;
208
208
  }
209
- import { isNamespacedCoveringRuleName, namespacedRuleNameCovers, wildcardMatch } from "./namespaced-rule-name.js";
210
- export { isNamespacedCoveringRuleName, namespacedRuleNameCovers, wildcardMatch };
209
+ import { isCoveringRuleName, ruleNameCovers, wildcardMatch } from "./namespaced-rule-name.js";
210
+ export { isCoveringRuleName, ruleNameCovers, wildcardMatch };
211
211
  /**
212
212
  * design/388 A-6/7/8/9 (D-3): the per-tool RULE FACE — the parameters a `Tool(param:pattern)` rule may name
213
213
  * (`params`, the SCALAR top-level keys ∪ the path target's aliases), the ones the generic lexical matcher must
@@ -1,5 +1,5 @@
1
1
  import { attest } from "./tool-policy.js";
2
- import { MCP_NAMESPACE, protocolOf } from "./protocol-table.js";
2
+ import { MCP_NAMESPACE } from "./protocol-table.js";
3
3
  import { catalogRuleFaceOf, isShellFaceToolName, pathTargetOf } from "./tool-registry.js";
4
4
  import { HOST_UNREADABLE_SENTENCE, callHostTargetOf } from "./host-target.js";
5
5
  import { indexOfUnescaped, lastIndexOfUnescaped, parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js";
@@ -36,9 +36,8 @@ export const DEFAULT_CAPS = {
36
36
  maxStars: 16,
37
37
  maxScalarValueChars: DEFAULT_MAX_SCALAR_VALUE_CHARS,
38
38
  };
39
- import { isNamespacedCoveringRuleName, namespacedRuleNameCovers, wildcardMatch } from "./namespaced-rule-name.js";
40
- import { wildcardPeerRefusalOf } from "./tool-name-aliases.js";
41
- export { isNamespacedCoveringRuleName, namespacedRuleNameCovers, wildcardMatch };
39
+ import { isCoveringRuleName, ruleNameCovers, wildcardMatch } from "./namespaced-rule-name.js";
40
+ export { isCoveringRuleName, ruleNameCovers, wildcardMatch };
42
41
  const vocabularyOf = (face) => face.params ?? face.primaryParams;
43
42
  const carriedPhrase = (face) => {
44
43
  const vocabulary = vocabularyOf(face);
@@ -119,22 +118,13 @@ function compile(rules, caps, primaryFieldGeneric, ruleFaces, pathBases = {}) {
119
118
  bad(text, why.code, why.message);
120
119
  continue;
121
120
  }
122
- if (parsed.toolName === "*") {
123
- bad(text, "invalid.tool_name", "`*` names no tool — a rule reaches the tool it names, so this one would compile and never match; name the tool", { name: "defaultAction" });
124
- continue;
125
- }
126
121
  const toolName = parsed.toolName;
127
122
  const parenthesised = parsed.ruleContent !== undefined || indexOfUnescaped(text, "(") !== -1;
128
123
  if (toolName.startsWith(MCP_NAMESPACE.prefix) && parenthesised) {
129
124
  bad(text, "unsupported.mcp_paren", "MCP rules do not support patterns in parentheses (CC parity); a rule names a whole tool (`mcp__<server>__<tool>`) or a whole server (`mcp__<server>`), and what a tool does is declared per tool in the MCP server's spec", { name: "toolAxes" });
130
125
  continue;
131
126
  }
132
- const wildPeer = wildcardPeerRefusalOf(toolName, verb);
133
- if (wildPeer !== undefined) {
134
- bad(text, "invalid.tool_name", wildPeer);
135
- continue;
136
- }
137
- if (parenthesised && isNamespacedCoveringRuleName(toolName, verb)) {
127
+ if (ruleContentOf(parsed) !== undefined && isCoveringRuleName(toolName, verb)) {
138
128
  bad(text, "unsupported.covering_paren", `"${toolName}" reaches a SET of tools, and the generic param DSL is keyed by exact tool name — a parenthesised rule on it can never match. ` +
139
129
  `Write the parenthesised rule against one exact tool name, or drop the parentheses to keep the covering rule.`);
140
130
  continue;
@@ -142,7 +132,7 @@ function compile(rules, caps, primaryFieldGeneric, ruleFaces, pathBases = {}) {
142
132
  const entry = byTool.get(toolName) ?? { bare: {}, param: { deny: [], ask: [] }, content: { deny: [], ask: [] } };
143
133
  const content = ruleContentOf(parsed);
144
134
  if (content === undefined) {
145
- if (isNamespacedCoveringRuleName(toolName, verb)) {
135
+ if (isCoveringRuleName(toolName, verb)) {
146
136
  namespacedCovering[verb].push({ ruleName: toolName, ruleText: text, ...(r.source !== undefined ? { source: r.source } : {}) });
147
137
  continue;
148
138
  }
@@ -339,7 +329,8 @@ export function createPermissionRulePolicy(rules, opts) {
339
329
  for (const r of namespacedCovering[behavior])
340
330
  nameSets[behavior].push(r.ruleName);
341
331
  }
342
- const coveringHit = (lane, toolName) => lane.length === 0 ? undefined : lane.find((r) => namespacedRuleNameCovers(r.ruleName, toolName));
332
+ const heldFor = (table, req) => heldValues("policy.permissionRules", table, req);
333
+ const coveringHit = (lane, req) => lane.length === 0 ? undefined : heldFor({ get: (name) => lane.find((r) => ruleNameCovers(r.ruleName, name)) }, req)[0];
343
334
  const hasCovering = namespacedCovering.deny.length > 0 || namespacedCovering.ask.length > 0;
344
335
  const faceRules = {
345
336
  deny: [...byTool.values()].flatMap((e) => e.content.deny.filter((r) => r.faces !== undefined)),
@@ -454,7 +445,7 @@ export function createPermissionRulePolicy(rules, opts) {
454
445
  requiresRealApproval: true,
455
446
  });
456
447
  const entryFor = (req) => {
457
- const entries = heldValues("policy.permissionRules", byTool, req);
448
+ const entries = heldFor(byTool, req);
458
449
  if (entries.length <= 1)
459
450
  return entries[0];
460
451
  const bare = {};
@@ -474,12 +465,12 @@ export function createPermissionRulePolicy(rules, opts) {
474
465
  ...(pathSpecs.length > 0 ? { pathRules: (anchors) => boundAt(policyAnchors(pathBases, anchors)).rows } : {}),
475
466
  check(req) {
476
467
  const entry = entryFor(req);
477
- const covering = hasCovering && protocolOf(req.toolName) !== undefined;
468
+ const covering = hasCovering;
478
469
  if (entry?.bare.deny) {
479
470
  return { action: "deny", message: ruleDenial(req, entry.bare.deny.ruleText, entry.bare.deny.source) };
480
471
  }
481
472
  if (covering) {
482
- const coveringDeny = coveringHit(namespacedCovering.deny, req.toolName);
473
+ const coveringDeny = coveringHit(namespacedCovering.deny, req);
483
474
  if (coveringDeny)
484
475
  return { action: "deny", message: ruleDenial(req, coveringDeny.ruleText, coveringDeny.source) };
485
476
  }
@@ -509,7 +500,7 @@ export function createPermissionRulePolicy(rules, opts) {
509
500
  }
510
501
  }
511
502
  if (covering) {
512
- const coveringAsk = coveringHit(namespacedCovering.ask, req.toolName);
503
+ const coveringAsk = coveringHit(namespacedCovering.ask, req);
513
504
  if (coveringAsk)
514
505
  return { action: "ask", message: ruleAskMessage(coveringAsk.ruleText, coveringAsk.source), matchedAskRule: coveringAsk.ruleText };
515
506
  }
@@ -44,7 +44,7 @@ export declare function disclosedRuleSet(rules: readonly PersistedRuleHitRule[])
44
44
  * lane's allow carries: one authority, one word) and the rule text disclosed through the display
45
45
  * transform; the STATION attributes the refusal (`tightened: "deny"` ⇒ it writes the pass's refusing
46
46
  * layer — the attribution is a station's word and lives in the station file). A path row's hit reports where
47
- * it met the call; with `call` given and the face the call's own, the deny carries that as its attested `pathDenial`, so every exit that
47
+ * it met the call; with the face the call's own (`call`, the request the lane read), the deny carries that as its attested `pathDenial`, so every exit that
48
48
  * reads the deny (the gate's exit, the approved-edit re-check, a resumed leg's belt) mints the same card from the same fact.
49
49
  * · ASK — on an allow, a fresh ask is minted carrying `matchedAskRule` (the marker an explicit `ask`
50
50
  * permission rule stamps, so the ask's origin derives as `ask_rule`: the classifier never answers
@@ -69,7 +69,7 @@ export declare function disclosedRuleSet(rules: readonly PersistedRuleHitRule[])
69
69
  * resumed-call belt, which vetoes only the call form: a person already answered that call's question.
70
70
  * An allow hit (or no hit) returns the decision untouched: the allow arm is the station's.
71
71
  */
72
- export declare function applyPersistedTightening(decision: PermissionResult, read: PersistedRuleRead | undefined, call?: ToolCallRequest): {
72
+ export declare function applyPersistedTightening(decision: PermissionResult, read: PersistedRuleRead | undefined, call: ToolCallRequest): {
73
73
  decision: PermissionResult;
74
74
  tightened?: "deny" | "ask" | "unreadable" | "store_unreadable";
75
75
  };