@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
@@ -13,7 +13,6 @@ import { FileUsageWindowStore } from "./usage-window-store.js";
13
13
  import { FileBackgroundAgentStore } from "./background-agent-store.js";
14
14
  import { FileMailboxStore } from "./mailbox-store.js";
15
15
  import { FileRosterStore } from "../../agents/roster-store.js";
16
- import { FileStrategyStore } from "./strategy-store.js";
17
16
  import { FileFileHistoryStore } from "./file-history-store.js";
18
17
  export { FileCheckpointStore } from "./checkpoint-store.js";
19
18
  export { FileMemoryStore } from "./memory-store.js";
@@ -23,7 +22,6 @@ export { FileSessionPolicyStore } from "./session-policy-store.js";
23
22
  export { FileFileHistoryStore } from "./file-history-store.js";
24
23
  export { FileWorkflowJournalStore, MAX_JOURNAL_RESULT_BYTES, oversizeJournalResult } from "./workflow-journal-store.js";
25
24
  export { FileUsageWindowStore } from "./usage-window-store.js";
26
- export { FileStrategyStore } from "./strategy-store.js";
27
25
  export { resolveDataRoot, sanitizeScope, sanitizePathComponent, createFileConsolidationLock } from "./fs-atomic.js";
28
26
  export { FileStoreLockError } from "./fs-atomic.js";
29
27
  export { atomicWriteFile, writeThenLink, ensureDir, readJsonlRecords, AppendLog } from "./fs-atomic.js";
@@ -41,7 +39,6 @@ export class FileStorageBackend {
41
39
  usageWindowStore;
42
40
  backgroundAgentStore;
43
41
  mailboxStore;
44
- strategyStore;
45
42
  rosterStore;
46
43
  fileHistoryStore;
47
44
  consolidationLock;
@@ -84,15 +81,6 @@ export class FileStorageBackend {
84
81
  built.push(() => this.fileMailbox.close());
85
82
  this.mailboxStore = this.fileMailbox;
86
83
  this.rosterStore = new FileRosterStore(join(this.root, "roster.json"), corruptRead ?? {});
87
- const strategiesRoot = join(this.root, "strategies");
88
- this.strategyStore = new FileStrategyStore({
89
- root: strategiesRoot,
90
- ...(opts.onCorruptRead !== undefined
91
- ? {
92
- onIncident: (i) => opts.onCorruptRead?.({ kind: "file", path: i.path ?? strategiesRoot, reason: `strategy ${i.op}: ${i.error}` }),
93
- }
94
- : {}),
95
- });
96
84
  this.fileHistoryStore = new FileFileHistoryStore(this.root, { ...(opts.fileHistory ?? {}), ...(corruptRead ?? {}) });
97
85
  this.consolidationLock = createFileConsolidationLock(join(this.root, "consolidation-locks"));
98
86
  }
@@ -72,6 +72,9 @@ export declare class FileSessionRepo implements SessionRepo {
72
72
  olderThanMs?: number;
73
73
  scope?: string;
74
74
  }): Promise<PlacedSessionRow[]>;
75
+ /** The row as it stands before a file-replacing write: `undefined` ⇔ there is no row (no file); a row
76
+ * whose header cannot be read answers an empty header (it exists, its cells are unknown). */
77
+ private priorHeader;
75
78
  /** Cheap placement probe for one id (the TtlSessionStore's cold-cache release consults it so the
76
79
  * placed real-deletion obligation holds after a restart). `undefined` = missing file OR unplaced. */
77
80
  placementOf(sessionId: string): Promise<SessionPlacementRecord | undefined>;
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { BaseSessionStorage, StoredSession, SessionError, getEntriesToFork, uuidv7, validateEntriesForImport, } from "../../internal/harness.js";
3
+ import { BaseSessionStorage, StoredSession, SessionError, assertForkTarget, forkRowCells, getEntriesToFork, incarnationCell, uuidv7, validateEntriesForImport, } from "../../internal/harness.js";
4
4
  import { canonicalStoreKey, AppendLog, atomicWriteFile, ensureDir, readJsonlRecords, sanitizePathComponent } from "./fs-atomic.js";
5
5
  import { guardCorruptSink } from "./corrupt-read-sink.js";
6
6
  import { assertAdoptionBootGate } from "./adoption/marker.js";
@@ -102,10 +102,25 @@ export class FileSessionRepo {
102
102
  if (meta === undefined) {
103
103
  throw new SessionError("corrupt_session", `Session ${id} is unreadable: its log has no meta header (${tally?.dropped ?? 0} of ${tally?.total ?? lines.length} records skipped as corrupt)`);
104
104
  }
105
- const { createdAt, forkedFrom, placement } = meta;
106
- return { createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}), ...(placement !== undefined ? { placement } : {}), entries };
105
+ const { createdAt, forkedFrom, placement, incarnation, forkedFromIncarnation } = meta;
106
+ for (const [key, token] of [["incarnation", incarnation], ["forkedFromIncarnation", forkedFromIncarnation]]) {
107
+ if (token !== undefined && typeof token !== "string")
108
+ this.disclose(path, `session header carries a non-string ${key} (${typeof token}) — read as an UNKNOWN occupant`, id);
109
+ }
110
+ return {
111
+ meta: {
112
+ id,
113
+ createdAt,
114
+ ...(forkedFrom !== undefined ? { forkedFrom } : {}),
115
+ ...(placement !== undefined ? { placement } : {}),
116
+ ...(typeof incarnation === "string" ? { incarnation } : {}),
117
+ ...(typeof forkedFromIncarnation === "string" ? { forkedFromIncarnation } : {}),
118
+ },
119
+ entries,
120
+ };
107
121
  }
108
- storage(id, createdAt, entries, forkedFrom, placement) {
122
+ storage(meta, entries) {
123
+ const { id } = meta;
109
124
  const canonical = canonicalStoreKey(this.pathFor(id));
110
125
  const live = sharedSessionStorages.get(canonical)?.deref();
111
126
  if (live !== undefined) {
@@ -114,7 +129,7 @@ export class FileSessionRepo {
114
129
  return live;
115
130
  }
116
131
  const log = new AppendLog(this.pathFor(id));
117
- const created = new FileSessionStorage(log, { id, createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}), ...(placement !== undefined ? { placement } : {}) }, entries, canonical);
132
+ const created = new FileSessionStorage(log, meta, entries, canonical);
118
133
  sharedSessionStorages.set(canonical, new WeakRef(created));
119
134
  created.addHolder(this);
120
135
  this.joined.set(canonical, new WeakRef(created));
@@ -140,15 +155,16 @@ export class FileSessionRepo {
140
155
  if (!existsSync(path)) {
141
156
  evictSharedSessionStorage(canonicalStoreKey(path));
142
157
  const placement = options.placement !== undefined ? { ...options.placement, placedAt: options.placement.placedAt ?? Date.now() } : undefined;
143
- atomicWriteFile(this.tmpDir, path, `${JSON.stringify({ kind: "meta", id, createdAt, ...(placement !== undefined ? { placement } : {}) })}\n`);
144
- return new StoredSession(this.storage(id, createdAt, [], undefined, placement));
158
+ const meta = { id, createdAt, ...(placement !== undefined ? { placement } : {}), ...incarnationCell(undefined) };
159
+ atomicWriteFile(this.tmpDir, path, `${JSON.stringify({ kind: "meta", ...meta })}\n`);
160
+ return new StoredSession(this.storage(meta, []));
145
161
  }
146
- const { createdAt: existingCreatedAt, forkedFrom, placement, entries } = this.read(id);
147
- return new StoredSession(this.storage(id, existingCreatedAt || createdAt, entries, forkedFrom, placement));
162
+ const { meta, entries } = this.read(id);
163
+ return new StoredSession(this.storage({ ...meta, createdAt: meta.createdAt || createdAt }, entries));
148
164
  }
149
165
  async open(metadata) {
150
- const { createdAt, forkedFrom, placement, entries } = this.read(metadata.id);
151
- return new StoredSession(this.storage(metadata.id, createdAt, entries, forkedFrom, placement));
166
+ const { meta, entries } = this.read(metadata.id);
167
+ return new StoredSession(this.storage(meta, entries));
152
168
  }
153
169
  async list() {
154
170
  let names;
@@ -167,7 +183,7 @@ export class FileSessionRepo {
167
183
  continue;
168
184
  const id = name.slice(0, -SUFFIX.length);
169
185
  try {
170
- const { createdAt, forkedFrom, placement } = this.read(id);
186
+ const { createdAt, forkedFrom, placement } = this.read(id).meta;
171
187
  if (placement !== undefined)
172
188
  continue;
173
189
  out.push({ id, createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}) });
@@ -199,7 +215,7 @@ export class FileSessionRepo {
199
215
  const id = name.slice(0, -SUFFIX.length);
200
216
  let placement;
201
217
  try {
202
- placement = this.read(id).placement;
218
+ placement = this.read(id).meta.placement;
203
219
  }
204
220
  catch (err) {
205
221
  if (!(err instanceof SessionError && err.code === "not_found")) {
@@ -227,9 +243,17 @@ export class FileSessionRepo {
227
243
  }
228
244
  return out;
229
245
  }
246
+ priorHeader(id) {
247
+ try {
248
+ return this.read(id).meta;
249
+ }
250
+ catch (err) {
251
+ return err instanceof SessionError && err.code === "not_found" ? undefined : {};
252
+ }
253
+ }
230
254
  async placementOf(sessionId) {
231
255
  try {
232
- return this.read(sessionId).placement;
256
+ return this.read(sessionId).meta.placement;
233
257
  }
234
258
  catch {
235
259
  return undefined;
@@ -245,20 +269,20 @@ export class FileSessionRepo {
245
269
  }
246
270
  }
247
271
  async fork(sourceMetadata, options = {}) {
272
+ assertForkTarget(sourceMetadata.id, options.id);
248
273
  const source = await this.open(sourceMetadata);
249
274
  const forked = await getEntriesToFork(source.getStorage(), options);
250
275
  const id = options.id ?? uuidv7();
251
276
  const createdAt = new Date().toISOString();
252
277
  const path = this.pathFor(id);
253
- const lines = [
254
- JSON.stringify({ kind: "meta", id, createdAt, forkedFrom: sourceMetadata.id }),
255
- ];
278
+ const meta = { id, createdAt, ...forkRowCells(await source.getMetadata()) };
279
+ const lines = [JSON.stringify({ kind: "meta", ...meta })];
256
280
  for (const entry of forked) {
257
281
  lines.push(JSON.stringify({ kind: "entry", entry }));
258
282
  }
259
283
  evictSharedSessionStorage(canonicalStoreKey(path));
260
284
  atomicWriteFile(this.tmpDir, path, `${lines.join("\n")}\n`);
261
- return new StoredSession(this.storage(id, createdAt, [...forked], sourceMetadata.id));
285
+ return new StoredSession(this.storage(meta, [...forked]));
262
286
  }
263
287
  async exportEntries(sessionId) {
264
288
  return this.read(sessionId).entries;
@@ -266,9 +290,10 @@ export class FileSessionRepo {
266
290
  async importEntries(sessionId, owner, entries, options) {
267
291
  void owner;
268
292
  const validated = validateEntriesForImport(entries, options);
293
+ const prior = this.priorHeader(sessionId);
269
294
  evictSharedSessionStorage(canonicalStoreKey(this.pathFor(sessionId)));
270
295
  const createdAt = new Date().toISOString();
271
- const lines = [JSON.stringify({ kind: "meta", id: sessionId, createdAt })];
296
+ const lines = [JSON.stringify({ kind: "meta", id: sessionId, createdAt, ...incarnationCell(prior) })];
272
297
  for (const entry of validated) {
273
298
  lines.push(JSON.stringify({ kind: "entry", entry }));
274
299
  }
@@ -5,6 +5,7 @@ import { Type } from "typebox";
5
5
  import { ARTIFACT_ACTION_CONCURRENCY_SAFE, ARTIFACT_COMMIT_THROW_OUTCOME, ARTIFACT_FAILURE_RESULT_CODE, ARTIFACT_HOST_ERROR_CODE, ARTIFACT_LIST_SCOPES, ARTIFACT_PLANE_CONTENT_ORIGIN, ARTIFACT_PUBLISH_CONTEXTS, } from "../../core/artifact-host.js";
6
6
  import { ARTIFACT_TOOL_NAME, toolFace } from "../../core/tool-catalog-entries.js";
7
7
  import { resolveAsk } from "../../core/tool-policy.js";
8
+ import { approvalCardOf } from "../../core/tool-roster.js";
8
9
  import { errorResult } from "../../core/tools.js";
9
10
  import { inlineUntrusted } from "../../core/untrusted-text.js";
10
11
  import { actionDescription, artifactDescription, PARAM_TEXT, urlDescription } from "./artifact-text.js";
@@ -343,6 +344,7 @@ export function createArtifactTool(opts) {
343
344
  args: { action: "read", url },
344
345
  message: `Read a shared artifact: ${inlineUntrusted(url, 300)}${title}${version} — an artifact another person shared with this user; approving covers re-reads of it for the rest of this run.`,
345
346
  requiresRealApproval: true,
347
+ approvalCard: approvalCardOf({ toolName: ARTIFACT_TOOL_NAME }, toolFace("artifact").renderHints?.approvalCard),
346
348
  }, onAsk, ctx.signal);
347
349
  if (resolved.action !== "allow")
348
350
  return { ok: false, reason: resolved.message ?? "not approved" };
@@ -16,9 +16,8 @@ export declare const NOT_AUTO_ALLOWED = "\u2014 not auto-allowed";
16
16
  export declare function programBasename(word: string): string;
17
17
  /**
18
18
  * The `NAME=` / `NAME+=` prefix as WRITTEN in a whitespace-split token. This is NOT the position reader's grammar
19
- * — the reader reads the lexer's {@link LexedWord.assignment} fact — but the two readers that still walk the
20
- * command TEXT rather than its words (the unverifiable-delete policy and the activity summary) read it, and
21
- * they read it here so that they read one spelling. It retires with them.
19
+ * — the reader reads the lexer's {@link LexedWord.assignment} fact — but the one reader that still walks the
20
+ * command TEXT rather than its words (the activity summary) reads it here. It retires with it.
22
21
  */
23
22
  export declare const ASSIGNMENT_PREFIX: RegExp;
24
23
  /** The prefix a whitespace-split token BEGINS with, or `undefined` — the text readers' view of
@@ -30,6 +29,10 @@ export declare function writtenAssignmentPrefix(token: string): {
30
29
  readonly appends: boolean;
31
30
  readonly settlesWholeName: boolean;
32
31
  } | undefined;
32
+ /** The names a segment's COMMAND ENVIRONMENT sets for the program it starts: the assignment words in front of the
33
+ * program (the lexer's {@link LexedWord.assignment} fact, a launcher's `NAME=value` operand included) — or `all` where
34
+ * a launcher in front of it may set, clear or replace the environment (`env -u` / `-i`, `sudo`, `su`, …). */
35
+ export declare function commandEnvironmentNames(argv: readonly LexedWord[]): ReadonlySet<string> | "all";
33
36
  export { SHELL_CONTROL_PREFIXES };
34
37
  /** A control word is a keyword only when bash sees it BARE: `'if'` / `"do"` / `\\!` are command names (a program
35
38
  * looked up on PATH, whose failure changes nothing), so a quoted or escaped spelling never counts as one. */
@@ -99,6 +102,10 @@ export interface ProgramPosition {
99
102
  } | undefined;
100
103
  /** Why no program could be read; `undefined` beside `program: undefined` means the chain simply names none. */
101
104
  readonly refusal: ProgramPositionRefusal | undefined;
105
+ /** A launcher of the chain moves the directory or the root the program runs in, or where its word is looked up
106
+ * (`env -C <dir>`, `sudo -R <dir>`, `env -P <dirs>`): the program is still the word it names, but where its path
107
+ * operands resolve is not the command line's working directory — each consumer reads that as it must. */
108
+ readonly launcherMovesBase: boolean;
102
109
  }
103
110
  /**
104
111
  * Read the program position of one segment's argv words.
@@ -1,5 +1,6 @@
1
1
  import { SHELL_CONTROL_PREFIXES } from "../../shell/lex.js";
2
- import { BLOCK_WORD_NAMES, LAUNCHER_NAMES, feedsArgvFromStdin, launcherOptionStep, launcherRunsShell, programRow, selectedOptions, streamDefaultProgram } from "./program-read-model.js";
2
+ import { shellWordsOf } from "../../shell/shell-words.js";
3
+ import { BLOCK_WORD_NAMES, LAUNCHER_NAMES, feedsArgvFromStdin, launcherOptionMovesBase, launcherOptionStep, launcherRunsShell, programRow, selectedOptions, streamDefaultProgram } from "./program-read-model.js";
3
4
  export const NOT_AUTO_ALLOWED = "— not auto-allowed";
4
5
  export function programBasename(word) {
5
6
  return word.includes("/") ? (word.split("/").pop() ?? word) : word;
@@ -14,6 +15,13 @@ export function writtenAssignmentPrefix(token) {
14
15
  const bracket = written.indexOf("[");
15
16
  return { prefix, name: bracket < 0 ? written : written.slice(0, bracket), appends, settlesWholeName: !appends && bracket < 0 };
16
17
  }
18
+ const ENVIRONMENT_KEEPING_LAUNCHERS = new Set(["nice", "nohup", "time", "timeout", "stdbuf", "ionice", "chrt", "taskset", "command", "builtin", "caffeinate"]);
19
+ export function commandEnvironmentNames(argv) {
20
+ const pos = readProgramPosition(argv);
21
+ if (pos.launchers.some((l) => !ENVIRONMENT_KEEPING_LAUNCHERS.has(l.name)))
22
+ return "all";
23
+ return new Set(argv.slice(0, pos.program?.index ?? argv.length).flatMap((w) => (w.assignment?.bare === true ? [w.assignment.name] : [])));
24
+ }
17
25
  export { SHELL_CONTROL_PREFIXES };
18
26
  export function isBareControlWord(words, i, set) {
19
27
  return isShellSyntaxWord(words[i], set);
@@ -21,7 +29,7 @@ export function isBareControlWord(words, i, set) {
21
29
  export function isShellSyntaxWord(w, set) {
22
30
  return w !== undefined && w.atCommandPosition && w.raw === w.folded && set.has(w.folded);
23
31
  }
24
- export const LOOP_LIST_HEADS = new Set(["for", "select"]);
32
+ export const LOOP_LIST_HEADS = shellWordsOf("loop_list_head");
25
33
  export function isLeadingAssignmentWord(words, i) {
26
34
  const w = words[i];
27
35
  return w !== undefined && w.assignment !== undefined && w.assignment.bare && w.atCommandPosition;
@@ -30,7 +38,7 @@ function launcherRowOf(name) {
30
38
  const row = programRow(name);
31
39
  return row?.launcher !== undefined ? row : undefined;
32
40
  }
33
- function pastLauncherOptions(launcher, folded, i) {
41
+ function pastLauncherOptions(launcher, folded, i, moved) {
34
42
  const row = launcherRowOf(launcher);
35
43
  if (row === undefined)
36
44
  return i;
@@ -47,6 +55,8 @@ function pastLauncherOptions(launcher, folded, i) {
47
55
  return i;
48
56
  if (step === 2 && folded[i + 1] === undefined)
49
57
  return i;
58
+ if (launcherOptionMovesBase(row, tok))
59
+ moved?.();
50
60
  i += step;
51
61
  }
52
62
  }
@@ -60,7 +70,7 @@ export function readProgramPosition(words) {
60
70
  controlWords.push(i);
61
71
  i = pastLauncherOptions(controlWord, folded, i + 1);
62
72
  }
63
- const none = (written, assignments, refusal) => ({ controlWords, written, assignments, launchers: [], argvFromStdin: false, runsDefaultProgram: false, program: undefined, refusal });
73
+ const none = (written, assignments, refusal) => ({ controlWords, written, assignments, launchers: [], argvFromStdin: false, runsDefaultProgram: false, program: undefined, refusal, launcherMovesBase: false });
64
74
  if (i >= folded.length)
65
75
  return none(undefined, []);
66
76
  const written = i;
@@ -78,25 +88,27 @@ export function readProgramPosition(words) {
78
88
  return none(written, assignments, { kind: "option_at_program_position", owner, option: head });
79
89
  }
80
90
  if (!LAUNCHER_NAMES.has(basename)) {
81
- return { controlWords, written, assignments, launchers: [], argvFromStdin: false, runsDefaultProgram: false, program: { index: i, name: head, basename }, refusal: undefined };
91
+ return { controlWords, written, assignments, launchers: [], argvFromStdin: false, runsDefaultProgram: false, program: { index: i, name: head, basename }, refusal: undefined, launcherMovesBase: false };
82
92
  }
83
93
  const launchers = [{ index: i, name: basename }];
84
94
  const owner = head;
85
95
  const shellRefusal = (launcher, from) => ({ kind: "control_syntax_at_program_position", owner, word: "sh", runsShell: { launcher, from } });
86
96
  let lastRow = launcherRowOf(basename)?.launcher;
87
97
  let owedOperands = lastRow?.requiredOperands ?? 0;
88
- const chain0 = (program, refusal) => ({ controlWords, written, assignments, launchers, argvFromStdin: false, runsDefaultProgram: false, program, refusal });
98
+ const chain0 = (program, refusal) => ({ controlWords, written, assignments, launchers, argvFromStdin: false, runsDefaultProgram: false, program, refusal, launcherMovesBase: false });
89
99
  if (launcherRunsShell(launcherRowOf(basename), folded.slice(i + 1)))
90
100
  return chain0(undefined, shellRefusal(i, i + 1));
91
101
  let argvFromStdin = feedsArgvFromStdin(basename);
92
102
  let lastReentry = argvFromStdin ? i : -1;
93
103
  let reentryDefault = argvFromStdin ? streamDefaultProgram(basename) : undefined;
104
+ let launcherMovesBase = false;
105
+ const moved = () => { launcherMovesBase = true; };
94
106
  const chain = (program, refusal) => {
95
107
  const runsDefaultProgram = lastReentry === folded.length - 1;
96
- return { controlWords, written, assignments, launchers, argvFromStdin, runsDefaultProgram, ...(runsDefaultProgram && reentryDefault !== undefined ? { defaultProgram: reentryDefault } : {}), program, refusal };
108
+ return { controlWords, written, assignments, launchers, argvFromStdin, runsDefaultProgram, ...(runsDefaultProgram && reentryDefault !== undefined ? { defaultProgram: reentryDefault } : {}), program, refusal, launcherMovesBase };
97
109
  };
98
110
  const selectsShell = (l, end) => selectedOptions(launcherRowOf(l.name), folded.slice(l.index + 1, end)).some((o) => o.mode === "interactive");
99
- for (let j = pastLauncherOptions(basename, folded, i + 1); j < folded.length; j++) {
111
+ for (let j = pastLauncherOptions(basename, folded, i + 1, moved); j < folded.length; j++) {
100
112
  const t = folded[j];
101
113
  if (owedOperands > 0 && !t.startsWith("-")) {
102
114
  owedOperands -= 1;
@@ -121,7 +133,7 @@ export function readProgramPosition(words) {
121
133
  if (launcherRunsShell(launcherRowOf(wordBasename), folded.slice(j + 1)))
122
134
  return chain(undefined, shellRefusal(j, j + 1));
123
135
  owedOperands = lastRow?.requiredOperands ?? 0;
124
- j = pastLauncherOptions(wordBasename, folded, j + 1) - 1;
136
+ j = pastLauncherOptions(wordBasename, folded, j + 1, moved) - 1;
125
137
  continue;
126
138
  }
127
139
  if (t.startsWith("-"))
@@ -361,7 +361,7 @@ export declare function formatOutOfRootReadApprovalOption(directory: string): st
361
361
  * resolved to nothing, was dropped, and the verdict was empty, i.e. a read outside every root
362
362
  * AUTO-ALLOWED on the one face whose doctrine is hard refusal in exactly that direction.
363
363
  * The second fact is why the lane split in
364
- * {@link collectSegmentBoundaryFindings} tests {@link isAbsolutePathForm} BESIDE this predicate,
364
+ * {@link collectSegmentBoundaryFindings} tests {@link isAbsoluteForFamily} (an address's own family) BESIDE this predicate,
365
365
  * which is what the option-payload arm always did; the two now read one rule rather than two. */
366
366
  export declare function isPathShapedToken(tok: string): boolean;
367
367
  /** Resolve one operand against the boundary's base, WITHOUT touching the filesystem. Returns undefined
@@ -1,5 +1,6 @@
1
- import { isAbsoluteForFamily, isAbsolutePathForm, isBlockedDevicePath, isShellRootedSpellingUnmapped, joinForFamily, nativeUncSpellingOf, normalizeAbsPathLexically, win32NamespaceScreen, pathFamilyOf } from "./safety.js";
1
+ import { anchorSpelling, isAbsoluteForFamily, isBlockedDevicePath, isShellRootedSpellingUnmapped, nativeUncSpellingOf, normalizeAbsPathLexically, win32NamespaceScreen, pathFamilyOf } from "./safety.js";
2
2
  import { PARSE_FAILURE_SENTENCE, UNRESOLVED_SENTENCE, argvOf, lexShell, literalTildeSpelling, } from "../../shell/lex.js";
3
+ import { shellWordsOf } from "../../shell/shell-words.js";
3
4
  import { textBindsHome } from "./shell-read-bindings.js";
4
5
  import { NOT_AUTO_ALLOWED, argvReentryUnjudged, isBareControlWord, programPositionRefusalSentence, readProgramPosition, } from "./bash-program-position.js";
5
6
  import { BLOCK_WORD_NAMES, attachedStdinFile, declaresOption, firstOperandSlot, opensNoOperand, programDelivery, programRow, programTextReadings, readArgRoles, readArgs, readProgramTextLanguage, runsShellText, selectedOptions, slotValueCandidates, suppliesFirstOperandSlot, takesSeparatedDatum, valuesAreLiteral, writeReadings, writesPath, } from "./program-read-model.js";
@@ -171,7 +172,7 @@ const PARENT_SEGMENT_RE = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
171
172
  export function resolveOperandSpellings(base, operand, homeDir) {
172
173
  let raw = operand;
173
174
  if (raw === "~" || raw.startsWith("~/")) {
174
- if (homeDir === undefined || !isAbsolutePathForm(homeDir))
175
+ if (homeDir === undefined || !isAbsoluteForFamily("own", homeDir))
175
176
  return undefined;
176
177
  raw = raw === "~" ? homeDir : `${homeDir.replace(/[/\\]+$/, "")}/${raw.slice(2)}`;
177
178
  }
@@ -181,13 +182,13 @@ export function resolveOperandSpellings(base, operand, homeDir) {
181
182
  const family = base === undefined ? undefined : pathFamilyOf({ root: base });
182
183
  if (!win32NamespaceScreen(raw, family).ok)
183
184
  return undefined;
184
- if (isAbsoluteForFamily(family, raw))
185
+ if (isAbsoluteForFamily(family ?? "own", raw))
185
186
  return spellingsOf(nativeUncSpellingOf(family, raw));
186
187
  if (base === undefined || family === undefined || !isAbsoluteForFamily(family, base))
187
188
  return undefined;
188
189
  if (isShellRootedSpellingUnmapped(family, raw))
189
190
  return undefined;
190
- return spellingsOf(joinForFamily(family, base, raw));
191
+ return spellingsOf(anchorSpelling(family, base, raw));
191
192
  }
192
193
  function spellingsOf(absolute) {
193
194
  const folded = normalizeAbsPathLexically(absolute);
@@ -660,7 +661,7 @@ export function attachedOptionPayloads(tok) {
660
661
  return out;
661
662
  }
662
663
  const whole = literal(tok.slice(1));
663
- if (whole.length > 0 && (isPathShapedToken(whole) || isAbsolutePathForm(whole)))
664
+ if (whole.length > 0 && (isPathShapedToken(whole) || isAbsoluteForFamily("own", whole)))
664
665
  out.push(whole);
665
666
  for (let k = 2; k <= tok.length; k++) {
666
667
  if (![...tok.slice(1, k)].every((ch) => CLUSTER_OPTION_CHAR.test(ch)))
@@ -744,7 +745,7 @@ export function classifyCompoundReadonlyDetailed(command, allow, boundary) {
744
745
  const written = folded[from];
745
746
  if (written === undefined)
746
747
  return { reason: "empty command" };
747
- if (written === "coproc")
748
+ if (shellWordsOf("background").has(written))
748
749
  return { reason: "`coproc` backgrounds a command — a backgrounded process outlives the command" };
749
750
  if (isBareControlWord(words, from, BLOCK_WORD_NAMES)) {
750
751
  unlistedSegments.add(si);
@@ -777,7 +778,7 @@ export function classifyCompoundReadonlyDetailed(command, allow, boundary) {
777
778
  if (runsShellText(name))
778
779
  return { reason: unjudgedProgramTextRefusal(parsed.name, "runs its argument as shell text") };
779
780
  if (pos.launchers.length > 0) {
780
- const listed = program !== undefined && (allow.has(program.basename) || program.basename === "cd");
781
+ const listed = program !== undefined && allow.has(program.basename);
781
782
  if (program !== undefined && !listed && runsShellText(program.basename)) {
782
783
  return { reason: `"${written}" runs "${program.basename}", which runs its argument as shell text this classifier does not read ${NOT_AUTO_ALLOWED}` };
783
784
  }
@@ -3,6 +3,10 @@
3
3
  * type gates all still run). One type for both seats: the two faces differ in WHICH band consults
4
4
  * them, never in what the word means. */
5
5
  export type ContainmentFace = "open" | "roots";
6
+ /** Does a face state skip the roots-containment step? The ONE reading of a face seat a containment judgment takes — the
7
+ * structured tools' path check and the shell station's write face alike: `"open"` skips it; `"roots"` and an ABSENT
8
+ * state run it (absent ⇒ roots, the historical fence). */
9
+ export declare function skipsRootsContainment(face: ContainmentFace | undefined): boolean;
6
10
  /** Which band a face governs. Selects the seat spellings and the error codes below — never the
7
11
  * resolution ORDER, which is one body shared by both. */
8
12
  export type ContainmentFaceKind = "read" | "write";
@@ -1,4 +1,7 @@
1
1
  import { describeRejectedValue } from "../../core/tool-search-setting.js";
2
+ export function skipsRootsContainment(face) {
3
+ return face === "open";
4
+ }
2
5
  const FACE_DIALECT = {
3
6
  read: {
4
7
  key: "readFace",
@@ -253,6 +253,16 @@ export declare function bashMaxOutputChars(): number;
253
253
  export declare const FILE_PATH_PARAMS: {
254
254
  file_path: Type.TString;
255
255
  };
256
+ /**
257
+ * Write's pre-validation argument repair — CC 2.1.296 `orr` (the Write tool's `coerceInput`, cli296.js:247143),
258
+ * the two rules that matter under a non-strict schema: `path` is read as `file_path` when `file_path` is absent
259
+ * and `path` is a string; exactly one of `file_text` / `file_content` is read as `content` when `content` is
260
+ * absent and the value is a string. Anything else — both content aliases, a present `file_path`, a non-string
261
+ * value — is left to the schema, which refuses it by naming the parameter. The repaired key REPLACES the alias
262
+ * (it is deleted), so no second spelling rides on into the gates. Idempotent, and the very same object for a
263
+ * call that needs no repair (the loop's "prepared arguments changed" test is a reference test).
264
+ */
265
+ export declare function coerceWriteArguments(args: unknown): unknown;
256
266
  /** Honest head+tail truncation to {@link bashMaxOutputChars}: the middle is dropped with a real-total
257
267
  * marker, because the head AND tail both matter for a shell. Kept as the inline shape even beside the
258
268
  * file-backed recovery below — unlike a background task's poll-for-the-latest-tail interaction, a
@@ -185,6 +185,24 @@ export function bashMaxOutputChars() {
185
185
  export const FILE_PATH_PARAMS = {
186
186
  file_path: Type.String({ description: "File path (within the configured root)." }),
187
187
  };
188
+ const WRITE_CONTENT_ALIASES = ["file_text", "file_content"];
189
+ export function coerceWriteArguments(args) {
190
+ if (args === null || typeof args !== "object" || Array.isArray(args))
191
+ return args;
192
+ const a = args;
193
+ let repaired;
194
+ if (!Object.hasOwn(a, "file_path") && typeof a["path"] === "string") {
195
+ repaired = { ...a, file_path: a["path"] };
196
+ delete repaired["path"];
197
+ }
198
+ const spelled = WRITE_CONTENT_ALIASES.filter((k) => Object.hasOwn(a, k));
199
+ const alias = spelled[0];
200
+ if (alias !== undefined && spelled.length === 1 && !Object.hasOwn(a, "content") && typeof a[alias] === "string") {
201
+ repaired = { ...(repaired ?? a), content: a[alias] };
202
+ delete repaired[alias];
203
+ }
204
+ return repaired ?? args;
205
+ }
188
206
  export function clipShellOutput(s) {
189
207
  return clipWithFilePointer(s, bashMaxOutputChars());
190
208
  }
@@ -3,7 +3,7 @@ import { Type } from "typebox";
3
3
  import { defineTool, errorResult } from "../../core/tools.js";
4
4
  import { sha256, resolveKey, violationText, violationDetails, requireRead, checkStale, checkEditMatch, checkNoChange, fileArgPath, resolveQuoteMatch, adaptNewStringQuotes, resolveEscapeMatch, adaptNewStringEscapes, escapeMatchWasAttempted, ESCAPE_MATCH_MISS_NOTE, deletionOldString, countOccurrences, READ_REFUSED_ESCAPE_HINT, } from "./safety.js";
5
5
  import { decodeTextBytes, encodeTextForFile, normalizeEditText, normalizeFileText } from "./encoding.js";
6
- import { MAX_EDIT_BYTES, decodeEditBytes, tooLargeToEditMessage, truncatedUtf16BodyMessage, persistedTextOf, notReadRefusalText, enoentMessage, FILE_STATE_TRAILER, FILE_PATH_PARAMS, ipynbRedirect, countLines, applyRecordedEdit, } from "./fs-shared.js";
6
+ import { MAX_EDIT_BYTES, decodeEditBytes, tooLargeToEditMessage, truncatedUtf16BodyMessage, persistedTextOf, notReadRefusalText, enoentMessage, FILE_STATE_TRAILER, FILE_PATH_PARAMS, coerceWriteArguments, ipynbRedirect, countLines, applyRecordedEdit, } from "./fs-shared.js";
7
7
  import { toolFace } from "../../core/tool-catalog-entries.js";
8
8
  import { thrownText } from "../../core/thrown-value.js";
9
9
  async function envFinalWrite(env, key, content, signal, opts) {
@@ -326,7 +326,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
326
326
  });
327
327
  }
328
328
  export function createWriteFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited, writeFace, writeDeny) {
329
- return defineTool({
329
+ const write = defineTool({
330
330
  name: "Write",
331
331
  ...toolFace("write"),
332
332
  description: "Writes a file to the local filesystem, overwriting if one exists.\n" +
@@ -420,6 +420,8 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
420
420
  };
421
421
  },
422
422
  });
423
+ write.prepareArguments = coerceWriteArguments;
424
+ return write;
423
425
  }
424
426
  const notebookNotIpynbMessage = (notebookPath) => `Error (NotebookEdit): "${notebookPath}" is not a .ipynb file; use Edit for other file types.`;
425
427
  const NOTEBOOK_CELL_TYPE_REQUIRED_MESSAGE = `Error (NotebookEdit): Cell type is required when using edit_mode=insert.`;
@@ -188,7 +188,7 @@ export interface HandsToolkitOptions {
188
188
  * same list as {@link import("./shell-read-check.js").ShellReadJudgeInput.readDenyHomes}. */
189
189
  readDenyHomes?: readonly string[];
190
190
  /** Where each home-anchored row's literal head leads beyond the home's spellings, and each rooted row's (`/…`) beyond its own
191
- * spelling ({@link import("./read-deny-home.js").readDenyHomeLinksOf} / {@link import("./read-deny-home.js").readDenyRootLinksOf},
191
+ * spelling ({@link import("./read-deny-home.js").readDenyLinksOf},
192
192
  * keyed by the row's pattern) — the Runner passes the
193
193
  * locations its own judge was compiled with, so the band and the gate judge one placement.
194
194
  * Unless a mount carries both, the band resolves the placement itself the way the Runner does (the homes it carries
@@ -1,3 +1,24 @@
1
+ /**
2
+ * The PROGRAM READ MODEL — one table that answers "what does this program do with its k-th word", and the readers
3
+ * every shell face reads it through.
4
+ *
5
+ * A row is one program (the basename bash runs; a versioned spelling of an interpreter is its row), carrying every
6
+ * argument fact the shell faces need: its option grammar (one per implementation where implementations disagree),
7
+ * the ROLE each option value and each operand plays, whether a form walks a tree, how an interpreter receives the
8
+ * program it runs, what a launcher steps over before the program it runs, which words a builtin keeps as shell text
9
+ * to run later. The rows live in `program-read-model.rows.ts`; this module is the schema and the readers.
10
+ *
11
+ * Every reader below is a PROJECTION of the same row: the option-slot reading the candidate extraction walks
12
+ * (`readArgs`), the traversal reading, the program-delivery reading, the launcher step, the program-text reading of a
13
+ * language the station parses, the message slots, the code bodies. A reader never carries a table of its own; a fact
14
+ * one reader needs and the row does not state is added to the row, where every other reader sees it too.
15
+ *
16
+ * Rows are checked against the real binaries by `test/program-read-model.binary-gate.test.ts`: each declared option
17
+ * of each row is run, per implementation, on fixtures whose every word is a FIFO, and the words the binary OPENED are
18
+ * compared with the words the row predicts it opens. A row whose opener cannot be witnessed locally (a daemon, an
19
+ * object store, a shell builtin) says so in its `witness`, with the reason.
20
+ */
21
+ import { type WordPiece } from "../../shell/lex.js";
1
22
  /** What a datum names: never a file. */
2
23
  export declare const DATUM_FORMS: readonly ["count", "pattern", "name-pattern", "label", "message", "delimiter"];
3
24
  export type DatumForm = (typeof DATUM_FORMS)[number];
@@ -339,11 +360,9 @@ export interface ProgramRow {
339
360
  /** What a declaration builtin does to every name it binds with no switch selected ({@link OptionSpec.assigns}):
340
361
  * `readonly` makes them read-only, as `declare -r` does. */
341
362
  readonly assigns?: DeclaredAttribute;
342
- /** It moves the working directory in a way the station does not follow (`pushd`). */
343
- readonly movesBase?: "unfollowed";
344
- /** What it writes to its standard output, read off its operands ({@link printedText}): `words` = its operands joined
345
- * by a space (`echo`), `format` = its one operand as a format (`printf`). */
346
- readonly prints?: "words" | "format";
363
+ /** It moves the working directory through the directory stack (`pushd` / `popd`): the working-directory thread reads
364
+ * how (`shell-read-workdir.ts`). */
365
+ readonly movesBase?: "stack";
347
366
  /** A shell block word at the program position: it names no command of its own. */
348
367
  readonly syntax?: "block";
349
368
  /** Git-style subcommands: the word after the program's own options names one, and the words after it are read by
@@ -383,7 +402,7 @@ export declare function isDeclarationBuiltin(name: string): boolean;
383
402
  export declare function declaredAttributes(row: ProgramRow | undefined, args: readonly string[]): ReadonlySet<DeclaredAttribute>;
384
403
  /** Opens no file, starts no program and evaluates nothing (a declaration builtin: only bare — its caller checks). */
385
404
  export declare function isInertBuiltin(name: string): boolean;
386
- export declare function movesBaseUnfollowed(name: string): boolean;
405
+ export declare function movesBaseByStack(name: string): boolean;
387
406
  /** The LONG-OPTION name carried by a token (`--file=p` → `file`, `--reg` → `reg`); `undefined` for anything that is
388
407
  * not a long option with a non-empty name (`--`, `--=x`, a short option). */
389
408
  export declare function longOptionNameOf(tok: string): string | undefined;
@@ -668,14 +687,6 @@ export declare function programDelivery(row: ProgramRow | undefined, args: reado
668
687
  /** The language of the program an interpreter row runs: the language of its program operand, or of the first option that
669
688
  * delivers program text or a program file. `undefined` for a row that runs no program text. */
670
689
  export declare function programLanguageOf(row: ProgramRow | undefined): string | undefined;
671
- /**
672
- * The text a printer row ({@link ProgramRow.prints}) writes to its standard output, read off its argv (the program word
673
- * excluded; `undefined` = a word whose value the command does not spell). `undefined` wherever the words do not spell the
674
- * text: such a word, an option the row does not explain, an escape the program may interpret (a backslash: `echo -e`, an
675
- * `echo` that reads escapes by default, every `printf` format), a format with a directive (`%`) or values to fill it, or a
676
- * row that prints nothing it is handed.
677
- */
678
- export declare function printedText(row: ProgramRow | undefined, args: readonly (string | undefined)[]): string | undefined;
679
690
  /**
680
691
  * The argv indices some implementation of the row reads as a path it OPENS (an ordinary operand, a value or operand of a
681
692
  * reading path role, a program file) while another reads them as something else (program text, a backup suffix, a
@@ -701,6 +712,11 @@ export declare function launcherOptionArity(row: ProgramRow, tok: string): 0 | 1
701
712
  export declare function launcherOptionPathRole(row: ProgramRow, tok: string): Extract<ArgRole, {
702
713
  kind: "path";
703
714
  }> | undefined;
715
+ /** Does this launcher option word move the base its program runs in — the directory, the root, or where its word is looked
716
+ * up (an option whose value's role is a path with a `base`) — read off the option itself, whether or not its value is
717
+ * written (`nsenter -w` bare takes the target process's directory)? In a cluster, a letter up to the first that takes a
718
+ * value. `false` for any word the walk does not step. */
719
+ export declare function launcherOptionMovesBase(row: ProgramRow, tok: string): boolean;
704
720
  /** How many words one launcher option occupies ({@link readLauncherOption}), or `undefined` where the row does not
705
721
  * explain it. */
706
722
  export declare function launcherOptionStep(row: ProgramRow, tok: string): 1 | 2 | undefined;
@@ -754,13 +770,18 @@ export declare function subcommandOf(row: ProgramRow | undefined, words: readonl
754
770
  readonly relocated: boolean;
755
771
  } | undefined;
756
772
  /**
757
- * The indices of the SETTLED words a row's subcommand reads as message text: the value of its message option attached
758
- * or as the next word, by its own grammar. The global options are read exactly ({@link subcommandOf}); in the subcommand
759
- * an unknown long option takes no value, an unknown short letter or an unsettled word takes the next word (which is
760
- * then never a message).
773
+ * The indices of the words a row's subcommand reads as message text: the value of its message option attached or as the
774
+ * next word, by its own grammar. The global options are read exactly ({@link subcommandOf}); in the subcommand an unknown
775
+ * long option takes no value, an unknown short letter or an unsettled word takes the next word (which is then never a
776
+ * message). The option word itself is settled; its separate value is a message whether the text settles it or the shell
777
+ * computes it as ONE argument — every expansion in it a command substitution in double quotes, whose output is one field
778
+ * (a parameter is not taken so: `"$@"` and `"${a[@]}"` are several).
761
779
  */
762
780
  export declare function messageWords(program: string, words: readonly {
763
781
  readonly folded: string;
764
782
  readonly expands: unknown;
783
+ readonly word?: {
784
+ readonly expansions: readonly WordPiece[];
785
+ };
765
786
  }[], programIndex: number): Set<number>;
766
787
  export {};