@sema-agent/server 7.146.0 → 7.148.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 (850) hide show
  1. package/MIGRATION.md +1423 -24
  2. package/README.md +21 -8
  3. package/README.zh-CN.md +15 -9
  4. package/USAGE.md +537 -216
  5. package/deploy/facts/lanes.json +7 -0
  6. package/deploy/sema-up/README.md +18 -7
  7. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  8. package/deploy/sema-up/chart/templates/_helpers.tpl +42 -4
  9. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -4
  10. package/deploy/sema-up/chart/templates/server.yaml +5 -4
  11. package/deploy/sema-up/chart/values.yaml +4 -3
  12. package/deploy/sema-up/cluster-up.sh +75 -176
  13. package/deploy/sema-up/kube-up.sh +29 -49
  14. package/deploy/sema-up/lib/chart-run.sh +23 -0
  15. package/deploy/sema-up/lib/dataroot.sh +3 -3
  16. package/deploy/sema-up/lib/flagshape.sh +4 -0
  17. package/deploy/sema-up/lib/health.sh +62 -13
  18. package/deploy/sema-up/lib/helm-values-cluster.py +18 -0
  19. package/deploy/sema-up/lib/helm-values-kube.py +35 -0
  20. package/deploy/sema-up/lib/lanes.json +2 -5
  21. package/deploy/sema-up/lib/regcred.py +8 -0
  22. package/deploy/sema-up/lib/remote/helm-install.sh +3 -0
  23. package/deploy/sema-up/lib/remote/image-import.sh +6 -0
  24. package/deploy/sema-up/lib/remote/machine-precheck.sh +11 -0
  25. package/deploy/sema-up/lib/remote/region-probe.sh +3 -0
  26. package/deploy/sema-up/lib/remote/stack-ready.sh +26 -0
  27. package/deploy/sema-up/lib/remote/vxlan-guard.sh +8 -0
  28. package/deploy/sema-up/lib/render-stack.py +17 -0
  29. package/deploy/sema-up/lib/shell-text.sh +87 -0
  30. package/deploy/sema-up/lib/usage-flags.txt +10 -0
  31. package/deploy/sema-up/lib/usage-head.txt +1 -0
  32. package/deploy/sema-up/lib/usage-tail.txt +13 -0
  33. package/deploy/sema-up/preflight.sh +10 -8
  34. package/deploy/sema-up/sema-up.sh +26 -48
  35. package/deploy/sema-up/smoke.sh +7 -6
  36. package/deploy/sema-up/stack/compose.dataroot.yaml +3 -0
  37. package/deploy/sema-up/stack/compose.yaml +18 -5
  38. package/deploy/sema-up/stack/sema-stack-cluster.yaml +25 -3
  39. package/dist/adoption/plan.d.ts +24 -0
  40. package/dist/adoption/plan.js +53 -1
  41. package/dist/adoption/runner.d.ts +10 -1
  42. package/dist/adoption/runner.js +36 -16
  43. package/dist/adoption/wire.d.ts +3 -3
  44. package/dist/approval-card.d.ts +32 -34
  45. package/dist/approval-gate-currency.js +3 -2
  46. package/dist/approval-reconciler.d.ts +4 -13
  47. package/dist/approval-reconciler.js +2 -2
  48. package/dist/approval.d.ts +16 -14
  49. package/dist/approval.js +3 -3
  50. package/dist/audit.d.ts +10 -3
  51. package/dist/audit.js +17 -9
  52. package/dist/auth-bridge.d.ts +0 -14
  53. package/dist/auth-bridge.js +0 -7
  54. package/dist/auto-mode-face.d.ts +10 -7
  55. package/dist/auto-mode-face.js +7 -4
  56. package/dist/background-agent-name.d.ts +19 -1
  57. package/dist/background-agent-name.js +12 -2
  58. package/dist/background-session-reap.d.ts +1 -1
  59. package/dist/bake-runner/main.js +9 -6
  60. package/dist/bench/s1/arms.js +6 -5
  61. package/dist/bench/s1/live-deps.js +8 -3
  62. package/dist/bench/s1/run-firm.js +6 -5
  63. package/dist/bind-host.d.ts +25 -0
  64. package/dist/bind-host.js +25 -0
  65. package/dist/boot/adoption.d.ts +3 -0
  66. package/dist/boot/adoption.js +1 -0
  67. package/dist/boot/crash-last.js +2 -1
  68. package/dist/boot/deployment-mods.d.ts +1 -1
  69. package/dist/boot/device-lane.js +5 -5
  70. package/dist/boot/engine-lease.d.ts +12 -11
  71. package/dist/boot/execution-env.d.ts +19 -8
  72. package/dist/boot/execution-env.js +80 -62
  73. package/dist/boot/execution-lane.d.ts +37 -9
  74. package/dist/boot/execution-lane.js +74 -20
  75. package/dist/boot/exit-boundary.d.ts +3 -3
  76. package/dist/boot/exit-boundary.js +6 -5
  77. package/dist/boot/exit-last.js +2 -1
  78. package/dist/boot/fatal-exit.d.ts +3 -5
  79. package/dist/boot/fatal-exit.js +2 -2
  80. package/dist/boot/governance-seams.d.ts +2 -2
  81. package/dist/boot/leader.d.ts +15 -0
  82. package/dist/boot/leader.js +18 -16
  83. package/dist/boot/memory-boundary.d.ts +11 -11
  84. package/dist/boot/memory-boundary.js +3 -3
  85. package/dist/boot/memory-consolidation.d.ts +50 -53
  86. package/dist/boot/memory-consolidation.js +11 -18
  87. package/dist/boot/org-memory.js +4 -3
  88. package/dist/boot/parked-revive-gate.d.ts +1 -1
  89. package/dist/boot/parked-revive-gate.js +3 -3
  90. package/dist/boot/reapers.d.ts +4 -3
  91. package/dist/boot/reapers.js +20 -17
  92. package/dist/boot/resolve-spec.d.ts +5 -3
  93. package/dist/boot/resolve-spec.js +133 -165
  94. package/dist/boot/retention-lane.d.ts +7 -45
  95. package/dist/boot/retention-lane.js +2 -2
  96. package/dist/boot/runner-deps.d.ts +28 -7
  97. package/dist/boot/runner-deps.js +11 -4
  98. package/dist/boot/runtime-caps.d.ts +2 -2
  99. package/dist/boot/runtime-caps.js +16 -2
  100. package/dist/boot/session-faces.d.ts +17 -7
  101. package/dist/boot/session-faces.js +122 -96
  102. package/dist/boot/shutdown.d.ts +5 -4
  103. package/dist/boot/shutdown.js +6 -4
  104. package/dist/boot/stage-01-config.d.ts +1 -1
  105. package/dist/boot/stage-01-config.js +14 -2
  106. package/dist/boot/stage-02-web-admin-pull.d.ts +2 -1
  107. package/dist/boot/stage-02-web-admin-pull.js +15 -8
  108. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  109. package/dist/boot/stage-03-store-backend.js +4 -3
  110. package/dist/boot/stage-04-stores.d.ts +3 -1
  111. package/dist/boot/stage-04-stores.js +2 -2
  112. package/dist/boot/stage-05-execution-env.d.ts +9 -5
  113. package/dist/boot/stage-05-execution-env.js +5 -5
  114. package/dist/boot/stage-06-runners.d.ts +13 -15
  115. package/dist/boot/stage-06-runners.js +24 -18
  116. package/dist/boot/stage-07-capability-layer.d.ts +14 -11
  117. package/dist/boot/stage-07-capability-layer.js +9 -12
  118. package/dist/boot/stage-08-reapers.d.ts +14 -11
  119. package/dist/boot/stage-08-reapers.js +3 -3
  120. package/dist/boot/stage-09-leader.d.ts +16 -18
  121. package/dist/boot/stage-09-leader.js +12 -4
  122. package/dist/boot/stage-10-http-server.d.ts +16 -18
  123. package/dist/boot/stage-10-http-server.js +9 -5
  124. package/dist/boot/store-evidence.d.ts +20 -0
  125. package/dist/boot/store-evidence.js +13 -0
  126. package/dist/boot/stores.d.ts +12 -0
  127. package/dist/boot/stores.js +39 -17
  128. package/dist/boot/task-list-lane.d.ts +4 -4
  129. package/dist/boot/web-admin.d.ts +4 -2
  130. package/dist/boot/web-admin.js +58 -38
  131. package/dist/boot/webfetch-summarize-lane.d.ts +1 -1
  132. package/dist/boot-reclaim.d.ts +3 -1
  133. package/dist/boot-reclaim.js +1 -1
  134. package/dist/brain.d.ts +7 -18
  135. package/dist/brain.js +5 -17
  136. package/dist/budget.js +6 -6
  137. package/dist/capabilities/center-plugins.d.ts +9 -1
  138. package/dist/capabilities/center-plugins.js +37 -11
  139. package/dist/capabilities/center-wiring.js +1 -1
  140. package/dist/capabilities/composite-members.d.ts +3 -2
  141. package/dist/capabilities/composite-members.js +3 -2
  142. package/dist/capabilities/execution-lane.d.ts +7 -7
  143. package/dist/capabilities/execution-lane.js +2 -2
  144. package/dist/capabilities/hands-lane.d.ts +1 -1
  145. package/dist/capabilities/repo-tools.d.ts +2 -0
  146. package/dist/capabilities/repo-tools.js +14 -12
  147. package/dist/capabilities/scenarios.d.ts +38 -33
  148. package/dist/capabilities/scenarios.js +24 -25
  149. package/dist/capabilities/settings-contract.d.ts +29 -0
  150. package/dist/capabilities/settings-contract.js +14 -0
  151. package/dist/capabilities/skills.d.ts +6 -6
  152. package/dist/capabilities/tool-defer.d.ts +1 -1
  153. package/dist/capability-facts.d.ts +69 -4
  154. package/dist/capability-facts.js +77 -5
  155. package/dist/card-void.d.ts +4 -4
  156. package/dist/card-void.js +1 -1
  157. package/dist/center-credential-renewer.d.ts +6 -2
  158. package/dist/center-credential-renewer.js +10 -5
  159. package/dist/config-catalog.d.ts +16 -1
  160. package/dist/config-catalog.js +68 -61
  161. package/dist/config-dir-trust.d.ts +37 -4
  162. package/dist/config-dir-trust.js +88 -44
  163. package/dist/config-invariants.d.ts +58 -4
  164. package/dist/config-invariants.js +104 -23
  165. package/dist/config-lkg.d.ts +24 -2
  166. package/dist/config-lkg.js +22 -9
  167. package/dist/config-provider.d.ts +9 -9
  168. package/dist/config-provider.js +25 -108
  169. package/dist/config-pull-failure.d.ts +50 -0
  170. package/dist/config-pull-failure.js +48 -0
  171. package/dist/config-types.d.ts +215 -202
  172. package/dist/config.d.ts +69 -14
  173. package/dist/config.js +188 -96
  174. package/dist/degenerate-instrument.js +1 -1
  175. package/dist/delegated-session-owner.d.ts +1 -1
  176. package/dist/deleted-session-ids.d.ts +3 -2
  177. package/dist/deploy-facts.js +3 -2
  178. package/dist/deployment-governance.d.ts +3 -3
  179. package/dist/deployment-shape.d.ts +51 -16
  180. package/dist/deployment-shape.js +31 -6
  181. package/dist/egress-redirect.d.ts +25 -7
  182. package/dist/egress-redirect.js +20 -2
  183. package/dist/elicitation.d.ts +6 -5
  184. package/dist/elicitation.js +23 -22
  185. package/dist/engine-identity.d.ts +28 -0
  186. package/dist/engine-identity.js +10 -0
  187. package/dist/entry-problems.d.ts +5 -7
  188. package/dist/entry-problems.js +1 -1
  189. package/dist/env-facts.d.ts +7 -5
  190. package/dist/env-facts.js +12 -7
  191. package/dist/env-witness.d.ts +7 -0
  192. package/dist/env-witness.js +16 -0
  193. package/dist/execution-config-root.d.ts +5 -5
  194. package/dist/execution-config-root.js +5 -5
  195. package/dist/execution-env-purpose.d.ts +14 -0
  196. package/dist/execution-env-purpose.js +29 -0
  197. package/dist/execution-lane-caps.d.ts +53 -103
  198. package/dist/execution-lane-caps.js +51 -83
  199. package/dist/execution-placement.d.ts +65 -9
  200. package/dist/execution-placement.js +58 -0
  201. package/dist/field-conflict.d.ts +7 -11
  202. package/dist/field-conflict.js +3 -3
  203. package/dist/file-history-capture.d.ts +3 -3
  204. package/dist/file-history-disclosure.d.ts +2 -2
  205. package/dist/file-history-disclosure.js +1 -0
  206. package/dist/fleet/drain-entry.d.ts +17 -10
  207. package/dist/fleet/drain-entry.js +1 -1
  208. package/dist/fleet/fleet-bus.d.ts +21 -3
  209. package/dist/fleet/fleet-bus.js +35 -25
  210. package/dist/fleet/fleet-reconciler.js +2 -2
  211. package/dist/fleet/serving-lease.js +6 -7
  212. package/dist/fleet-client.js +3 -3
  213. package/dist/fleet-lease.d.ts +0 -1
  214. package/dist/fleet-lease.js +4 -5
  215. package/dist/history-anchor.d.ts +42 -0
  216. package/dist/history-anchor.js +34 -0
  217. package/dist/hitl-delivery.d.ts +90 -0
  218. package/dist/hitl-delivery.js +98 -0
  219. package/dist/hooks/classic-stations.d.ts +8 -11
  220. package/dist/hooks/classic-stations.js +0 -7
  221. package/dist/hooks/command-spawn.d.ts +1 -4
  222. package/dist/hooks/command-spawn.js +0 -11
  223. package/dist/hooks/hook-llm.d.ts +5 -1
  224. package/dist/hooks/hook-llm.js +24 -15
  225. package/dist/hooks/hook-runner.d.ts +119 -65
  226. package/dist/hooks/hook-runner.js +415 -217
  227. package/dist/host-lsp-manager.d.ts +22 -8
  228. package/dist/host-lsp-manager.js +4 -1
  229. package/dist/http/active-run-conflict.d.ts +2 -43
  230. package/dist/http/active-run-conflict.js +3 -7
  231. package/dist/http/admission.d.ts +4 -18
  232. package/dist/http/admission.js +75 -78
  233. package/dist/http/approval-sweeps.d.ts +5 -8
  234. package/dist/http/approval-sweeps.js +55 -30
  235. package/dist/http/caller-trust.d.ts +8 -10
  236. package/dist/http/caller-trust.js +1 -2
  237. package/dist/http/checkpoint-ctx.d.ts +17 -0
  238. package/dist/http/checkpoint-ctx.js +6 -0
  239. package/dist/http/claim-heartbeat.d.ts +49 -0
  240. package/dist/http/claim-heartbeat.js +46 -0
  241. package/dist/http/claim-latency.d.ts +10 -0
  242. package/dist/http/claim-latency.js +12 -0
  243. package/dist/http/claimed-run.d.ts +4 -3
  244. package/dist/http/dispatch.d.ts +8 -1
  245. package/dist/http/dispatch.js +77 -31
  246. package/dist/http/durable-run-leg.d.ts +3 -3
  247. package/dist/http/durable-run-leg.js +18 -12
  248. package/dist/http/execution-context.d.ts +18 -42
  249. package/dist/http/execution-context.js +11 -5
  250. package/dist/http/health-reader.d.ts +36 -0
  251. package/dist/http/health-reader.js +21 -0
  252. package/dist/http/health-self-report.d.ts +9 -9
  253. package/dist/http/health-self-report.js +4 -4
  254. package/dist/http/idempotency.d.ts +9 -5
  255. package/dist/http/idempotency.js +4 -4
  256. package/dist/http/memory-compliance-failure-reply.d.ts +15 -16
  257. package/dist/http/memory-compliance-failure-reply.js +7 -2
  258. package/dist/http/memory-face-fault-reply.d.ts +67 -0
  259. package/dist/http/memory-face-fault-reply.js +71 -0
  260. package/dist/http/memory-store-refusal-reply.d.ts +3 -3
  261. package/dist/http/memory-store-refusal-reply.js +6 -0
  262. package/dist/http/owner-access.d.ts +391 -56
  263. package/dist/http/owner-access.js +372 -33
  264. package/dist/http/principal-gate.d.ts +26 -94
  265. package/dist/http/principal-gate.js +24 -81
  266. package/dist/http/refusal-body.d.ts +35 -2
  267. package/dist/http/refusal-body.js +34 -0
  268. package/dist/http/request-guards.d.ts +10 -14
  269. package/dist/http/request-guards.js +34 -68
  270. package/dist/http/resume-composition.d.ts +37 -0
  271. package/dist/http/resume-composition.js +18 -0
  272. package/dist/http/resume-ctx-read.d.ts +35 -12
  273. package/dist/http/resume-ctx-read.js +25 -6
  274. package/dist/http/resume-legs.d.ts +24 -24
  275. package/dist/http/resume-legs.js +260 -171
  276. package/dist/http/retired-skill-manifest.d.ts +1 -1
  277. package/dist/http/rewind-files-reply.d.ts +34 -0
  278. package/dist/http/rewind-files-reply.js +36 -0
  279. package/dist/http/route-ctx.d.ts +56 -47
  280. package/dist/http/route-table.js +2 -0
  281. package/dist/http/routes/a2a-serve.d.ts +43 -5
  282. package/dist/http/routes/a2a-serve.js +58 -37
  283. package/dist/http/routes/admin-config-refresh.js +2 -2
  284. package/dist/http/routes/admin-drain.js +2 -2
  285. package/dist/http/routes/admin-memory-optout.js +4 -3
  286. package/dist/http/routes/adoption.d.ts +3 -0
  287. package/dist/http/routes/adoption.js +17 -10
  288. package/dist/http/routes/agents-roster.js +13 -7
  289. package/dist/http/routes/approvals-assistant.js +73 -88
  290. package/dist/http/routes/attachments.js +2 -2
  291. package/dist/http/routes/background.d.ts +3 -3
  292. package/dist/http/routes/background.js +9 -17
  293. package/dist/http/routes/capabilities.js +23 -20
  294. package/dist/http/routes/config-catalog.js +2 -2
  295. package/dist/http/routes/devices.d.ts +3 -3
  296. package/dist/http/routes/devices.js +5 -5
  297. package/dist/http/routes/diagnostics.d.ts +27 -3
  298. package/dist/http/routes/diagnostics.js +27 -21
  299. package/dist/http/routes/engine.js +2 -2
  300. package/dist/http/routes/fleet.js +5 -3
  301. package/dist/http/routes/images.js +10 -6
  302. package/dist/http/routes/leader.d.ts +7 -2
  303. package/dist/http/routes/leader.js +12 -3
  304. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  305. package/dist/http/routes/mcp-reconnect.js +7 -15
  306. package/dist/http/routes/memory-bundle.js +4 -7
  307. package/dist/http/routes/memory-compliance.js +4 -4
  308. package/dist/http/routes/memory-consolidation.js +6 -9
  309. package/dist/http/routes/memory-origin.d.ts +3 -1
  310. package/dist/http/routes/memory-origin.js +7 -8
  311. package/dist/http/routes/memory-policy.js +17 -12
  312. package/dist/http/routes/notify-wake.js +12 -20
  313. package/dist/http/routes/observability.js +3 -3
  314. package/dist/http/routes/principal-inbox.d.ts +1 -1
  315. package/dist/http/routes/principal-inbox.js +2 -2
  316. package/dist/http/routes/projects.d.ts +70 -0
  317. package/dist/http/routes/projects.js +87 -0
  318. package/dist/http/routes/retention-ops.js +6 -9
  319. package/dist/http/routes/rules.js +50 -7
  320. package/dist/http/routes/run-memory-capture.d.ts +2 -2
  321. package/dist/http/routes/run-memory-capture.js +6 -12
  322. package/dist/http/routes/run-permission-mode.d.ts +2 -2
  323. package/dist/http/routes/run-permission-mode.js +10 -17
  324. package/dist/http/routes/runs.js +148 -233
  325. package/dist/http/routes/session-memory-sync.d.ts +2 -2
  326. package/dist/http/routes/session-memory-sync.js +7 -13
  327. package/dist/http/routes/session-sync.d.ts +4 -2
  328. package/dist/http/routes/session-sync.js +48 -30
  329. package/dist/http/routes/sessions-list.d.ts +3 -3
  330. package/dist/http/routes/sessions-list.js +9 -9
  331. package/dist/http/routes/sessions.d.ts +7 -1
  332. package/dist/http/routes/sessions.js +177 -112
  333. package/dist/http/routes/shared-memory.js +6 -6
  334. package/dist/http/routes/side-query.js +2 -2
  335. package/dist/http/routes/tasks.js +144 -122
  336. package/dist/http/routes/trace-usage.d.ts +0 -12
  337. package/dist/http/routes/trace-usage.js +40 -46
  338. package/dist/http/routes/workflows.d.ts +39 -4
  339. package/dist/http/routes/workflows.js +42 -14
  340. package/dist/http/run-concurrency-cap.d.ts +20 -12
  341. package/dist/http/run-concurrency-cap.js +39 -26
  342. package/dist/http/run-read-face.d.ts +3 -5
  343. package/dist/http/run-read-face.js +8 -13
  344. package/dist/http/send.d.ts +12 -3
  345. package/dist/http/send.js +42 -6
  346. package/dist/http/server.d.ts +63 -50
  347. package/dist/http/server.js +52 -16
  348. package/dist/http/sse-log.d.ts +17 -10
  349. package/dist/http/sse-log.js +7 -7
  350. package/dist/http/stored-body-replay.d.ts +17 -3
  351. package/dist/http/stored-body-replay.js +11 -2
  352. package/dist/http/submit-ctx.d.ts +16 -8
  353. package/dist/http/submit-ctx.js +6 -2
  354. package/dist/http/submit-replay.d.ts +19 -6
  355. package/dist/http/submit-replay.js +18 -6
  356. package/dist/http/sync-submit-body.d.ts +3 -4
  357. package/dist/http/sync-submit-body.js +4 -5
  358. package/dist/http/wire-types.d.ts +23 -13
  359. package/dist/identity-source.d.ts +36 -5
  360. package/dist/identity-source.js +27 -0
  361. package/dist/index.d.ts +3 -2
  362. package/dist/index.js +2 -1
  363. package/dist/instruction-source-notice.d.ts +1 -1
  364. package/dist/key-resolver.d.ts +32 -0
  365. package/dist/key-resolver.js +23 -9
  366. package/dist/leader/diffup.js +2 -1
  367. package/dist/leader/endpoint.d.ts +26 -5
  368. package/dist/leader/endpoint.js +154 -14
  369. package/dist/leader/fanout.d.ts +3 -0
  370. package/dist/leader/fanout.js +78 -63
  371. package/dist/leader/leader.d.ts +16 -0
  372. package/dist/leader/leader.js +43 -3
  373. package/dist/leader/merge.d.ts +9 -1
  374. package/dist/leader/merge.js +16 -1
  375. package/dist/leader/repair-wire.js +1 -1
  376. package/dist/leader/wire.d.ts +36 -3
  377. package/dist/leader/wire.js +59 -18
  378. package/dist/lsp-evict.d.ts +6 -4
  379. package/dist/lsp-evict.js +2 -3
  380. package/dist/memory-embedder-reembed.d.ts +38 -0
  381. package/dist/memory-embedder-reembed.js +20 -0
  382. package/dist/memory-incident-sink.d.ts +0 -28
  383. package/dist/memory-incident-sink.js +5 -0
  384. package/dist/memory-operator-faces.d.ts +5 -4
  385. package/dist/memory-operator-faces.js +1 -1
  386. package/dist/memory-scope.d.ts +31 -4
  387. package/dist/memory-scope.js +65 -16
  388. package/dist/memory-store-refusal.d.ts +20 -4
  389. package/dist/memory-store-refusal.js +1 -1
  390. package/dist/memory-sync-client.d.ts +5 -2
  391. package/dist/memory-sync-client.js +19 -14
  392. package/dist/memory-sync.js +1 -1
  393. package/dist/model-compat.d.ts +51 -28
  394. package/dist/model-compat.js +55 -23
  395. package/dist/model-context-window.d.ts +18 -0
  396. package/dist/model-context-window.js +2 -0
  397. package/dist/model-entry-headers.d.ts +46 -4
  398. package/dist/model-entry-headers.js +2 -2
  399. package/dist/model-entry-refusal.d.ts +46 -17
  400. package/dist/model-entry-refusal.js +47 -27
  401. package/dist/model-plane-face.d.ts +19 -11
  402. package/dist/model-plane-face.js +9 -1
  403. package/dist/model-plane-probe.d.ts +2 -0
  404. package/dist/model-plane-probe.js +17 -0
  405. package/dist/model-provider.d.ts +3 -3
  406. package/dist/model-route-endpoint.d.ts +9 -5
  407. package/dist/model-route-endpoint.js +29 -7
  408. package/dist/model-select.d.ts +16 -0
  409. package/dist/model-select.js +4 -0
  410. package/dist/mountinfo.d.ts +37 -0
  411. package/dist/mountinfo.js +47 -0
  412. package/dist/namespaced-rule-name.d.ts +28 -2
  413. package/dist/namespaced-rule-name.js +10 -1
  414. package/dist/net-address.d.ts +1 -1
  415. package/dist/observability/corrupt-read-seat.d.ts +4 -4
  416. package/dist/observability/corrupt-read-seat.js +5 -3
  417. package/dist/observability/fail-open.d.ts +75 -47
  418. package/dist/observability/fail-open.js +77 -47
  419. package/dist/observability/logger.d.ts +22 -1
  420. package/dist/observability/logger.js +89 -12
  421. package/dist/observability/metrics.d.ts +2 -0
  422. package/dist/observability/metrics.js +5 -4
  423. package/dist/observability/otel-exporter.js +2 -1
  424. package/dist/observability/principal-context.d.ts +14 -7
  425. package/dist/observability/principal-context.js +8 -3
  426. package/dist/observability/run-terminal.d.ts +7 -1
  427. package/dist/observability/run-terminal.js +5 -2
  428. package/dist/observability/secret-env-scrub.d.ts +30 -4
  429. package/dist/observability/secret-env-scrub.js +15 -3
  430. package/dist/orchestration/hardened-vm-runner.d.ts +1 -0
  431. package/dist/orchestration/hardened-vm-runner.js +11 -5
  432. package/dist/orchestration/hardened-vm-worker-runner.d.ts +0 -14
  433. package/dist/orchestration/hardened-vm-worker-runner.js +5 -0
  434. package/dist/orchestration/hardened-vm-worker.js +2 -1
  435. package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
  436. package/dist/orchestration/workflow-agent-session-index.js +1 -6
  437. package/dist/orchestration/workflow-completion-inbox.d.ts +29 -9
  438. package/dist/orchestration/workflow-completion-inbox.js +65 -46
  439. package/dist/org-delivery-gate.d.ts +5 -4
  440. package/dist/org-delivery-gate.js +7 -6
  441. package/dist/org-memory-admission.d.ts +5 -2
  442. package/dist/org-memory-admission.js +25 -10
  443. package/dist/outbound-dispatcher.d.ts +1 -1
  444. package/dist/outbound-dispatcher.js +6 -12
  445. package/dist/owner-brands.d.ts +81 -0
  446. package/dist/owner-brands.js +42 -0
  447. package/dist/owner-faces.d.ts +531 -0
  448. package/dist/owner-faces.js +126 -0
  449. package/dist/parked-decide.d.ts +100 -31
  450. package/dist/parked-decide.js +162 -48
  451. package/dist/parked-revive-surface.d.ts +14 -4
  452. package/dist/parked-revive-surface.js +5 -4
  453. package/dist/peer-directory.d.ts +6 -8
  454. package/dist/peer-directory.js +3 -2
  455. package/dist/permission-rule-vocab.d.ts +53 -8
  456. package/dist/permission-rule-vocab.js +30 -2
  457. package/dist/plugins/adoption-log-sql.d.ts +0 -22
  458. package/dist/plugins/adoption-log-sql.js +5 -3
  459. package/dist/plugins/approval-ask-retired-delete-by-task-m50.d.ts +7 -0
  460. package/dist/plugins/approval-ask-retired-delete-by-task-m50.js +17 -0
  461. package/dist/plugins/approval-ask-store-file.d.ts +28 -9
  462. package/dist/plugins/approval-ask-store-file.js +74 -18
  463. package/dist/plugins/approval-ask-store-memory.d.ts +17 -7
  464. package/dist/plugins/approval-ask-store-memory.js +35 -12
  465. package/dist/plugins/approval-ask-store-sql.d.ts +42 -20
  466. package/dist/plugins/approval-ask-store-sql.js +44 -11
  467. package/dist/plugins/approval-exemption-skill-m46.d.ts +20 -0
  468. package/dist/plugins/approval-exemption-skill-m46.js +25 -0
  469. package/dist/plugins/approval-exemption-store.d.ts +6 -1
  470. package/dist/plugins/approval-exemption-store.js +14 -0
  471. package/dist/plugins/approval-nonce-store.d.ts +39 -12
  472. package/dist/plugins/approval-nonce-store.js +72 -23
  473. package/dist/plugins/blob-backend.d.ts +40 -195
  474. package/dist/plugins/blob-backend.js +41 -234
  475. package/dist/plugins/caching-session-store.d.ts +22 -5
  476. package/dist/plugins/caching-session-store.js +43 -4
  477. package/dist/plugins/checkpoint-index-columns.d.ts +33 -6
  478. package/dist/plugins/checkpoint-index-columns.js +39 -3
  479. package/dist/plugins/checkpoint-store-face.d.ts +84 -11
  480. package/dist/plugins/checkpoint-store-face.js +18 -1
  481. package/dist/plugins/device-store-sql.d.ts +2 -2
  482. package/dist/plugins/device-store-sql.js +6 -10
  483. package/dist/plugins/ensure-index.d.ts +13 -1
  484. package/dist/plugins/ensure-index.js +154 -21
  485. package/dist/plugins/ensure-schema-lock-policy.d.ts +0 -15
  486. package/dist/plugins/ensure-schema-lock-policy.js +3 -2
  487. package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
  488. package/dist/plugins/execution-lane-rename-m40.js +10 -0
  489. package/dist/plugins/execution-placement-sql.d.ts +63 -88
  490. package/dist/plugins/execution-placement-sql.js +94 -54
  491. package/dist/plugins/file-run-store.d.ts +89 -35
  492. package/dist/plugins/file-run-store.js +197 -142
  493. package/dist/plugins/host-platform.d.ts +24 -3
  494. package/dist/plugins/host-platform.js +15 -1
  495. package/dist/plugins/image-bake-store-sql.js +2 -0
  496. package/dist/plugins/imported-layer-root-retirement.d.ts +19 -10
  497. package/dist/plugins/imported-layer-root-retirement.js +4 -8
  498. package/dist/plugins/inbox-payload-column.d.ts +14 -0
  499. package/dist/plugins/inbox-payload-column.js +13 -0
  500. package/dist/plugins/k8s-pod-reaper.d.ts +1 -3
  501. package/dist/plugins/k8s-pod-reaper.js +0 -1
  502. package/dist/plugins/leader-run-store-sql.d.ts +22 -0
  503. package/dist/plugins/leader-run-store-sql.js +34 -4
  504. package/dist/plugins/local-checkpoint-store.d.ts +63 -23
  505. package/dist/plugins/local-checkpoint-store.js +204 -81
  506. package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
  507. package/dist/plugins/local-data-dir-modes-m39.js +183 -0
  508. package/dist/plugins/local-docker-capacity.d.ts +93 -0
  509. package/dist/plugins/local-docker-capacity.js +194 -0
  510. package/dist/plugins/local-docker-reaper.d.ts +92 -0
  511. package/dist/plugins/local-docker-reaper.js +102 -0
  512. package/dist/plugins/local-engine-high-water.d.ts +13 -0
  513. package/dist/plugins/local-engine-high-water.js +62 -0
  514. package/dist/plugins/local-session-store.d.ts +442 -143
  515. package/dist/plugins/local-session-store.js +941 -289
  516. package/dist/plugins/local-session-window.d.ts +3 -2
  517. package/dist/plugins/local-session-window.js +3 -2
  518. package/dist/plugins/local-sidecar-quarantine.js +3 -2
  519. package/dist/plugins/local-task-attachment-store.d.ts +20 -1
  520. package/dist/plugins/local-task-attachment-store.js +32 -12
  521. package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
  522. package/dist/plugins/memory-embedder-fingerprint.js +52 -12
  523. package/dist/plugins/memory-embedder.d.ts +1 -1
  524. package/dist/plugins/memory-embedder.js +11 -9
  525. package/dist/plugins/memory-engine-pg.d.ts +7 -5
  526. package/dist/plugins/memory-engine-pg.js +18 -9
  527. package/dist/plugins/memory-engine-sql-apply.d.ts +21 -8
  528. package/dist/plugins/memory-engine-sql-apply.js +7 -9
  529. package/dist/plugins/memory-engine-tidb.d.ts +12 -7
  530. package/dist/plugins/memory-engine-tidb.js +18 -10
  531. package/dist/plugins/memory-optout-grant-store-sql.d.ts +1 -1
  532. package/dist/plugins/memory-optout-grant-store-sql.js +3 -3
  533. package/dist/plugins/memory-run-store.d.ts +39 -27
  534. package/dist/plugins/memory-run-store.js +72 -70
  535. package/dist/plugins/one-time-migration-marker.d.ts +10 -0
  536. package/dist/plugins/one-time-migration-marker.js +7 -0
  537. package/dist/plugins/one-time-migrations.d.ts +325 -74
  538. package/dist/plugins/one-time-migrations.js +705 -230
  539. package/dist/plugins/org-delivery-quota-store.js +2 -1
  540. package/dist/plugins/owner-only-file-mode.d.ts +29 -0
  541. package/dist/plugins/owner-only-file-mode.js +141 -0
  542. package/dist/plugins/permission-rule-store-file.d.ts +11 -11
  543. package/dist/plugins/permission-rule-store-file.js +14 -2
  544. package/dist/plugins/permission-rule-store-sql.d.ts +4 -63
  545. package/dist/plugins/permission-rule-store-sql.js +6 -86
  546. package/dist/plugins/pg-pool.d.ts +3 -32
  547. package/dist/plugins/pg-pool.js +27 -11
  548. package/dist/plugins/release-version.d.ts +20 -0
  549. package/dist/plugins/release-version.js +28 -0
  550. package/dist/plugins/remote-env-adb.d.ts +20 -4
  551. package/dist/plugins/remote-env-adb.js +39 -11
  552. package/dist/plugins/remote-env-e2b.d.ts +19 -28
  553. package/dist/plugins/remote-env-e2b.js +40 -29
  554. package/dist/plugins/remote-env-host.d.ts +11 -10
  555. package/dist/plugins/remote-env-host.js +40 -27
  556. package/dist/plugins/remote-env-k8s.d.ts +45 -47
  557. package/dist/plugins/remote-env-k8s.js +122 -62
  558. package/dist/plugins/remote-env-local-docker.d.ts +152 -15
  559. package/dist/plugins/remote-env-local-docker.js +370 -75
  560. package/dist/plugins/remote-env-ssh.d.ts +8 -2
  561. package/dist/plugins/remote-env-ssh.js +26 -24
  562. package/dist/plugins/remote-scratchpad.js +4 -0
  563. package/dist/plugins/remote-shell.d.ts +59 -30
  564. package/dist/plugins/remote-shell.js +175 -62
  565. package/dist/plugins/retention-declaration.d.ts +37 -0
  566. package/dist/plugins/retention-declaration.js +9 -0
  567. package/dist/plugins/retention-deployment.d.ts +158 -0
  568. package/dist/plugins/retention-deployment.js +119 -0
  569. package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
  570. package/dist/plugins/retention-lane-store-sql.js +103 -1
  571. package/dist/plugins/retention-sql.d.ts +29 -0
  572. package/dist/plugins/retention-sql.js +8 -0
  573. package/dist/plugins/run-preview-m45.d.ts +11 -0
  574. package/dist/plugins/run-preview-m45.js +19 -0
  575. package/dist/plugins/run-store-sql.d.ts +56 -68
  576. package/dist/plugins/run-store-sql.js +127 -116
  577. package/dist/plugins/s3-answer.d.ts +2 -1
  578. package/dist/plugins/sandbox-reaper.d.ts +26 -0
  579. package/dist/plugins/sandbox-reaper.js +2 -0
  580. package/dist/plugins/scenario-prefix-switch-m42.d.ts +54 -0
  581. package/dist/plugins/scenario-prefix-switch-m42.js +55 -0
  582. package/dist/plugins/scheduler-support.d.ts +16 -1
  583. package/dist/plugins/scheduler-support.js +22 -2
  584. package/dist/plugins/schema-consistency.d.ts +42 -15
  585. package/dist/plugins/schema-consistency.js +213 -13
  586. package/dist/plugins/schema-drift-sentence.d.ts +4 -1
  587. package/dist/plugins/schema-drift-sentence.js +4 -4
  588. package/dist/plugins/schema-ensure-legs.d.ts +10 -1
  589. package/dist/plugins/schema-ensure-legs.js +12 -2
  590. package/dist/plugins/schema-indexes.js +3 -0
  591. package/dist/plugins/schema-live-columns.d.ts +14 -5
  592. package/dist/plugins/schema-live-columns.js +10 -10
  593. package/dist/plugins/send-user-file.js +2 -1
  594. package/dist/plugins/session-birth.d.ts +37 -0
  595. package/dist/plugins/session-birth.js +38 -0
  596. package/dist/plugins/session-epoch-anchor.d.ts +40 -12
  597. package/dist/plugins/session-epoch-anchor.js +33 -11
  598. package/dist/plugins/session-ext-table.d.ts +98 -0
  599. package/dist/plugins/session-ext-table.js +134 -0
  600. package/dist/plugins/session-fork.d.ts +16 -2
  601. package/dist/plugins/session-fork.js +3 -0
  602. package/dist/plugins/session-ownership.d.ts +1 -1
  603. package/dist/plugins/session-placement.d.ts +22 -16
  604. package/dist/plugins/session-placement.js +15 -12
  605. package/dist/plugins/session-row-sql.d.ts +20 -7
  606. package/dist/plugins/session-row-sql.js +16 -8
  607. package/dist/plugins/session-run-rollup.d.ts +29 -0
  608. package/dist/plugins/session-run-rollup.js +40 -0
  609. package/dist/plugins/session-store-sql.d.ts +157 -0
  610. package/dist/plugins/session-store-sql.js +398 -0
  611. package/dist/plugins/session-withdraw-sql.d.ts +36 -0
  612. package/dist/plugins/session-withdraw-sql.js +42 -0
  613. package/dist/plugins/session-workspace-cell.d.ts +2 -2
  614. package/dist/plugins/session-workspace-cell.js +2 -2
  615. package/dist/plugins/shared-memory-store-sql.d.ts +10 -69
  616. package/dist/plugins/shared-memory-store-sql.js +15 -14
  617. package/dist/plugins/shell-sentinel-owner-m48.d.ts +20 -0
  618. package/dist/plugins/shell-sentinel-owner-m48.js +3 -0
  619. package/dist/plugins/snapshot-bucket-presence.d.ts +2 -1
  620. package/dist/plugins/snapshot-bucket-presence.js +1 -1
  621. package/dist/plugins/sql-driver.d.ts +24 -8
  622. package/dist/plugins/sql-driver.js +43 -9
  623. package/dist/plugins/sql-errors.d.ts +35 -5
  624. package/dist/plugins/sql-errors.js +55 -3
  625. package/dist/plugins/sql-json-column.d.ts +8 -7
  626. package/dist/plugins/sql-json-column.js +10 -6
  627. package/dist/plugins/store-backend.d.ts +114 -18
  628. package/dist/plugins/store-backend.js +340 -87
  629. package/dist/plugins/store-contracts.d.ts +97 -30
  630. package/dist/plugins/store-contracts.js +23 -1
  631. package/dist/plugins/store-fault.d.ts +28 -2
  632. package/dist/plugins/store-fault.js +20 -2
  633. package/dist/plugins/task-attachment-store.d.ts +21 -11
  634. package/dist/plugins/task-attachment-store.js +37 -19
  635. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  636. package/dist/plugins/text-column-capacity.d.ts +21 -0
  637. package/dist/plugins/text-column-capacity.js +5 -0
  638. package/dist/plugins/tidb-pool.d.ts +3 -2
  639. package/dist/plugins/tidb-pool.js +42 -39
  640. package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
  641. package/dist/plugins/web-search.js +5 -3
  642. package/dist/plugins/workflow-run-store-sql.d.ts +6 -1
  643. package/dist/plugins/workflow-run-store-sql.js +11 -5
  644. package/dist/principal-jwt.d.ts +4 -4
  645. package/dist/principal-jwt.js +20 -5
  646. package/dist/principal-shape.d.ts +41 -0
  647. package/dist/principal-shape.js +19 -0
  648. package/dist/question.d.ts +13 -23
  649. package/dist/question.js +21 -39
  650. package/dist/read-face-posture.d.ts +6 -6
  651. package/dist/read-face-posture.js +2 -2
  652. package/dist/request-declaration-refusal.d.ts +35 -12
  653. package/dist/request-declaration-refusal.js +21 -9
  654. package/dist/request-form.d.ts +58 -0
  655. package/dist/request-form.js +72 -0
  656. package/dist/request-image-urls.d.ts +8 -2
  657. package/dist/request-image-urls.js +2 -1
  658. package/dist/request-images.d.ts +20 -0
  659. package/dist/request-images.js +59 -0
  660. package/dist/request-key-closure.d.ts +6 -7
  661. package/dist/request-key-closure.js +15 -2
  662. package/dist/request-value-closure.d.ts +5 -5
  663. package/dist/request-value-closure.js +1 -1
  664. package/dist/resource-suspend.d.ts +7 -16
  665. package/dist/resource-suspend.js +1 -1
  666. package/dist/retired-name-host-notice.d.ts +21 -0
  667. package/dist/retired-name-host-notice.js +12 -0
  668. package/dist/rule-entry-list.d.ts +2 -2
  669. package/dist/rule-entry-list.js +3 -4
  670. package/dist/rule-issue-reading.d.ts +58 -0
  671. package/dist/rule-issue-reading.js +14 -0
  672. package/dist/rules-consent.d.ts +46 -16
  673. package/dist/rules-consent.js +102 -53
  674. package/dist/run-acceptance.d.ts +3 -1
  675. package/dist/run-acceptance.js +3 -0
  676. package/dist/run-local.d.ts +14 -6
  677. package/dist/run-local.js +109 -93
  678. package/dist/run-stale-default.d.ts +7 -0
  679. package/dist/run-stale-default.js +2 -0
  680. package/dist/runs.d.ts +73 -44
  681. package/dist/runs.js +65 -43
  682. package/dist/runtime-caps-resolver.d.ts +36 -14
  683. package/dist/runtime-caps-resolver.js +31 -14
  684. package/dist/runtime-governance.d.ts +2 -2
  685. package/dist/runtime-governance.js +5 -3
  686. package/dist/sealed-key-layout.d.ts +12 -6
  687. package/dist/sealed-key-layout.js +5 -1
  688. package/dist/sealed-key.d.ts +12 -2
  689. package/dist/sealed-key.js +116 -26
  690. package/dist/security.d.ts +133 -161
  691. package/dist/security.js +163 -59
  692. package/dist/sema-config-dir.d.ts +20 -0
  693. package/dist/sema-config-dir.js +27 -2
  694. package/dist/server-secret-env.d.ts +23 -5
  695. package/dist/server-secret-env.js +14 -4
  696. package/dist/service-version.d.ts +2 -1
  697. package/dist/service-version.js +2 -2
  698. package/dist/session-children-retire.d.ts +7 -5
  699. package/dist/session-children-retire.js +7 -2
  700. package/dist/session-deletion.d.ts +88 -0
  701. package/dist/session-deletion.js +8 -0
  702. package/dist/session-erase.d.ts +145 -0
  703. package/dist/session-erase.js +115 -0
  704. package/dist/session-error-wire.d.ts +6 -6
  705. package/dist/session-error-wire.js +1 -1
  706. package/dist/session-incarnation.d.ts +60 -10
  707. package/dist/session-incarnation.js +19 -2
  708. package/dist/session-policy-wire.d.ts +6 -3
  709. package/dist/session-policy-wire.js +11 -1
  710. package/dist/session-resident-background.d.ts +29 -47
  711. package/dist/session-resident-background.js +77 -8
  712. package/dist/session-row.d.ts +3 -2
  713. package/dist/session-rules-seat.d.ts +12 -0
  714. package/dist/session-rules-seat.js +19 -0
  715. package/dist/session-sync-kernel.d.ts +1 -1
  716. package/dist/session-titler.d.ts +7 -15
  717. package/dist/session-titler.js +1 -1
  718. package/dist/session-workspace.d.ts +11 -2
  719. package/dist/session-workspace.js +5 -0
  720. package/dist/sighup-idle.d.ts +1 -1
  721. package/dist/spec-fields.d.ts +57 -3
  722. package/dist/spec-fields.js +72 -51
  723. package/dist/store-posture.d.ts +138 -21
  724. package/dist/store-posture.js +188 -3
  725. package/dist/stored-request-hooks-m20.d.ts +3 -2
  726. package/dist/stored-request-hooks-m20.js +21 -16
  727. package/dist/stores-sql/background-agent-store-sql.d.ts +11 -2
  728. package/dist/stores-sql/background-agent-store-sql.js +73 -20
  729. package/dist/stores-sql/blob-index-sql.d.ts +148 -0
  730. package/dist/stores-sql/blob-index-sql.js +154 -0
  731. package/dist/stores-sql/checkpoint-store-sql.d.ts +34 -29
  732. package/dist/stores-sql/checkpoint-store-sql.js +86 -71
  733. package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
  734. package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
  735. package/dist/stores-sql/mailbox-store-sql.d.ts +7 -4
  736. package/dist/stores-sql/mailbox-store-sql.js +31 -32
  737. package/dist/{plugins → stores-sql}/pg-session-storage.d.ts +30 -81
  738. package/dist/{plugins → stores-sql}/pg-session-storage.js +123 -244
  739. package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +197 -223
  740. package/dist/{plugins → stores-sql}/retention-store-sql.js +148 -207
  741. package/dist/{plugins → stores-sql}/session-event-payload.d.ts +1 -1
  742. package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
  743. package/dist/stores-sql/session-policy-store-sql.d.ts +33 -11
  744. package/dist/stores-sql/session-policy-store-sql.js +44 -36
  745. package/dist/stores-sql/session-write-hooks.d.ts +65 -0
  746. package/dist/stores-sql/session-write-hooks.js +2 -0
  747. package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +14 -15
  748. package/dist/{plugins → stores-sql}/tidb-session-storage.js +22 -28
  749. package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +67 -138
  750. package/dist/{plugins → stores-sql}/tidb-session-store.js +130 -200
  751. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  752. package/dist/stores-sql/tool-result-store-sql.js +1 -1
  753. package/dist/stores-sql/windowed-session-storage.d.ts +61 -0
  754. package/dist/stores-sql/windowed-session-storage.js +70 -0
  755. package/dist/submission-exit.d.ts +50 -0
  756. package/dist/submission-exit.js +108 -0
  757. package/dist/task-a2a.d.ts +7 -1
  758. package/dist/task-a2a.js +18 -17
  759. package/dist/task-cwd.d.ts +31 -3
  760. package/dist/task-cwd.js +25 -1
  761. package/dist/task-mcp.d.ts +11 -3
  762. package/dist/task-mcp.js +16 -19
  763. package/dist/task-settings.d.ts +23 -14
  764. package/dist/task-settings.js +35 -13
  765. package/dist/terminal.d.ts +25 -43
  766. package/dist/terminal.js +31 -11
  767. package/dist/tool-approval.d.ts +152 -137
  768. package/dist/tool-approval.js +199 -199
  769. package/dist/trace/core-keyset-guard.d.ts +22 -9
  770. package/dist/trace/core-vocab-wire.d.ts +95 -0
  771. package/dist/trace/core-vocab-wire.js +15 -0
  772. package/dist/trace/engine-notice-wire.d.ts +1 -1
  773. package/dist/trace/engine-notice-wire.js +2 -0
  774. package/dist/trace/exit-seat.d.ts +52 -4
  775. package/dist/trace/exit-seat.js +43 -36
  776. package/dist/trace/extension-call-face.d.ts +3 -2
  777. package/dist/trace/extension-call-face.js +3 -1
  778. package/dist/trace/ledger-events.d.ts +4 -7
  779. package/dist/trace/ledger-sink.d.ts +1 -30
  780. package/dist/trace/ledger-sink.js +0 -11
  781. package/dist/trace/model-segment.d.ts +5 -5
  782. package/dist/trace/model-segment.js +5 -5
  783. package/dist/trace/project.d.ts +88 -58
  784. package/dist/trace/project.js +64 -28
  785. package/dist/trace/redact.d.ts +124 -28
  786. package/dist/trace/redact.js +936 -328
  787. package/dist/trace/self-minted-wire.d.ts +750 -29
  788. package/dist/trace/self-minted-wire.js +262 -25
  789. package/dist/trace/sema-provenance.d.ts +45 -41
  790. package/dist/trace/sema-provenance.js +81 -10
  791. package/dist/trace/task-notification-facets.d.ts +70 -2
  792. package/dist/trace/task-notification-facets.js +71 -1
  793. package/dist/trace/wire-projection-faces.d.ts +43 -8
  794. package/dist/trace/wire-projection-faces.js +28 -3
  795. package/dist/unread-request-members.d.ts +66 -0
  796. package/dist/unread-request-members.js +41 -0
  797. package/dist/upstream-body.d.ts +23 -0
  798. package/dist/upstream-body.js +16 -0
  799. package/dist/url-refusal.d.ts +3 -3
  800. package/dist/url-refusal.js +11 -4
  801. package/dist/user-authored-text.d.ts +1 -1
  802. package/dist/user-input-ledger.d.ts +7 -8
  803. package/dist/user-input-ledger.js +1 -1
  804. package/dist/web-admin-client/apply-effective.d.ts +6 -3
  805. package/dist/web-admin-client/apply-effective.js +33 -40
  806. package/dist/web-admin-client/apply-ledger.d.ts +16 -7
  807. package/dist/web-admin-client/apply-ledger.js +5 -2
  808. package/dist/web-admin-client/candidate-gate.d.ts +35 -3
  809. package/dist/web-admin-client/candidate-gate.js +53 -5
  810. package/dist/web-admin-client/center-request.d.ts +6 -3
  811. package/dist/web-admin-client/center-request.js +17 -16
  812. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  813. package/dist/web-admin-client/effective-keys.js +1 -2
  814. package/dist/web-admin-client/entry-metadata.d.ts +8 -0
  815. package/dist/web-admin-client/entry-metadata.js +13 -0
  816. package/dist/web-admin-client/facade.d.ts +1 -1
  817. package/dist/web-admin-client/http-client.d.ts +2 -1
  818. package/dist/web-admin-client/http-client.js +40 -13
  819. package/dist/web-admin-client/mcp-revocation.d.ts +1 -1
  820. package/dist/web-admin-client/mcp-revocation.js +2 -1
  821. package/dist/web-admin-client/read-warnings.d.ts +1 -0
  822. package/dist/web-admin-client/read-warnings.js +1 -0
  823. package/dist/web-admin-client/restart-signal.js +13 -6
  824. package/dist/web-admin-client/skills-mcp.d.ts +22 -8
  825. package/dist/web-admin-client/skills-mcp.js +26 -21
  826. package/dist/web-admin-client/types.d.ts +35 -51
  827. package/dist/workspace-space.d.ts +3 -2
  828. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +4 -4
  829. package/package.json +16 -12
  830. package/scripts/drain-check-local.mjs +118 -173
  831. package/scripts/drain-check.mysql.sql +20 -16
  832. package/scripts/drain-check.pg.sql +20 -16
  833. package/wire-changes/server@7.147.0.json +813 -0
  834. package/wire-changes/server@7.148.0.json +1517 -0
  835. package/dist/http/memory-control-plane-corrupt-reply.d.ts +0 -25
  836. package/dist/http/memory-control-plane-corrupt-reply.js +0 -19
  837. package/dist/http/memory-layer-reply.d.ts +0 -30
  838. package/dist/http/memory-layer-reply.js +0 -24
  839. package/dist/lsp/e2b-bridge.d.ts +0 -57
  840. package/dist/lsp/e2b-bridge.js +0 -139
  841. package/dist/lsp/e2b-manager.d.ts +0 -93
  842. package/dist/lsp/e2b-manager.js +0 -178
  843. package/dist/lsp/lsp-frames.d.ts +0 -14
  844. package/dist/lsp/lsp-frames.js +0 -25
  845. package/dist/lsp/manager.d.ts +0 -129
  846. package/dist/lsp/manager.js +0 -127
  847. package/dist/lsp/types.d.ts +0 -12
  848. package/dist/lsp/types.js +0 -2
  849. package/dist/lsp/ws-transport.d.ts +0 -40
  850. package/dist/lsp/ws-transport.js +0 -210
@@ -1,31 +1,36 @@
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 { SCENARIO_SUBJECT_PREFIX } from "../owner-brands.js";
2
+ import { lstat, readFile } from "node:fs/promises";
3
+ import { closeSync, existsSync, fsyncSync, lstatSync, openSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
3
4
  import { AsyncLocalStorage } from "node:async_hooks";
4
5
  import { isDeepStrictEqual } from "node:util";
5
6
  import { dirname, join, resolve } from "node:path";
7
+ import { IS_WIN32 } from "./host-platform.js";
8
+ import { atomicWriteFileAsync, atomicWriteFileSync, proveDurableSync } from "./owner-only-file-mode.js";
6
9
  import { assertFirstOwnerWritable, forkOwnerOf, importerOwnerOf, placedSessionOwnerOf } from "./session-ownership.js";
7
10
  import { SessionOwnerUnreadableError, StampedSessionArchiveError, sessionUnreadableOf } from "../session-error-wire.js";
8
- import { FileSessionRepo, SessionError, StoredSession, admitSessionPlacement, atomicWriteFile, sessionLogMarkOf, settledForkOptions, uuidv7, validateEntriesForImport } from "@sema-agent/core";
11
+ import { SHELL_SENTINEL_OWNER_M48 } from "./shell-sentinel-owner-m48.js";
12
+ import { FileSessionRepo, SessionError, StoredSession, admitSessionPlacement, atomicWriteFile, ensureDir, forkRowCells, sessionLogMarkOf, settledForkOptions, uuidv7, validateEntriesForImport } from "@sema-agent/core";
9
13
  import { placementCapabilityOf } from "./session-placement.js";
10
14
  import { z } from "zod";
11
15
  import { contentForkRelation, fastForwardSharedContentDiverged, identicalIdsAlsoIdenticalContent } from "../session-sync-content.js";
12
- import { StoreBootRefusalError } from "./store-contracts.js";
16
+ import { SCHEMA_MISMATCH_ERROR_CODE, StoreBootRefusalError } from "./store-contracts.js";
17
+ import { entryTypeWithdrawable } from "./session-birth.js";
13
18
  import { judgeSessionRowWrite, sessionRowTagKey, settleSessionRowWrite, } from "../session-row.js";
14
19
  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";
20
+ import { createZodColumnReader, decodeJsonColumn, isJsonColumnBodyProblem, isJsonObject, readJsonObjectColumn, readStringRecordColumn, SqlJsonColumnError } from "./sql-json-column.js";
21
+ import { RENAMED_EXECUTION_LANE_M40, RETIRED_EXECUTION_LANE_WORD_M40 } from "./execution-lane-rename-m40.js";
22
+ import { ExecutionLaneRenameWriteRefusedError, ONE_TIME_MIGRATIONS } from "./one-time-migrations.js";
23
+ import { compareReleaseTriples, releaseText, releaseTriple } from "./release-version.js";
18
24
  import { recordFailOpen } from "../observability/fail-open.js";
19
- import { createLogger } from "../observability/logger.js";
20
- import { UNMANAGED_RETENTION } from "./retention-store-sql.js";
21
- import { UnreadableLeaf, carriedRowCells, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
25
+ import { createLogger, writeLogText } from "../observability/logger.js";
26
+ import { UNMANAGED_RETENTION } from "./retention-declaration.js";
27
+ import { UnreadableLeaf, carriedRowCells, forkLineageOfRow, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
22
28
  import { isExecutionLane } from "../execution-lane-caps.js";
23
- import { SessionForkInProgressError, SessionWriterPresentError, WRITABLE, assertPlaceableLane, isUnusableArchive, settledMirrorOutcome, settledPlaceOutcome, } from "../execution-placement.js";
29
+ import { SessionForkInProgressError, WRITABLE, assertPlaceableLane, judgeSessionWrite, settledMirrorOutcome, settledPlaceOutcome, SessionDeletingError, } from "../execution-placement.js";
24
30
  import { classifyForkTarget, forkByFreshId } from "./session-fork.js";
25
31
  import { decodeInstructionSource, encodeInstructionSource, sameInstructionSourceBaseline } from "../instruction-source-notice.js";
26
32
  import { decodeSessionWorkspaceCell, encodeSessionWorkspaceCell } from "./session-workspace-cell.js";
27
33
  import { createCorruptReadFailOpenSeat } from "../observability/corrupt-read-seat.js";
28
- import { buildObjectivePreview, isAttachmentFrame } from "../user-authored-text.js";
29
34
  import { classifySyncRelationshipByIds, ownerCellIs, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, } from "../session-sync-kernel.js";
30
35
  const ownerEq = (a, b) => (a ?? null) === (b ?? null);
31
36
  const isNotFound = (e) => e instanceof SessionError && e.code === "not_found";
@@ -49,46 +54,29 @@ function archiveFaultWord(code) {
49
54
  }
50
55
  }
51
56
  }
52
- function unusableArchiveOf(word, fault) {
53
- const code = fault instanceof SessionError ? undefined : fault?.code;
57
+ function unusableArchiveOfFault(fault) {
58
+ const engine = fault instanceof SessionOwnerUnreadableError ? "corrupt_session" : fault instanceof SessionError ? archiveFaultWord(fault.code) : "storage";
59
+ const word = engine === "absent" ? "storage" : engine;
60
+ const code = fault instanceof SessionError || fault instanceof SessionOwnerUnreadableError ? undefined : fault?.code;
54
61
  return { state: "unusable", word, fault, ...(word === "storage" && typeof code === "string" ? { errno: code } : {}) };
55
62
  }
56
- function ownerUnreadableArchive(probe) {
57
- const { fault } = probe;
58
- if (probe.word === "storage" || !(fault instanceof SessionError))
59
- return probe;
60
- return { ...probe, fault: new SessionOwnerUnreadableError(fault) };
63
+ function ownerUnreadableFault(fault) {
64
+ return fault instanceof SessionError ? new SessionOwnerUnreadableError(fault) : fault instanceof Error ? fault : new Error(String(fault));
61
65
  }
66
+ const ABSENT_ROW = { row: "absent" };
67
+ const NO_CELL = { state: "none" };
62
68
  function importRowRefused(sessionId, landing) {
63
69
  return new SessionError("conflict", landing === "create"
64
70
  ? `session ${sessionId} already exists — importEntries only creates a session (replaceEntries rewrites one); nothing was written`
65
71
  : `session ${sessionId} is not the importer's row — an import replaces only a log whose row the importer owns (or an anonymous row, by an anonymous import); nothing was written`);
66
72
  }
67
- const SESSION_OBJECTIVE_PREVIEW_CHARS = 200;
68
- function userText(entry) {
69
- if (entry.type !== "message")
70
- return null;
71
- const msg = entry.message;
72
- if (msg.role !== "user" || isAttachmentFrame(msg))
73
- return null;
74
- const c = msg.content;
75
- if (typeof c === "string")
76
- return c;
77
- if (Array.isArray(c)) {
78
- return c
79
- .filter((p) => !!p && typeof p === "object" && p.type === "text")
80
- .map((p) => p.text)
81
- .join(" ");
82
- }
83
- return null;
84
- }
85
73
  const SIDECAR_DEGRADES = { lastActivity: "recency", title: "title", pinned: "pin state", "instruction-source": "instruction-source baseline", workspace: "each session's last recorded place and project (the next frame records it again)", "session-meta": "session owner + incarnation (the write that needed it failed loudly)" };
86
74
  const readPinnedSidecar = createZodColumnReader(z.record(z.string(), z.literal(true)));
87
75
  const MIRROR_CELL = { role: "mirror" };
88
76
  function readSessionMetaCell(cell) {
89
77
  return typeof cell === "string"
90
- ? { incarnation: cell, owner: undefined, execution: undefined, forkKey: undefined, createdAt: undefined, forkedFrom: undefined }
91
- : { incarnation: cell.incarnation, owner: cell.owner, execution: cell.execution, forkKey: cell.forkKey, createdAt: cell.createdAt, forkedFrom: cell.forkedFrom };
78
+ ? { incarnation: cell, owner: undefined, execution: undefined, forkKey: undefined, createdAt: undefined, forkedFrom: undefined, forkedFromIncarnation: undefined, dropping: false, deleting: undefined, clientWrites: undefined }
79
+ : { incarnation: cell.incarnation, owner: cell.owner, execution: cell.execution, forkKey: cell.forkKey, createdAt: cell.createdAt, forkedFrom: cell.forkedFrom, forkedFromIncarnation: cell.forkedFromIncarnation, dropping: cell.dropping === true, deleting: cell.deleting, clientWrites: cell.clientWrites };
92
80
  }
93
81
  const ownerRecorded = (cell) => cell !== undefined && readSessionMetaCell(cell).owner !== undefined;
94
82
  const cellObjectOf = (cell) => (typeof cell === "string" ? { incarnation: cell } : cell);
@@ -104,9 +92,9 @@ function archivedRowFactsOf(head) {
104
92
  ...(typeof forkedFrom === "string" && forkedFrom !== "" ? { forkedFrom } : {}),
105
93
  };
106
94
  }
107
- function rowFactsFilled(before, facts) {
95
+ function rowFactsFilled(sessionId, before, facts, archiveToken) {
108
96
  if (before === undefined)
109
- return { incarnation: mintSessionIncarnation(), createdAt: facts.createdAt, ...(facts.forkedFrom !== undefined ? { forkedFrom: facts.forkedFrom } : {}) };
97
+ return { incarnation: firstCellIncarnationOf(sessionId, archiveToken).incarnation, createdAt: facts.createdAt, ...(facts.forkedFrom !== undefined ? { forkedFrom: facts.forkedFrom } : {}) };
110
98
  const held = cellObjectOf(before);
111
99
  const add = {
112
100
  ...(held.createdAt === undefined ? { createdAt: facts.createdAt } : {}),
@@ -123,12 +111,37 @@ function rowCreatedAtOf(sessionId, meta) {
123
111
  }
124
112
  function rowMetadataOf(sessionId, meta) {
125
113
  const face = sessionFaceFromRow(sessionId, meta.owner ?? null, meta.incarnation);
126
- return { id: sessionId, createdAt: rowCreatedAtOf(sessionId, meta), owner: face.owner, incarnation: face.incarnation, ...(meta.forkedFrom !== undefined ? { forkedFrom: meta.forkedFrom } : {}) };
114
+ return { id: sessionId, createdAt: rowCreatedAtOf(sessionId, meta), owner: face.owner, incarnation: face.incarnation, ...forkLineageOfRow(sessionId, meta.forkedFrom, meta.forkedFromIncarnation) };
127
115
  }
128
116
  const isForkCreatedCell = (meta) => meta !== undefined && meta.forkKey !== undefined && meta.execution === undefined;
117
+ function placementCellOf(meta, forking) {
118
+ return forking ? { role: "forking" } : (meta?.execution ?? { role: "absent" });
119
+ }
129
120
  function readingOfCell(sessionId, meta, forking) {
130
- return { occupant: sessionFaceFromRow(sessionId, meta.owner ?? null, meta.incarnation), cell: forking ? { role: "forking" } : (meta.execution ?? { role: "absent" }) };
121
+ return { occupant: sessionFaceFromRow(sessionId, meta.owner ?? null, meta.incarnation), cell: placementCellOf(meta, forking) };
122
+ }
123
+ function legacyNeedsMint(seen) {
124
+ return seen.row === "legacy" && seen.placed === undefined && seen.unreadable === undefined;
131
125
  }
126
+ function executionReadingOf(sessionId, seen) {
127
+ switch (seen.row) {
128
+ case "absent":
129
+ return undefined;
130
+ case "live":
131
+ return seen.placed !== undefined ? undefined : readingOfCell(sessionId, seen.meta, seen.forking);
132
+ case "legacy":
133
+ if (seen.placed !== undefined)
134
+ return undefined;
135
+ if (seen.unreadable !== undefined)
136
+ throw ownerUnreadableFault(seen.unreadable);
137
+ throw new Error(`LocalSessionStore: session ${sessionId} is a legacy row — its cell is minted under its write lock before an execution reading is given`);
138
+ default: {
139
+ const unknownRow = seen;
140
+ throw new Error(`LocalSessionStore: no row observation ${JSON.stringify(unknownRow)}`);
141
+ }
142
+ }
143
+ }
144
+ const SESSION_META_SIDECAR_FILE = "session-incarnations.json";
132
145
  const LOCAL_EXECUTION_CELL = z.discriminatedUnion("role", [
133
146
  z.object({ role: z.literal("writer"), lane: z.custom((v) => typeof v === "string" && isExecutionLane(v), { message: "not an execution lane" }) }).strict(),
134
147
  z.object({ role: z.literal("mirror") }).strict(),
@@ -141,31 +154,53 @@ export const SESSION_META_CELL_OBJECT = z
141
154
  forkKey: z.string().optional(),
142
155
  createdAt: z.string().optional(),
143
156
  forkedFrom: z.string().optional(),
157
+ forkedFromIncarnation: z.string().optional(),
158
+ dropping: z.literal(true).optional(),
159
+ deleting: z.string().min(1).optional(),
160
+ clientWrites: z.number().int().nonnegative().optional(),
144
161
  })
145
162
  .strict();
146
163
  const SESSION_META_SIDECAR = z.record(z.string(), z.union([z.string(), SESSION_META_CELL_OBJECT]));
147
164
  const readSessionMetaSidecar = createZodColumnReader(SESSION_META_SIDECAR);
148
- function ownerClaimWindowOpen() {
149
- const window = ONE_TIME_MIGRATIONS.find((w) => w.id === "M10");
150
- if (window === undefined)
151
- throw new Error("LocalSessionStore: the one-time migration registry has no M10 row (the local session-owner claim window) — the claim arm outlived its registry row; delete it with that row");
152
- return migrationWindowOpen(window, new Date().toISOString().slice(0, 10));
153
- }
154
165
  const SIDECAR_MAP_STATE_BY_PATH = new Map();
155
166
  const SESSION_WRITE_LOCKS_BY_ROOT = new Map();
156
167
  const quotedFieldNames = (names) => `${names.slice(0, 8).map((name) => JSON.stringify(name.slice(0, 64))).join(", ")}${names.length > 8 ? `, … (${names.length} in all)` : ""}`;
168
+ function releaseAtOrAbove(a, b) {
169
+ const ta = releaseTriple(a);
170
+ const tb = releaseTriple(b);
171
+ return ta !== undefined && tb !== undefined && compareReleaseTriples(ta, tb) >= 0;
172
+ }
173
+ function describeDataRootFinding(f) {
174
+ if (f.problem === "newer-engine") {
175
+ 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}).`;
176
+ }
177
+ if (f.problem === "skipped-required-migration") {
178
+ const { highWater, requiredSince, sessions } = f.skippedMigration;
179
+ const consequence = "This build no longer carries that migration: opening the root would give those sessions a new occupant token, and memory records kept under the old one (capture opt-outs, pollution marks) would stop applying to them.";
180
+ if (highWater !== null && releaseAtOrAbove(highWater, requiredSince)) {
181
+ return (` ${f.label} at ${f.path}: ${sessions} session archive(s) have no session-meta cell, and the data root was last opened by server ${highWater}, which carries the one-time migration ` +
182
+ `(introduced in ${requiredSince}) that gives such an archive its row cell under the occupant token the archive already carries — but that start did not finish it ` +
183
+ `(${ARCHIVE_TOKEN_ADOPTION_PENDING_FILE} is still in the data root). ${consequence} ` +
184
+ `Start the root once with a release at or above ${highWater} that still carries that migration (server or run-local) and let that start finish, then start this build.`);
185
+ }
186
+ const lastOpened = highWater === null ? "no engine that records a high-water (run-local before 7.148.0, or a server before 7.144.0)" : `server ${highWater}`;
187
+ return (` ${f.label} at ${f.path}: ${sessions} session archive(s) have no session-meta cell, and the data root was last opened by ${lastOpened} — older than ${requiredSince}, ` +
188
+ `whose one-time migration gives such an archive its row cell under the occupant token the archive already carries. ${consequence} ` +
189
+ `Start the root once with a release at or above ${requiredSince} that still carries that migration (server or run-local) and let that start finish, then start this build.`);
190
+ }
191
+ return f.unknownFields.length > 0
192
+ ? ` ${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).`
193
+ : ` ${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.`;
194
+ }
157
195
  function describeLocalDataRootMismatch(findings, when) {
158
196
  const lines = [
159
197
  when === "boot"
160
198
  ? `${SCHEMA_MISMATCH_ERROR_CODE}: the local data root does not match this build's shape — refusing to start.`
161
199
  : `${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
200
  ];
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.");
201
+ for (const f of findings)
202
+ lines.push(describeDataRootFinding(f));
203
+ 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 four 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;", " (4) the data root skipped a required one-time migration, or a release carrying it opened the root and did not finish it (the finding names the release to start with): start the root once with a release at or above that one that still carries the migration (server or run-local), let that start finish, then start this build.", "The check behind this refusal only reads: the file(s) named above were left in place, unchanged.");
169
204
  return lines.join("\n");
170
205
  }
171
206
  export class LocalDataRootSchemaMismatchError extends StoreBootRefusalError {
@@ -179,6 +214,165 @@ export class LocalDataRootSchemaMismatchError extends StoreBootRefusalError {
179
214
  this.name = "LocalDataRootSchemaMismatchError";
180
215
  }
181
216
  }
217
+ export const ARCHIVE_TOKEN_ADOPTION_SINCE = "7.148.0";
218
+ export function archiveTokenAdoptionCarried() {
219
+ return ONE_TIME_MIGRATIONS.some((w) => w.id === "M47");
220
+ }
221
+ const ARCHIVE_DIR = "sessions";
222
+ const ARCHIVE_SUFFIX = ".jsonl";
223
+ const ARCHIVE_TOKEN_ADOPTION_PENDING_FILE = "archive-token-adoption.pending";
224
+ const archiveTokenAdoptionPendingPath = (root) => resolve(join(root, ARCHIVE_TOKEN_ADOPTION_PENDING_FILE));
225
+ function archiveTokenAdoptionPendingPresent(root) {
226
+ try {
227
+ lstatSync(archiveTokenAdoptionPendingPath(root));
228
+ return true;
229
+ }
230
+ catch (e) {
231
+ if (e?.code === "ENOENT")
232
+ return false;
233
+ throw e;
234
+ }
235
+ }
236
+ function recordArchiveTokenAdoptionPending(root, priorHighWater) {
237
+ const path = archiveTokenAdoptionPendingPath(root);
238
+ const prior = priorHighWater === undefined ? "none" : releaseText(priorHighWater);
239
+ try {
240
+ atomicWriteFileSync(path, `opened ${new Date().toISOString()} by a release that adopts archive tokens; engine high-water before that start: ${prior}\n`, { durability: "required" });
241
+ }
242
+ catch (e) {
243
+ throw new Error(`DB_BACKEND=local cannot record ${path} durably: ${e instanceof Error ? e.message : String(e)} — refusing to open the data root: ` +
244
+ "this start raises the engine high-water past the release that adopts the occupant tokens of session archives without a session-meta cell, and without this record a later build could not tell that the adoption has not finished. " +
245
+ "Repair write access to the data root and restart.", { cause: e });
246
+ }
247
+ }
248
+ function dischargeArchiveTokenAdoptionPending(root) {
249
+ const path = archiveTokenAdoptionPendingPath(root);
250
+ try {
251
+ unlinkSync(path);
252
+ }
253
+ catch (e) {
254
+ if (e?.code !== "ENOENT")
255
+ throw e;
256
+ return;
257
+ }
258
+ if (!IS_WIN32)
259
+ fsyncPaths(dirname(path));
260
+ }
261
+ function archiveIdsInSync(root) {
262
+ let names;
263
+ try {
264
+ names = readdirSync(join(root, ARCHIVE_DIR));
265
+ }
266
+ catch (e) {
267
+ if (e?.code === "ENOENT")
268
+ return [];
269
+ throw e;
270
+ }
271
+ return names.filter((name) => name.endsWith(ARCHIVE_SUFFIX)).map((name) => name.slice(0, -ARCHIVE_SUFFIX.length));
272
+ }
273
+ export function archiveIdsWithoutCellSync(root) {
274
+ const sidecarPath = resolve(join(root, SESSION_META_SIDECAR_FILE));
275
+ const label = `${SESSION_META_SIDECAR_FILE} (the local session-meta sidecar)`;
276
+ const raw = readDataRootFileTextSync(sidecarPath);
277
+ if (raw.state === "unreadable")
278
+ throw new LocalDataRootSchemaMismatchError([dataRootMismatchOf(sidecarPath, label, raw.fault)], "boot");
279
+ let cells = {};
280
+ if (raw.state === "text") {
281
+ try {
282
+ cells = decodeJsonColumn(raw.text, readSessionMetaSidecar, label);
283
+ }
284
+ catch (e) {
285
+ throw new LocalDataRootSchemaMismatchError([dataRootMismatchOf(sidecarPath, label, e)], "boot");
286
+ }
287
+ }
288
+ return archiveIdsInSync(root)
289
+ .filter((id) => !Object.hasOwn(cells, id))
290
+ .sort();
291
+ }
292
+ export function holdArchiveTokenAdoptionDuty(root, priorHighWater, adoptionCarried) {
293
+ const since = releaseTriple(ARCHIVE_TOKEN_ADOPTION_SINCE);
294
+ const neverOpenedByAdoptingRelease = priorHighWater === undefined || compareReleaseTriples(priorHighWater, since) < 0;
295
+ if (adoptionCarried) {
296
+ if (neverOpenedByAdoptingRelease && archiveIdsInSync(root).length > 0)
297
+ recordArchiveTokenAdoptionPending(root, priorHighWater);
298
+ return;
299
+ }
300
+ const owed = archiveTokenAdoptionPendingPresent(root);
301
+ if (!neverOpenedByAdoptingRelease && !owed)
302
+ return;
303
+ const cellLess = archiveIdsWithoutCellSync(root);
304
+ if (cellLess.length === 0) {
305
+ if (owed)
306
+ dischargeArchiveTokenAdoptionPending(root);
307
+ return;
308
+ }
309
+ throw new LocalDataRootSchemaMismatchError([
310
+ {
311
+ path: resolve(join(root, ARCHIVE_DIR)),
312
+ label: `${ARCHIVE_DIR} (the local session archives)`,
313
+ problem: "skipped-required-migration",
314
+ unknownFields: [],
315
+ skippedMigration: { highWater: priorHighWater === undefined ? null : releaseText(priorHighWater), requiredSince: ARCHIVE_TOKEN_ADOPTION_SINCE, sessions: cellLess.length },
316
+ },
317
+ ], "boot");
318
+ }
319
+ function firstCellIncarnationOf(sessionId, archiveToken) {
320
+ const token = archiveTokenAdoptionCarried() ? incarnationFromCell(sessionId, archiveToken, "absent") : undefined;
321
+ return token !== undefined ? { incarnation: token, adopted: true } : { incarnation: mintSessionIncarnation(), adopted: false };
322
+ }
323
+ function cellChangedUnderLock(sessionId) {
324
+ throw new SessionError("storage", `LocalSessionStore: session ${sessionId}'s session-meta cell changed under its own write lock between the observation and the write — refusing to guess its occupant`);
325
+ }
326
+ export async function readDataRootFileText(path) {
327
+ try {
328
+ return { state: "text", text: await readFile(path, "utf8") };
329
+ }
330
+ catch (e) {
331
+ if (e?.code !== "ENOENT")
332
+ return { state: "unreadable", fault: e };
333
+ return judgedAfterEnoent(e, await lstat(path).then(() => ENTRY_PRESENT, (fault) => ({ fault })));
334
+ }
335
+ }
336
+ export function readDataRootFileTextSync(path) {
337
+ try {
338
+ return { state: "text", text: readFileSync(path, "utf8") };
339
+ }
340
+ catch (e) {
341
+ if (e?.code !== "ENOENT")
342
+ return { state: "unreadable", fault: e };
343
+ let probe = ENTRY_PRESENT;
344
+ try {
345
+ lstatSync(path);
346
+ }
347
+ catch (fault) {
348
+ probe = { fault };
349
+ }
350
+ return judgedAfterEnoent(e, probe);
351
+ }
352
+ }
353
+ const ENTRY_PRESENT = { present: true };
354
+ function judgedAfterEnoent(readFault, probe) {
355
+ if ("present" in probe)
356
+ return { state: "unreadable", fault: readFault };
357
+ return probe.fault?.code === "ENOENT" ? { state: "absent" } : { state: "unreadable", fault: probe.fault };
358
+ }
359
+ export function dataRootMismatchOf(path, label, fault) {
360
+ if (fault instanceof SqlJsonColumnError)
361
+ return { path, label, problem: fault.problem, unknownFields: fault.unknownKeys };
362
+ const code = fault?.code;
363
+ return { path, label, problem: typeof code === "string" && /^E[A-Z0-9_]+$/.test(code) ? code : "unreadable", unknownFields: [] };
364
+ }
365
+ async function readSessionMetaForM40(path) {
366
+ const raw = await readDataRootFileText(path);
367
+ if (raw.state !== "text")
368
+ return raw;
369
+ try {
370
+ return { state: "read", entries: decodeJsonColumn(raw.text, readJsonObjectColumn, `${SESSION_META_SIDECAR_FILE} (one-time migration M40)`) };
371
+ }
372
+ catch (e) {
373
+ return { state: "unreadable", fault: e };
374
+ }
375
+ }
182
376
  class SidecarMap {
183
377
  path;
184
378
  label;
@@ -243,16 +437,27 @@ class SidecarMap {
243
437
  if (disk.state === "unreadable")
244
438
  throw this.refuse(path, disk);
245
439
  }
246
- 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 };
440
+ async landed(path, changed, draft) {
441
+ const disk = await this.readDisk(path);
442
+ switch (disk.state) {
443
+ case "absent":
444
+ return "not-landed";
445
+ case "unreadable":
446
+ return "unknown";
447
+ case "read":
448
+ return changed.every((key) => (draft.has(key) ? Object.hasOwn(disk.entries, key) && isDeepStrictEqual(disk.entries[key], draft.get(key)) : !Object.hasOwn(disk.entries, key))) ? "landed" : "not-landed";
449
+ default: {
450
+ const unknownRead = disk;
451
+ throw new Error(`SidecarMap: no sidecar read state ${JSON.stringify(unknownRead)}`);
452
+ }
253
453
  }
454
+ }
455
+ async readDisk(path) {
456
+ const raw = await readDataRootFileText(path);
457
+ if (raw.state !== "text")
458
+ return raw;
254
459
  try {
255
- return { state: "read", entries: decodeJsonColumn(text, this.read, this.label) };
460
+ return { state: "read", entries: decodeJsonColumn(raw.text, this.read, this.label) };
256
461
  }
257
462
  catch (e) {
258
463
  return { state: "unreadable", fault: e };
@@ -260,15 +465,13 @@ class SidecarMap {
260
465
  }
261
466
  refuse(path, disk) {
262
467
  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");
468
+ const refusal = new LocalDataRootSchemaMismatchError([dataRootMismatchOf(path, this.label, disk.fault)], "serving");
266
469
  if (shape)
267
470
  this.state.refused = refusal;
268
471
  return refusal;
269
472
  }
270
473
  persist() {
271
- return this.commit(() => true);
474
+ return this.commit(() => true, "best-effort");
272
475
  }
273
476
  syncToDisk() {
274
477
  const path = this.path;
@@ -276,10 +479,10 @@ class SidecarMap {
276
479
  return;
277
480
  fsyncPaths(path, dirname(path));
278
481
  }
279
- deleteKey(key) {
280
- return this.commit((draft) => draft.delete(key));
482
+ deleteKey(key, durability) {
483
+ return this.commit((draft) => draft.delete(key), durability);
281
484
  }
282
- commit(change) {
485
+ commit(change, durability) {
283
486
  const path = this.path;
284
487
  const st = this.state;
285
488
  const link = st.writeChain.then(async () => {
@@ -287,8 +490,27 @@ class SidecarMap {
287
490
  throw st.refused;
288
491
  const base = new Map(st.entries);
289
492
  const draft = new Map(base);
290
- if (!change(draft))
291
- return true;
493
+ if (!change(draft)) {
494
+ if (durability !== "required" || !path || !existsSync(path))
495
+ return true;
496
+ try {
497
+ proveDurableSync(path);
498
+ return true;
499
+ }
500
+ catch (e) {
501
+ this.onWriteFailed(e);
502
+ return false;
503
+ }
504
+ }
505
+ const changed = [...new Set([...base.keys(), ...draft.keys()])].filter((key) => !(draft.has(key) === base.has(key) && draft.get(key) === base.get(key)));
506
+ const publish = () => {
507
+ for (const key of changed) {
508
+ if (draft.has(key))
509
+ st.entries.set(key, draft.get(key));
510
+ else
511
+ st.entries.delete(key);
512
+ }
513
+ };
292
514
  if (path) {
293
515
  if (this.strict) {
294
516
  const disk = await this.readDisk(path);
@@ -300,23 +522,16 @@ class SidecarMap {
300
522
  }
301
523
  }
302
524
  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);
525
+ await atomicWriteFileAsync(path, JSON.stringify(Object.fromEntries(draft)), { durability });
306
526
  }
307
527
  catch (e) {
308
528
  this.onWriteFailed(e);
529
+ if (durability === "required" && (await this.landed(path, changed, draft)) !== "not-landed")
530
+ publish();
309
531
  return false;
310
532
  }
311
533
  }
312
- for (const key of new Set([...base.keys(), ...draft.keys()])) {
313
- if (draft.has(key) === base.has(key) && draft.get(key) === base.get(key))
314
- continue;
315
- if (draft.has(key))
316
- st.entries.set(key, draft.get(key));
317
- else
318
- st.entries.delete(key);
319
- }
534
+ publish();
320
535
  return true;
321
536
  });
322
537
  st.writeChain = link.then(() => undefined, () => undefined);
@@ -428,14 +643,12 @@ export class LocalSessionStore {
428
643
  listPlaced;
429
644
  recordedPlacementOf;
430
645
  pending = new Map();
431
- rowsDropped = new Set();
432
646
  lastActivity = new Map();
433
647
  stagings = new Map();
434
648
  loaded;
435
649
  flushTimer;
436
650
  writeChain = Promise.resolve();
437
651
  dirty = false;
438
- writeSeq = 0;
439
652
  lastWarnAt = 0;
440
653
  sidecarPath;
441
654
  titles;
@@ -450,10 +663,12 @@ export class LocalSessionStore {
450
663
  executionPlacementBackfill;
451
664
  dataRoot;
452
665
  archiveSnapshotsSettled = 0;
666
+ withdrawCheckpointCtx;
453
667
  constructor(repo, runs, dataDir, opts) {
454
668
  this.repo = repo;
455
669
  this.runs = runs;
456
670
  this.sidecarPath = dataDir ? join(dataDir, "session-last-activity.json") : undefined;
671
+ this.withdrawCheckpointCtx = opts?.withdrawCheckpointCtx;
457
672
  this.log = opts?.log ?? createLogger();
458
673
  const repoPlacementOf = repo.placementOf;
459
674
  const repoListPlaced = repo.listPlaced;
@@ -493,7 +708,7 @@ export class LocalSessionStore {
493
708
  this.pinned = sidecar("session-pinned.json", "pinned", readPinnedSidecar);
494
709
  this.instructionSources = sidecar("session-instruction-sources.json", "instruction-source", readStringRecordColumn);
495
710
  this.workspaces = sidecar("session-workspaces.json", "workspace", readStringRecordColumn);
496
- this.meta = sidecar("session-incarnations.json", "session-meta", readSessionMetaSidecar, true);
711
+ this.meta = sidecar(SESSION_META_SIDECAR_FILE, "session-meta", readSessionMetaSidecar, true);
497
712
  const root = dataDir ? resolve(dataDir) : undefined;
498
713
  this.dataRoot = root;
499
714
  if (root !== undefined && !SESSION_WRITE_LOCKS_BY_ROOT.has(root))
@@ -504,10 +719,44 @@ export class LocalSessionStore {
504
719
  place: (sessionId, occupant, cell) => this.placeExecution(sessionId, occupant, cell),
505
720
  markMirror: (sessionId, occupant) => this.markMirrorExecution(sessionId, occupant),
506
721
  };
507
- this.executionPlacementBackfill = {
508
- read: (sessionId) => this.placementRead(sessionId),
509
- place: (sessionId, occupant, cell) => this.placeExecution(sessionId, occupant, cell),
510
- };
722
+ this.executionPlacementBackfill = { backfill: (sessionId, lanes) => this.backfillPlacement(sessionId, lanes) };
723
+ }
724
+ async scenarioPrefixOwnerRowsM42() {
725
+ await this.meta.ensureLoaded();
726
+ let n = 0;
727
+ for (const id of [...this.liveCellIds(), ...this.droppingIds(), ...this.deletingIds()]) {
728
+ const cell = this.cellStateOf(id);
729
+ if (cell.state !== "none" && typeof cell.meta.owner === "string" && cell.meta.owner.startsWith(SCENARIO_SUBJECT_PREFIX))
730
+ n += 1;
731
+ }
732
+ return n;
733
+ }
734
+ async renameRetiredExecutionLaneWordM40() {
735
+ if (this.dataRoot === undefined)
736
+ return 0;
737
+ const path = join(this.dataRoot, SESSION_META_SIDECAR_FILE);
738
+ const disk = await readSessionMetaForM40(path);
739
+ if (disk.state !== "read")
740
+ return 0;
741
+ let renamed = 0;
742
+ for (const cell of Object.values(disk.entries)) {
743
+ if (!isJsonObject(cell))
744
+ continue;
745
+ const execution = cell["execution"];
746
+ if (!isJsonObject(execution) || execution["role"] !== "writer" || execution["lane"] !== RETIRED_EXECUTION_LANE_WORD_M40)
747
+ continue;
748
+ execution["lane"] = RENAMED_EXECUTION_LANE_M40;
749
+ renamed += 1;
750
+ }
751
+ if (renamed === 0)
752
+ return 0;
753
+ try {
754
+ await atomicWriteFileAsync(path, JSON.stringify(disk.entries), { durability: "best-effort" });
755
+ }
756
+ catch (e) {
757
+ throw new ExecutionLaneRenameWriteRefusedError(path, e);
758
+ }
759
+ return renamed;
511
760
  }
512
761
  async assertStrictSidecarsReadable() {
513
762
  const loads = await Promise.allSettled(this.strictSidecars.map((map) => map.reconcile()));
@@ -522,39 +771,67 @@ export class LocalSessionStore {
522
771
  if (findings.length > 0)
523
772
  throw new LocalDataRootSchemaMismatchError(findings, "boot");
524
773
  }
525
- async forkingRead(sessionId, meta) {
774
+ async forkingOf(sessionId, meta) {
526
775
  if (!isForkCreatedCell(meta))
527
776
  return false;
528
- const probe = await this.probeArchive(sessionId);
529
- if (probe.state === "unusable" && probe.word === "storage")
530
- return probe;
531
- return probe.state === "absent";
777
+ return (await this.probeArchive(sessionId)).state === "absent";
532
778
  }
533
- async forkingOf(sessionId, meta) {
534
- const forking = await this.forkingRead(sessionId, meta);
535
- if (typeof forking !== "boolean")
536
- throw forking.fault;
537
- return forking;
538
- }
539
- async placementRead(sessionId) {
540
- const read = await this.metaRead(sessionId);
541
- if (read.state === "unusable")
542
- return read;
543
- if (read.state === "absent")
544
- return undefined;
545
- const partition = await this.placementProbe(sessionId);
546
- if (partition.state === "placed")
547
- return undefined;
548
- if (partition.state === "unusable" && partition.word === "storage")
549
- return partition;
550
- const forking = await this.forkingRead(sessionId, read.meta);
551
- return typeof forking === "boolean" ? readingOfCell(sessionId, read.meta, forking) : forking;
779
+ async observeRow(sessionId, want = {}) {
780
+ await this.meta.ensureLoaded();
781
+ const cell = this.cellStateOf(sessionId);
782
+ if (cell.state === "dropping" || cell.state === "deleting")
783
+ return ABSENT_ROW;
784
+ if (cell.state === "none") {
785
+ const probe = await this.probeArchive(sessionId);
786
+ if (probe.state === "absent")
787
+ return ABSENT_ROW;
788
+ const placed = want.placement === true ? await this.placedRecordOf(sessionId) : undefined;
789
+ return { row: "legacy", placed, ...(probe.state === "corrupt" ? { unreadable: probe.fault } : {}) };
790
+ }
791
+ const placed = want.placement === true ? await this.placedRecordOf(sessionId) : undefined;
792
+ const forking = want.forking === true && placed === undefined ? await this.forkingOf(sessionId, cell.meta) : false;
793
+ return { row: "live", meta: cell.meta, placed, forking };
552
794
  }
553
795
  async readExecutionPlacement(sessionId) {
554
- const reading = await this.placementRead(sessionId);
555
- if (isUnusableArchive(reading))
556
- throw reading.fault;
557
- return reading;
796
+ const seen = await this.observeRow(sessionId, { placement: true, forking: true });
797
+ if (!legacyNeedsMint(seen))
798
+ return executionReadingOf(sessionId, seen);
799
+ return this.withSessionWriteLock(sessionId, async () => {
800
+ const held = await this.observeRow(sessionId, { placement: true, forking: true });
801
+ return legacyNeedsMint(held) ? readingOfCell(sessionId, await this.mintHeld(sessionId), false) : executionReadingOf(sessionId, held);
802
+ });
803
+ }
804
+ async backfillPlacement(sessionId, lanes) {
805
+ return this.withSessionWriteLock(sessionId, async () => {
806
+ let seen;
807
+ try {
808
+ seen = await this.observeRow(sessionId, { placement: true, forking: true });
809
+ }
810
+ catch (fault) {
811
+ return unusableArchiveOfFault(fault);
812
+ }
813
+ if (seen.row === "absent" || seen.placed !== undefined)
814
+ return { outcome: "skipped" };
815
+ if (seen.row === "legacy" && seen.unreadable !== undefined)
816
+ return unusableArchiveOfFault(ownerUnreadableFault(seen.unreadable));
817
+ if (seen.row === "live" && (seen.meta.execution !== undefined || seen.forking))
818
+ return { outcome: "skipped" };
819
+ if (lanes.length !== 1)
820
+ return { outcome: "ambiguous" };
821
+ const writer = { role: "writer", lane: lanes[0] };
822
+ assertPlaceableLane(sessionId, writer);
823
+ let first;
824
+ if (seen.row === "legacy") {
825
+ try {
826
+ first = await this.firstCellIncarnationHeld(sessionId);
827
+ }
828
+ catch (fault) {
829
+ return unusableArchiveOfFault(fault);
830
+ }
831
+ }
832
+ await this.writeMeta(sessionId, (before) => before === undefined ? { incarnation: first ?? cellChangedUnderLock(sessionId), execution: writer } : readSessionMetaCell(before).execution === undefined ? cellWith(before, { execution: writer }) : undefined);
833
+ return { outcome: "placed" };
834
+ });
558
835
  }
559
836
  async placeExecution(sessionId, occupant, cell) {
560
837
  assertPlaceableLane(sessionId, cell);
@@ -566,7 +843,7 @@ export class LocalSessionStore {
566
843
  async markMirrorExecution(sessionId, occupant) {
567
844
  return this.withSessionWriteLock(sessionId, async () => {
568
845
  await this.meta.ensureLoaded();
569
- const claim = this.metaCellOf(sessionId) === undefined ? undefined : await this.runs.getActiveTaskId(sessionId);
846
+ const claim = this.liveCellOf(sessionId) === undefined ? undefined : await this.runs.getActiveTaskId(sessionId);
570
847
  return this.settleExecutionHeld(sessionId, (reading) => {
571
848
  const settled = settledMirrorOutcome(sessionId, reading, occupant);
572
849
  if (settled !== WRITABLE)
@@ -576,9 +853,9 @@ export class LocalSessionStore {
576
853
  });
577
854
  }
578
855
  async settleExecutionHeld(sessionId, judge) {
579
- await this.meta.ensureLoaded();
580
- const placed = await this.isPlacedRow(sessionId);
581
- const forking = !placed && (await this.forkingOf(sessionId, this.metaCellOf(sessionId)));
856
+ const seen = await this.observeRow(sessionId, { placement: true, forking: true });
857
+ const placed = seen.row !== "absent" && seen.placed !== undefined;
858
+ const forking = seen.row === "live" && seen.forking;
582
859
  let settled;
583
860
  let fault;
584
861
  await this.writeMeta(sessionId, (before) => {
@@ -595,33 +872,75 @@ export class LocalSessionStore {
595
872
  throw fault;
596
873
  return settled.outcome;
597
874
  }
598
- metaCellOf(sessionId) {
875
+ cellStateOf(sessionId) {
599
876
  const cell = this.meta.state.entries.get(sessionId);
600
- return cell === undefined ? undefined : readSessionMetaCell(cell);
877
+ if (cell === undefined)
878
+ return NO_CELL;
879
+ const meta = readSessionMetaCell(cell);
880
+ return { state: meta.deleting !== undefined ? "deleting" : meta.dropping ? "dropping" : "live", meta };
881
+ }
882
+ async rowDropping(sessionId) {
883
+ await this.meta.ensureLoaded();
884
+ const state = this.cellStateOf(sessionId).state;
885
+ return state === "dropping" || state === "deleting";
886
+ }
887
+ rowGoneSince(sessionId, hadLiveCell) {
888
+ const state = this.cellStateOf(sessionId).state;
889
+ return state === "dropping" || state === "deleting" || (state === "none" && hadLiveCell);
890
+ }
891
+ liveCellOf(sessionId) {
892
+ const cell = this.cellStateOf(sessionId);
893
+ return cell.state === "live" ? cell.meta : undefined;
894
+ }
895
+ liveCellIds() {
896
+ return [...this.meta.state.entries]
897
+ .filter(([, cell]) => {
898
+ const meta = readSessionMetaCell(cell);
899
+ return !meta.dropping && meta.deleting === undefined;
900
+ })
901
+ .map(([id]) => id)
902
+ .sort();
903
+ }
904
+ deletingIds() {
905
+ return [...this.meta.state.entries].filter(([id]) => this.cellStateOf(id).state === "deleting").map(([id]) => id).sort();
906
+ }
907
+ droppingIds() {
908
+ return [...this.meta.state.entries].filter(([id]) => this.cellStateOf(id).state === "dropping").map(([id]) => id).sort();
601
909
  }
602
910
  async writeMeta(sessionId, next) {
603
911
  await this.meta.ensureLoaded();
604
912
  let changed = false;
605
913
  const committed = await this.meta.commit((draft) => {
606
- const value = next(draft.get(sessionId));
914
+ const before = draft.get(sessionId);
915
+ if (before !== undefined && readSessionMetaCell(before).deleting !== undefined)
916
+ throw new SessionError("not_found", `LocalSessionStore: session ${sessionId} is being deleted — it reads as gone; this write was not made`);
917
+ if (before !== undefined && readSessionMetaCell(before).dropping)
918
+ throw new Error(`LocalSessionStore: session ${sessionId}'s cell carries the drop mark while another writer reached it — every section finishes a pending drop first; refusing to write`);
919
+ const value = next(before);
607
920
  if (value === undefined)
608
921
  return false;
609
922
  draft.set(sessionId, value);
610
923
  changed = true;
611
924
  return true;
612
- });
925
+ }, "best-effort");
613
926
  if (!committed)
614
927
  throw new SessionError("storage", `LocalSessionStore: session-meta sidecar write failed for session ${sessionId} — nothing was recorded`);
615
928
  return changed;
616
929
  }
617
930
  async dropMeta(sessionId) {
618
931
  await this.meta.ensureLoaded();
619
- if (!(await this.meta.deleteKey(sessionId)))
620
- throw new SessionError("storage", `LocalSessionStore: session-meta sidecar write failed while deleting session ${sessionId} — its owner/incarnation cell is still on disk`);
932
+ if (!(await this.meta.deleteKey(sessionId, "best-effort")))
933
+ throw new SessionError("storage", `LocalSessionStore: session-meta sidecar write failed while withdrawing session ${sessionId}'s cell — the cell is still on disk`);
934
+ }
935
+ async withRootRow(sessionId, body) {
936
+ return this.withSessionWriteLock(sessionId, async () => body(await this.rowPresentHeld(sessionId)));
937
+ }
938
+ async rowPresentHeld(sessionId) {
939
+ return (await this.observeRow(sessionId)).row !== "absent";
621
940
  }
622
941
  async withSessionWriteLock(sessionId, body) {
623
942
  const locks = this.sessionWriteLocks;
624
- const link = (locks.get(sessionId) ?? Promise.resolve()).then(body);
943
+ const link = (locks.get(sessionId) ?? Promise.resolve()).then(async () => body(await this.finishPendingDropHeld(sessionId)));
625
944
  const tail = link.then(() => undefined, () => undefined);
626
945
  locks.set(sessionId, tail);
627
946
  try {
@@ -639,7 +958,9 @@ export class LocalSessionStore {
639
958
  }
640
959
  catch (e) {
641
960
  const word = e instanceof SessionError ? archiveFaultWord(e.code) : "storage";
642
- return word === "absent" ? { state: "absent" } : unusableArchiveOf(word, e);
961
+ if (word === "storage")
962
+ throw e;
963
+ return word === "absent" ? { state: "absent" } : { state: "corrupt", fault: e };
643
964
  }
644
965
  }
645
966
  async placementProbe(sessionId) {
@@ -649,76 +970,88 @@ export class LocalSessionStore {
649
970
  }
650
971
  catch (e) {
651
972
  const word = e instanceof SessionError ? archiveFaultWord(e.code) : "storage";
652
- return word === "absent" ? { state: "ordinary" } : unusableArchiveOf(word, e);
973
+ if (word === "storage")
974
+ throw e;
975
+ return word === "absent" ? { state: "ordinary" } : { state: "corrupt", fault: e };
653
976
  }
654
977
  }
655
978
  async placedRecordOf(sessionId) {
656
979
  const probe = await this.placementProbe(sessionId);
657
- if (probe.state === "unusable") {
658
- if (probe.word === "storage")
659
- throw probe.fault;
660
- return undefined;
661
- }
662
980
  return probe.state === "placed" ? probe.record : undefined;
663
981
  }
664
- async isPlacedRow(sessionId) {
665
- return (await this.placedRecordOf(sessionId)) !== undefined;
666
- }
667
982
  async placedRecordOrThrow(sessionId) {
668
983
  const probe = await this.placementProbe(sessionId);
669
- if (probe.state === "unusable")
984
+ if (probe.state === "corrupt")
670
985
  throw probe.fault;
671
986
  return probe.state === "placed" ? probe.record : undefined;
672
987
  }
673
988
  async existsInRepo(sessionId) {
674
989
  const probe = await this.probeArchive(sessionId);
675
- if (probe.state === "unusable")
990
+ if (probe.state === "corrupt")
676
991
  throw probe.fault;
677
992
  return probe.state === "readable";
678
993
  }
679
994
  async metaOf(sessionId) {
680
- const read = await this.metaRead(sessionId);
681
- if (read.state === "unusable")
682
- throw read.fault;
683
- return read.state === "found" ? read.meta : undefined;
995
+ const seen = await this.observeRow(sessionId);
996
+ switch (seen.row) {
997
+ case "absent":
998
+ return undefined;
999
+ case "live":
1000
+ return seen.meta;
1001
+ case "legacy":
1002
+ if (seen.unreadable !== undefined)
1003
+ throw ownerUnreadableFault(seen.unreadable);
1004
+ return this.withSessionWriteLock(sessionId, () => this.metaHeld(sessionId));
1005
+ default: {
1006
+ const unknownRow = seen;
1007
+ throw new Error(`LocalSessionStore: no row observation ${JSON.stringify(unknownRow)}`);
1008
+ }
1009
+ }
684
1010
  }
685
- async metaRead(sessionId) {
686
- await this.meta.ensureLoaded();
687
- const cell = this.meta.state.entries.get(sessionId);
688
- if (cell !== undefined)
689
- return { state: "found", meta: readSessionMetaCell(cell) };
690
- const probe = await this.probeArchive(sessionId);
691
- if (probe.state === "unusable")
692
- return ownerUnreadableArchive(probe);
693
- if (probe.state === "absent")
694
- return { state: "absent" };
695
- const meta = await this.cellOfPresent(sessionId);
696
- return meta === undefined ? { state: "absent" } : { state: "found", meta };
697
- }
698
- async cellOfPresent(sessionId) {
699
- if (!this.meta.state.entries.has(sessionId))
700
- await this.writeMeta(sessionId, (before) => (before === undefined && !this.rowsDropped.has(sessionId) ? { incarnation: mintSessionIncarnation() } : undefined));
701
- const cell = this.meta.state.entries.get(sessionId);
702
- return cell === undefined ? undefined : readSessionMetaCell(cell);
1011
+ async metaHeld(sessionId) {
1012
+ const seen = await this.observeRow(sessionId);
1013
+ if (seen.row === "absent")
1014
+ return undefined;
1015
+ if (seen.row === "live")
1016
+ return seen.meta;
1017
+ if (seen.unreadable !== undefined)
1018
+ throw ownerUnreadableFault(seen.unreadable);
1019
+ return this.mintHeld(sessionId);
1020
+ }
1021
+ async mintHeld(sessionId) {
1022
+ const incarnation = await this.firstCellIncarnationHeld(sessionId);
1023
+ await this.writeMeta(sessionId, (before) => (before === undefined ? { incarnation } : undefined));
1024
+ const meta = this.liveCellOf(sessionId);
1025
+ if (meta === undefined)
1026
+ throw new SessionError("storage", `LocalSessionStore: session ${sessionId} has no live session-meta cell right after its cell was minted under its own write lock — refusing to guess`);
1027
+ return meta;
1028
+ }
1029
+ async firstCellIncarnationHeld(sessionId) {
1030
+ const head = archiveTokenAdoptionCarried() ? await this.archiveHeadOf(sessionId) : undefined;
1031
+ return firstCellIncarnationOf(sessionId, head?.incarnation).incarnation;
703
1032
  }
704
1033
  async headOf(sessionId) {
705
1034
  const before = await this.metaOf(sessionId);
706
1035
  if (before === undefined)
707
1036
  return undefined;
708
1037
  const leaf = await this.archiveHeadOf(sessionId).then((archive) => archive?.leafId ?? null, (fault) => new UnreadableLeaf(fault));
709
- const cell = this.meta.state.entries.get(sessionId);
710
- const after = cell === undefined ? undefined : readSessionMetaCell(cell);
1038
+ const after = this.liveCellOf(sessionId);
711
1039
  if (after === undefined || after.incarnation !== before.incarnation || (after.owner ?? null) !== (before.owner ?? null))
712
1040
  return undefined;
713
1041
  return headFromRow(sessionId, before.owner ?? null, before.incarnation, leaf);
714
1042
  }
1043
+ async deletionHeadOf(sessionId) {
1044
+ await this.meta.ensureLoaded();
1045
+ const cell = this.cellStateOf(sessionId);
1046
+ if (cell.state === "dropping" || cell.state === "deleting")
1047
+ return headFromRow(sessionId, cell.meta.owner ?? null, cell.meta.incarnation, null);
1048
+ return this.headOf(sessionId);
1049
+ }
715
1050
  archiveHeadOf(sessionId) {
716
1051
  return this.repo.headOf(sessionId);
717
1052
  }
718
1053
  async handOut(sessionId, session) {
719
- const meta = await this.cellOfPresent(sessionId);
720
- if (meta === undefined)
721
- throw new SessionError("storage", `LocalSessionStore: session ${sessionId} lost its session-meta cell while it was being handed out`);
1054
+ const meta = this.liveCellOf(sessionId) ?? (await this.mintHeld(sessionId));
722
1055
  return { session: occupiedBy(session, rowMetadataOf(sessionId, meta)), sessionId };
723
1056
  }
724
1057
  async sessionMetadataOf(sessionId) {
@@ -726,11 +1059,15 @@ export class LocalSessionStore {
726
1059
  return meta === undefined ? undefined : rowMetadataOf(sessionId, meta);
727
1060
  }
728
1061
  async probeTitle(sessionId) {
1062
+ if (await this.rowDropping(sessionId))
1063
+ return "none";
729
1064
  await this.titles.ensureLoaded();
730
1065
  return this.titles.state.entries.has(sessionId) ? "titled" : "untitled";
731
1066
  }
732
1067
  async setTitleIfNull(sessionId, title) {
733
1068
  return this.withSessionWriteLock(sessionId, async () => {
1069
+ if (!(await this.rowPresentHeld(sessionId)))
1070
+ return false;
734
1071
  await this.titles.ensureLoaded();
735
1072
  const entries = this.titles.state.entries;
736
1073
  if (entries.has(sessionId))
@@ -756,6 +1093,7 @@ export class LocalSessionStore {
756
1093
  case "mirror":
757
1094
  return { kind: "mirror" };
758
1095
  case "write":
1096
+ await this.bumpClientWritesHeld(sessionId);
759
1097
  await this.writeRowCellsHeld(sessionId, cells);
760
1098
  return settleSessionRowWrite(how, verdict, await this.listRowOf(sessionId));
761
1099
  case "changed":
@@ -768,7 +1106,7 @@ export class LocalSessionStore {
768
1106
  });
769
1107
  }
770
1108
  async lockedRowOf(sessionId) {
771
- const meta = this.metaCellOf(sessionId);
1109
+ const meta = this.liveCellOf(sessionId);
772
1110
  if (meta === undefined || (await this.rowHeaderOf(sessionId)) === undefined)
773
1111
  return undefined;
774
1112
  return { owner: meta.owner ?? null, incarnation: incarnationFromCell(sessionId, meta.incarnation, "absent"), placed: false, mirror: meta.execution?.role === "mirror", cells: this.rowCellsOf(sessionId) };
@@ -786,7 +1124,7 @@ export class LocalSessionStore {
786
1124
  return false;
787
1125
  draft.set(sessionId, title);
788
1126
  return true;
789
- });
1127
+ }, "best-effort");
790
1128
  if (!ok)
791
1129
  throw new SessionError("storage", `LocalSessionStore: title sidecar write failed for session ${sessionId} — the title was not recorded`);
792
1130
  }
@@ -798,7 +1136,7 @@ export class LocalSessionStore {
798
1136
  return false;
799
1137
  draft.set(sessionId, true);
800
1138
  return true;
801
- });
1139
+ }, "best-effort");
802
1140
  if (!ok)
803
1141
  throw new SessionError("storage", `LocalSessionStore: pin sidecar write failed for session ${sessionId} — the pin was not recorded`);
804
1142
  }
@@ -806,23 +1144,28 @@ export class LocalSessionStore {
806
1144
  async listRowOf(sessionId) {
807
1145
  await this.loadRowSidecars();
808
1146
  const header = await this.rowHeaderOf(sessionId);
809
- const meta = this.metaCellOf(sessionId);
1147
+ const meta = this.liveCellOf(sessionId);
810
1148
  if (header === undefined || meta === undefined)
811
1149
  return undefined;
812
- return { row: await this.summaryOf(await this.listedRowOf(header), await this.runRollups()), occupant: { owner: meta.owner ?? null, incarnation: incarnationFromCell(sessionId, meta.incarnation, "absent") } };
1150
+ return { row: this.summaryOf(await this.listedRowOf(header), await this.runRollups()), occupant: { owner: meta.owner ?? null, incarnation: incarnationFromCell(sessionId, meta.incarnation, "absent") } };
813
1151
  }
814
1152
  async sessionSummaryOf(sessionId) {
815
1153
  await this.loadRowSidecars();
816
1154
  const header = await this.rowHeaderOf(sessionId);
817
- return header === undefined ? undefined : this.summaryOf(await this.listedRowOf(header), await this.runRollups());
1155
+ if (header === undefined)
1156
+ return undefined;
1157
+ const hadLiveCell = this.cellStateOf(sessionId).state === "live";
1158
+ const listed = await this.listedRowOf(header);
1159
+ const rollups = await this.runRollups();
1160
+ return this.rowGoneSince(sessionId, hadLiveCell) ? undefined : this.summaryOf(listed, rollups);
818
1161
  }
819
1162
  async rowHeaders() {
820
1163
  await this.meta.ensureLoaded();
821
- const listed = await this.repo.list();
1164
+ const listed = (await this.repo.list()).filter((row) => this.cellStateOf(row.id).state !== "dropping");
822
1165
  const listedIds = new Set(listed.map((row) => row.id));
823
1166
  const placed = this.createPlacedIdsReader();
824
1167
  const rows = listed.map((header) => ({ sessionId: header.id, header }));
825
- for (const id of [...this.meta.state.entries.keys()].sort()) {
1168
+ for (const id of this.liveCellIds()) {
826
1169
  if (listedIds.has(id))
827
1170
  continue;
828
1171
  const row = await this.headerUnreadableRowOf(id, placed);
@@ -833,10 +1176,13 @@ export class LocalSessionStore {
833
1176
  }
834
1177
  async rowHeaderOf(sessionId) {
835
1178
  await this.meta.ensureLoaded();
1179
+ const cell = this.cellStateOf(sessionId);
1180
+ if (cell.state === "dropping" || cell.state === "deleting")
1181
+ return undefined;
836
1182
  const header = (await this.repo.list()).find((m) => m.id === sessionId);
837
1183
  if (header !== undefined)
838
1184
  return { sessionId, header };
839
- return this.meta.state.entries.has(sessionId) ? this.headerUnreadableRowOf(sessionId, this.createPlacedIdsReader()) : undefined;
1185
+ return cell.state === "live" ? this.headerUnreadableRowOf(sessionId, this.createPlacedIdsReader()) : undefined;
840
1186
  }
841
1187
  async headerUnreadableRowOf(sessionId, placed) {
842
1188
  try {
@@ -856,35 +1202,43 @@ export class LocalSessionStore {
856
1202
  return { sessionId: row.sessionId, unreadable: row.headerFault };
857
1203
  try {
858
1204
  await this.archiveHeadOf(row.sessionId);
859
- return { sessionId: row.sessionId, readable: row.header };
1205
+ return { sessionId: row.sessionId, readable: true };
860
1206
  }
861
1207
  catch (fault) {
862
1208
  return { sessionId: row.sessionId, unreadable: fault };
863
1209
  }
864
1210
  }
865
1211
  async readInstructionSource(sessionId) {
1212
+ if (await this.rowDropping(sessionId))
1213
+ return decodeInstructionSource(undefined);
866
1214
  await this.instructionSources.ensureLoaded();
867
1215
  return decodeInstructionSource(this.instructionSources.state.entries.get(sessionId));
868
1216
  }
869
1217
  async recordInstructionSource(sessionId, expected, source) {
870
- await this.instructionSources.ensureLoaded();
871
- const committed = await this.instructionSources.commit((draft) => {
872
- if (!sameInstructionSourceBaseline(decodeInstructionSource(draft.get(sessionId)), expected))
873
- return false;
874
- draft.set(sessionId, encodeInstructionSource(source));
875
- return true;
1218
+ await this.withSessionWriteLock(sessionId, async () => {
1219
+ if (!(await this.rowPresentHeld(sessionId)))
1220
+ return;
1221
+ await this.instructionSources.ensureLoaded();
1222
+ const committed = await this.instructionSources.commit((draft) => {
1223
+ if (!sameInstructionSourceBaseline(decodeInstructionSource(draft.get(sessionId)), expected))
1224
+ return false;
1225
+ draft.set(sessionId, encodeInstructionSource(source));
1226
+ return true;
1227
+ }, "best-effort");
1228
+ if (!committed)
1229
+ throw new Error("LocalSessionStore: instruction-source sidecar write failed — baseline not recorded");
876
1230
  });
877
- if (!committed)
878
- throw new Error("LocalSessionStore: instruction-source sidecar write failed — baseline not recorded");
879
1231
  }
880
1232
  async readSessionWorkspace(sessionId) {
1233
+ if (await this.rowDropping(sessionId))
1234
+ return undefined;
881
1235
  await this.workspaces.ensureLoaded();
882
1236
  return decodeSessionWorkspaceCell(this.workspaces.state.entries.get(sessionId) ?? null, sessionId);
883
1237
  }
884
1238
  async recordSessionWorkspace(sessionId, incarnation, workspace) {
885
1239
  const text = encodeSessionWorkspaceCell(workspace, JSON.stringify, sessionId);
886
1240
  await this.withSessionWriteLock(sessionId, async () => {
887
- if ((await this.metaOf(sessionId))?.incarnation !== incarnation)
1241
+ if ((await this.metaHeld(sessionId))?.incarnation !== incarnation)
888
1242
  return;
889
1243
  await this.workspaces.ensureLoaded();
890
1244
  const committed = await this.workspaces.commit((draft) => {
@@ -892,7 +1246,7 @@ export class LocalSessionStore {
892
1246
  return false;
893
1247
  draft.set(sessionId, text);
894
1248
  return true;
895
- });
1249
+ }, "best-effort");
896
1250
  if (!committed)
897
1251
  throw new SessionError("storage", `LocalSessionStore: workspace sidecar write failed for session ${sessionId} — its place was not recorded`);
898
1252
  });
@@ -943,10 +1297,8 @@ export class LocalSessionStore {
943
1297
  this.dirty = false;
944
1298
  const snapshot = Object.fromEntries(this.lastActivity);
945
1299
  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);
1300
+ ensureDir(dirname(path));
1301
+ await atomicWriteFileAsync(path, JSON.stringify(snapshot), { durability: "best-effort" });
950
1302
  }
951
1303
  catch (e) {
952
1304
  this.warnSidecar("write", e);
@@ -963,7 +1315,7 @@ export class LocalSessionStore {
963
1315
  if (now - this.lastWarnAt < 60_000)
964
1316
  return;
965
1317
  this.lastWarnAt = now;
966
- console.warn(`LocalSessionStore: ${which} sidecar ${op} failed (${SIDECAR_DEGRADES[which]} persistence degraded): ${e instanceof Error ? e.message : String(e)}`);
1318
+ writeLogText("stderr", `LocalSessionStore: ${which} sidecar ${op} failed (${SIDECAR_DEGRADES[which]} persistence degraded): ${e instanceof Error ? e.message : String(e)}`);
967
1319
  }
968
1320
  async acquire(sessionId, opts) {
969
1321
  if (!sessionId && this.declaredPlacement(opts) !== undefined)
@@ -992,6 +1344,9 @@ export class LocalSessionStore {
992
1344
  }
993
1345
  async load(sessionId, requireExisting = false, opts) {
994
1346
  return this.withSessionWriteLock(sessionId, async () => {
1347
+ await this.meta.ensureLoaded();
1348
+ if (this.cellStateOf(sessionId).state === "deleting")
1349
+ throw new SessionError("not_found", `session ${sessionId} not found (it is being deleted)`);
995
1350
  try {
996
1351
  return await this.openForItselfHeld(sessionId, opts);
997
1352
  }
@@ -1002,7 +1357,7 @@ export class LocalSessionStore {
1002
1357
  throw e;
1003
1358
  }
1004
1359
  await this.meta.ensureLoaded();
1005
- if (isForkCreatedCell(this.metaCellOf(sessionId)))
1360
+ if (isForkCreatedCell(this.liveCellOf(sessionId)))
1006
1361
  throw new SessionForkInProgressError(sessionId);
1007
1362
  const placement = this.declaredPlacement(opts);
1008
1363
  if (placement !== undefined)
@@ -1019,7 +1374,7 @@ export class LocalSessionStore {
1019
1374
  return this.handOut(sessionId, session);
1020
1375
  }
1021
1376
  async createPlacedHeld(sessionId, placement, opts) {
1022
- if (this.metaCellOf(sessionId) !== undefined) {
1377
+ if (this.liveCellOf(sessionId) !== undefined) {
1023
1378
  throw new SessionError("placement_refused", `session ${sessionId} is already registered as an ordinary session — refusing to create a subagent transcript on its id`);
1024
1379
  }
1025
1380
  const { owner: treeOwner, ...tuple } = placement;
@@ -1046,8 +1401,8 @@ export class LocalSessionStore {
1046
1401
  return this.handOut(sessionId, session);
1047
1402
  }
1048
1403
  async holdsThisPlacement(sessionId, opts) {
1049
- const probe = await this.placementProbe(sessionId);
1050
- if (probe.state === "unusable")
1404
+ const probe = await this.placementProbe(sessionId).then((answer) => answer, (fault) => ({ state: "storage", fault }));
1405
+ if (probe.state === "corrupt" || probe.state === "storage")
1051
1406
  return true;
1052
1407
  try {
1053
1408
  admitSessionPlacement(sessionId, probe.state === "placed" ? probe.record : undefined, opts, placementCapabilityOf(this));
@@ -1068,28 +1423,31 @@ export class LocalSessionStore {
1068
1423
  }
1069
1424
  }
1070
1425
  async register(sessionId, owner) {
1071
- await this.withSessionWriteLock(sessionId, async () => {
1426
+ await this.registerWithReceipt(sessionId, owner);
1427
+ }
1428
+ async registerWithReceipt(sessionId, owner) {
1429
+ return this.withSessionWriteLock(sessionId, async () => {
1072
1430
  await this.meta.ensureLoaded();
1073
- if (ownerRecorded(this.meta.state.entries.get(sessionId)))
1074
- return;
1431
+ if (this.cellStateOf(sessionId).state === "deleting")
1432
+ return {};
1433
+ if (this.liveCellOf(sessionId)?.owner !== undefined)
1434
+ return {};
1075
1435
  assertFirstOwnerWritable(sessionId, owner);
1076
1436
  const existed = await this.existsInRepo(sessionId);
1077
- if (!existed && isForkCreatedCell(this.metaCellOf(sessionId)))
1437
+ if (!existed && isForkCreatedCell(this.liveCellOf(sessionId)))
1078
1438
  throw new SessionForkInProgressError(sessionId);
1079
- const claim = existed && owner !== null && ownerClaimWindowOpen() && !(await this.isPlacedRow(sessionId));
1439
+ const born = mintSessionIncarnation();
1440
+ const first = existed && this.liveCellOf(sessionId) === undefined ? await this.firstCellIncarnationHeld(sessionId) : undefined;
1080
1441
  const wrote = await this.writeMeta(sessionId, (before) => {
1081
1442
  if (ownerRecorded(before))
1082
1443
  return undefined;
1083
- if (claim)
1084
- return before === undefined ? { incarnation: mintSessionIncarnation(), owner } : cellWith(before, { owner });
1085
1444
  if (!existed)
1086
- return { incarnation: mintSessionIncarnation(), owner, createdAt: rowBornNow() };
1087
- return before === undefined ? { incarnation: mintSessionIncarnation() } : undefined;
1445
+ return { incarnation: born, owner, createdAt: rowBornNow(), clientWrites: 0 };
1446
+ return before === undefined ? { incarnation: first ?? cellChangedUnderLock(sessionId) } : undefined;
1088
1447
  });
1089
1448
  if (!existed && wrote)
1090
1449
  await this.repo.create({ id: sessionId });
1091
- if (claim && wrote)
1092
- this.log.info("session_owner_claimed", { sessionId });
1450
+ return !existed && wrote ? { minted: { sessionId, owner, incarnation: born } } : {};
1093
1451
  });
1094
1452
  }
1095
1453
  async touch(sessionId) {
@@ -1101,8 +1459,10 @@ export class LocalSessionStore {
1101
1459
  await this.withSessionWriteLock(sessionId, async () => {
1102
1460
  if ((await this.placedRecordOrThrow(sessionId)) === undefined)
1103
1461
  return;
1462
+ const verdict = await judgeSessionWrite({ op: "delete", rootSessionId: sessionId }, { claim: () => this.runs.getActiveTaskId(sessionId), cell: async () => ({ role: "absent" }), deleting: async () => this.deletingTokenOf(sessionId) });
1463
+ if (verdict !== "proceed")
1464
+ throw verdict.refuse;
1104
1465
  await this.dropRowHeld(sessionId);
1105
- await this.repo.delete({ id: sessionId, createdAt: "" });
1106
1466
  });
1107
1467
  }
1108
1468
  async listSessions(opts) {
@@ -1110,28 +1470,31 @@ export class LocalSessionStore {
1110
1470
  }
1111
1471
  async listSessionRows(opts) {
1112
1472
  await this.loadRowSidecars();
1473
+ const hadLiveCell = new Set(this.liveCellIds());
1113
1474
  const rows = [];
1114
1475
  for (const header of await this.rowHeaders())
1115
1476
  rows.push(await this.listedRowOf(header));
1116
- const rollups = await this.runRollups();
1477
+ const rollups = await this.runRollups(opts.q);
1117
1478
  const all = [];
1118
1479
  for (const row of rows) {
1480
+ if (opts.q && !rollups.has(row.sessionId))
1481
+ continue;
1482
+ if (this.rowGoneSince(row.sessionId, hadLiveCell.has(row.sessionId)))
1483
+ continue;
1119
1484
  const owner = this.ownerCellOf(row.sessionId);
1120
1485
  if (opts.owner !== undefined && owner !== opts.owner && !(opts.includeUnowned === true && owner === null))
1121
1486
  continue;
1122
- const meta = this.metaCellOf(row.sessionId);
1487
+ const meta = this.liveCellOf(row.sessionId);
1123
1488
  const incarnation = meta === undefined ? undefined : incarnationFromCell(row.sessionId, meta.incarnation, "absent");
1124
- all.push({ row: await this.summaryOf(row, rollups), occupant: { owner, incarnation } });
1489
+ all.push({ row: this.summaryOf(row, rollups), occupant: { owner, incarnation } });
1125
1490
  }
1126
- const qlc = opts.q?.toLowerCase();
1127
- const filtered = qlc ? all.filter(({ row: r }) => r.objectivePreview?.toLowerCase().includes(qlc)) : all;
1128
- filtered.sort(({ row: a }, { row: b }) => a.lastActivityAt !== b.lastActivityAt
1491
+ all.sort(({ row: a }, { row: b }) => a.lastActivityAt !== b.lastActivityAt
1129
1492
  ? (a.lastActivityAt < b.lastActivityAt ? 1 : -1)
1130
1493
  : (a.sessionId < b.sessionId ? 1 : -1));
1131
1494
  const c = opts.cursor;
1132
1495
  const after = c
1133
- ? filtered.filter(({ row: r }) => r.lastActivityAt < c.lastActivityAt || (r.lastActivityAt === c.lastActivityAt && r.sessionId < c.sessionId))
1134
- : filtered;
1496
+ ? all.filter(({ row: r }) => r.lastActivityAt < c.lastActivityAt || (r.lastActivityAt === c.lastActivityAt && r.sessionId < c.sessionId))
1497
+ : all;
1135
1498
  return after.slice(0, opts.limit);
1136
1499
  }
1137
1500
  async loadRowSidecars() {
@@ -1141,58 +1504,43 @@ export class LocalSessionStore {
1141
1504
  await this.meta.ensureLoaded();
1142
1505
  }
1143
1506
  ownerCellOf(sessionId) {
1144
- const cell = this.meta.state.entries.get(sessionId);
1145
- return cell === undefined ? null : (readSessionMetaCell(cell).owner ?? null);
1507
+ return this.liveCellOf(sessionId)?.owner ?? null;
1146
1508
  }
1147
- async summaryOf(listed, rollups) {
1509
+ summaryOf(listed, rollups) {
1148
1510
  const { sessionId } = listed;
1149
- const firstActivityAt = rowCreatedAtOf(sessionId, this.metaCellOf(sessionId));
1511
+ const firstActivityAt = rowCreatedAtOf(sessionId, this.liveCellOf(sessionId));
1150
1512
  const owner = this.ownerCellOf(sessionId);
1151
1513
  const cells = this.rowCellsOf(sessionId);
1152
1514
  const rowTag = this.rowTagKeyOf(sessionId, owner, cells);
1153
1515
  const lastActivityAt = this.lastActivity.get(sessionId) ?? firstActivityAt;
1154
- const objectivePreview = "readable" in listed ? await this.previewOf(listed.readable) : null;
1155
1516
  return {
1156
1517
  sessionId,
1157
1518
  owner,
1158
1519
  lastActivityAt,
1159
1520
  firstActivityAt,
1160
- objectivePreview,
1161
- ...this.runProjectionOf(sessionId, rollups),
1521
+ ...this.runRollupOf(sessionId, rollups),
1162
1522
  title: cells.title,
1163
1523
  pinned: cells.pinned,
1164
1524
  ...rowTag,
1165
1525
  ...("unreadable" in listed ? { unreadable: sessionUnreadableOf(listed.unreadable) } : {}),
1526
+ ...this.listedLineageOf(sessionId),
1166
1527
  };
1167
1528
  }
1529
+ listedLineageOf(sessionId) {
1530
+ const lineage = forkLineageOfRow(sessionId, this.liveCellOf(sessionId)?.forkedFrom, undefined);
1531
+ return lineage.forkedFrom !== undefined ? { forkedFrom: lineage.forkedFrom } : {};
1532
+ }
1168
1533
  rowTagKeyOf(sessionId, owner, cells) {
1169
- const meta = this.metaCellOf(sessionId);
1534
+ const meta = this.liveCellOf(sessionId);
1170
1535
  if (meta === undefined)
1171
1536
  return {};
1172
1537
  return sessionRowTagKey({ sessionId, owner, incarnation: incarnationFromCell(sessionId, meta.incarnation, "absent"), cells });
1173
1538
  }
1174
- async runRollups() {
1175
- const rows = await this.runs.listSessions({ limit: Number.MAX_SAFE_INTEGER });
1176
- return new Map(rows.map((r) => [r.sessionId, { runCount: r.runCount, lastStatus: r.lastStatus, lastRunId: r.lastRunId, ...(r.lastTaskRunId !== undefined ? { lastTaskRunId: r.lastTaskRunId } : {}) }]));
1539
+ runRollups(q) {
1540
+ return this.runs.sessionRunRollups(q ? { q } : {});
1177
1541
  }
1178
- runProjectionOf(sessionId, rollups) {
1179
- return rollups.get(sessionId) ?? { runCount: 0, lastStatus: "", lastRunId: null };
1180
- }
1181
- async previewOf(meta) {
1182
- try {
1183
- const session = await this.repo.open({ id: meta.id, createdAt: meta.createdAt });
1184
- const entries = await session.getEntries();
1185
- for (let i = entries.length - 1; i >= 0; i--) {
1186
- const text = userText(entries[i]);
1187
- const preview = text === null ? "" : buildObjectivePreview(text, SESSION_OBJECTIVE_PREVIEW_CHARS);
1188
- if (preview !== "")
1189
- return preview;
1190
- }
1191
- return null;
1192
- }
1193
- catch {
1194
- return null;
1195
- }
1542
+ runRollupOf(sessionId, rollups) {
1543
+ return rollups.get(sessionId) ?? { runCount: 0, lastStatus: "", lastRunId: null, objectivePreview: null };
1196
1544
  }
1197
1545
  async fork(sourceId, owner, opts) {
1198
1546
  return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner, opts);
@@ -1205,6 +1553,9 @@ export class LocalSessionStore {
1205
1553
  return this.withSessionWriteLock(first, () => this.withSessionWriteLock(second, () => this.forkToHeld(sourceId, owner, target)));
1206
1554
  }
1207
1555
  async forkToHeld(sourceId, owner, target) {
1556
+ await this.meta.ensureLoaded();
1557
+ if (this.cellStateOf(sourceId).state === "deleting")
1558
+ return null;
1208
1559
  const sourceMeta = { id: sourceId, createdAt: "" };
1209
1560
  let source;
1210
1561
  try {
@@ -1217,15 +1568,25 @@ export class LocalSessionStore {
1217
1568
  }
1218
1569
  await this.assertArchiveAnonymous(sourceId);
1219
1570
  const forkOptions = await settledForkOptions(source);
1220
- const forkOwner = forkOwnerOf(owner, this.metaCellOf(sourceId)?.owner);
1221
- const existing = this.metaCellOf(target.id);
1571
+ const sourceCell = this.liveCellOf(sourceId);
1572
+ const forkOwner = forkOwnerOf(owner, sourceCell?.owner);
1573
+ const sourceToken = sourceCell === undefined ? undefined : incarnationFromCell(sourceId, sourceCell.incarnation, "absent");
1574
+ const lineage = forkRowCells({ ...sourceMeta, ...(sourceToken !== undefined ? { incarnation: sourceToken } : {}) });
1575
+ const existing = this.liveCellOf(target.id);
1222
1576
  const targetArchived = await this.existsInRepo(target.id);
1223
1577
  const placement = this.placements !== undefined ? target.placement : undefined;
1224
1578
  const copy = () => this.repo.fork(sourceMeta, { ...forkOptions, id: target.id, ...(placement !== undefined ? { placement } : {}) });
1225
1579
  if (existing === undefined && !targetArchived) {
1226
1580
  const created = await this.writeMeta(target.id, (before) => before !== undefined
1227
1581
  ? undefined
1228
- : { incarnation: mintSessionIncarnation(), ...(forkOwner !== undefined ? { owner: forkOwner } : {}), ...(target.key !== undefined ? { forkKey: target.key } : {}), createdAt: rowBornNow(), forkedFrom: sourceId });
1582
+ : {
1583
+ incarnation: mintSessionIncarnation(),
1584
+ ...(forkOwner !== undefined ? { owner: forkOwner } : {}),
1585
+ ...(target.key !== undefined ? { forkKey: target.key } : {}),
1586
+ createdAt: rowBornNow(),
1587
+ ...(lineage.forkedFrom !== undefined ? { forkedFrom: lineage.forkedFrom } : {}),
1588
+ ...(lineage.forkedFromIncarnation !== undefined ? { forkedFromIncarnation: lineage.forkedFromIncarnation } : {}),
1589
+ });
1229
1590
  if (!created)
1230
1591
  throw new SessionError("storage", `LocalSessionStore: fork target ${target.id} gained a session-meta cell under its own write lock — refusing to guess`);
1231
1592
  let forked;
@@ -1241,8 +1602,12 @@ export class LocalSessionStore {
1241
1602
  return { outcome: "created", id: target.id };
1242
1603
  }
1243
1604
  const out = classifyForkTarget(target, { owner: existing?.owner, forkKey: existing?.forkKey }, forkOwner);
1244
- if (out.outcome === "replayed" && !targetArchived)
1605
+ if (out.outcome === "replayed" && isForkCreatedCell(existing) && !targetArchived) {
1606
+ const recorded = existing?.forkedFromIncarnation;
1607
+ if (recorded !== undefined && recorded !== lineage.forkedFromIncarnation)
1608
+ return null;
1245
1609
  await copy();
1610
+ }
1246
1611
  return out;
1247
1612
  }
1248
1613
  async admitForkedPlacement(sessionId, forked, placement) {
@@ -1263,6 +1628,9 @@ export class LocalSessionStore {
1263
1628
  async exportEntries(sessionId) {
1264
1629
  if (!this.repo.exportEntries)
1265
1630
  return null;
1631
+ await this.meta.ensureLoaded();
1632
+ if (this.cellStateOf(sessionId).state !== "live" && this.cellStateOf(sessionId).state !== "none")
1633
+ return null;
1266
1634
  try {
1267
1635
  return await this.repo.exportEntries(sessionId);
1268
1636
  }
@@ -1305,11 +1673,9 @@ export class LocalSessionStore {
1305
1673
  if (landing === "replace")
1306
1674
  await this.assertNotPlacedHeld(sessionId);
1307
1675
  validateEntriesForImport(entries);
1308
- 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);
1676
+ const current = this.liveCellOf(sessionId);
1677
+ if (landing !== "judged")
1678
+ await this.admitImportHeld(sessionId, current);
1313
1679
  if (!(await this.importMayLand(sessionId, current, owner, landing)))
1314
1680
  throw importRowRefused(sessionId, landing);
1315
1681
  const prior = await this.landImportCell(sessionId, owner);
@@ -1317,8 +1683,8 @@ export class LocalSessionStore {
1317
1683
  case "create":
1318
1684
  case "judged":
1319
1685
  try {
1320
- const archive = (await this.archiveHeadOf(sessionId)) ?? null;
1321
- await this.repo.importEntries(sessionId, undefined, entries, { expect: { row: archive } });
1686
+ const expectRow = landing === "create" ? null : ((await this.archiveHeadOf(sessionId)) ?? null);
1687
+ await this.repo.importEntries(sessionId, undefined, entries, { expect: { row: expectRow } });
1322
1688
  }
1323
1689
  catch (err) {
1324
1690
  await this.withdrawImportCell(sessionId, prior, err);
@@ -1348,6 +1714,15 @@ export class LocalSessionStore {
1348
1714
  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
1715
  }
1350
1716
  }
1717
+ async admitImportHeld(sessionId, current) {
1718
+ const verdict = await judgeSessionWrite({ op: "import", rootSessionId: sessionId }, {
1719
+ claim: () => this.runs.getActiveTaskId(sessionId),
1720
+ cell: async () => placementCellOf(current, await this.forkingOf(sessionId, current)),
1721
+ deleting: async () => this.deletingTokenOf(sessionId),
1722
+ });
1723
+ if (verdict !== "proceed")
1724
+ throw verdict.refuse;
1725
+ }
1351
1726
  async importMayLand(sessionId, current, owner, landing) {
1352
1727
  switch (landing) {
1353
1728
  case "create":
@@ -1369,7 +1744,7 @@ export class LocalSessionStore {
1369
1744
  const cells = carriedRowCells(sessionId, before === undefined ? undefined : readSessionMetaCell(before));
1370
1745
  const base = before === undefined ? { createdAt: rowBornNow() } : cellObjectOf(before);
1371
1746
  const { forkKey: _carriedByTheRule, ...kept } = base;
1372
- return { ...kept, incarnation: cells.incarnation, owner, execution: MIRROR_CELL, ...(cells.forkKey !== null ? { forkKey: cells.forkKey } : {}) };
1747
+ return { ...kept, incarnation: cells.incarnation, owner, execution: MIRROR_CELL, ...(cells.forkKey !== null ? { forkKey: cells.forkKey } : {}), clientWrites: (kept.clientWrites ?? 0) + 1 };
1373
1748
  });
1374
1749
  return prior;
1375
1750
  }
@@ -1401,11 +1776,9 @@ export class LocalSessionStore {
1401
1776
  }
1402
1777
  async commitStagedImport(sessionId, owner, staged, opts) {
1403
1778
  return this.withImportLock(sessionId, owner, async (importer) => {
1404
- const current = await this.metaOf(sessionId);
1779
+ const current = await this.metaHeld(sessionId);
1405
1780
  if (!ownerCellIs(opts.expectedOwner, current === undefined ? undefined : (current.owner ?? null)))
1406
1781
  throw new StagedCommitOwnerChangedError(sessionId);
1407
- if (await this.forkingOf(sessionId, current))
1408
- throw new SessionForkInProgressError(sessionId);
1409
1782
  await this.assertNotPlacedHeld(sessionId);
1410
1783
  const dstEntries = await this.exportEntries(sessionId);
1411
1784
  const rel = classifySyncRelationshipByIds(staged.map((e) => e.id), dstEntries === null ? null : dstEntries.map((e) => e.id));
@@ -1421,8 +1794,7 @@ export class LocalSessionStore {
1421
1794
  if (diverged)
1422
1795
  throw new SyncConflictError(sessionId, contentForkRelation(diverged, rel.newEntryIds));
1423
1796
  }
1424
- if (current?.execution?.role === "writer")
1425
- throw new SessionWriterPresentError(sessionId, current.execution.lane);
1797
+ await this.admitImportHeld(sessionId, current);
1426
1798
  if (rel.relation !== "identical" || !contentEqual) {
1427
1799
  await this.importHeld(sessionId, importer, staged, "judged");
1428
1800
  }
@@ -1430,46 +1802,326 @@ export class LocalSessionStore {
1430
1802
  await this.landImportCell(sessionId, importer);
1431
1803
  await this.touch(sessionId);
1432
1804
  }
1433
- const meta = await this.metaOf(sessionId);
1805
+ const meta = await this.metaHeld(sessionId);
1434
1806
  if (meta === undefined)
1435
1807
  throw new SessionError("storage", `LocalSessionStore: staged commit for session ${sessionId} left no session face — nothing was settled`);
1436
1808
  const face = sessionFaceFromRow(sessionId, meta.owner ?? null, meta.incarnation);
1437
1809
  return { relation: rel.relation, face };
1438
1810
  });
1439
1811
  }
1440
- async deleteSession(sessionId, owner) {
1441
- return this.withSessionWriteLock(sessionId, () => this.deleteSessionHeld(sessionId, owner));
1812
+ async deleteSession(sessionId, owner, token, legs) {
1813
+ return this.withSessionWriteLock(sessionId, (dropped) => this.deleteSessionHeld(sessionId, owner, token, legs, dropped));
1442
1814
  }
1443
- async deleteSessionHeld(sessionId, owner) {
1444
- const current = await this.metaOf(sessionId);
1445
- if (current === undefined || !ownerEq(current.owner ?? null, owner))
1446
- return false;
1815
+ async deleteSessionHeld(sessionId, owner, token, legs, dropped) {
1816
+ if (dropped !== undefined)
1817
+ return undefined;
1818
+ await this.meta.ensureLoaded();
1819
+ const cell = this.cellStateOf(sessionId);
1820
+ if (cell.state === "none" || !ownerEq(cell.meta.owner ?? null, owner))
1821
+ return undefined;
1822
+ const verdict = await judgeSessionWrite({ op: "erase", rootSessionId: sessionId, token }, { claim: () => this.runs.getActiveTaskId(sessionId), cell: async () => ({ role: "absent" }), deleting: async () => this.deletingTokenOf(sessionId) });
1823
+ if (verdict !== "proceed")
1824
+ throw verdict.refuse;
1825
+ const occupant = { owner: cell.meta.owner ?? null, incarnation: cell.meta.incarnation };
1826
+ await legs(occupant);
1827
+ await this.dropRowHeld(sessionId);
1828
+ return occupant;
1829
+ }
1830
+ deletingTokenOf(sessionId) {
1831
+ const cell = this.cellStateOf(sessionId);
1832
+ return cell.state === "none" ? null : (cell.meta.deleting ?? null);
1833
+ }
1834
+ sessionDeletion = {
1835
+ fence: (sessionId, owner, opts) => this.withSessionWriteLock(sessionId, (dropped) => this.fenceDeletionHeld(sessionId, owner, opts, dropped)),
1836
+ holds: async (sessionId, token) => {
1837
+ await this.meta.ensureLoaded();
1838
+ return this.deletingTokenOf(sessionId) === token;
1839
+ },
1840
+ pending: async () => {
1841
+ await this.meta.ensureLoaded();
1842
+ return this.deletingIds().map((sessionId) => ({ sessionId, owner: this.deletionOwnerOf(sessionId), token: this.deletingTokenOf(sessionId) }));
1843
+ },
1844
+ };
1845
+ deletionOwnerOf(sessionId) {
1846
+ const cell = this.cellStateOf(sessionId);
1847
+ return cell.state === "none" ? null : (cell.meta.owner ?? null);
1848
+ }
1849
+ async fenceDeletionHeld(sessionId, owner, opts, dropped) {
1850
+ if (dropped !== undefined)
1851
+ return undefined;
1852
+ await this.meta.ensureLoaded();
1853
+ const state = this.cellStateOf(sessionId);
1854
+ let current;
1855
+ if (state.state === "deleting")
1856
+ current = state.meta;
1857
+ else {
1858
+ if (opts.expect !== undefined)
1859
+ return undefined;
1860
+ const seen = await this.observeRow(sessionId);
1861
+ if (seen.row === "absent")
1862
+ return undefined;
1863
+ current = seen.row === "live" ? seen.meta : undefined;
1864
+ }
1865
+ if (!ownerEq(current?.owner ?? null, owner))
1866
+ return undefined;
1867
+ if (state.state === "deleting") {
1868
+ const held = state.meta.deleting;
1869
+ if (opts.expect !== undefined && held !== opts.expect)
1870
+ return undefined;
1871
+ if (opts.holder(held) === "running")
1872
+ return { inProgress: true };
1873
+ }
1874
+ else {
1875
+ const verdict = await judgeSessionWrite({ op: "delete", rootSessionId: sessionId }, { claim: () => this.runs.getActiveTaskId(sessionId), cell: async () => ({ role: "absent" }), deleting: async () => this.deletingTokenOf(sessionId) });
1876
+ if (verdict !== "proceed")
1877
+ throw verdict.refuse;
1878
+ }
1879
+ const token = uuidv7();
1880
+ let incarnation;
1881
+ const committed = await this.meta.commit((draft) => {
1882
+ const before = draft.get(sessionId);
1883
+ const base = before === undefined ? { incarnation: mintSessionIncarnation() } : cellObjectOf(before);
1884
+ incarnation = before === undefined ? undefined : base.incarnation;
1885
+ draft.set(sessionId, { ...base, deleting: token });
1886
+ return true;
1887
+ }, "required");
1888
+ if (!committed)
1889
+ throw new SessionError("storage", `LocalSessionStore: the deletion fence of session ${sessionId} could not be made durable — nothing of the session was removed; retry the delete`);
1890
+ return { token, incarnation };
1891
+ }
1892
+ async bumpClientWritesHeld(sessionId) {
1893
+ await this.meta.ensureLoaded();
1894
+ if (this.cellStateOf(sessionId).state === "deleting")
1895
+ throw new SessionDeletingError(sessionId);
1896
+ if (this.liveCellOf(sessionId) === undefined)
1897
+ return;
1898
+ const committed = await this.meta.commit((draft) => {
1899
+ const before = draft.get(sessionId);
1900
+ if (before === undefined)
1901
+ return false;
1902
+ const cell = readSessionMetaCell(before);
1903
+ if (cell.dropping)
1904
+ throw new Error(`LocalSessionStore: session ${sessionId}'s cell carries the drop mark while a client write reached it — every section finishes a pending drop first; refusing to write`);
1905
+ draft.set(sessionId, { ...cellObjectOf(before), clientWrites: (cell.clientWrites ?? 0) + 1 });
1906
+ return true;
1907
+ }, "required");
1908
+ if (!committed)
1909
+ throw new SessionError("storage", `LocalSessionStore: the client-write count of session ${sessionId} could not be made durable — the write was not made; retry it`);
1910
+ }
1911
+ async withClientWrite(sessionId, write) {
1912
+ return this.withSessionWriteLock(sessionId, async () => {
1913
+ let open = true;
1914
+ try {
1915
+ return await write(async () => {
1916
+ if (!open)
1917
+ throw new Error(`LocalSessionStore.withClientWrite: session ${sessionId}'s client-write count was taken after its lock section ended`);
1918
+ await this.bumpClientWritesHeld(sessionId);
1919
+ });
1920
+ }
1921
+ finally {
1922
+ open = false;
1923
+ }
1924
+ });
1925
+ }
1926
+ async withdrawBirth(birth, ownTaskId) {
1927
+ const runs = this.runs;
1928
+ return this.withSessionWriteLock(birth.sessionId, async (dropped) => {
1929
+ const sessionId = birth.sessionId;
1930
+ const kept = (word) => ({ withdrawn: false, kept: word });
1931
+ if (dropped !== undefined)
1932
+ return kept("absent");
1933
+ await this.meta.ensureLoaded();
1934
+ if (this.cellStateOf(sessionId).state === "deleting")
1935
+ return kept("claim");
1936
+ const seen = await this.observeRow(sessionId);
1937
+ if (seen.row === "absent")
1938
+ return kept("absent");
1939
+ if (seen.row !== "live")
1940
+ return kept("incarnation");
1941
+ const cell = seen.meta;
1942
+ if (cell.incarnation !== birth.incarnation || cell.owner === undefined || cell.owner !== birth.owner)
1943
+ return kept("incarnation");
1944
+ if (cell.clientWrites === undefined)
1945
+ return kept("upgrade_pending");
1946
+ if (await this.archiveHasContentHeld(sessionId))
1947
+ return kept("content");
1948
+ const verdict = await judgeSessionWrite({ op: "delete", rootSessionId: sessionId }, { claim: () => this.runs.getActiveTaskId(sessionId), cell: async () => placementCellOf(cell, false), deleting: async () => this.deletingTokenOf(sessionId) });
1949
+ if (verdict !== "proceed")
1950
+ return kept("claim");
1951
+ if (cell.clientWrites > 0)
1952
+ return kept("client_writes");
1953
+ if (typeof runs.withdrawRunsOf !== "function") {
1954
+ throw new Error(`LocalSessionStore.withdrawBirth: the run store wired into this session store cannot answer the withdraw's run half (withdrawRunsOf) — cannot tell whether session ${sessionId} has other runs; refusing to guess`);
1955
+ }
1956
+ const runsVerdict = runs.withdrawRunsOf.call(this.runs, sessionId, ownTaskId);
1957
+ if (runsVerdict !== "proceed")
1958
+ return kept(runsVerdict.keep);
1959
+ this.withdrawCheckpointCtx?.(sessionId);
1960
+ await this.dropRowHeld(sessionId);
1961
+ return { withdrawn: true };
1962
+ });
1963
+ }
1964
+ async archiveHasContentHeld(sessionId) {
1965
+ if (!this.repo.exportEntries)
1966
+ return true;
1967
+ let entries;
1447
1968
  try {
1448
- await this.repo.delete({ id: sessionId, createdAt: "" });
1969
+ entries = await this.repo.exportEntries(sessionId);
1449
1970
  }
1450
1971
  catch (e) {
1451
- if (!isNotFound(e))
1452
- throw e;
1972
+ if (isNotFound(e))
1973
+ return false;
1974
+ throw e;
1453
1975
  }
1454
- await this.dropRowHeld(sessionId);
1455
- return true;
1976
+ return entries.some((e) => !entryTypeWithdrawable(e.type));
1456
1977
  }
1457
1978
  async dropRowHeld(sessionId) {
1458
- this.rowsDropped.add(sessionId);
1979
+ await this.meta.ensureLoaded();
1980
+ let marked;
1981
+ const s0 = await this.meta.commit((draft) => {
1982
+ const before = draft.get(sessionId);
1983
+ if (before !== undefined && readSessionMetaCell(before).dropping) {
1984
+ marked = cellObjectOf(before);
1985
+ return false;
1986
+ }
1987
+ marked = { ...(before === undefined ? { incarnation: mintSessionIncarnation() } : cellObjectOf(before)), dropping: true };
1988
+ draft.set(sessionId, marked);
1989
+ return true;
1990
+ }, "required");
1991
+ if (!s0)
1992
+ throw new SessionError("storage", `LocalSessionStore: the drop mark of session ${sessionId} could not be made durable — nothing of the session was removed; retry the delete`);
1459
1993
  for (const [map, which] of [[this.titles, "title"], [this.pinned, "pinned"], [this.instructionSources, "instruction-source"], [this.workspaces, "workspace"]]) {
1460
1994
  await map.ensureLoaded();
1461
- if (!(await map.deleteKey(sessionId)))
1462
- throw new SessionError("storage", `LocalSessionStore: ${which} sidecar write failed while deleting session ${sessionId} — its ${which} is still on disk; retry the delete`);
1995
+ if (!(await map.deleteKey(sessionId, "required")))
1996
+ throw new SessionError("storage", `LocalSessionStore: ${which} sidecar write failed while deleting session ${sessionId} — the session already reads as deleted; retry the delete (or restart) to finish it`);
1463
1997
  }
1464
1998
  await this.ensureLoaded();
1465
1999
  this.lastActivity.delete(sessionId);
1466
2000
  this.scheduleFlush();
1467
- await this.dropMeta(sessionId);
2001
+ try {
2002
+ await this.repo.delete({ id: sessionId, createdAt: "" });
2003
+ }
2004
+ catch (e) {
2005
+ if (!isNotFound(e))
2006
+ throw e;
2007
+ }
2008
+ this.syncArchiveDirectory();
2009
+ if (!(await this.meta.deleteKey(sessionId, "required")))
2010
+ throw new SessionError("storage", `LocalSessionStore: session-meta sidecar write failed while deleting session ${sessionId} — the session already reads as deleted; retry the delete (or restart) to finish it`);
2011
+ return readSessionMetaCell(marked);
1468
2012
  }
1469
2013
  syncArchiveDirectory() {
1470
- if (this.dataRoot === undefined)
2014
+ if (this.dataRoot === undefined || !(this.repo instanceof FileSessionRepo) || IS_WIN32)
1471
2015
  return;
1472
- fsyncPaths(join(this.dataRoot, "sessions"));
2016
+ fsyncPaths(join(this.dataRoot, ARCHIVE_DIR));
2017
+ }
2018
+ async finishPendingDropHeld(sessionId) {
2019
+ await this.meta.ensureLoaded();
2020
+ if (this.cellStateOf(sessionId).state !== "dropping")
2021
+ return undefined;
2022
+ return this.dropRowHeld(sessionId);
2023
+ }
2024
+ async finishPendingDrops() {
2025
+ await this.meta.ensureLoaded();
2026
+ for (const sessionId of this.droppingIds()) {
2027
+ try {
2028
+ await this.withSessionWriteLock(sessionId, async () => undefined);
2029
+ }
2030
+ catch (e) {
2031
+ this.log.warn("session_drop_unfinished", {
2032
+ sessionId,
2033
+ error: e instanceof Error ? e.message : String(e),
2034
+ effect: "the session reads as deleted everywhere; its remaining bytes are removed the next time anything touches this session id, or at the next start",
2035
+ });
2036
+ }
2037
+ }
2038
+ }
2039
+ async clearShellSentinelOwnersM48() {
2040
+ await this.meta.ensureLoaded();
2041
+ let cleared = 0;
2042
+ const committed = await this.meta.commit((draft) => {
2043
+ cleared = 0;
2044
+ for (const sessionId of [...draft.keys()]) {
2045
+ const cell = draft.get(sessionId);
2046
+ if (cell === undefined || typeof cell === "string" || cell.owner !== SHELL_SENTINEL_OWNER_M48 || readSessionMetaCell(cell).dropping)
2047
+ continue;
2048
+ draft.set(sessionId, { ...cell, owner: null });
2049
+ cleared += 1;
2050
+ }
2051
+ return cleared > 0;
2052
+ }, "best-effort");
2053
+ if (!committed)
2054
+ throw new SessionError("storage", "LocalSessionStore: the one-time migration M48 could not write the session-meta cells it cleared of the shell sentinel owner durably — nothing was changed");
2055
+ return cleared;
2056
+ }
2057
+ async adoptArchiveTokensM47() {
2058
+ const root = this.dataRoot;
2059
+ if (root === undefined)
2060
+ return { adopted: 0, minted: 0 };
2061
+ const owed = archiveTokenAdoptionPendingPresent(root);
2062
+ const rows = await this.rowHeaders();
2063
+ let adopted = 0;
2064
+ let minted = 0;
2065
+ const committed = await this.meta.commit((draft) => {
2066
+ adopted = 0;
2067
+ minted = 0;
2068
+ for (const row of rows) {
2069
+ if (!("header" in row) || draft.get(row.sessionId) !== undefined)
2070
+ continue;
2071
+ const first = firstCellIncarnationOf(row.sessionId, row.header.incarnation);
2072
+ const facts = archivedRowFactsOf(row.header);
2073
+ draft.set(row.sessionId, { incarnation: first.incarnation, createdAt: facts.createdAt, ...(facts.forkedFrom !== undefined ? { forkedFrom: facts.forkedFrom } : {}) });
2074
+ if (first.adopted)
2075
+ adopted += 1;
2076
+ else
2077
+ minted += 1;
2078
+ }
2079
+ return adopted + minted > 0;
2080
+ }, owed ? "required" : "best-effort");
2081
+ if (!committed)
2082
+ throw new SessionError("storage", "LocalSessionStore: the one-time migration M47 could not write the session-meta cells it adopted into the sidecar durably — the adoption stays owed");
2083
+ if (owed) {
2084
+ await this.assertArchiveTokensAdoptedM47(root);
2085
+ dischargeArchiveTokenAdoptionPending(root);
2086
+ }
2087
+ return { adopted, minted };
2088
+ }
2089
+ async assertArchiveTokensAdoptedM47(root) {
2090
+ let left = 0;
2091
+ for (const sessionId of archiveIdsWithoutCellSync(root))
2092
+ if ((await this.adoptionScopeOf(sessionId)) === "in")
2093
+ left += 1;
2094
+ if (left > 0) {
2095
+ throw new SessionError("storage", `LocalSessionStore: the one-time migration M47 did not adopt ${left} session archive(s) without a session-meta cell whose header it can read (the session listing it read did not list them) — refusing to start. ` +
2096
+ `${ARCHIVE_TOKEN_ADOPTION_PENDING_FILE} stays in the data root; the next start of a release carrying this migration tries again.`);
2097
+ }
2098
+ }
2099
+ async adoptionScopeOf(sessionId) {
2100
+ let placed;
2101
+ try {
2102
+ placed = await this.recordedPlacementOf(sessionId);
2103
+ }
2104
+ catch (fault) {
2105
+ return this.skippedFromAdoption(sessionId, fault);
2106
+ }
2107
+ if (placed !== undefined)
2108
+ return "out";
2109
+ try {
2110
+ return (await this.archiveHeadOf(sessionId)) === undefined ? "out" : "in";
2111
+ }
2112
+ catch (fault) {
2113
+ if (fault instanceof SessionError && archiveFaultWord(fault.code) === "corrupt_session")
2114
+ return this.skippedFromAdoption(sessionId, fault);
2115
+ return "in";
2116
+ }
2117
+ }
2118
+ skippedFromAdoption(sessionId, fault) {
2119
+ this.log.warn("archive_token_adoption_skipped", {
2120
+ sessionId,
2121
+ error: fault instanceof Error ? fault.message : String(fault),
2122
+ effect: "this session archive's header cannot be read, so the one-time migration M47 cannot keep its occupant token; if it becomes readable only after the migration's window, its first read mints a new token and memory records kept under the old one stop applying",
2123
+ });
2124
+ return "out";
1473
2125
  }
1474
2126
  archiveSnapshotDir() {
1475
2127
  return this.dataRoot === undefined ? undefined : join(this.dataRoot, LOCAL_ARCHIVE_SNAPSHOT_DIR);
@@ -1538,10 +2190,10 @@ export class LocalSessionStore {
1538
2190
  catch (e) {
1539
2191
  throw refuse("restoring the archive from the snapshot failed", e);
1540
2192
  }
1541
- await this.fillRowFacts(id, record.rowFacts);
2193
+ await this.fillRowFacts(id, record.rowFacts, record.archiveIncarnation);
1542
2194
  }
1543
2195
  else if (head.owner === null) {
1544
- await this.fillRowFacts(id, record.rowFacts);
2196
+ await this.fillRowFacts(id, record.rowFacts, record.archiveIncarnation);
1545
2197
  }
1546
2198
  else if (head.incarnation !== (record.archiveIncarnation ?? undefined)) {
1547
2199
  throw refuse("the session's archive is a different generation from the one the snapshot was taken of");
@@ -1550,8 +2202,8 @@ export class LocalSessionStore {
1550
2202
  fsyncPaths(dir);
1551
2203
  });
1552
2204
  }
1553
- async fillRowFacts(sessionId, facts) {
1554
- await this.writeMeta(sessionId, (before) => rowFactsFilled(before, facts));
2205
+ async fillRowFacts(sessionId, facts, archiveToken) {
2206
+ await this.writeMeta(sessionId, (before) => rowFactsFilled(sessionId, before, facts, archiveToken));
1555
2207
  }
1556
2208
  async migrateLocalArchives() {
1557
2209
  const dir = this.archiveSnapshotDir();
@@ -1576,20 +2228,20 @@ export class LocalSessionStore {
1576
2228
  const committed = await this.meta.commit((draft) => {
1577
2229
  rowFactsBackfilled = 0;
1578
2230
  for (const row of listed) {
1579
- const next = rowFactsFilled(draft.get(row.id), archivedRowFactsOf(row));
2231
+ const next = rowFactsFilled(row.id, draft.get(row.id), archivedRowFactsOf(row), row.incarnation);
1580
2232
  if (next === undefined)
1581
2233
  continue;
1582
2234
  draft.set(row.id, next);
1583
2235
  rowFactsBackfilled += 1;
1584
2236
  }
1585
2237
  return rowFactsBackfilled > 0;
1586
- });
2238
+ }, "best-effort");
1587
2239
  if (!committed)
1588
2240
  throw new SessionError("storage", "LocalSessionStore: the one-time migration M33 could not write the row facts it backfilled into the session-meta sidecar — no archive was touched");
1589
2241
  this.meta.syncToDisk();
1590
2242
  let anonymized = 0;
1591
2243
  if (listed.some((row) => (row.owner ?? null) !== null)) {
1592
- mkdirSync(dir, { recursive: true, mode: 0o700 });
2244
+ ensureDir(dir);
1593
2245
  fsyncPaths(dir, dirname(dir));
1594
2246
  }
1595
2247
  const keptStamped = [];
@@ -1641,7 +2293,7 @@ export class LocalSessionStore {
1641
2293
  }
1642
2294
  if (!sameLog(validated, row.entries))
1643
2295
  return { outcome: "kept", reason: "normalizes" };
1644
- const cell = this.metaCellOf(sessionId);
2296
+ const cell = this.liveCellOf(sessionId);
1645
2297
  if (cell?.createdAt === undefined)
1646
2298
  throw new Error(`LocalSessionStore: the one-time migration M33 reached session ${sessionId}'s archive before its row facts were backfilled — refusing to rewrite it`);
1647
2299
  const record = {