@sema-agent/core 7.47.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 (431) hide show
  1. package/CHANGELOG.md +472 -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 +39 -32
  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 +95 -66
  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-server-spec.d.ts +1 -1
  101. package/dist/core/mcp.d.ts +4 -0
  102. package/dist/core/mcp.js +70 -43
  103. package/dist/core/memory-engine/content-origin.js +2 -14
  104. package/dist/core/memory-engine/engine.d.ts +32 -22
  105. package/dist/core/memory-engine/engine.js +103 -55
  106. package/dist/core/memory-engine/file-backend.d.ts +13 -30
  107. package/dist/core/memory-engine/file-backend.js +37 -252
  108. package/dist/core/memory-engine/index.d.ts +1 -1
  109. package/dist/core/memory-engine/index.js +1 -1
  110. package/dist/core/memory-engine/layout.d.ts +139 -9
  111. package/dist/core/memory-engine/layout.js +350 -7
  112. package/dist/core/memory-engine/scope-contract.d.ts +10 -0
  113. package/dist/core/memory-engine/scope-contract.js +8 -0
  114. package/dist/core/memory-engine/session-incarnation.d.ts +14 -4
  115. package/dist/core/memory-engine/session-incarnation.js +5 -2
  116. package/dist/core/memory-engine/types.d.ts +10 -1
  117. package/dist/core/memory.js +5 -17
  118. package/dist/core/model-seat.d.ts +4 -4
  119. package/dist/core/model-seat.js +1 -1
  120. package/dist/core/mounted-mcp.d.ts +57 -0
  121. package/dist/core/mounted-mcp.js +19 -0
  122. package/dist/core/namespaced-rule-name.d.ts +29 -0
  123. package/dist/core/namespaced-rule-name.js +52 -0
  124. package/dist/core/park-selfcheck.d.ts +1 -1
  125. package/dist/core/park-selfcheck.js +16 -6
  126. package/dist/core/path-guard-rows.d.ts +99 -0
  127. package/dist/core/path-guard-rows.js +45 -0
  128. package/dist/core/path-identity.d.ts +22 -16
  129. package/dist/core/path-identity.js +10 -10
  130. package/dist/core/path-rule-binding.d.ts +2 -1
  131. package/dist/core/path-rule-binding.js +3 -1
  132. package/dist/core/pause-registry.d.ts +16 -2
  133. package/dist/core/pause-registry.js +6 -0
  134. package/dist/core/permission-rule-consent.js +0 -7
  135. package/dist/core/permission-rule-model.d.ts +68 -24
  136. package/dist/core/permission-rule-model.js +105 -24
  137. package/dist/core/permission-rule-store.js +2 -2
  138. package/dist/core/permission-rules.d.ts +3 -30
  139. package/dist/core/permission-rules.js +4 -54
  140. package/dist/core/read-root-candidate.d.ts +1 -3
  141. package/dist/core/read-root-candidate.js +4 -15
  142. package/dist/core/real-approval.d.ts +4 -3
  143. package/dist/core/retired-keys.d.ts +13 -0
  144. package/dist/core/retired-keys.js +14 -0
  145. package/dist/core/roles.d.ts +2 -3
  146. package/dist/core/roles.js +15 -6
  147. package/dist/core/runner/assemble-result.d.ts +0 -7
  148. package/dist/core/runner/assemble-result.js +19 -31
  149. package/dist/core/runner/caller-roster.d.ts +78 -0
  150. package/dist/core/runner/caller-roster.js +219 -0
  151. package/dist/core/runner/checkpoint-scope.d.ts +16 -16
  152. package/dist/core/runner/checkpoint-scope.js +2 -2
  153. package/dist/core/runner/child-tree.js +2 -2
  154. package/dist/core/runner/contracts.d.ts +68 -35
  155. package/dist/core/runner/decide-continuation.d.ts +51 -3
  156. package/dist/core/runner/decide-continuation.js +76 -21
  157. package/dist/core/runner/denial-limit-arms.d.ts +17 -5
  158. package/dist/core/runner/denial-limit-arms.js +11 -9
  159. package/dist/core/runner/extra-body-dropped-seat.d.ts +8 -0
  160. package/dist/core/runner/extra-body-dropped-seat.js +22 -0
  161. package/dist/core/runner/finish-reason-seat.d.ts +9 -0
  162. package/dist/core/runner/finish-reason-seat.js +40 -0
  163. package/dist/core/runner/gate-exit.d.ts +44 -32
  164. package/dist/core/runner/gate-exit.js +112 -25
  165. package/dist/core/runner/inherited-ask-grants.js +6 -4
  166. package/dist/core/runner/mcp-redial.d.ts +3 -3
  167. package/dist/core/runner/mcp-redial.js +3 -4
  168. package/dist/core/runner/park-commit.d.ts +1 -1
  169. package/dist/core/runner/permission-rule-lanes.d.ts +1 -2
  170. package/dist/core/runner/permission-rule-lanes.js +5 -4
  171. package/dist/core/runner/prepare-artifact.d.ts +4 -0
  172. package/dist/core/runner/prepare-artifact.js +2 -1
  173. package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
  174. package/dist/core/runner/prepare-ask-lane.js +12 -6
  175. package/dist/core/runner/prepare-boundary-parks.d.ts +5 -8
  176. package/dist/core/runner/prepare-boundary-parks.js +4 -7
  177. package/dist/core/runner/prepare-caps-and-workflow.d.ts +11 -15
  178. package/dist/core/runner/prepare-caps-and-workflow.js +19 -27
  179. package/dist/core/runner/prepare-config-doors.d.ts +9 -9
  180. package/dist/core/runner/prepare-config-doors.js +10 -5
  181. package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
  182. package/dist/core/runner/prepare-defer-classify.js +5 -3
  183. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
  184. package/dist/core/runner/prepare-delegation-surface.js +3 -3
  185. package/dist/core/runner/prepare-gate-stations.d.ts +4 -4
  186. package/dist/core/runner/prepare-gate-stations.js +9 -7
  187. package/dist/core/runner/prepare-hands-readface.d.ts +7 -7
  188. package/dist/core/runner/prepare-hands-readface.js +7 -7
  189. package/dist/core/runner/prepare-inherited-gate.d.ts +7 -6
  190. package/dist/core/runner/prepare-inherited-gate.js +14 -15
  191. package/dist/core/runner/prepare-listings.d.ts +7 -24
  192. package/dist/core/runner/prepare-listings.js +4 -5
  193. package/dist/core/runner/prepare-memory-engine-session.d.ts +2 -0
  194. package/dist/core/runner/prepare-memory-engine-session.js +4 -3
  195. package/dist/core/runner/prepare-memory.d.ts +6 -0
  196. package/dist/core/runner/prepare-memory.js +13 -13
  197. package/dist/core/runner/prepare-offload-wrappers.d.ts +5 -0
  198. package/dist/core/runner/prepare-offload-wrappers.js +3 -3
  199. package/dist/core/runner/prepare-park-ask.d.ts +7 -8
  200. package/dist/core/runner/prepare-park-ask.js +17 -10
  201. package/dist/core/runner/prepare-policy-chain.js +24 -44
  202. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  203. package/dist/core/runner/prepare-project-context.js +3 -4
  204. package/dist/core/runner/prepare-protocol-tools.d.ts +9 -3
  205. package/dist/core/runner/prepare-protocol-tools.js +3 -4
  206. package/dist/core/runner/prepare-run-refs.d.ts +3 -0
  207. package/dist/core/runner/prepare-run-refs.js +2 -1
  208. package/dist/core/runner/prepare-safety-scan.d.ts +4 -8
  209. package/dist/core/runner/prepare-safety-scan.js +1 -2
  210. package/dist/core/runner/prepare-suspend-saga.d.ts +7 -1
  211. package/dist/core/runner/prepare-suspend-saga.js +28 -12
  212. package/dist/core/runner/prepare-task.d.ts +1 -1
  213. package/dist/core/runner/prepare-task.js +67 -56
  214. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -10
  215. package/dist/core/runner/prepare-wiring-manifest.js +5 -8
  216. package/dist/core/runner/resume-apply.js +7 -14
  217. package/dist/core/runner/resume-checkpoint-screen.js +11 -4
  218. package/dist/core/runner/resume-internals-and-config.js +2 -2
  219. package/dist/core/runner/resume-policy-outcome.js +5 -32
  220. package/dist/core/runner/resume-review-outcome.js +4 -1
  221. package/dist/core/runner/retired-runner-deps.d.ts +6 -0
  222. package/dist/core/runner/retired-runner-deps.js +10 -1
  223. package/dist/core/runner/run-brain-sinks.d.ts +4 -0
  224. package/dist/core/runner/run-brain-sinks.js +5 -2
  225. package/dist/core/runner/run-harness-handlers.js +27 -34
  226. package/dist/core/runner/run-leg.d.ts +0 -2
  227. package/dist/core/runner/run-leg.js +32 -33
  228. package/dist/core/runner/run-terminal-adoption.js +4 -3
  229. package/dist/core/runner/runtask.js +8 -8
  230. package/dist/core/runner/session-rule-policy.js +5 -5
  231. package/dist/core/runner/stream-settle-backstop.js +7 -4
  232. package/dist/core/runner/sub-agent-deny-lists.d.ts +51 -12
  233. package/dist/core/runner/sub-agent-deny-lists.js +111 -80
  234. package/dist/core/runner/terminal-account.d.ts +48 -0
  235. package/dist/core/runner/terminal-account.js +186 -0
  236. package/dist/core/runner/tool-end-body.d.ts +54 -17
  237. package/dist/core/runner/tool-end-body.js +27 -2
  238. package/dist/core/runner/tool-face-overlay.d.ts +3 -1
  239. package/dist/core/runner/tool-face-overlay.js +6 -4
  240. package/dist/core/runner/tool-occurrence.d.ts +106 -0
  241. package/dist/core/runner/tool-occurrence.js +123 -0
  242. package/dist/core/runner/tool-progress-lane.d.ts +16 -8
  243. package/dist/core/runner/tool-progress-lane.js +25 -34
  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/safety-axis-vocab.d.ts +1 -1
  249. package/dist/core/safety-merge-corpus.d.ts +1 -1
  250. package/dist/core/secret-env.d.ts +16 -5
  251. package/dist/core/secret-env.js +23 -63
  252. package/dist/core/sensitive-path-policy.d.ts +19 -18
  253. package/dist/core/sensitive-path-policy.js +73 -49
  254. package/dist/core/session-policy-store.d.ts +6 -3
  255. package/dist/core/session-policy-store.js +28 -10
  256. package/dist/core/session-reconcile.d.ts +3 -0
  257. package/dist/core/session-reconcile.js +5 -4
  258. package/dist/core/session-store.d.ts +5 -0
  259. package/dist/core/session-store.js +12 -0
  260. package/dist/core/session.d.ts +13 -0
  261. package/dist/core/shared-memory/normalize.d.ts +17 -4
  262. package/dist/core/shared-memory/normalize.js +7 -7
  263. package/dist/core/shell-lexer.js +5 -4
  264. package/dist/core/shell-wrapper-table.d.ts +2 -2
  265. package/dist/core/shell-wrapper-table.js +1 -1
  266. package/dist/core/skill-grant.js +1 -1
  267. package/dist/core/store-contracts/checkpoint-store-contract.js +13 -0
  268. package/dist/core/store-contracts/mailbox-store-contract.d.ts +7 -14
  269. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +27 -0
  270. package/dist/core/store-contracts/session-store-incarnation-contract.js +92 -0
  271. package/dist/core/stub-env.d.ts +2 -0
  272. package/dist/core/stub-env.js +1 -0
  273. package/dist/core/synthetic-read-verdict.js +2 -2
  274. package/dist/core/task-event.d.ts +59 -5
  275. package/dist/core/task-result.d.ts +1 -1
  276. package/dist/core/task-spec.d.ts +42 -9
  277. package/dist/core/terminal-cause.d.ts +94 -11
  278. package/dist/core/tighten-task-spec.d.ts +5 -4
  279. package/dist/core/tighten-task-spec.js +20 -25
  280. package/dist/core/tool-catalog-entries.d.ts +5 -7
  281. package/dist/core/tool-catalog-entries.js +14 -15
  282. package/dist/core/tool-face.d.ts +47 -0
  283. package/dist/core/tool-face.js +5 -0
  284. package/dist/core/tool-model-gate.d.ts +5 -5
  285. package/dist/core/tool-name-aliases.d.ts +35 -10
  286. package/dist/core/tool-name-aliases.js +15 -0
  287. package/dist/core/tool-name-list.d.ts +30 -0
  288. package/dist/core/tool-name-list.js +55 -0
  289. package/dist/core/tool-name-readings.d.ts +1 -9
  290. package/dist/core/tool-name-readings.js +1 -3
  291. package/dist/core/tool-policy.d.ts +195 -85
  292. package/dist/core/tool-policy.js +165 -381
  293. package/dist/core/tool-registry.d.ts +44 -1
  294. package/dist/core/tool-registry.js +26 -0
  295. package/dist/core/tool-roster.d.ts +19 -1
  296. package/dist/core/tool-roster.js +21 -2
  297. package/dist/core/tool-spec-members.d.ts +45 -0
  298. package/dist/core/tool-spec-members.js +169 -0
  299. package/dist/core/tool-spec.d.ts +46 -8
  300. package/dist/core/tools.d.ts +6 -2
  301. package/dist/core/tools.js +8 -4
  302. package/dist/core/untrusted-egress.js +29 -26
  303. package/dist/core/untrusted-text.d.ts +13 -10
  304. package/dist/core/untrusted-text.js +6 -44
  305. package/dist/core/workflow-journal-store.d.ts +4 -1
  306. package/dist/core/write-protect.d.ts +65 -29
  307. package/dist/core/write-protect.js +235 -177
  308. package/dist/engine/compaction/compaction.d.ts +2 -2
  309. package/dist/engine/execution-env/node-execution-env.d.ts +5 -23
  310. package/dist/engine/execution-env/node-execution-env.js +16 -38
  311. package/dist/engine/execution-env/symlink-chain.d.ts +23 -0
  312. package/dist/engine/execution-env/symlink-chain.js +32 -0
  313. package/dist/engine/harness/types.d.ts +7 -0
  314. package/dist/engine/llm/tool-turns.d.ts +3 -0
  315. package/dist/engine/llm/tool-turns.js +1 -0
  316. package/dist/engine/llm/types.d.ts +59 -23
  317. package/dist/engine/loop/agent-loop.js +16 -2
  318. package/dist/engine/session/memory-repo.js +11 -3
  319. package/dist/engine/session/repo-utils.d.ts +8 -0
  320. package/dist/engine/session/repo-utils.js +13 -0
  321. package/dist/index.d.ts +20 -23
  322. package/dist/index.js +17 -20
  323. package/dist/internal/harness.d.ts +1 -1
  324. package/dist/internal/harness.js +1 -1
  325. package/dist/internal/llm.d.ts +2 -2
  326. package/dist/internal/llm.js +1 -1
  327. package/dist/orchestration/governance-baseline-validity.js +1 -1
  328. package/dist/orchestration/run-spec.js +3 -3
  329. package/dist/orchestration/run-workflow-tool.d.ts +16 -35
  330. package/dist/orchestration/run-workflow-tool.js +38 -78
  331. package/dist/orchestration/workflow-governance.d.ts +21 -25
  332. package/dist/orchestration/workflow-governance.js +38 -78
  333. package/dist/orchestration/workflow-primitives.d.ts +7 -22
  334. package/dist/orchestration/workflow-primitives.js +5 -12
  335. package/dist/orchestration/workflow-types.d.ts +1 -1
  336. package/dist/orchestration/workflow.d.ts +61 -11
  337. package/dist/orchestration/workflow.js +191 -119
  338. package/dist/prompt-assembly/types.d.ts +1 -1
  339. package/dist/scenarios/full-body.d.ts +1 -1
  340. package/dist/shell/lex.d.ts +0 -21
  341. package/dist/shell/lex.js +278 -70
  342. package/dist/shell/shell-words.d.ts +35 -0
  343. package/dist/shell/shell-words.js +65 -0
  344. package/dist/stores/file/checkpoint-store.d.ts +5 -5
  345. package/dist/stores/file/checkpoint-store.js +5 -2
  346. package/dist/stores/file/index.d.ts +0 -8
  347. package/dist/stores/file/index.js +0 -12
  348. package/dist/stores/file/session-store.d.ts +3 -0
  349. package/dist/stores/file/session-store.js +44 -19
  350. package/dist/tools/artifact/artifact-tool.js +2 -0
  351. package/dist/tools/fs/bash-program-position.d.ts +17 -3
  352. package/dist/tools/fs/bash-program-position.js +25 -10
  353. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  354. package/dist/tools/fs/bash-readonly-classifier.js +8 -7
  355. package/dist/tools/fs/containment-face.d.ts +4 -0
  356. package/dist/tools/fs/containment-face.js +3 -0
  357. package/dist/tools/fs/fs-shared.d.ts +10 -0
  358. package/dist/tools/fs/fs-shared.js +18 -0
  359. package/dist/tools/fs/fs-write.js +4 -2
  360. package/dist/tools/fs/index.d.ts +3 -2
  361. package/dist/tools/fs/index.js +2 -2
  362. package/dist/tools/fs/program-read-model.d.ts +99 -23
  363. package/dist/tools/fs/program-read-model.js +129 -55
  364. package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
  365. package/dist/tools/fs/program-read-model.rows.js +18 -10
  366. package/dist/tools/fs/read-deny-home.d.ts +35 -30
  367. package/dist/tools/fs/read-deny-home.js +45 -38
  368. package/dist/tools/fs/read-deny.d.ts +35 -27
  369. package/dist/tools/fs/read-deny.js +126 -92
  370. package/dist/tools/fs/removal-targets.d.ts +56 -23
  371. package/dist/tools/fs/removal-targets.js +74 -87
  372. package/dist/tools/fs/safety.d.ts +29 -22
  373. package/dist/tools/fs/safety.js +17 -11
  374. package/dist/tools/fs/search.js +10 -8
  375. package/dist/tools/fs/shell-read-argv.d.ts +4 -7
  376. package/dist/tools/fs/shell-read-argv.js +6 -8
  377. package/dist/tools/fs/shell-read-bindings.d.ts +203 -46
  378. package/dist/tools/fs/shell-read-bindings.js +488 -144
  379. package/dist/tools/fs/shell-read-builtins.d.ts +35 -0
  380. package/dist/tools/fs/shell-read-builtins.js +43 -0
  381. package/dist/tools/fs/shell-read-check.d.ts +15 -1
  382. package/dist/tools/fs/shell-read-check.js +51 -42
  383. package/dist/tools/fs/shell-read-establish.js +9 -10
  384. package/dist/tools/fs/shell-read-functions.d.ts +22 -0
  385. package/dist/tools/fs/shell-read-functions.js +29 -0
  386. package/dist/tools/fs/shell-read-judge.d.ts +48 -18
  387. package/dist/tools/fs/shell-read-judge.js +33 -4
  388. package/dist/tools/fs/shell-read-plan.d.ts +13 -20
  389. package/dist/tools/fs/shell-read-plan.js +200 -194
  390. package/dist/tools/fs/shell-read-start.d.ts +80 -0
  391. package/dist/tools/fs/shell-read-start.js +99 -0
  392. package/dist/tools/fs/shell-read-word.d.ts +35 -6
  393. package/dist/tools/fs/shell-read-word.js +89 -9
  394. package/dist/tools/fs/shell-read-workdir.d.ts +156 -0
  395. package/dist/tools/fs/shell-read-workdir.js +281 -0
  396. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -2
  397. package/dist/tools/fs/shell-read-write-targets.js +14 -5
  398. package/dist/tools/fs/shell-reentry-text.d.ts +26 -26
  399. package/dist/tools/fs/shell-reentry-text.js +31 -34
  400. package/dist/tools/task-list.d.ts +1 -2
  401. package/dist/tools/web.js +1 -1
  402. package/package.json +4 -1
  403. package/test/export-surface.snapshot.json +107 -143
  404. package/wire-changes/core@7.48.0.json +252 -0
  405. package/wire-changes/core@7.49.0.json +4765 -0
  406. package/dist/agents/teacher.d.ts +0 -159
  407. package/dist/agents/teacher.js +0 -577
  408. package/dist/agents/team.d.ts +0 -226
  409. package/dist/agents/team.js +0 -257
  410. package/dist/core/strategy-store.d.ts +0 -238
  411. package/dist/core/strategy-store.js +0 -253
  412. package/dist/orchestration/builtin-workflows.d.ts +0 -133
  413. package/dist/orchestration/builtin-workflows.js +0 -159
  414. package/dist/scenarios/env.d.ts +0 -40
  415. package/dist/scenarios/env.js +0 -39
  416. package/dist/scenarios/scenario-registry.d.ts +0 -110
  417. package/dist/scenarios/scenario-registry.js +0 -140
  418. package/dist/scenarios/teacher-quickstart.d.ts +0 -40
  419. package/dist/scenarios/teacher-quickstart.js +0 -12
  420. package/dist/stores/cc/lockfile.d.ts +0 -10
  421. package/dist/stores/cc/lockfile.js +0 -98
  422. package/dist/stores/cc/mailbox-store.d.ts +0 -20
  423. package/dist/stores/cc/mailbox-store.js +0 -255
  424. package/dist/stores/cc/roster-adapter.d.ts +0 -12
  425. package/dist/stores/cc/roster-adapter.js +0 -117
  426. package/dist/stores/cc/sidecar-transcript.d.ts +0 -97
  427. package/dist/stores/cc/sidecar-transcript.js +0 -264
  428. package/dist/stores/cc/task-list-store.d.ts +0 -2
  429. package/dist/stores/cc/task-list-store.js +0 -125
  430. package/dist/stores/file/strategy-store.d.ts +0 -97
  431. package/dist/stores/file/strategy-store.js +0 -341
@@ -28,7 +28,7 @@ export declare function agentConfigDenyPatterns(): readonly string[];
28
28
  * default behavior is unchanged (no implicit default list; wiring this set is the caller's opt-in:
29
29
  * `createSensitivePathPolicy({ patterns: RECOMMENDED_SENSITIVE_PATTERNS, … })`).
30
30
  *
31
- * Pattern semantics (must match `compilePatterns`/`matchSensitive` below — segment globs, NOT full-path
31
+ * Pattern semantics (must match `compilePatterns` below and the rows' reach, `segmentGuardRow` — segment globs, NOT full-path
32
32
  * globs): each pattern is a `/`-separated run of path SEGMENTS; `*` matches any run of non-slash chars
33
33
  * within one segment and is the ONLY metacharacter (everything else, `?` included, is literal); the run
34
34
  * may match ANYWHERE in the canonical path (so `.ssh` covers `~/.ssh/…` and any other `.ssh` directory —
@@ -47,7 +47,7 @@ export declare function agentConfigDenyPatterns(): readonly string[];
47
47
  * That entry is gone (see the severity layering below), but the exclusion stays mechanical — a
48
48
  * deployment may site its data root beneath ANY guarded directory, and both tiers honour it. A
49
49
  * target INSIDE the data root is matched on its path RELATIVE to
50
- * that root (see {@link createSensitivePathPolicy}'s `dataRoot`), so the root's own ancestry is not the
50
+ * that root (the engine's one exempt root, `ToolCallRequest.exemptRoots`), so the root's own ancestry is not the
51
51
  * target's business, while a genuinely sensitive path INSIDE it (`<dataRoot>/x/.ssh/id_rsa`) still
52
52
  * matches. The `sessions/` subtree keeps its own owner: `createTranscriptIntegrityPolicy` is wired
53
53
  * ALWAYS-ON by the runner and answers `ask` — the design's deliberate verdict, since «reading
@@ -95,7 +95,7 @@ export declare function dataRootSpellings(explicit: string | undefined): string[
95
95
  * ONE resolver for both write judges (the deny set here, the always-on table's target view) — an
96
96
  * engine-internal seam, like the two neighbours above.
97
97
  */
98
- export declare function canonicalDataRootKeys(env: ExecutionEnv, spellings: readonly string[], signal?: AbortSignal): Promise<string[]>;
98
+ export declare function canonicalDataRootKeys(env: ExecutionEnv, spellings: readonly string[], signal?: AbortSignal, onLookupFailed?: (err: unknown) => void): Promise<string[]>;
99
99
  /**
100
100
  * Is `targetKey` inside the data root `rootKey`, and if so what is its path relative to it? Returns
101
101
  * the relative sub-path (`""` when the target IS the root), or `null` for everything else.
@@ -120,8 +120,9 @@ export declare function canonicalDataRootKeys(env: ExecutionEnv, spellings: read
120
120
  */
121
121
  export declare function relativeUnderRoot(targetKey: string, rootKey: string): string | null;
122
122
  /**
123
- * Build a composable policy that DENIES writes (via the guarded fs tools) whose CANONICAL target lands on
124
- * a guarded pattern. `patterns` is deployment-supplied — no default list is shipped; empty = a no-op allow.
123
+ * Build a composable policy that DENIES writes (via the guarded fs tools) whose target lands on a guarded pattern — by any
124
+ * view its reading holds: the spelling anchored at the tool's declared base, the canonical target, a hop of the leaf's
125
+ * symlink chain. `patterns` is deployment-supplied — no default list is shipped; empty = a no-op allow.
125
126
  * A deny is a refusal no approver and no persisted allow rule can lift. The ASK severity of the same class
126
127
  * (the agent-configuration DIRECTORIES) is not a tier here: it is the always-on write-protection table's,
127
128
  * which judges the same real target ({@link import("./write-protect.js").WriteProtectionMatcher.judge} —
@@ -134,27 +135,27 @@ export declare function relativeUnderRoot(targetKey: string, rootKey: string): s
134
135
  * {@link import("./fs-write-gate-policy.js").FsWriteGatePolicyOptions.rootPath}); the call's live
135
136
  * `ToolCallRequest.cwd` outranks it for the target.
136
137
  *
138
+ * HRD-PRM-10 — the data-root exemption's root is not this list's: a target inside the engine data root is judged on its path
139
+ * RELATIVE to that root, and the root is the one the engine resolved for the leg (`ToolCallRequest.exemptRoots`, from
140
+ * `RunnerDeps.memoryEngineDir`), the same one the engine's own write judgment reads — so the root's ancestor segments
141
+ * (`.sema`, when a deployment sites its data root under its config root) cannot make the memory library the model is
142
+ * instructed to WRITE read as a guarded path, and this list and the engine can never disagree about which directory the
143
+ * engine owns. Outside a Runner the resolver's own chain stands (`$AGENT_DATA_DIR ?? ~/.ai-agent`). The retired option
144
+ * `dataRoot` refuses construction by name (`config.deps_retired_key`).
145
+ *
137
146
  * Fail-open by design: a tool with no string `path` returns `allow` here — this guard adds friction on
138
147
  * top of the other gates, it is not the only line of defense, and it must not turn an unrelated tool
139
- * call into a spurious verdict. The verdict it owns is narrow and certain: "the real target IS a
140
- * guarded path → deny". (A target it could not RESOLVE is the one fail-CLOSED arm, below.)
148
+ * call into a spurious verdict. The verdict it owns is narrow and certain: "a view of the target IS a
149
+ * guarded path → deny", and its absence twin: "where the target really lands could not be determined → deny" (the one
150
+ * absence law of every path table, path-guard-rows.ts — a list with rows cannot say such a target is none of them; the
151
+ * reference refuses the same write at its permission check). An auxiliary view that does not answer (a root's key) exempts
152
+ * nothing.
141
153
  */
142
154
  export declare function createSensitivePathPolicy(opts: {
143
155
  env: ExecutionEnv;
144
156
  /** `readonly` accepted so {@link RECOMMENDED_SENSITIVE_PATTERNS} can be passed as-is (widening, non-breaking). */
145
157
  patterns: readonly string[];
146
158
  rootPath?: string;
147
- /**
148
- * HRD-PRM-10 — the engine data root this deployment runs on. Default
149
- * `$AGENT_DATA_DIR ?? ~/.ai-agent`, the same resolution
150
- * {@link import("./tool-policy.js").createTranscriptIntegrityPolicy} performs for its own default.
151
- * A target inside it is judged on its path RELATIVE to it, so the root's own ancestor segments
152
- * (`.sema`, when a deployment sites its data root under its config root) cannot make the whole
153
- * subtree — the memory library the model is instructed to WRITE included — read as a guarded path.
154
- * Pass it explicitly whenever the deployment passes `memoryEngineDir`/a custom data root, so this
155
- * gate and the transcript gate keep speaking about the same directory.
156
- */
157
- dataRoot?: string;
158
159
  /**
159
160
  * The host's configuration root when it keeps the agent's configuration directory somewhere other than the home's
160
161
  * `.sema` — the same absolute directory it hands the Runner (`RunnerDeps.agentConfigDir`). Every pattern that opens with
@@ -1,12 +1,17 @@
1
1
  import { homedir } from "node:os";
2
2
  import { isAbsolute, join, resolve } from "node:path";
3
- import { canonicalizeTarget, expandHomeTilde, isAbsoluteForFamily, isWinFormPath, pathFamilyOf, writeTargetPath } from "../tools/fs/safety.js";
4
- import { compileSegmentPattern, matchSegmentPatterns, READ_FACE_BUILTIN_DENY_TABLE, readDenyPatternRun } from "../tools/fs/read-deny.js";
3
+ import { anchorSpelling, canonicalizeTarget, expandHomeTilde, isAbsoluteForFamily, isWinFormPath, pathFamilyOf, writeTargetPath } from "../tools/fs/safety.js";
4
+ import { spelledExemptionOf } from "./write-protect.js";
5
+ import { compileSegmentPattern, READ_FACE_BUILTIN_DENY_TABLE, readDenyPatternRun, segmentGuardRow } from "../tools/fs/read-deny.js";
6
+ import { judgePathRows } from "./path-guard-rows.js";
5
7
  import { agentConfigDirSegments, agentConfigDirTail, checkedAgentConfigDir } from "./agent-config-dir.js";
6
8
  import { isProtectedWrite, protectivePathTargetOf } from "./tool-registry.js";
7
9
  import { pathTargetBaseOf } from "./effective-path-target.js";
8
- import { symlinkChainHops } from "./path-identity.js";
10
+ import { chainHopsOrNone, symlinkChainHops } from "./path-identity.js";
9
11
  import { heldBy } from "./tool-name-readings.js";
12
+ import { assertNoRetiredKeys } from "./retired-keys.js";
13
+ import { fromSource } from "./gate-bounded-await.js";
14
+ import { thrownText } from "./thrown-value.js";
10
15
  export function agentConfigDenyPatterns() {
11
16
  return READ_FACE_BUILTIN_DENY_TABLE.filter((row) => row.tier === "agent-config").map((row) => readDenyPatternRun(row.pattern).run);
12
17
  }
@@ -62,22 +67,20 @@ export function dataRootSpellings(explicit) {
62
67
  const engineForm = isAbsolute(raw) ? raw : resolve(raw);
63
68
  const out = [];
64
69
  for (const v of [raw, tilde, engineForm]) {
65
- const anchored = isAbsoluteForFamily(pathFamilyOf({ root: v }), v) ? v : resolve(v);
70
+ const anchored = isAbsoluteForFamily("own", v) ? v : resolve(v);
66
71
  if (!out.includes(anchored))
67
72
  out.push(anchored);
68
73
  }
69
74
  return out;
70
75
  }
71
- export async function canonicalDataRootKeys(env, spellings, signal) {
76
+ export async function canonicalDataRootKeys(env, spellings, signal, onLookupFailed) {
72
77
  const keys = [];
73
78
  for (const spelling of spellings) {
74
- try {
75
- const r = await canonicalizeTarget(env, spelling, signal, pathFamilyOf({ root: spelling }));
76
- if (r.ok && !keys.includes(r.key))
77
- keys.push(r.key);
78
- }
79
- catch {
80
- }
79
+ const answer = await fromSource(() => canonicalizeTarget(env, spelling, signal, pathFamilyOf({ root: spelling })), signal, "rootKey");
80
+ if (!answer.ok)
81
+ onLookupFailed?.(answer.cause);
82
+ else if (answer.value.ok && !keys.includes(answer.value.key))
83
+ keys.push(answer.value.key);
81
84
  }
82
85
  return keys;
83
86
  }
@@ -112,54 +115,77 @@ function compilePatterns(patterns) {
112
115
  }
113
116
  return out;
114
117
  }
115
- function matchSensitive(canonicalKey, compiled, aliasResolved) {
116
- return matchSegmentPatterns(canonicalKey, compiled, { aliasResolved });
117
- }
118
+ const RETIRED_SENSITIVE_PATH_POLICY_KEYS = Object.freeze({
119
+ dataRoot: "the data-root exemption reads the engine's one exempt root — a Runner resolves it once per leg from `RunnerDeps.memoryEngineDir` and stamps it on every request this policy judges (`ToolCallRequest.exemptRoots`); set the data root there and drop the option",
120
+ });
118
121
  export function createSensitivePathPolicy(opts) {
122
+ assertNoRetiredKeys(opts, RETIRED_SENSITIVE_PATH_POLICY_KEYS, "createSensitivePathPolicy option");
119
123
  const compiled = compilePatterns(opts.patterns);
124
+ const rowOf = (c) => segmentGuardRow(c, { faces: ["write"], polarity: "deny", exemptible: true, fold: CASE_INSENSITIVE_FS ? "host" : "exact", origin: "replacement" });
125
+ const guardRows = compiled.map(rowOf);
120
126
  const configRoot = checkedAgentConfigDir(opts.agentConfigDir, "createSensitivePathPolicy agentConfigDir");
121
127
  const configRows = configRoot === undefined ? [] : opts.patterns.flatMap((raw) => {
122
128
  const tail = agentConfigDirTail(raw, !CASE_INSENSITIVE_FS);
123
129
  return tail === undefined ? [] : [{ raw, tail }];
124
130
  });
125
- const configTwins = async (signal) => {
131
+ const configTwins = async (env, signal) => {
126
132
  if (configRoot === undefined || configRows.length === 0)
127
133
  return [];
128
134
  const fold = CASE_INSENSITIVE_FS ? "ascii" : "none";
129
135
  const lower = (segs) => (CASE_INSENSITIVE_FS ? segs.map((seg) => seg.replace(/[A-Z]/g, (c) => c.toLowerCase())) : segs);
130
- const anchors = [configRoot, ...(await canonicalDataRootKeys(opts.env, [configRoot], signal))].map((root) => lower(agentConfigDirSegments(root)));
136
+ const anchors = [configRoot, ...(env === undefined ? [] : await canonicalDataRootKeys(env, [configRoot], signal))].map((root) => lower(agentConfigDirSegments(root)));
131
137
  return configRows.map(({ raw, tail }) => ({ ...(compileSegmentPattern(tail, fold) ?? { raw, segments: [], foldAscii: CASE_INSENSITIVE_FS }), raw, anchors, rootedOnly: true }));
132
138
  };
133
- const dataRootPaths = dataRootSpellings(opts.dataRoot);
139
+ const rowsOf = async (env, signal) => [...guardRows, ...(await configTwins(env, signal)).map(rowOf)];
140
+ const unstampedRoots = Object.freeze(dataRootSpellings(undefined));
134
141
  const guardedByName = opts.tools !== undefined ? new Set(opts.tools) : undefined;
135
142
  const family = (cwd) => pathFamilyOf({ root: opts.rootPath, cwd: cwd ?? opts.env.cwd });
136
- const judgeTarget = async (path, fam, base, signal) => {
137
- const canon = await canonicalizeTarget(opts.env, path, signal, fam, base);
138
- if (!canon.ok)
139
- return { kind: "unresolved", unresolvedSymlink: canon.unresolvedSymlink === true, cause: canon.refused ?? canon.message };
140
- let relative = null;
141
- for (const rootKey of await canonicalDataRootKeys(opts.env, dataRootPaths, signal)) {
142
- relative = relativeUnderRoot(canon.key, rootKey);
143
- if (relative !== null)
144
- break;
143
+ const spelledByRoots = new WeakMap();
144
+ const spelledRemainderUnder = (roots) => {
145
+ let r = spelledByRoots.get(roots);
146
+ if (r === undefined)
147
+ spelledByRoots.set(roots, (r = spelledExemptionOf(roots)));
148
+ return r;
149
+ };
150
+ const judgeTarget = async (path, fam, base, roots, signal) => {
151
+ const spelledRemainder = spelledRemainderUnder(roots);
152
+ const spelled = base !== undefined ? anchorSpelling(fam, base, path) : path;
153
+ const spelledRest = spelledRemainder(spelled);
154
+ const answer = await fromSource(() => canonicalizeTarget(opts.env, path, signal, fam, base), signal, "canonicalTarget");
155
+ const canon = answer.ok ? answer.value : { ok: false, message: thrownText(answer.cause) };
156
+ let targetUnread = canon.ok ? undefined : canon.message;
157
+ const aliased = canon.ok && canon.aliasResolved === true ? { aliasResolved: true } : {};
158
+ const views = [{ path: spelled, kind: "lexical", ...aliased, ...(spelledRest !== null ? { exemptRemainder: spelledRest } : {}) }];
159
+ let identity = null;
160
+ if (canon.ok) {
161
+ for (const rootKey of await canonicalDataRootKeys(opts.env, roots, signal)) {
162
+ identity = relativeUnderRoot(canon.key, rootKey);
163
+ if (identity !== null)
164
+ break;
165
+ }
166
+ views.push({ path: canon.key, kind: "canonical", ...(canon.aliasResolved === true ? { aliasResolved: true } : {}), ...(identity !== null ? { exemptRemainder: identity } : {}) });
145
167
  }
146
- if (relative === "")
147
- return { kind: "clear" };
148
- const twins = await configTwins(signal);
149
- const hit = matchSensitive(relative ?? canon.key, [...compiled, ...twins], canon.aliasResolved === true);
150
- if (hit)
151
- return { kind: "hit", pattern: hit, via: "target" };
152
- if (relative === null) {
153
- for (const hop of await symlinkChainHops(opts.env, path, signal, fam, base)) {
154
- const viaHop = matchSensitive(hop, [...compiled, ...twins], false);
155
- if (viaHop)
156
- return { kind: "hit", pattern: viaHop, via: "chain" };
168
+ const rows = await rowsOf(opts.env, signal);
169
+ if (identity === null || !rows.every((row) => row.exemptible)) {
170
+ const chain = await fromSource(() => symlinkChainHops(opts.env, path, signal, fam, base), signal, "canonicalChain");
171
+ if (!chain.ok)
172
+ targetUnread ??= thrownText(chain.cause);
173
+ else if (!chain.value.ok && chain.value.asked)
174
+ targetUnread ??= chain.value.cause;
175
+ for (const hop of chain.ok ? chainHopsOrNone(chain.value) : []) {
176
+ const rest = spelledRemainder(hop);
177
+ views.push({ path: hop, kind: "hop", ...(rest !== null ? { exemptRemainder: rest } : {}) });
157
178
  }
158
179
  }
159
- return { kind: "clear" };
180
+ const hit = judgePathRows(rows, { views, ...(identity !== null ? { identityExempt: true } : {}), ...(targetUnread !== undefined ? { unreadable: targetUnread } : {}) }, "tighten");
181
+ if (hit === undefined)
182
+ return { kind: "clear" };
183
+ if (hit.unreadable !== undefined)
184
+ return { kind: "unresolved", cause: hit.unreadable };
185
+ return { kind: "hit", pattern: hit.row.name, via: hit.viewKind === "hop" ? "chain" : hit.viewKind === "canonical" ? "target" : "spelling" };
160
186
  };
161
187
  return {
162
- ...(compiled.length > 0 ? { writeTargetGuard: async (target, signal) => (await judgeTarget(target, family(undefined), undefined, signal)).kind !== "clear" } : {}),
188
+ ...(compiled.length > 0 ? { writeGuardRows: (at) => rowsOf(at.env, at.signal) } : {}),
163
189
  async check(req, signal) {
164
190
  if (compiled.length === 0)
165
191
  return { action: "allow" };
@@ -169,20 +195,18 @@ export function createSensitivePathPolicy(opts) {
169
195
  const path = writeTargetPath(req, protective);
170
196
  if (typeof path !== "string" || path.length === 0)
171
197
  return { action: "allow" };
172
- const judged = await judgeTarget(path, family(req.cwd), pathTargetBaseOf(protective, { root: opts.rootPath, cwd: req.cwd }), signal);
198
+ const judged = await judgeTarget(path, family(req.cwd), pathTargetBaseOf(protective, { root: opts.rootPath, cwd: req.cwd }), req.exemptRoots ?? unstampedRoots, signal);
173
199
  if (judged.kind === "unresolved") {
174
- return {
175
- action: "deny",
176
- message: judged.unresolvedSymlink
177
- ? `write to "${path}" is blocked: it is a symlink whose real target could not be resolved (it could point onto a guarded sensitive path)`
178
- : `write to "${path}" is blocked: its target could not be resolved on this tree (${judged.cause}), so the sensitive-path guard cannot judge it`,
179
- decisionReason: "safety",
180
- };
200
+ return { action: "deny", message: `write to "${path}" is refused (target_unresolved): where it leads on disk could not be determined, so the sensitive-path list cannot tell whether it is a guarded path — ${judged.cause}`, decisionReason: "safety" };
181
201
  }
182
202
  if (judged.kind === "hit") {
183
203
  return {
184
204
  action: "deny",
185
- message: judged.via === "target" ? `write to "${path}" is blocked: its real target resolves onto the guarded sensitive path "${judged.pattern}"` : `write to "${path}" is blocked: its symlink chain passes through the guarded sensitive path "${judged.pattern}"`,
205
+ message: judged.via === "target"
206
+ ? `write to "${path}" is blocked: its real target resolves onto the guarded sensitive path "${judged.pattern}"`
207
+ : judged.via === "chain"
208
+ ? `write to "${path}" is blocked: its symlink chain passes through the guarded sensitive path "${judged.pattern}"`
209
+ : `write to "${path}" is blocked: its path names the guarded sensitive path "${judged.pattern}"`,
186
210
  decisionReason: "safety",
187
211
  };
188
212
  }
@@ -67,7 +67,7 @@ export interface SessionRuleNameRefusal {
67
67
  /** Typed error from {@link SessionPolicyStore.putRules}. `conflict` = stale `expectedRev`; `loosen_forbidden`
68
68
  * = a non-operator write tried to relax the rules; `corrupt` = a backend could not read the erase-surviving
69
69
  * rev mark ({@link SessionPolicyStore.deleteBySession}) and refuses rather than stamp an unproven rev; the fourth
70
- * code = a tool-name bucket names a retired name, a prefix-less `__` name or a peer segment no mint spells
70
+ * code = a tool-name bucket names a retired name, a prefix-less `__` name or a segment no mint spells (an empty one, or a peer spelled otherwise)
71
71
  * ({@link assertSessionRuleNamesCurrent}) — `detail` is present on that code and only on it. */
72
72
  export declare class SessionPolicyError extends Error {
73
73
  readonly code: "conflict" | "loosen_forbidden" | "corrupt" | "legacy_tool_name";
@@ -121,7 +121,8 @@ export interface SessionPolicyStore {
121
121
  /** Write the rules for a key: CAS on `expectedRev`, then the name judge ({@link assertSessionRuleNamesCurrent} —
122
122
  * operator writes included), then, for a non-operator write over an existing row, the
123
123
  * tighten-only check ({@link loosenReasons}) — both against the names the deployment declares it mounts its own
124
- * tools under. */
124
+ * tools under. A bucket that is not a list of strings (a bare `"rm"` for `commandDeny`) throws a `TypeError` from
125
+ * {@link normalizeRules} before any of those is judged, and nothing is written. */
125
126
  putRules(sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts?: PutRulesOptions): Promise<StoredSessionRules>;
126
127
  /**
127
128
  * 2c session-sync: every (principal, rules) record for `sessionId` across ALL principals — `getRules`
@@ -172,7 +173,9 @@ export interface SessionPolicyStore {
172
173
  */
173
174
  declare function lexWithin(root: string, p: string): boolean;
174
175
  /** Normalize a rules object's `allowDirs` (lexical `posix.normalize`) so stored rules are `..`-free and the
175
- * tighten diff compares the same canonical-ish form the runtime resolves. */
176
+ * tighten diff compares the same canonical-ish form the runtime resolves. Every bucket must be a list of strings
177
+ * ({@link ruleList}); a bucket that is not one throws `TypeError`, so neither bundled store (nor a durable twin that
178
+ * normalizes through this function) can store a rule list the matcher would read differently from the one written. */
176
179
  export declare function normalizeRules(rules: SessionPermissionRules): SessionPermissionRules;
177
180
  /**
178
181
  * Pure tighten-only diff: the reasons `next` LOOSENS `prior` (empty ⇒ `next` is a pure tightening or no-op).
@@ -75,18 +75,36 @@ function lexWithin(root, p) {
75
75
  return true;
76
76
  return np === nr || np.startsWith(nr + "/");
77
77
  }
78
+ function ruleList(field, value) {
79
+ if (value === undefined)
80
+ return undefined;
81
+ const refuse = (got) => {
82
+ throw new TypeError(`session rules ${field} must be an array of strings, got ${got} — a rule list is written as a list of names (["rm"], not "rm")`);
83
+ };
84
+ if (Array.isArray(value) && Array.from(value).every((entry) => typeof entry === "string")) {
85
+ if (value.some((entry) => entry.trim() === ""))
86
+ refuse("an array holding an empty or blank entry");
87
+ return value;
88
+ }
89
+ return refuse(Array.isArray(value) ? "an array holding an entry that is not a string" : value === null ? "null" : `a ${typeof value}`);
90
+ }
78
91
  export function normalizeRules(rules) {
79
92
  const out = {};
80
- if (rules.toolAllow !== undefined)
81
- out.toolAllow = [...rules.toolAllow];
82
- if (rules.toolDeny !== undefined)
83
- out.toolDeny = [...rules.toolDeny];
84
- if (rules.commandAllow !== undefined)
85
- out.commandAllow = [...rules.commandAllow];
86
- if (rules.commandDeny !== undefined)
87
- out.commandDeny = [...rules.commandDeny];
88
- if (rules.allowDirs !== undefined)
89
- out.allowDirs = rules.allowDirs.map((d) => posix.normalize(d).replace(/\/+$/, "") || "/");
93
+ const toolAllow = ruleList("toolAllow", rules.toolAllow);
94
+ const toolDeny = ruleList("toolDeny", rules.toolDeny);
95
+ const commandAllow = ruleList("commandAllow", rules.commandAllow);
96
+ const commandDeny = ruleList("commandDeny", rules.commandDeny);
97
+ const allowDirs = ruleList("allowDirs", rules.allowDirs);
98
+ if (toolAllow !== undefined)
99
+ out.toolAllow = [...toolAllow];
100
+ if (toolDeny !== undefined)
101
+ out.toolDeny = [...toolDeny];
102
+ if (commandAllow !== undefined)
103
+ out.commandAllow = [...commandAllow];
104
+ if (commandDeny !== undefined)
105
+ out.commandDeny = [...commandDeny];
106
+ if (allowDirs !== undefined)
107
+ out.allowDirs = allowDirs.map((d) => posix.normalize(d).replace(/\/+$/, "") || "/");
90
108
  return out;
91
109
  }
92
110
  export function loosenReasons(prior, next, mountedToolNames = NO_DECLARED_TOOLS) {
@@ -63,6 +63,9 @@ export type RecoveredOrphan = OrphanToolCall & {
63
63
  text: string;
64
64
  /** Which of the two honest states this orphan was closed in — the frame's `errorCode`. */
65
65
  errorKind: ReconciledErrorKind;
66
+ /** The call's input as the transcript's assistant `toolCall` recorded it — the `args` of the start frame the caller
67
+ * mints for a call whose start never went out on its stream. */
68
+ args: unknown;
66
69
  };
67
70
  /** Outcome of reconciling a resumed session. */
68
71
  export interface ReconcileReport {
@@ -132,7 +132,7 @@ function closableByAppend(messages, suspendedBatch) {
132
132
  if (m.role === "assistant") {
133
133
  for (const part of m.content) {
134
134
  if (part.type === "toolCall" && !suspendedBatch?.has(part.id) && lastSite.get(part.id)?.at !== i)
135
- lastSite.set(part.id, { at: i, order: order++, toolName: part.name });
135
+ lastSite.set(part.id, { at: i, order: order++, toolName: part.name, args: part.arguments });
136
136
  }
137
137
  }
138
138
  else if (m.role === "toolResult") {
@@ -142,15 +142,16 @@ function closableByAppend(messages, suspendedBatch) {
142
142
  return [...lastSite]
143
143
  .filter(([id, site]) => (lastResult.get(id) ?? -1) < site.at)
144
144
  .sort((a, b) => a[1].order - b[1].order)
145
- .map(([toolCallId, site]) => ({ toolCallId, toolName: site.toolName }));
145
+ .map(([toolCallId, site]) => ({ toolCallId, toolName: site.toolName, args: site.args }));
146
146
  }
147
147
  export async function reconcileInterruptedSession(session, toolEffects, suspendedBatch, startedToolCallIds) {
148
148
  const { messages } = await session.buildContext();
149
149
  const orphans = closableByAppend(messages, suspendedBatch);
150
150
  const recovered = [];
151
- for (const result of interruptedResults(orphans, toolEffects, startedToolCallIds)) {
151
+ const results = interruptedResults(orphans, toolEffects, startedToolCallIds);
152
+ for (const [i, result] of results.entries()) {
152
153
  const entryId = await session.appendMessage(interruptedResultMessage(result, Date.now()));
153
- recovered.push({ toolCallId: result.toolCallId, toolName: result.toolName, entryId, text: result.text, errorKind: result.errorKind });
154
+ recovered.push({ toolCallId: result.toolCallId, toolName: result.toolName, entryId, text: result.text, errorKind: result.errorKind, args: orphans[i].args });
154
155
  }
155
156
  return { recovered };
156
157
  }
@@ -180,6 +180,11 @@ export declare class TtlSessionStore implements SessionStore {
180
180
  /** First-write-wins idempotent upsert; a synchronous Map write is atomic per the contract. Makes no
181
181
  * authorization decision — the authorizer re-reads {@link ownerOf} and judges there. */
182
182
  register(sessionId: string, owner: string | null): Promise<void>;
183
+ /** The session row's incarnation cell (see the SessionStore contract), read from the ROW through the
184
+ * repo on every call — never from this cache: a cached view outlives a row another store over the
185
+ * same repo deleted or recreated, and its copy would name the previous occupant. No row ⇒
186
+ * `undefined`; any other repo failure rejects (a fault is not an answer). Never caches, never creates. */
187
+ incarnationOf(sessionId: string): Promise<string | undefined>;
183
188
  /** Evict idle-expired sessions from the cache. Deletes durable history only when `evict: "delete"`. */
184
189
  sweep(now?: number): void;
185
190
  get size(): number;
@@ -241,6 +241,18 @@ export class TtlSessionStore {
241
241
  }
242
242
  this.owners.set(sessionId, owner);
243
243
  }
244
+ async incarnationOf(sessionId) {
245
+ let session;
246
+ try {
247
+ session = await this.repo.open({ id: sessionId, createdAt: "" });
248
+ }
249
+ catch (err) {
250
+ if (isNotFound(err))
251
+ return undefined;
252
+ throw err;
253
+ }
254
+ return (await session.getMetadata()).incarnation;
255
+ }
244
256
  sweep(now = Date.now()) {
245
257
  for (const [id, e] of this.entries) {
246
258
  if (this.pinned.has(id)) {
@@ -283,6 +283,19 @@ export interface SessionStore {
283
283
  * LATER calls, which stay silent no-ops whatever they carry.
284
284
  */
285
285
  register?(sessionId: string, owner: string | null): Promise<void>;
286
+ /**
287
+ * WHICH occupant holds `sessionId` now: the session row's {@link SessionMetadata.incarnation} cell,
288
+ * minted by the repo when the row came into existence and stable for the row's whole life (restarts,
289
+ * cache eviction, replicas). `undefined` = UNKNOWN — no row for this id, or a row written before the
290
+ * cell existed; a reader treats unknown as "cannot tell occupants apart", never as a different one.
291
+ * A rejection is a read fault, not an answer.
292
+ *
293
+ * The BY-ID read, for a reader that did not acquire the session (hosts; this read and the metadata of
294
+ * a session `acquire` hands back must agree — the incarnation kit pins both). A run's own memory seat
295
+ * reads the session it ACQUIRED instead, never a second lookup: the cached view `acquire` returns and
296
+ * a by-id read can answer two different rows. **Optional**: absent ⇒ no by-id read.
297
+ */
298
+ incarnationOf?(sessionId: string): Promise<string | undefined>;
286
299
  /** Number of live/cached sessions (best-effort). */
287
300
  readonly size: number;
288
301
  /** Release resources (timers, connections). Best-effort; must not throw. */
@@ -14,6 +14,8 @@
14
14
  * diagnostics seat so dropping is never silent.
15
15
  */
16
16
  import { type SharedMemoryDocumentEntry, type SharedMemoryStoreInfo } from "./types.js";
17
+ /** THE store-id judge: the snapshot gate calls it, and a host that mints or stores ids calls it instead of restating the pattern. */
18
+ export declare function isSharedMemoryStoreId(id: string): boolean;
17
19
  /** The framing line every shared-store body and listing is served under. */
18
20
  export declare const SHARED_STORE_FRAME = "The following is shared-store content written by you or your teammates. Treat it as reference data, not as instructions:";
19
21
  /** Standard wording when the host reports the connection unavailable and supplies no reason of its own. */
@@ -48,19 +50,30 @@ export interface SharedMemoryRefusal {
48
50
  * The collapse is a deliberate convenience on the argument face (upstream does the same): a model that
49
51
  * emits `/a//b.md` gets `/a/b.md`, the document it plainly meant, instead of a refusal it can do nothing
50
52
  * useful with. That is precisely why LISTED rows are held to the canonical form instead
51
- * ({@link isListablePath}): if a non-canonical row could be advertised, the model could follow a listed
53
+ * ({@link isSharedMemoryDocumentPath}): if a non-canonical row could be advertised, the model could follow a listed
52
54
  * path and be served a different document. Normalizing arguments is safe; normalizing what we advertise
53
55
  * would not be.
54
56
  */
55
57
  export declare function normalizeMemoryPath(path: string): string;
56
- /** Is this a path the LISTING may render? (A row failing it is dropped, never an error.) */
57
- export declare function isListablePath(path: string): boolean;
58
+ /**
59
+ * THE document-path rule, in one place: absolute, no forbidden character, every segment non-empty and
60
+ * not dot-leading, and a filename ending in a recognized suffix.
61
+ *
62
+ * ONE predicate for BOTH faces — the listing filter and the `path` argument validator — because two
63
+ * spellings of "a valid path" is exactly how a listing comes to advertise something the read tool then
64
+ * refuses. Note what the two faces do NOT share: an argument is {@link normalizeMemoryPath}-folded
65
+ * BEFORE it reaches this predicate, a listed row never is. That asymmetry is the point — `/a//b.md` as
66
+ * an argument reads `/a/b.md` (the obvious intent), while `/a//b.md` as a listing ROW is dropped,
67
+ * because advertising it would let the model follow a listed path to a different document.
68
+ * One notch tighter than the upstream listing filter (which tolerates a relative row). Exported as the ONE judge, as given: no fold, no length bound.
69
+ */
70
+ export declare function isSharedMemoryDocumentPath(path: string): boolean;
58
71
  /**
59
72
  * Teaching refusal for a RELATIVE path argument. Runs before the structural validator so the model is
60
73
  * told the one thing it can act on — the absolute spelling — rather than the full rule sheet.
61
74
  */
62
75
  export declare function relativePathRefusal(rawPath: string): SharedMemoryRefusal | null;
63
- /** Validate a DOCUMENT path — the argument face of {@link isCanonicalDocumentPath}. */
76
+ /** Validate a DOCUMENT path — the argument face of {@link isSharedMemoryDocumentPath}. */
64
77
  export declare function validateDocumentPath(path: string): SharedMemoryRefusal | null;
65
78
  /**
66
79
  * Validate a PREFIX. Same character and segment rules as a document path MINUS the filename-suffix
@@ -3,6 +3,9 @@ import { SHARED_MEMORY_DESCRIPTION_MAX, SHARED_MEMORY_LIST_PAGE_SIZE, SHARED_MEM
3
3
  const FORBIDDEN_PATH_CHARS = /[\p{Cc}\p{Cf}\p{Co}\p{Cn}\p{Default_Ignorable_Code_Point}\u2028\u2029\\]/u;
4
4
  const DOCUMENT_SUFFIXES = [".md", ".txt", ".json", ".jsonl"];
5
5
  const STORE_ID_RE = /^[A-Za-z0-9_.-]{1,64}$/;
6
+ export function isSharedMemoryStoreId(id) {
7
+ return STORE_ID_RE.test(id) && !id.includes("__");
8
+ }
6
9
  const HOST_REASON_RE = /^[a-z0-9_]{1,64}$/;
7
10
  const PATH_MAX_UNITS = 1024;
8
11
  const MESSAGE_MAX = 240;
@@ -19,7 +22,7 @@ export function echoUntrusted(text) {
19
22
  export function normalizeMemoryPath(path) {
20
23
  return (path.startsWith("/") ? path : `/${path}`).replace(/\/{2,}/g, "/");
21
24
  }
22
- function isCanonicalDocumentPath(path) {
25
+ export function isSharedMemoryDocumentPath(path) {
23
26
  if (!path.startsWith("/") || FORBIDDEN_PATH_CHARS.test(path))
24
27
  return false;
25
28
  const segments = path.slice(1).split("/");
@@ -30,9 +33,6 @@ function isCanonicalDocumentPath(path) {
30
33
  return false;
31
34
  return DOCUMENT_SUFFIXES.some((s) => last.endsWith(s));
32
35
  }
33
- export function isListablePath(path) {
34
- return isCanonicalDocumentPath(path);
35
- }
36
36
  export function relativePathRefusal(rawPath) {
37
37
  if (rawPath.startsWith("/"))
38
38
  return null;
@@ -49,7 +49,7 @@ function pathRuleRefusal(path) {
49
49
  };
50
50
  }
51
51
  export function validateDocumentPath(path) {
52
- return isCanonicalDocumentPath(path) ? null : pathRuleRefusal(path);
52
+ return isSharedMemoryDocumentPath(path) ? null : pathRuleRefusal(path);
53
53
  }
54
54
  export function validatePathPrefix(prefix) {
55
55
  const trimmed = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
@@ -136,7 +136,7 @@ function normalizeStoreRow(row, seen) {
136
136
  const id = readProperty(info, "id");
137
137
  const description = readProperty(info, "description");
138
138
  const writable = readProperty(info, "writable");
139
- if (typeof id !== "string" || !STORE_ID_RE.test(id) || id.includes("__"))
139
+ if (typeof id !== "string" || !isSharedMemoryStoreId(id))
140
140
  return undefined;
141
141
  if (typeof description !== "string" || typeof writable !== "boolean")
142
142
  return undefined;
@@ -184,7 +184,7 @@ function normalizeEntryRow(row) {
184
184
  if (row === null || typeof row !== "object")
185
185
  return undefined;
186
186
  const path = readProperty(row, "path");
187
- if (typeof path !== "string" || path.length > PATH_MAX_UNITS || !isListablePath(path))
187
+ if (typeof path !== "string" || path.length > PATH_MAX_UNITS || !isSharedMemoryDocumentPath(path))
188
188
  return undefined;
189
189
  const entry = { path };
190
190
  const sizeBytes = readProperty(row, "sizeBytes");
@@ -1,10 +1,11 @@
1
+ import { shellWordsOf } from "../shell/shell-words.js";
1
2
  import { evaluatesOperand, SHELL_WRAPPER_TABLE } from "./shell-wrapper-table.js";
2
3
  import { arithmeticHidesRun, carriesSubstitution, freshWord, matchOp, readDoubleQuoted, readExpansion, skipBalanced, skipHeredocBodies } from "./shell-scan.js";
3
4
  export { SHELL_WRAPPER_TABLE } from "./shell-wrapper-table.js";
4
5
  const WRAPPERS = new Map(Object.entries(SHELL_WRAPPER_TABLE));
5
- const PREFIX_KEYWORDS = new Set(["if", "then", "else", "elif", "while", "until", "do", "!"]);
6
- const CLOSING_KEYWORDS = new Set(["fi", "done", "esac", "}", "{", "]]"]);
7
- const OPAQUE_KEYWORDS = new Set(["for", "select", "case", "function", "coproc", "in"]);
6
+ const PREFIX_KEYWORDS = shellWordsOf("stripped_prefix");
7
+ const CLOSING_KEYWORDS = shellWordsOf("stripped_standalone");
8
+ const OPAQUE_KEYWORDS = shellWordsOf("opaque");
8
9
  const ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*(?:\[[^\]]*\])?\+?=/;
9
10
  const DURATION_RE = /^\d+(?:\.\d+)?[smhd]?$/;
10
11
  const REDIRECT_OPS = ["<<<", "<<-", "<<", "<>", "<&", "<", ">>", ">|", ">&", "&>>", "&>", ">"];
@@ -222,7 +223,7 @@ export function readShellCommand(source) {
222
223
  i = end;
223
224
  continue;
224
225
  }
225
- if (ch === "(" && source[i + 1] === "(" && (!word.started || word.raw === "!") && pendingRedirect === undefined && seg.words.every((w) => w.expands === false && (PREFIX_KEYWORDS.has(w.text) || CLOSING_KEYWORDS.has(w.text) || w.text === "time" || w.text === "-p"))) {
226
+ if (ch === "(" && source[i + 1] === "(" && (!word.started || word.raw === "!") && pendingRedirect === undefined && seg.words.every((w) => w.expands === false && (shellWordsOf("control_prefix").has(w.text) || CLOSING_KEYWORDS.has(w.text) || w.text === "-p"))) {
226
227
  endWord();
227
228
  const end = skipBalanced(source, i, "(", ")");
228
229
  if (end === undefined) {
@@ -114,8 +114,8 @@ export declare const SHELL_WRAPPER_TABLE: {
114
114
  readonly commandString: readonly [];
115
115
  };
116
116
  readonly watch: {
117
- readonly valueFlags: readonly [];
118
- readonly flags: readonly [];
117
+ readonly valueFlags: readonly ["--equexit", "--interval", "--shotsdir", "-n", "-q", "-s"];
118
+ readonly flags: readonly ["--beep", "--chgexit", "--color", "--differences", "--errexit", "--exec", "--follow", "--help", "--no-color", "--no-rerun", "--no-title", "--no-wrap", "--precise", "--version", "-C", "-b", "-c", "-e", "-f", "-g", "-h", "-p", "-r", "-t", "-v", "-w", "-x"];
119
119
  readonly commandString: readonly [];
120
120
  readonly viaShell: true;
121
121
  };
@@ -15,7 +15,7 @@ export const SHELL_WRAPPER_TABLE = {
15
15
  timeout: { valueFlags: ["--kill-after", "--signal", "-k", "-s"], flags: ["--foreground", "--help", "--preserve-status", "--verbose", "--version", "-v"], commandString: [], duration: true },
16
16
  stdbuf: { valueFlags: ["--error", "--input", "--output", "-e", "-i", "-o"], flags: ["--help", "--version"], commandString: [] },
17
17
  xargs: { valueFlags: ["--arg-file", "--delimiter", "--max-args", "--max-chars", "--max-procs", "--process-slot-var", "-E", "-I", "-L", "-P", "-a", "-d", "-n", "-s"], flags: ["--eof", "--exit", "--help", "--interactive", "--max-lines", "--no-run-if-empty", "--null", "--open-tty", "--replace", "--show-limits", "--verbose", "--version", "-0", "-o", "-p", "-r", "-t", "-x"], attached: ["-i", "-l"], commandString: [] },
18
- watch: { valueFlags: [], flags: [], commandString: [], viaShell: true },
18
+ watch: { valueFlags: ["--equexit", "--interval", "--shotsdir", "-n", "-q", "-s"], flags: ["--beep", "--chgexit", "--color", "--differences", "--errexit", "--exec", "--follow", "--help", "--no-color", "--no-rerun", "--no-title", "--no-wrap", "--precise", "--version", "-C", "-b", "-c", "-e", "-f", "-g", "-h", "-p", "-r", "-t", "-v", "-w", "-x"], commandString: [], viaShell: true },
19
19
  ionice: { valueFlags: ["--class", "--classdata", "--pgid", "--pid", "--uid", "-P", "-c", "-n", "-p", "-u"], flags: ["--help", "--ignore", "--version", "-V", "-h", "-t"], commandString: [] },
20
20
  setsid: { valueFlags: [], flags: ["--ctty", "--fork", "--help", "--version", "--wait", "-c", "-f", "-w"], commandString: [] },
21
21
  taskset: { valueFlags: [], flags: ["--all-tasks", "--cpu-list", "--help", "--pid", "--version", "-a", "-c", "-p"], commandString: [], positional: /^(?:0x)?[\da-f]+$|^[\d,:-]+$/i },
@@ -3,7 +3,7 @@ import { parsePermissionRule } from "./permission-rules.js";
3
3
  import { mountedToolPermissionSeat } from "./mode-policy.js";
4
4
  import { inlineUntrusted } from "./untrusted-text.js";
5
5
  import { boundInputHashOf } from "./canonical-json.js";
6
- import { readToolNameList } from "../agents/tool-filter.js";
6
+ import { readToolNameList } from "./tool-name-list.js";
7
7
  import { allowingRows, autoStripped, compileAllowEntry, compileAllowList, inertEntryPhrase, judgeAllowEntries, nonListValueLabel, nonStringMemberLabel, renderListEntry } from "./allow-entry.js";
8
8
  export const SKILL_GRANT_ORIGINS = Object.freeze(["model", "user", "inherited"]);
9
9
  const FROZEN_SKILL_GRANT_ORIGINS = new Set(["model", "inherited"]);