@sema-agent/server 7.146.0 → 7.147.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (398) hide show
  1. package/MIGRATION.md +384 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +170 -66
  5. package/deploy/facts/lanes.json +7 -0
  6. package/deploy/sema-up/README.md +2 -2
  7. package/deploy/sema-up/chart/templates/_helpers.tpl +22 -0
  8. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -4
  9. package/deploy/sema-up/chart/templates/server.yaml +5 -4
  10. package/deploy/sema-up/chart/values.yaml +1 -1
  11. package/deploy/sema-up/cluster-up.sh +2 -1
  12. package/deploy/sema-up/lib/flagshape.sh +4 -0
  13. package/deploy/sema-up/lib/lanes.json +2 -5
  14. package/deploy/sema-up/sema-up.sh +12 -9
  15. package/deploy/sema-up/stack/compose.yaml +5 -4
  16. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -3
  17. package/dist/approval-card.d.ts +4 -4
  18. package/dist/approval.d.ts +3 -3
  19. package/dist/audit.js +2 -2
  20. package/dist/bench/s1/live-deps.js +3 -1
  21. package/dist/boot/crash-last.js +2 -1
  22. package/dist/boot/device-lane.js +5 -5
  23. package/dist/boot/engine-lease.d.ts +12 -11
  24. package/dist/boot/execution-env.d.ts +2 -1
  25. package/dist/boot/execution-env.js +45 -33
  26. package/dist/boot/execution-lane.d.ts +37 -9
  27. package/dist/boot/execution-lane.js +74 -20
  28. package/dist/boot/exit-boundary.d.ts +1 -1
  29. package/dist/boot/exit-last.js +2 -1
  30. package/dist/boot/governance-seams.d.ts +2 -2
  31. package/dist/boot/leader.js +1 -1
  32. package/dist/boot/memory-boundary.d.ts +11 -11
  33. package/dist/boot/memory-boundary.js +3 -3
  34. package/dist/boot/memory-consolidation.d.ts +13 -24
  35. package/dist/boot/memory-consolidation.js +0 -13
  36. package/dist/boot/org-memory.js +3 -2
  37. package/dist/boot/parked-revive-gate.d.ts +1 -1
  38. package/dist/boot/parked-revive-gate.js +1 -1
  39. package/dist/boot/reapers.js +1 -1
  40. package/dist/boot/resolve-spec.js +91 -111
  41. package/dist/boot/retention-lane.d.ts +4 -42
  42. package/dist/boot/session-faces.d.ts +1 -0
  43. package/dist/boot/session-faces.js +8 -2
  44. package/dist/boot/stage-01-config.d.ts +1 -1
  45. package/dist/boot/stage-01-config.js +6 -2
  46. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -1
  47. package/dist/boot/stage-02-web-admin-pull.js +10 -8
  48. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  49. package/dist/boot/stage-04-stores.d.ts +1 -1
  50. package/dist/boot/stage-05-execution-env.d.ts +2 -2
  51. package/dist/boot/stage-05-execution-env.js +3 -3
  52. package/dist/boot/stage-06-runners.d.ts +3 -3
  53. package/dist/boot/stage-06-runners.js +1 -3
  54. package/dist/boot/stage-07-capability-layer.d.ts +3 -3
  55. package/dist/boot/stage-07-capability-layer.js +4 -9
  56. package/dist/boot/stage-08-reapers.d.ts +3 -3
  57. package/dist/boot/stage-09-leader.d.ts +4 -3
  58. package/dist/boot/stage-10-http-server.d.ts +4 -3
  59. package/dist/boot/stage-10-http-server.js +2 -1
  60. package/dist/boot/stores.d.ts +9 -0
  61. package/dist/boot/stores.js +18 -7
  62. package/dist/boot/web-admin.js +3 -3
  63. package/dist/capabilities/center-plugins.d.ts +2 -0
  64. package/dist/capabilities/center-plugins.js +6 -2
  65. package/dist/capabilities/composite-members.d.ts +1 -0
  66. package/dist/capabilities/composite-members.js +1 -0
  67. package/dist/capabilities/execution-lane.d.ts +7 -7
  68. package/dist/capabilities/execution-lane.js +2 -2
  69. package/dist/capabilities/hands-lane.d.ts +1 -1
  70. package/dist/capabilities/scenarios.d.ts +32 -18
  71. package/dist/capabilities/scenarios.js +13 -9
  72. package/dist/capabilities/settings-contract.d.ts +29 -0
  73. package/dist/capabilities/settings-contract.js +14 -0
  74. package/dist/capabilities/skills.d.ts +5 -5
  75. package/dist/capability-facts.d.ts +38 -0
  76. package/dist/capability-facts.js +34 -1
  77. package/dist/center-credential-renewer.js +3 -2
  78. package/dist/config-catalog.js +6 -5
  79. package/dist/config-invariants.d.ts +18 -3
  80. package/dist/config-invariants.js +36 -19
  81. package/dist/config-lkg.d.ts +1 -1
  82. package/dist/config-lkg.js +5 -6
  83. package/dist/config-provider.js +2 -0
  84. package/dist/config-types.d.ts +190 -186
  85. package/dist/config.d.ts +68 -5
  86. package/dist/config.js +80 -54
  87. package/dist/degenerate-instrument.js +1 -1
  88. package/dist/deploy-facts.js +3 -2
  89. package/dist/deployment-shape.d.ts +21 -12
  90. package/dist/deployment-shape.js +24 -6
  91. package/dist/env-facts.d.ts +7 -5
  92. package/dist/env-facts.js +10 -6
  93. package/dist/execution-config-root.d.ts +5 -5
  94. package/dist/execution-config-root.js +5 -5
  95. package/dist/execution-env-purpose.d.ts +14 -0
  96. package/dist/execution-env-purpose.js +29 -0
  97. package/dist/execution-lane-caps.d.ts +49 -86
  98. package/dist/execution-lane-caps.js +52 -62
  99. package/dist/execution-placement.d.ts +18 -2
  100. package/dist/execution-placement.js +19 -0
  101. package/dist/file-history-disclosure.d.ts +2 -2
  102. package/dist/fleet/drain-entry.d.ts +17 -10
  103. package/dist/fleet/drain-entry.js +1 -1
  104. package/dist/fleet/serving-lease.js +3 -4
  105. package/dist/history-anchor.d.ts +22 -0
  106. package/dist/history-anchor.js +14 -0
  107. package/dist/hooks/command-spawn.d.ts +1 -4
  108. package/dist/hooks/command-spawn.js +0 -11
  109. package/dist/hooks/hook-runner.d.ts +52 -8
  110. package/dist/hooks/hook-runner.js +160 -36
  111. package/dist/http/approval-sweeps.d.ts +2 -5
  112. package/dist/http/approval-sweeps.js +3 -2
  113. package/dist/http/checkpoint-ctx.d.ts +17 -0
  114. package/dist/http/checkpoint-ctx.js +6 -0
  115. package/dist/http/claim-latency.d.ts +10 -0
  116. package/dist/http/claim-latency.js +12 -0
  117. package/dist/http/dispatch.js +1 -1
  118. package/dist/http/durable-run-leg.js +2 -1
  119. package/dist/http/execution-context.d.ts +3 -1
  120. package/dist/http/execution-context.js +4 -0
  121. package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
  122. package/dist/http/memory-compliance-failure-reply.js +3 -0
  123. package/dist/http/memory-store-refusal-reply.d.ts +3 -3
  124. package/dist/http/memory-store-refusal-reply.js +3 -0
  125. package/dist/http/owner-access.d.ts +11 -0
  126. package/dist/http/owner-access.js +7 -4
  127. package/dist/http/principal-gate.d.ts +3 -14
  128. package/dist/http/principal-gate.js +0 -13
  129. package/dist/http/request-guards.d.ts +1 -1
  130. package/dist/http/resume-legs.d.ts +7 -10
  131. package/dist/http/resume-legs.js +79 -50
  132. package/dist/http/rewind-files-reply.d.ts +34 -0
  133. package/dist/http/rewind-files-reply.js +36 -0
  134. package/dist/http/route-ctx.d.ts +32 -16
  135. package/dist/http/route-table.js +2 -0
  136. package/dist/http/routes/approvals-assistant.js +5 -25
  137. package/dist/http/routes/capabilities.js +8 -2
  138. package/dist/http/routes/devices.js +1 -1
  139. package/dist/http/routes/memory-origin.d.ts +1 -1
  140. package/dist/http/routes/notify-wake.js +2 -1
  141. package/dist/http/routes/projects.d.ts +70 -0
  142. package/dist/http/routes/projects.js +86 -0
  143. package/dist/http/routes/rules.js +30 -2
  144. package/dist/http/routes/runs.js +8 -8
  145. package/dist/http/routes/session-sync.js +27 -10
  146. package/dist/http/routes/sessions.d.ts +7 -1
  147. package/dist/http/routes/sessions.js +104 -2
  148. package/dist/http/routes/tasks.js +9 -3
  149. package/dist/http/server.d.ts +24 -10
  150. package/dist/http/server.js +17 -6
  151. package/dist/http/stored-body-replay.d.ts +5 -3
  152. package/dist/http/stored-body-replay.js +10 -1
  153. package/dist/http/wire-types.d.ts +19 -10
  154. package/dist/index.d.ts +2 -2
  155. package/dist/index.js +1 -1
  156. package/dist/instruction-source-notice.d.ts +1 -1
  157. package/dist/memory-embedder-reembed.d.ts +38 -0
  158. package/dist/memory-embedder-reembed.js +20 -0
  159. package/dist/memory-incident-sink.d.ts +0 -28
  160. package/dist/memory-incident-sink.js +5 -0
  161. package/dist/memory-scope.d.ts +2 -2
  162. package/dist/memory-scope.js +1 -1
  163. package/dist/memory-store-refusal.d.ts +17 -4
  164. package/dist/memory-store-refusal.js +1 -1
  165. package/dist/memory-sync-client.d.ts +2 -0
  166. package/dist/memory-sync-client.js +13 -11
  167. package/dist/memory-sync.js +1 -1
  168. package/dist/model-compat.d.ts +35 -21
  169. package/dist/model-compat.js +49 -21
  170. package/dist/model-entry-refusal.d.ts +11 -1
  171. package/dist/model-entry-refusal.js +7 -0
  172. package/dist/namespaced-rule-name.d.ts +18 -1
  173. package/dist/namespaced-rule-name.js +12 -0
  174. package/dist/observability/fail-open.d.ts +17 -9
  175. package/dist/observability/fail-open.js +17 -9
  176. package/dist/observability/metrics.d.ts +2 -0
  177. package/dist/observability/metrics.js +2 -0
  178. package/dist/observability/secret-env-scrub.d.ts +30 -4
  179. package/dist/observability/secret-env-scrub.js +15 -3
  180. package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
  181. package/dist/orchestration/workflow-agent-session-index.js +1 -6
  182. package/dist/orchestration/workflow-completion-inbox.d.ts +26 -6
  183. package/dist/orchestration/workflow-completion-inbox.js +63 -45
  184. package/dist/org-memory-admission.d.ts +5 -2
  185. package/dist/org-memory-admission.js +25 -10
  186. package/dist/parked-decide.d.ts +33 -26
  187. package/dist/parked-decide.js +36 -44
  188. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  189. package/dist/plugins/approval-ask-store-file.js +37 -4
  190. package/dist/plugins/approval-ask-store-memory.d.ts +5 -3
  191. package/dist/plugins/approval-ask-store-memory.js +11 -2
  192. package/dist/plugins/approval-ask-store-sql.d.ts +3 -0
  193. package/dist/plugins/approval-ask-store-sql.js +3 -1
  194. package/dist/plugins/blob-backend.d.ts +38 -195
  195. package/dist/plugins/blob-backend.js +33 -229
  196. package/dist/plugins/caching-session-store.d.ts +9 -4
  197. package/dist/plugins/checkpoint-index-columns.d.ts +1 -1
  198. package/dist/plugins/checkpoint-index-columns.js +3 -2
  199. package/dist/plugins/checkpoint-store-face.d.ts +1 -1
  200. package/dist/plugins/device-store-sql.d.ts +2 -2
  201. package/dist/plugins/device-store-sql.js +5 -10
  202. package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
  203. package/dist/plugins/execution-lane-rename-m40.js +10 -0
  204. package/dist/plugins/execution-placement-sql.d.ts +63 -88
  205. package/dist/plugins/execution-placement-sql.js +94 -54
  206. package/dist/plugins/file-run-store.d.ts +26 -12
  207. package/dist/plugins/file-run-store.js +63 -63
  208. package/dist/plugins/imported-layer-root-retirement.d.ts +17 -8
  209. package/dist/plugins/imported-layer-root-retirement.js +4 -8
  210. package/dist/plugins/inbox-payload-column.d.ts +14 -0
  211. package/dist/plugins/inbox-payload-column.js +13 -0
  212. package/dist/plugins/local-checkpoint-store.d.ts +5 -0
  213. package/dist/plugins/local-checkpoint-store.js +11 -7
  214. package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
  215. package/dist/plugins/local-data-dir-modes-m39.js +182 -0
  216. package/dist/plugins/local-engine-high-water.d.ts +11 -0
  217. package/dist/plugins/local-engine-high-water.js +59 -0
  218. package/dist/plugins/local-session-store.d.ts +91 -25
  219. package/dist/plugins/local-session-store.js +124 -45
  220. package/dist/plugins/local-task-attachment-store.d.ts +2 -0
  221. package/dist/plugins/local-task-attachment-store.js +10 -11
  222. package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
  223. package/dist/plugins/memory-embedder-fingerprint.js +52 -12
  224. package/dist/plugins/memory-embedder.d.ts +1 -1
  225. package/dist/plugins/memory-embedder.js +2 -2
  226. package/dist/plugins/memory-engine-pg.d.ts +4 -3
  227. package/dist/plugins/memory-engine-pg.js +4 -4
  228. package/dist/plugins/memory-engine-sql-apply.d.ts +13 -4
  229. package/dist/plugins/memory-engine-sql-apply.js +1 -1
  230. package/dist/plugins/memory-engine-tidb.d.ts +8 -5
  231. package/dist/plugins/memory-engine-tidb.js +7 -8
  232. package/dist/plugins/one-time-migrations.d.ts +145 -35
  233. package/dist/plugins/one-time-migrations.js +272 -120
  234. package/dist/plugins/owner-only-file-mode.d.ts +29 -0
  235. package/dist/plugins/owner-only-file-mode.js +141 -0
  236. package/dist/plugins/permission-rule-store-file.d.ts +8 -8
  237. package/dist/plugins/permission-rule-store-file.js +14 -2
  238. package/dist/plugins/pg-pool.d.ts +2 -1
  239. package/dist/plugins/pg-pool.js +11 -4
  240. package/dist/plugins/release-version.d.ts +20 -0
  241. package/dist/plugins/release-version.js +28 -0
  242. package/dist/plugins/remote-env-adb.d.ts +20 -4
  243. package/dist/plugins/remote-env-adb.js +39 -11
  244. package/dist/plugins/remote-env-e2b.d.ts +12 -3
  245. package/dist/plugins/remote-env-e2b.js +6 -4
  246. package/dist/plugins/remote-env-host.d.ts +9 -1
  247. package/dist/plugins/remote-env-host.js +6 -5
  248. package/dist/plugins/remote-env-k8s.d.ts +5 -2
  249. package/dist/plugins/remote-env-k8s.js +15 -4
  250. package/dist/plugins/remote-env-local-docker.d.ts +4 -1
  251. package/dist/plugins/remote-env-local-docker.js +3 -3
  252. package/dist/plugins/remote-env-ssh.d.ts +8 -2
  253. package/dist/plugins/remote-env-ssh.js +26 -24
  254. package/dist/plugins/remote-shell.d.ts +59 -30
  255. package/dist/plugins/remote-shell.js +175 -62
  256. package/dist/plugins/retention-declaration.d.ts +37 -0
  257. package/dist/plugins/retention-declaration.js +9 -0
  258. package/dist/plugins/retention-deployment.d.ts +126 -0
  259. package/dist/plugins/retention-deployment.js +88 -0
  260. package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
  261. package/dist/plugins/retention-lane-store-sql.js +103 -1
  262. package/dist/plugins/retention-sql.d.ts +29 -0
  263. package/dist/plugins/retention-sql.js +8 -0
  264. package/dist/plugins/schema-consistency.d.ts +0 -6
  265. package/dist/plugins/schema-consistency.js +1 -2
  266. package/dist/plugins/schema-ensure-legs.d.ts +1 -1
  267. package/dist/plugins/schema-ensure-legs.js +11 -1
  268. package/dist/plugins/session-ext-table.d.ts +74 -0
  269. package/dist/plugins/session-ext-table.js +121 -0
  270. package/dist/plugins/session-fork.d.ts +1 -1
  271. package/dist/plugins/session-row-sql.d.ts +8 -7
  272. package/dist/plugins/session-row-sql.js +15 -7
  273. package/dist/plugins/session-store-sql.d.ts +131 -0
  274. package/dist/plugins/session-store-sql.js +298 -0
  275. package/dist/plugins/session-workspace-cell.d.ts +2 -2
  276. package/dist/plugins/session-workspace-cell.js +2 -2
  277. package/dist/plugins/shared-memory-store-sql.d.ts +10 -0
  278. package/dist/plugins/shared-memory-store-sql.js +13 -13
  279. package/dist/plugins/snapshot-bucket-presence.js +1 -1
  280. package/dist/plugins/sql-driver.d.ts +23 -7
  281. package/dist/plugins/sql-driver.js +41 -8
  282. package/dist/plugins/sql-errors.d.ts +5 -2
  283. package/dist/plugins/sql-errors.js +4 -0
  284. package/dist/plugins/store-backend.d.ts +35 -8
  285. package/dist/plugins/store-backend.js +184 -62
  286. package/dist/plugins/store-contracts.d.ts +9 -2
  287. package/dist/plugins/store-contracts.js +1 -0
  288. package/dist/plugins/task-attachment-store.d.ts +6 -8
  289. package/dist/plugins/task-attachment-store.js +15 -10
  290. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  291. package/dist/plugins/text-column-capacity.d.ts +21 -0
  292. package/dist/plugins/text-column-capacity.js +5 -0
  293. package/dist/plugins/tidb-pool.d.ts +3 -2
  294. package/dist/plugins/tidb-pool.js +19 -31
  295. package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
  296. package/dist/read-face-posture.d.ts +6 -6
  297. package/dist/read-face-posture.js +2 -2
  298. package/dist/request-value-closure.d.ts +5 -5
  299. package/dist/request-value-closure.js +1 -1
  300. package/dist/rules-consent.d.ts +41 -13
  301. package/dist/rules-consent.js +101 -53
  302. package/dist/run-local.js +17 -13
  303. package/dist/runs.d.ts +48 -30
  304. package/dist/runs.js +26 -16
  305. package/dist/runtime-caps-resolver.d.ts +15 -8
  306. package/dist/runtime-caps-resolver.js +14 -10
  307. package/dist/sealed-key-layout.d.ts +2 -3
  308. package/dist/sealed-key-layout.js +0 -1
  309. package/dist/sealed-key.js +7 -5
  310. package/dist/security.d.ts +5 -3
  311. package/dist/sema-config-dir.js +2 -2
  312. package/dist/server-secret-env.d.ts +2 -0
  313. package/dist/service-version.d.ts +2 -1
  314. package/dist/service-version.js +2 -2
  315. package/dist/session-error-wire.d.ts +1 -1
  316. package/dist/session-incarnation.d.ts +8 -5
  317. package/dist/session-resident-background.d.ts +85 -32
  318. package/dist/session-resident-background.js +75 -7
  319. package/dist/session-row.d.ts +1 -1
  320. package/dist/session-sync-kernel.d.ts +1 -1
  321. package/dist/session-workspace.d.ts +11 -2
  322. package/dist/session-workspace.js +5 -0
  323. package/dist/sighup-idle.d.ts +1 -1
  324. package/dist/store-posture.d.ts +2 -1
  325. package/dist/stored-request-hooks-m20.d.ts +3 -2
  326. package/dist/stored-request-hooks-m20.js +19 -14
  327. package/dist/stores-sql/blob-index-sql.d.ts +148 -0
  328. package/dist/stores-sql/blob-index-sql.js +154 -0
  329. package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
  330. package/dist/stores-sql/checkpoint-store-sql.js +11 -27
  331. package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
  332. package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
  333. package/dist/{plugins → stores-sql}/pg-session-storage.d.ts +23 -76
  334. package/dist/{plugins → stores-sql}/pg-session-storage.js +80 -209
  335. package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +123 -217
  336. package/dist/{plugins → stores-sql}/retention-store-sql.js +66 -190
  337. package/dist/{plugins → stores-sql}/session-event-payload.d.ts +1 -1
  338. package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
  339. package/dist/stores-sql/session-policy-store-sql.d.ts +1 -1
  340. package/dist/stores-sql/session-write-hooks.d.ts +59 -0
  341. package/dist/stores-sql/session-write-hooks.js +2 -0
  342. package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +10 -11
  343. package/dist/{plugins → stores-sql}/tidb-session-storage.js +10 -21
  344. package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +32 -122
  345. package/dist/{plugins → stores-sql}/tidb-session-store.js +71 -171
  346. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  347. package/dist/stores-sql/tool-result-store-sql.js +1 -1
  348. package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
  349. package/dist/stores-sql/windowed-session-storage.js +61 -0
  350. package/dist/task-a2a.d.ts +7 -1
  351. package/dist/task-a2a.js +15 -14
  352. package/dist/task-cwd.d.ts +11 -3
  353. package/dist/task-cwd.js +17 -1
  354. package/dist/task-mcp.d.ts +10 -2
  355. package/dist/task-mcp.js +13 -16
  356. package/dist/task-settings.d.ts +9 -4
  357. package/dist/task-settings.js +17 -6
  358. package/dist/tool-approval.d.ts +21 -8
  359. package/dist/tool-approval.js +10 -6
  360. package/dist/trace/core-keyset-guard.d.ts +9 -3
  361. package/dist/trace/engine-notice-wire.d.ts +1 -1
  362. package/dist/trace/engine-notice-wire.js +2 -0
  363. package/dist/trace/exit-seat.d.ts +23 -1
  364. package/dist/trace/exit-seat.js +3 -0
  365. package/dist/trace/project.d.ts +33 -16
  366. package/dist/trace/project.js +12 -9
  367. package/dist/trace/redact.d.ts +1 -1
  368. package/dist/trace/redact.js +61 -73
  369. package/dist/trace/self-minted-wire.d.ts +263 -10
  370. package/dist/trace/self-minted-wire.js +90 -2
  371. package/dist/trace/sema-provenance.d.ts +45 -41
  372. package/dist/trace/sema-provenance.js +81 -10
  373. package/dist/trace/task-notification-facets.d.ts +70 -2
  374. package/dist/trace/task-notification-facets.js +71 -1
  375. package/dist/trace/wire-projection-faces.d.ts +38 -2
  376. package/dist/trace/wire-projection-faces.js +26 -1
  377. package/dist/unread-request-members.d.ts +66 -0
  378. package/dist/unread-request-members.js +41 -0
  379. package/dist/user-input-ledger.d.ts +2 -2
  380. package/dist/web-admin-client/apply-effective.js +22 -26
  381. package/dist/web-admin-client/candidate-gate.d.ts +35 -3
  382. package/dist/web-admin-client/candidate-gate.js +53 -5
  383. package/dist/web-admin-client/center-request.d.ts +4 -1
  384. package/dist/web-admin-client/center-request.js +2 -1
  385. package/dist/web-admin-client/effective-keys.js +0 -1
  386. package/dist/web-admin-client/facade.d.ts +1 -1
  387. package/dist/web-admin-client/http-client.d.ts +1 -0
  388. package/dist/web-admin-client/http-client.js +17 -9
  389. package/dist/web-admin-client/restart-signal.js +5 -2
  390. package/dist/web-admin-client/skills-mcp.d.ts +20 -6
  391. package/dist/web-admin-client/skills-mcp.js +26 -21
  392. package/dist/web-admin-client/types.d.ts +34 -49
  393. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +2 -2
  394. package/package.json +13 -12
  395. package/scripts/drain-check-local.mjs +2 -28
  396. package/scripts/drain-check.mysql.sql +2 -18
  397. package/scripts/drain-check.pg.sql +2 -18
  398. package/wire-changes/server@7.147.0.json +813 -0
@@ -12,19 +12,15 @@
12
12
  */
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
- import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
16
- import type { SessionWorkspace } from "../session-workspace.js";
17
15
  import type { RetentionDeclaration } from "@sema-agent/core";
18
- import type { SessionSummary } from "./store-contracts.js";
19
- import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowAnswer, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
20
- import { type PlacementAcquireOpts } from "./session-placement.js";
16
+ import type { SessionWriteHooks } from "./session-write-hooks.js";
17
+ import { type PlacementAcquireOpts } from "../plugins/session-placement.js";
21
18
  import { type StagingHandle } from "../session-sync-kernel.js";
22
- import { type ForkPlacement, type ForkTarget, type ForkToOutcome } from "./session-fork.js";
23
- import { type ExecutionPlacementSeat } from "../execution-placement.js";
19
+ import { type ForkPlacement, type ForkTarget, type ForkToOutcome } from "../plugins/session-fork.js";
24
20
  export declare class TiDBSessionStore implements SessionStore {
25
21
  private readonly pool;
26
22
  /** 托管留存声明 = 装配交给它的词(后端按扫车道开不开给;不给 ⇒ `"none"`)。读法逐字见
27
- * `retention-store-sql.ts` 的 {@link MANAGED_RETENTION}。 */
23
+ * `retention-declaration.ts` 的 {@link MANAGED_RETENTION}。 */
28
24
  readonly retention: RetentionDeclaration;
29
25
  /** [ref] C18(core 5.48.0 [ref]):placement **声明**。六条义务的实装点逐条见
30
26
  * {@link SESSION_PLACEMENTS} 顶注与本类的 acquire / listSessions / release / listPlaced /
@@ -39,15 +35,11 @@ export declare class TiDBSessionStore implements SessionStore {
39
35
  readonly durability: "durable";
40
36
  /** In-flight acquisitions keyed by id, so concurrent acquire(sameId) in one process share one. */
41
37
  private readonly pending;
42
- /**
43
- * 执行席 —— 根会话行上这一位住客的那一格的唯一读口与 place / markMirror 两只写口(规则在 `src/execution-placement.ts`;
44
- * 实现与语句的唯一属主 = `execution-placement-sql.ts`,两孪生同一只实现,本店只交自己的池)。
45
- */
46
- readonly executionPlacement: ExecutionPlacementSeat;
47
- /** 本店的池包成的 SqlDriver:执行席与会话行格写原语的事务都经它借连接(连接初始化钉住事务读语义,`sql-driver.ts`)。 */
48
- private readonly db;
49
- constructor(pool: Pool, opts?: {
38
+ /** 部署的事务内钩(组合座交入;导入 / 删除 / fork 的写事务锁住根行之后、写之前调,`session-write-hooks.ts`)。 */
39
+ private readonly hooks;
40
+ constructor(pool: Pool, opts: {
50
41
  readonly retention?: RetentionDeclaration;
42
+ readonly hooks: SessionWriteHooks;
51
43
  });
52
44
  acquire(sessionId?: string, opts?: PlacementAcquireOpts): Promise<AcquiredSession>;
53
45
  private load;
@@ -111,88 +103,6 @@ export declare class TiDBSessionStore implements SessionStore {
111
103
  /** 真删除一只 placed 会话的两张表(条件删门在 `placement_kind IS NOT NULL`;事务内 meta 先删=提交点,
112
104
  * 与 {@link deleteSession} 同序)。任何 I/O 失败上浮成 `SessionError("storage")`,绝不吞。 */
113
105
  private deletePlacedSession;
114
- /**
115
- * E16 — list the caller's sessions (CC /resume picker), keyset-paginated newest-first by last activity.
116
- * Session IDENTITY/enumeration comes from `session_meta` (owner-filtered on the service's OWN owner column);
117
- * a LEFT JOIN onto the `task_run` runs ledger supplies run-level PREVIEW (latest run's objective/status,
118
- * run count) but never gates which sessions exist — a session with zero runs still lists (preview null/0).
119
- * Keyset on `(updated_at, session_id)`; `updated_at` is the session's last-activity marker (touch on every
120
- * run/append). The shipped {@link SessionSummary} shape is preserved (additive): `lastActivityAt` =
121
- * `session_meta.updated_at`, `firstActivityAt` = `session_meta.created_at`.
122
- */
123
- listSessions(opts: {
124
- owner?: string;
125
- includeUnowned?: boolean;
126
- cursor?: {
127
- lastActivityAt: string;
128
- sessionId: string;
129
- };
130
- limit: number;
131
- q?: string;
132
- }): Promise<SessionSummary[]>;
133
- /** {@link listSessions} with each row's occupant (owner + incarnation) from the same SELECT — `OwnerAwareSessionStore.listSessionRows`; the incarnation never enters the row. */
134
- listSessionRows(opts: {
135
- owner?: string;
136
- includeUnowned?: boolean;
137
- cursor?: {
138
- lastActivityAt: string;
139
- sessionId: string;
140
- };
141
- limit: number;
142
- q?: string;
143
- }): Promise<Array<SessionRowAnswer<SessionSummary>>>;
144
- /** ONE session's list row — {@link listSessions}' projection (same SELECT, same mapper) scoped to one id; `undefined` = not a
145
- * row of the list face (no such session / placed). The run-preview subqueries are scoped to the same id (an index read on
146
- * `task_run.session_id`, not the whole-ledger aggregate the list pays). */
147
- sessionSummaryOf(sessionId: string): Promise<SessionSummary | undefined>;
148
- /** {@link sessionSummaryOf} 那一读 + 读到它时行上的住客(写原语的应答行要复核住客,`settleSessionRowWrite`)。 */
149
- private listRowOf;
150
- /**
151
- * 会话行格写原语 —— 用户标题 / 置顶的唯一写口(规则与判序:`src/session-row.ts` 头注「写原语」;事务与语句:`session-row-sql.ts`,PG 孪生同一只实现)。
152
- * 一只经 `SqlDriver.connect()` 的事务锁住根行、交 core 判官判行期望(属主 + 化身)→ 写者身份(客户端写在镜像上 ⇒ `mirror`)→ 标签 → 写;
153
- * 应答行在提交 / 回滚之后另读(列表行同一只投影,含新 `rowTag`)、复核仍是判过的那一位住客。只写列表面上的行(委派分区行 ⇒ `absent`);
154
- * `updated_at` 不动(改名不是活动)。重载按入口分:`replay` 的结局只有 `written` / `absent`。
155
- */
156
- writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ClientSessionRowWrite): Promise<SessionRowWriteOutcome<SessionSummary>>;
157
- writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ReplaySessionRowWrite): Promise<ReplaySessionRowWriteOutcome<SessionSummary>>;
158
- /**
159
- * The list projection's ONE SELECT + row mapper ({@link listSessions} and {@link sessionSummaryOf} both read through it).
160
- * `runSessionId` scopes the two run-preview subqueries to one session (the single-row read); the list leaves them whole.
161
- * Placeholder order follows the text: subquery params, then `where` params, then the LIMIT.
162
- * `sm.incarnation` is read for the row tag only (`rowTag`, `sessionRowTagKey` — present-iff the row has a token) and the answer row's occupant
163
- * check; it never enters the list row itself.
164
- */
165
- private selectListRows;
166
- /** Cheap pre-LLM probe: "none" = session_meta row not yet REGISTERED (createRun fires
167
- * before the Runner's session acquire — the titler retries), "untitled" / "titled" as read. */
168
- probeTitle(sessionId: string): Promise<"none" | "untitled" | "titled">;
169
- /** The auto-titler's write: lands ONLY when title IS NULL (a concurrent/prior title — the user's included — wins;
170
- * writer order: `src/session-row.ts`). Returns whether this call set it. */
171
- setTitleIfNull(sessionId: string, title: string): Promise<boolean>;
172
- /**
173
- * [ref] —— 换源通告的基线读(一行 PK SELECT;列的三态读法见 `tidb-pool.ts` 的列注)。行不在 ⇒ 读作「没记过」
174
- * (loader 跑在 acquire 之后,行恒在;不在 = 被带外删了,没有下一 run 可比)。
175
- */
176
- readInstructionSource(sessionId: string): Promise<InstructionSourceBaseline>;
177
- /**
178
- * [ref] —— CAS 覆写基线(期望值 = 调用方读到的那一份;不符 ⇒ 0 行、不抛 —— 契约见 `InstructionSourceStore`)。
179
- * **不**碰 `updated_at`(那是会话活动时刻,留存腿按它判龄;记基线不是一次会话活动)。
180
- * 两条基线查询都带每查询超时(`BASELINE_IO_DEADLINE_MS`,mysql2 的 `{sql, timeout}` 形):被行锁卡住的写由查询自己
181
- * 在期限内拒。⚠️ mysql2 的超时是客户端侧的 —— 拒之后那条连接仍被挂着的命令占到服务端结束(S-9 同形,如实披露)。
182
- */
183
- recordInstructionSource(sessionId: string, expected: InstructionSourceBaseline, source: string | null): Promise<void>;
184
- /**
185
- * 会话行的位置与项目那一格(`session_meta.workspace`;域律 `src/session-workspace.ts`,列属主 `session-workspace-cell.ts`)。
186
- * 一行 PK 读,按文本投影、恰好解一次;行不在 / 格为 NULL ⇒ `undefined`;格读不出 ⇒ 读作没有记录并计一次(F 类,见列属主头注)。
187
- */
188
- readSessionWorkspace(sessionId: string): Promise<SessionWorkspace | undefined>;
189
- /**
190
- * 整格覆写(最后一帧胜;唯一调用方 = 腿的记录座,它在腿的流循环里一笔一笔等写完)。**住客围栏**:只写化身仍是 `incarnation` 的那一行 ——
191
- * 行不在、或同 id 已换了住客 ⇒ 0 行、不抛(那一行不是这条腿的)。**不**碰 `updated_at`(那是会话活动时刻,留存腿按它判龄;记下站在哪里不是一次会话活动)。
192
- * 走店的一般查询面(与腿循环里的账本追加同一形,不另设每查询的客户端超时):mysql2 的客户端超时只是不再等,服务端照把那条 UPDATE 跑完 ——
193
- * 一笔「放弃等待却仍会落地」的写正是这条腿离开之后还能盖掉下一条腿的那一形。卡住的上界归服务端(锁等待超时)与部署的池策略。
194
- */
195
- recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
196
106
  /**
197
107
  * E17 — fork a session: copy its WHOLE durable history to a NEW uuidv7 session owned by `owner`, mirroring
198
108
  * core's `PgSessionRepo.fork`. Entry ids / parent links are per-session and copy VERBATIM; only `seq` is
@@ -210,11 +120,12 @@ export declare class TiDBSessionStore implements SessionStore {
210
120
  }): Promise<string | null>;
211
121
  /**
212
122
  * **fork 续写的店契约**(三店同一只私有体;core `fork` 是它的投影):把源会话的整份历史拷进调用方给的目标 id。
213
- * 1. **原子目标创建**:事务首句 `INSERT INTO session_meta (…, fork_key) VALUES (:target, …)`,主键即排他(meta 先,随后拷事件、COMMIT);
214
- * 2. **赢家分类**:撞上已在的目标 ⇒ 输方整笔回滚,在新视图里读目标、按属主与 fork 键分类({@link classifyForkTarget});读不到(赢方在分类前回滚或被删)
123
+ * 1. **原子目标创建**:事务首句 `INSERT INTO session_meta (…) VALUES (:target, …)`,主键即排他(meta 先,随后事务内钩 `fork` —— 部署在同一事务里记下 fork 键
124
+ * (部署格,旁表)—— 再拷事件、COMMIT);
125
+ * 2. **赢家分类**:撞上已在的目标 ⇒ 输方整笔回滚,经钩读目标那一行的属主与 fork 键(一次读)、分类({@link classifyForkTarget});读不到(赢方在分类前回滚或被删)
215
126
  * ⇒ 整笔重跑一次,再撞而仍读不到 ⇒ {@link ForkTargetUnsettledError}(不猜);
216
- * 3. 单事务 ⇒ 无中间态;`fork_key` 只由这里写(此后每个写携带:UPDATE 只碰点名列,整段换装经 `carriedRowCells` 带回)。
217
- * 源不在 ⇒ `null`(先于目标判:源被删之后的同键重试答「源不在」)。新行 = 新住客(化身新铸),执行格 absent(INSERT 列名显式)。
127
+ * 3. 单事务 ⇒ 无中间态;fork 键只由这一钩写。
128
+ * 源不在 ⇒ `null`(先于目标判:源被删之后的同键重试答「源不在」)。新行 = 新住客(化身新铸),部署格全缺省(执行格 absent)。
218
129
  */
219
130
  forkTo(sourceId: string, owner: string | null, target: ForkTarget): Promise<ForkToOutcome | null>;
220
131
  /** {@link forkTo} 的一次尝试:`"unsettled"` = 撞上目标而回滚之后读不到它(交给外层重跑一次)。 */
@@ -266,33 +177,32 @@ export declare class TiDBSessionStore implements SessionStore {
266
177
  * re-implement any of those checks in SQL ([[core-service-boundary]]; the same discipline fork's hand-SQL
267
178
  * was warned about). A violation throws `SessionError("invalid_session")` and NOTHING is written.
268
179
  * The write: re-seq dense from 0, entry_id/parent_id/payload/ts verbatim, in ONE txn (COMMIT is the sole commit point), written
269
- * **lock-or-create, meta first** (读写序律,`execution-placement-sql.ts` 头注):the `session_meta` row is occupied first (an existing row ⇒
270
- * refused before anything else about it is read), locked, the run claim on the root is read on THIS connection (a claim ⇒
271
- * `SessionActiveRunError`, nothing written), then the entries. The leaf is the replayed leaf (core's `leafIdAfterEntry`), matching core's import.
180
+ * **lock-or-create, meta first**:the `session_meta` row is occupied first (an existing row ⇒ refused before anything else about it is read), locked,
181
+ * then the deployment's transaction hook `import` runs on THIS connection (its run claim on the root ⇒ refused, nothing written; its cells for an
182
+ * imported row), then the entries. The leaf is the replayed leaf (core's `leafIdAfterEntry`), matching core's import.
272
183
  */
273
184
  importEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
274
185
  /**
275
- * 2c session-sync (§7/§8) — IDEMPOTENT REPLACE: purge any existing conversation log for `sessionId` then re-import
276
- * `entries` verbatim under `owner`, ALL in ONE txn. Unlike {@link importEntries} (fresh-only: an existing session at the
277
- * destination is refused), replace is safe to run OVER an existing session
278
- * — the §7 fast-forward path and a §8 retry-to-completion both land on it. The validate gate runs FIRST (fail-closed,
279
- * before any write), then the OWNER-GUARDED meta delete (defense-in-depth, mirrors {@link deleteSession}
280
- * — a wrong-owner caller can never replace another tenant's session: its delete matches nothing and the re-insert collides on the PK),
281
- * then the SAME shared write body importEntries uses (session_meta first, the in-txn claim read, then the events swapped) — ONE source of
282
- * the write logic, no copy-paste drift. COMMIT is the sole commit point: a mid-replace crash rolls the whole swap back.
186
+ * 2c session-sync (§7/§8) — IDEMPOTENT REPLACE: rewrite the conversation log of `sessionId` with `entries` verbatim under `owner`, ALL in ONE txn.
187
+ * Unlike {@link importEntries} (fresh-only: an existing session at the destination is refused), replace is safe to run OVER an existing
188
+ * session — the §7 fast-forward path and a §8 retry-to-completion both land on it. The validate gate runs FIRST (fail-closed, before any write),
189
+ * then the row: an absent id is created (a fresh occupant, like {@link importEntries}); an existing row is rewritten IN PLACE only when it is the
190
+ * importer's (`owner <=> ?`, defense-in-depth — a wrong-owner caller can never replace another tenant's session: nothing is written) and not a
191
+ * delegation transcript — the same occupant keeps its incarnation and its deployment cells (the side table row is not touched by the store), its
192
+ * created time is the replace's (the log is new). Then the SAME shared write body importEntries uses (the hook, then the events swapped) —
193
+ * ONE source of the write logic. COMMIT is the sole commit point: a mid-replace crash rolls the whole swap back.
283
194
  */
284
195
  replaceEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
285
196
  /**
286
197
  * Shared write half of {@link importEntries} / {@link replaceEntries} (ONE source of the write logic, no drift), in the global lock
287
- * order (根 `session_meta` 先于 `task_*` 与 `session_event`):
288
- * ① session_meta FIRST — `create` occupies the row (`ON DUPLICATE KEY UPDATE session_id = session_id`: an existing row is left as it is),
289
- * `replace` is a plain INSERT after the caller's owner-guarded delete (a foreign row collides on the PK — the existing refusal);
290
- * owner = the RE-STAMPED authenticated principal (never the bundle's); 化身与 fork 键按 `carriedRowCells`(或新铸),执行格 = mirror
291
- * —— 导入落下的行是「写者在别处」(列名与值同出 `importedRowCells`);
292
- * ② `create`: lock the row and judge it is the one this call occupied (化身 = 本次铸的)— a row that existed before ⇒ refused before
293
- * anything else about it is read (no claim read on someone else's row);
294
- * ③ 读写序律:the run claim on the root, read on THIS connection after the row lock (`lockRootAndReadClaim`) — a claim ⇒ `SessionActiveRunError`;
295
- * ④ the validated entries re-seq dense from 0 (entry_id/parent_id/type/ts verbatim); `replace` drops the old log first.
198
+ * order (根 `session_meta` 先于 `task_*`、部署格与 `session_event`):
199
+ * ① session_meta FIRST — `create` occupies the row (`ON DUPLICATE KEY UPDATE leaf_seq = leaf_seq`: an existing row is left as it is) and then
200
+ * locks it and judges it is the one this call occupied (化身 = 本次铸的;a row that existed before ⇒ refused before anything else about it is
201
+ * read — no hook on someone else's row); `replace` rewrites the locked row in place (owner-guarded, not placed — 0 rows = another owner's row,
202
+ * refused, nothing written). owner = the RE-STAMPED authenticated principal (never the bundle's);
203
+ * ② the deployment's transaction hook `import` on THIS connection after the row lock (`session-write-hooks.ts`):its run claim on the root ⇒
204
+ * refused; its cells for an imported row (a writer here ⇒ refused; otherwise the row is a mirror —「写者在别处」);
205
+ * ③ the validated entries re-seq dense from 0 (entry_id/parent_id/type/ts verbatim); `replace` drops the old log first.
296
206
  * Runs inside the caller's open transaction (`conn`) so the caller owns begin/commit/rollback (COMMIT is the sole commit point).
297
207
  * `validated` is the OUTPUT of {@link validateEntriesForImport} (the caller runs the gate before opening the txn).
298
208
  */
@@ -1,38 +1,43 @@
1
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
- import { jsonColumnText } from "./sql-json-column.js";
5
- import { escapeLike } from "./sql-escape.js";
6
- import { BASELINE_IO_DEADLINE_MS, decodeInstructionSource, encodeInstructionSource } from "../instruction-source-notice.js";
7
- import { decodeSessionWorkspaceCell, encodeSessionWorkspaceCell } from "./session-workspace-cell.js";
8
- import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "./retention-store-sql.js";
9
- import { carriedRowCells, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
10
- import { sessionRowTagKey } from "../session-row.js";
11
- import { writeSessionRowCellsSql } from "./session-row-sql.js";
4
+ import { jsonColumnText } from "../plugins/sql-json-column.js";
5
+ import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
6
+ import { headFromRow, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
12
7
  import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
13
- import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SESSION_DURABILITY, SESSION_PLACEMENTS, } from "./session-placement.js";
8
+ import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SESSION_DURABILITY, SESSION_PLACEMENTS, } from "../plugins/session-placement.js";
14
9
  import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
15
- import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue, placedSessionOwnerOf } from "./session-ownership.js";
10
+ import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue, placedSessionOwnerOf } from "../plugins/session-ownership.js";
16
11
  import { selectForkRows } from "@sema-agent/core";
17
- import { FORK_LOG_ROW_SHAPE, ForkTargetUnsettledError, classifyForkTarget, forkByFreshId, forkPointOf } from "./session-fork.js";
18
- import { isMysqlDupKeyError } from "./sql-errors.js";
19
- import { mysqlDriver } from "./sql-driver.js";
20
- import { MIRROR_ON_COMMIT_GUARD, MIRROR_ON_COMMIT_SET, SESSION_META_PLACEMENT_READ_COLUMNS, createSqlExecutionPlacementSeat, importedRowCells, lockRootAndReadClaim, placementCellFromRow, readPlacementRowStatement } from "./execution-placement-sql.js";
21
- import { SessionActiveRunError, SessionWriterPresentError } from "../execution-placement.js";
22
- const rowsOnConnection = (conn) => async (sql, values) => (await conn.query(sql, [...values]))[0];
12
+ import { FORK_LOG_ROW_SHAPE, ForkTargetUnsettledError, classifyForkTarget, forkByFreshId, forkPointOf } from "../plugins/session-fork.js";
13
+ import { isMysqlDupKeyError } from "../plugins/sql-errors.js";
14
+ const statementsOn = (conn) => ({
15
+ async query(sql, params) {
16
+ const [res] = await conn.query(sql, params ?? []);
17
+ return Array.isArray(res) ? { rows: res, affected: 0 } : { rows: [], affected: res.affectedRows };
18
+ },
19
+ });
20
+ class HookRefusal {
21
+ refuse;
22
+ constructor(refuse) {
23
+ this.refuse = refuse;
24
+ }
25
+ }
26
+ function settleHookVerdict(verdict) {
27
+ if (verdict !== "proceed")
28
+ throw new HookRefusal(verdict.refuse);
29
+ }
23
30
  export class TiDBSessionStore {
24
31
  pool;
25
32
  retention;
26
33
  placements = SESSION_PLACEMENTS;
27
34
  durability = SESSION_DURABILITY;
28
35
  pending = new Map();
29
- executionPlacement;
30
- db;
31
- constructor(pool, opts = {}) {
36
+ hooks;
37
+ constructor(pool, opts) {
32
38
  this.pool = pool;
33
39
  this.retention = opts.retention ?? UNMANAGED_RETENTION;
34
- this.db = mysqlDriver(pool);
35
- this.executionPlacement = createSqlExecutionPlacementSeat(this.db);
40
+ this.hooks = opts.hooks;
36
41
  }
37
42
  async acquire(sessionId, opts) {
38
43
  if (!sessionId) {
@@ -83,7 +88,7 @@ export class TiDBSessionStore {
83
88
  const now = new Date();
84
89
  await this.pool.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, " +
85
90
  "placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) " +
86
- "VALUES (?,?,?,?,NULL,0,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE session_id = session_id", [sessionId, owner, now, now, mintSessionIncarnation(), ...placementInsertValues(placement, now)]);
91
+ "VALUES (?,?,?,?,NULL,0,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE leaf_seq = leaf_seq", [sessionId, owner, now, now, mintSessionIncarnation(), ...placementInsertValues(placement, now)]);
87
92
  }
88
93
  async placementOf(sessionId) {
89
94
  const [rows] = await this.pool.query(`SELECT ${PLACEMENT_SELECT_COLUMNS} FROM session_meta WHERE session_id = ?`, [sessionId]);
@@ -151,104 +156,6 @@ export class TiDBSessionStore {
151
156
  conn.release();
152
157
  }
153
158
  }
154
- async listSessions(opts) {
155
- return (await this.listSessionRows(opts)).map((a) => a.row);
156
- }
157
- async listSessionRows(opts) {
158
- const params = [];
159
- const where = ["sm.placement_kind IS NULL"];
160
- if (opts.owner !== undefined) {
161
- where.push(opts.includeUnowned ? "(sm.owner = ? OR sm.owner IS NULL)" : "sm.owner = ?");
162
- params.push(opts.owner);
163
- }
164
- if (opts.q) {
165
- where.push("EXISTS (SELECT 1 FROM task_run tr WHERE tr.session_id = sm.session_id AND LOWER(tr.objective_preview) LIKE LOWER(?) ESCAPE '\\\\')");
166
- params.push(`%${escapeLike(opts.q)}%`);
167
- }
168
- if (opts.cursor) {
169
- where.push("(sm.updated_at < ? OR (sm.updated_at = ? AND sm.session_id < ?))");
170
- params.push(new Date(opts.cursor.lastActivityAt), new Date(opts.cursor.lastActivityAt), opts.cursor.sessionId);
171
- }
172
- return this.selectListRows({ where, params, limit: opts.limit });
173
- }
174
- async sessionSummaryOf(sessionId) {
175
- return (await this.listRowOf(sessionId))?.row;
176
- }
177
- async listRowOf(sessionId) {
178
- return (await this.selectListRows({ where: ["sm.placement_kind IS NULL", "sm.session_id = ?"], params: [sessionId], limit: 1, runSessionId: sessionId }))[0];
179
- }
180
- async writeSessionRowCells(sessionId, cells, how) {
181
- return writeSessionRowCellsSql(this.db, sessionId, cells, how, (id) => this.listRowOf(id));
182
- }
183
- async selectListRows(q) {
184
- const runWhere = q.runSessionId !== undefined ? " WHERE session_id = ?" : "";
185
- const runParams = q.runSessionId !== undefined ? [q.runSessionId, q.runSessionId] : [];
186
- const sql = "SELECT sm.session_id, sm.owner, sm.incarnation, sm.updated_at AS last_activity, sm.created_at AS first_activity, sm.title, sm.pinned, " +
187
- " COALESCE(r.run_count, 0) AS run_count, latest.objective_preview, latest.status, latest.task_id AS last_run_id, latest.last_task_run_id " +
188
- "FROM session_meta sm " +
189
- "LEFT JOIN (SELECT session_id, COUNT(*) AS run_count FROM task_run" + runWhere + " GROUP BY session_id) r ON r.session_id = sm.session_id " +
190
- "LEFT JOIN (SELECT session_id, objective_preview, status, task_id, last_task_run_id FROM (" +
191
- " SELECT session_id, objective_preview, status, task_id, JSON_UNQUOTE(JSON_EXTRACT(result, '$.runId')) AS last_task_run_id, " +
192
- " ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY created_at DESC, task_id DESC) AS rn FROM task_run" + runWhere +
193
- " ) lr WHERE rn = 1) latest ON latest.session_id = sm.session_id " +
194
- (q.where.length ? "WHERE " + q.where.join(" AND ") + " " : "") +
195
- "ORDER BY sm.updated_at DESC, sm.session_id DESC LIMIT ?";
196
- const [rows] = await this.pool.query(sql, [...runParams, ...q.params, q.limit]);
197
- return rows.map((r) => {
198
- const runId = r.last_task_run_id;
199
- const sessionId = String(r.session_id);
200
- const owner = r.owner ?? null;
201
- const incarnation = incarnationFromCell(sessionId, r.incarnation, "absent");
202
- const title = r.title ?? null;
203
- const pinned = Number(r.pinned) === 1;
204
- const row = {
205
- sessionId,
206
- owner,
207
- lastActivityAt: r.last_activity instanceof Date ? r.last_activity.toISOString() : String(r.last_activity),
208
- firstActivityAt: r.first_activity instanceof Date ? r.first_activity.toISOString() : String(r.first_activity),
209
- runCount: Number(r.run_count),
210
- objectivePreview: r.objective_preview ?? null,
211
- lastStatus: r.status == null ? "" : String(r.status),
212
- lastRunId: r.last_run_id ?? null,
213
- ...(typeof runId === "string" && runId !== "" ? { lastTaskRunId: runId } : {}),
214
- title,
215
- pinned,
216
- ...sessionRowTagKey({ sessionId, owner, incarnation, cells: { title, pinned } }),
217
- };
218
- return { row, occupant: { owner, incarnation } };
219
- });
220
- }
221
- async probeTitle(sessionId) {
222
- const [rows] = await this.pool.query("SELECT title FROM session_meta WHERE session_id = ?", [sessionId]);
223
- if (!rows[0])
224
- return "none";
225
- return rows[0].title == null ? "untitled" : "titled";
226
- }
227
- async setTitleIfNull(sessionId, title) {
228
- const [r] = await this.pool.query("UPDATE session_meta SET title = ? WHERE session_id = ? AND title IS NULL", [title, sessionId]);
229
- return r.affectedRows > 0;
230
- }
231
- async readInstructionSource(sessionId) {
232
- const [rows] = await this.pool.query({ sql: "SELECT instruction_source FROM session_meta WHERE session_id = ?", timeout: BASELINE_IO_DEADLINE_MS }, [sessionId]);
233
- return decodeInstructionSource(rows[0]?.instruction_source);
234
- }
235
- async recordInstructionSource(sessionId, expected, source) {
236
- const next = encodeInstructionSource(source);
237
- if (expected.recorded) {
238
- await this.pool.query({ sql: "UPDATE session_meta SET instruction_source = ? WHERE session_id = ? AND instruction_source = ?", timeout: BASELINE_IO_DEADLINE_MS }, [next, sessionId, encodeInstructionSource(expected.source)]);
239
- }
240
- else {
241
- await this.pool.query({ sql: "UPDATE session_meta SET instruction_source = ? WHERE session_id = ? AND instruction_source IS NULL", timeout: BASELINE_IO_DEADLINE_MS }, [next, sessionId]);
242
- }
243
- }
244
- async readSessionWorkspace(sessionId) {
245
- const [rows] = await this.pool.query(`SELECT ${jsonColumnText("mysql", "workspace")} FROM session_meta WHERE session_id = ?`, [sessionId]);
246
- const row = rows[0];
247
- return row === undefined ? undefined : decodeSessionWorkspaceCell(row.workspace, sessionId);
248
- }
249
- async recordSessionWorkspace(sessionId, incarnation, workspace) {
250
- await this.pool.query("UPDATE session_meta SET workspace = ? WHERE session_id = ? AND incarnation = ?", [encodeSessionWorkspaceCell(workspace, JSON.stringify, sessionId), sessionId, incarnation]);
251
- }
252
159
  async fork(sourceId, owner, opts) {
253
160
  return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner, opts);
254
161
  }
@@ -278,8 +185,8 @@ export class TiDBSessionStore {
278
185
  try {
279
186
  await conn.beginTransaction();
280
187
  try {
281
- await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, fork_key, " +
282
- "placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", [target.id, forkOwner, now, now, forkLeafId, eventRows.length, mintSessionIncarnation(), target.key ?? null, ...placementInsertValues(target.placement, now)]);
188
+ await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, " +
189
+ "placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", [target.id, forkOwner, now, now, forkLeafId, eventRows.length, mintSessionIncarnation(), ...placementInsertValues(target.placement, now)]);
283
190
  }
284
191
  catch (err) {
285
192
  if (!isMysqlDupKeyError(err))
@@ -287,6 +194,8 @@ export class TiDBSessionStore {
287
194
  collided = true;
288
195
  }
289
196
  if (!collided) {
197
+ if (target.key !== undefined)
198
+ await this.hooks.keyedFork.record(statementsOn(conn), { rootSessionId: target.id, forkKey: target.key });
290
199
  for (let seq = 0; seq < eventRows.length; seq++) {
291
200
  const r = eventRows[seq];
292
201
  const payload = String(r.payload);
@@ -303,14 +212,15 @@ export class TiDBSessionStore {
303
212
  }
304
213
  catch {
305
214
  }
215
+ if (err instanceof HookRefusal)
216
+ throw err.refuse;
306
217
  throw err instanceof SessionError ? err : new SessionError("storage", `fork of session ${sourceId} failed`, err instanceof Error ? err : undefined);
307
218
  }
308
219
  finally {
309
220
  conn.release();
310
221
  }
311
- const [existing] = await this.pool.query("SELECT owner, fork_key FROM session_meta WHERE session_id = ?", [target.id]);
312
- const row = existing[0];
313
- return row === undefined ? "unsettled" : classifyForkTarget(target, { owner: row.owner ?? null, forkKey: row.fork_key ?? null }, forkOwner);
222
+ const existing = await this.hooks.keyedFork.targetOf(target.id);
223
+ return existing === undefined ? "unsettled" : classifyForkTarget(target, existing, forkOwner);
314
224
  }
315
225
  async exportEntries(sessionId) {
316
226
  const [metaRows] = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = ?", [sessionId]);
@@ -350,7 +260,7 @@ export class TiDBSessionStore {
350
260
  const conn = await this.pool.getConnection();
351
261
  try {
352
262
  await conn.beginTransaction();
353
- await this.writeImportedLog(conn, sessionId, owner, validated, { incarnation: mintSessionIncarnation(), forkKey: null }, "create");
263
+ await this.writeImportedLog(conn, sessionId, owner, validated, "create");
354
264
  await conn.commit();
355
265
  }
356
266
  catch (err) {
@@ -359,7 +269,9 @@ export class TiDBSessionStore {
359
269
  }
360
270
  catch {
361
271
  }
362
- if (err instanceof SessionError || err instanceof SessionActiveRunError)
272
+ if (err instanceof HookRefusal)
273
+ throw err.refuse;
274
+ if (err instanceof SessionError)
363
275
  throw err;
364
276
  throw new SessionError("storage", `import of session ${sessionId} failed`, err instanceof Error ? err : undefined);
365
277
  }
@@ -373,21 +285,13 @@ export class TiDBSessionStore {
373
285
  const conn = await this.pool.getConnection();
374
286
  try {
375
287
  await conn.beginTransaction();
376
- const [placedRows] = await conn.query(`SELECT ${SESSION_META_PLACEMENT_READ_COLUMNS}, fork_key FROM session_meta WHERE session_id = ? FOR UPDATE`, [sessionId]);
377
- const prior = placedRows[0];
288
+ const [lockedRows] = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = ? FOR UPDATE", [sessionId]);
289
+ const prior = lockedRows[0];
378
290
  if (prior?.placement_kind != null) {
379
291
  await conn.rollback();
380
292
  throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${String(prior.placement_kind)} placement partition — a session-sync replace cannot overwrite a delegation transcript`);
381
293
  }
382
- const [purged] = await conn.query("DELETE FROM session_meta WHERE session_id = ? AND owner <=> ? AND placement_kind IS NULL", [sessionId, owner]);
383
- const swapped = purged.affectedRows === 1 ? prior : undefined;
384
- const cells = carriedRowCells(sessionId, swapped === undefined ? undefined : { incarnation: swapped.incarnation, forkKey: swapped.fork_key });
385
- const priorCell = swapped === undefined ? undefined : placementCellFromRow(sessionId, swapped);
386
- if (priorCell?.role === "writer") {
387
- await conn.rollback();
388
- throw new SessionWriterPresentError(sessionId, priorCell.lane);
389
- }
390
- await this.writeImportedLog(conn, sessionId, owner, validated, cells, "replace");
294
+ await this.writeImportedLog(conn, sessionId, owner, validated, prior === undefined ? "create" : "replace");
391
295
  await conn.commit();
392
296
  }
393
297
  catch (err) {
@@ -396,7 +300,9 @@ export class TiDBSessionStore {
396
300
  }
397
301
  catch {
398
302
  }
399
- if (err instanceof SessionError || err instanceof SessionWriterPresentError || err instanceof SessionActiveRunError)
303
+ if (err instanceof HookRefusal)
304
+ throw err.refuse;
305
+ if (err instanceof SessionError)
400
306
  throw err;
401
307
  throw new SessionError("storage", `replace of session ${sessionId} failed`, err instanceof Error ? err : undefined);
402
308
  }
@@ -404,23 +310,26 @@ export class TiDBSessionStore {
404
310
  conn.release();
405
311
  }
406
312
  }
407
- async writeImportedLog(conn, sessionId, owner, validated, cells, mode) {
313
+ async writeImportedLog(conn, sessionId, owner, validated, mode) {
408
314
  let leafId = null;
409
315
  for (const e of validated)
410
316
  leafId = leafIdAfterEntry(e);
411
317
  const now = new Date();
412
- const imported = importedRowCells(cells.forkKey);
413
- await conn.query(`INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, ${imported.columns}) VALUES (?,?,?,?,?,?,?,?,?)` +
414
- (mode === "create" ? " ON DUPLICATE KEY UPDATE session_id = session_id" : ""), [sessionId, owner, now, now, leafId, validated.length, cells.incarnation, ...imported.values]);
415
318
  if (mode === "create") {
319
+ const minted = mintSessionIncarnation();
320
+ await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES (?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE leaf_seq = leaf_seq", [sessionId, owner, now, now, leafId, validated.length, minted]);
416
321
  const [held] = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = ? FOR UPDATE", [sessionId]);
417
- if (String(held[0]?.incarnation) !== cells.incarnation) {
322
+ if (String(held[0]?.incarnation) !== minted) {
418
323
  throw new SessionError("storage", `import of session ${sessionId} failed`, new Error(`session ${sessionId} already exists — importEntries only creates a session (replaceEntries rewrites one); nothing was written`));
419
324
  }
420
325
  }
421
- const claim = await lockRootAndReadClaim(rowsOnConnection(conn), "mysql", sessionId);
422
- if (claim.activeTaskId !== null)
423
- throw new SessionActiveRunError(sessionId, claim.activeTaskId);
326
+ else {
327
+ const [rewritten] = await conn.query("UPDATE session_meta SET owner = ?, created_at = ?, updated_at = ?, leaf_id = ?, leaf_seq = ? WHERE session_id = ? AND owner <=> ? AND placement_kind IS NULL", [owner, now, now, leafId, validated.length, sessionId, owner]);
328
+ if (rewritten.affectedRows !== 1) {
329
+ throw new SessionError("storage", `replace of session ${sessionId} failed`, new Error(`session ${sessionId} belongs to another owner — a replace rewrites only the importer's own session; nothing was written`));
330
+ }
331
+ }
332
+ settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "import", rootSessionId: sessionId }));
424
333
  if (mode === "replace")
425
334
  await conn.query("DELETE FROM session_event WHERE session_id = ?", [sessionId]);
426
335
  for (let seq = 0; seq < validated.length; seq++) {
@@ -429,7 +338,7 @@ export class TiDBSessionStore {
429
338
  }
430
339
  }
431
340
  beginImportStaging(realSessionId, token) {
432
- return new TiDBStagingHandle(this.pool, realSessionId, stagingIdFor(realSessionId, token));
341
+ return new TiDBStagingHandle(this.pool, this.hooks, realSessionId, stagingIdFor(realSessionId, token));
433
342
  }
434
343
  async assertNotPlaced(sessionId, what) {
435
344
  const placed = await this.placementOf(sessionId);
@@ -457,11 +366,9 @@ export class TiDBSessionStore {
457
366
  const conn = await this.pool.getConnection();
458
367
  try {
459
368
  await conn.beginTransaction();
460
- const claim = await lockRootAndReadClaim(rowsOnConnection(conn), "mysql", sessionId);
461
- if (claim.activeTaskId !== null) {
462
- await conn.rollback();
463
- throw new SessionActiveRunError(sessionId, claim.activeTaskId);
464
- }
369
+ const [locked] = await conn.query("SELECT session_id FROM session_meta WHERE session_id = ? FOR UPDATE", [sessionId]);
370
+ if (locked.length > 0)
371
+ settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "delete", rootSessionId: sessionId }));
465
372
  const [metaRes] = await conn.query("DELETE FROM session_meta WHERE session_id = ? AND owner <=> ?", [sessionId, owner]);
466
373
  if (metaRes.affectedRows > 0)
467
374
  await conn.query("DELETE FROM session_event WHERE session_id = ?", [sessionId]);
@@ -474,6 +381,8 @@ export class TiDBSessionStore {
474
381
  }
475
382
  catch {
476
383
  }
384
+ if (err instanceof HookRefusal)
385
+ throw err.refuse;
477
386
  throw err;
478
387
  }
479
388
  finally {
@@ -489,12 +398,14 @@ export class TiDBSessionStore {
489
398
  }
490
399
  class TiDBStagingHandle {
491
400
  pool;
401
+ hooks;
492
402
  realSessionId;
493
403
  stagingId;
494
404
  nextSeq = 0;
495
405
  seen = new Set();
496
- constructor(pool, realSessionId, stagingId) {
406
+ constructor(pool, hooks, realSessionId, stagingId) {
497
407
  this.pool = pool;
408
+ this.hooks = hooks;
498
409
  this.realSessionId = realSessionId;
499
410
  this.stagingId = stagingId;
500
411
  }
@@ -533,18 +444,14 @@ class TiDBStagingHandle {
533
444
  }
534
445
  }
535
446
  else {
536
- 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]);
447
+ 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 leaf_seq = leaf_seq", [this.realSessionId, owner, now, now, minted]);
537
448
  const [claimed] = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = ? FOR UPDATE", [this.realSessionId]);
538
449
  if (String(claimed[0]?.incarnation) !== minted) {
539
450
  await conn.rollback();
540
451
  throw new StagedCommitOwnerChangedError(this.realSessionId);
541
452
  }
542
453
  }
543
- const claim = await lockRootAndReadClaim(rowsOnConnection(conn), "mysql", this.realSessionId);
544
- if (claim.activeTaskId !== null) {
545
- await conn.rollback();
546
- throw new SessionActiveRunError(this.realSessionId, claim.activeTaskId);
547
- }
454
+ settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "import", rootSessionId: this.realSessionId }));
548
455
  const [stagedRows] = await conn.query("SELECT entry_id FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE", [this.stagingId]);
549
456
  const [dstRows] = await conn.query("SELECT entry_id FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE", [this.realSessionId]);
550
457
  const [dstMeta] = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = ?", [this.realSessionId]);
@@ -579,16 +486,7 @@ class TiDBStagingHandle {
579
486
  let leafId = null;
580
487
  if (lastRow[0])
581
488
  leafId = leafIdAfterEntry(decodeSessionEventPayload(lastRow[0].payload, this.realSessionId, "last staged row"));
582
- const [settle] = await conn.query(`UPDATE session_meta SET owner = ?, updated_at = ?, leaf_id = ?, leaf_seq = ?, ${MIRROR_ON_COMMIT_SET} WHERE session_id = ? AND ${MIRROR_ON_COMMIT_GUARD}`, [owner, now, leafId, srcIds.length, this.realSessionId]);
583
- if (settle.affectedRows !== 1) {
584
- const locked = readPlacementRowStatement("mysql", this.realSessionId, { lock: true });
585
- const [lockedRows] = await conn.query(locked.text, [...locked.values]);
586
- const cell = lockedRows[0] === undefined ? undefined : placementCellFromRow(this.realSessionId, lockedRows[0]);
587
- await conn.rollback();
588
- if (cell?.role === "writer")
589
- throw new SessionWriterPresentError(this.realSessionId, cell.lane);
590
- throw new SessionError("storage", `staged commit for ${this.realSessionId}: the commit point matched no row under a held row lock and the row is not a writer — refusing to guess`);
591
- }
489
+ await conn.query("UPDATE session_meta SET owner = ?, updated_at = ?, leaf_id = ?, leaf_seq = ? WHERE session_id = ?", [owner, now, leafId, srcIds.length, this.realSessionId]);
592
490
  const [afterMeta] = await conn.query("SELECT placement_kind, owner, incarnation FROM session_meta WHERE session_id = ?", [this.realSessionId]);
593
491
  if (afterMeta[0]?.placement_kind != null) {
594
492
  await conn.rollback();
@@ -603,7 +501,9 @@ class TiDBStagingHandle {
603
501
  await conn.rollback();
604
502
  }
605
503
  catch { }
606
- if (err instanceof SyncConflictError || err instanceof SessionError || err instanceof SessionWriterPresentError || err instanceof SessionActiveRunError)
504
+ if (err instanceof HookRefusal)
505
+ throw err.refuse;
506
+ if (err instanceof SyncConflictError || err instanceof SessionError)
607
507
  throw err;
608
508
  throw new SessionError("storage", `staged commit for ${this.realSessionId} failed`, err instanceof Error ? err : undefined);
609
509
  }
@@ -31,7 +31,7 @@ export declare function assertToolResultProvenanceSchema(query: (sql: string) =>
31
31
  export declare class SqlToolResultStore implements ToolResultStore {
32
32
  protected readonly db: SqlDriver;
33
33
  /** 托管留存声明 = 装配交给它的词(后端按扫车道开不开给;不给 ⇒ `"none"`)。读法与「为什么 SQL 店答 managed 而三方法的实现体在
34
- * `retention-store-sql.ts`」逐字见 {@link MANAGED_RETENTION}(扫车道开着时本店的 `tool_result` 行由那只聚合店按期清:随会话树删 + 孤儿腿)。 */
34
+ * `retention-store-sql.ts`」逐字见 `plugins/retention-declaration.ts` 的 {@link MANAGED_RETENTION}(扫车道开着时本店的 `tool_result` 行由那只聚合店按期清:随会话树删 + 孤儿腿)。 */
35
35
  readonly retention: RetentionDeclaration;
36
36
  constructor(db: SqlDriver, opts?: {
37
37
  readonly retention?: RetentionDeclaration;