@sema-agent/core 7.48.0 → 7.49.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 +418 -0
  2. package/README.md +2 -2
  3. package/dist/agents/cascade.d.ts +2 -5
  4. package/dist/agents/child-failure.d.ts +20 -0
  5. package/dist/agents/child-failure.js +28 -0
  6. package/dist/agents/cross-session-ref.d.ts +11 -3
  7. package/dist/agents/cross-session-ref.js +6 -0
  8. package/dist/agents/cumulative-stats.d.ts +12 -15
  9. package/dist/agents/cumulative-stats.js +1 -1
  10. package/dist/agents/extra-tool-names.js +1 -1
  11. package/dist/agents/inherited-tool-pool.d.ts +17 -13
  12. package/dist/agents/inherited-tool-pool.js +4 -3
  13. package/dist/agents/peer-session-drain.d.ts +3 -2
  14. package/dist/agents/peer-session-drain.js +5 -4
  15. package/dist/agents/send-message-tool.js +2 -9
  16. package/dist/agents/subagent.d.ts +10 -29
  17. package/dist/agents/subagent.js +65 -53
  18. package/dist/agents/suspend-guard.d.ts +1 -1
  19. package/dist/agents/tool-filter.d.ts +76 -65
  20. package/dist/agents/tool-filter.js +122 -92
  21. package/dist/agents/verify.d.ts +4 -7
  22. package/dist/agents/verify.js +6 -7
  23. package/dist/bench/metrics.d.ts +1 -2
  24. package/dist/brain/anthropic.js +133 -79
  25. package/dist/brain/errors.d.ts +53 -6
  26. package/dist/brain/errors.js +22 -2
  27. package/dist/brain/input-too-long.d.ts +40 -19
  28. package/dist/brain/input-too-long.js +3 -16
  29. package/dist/brain/open-responses.js +19 -10
  30. package/dist/brain/openai.js +123 -32
  31. package/dist/brain/provider-dialect.d.ts +214 -0
  32. package/dist/brain/provider-dialect.js +186 -0
  33. package/dist/brain/request-params.d.ts +27 -8
  34. package/dist/brain/request-params.js +7 -12
  35. package/dist/brain/request-route.d.ts +84 -0
  36. package/dist/brain/request-route.js +41 -0
  37. package/dist/brain/retry.d.ts +8 -0
  38. package/dist/brain/retry.js +10 -4
  39. package/dist/brain/route-adjudicator.d.ts +7 -0
  40. package/dist/brain/route-adjudicator.js +5 -1
  41. package/dist/brain/status-sink.d.ts +20 -0
  42. package/dist/brain/status-sink.js +11 -0
  43. package/dist/brain/stream-engine.d.ts +52 -1
  44. package/dist/brain/stream-engine.js +81 -24
  45. package/dist/brain/stream-shared.d.ts +57 -11
  46. package/dist/brain/stream-shared.js +28 -2
  47. package/dist/core/a2a.d.ts +1 -0
  48. package/dist/core/agent-config-dir.js +3 -2
  49. package/dist/core/allow-entry.d.ts +21 -12
  50. package/dist/core/allow-entry.js +38 -31
  51. package/dist/core/arg-summary.d.ts +17 -24
  52. package/dist/core/arg-summary.js +169 -68
  53. package/dist/core/ask-origin.d.ts +42 -24
  54. package/dist/core/ask-origin.js +18 -13
  55. package/dist/core/auto-mode-prompt-assets.d.ts +2 -2
  56. package/dist/core/auto-mode-prompt-assets.js +1 -1
  57. package/dist/core/auto-mode-prompt.d.ts +11 -1
  58. package/dist/core/auto-mode-prompt.js +13 -10
  59. package/dist/core/auto-mode-rebuild.d.ts +7 -0
  60. package/dist/core/auto-mode-rebuild.js +4 -1
  61. package/dist/core/canonical-json.d.ts +3 -0
  62. package/dist/core/canonical-json.js +2 -1
  63. package/dist/core/checkpoint-requirements.d.ts +892 -0
  64. package/dist/core/checkpoint-requirements.js +333 -0
  65. package/dist/core/checkpoint-store.d.ts +94 -65
  66. package/dist/core/checkpoint-store.js +14 -26
  67. package/dist/core/child-clamp-seats.d.ts +228 -69
  68. package/dist/core/child-clamp-seats.js +211 -17
  69. package/dist/core/classifier-unavailable-arm.d.ts +5 -1
  70. package/dist/core/classifier-unavailable-arm.js +3 -3
  71. package/dist/core/compliance.d.ts +3 -0
  72. package/dist/core/compliance.js +9 -4
  73. package/dist/core/credential-vocabulary.d.ts +140 -0
  74. package/dist/core/credential-vocabulary.js +131 -0
  75. package/dist/core/decision-receipt.js +1 -1
  76. package/dist/core/effective-path-target.d.ts +73 -16
  77. package/dist/core/effective-path-target.js +63 -33
  78. package/dist/core/engine-notice.d.ts +32 -7
  79. package/dist/core/file-history-store.js +2 -2
  80. package/dist/core/fs-write-gate-policy.d.ts +4 -0
  81. package/dist/core/fs-write-gate-policy.js +22 -5
  82. package/dist/core/gate-bounded-await.d.ts +28 -13
  83. package/dist/core/gate-bounded-await.js +41 -4
  84. package/dist/core/gate-fold.d.ts +1 -1
  85. package/dist/core/gate-fold.js +24 -25
  86. package/dist/core/gate-lanes.js +16 -15
  87. package/dist/core/gate-outcome.d.ts +20 -5
  88. package/dist/core/gate-outcome.js +5 -0
  89. package/dist/core/governance-codes.d.ts +11 -4
  90. package/dist/core/governance-codes.js +14 -3
  91. package/dist/core/hooks.d.ts +4 -0
  92. package/dist/core/locked-config.d.ts +8 -0
  93. package/dist/core/locked-config.js +14 -2
  94. package/dist/core/lsp.js +3 -5
  95. package/dist/core/mailbox-store.d.ts +3 -4
  96. package/dist/core/mcp-connection-pool.d.ts +75 -0
  97. package/dist/core/mcp-connection-pool.js +128 -0
  98. package/dist/core/mcp-failure.d.ts +2 -1
  99. package/dist/core/mcp-failure.js +13 -2
  100. package/dist/core/mcp.d.ts +4 -0
  101. package/dist/core/mcp.js +70 -43
  102. package/dist/core/memory-engine/content-origin.js +2 -14
  103. package/dist/core/memory-engine/engine.d.ts +32 -22
  104. package/dist/core/memory-engine/engine.js +103 -55
  105. package/dist/core/memory-engine/file-backend.d.ts +13 -30
  106. package/dist/core/memory-engine/file-backend.js +37 -252
  107. package/dist/core/memory-engine/index.d.ts +1 -1
  108. package/dist/core/memory-engine/index.js +1 -1
  109. package/dist/core/memory-engine/layout.d.ts +139 -9
  110. package/dist/core/memory-engine/layout.js +350 -7
  111. package/dist/core/memory-engine/scope-contract.d.ts +10 -0
  112. package/dist/core/memory-engine/scope-contract.js +8 -0
  113. package/dist/core/memory-engine/session-incarnation.d.ts +14 -4
  114. package/dist/core/memory-engine/session-incarnation.js +5 -2
  115. package/dist/core/memory-engine/types.d.ts +10 -1
  116. package/dist/core/memory.js +5 -17
  117. package/dist/core/model-seat.d.ts +4 -4
  118. package/dist/core/model-seat.js +1 -1
  119. package/dist/core/mounted-mcp.d.ts +57 -0
  120. package/dist/core/mounted-mcp.js +19 -0
  121. package/dist/core/namespaced-rule-name.d.ts +29 -0
  122. package/dist/core/namespaced-rule-name.js +52 -0
  123. package/dist/core/park-selfcheck.d.ts +1 -1
  124. package/dist/core/park-selfcheck.js +16 -6
  125. package/dist/core/path-guard-rows.d.ts +99 -0
  126. package/dist/core/path-guard-rows.js +45 -0
  127. package/dist/core/path-identity.d.ts +22 -16
  128. package/dist/core/path-identity.js +10 -10
  129. package/dist/core/path-rule-binding.d.ts +2 -1
  130. package/dist/core/path-rule-binding.js +3 -1
  131. package/dist/core/pause-registry.d.ts +16 -2
  132. package/dist/core/pause-registry.js +6 -0
  133. package/dist/core/permission-rule-consent.js +0 -7
  134. package/dist/core/permission-rule-model.d.ts +68 -24
  135. package/dist/core/permission-rule-model.js +105 -24
  136. package/dist/core/permission-rule-store.js +2 -2
  137. package/dist/core/permission-rules.d.ts +3 -30
  138. package/dist/core/permission-rules.js +4 -54
  139. package/dist/core/read-root-candidate.d.ts +1 -3
  140. package/dist/core/read-root-candidate.js +4 -15
  141. package/dist/core/real-approval.d.ts +4 -3
  142. package/dist/core/retired-keys.d.ts +13 -0
  143. package/dist/core/retired-keys.js +14 -0
  144. package/dist/core/roles.d.ts +2 -3
  145. package/dist/core/roles.js +15 -6
  146. package/dist/core/runner/assemble-result.d.ts +0 -7
  147. package/dist/core/runner/assemble-result.js +19 -31
  148. package/dist/core/runner/caller-roster.d.ts +78 -0
  149. package/dist/core/runner/caller-roster.js +219 -0
  150. package/dist/core/runner/checkpoint-scope.d.ts +16 -16
  151. package/dist/core/runner/checkpoint-scope.js +2 -2
  152. package/dist/core/runner/child-tree.js +2 -2
  153. package/dist/core/runner/contracts.d.ts +68 -35
  154. package/dist/core/runner/decide-continuation.d.ts +51 -3
  155. package/dist/core/runner/decide-continuation.js +76 -21
  156. package/dist/core/runner/denial-limit-arms.d.ts +17 -5
  157. package/dist/core/runner/denial-limit-arms.js +11 -9
  158. package/dist/core/runner/extra-body-dropped-seat.d.ts +8 -0
  159. package/dist/core/runner/extra-body-dropped-seat.js +22 -0
  160. package/dist/core/runner/finish-reason-seat.d.ts +9 -0
  161. package/dist/core/runner/finish-reason-seat.js +40 -0
  162. package/dist/core/runner/gate-exit.d.ts +44 -32
  163. package/dist/core/runner/gate-exit.js +112 -25
  164. package/dist/core/runner/inherited-ask-grants.js +6 -4
  165. package/dist/core/runner/mcp-redial.d.ts +3 -3
  166. package/dist/core/runner/mcp-redial.js +3 -4
  167. package/dist/core/runner/permission-rule-lanes.d.ts +1 -2
  168. package/dist/core/runner/permission-rule-lanes.js +5 -4
  169. package/dist/core/runner/prepare-artifact.d.ts +4 -0
  170. package/dist/core/runner/prepare-artifact.js +2 -1
  171. package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
  172. package/dist/core/runner/prepare-ask-lane.js +12 -6
  173. package/dist/core/runner/prepare-boundary-parks.d.ts +5 -8
  174. package/dist/core/runner/prepare-boundary-parks.js +4 -7
  175. package/dist/core/runner/prepare-caps-and-workflow.d.ts +11 -15
  176. package/dist/core/runner/prepare-caps-and-workflow.js +19 -27
  177. package/dist/core/runner/prepare-config-doors.d.ts +9 -9
  178. package/dist/core/runner/prepare-config-doors.js +10 -5
  179. package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
  180. package/dist/core/runner/prepare-defer-classify.js +5 -3
  181. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
  182. package/dist/core/runner/prepare-delegation-surface.js +3 -3
  183. package/dist/core/runner/prepare-gate-stations.d.ts +4 -4
  184. package/dist/core/runner/prepare-gate-stations.js +9 -7
  185. package/dist/core/runner/prepare-hands-readface.d.ts +7 -7
  186. package/dist/core/runner/prepare-hands-readface.js +7 -7
  187. package/dist/core/runner/prepare-inherited-gate.d.ts +7 -6
  188. package/dist/core/runner/prepare-inherited-gate.js +14 -15
  189. package/dist/core/runner/prepare-listings.d.ts +7 -24
  190. package/dist/core/runner/prepare-listings.js +4 -5
  191. package/dist/core/runner/prepare-memory-engine-session.d.ts +2 -0
  192. package/dist/core/runner/prepare-memory-engine-session.js +4 -3
  193. package/dist/core/runner/prepare-memory.d.ts +6 -0
  194. package/dist/core/runner/prepare-memory.js +13 -13
  195. package/dist/core/runner/prepare-offload-wrappers.d.ts +5 -0
  196. package/dist/core/runner/prepare-offload-wrappers.js +3 -3
  197. package/dist/core/runner/prepare-park-ask.d.ts +7 -8
  198. package/dist/core/runner/prepare-park-ask.js +17 -10
  199. package/dist/core/runner/prepare-policy-chain.js +24 -44
  200. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  201. package/dist/core/runner/prepare-project-context.js +3 -4
  202. package/dist/core/runner/prepare-protocol-tools.d.ts +9 -3
  203. package/dist/core/runner/prepare-protocol-tools.js +3 -4
  204. package/dist/core/runner/prepare-run-refs.d.ts +3 -0
  205. package/dist/core/runner/prepare-run-refs.js +2 -1
  206. package/dist/core/runner/prepare-safety-scan.d.ts +4 -8
  207. package/dist/core/runner/prepare-safety-scan.js +1 -2
  208. package/dist/core/runner/prepare-suspend-saga.d.ts +7 -1
  209. package/dist/core/runner/prepare-suspend-saga.js +28 -12
  210. package/dist/core/runner/prepare-task.d.ts +1 -1
  211. package/dist/core/runner/prepare-task.js +67 -56
  212. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -10
  213. package/dist/core/runner/prepare-wiring-manifest.js +5 -8
  214. package/dist/core/runner/resume-apply.js +7 -14
  215. package/dist/core/runner/resume-checkpoint-screen.js +11 -4
  216. package/dist/core/runner/resume-internals-and-config.js +2 -2
  217. package/dist/core/runner/resume-policy-outcome.js +5 -32
  218. package/dist/core/runner/resume-review-outcome.js +4 -1
  219. package/dist/core/runner/retired-runner-deps.d.ts +6 -0
  220. package/dist/core/runner/retired-runner-deps.js +10 -1
  221. package/dist/core/runner/run-brain-sinks.d.ts +4 -0
  222. package/dist/core/runner/run-brain-sinks.js +5 -2
  223. package/dist/core/runner/run-harness-handlers.js +27 -34
  224. package/dist/core/runner/run-leg.d.ts +0 -2
  225. package/dist/core/runner/run-leg.js +32 -33
  226. package/dist/core/runner/run-terminal-adoption.js +4 -3
  227. package/dist/core/runner/runtask.js +8 -8
  228. package/dist/core/runner/session-rule-policy.js +5 -5
  229. package/dist/core/runner/stream-settle-backstop.js +7 -4
  230. package/dist/core/runner/sub-agent-deny-lists.d.ts +51 -12
  231. package/dist/core/runner/sub-agent-deny-lists.js +111 -80
  232. package/dist/core/runner/terminal-account.d.ts +48 -0
  233. package/dist/core/runner/terminal-account.js +186 -0
  234. package/dist/core/runner/tool-end-body.d.ts +54 -17
  235. package/dist/core/runner/tool-end-body.js +27 -2
  236. package/dist/core/runner/tool-face-overlay.d.ts +3 -1
  237. package/dist/core/runner/tool-face-overlay.js +6 -4
  238. package/dist/core/runner/tool-occurrence.d.ts +106 -0
  239. package/dist/core/runner/tool-occurrence.js +123 -0
  240. package/dist/core/runner/tool-progress-lane.d.ts +16 -8
  241. package/dist/core/runner/tool-progress-lane.js +25 -34
  242. package/dist/core/runner/turn-attachments.d.ts +1 -1
  243. package/dist/core/runner/write-protection-station.d.ts +21 -9
  244. package/dist/core/runner/write-protection-station.js +20 -17
  245. package/dist/core/runner-deps.d.ts +12 -22
  246. package/dist/core/secret-env.d.ts +16 -5
  247. package/dist/core/secret-env.js +23 -63
  248. package/dist/core/sensitive-path-policy.d.ts +19 -18
  249. package/dist/core/sensitive-path-policy.js +73 -49
  250. package/dist/core/session-reconcile.d.ts +3 -0
  251. package/dist/core/session-reconcile.js +5 -4
  252. package/dist/core/session-store.d.ts +5 -0
  253. package/dist/core/session-store.js +12 -0
  254. package/dist/core/session.d.ts +13 -0
  255. package/dist/core/shared-memory/normalize.d.ts +17 -4
  256. package/dist/core/shared-memory/normalize.js +7 -7
  257. package/dist/core/shell-lexer.js +5 -4
  258. package/dist/core/skill-grant.js +1 -1
  259. package/dist/core/store-contracts/checkpoint-store-contract.js +13 -0
  260. package/dist/core/store-contracts/mailbox-store-contract.d.ts +7 -14
  261. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +27 -0
  262. package/dist/core/store-contracts/session-store-incarnation-contract.js +92 -0
  263. package/dist/core/stub-env.d.ts +2 -0
  264. package/dist/core/stub-env.js +1 -0
  265. package/dist/core/synthetic-read-verdict.js +2 -2
  266. package/dist/core/task-event.d.ts +59 -5
  267. package/dist/core/task-result.d.ts +1 -1
  268. package/dist/core/task-spec.d.ts +42 -9
  269. package/dist/core/terminal-cause.d.ts +94 -11
  270. package/dist/core/tighten-task-spec.d.ts +5 -4
  271. package/dist/core/tighten-task-spec.js +20 -25
  272. package/dist/core/tool-catalog-entries.d.ts +5 -7
  273. package/dist/core/tool-catalog-entries.js +14 -15
  274. package/dist/core/tool-face.d.ts +47 -0
  275. package/dist/core/tool-face.js +5 -0
  276. package/dist/core/tool-model-gate.d.ts +5 -5
  277. package/dist/core/tool-name-aliases.d.ts +26 -2
  278. package/dist/core/tool-name-aliases.js +13 -0
  279. package/dist/core/tool-name-list.d.ts +30 -0
  280. package/dist/core/tool-name-list.js +55 -0
  281. package/dist/core/tool-name-readings.d.ts +1 -9
  282. package/dist/core/tool-name-readings.js +1 -3
  283. package/dist/core/tool-policy.d.ts +195 -85
  284. package/dist/core/tool-policy.js +165 -380
  285. package/dist/core/tool-registry.d.ts +44 -1
  286. package/dist/core/tool-registry.js +26 -0
  287. package/dist/core/tool-roster.d.ts +19 -1
  288. package/dist/core/tool-roster.js +21 -2
  289. package/dist/core/tool-spec-members.d.ts +45 -0
  290. package/dist/core/tool-spec-members.js +169 -0
  291. package/dist/core/tool-spec.d.ts +46 -8
  292. package/dist/core/tools.d.ts +6 -2
  293. package/dist/core/tools.js +8 -4
  294. package/dist/core/untrusted-egress.js +29 -26
  295. package/dist/core/untrusted-text.d.ts +13 -10
  296. package/dist/core/untrusted-text.js +6 -44
  297. package/dist/core/workflow-journal-store.d.ts +4 -1
  298. package/dist/core/write-protect.d.ts +65 -29
  299. package/dist/core/write-protect.js +235 -177
  300. package/dist/engine/compaction/compaction.d.ts +2 -2
  301. package/dist/engine/execution-env/node-execution-env.d.ts +5 -23
  302. package/dist/engine/execution-env/node-execution-env.js +16 -38
  303. package/dist/engine/execution-env/symlink-chain.d.ts +23 -0
  304. package/dist/engine/execution-env/symlink-chain.js +32 -0
  305. package/dist/engine/harness/types.d.ts +7 -0
  306. package/dist/engine/llm/tool-turns.d.ts +3 -0
  307. package/dist/engine/llm/tool-turns.js +1 -0
  308. package/dist/engine/llm/types.d.ts +59 -23
  309. package/dist/engine/loop/agent-loop.js +16 -2
  310. package/dist/engine/session/memory-repo.js +11 -3
  311. package/dist/engine/session/repo-utils.d.ts +8 -0
  312. package/dist/engine/session/repo-utils.js +13 -0
  313. package/dist/index.d.ts +20 -23
  314. package/dist/index.js +17 -20
  315. package/dist/internal/harness.d.ts +1 -1
  316. package/dist/internal/harness.js +1 -1
  317. package/dist/internal/llm.d.ts +2 -2
  318. package/dist/internal/llm.js +1 -1
  319. package/dist/orchestration/governance-baseline-validity.js +1 -1
  320. package/dist/orchestration/run-spec.js +3 -3
  321. package/dist/orchestration/run-workflow-tool.d.ts +16 -35
  322. package/dist/orchestration/run-workflow-tool.js +38 -78
  323. package/dist/orchestration/workflow-governance.d.ts +21 -25
  324. package/dist/orchestration/workflow-governance.js +38 -78
  325. package/dist/orchestration/workflow-primitives.d.ts +7 -22
  326. package/dist/orchestration/workflow-primitives.js +5 -12
  327. package/dist/orchestration/workflow-types.d.ts +1 -1
  328. package/dist/orchestration/workflow.d.ts +61 -11
  329. package/dist/orchestration/workflow.js +189 -117
  330. package/dist/scenarios/full-body.d.ts +1 -1
  331. package/dist/shell/lex.d.ts +0 -21
  332. package/dist/shell/lex.js +250 -68
  333. package/dist/shell/shell-words.d.ts +35 -0
  334. package/dist/shell/shell-words.js +65 -0
  335. package/dist/stores/file/checkpoint-store.d.ts +5 -5
  336. package/dist/stores/file/checkpoint-store.js +5 -2
  337. package/dist/stores/file/index.d.ts +0 -8
  338. package/dist/stores/file/index.js +0 -12
  339. package/dist/stores/file/session-store.d.ts +3 -0
  340. package/dist/stores/file/session-store.js +44 -19
  341. package/dist/tools/artifact/artifact-tool.js +2 -0
  342. package/dist/tools/fs/bash-program-position.d.ts +10 -3
  343. package/dist/tools/fs/bash-program-position.js +21 -9
  344. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  345. package/dist/tools/fs/bash-readonly-classifier.js +8 -7
  346. package/dist/tools/fs/containment-face.d.ts +4 -0
  347. package/dist/tools/fs/containment-face.js +3 -0
  348. package/dist/tools/fs/fs-shared.d.ts +10 -0
  349. package/dist/tools/fs/fs-shared.js +18 -0
  350. package/dist/tools/fs/fs-write.js +4 -2
  351. package/dist/tools/fs/index.d.ts +1 -1
  352. package/dist/tools/fs/program-read-model.d.ts +39 -18
  353. package/dist/tools/fs/program-read-model.js +29 -19
  354. package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
  355. package/dist/tools/fs/program-read-model.rows.js +5 -5
  356. package/dist/tools/fs/read-deny-home.d.ts +33 -47
  357. package/dist/tools/fs/read-deny-home.js +35 -49
  358. package/dist/tools/fs/read-deny.d.ts +23 -22
  359. package/dist/tools/fs/read-deny.js +114 -89
  360. package/dist/tools/fs/removal-targets.d.ts +56 -23
  361. package/dist/tools/fs/removal-targets.js +65 -87
  362. package/dist/tools/fs/safety.d.ts +29 -22
  363. package/dist/tools/fs/safety.js +17 -11
  364. package/dist/tools/fs/search.js +10 -8
  365. package/dist/tools/fs/shell-read-argv.js +1 -1
  366. package/dist/tools/fs/shell-read-bindings.d.ts +169 -37
  367. package/dist/tools/fs/shell-read-bindings.js +324 -86
  368. package/dist/tools/fs/shell-read-builtins.d.ts +35 -0
  369. package/dist/tools/fs/shell-read-builtins.js +43 -0
  370. package/dist/tools/fs/shell-read-check.d.ts +15 -1
  371. package/dist/tools/fs/shell-read-check.js +51 -42
  372. package/dist/tools/fs/shell-read-establish.js +6 -6
  373. package/dist/tools/fs/shell-read-functions.d.ts +22 -0
  374. package/dist/tools/fs/shell-read-functions.js +29 -0
  375. package/dist/tools/fs/shell-read-judge.d.ts +48 -18
  376. package/dist/tools/fs/shell-read-judge.js +33 -4
  377. package/dist/tools/fs/shell-read-plan.d.ts +12 -6
  378. package/dist/tools/fs/shell-read-plan.js +118 -113
  379. package/dist/tools/fs/shell-read-start.d.ts +29 -6
  380. package/dist/tools/fs/shell-read-start.js +16 -9
  381. package/dist/tools/fs/shell-read-word.js +4 -0
  382. package/dist/tools/fs/shell-read-workdir.d.ts +156 -0
  383. package/dist/tools/fs/shell-read-workdir.js +281 -0
  384. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -2
  385. package/dist/tools/fs/shell-read-write-targets.js +14 -5
  386. package/dist/tools/fs/shell-reentry-text.d.ts +4 -6
  387. package/dist/tools/fs/shell-reentry-text.js +3 -11
  388. package/dist/tools/task-list.d.ts +1 -2
  389. package/dist/tools/web.js +1 -1
  390. package/package.json +4 -1
  391. package/test/export-surface.snapshot.json +107 -143
  392. package/wire-changes/core@7.49.0.json +4765 -0
  393. package/dist/agents/teacher.d.ts +0 -159
  394. package/dist/agents/teacher.js +0 -577
  395. package/dist/agents/team.d.ts +0 -226
  396. package/dist/agents/team.js +0 -257
  397. package/dist/core/strategy-store.d.ts +0 -238
  398. package/dist/core/strategy-store.js +0 -253
  399. package/dist/orchestration/builtin-workflows.d.ts +0 -133
  400. package/dist/orchestration/builtin-workflows.js +0 -159
  401. package/dist/scenarios/env.d.ts +0 -40
  402. package/dist/scenarios/env.js +0 -39
  403. package/dist/scenarios/scenario-registry.d.ts +0 -110
  404. package/dist/scenarios/scenario-registry.js +0 -140
  405. package/dist/scenarios/teacher-quickstart.d.ts +0 -40
  406. package/dist/scenarios/teacher-quickstart.js +0 -12
  407. package/dist/stores/cc/lockfile.d.ts +0 -10
  408. package/dist/stores/cc/lockfile.js +0 -98
  409. package/dist/stores/cc/mailbox-store.d.ts +0 -20
  410. package/dist/stores/cc/mailbox-store.js +0 -255
  411. package/dist/stores/cc/roster-adapter.d.ts +0 -12
  412. package/dist/stores/cc/roster-adapter.js +0 -117
  413. package/dist/stores/cc/sidecar-transcript.d.ts +0 -97
  414. package/dist/stores/cc/sidecar-transcript.js +0 -264
  415. package/dist/stores/cc/task-list-store.d.ts +0 -2
  416. package/dist/stores/cc/task-list-store.js +0 -125
  417. package/dist/stores/file/strategy-store.d.ts +0 -97
  418. package/dist/stores/file/strategy-store.js +0 -341
@@ -1,6 +1,7 @@
1
1
  import { hasWin32AliasedComponent, normalizeAbsPathLexically, withinAnyRoot } from "./safety.js";
2
2
  import { globInWord } from "./bash-readonly-classifier.js";
3
3
  import { readOfText } from "./shell-read-word.js";
4
+ import { skipsRootsContainment } from "./containment-face.js";
4
5
  export const SHELL_READ_FINDINGS = Object.freeze(["deny_hit", "outside_roots", "unestablishable", "declined", "identity_changed", "none"]);
5
6
  export const SHELL_READ_SEATS = Object.freeze(["full_shell", "readonly", "rejudge"]);
6
7
  export const SHELL_READ_FINDING_RANK = {
@@ -37,11 +38,11 @@ export function judgeRead(read, boundary) {
37
38
  const folded = normalizeAbsPathLexically(view);
38
39
  const pattern = writeTarget ? boundary.writeDenyMatch(folded) : boundary.denyMatch(folded, walked);
39
40
  if (pattern !== null)
40
- return { read, kind: "deny_hit", deny: { pattern, matchedView: folded, ...(writeTarget ? { face: "write" } : {}) }, containment: containmentOf(read, boundary) };
41
+ return { read, kind: "deny_hit", deny: { pattern, matchedView: folded, ...(writeTarget ? { face: "write" } : {}) }, containment: writeTarget ? writeContainmentOf(read, boundary) : containmentOf(read, boundary) };
41
42
  }
42
43
  }
43
44
  if (writeTarget)
44
- return { read, kind: "none", containment: containmentOf(read, boundary) };
45
+ return judgeWrite(read, boundary);
45
46
  const containment = containmentOf(read, boundary);
46
47
  if (read.candidate.denyOnly === true)
47
48
  return { read, kind: "none", containment };
@@ -72,19 +73,45 @@ function containmentOf(read, boundary) {
72
73
  return "unknown";
73
74
  return withinAnyRoot(boundary.roots, read.target) ? "inside" : "outside";
74
75
  }
76
+ const NULL_DEVICE = "/dev/null";
77
+ function writeContainmentOf(read, boundary) {
78
+ const device = read.written?.seat === "landing" && read.candidate.expands === false && read.candidate.spelled === NULL_DEVICE;
79
+ if (read.written === undefined || read.candidate.denyOnly === true || device)
80
+ return "open";
81
+ const roots = boundary.writeRoots;
82
+ if (roots === undefined || read.target === undefined)
83
+ return "unknown";
84
+ return withinAnyRoot(roots, read.target) ? "inside" : "outside";
85
+ }
86
+ function judgeWrite(read, boundary) {
87
+ const containment = writeContainmentOf(read, boundary);
88
+ if (containment === "open")
89
+ return { read, kind: "none", containment };
90
+ if (read.established === "unestablishable" || boundary.writeRoots === undefined)
91
+ return { read, kind: "unestablishable", containment };
92
+ return { read, kind: containment === "outside" && !skipsRootsContainment(boundary.writeFace) ? "outside_roots" : "none", containment };
93
+ }
75
94
  export function stationGuards(boundary) {
76
95
  return boundary.face !== "open" || boundary.denyMatch !== undefined;
77
96
  }
97
+ export function writeFaceGuards(boundary) {
98
+ return !skipsRootsContainment(boundary.writeFace) || boundary.writeDenyMatch !== undefined;
99
+ }
100
+ export function anyFaceGuards(boundary) {
101
+ return stationGuards(boundary) || writeFaceGuards(boundary);
102
+ }
78
103
  export function foldShellReadFindings(judged) {
79
104
  let best = { finding: "none" };
80
105
  let unestablished = false;
81
106
  const raise = (next) => {
82
107
  if (next.finding === "unestablishable" || next.finding === "declined") {
83
- if (!judged.guards)
108
+ const face = next.witness?.read.candidate.face;
109
+ if (!(next.witness === undefined ? judged.guards || judged.writeGuards : face === "write" ? judged.writeGuards : judged.guards))
84
110
  return;
85
111
  unestablished = true;
86
112
  }
87
- if (SHELL_READ_FINDING_RANK[next.finding] > SHELL_READ_FINDING_RANK[best.finding])
113
+ const rank = SHELL_READ_FINDING_RANK[next.finding] - SHELL_READ_FINDING_RANK[best.finding];
114
+ if (rank > 0 || (rank === 0 && next.witness?.read.candidate.face === "write" && best.witness?.read.candidate.face !== "write"))
88
115
  best = next;
89
116
  };
90
117
  const walk = (j, denyOnly) => {
@@ -125,6 +152,7 @@ export function judgeEstablishedPlan(plan, established, boundary, nestedOf) {
125
152
  nested: segment.nested.map(nestedOf),
126
153
  })),
127
154
  guards: stationGuards(boundary),
155
+ writeGuards: writeFaceGuards(boundary),
128
156
  };
129
157
  }
130
158
  export function allFindings(judged) {
@@ -208,5 +236,6 @@ export function rejudge(judged, boundary) {
208
236
  nested: s.nested.map((n) => rejudge(n, boundary)),
209
237
  })),
210
238
  guards: stationGuards(boundary),
239
+ writeGuards: writeFaceGuards(boundary),
211
240
  };
212
241
  }
@@ -24,9 +24,9 @@
24
24
  * and here-strings are data (an UNQUOTED here-document body's substitutions are nested plans) — except to a shell
25
25
  * whose argv names no script: what arrives on its standard input is its script, a nested plan where the text spells it;
26
26
  * · `$(…)`, backticks and `<(…)` bodies are nested plans; a word carrying one cannot be resolved from the text;
27
- * · `cd` moves the base the LATER segments resolve against, as a candidate SET (`&&` proves a move, nothing
28
- * else does); `pushd`/`popd` lose the base; a loop body with a base move is replayed per LITERAL beat and
29
- * otherwise loses the base for what follows.
27
+ * · the working-directory thread (`shell-read-workdir.ts`) moves the base the LATER segments resolve against, as a
28
+ * candidate SET (`&&` proves a move, nothing else does), and gives `PWD` / `OLDPWD` their values; a loop body with
29
+ * a base move is replayed per LITERAL beat and otherwise loses the base for what follows.
30
30
  * Every ambiguity resolves toward MORE candidates, never fewer.
31
31
  */
32
32
  import { type LenientConnector, type LexedRedirect, type LexedWord } from "../../shell/lex.js";
@@ -47,6 +47,9 @@ export interface ShellReadDeclarations {
47
47
  readonly cwd: string;
48
48
  readonly roots: readonly string[];
49
49
  readonly face?: "open" | "roots";
50
+ /** The `CDPATH` the shell starts with (the environment's declared one, `ExecutionEnv.cdPath`; a bare relative `cd` is
51
+ * searched through it): absent ⇒ none declared, read as unset; `unknown` ⇒ the text cannot know it. */
52
+ readonly cdPath?: readonly string[] | "unknown";
50
53
  }
51
54
  export interface ReadCandidate {
52
55
  /** The folded word (quotes removed, a literal leading tilde spelled `./~`) — a pattern's name where nothing matches. */
@@ -70,10 +73,10 @@ export interface ReadCandidate {
70
73
  /** A home word the plan does not place (the text may rebind `HOME`, it sits in a value, or it is a literal tilde the
71
74
  * program may expand itself): spelled with the DECLARED home. The judge reads it for deny rows ONLY. */
72
75
  readonly floorSpelled?: string;
73
- /** A deny-floor shadow of a `floorSpelled` spelling, established as a read and judged for deny rows only. */
76
+ /** Deny rows only (never containment): a `floorSpelled` shadow, or the read copy of a word the program only writes. */
74
77
  readonly denyOnly?: true;
75
- /** A WRITE target of the command (a `>`-family redirection, a `sed` `w` file, the operands of a file-writing command):
76
- * established like a read and judged against the deny table's WRITE face only — never for containment, never a read. */
78
+ /** A WRITE target (a `>`-family redirection, a `sed` `w` file, a file-writing command's operand): established like a
79
+ * read, judged on the WRITE face — the write deny rows, then the write roots where written — never as a read. */
77
80
  readonly face?: "write";
78
81
  /** A reading the station takes where no row says the program opens the word — a word of a program with no row, a
79
82
  * role only another implementation reads — so the filesystem decides it: `containment` = the spelling always meets
@@ -129,6 +132,9 @@ export interface ShellReadSegment {
129
132
  /** Substitution bodies, unquoted here-document substitutions, and re-entered shell text, each its own plan. */
130
133
  readonly nested: readonly ShellReadPlan[];
131
134
  readonly reach: SegmentReach;
135
+ /** The names a change that can reach this segment touched (`BindingTable.rebound`): any other name still holds what the
136
+ * shell provides here. `all`: any name may have changed. */
137
+ readonly rebound: ReadonlySet<string> | "all";
132
138
  }
133
139
  export interface ShellReadPlan {
134
140
  readonly segments: readonly ShellReadSegment[];
@@ -1,13 +1,15 @@
1
1
  import { lexShell, PARSE_FAILURE_SENTENCE, substitutionsInHeredocBody, literalTextOf, literalTildeSpelling, } from "../../shell/lex.js";
2
2
  import { programTextOf, readOfText, readsOf, rewritten, segmentReadingsOf, settledFieldsOf, spelledWithHome, isArithmeticBody, unreadableSentenceOf } from "./shell-read-word.js";
3
3
  import { assignmentArithmetic, bare, listedVerbCandidates, pathSubcommandOf, programTextSentence, pushAssignment, roleReads, subscriptOnly, unlistedWords } from "./shell-read-argv.js";
4
- import { ARITHMETIC_TEST_OPERATORS, BindingThread, arithmeticSubstitutions, isArithmeticCommand, bindingEffectOf, loopListHeadAt, parameterBodySubstitutions, projectSegment, valueOf, } from "./shell-read-bindings.js";
5
- import { isShellSyntaxWord, programBasename, programPositionRunsShell, readProgramPosition, shellScanDeclineOf, } from "./bash-program-position.js";
4
+ import { ARITHMETIC_TEST_OPERATORS, BindingThread, arithmeticSubstitutions, isArithmeticCommand, bindingEffectOf, joinRebound, loopListHeadAt, namesBoundIn, parameterBodySubstitutions, projectSegment, valueOf, } from "./shell-read-bindings.js";
5
+ import { commandEnvironmentNames, isShellSyntaxWord, programBasename, programPositionRunsShell, readProgramPosition, shellScanDeclineOf, } from "./bash-program-position.js";
6
+ import { WorkdirThread, baseShiftOf, textCalls, textMayMove, textMovesOf, textSetsPhysical, withCommandEnvironment } from "./shell-read-workdir.js";
6
7
  import { POLL_LOOP_MAX_BEATS, attachedOptionPayloads, resolveOperandLexically, isPathShapedToken, programTextSlotFiles, } from "./bash-readonly-classifier.js";
7
- import { BLOCK_WORD_NAMES, commandLineOf, commandLineOptionSpellings, declaredAttributes, isDeclarationBuiltin, isInertBuiltin, launcherOptionPathRole, launcherOptionStep, readOptionRole, programLanguageOf, programTextOpensOf, selectedOptions, movesBaseUnfollowed, opensNoOperand, programDelivery, programRow, readArgRoles, runsShellText, } from "./program-read-model.js";
8
- import { isAbsolutePathForm } from "./safety.js";
9
- import { codeBodies, launchedInput, pipedTextOf, reentryText, scriptFileAt, stdinTextsOf } from "./shell-reentry-text.js";
10
- import { plannedOnce, rebasePlan, startedAt, stdinFeeds } from "./shell-read-start.js";
8
+ import { BLOCK_WORD_NAMES, commandLineOf, commandLineOptionSpellings, declaredAttributes, isDeclarationBuiltin, isInertBuiltin, launcherOptionPathRole, launcherOptionStep, readOptionRole, programLanguageOf, programTextOpensOf, selectedOptions, movesBaseByStack, opensNoOperand, programDelivery, programRow, readArgRoles, runsShellText, } from "./program-read-model.js";
9
+ import { shellWordsOf } from "../../shell/shell-words.js";
10
+ import { isAbsoluteForFamily } from "./safety.js";
11
+ import { codeBodies, launchedInput, reentryText, scriptFileAt, stdinTextsOf } from "./shell-reentry-text.js";
12
+ import { heldAt, plannedOnce, rebasePlan, startedAt, stdinFeeds } from "./shell-read-start.js";
11
13
  export const READ_CANDIDATE_PROVENANCES = Object.freeze([
12
14
  "operand",
13
15
  "option_value",
@@ -23,22 +25,26 @@ export const READ_CANDIDATE_PROVENANCES = Object.freeze([
23
25
  "object_path",
24
26
  ]);
25
27
  const NESTING_MAX = 4;
26
- const LOOP_END = new Set(["done"]), COPROC = new Set(["coproc"]);
27
- const BASE_CANDIDATE_MAX = 256;
28
+ const LOOP_END = shellWordsOf("loop_end"), LOOP_CONDITION = shellWordsOf("loop_condition"), COPROC = shellWordsOf("background"), RESERVED = shellWordsOf("rule_reserved");
28
29
  export function planShellReads(command, decl, depth = 0, start) {
30
+ return planText(command, decl, depth, start).plan;
31
+ }
32
+ function planText(command, decl, depth, start, from) {
29
33
  const shape = { grouped: false, backgrounded: false, strayRedirection: false };
30
34
  if (depth > NESTING_MAX)
31
- return { segments: [], unreadable: `command text nested more than ${NESTING_MAX} levels deep`, shape };
35
+ return { plan: { segments: [], unreadable: `command text nested more than ${NESTING_MAX} levels deep`, shape }, end: "unmodelled", physical: false };
32
36
  const lexed = lexShell(command);
33
37
  if ("parseFailure" in lexed)
34
- return { segments: [], unreadable: PARSE_FAILURE_SENTENCE[lexed.parseFailure.reason], shape };
38
+ return { plan: { segments: [], unreadable: PARSE_FAILURE_SENTENCE[lexed.parseFailure.reason], shape }, end: "unmodelled", physical: false };
35
39
  const unreadable = unreadableSentenceOf(lexed, command);
36
40
  const projected = lexed.segments.map((segment) => projectSegment(segment));
37
41
  const named = projected.map((p, si) => bindingEffectOf(lexed.segments[si], p.argv, p, () => "unknown"));
38
42
  const boundHere = (name) => named.some((e) => e.all === "unknown" || e.bindings.some((b) => b.name === name));
39
43
  const boundAnywhere = (name) => boundHere(name) || start?.boundAnywhere(name) === true;
40
44
  const declaredHome = decl.homeDir !== undefined ? [{ spelled: decl.homeDir }] : "unknown";
41
- const initial = start?.initial ?? ((name) => (name === "HOME" ? declaredHome : "unknown"));
45
+ const declaredCdPath = decl.cdPath === "unknown" ? "unknown" : [{ spelled: (decl.cdPath ?? []).join(":") }];
46
+ const declared = (name, rebound) => (rebound ? "unknown" : name === "HOME" ? declaredHome : name === "CDPATH" ? declaredCdPath : "unknown");
47
+ const initial = start?.initial ?? ((name) => declared(name, false));
42
48
  const loopShapes = projected.map((p) => loopShapeOf(p));
43
49
  const loopEndOf = new Map();
44
50
  {
@@ -53,26 +59,35 @@ export function planShellReads(command, decl, depth = 0, start) {
53
59
  }
54
60
  });
55
61
  }
56
- const names = new BindingThread(lexed.segments, named, initial, boundAnywhere, loopEndOf, start?.homeTouched === true, start?.inherited);
57
- const laterStart = { initial: (name) => (name === "HOME" && !boundAnywhere("HOME") ? declaredHome : "unknown"), boundAnywhere, homeTouched: boundAnywhere("HOME"), inherited: names.inheritedAt() };
58
- const nestedPlanner = (table) => (text, kind, at) => planShellReads(text, decl, depth + 1, kind === "substitution" ? { initial: (name) => table.stateOf(name), boundAnywhere, homeTouched: table.homeTouched, inherited: table.inherited } : startedAt(laterStart, at));
62
+ const names = new BindingThread(lexed.segments, named, initial, boundAnywhere, loopEndOf, start?.homeTouched === true, start?.inherited, start?.names);
63
+ const laterStart = { initial: (name) => declared(name, boundAnywhere(name)), boundAnywhere, homeTouched: boundAnywhere("HOME"), inherited: names.inheritedAt() };
64
+ const nestedPlanner = (table, si) => (text, kind, at) => planShellReads(text, decl, depth + 1, kind === "substitution" ? { initial: (name) => table.stateOf(name), boundAnywhere, homeTouched: table.homeTouched, inherited: table.inherited, thread: heldThread(moves.functions), names: heldAt(table) } : startedAt(kind === "now" ? { ...laterStart, inherited: { ...laterStart.inherited, rebound: joinRebound(table.inherited.rebound, commandEnvironmentNames(projected[si].argv)) }, ...(readProgramPosition(projected[si].argv).program?.basename === "eval" ? { thread: heldThread(moves.unread), names: heldAt(table, names.functionBound()) } : {}) } : { ...laterStart, thread: heldThread(moves.functions, workdir.mayBePhysical || moves.physicalAt.some(Boolean)), names: names.heldAnywhere() }, at));
59
65
  const empty = lexed.segments.map((segment, si) => segmentIsEmpty(segment, projected[si]));
60
66
  const facts = projected.map(() => undefined);
61
67
  const tables = projected.map(() => undefined);
62
- const fedAt = stdinFeeds(lexed.segments, start?.stdin ?? [], (si) => (facts[si - 1] === undefined ? [projected[si - 1].argv] : (facts[si - 1].readings ?? [facts[si - 1]]).map((f) => f.argv)).map((argv) => pipedTextOf(lexed.segments[si - 1], argv)));
68
+ const fedAt = stdinFeeds(lexed.segments, start?.stdin ?? [], (si) => [{ from: `${lexed.segments[si - 1].text.trim()} |` }]);
63
69
  const factsAt = (si, table) => {
64
70
  tables[si] ??= table;
65
- return (facts[si] ??= segmentFacts(lexed.segments[si], projected[si], decl, table, nestedPlanner(table), fedAt(si)));
71
+ return (facts[si] ??= segmentFacts(lexed.segments[si], projected[si], decl, table, nestedPlanner(table, si), fedAt(si)));
66
72
  };
67
73
  const basesOf = projected.map(() => new Set());
68
74
  const unmodelled = projected.map(() => false);
69
- let candidates = (start?.bases !== undefined && start.bases !== "unmodelled" ? start.bases : [decl.cwd]).map((base) => ({ base, skippedCd: -1 }));
70
- let baseLost = start?.bases === "unmodelled";
71
- let floorBases;
72
- const loseBase = (reachable, floorTarget) => {
73
- baseLost = true;
74
- floorBases ??= [...new Set([...reachable.map((c) => c.base), ...(floorTarget !== undefined ? [floorTarget] : [])])];
75
+ const moves = textMovesOf(new Set(projected.flatMap((p) => p.defines)), projected.map((p) => p.argv), (si) => names.inFunctionBody(si), start?.thread);
76
+ names.runsBodies(moves.runsBodyAt, moves.bodiesAt.some((b) => b.length > 0), moves.mayCallAt.map((c, si) => (c ? commandEnvironmentNames(projected[si].argv) : undefined)));
77
+ const workdir = new WorkdirThread(from ?? start?.bases ?? [decl.cwd], depth === 0 || start?.bases !== undefined || from !== undefined, start?.thread?.physical === true);
78
+ const workdirContext = {
79
+ ...(decl.homeDir !== undefined ? { homeDir: decl.homeDir } : {}),
80
+ textBinds: boundAnywhere,
81
+ evalEnd: (text, c, table) => {
82
+ if (moves.bodyMoves && textCalls(text, moves.functions))
83
+ return { end: "unmodelled", physical: false };
84
+ if (!textMayMove(text, moves.functions) && !textSetsPhysical(text))
85
+ return { end: [c], physical: false };
86
+ const ran = planText(text, decl, depth + 1, { initial: (name) => table.stateOf(name), boundAnywhere, homeTouched: table.homeTouched, inherited: table.inherited, thread: heldThread(moves.unread), names: heldAt(table, names.functionBound()) }, [c]);
87
+ return { end: ran.end, physical: ran.physical };
88
+ },
75
89
  };
90
+ const heldThread = (unread, physical = workdir.mayBePhysical) => ({ physical, functions: moves.functions, unread });
76
91
  let andRunStart = 0;
77
92
  let crossedAllAnd = true;
78
93
  let crossedAnyOr = false;
@@ -89,55 +104,28 @@ export function planShellReads(command, decl, depth = 0, start) {
89
104
  }
90
105
  if (empty[si]) {
91
106
  if (!replay)
92
- factsAt(si, names.tableAt(si, andRunStart));
107
+ factsAt(si, names.tableAt(si, andRunStart, workdir.where(andRunStart)));
93
108
  return;
94
109
  }
95
110
  if (previousEmitted >= 0 && !crossedAllAnd)
96
111
  andRunStart = crossedAnyOr ? si + 1 : si;
97
- const f = replay ? facts[si] : factsAt(si, names.tableAt(si, andRunStart));
98
- const pipeFed = f.connector === "|" || lexed.segments[si + 1]?.connector === "|";
99
- reaches[si] ??= { andJoined: previousEmitted >= 0 && crossedAllAnd, orBroken: !crossedAllAnd && crossedAnyOr, moveProvable: f.moveProvable && !replay && !pipeFed };
112
+ const f = replay ? facts[si] : factsAt(si, names.tableAt(si, andRunStart, workdir.where(andRunStart)));
113
+ const pipeFed = f.connector === "|" || lexed.segments[si + 1]?.connector === "|", call = moves.callAt(si, tables[si]);
114
+ reaches[si] ??= { andJoined: previousEmitted >= 0 && crossedAllAnd, orBroken: !crossedAllAnd && crossedAnyOr, moveProvable: f.moveProvable && !replay && !pipeFed && !names.runsApart(si) };
100
115
  crossedAllAnd = true;
101
116
  crossedAnyOr = false;
102
117
  previousEmitted = si;
103
- const reachable = candidates.filter((c) => c.skippedCd < andRunStart);
104
- if (baseLost)
118
+ if (workdir.lost)
105
119
  unmodelled[si] = true;
106
120
  else
107
- for (const c of reachable)
121
+ for (const c of workdir.reachable(andRunStart))
108
122
  basesOf[si].add(c.base);
109
- const move = f.baseMove;
110
- if (move === undefined || baseLost)
111
- return;
112
- if (typeof move.target === "string" || move.target.expands !== false) {
113
- loseBase(reachable, typeof move.target !== "string" ? move.target.floorSpelled : move.target === "home" ? decl.homeDir : undefined);
114
- return;
115
- }
116
- const target = move.target.spelled;
117
- const provable = reaches[si].moveProvable;
118
- const branched = [];
119
- for (const c of candidates) {
120
- if (c.skippedCd < andRunStart) {
121
- const moved = joinBase(c.base, target, decl.homeDir);
122
- if (moved === undefined) {
123
- loseBase(reachable);
124
- return;
125
- }
126
- branched.push({ base: moved, skippedCd: c.skippedCd });
127
- }
128
- branched.push({ base: c.base, skippedCd: provable ? si : c.skippedCd });
129
- }
130
- const byBase = new Map();
131
- for (const c of branched) {
132
- const prior = byBase.get(c.base);
133
- if (prior === undefined || c.skippedCd < prior.skippedCd)
134
- byBase.set(c.base, c);
135
- }
136
- const next = [...byBase.values()];
137
- if (next.length > BASE_CANDIDATE_MAX)
138
- loseBase(reachable);
139
- else
140
- candidates = next;
123
+ if (moves.physicalAt[si])
124
+ workdir.setPhysical();
125
+ if (f.shift !== undefined && call !== "named")
126
+ workdir.shift(si, { shift: f.shift, provable: reaches[si].moveProvable && call === undefined, andRunStart, table: withCommandEnvironment(tables[si], projected[si].argv), cdable: moves.cdableFrom >= 0 && si > moves.cdableFrom }, workdirContext);
127
+ if (moves.losesBaseAt(si, tables[si]))
128
+ workdir.lose(si, workdir.reachable(andRunStart));
141
129
  };
142
130
  const bind = (si) => {
143
131
  const table = tables[si];
@@ -146,12 +134,16 @@ export function planShellReads(command, decl, depth = 0, start) {
146
134
  const end = loopEndOf.get(si);
147
135
  if (end !== undefined) {
148
136
  const body = named.slice(si + 1, end);
149
- names.kill(si, body.some((e) => e.all === "unknown") ? "all" : body.flatMap((e) => e.bindings.map((b) => b.name)));
137
+ names.kill(si, body.some((e) => e.all === "unknown") ? "all" : [...body.flatMap((e) => e.bindings.map((b) => b.name)), ...(moves.mayMove.slice(si + 1, end).some(Boolean) ? ["PWD", "OLDPWD"] : [])]);
150
138
  }
151
139
  const segment = lexed.segments[si];
152
- const effect = bindingEffectOf(segment, projected[si].argv, projected[si], (w, quoted, overlay) => valueOf(w, table, quoted, overlay));
140
+ const effect = bindingEffectOf(segment, projected[si].argv, projected[si], (w, quoted, overlay) => valueOf(w, table, quoted, overlay), moves.callAt(si, table), (builtin) => table.builtinState(builtin));
153
141
  const { all, ...rest } = effect;
154
142
  names.apply(si, all === "reset" ? rest : effect, segment.connector === "&&" || segment.connector === "||", end);
143
+ if (moves.callAt(si, table) !== undefined)
144
+ names.kill(si, moves.callsUnread(si, table) ? "all" : names.functionBound());
145
+ if (moves.bodiesAt[si].length > 0)
146
+ names.runsLater(si, moves.bodiesAt[si].reduce((held, b) => joinRebound(held, namesBoundIn(b)), names.functionBound()));
155
147
  };
156
148
  for (let si = 0; si < projected.length; si++) {
157
149
  const shapeOfLoop = loopShapes[si];
@@ -168,17 +160,15 @@ export function planShellReads(command, decl, depth = 0, start) {
168
160
  const frame = loops.pop();
169
161
  if (frame === undefined)
170
162
  continue;
171
- const body = facts.slice(frame.bodyStart, si);
172
- const moves = body.some((b) => b?.baseMove !== undefined);
173
- if (!moves)
163
+ if (!facts.slice(frame.bodyStart, si).some((b, k) => b?.shift !== undefined || (tables[frame.bodyStart + k] !== undefined && moves.losesBaseAt(frame.bodyStart + k, tables[frame.bodyStart + k]))))
174
164
  continue;
175
165
  if (frame.beats === undefined) {
176
166
  for (let k = frame.bodyStart; k < si; k++)
177
167
  unmodelled[k] = true;
178
- loseBase(candidates.filter((c) => c.skippedCd < andRunStart));
168
+ workdir.lose(si, workdir.reachable(andRunStart));
179
169
  continue;
180
170
  }
181
- for (let beat = 1; beat < frame.beats && !baseLost; beat++) {
171
+ for (let beat = 1; beat < frame.beats && !workdir.lost; beat++) {
182
172
  for (let k = frame.bodyStart; k < si; k++)
183
173
  thread(k, true);
184
174
  }
@@ -195,14 +185,19 @@ export function planShellReads(command, decl, depth = 0, start) {
195
185
  if (threaded.empty)
196
186
  continue;
197
187
  const bases = unmodelled[si] ? "unmodelled" : [...basesOf[si]];
188
+ const floorBases = workdir.floorBases;
198
189
  const floor = unmodelled[si] && floorBases !== undefined && floorBases.length > 0 ? floorBases : undefined;
199
- const withBases = (c) => {
190
+ const placedAt = (at, atFloor) => (c) => {
200
191
  const relative = relativeSpelling(c.spelled);
201
- const here = c.provenance === "object_path" && bases !== "unmodelled" ? [...new Set(bases.flatMap(ancestorsOf))] : bases;
202
- return { ...c, bases: relative ? here : [], ...(relative && floor !== undefined ? { floorBases: floor } : {}) };
192
+ const here = c.provenance === "object_path" && at !== "unmodelled" ? [...new Set(at.flatMap(ancestorsOf))] : at;
193
+ return { ...c, bases: relative ? here : [], ...(relative && atFloor !== undefined ? { floorBases: atFloor } : {}) };
203
194
  };
195
+ const withBases = placedAt(bases, floor);
204
196
  const move = threaded.baseMove;
205
197
  for (const f of threaded.readings ?? [threaded]) {
198
+ const moved = readProgramPosition(f.argv).launcherMovesBase;
199
+ const programPlaced = moved ? placedAt("unmodelled", bases !== "unmodelled" ? bases : floor) : withBases;
200
+ const programRead = (c) => (c.provenance === "stdin_redirect" ? withBases(c) : programPlaced(c));
206
201
  segments.push({
207
202
  text: f.text,
208
203
  connector: f.connector,
@@ -213,16 +208,17 @@ export function planShellReads(command, decl, depth = 0, start) {
213
208
  ...(f.unreadProgramText !== undefined ? { unreadProgramText: f.unreadProgramText } : {}),
214
209
  ...(f.stdin !== undefined ? { stdin: f.stdin } : {}),
215
210
  declines: f.declines,
216
- reads: dedupe(f.reads).map(withBases),
211
+ reads: dedupe(f.reads).map(programRead),
217
212
  redirects: f.redirects.map((r) => (r.target === undefined ? { op: r.op, direction: r.direction } : { op: r.op, direction: r.direction, target: withBases(r.target) })),
218
213
  ...(move !== undefined ? { baseMove: { target: typeof move.target === "string" ? move.target : withBases(move.target) } } : {}),
219
214
  bases,
220
- nested: f.nested.map((n) => (f.elsewhere?.has(n) === true ? n : rebasePlan(n, decl.cwd, bases))),
215
+ nested: f.nested.map((n) => (f.elsewhere?.has(n) === true ? n : rebasePlan(n, decl.cwd, moved && f.shellSide?.has(n) !== true ? "unmodelled" : bases, bases === "unmodelled" ? floor : bases))),
221
216
  reach: reaches[si] ?? { andJoined: false, orBroken: false, moveProvable: false },
217
+ rebound: tables[si]?.rebound ?? "all",
222
218
  });
223
219
  }
224
220
  }
225
- return { segments, shape, ...(unreadable !== undefined ? { unreadable } : {}) };
221
+ return { plan: { segments, shape, ...(unreadable !== undefined ? { unreadable } : {}) }, end: workdir.end(), physical: workdir.mayBePhysical };
226
222
  }
227
223
  function ancestorsOf(dir) {
228
224
  const out = [dir];
@@ -234,15 +230,14 @@ function parentOf(dir) {
234
230
  return resolveOperandLexically(dir, "..", undefined) ?? dir;
235
231
  }
236
232
  function loopShapeOf(p) {
237
- const head = p.argv[0];
238
- if (head === undefined)
233
+ if (p.argv[0] === undefined)
239
234
  return undefined;
240
235
  if (loopListHeadAt(p.argv) !== undefined)
241
236
  return "head";
242
- if (isShellSyntaxWord(head, LOOP_END))
237
+ if (isShellSyntaxWord(p.argv[0], LOOP_END))
243
238
  return "end";
244
239
  const pos = readProgramPosition(p.argv);
245
- return pos.controlWords.some((i) => p.argv[i].folded === "while" || p.argv[i].folded === "until") ? "head" : undefined;
240
+ return pos.controlWords.some((i) => LOOP_CONDITION.has(p.argv[i].folded)) ? "head" : undefined;
246
241
  }
247
242
  function segmentIsEmpty(segment, p) {
248
243
  if (p.argv.length > 0 || p.redirects.length > 0 || segment.substitutions.length > 0)
@@ -263,19 +258,22 @@ function segmentFacts(segment, p, decl, table, nestedPlan, fed) {
263
258
  if (readings.length === 1)
264
259
  return readingFacts(segment, { ...p, argv: readings[0].argv }, decl, readings[0], nestedPlan, fed);
265
260
  const once = plannedOnce(nestedPlan);
266
- const each = readings.map((reading) => readingFacts(segment, { ...p, argv: reading.argv }, decl, reading, once, fed));
261
+ return readInSeveral(readings.map((reading) => readingFacts(segment, { ...p, argv: reading.argv }, decl, reading, once, fed)));
262
+ }
263
+ function readInSeveral(each) {
267
264
  const first = each[0];
268
265
  const move = each.find((f) => f.baseMove !== undefined)?.baseMove;
269
266
  return {
270
267
  ...first,
271
268
  moveProvable: false,
272
269
  ...(move !== undefined ? { baseMove: { target: typeof move.target === "string" ? move.target : { ...move.target, expands: "unresolvable" } } } : {}),
273
- readings: each,
270
+ ...(each.some((f) => f.shift !== undefined) ? { shift: { kind: "lost" } } : {}),
271
+ readings: each.flatMap((f) => f.readings ?? [f]),
274
272
  };
275
273
  }
276
274
  function readingFacts(segment, p, decl, reading, nestedPlan, fed) {
277
275
  const table = reading.table;
278
- const planner = (text, kind, at) => nestedPlan(text, kind, kind === "later" ? { ...at, floors: reading.floors } : at);
276
+ const planner = (text, kind, at) => nestedPlan(text, kind, kind !== "substitution" ? { ...at, floors: reading.floors } : at);
279
277
  const facts = segmentFactsRead(segment, p, decl, table, planner, fed, reading.reads);
280
278
  const unresolvedRaw = new Set();
281
279
  for (const t of segment.tokens) {
@@ -297,11 +295,13 @@ function readingFacts(segment, p, decl, reading, nestedPlan, fed) {
297
295
  return [unsettle(one), ...shadows];
298
296
  };
299
297
  const move = facts.baseMove;
298
+ const shift = facts.shift;
300
299
  return {
301
300
  ...facts,
302
301
  reads: facts.reads.flatMap(each),
303
302
  redirects: facts.redirects.map((r) => (r.target === undefined ? r : { ...r, target: unsettle(r.target) })),
304
303
  ...(move !== undefined && typeof move.target !== "string" ? { baseMove: { target: unsettle(move.target) } } : {}),
304
+ ...(shift?.kind === "cd" && typeof shift.target !== "string" ? { shift: { ...shift, target: unsettle(shift.target) } } : {}),
305
305
  };
306
306
  }
307
307
  function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
@@ -343,6 +343,7 @@ function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
343
343
  }
344
344
  if (unreadableHeredoc)
345
345
  nested.push({ segments: [], unreadable: "a here-document body carries a substitution this lexer cannot delimit", shape: { grouped: false, backgrounded: false, strayRedirection: false } });
346
+ const shellSide = new Set(nested);
346
347
  if (isArithmeticCommand(segment))
347
348
  ctx.reentered.push(...arithmeticSubstitutions(p.argv.map((w) => w.folded).join(" "), table));
348
349
  for (const t of segment.tokens) {
@@ -365,26 +366,31 @@ function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
365
366
  };
366
367
  const done = (f) => {
367
368
  const reentered = [...new Set(ctx.reentered)];
368
- for (const body of reentered)
369
- nested.push(nestedPlan(body, "later"));
369
+ for (const body of reentered) {
370
+ const plan = nestedPlan(body, "later");
371
+ nested.push(plan);
372
+ shellSide.add(plan);
373
+ }
370
374
  return {
371
375
  ...base,
372
376
  ...f,
373
377
  ...(reentered.length > 0 ? { reentry: "shell" } : {}),
374
378
  ...(f.unreadProgramText === undefined && ctx.programText !== undefined ? { unreadProgramText: ctx.programText } : {}),
375
379
  ...(elsewhere.size > 0 ? { elsewhere } : {}),
380
+ ...(shellSide.size > 0 ? { shellSide } : {}),
376
381
  };
377
382
  };
378
383
  const head = toks[0]?.folded;
379
- if (isShellSyntaxWord(p.argv[0], COPROC) && toks.length > 1) {
380
- const tail = (from) => ({ ...p, argv: p.argv.slice(from) });
381
- const first = segmentFacts(segment, tail(1), decl, table, nestedPlan, fed);
382
- if (toks.length < 3)
383
- return { ...first, backgrounds: true };
384
- const second = segmentFacts(segment, tail(2), decl, table, nestedPlan, fed);
385
- const seen = new Set(first.nested.map((n) => JSON.stringify(n.segments.map((sg) => sg.text))));
386
- const extraNested = second.nested.filter((n) => !seen.has(JSON.stringify(n.segments.map((sg) => sg.text))));
387
- return { ...first, ...(second.reentry === "shell" ? { reentry: second.reentry } : {}), reads: [...first.reads, ...second.reads], nested: [...first.nested, ...extraNested], backgrounds: true };
384
+ const pos = readProgramPosition(p.argv);
385
+ const coproc = pos.written !== undefined && isShellSyntaxWord(p.argv[pos.written], COPROC) ? pos.written : undefined;
386
+ if (coproc !== undefined && toks.length > coproc + 1) {
387
+ const once = plannedOnce(nestedPlan);
388
+ const tail = (from) => ({ ...p, argv: p.argv.slice(coproc + from) });
389
+ const background = (f) => ({ ...f, backgrounds: true, ...(f.readings !== undefined ? { readings: f.readings.map((g) => ({ ...g, backgrounds: true })) } : {}) });
390
+ const first = segmentFacts(segment, tail(1), decl, table, once, fed);
391
+ if (toks.length < coproc + 3 || isShellSyntaxWord(p.argv[coproc + 1], RESERVED) || readProgramPosition(tail(2).argv.slice(0, 1)).refusal !== undefined)
392
+ return background(first);
393
+ return background(readInSeveral([first, segmentFacts(segment, tail(2), decl, table, once, fed)]));
388
394
  }
389
395
  const loop = loopShapeOf(p);
390
396
  if (loopListHeadAt(p.argv) !== undefined)
@@ -398,7 +404,6 @@ function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
398
404
  }
399
405
  return done({ empty: false, launcherChain: [], declines: [], reads, moveProvable: false });
400
406
  }
401
- const pos = readProgramPosition(p.argv);
402
407
  const written = pos.written;
403
408
  if (written === undefined)
404
409
  return done({ empty: segmentIsEmpty(segment, p), launcherChain: [], declines: [], reads, moveProvable: false });
@@ -482,6 +487,7 @@ function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
482
487
  }
483
488
  }
484
489
  let reentry;
490
+ let shift;
485
491
  if (pos.argvFromStdin)
486
492
  reentry = "argv";
487
493
  const reenterShell = (verb, argvs) => {
@@ -500,7 +506,7 @@ function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
500
506
  script(reentryText(shell, t, s.literal), user);
501
507
  }
502
508
  for (const { text, ...at } of scripts.values()) {
503
- const plan = nestedPlan(text, "later", at);
509
+ const plan = nestedPlan(text, "now", at);
504
510
  nested.push(plan);
505
511
  if (at.as?.login === true)
506
512
  elsewhere.add(plan);
@@ -511,14 +517,14 @@ function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
511
517
  if (file !== undefined)
512
518
  reads.push(bare(file, "operand"));
513
519
  }
514
- return done({ empty: false, launcherChain, verb, reentry: "shell", declines, reads, moveProvable: false, ...(loopHead !== undefined ? { loopHead } : {}), ...(stdin.length > 0 ? { stdin } : {}) });
520
+ return done({ empty: false, launcherChain, verb, reentry: "shell", declines, reads, moveProvable: false, ...(loopHead !== undefined ? { loopHead } : {}), ...(stdin.length > 0 ? { stdin } : {}), ...(shift !== undefined ? { shift } : {}) });
515
521
  };
516
522
  if (pos.refusal !== undefined) {
517
523
  const owner = programRow(programBasename(pos.refusal.owner));
518
524
  if (pos.refusal.kind === "option_at_program_position" && owner !== undefined && commandLineOptionSpellings(owner).length > 0) {
519
525
  const text = commandStringValue(owner, toks, pos.refusal.option);
520
526
  if (text !== undefined) {
521
- nested.push(nestedPlan(text.value, "later"));
527
+ nested.push(nestedPlan(text.value, "now"));
522
528
  reentry = "shell";
523
529
  unlistedWords(reads, toks, text.next, launcherIndices, "unlisted_word", ctx);
524
530
  return done({ empty: false, launcherChain, ...(verb !== undefined ? { verb } : {}), reentry, declines, reads, moveProvable: false, ...(loopHead !== undefined ? { loopHead } : {}) });
@@ -551,6 +557,12 @@ function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
551
557
  const programRead = toks[program.index];
552
558
  if (rewritten(programRead) || isPathShapedToken(programRead.folded))
553
559
  reads.push(bare(programRead, "program_path"));
560
+ if (rewritten(programRead) && !programRead.word.pieces.some((p) => p.literal?.includes("/") === true))
561
+ shift = { kind: "lost" };
562
+ if (basename === "eval")
563
+ shift = argReads.every((r) => literalTextOf(r.word.pieces) !== undefined) && reentryText("eval", texts) !== undefined ? { kind: "eval", text: reentryText("eval", texts) } : { kind: "lost" };
564
+ if (basename === "source" || basename === ".")
565
+ shift = { kind: "lost" };
554
566
  if (runsShellText(basename))
555
567
  return reenterShell(verb, [{ shell: basename, texts, args, words: argReads }]);
556
568
  const row = programRow(basename);
@@ -559,11 +571,16 @@ function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
559
571
  reentry = "interpreter";
560
572
  const moveProvable = pos.controlWords.length === 0 && pos.launchers.length === 0 && pos.assignments.length === 0 && program.name === basename;
561
573
  let baseMove;
574
+ const moving = baseShiftOf(basename, argReads, (r) => bare(r, "cd_target"));
575
+ if (moving !== undefined)
576
+ shift = moving.shift;
577
+ if (moving?.reported !== undefined)
578
+ baseMove = { target: moving.reported };
562
579
  if (basename === "cd") {
563
- baseMove = cdMove(argReads, reads);
580
+ if (typeof baseMove?.target === "object")
581
+ reads.push(baseMove.target);
564
582
  }
565
- else if (movesBaseUnfollowed(basename)) {
566
- baseMove = { target: "unmodelled" };
583
+ else if (movesBaseByStack(basename)) {
567
584
  unlistedWords(reads, toks, program.index, launcherIndices, "unlisted_word", ctx, program);
568
585
  }
569
586
  else if (listed) {
@@ -631,6 +648,7 @@ function segmentFactsRead(segment, p, decl, table, nestedPlan, fed, toks) {
631
648
  declines,
632
649
  reads,
633
650
  ...(baseMove !== undefined ? { baseMove } : {}),
651
+ ...(shift !== undefined ? { shift } : {}),
634
652
  moveProvable,
635
653
  ...(loopHead !== undefined ? { loopHead } : {}),
636
654
  });
@@ -648,10 +666,7 @@ function dedupe(reads) {
648
666
  return out;
649
667
  }
650
668
  function relativeSpelling(spelled) {
651
- return !(isAbsolutePathForm(spelled) || spelled.startsWith("~"));
652
- }
653
- function joinBase(base, target, homeDir) {
654
- return resolveOperandLexically(base, target, homeDir);
669
+ return !(isAbsoluteForFamily("own", spelled) || spelled.startsWith("~"));
655
670
  }
656
671
  function redirectFact(r, table, homeDir) {
657
672
  if (r.target === undefined)
@@ -692,13 +707,3 @@ function literalBeats(toks) {
692
707
  return undefined;
693
708
  return words.length <= POLL_LOOP_MAX_BEATS ? words.length : undefined;
694
709
  }
695
- function cdMove(args, reads) {
696
- const target = args.find((t) => !t.folded.startsWith("-") || t.folded === "-");
697
- if (target === undefined)
698
- return { target: "home" };
699
- if (target.folded === "-")
700
- return { target: "previous" };
701
- const c = bare(target, "cd_target");
702
- reads.push(c);
703
- return { target: c };
704
- }