@sema-agent/server 7.150.0 → 7.151.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 (341) hide show
  1. package/MIGRATION.md +185 -0
  2. package/USAGE.md +53 -25
  3. package/deploy/sema-up/README.md +7 -1
  4. package/deploy/sema-up/chart/Chart.yaml +1 -1
  5. package/deploy/sema-up/chart/templates/NOTES.txt +5 -0
  6. package/deploy/sema-up/chart/templates/_helpers.tpl +15 -16
  7. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -3
  8. package/deploy/sema-up/chart/templates/server.yaml +5 -3
  9. package/deploy/sema-up/chart/values-full-single.yaml +3 -1
  10. package/deploy/sema-up/chart/values.yaml +8 -1
  11. package/deploy/sema-up/cluster-up.sh +1 -0
  12. package/deploy/sema-up/kube-up.sh +1 -1
  13. package/deploy/sema-up/lib/flagshape.sh +1 -1
  14. package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
  15. package/deploy/sema-up/lib/helm-values-kube.py +2 -0
  16. package/dist/adoption/import.js +3 -3
  17. package/dist/approval-ask-machine.d.ts +1 -1
  18. package/dist/approval-card.d.ts +2 -2
  19. package/dist/approval-gate-currency.d.ts +1 -1
  20. package/dist/auth-bridge.d.ts +20 -0
  21. package/dist/auth-bridge.js +9 -0
  22. package/dist/background-session-reap.d.ts +1 -1
  23. package/dist/bake-runner/main.d.ts +4 -2
  24. package/dist/bind-host.d.ts +2 -3
  25. package/dist/bind-host.js +2 -4
  26. package/dist/boot/budget-tracing.js +1 -1
  27. package/dist/boot/engine-lease.d.ts +5 -0
  28. package/dist/boot/engine-lease.js +1 -0
  29. package/dist/boot/execution-env.d.ts +8 -14
  30. package/dist/boot/execution-env.js +54 -17
  31. package/dist/boot/execution-lane.js +32 -5
  32. package/dist/boot/late-binds.d.ts +8 -4
  33. package/dist/boot/late-binds.js +7 -1
  34. package/dist/boot/memory-consolidation.d.ts +3 -2
  35. package/dist/boot/memory-consolidation.js +6 -6
  36. package/dist/boot/reapers.d.ts +5 -0
  37. package/dist/boot/reapers.js +12 -1
  38. package/dist/boot/resolve-spec.d.ts +3 -3
  39. package/dist/boot/resolve-spec.js +31 -22
  40. package/dist/boot/runner-deps.d.ts +9 -2
  41. package/dist/boot/runner-deps.js +3 -3
  42. package/dist/boot/session-age-out.d.ts +36 -0
  43. package/dist/boot/session-age-out.js +101 -0
  44. package/dist/boot/session-faces.d.ts +5 -14
  45. package/dist/boot/session-faces.js +9 -5
  46. package/dist/boot/stage-01-config.js +2 -0
  47. package/dist/boot/stage-05-execution-env.d.ts +5 -3
  48. package/dist/boot/stage-05-execution-env.js +2 -2
  49. package/dist/boot/stage-06-runners.d.ts +7 -4
  50. package/dist/boot/stage-06-runners.js +15 -6
  51. package/dist/boot/stage-07-capability-layer.d.ts +7 -4
  52. package/dist/boot/stage-07-capability-layer.js +2 -1
  53. package/dist/boot/stage-08-reapers.d.ts +7 -4
  54. package/dist/boot/stage-08-reapers.js +4 -2
  55. package/dist/boot/stage-09-leader.d.ts +11 -5
  56. package/dist/boot/stage-09-leader.js +12 -3
  57. package/dist/boot/stage-10-http-server.d.ts +11 -5
  58. package/dist/boot/stage-10-http-server.js +3 -6
  59. package/dist/boot/web-admin.js +3 -1
  60. package/dist/boot/workflow-orchestration.d.ts +7 -2
  61. package/dist/boot/workflow-orchestration.js +24 -7
  62. package/dist/bounded-rotation.d.ts +29 -0
  63. package/dist/bounded-rotation.js +24 -0
  64. package/dist/capability-facts.d.ts +22 -13
  65. package/dist/capability-facts.js +28 -18
  66. package/dist/center-credential.d.ts +25 -6
  67. package/dist/center-credential.js +25 -5
  68. package/dist/config-catalog.d.ts +26 -7
  69. package/dist/config-catalog.js +14 -11
  70. package/dist/config-invariants.d.ts +2 -1
  71. package/dist/config-invariants.js +4 -4
  72. package/dist/config-types.d.ts +10 -4
  73. package/dist/config.d.ts +51 -2
  74. package/dist/config.js +35 -24
  75. package/dist/deployment-shape.d.ts +9 -1
  76. package/dist/deployment-shape.js +2 -1
  77. package/dist/device-protocol.d.ts +5 -4
  78. package/dist/device-protocol.js +1 -1
  79. package/dist/device-ws-hub.js +3 -2
  80. package/dist/device-ws-protocol.d.ts +63 -10
  81. package/dist/device-ws-protocol.js +56 -25
  82. package/dist/engine-identity.d.ts +19 -7
  83. package/dist/engine-identity.js +10 -2
  84. package/dist/entry-mode-clamp-notice.d.ts +2 -1
  85. package/dist/execution-env-refusal.d.ts +23 -0
  86. package/dist/execution-env-refusal.js +5 -0
  87. package/dist/execution-lane-caps.d.ts +3 -1
  88. package/dist/field-conflict.d.ts +1 -1
  89. package/dist/file-history-capture.d.ts +11 -4
  90. package/dist/file-history-capture.js +9 -6
  91. package/dist/file-history-disclosure.d.ts +7 -5
  92. package/dist/file-history-disclosure.js +7 -5
  93. package/dist/fleet/drain-entry.d.ts +4 -0
  94. package/dist/hooks/hook-runner.js +2 -2
  95. package/dist/host-lsp-manager.d.ts +2 -2
  96. package/dist/http/admission.js +17 -2
  97. package/dist/http/approval-sweeps.js +5 -5
  98. package/dist/http/checkpoint-ctx.d.ts +2 -0
  99. package/dist/http/checkpoint-ctx.js +1 -0
  100. package/dist/http/dispatch.d.ts +2 -1
  101. package/dist/http/dispatch.js +34 -27
  102. package/dist/http/durable-run-leg.js +16 -16
  103. package/dist/http/health-self-report.d.ts +9 -1
  104. package/dist/http/health-self-report.js +2 -0
  105. package/dist/http/owner-access.d.ts +31 -11
  106. package/dist/http/owner-access.js +37 -6
  107. package/dist/http/resume-ctx-read.js +25 -3
  108. package/dist/http/resume-legs.d.ts +2 -1
  109. package/dist/http/resume-legs.js +259 -79
  110. package/dist/http/route-ctx.d.ts +18 -8
  111. package/dist/http/route-table.d.ts +1 -1
  112. package/dist/http/routes/admin-memory-optout.d.ts +1 -1
  113. package/dist/http/routes/agents-roster.js +2 -3
  114. package/dist/http/routes/approvals-assistant.js +4 -3
  115. package/dist/http/routes/background.d.ts +3 -3
  116. package/dist/http/routes/capabilities.js +8 -5
  117. package/dist/http/routes/fleet.js +10 -8
  118. package/dist/http/routes/projects.d.ts +3 -2
  119. package/dist/http/routes/projects.js +4 -5
  120. package/dist/http/routes/runs.js +102 -36
  121. package/dist/http/routes/session-sync.d.ts +4 -3
  122. package/dist/http/routes/session-sync.js +172 -77
  123. package/dist/http/routes/sessions-list.js +13 -7
  124. package/dist/http/routes/sessions.js +10 -6
  125. package/dist/http/routes/tasks.js +30 -16
  126. package/dist/http/routes/trace-usage.js +2 -2
  127. package/dist/http/server.d.ts +21 -27
  128. package/dist/http/server.js +2 -2
  129. package/dist/http/submit-ctx.js +5 -4
  130. package/dist/http/wire-types.d.ts +14 -4
  131. package/dist/identity-source.d.ts +20 -3
  132. package/dist/identity-source.js +14 -0
  133. package/dist/index.d.ts +4 -0
  134. package/dist/lane-home-probe.d.ts +3 -7
  135. package/dist/lane-home-probe.js +0 -1
  136. package/dist/leader/leader.d.ts +2 -1
  137. package/dist/leg-facts.d.ts +13 -5
  138. package/dist/leg-facts.js +2 -1
  139. package/dist/memory-scope.d.ts +5 -4
  140. package/dist/memory-scope.js +4 -4
  141. package/dist/memory-sync-client.d.ts +30 -2
  142. package/dist/memory-sync-client.js +16 -9
  143. package/dist/observability/err-text.d.ts +7 -0
  144. package/dist/observability/err-text.js +5 -0
  145. package/dist/observability/fail-open.d.ts +7 -3
  146. package/dist/observability/fail-open.js +7 -3
  147. package/dist/observability/metrics.js +1 -0
  148. package/dist/orchestration/workflow-notify-journal.d.ts +18 -1
  149. package/dist/orchestration/workflow-notify-journal.js +12 -1
  150. package/dist/orchestration/workflow-park-decision.d.ts +70 -18
  151. package/dist/orchestration/workflow-park-decision.js +49 -10
  152. package/dist/orchestration/workflow-park-holder.d.ts +65 -0
  153. package/dist/orchestration/workflow-park-holder.js +98 -0
  154. package/dist/orchestration/workflow-record-fence.d.ts +36 -0
  155. package/dist/orchestration/workflow-record-fence.js +42 -0
  156. package/dist/owner-faces.d.ts +1 -1
  157. package/dist/parked-decide.d.ts +104 -53
  158. package/dist/parked-decide.js +153 -117
  159. package/dist/peer-directory.d.ts +2 -2
  160. package/dist/peer-inbound-face.d.ts +2 -6
  161. package/dist/peer-inbound-face.js +0 -3
  162. package/dist/permission-mode-admission.d.ts +41 -5
  163. package/dist/permission-mode-admission.js +29 -3
  164. package/dist/plugins/adoption-log-sql.d.ts +3 -1
  165. package/dist/plugins/adoption-log-sql.js +7 -1
  166. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  167. package/dist/plugins/approval-ask-store-file.js +8 -0
  168. package/dist/plugins/approval-ask-store-memory.d.ts +6 -1
  169. package/dist/plugins/approval-ask-store-memory.js +28 -9
  170. package/dist/plugins/approval-ask-store-sql.d.ts +43 -2
  171. package/dist/plugins/approval-ask-store-sql.js +34 -5
  172. package/dist/plugins/approval-exemption-store.js +3 -0
  173. package/dist/plugins/approval-nonce-store.js +2 -0
  174. package/dist/plugins/breaker-state-sql.js +2 -0
  175. package/dist/plugins/caching-session-store.d.ts +9 -15
  176. package/dist/plugins/caching-session-store.js +6 -0
  177. package/dist/plugins/device-store-sql.js +6 -0
  178. package/dist/plugins/error-origin.d.ts +18 -0
  179. package/dist/plugins/error-origin.js +10 -0
  180. package/dist/plugins/execution-placement-sql.js +6 -6
  181. package/dist/plugins/image-bake-store-sql.js +6 -0
  182. package/dist/plugins/image-index-sql.js +4 -0
  183. package/dist/plugins/landed-attendants.d.ts +212 -0
  184. package/dist/plugins/landed-attendants.js +161 -0
  185. package/dist/plugins/leader-run-store-sql.js +4 -0
  186. package/dist/plugins/local-session-live-reference.d.ts +40 -0
  187. package/dist/plugins/local-session-live-reference.js +39 -0
  188. package/dist/plugins/local-session-store.d.ts +71 -33
  189. package/dist/plugins/local-session-store.js +129 -27
  190. package/dist/plugins/memory-engine-pg.js +4 -0
  191. package/dist/plugins/memory-engine-sql-apply.js +5 -2
  192. package/dist/plugins/memory-engine-tidb.js +4 -0
  193. package/dist/plugins/memory-optout-grant-store-sql.js +4 -0
  194. package/dist/plugins/memory-sync-store-pg.js +5 -0
  195. package/dist/plugins/memory-sync-store-tidb.js +5 -0
  196. package/dist/plugins/one-time-migrations.d.ts +57 -63
  197. package/dist/plugins/one-time-migrations.js +162 -90
  198. package/dist/plugins/org-delivery-quota-store.js +4 -1
  199. package/dist/plugins/outcome-ledger-sql.js +2 -0
  200. package/dist/plugins/permission-rule-store-sql.js +11 -4
  201. package/dist/plugins/pg-cost-quota.js +4 -0
  202. package/dist/plugins/pg-pool.js +24 -1
  203. package/dist/plugins/pg-rate-limiter.js +2 -0
  204. package/dist/plugins/posix-shell-fs.d.ts +6 -1
  205. package/dist/plugins/posix-shell-fs.js +4 -1
  206. package/dist/plugins/remote-env-adb.d.ts +19 -15
  207. package/dist/plugins/remote-env-adb.js +41 -15
  208. package/dist/plugins/remote-env-connect-seat.d.ts +105 -0
  209. package/dist/plugins/remote-env-connect-seat.js +72 -0
  210. package/dist/plugins/remote-env-device.d.ts +22 -5
  211. package/dist/plugins/remote-env-device.js +106 -8
  212. package/dist/plugins/remote-env-e2b.d.ts +15 -6
  213. package/dist/plugins/remote-env-e2b.js +42 -32
  214. package/dist/plugins/remote-env-file-error.js +6 -2
  215. package/dist/plugins/remote-env-host.d.ts +17 -0
  216. package/dist/plugins/remote-env-host.js +30 -38
  217. package/dist/plugins/remote-env-k8s.d.ts +41 -39
  218. package/dist/plugins/remote-env-k8s.js +53 -30
  219. package/dist/plugins/remote-env-local-docker.d.ts +13 -8
  220. package/dist/plugins/remote-env-local-docker.js +34 -41
  221. package/dist/plugins/remote-env-ssh.d.ts +18 -12
  222. package/dist/plugins/remote-env-ssh.js +60 -59
  223. package/dist/plugins/remote-shell.d.ts +98 -1
  224. package/dist/plugins/remote-shell.js +218 -0
  225. package/dist/plugins/resume-anchor-store-sql.d.ts +11 -2
  226. package/dist/plugins/resume-anchor-store-sql.js +12 -5
  227. package/dist/plugins/retention-lane-store-sql.js +4 -0
  228. package/dist/plugins/retention-sql.d.ts +20 -2
  229. package/dist/plugins/retention-sql.js +11 -0
  230. package/dist/plugins/run-store-sql.d.ts +21 -2
  231. package/dist/plugins/run-store-sql.js +31 -7
  232. package/dist/plugins/scenario-prefix-switch-m42.d.ts +1 -1
  233. package/dist/plugins/scenario-prefix-switch-m42.js +2 -2
  234. package/dist/plugins/schema-consistency.d.ts +4 -2
  235. package/dist/plugins/schema-consistency.js +19 -2
  236. package/dist/plugins/send-file-ledger.js +3 -0
  237. package/dist/plugins/serving-requirement.d.ts +26 -7
  238. package/dist/plugins/serving-requirement.js +11 -4
  239. package/dist/plugins/session-birth.d.ts +6 -2
  240. package/dist/plugins/session-capture-record-store-sql.js +2 -0
  241. package/dist/plugins/session-ext-table.d.ts +14 -1
  242. package/dist/plugins/session-ext-table.js +5 -1
  243. package/dist/plugins/session-row-sql.d.ts +49 -2
  244. package/dist/plugins/session-row-sql.js +56 -13
  245. package/dist/plugins/session-store-sql.d.ts +34 -18
  246. package/dist/plugins/session-store-sql.js +98 -17
  247. package/dist/plugins/shared-memory-store-sql.d.ts +1 -0
  248. package/dist/plugins/shared-memory-store-sql.js +6 -2
  249. package/dist/plugins/sql-driver.d.ts +2 -1
  250. package/dist/plugins/sql-driver.js +18 -3
  251. package/dist/plugins/sql-errors.d.ts +11 -6
  252. package/dist/plugins/sql-errors.js +5 -6
  253. package/dist/plugins/stop-then-start-sentence.d.ts +10 -4
  254. package/dist/plugins/stop-then-start-sentence.js +14 -10
  255. package/dist/plugins/stop-then-start.d.ts +109 -0
  256. package/dist/plugins/stop-then-start.js +104 -0
  257. package/dist/plugins/store-backend.d.ts +40 -13
  258. package/dist/plugins/store-backend.js +169 -27
  259. package/dist/plugins/store-boundary.d.ts +57 -0
  260. package/dist/plugins/store-boundary.js +139 -0
  261. package/dist/plugins/store-contracts.d.ts +17 -0
  262. package/dist/plugins/store-fault-sentence-m53.d.ts +7 -0
  263. package/dist/plugins/store-fault-sentence-m53.js +25 -0
  264. package/dist/plugins/store-fault.d.ts +26 -7
  265. package/dist/plugins/store-fault.js +18 -2
  266. package/dist/plugins/task-attachment-store.js +9 -0
  267. package/dist/plugins/task-list-store-sql.d.ts +2 -2
  268. package/dist/plugins/task-list-store-sql.js +10 -8
  269. package/dist/plugins/tidb-cost-quota.js +4 -0
  270. package/dist/plugins/tidb-pool.js +2 -1
  271. package/dist/plugins/tidb-rate-limiter.js +4 -0
  272. package/dist/plugins/user-input-ledger-sql.js +4 -0
  273. package/dist/plugins/workflow-run-store-sql.d.ts +28 -10
  274. package/dist/plugins/workflow-run-store-sql.js +64 -4
  275. package/dist/plugins/workspace-fill.d.ts +4 -7
  276. package/dist/plugins/workspace-fill.js +2 -1
  277. package/dist/plugins/write-behind-counter.js +5 -0
  278. package/dist/project-context.d.ts +0 -2
  279. package/dist/project-context.js +4 -8
  280. package/dist/project-registry.d.ts +46 -0
  281. package/dist/project-registry.js +44 -0
  282. package/dist/request-key-closure.js +1 -1
  283. package/dist/request-value-closure.d.ts +4 -0
  284. package/dist/request-value-closure.js +10 -0
  285. package/dist/run-acceptance.d.ts +3 -2
  286. package/dist/run-acceptance.js +4 -6
  287. package/dist/run-local.js +1 -0
  288. package/dist/runs.d.ts +37 -8
  289. package/dist/runs.js +53 -12
  290. package/dist/runtime-governance.d.ts +5 -4
  291. package/dist/security.d.ts +30 -32
  292. package/dist/security.js +41 -7
  293. package/dist/server-secret-env.d.ts +12 -10
  294. package/dist/server-secret-env.js +24 -15
  295. package/dist/session-bindings.d.ts +82 -0
  296. package/dist/session-bindings.js +18 -0
  297. package/dist/session-children-retire.d.ts +2 -2
  298. package/dist/session-children-retire.js +2 -6
  299. package/dist/session-deletion.d.ts +16 -1
  300. package/dist/session-error-wire.d.ts +3 -3
  301. package/dist/session-error-wire.js +3 -3
  302. package/dist/session-incarnation.d.ts +10 -0
  303. package/dist/session-incarnation.js +10 -1
  304. package/dist/session-kind.d.ts +57 -0
  305. package/dist/session-kind.js +22 -0
  306. package/dist/session-row.d.ts +21 -1
  307. package/dist/session-row.js +7 -1
  308. package/dist/session-sync-kernel.d.ts +24 -14
  309. package/dist/session-sync-kernel.js +3 -6
  310. package/dist/session-sync.d.ts +4 -98
  311. package/dist/session-sync.js +1 -71
  312. package/dist/session-titler.d.ts +10 -3
  313. package/dist/session-titler.js +19 -6
  314. package/dist/ssh-host-key.d.ts +5 -0
  315. package/dist/ssh-host-key.js +3 -0
  316. package/dist/stores-sql/file-history-store-sql.d.ts +52 -2
  317. package/dist/stores-sql/file-history-store-sql.js +108 -70
  318. package/dist/stores-sql/pg-session-storage.d.ts +34 -7
  319. package/dist/stores-sql/pg-session-storage.js +37 -13
  320. package/dist/stores-sql/retention-store-sql.d.ts +1 -9
  321. package/dist/stores-sql/retention-store-sql.js +2 -10
  322. package/dist/stores-sql/session-policy-store-sql.d.ts +30 -7
  323. package/dist/stores-sql/session-policy-store-sql.js +61 -44
  324. package/dist/stores-sql/session-write-hooks.d.ts +11 -0
  325. package/dist/stores-sql/tidb-session-store.d.ts +58 -7
  326. package/dist/stores-sql/tidb-session-store.js +37 -13
  327. package/dist/submission-exit.d.ts +3 -2
  328. package/dist/submission-exit.js +9 -1
  329. package/dist/task-cwd.d.ts +2 -2
  330. package/dist/task-settings.d.ts +6 -5
  331. package/dist/terminal.d.ts +33 -2
  332. package/dist/terminal.js +139 -9
  333. package/dist/tool-approval.d.ts +140 -6
  334. package/dist/tool-approval.js +165 -72
  335. package/dist/trace/self-minted-wire.d.ts +290 -27
  336. package/dist/trace/self-minted-wire.js +101 -13
  337. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +78 -27
  338. package/package.json +2 -2
  339. package/wire-changes/server@7.151.0.json +762 -0
  340. package/dist/http/session-inherit-env.d.ts +0 -36
  341. package/dist/http/session-inherit-env.js +0 -6
@@ -0,0 +1,7 @@
1
+ type Json = Record<string, unknown>;
2
+ /** 后台子代记录的改写(见头注);不改 ⇒ `undefined`。 */
3
+ export declare function rewrittenBackgroundRecordM53(record: unknown): Json | undefined;
4
+ /** `tool_end` 事件数据的改写(见头注);不改 ⇒ `undefined`。 */
5
+ export declare function rewrittenToolEndDataM53(data: unknown): Json | undefined;
6
+ export {};
7
+ //# sourceMappingURL=store-fault-sentence-m53.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { outwardFailureCode, outwardStoreMessageOf } from "../terminal.js";
2
+ const isRecord = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
3
+ function outwardOrUnchanged(code, message) {
4
+ if (typeof code !== "string" || typeof message !== "string" || outwardFailureCode(code) === code)
5
+ return undefined;
6
+ const outward = outwardStoreMessageOf(code, message);
7
+ return outward === message ? undefined : outward;
8
+ }
9
+ export function rewrittenBackgroundRecordM53(record) {
10
+ if (!isRecord(record))
11
+ return undefined;
12
+ const outward = outwardOrUnchanged(record.errorCode, record.error);
13
+ if (outward === undefined)
14
+ return undefined;
15
+ const old = record.error;
16
+ const summary = typeof record.summary === "string" && old !== "" && record.summary.includes(old) ? { summary: record.summary.split(old).join(outward) } : {};
17
+ return { ...record, error: outward, ...summary };
18
+ }
19
+ export function rewrittenToolEndDataM53(data) {
20
+ if (!isRecord(data) || !isRecord(data.structured))
21
+ return undefined;
22
+ const outward = outwardOrUnchanged(data.structured.errorCode, data.structured.errorMessage);
23
+ return outward === undefined ? undefined : { ...data, structured: { ...data.structured, errorMessage: outward } };
24
+ }
25
+ //# sourceMappingURL=store-fault-sentence-m53.js.map
@@ -6,12 +6,13 @@ export type StoreFaultClass = "corrupt" | "connection" | "deployment" | "defect"
6
6
  */
7
7
  export declare function isCodeDefect(err: unknown): boolean;
8
8
  /** 判官本体。纯函数、不抛;只看被抛出的这一只对象(不沿 `cause` 链找)。连接类与部署错配两张识别表互斥,两者之间判序不影响结果(`test/store-fault-judge.test.ts` 钉互斥);
9
- * 程序错排在它们之后(带那两类归因键的对象按那两类认)。 */
9
+ * 程序错排在它们之后(带那两类归因键的对象按那两类认)。
10
+ * 首臂 = 店边界形({@link StoreBoundaryFault}):边界在**原错**上判过一次(连接类整句、errno 臂、1005 内层号都只在原错上读得到),词随形带走 —— 读它就是读那一判,不是第二种读法。 */
10
11
  export declare function storeFaultClassOf(err: unknown): StoreFaultClass;
11
12
  /**
12
13
  * 判官每个词的**对外句**(闭集 `Record`:判官加一个词而不在这里写出它对外怎么说 = 编译错)。这是本判官唯一的一张「对外怎么说」的表 ——
13
- * 失败要出 wire 时,调用方只拿**类别**说话,不把驱动的原句(带库用户名 / 主机 / 表名 / 约束名)交出去;驱动原句只进运维日志
14
- * (`terminal.ts` 的折码处),出口脱敏器是模式脱敏、认不出「这是库账号 / 表名」。每句只说两件事:**坏在哪一类** + **重试有没有用**,不带任何实例化的名字。
14
+ * 失败要出店 / 出 wire 时,调用方只拿**类别**说话,不把驱动的原句(带库用户名 / 主机 / 表名 / 约束名)交出去;驱动原句只进运维日志
15
+ * (店边界 `terminal.ts` 的 `storeBoundaryFault` 那一行 `at=store`),出口脱敏器是模式脱敏、认不出「这是库账号 / 表名」。每句只说两件事:**坏在哪一类** + **重试有没有用**,不带任何实例化的名字。
15
16
  *
16
17
  * 🔴 **句子只断言码本身证明得了的事**:判官读到的只有一个码,而同一个码形可以出自库驱动,也可以出自别的系统调用(`ECONNREFUSED` 出自任何一个套接字、`ENOENT` 出自文件系统),
17
18
  * 所以 `connection` 说「一条连接」而不说「数据库的连接」;`unclassified` 只说「带了一个本服务不公布的码」。只有 `deployment` 的两类码(缺表、权限拒:`42P01` / `42501` /
@@ -22,9 +23,27 @@ export declare function storeFaultClassOf(err: unknown): StoreFaultClass;
22
23
  */
23
24
  export declare const STORE_FAULT_OUTWARD_SENTENCE: Readonly<Record<StoreFaultClass, string>>;
24
25
  /**
25
- * 一个**失败码**的对外句 —— 终局因由只带码(引擎把错压成 `code` + `message` 两格,类型与 `cause` 链已不在)的那些站点用:判官读 `{ code }`(识别表按 `code` 字符串登记,
26
- * 数字 `errno` 只是同一件事的另一种写法,这里读不到也不影响:每个登记行都有 `code`),交回的词按 {@link STORE_FAULT_OUTWARD_SENTENCE} 说一句。
27
- * 判官是整仓一只,这里不第二次认「什么算连接类 / 部署错配类」。
26
+ * 对外句的**唯一拼法**(纯函数):判官的词 → 本表那一句 + 原码尾注 `[original code: <码>]`(码缺席 ⇒ 只有类别句)。两种输入、一只拼法:
27
+ * · 店边界({@link StoreBoundaryFault}):词是边界在**原错**上判的那一个(带 `cause` 的包装、只带 errno 的错、无码整句 —— 只看码判不出的那几形都在原错上判得出);
28
+ * · 只剩码的站点(终局因由 / 投影 / 抛出臂:引擎已把错压成 `code` + `message` 两格,类型与 `cause` 链已不在,`terminal.ts` 的 `outwardStoreMessage`):词 = 判官读 `{ code }`。
29
+ * 两者对同一次店失败说同一句(边界句原样过出口,出口的幂等判 `isOutwardStoreSentence` 由本表派生)。句子里没有「持久层失败」之类的前缀:码 `internal.store_error` 已经在同一条因由上说了辖域,
30
+ * 而抛出臂(没有因由、没有码)上同一句不该替抛出物断言它的出身。
28
31
  */
29
- export declare function storeFaultSentenceOfCode(code: string): string;
32
+ export declare function outwardStoreSentence(cls: StoreFaultClass, code: string | undefined): string;
33
+ /** 判官的词(闭集,与 {@link STORE_FAULT_OUTWARD_SENTENCE} 的键同一份):出口幂等判逐词比对用。 */
34
+ export declare const STORE_FAULT_CLASSES: readonly StoreFaultClass[];
35
+ /**
36
+ * **店边界形** —— 一只 SQL 店的任何出口(抛出值 / 包装错的 `cause` / 失败文本 / 交出的读器与事务对象)上驱动错的**唯一**形:原码 + 稳定句,别无他物。
37
+ * 自有成员恰为 `name` · `message` · `stack`(新铸,第一行就是稳定句)· `faultClass` · `code`(有码时)· `errno`(原错链上带数字 errno 时)——
38
+ * **不带** `cause`、`sqlMessage`、`sql`(带插值后的语句全文)、`sqlState`、`fatal`、node-pg `DatabaseError` 的 `detail` / `hint` / `where` / `table` / `column` / `constraint` …、
39
+ * 套接字错的 `syscall` / `address` / `port`、原 stack:引擎在拒绝路径上对抛出值做全量自有键快照,同主体档不跑主机 / 路径脱敏,所以只能是新对象,不能「删几键」。
40
+ * 唯一铸点 = 边界函数 `terminal.ts` 的 `storeBoundaryFault`(它在铸之前把原句经出口座记一行运维日志);本类住在判官旁边,因为判官的首臂读它带的词(`terminal.ts` → 本文件已是一条边,反向成环)。
41
+ */
42
+ export declare class StoreBoundaryFault extends Error {
43
+ /** 判官在**原错**上读出的词(只算一次、随形带走)。 */
44
+ readonly faultClass: StoreFaultClass;
45
+ readonly code?: string;
46
+ readonly errno?: number;
47
+ constructor(faultClass: StoreFaultClass, code: string | undefined, errno: number | undefined);
48
+ }
30
49
  //# sourceMappingURL=store-fault.d.ts.map
@@ -7,6 +7,8 @@ export function isCodeDefect(err) {
7
7
  return RUNTIME_DEFECT_CLASSES.some((cls) => err instanceof cls) && !Object.hasOwn(err, "cause");
8
8
  }
9
9
  export function storeFaultClassOf(err) {
10
+ if (err instanceof StoreBoundaryFault)
11
+ return err.faultClass;
10
12
  if (err instanceof StoreCorruptError)
11
13
  return "corrupt";
12
14
  if (isConnectionFaultError(err))
@@ -22,7 +24,21 @@ export const STORE_FAULT_OUTWARD_SENTENCE = {
22
24
  defect: "the service failed on an internal error; retrying fails the same way until the service is fixed",
23
25
  unclassified: "the failure carried a code this service does not publish; the operator log carries the original sentence",
24
26
  };
25
- export function storeFaultSentenceOfCode(code) {
26
- return STORE_FAULT_OUTWARD_SENTENCE[storeFaultClassOf({ code })];
27
+ export function outwardStoreSentence(cls, code) {
28
+ const sentence = STORE_FAULT_OUTWARD_SENTENCE[cls];
29
+ return code === undefined ? sentence : `${sentence} [original code: ${code}]`;
30
+ }
31
+ export const STORE_FAULT_CLASSES = Object.keys(STORE_FAULT_OUTWARD_SENTENCE);
32
+ export class StoreBoundaryFault extends Error {
33
+ faultClass;
34
+ constructor(faultClass, code, errno) {
35
+ super(outwardStoreSentence(faultClass, code));
36
+ this.name = "StoreBoundaryFault";
37
+ this.faultClass = faultClass;
38
+ if (code !== undefined)
39
+ this.code = code;
40
+ if (errno !== undefined)
41
+ this.errno = errno;
42
+ }
27
43
  }
28
44
  //# sourceMappingURL=store-fault.js.map
@@ -6,6 +6,7 @@ import { mysqlDriver, pgDriver } from "./sql-driver.js";
6
6
  import { isDupKeyError, retryOnDeadlock } from "./sql-errors.js";
7
7
  import { countClientWriteOn } from "./session-withdraw-sql.js";
8
8
  import { readAtAddress } from "./blob-backend.js";
9
+ import { sealStoreClass } from "./store-boundary.js";
9
10
  export const TaskAttachmentRecordSchema = z.object({
10
11
  id: z.string(),
11
12
  owner: z.string(),
@@ -420,6 +421,10 @@ export class TiDBTaskAttachmentStore {
420
421
  return out;
421
422
  }
422
423
  }
424
+ sealStoreClass(TiDBTaskAttachmentStore, "tidb-task-attachment-store", {
425
+ put: "exit", get: "exit", getContent: "exit", releaseBytes: "exit", bind: "exit", listBySession: "exit", delete: "exit", deleteBySession: "exit",
426
+ reapUnbound: "exit", sweepOrphanObjects: "exit",
427
+ });
423
428
  export class PgTaskAttachmentStore {
424
429
  bytes;
425
430
  db;
@@ -536,4 +541,8 @@ export class PgTaskAttachmentStore {
536
541
  return out;
537
542
  }
538
543
  }
544
+ sealStoreClass(PgTaskAttachmentStore, "pg-task-attachment-store", {
545
+ put: "exit", get: "exit", getContent: "exit", releaseBytes: "exit", bind: "exit", listBySession: "exit", delete: "exit", deleteBySession: "exit",
546
+ reapUnbound: "exit", sweepOrphanObjects: "exit",
547
+ });
539
548
  //# sourceMappingURL=task-attachment-store.js.map
@@ -50,9 +50,9 @@ export declare function ensurePgTaskListSchema(q: (text: string, params?: unknow
50
50
  * 事务内先对 meta 行 `FOR UPDATE`(与 `mutate` 同一把分区锁)⇒ 与并发的清单写串行,不会删到
51
51
  * 一半被插回新行。meta 行不在(从未写过任何任务)= 无锁可拿,DELETE 命中 0 行,幂等。
52
52
  */
53
- export declare function deleteTiDBTaskList(pool: MySqlPool, listKey: string): Promise<void>;
53
+ export declare const deleteTiDBTaskList: (pool: MySqlPool, listKey: string) => Promise<void>;
54
54
  /** {@link deleteTiDBTaskList} 的 PG 孪生(同语义、同事务姿势)。 */
55
- export declare function deletePgTaskList(pool: PgPool, listKey: string): Promise<void>;
55
+ export declare const deletePgTaskList: (pool: PgPool, listKey: string) => Promise<void>;
56
56
  export declare function createTiDBTaskListStore(pool: MySqlPool, listKey: string): TaskListStore;
57
57
  export declare function createPgTaskListStore(pool: PgPool, listKey: string): TaskListStore;
58
58
  //# sourceMappingURL=task-list-store-sql.d.ts.map
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { assertJsonMetadata } from "@sema-agent/core";
3
3
  import { pgHasUnstorable, pgSafeJsonStringify, PgUnstorableError } from "./pg-safe-json.js";
4
4
  import { decodeJsonColumn, foldJsonColumn, jsonColumnText } from "./sql-json-column.js";
5
+ import { sealStoreFunction, sealStoreObject } from "./store-boundary.js";
5
6
  export const TASK_LIST_META_TABLE = "task_list_meta";
6
7
  export const TASK_LIST_ITEM_TABLE = "task_list_item";
7
8
  export function taskListKeyFor(sessionId) {
@@ -98,7 +99,7 @@ async function setOn(exec, dialect, listKey, id, itemJson) {
98
99
  ? `UPDATE ${TASK_LIST_META_TABLE} SET next_sort = ? WHERE list_key = ?`
99
100
  : `UPDATE ${TASK_LIST_META_TABLE} SET next_sort = $1 WHERE list_key = $2`, [sort + 1, listKey]);
100
101
  }
101
- export async function deleteTiDBTaskList(pool, listKey) {
102
+ export const deleteTiDBTaskList = sealStoreFunction(async function deleteTiDBTaskList(pool, listKey) {
102
103
  assertListKeyBytes(listKey);
103
104
  const c = await pool.getConnection();
104
105
  try {
@@ -115,8 +116,8 @@ export async function deleteTiDBTaskList(pool, listKey) {
115
116
  finally {
116
117
  c.release();
117
118
  }
118
- }
119
- export async function deletePgTaskList(pool, listKey) {
119
+ }, "sql-task-list-tidb.delete");
120
+ export const deletePgTaskList = sealStoreFunction(async function deletePgTaskList(pool, listKey) {
120
121
  if (pgHasUnstorable(listKey))
121
122
  throw new PgUnstorableError("task-list listKey");
122
123
  assertListKeyBytes(listKey);
@@ -135,7 +136,8 @@ export async function deletePgTaskList(pool, listKey) {
135
136
  finally {
136
137
  c.release();
137
138
  }
138
- }
139
+ }, "sql-task-list-pg.delete");
140
+ const TASK_LIST_EXIT_PLAN = { get: "exit", set: "exit", delete: "exit", list: "exit", allocateId: "exit", mutate: "exit" };
139
141
  export function createTiDBTaskListStore(pool, listKey) {
140
142
  assertListKeyBytes(listKey);
141
143
  const tx = async (fn) => {
@@ -158,7 +160,7 @@ export function createTiDBTaskListStore(pool, listKey) {
158
160
  const [rows] = (await q.query(sql, params));
159
161
  return rows;
160
162
  };
161
- const build = (q, inTx) => ({
163
+ const build = (q, inTx) => sealStoreObject({
162
164
  get: async (id) => {
163
165
  const [rows] = (await q.query(`SELECT ${jsonColumnText("mysql", "item_json")} FROM ${TASK_LIST_ITEM_TABLE} WHERE list_key = ? AND id = ?${inTx ? " FOR UPDATE" : ""}`, [listKey, id]));
164
166
  return rows.length > 0 ? parseItem(rows[0].item_json, `for list ${listKey} item ${JSON.stringify(id)}`) : undefined;
@@ -187,7 +189,7 @@ export function createTiDBTaskListStore(pool, listKey) {
187
189
  return fn(build(c, true));
188
190
  }),
189
191
  }),
190
- });
192
+ }, inTx ? "sql-task-list-tidb-tx" : "sql-task-list-tidb", TASK_LIST_EXIT_PLAN);
191
193
  return build(pool, false);
192
194
  }
193
195
  export function createPgTaskListStore(pool, listKey) {
@@ -214,7 +216,7 @@ export function createPgTaskListStore(pool, listKey) {
214
216
  const res = await q.query(sql, params);
215
217
  return res.rows;
216
218
  };
217
- const build = (q, inTx) => ({
219
+ const build = (q, inTx) => sealStoreObject({
218
220
  get: async (id) => {
219
221
  const { rows } = await q.query(`SELECT ${jsonColumnText("pg", "item_json")} FROM ${TASK_LIST_ITEM_TABLE} WHERE list_key = $1 AND id = $2${inTx ? " FOR UPDATE" : ""}`, [listKey, id]);
220
222
  return rows.length > 0 ? parseItem(rows[0].item_json, `for list ${listKey} item ${JSON.stringify(id)}`) : undefined;
@@ -250,7 +252,7 @@ export function createPgTaskListStore(pool, listKey) {
250
252
  return fn(build(c, true));
251
253
  }),
252
254
  }),
253
- });
255
+ }, inTx ? "sql-task-list-pg-tx" : "sql-task-list-pg", TASK_LIST_EXIT_PLAN);
254
256
  return build(pool, false);
255
257
  }
256
258
  //# sourceMappingURL=task-list-store-sql.js.map
@@ -1,5 +1,6 @@
1
1
  import { decideQuota } from "../observability/cost-quota.js";
2
2
  import { WriteBehindCounter } from "./write-behind-counter.js";
3
+ import { sealStoreClass } from "./store-boundary.js";
3
4
  export class TiDBCostQuota {
4
5
  counter;
5
6
  limitMicroUsd;
@@ -50,4 +51,7 @@ export class TiDBCostQuota {
50
51
  this.counter.stop();
51
52
  }
52
53
  }
54
+ sealStoreClass(TiDBCostQuota, "tidb-cost-quota", {
55
+ setLimits: "exit", check: "exit", add: "exit", flush: "exit", refresh: "exit", reap: "exit", startRefresh: "exit", stop: "exit",
56
+ });
53
57
  //# sourceMappingURL=tidb-cost-quota.js.map
@@ -8,7 +8,7 @@ import { TIDB_PERMISSION_RULE_STATEMENTS } from "./permission-rule-store-sql.js"
8
8
  import { TIDB_RETENTION_TOMBSTONE_STATEMENTS } from "../stores-sql/retention-store-sql.js";
9
9
  import { TIDB_RETENTION_LANE_STATEMENTS } from "./retention-lane-store-sql.js";
10
10
  import { SESSION_META_INCARNATION_COLUMN, SESSION_META_LINEAGE_COLUMNS } from "../session-incarnation.js";
11
- import { ensureSessionExtTableMysql } from "./session-ext-table.js";
11
+ import { SESSION_EXT_INDEXES, ensureSessionExtTableMysql } from "./session-ext-table.js";
12
12
  import { CHECKPOINT_HOST_SESSION_COLUMN } from "./checkpoint-index-columns.js";
13
13
  import { RUN_IDENTITY_COLUMNS } from "../identity-source.js";
14
14
  import { SESSION_RULES_COLUMN } from "./session-rules-column.js";
@@ -843,6 +843,7 @@ export async function releaseEnsureSchemaLock(conn, tag = "ensureSchema") {
843
843
  export const SCHEMA_INDEXES = [
844
844
  ...CENTRAL_SCHEMA_INDEXES,
845
845
  ...SESSION_INDEXES,
846
+ ...SESSION_EXT_INDEXES,
846
847
  ...TOOL_RESULT_INDEXES,
847
848
  ...COST_QUOTA_INDEXES,
848
849
  ...RATE_LIMIT_INDEXES,
@@ -1,4 +1,5 @@
1
1
  import { WriteBehindCounter } from "./write-behind-counter.js";
2
+ import { sealStoreClass } from "./store-boundary.js";
2
3
  export class TiDBRateLimiter {
3
4
  counter;
4
5
  limit;
@@ -48,4 +49,7 @@ export class TiDBRateLimiter {
48
49
  this.counter.stop();
49
50
  }
50
51
  }
52
+ sealStoreClass(TiDBRateLimiter, "tidb-rate-limiter", {
53
+ setLimits: "exit", check: "exit", sweep: "exit", flush: "exit", refresh: "exit", startRefresh: "exit", stop: "exit",
54
+ });
51
55
  //# sourceMappingURL=tidb-rate-limiter.js.map
@@ -2,6 +2,7 @@ import { recordFailOpen } from "../observability/fail-open.js";
2
2
  import { isDupKeyError } from "./sql-errors.js";
3
3
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
4
4
  import { assertPageLimit, CRASHED_CAUSE, SETTLE_HOW_WORDS, USER_INPUT_KIND_WORDS, USER_INPUT_OPEN_SCAN_MAX, USER_INPUT_SESSION_READ_MAX, } from "../user-input-ledger.js";
5
+ import { sealStoreClass } from "./store-boundary.js";
5
6
  export const USER_INPUT_ACCEPTANCE_TABLE = "user_input_acceptance";
6
7
  const COLUMNS = "accept_id, input_kind, message_id, session_id, task_id, writer, accepted_at_ms, state, state_at_ms, settle_how, announced_at_ms, announced_task_id";
7
8
  function rowState(r) {
@@ -116,6 +117,9 @@ export class SqlUserInputLedgerStore {
116
117
  return res.affected;
117
118
  }
118
119
  }
120
+ sealStoreClass(SqlUserInputLedgerStore, "sql-user-input-ledger-store", {
121
+ accept: "exit", settle: "exit", converge: "exit", announce: "exit", listOpen: "exit", listCrashed: "exit", listUnannounced: "exit", deleteBySession: "exit",
122
+ });
119
123
  export class TiDBUserInputLedgerStore extends SqlUserInputLedgerStore {
120
124
  constructor(pool) {
121
125
  super(mysqlDriver(pool));
@@ -60,9 +60,9 @@ import { summarizeWorkflowRun, type CheckpointToken, type WorkflowRunStore } fro
60
60
  import { type WorkflowCompletionInbox, type WorkflowCompletionInboxEntry } from "../orchestration/workflow-completion-inbox.js";
61
61
  import type { WorkflowNotifyJournalStore, WorkflowNotifyJournalEntry } from "../orchestration/workflow-notify-journal.js";
62
62
  import { type WorkflowAgentSessionIndex, type WorkflowAgentSessionRow } from "../orchestration/workflow-agent-session-index.js";
63
- import { type WorkflowParkDecision, type WorkflowParkDecisionStore } from "../orchestration/workflow-park-decision.js";
63
+ import { type HolderFenceVerdict, type RecordFenced, type WorkflowParkDecision, type WorkflowParkDecisionStore } from "../orchestration/workflow-park-decision.js";
64
64
  import type { SqlRunStore } from "./run-store-sql.js";
65
- import { type SqlDialect, type SqlDriver, type SqlExec } from "./sql-driver.js";
65
+ import { type SqlDialect, type SqlDriver, type SqlExec, type SqlTxConn } from "./sql-driver.js";
66
66
  type WorkflowRun = Parameters<WorkflowRunStore["put"]>[1];
67
67
  /** Row cap guard: a run blob beyond this is NOT stored on update (the prior revision stays) — TiDB's
68
68
  * ~6 MiB txn-entry limit would otherwise throw the (best-effort) progress write into the workflow loop.
@@ -126,6 +126,14 @@ export declare class SqlWorkflowRunStore implements WorkflowRunStore {
126
126
  private q;
127
127
  put(id: string, run: WorkflowRun): Promise<void>;
128
128
  get(id: string): Promise<WorkflowRun | null>;
129
+ /**
130
+ * **记录栅栏(SQL 形)**:一个事务 —— 锁读这条 run 的记录行且它读作 `running`(`workflow_run … FOR UPDATE`,行体经本店唯一列读者 {@link readRunColumn} 解、
131
+ * `rev` 列覆盖),读到 ⇒ `fn` 在**同一个事务连接**上跑(收锁住的那份记录);没读到(终态 / 不在)⇒ `fn` 不调、答 `{ running: false }`。提交即放锁;`fn` 抛或提交失败 ⇒ 整只抛。
132
+ * 引擎的记录写是同一行的 CAS `UPDATE`({@link update}),notify 门的收养翻转同一条 ⇒ 与本段在这一行锁上串行:本段先拿锁 ⇒ 段里落的写先于终态提交;
133
+ * 终态先提交 ⇒ 本段读不到 `running`。决定行的写就落在这里(契约属主 = `orchestration/workflow-park-decision.ts` 文件头)。
134
+ * 取锁序 = `workflow_run` → `workflow_park_decision`(段里只碰这两张表;认领栅栏那一形先锁 `task_run` / `task_active` 再碰 `workflow_park_decision`,两序不成环)。
135
+ */
136
+ whileRunning<T>(id: string, fn: (tx: SqlTxConn, run: WorkflowRun) => Promise<T>): Promise<RecordFenced<T>>;
129
137
  update(id: string, scope: string, run: WorkflowRun, expect?: {
130
138
  rev: number;
131
139
  }): Promise<boolean>;
@@ -212,24 +220,34 @@ export declare class SqlWorkflowAgentSessionIndex implements WorkflowAgentSessio
212
220
  /**
213
221
  * workflow 子代停驻卡的耐久决定行 —— SQL 双生(契约与律的唯一属主 =
214
222
  * {@link import("../orchestration/workflow-park-decision.js").WorkflowParkDecisionStore} 的文件头,这里只记方言差)。
215
- * 写者只有 `/decide` 的 workflow 车道(开跑之前那一笔);删除只有 run 收割的级联({@link SqlWorkflowRunStore.reap})。
216
- *
217
- * 构造时绑在 SQL run 店上(`claims`):写在那只 run 店的认领栅栏事务里(`SqlRunStore.whileHoldingClaim` 交出的那条事务连接 —— 栅栏攥着
218
- * run 行与认领行的锁直到提交),读走那只 run 店的池(`claims.db`)—— 决定行、认领、读写同一只池,池只有这一处来源。
223
+ * 写在两只栅栏之一的事务里:记录栅栏(`records`:{@link SqlWorkflowRunStore.whileRunning} 交出的那条事务连接 —— 栅栏攥着 `workflow_run` 行锁直到提交)
224
+ * 或认领栅栏(`claims`:`SqlRunStore.whileHoldingClaim` 交出的那条事务连接 —— 攥着 run 行与认领行的锁直到提交);读走 run 店的池(`claims.db`)。
225
+ * 两只栅栏、决定行、读写同一只池(后端交出的三只店同出一只池)。删除只有 run 收割的级联({@link SqlWorkflowRunStore.reap})。
219
226
  *
220
227
  * ── 方言差,保持显式 ──────────────────────────────────────────────────────────────────────────
221
228
  * - `?` 占位 vs `$n`。
222
- * - 整行 upsert(主键 `token`):TiDB `INSERT … ON DUPLICATE KEY UPDATE col = VALUES(col)` vs PG `INSERT … ON CONFLICT (token) DO UPDATE SET col = EXCLUDED.col`。
223
- * 无条件:序由认领栅栏定(同一宿主会话上的受理由认领串行,栅栏事务的提交序就是受理序),`accepted_at_ms` 只是记下的值。
229
+ * - 认领栅栏下的整行 upsert(主键 `token`):TiDB `INSERT … ON DUPLICATE KEY UPDATE col = VALUES(col)` vs PG `INSERT … ON CONFLICT (token) DO UPDATE SET col = EXCLUDED.col`。
230
+ * 序由认领栅栏定,`accepted_at_ms` 只是记下的值。
231
+ * - 记录栅栏下的「没有才插」(先受理者立):TiDB `INSERT … ON DUPLICATE KEY UPDATE token = token` vs PG `INSERT … ON CONFLICT (token) DO NOTHING`;
232
+ * 之后**锁定读**行上的指纹(`SELECT … FOR UPDATE`)判立没立 —— TiDB 悲观事务里的普通读走事务起点的快照,一只等锁等过对方提交的事务用普通读会读不到
233
+ * 对方刚落的行;锁定读读最新提交。PG READ COMMITTED 每句新快照,锁定读同样成立。收场转交的条件读同此(认领栅栏里锁定读指纹再写)。
224
234
  * - `outcome` 是 JSON 承载的文本列(TiDB MEDIUMTEXT / PG TEXT,与 `workflow_run.run` 同形:两方言同一条 `JSON.stringify` 绑定),
225
235
  * 读腿按文本投影、恰好解一次({@link jsonColumnText} / {@link decodeJsonColumn})。
226
236
  * - schema 属主同店内其余表:TiDB DDL 在 tidb-pool.ts,PG DDL 在 pg-pool.ts(店不建表)。
227
237
  */
228
238
  export declare class SqlWorkflowParkDecisionStore implements WorkflowParkDecisionStore {
229
239
  readonly claims: SqlRunStore;
230
- constructor(claims: SqlRunStore);
240
+ readonly records: SqlWorkflowRunStore;
241
+ constructor(claims: SqlRunStore, records: SqlWorkflowRunStore);
231
242
  private q;
232
- record(d: WorkflowParkDecision): Promise<boolean>;
243
+ /** 事务里锁定读这张卡行上的指纹(没有行 ⇒ `undefined`)。 */
244
+ private lockedFingerprint;
245
+ record(d: WorkflowParkDecision, condition?: {
246
+ readonly expect: string;
247
+ }): Promise<boolean>;
248
+ recordForRunningHolder(d: WorkflowParkDecision, request: {
249
+ readonly restated: boolean;
250
+ }): Promise<HolderFenceVerdict>;
233
251
  get(token: CheckpointToken): Promise<WorkflowParkDecision | null>;
234
252
  listByHostSession(hostSessionId: string): Promise<WorkflowParkDecision[]>;
235
253
  }
@@ -2,10 +2,12 @@ import { redactErrorMessage, redactHead } from "../observability/run-terminal.js
2
2
  import { summarizeWorkflowRun, isTerminalWorkflowStatus, WorkflowRunStoreError } from "@sema-agent/core";
3
3
  import { foldKeyFamily, MAX_PENDING_PER_SESSION, PURGE_FENCE_MS, SERVED_FENCE_MS, } from "../orchestration/workflow-completion-inbox.js";
4
4
  import {} from "../orchestration/workflow-agent-session-index.js";
5
- import { readWorkflowParkOutcome } from "../orchestration/workflow-park-decision.js";
5
+ import { holderFenceAdmits, readWorkflowParkOutcome } from "../orchestration/workflow-park-decision.js";
6
6
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
7
7
  import { createObjectColumnReader, decodeJsonColumn, foldJsonColumn, jsonColumnText } from "./sql-json-column.js";
8
8
  import { isDupKeyError } from "./sql-errors.js";
9
+ import { storeBoundaryFault } from "../terminal.js";
10
+ import { sealStoreClass } from "./store-boundary.js";
9
11
  export const MAX_RUN_BLOB_BYTES = 4 * 1024 * 1024;
10
12
  const SLIM_HEADER_MAX = 200;
11
13
  const onWarnType = (msg, meta) => void [msg, meta];
@@ -81,6 +83,37 @@ export class SqlWorkflowRunStore {
81
83
  run.rev = Number(r.rev);
82
84
  return run;
83
85
  }
86
+ async whileRunning(id, fn) {
87
+ const conn = await this.db.connect();
88
+ try {
89
+ await conn.begin();
90
+ try {
91
+ const { rows } = await conn.query(this.q(`SELECT ${jsonColumnText("mysql", "run")}, rev FROM workflow_run WHERE id = ? AND status = 'running' FOR UPDATE`, `SELECT ${jsonColumnText("pg", "run")}, rev FROM workflow_run WHERE id = $1 AND status = 'running' FOR UPDATE`), [id]);
92
+ const r = rows[0];
93
+ let fenced = { running: false };
94
+ if (r !== undefined) {
95
+ const run = readRunColumn(r.run, id);
96
+ run.rev = Number(r.rev);
97
+ fenced = { running: true, value: await fn(conn, run) };
98
+ }
99
+ await conn.commit();
100
+ return fenced;
101
+ }
102
+ catch (err) {
103
+ try {
104
+ await conn.rollback();
105
+ }
106
+ catch (rollbackErr) {
107
+ const rollbackFault = storeBoundaryFault(rollbackErr, "sql-workflow-run-store.whileRunning");
108
+ throw new Error(`workflow record fence on ${id}: the segment failed and its rollback failed too (${rollbackFault instanceof Error ? rollbackFault.message : String(rollbackFault)})`, { cause: err });
109
+ }
110
+ throw err;
111
+ }
112
+ }
113
+ finally {
114
+ conn.release();
115
+ }
116
+ }
84
117
  async update(id, scope, run, expect) {
85
118
  run = withRedactedError(run);
86
119
  let blob = JSON.stringify({ ...run, id, scope });
@@ -166,6 +199,7 @@ export class SqlWorkflowRunStore {
166
199
  return res.affected;
167
200
  }
168
201
  }
202
+ sealStoreClass(SqlWorkflowRunStore, "sql-workflow-run-store", { put: "exit", get: "exit", update: "exit", whileRunning: "exit", listByScope: "exit", reapAllScopes: "exit", reap: "exit" });
169
203
  const KIND_SERVED = "served";
170
204
  const KIND_PURGE = "purge";
171
205
  export async function writeInboxPurgeFenceOn(exec, dialect, sessionId, purgedOwner, now) {
@@ -264,6 +298,7 @@ export class SqlWorkflowCompletionInbox {
264
298
  await this.db.query(this.q("DELETE FROM workflow_completion_inbox WHERE session_id = ? AND run_id <> ? AND REGEXP_REPLACE(run_id, '#[0-9]+$', '') = ?", "DELETE FROM workflow_completion_inbox WHERE session_id = $1 AND run_id <> $2 AND regexp_replace(run_id, '#[0-9]+$', '') = $3"), [sessionId, runId, runId]);
265
299
  }
266
300
  }
301
+ sealStoreClass(SqlWorkflowCompletionInbox, "sql-workflow-completion-inbox", { enqueue: "exit", pending: "exit", ack: "exit", purge: "exit", markTerminalServed: "exit" });
267
302
  export class SqlWorkflowNotifyJournalStore {
268
303
  db;
269
304
  constructor(db) {
@@ -309,6 +344,7 @@ export class SqlWorkflowNotifyJournalStore {
309
344
  };
310
345
  }
311
346
  }
347
+ sealStoreClass(SqlWorkflowNotifyJournalStore, "sql-workflow-notify-journal-store", { record: "exit", ack: "exit", listPending: "exit", reapAcked: "exit", get: "exit" });
312
348
  export class SqlWorkflowAgentSessionIndex {
313
349
  db;
314
350
  constructor(db) {
@@ -328,6 +364,7 @@ export class SqlWorkflowAgentSessionIndex {
328
364
  return rows.map((r) => ({ sessionId: String(r.session_id), runId: String(r.run_id), agentKey: String(r.agent_key), scope: String(r.scope) }));
329
365
  }
330
366
  }
367
+ sealStoreClass(SqlWorkflowAgentSessionIndex, "sql-workflow-agent-session-index", { ensurePointers: "exit", listByAgentSession: "exit" });
331
368
  function readParkDecisionOutcomeColumn(v, where) {
332
369
  try {
333
370
  return decodeJsonColumn(v, readWorkflowParkOutcome, `workflow_park_decision.outcome ${where}`);
@@ -338,19 +375,41 @@ function readParkDecisionOutcomeColumn(v, where) {
338
375
  }
339
376
  export class SqlWorkflowParkDecisionStore {
340
377
  claims;
341
- constructor(claims) {
378
+ records;
379
+ constructor(claims, records) {
342
380
  this.claims = claims;
381
+ this.records = records;
343
382
  }
344
383
  q(mysql, pg) {
345
384
  return this.claims.db.dialect === "mysql" ? mysql : pg;
346
385
  }
347
- async record(d) {
386
+ async lockedFingerprint(tx, token) {
387
+ const { rows } = await tx.query(this.q("SELECT fingerprint FROM workflow_park_decision WHERE token = ? FOR UPDATE", "SELECT fingerprint FROM workflow_park_decision WHERE token = $1 FOR UPDATE"), [token]);
388
+ const r = rows[0];
389
+ return r === undefined ? undefined : String(r.fingerprint);
390
+ }
391
+ async record(d, condition) {
348
392
  const fenced = await this.claims.whileHoldingClaim(d.hostSessionId, d.deliveredTaskId, async (tx) => {
393
+ if (condition !== undefined && (await this.lockedFingerprint(tx, d.token)) !== condition.expect)
394
+ return false;
349
395
  await tx.query(this.q(`INSERT INTO workflow_park_decision (token, run_id, host_session_id, outcome, fingerprint, delivered_task_id, accepted_at_ms) VALUES (?,?,?,?,?,?,?)
350
396
  ON DUPLICATE KEY UPDATE ${PARK_DECISION_REPLACED_COLUMNS.map((c) => `${c} = VALUES(${c})`).join(", ")}`, `INSERT INTO workflow_park_decision (token, run_id, host_session_id, outcome, fingerprint, delivered_task_id, accepted_at_ms) VALUES ($1,$2,$3,$4,$5,$6,$7)
351
397
  ON CONFLICT (token) DO UPDATE SET ${PARK_DECISION_REPLACED_COLUMNS.map((c) => `${c} = EXCLUDED.${c}`).join(", ")}`), [d.token, d.runId, d.hostSessionId, JSON.stringify(d.outcome), d.fingerprint, d.deliveredTaskId, d.acceptedAtMs]);
398
+ return true;
399
+ });
400
+ return fenced.held && fenced.value;
401
+ }
402
+ async recordForRunningHolder(d, request) {
403
+ const fenced = await this.records.whileRunning(d.runId, async (tx, run) => {
404
+ const admits = holderFenceAdmits(run, d, request.restated);
405
+ if (admits !== "land")
406
+ return admits;
407
+ await tx.query(this.q(`INSERT INTO workflow_park_decision (token, run_id, host_session_id, outcome, fingerprint, delivered_task_id, accepted_at_ms) VALUES (?,?,?,?,?,?,?)
408
+ ON DUPLICATE KEY UPDATE token = token`, `INSERT INTO workflow_park_decision (token, run_id, host_session_id, outcome, fingerprint, delivered_task_id, accepted_at_ms) VALUES ($1,$2,$3,$4,$5,$6,$7)
409
+ ON CONFLICT (token) DO NOTHING`), [d.token, d.runId, d.hostSessionId, JSON.stringify(d.outcome), d.fingerprint, d.deliveredTaskId, d.acceptedAtMs]);
410
+ return (await this.lockedFingerprint(tx, d.token)) === d.fingerprint ? "stands" : "other_decision";
352
411
  });
353
- return fenced.held;
412
+ return fenced.running ? fenced.value : "not_running";
354
413
  }
355
414
  async get(token) {
356
415
  const { rows } = await this.claims.db.query(this.q(`SELECT token, run_id, host_session_id, ${jsonColumnText("mysql", "outcome")}, fingerprint, delivered_task_id, accepted_at_ms FROM workflow_park_decision WHERE token = ?`, `SELECT token, run_id, host_session_id, ${jsonColumnText("pg", "outcome")}, fingerprint, delivered_task_id, accepted_at_ms FROM workflow_park_decision WHERE token = $1`), [token]);
@@ -362,6 +421,7 @@ export class SqlWorkflowParkDecisionStore {
362
421
  return rows.map(rowToParkDecision);
363
422
  }
364
423
  }
424
+ sealStoreClass(SqlWorkflowParkDecisionStore, "sql-workflow-park-decision-store", { record: "exit", recordForRunningHolder: "exit", get: "exit", listByHostSession: "exit" });
365
425
  const PARK_DECISION_REPLACED_COLUMNS = ["run_id", "host_session_id", "outcome", "fingerprint", "delivered_task_id", "accepted_at_ms"];
366
426
  function rowToParkDecision(r) {
367
427
  const token = String(r.token);
@@ -1,4 +1,5 @@
1
- import { RemoteExecutionError, type ExecutionEnvFactoryContext } from "@sema-agent/core";
1
+ import { type ExecutionEnvFactoryContext } from "@sema-agent/core";
2
+ import { type ConnectRefusal } from "./remote-env-connect-seat.js";
2
3
  import type { Logger } from "../observability/logger.js";
3
4
  import { type TaskAttachmentStore } from "./task-attachment-store.js";
4
5
  /** 车道一次固定命令的读数(适配器自己的起动身份 —— 与引擎命令同一个执行用户;从不抛)。 */
@@ -29,14 +30,10 @@ export interface WorkspaceChannel {
29
30
  }
30
31
  /** 恢复点交出的工作区从哪来:`minted` = 车道刚铸出的新工作区(没有可恢复的树 —— 含恢复点指的归档不在:`latest` 悬空、停驻快照的归档不在);`restored` = 恢复出来的(空间 `latest` / 停驻快照的树真回来了)。 */
31
32
  export type WorkspaceOrigin = "minted" | "restored";
32
- /** 一次填充的结局。`standing` = 这只执行环境此后每一次恢复点都会得到同一个答(适配器把它记成这只环境的拒,不再建机器)。 */
33
+ /** 一次填充的结局。拒的那一臂是一只建连的拒:`standing` = 这只执行环境此后每一次恢复点都会得到同一个答(适配器的建连座记住它,不再建机器)。 */
33
34
  export type FillOutcome = {
34
35
  readonly ok: true;
35
- } | {
36
- readonly ok: false;
37
- readonly error: RemoteExecutionError;
38
- readonly standing: boolean;
39
- };
36
+ } | ConnectRefusal;
40
37
  /** 恢复点的填充座(按 run 由工厂装配交给适配器;适配器只在恢复点调它、只说工作区从哪来,不判内容)。 */
41
38
  export interface WorkspaceFill {
42
39
  /** 往恢复点即将交出的工作区(根 = `root`,适配器的工作根;来处 = `origin`)里放内容。 */
@@ -1,12 +1,13 @@
1
1
  import path from "node:path";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { RemoteExecutionError } from "@sema-agent/core";
4
+ import { connectRefusal } from "./remote-env-connect-seat.js";
4
5
  import { readProjectBundle } from "../project-bundle.js";
5
6
  import { attachmentBytesOf, attachmentPlacementsOf, seedOfSession } from "./task-attachment-store.js";
6
7
  import { shellQuote } from "./remote-shell.js";
7
8
  const FILLED = { ok: true };
8
9
  function refused(message, standing = false) {
9
- return { ok: false, error: new RemoteExecutionError("connect_failed", message), standing };
10
+ return standing ? connectRefusal("workspace_fill_refused", message) : { ok: false, error: new RemoteExecutionError("connect_failed", message), standing: false };
10
11
  }
11
12
  function errText(e) {
12
13
  return e instanceof Error ? e.message : String(e);
@@ -1,4 +1,5 @@
1
1
  import { alignedWindow, retryAfterSecUntil } from "../observability/cost-quota.js";
2
+ import { sealStoreClass } from "./store-boundary.js";
2
3
  const SEP = "\u0000";
3
4
  export const DEFAULT_COUNTER_QUERY_TIMEOUT_MS = 30_000;
4
5
  export class SqlWriteBehindCounter {
@@ -173,6 +174,10 @@ export class SqlWriteBehindCounter {
173
174
  this.windowMs = windowMs;
174
175
  }
175
176
  }
177
+ sealStoreClass(SqlWriteBehindCounter, "sql-write-behind-counter", {
178
+ window: "exit", bucket: "exit", usedIn: "exit", used: "exit", addLocal: "exit", retryAfterSec: "exit", flush: "exit", refresh: "exit", reap: "exit",
179
+ startRefresh: "exit", stop: "exit", refreshRunning: "exit", setWindowMs: "exit",
180
+ });
176
181
  export class WriteBehindCounter extends SqlWriteBehindCounter {
177
182
  constructor(pool, t, windowMs, now, onDegraded, queryTimeoutMs) {
178
183
  super("mysql", pool, t, windowMs, now, onDegraded, queryTimeoutMs);
@@ -37,8 +37,6 @@ export interface ProjectContextDecision {
37
37
  readonly context: ProjectContext | undefined;
38
38
  readonly withheldPathItems: number;
39
39
  }
40
- /** 一只登记对这位观者可见吗(读面与继承同调的唯一判):全舰队视域看全部;否则只看 `registeredBy` 恰是自己的。看不见与不存在同答。 */
41
- export declare function projectVisible(registration: Pick<ProjectRegistration, "registeredBy">, viewer: ListFilter): boolean;
42
40
  /**
43
41
  * 项目简报的唯一铸点(纯串;各段缺席不出,全缺席 ⇒ 无简报)。只读 {@link PROJECT_BRIEF_FIELDS};路径两段只在 `pathsHonored` 时出,且只列名字 ——
44
42
  * `file` 条目未附上,段名写明「相关时去读」。首行点名来源与项目 id(不带 `displayName`:身份字段不进提示)。
@@ -1,11 +1,9 @@
1
1
  import { PROJECT_ID_REGEX } from "@sema-agent/core";
2
2
  import { isAbsoluteHostPath } from "@sema-agent/settings-schema";
3
+ import { visibleProjectOf } from "./project-registry.js";
3
4
  import { MAX_SETTINGS_OUTPUT_STYLE_CHARS } from "./task-settings.js";
4
5
  export const PROJECT_BRIEF_FIELDS = ["goal", "notes", "contextItems"];
5
6
  export const PROJECT_CONTENT_ATTESTATION_FIELD = "contentAttestedAt";
6
- export function projectVisible(registration, viewer) {
7
- return viewer.fleetWide || registration.registeredBy === viewer.gateOwner;
8
- }
9
7
  export function buildProjectBrief(projectId, registration, pathsHonored) {
10
8
  const sections = [];
11
9
  if (registration.goal !== undefined && registration.goal.length > 0)
@@ -29,11 +27,9 @@ export function buildProjectBrief(projectId, registration, pathsHonored) {
29
27
  return [`# Project context (from this deployment's project registry; project ${projectId})`, ...sections].join("\n\n");
30
28
  }
31
29
  export function projectContextOf(input) {
32
- const { projectId, registry, viewer } = input;
33
- if (projectId === undefined || registry === undefined || viewer === undefined)
34
- return { context: undefined, withheldPathItems: 0 };
35
- const registration = Object.hasOwn(registry, projectId) ? registry[projectId] : undefined;
36
- if (registration === undefined || !projectVisible(registration, viewer))
30
+ const { projectId } = input;
31
+ const registration = visibleProjectOf(input.registry, projectId, input.viewer);
32
+ if (projectId === undefined || registration === undefined)
37
33
  return { context: undefined, withheldPathItems: 0 };
38
34
  if (registration[PROJECT_CONTENT_ATTESTATION_FIELD] === undefined)
39
35
  return { context: undefined, withheldPathItems: 0 };