@sema-agent/core 7.48.0 → 7.49.1

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 (421) hide show
  1. package/CHANGELOG.md +457 -0
  2. package/README.md +2 -2
  3. package/dist/agents/cascade.d.ts +2 -5
  4. package/dist/agents/child-failure.d.ts +20 -0
  5. package/dist/agents/child-failure.js +28 -0
  6. package/dist/agents/cross-session-ref.d.ts +11 -3
  7. package/dist/agents/cross-session-ref.js +6 -0
  8. package/dist/agents/cumulative-stats.d.ts +12 -15
  9. package/dist/agents/cumulative-stats.js +1 -1
  10. package/dist/agents/extra-tool-names.js +1 -1
  11. package/dist/agents/inherited-tool-pool.d.ts +17 -13
  12. package/dist/agents/inherited-tool-pool.js +4 -3
  13. package/dist/agents/peer-session-drain.d.ts +3 -2
  14. package/dist/agents/peer-session-drain.js +5 -4
  15. package/dist/agents/send-message-tool.js +2 -9
  16. package/dist/agents/subagent.d.ts +10 -29
  17. package/dist/agents/subagent.js +65 -53
  18. package/dist/agents/suspend-guard.d.ts +1 -1
  19. package/dist/agents/tool-filter.d.ts +76 -65
  20. package/dist/agents/tool-filter.js +122 -92
  21. package/dist/agents/verify.d.ts +4 -7
  22. package/dist/agents/verify.js +6 -7
  23. package/dist/bench/metrics.d.ts +1 -2
  24. package/dist/brain/anthropic.js +133 -79
  25. package/dist/brain/errors.d.ts +53 -6
  26. package/dist/brain/errors.js +22 -2
  27. package/dist/brain/input-too-long.d.ts +40 -19
  28. package/dist/brain/input-too-long.js +3 -16
  29. package/dist/brain/open-responses.js +19 -10
  30. package/dist/brain/openai.js +123 -32
  31. package/dist/brain/provider-dialect.d.ts +214 -0
  32. package/dist/brain/provider-dialect.js +186 -0
  33. package/dist/brain/request-params.d.ts +27 -8
  34. package/dist/brain/request-params.js +7 -12
  35. package/dist/brain/request-route.d.ts +84 -0
  36. package/dist/brain/request-route.js +41 -0
  37. package/dist/brain/retry.d.ts +8 -0
  38. package/dist/brain/retry.js +10 -4
  39. package/dist/brain/route-adjudicator.d.ts +7 -0
  40. package/dist/brain/route-adjudicator.js +5 -1
  41. package/dist/brain/status-sink.d.ts +20 -0
  42. package/dist/brain/status-sink.js +11 -0
  43. package/dist/brain/stream-engine.d.ts +52 -1
  44. package/dist/brain/stream-engine.js +81 -24
  45. package/dist/brain/stream-shared.d.ts +57 -11
  46. package/dist/brain/stream-shared.js +28 -2
  47. package/dist/core/a2a.d.ts +1 -0
  48. package/dist/core/agent-config-dir.js +3 -2
  49. package/dist/core/allow-entry.d.ts +21 -12
  50. package/dist/core/allow-entry.js +38 -31
  51. package/dist/core/arg-summary.d.ts +21 -28
  52. package/dist/core/arg-summary.js +235 -88
  53. package/dist/core/ask-origin.d.ts +42 -24
  54. package/dist/core/ask-origin.js +18 -13
  55. package/dist/core/auto-mode-prompt-assets.d.ts +2 -2
  56. package/dist/core/auto-mode-prompt-assets.js +1 -1
  57. package/dist/core/auto-mode-prompt.d.ts +11 -1
  58. package/dist/core/auto-mode-prompt.js +13 -10
  59. package/dist/core/auto-mode-rebuild.d.ts +7 -0
  60. package/dist/core/auto-mode-rebuild.js +4 -1
  61. package/dist/core/canonical-json.d.ts +3 -0
  62. package/dist/core/canonical-json.js +2 -1
  63. package/dist/core/checkpoint-requirements.d.ts +892 -0
  64. package/dist/core/checkpoint-requirements.js +333 -0
  65. package/dist/core/checkpoint-store.d.ts +94 -65
  66. package/dist/core/checkpoint-store.js +14 -26
  67. package/dist/core/child-clamp-seats.d.ts +228 -69
  68. package/dist/core/child-clamp-seats.js +211 -17
  69. package/dist/core/classifier-unavailable-arm.d.ts +5 -1
  70. package/dist/core/classifier-unavailable-arm.js +3 -3
  71. package/dist/core/compliance.d.ts +3 -0
  72. package/dist/core/compliance.js +9 -4
  73. package/dist/core/credential-vocabulary.d.ts +140 -0
  74. package/dist/core/credential-vocabulary.js +131 -0
  75. package/dist/core/decision-receipt.js +1 -1
  76. package/dist/core/effective-path-target.d.ts +73 -16
  77. package/dist/core/effective-path-target.js +63 -33
  78. package/dist/core/engine-notice.d.ts +43 -11
  79. package/dist/core/file-history-store.js +2 -2
  80. package/dist/core/fs-write-gate-policy.d.ts +4 -0
  81. package/dist/core/fs-write-gate-policy.js +22 -5
  82. package/dist/core/gate-bounded-await.d.ts +28 -13
  83. package/dist/core/gate-bounded-await.js +41 -4
  84. package/dist/core/gate-fold.d.ts +1 -1
  85. package/dist/core/gate-fold.js +24 -25
  86. package/dist/core/gate-lanes.js +16 -15
  87. package/dist/core/gate-outcome.d.ts +20 -5
  88. package/dist/core/gate-outcome.js +5 -0
  89. package/dist/core/governance-codes.d.ts +11 -4
  90. package/dist/core/governance-codes.js +14 -3
  91. package/dist/core/hooks.d.ts +21 -0
  92. package/dist/core/locked-config.d.ts +8 -0
  93. package/dist/core/locked-config.js +14 -2
  94. package/dist/core/lsp.js +3 -5
  95. package/dist/core/mailbox-store.d.ts +3 -4
  96. package/dist/core/mcp-connection-pool.d.ts +75 -0
  97. package/dist/core/mcp-connection-pool.js +128 -0
  98. package/dist/core/mcp-failure.d.ts +2 -1
  99. package/dist/core/mcp-failure.js +13 -2
  100. package/dist/core/mcp.d.ts +4 -0
  101. package/dist/core/mcp.js +70 -43
  102. package/dist/core/memory-engine/content-origin.js +2 -14
  103. package/dist/core/memory-engine/engine.d.ts +32 -22
  104. package/dist/core/memory-engine/engine.js +103 -55
  105. package/dist/core/memory-engine/file-backend.d.ts +13 -30
  106. package/dist/core/memory-engine/file-backend.js +37 -252
  107. package/dist/core/memory-engine/index.d.ts +1 -1
  108. package/dist/core/memory-engine/index.js +1 -1
  109. package/dist/core/memory-engine/layout.d.ts +139 -9
  110. package/dist/core/memory-engine/layout.js +350 -7
  111. package/dist/core/memory-engine/scope-contract.d.ts +10 -0
  112. package/dist/core/memory-engine/scope-contract.js +8 -0
  113. package/dist/core/memory-engine/session-incarnation.d.ts +14 -4
  114. package/dist/core/memory-engine/session-incarnation.js +5 -2
  115. package/dist/core/memory-engine/types.d.ts +10 -1
  116. package/dist/core/memory.js +5 -17
  117. package/dist/core/model-seat.d.ts +4 -4
  118. package/dist/core/model-seat.js +1 -1
  119. package/dist/core/mounted-mcp.d.ts +57 -0
  120. package/dist/core/mounted-mcp.js +19 -0
  121. package/dist/core/namespaced-rule-name.d.ts +29 -0
  122. package/dist/core/namespaced-rule-name.js +52 -0
  123. package/dist/core/park-selfcheck.d.ts +1 -1
  124. package/dist/core/park-selfcheck.js +16 -6
  125. package/dist/core/path-guard-rows.d.ts +99 -0
  126. package/dist/core/path-guard-rows.js +45 -0
  127. package/dist/core/path-identity.d.ts +22 -16
  128. package/dist/core/path-identity.js +10 -10
  129. package/dist/core/path-rule-binding.d.ts +2 -1
  130. package/dist/core/path-rule-binding.js +3 -1
  131. package/dist/core/pause-registry.d.ts +16 -2
  132. package/dist/core/pause-registry.js +6 -0
  133. package/dist/core/permission-rule-consent.js +0 -7
  134. package/dist/core/permission-rule-model.d.ts +68 -24
  135. package/dist/core/permission-rule-model.js +105 -24
  136. package/dist/core/permission-rule-store.js +2 -2
  137. package/dist/core/permission-rules.d.ts +3 -30
  138. package/dist/core/permission-rules.js +4 -54
  139. package/dist/core/read-root-candidate.d.ts +1 -3
  140. package/dist/core/read-root-candidate.js +4 -15
  141. package/dist/core/real-approval.d.ts +4 -3
  142. package/dist/core/retired-keys.d.ts +13 -0
  143. package/dist/core/retired-keys.js +14 -0
  144. package/dist/core/roles.d.ts +2 -3
  145. package/dist/core/roles.js +15 -6
  146. package/dist/core/runner/assemble-result.d.ts +0 -7
  147. package/dist/core/runner/assemble-result.js +19 -31
  148. package/dist/core/runner/caller-roster.d.ts +78 -0
  149. package/dist/core/runner/caller-roster.js +219 -0
  150. package/dist/core/runner/checkpoint-scope.d.ts +16 -16
  151. package/dist/core/runner/checkpoint-scope.js +2 -2
  152. package/dist/core/runner/child-tree.js +2 -2
  153. package/dist/core/runner/contracts.d.ts +68 -35
  154. package/dist/core/runner/decide-continuation.d.ts +51 -3
  155. package/dist/core/runner/decide-continuation.js +76 -21
  156. package/dist/core/runner/denial-limit-arms.d.ts +17 -5
  157. package/dist/core/runner/denial-limit-arms.js +11 -9
  158. package/dist/core/runner/extra-body-dropped-seat.d.ts +8 -0
  159. package/dist/core/runner/extra-body-dropped-seat.js +22 -0
  160. package/dist/core/runner/finish-reason-seat.d.ts +9 -0
  161. package/dist/core/runner/finish-reason-seat.js +40 -0
  162. package/dist/core/runner/gate-exit.d.ts +44 -32
  163. package/dist/core/runner/gate-exit.js +112 -25
  164. package/dist/core/runner/inherited-ask-grants.js +6 -4
  165. package/dist/core/runner/mcp-redial.d.ts +3 -3
  166. package/dist/core/runner/mcp-redial.js +3 -4
  167. package/dist/core/runner/permission-rule-lanes.d.ts +1 -2
  168. package/dist/core/runner/permission-rule-lanes.js +5 -4
  169. package/dist/core/runner/prepare-artifact.d.ts +4 -0
  170. package/dist/core/runner/prepare-artifact.js +2 -1
  171. package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
  172. package/dist/core/runner/prepare-ask-lane.js +12 -6
  173. package/dist/core/runner/prepare-boundary-parks.d.ts +5 -8
  174. package/dist/core/runner/prepare-boundary-parks.js +4 -7
  175. package/dist/core/runner/prepare-caps-and-workflow.d.ts +11 -15
  176. package/dist/core/runner/prepare-caps-and-workflow.js +19 -27
  177. package/dist/core/runner/prepare-config-doors.d.ts +9 -9
  178. package/dist/core/runner/prepare-config-doors.js +10 -5
  179. package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
  180. package/dist/core/runner/prepare-defer-classify.js +5 -3
  181. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
  182. package/dist/core/runner/prepare-delegation-surface.js +3 -3
  183. package/dist/core/runner/prepare-gate-stations.d.ts +4 -4
  184. package/dist/core/runner/prepare-gate-stations.js +9 -7
  185. package/dist/core/runner/prepare-hands-readface.d.ts +7 -7
  186. package/dist/core/runner/prepare-hands-readface.js +7 -7
  187. package/dist/core/runner/prepare-inherited-gate.d.ts +7 -6
  188. package/dist/core/runner/prepare-inherited-gate.js +14 -15
  189. package/dist/core/runner/prepare-listings.d.ts +7 -24
  190. package/dist/core/runner/prepare-listings.js +4 -5
  191. package/dist/core/runner/prepare-memory-engine-session.d.ts +2 -0
  192. package/dist/core/runner/prepare-memory-engine-session.js +4 -3
  193. package/dist/core/runner/prepare-memory.d.ts +6 -0
  194. package/dist/core/runner/prepare-memory.js +13 -13
  195. package/dist/core/runner/prepare-offload-wrappers.d.ts +5 -0
  196. package/dist/core/runner/prepare-offload-wrappers.js +3 -3
  197. package/dist/core/runner/prepare-park-ask.d.ts +7 -8
  198. package/dist/core/runner/prepare-park-ask.js +17 -10
  199. package/dist/core/runner/prepare-policy-chain.js +24 -44
  200. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  201. package/dist/core/runner/prepare-project-context.js +3 -4
  202. package/dist/core/runner/prepare-protocol-tools.d.ts +9 -3
  203. package/dist/core/runner/prepare-protocol-tools.js +3 -4
  204. package/dist/core/runner/prepare-run-refs.d.ts +3 -0
  205. package/dist/core/runner/prepare-run-refs.js +2 -1
  206. package/dist/core/runner/prepare-safety-scan.d.ts +4 -8
  207. package/dist/core/runner/prepare-safety-scan.js +1 -2
  208. package/dist/core/runner/prepare-suspend-saga.d.ts +7 -1
  209. package/dist/core/runner/prepare-suspend-saga.js +28 -12
  210. package/dist/core/runner/prepare-task.d.ts +1 -1
  211. package/dist/core/runner/prepare-task.js +67 -56
  212. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -10
  213. package/dist/core/runner/prepare-wiring-manifest.js +5 -8
  214. package/dist/core/runner/resume-apply.js +7 -14
  215. package/dist/core/runner/resume-checkpoint-screen.js +11 -4
  216. package/dist/core/runner/resume-internals-and-config.js +2 -2
  217. package/dist/core/runner/resume-policy-outcome.js +5 -32
  218. package/dist/core/runner/resume-review-outcome.js +4 -1
  219. package/dist/core/runner/retired-runner-deps.d.ts +6 -0
  220. package/dist/core/runner/retired-runner-deps.js +10 -1
  221. package/dist/core/runner/run-brain-sinks.d.ts +4 -0
  222. package/dist/core/runner/run-brain-sinks.js +5 -2
  223. package/dist/core/runner/run-harness-handlers.js +29 -36
  224. package/dist/core/runner/run-leg.d.ts +0 -2
  225. package/dist/core/runner/run-leg.js +32 -33
  226. package/dist/core/runner/run-terminal-adoption.js +4 -3
  227. package/dist/core/runner/runtask.js +8 -8
  228. package/dist/core/runner/session-rule-policy.js +5 -5
  229. package/dist/core/runner/stream-settle-backstop.js +7 -4
  230. package/dist/core/runner/sub-agent-deny-lists.d.ts +51 -12
  231. package/dist/core/runner/sub-agent-deny-lists.js +111 -80
  232. package/dist/core/runner/terminal-account.d.ts +48 -0
  233. package/dist/core/runner/terminal-account.js +186 -0
  234. package/dist/core/runner/tool-end-body.d.ts +54 -17
  235. package/dist/core/runner/tool-end-body.js +27 -2
  236. package/dist/core/runner/tool-face-overlay.d.ts +3 -1
  237. package/dist/core/runner/tool-face-overlay.js +6 -4
  238. package/dist/core/runner/tool-occurrence.d.ts +106 -0
  239. package/dist/core/runner/tool-occurrence.js +123 -0
  240. package/dist/core/runner/tool-progress-lane.d.ts +16 -8
  241. package/dist/core/runner/tool-progress-lane.js +25 -34
  242. package/dist/core/runner/tool-turn-repair-seat.d.ts +28 -4
  243. package/dist/core/runner/tool-turn-repair-seat.js +36 -13
  244. package/dist/core/runner/turn-attachments.d.ts +1 -1
  245. package/dist/core/runner/write-protection-station.d.ts +21 -9
  246. package/dist/core/runner/write-protection-station.js +20 -17
  247. package/dist/core/runner-deps.d.ts +12 -22
  248. package/dist/core/secret-env.d.ts +16 -5
  249. package/dist/core/secret-env.js +23 -63
  250. package/dist/core/sensitive-path-policy.d.ts +19 -18
  251. package/dist/core/sensitive-path-policy.js +73 -49
  252. package/dist/core/session-reconcile.d.ts +3 -0
  253. package/dist/core/session-reconcile.js +5 -4
  254. package/dist/core/session-store.d.ts +5 -0
  255. package/dist/core/session-store.js +12 -0
  256. package/dist/core/session.d.ts +13 -0
  257. package/dist/core/shared-memory/normalize.d.ts +17 -4
  258. package/dist/core/shared-memory/normalize.js +7 -7
  259. package/dist/core/shell-lexer.js +5 -4
  260. package/dist/core/shell-wrapper-projection.js +1 -1
  261. package/dist/core/skill-grant.js +1 -1
  262. package/dist/core/store-contracts/checkpoint-store-contract.js +13 -0
  263. package/dist/core/store-contracts/mailbox-store-contract.d.ts +7 -14
  264. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +27 -0
  265. package/dist/core/store-contracts/session-store-incarnation-contract.js +92 -0
  266. package/dist/core/stub-env.d.ts +2 -0
  267. package/dist/core/stub-env.js +1 -0
  268. package/dist/core/synthetic-read-verdict.js +2 -2
  269. package/dist/core/task-event.d.ts +59 -5
  270. package/dist/core/task-result.d.ts +1 -1
  271. package/dist/core/task-spec.d.ts +42 -9
  272. package/dist/core/terminal-cause.d.ts +94 -11
  273. package/dist/core/tighten-task-spec.d.ts +5 -4
  274. package/dist/core/tighten-task-spec.js +20 -25
  275. package/dist/core/tool-catalog-entries.d.ts +5 -7
  276. package/dist/core/tool-catalog-entries.js +14 -15
  277. package/dist/core/tool-face.d.ts +47 -0
  278. package/dist/core/tool-face.js +5 -0
  279. package/dist/core/tool-model-gate.d.ts +5 -5
  280. package/dist/core/tool-name-aliases.d.ts +26 -2
  281. package/dist/core/tool-name-aliases.js +13 -0
  282. package/dist/core/tool-name-list.d.ts +30 -0
  283. package/dist/core/tool-name-list.js +55 -0
  284. package/dist/core/tool-name-readings.d.ts +1 -9
  285. package/dist/core/tool-name-readings.js +1 -3
  286. package/dist/core/tool-policy.d.ts +195 -85
  287. package/dist/core/tool-policy.js +165 -380
  288. package/dist/core/tool-registry.d.ts +44 -1
  289. package/dist/core/tool-registry.js +26 -0
  290. package/dist/core/tool-roster.d.ts +19 -1
  291. package/dist/core/tool-roster.js +21 -2
  292. package/dist/core/tool-spec-members.d.ts +45 -0
  293. package/dist/core/tool-spec-members.js +169 -0
  294. package/dist/core/tool-spec.d.ts +46 -8
  295. package/dist/core/tools.d.ts +6 -2
  296. package/dist/core/tools.js +8 -4
  297. package/dist/core/untrusted-egress.js +29 -26
  298. package/dist/core/untrusted-text.d.ts +13 -10
  299. package/dist/core/untrusted-text.js +6 -44
  300. package/dist/core/workflow-journal-store.d.ts +4 -1
  301. package/dist/core/write-protect.d.ts +65 -29
  302. package/dist/core/write-protect.js +235 -177
  303. package/dist/engine/compaction/compaction.d.ts +2 -2
  304. package/dist/engine/execution-env/node-execution-env.d.ts +5 -23
  305. package/dist/engine/execution-env/node-execution-env.js +16 -38
  306. package/dist/engine/execution-env/symlink-chain.d.ts +23 -0
  307. package/dist/engine/execution-env/symlink-chain.js +32 -0
  308. package/dist/engine/harness/types.d.ts +7 -0
  309. package/dist/engine/llm/tool-turns.d.ts +3 -0
  310. package/dist/engine/llm/tool-turns.js +1 -0
  311. package/dist/engine/llm/types.d.ts +59 -23
  312. package/dist/engine/loop/agent-loop.js +16 -2
  313. package/dist/engine/session/memory-repo.js +11 -3
  314. package/dist/engine/session/repo-utils.d.ts +8 -0
  315. package/dist/engine/session/repo-utils.js +13 -0
  316. package/dist/index.d.ts +20 -23
  317. package/dist/index.js +17 -20
  318. package/dist/internal/harness.d.ts +1 -1
  319. package/dist/internal/harness.js +1 -1
  320. package/dist/internal/llm.d.ts +2 -2
  321. package/dist/internal/llm.js +1 -1
  322. package/dist/orchestration/governance-baseline-validity.js +1 -1
  323. package/dist/orchestration/run-spec.js +3 -3
  324. package/dist/orchestration/run-workflow-tool.d.ts +16 -35
  325. package/dist/orchestration/run-workflow-tool.js +38 -78
  326. package/dist/orchestration/workflow-governance.d.ts +21 -25
  327. package/dist/orchestration/workflow-governance.js +38 -78
  328. package/dist/orchestration/workflow-primitives.d.ts +7 -22
  329. package/dist/orchestration/workflow-primitives.js +5 -12
  330. package/dist/orchestration/workflow-types.d.ts +1 -1
  331. package/dist/orchestration/workflow.d.ts +61 -11
  332. package/dist/orchestration/workflow.js +189 -117
  333. package/dist/scenarios/full-body.d.ts +1 -1
  334. package/dist/shell/lex.d.ts +0 -21
  335. package/dist/shell/lex.js +250 -68
  336. package/dist/shell/shell-words.d.ts +35 -0
  337. package/dist/shell/shell-words.js +65 -0
  338. package/dist/stores/file/checkpoint-store.d.ts +5 -5
  339. package/dist/stores/file/checkpoint-store.js +5 -2
  340. package/dist/stores/file/index.d.ts +0 -8
  341. package/dist/stores/file/index.js +0 -12
  342. package/dist/stores/file/session-store.d.ts +3 -0
  343. package/dist/stores/file/session-store.js +44 -19
  344. package/dist/tools/artifact/artifact-tool.js +2 -0
  345. package/dist/tools/fs/bash-program-position.d.ts +12 -4
  346. package/dist/tools/fs/bash-program-position.js +32 -9
  347. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  348. package/dist/tools/fs/bash-readonly-classifier.js +8 -7
  349. package/dist/tools/fs/containment-face.d.ts +4 -0
  350. package/dist/tools/fs/containment-face.js +3 -0
  351. package/dist/tools/fs/fs-shared.d.ts +10 -0
  352. package/dist/tools/fs/fs-shared.js +18 -0
  353. package/dist/tools/fs/fs-write.js +4 -2
  354. package/dist/tools/fs/index.d.ts +1 -1
  355. package/dist/tools/fs/program-read-model.d.ts +46 -20
  356. package/dist/tools/fs/program-read-model.js +50 -20
  357. package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
  358. package/dist/tools/fs/program-read-model.rows.js +16 -15
  359. package/dist/tools/fs/read-deny-home.d.ts +33 -47
  360. package/dist/tools/fs/read-deny-home.js +35 -49
  361. package/dist/tools/fs/read-deny.d.ts +23 -22
  362. package/dist/tools/fs/read-deny.js +114 -89
  363. package/dist/tools/fs/removal-targets.d.ts +56 -23
  364. package/dist/tools/fs/removal-targets.js +65 -87
  365. package/dist/tools/fs/safety.d.ts +29 -22
  366. package/dist/tools/fs/safety.js +17 -11
  367. package/dist/tools/fs/search.js +10 -8
  368. package/dist/tools/fs/shell-read-argv.js +1 -1
  369. package/dist/tools/fs/shell-read-bindings.d.ts +169 -37
  370. package/dist/tools/fs/shell-read-bindings.js +324 -86
  371. package/dist/tools/fs/shell-read-builtins.d.ts +35 -0
  372. package/dist/tools/fs/shell-read-builtins.js +43 -0
  373. package/dist/tools/fs/shell-read-check.d.ts +15 -1
  374. package/dist/tools/fs/shell-read-check.js +67 -43
  375. package/dist/tools/fs/shell-read-establish.js +6 -6
  376. package/dist/tools/fs/shell-read-functions.d.ts +22 -0
  377. package/dist/tools/fs/shell-read-functions.js +29 -0
  378. package/dist/tools/fs/shell-read-judge.d.ts +51 -18
  379. package/dist/tools/fs/shell-read-judge.js +40 -4
  380. package/dist/tools/fs/shell-read-plan.d.ts +12 -6
  381. package/dist/tools/fs/shell-read-plan.js +118 -113
  382. package/dist/tools/fs/shell-read-start.d.ts +29 -6
  383. package/dist/tools/fs/shell-read-start.js +16 -9
  384. package/dist/tools/fs/shell-read-word.js +4 -0
  385. package/dist/tools/fs/shell-read-workdir.d.ts +156 -0
  386. package/dist/tools/fs/shell-read-workdir.js +281 -0
  387. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -2
  388. package/dist/tools/fs/shell-read-write-targets.js +14 -5
  389. package/dist/tools/fs/shell-reentry-text.d.ts +4 -6
  390. package/dist/tools/fs/shell-reentry-text.js +3 -11
  391. package/dist/tools/task-list.d.ts +1 -2
  392. package/dist/tools/web.js +1 -1
  393. package/package.json +4 -1
  394. package/test/export-surface.snapshot.json +107 -143
  395. package/wire-changes/core@7.49.0.json +4765 -0
  396. package/dist/agents/teacher.d.ts +0 -159
  397. package/dist/agents/teacher.js +0 -577
  398. package/dist/agents/team.d.ts +0 -226
  399. package/dist/agents/team.js +0 -257
  400. package/dist/core/strategy-store.d.ts +0 -238
  401. package/dist/core/strategy-store.js +0 -253
  402. package/dist/orchestration/builtin-workflows.d.ts +0 -133
  403. package/dist/orchestration/builtin-workflows.js +0 -159
  404. package/dist/scenarios/env.d.ts +0 -40
  405. package/dist/scenarios/env.js +0 -39
  406. package/dist/scenarios/scenario-registry.d.ts +0 -110
  407. package/dist/scenarios/scenario-registry.js +0 -140
  408. package/dist/scenarios/teacher-quickstart.d.ts +0 -40
  409. package/dist/scenarios/teacher-quickstart.js +0 -12
  410. package/dist/stores/cc/lockfile.d.ts +0 -10
  411. package/dist/stores/cc/lockfile.js +0 -98
  412. package/dist/stores/cc/mailbox-store.d.ts +0 -20
  413. package/dist/stores/cc/mailbox-store.js +0 -255
  414. package/dist/stores/cc/roster-adapter.d.ts +0 -12
  415. package/dist/stores/cc/roster-adapter.js +0 -117
  416. package/dist/stores/cc/sidecar-transcript.d.ts +0 -97
  417. package/dist/stores/cc/sidecar-transcript.js +0 -264
  418. package/dist/stores/cc/task-list-store.d.ts +0 -2
  419. package/dist/stores/cc/task-list-store.js +0 -125
  420. package/dist/stores/file/strategy-store.d.ts +0 -97
  421. package/dist/stores/file/strategy-store.js +0 -341
@@ -20,8 +20,9 @@ import type { Checkpoint, CheckpointGate, CheckpointRow, PendingAction, ResumeOu
20
20
  *
21
21
  * - `facet` — the deadline semantic a deployment's reaper applies (see {@link Checkpoint.deadline}):
22
22
  * `A` = approval gates, whose `deadline` is an SLA resolve-deny; `B` = unattended-TTL gates, whose
23
- * `deadline` is an abandonment TTL (`expire`/`reap` only — never a resolve-deny; these pauses produce
24
- * no `tool_end` and therefore no settlement); `-` = the non-gate park (`task_done`).
23
+ * `deadline` is an abandonment TTL (`expire`/`reap` only — never a resolve-deny, so a sweep's word is not a
24
+ * decider on these rows; these pauses produce no `tool_end`, and the one that records its decider — the plan
25
+ * pause — files the settlement on the resolved row alone); `-` = the non-gate park (`task_done`).
25
26
  * - `taskStatus` — the `TaskResult.status` word a run assembles when it stops at this gate (approvals and
26
27
  * resource slices read `suspended`; the two review pauses read `needs_review`).
27
28
  * - `terminal` — `task_done` is the 1C handle / deployment-park convention, not a run pause core mints.
@@ -79,6 +80,19 @@ export type PendingKind = (typeof PAUSE_REGISTRY)[GateKind]["pending"];
79
80
  export type ResumeGate = (typeof PAUSE_REGISTRY)[GateKind]["resume"] | "wake";
80
81
  /** Runtime twin of {@link GateKind}. */
81
82
  export declare function isGateKind(v: unknown): v is GateKind;
83
+ /** The deadline semantic of a parked row — the registry's `facet` column, read with the gate's card: a question parked
84
+ * under an approval gate kind is not an approval, so its deadline voids it (`B`) instead of denying it (`A`). No gate
85
+ * kind of its own (the once-only winner, the reaper deadline and the batch stay one family). Only the literal
86
+ * `"question"` card marks a question. */
87
+ export declare function facetOf(gate: {
88
+ readonly kind: GateKind;
89
+ readonly approvalCard?: unknown;
90
+ }): "A" | "B" | "-";
91
+ /** Is this parked row a question to the user: an approval-kind gate whose card is the reserved `"question"`. */
92
+ export declare function isQuestionGate(gate: {
93
+ readonly kind: GateKind;
94
+ readonly approvalCard?: unknown;
95
+ }): boolean;
82
96
  /** The one resume-match predicate: does this outcome gate word answer a checkpoint of this kind? `wake`
83
97
  * answers any (it passed its own, stricter, non-gate validation upstream); every other word must be the
84
98
  * row's `resume` column. Replaces a six-arm hand-written disjunction. */
@@ -9,6 +9,12 @@ export const PAUSE_REGISTRY = {
9
9
  export function isGateKind(v) {
10
10
  return typeof v === "string" && Object.prototype.hasOwnProperty.call(PAUSE_REGISTRY, v);
11
11
  }
12
+ export function facetOf(gate) {
13
+ return isQuestionGate(gate) ? "B" : PAUSE_REGISTRY[gate.kind].facet;
14
+ }
15
+ export function isQuestionGate(gate) {
16
+ return PAUSE_REGISTRY[gate.kind].pending === "tool_approval" && gate.approvalCard === "question";
17
+ }
12
18
  export function resumeGateMatches(gateKind, outcomeGate) {
13
19
  return outcomeGate === "wake" || PAUSE_REGISTRY[gateKind].resume === outcomeGate;
14
20
  }
@@ -736,13 +736,6 @@ export async function prepareCcImport(opts) {
736
736
  skipped.push({ rule: String(entry), reason: `settings ${behavior} entry is not a string` });
737
737
  continue;
738
738
  }
739
- if (behavior === "allow" && entry.startsWith("Read(") && "reject" in parseRuleText(entry, behavior)) {
740
- skipped.push({
741
- rule: entry,
742
- reason: "unsupported.form: only the Read(//abs-dir/**) directory form imports in v1 — this entry stays in the settings file, unimported",
743
- });
744
- continue;
745
- }
746
739
  if (!/^[A-Za-z][A-Za-z0-9_]*\(.*\)$/.test(entry)) {
747
740
  const reason = /^[A-Za-z][A-Za-z0-9_-]*$/.test(entry)
748
741
  ? "unsupported.form: a bare tool-name entry is a name-set item, not a command rule — it stays in the settings file, unimported"
@@ -118,12 +118,8 @@ export declare const RULE_BEHAVIORS_BY_PRECEDENCE: readonly RuleBehavior[];
118
118
  * fabricated tightening is still a fabrication).
119
119
  */
120
120
  export declare function readRowBehavior(v: unknown): RuleBehavior | undefined;
121
- /** The tools the rule lane speaks for. `"Bash"` is the historical member; `"Read"` (design/382 §2.2)
122
- * speaks EXACTLY ONE grammar — the double-slash directory form `Read(//abs/**)`, the directory
123
- * read-authorization a compound card's cd segment mints — and its admission is the CLOSED two-point
124
- * one on {@link directoryRuleAdmits}: a Read tool call under the directory, and a compound's cd
125
- * segment resolving into it. Nothing else — never a write, never a whole Bash command, never a
126
- * non-cd segment.
121
+ /** The tools the rule lane speaks for — derived per grammar ({@link ruleToolGrammarOf}): the command word, every catalog
122
+ * path tool, the name tools, the domain tools.
127
123
  *
128
124
  * design/276 §3.3 (doctrine, for whoever widens this set): a future `SendMessage(…)`-form rule MAY
129
125
  * clear the peer-referral ask — that ask is classifier hesitation by construction (#144: allow rules
@@ -135,7 +131,7 @@ export type PersistedRuleTool = string;
135
131
  * A rule word, not a tool list: the calls a command rule reaches are the SHELL FACE's — every catalog definition whose
136
132
  * `command` the command grammar judges ({@link laneToolNamesOf}). */
137
133
  export declare const COMMAND_RULE_TOOL = "Bash";
138
- /** The one tool whose path family has an ALLOW form (the `Read(//abs-dir/**)` directory grant). */
134
+ /** The read face's rule word: a `Read(<path>)` rule speaks for every call that reads a path ({@link PATH_RULE_FACES}). */
139
135
  export declare const READ_RULE_TOOL = "Read";
140
136
  /**
141
137
  * The path rules that speak for a FACE rather than for one tool — one table, one column per tightening behavior (the
@@ -150,8 +146,13 @@ export declare const READ_RULE_TOOL = "Read";
150
146
  * results and reach the shell's file commands; an ask is a question asked at the gate before the call runs, so it is
151
147
  * judged there, on the call's own path target only — it does not descend (a search rooted above the asked path is not
152
148
  * asked, and its results are not filtered).
149
+ *
150
+ * The ALLOW column is the reference's too: an allow by path is read on these two tools only — a `Read(<path>)` allow speaks
151
+ * for the read face, an `Edit(<path>)` allow for the write face and the read face (an edit allowed is a read allowed) — and
152
+ * an allow by path on any other tool has no rule form. It is judged at the gate with the narrow reading
153
+ * ({@link adjudicatePersistedPathRules}): every view of the call's target must lie where the rule names.
153
154
  */
154
- export declare const PATH_RULE_FACES: Readonly<Record<string, Readonly<Partial<Record<"deny" | "ask", readonly DenyFace[]>>>>>;
155
+ export declare const PATH_RULE_FACES: Readonly<Record<string, Readonly<Partial<Record<RuleBehavior, readonly DenyFace[]>>>>>;
155
156
  /** The face a call's protective path target is on: `read` for a read slot, `write` for every other access; none without
156
157
  * one — the face a {@link PATH_RULE_FACES} rule is judged against, in a rule policy and in the persisted rule lane alike. */
157
158
  export declare function callPathFaceOf(call: ToolCallLike): DenyFace | undefined;
@@ -168,27 +169,44 @@ export declare const NAME_RULE_FACES: Readonly<Record<string, {
168
169
  readonly param: string;
169
170
  readonly params: readonly string[];
170
171
  }>>;
172
+ /**
173
+ * The tools whose rule content names the HOST of the URL one parameter carries — the reference's `WebFetch(domain:<host>)`
174
+ * form, one row per tool: `param` is the URL argument the host is read from ({@link domainRuleSubjectOf}). Read by
175
+ * {@link parseDomainRuleContent}; an allow only (see there).
176
+ */
177
+ export declare const DOMAIN_RULE_FACES: Readonly<Record<string, {
178
+ readonly param: string;
179
+ }>>;
171
180
  /**
172
181
  * Which tools a rule may name — the model's tool set is DERIVED, never a hand-kept list: the command rule word
173
182
  * ({@link COMMAND_RULE_TOOL}) takes the COMMAND grammar — the other shell-face tools take none of their own, their command is
174
183
  * judged by the `Bash(…)` rules ({@link laneToolNamesOf}); every tool the catalog declares a path target for (Read / Edit / Write /
175
- * Glob / NotebookEdit / …, `pathTargetOf`) takes the PATH grammar; the tools of {@link NAME_RULE_FACES} take the NAME grammar.
184
+ * Glob / NotebookEdit / …, `pathTargetOf`) takes the PATH grammar; the tools of {@link NAME_RULE_FACES} take the NAME grammar;
185
+ * the tools of {@link DOMAIN_RULE_FACES} the DOMAIN grammar.
176
186
  * A rule on any other tool has no content this model can judge and is refused (`unsupported.tool`). The tool set therefore
177
187
  * moves with the catalog's declarations, exactly as the protective fences' covered set does.
178
188
  */
179
- export declare function ruleToolGrammarOf(toolName: string): "command" | "path" | "name" | undefined;
189
+ export type RuleGrammar = "command" | "path" | "name" | "domain";
190
+ export declare function ruleToolGrammarOf(toolName: string): RuleGrammar | undefined;
191
+ /**
192
+ * The HOST a domain-grammar tool's call fetches — the string every DOMAIN rule is read against — or undefined: not a domain
193
+ * tool; no URL argument, or one that is not a string; a URL that does not parse, is not http(s), or carries credentials (the
194
+ * fetch tool refuses those itself). The host is the URL parser's (lower case, IDN in its ASCII form, an IPv4 shorthand
195
+ * expanded — the host the fetch connects to), its trailing dots read off.
196
+ */
197
+ export declare function domainRuleSubjectOf(toolName: string, args: unknown): string | undefined;
180
198
  /** The value a name-grammar tool's call carries in its name argument — the string every NAME rule is read against, the one a
181
199
  * `Skill` call is resolved by — or undefined (not a name-grammar tool; no such argument; not a string). */
182
200
  export declare function nameRuleSubjectOf(toolName: string, args: unknown): string | undefined;
183
201
  /** The rule WORDS a call is judged under, once each: for every name the call answers to (the model's spelling, the wire
184
202
  * name, the other aliases that resolve to it), the word its rules are written in — a shell-face name's is the command
185
- * grammar's {@link COMMAND_RULE_TOOL} (a `Bash(<command>)` rule reaches `Monitor` as it reaches `Bash`), a path or name
186
- * tool's its own name — so a stored `Bash(…)` or `Edit(…)` row reaches a tool that answers to that name under another wire
203
+ * grammar's {@link COMMAND_RULE_TOOL} (a `Bash(<command>)` rule reaches `Monitor` as it reaches `Bash`), a path, name or
204
+ * domain tool's its own name — so a stored `Bash(…)` or `Edit(…)` row reaches a tool that answers to that name under another wire
187
205
  * name, as a policy's rule does. Every lane that judges a call by a rule's word reads it here: the policy compiler, the
188
206
  * person's stored rows, the org partition, the allow lists. Empty ⇒ no grammar speaks for the call. */
189
207
  export declare function laneToolNamesOf(req: Pick<import("./tool-policy.js").ToolCallRequest, "toolName" | "face">): readonly string[];
190
208
  /**
191
- * Match forms. Four members, two of them one predicate:
209
+ * Match forms, one family per grammar (`prefix` and `wildcard` are one predicate):
192
210
  * · `exact` — admits the one string the rule spells.
193
211
  * · `wildcard` — the TRAILING SPACE-STAR spelling (`Bash(npm run *)`): the body, or the body followed
194
212
  * by a space and more, inside the body's own segment count. This is the spelling upstream itself
@@ -202,15 +220,18 @@ export declare function laneToolNamesOf(req: Pick<import("./tool-policy.js").Too
202
220
  * compound and importing/minting one as a chain licence would grant what nobody approved.
203
221
  * · `subpath` — the directory form (design/382 §2.2): `Tool(//abs-dir/**)`, reaching every path under
204
222
  * one absolute directory. Never admits any command; its one predicate is {@link directoryRuleAdmits}.
205
- * As an ALLOW it exists for the Read tool only (a standing yes to reads under a directory); as a
206
- * deny/ask it is legal on every path-targeting tool.
207
- * · `path` — the PATH PATTERN form, deny/ask only: upstream's file-rule spelling over a tool's path
208
- * target (`Edit(~/.sema/settings.json)`, `Write(src/**)`, `Read(//etc/*)`), stored AS SPELLED and
223
+ * As an ALLOW it exists on the two face tools ({@link PATH_RULE_FACES}); as a deny/ask on every
224
+ * path-targeting tool.
225
+ * · `path` — the PATH PATTERN form: upstream's file-rule spelling over a tool's path target
226
+ * (`Edit(~/.sema/settings.json)`, `Write(src/**)`, `Read(//etc/*)`), stored AS SPELLED and
209
227
  * resolved at match time against the call's bases (`//` absolute, `~/` home, `/` project root,
210
- * otherwise the working directory). Its predicate is {@link pathRuleReaches}. Not an allow form: a
211
- * standing approval by path pattern would be a wider grant than any card ever shows a person.
228
+ * otherwise the working directory). Its predicate is {@link pathRuleReachOf}. As an ALLOW it exists
229
+ * on the two face tools, judged with the narrow reading (every view of the target inside the pattern).
230
+ * · `name` / `name_prefix` — the NAME grammar ({@link parseNameRuleContent}).
231
+ * · `host` / `subdomain` — the DOMAIN grammar ({@link parseDomainRuleContent}): one exact host, or every
232
+ * host below one domain.
212
233
  */
213
- export type PersistedRuleMatch = "exact" | "prefix" | "wildcard" | "subpath" | "path" | "name" | "name_prefix";
234
+ export type PersistedRuleMatch = "exact" | "prefix" | "wildcard" | "subpath" | "path" | "name" | "name_prefix" | "host" | "subdomain";
214
235
  /**
215
236
  * Where a rule applies — design/382 §4.1, the THREE consent dimensions.
216
237
  *
@@ -403,6 +424,9 @@ export declare const BARE_INTERPRETER_NAMES: ReadonlySet<string>;
403
424
  * `! node -e …` is the `node` licence the interpreter table exists to refuse, wearing one extra token.
404
425
  * `{`/`}`/`[[`/`]]` are already refused by the floor's argv[0] metacharacter rule; listed anyway,
405
426
  * because that rule belongs to another module and is not this lane's to depend on.
427
+ *
428
+ * The words are the shell-word table's `rule_reserved` role (`src/shell/shell-words.ts`): the table is where a word's
429
+ * grammar is stated once, and this lane only reads the projection.
406
430
  */
407
431
  export declare const SHELL_RESERVED_WORDS: ReadonlySet<string>;
408
432
  /**
@@ -474,6 +498,10 @@ export declare function escapeForDisclosure(value: unknown): string;
474
498
  * the scanner while it runs, and the render's own strip would then glue it back together in clear
475
499
  * AFTER the scanner missed it. Strip first, scan, then render: the render's strip is idempotent over
476
500
  * text this already stripped, so the final bytes are the same as a single render, minus the leak.
501
+ *
502
+ * It knows nothing of the engine's envelope defusal and needs to know nothing: a defused tag or
503
+ * fence sentinel (untrusted-text.ts) is spelled with printable ASCII, so this pass — and any other
504
+ * format-character cleaning a downstream layer applies to a defused string — leaves it as it was.
477
505
  */
478
506
  export declare function stripFormatCharacters(text: string): string;
479
507
  /**
@@ -547,6 +575,9 @@ export declare function formatRuleText(command: string, match: PersistedRuleMatc
547
575
  /** Does a name rule reach this name? `name`: equal; `name_prefix`: begins with it (no boundary — the reference's `startsWith`).
548
576
  * A rule of any other family reaches no name. */
549
577
  export declare function nameRuleAdmits(rule: Pick<PersistedRule, "match" | "command">, name: string): boolean;
578
+ /** Does a domain rule reach this host (as {@link domainRuleSubjectOf} reads it)? `host`: equal; `subdomain`: a name ending in
579
+ * `.<domain>` with at least one label before it and no empty label. A rule of any other family reaches no host. */
580
+ export declare function domainRuleAdmits(rule: Pick<PersistedRule, "match" | "command">, host: string): boolean;
550
581
  /**
551
582
  * The closed set of BASES a path-form pattern resolves against — facts of the CALL (its execution
552
583
  * environment and its task), never of the rule:
@@ -946,16 +977,17 @@ export declare function adjudicatePersistedRules(rules: readonly PersistedRule[]
946
977
  /**
947
978
  * The path-family sibling of {@link adjudicatePersistedRules}: the same precedence walk over the rules
948
979
  * of one path-targeting tool, judged on the call's target views by the one judgment every path rule meets
949
- * ({@link pathRowsReach}: rows outer, views inner).
980
+ * (`judgePathRows`: rows outer, views inner).
950
981
  *
951
982
  * TWO readings, because the two directions read the call's path slot differently (design/388 B6/B17): the
952
983
  * tightening arms judge the PROTECTIVE reading's views (`views.tighten` — the object face's declaration, the catalog's
953
984
  * for the name as the floor: reading the catalog for an undeclared same-name tool can only tighten a deny), every row
954
985
  * also where its head leads (`leads`, the judging leg's resolution); the allow arm judges the DECLARED reading's views
955
986
  * (`views.allow` — the face's OWN declaration only: a same-name tool that declares no path target is unconfirmable, and
956
- * no grant admits it) with the directory rows, for the Read tool ONLY — a directory allow is a standing yes to reads,
957
- * never to a write — and admits only when every view lies under the directory. Either reading may be absent (no
958
- * resolvable path in that reading); the arm that needs it then speaks for nothing.
987
+ * no grant admits it) — the NARROW reading: a row speaks for a call on a face its {@link PATH_RULE_FACES} allow column
988
+ * holds, and admits it only when it admits EVERY view (a link out of the named place is not admitted through its
989
+ * spelling, a case twin is another spelling, no row is judged where its head leads, an unreadable view is admitted by
990
+ * nothing). Either reading may be absent (no resolvable path in that reading); the arm that needs it then speaks for nothing.
959
991
  *
960
992
  * A row is anchored by `anchorsOf` (the person's rows: a project row at its scope root, the rest at the chain's top leg).
961
993
  *
@@ -989,6 +1021,18 @@ export declare function adjudicatePersistedNameRules(rules: readonly PersistedRu
989
1021
  sessionId?: string;
990
1022
  scopeIdentity?: ScopeRootIdentity;
991
1023
  }): PersistedRuleVerdict | undefined;
1024
+ /**
1025
+ * The domain-family sibling of {@link adjudicatePersistedNameRules}: the rules of one domain tool ({@link DOMAIN_RULE_FACES})
1026
+ * judged on the host the call fetches ({@link domainRuleSubjectOf}) by {@link domainRuleAdmits}; the first table-order rule
1027
+ * that is eligible and admits the host is the answer. The grammar has an allow form only, so only an allow can speak.
1028
+ */
1029
+ export declare function adjudicatePersistedDomainRules(rules: readonly PersistedRule[], call: {
1030
+ tool: string;
1031
+ host: string | undefined;
1032
+ cwd: string | undefined;
1033
+ sessionId?: string;
1034
+ scopeIdentity?: ScopeRootIdentity;
1035
+ }): PersistedRuleVerdict | undefined;
992
1036
  /**
993
1037
  * The ONE offer a name tool's approval card carries: the exact rule for the name the call carries (`Skill(<name>)`, the reference's
994
1038
  * "don't ask again for <skill>" row), or undefined when no rule spells exactly that name (the round-trip law: the canonical text,
@@ -1,4 +1,5 @@
1
1
  import { homedir } from "node:os";
2
+ import { shellWordsOf } from "../shell/shell-words.js";
2
3
  import { parsePermissionRule } from "./permission-rule-syntax.js";
3
4
  import { compileReadDeny } from "../tools/fs/read-deny.js";
4
5
  import { carriesShellRedirection, parseLeadingCommandName, splitShellCompoundSegments } from "../tools/fs/bash-readonly-classifier.js";
@@ -6,7 +7,7 @@ import { inlineUntrusted } from "./untrusted-text.js";
6
7
  import { isShellFaceToolName, pathTargetOf, protectivePathTargetOf } from "./tool-registry.js";
7
8
  import { heldValues } from "./tool-name-readings.js";
8
9
  import { carriesDotSegment, isAbsoluteForFamily, isIdentityDir, isPathIdentity, isShellRootedSpellingUnmapped, isUncIdentity, pathFamilyOf, pathIdentityOf } from "../tools/fs/safety.js";
9
- import { bindPathRules, pathRowsReach, rowsWhereTheyLead } from "./path-rule-binding.js";
10
+ import { bindPathRules, judgeRuleRows, rowsWhereTheyLead } from "./path-rule-binding.js";
10
11
  import { readShellCommand } from "./shell-lexer.js";
11
12
  import { readOnlyLineObjection, readOnlyShellVerdict } from "./read-only-shell.js";
12
13
  export const RULE_BEHAVIORS = Object.freeze(["deny", "ask", "allow"]);
@@ -30,8 +31,8 @@ if (!isShellFaceToolName(COMMAND_RULE_TOOL))
30
31
  throw new Error(`permission rules: the command rule word ${JSON.stringify(COMMAND_RULE_TOOL)} names no definition on the catalog's shell face`);
31
32
  export const READ_RULE_TOOL = "Read";
32
33
  export const PATH_RULE_FACES = Object.freeze({
33
- [READ_RULE_TOOL]: Object.freeze({ deny: Object.freeze(["read", "write"]), ask: Object.freeze(["read"]) }),
34
- Edit: Object.freeze({ deny: Object.freeze(["write"]), ask: Object.freeze(["write"]) }),
34
+ [READ_RULE_TOOL]: Object.freeze({ deny: Object.freeze(["read", "write"]), ask: Object.freeze(["read"]), allow: Object.freeze(["read"]) }),
35
+ Edit: Object.freeze({ deny: Object.freeze(["write"]), ask: Object.freeze(["write"]), allow: Object.freeze(["write", "read"]) }),
35
36
  });
36
37
  export function callPathFaceOf(call) {
37
38
  const target = protectivePathTargetOf(call);
@@ -43,13 +44,37 @@ export function faceAskSpeaksFor(face) {
43
44
  export const NAME_RULE_FACES = Object.freeze({
44
45
  Skill: Object.freeze({ param: "skill", params: Object.freeze(["skill", "args"]) }),
45
46
  });
47
+ export const DOMAIN_RULE_FACES = Object.freeze({
48
+ WebFetch: Object.freeze({ param: "url" }),
49
+ });
46
50
  export function ruleToolGrammarOf(toolName) {
47
51
  if (toolName === COMMAND_RULE_TOOL)
48
52
  return "command";
49
53
  if (Object.hasOwn(NAME_RULE_FACES, toolName))
50
54
  return "name";
55
+ if (Object.hasOwn(DOMAIN_RULE_FACES, toolName))
56
+ return "domain";
51
57
  return pathTargetOf(toolName) !== undefined ? "path" : undefined;
52
58
  }
59
+ export function domainRuleSubjectOf(toolName, args) {
60
+ const face = Object.hasOwn(DOMAIN_RULE_FACES, toolName) ? DOMAIN_RULE_FACES[toolName] : undefined;
61
+ if (face === undefined || typeof args !== "object" || args === null || Array.isArray(args) || !Object.hasOwn(args, face.param))
62
+ return undefined;
63
+ const value = args[face.param];
64
+ if (typeof value !== "string")
65
+ return undefined;
66
+ let url;
67
+ try {
68
+ url = new URL(value);
69
+ }
70
+ catch {
71
+ return undefined;
72
+ }
73
+ if ((url.protocol !== "http:" && url.protocol !== "https:") || url.username !== "" || url.password !== "")
74
+ return undefined;
75
+ const host = url.hostname.replace(/\.+$/, "");
76
+ return host === "" ? undefined : host;
77
+ }
53
78
  export function nameRuleSubjectOf(toolName, args) {
54
79
  const face = Object.hasOwn(NAME_RULE_FACES, toolName) ? NAME_RULE_FACES[toolName] : undefined;
55
80
  if (face === undefined || typeof args !== "object" || args === null || Array.isArray(args) || !Object.hasOwn(args, face.param))
@@ -83,11 +108,7 @@ export const BARE_INTERPRETER_NAMES = new Set([
83
108
  "setsid", "flock", "unshare", "nsenter", "script", "numactl", "prlimit", "systemd-run", "parallel",
84
109
  "strace", "ltrace", "valgrind", "gdb", "lldb", "perf", "firejail", "bwrap",
85
110
  ]);
86
- export const SHELL_RESERVED_WORDS = new Set([
87
- "!", "[[", "]]", "{", "}", "((", "))",
88
- "case", "esac", "coproc", "do", "done", "elif", "else", "fi", "for",
89
- "function", "if", "in", "select", "then", "time", "until", "while",
90
- ]);
111
+ export const SHELL_RESERVED_WORDS = shellWordsOf("rule_reserved");
91
112
  export const SUGGESTION_LEXICON = [
92
113
  "git stash list", "git stash show", "git stash push", "git stash pop", "git stash drop",
93
114
  "git remote show", "git worktree list", "git submodule update", "git submodule status",
@@ -274,8 +295,10 @@ export function parseRuleText(text, behavior) {
274
295
  const grammar = ruleToolGrammarOf(parsed.toolName);
275
296
  if (grammar === "name")
276
297
  return parseNameRuleContent(text, parsed.toolName, parsed.ruleContent, behavior);
298
+ if (grammar === "domain")
299
+ return parseDomainRuleContent(text, parsed.toolName, parsed.ruleContent, behavior);
277
300
  if (grammar !== "path") {
278
- return reject("unsupported.tool", `only Bash command rules, rules on path-targeting tools (Read, Edit, Write, …) and Skill name rules have a rule form in this version (got "${parsed.toolName}")`);
301
+ return reject("unsupported.tool", `only Bash command rules, rules on path-targeting tools (Read, Edit, Write, …), Skill name rules and WebFetch domain rules have a rule form in this version (got "${parsed.toolName}")`);
279
302
  }
280
303
  return parsePathRuleContent(text, parsed.toolName, parsed.ruleContent, behavior);
281
304
  }
@@ -332,6 +355,8 @@ export function formatRuleText(command, match, tool) {
332
355
  return `${tool}(//${command.startsWith("/") ? command.slice(1) : command}/**)`;
333
356
  if (match === "path" || match === "name")
334
357
  return `${tool}(${command})`;
358
+ if (match === "host" || match === "subdomain")
359
+ return `${tool}(domain:${match === "subdomain" ? "*." : ""}${command})`;
335
360
  return `${tool}(${command}${match === "prefix" || match === "name_prefix" ? ":*" : match === "wildcard" ? " *" : ""})`;
336
361
  }
337
362
  function parseNameRuleContent(text, tool, content, behavior, verifyCanonical = true) {
@@ -371,7 +396,60 @@ export function nameRuleAdmits(rule, name) {
371
396
  return name.startsWith(rule.command);
372
397
  return false;
373
398
  }
399
+ function parseDomainRuleContent(text, tool, content, behavior) {
400
+ if (behavior !== "allow") {
401
+ 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`);
402
+ }
403
+ if (!content.startsWith("domain:")) {
404
+ 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}")`);
405
+ }
406
+ const spelled = content.slice("domain:".length);
407
+ if (spelled === "*")
408
+ return reject("unsupported.wildcard", `"${text}" names every host — the whole tool is the bare ${tool} entry`);
409
+ const below = spelled.startsWith("*.");
410
+ const body = below ? spelled.slice(2) : spelled;
411
+ if (body.includes("*")) {
412
+ 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>\``);
413
+ }
414
+ const host = hostOfSpelling(body);
415
+ if (host === undefined || hostOfSpelling(host) !== host) {
416
+ 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`);
417
+ }
418
+ if (below && isAddressHost(host))
419
+ return reject("invalid.grammar", `"${text}" names hosts below an address, which has none — write the address itself`);
420
+ const match = below ? "subdomain" : "host";
421
+ return { rule: { behavior, rule: formatRuleText(host, match, tool), tool, match, command: host } };
422
+ }
423
+ function hostOfSpelling(spelled) {
424
+ if (spelled === "" || /[\s/\\@?#%]/.test(spelled) || (spelled.includes(":") && !/^\[[^\]]+\]$/.test(spelled)))
425
+ return undefined;
426
+ let url;
427
+ try {
428
+ url = new URL(`https://${spelled}/`);
429
+ }
430
+ catch {
431
+ return undefined;
432
+ }
433
+ if (url.port !== "" || url.username !== "" || url.password !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "")
434
+ return undefined;
435
+ const host = url.hostname.replace(/\.+$/, "");
436
+ return host === "" ? undefined : host;
437
+ }
438
+ function isAddressHost(host) {
439
+ return host.startsWith("[") || /^\d+\.\d+\.\d+\.\d+$/.test(host);
440
+ }
441
+ export function domainRuleAdmits(rule, host) {
442
+ if (rule.match === "host")
443
+ return host === rule.command;
444
+ if (rule.match !== "subdomain" || isAddressHost(host) || !host.endsWith(`.${rule.command}`))
445
+ return false;
446
+ const labels = host.slice(0, -(rule.command.length + 1));
447
+ return labels !== "" && !labels.split(".").includes("");
448
+ }
374
449
  function parsePathRuleContent(text, tool, content, behavior) {
450
+ if (behavior === "allow" && PATH_RULE_FACES[tool]?.allow === undefined) {
451
+ return reject("unsupported.tool", `an allow by path is read on Read and Edit rules only — an Edit rule speaks for every tool that edits a file, a Read rule for every tool that reads one ("${text}"); write Edit(<path>) or Read(<path>)`);
452
+ }
375
453
  const dirForm = /^\/\/(.+)\/\*\*$/.exec(content);
376
454
  if (dirForm !== null) {
377
455
  const bodyR = dirForm[1];
@@ -385,17 +463,9 @@ function parsePathRuleContent(text, tool, content, behavior) {
385
463
  if (segments.some((sg) => sg === "." || sg === "..")) {
386
464
  return reject("invalid.path_traversal", `the directory body of "${text}" carries a \`.\` or \`..\` segment — the lexical normal form is the only accepted spelling, and a traversal segment is not normalized away`);
387
465
  }
388
- if (behavior === "allow" && tool !== READ_RULE_TOOL) {
389
- return reject("unsupported.tool", `an allow rule on a path tool speaks for the Read directory form only — a standing approval of a WRITE by directory is not a rule form ("${text}"); write it as a deny or an ask, or approve the call`);
390
- }
391
466
  const dir = "/" + bodyR;
392
467
  return { rule: { behavior, rule: formatRuleText(dir, "subpath", tool), tool, match: "subpath", command: dir } };
393
468
  }
394
- if (behavior === "allow") {
395
- return tool === READ_RULE_TOOL
396
- ? reject("invalid.grammar", `only the Read(//abs-dir/**) directory form is a rule this version reads ("${text}") — relative, single-file, single-slash and no-tail spellings have no rule form`)
397
- : reject("unsupported.tool", `an allow rule on "${tool}" has no rule form — only the Read directory form is a standing approval by path ("${text}"); write it as a deny or an ask, or approve the call`);
398
- }
399
469
  if (content.trim() !== content || content === "") {
400
470
  return reject("invalid.grammar", `the path pattern of "${text}" is empty or carries surrounding whitespace — the pattern is stored as spelled, so the spelling must be the one meant`);
401
471
  }
@@ -458,7 +528,7 @@ function resolvePathPattern(pattern, bases, behavior, treeFamily = pathFamilyOf(
458
528
  }
459
529
  const needed = ruleBasesNeeded({ match: "path", command: pattern })[0];
460
530
  const baseValue = baseValueOf(needed, bases);
461
- const base = baseValue !== undefined && isAbsoluteForFamily(family, baseValue) ? pathIdentityOf(baseValue, family, tree) : undefined;
531
+ const base = baseValue !== undefined && isAbsoluteForFamily(family ?? "own", baseValue) ? pathIdentityOf(baseValue, family, tree) : undefined;
462
532
  if (base === undefined || !base.ok)
463
533
  return { missingBase: needed };
464
534
  const normal = base.identity;
@@ -470,7 +540,7 @@ function resolvePathPattern(pattern, bases, behavior, treeFamily = pathFamilyOf(
470
540
  return { resolved: (normal === "/" ? "" : normal) + "/" + body };
471
541
  }
472
542
  export function isUsablePathBase(base, family) {
473
- return base !== undefined && isAbsoluteForFamily(family, base) && pathIdentityOf(base, family, base).ok;
543
+ return base !== undefined && isAbsoluteForFamily(family ?? "own", base) && pathIdentityOf(base, family, base).ok;
474
544
  }
475
545
  function segmentGlobMatches(glob, segment) {
476
546
  if (!glob.includes("*"))
@@ -790,7 +860,7 @@ function userHomeTopLevel() {
790
860
  }
791
861
  return HOME_TOP_LEVEL === null ? undefined : HOME_TOP_LEVEL;
792
862
  }
793
- const CURSOR_MOVING_HEADS = new Set(["cd", "pushd", "popd"]);
863
+ const CURSOR_MOVING_HEADS = shellWordsOf("base_mover");
794
864
  const OPAQUE_CURSOR_MOVERS = new Set(["eval", "source", ".", "trap", "enable", "builtin", "command", "time"]);
795
865
  function pushdPopdLeavesCursor(head, args) {
796
866
  for (const a of args) {
@@ -817,7 +887,7 @@ function movesShellCursor(segment) {
817
887
  return true;
818
888
  if (!CURSOR_MOVING_HEADS.has(head))
819
889
  return false;
820
- return !((head === "pushd" || head === "popd") && pushdPopdLeavesCursor(head, words.slice(1)));
890
+ return !(shellWordsOf("stack_mover").has(head) && pushdPopdLeavesCursor(head, words.slice(1)));
821
891
  }
822
892
  function resolveCdSegmentDirectories(segments, cwd) {
823
893
  let priorCd = false;
@@ -934,9 +1004,11 @@ export function adjudicatePersistedPathRules(rules, call, views, anchorsOf, lead
934
1004
  const bind = (rows) => bindPathRules(rows.map((rule) => ({ parsed: rule, ruleText: rule.rule, rule })), (s) => anchorsOf(s.rule));
935
1005
  for (const behavior of RULE_BEHAVIORS_BY_PRECEDENCE) {
936
1006
  if (behavior === "allow") {
937
- if (views.allow === undefined || call.tool !== READ_RULE_TOOL)
1007
+ const face = call.face;
1008
+ if (views.allow === undefined || face === undefined)
938
1009
  continue;
939
- const hit = pathRowsReach(bind(rules.filter((rule) => rule.match === "subpath" && eligiblePersisted(rule, call, behavior))), views.allow, "allow");
1010
+ const allowing = rules.filter((rule) => (rule.match === "path" || rule.match === "subpath") && PATH_RULE_FACES[rule.tool]?.allow?.includes(face) === true && eligiblePersisted(rule, { ...call, tool: rule.tool }, behavior));
1011
+ const hit = judgeRuleRows(bind(allowing), views.allow, "allow");
940
1012
  if (hit !== undefined)
941
1013
  return { behavior, rules: [hit.row.spec.rule] };
942
1014
  continue;
@@ -946,7 +1018,7 @@ export function adjudicatePersistedPathRules(rules, call, views, anchorsOf, lead
946
1018
  const rows = rules.filter((rule) => (rule.match === "path" || rule.match === "subpath") && speaks(rule, behavior));
947
1019
  if (rows.length === 0)
948
1020
  continue;
949
- const hit = pathRowsReach(rowsWhereTheyLead(bind(rows), leads), views.tighten, "tighten");
1021
+ const hit = judgeRuleRows(rowsWhereTheyLead(bind(rows), leads), views.tighten, "tighten");
950
1022
  if (hit === undefined)
951
1023
  continue;
952
1024
  return hit.unreadable !== undefined ? { behavior: "ask", rules: [hit.row.spec.rule], unreadable: hit.unreadable } : { behavior, rules: [hit.row.spec.rule] };
@@ -964,6 +1036,15 @@ export function adjudicatePersistedNameRules(rules, call) {
964
1036
  }
965
1037
  return undefined;
966
1038
  }
1039
+ export function adjudicatePersistedDomainRules(rules, call) {
1040
+ if (call.host === undefined)
1041
+ return undefined;
1042
+ for (const rule of rules) {
1043
+ if (eligiblePersisted(rule, call, "allow") && domainRuleAdmits(rule, call.host))
1044
+ return { behavior: "allow", rules: [rule] };
1045
+ }
1046
+ return undefined;
1047
+ }
967
1048
  export function suggestRuleForName(tool, name) {
968
1049
  if (ruleToolGrammarOf(tool) !== "name")
969
1050
  return undefined;
@@ -2,7 +2,7 @@ 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
4
  import { thrownText } from "./thrown-value.js";
5
- import { isAbsolutePathForm } from "../tools/fs/safety.js";
5
+ import { validatePath } from "./effective-path-target.js";
6
6
  export function sameRuleOwner(a, b) {
7
7
  return a.kind === "principal" ? b.kind === "principal" && a.principal === b.principal : b.kind === "local-owner";
8
8
  }
@@ -152,7 +152,7 @@ export function isValidDurableScope(scope) {
152
152
  return sc?.kind === "global" || (sc?.kind === "project" && isProjectScopeRoot(sc.root));
153
153
  }
154
154
  export function isProjectScopeRoot(root) {
155
- return typeof root === "string" && isAbsolutePathForm(root);
155
+ return validatePath(root, { intent: "validate-coordinate" }) !== undefined;
156
156
  }
157
157
  function isValidDot(dot) {
158
158
  const d = dot;
@@ -36,7 +36,7 @@
36
36
  * `//` absolute, `~/` home, `/` project root, otherwise working-directory relative; `*` within a segment,
37
37
  * `**` across) and is judged on the call's PROTECTIVE path target (design/388: the object face's declaration, the
38
38
  * catalog's for the name as the floor), every view of it (`callTargetViewsOf`), by the one judgment every path rule
39
- * meets (`pathRowsReach`). A pattern's ANCHORS are the rule's own fact, taken once where the policy is installed: the
39
+ * meets (`judgePathRows`). A pattern's ANCHORS are the rule's own fact, taken once where the policy is installed: the
40
40
  * declared `pathBases` word by word, else the leg the Runner installs the policy on (`policyAnchors`) — never the
41
41
  * call's live working directory. A declared base that is not a usable absolute path is refused at compile time
42
42
  * (`unsupported.path_base`); a policy called outside a Runner leg with no declared base reads such a rule
@@ -158,35 +158,8 @@ export interface PermissionRulePolicyOptions {
158
158
  */
159
159
  pathBases?: PathRuleBases;
160
160
  }
161
- /**
162
- * Linear-time `*` glob match (design/127 M6: NOT a RegExp — patterns may come from tenant-supplied
163
- * Settings layers; the classic two-pointer walk has zero ReDoS surface and the same semantics as CC's
164
- * anchored `^…$`+dotAll construction: every non-`*` char is literal, `*` spans anything incl. newlines).
165
- */
166
- export declare function wildcardMatch(pattern: string, value: string): boolean;
167
- /**
168
- * Does a namespaced RULE name cover `toolName`? — the tail branch of CC's rule matcher `VRp`
169
- * (:595780), which is UNCONDITIONAL there (its `globMatching` option gates only the whole-name glob
170
- * branch, so CC's allow lane — `mVo` :595803, which passes no options — matches MCP segments too).
171
- * Three covering forms, matching CC's three disjuncts (`<ns>` = any protocol-table prefix):
172
- * · `<ns>__<peer>` — every tool of that peer (rule tool segment absent);
173
- * · `<ns>__<peer>__*` — the same set, spelled with the star CC's own settings validator
174
- * recommends (`"MCP rules do not support patterns in parentheses …
175
- * use mcp__srv__*"`);
176
- * · `<ns>__<peer>__get_*` — a glob over the tool segment.
177
- * A LITERAL tool segment is deliberately NOT covered here: it is an exact tool name, and the exact
178
- * table already answers it — the same split CC makes with its leading `ruleName === toolName`.
179
- *
180
- * Rule and target must be in the SAME namespace, and peer segments compare EXACTLY (CC
181
- * `s.serverName === a.serverName`). A rule must therefore spell the peer the way the mint does; that
182
- * is the same string an operator reads off any mounted tool name, and inventing a fuzzier comparison
183
- * here would let one rule reach a peer — or a protocol — it does not name.
184
- */
185
- export declare function namespacedRuleNameCovers(ruleName: string, toolName: string): boolean;
186
- /** True ⇔ this rule name is a namespaced name whose reach is a SET of tools rather than one exact tool
187
- * — i.e. the shapes {@link namespacedRuleNameCovers} answers and an exact name table cannot. Every
188
- * lane that keys rules by exact tool name needs this to know which entries it must NOT key that way. */
189
- export declare function isNamespacedCoveringRuleName(name: string): boolean;
161
+ import { isNamespacedCoveringRuleName, namespacedRuleNameCovers, wildcardMatch } from "./namespaced-rule-name.js";
162
+ export { isNamespacedCoveringRuleName, namespacedRuleNameCovers, wildcardMatch };
190
163
  /**
191
164
  * design/388 A-6/7/8/9 (D-3): the per-tool RULE FACE — the parameters a `Tool(param:pattern)` rule may name
192
165
  * (`params`, the SCALAR top-level keys ∪ the path target's aliases), the ones the generic lexical matcher must