@sema-agent/core 7.51.2 → 7.52.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 (363) hide show
  1. package/CHANGELOG.md +504 -2
  2. package/dist/agents/agent-transcript-tool.d.ts +1 -1
  3. package/dist/agents/cascade.js +4 -0
  4. package/dist/agents/cumulative-stats.d.ts +3 -0
  5. package/dist/agents/cumulative-stats.js +3 -0
  6. package/dist/agents/list-agents-tool.d.ts +1 -1
  7. package/dist/agents/read-inbox-tool.d.ts +1 -1
  8. package/dist/agents/repair-loop.js +4 -0
  9. package/dist/agents/send-message-tool.d.ts +11 -16
  10. package/dist/agents/send-message-tool.js +11 -13
  11. package/dist/agents/subagent.js +6 -4
  12. package/dist/agents/tool-filter.d.ts +40 -13
  13. package/dist/agents/tool-filter.js +28 -14
  14. package/dist/bench/metrics.d.ts +1 -0
  15. package/dist/brain/anthropic.js +16 -12
  16. package/dist/brain/model-presets.d.ts +4 -5
  17. package/dist/brain/model-presets.js +4 -4
  18. package/dist/brain/open-responses.js +16 -12
  19. package/dist/brain/openai.js +16 -12
  20. package/dist/brain/request-params.d.ts +12 -33
  21. package/dist/brain/request-params.js +5 -2
  22. package/dist/brain/request-route.d.ts +9 -8
  23. package/dist/brain/request-route.js +1 -0
  24. package/dist/brain/route-adjudicator.d.ts +117 -47
  25. package/dist/brain/route-adjudicator.js +171 -42
  26. package/dist/brain/route-conformance.js +2 -2
  27. package/dist/brain/status-sink.d.ts +18 -0
  28. package/dist/brain/status-sink.js +7 -0
  29. package/dist/core/allow-entry.d.ts +40 -8
  30. package/dist/core/allow-entry.js +23 -16
  31. package/dist/core/auto-compaction.d.ts +23 -20
  32. package/dist/core/auto-compaction.js +37 -16
  33. package/dist/core/auto-promote.d.ts +2 -1
  34. package/dist/core/auto-promote.js +2 -2
  35. package/dist/core/caller-tool-name.d.ts +9 -0
  36. package/dist/core/caller-tool-name.js +13 -0
  37. package/dist/core/checkpoint-requirements.d.ts +7 -1
  38. package/dist/core/checkpoint-requirements.js +2 -0
  39. package/dist/core/checkpoint-store.d.ts +36 -12
  40. package/dist/core/checkpoint-store.js +1 -1
  41. package/dist/core/child-clamp-seats.d.ts +6 -0
  42. package/dist/core/child-clamp-seats.js +2 -1
  43. package/dist/core/credential-vocabulary.d.ts +33 -8
  44. package/dist/core/credential-vocabulary.js +32 -7
  45. package/dist/core/decision-receipt.d.ts +2 -2
  46. package/dist/core/delegation-frames.d.ts +9 -2
  47. package/dist/core/delegation-name.d.ts +18 -0
  48. package/dist/core/delegation-name.js +47 -0
  49. package/dist/core/engine-capabilities.d.ts +8 -1
  50. package/dist/core/engine-capabilities.js +1 -1
  51. package/dist/core/engine-notice.d.ts +36 -19
  52. package/dist/core/frontmatter-fence.d.ts +10 -0
  53. package/dist/core/frontmatter-fence.js +6 -0
  54. package/dist/core/gate-fold.js +5 -23
  55. package/dist/core/gate-lanes.js +356 -270
  56. package/dist/core/governance-codes.d.ts +9 -4
  57. package/dist/core/governance-codes.js +9 -3
  58. package/dist/core/hooks.d.ts +276 -734
  59. package/dist/core/hooks.js +97 -192
  60. package/dist/{tools/fs → core}/image-admission.d.ts +21 -3
  61. package/dist/{tools/fs → core}/image-admission.js +24 -6
  62. package/dist/core/mcp-connection-pool.d.ts +4 -1
  63. package/dist/core/mcp-connection-pool.js +7 -2
  64. package/dist/core/mcp-probe.d.ts +12 -9
  65. package/dist/core/mcp-probe.js +2 -2
  66. package/dist/core/mcp-server-spec.d.ts +16 -0
  67. package/dist/core/mcp.d.ts +11 -7
  68. package/dist/core/mcp.js +11 -5
  69. package/dist/core/memory-engine/data-plane.js +5 -1
  70. package/dist/core/memory-engine/distiller.js +16 -2
  71. package/dist/core/memory-engine/engine.d.ts +8 -11
  72. package/dist/core/memory-engine/engine.js +9 -5
  73. package/dist/core/memory-engine/frontmatter.d.ts +6 -0
  74. package/dist/core/memory-engine/frontmatter.js +19 -16
  75. package/dist/core/memory.d.ts +2 -2
  76. package/dist/core/mode-policy.d.ts +4 -0
  77. package/dist/core/mode-policy.js +7 -5
  78. package/dist/core/model-seat.d.ts +4 -1
  79. package/dist/core/mods/dollar.d.ts +109 -0
  80. package/dist/core/mods/dollar.js +154 -0
  81. package/dist/core/mods/host.d.ts +23 -0
  82. package/dist/core/mods/host.js +21 -0
  83. package/dist/core/mods/kernel/budget.d.ts +43 -0
  84. package/dist/core/mods/kernel/budget.js +83 -0
  85. package/dist/core/mods/kernel/dispatch.d.ts +100 -0
  86. package/dist/core/mods/kernel/dispatch.js +701 -0
  87. package/dist/core/mods/kernel/events.d.ts +242 -0
  88. package/dist/core/mods/kernel/events.js +210 -0
  89. package/dist/core/mods/kernel/index.d.ts +6 -0
  90. package/dist/core/mods/kernel/index.js +5 -0
  91. package/dist/core/mods/kernel/receive.d.ts +65 -0
  92. package/dist/core/mods/kernel/receive.js +123 -0
  93. package/dist/core/mods/kernel/registration.d.ts +131 -0
  94. package/dist/core/mods/kernel/registration.js +102 -0
  95. package/dist/core/mods/kernel/site-rules.d.ts +84 -0
  96. package/dist/core/mods/kernel/site-rules.js +104 -0
  97. package/dist/core/mods/stations.d.ts +350 -0
  98. package/dist/core/mods/stations.js +444 -0
  99. package/dist/core/own-property-receipt.js +11 -23
  100. package/dist/core/parent-spec-seat.d.ts +18 -7
  101. package/dist/core/parent-spec-seat.js +6 -2
  102. package/dist/core/path-identity.js +2 -1
  103. package/dist/core/permission-rule-syntax.d.ts +15 -0
  104. package/dist/core/permission-rule-syntax.js +24 -16
  105. package/dist/core/permission-rules.d.ts +7 -1
  106. package/dist/core/permission-rules.js +26 -15
  107. package/dist/core/prompt-section-seat.d.ts +15 -27
  108. package/dist/core/prompt-section-seat.js +73 -40
  109. package/dist/core/protocol-http-auth.d.ts +3 -1
  110. package/dist/core/protocol-http-auth.js +2 -0
  111. package/dist/core/reminder-mint.d.ts +2 -2
  112. package/dist/core/remote-env.d.ts +24 -12
  113. package/dist/core/remote-env.js +1 -0
  114. package/dist/core/resume-startup-cause.d.ts +16 -1
  115. package/dist/core/resume-startup-cause.js +4 -1
  116. package/dist/core/retired-keys.d.ts +4 -0
  117. package/dist/core/retired-keys.js +3 -0
  118. package/dist/core/runner/announce-once-ledger.d.ts +31 -1
  119. package/dist/core/runner/announce-once-ledger.js +22 -0
  120. package/dist/core/runner/assemble-result.d.ts +9 -0
  121. package/dist/core/runner/assemble-result.js +3 -1
  122. package/dist/core/runner/call-hook-stations.d.ts +23 -27
  123. package/dist/core/runner/call-hook-stations.js +58 -42
  124. package/dist/core/runner/caller-stop-gate.d.ts +25 -0
  125. package/dist/core/runner/caller-stop-gate.js +7 -0
  126. package/dist/core/runner/compaction-seams.d.ts +8 -22
  127. package/dist/core/runner/compaction-seams.js +28 -53
  128. package/dist/core/runner/construction-doors.js +11 -1
  129. package/dist/core/runner/contracts.d.ts +85 -83
  130. package/dist/core/runner/credential-origin-seat.d.ts +2 -0
  131. package/dist/core/runner/credential-origin-seat.js +19 -0
  132. package/dist/core/runner/extra-body-dropped-seat.d.ts +2 -6
  133. package/dist/core/runner/gate-exit.d.ts +47 -36
  134. package/dist/core/runner/gate-exit.js +8 -2
  135. package/dist/core/runner/image.js +1 -2
  136. package/dist/core/runner/inline-image.js +1 -1
  137. package/dist/core/runner/notice-seat-input.d.ts +11 -0
  138. package/dist/core/runner/notice-seat-input.js +1 -0
  139. package/dist/core/runner/person-path-rows.js +1 -0
  140. package/dist/core/runner/prepare-acquire-reconcile.js +5 -1
  141. package/dist/core/runner/prepare-ask-lane.d.ts +5 -7
  142. package/dist/core/runner/prepare-ask-lane.js +6 -15
  143. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
  144. package/dist/core/runner/prepare-caps-and-workflow.js +25 -8
  145. package/dist/core/runner/prepare-config-doors.d.ts +5 -0
  146. package/dist/core/runner/prepare-config-doors.js +9 -0
  147. package/dist/core/runner/prepare-defer-classify.js +3 -4
  148. package/dist/core/runner/prepare-gate-stations.d.ts +8 -11
  149. package/dist/core/runner/prepare-gate-stations.js +11 -10
  150. package/dist/core/runner/prepare-inherited-gate.d.ts +8 -12
  151. package/dist/core/runner/prepare-inherited-gate.js +8 -21
  152. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  153. package/dist/core/runner/prepare-park-ask.js +10 -14
  154. package/dist/core/runner/prepare-policy-chain.d.ts +17 -15
  155. package/dist/core/runner/prepare-policy-chain.js +95 -100
  156. package/dist/core/runner/prepare-project-context.js +7 -10
  157. package/dist/core/runner/prepare-prompt-assembly.d.ts +45 -40
  158. package/dist/core/runner/prepare-prompt-assembly.js +134 -101
  159. package/dist/core/runner/prepare-prompt-inputs.d.ts +18 -19
  160. package/dist/core/runner/prepare-prompt-inputs.js +5 -7
  161. package/dist/core/runner/prepare-protocol-tools.d.ts +6 -4
  162. package/dist/core/runner/prepare-protocol-tools.js +2 -2
  163. package/dist/core/runner/prepare-safety-scan.js +6 -10
  164. package/dist/core/runner/prepare-suspend-saga.d.ts +6 -0
  165. package/dist/core/runner/prepare-suspend-saga.js +7 -5
  166. package/dist/core/runner/prepare-task.js +33 -15
  167. package/dist/core/runner/prepare-turn-wiring.d.ts +13 -15
  168. package/dist/core/runner/prepare-turn-wiring.js +29 -27
  169. package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -6
  170. package/dist/core/runner/prepare-wiring-manifest.js +7 -7
  171. package/dist/core/runner/resume-apply.d.ts +5 -2
  172. package/dist/core/runner/resume-apply.js +24 -42
  173. package/dist/core/runner/resume-checkpoint-screen.d.ts +1 -0
  174. package/dist/core/runner/resume-checkpoint-screen.js +9 -1
  175. package/dist/core/runner/resume-preflight.js +38 -16
  176. package/dist/core/runner/retired-runner-deps.js +3 -1
  177. package/dist/core/runner/run-brain-sinks.d.ts +3 -2
  178. package/dist/core/runner/run-brain-sinks.js +5 -1
  179. package/dist/core/runner/run-compaction-machinery.d.ts +7 -1
  180. package/dist/core/runner/run-compaction-machinery.js +7 -33
  181. package/dist/core/runner/run-harness-handlers.js +4 -2
  182. package/dist/core/runner/run-identity-wiring.d.ts +3 -1
  183. package/dist/core/runner/run-identity-wiring.js +3 -2
  184. package/dist/core/runner/run-leg.js +62 -84
  185. package/dist/core/runner/run-notification-lane.js +1 -0
  186. package/dist/core/runner/run-recovery-lanes.d.ts +3 -0
  187. package/dist/core/runner/run-recovery-lanes.js +11 -10
  188. package/dist/core/runner/run-settle-and-teardown.js +9 -28
  189. package/dist/core/runner/run-stop-and-final-verify.js +18 -25
  190. package/dist/core/runner/run-terminal-adoption.d.ts +3 -0
  191. package/dist/core/runner/run-terminal-adoption.js +33 -22
  192. package/dist/core/runner/run-tool-mount-facts.js +5 -3
  193. package/dist/core/runner/run-turn-boundary.js +23 -34
  194. package/dist/core/runner/runtask.d.ts +6 -7
  195. package/dist/core/runner/runtask.js +14 -72
  196. package/dist/core/runner/session-rule-policy.d.ts +5 -5
  197. package/dist/core/runner/session-rule-policy.js +13 -8
  198. package/dist/core/runner/side-call-account.d.ts +68 -0
  199. package/dist/core/runner/side-call-account.js +61 -0
  200. package/dist/core/runner/stream-halt-verbs.js +12 -17
  201. package/dist/core/runner/stream-lifecycle-verbs.js +8 -5
  202. package/dist/core/runner/stream-steer-verb.d.ts +1 -1
  203. package/dist/core/runner/stream-steer-verb.js +20 -38
  204. package/dist/core/runner/sub-agent-deny-lists.d.ts +121 -22
  205. package/dist/core/runner/sub-agent-deny-lists.js +228 -71
  206. package/dist/core/runner/tool-end-body.d.ts +5 -6
  207. package/dist/core/runner/tool-end-body.js +6 -4
  208. package/dist/core/runner/tool-not-executed.d.ts +15 -0
  209. package/dist/core/runner/tool-not-executed.js +30 -0
  210. package/dist/core/runner/tool-occurrence.d.ts +8 -7
  211. package/dist/core/runner/tool-occurrence.js +5 -5
  212. package/dist/core/runner/tool-result-media-seat.d.ts +1 -1
  213. package/dist/core/runner-deps.d.ts +20 -28
  214. package/dist/core/safety-merge-corpus.js +18 -2
  215. package/dist/core/secret-env.d.ts +88 -20
  216. package/dist/core/secret-env.js +192 -38
  217. package/dist/core/session-policy-store.d.ts +52 -16
  218. package/dist/core/session-policy-store.js +118 -28
  219. package/dist/core/session-reconcile.d.ts +4 -0
  220. package/dist/core/session-reconcile.js +2 -1
  221. package/dist/core/shell-read-silence.js +3 -32
  222. package/dist/core/side-query.d.ts +5 -3
  223. package/dist/core/skill-grant.d.ts +3 -2
  224. package/dist/core/skill-grant.js +17 -9
  225. package/dist/core/skills-directory.d.ts +31 -11
  226. package/dist/core/skills-directory.js +655 -104
  227. package/dist/core/task-event.d.ts +13 -3
  228. package/dist/core/task-result.d.ts +8 -0
  229. package/dist/core/task-spec.d.ts +20 -19
  230. package/dist/core/task-stream.d.ts +14 -4
  231. package/dist/core/tighten-task-spec.d.ts +4 -3
  232. package/dist/core/tighten-task-spec.js +2 -4
  233. package/dist/core/tool-check-reading.d.ts +74 -0
  234. package/dist/core/tool-check-reading.js +80 -0
  235. package/dist/core/tool-policy.d.ts +59 -18
  236. package/dist/core/tool-policy.js +77 -12
  237. package/dist/core/tool-registry.d.ts +20 -0
  238. package/dist/core/tool-registry.js +4 -0
  239. package/dist/core/tool-spec.d.ts +10 -6
  240. package/dist/core/tools.js +2 -0
  241. package/dist/core/trace.d.ts +51 -32
  242. package/dist/core/unc-spelling.d.ts +10 -0
  243. package/dist/core/unc-spelling.js +3 -0
  244. package/dist/core/untrusted-egress.d.ts +8 -0
  245. package/dist/core/untrusted-egress.js +4 -1
  246. package/dist/core/user-attachment.d.ts +12 -2
  247. package/dist/core/user-attachment.js +17 -2
  248. package/dist/core/wiring-manifest.d.ts +14 -13
  249. package/dist/core/wiring-manifest.js +2 -2
  250. package/dist/engine/compaction/utils.js +1 -2
  251. package/dist/engine/execution-env/node-execution-env.d.ts +4 -3
  252. package/dist/engine/execution-env/symlink-chain.js +1 -1
  253. package/dist/engine/harness/agent-harness.d.ts +1 -1
  254. package/dist/engine/harness/agent-harness.js +3 -3
  255. package/dist/engine/harness/types.d.ts +17 -16
  256. package/dist/engine/harness/types.js +7 -1
  257. package/dist/engine/llm/types.d.ts +12 -7
  258. package/dist/engine/loop/agent-loop.js +1 -1
  259. package/dist/engine/loop/types.d.ts +1 -1
  260. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  261. package/dist/engine/session/import-validate.js +3 -10
  262. package/dist/engine/session/session.d.ts +4 -4
  263. package/dist/engine/session/session.js +4 -4
  264. package/dist/index.d.ts +20 -16
  265. package/dist/index.js +13 -11
  266. package/dist/internal/harness.d.ts +1 -1
  267. package/dist/internal/harness.js +1 -1
  268. package/dist/orchestration/run-spec.d.ts +4 -6
  269. package/dist/orchestration/run-spec.js +39 -63
  270. package/dist/orchestration/run-workflow-tool.js +1 -2
  271. package/dist/orchestration/workflow.d.ts +11 -3
  272. package/dist/orchestration/workflow.js +11 -2
  273. package/dist/prompt-assembly/artifact-store.d.ts +37 -56
  274. package/dist/prompt-assembly/artifact-store.js +31 -49
  275. package/dist/prompt-assembly/artifact.d.ts +89 -76
  276. package/dist/prompt-assembly/artifact.js +167 -146
  277. package/dist/prompt-assembly/assemble.d.ts +34 -29
  278. package/dist/prompt-assembly/assemble.js +33 -220
  279. package/dist/prompt-assembly/bands.d.ts +20 -0
  280. package/dist/prompt-assembly/bands.js +18 -0
  281. package/dist/prompt-assembly/catalog/constitution.d.ts +23 -0
  282. package/dist/prompt-assembly/catalog/constitution.js +69 -0
  283. package/dist/prompt-assembly/catalog/core-behavior.d.ts +9 -0
  284. package/dist/prompt-assembly/catalog/core-behavior.js +85 -0
  285. package/dist/prompt-assembly/catalog/index.d.ts +11 -0
  286. package/dist/prompt-assembly/catalog/index.js +59 -0
  287. package/dist/prompt-assembly/catalog/modes.d.ts +15 -0
  288. package/dist/prompt-assembly/catalog/modes.js +137 -0
  289. package/dist/prompt-assembly/catalog/role.d.ts +10 -0
  290. package/dist/prompt-assembly/catalog/role.js +157 -0
  291. package/dist/prompt-assembly/catalog/schema.d.ts +124 -0
  292. package/dist/prompt-assembly/catalog/schema.js +25 -0
  293. package/dist/prompt-assembly/catalog/sections.d.ts +9 -0
  294. package/dist/prompt-assembly/catalog/sections.js +98 -0
  295. package/dist/prompt-assembly/catalog/team.d.ts +4 -0
  296. package/dist/prompt-assembly/catalog/team.js +258 -0
  297. package/dist/prompt-assembly/composer.d.ts +24 -28
  298. package/dist/prompt-assembly/composer.js +124 -46
  299. package/dist/prompt-assembly/epoch.d.ts +89 -79
  300. package/dist/prompt-assembly/epoch.js +115 -142
  301. package/dist/prompt-assembly/explain.d.ts +58 -34
  302. package/dist/prompt-assembly/explain.js +21 -24
  303. package/dist/prompt-assembly/overlay.d.ts +127 -0
  304. package/dist/prompt-assembly/overlay.js +301 -0
  305. package/dist/prompt-assembly/renderers.d.ts +21 -0
  306. package/dist/prompt-assembly/renderers.js +39 -0
  307. package/dist/prompt-assembly/section-usage.d.ts +3 -3
  308. package/dist/prompt-assembly/section-usage.js +1 -1
  309. package/dist/prompt-assembly/select.d.ts +56 -0
  310. package/dist/prompt-assembly/select.js +69 -0
  311. package/dist/prompt-assembly/sugar.d.ts +31 -0
  312. package/dist/prompt-assembly/sugar.js +20 -0
  313. package/dist/prompt-assembly/turn-snapshot.d.ts +3 -2
  314. package/dist/prompt-assembly/types.d.ts +45 -148
  315. package/dist/prompts/coordinator.d.ts +2 -21
  316. package/dist/prompts/coordinator.js +3 -255
  317. package/dist/prompts/cowork.d.ts +27 -16
  318. package/dist/prompts/cowork.js +2 -23
  319. package/dist/prompts/default.d.ts +38 -500
  320. package/dist/prompts/default.js +20 -285
  321. package/dist/prompts/supervisor.d.ts +6 -71
  322. package/dist/prompts/supervisor.js +6 -117
  323. package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -5
  324. package/dist/tools/fs/dir-entries.d.ts +4 -2
  325. package/dist/tools/fs/dir-entries.js +16 -4
  326. package/dist/tools/fs/fs-bash.d.ts +1 -1
  327. package/dist/tools/fs/fs-bash.js +22 -11
  328. package/dist/tools/fs/fs-pdf.js +17 -13
  329. package/dist/tools/fs/fs-read.js +4 -3
  330. package/dist/tools/fs/fs-shared.d.ts +1 -10
  331. package/dist/tools/fs/fs-shared.js +3 -4
  332. package/dist/tools/fs/fs-write.js +2 -1
  333. package/dist/tools/fs/notebook.js +1 -1
  334. package/dist/tools/fs/pdf.d.ts +20 -10
  335. package/dist/tools/fs/pdf.js +19 -10
  336. package/dist/tools/fs/program-read-model.d.ts +68 -8
  337. package/dist/tools/fs/program-read-model.js +112 -33
  338. package/dist/tools/fs/program-read-model.rows.js +30 -10
  339. package/dist/tools/fs/read-deny-home.d.ts +25 -9
  340. package/dist/tools/fs/read-deny-home.js +28 -27
  341. package/dist/tools/fs/read-deny.d.ts +41 -11
  342. package/dist/tools/fs/read-deny.js +51 -5
  343. package/dist/tools/fs/safety.d.ts +4 -26
  344. package/dist/tools/fs/safety.js +10 -32
  345. package/dist/tools/fs/search.js +15 -10
  346. package/dist/tools/fs/shell-read-argv.d.ts +3 -2
  347. package/dist/tools/fs/shell-read-argv.js +118 -38
  348. package/dist/tools/fs/shell-read-establish.d.ts +5 -5
  349. package/dist/tools/fs/shell-read-establish.js +11 -61
  350. package/dist/tools/fs/shell-read-identity.js +6 -3
  351. package/dist/tools/fs/shell-read-judge.d.ts +13 -15
  352. package/dist/tools/fs/shell-read-judge.js +18 -4
  353. package/dist/tools/fs/shell-read-plan.d.ts +2 -2
  354. package/dist/tools/web.js +1 -1
  355. package/package.json +13 -1
  356. package/test/export-surface.snapshot.json +242 -99
  357. package/wire-changes/core@7.52.0.json +5859 -0
  358. package/dist/core/hook-seats.d.ts +0 -66
  359. package/dist/core/hook-seats.js +0 -37
  360. package/dist/prompt-assembly/packs/sema-default.d.ts +0 -2
  361. package/dist/prompt-assembly/packs/sema-default.js +0 -147
  362. package/dist/prompts/simple-sections.d.ts +0 -58
  363. package/dist/prompts/simple-sections.js +0 -43
@@ -2,12 +2,84 @@ import { literalTildeSpelling } from "../../shell/lex.js";
2
2
  import { expandsOf, partPattern, readOfText, rewritten } from "./shell-read-word.js";
3
3
  import { arithmeticSubstitutions } from "./shell-read-bindings.js";
4
4
  import { argvProgramOf, attachedOptionPayloads, isPathShapedToken } from "./bash-readonly-classifier.js";
5
- import { assignmentArgumentsAreNotPaths, evaluatesOperands, messageWords, moduleTailOf, objectPathsOf, opensNoOperand, programRow, programTextMayOpen, programTextOpensOf, readArgs, selectsContentsTraversal, slotValueCandidates, subcommandOf, valuesAreLiteral, walksCwdWithoutOperand, wordsReadBySomeImplementations, } from "./program-read-model.js";
5
+ import { assignmentArgumentsAreNotPaths, deliversOperands, evaluatesOperands, messageWords, moduleTailOf, objectPathsOf, opensNoOperand, programRow, programTextMayOpen, programTextOpensOf, readArgs, readOptionSlot, selectsContentsTraversal, slotValueCandidates, subcommandOf, valuesAreLiteral, walksCwdWithoutOperand, wordsReadBySomeImplementations, } from "./program-read-model.js";
6
6
  export function pathSubcommandOf(row, args) {
7
7
  const sub = subcommandOf(row, args);
8
8
  return sub !== undefined && (sub.row.operands !== undefined || sub.row.traversal !== undefined) ? sub : undefined;
9
9
  }
10
+ function slotOperandAt(row, words) {
11
+ for (const e of readArgs(row, words, { firstOperandSlot: true }))
12
+ if (e.kind === "operand" && e.slot !== undefined)
13
+ return e.at;
14
+ return undefined;
15
+ }
16
+ function mayOwnNextWord(row, tok) {
17
+ if (tok.startsWith("--"))
18
+ return !tok.includes("=");
19
+ for (let k = 2; k <= tok.length; k++)
20
+ if (readOptionSlot(row, tok.slice(0, k)).form === "unexplained")
21
+ return k === tok.length;
22
+ return false;
23
+ }
24
+ const rootValue = (spec) => spec?.walks === true || spec?.delivers === "operands";
25
+ class RootsGathered {
26
+ positional = [];
27
+ places = [];
28
+ maybeValues = new Set();
29
+ stdinOperands = 0;
30
+ slotOwned = false;
31
+ unsettled = false;
32
+ slotWord;
33
+ value(r, e, namesPath) {
34
+ if (!e.consumed) {
35
+ this.maybeValues.add(r);
36
+ if (e.unsettled === true)
37
+ this.unsettled = true;
38
+ }
39
+ else if (namesPath && rootValue(e.spec))
40
+ this.positional.push(r);
41
+ }
42
+ attached(named, spec) {
43
+ if (rootValue(spec))
44
+ for (const c of named)
45
+ if (c.denyOnly !== true)
46
+ this.places.push({ ...c, provenance: "traversal_root" });
47
+ }
48
+ slot(r) {
49
+ this.slotWord = r;
50
+ if (this.maybeValues.has(r))
51
+ this.slotOwned = true;
52
+ }
53
+ operand(r, namesPath, stdin) {
54
+ if (stdin) {
55
+ if (!this.maybeValues.has(r))
56
+ this.stdinOperands++;
57
+ }
58
+ else if (namesPath)
59
+ this.positional.push(r);
60
+ }
61
+ restart() {
62
+ this.positional.length = 0;
63
+ this.places.length = 0;
64
+ this.stdinOperands = 0;
65
+ this.slotOwned = false;
66
+ this.unsettled = false;
67
+ this.slotWord = undefined;
68
+ }
69
+ roots(row) {
70
+ const leftOver = this.positional.filter((r) => !this.maybeValues.has(r)).length + this.places.length + this.stdinOperands;
71
+ const reads = this.unsettled
72
+ ? [...this.positional, ...(this.slotWord !== undefined ? [this.slotWord] : []), ...(walksCwdWithoutOperand(row) ? [readOfText(".")] : [])]
73
+ : walksCwdWithoutOperand(row) && leftOver <= (this.slotOwned ? 1 : 0) ? [...this.positional, readOfText(".")] : [...this.positional];
74
+ return [...reads.map((r) => bare(r, "traversal_root")), ...this.places];
75
+ }
76
+ }
10
77
  export const programTextSentence = (language) => `runs inline ${language} program text, which may open files this station does not read`;
78
+ const deliveredOperandsSentence = "takes the files it works on from a list file, whose names this station does not read";
79
+ function noteDeliveredOperands(row, words, ctx) {
80
+ if (deliversOperands(row, words))
81
+ ctx.programText ??= deliveredOperandsSentence;
82
+ }
11
83
  export function bare(r, provenance, spelled = r.folded) {
12
84
  const whole = spelled === r.folded;
13
85
  const pattern = whole ? r.pattern : partPattern(r, spelled);
@@ -105,9 +177,9 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
105
177
  else if (j > argvStart && !declaredNotPaths)
106
178
  pushAssignment(reads, toks[j].word, provenance);
107
179
  }
108
- const positional = [];
180
+ const gathered = new RootsGathered();
181
+ let slotWord;
109
182
  const pastOperand = new Set();
110
- let stdinOperand = false;
111
183
  let globs = "expanded";
112
184
  let walkRow = slotRow;
113
185
  let walkFrom = argvStart + 1;
@@ -159,8 +231,11 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
159
231
  const at = (k) => first + k;
160
232
  const words = toks.slice(first, last).map((r, k) => ({ folded: r.folded, skip: skipped(at(k)) }));
161
233
  globs = valuesAreLiteral(row, words.map((w) => w.folded)) ? "literal" : "expanded";
234
+ noteDeliveredOperands(row, words.map((w) => w.folded), ctx);
162
235
  const someRead = wordsReadBySomeImplementations(row, words.map((w) => w.folded));
163
- for (const e of readArgs(row, words, { firstOperandSlot: slotKind })) {
236
+ const slotAt = slotOperandAt(row, words);
237
+ slotWord = slotAt === undefined ? undefined : at(slotAt);
238
+ for (const e of readArgs(row, words, { firstOperandSlot: slotKind, ownedValues: true })) {
164
239
  const start = reads.length;
165
240
  handleUnlistedEvent(e, at(e.at));
166
241
  if (someRead.has(e.at))
@@ -175,8 +250,7 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
175
250
  walkRow = sub.row;
176
251
  walkFrom = argvStart + 2 + sub.at;
177
252
  relocated = sub.relocated;
178
- positional.length = 0;
179
- stdinOperand = false;
253
+ gathered.restart();
180
254
  walk(sub.row, walkFrom, toks.length, true);
181
255
  }
182
256
  else
@@ -199,6 +273,7 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
199
273
  return;
200
274
  case "value": {
201
275
  if (!e.consumed) {
276
+ gathered.value(r, e, false);
202
277
  if (r.expands !== "unresolvable")
203
278
  reads.push(...roleReads(r, "option_value", r.folded, e.role, globs, ctx, true));
204
279
  return;
@@ -207,8 +282,7 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
207
282
  return;
208
283
  const named = roleReads(r, "option_value", r.folded, e.role, globs, ctx);
209
284
  reads.push(...named);
210
- if (named.some((c) => c.denyOnly !== true))
211
- positional.push(r);
285
+ gathered.value(r, e, named.some((c) => c.denyOnly !== true));
212
286
  return;
213
287
  }
214
288
  case "option":
@@ -224,15 +298,20 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
224
298
  speculate(whole, "whole");
225
299
  return;
226
300
  }
227
- reads.push(...roleReads(r, "option_value", literalTildeSpelling(e.reading.attached), e.reading.role, globs, ctx, pastOperand.has(j)));
301
+ {
302
+ const named = roleReads(r, "option_value", literalTildeSpelling(e.reading.attached), e.reading.role, globs, ctx, pastOperand.has(j));
303
+ reads.push(...named);
304
+ gathered.attached(named, e.reading.spec);
305
+ }
228
306
  return;
229
307
  case "operand": {
230
308
  const role = e.dash === undefined ? (e.slot ?? e.role) : undefined;
231
309
  const named = role !== undefined ? roleReads(r, provenance, r.folded, role, globs, ctx) : [bare(r, provenance)];
232
- if (e.dash === "stdin")
233
- stdinOperand = true;
234
- else if (named.some((c) => c.denyOnly !== true))
235
- positional.push(r);
310
+ const inSlot = j === slotWord;
311
+ if (inSlot)
312
+ gathered.slot(r);
313
+ else
314
+ gathered.operand(r, named.some((c) => c.denyOnly !== true), e.dash === "stdin");
236
315
  reads.push(...named);
237
316
  speculate(start, r.word.assignment !== undefined ? "whole" : "containment");
238
317
  const values = reads.length;
@@ -244,9 +323,8 @@ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ct
244
323
  }
245
324
  }
246
325
  if (walkRow !== undefined && argv !== undefined && selectsContentsTraversal(walkRow, toks.slice(walkFrom).map((x) => x.folded))) {
247
- const roots = positional.length > 0 ? positional : stdinOperand || !walksCwdWithoutOperand(walkRow) ? [] : [readOfText(".")];
248
- for (const root of roots)
249
- reads.push(relocated ? { ...bare(root, "traversal_root"), expands: "unresolvable" } : bare(root, "traversal_root"));
326
+ for (const root of gathered.roots(walkRow))
327
+ reads.push(relocated ? { ...root, expands: "unresolvable" } : root);
250
328
  }
251
329
  for (let j = from; j < toks.length; j++) {
252
330
  if (launcherIndices.has(j) || j === argvStart)
@@ -293,17 +371,19 @@ export function listedVerbCandidates(name, argReads, reads, ctx) {
293
371
  function walkListed(row, argReads, first, last, expanding, reads, ctx, relocated) {
294
372
  const slice = argReads.slice(first, last);
295
373
  const args = slice.map((r) => r.folded);
296
- const positional = [];
297
- let stdinOperand = false;
374
+ const gathered = new RootsGathered();
375
+ let unexplained = false;
298
376
  const globs = valuesAreLiteral(row, args) ? "literal" : "expanded";
377
+ noteDeliveredOperands(row, args, ctx);
299
378
  const someRead = wordsReadBySomeImplementations(row, args);
300
379
  const pastOperand = new Set();
301
380
  const offer = (k, value, role, provenance, alsoOperand = false) => {
302
381
  const named = roleReads(slice[k], provenance, value, role, globs, ctx, alsoOperand);
303
382
  reads.push(...named);
304
- return named.filter((c) => c.denyOnly !== true).length;
383
+ return named;
305
384
  };
306
- for (const e of readArgs(row, args.map((folded, k) => ({ folded, skip: expanding.has(first + k) })), { firstOperandSlot: true })) {
385
+ const opensPath = (named) => named.some((c) => c.denyOnly !== true);
386
+ for (const e of readArgs(row, args.map((folded, k) => ({ folded, skip: expanding.has(first + k) })), { firstOperandSlot: true, ownedValues: true })) {
307
387
  const t = args[e.at];
308
388
  const start = reads.length;
309
389
  switch (e.kind) {
@@ -316,42 +396,43 @@ function walkListed(row, argReads, first, last, expanding, reads, ctx, relocated
316
396
  case "value": {
317
397
  if (expanding.has(first + e.at))
318
398
  break;
319
- if (offer(e.at, t, e.role, "option_value", !e.consumed) > 0 && e.consumed)
320
- positional.push(slice[e.at]);
399
+ gathered.value(slice[e.at], e, opensPath(offer(e.at, t, e.role, "option_value", !e.consumed)));
321
400
  break;
322
401
  }
323
402
  case "option":
324
403
  if (e.reading === "no-value")
325
404
  break;
326
405
  if (e.reading === "speculative") {
406
+ if (row === undefined || mayOwnNextWord(row, t))
407
+ unexplained = true;
327
408
  for (const payload of attachedOptionPayloads(t))
328
409
  reads.push(bare(slice[e.at], "option_value", payload));
329
410
  break;
330
411
  }
331
- offer(e.at, literalTildeSpelling(e.reading.attached), e.reading.role, "option_value", pastOperand.has(e.at));
412
+ gathered.attached(offer(e.at, literalTildeSpelling(e.reading.attached), e.reading.role, "option_value", pastOperand.has(e.at)), e.reading.spec);
332
413
  break;
333
414
  case "operand": {
334
415
  if (e.dash !== undefined) {
335
- if (e.slot !== undefined)
416
+ if (e.slot !== undefined) {
417
+ gathered.slot(slice[e.at]);
336
418
  break;
337
- if (e.dash === "stdin")
338
- stdinOperand = true;
339
- else
340
- positional.push(slice[e.at]);
419
+ }
420
+ gathered.operand(slice[e.at], true, e.dash === "stdin");
341
421
  break;
342
422
  }
343
423
  if (e.slot !== undefined) {
344
- if (offer(e.at, t, e.slot, "operand") > 0)
345
- positional.push(slice[e.at]);
424
+ offer(e.at, t, e.slot, "operand");
425
+ if (unexplained && e.slot.kind === "datum" && e.slot.form === "pattern")
426
+ reads.push(bare(slice[e.at], "operand"));
427
+ gathered.slot(slice[e.at]);
346
428
  break;
347
429
  }
348
- let named = 1;
430
+ let opens = true;
349
431
  if (e.role !== undefined)
350
- named = offer(e.at, t, e.role, "operand");
432
+ opens = opensPath(offer(e.at, t, e.role, "operand"));
351
433
  else
352
434
  reads.push(bare(slice[e.at], "operand"));
353
- if (named > 0)
354
- positional.push(slice[e.at]);
435
+ gathered.operand(slice[e.at], opens, false);
355
436
  if (row?.operands?.assignmentArguments !== "not-paths")
356
437
  pushAssignment(reads, slice[e.at].word, "operand");
357
438
  break;
@@ -362,10 +443,9 @@ function walkListed(row, argReads, first, last, expanding, reads, ctx, relocated
362
443
  if (reads[i].expands !== "unresolvable" && reads[i].denyOnly !== true)
363
444
  reads[i] = { ...reads[i], speculative: "containment" };
364
445
  }
365
- if (selectsContentsTraversal(row, args)) {
366
- const roots = positional.length > 0 ? positional : stdinOperand || !walksCwdWithoutOperand(row) ? [] : [readOfText(".")];
367
- for (const root of roots)
368
- reads.push(relocated ? { ...bare(root, "traversal_root"), expands: "unresolvable" } : bare(root, "traversal_root"));
446
+ if (row !== undefined && selectsContentsTraversal(row, args)) {
447
+ for (const root of gathered.roots(row))
448
+ reads.push(relocated ? { ...root, expands: "unresolvable" } : root);
369
449
  }
370
450
  }
371
451
  export function subscriptOnly(word) {
@@ -4,8 +4,8 @@
4
4
  * decides WHICH words a command may read, `shell-read-write-targets.ts` which words it writes and at which seat;
5
5
  * this module decides WHERE each of them lands: the canonical
6
6
  * target of a spelling (symlinks resolved, a `..` applied the way the kernel applies it), the expansion of
7
- * a pattern (shell pathname semantics over `env.listDir`), the members of a traversal root (a bounded
8
- * recursive enumeration, dot files included, symlinked directories not followed), and the fail-closed arm
7
+ * a pattern (shell pathname semantics over `env.listDir`), what a traversal root is (one stat: a recursive read is
8
+ * judged by the root it names, its tree is never enumerated), and the fail-closed arm
9
9
  * for what could not be established. The judge (`shell-read-judge.ts`) is downstream; nothing here decides
10
10
  * allow/ask/deny.
11
11
  *
@@ -27,9 +27,9 @@
27
27
  * 4. NO MATCH is not a refusal: an unmatched pattern is handed to the command verbatim, so an empty
28
28
  * expansion falls back to the literal spelling.
29
29
  * 5. What it cannot enumerate is `unestablishable`, never silently vouched: an unreadable directory, a
30
- * pattern construct the matcher does not model, an expansion or a traversal past
30
+ * pattern construct the matcher does not model, an expansion past
31
31
  * {@link MAX_GLOB_DIRS} / {@link MAX_GLOB_MATCHES}.
32
- * 6. Cost is paid only by candidates that carry a metacharacter or a traversal, and only past the roots'
32
+ * 6. Cost is paid only by candidates that carry a metacharacter, and only past the roots'
33
33
  * own spelling. Inside one enumerated directory only the SYMLINK entries need their own `realpath`.
34
34
  *
35
35
  * NOT cached deliberately: a per-run memo keyed on the spelling would answer from a snapshot of where a
@@ -44,7 +44,7 @@ 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
46
  /**
47
- * How many directories one glob operand (or one traversal root) may enumerate, and how many names it may
47
+ * How many directories one glob operand may enumerate, and how many names it may
48
48
  * match, before the leg gives up and answers `unestablishable`.
49
49
  *
50
50
  * Sized so the everyday shapes never hit them: 256 directories covers a two-level pattern over a source
@@ -366,54 +366,11 @@ function establishLegOver(seen, readRoots) {
366
366
  }
367
367
  return { paths: frontier, canonical: canonicalOfMatch };
368
368
  };
369
- const enumerateTraversal = async (rootCanonical, signal) => {
370
- const found = [];
371
- const listedDirs = new Set();
372
- let root;
373
- const stack = [{ dir: rootCanonical, spell: rootCanonical }];
374
- const visited = new Set([rootCanonical]);
375
- let dirsUsed = 0;
376
- while (stack.length > 0) {
377
- const { dir, spell } = stack.pop();
378
- if (++dirsUsed > MAX_GLOB_DIRS)
379
- return bounded(`"${rootCanonical}" holds more than ${MAX_GLOB_DIRS} directories, too many to enumerate to verify what a recursive read would open`);
380
- const listed = await env.listDir(dir, signal);
381
- if (!listed.ok) {
382
- if (dir === rootCanonical && listed.error.code === "not_directory")
383
- root = "file";
384
- if (listed.error.code === "not_directory" || listed.error.code === "not_found")
385
- continue;
386
- return { unverifiable: `"${dir}" cannot be enumerated to verify what a recursive read would open (${listed.error.code})` };
387
- }
388
- listedDirs.add(dir);
389
- if (dir === rootCanonical)
390
- root = "directory";
391
- for (const entry of listed.value) {
392
- if (found.length >= MAX_GLOB_MATCHES)
393
- return bounded(`"${rootCanonical}" holds more than ${MAX_GLOB_MATCHES} entries, too many to enumerate to verify what a recursive read would open`);
394
- const path = joinUnder(spell, entry.name);
395
- const canonicalPath = joinUnder(dir, entry.name);
396
- if (entry.kind === "symlink") {
397
- const canon = await env.canonicalPath(canonicalPath, signal);
398
- found.push({ path, ...(canon.ok ? { canonical: canon.value } : {}), symlink: true });
399
- if (canon.ok && !visited.has(canon.value)) {
400
- visited.add(canon.value);
401
- stack.push({ dir: canon.value, spell: path });
402
- }
403
- continue;
404
- }
405
- if (entry.kind === "directory") {
406
- if (!visited.has(canonicalPath)) {
407
- visited.add(canonicalPath);
408
- stack.push({ dir: canonicalPath, spell: path });
409
- }
410
- continue;
411
- }
412
- found.push({ path, canonical: canonicalPath, symlink: false });
413
- }
414
- }
415
- const members = found.map(({ path, canonical, symlink }) => ({ path, ...(canonical !== undefined ? { canonical } : {}), node: (symlink && canonical !== undefined && listedDirs.has(canonical) ? "directory" : "file") }));
416
- return { ...(root !== undefined ? { root } : {}), members };
369
+ const rootKindOf = async (rootCanonical, signal) => {
370
+ const info = await env.fileInfo(rootCanonical, signal);
371
+ if (!info.ok)
372
+ return undefined;
373
+ return info.value.kind === "directory" ? "directory" : info.value.kind === "file" ? "file" : undefined;
417
374
  };
418
375
  const entryOf = async (spelling, signal) => {
419
376
  const parts = spelling.split(SEP);
@@ -554,20 +511,13 @@ function establishLegOver(seen, readRoots) {
554
511
  }
555
512
  return reads;
556
513
  };
557
- const land = async (read, spellings, folded) => {
514
+ const land = async (read) => {
558
515
  if (!traversal || (read.established !== "canonical" && read.established !== "expansion") || read.target === undefined) {
559
516
  out.push(read);
560
517
  return;
561
518
  }
562
- const walkedTree = await enumerateTraversal(read.target, signal);
563
- if ("unverifiable" in walkedTree) {
564
- out.push(read);
565
- out.push({ candidate, established: "unestablishable", spellings, origin: folded, cause: walkedTree.unverifiable });
566
- return;
567
- }
568
- out.push({ ...read, ...(walkedTree.root !== undefined ? { node: walkedTree.root } : {}) });
569
- for (const m of walkedTree.members)
570
- out.push({ ...(await canonicalReadOf(candidate, m.path, [m.path], "expansion", folded, m.canonical, signal)), node: m.node, walked: true });
519
+ const node = await rootKindOf(read.target, signal);
520
+ out.push({ ...read, ...(node !== undefined ? { node } : {}) });
571
521
  };
572
522
  for (const [i, lexical] of lexicalReadsOf(candidate, boundary).entries()) {
573
523
  if (lexical.established !== "lexical" || passesUnasked(candidate)) {
@@ -590,16 +540,16 @@ function establishLegOver(seen, readRoots) {
590
540
  matched = true;
591
541
  for (const t of expanded.paths)
592
542
  for (const read of await atSeats(t, [t, folded], "expansion", folded, expanded.canonical.get(t)))
593
- await land(read, spellings, folded);
543
+ await land(read);
594
544
  }
595
545
  }
596
546
  if (!matched || candidate.pattern !== undefined)
597
547
  for (const read of await atSeats(walked, spellings, "canonical", undefined, undefined))
598
- await land(read, spellings, folded);
548
+ await land(read);
599
549
  continue;
600
550
  }
601
551
  for (const read of await atSeats(walked, spellings, "canonical", undefined, undefined))
602
- await land(read, spellings, folded);
552
+ await land(read);
603
553
  }
604
554
  }
605
555
  return out;
@@ -40,14 +40,17 @@ function sameTargets(a, b) {
40
40
  }
41
41
  export async function withHomeViews(door, placed, established, signal) {
42
42
  const views = homeViewsOf(door.fs, placed, signal);
43
+ const unread = [];
43
44
  const place = async (p) => {
44
45
  const reads = new Map();
45
46
  for (const [segment, rs] of p.reads) {
46
47
  const out = [];
47
48
  for (const r of rs) {
48
49
  const landed = r.target !== undefined && (r.established === "canonical" || r.established === "expansion");
49
- const view = landed ? await views.of(r.target, r.node === "file" ? { leaf: "file" } : undefined) : undefined;
50
- out.push(view === undefined || r.spellings.includes(view) ? r : { ...r, spellings: [...r.spellings, view] });
50
+ const home = landed ? await views.of(r.target, r.node === "file" ? { leaf: "file" } : undefined) : {};
51
+ if (home.unread !== undefined && !unread.includes(home.unread))
52
+ unread.push(home.unread);
53
+ out.push(home.view === undefined || r.spellings.includes(home.view) ? r : { ...r, spellings: [...r.spellings, home.view] });
51
54
  }
52
55
  reads.set(segment, out);
53
56
  }
@@ -57,6 +60,6 @@ export async function withHomeViews(door, placed, established, signal) {
57
60
  return { ...p, reads, nested };
58
61
  };
59
62
  const withViews = await place(established);
60
- door.adopt(views.unread.map((p) => ({ op: "fileIdentity", detail: `"${p}" cannot be identified against the home directories` })));
63
+ door.adopt(unread.map((p) => ({ op: "fileIdentity", detail: `"${p}" cannot be identified against the home directories` })));
61
64
  return withViews;
62
65
  }
@@ -96,7 +96,9 @@ export declare function dispositionOfCell(cell: ShellReadCell, context?: ShellRe
96
96
  * · `unanswered` — the judge did not FINISH: it threw or timed out, the lookup it stands on was absent, or — recorded at the
97
97
  * one door to the environment ({@link import("./shell-read-environment.js").Incomplete}) — an environment lookup gave no
98
98
  * answer (an error that is no conclusive "not there"), the station stopped at its own bound (more directories or names than
99
- * it enumerates, a link chain past its hop bound), or a home view could not be read. Any one makes the whole command
99
+ * it enumerates, a link chain past its hop bound), or a home view could not be read — or a recursive read's root may hold a
100
+ * home-anchored deny row that no home places (the environment declares none, or its placement did not answer), a home the
101
+ * station cannot place either. Any one makes the whole command
100
102
  * `unanswered`, whatever the other words found. Not a fact about the command but the state of the station, so it is not
101
103
  * the `unestablishable` row's question (a text the station cannot place — a word the shell rewrites, a lost working
102
104
  * directory, a pattern its matcher does not model). Absence is not
@@ -239,13 +241,9 @@ export interface EstablishedRead {
239
241
  readonly spellings: readonly string[];
240
242
  /** The pattern or traversal root an `expansion` came from, as folded. */
241
243
  readonly origin?: string;
242
- /** What the environment said the target IS, where a read asked — only a traversal asks (it lists the root and
243
- * every member it walks; a link's answer is its target's). Absent = nothing asked; a plain read never lists. */
244
+ /** What the environment said the target IS, where a read asked — only a traversal root asks (it lists the root
245
+ * once; a link's answer is its target's). Absent = nothing asked; a plain read never lists. */
244
246
  readonly node?: "directory" | "file";
245
- /** A MEMBER a traversal enumerated below its root — never a spelling of the command. The deny judge reads it
246
- * ({@link import("./read-deny.js").ReadDenyMatcher.matchPath} `walked`): the segment rows still judge it, a
247
- * rule-DSL row does not. Absent on the root itself, on a pattern's own matches and on every spelled operand. */
248
- readonly walked?: true;
249
247
  /** Why the candidate could not be established. */
250
248
  readonly cause?: string;
251
249
  /** The candidate's spelling WAS placed and the environment could not resolve where it leads (the whole chain unanswered by
@@ -263,12 +261,12 @@ export interface EstablishedRead {
263
261
  readonly child?: true;
264
262
  };
265
263
  }
266
- /** What the judge found about ONE candidate. `kind` is one of the four candidate-level findings
267
- * (`declined` is a segment fact, `identity_changed` a re-establishment fact, `unanswered` a fact of the whole establishment —
268
- * none is a candidate's). */
264
+ /** What the judge found about ONE candidate. `kind` is one of the five candidate-level findings (`declined` is a segment fact,
265
+ * `identity_changed` a re-establishment fact — neither is a candidate's; `unanswered` is a fact of the whole establishment, and
266
+ * a candidate's where a recursive read's root may hold a deny row no home places, {@link judgeRead}). */
269
267
  export interface ReadFinding {
270
268
  readonly read: EstablishedRead;
271
- readonly kind: Extract<ShellReadFinding, "deny_hit" | "outside_roots" | "unestablishable" | "none">;
269
+ readonly kind: Extract<ShellReadFinding, "deny_hit" | "outside_roots" | "unestablishable" | "unanswered" | "none">;
272
270
  /** The deny row that matched, the view it matched on, and — for a write target — the face (`write`) that refused. */
273
271
  readonly deny?: {
274
272
  readonly pattern: string;
@@ -379,10 +377,10 @@ export interface ShellReadFold {
379
377
  * yields more words, never zero, so an unreadable text is never "no candidates";
380
378
  * · a SHELL re-entry segment (`sh -c S`, `eval S`, `env -S 'S'`, a launcher handing text to a shell) is at
381
379
  * least `unestablishable` — what runs is text the shell parses, not this plan — and its nested plan may
382
- * only RAISE that to `deny_hit`: the nested text's deny hits count (a refusal is a refusal whatever runs
383
- * it), its containment and its silence do not (an inner `cat inside.txt` vouches nothing about the
384
- * outer text). A substitution body (`$(…)`, a backtick, `<(…)`) is nested WITHOUT re-entry and folds
385
- * in full.
380
+ * only RAISE that: the nested text's deny-relation answers count — a deny hit (a refusal is a refusal whatever
381
+ * runs it) and a root that may hold a row no home places — its containment and its other silence do not (an inner
382
+ * `cat inside.txt` vouches nothing about the outer text). A substitution body (`$(…)`, a backtick, `<(…)`) is nested
383
+ * WITHOUT re-entry and folds in full.
386
384
  */
387
385
  export declare function foldShellReadFindings(judged: JudgedPlan): ShellReadFold;
388
386
  /** Judge every established read of a plan, in place. `establish` runs per segment so a caller can bound it. */
@@ -46,14 +46,28 @@ export function judgeRead(read, boundary) {
46
46
  const writeTarget = read.candidate.face === "write";
47
47
  const deny = writeTarget ? boundary.writeDenyMatch : boundary.denyMatch;
48
48
  if (deny !== undefined) {
49
- const walked = read.walked === true ? { walked: true } : undefined;
50
49
  const views = [...(read.target !== undefined ? [read.target] : []), ...read.spellings];
51
50
  for (const view of views) {
52
51
  const folded = normalizeAbsPathLexically(view);
53
- const pattern = writeTarget ? boundary.writeDenyMatch(folded) : boundary.denyMatch(folded, walked);
52
+ const pattern = writeTarget ? boundary.writeDenyMatch(folded) : boundary.denyMatch(folded);
54
53
  if (pattern !== null)
55
54
  return { read, kind: "deny_hit", deny: { pattern, matchedView: folded, ...(writeTarget ? { face: "write" } : {}) }, containment: writeTarget ? writeContainmentOf(read, boundary) : containmentOf(read, boundary) };
56
55
  }
56
+ if (!writeTarget && read.candidate.provenance === "traversal_root" && boundary.denyUnder !== undefined) {
57
+ let unplaced;
58
+ for (const view of views) {
59
+ const folded = normalizeAbsPathLexically(view);
60
+ const under = boundary.denyUnder(folded);
61
+ if (typeof under === "string")
62
+ return { read, kind: "deny_hit", deny: { pattern: under, matchedView: folded }, containment: containmentOf(read, boundary) };
63
+ if (under !== null && typeof under === "object")
64
+ unplaced ??= under.unplaced;
65
+ }
66
+ if (unplaced !== undefined) {
67
+ const cause = `the home-anchored deny row "${unplaced}" cannot be placed (no home directory is known for this environment), so "${read.target ?? read.candidate.spelled}" may hold it`;
68
+ return { read: { ...read, cause }, kind: "unanswered", containment: containmentOf(read, boundary) };
69
+ }
70
+ }
57
71
  }
58
72
  if (writeTarget)
59
73
  return judgeWrite(read, boundary);
@@ -119,7 +133,7 @@ export function foldShellReadFindings(judged) {
119
133
  let unestablished = false;
120
134
  let unplacedRead = false;
121
135
  const raise = (next) => {
122
- if (next.finding === "unestablishable" || next.finding === "declined") {
136
+ if (next.finding === "unestablishable" || next.finding === "declined" || next.finding === "unanswered") {
123
137
  const face = next.witness?.read.candidate.face;
124
138
  if (!(next.witness === undefined ? judged.guards || judged.writeGuards : face === "write" ? judged.writeGuards : judged.guards))
125
139
  return;
@@ -136,7 +150,7 @@ export function foldShellReadFindings(judged) {
136
150
  raise({ finding: "unestablishable" });
137
151
  for (const s of j.segments) {
138
152
  for (const f of s.findings) {
139
- if (denyOnly && f.kind !== "deny_hit")
153
+ if (denyOnly && f.kind !== "deny_hit" && f.kind !== "unanswered")
140
154
  continue;
141
155
  if (f.kind !== "none")
142
156
  raise({ finding: f.kind, witness: f });
@@ -119,8 +119,8 @@ export interface ShellReadSegment {
119
119
  /** `shell` = the program runs its argument as shell text; `argv` = its operands arrive on a stream;
120
120
  * `interpreter` = it runs an inline program in a language this parser does not read (a shape fact only). */
121
121
  readonly reentry?: "shell" | "argv" | "interpreter";
122
- /** Program text the station READS as a rule (a `sed` script names the files it opens) but could not read here —
123
- * supplied from a file, or not parseable to its end. What such a segment reads is then not established. */
122
+ /** Why what this segment reads is not established, as a sentence: program text the station could not read (a script supplied
123
+ * from a file, text not parseable to its end, a program on standard input) or the list file the operands come from. */
124
124
  readonly unreadProgramText?: string;
125
125
  readonly declines: readonly ShellScanDecline[];
126
126
  readonly reads: readonly ReadCandidate[];
package/dist/tools/web.js CHANGED
@@ -6,7 +6,7 @@ import { egressThrownText } from "../core/untrusted-egress.js";
6
6
  import { isPrivateHost } from "../core/runner/image.js";
7
7
  import { DEFAULT_CHARS_PER_TOKEN } from "../internal/harness.js";
8
8
  import { binaryMagicFormat } from "./fs/safety.js";
9
- import { pdfMagicMatches } from "./fs/pdf.js";
9
+ import { pdfMagicMatches } from "../core/image-admission.js";
10
10
  import { toolFace } from "../core/tool-catalog-entries.js";
11
11
  import { thrownText } from "../core/thrown-value.js";
12
12
  const MAX_REDIRECTS = 10;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.51.2",
3
+ "version": "7.52.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -46,6 +46,9 @@
46
46
  "engines": {
47
47
  "node": ">=20.18.1"
48
48
  },
49
+ "workspaces": [
50
+ "packages/*"
51
+ ],
49
52
  "scripts": {
50
53
  "build": "rm -rf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.build.json --emitDeclarationOnly --removeComments false",
51
54
  "typecheck": "tsc --noEmit && echo \"typecheck OK (tsc --noEmit clean)\"",
@@ -78,8 +81,14 @@
78
81
  "gate:error-surface": "node scripts/verify-error-surface.mjs",
79
82
  "gate:criteria": "node scripts/criteria-lint.mjs",
80
83
  "gate:reminder-literal": "node scripts/verify-reminder-literal.mjs",
84
+ "gate:catalog-literal": "node scripts/verify-catalog-literal.mjs",
85
+ "gate:sugar-single-site": "node scripts/verify-sugar-single-site.mjs",
81
86
  "gate:wire": "node scripts/wire-changes.mjs",
82
87
  "gate:layering": "node scripts/verify-layering.mjs",
88
+ "gate:import-cycles": "node scripts/verify-import-cycles.mjs",
89
+ "gate:packages": "node scripts/verify-packages.mjs",
90
+ "gate:packages:pack": "node scripts/verify-packages.mjs --pack",
91
+ "build:packages": "node scripts/verify-packages.mjs --build",
83
92
  "gate:phase-api": "node scripts/verify-phase-api.mjs",
84
93
  "gate:closed-sets": "node scripts/verify-closed-sets.mjs",
85
94
  "gate:agent-returns": "node scripts/verify-agent-returns.mjs",
@@ -88,7 +97,10 @@
88
97
  "gate:tool-names": "node scripts/verify-tool-name-tables.mjs",
89
98
  "gate:name-readings": "node scripts/verify-name-readings.mjs",
90
99
  "gate:tool-docs": "node scripts/gen-tool-spec-fields.mjs --check",
100
+ "gate:prompt-docs": "node scripts/gen-prompt-docs.mjs --check",
101
+ "gen:prompt-docs": "node scripts/gen-prompt-docs.mjs",
91
102
  "gate:backlog-ledger": "node scripts/verify-backlog-ledger.mjs",
103
+ "gate:changelog-coverage": "node scripts/verify-changelog-coverage.mjs",
92
104
  "gate:child-clamp-seats": "node scripts/verify-child-clamp-seats.mjs",
93
105
  "gate:spawn-sites": "node scripts/verify-spawn-sites.mjs",
94
106
  "gate:point-free-callbacks": "node scripts/verify-point-free-callbacks.mjs",