@sema-agent/core 7.55.0 → 7.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (418) hide show
  1. package/CHANGELOG.md +513 -0
  2. package/dist/agents/cascade.js +1 -1
  3. package/dist/agents/cc-peer-directory.js +1 -0
  4. package/dist/agents/child-model-seat.d.ts +0 -11
  5. package/dist/agents/child-model-seat.js +0 -3
  6. package/dist/agents/list-agents-tool.d.ts +3 -1
  7. package/dist/agents/list-agents-tool.js +7 -7
  8. package/dist/agents/peer-directory.d.ts +22 -4
  9. package/dist/agents/peer-directory.js +24 -2
  10. package/dist/agents/peer-session-drain.js +2 -2
  11. package/dist/agents/repair-loop.js +2 -2
  12. package/dist/agents/send-message-tool.js +4 -4
  13. package/dist/agents/subagent.d.ts +6 -4
  14. package/dist/agents/subagent.js +64 -60
  15. package/dist/agents/tool-filter.d.ts +7 -5
  16. package/dist/agents/tool-filter.js +12 -9
  17. package/dist/agents/verify.d.ts +3 -2
  18. package/dist/agents/verify.js +3 -3
  19. package/dist/brain/anthropic.js +30 -73
  20. package/dist/brain/media-degrade.d.ts +18 -7
  21. package/dist/brain/media-degrade.js +13 -2
  22. package/dist/brain/model-capability.d.ts +37 -0
  23. package/dist/brain/model-capability.js +34 -0
  24. package/dist/brain/model-presets.d.ts +2 -8
  25. package/dist/brain/model-presets.js +1 -6
  26. package/dist/brain/open-responses.js +13 -26
  27. package/dist/brain/openai.js +14 -87
  28. package/dist/brain/reasoning-request.d.ts +96 -0
  29. package/dist/brain/reasoning-request.js +174 -0
  30. package/dist/brain/reasoning.d.ts +11 -11
  31. package/dist/brain/reasoning.js +4 -3
  32. package/dist/brain/request-params.d.ts +7 -4
  33. package/dist/brain/request-params.js +7 -4
  34. package/dist/brain/status-sink.d.ts +20 -0
  35. package/dist/brain/status-sink.js +7 -0
  36. package/dist/core/a2a.d.ts +2 -1
  37. package/dist/core/a2a.js +19 -13
  38. package/dist/core/agent-definition.d.ts +10 -0
  39. package/dist/core/allow-entry.d.ts +20 -13
  40. package/dist/core/allow-entry.js +26 -22
  41. package/dist/core/allow-rule-shadow.d.ts +3 -3
  42. package/dist/core/ask-origin.d.ts +64 -18
  43. package/dist/core/ask-origin.js +63 -23
  44. package/dist/core/ask-question.d.ts +51 -8
  45. package/dist/core/ask-question.js +3 -4
  46. package/dist/core/ask-unresolvable-notice.d.ts +8 -3
  47. package/dist/core/ask-unresolvable-notice.js +16 -2
  48. package/dist/core/checkpoint-generation.d.ts +53 -0
  49. package/dist/core/checkpoint-generation.js +35 -0
  50. package/dist/core/checkpoint-queries.d.ts +180 -0
  51. package/dist/core/checkpoint-queries.js +272 -0
  52. package/dist/core/checkpoint-requirements.d.ts +13 -1
  53. package/dist/core/checkpoint-requirements.js +4 -0
  54. package/dist/core/checkpoint-store.d.ts +88 -24
  55. package/dist/core/checkpoint-store.js +24 -9
  56. package/dist/core/cross-boundary-taint.d.ts +6 -5
  57. package/dist/core/cross-boundary-taint.js +1 -3
  58. package/dist/core/decision-receipt.d.ts +37 -3
  59. package/dist/core/decision-receipt.js +17 -17
  60. package/dist/core/delegation-not-started.d.ts +6 -0
  61. package/dist/core/delegation-not-started.js +17 -0
  62. package/dist/core/engine-attestation.d.ts +93 -0
  63. package/dist/core/engine-attestation.js +70 -0
  64. package/dist/core/engine-capabilities.d.ts +4 -1
  65. package/dist/core/engine-capabilities.js +1 -1
  66. package/dist/core/engine-notice.d.ts +30 -11
  67. package/dist/core/file-history-store.d.ts +90 -14
  68. package/dist/core/file-history-store.js +23 -15
  69. package/dist/core/gate-fold.d.ts +1 -1
  70. package/dist/core/gate-fold.js +22 -10
  71. package/dist/core/gate-lanes.js +73 -64
  72. package/dist/core/gate-outcome.d.ts +26 -5
  73. package/dist/core/gate-outcome.js +26 -0
  74. package/dist/core/governance-codes.d.ts +5 -1
  75. package/dist/core/governance-codes.js +6 -0
  76. package/dist/core/hooks.d.ts +17 -16
  77. package/dist/core/hooks.js +3 -1
  78. package/dist/core/host-floor.d.ts +128 -0
  79. package/dist/core/host-floor.js +206 -0
  80. package/dist/core/host-target.d.ts +38 -3
  81. package/dist/core/host-target.js +38 -5
  82. package/dist/core/mcp.d.ts +5 -1
  83. package/dist/core/mcp.js +67 -41
  84. package/dist/core/memory-engine/delegation-settlement.js +15 -24
  85. package/dist/core/memory-engine/engine.js +4 -4
  86. package/dist/core/memory-engine/layout.d.ts +60 -8
  87. package/dist/core/memory-engine/layout.js +65 -6
  88. package/dist/core/model-seat.d.ts +10 -3
  89. package/dist/core/mods/classic-events.d.ts +67 -0
  90. package/dist/core/mods/classic-events.js +1 -0
  91. package/dist/core/mods/dollar.d.ts +13 -0
  92. package/dist/core/mods/extension-call.d.ts +31 -0
  93. package/dist/core/mods/extension-call.js +43 -0
  94. package/dist/core/mods/kernel/dispatch.d.ts +20 -3
  95. package/dist/core/mods/kernel/dispatch.js +140 -69
  96. package/dist/core/mods/kernel/events.d.ts +47 -11
  97. package/dist/core/mods/kernel/events.js +66 -42
  98. package/dist/core/mods/kernel/index.d.ts +4 -4
  99. package/dist/core/mods/kernel/index.js +2 -2
  100. package/dist/core/mods/kernel/receive.d.ts +39 -5
  101. package/dist/core/mods/kernel/receive.js +290 -17
  102. package/dist/core/mods/kernel/registration.d.ts +33 -2
  103. package/dist/core/mods/kernel/registration.js +6 -7
  104. package/dist/core/mods/kernel/site-rules.d.ts +44 -16
  105. package/dist/core/mods/kernel/site-rules.js +24 -42
  106. package/dist/core/mods/spec-seats.d.ts +69 -0
  107. package/dist/core/mods/spec-seats.js +114 -0
  108. package/dist/core/mods/stations.d.ts +28 -8
  109. package/dist/core/mods/stations.js +29 -13
  110. package/dist/core/mods/tool-call.d.ts +79 -0
  111. package/dist/core/mods/tool-call.js +152 -0
  112. package/dist/core/namespaced-rule-name.d.ts +32 -38
  113. package/dist/core/namespaced-rule-name.js +29 -36
  114. package/dist/core/parent-spec-seat.d.ts +6 -1
  115. package/dist/core/parent-spec-seat.js +2 -0
  116. package/dist/core/park-selfcheck.js +1 -0
  117. package/dist/core/permission-rule-consent.d.ts +19 -0
  118. package/dist/core/permission-rule-consent.js +116 -26
  119. package/dist/core/permission-rule-model.d.ts +14 -3
  120. package/dist/core/permission-rule-model.js +39 -24
  121. package/dist/core/permission-rule-org.d.ts +6 -2
  122. package/dist/core/permission-rule-org.js +7 -3
  123. package/dist/core/permission-rule-provider.d.ts +5 -2
  124. package/dist/core/permission-rule-provider.js +41 -11
  125. package/dist/core/permission-rule-store.d.ts +3 -2
  126. package/dist/core/permission-rule-store.js +11 -2
  127. package/dist/core/permission-rule-sync.js +3 -2
  128. package/dist/core/permission-rule-syntax.d.ts +5 -4
  129. package/dist/core/permission-rule-syntax.js +2 -2
  130. package/dist/core/permission-rules.d.ts +4 -4
  131. package/dist/core/permission-rules.js +11 -20
  132. package/dist/core/persisted-rule-arms.d.ts +2 -2
  133. package/dist/core/persisted-rule-arms.js +10 -12
  134. package/dist/core/principal-name.d.ts +20 -0
  135. package/dist/core/principal-name.js +22 -0
  136. package/dist/core/prompt-section-seat.js +4 -6
  137. package/dist/core/protocol-http-auth.d.ts +26 -5
  138. package/dist/core/protocol-http-auth.js +137 -4
  139. package/dist/core/retired-keys.d.ts +16 -0
  140. package/dist/core/retired-keys.js +16 -4
  141. package/dist/core/rewrite-rejudge.d.ts +60 -0
  142. package/dist/core/rewrite-rejudge.js +36 -0
  143. package/dist/core/roles.d.ts +28 -1
  144. package/dist/core/roles.js +31 -19
  145. package/dist/core/rule-lane-reads.d.ts +20 -15
  146. package/dist/core/rule-lane-reads.js +45 -17
  147. package/dist/core/rule-owner.d.ts +3 -3
  148. package/dist/core/rule-owner.js +3 -2
  149. package/dist/core/runner/announce-once-ledger.d.ts +14 -0
  150. package/dist/core/runner/announce-once-ledger.js +5 -0
  151. package/dist/core/runner/assemble-result.d.ts +1 -0
  152. package/dist/core/runner/assemble-result.js +1 -1
  153. package/dist/core/runner/call-hook-stations.d.ts +3 -2
  154. package/dist/core/runner/caller-roster.d.ts +16 -4
  155. package/dist/core/runner/caller-roster.js +33 -6
  156. package/dist/core/runner/capability-withheld-seat.d.ts +5 -0
  157. package/dist/core/runner/capability-withheld-seat.js +24 -0
  158. package/dist/core/runner/classifier-ladder.d.ts +7 -9
  159. package/dist/core/runner/classifier-ladder.js +3 -3
  160. package/dist/core/runner/construction-doors.js +3 -0
  161. package/dist/core/runner/contracts.d.ts +28 -25
  162. package/dist/core/runner/cross-boundary-lane.d.ts +17 -13
  163. package/dist/core/runner/cross-boundary-lane.js +40 -27
  164. package/dist/core/runner/denial-limit-arms.d.ts +2 -2
  165. package/dist/core/runner/denial-limit-arms.js +1 -1
  166. package/dist/core/runner/file-history-request.d.ts +18 -0
  167. package/dist/core/runner/file-history-request.js +21 -13
  168. package/dist/core/runner/file-history-restore.d.ts +45 -0
  169. package/dist/core/runner/file-history-restore.js +46 -0
  170. package/dist/core/runner/gate-exit.d.ts +3 -3
  171. package/dist/core/runner/gate-exit.js +4 -3
  172. package/dist/core/runner/image.d.ts +7 -17
  173. package/dist/core/runner/image.js +9 -60
  174. package/dist/core/runner/permission-rule-lanes.d.ts +4 -4
  175. package/dist/core/runner/permission-rule-lanes.js +26 -10
  176. package/dist/core/runner/person-path-rows.d.ts +12 -9
  177. package/dist/core/runner/person-path-rows.js +18 -7
  178. package/dist/core/runner/prepare-ask-lane.d.ts +2 -2
  179. package/dist/core/runner/prepare-ask-lane.js +5 -2
  180. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -6
  181. package/dist/core/runner/prepare-caps-and-workflow.js +4 -3
  182. package/dist/core/runner/prepare-config-doors.d.ts +29 -38
  183. package/dist/core/runner/prepare-config-doors.js +42 -25
  184. package/dist/core/runner/prepare-defer-classify.d.ts +4 -3
  185. package/dist/core/runner/prepare-defer-classify.js +7 -8
  186. package/dist/core/runner/prepare-file-history.d.ts +48 -8
  187. package/dist/core/runner/prepare-file-history.js +7 -39
  188. package/dist/core/runner/prepare-gate-stations.d.ts +1 -1
  189. package/dist/core/runner/prepare-gate-stations.js +10 -1
  190. package/dist/core/runner/prepare-hands-readface.d.ts +2 -8
  191. package/dist/core/runner/prepare-hands-readface.js +15 -14
  192. package/dist/core/runner/prepare-inherited-gate.d.ts +6 -3
  193. package/dist/core/runner/prepare-inherited-gate.js +5 -3
  194. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  195. package/dist/core/runner/prepare-park-ask.js +5 -5
  196. package/dist/core/runner/prepare-policy-chain.d.ts +6 -3
  197. package/dist/core/runner/prepare-policy-chain.js +65 -72
  198. package/dist/core/runner/prepare-prompt-assembly.d.ts +5 -7
  199. package/dist/core/runner/prepare-prompt-assembly.js +10 -8
  200. package/dist/core/runner/prepare-prompt-inputs.d.ts +11 -9
  201. package/dist/core/runner/prepare-prompt-inputs.js +3 -5
  202. package/dist/core/runner/prepare-protocol-tools.d.ts +2 -0
  203. package/dist/core/runner/prepare-protocol-tools.js +3 -3
  204. package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
  205. package/dist/core/runner/prepare-safety-scan.js +0 -17
  206. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
  207. package/dist/core/runner/prepare-suspend-saga.js +5 -6
  208. package/dist/core/runner/prepare-task.d.ts +1 -1
  209. package/dist/core/runner/prepare-task.js +29 -20
  210. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +7 -4
  211. package/dist/core/runner/prepare-tool-disclosure-mount.js +20 -16
  212. package/dist/core/runner/prepare-turn-wiring.d.ts +0 -4
  213. package/dist/core/runner/prepare-turn-wiring.js +2 -7
  214. package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -2
  215. package/dist/core/runner/prepare-wiring-manifest.js +3 -3
  216. package/dist/core/runner/resume-admission.js +1 -1
  217. package/dist/core/runner/resume-apply.js +11 -12
  218. package/dist/core/runner/resume-checkpoint-screen.js +3 -0
  219. package/dist/core/runner/resume-claim.d.ts +4 -2
  220. package/dist/core/runner/resume-claim.js +15 -8
  221. package/dist/core/runner/retired-runner-deps.js +1 -0
  222. package/dist/core/runner/rewind-files.d.ts +15 -0
  223. package/dist/core/runner/rewind-files.js +132 -0
  224. package/dist/core/runner/run-attachment-seats.js +2 -1
  225. package/dist/core/runner/run-brain-sinks.d.ts +4 -1
  226. package/dist/core/runner/run-brain-sinks.js +4 -2
  227. package/dist/core/runner/run-harness-handlers.js +12 -4
  228. package/dist/core/runner/run-leg.d.ts +1 -1
  229. package/dist/core/runner/run-leg.js +30 -18
  230. package/dist/core/runner/run-settle-and-teardown.js +2 -1
  231. package/dist/core/runner/run-turn-boundary.js +1 -10
  232. package/dist/core/runner/runtask.d.ts +3 -0
  233. package/dist/core/runner/runtask.js +8 -9
  234. package/dist/core/runner/session-read-file-states.d.ts +15 -0
  235. package/dist/core/runner/session-read-file-states.js +28 -0
  236. package/dist/core/runner/session-rule-policy.js +4 -3
  237. package/dist/core/runner/shell-env-withheld.d.ts +33 -0
  238. package/dist/core/runner/shell-env-withheld.js +51 -0
  239. package/dist/core/runner/skills-seat.js +6 -2
  240. package/dist/core/runner/stream-settle-backstop.js +1 -1
  241. package/dist/core/runner/sub-agent-deny-lists.js +4 -7
  242. package/dist/core/runner/synthetic-tools.d.ts +6 -0
  243. package/dist/core/runner/synthetic-tools.js +4 -1
  244. package/dist/core/runner/tool-disclosure.d.ts +10 -12
  245. package/dist/core/runner/tool-disclosure.js +1 -3
  246. package/dist/core/runner/tool-end-body.d.ts +1 -1
  247. package/dist/core/runner/tool-end-body.js +10 -9
  248. package/dist/core/runner/tool-not-executed.d.ts +5 -0
  249. package/dist/core/runner/tool-not-executed.js +11 -0
  250. package/dist/core/runner/tool-occurrence.d.ts +8 -0
  251. package/dist/core/runner/tool-occurrence.js +7 -0
  252. package/dist/core/runner/turn-attachments.d.ts +33 -15
  253. package/dist/core/runner/turn-attachments.js +62 -37
  254. package/dist/core/runner-deps.d.ts +5 -5
  255. package/dist/core/safety-axis-vocab.d.ts +6 -4
  256. package/dist/core/safety-axis-vocab.js +6 -5
  257. package/dist/core/safety-merge-corpus.js +11 -3
  258. package/dist/core/secret-env.d.ts +26 -3
  259. package/dist/core/secret-env.js +44 -8
  260. package/dist/core/select-model.js +3 -3
  261. package/dist/core/session-policy-store.d.ts +27 -15
  262. package/dist/core/session-policy-store.js +156 -36
  263. package/dist/core/session-store.d.ts +29 -10
  264. package/dist/core/session-store.js +51 -19
  265. package/dist/core/session.d.ts +82 -16
  266. package/dist/core/session.js +53 -0
  267. package/dist/core/shell-read-silence.d.ts +11 -12
  268. package/dist/core/shell-read-silence.js +9 -5
  269. package/dist/core/shell-run-candidates.js +3 -1
  270. package/dist/core/shell-wrapper-projection.js +1 -1
  271. package/dist/core/shell-wrapper-table.d.ts +14 -7
  272. package/dist/core/shell-wrapper-table.js +5 -4
  273. package/dist/core/side-query.js +10 -1
  274. package/dist/core/skill-grant.d.ts +7 -2
  275. package/dist/core/skill-grant.js +5 -0
  276. package/dist/core/skills-directory.js +6 -2
  277. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +11 -1
  278. package/dist/core/store-contracts/checkpoint-store-contract.js +140 -2
  279. package/dist/core/store-contracts/file-history-store-contract.js +31 -1
  280. package/dist/core/store-contracts/peer-directory-contract.d.ts +7 -2
  281. package/dist/core/store-contracts/peer-directory-contract.js +14 -2
  282. package/dist/core/store-contracts/session-repo-contract.js +51 -0
  283. package/dist/core/store-contracts/session-store-contract.d.ts +2 -1
  284. package/dist/core/store-contracts/session-store-contract.js +150 -14
  285. package/dist/core/stub-env.d.ts +2 -0
  286. package/dist/core/stub-env.js +1 -0
  287. package/dist/core/synthetic-read-verdict.js +3 -3
  288. package/dist/core/task-event.d.ts +32 -4
  289. package/dist/core/task-notification.d.ts +16 -12
  290. package/dist/core/task-registry.d.ts +9 -0
  291. package/dist/core/task-registry.js +11 -1
  292. package/dist/core/task-result.d.ts +18 -0
  293. package/dist/core/task-spec.d.ts +18 -13
  294. package/dist/core/tighten-task-spec.js +2 -1
  295. package/dist/core/tool-catalog-entries.d.ts +17 -0
  296. package/dist/core/tool-catalog-entries.js +13 -2
  297. package/dist/core/tool-check-reading.d.ts +11 -0
  298. package/dist/core/tool-check-reading.js +11 -6
  299. package/dist/core/tool-model-gate.d.ts +2 -2
  300. package/dist/core/tool-name-aliases.d.ts +16 -16
  301. package/dist/core/tool-name-aliases.js +13 -19
  302. package/dist/core/tool-name-readings.d.ts +5 -35
  303. package/dist/core/tool-name-readings.js +1 -9
  304. package/dist/core/tool-policy.d.ts +68 -198
  305. package/dist/core/tool-policy.js +149 -198
  306. package/dist/core/tool-result-store.js +3 -0
  307. package/dist/core/tool-roster.d.ts +14 -19
  308. package/dist/core/tool-roster.js +11 -44
  309. package/dist/core/tool-spec-members.js +7 -2
  310. package/dist/core/tool-spec.d.ts +19 -11
  311. package/dist/core/tools.js +49 -9
  312. package/dist/core/trace.d.ts +2 -2
  313. package/dist/core/types.d.ts +1 -1
  314. package/dist/core/user-attachment.d.ts +8 -1
  315. package/dist/core/user-attachment.js +64 -4
  316. package/dist/core/wiring-manifest.d.ts +9 -0
  317. package/dist/core/wiring-manifest.js +1 -0
  318. package/dist/core/workflow-journal-store.js +1 -1
  319. package/dist/engine/compaction/compaction.js +3 -2
  320. package/dist/engine/execution-env/node-execution-env.d.ts +7 -3
  321. package/dist/engine/execution-env/node-execution-env.js +11 -2
  322. package/dist/engine/harness/agent-harness.d.ts +25 -1
  323. package/dist/engine/harness/agent-harness.js +54 -7
  324. package/dist/engine/harness/types.d.ts +29 -2
  325. package/dist/engine/llm/types.d.ts +28 -11
  326. package/dist/engine/loop/agent-loop.d.ts +22 -0
  327. package/dist/engine/loop/agent-loop.js +16 -26
  328. package/dist/engine/loop/types.d.ts +16 -6
  329. package/dist/engine/session/memory-repo.d.ts +9 -2
  330. package/dist/engine/session/memory-repo.js +11 -1
  331. package/dist/engine/session/repo-utils.d.ts +6 -5
  332. package/dist/engine/session/repo-utils.js +14 -6
  333. package/dist/index.d.ts +22 -10
  334. package/dist/index.js +15 -7
  335. package/dist/internal/harness-types.d.ts +1 -1
  336. package/dist/internal/llm.d.ts +1 -1
  337. package/dist/orchestration/run-workflow-tool.d.ts +6 -4
  338. package/dist/orchestration/run-workflow-tool.js +5 -3
  339. package/dist/orchestration/workflow.d.ts +3 -0
  340. package/dist/orchestration/workflow.js +36 -18
  341. package/dist/prompt-assembly/artifact.d.ts +2 -2
  342. package/dist/prompt-assembly/artifact.js +5 -3
  343. package/dist/prompt-assembly/assemble.d.ts +3 -2
  344. package/dist/prompt-assembly/assemble.js +1 -1
  345. package/dist/prompt-assembly/catalog/schema.d.ts +43 -12
  346. package/dist/prompt-assembly/catalog/schema.js +34 -2
  347. package/dist/prompt-assembly/catalog/sections.js +0 -1
  348. package/dist/prompt-assembly/composer.d.ts +3 -1
  349. package/dist/prompt-assembly/composer.js +1 -1
  350. package/dist/prompt-assembly/event-registry.js +1 -0
  351. package/dist/prompt-assembly/overlay.js +3 -3
  352. package/dist/prompt-assembly/renderers.d.ts +1 -1
  353. package/dist/prompt-assembly/renderers.js +0 -3
  354. package/dist/prompt-assembly/role-base.d.ts +15 -0
  355. package/dist/prompt-assembly/role-base.js +9 -0
  356. package/dist/prompt-assembly/select.d.ts +39 -14
  357. package/dist/prompt-assembly/select.js +47 -16
  358. package/dist/prompt-assembly/sugar.d.ts +17 -11
  359. package/dist/prompt-assembly/sugar.js +8 -3
  360. package/dist/prompt-assembly/tool-catalog.d.ts +37 -3
  361. package/dist/prompt-assembly/tool-catalog.js +114 -4
  362. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -10
  363. package/dist/prompt-assembly/turn-snapshot.js +0 -3
  364. package/dist/prompt-assembly/types.d.ts +0 -7
  365. package/dist/scenarios/full-body.d.ts +11 -11
  366. package/dist/scenarios/full-body.js +4 -3
  367. package/dist/shell/lex.js +10 -1
  368. package/dist/stores/file/adoption/adopt.js +4 -3
  369. package/dist/stores/file/adoption/marker.js +3 -2
  370. package/dist/stores/file/checkpoint-store.d.ts +21 -0
  371. package/dist/stores/file/checkpoint-store.js +38 -6
  372. package/dist/stores/file/file-history-store.js +6 -7
  373. package/dist/stores/file/index.js +2 -1
  374. package/dist/stores/file/permission-rule-adopt.js +4 -3
  375. package/dist/stores/file/permission-rule-store.js +11 -5
  376. package/dist/stores/file/session-store.d.ts +29 -6
  377. package/dist/stores/file/session-store.js +42 -50
  378. package/dist/tools/artifact/artifact-tool.js +3 -2
  379. package/dist/tools/fs/bash-program-position.d.ts +19 -2
  380. package/dist/tools/fs/bash-program-position.js +56 -9
  381. package/dist/tools/fs/fs-bash.js +5 -5
  382. package/dist/tools/fs/fs-glob.d.ts +86 -0
  383. package/dist/tools/fs/fs-glob.js +226 -0
  384. package/dist/tools/fs/fs-read.js +14 -14
  385. package/dist/tools/fs/fs-search-tools.js +7 -7
  386. package/dist/tools/fs/pattern-kill-guard.d.ts +22 -0
  387. package/dist/tools/fs/pattern-kill-guard.js +397 -0
  388. package/dist/tools/fs/pdf.d.ts +9 -6
  389. package/dist/tools/fs/pdf.js +3 -3
  390. package/dist/tools/fs/program-read-model.d.ts +57 -7
  391. package/dist/tools/fs/program-read-model.js +11 -1
  392. package/dist/tools/fs/program-read-model.rows.js +78 -21
  393. package/dist/tools/fs/read-deny-home.js +3 -2
  394. package/dist/tools/fs/removal-targets.d.ts +17 -7
  395. package/dist/tools/fs/removal-targets.js +47 -20
  396. package/dist/tools/fs/search.d.ts +10 -0
  397. package/dist/tools/fs/search.js +20 -26
  398. package/dist/tools/fs/shell-read-argv.js +89 -12
  399. package/dist/tools/fs/shell-read-bindings.d.ts +31 -0
  400. package/dist/tools/fs/shell-read-bindings.js +41 -0
  401. package/dist/tools/fs/shell-read-check.js +6 -19
  402. package/dist/tools/fs/shell-read-environment.d.ts +5 -1
  403. package/dist/tools/fs/shell-read-environment.js +3 -2
  404. package/dist/tools/fs/shell-read-establish.d.ts +15 -0
  405. package/dist/tools/fs/shell-read-establish.js +19 -20
  406. package/dist/tools/fs/shell-read-plan.js +24 -16
  407. package/dist/tools/fs/shell-read-start.d.ts +6 -1
  408. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -1
  409. package/dist/tools/fs/shell-read-write-targets.js +2 -3
  410. package/dist/tools/fs/shell-reentry-text.d.ts +9 -0
  411. package/dist/tools/fs/shell-reentry-text.js +40 -2
  412. package/dist/tools/todo.js +63 -63
  413. package/dist/tools/web.d.ts +19 -14
  414. package/dist/tools/web.js +38 -55
  415. package/package.json +1 -1
  416. package/test/export-surface.snapshot.json +143 -5
  417. package/wire-changes/core@7.56.0.json +3891 -0
  418. package/wire-changes/mods-kernel@7.56.0.json +303 -0
@@ -21,9 +21,46 @@ function mayOwnNextWord(row, tok) {
21
21
  return k === tok.length;
22
22
  return false;
23
23
  }
24
+ class OperandBase {
25
+ base;
26
+ taken = [];
27
+ move(spec, value, settled, at) {
28
+ if (spec?.rebases !== true)
29
+ return;
30
+ if (!settled)
31
+ this.base = "unsettled";
32
+ else if (namesOwnPlace(value))
33
+ this.base = value;
34
+ else if (this.base !== "unsettled")
35
+ this.base = this.base === undefined ? value : joinSpelling(this.base, value);
36
+ if (typeof this.base === "string" && this.base !== "unsettled")
37
+ this.taken.push({ at, base: this.base });
38
+ }
39
+ of(r) {
40
+ if (this.base === undefined || namesOwnPlace(r.folded))
41
+ return r;
42
+ if (this.base === "unsettled" || r.expands !== false)
43
+ return { ...r, expands: "unresolvable" };
44
+ return { word: r.word, raw: r.raw, folded: joinSpelling(this.base, r.folded), expands: false };
45
+ }
46
+ covers(unsettled) {
47
+ const out = new Set();
48
+ for (const u of unsettled) {
49
+ if (namesOwnPlace(u.folded) || u.folded.split("/").includes(".."))
50
+ continue;
51
+ for (const t of this.taken)
52
+ if (t.at < u.at)
53
+ out.add(t.base);
54
+ }
55
+ return [...out].map((b) => readOfText(b));
56
+ }
57
+ }
58
+ const namesOwnPlace = (spelled) => spelled.startsWith("/") || spelled.startsWith("~");
59
+ const joinSpelling = (base, rel) => (base.endsWith("/") ? `${base}${rel}` : `${base}/${rel}`);
24
60
  const rootValue = (spec) => spec?.walks === true || spec?.delivers === "operands";
25
61
  class RootsGathered {
26
62
  positional = [];
63
+ opened = new Map();
27
64
  places = [];
28
65
  maybeValues = new Set();
29
66
  stdinOperands = 0;
@@ -50,16 +87,23 @@ class RootsGathered {
50
87
  if (this.maybeValues.has(r))
51
88
  this.slotOwned = true;
52
89
  }
53
- operand(r, namesPath, stdin) {
90
+ operand(r, namesPath, stdin, opened = r) {
54
91
  if (stdin) {
55
92
  if (!this.maybeValues.has(r))
56
93
  this.stdinOperands++;
57
94
  }
58
- else if (namesPath)
95
+ else if (namesPath) {
59
96
  this.positional.push(r);
97
+ if (opened !== r)
98
+ this.opened.set(r, opened);
99
+ }
100
+ }
101
+ cover(place) {
102
+ this.places.push(bare(place, "traversal_root"));
60
103
  }
61
104
  restart() {
62
105
  this.positional.length = 0;
106
+ this.opened.clear();
63
107
  this.places.length = 0;
64
108
  this.stdinOperands = 0;
65
109
  this.slotOwned = false;
@@ -71,7 +115,7 @@ class RootsGathered {
71
115
  const reads = this.unsettled
72
116
  ? [...this.positional, ...(this.slotWord !== undefined ? [this.slotWord] : []), ...(walksCwdWithoutOperand(row) ? [readOfText(".")] : [])]
73
117
  : walksCwdWithoutOperand(row) && leftOver <= (this.slotOwned ? 1 : 0) ? [...this.positional, readOfText(".")] : [...this.positional];
74
- return [...reads.map((r) => bare(r, "traversal_root")), ...this.places];
118
+ return [...reads.map((r) => bare(this.opened.get(r) ?? r, "traversal_root")), ...this.places];
75
119
  }
76
120
  }
77
121
  export const programTextSentence = (language) => `runs inline ${language} program text, which may open files this station does not read`;
@@ -178,6 +222,9 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
178
222
  pushAssignment(reads, toks[j].word, provenance);
179
223
  }
180
224
  const gathered = new RootsGathered();
225
+ let base = new OperandBase();
226
+ const valueAt = new Set();
227
+ const unsettledAt = [];
181
228
  let slotWord;
182
229
  const pastOperand = new Set();
183
230
  let globs = "expanded";
@@ -251,6 +298,8 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
251
298
  walkFrom = argvStart + 2 + sub.at;
252
299
  relocated = sub.relocated;
253
300
  gathered.restart();
301
+ base = new OperandBase();
302
+ unsettledAt.length = 0;
254
303
  walk(sub.row, walkFrom, toks.length, true);
255
304
  }
256
305
  else
@@ -268,10 +317,12 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
268
317
  return;
269
318
  case "dash-word-operand":
270
319
  pastOperand.add(j);
271
- reads.push(bare(r, provenance));
320
+ reads.push(bare(base.of(r), provenance));
272
321
  speculate(start, "whole");
273
322
  return;
274
323
  case "value": {
324
+ base.move(e.spec, r.folded, e.consumed && r.expands === false, j);
325
+ valueAt.add(j);
275
326
  if (!e.consumed) {
276
327
  gathered.value(r, e, false);
277
328
  if (r.expands !== "unresolvable")
@@ -299,19 +350,23 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
299
350
  return;
300
351
  }
301
352
  {
353
+ base.move(e.reading.spec, literalTildeSpelling(e.reading.attached), r.expands === false, j);
302
354
  const named = roleReads(r, "option_value", literalTildeSpelling(e.reading.attached), e.reading.role, globs, ctx, pastOperand.has(j));
303
355
  reads.push(...named);
304
356
  gathered.attached(named, e.reading.spec);
305
357
  }
306
358
  return;
307
359
  case "operand": {
308
- const role = e.dash === undefined ? (e.slot ?? e.role) : undefined;
309
- const named = role !== undefined ? roleReads(r, provenance, r.folded, role, globs, ctx) : [bare(r, provenance)];
310
360
  const inSlot = j === slotWord;
361
+ const o = inSlot || e.dash !== undefined ? r : base.of(r);
362
+ const role = e.dash === undefined ? (e.slot ?? e.role) : undefined;
363
+ const named = role !== undefined ? roleReads(o, provenance, o.folded, role, globs, ctx) : [bare(o, provenance)];
311
364
  if (inSlot)
312
365
  gathered.slot(r);
313
366
  else
314
- gathered.operand(r, named.some((c) => c.denyOnly !== true), e.dash === "stdin");
367
+ gathered.operand(r, named.some((c) => c.denyOnly !== true), e.dash === "stdin", o);
368
+ if (!inSlot && e.dash === undefined && r.expands !== false)
369
+ unsettledAt.push({ at: j, folded: r.folded });
315
370
  reads.push(...named);
316
371
  speculate(start, r.word.assignment !== undefined ? "whole" : "containment");
317
372
  const values = reads.length;
@@ -323,6 +378,11 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
323
378
  }
324
379
  }
325
380
  if (walkRow !== undefined && argv !== undefined && selectsContentsTraversal(walkRow, toks.slice(walkFrom).map((x) => x.folded))) {
381
+ for (const j of expanding)
382
+ if (j >= walkFrom && !valueAt.has(j))
383
+ unsettledAt.push({ at: j, folded: toks[j].folded });
384
+ for (const place of base.covers(unsettledAt))
385
+ gathered.cover(place);
326
386
  for (const root of gathered.roots(walkRow))
327
387
  reads.push(relocated ? { ...root, expands: "unresolvable" } : root);
328
388
  }
@@ -372,6 +432,9 @@ function walkListed(row, argReads, first, last, expanding, reads, ctx, relocated
372
432
  const slice = argReads.slice(first, last);
373
433
  const args = slice.map((r) => r.folded);
374
434
  const gathered = new RootsGathered();
435
+ const base = new OperandBase();
436
+ const valueAt = new Set();
437
+ const unsettledAt = [];
375
438
  let unexplained = false;
376
439
  const globs = valuesAreLiteral(row, args) ? "literal" : "expanded";
377
440
  noteDeliveredOperands(row, args, ctx);
@@ -391,9 +454,11 @@ function walkListed(row, argReads, first, last, expanding, reads, ctx, relocated
391
454
  break;
392
455
  case "dash-word-operand":
393
456
  pastOperand.add(e.at);
394
- reads.push(bare(slice[e.at], "operand"));
457
+ reads.push(bare(base.of(slice[e.at]), "operand"));
395
458
  break;
396
459
  case "value": {
460
+ base.move(e.spec, t, e.consumed && slice[e.at].expands === false, e.at);
461
+ valueAt.add(e.at);
397
462
  if (expanding.has(first + e.at))
398
463
  break;
399
464
  gathered.value(slice[e.at], e, opensPath(offer(e.at, t, e.role, "option_value", !e.consumed)));
@@ -409,6 +474,7 @@ function walkListed(row, argReads, first, last, expanding, reads, ctx, relocated
409
474
  reads.push(bare(slice[e.at], "option_value", payload));
410
475
  break;
411
476
  }
477
+ base.move(e.reading.spec, literalTildeSpelling(e.reading.attached), slice[e.at].expands === false, e.at);
412
478
  gathered.attached(offer(e.at, literalTildeSpelling(e.reading.attached), e.reading.role, "option_value", pastOperand.has(e.at)), e.reading.spec);
413
479
  break;
414
480
  case "operand": {
@@ -428,11 +494,17 @@ function walkListed(row, argReads, first, last, expanding, reads, ctx, relocated
428
494
  break;
429
495
  }
430
496
  let opens = true;
431
- if (e.role !== undefined)
432
- opens = opensPath(offer(e.at, t, e.role, "operand"));
497
+ const o = base.of(slice[e.at]);
498
+ if (e.role !== undefined) {
499
+ const named = roleReads(o, "operand", o.folded, e.role, globs, ctx);
500
+ reads.push(...named);
501
+ opens = opensPath(named);
502
+ }
433
503
  else
434
- reads.push(bare(slice[e.at], "operand"));
435
- gathered.operand(slice[e.at], opens, false);
504
+ reads.push(bare(o, "operand"));
505
+ gathered.operand(slice[e.at], opens, false, o);
506
+ if (slice[e.at].expands !== false)
507
+ unsettledAt.push({ at: e.at, folded: t });
436
508
  if (row?.operands?.assignmentArguments !== "not-paths")
437
509
  pushAssignment(reads, slice[e.at].word, "operand");
438
510
  break;
@@ -444,6 +516,11 @@ function walkListed(row, argReads, first, last, expanding, reads, ctx, relocated
444
516
  reads[i] = { ...reads[i], speculative: "containment" };
445
517
  }
446
518
  if (row !== undefined && selectsContentsTraversal(row, args)) {
519
+ for (let k = 0; k < slice.length; k++)
520
+ if (expanding.has(first + k) && !valueAt.has(k))
521
+ unsettledAt.push({ at: k, folded: args[k] });
522
+ for (const place of base.covers(unsettledAt))
523
+ gathered.cover(place);
447
524
  for (const root of gathered.roots(row))
448
525
  reads.push(relocated ? { ...root, expands: "unresolvable" } : root);
449
526
  }
@@ -106,6 +106,37 @@ export declare const functionKey: (name: string) => string;
106
106
  /** What a function name is at a reader: `defined` — it certainly is one; `absent` — it certainly is not; `maybe` — a branch,
107
107
  * a loop, a body that may not have run, a removal that may not have found it. */
108
108
  export type FunctionState = "defined" | "maybe" | "absent";
109
+ /** The function names a segment exports to child shells — the `-f` function flag with export (`export` always exports;
110
+ * `declare` / `typeset` need `-x`), its bare-name operands. A child `bash -c` inherits them as functions
111
+ * ({@link inheritedExportedFunctions}); their definitions are text this reader does not hold, so a call to one shadows
112
+ * the builtin of the name (#1628). */
113
+ export declare function exportedFunctionNames(argv: readonly LexedWord[]): string[];
114
+ /** A function the CHILD inherits through its ENVIRONMENT: bash exports a function as a `BASH_FUNC_<name>%%` variable
115
+ * (older bashes `BASH_FUNC_<name>()`), and a child reading that variable from its environment defines `<name>` as a
116
+ * function whose body the engine cannot read. Delivered on a child's env by `env BASH_FUNC_x%%=… cmd` (env's own
117
+ * assignment grammar accepts the `%%` name the shell's does not). Each such assignment word of a segment names an
118
+ * inherited function — the same law as {@link exportedFunctionNames}, a different delivery; a value the shell rewrites
119
+ * (`env "BASH_FUNC_$n%%=…"`) names a function the text cannot settle and is not tracked (KL-1628-a). */
120
+ export declare function envFunctionNames(argv: readonly LexedWord[]): string[];
121
+ /** Whether the BODY of a function a child shell inherits is text the engine read: `read` — the text (or a shell above it)
122
+ * defines the function it exports (`f(){ …; }; export -f f`, `declare -fx f`), so the body's segments are read where it
123
+ * is defined; `unread` — delivered on an environment (`BASH_FUNC_f%%=…`, {@link envFunctionNames}), or exported with no
124
+ * definition the text holds (one a `source` or an `eval` made): a body nobody read. */
125
+ export type InheritedBody = "read" | "unread";
126
+ /** The functions a text's child shells inherit, each with its {@link InheritedBody}: what the shell running the text
127
+ * inherited; then each name the text exports — `read` where the text defines it, else what it was inherited as, else
128
+ * `unread`; then each name it delivers on an environment, `unread` whatever else holds it. */
129
+ export declare function inheritedFunctionsOf(segments: readonly {
130
+ readonly argv: readonly LexedWord[];
131
+ readonly defines: readonly string[];
132
+ }[], inherited: ReadonlyMap<string, InheritedBody> | undefined): ReadonlyMap<string, InheritedBody>;
133
+ /** What a child shell (`bash -c`) inherits from a parent that EXPORTED functions ({@link exportedFunctionNames}): each
134
+ * such name is a `defined` function in the child — bash looks a function up before the builtin of its name, so
135
+ * `declare X=…` run under an exported `declare` function is the FUNCTION (it binds nothing), not the declaration
136
+ * builtin. Every other function key is `absent` (a child inherits only exported functions), every builtin enabled;
137
+ * the child holds none of their bodies, so a call to one may change any name (`bound: "all"`). The existing binding
138
+ * carrier ({@link PlanStart.names}), not a second table (#1628). */
139
+ export declare function inheritedExportedFunctions(names: ReadonlyMap<string, InheritedBody>): InheritedFunctions;
109
140
  /** One binding a segment makes: a name and what it now holds (`unknown` = made unknown). `prefix`: a binding that may or
110
141
  * may not take effect (a prefix assignment to a special builtin, which may or may not outlive its command; an `unset` of a
111
142
  * function, which a read-only function refuses). `loopVar`: a `for` loop's variable, held through its body. */
@@ -46,6 +46,47 @@ const isFunctionKey = (name) => name.startsWith(FUNCTION_KEY_PREFIX);
46
46
  const FUNCTION_DEFINED = [{ spelled: "defined" }];
47
47
  const FUNCTION_ABSENT = [{ spelled: "absent" }];
48
48
  const isDefinedValue = (v) => v.spelled === "defined";
49
+ const EXPORT_FUNCTION_BUILTINS = new Set(["export", "declare", "typeset"]);
50
+ export function exportedFunctionNames(argv) {
51
+ const program = readProgramPosition(argv).program;
52
+ const prog = program?.basename;
53
+ if (program === undefined || prog === undefined || !EXPORT_FUNCTION_BUILTINS.has(prog))
54
+ return [];
55
+ const literal = (w) => w.expansions.length === 0;
56
+ const rest = argv.slice(program.index + 1);
57
+ const flags = new Set(rest.filter((w) => literal(w) && /^-[A-Za-z]+$/.test(w.folded)).flatMap((w) => [...w.folded.slice(1)]));
58
+ if (!flags.has("f") || !(prog === "export" || flags.has("x")))
59
+ return [];
60
+ return rest.filter((w) => literal(w) && w.assignment === undefined && /^[A-Za-z_][A-Za-z0-9_]*$/.test(w.folded)).map((w) => w.folded);
61
+ }
62
+ export function envFunctionNames(argv) {
63
+ const out = [];
64
+ for (const w of argv) {
65
+ if (w.expansions.length !== 0)
66
+ continue;
67
+ const m = /^BASH_FUNC_([A-Za-z_][A-Za-z0-9_]*)(?:%%|\(\))=/.exec(w.folded);
68
+ if (m !== null)
69
+ out.push(m[1]);
70
+ }
71
+ return out;
72
+ }
73
+ export function inheritedFunctionsOf(segments, inherited) {
74
+ const defined = new Set(segments.flatMap((s) => s.defines));
75
+ const out = new Map(inherited ?? []);
76
+ for (const s of segments)
77
+ for (const name of exportedFunctionNames(s.argv))
78
+ out.set(name, defined.has(name) ? "read" : out.get(name) ?? "unread");
79
+ for (const s of segments)
80
+ for (const name of envFunctionNames(s.argv))
81
+ out.set(name, "unread");
82
+ return out;
83
+ }
84
+ export function inheritedExportedFunctions(names) {
85
+ return {
86
+ start: (key) => (isFunctionKey(key) ? (names.has(key.slice(FUNCTION_KEY_PREFIX.length)) ? FUNCTION_DEFINED : FUNCTION_ABSENT) : BUILTIN_ENABLED),
87
+ bound: "all",
88
+ };
89
+ }
49
90
  const ATTRIBUTE_KINDS = { integer: "integer", transformed: "transformed", frozen: "frozen" };
50
91
  const EVERY_ATTRIBUTE = Object.values(ATTRIBUTE_KINDS).map((kind) => ({ kind }));
51
92
  export function projectSegment(segment) {
@@ -3,7 +3,7 @@ import { errorResult } from "../../core/tools.js";
3
3
  import { BASH_READONLY_CONFINEMENT_NOTE } from "./fs-shared.js";
4
4
  import { isAbsoluteForFamily, normalizeAbsPathLexically, pathFamilyOf, violationDetails, withinAnyRoot } from "./safety.js";
5
5
  import { checkedReadDenyHomes, fitMiddle, READ_DENY_SENTENCE_PART_MAX, readDenySentence } from "./read-deny.js";
6
- import { NOT_AUTO_ALLOWED, programPositionRefusalSentence, readProgramPosition } from "./bash-program-position.js";
6
+ import { NOT_AUTO_ALLOWED, declinedProgramSentence } from "./bash-program-position.js";
7
7
  import { bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
8
8
  import { planShellReads } from "./shell-read-plan.js";
9
9
  import { createShellReadEstablisher, establishShellReadsLexically } from "./shell-read-establish.js";
@@ -12,10 +12,10 @@ import { anyOptionWord, programArgs } from "./shell-read-write-targets.js";
12
12
  import { shellReadFaceWords } from "./shell-read-face.js";
13
13
  import { passesUnasked } from "./shell-read-device.js";
14
14
  import { programRow, selectedOptions } from "./program-read-model.js";
15
+ import { refusedByRemoval, SHELL_REMOVAL_COMMANDS } from "./removal-targets.js";
15
16
  import { allFindings, anyFaceGuards, decideShellRead, denyRowAtOrBelow, foldShellReadFindings, judgeEstablishedPlan, readRootGrantCandidateOf, stationGuards, writeFaceGuards, } from "./shell-read-judge.js";
16
17
  import { seatOf } from "../../core/tool-name-readings.js";
17
18
  import { normalizeReadRootCandidate } from "../../core/read-root-candidate.js";
18
- import { argvOf, lexShellText } from "../../shell/lex.js";
19
19
  import { assertNoRetiredShellReadBoundaryKeys } from "../../core/retired-keys.js";
20
20
  function containmentDetails(paths) {
21
21
  return { type: "readonly_out_of_root", code: "readonly_out_of_root", paths };
@@ -26,7 +26,6 @@ function unestablishedDetails() {
26
26
  function findingDetails(finding, paths) {
27
27
  return finding === "unestablishable" || finding === "declined" || finding === "unanswered" ? unestablishedDetails() : containmentDetails(paths);
28
28
  }
29
- const REMOVAL_COMMANDS = new Set(["rm", "rmdir"]);
30
29
  const WRITES_BENEATH = {
31
30
  cp: () => true,
32
31
  mv: () => true,
@@ -74,15 +73,16 @@ function writesPlacedOf(judged, boundary) {
74
73
  if (UNPLACED_OPTIONS[verb]?.(programArgs(s.segment).map((w) => w.spelled)) === true)
75
74
  return false;
76
75
  const beneath = WRITES_BENEATH[verb]?.(s.segment) === true;
76
+ const removal = SHELL_REMOVAL_COMMANDS.includes(verb);
77
77
  for (const f of s.findings) {
78
78
  if (f.read.candidate.face !== "write")
79
79
  continue;
80
80
  const target = f.read.target;
81
81
  if (f.read.established === "unestablishable" || target === undefined || isPattern(f) || f.kind !== "none" || (f.containment !== "inside" && f.containment !== "open"))
82
82
  return false;
83
- if (beneath && denyRowAtOrBelow(f.read, boundary, !REMOVAL_COMMANDS.has(verb)))
83
+ if (beneath && denyRowAtOrBelow(f.read, boundary, !removal))
84
84
  return false;
85
- if (REMOVAL_COMMANDS.has(verb) && writeRoots.some((root) => withinAnyRoot([target], root)))
85
+ if (removal && !refusedByRemoval(f.read) && writeRoots.some((root) => withinAnyRoot([target], root)))
86
86
  return false;
87
87
  }
88
88
  return true;
@@ -182,19 +182,6 @@ function shellCommandOf(args) {
182
182
  const command = args?.command;
183
183
  return typeof command === "string" ? command : undefined;
184
184
  }
185
- function declineSentence(segment) {
186
- const lexed = lexShellText(segment.text);
187
- if (!("reject" in lexed) && lexed.segments[0] !== undefined) {
188
- const pos = readProgramPosition(argvOf(lexed.segments[0]));
189
- if (pos.refusal !== undefined)
190
- return programPositionRefusalSentence(pos.refusal);
191
- if (pos.argvFromStdin) {
192
- const runs = pos.program?.basename ?? "the program it runs";
193
- return `"${segment.text.trim()}" hands ${runs} its operands on its standard input, which this station does not read ${NOT_AUTO_ALLOWED}`;
194
- }
195
- }
196
- return `"${segment.text.trim()}" runs a program this station cannot name ${NOT_AUTO_ALLOWED}`;
197
- }
198
185
  const ACCOUNT_PART_MAX = READ_DENY_SENTENCE_PART_MAX;
199
186
  const CAUSE_SPELLING_MAX = 64;
200
187
  function fitCause(cause) {
@@ -236,7 +223,7 @@ function findingAccount(fold, boundary) {
236
223
  return `the text given to ${reenteredPrograms(fold)} — as an argument, on standard input, or in the file it names — runs as shell text this station does not parse ${NOT_AUTO_ALLOWED}`;
237
224
  return `the command's text could not be read as shell, so where it ${verb} cannot be established ${NOT_AUTO_ALLOWED}`;
238
225
  case "declined":
239
- return fold.declined !== undefined ? declineSentence(fold.declined) : `the station could not read which program runs ${NOT_AUTO_ALLOWED}`;
226
+ return fold.declined !== undefined ? declinedProgramSentence(fold.declined.text) : `the station could not read which program runs ${NOT_AUTO_ALLOWED}`;
240
227
  case "identity_changed":
241
228
  return `"${fitMiddle(w?.read.candidate.raw ?? "?", ACCOUNT_PART_MAX)}" resolves to "${fitMiddle(w?.read.target ?? "?", ACCOUNT_PART_MAX)}" now, not where it resolved when this call was judged`;
242
229
  case "deny_hit":
@@ -9,7 +9,8 @@
9
9
  * establisher and the write guards are handed an {@link ObservedFilesystem}, never the environment, so a lookup they make
10
10
  * cannot escape the record.
11
11
  *
12
- * What is NOT a failure: a conclusive absence (`not_found`, `not_directory` — the answer is "nothing there") and an adapter
12
+ * What is NOT a failure: a conclusive absence (`not_found`, `not_directory`, `name_too_long` — the answer is "nothing there", or
13
+ * "nothing can be there": the program that opens such a name fails the same way) and an adapter
13
14
  * that has no filesystem surface (`not_supported` — its answer that there is no file face: a chain it answers so keeps the
14
15
  * lexical reading). Everything else is, and is recorded wherever it falls — a resolver that answered nothing at all in the
15
16
  * command included: a lookup that failed (`permission_denied`, `timeout`, `unknown`, a throw) is a resolver failing, whose
@@ -47,6 +48,9 @@ export interface ObservedEnvironment {
47
48
  /** Records that came from outside the door: the declared roots' own lookup and the home views a read could not be placed against. */
48
49
  adopt(records: readonly Incomplete[]): void;
49
50
  }
51
+ /** The answers that say NOTHING IS THERE — no entry (`not_found`), nothing reachable under a non-directory (`not_directory`), a
52
+ * name the filesystem cannot hold (`name_too_long`): the station's one reading of an absence, wherever a lookup is read. */
53
+ export declare const isAbsence: (code: string) => boolean;
50
54
  /** The declared roots, canonicalized ONCE per establisher (through the same door), with what that lookup could not finish —
51
55
  * merged into every establishment. `known`: the caller already holds them as canonical identities and they are used verbatim
52
56
  * (re-resolving one that was since swapped for a link would substitute the link's target for the root that was approved). */
@@ -1,6 +1,7 @@
1
1
  import { canonicalPathIsAuthoritative } from "../../core/remote-env.js";
2
2
  const OBSERVED = Symbol("observedFilesystem");
3
- const CONCLUSIVE = new Set(["not_found", "not_directory", "not_supported"]);
3
+ export const isAbsence = (code) => code === "not_found" || code === "not_directory" || code === "name_too_long";
4
+ const conclusive = (code) => isAbsence(code) || code === "not_supported";
4
5
  export function canonicalRootsOnce(env, readRoots, known) {
5
6
  let once = known ? Promise.resolve({ roots: readRoots, incomplete: [] }) : undefined;
6
7
  return (signal) => (once ??= (async () => {
@@ -21,7 +22,7 @@ export function observeEnvironment(env) {
21
22
  const records = [];
22
23
  const note = (op, path, what, code) => void records.push({ op, detail: `"${path}" ${what} (${code})` });
23
24
  const watch = (op, path, what, ask) => new Promise((resolve) => resolve(ask())).then((r) => {
24
- if (!r.ok && !CONCLUSIVE.has(r.error.code))
25
+ if (!r.ok && !conclusive(r.error.code))
25
26
  note(op, path, what, r.error.code);
26
27
  return r;
27
28
  }, (e) => {
@@ -43,6 +43,7 @@ import { type ObservedEnvironment } from "./shell-read-environment.js";
43
43
  import type { ShellReadPlan, ShellReadSegment } from "./shell-read-plan.js";
44
44
  import type { EstablishedRead, ShellReadBoundary } from "./shell-read-judge.js";
45
45
  import type { HomeViewSource } from "./read-deny-home.js";
46
+ import { type FsGlob } from "./fs-glob.js";
46
47
  /**
47
48
  * How many directories one glob operand may enumerate, and how many names it may
48
49
  * match, before the leg gives up and answers `unestablishable`.
@@ -54,6 +55,20 @@ import type { HomeViewSource } from "./read-deny-home.js";
54
55
  */
55
56
  export declare const MAX_GLOB_DIRS = 256;
56
57
  export declare const MAX_GLOB_MATCHES = 8192;
58
+ /**
59
+ * Compile ONE path segment of a shell glob into a whole-name matcher (the engine in `fs-glob.ts`), or `undefined` when the
60
+ * pattern uses a construct this matcher does not model (the caller then fails closed).
61
+ *
62
+ * Modelled: `*` (any run of non-separator characters), `?` (one), and bracket expressions with `!`/`^`
63
+ * negation, `a-z` ranges, and a literal `]` in first position. A POSIX class (`[[:alpha:]]`) is deliberately widened to
64
+ * "any one character" rather than refused — the lexer spells such a class, and one holding a quoted character, `?` —
65
+ * over-matching only adds candidates to verify, the safe direction here, while refusing would cost a usable idiom.
66
+ *
67
+ * NOT modelled: a backslash. Its escaping semantics interact with the lexer's quote folding (which has
68
+ * already rewritten the token), so a segment carrying one is failed closed rather than guessed at. The
69
+ * leading-dot rule is not a property of the pattern's own text and lives in {@link globSegmentMatches}.
70
+ */
71
+ export declare function compileGlobSegment(pattern: string): FsGlob | undefined;
57
72
  /** The reads of one plan, established: per segment, in the plan's own order, with nested plans beside. */
58
73
  export interface EstablishedPlan {
59
74
  readonly plan: ShellReadPlan;
@@ -1,45 +1,44 @@
1
- import { canonicalRootsOnce, observeEnvironment } from "./shell-read-environment.js";
1
+ import { canonicalRootsOnce, isAbsence, observeEnvironment } from "./shell-read-environment.js";
2
2
  import { anchorSpelling, normalizeAbsPathLexically, pathFamilyOf } from "./safety.js";
3
3
  import { boundaryIsTheSandbox } from "../../core/remote-env.js";
4
4
  import { resolveOperandSpellings } from "./bash-readonly-classifier.js";
5
5
  import { judgedCandidatesOf, sedBackupNames } from "./shell-read-write-targets.js";
6
6
  import { movedReads, withHomeViews } from "./shell-read-identity.js";
7
7
  import { passesUnasked } from "./shell-read-device.js";
8
+ import { compileFsGlob, fsGlobClass } from "./fs-glob.js";
8
9
  const GLOB_METACHAR_RE = /[*?[]/;
9
10
  export const MAX_GLOB_DIRS = 256;
10
11
  export const MAX_GLOB_MATCHES = 8192;
11
12
  const WRITE_LINK_HOPS = 32;
12
- function compileGlobSegment(pattern) {
13
+ export function compileGlobSegment(pattern) {
13
14
  if (pattern.includes("\\"))
14
15
  return undefined;
15
- let src = "";
16
+ const tokens = [];
16
17
  for (let i = 0; i < pattern.length; i++) {
17
18
  const ch = pattern[i];
18
19
  if (ch === "*") {
19
- src += "[^/]*";
20
+ tokens.push({ kind: "star" });
20
21
  }
21
22
  else if (ch === "?") {
22
- src += "[^/]";
23
+ tokens.push({ kind: "one" });
23
24
  }
24
25
  else if (ch === "[") {
25
- const cls = parseBracketExpression(pattern, i);
26
- if (cls === undefined)
27
- src += "\\[";
26
+ const bracket = parseBracketExpression(pattern, i);
27
+ if (bracket === undefined)
28
+ tokens.push({ kind: "text", text: "[" });
28
29
  else {
29
- src += cls.source;
30
- i = cls.end;
30
+ const cls = fsGlobClass(bracket.source);
31
+ if (cls === undefined)
32
+ return undefined;
33
+ tokens.push({ kind: "class", class: cls });
34
+ i = bracket.end;
31
35
  }
32
36
  }
33
37
  else {
34
- src += ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
38
+ tokens.push({ kind: "text", text: ch });
35
39
  }
36
40
  }
37
- try {
38
- return new RegExp(`^${src}$`, "s");
39
- }
40
- catch {
41
- return undefined;
42
- }
41
+ return compileFsGlob(tokens, "start");
43
42
  }
44
43
  function parseBracketExpression(pattern, start) {
45
44
  let i = start + 1;
@@ -248,7 +247,7 @@ function establishLegOver(seen, readRoots) {
248
247
  return { value: canon.value, exact: true };
249
248
  return { value: [canon.value.replace(TRAILING_SEP, ""), ...[...tail].reverse()].join("/"), exact: false, missing: allAbsent };
250
249
  }
251
- if (canon.error.code !== "not_found" && canon.error.code !== "not_directory")
250
+ if (!isAbsence(canon.error.code))
252
251
  allAbsent = false;
253
252
  tail.push(parts.pop());
254
253
  }
@@ -339,7 +338,7 @@ function establishLegOver(seen, readRoots) {
339
338
  return { unverifiable: `"${dir}" cannot be resolved to verify what "${seg}" would expand to` };
340
339
  const listed = await env.listDir(canonDir?.value ?? dir, signal);
341
340
  if (!listed.ok) {
342
- if (listed.error.code === "not_found" || listed.error.code === "not_directory")
341
+ if (isAbsence(listed.error.code))
343
342
  continue;
344
343
  return { unverifiable: `"${dir}" cannot be enumerated to verify what "${seg}" would expand to (${listed.error.code})` };
345
344
  }
@@ -414,7 +413,7 @@ function establishLegOver(seen, readRoots) {
414
413
  return parent.missing === true ? name : undefined;
415
414
  const entry = await env.fileInfo(joinUnder(parent.value, leaf), signal);
416
415
  if (!entry.ok)
417
- return entry.error.code === "not_found" || entry.error.code === "not_directory" ? name : undefined;
416
+ return isAbsence(entry.error.code) ? name : undefined;
418
417
  if (entry.value.kind !== "symlink")
419
418
  return name;
420
419
  const link = await env.readLink?.(joinUnder(parent.value, leaf), signal);