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