@sema-agent/core 7.48.0 → 7.49.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (421) hide show
  1. package/CHANGELOG.md +457 -0
  2. package/README.md +2 -2
  3. package/dist/agents/cascade.d.ts +2 -5
  4. package/dist/agents/child-failure.d.ts +20 -0
  5. package/dist/agents/child-failure.js +28 -0
  6. package/dist/agents/cross-session-ref.d.ts +11 -3
  7. package/dist/agents/cross-session-ref.js +6 -0
  8. package/dist/agents/cumulative-stats.d.ts +12 -15
  9. package/dist/agents/cumulative-stats.js +1 -1
  10. package/dist/agents/extra-tool-names.js +1 -1
  11. package/dist/agents/inherited-tool-pool.d.ts +17 -13
  12. package/dist/agents/inherited-tool-pool.js +4 -3
  13. package/dist/agents/peer-session-drain.d.ts +3 -2
  14. package/dist/agents/peer-session-drain.js +5 -4
  15. package/dist/agents/send-message-tool.js +2 -9
  16. package/dist/agents/subagent.d.ts +10 -29
  17. package/dist/agents/subagent.js +65 -53
  18. package/dist/agents/suspend-guard.d.ts +1 -1
  19. package/dist/agents/tool-filter.d.ts +76 -65
  20. package/dist/agents/tool-filter.js +122 -92
  21. package/dist/agents/verify.d.ts +4 -7
  22. package/dist/agents/verify.js +6 -7
  23. package/dist/bench/metrics.d.ts +1 -2
  24. package/dist/brain/anthropic.js +133 -79
  25. package/dist/brain/errors.d.ts +53 -6
  26. package/dist/brain/errors.js +22 -2
  27. package/dist/brain/input-too-long.d.ts +40 -19
  28. package/dist/brain/input-too-long.js +3 -16
  29. package/dist/brain/open-responses.js +19 -10
  30. package/dist/brain/openai.js +123 -32
  31. package/dist/brain/provider-dialect.d.ts +214 -0
  32. package/dist/brain/provider-dialect.js +186 -0
  33. package/dist/brain/request-params.d.ts +27 -8
  34. package/dist/brain/request-params.js +7 -12
  35. package/dist/brain/request-route.d.ts +84 -0
  36. package/dist/brain/request-route.js +41 -0
  37. package/dist/brain/retry.d.ts +8 -0
  38. package/dist/brain/retry.js +10 -4
  39. package/dist/brain/route-adjudicator.d.ts +7 -0
  40. package/dist/brain/route-adjudicator.js +5 -1
  41. package/dist/brain/status-sink.d.ts +20 -0
  42. package/dist/brain/status-sink.js +11 -0
  43. package/dist/brain/stream-engine.d.ts +52 -1
  44. package/dist/brain/stream-engine.js +81 -24
  45. package/dist/brain/stream-shared.d.ts +57 -11
  46. package/dist/brain/stream-shared.js +28 -2
  47. package/dist/core/a2a.d.ts +1 -0
  48. package/dist/core/agent-config-dir.js +3 -2
  49. package/dist/core/allow-entry.d.ts +21 -12
  50. package/dist/core/allow-entry.js +38 -31
  51. package/dist/core/arg-summary.d.ts +21 -28
  52. package/dist/core/arg-summary.js +235 -88
  53. package/dist/core/ask-origin.d.ts +42 -24
  54. package/dist/core/ask-origin.js +18 -13
  55. package/dist/core/auto-mode-prompt-assets.d.ts +2 -2
  56. package/dist/core/auto-mode-prompt-assets.js +1 -1
  57. package/dist/core/auto-mode-prompt.d.ts +11 -1
  58. package/dist/core/auto-mode-prompt.js +13 -10
  59. package/dist/core/auto-mode-rebuild.d.ts +7 -0
  60. package/dist/core/auto-mode-rebuild.js +4 -1
  61. package/dist/core/canonical-json.d.ts +3 -0
  62. package/dist/core/canonical-json.js +2 -1
  63. package/dist/core/checkpoint-requirements.d.ts +892 -0
  64. package/dist/core/checkpoint-requirements.js +333 -0
  65. package/dist/core/checkpoint-store.d.ts +94 -65
  66. package/dist/core/checkpoint-store.js +14 -26
  67. package/dist/core/child-clamp-seats.d.ts +228 -69
  68. package/dist/core/child-clamp-seats.js +211 -17
  69. package/dist/core/classifier-unavailable-arm.d.ts +5 -1
  70. package/dist/core/classifier-unavailable-arm.js +3 -3
  71. package/dist/core/compliance.d.ts +3 -0
  72. package/dist/core/compliance.js +9 -4
  73. package/dist/core/credential-vocabulary.d.ts +140 -0
  74. package/dist/core/credential-vocabulary.js +131 -0
  75. package/dist/core/decision-receipt.js +1 -1
  76. package/dist/core/effective-path-target.d.ts +73 -16
  77. package/dist/core/effective-path-target.js +63 -33
  78. package/dist/core/engine-notice.d.ts +43 -11
  79. package/dist/core/file-history-store.js +2 -2
  80. package/dist/core/fs-write-gate-policy.d.ts +4 -0
  81. package/dist/core/fs-write-gate-policy.js +22 -5
  82. package/dist/core/gate-bounded-await.d.ts +28 -13
  83. package/dist/core/gate-bounded-await.js +41 -4
  84. package/dist/core/gate-fold.d.ts +1 -1
  85. package/dist/core/gate-fold.js +24 -25
  86. package/dist/core/gate-lanes.js +16 -15
  87. package/dist/core/gate-outcome.d.ts +20 -5
  88. package/dist/core/gate-outcome.js +5 -0
  89. package/dist/core/governance-codes.d.ts +11 -4
  90. package/dist/core/governance-codes.js +14 -3
  91. package/dist/core/hooks.d.ts +21 -0
  92. package/dist/core/locked-config.d.ts +8 -0
  93. package/dist/core/locked-config.js +14 -2
  94. package/dist/core/lsp.js +3 -5
  95. package/dist/core/mailbox-store.d.ts +3 -4
  96. package/dist/core/mcp-connection-pool.d.ts +75 -0
  97. package/dist/core/mcp-connection-pool.js +128 -0
  98. package/dist/core/mcp-failure.d.ts +2 -1
  99. package/dist/core/mcp-failure.js +13 -2
  100. package/dist/core/mcp.d.ts +4 -0
  101. package/dist/core/mcp.js +70 -43
  102. package/dist/core/memory-engine/content-origin.js +2 -14
  103. package/dist/core/memory-engine/engine.d.ts +32 -22
  104. package/dist/core/memory-engine/engine.js +103 -55
  105. package/dist/core/memory-engine/file-backend.d.ts +13 -30
  106. package/dist/core/memory-engine/file-backend.js +37 -252
  107. package/dist/core/memory-engine/index.d.ts +1 -1
  108. package/dist/core/memory-engine/index.js +1 -1
  109. package/dist/core/memory-engine/layout.d.ts +139 -9
  110. package/dist/core/memory-engine/layout.js +350 -7
  111. package/dist/core/memory-engine/scope-contract.d.ts +10 -0
  112. package/dist/core/memory-engine/scope-contract.js +8 -0
  113. package/dist/core/memory-engine/session-incarnation.d.ts +14 -4
  114. package/dist/core/memory-engine/session-incarnation.js +5 -2
  115. package/dist/core/memory-engine/types.d.ts +10 -1
  116. package/dist/core/memory.js +5 -17
  117. package/dist/core/model-seat.d.ts +4 -4
  118. package/dist/core/model-seat.js +1 -1
  119. package/dist/core/mounted-mcp.d.ts +57 -0
  120. package/dist/core/mounted-mcp.js +19 -0
  121. package/dist/core/namespaced-rule-name.d.ts +29 -0
  122. package/dist/core/namespaced-rule-name.js +52 -0
  123. package/dist/core/park-selfcheck.d.ts +1 -1
  124. package/dist/core/park-selfcheck.js +16 -6
  125. package/dist/core/path-guard-rows.d.ts +99 -0
  126. package/dist/core/path-guard-rows.js +45 -0
  127. package/dist/core/path-identity.d.ts +22 -16
  128. package/dist/core/path-identity.js +10 -10
  129. package/dist/core/path-rule-binding.d.ts +2 -1
  130. package/dist/core/path-rule-binding.js +3 -1
  131. package/dist/core/pause-registry.d.ts +16 -2
  132. package/dist/core/pause-registry.js +6 -0
  133. package/dist/core/permission-rule-consent.js +0 -7
  134. package/dist/core/permission-rule-model.d.ts +68 -24
  135. package/dist/core/permission-rule-model.js +105 -24
  136. package/dist/core/permission-rule-store.js +2 -2
  137. package/dist/core/permission-rules.d.ts +3 -30
  138. package/dist/core/permission-rules.js +4 -54
  139. package/dist/core/read-root-candidate.d.ts +1 -3
  140. package/dist/core/read-root-candidate.js +4 -15
  141. package/dist/core/real-approval.d.ts +4 -3
  142. package/dist/core/retired-keys.d.ts +13 -0
  143. package/dist/core/retired-keys.js +14 -0
  144. package/dist/core/roles.d.ts +2 -3
  145. package/dist/core/roles.js +15 -6
  146. package/dist/core/runner/assemble-result.d.ts +0 -7
  147. package/dist/core/runner/assemble-result.js +19 -31
  148. package/dist/core/runner/caller-roster.d.ts +78 -0
  149. package/dist/core/runner/caller-roster.js +219 -0
  150. package/dist/core/runner/checkpoint-scope.d.ts +16 -16
  151. package/dist/core/runner/checkpoint-scope.js +2 -2
  152. package/dist/core/runner/child-tree.js +2 -2
  153. package/dist/core/runner/contracts.d.ts +68 -35
  154. package/dist/core/runner/decide-continuation.d.ts +51 -3
  155. package/dist/core/runner/decide-continuation.js +76 -21
  156. package/dist/core/runner/denial-limit-arms.d.ts +17 -5
  157. package/dist/core/runner/denial-limit-arms.js +11 -9
  158. package/dist/core/runner/extra-body-dropped-seat.d.ts +8 -0
  159. package/dist/core/runner/extra-body-dropped-seat.js +22 -0
  160. package/dist/core/runner/finish-reason-seat.d.ts +9 -0
  161. package/dist/core/runner/finish-reason-seat.js +40 -0
  162. package/dist/core/runner/gate-exit.d.ts +44 -32
  163. package/dist/core/runner/gate-exit.js +112 -25
  164. package/dist/core/runner/inherited-ask-grants.js +6 -4
  165. package/dist/core/runner/mcp-redial.d.ts +3 -3
  166. package/dist/core/runner/mcp-redial.js +3 -4
  167. package/dist/core/runner/permission-rule-lanes.d.ts +1 -2
  168. package/dist/core/runner/permission-rule-lanes.js +5 -4
  169. package/dist/core/runner/prepare-artifact.d.ts +4 -0
  170. package/dist/core/runner/prepare-artifact.js +2 -1
  171. package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
  172. package/dist/core/runner/prepare-ask-lane.js +12 -6
  173. package/dist/core/runner/prepare-boundary-parks.d.ts +5 -8
  174. package/dist/core/runner/prepare-boundary-parks.js +4 -7
  175. package/dist/core/runner/prepare-caps-and-workflow.d.ts +11 -15
  176. package/dist/core/runner/prepare-caps-and-workflow.js +19 -27
  177. package/dist/core/runner/prepare-config-doors.d.ts +9 -9
  178. package/dist/core/runner/prepare-config-doors.js +10 -5
  179. package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
  180. package/dist/core/runner/prepare-defer-classify.js +5 -3
  181. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
  182. package/dist/core/runner/prepare-delegation-surface.js +3 -3
  183. package/dist/core/runner/prepare-gate-stations.d.ts +4 -4
  184. package/dist/core/runner/prepare-gate-stations.js +9 -7
  185. package/dist/core/runner/prepare-hands-readface.d.ts +7 -7
  186. package/dist/core/runner/prepare-hands-readface.js +7 -7
  187. package/dist/core/runner/prepare-inherited-gate.d.ts +7 -6
  188. package/dist/core/runner/prepare-inherited-gate.js +14 -15
  189. package/dist/core/runner/prepare-listings.d.ts +7 -24
  190. package/dist/core/runner/prepare-listings.js +4 -5
  191. package/dist/core/runner/prepare-memory-engine-session.d.ts +2 -0
  192. package/dist/core/runner/prepare-memory-engine-session.js +4 -3
  193. package/dist/core/runner/prepare-memory.d.ts +6 -0
  194. package/dist/core/runner/prepare-memory.js +13 -13
  195. package/dist/core/runner/prepare-offload-wrappers.d.ts +5 -0
  196. package/dist/core/runner/prepare-offload-wrappers.js +3 -3
  197. package/dist/core/runner/prepare-park-ask.d.ts +7 -8
  198. package/dist/core/runner/prepare-park-ask.js +17 -10
  199. package/dist/core/runner/prepare-policy-chain.js +24 -44
  200. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  201. package/dist/core/runner/prepare-project-context.js +3 -4
  202. package/dist/core/runner/prepare-protocol-tools.d.ts +9 -3
  203. package/dist/core/runner/prepare-protocol-tools.js +3 -4
  204. package/dist/core/runner/prepare-run-refs.d.ts +3 -0
  205. package/dist/core/runner/prepare-run-refs.js +2 -1
  206. package/dist/core/runner/prepare-safety-scan.d.ts +4 -8
  207. package/dist/core/runner/prepare-safety-scan.js +1 -2
  208. package/dist/core/runner/prepare-suspend-saga.d.ts +7 -1
  209. package/dist/core/runner/prepare-suspend-saga.js +28 -12
  210. package/dist/core/runner/prepare-task.d.ts +1 -1
  211. package/dist/core/runner/prepare-task.js +67 -56
  212. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -10
  213. package/dist/core/runner/prepare-wiring-manifest.js +5 -8
  214. package/dist/core/runner/resume-apply.js +7 -14
  215. package/dist/core/runner/resume-checkpoint-screen.js +11 -4
  216. package/dist/core/runner/resume-internals-and-config.js +2 -2
  217. package/dist/core/runner/resume-policy-outcome.js +5 -32
  218. package/dist/core/runner/resume-review-outcome.js +4 -1
  219. package/dist/core/runner/retired-runner-deps.d.ts +6 -0
  220. package/dist/core/runner/retired-runner-deps.js +10 -1
  221. package/dist/core/runner/run-brain-sinks.d.ts +4 -0
  222. package/dist/core/runner/run-brain-sinks.js +5 -2
  223. package/dist/core/runner/run-harness-handlers.js +29 -36
  224. package/dist/core/runner/run-leg.d.ts +0 -2
  225. package/dist/core/runner/run-leg.js +32 -33
  226. package/dist/core/runner/run-terminal-adoption.js +4 -3
  227. package/dist/core/runner/runtask.js +8 -8
  228. package/dist/core/runner/session-rule-policy.js +5 -5
  229. package/dist/core/runner/stream-settle-backstop.js +7 -4
  230. package/dist/core/runner/sub-agent-deny-lists.d.ts +51 -12
  231. package/dist/core/runner/sub-agent-deny-lists.js +111 -80
  232. package/dist/core/runner/terminal-account.d.ts +48 -0
  233. package/dist/core/runner/terminal-account.js +186 -0
  234. package/dist/core/runner/tool-end-body.d.ts +54 -17
  235. package/dist/core/runner/tool-end-body.js +27 -2
  236. package/dist/core/runner/tool-face-overlay.d.ts +3 -1
  237. package/dist/core/runner/tool-face-overlay.js +6 -4
  238. package/dist/core/runner/tool-occurrence.d.ts +106 -0
  239. package/dist/core/runner/tool-occurrence.js +123 -0
  240. package/dist/core/runner/tool-progress-lane.d.ts +16 -8
  241. package/dist/core/runner/tool-progress-lane.js +25 -34
  242. package/dist/core/runner/tool-turn-repair-seat.d.ts +28 -4
  243. package/dist/core/runner/tool-turn-repair-seat.js +36 -13
  244. package/dist/core/runner/turn-attachments.d.ts +1 -1
  245. package/dist/core/runner/write-protection-station.d.ts +21 -9
  246. package/dist/core/runner/write-protection-station.js +20 -17
  247. package/dist/core/runner-deps.d.ts +12 -22
  248. package/dist/core/secret-env.d.ts +16 -5
  249. package/dist/core/secret-env.js +23 -63
  250. package/dist/core/sensitive-path-policy.d.ts +19 -18
  251. package/dist/core/sensitive-path-policy.js +73 -49
  252. package/dist/core/session-reconcile.d.ts +3 -0
  253. package/dist/core/session-reconcile.js +5 -4
  254. package/dist/core/session-store.d.ts +5 -0
  255. package/dist/core/session-store.js +12 -0
  256. package/dist/core/session.d.ts +13 -0
  257. package/dist/core/shared-memory/normalize.d.ts +17 -4
  258. package/dist/core/shared-memory/normalize.js +7 -7
  259. package/dist/core/shell-lexer.js +5 -4
  260. package/dist/core/shell-wrapper-projection.js +1 -1
  261. package/dist/core/skill-grant.js +1 -1
  262. package/dist/core/store-contracts/checkpoint-store-contract.js +13 -0
  263. package/dist/core/store-contracts/mailbox-store-contract.d.ts +7 -14
  264. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +27 -0
  265. package/dist/core/store-contracts/session-store-incarnation-contract.js +92 -0
  266. package/dist/core/stub-env.d.ts +2 -0
  267. package/dist/core/stub-env.js +1 -0
  268. package/dist/core/synthetic-read-verdict.js +2 -2
  269. package/dist/core/task-event.d.ts +59 -5
  270. package/dist/core/task-result.d.ts +1 -1
  271. package/dist/core/task-spec.d.ts +42 -9
  272. package/dist/core/terminal-cause.d.ts +94 -11
  273. package/dist/core/tighten-task-spec.d.ts +5 -4
  274. package/dist/core/tighten-task-spec.js +20 -25
  275. package/dist/core/tool-catalog-entries.d.ts +5 -7
  276. package/dist/core/tool-catalog-entries.js +14 -15
  277. package/dist/core/tool-face.d.ts +47 -0
  278. package/dist/core/tool-face.js +5 -0
  279. package/dist/core/tool-model-gate.d.ts +5 -5
  280. package/dist/core/tool-name-aliases.d.ts +26 -2
  281. package/dist/core/tool-name-aliases.js +13 -0
  282. package/dist/core/tool-name-list.d.ts +30 -0
  283. package/dist/core/tool-name-list.js +55 -0
  284. package/dist/core/tool-name-readings.d.ts +1 -9
  285. package/dist/core/tool-name-readings.js +1 -3
  286. package/dist/core/tool-policy.d.ts +195 -85
  287. package/dist/core/tool-policy.js +165 -380
  288. package/dist/core/tool-registry.d.ts +44 -1
  289. package/dist/core/tool-registry.js +26 -0
  290. package/dist/core/tool-roster.d.ts +19 -1
  291. package/dist/core/tool-roster.js +21 -2
  292. package/dist/core/tool-spec-members.d.ts +45 -0
  293. package/dist/core/tool-spec-members.js +169 -0
  294. package/dist/core/tool-spec.d.ts +46 -8
  295. package/dist/core/tools.d.ts +6 -2
  296. package/dist/core/tools.js +8 -4
  297. package/dist/core/untrusted-egress.js +29 -26
  298. package/dist/core/untrusted-text.d.ts +13 -10
  299. package/dist/core/untrusted-text.js +6 -44
  300. package/dist/core/workflow-journal-store.d.ts +4 -1
  301. package/dist/core/write-protect.d.ts +65 -29
  302. package/dist/core/write-protect.js +235 -177
  303. package/dist/engine/compaction/compaction.d.ts +2 -2
  304. package/dist/engine/execution-env/node-execution-env.d.ts +5 -23
  305. package/dist/engine/execution-env/node-execution-env.js +16 -38
  306. package/dist/engine/execution-env/symlink-chain.d.ts +23 -0
  307. package/dist/engine/execution-env/symlink-chain.js +32 -0
  308. package/dist/engine/harness/types.d.ts +7 -0
  309. package/dist/engine/llm/tool-turns.d.ts +3 -0
  310. package/dist/engine/llm/tool-turns.js +1 -0
  311. package/dist/engine/llm/types.d.ts +59 -23
  312. package/dist/engine/loop/agent-loop.js +16 -2
  313. package/dist/engine/session/memory-repo.js +11 -3
  314. package/dist/engine/session/repo-utils.d.ts +8 -0
  315. package/dist/engine/session/repo-utils.js +13 -0
  316. package/dist/index.d.ts +20 -23
  317. package/dist/index.js +17 -20
  318. package/dist/internal/harness.d.ts +1 -1
  319. package/dist/internal/harness.js +1 -1
  320. package/dist/internal/llm.d.ts +2 -2
  321. package/dist/internal/llm.js +1 -1
  322. package/dist/orchestration/governance-baseline-validity.js +1 -1
  323. package/dist/orchestration/run-spec.js +3 -3
  324. package/dist/orchestration/run-workflow-tool.d.ts +16 -35
  325. package/dist/orchestration/run-workflow-tool.js +38 -78
  326. package/dist/orchestration/workflow-governance.d.ts +21 -25
  327. package/dist/orchestration/workflow-governance.js +38 -78
  328. package/dist/orchestration/workflow-primitives.d.ts +7 -22
  329. package/dist/orchestration/workflow-primitives.js +5 -12
  330. package/dist/orchestration/workflow-types.d.ts +1 -1
  331. package/dist/orchestration/workflow.d.ts +61 -11
  332. package/dist/orchestration/workflow.js +189 -117
  333. package/dist/scenarios/full-body.d.ts +1 -1
  334. package/dist/shell/lex.d.ts +0 -21
  335. package/dist/shell/lex.js +250 -68
  336. package/dist/shell/shell-words.d.ts +35 -0
  337. package/dist/shell/shell-words.js +65 -0
  338. package/dist/stores/file/checkpoint-store.d.ts +5 -5
  339. package/dist/stores/file/checkpoint-store.js +5 -2
  340. package/dist/stores/file/index.d.ts +0 -8
  341. package/dist/stores/file/index.js +0 -12
  342. package/dist/stores/file/session-store.d.ts +3 -0
  343. package/dist/stores/file/session-store.js +44 -19
  344. package/dist/tools/artifact/artifact-tool.js +2 -0
  345. package/dist/tools/fs/bash-program-position.d.ts +12 -4
  346. package/dist/tools/fs/bash-program-position.js +32 -9
  347. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  348. package/dist/tools/fs/bash-readonly-classifier.js +8 -7
  349. package/dist/tools/fs/containment-face.d.ts +4 -0
  350. package/dist/tools/fs/containment-face.js +3 -0
  351. package/dist/tools/fs/fs-shared.d.ts +10 -0
  352. package/dist/tools/fs/fs-shared.js +18 -0
  353. package/dist/tools/fs/fs-write.js +4 -2
  354. package/dist/tools/fs/index.d.ts +1 -1
  355. package/dist/tools/fs/program-read-model.d.ts +46 -20
  356. package/dist/tools/fs/program-read-model.js +50 -20
  357. package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
  358. package/dist/tools/fs/program-read-model.rows.js +16 -15
  359. package/dist/tools/fs/read-deny-home.d.ts +33 -47
  360. package/dist/tools/fs/read-deny-home.js +35 -49
  361. package/dist/tools/fs/read-deny.d.ts +23 -22
  362. package/dist/tools/fs/read-deny.js +114 -89
  363. package/dist/tools/fs/removal-targets.d.ts +56 -23
  364. package/dist/tools/fs/removal-targets.js +65 -87
  365. package/dist/tools/fs/safety.d.ts +29 -22
  366. package/dist/tools/fs/safety.js +17 -11
  367. package/dist/tools/fs/search.js +10 -8
  368. package/dist/tools/fs/shell-read-argv.js +1 -1
  369. package/dist/tools/fs/shell-read-bindings.d.ts +169 -37
  370. package/dist/tools/fs/shell-read-bindings.js +324 -86
  371. package/dist/tools/fs/shell-read-builtins.d.ts +35 -0
  372. package/dist/tools/fs/shell-read-builtins.js +43 -0
  373. package/dist/tools/fs/shell-read-check.d.ts +15 -1
  374. package/dist/tools/fs/shell-read-check.js +67 -43
  375. package/dist/tools/fs/shell-read-establish.js +6 -6
  376. package/dist/tools/fs/shell-read-functions.d.ts +22 -0
  377. package/dist/tools/fs/shell-read-functions.js +29 -0
  378. package/dist/tools/fs/shell-read-judge.d.ts +51 -18
  379. package/dist/tools/fs/shell-read-judge.js +40 -4
  380. package/dist/tools/fs/shell-read-plan.d.ts +12 -6
  381. package/dist/tools/fs/shell-read-plan.js +118 -113
  382. package/dist/tools/fs/shell-read-start.d.ts +29 -6
  383. package/dist/tools/fs/shell-read-start.js +16 -9
  384. package/dist/tools/fs/shell-read-word.js +4 -0
  385. package/dist/tools/fs/shell-read-workdir.d.ts +156 -0
  386. package/dist/tools/fs/shell-read-workdir.js +281 -0
  387. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -2
  388. package/dist/tools/fs/shell-read-write-targets.js +14 -5
  389. package/dist/tools/fs/shell-reentry-text.d.ts +4 -6
  390. package/dist/tools/fs/shell-reentry-text.js +3 -11
  391. package/dist/tools/task-list.d.ts +1 -2
  392. package/dist/tools/web.js +1 -1
  393. package/package.json +4 -1
  394. package/test/export-surface.snapshot.json +107 -143
  395. package/wire-changes/core@7.49.0.json +4765 -0
  396. package/dist/agents/teacher.d.ts +0 -159
  397. package/dist/agents/teacher.js +0 -577
  398. package/dist/agents/team.d.ts +0 -226
  399. package/dist/agents/team.js +0 -257
  400. package/dist/core/strategy-store.d.ts +0 -238
  401. package/dist/core/strategy-store.js +0 -253
  402. package/dist/orchestration/builtin-workflows.d.ts +0 -133
  403. package/dist/orchestration/builtin-workflows.js +0 -159
  404. package/dist/scenarios/env.d.ts +0 -40
  405. package/dist/scenarios/env.js +0 -39
  406. package/dist/scenarios/scenario-registry.d.ts +0 -110
  407. package/dist/scenarios/scenario-registry.js +0 -140
  408. package/dist/scenarios/teacher-quickstart.d.ts +0 -40
  409. package/dist/scenarios/teacher-quickstart.js +0 -12
  410. package/dist/stores/cc/lockfile.d.ts +0 -10
  411. package/dist/stores/cc/lockfile.js +0 -98
  412. package/dist/stores/cc/mailbox-store.d.ts +0 -20
  413. package/dist/stores/cc/mailbox-store.js +0 -255
  414. package/dist/stores/cc/roster-adapter.d.ts +0 -12
  415. package/dist/stores/cc/roster-adapter.js +0 -117
  416. package/dist/stores/cc/sidecar-transcript.d.ts +0 -97
  417. package/dist/stores/cc/sidecar-transcript.js +0 -264
  418. package/dist/stores/cc/task-list-store.d.ts +0 -2
  419. package/dist/stores/cc/task-list-store.js +0 -125
  420. package/dist/stores/file/strategy-store.d.ts +0 -97
  421. package/dist/stores/file/strategy-store.js +0 -341
@@ -0,0 +1,35 @@
1
+ /**
2
+ * WHETHER A BUILTIN RUNS BY ITS NAME — a binding of the same ordered thread as a variable's or a function's
3
+ * (`shell-read-bindings.ts`). `enable -n NAME` disables a builtin (the word then finds no command: "command not found"),
4
+ * `enable NAME` enables it again, and every builtin starts a text enabled. The thread keeps this the way it keeps a
5
+ * function's definition, under a key neither a variable nor a function can spell, and a reader at a segment asks the one
6
+ * ordered rule ({@link BuiltinState}) before it takes a word for the builtin of its name; this module is the vocabulary of
7
+ * that key and the reading of an `enable` into bindings.
8
+ */
9
+ import type { LexedWord } from "../../shell/lex.js";
10
+ import type { Binding, BoundValue, NameState } from "./shell-read-bindings.js";
11
+ /** What a builtin is at a reader: `enabled` — it certainly runs by its name; `disabled` — it certainly does not (`enable -n`:
12
+ * "command not found"); `maybe` — an `enable` that may not have run, a call that may have run one, one for every builtin. */
13
+ export type BuiltinState = "enabled" | "maybe" | "disabled";
14
+ /** The builtin whose words are read into the availability of the others. */
15
+ export declare const ENABLE_BUILTIN = "enable";
16
+ export declare const builtinKey: (name: string) => string;
17
+ export declare const isBuiltinKey: (name: string) => boolean;
18
+ /** Every builtin at once (`enable -f FILE` loads one over any name; a name the text computes may be any). */
19
+ export declare const BUILTINS_ALL: string;
20
+ export declare const BUILTIN_ENABLED: NameState;
21
+ export declare const isDisabledValue: (v: BoundValue) => boolean;
22
+ /** What the shell finds looking up each word of a chain it runs as builtins (`builtin` / `command` in front, then the name): the
23
+ * same reading ({@link BuiltinState}) for every word, since the chain runs no further than its first word that finds no command —
24
+ * one that certainly does not run makes the chain certainly not run, and one that may not makes it so. `ask` absent: all enabled. */
25
+ export declare function chainAvailability(words: readonly string[], ask: ((builtin: string) => BuiltinState) | undefined): BuiltinState;
26
+ /** Does an entry (or a made-unreadable name) with this name speak for `name`: no name (every name), the name itself, or —
27
+ * for a builtin's key — every builtin at once. */
28
+ export declare const speaksFor: (subject: string | undefined, name: string) => boolean;
29
+ /** The bindings an `enable [-a] [-dnps] [-f FILE] [NAME…]` makes: `enable NAME` enables each name, `enable -n NAME` disables it
30
+ * (a literal name: its key); any other switch — `-f FILE` loads a builtin over any name, `-d`, the listing switches — or a
31
+ * name the text computes may change every builtin; a bare listing (`enable`, `enable -n`) changes none. The words in front of
32
+ * the first name are read as the shell reads them: a word opening with `-` (but a lone `-`, which is a name) is switches, `--`
33
+ * ends them, and a letter outside the switches is an invalid option — the shell rejects the whole call (status 2) and nothing
34
+ * changes, whatever else the call says; a word after the first name is a name. */
35
+ export declare function enableBindings(args: readonly LexedWord[]): Binding[];
@@ -0,0 +1,43 @@
1
+ export const ENABLE_BUILTIN = "enable";
2
+ const BUILTIN_KEY_PREFIX = "[]";
3
+ export const builtinKey = (name) => BUILTIN_KEY_PREFIX + name;
4
+ export const isBuiltinKey = (name) => name.startsWith(BUILTIN_KEY_PREFIX);
5
+ export const BUILTINS_ALL = builtinKey("*");
6
+ export const BUILTIN_ENABLED = [{ spelled: "enabled" }];
7
+ const BUILTIN_DISABLED = [{ spelled: "disabled" }];
8
+ export const isDisabledValue = (v) => v.spelled === "disabled";
9
+ export function chainAvailability(words, ask) {
10
+ const states = words.map((w) => ask?.(w) ?? "enabled");
11
+ return states.includes("disabled") ? "disabled" : states.includes("maybe") ? "maybe" : "enabled";
12
+ }
13
+ export const speaksFor = (subject, name) => subject === undefined || subject === name || (subject === BUILTINS_ALL && isBuiltinKey(name));
14
+ const ENABLE_SWITCHES = "adfnps";
15
+ export function enableBindings(args) {
16
+ const computed = (w) => w.pieces.some((p) => p.expansion !== undefined || p.literal === undefined);
17
+ let disable = false;
18
+ let other = false;
19
+ let at = 0;
20
+ for (; at < args.length; at++) {
21
+ const w = args[at];
22
+ if (w.folded === "--") {
23
+ at++;
24
+ break;
25
+ }
26
+ if (computed(w) || w.folded.length < 2 || !w.folded.startsWith("-"))
27
+ break;
28
+ for (const letter of w.folded.slice(1)) {
29
+ if (!ENABLE_SWITCHES.includes(letter))
30
+ return [];
31
+ if (letter === "f")
32
+ return [{ name: BUILTINS_ALL, state: "unknown" }];
33
+ if (letter === "n")
34
+ disable = true;
35
+ else
36
+ other = true;
37
+ }
38
+ }
39
+ const names = args.slice(at);
40
+ if (other || names.some(computed))
41
+ return [{ name: BUILTINS_ALL, state: "unknown" }];
42
+ return names.map((w) => ({ name: builtinKey(w.folded), state: disable ? BUILTIN_DISABLED : BUILTIN_ENABLED }));
43
+ }
@@ -44,6 +44,9 @@ export type ShellReadRefusalDetails = NonNullable<ReturnType<typeof violationDet
44
44
  export interface ShellReadVerdict {
45
45
  readonly disposition: ShellReadDisposition;
46
46
  readonly finding: ShellReadFinding;
47
+ /** `write` ⇔ the finding is about where the command WRITES — its witness is a write target (a redirection's, or a word
48
+ * the program's row writes), judged against the write roots. Absent ⇒ a read's finding (or `vouch`). */
49
+ readonly face?: "write";
47
50
  /** `refuse`: the refusal text (`Error (<tool>): …`, the five-segment deny sentence or the containment
48
51
  * sentence). `mandated` / `ordinary`: the ACCOUNT of the finding, for the approval card's disclosure seat
49
52
  * (never the ask's own message — that is the auto-mode classifier's input). Absent for `vouch`. */
@@ -58,7 +61,7 @@ export interface ShellReadVerdict {
58
61
  /** A FACT beside the disposition, for the mode that answers a file command itself: every write target this command
59
62
  * names was established inside the boundary's WRITE roots at every seat the program writes it (the entry, where it
60
63
  * lands, or both — `WriteSeat`, shell-read-write-targets.ts), off the write-protection table and off every deployment
61
- * write guard (`ShellReadBoundary.writeProtectionJudge` / `writeTargetGuard`), no removal (`rm` / `rmdir`) targets a
64
+ * guarded write row (the leg's one write judgment, `ShellReadBoundary.writeTargetJudge`), no removal (`rm` / `rmdir`) targets a
62
65
  * write root or a directory containing one, every program the command runs is in its text (no `sed -f` file, no
63
66
  * re-entered shell, no unreadable text), and the line's order keeps each segment's establishment true
64
67
  * ({@link lineOrderPlaced}). Absent ⇒ not established — the mode does not answer; the station's own table is unchanged. */
@@ -67,6 +70,11 @@ export interface ShellReadVerdict {
67
70
  * the table's own judge, as a write tool's target is — so the command's ask carries that table's class, whichever
68
71
  * mode reads it. Absent ⇒ no target the station established is covered (or no judge is mounted). */
69
72
  readonly writeProtected?: true;
73
+ /** A FACT beside the disposition: a target this command's program WRITES was placed and the environment could not say
74
+ * where it leads ({@link EstablishedRead.unresolved}) — the reading's `target_unresolved` fact, the same one a write
75
+ * tool's unresolved target states, so the command's ask carries that class (no automatic lane answers it; the shell face
76
+ * releases it under bypassPermissions). Absent ⇒ every written target resolved, or was not placed for another reason. */
77
+ readonly targetUnresolved?: true;
70
78
  /** A FACT beside `writesPlaced`'s absence: the command is one of the mode's file commands whose writes the station would
71
79
  * have placed but for an OPTION WORD on a `cp` / `mv` segment ({@link UNPLACED_OPTIONS}: any word starting with `-` — the
72
80
  * reference implementation's rule, "with flags requires manual approval to ensure path safety"). Names the first such
@@ -149,6 +157,12 @@ export interface ShellReadStationOptions {
149
157
  /** The home views of the placement the boundary's deny seats were compiled at (read-deny-home.ts) — the Runner's. Absent,
150
158
  * the declared home alone. */
151
159
  readonly homeViews?: HomeViewSource;
160
+ /** The engine's account of a REFUSED admission, called with the call's id before the refusal is handed back to the tool
161
+ * — the one trusted channel by which a refusal after the gate reaches the call's record (the tool's result is the
162
+ * tool's own word, and a refusal must not read as a tool failure under the gate's `allowed`). Every shell tool this
163
+ * station admits (both Bash faces, Monitor) refuses through {@link ShellReadStation.admit}, so this is the one
164
+ * place to hear it. The Runner's; absent outside one. */
165
+ readonly onAdmissionRefused?: (toolCallId: string) => void;
152
166
  }
153
167
  /** What a consumer hands {@link judgeShellRead}: the command as written, the environment whose filesystem
154
168
  * answers where its words land (symlinks resolved, patterns expanded — a `NodeExecutionEnv` over a real
@@ -9,10 +9,11 @@ import { createShellReadEstablisher, establishShellReadsLexically } from "./shel
9
9
  import { declaredReadDenyHomes, homeViewsAt } from "./read-deny-home.js";
10
10
  import { programArgs } from "./shell-read-write-targets.js";
11
11
  import { programRow, selectedOptions } from "./program-read-model.js";
12
- import { allFindings, decideShellRead, denyRowAtOrBelow, foldShellReadFindings, judgeEstablishedPlan, readRootGrantCandidateOf, stationGuards, } from "./shell-read-judge.js";
12
+ import { allFindings, anyFaceGuards, decideShellRead, decideShellReadForRedirects, denyRowAtOrBelow, foldShellReadFindings, judgeEstablishedPlan, readRootGrantCandidateOf, stationGuards, writeFaceGuards, } from "./shell-read-judge.js";
13
13
  import { seatOf } from "../../core/tool-name-readings.js";
14
14
  import { normalizeReadRootCandidate } from "../../core/read-root-candidate.js";
15
15
  import { argvOf, lexShellText } from "../../shell/lex.js";
16
+ import { assertNoRetiredShellReadBoundaryKeys } from "../../core/retired-keys.js";
16
17
  function containmentDetails(paths) {
17
18
  return { type: "readonly_out_of_root", code: "readonly_out_of_root", paths };
18
19
  }
@@ -75,7 +76,7 @@ function writesPlacedOf(judged, boundary) {
75
76
  if (f.read.candidate.face !== "write")
76
77
  continue;
77
78
  const target = f.read.target;
78
- if (f.read.established === "unestablishable" || target === undefined || isPattern(f) || !withinAnyRoot(writeRoots, target))
79
+ if (f.read.established === "unestablishable" || target === undefined || isPattern(f) || f.kind !== "none" || (f.containment !== "inside" && f.containment !== "open"))
79
80
  return false;
80
81
  if (beneath && denyRowAtOrBelow(f.read, boundary, !REMOVAL_COMMANDS.has(verb)))
81
82
  return false;
@@ -97,10 +98,9 @@ function unplacedOptionsOf(judged) {
97
98
  }
98
99
  return undefined;
99
100
  }
100
- async function writeGuardsOf(judged, boundary, placed, env, signal) {
101
- const protection = boundary.writeProtectionJudge;
102
- const guard = placed ? boundary.writeTargetGuard : undefined;
103
- if (protection === undefined && guard === undefined)
101
+ async function writeGuardsOf(judged, boundary, env, signal) {
102
+ const judge = boundary.writeTargetJudge;
103
+ if (judge === undefined)
104
104
  return { unplaced: false, writeProtected: false };
105
105
  const { runs, concurrent } = runOrderOf(judged);
106
106
  const asKey = new Map();
@@ -155,25 +155,17 @@ async function writeGuardsOf(judged, boundary, placed, env, signal) {
155
155
  const dir = fold(parentOf(view));
156
156
  return writtenBy.some((paths, i) => i !== at && (i < at || concurrent) && paths.some((w) => withinAnyRoot([fold(w)], dir) || withinAnyRoot([dir], fold(w))));
157
157
  };
158
- const answer = async (ask) => {
159
- try {
160
- return (await ask()) === true;
161
- }
162
- catch {
163
- return undefined;
164
- }
165
- };
158
+ const answer = async (view, key) => (await judge(view, { ...(signal !== undefined ? { signal } : {}), ...(key ? { canonical: true } : {}) })) ?? "clear";
166
159
  const asked = [...[...asKey].map(([view, v]) => [view, v, true]), ...[...asSpelling].filter(([view]) => !landingKeys.has(view)).map(([view, v]) => [view, v, false])];
167
160
  let unplaced = false;
168
161
  let writeProtected = false;
169
162
  for (const [view, v, key] of asked) {
170
163
  if (!(await writable(view, v)) && !changedBefore(view, v.at))
171
164
  continue;
172
- const covered = protection === undefined ? false : await answer(() => protection(view, { ...(signal !== undefined ? { signal } : {}), ...(key ? { canonical: true } : {}) }));
173
- const guarded = guard === undefined ? false : await answer(() => guard(view, signal));
174
- if (covered === true)
165
+ const found = await answer(view, key);
166
+ if (found === "protected")
175
167
  writeProtected = true;
176
- if (covered !== false || guarded !== false)
168
+ if (found !== "clear")
177
169
  unplaced = true;
178
170
  if (writeProtected)
179
171
  break;
@@ -206,15 +198,21 @@ const CAUSE_SPELLING_MAX = 64;
206
198
  function fitCause(cause) {
207
199
  return cause.replace(/^"([^"]*)"/, (_m, spelling) => `"${fitMiddle(spelling, CAUSE_SPELLING_MAX)}"`);
208
200
  }
209
- function findingAccount(fold, roots, face) {
210
- const allowed = fitMiddle(roots.length > 0 ? roots.join(", ") : "(none)", ACCOUNT_PART_MAX);
201
+ function findingAccount(fold, boundary) {
211
202
  const w = fold.witness;
203
+ const writes = w?.read.candidate.face === "write";
204
+ const roots = writes ? (boundary.writeRoots ?? []) : boundary.roots;
205
+ const allowed = fitMiddle(roots.length > 0 ? roots.join(", ") : "(none)", ACCOUNT_PART_MAX);
212
206
  switch (fold.finding) {
213
207
  case "outside_roots":
214
- return `reads outside the allowed directories for this session: "${fitMiddle(w?.read.target ?? "?", ACCOUNT_PART_MAX)}" is not under ${allowed} ${NOT_AUTO_ALLOWED}`;
208
+ return `${writes ? "writes" : "reads"} outside the allowed directories for this session: "${fitMiddle(w?.read.target ?? "?", ACCOUNT_PART_MAX)}" is not under ${allowed} ${NOT_AUTO_ALLOWED}`;
215
209
  case "unestablishable":
210
+ if (writes && boundary.writeRoots === undefined)
211
+ return `this command writes "${fitMiddle(w.read.target ?? w.read.candidate.spelled, ACCOUNT_PART_MAX)}", and this session declares no directories it may write ${NOT_AUTO_ALLOWED}`;
212
+ if (writes)
213
+ return `where this command writes could not be established${w.read.cause !== undefined ? `: ${fitCause(w.read.cause)}` : ""}, so it cannot be confirmed to write only inside the allowed directories for this session (${allowed}) ${NOT_AUTO_ALLOWED}`;
216
214
  if (w?.read.cause !== undefined)
217
- return `where this command reads could not be established: ${fitCause(w.read.cause)}, so it cannot be confirmed ${face === "open" ? "not to read a path the deny list names" : `to read only inside the allowed directories for this session (${allowed})`} ${NOT_AUTO_ALLOWED}`;
215
+ return `where this command reads could not be established: ${fitCause(w.read.cause)}, so it cannot be confirmed ${boundary.face === "open" ? "not to read a path the deny list names" : `to read only inside the allowed directories for this session (${allowed})`} ${NOT_AUTO_ALLOWED}`;
218
216
  if (fold.declined?.baseMove !== undefined)
219
217
  return `"${fitMiddle(fold.declined.text.trim(), ACCOUNT_PART_MAX)}" moves the working directory somewhere this command's text does not settle ${NOT_AUTO_ALLOWED}`;
220
218
  if (fold.declined?.unreadProgramText !== undefined)
@@ -243,7 +241,21 @@ function outsidePathsOf(judged) {
243
241
  }
244
242
  return out;
245
243
  }
246
- function refusalOf(tool, fold, roots, paths, face) {
244
+ function outsideFindingsAreRedirections(judged) {
245
+ const walk = (plan) => plan.segments.every((s) => {
246
+ const redirected = new Set(s.segment.redirects.flatMap((r) => (r.direction === "out" && r.target !== undefined ? [r.target.raw] : [])));
247
+ const slotWrites = new Set(s.segment.reads.filter((c) => c.provenance === "write_redirect_target").map((c) => c.raw));
248
+ for (const f of s.findings) {
249
+ if (f.kind !== "outside_roots")
250
+ continue;
251
+ if (f.read.written === undefined || f.read.candidate.provenance !== "write_redirect_target" || !redirected.has(f.read.candidate.raw) || slotWrites.has(f.read.candidate.raw))
252
+ return false;
253
+ }
254
+ return s.nested.every(walk);
255
+ });
256
+ return walk(judged);
257
+ }
258
+ function refusalOf(tool, fold, boundary, paths) {
247
259
  const w = fold.witness;
248
260
  if (fold.finding === "deny_hit" && w?.deny !== undefined) {
249
261
  const target = w.read.target ?? w.deny.matchedView;
@@ -252,7 +264,7 @@ function refusalOf(tool, fold, roots, paths, face) {
252
264
  const sentence = `Error (${tool}): ${core} ${write ? WRITE_DENY_REFUSAL_NOTE : DENY_REFUSAL_NOTE}`;
253
265
  return { sentence, ...(write ? {} : { details: violationDetails({ code: "read_path_denied", message: sentence, target, pattern: w.deny.pattern }) }) };
254
266
  }
255
- const account = findingAccount(fold, roots, face);
267
+ const account = findingAccount(fold, boundary);
256
268
  const sentence = `Error (${tool}): ${account}. ${BASH_READONLY_CONFINEMENT_NOTE}`;
257
269
  return { sentence, details: findingDetails(fold.finding, paths) };
258
270
  }
@@ -263,42 +275,46 @@ const DENY_REFUSAL_NOTE = "The read boundary judges the paths this command names
263
275
  "a file the command reaches without naming it is judged only where a segment deny row (built in, or the deployment's " +
264
276
  "own pattern) reaches into the tree a recursive read walks.";
265
277
  function verdictOf(tool, seat, fold, judged, boundary) {
266
- const disposition = decideShellRead(fold.finding, seat);
278
+ const redirectsOnly = fold.finding === "outside_roots" && fold.unestablished !== true && outsideFindingsAreRedirections(judged);
279
+ const disposition = decideShellReadForRedirects(fold.finding, seat, redirectsOnly);
267
280
  if (disposition === "vouch")
268
281
  return { disposition, finding: fold.finding };
269
282
  const paths = outsidePathsOf(judged);
270
283
  const unestablished = fold.unestablished === true ? { unestablished: true } : {};
284
+ const writes = fold.witness?.read.candidate.face === "write" ? { face: "write" } : {};
271
285
  if (disposition === "refuse") {
272
- const r = refusalOf(tool, fold, boundary.roots, paths, boundary.face);
273
- return { disposition, finding: fold.finding, sentence: r.sentence, ...(r.details !== undefined ? { details: r.details } : {}) };
286
+ const r = refusalOf(tool, fold, boundary, paths);
287
+ return { disposition, finding: fold.finding, ...writes, sentence: r.sentence, ...(r.details !== undefined ? { details: r.details } : {}) };
274
288
  }
275
289
  const grant = fold.finding === "outside_roots" ? readRootGrantCandidateOf(judged, boundary) : undefined;
276
290
  const containment = fold.finding === "outside_roots" || fold.finding === "unestablishable";
277
291
  return {
278
292
  disposition,
279
293
  finding: fold.finding,
294
+ ...writes,
280
295
  ...unestablished,
281
- sentence: findingAccount(fold, boundary.roots, boundary.face),
296
+ sentence: findingAccount(fold, boundary),
282
297
  ...(disposition === "mandated" && containment ? { details: findingDetails(fold.finding, paths) } : {}),
283
298
  ...(grant !== undefined ? { readRootCandidate: { dir: grant.dir, ...(grant.covers !== undefined ? { covers: grant.covers } : {}), clearsThisAsk: true } } : {}),
284
299
  };
285
300
  }
286
- function declarationsOf(boundary, readers) {
287
- return { readers, cwd: boundary.cwd ?? boundary.roots[0] ?? "", roots: boundary.roots, ...(boundary.homeDir !== undefined ? { homeDir: boundary.homeDir } : {}), ...(boundary.face !== undefined ? { face: boundary.face } : {}) };
301
+ function declarationsOf(boundary, readers, cdPath) {
302
+ return { readers, cwd: boundary.cwd ?? boundary.roots[0] ?? "", roots: boundary.roots, cdPath, ...(boundary.homeDir !== undefined ? { homeDir: boundary.homeDir } : {}), ...(boundary.face !== undefined ? { face: boundary.face } : {}) };
288
303
  }
289
304
  function judgePlan(plan, established, boundary) {
290
305
  return judgeEstablishedPlan(plan, established.reads, boundary, (n) => {
291
306
  const nested = established.nested.get(n);
292
- return nested === undefined ? { plan: n, segments: [], guards: stationGuards(boundary) } : judgePlan(n, nested, boundary);
307
+ return nested === undefined ? { plan: n, segments: [], guards: stationGuards(boundary), writeGuards: writeFaceGuards(boundary) } : judgePlan(n, nested, boundary);
293
308
  });
294
309
  }
295
310
  async function applyAlsoInside(judged, boundary) {
296
311
  const alsoInside = boundary.alsoInside;
297
312
  if (alsoInside === undefined)
298
313
  return judged;
314
+ const outsideRead = (f) => f.kind === "outside_roots" && f.read.candidate.face !== "write" && f.read.target !== undefined;
299
315
  const rescued = new Set();
300
316
  for (const f of allFindings(judged)) {
301
- if (f.kind !== "outside_roots" || f.read.target === undefined || rescued.has(f.read.target))
317
+ if (!outsideRead(f) || rescued.has(f.read.target))
302
318
  continue;
303
319
  if (await alsoInside(f.read.target))
304
320
  rescued.add(f.read.target);
@@ -308,28 +324,31 @@ async function applyAlsoInside(judged, boundary) {
308
324
  const rewrite = (j) => ({
309
325
  plan: j.plan,
310
326
  guards: j.guards,
327
+ writeGuards: j.writeGuards,
311
328
  segments: j.segments.map((s) => ({
312
329
  segment: s.segment,
313
- findings: s.findings.map((f) => (f.kind === "outside_roots" && f.read.target !== undefined && rescued.has(f.read.target) ? { ...f, kind: "none", containment: "inside" } : f)),
330
+ findings: s.findings.map((f) => (outsideRead(f) && rescued.has(f.read.target) ? { ...f, kind: "none", containment: "inside" } : f)),
314
331
  nested: s.nested.map(rewrite),
315
332
  })),
316
333
  });
317
334
  return rewrite(judged);
318
335
  }
319
336
  async function runShellReadStation(tool, command, declared, seat, readers, establisher, env, signal) {
320
- const plan = planShellReads(command, declarationsOf(declared, readers));
337
+ const plan = planShellReads(command, declarationsOf(declared, readers, env.cdPath));
321
338
  const established = await establisher.establish(plan, declared, signal);
322
339
  const boundary = { ...declared, roots: established.roots };
323
340
  const judged = await applyAlsoInside(judgePlan(plan, established, boundary), boundary);
324
341
  const judgedVerdict = verdictOf(tool, seat, foldShellReadFindings(judged), judged, boundary);
325
342
  const unplaced = unplacedOptionsOf(judged);
326
343
  const placed = writesPlacedOf(judged, boundary);
327
- const guarded = judgedVerdict.disposition === "refuse" ? { unplaced: false, writeProtected: false } : await writeGuardsOf(judged, boundary, placed, env, signal);
328
- const facts = guarded.writeProtected ? { ...judgedVerdict, writeProtected: true } : judgedVerdict;
344
+ const guarded = judgedVerdict.disposition === "refuse" ? { unplaced: false, writeProtected: false } : await writeGuardsOf(judged, boundary, env, signal);
345
+ const unresolved = judgedVerdict.disposition !== "refuse" && runOrderOf(judged).runs.some((s) => s.findings.some((f) => f.read.written !== undefined && f.read.unresolved === true));
346
+ const facts = { ...judgedVerdict, ...(guarded.writeProtected ? { writeProtected: true } : {}), ...(unresolved ? { targetUnresolved: true } : {}) };
329
347
  const verdict = placed && !guarded.unplaced ? { ...facts, writesPlaced: true } : unplaced !== undefined ? { ...facts, unplacedOptions: unplaced } : facts;
330
348
  return { plan, established, judged, boundary, verdict };
331
349
  }
332
350
  export async function judgeShellRead(input) {
351
+ assertNoRetiredShellReadBoundaryKeys(input.boundary);
333
352
  const readers = input.readers ?? new Set(bashClassifyDefaultAllow());
334
353
  const run = await runShellReadStation(input.tool ?? "Bash", input.command, input.boundary, input.seat, readers, createShellReadEstablisher(input.env, input.boundary.roots, { homeViews: homeViewsAt(input.env, input.readDenyHomes === undefined ? declaredReadDenyHomes(input.env) : checkedReadDenyHomes(input.readDenyHomes)) }), input.env, input.signal);
335
354
  const offer = normalizeReadRootCandidate(run.verdict.readRootCandidate);
@@ -341,8 +360,9 @@ export async function judgeShellRead(input) {
341
360
  };
342
361
  }
343
362
  export function judgeShellReadLexically(command, boundary, seat, opts) {
363
+ assertNoRetiredShellReadBoundaryKeys(boundary);
344
364
  const readers = opts?.readers ?? new Set(bashClassifyDefaultAllow());
345
- const plan = planShellReads(command, declarationsOf(boundary, readers));
365
+ const plan = planShellReads(command, declarationsOf(boundary, readers, []));
346
366
  const established = establishShellReadsLexically(plan, boundary);
347
367
  const judged = judgePlan(plan, established, boundary);
348
368
  return verdictOf(opts?.tool ?? "Bash", seat, foldShellReadFindings(judged), judged, boundary);
@@ -393,7 +413,7 @@ export function createShellReadStation(opts) {
393
413
  const member = memberOf(call) ?? toolName;
394
414
  const seat = opts.seatOf(member);
395
415
  const boundary = opts.boundaryOf(member, undefined);
396
- if (boundary === undefined || !(stationGuards(boundary) || boundary.writeDenyMatch !== undefined))
416
+ if (boundary === undefined || !(anyFaceGuards(boundary) || boundary.writeTargetJudge !== undefined))
397
417
  return { disposition: "vouch", finding: "none" };
398
418
  const fold = { finding: "unestablishable" };
399
419
  const disposition = decideShellRead("unestablishable", seat);
@@ -411,8 +431,12 @@ export function createShellReadStation(opts) {
411
431
  if (a === undefined)
412
432
  return undefined;
413
433
  admissions.delete(toolCallId);
434
+ const refused = (r) => {
435
+ opts.onAdmissionRefused?.(toolCallId);
436
+ return r;
437
+ };
414
438
  if (a.toolName !== toolName || a.command !== command) {
415
- return errorResult(`Error (${toolName}): the command that would run is not the one the read station judged for this call — refused before the spawn; re-submit it as a fresh call.`, containmentDetails([]));
439
+ return refused(errorResult(`Error (${toolName}): the command that would run is not the one the read station judged for this call — refused before the spawn; re-submit it as a fresh call.`, containmentDetails([])));
416
440
  }
417
441
  const establisher = establisherFor(a.boundary.roots);
418
442
  const { changed } = await establisher.reestablish(a.established, a.boundary, signal);
@@ -422,13 +446,13 @@ export function createShellReadStation(opts) {
422
446
  const judged = await applyAlsoInside(judgePlan(a.established.plan, again, a.boundary), a.boundary);
423
447
  const fold = foldShellReadFindings(judged);
424
448
  if (fold.finding === "deny_hit") {
425
- const r = refusalOf(toolName, fold, a.boundary.roots, outsidePathsOf(judged));
426
- return errorResult(r.sentence, r.details);
449
+ const r = refusalOf(toolName, fold, a.boundary, outsidePathsOf(judged));
450
+ return refused(errorResult(r.sentence, r.details));
427
451
  }
428
452
  const moved = changed[0];
429
453
  const identity = { finding: "identity_changed", witness: { read: moved, kind: "none", containment: "unknown" } };
430
- const r = refusalOf(toolName, identity, a.boundary.roots, []);
431
- return errorResult(r.sentence, containmentDetails(changed.map((c) => c.target).filter((t) => t !== undefined)));
454
+ const r = refusalOf(toolName, identity, a.boundary, []);
455
+ return refused(errorResult(r.sentence, containmentDetails(changed.map((c) => c.target).filter((t) => t !== undefined))));
432
456
  };
433
457
  return { check, admit };
434
458
  }
@@ -1,4 +1,4 @@
1
- import { isAbsoluteForFamily, normalizeAbsPathLexically, pathFamilyOf } from "./safety.js";
1
+ import { anchorSpelling, normalizeAbsPathLexically, pathFamilyOf } from "./safety.js";
2
2
  import { boundaryIsTheSandbox, canonicalPathIsAuthoritative } from "../../core/remote-env.js";
3
3
  import { resolveOperandSpellings } from "./bash-readonly-classifier.js";
4
4
  import { judgedCandidatesOf, sedBackupNames } from "./shell-read-write-targets.js";
@@ -224,7 +224,7 @@ export function createShellReadEstablisher(env, readRoots, opts) {
224
224
  const entry = await env.fileInfo(at, signal);
225
225
  const link = entry.ok && entry.value.kind === "symlink" && linkHops > 0 ? await env.readLink?.(at, signal) : undefined;
226
226
  if (link?.ok === true) {
227
- const landing = isAbsoluteForFamily(family, link.value) ? link.value : joinUnder(parts.slice(0, -1).join("/") || "/", link.value);
227
+ const landing = anchorSpelling(family, parts.slice(0, -1).join("/") || "/", link.value);
228
228
  return await resolve([landing, ...[...tail].reverse()].join("/"), signal, true, linkHops - 1);
229
229
  }
230
230
  if (entry.ok || entry.error.code !== "not_found")
@@ -416,11 +416,11 @@ export function createShellReadEstablisher(env, readRoots, opts) {
416
416
  const real = canonicalKnown !== undefined ? { value: canonicalKnown, exact: true } : seat === "entry" ? await entryOf(spelling, signal) : await resolve(spelling, signal, candidate.face === "write");
417
417
  if (real === undefined) {
418
418
  if (resolverIsAuthoritative)
419
- return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads` };
419
+ return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads`, unresolved: true };
420
420
  return { candidate, established: "lexical", target: normalizeAbsPathLexically(spelling), spellings, ...(origin !== undefined ? { origin } : {}) };
421
421
  }
422
422
  if (!real.exact && real.missing !== true) {
423
- return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads` };
423
+ return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads`, unresolved: true };
424
424
  }
425
425
  return { candidate, established, target: real.value, spellings, ...(origin !== undefined ? { origin } : {}) };
426
426
  };
@@ -451,7 +451,7 @@ export function createShellReadEstablisher(env, readRoots, opts) {
451
451
  if (link?.ok !== true)
452
452
  return undefined;
453
453
  const dir = name.slice(0, name.search(/[^/]*$/));
454
- name = isAbsoluteForFamily(family, link.value) ? link.value : `${dir}${link.value}`;
454
+ name = dir === "" ? link.value : anchorSpelling(family, dir, link.value);
455
455
  }
456
456
  return undefined;
457
457
  };
@@ -558,7 +558,7 @@ export function createShellReadEstablisher(env, readRoots, opts) {
558
558
  };
559
559
  for (const [i, lexical] of lexicalReadsOf(candidate, boundary).entries()) {
560
560
  if (lexical.established !== "lexical") {
561
- out.push(lexical);
561
+ out.push(writtenAt(lexical, writtenSeats, writtenSeats.written.includes("landing") ? "landing" : "entry"));
562
562
  continue;
563
563
  }
564
564
  const folded = lexical.target;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * THE FUNCTION READING — whether a command's program word is a function of the text where a reader stands. A function is a
3
+ * binding of its name in the binding thread (`shell-read-bindings.ts`: defined by its head, unbound by an `unset`, visible
4
+ * and certain by the one ordered rule); this module asks that thread's {@link FunctionState} for the word the shell looks up
5
+ * first, and names what the answer means for the walk. The text's functions (`functions`) are only the CANDIDATE names: the
6
+ * ones its definition heads show, and the ones a literal `eval` text defines.
7
+ */
8
+ import { type LexedWord } from "../../shell/lex.js";
9
+ import { type FunctionState } from "./shell-read-bindings.js";
10
+ /** Does a command name one of the text's `functions` where a reader stands: the word the shell looks up first — the program,
11
+ * or the launcher in front of it (`command`, `nice`, … are words like any, and a function of their name runs instead) —
12
+ * `named`: it is a function's name and defined there (bash looks functions up before builtins: it shadows a builtin of the
13
+ * name); `maybe`: it may be one (the definition may not have run, the word is one the text computes, or an external
14
+ * launcher stands in front of a program word that may be); `undefined`: it is not (`builtin` / `command` skip functions).
15
+ * The whole word names a function (`x/f` included: a `/` tells a function from a path in no way). `stateOf`: what a
16
+ * function of a name is there ({@link BindingTable.functionState}) — the binding thread's one rule; absent, any
17
+ * definition may be the one (the question put before the walk, where no reader stands yet). */
18
+ export type FunctionCall = "named" | "maybe";
19
+ export declare function functionCallOf(argv: readonly LexedWord[], functions: ReadonlySet<string>, stateOf?: (name: string) => FunctionState): FunctionCall | undefined;
20
+ /** The functions a literal `eval` text defines — the shell that runs it is this one, so they stand after it like any: names
21
+ * the text's own definition heads do not show (a text the plan cannot read hands none; the names it makes unknown are all). */
22
+ export declare function evalDefinedFunctions(argvs: readonly (readonly LexedWord[])[], depth?: number): string[];
@@ -0,0 +1,29 @@
1
+ import { lexShell, literalTextOf } from "../../shell/lex.js";
2
+ import { readProgramPosition } from "./bash-program-position.js";
3
+ import { SPECIAL_BUILTINS, projectSegment } from "./shell-read-bindings.js";
4
+ import { reentryText } from "./shell-reentry-text.js";
5
+ export function functionCallOf(argv, functions, stateOf = () => "maybe") {
6
+ const pos = readProgramPosition(argv);
7
+ const headAt = pos.launchers[0]?.index ?? pos.program?.index;
8
+ if (headAt === undefined || functions.size === 0)
9
+ return undefined;
10
+ const stateOfWord = (w) => (w.pieces.some((p) => p.expansion !== undefined || p.literal === undefined) ? ([...functions].some((name) => stateOf(name) !== "absent") ? "maybe" : "absent") : functions.has(w.folded) ? stateOf(w.folded) : "absent");
11
+ const headWord = argv[headAt];
12
+ const head = stateOfWord(headWord);
13
+ if (head !== "absent")
14
+ return head === "defined" && !SPECIAL_BUILTINS.has(headWord.folded) ? "named" : "maybe";
15
+ if (pos.program === undefined || pos.launchers.length === 0 || pos.launchers.some((l) => l.name === "builtin" || l.name === "command"))
16
+ return undefined;
17
+ return stateOfWord(argv[pos.program.index]) === "absent" ? undefined : "maybe";
18
+ }
19
+ export function evalDefinedFunctions(argvs, depth = 0) {
20
+ return depth > 4 ? [] : argvs.flatMap((argv) => {
21
+ const pos = readProgramPosition(argv);
22
+ const words = pos.program?.basename === "eval" ? argv.slice(pos.program.index + 1).map((w) => literalTextOf(w.pieces)) : [];
23
+ const lexed = words.length > 0 && words.every((t) => t !== undefined) ? lexShell(reentryText("eval", words) ?? "") : undefined;
24
+ if (lexed === undefined || "parseFailure" in lexed)
25
+ return [];
26
+ const inner = lexed.segments.map((segment) => projectSegment(segment));
27
+ return [...inner.flatMap((p) => p.defines), ...evalDefinedFunctions(inner.map((p) => p.argv), depth + 1)];
28
+ });
29
+ }