@sema-agent/server 7.138.0 → 7.140.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 (332) hide show
  1. package/MIGRATION.md +457 -7
  2. package/README.md +7 -4
  3. package/README.zh-CN.md +6 -4
  4. package/USAGE.md +91 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/runner.js +1 -1
  8. package/dist/adoption/sql.js +1 -1
  9. package/dist/adoption/wire.d.ts +3 -3
  10. package/dist/approval-card.d.ts +26 -26
  11. package/dist/bench/s1/arms.d.ts +1 -1
  12. package/dist/bench/s1/arms.js +1 -3
  13. package/dist/bench/s1/live-deps.js +3 -2
  14. package/dist/bench/s1/row.d.ts +5 -2
  15. package/dist/bench/s1/row.js +2 -22
  16. package/dist/bench/s1/run-firm.js +2 -2
  17. package/dist/boot/adoption.js +1 -1
  18. package/dist/boot/device-lane.d.ts +8 -3
  19. package/dist/boot/device-lane.js +7 -2
  20. package/dist/boot/execution-env.d.ts +4 -2
  21. package/dist/boot/execution-env.js +28 -34
  22. package/dist/boot/execution-lane.d.ts +9 -1
  23. package/dist/boot/execution-lane.js +6 -1
  24. package/dist/boot/governance-seams.d.ts +3 -2
  25. package/dist/boot/governance-seams.js +9 -11
  26. package/dist/boot/memory-boundary.d.ts +14 -51
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +15 -11
  29. package/dist/boot/memory-consolidation.js +25 -14
  30. package/dist/boot/resolve-spec.d.ts +4 -6
  31. package/dist/boot/resolve-spec.js +10 -13
  32. package/dist/boot/retention-lane.d.ts +5 -5
  33. package/dist/boot/retention-lane.js +0 -6
  34. package/dist/boot/runner-deps.d.ts +6 -4
  35. package/dist/boot/runner-deps.js +3 -1
  36. package/dist/boot/session-faces.d.ts +0 -3
  37. package/dist/boot/session-faces.js +15 -8
  38. package/dist/boot/stage-02-web-admin-pull.js +2 -2
  39. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  40. package/dist/boot/stage-03-store-backend.js +2 -3
  41. package/dist/boot/stage-04-stores.d.ts +2 -6
  42. package/dist/boot/stage-04-stores.js +5 -5
  43. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  44. package/dist/boot/stage-05-execution-env.js +2 -2
  45. package/dist/boot/stage-06-runners.d.ts +2 -6
  46. package/dist/boot/stage-06-runners.js +2 -3
  47. package/dist/boot/stage-07-capability-layer.d.ts +2 -6
  48. package/dist/boot/stage-08-reapers.d.ts +2 -6
  49. package/dist/boot/stage-09-leader.d.ts +2 -6
  50. package/dist/boot/stage-09-leader.js +1 -2
  51. package/dist/boot/stage-10-http-server.d.ts +2 -6
  52. package/dist/boot/stage-10-http-server.js +7 -6
  53. package/dist/boot/stores.d.ts +3 -6
  54. package/dist/boot/stores.js +27 -40
  55. package/dist/boot/task-list-lane.d.ts +1 -1
  56. package/dist/boot/task-list-lane.js +1 -1
  57. package/dist/boot/web-admin.js +37 -11
  58. package/dist/capabilities/execution-lane.d.ts +2 -2
  59. package/dist/capabilities/mcp-probe.js +1 -1
  60. package/dist/capabilities/scenarios.d.ts +1 -1
  61. package/dist/capability-facts.d.ts +15 -7
  62. package/dist/capability-facts.js +8 -3
  63. package/dist/config-catalog.js +9 -8
  64. package/dist/config-invariants.d.ts +23 -3
  65. package/dist/config-invariants.js +42 -0
  66. package/dist/config-provider.d.ts +33 -37
  67. package/dist/config-provider.js +51 -30
  68. package/dist/config-types.d.ts +21 -7
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +46 -16
  71. package/dist/delegated-session-owner.d.ts +4 -3
  72. package/dist/delegated-session-owner.js +1 -1
  73. package/dist/deployment-shape.d.ts +10 -9
  74. package/dist/deployment-shape.js +4 -4
  75. package/dist/device-enrollment.d.ts +41 -2
  76. package/dist/device-enrollment.js +27 -12
  77. package/dist/device-store.d.ts +58 -85
  78. package/dist/device-store.js +19 -49
  79. package/dist/device-ws-hub.js +2 -1
  80. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  81. package/dist/exec-cut.d.ts +60 -0
  82. package/dist/exec-cut.js +36 -0
  83. package/dist/execution-lane-caps.d.ts +115 -105
  84. package/dist/execution-lane-caps.js +76 -82
  85. package/dist/execution-placement.d.ts +259 -0
  86. package/dist/execution-placement.js +199 -0
  87. package/dist/field-conflict.d.ts +3 -2
  88. package/dist/field-conflict.js +1 -0
  89. package/dist/hosted-posture.d.ts +2 -2
  90. package/dist/http/admission.js +8 -19
  91. package/dist/http/checkpoint-ctx.d.ts +21 -12
  92. package/dist/http/checkpoint-ctx.js +4 -2
  93. package/dist/http/claimed-run.d.ts +1 -1
  94. package/dist/http/durable-run-leg.js +9 -5
  95. package/dist/http/execution-context.d.ts +218 -0
  96. package/dist/http/execution-context.js +330 -0
  97. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  98. package/dist/http/memory-compliance-failure-reply.js +5 -0
  99. package/dist/http/memory-layer-reply.d.ts +30 -0
  100. package/dist/http/memory-layer-reply.js +24 -0
  101. package/dist/http/resume-legs.js +63 -36
  102. package/dist/http/retired-service-deps.d.ts +1 -1
  103. package/dist/http/retired-service-deps.js +1 -1
  104. package/dist/http/route-ctx.d.ts +14 -8
  105. package/dist/http/routes/adoption.js +1 -1
  106. package/dist/http/routes/background.d.ts +29 -1
  107. package/dist/http/routes/background.js +29 -2
  108. package/dist/http/routes/devices.d.ts +13 -19
  109. package/dist/http/routes/devices.js +21 -26
  110. package/dist/http/routes/memory-bundle.js +3 -0
  111. package/dist/http/routes/memory-compliance.js +8 -1
  112. package/dist/http/routes/memory-consolidation.js +12 -1
  113. package/dist/http/routes/memory-origin.js +14 -2
  114. package/dist/http/routes/memory-policy.js +5 -0
  115. package/dist/http/routes/session-sync.js +110 -15
  116. package/dist/http/routes/sessions.d.ts +6 -0
  117. package/dist/http/routes/sessions.js +143 -22
  118. package/dist/http/routes/side-query.d.ts +12 -4
  119. package/dist/http/routes/side-query.js +4 -5
  120. package/dist/http/routes/tasks.js +41 -33
  121. package/dist/http/server.d.ts +40 -44
  122. package/dist/http/server.js +13 -2
  123. package/dist/http/session-inherit-env.d.ts +3 -4
  124. package/dist/http/session-inherit-env.js +1 -1
  125. package/dist/http/wire-types.d.ts +8 -12
  126. package/dist/identity-source.d.ts +1 -1
  127. package/dist/identity-source.js +1 -1
  128. package/dist/index.d.ts +1 -0
  129. package/dist/lane-home-probe.d.ts +68 -42
  130. package/dist/lane-home-probe.js +53 -30
  131. package/dist/leader/wire.js +5 -4
  132. package/dist/memory-bundle-engine.d.ts +16 -9
  133. package/dist/memory-bundle-engine.js +29 -6
  134. package/dist/memory-operator-faces.d.ts +85 -30
  135. package/dist/memory-operator-faces.js +136 -30
  136. package/dist/memory-project-layer.d.ts +3 -21
  137. package/dist/memory-project-layer.js +11 -8
  138. package/dist/memory-scope-stores.d.ts +73 -0
  139. package/dist/memory-scope-stores.js +86 -0
  140. package/dist/memory-scope.d.ts +29 -5
  141. package/dist/memory-scope.js +16 -4
  142. package/dist/memory-sync-client.d.ts +70 -18
  143. package/dist/memory-sync-client.js +215 -117
  144. package/dist/memory-sync.d.ts +1 -1
  145. package/dist/memory-sync.js +3 -2
  146. package/dist/observability/cost-taxonomy.d.ts +2 -2
  147. package/dist/observability/fail-open.d.ts +12 -0
  148. package/dist/observability/fail-open.js +12 -0
  149. package/dist/observability/run-terminal.js +0 -1
  150. package/dist/orchestration/workflow-notify-journal.js +4 -4
  151. package/dist/org-memory-admission.d.ts +4 -3
  152. package/dist/org-memory-admission.js +6 -6
  153. package/dist/parked-decide.d.ts +34 -9
  154. package/dist/parked-decide.js +12 -19
  155. package/dist/parked-revive-surface.d.ts +12 -15
  156. package/dist/parked-revive-surface.js +3 -6
  157. package/dist/peer-seat.d.ts +13 -7
  158. package/dist/peer-seat.js +37 -3
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  161. package/dist/plugins/approval-ask-store-file.js +11 -0
  162. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  163. package/dist/plugins/approval-ask-store-memory.js +12 -0
  164. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  165. package/dist/plugins/approval-ask-store-sql.js +44 -26
  166. package/dist/plugins/approval-nonce-store.js +4 -4
  167. package/dist/plugins/blob-backend.d.ts +4 -4
  168. package/dist/plugins/blob-backend.js +7 -7
  169. package/dist/plugins/breaker-state-sql.js +2 -2
  170. package/dist/plugins/caching-session-store.d.ts +6 -2
  171. package/dist/plugins/caching-session-store.js +9 -3
  172. package/dist/plugins/checkpoint-index-columns.js +2 -2
  173. package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
  174. package/dist/plugins/checkpoint-store-sql.js +21 -23
  175. package/dist/plugins/device-store-sql.d.ts +25 -51
  176. package/dist/plugins/device-store-sql.js +68 -122
  177. package/dist/plugins/ensure-index.d.ts +1 -2
  178. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  179. package/dist/plugins/execution-placement-sql.js +223 -0
  180. package/dist/plugins/file-history-store-sql.d.ts +9 -8
  181. package/dist/plugins/file-history-store-sql.js +9 -9
  182. package/dist/plugins/file-run-store.d.ts +6 -1
  183. package/dist/plugins/file-run-store.js +4 -0
  184. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  185. package/dist/plugins/fork-routing-session-store.js +16 -0
  186. package/dist/plugins/image-bake-store-sql.js +9 -9
  187. package/dist/plugins/image-index-sql.js +11 -11
  188. package/dist/plugins/leader-run-store-sql.js +4 -4
  189. package/dist/plugins/local-session-store.d.ts +149 -50
  190. package/dist/plugins/local-session-store.js +382 -107
  191. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  192. package/dist/plugins/mailbox-store-sql.js +4 -4
  193. package/dist/plugins/memory-engine-tidb.js +5 -5
  194. package/dist/plugins/memory-key-guards.d.ts +4 -0
  195. package/dist/plugins/memory-key-guards.js +1 -0
  196. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  197. package/dist/plugins/memory-run-store.d.ts +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +117 -8
  200. package/dist/plugins/one-time-migrations.js +408 -38
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  203. package/dist/plugins/outcome-ledger-sql.js +2 -2
  204. package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
  205. package/dist/plugins/permission-rule-store-sql.js +29 -32
  206. package/dist/plugins/pg-pool.d.ts +7 -3
  207. package/dist/plugins/pg-session-storage.d.ts +31 -20
  208. package/dist/plugins/pg-session-storage.js +120 -52
  209. package/dist/plugins/remote-env-adb.d.ts +18 -19
  210. package/dist/plugins/remote-env-adb.js +91 -66
  211. package/dist/plugins/remote-env-device.d.ts +23 -31
  212. package/dist/plugins/remote-env-device.js +19 -17
  213. package/dist/plugins/remote-env-e2b.d.ts +27 -21
  214. package/dist/plugins/remote-env-e2b.js +91 -45
  215. package/dist/plugins/remote-env-file-error.d.ts +3 -1
  216. package/dist/plugins/remote-env-file-error.js +9 -3
  217. package/dist/plugins/remote-env-host.d.ts +31 -34
  218. package/dist/plugins/remote-env-host.js +91 -72
  219. package/dist/plugins/remote-env-k8s.d.ts +13 -29
  220. package/dist/plugins/remote-env-k8s.js +63 -55
  221. package/dist/plugins/remote-env-local-docker.d.ts +24 -12
  222. package/dist/plugins/remote-env-local-docker.js +49 -61
  223. package/dist/plugins/remote-env-ssh.d.ts +11 -19
  224. package/dist/plugins/remote-env-ssh.js +74 -57
  225. package/dist/plugins/remote-shell.d.ts +100 -3
  226. package/dist/plugins/remote-shell.js +166 -2
  227. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  228. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  229. package/dist/plugins/retention-lane-store-sql.js +2 -2
  230. package/dist/plugins/retention-store-sql.d.ts +10 -7
  231. package/dist/plugins/retention-store-sql.js +13 -7
  232. package/dist/plugins/run-store-sql.d.ts +13 -3
  233. package/dist/plugins/run-store-sql.js +20 -15
  234. package/dist/plugins/schema-consistency.d.ts +21 -11
  235. package/dist/plugins/schema-consistency.js +15 -9
  236. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  237. package/dist/plugins/schema-drift-sentence.js +38 -0
  238. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  239. package/dist/plugins/schema-ensure-legs.js +2 -2
  240. package/dist/plugins/schema-live-columns.d.ts +1 -1
  241. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  242. package/dist/plugins/session-fork.d.ts +54 -0
  243. package/dist/plugins/session-fork.js +38 -1
  244. package/dist/plugins/session-row-sql.d.ts +22 -0
  245. package/dist/plugins/session-row-sql.js +81 -0
  246. package/dist/plugins/session-rules-column.d.ts +1 -1
  247. package/dist/plugins/session-rules-column.js +1 -1
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  250. package/dist/plugins/session-workspace-cell.js +1 -1
  251. package/dist/plugins/shared-memory-store-sql.js +2 -2
  252. package/dist/plugins/sql-driver.d.ts +10 -5
  253. package/dist/plugins/sql-driver.js +6 -6
  254. package/dist/plugins/sql-errors.js +1 -1
  255. package/dist/plugins/store-backend.d.ts +5 -7
  256. package/dist/plugins/store-backend.js +23 -11
  257. package/dist/plugins/store-contracts.d.ts +25 -5
  258. package/dist/plugins/task-list-store-sql.js +11 -11
  259. package/dist/plugins/tidb-pool.d.ts +12 -7
  260. package/dist/plugins/tidb-pool.js +13 -7
  261. package/dist/plugins/tidb-session-storage.js +4 -4
  262. package/dist/plugins/tidb-session-store.d.ts +57 -26
  263. package/dist/plugins/tidb-session-store.js +127 -59
  264. package/dist/plugins/tool-result-store-sql.d.ts +5 -4
  265. package/dist/plugins/tool-result-store-sql.js +21 -13
  266. package/dist/plugins/user-input-ledger-sql.js +2 -2
  267. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  268. package/dist/plugins/workflow-run-store-sql.js +19 -19
  269. package/dist/plugins/write-behind-counter.d.ts +2 -3
  270. package/dist/plugins/write-behind-counter.js +5 -5
  271. package/dist/project-memory.d.ts +9 -15
  272. package/dist/project-memory.js +5 -4
  273. package/dist/request-key-closure.js +1 -1
  274. package/dist/request-value-closure.d.ts +6 -7
  275. package/dist/request-value-closure.js +1 -3
  276. package/dist/resource-suspend.d.ts +14 -12
  277. package/dist/resource-suspend.js +1 -1
  278. package/dist/resource-window.d.ts +4 -3
  279. package/dist/run-local.d.ts +6 -0
  280. package/dist/run-local.js +34 -35
  281. package/dist/runs.d.ts +10 -5
  282. package/dist/runs.js +52 -66
  283. package/dist/security.d.ts +38 -12
  284. package/dist/security.js +8 -2
  285. package/dist/server-secret-env.d.ts +9 -5
  286. package/dist/session-children-retire.d.ts +27 -9
  287. package/dist/session-children-retire.js +9 -5
  288. package/dist/session-incarnation.d.ts +20 -10
  289. package/dist/session-incarnation.js +5 -3
  290. package/dist/session-policy-wire.d.ts +1 -1
  291. package/dist/session-row.d.ts +114 -5
  292. package/dist/session-row.js +55 -5
  293. package/dist/session-titler.d.ts +2 -2
  294. package/dist/sql-engine-posture.d.ts +1 -1
  295. package/dist/sql-engine-posture.js +1 -1
  296. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  297. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  298. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  299. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  300. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  301. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  302. package/dist/task-a2a.d.ts +1 -1
  303. package/dist/task-a2a.js +5 -6
  304. package/dist/task-cwd.d.ts +6 -1
  305. package/dist/task-mcp.d.ts +22 -9
  306. package/dist/task-mcp.js +17 -9
  307. package/dist/terminal.d.ts +27 -0
  308. package/dist/terminal.js +6 -0
  309. package/dist/tool-approval.d.ts +6 -0
  310. package/dist/tool-approval.js +8 -1
  311. package/dist/trace/project.d.ts +1 -0
  312. package/dist/trace/project.js +1 -0
  313. package/dist/trace/redact.d.ts +3 -1
  314. package/dist/trace/redact.js +80 -28
  315. package/dist/trace/self-minted-wire.d.ts +169 -2
  316. package/dist/trace/self-minted-wire.js +54 -1
  317. package/dist/url-refusal.d.ts +1 -1
  318. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  319. package/dist/web-admin-client/apply-effective.js +20 -10
  320. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  321. package/dist/web-admin-client/candidate-gate.js +1 -1
  322. package/dist/web-admin-client/http-client.d.ts +5 -4
  323. package/dist/web-admin-client/http-client.js +2 -2
  324. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  325. package/dist/web-admin-client/read-warnings.js +5 -9
  326. package/dist/web-admin-client/skills-mcp.js +3 -2
  327. package/dist/web-admin-client/types.d.ts +6 -0
  328. package/package.json +3 -3
  329. package/wire-changes/server@7.139.0.json +348 -0
  330. package/wire-changes/server@7.140.0.json +184 -0
  331. package/dist/http/device-placement.d.ts +0 -175
  332. package/dist/http/device-placement.js +0 -145
@@ -8,14 +8,14 @@
8
8
  * `errorCode` 直接是裸 `42703` / `ER_BAD_FIELD_ERROR`。
9
9
  *
10
10
  * ── 与 SCHEMA POLICY(`tidb-pool.ts` 顶注,clay 裁 2026-07-26)不冲突 ─────────────────────────────
11
- * 那条口径禁的是 **ALTER seam**:「代码是 schema 的唯一真源、存量库删库重建、`ensure*` 里不许再出现
12
- * `ALTER TABLE`」。本探针**一条 DDL 都不发**,它是只读的 information_schema 对账 —— 恰恰是那条口径的
11
+ * 那条口径禁的是 **ALTER seam**:「代码是 schema 的唯一真源、不做增量(缺列由部署者按拒启句打印的 ALTER 自己执行)、
12
+ * `ensure*` 里不许再出现 `ALTER TABLE`」。本探针**一条 DDL 都不发**,它是只读的 information_schema 对账 —— 恰恰是那条口径的
13
13
  * **执行面**:口径说「库必须等于代码」,在此之前没有任何东西检查过这句话是不是真的。两者一个是禁令
14
14
  * (不许代码偷改库)、一个是对账(库偏离了就别起),同向不同面。
15
15
  *
16
16
  * ── 为什么两个方向都拒(缺列 **和** 多列)──────────────────────────────────────────────────────
17
- * 缺列 = 库比代码旧(升级没重建库):读写会在用户路径上炸。多列 = 库比代码**新** —— 滚动升级窗里一个
18
- * **旧副本**连上了已被新副本重建过的库;它按旧列集读写,新列上的不变量(本例:`gen` 血统)在它眼里
17
+ * 缺列 = 库比代码旧(升级时库没跟上):读写会在用户路径上炸。多列 = 库比代码**新** —— 滚动升级窗里一个
18
+ * **旧副本**连上了已被新副本升过的库;它按旧列集读写,新列上的不变量(本例:`gen` 血统)在它眼里
19
19
  * 根本不存在,于是它会悄悄写出违反新契约的行。后者比前者更隐蔽,所以不做「幽灵列只警告」。
20
20
  * ⇒ 运维顺序因此是**先升库再升副本**(见 `docs/DEPLOY-PREREQS.md` 滚动升级窗那一节)。
21
21
  *
@@ -50,7 +50,7 @@
50
50
  */
51
51
  import type { Pool as MysqlPool } from "mysql2/promise";
52
52
  import type { Pool as PgPool } from "pg";
53
- import { type SchemaDialect } from "./schema-ensure-legs.js";
53
+ import type { SqlDialect } from "./sql-driver.js";
54
54
  import { type SchemaProbeRead } from "./schema-live-columns.js";
55
55
  import { StoreBootRefusalError } from "./store-contracts.js";
56
56
  /** 一列:名字 + 它在 `CREATE TABLE` 里的**原样定义文本**(生成 ADD COLUMN 指路用,不另手写)。 */
@@ -72,7 +72,7 @@ export interface ParsedTable {
72
72
  */
73
73
  export declare function parseCreateTable(stmt: string): ParsedTable | undefined;
74
74
  /** 启动时真会建出来的**全部**表形(录制 → 解析)。同名表二次出现以**先到**为准(`IF NOT EXISTS` 的真语义)。 */
75
- export declare function expectedTableShapes(dialect: SchemaDialect): Promise<Map<string, ParsedTable>>;
75
+ export declare function expectedTableShapes(dialect: SqlDialect): Promise<Map<string, ParsedTable>>;
76
76
  /** 拒启码(机读)。附录 A / 拒启码表的登记名。 */
77
77
  export declare const SCHEMA_MISMATCH_ERROR_CODE = "config.schema_mismatch";
78
78
  /** 一张表的对账结论。 */
@@ -85,14 +85,24 @@ export interface TableMismatch {
85
85
  }
86
86
  /** 对账(纯函数:期望 × 实况 → 不符表清单)。 */
87
87
  export declare function diffSchema(expected: Map<string, ParsedTable>, live: Map<string, Set<string>>): TableMismatch[];
88
- /** 拒启文案(表名 + 缺/多列 + 两条路;ADD COLUMN 句由解析结果**生成**)。 */
89
- export declare function describeSchemaMismatch(dialect: SchemaDialect, mismatches: readonly TableMismatch[]): string;
88
+ /**
89
+ * 逐表段 + 闭集出路段(多行):整库对账的拒启句与单表探针的拒因共用这一份句形(`schema-drift-sentence.ts` 的 `describeProbedTableDrift` 读它)。
90
+ * ADD COLUMN 句由解析结果**生成**,不手写。
91
+ *
92
+ * 出路段 = {@link SCHEMA_MISMATCH_WAYS_OUT}(**闭集三条**,常量;句子的属主在 `schema-drift-sentence.ts`):
93
+ * ① 部署与这个库匹配的构建;② 库比构建旧(缺列)⇒ 部署者自己执行上面逐字打印的 `ALTER TABLE … ADD COLUMN …`(列集对账与拒启句永不改库);
94
+ * ③ 库比构建新(多列)⇒ 部署持有那些列的构建(先升库、再升副本)。
95
+ * 🔴 **没有销毁性的一条**:对带数据的库,任何「删库 / 重建」的指路都是教人销毁数据 —— 这里不写,拒启句的钉也守着(`test/schema-consistency.test.ts` ⑤)。
96
+ */
97
+ export declare function describeMismatchedTables(mismatches: readonly TableMismatch[]): string[];
98
+ /** 拒启文案(表头 + 逐表段与闭集三条出路 {@link describeMismatchedTables})。 */
99
+ export declare function describeSchemaMismatch(dialect: SqlDialect, mismatches: readonly TableMismatch[]): string;
90
100
  /**
91
101
  * 启动期对账:**建表之后**跑,不符即抛(= 拒启)。两方言 + InnoDB 同腿(MySQL 协议共用 `mysql` 臂)。
92
102
  *
93
103
  * 失败方向见文件头注:读抛 / 整库零行 ⇒ 拒。
94
104
  */
95
- export declare function assertSchemaMatchesCode(read: SchemaProbeRead, dialect: SchemaDialect): Promise<void>;
105
+ export declare function assertSchemaMatchesCode(read: SchemaProbeRead, dialect: SqlDialect): Promise<void>;
96
106
  /**
97
107
  * 跳版升级的库的拒启:退役 schema(快照索引表 / 单座列)任一仍在。**具名类型**:锚类型不锚文案(日志消费端与测试按 `code` / 类型分派);
98
108
  * 码取列册对账同一个 `config.schema_mismatch`,运维面一个拒启码一个读法。拒句点名在场的坐标、说明第三条(旧拼法改写)同样没跑过、给出路。
@@ -109,10 +119,10 @@ export declare class RetiredSchemaPresentError extends StoreBootRefusalError {
109
119
  * 快照索引表 / 单座列任一在场 ⇒ 抛 {@link RetiredSchemaPresentError}。新库(建表之前一列都没有)⇒ 不在场、过。读抛 ⇒ 原样上抛(拒启;不是确定的拒启表态,
110
120
  * 开库等待窗照常重试 —— 与修前两条目录探针抛出时同一个去向)。
111
121
  */
112
- export declare function assertRetiredSchemaAbsent(read: SchemaProbeRead, dialect: SchemaDialect): Promise<void>;
122
+ export declare function assertRetiredSchemaAbsent(read: SchemaProbeRead, dialect: SqlDialect): Promise<void>;
113
123
  /** 探针口的池参数:方言与它的池一起走(判别联合 ⇒ 不靠调用方断言「这个池是哪种」)。 */
114
124
  export type RetiredSchemaProbeTarget = {
115
- readonly dialect: "tidb";
125
+ readonly dialect: "mysql";
116
126
  readonly pool: MysqlPool;
117
127
  } | {
118
128
  readonly dialect: "pg";
@@ -1,5 +1,6 @@
1
1
  import { recordEnsureStatements } from "./schema-ensure-legs.js";
2
2
  import { readLiveColumns } from "./schema-live-columns.js";
3
+ import { SCHEMA_MISMATCH_WAYS_OUT } from "./schema-drift-sentence.js";
3
4
  import { StoreBootRefusalError } from "./store-contracts.js";
4
5
  const QUOTES = new Set(["'", '"', "`"]);
5
6
  function stripLineComments(sql) {
@@ -120,13 +121,8 @@ export function diffSchema(expected, live) {
120
121
  }
121
122
  return out;
122
123
  }
123
- export function describeSchemaMismatch(dialect, mismatches) {
124
- const lines = [
125
- `${SCHEMA_MISMATCH_ERROR_CODE}: the ${dialect === "pg" ? "PostgreSQL" : "MySQL-protocol"} database does not match this build's schema — refusing to start.`,
126
- "This repo ships no ALTER seam (schema policy: the code is the single source of truth), so a drifted database is",
127
- "a deployment-order problem, not something the process may quietly work around.",
128
- "",
129
- ];
124
+ export function describeMismatchedTables(mismatches) {
125
+ const lines = [];
130
126
  for (const m of mismatches) {
131
127
  if (m.missing.length > 0) {
132
128
  lines.push(` table ${m.table}: MISSING column(s) ${m.missing.map((c) => c.name).join(", ")} — the database is OLDER than this build.`);
@@ -138,7 +134,17 @@ export function describeSchemaMismatch(dialect, mismatches) {
138
134
  lines.push(` No ALTER is suggested for this direction: deploy the build that owns those columns (upgrade the database FIRST, then the replicas).`);
139
135
  }
140
136
  }
141
- lines.push("", " Two ways out: (1) deploy the build whose code matches this database; (2) rebuild the database from this build", " (DROP DATABASE <name>; CREATE DATABASE <name>; restart — every table is recreated by ensure*Schema at boot).");
137
+ lines.push("", ...SCHEMA_MISMATCH_WAYS_OUT);
138
+ return lines;
139
+ }
140
+ export function describeSchemaMismatch(dialect, mismatches) {
141
+ const lines = [
142
+ `${SCHEMA_MISMATCH_ERROR_CODE}: the ${dialect === "pg" ? "PostgreSQL" : "MySQL-protocol"} database does not match this build's schema — refusing to start.`,
143
+ "Schema policy: the code is the single source of truth, and the column check that produced this refusal never alters a database —",
144
+ "so a drifted database is a deployment-order problem, not something the process may quietly work around.",
145
+ "",
146
+ ...describeMismatchedTables(mismatches),
147
+ ];
142
148
  return lines.join("\n");
143
149
  }
144
150
  export async function assertSchemaMatchesCode(read, dialect) {
@@ -193,7 +199,7 @@ export async function assertRetiredSchemaAbsent(read, dialect) {
193
199
  throw new RetiredSchemaPresentError(found);
194
200
  }
195
201
  export async function assertRetiredSchemaAbsentOnPool(target) {
196
- if (target.dialect === "tidb") {
202
+ if (target.dialect === "mysql") {
197
203
  const conn = await target.pool.getConnection();
198
204
  try {
199
205
  await assertRetiredSchemaAbsent(async (sql) => (await conn.query(sql))[0], "mysql");
@@ -0,0 +1,27 @@
1
+ import type { SqlDialect } from "./sql-driver.js";
2
+ /** 「库比构建旧」那一条出路的两个可变件:旧在哪(括注)、部署者要自己做的那件事。其余字一个不变。 */
3
+ export interface OlderWayOut {
4
+ /** 括注:库旧在哪(例:`missing columns`)。 */
5
+ readonly when: string;
6
+ /** 部署者自己执行的动作(接在冒号后;句尾的「列集对账不改库」声明由本函数补)。 */
7
+ readonly action: string;
8
+ }
9
+ /**
10
+ * 出路段:**闭集三条**,编号 (1)(2)(3) 与顺序固定,与不符的方向无关(每一条自带适用条件)。
11
+ * 🔴 **没有销毁性的一条**:对带数据的库,任何「删库 / 重建」的指路都是教人销毁数据。
12
+ */
13
+ export declare function schemaWaysOut(older: OlderWayOut): readonly string[];
14
+ /** 缺列方向的出路段(整库对账与单表探针共用;逐字钉在 `test/schema-consistency.test.ts` ⑤)。 */
15
+ export declare const SCHEMA_MISMATCH_WAYS_OUT: readonly string[];
16
+ /** 探针读口:一条 SQL → `{ rows }`(各 `assert*Schema` 的 `query` 形)。 */
17
+ export type ProbeQuery = (sql: string) => Promise<{
18
+ rows: Record<string, unknown>[];
19
+ }>;
20
+ /**
21
+ * 单表探针抓到可证缺列后,拒因里「哪几列缺、该跑哪几条 ALTER」+ 闭集出路这一整段(多行文本,不含探针自己的首句与底层错误)。
22
+ *
23
+ * 读不出库的列册(权限 / 超时)或对账看不到探针点名的表有缺列(探针与建表语句互相矛盾)⇒ **响亮**地说「ALTER 没能打印」及原因,
24
+ * 出路仍是同一份闭集(旧一条改成「按建表语句自己加」)—— 绝不退回销毁性指路,也不悄悄吞掉这一段。
25
+ */
26
+ export declare function describeProbedTableDrift(query: ProbeQuery, dialect: SqlDialect, tables: readonly string[]): Promise<string>;
27
+ //# sourceMappingURL=schema-drift-sentence.d.ts.map
@@ -0,0 +1,38 @@
1
+ import { readLiveColumns } from "./schema-live-columns.js";
2
+ export function schemaWaysOut(older) {
3
+ return [
4
+ " Ways out (these three and no others):",
5
+ " (1) deploy the build whose code matches this database;",
6
+ ` (2) the database is OLDER than this build (${older.when}): ${older.action} — the column check and this refusal never alter a database (the only DDL this service runs by itself on a table that already exists is idempotent index creation and the registered one-time migrations, see MIGRATION.md);`,
7
+ " (3) the database is NEWER than this build (unknown columns): deploy the build that owns those columns — upgrade the database first, then the replicas.",
8
+ ];
9
+ }
10
+ export const SCHEMA_MISMATCH_WAYS_OUT = schemaWaysOut({
11
+ when: "missing columns",
12
+ action: "run the ALTER TABLE … ADD COLUMN statements printed above yourself",
13
+ });
14
+ export async function describeProbedTableDrift(query, dialect, tables) {
15
+ const d = dialect === "pg" ? "pg" : "mysql";
16
+ const read = async (sql) => (await query(sql)).rows;
17
+ let why;
18
+ try {
19
+ const sc = await import("./schema-consistency.js");
20
+ const diff = sc.diffSchema(await sc.expectedTableShapes(d), await readLiveColumns(read, d));
21
+ const missingOnly = diff.filter((m) => tables.includes(m.table) && m.missing.length > 0).map((m) => ({ ...m, ghost: [] }));
22
+ if (missingOnly.length > 0)
23
+ return sc.describeMismatchedTables(missingOnly).join("\n");
24
+ why = "this build's table definitions list no column that the database lacks on that table, so the probe and the definitions disagree";
25
+ }
26
+ catch (err) {
27
+ why = `information_schema.columns could not be read (${err instanceof Error ? err.message : String(err)})`;
28
+ }
29
+ return [
30
+ ` table ${tables.join(", ")}: a column this build reads is MISSING — the database is OLDER than this build; the ALTER statements could not be printed: ${why}.`,
31
+ "",
32
+ ...schemaWaysOut({
33
+ when: "missing columns",
34
+ action: "add each missing column yourself with an ALTER TABLE … ADD COLUMN statement built from this build's definition of the table",
35
+ }),
36
+ ].join("\n");
37
+ }
38
+ //# sourceMappingURL=schema-drift-sentence.js.map
@@ -1,6 +1,5 @@
1
1
  import type { PgQueryFn } from "./pg-query.js";
2
- /** 录制的方言。`mysql` = MySQL 协议(TiDB / InnoDB 同腿),`pg` = PostgreSQL。 */
3
- export type SchemaDialect = "mysql" | "pg";
2
+ import type { SqlDialect } from "./sql-driver.js";
4
3
  /** advisory lock 语句 —— 与 schema 无关,录制时跳过(但要答成功,否则 ensure* 走降级告警路径)。 */
5
4
  export declare const LOCK_RE: RegExp;
6
5
  /**
@@ -51,5 +50,5 @@ export declare const DRIVEN_ENSURES: {
51
50
  readonly pg: readonly string[];
52
51
  };
53
52
  /** 跑一遍一条方言的真 `ensure*` 全腿,返回**录制到的语句流**(顺序即执行顺序)。纯内存,不碰数据库。 */
54
- export declare function recordEnsureStatements(dialect: SchemaDialect): Promise<string[]>;
53
+ export declare function recordEnsureStatements(dialect: SqlDialect): Promise<string[]>;
55
54
  //# sourceMappingURL=schema-ensure-legs.d.ts.map
@@ -6,10 +6,10 @@ import { ensurePgMemoryEmbedderMetaSchema } from "./memory-embedder-fingerprint.
6
6
  import { ensureTiDBMemorySyncSchema, ensureTiDBMemoryHistorySchema } from "./memory-sync-store-tidb.js";
7
7
  import { ensurePgMemorySyncSchema, ensurePgMemoryHistorySchema } from "./memory-sync-store-pg.js";
8
8
  import { ensureTiDBRosterSchema, ensurePgRosterSchema } from "./roster-store-sql.js";
9
- import { ensureTiDBBackgroundAgentSchema, ensurePgBackgroundAgentSchema } from "./background-agent-store-sql.js";
9
+ import { ensureTiDBBackgroundAgentSchema, ensurePgBackgroundAgentSchema } from "../stores-sql/background-agent-store-sql.js";
10
10
  import { ensureTiDBMailboxSchema, ensurePgMailboxSchema } from "./mailbox-store-sql.js";
11
11
  import { ensureTiDBTaskAttachmentSchema, ensurePgTaskAttachmentSchema } from "./task-attachment-store.js";
12
- import { ensureTiDBUsageWindowSchema, ensurePgUsageWindowSchema } from "./usage-window-store-sql.js";
12
+ import { ensureTiDBUsageWindowSchema, ensurePgUsageWindowSchema } from "../stores-sql/usage-window-store-sql.js";
13
13
  import { ensureTiDBTaskListSchema, ensurePgTaskListSchema } from "./task-list-store-sql.js";
14
14
  import { ensureTiDBDeviceSchema, ensurePgDeviceSchema } from "./device-store-sql.js";
15
15
  export const LOCK_RE = /GET_LOCK|RELEASE_LOCK|pg_(try_)?advisory/i;
@@ -3,7 +3,7 @@
3
3
  * 缺这条索引的列」也读它)。叶子模块:不值依赖任何模块(列册探针那边经录制驱动 / 建表腿值依赖 `ensure-index.ts`,读者若住在那边,
4
4
  * `ensure-index.ts` 读它就是一个运行时环)。方言词是 `ensure-index.ts` 的闭集(类型边,擦除)。
5
5
  */
6
- import type { SqlDialect } from "./ensure-index.js";
6
+ import type { SqlDialect } from "./sql-driver.js";
7
7
  /** 探针读口:一条 SQL → 行数组(两方言由调用方各自绑)。 */
8
8
  export type SchemaProbeRead = (sql: string) => Promise<Array<Record<string, unknown>>>;
9
9
  /**
@@ -83,7 +83,7 @@ export class SqlSessionCaptureRecordStore {
83
83
  }
84
84
  export class TiDBSessionCaptureRecordStore extends SqlSessionCaptureRecordStore {
85
85
  constructor(pool) {
86
- super(mysqlDriver(pool), "tidb");
86
+ super(mysqlDriver(pool), "mysql");
87
87
  }
88
88
  }
89
89
  export class PgSessionCaptureRecordStore extends SqlSessionCaptureRecordStore {
@@ -11,6 +11,10 @@
11
11
  * ## 为什么拷贝仍读整份日志、判官却读唤醒出来的会话
12
12
  * 拷贝要的是**整份**持久日志(压缩地板绕过 —— 唤醒只装 `[地板..叶]`,从唤醒里拷会丢地板之前的全部历史);判官要的只是叶链上最近几条(它是引擎的一份纯函数,
13
13
  * 吃一只 `Session`):唤醒出来的会话 `getEntry` 在地板之下会回读持久日志,所以判官在压缩过的会话上也看得见整条叶链。
14
+ *
15
+ * ## fork 续写的店契约(本文件下半)
16
+ * 三店同一只私有体 `forkTo(sourceId, owner, { id, key? })`:目标行原子新建(唯一创建点)· 撞已在按属主与 fork 键分类({@link classifyForkTarget})·
17
+ * local 崩溃中间态 `forking` 只许同键重试补齐 · fork 键此后每个写携带(整段换装经 `carriedRowCells`)。core 契约 `fork(sourceId, owner?)` = {@link forkByFreshId}。
14
18
  */
15
19
  import { type ForkPoint, type ForkRowShape, type SessionStorage } from "@sema-agent/core";
16
20
  /** 问引擎:这只(已唤醒的)源会话从哪里拷。SQL 孪生用它;local 店手里就是引擎的 `Session`,直接调 `settledForkOptions`。 */
@@ -28,4 +32,54 @@ export interface ForkLogRow {
28
32
  * 引擎的原词原句)。本仓不再自写父链走法 —— 那曾是与引擎 `getPathToRoot` 同义的第二份。
29
33
  */
30
34
  export declare const FORK_LOG_ROW_SHAPE: ForkRowShape<ForkLogRow>;
35
+ /**
36
+ * `forkTo` 的目标:调用方给的新会话 id 与(可选)fork 键。`key` 是服务端拼的不透明串(路由按「源 + 调用方 nonce」派生),存进新行那一格
37
+ * `fork_key`(SQL)/ `forkKey`(local):一个事实 = 「建这一行的那次 fork 请求的键」。缺 = 非键控 fork(core 契约的现铸形)。
38
+ */
39
+ export interface ForkTarget {
40
+ readonly id: string;
41
+ readonly key?: string;
42
+ }
43
+ /**
44
+ * fork 键的**唯一**拼法(路由带键 ⇒ `forkTo(…, { id: forkId, key: forkKeyOf(源, forkNonce) })`):服务端拼的不透明串 —— 源 id 与调用方 nonce 的摘要,
45
+ * 定长(远短于 `fork_key` 的 255),源进摘要 ⇒ 「异源同 nonce」拼出不同的键(撞同一只目标即 409,不当重放)。调用方不读它,只靠同键重试同答。
46
+ */
47
+ export declare function forkKeyOf(sourceId: string, forkNonce: string): string;
48
+ /**
49
+ * `forkTo` 的结果(`null` = 源不在,与 core `fork` 的 `null` 同义):
50
+ * · `created` —— 目标行由这一次新建(唯一创建点:SQL 事务首句 INSERT 主键排他;local 两把写锁内「目标格不在 ∧ 目标存档不在」才写目标格);
51
+ * · `replayed` —— 目标已在,属主 = 本次新属主 ∧ fork 键相等(同键重试同答;local `forking` 中间态在两把锁内补拷存档后也答它);
52
+ * · `occupied` —— 目标已在而不是这次请求的那一行:`sameOwner: false` ⇒ 路由答与未知源同形的 404(不泄存在),`true` ⇒ 409 `conflict.fork_id_taken`(终局)。
53
+ */
54
+ export type ForkToOutcome = {
55
+ readonly outcome: "created";
56
+ readonly id: string;
57
+ } | {
58
+ readonly outcome: "replayed";
59
+ readonly id: string;
60
+ } | {
61
+ readonly outcome: "occupied";
62
+ readonly sameOwner: boolean;
63
+ };
64
+ /** 撞上已在的目标时的唯一分类(三店同调):属主不同 ⇒ `occupied{sameOwner:false}`;属主同 ∧ 本次带键 ∧ 键相等 ⇒ `replayed`;其余(含无键的普通会话、无键请求)⇒ `occupied{sameOwner:true}`。 */
65
+ export declare function classifyForkTarget(target: ForkTarget, existing: {
66
+ readonly owner: string | null | undefined;
67
+ readonly forkKey: string | null | undefined;
68
+ }, forkOwner: string | null | undefined): ForkToOutcome;
69
+ /**
70
+ * SQL 输方回滚后在新视图里读不到目标(赢方在分类之前回滚或被删),整笔重跑一次之后仍读不到 —— 不猜,响亮抛;路由答 503 `permanence: "transient"`。
71
+ * 具名类型:按类型认,不按文案。
72
+ */
73
+ export declare class ForkTargetUnsettledError extends Error {
74
+ readonly targetId: string;
75
+ readonly code = "state.fork_target_unsettled";
76
+ constructor(targetId: string);
77
+ }
78
+ /** 三店的 `forkTo` 形(私有体,经店面 `forkTo` 交出)。 */
79
+ export type ForkToFn = (sourceId: string, owner: string | null, target: ForkTarget) => Promise<ForkToOutcome | null>;
80
+ /**
81
+ * core 契约 `fork(sourceId, owner?) → string | null` = `forkTo(…, { id: uuidv7() })` 的投影(三店同一只):`created` ⇒ 新 id;源不在 ⇒ `null`。
82
+ * 新铸的 uuidv7 撞上已在的行(`occupied` / `replayed`)不是一个答案,是不变量破 ⇒ 响亮抛 —— 无键请求永不答 `replayed`。
83
+ */
84
+ export declare function forkByFreshId(forkTo: ForkToFn, sourceId: string, owner: string | null | undefined): Promise<string | null>;
31
85
  //# sourceMappingURL=session-fork.d.ts.map
@@ -1,4 +1,5 @@
1
- import { StoredSession, settledForkOptions } from "@sema-agent/core";
1
+ import { SessionError, StoredSession, settledForkOptions, uuidv7 } from "@sema-agent/core";
2
+ import { createHash } from "node:crypto";
2
3
  export async function forkPointOf(storage) {
3
4
  return settledForkOptions(new StoredSession(storage));
4
5
  }
@@ -6,4 +7,40 @@ export const FORK_LOG_ROW_SHAPE = {
6
7
  idOf: (row) => row.entry_id,
7
8
  parentOf: (row) => row.parent_id,
8
9
  };
10
+ export function forkKeyOf(sourceId, forkNonce) {
11
+ return `fork1:${createHash("sha256").update(sourceId).update("\u0000").update(forkNonce).digest("hex")}`;
12
+ }
13
+ export function classifyForkTarget(target, existing, forkOwner) {
14
+ if ((existing.owner ?? null) !== (forkOwner ?? null))
15
+ return { outcome: "occupied", sameOwner: false };
16
+ if (target.key !== undefined && existing.forkKey === target.key)
17
+ return { outcome: "replayed", id: target.id };
18
+ return { outcome: "occupied", sameOwner: true };
19
+ }
20
+ export class ForkTargetUnsettledError extends Error {
21
+ targetId;
22
+ code = "state.fork_target_unsettled";
23
+ constructor(targetId) {
24
+ super(`fork target ${targetId} collided with another writer twice and could not be read back either time — retry the fork`);
25
+ this.targetId = targetId;
26
+ this.name = "ForkTargetUnsettledError";
27
+ }
28
+ }
29
+ export async function forkByFreshId(forkTo, sourceId, owner) {
30
+ const id = uuidv7();
31
+ const out = await forkTo(sourceId, owner ?? null, { id });
32
+ if (out === null)
33
+ return null;
34
+ switch (out.outcome) {
35
+ case "created":
36
+ return out.id;
37
+ case "replayed":
38
+ case "occupied":
39
+ throw new SessionError("storage", `fork of session ${sourceId}: a freshly minted id ${id} collided with an existing session (${out.outcome}) — nothing was written`);
40
+ default: {
41
+ const unknownOutcome = out;
42
+ throw new Error(`forkByFreshId: no arm for ${JSON.stringify(unknownOutcome)}`);
43
+ }
44
+ }
45
+ }
9
46
  //# sourceMappingURL=session-fork.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * **会话行格写原语的 SQL 半** —— 两孪生(`tidb-session-store.ts` / `pg-session-storage.ts`)同一只实现,方言只在语句文本里。规则在 `src/session-row.ts`
3
+ * 头注「写原语」;判序只在那里的判官 `judgeSessionRowWrite`,本模块只说事务与语句。
4
+ *
5
+ * ## 一只事务(经 `SqlDriver.connect()`)
6
+ * ① `SELECT … FROM session_meta WHERE session_id = ? FOR UPDATE` —— 判定读 = 锁读(当前读;连接初始化钉住读语义:TiDB 悲观、设不上拒启,
7
+ * `sql-driver.ts` 的 `@contract txn.read-semantics`);行期望带 `log` 时,同锁下读日志的 mark:长度 = 锁住的行上的 `leaf_seq`(每次追加与
8
+ * `leaf_id` 同句 +1,导入 / 换装 / fork 写成条目数),末条 = `session_event` 按 `seq` 倒序第一行(锁读)。追加者先锁这一行再插事件,所以持锁期间 mark 不动。
9
+ * ② 判官(行期望 → 写者身份 → 标签);不是 `write` ⇒ 回滚、一个字节不写。
10
+ * ③ `write` ⇒ 按 `session_id` 写(行已被本事务锁住)→ 提交。`updated_at` 不动(改名不是活动 —— 列表的新近序不变)。
11
+ * 应答行在**提交 / 回滚之后**由调用方的列表行读另读(新语句、新快照 —— 不在事务内用普通 `SELECT` 读:TiDB 悲观事务的普通读钉 `start_ts`,
12
+ * 等锁之后会读回判定之前的旧值),再交 `settleSessionRowWrite` 复核住客。
13
+ * 全局锁序:只锁根 `session_meta` 一行(读日志 mark 时再锁它的末条事件),不碰 `task_*`,不进放置写者「根 meta → `task_*`」锁序的后段。
14
+ */
15
+ import type { SqlDriver } from "./sql-driver.js";
16
+ import { type SessionRowAnswer, type SessionRowPatch, type SessionRowWrite, type SessionRowWriteOutcome } from "../session-row.js";
17
+ /**
18
+ * 写原语(头注):一只事务里锁根行 → 判官 → 写 / 不写;提交 / 回滚之后经 `readAnswer`(调用方店的列表行读 + 行上的住客)另读应答行。
19
+ * `db` = 店自己的池包成的 SqlDriver(连接经它的初始化);`readAnswer` 只在判官答 `write` / `changed` 时被调。
20
+ */
21
+ export declare function writeSessionRowCellsSql<Row>(db: Pick<SqlDriver, "dialect" | "connect">, sessionId: string, cells: SessionRowPatch, how: SessionRowWrite, readAnswer: (sessionId: string) => Promise<SessionRowAnswer<Row> | undefined>): Promise<SessionRowWriteOutcome<Row>>;
22
+ //# sourceMappingURL=session-row-sql.d.ts.map
@@ -0,0 +1,81 @@
1
+ import { incarnationFromCell } from "../session-incarnation.js";
2
+ import { judgeSessionRowWrite, settleSessionRowWrite, } from "../session-row.js";
3
+ function binder(dialect) {
4
+ const values = [];
5
+ return {
6
+ values,
7
+ p: (v) => {
8
+ values.push(v);
9
+ return dialect === "pg" ? `$${values.length}` : "?";
10
+ },
11
+ };
12
+ }
13
+ function lockedRowFrom(dialect, sessionId, row) {
14
+ return {
15
+ owner: row.owner === null || row.owner === undefined ? null : String(row.owner),
16
+ incarnation: incarnationFromCell(sessionId, row.incarnation, "absent"),
17
+ placed: row.placement_kind !== null && row.placement_kind !== undefined,
18
+ mirror: row.execution_role === "mirror",
19
+ cells: {
20
+ title: row.title === null || row.title === undefined ? null : String(row.title),
21
+ pinned: dialect === "pg" ? row.pinned === true : Number(row.pinned) === 1,
22
+ },
23
+ };
24
+ }
25
+ async function lockedLogMark(conn, dialect, sessionId, leafSeq) {
26
+ const b = binder(dialect);
27
+ const last = await conn.query(`SELECT entry_id FROM session_event WHERE session_id = ${b.p(sessionId)} ORDER BY seq DESC LIMIT 1 FOR UPDATE`, b.values);
28
+ const id = last.rows[0]?.entry_id;
29
+ return { length: Number(leafSeq), lastEntryId: id === undefined || id === null ? null : String(id) };
30
+ }
31
+ export async function writeSessionRowCellsSql(db, sessionId, cells, how, readAnswer) {
32
+ const dialect = db.dialect;
33
+ let verdict;
34
+ const conn = await db.connect();
35
+ try {
36
+ await conn.begin();
37
+ const read = binder(dialect);
38
+ const locked = (await conn.query(`SELECT owner, incarnation, placement_kind, execution_role, title, pinned, leaf_seq FROM session_meta WHERE session_id = ${read.p(sessionId)} FOR UPDATE`, read.values)).rows[0];
39
+ const mark = how.row.log === undefined || locked === undefined ? undefined : await lockedLogMark(conn, dialect, sessionId, locked.leaf_seq);
40
+ verdict = judgeSessionRowWrite(sessionId, how, locked === undefined ? undefined : lockedRowFrom(dialect, sessionId, locked), () => {
41
+ if (mark === undefined)
42
+ throw new Error(`writeSessionRowCellsSql: the judge asked session ${sessionId}'s log mark, but the expectation carries none`);
43
+ return mark;
44
+ });
45
+ if (verdict !== "write") {
46
+ await conn.rollback();
47
+ }
48
+ else {
49
+ const w = binder(dialect);
50
+ const sets = [];
51
+ if (cells.title !== undefined)
52
+ sets.push(`title = ${w.p(cells.title)}`);
53
+ if (cells.pinned !== undefined)
54
+ sets.push(`pinned = ${w.p(cells.pinned)}`);
55
+ if (sets.length > 0)
56
+ await conn.query(`UPDATE session_meta SET ${sets.join(", ")} WHERE session_id = ${w.p(sessionId)}`, w.values);
57
+ await conn.commit();
58
+ }
59
+ }
60
+ catch (err) {
61
+ await conn.rollback().catch(() => undefined);
62
+ throw err;
63
+ }
64
+ finally {
65
+ conn.release();
66
+ }
67
+ switch (verdict) {
68
+ case "absent":
69
+ return { kind: "absent" };
70
+ case "mirror":
71
+ return { kind: "mirror" };
72
+ case "write":
73
+ case "changed":
74
+ return settleSessionRowWrite(how, verdict, await readAnswer(sessionId));
75
+ default: {
76
+ const unknownVerdict = verdict;
77
+ throw new Error(`writeSessionRowCellsSql: no verdict ${JSON.stringify(unknownVerdict)}`);
78
+ }
79
+ }
80
+ }
81
+ //# sourceMappingURL=session-row-sql.js.map
@@ -10,7 +10,7 @@
10
10
  */
11
11
  export declare const SESSION_RULES_COLUMN: {
12
12
  readonly name: "rule_entries";
13
- readonly tidb: "rule_entries JSON NOT NULL";
13
+ readonly mysql: "rule_entries JSON NOT NULL";
14
14
  readonly pg: "rule_entries JSONB NOT NULL";
15
15
  };
16
16
  //# sourceMappingURL=session-rules-column.d.ts.map
@@ -1,6 +1,6 @@
1
1
  export const SESSION_RULES_COLUMN = {
2
2
  name: "rule_entries",
3
- tidb: "rule_entries JSON NOT NULL",
3
+ mysql: "rule_entries JSON NOT NULL",
4
4
  pg: "rule_entries JSONB NOT NULL",
5
5
  };
6
6
  //# sourceMappingURL=session-rules-column.js.map
@@ -46,7 +46,7 @@ export declare function ensureSessionDurable(hostStore: SessionStore, sessionId:
46
46
  * 恒否决 park——bg 子会话住 subRunner 私有 TTL 店、从不在 host 店,core 收 veto 即 expire checkpoint
47
47
  * (14ms~2s)+ settle failed,赎回窗口不存在):
48
48
  * 0. 属主([ref],`delegated-session-owner.ts`):按委派继承律判一次(唯一判据 `judgeDelegatedOwner`)—— 来源集 = 见证的根这一只(调用方交出的委派树根 + 调用方判断那一刻
49
- * 读到的根行住客:请求腿 = 本请求经鉴权解析的会话与受理时那一读,`/decide` 赎回腿 = 行上的 `rootSessionId` 与 claim 前后两读的那一位;core 的挂点签名只给子代 id,
49
+ * 读到的根行住客:请求腿 = 本请求经鉴权解析的会话与受理时那一读,`/decide` 赎回腿 = 行上的 `rootSessionId` 与写口放行的那一位(认领之前解析器那一读);core 的挂点签名只给子代 id,
50
50
  * 场景层把见证绑进闭包交下来)。此刻根行在而住客不是见证的那一位(删了、同 id 被重新登记)⇒ **拒**(`assertRootStillWitnessed`:不按新住客晋升 / 认领);
51
51
  * 缺席(行上没有根)或根行此刻不在 ⇒ 孤行哨兵。
52
52
  * 1. 快路:已在 host 店且有真内容 → 通过,**前提是那一行的属主按律定得下来**(`settleDelegatedOwner`:记着别的委派树 ⇒ 抛;记着这一棵 ⇒ 按它记录的根、父再判一次
@@ -3,7 +3,7 @@ import { type JsonColumnReader } from "./sql-json-column.js";
3
3
  /** 列定义的唯一属主(两方言 DDL 内插它;存量库缺这一列 ⇒ 起服列册对账拒启,拒句里的 ALTER 由建表语句生成)。 */
4
4
  export declare const SESSION_META_WORKSPACE_COLUMN: {
5
5
  readonly name: "workspace";
6
- readonly tidb: "workspace JSON NULL";
6
+ readonly mysql: "workspace JSON NULL";
7
7
  readonly pg: "workspace JSONB";
8
8
  };
9
9
  /** 这一格的形读者(三只店与帧投影同一只;写前也过它 —— 写得进的就是读得出的)。 */
@@ -4,7 +4,7 @@ import { recordFailOpen } from "../observability/fail-open.js";
4
4
  import { assertJsonColumnWritable, createZodColumnReader, decodeJsonColumn, isJsonColumnBodyProblem } from "./sql-json-column.js";
5
5
  export const SESSION_META_WORKSPACE_COLUMN = {
6
6
  name: "workspace",
7
- tidb: "workspace JSON NULL",
7
+ mysql: "workspace JSON NULL",
8
8
  pg: "workspace JSONB",
9
9
  };
10
10
  const SESSION_WORKSPACE_SHAPE = z.object({
@@ -106,8 +106,8 @@ export class SqlSharedMemoryStore {
106
106
  this.now = opts.now ?? Date.now;
107
107
  this.maxListLimit = opts.maxListLimit ?? DEFAULT_MAX_LIST_LIMIT;
108
108
  }
109
- q(tidb, pg) {
110
- return this.db.dialect === "tidb" ? tidb : pg;
109
+ q(mysql, pg) {
110
+ return this.db.dialect === "mysql" ? mysql : pg;
111
111
  }
112
112
  async snapshot(ctx, opts) {
113
113
  throwIfAborted(opts.signal);
@@ -68,7 +68,7 @@
68
68
  * ─────────────────────────────────────────────────────────────────────────────────────────────────
69
69
  * Reference collapses: image-bake-store-sql.ts (class + transactions) and checkpoint-store-sql.ts
70
70
  * (class, no transactions). Function-style stores follow task-list-store-sql.ts instead (inline
71
- * `dialect === "tidb" ? … : …` ternaries, no driver object) — pick the shape the pair already has.
71
+ * `dialect === "mysql" ? … : …` ternaries, no driver object) — pick the shape the pair already has.
72
72
  *
73
73
  * STEPS
74
74
  * 1. Read BOTH twins end to end first. List every method and mark it identical / SQL-text-only /
@@ -79,7 +79,7 @@
79
79
  * 3. Per statement, write BOTH texts at the call site via a 2-arg `q(tidbSql, pgSql)` helper.
80
80
  * Never build one text with a placeholder-numbering loop — the point is that a reviewer sees
81
81
  * the two statements adjacent.
82
- * 4. Genuinely-divergent algorithms get an `if (dialect === "tidb") { … } else { … }` block with a
82
+ * 4. Genuinely-divergent algorithms get an `if (dialect === "mysql") { … } else { … }` block with a
83
83
  * 🔴 comment saying WHY both are correct. Never "unify" them by picking one engine's algorithm.
84
84
  * 5. Old files become thin re-export shims. Class names, ctor arity, exported constants/types and
85
85
  * helper names stay EXACTLY as they were — consumers and tests must not move.
@@ -98,7 +98,7 @@
98
98
  * (pgSafeJsonStringify / pgProtocolJsonStringify / pgSanitizeText / pgHasUnstorable) · DDL. Each arm's
99
99
  * BEHAVIOR (what pgSafeJsonStringify/pgProtocolJsonStringify actually do to the bytes) stays written out
100
100
  * in `pg-safe-json.ts`, untouched — `dialectJsonEncoder`/`dialectProtocolJsonEncoder` below ([ref]
101
- * redesign①) only collect the identical `dialect === "tidb" ? JSON.stringify(v) : pgSafe…(v)` DISPATCH
101
+ * redesign①) only collect the identical `dialect === "mysql" ? JSON.stringify(v) : pgSafe…(v)` DISPATCH
102
102
  * ternary that five call sites duplicated verbatim; they are not a new abstraction over the encodings
103
103
  * themselves, so this does not violate the rule above.
104
104
  *
@@ -112,7 +112,12 @@
112
112
  import type { Pool as MySqlPool } from "mysql2/promise";
113
113
  import type { Pool as PgPool } from "pg";
114
114
  import type { Logger } from "../observability/logger.js";
115
- export type SqlDialect = "tidb" | "pg";
115
+ /**
116
+ * SQL **文本**的语法族(调用方说,不探):`mysql` = MySQL 协议(TiDB / MySQL InnoDB / MariaDB 同一套语句),`pg` = PostgreSQL。
117
+ * 与 `@sema-agent/core-stores-sql` 公面的 `SqlDialect` 同词同集 —— 部署词 / 引擎词都不是它的别名:谁在答是另一轴 {@link SqlEngine}(驱动探)。
118
+ * 本仓只这一份方言闭集(加腿 = 编译错,不是运行期的 `default` 臂)。
119
+ */
120
+ export type SqlDialect = "mysql" | "pg";
116
121
  export type SqlRow = Record<string, unknown>;
117
122
  /** Normalized statement result: `rows` for SELECTs, `affected` for INSERT/UPDATE/DELETE. */
118
123
  export interface SqlResult {
@@ -337,7 +342,7 @@ export declare function sqlWallsOf(engine: SqlEngineReading | undefined): SqlWal
337
342
  * 放在某一只店里会让下一个需要它的店要么 import 一整只无关的店、要么自己抄一份(抄出来的两份会漂)。
338
343
  */
339
344
  export declare const SQL_DB_NOW_MS: {
340
- readonly tidb: "CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS SIGNED)";
345
+ readonly mysql: "CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS SIGNED)";
341
346
  readonly pg: "(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint";
342
347
  };
343
348
  /**
@@ -48,7 +48,7 @@ export function sqlWallsOf(engine) {
48
48
  switch (engine.dialect) {
49
49
  case "pg":
50
50
  return NO_WALLS;
51
- case "tidb":
51
+ case "mysql":
52
52
  return SQL_FACTS_ABSENT_WALLS;
53
53
  default: {
54
54
  const unhandled = engine.dialect;
@@ -57,11 +57,11 @@ export function sqlWallsOf(engine) {
57
57
  }
58
58
  }
59
59
  export const SQL_DB_NOW_MS = {
60
- tidb: "CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS SIGNED)",
60
+ mysql: "CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS SIGNED)",
61
61
  pg: "(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint",
62
62
  };
63
63
  export async function sqlDbNowMs(db, subject) {
64
- const sql = db.dialect === "tidb" ? `SELECT ${SQL_DB_NOW_MS.tidb} AS db_now_ms` : `SELECT ${SQL_DB_NOW_MS.pg} AS db_now_ms`;
64
+ const sql = `SELECT ${SQL_DB_NOW_MS[db.dialect]} AS db_now_ms`;
65
65
  const { rows } = await db.query(sql, []);
66
66
  const n = Number(rows[0]?.["db_now_ms"] ?? Number.NaN);
67
67
  if (!Number.isFinite(n))
@@ -375,7 +375,7 @@ export function createCloseAwarePool(pool) {
375
375
  export function mysqlDriver(pool) {
376
376
  const session = poolSession(pool);
377
377
  return {
378
- dialect: "tidb",
378
+ dialect: "mysql",
379
379
  get facts() {
380
380
  return session.facts;
381
381
  },
@@ -471,6 +471,6 @@ export function pgDriver(pool) {
471
471
  },
472
472
  };
473
473
  }
474
- export const dialectJsonEncoder = (dialect) => dialect === "tidb" ? JSON.stringify : pgSafeJsonStringify;
475
- export const dialectProtocolJsonEncoder = (dialect) => dialect === "tidb" ? (v) => JSON.stringify(v) : pgProtocolJsonStringify;
474
+ export const dialectJsonEncoder = (dialect) => dialect === "mysql" ? JSON.stringify : pgSafeJsonStringify;
475
+ export const dialectProtocolJsonEncoder = (dialect) => dialect === "mysql" ? (v) => JSON.stringify(v) : pgProtocolJsonStringify;
476
476
  //# sourceMappingURL=sql-driver.js.map
@@ -13,7 +13,7 @@ export function isPgUniqueViolation(err) {
13
13
  return String(keyOf(err, "code")) === PG_UNIQUE_VIOLATION_SQLSTATE;
14
14
  }
15
15
  export function isDupKeyError(dialect, err) {
16
- return dialect === "tidb" ? isMysqlDupKeyError(err) : isPgUniqueViolation(err);
16
+ return dialect === "mysql" ? isMysqlDupKeyError(err) : isPgUniqueViolation(err);
17
17
  }
18
18
  export const PG_UNDEFINED_COLUMN_SQLSTATE = "42703";
19
19
  export const MYSQL_ER_BAD_FIELD_ERRNO = 1054;