@sema-agent/server 7.132.0 → 7.134.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 (473) hide show
  1. package/MIGRATION.md +353 -0
  2. package/README.md +8 -9
  3. package/README.zh-CN.md +7 -6
  4. package/USAGE.md +301 -64
  5. package/deploy/sema-up/README.md +35 -1
  6. package/deploy/sema-up/chart/Chart.yaml +1 -1
  7. package/deploy/sema-up/chart/templates/NOTES.txt +15 -0
  8. package/deploy/sema-up/chart/templates/_helpers.tpl +129 -0
  9. package/deploy/sema-up/chart/templates/sealed-key.yaml +24 -0
  10. package/deploy/sema-up/chart/templates/server-v4pro.yaml +19 -2
  11. package/deploy/sema-up/chart/templates/server.yaml +19 -2
  12. package/deploy/sema-up/chart/values-full-single.yaml +5 -1
  13. package/deploy/sema-up/chart/values.yaml +34 -0
  14. package/deploy/sema-up/stack/compose.yaml +3 -0
  15. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
  16. package/dist/adoption/plan.d.ts +3 -2
  17. package/dist/adoption/plan.js +7 -5
  18. package/dist/adoption/runner.js +3 -10
  19. package/dist/adoption/sql.d.ts +3 -2
  20. package/dist/adoption/sql.js +3 -5
  21. package/dist/adoption/wire.d.ts +4 -1
  22. package/dist/adoption/wire.js +1 -0
  23. package/dist/approval-ask-audit-store.d.ts +32 -14
  24. package/dist/approval-ask-audit-store.js +272 -74
  25. package/dist/approval-ask-machine.d.ts +15 -0
  26. package/dist/approval-ask-machine.js +6 -0
  27. package/dist/approval-ask-retention.d.ts +136 -0
  28. package/dist/approval-ask-retention.js +102 -0
  29. package/dist/approval-card.d.ts +83 -23
  30. package/dist/approval-card.js +45 -15
  31. package/dist/approval-content-kind.d.ts +5 -2
  32. package/dist/approval-content-kind.js +16 -3
  33. package/dist/approval-deny-reasons.d.ts +1 -1
  34. package/dist/approval-gate-currency.d.ts +72 -0
  35. package/dist/approval-gate-currency.js +94 -0
  36. package/dist/approval.d.ts +15 -0
  37. package/dist/approval.js +55 -17
  38. package/dist/audit.d.ts +27 -10
  39. package/dist/audit.js +12 -16
  40. package/dist/bake-runner/main.js +3 -2
  41. package/dist/boot/coordinators.d.ts +6 -1
  42. package/dist/boot/coordinators.js +6 -3
  43. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  44. package/dist/boot/execution-env.js +9 -7
  45. package/dist/boot/execution-lane.js +11 -0
  46. package/dist/boot/leader.d.ts +2 -0
  47. package/dist/boot/leader.js +4 -1
  48. package/dist/boot/memory-boundary.js +2 -2
  49. package/dist/boot/memory-consolidation.d.ts +5 -4
  50. package/dist/boot/memory-consolidation.js +7 -6
  51. package/dist/boot/org-delivery.d.ts +28 -0
  52. package/dist/boot/org-delivery.js +20 -0
  53. package/dist/boot/read-deny-anchor-audit.js +2 -2
  54. package/dist/boot/reapers.d.ts +3 -0
  55. package/dist/boot/reapers.js +21 -1
  56. package/dist/boot/resolve-spec.d.ts +3 -0
  57. package/dist/boot/resolve-spec.js +60 -81
  58. package/dist/boot/runner-deps.d.ts +18 -5
  59. package/dist/boot/runner-deps.js +10 -0
  60. package/dist/boot/session-faces.d.ts +1 -0
  61. package/dist/boot/session-faces.js +4 -2
  62. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  63. package/dist/boot/stage-06-runners.d.ts +6 -3
  64. package/dist/boot/stage-06-runners.js +20 -4
  65. package/dist/boot/stage-07-capability-layer.d.ts +4 -1
  66. package/dist/boot/stage-07-capability-layer.js +24 -3
  67. package/dist/boot/stage-08-reapers.d.ts +4 -1
  68. package/dist/boot/stage-08-reapers.js +2 -2
  69. package/dist/boot/stage-09-leader.d.ts +5 -1
  70. package/dist/boot/stage-09-leader.js +2 -2
  71. package/dist/boot/stage-10-http-server.d.ts +5 -1
  72. package/dist/boot/stage-10-http-server.js +6 -6
  73. package/dist/boot/stores.d.ts +8 -0
  74. package/dist/boot/stores.js +10 -14
  75. package/dist/boot/web-admin.d.ts +10 -0
  76. package/dist/boot/web-admin.js +162 -65
  77. package/dist/boot/workflow-orchestration.d.ts +2 -0
  78. package/dist/boot/workflow-orchestration.js +2 -1
  79. package/dist/brain.js +8 -3
  80. package/dist/breaker-key.d.ts +6 -0
  81. package/dist/breaker-key.js +29 -0
  82. package/dist/budget.js +5 -1
  83. package/dist/caller-text-echo.d.ts +11 -6
  84. package/dist/caller-text-echo.js +10 -12
  85. package/dist/canonical-json.d.ts +2 -41
  86. package/dist/canonical-json.js +2 -13
  87. package/dist/capabilities/center-plugins.d.ts +7 -0
  88. package/dist/capabilities/center-plugins.js +1 -1
  89. package/dist/capabilities/composite-members.d.ts +1 -1
  90. package/dist/capabilities/composite-members.js +1 -1
  91. package/dist/capabilities/execution-lane.js +2 -2
  92. package/dist/capabilities/hands-lane.d.ts +21 -0
  93. package/dist/capabilities/hands-lane.js +10 -0
  94. package/dist/capabilities/mcp-probe.d.ts +36 -4
  95. package/dist/capabilities/mcp-probe.js +29 -6
  96. package/dist/capabilities/scenarios.d.ts +63 -7
  97. package/dist/capabilities/scenarios.js +39 -7
  98. package/dist/capability-facts.d.ts +36 -4
  99. package/dist/capability-facts.js +30 -4
  100. package/dist/center-config-tier.d.ts +69 -0
  101. package/dist/center-config-tier.js +42 -0
  102. package/dist/checkpoint-ctx-seal-m28.d.ts +42 -0
  103. package/dist/checkpoint-ctx-seal-m28.js +19 -0
  104. package/dist/checkpoint-identity.d.ts +16 -0
  105. package/dist/checkpoint-identity.js +19 -0
  106. package/dist/config-catalog.d.ts +17 -10
  107. package/dist/config-catalog.js +43 -28
  108. package/dist/config-d-migration.d.ts +3 -1
  109. package/dist/config-d-migration.js +2 -2
  110. package/dist/config-dir-trust.d.ts +114 -0
  111. package/dist/config-dir-trust.js +388 -0
  112. package/dist/config-invariants.d.ts +18 -5
  113. package/dist/config-invariants.js +35 -3
  114. package/dist/config-provider.d.ts +6 -1
  115. package/dist/config-provider.js +6 -2
  116. package/dist/config-types.d.ts +71 -14
  117. package/dist/config-types.js +8 -0
  118. package/dist/config.d.ts +13 -0
  119. package/dist/config.js +121 -59
  120. package/dist/delegated-session-owner.d.ts +185 -0
  121. package/dist/delegated-session-owner.js +119 -0
  122. package/dist/device-protocol.d.ts +5 -3
  123. package/dist/device-protocol.js +1 -1
  124. package/dist/device-ws-hub.js +21 -2
  125. package/dist/device-ws-protocol.d.ts +70 -6
  126. package/dist/device-ws-protocol.js +50 -23
  127. package/dist/elicitation.js +3 -2
  128. package/dist/entry-problems.d.ts +34 -0
  129. package/dist/entry-problems.js +14 -0
  130. package/dist/execution-lane-caps.d.ts +49 -10
  131. package/dist/execution-lane-caps.js +43 -4
  132. package/dist/field-conflict.d.ts +44 -0
  133. package/dist/field-conflict.js +21 -0
  134. package/dist/file-history-disclosure.d.ts +34 -5
  135. package/dist/file-history-disclosure.js +2 -2
  136. package/dist/fleet/fleet-bus.d.ts +84 -6
  137. package/dist/fleet/fleet-bus.js +42 -9
  138. package/dist/hooks/classic-stations.d.ts +10 -5
  139. package/dist/hooks/classic-stations.js +4 -4
  140. package/dist/hooks/command-spawn.d.ts +57 -0
  141. package/dist/hooks/command-spawn.js +139 -0
  142. package/dist/hooks/hook-llm.js +2 -2
  143. package/dist/hooks/hook-runner.d.ts +127 -25
  144. package/dist/hooks/hook-runner.js +668 -287
  145. package/dist/hooks/hook-source.d.ts +44 -0
  146. package/dist/hooks/hook-source.js +40 -0
  147. package/dist/http/active-run-conflict.d.ts +5 -13
  148. package/dist/http/active-run-conflict.js +17 -27
  149. package/dist/http/admission.js +111 -60
  150. package/dist/http/approval-sweeps.js +58 -19
  151. package/dist/http/caller-trust.d.ts +77 -0
  152. package/dist/http/caller-trust.js +143 -0
  153. package/dist/http/checkpoint-ctx.d.ts +173 -18
  154. package/dist/http/checkpoint-ctx.js +171 -12
  155. package/dist/http/claimed-run.d.ts +4 -3
  156. package/dist/http/device-placement.d.ts +2 -4
  157. package/dist/http/device-placement.js +2 -1
  158. package/dist/http/dispatch.js +13 -7
  159. package/dist/http/durable-run-leg.d.ts +4 -32
  160. package/dist/http/durable-run-leg.js +37 -12
  161. package/dist/http/idempotency.d.ts +2 -2
  162. package/dist/http/idempotency.js +2 -2
  163. package/dist/http/owner-access.d.ts +56 -0
  164. package/dist/http/owner-access.js +42 -0
  165. package/dist/http/principal-gate.d.ts +78 -27
  166. package/dist/http/principal-gate.js +64 -14
  167. package/dist/http/readiness.d.ts +11 -5
  168. package/dist/http/readiness.js +10 -3
  169. package/dist/http/refusal-body.d.ts +12 -0
  170. package/dist/http/refusal-body.js +11 -0
  171. package/dist/http/request-guards.d.ts +16 -32
  172. package/dist/http/request-guards.js +31 -30
  173. package/dist/http/resume-ctx-read.d.ts +50 -6
  174. package/dist/http/resume-ctx-read.js +33 -7
  175. package/dist/http/resume-legs.d.ts +5 -5
  176. package/dist/http/resume-legs.js +163 -91
  177. package/dist/http/retired-service-deps.d.ts +43 -0
  178. package/dist/http/retired-service-deps.js +18 -0
  179. package/dist/http/route-ctx.d.ts +44 -37
  180. package/dist/http/route-ctx.js +4 -0
  181. package/dist/http/route-table.js +2 -0
  182. package/dist/http/routes/a2a-serve.js +3 -4
  183. package/dist/http/routes/admin-config-refresh.js +1 -1
  184. package/dist/http/routes/admin-drain.js +1 -1
  185. package/dist/http/routes/admin-memory-optout.js +5 -1
  186. package/dist/http/routes/adoption.js +2 -9
  187. package/dist/http/routes/agents-roster.js +2 -3
  188. package/dist/http/routes/approvals-assistant.js +91 -56
  189. package/dist/http/routes/background.js +3 -2
  190. package/dist/http/routes/capabilities.js +20 -4
  191. package/dist/http/routes/config-catalog.d.ts +3 -3
  192. package/dist/http/routes/config-catalog.js +1 -1
  193. package/dist/http/routes/devices.js +3 -17
  194. package/dist/http/routes/diagnostics.d.ts +3 -3
  195. package/dist/http/routes/diagnostics.js +6 -2
  196. package/dist/http/routes/fleet.js +4 -4
  197. package/dist/http/routes/images.js +6 -7
  198. package/dist/http/routes/leader.js +1 -8
  199. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  200. package/dist/http/routes/mcp-reconnect.js +3 -2
  201. package/dist/http/routes/memory-bundle.js +1 -1
  202. package/dist/http/routes/memory-compliance.js +1 -1
  203. package/dist/http/routes/memory-consolidation.js +1 -1
  204. package/dist/http/routes/memory-origin.d.ts +1 -1
  205. package/dist/http/routes/memory-origin.js +1 -1
  206. package/dist/http/routes/memory-policy.d.ts +1 -1
  207. package/dist/http/routes/memory-policy.js +3 -2
  208. package/dist/http/routes/notify-wake.js +8 -21
  209. package/dist/http/routes/observability.js +2 -2
  210. package/dist/http/routes/principal-inbox.d.ts +39 -0
  211. package/dist/http/routes/principal-inbox.js +76 -0
  212. package/dist/http/routes/retention-ops.js +2 -9
  213. package/dist/http/routes/rules.js +9 -22
  214. package/dist/http/routes/runs.d.ts +5 -0
  215. package/dist/http/routes/runs.js +63 -115
  216. package/dist/http/routes/session-sync.d.ts +3 -2
  217. package/dist/http/routes/session-sync.js +53 -48
  218. package/dist/http/routes/sessions-list.d.ts +1 -1
  219. package/dist/http/routes/sessions-list.js +3 -4
  220. package/dist/http/routes/sessions.js +33 -43
  221. package/dist/http/routes/side-query.js +6 -4
  222. package/dist/http/routes/tasks.js +71 -30
  223. package/dist/http/routes/trace-usage.js +7 -9
  224. package/dist/http/routes/workflows.d.ts +351 -0
  225. package/dist/http/routes/workflows.js +153 -119
  226. package/dist/http/run-declared-families.d.ts +16 -0
  227. package/dist/http/run-declared-families.js +12 -0
  228. package/dist/http/send.d.ts +24 -3
  229. package/dist/http/send.js +14 -2
  230. package/dist/http/server.d.ts +23 -18
  231. package/dist/http/server.js +20 -15
  232. package/dist/http/stored-body-replay.js +17 -4
  233. package/dist/http/submit-ctx.d.ts +4 -3
  234. package/dist/http/submit-ctx.js +23 -6
  235. package/dist/http/submit-replay.d.ts +4 -3
  236. package/dist/http/submit-replay.js +5 -4
  237. package/dist/http/wire-types.d.ts +17 -10
  238. package/dist/index.d.ts +3 -1
  239. package/dist/index.js +1 -1
  240. package/dist/lane-home-probe.d.ts +78 -9
  241. package/dist/lane-home-probe.js +78 -12
  242. package/dist/leader/endpoint.d.ts +4 -1
  243. package/dist/leader/endpoint.js +11 -10
  244. package/dist/leader/wire.d.ts +4 -0
  245. package/dist/leader/wire.js +8 -7
  246. package/dist/limit-refusal.d.ts +17 -5
  247. package/dist/limit-refusal.js +4 -1
  248. package/dist/main.js +1 -1
  249. package/dist/mcp-assertion-header.d.ts +31 -10
  250. package/dist/mcp-assertion-header.js +10 -3
  251. package/dist/memory-origin-fence.js +2 -2
  252. package/dist/memory-project-layer.d.ts +37 -0
  253. package/dist/memory-project-layer.js +46 -0
  254. package/dist/memory-scope.d.ts +23 -16
  255. package/dist/memory-scope.js +24 -8
  256. package/dist/memory-sync-client.d.ts +27 -13
  257. package/dist/memory-sync-client.js +107 -18
  258. package/dist/model-compat.d.ts +1 -1
  259. package/dist/model-entry-refusal.d.ts +9 -1
  260. package/dist/model-entry-refusal.js +6 -1
  261. package/dist/model-plane-face.d.ts +51 -0
  262. package/dist/model-plane-face.js +24 -0
  263. package/dist/model-provider.d.ts +5 -0
  264. package/dist/model-provider.js +4 -2
  265. package/dist/model-route-endpoint.d.ts +8 -11
  266. package/dist/model-route-endpoint.js +4 -7
  267. package/dist/model-route-env.d.ts +22 -0
  268. package/dist/model-route-env.js +2 -0
  269. package/dist/model-route-gap.d.ts +60 -30
  270. package/dist/model-route-gap.js +21 -15
  271. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  272. package/dist/observability/corrupt-read-seat.js +9 -0
  273. package/dist/observability/err-text.d.ts +6 -0
  274. package/dist/observability/err-text.js +18 -4
  275. package/dist/observability/fail-open.d.ts +35 -9
  276. package/dist/observability/fail-open.js +35 -9
  277. package/dist/observability/metrics.js +1 -0
  278. package/dist/observability/prompt-manifest.d.ts +22 -25
  279. package/dist/observability/prompt-manifest.js +54 -50
  280. package/dist/observability/tool-trace.js +9 -10
  281. package/dist/operator-ask.d.ts +10 -0
  282. package/dist/operator-ask.js +3 -0
  283. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  284. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  285. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  286. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  287. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  288. package/dist/orchestration/workflow-notify-journal.js +27 -34
  289. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  290. package/dist/orchestration/workflow-park-decision.js +119 -0
  291. package/dist/org-delivery-gate.d.ts +81 -0
  292. package/dist/org-delivery-gate.js +59 -0
  293. package/dist/panel-tool-names.d.ts +23 -0
  294. package/dist/panel-tool-names.js +8 -0
  295. package/dist/parked-decide.d.ts +43 -11
  296. package/dist/parked-decide.js +100 -51
  297. package/dist/parked-revive-surface.d.ts +9 -2
  298. package/dist/parked-revive-surface.js +11 -8
  299. package/dist/permission-rule-list.d.ts +1 -1
  300. package/dist/permission-rule-list.js +2 -2
  301. package/dist/permission-rule-vocab.d.ts +1 -23
  302. package/dist/permission-rule-vocab.js +0 -13
  303. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  304. package/dist/plugins/approval-ask-store-file.js +21 -0
  305. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  306. package/dist/plugins/approval-ask-store-memory.js +34 -2
  307. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  308. package/dist/plugins/approval-ask-store-sql.js +37 -1
  309. package/dist/plugins/approval-payload-fingerprint.d.ts +14 -6
  310. package/dist/plugins/approval-payload-fingerprint.js +1 -1
  311. package/dist/plugins/blob-backend.d.ts +38 -25
  312. package/dist/plugins/blob-backend.js +29 -19
  313. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  314. package/dist/plugins/breaker-state-sql.js +4 -4
  315. package/dist/plugins/caching-session-store.d.ts +4 -1
  316. package/dist/plugins/caching-session-store.js +7 -3
  317. package/dist/plugins/checkpoint-store-sql.d.ts +36 -1
  318. package/dist/plugins/checkpoint-store-sql.js +65 -3
  319. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  320. package/dist/plugins/file-history-store-sql.js +3 -1
  321. package/dist/plugins/file-run-store.d.ts +5 -13
  322. package/dist/plugins/file-run-store.js +9 -0
  323. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  324. package/dist/plugins/fork-routing-session-store.js +12 -1
  325. package/dist/plugins/host-platform.d.ts +2 -0
  326. package/dist/plugins/host-platform.js +2 -1
  327. package/dist/plugins/local-checkpoint-store.d.ts +15 -2
  328. package/dist/plugins/local-checkpoint-store.js +32 -1
  329. package/dist/plugins/local-session-store.d.ts +13 -10
  330. package/dist/plugins/local-session-store.js +23 -14
  331. package/dist/plugins/local-session-window.d.ts +45 -0
  332. package/dist/plugins/local-session-window.js +38 -0
  333. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  334. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  335. package/dist/plugins/mailbox-store-sql.js +77 -52
  336. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  337. package/dist/plugins/memory-run-store.d.ts +5 -13
  338. package/dist/plugins/memory-run-store.js +8 -0
  339. package/dist/plugins/one-time-migrations.d.ts +121 -5
  340. package/dist/plugins/one-time-migrations.js +332 -8
  341. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  342. package/dist/plugins/org-delivery-quota-store.js +228 -0
  343. package/dist/plugins/permission-rule-store-file.js +10 -6
  344. package/dist/plugins/permission-rule-store-sql.js +8 -8
  345. package/dist/plugins/pg-pool.js +39 -7
  346. package/dist/plugins/pg-session-storage.d.ts +18 -5
  347. package/dist/plugins/pg-session-storage.js +68 -23
  348. package/dist/plugins/remote-env-adb.d.ts +6 -12
  349. package/dist/plugins/remote-env-adb.js +12 -2
  350. package/dist/plugins/remote-env-device.d.ts +14 -7
  351. package/dist/plugins/remote-env-device.js +23 -15
  352. package/dist/plugins/remote-env-k8s.d.ts +8 -12
  353. package/dist/plugins/remote-env-k8s.js +23 -5
  354. package/dist/plugins/remote-env-local-docker.d.ts +10 -15
  355. package/dist/plugins/remote-env-local-docker.js +12 -9
  356. package/dist/plugins/remote-env-ssh.d.ts +10 -16
  357. package/dist/plugins/remote-env-ssh.js +17 -11
  358. package/dist/plugins/run-store-sql.d.ts +16 -14
  359. package/dist/plugins/run-store-sql.js +22 -2
  360. package/dist/plugins/schema-indexes.js +2 -0
  361. package/dist/plugins/send-user-file.js +2 -2
  362. package/dist/plugins/session-epoch-anchor.d.ts +32 -6
  363. package/dist/plugins/session-epoch-anchor.js +2 -3
  364. package/dist/plugins/session-ownership.d.ts +11 -0
  365. package/dist/plugins/session-ownership.js +11 -0
  366. package/dist/plugins/session-placement.d.ts +12 -14
  367. package/dist/plugins/session-placement.js +2 -15
  368. package/dist/plugins/session-store.d.ts +18 -8
  369. package/dist/plugins/session-store.js +15 -4
  370. package/dist/plugins/sql-driver.d.ts +74 -11
  371. package/dist/plugins/sql-driver.js +210 -12
  372. package/dist/plugins/sql-errors.d.ts +23 -0
  373. package/dist/plugins/sql-errors.js +21 -0
  374. package/dist/plugins/store-backend.d.ts +36 -1
  375. package/dist/plugins/store-backend.js +40 -14
  376. package/dist/plugins/store-contracts.d.ts +42 -0
  377. package/dist/plugins/store-contracts.js +1 -0
  378. package/dist/plugins/tidb-pool.d.ts +6 -1
  379. package/dist/plugins/tidb-pool.js +44 -10
  380. package/dist/plugins/tidb-session-storage.d.ts +3 -2
  381. package/dist/plugins/tidb-session-storage.js +2 -2
  382. package/dist/plugins/tidb-session-store.d.ts +25 -5
  383. package/dist/plugins/tidb-session-store.js +67 -21
  384. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  385. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  386. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  387. package/dist/plugins/workflow-run-store-sql.js +50 -9
  388. package/dist/posture-source.d.ts +18 -10
  389. package/dist/posture-source.js +4 -1
  390. package/dist/project-identity.d.ts +45 -6
  391. package/dist/project-identity.js +162 -8
  392. package/dist/project-layer-key.d.ts +28 -0
  393. package/dist/project-layer-key.js +29 -0
  394. package/dist/question.js +3 -2
  395. package/dist/read-deny-entry.d.ts +25 -0
  396. package/dist/read-deny-entry.js +12 -0
  397. package/dist/read-face-posture.d.ts +37 -11
  398. package/dist/read-face-posture.js +32 -3
  399. package/dist/request-key-closure.d.ts +7 -0
  400. package/dist/request-key-closure.js +9 -1
  401. package/dist/request-tool-axes.d.ts +12 -0
  402. package/dist/request-tool-axes.js +20 -0
  403. package/dist/request-value-closure.d.ts +5 -0
  404. package/dist/request-value-closure.js +11 -3
  405. package/dist/rule-entry-list.d.ts +26 -2
  406. package/dist/rule-entry-list.js +72 -12
  407. package/dist/rules-consent.js +3 -4
  408. package/dist/run-acceptance.d.ts +19 -3
  409. package/dist/run-acceptance.js +13 -1
  410. package/dist/run-local.js +37 -6
  411. package/dist/runs.d.ts +12 -10
  412. package/dist/runs.js +25 -6
  413. package/dist/runtime-caps-resolver.d.ts +16 -6
  414. package/dist/runtime-caps-resolver.js +18 -5
  415. package/dist/sandbox-pkg-source.d.ts +19 -1
  416. package/dist/sandbox-pkg-source.js +29 -7
  417. package/dist/sealed-key.d.ts +32 -0
  418. package/dist/sealed-key.js +55 -1
  419. package/dist/security.d.ts +95 -15
  420. package/dist/security.js +52 -9
  421. package/dist/session-error-wire.d.ts +31 -7
  422. package/dist/session-error-wire.js +9 -2
  423. package/dist/session-sync-kernel.d.ts +37 -10
  424. package/dist/session-sync-kernel.js +10 -0
  425. package/dist/session-titled-frame.d.ts +33 -0
  426. package/dist/session-titled-frame.js +4 -0
  427. package/dist/session-titler.d.ts +9 -1
  428. package/dist/session-titler.js +15 -4
  429. package/dist/spec-fields.js +21 -2
  430. package/dist/sql-engine-posture.d.ts +3 -1
  431. package/dist/task-a2a.js +4 -3
  432. package/dist/task-cwd.d.ts +1 -1
  433. package/dist/task-mcp.d.ts +5 -5
  434. package/dist/task-mcp.js +27 -74
  435. package/dist/task-settings.d.ts +93 -88
  436. package/dist/task-settings.js +71 -63
  437. package/dist/text-bidi.d.ts +0 -4
  438. package/dist/text-bidi.js +1 -3
  439. package/dist/tighten-only-seats.d.ts +150 -0
  440. package/dist/tighten-only-seats.js +196 -0
  441. package/dist/tool-approval.d.ts +451 -136
  442. package/dist/tool-approval.js +733 -256
  443. package/dist/tool-rule-names.d.ts +6 -0
  444. package/dist/trace/core-keyset-guard.d.ts +108 -25
  445. package/dist/trace/exit-seat.d.ts +40 -0
  446. package/dist/trace/exit-seat.js +130 -0
  447. package/dist/trace/ledger-events.d.ts +6 -3
  448. package/dist/trace/project.d.ts +98 -13
  449. package/dist/trace/project.js +57 -32
  450. package/dist/trace/projection-table.d.ts +100 -0
  451. package/dist/trace/projection-table.js +130 -0
  452. package/dist/trace/redact.d.ts +57 -0
  453. package/dist/trace/redact.js +962 -168
  454. package/dist/trace/self-minted-wire.d.ts +349 -1
  455. package/dist/trace/self-minted-wire.js +118 -1
  456. package/dist/trace/wire-projection-faces.d.ts +304 -2
  457. package/dist/trace/wire-projection-faces.js +160 -13
  458. package/dist/url-refusal.js +2 -0
  459. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  460. package/dist/web-admin-client/apply-effective.js +56 -5
  461. package/dist/web-admin-client/facade.d.ts +1 -1
  462. package/dist/web-admin-client/facade.js +1 -1
  463. package/dist/web-admin-client/read-face.js +5 -9
  464. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  465. package/dist/web-admin-client/skills-mcp.js +6 -8
  466. package/dist/web-admin-client/types.d.ts +1 -1
  467. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
  468. package/package.json +8 -4
  469. package/scripts/drain-check-local.mjs +34 -2
  470. package/scripts/drain-check.mysql.sql +20 -2
  471. package/scripts/drain-check.pg.sql +20 -2
  472. package/dist/mcp-tool-faces.d.ts +0 -69
  473. package/dist/mcp-tool-faces.js +0 -106
@@ -7,8 +7,9 @@ import { FileWorkflowJournalStore } from "@sema-agent/core";
7
7
  import { fileSessionCaptureRecordStore } from "@sema-agent/core";
8
8
  import { TiDBOutcomeLedger, PgOutcomeLedger } from "./outcome-ledger-sql.js";
9
9
  import { FileOutcomeSink } from "./file-outcome-sink.js";
10
- import { TiDBWorkflowRunStore, TiDBWorkflowCompletionInbox, TiDBWorkflowNotifyJournalStore, TiDBWorkflowAgentSessionIndex, PgWorkflowRunStore, PgWorkflowCompletionInbox, PgWorkflowNotifyJournalStore, PgWorkflowAgentSessionIndex } from "./workflow-run-store-sql.js";
10
+ import { TiDBWorkflowRunStore, TiDBWorkflowCompletionInbox, TiDBWorkflowNotifyJournalStore, TiDBWorkflowAgentSessionIndex, SqlWorkflowParkDecisionStore, PgWorkflowRunStore, PgWorkflowCompletionInbox, PgWorkflowNotifyJournalStore, PgWorkflowAgentSessionIndex } from "./workflow-run-store-sql.js";
11
11
  import { FileRunStore } from "./file-run-store.js";
12
+ import { LocalWorkflowParkDecisionStore } from "../orchestration/workflow-park-decision.js";
12
13
  import { StoreBootRefusalError } from "./store-contracts.js";
13
14
  import { buildStoreOpenRefusalTail } from "./store-open-refusal.js";
14
15
  import { acquireLocalBackendWithWait } from "./store-lock-wait.js";
@@ -19,6 +20,7 @@ import { LocalCheckpointStore } from "./local-checkpoint-store.js";
19
20
  import { FileResumeAnchorStore } from "./file-resume-anchor-store.js";
20
21
  import { TiDBApprovalExemptionStore, PgApprovalExemptionStore, FileApprovalExemptionStore } from "./approval-exemption-store.js";
21
22
  import { TiDBApprovalNonceStore, PgApprovalNonceStore, FileApprovalNonceStore } from "./approval-nonce-store.js";
23
+ import { TiDBOrgDeliveryQuotaStore, PgOrgDeliveryQuotaStore, FileOrgDeliveryQuotaStore } from "./org-delivery-quota-store.js";
22
24
  import { TiDBApprovalAskStore, PgApprovalAskStore } from "./approval-ask-store-sql.js";
23
25
  import { FileApprovalAskStore } from "./approval-ask-store-file.js";
24
26
  import { TiDBMemoryOptOutGrantStore, PgMemoryOptOutGrantStore } from "./memory-optout-grant-store-sql.js";
@@ -61,7 +63,7 @@ import { openWithinWait } from "./store-open-wait.js";
61
63
  function historyBlobBackend(config, dialect, pool) {
62
64
  const s = config.snapshotBlobStore;
63
65
  if (!s)
64
- return new SqlBlobBackend(dialect, pool, config.snapshotBlobSqlMaxBytes, FILE_HISTORY_BLOB_TABLE);
66
+ return new SqlBlobBackend(dialect, pool, config.snapshotBlobSqlMaxBytes, FILE_HISTORY_BLOB_TABLE, createLogger(config.logLevel));
65
67
  return new MinioBlobBackend({
66
68
  endpoint: s.endpoint,
67
69
  bucket: s.bucket,
@@ -75,7 +77,7 @@ function historyBlobBackend(config, dialect, pool) {
75
77
  function legacyBlobBackend(config, dialect, pool) {
76
78
  const s = config.snapshotBlobStore;
77
79
  if (!s)
78
- return new SqlBlobBackend(dialect, pool, config.snapshotBlobSqlMaxBytes);
80
+ return new SqlBlobBackend(dialect, pool, config.snapshotBlobSqlMaxBytes, undefined, createLogger(config.logLevel));
79
81
  return new MinioBlobBackend({
80
82
  endpoint: s.endpoint,
81
83
  bucket: s.bucket,
@@ -121,10 +123,12 @@ class TiDBBackend {
121
123
  }
122
124
  ensureSchema() { return ensureTidbSchema(this.pool); }
123
125
  close() { return this.pool.end(); }
124
- run() { return new TiDBRunStore(this.pool); }
126
+ run() { return (this.runStoreInst ??= new TiDBRunStore(this.pool)); }
127
+ runStoreInst;
125
128
  resumeAnchor() { return new TiDBResumeAnchorStore(this.pool); }
126
129
  approvalExemption() { return new TiDBApprovalExemptionStore(this.pool); }
127
130
  approvalNonce() { return new TiDBApprovalNonceStore(this.pool); }
131
+ orgDeliveryQuota() { return new TiDBOrgDeliveryQuotaStore(this.pool); }
128
132
  approvalAsk() { return new TiDBApprovalAskStore(this.pool); }
129
133
  permissionRule() { return createSqlPermissionRuleStores(mysqlDriver(this.pool)); }
130
134
  sendFileLedger() { return new TiDBSendFileLedger(this.pool); }
@@ -142,8 +146,11 @@ class TiDBBackend {
142
146
  completionInbox(onWarn) { return new TiDBWorkflowCompletionInbox(this.pool, onWarn); }
143
147
  notifyJournal() { return new TiDBWorkflowNotifyJournalStore(this.pool); }
144
148
  workflowAgentSessionIndex() { return new TiDBWorkflowAgentSessionIndex(this.pool); }
149
+ workflowParkDecisions() { return (this.parkDecisionsInst ??= new SqlWorkflowParkDecisionStore(this.run())); }
150
+ parkDecisionsInst;
145
151
  sharedMemoryStore(authorizer) { return new TiDBSharedMemoryStore(this.pool, authorizer); }
146
152
  checkpoint(logger) { return new TiDBCheckpointStore(this.pool, logger); }
153
+ checkpointReadView() { return this.checkpoint(); }
147
154
  toolResult() { return new TiDBToolResultStore(this.pool); }
148
155
  imageIndex() { return new TiDBImageIndex(this.pool); }
149
156
  imageBake() { return new TiDBImageBake(this.pool); }
@@ -154,7 +161,7 @@ class TiDBBackend {
154
161
  breaker(onWriteFail) { return new TiDBBreakerState(this.pool, onWriteFail); }
155
162
  costQuota(limitMicroUsd, windowMs, onDegraded) { return new TiDBCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
156
163
  rateLimiter(limit, onDegraded) { return new TiDBRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
157
- session() { return new TiDBSessionStore(this.pool); }
164
+ session() { return new TiDBSessionStore(this.pool, { log: createLogger(this.config.logLevel) }); }
158
165
  adoptionLog() { return new TiDBAdoptionLogStore(this.pool); }
159
166
  mysqlPool() { return this.pool; }
160
167
  pgPool() { return undefined; }
@@ -170,10 +177,12 @@ class PgBackend {
170
177
  }
171
178
  ensureSchema() { return ensurePgSchema(this.pool); }
172
179
  close() { return this.pool.end(); }
173
- run() { return new PgRunStore(this.pool); }
180
+ run() { return (this.runStoreInst ??= new PgRunStore(this.pool)); }
181
+ runStoreInst;
174
182
  resumeAnchor() { return new PgResumeAnchorStore(this.pool); }
175
183
  approvalExemption() { return new PgApprovalExemptionStore(this.pool); }
176
184
  approvalNonce() { return new PgApprovalNonceStore(this.pool); }
185
+ orgDeliveryQuota() { return new PgOrgDeliveryQuotaStore(this.pool); }
177
186
  approvalAsk() { return new PgApprovalAskStore(this.pool); }
178
187
  permissionRule() { return createSqlPermissionRuleStores(pgDriver(this.pool)); }
179
188
  sendFileLedger() { return new PgSendFileLedger(this.pool); }
@@ -191,8 +200,11 @@ class PgBackend {
191
200
  completionInbox(onWarn) { return new PgWorkflowCompletionInbox(this.pool, onWarn); }
192
201
  notifyJournal() { return new PgWorkflowNotifyJournalStore(this.pool); }
193
202
  workflowAgentSessionIndex() { return new PgWorkflowAgentSessionIndex(this.pool); }
203
+ workflowParkDecisions() { return (this.parkDecisionsInst ??= new SqlWorkflowParkDecisionStore(this.run())); }
204
+ parkDecisionsInst;
194
205
  sharedMemoryStore(authorizer) { return new PgSharedMemoryStore(this.pool, authorizer); }
195
206
  checkpoint(logger) { return new PgCheckpointStore(this.pool, logger); }
207
+ checkpointReadView() { return this.checkpoint(); }
196
208
  toolResult() { return new PgToolResultStore(this.pool); }
197
209
  imageIndex() { return new PgImageIndex(this.pool); }
198
210
  imageBake() { return new PgImageBake(this.pool); }
@@ -203,7 +215,7 @@ class PgBackend {
203
215
  breaker(onWriteFail) { return new PgBreakerState(this.pool, onWriteFail); }
204
216
  costQuota(limitMicroUsd, windowMs, onDegraded) { return new PgCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
205
217
  rateLimiter(limit, onDegraded) { return new PgRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
206
- session() { return new PgSessionStore(this.pool); }
218
+ session() { return new PgSessionStore(this.pool, { log: createLogger(this.config.logLevel) }); }
207
219
  adoptionLog() { return new PgAdoptionLogStore(this.pool); }
208
220
  mysqlPool() { return undefined; }
209
221
  pgPool() { return this.pool; }
@@ -217,6 +229,7 @@ class LocalBackend {
217
229
  resumeAnchorStore;
218
230
  approvalExemptionStore;
219
231
  approvalNonceStore;
232
+ orgDeliveryQuotaStore;
220
233
  approvalAskStore;
221
234
  sendFileLedgerStore;
222
235
  userInputLedgerStore;
@@ -225,6 +238,7 @@ class LocalBackend {
225
238
  onWarn;
226
239
  onError;
227
240
  discloseWarn;
241
+ parkDecisionLedgerRoot;
228
242
  constructor(root, config, onWarn, onError) {
229
243
  this.onWarn = onWarn;
230
244
  this.discloseWarn = onWarn ?? ((msg, meta) => console.warn(msg, meta));
@@ -249,9 +263,11 @@ class LocalBackend {
249
263
  strictHydrate: config.runStoreStrictHydrate,
250
264
  });
251
265
  this.sessionStore = new LocalSessionStore(createLocalSessionRepo(this.fileBackend.root), this.runStore, this.fileBackend.root, { log: createLogger(config.logLevel) });
266
+ this.parkDecisionLedgerRoot = config.workflowRunStoreBackend === "memory" ? undefined : join(this.fileBackend.root, "workflows");
252
267
  this.resumeAnchorStore = new FileResumeAnchorStore(this.fileBackend.root);
253
268
  this.approvalExemptionStore = new FileApprovalExemptionStore(this.fileBackend.root, { warn: corruptReadWarn });
254
269
  this.approvalNonceStore = new FileApprovalNonceStore(this.fileBackend.root, { warn: corruptReadWarn });
270
+ this.orgDeliveryQuotaStore = new FileOrgDeliveryQuotaStore(this.fileBackend.root, { warn: corruptReadWarn });
255
271
  this.approvalAskStore = new FileApprovalAskStore(this.fileBackend.root);
256
272
  this.sendFileLedgerStore = new FileSendFileLedger(this.fileBackend.root);
257
273
  this.userInputLedgerStore = new FileUserInputLedgerStore(this.fileBackend.root);
@@ -284,9 +300,14 @@ class LocalBackend {
284
300
  await this.fileBackend.dispose();
285
301
  }
286
302
  run() { return this.runStore; }
303
+ workflowParkDecisions() {
304
+ return (this.parkDecisionsInst ??= new LocalWorkflowParkDecisionStore(this.runStore, this.parkDecisionLedgerRoot));
305
+ }
306
+ parkDecisionsInst;
287
307
  resumeAnchor() { return this.resumeAnchorStore; }
288
308
  approvalExemption() { return this.approvalExemptionStore; }
289
309
  approvalNonce() { return this.approvalNonceStore; }
310
+ orgDeliveryQuota() { return this.orgDeliveryQuotaStore; }
290
311
  approvalAsk() { return this.approvalAskStore; }
291
312
  permissionRule() {
292
313
  if (!this.permissionRuleStoresInst) {
@@ -300,20 +321,25 @@ class LocalBackend {
300
321
  permissionRuleStoresInst;
301
322
  sendFileLedger() { return this.sendFileLedgerStore; }
302
323
  userInputLedger() { return this.userInputLedgerStore; }
324
+ checkpointStoreOf() {
325
+ return (this.checkpointStoreInst ??= new LocalCheckpointStore(this.fileBackend.root, {
326
+ getActiveTaskId: (sessionId) => this.runStore.getActiveTaskId(sessionId),
327
+ ownerOf: (sessionId) => this.sessionStore.ownerOf(sessionId),
328
+ }));
329
+ }
303
330
  checkpoint() {
304
- if (!this.checkpointStoreInst) {
305
- const cs = new LocalCheckpointStore(this.fileBackend.root, {
306
- getActiveTaskId: (sessionId) => this.runStore.getActiveTaskId(sessionId),
307
- ownerOf: (sessionId) => this.sessionStore.ownerOf(sessionId),
308
- });
309
- this.checkpointStoreInst = cs;
331
+ const cs = this.checkpointStoreOf();
332
+ if (!this.checkpointProbeWired) {
333
+ this.checkpointProbeWired = true;
310
334
  this.runStore.setCheckpointProbe({
311
335
  hasPending: async (sessionId) => (await cs.findPendingTokenBySession(sessionId)) !== null,
312
336
  hasExpired: (sessionId) => cs.hasExpiredBySession(sessionId),
313
337
  });
314
338
  }
315
- return this.checkpointStoreInst;
339
+ return cs;
316
340
  }
341
+ checkpointReadView() { return this.checkpointStoreOf(); }
342
+ checkpointProbeWired = false;
317
343
  checkpointStoreInst;
318
344
  sessionPolicy() { return this.fileBackend.sessionPolicyStore; }
319
345
  fileUsageWindowStore() { return this.fileBackend.usageWindowStore; }
@@ -183,6 +183,22 @@ export declare function readLegacyHolderDeathCode(row: {
183
183
  * 正常失败(别的码 / 无码)/ park 过期收割(`approval.expired`:腿 park 过)都**不是**死亡判决。
184
184
  */
185
185
  export declare function isHolderDeathVerdict(run: Pick<RunRecord, "status" | "errorCode">): boolean;
186
+ /**
187
+ * `listRuns` 的查询(三只 run 店同一份形)。过滤都是精确匹配(`""` 是合法的精确值,不是「不滤」—— B10)。
188
+ * `owner`:串 = 恰这一个主体的 run;`null` = 属主为 null 的 run(不带主体提交的任务:匿名 / 单用户 —— 审批读面的公共 scope `_` 由调用方译成它,店里不认 `_`);
189
+ * 缺席 = 不按属主过滤。SQL 两方言 `null` ⇒ `owner IS NULL`(`owner = NULL` 恒不为真)。
190
+ */
191
+ export interface RunListQuery {
192
+ status?: string;
193
+ jobId?: string;
194
+ source?: string;
195
+ owner?: string | null;
196
+ cursor?: {
197
+ createdAt: string;
198
+ taskId: string;
199
+ };
200
+ limit: number;
201
+ }
186
202
  export interface RunRecord {
187
203
  taskId: string;
188
204
  sessionId: string;
@@ -272,6 +288,32 @@ export type CreateRunOutcome = {
272
288
  existing?: undefined;
273
289
  activeTaskId: string;
274
290
  };
291
+ /** SQL 认领栅栏的结局:`held: false` = 认领此刻不是这条 run 的(或它已不在跑),`write` 一次都没调。 */
292
+ export type ClaimFenced<T> = {
293
+ readonly held: true;
294
+ readonly value: T;
295
+ } | {
296
+ readonly held: false;
297
+ };
298
+ /**
299
+ * 进程内栅栏 `write` 交回的**落定记号**。`write` 的型是 `() => FenceLanded`:只有**同步**交回这只记号的回调编得过 —— async 回调交回的是
300
+ * `Promise<FenceLanded>`(把真正的写挪到栅栏判完、放开之后的别的微任务里,「判认领与落行同一拍」就不成立了),不是它;栅栏方法没有类型参数,
301
+ * 显式写宽(`<unknown>` / `<void>` / `<object>`)无处可写,回调把返回标宽也编不过。要带出值,用闭包。
302
+ */
303
+ export declare const FENCE_LANDED: unique symbol;
304
+ export type FenceLanded = typeof FENCE_LANDED;
305
+ /**
306
+ * **宿主会话的认领当写栅栏用 —— 进程内那一形**(属主 = run 店;Memory / File 两孪生同律)。认领 = 会话 → 持有它的 taskId,
307
+ * 由 {@link CreateRunOutcome} 的建行(或续跑腿的 markResuming)取得、终局 / 收割放掉 —— 「这次受理还持有会话吗」只问它,不另记身份。
308
+ *
309
+ * `whileHoldingClaim(sessionId, taskId, write)`:**只有** `taskId` 此刻持有 `sessionId` 的认领、且这条 run 是活的(`running`)时才调 `write`、答 `true`,
310
+ * 同一拍把这条 run 的活性戳刷新(一次栅栏写也是一次心跳);否则一次都不调、答 `false`。整只是**同步**的 —— `write` 也是:判、刷新、`write` 落定是同一个同步段,
311
+ * 中间没有让出,所以认领在 `write` 落定之前挪不动(这一条由类型担保 —— `write` 必须同步交回 {@link FENCE_LANDED},不靠「这里恰好没有 await」)。
312
+ * SQL 孪生的栅栏是一个事务(`run-store-sql.ts` `SqlRunStore.whileHoldingClaim`,`write` 收那只事务连接,结局 {@link ClaimFenced}),不是本型。
313
+ */
314
+ export interface InProcessClaimFence {
315
+ whileHoldingClaim(sessionId: string, taskId: string, write: () => FenceLanded): boolean;
316
+ }
275
317
  /**
276
318
  * `RunRecord["status"]`(= `"running" | TaskStatus`)的**唯一分类表** —— 词表的属主 = 判据的属主,
277
319
  * 与类型定义同住一个文件。四个类:
@@ -20,6 +20,7 @@ export function readLegacyHolderDeathCode(row) {
20
20
  export function isHolderDeathVerdict(run) {
21
21
  return run.status === "failed" && run.errorCode !== null && RUN_HOLDER_DEATH_CODE_WORDS.has(run.errorCode);
22
22
  }
23
+ export const FENCE_LANDED = Symbol("in-process-claim-fence.landed");
23
24
  export const RUN_STATUS_CLASS = {
24
25
  running: "live",
25
26
  completed: "succeeded",
@@ -23,7 +23,12 @@ export declare function tidbPoolOptions(tidb: NonNullable<ServiceConfig["tidb"]>
23
23
  * · `lib/base/pool.js` / `lib/promise/pool.js` —— 池层与 promise 包装层都**不**把连接 error 再
24
24
  * emit 到池对象上(池层只 emit acquire/connection/enqueue/release)。
25
25
  * ⇒ 空闲连接被 KILL / 故障切换杀掉 = 库内吞并淘汰,进程不死、池自愈。真 TiDB KILL 钉在
26
- * `test/pool-idle-error-db-integration.test.ts` 第③格。升 mysql2 大版时按上面三坐标复核。 */
26
+ * `test/pool-idle-error-db-integration.test.ts` 第③格。升 mysql2 大版时按上面三坐标复核。
27
+ *
28
+ * **另一种病不在上面三坐标里 —— 库回完一个错误包随后才关连接**(1153 语句超过 `max_allowed_packet`:库读完包头就拒、回错、关连接):
29
+ * mysql2 的 `pool.query` 只看见「这条语句完了」,连接照常归还,库的 FIN / RST 晚一拍才到,紧接着的下一笔借到这条半死的连接、答 `write ECONNRESET`
30
+ * (`lib/base/pool.js` 的 `query` 只对只读节点三个错误号销毁连接)。所以交出去的池是 {@link createCloseAwarePool} 包过的:池上的裸语句改为每条租一条连接,
31
+ * 收到 `isConnectionUnusableError` 一类的错先销毁再抛(借出给店的连接走同一只守卫,`sql-driver.ts` 的 `leaseOf`)。 */
27
32
  export declare function createTidbPool(config: ServiceConfig): Pool;
28
33
  /**
29
34
  * Idempotent schema. `session_event` is the append-only L1 event log (positional slicing by
@@ -9,6 +9,7 @@ import { TIDB_RETENTION_STATEMENTS } from "./retention-store-sql.js";
9
9
  import { SESSION_META_INCARNATION_COLUMN } from "../session-incarnation.js";
10
10
  import { SESSION_RULES_COLUMN } from "./session-rules-column.js";
11
11
  import { SESSION_TITLE_MAX_CHARS } from "../session-row.js";
12
+ import { PRINCIPAL_MAX_UTF8_BYTES } from "../security.js";
12
13
  import { ensureIndexes } from "./ensure-index.js";
13
14
  import { CENTRAL_SCHEMA_INDEXES } from "./schema-indexes.js";
14
15
  import { SESSION_INDEXES } from "./pg-session-storage.js";
@@ -24,6 +25,7 @@ import { PERMISSION_RULE_INDEXES } from "./permission-rule-store-sql.js";
24
25
  import { RETENTION_INDEXES } from "./retention-store-sql.js";
25
26
  import { LEADER_RUN_INDEXES } from "./leader-run-store-sql.js";
26
27
  import { pollUntilLocked } from "./ensure-schema-lock-policy.js";
28
+ import { createCloseAwarePool } from "./sql-driver.js";
27
29
  export function tidbPoolOptions(tidb) {
28
30
  const { host, port, user, password, database, connectionLimit } = tidb;
29
31
  const limit = connectionLimit ?? 10;
@@ -48,7 +50,7 @@ export function createTidbPool(config) {
48
50
  throw new Error("TiDB config missing. Set MYSQL_HOST / MYSQL_USER / MYSQL_DATABASE (and MYSQL_PASSWORD/PORT) " +
49
51
  "when SESSION_BACKEND=mysql.");
50
52
  }
51
- return mysql.createPool(tidbPoolOptions(config.tidb));
53
+ return createCloseAwarePool(mysql.createPool(tidbPoolOptions(config.tidb)));
52
54
  }
53
55
  export const SCHEMA_STATEMENTS = [
54
56
  `CREATE TABLE IF NOT EXISTS session_meta (
@@ -193,7 +195,7 @@ export const SCHEMA_STATEMENTS = [
193
195
  -- ever served an operator's ad-hoc WHERE error_code LIKE 'budget.%', while every task_run write paid its
194
196
  -- maintenance cost. If you need that ad-hoc query, build the index temporarily and drop it after.
195
197
  ) COLLATE utf8mb4_bin`,
196
- `CREATE TABLE IF NOT EXISTS circuit_breaker (
198
+ `CREATE TABLE IF NOT EXISTS breaker_state (
197
199
  breaker_key VARCHAR(190) NOT NULL,
198
200
  phase VARCHAR(16) NOT NULL,
199
201
  failures INT NOT NULL DEFAULT 0,
@@ -373,6 +375,25 @@ export const SCHEMA_STATEMENTS = [
373
375
  expires_at_ms BIGINT NOT NULL,
374
376
  PRIMARY KEY (session_id, nonce),
375
377
  KEY idx_approval_nonce_expires (expires_at_ms)
378
+ ) COLLATE utf8mb4_bin`,
379
+ `CREATE TABLE IF NOT EXISTS org_delivery_sender_quota (
380
+ from_scope VARBINARY(${PRINCIPAL_MAX_UTF8_BYTES}) NOT NULL,
381
+ window_start_ms BIGINT NOT NULL,
382
+ attempts INT NOT NULL,
383
+ PRIMARY KEY (from_scope)
384
+ ) COLLATE utf8mb4_bin`,
385
+ `CREATE TABLE IF NOT EXISTS org_delivery_recipient_quota (
386
+ to_scope VARBINARY(${PRINCIPAL_MAX_UTF8_BYTES}) NOT NULL,
387
+ window_start_ms BIGINT NOT NULL,
388
+ delivered INT NOT NULL,
389
+ PRIMARY KEY (to_scope)
390
+ ) COLLATE utf8mb4_bin`,
391
+ `CREATE TABLE IF NOT EXISTS org_delivery_pair_quota (
392
+ to_scope VARBINARY(${PRINCIPAL_MAX_UTF8_BYTES}) NOT NULL,
393
+ from_scope VARBINARY(${PRINCIPAL_MAX_UTF8_BYTES}) NOT NULL,
394
+ window_start_ms BIGINT NOT NULL,
395
+ delivered INT NOT NULL,
396
+ PRIMARY KEY (to_scope, from_scope)
376
397
  ) COLLATE utf8mb4_bin`,
377
398
  `CREATE TABLE IF NOT EXISTS user_input_acceptance (
378
399
  accept_id VARCHAR(64) NOT NULL,
@@ -517,18 +538,19 @@ export const SCHEMA_STATEMENTS = [
517
538
  ) COLLATE utf8mb4_bin`,
518
539
  `CREATE TABLE IF NOT EXISTS workflow_journal (
519
540
  run_id VARCHAR(191) NOT NULL,
541
+ incarnation VARCHAR(64) NOT NULL,
520
542
  ordinal INT NOT NULL,
521
- -- scope (SVC-2 / CORE-9 audit BLOCKER): the table predates scope — it was added when the
522
- -- WorkflowJournalStore seam grew a scope param for cross-tenant resume isolation. Every new append writes the
523
- -- run's REAL scope. EXISTING (pre-migration) rows carry '' = CROSS-SCOPE ORPHANS, harmless by construction: a
524
- -- real resume filters WHERE scope = <caller>, so the blank-scope residue never matches a tenant. The
525
- -- NOT NULL DEFAULT '' is what let the original ALTER succeed on a table that already had rows — keep the
526
- -- default so the "'' = orphan, never a tenant" reading stays true.
527
- scope VARCHAR(190) NOT NULL DEFAULT '',
528
543
  call_key VARCHAR(255) NOT NULL,
529
544
  result MEDIUMTEXT NOT NULL,
530
545
  created_at_ms BIGINT NOT NULL,
531
- PRIMARY KEY (run_id, ordinal)
546
+ PRIMARY KEY (run_id, incarnation, ordinal)
547
+ ) COLLATE utf8mb4_bin`,
548
+ `CREATE TABLE IF NOT EXISTS workflow_journal_owner (
549
+ run_id VARCHAR(191) NOT NULL,
550
+ scope VARCHAR(190) NOT NULL,
551
+ incarnation VARCHAR(64) NOT NULL,
552
+ appended_at_ms BIGINT NOT NULL,
553
+ PRIMARY KEY (run_id)
532
554
  ) COLLATE utf8mb4_bin`,
533
555
  `CREATE TABLE IF NOT EXISTS workflow_resume_claim (
534
556
  source_run_id VARCHAR(191) NOT NULL,
@@ -556,6 +578,18 @@ export const SCHEMA_STATEMENTS = [
556
578
  scope VARCHAR(190) NOT NULL,
557
579
  PRIMARY KEY (session_id, run_id),
558
580
  KEY idx_wfagentsess_run (run_id)
581
+ ) COLLATE utf8mb4_bin`,
582
+ `CREATE TABLE IF NOT EXISTS workflow_park_decision (
583
+ token VARCHAR(120) NOT NULL,
584
+ run_id VARCHAR(191) NOT NULL,
585
+ host_session_id VARCHAR(190) NOT NULL,
586
+ outcome MEDIUMTEXT NOT NULL,
587
+ fingerprint VARCHAR(64) NOT NULL,
588
+ delivered_task_id VARCHAR(191) NOT NULL,
589
+ accepted_at_ms BIGINT NOT NULL,
590
+ PRIMARY KEY (token),
591
+ KEY idx_wfparkdec_host (host_session_id),
592
+ KEY idx_wfparkdec_run (run_id)
559
593
  ) COLLATE utf8mb4_bin`,
560
594
  `CREATE TABLE IF NOT EXISTS workflow_completion_inbox (
561
595
  seq BIGINT NOT NULL AUTO_INCREMENT,
@@ -27,8 +27,9 @@ export declare class TiDBSessionStorage extends BaseSessionStorage {
27
27
  * [ref] S3 [ref] pull seam — `SessionStorage.getEpochAnchor?()`: the nearest prompt-epoch carrier BELOW the
28
28
  * bounded-wake floor, AS STORED (engine 7.52.0: `Promise<unknown>`; the engine reads the record, a retired shape
29
29
  * included). The engine's `Session.getPromptEpoch()` asks for it only when the in-window branch walk finds no carrier
30
- * (a rewind/resumeAt cut inside the kept tail; compactions without a restatement). The one reading of both SQL twins
31
- * is {@link scanEpochAnchor}; this backend supplies its page query (keyset-paged to exhaustion).
30
+ * (a rewind/resumeAt cut inside the kept tail; compactions without a restatement). The one reading of every windowed
31
+ * storage (both SQL twins, the local leg's view window) is {@link scanEpochAnchor}; this backend supplies its page query
32
+ * (keyset-paged to exhaustion) and decodes its own rows.
32
33
  */
33
34
  getEpochAnchor(): Promise<unknown>;
34
35
  /**
@@ -24,10 +24,10 @@ export class TiDBSessionStorage extends BaseSessionStorage {
24
24
  this.floorSeq = floorSeq;
25
25
  }
26
26
  async getEpochAnchor() {
27
- return scanEpochAnchor(this.sessionId, this.floorSeq, async (beforeSeq) => {
27
+ return scanEpochAnchor(this.floorSeq, async (beforeSeq) => {
28
28
  const [rows] = await this.pool.query(SESSION_EVENT_LATEST_BY_TYPE_SQL.epochAnchor, [this.sessionId, beforeSeq]);
29
29
  return rows.map((r) => ({ seq: r.seq, payload: r.payload }));
30
- });
30
+ }, (row) => decodeSessionEventPayload(row.payload, this.sessionId, row.seq));
31
31
  }
32
32
  static async wake(pool, sessionId) {
33
33
  const [metaRows] = await pool.query(`SELECT created_at, leaf_id, leaf_seq, incarnation, ${PLACEMENT_SELECT_COLUMNS} FROM session_meta WHERE session_id = ?`, [sessionId]);
@@ -10,12 +10,13 @@
10
10
  * Persistence only. **Tenant authorization (who may wake which session) lives in the service layer**
11
11
  * (`src/security.ts`), which uses `ownerOf` / `register` below. The store does not infer identity.
12
12
  */
13
- import { type AcquiredSession, type PlacedSessionRow, type SessionPlacement, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
13
+ import { type AcquiredSession, type PlacedSessionRow, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
14
14
  import type { Pool } from "mysql2/promise";
15
15
  import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
16
16
  import { type SessionFace } from "../session-incarnation.js";
17
17
  import type { SessionSummary } from "./store-contracts.js";
18
18
  import type { SessionRowPatch } from "../session-row.js";
19
+ import { type OrphanOwnerLog } from "../delegated-session-owner.js";
19
20
  import { type PlacementAcquireOpts } from "./session-placement.js";
20
21
  import { type StagingHandle } from "../session-sync-kernel.js";
21
22
  export declare class TiDBSessionStore implements SessionStore {
@@ -33,14 +34,33 @@ export declare class TiDBSessionStore implements SessionStore {
33
34
  };
34
35
  /** In-flight acquisitions keyed by id, so concurrent acquire(sameId) in one process share one. */
35
36
  private readonly pending;
36
- constructor(pool: Pool);
37
+ /** 委派子代孤行那一行 warn 的去处(`delegated-session-owner.ts`;装配给部署的 logger,缺席 ⇒ 缺省 logger,不静默)。 */
38
+ private readonly log;
39
+ constructor(pool: Pool, opts?: {
40
+ readonly log?: OrphanOwnerLog;
41
+ });
37
42
  acquire(sessionId?: string, opts?: PlacementAcquireOpts): Promise<AcquiredSession>;
38
43
  private load;
39
- /** Idempotently create a session_meta row with an owner. Existing owner is never overwritten.
44
+ /** Idempotently create a session_meta row with an owner (core `SessionStore.register`). Existing owner is never overwritten. */
45
+ register(sessionId: string, owner: string | null): Promise<void>;
46
+ /**
47
+ * acquire 的建行(唯一一处):普通会话 ⇒ 无主(按 id 首建的既有读法);placed 会话 ⇒ 属主按委派继承律在**这一笔**里写入
48
+ * (S-1770:根会话的属主,根行不在取父,都不在 ⇒ 孤行哨兵 + warn;属主 = 判断时见证到的,读来源 → 判 → INSERT —— 读法与顺序只住在
49
+ * `delegated-session-owner.ts` 的 `createPlacedSessionRow`,本店只交既有读写口,PG 孪生同调)。
50
+ */
51
+ private createOnAcquire;
52
+ /**
53
+ * S-1770 委派继承律的**认领口**:这只会话的那一位住客(判断时读到的化身)仍是无主的 placed 行 ⇒ 写 `owner`,返回是否写成
54
+ * (句 = `CLAIM_OWNERLESS_PLACED_SQL`,只 CAS 子行自己,与停驻挂点 / M23 同一只;规则在 `delegated-session-owner.ts`)。普通会话行不在射程(placed 判别位)。
55
+ */
56
+ claimOwnerless(sessionId: string, incarnation: string, owner: string): Promise<boolean>;
57
+ /** 若干来源行的会话面(属主 + 化身)一次读回(委派继承律的来源读;本店没有的 id 不在表里;化身经与 {@link sessionFaceOf} 同一只解码,空 ⇒ 响亮拒)。 */
58
+ private sourceFaces;
59
+ /** The one session_meta INSERT. Existing owner is never overwritten.
40
60
  * [ref]:`placement` 在场时与行同一条 INSERT 落六列;ON DUPLICATE 臂是 no-op ⇒ 已存在的行的元组
41
61
  * **永不被重写**(first-write 不可变,义务 5 的写侧半场)。
42
62
  * S-790:化身与属主同一条 first-write 律 —— 新行铸一只(`mintSessionIncarnation`),已存在的行 no-op 臂一个字节不动。 */
43
- register(sessionId: string, owner: string | null, placement?: SessionPlacement): Promise<void>;
63
+ private insertMeta;
44
64
  /**
45
65
  * [ref] 义务 6 的廉价探针 —— 一行 PK SELECT:`undefined` = 普通会话或无此行。
46
66
  * (release 的冷缓存腿、包装店的暖命中准入判、诊断面共用这一只。)
@@ -167,7 +187,7 @@ export declare class TiDBSessionStore implements SessionStore {
167
187
  * We instead SELECT the entire `session_event` log ordered by seq — every durable entry, compaction floor
168
188
  * bypassed — and re-seq it dense from 0 into the new session, ids/parents/payload byte-verbatim.
169
189
  */
170
- fork(sourceId: string, owner: string | null): Promise<string | null>;
190
+ fork(sourceId: string, owner?: string | null): Promise<string | null>;
171
191
  /**
172
192
  * 2c session-sync — EXPORT a session's full durable entry log for cross-backend migration. This is the
173
193
  * READ half of {@link fork}, surfaced as the core `SessionRepo.exportEntries` seam: the WHOLE `session_event` log,
@@ -1,4 +1,4 @@
1
- import { StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry } from "@sema-agent/core";
1
+ import { StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry, admitSessionPlacement } from "@sema-agent/core";
2
2
  import { TiDBSessionStorage } from "./tidb-session-storage.js";
3
3
  import { decodeSessionEventPayload } from "./session-event-payload.js";
4
4
  import { jsonColumnText } from "./sql-json-column.js";
@@ -7,20 +7,25 @@ import { BASELINE_IO_DEADLINE_MS, decodeInstructionSource, encodeInstructionSour
7
7
  import { MANAGED_RETENTION } from "./retention-store-sql.js";
8
8
  import { carriedIncarnation, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
9
9
  import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
10
- import { assertPlacementAdmission, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SQL_SESSION_PLACEMENTS, } from "./session-placement.js";
11
- import { classifySyncRelationshipByIds, SyncConflictError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
10
+ import { CLAIM_OWNERLESS_PLACED_SQL, claimOwnerlessParams, createPlacedSessionRow } from "../delegated-session-owner.js";
11
+ import { createLogger } from "../observability/logger.js";
12
+ import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SQL_SESSION_PLACEMENTS, } from "./session-placement.js";
13
+ import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
14
+ import { assertFirstOwnerWritable, forkOwnerOf } from "./session-ownership.js";
12
15
  export class TiDBSessionStore {
13
16
  pool;
14
17
  retention = MANAGED_RETENTION;
15
18
  placements = SQL_SESSION_PLACEMENTS;
16
19
  pending = new Map();
17
- constructor(pool) {
20
+ log;
21
+ constructor(pool, opts = {}) {
18
22
  this.pool = pool;
23
+ this.log = opts.log ?? createLogger();
19
24
  }
20
25
  async acquire(sessionId, opts) {
21
26
  if (!sessionId) {
22
27
  const id = uuidv7();
23
- await this.register(id, null, opts?.placement);
28
+ await this.createOnAcquire(id, opts?.placement);
24
29
  return this.load(id, false, opts);
25
30
  }
26
31
  if (opts?.requireExisting)
@@ -28,7 +33,7 @@ export class TiDBSessionStore {
28
33
  const inflight = this.pending.get(sessionId);
29
34
  if (inflight) {
30
35
  const acquired = await inflight;
31
- assertPlacementAdmission(sessionId, await this.placementOf(sessionId), opts);
36
+ admitSessionPlacement(sessionId, await this.placementOf(sessionId), opts ?? {}, placementCapabilityOf(this));
32
37
  return acquired;
33
38
  }
34
39
  const p = this.load(sessionId, false, opts).finally(() => this.pending.delete(sessionId));
@@ -38,20 +43,45 @@ export class TiDBSessionStore {
38
43
  async load(sessionId, requireExisting = false, opts) {
39
44
  let storage = await TiDBSessionStorage.wake(this.pool, sessionId);
40
45
  if (storage) {
41
- assertPlacementAdmission(sessionId, (await storage.getMetadata()).placement, opts);
46
+ admitSessionPlacement(sessionId, (await storage.getMetadata()).placement, opts ?? {}, placementCapabilityOf(this));
42
47
  return { session: new StoredSession(storage), sessionId };
43
48
  }
44
49
  if (requireExisting)
45
50
  throw new SessionError("not_found", `session ${sessionId} not found`);
46
- await this.register(sessionId, null, opts?.placement);
51
+ await this.createOnAcquire(sessionId, opts?.placement);
47
52
  storage = await TiDBSessionStorage.wake(this.pool, sessionId);
48
53
  if (!storage) {
49
54
  throw new SessionError("storage", `session ${sessionId} vanished between register and wake (a concurrent purge won the race) — retry`);
50
55
  }
51
- assertPlacementAdmission(sessionId, (await storage.getMetadata()).placement, opts);
56
+ admitSessionPlacement(sessionId, (await storage.getMetadata()).placement, opts ?? {}, placementCapabilityOf(this));
52
57
  return { session: new StoredSession(storage), sessionId };
53
58
  }
54
- async register(sessionId, owner, placement) {
59
+ async register(sessionId, owner) {
60
+ if (typeof owner !== "string" && owner !== null) {
61
+ if ((await this.ownerOf(sessionId)) !== undefined)
62
+ return;
63
+ assertFirstOwnerWritable(sessionId, owner);
64
+ }
65
+ await this.insertMeta(sessionId, owner, undefined);
66
+ }
67
+ async createOnAcquire(sessionId, placement) {
68
+ if (placement === undefined)
69
+ return this.insertMeta(sessionId, null, undefined);
70
+ await createPlacedSessionRow(sessionId, placement, {
71
+ readFaces: (ids) => this.sourceFaces(ids),
72
+ insert: (owner) => this.insertMeta(sessionId, owner, placement),
73
+ readOwner: () => this.ownerOf(sessionId),
74
+ }, this.log);
75
+ }
76
+ async claimOwnerless(sessionId, incarnation, owner) {
77
+ const [res] = await this.pool.query(CLAIM_OWNERLESS_PLACED_SQL.tidb, [...claimOwnerlessParams(sessionId, incarnation, owner)]);
78
+ return res.affectedRows > 0;
79
+ }
80
+ async sourceFaces(ids) {
81
+ const [rows] = await this.pool.query(`SELECT session_id, owner, incarnation FROM session_meta WHERE session_id IN (${ids.map(() => "?").join(", ")})`, [...ids]);
82
+ return new Map(rows.map((r) => [String(r.session_id), sessionFaceFromRow(String(r.session_id), r.owner ?? null, r.incarnation)]));
83
+ }
84
+ async insertMeta(sessionId, owner, placement) {
55
85
  const now = new Date();
56
86
  await this.pool.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, " +
57
87
  "placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) " +
@@ -235,10 +265,11 @@ export class TiDBSessionStore {
235
265
  }
236
266
  }
237
267
  async fork(sourceId, owner) {
238
- const [metaRows] = await this.pool.query("SELECT leaf_id FROM session_meta WHERE session_id = ?", [sourceId]);
268
+ const [metaRows] = await this.pool.query("SELECT leaf_id, owner FROM session_meta WHERE session_id = ?", [sourceId]);
239
269
  if (!metaRows[0])
240
270
  return null;
241
271
  const leafId = metaRows[0].leaf_id ?? null;
272
+ const forkOwner = forkOwnerOf(owner, metaRows[0].owner ?? null) ?? null;
242
273
  const [eventRows] = await this.pool.query(`SELECT entry_id, parent_id, type, ${jsonColumnText("tidb", "payload")}, ts FROM session_event WHERE session_id = ? ORDER BY seq ASC`, [sourceId]);
243
274
  const newId = uuidv7();
244
275
  const now = new Date();
@@ -250,7 +281,7 @@ export class TiDBSessionStore {
250
281
  const payload = String(r.payload);
251
282
  await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES (?,?,?,?,?,?,?)", [newId, seq, r.entry_id, r.parent_id ?? null, r.type, payload, r.ts]);
252
283
  }
253
- await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES (?,?,?,?,?,?,?)", [newId, owner, now, now, leafId, eventRows.length, mintSessionIncarnation()]);
284
+ await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES (?,?,?,?,?,?,?)", [newId, forkOwner, now, now, leafId, eventRows.length, mintSessionIncarnation()]);
254
285
  await conn.commit();
255
286
  return newId;
256
287
  }
@@ -452,6 +483,23 @@ class TiDBStagingHandle {
452
483
  const conn = await this.pool.getConnection();
453
484
  try {
454
485
  await conn.beginTransaction();
486
+ const now = new Date();
487
+ const minted = mintSessionIncarnation();
488
+ if (opts.expectedOwner !== undefined) {
489
+ const [cas] = await conn.query("UPDATE session_meta SET leaf_seq = leaf_seq + 1 WHERE session_id = ? AND owner <=> ?", [this.realSessionId, opts.expectedOwner]);
490
+ if (cas.affectedRows !== 1) {
491
+ await conn.rollback();
492
+ throw new StagedCommitOwnerChangedError(this.realSessionId);
493
+ }
494
+ }
495
+ else {
496
+ await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES (?,?,?,?,NULL,0,?) ON DUPLICATE KEY UPDATE session_id = session_id", [this.realSessionId, owner, now, now, minted]);
497
+ const [claimed] = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = ? FOR UPDATE", [this.realSessionId]);
498
+ if (String(claimed[0]?.incarnation) !== minted) {
499
+ await conn.rollback();
500
+ throw new StagedCommitOwnerChangedError(this.realSessionId);
501
+ }
502
+ }
455
503
  const [stagedRows] = await conn.query("SELECT entry_id FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE", [this.stagingId]);
456
504
  const [dstRows] = await conn.query("SELECT entry_id FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE", [this.realSessionId]);
457
505
  const [dstMeta] = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = ?", [this.realSessionId]);
@@ -460,15 +508,15 @@ class TiDBStagingHandle {
460
508
  throw new SessionError("placement_refused", `session ${this.realSessionId} belongs to the ${String(dstMeta[0].placement_kind)} placement partition — a session-sync staged commit cannot overwrite a delegation transcript`);
461
509
  }
462
510
  const srcIds = stagedRows.map((r) => String(r.entry_id));
463
- const dstIds = dstMeta[0] ? dstRows.map((r) => String(r.entry_id)) : null;
511
+ const dstIds = opts.expectedOwner !== undefined ? dstRows.map((r) => String(r.entry_id)) : null;
464
512
  const rel = classifySyncRelationshipByIds(srcIds, dstIds);
465
- if ((rel.relation === "fork" || rel.relation === "stale") && opts?.resolution !== "overwrite-dst") {
513
+ if ((rel.relation === "fork" || rel.relation === "stale") && opts.resolution !== "overwrite-dst") {
466
514
  await conn.rollback();
467
515
  throw new SyncConflictError(this.realSessionId, rel);
468
516
  }
469
- if (rel.relation === "fast_forward" && opts?.resolution !== "overwrite-dst" && dstIds !== null && dstIds.length > 0) {
470
- const [dstFull] = await conn.query(`SELECT seq, ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? ORDER BY seq ASC`, [this.realSessionId]);
471
- const [stagedFull] = await conn.query(`SELECT entry_id, ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? ORDER BY seq ASC`, [this.stagingId]);
517
+ if (rel.relation === "fast_forward" && opts.resolution !== "overwrite-dst" && dstIds !== null && dstIds.length > 0) {
518
+ const [dstFull] = await conn.query(`SELECT seq, ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE`, [this.realSessionId]);
519
+ const [stagedFull] = await conn.query(`SELECT entry_id, ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE`, [this.stagingId]);
472
520
  const dstSet = new Set(dstIds);
473
521
  const dstEntries = dstFull.map((r) => decodeSessionEventPayload(r.payload, this.realSessionId, r.seq));
474
522
  const stagedShared = stagedFull
@@ -482,13 +530,11 @@ class TiDBStagingHandle {
482
530
  }
483
531
  await conn.query("DELETE FROM session_event WHERE session_id = ?", [this.realSessionId]);
484
532
  await conn.query("UPDATE session_event SET session_id = ? WHERE session_id = ?", [this.realSessionId, this.stagingId]);
485
- const [lastRow] = await conn.query(`SELECT ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? ORDER BY seq DESC LIMIT 1`, [this.realSessionId]);
533
+ const [lastRow] = await conn.query(`SELECT ${jsonColumnText("tidb", "payload")} FROM session_event WHERE session_id = ? ORDER BY seq DESC LIMIT 1 FOR UPDATE`, [this.realSessionId]);
486
534
  let leafId = null;
487
535
  if (lastRow[0])
488
536
  leafId = leafIdAfterEntry(decodeSessionEventPayload(lastRow[0].payload, this.realSessionId, "last staged row"));
489
- const now = new Date();
490
- await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES (?,?,?,?,?,?,?) " +
491
- "ON DUPLICATE KEY UPDATE owner = VALUES(owner), updated_at = VALUES(updated_at), leaf_id = VALUES(leaf_id), leaf_seq = VALUES(leaf_seq)", [this.realSessionId, owner, now, now, leafId, srcIds.length, mintSessionIncarnation()]);
537
+ await conn.query("UPDATE session_meta SET owner = ?, updated_at = ?, leaf_id = ?, leaf_seq = ? WHERE session_id = ?", [owner, now, leafId, srcIds.length, this.realSessionId]);
492
538
  const [afterMeta] = await conn.query("SELECT placement_kind, owner, incarnation FROM session_meta WHERE session_id = ?", [this.realSessionId]);
493
539
  if (afterMeta[0]?.placement_kind != null) {
494
540
  await conn.rollback();