@sema-agent/server 7.146.0 → 7.147.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 (398) hide show
  1. package/MIGRATION.md +384 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +170 -66
  5. package/deploy/facts/lanes.json +7 -0
  6. package/deploy/sema-up/README.md +2 -2
  7. package/deploy/sema-up/chart/templates/_helpers.tpl +22 -0
  8. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -4
  9. package/deploy/sema-up/chart/templates/server.yaml +5 -4
  10. package/deploy/sema-up/chart/values.yaml +1 -1
  11. package/deploy/sema-up/cluster-up.sh +2 -1
  12. package/deploy/sema-up/lib/flagshape.sh +4 -0
  13. package/deploy/sema-up/lib/lanes.json +2 -5
  14. package/deploy/sema-up/sema-up.sh +12 -9
  15. package/deploy/sema-up/stack/compose.yaml +5 -4
  16. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -3
  17. package/dist/approval-card.d.ts +4 -4
  18. package/dist/approval.d.ts +3 -3
  19. package/dist/audit.js +2 -2
  20. package/dist/bench/s1/live-deps.js +3 -1
  21. package/dist/boot/crash-last.js +2 -1
  22. package/dist/boot/device-lane.js +5 -5
  23. package/dist/boot/engine-lease.d.ts +12 -11
  24. package/dist/boot/execution-env.d.ts +2 -1
  25. package/dist/boot/execution-env.js +45 -33
  26. package/dist/boot/execution-lane.d.ts +37 -9
  27. package/dist/boot/execution-lane.js +74 -20
  28. package/dist/boot/exit-boundary.d.ts +1 -1
  29. package/dist/boot/exit-last.js +2 -1
  30. package/dist/boot/governance-seams.d.ts +2 -2
  31. package/dist/boot/leader.js +1 -1
  32. package/dist/boot/memory-boundary.d.ts +11 -11
  33. package/dist/boot/memory-boundary.js +3 -3
  34. package/dist/boot/memory-consolidation.d.ts +13 -24
  35. package/dist/boot/memory-consolidation.js +0 -13
  36. package/dist/boot/org-memory.js +3 -2
  37. package/dist/boot/parked-revive-gate.d.ts +1 -1
  38. package/dist/boot/parked-revive-gate.js +1 -1
  39. package/dist/boot/reapers.js +1 -1
  40. package/dist/boot/resolve-spec.js +91 -111
  41. package/dist/boot/retention-lane.d.ts +4 -42
  42. package/dist/boot/session-faces.d.ts +1 -0
  43. package/dist/boot/session-faces.js +8 -2
  44. package/dist/boot/stage-01-config.d.ts +1 -1
  45. package/dist/boot/stage-01-config.js +6 -2
  46. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -1
  47. package/dist/boot/stage-02-web-admin-pull.js +10 -8
  48. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  49. package/dist/boot/stage-04-stores.d.ts +1 -1
  50. package/dist/boot/stage-05-execution-env.d.ts +2 -2
  51. package/dist/boot/stage-05-execution-env.js +3 -3
  52. package/dist/boot/stage-06-runners.d.ts +3 -3
  53. package/dist/boot/stage-06-runners.js +1 -3
  54. package/dist/boot/stage-07-capability-layer.d.ts +3 -3
  55. package/dist/boot/stage-07-capability-layer.js +4 -9
  56. package/dist/boot/stage-08-reapers.d.ts +3 -3
  57. package/dist/boot/stage-09-leader.d.ts +4 -3
  58. package/dist/boot/stage-10-http-server.d.ts +4 -3
  59. package/dist/boot/stage-10-http-server.js +2 -1
  60. package/dist/boot/stores.d.ts +9 -0
  61. package/dist/boot/stores.js +18 -7
  62. package/dist/boot/web-admin.js +3 -3
  63. package/dist/capabilities/center-plugins.d.ts +2 -0
  64. package/dist/capabilities/center-plugins.js +6 -2
  65. package/dist/capabilities/composite-members.d.ts +1 -0
  66. package/dist/capabilities/composite-members.js +1 -0
  67. package/dist/capabilities/execution-lane.d.ts +7 -7
  68. package/dist/capabilities/execution-lane.js +2 -2
  69. package/dist/capabilities/hands-lane.d.ts +1 -1
  70. package/dist/capabilities/scenarios.d.ts +32 -18
  71. package/dist/capabilities/scenarios.js +13 -9
  72. package/dist/capabilities/settings-contract.d.ts +29 -0
  73. package/dist/capabilities/settings-contract.js +14 -0
  74. package/dist/capabilities/skills.d.ts +5 -5
  75. package/dist/capability-facts.d.ts +38 -0
  76. package/dist/capability-facts.js +34 -1
  77. package/dist/center-credential-renewer.js +3 -2
  78. package/dist/config-catalog.js +6 -5
  79. package/dist/config-invariants.d.ts +18 -3
  80. package/dist/config-invariants.js +36 -19
  81. package/dist/config-lkg.d.ts +1 -1
  82. package/dist/config-lkg.js +5 -6
  83. package/dist/config-provider.js +2 -0
  84. package/dist/config-types.d.ts +190 -186
  85. package/dist/config.d.ts +68 -5
  86. package/dist/config.js +80 -54
  87. package/dist/degenerate-instrument.js +1 -1
  88. package/dist/deploy-facts.js +3 -2
  89. package/dist/deployment-shape.d.ts +21 -12
  90. package/dist/deployment-shape.js +24 -6
  91. package/dist/env-facts.d.ts +7 -5
  92. package/dist/env-facts.js +10 -6
  93. package/dist/execution-config-root.d.ts +5 -5
  94. package/dist/execution-config-root.js +5 -5
  95. package/dist/execution-env-purpose.d.ts +14 -0
  96. package/dist/execution-env-purpose.js +29 -0
  97. package/dist/execution-lane-caps.d.ts +49 -86
  98. package/dist/execution-lane-caps.js +52 -62
  99. package/dist/execution-placement.d.ts +18 -2
  100. package/dist/execution-placement.js +19 -0
  101. package/dist/file-history-disclosure.d.ts +2 -2
  102. package/dist/fleet/drain-entry.d.ts +17 -10
  103. package/dist/fleet/drain-entry.js +1 -1
  104. package/dist/fleet/serving-lease.js +3 -4
  105. package/dist/history-anchor.d.ts +22 -0
  106. package/dist/history-anchor.js +14 -0
  107. package/dist/hooks/command-spawn.d.ts +1 -4
  108. package/dist/hooks/command-spawn.js +0 -11
  109. package/dist/hooks/hook-runner.d.ts +52 -8
  110. package/dist/hooks/hook-runner.js +160 -36
  111. package/dist/http/approval-sweeps.d.ts +2 -5
  112. package/dist/http/approval-sweeps.js +3 -2
  113. package/dist/http/checkpoint-ctx.d.ts +17 -0
  114. package/dist/http/checkpoint-ctx.js +6 -0
  115. package/dist/http/claim-latency.d.ts +10 -0
  116. package/dist/http/claim-latency.js +12 -0
  117. package/dist/http/dispatch.js +1 -1
  118. package/dist/http/durable-run-leg.js +2 -1
  119. package/dist/http/execution-context.d.ts +3 -1
  120. package/dist/http/execution-context.js +4 -0
  121. package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
  122. package/dist/http/memory-compliance-failure-reply.js +3 -0
  123. package/dist/http/memory-store-refusal-reply.d.ts +3 -3
  124. package/dist/http/memory-store-refusal-reply.js +3 -0
  125. package/dist/http/owner-access.d.ts +11 -0
  126. package/dist/http/owner-access.js +7 -4
  127. package/dist/http/principal-gate.d.ts +3 -14
  128. package/dist/http/principal-gate.js +0 -13
  129. package/dist/http/request-guards.d.ts +1 -1
  130. package/dist/http/resume-legs.d.ts +7 -10
  131. package/dist/http/resume-legs.js +79 -50
  132. package/dist/http/rewind-files-reply.d.ts +34 -0
  133. package/dist/http/rewind-files-reply.js +36 -0
  134. package/dist/http/route-ctx.d.ts +32 -16
  135. package/dist/http/route-table.js +2 -0
  136. package/dist/http/routes/approvals-assistant.js +5 -25
  137. package/dist/http/routes/capabilities.js +8 -2
  138. package/dist/http/routes/devices.js +1 -1
  139. package/dist/http/routes/memory-origin.d.ts +1 -1
  140. package/dist/http/routes/notify-wake.js +2 -1
  141. package/dist/http/routes/projects.d.ts +70 -0
  142. package/dist/http/routes/projects.js +86 -0
  143. package/dist/http/routes/rules.js +30 -2
  144. package/dist/http/routes/runs.js +8 -8
  145. package/dist/http/routes/session-sync.js +27 -10
  146. package/dist/http/routes/sessions.d.ts +7 -1
  147. package/dist/http/routes/sessions.js +104 -2
  148. package/dist/http/routes/tasks.js +9 -3
  149. package/dist/http/server.d.ts +24 -10
  150. package/dist/http/server.js +17 -6
  151. package/dist/http/stored-body-replay.d.ts +5 -3
  152. package/dist/http/stored-body-replay.js +10 -1
  153. package/dist/http/wire-types.d.ts +19 -10
  154. package/dist/index.d.ts +2 -2
  155. package/dist/index.js +1 -1
  156. package/dist/instruction-source-notice.d.ts +1 -1
  157. package/dist/memory-embedder-reembed.d.ts +38 -0
  158. package/dist/memory-embedder-reembed.js +20 -0
  159. package/dist/memory-incident-sink.d.ts +0 -28
  160. package/dist/memory-incident-sink.js +5 -0
  161. package/dist/memory-scope.d.ts +2 -2
  162. package/dist/memory-scope.js +1 -1
  163. package/dist/memory-store-refusal.d.ts +17 -4
  164. package/dist/memory-store-refusal.js +1 -1
  165. package/dist/memory-sync-client.d.ts +2 -0
  166. package/dist/memory-sync-client.js +13 -11
  167. package/dist/memory-sync.js +1 -1
  168. package/dist/model-compat.d.ts +35 -21
  169. package/dist/model-compat.js +49 -21
  170. package/dist/model-entry-refusal.d.ts +11 -1
  171. package/dist/model-entry-refusal.js +7 -0
  172. package/dist/namespaced-rule-name.d.ts +18 -1
  173. package/dist/namespaced-rule-name.js +12 -0
  174. package/dist/observability/fail-open.d.ts +17 -9
  175. package/dist/observability/fail-open.js +17 -9
  176. package/dist/observability/metrics.d.ts +2 -0
  177. package/dist/observability/metrics.js +2 -0
  178. package/dist/observability/secret-env-scrub.d.ts +30 -4
  179. package/dist/observability/secret-env-scrub.js +15 -3
  180. package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
  181. package/dist/orchestration/workflow-agent-session-index.js +1 -6
  182. package/dist/orchestration/workflow-completion-inbox.d.ts +26 -6
  183. package/dist/orchestration/workflow-completion-inbox.js +63 -45
  184. package/dist/org-memory-admission.d.ts +5 -2
  185. package/dist/org-memory-admission.js +25 -10
  186. package/dist/parked-decide.d.ts +33 -26
  187. package/dist/parked-decide.js +36 -44
  188. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  189. package/dist/plugins/approval-ask-store-file.js +37 -4
  190. package/dist/plugins/approval-ask-store-memory.d.ts +5 -3
  191. package/dist/plugins/approval-ask-store-memory.js +11 -2
  192. package/dist/plugins/approval-ask-store-sql.d.ts +3 -0
  193. package/dist/plugins/approval-ask-store-sql.js +3 -1
  194. package/dist/plugins/blob-backend.d.ts +38 -195
  195. package/dist/plugins/blob-backend.js +33 -229
  196. package/dist/plugins/caching-session-store.d.ts +9 -4
  197. package/dist/plugins/checkpoint-index-columns.d.ts +1 -1
  198. package/dist/plugins/checkpoint-index-columns.js +3 -2
  199. package/dist/plugins/checkpoint-store-face.d.ts +1 -1
  200. package/dist/plugins/device-store-sql.d.ts +2 -2
  201. package/dist/plugins/device-store-sql.js +5 -10
  202. package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
  203. package/dist/plugins/execution-lane-rename-m40.js +10 -0
  204. package/dist/plugins/execution-placement-sql.d.ts +63 -88
  205. package/dist/plugins/execution-placement-sql.js +94 -54
  206. package/dist/plugins/file-run-store.d.ts +26 -12
  207. package/dist/plugins/file-run-store.js +63 -63
  208. package/dist/plugins/imported-layer-root-retirement.d.ts +17 -8
  209. package/dist/plugins/imported-layer-root-retirement.js +4 -8
  210. package/dist/plugins/inbox-payload-column.d.ts +14 -0
  211. package/dist/plugins/inbox-payload-column.js +13 -0
  212. package/dist/plugins/local-checkpoint-store.d.ts +5 -0
  213. package/dist/plugins/local-checkpoint-store.js +11 -7
  214. package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
  215. package/dist/plugins/local-data-dir-modes-m39.js +182 -0
  216. package/dist/plugins/local-engine-high-water.d.ts +11 -0
  217. package/dist/plugins/local-engine-high-water.js +59 -0
  218. package/dist/plugins/local-session-store.d.ts +91 -25
  219. package/dist/plugins/local-session-store.js +124 -45
  220. package/dist/plugins/local-task-attachment-store.d.ts +2 -0
  221. package/dist/plugins/local-task-attachment-store.js +10 -11
  222. package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
  223. package/dist/plugins/memory-embedder-fingerprint.js +52 -12
  224. package/dist/plugins/memory-embedder.d.ts +1 -1
  225. package/dist/plugins/memory-embedder.js +2 -2
  226. package/dist/plugins/memory-engine-pg.d.ts +4 -3
  227. package/dist/plugins/memory-engine-pg.js +4 -4
  228. package/dist/plugins/memory-engine-sql-apply.d.ts +13 -4
  229. package/dist/plugins/memory-engine-sql-apply.js +1 -1
  230. package/dist/plugins/memory-engine-tidb.d.ts +8 -5
  231. package/dist/plugins/memory-engine-tidb.js +7 -8
  232. package/dist/plugins/one-time-migrations.d.ts +145 -35
  233. package/dist/plugins/one-time-migrations.js +272 -120
  234. package/dist/plugins/owner-only-file-mode.d.ts +29 -0
  235. package/dist/plugins/owner-only-file-mode.js +141 -0
  236. package/dist/plugins/permission-rule-store-file.d.ts +8 -8
  237. package/dist/plugins/permission-rule-store-file.js +14 -2
  238. package/dist/plugins/pg-pool.d.ts +2 -1
  239. package/dist/plugins/pg-pool.js +11 -4
  240. package/dist/plugins/release-version.d.ts +20 -0
  241. package/dist/plugins/release-version.js +28 -0
  242. package/dist/plugins/remote-env-adb.d.ts +20 -4
  243. package/dist/plugins/remote-env-adb.js +39 -11
  244. package/dist/plugins/remote-env-e2b.d.ts +12 -3
  245. package/dist/plugins/remote-env-e2b.js +6 -4
  246. package/dist/plugins/remote-env-host.d.ts +9 -1
  247. package/dist/plugins/remote-env-host.js +6 -5
  248. package/dist/plugins/remote-env-k8s.d.ts +5 -2
  249. package/dist/plugins/remote-env-k8s.js +15 -4
  250. package/dist/plugins/remote-env-local-docker.d.ts +4 -1
  251. package/dist/plugins/remote-env-local-docker.js +3 -3
  252. package/dist/plugins/remote-env-ssh.d.ts +8 -2
  253. package/dist/plugins/remote-env-ssh.js +26 -24
  254. package/dist/plugins/remote-shell.d.ts +59 -30
  255. package/dist/plugins/remote-shell.js +175 -62
  256. package/dist/plugins/retention-declaration.d.ts +37 -0
  257. package/dist/plugins/retention-declaration.js +9 -0
  258. package/dist/plugins/retention-deployment.d.ts +126 -0
  259. package/dist/plugins/retention-deployment.js +88 -0
  260. package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
  261. package/dist/plugins/retention-lane-store-sql.js +103 -1
  262. package/dist/plugins/retention-sql.d.ts +29 -0
  263. package/dist/plugins/retention-sql.js +8 -0
  264. package/dist/plugins/schema-consistency.d.ts +0 -6
  265. package/dist/plugins/schema-consistency.js +1 -2
  266. package/dist/plugins/schema-ensure-legs.d.ts +1 -1
  267. package/dist/plugins/schema-ensure-legs.js +11 -1
  268. package/dist/plugins/session-ext-table.d.ts +74 -0
  269. package/dist/plugins/session-ext-table.js +121 -0
  270. package/dist/plugins/session-fork.d.ts +1 -1
  271. package/dist/plugins/session-row-sql.d.ts +8 -7
  272. package/dist/plugins/session-row-sql.js +15 -7
  273. package/dist/plugins/session-store-sql.d.ts +131 -0
  274. package/dist/plugins/session-store-sql.js +298 -0
  275. package/dist/plugins/session-workspace-cell.d.ts +2 -2
  276. package/dist/plugins/session-workspace-cell.js +2 -2
  277. package/dist/plugins/shared-memory-store-sql.d.ts +10 -0
  278. package/dist/plugins/shared-memory-store-sql.js +13 -13
  279. package/dist/plugins/snapshot-bucket-presence.js +1 -1
  280. package/dist/plugins/sql-driver.d.ts +23 -7
  281. package/dist/plugins/sql-driver.js +41 -8
  282. package/dist/plugins/sql-errors.d.ts +5 -2
  283. package/dist/plugins/sql-errors.js +4 -0
  284. package/dist/plugins/store-backend.d.ts +35 -8
  285. package/dist/plugins/store-backend.js +184 -62
  286. package/dist/plugins/store-contracts.d.ts +9 -2
  287. package/dist/plugins/store-contracts.js +1 -0
  288. package/dist/plugins/task-attachment-store.d.ts +6 -8
  289. package/dist/plugins/task-attachment-store.js +15 -10
  290. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  291. package/dist/plugins/text-column-capacity.d.ts +21 -0
  292. package/dist/plugins/text-column-capacity.js +5 -0
  293. package/dist/plugins/tidb-pool.d.ts +3 -2
  294. package/dist/plugins/tidb-pool.js +19 -31
  295. package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
  296. package/dist/read-face-posture.d.ts +6 -6
  297. package/dist/read-face-posture.js +2 -2
  298. package/dist/request-value-closure.d.ts +5 -5
  299. package/dist/request-value-closure.js +1 -1
  300. package/dist/rules-consent.d.ts +41 -13
  301. package/dist/rules-consent.js +101 -53
  302. package/dist/run-local.js +17 -13
  303. package/dist/runs.d.ts +48 -30
  304. package/dist/runs.js +26 -16
  305. package/dist/runtime-caps-resolver.d.ts +15 -8
  306. package/dist/runtime-caps-resolver.js +14 -10
  307. package/dist/sealed-key-layout.d.ts +2 -3
  308. package/dist/sealed-key-layout.js +0 -1
  309. package/dist/sealed-key.js +7 -5
  310. package/dist/security.d.ts +5 -3
  311. package/dist/sema-config-dir.js +2 -2
  312. package/dist/server-secret-env.d.ts +2 -0
  313. package/dist/service-version.d.ts +2 -1
  314. package/dist/service-version.js +2 -2
  315. package/dist/session-error-wire.d.ts +1 -1
  316. package/dist/session-incarnation.d.ts +8 -5
  317. package/dist/session-resident-background.d.ts +85 -32
  318. package/dist/session-resident-background.js +75 -7
  319. package/dist/session-row.d.ts +1 -1
  320. package/dist/session-sync-kernel.d.ts +1 -1
  321. package/dist/session-workspace.d.ts +11 -2
  322. package/dist/session-workspace.js +5 -0
  323. package/dist/sighup-idle.d.ts +1 -1
  324. package/dist/store-posture.d.ts +2 -1
  325. package/dist/stored-request-hooks-m20.d.ts +3 -2
  326. package/dist/stored-request-hooks-m20.js +19 -14
  327. package/dist/stores-sql/blob-index-sql.d.ts +148 -0
  328. package/dist/stores-sql/blob-index-sql.js +154 -0
  329. package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
  330. package/dist/stores-sql/checkpoint-store-sql.js +11 -27
  331. package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
  332. package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
  333. package/dist/{plugins → stores-sql}/pg-session-storage.d.ts +23 -76
  334. package/dist/{plugins → stores-sql}/pg-session-storage.js +80 -209
  335. package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +123 -217
  336. package/dist/{plugins → stores-sql}/retention-store-sql.js +66 -190
  337. package/dist/{plugins → stores-sql}/session-event-payload.d.ts +1 -1
  338. package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
  339. package/dist/stores-sql/session-policy-store-sql.d.ts +1 -1
  340. package/dist/stores-sql/session-write-hooks.d.ts +59 -0
  341. package/dist/stores-sql/session-write-hooks.js +2 -0
  342. package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +10 -11
  343. package/dist/{plugins → stores-sql}/tidb-session-storage.js +10 -21
  344. package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +32 -122
  345. package/dist/{plugins → stores-sql}/tidb-session-store.js +71 -171
  346. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  347. package/dist/stores-sql/tool-result-store-sql.js +1 -1
  348. package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
  349. package/dist/stores-sql/windowed-session-storage.js +61 -0
  350. package/dist/task-a2a.d.ts +7 -1
  351. package/dist/task-a2a.js +15 -14
  352. package/dist/task-cwd.d.ts +11 -3
  353. package/dist/task-cwd.js +17 -1
  354. package/dist/task-mcp.d.ts +10 -2
  355. package/dist/task-mcp.js +13 -16
  356. package/dist/task-settings.d.ts +9 -4
  357. package/dist/task-settings.js +17 -6
  358. package/dist/tool-approval.d.ts +21 -8
  359. package/dist/tool-approval.js +10 -6
  360. package/dist/trace/core-keyset-guard.d.ts +9 -3
  361. package/dist/trace/engine-notice-wire.d.ts +1 -1
  362. package/dist/trace/engine-notice-wire.js +2 -0
  363. package/dist/trace/exit-seat.d.ts +23 -1
  364. package/dist/trace/exit-seat.js +3 -0
  365. package/dist/trace/project.d.ts +33 -16
  366. package/dist/trace/project.js +12 -9
  367. package/dist/trace/redact.d.ts +1 -1
  368. package/dist/trace/redact.js +61 -73
  369. package/dist/trace/self-minted-wire.d.ts +263 -10
  370. package/dist/trace/self-minted-wire.js +90 -2
  371. package/dist/trace/sema-provenance.d.ts +45 -41
  372. package/dist/trace/sema-provenance.js +81 -10
  373. package/dist/trace/task-notification-facets.d.ts +70 -2
  374. package/dist/trace/task-notification-facets.js +71 -1
  375. package/dist/trace/wire-projection-faces.d.ts +38 -2
  376. package/dist/trace/wire-projection-faces.js +26 -1
  377. package/dist/unread-request-members.d.ts +66 -0
  378. package/dist/unread-request-members.js +41 -0
  379. package/dist/user-input-ledger.d.ts +2 -2
  380. package/dist/web-admin-client/apply-effective.js +22 -26
  381. package/dist/web-admin-client/candidate-gate.d.ts +35 -3
  382. package/dist/web-admin-client/candidate-gate.js +53 -5
  383. package/dist/web-admin-client/center-request.d.ts +4 -1
  384. package/dist/web-admin-client/center-request.js +2 -1
  385. package/dist/web-admin-client/effective-keys.js +0 -1
  386. package/dist/web-admin-client/facade.d.ts +1 -1
  387. package/dist/web-admin-client/http-client.d.ts +1 -0
  388. package/dist/web-admin-client/http-client.js +17 -9
  389. package/dist/web-admin-client/restart-signal.js +5 -2
  390. package/dist/web-admin-client/skills-mcp.d.ts +20 -6
  391. package/dist/web-admin-client/skills-mcp.js +26 -21
  392. package/dist/web-admin-client/types.d.ts +34 -49
  393. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +2 -2
  394. package/package.json +13 -12
  395. package/scripts/drain-check-local.mjs +2 -28
  396. package/scripts/drain-check.mysql.sql +2 -18
  397. package/scripts/drain-check.pg.sql +2 -18
  398. package/wire-changes/server@7.147.0.json +813 -0
@@ -0,0 +1,59 @@
1
+ import { join, resolve } from "node:path";
2
+ import { z } from "zod";
3
+ import { atomicWriteFileSync, proveDurableSync } from "./owner-only-file-mode.js";
4
+ import { createZodColumnReader, decodeJsonColumn } from "./sql-json-column.js";
5
+ import { LocalDataRootSchemaMismatchError, dataRootMismatchOf, readDataRootFileTextSync } from "./local-session-store.js";
6
+ import { buildReleaseTriple, compareReleaseTriples, releaseText, releaseTriple } from "./release-version.js";
7
+ import { StoreBootRefusalError } from "./store-contracts.js";
8
+ export const ENGINE_HIGH_WATER_FILE = "engine-high-water.json";
9
+ const ENGINE_HIGH_WATER_LABEL = `${ENGINE_HIGH_WATER_FILE} (the local engine high-water marker)`;
10
+ const ENGINE_HIGH_WATER_SHAPE = z
11
+ .object({ highWater: z.string() })
12
+ .strict()
13
+ .transform((v, ctx) => {
14
+ const triple = releaseTriple(v.highWater);
15
+ if (triple === undefined) {
16
+ ctx.addIssue({ code: "custom", message: "highWater is not a plain x.y.z release version" });
17
+ return z.NEVER;
18
+ }
19
+ return { highWater: v.highWater, triple };
20
+ });
21
+ const readEngineHighWater = createZodColumnReader(ENGINE_HIGH_WATER_SHAPE);
22
+ export function holdEngineHighWater(root, engine) {
23
+ const own = buildReleaseTriple(engine);
24
+ if (own === undefined) {
25
+ throw new StoreBootRefusalError(`DB_BACKEND=local: this build's own version (package.json "version": ${JSON.stringify(engine.slice(0, 64))}) is not a release version (x.y.z, optionally with a prerelease or build suffix) — refusing to open the data root ${root}: ` +
26
+ "the engine high-water fence needs this build's release line to show it is not older than the engines that opened this data root, and to record its own. " +
27
+ "Run a build whose package.json carries a release version.");
28
+ }
29
+ const path = resolve(join(root, ENGINE_HIGH_WATER_FILE));
30
+ const raw = readDataRootFileTextSync(path);
31
+ let equal = false;
32
+ if (raw.state === "unreadable")
33
+ throw new LocalDataRootSchemaMismatchError([dataRootMismatchOf(path, ENGINE_HIGH_WATER_LABEL, raw.fault)], "boot");
34
+ if (raw.state === "text") {
35
+ let marker;
36
+ try {
37
+ marker = decodeJsonColumn(raw.text, readEngineHighWater, ENGINE_HIGH_WATER_LABEL);
38
+ }
39
+ catch (e) {
40
+ throw new LocalDataRootSchemaMismatchError([dataRootMismatchOf(path, ENGINE_HIGH_WATER_LABEL, e)], "boot");
41
+ }
42
+ const order = compareReleaseTriples(marker.triple, own);
43
+ if (order > 0) {
44
+ throw new LocalDataRootSchemaMismatchError([{ path, label: ENGINE_HIGH_WATER_LABEL, problem: "newer-engine", unknownFields: [], engineHighWater: { highWater: marker.highWater, engine } }], "boot");
45
+ }
46
+ equal = order === 0;
47
+ }
48
+ try {
49
+ if (equal)
50
+ proveDurableSync(path);
51
+ else
52
+ atomicWriteFileSync(path, `${JSON.stringify({ highWater: releaseText(own) })}\n`, { durability: "required" });
53
+ }
54
+ catch (e) {
55
+ throw new Error(`DB_BACKEND=local cannot record the engine high-water ${path} durably: ${e instanceof Error ? e.message : String(e)} — refusing to open the data root: ` +
56
+ "writing to it without a durable high-water would let an older engine open it later unrefused. Repair write access to the data root and restart.", { cause: e });
57
+ }
58
+ }
59
+ //# sourceMappingURL=local-engine-high-water.js.map
@@ -3,10 +3,10 @@ import { type PlacementAcquireOpts } from "./session-placement.js";
3
3
  import { z } from "zod";
4
4
  import { StoreBootRefusalError, type SessionRunClaimSource, type SessionRunRollupSource, type SessionSummary } from "./store-contracts.js";
5
5
  import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowAnswer, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
6
+ import { type SqlJsonColumnProblem } from "./sql-json-column.js";
6
7
  import { type LocalArchiveKeptStamped, type LocalArchiveUnreadable } from "./one-time-migrations.js";
7
8
  import { type Logger } from "../observability/logger.js";
8
9
  import { type SessionFace } from "../session-incarnation.js";
9
- import { type ExecutionLane } from "../execution-lane-caps.js";
10
10
  import { type ExecutionPlacementSeat, type LocalPlacementBackfillSeat } from "../execution-placement.js";
11
11
  import { type ForkPlacement, type ForkTarget, type ForkToOutcome } from "./session-fork.js";
12
12
  import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
@@ -21,7 +21,7 @@ export declare const SESSION_META_CELL_OBJECT: z.ZodObject<{
21
21
  owner: z.ZodOptional<z.ZodNullable<z.ZodString>>;
22
22
  execution: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
23
23
  role: z.ZodLiteral<"writer">;
24
- lane: z.ZodCustom<ExecutionLane, ExecutionLane>;
24
+ lane: z.ZodCustom<"host" | "e2b" | "k8s" | "ssh" | "adb" | "local-docker" | "device" | "none", "host" | "e2b" | "k8s" | "ssh" | "adb" | "local-docker" | "device" | "none">;
25
25
  }, z.core.$strict>, z.ZodObject<{
26
26
  role: z.ZodLiteral<"mirror">;
27
27
  }, z.core.$strict>], "role">>;
@@ -29,33 +29,84 @@ export declare const SESSION_META_CELL_OBJECT: z.ZodObject<{
29
29
  createdAt: z.ZodOptional<z.ZodString>;
30
30
  forkedFrom: z.ZodOptional<z.ZodString>;
31
31
  }, z.core.$strict>;
32
- /** 严格边档清册里一只边档的对账读数(拒句的逐文件一段;{@link LocalDataRootSchemaMismatchError.findings})。 */
33
- export interface LocalSidecarMismatch {
32
+ /**
33
+ * 数据根对账里一只文件的读数(拒句的逐文件一段;{@link LocalDataRootSchemaMismatchError.findings}):严格边档清册里的一只边档,或引擎版本高水位 marker
34
+ * (`local-engine-high-water.ts`)。**判别联合**,判别位 `problem`:`"newer-engine"` ⇒ {@link LocalDataRootNewerEngine}(必带 `engineHighWater`);
35
+ * 别的每一个词 ⇒ {@link LocalDataRootUnreadable}(没有 `engineHighWater`)—— 载荷跟着判别位走,由类型保证,不靠运行时约定。
36
+ */
37
+ export type LocalDataRootMismatch = LocalDataRootUnreadable | LocalDataRootNewerEngine;
38
+ /**
39
+ * 读不出的种别:形 —— `not-json` / `malformed` / `not-text`(形读者的判词),或 I/O 的 errno(`EACCES` / `EISDIR` / 悬空符号链接的 `ENOENT` …,恒以 `E` 起头);
40
+ * 说不出 errno ⇒ `unreadable`。`newer-engine` 不在其中(那是另一支)。
41
+ */
42
+ export type LocalDataRootReadProblem = SqlJsonColumnProblem | `E${string}` | "unreadable";
43
+ /** 一只文件本构建读不出(形或 I/O);形读者不认得的键非空 ⇒ 这只文件是更新的引擎写下的(数据根比本构建新)。 */
44
+ export interface LocalDataRootUnreadable {
34
45
  /** 文件的绝对路径。 */
35
46
  readonly path: string;
36
- /** 角色(`<文件名> (the local <角色> sidecar)`)。 */
47
+ /** 角色(`<文件名> (the local <角色> sidecar)` / `<文件名> (the local engine high-water marker)`)。 */
37
48
  readonly label: string;
38
- /** 读不出的种别:`not-json` / `malformed`(形),或 I/O 的 errno(`EACCES` …;没有 errno ⇒ `unreadable`)。 */
39
- readonly problem: string;
40
- /** 格里本构建不认得的键(非空 ⇒ 这只边档是更新的引擎写下的:数据根比本构建新)。 */
49
+ readonly problem: LocalDataRootReadProblem;
50
+ /** 本构建不认得的键(非空 ⇒ 这只文件是更新的引擎写下的:数据根比本构建新)。 */
41
51
  readonly unknownFields: readonly string[];
42
52
  }
53
+ /** 引擎版本高水位 marker 读得出,而它记的高水位高于本构建:数据根被更新的引擎服务过。 */
54
+ export interface LocalDataRootNewerEngine {
55
+ /** marker 的绝对路径。 */
56
+ readonly path: string;
57
+ /** 角色(`engine-high-water.json (the local engine high-water marker)`)。 */
58
+ readonly label: string;
59
+ readonly problem: "newer-engine";
60
+ readonly unknownFields: readonly [];
61
+ /** marker 记的高水位(纯 `x.y.z`)与本构建的版本串(原样:发布线读得出的 `x.y.z`,可带预发布 / 构建后缀;读不出的构建在判之前就拒了)。 */
62
+ readonly engineHighWater: {
63
+ readonly highWater: string;
64
+ readonly engine: string;
65
+ };
66
+ }
43
67
  /**
44
68
  * **local 数据根的「本构建读不出」拒**(起服对账一族的 local 腿;机读码与 SQL 列册对账同一个 `config.schema_mismatch`,拒句同形)。
45
69
  * 一只严格边档用它那一只严格读者读不出 ⇒ 这一只拒,文件原地不动、一个字节不改。两处触发、一只类:
46
- * · `boot` —— 起服对账({@link LocalSessionStore.assertStrictSidecarsReadable},唯一调用点 `LocalBackend.ensureSchema()`)把清册里每一只读不出的合成一只:拒启;
70
+ * · `boot` —— 两处拒启:local 后端**打开数据根的那一刻**(锁在手之后、本仓任何一只店建起之前;`store-backend.ts` 的 `openLocalBackend`)引擎版本高水位 marker 读不出 / 高于本构建
71
+ * (`local-engine-high-water.ts`,读数种别 `newer-engine` 或读不出的那几种;同一判词「数据根比代码新」的第二种证据 —— 这一拒由公开序列的 `ensureSchema()` 交出);
72
+ * 起服对账(`LocalBackend.ensureSchema()`)里严格边档清册读不出的每一只合成一只({@link LocalSessionStore.assertStrictSidecarsReadable});
47
73
  * · `serving` —— 起服之后这只边档第一次被碰时才读不出(嵌入方跳过了 `ensureSchema()`;或起服后别的写者把它写成本构建读不出的形,在替换之前那一读里看见):
48
74
  * 读不出的是**形**(`not-json` / `malformed`,确定的判词)⇒ 这只边档在本进程里从此每一读每一写都交这一只(重启才重判);碰它的请求失败(把它交给 HTTP 全局兜底的路由:
49
75
  * 500 `internal.error` + 一行 error 级 `request_error` 带本码;自己接住故障的路由按各自的 500 臂答)。I/O 故障(非确定)不粘。
50
76
  * 属 {@link StoreBootRefusalError} 族:起服降级臂不吞它(缺省推导的 local 不会被吃成内存后端)。拒句零原文:只点名文件、种别与不认得的键名,不带任何值。
51
77
  */
52
78
  export declare class LocalDataRootSchemaMismatchError extends StoreBootRefusalError {
53
- readonly findings: readonly LocalSidecarMismatch[];
79
+ readonly findings: readonly LocalDataRootMismatch[];
54
80
  readonly when: "boot" | "serving";
55
81
  /** 机读码(与 SQL 列册对账 / 退役 schema 的拒启同一个)。 */
56
82
  readonly code = "config.schema_mismatch";
57
- constructor(findings: readonly LocalSidecarMismatch[], when: "boot" | "serving");
83
+ constructor(findings: readonly LocalDataRootMismatch[], when: "boot" | "serving");
58
84
  }
85
+ /** 数据根对账文件的一读的读数(两个出口同形)。 */
86
+ export type DataRootFileText = {
87
+ readonly state: "absent";
88
+ } | {
89
+ readonly state: "unreadable";
90
+ readonly fault: unknown;
91
+ } | {
92
+ readonly state: "text";
93
+ readonly text: string;
94
+ };
95
+ /**
96
+ * 数据根对账文件的**字节一读**(唯一的判;异步 {@link readDataRootFileText} 与同步 {@link readDataRootFileTextSync} 两个出口只差 I/O 的同步性,`ENOENT` 之后的判都经 {@link judgedAfterEnoent}):
97
+ * 严格边档的三个触点({@link SidecarMap.readDisk})、一次性迁移 M40 的会话腿走异步出口,引擎版本高水位 marker(`local-engine-high-water.ts`,在后端打开的同步段里判)走同步出口,
98
+ * 所以「缺文件 / 读不出(I/O)/ 读到字节」在各处是同一个判决。形判据不在这里:每个调用点用自己的具名形读者解码([ref] 门③:读者不经参数传递,哪一套键判了这只文件在调用点上看得见)。
99
+ * **缺席 = 目录里没有这一项**(不是读不出):`ENOENT` 而这一项还在(悬空符号链接 —— 链接在、目标不在)⇒ 读不出,不当缺席(与配置根的悬空链接同一条律);
100
+ * 读完 `ENOENT` 到 `lstat` 之间这一项被删 ⇒ 缺席;`lstat` 自己判不了(非 `ENOENT`)⇒ 读不出。
101
+ */
102
+ export declare function readDataRootFileText(path: string): Promise<DataRootFileText>;
103
+ /** {@link readDataRootFileText} 的同步出口(同一判)。 */
104
+ export declare function readDataRootFileTextSync(path: string): DataRootFileText;
105
+ /**
106
+ * 读不出的故障 → 一段读数(唯一一处;严格边档的拒与引擎版本高水位 marker 同经它):形的故障({@link SqlJsonColumnError})带它的种别与形读者不认得的键,
107
+ * I/O 故障带 errno(没有 ⇒ `unreadable`)。
108
+ */
109
+ export declare function dataRootMismatchOf(path: string, label: string, fault: unknown): LocalDataRootUnreadable;
59
110
  /**
60
111
  * local 车道会话存档的**唯一 repo 出口**:本仓里每一只读 `<root>/sessions/*.jsonl` 的 `FileSessionRepo` 都从这里取(`LocalSessionStore` 的底座;会话视图窗不再自开 repo,
61
112
  * 它的元数据与日志都由店交出 —— `local-session-window.ts`),所以坏记录的披露座只有这一份接线。座 = F 类 `server.session-repo.corrupt-record-skipped`(会话存档里读不出的记录丢的是可用性:
@@ -158,27 +209,24 @@ export declare class LocalSessionStore implements SessionStore {
158
209
  /** Set by touch/delete when the map mutated; cleared by a write that captured the change. A write that ran while this
159
210
  * is set re-runs (a touch that landed during an active write isn't dropped). Drives the chain's re-run guard (bug B). */
160
211
  private dirty;
161
- /** Monotonic per-write counter — feeds a UNIQUE tmp filename per write so overlapping writes can't share one tmp path
162
- * and tear each other's bytes (bug B). */
163
- private writeSeq;
164
212
  /** Last time a sidecar-write error was warned (bug D): throttle the best-effort warn so a persistently-failing disk
165
213
  * doesn't spam the log on every debounced flush. 0 ⇒ never warned. */
166
214
  private lastWarnAt;
167
215
  /** Absolute path to the lastActivity JSON sidecar, or undefined when not persisting (in-memory-only deployment). */
168
216
  private readonly sidecarPath?;
169
- /** Title map (`session-titles.json`) — the local twin of the SQL `session_meta.title` column: one cell, two writers
217
+ /** Title map (`session-titles.json`) — the local twin of the SQL `session_ext.title` column: one cell, two writers
170
218
  * (the user's {@link writeSessionRowCells}; the auto-titler's {@link setTitleIfNull}, fills an absent cell
171
219
  * only — writer order: `src/session-row.ts`; both under the session write lock). Separate file from the lastActivity sidecar ON PURPOSE (that format is a
172
220
  * #10 clobber area); ultra-low-rate. Cross-instance sharing: see {@link SidecarMap}. */
173
221
  private readonly titles;
174
- /** Pin map (`session-pinned.json`, {@link readPinnedSidecar}) — the local twin of `session_meta.pinned`. Its own file for
222
+ /** Pin map (`session-pinned.json`, {@link readPinnedSidecar}) — the local twin of `session_ext.pinned`. Its own file for
175
223
  * the same reason titles have theirs (one small map per concern, no format coupling); absent key = not pinned. */
176
224
  private readonly pinned;
177
225
  /** [ref] — instruction-source baselines (`session-instruction-sources.json`): the chain member the session's previous
178
- * run selected, encoded like the SQL twins' `session_meta.instruction_source` column ('' = none; missing key = never
226
+ * run selected, encoded like the SQL twins' `session_ext.instruction_source` column ('' = none; missing key = never
179
227
  * recorded). Its own file for the same reason titles have theirs: one small map per concern, no format coupling. */
180
228
  private readonly instructionSources;
181
- /** 会话行的位置与项目那一格(`session-workspaces.json`)—— SQL 孪生 `session_meta.workspace` 的 local 对位:`{ [sessionId]: 那一格的文本 }`,
229
+ /** 会话行的位置与项目那一格(`session-workspaces.json`)—— SQL 孪生 `session_ext.workspace` 的 local 对位:`{ [sessionId]: 那一格的文本 }`,
182
230
  * 文本与 SQL 列同一只编解码(`session-workspace-cell.ts`,一格读不出 ⇒ 读作没有记录并计一次)。缺键 = 没有记录。一事一档,理由同 titles。 */
183
231
  private readonly workspaces;
184
232
  /**
@@ -212,7 +260,7 @@ export declare class LocalSessionStore implements SessionStore {
212
260
  * 执行席(规则在 `src/execution-placement.ts`):读 = 那一格的 `execution` 位(fork 创建的格而存档不在 ⇒ `forking`);place / markMirror 在这只会话的写锁里判住客
213
261
  * (化身 + 头读投影的属主 `owner ?? null`)再只改 `execution` 那一位 —— 住客换了(删后重建、M10 窗内认领)⇒ `gone`,不写。
214
262
  * 认领(run 店的单活槽,{@link SessionRunClaimSource}):`markMirror` 在同一把写锁里读它、只拦「absent → mirror」这一笔(判序与 SQL 孪生同,见 {@link markMirrorExecution});`place` 是认领持有者自己的初始放置写,不读认领。
215
- * local 的导入三写口不读认领 —— 它们的认领判在路由(第一阶段写前判 + 提交前复判),没有 SQL 孪生那样的写事务内读。
263
+ * 导入三写口(整段 `importEntries` / `replaceEntries` / 暂存提交)同在写锁里读它、交 SQL 事务内钩 `import` 臂的那一只判官 {@link judgeImportWrite}(见 {@link admitImportHeld});路由的认领判(第一阶段写前判 + 提交前复判)是更早的一道,不是唯一一道。
216
264
  */
217
265
  readonly executionPlacement: ExecutionPlacementSeat;
218
266
  /**
@@ -237,6 +285,15 @@ export declare class LocalSessionStore implements SessionStore {
237
285
  constructor(repo: HeadReadingSessionRepo & Partial<PlacementRecordingRepo>, runs: SessionRunRollupSource & SessionRunClaimSource, dataDir?: string, opts?: {
238
286
  log?: Pick<Logger, "info" | "warn">;
239
287
  } & LocalSessionStoreDeclaration);
288
+ /**
289
+ * 🔴 一次性迁移 **M40 的 local 会话腿**(窗口见 `plugins/one-time-migrations.ts` 登记表 M40 行;唯一调用点 `LocalBackend.ensureSchema()` 的**第一步**,
290
+ * 排在 {@link assertStrictSidecarsReadable} 之前 —— 严格读的执行位只认本版车道词,旧词先到那里就整只拒启):会话元数据边档里 `execution` 位记成退役词的
291
+ * writer 格改记新名,整只原子重写一次。只改那一个词,别的字节按 JSON 原样写回。字节经边档的唯一一读({@link readDataRootFileText})。
292
+ * 缺文件 / 读不出(I/O 故障如 EACCES / EISDIR、坏 JSON、不是对象)⇒ 不抛、不改,交给其后的严格对账判 —— 它读同一只文件、以 `config.schema_mismatch`
293
+ * 拒(那是这只拒码在本店的唯一铸点);这里抛了,拒码与具名拒都会丢。读得出而写不进 ⇒ {@link ExecutionLaneRenameWriteRefusedError}(拒启,降级臂不吞)。
294
+ * 内存部署 / 没有旧词 ⇒ 零写。返回改了的格数。词的属主 = `execution-lane-rename-m40.ts`。
295
+ */
296
+ renameRetiredExecutionLaneWordM40(): Promise<number>;
240
297
  /**
241
298
  * [ref] —— **起服对账的 local 腿**(SQL 列册对账的对位;唯一调用点 `LocalBackend.ensureSchema()`,排在 M33 快照消化之前 —— 消化会写会话元数据格):
242
299
  * 严格边档清册里的每一只读一次盘(`SidecarMap.reconcile`:冷 ⇒ 载入那一读;已载入 ⇒ 重读只判);读不出的合成**一只** {@link LocalDataRootSchemaMismatchError}
@@ -428,7 +485,7 @@ export declare class LocalSessionStore implements SessionStore {
428
485
  * 抛 ⇒ 读不出(尾部 I/O 故障之类:行头好、头读照样抛);否则读得出。所以行上的 `unreadable` 与头读、读口对同一只会话的答复同读。
429
486
  */
430
487
  private listedRowOf;
431
- /** [ref] —— 换源通告的基线读(local 孪生;三态读法与 SQL 两孪生的 `session_meta.instruction_source` 同一只编解码)。 */
488
+ /** [ref] —— 换源通告的基线读(local 孪生;三态读法与 SQL 两孪生的 `session_ext.instruction_source` 同一只编解码)。 */
432
489
  readInstructionSource(sessionId: string): Promise<InstructionSourceBaseline>;
433
490
  /**
434
491
  * [ref] —— CAS 覆写基线(契约见 `InstructionSourceStore`:期望值不符 ⇒ 不写;**没落盘 ⇒ 内存不动、抛**,与 SQL 孪生
@@ -692,9 +749,10 @@ export declare class LocalSessionStore implements SessionStore {
692
749
  private withImportLock;
693
750
  /**
694
751
  * 三个导入写口(`importEntries` / `replaceEntries` / 暂存提交的换装臂)的共用写序(调用方持这只会话的写锁,属主的形已在入口判过):
695
- * ① 只读、零写:引擎闸 `validateEntriesForImport`(坏包 ⇒ `invalid_session`)· 本处写者 ⇒ {@link SessionWriterPresentError} · `forking` ⇒
696
- * {@link SessionForkInProgressError} · 不是这个写口的门判过的行 ⇒ {@link importRowRefused}(判据 {@link importMayLand},按写口带来的 {@link ImportLanding};
697
- * 这条路径上唯一一次判属主);
752
+ * ① 只读、零写:引擎闸 `validateEntriesForImport`(坏包 ⇒ `invalid_session`)· 准入 {@link admitImportHeld}(读 run 认领、交判官 {@link judgeImportWrite}:活 run 认领 ⇒
753
+ * `SessionActiveRunError` · 本处写者 ⇒ `SessionWriterPresentError` · `forking` ⇒ {@link SessionForkInProgressError};SQL 孪生的事务内钩 `import` 臂同一只判官、同序、同拒;
754
+ * `judged` 在 {@link commitStagedImport} 里准入过,不再来一遍)· 不是这个写口的门判过的行 ⇒ {@link importRowRefused}(判据 {@link importMayLand},按写口带来的
755
+ * {@link ImportLanding};这条路径上唯一一次判属主);
698
756
  * ② 行先:{@link landImportCell}(属主重戳为导入者、化身与 fork 键沿用或新铸、执行位 mirror;建档时刻与谱系照带,新行写此刻);
699
757
  * ③ 内容,按写口的动词(唯一读法在下面这一只 switch;这里不读那一格、不判属主,只换**内容**;交给引擎仓的属主恒缺席 —— 规则一):
700
758
  * · `create` / `judged`(导入):原地换锁里读到的那一份存档的日志(没有存档 ⇒ 导入新建一只,匿名),以锁里读到的存档头为 `expect.row`(**行代号**:
@@ -714,6 +772,14 @@ export declare class LocalSessionStore implements SessionStore {
714
772
  * 读法 = {@link isPlacedRow}(行头读不出 ⇒ 按普通行答,坏档照样换得掉;说不出 ⇒ 抛)。
715
773
  */
716
774
  private assertNotPlacedHeld;
775
+ /**
776
+ * 三个导入写口落 mirror 之前的**准入**(调用方持这只会话的写锁;{@link importHeld} ① 与暂存提交「内容相同、日志不动」那一支都经这一处):**取锁 → 读认领 → 判 → 写**,
777
+ * 与 {@link markMirrorExecution} 同序。判官就是 SQL 孪生事务内钩 `import` 臂的那一只 {@link judgeImportWrite}(词、拒码、先认领后写者都在判官里,本店不另写一份):
778
+ * 认领读 run 店的单活槽({@link SessionRunClaimSource})、格读 {@link placementCellOf}(`forking` 按 {@link forkingOf} 判好)—— 读口按需:认领在 ⇒ 不探存档。
779
+ * 拒 ⇒ 抛那只 typed 错,此时导入的任何一笔写(格 / 存档 / 新近)都还没发生(读侧的补铸除外:盘上有会话而格缺时,读它的读口先补铸那一格 —— 与别的读口同,幂等)。
780
+ * 路由的认领判(第一阶段写前判 + 提交前复判)仍在、更早一道,不再是唯一防线。
781
+ */
782
+ private admitImportHeld;
717
783
  /**
718
784
  * ① 的属主 / 住客条件:这一行此刻是不是这个写口的门判过的行({@link ImportLanding};一次读、零写 —— 格缺时「行在不在」只问存档的唯一探针
719
785
  * {@link existsInRepo},不补铸那一格)。属主比的是头读的读法 `owner ?? null`(从没被记下 = 无主;格缺而存档在 = 从没被记下),比较器是删除门的那一只
@@ -723,7 +789,7 @@ export declare class LocalSessionStore implements SessionStore {
723
789
  private importMayLand;
724
790
  /**
725
791
  * ② 推送落下的那一格 —— 三个导入写口与暂存提交「内容相同、日志不动」那一支都经这**一处**(调用方持写锁、① 已判过):属主重戳为导入者;
726
- * 化身与 fork 键经 {@link carriedRowCells}(三只店同一句)读那一格自己 —— 行在 ⇒ 沿用(同一行 = 同一位住客,属主重戳与否不改这一判),刚建 ⇒ 新铸、不带键;
792
+ * 化身与 fork 键经 {@link carriedRowCells}(SQL 两店就地改写同一行、化身与旁表行不动 —— 同一结局)读那一格自己 —— 行在 ⇒ 沿用(同一行 = 同一位住客,属主重戳与否不改这一判),刚建 ⇒ 新铸、不带键;
727
793
  * 执行位 = mirror(导入落下的是「写者在别处」;SQL 孪生的提交点同一句写)。**在 `before` 上改这三位**,别的位(建档时刻、谱系)照带;刚建的行建档时刻 = 此刻。
728
794
  * 只写这一格:四只行边档与新近不在这里。交回写之前的那一格(`undefined` = 原本没有),③ 撤回用。
729
795
  */
@@ -764,7 +830,7 @@ export declare class LocalSessionStore implements SessionStore {
764
830
  * {@link LocalStagingHandle.commit} 的整段 —— **一次持锁**(这只会话的写锁,{@link withSessionWriteLock}):属主 CAS(此刻的属主格 = 这次导入的门判过的那一格,
765
831
  * 否则抛 {@link StagedCommitOwnerChangedError},一个字节不写;在判目的端日志之前判,SQL 孪生同序)→ 分区判 → 提交时重新分类(目的端此刻的日志 vs staged)→
766
832
  * 这一提交要换日志(不是「id 与内容都相同」那一支)⇒ 带戳存档判({@link assertArchiveAnonymous};只有 local 店有带戳存档,SQL 孪生没有这一步)→
767
- * 拒未经同意的 fork / stale / 载荷分叉(带戳判在它们之前:带戳存档上日志换不了,同步冲突与载荷分叉的出路 —— 带同意重来 —— 无从谈起)→ 本处写者拒 → 换装(导入写序 ①②③,落点 `judged` = 上面这一判;化身与 fork 键按换装律沿用)或 identical 且内容相同时日志不动、
833
+ * 拒未经同意的 fork / stale / 载荷分叉(带戳判在它们之前:带戳存档上日志换不了,同步冲突与载荷分叉的出路 —— 带同意重来 —— 无从谈起)→ 准入({@link admitImportHeld}:读认领 → 本处写者 / fork 中间态 → 拒;fork 中间态的拒也在这一处,不再在提交入口另判一遍)→ 换装(导入写序 ①②③,落点 `judged` = 上面这一判;化身与 fork 键按换装律沿用)或 identical 且内容相同时日志不动、
768
834
  * 行照样落(② 那一格:重戳属主 + mirror)→ 两支同记新近(推送即活动)→ **在同一次持锁里**
769
835
  * 读出落定的会话面交回。锁放下之前谁也换不掉这一位住客,所以交回的面就是这次提交落下的那一位(SQL 孪生在提交事务里读同一行,同义);
770
836
  * 提交之后按住客条件写的重放(会话行两位)只认这只面,不再重读。改属主的每一条写腿(登记 / 删除 / 导入 / 换装)都在同一把锁里,所以锁内这一读到锁放下都成立。
@@ -1,26 +1,27 @@
1
- import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
- import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
1
+ import { lstat, readFile } from "node:fs/promises";
2
+ import { closeSync, existsSync, fsyncSync, lstatSync, openSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
  import { isDeepStrictEqual } from "node:util";
5
5
  import { dirname, join, resolve } from "node:path";
6
+ import { atomicWriteFileAsync } from "./owner-only-file-mode.js";
6
7
  import { assertFirstOwnerWritable, forkOwnerOf, importerOwnerOf, placedSessionOwnerOf } from "./session-ownership.js";
7
8
  import { SessionOwnerUnreadableError, StampedSessionArchiveError, sessionUnreadableOf } from "../session-error-wire.js";
8
- import { FileSessionRepo, SessionError, StoredSession, admitSessionPlacement, atomicWriteFile, sessionLogMarkOf, settledForkOptions, uuidv7, validateEntriesForImport } from "@sema-agent/core";
9
+ import { FileSessionRepo, SessionError, StoredSession, admitSessionPlacement, atomicWriteFile, ensureDir, sessionLogMarkOf, settledForkOptions, uuidv7, validateEntriesForImport } from "@sema-agent/core";
9
10
  import { placementCapabilityOf } from "./session-placement.js";
10
11
  import { z } from "zod";
11
12
  import { contentForkRelation, fastForwardSharedContentDiverged, identicalIdsAlsoIdenticalContent } from "../session-sync-content.js";
12
- import { StoreBootRefusalError } from "./store-contracts.js";
13
+ import { SCHEMA_MISMATCH_ERROR_CODE, StoreBootRefusalError } from "./store-contracts.js";
13
14
  import { judgeSessionRowWrite, sessionRowTagKey, settleSessionRowWrite, } from "../session-row.js";
14
15
  import { quarantineUnreadableSidecar } from "./local-sidecar-quarantine.js";
15
- import { createZodColumnReader, decodeJsonColumn, isJsonColumnBodyProblem, readStringRecordColumn, SqlJsonColumnError } from "./sql-json-column.js";
16
- import { SCHEMA_MISMATCH_ERROR_CODE } from "./schema-consistency.js";
17
- import { ONE_TIME_MIGRATIONS, migrationWindowOpen } from "./one-time-migrations.js";
16
+ import { createZodColumnReader, decodeJsonColumn, isJsonColumnBodyProblem, isJsonObject, readJsonObjectColumn, readStringRecordColumn, SqlJsonColumnError } from "./sql-json-column.js";
17
+ import { RENAMED_EXECUTION_LANE_M40, RETIRED_EXECUTION_LANE_WORD_M40 } from "./execution-lane-rename-m40.js";
18
+ import { ExecutionLaneRenameWriteRefusedError, ONE_TIME_MIGRATIONS, migrationWindowOpen } from "./one-time-migrations.js";
18
19
  import { recordFailOpen } from "../observability/fail-open.js";
19
20
  import { createLogger } from "../observability/logger.js";
20
- import { UNMANAGED_RETENTION } from "./retention-store-sql.js";
21
+ import { UNMANAGED_RETENTION } from "./retention-declaration.js";
21
22
  import { UnreadableLeaf, carriedRowCells, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
22
23
  import { isExecutionLane } from "../execution-lane-caps.js";
23
- import { SessionForkInProgressError, SessionWriterPresentError, WRITABLE, assertPlaceableLane, isUnusableArchive, settledMirrorOutcome, settledPlaceOutcome, } from "../execution-placement.js";
24
+ import { SessionForkInProgressError, WRITABLE, assertPlaceableLane, isUnusableArchive, judgeImportWrite, settledMirrorOutcome, settledPlaceOutcome, } from "../execution-placement.js";
24
25
  import { classifyForkTarget, forkByFreshId } from "./session-fork.js";
25
26
  import { decodeInstructionSource, encodeInstructionSource, sameInstructionSourceBaseline } from "../instruction-source-notice.js";
26
27
  import { decodeSessionWorkspaceCell, encodeSessionWorkspaceCell } from "./session-workspace-cell.js";
@@ -126,9 +127,13 @@ function rowMetadataOf(sessionId, meta) {
126
127
  return { id: sessionId, createdAt: rowCreatedAtOf(sessionId, meta), owner: face.owner, incarnation: face.incarnation, ...(meta.forkedFrom !== undefined ? { forkedFrom: meta.forkedFrom } : {}) };
127
128
  }
128
129
  const isForkCreatedCell = (meta) => meta !== undefined && meta.forkKey !== undefined && meta.execution === undefined;
130
+ function placementCellOf(meta, forking) {
131
+ return forking ? { role: "forking" } : (meta?.execution ?? { role: "absent" });
132
+ }
129
133
  function readingOfCell(sessionId, meta, forking) {
130
- return { occupant: sessionFaceFromRow(sessionId, meta.owner ?? null, meta.incarnation), cell: forking ? { role: "forking" } : (meta.execution ?? { role: "absent" }) };
134
+ return { occupant: sessionFaceFromRow(sessionId, meta.owner ?? null, meta.incarnation), cell: placementCellOf(meta, forking) };
131
135
  }
136
+ const SESSION_META_SIDECAR_FILE = "session-incarnations.json";
132
137
  const LOCAL_EXECUTION_CELL = z.discriminatedUnion("role", [
133
138
  z.object({ role: z.literal("writer"), lane: z.custom((v) => typeof v === "string" && isExecutionLane(v), { message: "not an execution lane" }) }).strict(),
134
139
  z.object({ role: z.literal("mirror") }).strict(),
@@ -154,18 +159,23 @@ function ownerClaimWindowOpen() {
154
159
  const SIDECAR_MAP_STATE_BY_PATH = new Map();
155
160
  const SESSION_WRITE_LOCKS_BY_ROOT = new Map();
156
161
  const quotedFieldNames = (names) => `${names.slice(0, 8).map((name) => JSON.stringify(name.slice(0, 64))).join(", ")}${names.length > 8 ? `, … (${names.length} in all)` : ""}`;
162
+ function describeDataRootFinding(f) {
163
+ if (f.problem === "newer-engine") {
164
+ return ` ${f.label} at ${f.path}: engine high-water ${f.engineHighWater.highWater} — the data root has been opened by server ${f.engineHighWater.highWater}, NEWER than this build (${f.engineHighWater.engine}).`;
165
+ }
166
+ return f.unknownFields.length > 0
167
+ ? ` ${f.label} at ${f.path}: UNKNOWN field(s) ${quotedFieldNames(f.unknownFields)} — the data root is NEWER than this build (a newer server engine wrote it).`
168
+ : ` ${f.label} at ${f.path}: unreadable (${f.problem}) — this build cannot read it, and an unreadable file is treated as a mismatch, not as empty.`;
169
+ }
157
170
  function describeLocalDataRootMismatch(findings, when) {
158
171
  const lines = [
159
172
  when === "boot"
160
173
  ? `${SCHEMA_MISMATCH_ERROR_CODE}: the local data root does not match this build's shape — refusing to start.`
161
174
  : `${SCHEMA_MISMATCH_ERROR_CODE}: the local data root does not match this build's shape — refusing to read or write the file(s) named below until this process is restarted.`,
162
175
  ];
163
- for (const f of findings) {
164
- lines.push(f.unknownFields.length > 0
165
- ? ` ${f.label} at ${f.path}: UNKNOWN field(s) ${quotedFieldNames(f.unknownFields)} — the data root is NEWER than this build (a newer server engine wrote it).`
166
- : ` ${f.label} at ${f.path}: unreadable (${f.problem}) — this build cannot read it, and an unreadable file is treated as a mismatch, not as empty.`);
167
- }
168
- lines.push("This build never reads such a file as empty and never writes over it: either would silently change what the file records.", "", " Ways out (these three and no others):", " (1) the data root is NEWER than this build (unknown fields): deploy the build that wrote it;", " (2) roll back: restore the data-root backup taken before the upgrade, then start this build — also the way out when the file's bytes are damaged (not-json / malformed);", " (3) the file could not be read at all (an I/O error such as EACCES): repair access to it (permissions, mount, disk) and restart — nothing in the file needs changing.", "The check behind this refusal only reads: the file was left in place and nothing in the data root was changed.");
176
+ for (const f of findings)
177
+ lines.push(describeDataRootFinding(f));
178
+ lines.push("This build never reads such a file as empty and never writes over it: either would silently change what the file records.", "", " Ways out (these three and no others):", " (1) the data root is NEWER than this build (unknown fields, or an engine high-water above this build): deploy the build that wrote it (for a high-water: server at or above it) — to run an older build on this machine, give it its own data root (LOCAL_DATA_ROOT);", " (2) roll back: restore the data-root backup taken before the upgrade, then start this build — also the way out when the file's bytes are damaged (not-json / malformed);", " (3) the file could not be read at all (an I/O error such as EACCES): repair access to it (permissions, mount, disk) and restart — nothing in the file needs changing.", "The check behind this refusal only reads: the file(s) named above were left in place, unchanged.");
169
179
  return lines.join("\n");
170
180
  }
171
181
  export class LocalDataRootSchemaMismatchError extends StoreBootRefusalError {
@@ -179,6 +189,56 @@ export class LocalDataRootSchemaMismatchError extends StoreBootRefusalError {
179
189
  this.name = "LocalDataRootSchemaMismatchError";
180
190
  }
181
191
  }
192
+ export async function readDataRootFileText(path) {
193
+ try {
194
+ return { state: "text", text: await readFile(path, "utf8") };
195
+ }
196
+ catch (e) {
197
+ if (e?.code !== "ENOENT")
198
+ return { state: "unreadable", fault: e };
199
+ return judgedAfterEnoent(e, await lstat(path).then(() => ENTRY_PRESENT, (fault) => ({ fault })));
200
+ }
201
+ }
202
+ export function readDataRootFileTextSync(path) {
203
+ try {
204
+ return { state: "text", text: readFileSync(path, "utf8") };
205
+ }
206
+ catch (e) {
207
+ if (e?.code !== "ENOENT")
208
+ return { state: "unreadable", fault: e };
209
+ let probe = ENTRY_PRESENT;
210
+ try {
211
+ lstatSync(path);
212
+ }
213
+ catch (fault) {
214
+ probe = { fault };
215
+ }
216
+ return judgedAfterEnoent(e, probe);
217
+ }
218
+ }
219
+ const ENTRY_PRESENT = { present: true };
220
+ function judgedAfterEnoent(readFault, probe) {
221
+ if ("present" in probe)
222
+ return { state: "unreadable", fault: readFault };
223
+ return probe.fault?.code === "ENOENT" ? { state: "absent" } : { state: "unreadable", fault: probe.fault };
224
+ }
225
+ export function dataRootMismatchOf(path, label, fault) {
226
+ if (fault instanceof SqlJsonColumnError)
227
+ return { path, label, problem: fault.problem, unknownFields: fault.unknownKeys };
228
+ const code = fault?.code;
229
+ return { path, label, problem: typeof code === "string" && /^E[A-Z0-9_]+$/.test(code) ? code : "unreadable", unknownFields: [] };
230
+ }
231
+ async function readSessionMetaForM40(path) {
232
+ const raw = await readDataRootFileText(path);
233
+ if (raw.state !== "text")
234
+ return raw;
235
+ try {
236
+ return { state: "read", entries: decodeJsonColumn(raw.text, readJsonObjectColumn, `${SESSION_META_SIDECAR_FILE} (one-time migration M40)`) };
237
+ }
238
+ catch (e) {
239
+ return { state: "unreadable", fault: e };
240
+ }
241
+ }
182
242
  class SidecarMap {
183
243
  path;
184
244
  label;
@@ -244,15 +304,11 @@ class SidecarMap {
244
304
  throw this.refuse(path, disk);
245
305
  }
246
306
  async readDisk(path) {
247
- let text;
248
- try {
249
- text = await readFile(path, "utf8");
250
- }
251
- catch (e) {
252
- return e?.code === "ENOENT" ? { state: "absent" } : { state: "unreadable", fault: e };
253
- }
307
+ const raw = await readDataRootFileText(path);
308
+ if (raw.state !== "text")
309
+ return raw;
254
310
  try {
255
- return { state: "read", entries: decodeJsonColumn(text, this.read, this.label) };
311
+ return { state: "read", entries: decodeJsonColumn(raw.text, this.read, this.label) };
256
312
  }
257
313
  catch (e) {
258
314
  return { state: "unreadable", fault: e };
@@ -260,9 +316,7 @@ class SidecarMap {
260
316
  }
261
317
  refuse(path, disk) {
262
318
  const shape = isJsonColumnBodyProblem(disk.fault);
263
- const problem = disk.fault instanceof SqlJsonColumnError ? disk.fault.problem : (disk.fault?.code ?? "unreadable");
264
- const unknownFields = disk.fault instanceof SqlJsonColumnError ? disk.fault.unknownKeys : [];
265
- const refusal = new LocalDataRootSchemaMismatchError([{ path, label: this.label, problem, unknownFields }], "serving");
319
+ const refusal = new LocalDataRootSchemaMismatchError([dataRootMismatchOf(path, this.label, disk.fault)], "serving");
266
320
  if (shape)
267
321
  this.state.refused = refusal;
268
322
  return refusal;
@@ -300,9 +354,7 @@ class SidecarMap {
300
354
  }
301
355
  }
302
356
  try {
303
- const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
304
- await writeFile(tmp, JSON.stringify(Object.fromEntries(draft)), "utf8");
305
- await rename(tmp, path);
357
+ await atomicWriteFileAsync(path, JSON.stringify(Object.fromEntries(draft)), { durability: "best-effort" });
306
358
  }
307
359
  catch (e) {
308
360
  this.onWriteFailed(e);
@@ -435,7 +487,6 @@ export class LocalSessionStore {
435
487
  flushTimer;
436
488
  writeChain = Promise.resolve();
437
489
  dirty = false;
438
- writeSeq = 0;
439
490
  lastWarnAt = 0;
440
491
  sidecarPath;
441
492
  titles;
@@ -493,7 +544,7 @@ export class LocalSessionStore {
493
544
  this.pinned = sidecar("session-pinned.json", "pinned", readPinnedSidecar);
494
545
  this.instructionSources = sidecar("session-instruction-sources.json", "instruction-source", readStringRecordColumn);
495
546
  this.workspaces = sidecar("session-workspaces.json", "workspace", readStringRecordColumn);
496
- this.meta = sidecar("session-incarnations.json", "session-meta", readSessionMetaSidecar, true);
547
+ this.meta = sidecar(SESSION_META_SIDECAR_FILE, "session-meta", readSessionMetaSidecar, true);
497
548
  const root = dataDir ? resolve(dataDir) : undefined;
498
549
  this.dataRoot = root;
499
550
  if (root !== undefined && !SESSION_WRITE_LOCKS_BY_ROOT.has(root))
@@ -509,6 +560,33 @@ export class LocalSessionStore {
509
560
  place: (sessionId, occupant, cell) => this.placeExecution(sessionId, occupant, cell),
510
561
  };
511
562
  }
563
+ async renameRetiredExecutionLaneWordM40() {
564
+ if (this.dataRoot === undefined)
565
+ return 0;
566
+ const path = join(this.dataRoot, SESSION_META_SIDECAR_FILE);
567
+ const disk = await readSessionMetaForM40(path);
568
+ if (disk.state !== "read")
569
+ return 0;
570
+ let renamed = 0;
571
+ for (const cell of Object.values(disk.entries)) {
572
+ if (!isJsonObject(cell))
573
+ continue;
574
+ const execution = cell["execution"];
575
+ if (!isJsonObject(execution) || execution["role"] !== "writer" || execution["lane"] !== RETIRED_EXECUTION_LANE_WORD_M40)
576
+ continue;
577
+ execution["lane"] = RENAMED_EXECUTION_LANE_M40;
578
+ renamed += 1;
579
+ }
580
+ if (renamed === 0)
581
+ return 0;
582
+ try {
583
+ await atomicWriteFileAsync(path, JSON.stringify(disk.entries), { durability: "best-effort" });
584
+ }
585
+ catch (e) {
586
+ throw new ExecutionLaneRenameWriteRefusedError(path, e);
587
+ }
588
+ return renamed;
589
+ }
512
590
  async assertStrictSidecarsReadable() {
513
591
  const loads = await Promise.allSettled(this.strictSidecars.map((map) => map.reconcile()));
514
592
  const findings = [];
@@ -943,10 +1021,8 @@ export class LocalSessionStore {
943
1021
  this.dirty = false;
944
1022
  const snapshot = Object.fromEntries(this.lastActivity);
945
1023
  try {
946
- await mkdir(join(path, ".."), { recursive: true });
947
- const tmp = `${path}.${process.pid}.${++this.writeSeq}.tmp`;
948
- await writeFile(tmp, JSON.stringify(snapshot), "utf8");
949
- await rename(tmp, path);
1024
+ ensureDir(dirname(path));
1025
+ await atomicWriteFileAsync(path, JSON.stringify(snapshot), { durability: "best-effort" });
950
1026
  }
951
1027
  catch (e) {
952
1028
  this.warnSidecar("write", e);
@@ -1306,10 +1382,8 @@ export class LocalSessionStore {
1306
1382
  await this.assertNotPlacedHeld(sessionId);
1307
1383
  validateEntriesForImport(entries);
1308
1384
  const current = this.metaCellOf(sessionId);
1309
- if (current?.execution?.role === "writer")
1310
- throw new SessionWriterPresentError(sessionId, current.execution.lane);
1311
- if (await this.forkingOf(sessionId, current))
1312
- throw new SessionForkInProgressError(sessionId);
1385
+ if (landing !== "judged")
1386
+ await this.admitImportHeld(sessionId, current);
1313
1387
  if (!(await this.importMayLand(sessionId, current, owner, landing)))
1314
1388
  throw importRowRefused(sessionId, landing);
1315
1389
  const prior = await this.landImportCell(sessionId, owner);
@@ -1348,6 +1422,14 @@ export class LocalSessionStore {
1348
1422
  throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${placed.kind} placement partition (a delegated agent's transcript) — a session import cannot replace its log`);
1349
1423
  }
1350
1424
  }
1425
+ async admitImportHeld(sessionId, current) {
1426
+ const verdict = await judgeImportWrite(sessionId, {
1427
+ claim: () => this.runs.getActiveTaskId(sessionId),
1428
+ cell: async () => placementCellOf(current, await this.forkingOf(sessionId, current)),
1429
+ });
1430
+ if (verdict !== "proceed")
1431
+ throw verdict.refuse;
1432
+ }
1351
1433
  async importMayLand(sessionId, current, owner, landing) {
1352
1434
  switch (landing) {
1353
1435
  case "create":
@@ -1404,8 +1486,6 @@ export class LocalSessionStore {
1404
1486
  const current = await this.metaOf(sessionId);
1405
1487
  if (!ownerCellIs(opts.expectedOwner, current === undefined ? undefined : (current.owner ?? null)))
1406
1488
  throw new StagedCommitOwnerChangedError(sessionId);
1407
- if (await this.forkingOf(sessionId, current))
1408
- throw new SessionForkInProgressError(sessionId);
1409
1489
  await this.assertNotPlacedHeld(sessionId);
1410
1490
  const dstEntries = await this.exportEntries(sessionId);
1411
1491
  const rel = classifySyncRelationshipByIds(staged.map((e) => e.id), dstEntries === null ? null : dstEntries.map((e) => e.id));
@@ -1421,8 +1501,7 @@ export class LocalSessionStore {
1421
1501
  if (diverged)
1422
1502
  throw new SyncConflictError(sessionId, contentForkRelation(diverged, rel.newEntryIds));
1423
1503
  }
1424
- if (current?.execution?.role === "writer")
1425
- throw new SessionWriterPresentError(sessionId, current.execution.lane);
1504
+ await this.admitImportHeld(sessionId, current);
1426
1505
  if (rel.relation !== "identical" || !contentEqual) {
1427
1506
  await this.importHeld(sessionId, importer, staged, "judged");
1428
1507
  }
@@ -1589,7 +1668,7 @@ export class LocalSessionStore {
1589
1668
  this.meta.syncToDisk();
1590
1669
  let anonymized = 0;
1591
1670
  if (listed.some((row) => (row.owner ?? null) !== null)) {
1592
- mkdirSync(dir, { recursive: true, mode: 0o700 });
1671
+ ensureDir(dir);
1593
1672
  fsyncPaths(dir, dirname(dir));
1594
1673
  }
1595
1674
  const keptStamped = [];
@@ -1,4 +1,6 @@
1
1
  import { type AttachmentStoreBindOutcome, type TaskAttachmentStore, type TaskAttachmentRecord } from "./task-attachment-store.js";
2
+ /** 附件店在数据根下的目录(构造器与一次性迁移 M39 的名册同读这一只)。 */
3
+ export declare function attachmentStoreDirFor(rootDir: string): string;
2
4
  export declare class LocalTaskAttachmentStore implements TaskAttachmentStore {
3
5
  private readonly dir;
4
6
  private metas;