@sema-agent/server 7.148.0 → 7.150.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 (360) hide show
  1. package/MIGRATION.md +551 -9
  2. package/README.md +17 -2
  3. package/README.zh-CN.md +5 -1
  4. package/USAGE.md +126 -68
  5. package/dist/adoption/wire.d.ts +3 -3
  6. package/dist/approval-card.d.ts +33 -33
  7. package/dist/approval-gate-currency.d.ts +1 -1
  8. package/dist/approval-leg-origin.d.ts +33 -0
  9. package/dist/approval-leg-origin.js +4 -0
  10. package/dist/audit.d.ts +1 -1
  11. package/dist/audit.js +2 -2
  12. package/dist/auth-bridge.d.ts +9 -1
  13. package/dist/auth-bridge.js +4 -4
  14. package/dist/auth-keys.d.ts +10 -1
  15. package/dist/auth-keys.js +8 -0
  16. package/dist/background-session-reap.d.ts +1 -1
  17. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
  18. package/dist/boot/deferred-sandbox-path-env.js +1 -2
  19. package/dist/boot/execution-env.js +10 -11
  20. package/dist/boot/execution-lane.d.ts +6 -4
  21. package/dist/boot/execution-lane.js +3 -1
  22. package/dist/boot/org-delivery.d.ts +3 -2
  23. package/dist/boot/parked-revive-gate.d.ts +5 -5
  24. package/dist/boot/parked-revive-gate.js +2 -3
  25. package/dist/boot/resolve-spec.d.ts +4 -5
  26. package/dist/boot/resolve-spec.js +66 -28
  27. package/dist/boot/retention-lane.d.ts +1 -1
  28. package/dist/boot/runner-deps.js +3 -1
  29. package/dist/boot/session-faces.js +21 -21
  30. package/dist/boot/stage-01-config.d.ts +1 -0
  31. package/dist/boot/stage-01-config.js +4 -3
  32. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
  33. package/dist/boot/stage-02-web-admin-pull.js +10 -2
  34. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  35. package/dist/boot/stage-03-store-backend.js +4 -4
  36. package/dist/boot/stage-04-stores.d.ts +1 -0
  37. package/dist/boot/stage-05-execution-env.d.ts +1 -0
  38. package/dist/boot/stage-06-runners.d.ts +2 -1
  39. package/dist/boot/stage-06-runners.js +1 -2
  40. package/dist/boot/stage-07-capability-layer.d.ts +2 -1
  41. package/dist/boot/stage-07-capability-layer.js +1 -1
  42. package/dist/boot/stage-08-reapers.d.ts +2 -1
  43. package/dist/boot/stage-09-leader.d.ts +2 -1
  44. package/dist/boot/stage-10-http-server.d.ts +35 -2
  45. package/dist/boot/stage-10-http-server.js +9 -4
  46. package/dist/boot/stores.d.ts +3 -0
  47. package/dist/boot/stores.js +22 -19
  48. package/dist/boot/web-admin.d.ts +1 -1
  49. package/dist/capabilities/tool-defer.d.ts +1 -1
  50. package/dist/capability-facts.d.ts +70 -60
  51. package/dist/capability-facts.js +45 -49
  52. package/dist/card-settle-stored-scope.d.ts +56 -0
  53. package/dist/card-settle-stored-scope.js +27 -0
  54. package/dist/card-void.d.ts +7 -12
  55. package/dist/card-void.js +2 -7
  56. package/dist/config-catalog.d.ts +12 -0
  57. package/dist/config-catalog.js +37 -33
  58. package/dist/config-invariants.d.ts +5 -4
  59. package/dist/config-invariants.js +4 -4
  60. package/dist/config-types.d.ts +49 -16
  61. package/dist/config.d.ts +6 -5
  62. package/dist/config.js +35 -24
  63. package/dist/context-window-notice.d.ts +31 -0
  64. package/dist/context-window-notice.js +28 -0
  65. package/dist/deployment-governance.d.ts +7 -28
  66. package/dist/deployment-governance.js +1 -14
  67. package/dist/deployment-shape.d.ts +20 -11
  68. package/dist/deployment-shape.js +1 -0
  69. package/dist/device-ws-hub.d.ts +1 -0
  70. package/dist/device-ws-hub.js +64 -28
  71. package/dist/device-ws-protocol.d.ts +20 -6
  72. package/dist/device-ws-protocol.js +9 -2
  73. package/dist/engine-identity.d.ts +7 -1
  74. package/dist/engine-identity.js +5 -0
  75. package/dist/env-name-allowlist-knobs.d.ts +6 -5
  76. package/dist/env-name-allowlist-knobs.js +5 -0
  77. package/dist/execution-lane-caps.d.ts +20 -5
  78. package/dist/execution-lane-caps.js +18 -4
  79. package/dist/field-conflict.d.ts +2 -2
  80. package/dist/fleet/engine-signal.d.ts +14 -1
  81. package/dist/fleet/engine-signal.js +5 -1
  82. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  83. package/dist/fleet/serving-lease.d.ts +18 -3
  84. package/dist/fleet/serving-lease.js +81 -35
  85. package/dist/fleet-client.d.ts +13 -11
  86. package/dist/fleet-client.js +53 -33
  87. package/dist/history-anchor.js +1 -1
  88. package/dist/hitl-delivery.d.ts +1 -3
  89. package/dist/hitl-delivery.js +1 -1
  90. package/dist/hooks/command-spawn.d.ts +7 -2
  91. package/dist/hooks/command-spawn.js +13 -6
  92. package/dist/hooks/hook-runner.d.ts +5 -0
  93. package/dist/hooks/hook-runner.js +11 -7
  94. package/dist/http/active-run-conflict.d.ts +3 -7
  95. package/dist/http/active-run-conflict.js +2 -2
  96. package/dist/http/admission.js +5 -5
  97. package/dist/http/approval-sweeps.js +5 -3
  98. package/dist/http/caller-trust.d.ts +2 -2
  99. package/dist/http/caller-trust.js +3 -3
  100. package/dist/http/checkpoint-ctx.d.ts +18 -63
  101. package/dist/http/checkpoint-ctx.js +1 -24
  102. package/dist/http/claim-heartbeat.d.ts +6 -6
  103. package/dist/http/claim-heartbeat.js +6 -6
  104. package/dist/http/claimed-run.d.ts +16 -9
  105. package/dist/http/claimed-run.js +8 -0
  106. package/dist/http/dispatch.js +7 -5
  107. package/dist/http/durable-run-leg.d.ts +8 -1
  108. package/dist/http/durable-run-leg.js +34 -15
  109. package/dist/http/execution-context.d.ts +115 -14
  110. package/dist/http/execution-context.js +130 -40
  111. package/dist/http/owner-access.d.ts +246 -21
  112. package/dist/http/owner-access.js +186 -8
  113. package/dist/http/principal-gate.d.ts +18 -11
  114. package/dist/http/principal-gate.js +3 -1
  115. package/dist/http/readiness.d.ts +4 -2
  116. package/dist/http/resume-ctx-read.d.ts +14 -3
  117. package/dist/http/resume-ctx-read.js +24 -3
  118. package/dist/http/resume-legs.d.ts +35 -3
  119. package/dist/http/resume-legs.js +262 -106
  120. package/dist/http/route-ctx.d.ts +29 -11
  121. package/dist/http/routes/a2a-serve.js +17 -14
  122. package/dist/http/routes/approvals-assistant.d.ts +3 -2
  123. package/dist/http/routes/approvals-assistant.js +11 -11
  124. package/dist/http/routes/capabilities.js +5 -4
  125. package/dist/http/routes/config-catalog.d.ts +1 -1
  126. package/dist/http/routes/diagnostics.d.ts +3 -1
  127. package/dist/http/routes/diagnostics.js +30 -5
  128. package/dist/http/routes/mcp-reconnect.js +2 -2
  129. package/dist/http/routes/memory-origin.d.ts +1 -1
  130. package/dist/http/routes/notify-wake.js +2 -3
  131. package/dist/http/routes/principal-inbox.d.ts +3 -1
  132. package/dist/http/routes/principal-inbox.js +3 -2
  133. package/dist/http/routes/projects.d.ts +1 -0
  134. package/dist/http/routes/projects.js +5 -3
  135. package/dist/http/routes/retention-ops.d.ts +1 -1
  136. package/dist/http/routes/run-permission-mode.d.ts +1 -1
  137. package/dist/http/routes/run-permission-mode.js +2 -2
  138. package/dist/http/routes/runs.d.ts +1 -1
  139. package/dist/http/routes/runs.js +33 -37
  140. package/dist/http/routes/session-memory-sync.js +4 -5
  141. package/dist/http/routes/session-sync.d.ts +24 -5
  142. package/dist/http/routes/session-sync.js +45 -37
  143. package/dist/http/routes/sessions-list.d.ts +1 -1
  144. package/dist/http/routes/sessions.d.ts +1 -1
  145. package/dist/http/routes/sessions.js +18 -44
  146. package/dist/http/routes/tasks.js +136 -101
  147. package/dist/http/routes/trace-usage.js +14 -14
  148. package/dist/http/routes/workflows.d.ts +1 -0
  149. package/dist/http/routes/workflows.js +20 -38
  150. package/dist/http/server.d.ts +12 -9
  151. package/dist/http/server.js +35 -6
  152. package/dist/http/stored-body-replay.d.ts +3 -24
  153. package/dist/http/stored-body-replay.js +2 -12
  154. package/dist/http/submit-ctx.d.ts +2 -2
  155. package/dist/http/submit-ctx.js +5 -5
  156. package/dist/http/submit-replay.d.ts +15 -2
  157. package/dist/http/submit-replay.js +10 -4
  158. package/dist/http/wire-types.d.ts +6 -3
  159. package/dist/index.d.ts +3 -1
  160. package/dist/index.js +1 -0
  161. package/dist/leader/endpoint.js +9 -8
  162. package/dist/leg-facts.d.ts +13 -2
  163. package/dist/leg-facts.js +2 -0
  164. package/dist/local-store-declaration.d.ts +54 -0
  165. package/dist/local-store-declaration.js +59 -0
  166. package/dist/mcp-assertion-header.d.ts +1 -1
  167. package/dist/mcp-assertion-header.js +1 -1
  168. package/dist/mcp-authorization-face.d.ts +18 -14
  169. package/dist/mcp-authorization-face.js +3 -2
  170. package/dist/memory-project-layer.js +1 -1
  171. package/dist/memory-scope.d.ts +4 -4
  172. package/dist/memory-scope.js +4 -4
  173. package/dist/memory-store-refusal.d.ts +1 -1
  174. package/dist/memory-sync-client.js +2 -2
  175. package/dist/model-context-window.d.ts +50 -11
  176. package/dist/model-context-window.js +20 -1
  177. package/dist/model-entry-refusal.d.ts +5 -4
  178. package/dist/model-entry-refusal.js +17 -3
  179. package/dist/model-reach-scope.d.ts +2 -1
  180. package/dist/observability/corrupt-read-seat.d.ts +1 -1
  181. package/dist/observability/fail-open.d.ts +19 -7
  182. package/dist/observability/fail-open.js +19 -7
  183. package/dist/observability/logger.d.ts +7 -0
  184. package/dist/observability/logger.js +3 -1
  185. package/dist/observability/metrics.js +1 -1
  186. package/dist/observability/run-terminal.d.ts +1 -1
  187. package/dist/observability/run-terminal.js +1 -0
  188. package/dist/observability/secret-env-scrub.d.ts +1 -1
  189. package/dist/observability/secret-env-scrub.js +1 -1
  190. package/dist/org-delivery-gate.d.ts +31 -6
  191. package/dist/org-delivery-gate.js +7 -4
  192. package/dist/owner-faces.d.ts +30 -5
  193. package/dist/owner-faces.js +2 -0
  194. package/dist/park-row-commit.d.ts +67 -0
  195. package/dist/park-row-commit.js +76 -0
  196. package/dist/parked-decide.d.ts +9 -7
  197. package/dist/parked-decide.js +25 -6
  198. package/dist/peer-directory.d.ts +1 -1
  199. package/dist/peer-seat.d.ts +1 -1
  200. package/dist/peer-seat.js +52 -59
  201. package/dist/plugins/adoption-log-sql.d.ts +3 -3
  202. package/dist/plugins/adoption-log-sql.js +19 -19
  203. package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
  204. package/dist/plugins/approval-ask-store-sql.js +3 -5
  205. package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
  206. package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
  207. package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
  208. package/dist/plugins/approval-exemption-skill-m46.js +6 -10
  209. package/dist/plugins/approval-exemption-store.d.ts +26 -1
  210. package/dist/plugins/approval-exemption-store.js +28 -19
  211. package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
  212. package/dist/plugins/ensure-schema-lock-policy.js +75 -9
  213. package/dist/plugins/exec-path-law.d.ts +34 -0
  214. package/dist/plugins/exec-path-law.js +13 -0
  215. package/dist/plugins/file-run-store.d.ts +22 -9
  216. package/dist/plugins/file-run-store.js +51 -23
  217. package/dist/plugins/local-checkpoint-store.d.ts +8 -1
  218. package/dist/plugins/local-checkpoint-store.js +5 -1
  219. package/dist/plugins/local-data-root-admission.d.ts +52 -0
  220. package/dist/plugins/local-data-root-admission.js +100 -0
  221. package/dist/plugins/local-engine-high-water.d.ts +28 -7
  222. package/dist/plugins/local-engine-high-water.js +27 -22
  223. package/dist/plugins/local-session-store.d.ts +134 -49
  224. package/dist/plugins/local-session-store.js +149 -108
  225. package/dist/plugins/memory-run-store.d.ts +14 -9
  226. package/dist/plugins/memory-run-store.js +48 -22
  227. package/dist/plugins/one-time-migration-marker.d.ts +19 -3
  228. package/dist/plugins/one-time-migration-marker.js +7 -1
  229. package/dist/plugins/one-time-migrations.d.ts +110 -59
  230. package/dist/plugins/one-time-migrations.js +224 -136
  231. package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
  232. package/dist/plugins/org-delivery-quota-store.js +29 -14
  233. package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
  234. package/dist/plugins/permission-rule-store-sql.js +5 -9
  235. package/dist/plugins/pg-pool.d.ts +1 -1
  236. package/dist/plugins/pg-pool.js +14 -12
  237. package/dist/plugins/posix-shell-fs.js +4 -2
  238. package/dist/plugins/remote-env-adb.js +3 -2
  239. package/dist/plugins/remote-env-device.d.ts +3 -2
  240. package/dist/plugins/remote-env-device.js +8 -6
  241. package/dist/plugins/remote-env-e2b.d.ts +4 -8
  242. package/dist/plugins/remote-env-e2b.js +21 -15
  243. package/dist/plugins/remote-env-host.d.ts +51 -10
  244. package/dist/plugins/remote-env-host.js +97 -31
  245. package/dist/plugins/remote-env-k8s.js +7 -4
  246. package/dist/plugins/remote-env-local-docker.d.ts +6 -3
  247. package/dist/plugins/remote-env-local-docker.js +11 -25
  248. package/dist/plugins/remote-env-ssh.js +4 -3
  249. package/dist/plugins/remote-shell.d.ts +3 -1
  250. package/dist/plugins/remote-shell.js +3 -3
  251. package/dist/plugins/replica-registration.d.ts +35 -0
  252. package/dist/plugins/replica-registration.js +48 -0
  253. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  254. package/dist/plugins/run-store-sql.d.ts +27 -8
  255. package/dist/plugins/run-store-sql.js +46 -18
  256. package/dist/plugins/scenario-prefix-switch-m42.d.ts +8 -12
  257. package/dist/plugins/scenario-prefix-switch-m42.js +9 -6
  258. package/dist/plugins/schema-consistency.d.ts +91 -5
  259. package/dist/plugins/schema-consistency.js +189 -7
  260. package/dist/plugins/schema-drift-sentence.d.ts +6 -5
  261. package/dist/plugins/schema-drift-sentence.js +13 -5
  262. package/dist/plugins/serving-requirement.d.ts +49 -0
  263. package/dist/plugins/serving-requirement.js +53 -0
  264. package/dist/plugins/session-placement.d.ts +5 -4
  265. package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
  266. package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
  267. package/dist/plugins/stop-then-start-sentence.d.ts +55 -0
  268. package/dist/plugins/stop-then-start-sentence.js +51 -0
  269. package/dist/plugins/store-backend.d.ts +23 -9
  270. package/dist/plugins/store-backend.js +58 -24
  271. package/dist/plugins/store-contracts.d.ts +45 -5
  272. package/dist/plugins/store-contracts.js +19 -0
  273. package/dist/plugins/tidb-pool.d.ts +11 -6
  274. package/dist/plugins/tidb-pool.js +22 -12
  275. package/dist/plugins/workspace-custody.d.ts +44 -0
  276. package/dist/plugins/workspace-custody.js +47 -0
  277. package/dist/process-instance.d.ts +3 -0
  278. package/dist/process-instance.js +7 -0
  279. package/dist/project-context.d.ts +66 -0
  280. package/dist/project-context.js +70 -0
  281. package/dist/project-identity.d.ts +8 -7
  282. package/dist/project-identity.js +40 -17
  283. package/dist/project-layer-key.d.ts +13 -2
  284. package/dist/project-layer-key.js +9 -1
  285. package/dist/question.d.ts +9 -21
  286. package/dist/question.js +28 -19
  287. package/dist/read-deny-floor-notice.d.ts +36 -0
  288. package/dist/read-deny-floor-notice.js +40 -0
  289. package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
  290. package/dist/request-declaration-disposition.js +39 -0
  291. package/dist/run-cancel-card.d.ts +28 -0
  292. package/dist/run-cancel-card.js +20 -0
  293. package/dist/run-cancel-context.d.ts +10 -0
  294. package/dist/run-cancel-context.js +1 -0
  295. package/dist/run-local.js +28 -13
  296. package/dist/runs.d.ts +31 -7
  297. package/dist/runs.js +34 -16
  298. package/dist/security.d.ts +3 -3
  299. package/dist/server-secret-env.d.ts +34 -20
  300. package/dist/server-secret-env.js +61 -18
  301. package/dist/session-agent.d.ts +4 -12
  302. package/dist/session-agent.js +0 -6
  303. package/dist/session-children-retire.d.ts +6 -3
  304. package/dist/session-children-retire.js +2 -5
  305. package/dist/session-incarnation.d.ts +18 -4
  306. package/dist/session-incarnation.js +4 -1
  307. package/dist/session-notify-door.d.ts +4 -4
  308. package/dist/session-notify-door.js +5 -6
  309. package/dist/session-rules-seat.d.ts +4 -3
  310. package/dist/session-rules-seat.js +3 -11
  311. package/dist/session-sync.js +3 -3
  312. package/dist/session-workspace.d.ts +3 -3
  313. package/dist/store-admission.d.ts +33 -0
  314. package/dist/store-admission.js +49 -0
  315. package/dist/store-posture.d.ts +5 -0
  316. package/dist/store-posture.js +18 -1
  317. package/dist/stores-sql/checkpoint-store-sql.d.ts +22 -3
  318. package/dist/stores-sql/checkpoint-store-sql.js +109 -23
  319. package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
  320. package/dist/stores-sql/file-history-store-sql.js +3 -5
  321. package/dist/stores-sql/retention-store-sql.d.ts +1 -1
  322. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  323. package/dist/stores-sql/tool-result-store-sql.js +4 -6
  324. package/dist/task-a2a.d.ts +3 -8
  325. package/dist/task-a2a.js +25 -27
  326. package/dist/task-cwd.d.ts +27 -10
  327. package/dist/task-cwd.js +3 -0
  328. package/dist/task-mcp.d.ts +3 -3
  329. package/dist/task-mcp.js +1 -1
  330. package/dist/task-settings.d.ts +13 -0
  331. package/dist/task-settings.js +14 -7
  332. package/dist/terminal.d.ts +2 -0
  333. package/dist/terminal.js +21 -6
  334. package/dist/tool-approval.d.ts +50 -41
  335. package/dist/tool-approval.js +96 -73
  336. package/dist/trace/engine-notice-wire.d.ts +38 -21
  337. package/dist/trace/engine-notice-wire.js +25 -4
  338. package/dist/trace/ledger-sink.d.ts +3 -1
  339. package/dist/trace/project.d.ts +8 -5
  340. package/dist/trace/project.js +11 -9
  341. package/dist/trace/projection-table.d.ts +33 -2
  342. package/dist/trace/redact.d.ts +12 -0
  343. package/dist/trace/redact.js +5 -1
  344. package/dist/trace/self-minted-wire.d.ts +268 -56
  345. package/dist/trace/self-minted-wire.js +110 -40
  346. package/dist/trace/tool-card.d.ts +3 -0
  347. package/dist/trace/tool-card.js +24 -0
  348. package/dist/trace/tool-output-media.d.ts +2 -1
  349. package/dist/unattended-permission-mode.d.ts +1 -1
  350. package/dist/web-admin-client/apply-effective.d.ts +1 -1
  351. package/dist/web-admin-client/apply-effective.js +22 -9
  352. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  353. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  354. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
  355. package/docs/error-text-lexicon.json +317 -0
  356. package/package.json +4 -2
  357. package/scripts/embed-minimal.mjs +183 -0
  358. package/wire-changes/server@7.149.0.json +372 -0
  359. package/wire-changes/server@7.150.0.json +335 -0
  360. package/dist/plain-string-record.js +0 -70
@@ -9,6 +9,7 @@ import { type RunStallWiring } from "./store-contracts.js";
9
9
  import { type StoreOpenLane } from "./store-open-refusal.js";
10
10
  import { type OneTimeMigrationReport, type SqlOneTimeMigrationInput } from "./one-time-migrations.js";
11
11
  import { LocalSessionStore } from "./local-session-store.js";
12
+ import { type LocalStoreDeclaration } from "../local-store-declaration.js";
12
13
  import type { LocalSessionRows } from "./local-session-window.js";
13
14
  import type { SessionHeadReader } from "./caching-session-store.js";
14
15
  import { LocalCheckpointStore } from "./local-checkpoint-store.js";
@@ -41,9 +42,10 @@ import { type SqlTransactionHooks } from "../stores-sql/retention-store-sql.js";
41
42
  import type { SessionEraser } from "../session-erase.js";
42
43
  import { type RetentionDestructiveInput, type RetentionExecutor, type RetentionExecutorOptions } from "./retention-deployment.js";
43
44
  import { type RetentionLaneStore } from "./retention-lane-store-sql.js";
45
+ import { type LocalDataRootAdmission } from "./local-data-root-admission.js";
44
46
  import type { PermissionRuleStoreBundle } from "../rules-consent.js";
45
47
  import { type SqlDriver } from "./sql-driver.js";
46
- import { type StoreDegradedReason } from "../store-posture.js";
48
+ import { type StoreDegradedReason, type StoreDurability as MediumDurability } from "../store-posture.js";
47
49
  /** boot 期的一条日志座(级别由**座位**决定,不由参数携带):`createStoreBackend` 的 `onWarn` / `onError`
48
50
  * 两参与 `LocalBackend` 的同名字段共用此形。装配层把它们分别接到 `logger.warn` / `logger.error` 上。 */
49
51
  export type BootLogSink = (msg: string, meta?: Record<string, unknown>) => void;
@@ -54,6 +56,10 @@ export type BootLogSink = (msg: string, meta?: Record<string, unknown>) => void;
54
56
  export interface StoreBackendObservers {
55
57
  readonly runStallVerdicts?: (landed: number) => void;
56
58
  }
59
+ /** 开店时递进来的起服事实(只 local 腿读):数据根介质的判定(起服第 2 段的取证;缺席 = 这条入口没有起服证据 ⇒ 按 `unknown` 读 —— 判不出的介质不放宽 local 店的声明)。 */
60
+ export interface StoreOpenFacts {
61
+ readonly dataRootMedium?: MediumDurability;
62
+ }
57
63
  /** run 店落死亡判决的两样:窗 = 后端持有的配置对象的 `runStaleSec`(热发布原地改写 ⇒ getter 读到热值;判活窗只有这一个来源)+ 计数座。 */
58
64
  export declare function createRunStallWiring(config: Pick<ServiceConfig, "runStaleSec">, observers: StoreBackendObservers | undefined): RunStallWiring;
59
65
  export type RunStore = TiDBRunStore | PgRunStore | FileRunStore;
@@ -426,6 +432,8 @@ export interface StoreBackend {
426
432
  * 拒绝句不带盘上位置)的同时,路径进本 backend 的 `file_store_corrupt_read` 披露座(逐命中 warn)。修前 `boot/stores.ts` 在同一数据根上自己
427
433
  * `new FileUsageWindowStore(root)` 第二只实例、不接座 ⇒ 坏值只拒不披露,运维找不到是哪只文件。SQL 后端省略(它们有自己的 usage-window 双方言店)。 */
428
434
  fileUsageWindowStore?(): UsageWindowStore;
435
+ /** **local 后端专有**:构造时交给两半店(会话店顶层、检查点店)的那一份声明(唯一读者 `localStoreDeclarationOf`)—— 装配值从这里读回,不重算。SQL 后端省略。 */
436
+ localStoreDeclaration?(): LocalStoreDeclaration;
429
437
  }
430
438
  /**
431
439
  * 托管留存执行器的**组合**:引擎座表那一半(`SqlRetentionStore`,事务属主)+ 部署那一半(hold / 审计 / 认领判 / 十三张部署表,
@@ -459,7 +467,7 @@ export declare function createSqlSessionEraser(db: SqlDriver, opts: RetentionExe
459
467
  * ({@link SQL_BLOB_POSTURE_FACTS})。云形缺 MinIO ⇒ fail-loud(启动了才是假安全);`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true` =
460
468
  * 显式接受 SQL 店 + per-blob 帽(单机/测试台形;pg 上帽只来自 `SNAPSHOT_BLOB_SQL_MAX_BYTES`)。local 后端不经此门(file 店,无 SQL 包墙)。 */
461
469
  export declare function assertCloudSnapshotBlobPosture(kind: "mysql" | "pg" | "local", config: Pick<ServiceConfig, "snapshotBlobStore" | "snapshotBlobAllowSql">): void;
462
- /** SQL 一次性迁移段的输入(两只 SQL 后端同一份;各条目那一半的来源见 `SqlOneTimeMigrationInput`):M32 的车道表 · M40 的判活窗 = 收割器的同一个窗 · M48 的部署形读数(表里那一行)。 */
470
+ /** SQL 一次性迁移段的输入(两只 SQL 后端同一份;各条目那一半的来源见 `SqlOneTimeMigrationInput`):M32 的车道表 · M52 的判活窗(停-迁-起证据的心跳一半)= 收割器的同一个窗 · M48 的部署形读数(表里那一行)。 */
463
471
  export declare function buildSqlOneTimeMigrationInput(config: ServiceConfig): SqlOneTimeMigrationInput;
464
472
  /**
465
473
  * LOCAL backend (clay 2026-06-25 seamless local↔cloud; P0.5 variant-2 + variant-1 file-backed 2026-06-26): a DB-less StoreBackend
@@ -517,25 +525,31 @@ export interface LocalStoreBackend extends StoreBackend {
517
525
  toolResult(): ToolResultStoreFull;
518
526
  /** local 后端恒有(引擎 File 后端的用量窗账本;接口上可选的那一口在这里必在)。 */
519
527
  fileUsageWindowStore(): UsageWindowStore;
528
+ /** local 后端恒有(构造时交给两半店的声明;接口上可选的那一口在这里必在)。 */
529
+ localStoreDeclaration(): LocalStoreDeclaration;
520
530
  }
521
531
  /** 引擎 File 后端上会话店之外、本仓没有自己一份口的店(run-local 的 Runner 接它们;工具结果 / 用量窗 / 会话规则走既有的 `toolResult()` / `fileUsageWindowStore()` / `sessionPolicy()` 口)。 */
522
532
  export type LocalEngineFileStores = Pick<FileStorageBackend, "checkpointStore" | "backgroundAgentStore" | "mailboxStore" | "rosterStore">;
523
533
  /** 打开 local 数据根的入口参数:哪条路在开(只决定拒句的措辞 —— 表头与「换根」半句)与三只 boot 期日志座(见 {@link createStoreBackend})。 */
524
534
  export interface LocalBackendOpenOptions {
525
535
  readonly lane: StoreOpenLane;
536
+ /** 这只根的数据根准入读数(必填;{@link admitLocalDataRoot} 铸,调用方在它对这只根的第一笔写之前取 —— 打开就是第一笔写的调用方当场取,见 {@link createStoreBackend})。 */
537
+ readonly admission: LocalDataRootAdmission;
526
538
  readonly onWarn?: BootLogSink;
527
539
  readonly onError?: BootLogSink;
528
540
  readonly observers?: StoreBackendObservers;
541
+ /** 起服事实(数据根介质的判定;HTTP 服务递第 2 段的取证,缺席 ⇒ `unknown`)。 */
542
+ readonly facts?: StoreOpenFacts;
529
543
  }
530
544
  /**
531
545
  * **local 后端打开数据根**(唯一的路 —— 本仓每一条打开 local 数据根的入口都经它:`createStoreBackend` 的 local 腿(HTTP 服务)与 run-local;一个数据根一只会话行店、一份装配):
532
- * 取锁(引擎 File 后端)→ **判与抬引擎版本高水位**({@link holdEngineHighWater},同步一段;判前钩 = 判或记收令牌欠账 {@link holdArchiveTokenAdoptionDuty},抬之前)→ 本仓的店。
546
+ * 数据根准入(调用方交来的读数 `opts.admission`,判已在本进程第一笔写之前做完)→ 取锁(引擎 File 后端:建根、锁文件、引擎自己的店目录与空账本)→
547
+ * **锁内一步**({@link holdLocalDataRootAdmission}:核物理数据根与准入那一次观察都没动 → 跳版臂记 / 销账 → 抬引擎版本高水位)→ 本仓的店。
533
548
  * 打开之后的公开序列两条入口相同:`ensureSchema()`(起服对账)→ `runOneTimeMigrations()` → 才读会话。
534
- * 判与抬住在这里而不是 `ensureSchema()` 里,因为围栏以**第一笔落盘**界定:本仓的店在构造里就会写(撕裂的问账日志留证 + 压实、撕裂的 run 认领挪开、会话收尾的活跃边档落盘),
535
- * 判必须先于它们(被拒 = 本仓的店一只都没开、不抬、不改既有文件的字节;判拒发生在钥匙对与空账本已落、店尚未开之后;下一版改为判拒先于任何写),抬必须先于它们(写到一半崩了,水位已是本构建)。所以这一段之后才有店,没有任何一只店先于它。
536
- * 拒(判拒 / 本构建版本读不出 / 抬写不进 / 本仓的店构造时抛)⇒ 交回一只 {@link RefusedLocalBackend}:只握着锁,`ensureSchema()` 交出那一只拒(公开序列在它该在的那一步拒,降级臂按拒的族判),
537
- * `close()` 只放锁 —— 没有店、没有收尾写。
538
- * 残余(如实):引擎 `FileStorageBackend` 的构造把取锁与它自己的店放在同一步(建子目录、店目录锁、撕裂账本尾的收拢)—— 那几笔在判之前,本仓在两者之间没有座(要 core 给)。
549
+ * 抬住在这里而不是 `ensureSchema()` 里,因为围栏以**第一笔落盘**界定:本仓的店在构造里就会写(撕裂的问账日志留证 + 压实、撕裂的 run 认领挪开、会话收尾的活跃边档落盘),
550
+ * 抬必须先于它们(写到一半崩了,水位已是本构建)。所以这一段之后才有店,没有任何一只店先于它。
551
+ * 锁内一步拒(准入判过的输入在准入之后被另一只引擎动过 / 记账或抬写不进)或本仓的店构造时抛 ⇒ 交回一只 {@link RefusedLocalBackend}:只握着锁,`ensureSchema()` 交出那一只拒
552
+ * (公开序列在它该在的那一步拒,降级臂按拒的族判),`close()` 只放锁 —— 没有店、没有收尾写。
539
553
  */
540
554
  export declare function openLocalBackend(config: ServiceConfig, opts: LocalBackendOpenOptions): LocalStoreBackend;
541
555
  /** Build the durable-store backend for `config.dbBackend`. Does NOT connect (the pool connects lazily); the next
@@ -573,7 +587,7 @@ export declare function localBackendRootOf(config: Pick<ServiceConfig, "localDat
573
587
  export declare function openStoreBackendWithFallback(config: ServiceConfig, logger: {
574
588
  warn: (msg: string, meta?: Record<string, unknown>) => void;
575
589
  error?: (msg: string, meta?: Record<string, unknown>) => void;
576
- }, observers?: StoreBackendObservers): Promise<{
590
+ }, observers?: StoreBackendObservers, localAdmission?: LocalDataRootAdmission, facts?: StoreOpenFacts): Promise<{
577
591
  backend: StoreBackend | undefined;
578
592
  degradedReason: StoreDegradedReason | undefined;
579
593
  }>;
@@ -15,15 +15,18 @@ import { StoreBootRefusalError } from "./store-contracts.js";
15
15
  import { databaseAnswerCodesOf, isDeploymentFaultError } from "./sql-errors.js";
16
16
  import { buildStoreOpenRefusalTail } from "./store-open-refusal.js";
17
17
  import { acquireLocalBackendWithWait } from "./store-lock-wait.js";
18
- import { NO_SQL_MIGRATIONS, assertExecutionPlacementSwitchAdmissibleOnPool, backfillLocalAskExecutionLanes, backfillLocalExecutionPlacements, runSqlOneTimeMigrations } from "./one-time-migrations.js";
18
+ import { NO_SQL_MIGRATIONS, migrationCarried, assertExecutionPlacementSwitchAdmissibleOnPool, backfillLocalAskExecutionLanes, backfillLocalExecutionPlacements, runSqlOneTimeMigrations } from "./one-time-migrations.js";
19
19
  import { deployedExecutionLanes } from "../execution-lane-caps.js";
20
20
  import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
21
21
  import { LOCAL_MARKER_FILE, switchLocalScenarioPrefixM42 } from "./scenario-prefix-switch-m42.js";
22
- import { LOCAL_LEGACY_SKILL_EXEMPTION_MARKER_FILE, dropLocalLegacySkillExemptionsM46 } from "./approval-exemption-skill-m46.js";
22
+ import { dropLocalLegacySkillExemptionsM46 } from "./approval-exemption-skill-m46.js";
23
+ import { LOCAL_SKILL_EXEMPTION_CUTOFF_FILE, assertSkillExemptionCutoffHeldOnPool } from "./approval-exemption-skill-cutoff.js";
24
+ import { SKILL_EXEMPTION_CUTOFF_ID } from "./one-time-migration-marker.js";
23
25
  import { LocalTaskAttachmentStore } from "./local-task-attachment-store.js";
24
26
  import { assertRetiredSchemaAbsentOnPool } from "./schema-consistency.js";
25
- import { LocalSessionStore, archiveTokenAdoptionCarried, holdArchiveTokenAdoptionDuty, createLocalSessionRepo } from "./local-session-store.js";
26
- import { SESSION_DURABILITY, SESSION_PLACEMENTS } from "./session-placement.js";
27
+ import { LocalSessionStore, createLocalSessionRepo } from "./local-session-store.js";
28
+ import { localStoreDeclarationOf } from "../local-store-declaration.js";
29
+ import { SESSION_PLACEMENTS } from "./session-placement.js";
27
30
  import { LocalCheckpointStore, dropLocalCheckpointCtxStrict } from "./local-checkpoint-store.js";
28
31
  import { FileResumeAnchorStore } from "./file-resume-anchor-store.js";
29
32
  import { TiDBApprovalExemptionStore, PgApprovalExemptionStore, FileApprovalExemptionStore } from "./approval-exemption-store.js";
@@ -76,8 +79,7 @@ import { createSqlPermissionRuleStores } from "./permission-rule-store-sql.js";
76
79
  import { FILE_PERMISSION_RULE_DIR, createFilePermissionRuleStores, fileRuleBucketOwners, isFileBucketDamaged } from "./permission-rule-store-file.js";
77
80
  import { retireImportedLayerRootAllows } from "./imported-layer-root-retirement.js";
78
81
  import { buildDataDirLayout, tightenLocalDataDirModes } from "./local-data-dir-modes-m39.js";
79
- import { holdEngineHighWater } from "./local-engine-high-water.js";
80
- import { serviceVersion } from "../service-version.js";
82
+ import { admitLocalDataRoot, holdLocalDataRootAdmission } from "./local-data-root-admission.js";
81
83
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
82
84
  import { localDataRootHoldsData, storeDegradedReasonFor } from "../store-posture.js";
83
85
  import { openWithinWait } from "./store-open-wait.js";
@@ -377,10 +379,12 @@ class LocalBackend {
377
379
  parkDecisionLedgerRoot;
378
380
  executionLanes;
379
381
  identityBoundary;
382
+ storeDeclaration;
380
383
  dataDirLayout;
381
- constructor(fileBackend, config, discloseWarn, onWarn, onError, observers) {
384
+ constructor(fileBackend, config, discloseWarn, onWarn, onError, observers, facts) {
382
385
  this.executionLanes = deployedExecutionLanes(config);
383
386
  this.identityBoundary = DEPLOYMENT_SHAPE_PREDICATES.identityBoundary.holds(config);
387
+ this.storeDeclaration = localStoreDeclarationOf(config, facts?.dataRootMedium ?? "unknown");
384
388
  this.onWarn = onWarn;
385
389
  this.discloseWarn = discloseWarn;
386
390
  this.onError = onError;
@@ -397,7 +401,7 @@ class LocalBackend {
397
401
  const physicalRoot = this.fileBackend.root;
398
402
  this.sessionStore = new LocalSessionStore(createLocalSessionRepo(physicalRoot), this.runStore, physicalRoot, {
399
403
  log: createLogger(config.logLevel),
400
- durability: SESSION_DURABILITY,
404
+ durability: this.storeDeclaration.durability,
401
405
  placements: SESSION_PLACEMENTS,
402
406
  withdrawCheckpointCtx: (sessionId) => dropLocalCheckpointCtxStrict(physicalRoot, sessionId),
403
407
  });
@@ -420,7 +424,6 @@ class LocalBackend {
420
424
  }
421
425
  async ensureSchema() {
422
426
  this.executionLaneWordsRenamed += await this.sessionStore.renameRetiredExecutionLaneWordM40();
423
- await this.sessionStore.assertStrictSidecarsReadable();
424
427
  await this.sessionStore.finishPendingDrops();
425
428
  await this.sessionStore.settleArchiveSnapshots();
426
429
  const adoption = await this.sessionStore.adoptArchiveTokensM47();
@@ -443,11 +446,24 @@ class LocalBackend {
443
446
  });
444
447
  const runErrorCodeColumnRewritten = this.runStore.migrateRunErrorCodeColumnM44();
445
448
  const runObjectivePreviewRewritten = this.runStore.migrateRunObjectivePreviewM45();
446
- const shellSentinelOwnersCleared = this.identityBoundary ? 0 : (await this.sessionStore.clearShellSentinelOwnersM48()) + this.runStore.clearShellSentinelOwnersM48();
449
+ const shellSentinelOwnerRowsRetained = [];
450
+ let shellSentinelOwnersCleared = 0;
451
+ if (this.identityBoundary) {
452
+ for (const [surface, rows] of [
453
+ ["session_meta", await this.sessionStore.countShellSentinelOwnersM48()],
454
+ ["task_run", this.runStore.countShellSentinelOwnersM48()],
455
+ ]) {
456
+ if (rows > 0)
457
+ shellSentinelOwnerRowsRetained.push({ surface, rows });
458
+ }
459
+ }
460
+ else {
461
+ shellSentinelOwnersCleared = (await this.sessionStore.clearShellSentinelOwnersM48()) + this.runStore.clearShellSentinelOwnersM48();
462
+ }
447
463
  const checkpointHostSessionsBackfilled = existsSync(join(this.fileBackend.root, "checkpoint-session-tokens.json")) || existsSync(join(this.fileBackend.root, "checkpoint-scopes.json"))
448
464
  ? await this.checkpointStoreOf().backfillHostSessionsM49()
449
465
  : 0;
450
- const legacySkillExemptionsDropped = dropLocalLegacySkillExemptionsM46(join(this.fileBackend.root, LOCAL_LEGACY_SKILL_EXEMPTION_MARKER_FILE), this.approvalExemptionStore);
466
+ const legacySkillExemptionsDropped = dropLocalLegacySkillExemptionsM46(join(this.fileBackend.root, LOCAL_SKILL_EXEMPTION_CUTOFF_FILE), this.approvalExemptionStore);
451
467
  const archives = await this.sessionStore.migrateLocalArchives();
452
468
  const placements = await backfillLocalExecutionPlacements(this.runStore.sessionIdsWithRuns(), this.sessionStore.executionPlacementBackfill, { lanes: this.executionLanes });
453
469
  const importedLayerRoot = { importedLayerRootAllowsRetired: [], importedLayerRootBucketsSkipped: [], importedLayerRootBucketsUnnamed: 0 };
@@ -467,6 +483,7 @@ class LocalBackend {
467
483
  runErrorCodeColumnRewritten,
468
484
  runObjectivePreviewRewritten,
469
485
  shellSentinelOwnersCleared,
486
+ shellSentinelOwnerRowsRetained,
470
487
  checkpointHostSessionsBackfilled,
471
488
  legacySkillExemptionsDropped,
472
489
  executionPlacementsBackfilled: placements.backfilled,
@@ -524,6 +541,7 @@ class LocalBackend {
524
541
  userInputLedger() { return this.userInputLedgerStore; }
525
542
  checkpointStoreOf() {
526
543
  return (this.checkpointStoreInst ??= new LocalCheckpointStore(this.fileBackend.root, {
544
+ durability: this.storeDeclaration.durability,
527
545
  getActiveTaskId: (sessionId) => this.runStore.getActiveTaskId(sessionId),
528
546
  readSessionOwner: async (sessionId) => (await this.sessionStore.headOf(sessionId))?.owner,
529
547
  }));
@@ -544,6 +562,7 @@ class LocalBackend {
544
562
  checkpointStoreInst;
545
563
  sessionPolicy() { return (this.sessionPolicyInst ??= createClientCountedPolicyStore(this.fileBackend.sessionPolicyStore, this.sessionStore)); }
546
564
  sessionPolicyInst;
565
+ localStoreDeclaration() { return this.storeDeclaration; }
547
566
  fileUsageWindowStore() { return this.fileBackend.usageWindowStore; }
548
567
  fileHistory() { return this.fileBackend.fileHistoryStore; }
549
568
  memoryOptOutGrant() { return undefined; }
@@ -568,7 +587,7 @@ const LOCAL_OPEN_REFUSAL_HEAD = {
568
587
  "run-local": (root) => `cannot open local data dir ${root}`,
569
588
  };
570
589
  export function openLocalBackend(config, opts) {
571
- const { lane, onWarn, onError, observers } = opts;
590
+ const { lane, admission, onWarn, onError, observers, facts } = opts;
572
591
  const root = localBackendRootOf(config);
573
592
  const discloseWarn = onWarn ?? createLogger().warn;
574
593
  let fileBackend;
@@ -587,25 +606,24 @@ export function openLocalBackend(config, opts) {
587
606
  (tail === undefined ? "" : ` ${tail}`));
588
607
  }
589
608
  try {
590
- const physicalRoot = fileBackend.root;
591
- holdEngineHighWater(physicalRoot, serviceVersion(), (prior) => holdArchiveTokenAdoptionDuty(physicalRoot, prior, archiveTokenAdoptionCarried()));
609
+ holdLocalDataRootAdmission(fileBackend.root, admission);
592
610
  }
593
611
  catch (refusal) {
594
- return new RefusedLocalBackend(fileBackend, refusal instanceof Error ? refusal : new Error(String(refusal)));
612
+ return new RefusedLocalBackend(() => fileBackend.dispose(), refusal instanceof Error ? refusal : new Error(String(refusal)));
595
613
  }
596
614
  try {
597
- return new LocalBackend(fileBackend, config, discloseWarn, onWarn, onError, observers);
615
+ return new LocalBackend(fileBackend, config, discloseWarn, onWarn, onError, observers, facts);
598
616
  }
599
617
  catch (failure) {
600
- return new RefusedLocalBackend(fileBackend, failure instanceof Error ? failure : new Error(String(failure)));
618
+ return new RefusedLocalBackend(() => fileBackend.dispose(), failure instanceof Error ? failure : new Error(String(failure)));
601
619
  }
602
620
  }
603
621
  class RefusedLocalBackend {
604
- fileBackend;
622
+ release;
605
623
  refusal;
606
624
  kind = "local";
607
- constructor(fileBackend, refusal) {
608
- this.fileBackend = fileBackend;
625
+ constructor(release, refusal) {
626
+ this.release = release;
609
627
  this.refusal = refusal;
610
628
  }
611
629
  refused() {
@@ -618,7 +636,7 @@ class RefusedLocalBackend {
618
636
  return this.refused();
619
637
  }
620
638
  async close() {
621
- await this.fileBackend.dispose();
639
+ await this.release();
622
640
  }
623
641
  run() { return this.refused(); }
624
642
  resumeAnchor() { return this.refused(); }
@@ -640,12 +658,13 @@ class RefusedLocalBackend {
640
658
  engineFileStores() { return this.refused(); }
641
659
  toolResult() { return this.refused(); }
642
660
  fileUsageWindowStore() { return this.refused(); }
661
+ localStoreDeclaration() { return this.refused(); }
643
662
  mysqlPool() { return undefined; }
644
663
  pgPool() { return undefined; }
645
664
  }
646
665
  export function createStoreBackend(config, onWarn, onError, observers) {
647
666
  if (config.dbBackend === "local")
648
- return openLocalBackend(config, { lane: "http", onWarn, onError, observers });
667
+ return openAdmittedLocalBackend(config, undefined, { onWarn, onError, observers });
649
668
  if (config.dbBackend === "pg") {
650
669
  if (!config.pg)
651
670
  return undefined;
@@ -657,11 +676,24 @@ export function createStoreBackend(config, onWarn, onError, observers) {
657
676
  const pool = createTidbPool(config);
658
677
  return withRetiredSchemaProbe(new TiDBBackend(pool, config, observers), { dialect: "mysql", pool }, { lanes: deployedExecutionLanes(config) });
659
678
  }
679
+ function openAdmittedLocalBackend(config, admission, sinks) {
680
+ let admitted = admission;
681
+ if (admitted === undefined) {
682
+ try {
683
+ admitted = admitLocalDataRoot(localBackendRootOf(config));
684
+ }
685
+ catch (refusal) {
686
+ return new RefusedLocalBackend(async () => undefined, refusal instanceof Error ? refusal : new Error(String(refusal)));
687
+ }
688
+ }
689
+ return openLocalBackend(config, { lane: "http", admission: admitted, ...sinks });
690
+ }
660
691
  function withRetiredSchemaProbe(inner, target, executionPlacementSwitch) {
661
692
  const guarded = Object.create(inner);
662
693
  guarded.ensureSchema = async () => {
663
694
  await assertRetiredSchemaAbsentOnPool(target);
664
695
  await assertExecutionPlacementSwitchAdmissibleOnPool(target.dialect, target.pool, executionPlacementSwitch);
696
+ await assertSkillExemptionCutoffHeldOnPool(target, migrationCarried(SKILL_EXEMPTION_CUTOFF_ID));
665
697
  await inner.ensureSchema();
666
698
  };
667
699
  return guarded;
@@ -669,7 +701,7 @@ function withRetiredSchemaProbe(inner, target, executionPlacementSwitch) {
669
701
  export function localBackendRootOf(config) {
670
702
  return config.localDataRoot ?? join(homedir(), ".ai-agent");
671
703
  }
672
- export async function openStoreBackendWithFallback(config, logger, observers) {
704
+ export async function openStoreBackendWithFallback(config, logger, observers, localAdmission, facts) {
673
705
  const sqlEngine = config.dbBackend === "mysql" || config.dbBackend === "pg" ? config.dbBackend : undefined;
674
706
  const mayFallback = config.dbBackend === "local" && !config.dbBackendExplicit && !localDataRootHoldsData(localBackendRootOf(config));
675
707
  let backend;
@@ -678,7 +710,9 @@ export async function openStoreBackendWithFallback(config, logger, observers) {
678
710
  const attempt = async () => {
679
711
  let built;
680
712
  try {
681
- built = createStoreBackend(config, (msg, meta) => logger.warn(msg, meta), logger.error === undefined ? undefined : (msg, meta) => logger.error(msg, meta), observers);
713
+ const onWarn = (msg, meta) => logger.warn(msg, meta);
714
+ const onError = logger.error === undefined ? undefined : (msg, meta) => logger.error(msg, meta);
715
+ built = config.dbBackend === "local" ? openAdmittedLocalBackend(config, localAdmission, { onWarn, onError, observers, ...(facts !== undefined ? { facts } : {}) }) : createStoreBackend(config, onWarn, onError, observers);
682
716
  inFlight = built;
683
717
  if (built)
684
718
  await built.ensureSchema();
@@ -21,6 +21,7 @@
21
21
  import type { PeerSessionTempo, TaskResult, TaskStatus } from "@sema-agent/core";
22
22
  import type { RunIdentity } from "../identity-source.js";
23
23
  import type { SessionUnreadable } from "../session-error-wire.js";
24
+ import type { ReadDenyFloorSummary } from "../read-deny-floor-notice.js";
24
25
  /**
25
26
  * **File 店族的 boot 期拒启基类**([ref];把 `RunStoreStrictHydrateError` 的那条论证升成一条族规)。
26
27
  *
@@ -50,6 +51,19 @@ export declare class StoreBootRefusalError extends Error {
50
51
  * 会话部署格旁表建不出(`session-ext-table.ts`)、local 数据根的严格边档对账(`local-session-store.ts`)。码住在族基类旁边:读它的几只模块彼此不必互相 import。
51
52
  */
52
53
  export declare const SCHEMA_MISMATCH_ERROR_CODE = "config.schema_mismatch";
54
+ /**
55
+ * **SQL 库的列形不是本构建读得动的形 ⇒ 起服拒**的唯一出口:整库列册对账的三条拒(列集不符 / information_schema 读不出 / 整库零行,`schema-consistency.ts`)与六只单表探针的拒启
56
+ * (`checkpoint` · `tool_result` · `file_history_*` · `approval_ask` · `permission_rule_approval` · `permission_rule_ticket`)都经这一只类抛 —— 运维按码(`code` 字段 + 消息首词,同一个
57
+ * {@link SCHEMA_MISMATCH_ERROR_CODE})判「要跑 ALTER」,不必先猜是哪一只判官拒的。两只判官答同一问(这张表缺不缺列),出口只有一个。
58
+ *
59
+ * 属 {@link StoreBootRefusalError} 族:这是一句**确定**的拒启表态(再等也等不来另一个答案)—— 判官万一挪进 `ensureSchema()` 的开库等待窗,它不被重试、不被改写成「库打不开」。
60
+ * 消息**必须**以码领头:调用方交整句(对账的 `describeSchemaMismatch`、探针的 `probedMissingColumnRefusal` 都是码领头的整句),构造期核一次 —— 缺码 = 编程错,当场响,不是静默补一个。
61
+ */
62
+ export declare class SchemaMismatchRefusedError extends StoreBootRefusalError {
63
+ /** 机读码(与退役 schema / 旁表建不出 / local 数据根的拒启同一个)。 */
64
+ readonly code = "config.schema_mismatch";
65
+ constructor(message: string, options?: ErrorOptions);
66
+ }
53
67
  /**
54
68
  * 取消便签里的**大脑相位**快照 —— core `BrainStatus` 帧经 `brainStatusEventData` 投影后的五键子集。
55
69
  *
@@ -142,6 +156,14 @@ export type AbortCause = (typeof ABORT_CAUSES)[number];
142
156
  export interface PersistedResultSideKeys {
143
157
  cancelContext?: RunCancelContext;
144
158
  abortCause?: AbortCause;
159
+ /**
160
+ * 这条 run **照跑了**,而它的读拒行不按字节守:读拒表有行、执行环境不守读拒字节地板、部署没要求地板(`READ_DENY_FLOOR_REQUIRED` 未设)。
161
+ * `{ code: "config.read_deny_floor_unkept", message }` —— 与这条 run 事件流上那一帧 `engine_notice` 同码同句(句子点名 `REMOTE_EXEC=<车道>` 与出路
162
+ * `READ_DENY_FLOOR_REQUIRED=true`)。判官是引擎(每 run 准备期一次,结果按 `runId` 接上);说法的唯一属主 `read-deny-floor-notice.ts`,挂点 = 出口封口
163
+ * (`terminal.ts` 的 `sealResultForExit`)。拒起的 run 不带它(那一句在失败终局的句子里)。为什么属于 run 账本而不属于 core 的任务结果:引擎只发通告、
164
+ * 不在结果上留这件事,而句子里的执行环境与旋钮名是本仓的部署事实。
165
+ */
166
+ readDenyFloor?: ReadDenyFloorSummary;
145
167
  }
146
168
  /**
147
169
  * 落库/上 wire 的终局结果 = core 的 `TaskResult` **加上** server 自铸的可缺席侧记键({@link PersistedResultSideKeys})。
@@ -226,11 +248,15 @@ export interface RunRecord {
226
248
  * MY replica" (consult the in-process inflight/steerable map authoritatively) from "held elsewhere" (fall
227
249
  * back to the durable cross-replica flag). null for pre-column rows. */
228
250
  instanceId: string | null;
251
+ /** **持有腿**(`task_run.leg_id`):此刻持有这条行的那一条腿的身份 —— 认领铸它({@link mintRunLegId}:建行 = 首腿、续跑认领 = 新腿),交回给认领者;
252
+ * 停驻写的 CAS 只许持有腿落下(`setSuspended` / `setNeedsReview` 的 `legId`)。与 {@link instanceId} 是两件事:那一列答「活腿在哪台副本上」(路由),
253
+ * 这一列答「是不是这一条腿」。`null` = 没有持有腿的记录(列之前的行;更早版本的副本建 / 认领的行)—— 本版的腿从不持有它。不上 wire。 */
254
+ legId: string | null;
229
255
  /** 🔴 [ref] P-45 —— 这条 run 上**挂没挂着取消请求**(耐久 `cancel_requested` 列的投影,跨副本)。
230
256
  *
231
257
  * 语义就是那一列的字节:`POST /v1/runs/:id/cancel` 的 running 臂 CAS 成功即置位,持有副本的心跳拍
232
- * poll 到它就 abort;`markResuming`(续跑认领)把它清零 —— 一次撞上 suspend 的取消不许在恢复之后
233
- * 再杀一遍(那条 reset 的理由逐字在 run-store-sql `markResuming` 上)。
258
+ * poll 到它就 abort;旗是对**持有腿**的请求 —— 停驻写不落在挂着的旗上(停驻 CAS 认它,停驻提交读到旗就以取消收场),
259
+ * `markResuming`(续跑认领 = 换持有腿)把它清零,旗随旧腿去。
234
260
  *
235
261
  * 为什么是**行上的一位**而不是读面再查一次:列早就在(三个后端同名),只是行投影没带它 —— 正是
236
262
  * 本仓 L1 键集门盯的那族「投影剥键」。读面每次 poll 再发一条 `isCancelRequested` 查询,是给热路径
@@ -275,9 +301,12 @@ export interface NewRunMeta {
275
301
  objectivePreview?: string | null;
276
302
  idem?: RunIdemBinding | null;
277
303
  }
304
+ /** 铸一只持有腿({@link RunRecord.legId})—— 三只 run 店的两个认领口(`createRun` / `markResuming`)都调它,别处不铸。唯一值而非计数:同一 `taskId` 的行被删后重建,
305
+ * 新行的持有腿与旧腿的永不相等(旧腿迟到的停驻写不会落到新行上);认领者随写拿到它,不必读回。 */
306
+ export declare function mintRunLegId(): string;
278
307
  /**
279
308
  * `RunStore.createRun` 的结局 —— 四形,一形一句话:
280
- * · `{ ok: true }` —— 新行已建、会话 claim 已拿;
309
+ * · `{ ok: true, legId }` —— 新行已建、会话 claim 已拿;`legId` = 这次认领铸下的持有腿({@link RunRecord.legId}),首腿的停驻写凭它;
281
310
  * · `{ ok: false, existing }` —— 这次提交的一把**幂等钥匙**已经指向一条行:`idem_key`(`Idempotency-Key`)或
282
311
  * `task_id`(调用方自铸的 `body.taskId`)—— 两把钥匙是**同一条律**(资源行上的唯一键),先认 `idem_key` 再认 `task_id`。
283
312
  * 这是「原子赢或观察」的**观察**臂:行原样交回,没建新行、没动会话;回执由提交面的唯一回执函数判。
@@ -288,6 +317,7 @@ export interface NewRunMeta {
288
317
  */
289
318
  export type CreateRunOutcome = {
290
319
  ok: true;
320
+ legId: string;
291
321
  existing?: undefined;
292
322
  activeTaskId?: undefined;
293
323
  sessionAbsent?: undefined;
@@ -309,10 +339,14 @@ export type CreateRunOutcome = {
309
339
  };
310
340
  /**
311
341
  * 认领栅栏交给 `write` 的那一行 run 此刻的事实(SQL = 锁住那一行读的;进程内 = 同步段里读的)。认领与 `running` 只认 taskId,而续跑腿让同一个 taskId
312
- * 停驻之后回到 `running`、认领仍在 —— 「接下这笔写的那条腿还是不是这条 run 的腿」要按腿问的写者拿它问(今天:副本 `instanceId`,续跑的 CAS 赢点把它改成接手的副本)。
342
+ * 停驻之后回到 `running`、认领仍在 —— 「接下这笔写的那条腿还是不是这条 run 的腿」要按腿问的写者拿它问:
343
+ * · `legId` = 行此刻的持有腿({@link RunRecord.legId},认领铸、交给认领者)。持认领的腿自己的写问「是不是我」—— 判据 {@link createLegCtxClaim},
344
+ * 与停驻写的 CAS(`setSuspended` / `setNeedsReview`)、撤回(`withdrawMintedRun`)同一只身份读法;
345
+ * · `instanceId` = 行此刻的副本。切档口(`routes/run-permission-mode.ts` `switchLegHolds`)问的是「是不是我切过的那条腿」,它手上是本副本活流登记簿里的流对象。
313
346
  */
314
347
  export interface FencedRun {
315
348
  readonly instanceId: string | null;
349
+ readonly legId: string | null;
316
350
  }
317
351
  /** SQL 认领栅栏的结局:`held: false` = 认领此刻不是这条 run 的(或它已不在跑),`write` 一次都没调。 */
318
352
  export type ClaimFenced<T> = {
@@ -346,7 +380,7 @@ export type ClaimFenceStore = import("./run-store-sql.js").SqlRunStore | InProce
346
380
  /**
347
381
  * 🔴 [ref] **一笔续跑输入写所凭的那只认领**(检查点店的写口 `putCtx` 交认领的那一形与 `rewriteCtxWhileClaimed` 收它):
348
382
  * `fence` = 写者持认领的那只 run 店(栅栏的来源就是认领本身 —— 检查点店不在构造期另绑一份 run 店),`taskId` = 持认领的 run,
349
- * `legHolds` = 腿判据(在栅栏里对锁住 / 同步段里那一行同步问一次,只读、不许 await)。
383
+ * `legHolds` = 腿判据(在栅栏里对锁住 / 同步段里那一行同步问一次,只读、不许 await;持认领的腿经 {@link createLegCtxClaim} 按持有腿判)。
350
384
  * 装配契约:SQL 检查点店只收同一库上的 SQL run 店(两店出自同一后端;店按判别位 `db` 判 SQL 形且方言同,错形响亮抛),local 检查点店只收进程内栅栏。
351
385
  */
352
386
  export interface CtxWriteClaim {
@@ -354,6 +388,12 @@ export interface CtxWriteClaim {
354
388
  readonly taskId: string;
355
389
  readonly legHolds: (run: FencedRun) => boolean;
356
390
  }
391
+ /**
392
+ * 持认领的腿写续跑输入所凭的那只认领(提交腿 `http/submit-ctx.ts` 与续跑腿的写回 `http/resume-legs.ts` 同调这一只):腿判据 = 「被锁那一行此刻的持有腿就是本腿」——
393
+ * `legId` = 认领交给本腿的那一只(建行 `createRun` 的 `{ ok: true, legId }` / 续跑认领 `markResuming` 的交回值)。同一副本上同一 taskId 被后来的腿重新认领 ⇒
394
+ * 持有腿已换 ⇒ 零写(副本没变,只问副本的判据分不开这两条腿)。
395
+ */
396
+ export declare function createLegCtxClaim(fence: ClaimFenceStore, taskId: string, legId: string): CtxWriteClaim;
357
397
  /**
358
398
  * `RunRecord["status"]`(= `"running" | TaskStatus`)的**唯一分类表** —— 词表的属主 = 判据的属主,
359
399
  * 与类型定义同住一个文件。四个类:
@@ -1,7 +1,20 @@
1
1
  import { RUN_STALE_SEC_DEFAULT } from "../run-stale-default.js";
2
+ import { randomUUID } from "node:crypto";
2
3
  export class StoreBootRefusalError extends Error {
3
4
  }
4
5
  export const SCHEMA_MISMATCH_ERROR_CODE = "config.schema_mismatch";
6
+ export class SchemaMismatchRefusedError extends StoreBootRefusalError {
7
+ code = SCHEMA_MISMATCH_ERROR_CODE;
8
+ constructor(message, options) {
9
+ super(codeLed(message), options);
10
+ this.name = "SchemaMismatchRefusedError";
11
+ }
12
+ }
13
+ function codeLed(message) {
14
+ if (!message.startsWith(`${SCHEMA_MISMATCH_ERROR_CODE}: `))
15
+ throw new TypeError(`a ${SCHEMA_MISMATCH_ERROR_CODE} refusal must lead with its code`);
16
+ return message;
17
+ }
5
18
  export const ABORT_CAUSES = ["client_disconnected", "ask_park_grace_exhausted", "cancel_verb", "stall_verdict"];
6
19
  export const RUN_STALLED_ERROR = "run stalled (instance lost?)";
7
20
  export const RUN_ORPHANED_ERROR = "run orphaned by engine restart";
@@ -13,7 +26,13 @@ const RUN_HOLDER_DEATH_CODE_WORDS = new Set(Object.values(RUN_HOLDER_DEATH_CODES
13
26
  export function isHolderDeathVerdict(run) {
14
27
  return run.status === "failed" && run.errorCode !== null && RUN_HOLDER_DEATH_CODE_WORDS.has(run.errorCode);
15
28
  }
29
+ export function mintRunLegId() {
30
+ return randomUUID();
31
+ }
16
32
  export const FENCE_LANDED = Symbol("in-process-claim-fence.landed");
33
+ export function createLegCtxClaim(fence, taskId, legId) {
34
+ return { fence, taskId, legHolds: (run) => run.legId === legId };
35
+ }
17
36
  export const RUN_STATUS_CLASS = {
18
37
  running: "live",
19
38
  completed: "succeeded",
@@ -51,7 +51,7 @@ export declare function createTidbPool(config: ServiceConfig): Pool;
51
51
  * 3. 生成的全集 SQL 基线导出到 `docs/schema/baseline-mysql.sql`(由本数组机器生成;它是**产物**,不是真源
52
52
  * —— 冲突时以本文件为准并重新生成)。
53
53
  *
54
- * 注意:`ensureSchema` 仍然保留(建表 + advisory lock + image_bake_admit 种子行),语句数组是纯 CREATE。数组之外只有一只只读探针:会话部署格旁表
54
+ * 注意:`ensureSchema` 仍然保留(建表 + advisory lock + 两只种子行:image_bake_admit 与 `Skill` 豁免截点),语句数组是纯 CREATE。数组之外只有一只只读探针:会话部署格旁表
55
55
  * `session_ext` 的外键列要抄被引列 `session_meta.session_id` 的字符集 / 排序规则(MySQL 外键两侧必须相同,而存量库上它随建库年代不同),建它之前读一次
56
56
  * `information_schema.COLUMNS`(`session-ext-table.ts` 的 `ensureSessionExtTableMysql`;探针只读、不导出任何 ALTER)。
57
57
  */
@@ -59,17 +59,22 @@ export declare const SCHEMA_STATEMENTS: readonly string[];
59
59
  /** Named MySQL/TiDB advisory lock that serializes the whole `ensureSchema` DDL run (see below). */
60
60
  export declare const ENSURE_SCHEMA_LOCK = "sema_ensure_schema";
61
61
  /** Acquire {@link ENSURE_SCHEMA_LOCK} on `conn` (the lock is CONNECTION-level, so the caller MUST then run all the
62
- * serialized work on this SAME connection and release with {@link releaseEnsureSchemaLock}). Returns whether it was
63
- * acquired. Exported so test setup that does ad-hoc DDL (e.g. a DROP-then-recreate) can serialize against
62
+ * serialized work on this SAME connection and release with {@link releaseEnsureSchemaLock}). Resolves once the lock is
63
+ * held. Exported so test setup that does ad-hoc DDL (e.g. a DROP-then-recreate) can serialize against
64
64
  * ensureSchema runs using the IDENTICAL lock.
65
65
  *
66
66
  * Under heavy concurrency (the integration suite fans ~20 callers at the SAME named lock; or a fleet boots N
67
67
  * replicas at once) a BLOCKING GET_LOCK(name, 30) would park ~19 sessions in a pessimistic WAIT on TiDB's advisory-
68
68
  * lock row — which both exhausts TiDB's "pessimistic lock retry limit" (surfacing as a thrown error) AND starves the
69
- * app's OWN `SELECT … FOR UPDATE` paths (image-bake admission/lease) of pessimistic-lock budget. So we acquire
70
- * NON-BLOCKING (GET_LOCK(name, 0) returns 0 immediately if held) and POLL with jittered backoff: one holder, the
69
+ * app's OWN `SELECT … FOR UPDATE` paths (image-bake admission/lease) of pessimistic-lock budget. So the lock is taken
70
+ * NON-BLOCKING (GET_LOCK(name, 0) returns 0 immediately if held) and POLLED with jittered backoff: one holder, the
71
71
  * rest sleeping (not blocked in-DB). Polls until acquired (7.83.0: never degrades to unserialized — the [ref]
72
- * `ADD INDEX` leg is not race-safe); a connection whose probe keeps throwing surfaces as a boot failure. */
72
+ * `ADD INDEX` leg is not race-safe); a connection whose probe keeps throwing surfaces as a boot failure.
73
+ *
74
+ * The probe statement, the reading of its answer, the engine's "pessimistic lock retry limit reached" sentence (read as
75
+ * "somebody else is on the lock row" — the same fact as `got = 0`, NOT a broken connection) and the polling law all
76
+ * live in ONE place, `ensure-schema-lock-policy.ts`; every other lock in this repo (the adoption arc lock) is taken
77
+ * through the same acquirer. */
73
78
  export declare function acquireEnsureSchemaLock(conn: PoolConnection, tag?: string): Promise<void>;
74
79
  /** Release {@link ENSURE_SCHEMA_LOCK} on `conn` (best-effort — the lock also auto-frees when the connection closes). */
75
80
  export declare function releaseEnsureSchemaLock(conn: PoolConnection, tag?: string): Promise<void>;
@@ -1,4 +1,3 @@
1
- import { writeLogText } from "../observability/logger.js";
2
1
  import mysql from "mysql2/promise";
3
2
  import { TIDB_APPROVAL_ASK_STATEMENTS } from "./approval-ask-store-sql.js";
4
3
  import { TIDB_MEMORY_OPTOUT_GRANT_STATEMENTS } from "./memory-optout-grant-store-sql.js";
@@ -30,7 +29,10 @@ import { PERMISSION_RULE_INDEXES } from "./permission-rule-store-sql.js";
30
29
  import { RETENTION_TOMBSTONE_INDEXES } from "../stores-sql/retention-store-sql.js";
31
30
  import { RETENTION_LANE_INDEXES } from "./retention-lane-store-sql.js";
32
31
  import { LEADER_RUN_INDEXES } from "./leader-run-store-sql.js";
33
- import { pollUntilLocked } from "./ensure-schema-lock-policy.js";
32
+ import { acquireAdvisoryLock, releaseAdvisoryLock } from "./ensure-schema-lock-policy.js";
33
+ import { SKILL_EXEMPTION_CUTOFF_ID, markIfAbsentSql, migrationMarkerDdl } from "./one-time-migration-marker.js";
34
+ import { replicaRegistrationDdl } from "./replica-registration.js";
35
+ import { servingRequirementDdl } from "./serving-requirement.js";
34
36
  import { createCloseAwarePool } from "./sql-driver.js";
35
37
  export function tidbPoolOptions(tidb) {
36
38
  const { host, port, user, password, database, connectionLimit } = tidb;
@@ -149,6 +151,11 @@ export const SCHEMA_STATEMENTS = [
149
151
  -- deliberately-absent idx_error_code note below.
150
152
  error_code VARCHAR(64) NULL,
151
153
  instance_id VARCHAR(64) NULL,
154
+ -- leg_id: the HOLDING LEG — minted at every claim (createRun = first leg, markResuming = the resuming leg) and handed to the claimer; a park
155
+ -- write lands only for the leg that holds the row (setSuspended / setNeedsReview CAS on it). instance_id says WHERE the live leg is (routing),
156
+ -- leg_id says WHICH leg it is. NULL = no holding leg on record (a row written before the column existed, or created by an earlier-version
157
+ -- replica; an earlier-version replica's resume claim leaves the column as it found it).
158
+ leg_id VARCHAR(36) NULL,
152
159
  -- job_id (work-view correlation substrate): groups the runs of one logical task across BOTH client doors.
153
160
  job_id VARCHAR(64) NULL,
154
161
  -- source (credential-derived system identity, clay 2026-06-12): which door submitted the run
@@ -774,6 +781,9 @@ export const SCHEMA_STATEMENTS = [
774
781
  pool VARCHAR(32) NOT NULL,
775
782
  PRIMARY KEY (pool)
776
783
  ) COLLATE utf8mb4_bin`,
784
+ migrationMarkerDdl("mysql"),
785
+ replicaRegistrationDdl("mysql"),
786
+ servingRequirementDdl("mysql"),
777
787
  ...TIDB_APPROVAL_ASK_STATEMENTS,
778
788
  ...TIDB_MEMORY_OPTOUT_GRANT_STATEMENTS,
779
789
  ...TIDB_SESSION_CAPTURE_OPTOUT_STATEMENTS,
@@ -817,19 +827,18 @@ export const SCHEMA_STATEMENTS = [
817
827
  ...TIDB_RETENTION_TOMBSTONE_STATEMENTS,
818
828
  ];
819
829
  export const ENSURE_SCHEMA_LOCK = "sema_ensure_schema";
830
+ const ENSURE_SCHEMA_LOCK_ID = { dialect: "mysql", name: ENSURE_SCHEMA_LOCK };
831
+ const lockConnOf = (conn) => ({
832
+ query: async (sql, params) => {
833
+ const [rows] = await conn.query(sql, params);
834
+ return { rows };
835
+ },
836
+ });
820
837
  export async function acquireEnsureSchemaLock(conn, tag = "ensureSchema") {
821
- await pollUntilLocked(async () => {
822
- const [rows] = await conn.query("SELECT GET_LOCK(?, 0) AS got", [ENSURE_SCHEMA_LOCK]);
823
- return Number(rows[0]?.got) === 1;
824
- }, { tag, label: `GET_LOCK('${ENSURE_SCHEMA_LOCK}')` });
838
+ await acquireAdvisoryLock(lockConnOf(conn), ENSURE_SCHEMA_LOCK_ID, tag);
825
839
  }
826
840
  export async function releaseEnsureSchemaLock(conn, tag = "ensureSchema") {
827
- try {
828
- await conn.query("SELECT RELEASE_LOCK(?)", [ENSURE_SCHEMA_LOCK]);
829
- }
830
- catch (err) {
831
- writeLogText("stderr", `[${tag}] RELEASE_LOCK('${ENSURE_SCHEMA_LOCK}') failed (lock auto-frees on connection close):`, err);
832
- }
841
+ await releaseAdvisoryLock(lockConnOf(conn), ENSURE_SCHEMA_LOCK_ID, tag);
833
842
  }
834
843
  export const SCHEMA_INDEXES = [
835
844
  ...CENTRAL_SCHEMA_INDEXES,
@@ -861,6 +870,7 @@ export async function ensureSchema(pool) {
861
870
  return Array.isArray(rows) ? rows : [];
862
871
  });
863
872
  await conn.query("INSERT IGNORE INTO image_bake_admit (pool) VALUES ('standard')");
873
+ await conn.query(markIfAbsentSql("mysql"), [SKILL_EXEMPTION_CUTOFF_ID, Date.now()]);
864
874
  await ensureIndexes((text, params) => conn.query(text, params), "mysql", SCHEMA_INDEXES);
865
875
  }
866
876
  finally {