@sema-agent/server 7.149.0 → 7.151.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 (441) hide show
  1. package/MIGRATION.md +307 -9
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +92 -40
  5. package/deploy/sema-up/README.md +7 -1
  6. package/deploy/sema-up/chart/Chart.yaml +1 -1
  7. package/deploy/sema-up/chart/templates/NOTES.txt +5 -0
  8. package/deploy/sema-up/chart/templates/_helpers.tpl +15 -16
  9. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -3
  10. package/deploy/sema-up/chart/templates/server.yaml +5 -3
  11. package/deploy/sema-up/chart/values-full-single.yaml +3 -1
  12. package/deploy/sema-up/chart/values.yaml +8 -1
  13. package/deploy/sema-up/cluster-up.sh +1 -0
  14. package/deploy/sema-up/kube-up.sh +1 -1
  15. package/deploy/sema-up/lib/flagshape.sh +1 -1
  16. package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
  17. package/deploy/sema-up/lib/helm-values-kube.py +2 -0
  18. package/dist/adoption/import.js +3 -3
  19. package/dist/approval-ask-machine.d.ts +1 -1
  20. package/dist/approval-card.d.ts +3 -3
  21. package/dist/approval-gate-currency.d.ts +2 -2
  22. package/dist/audit.d.ts +1 -1
  23. package/dist/audit.js +2 -2
  24. package/dist/auth-bridge.d.ts +20 -0
  25. package/dist/auth-bridge.js +9 -0
  26. package/dist/background-session-reap.d.ts +2 -2
  27. package/dist/bake-runner/main.d.ts +4 -2
  28. package/dist/bind-host.d.ts +2 -3
  29. package/dist/bind-host.js +2 -4
  30. package/dist/boot/budget-tracing.js +1 -1
  31. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
  32. package/dist/boot/deferred-sandbox-path-env.js +1 -2
  33. package/dist/boot/engine-lease.d.ts +5 -0
  34. package/dist/boot/engine-lease.js +1 -0
  35. package/dist/boot/execution-env.d.ts +8 -14
  36. package/dist/boot/execution-env.js +54 -17
  37. package/dist/boot/execution-lane.js +32 -5
  38. package/dist/boot/late-binds.d.ts +8 -4
  39. package/dist/boot/late-binds.js +7 -1
  40. package/dist/boot/memory-consolidation.d.ts +3 -2
  41. package/dist/boot/memory-consolidation.js +6 -6
  42. package/dist/boot/parked-revive-gate.d.ts +5 -5
  43. package/dist/boot/parked-revive-gate.js +2 -3
  44. package/dist/boot/reapers.d.ts +5 -0
  45. package/dist/boot/reapers.js +12 -1
  46. package/dist/boot/resolve-spec.d.ts +7 -8
  47. package/dist/boot/resolve-spec.js +80 -35
  48. package/dist/boot/retention-lane.d.ts +1 -1
  49. package/dist/boot/runner-deps.d.ts +9 -2
  50. package/dist/boot/runner-deps.js +3 -3
  51. package/dist/boot/session-age-out.d.ts +36 -0
  52. package/dist/boot/session-age-out.js +101 -0
  53. package/dist/boot/session-faces.d.ts +5 -14
  54. package/dist/boot/session-faces.js +28 -24
  55. package/dist/boot/stage-01-config.js +2 -0
  56. package/dist/boot/stage-03-store-backend.js +2 -2
  57. package/dist/boot/stage-05-execution-env.d.ts +5 -3
  58. package/dist/boot/stage-05-execution-env.js +2 -2
  59. package/dist/boot/stage-06-runners.d.ts +7 -4
  60. package/dist/boot/stage-06-runners.js +16 -8
  61. package/dist/boot/stage-07-capability-layer.d.ts +7 -4
  62. package/dist/boot/stage-07-capability-layer.js +3 -2
  63. package/dist/boot/stage-08-reapers.d.ts +7 -4
  64. package/dist/boot/stage-08-reapers.js +4 -2
  65. package/dist/boot/stage-09-leader.d.ts +11 -5
  66. package/dist/boot/stage-09-leader.js +12 -3
  67. package/dist/boot/stage-10-http-server.d.ts +44 -6
  68. package/dist/boot/stage-10-http-server.js +12 -10
  69. package/dist/boot/web-admin.d.ts +1 -1
  70. package/dist/boot/web-admin.js +3 -1
  71. package/dist/boot/workflow-orchestration.d.ts +7 -2
  72. package/dist/boot/workflow-orchestration.js +24 -7
  73. package/dist/bounded-rotation.d.ts +29 -0
  74. package/dist/bounded-rotation.js +24 -0
  75. package/dist/capability-facts.d.ts +84 -66
  76. package/dist/capability-facts.js +58 -41
  77. package/dist/center-credential.d.ts +25 -6
  78. package/dist/center-credential.js +25 -5
  79. package/dist/config-catalog.d.ts +26 -7
  80. package/dist/config-catalog.js +15 -12
  81. package/dist/config-invariants.d.ts +2 -1
  82. package/dist/config-invariants.js +4 -4
  83. package/dist/config-types.d.ts +22 -15
  84. package/dist/config.d.ts +51 -2
  85. package/dist/config.js +35 -24
  86. package/dist/deployment-governance.d.ts +7 -28
  87. package/dist/deployment-governance.js +1 -14
  88. package/dist/deployment-shape.d.ts +9 -1
  89. package/dist/deployment-shape.js +2 -1
  90. package/dist/device-protocol.d.ts +5 -4
  91. package/dist/device-protocol.js +1 -1
  92. package/dist/device-ws-hub.d.ts +1 -0
  93. package/dist/device-ws-hub.js +66 -29
  94. package/dist/device-ws-protocol.d.ts +79 -12
  95. package/dist/device-ws-protocol.js +64 -26
  96. package/dist/engine-identity.d.ts +23 -5
  97. package/dist/engine-identity.js +13 -0
  98. package/dist/entry-mode-clamp-notice.d.ts +2 -1
  99. package/dist/execution-env-refusal.d.ts +23 -0
  100. package/dist/execution-env-refusal.js +5 -0
  101. package/dist/execution-lane-caps.d.ts +3 -1
  102. package/dist/field-conflict.d.ts +3 -3
  103. package/dist/file-history-capture.d.ts +11 -4
  104. package/dist/file-history-capture.js +9 -6
  105. package/dist/file-history-disclosure.d.ts +7 -5
  106. package/dist/file-history-disclosure.js +7 -5
  107. package/dist/fleet/drain-entry.d.ts +4 -0
  108. package/dist/fleet/engine-signal.d.ts +14 -1
  109. package/dist/fleet/engine-signal.js +5 -1
  110. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  111. package/dist/fleet/serving-lease.d.ts +18 -3
  112. package/dist/fleet/serving-lease.js +81 -35
  113. package/dist/fleet-client.d.ts +13 -11
  114. package/dist/fleet-client.js +53 -33
  115. package/dist/history-anchor.js +1 -1
  116. package/dist/hitl-delivery.d.ts +1 -3
  117. package/dist/hitl-delivery.js +1 -1
  118. package/dist/hooks/hook-runner.js +2 -2
  119. package/dist/host-lsp-manager.d.ts +2 -2
  120. package/dist/http/active-run-conflict.d.ts +3 -7
  121. package/dist/http/active-run-conflict.js +2 -2
  122. package/dist/http/admission.js +19 -4
  123. package/dist/http/approval-sweeps.js +7 -6
  124. package/dist/http/caller-trust.d.ts +2 -2
  125. package/dist/http/caller-trust.js +3 -3
  126. package/dist/http/checkpoint-ctx.d.ts +20 -63
  127. package/dist/http/checkpoint-ctx.js +2 -24
  128. package/dist/http/claim-heartbeat.d.ts +4 -4
  129. package/dist/http/claim-heartbeat.js +3 -3
  130. package/dist/http/dispatch.d.ts +2 -1
  131. package/dist/http/dispatch.js +36 -29
  132. package/dist/http/durable-run-leg.js +17 -17
  133. package/dist/http/health-self-report.d.ts +9 -1
  134. package/dist/http/health-self-report.js +2 -0
  135. package/dist/http/owner-access.d.ts +264 -20
  136. package/dist/http/owner-access.js +215 -5
  137. package/dist/http/readiness.d.ts +4 -2
  138. package/dist/http/resume-ctx-read.js +38 -1
  139. package/dist/http/resume-legs.d.ts +2 -1
  140. package/dist/http/resume-legs.js +273 -89
  141. package/dist/http/route-ctx.d.ts +41 -19
  142. package/dist/http/route-table.d.ts +1 -1
  143. package/dist/http/routes/a2a-serve.js +15 -14
  144. package/dist/http/routes/admin-memory-optout.d.ts +1 -1
  145. package/dist/http/routes/agents-roster.js +2 -3
  146. package/dist/http/routes/approvals-assistant.d.ts +3 -2
  147. package/dist/http/routes/approvals-assistant.js +12 -12
  148. package/dist/http/routes/background.d.ts +3 -3
  149. package/dist/http/routes/capabilities.js +10 -6
  150. package/dist/http/routes/config-catalog.d.ts +1 -1
  151. package/dist/http/routes/fleet.js +10 -8
  152. package/dist/http/routes/mcp-reconnect.js +2 -2
  153. package/dist/http/routes/memory-origin.d.ts +1 -1
  154. package/dist/http/routes/notify-wake.js +2 -3
  155. package/dist/http/routes/projects.d.ts +4 -2
  156. package/dist/http/routes/projects.js +7 -6
  157. package/dist/http/routes/retention-ops.d.ts +1 -1
  158. package/dist/http/routes/run-permission-mode.d.ts +1 -1
  159. package/dist/http/routes/run-permission-mode.js +2 -2
  160. package/dist/http/routes/runs.d.ts +1 -1
  161. package/dist/http/routes/runs.js +125 -66
  162. package/dist/http/routes/session-memory-sync.js +4 -5
  163. package/dist/http/routes/session-sync.d.ts +4 -3
  164. package/dist/http/routes/session-sync.js +174 -83
  165. package/dist/http/routes/sessions-list.js +13 -7
  166. package/dist/http/routes/sessions.d.ts +1 -1
  167. package/dist/http/routes/sessions.js +26 -48
  168. package/dist/http/routes/tasks.js +38 -23
  169. package/dist/http/routes/trace-usage.js +14 -14
  170. package/dist/http/routes/workflows.d.ts +1 -0
  171. package/dist/http/routes/workflows.js +20 -38
  172. package/dist/http/server.d.ts +28 -30
  173. package/dist/http/server.js +7 -3
  174. package/dist/http/stored-body-replay.d.ts +3 -24
  175. package/dist/http/stored-body-replay.js +2 -12
  176. package/dist/http/submit-ctx.d.ts +1 -2
  177. package/dist/http/submit-ctx.js +8 -6
  178. package/dist/http/submit-replay.d.ts +15 -2
  179. package/dist/http/submit-replay.js +10 -4
  180. package/dist/http/wire-types.d.ts +19 -6
  181. package/dist/identity-source.d.ts +20 -3
  182. package/dist/identity-source.js +14 -0
  183. package/dist/index.d.ts +4 -0
  184. package/dist/lane-home-probe.d.ts +3 -7
  185. package/dist/lane-home-probe.js +0 -1
  186. package/dist/leader/endpoint.js +9 -8
  187. package/dist/leader/leader.d.ts +2 -1
  188. package/dist/leg-facts.d.ts +19 -5
  189. package/dist/leg-facts.js +3 -1
  190. package/dist/mcp-authorization-face.d.ts +18 -14
  191. package/dist/mcp-authorization-face.js +3 -2
  192. package/dist/memory-scope.d.ts +5 -4
  193. package/dist/memory-scope.js +4 -4
  194. package/dist/memory-store-refusal.d.ts +1 -1
  195. package/dist/memory-sync-client.d.ts +30 -2
  196. package/dist/memory-sync-client.js +16 -9
  197. package/dist/observability/corrupt-read-seat.d.ts +1 -1
  198. package/dist/observability/err-text.d.ts +7 -0
  199. package/dist/observability/err-text.js +5 -0
  200. package/dist/observability/fail-open.d.ts +12 -8
  201. package/dist/observability/fail-open.js +12 -8
  202. package/dist/observability/logger.d.ts +7 -0
  203. package/dist/observability/logger.js +3 -1
  204. package/dist/observability/metrics.js +1 -0
  205. package/dist/observability/run-terminal.d.ts +1 -1
  206. package/dist/orchestration/workflow-notify-journal.d.ts +18 -1
  207. package/dist/orchestration/workflow-notify-journal.js +12 -1
  208. package/dist/orchestration/workflow-park-decision.d.ts +70 -18
  209. package/dist/orchestration/workflow-park-decision.js +49 -10
  210. package/dist/orchestration/workflow-park-holder.d.ts +65 -0
  211. package/dist/orchestration/workflow-park-holder.js +98 -0
  212. package/dist/orchestration/workflow-record-fence.d.ts +36 -0
  213. package/dist/orchestration/workflow-record-fence.js +42 -0
  214. package/dist/owner-faces.d.ts +28 -4
  215. package/dist/owner-faces.js +2 -0
  216. package/dist/parked-decide.d.ts +104 -53
  217. package/dist/parked-decide.js +153 -117
  218. package/dist/peer-directory.d.ts +2 -2
  219. package/dist/peer-inbound-face.d.ts +2 -6
  220. package/dist/peer-inbound-face.js +0 -3
  221. package/dist/peer-seat.d.ts +1 -1
  222. package/dist/peer-seat.js +52 -59
  223. package/dist/permission-mode-admission.d.ts +41 -5
  224. package/dist/permission-mode-admission.js +29 -3
  225. package/dist/plugins/adoption-log-sql.d.ts +3 -1
  226. package/dist/plugins/adoption-log-sql.js +7 -1
  227. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  228. package/dist/plugins/approval-ask-store-file.js +8 -0
  229. package/dist/plugins/approval-ask-store-memory.d.ts +6 -1
  230. package/dist/plugins/approval-ask-store-memory.js +28 -9
  231. package/dist/plugins/approval-ask-store-sql.d.ts +43 -2
  232. package/dist/plugins/approval-ask-store-sql.js +34 -5
  233. package/dist/plugins/approval-exemption-store.js +3 -0
  234. package/dist/plugins/approval-nonce-store.js +2 -0
  235. package/dist/plugins/breaker-state-sql.js +2 -0
  236. package/dist/plugins/caching-session-store.d.ts +9 -15
  237. package/dist/plugins/caching-session-store.js +6 -0
  238. package/dist/plugins/device-store-sql.js +6 -0
  239. package/dist/plugins/error-origin.d.ts +18 -0
  240. package/dist/plugins/error-origin.js +10 -0
  241. package/dist/plugins/exec-path-law.d.ts +34 -0
  242. package/dist/plugins/exec-path-law.js +13 -0
  243. package/dist/plugins/execution-placement-sql.js +6 -6
  244. package/dist/plugins/file-run-store.js +1 -1
  245. package/dist/plugins/image-bake-store-sql.js +6 -0
  246. package/dist/plugins/image-index-sql.js +4 -0
  247. package/dist/plugins/landed-attendants.d.ts +212 -0
  248. package/dist/plugins/landed-attendants.js +161 -0
  249. package/dist/plugins/leader-run-store-sql.js +4 -0
  250. package/dist/plugins/local-checkpoint-store.d.ts +4 -1
  251. package/dist/plugins/local-checkpoint-store.js +3 -0
  252. package/dist/plugins/local-session-live-reference.d.ts +40 -0
  253. package/dist/plugins/local-session-live-reference.js +39 -0
  254. package/dist/plugins/local-session-store.d.ts +71 -33
  255. package/dist/plugins/local-session-store.js +129 -27
  256. package/dist/plugins/memory-engine-pg.js +4 -0
  257. package/dist/plugins/memory-engine-sql-apply.js +5 -2
  258. package/dist/plugins/memory-engine-tidb.js +4 -0
  259. package/dist/plugins/memory-optout-grant-store-sql.js +4 -0
  260. package/dist/plugins/memory-run-store.js +1 -1
  261. package/dist/plugins/memory-sync-store-pg.js +5 -0
  262. package/dist/plugins/memory-sync-store-tidb.js +5 -0
  263. package/dist/plugins/one-time-migrations.d.ts +80 -61
  264. package/dist/plugins/one-time-migrations.js +241 -104
  265. package/dist/plugins/org-delivery-quota-store.js +4 -1
  266. package/dist/plugins/outcome-ledger-sql.js +2 -0
  267. package/dist/plugins/permission-rule-store-sql.js +11 -4
  268. package/dist/plugins/pg-cost-quota.js +4 -0
  269. package/dist/plugins/pg-pool.js +28 -1
  270. package/dist/plugins/pg-rate-limiter.js +2 -0
  271. package/dist/plugins/posix-shell-fs.d.ts +6 -1
  272. package/dist/plugins/posix-shell-fs.js +8 -3
  273. package/dist/plugins/remote-env-adb.d.ts +19 -15
  274. package/dist/plugins/remote-env-adb.js +44 -17
  275. package/dist/plugins/remote-env-connect-seat.d.ts +105 -0
  276. package/dist/plugins/remote-env-connect-seat.js +72 -0
  277. package/dist/plugins/remote-env-device.d.ts +22 -5
  278. package/dist/plugins/remote-env-device.js +114 -14
  279. package/dist/plugins/remote-env-e2b.d.ts +18 -7
  280. package/dist/plugins/remote-env-e2b.js +53 -40
  281. package/dist/plugins/remote-env-file-error.js +6 -2
  282. package/dist/plugins/remote-env-host.d.ts +18 -1
  283. package/dist/plugins/remote-env-host.js +48 -47
  284. package/dist/plugins/remote-env-k8s.d.ts +41 -39
  285. package/dist/plugins/remote-env-k8s.js +56 -32
  286. package/dist/plugins/remote-env-local-docker.d.ts +13 -8
  287. package/dist/plugins/remote-env-local-docker.js +37 -43
  288. package/dist/plugins/remote-env-ssh.d.ts +18 -12
  289. package/dist/plugins/remote-env-ssh.js +64 -62
  290. package/dist/plugins/remote-shell.d.ts +101 -2
  291. package/dist/plugins/remote-shell.js +221 -3
  292. package/dist/plugins/replica-registration.d.ts +35 -0
  293. package/dist/plugins/replica-registration.js +48 -0
  294. package/dist/plugins/resume-anchor-store-sql.d.ts +11 -2
  295. package/dist/plugins/resume-anchor-store-sql.js +12 -5
  296. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  297. package/dist/plugins/retention-lane-store-sql.js +4 -0
  298. package/dist/plugins/retention-sql.d.ts +20 -2
  299. package/dist/plugins/retention-sql.js +11 -0
  300. package/dist/plugins/run-store-sql.d.ts +22 -3
  301. package/dist/plugins/run-store-sql.js +33 -9
  302. package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
  303. package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
  304. package/dist/plugins/schema-consistency.d.ts +9 -5
  305. package/dist/plugins/schema-consistency.js +48 -18
  306. package/dist/plugins/schema-drift-sentence.js +1 -1
  307. package/dist/plugins/send-file-ledger.js +3 -0
  308. package/dist/plugins/serving-requirement.d.ts +68 -0
  309. package/dist/plugins/serving-requirement.js +60 -0
  310. package/dist/plugins/session-birth.d.ts +6 -2
  311. package/dist/plugins/session-capture-record-store-sql.js +2 -0
  312. package/dist/plugins/session-ext-table.d.ts +14 -1
  313. package/dist/plugins/session-ext-table.js +5 -1
  314. package/dist/plugins/session-row-sql.d.ts +49 -2
  315. package/dist/plugins/session-row-sql.js +56 -13
  316. package/dist/plugins/session-store-sql.d.ts +34 -18
  317. package/dist/plugins/session-store-sql.js +98 -17
  318. package/dist/plugins/shared-memory-store-sql.d.ts +1 -0
  319. package/dist/plugins/shared-memory-store-sql.js +6 -2
  320. package/dist/plugins/sql-driver.d.ts +2 -1
  321. package/dist/plugins/sql-driver.js +18 -3
  322. package/dist/plugins/sql-errors.d.ts +11 -6
  323. package/dist/plugins/sql-errors.js +5 -6
  324. package/dist/plugins/stop-then-start-sentence.d.ts +48 -3
  325. package/dist/plugins/stop-then-start-sentence.js +48 -0
  326. package/dist/plugins/stop-then-start.d.ts +109 -0
  327. package/dist/plugins/stop-then-start.js +104 -0
  328. package/dist/plugins/store-backend.d.ts +40 -13
  329. package/dist/plugins/store-backend.js +169 -27
  330. package/dist/plugins/store-boundary.d.ts +57 -0
  331. package/dist/plugins/store-boundary.js +139 -0
  332. package/dist/plugins/store-contracts.d.ts +29 -2
  333. package/dist/plugins/store-contracts.js +3 -0
  334. package/dist/plugins/store-fault-sentence-m53.d.ts +7 -0
  335. package/dist/plugins/store-fault-sentence-m53.js +25 -0
  336. package/dist/plugins/store-fault.d.ts +26 -7
  337. package/dist/plugins/store-fault.js +18 -2
  338. package/dist/plugins/task-attachment-store.js +9 -0
  339. package/dist/plugins/task-list-store-sql.d.ts +2 -2
  340. package/dist/plugins/task-list-store-sql.js +10 -8
  341. package/dist/plugins/tidb-cost-quota.js +4 -0
  342. package/dist/plugins/tidb-pool.js +6 -1
  343. package/dist/plugins/tidb-rate-limiter.js +4 -0
  344. package/dist/plugins/user-input-ledger-sql.js +4 -0
  345. package/dist/plugins/workflow-run-store-sql.d.ts +28 -10
  346. package/dist/plugins/workflow-run-store-sql.js +64 -4
  347. package/dist/plugins/workspace-fill.d.ts +4 -7
  348. package/dist/plugins/workspace-fill.js +2 -1
  349. package/dist/plugins/write-behind-counter.js +5 -0
  350. package/dist/process-instance.d.ts +3 -0
  351. package/dist/process-instance.js +7 -0
  352. package/dist/project-context.d.ts +64 -0
  353. package/dist/project-context.js +66 -0
  354. package/dist/project-registry.d.ts +46 -0
  355. package/dist/project-registry.js +44 -0
  356. package/dist/question.d.ts +9 -21
  357. package/dist/question.js +28 -19
  358. package/dist/request-key-closure.js +1 -1
  359. package/dist/request-value-closure.d.ts +4 -0
  360. package/dist/request-value-closure.js +10 -0
  361. package/dist/run-acceptance.d.ts +3 -2
  362. package/dist/run-acceptance.js +4 -6
  363. package/dist/run-cancel-card.d.ts +1 -1
  364. package/dist/run-cancel-card.js +2 -1
  365. package/dist/run-local.js +1 -0
  366. package/dist/runs.d.ts +37 -8
  367. package/dist/runs.js +53 -12
  368. package/dist/runtime-governance.d.ts +5 -4
  369. package/dist/security.d.ts +31 -33
  370. package/dist/security.js +41 -7
  371. package/dist/server-secret-env.d.ts +12 -10
  372. package/dist/server-secret-env.js +24 -15
  373. package/dist/session-agent.d.ts +1 -10
  374. package/dist/session-agent.js +0 -6
  375. package/dist/session-bindings.d.ts +82 -0
  376. package/dist/session-bindings.js +18 -0
  377. package/dist/session-children-retire.d.ts +2 -2
  378. package/dist/session-children-retire.js +2 -6
  379. package/dist/session-deletion.d.ts +16 -1
  380. package/dist/session-error-wire.d.ts +3 -3
  381. package/dist/session-error-wire.js +3 -3
  382. package/dist/session-incarnation.d.ts +28 -4
  383. package/dist/session-incarnation.js +14 -2
  384. package/dist/session-kind.d.ts +57 -0
  385. package/dist/session-kind.js +22 -0
  386. package/dist/session-notify-door.d.ts +4 -4
  387. package/dist/session-notify-door.js +5 -6
  388. package/dist/session-row.d.ts +21 -1
  389. package/dist/session-row.js +7 -1
  390. package/dist/session-rules-seat.d.ts +4 -3
  391. package/dist/session-rules-seat.js +3 -11
  392. package/dist/session-sync-kernel.d.ts +24 -14
  393. package/dist/session-sync-kernel.js +3 -6
  394. package/dist/session-sync.d.ts +4 -98
  395. package/dist/session-sync.js +4 -74
  396. package/dist/session-titler.d.ts +10 -3
  397. package/dist/session-titler.js +19 -6
  398. package/dist/ssh-host-key.d.ts +5 -0
  399. package/dist/ssh-host-key.js +3 -0
  400. package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
  401. package/dist/stores-sql/checkpoint-store-sql.js +106 -18
  402. package/dist/stores-sql/file-history-store-sql.d.ts +52 -2
  403. package/dist/stores-sql/file-history-store-sql.js +108 -70
  404. package/dist/stores-sql/pg-session-storage.d.ts +34 -7
  405. package/dist/stores-sql/pg-session-storage.js +37 -13
  406. package/dist/stores-sql/retention-store-sql.d.ts +2 -10
  407. package/dist/stores-sql/retention-store-sql.js +2 -10
  408. package/dist/stores-sql/session-policy-store-sql.d.ts +30 -7
  409. package/dist/stores-sql/session-policy-store-sql.js +61 -44
  410. package/dist/stores-sql/session-write-hooks.d.ts +11 -0
  411. package/dist/stores-sql/tidb-session-store.d.ts +58 -7
  412. package/dist/stores-sql/tidb-session-store.js +37 -13
  413. package/dist/submission-exit.d.ts +3 -2
  414. package/dist/submission-exit.js +9 -1
  415. package/dist/task-cwd.d.ts +6 -5
  416. package/dist/task-settings.d.ts +19 -5
  417. package/dist/task-settings.js +13 -7
  418. package/dist/terminal.d.ts +33 -2
  419. package/dist/terminal.js +139 -9
  420. package/dist/tool-approval.d.ts +147 -12
  421. package/dist/tool-approval.js +206 -109
  422. package/dist/trace/engine-notice-wire.d.ts +14 -0
  423. package/dist/trace/engine-notice-wire.js +18 -1
  424. package/dist/trace/project.d.ts +4 -3
  425. package/dist/trace/project.js +3 -2
  426. package/dist/trace/projection-table.d.ts +33 -2
  427. package/dist/trace/self-minted-wire.d.ts +422 -26
  428. package/dist/trace/self-minted-wire.js +149 -23
  429. package/dist/trace/tool-card.d.ts +3 -0
  430. package/dist/trace/tool-card.js +24 -0
  431. package/dist/trace/tool-output-media.d.ts +2 -1
  432. package/dist/unattended-permission-mode.d.ts +1 -1
  433. package/dist/web-admin-client/apply-effective.d.ts +1 -1
  434. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  435. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  436. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +120 -41
  437. package/package.json +3 -3
  438. package/wire-changes/server@7.150.0.json +335 -0
  439. package/wire-changes/server@7.151.0.json +762 -0
  440. package/dist/http/session-inherit-env.d.ts +0 -36
  441. package/dist/http/session-inherit-env.js +0 -6
@@ -2,9 +2,9 @@ import { type AcquiredSession, type PlacedSessionRow, type SessionHead, type Ses
2
2
  import type { Pool, PoolClient } from "pg";
3
3
  import { WindowedSessionStorage } from "./windowed-session-storage.js";
4
4
  import { type PlacementAcquireOpts } from "../plugins/session-placement.js";
5
- import { type StagingHandle } from "../session-sync-kernel.js";
5
+ import { type EntriesStreamOptions, type StagedCommitOptions, type StagingHandle, type SyncRelation } from "../session-sync-kernel.js";
6
6
  import type { RetentionDeclaration } from "@sema-agent/core";
7
- import { type RegisterReceipt, type WithdrawOutcome } from "../session-incarnation.js";
7
+ import { type RegisterReceipt, type SessionFace, type WithdrawOutcome } from "../session-incarnation.js";
8
8
  import type { SessionWriteHooks } from "./session-write-hooks.js";
9
9
  import { type IndexSpec } from "../plugins/ensure-index.js";
10
10
  import { type SessionDeleteCondition } from "./tidb-session-store.js";
@@ -78,7 +78,7 @@ export declare class PgSessionStore implements SessionStore {
78
78
  /** Idempotently create a session_meta row with an owner (core `SessionStore.register`). Existing owner is never overwritten. */
79
79
  register(sessionId: string, owner: string | null): Promise<void>;
80
80
  /** 出生回执面(TiDB 孪生 {@link TiDBSessionStore.registerWithReceipt} 同义:候选 = 这一次 INSERT 入参里铸的化身)。 */
81
- registerWithReceipt(sessionId: string, owner: string | null): Promise<RegisterReceipt>;
81
+ registerWithReceipt(sessionId: string, owner: string | null, kind?: string): Promise<RegisterReceipt>;
82
82
  /** 条件删除(TiDB 孪生 {@link TiDBSessionStore.deleteSessionIf} 同义)。 */
83
83
  deleteSessionIf(sessionId: string, cond: SessionDeleteCondition): Promise<WithdrawOutcome>;
84
84
  /** acquire 的建行(唯一一处;placed 会话的属主 = `placement.owner` 原样,core 7.62.0 [ref] 盒义务 7 —— TiDB 孪生同名方法同注)。 */
@@ -86,6 +86,8 @@ export declare class PgSessionStore implements SessionStore {
86
86
  /** The one session_meta INSERT. Existing owner is never overwritten.
87
87
  * [ref]:`placement` 与行同一条 INSERT;`DO NOTHING` 臂 ⇒ 已存在行的元组永不被重写(first-write 不可变)。 */
88
88
  private insertMeta;
89
+ /** {@link insertMeta} 那一句,发在给定的连接上(带种类的登记在自己的事务连接上发)。 */
90
+ private insertMetaOn;
89
91
  /** [ref] 义务 6 的廉价探针(TiDB 孪生 {@link TiDBSessionStore.placementOf} 同义)。 */
90
92
  placementOf(sessionId: string): Promise<SessionPlacementRecord | undefined>;
91
93
  /** [ref] 义务 5 —— placed 分区枚举(年龄锚/上界/tupleIncomplete 三条读法与 TiDB 孪生逐字同义,
@@ -128,10 +130,7 @@ export declare class PgSessionStore implements SessionStore {
128
130
  * held connection across the stream → a slow consumer can't pin one). Start cursor = afterSeq ?? -1 (0-based dense).
129
131
  * Resolves to null (NOT an empty iterable) for a missing session — eager `session_meta` probe (BIGINT seq comes
130
132
  * back as a STRING in node-pg → always Number(...) the cursor advance). */
131
- exportEntriesStream(sessionId: string, opts?: {
132
- afterSeq?: number;
133
- batchSize?: number;
134
- }): Promise<AsyncIterable<SessionTreeEntry> | null>;
133
+ exportEntriesStream(sessionId: string, opts: EntriesStreamOptions): Promise<AsyncIterable<SessionTreeEntry> | null>;
135
134
  /** @see TiDBSessionStore.importEntries — 2c session-sync IMPORT: core `validateEntriesForImport` gate FIRST (fail-closed),
136
135
  * then persist its verbatim output (re-seq dense from 0, ids/parents/payload/ts unchanged) in ONE txn, **lock-or-create, meta first**
137
136
  * (占行 → 锁行判是不是本次占下的 → 部署的事务内钩 `import` → 事件);owner = the RE-STAMPED authenticated principal (never the bundle's). */
@@ -160,4 +159,32 @@ export declare class PgSessionStore implements SessionStore {
160
159
  get size(): number;
161
160
  dispose(): void;
162
161
  }
162
+ /**
163
+ * 2c session-sync P1d-β — the PG twin of {@link TiDBStagingHandle} (behaviourally identical; only the SQL dialect
164
+ * differs: `$n` placeholders, `$n::jsonb`, `ON CONFLICT (session_id, entry_id) DO NOTHING`, rowCount, FOR UPDATE,
165
+ * BEGIN/COMMIT handle). Streams entry batches into a shadow session_event partition, then atomically RENAMEs them
166
+ * onto the real session id in ONE txn; session_meta is locked (or claimed, for a fresh push) first and settled last, COMMIT = the commit point.
167
+ */
168
+ /** 暂存句柄(会话同步的推送腿逐批写、提交、撤销;由本店的 `stageImport` 交出)。导出只为组合座给它封口(店边界)。 */
169
+ export declare class PgStagingHandle implements StagingHandle {
170
+ private readonly pool;
171
+ private readonly hooks;
172
+ readonly realSessionId: string;
173
+ readonly stagingId: string;
174
+ private nextSeq;
175
+ private readonly seen;
176
+ constructor(pool: Pool, hooks: SessionWriteHooks, realSessionId: string, stagingId: string);
177
+ /** @see TiDBStagingHandle.appendBatch — DENSE re-seq (skip already-staged ids), idempotent ON CONFLICT (session_id,
178
+ * entry_id) DO NOTHING, one txn/batch. */
179
+ appendBatch(entries: SessionTreeEntry[]): Promise<void>;
180
+ /** @see TiDBStagingHandle.commit — the atomic swap: owner CAS first (lock / claim the session_meta row) → in-commit re-classify (FOR UPDATE)
181
+ * → purge dst → rename staging→real → settle session_meta. Throws StagedCommitOccupantChangedError when the owner cell is not the judged one,
182
+ * SyncConflictError on an unconsented fork/stale flip. */
183
+ commit(owner: string | null, opts: StagedCommitOptions): Promise<{
184
+ relation: SyncRelation["relation"];
185
+ face: SessionFace;
186
+ }>;
187
+ /** @see TiDBStagingHandle.abort — DELETE the staging rows. Idempotent. */
188
+ abort(): Promise<void>;
189
+ }
163
190
  //# sourceMappingURL=pg-session-storage.d.ts.map
@@ -8,7 +8,7 @@ import { pgSafeJsonStringify } from "../plugins/pg-safe-json.js";
8
8
  import { toIso } from "../plugins/sql-row-helpers.js";
9
9
  import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertCells, SESSION_DURABILITY, SESSION_PLACEMENTS, } from "../plugins/session-placement.js";
10
10
  import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
11
- import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
11
+ import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOccupantChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
12
12
  import { isPgUniqueViolation } from "../plugins/sql-errors.js";
13
13
  import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
14
14
  import { SESSION_META_INCARNATION_COLUMN, SESSION_META_LINEAGE_COLUMNS, forkLineageOfRow, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
@@ -224,14 +224,33 @@ export class PgSessionStore {
224
224
  async register(sessionId, owner) {
225
225
  await this.registerWithReceipt(sessionId, owner);
226
226
  }
227
- async registerWithReceipt(sessionId, owner) {
227
+ async registerWithReceipt(sessionId, owner, kind) {
228
228
  if (!isSessionOwnerValue(owner)) {
229
229
  if ((await this.headOf(sessionId)) !== undefined)
230
230
  return {};
231
231
  assertFirstOwnerWritable(sessionId, owner);
232
232
  }
233
233
  const incarnation = mintSessionIncarnation();
234
- await this.insertMeta(sessionId, owner, undefined, incarnation);
234
+ if (kind === undefined) {
235
+ await this.insertMeta(sessionId, owner, undefined, incarnation);
236
+ return { minted: { sessionId, owner, incarnation } };
237
+ }
238
+ const conn = await this.pool.connect();
239
+ try {
240
+ await conn.query("BEGIN");
241
+ await this.insertMetaOn(conn, sessionId, owner, undefined, incarnation);
242
+ const res = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = $1 FOR UPDATE", [sessionId]);
243
+ if (res.rows[0]?.incarnation === incarnation)
244
+ await this.hooks.birth.record(statementsOn(conn), { rootSessionId: sessionId, kind });
245
+ await conn.query("COMMIT");
246
+ }
247
+ catch (err) {
248
+ await conn.query("ROLLBACK").catch(() => undefined);
249
+ throw err;
250
+ }
251
+ finally {
252
+ conn.release();
253
+ }
235
254
  return { minted: { sessionId, owner, incarnation } };
236
255
  }
237
256
  async deleteSessionIf(sessionId, cond) {
@@ -254,9 +273,12 @@ export class PgSessionStore {
254
273
  await this.insertMeta(sessionId, placement === undefined ? null : placedSessionOwnerOf(sessionId, placement), placement);
255
274
  }
256
275
  async insertMeta(sessionId, owner, placement, incarnation = mintSessionIncarnation()) {
276
+ await this.insertMetaOn(this.pool, sessionId, owner, placement, incarnation);
277
+ }
278
+ async insertMetaOn(on, sessionId, owner, placement, incarnation) {
257
279
  const now = new Date();
258
280
  const placed = placementInsertCells(placement, now);
259
- await this.pool.query(`INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, ${placed.columns}) ` +
281
+ await on.query(`INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, ${placed.columns}) ` +
260
282
  "VALUES ($1,$2,$3,$4,NULL,0,$5,$6,$7,$8,$9,$10,$11) ON CONFLICT (session_id) DO NOTHING", [sessionId, owner, now, now, incarnation, ...placed.values]);
261
283
  }
262
284
  async placementOf(sessionId) {
@@ -318,7 +340,7 @@ export class PgSessionStore {
318
340
  }
319
341
  throw err instanceof SessionError
320
342
  ? err
321
- : new SessionError("storage", `placed session ${sessionId} could not be deleted: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : undefined);
343
+ : new SessionError("storage", `placed session ${sessionId} could not be deleted`, err instanceof Error ? err : undefined);
322
344
  }
323
345
  finally {
324
346
  conn.release();
@@ -420,11 +442,13 @@ export class PgSessionStore {
420
442
  if (metaRes.rows.length === 0)
421
443
  return null;
422
444
  const pool = this.pool;
423
- const batchSize = opts?.batchSize ?? 500;
445
+ const batchSize = opts.batchSize ?? 500;
424
446
  async function* gen() {
425
- let cursor = opts?.afterSeq ?? -1;
447
+ let cursor = opts.afterSeq ?? -1;
426
448
  for (;;) {
427
449
  const res = await pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND seq > $2 ORDER BY seq ASC LIMIT $3`, [sessionId, cursor, batchSize]);
450
+ if (res.rows.length > 0)
451
+ await opts.recheck();
428
452
  for (const r of res.rows)
429
453
  yield decodeSessionEventPayload(r.payload, sessionId, r.seq);
430
454
  if (res.rows.length < batchSize)
@@ -542,7 +566,7 @@ export class PgSessionStore {
542
566
  this.pending.clear();
543
567
  }
544
568
  }
545
- class PgStagingHandle {
569
+ export class PgStagingHandle {
546
570
  pool;
547
571
  hooks;
548
572
  realSessionId;
@@ -583,11 +607,11 @@ class PgStagingHandle {
583
607
  await conn.query("BEGIN");
584
608
  const now = new Date();
585
609
  const minted = mintSessionIncarnation();
586
- if (opts.expectedOwner !== undefined) {
587
- const cas = await conn.query("UPDATE session_meta SET leaf_seq = leaf_seq + 1 WHERE session_id = $1 AND owner IS NOT DISTINCT FROM $2", [this.realSessionId, opts.expectedOwner]);
610
+ if (opts.expectedOccupant !== undefined) {
611
+ const cas = await conn.query("UPDATE session_meta SET leaf_seq = leaf_seq + 1 WHERE session_id = $1 AND owner IS NOT DISTINCT FROM $2 AND incarnation = $3", [this.realSessionId, opts.expectedOccupant.owner, opts.expectedOccupant.incarnation]);
588
612
  if (cas.rowCount !== 1) {
589
613
  await conn.query("ROLLBACK");
590
- throw new StagedCommitOwnerChangedError(this.realSessionId);
614
+ throw new StagedCommitOccupantChangedError(this.realSessionId);
591
615
  }
592
616
  }
593
617
  else {
@@ -595,7 +619,7 @@ class PgStagingHandle {
595
619
  const claimed = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = $1 FOR UPDATE", [this.realSessionId]);
596
620
  if (String(claimed.rows[0]?.incarnation) !== minted) {
597
621
  await conn.query("ROLLBACK");
598
- throw new StagedCommitOwnerChangedError(this.realSessionId);
622
+ throw new StagedCommitOccupantChangedError(this.realSessionId);
599
623
  }
600
624
  }
601
625
  settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "import", rootSessionId: this.realSessionId }));
@@ -607,7 +631,7 @@ class PgStagingHandle {
607
631
  throw new SessionError("placement_refused", `session ${this.realSessionId} belongs to the ${dstMeta.rows[0].placement_kind} placement partition — a session-sync staged commit cannot overwrite a delegation transcript`);
608
632
  }
609
633
  const srcIds = staged.rows.map((r) => String(r.entry_id));
610
- const dstIds = opts.expectedOwner !== undefined ? dst.rows.map((r) => String(r.entry_id)) : null;
634
+ const dstIds = opts.expectedOccupant !== undefined ? dst.rows.map((r) => String(r.entry_id)) : null;
611
635
  const rel = classifySyncRelationshipByIds(srcIds, dstIds);
612
636
  if ((rel.relation === "fork" || rel.relation === "stale") && opts.resolution !== "overwrite-dst") {
613
637
  await conn.query("ROLLBACK");
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 托管留存(managed retention)的**执行器** —— 引擎座表那一半(阶段三逐字搬进 `@sema-agent/core-stores-sql` 的那一半)。
3
3
  * SINGLE-FILE DUAL-DIALECT([ref] A12 定型半场;先例 = `checkpoint-store-sql.ts`)。[ref] 车1,设计稿
4
- * `docs/DESIGN-270-retention-lane.md` v1.2。上游契约真源 = `@sema-agent/core` 的 `core/retention.d.ts`
4
+ * `docs/[ref]-retention-lane.md` v1.2。上游契约真源 = `@sema-agent/core` 的 `core/retention.d.ts`
5
5
  * (`ManagedRetentionCapability` / `RetentionReceipt`);事务内钩表的形 = core-stores-sql 的 `SqlTransactionHooks`(本文件的同名型是它的形,见下)。
6
6
  *
7
7
  * ── 一个事务、两个属主:执行器 + 部署钩 ─────────────────────────────────────────────────────────────
@@ -301,15 +301,7 @@ export declare class SqlRetentionStore implements ManagedRetentionCapability {
301
301
  * background-agent 活锚 / checkpoint 的 scope+status+deadline 复合索引)是独立一车,已进交接清单。
302
302
  */
303
303
  private countPinnedExpiryCandidates;
304
- /**
305
- * 会话树的**四条活引用腿**的 SQL 谓词(`sm` = session_meta 的别名)。写成一处的理由与 {@link horizonArms}
306
- * 相同,而且更重:它同时是「可删候选」的否定谓词与「被保护计数」的肯定谓词,两份手抄必然在某次改动后分家,
307
- * 而分家的方向是**多删**(候选放宽、计数收紧,两边都不会有人红)。
308
- *
309
- * 腿的词表:活跑 claim / 挂起 park / 活着的后台子代(running|parked,三根会话锚)/ 在跑的 workflow
310
- * (`originatingSessionId` 住在 run blob 里 ⇒ JSON 提取是真方言分叉)。fork 子代**结构上没有边**,
311
- * 理由见 {@link SqlRetentionStore.deleteExpiredSessions} 的头注。
312
- */
304
+ /** 会话树的活引用谓词(`sm` = session_meta 的别名):唯一写法在 `plugins/retention-sql.ts` 的 `sessionLiveReferenceSql`(候选的否定与被保护计数的肯定同读;会话老化的候选面与锁内重判同读)。 */
313
305
  private liveReferenceExists;
314
306
  /**
315
307
  * 本域过视界、**当下被活引用护住**的会话数 = receipt 的 `skipped`。
@@ -1,7 +1,7 @@
1
1
  import { peerSessionBoxHandle } from "@sema-agent/core";
2
2
  import { incarnationFromCell } from "../session-incarnation.js";
3
3
  import { MANAGED_RETENTION } from "../plugins/retention-declaration.js";
4
- import { retentionDomainWhere, retentionIdSet } from "../plugins/retention-sql.js";
4
+ import { retentionDomainWhere, retentionIdSet, sessionLiveReferenceSql } from "../plugins/retention-sql.js";
5
5
  import { dropMailboxBoxOn } from "./mailbox-store-sql.js";
6
6
  import { SEALED_EVENT, eraseSessionPolicyRowsOn } from "./session-policy-store-sql.js";
7
7
  export const RETENTION_TOMBSTONE_TABLE = "retention_tombstone";
@@ -217,15 +217,7 @@ export class SqlRetentionStore {
217
217
  return Number(rows[0]?.n ?? 0);
218
218
  }
219
219
  liveReferenceExists() {
220
- const workflow = this.db.dialect === "mysql"
221
- ? `EXISTS (SELECT 1 FROM workflow_run wr WHERE wr.status = 'running' AND JSON_UNQUOTE(JSON_EXTRACT(wr.run, '$.originatingSessionId')) = sm.session_id)`
222
- : `EXISTS (SELECT 1 FROM workflow_run wr WHERE wr.status = 'running' AND wr.run::json->>'originatingSessionId' = sm.session_id)`;
223
- return (`(sm.placement_kind IS NOT NULL` +
224
- ` OR EXISTS (SELECT 1 FROM task_active ta WHERE ta.session_id = sm.session_id)` +
225
- ` OR EXISTS (SELECT 1 FROM checkpoint c2 WHERE c2.session_id = sm.session_id AND c2.status = 'pending')` +
226
- ` OR EXISTS (SELECT 1 FROM background_agent ba WHERE ba.status IN ('running','parked')` +
227
- ` AND (ba.session_id = sm.session_id OR ba.parent_session_id = sm.session_id OR ba.root_session_id = sm.session_id))` +
228
- ` OR ${workflow})`);
220
+ return sessionLiveReferenceSql(this.db.dialect);
229
221
  }
230
222
  async countProtectedSessions(exec, input) {
231
223
  const w = this.domainWhere("sm.owner", input.domain, 1);
@@ -59,10 +59,16 @@ export type SessionPolicyStoreLogger = {
59
59
  warn(msg: string, fields?: Record<string, unknown>): void;
60
60
  };
61
61
  /**
62
- * 一次客户端写的计数(部署的格:会话规则是客户端可见的会话态,提交出口撤回一只刚铸的会话之前读这只计数)。组合座交入;店只在锁住根行、根行在、
63
- * 所有拒判之后、规则落行之前,同一事务、同一连接调它一次 —— 计数住部署的旁表,店目录不认识那张表。
62
+ * 会话根行的部署格(组合座交入;店目录不认识 `session_meta` 的列,也不认识客户端写计数住的旁表):
63
+ * · `lockRoot` —— 同一连接上锁会话根行(全局锁序首句:根 `session_meta` → 别的表),答行在不在。语句是部署的「锁根行读住客」那一句
64
+ * (组合座交 `plugins/session-row-sql.ts` 的 `SQL_SESSION_ROOT_SEAT`,与落定段、行格写原语、执行放置同一句),店不自写第二份;
65
+ * · `countClientWrite` —— 一次客户端写的计数(会话规则是客户端可见的会话态,提交出口撤回一只刚铸的会话之前读这只计数)。店只在锁住根行、根行在、
66
+ * 所有拒判之后、规则落行之前,同一事务、同一连接调它一次。
64
67
  */
65
- export type SessionClientWriteCounter = (conn: SqlTxConn, dialect: SqlDriver["dialect"], sessionId: string) => Promise<void>;
68
+ export interface SessionRootSeat {
69
+ lockRoot(conn: SqlTxConn, dialect: SqlDriver["dialect"], sessionId: string): Promise<boolean>;
70
+ countClientWrite(conn: SqlTxConn, dialect: SqlDriver["dialect"], sessionId: string): Promise<void>;
71
+ }
66
72
  /**
67
73
  * 按会话抹除规则行的**唯一语句源**(店的 `deleteBySession` 在自己的事务里调;会话删除执行体 `retention-store-sql.ts` 在会话树事务里对一批会话调 ——
68
74
  * 同一组语句,`session_policy_rev_mark` 的高水位律只写这一处,core 契约 `session_policy.rev_survives_delete`)。调用方持事务。
@@ -73,9 +79,9 @@ export declare function eraseSessionPolicyRowsOn(conn: Pick<SqlTxConn, "query">,
73
79
  /** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
74
80
  export declare class SqlSessionPolicyStore implements SessionPolicyStore {
75
81
  private readonly db;
76
- private readonly countClientWrite;
82
+ private readonly root;
77
83
  private readonly logger;
78
- constructor(db: SqlDriver, countClientWrite: SessionClientWriteCounter, logger: SessionPolicyStoreLogger);
84
+ constructor(db: SqlDriver, root: SessionRootSeat, logger: SessionPolicyStoreLogger);
79
85
  /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
80
86
  private q;
81
87
  /** 判据属主 = `sql-errors.ts`([ref] P1-①;本站点旧形已是并集形,收编后语义不变)。 */
@@ -92,6 +98,19 @@ export declare class SqlSessionPolicyStore implements SessionPolicyStore {
92
98
  listBySession(sessionId: string): Promise<SessionRulesRecord[]>;
93
99
  /** Operator/owner write: CAS + tighten-only (unless operator) + rev bump, atomic in one txn; first-write race retried. */
94
100
  putRules(sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts?: PutRulesOptions): Promise<StoredSessionRules>;
101
+ /**
102
+ * 一次规则写的**语句源**(read-check-write 全在调用方的事务里;调用方已锁根行 —— {@link putRules} 自己锁,落定段在段首锁并判过住客):锁读 prior 与记号 →
103
+ * CAS → 引擎写口判官 → 号 → 计客户端写(`how.count`)→ 写。`how.skip(prior)` 为真 ⇒ 不写、不计,答 `{ skipped: true }`(落定重放的等内容跳过)。
104
+ * 首写撞主键(并发首写赢了)照旧抛出,由事务主人的重来臂接住({@link retrying} / 落定段的 `alsoRetry` 读同一只 {@link isDupKey})。
105
+ */
106
+ putRulesOn(conn: SqlTxConn, sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts: PutRulesOptions | undefined, how: {
107
+ readonly count: boolean;
108
+ readonly skip?: (prior: StoredSessionRules | null) => boolean;
109
+ }): Promise<StoredSessionRules | {
110
+ readonly skipped: true;
111
+ }>;
112
+ /** 判据属主 = `sql-errors.ts`:并发首写撞主键(落定段的重来臂读它)。 */
113
+ isDupKeyError(e: unknown): boolean;
95
114
  /**
96
115
  * The store's ONE retry arm: re-run the whole read-check-write transaction while the failure is an engine
97
116
  * ARBITRATION artifact rather than an answer. Two such artifacts, and only two:
@@ -164,6 +183,10 @@ export declare class SqlSessionPolicyStore implements SessionPolicyStore {
164
183
  * 残余(如实):记号里**已有**的世代若被列外改到 BIGINT 上界,冲突臂的 `记号.gen + 1` 在库里溢出 ⇒ 这次抹除响亮失败
165
184
  * (驱动错误;那只记号本来就读不回,`putRules` 同样拒)—— 抹除不读记号,这一形不在本判词射程内。 */
166
185
  deleteBySession(sessionId: string): Promise<void>;
186
+ /** 抹除的语句源(调用方的事务里;落定段的擦除腿与 {@link deleteBySession} 同一只):交回被封的键 —— 提交**之后**经 {@link discloseErased} 报。 */
187
+ eraseOn(conn: SqlTxConn, sessionId: string): Promise<readonly SealedEraseKey[]>;
188
+ /** 抹除提交之后的响亮面(封键 ⇒ 一条 warn;无封键 ⇒ 什么都不做)。 */
189
+ discloseErased(sessionId: string, sealed: readonly SealedEraseKey[]): void;
167
190
  /**
168
191
  * [ref] 封键的响亮面:一次抹除至多一条 warn。走到这里删除已经提交,一次日志故障不许把它报成失败(E21 会因此 500、
169
192
  * overwrite-dst 会因此中止;codex 7.105.0 修复验证轮 [medium] 同形)⇒ 日志座抛不往外抛;**也不吞披露**:同一行落日志出口接缝的
@@ -183,10 +206,10 @@ export declare class SqlSessionPolicyStore implements SessionPolicyStore {
183
206
  }
184
207
  /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
185
208
  export declare class TiDBSessionPolicyStore extends SqlSessionPolicyStore {
186
- constructor(pool: MySqlPool, countClientWrite: SessionClientWriteCounter, logger: SessionPolicyStoreLogger);
209
+ constructor(pool: MySqlPool, root: SessionRootSeat, logger: SessionPolicyStoreLogger);
187
210
  }
188
211
  /** PostgreSQL binding — historical class name + ctor shape preserved. */
189
212
  export declare class PgSessionPolicyStore extends SqlSessionPolicyStore {
190
- constructor(pool: PgPool, countClientWrite: SessionClientWriteCounter, logger: SessionPolicyStoreLogger);
213
+ constructor(pool: PgPool, root: SessionRootSeat, logger: SessionPolicyStoreLogger);
191
214
  }
192
215
  //# sourceMappingURL=session-policy-store-sql.d.ts.map
@@ -87,11 +87,11 @@ export async function eraseSessionPolicyRowsOn(conn, dialect, sessionIds) {
87
87
  }
88
88
  export class SqlSessionPolicyStore {
89
89
  db;
90
- countClientWrite;
90
+ root;
91
91
  logger;
92
- constructor(db, countClientWrite, logger) {
92
+ constructor(db, root, logger) {
93
93
  this.db = db;
94
- this.countClientWrite = countClientWrite;
94
+ this.root = root;
95
95
  this.logger = logger;
96
96
  }
97
97
  q(mysql, pg) {
@@ -124,50 +124,67 @@ export class SqlSessionPolicyStore {
124
124
  }));
125
125
  }
126
126
  async putRules(sessionId, principal, rules, opts) {
127
- const key = policyKey(sessionId, principal);
128
127
  return this.retrying(() => this.tx(async (conn) => {
129
- const root = await conn.query(this.q("SELECT session_id FROM session_meta WHERE session_id = ? FOR UPDATE", "SELECT session_id FROM session_meta WHERE session_id = $1 FOR UPDATE"), [sessionId]);
130
- const { rows } = await conn.query(this.q(`SELECT ${jsonColumnText("mysql", RULES)}, rev, gen FROM session_policy WHERE policy_key = ? FOR UPDATE`, `SELECT ${jsonColumnText("pg", RULES)}, rev, gen FROM session_policy WHERE policy_key = $1 FOR UPDATE`), [key]);
131
- const prior = rows[0] ? rowToStored(rows[0], `for session ${JSON.stringify(sessionId)} (pre-write snapshot)`) : undefined;
132
- const priorRev = prior?.rev ?? 0;
133
- if (opts?.expectedRev !== undefined && priorRev !== opts.expectedRev) {
134
- throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts.expectedRev}, have ${priorRev})`);
135
- }
136
- const { rows: markRows } = await conn.query(this.q("SELECT rev, gen FROM session_policy_rev_mark WHERE policy_key = ? FOR UPDATE", "SELECT rev, gen FROM session_policy_rev_mark WHERE policy_key = $1 FOR UPDATE"), [key]);
137
- const markRev = markRows[0] ? Number(markRows[0].rev) : 0;
138
- const markGen = markRows[0] ? Number(markRows[0].gen) : 0;
139
- if (markRows[0] !== undefined && (!isStoredCounter(markGen, 0) || !isStoredCounter(markRev, 0))) {
140
- throw new SessionPolicyError("corrupt", `the session rules rev mark for session ${JSON.stringify(sessionId)} is unreadable (rev ${String(markRows[0].rev)}, gen ${String(markRows[0].gen)}) — refusing rather than stamp a rev/lineage nothing proves`);
141
- }
142
- const clean = judgeSessionRulesWrite(prior ?? null, rules, opts);
143
- if (this.db.dialect === "pg" && pgHasUnstorable(clean)) {
144
- throw new SessionPolicyError("loosen_forbidden", "rules contain unstorable bytes (NUL / lone surrogate) — refused; strip them at the source (authorization rules are never rewritten by the store)");
145
- }
146
- const nextRev = Math.max(priorRev, markRev) + 1;
147
- const nextGen = prior?.gen ?? markGen;
148
- if (!isStoredCounter(nextRev, 1) || !isStoredCounter(nextGen, 0)) {
149
- throw new SessionPolicyError("corrupt", `session rules for session ${JSON.stringify(sessionId)}: the next stamp (rev ${nextRev}, gen ${nextGen}) would leave the safe-integer range this store reads back — refusing before writing a row nothing could read`);
150
- }
151
- if (root.rows.length > 0)
152
- await this.countClientWrite(conn, this.db.dialect, sessionId);
153
- const json = JSON.stringify(clean);
154
- const now = new Date();
155
- if (prior) {
156
- const upd = await conn.query(this.q(`UPDATE session_policy SET principal = ?, ${RULES} = ?, rev = ?, gen = ?, updated_at = ? WHERE policy_key = ? AND rev = ?`, `UPDATE session_policy SET principal = $1, ${RULES} = $2::jsonb, rev = $3, gen = $4, updated_at = $5 WHERE policy_key = $6 AND rev = $7`), [principal ?? null, json, nextRev, nextGen, now, key, priorRev]);
157
- if (upd.affected !== 1)
158
- throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts?.expectedRev ?? priorRev}, have ${priorRev})`);
159
- }
160
- else {
161
- await conn.query(this.q(`INSERT INTO session_policy (policy_key, session_id, principal, ${RULES}, rev, gen, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)`, `INSERT INTO session_policy (policy_key, session_id, principal, ${RULES}, rev, gen, created_at, updated_at) VALUES ($1,$2,$3,$4::jsonb,$5,$6,$7,$8)`), [key, sessionId, principal ?? null, json, nextRev, nextGen, now, now]);
162
- }
163
- return structuredClone({ ...clean, rev: nextRev, gen: nextGen });
128
+ const present = await this.root.lockRoot(conn, this.db.dialect, sessionId);
129
+ const out = await this.putRulesOn(conn, sessionId, principal, rules, opts, { count: present });
130
+ if ("skipped" in out)
131
+ throw new Error("SqlSessionPolicyStore.putRules: no skip predicate was given, yet the write was skipped");
132
+ return out;
164
133
  }));
165
134
  }
135
+ async putRulesOn(conn, sessionId, principal, rules, opts, how) {
136
+ const key = policyKey(sessionId, principal);
137
+ const { rows } = await conn.query(this.q(`SELECT ${jsonColumnText("mysql", RULES)}, rev, gen FROM session_policy WHERE policy_key = ? FOR UPDATE`, `SELECT ${jsonColumnText("pg", RULES)}, rev, gen FROM session_policy WHERE policy_key = $1 FOR UPDATE`), [key]);
138
+ const prior = rows[0] ? rowToStored(rows[0], `for session ${JSON.stringify(sessionId)} (pre-write snapshot)`) : undefined;
139
+ if (how.skip?.(prior ?? null) === true)
140
+ return { skipped: true };
141
+ const priorRev = prior?.rev ?? 0;
142
+ if (opts?.expectedRev !== undefined && priorRev !== opts.expectedRev) {
143
+ throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts.expectedRev}, have ${priorRev})`);
144
+ }
145
+ const { rows: markRows } = await conn.query(this.q("SELECT rev, gen FROM session_policy_rev_mark WHERE policy_key = ? FOR UPDATE", "SELECT rev, gen FROM session_policy_rev_mark WHERE policy_key = $1 FOR UPDATE"), [key]);
146
+ const markRev = markRows[0] ? Number(markRows[0].rev) : 0;
147
+ const markGen = markRows[0] ? Number(markRows[0].gen) : 0;
148
+ if (markRows[0] !== undefined && (!isStoredCounter(markGen, 0) || !isStoredCounter(markRev, 0))) {
149
+ throw new SessionPolicyError("corrupt", `the session rules rev mark for session ${JSON.stringify(sessionId)} is unreadable (rev ${String(markRows[0].rev)}, gen ${String(markRows[0].gen)}) — refusing rather than stamp a rev/lineage nothing proves`);
150
+ }
151
+ const clean = judgeSessionRulesWrite(prior ?? null, rules, opts);
152
+ if (this.db.dialect === "pg" && pgHasUnstorable(clean)) {
153
+ throw new SessionPolicyError("loosen_forbidden", "rules contain unstorable bytes (NUL / lone surrogate) — refused; strip them at the source (authorization rules are never rewritten by the store)");
154
+ }
155
+ const nextRev = Math.max(priorRev, markRev) + 1;
156
+ const nextGen = prior?.gen ?? markGen;
157
+ if (!isStoredCounter(nextRev, 1) || !isStoredCounter(nextGen, 0)) {
158
+ throw new SessionPolicyError("corrupt", `session rules for session ${JSON.stringify(sessionId)}: the next stamp (rev ${nextRev}, gen ${nextGen}) would leave the safe-integer range this store reads back — refusing before writing a row nothing could read`);
159
+ }
160
+ if (how.count)
161
+ await this.root.countClientWrite(conn, this.db.dialect, sessionId);
162
+ const json = JSON.stringify(clean);
163
+ const now = new Date();
164
+ if (prior) {
165
+ const upd = await conn.query(this.q(`UPDATE session_policy SET principal = ?, ${RULES} = ?, rev = ?, gen = ?, updated_at = ? WHERE policy_key = ? AND rev = ?`, `UPDATE session_policy SET principal = $1, ${RULES} = $2::jsonb, rev = $3, gen = $4, updated_at = $5 WHERE policy_key = $6 AND rev = $7`), [principal ?? null, json, nextRev, nextGen, now, key, priorRev]);
166
+ if (upd.affected !== 1)
167
+ throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts?.expectedRev ?? priorRev}, have ${priorRev})`);
168
+ }
169
+ else {
170
+ await conn.query(this.q(`INSERT INTO session_policy (policy_key, session_id, principal, ${RULES}, rev, gen, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)`, `INSERT INTO session_policy (policy_key, session_id, principal, ${RULES}, rev, gen, created_at, updated_at) VALUES ($1,$2,$3,$4::jsonb,$5,$6,$7,$8)`), [key, sessionId, principal ?? null, json, nextRev, nextGen, now, now]);
171
+ }
172
+ return structuredClone({ ...clean, rev: nextRev, gen: nextGen });
173
+ }
174
+ isDupKeyError(e) {
175
+ return this.isDupKey(e);
176
+ }
166
177
  retrying(attempt) {
167
178
  return retryOnDeadlock(this.db.dialect, attempt, (e) => this.isDupKey(e));
168
179
  }
169
180
  async deleteBySession(sessionId) {
170
- const sealed = await this.retrying(() => this.tx((conn) => eraseSessionPolicyRowsOn(conn, this.db.dialect, [sessionId])));
181
+ const sealed = await this.retrying(() => this.tx((conn) => this.eraseOn(conn, sessionId)));
182
+ this.discloseErased(sessionId, sealed);
183
+ }
184
+ eraseOn(conn, sessionId) {
185
+ return eraseSessionPolicyRowsOn(conn, this.db.dialect, [sessionId]);
186
+ }
187
+ discloseErased(sessionId, sealed) {
171
188
  if (sealed.length > 0)
172
189
  this.discloseSealed(sessionId, sealed);
173
190
  }
@@ -206,13 +223,13 @@ export class SqlSessionPolicyStore {
206
223
  }
207
224
  }
208
225
  export class TiDBSessionPolicyStore extends SqlSessionPolicyStore {
209
- constructor(pool, countClientWrite, logger) {
210
- super(mysqlDriver(pool), countClientWrite, logger);
226
+ constructor(pool, root, logger) {
227
+ super(mysqlDriver(pool), root, logger);
211
228
  }
212
229
  }
213
230
  export class PgSessionPolicyStore extends SqlSessionPolicyStore {
214
- constructor(pool, countClientWrite, logger) {
215
- super(pgDriver(pool), countClientWrite, logger);
231
+ constructor(pool, root, logger) {
232
+ super(pgDriver(pool), root, logger);
216
233
  }
217
234
  }
218
235
  //# sourceMappingURL=session-policy-store-sql.js.map
@@ -13,6 +13,10 @@
13
13
  * 不对别的表的行加锁,也不在 EvalPlanQual 里复判它们。所以是**两句**:店先 `FOR UPDATE` 锁根行(删除 / 重登记 / 改属主都要先拿这把锁),
14
14
  * 再在**同一连接**上交出语句面,部署用新语句锁定读(PG 新快照、TiDB 当前读)。钩里的写随店的事务提交或回滚(店拒、或任何一步抛,一起撤)。
15
15
  *
16
+ * ## 建行钩(服务扩展,包里没有)
17
+ * 带种类的登记(`registerWithReceipt` 的第三参;会话种类的律在 `session-kind.ts`):店在一只事务里 INSERT、锁定读回化身,读回的 = 本次铸的(这一笔真建了行)⇒
18
+ * {@link SessionWriteHooks.birth} 在同一事务里记下种类;别的(已在的行 / 并发登记里输的那一只)⇒ 零写。包随店搬家时这一组同是向引擎的请托。
19
+ *
16
20
  * ## 键控 fork(服务扩展,包里没有)
17
21
  * 调用方给目标 id 与键的 fork(`forkTo`)是本服务的扩展:键是部署格,在建目标行的同一事务里由 {@link SessionWriteHooks.keyedFork} 记下;撞上已在的目标时
18
22
  * 读它的属主与键。包随店搬家时这一组是向引擎的请托(带目标 id 与键的 fork,或钩表多一组)。
@@ -61,5 +65,12 @@ export interface SessionWriteHooks {
61
65
  }): Promise<void>;
62
66
  targetOf(sessionId: string): Promise<ForkTargetReading | undefined>;
63
67
  };
68
+ /** 建行钩(服务扩展,与键控 fork 同族):带种类的登记里,本事务的 INSERT 真建了这一行(锁定读回的化身 = 本次铸的)⇒ 在同一事务里记下它的种类(部署格的值,座原样转交、不解读;律在部署那一侧)。 */
69
+ readonly birth: {
70
+ record(txn: SessionTxStatements, born: {
71
+ readonly rootSessionId: string;
72
+ readonly kind: string;
73
+ }): Promise<void>;
74
+ };
64
75
  }
65
76
  //# sourceMappingURL=session-write-hooks.d.ts.map
@@ -13,10 +13,10 @@
13
13
  import { type AcquiredSession, type PlacedSessionRow, type SessionHead, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
14
14
  import type { Pool } from "mysql2/promise";
15
15
  import type { RetentionDeclaration } from "@sema-agent/core";
16
- import { type RegisterReceipt, type WithdrawJudgeVerdict, type WithdrawOutcome } from "../session-incarnation.js";
16
+ import { type RegisterReceipt, type SessionFace, type WithdrawJudgeVerdict, type WithdrawOutcome } from "../session-incarnation.js";
17
17
  import type { SessionTxStatements, SessionWriteHooks } from "./session-write-hooks.js";
18
18
  import { type PlacementAcquireOpts } from "../plugins/session-placement.js";
19
- import { type StagingHandle } from "../session-sync-kernel.js";
19
+ import { type EntriesStreamOptions, type StagedCommitOptions, type StagingHandle, type SyncRelation } from "../session-sync-kernel.js";
20
20
  import { type ForkPlacement, type ForkTarget, type ForkToOutcome } from "../plugins/session-fork.js";
21
21
  /**
22
22
  * **条件撤回的店那一半**(两方言一形;TiDB 孪生与 PG 孪生 `pg-session-storage.ts` 同调;撤回的规则在 `plugins/session-birth.ts` 头注)。
@@ -75,7 +75,7 @@ export declare class TiDBSessionStore implements SessionStore {
75
75
  * 出生回执面(`plugins/session-birth.ts`):与 {@link register} 同一笔 INSERT;候选化身 = 这一次 INSERT 入参里铸的那一只(不复读)。
76
76
  * 已在的行上 INSERT 是 no-op 臂 ⇒ 行上的化身不是候选 ⇒ 撤回在锁内比对时判「留」—— 出生事实只在那里判,这里不分新旧行。
77
77
  */
78
- registerWithReceipt(sessionId: string, owner: string | null): Promise<RegisterReceipt>;
78
+ registerWithReceipt(sessionId: string, owner: string | null, kind?: string): Promise<RegisterReceipt>;
79
79
  /** 条件删除(`plugins/session-birth.ts` 的撤回;本连接开事务 → 店与部署两半同一语句面 → 撤回才提交,否则回滚)。 */
80
80
  deleteSessionIf(sessionId: string, cond: SessionDeleteCondition): Promise<WithdrawOutcome>;
81
81
  /**
@@ -88,6 +88,8 @@ export declare class TiDBSessionStore implements SessionStore {
88
88
  * **永不被重写**(first-write 不可变,义务 5 的写侧半场)。
89
89
  * [ref]:化身与属主同一条 first-write 律 —— 新行铸一只(`mintSessionIncarnation`),已存在的行 no-op 臂一个字节不动。 */
90
90
  private insertMeta;
91
+ /** {@link insertMeta} 那一句,发在给定的连接上(带种类的登记在自己的事务连接上发,与锁定读、建行钩同一事务)。 */
92
+ private insertMetaOn;
91
93
  /**
92
94
  * [ref] 义务 6 的廉价探针 —— 一行 PK SELECT:`undefined` = 普通会话或无此行。
93
95
  * (release 的冷缓存腿、包装店的暖命中准入判、诊断面共用这一只。)
@@ -194,10 +196,7 @@ export declare class TiDBSessionStore implements SessionStore {
194
196
  * (按文本投影 + `decodeSessionEventPayload` 恰好解一次码,坏体当场拒). The check is eager (the method is async) because a generator body is lazy — it can't decide
195
197
  * `null` vs. iterable on its own first pull.
196
198
  */
197
- exportEntriesStream(sessionId: string, opts?: {
198
- afterSeq?: number;
199
- batchSize?: number;
200
- }): Promise<AsyncIterable<SessionTreeEntry> | null>;
199
+ exportEntriesStream(sessionId: string, opts: EntriesStreamOptions): Promise<AsyncIterable<SessionTreeEntry> | null>;
201
200
  /**
202
201
  * 2c session-sync — IMPORT a verbatim entry log (from another backend's {@link exportEntries}) into a
203
202
  * (typically NEW) `sessionId`. This is the WRITE half of {@link fork}, surfaced as `SessionRepo.importEntries`,
@@ -269,5 +268,57 @@ export declare class TiDBSessionStore implements SessionStore {
269
268
  get size(): number;
270
269
  dispose(): void;
271
270
  }
271
+ /**
272
+ * 2c session-sync P1d-β (PUSH streaming, §15 + §4) — a TiDB {@link StagingHandle}: stream entry batches into a SHADOW
273
+ * `session_event` partition (the staging id, NO meta row → invisible to wake), then atomically SWAP them into the real
274
+ * session id. Server memory is bounded by the in-flight batch (one txn per batch), not the whole log. The swap is a
275
+ * row-key RENAME (`UPDATE session_event SET session_id`) — metadata-only, NO payload re-copy — so the commit cost is
276
+ * the rename + a DELETE of the (purged) dst rows, in ONE txn; the session_meta row is locked (or, for a fresh push, claimed) first and
277
+ * settled last, and COMMIT is the sole commit point.
278
+ */
279
+ /** 暂存句柄(会话同步的推送腿逐批写、提交、撤销;由本店的 `stageImport` 交出)。导出只为组合座给它封口(店边界)。 */
280
+ export declare class TiDBStagingHandle implements StagingHandle {
281
+ private readonly pool;
282
+ private readonly hooks;
283
+ readonly realSessionId: string;
284
+ readonly stagingId: string;
285
+ /** Running DENSE seq counter for the staging rows. Advanced ONLY for a logically-NEW entry id (tracked in `seen`), so
286
+ * a re-streamed entry (Phase-B resume) is skipped and the staged seqs stay dense 0..N-1 (NO holes that would collide
287
+ * with a future F2 leaf_seq append on the committed session). */
288
+ private nextSeq;
289
+ /** Entry ids already staged by THIS handle — a resume re-streams from the start, so dedup keeps seqs dense + idempotent. */
290
+ private readonly seen;
291
+ constructor(pool: Pool, hooks: SessionWriteHooks, realSessionId: string, stagingId: string);
292
+ /** Append a batch to the staging id, re-seq'd DENSE from the running counter (skipping ids already staged), IDEMPOTENTLY
293
+ * (ON DUPLICATE KEY UPDATE on the `(session_id, entry_id)` uk is the durable backstop too). One txn per batch
294
+ * (bounded). NO meta row is written (the staging id stays invisible). ts is the entry's own ISO timestamp (verbatim). */
295
+ appendBatch(entries: SessionTreeEntry[]): Promise<void>;
296
+ /**
297
+ * The ATOMIC swap (ONE txn), §15 commit:
298
+ * 0. OWNER CAS FIRST ([ref] — one gate predicate, two phases): the session's owner cell must still be the one the import's gate
299
+ * judged (`opts.expectedOccupant`: owner ∧ incarnation). Like every other writer of a session (append, replace, delete) the staged commit takes the
300
+ * session_meta row FIRST, then the logs — one lock order. A present cell: one conditioned UPDATE (current read; takes the row lock
301
+ * until COMMIT); it bumps `leaf_seq` only as the CAS's always-changing column (a same-value UPDATE can read 0 affected rows on the
302
+ * MySQL protocol depending on driver flags), and step 4 overwrites that column with the real value in the same txn. An absent cell
303
+ * (a fresh push) cannot be locked, so it is CLAIMED: insert this commit's row now (a no-op on an existing key), then a locking read
304
+ * must show the incarnation this commit minted — anything else is a row somebody else created. Either miss ⇒ ROLLBACK → throw
305
+ * StagedCommitOccupantChangedError (route → 409), before anything about the row is read or reported (no placement, no relation,
306
+ * no entry ids).
307
+ * 1. IN-COMMIT RE-CLASSIFY (closes the Phase-A plan→commit TOCTOU): read the dst's CURRENT entry ids + the staged
308
+ * ids under `FOR UPDATE` (row-locks serialize a concurrent commit/run) → classifySyncRelationshipByIds. A flip
309
+ * to fork/stale WITHOUT `resolution:"overwrite-dst"` → ROLLBACK → throw SyncConflictError (route → 409).
310
+ * 2. DELETE the dst's existing session_event rows (the replaceEntries purge).
311
+ * 3. UPDATE session_event SET session_id = real WHERE session_id = staging (the row-key RENAME — metadata only).
312
+ * 4. UPDATE session_meta(owner, leaf_id=replayedLeaf, leaf_seq=N) on the row step 0 locked or claimed; COMMIT is the sole commit point.
313
+ * A crash before COMMIT: the DELETE+rename roll back (dst untouched) and the staging rows keep no meta = invisible
314
+ * orphans (reaped by sweepStagingSessions). `owner` is the re-stamped importing principal (§9).
315
+ */
316
+ commit(owner: string | null, opts: StagedCommitOptions): Promise<{
317
+ relation: SyncRelation["relation"];
318
+ face: SessionFace;
319
+ }>;
320
+ /** Drop the staging rows (Phase B aborted before commit). Idempotent (a missing staging id deletes 0). */
321
+ abort(): Promise<void>;
322
+ }
272
323
  export {};
273
324
  //# sourceMappingURL=tidb-session-store.d.ts.map