@sema-agent/core 7.54.1 → 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 (621) hide show
  1. package/CHANGELOG.md +950 -0
  2. package/dist/agents/agent-transcript-tool.js +2 -1
  3. package/dist/agents/builtin-agents.d.ts +12 -14
  4. package/dist/agents/cascade.js +1 -1
  5. package/dist/agents/cc-peer-directory.d.ts +36 -0
  6. package/dist/agents/cc-peer-directory.js +139 -0
  7. package/dist/agents/cc-peer-wire.d.ts +105 -0
  8. package/dist/agents/cc-peer-wire.js +98 -0
  9. package/dist/agents/cc-uds-peer-channel.d.ts +40 -0
  10. package/dist/agents/cc-uds-peer-channel.js +457 -0
  11. package/dist/agents/child-model-seat.d.ts +0 -11
  12. package/dist/agents/child-model-seat.js +0 -3
  13. package/dist/agents/cross-session-envelope.d.ts +4 -1
  14. package/dist/agents/cross-session-envelope.js +8 -1
  15. package/dist/agents/cross-session-judge.d.ts +2 -2
  16. package/dist/agents/list-agents-tool.d.ts +3 -1
  17. package/dist/agents/list-agents-tool.js +9 -8
  18. package/dist/agents/observer.d.ts +4 -5
  19. package/dist/agents/observer.js +2 -1
  20. package/dist/agents/peer-admission.d.ts +6 -0
  21. package/dist/agents/peer-admission.js +4 -1
  22. package/dist/agents/peer-directory.d.ts +65 -13
  23. package/dist/agents/peer-directory.js +90 -25
  24. package/dist/agents/peer-notices.d.ts +1 -1
  25. package/dist/agents/peer-session-drain.d.ts +3 -1
  26. package/dist/agents/peer-session-drain.js +3 -2
  27. package/dist/agents/read-inbox-tool.js +2 -1
  28. package/dist/agents/repair-loop.js +2 -2
  29. package/dist/agents/retain-ledger.d.ts +1 -1
  30. package/dist/agents/roster-store.d.ts +1 -1
  31. package/dist/agents/send-message-tool.d.ts +10 -0
  32. package/dist/agents/send-message-tool.js +37 -8
  33. package/dist/agents/subagent.d.ts +39 -82
  34. package/dist/agents/subagent.js +109 -106
  35. package/dist/agents/tool-filter.d.ts +31 -46
  36. package/dist/agents/tool-filter.js +13 -33
  37. package/dist/agents/verify.d.ts +6 -5
  38. package/dist/agents/verify.js +3 -3
  39. package/dist/brain/anthropic.js +43 -81
  40. package/dist/brain/errors.d.ts +15 -0
  41. package/dist/brain/errors.js +10 -0
  42. package/dist/brain/media-degrade.d.ts +18 -7
  43. package/dist/brain/media-degrade.js +13 -2
  44. package/dist/brain/model-capability.d.ts +37 -0
  45. package/dist/brain/model-capability.js +34 -0
  46. package/dist/brain/model-presets.d.ts +6 -12
  47. package/dist/brain/model-presets.js +1 -6
  48. package/dist/brain/open-responses.js +27 -35
  49. package/dist/brain/openai.js +30 -98
  50. package/dist/brain/provider-dialect.d.ts +39 -3
  51. package/dist/brain/provider-dialect.js +28 -0
  52. package/dist/brain/reasoning-request.d.ts +96 -0
  53. package/dist/brain/reasoning-request.js +174 -0
  54. package/dist/brain/reasoning.d.ts +12 -12
  55. package/dist/brain/reasoning.js +4 -3
  56. package/dist/brain/request-params.d.ts +62 -9
  57. package/dist/brain/request-params.js +42 -7
  58. package/dist/brain/retry.d.ts +1 -1
  59. package/dist/brain/route-adjudicator.d.ts +15 -0
  60. package/dist/brain/route-adjudicator.js +15 -0
  61. package/dist/brain/status-sink.d.ts +20 -0
  62. package/dist/brain/status-sink.js +7 -0
  63. package/dist/brain/stream-engine.js +6 -5
  64. package/dist/brain/stream-shared.d.ts +1 -1
  65. package/dist/core/a2a.d.ts +2 -1
  66. package/dist/core/a2a.js +19 -13
  67. package/dist/core/advisor-verdict.d.ts +120 -0
  68. package/dist/core/advisor-verdict.js +92 -0
  69. package/dist/core/agent-definition.d.ts +17 -8
  70. package/dist/core/allow-entry.d.ts +69 -36
  71. package/dist/core/allow-entry.js +81 -52
  72. package/dist/core/allow-rule-shadow.d.ts +10 -3
  73. package/dist/core/approval-object.d.ts +2 -1
  74. package/dist/core/approval-object.js +6 -6
  75. package/dist/core/ask-origin.d.ts +127 -29
  76. package/dist/core/ask-origin.js +87 -25
  77. package/dist/core/ask-question.d.ts +51 -8
  78. package/dist/core/ask-question.js +12 -7
  79. package/dist/core/ask-unresolvable-notice.d.ts +8 -3
  80. package/dist/core/ask-unresolvable-notice.js +16 -2
  81. package/dist/core/auto-compaction.d.ts +11 -8
  82. package/dist/core/auto-compaction.js +4 -2
  83. package/dist/core/auto-mode-arming.d.ts +2 -2
  84. package/dist/core/auto-mode-defaults.d.ts +8 -8
  85. package/dist/core/auto-mode-prompt-assets.d.ts +3 -3
  86. package/dist/core/auto-mode-prompt.d.ts +1 -1
  87. package/dist/core/auto-mode.d.ts +17 -9
  88. package/dist/core/auto-mode.js +9 -6
  89. package/dist/core/background-agent-store.d.ts +3 -3
  90. package/dist/core/canonical-json.d.ts +1 -1
  91. package/dist/core/canonical-json.js +2 -2
  92. package/dist/core/checkpoint-generation.d.ts +53 -0
  93. package/dist/core/checkpoint-generation.js +35 -0
  94. package/dist/core/checkpoint-queries.d.ts +180 -0
  95. package/dist/core/checkpoint-queries.js +272 -0
  96. package/dist/core/checkpoint-requirements.d.ts +19 -4
  97. package/dist/core/checkpoint-requirements.js +8 -14
  98. package/dist/core/checkpoint-store.d.ts +165 -72
  99. package/dist/core/checkpoint-store.js +79 -82
  100. package/dist/core/child-clamp-seats.d.ts +6 -0
  101. package/dist/core/child-clamp-seats.js +3 -16
  102. package/dist/core/compliance.d.ts +2 -2
  103. package/dist/core/context-edit.d.ts +6 -7
  104. package/dist/core/cross-boundary-taint.d.ts +11 -4
  105. package/dist/core/cross-boundary-taint.js +3 -3
  106. package/dist/core/decision-receipt.d.ts +37 -3
  107. package/dist/core/decision-receipt.js +19 -19
  108. package/dist/core/delegation-not-started.d.ts +6 -0
  109. package/dist/core/delegation-not-started.js +17 -0
  110. package/dist/core/effective-path-target.d.ts +1 -1
  111. package/dist/core/engine-attestation.d.ts +93 -0
  112. package/dist/core/engine-attestation.js +70 -0
  113. package/dist/core/engine-capabilities.d.ts +4 -1
  114. package/dist/core/engine-capabilities.js +1 -1
  115. package/dist/core/engine-notice.d.ts +45 -12
  116. package/dist/core/exec-output-tail.d.ts +1 -1
  117. package/dist/core/file-history-retention.d.ts +1 -1
  118. package/dist/core/file-history-store.d.ts +147 -21
  119. package/dist/core/file-history-store.js +58 -32
  120. package/dist/core/gate-fold.d.ts +1 -1
  121. package/dist/core/gate-fold.js +24 -11
  122. package/dist/core/gate-lanes.js +81 -68
  123. package/dist/core/gate-outcome.d.ts +40 -6
  124. package/dist/core/gate-outcome.js +56 -0
  125. package/dist/core/governance-codes.d.ts +17 -3
  126. package/dist/core/governance-codes.js +16 -0
  127. package/dist/core/hands-band.d.ts +1 -1
  128. package/dist/core/hooks.d.ts +21 -20
  129. package/dist/core/hooks.js +3 -1
  130. package/dist/core/host-floor.d.ts +128 -0
  131. package/dist/core/host-floor.js +206 -0
  132. package/dist/core/host-target.d.ts +84 -0
  133. package/dist/core/host-target.js +92 -0
  134. package/dist/core/image-downsample.d.ts +1 -1
  135. package/dist/core/lsp-diagnostics.d.ts +4 -4
  136. package/dist/core/lsp.d.ts +3 -3
  137. package/dist/core/mailbox-store.d.ts +4 -0
  138. package/dist/core/mcp-connection-pool.d.ts +18 -2
  139. package/dist/core/mcp-connection-pool.js +53 -6
  140. package/dist/core/mcp-failure.d.ts +23 -4
  141. package/dist/core/mcp-failure.js +26 -11
  142. package/dist/core/mcp-listing-drift.d.ts +10 -0
  143. package/dist/core/mcp-listing-drift.js +6 -0
  144. package/dist/core/mcp-server-link.d.ts +173 -0
  145. package/dist/core/mcp-server-link.js +127 -0
  146. package/dist/core/mcp-server-spec.d.ts +4 -2
  147. package/dist/core/mcp-session-end.d.ts +26 -0
  148. package/dist/core/mcp-session-end.js +29 -0
  149. package/dist/core/mcp-startup-request.d.ts +17 -0
  150. package/dist/core/mcp-startup-request.js +17 -0
  151. package/dist/core/mcp.d.ts +17 -13
  152. package/dist/core/mcp.js +240 -189
  153. package/dist/core/media-byte-cap.d.ts +1 -1
  154. package/dist/core/memory-engine/delegation-settlement.js +15 -24
  155. package/dist/core/memory-engine/engine.d.ts +9 -9
  156. package/dist/core/memory-engine/engine.js +4 -4
  157. package/dist/core/memory-engine/layout.d.ts +60 -8
  158. package/dist/core/memory-engine/layout.js +65 -6
  159. package/dist/core/memory-engine/provenance-wording.d.ts +1 -1
  160. package/dist/core/memory-engine/tools.js +4 -1
  161. package/dist/core/mode-policy.d.ts +1 -1
  162. package/dist/core/model-seat.d.ts +10 -3
  163. package/dist/core/mods/classic-events.d.ts +67 -0
  164. package/dist/core/mods/classic-events.js +1 -0
  165. package/dist/core/mods/dollar.d.ts +13 -0
  166. package/dist/core/mods/extension-call.d.ts +31 -0
  167. package/dist/core/mods/extension-call.js +43 -0
  168. package/dist/core/mods/kernel/dispatch.d.ts +20 -3
  169. package/dist/core/mods/kernel/dispatch.js +140 -69
  170. package/dist/core/mods/kernel/events.d.ts +47 -11
  171. package/dist/core/mods/kernel/events.js +66 -42
  172. package/dist/core/mods/kernel/index.d.ts +4 -4
  173. package/dist/core/mods/kernel/index.js +2 -2
  174. package/dist/core/mods/kernel/receive.d.ts +39 -5
  175. package/dist/core/mods/kernel/receive.js +290 -17
  176. package/dist/core/mods/kernel/registration.d.ts +33 -2
  177. package/dist/core/mods/kernel/registration.js +6 -7
  178. package/dist/core/mods/kernel/site-rules.d.ts +44 -16
  179. package/dist/core/mods/kernel/site-rules.js +24 -42
  180. package/dist/core/mods/spec-seats.d.ts +69 -0
  181. package/dist/core/mods/spec-seats.js +114 -0
  182. package/dist/core/mods/stations.d.ts +28 -8
  183. package/dist/core/mods/stations.js +29 -13
  184. package/dist/core/mods/tool-call.d.ts +79 -0
  185. package/dist/core/mods/tool-call.js +152 -0
  186. package/dist/core/namespaced-rule-name.d.ts +44 -24
  187. package/dist/core/namespaced-rule-name.js +50 -43
  188. package/dist/core/output-style.d.ts +1 -1
  189. package/dist/core/param-rule-reach.d.ts +15 -0
  190. package/dist/core/param-rule-reach.js +30 -0
  191. package/dist/core/parent-spec-seat.d.ts +13 -1
  192. package/dist/core/parent-spec-seat.js +6 -0
  193. package/dist/core/park-selfcheck.js +1 -0
  194. package/dist/core/pause-registry.d.ts +16 -5
  195. package/dist/core/pause-registry.js +6 -2
  196. package/dist/core/peer-channel.d.ts +70 -0
  197. package/dist/core/peer-channel.js +1 -0
  198. package/dist/core/permission-rule-consent.d.ts +30 -5
  199. package/dist/core/permission-rule-consent.js +147 -50
  200. package/dist/core/permission-rule-model.d.ts +245 -65
  201. package/dist/core/permission-rule-model.js +421 -107
  202. package/dist/core/permission-rule-org.d.ts +6 -2
  203. package/dist/core/permission-rule-org.js +7 -3
  204. package/dist/core/permission-rule-provider.d.ts +6 -2
  205. package/dist/core/permission-rule-provider.js +41 -11
  206. package/dist/core/permission-rule-store.d.ts +22 -26
  207. package/dist/core/permission-rule-store.js +12 -6
  208. package/dist/core/permission-rule-sync.d.ts +11 -2
  209. package/dist/core/permission-rule-sync.js +11 -3
  210. package/dist/core/permission-rule-syntax.d.ts +5 -4
  211. package/dist/core/permission-rule-syntax.js +2 -2
  212. package/dist/core/permission-rules.d.ts +53 -15
  213. package/dist/core/permission-rules.js +125 -100
  214. package/dist/core/persisted-rule-arms.d.ts +6 -4
  215. package/dist/core/persisted-rule-arms.js +10 -12
  216. package/dist/core/present-plan-tool.js +3 -1
  217. package/dist/core/principal-name.d.ts +20 -0
  218. package/dist/core/principal-name.js +22 -0
  219. package/dist/core/prompt-section-seat.js +4 -6
  220. package/dist/core/protocol-http-auth.d.ts +26 -5
  221. package/dist/core/protocol-http-auth.js +137 -4
  222. package/dist/core/protocol-naming.d.ts +2 -2
  223. package/dist/core/read-only-shell-table.d.ts +1 -1
  224. package/dist/core/read-only-shell.d.ts +1 -1
  225. package/dist/core/retired-keys.d.ts +16 -0
  226. package/dist/core/retired-keys.js +16 -4
  227. package/dist/core/rewrite-rejudge.d.ts +60 -0
  228. package/dist/core/rewrite-rejudge.js +36 -0
  229. package/dist/core/roles.d.ts +34 -3
  230. package/dist/core/roles.js +66 -53
  231. package/dist/core/rule-lane-reads.d.ts +20 -15
  232. package/dist/core/rule-lane-reads.js +45 -17
  233. package/dist/core/rule-offer-read.d.ts +53 -0
  234. package/dist/core/rule-offer-read.js +146 -0
  235. package/dist/core/rule-owner.d.ts +46 -0
  236. package/dist/core/rule-owner.js +29 -0
  237. package/dist/core/runner/advisor-instructions.d.ts +70 -0
  238. package/dist/core/runner/advisor-instructions.js +61 -0
  239. package/dist/core/runner/advisor-tool.d.ts +33 -0
  240. package/dist/core/runner/advisor-tool.js +112 -0
  241. package/dist/core/runner/announce-once-ledger.d.ts +18 -26
  242. package/dist/core/runner/announce-once-ledger.js +19 -46
  243. package/dist/core/runner/approval-feedback.js +6 -3
  244. package/dist/core/runner/assemble-result.d.ts +2 -1
  245. package/dist/core/runner/assemble-result.js +1 -1
  246. package/dist/core/runner/call-hook-stations.d.ts +3 -2
  247. package/dist/core/runner/caller-roster.d.ts +21 -2
  248. package/dist/core/runner/caller-roster.js +38 -0
  249. package/dist/core/runner/capability-withheld-seat.d.ts +5 -0
  250. package/dist/core/runner/capability-withheld-seat.js +24 -0
  251. package/dist/core/runner/classifier-ladder.d.ts +33 -13
  252. package/dist/core/runner/classifier-ladder.js +15 -14
  253. package/dist/core/runner/compaction-restate.d.ts +10 -6
  254. package/dist/core/runner/compaction-restate.js +9 -3
  255. package/dist/core/runner/construction-doors.js +3 -0
  256. package/dist/core/runner/contracts.d.ts +40 -33
  257. package/dist/core/runner/conversation-segment.d.ts +28 -0
  258. package/dist/core/runner/conversation-segment.js +42 -0
  259. package/dist/core/runner/cross-boundary-lane.d.ts +21 -16
  260. package/dist/core/runner/cross-boundary-lane.js +58 -34
  261. package/dist/core/runner/decide-continuation.d.ts +7 -4
  262. package/dist/core/runner/decide-continuation.js +2 -2
  263. package/dist/core/runner/denial-limit-arms.d.ts +4 -4
  264. package/dist/core/runner/denial-limit-arms.js +1 -1
  265. package/dist/core/runner/derived-route-fallback.d.ts +24 -24
  266. package/dist/core/runner/derived-route-fallback.js +9 -7
  267. package/dist/core/runner/file-history-request.d.ts +18 -0
  268. package/dist/core/runner/file-history-request.js +21 -13
  269. package/dist/core/runner/file-history-restore.d.ts +45 -0
  270. package/dist/core/runner/file-history-restore.js +46 -0
  271. package/dist/core/runner/gate-exit.d.ts +9 -3
  272. package/dist/core/runner/gate-exit.js +11 -5
  273. package/dist/core/runner/image.d.ts +7 -17
  274. package/dist/core/runner/image.js +9 -60
  275. package/dist/core/runner/initial-run-state.js +1 -1
  276. package/dist/core/runner/permission-rule-lanes.d.ts +6 -6
  277. package/dist/core/runner/permission-rule-lanes.js +38 -15
  278. package/dist/core/runner/person-path-rows.d.ts +12 -9
  279. package/dist/core/runner/person-path-rows.js +19 -8
  280. package/dist/core/runner/prepare-advisor.d.ts +63 -0
  281. package/dist/core/runner/prepare-advisor.js +77 -0
  282. package/dist/core/runner/prepare-ask-lane.d.ts +2 -2
  283. package/dist/core/runner/prepare-ask-lane.js +5 -2
  284. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -6
  285. package/dist/core/runner/prepare-caps-and-workflow.js +5 -4
  286. package/dist/core/runner/prepare-config-doors.d.ts +31 -38
  287. package/dist/core/runner/prepare-config-doors.js +57 -34
  288. package/dist/core/runner/prepare-defer-classify.d.ts +4 -3
  289. package/dist/core/runner/prepare-defer-classify.js +7 -8
  290. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
  291. package/dist/core/runner/prepare-delegation-surface.js +1 -1
  292. package/dist/core/runner/prepare-file-history.d.ts +48 -8
  293. package/dist/core/runner/prepare-file-history.js +41 -73
  294. package/dist/core/runner/prepare-gate-stations.d.ts +2 -5
  295. package/dist/core/runner/prepare-gate-stations.js +12 -8
  296. package/dist/core/runner/prepare-hands-readface.d.ts +2 -8
  297. package/dist/core/runner/prepare-hands-readface.js +15 -14
  298. package/dist/core/runner/prepare-inherited-gate.d.ts +11 -7
  299. package/dist/core/runner/prepare-inherited-gate.js +8 -6
  300. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  301. package/dist/core/runner/prepare-park-ask.js +12 -12
  302. package/dist/core/runner/prepare-policy-chain.d.ts +7 -4
  303. package/dist/core/runner/prepare-policy-chain.js +68 -81
  304. package/dist/core/runner/prepare-project-context.d.ts +5 -2
  305. package/dist/core/runner/prepare-project-context.js +6 -3
  306. package/dist/core/runner/prepare-prompt-assembly.d.ts +5 -7
  307. package/dist/core/runner/prepare-prompt-assembly.js +10 -8
  308. package/dist/core/runner/prepare-prompt-inputs.d.ts +11 -9
  309. package/dist/core/runner/prepare-prompt-inputs.js +4 -6
  310. package/dist/core/runner/prepare-protocol-tools.d.ts +4 -2
  311. package/dist/core/runner/prepare-protocol-tools.js +5 -4
  312. package/dist/core/runner/prepare-safety-scan.d.ts +9 -12
  313. package/dist/core/runner/prepare-safety-scan.js +8 -23
  314. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
  315. package/dist/core/runner/prepare-suspend-saga.js +5 -6
  316. package/dist/core/runner/prepare-task.d.ts +1 -1
  317. package/dist/core/runner/prepare-task.js +41 -35
  318. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +7 -4
  319. package/dist/core/runner/prepare-tool-disclosure-mount.js +24 -18
  320. package/dist/core/runner/prepare-turn-wiring.d.ts +0 -4
  321. package/dist/core/runner/prepare-turn-wiring.js +2 -7
  322. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -4
  323. package/dist/core/runner/prepare-wiring-manifest.js +3 -3
  324. package/dist/core/runner/resume-admission.js +1 -1
  325. package/dist/core/runner/resume-apply.js +11 -12
  326. package/dist/core/runner/resume-checkpoint-screen.js +3 -0
  327. package/dist/core/runner/resume-claim.d.ts +4 -2
  328. package/dist/core/runner/resume-claim.js +15 -8
  329. package/dist/core/runner/resume-policy-outcome.js +1 -1
  330. package/dist/core/runner/retired-runner-deps.js +2 -1
  331. package/dist/core/runner/rewind-files.d.ts +15 -0
  332. package/dist/core/runner/rewind-files.js +132 -0
  333. package/dist/core/runner/run-attachment-seats.js +2 -2
  334. package/dist/core/runner/run-brain-sinks.d.ts +4 -1
  335. package/dist/core/runner/run-brain-sinks.js +4 -2
  336. package/dist/core/runner/run-compaction-machinery.d.ts +1 -1
  337. package/dist/core/runner/run-compaction-machinery.js +2 -12
  338. package/dist/core/runner/run-git-lane.d.ts +1 -0
  339. package/dist/core/runner/run-harness-handlers.js +14 -7
  340. package/dist/core/runner/run-leg.d.ts +1 -1
  341. package/dist/core/runner/run-leg.js +43 -18
  342. package/dist/core/runner/run-settle-and-teardown.js +3 -2
  343. package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
  344. package/dist/core/runner/run-turn-boundary.d.ts +1 -0
  345. package/dist/core/runner/run-turn-boundary.js +15 -22
  346. package/dist/core/runner/runtask.d.ts +3 -0
  347. package/dist/core/runner/runtask.js +8 -9
  348. package/dist/core/runner/session-read-file-states.d.ts +15 -0
  349. package/dist/core/runner/session-read-file-states.js +28 -0
  350. package/dist/core/runner/session-rule-policy.d.ts +22 -21
  351. package/dist/core/runner/session-rule-policy.js +17 -31
  352. package/dist/core/runner/shell-env-withheld.d.ts +33 -0
  353. package/dist/core/runner/shell-env-withheld.js +51 -0
  354. package/dist/core/runner/side-call-account.d.ts +32 -8
  355. package/dist/core/runner/side-call-account.js +23 -1
  356. package/dist/core/runner/skills-seat.js +6 -2
  357. package/dist/core/runner/stream-settle-backstop.js +1 -1
  358. package/dist/core/runner/strict-output-schema.d.ts +1 -1
  359. package/dist/core/runner/sub-agent-deny-lists.js +4 -6
  360. package/dist/core/runner/synthetic-tools.d.ts +16 -13
  361. package/dist/core/runner/synthetic-tools.js +8 -3
  362. package/dist/core/runner/tool-disclosure.d.ts +10 -12
  363. package/dist/core/runner/tool-disclosure.js +3 -4
  364. package/dist/core/runner/tool-end-body.d.ts +1 -1
  365. package/dist/core/runner/tool-end-body.js +10 -9
  366. package/dist/core/runner/tool-face-overlay.d.ts +2 -1
  367. package/dist/core/runner/tool-face-overlay.js +17 -1
  368. package/dist/core/runner/tool-not-executed.d.ts +5 -0
  369. package/dist/core/runner/tool-not-executed.js +11 -0
  370. package/dist/core/runner/tool-occurrence.d.ts +8 -0
  371. package/dist/core/runner/tool-occurrence.js +7 -0
  372. package/dist/core/runner/tool-progress-lane.d.ts +2 -2
  373. package/dist/core/runner/tool-turn-repair-seat.d.ts +3 -2
  374. package/dist/core/runner/turn-attachments.d.ts +91 -80
  375. package/dist/core/runner/turn-attachments.js +62 -37
  376. package/dist/core/runner-deps.d.ts +42 -42
  377. package/dist/core/runtime-caps.d.ts +4 -4
  378. package/dist/core/safety-axis-vocab.d.ts +6 -4
  379. package/dist/core/safety-axis-vocab.js +6 -5
  380. package/dist/core/safety-merge-corpus.js +26 -2
  381. package/dist/core/same-plain-value.d.ts +40 -0
  382. package/dist/core/same-plain-value.js +49 -0
  383. package/dist/core/scheduler.d.ts +1 -1
  384. package/dist/core/secret-env.d.ts +26 -3
  385. package/dist/core/secret-env.js +44 -8
  386. package/dist/core/select-model.js +3 -3
  387. package/dist/core/sensitive-path-policy.d.ts +1 -1
  388. package/dist/core/sensitive-path-policy.js +1 -1
  389. package/dist/core/session-policy-store.d.ts +102 -35
  390. package/dist/core/session-policy-store.js +210 -48
  391. package/dist/core/session-reconcile.d.ts +2 -2
  392. package/dist/core/session-store.d.ts +106 -37
  393. package/dist/core/session-store.js +165 -91
  394. package/dist/core/session.d.ts +101 -57
  395. package/dist/core/session.js +53 -0
  396. package/dist/core/shared-memory/tools.js +3 -1
  397. package/dist/core/shared-memory/types.d.ts +1 -1
  398. package/dist/core/shell-read-silence.d.ts +11 -12
  399. package/dist/core/shell-read-silence.js +9 -5
  400. package/dist/core/shell-run-candidates.js +3 -1
  401. package/dist/core/shell-wrapper-projection.js +1 -1
  402. package/dist/core/shell-wrapper-table.d.ts +14 -7
  403. package/dist/core/shell-wrapper-table.js +5 -4
  404. package/dist/core/side-query.d.ts +10 -0
  405. package/dist/core/side-query.js +15 -1
  406. package/dist/core/skill-grant.d.ts +7 -2
  407. package/dist/core/skill-grant.js +5 -0
  408. package/dist/core/skills-directory.js +6 -2
  409. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +11 -1
  410. package/dist/core/store-contracts/checkpoint-store-contract.js +168 -3
  411. package/dist/core/store-contracts/file-history-store-contract.js +74 -4
  412. package/dist/core/store-contracts/peer-directory-contract.d.ts +7 -2
  413. package/dist/core/store-contracts/peer-directory-contract.js +14 -2
  414. package/dist/core/store-contracts/session-policy-store-contract.d.ts +7 -0
  415. package/dist/core/store-contracts/session-policy-store-contract.js +130 -0
  416. package/dist/core/store-contracts/session-repo-contract.js +176 -0
  417. package/dist/core/store-contracts/session-store-contract.d.ts +6 -8
  418. package/dist/core/store-contracts/session-store-contract.js +361 -51
  419. package/dist/core/store-contracts/workflow-journal-store-contract.js +26 -1
  420. package/dist/core/stub-env.d.ts +2 -0
  421. package/dist/core/stub-env.js +1 -0
  422. package/dist/core/surrogate-safe-slice.d.ts +3 -3
  423. package/dist/core/synthetic-read-verdict.js +3 -3
  424. package/dist/core/task-event.d.ts +42 -8
  425. package/dist/core/task-notification.d.ts +19 -15
  426. package/dist/core/task-registry-agent.d.ts +4 -4
  427. package/dist/core/task-registry-shared.d.ts +4 -4
  428. package/dist/core/task-registry-shared.js +1 -0
  429. package/dist/core/task-registry.d.ts +13 -4
  430. package/dist/core/task-registry.js +14 -2
  431. package/dist/core/task-result.d.ts +21 -3
  432. package/dist/core/task-spec.d.ts +69 -64
  433. package/dist/core/task-stream.d.ts +6 -6
  434. package/dist/core/task-tool-shape.d.ts +4 -4
  435. package/dist/core/tighten-task-spec.d.ts +1 -1
  436. package/dist/core/tighten-task-spec.js +2 -1
  437. package/dist/core/tool-catalog-entries.d.ts +36 -2
  438. package/dist/core/tool-catalog-entries.js +69 -43
  439. package/dist/core/tool-check-reading.d.ts +11 -0
  440. package/dist/core/tool-check-reading.js +11 -6
  441. package/dist/core/tool-conformance.js +1 -0
  442. package/dist/core/tool-errors.d.ts +2 -2
  443. package/dist/core/tool-face-reader.js +5 -3
  444. package/dist/core/tool-face.d.ts +21 -2
  445. package/dist/core/tool-face.js +1 -1
  446. package/dist/core/tool-model-gate.d.ts +5 -5
  447. package/dist/core/tool-name-aliases.d.ts +29 -8
  448. package/dist/core/tool-name-aliases.js +22 -4
  449. package/dist/core/tool-name-readings.d.ts +10 -43
  450. package/dist/core/tool-name-readings.js +2 -11
  451. package/dist/core/tool-policy.d.ts +85 -196
  452. package/dist/core/tool-policy.js +150 -175
  453. package/dist/core/tool-registry.d.ts +14 -7
  454. package/dist/core/tool-registry.js +17 -7
  455. package/dist/core/tool-result-store.js +5 -1
  456. package/dist/core/tool-roster.d.ts +57 -33
  457. package/dist/core/tool-roster.js +56 -68
  458. package/dist/core/tool-spec-members.js +8 -3
  459. package/dist/core/tool-spec.d.ts +53 -31
  460. package/dist/core/tools.js +51 -10
  461. package/dist/core/trace.d.ts +6 -2
  462. package/dist/core/types.d.ts +1 -1
  463. package/dist/core/untrusted-egress.d.ts +1 -1
  464. package/dist/core/untrusted-text.d.ts +1 -1
  465. package/dist/core/user-attachment.d.ts +8 -1
  466. package/dist/core/user-attachment.js +64 -4
  467. package/dist/core/warm-resume.d.ts +1 -1
  468. package/dist/core/wiring-manifest.d.ts +58 -0
  469. package/dist/core/wiring-manifest.js +14 -5
  470. package/dist/core/workflow-journal-store.d.ts +62 -0
  471. package/dist/core/workflow-journal-store.js +78 -2
  472. package/dist/core/workflow-run-store.d.ts +6 -0
  473. package/dist/core/workflow-run-store.js +2 -0
  474. package/dist/engine/compaction/compaction.d.ts +9 -9
  475. package/dist/engine/compaction/compaction.js +3 -2
  476. package/dist/engine/execution-env/node-execution-env.d.ts +26 -8
  477. package/dist/engine/execution-env/node-execution-env.js +54 -24
  478. package/dist/engine/harness/agent-harness.d.ts +39 -9
  479. package/dist/engine/harness/agent-harness.js +87 -33
  480. package/dist/engine/harness/types.d.ts +58 -9
  481. package/dist/engine/harness/types.js +12 -1
  482. package/dist/engine/llm/types.d.ts +36 -19
  483. package/dist/engine/llm/validation.d.ts +4 -1
  484. package/dist/engine/llm/validation.js +13 -1
  485. package/dist/engine/loop/agent-loop.d.ts +23 -1
  486. package/dist/engine/loop/agent-loop.js +26 -34
  487. package/dist/engine/loop/types.d.ts +31 -19
  488. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  489. package/dist/engine/session/memory-repo.d.ts +18 -12
  490. package/dist/engine/session/memory-repo.js +24 -9
  491. package/dist/engine/session/parent-chain-guard.d.ts +3 -2
  492. package/dist/engine/session/parent-chain-guard.js +1 -1
  493. package/dist/engine/session/repo-utils.d.ts +32 -3
  494. package/dist/engine/session/repo-utils.js +50 -5
  495. package/dist/index.d.ts +47 -23
  496. package/dist/index.js +35 -17
  497. package/dist/internal/harness-types.d.ts +1 -1
  498. package/dist/internal/harness.d.ts +3 -3
  499. package/dist/internal/harness.js +2 -2
  500. package/dist/internal/llm.d.ts +2 -2
  501. package/dist/internal/llm.js +1 -1
  502. package/dist/orchestration/goal.js +2 -2
  503. package/dist/orchestration/run-workflow-tool.d.ts +8 -6
  504. package/dist/orchestration/run-workflow-tool.js +10 -6
  505. package/dist/orchestration/workflow-size-guideline.d.ts +4 -4
  506. package/dist/orchestration/workflow-types.d.ts +18 -0
  507. package/dist/orchestration/workflow-types.js +6 -0
  508. package/dist/orchestration/workflow.d.ts +5 -13
  509. package/dist/orchestration/workflow.js +57 -49
  510. package/dist/prompt-assembly/artifact.d.ts +2 -2
  511. package/dist/prompt-assembly/artifact.js +5 -3
  512. package/dist/prompt-assembly/assemble.d.ts +3 -2
  513. package/dist/prompt-assembly/assemble.js +1 -1
  514. package/dist/prompt-assembly/catalog/schema.d.ts +43 -12
  515. package/dist/prompt-assembly/catalog/schema.js +34 -2
  516. package/dist/prompt-assembly/catalog/sections.js +0 -1
  517. package/dist/prompt-assembly/composer.d.ts +3 -1
  518. package/dist/prompt-assembly/composer.js +1 -1
  519. package/dist/prompt-assembly/event-registry.js +2 -0
  520. package/dist/prompt-assembly/overlay.js +3 -3
  521. package/dist/prompt-assembly/renderers.d.ts +1 -1
  522. package/dist/prompt-assembly/renderers.js +0 -3
  523. package/dist/prompt-assembly/role-base.d.ts +15 -0
  524. package/dist/prompt-assembly/role-base.js +9 -0
  525. package/dist/prompt-assembly/select.d.ts +39 -14
  526. package/dist/prompt-assembly/select.js +47 -16
  527. package/dist/prompt-assembly/sugar.d.ts +17 -11
  528. package/dist/prompt-assembly/sugar.js +8 -3
  529. package/dist/prompt-assembly/tool-catalog.d.ts +39 -2
  530. package/dist/prompt-assembly/tool-catalog.js +117 -4
  531. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -10
  532. package/dist/prompt-assembly/turn-snapshot.js +0 -3
  533. package/dist/prompt-assembly/types.d.ts +0 -7
  534. package/dist/prompts/default.d.ts +14 -14
  535. package/dist/scenarios/full-body.d.ts +15 -15
  536. package/dist/scenarios/full-body.js +4 -3
  537. package/dist/shell/lex.js +10 -1
  538. package/dist/stores/file/adoption/adopt.d.ts +4 -3
  539. package/dist/stores/file/adoption/adopt.js +27 -33
  540. package/dist/stores/file/adoption/marker.d.ts +6 -13
  541. package/dist/stores/file/adoption/marker.js +9 -17
  542. package/dist/stores/file/checkpoint-store.d.ts +24 -0
  543. package/dist/stores/file/checkpoint-store.js +45 -7
  544. package/dist/stores/file/file-history-store.d.ts +20 -19
  545. package/dist/stores/file/file-history-store.js +157 -141
  546. package/dist/stores/file/fs-atomic.d.ts +15 -0
  547. package/dist/stores/file/fs-atomic.js +89 -14
  548. package/dist/stores/file/index.d.ts +1 -1
  549. package/dist/stores/file/index.js +2 -1
  550. package/dist/stores/file/mailbox-store.d.ts +3 -0
  551. package/dist/stores/file/mailbox-store.js +3 -1
  552. package/dist/stores/file/permission-rule-adopt.d.ts +1 -1
  553. package/dist/stores/file/permission-rule-adopt.js +18 -18
  554. package/dist/stores/file/permission-rule-store.d.ts +18 -2
  555. package/dist/stores/file/permission-rule-store.js +31 -18
  556. package/dist/stores/file/session-policy-store.d.ts +3 -3
  557. package/dist/stores/file/session-store.d.ts +54 -16
  558. package/dist/stores/file/session-store.js +114 -85
  559. package/dist/stores/file/workflow-journal-store.d.ts +10 -4
  560. package/dist/stores/file/workflow-journal-store.js +28 -13
  561. package/dist/tools/artifact/artifact-text.d.ts +1 -1
  562. package/dist/tools/artifact/artifact-tool.js +5 -3
  563. package/dist/tools/fs/bash-program-position.d.ts +19 -2
  564. package/dist/tools/fs/bash-program-position.js +56 -9
  565. package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -3
  566. package/dist/tools/fs/fs-bash.d.ts +2 -2
  567. package/dist/tools/fs/fs-bash.js +8 -6
  568. package/dist/tools/fs/fs-glob.d.ts +86 -0
  569. package/dist/tools/fs/fs-glob.js +226 -0
  570. package/dist/tools/fs/fs-read.js +14 -14
  571. package/dist/tools/fs/fs-search-tools.js +7 -7
  572. package/dist/tools/fs/fs-shared.d.ts +8 -8
  573. package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
  574. package/dist/tools/fs/notebook.d.ts +2 -2
  575. package/dist/tools/fs/pattern-kill-guard.d.ts +22 -0
  576. package/dist/tools/fs/pattern-kill-guard.js +397 -0
  577. package/dist/tools/fs/pdf.d.ts +9 -6
  578. package/dist/tools/fs/pdf.js +3 -3
  579. package/dist/tools/fs/program-read-model.d.ts +57 -7
  580. package/dist/tools/fs/program-read-model.js +11 -1
  581. package/dist/tools/fs/program-read-model.rows.js +78 -21
  582. package/dist/tools/fs/read-deny-home.js +3 -2
  583. package/dist/tools/fs/read-deny.d.ts +29 -7
  584. package/dist/tools/fs/read-deny.js +63 -22
  585. package/dist/tools/fs/removal-targets.d.ts +18 -8
  586. package/dist/tools/fs/removal-targets.js +47 -20
  587. package/dist/tools/fs/safety.d.ts +9 -9
  588. package/dist/tools/fs/search.d.ts +16 -6
  589. package/dist/tools/fs/search.js +20 -26
  590. package/dist/tools/fs/shell-read-argv.js +89 -12
  591. package/dist/tools/fs/shell-read-bindings.d.ts +31 -0
  592. package/dist/tools/fs/shell-read-bindings.js +41 -0
  593. package/dist/tools/fs/shell-read-check.js +12 -20
  594. package/dist/tools/fs/shell-read-environment.d.ts +5 -1
  595. package/dist/tools/fs/shell-read-environment.js +3 -2
  596. package/dist/tools/fs/shell-read-establish.d.ts +15 -0
  597. package/dist/tools/fs/shell-read-establish.js +19 -20
  598. package/dist/tools/fs/shell-read-judge.d.ts +4 -0
  599. package/dist/tools/fs/shell-read-judge.js +5 -2
  600. package/dist/tools/fs/shell-read-plan.js +24 -16
  601. package/dist/tools/fs/shell-read-start.d.ts +6 -1
  602. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -1
  603. package/dist/tools/fs/shell-read-write-targets.js +2 -3
  604. package/dist/tools/fs/shell-reentry-text.d.ts +9 -0
  605. package/dist/tools/fs/shell-reentry-text.js +40 -2
  606. package/dist/tools/loop-tick.d.ts +2 -2
  607. package/dist/tools/scheduler-tools.js +5 -1
  608. package/dist/tools/task-list.d.ts +7 -7
  609. package/dist/tools/task-list.js +5 -1
  610. package/dist/tools/todo.js +65 -64
  611. package/dist/tools/web.d.ts +20 -15
  612. package/dist/tools/web.js +55 -69
  613. package/dist/tools/worktree.d.ts +3 -3
  614. package/dist/tools/worktree.js +3 -1
  615. package/package.json +1 -1
  616. package/test/export-surface.snapshot.json +5894 -5638
  617. package/wire-changes/core@7.55.0.json +3318 -0
  618. package/wire-changes/core@7.56.0.json +3891 -0
  619. package/wire-changes/mods-kernel@7.56.0.json +303 -0
  620. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +0 -39
  621. package/dist/core/store-contracts/session-store-incarnation-contract.js +0 -95
@@ -1,17 +1,21 @@
1
1
  import { homedir } from "node:os";
2
2
  import { shellWordsOf } from "../shell/shell-words.js";
3
- import { parsePermissionRule } from "./permission-rule-syntax.js";
3
+ import { lexShell } from "../shell/lex.js";
4
+ import { readProgramPosition } from "../tools/fs/bash-program-position.js";
5
+ import { parsePermissionRule, ruleSpellingOf } from "./permission-rule-syntax.js";
4
6
  import { compileReadDeny } from "../tools/fs/read-deny.js";
5
7
  import { carriesShellRedirection, parseLeadingCommandName, splitShellCompoundSegments } from "../tools/fs/bash-readonly-classifier.js";
6
8
  import { INLINE_TRUNCATION_MARK, inlineUntrusted } from "./untrusted-text.js";
7
- import { isShellFaceToolName, pathTargetOf, protectivePathTargetOf } from "./tool-registry.js";
9
+ import { catalogRuleFaceOf, hostTargetOf, isShellFaceToolName, pathTargetOf, protectivePathTargetOf } from "./tool-registry.js";
10
+ import { HOST_UNREADABLE_SENTENCE, hostPatternAdmits, readHostPattern, spelledHostPattern } from "./host-target.js";
8
11
  import { heldValues } from "./tool-name-readings.js";
9
12
  import { carriesDotSegment, isAbsoluteForFamily, isIdentityDir, isPathIdentity, isShellRootedSpellingUnmapped, isUncIdentity, pathFamilyOf, pathIdentityOf } from "../tools/fs/safety.js";
10
13
  import { bindPathRules, judgeRuleRows, rowsWhereTheyLead } from "./path-rule-binding.js";
11
14
  import { readShellCommand } from "./shell-run-candidates.js";
12
15
  import { lineDescriptorsOfText, readOnlyLineObjection, readOnlyShellVerdict } from "./read-only-shell.js";
13
16
  import { ARTIFACT_ACTIONS } from "./artifact-host.js";
14
- import { ARTIFACT_TOOL_NAME } from "./tool-catalog-entries.js";
17
+ import { ARTIFACT_TOOL_NAME, TOOL_CATALOG_ENTRIES } from "./tool-catalog-entries.js";
18
+ import { paramRuleReaches } from "./param-rule-reach.js";
15
19
  export const RULE_BEHAVIORS = Object.freeze(["deny", "ask", "allow"]);
16
20
  const RULE_BEHAVIOR_SET = new Set(RULE_BEHAVIORS);
17
21
  export function isRuleBehavior(v) {
@@ -43,64 +47,121 @@ export function callPathFaceOf(call) {
43
47
  export function faceRowsSpeakFor(face) {
44
48
  return face !== undefined && Object.values(PATH_RULE_FACES).some((columns) => columns.deny?.includes(face) === true || columns.ask?.includes(face) === true);
45
49
  }
50
+ function catalogVocabularyOf(tool) {
51
+ const params = catalogRuleFaceOf(tool)?.params;
52
+ if (params === undefined)
53
+ throw new Error(`permission rules: the catalog declares no rule vocabulary for ${JSON.stringify(tool)}`);
54
+ return params;
55
+ }
56
+ const DELEGATION_RULE_TOOL = (() => {
57
+ const row = TOOL_CATALOG_ENTRIES.find((e) => e.id === "agent");
58
+ if (row === undefined)
59
+ throw new Error("permission rules: the catalog carries no delegation tool row (`agent`)");
60
+ return row.name;
61
+ })();
46
62
  export const NAME_RULE_FACES = Object.freeze({
47
- Skill: Object.freeze({ param: "skill", params: Object.freeze(["skill", "args"]) }),
63
+ Skill: Object.freeze({ param: "skill", params: Object.freeze(catalogVocabularyOf("Skill")) }),
48
64
  [ARTIFACT_TOOL_NAME]: Object.freeze({ param: "action", params: Object.freeze(["action"]), words: ARTIFACT_ACTIONS }),
65
+ [DELEGATION_RULE_TOOL]: Object.freeze({ param: "subagent_type", params: Object.freeze(["subagent_type"]), values: "mounted" }),
49
66
  });
67
+ function nameRuleFaceOf(tool) {
68
+ return Object.hasOwn(NAME_RULE_FACES, tool) ? NAME_RULE_FACES[tool] : undefined;
69
+ }
50
70
  function keyedValueParamOf(tool) {
51
- const face = Object.hasOwn(NAME_RULE_FACES, tool) ? NAME_RULE_FACES[tool] : undefined;
71
+ const face = nameRuleFaceOf(tool);
52
72
  return face?.words !== undefined ? face.param : undefined;
53
73
  }
54
- export function keyedValueAbsentReason(tool) {
55
- const key = keyedValueParamOf(tool);
56
- return key === undefined ? undefined : `the ${tool} call names no ${key} as the tool reads it, so the ${key} it runs is not one its arguments show — a rule by ${key} cannot be read against it`;
74
+ export function normalizeSubagentType(value) {
75
+ return value.normalize("NFKC").toLowerCase().replace(/[\p{White_Space}\p{Pd}_]+/gu, "");
76
+ }
77
+ export function canonicalNameValue(value, values) {
78
+ if (values.includes(value))
79
+ return { kind: "entry", entry: value };
80
+ const key = normalizeSubagentType(value);
81
+ const hits = key === "" ? [] : [...new Set(values.filter((v) => normalizeSubagentType(v) === key))];
82
+ if (hits.length === 1)
83
+ return { kind: "entry", entry: hits[0] };
84
+ return hits.length > 1 ? { kind: "ambiguous", entries: hits } : { kind: "unknown" };
85
+ }
86
+ export function nameValueAbsentReason(tool) {
87
+ const face = nameRuleFaceOf(tool);
88
+ if (face === undefined || (face.words === undefined && face.values === undefined))
89
+ return undefined;
90
+ const key = face.param;
91
+ return `the ${tool} call names no ${key} as the tool reads it, so the ${key} it runs is not one its arguments show — a rule by ${key} cannot be read against it`;
57
92
  }
58
93
  export function nameGrammarReads(tool, content) {
59
94
  if (ruleToolGrammarOf(tool) !== "name")
60
95
  return false;
61
- const key = keyedValueParamOf(tool);
62
- if (key === undefined)
63
- return true;
96
+ const face = nameRuleFaceOf(tool);
97
+ const key = contentKeyOf(content);
98
+ if (face.words !== undefined)
99
+ return key === face.param;
100
+ return key === undefined || face.params.includes(key) || !(catalogRuleFaceOf(tool)?.params ?? []).includes(key);
101
+ }
102
+ export function mountedValueArmOf(text, tool, behavior) {
103
+ if (behavior === "allow" || nameRuleFaceOf(tool)?.values !== "mounted")
104
+ return undefined;
105
+ const parsed = parseRuleText(text, behavior);
106
+ return "rule" in parsed && parsed.rule.match === "name" ? parsed.rule : undefined;
107
+ }
108
+ export function mountedOnlyArgumentOf(tool, content, mountedParams) {
109
+ if (nameRuleFaceOf(tool)?.values !== "mounted" || mountedParams === undefined)
110
+ return undefined;
111
+ const key = contentKeyOf(content);
112
+ return key !== undefined && mountedParams.includes(key) && !(catalogRuleFaceOf(tool)?.params ?? []).includes(key) ? key : undefined;
113
+ }
114
+ export function mountedArgumentArmOf(rule) {
115
+ const face = nameRuleFaceOf(rule.tool);
116
+ if (rule.behavior === "allow" || rule.match !== "name" || face?.values !== "mounted")
117
+ return undefined;
118
+ const key = contentKeyOf(rule.command);
119
+ if (key === undefined || face.params.includes(key) || !(catalogRuleFaceOf(rule.tool)?.params ?? []).includes(key))
120
+ return undefined;
121
+ const pattern = rule.command.slice(rule.command.indexOf(":") + 1).trim();
122
+ return pattern === "" ? undefined : { param: key, pattern };
123
+ }
124
+ function contentKeyOf(content) {
64
125
  const colon = content.indexOf(":");
65
- return colon > 0 && content.slice(0, colon).trim() === key;
126
+ return colon > 0 ? content.slice(0, colon).trim() : undefined;
66
127
  }
67
- export const DOMAIN_RULE_FACES = Object.freeze({
68
- WebFetch: Object.freeze({ param: "url" }),
69
- });
128
+ export const DOMAIN_RULE_MARKER = "domain:";
70
129
  export function ruleToolGrammarOf(toolName) {
71
130
  if (toolName === COMMAND_RULE_TOOL)
72
131
  return "command";
73
132
  if (Object.hasOwn(NAME_RULE_FACES, toolName))
74
133
  return "name";
75
- if (Object.hasOwn(DOMAIN_RULE_FACES, toolName))
134
+ if (hostTargetOf(toolName) !== undefined)
76
135
  return "domain";
77
136
  return pathTargetOf(toolName) !== undefined ? "path" : undefined;
78
137
  }
79
- export function domainRuleSubjectOf(toolName, args) {
80
- const face = Object.hasOwn(DOMAIN_RULE_FACES, toolName) ? DOMAIN_RULE_FACES[toolName] : undefined;
81
- if (face === undefined || typeof args !== "object" || args === null || Array.isArray(args) || !Object.hasOwn(args, face.param))
82
- return undefined;
83
- const value = args[face.param];
84
- if (typeof value !== "string")
85
- return undefined;
86
- let url;
87
- try {
88
- url = new URL(value);
89
- }
90
- catch {
138
+ export function shellRuleParamOf(tool, content) {
139
+ const colon = content.indexOf(":");
140
+ if (colon <= 0)
91
141
  return undefined;
142
+ const param = content.slice(0, colon).trim();
143
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(param) && (catalogRuleFaceOf(tool)?.params ?? []).includes(param) ? param : undefined;
144
+ }
145
+ export function ruleLaneOf(tool, content) {
146
+ if (isShellFaceToolName(tool))
147
+ return shellRuleParamOf(tool, content) !== undefined && !(tool === COMMAND_RULE_TOOL && content.endsWith(":*")) ? "param" : "command";
148
+ switch (ruleToolGrammarOf(tool)) {
149
+ case "name":
150
+ return nameGrammarReads(tool, content) ? "name" : "param";
151
+ case "path":
152
+ return content.indexOf(":") <= 0 ? "path" : "param";
153
+ case "domain":
154
+ return content.startsWith(DOMAIN_RULE_MARKER) ? "domain" : "param";
155
+ default:
156
+ return "param";
92
157
  }
93
- if ((url.protocol !== "http:" && url.protocol !== "https:") || url.username !== "" || url.password !== "")
94
- return undefined;
95
- const host = url.hostname.replace(/\.+$/, "");
96
- return host === "" ? undefined : host;
97
158
  }
98
159
  export function nameRuleSubjectOf(toolName, args) {
99
160
  const face = Object.hasOwn(NAME_RULE_FACES, toolName) ? NAME_RULE_FACES[toolName] : undefined;
100
161
  if (face === undefined || typeof args !== "object" || args === null || Array.isArray(args) || !Object.hasOwn(args, face.param))
101
162
  return undefined;
102
163
  const value = args[face.param];
103
- return typeof value === "string" ? value : undefined;
164
+ return typeof value === "string" && value !== "" ? value : undefined;
104
165
  }
105
166
  export function laneToolNamesOf(req) {
106
167
  const words = heldValues("policy.persistedGrammar", { get: ruleWordOf }, req);
@@ -111,9 +172,9 @@ function ruleWordOf(name) {
111
172
  return COMMAND_RULE_TOOL;
112
173
  return ruleToolGrammarOf(name) !== undefined ? name : undefined;
113
174
  }
114
- export const PERSISTED_RULE_MATCHES = Object.freeze(["exact", "prefix", "wildcard", "subpath", "path", "name", "name_prefix", "host", "subdomain"]);
175
+ export const PERSISTED_RULE_MATCHES = Object.freeze(["exact", "prefix", "wildcard", "pattern", "subpath", "path", "name", "name_prefix", "host", "subdomain"]);
115
176
  function isCommandMatch(match) {
116
- return match === "exact" || match === "prefix" || match === "wildcard";
177
+ return match === "exact" || match === "prefix" || match === "wildcard" || match === "pattern";
117
178
  }
118
179
  export const RULE_ADD_ORIGINS = Object.freeze(["user", "imported-cc", "starter"]);
119
180
  export const MAX_RULE_TEXT_CHARS = 512;
@@ -282,10 +343,32 @@ const CONTROL_CHARS_GLOBAL_RE = new RegExp(CONTROL_CHARS_RE.source, "gu");
282
343
  const RULE_SPELLING_REJECT_BYTES = "<>";
283
344
  const RULE_SPELLING_REJECT_RE = new RegExp(`[${RULE_SPELLING_REJECT_BYTES}]`);
284
345
  function valueRuleFormsSentence() {
285
- return Object.entries(NAME_RULE_FACES)
286
- .map(([tool, face]) => (face.words !== undefined ? `${tool}(${face.param}:<${face.param}>) rules` : `${tool}(<${face.param}>) rules`))
346
+ return Object.keys(NAME_RULE_FACES)
347
+ .map((tool) => `${nameRuleFormOf(tool)} rules`)
287
348
  .join(", ");
288
349
  }
350
+ function nameRuleFormOf(tool) {
351
+ return formatRuleText(`<${nameRuleFaceOf(tool).param}>`, "name", tool);
352
+ }
353
+ export function ruleShapeOf(call, behavior) {
354
+ if (isShellFaceToolName(call.toolName))
355
+ return formatRuleText("<command>", "exact", COMMAND_RULE_TOOL);
356
+ switch (ruleToolGrammarOf(call.toolName)) {
357
+ case "name":
358
+ return nameRuleFormOf(call.toolName);
359
+ case "domain":
360
+ return formatRuleText("<host>", "host", call.toolName);
361
+ case "path": {
362
+ if (behavior !== "allow")
363
+ return formatRuleText("<path>", "path", call.toolName);
364
+ const face = callPathFaceOf(call) ?? (pathTargetOf(call.toolName)?.access === "read" ? "read" : "write");
365
+ const word = Object.keys(PATH_RULE_FACES).find((tool) => PATH_RULE_FACES[tool].allow?.includes(face) === true) ?? call.toolName;
366
+ return formatRuleText("<path>", "path", word);
367
+ }
368
+ default:
369
+ return undefined;
370
+ }
371
+ }
289
372
  const DISCLOSED_RULE_TEXT_MAX_CHARS = 120;
290
373
  export function escapeForDisclosure(value) {
291
374
  let text;
@@ -319,6 +402,19 @@ export function renderUntrustedCommandText(text, maxLen = DISCLOSED_RULE_TEXT_MA
319
402
  export function hasUnrenderableCharacters(text) {
320
403
  return CONTROL_CHARS_RE.test(text);
321
404
  }
405
+ export function ruleContentOf(parsed) {
406
+ const content = parsed.ruleContent;
407
+ return content === undefined || (parsed.toolName === COMMAND_RULE_TOOL && foldSpacing(content) === "*") ? undefined : content;
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
+ }
322
418
  export function parseRuleText(text, behavior) {
323
419
  if (!isRuleBehavior(behavior)) {
324
420
  return reject("invalid.grammar", `rule "${text}" was parsed under behavior ${escapeForDisclosure(behavior)}, which is not one of ${RULE_BEHAVIORS.join("/")}`);
@@ -330,8 +426,19 @@ export function parseRuleText(text, behavior) {
330
426
  if (CONTROL_CHARS_RE.test(text)) {
331
427
  return reject("invalid.control_chars", "rule text contains control characters (C0/C1, tab excepted), which cannot be part of a readable command");
332
428
  }
333
- const parsed = parsePermissionRule(text);
334
- if (parsed.ruleContent === undefined) {
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
+ }
440
+ const ruleContent = ruleContentOf(parsed);
441
+ if (ruleContent === undefined) {
335
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`);
336
443
  }
337
444
  if (parsed.toolName !== COMMAND_RULE_TOOL) {
@@ -339,16 +446,16 @@ export function parseRuleText(text, behavior) {
339
446
  if (grammar === undefined) {
340
447
  return reject("unsupported.tool", `only Bash command rules, rules on path-targeting tools (Read, Edit, Write, …), ${valueRuleFormsSentence()} and WebFetch domain rules have a rule form in this version (got "${parsed.toolName}")`);
341
448
  }
342
- if (!tighten && RULE_SPELLING_REJECT_RE.test(parsed.ruleContent)) {
449
+ if (!tighten && RULE_SPELLING_REJECT_RE.test(ruleContent)) {
343
450
  return reject("invalid.grammar", `"${text}" carries a shell redirection byte (${[...RULE_SPELLING_REJECT_BYTES].map((b) => `\`${b}\``).join(" or ")}) — no path, name or host allow spells one, and the grant would not read as what it looks like`);
344
451
  }
345
452
  if (grammar === "name")
346
- return parseNameRuleContent(text, parsed.toolName, parsed.ruleContent, behavior);
453
+ return parseNameRuleContent(text, parsed.toolName, ruleContent, behavior);
347
454
  if (grammar === "domain")
348
- return parseDomainRuleContent(text, parsed.toolName, parsed.ruleContent, behavior);
349
- return parsePathRuleContent(text, parsed.toolName, parsed.ruleContent, behavior);
455
+ return parseDomainRuleContent(text, parsed.toolName, ruleContent, behavior);
456
+ return parsePathRuleContent(text, parsed.toolName, ruleContent, behavior);
350
457
  }
351
- const content = parsed.ruleContent;
458
+ const content = foldSpacing(ruleContent) ?? ruleContent;
352
459
  const prefixBody = /^(.+):\*$/.exec(content)?.[1];
353
460
  let match;
354
461
  let body;
@@ -366,14 +473,18 @@ export function parseRuleText(text, behavior) {
366
473
  body = wildcardBody;
367
474
  }
368
475
  else if (hasUnescapedStar(content)) {
369
- return reject("unsupported.wildcard", `a \`*\` before the end of the pattern matches anything at that position, which has no rule form in this version — only the trailing \`<command> *\` form does ("${text}")`);
476
+ match = "pattern";
477
+ body = content;
370
478
  }
371
479
  else {
372
480
  match = "exact";
373
481
  body = content;
374
482
  }
375
483
  }
376
- const shape = ruleLaneShapeOf(body, MATCH_READING);
484
+ if (match === "pattern" && !tighten && hasUnescapedStar(body.trim().split(/[ \t]+/)[0] ?? "")) {
485
+ return reject("unsupported.wildcard", `a \`*\` in the first word of "${text}" leaves open which program runs — an allow names the program it grants (a deny or an ask may leave it open)`);
486
+ }
487
+ const shape = ruleLaneShapeOf(match === "pattern" ? globFloorText(body) : body, MATCH_READING);
377
488
  if ("reject" in shape) {
378
489
  if (body.trim() === "" || shape.reject === "empty command")
379
490
  return reject("invalid.empty_command", `rule "${text}" names no command`);
@@ -381,14 +492,16 @@ export function parseRuleText(text, behavior) {
381
492
  return reject("invalid.unbalanced_quotes", `rule "${text}" has unbalanced quoting, so the command it names is not determinable`);
382
493
  return reject("invalid.not_simple_command", `rule "${text}" is not a command this lane can name (${shape.reject})`);
383
494
  }
384
- if (match === "wildcard" && shape.segments.length > 1) {
385
- return reject("unsupported.wildcard", `a \`<chain> *\` pattern names more than one command, and the trailing-star form has no chain reading — the compound-prefix form spells one with the \`:*\` marker ("${text}")`);
495
+ if ((match === "wildcard" || match === "pattern") && shape.segments.length > 1) {
496
+ return reject("unsupported.wildcard", match === "pattern"
497
+ ? `a \`*\` pattern names one command, and "${text}" names more than one — a compound is covered segment by segment, and the compound-prefix form spells a chain with the \`:*\` marker`
498
+ : `a \`<chain> *\` pattern names more than one command, and the trailing-star form has no chain reading — the compound-prefix form spells one with the \`:*\` marker ("${text}")`);
386
499
  }
387
500
  const head = commandBasename(shape.names[shape.names.length - 1] ?? "");
388
- if ((match === "prefix" || match === "wildcard") && BARE_INTERPRETER_NAMES.has(head) && !tighten) {
501
+ if (match !== "exact" && BARE_INTERPRETER_NAMES.has(head) && !tighten) {
389
502
  return reject("invalid.bare_interpreter_prefix", shape.segments.length > 1
390
503
  ? `prefix rule "${text}" leaves its final segment open-ended under the interpreter "${head}" — such a rule authorizes running arbitrary programs, which one approval click cannot be read as having granted (an exact rule naming the whole command line is accepted)`
391
- : `prefix rule "${text}" is headed by the interpreter "${head}" — such a rule authorizes running arbitrary programs, which one approval click cannot be read as having granted (an exact rule naming the whole command line is accepted)`);
504
+ : `${match === "pattern" ? "pattern" : "prefix"} rule "${text}" is headed by the interpreter "${head}" — such a rule authorizes running arbitrary programs, which one approval click cannot be read as having granted (an exact rule naming the whole command line is accepted)`);
392
505
  }
393
506
  const command = foldSpacing(body);
394
507
  if (command === undefined) {
@@ -404,13 +517,15 @@ export function formatRuleText(command, match, tool) {
404
517
  if (match === "name")
405
518
  return `${tool}(${keyedValueParamOf(tool) !== undefined ? `${keyedValueParamOf(tool)}:` : ""}${command})`;
406
519
  if (match === "host" || match === "subdomain")
407
- return `${tool}(domain:${match === "subdomain" ? "*." : ""}${command})`;
520
+ return `${tool}(domain:${spelledHostPattern({ match, host: command })})`;
408
521
  return `${tool}(${command}${match === "prefix" || match === "name_prefix" ? ":*" : match === "wildcard" ? " *" : ""})`;
409
522
  }
410
523
  function parseNameRuleContent(text, tool, content, behavior, verifyCanonical = true) {
411
524
  const face = NAME_RULE_FACES[tool];
412
525
  if (face?.words !== undefined)
413
526
  return parseKeyedValueRuleContent(text, tool, content, behavior, { param: face.param, words: face.words });
527
+ if (face?.values === "mounted")
528
+ return parseMountedValueRuleContent(text, tool, content, behavior, face);
414
529
  const bare = content.startsWith("/") ? content.slice(1) : content;
415
530
  const colon = bare.indexOf(":");
416
531
  if (face !== undefined && colon > 0 && face.params.includes(bare.slice(0, colon).trim())) {
@@ -457,6 +572,28 @@ function parseKeyedValueRuleContent(text, tool, content, behavior, face) {
457
572
  }
458
573
  return { rule: { behavior, rule: formatRuleText(word, "name", tool), tool, match: "name", command: word } };
459
574
  }
575
+ function parseMountedValueRuleContent(text, tool, content, behavior, face) {
576
+ const form = nameRuleFormOf(tool);
577
+ const key = contentKeyOf(content);
578
+ if (key !== undefined && face.params.includes(key)) {
579
+ return reject("invalid.grammar", `"${text}" is the parameter spelling, which a ${tool} rule does not have — a ${tool} rule names the ${face.param} itself: ${form}`);
580
+ }
581
+ if (behavior === "allow" && key !== undefined && (catalogRuleFaceOf(tool)?.params ?? []).includes(key)) {
582
+ return reject("invalid.grammar", `"${text}" is a rule on the ${key} argument, which an allow entry has no form for — an allow reads one way, so it does not grant the ${face.param} "${content}" either; a ${tool} allow names the ${face.param}: ${form}`);
583
+ }
584
+ if (content === "" || content.trim() !== content) {
585
+ return reject("invalid.grammar", `the ${face.param} in "${text}" is empty or carries surrounding whitespace — a ${tool} rule names the ${face.param} exactly as it is spelled, so the spelling must be the one meant`);
586
+ }
587
+ if (content.includes("*")) {
588
+ return reject("unsupported.wildcard", `a \`*\` in the ${face.param} of "${text}" has no rule form — a ${tool} rule names one ${face.param} (${form}); the whole tool is the bare ${tool} entry`);
589
+ }
590
+ const canonical = formatRuleText(content, "name", tool);
591
+ const back = parsePermissionRule(canonical);
592
+ if (back.toolName !== tool || back.ruleContent !== content) {
593
+ return reject("invalid.grammar", `the ${face.param} in "${text}" has no spelling the rule syntax reads back (a backslash or an unbalanced parenthesis) — no ${tool} rule can name it`);
594
+ }
595
+ return { rule: { behavior, rule: canonical, tool, match: "name", command: content } };
596
+ }
460
597
  export function nameRuleAdmits(rule, name) {
461
598
  if (rule.match === "name")
462
599
  return name === rule.command;
@@ -464,55 +601,50 @@ export function nameRuleAdmits(rule, name) {
464
601
  return name.startsWith(rule.command);
465
602
  return false;
466
603
  }
604
+ export function nameRuleReach(rule, name, values) {
605
+ const face = nameRuleFaceOf(rule.tool);
606
+ if (face?.values !== "mounted")
607
+ return nameRuleAdmits(rule, name) ? "reached" : "not_reached";
608
+ if (rule.match !== "name")
609
+ return "not_reached";
610
+ if (values === undefined)
611
+ return "unread";
612
+ const operand = canonicalNameValue(rule.command, values);
613
+ const called = canonicalNameValue(name, values);
614
+ if (operand.kind === "ambiguous")
615
+ return rule.behavior !== "allow" && called.kind === "entry" && operand.entries.includes(called.entry) ? "reached" : "not_reached";
616
+ if (operand.kind === "entry" && called.kind === "entry")
617
+ return operand.entry === called.entry ? "reached" : "not_reached";
618
+ return name === rule.command ? "reached" : "not_reached";
619
+ }
620
+ export function nameValuesUnreadReason(tool) {
621
+ const face = nameRuleFaceOf(tool);
622
+ return `the ${tool} tool this call reaches declares no ${face?.param ?? "value"} values it offers, so which ${face?.param ?? "value"} the call runs cannot be read against a ${tool} rule`;
623
+ }
467
624
  function parseDomainRuleContent(text, tool, content, behavior) {
468
- if (behavior !== "allow") {
469
- return reject("unsupported.tool", `a ${tool} rule by host has an allow form only in this version ("${text}") — a ${behavior} by host is not read; write the ${behavior} on the whole ${tool} tool`);
470
- }
471
- if (!content.startsWith("domain:")) {
625
+ if (!content.startsWith(DOMAIN_RULE_MARKER)) {
472
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}")`);
473
627
  }
474
- const spelled = content.slice("domain:".length);
475
- if (spelled === "*")
476
- return reject("unsupported.wildcard", `"${text}" names every host — the whole tool is the bare ${tool} entry`);
477
- const below = spelled.startsWith("*.");
478
- const body = below ? spelled.slice(2) : spelled;
479
- if (body.includes("*")) {
480
- 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>\``);
481
- }
482
- const host = hostOfSpelling(body);
483
- if (host === undefined || hostOfSpelling(host) !== host) {
484
- 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
+ }
485
642
  }
486
- if (below && isAddressHost(host))
487
- return reject("invalid.grammar", `"${text}" names hosts below an address, which has none — write the address itself`);
488
- const match = below ? "subdomain" : "host";
643
+ const { match, host } = read.pattern;
489
644
  return { rule: { behavior, rule: formatRuleText(host, match, tool), tool, match, command: host } };
490
645
  }
491
- function hostOfSpelling(spelled) {
492
- if (spelled === "" || /[\s/\\@?#%]/.test(spelled) || (spelled.includes(":") && !/^\[[^\]]+\]$/.test(spelled)))
493
- return undefined;
494
- let url;
495
- try {
496
- url = new URL(`https://${spelled}/`);
497
- }
498
- catch {
499
- return undefined;
500
- }
501
- if (url.port !== "" || url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "")
502
- return undefined;
503
- const host = url.hostname.replace(/\.+$/, "");
504
- return host === "" ? undefined : host;
505
- }
506
- function isAddressHost(host) {
507
- return host.startsWith("[") || /^\d+\.\d+\.\d+\.\d+$/.test(host);
508
- }
509
646
  export function domainRuleAdmits(rule, host) {
510
- if (rule.match === "host")
511
- return host === rule.command;
512
- if (rule.match !== "subdomain" || isAddressHost(host) || !host.endsWith(`.${rule.command}`))
513
- return false;
514
- const labels = host.slice(0, -(rule.command.length + 1));
515
- return labels !== "" && !labels.split(".").includes("");
647
+ return (rule.match === "host" || rule.match === "subdomain") && hostPatternAdmits({ match: rule.match, host: rule.command }, host);
516
648
  }
517
649
  function parsePathRuleContent(text, tool, content, behavior) {
518
650
  if (behavior === "allow" && PATH_RULE_FACES[tool]?.allow === undefined) {
@@ -712,6 +844,10 @@ const NOT_REACHED = { reach: "not_reached" };
712
844
  export function programRunReachOf(rule, command) {
713
845
  if (!isCommandMatch(rule.match))
714
846
  return NOT_REACHED;
847
+ if (rule.match === "pattern") {
848
+ const pattern = commandGlobOf(rule.command, "words");
849
+ return pattern === undefined ? NOT_REACHED : anySegmentReach(readCommandMemo(command), (argv) => globArgvReachOf(pattern, argv));
850
+ }
715
851
  const body = readRuleBody(rule.command);
716
852
  if (body === undefined)
717
853
  return NOT_REACHED;
@@ -727,7 +863,8 @@ export function programRunReachOf(rule, command) {
727
863
  return hidden === undefined ? NOT_REACHED : { reach: "unreadable", reason: hidden.peelUnreadable };
728
864
  let outcome = REACHED;
729
865
  for (let i = 0; i < body.segments.length; i++) {
730
- const r = segmentReachOf(body.segments[i].argv, shape.segments[i], exact || i < body.segments.length - 1);
866
+ const bodyArgv = body.segments[i].argv;
867
+ const r = segmentReachOf(shape.segments[i], (argv) => argvReachOf(bodyArgv, argv, exact || i < body.segments.length - 1));
731
868
  if (r.reach === "not_reached")
732
869
  return hidden === undefined ? NOT_REACHED : { reach: "unreadable", reason: hidden.peelUnreadable };
733
870
  if (r.reach === "unreadable")
@@ -736,9 +873,12 @@ export function programRunReachOf(rule, command) {
736
873
  return outcome;
737
874
  }
738
875
  const bodyArgv = body.segments[0].argv;
876
+ return anySegmentReach(shape, (argv) => argvReachOf(bodyArgv, argv, exact));
877
+ }
878
+ function anySegmentReach(shape, reachOf) {
739
879
  let unreadable;
740
880
  for (const segment of shape.segments) {
741
- const r = segmentReachOf(bodyArgv, segment, exact);
881
+ const r = segmentReachOf(segment, reachOf);
742
882
  if (r.reach === "reached")
743
883
  return REACHED;
744
884
  if (r.reach === "unreadable")
@@ -749,12 +889,12 @@ export function programRunReachOf(rule, command) {
749
889
  export function ruleReachesProgramRun(rule, command) {
750
890
  return programRunReachOf(rule, command).reach === "reached";
751
891
  }
752
- function segmentReachOf(bodyArgv, segment, exact) {
892
+ function segmentReachOf(segment, reachOf) {
753
893
  if (segment.unreadable !== undefined)
754
894
  return { reach: "unreadable", reason: segment.unreadable };
755
895
  let unreadable;
756
896
  for (const argv of [segment.argv, ...segment.peeled]) {
757
- const r = argvReachOf(bodyArgv, argv, exact);
897
+ const r = reachOf(argv);
758
898
  if (r.reach === "reached")
759
899
  return REACHED;
760
900
  if (r.reach === "unreadable")
@@ -783,6 +923,148 @@ function argvReachOf(bodyArgv, argv, exact) {
783
923
  const loose = argv.slice(bodyArgv.length).find((w) => w.expands === "many");
784
924
  return loose === undefined ? NOT_REACHED : { reach: "unreadable", reason: `an unquoted expansion (${loose.raw}) may expand to nothing` };
785
925
  }
926
+ const GLOB_STAR = -1;
927
+ const GLOB_HOLE = -2;
928
+ const GLOB_GROUP = -3;
929
+ function globFloorText(body) {
930
+ let out = "";
931
+ for (let i = 0; i < body.length; i++) {
932
+ if (body[i] === "\\" && body[i + 1] === "*") {
933
+ out += "*";
934
+ i++;
935
+ }
936
+ else
937
+ out += body[i] === "*" ? "x" : body[i];
938
+ }
939
+ return out;
940
+ }
941
+ function commandGlobOf(body, reading) {
942
+ const shape = ruleLaneShapeOf(globFloorText(body), MATCH_READING);
943
+ return "reject" in shape || shape.segments.length !== 1 ? undefined : compileGlob(reading === "words" ? unquotedPattern(body) : body);
944
+ }
945
+ function unquotedPattern(body) {
946
+ let out = "";
947
+ let quote;
948
+ for (const ch of body) {
949
+ if (quote === undefined ? ch === "'" || ch === '"' : ch === quote)
950
+ quote = quote === undefined ? ch : undefined;
951
+ else
952
+ out += ch;
953
+ }
954
+ return out;
955
+ }
956
+ function compileGlob(pattern) {
957
+ const nodes = [];
958
+ const groupEnds = new Map();
959
+ for (let i = 0; i < pattern.length; i++) {
960
+ const ch = pattern[i];
961
+ if (ch === "\\" && pattern[i + 1] === "*") {
962
+ nodes.push(0x2a);
963
+ i++;
964
+ continue;
965
+ }
966
+ if (ch === "/" && pattern.startsWith("**/", i + 1)) {
967
+ let j = i + 1;
968
+ while (pattern.startsWith("**/", j))
969
+ j += 3;
970
+ nodes.push(0x2f, GLOB_GROUP, GLOB_STAR, 0x2f);
971
+ groupEnds.set(nodes.length - 3, nodes.length);
972
+ i = j - 1;
973
+ continue;
974
+ }
975
+ nodes.push(ch === "*" ? GLOB_STAR : ch.charCodeAt(0));
976
+ }
977
+ return { nodes, groupEnds };
978
+ }
979
+ function globMatches(glob, subject, holes) {
980
+ const { nodes, groupEnds } = glob;
981
+ const m = nodes.length;
982
+ let live = new Uint8Array(m + 1);
983
+ let next = new Uint8Array(m + 1);
984
+ const close = (set) => {
985
+ for (let p = 0; p < m; p++) {
986
+ if (set[p] !== 1)
987
+ continue;
988
+ if (nodes[p] === GLOB_STAR || nodes[p] === GLOB_GROUP)
989
+ set[p + 1] = 1;
990
+ if (nodes[p] === GLOB_GROUP)
991
+ set[groupEnds.get(p)] = 1;
992
+ }
993
+ };
994
+ live[0] = 1;
995
+ close(live);
996
+ for (const unit of subject) {
997
+ next.fill(0);
998
+ if (unit === GLOB_HOLE && holes === "any")
999
+ next.fill(1, live.indexOf(1));
1000
+ else {
1001
+ for (let p = 0; p < m; p++) {
1002
+ if (live[p] !== 1)
1003
+ continue;
1004
+ if (nodes[p] === GLOB_STAR)
1005
+ next[p] = 1;
1006
+ else if (nodes[p] === unit)
1007
+ next[p + 1] = 1;
1008
+ }
1009
+ close(next);
1010
+ }
1011
+ if (next.indexOf(1) < 0)
1012
+ return false;
1013
+ [live, next] = [next, live];
1014
+ }
1015
+ return live[m] === 1;
1016
+ }
1017
+ function unitsOf(text) {
1018
+ const out = [];
1019
+ for (let i = 0; i < text.length; i++)
1020
+ out.push(text.charCodeAt(i));
1021
+ return out;
1022
+ }
1023
+ function globAdmitsSpelling(body, folded) {
1024
+ const glob = commandGlobOf(body, "spelling");
1025
+ return glob !== undefined && globMatches(glob, unitsOf(folded), "inside_star");
1026
+ }
1027
+ function globArgvReachOf(glob, argv) {
1028
+ if (argv.length === 0)
1029
+ return NOT_REACHED;
1030
+ const subject = [];
1031
+ let hole;
1032
+ let anchored = false;
1033
+ argv.forEach((w, k) => {
1034
+ if (w.expands !== "many") {
1035
+ if (anchored)
1036
+ subject.push(0x20);
1037
+ anchored = true;
1038
+ }
1039
+ if (w.expands === false)
1040
+ subject.push(...unitsOf(w.text));
1041
+ else {
1042
+ subject.push(GLOB_HOLE);
1043
+ hole ??= { at: k, raw: w.raw };
1044
+ }
1045
+ });
1046
+ if (globMatches(glob, subject, "inside_star"))
1047
+ return REACHED;
1048
+ if (hole === undefined || !globMatches(glob, subject, "any"))
1049
+ return NOT_REACHED;
1050
+ return { reach: "unreadable", reason: hole.at === 0 ? "the program name is an expansion" : `an expansion (${hole.raw}) stands where the rule's pattern reads` };
1051
+ }
1052
+ export function commandPatternMayRun(body, programs) {
1053
+ const lexed = lexShell(body);
1054
+ const words = "parseFailure" in lexed ? undefined : lexed.segments[0]?.tokens.filter((t) => t.kind === "word");
1055
+ if (words === undefined || words.length === 0)
1056
+ return true;
1057
+ const position = readProgramPosition(words);
1058
+ const program = position.program;
1059
+ if (program === undefined || position.refusal !== undefined || hasUnescapedStar(words[program.index]?.raw ?? "*"))
1060
+ return true;
1061
+ const rest = words.slice(program.index + 1).map((w) => ` ${w.raw}`).join("");
1062
+ const glob = compileGlob(unquotedPattern(`${program.basename}${rest}`.toLowerCase()));
1063
+ return programs.some((p) => {
1064
+ const head = unitsOf(p.toLowerCase());
1065
+ return globMatches(glob, head, "inside_star") || globMatches(glob, [...head, 0x20, GLOB_HOLE], "any");
1066
+ });
1067
+ }
786
1068
  function readRuleBody(body) {
787
1069
  const shape = readShellCommand(body);
788
1070
  if (shape.grouped || shape.segments.length === 0)
@@ -806,6 +1088,8 @@ function admitsUnder(rule, command, reading) {
806
1088
  return false;
807
1089
  if (rule.match === "exact")
808
1090
  return folded === rule.command;
1091
+ if (rule.match === "pattern")
1092
+ return shape.segments.length === 1 && globAdmitsSpelling(rule.command, folded);
809
1093
  const bodyShape = ruleLaneShapeOf(rule.command, reading);
810
1094
  if ("reject" in bodyShape || foldSpacing(rule.command) === undefined)
811
1095
  return false;
@@ -815,9 +1099,9 @@ function admitsUnder(rule, command, reading) {
815
1099
  }
816
1100
  export const EDITED_RULE_BREADTH_WARNING_CODES = Object.freeze(["broad_prefix", "compound_prefix"]);
817
1101
  export function ruleBreadthWarningsOf(rule) {
818
- if (rule.match !== "prefix" && rule.match !== "wildcard")
1102
+ if (rule.match !== "prefix" && rule.match !== "wildcard" && rule.match !== "pattern")
819
1103
  return [];
820
- const bodyShape = ruleLaneShapeOf(rule.command, MATCH_READING);
1104
+ const bodyShape = ruleLaneShapeOf(rule.match === "pattern" ? globFloorText(rule.command) : rule.command, MATCH_READING);
821
1105
  if ("reject" in bodyShape)
822
1106
  return [];
823
1107
  if (bodyShape.segments.length > 1) {
@@ -1102,8 +1386,10 @@ export function adjudicatePersistedPathRules(rules, call, views, anchorsOf, lead
1102
1386
  return undefined;
1103
1387
  }
1104
1388
  export function adjudicatePersistedNameRules(rules, call) {
1105
- if (call.name === undefined) {
1106
- if (keyedValueAbsentReason(call.tool) === undefined)
1389
+ const name = nameRuleSubjectOf(call.tool, call.request.args);
1390
+ const values = call.request.face?.nameValues;
1391
+ if (name === undefined) {
1392
+ if (nameValueAbsentReason(call.tool) === undefined)
1107
1393
  return undefined;
1108
1394
  for (const behavior of ["deny", "ask"]) {
1109
1395
  const row = rules.find((rule) => rule.match === "name" && eligiblePersisted(rule, call, behavior));
@@ -1114,21 +1400,48 @@ export function adjudicatePersistedNameRules(rules, call) {
1114
1400
  }
1115
1401
  for (const behavior of RULE_BEHAVIORS_BY_PRECEDENCE) {
1116
1402
  for (const rule of rules) {
1117
- if (eligiblePersisted(rule, call, behavior) && nameRuleAdmits(rule, call.name))
1403
+ if (!eligiblePersisted(rule, call, behavior))
1404
+ continue;
1405
+ const arm = mountedArgumentArmOf(rule);
1406
+ if (arm !== undefined && paramRuleReaches(call.request, arm.param, arm.pattern))
1118
1407
  return { behavior, rules: [rule] };
1408
+ const reach = nameRuleReach(rule, name, values);
1409
+ if (reach === "reached")
1410
+ return { behavior, rules: [rule] };
1411
+ if (reach === "unread" && behavior !== "allow")
1412
+ return behavior === "deny" ? { behavior, rules: [rule] } : { behavior, rules: [rule], unreadable: nameValuesUnreadReason(call.tool) };
1119
1413
  }
1120
1414
  }
1121
1415
  return undefined;
1122
1416
  }
1123
1417
  export function adjudicatePersistedDomainRules(rules, call) {
1124
- if (call.host === undefined)
1125
- return undefined;
1126
- for (const rule of rules) {
1127
- if (eligiblePersisted(rule, call, "allow") && domainRuleAdmits(rule, call.host))
1128
- return { behavior: "allow", rules: [rule] };
1418
+ for (const behavior of RULE_BEHAVIORS_BY_PRECEDENCE) {
1419
+ const reading = behavior === "allow" ? call.hosts.grant : call.hosts.tighten;
1420
+ if (reading === undefined)
1421
+ continue;
1422
+ const speaks = (rule) => (rule.match === "host" || rule.match === "subdomain") && eligiblePersisted(rule, call, behavior);
1423
+ if ("unreadable" in reading) {
1424
+ if (behavior === "allow")
1425
+ continue;
1426
+ const row = rules.find(speaks);
1427
+ if (row === undefined)
1428
+ continue;
1429
+ return behavior === "deny" ? { behavior: "deny", rules: [row] } : { behavior: "ask", rules: [row], unreadable: `the host the call reaches cannot be read (${HOST_UNREADABLE_SENTENCE[reading.unreadable]})` };
1430
+ }
1431
+ const row = rules.find((rule) => speaks(rule) && domainRuleAdmits(rule, reading.host));
1432
+ if (row !== undefined)
1433
+ return { behavior, rules: [row] };
1129
1434
  }
1130
1435
  return undefined;
1131
1436
  }
1437
+ export function suggestRuleForHost(tool, host) {
1438
+ if (ruleToolGrammarOf(tool) !== "domain")
1439
+ return undefined;
1440
+ const parsed = parseRuleText(formatRuleText(host, "host", tool), "allow");
1441
+ if (!("rule" in parsed) || parsed.rule.match !== "host" || parsed.rule.command !== host)
1442
+ return undefined;
1443
+ return { kind: "single", rule: parsed.rule.rule, match: "host", command: host };
1444
+ }
1132
1445
  export function suggestRuleForName(tool, name) {
1133
1446
  if (ruleToolGrammarOf(tool) !== "name")
1134
1447
  return undefined;
@@ -1163,6 +1476,7 @@ export function segmentCoverageOf(command, rules, call) {
1163
1476
  });
1164
1477
  }
1165
1478
  export const RULE_OFFER_BATCH_MEMBER_KINDS = Object.freeze(["command", "directoryRead"]);
1479
+ export const RULE_OFFER_KINDS = Object.freeze(["single", "batch"]);
1166
1480
  export const UNCOVERED_SEGMENT_REASONS = Object.freeze(["redirection", "no_rule_form", "cap_overflow"]);
1167
1481
  export const UNCOVERED_SEGMENT_REASON_BASELINE = {
1168
1482
  redirection: "This part carries a redirection, so it is approved per use — no rule can cover this spelling. If you trust its redirection-free form, a rule can be written for that form (the card's edit row, or settings).",