@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
@@ -0,0 +1,223 @@
1
+ import { isExecutionLane } from "../execution-lane-caps.js";
2
+ import { sessionFaceFromRow } from "../session-incarnation.js";
3
+ import { toIso } from "./sql-row-helpers.js";
4
+ import { WRITABLE, assertPlaceableLane, settledMirrorOutcome, settledPlaceOutcome, } from "../execution-placement.js";
5
+ export const SESSION_META_PLACEMENT_COLUMNS = [
6
+ { name: "execution_role", mysql: "execution_role VARCHAR(16) NULL", pg: `execution_role VARCHAR(16) COLLATE "C"` },
7
+ { name: "execution_lane", mysql: "execution_lane VARCHAR(32) NULL", pg: `execution_lane VARCHAR(32) COLLATE "C"` },
8
+ { name: "execution_device_id", mysql: "execution_device_id VARCHAR(64) NULL", pg: `execution_device_id VARCHAR(64) COLLATE "C"` },
9
+ { name: "execution_device_owner_tenant", mysql: "execution_device_owner_tenant VARCHAR(190) NULL", pg: `execution_device_owner_tenant VARCHAR(190) COLLATE "C"` },
10
+ { name: "execution_device_owner_subject", mysql: "execution_device_owner_subject VARCHAR(190) NULL", pg: `execution_device_owner_subject VARCHAR(190) COLLATE "C"` },
11
+ { name: "execution_placed_at", mysql: "execution_placed_at DATETIME(3) NULL", pg: "execution_placed_at TIMESTAMPTZ(3)" },
12
+ { name: "execution_rev", mysql: "execution_rev BIGINT NULL", pg: "execution_rev BIGINT" },
13
+ { name: "fork_key", mysql: "fork_key VARCHAR(255) NULL", pg: `fork_key VARCHAR(255) COLLATE "C"` },
14
+ ];
15
+ export function sessionMetaPlacementColumnsDdl(dialect) {
16
+ return SESSION_META_PLACEMENT_COLUMNS.map((c) => c[dialect]).join(",\n ");
17
+ }
18
+ const WRITER = "writer";
19
+ const MIRROR = "mirror";
20
+ const NOT_WRITER = "(execution_role IS NULL OR execution_role <> 'writer')";
21
+ export const MIRROR_ON_COMMIT_SET = "execution_role = 'mirror'";
22
+ export const MIRROR_ON_COMMIT_GUARD = NOT_WRITER;
23
+ export function importedRowCells(forkKey) {
24
+ return { columns: "fork_key, execution_role", values: [forkKey, MIRROR] };
25
+ }
26
+ export const SESSION_META_PLACEMENT_READ_COLUMNS = "owner, incarnation, placement_kind, execution_role, execution_lane, execution_device_id, execution_device_owner_tenant, execution_device_owner_subject, execution_placed_at, execution_rev";
27
+ function binder(dialect) {
28
+ const values = [];
29
+ return {
30
+ values,
31
+ p: (v) => {
32
+ values.push(v);
33
+ return dialect === "pg" ? `$${values.length}` : "?";
34
+ },
35
+ };
36
+ }
37
+ function occupantPredicate(dialect, p, root, occupant) {
38
+ const nullSafeEq = dialect === "pg" ? "IS NOT DISTINCT FROM" : "<=>";
39
+ return `session_id = ${p(root)} AND incarnation = ${p(occupant.incarnation)} AND owner ${nullSafeEq} ${p(occupant.owner)} AND placement_kind IS NULL`;
40
+ }
41
+ export function readPlacementRowStatement(dialect, root, opts) {
42
+ const b = binder(dialect);
43
+ return { text: `SELECT ${SESSION_META_PLACEMENT_READ_COLUMNS} FROM session_meta WHERE session_id = ${b.p(root)}${opts.lock ? " FOR UPDATE" : ""}`, values: b.values };
44
+ }
45
+ export function placeWriterStatement(dialect, root, occupant, cell, now) {
46
+ const b = binder(dialect);
47
+ const set = `execution_role = '${WRITER}', execution_lane = ${b.p(cell.lane)}, execution_device_id = ${b.p(cell.device?.id ?? null)}, ` +
48
+ `execution_device_owner_tenant = ${b.p(cell.device?.owner.tenant ?? null)}, execution_device_owner_subject = ${b.p(cell.device?.owner.subject ?? null)}, ` +
49
+ `execution_placed_at = ${b.p(now)}, execution_rev = 0`;
50
+ return { text: `UPDATE session_meta SET ${set} WHERE ${occupantPredicate(dialect, b.p, root, occupant)} AND execution_role IS NULL`, values: b.values };
51
+ }
52
+ export function bindDeviceStatement(dialect, root, occupant, device, now) {
53
+ const b = binder(dialect);
54
+ const set = `execution_device_id = ${b.p(device.id)}, execution_device_owner_tenant = ${b.p(device.owner.tenant)}, execution_device_owner_subject = ${b.p(device.owner.subject)}, execution_placed_at = ${b.p(now)}, execution_rev = 0`;
55
+ return {
56
+ text: `UPDATE session_meta SET ${set} WHERE ${occupantPredicate(dialect, b.p, root, occupant)} AND execution_role = '${WRITER}' AND execution_lane = 'device' AND execution_device_id IS NULL`,
57
+ values: b.values,
58
+ };
59
+ }
60
+ export function rebindDeviceStatement(dialect, root, occupant, owner, expectedRev, targetDeviceId, now) {
61
+ const b = binder(dialect);
62
+ const set = `execution_device_id = ${b.p(targetDeviceId)}, execution_placed_at = ${b.p(now)}, execution_rev = execution_rev + 1`;
63
+ const where = `${occupantPredicate(dialect, b.p, root, occupant)} AND execution_role = '${WRITER}' AND execution_lane = 'device' ` +
64
+ `AND execution_device_owner_tenant = ${b.p(owner.tenant)} AND execution_device_owner_subject = ${b.p(owner.subject)} AND execution_rev = ${b.p(expectedRev)}`;
65
+ return { text: `UPDATE session_meta SET ${set} WHERE ${where}`, values: b.values };
66
+ }
67
+ export function markMirrorStatement(dialect, root, occupant) {
68
+ const b = binder(dialect);
69
+ return { text: `UPDATE session_meta SET execution_role = '${MIRROR}' WHERE ${occupantPredicate(dialect, b.p, root, occupant)} AND ${NOT_WRITER}`, values: b.values };
70
+ }
71
+ export function backfillWriterStatement(dialect, lane, now) {
72
+ const b = binder(dialect);
73
+ return {
74
+ text: `UPDATE session_meta SET execution_role = '${WRITER}', execution_lane = ${b.p(lane)}, execution_placed_at = ${b.p(now)}, execution_rev = 0 ` +
75
+ "WHERE execution_role IS NULL AND placement_kind IS NULL AND EXISTS (SELECT 1 FROM task_run tr WHERE tr.session_id = session_meta.session_id)",
76
+ values: b.values,
77
+ };
78
+ }
79
+ export function migrateDeviceBindingsStatement(dialect, sourceTable) {
80
+ if (dialect === "pg") {
81
+ return {
82
+ text: `UPDATE session_meta SET execution_role = '${WRITER}', execution_lane = 'device', execution_device_id = ds.device_id, ` +
83
+ "execution_device_owner_tenant = ds.owner_tenant, execution_device_owner_subject = ds.owner_subject, execution_placed_at = ds.bound_at, execution_rev = ds.rev " +
84
+ `FROM ${sourceTable} ds WHERE ds.root_session_id = session_meta.session_id AND session_meta.placement_kind IS NULL`,
85
+ values: [],
86
+ };
87
+ }
88
+ const str = (col) => `CONVERT(ds.${col} USING utf8mb4)`;
89
+ return {
90
+ text: `UPDATE session_meta sm JOIN ${sourceTable} ds ON ${str("root_session_id")} COLLATE utf8mb4_bin = sm.session_id ` +
91
+ `SET sm.execution_role = '${WRITER}', sm.execution_lane = 'device', sm.execution_device_id = ${str("device_id")}, ` +
92
+ `sm.execution_device_owner_tenant = ${str("owner_tenant")}, sm.execution_device_owner_subject = ${str("owner_subject")}, sm.execution_placed_at = ds.bound_at, sm.execution_rev = ds.rev ` +
93
+ "WHERE sm.placement_kind IS NULL",
94
+ values: [],
95
+ };
96
+ }
97
+ export function countOrphanBindingsStatement(dialect, sourceTable) {
98
+ const rootKey = dialect === "pg" ? "ds.root_session_id" : "CONVERT(ds.root_session_id USING utf8mb4) COLLATE utf8mb4_bin";
99
+ return {
100
+ text: `SELECT COUNT(*) AS orphans FROM ${sourceTable} ds WHERE NOT EXISTS (SELECT 1 FROM session_meta sm WHERE sm.session_id = ${rootKey} AND sm.placement_kind IS NULL)`,
101
+ values: [],
102
+ };
103
+ }
104
+ export class ExecutionPlacementRowError extends Error {
105
+ sessionId;
106
+ code = "execution_placement.row_unreadable";
107
+ constructor(sessionId, detail) {
108
+ super(`session ${sessionId}: the execution placement columns do not form a placement (${detail}) — refusing to read it as any placement`);
109
+ this.sessionId = sessionId;
110
+ this.name = "ExecutionPlacementRowError";
111
+ }
112
+ }
113
+ const isNull = (v) => v === null || v === undefined;
114
+ const text = (v) => (Buffer.isBuffer(v) ? v.toString("utf8") : String(v));
115
+ export function placementCellFromRow(root, row) {
116
+ const role = row.execution_role;
117
+ const deviceCols = [row.execution_device_id, row.execution_device_owner_tenant, row.execution_device_owner_subject];
118
+ if (isNull(role) || role === MIRROR) {
119
+ if (!isNull(row.execution_lane) || deviceCols.some((v) => !isNull(v)))
120
+ throw new ExecutionPlacementRowError(root, `role ${isNull(role) ? "NULL" : MIRROR} with a lane or device`);
121
+ return isNull(role) ? { role: "absent" } : { role: "mirror" };
122
+ }
123
+ if (role !== WRITER)
124
+ throw new ExecutionPlacementRowError(root, `role ${JSON.stringify(text(role))} is not writer / mirror / NULL`);
125
+ const lane = isNull(row.execution_lane) ? "" : text(row.execution_lane);
126
+ if (!isExecutionLane(lane))
127
+ throw new ExecutionPlacementRowError(root, `writer lane ${JSON.stringify(lane)} is not an execution lane`);
128
+ if (isNull(row.execution_device_id)) {
129
+ if (deviceCols.some((v) => !isNull(v)))
130
+ throw new ExecutionPlacementRowError(root, "device owner without a device");
131
+ return { role: "writer", lane };
132
+ }
133
+ if (lane !== "device")
134
+ throw new ExecutionPlacementRowError(root, `a device on the ${lane} lane`);
135
+ if (deviceCols.some(isNull) || isNull(row.execution_rev) || isNull(row.execution_placed_at))
136
+ throw new ExecutionPlacementRowError(root, "a device without its owner, rev or placement time");
137
+ return {
138
+ role: "writer",
139
+ lane,
140
+ device: {
141
+ id: text(row.execution_device_id),
142
+ owner: { tenant: text(row.execution_device_owner_tenant), subject: text(row.execution_device_owner_subject) },
143
+ rev: Number(row.execution_rev),
144
+ placedAt: toIso(row.execution_placed_at),
145
+ },
146
+ };
147
+ }
148
+ export function placementReadingFromRow(root, row) {
149
+ if (row === undefined || !isNull(row.placement_kind))
150
+ return undefined;
151
+ return { occupant: sessionFaceFromRow(root, isNull(row.owner) ? null : text(row.owner), row.incarnation), cell: placementCellFromRow(root, row) };
152
+ }
153
+ export function rowsOnSqlExec(exec) {
154
+ return async (sql, values) => (await exec.query(sql, [...values])).rows;
155
+ }
156
+ export async function lockRootAndReadClaim(conn, dialect, root) {
157
+ const ph = dialect === "pg" ? "$1" : "?";
158
+ const row = await conn(`SELECT session_id FROM session_meta WHERE session_id = ${ph} FOR UPDATE`, [root]);
159
+ if (row.length === 0)
160
+ return { rowPresent: false, activeTaskId: null };
161
+ const claim = await conn(`SELECT task_id FROM task_active WHERE session_id = ${ph} FOR UPDATE`, [root]);
162
+ return { rowPresent: true, activeTaskId: claim[0] === undefined ? null : text(claim[0].task_id) };
163
+ }
164
+ export async function readPlacementOn(conn, dialect, root) {
165
+ const s = readPlacementRowStatement(dialect, root, { lock: true });
166
+ return placementReadingFromRow(root, (await conn(s.text, s.values))[0]);
167
+ }
168
+ function placementInvariant(root, what) {
169
+ return new Error(`execution placement of session ${root}: ${what} matched no row while a re-read finds the cell writable — engine/driver anomaly, refusing to guess`);
170
+ }
171
+ export function createSqlExecutionPlacementSeat(db) {
172
+ const read = async (root) => {
173
+ const s = readPlacementRowStatement(db.dialect, root, { lock: false });
174
+ return placementReadingFromRow(root, (await db.query(s.text, [...s.values])).rows[0]);
175
+ };
176
+ return {
177
+ read,
178
+ async place(root, occupant, cell) {
179
+ assertPlaceableLane(root, cell);
180
+ const s = placeWriterStatement(db.dialect, root, occupant, { lane: cell.lane }, new Date());
181
+ if ((await db.query(s.text, [...s.values])).affected === 1)
182
+ return { outcome: "placed" };
183
+ const settled = settledPlaceOutcome(root, await read(root), occupant, cell);
184
+ if (settled === WRITABLE)
185
+ throw placementInvariant(root, "place");
186
+ return settled;
187
+ },
188
+ async markMirror(root, occupant) {
189
+ const conn = await db.connect();
190
+ try {
191
+ await conn.begin();
192
+ const rowsOn = rowsOnSqlExec(conn);
193
+ const claim = await lockRootAndReadClaim(rowsOn, db.dialect, root);
194
+ if (!claim.rowPresent) {
195
+ await conn.rollback();
196
+ return { outcome: "gone" };
197
+ }
198
+ const settled = settledMirrorOutcome(root, await readPlacementOn(rowsOn, db.dialect, root), occupant);
199
+ if (settled !== WRITABLE) {
200
+ await conn.rollback();
201
+ return settled;
202
+ }
203
+ if (claim.activeTaskId !== null) {
204
+ await conn.rollback();
205
+ return { outcome: "active", activeTaskId: claim.activeTaskId };
206
+ }
207
+ const s = markMirrorStatement(db.dialect, root, occupant);
208
+ if ((await conn.query(s.text, [...s.values])).affected !== 1)
209
+ throw placementInvariant(root, "markMirror under a held row lock");
210
+ await conn.commit();
211
+ return { outcome: "mirrored" };
212
+ }
213
+ catch (err) {
214
+ await conn.rollback().catch(() => undefined);
215
+ throw err;
216
+ }
217
+ finally {
218
+ conn.release();
219
+ }
220
+ },
221
+ };
222
+ }
223
+ //# sourceMappingURL=execution-placement-sql.js.map
@@ -3,7 +3,7 @@ import type { Pool as PgPool } from "pg";
3
3
  import { type FileHistoryRetentionPolicy, type ExecutionEnv, type FileHistoryStore, type FileHistoryResult, type FileHistoryTrackResult, type FileHistoryRestoreResult, type FileHistoryExport, type FileHistoryExportResult } from "@sema-agent/core";
4
4
  import { type BlobBackend } from "./blob-backend.js";
5
5
  import type { SyncBlobPutResult } from "./store-backend.js";
6
- import { type SqlDriver } from "./sql-driver.js";
6
+ import { type SqlDriver, type SqlDialect } from "./sql-driver.js";
7
7
  /** The file-history blob domain's SQL table (BlobBackend `table`/MinIO index binding + the A7 assert). */
8
8
  export declare const FILE_HISTORY_BLOB_TABLE = "file_history_blob";
9
9
  /** MinIO object-key prefix for this domain (snapshot epoch used "blobs/"; a separate prefix keeps the
@@ -183,17 +183,18 @@ export declare class PgFileHistoryStore extends SqlFileHistoryStore {
183
183
  }
184
184
  /**
185
185
  * [ref] D1② — A7-recipe upgrade precondition assert (**refuse to start**, never a warn; the form and
186
- * rationale follow `assertPermissionRuleTicketSchema` verbatim: this repo ships no ALTER TABLE, and
186
+ * rationale follow `assertPermissionRuleTicketSchema` verbatim: the boot check never alters a table, and
187
187
  * `CREATE TABLE IF NOT EXISTS` is a no-op on an existing table whose columns predate this build).
188
188
  *
189
189
  * The probe is one always-empty `WHERE 1=0` read per table naming every column this build references.
190
- * ONLY a provable missing-column error gets the destructive recreate guidance — everything else
191
- * (timeout / reset / permission / missing table) rethrows untouched: advising `DROP TABLE` for a
192
- * connection blip is more expensive than the defect this gate exists to stop. Honest cost statement:
193
- * dropping these tables loses per-file REWIND history (working-tree convenience state) — session
194
- * transcripts and every other durable subsystem are untouched.
190
+ * ONLY a provable missing-column error gets the missing-column refusal — everything else
191
+ * (timeout / reset / permission / missing table) rethrows untouched: diagnosing a connection blip as a
192
+ * schema drift is more expensive than the defect this gate exists to stop. The refusal's ways out are the
193
+ * closed set of three shared with the whole-database check (`describeProbedTableDrift`); none of them
194
+ * is destructive. Honest cost statement: the history lives in these tables (per-file REWIND history,
195
+ * working-tree convenience state) — session transcripts and every other durable subsystem are untouched.
195
196
  */
196
197
  export declare function assertFileHistorySchema(query: (sql: string) => Promise<{
197
198
  rows: Record<string, unknown>[];
198
- }>, dialect: "tidb" | "pg"): Promise<void>;
199
+ }>, dialect: SqlDialect): Promise<void>;
199
200
  //# sourceMappingURL=file-history-store-sql.d.ts.map
@@ -3,6 +3,7 @@ import { captureFileBackupViaEnv, applyFileRestoreViaEnv, fileHistoryModeProvenD
3
3
  import { BlobTooLargeError, SqlBlobBackend } from "./blob-backend.js";
4
4
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
5
5
  import { isMissingColumnError } from "./sql-errors.js";
6
+ import { describeProbedTableDrift } from "./schema-drift-sentence.js";
6
7
  import { recordFailOpen } from "../observability/fail-open.js";
7
8
  import { foldCaughtFailure } from "../file-history-disclosure.js";
8
9
  export const FILE_HISTORY_BLOB_TABLE = "file_history_blob";
@@ -32,13 +33,13 @@ export class SqlFileHistoryStore {
32
33
  this.db = db;
33
34
  this.pool = pool;
34
35
  this.blobs = blobBackend ?? new SqlBlobBackend(this.db.dialect, this.pool, undefined, FILE_HISTORY_BLOB_TABLE);
35
- this.boundaryKeep = resolveFileHistoryRetention(this.db.dialect === "tidb" ? "TiDBFileHistoryStore" : "PgFileHistoryStore", opts?.retention);
36
+ this.boundaryKeep = resolveFileHistoryRetention(this.db.dialect === "mysql" ? "TiDBFileHistoryStore" : "PgFileHistoryStore", opts?.retention);
36
37
  }
37
- q(tidb, pg) {
38
- return this.db.dialect === "tidb" ? tidb : pg;
38
+ q(mysql, pg) {
39
+ return this.db.dialect === "mysql" ? mysql : pg;
39
40
  }
40
41
  bool(v) {
41
- return this.db.dialect === "tidb" ? (v ? 1 : 0) : v;
42
+ return this.db.dialect === "mysql" ? (v ? 1 : 0) : v;
42
43
  }
43
44
  leaseKey(scope, entryId) {
44
45
  return `${scope}\x00${entryId}`;
@@ -835,11 +836,10 @@ export async function assertFileHistorySchema(query, dialect) {
835
836
  throw err;
836
837
  throw new Error(`${table} is missing required columns — refusing to start. This build's per-file rewind history (S-15) ` +
837
838
  `references every probed column unconditionally; a partial table would fail mid-write and could publish a ` +
838
- `half-shaped history graph. This repository ships no ALTER TABLE migrations, so the table must be recreated: run ` +
839
- `\`DROP TABLE ${table};\` on the ${dialect === "pg" ? "PostgreSQL" : "MySQL-protocol"} backend and restart — the ` +
840
- `schema is recreated at boot. Cost, honestly stated: dropping file_history_* tables loses per-file REWIND history ` +
841
- `(working-tree convenience state); session transcripts and all other durable subsystems are untouched. ` +
842
- `Underlying probe error: ${err instanceof Error ? err.message : String(err)}`);
839
+ `half-shaped history graph. The file_history_* tables hold the per-file REWIND history (working-tree convenience ` +
840
+ `state); session transcripts and all other durable subsystems are untouched, and an in-place ALTER keeps the history.\n` +
841
+ `Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
842
+ (await describeProbedTableDrift(query, dialect, [table])));
843
843
  }
844
844
  }
845
845
  }
@@ -152,7 +152,7 @@ export declare class FileRunStore implements InProcessClaimFence {
152
152
  whileHoldingClaim(sessionId: string, taskId: string, write: () => FenceLanded): boolean;
153
153
  getRun(taskId: string): Promise<RunRecord | undefined>;
154
154
  /** Undo THIS submission's binding (key + body fingerprint → null) — called only where the submit surface refuses the submission
155
- * before any work and terminalizes the row it just claimed (the sync leg's zero-work refusal, a device-placement reject):
155
+ * before any work and terminalizes the row it just claimed (the sync leg's zero-work refusal, an execution-placement reject):
156
156
  * the error answer invites a retry, and a retry with the same key must run, not replay the refusal. The row stays (it
157
157
  * is the refusal's record); only the binding is freed. */
158
158
  releaseIdemKey(taskId: string): Promise<void>;
@@ -332,6 +332,11 @@ export declare class FileRunStore implements InProcessClaimFence {
332
332
  * 起服时调(`LocalBackend.runOneTimeMigrations`,任何读者之前);数据根独占 ⇒ 本车道没有滚动升级窗,不需要读侧半场。幂等:第二跑零命中零写盘。
333
333
  */
334
334
  migrateLegacyHolderDeathCodes(): number;
335
+ /**
336
+ * 一次性迁移 **M32 的 local 腿**(执行放置回填;窗、删法与删后谁接住:窗口见 `plugins/one-time-migrations.ts` 登记表):有 run 行的会话 id(去重)。
337
+ * 起服时调(`LocalBackend.runOneTimeMigrations`,任何读者之前);读内存索引(构造时已从盘上 hydrate),零写。
338
+ */
339
+ sessionIdsWithRuns(): ReadonlySet<string>;
335
340
  /** Release every open event-log fd (called by LocalBackend.close so a graceful restart can re-open the data dir). */
336
341
  dispose(): void;
337
342
  }
@@ -280,6 +280,7 @@ export class FileRunStore {
280
280
  "Refusing to boot with an EMPTY single-active claim index (every session would look unclaimed, so a delete or a fresh run could race a live/parked one). " +
281
281
  "Fix the permissions/mount on that path, point LOCAL_DATA_ROOT at a readable dir, or unset RUN_STORE_STRICT_HYDRATE to boot anyway (the pre-existing behavior).");
282
282
  }
283
+ recordFailOpen("server.run-store.file-active-claims-unreadable", detail);
283
284
  claims = [];
284
285
  }
285
286
  const nonTerminalBySanitizedSession = new Map();
@@ -1009,6 +1010,9 @@ export class FileRunStore {
1009
1010
  }
1010
1011
  return migrated;
1011
1012
  }
1013
+ sessionIdsWithRuns() {
1014
+ return new Set([...this.runs.values()].map((r) => r.sessionId));
1015
+ }
1012
1016
  dispose() {
1013
1017
  for (const log of this.eventLogs.values()) {
1014
1018
  try {
@@ -25,6 +25,8 @@
25
25
  */
26
26
  import { type SessionStore, type AcquiredSession, type SessionStoreSummary, type SessionPlacement, type PlacedSessionRow } from "@sema-agent/core";
27
27
  import { type DelegationTree } from "../delegated-session-owner.js";
28
+ import type { ExecutionPlacementSeat } from "../execution-placement.js";
29
+ import type { ForkToFn } from "./session-fork.js";
28
30
  /** acquire 选项形(core 5.48 SessionStore 契约):[ref] placement=受信 spawner 的分区建会话声明。 */
29
31
  type AcquireOpts = {
30
32
  requireExisting?: boolean;
@@ -54,6 +56,10 @@ export declare class ForkRoutingSessionStore implements SessionStore {
54
56
  olderThanMs?: number;
55
57
  scope?: string;
56
58
  }) => Promise<PlacedSessionRow[]>;
59
+ /** 执行席透传(host 有才有):执行放置是**根会话行**上的一格,根会话行住 host durable 店;transient 店没有会话行可放,也就不声明席。 */
60
+ readonly executionPlacement?: ExecutionPlacementSeat;
61
+ /** fork 续写的店契约透传(host 有才有):目标在 host 店原子新建,新 id 与源一并记 host 路由(与 {@link fork} 的 host 腿同)。 */
62
+ readonly forkTo?: ForkToFn;
57
63
  constructor(host: SessionStore, transient: SessionStore);
58
64
  private remember;
59
65
  private pick;
@@ -11,6 +11,8 @@ export class ForkRoutingSessionStore {
11
11
  placed = new Set();
12
12
  placements;
13
13
  listPlaced;
14
+ executionPlacement;
15
+ forkTo;
14
16
  constructor(host, transient) {
15
17
  this.host = host;
16
18
  this.transient = transient;
@@ -23,6 +25,20 @@ export class ForkRoutingSessionStore {
23
25
  if (host.fork === undefined && transient.fork === undefined) {
24
26
  this.fork = undefined;
25
27
  }
28
+ const hostFaces = host;
29
+ if (hostFaces.executionPlacement !== undefined)
30
+ this.executionPlacement = hostFaces.executionPlacement;
31
+ const hostForkTo = hostFaces.forkTo;
32
+ if (typeof hostForkTo === "function") {
33
+ this.forkTo = async (sourceId, owner, target) => {
34
+ const out = await hostForkTo.call(host, sourceId, owner, target);
35
+ if (out !== null && out.outcome !== "occupied") {
36
+ this.remember(sourceId, "host");
37
+ this.remember(out.id, "host");
38
+ }
39
+ return out;
40
+ };
41
+ }
26
42
  }
27
43
  remember(id, where) {
28
44
  this.route.delete(id);
@@ -145,7 +145,7 @@ export async function ensurePgImageBakeSchema(pool, poolName = "standard") {
145
145
  }
146
146
  const isDupKey = isDupKeyError;
147
147
  function dupKeyName(dialect, err) {
148
- if (dialect === "tidb") {
148
+ if (dialect === "mysql") {
149
149
  const msg = String(err?.sqlMessage ?? err?.message ?? "");
150
150
  if (/uq_image_bake_event_line/i.test(msg))
151
151
  return "uq_line";
@@ -169,8 +169,8 @@ export class SqlImageBake {
169
169
  this.db = db;
170
170
  this.poolName = poolName;
171
171
  }
172
- q(tidb, pg) {
173
- return this.db.dialect === "tidb" ? tidb : pg;
172
+ q(mysql, pg) {
173
+ return this.db.dialect === "mysql" ? mysql : pg;
174
174
  }
175
175
  insertParams(bakeId, input, now) {
176
176
  return [
@@ -214,7 +214,7 @@ export class SqlImageBake {
214
214
  const conn = await this.db.connect();
215
215
  let inserted = false;
216
216
  try {
217
- if (this.db.dialect === "tidb") {
217
+ if (this.db.dialect === "mysql") {
218
218
  await conn.begin();
219
219
  await conn.query("INSERT INTO image_bake_admit (pool) VALUES (?) ON DUPLICATE KEY UPDATE pool = pool", [this.poolName]);
220
220
  await conn.query("SELECT pool FROM image_bake_admit WHERE pool = ? FOR UPDATE", [this.poolName]);
@@ -255,15 +255,15 @@ export class SqlImageBake {
255
255
  return { created: true, bake };
256
256
  }
257
257
  async getBake(bakeId) {
258
- const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("tidb")} FROM image_bake WHERE bake_id = ? LIMIT 1`, `SELECT ${bakeSelectCols("pg")} FROM image_bake WHERE bake_id = $1 LIMIT 1`), [bakeId]);
258
+ const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("mysql")} FROM image_bake WHERE bake_id = ? LIMIT 1`, `SELECT ${bakeSelectCols("pg")} FROM image_bake WHERE bake_id = $1 LIMIT 1`), [bakeId]);
259
259
  return rows[0] ? refusingBakeRow(() => mapRow(rows[0])) : null;
260
260
  }
261
261
  async getBakeByIdem(idemKey) {
262
- const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("tidb")} FROM image_bake WHERE idem_key = ? LIMIT 1`, `SELECT ${bakeSelectCols("pg")} FROM image_bake WHERE idem_key = $1 LIMIT 1`), [idemKey]);
262
+ const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("mysql")} FROM image_bake WHERE idem_key = ? LIMIT 1`, `SELECT ${bakeSelectCols("pg")} FROM image_bake WHERE idem_key = $1 LIMIT 1`), [idemKey]);
263
263
  return rows[0] ? refusingBakeRow(() => mapRow(rows[0])) : null;
264
264
  }
265
265
  async findActiveByArgv(argv) {
266
- const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("tidb")} FROM image_bake WHERE status IN ('queued','running') AND argv = CAST(? AS JSON) ` +
266
+ const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("mysql")} FROM image_bake WHERE status IN ('queued','running') AND argv = CAST(? AS JSON) ` +
267
267
  "ORDER BY created_at DESC LIMIT 1", `SELECT ${bakeSelectCols("pg")} FROM image_bake WHERE status IN ('queued','running') AND argv = $1::jsonb ` +
268
268
  "ORDER BY created_at DESC LIMIT 1"), [JSON.stringify(argv)]);
269
269
  return rows[0] ? refusingBakeRow(() => mapRow(rows[0])) : null;
@@ -380,7 +380,7 @@ export class SqlImageBake {
380
380
  return m == null ? 0 : Number(m);
381
381
  }
382
382
  async getEvents(bakeId, afterSeq) {
383
- const { rows } = await this.db.query(this.q(`SELECT seq, line_ord, kind, level, ${jsonColumnText("tidb", "data")}, ts FROM image_bake_event WHERE bake_id = ? AND seq > ? ORDER BY seq ASC`, `SELECT seq, line_ord, kind, level, ${jsonColumnText("pg", "data")}, ts FROM image_bake_event WHERE bake_id = $1 AND seq > $2 ORDER BY seq ASC`), [bakeId, afterSeq]);
383
+ const { rows } = await this.db.query(this.q(`SELECT seq, line_ord, kind, level, ${jsonColumnText("mysql", "data")}, ts FROM image_bake_event WHERE bake_id = ? AND seq > ? ORDER BY seq ASC`, `SELECT seq, line_ord, kind, level, ${jsonColumnText("pg", "data")}, ts FROM image_bake_event WHERE bake_id = $1 AND seq > $2 ORDER BY seq ASC`), [bakeId, afterSeq]);
384
384
  return rows.map((r) => ({
385
385
  seq: Number(r.seq),
386
386
  lineOrd: r.line_ord === null ? null : Number(r.line_ord),
@@ -401,7 +401,7 @@ export class SqlImageBake {
401
401
  await this.tx(async (conn) => {
402
402
  const res = await conn.query(this.q("UPDATE image_bake SET status=?, state=?, digest=?, repo=?, ref=?, index_id=?, exit_code=?, error=?, " +
403
403
  "error_code=?, manifest_sha=?, tag=?, ingest_secret=NULL, updated_at=? WHERE bake_id=? AND status IN ('queued','running')", "UPDATE image_bake SET status=$1, state=$2, digest=$3, repo=$4, ref=$5, index_id=$6, exit_code=$7, error=$8, " +
404
- "error_code=$9, manifest_sha=$10, tag=$11, ingest_secret=NULL, updated_at=$12 WHERE bake_id=$13 AND status IN ('queued','running')"), this.db.dialect === "tidb" ? this.terminalParamsTidb(bakeId, t) : this.terminalParamsPg(bakeId, t));
404
+ "error_code=$9, manifest_sha=$10, tag=$11, ingest_secret=NULL, updated_at=$12 WHERE bake_id=$13 AND status IN ('queued','running')"), this.db.dialect === "mysql" ? this.terminalParamsTidb(bakeId, t) : this.terminalParamsPg(bakeId, t));
405
405
  affectedRows = res.affected;
406
406
  await conn.query(this.q("DELETE FROM image_bake_lease WHERE pool = ? AND bake_id = ?", "DELETE FROM image_bake_lease WHERE pool = $1 AND bake_id = $2"), [this.poolName, bakeId]);
407
407
  });
@@ -181,8 +181,8 @@ export class SqlImageIndex {
181
181
  constructor(db) {
182
182
  this.db = db;
183
183
  }
184
- q(tidb, pg) {
185
- return this.db.dialect === "tidb" ? tidb : pg;
184
+ q(mysql, pg) {
185
+ return this.db.dialect === "mysql" ? mysql : pg;
186
186
  }
187
187
  static idFor(repo, digest) {
188
188
  return createHash("sha256").update(`${repo}@${digest}`).digest("hex").slice(0, 64);
@@ -216,7 +216,7 @@ export class SqlImageIndex {
216
216
  status = EXCLUDED.status, visibility = EXCLUDED.visibility, tenant_id = EXCLUDED.tenant_id,
217
217
  manifest_sha = EXCLUDED.manifest_sha, recipe_git_sha = EXCLUDED.recipe_git_sha,
218
218
  generator_version = EXCLUDED.generator_version, build_date = EXCLUDED.build_date,
219
- supersedes = EXCLUDED.supersedes, signed = EXCLUDED.signed, updated_at = EXCLUDED.updated_at`), this.db.dialect === "tidb" ? tidbUpsertParams(e, id, now) : pgUpsertParams(e, id, now));
219
+ supersedes = EXCLUDED.supersedes, signed = EXCLUDED.signed, updated_at = EXCLUDED.updated_at`), this.db.dialect === "mysql" ? tidbUpsertParams(e, id, now) : pgUpsertParams(e, id, now));
220
220
  return id;
221
221
  }
222
222
  async registerBuilding(e) {
@@ -258,21 +258,21 @@ export class SqlImageIndex {
258
258
  supersedes = EXCLUDED.supersedes,
259
259
  signed = CASE WHEN sandbox_image_index.status = 'published'
260
260
  THEN sandbox_image_index.signed ELSE EXCLUDED.signed END,
261
- updated_at = EXCLUDED.updated_at`), this.db.dialect === "tidb" ? tidbUpsertParams(e, id, now) : pgUpsertParams(e, id, now));
261
+ updated_at = EXCLUDED.updated_at`), this.db.dialect === "mysql" ? tidbUpsertParams(e, id, now) : pgUpsertParams(e, id, now));
262
262
  return id;
263
263
  }
264
264
  async getById(id) {
265
- const { rows } = await this.db.query(this.q(`SELECT ${imageSelectCols("tidb")} FROM sandbox_image_index WHERE id=? LIMIT 1`, `SELECT ${imageSelectCols("pg")} FROM sandbox_image_index WHERE id = $1 LIMIT 1`), [id]);
265
+ const { rows } = await this.db.query(this.q(`SELECT ${imageSelectCols("mysql")} FROM sandbox_image_index WHERE id=? LIMIT 1`, `SELECT ${imageSelectCols("pg")} FROM sandbox_image_index WHERE id = $1 LIMIT 1`), [id]);
266
266
  return rows[0] ? refusingImageRow(() => mapRow(rows[0])) : null;
267
267
  }
268
268
  async getByDigest(digest) {
269
- const { rows } = await this.db.query(this.q(`SELECT ${imageSelectCols("tidb")} FROM sandbox_image_index WHERE digest=? ORDER BY updated_at DESC LIMIT 1`, `SELECT ${imageSelectCols("pg")} FROM sandbox_image_index WHERE digest = $1 ORDER BY updated_at DESC LIMIT 1`), [digest]);
269
+ const { rows } = await this.db.query(this.q(`SELECT ${imageSelectCols("mysql")} FROM sandbox_image_index WHERE digest=? ORDER BY updated_at DESC LIMIT 1`, `SELECT ${imageSelectCols("pg")} FROM sandbox_image_index WHERE digest = $1 ORDER BY updated_at DESC LIMIT 1`), [digest]);
270
270
  return rows[0] ? refusingImageRow(() => mapRow(rows[0])) : null;
271
271
  }
272
272
  async latestPublished(profile, viewer) {
273
- if (this.db.dialect === "tidb") {
273
+ if (this.db.dialect === "mysql") {
274
274
  const vis = visibilityClauseTidb(viewer);
275
- const { rows } = await this.db.query(`SELECT ${imageSelectCols("tidb")} FROM sandbox_image_index
275
+ const { rows } = await this.db.query(`SELECT ${imageSelectCols("mysql")} FROM sandbox_image_index
276
276
  WHERE profile=? AND status='published' AND ${vis.sql}
277
277
  ORDER BY (build_date IS NULL), build_date DESC, created_at DESC LIMIT 1`, [profile, ...vis.params]);
278
278
  return rows[0] ? refusingImageRow(() => mapRow(rows[0])) : null;
@@ -286,7 +286,7 @@ export class SqlImageIndex {
286
286
  async list(filter) {
287
287
  const limit = Math.min(Math.max(1, filter.limit ?? DEFAULT_LIMIT), MAX_LIMIT);
288
288
  let rawRows;
289
- if (this.db.dialect === "tidb") {
289
+ if (this.db.dialect === "mysql") {
290
290
  const vis = visibilityClauseTidb(filter);
291
291
  const where = [vis.sql];
292
292
  const params = [...vis.params];
@@ -309,7 +309,7 @@ export class SqlImageIndex {
309
309
  params.push(new Date(c.createdAt), new Date(c.createdAt), c.id);
310
310
  }
311
311
  }
312
- const sql = `SELECT ${imageSelectCols("tidb")} FROM sandbox_image_index WHERE ${where.join(" AND ")} ` +
312
+ const sql = `SELECT ${imageSelectCols("mysql")} FROM sandbox_image_index WHERE ${where.join(" AND ")} ` +
313
313
  `ORDER BY created_at DESC, id DESC LIMIT ?`;
314
314
  const { rows } = await this.db.query(sql, [...params, limit + 1]);
315
315
  rawRows = rows;
@@ -359,7 +359,7 @@ export class SqlImageIndex {
359
359
  return { entries, nextCursor };
360
360
  }
361
361
  async setStatus(id, status, opts) {
362
- if (this.db.dialect === "tidb") {
362
+ if (this.db.dialect === "mysql") {
363
363
  const sets = ["status=?", "updated_at=?"];
364
364
  const params = [status, new Date()];
365
365
  if (opts && "supersedes" in opts) {
@@ -95,8 +95,8 @@ export class SqlLeaderRunStore {
95
95
  constructor(db) {
96
96
  this.db = db;
97
97
  }
98
- q(tidb, pg) {
99
- return this.db.dialect === "tidb" ? tidb : pg;
98
+ q(mysql, pg) {
99
+ return this.db.dialect === "mysql" ? mysql : pg;
100
100
  }
101
101
  async createRun(input) {
102
102
  const id = uuidv7();
@@ -120,12 +120,12 @@ export class SqlLeaderRunStore {
120
120
  return { created: true, run };
121
121
  }
122
122
  async getRun(id) {
123
- const res = await this.db.query(this.q(`SELECT ${selectCols("tidb")} FROM ${LEADER_RUN_TABLE} WHERE leader_run_id = ?`, `SELECT ${selectCols("pg")} FROM ${LEADER_RUN_TABLE} WHERE leader_run_id = $1`), [id]);
123
+ const res = await this.db.query(this.q(`SELECT ${selectCols("mysql")} FROM ${LEADER_RUN_TABLE} WHERE leader_run_id = ?`, `SELECT ${selectCols("pg")} FROM ${LEADER_RUN_TABLE} WHERE leader_run_id = $1`), [id]);
124
124
  const row = res.rows[0];
125
125
  return row ? mapRow(row) : undefined;
126
126
  }
127
127
  async getByIdem(idemKey) {
128
- const res = await this.db.query(this.q(`SELECT ${selectCols("tidb")} FROM ${LEADER_RUN_TABLE} WHERE idem_key = ?`, `SELECT ${selectCols("pg")} FROM ${LEADER_RUN_TABLE} WHERE idem_key = $1`), [idemKey]);
128
+ const res = await this.db.query(this.q(`SELECT ${selectCols("mysql")} FROM ${LEADER_RUN_TABLE} WHERE idem_key = ?`, `SELECT ${selectCols("pg")} FROM ${LEADER_RUN_TABLE} WHERE idem_key = $1`), [idemKey]);
129
129
  const row = res.rows[0];
130
130
  return row ? mapRow(row) : undefined;
131
131
  }