@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
@@ -18,7 +18,7 @@ import { landedUndecided, layerDigest, layerObservationLookup, readOnlyLayerCand
18
18
  import { markOriginClearanceTombstoned, openOriginClearance, readOriginClearances, settleOriginClearance } from "./origin-clearance.js";
19
19
  import { DEFAULT_MAX_ENTRY_DEPTH, MEMORY_INDEX_FILENAME, canonicalJsonStringify, captureErasureInput, erasureRequestInvalid, erasureSelectHash, scanEntryFiles, } from "./file-backend.js";
20
20
  import { assembleMemoryExportBundle, computeMemoryBundleHash, memoryBundleInvalid, } from "./export-bundle.js";
21
- import { QUARANTINE_DIR, SCAN_FUSE_THRESHOLD, quarantineAndTombstone, restoreContainedBaseline, indexLineIsStale, readIndexRevs, writeIndexRevs, bumpScanFuse, scanFuseKey, canonicalize, landingPathOf, landsOutsideStore, storeActRefusal, storeRootMovedRefusal, adoptRootScopeLayout, clearScanFuse, adoptCanonicalKeyedControlDir, deriveControlPlaneDir, drainMemoryAnnouncements, enqueueMemoryAnnouncement, ensureDirExists, markSessionPolluted, readSessionPollution, fileSessionCaptureRecordStore, recordRetrievedAccount, writeFileNoFollow, withPermissionRemedy, permissionRemedyOf, readRetrievedAccount, registerScope, registeredScopes, resolveMemoryEngineRoot, scopeDirFor, appendChallengeEvents, appendLineageAudit, rebuildStrictControlPlaneLedger, isStrictControlPlaneLedgerCorrupt, CHALLENGE_LEDGER_MAX_EVENTS, adjudicateLineagePending, challengedEntryIds, clearLineageForEntries, discardLineagePending, lineageAccountOfEntry, lineageContributionsOfSession, lineageLatchedIds, promoteLineagePending, readChallengeEvents, readChallengedHistory, readLineageRecord, recordChallengedHistory, recordLineageCredential, reconcileLineage, readProjectionDebts, resolveChallengeEvent, settleProjectionDebts, stageLineagePending, stageProjectionDebts, } from "./layout.js";
21
+ import { QUARANTINE_DIR, SCAN_FUSE_THRESHOLD, quarantineAndTombstone, restoreContainedBaseline, indexLineIsStale, readIndexRevs, writeIndexRevs, bumpScanFuse, scanFuseKey, canonicalize, anchorStoreRoot, landingPathOf, landsOutsideStore, storeActRefusal, storeRootMovedRefusal, adoptRootScopeLayout, clearScanFuse, effectiveControlRootOf, drainMemoryAnnouncements, enqueueMemoryAnnouncement, ensureDirExists, markSessionPolluted, readSessionPollution, fileSessionCaptureRecordStore, recordRetrievedAccount, writeFileNoFollow, withPermissionRemedy, permissionRemedyOf, readRetrievedAccount, registerScope, registeredScopes, scopeDirFor, appendChallengeEvents, appendLineageAudit, rebuildStrictControlPlaneLedger, isStrictControlPlaneLedgerCorrupt, CHALLENGE_LEDGER_MAX_EVENTS, adjudicateLineagePending, challengedEntryIds, clearLineageForEntries, discardLineagePending, lineageAccountOfEntry, lineageContributionsOfSession, lineageLatchedIds, promoteLineagePending, readChallengeEvents, readChallengedHistory, readLineageRecord, recordChallengedHistory, recordLineageCredential, reconcileLineage, readProjectionDebts, resolveChallengeEvent, settleProjectionDebts, stageLineagePending, stageProjectionDebts, } from "./layout.js";
22
22
  import { createSessionIncarnationSeat, recalledForAnyIdentity, recalledForIdentity, rememberForIdentity } from "./session-incarnation.js";
23
23
  import { scanMemoryFileName, scanMemoryWrite, scanRemediation } from "./scan.js";
24
24
  import { writableRootOf } from "./types.js";
@@ -311,7 +311,6 @@ const STALE_LEG_NOTE = "memory session account: this harvest belongs to an earli
311
311
  export class MemoryEngine {
312
312
  backend;
313
313
  memoryDir;
314
- mountedRoot;
315
314
  controlDir;
316
315
  now;
317
316
  perScopeBudgetBytes;
@@ -324,7 +323,7 @@ export class MemoryEngine {
324
323
  provenance;
325
324
  onIncident;
326
325
  unsettleableDisclosed = false;
327
- backendPinnedRoot;
326
+ zeroCopy;
328
327
  constructor(opts) {
329
328
  if (opts.provenance !== undefined && opts.provenance !== "off" && opts.provenance !== "carry") {
330
329
  const got = typeof opts.provenance === "string" ? JSON.stringify(opts.provenance) : opts.provenance === null ? "null" : typeof opts.provenance;
@@ -336,13 +335,13 @@ export class MemoryEngine {
336
335
  this.backend = opts.backend;
337
336
  this.memoryDir = opts.memoryDir;
338
337
  const pinned = opts.backend.directoryRoot;
339
- if (typeof pinned === "string" && pinned)
340
- this.backendPinnedRoot = pinned;
341
- const backendControl = opts.backend.controlPlaneRoot;
342
- this.controlDir =
343
- typeof backendControl === "string" && backendControl
344
- ? backendControl
345
- : (opts.controlDir ?? (adoptCanonicalKeyedControlDir(resolveMemoryEngineRoot(), opts.memoryDir, opts.onIncident), deriveControlPlaneDir(resolveMemoryEngineRoot(), opts.memoryDir)));
338
+ if (typeof pinned === "string" && pinned !== "" && landingPathOf(pinned) !== landingPathOf(opts.memoryDir)) {
339
+ const e = new Error(`MemoryEngineOptions.memoryDir must be the File backend's own directory (the backend is pinned to ${pinned}, memoryDir is ${opts.memoryDir}) — a copy-out projection over a File backend is not supported; pass the backend's directoryRoot as memoryDir.`);
340
+ e.code = "config.memory_dir_not_backend_root";
341
+ throw e;
342
+ }
343
+ this.zeroCopy = typeof pinned === "string" && pinned !== "";
344
+ this.controlDir = effectiveControlRootOf(opts.memoryDir, { pinnedBy: opts.backend, controlDir: opts.controlDir, onIncident: opts.onIncident });
346
345
  this.now = opts.now ?? Date.now;
347
346
  this.perScopeBudgetBytes = opts.perScopeBudgetBytes ?? Number.MAX_SAFE_INTEGER;
348
347
  this.perFileBytes = opts.perFileBytes ?? MAX_MEMORY_BYTES;
@@ -416,8 +415,7 @@ export class MemoryEngine {
416
415
  return candidates;
417
416
  }
418
417
  async committedContentUnadopted(id) {
419
- const zeroCopy = this.backendPinnedRoot !== undefined && canonicalize(this.backendPinnedRoot) === canonicalize(this.memoryDir);
420
- const view = zeroCopy ? undefined : this.backend.restrictedAdoptionView?.({ audit: false });
418
+ const view = this.zeroCopy ? undefined : this.backend.restrictedAdoptionView?.({ audit: false });
421
419
  if (view === undefined)
422
420
  return this.committedContentFor(id);
423
421
  try {
@@ -429,7 +427,7 @@ export class MemoryEngine {
429
427
  }
430
428
  return this.backend.readCommittedShadow?.(id) ?? readSafe(join(this.controlDir, "shadow", `${id}.md`));
431
429
  }
432
- async restoreReadOnlyLayers(candidates, report, selfLeg, root) {
430
+ async restoreReadOnlyLayers(candidates, report, selfLeg, handle) {
433
431
  let writers;
434
432
  for (const c of candidates) {
435
433
  const text = readSafe(c.path);
@@ -452,7 +450,7 @@ export class MemoryEngine {
452
450
  continue;
453
451
  }
454
452
  try {
455
- this.refuseOutsideStore(root, c.path, "restoring the read-only layer file");
453
+ this.refuseOutsideStore(c.path, "restoring the read-only layer file", handle);
456
454
  writeFileNoFollow(c.path, committed);
457
455
  report.restored.push(c.relPath);
458
456
  }
@@ -754,11 +752,12 @@ export class MemoryEngine {
754
752
  const writableRoot = handle.writableRoot;
755
753
  if (handle.writeScope === null || writableRoot === null || !existsSync(writableRoot))
756
754
  return out;
755
+ this.assertRootAnchored(handle);
757
756
  {
758
757
  const indexPath = join(writableRoot, MEMORY_INDEX_FILENAME);
759
758
  const baseline = handle.planeBaseline !== undefined ? handle.planeBaseline.index : handle.indexText;
760
759
  try {
761
- this.refuseOutsideStore(handle.memoryDir, indexPath, "sweeping the index");
760
+ this.refuseOutsideStore(indexPath, "sweeping the index", handle);
762
761
  const first = existsSync(indexPath) ? readSafe(indexPath) : undefined;
763
762
  if (first !== undefined && first !== baseline) {
764
763
  const onDisk = readSafe(indexPath);
@@ -839,6 +838,11 @@ export class MemoryEngine {
839
838
  }
840
839
  text2 = fresh;
841
840
  }
841
+ const refusedHere = this.storeActRefusedText(canonical, "sweeping a residue file", handle);
842
+ if (refusedHere !== undefined) {
843
+ out.failures.push({ path: rel, detail: `${refusedHere} — not swept` });
844
+ continue;
845
+ }
842
846
  const q = quarantineAndTombstone(canonical, text2, join(this.controlDir, QUARANTINE_DIR), this.now);
843
847
  let contained = q.removed;
844
848
  if (committed !== undefined) {
@@ -1092,7 +1096,7 @@ export class MemoryEngine {
1092
1096
  if (kept.length === lines.length)
1093
1097
  continue;
1094
1098
  try {
1095
- this.refuseOutsideStore(landingPathOf(this.memoryDir), indexPath, "rewriting the index");
1099
+ this.refuseOutsideStore(indexPath, "rewriting the index");
1096
1100
  writeFileNoFollow(indexPath, kept.join("\n"));
1097
1101
  }
1098
1102
  catch {
@@ -1395,10 +1399,11 @@ export class MemoryEngine {
1395
1399
  }
1396
1400
  async adoptLayoutBeforeMount(writeScope) {
1397
1401
  const adopt = this.backend.adoptRootScopeLayout;
1398
- const zeroCopy = this.backendPinnedRoot !== undefined && canonicalize(this.backendPinnedRoot) === canonicalize(this.memoryDir);
1399
- if (adopt !== undefined)
1402
+ if (adopt !== undefined) {
1400
1403
  await adopt.call(this.backend, { ownerCandidate: writeScope, onIncident: this.onIncident });
1401
- if (adopt === undefined || !zeroCopy)
1404
+ this.assertRootAnchored();
1405
+ }
1406
+ if (adopt === undefined || !this.zeroCopy)
1402
1407
  adoptRootScopeLayout(this.memoryDir, this.controlDir, { ownerCandidate: writeScope, onIncident: this.onIncident, convertSidecars: convertV1SidecarKeys });
1403
1408
  }
1404
1409
  async materialize(scopes, writeScope, opts) {
@@ -1413,13 +1418,11 @@ export class MemoryEngine {
1413
1418
  }
1414
1419
  }
1415
1420
  async materializeCore(scopes, writeScope, opts, legSeat) {
1421
+ this.assertRootAnchored();
1416
1422
  ensureDirExists(this.memoryDir);
1417
- const physicalRoot = landingPathOf(this.memoryDir);
1418
- this.mountedRoot ??= physicalRoot;
1419
- if (physicalRoot !== this.mountedRoot)
1420
- throw storeRootMovedRefusal("engine", this.memoryDir);
1421
1423
  ensureDirExists(this.controlDir);
1422
1424
  await this.adoptLayoutBeforeMount(writeScope);
1425
+ this.assertRootAnchored();
1423
1426
  const restricted = opts?.adoptionRestricted === true;
1424
1427
  let sessionAccountFailed = false;
1425
1428
  let accountLeg;
@@ -1433,7 +1436,7 @@ export class MemoryEngine {
1433
1436
  scopeDirs.set(writeScope, registerScope(this.memoryDir, this.controlDir, writeScope));
1434
1437
  for (const dir of scopeDirs.values())
1435
1438
  this.censusScopeTree(dir);
1436
- const layerBaseline = this.backendPinnedRoot !== undefined && canonicalize(this.backendPinnedRoot) === canonicalize(this.memoryDir)
1439
+ const layerBaseline = this.zeroCopy
1437
1440
  ? readOnlyLayerCensus([...scopeDirs].filter(([scope]) => scope !== writeScope).map(([, dir]) => dir))
1438
1441
  : undefined;
1439
1442
  if (layerBaseline !== undefined)
@@ -1509,6 +1512,7 @@ export class MemoryEngine {
1509
1512
  const stagingName = `${captureName}.staging`;
1510
1513
  const stagingPath = stagingPathBeside(canonical, stagingName);
1511
1514
  try {
1515
+ this.refuseOutsideStore(canonical, "containing session residue");
1512
1516
  renameSync(canonical, stagingPath);
1513
1517
  if (readFileSync(stagingPath, "utf8") === text) {
1514
1518
  writeFileNoFollow(canonical, committed);
@@ -1599,10 +1603,11 @@ export class MemoryEngine {
1599
1603
  ...(layerBaseline !== undefined ? { layerBaseline } : {}),
1600
1604
  ...(accountLeg !== undefined ? { accountLeg } : {}),
1601
1605
  };
1602
- if (handle.writableRoot !== null)
1606
+ if (handle.writableRoot !== null) {
1607
+ this.refuseOutsideStore(join(handle.writableRoot, MEMORY_INDEX_FILENAME), "creating the write home", handle);
1603
1608
  ensureDirExists(handle.writableRoot);
1604
- const zeroCopy = this.backendPinnedRoot !== undefined && canonicalize(this.backendPinnedRoot) === canonicalize(this.memoryDir);
1605
- const projectionRoot = zeroCopy ? undefined : this.mountedRoot;
1609
+ }
1610
+ const zeroCopy = this.zeroCopy;
1606
1611
  for (const [scope, dir] of scopeDirs) {
1607
1612
  const scopeEntries = (byScope.get(scope) ?? []).slice();
1608
1613
  scopeEntries.sort((a, b) => (mtimeById.get(b.id) ?? 0) - (mtimeById.get(a.id) ?? 0));
@@ -1612,8 +1617,10 @@ export class MemoryEngine {
1612
1617
  handle.repoIndexTargets?.set(entry.id, entry.frontmatter.provenance.path);
1613
1618
  const stale = join(dir, `${entry.slug}.md`);
1614
1619
  const staleText = readSafe(stale);
1615
- if (staleText !== undefined && parseEntryFile(staleText).id === entry.id)
1620
+ if (staleText !== undefined && parseEntryFile(staleText).id === entry.id) {
1621
+ this.refuseOutsideStore(stale, "removing a stale projection", handle);
1616
1622
  rmSync(stale, { force: true });
1623
+ }
1617
1624
  continue;
1618
1625
  }
1619
1626
  const fullText = serializeEntryFile(entry);
@@ -1624,8 +1631,8 @@ export class MemoryEngine {
1624
1631
  const { deleted: _stubDrop, ...stubFm } = entry.frontmatter;
1625
1632
  const text = stub ? serializeEntryFile({ id: entry.id, frontmatter: stubFm, body: STUB_ARCHIVED_LINE }) : fullText;
1626
1633
  const target = join(dir, `${entry.slug}.md`);
1627
- if (projectionRoot !== undefined)
1628
- this.writeIfChanged(target, text, projectionRoot);
1634
+ if (!zeroCopy)
1635
+ this.writeIfChanged(target, text, handle);
1629
1636
  const onDiskRev = stub ? revOfText(text, entry.id) : entry.rev;
1630
1637
  const path = canonicalize(target);
1631
1638
  handle.materialized.push({
@@ -1908,6 +1915,14 @@ export class MemoryEngine {
1908
1915
  restored: [],
1909
1916
  warnings: [],
1910
1917
  };
1918
+ try {
1919
+ this.assertRootAnchored(handle);
1920
+ }
1921
+ catch (err) {
1922
+ report.ok = false;
1923
+ report.incident = { kind: "sidecar_corrupt", detail: `memory control plane refused: ${this.writeFailureText(err)}` };
1924
+ return report;
1925
+ }
1911
1926
  const writeScope = handle.writeScope;
1912
1927
  const captureRead = opts?.sessionId !== undefined ? await this.sessionCaptureOptOutOrFault(opts.sessionId) : { fault: false };
1913
1928
  const captureOptOut = captureRead.record;
@@ -1962,6 +1977,7 @@ export class MemoryEngine {
1962
1977
  return report;
1963
1978
  }
1964
1979
  try {
1980
+ this.assertRootAnchored(handle);
1965
1981
  this.backend.checkControlPlane?.();
1966
1982
  }
1967
1983
  catch (err) {
@@ -1977,6 +1993,7 @@ export class MemoryEngine {
1977
1993
  await this.settleUndecidedObservations(handle, report, landedUndecided(observed));
1978
1994
  try {
1979
1995
  if (carry) {
1996
+ this.assertRootAnchored(handle);
1980
1997
  reconcileHolds(this.controlDir, { homeOf: (scope) => scopeDirFor(this.memoryDir, this.controlDir, scope), now: this.now });
1981
1998
  const newlyExpired = expireOverdueSettlements(this.controlDir, { timeoutMs: this.holdSettleTimeoutMs, now: this.now });
1982
1999
  if (newlyExpired.length > 0) {
@@ -2077,6 +2094,11 @@ export class MemoryEngine {
2077
2094
  report.rejections.push({ path: rel, code: "outside_root", reason: "file resolves outside the writable memory root (containment gate, fail-closed)" });
2078
2095
  return;
2079
2096
  }
2097
+ const refusedHere = this.storeActRefusedText(f.canonical, "containing a withheld file", handle);
2098
+ if (refusedHere !== undefined) {
2099
+ report.rejections.push({ path: rel, code: "outside_root", reason: `containment refused (fail-closed): ${refusedHere}` });
2100
+ return;
2101
+ }
2080
2102
  const baseId = handle.baseIds.get(f.canonical);
2081
2103
  if (baseId !== undefined && !pollutedStubs.has(f.canonical) && revOfText(f.text, baseId) === handle.baseRevs.get(f.canonical))
2082
2104
  return;
@@ -2106,7 +2128,10 @@ export class MemoryEngine {
2106
2128
  if (priorId !== undefined) {
2107
2129
  const committed = await this.committedContentFor(priorId);
2108
2130
  if (committed !== undefined) {
2109
- const put = restoreContainedBaseline(f.canonical, committed, q.leftOnPlane);
2131
+ const refusedRestore = this.storeActRefusedText(f.canonical, "restoring a contained file's baseline", handle);
2132
+ if (refusedRestore !== undefined)
2133
+ report.warnings.push(`${rel}: the committed baseline was not restored — ${refusedRestore}`);
2134
+ const put = refusedRestore === undefined ? restoreContainedBaseline(f.canonical, committed, q.leftOnPlane) : undefined;
2110
2135
  if (put === "written") {
2111
2136
  report.restored.push(rel);
2112
2137
  contained = true;
@@ -2151,6 +2176,7 @@ export class MemoryEngine {
2151
2176
  catch {
2152
2177
  }
2153
2178
  try {
2179
+ this.refuseOutsideStore(pollutedIndexPath, "restoring the index", handle);
2154
2180
  writeFileNoFollow(pollutedIndexPath, handle.indexText);
2155
2181
  report.warnings.push(`memory index restored to its pre-session state — this session's index additions were not retained (session polluted; ${indexCaptureLanded ? "the removed text was captured to quarantine" : "the quarantine capture did NOT land — the removed text is gone"})`);
2156
2182
  return true;
@@ -2186,7 +2212,7 @@ export class MemoryEngine {
2186
2212
  signalContainment((c) => (c.indexRolledBack = true));
2187
2213
  report.warnings.push(`memory harvest refused by an incident while this session is marked externally exposed — no entries were committed; instruction-form files were withheld and the derived index rolled back: ${inlineUntrusted(pollutedReason, 200)}`);
2188
2214
  };
2189
- await this.restoreReadOnlyLayers(readOnlyRestorable, report, handle.accountLeg, handle.memoryDir);
2215
+ await this.restoreReadOnlyLayers(readOnlyRestorable, report, handle.accountLeg, handle);
2190
2216
  const missingWritable = writable.filter((m) => !recordByPath.has(m.path) && !existsSync(m.path) && !presentIds.has(m.id));
2191
2217
  report.missing.push(...missingWritable.map((m) => m.relPath));
2192
2218
  if (writable.length > 0 && missingWritable.length / writable.length > MASS_DELETION_FUSE_RATIO) {
@@ -2213,7 +2239,7 @@ export class MemoryEngine {
2213
2239
  if (content === undefined)
2214
2240
  continue;
2215
2241
  try {
2216
- this.refuseOutsideStore(handle.memoryDir, m.path, "restoring a memory file");
2242
+ this.refuseOutsideStore(m.path, "restoring a memory file", handle);
2217
2243
  mkdirSync(dirname(m.path), { recursive: true });
2218
2244
  writeFileNoFollow(m.path, content);
2219
2245
  report.restored.push(m.relPath);
@@ -2281,7 +2307,7 @@ export class MemoryEngine {
2281
2307
  if (fastBaseId !== undefined && !stubByPath.has(f.canonical) && revOfText(f.text, fastBaseId) === revByBasePath.get(f.canonical)) {
2282
2308
  const fastDebt = projectionDebts.get(debtSeat(writeScope, homeRel(writableRoot, f.canonical)));
2283
2309
  if (fastDebt !== undefined && fastDebt.entryId === fastBaseId)
2284
- await this.repairProjectionSeat(seatOf(f.canonical), f.canonical, fastBaseId, debtClears, report.warnings);
2310
+ await this.repairProjectionSeat(seatOf(f.canonical), f.canonical, fastBaseId, debtClears, report.warnings, handle);
2285
2311
  continue;
2286
2312
  }
2287
2313
  processed++;
@@ -2307,13 +2333,15 @@ export class MemoryEngine {
2307
2333
  scanFindings.push(...scanMemoryWrite(f.text));
2308
2334
  const finding = scanFindings[0];
2309
2335
  if (finding !== undefined) {
2310
- const q = quarantineAndTombstone(f.canonical, f.text, join(this.controlDir, QUARANTINE_DIR), this.now);
2336
+ const refusedHere = this.storeActRefusedText(f.canonical, "quarantining a rejected file", handle);
2337
+ const q = refusedHere === undefined ? quarantineAndTombstone(f.canonical, f.text, join(this.controlDir, QUARANTINE_DIR), this.now) : { removed: false, leftOnPlane: f.text, detail: refusedHere };
2311
2338
  let contained = q.removed;
2312
2339
  const priorId = idByBasePath.get(f.canonical);
2313
2340
  if (priorId !== undefined) {
2314
2341
  const committed = await this.committedContentFor(priorId);
2315
2342
  if (committed !== undefined) {
2316
2343
  try {
2344
+ this.refuseOutsideStore(f.canonical, "restoring a rejected file's committed content", handle);
2317
2345
  writeFileNoFollow(f.canonical, committed);
2318
2346
  report.restored.push(rel);
2319
2347
  contained = true;
@@ -2415,7 +2443,8 @@ export class MemoryEngine {
2415
2443
  continue;
2416
2444
  }
2417
2445
  if (instructionForm && sessionPending && lineageSessionId !== undefined) {
2418
- const held = openInstructionHold(this.controlDir, {
2446
+ const holdRefused = this.storeActRefusedText(f.canonical, "holding an instruction-form file", handle);
2447
+ const held = holdRefused !== undefined ? { ok: false, reason: holdRefused } : openInstructionHold(this.controlDir, {
2419
2448
  sessionId: lineageSessionId,
2420
2449
  rel: homeRel(writableRoot, f.canonical),
2421
2450
  absPath: f.canonical,
@@ -2438,6 +2467,7 @@ export class MemoryEngine {
2438
2467
  const committed = await this.committedContentFor(committedIdHere);
2439
2468
  if (committed !== undefined) {
2440
2469
  try {
2470
+ this.refuseOutsideStore(f.canonical, "restoring a held entry's committed content", handle);
2441
2471
  writeFileNoFollow(f.canonical, committed);
2442
2472
  report.restored.push(rel);
2443
2473
  }
@@ -2489,7 +2519,7 @@ export class MemoryEngine {
2489
2519
  if (revOfText(f.text, id) === revByBasePath.get(f.canonical)) {
2490
2520
  const seatDebt = projectionDebts.get(debtSeat(writeScope, homeRel(writableRoot, f.canonical)));
2491
2521
  if (seatDebt !== undefined && seatDebt.entryId === id)
2492
- await this.repairProjectionSeat(seatOf(f.canonical), f.canonical, id, debtClears, report.warnings);
2522
+ await this.repairProjectionSeat(seatOf(f.canonical), f.canonical, id, debtClears, report.warnings, handle);
2493
2523
  continue;
2494
2524
  }
2495
2525
  pendingProjections.push({ path: f.canonical, entry, needed: minted || parsed.id !== id || needsCompletion(parsed, fm), ...(debtReconciled ? { idCompletion: true } : {}) });
@@ -2507,6 +2537,7 @@ export class MemoryEngine {
2507
2537
  reboundId = parsed.id;
2508
2538
  const idless = serializeEntryFile({ id: "", frontmatter: fm, body: parsed.body }).replace(/^id:[ \t]*$\r?\n/m, "");
2509
2539
  try {
2540
+ this.refuseOutsideStore(f.canonical, "stripping a copied id", handle);
2510
2541
  writeFileNoFollow(f.canonical, idless);
2511
2542
  }
2512
2543
  catch (err) {
@@ -2526,6 +2557,7 @@ export class MemoryEngine {
2526
2557
  if (fm.origin !== undefined && parsed.id !== undefined && reboundId === undefined) {
2527
2558
  const idless = serializeEntryFile({ id: "", frontmatter: fm, body: parsed.body }).replace(/^id:[ \t]*$\r?\n/m, "");
2528
2559
  try {
2560
+ this.refuseOutsideStore(f.canonical, "stripping a model-written id", handle);
2529
2561
  writeFileNoFollow(f.canonical, idless);
2530
2562
  }
2531
2563
  catch (err) {
@@ -2648,6 +2680,7 @@ export class MemoryEngine {
2648
2680
  }
2649
2681
  const idless = serializeEntryFile({ id: "", frontmatter: p.entry.frontmatter, body: p.entry.body }).replace(/^id:[ \t]*$\r?\n/m, "");
2650
2682
  try {
2683
+ this.refuseOutsideStore(flipRec.canonical, "re-minting a marked commit's id", handle);
2651
2684
  writeFileNoFollow(flipRec.canonical, idless);
2652
2685
  }
2653
2686
  catch (err) {
@@ -2760,7 +2793,7 @@ export class MemoryEngine {
2760
2793
  }
2761
2794
  }
2762
2795
  try {
2763
- patchReport = await this.backend.applyPatches(patches);
2796
+ patchReport = await this.backend.applyPatches(patches, { anchoredRoot: handle.memoryDir });
2764
2797
  }
2765
2798
  catch (err) {
2766
2799
  if (lineageArmed) {
@@ -2827,14 +2860,14 @@ export class MemoryEngine {
2827
2860
  }
2828
2861
  }
2829
2862
  const appliedIds = new Set(patchReport.applied.filter((a) => a.op !== "delete").map((a) => a.id));
2830
- const planeAfterFailedWriteBack = this.backendPinnedRoot !== undefined && canonicalize(this.backendPinnedRoot) === canonicalize(this.memoryDir)
2863
+ const planeAfterFailedWriteBack = this.zeroCopy
2831
2864
  ? "this plane is the store itself: the next read adopts them as an out-of-session edit unless the committed copy is put back first"
2832
2865
  : "the next materialize re-projects the committed copy over them";
2833
2866
  for (const p of pendingProjections) {
2834
2867
  const pRel = relative(handle.memoryDir, p.path);
2835
2868
  const pSeat = seatOf(p.path);
2836
2869
  if (appliedIds.has(p.entry.id)) {
2837
- const writeBackFailure = this.writeBackProjection(p.path, p.entry, p.needed);
2870
+ const writeBackFailure = this.writeBackProjection(p.path, p.entry, p.needed, handle);
2838
2871
  if (p.idCompletion !== true) {
2839
2872
  if (writeBackFailure !== undefined)
2840
2873
  report.warnings.push(`${pRel}: the entry committed but its projection write-back FAILED — the plane keeps this session's bytes (${planeAfterFailedWriteBack}): ${writeBackFailure}`);
@@ -2855,6 +2888,7 @@ export class MemoryEngine {
2855
2888
  let planeHoldsCommitted = false;
2856
2889
  if (committed !== undefined && readNoFollowSafe(p.path) !== committed) {
2857
2890
  try {
2891
+ this.refuseOutsideStore(p.path, "writing a CAS winner's committed copy back", handle);
2858
2892
  writeFileNoFollow(p.path, committed);
2859
2893
  planeHoldsCommitted = true;
2860
2894
  }
@@ -3069,7 +3103,7 @@ export class MemoryEngine {
3069
3103
  }
3070
3104
  let patchReport;
3071
3105
  try {
3072
- patchReport = await this.backend.applyPatches([patch]);
3106
+ patchReport = await this.backend.applyPatches([patch], { anchoredRoot: handle.memoryDir });
3073
3107
  }
3074
3108
  catch (err) {
3075
3109
  try {
@@ -4832,7 +4866,7 @@ export class MemoryEngine {
4832
4866
  if (opts.write) {
4833
4867
  let wrote = false;
4834
4868
  try {
4835
- this.refuseOutsideStore(handle.memoryDir, indexPath, "writing the index");
4869
+ this.refuseOutsideStore(indexPath, "writing the index", handle);
4836
4870
  mkdirSync(dirname(indexPath), { recursive: true });
4837
4871
  if (readNoFollowSafe(indexPath) !== text)
4838
4872
  writeFileNoFollow(indexPath, text);
@@ -4852,8 +4886,7 @@ export class MemoryEngine {
4852
4886
  }
4853
4887
  async committedContentOrFault(id) {
4854
4888
  let fault = false;
4855
- const zeroCopy = this.backendPinnedRoot !== undefined && canonicalize(this.backendPinnedRoot) === canonicalize(this.memoryDir);
4856
- if (!zeroCopy) {
4889
+ if (!this.zeroCopy) {
4857
4890
  try {
4858
4891
  const [entry] = await this.backend.getByIds([id]);
4859
4892
  if (entry !== undefined && entry.id === id)
@@ -4872,8 +4905,7 @@ export class MemoryEngine {
4872
4905
  return { fault };
4873
4906
  }
4874
4907
  async committedFrontmatterFor(id) {
4875
- const zeroCopy = this.backendPinnedRoot !== undefined && canonicalize(this.backendPinnedRoot) === canonicalize(this.memoryDir);
4876
- if (!zeroCopy) {
4908
+ if (!this.zeroCopy) {
4877
4909
  const face = this.backend.retrievalView?.() ?? this.backend;
4878
4910
  try {
4879
4911
  const [entry] = await face.getByIds([id]);
@@ -4923,7 +4955,7 @@ export class MemoryEngine {
4923
4955
  }
4924
4956
  let cleared = false;
4925
4957
  try {
4926
- this.refuseOutsideStore(handle.memoryDir, indexPath, "clearing the index");
4958
+ this.refuseOutsideStore(indexPath, "clearing the index", handle);
4927
4959
  writeFileNoFollow(indexPath, "");
4928
4960
  cleared = true;
4929
4961
  }
@@ -4943,25 +4975,41 @@ export class MemoryEngine {
4943
4975
  contained: cleared,
4944
4976
  };
4945
4977
  }
4946
- refuseOutsideStore(root, path, act) {
4947
- if (landsOutsideStore(root, path))
4978
+ refuseOutsideStore(path, act, handle) {
4979
+ if (landsOutsideStore(this.assertRootAnchored(handle), path))
4948
4980
  throw storeActRefusal(act, path);
4949
4981
  }
4950
- writeIfChanged(target, text, root) {
4951
- this.refuseOutsideStore(root, target, "the projection write");
4982
+ storeActRefusedText(path, act, handle) {
4983
+ try {
4984
+ this.refuseOutsideStore(path, act, handle);
4985
+ return undefined;
4986
+ }
4987
+ catch (err) {
4988
+ return this.writeFailureText(err);
4989
+ }
4990
+ }
4991
+ assertRootAnchored(handle) {
4992
+ const root = anchorStoreRoot(this.memoryDir, this.controlDir, { onIncident: this.onIncident, now: this.now });
4993
+ if (handle !== undefined && handle.memoryDir !== root)
4994
+ throw storeRootMovedRefusal(this.memoryDir, { controlDir: this.controlDir, recorded: root, now: handle.memoryDir });
4995
+ return root;
4996
+ }
4997
+ writeIfChanged(target, text, handle) {
4998
+ this.refuseOutsideStore(target, "the projection write", handle);
4952
4999
  const current = readNoFollowSafe(target);
4953
5000
  if (current === text)
4954
5001
  return;
4955
5002
  mkdirSync(dirname(target), { recursive: true });
4956
5003
  writeFileNoFollow(target, text);
4957
5004
  }
4958
- writeBackProjection(path, entry, needed) {
5005
+ writeBackProjection(path, entry, needed, handle) {
4959
5006
  if (!needed)
4960
5007
  return undefined;
4961
5008
  const text = serializeEntryFile(entry);
4962
5009
  if (readNoFollowSafe(path) === text)
4963
5010
  return undefined;
4964
5011
  try {
5012
+ this.refuseOutsideStore(path, "the projection write-back", handle);
4965
5013
  writeFileNoFollow(path, text);
4966
5014
  return undefined;
4967
5015
  }
@@ -4969,12 +5017,13 @@ export class MemoryEngine {
4969
5017
  return this.writeFailureText(err);
4970
5018
  }
4971
5019
  }
4972
- async repairProjectionSeat(seat, canonical, entryId, debtClears, warnings) {
5020
+ async repairProjectionSeat(seat, canonical, entryId, debtClears, warnings, handle) {
4973
5021
  const committed = await this.committedContentFor(entryId);
4974
5022
  if (committed === undefined)
4975
5023
  return;
4976
5024
  if (readNoFollowSafe(canonical) !== committed) {
4977
5025
  try {
5026
+ this.refuseOutsideStore(canonical, "repairing a projection seat", handle);
4978
5027
  writeFileNoFollow(canonical, committed);
4979
5028
  }
4980
5029
  catch (err) {
@@ -4985,8 +5034,7 @@ export class MemoryEngine {
4985
5034
  debtClears.push({ ...seat, entryId });
4986
5035
  }
4987
5036
  async debtCommittedProjection(entryId) {
4988
- const zeroCopy = this.backendPinnedRoot !== undefined && canonicalize(this.backendPinnedRoot) === canonicalize(this.memoryDir);
4989
- if (zeroCopy)
5037
+ if (this.zeroCopy)
4990
5038
  return { state: "stale" };
4991
5039
  const face = this.backend.retrievalView?.() ?? this.backend;
4992
5040
  try {
@@ -1,4 +1,4 @@
1
- import { scopeDirName, type ChallengeEvent, type MemoryPartitionIncidentSink } from "./layout.js";
1
+ import { scopeDirName, type TxnLockTimings, type ChallengeEvent, type MemoryPartitionIncidentSink } from "./layout.js";
2
2
  import type { MemoryBundleImportPlan, MemoryImportReport } from "./export-bundle.js";
3
3
  import type { HarvestRejection, MemoryBackend, MemoryEntry, MemoryEntryHeader, MemoryScopeEnumeration, NotePatch, PatchReport, ScoredMemoryEntry } from "./types.js";
4
4
  export { DEFAULT_MAX_ENTRY_DEPTH, MEMORY_INDEX_FILENAME, scanEntryFiles, type ScannedEntryFile } from "./entry-scan.js";
@@ -312,12 +312,6 @@ export interface MemoryExportSnapshot {
312
312
  quarantined: string[];
313
313
  quarantineOpaque: number;
314
314
  }
315
- /** Test hook (证伪式复审 L3): small constants so the crash-window leg does not really wait 15-30s. */
316
- interface TxnLockTimings {
317
- staleMs?: number;
318
- waitMs?: number;
319
- stealGraceMs?: number;
320
- }
321
315
  /** Construction options for {@link FileMemoryEngineBackend}. */
322
316
  export interface FileMemoryEngineBackendOptions {
323
317
  /** ⚠️ 分家坑(service 报告 2026-07-09):不显式传时,控制平面按无参 `resolveMemoryEngineRoot()`
@@ -353,8 +347,6 @@ export declare class FileMemoryEngineBackend implements MemoryBackend {
353
347
  * NEVER under {@link directoryRoot}, always on the config-root side even for an in-repo memory dir.
354
348
  * The engine picks this up (like `directoryRoot`) so both sides share ONE control plane. */
355
349
  readonly controlPlaneRoot: string;
356
- /** {@link directoryRoot}'s physical form at construction — the root every act on a store path is judged against. */
357
- private readonly physicalRoot;
358
350
  /** Injected clock (opus 1.258 LOW: match the engine's `this.now` determinism discipline). */
359
351
  private readonly now;
360
352
  /** The construction-time incident sink (partition adoption, layout adoption on the standalone faces). */
@@ -687,13 +679,16 @@ export declare class FileMemoryEngineBackend implements MemoryBackend {
687
679
  * first op that would land outside the store, naming the op (its path on `detail.path`). Runs over a
688
680
  * whole batch before its commit marker (no journal is written; a caller that staged files removes
689
681
  * them) and per op at the act itself; past the marker a refusal keeps the journal, which replays
690
- * once the path resolves inside the store again.
682
+ * once the path resolves inside the store again. The root judged against is the anchored one, read at
683
+ * this call ({@link assertRootAnchored}); with `anchoredRoot` (the caller's session root) it must be that one.
691
684
  */
692
685
  private refuseEscapingTargets;
693
- /** At the head of every face (both recovery legs, the lock-free faces' layout seat, the adoption face):
694
- * the store root still resolves to the directory this backend opened. A root re-pointed since would
695
- * carry every read, adoption, sweep and restore — not only the journal's acts — into another directory. */
696
- private assertRootIntact;
686
+ /** At construction and at the head of every face (both recovery legs, the lock-free faces' layout seat,
687
+ * the adoption face): the store root against its anchor ({@link anchorStoreRoot}); answers the anchored
688
+ * physical root. A root re-pointed since would carry every read, adoption, sweep and restore — not only
689
+ * the journal's acts — into another directory. Read every time, never kept. With `expected` (a caller
690
+ * session's root) the anchor must be that root too — the engine's handle comparison, at this act. */
691
+ private assertRootAnchored;
697
692
  /** Remove orphaned `.stage-*` files (a crash BEFORE the journal rename = clean rollback: nothing
698
693
  * was committed, the staged tmps are litter). Dotfiles are invisible to entry scans regardless. */
699
694
  private sweepStagedFiles;
@@ -978,23 +973,11 @@ export declare class FileMemoryEngineBackend implements MemoryBackend {
978
973
  * 5. EXECUTE: atomic renames / deletes / shadow updates.
979
974
  * 6. Persist the ledger (atomic, fail-closed) and drop the journal.
980
975
  */
981
- applyPatches(patches: readonly NotePatch[]): Promise<PatchReport>;
976
+ applyPatches(patches: readonly NotePatch[], opts?: {
977
+ readonly anchoredRoot?: string;
978
+ }): Promise<PatchReport>;
982
979
  private txnLockDir;
983
- /**
984
- * 证伪式复审 L1/L2/L3 — the mkdir mutex, hardened (same shape as layout.ts' acquireSidecarLock, but
985
- * fail-closed timings and an async wait):
986
- * - L2: every acquisition writes an OWNER TOKEN (`<lock>/owner`, crypto-random — never a
987
- * timestamp) immediately after mkdir. Release only removes the lock while the token is still
988
- * ours; {@link assertTxnLockOwnership} re-verifies it at the commit point.
989
- * - L1: the stale steal is SINGLE-WINNER — atomic rename of the stale lock dir to a unique
990
- * tombstone (exactly one racer's rename succeeds; losers ENOENT back into the wait loop), then
991
- * the winner removes the tombstone and re-acquires through a NORMAL mkdir. Never a bare
992
- * rmSync+mkdir (two same-window stealers would force-rm each other's FRESH lock = double hold).
993
- * - L2 (steal side): only a lock that is BOTH past its stale line AND carries an owner token is
994
- * stealable (a tokenless dir is a mid-acquisition, not a crashed holder).
995
- * - L3: the wait deadline is `max(waitMs, observed lock's stale line + grace)` — a holder that
996
- * crashed with `remaining stale > waitMs` no longer strands every waiter in an all-throw window.
997
- */
980
+ /** The store's transaction lock, waited for ({@link acquireStoreTxnLock} — the one mkdir mutex, shared with a re-anchor of the root). */
998
981
  private acquireTxnLock;
999
982
  /** 证伪式复审 L2 — 提交点归属复验: called immediately before the JOURNAL write (the persistent commit
1000
983
  * point). A holder that stalled past the stale deadline (slow disk / GC pause / long txn) and was