@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
@@ -33,6 +33,7 @@ const OBJECT_PATH = { kind: "object-path" };
33
33
  const text = (language) => ({ kind: "program-text", language });
34
34
  const file = (language) => ({ kind: "program-file", language });
35
35
  const WALKS = { walks: true };
36
+ const REBASES = { rebases: true };
36
37
  const SWITCH = { arity: 0 };
37
38
  const QUERY = { arity: 0, mode: "queries" };
38
39
  const val = (value, extra = {}) => ({ arity: 1, ...(value !== undefined ? { value } : {}), ...extra });
@@ -164,8 +165,8 @@ const date = {
164
165
  };
165
166
  const tar = {
166
167
  key: "tar",
167
- grammars: [grammar({ b: val(COUNT), C: val(NAMES, WALKS), f: val(READ), T: val(READ, { delivers: "operands" }), X: val(READ), g: val(), ...switches("adhjJkmOpPStvwWxzZ"), ...switches("cru", { selects: "traversal" }) }, {
168
- "blocking-factor": val(COUNT), directory: val(NAMES, WALKS), exclude: val(NAME_PATTERN), "exclude-from": val(READ), file: val(READ), "files-from": val(READ, { delivers: "operands" }), "strip-components": val(COUNT),
168
+ grammars: [grammar({ b: val(COUNT), C: val(NAMES, REBASES), f: val(READ), T: val(READ, { delivers: "operands" }), X: val(READ), g: val(), ...switches("adhjJkmOpPStvwWxzZ"), ...switches("cru", { selects: "traversal" }) }, {
169
+ "blocking-factor": val(COUNT), directory: val(NAMES, REBASES), exclude: val(NAME_PATTERN), "exclude-from": val(READ), file: val(READ), "files-from": val(READ, { delivers: "operands" }), "strip-components": val(COUNT),
169
170
  ...longSwitches(["absolute-names", "dereference", "extract", "get", "interactive", "keep-old-files", "list", "preserve-permissions", "sparse", "to-stdout", "touch", "verbose", "verify"]), ...BANNERS,
170
171
  ...longSwitches(["append", "create", "update"], { selects: "traversal" }),
171
172
  })],
@@ -176,6 +177,7 @@ const find = {
176
177
  key: "find",
177
178
  grammars: implementations(grammar({ D: val(LABEL), O: val(COUNT), f: val(NAMES), ...switches("HLPdsx") }, BANNERS, {
178
179
  afterFirstOperand: "options",
180
+ acceptsAbbreviations: false,
179
181
  dashWords: {
180
182
  amin: val(COUNT), anewer: val(NAMES), atime: val(COUNT), cmin: val(COUNT), cnewer: val(NAMES), context: val(LABEL),
181
183
  ctime: val(COUNT), fstype: val(LABEL), gid: val(COUNT), group: val(LABEL), ilname: val(NAMES), iname: val(NAME_PATTERN),
@@ -306,7 +308,7 @@ function interpreterGrammar(language, g) {
306
308
  put(s, val(file(language), { fillsSlot: true, ...(g.fileEndsOptions === true ? { endsOptions: true } : {}) }));
307
309
  for (const s of g.longText ?? [])
308
310
  put(s, val(text(language), { fillsSlot: true }));
309
- return { dialect: "any", short, long, dashWords, afterFirstOperand: "either" };
311
+ return { dialect: "any", short, long, dashWords, afterFirstOperand: "either", ...(g.exactLongNames === true ? { acceptsAbbreviations: false } : {}) };
310
312
  }
311
313
  const interpreter = (key, language, g, program, stdinWhenUnnamed, extra = {}, departures) => ({
312
314
  key,
@@ -317,9 +319,9 @@ const interpreter = (key, language, g, program, stdinWhenUnnamed, extra = {}, de
317
319
  witness: BINARY,
318
320
  ...extra,
319
321
  });
320
- const PERL = { text: "eEmM", separated: "I", attached: "iCFxDV", plain: "nplawcsStTUWX", banner: "vVh" };
321
- const RUBY = { text: "e", separated: "IrECKT", attached: "F", plain: "nplawcsSU", banner: "vh" };
322
- const PYTHON = { text: "c", module: "m", separated: "WXQ", plain: "uBEIsSqbdvxOtR", banner: "Vh?", interactive: "i", fileEndsOptions: true };
322
+ const PERL = { text: "eEmM", separated: "I", attached: "iCFxDV", plain: "nplawcsStTUWX", banner: "vVh", exactLongNames: true };
323
+ const RUBY = { text: "e", separated: "IrECKT", attached: "F", plain: "nplawcsSU", banner: "vh", exactLongNames: true };
324
+ const PYTHON = { text: "c", module: "m", separated: "WXQ", plain: "uBEIsSqbdvxOtR", banner: "Vh?", interactive: "i", fileEndsOptions: true, exactLongNames: true };
323
325
  const NODE = {
324
326
  longText: ["--eval", "--print"], text: "ep",
325
327
  longModules: ["--import", "--loader", "--experimental-loader", "--require"],
@@ -327,25 +329,25 @@ const NODE = {
327
329
  longAttached: ["--max-old-space-size"],
328
330
  longNamesProgram: ["--test"],
329
331
  longPlain: ["--inspect", "--inspect-brk", "--no-warnings", "--trace-warnings", "--watch", "--enable-source-maps", "--experimental-modules", "--no-deprecation", "--check"],
330
- modules: "r", plain: "c", banner: "vh", interactive: "i",
332
+ modules: "r", plain: "c", banner: "vh", interactive: "i", exactLongNames: true,
331
333
  };
332
- const PHP = { longText: ["--run", "--process-begin", "--process-code", "--process-end"], text: "rRBE", file: "fF", separated: "dcz", plain: "nqswl", banner: "vhi", interactive: "a" };
334
+ const PHP = { longText: ["--run", "--process-begin", "--process-code", "--process-end"], text: "rRBE", file: "fF", separated: "dcz", plain: "nqswl", banner: "vhi", interactive: "a", exactLongNames: true };
333
335
  const LUA = { text: "e", separated: "l", banner: "vh", interactive: "i" };
334
336
  const EXPECT = { text: "c", separated: "Db", plain: "dN", banner: "vh", interactive: "i" };
335
337
  const AWK = { longText: ["--source"], text: "e", longFile: ["--file", "--exec"], file: "fE", separated: "F", assignment: "v", banner: "V" };
336
338
  const POSIX_AWK = { file: "f", separated: "F", assignment: "v" };
337
339
  const AWK_IMPLEMENTATIONS = { gnu: {}, bsd: { lacks: ["-E", "--file", "--exec", "--source"] } };
338
- const JQ = { longFile: ["--from-file"], file: "f", separated: "L", plain: "nrjcsSaCMRe", banner: "h" };
340
+ const JQ = { longFile: ["--from-file"], file: "f", separated: "L", plain: "nrjcsSaCMRe", banner: "h", exactLongNames: true };
339
341
  const BC = { longText: ["--expression"], text: "e", plain: "lqsw", banner: "vh", interactive: "i" };
340
342
  const DC = { longText: ["--expression"], text: "e", longFile: ["--file"], file: "f", banner: "hV" };
341
- const SQLITE = { longText: ["-cmd"], longSupplemental: ["-init"] };
343
+ const SQLITE = { longText: ["-cmd"], longSupplemental: ["-init"], exactLongNames: true };
342
344
  const PSQL = { longText: ["--command"], text: "c", longFile: ["--file"], file: "f", separated: "hpUdoF", plain: "aeEqtxXwW", banner: "lV?" };
343
345
  const OSASCRIPT = { text: "e", separated: "ls", plain: "i", banner: "h" };
344
346
  const RSCRIPT = { text: "e", banner: "h" };
345
347
  const JULIA = { longText: ["--eval", "--print"], text: "eE", separated: "JLp", plain: "q", banner: "vh", interactive: "i" };
346
348
  const GHCI = { text: "e", separated: "io" };
347
349
  const GROOVY = { text: "e", separated: "Dcl", plain: "n", banner: "vh", interactive: "i" };
348
- const BUN = { longText: ["--eval", "--print"], text: "ep", modules: "r", banner: "vh" };
350
+ const BUN = { longText: ["--eval", "--print"], text: "ep", modules: "r", banner: "vh", exactLongNames: true };
349
351
  const launcher = (key, spec = {}, g, extra = {}) => ({
350
352
  key,
351
353
  grammars: g === undefined ? [] : [g],
@@ -396,6 +398,47 @@ const SU_OPTIONS = {
396
398
  programs: ["-m", "-p", "--preserve-environment"],
397
399
  };
398
400
  const SU = launcherGrammar(SU_OPTIONS, { stepsDashDash: true });
401
+ const VALGRIND_FLAGS = [
402
+ "trace-children", "trace-children-skip", "trace-children-skip-by-arg", "child-silent-after-fork", "vgdb", "vgdb-stop-at", "track-fds", "time-stamp",
403
+ "xml", "xml-user-comment", "demangle", "error-limit", "exit-on-first-error", "error-markers", "show-error-list", "keep-debuginfo", "show-below-main",
404
+ "default-suppressions", "gen-suppressions", "dsymutil", "xtree-memory", "fullpath-after", "allow-mismatched-debuginfo", "smc-check", "read-inline-info",
405
+ "read-var-info", "run-libc-freeres", "run-cxx-freeres", "fair-sched", "sigill-diagnostics", "show-emwarns", "vgdb-shadow-registers", "sim-hints",
406
+ "kernel-variant", "soname-synonyms", "resync-filter", "tool",
407
+ "leak-check", "leak-resolution", "show-leak-kinds", "errors-for-leak-kinds", "leak-check-heuristics", "show-reachable", "show-possibly-lost", "xtree-leak",
408
+ "undef-value-errors", "track-origins", "partial-loads-ok", "expensive-definedness-checks", "workaround-gcc296-bugs", "ignore-ranges", "ignore-range-below-sp",
409
+ "malloc-fill", "free-fill", "keep-stacktraces", "show-mismatched-frees",
410
+ ];
411
+ const VALGRIND_COUNTS = [
412
+ "vgdb-error", "log-fd", "xml-fd", "num-callers", "error-exitcode", "input-fd", "max-stackframe", "main-stacksize", "alignment", "redzone-size",
413
+ "max-threads", "merge-recursive-frames", "num-transtab-sectors", "avg-transtab-entry-size", "valgrind-stacksize", "vgdb-poll",
414
+ "unw-stack-scan-thresh", "unw-stack-scan-frames", "freelist-vol", "freelist-big-blocks",
415
+ ];
416
+ const VALGRIND_OUTPUT_FILES = ["log-file", "xml-file", "xtree-memory-file", "xtree-leak-file", "callgrind-out-file", "cachegrind-out-file", "massif-out-file", "dhat-out-file"];
417
+ const VALGRIND_WRITE = { kind: "path", effect: "write", programEnv: true };
418
+ const VALGRIND_READ = { kind: "path", effect: "read", programEnv: true };
419
+ const VALGRIND = launcherGrammar({
420
+ switches: ["-q", "-v", "-s", "--quiet", "--verbose", "-h", "--help", "--help-debug", "--help-dyn-options", "--version"],
421
+ banners: ["-h", "--help", "--help-debug", "--help-dyn-options", "--version"],
422
+ optional: {
423
+ ...Object.fromEntries(VALGRIND_FLAGS.map((n) => [`--${n}`, LABEL])),
424
+ ...Object.fromEntries(VALGRIND_COUNTS.map((n) => [`--${n}`, COUNT])),
425
+ ...Object.fromEntries(VALGRIND_OUTPUT_FILES.map((n) => [`--${n}`, VALGRIND_WRITE])),
426
+ "--suppressions": VALGRIND_READ,
427
+ },
428
+ }, { acceptsAbbreviations: false, stepsDashDash: true });
429
+ const SETPRIV = launcherGrammar({
430
+ switches: ["--nnp", "--no-new-privs", "--clear-groups", "--keep-groups", "--init-groups", "--reset-env", "-d", "--dump", "-h", "--help", "-V", "--version"],
431
+ banners: ["-d", "--dump", "-h", "--help", "-V", "--version"],
432
+ values: Object.fromEntries(["ambient-caps", "inh-caps", "bounding-set", "ruid", "euid", "rgid", "egid", "reuid", "regid", "groups", "securebits", "pdeathsig", "selinux-label", "apparmor-profile"].map((n) => [`--${n}`, LABEL])),
433
+ }, { stepsDashDash: true });
434
+ const CHROOT_GRAMMARS = implementations(launcherGrammar({ switches: ["--skip-chdir", "--help", "--version"], values: { "--userspec": LABEL, "--groups": LABEL, "-u": LABEL, "-g": LABEL, "-G": LABEL } }, { stepsDashDash: true }), { lacks: ["-u", "-g", "-G"] }, { lacks: ["--skip-chdir", "--help", "--version", "--userspec", "--groups"] });
435
+ const PRLIMIT_RESOURCES = { c: "core", d: "data", e: "nice", f: "fsize", i: "sigpending", l: "memlock", m: "rss", n: "nofile", q: "msgqueue", r: "rtprio", s: "stack", t: "cpu", u: "nproc", v: "as", x: "locks", y: "rttime" };
436
+ const PRLIMIT = launcherGrammar({
437
+ switches: ["--noheadings", "--raw", "--verbose", "-h", "--help", "-V", "--version"],
438
+ banners: ["-h", "--help", "-V", "--version"],
439
+ values: { "-p": COUNT, "--pid": COUNT, "-o": LABEL, "--output": LABEL },
440
+ optional: Object.fromEntries(Object.entries(PRLIMIT_RESOURCES).flatMap(([letter, name]) => [[`-${letter}`, LABEL], [`--${name}`, LABEL]])),
441
+ }, { stepsDashDash: true });
399
442
  const SU_LINUX_ONLY = [...SU_OPTIONS.switches, ...Object.keys(SU_OPTIONS.values), ...SU_OPTIONS.shellText, ...SU_OPTIONS.programs].filter((s) => !["-f", "-l", "-m"].includes(s));
400
443
  const launchers = [
401
444
  { key: "env", grammars: implementations(launcherGrammar({
@@ -427,8 +470,8 @@ const launchers = [
427
470
  launcher("pkexec", { whenNoProgram: "shell" }, launcherGrammar({ switches: ["--disable-internal-agent", "--keep-cwd", "--version", "--help"], values: { "--user": LABEL } }), { witness: PRIVILEGED }),
428
471
  { key: "su", grammars: implementations(SU, { afterFirstOperand: "options" }, { lacks: SU_LINUX_ONLY, afterFirstOperand: "operands" }), launcher: { shellText: "user", asUser: { home: "target", loginWith: ["-l", "--login"] } }, witness: PRIVILEGED },
429
472
  { key: "runuser", grammars: [launcherGrammar({ ...SU_OPTIONS, values: { ...SU_OPTIONS.values, "-u": LABEL, "--user": LABEL } }, { afterFirstOperand: "options", stepsDashDash: true })], launcher: { shellText: "user", programWith: ["-u", "--user"], asUser: { home: "target", loginWith: ["-l", "--login"] } }, witness: PRIVILEGED },
430
- launcher("chroot", { requiredOperands: 1, whenNoProgram: "shell" }, undefined, { witness: PRIVILEGED }),
431
- launcher("setpriv", {}, undefined, { witness: PRIVILEGED }),
473
+ { key: "chroot", grammars: CHROOT_GRAMMARS, launcher: { requiredOperands: 1, whenNoProgram: "shell" }, witness: PRIVILEGED },
474
+ launcher("setpriv", {}, SETPRIV, { witness: PRIVILEGED }),
432
475
  launcher("nohup", {}, grammar({}, BANNERS, { stepsDashDash: true })),
433
476
  { key: "nice", grammars: implementations(grammar({ n: val(COUNT) }, { adjustment: val(COUNT), ...BANNERS }, { stepsDashDash: true }), {}, { lacks: ["--adjustment", "--help", "--version"] }), launcher: { negativeIntegerIsOwnArgument: true }, witness: BINARY },
434
477
  launcher("ionice", {}, launcherGrammar({
@@ -470,7 +513,7 @@ const launchers = [
470
513
  }), { witness: LINUX_NAMESPACE }),
471
514
  launcher("script", { shellText: "joined" }, launcherGrammar({ shellText: ["-c", "--command"] }), { witness: SHELL_TEXT_LAUNCHER }),
472
515
  launcher("numactl", {}, undefined, { witness: LINUX_NAMESPACE }),
473
- launcher("prlimit", {}, undefined, { witness: LINUX_NAMESPACE }),
516
+ launcher("prlimit", {}, PRLIMIT, { witness: LINUX_NAMESPACE }),
474
517
  launcher("systemd-run", {}, undefined, { witness: { declared: "hands the program to a service manager" } }),
475
518
  launcher("strace", {}, launcherGrammar({
476
519
  switches: ["-f", "-c", "-C", "-d", "-D", "-h", "-i", "-k", "-q", "-r", "-t", "-T", "-v", "-V", "-w", "-x", "-y", "-z", "-Z", "-F", "--follow-forks", "--summary-only", "--summary", "--debug", "--help", "--version"],
@@ -486,7 +529,7 @@ const launchers = [
486
529
  banners: ["-V"],
487
530
  values: { "-a": COUNT, "-A": COUNT, "-e": LABEL, "-l": NAMES, "-n": COUNT, "-o": WRITE, "-p": COUNT, "-s": COUNT, "-u": LABEL, "-x": LABEL, "-D": LABEL, "-F": READ, "-w": COUNT, "--align": COUNT, "--config": READ, "--debug": LABEL, "--indent": COUNT, "--library": NAMES, "--output": WRITE, "--string-max": COUNT, "--where": COUNT },
488
531
  }), { witness: LINUX_NAMESPACE }),
489
- launcher("valgrind"),
532
+ launcher("valgrind", {}, VALGRIND),
490
533
  launcher("firejail", { whenNoProgram: "shell" }, undefined, { witness: LINUX_NAMESPACE }),
491
534
  launcher("bwrap", {}, undefined, { witness: LINUX_NAMESPACE }),
492
535
  ];
@@ -507,12 +550,25 @@ const GIT_GREP = {
507
550
  traversal: { discloses: "contents", always: true },
508
551
  };
509
552
  const gitObjectSubcommand = (short = {}, long = {}) => ({ grammar: grammar(short, long), operands: { rest: OBJECT_PATH } });
553
+ const GIT_RUNS_LATER = [
554
+ ...["core.pager", "core.editor", "sequence.editor", "core.sshcommand", "core.gitproxy", "core.askpass", "core.alternaterefscommand",
555
+ "diff.external", "diff.*.command", "diff.*.textconv", "merge.*.driver", "filter.*.clean", "filter.*.smudge", "filter.*.process",
556
+ "difftool.*.cmd", "difftool.*.path", "mergetool.*.cmd", "mergetool.*.path", "interactive.difffilter",
557
+ "gpg.program", "gpg.*.program", "gpg.ssh.defaultkeycommand", "browser.*.cmd", "browser.*.path", "man.*.cmd", "man.*.path",
558
+ "guitool.*.cmd", "instaweb.httpd", "trailer.*.cmd", "trailer.*.command", "uploadpack.packobjectshook", "gc.recentobjectshook",
559
+ "remote.*.receivepack", "remote.*.uploadpack",
560
+ "sendemail.*tocmd", "sendemail.*cccmd", "sendemail.*headercmd", "sendemail.*sendmailcmd", "sendemail.*smtpserver"].map((key) => ({ key })),
561
+ { key: "pager.*", orBoolean: true }, { key: "core.fsmonitor", orBoolean: true },
562
+ { key: "alias.*", bang: true }, { key: "credential.helper", prefixed: "git credential-" }, { key: "credential.*.helper", prefixed: "git credential-" },
563
+ ];
510
564
  const git = {
511
565
  key: "git",
512
- grammars: [grammar({ p: SWITCH, P: SWITCH, C: val(WORKDIR), c: val() }, {
566
+ grammars: [grammar({ p: SWITCH, P: SWITCH, C: val(WORKDIR), c: val(undefined, { runsLater: { keys: GIT_RUNS_LATER } }) }, {
513
567
  ...longSwitches(["paginate", "no-pager", "bare", "no-replace-objects", "literal-pathspecs", "glob-pathspecs", "noglob-pathspecs", "icase-pathspecs", "no-optional-locks"]),
514
- "git-dir": val(WORKDIR), "work-tree": val(WORKDIR), namespace: val(), "config-env": val(), "super-prefix": val(),
515
- })],
568
+ "git-dir": val(WORKDIR), "work-tree": val(WORKDIR), namespace: val(), "config-env": val(undefined, { runsLater: { keys: GIT_RUNS_LATER, valueFrom: "environment" } }), "super-prefix": val(),
569
+ version: { arity: 0, mode: "banner" },
570
+ }, { acceptsAbbreviations: false })],
571
+ body: "option-values",
516
572
  subcommands: {
517
573
  commit: gitSubcommand("apsnvqeioz", "Su", "mFCct", ["message", "file", "template", "pathspec-from-file", "author", "date", "cleanup", "fixup", "squash", "trailer", "reuse-message", "reedit-message"]),
518
574
  tag: gitSubcommand("asfdvle", "n", "mFu", ["message", "file", "local-user", "cleanup", "trailer", "sort", "format", "contains", "no-contains", "points-at", "merged", "no-merged"]),
@@ -562,7 +618,7 @@ const CONTAINER_RUN = {
562
618
  };
563
619
  const containerRuntime = (key) => ({
564
620
  key,
565
- grammars: [grammar({ H: val(LABEL), c: val(LABEL), l: val(LABEL), D: SWITCH }, { host: val(LABEL), context: val(LABEL), "log-level": val(LABEL), config: val(READ), tlscacert: val(READ), tlscert: val(READ), tlskey: val(READ), ...longSwitches(["debug", "tls", "tlsverify"]) })],
621
+ grammars: [grammar({ H: val(LABEL), c: val(LABEL), l: val(LABEL), D: SWITCH }, { host: val(LABEL), context: val(LABEL), "log-level": val(LABEL), config: val(READ), tlscacert: val(READ), tlscert: val(READ), tlskey: val(READ), ...longSwitches(["debug", "tls", "tlsverify"]) }, { acceptsAbbreviations: false })],
566
622
  operands: { assignmentArguments: "not-paths" },
567
623
  subcommands: { run: CONTAINER_RUN, create: CONTAINER_RUN },
568
624
  witness: { declared: "a build tool / container client: what a NAME=value argument means is the build's or the container's" },
@@ -595,8 +651,8 @@ const copyGrammars = (bsdSuffixSwitch, destination) => implementations(grammar({
595
651
  const writerRows = [
596
652
  { key: "mkdir", grammars: [], operands: { rest: CREATED, dash: "file" }, witness: BINARY },
597
653
  { key: "touch", grammars: implementations(grammar({ h: { arity: 0, writesAt: ["landing", "entry"] } }, { "no-dereference": { arity: 0, writesAt: ["landing", "entry"] } }), {}, { lacks: ["--no-dereference"] }), operands: { rest: TOUCHED, dash: "file" }, witness: BINARY },
598
- { key: "rm", grammars: implementations(grammar({ ...switches("rR", { selects: "traversal" }), f: { arity: 0, mode: "forced" } }, { ...longSwitches(["recursive"], { selects: "traversal" }), force: { arity: 0, mode: "forced" } }), {}, { lacks: ["--recursive", "--force"] }), operands: { rest: REMOVED, dash: "file" }, witness: BINARY },
599
- { key: "rmdir", grammars: implementations(grammar({ p: { arity: 0, effect: "writes" } }, { parents: { arity: 0, effect: "writes" } }), {}, { lacks: ["--parents"] }), operands: { rest: REMOVED, dash: "file" }, witness: BINARY },
654
+ { key: "rm", grammars: implementations(grammar({ ...switches("rR", { selects: "traversal" }), f: { arity: 0, mode: "forced" }, ...switches("diIvPWx", { effect: "none" }) }, { ...longSwitches(["recursive"], { selects: "traversal" }), force: { arity: 0, mode: "forced" }, ...longSwitches(["dir", "verbose", "one-file-system", "no-preserve-root", "-presume-input-tty"], { effect: "none" }), interactive: { arity: "attached", value: LABEL, effect: "none" }, "preserve-root": { arity: "attached", value: LABEL, effect: "none" }, ...BANNERS }), { lacks: ["-P", "-W", "-x"] }, { lacks: ["--recursive", "--force", "--dir", "--verbose", "--one-file-system", "--no-preserve-root", "---presume-input-tty", "--interactive", "--preserve-root", "--help", "--version"] }), operands: { rest: REMOVED, dash: "file" }, optionsComplete: true, witness: BINARY },
655
+ { key: "rmdir", grammars: implementations(grammar({ p: { arity: 0, effect: "writes" }, v: { arity: 0, effect: "none" } }, { parents: { arity: 0, effect: "writes" }, ...longSwitches(["verbose", "ignore-fail-on-non-empty"], { effect: "none" }), ...BANNERS }), {}, { lacks: ["--parents", "--verbose", "--ignore-fail-on-non-empty", "--help", "--version"] }), operands: { rest: REMOVED, dash: "file" }, optionsComplete: true, witness: BINARY },
600
656
  { key: "tee", grammars: [], operands: { rest: WRITE_THROUGH, dash: "file" }, witness: BINARY },
601
657
  { key: "cp", grammars: copyGrammars(true, COPY_DESTINATION), operands: { rest: COPY_SOURCE, last: COPY_DESTINATION, dash: "file" }, witness: BINARY },
602
658
  { key: "mv", grammars: copyGrammars(false, MOVE_DESTINATION), operands: { rest: MOVE_SOURCE, last: MOVE_DESTINATION, dash: "file" }, witness: BINARY },
@@ -651,6 +707,7 @@ export const PROGRAM_ROWS = [
651
707
  ...[...shellWordsOf("stack_mover")].map((key) => ({ key, grammars: [], movesBase: "stack", witness: BUILTIN_ONLY })),
652
708
  ...SHELLS.map((s) => shellRow(s, "c-option", BINARY, [SHELL_GRAMMARS[s]], "script")),
653
709
  shellRow("eval", "words", BUILTIN_ONLY), shellRow("source", "file-operand", BUILTIN_ONLY), shellRow(".", "file-operand", BUILTIN_ONLY), shellRow("parallel", "non-option-words", BINARY, [], "script"),
710
+ shellRow("fc", "history", BUILTIN_ONLY, [grammar({ ...switches("nr"), s: { arity: 0, effect: "executes" }, ...switches("l", { mode: "banner" }), e: val(COMMAND, { executesWhenValue: "-" }) }, {}, { afterFirstOperand: "operands" })]),
654
711
  ...codeBodyRows,
655
712
  ...[...shellWordsOf("block")].map((k) => block(k, BLOCK_GRAMMARS[k])),
656
713
  ];
@@ -5,6 +5,7 @@ import { thrownText } from "../../core/thrown-value.js";
5
5
  import { agentConfigDirTail } from "../../core/agent-config-dir.js";
6
6
  import { validatePath } from "../../core/effective-path-target.js";
7
7
  import { readDenyPatternRun, readDenyRowAnchor, readDenyRunHead } from "./read-deny.js";
8
+ import { isAbsence } from "./shell-read-environment.js";
8
9
  export const AUXILIARY_HOME_PROBE_MS = 2_000;
9
10
  const said = new Set();
10
11
  function sayOnce(kind, line) {
@@ -75,7 +76,7 @@ async function homeSpellingsOf(env, home, signal) {
75
76
  const canonical = await env.canonicalPath(home, signal);
76
77
  if (canonical.ok)
77
78
  return { spellings: canonical.value !== home ? [home, canonical.value] : [home], canonical: canonical.value, answered: true };
78
- return { spellings: [home], answered: canonical.error.code === "not_found" || canonical.error.code === "not_directory" };
79
+ return { spellings: [home], answered: isAbsence(canonical.error.code) };
79
80
  }
80
81
  catch {
81
82
  return { spellings: [home], answered: false };
@@ -202,7 +203,7 @@ export async function whereItsAnsweredPrefixLeads(env, at, family = pathFamilyOf
202
203
  }
203
204
  const NO_CHAIN = { ancestors: [], whole: false, unread: [] };
204
205
  const unreadChain = (at) => ({ ancestors: [], whole: false, unread: [at] });
205
- const identityUnread = (id) => id === undefined || (!id.ok && id.error.code !== "not_found" && id.error.code !== "not_directory" && id.error.code !== "not_supported");
206
+ const identityUnread = (id) => id === undefined || (!id.ok && !isAbsence(id.error.code) && id.error.code !== "not_supported");
206
207
  async function chainAbove(env, identity, canonical, signal, deadline) {
207
208
  let own;
208
209
  const ancestors = [];
@@ -1,3 +1,4 @@
1
+ import type { EstablishedRead } from "./shell-read-judge.js";
1
2
  import { type EstablishedPlan } from "./shell-read-establish.js";
2
3
  import type { ExecutionEnv } from "../../internal/harness-types.js";
3
4
  import type { ToolCallRequest } from "../../core/tool-policy.js";
@@ -35,8 +36,8 @@ export declare function dangerousRemovalOf(path: string, seat: RemovalSeat): Dan
35
36
  * (`rewritten`), the operands of a removal its argv stream fills in (`stream`), or an operand relative to the directory a
36
37
  * launcher option moved the removal into (`relocated`); or a shell handed text the engine cannot read
37
38
  * (`unverifiable_shell_text`) — a text a program produces (`produced`: what a pipe or a `<(…)` carries, an argv a stream
38
- * fills in, a substitution), or a text nobody read (`unread`: one the lexer could not read at all, {@link unreadPlan}, or a
39
- * segment whose program the walk could not name, {@link programUnread}). */
39
+ * fills in, a substitution), a text nobody read (`unread`: one the lexer could not read at all, {@link unreadPlan}), or a
40
+ * segment whose program the walk could not name (`position`, {@link programUnread}). */
40
41
  export type RemovalFinding = (DangerousRemoval & {
41
42
  readonly guard: "dangerous_delete";
42
43
  readonly untrackedFrom?: string;
@@ -49,7 +50,7 @@ export type RemovalFinding = (DangerousRemoval & {
49
50
  readonly guard: "unverifiable_shell_text";
50
51
  readonly program: string;
51
52
  readonly text: string;
52
- readonly source: "produced" | "unread";
53
+ readonly source: "produced" | "unread" | "position";
53
54
  };
54
55
  /**
55
56
  * The names a removal target may be built on and still be verified: a root the SHELL provides, never one a command's
@@ -62,10 +63,12 @@ export type RemovalFinding = (DangerousRemoval & {
62
63
  export declare const KNOWN_SAFE_ROOTS: readonly string[];
63
64
  /**
64
65
  * The removal findings of an established plan — ONE walk, nested plans included (a `bash -c` / `sh -c` text, a substitution
65
- * body), two words. For every `rm` / `rmdir` segment, each of its OPERANDS (every write target that is not one of its
66
- * option words: a word some implementation's reading of the program's row writes, {@link writeReadings}) as the plan
67
- * established it on the WRITE face (the entry the program unlinks, and the deny-floor shadow of a home word the plan did
68
- * not place) is judged by {@link dangerousRemovalOf}. In a recursive forced removal ({@link recursiveForce}), an operand
66
+ * body), two words. For every removal segment ({@link removalProgramOf}: an `rm` / `rmdir` the text names, or a program it
67
+ * does not settle, read as `rm`) whose argv the program does not refuse outright ({@link optionsReceived}), each of its
68
+ * OPERANDS (every write target that is not one of its option words: a word some implementation's reading of the
69
+ * program's row writes, {@link writeReadings}) as the plan established it on the WRITE face (the entry the program
70
+ * unlinks, and the deny-floor shadow of a home word the plan did not place) is judged by {@link dangerousRemovalOf},
71
+ * save one the program refuses by its own first step ({@link refusedByRemoval}: `.`, `..`). In a recursive forced removal ({@link recursiveForce}), an operand
69
72
  * the shell rewrites from a value the plan's table does not settle — a name with no value here or one set in a form the
70
73
  * table cannot read, a substitution, a parameter operator, a positional parameter — is an unverifiable target, save a
71
74
  * literal path below a known-safe root the shell still provides there ({@link belowKnownSafeRoot}); so are the operands
@@ -80,6 +83,13 @@ export declare const KNOWN_SAFE_ROOTS: readonly string[];
80
83
  * own. The read-face copy of an operand is not judged: it follows a link the removal does not follow.
81
84
  */
82
85
  export declare function removalFindingsOf(established: EstablishedPlan, seat: RemovalSeat, safeRoots?: readonly string[], enclosingRelocated?: boolean): RemovalFinding[];
86
+ /** Does the removal program refuse this operand by its own first step — its last path component is `.` or `..`, trailing
87
+ * separators stripped (POSIX `rm` step 1, which GNU, BSD and BusyBox `rm` keep; `rmdir(2)` fails on such a component
88
+ * too)? It removes nothing there, wherever the name lands. The name is the one the program RECEIVES: a pattern's match
89
+ * (the spelling an expansion carries beside its origin: `.*` hands it `.` and `..` beside the dotfiles, and only those two
90
+ * are refused), else the word as the plan spells it; a word the shell rewrites from a value the plan does not settle
91
+ * names nothing known, and is never refused here. */
92
+ export declare function refusedByRemoval(read: EstablishedRead): boolean;
83
93
  /** Does this plan (nested plans included) run a removal program, hand a shell a script built when it runs, or hold text
84
94
  * nobody could read ({@link unreadPlan}, {@link programUnread})? The guard establishes nothing for a command that does none. */
85
95
  export declare function planRunsRemoval(plan: EstablishedPlan["plan"]): boolean;
@@ -2,11 +2,11 @@ import { planShellReads } from "./shell-read-plan.js";
2
2
  import { literalTextOf } from "../../shell/lex.js";
3
3
  import { createShellReadEstablisher, establishShellReadsLexically } from "./shell-read-establish.js";
4
4
  import { isAbsoluteForFamily, isWinFormPath, normalizeAbsPathLexically } from "./safety.js";
5
- import { launchedCommands, launchedReadings, reentryText, scriptFileAt, spellLaunched } from "./shell-reentry-text.js";
6
- import { readProgramPosition } from "./bash-program-position.js";
5
+ import { launchedCommands, launchedReadings, reentryText, runsStoredLine, scriptFileAt, spellLaunched } from "./shell-reentry-text.js";
6
+ import { declinedProgramSentence, readProgramPosition } from "./bash-program-position.js";
7
7
  import { bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
8
8
  import { programArgs } from "./shell-read-write-targets.js";
9
- import { implementationsOf, programRow, runsShellText, selectedOptions, writeReadings } from "./program-read-model.js";
9
+ import { declaresOption, implementationsOf, programRow, rowRefusesArgv, runsShellText, selectedOptions, writeReadings } from "./program-read-model.js";
10
10
  import { inlineUntrusted } from "../../core/untrusted-text.js";
11
11
  export const SHELL_REMOVAL_COMMANDS = Object.freeze(["rm", "rmdir"]);
12
12
  const REMOVAL_COMMAND_SET = new Set(SHELL_REMOVAL_COMMANDS);
@@ -68,10 +68,19 @@ function belowKnownSafeRoot(word, safe, segment) {
68
68
  const below = rest.map((p) => p.literal).join("");
69
69
  return /^\/[^*?[\]]+$/.test(below) && !below.split("/").includes("..");
70
70
  }
71
- function recursiveForce(row, args) {
71
+ function optionsReceived(row, args) {
72
+ const rewrittenAt = args.findIndex((w) => w.rewritten === true);
73
+ const asWritten = (rewrittenAt < 0 ? args : args.slice(0, rewrittenAt)).map((w) => w.spelled);
74
+ const present = args.filter((w) => w.vanishes !== true);
75
+ const chosenAt = present.findIndex((w) => w.rewritten === true);
76
+ const shellChooses = chosenAt >= 0 && writeReadings(row, present.slice(0, chosenAt + 1).map((w) => w.spelled)).some((r) => r.written.length === 0);
77
+ return { refused: rowRefusesArgv(row, asWritten), shellChooses };
78
+ }
79
+ function recursiveForce(row, args, shellChooses) {
72
80
  return implementationsOf(row).some((view) => {
73
81
  const selected = selectedOptions(view, args, "one-implementation");
74
- return selected.some((o) => o.selects === "traversal") && selected.some((o) => o.mode === "forced");
82
+ const selects = (has) => selected.some(has) || (shellChooses && declaresOption(view, has));
83
+ return selects((o) => o.selects === "traversal") && selects((o) => o.mode === "forced");
75
84
  });
76
85
  }
77
86
  export function removalFindingsOf(established, seat, safeRoots = KNOWN_SAFE_ROOTS, enclosingRelocated = false) {
@@ -81,23 +90,27 @@ export function removalFindingsOf(established, seat, safeRoots = KNOWN_SAFE_ROOT
81
90
  const safe = new Set(safeRoots);
82
91
  for (const segment of established.plan.segments) {
83
92
  const program = segment.verb?.basename;
84
- const row = program !== undefined && REMOVAL_COMMAND_SET.has(program) ? programRow(program) : undefined;
85
- if (program !== undefined && row !== undefined) {
86
- const args = programArgs(segment);
87
- const spelled = args.map((w) => w.spelled);
93
+ const removal = removalProgramOf(segment);
94
+ const args = removal === undefined ? [] : programArgs(segment);
95
+ const spelled = args.map((w) => w.spelled);
96
+ const received = removal === undefined ? undefined : optionsReceived(removal.row, args);
97
+ if (removal !== undefined && received?.refused === false) {
98
+ const { row, named } = removal;
88
99
  const removed = new Set(writeReadings(row, spelled).flatMap((r) => r.written.map((w) => w.at)));
89
100
  const options = new Set(args.filter((_, at) => !removed.has(at)).map((w) => w.raw));
90
- const forced = recursiveForce(row, spelled);
101
+ const forced = recursiveForce(row, spelled, received.shellChooses);
91
102
  if (forced && segment.reentry === "argv")
92
- out.push({ guard: "unverifiable_delete", program, operand: segment.text.trim(), source: "stream" });
103
+ out.push({ guard: "unverifiable_delete", program: removal.program, operand: segment.text.trim(), source: "stream" });
93
104
  const relocated = enclosingRelocated || readProgramPosition(segment.argv).launcherMovesBase;
94
105
  for (const read of established.reads.get(segment) ?? []) {
95
- if (read.candidate.face !== "write" || options.has(read.candidate.raw))
106
+ if ((named && read.candidate.face !== "write") || read.candidate.provenance === "program_path" || options.has(read.candidate.raw))
107
+ continue;
108
+ if (refusedByRemoval(read))
96
109
  continue;
97
110
  const kind = removalKindOf(read, seat);
98
111
  if (kind !== undefined) {
99
112
  const untracked = read.candidate.bases === "unmodelled" ? established.plan.segments.find((s) => s.bases === "unmodelled")?.text.trim() : undefined;
100
- out.push({ guard: "dangerous_delete", kind, program, operand: read.candidate.raw, target: read.target ?? read.candidate.floorSpelled ?? read.candidate.spelled, ...(untracked !== undefined ? { untrackedFrom: untracked } : {}) });
113
+ out.push({ guard: "dangerous_delete", kind, program: removal.program, operand: read.candidate.raw, target: read.target ?? read.candidate.floorSpelled ?? read.candidate.spelled, ...(untracked !== undefined ? { untrackedFrom: untracked } : {}) });
101
114
  continue;
102
115
  }
103
116
  if (!forced || read.established !== "unestablishable" || (read.candidate.expands !== "unresolvable" && !relocated))
@@ -105,14 +118,14 @@ export function removalFindingsOf(established, seat, safeRoots = KNOWN_SAFE_ROOT
105
118
  const word = segment.argv.find((w) => w.raw === read.candidate.raw);
106
119
  if (word !== undefined && belowKnownSafeRoot(word, safe, segment))
107
120
  continue;
108
- out.push({ guard: "unverifiable_delete", program, operand: read.candidate.raw, source: read.candidate.expands === "unresolvable" ? "rewritten" : "relocated" });
121
+ out.push({ guard: "unverifiable_delete", program: removal.program, operand: read.candidate.raw, source: read.candidate.expands === "unresolvable" ? "rewritten" : "relocated" });
109
122
  }
110
123
  }
111
124
  const script = runtimeScriptOf(segment);
112
125
  if (script !== undefined && program !== undefined)
113
126
  out.push({ guard: "unverifiable_shell_text", program, text: script, source: "produced" });
114
127
  if (programUnread(segment))
115
- out.push({ guard: "unverifiable_shell_text", program: segment.launcherChain[0] ?? segment.text.trim(), text: segment.text.trim(), source: "unread" });
128
+ out.push({ guard: "unverifiable_shell_text", program: segment.launcherChain[0] ?? segment.text.trim(), text: segment.text.trim(), source: "position" });
116
129
  const handedRelocated = enclosingRelocated || readProgramPosition(segment.argv).launcherMovesBase;
117
130
  for (const nested of segment.nested) {
118
131
  if (unreadPlan(nested)) {
@@ -143,7 +156,7 @@ function runtimeScriptOf(segment) {
143
156
  const source = file === undefined ? undefined : argv[file];
144
157
  if (source !== undefined && source.pieces.length === 1 && source.pieces[0].expansion?.kind === "process")
145
158
  return source.raw;
146
- if (pos.argvFromStdin)
159
+ if (pos.argvFromStdin || runsStoredLine(program.basename, argv.map((w) => w.folded)))
147
160
  return segment.text.trim();
148
161
  }
149
162
  const named = pos.program !== undefined && pos.refusal === undefined ? [{ shell: program.basename, words: segment.argv.slice(pos.program.index + 1).map((_, k) => spell(pos.program.index + 1 + k)) }] : [];
@@ -157,8 +170,21 @@ function runtimeScriptOf(segment) {
157
170
  }
158
171
  const unreadPlan = (plan) => plan.unreadable !== undefined && plan.segments.length === 0;
159
172
  const programUnread = (s) => s.declines.includes("unreadable_program_position");
173
+ const programNameUnsettled = (s) => s.verb !== undefined && s.reads.some((c) => c.provenance === "program_path" && c.expands === "unresolvable");
174
+ const RM_REMOVAL_ROW = programRow("rm");
175
+ function removalProgramOf(s) {
176
+ const basename = s.verb?.basename;
177
+ const row = basename !== undefined && REMOVAL_COMMAND_SET.has(basename) ? programRow(basename) : undefined;
178
+ if (row !== undefined)
179
+ return { program: basename, row, named: true };
180
+ return programNameUnsettled(s) && RM_REMOVAL_ROW !== undefined ? { program: s.verb.name, row: RM_REMOVAL_ROW, named: false } : undefined;
181
+ }
182
+ export function refusedByRemoval(read) {
183
+ const name = read.established === "expansion" ? read.spellings.find((s) => s !== read.origin) : read.candidate.expands === "unresolvable" ? undefined : read.candidate.spelled;
184
+ return name !== undefined && /(?:^|\/)\.\.?\/*$/.test(name);
185
+ }
160
186
  export function planRunsRemoval(plan) {
161
- return unreadPlan(plan) || plan.segments.some((s) => (s.verb !== undefined && REMOVAL_COMMAND_SET.has(s.verb.basename)) || runtimeScriptOf(s) !== undefined || programUnread(s) || s.nested.some(planRunsRemoval));
187
+ return unreadPlan(plan) || plan.segments.some((s) => removalProgramOf(s) !== undefined || runtimeScriptOf(s) !== undefined || programUnread(s) || s.nested.some(planRunsRemoval));
162
188
  }
163
189
  const DANGEROUS_REMOVAL_SENTENCE = {
164
190
  filesystem_root: (p, t) => `\`${p}\` would remove the filesystem root (${t})`,
@@ -172,8 +198,9 @@ const UNVERIFIABLE_REMOVAL_SENTENCE = {
172
198
  relocated: (p, o) => `\`${p}\` recursive+force target ${o} is relative to a directory a launcher option moved the program into, which this command does not settle — the deleted path cannot be verified from this command`,
173
199
  };
174
200
  const UNREADABLE_TEXT_SENTENCE = {
175
- produced: (p, t) => `\`${p}\` runs shell text that is built only when the command runs (${t}) — the text this shell will run cannot be read by the engine, so whether it removes anything is unknown`,
176
- unread: (p, t) => `\`${p}\` runs shell text this guard cannot read (${t}) — the text this shell will run cannot be read by the engine, so whether it removes anything is unknown`,
201
+ produced: (p, t) => `\`${p}\` runs shell text that is built only when the command runs (${inlineUntrusted(t, 200)}) — the text this shell will run cannot be read by the engine, so whether it removes anything is unknown; write the text into the command itself (a \`-c\` string, a here-document or a here-string) and the engine reads it as it reads any command`,
202
+ unread: (p, t) => `\`${p}\` runs shell text this guard cannot read (${inlineUntrusted(t, 200)}) — the text this shell will run cannot be read by the engine, so whether it removes anything is unknown`,
203
+ position: (_p, t) => inlineUntrusted(declinedProgramSentence(t), 400),
177
204
  };
178
205
  function verdictOf(findings) {
179
206
  for (const f of findings) {
@@ -187,7 +214,7 @@ function verdictOf(findings) {
187
214
  return { guard: f.guard, text: UNVERIFIABLE_REMOVAL_SENTENCE[f.source](f.program, inlineUntrusted(f.operand, 200)) };
188
215
  for (const f of findings)
189
216
  if (f.guard === "unverifiable_shell_text")
190
- return { guard: f.guard, text: UNREADABLE_TEXT_SENTENCE[f.source](f.program, inlineUntrusted(f.text, 200)) };
217
+ return { guard: f.guard, text: UNREADABLE_TEXT_SENTENCE[f.source](f.program, f.text) };
191
218
  return undefined;
192
219
  }
193
220
  const REMOVAL_RESOLVE_TIMEOUT_MS = 10_000;
@@ -1,5 +1,6 @@
1
1
  import type { ExecutionEnv } from "../../internal/harness-types.js";
2
2
  import { type HomeViewsCarrier } from "./read-deny-home.js";
3
+ import { type FsGlob } from "./fs-glob.js";
3
4
  /** The JS scanner's own output ceiling, in bytes of emitted content rows. Pinned to the
4
5
  * ripgrep leg's figure ({@link MAX_EXEC_OUTPUT_BYTES}, the exec pipe's rolling-tail bound) so the two
5
6
  * legs cannot disagree about how much one search may return; see the collection site in {@link jsGrep}
@@ -56,6 +57,15 @@ export declare function splitGlobParam(glob: string): string[];
56
57
  * Exported for direct injection testing.
57
58
  */
58
59
  export declare function shellQuote(s: string): string;
60
+ /** Out-parameter for {@link compileGlobToken}: set to `true` when a glob-syntax construct
61
+ * could NOT be compiled and silently fell back to matching the metacharacter LITERALLY. Only the
62
+ * glob-syntax lane (`glob = true`) can set it; the gitignore lane has no such syntax to fail at. */
63
+ export interface GlobCompileReport {
64
+ degraded: boolean;
65
+ }
66
+ /** Compile a gitignore/glob token to a matcher tested against a relative posix path. The matcher is the engine in
67
+ * `fs-glob.ts`: its cost is bounded by the token's length times the path's, whatever the arrangement of the stars. */
68
+ export declare function compileGlobToken(token: string, anchored: boolean, glob?: boolean, report?: GlobCompileReport): FsGlob;
59
69
  /**
60
70
  * The tokens of a `glob` argument that this engine CANNOT compile as glob syntax, i.e. the ones whose
61
71
  * metacharacter would silently degrade to a literal match.
@@ -4,6 +4,7 @@ import { MAX_EXEC_OUTPUT_BYTES } from "../../core/exec-output-tail.js";
4
4
  import { anchorSpelling, hasBinaryExtension, isWinFormPath, pathFamilyOf } from "./safety.js";
5
5
  import { HomeViewUnreadError, homeViewsOf } from "./read-deny-home.js";
6
6
  import { createEntryDenyJudge } from "./dir-entries.js";
7
+ import { compileFsGlob, fsGlobClass } from "./fs-glob.js";
7
8
  import { thrownText } from "../../core/thrown-value.js";
8
9
  const WALK_MAX_FILES = 5000;
9
10
  const WALK_MAX_DEPTH = 32;
@@ -78,32 +79,31 @@ export function splitGlobParam(glob) {
78
79
  export function shellQuote(s) {
79
80
  return `'${s.replace(/'/g, `'\\''`)}'`;
80
81
  }
81
- function globTokenToRegExp(token, anchored, glob = false, report) {
82
- const charToRe = (ch) => ch === "*" ? "[^/]*" : ch === "?" ? "[^/]" : ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
83
- let re = "";
82
+ export function compileGlobToken(token, anchored, glob = false, report) {
83
+ const unitOf = (ch) => (ch === "*" ? { kind: "star" } : ch === "?" ? { kind: "one" } : { kind: "text", text: ch });
84
+ const tokens = [];
84
85
  for (let i = 0; i < token.length; i++) {
85
86
  const c = token[i];
86
87
  if (c === "*") {
87
88
  if (token[i + 1] === "*") {
88
- re += ".*";
89
+ tokens.push({ kind: "globstar" });
89
90
  i++;
90
91
  if (token[i + 1] === "/")
91
92
  i++;
92
93
  }
93
94
  else
94
- re += "[^/]*";
95
+ tokens.push({ kind: "star" });
95
96
  }
96
97
  else if (c === "?")
97
- re += "[^/]";
98
+ tokens.push({ kind: "one" });
98
99
  else if (glob && c === "{") {
99
100
  const close = token.indexOf("}", i);
100
101
  if (close > i + 1) {
101
- const alts = token.slice(i + 1, close).split(",").map((alt) => [...alt].map(charToRe).join(""));
102
- re += `(?:${alts.join("|")})`;
102
+ tokens.push({ kind: "alt", alternatives: token.slice(i + 1, close).split(",").map((alt) => [...alt].map(unitOf)) });
103
103
  i = close;
104
104
  }
105
105
  else {
106
- re += "\\{";
106
+ tokens.push({ kind: "text", text: "{" });
107
107
  if (report)
108
108
  report.degraded = true;
109
109
  }
@@ -122,34 +122,28 @@ function globTokenToRegExp(token, anchored, glob = false, report) {
122
122
  let cls;
123
123
  if (close !== -1) {
124
124
  const body = token.slice(bodyStart, close).replace(/[\\[\]]/g, "\\$&").replace(/\//g, "");
125
- cls = `[${neg ? "^/" : ""}${body}]`;
126
- try {
127
- new RegExp(cls);
128
- }
129
- catch {
130
- cls = undefined;
131
- }
125
+ cls = fsGlobClass(`[${neg ? "^/" : ""}${body}]`);
132
126
  }
133
127
  if (cls !== undefined) {
134
- re += cls;
128
+ tokens.push({ kind: "class", class: cls });
135
129
  i = close;
136
130
  }
137
131
  else {
138
- re += "\\[";
132
+ tokens.push({ kind: "text", text: "[" });
139
133
  if (report)
140
134
  report.degraded = true;
141
135
  }
142
136
  }
143
137
  else
144
- re += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
138
+ tokens.push({ kind: "text", text: c });
145
139
  }
146
- return new RegExp(`${anchored ? "^" : "(^|/)"}${re}$`);
140
+ return compileFsGlob(tokens, anchored ? "start" : "segment");
147
141
  }
148
142
  export function invalidGlobTokens(glob) {
149
143
  const bad = [];
150
144
  for (const token of splitGlobParam(glob)) {
151
145
  const report = { degraded: false };
152
- globTokenToRegExp(normalizeGlobToken(token), globIsAnchored(token), true, report);
146
+ compileGlobToken(normalizeGlobToken(token), globIsAnchored(token), true, report);
153
147
  if (report.degraded)
154
148
  bad.push(token);
155
149
  }
@@ -187,7 +181,7 @@ export function gitignoreMatcher(content) {
187
181
  const anchored = pat.startsWith("/");
188
182
  if (anchored)
189
183
  pat = pat.replace(/^\/+/, "");
190
- return { neg, dirOnly, anchored, re: globTokenToRegExp(pat, anchored), bare: pat };
184
+ return { neg, dirOnly, anchored, re: compileGlobToken(pat, anchored), bare: pat };
191
185
  });
192
186
  return (relRaw, isDir) => {
193
187
  const rel = relRaw.replace(/\\/g, "/");
@@ -834,7 +828,7 @@ export async function jsGrepDetailed(env, root, p, signal, guards, deny, denyOut
834
828
  return rel(abs);
835
829
  };
836
830
  if (p.glob) {
837
- const globRes = splitGlobParam(p.glob).map((g) => globTokenToRegExp(normalizeGlobToken(g), globIsAnchored(g), true));
831
+ const globRes = splitGlobParam(p.glob).map((g) => compileGlobToken(normalizeGlobToken(g), globIsAnchored(g), true));
838
832
  if (globRes.length > 0)
839
833
  files = files.filter((f) => globRes.some((re) => re.test(rel(f).replace(/\\/g, "/"))));
840
834
  }
@@ -842,7 +836,7 @@ export async function jsGrepDetailed(env, root, p, signal, guards, deny, denyOut
842
836
  if (p.type) {
843
837
  const tg = TYPE_GLOBS[p.type.toLowerCase()];
844
838
  if (tg) {
845
- const typeRe = globTokenToRegExp(tg, false, true);
839
+ const typeRe = compileGlobToken(tg, false, true);
846
840
  files = files.filter((f) => typeRe.test(basename(f)));
847
841
  }
848
842
  else {
@@ -1380,7 +1374,7 @@ export async function runGlobDetailed(env, root, pattern, opts = {}, signal) {
1380
1374
  }
1381
1375
  const pat = normalizeGlobToken(pattern);
1382
1376
  const anchored = globIsAnchored(pattern);
1383
- const re = globTokenToRegExp(pat, anchored, true);
1377
+ const re = compileGlobToken(pat, anchored, true);
1384
1378
  const startPrefix = start.replace(/[\\/]+$/, "") + (start.includes("\\") ? "\\" : "/");
1385
1379
  const startRelPrefix = startPrefix.startsWith(rootPrefix) ? startPrefix.slice(rootPrefix.length) : startPrefix;
1386
1380
  const toStartRel = (p) => {
@@ -1396,7 +1390,7 @@ export async function runGlobDetailed(env, root, pattern, opts = {}, signal) {
1396
1390
  const prunable = anchored && !groupSpansSlash;
1397
1391
  const firstStarStar = patternSegs.findIndex((s) => s.includes("**"));
1398
1392
  const starStarIdx = firstStarStar === -1 ? Infinity : firstStarStar;
1399
- const segRes = prunable ? patternSegs.map((s) => globTokenToRegExp(s, true, true)) : [];
1393
+ const segRes = prunable ? patternSegs.map((s) => compileGlobToken(s, true, true)) : [];
1400
1394
  const isLiteralSeg = (s) => s.length > 0 && !/[*?[{]/.test(s);
1401
1395
  const literalAt = prunable ? patternSegs.map((s) => (isLiteralSeg(s) ? s : undefined)) : [];
1402
1396
  const literalAfterStarStar = new Set(prunable ? patternSegs.filter((s, i) => i > starStarIdx && isLiteralSeg(s)) : []);