@sema-agent/server 1.323.0 → 2.0.1

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 (565) hide show
  1. package/dist/approval-hmac.d.ts +40 -0
  2. package/dist/approval-hmac.js +67 -0
  3. package/dist/approval.d.ts +31 -0
  4. package/dist/approval.js +72 -4
  5. package/dist/audit.d.ts +26 -0
  6. package/dist/audit.js +40 -1
  7. package/dist/auth-bridge.d.ts +10 -0
  8. package/dist/auth-bridge.js +47 -4
  9. package/dist/auth-keys.d.ts +19 -0
  10. package/dist/auth-keys.js +13 -0
  11. package/dist/bake-runner/main.d.ts +5 -0
  12. package/dist/bake-runner/main.js +43 -4
  13. package/dist/bake-runner/protocol.d.ts +57 -0
  14. package/dist/bake-runner/protocol.js +58 -3
  15. package/dist/bake-runner/runner.d.ts +54 -0
  16. package/dist/bake-runner/runner.js +87 -5
  17. package/dist/bench/l8/artifact.d.ts +63 -0
  18. package/dist/bench/l8/artifact.js +61 -2
  19. package/dist/bench/l8/escape.d.ts +36 -0
  20. package/dist/bench/l8/escape.js +25 -0
  21. package/dist/bench/l8/index.d.ts +14 -0
  22. package/dist/bench/l8/index.js +14 -0
  23. package/dist/bench/l8/probes.d.ts +74 -0
  24. package/dist/bench/l8/probes.js +60 -0
  25. package/dist/bench/l8/run-probes.d.ts +43 -0
  26. package/dist/bench/l8/run-probes.js +45 -2
  27. package/dist/bench/s1/arms.d.ts +126 -0
  28. package/dist/bench/s1/arms.js +188 -13
  29. package/dist/bench/s1/live-deps.d.ts +66 -0
  30. package/dist/bench/s1/live-deps.js +249 -11
  31. package/dist/bench/s1/oracle.d.ts +49 -0
  32. package/dist/bench/s1/oracle.js +36 -1
  33. package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
  34. package/dist/bench/s1/repair-oracle-adapter.js +30 -0
  35. package/dist/bench/s1/reviewer.d.ts +38 -0
  36. package/dist/bench/s1/reviewer.js +34 -0
  37. package/dist/bench/s1/row.d.ts +77 -0
  38. package/dist/bench/s1/row.js +62 -0
  39. package/dist/bench/s1/run-firm.d.ts +47 -0
  40. package/dist/bench/s1/run-firm.js +70 -4
  41. package/dist/bench/s1/runner-ctx.d.ts +71 -0
  42. package/dist/bench/s1/runner-ctx.js +36 -0
  43. package/dist/bench/s1/tasks.d.ts +108 -0
  44. package/dist/bench/s1/tasks.js +115 -0
  45. package/dist/boot/budget-tracing.d.ts +48 -0
  46. package/dist/boot/budget-tracing.js +86 -0
  47. package/dist/boot/config-center.d.ts +62 -0
  48. package/dist/boot/config-center.js +995 -0
  49. package/dist/boot/coordinators.d.ts +33 -0
  50. package/dist/boot/coordinators.js +97 -0
  51. package/dist/boot/execution-env.d.ts +26 -0
  52. package/dist/boot/execution-env.js +370 -0
  53. package/dist/boot/leader.d.ts +27 -0
  54. package/dist/boot/leader.js +81 -0
  55. package/dist/boot/reapers.d.ts +53 -0
  56. package/dist/boot/reapers.js +252 -0
  57. package/dist/boot/resolve-spec.d.ts +70 -0
  58. package/dist/boot/resolve-spec.js +1072 -0
  59. package/dist/boot/runner-deps.d.ts +101 -0
  60. package/dist/boot/runner-deps.js +343 -0
  61. package/dist/boot/runtime-caps.d.ts +21 -0
  62. package/dist/boot/runtime-caps.js +62 -0
  63. package/dist/boot/session-faces.d.ts +57 -0
  64. package/dist/boot/session-faces.js +157 -0
  65. package/dist/boot/shutdown.d.ts +50 -0
  66. package/dist/boot/shutdown.js +129 -0
  67. package/dist/boot/stores.d.ts +32 -0
  68. package/dist/boot/stores.js +361 -0
  69. package/dist/boot/workflow-orchestration.d.ts +46 -0
  70. package/dist/boot/workflow-orchestration.js +150 -0
  71. package/dist/boot-reclaim.d.ts +19 -0
  72. package/dist/boot-reclaim.js +20 -0
  73. package/dist/brain.d.ts +25 -0
  74. package/dist/brain.js +61 -1
  75. package/dist/budget.d.ts +66 -0
  76. package/dist/budget.js +128 -4
  77. package/dist/capabilities/builtin-tools.d.ts +3 -0
  78. package/dist/capabilities/builtin-tools.js +3 -0
  79. package/dist/capabilities/center-plugins.d.ts +17 -1
  80. package/dist/capabilities/center-plugins.js +45 -4
  81. package/dist/capabilities/center-prompts.d.ts +46 -0
  82. package/dist/capabilities/center-prompts.js +57 -3
  83. package/dist/capabilities/code-review-council.d.ts +28 -0
  84. package/dist/capabilities/code-review-council.js +40 -7
  85. package/dist/capabilities/collab-wire.d.ts +2 -0
  86. package/dist/capabilities/collab-workflows.d.ts +7 -0
  87. package/dist/capabilities/collab-workflows.js +44 -1
  88. package/dist/capabilities/oa-tools.d.ts +6 -0
  89. package/dist/capabilities/oa-tools.js +10 -0
  90. package/dist/capabilities/prompt.d.ts +14 -0
  91. package/dist/capabilities/prompt.js +14 -0
  92. package/dist/capabilities/prompts/code-review.d.ts +14 -0
  93. package/dist/capabilities/prompts/code-review.js +14 -0
  94. package/dist/capabilities/prompts/identity.d.ts +6 -0
  95. package/dist/capabilities/prompts/identity.js +6 -0
  96. package/dist/capabilities/prompts/team.d.ts +4 -0
  97. package/dist/capabilities/prompts/team.js +4 -0
  98. package/dist/capabilities/repo-tools.d.ts +11 -0
  99. package/dist/capabilities/repo-tools.js +8 -0
  100. package/dist/capabilities/sandbox-file-send.d.ts +95 -0
  101. package/dist/capabilities/sandbox-file-send.js +91 -3
  102. package/dist/capabilities/scenarios.d.ts +130 -0
  103. package/dist/capabilities/scenarios.js +180 -7
  104. package/dist/capabilities/select-environment-tool.d.ts +7 -0
  105. package/dist/capabilities/select-environment-tool.js +30 -0
  106. package/dist/capabilities/send-user-file-tool.d.ts +33 -0
  107. package/dist/capabilities/send-user-file-tool.js +65 -1
  108. package/dist/capabilities/skills.d.ts +29 -0
  109. package/dist/capabilities/skills.js +13 -2
  110. package/dist/capabilities/team.d.ts +22 -0
  111. package/dist/capabilities/team.js +22 -2
  112. package/dist/capabilities/tool-defer.d.ts +26 -0
  113. package/dist/capabilities/tool-defer.js +3 -0
  114. package/dist/config-center/apply-effective.d.ts +65 -0
  115. package/dist/config-center/apply-effective.js +204 -12
  116. package/dist/config-center/http-client.d.ts +34 -0
  117. package/dist/config-center/http-client.js +68 -0
  118. package/dist/config-center/restart-signal.d.ts +22 -0
  119. package/dist/config-center/restart-signal.js +39 -0
  120. package/dist/config-center/skills-mcp.d.ts +20 -1
  121. package/dist/config-center/skills-mcp.js +42 -7
  122. package/dist/config-center/types.d.ts +92 -1
  123. package/dist/config-lkg.d.ts +12 -0
  124. package/dist/config-lkg.js +41 -0
  125. package/dist/config-provider.d.ts +78 -0
  126. package/dist/config-provider.js +165 -2
  127. package/dist/config-types.d.ts +585 -1
  128. package/dist/config.d.ts +78 -2
  129. package/dist/config.js +874 -301
  130. package/dist/degenerate-instrument.d.ts +38 -0
  131. package/dist/degenerate-instrument.js +55 -3
  132. package/dist/elicitation.d.ts +39 -0
  133. package/dist/elicitation.js +87 -3
  134. package/dist/env-facts.d.ts +61 -0
  135. package/dist/env-facts.js +73 -7
  136. package/dist/fleet/fleet-bus.d.ts +185 -0
  137. package/dist/fleet/fleet-bus.js +333 -27
  138. package/dist/fleet/subagent-tail-bus.d.ts +11 -0
  139. package/dist/fleet/subagent-tail-bus.js +33 -2
  140. package/dist/fleet-client.d.ts +38 -0
  141. package/dist/fleet-client.js +44 -9
  142. package/dist/fleet-lease.d.ts +46 -0
  143. package/dist/fleet-lease.js +73 -5
  144. package/dist/hooks/branch-transcript.d.ts +8 -0
  145. package/dist/hooks/branch-transcript.js +33 -0
  146. package/dist/hooks/cc-stop-prompt.d.ts +42 -0
  147. package/dist/hooks/cc-stop-prompt.js +43 -1
  148. package/dist/hooks/hook-llm.d.ts +15 -0
  149. package/dist/hooks/hook-llm.js +47 -0
  150. package/dist/hooks/hook-runner.d.ts +89 -0
  151. package/dist/hooks/hook-runner.js +327 -18
  152. package/dist/http/idempotency.d.ts +31 -0
  153. package/dist/http/idempotency.js +34 -0
  154. package/dist/http/principal-gate.d.ts +17 -0
  155. package/dist/http/principal-gate.js +35 -3
  156. package/dist/http/route-ctx.d.ts +36 -0
  157. package/dist/http/routes/approvals-assistant.d.ts +25 -0
  158. package/dist/http/routes/approvals-assistant.js +253 -23
  159. package/dist/http/routes/attachments.js +22 -4
  160. package/dist/http/routes/capabilities.d.ts +7 -0
  161. package/dist/http/routes/capabilities.js +202 -1
  162. package/dist/http/routes/fleet.d.ts +6 -0
  163. package/dist/http/routes/fleet.js +94 -5
  164. package/dist/http/routes/images.d.ts +17 -0
  165. package/dist/http/routes/images.js +222 -14
  166. package/dist/http/routes/leader.d.ts +7 -0
  167. package/dist/http/routes/leader.js +10 -0
  168. package/dist/http/routes/memory-policy.d.ts +6 -0
  169. package/dist/http/routes/memory-policy.js +38 -6
  170. package/dist/http/routes/notify-wake.d.ts +10 -0
  171. package/dist/http/routes/notify-wake.js +19 -3
  172. package/dist/http/routes/observability.d.ts +6 -0
  173. package/dist/http/routes/observability.js +19 -2
  174. package/dist/http/routes/runs.d.ts +15 -0
  175. package/dist/http/routes/runs.js +464 -41
  176. package/dist/http/routes/session-sync.d.ts +15 -0
  177. package/dist/http/routes/session-sync.js +229 -19
  178. package/dist/http/routes/sessions-list.d.ts +6 -0
  179. package/dist/http/routes/sessions-list.js +35 -4
  180. package/dist/http/routes/sessions.d.ts +14 -0
  181. package/dist/http/routes/sessions.js +176 -31
  182. package/dist/http/routes/side-query.d.ts +8 -0
  183. package/dist/http/routes/side-query.js +26 -1
  184. package/dist/http/routes/tasks.d.ts +18 -0
  185. package/dist/http/routes/tasks.js +501 -38
  186. package/dist/http/routes/trace-usage.d.ts +10 -0
  187. package/dist/http/routes/trace-usage.js +66 -14
  188. package/dist/http/routes/workflows.d.ts +12 -0
  189. package/dist/http/routes/workflows.js +180 -19
  190. package/dist/http/run-meta.d.ts +11 -0
  191. package/dist/http/run-meta.js +6 -0
  192. package/dist/http/send.d.ts +19 -0
  193. package/dist/http/send.js +11 -0
  194. package/dist/http/server.d.ts +255 -0
  195. package/dist/http/server.js +921 -75
  196. package/dist/http/sse-log.d.ts +15 -0
  197. package/dist/http/sse-log.js +16 -0
  198. package/dist/http/tar.d.ts +9 -0
  199. package/dist/http/tar.js +21 -5
  200. package/dist/http/wire-gate.d.ts +9 -0
  201. package/dist/http/wire-gate.js +9 -0
  202. package/dist/http/wire-types.d.ts +187 -0
  203. package/dist/http/workspace-content.d.ts +8 -0
  204. package/dist/http/workspace-content.js +10 -0
  205. package/dist/images/bake-validate.d.ts +63 -0
  206. package/dist/images/bake-validate.js +71 -1
  207. package/dist/images/manifest.d.ts +19 -0
  208. package/dist/images/manifest.js +6 -1
  209. package/dist/index.d.ts +1 -5
  210. package/dist/index.js +10 -5
  211. package/dist/key-resolver.d.ts +23 -0
  212. package/dist/key-resolver.js +27 -2
  213. package/dist/leader/diffout.d.ts +23 -0
  214. package/dist/leader/diffout.js +8 -1
  215. package/dist/leader/diffup.d.ts +32 -0
  216. package/dist/leader/diffup.js +43 -0
  217. package/dist/leader/endpoint.d.ts +20 -0
  218. package/dist/leader/endpoint.js +25 -2
  219. package/dist/leader/fanout.d.ts +60 -0
  220. package/dist/leader/fanout.js +29 -3
  221. package/dist/leader/grader-env-factory.d.ts +74 -0
  222. package/dist/leader/grader-env-factory.js +62 -3
  223. package/dist/leader/leader.d.ts +97 -0
  224. package/dist/leader/leader.js +151 -4
  225. package/dist/leader/merge.d.ts +94 -0
  226. package/dist/leader/merge.js +112 -9
  227. package/dist/leader/planner.d.ts +52 -0
  228. package/dist/leader/planner.js +47 -3
  229. package/dist/leader/repair-oracle.d.ts +61 -0
  230. package/dist/leader/repair-oracle.js +59 -1
  231. package/dist/leader/repair-wire.d.ts +78 -0
  232. package/dist/leader/repair-wire.js +79 -2
  233. package/dist/leader/wire.d.ts +79 -0
  234. package/dist/leader/wire.js +255 -8
  235. package/dist/lsp/e2b-bridge.d.ts +16 -0
  236. package/dist/lsp/e2b-bridge.js +32 -4
  237. package/dist/lsp/e2b-manager.d.ts +10 -0
  238. package/dist/lsp/e2b-manager.js +18 -2
  239. package/dist/lsp/lsp-frames.d.ts +12 -0
  240. package/dist/lsp/lsp-frames.js +12 -0
  241. package/dist/lsp/manager.d.ts +12 -0
  242. package/dist/lsp/manager.js +35 -2
  243. package/dist/lsp/types.d.ts +10 -0
  244. package/dist/lsp/ws-transport.d.ts +12 -0
  245. package/dist/lsp/ws-transport.js +53 -5
  246. package/dist/lsp-evict.d.ts +14 -0
  247. package/dist/lsp-evict.js +15 -1
  248. package/dist/main.js +530 -2129
  249. package/dist/memory-export.d.ts +1 -0
  250. package/dist/memory-export.js +4 -0
  251. package/dist/memory-scope.d.ts +49 -0
  252. package/dist/memory-scope.js +87 -4
  253. package/dist/memory-sync-client.d.ts +24 -0
  254. package/dist/memory-sync-client.js +41 -2
  255. package/dist/memory-sync.d.ts +23 -0
  256. package/dist/memory-sync.js +70 -1
  257. package/dist/model-select.d.ts +39 -0
  258. package/dist/model-select.js +42 -1
  259. package/dist/observability/cost-quota.d.ts +20 -0
  260. package/dist/observability/cost-quota.js +4 -0
  261. package/dist/observability/cost-taxonomy.d.ts +45 -0
  262. package/dist/observability/cost-taxonomy.js +34 -0
  263. package/dist/observability/logger.d.ts +1 -0
  264. package/dist/observability/logger.js +6 -0
  265. package/dist/observability/metrics.d.ts +13 -0
  266. package/dist/observability/metrics.js +63 -0
  267. package/dist/observability/otel-exporter.d.ts +9 -0
  268. package/dist/observability/otel-exporter.js +11 -3
  269. package/dist/observability/principal-context.d.ts +9 -0
  270. package/dist/observability/principal-context.js +9 -0
  271. package/dist/observability/prompt-manifest.d.ts +37 -0
  272. package/dist/observability/prompt-manifest.js +35 -1
  273. package/dist/observability/rate-limit.d.ts +10 -0
  274. package/dist/observability/rate-limit.js +1 -0
  275. package/dist/observability/tool-trace.d.ts +24 -0
  276. package/dist/observability/tool-trace.js +41 -1
  277. package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
  278. package/dist/orchestration/hardened-vm-runner.js +119 -4
  279. package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
  280. package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
  281. package/dist/orchestration/hardened-vm-worker.js +27 -0
  282. package/dist/orchestration/subagent-steer.d.ts +44 -0
  283. package/dist/orchestration/subagent-steer.js +45 -1
  284. package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
  285. package/dist/orchestration/workflow-agent-steer.js +80 -1
  286. package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
  287. package/dist/orchestration/workflow-completion-inbox.js +283 -32
  288. package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
  289. package/dist/orchestration/workflow-notify-journal.js +187 -13
  290. package/dist/parked-decide.d.ts +61 -0
  291. package/dist/parked-decide.js +58 -1
  292. package/dist/per-task-image.d.ts +54 -0
  293. package/dist/per-task-image.js +41 -0
  294. package/dist/plan-cache-probe.d.ts +35 -0
  295. package/dist/plan-cache-probe.js +18 -3
  296. package/dist/plugins/approval-exemption-store.d.ts +7 -0
  297. package/dist/plugins/approval-exemption-store.js +28 -2
  298. package/dist/plugins/approval-store-sql.d.ts +64 -0
  299. package/dist/plugins/approval-store-sql.js +32 -0
  300. package/dist/plugins/background-agent-store-sql.d.ts +55 -0
  301. package/dist/plugins/background-agent-store-sql.js +91 -4
  302. package/dist/plugins/background-shell-support.d.ts +98 -0
  303. package/dist/plugins/background-shell-support.js +117 -13
  304. package/dist/plugins/blob-backend.d.ts +104 -0
  305. package/dist/plugins/blob-backend.js +163 -6
  306. package/dist/plugins/breaker-state-sql.d.ts +65 -1
  307. package/dist/plugins/breaker-state-sql.js +46 -8
  308. package/dist/plugins/caching-session-store.d.ts +42 -0
  309. package/dist/plugins/caching-session-store.js +63 -3
  310. package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
  311. package/dist/plugins/checkpoint-store-sql.js +239 -11
  312. package/dist/plugins/file-outcome-sink.d.ts +4 -0
  313. package/dist/plugins/file-outcome-sink.js +9 -0
  314. package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
  315. package/dist/plugins/file-resume-anchor-store.js +43 -4
  316. package/dist/plugins/file-run-store.d.ts +90 -0
  317. package/dist/plugins/file-run-store.js +145 -13
  318. package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
  319. package/dist/plugins/file-snapshot-store-sql.js +178 -10
  320. package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
  321. package/dist/plugins/file-workflow-journal-store.js +10 -0
  322. package/dist/plugins/fork-routing-session-store.d.ts +47 -0
  323. package/dist/plugins/fork-routing-session-store.js +51 -3
  324. package/dist/plugins/host-platform.d.ts +69 -0
  325. package/dist/plugins/host-platform.js +81 -2
  326. package/dist/plugins/image-bake-store-sql.d.ts +136 -1
  327. package/dist/plugins/image-bake-store-sql.js +241 -7
  328. package/dist/plugins/image-index-sql.d.ts +46 -0
  329. package/dist/plugins/image-index-sql.js +113 -4
  330. package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
  331. package/dist/plugins/k8s-bg-scripts.js +88 -4
  332. package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
  333. package/dist/plugins/k8s-exec-protocol.js +41 -0
  334. package/dist/plugins/local-checkpoint-store.d.ts +40 -0
  335. package/dist/plugins/local-checkpoint-store.js +86 -7
  336. package/dist/plugins/local-session-store.d.ts +157 -0
  337. package/dist/plugins/local-session-store.js +289 -34
  338. package/dist/plugins/local-task-attachment-store.js +14 -2
  339. package/dist/plugins/mailbox-store-sql.d.ts +38 -0
  340. package/dist/plugins/mailbox-store-sql.js +39 -6
  341. package/dist/plugins/memory-engine-pg.d.ts +30 -0
  342. package/dist/plugins/memory-engine-pg.js +122 -4
  343. package/dist/plugins/memory-engine-tidb.d.ts +26 -0
  344. package/dist/plugins/memory-engine-tidb.js +115 -5
  345. package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
  346. package/dist/plugins/memory-engine-vector-util.js +5 -0
  347. package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
  348. package/dist/plugins/memory-resume-anchor-store.js +17 -0
  349. package/dist/plugins/memory-run-store.d.ts +63 -0
  350. package/dist/plugins/memory-run-store.js +73 -8
  351. package/dist/plugins/memory-session-policy-store.d.ts +17 -0
  352. package/dist/plugins/memory-session-policy-store.js +17 -0
  353. package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
  354. package/dist/plugins/memory-sync-store-pg.js +45 -2
  355. package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
  356. package/dist/plugins/memory-sync-store-tidb.js +31 -1
  357. package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
  358. package/dist/plugins/outcome-ledger-sql.js +97 -3
  359. package/dist/plugins/pg-approval-store.d.ts +7 -0
  360. package/dist/plugins/pg-approval-store.js +7 -0
  361. package/dist/plugins/pg-breaker-state.d.ts +6 -0
  362. package/dist/plugins/pg-breaker-state.js +6 -0
  363. package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
  364. package/dist/plugins/pg-checkpoint-store.js +8 -0
  365. package/dist/plugins/pg-cost-quota.d.ts +17 -0
  366. package/dist/plugins/pg-cost-quota.js +6 -0
  367. package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
  368. package/dist/plugins/pg-file-snapshot-store.js +6 -0
  369. package/dist/plugins/pg-image-bake.d.ts +9 -0
  370. package/dist/plugins/pg-image-bake.js +9 -0
  371. package/dist/plugins/pg-image-index.d.ts +9 -0
  372. package/dist/plugins/pg-image-index.js +9 -0
  373. package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
  374. package/dist/plugins/pg-outcome-ledger.js +9 -0
  375. package/dist/plugins/pg-pool.d.ts +50 -0
  376. package/dist/plugins/pg-pool.js +78 -0
  377. package/dist/plugins/pg-query.d.ts +11 -0
  378. package/dist/plugins/pg-rate-limiter.d.ts +23 -0
  379. package/dist/plugins/pg-rate-limiter.js +12 -2
  380. package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
  381. package/dist/plugins/pg-resume-anchor-store.js +5 -0
  382. package/dist/plugins/pg-run-store.d.ts +7 -0
  383. package/dist/plugins/pg-run-store.js +7 -0
  384. package/dist/plugins/pg-safe-json.d.ts +13 -0
  385. package/dist/plugins/pg-safe-json.js +40 -4
  386. package/dist/plugins/pg-session-policy-store.d.ts +5 -0
  387. package/dist/plugins/pg-session-policy-store.js +5 -0
  388. package/dist/plugins/pg-session-storage.d.ts +72 -0
  389. package/dist/plugins/pg-session-storage.js +152 -20
  390. package/dist/plugins/pg-session-store.d.ts +10 -0
  391. package/dist/plugins/pg-session-store.js +10 -0
  392. package/dist/plugins/pg-tool-result-store.d.ts +7 -0
  393. package/dist/plugins/pg-tool-result-store.js +7 -0
  394. package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
  395. package/dist/plugins/pg-workflow-journal-store.js +7 -0
  396. package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
  397. package/dist/plugins/pg-workflow-run-store.js +7 -0
  398. package/dist/plugins/posix-shell-fs.d.ts +3 -0
  399. package/dist/plugins/posix-shell-fs.js +31 -1
  400. package/dist/plugins/remote-env-adb.d.ts +25 -0
  401. package/dist/plugins/remote-env-adb.js +95 -12
  402. package/dist/plugins/remote-env-e2b.d.ts +146 -0
  403. package/dist/plugins/remote-env-e2b.js +371 -40
  404. package/dist/plugins/remote-env-file-error.d.ts +34 -0
  405. package/dist/plugins/remote-env-file-error.js +37 -0
  406. package/dist/plugins/remote-env-host.d.ts +151 -0
  407. package/dist/plugins/remote-env-host.js +468 -70
  408. package/dist/plugins/remote-env-k8s.d.ts +135 -0
  409. package/dist/plugins/remote-env-k8s.js +318 -32
  410. package/dist/plugins/remote-env-local-docker.d.ts +65 -0
  411. package/dist/plugins/remote-env-local-docker.js +149 -24
  412. package/dist/plugins/remote-env-ssh.d.ts +35 -0
  413. package/dist/plugins/remote-env-ssh.js +117 -20
  414. package/dist/plugins/remote-scratchpad.d.ts +38 -0
  415. package/dist/plugins/remote-scratchpad.js +32 -2
  416. package/dist/plugins/remote-shell.d.ts +22 -0
  417. package/dist/plugins/remote-shell.js +27 -1
  418. package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
  419. package/dist/plugins/resume-anchor-store-sql.js +11 -0
  420. package/dist/plugins/roster-store-sql.d.ts +27 -0
  421. package/dist/plugins/roster-store-sql.js +54 -0
  422. package/dist/plugins/run-store-sql.d.ts +194 -0
  423. package/dist/plugins/run-store-sql.js +210 -9
  424. package/dist/plugins/s3-presign.d.ts +20 -0
  425. package/dist/plugins/s3-presign.js +49 -2
  426. package/dist/plugins/scheduler-support.d.ts +12 -0
  427. package/dist/plugins/scheduler-support.js +75 -3
  428. package/dist/plugins/send-file-ledger.d.ts +15 -0
  429. package/dist/plugins/send-file-ledger.js +53 -4
  430. package/dist/plugins/send-user-file.d.ts +77 -0
  431. package/dist/plugins/send-user-file.js +92 -4
  432. package/dist/plugins/session-policy-store-sql.d.ts +15 -0
  433. package/dist/plugins/session-policy-store-sql.js +75 -3
  434. package/dist/plugins/session-store.d.ts +45 -0
  435. package/dist/plugins/session-store.js +51 -0
  436. package/dist/plugins/sql-driver.d.ts +77 -0
  437. package/dist/plugins/sql-driver.js +4 -0
  438. package/dist/plugins/sql-escape.d.ts +8 -0
  439. package/dist/plugins/sql-escape.js +8 -0
  440. package/dist/plugins/sql-row-helpers.d.ts +25 -0
  441. package/dist/plugins/sql-row-helpers.js +25 -0
  442. package/dist/plugins/store-backend.d.ts +103 -0
  443. package/dist/plugins/store-backend.js +120 -27
  444. package/dist/plugins/store-contracts.d.ts +62 -0
  445. package/dist/plugins/store-contracts.js +3 -0
  446. package/dist/plugins/task-attachment-store.d.ts +60 -0
  447. package/dist/plugins/task-attachment-store.js +49 -3
  448. package/dist/plugins/task-list-store-sql.d.ts +25 -0
  449. package/dist/plugins/task-list-store-sql.js +21 -0
  450. package/dist/plugins/tidb-approval-store.d.ts +6 -0
  451. package/dist/plugins/tidb-approval-store.js +6 -0
  452. package/dist/plugins/tidb-breaker-state.d.ts +5 -0
  453. package/dist/plugins/tidb-breaker-state.js +5 -0
  454. package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
  455. package/dist/plugins/tidb-checkpoint-store.js +7 -0
  456. package/dist/plugins/tidb-cost-quota.d.ts +6 -0
  457. package/dist/plugins/tidb-cost-quota.js +1 -0
  458. package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
  459. package/dist/plugins/tidb-file-snapshot-store.js +6 -0
  460. package/dist/plugins/tidb-image-bake.d.ts +8 -0
  461. package/dist/plugins/tidb-image-bake.js +8 -0
  462. package/dist/plugins/tidb-image-index.d.ts +8 -0
  463. package/dist/plugins/tidb-image-index.js +8 -0
  464. package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
  465. package/dist/plugins/tidb-outcome-ledger.js +10 -0
  466. package/dist/plugins/tidb-pool.d.ts +56 -0
  467. package/dist/plugins/tidb-pool.js +181 -1
  468. package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
  469. package/dist/plugins/tidb-rate-limiter.js +5 -2
  470. package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
  471. package/dist/plugins/tidb-resume-anchor-store.js +5 -0
  472. package/dist/plugins/tidb-run-store.d.ts +7 -0
  473. package/dist/plugins/tidb-run-store.js +7 -0
  474. package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
  475. package/dist/plugins/tidb-session-policy-store.js +5 -0
  476. package/dist/plugins/tidb-session-storage.d.ts +43 -0
  477. package/dist/plugins/tidb-session-storage.js +62 -3
  478. package/dist/plugins/tidb-session-store.d.ts +142 -0
  479. package/dist/plugins/tidb-session-store.js +264 -16
  480. package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
  481. package/dist/plugins/tidb-tool-result-store.js +7 -0
  482. package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
  483. package/dist/plugins/tidb-workflow-journal-store.js +7 -0
  484. package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
  485. package/dist/plugins/tidb-workflow-run-store.js +8 -0
  486. package/dist/plugins/tool-result-store-sql.d.ts +58 -0
  487. package/dist/plugins/tool-result-store-sql.js +38 -2
  488. package/dist/plugins/web-search.d.ts +35 -0
  489. package/dist/plugins/web-search.js +45 -9
  490. package/dist/plugins/workflow-journal-limits.d.ts +10 -0
  491. package/dist/plugins/workflow-journal-limits.js +10 -0
  492. package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
  493. package/dist/plugins/workflow-journal-store-sql.js +58 -2
  494. package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
  495. package/dist/plugins/workflow-run-store-sql.js +84 -6
  496. package/dist/plugins/worktree-isolation.d.ts +45 -0
  497. package/dist/plugins/worktree-isolation.js +105 -6
  498. package/dist/plugins/write-behind-counter.d.ts +56 -1
  499. package/dist/plugins/write-behind-counter.js +53 -14
  500. package/dist/principal-jwt.d.ts +24 -0
  501. package/dist/principal-jwt.js +60 -5
  502. package/dist/project-memory.d.ts +22 -0
  503. package/dist/project-memory.js +144 -15
  504. package/dist/prompts-domain-validate.d.ts +28 -0
  505. package/dist/prompts-domain-validate.js +42 -1
  506. package/dist/question.d.ts +33 -0
  507. package/dist/question.js +69 -2
  508. package/dist/resource-suspend.d.ts +21 -0
  509. package/dist/resource-suspend.js +16 -0
  510. package/dist/router/route-orchestration.d.ts +78 -0
  511. package/dist/router/route-orchestration.js +77 -0
  512. package/dist/run-local.d.ts +32 -0
  513. package/dist/run-local.js +172 -8
  514. package/dist/runs.d.ts +189 -4
  515. package/dist/runs.js +465 -24
  516. package/dist/runtime-caps-resolver.d.ts +106 -0
  517. package/dist/runtime-caps-resolver.js +89 -4
  518. package/dist/runtime-governance.d.ts +84 -0
  519. package/dist/runtime-governance.js +127 -1
  520. package/dist/sandbox-pkg-source.d.ts +23 -0
  521. package/dist/sandbox-pkg-source.js +37 -0
  522. package/dist/sealed-key.d.ts +54 -0
  523. package/dist/sealed-key.js +68 -3
  524. package/dist/security.d.ts +108 -0
  525. package/dist/security.js +145 -6
  526. package/dist/sema-registry.d.ts +21 -0
  527. package/dist/sema-registry.js +21 -0
  528. package/dist/session-leaf-bus.d.ts +31 -0
  529. package/dist/session-leaf-bus.js +32 -0
  530. package/dist/session-sync-content.d.ts +46 -0
  531. package/dist/session-sync-content.js +47 -1
  532. package/dist/session-sync-kernel.d.ts +89 -0
  533. package/dist/session-sync-kernel.js +48 -3
  534. package/dist/session-sync.d.ts +125 -0
  535. package/dist/session-sync.js +181 -8
  536. package/dist/session-titler.d.ts +26 -0
  537. package/dist/session-titler.js +41 -7
  538. package/dist/session-watch.d.ts +53 -1
  539. package/dist/session-watch.js +73 -9
  540. package/dist/sighup-idle.d.ts +30 -0
  541. package/dist/sighup-idle.js +12 -1
  542. package/dist/spec-fields.d.ts +91 -3
  543. package/dist/spec-fields.js +116 -4
  544. package/dist/task-cwd.d.ts +50 -0
  545. package/dist/task-cwd.js +68 -1
  546. package/dist/task-mcp.d.ts +47 -0
  547. package/dist/task-mcp.js +29 -0
  548. package/dist/task-settings.d.ts +175 -0
  549. package/dist/task-settings.js +226 -11
  550. package/dist/task-workflow.d.ts +67 -0
  551. package/dist/task-workflow.js +75 -8
  552. package/dist/tool-approval.d.ts +91 -0
  553. package/dist/tool-approval.js +215 -0
  554. package/dist/trace/artifacts.d.ts +9 -0
  555. package/dist/trace/artifacts.js +30 -5
  556. package/dist/trace/core-keyset-guard.d.ts +18 -0
  557. package/dist/trace/ledger-sink.d.ts +43 -0
  558. package/dist/trace/ledger-sink.js +64 -5
  559. package/dist/trace/project.d.ts +133 -0
  560. package/dist/trace/project.js +189 -2
  561. package/dist/trace/redact.d.ts +19 -0
  562. package/dist/trace/redact.js +66 -9
  563. package/dist/usage-analytics.d.ts +26 -0
  564. package/dist/usage-analytics.js +26 -3
  565. package/package.json +1 -1
@@ -1,3 +1,37 @@
1
+ /**
2
+ * Durable approval store for the human-in-the-loop write gate (F4) — SINGLE-FILE DUAL-DIALECT
3
+ * (design/158 A12 定型半场). ONE implementation, TWO dialects; the historical `TiDBApprovalStore` /
4
+ * `PgApprovalStore` class names survive as thin ctor subclasses so every consumer (store-backend.ts,
5
+ * src/index.ts, the approval/audit/security-coverage suites) is untouched.
6
+ *
7
+ * When a gated tool call is about to run, a `pending` row is recorded here; the instance running the
8
+ * task polls it for a decision. Because the row is durable and shared, an operator's approve/deny can
9
+ * land on ANY instance — critical under async runs + a load balancer, where the deciding request and
10
+ * the waiting run are usually on different instances.
11
+ *
12
+ * Unlike the run/checkpoint PG store (whose schema is owned centrally by pg-pool.ts), this file also
13
+ * exports a self-contained PG schema (`PG_APPROVAL_SCHEMA` + `ensureSchema`) — the `approval` table is
14
+ * DISJOINT from the other PG stores' tables, so a CREATE IF NOT EXISTS here shadows nothing. Central
15
+ * aggregation into pg-pool.ts composes it alongside the others. Kept in lock-step with the TiDB store
16
+ * by the shared real-DB integration suite (test/pg-approval-store-integration.test.ts).
17
+ *
18
+ * ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
19
+ * - `?` placeholders vs `$n`
20
+ * - null-safe scope compare: `<=>` vs `IS NOT DISTINCT FROM` (a NULL scope row — legacy/untenanted
21
+ * run — is matched by a NULL scope arg; one tenant's scope NEVER matches another's — the
22
+ * single-DB fleet read guard, defense-in-depth)
23
+ * - JSON binding: TiDB `JSON.stringify` verbatim vs PG `pgProtocolJsonStringify` (codex R4-H2: the
24
+ * persisted approval `args` is the operator's REVIEW surface and must agree byte-for-byte with the
25
+ * policy-executed args — a lossy/rewriting envelope would let an operator approve one payload while
26
+ * a different one runs)
27
+ * - affectedRows vs rowCount (via SqlDriver)
28
+ * - schema ownership: TiDB DDL lives in tidb-pool.ts SCHEMA_STATEMENTS; PG DDL is this file's own
29
+ * self-contained `PG_APPROVAL_SCHEMA` (see the PG twin's original header — unlike the other PG
30
+ * stores, `approval` is NOT centrally owned by pg-pool.ts's DDL array, only aggregated into its
31
+ * `ensureSchema` composition).
32
+ * - `getStatus` result shape: `RowDataPacket[]` (mysql2 typed query) vs a plain-record `pg` query —
33
+ * normalized to `SqlRow` by the shared driver, so both arms read the same field names.
34
+ */
1
35
  import type { Pool as MySqlPool } from "mysql2/promise";
2
36
  import type { Pool as PgPool, PoolClient } from "pg";
3
37
  import { type SqlDriver } from "./sql-driver.js";
@@ -7,6 +41,7 @@ export interface ApprovalRow {
7
41
  taskId: string | null;
8
42
  sessionId: string | null;
9
43
  owner: string | null;
44
+ /** Single-DB fleet scope guard (defense-in-depth): the run's tenant identity, owner-sourced. Reads filter on it. */
10
45
  scope: string | null;
11
46
  toolName: string;
12
47
  args: unknown;
@@ -16,12 +51,23 @@ export interface ApprovalRow {
16
51
  createdAt: string;
17
52
  decidedAt: string | null;
18
53
  }
54
+ /** PG translation of the `approval` DDL in tidb-pool.ts SCHEMA_STATEMENTS (JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3),
55
+ * inline KEY→separate CREATE INDEX).
56
+ *
57
+ * SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
58
+ * drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead. */
19
59
  export declare const PG_APPROVAL_SCHEMA: string[];
60
+ /** Idempotent self-contained schema apply (for the integration test; central aggregation is done separately). */
20
61
  export declare function ensureSchema(pool: PgPool | PoolClient): Promise<void>;
62
+ /** Dual-dialect durable approval store. See the file header for the dialect-delta ledger. */
21
63
  export declare class SqlApprovalStore {
22
64
  protected readonly db: SqlDriver;
23
65
  constructor(db: SqlDriver);
66
+ /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
24
67
  private q;
68
+ /** JSON column binding: TiDB stores the string verbatim; PG goes through the LOSSLESS protocol envelope
69
+ * (codex R4-H2 — the approval `args` is the operator's review surface and must agree with the
70
+ * policy-executed args at the NUL position). */
25
71
  private json;
26
72
  createPending(id: string, scope: string | null, ctx: {
27
73
  taskId?: string | null;
@@ -35,17 +81,35 @@ export declare class SqlApprovalStore {
35
81
  reason: string | null;
36
82
  decidedBy: string | null;
37
83
  } | undefined>;
84
+ /** Apply a terminal decision, but only to a still-pending row (CAS). Returns whether it applied. The scope
85
+ * guard (null-safe) is carried on the operator path too for consistency with the read paths. */
38
86
  decide(id: string, scope: string | null, decision: "approved" | "denied" | "expired", reason: string | null, decidedBy: string | null): Promise<boolean>;
87
+ /** Pending approvals (operator queue), scoped to one tenant (single-DB fleet read guard, null-safe), optionally
88
+ * further filtered to one owner. */
39
89
  listPending(scope: string | null, owner?: string): Promise<ApprovalRow[]>;
40
90
  get(id: string, scope: string | null): Promise<ApprovalRow | undefined>;
91
+ /** Operator-only CROSS-tenant by-id read (no scope filter). The F4 operator queue serves/decides across
92
+ * tenants by design; the HTTP layer gates this to an operator and then re-binds decide() to the row's OWN
93
+ * scope. Tenants NEVER reach this — they use the scope-bound {@link get}. */
41
94
  getById(id: string): Promise<ApprovalRow | undefined>;
95
+ /** Operator-only CROSS-tenant pending list (no scope filter) — the unfiltered operator queue across all
96
+ * tenants. Tenants use the scope-bound {@link listPending}; this is HTTP-gated to an operator.
97
+ * 🔴 identical SQL text on both dialects (no placeholders, nothing dialect-specific) — kept as ONE literal
98
+ * rather than a `q()` pair so a reader isn't misled into looking for a divergence that isn't there. */
42
99
  listPendingAll(): Promise<ApprovalRow[]>;
100
+ /** Expire pending approvals older than `olderThanMs` (a never-answered operator request). Deliberately GLOBAL
101
+ * (NOT scope-filtered): staleness expiry is fleet-level maintenance that protects the worker regardless of
102
+ * tenant — a stale row in any scope must be reaped. Scope isolation applies to the per-tenant READ paths, not
103
+ * to this maintenance sweep. */
43
104
  expireStale(olderThanMs: number): Promise<number>;
44
105
  }
106
+ /** approval row → {@link ApprovalRow} — shared by BOTH dialects. design/158 S8 归位 / A12 收编. */
45
107
  export declare function mapRow(r: Record<string, unknown>): ApprovalRow;
108
+ /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
46
109
  export declare class TiDBApprovalStore extends SqlApprovalStore {
47
110
  constructor(pool: MySqlPool);
48
111
  }
112
+ /** PostgreSQL binding — historical class name + ctor shape preserved. */
49
113
  export declare class PgApprovalStore extends SqlApprovalStore {
50
114
  constructor(pool: PgPool);
51
115
  }
@@ -1,6 +1,11 @@
1
1
  import { parseJsonStrict as parseJson, toIsoOrNull as iso } from "./sql-row-helpers.js";
2
2
  import { pgProtocolJsonStringify } from "./pg-safe-json.js";
3
3
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
4
+ /** PG translation of the `approval` DDL in tidb-pool.ts SCHEMA_STATEMENTS (JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3),
5
+ * inline KEY→separate CREATE INDEX).
6
+ *
7
+ * SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
8
+ * drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead. */
4
9
  export const PG_APPROVAL_SCHEMA = [
5
10
  `CREATE TABLE IF NOT EXISTS approval (
6
11
  id VARCHAR(64) NOT NULL,
@@ -24,18 +29,24 @@ export const PG_APPROVAL_SCHEMA = [
24
29
  `CREATE INDEX IF NOT EXISTS idx_approval_status ON approval (status)`,
25
30
  `CREATE INDEX IF NOT EXISTS idx_approval_scope_status ON approval (scope, status)`,
26
31
  ];
32
+ /** Idempotent self-contained schema apply (for the integration test; central aggregation is done separately). */
27
33
  export async function ensureSchema(pool) {
28
34
  for (const stmt of PG_APPROVAL_SCHEMA)
29
35
  await pool.query(stmt);
30
36
  }
37
+ /** Dual-dialect durable approval store. See the file header for the dialect-delta ledger. */
31
38
  export class SqlApprovalStore {
32
39
  db;
33
40
  constructor(db) {
34
41
  this.db = db;
35
42
  }
43
+ /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
36
44
  q(tidb, pg) {
37
45
  return this.db.dialect === "tidb" ? tidb : pg;
38
46
  }
47
+ /** JSON column binding: TiDB stores the string verbatim; PG goes through the LOSSLESS protocol envelope
48
+ * (codex R4-H2 — the approval `args` is the operator's review surface and must agree with the
49
+ * policy-executed args at the NUL position). */
39
50
  json(value, label) {
40
51
  return this.db.dialect === "tidb" ? JSON.stringify(value) : pgProtocolJsonStringify(value, label);
41
52
  }
@@ -54,16 +65,23 @@ export class SqlApprovalStore {
54
65
  ]);
55
66
  }
56
67
  async getStatus(id, scope) {
68
+ // null-safe scope match (`<=>` / `IS NOT DISTINCT FROM`): a NULL scope row (legacy / untenanted run) is
69
+ // matched by a NULL scope arg, and a tenant's scope NEVER matches another tenant's — the single-DB fleet
70
+ // read guard (defense-in-depth).
57
71
  const { rows } = await this.db.query(this.q("SELECT status, reason, decided_by FROM approval WHERE id = ? AND scope <=> ?", "SELECT status, reason, decided_by FROM approval WHERE id = $1 AND scope IS NOT DISTINCT FROM $2"), [id, scope]);
58
72
  const r = rows[0];
59
73
  if (!r)
60
74
  return undefined;
61
75
  return { status: r.status, reason: r.reason ?? null, decidedBy: r.decided_by ?? null };
62
76
  }
77
+ /** Apply a terminal decision, but only to a still-pending row (CAS). Returns whether it applied. The scope
78
+ * guard (null-safe) is carried on the operator path too for consistency with the read paths. */
63
79
  async decide(id, scope, decision, reason, decidedBy) {
64
80
  const res = await this.db.query(this.q("UPDATE approval SET status = ?, reason = ?, decided_by = ?, decided_at = ? WHERE id = ? AND scope <=> ? AND status = 'pending'", "UPDATE approval SET status = $1, reason = $2, decided_by = $3, decided_at = $4 WHERE id = $5 AND scope IS NOT DISTINCT FROM $6 AND status = 'pending'"), [decision, reason, decidedBy, new Date(), id, scope]);
65
81
  return res.affected === 1;
66
82
  }
83
+ /** Pending approvals (operator queue), scoped to one tenant (single-DB fleet read guard, null-safe), optionally
84
+ * further filtered to one owner. */
67
85
  async listPending(scope, owner) {
68
86
  const { rows } = owner
69
87
  ? await this.db.query(this.q("SELECT * FROM approval WHERE status = 'pending' AND scope <=> ? AND owner = ? ORDER BY created_at ASC", "SELECT * FROM approval WHERE status = 'pending' AND scope IS NOT DISTINCT FROM $1 AND owner = $2 ORDER BY created_at ASC"), [scope, owner])
@@ -74,14 +92,25 @@ export class SqlApprovalStore {
74
92
  const { rows } = await this.db.query(this.q("SELECT * FROM approval WHERE id = ? AND scope <=> ?", "SELECT * FROM approval WHERE id = $1 AND scope IS NOT DISTINCT FROM $2"), [id, scope]);
75
93
  return rows[0] ? mapRow(rows[0]) : undefined;
76
94
  }
95
+ /** Operator-only CROSS-tenant by-id read (no scope filter). The F4 operator queue serves/decides across
96
+ * tenants by design; the HTTP layer gates this to an operator and then re-binds decide() to the row's OWN
97
+ * scope. Tenants NEVER reach this — they use the scope-bound {@link get}. */
77
98
  async getById(id) {
78
99
  const { rows } = await this.db.query(this.q("SELECT * FROM approval WHERE id = ?", "SELECT * FROM approval WHERE id = $1"), [id]);
79
100
  return rows[0] ? mapRow(rows[0]) : undefined;
80
101
  }
102
+ /** Operator-only CROSS-tenant pending list (no scope filter) — the unfiltered operator queue across all
103
+ * tenants. Tenants use the scope-bound {@link listPending}; this is HTTP-gated to an operator.
104
+ * 🔴 identical SQL text on both dialects (no placeholders, nothing dialect-specific) — kept as ONE literal
105
+ * rather than a `q()` pair so a reader isn't misled into looking for a divergence that isn't there. */
81
106
  async listPendingAll() {
82
107
  const { rows } = await this.db.query("SELECT * FROM approval WHERE status = 'pending' ORDER BY created_at ASC");
83
108
  return rows.map(mapRow);
84
109
  }
110
+ /** Expire pending approvals older than `olderThanMs` (a never-answered operator request). Deliberately GLOBAL
111
+ * (NOT scope-filtered): staleness expiry is fleet-level maintenance that protects the worker regardless of
112
+ * tenant — a stale row in any scope must be reaped. Scope isolation applies to the per-tenant READ paths, not
113
+ * to this maintenance sweep. */
85
114
  async expireStale(olderThanMs) {
86
115
  const cutoff = new Date(Date.now() - olderThanMs);
87
116
  const res = await this.db.query(this.q("UPDATE approval SET status = 'expired', reason = 'timed out before decision', decided_at = ? " +
@@ -90,6 +119,7 @@ export class SqlApprovalStore {
90
119
  return res.affected;
91
120
  }
92
121
  }
122
+ /** approval row → {@link ApprovalRow} — shared by BOTH dialects. design/158 S8 归位 / A12 收编. */
93
123
  export function mapRow(r) {
94
124
  return {
95
125
  id: String(r.id),
@@ -106,11 +136,13 @@ export function mapRow(r) {
106
136
  decidedAt: iso(r.decided_at),
107
137
  };
108
138
  }
139
+ /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
109
140
  export class TiDBApprovalStore extends SqlApprovalStore {
110
141
  constructor(pool) {
111
142
  super(mysqlDriver(pool));
112
143
  }
113
144
  }
145
+ /** PostgreSQL binding — historical class name + ctor shape preserved. */
114
146
  export class PgApprovalStore extends SqlApprovalStore {
115
147
  constructor(pool) {
116
148
  super(pgDriver(pool));
@@ -1,3 +1,34 @@
1
+ /**
2
+ * BackgroundAgentStore SQL twins(design/151 S1 server 半场,[1503] 提货单②)——core
3
+ * `RunnerDeps.backgroundAgentStore` / `SubagentToolOptions.background.agentStore` 的 TiDB/PG
4
+ * 双方言实现:每个后台子代(`a*` 句柄)一行 durable 执行记录。
5
+ *
6
+ * 语义对齐真源 = core `InMemoryBackgroundAgentStore`(dist/core/background-agent-store.js),
7
+ * 契约四条([1503] 必守):
8
+ * 1. `put` create-once:重复 (scope, handle) 键 → `BackgroundAgentStoreError("agent_record.already_exists")`
9
+ * (唯一键原子,多副本安全);scope 缺席/空串拒收(与 core 同文案——default-deny 谓词下无 scope 的
10
+ * 行会经 fallback 面变成公共可读)。
11
+ * 2. `update` rev-CAS:整行替换,`WHERE … AND rev = expect.rev`,命中即 `rev = expect.rev + 1`;
12
+ * 未命中 false(心跳静默主体在 core 调用侧,store 侧诚实——同 roster 语义条 3,绝不吞成假成功)。
13
+ * 3. `listBySession`/`listByScope` content-free:只 SELECT 摘要列(绝不拉 record_json——finalOutput/
14
+ * recentSteps 可能是大 blob);session 锚 = `sessionScoped ? owner : parentSessionId`,写入时物化为
15
+ * `session_anchor` 列(anchor 为 NULL 的行 `= ?` 恒假 = core `anchor !== sessionId` 同义)。
16
+ * 4. `delete` 条件删(可选 rev CAS);`reap` 三策略与 core 逐条同形(staleRunning 翻 failed 走
17
+ * rev-CAS 逐行,竞态输了跳过不计;terminal maxAge/keep 按 spawnedAt DESC)。[1522] LOW6 记账:裸
18
+ * `reap` 的批删 IN 无上界、stale 臂全量拉 record_json——server 自己的 retention 只走
19
+ * reapDurableAgents(core 编排,不触这两条);第三方直调超大 scope 时自行分批。
20
+ *
21
+ * 行形:`record_json` = 全量真源(get 逐位读回);查询/谓词列全是投影,写入时从 record 物化
22
+ * (展示列 name/agent_type 截到列宽 + PG 侧过 lossy 清洗——真源仍在 record_json;身份键列
23
+ * scope/handle/owner/session 锚**不清洗不截断**:改写身份键会让行与 engine 查询键错位,含不可存
24
+ * 字节的身份键=写入失败 fail-loud,与 [1439] 协议数据拒绝式同向)。
25
+ * PG unstorable bytes([1439] 三层定谳):record/usage 走 `pgSafeJsonStringify`(观测/内容面 lossy
26
+ * U+FFFD,与 run 行/journal 同族;终报含真 NUL 的病态输入换替换标记,内容面不失行);TiDB TEXT 存 verbatim。
27
+ *
28
+ * 键列 = 字节等价(TiDB VARBINARY / PG COLLATE "C"——roster F2 同案:PAD SPACE/大小写近撞不得穿过
29
+ * scope 隔离键)。`listReapScopes()` 是 server 侧扩展(非 core 接口)——reaper 的 DISTINCT-scope 枚举,
30
+ * 与 workflow-run store 的 reapAllScopes 同姿势。
31
+ */
1
32
  import type { Pool } from "mysql2/promise";
2
33
  import { type BackgroundAgentRecord, type BackgroundAgentRowSummary, type BackgroundAgentStore } from "@sema-agent/core";
3
34
  import type { PgQueryFn } from "./pg-query.js";
@@ -21,6 +52,9 @@ export declare class TiDBBackgroundAgentStore implements BackgroundAgentStore {
21
52
  update(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
22
53
  rev: number;
23
54
  }): Promise<boolean>;
55
+ /** core 1.383([1565] 提货单)REQUIRED 新方法——原子守卫 CAS(claim 的 reservation/rollback/finalize
56
+ * 绑定字段态非仅 rev:rev 可因无关写移动而字段义已变)。语义逐字对齐 core InMemoryBackgroundAgentStore
57
+ * ({@link parkGuardConditions} 顶注)。 */
24
58
  updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
25
59
  rev: number;
26
60
  status?: BackgroundAgentRecord["status"];
@@ -32,7 +66,22 @@ export declare class TiDBBackgroundAgentStore implements BackgroundAgentStore {
32
66
  delete(handle: string, scope: string, expect?: {
33
67
  rev: number;
34
68
  }): Promise<boolean>;
69
+ /** 🔴 返回值语义 = **受影响的不同行数**,不是「翻转数 + 删除数」的和(core 1.411 起)。
70
+ *
71
+ * core 1.411 把自己 InMemory 的 `return touched + doomed.size` 改成
72
+ * `return new Set([...flippedKeys, ...doomed]).size` —— 因为一行可能在**同一轮** reap 里既被翻转
73
+ * (stale running → failed)、又因为翻转后变成终态而落进 maxAge/keep 的删除集,旧写法把它算了两次。
74
+ * 这条被真双库对拆套件当场抓到(升 core 1.411 后 `tidb: reaped 计数: expected 4 to be 3`,core 1.404
75
+ * 下同文件全绿 —— A/B 判明是 core 的语义修,我们跟上)。
76
+ *
77
+ * ⚠️ 两个引擎**机制不同但语义一致**:PG 有 `DELETE … RETURNING handle`,可以单语句拿到真被删的 handle
78
+ * 直接并入同一个 Set;TiDB/MySQL **没有 RETURNING**,`affectedRows` 只给数量不给身份,所以按「是否已在
79
+ * 翻转集里」把 doomed 拆两批删 —— 重叠那批照样真删、只是不再计数。两边都不能用「doomed.size 当删除数」
80
+ * 近似:并发写者(真心跳复活)会让 `status <> 'running'` 守卫少删几行,那时 doomed.size 会虚高。 */
35
81
  reap(scope: string, now: number, opts?: ReapOpts): Promise<number>;
82
+ /** core 1.368([1516]②)可选接口成员落位(原 server 扩展 listReapScopes 更名对齐):当前持行
83
+ * scope 枚举——reaper 的 per-scope 联合 reap 入口。JS 侧 sort 与 core InMemory 逐字节同序
84
+ * (SQL ORDER BY 按 collation,非码元序)。 */
36
85
  listScopes(): Promise<string[]>;
37
86
  }
38
87
  export declare class PgBackgroundAgentStore implements BackgroundAgentStore {
@@ -43,6 +92,8 @@ export declare class PgBackgroundAgentStore implements BackgroundAgentStore {
43
92
  update(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
44
93
  rev: number;
45
94
  }): Promise<boolean>;
95
+ /** core 1.383([1565] 提货单)REQUIRED 新方法(TiDB twin 同案注释)。PG 侧占位符是显式编号 `$N`
96
+ * (不像 TiDB `?` 靠数组顺序)——`n` 是跑动计数器,只在真正消耗一个占位符(非 IS NULL 分支)时前增。 */
46
97
  updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
47
98
  rev: number;
48
99
  status?: BackgroundAgentRecord["status"];
@@ -54,7 +105,11 @@ export declare class PgBackgroundAgentStore implements BackgroundAgentStore {
54
105
  delete(handle: string, scope: string, expect?: {
55
106
  rev: number;
56
107
  }): Promise<boolean>;
108
+ /** 返回值 = 受影响的**不同行数**(core 1.411 语义;TiDB twin 的方法头注写了完整由来 + A/B 取证)。
109
+ * PG 这半有 `DELETE … RETURNING handle`,所以真被删的 handle 直接并进同一个 Set —— 不需要 TiDB 那边的
110
+ * 「拆两批发」变通,但两边的**结果**必须逐个相等(真双库对拆套件按 core InMemory 的计数逐字比)。 */
57
111
  reap(scope: string, now: number, opts?: ReapOpts): Promise<number>;
112
+ /** core 1.368 可选接口成员(TiDB twin 同案注释)。 */
58
113
  listScopes(): Promise<string[]>;
59
114
  }
60
115
  export {};
@@ -2,16 +2,28 @@ import { BackgroundAgentStoreError, } from "@sema-agent/core";
2
2
  import { pgSafeJsonStringify, pgSanitizeText } from "./pg-safe-json.js";
3
3
  export const BACKGROUND_AGENT_TABLE = "background_agents";
4
4
  const SUMMARY_COLS = "handle, owner, session_scoped, session_id, parent_session_id, root_session_id, name, agent_type, status, spawned_at_ms, updated_at_ms, settled_at_ms, usage_json";
5
+ /** put/update 共用的投影列(scope_key/handle 键列另拼;record_json 恒最后)。core 1.383([1565] 提货单)
6
+ * `updateIf` 的 CAS 守卫需要 `parked_checkpoint_token`/`park_claim_id` 是**专用列**——守卫条件必须在
7
+ * 数据库层面对当前行状态原子求值,不能只靠 record_json 这个 blob(那要求先读后写,读写之间就是
8
+ * CAS 本要堵的竞态窗)。`writerEpoch` 不在此列:updateIf 的 guard 参数集里没有它([1565] 原文/core
9
+ * 类型签名逐字核对过),只是 record_json 全量对象序列化里随手带上的普通字段,不需要专用列——
10
+ * record_json 的写法是 `json(stored)`(stored = `{...record, handle, scope, rev}` 整个对象展开),
11
+ * 任何 record 上有的字段(含 writerEpoch)天然随之落盘/读回,无需逐字段维护投影列表。 */
5
12
  const PROJ_COLS = "scope, owner, session_scoped, session_id, parent_session_id, root_session_id, session_anchor, name, agent_type, status, spawned_at_ms, updated_at_ms, settled_at_ms, usage_json, rev, parked_checkpoint_token, park_claim_id, record_json";
6
- const PROJ_N = PROJ_COLS.split(", ").length;
13
+ const PROJ_N = PROJ_COLS.split(", ").length; // F5: WHERE 占位符序号从列数派生,列序变更单点同步
7
14
  function assertScope(record) {
8
15
  if (record.scope === undefined || record.scope === "") {
16
+ // core InMemory 同文案(default-deny 谓词下无 scope 行=经 fallback 面公共可读,写入即拒)
9
17
  throw new Error("BackgroundAgentStore.put: refusing a row without a scope (world-readable through the fallback)");
10
18
  }
11
19
  }
20
+ /** stored 形归一(core put/update 语义:handle/scope/rev 以参数为准,record_json 存归一后的整行)。 */
12
21
  function storedForm(record, handle, scope, rev) {
13
22
  return { ...record, handle, scope, rev };
14
23
  }
24
+ /** 投影参数(键列除外,与 PROJ_COLS 同序)。json 序列化器/展示文本清洗器按方言注入([1439]:PG
25
+ * lossy 标记,TiDB verbatim)。展示列截到列宽(#3:超长 name 不该让整行 durable 写失败——真源在
26
+ * record_json,摘要投影截断是诚实形)。 */
15
27
  function projParams(stored, json, text) {
16
28
  return [
17
29
  stored.scope,
@@ -34,6 +46,7 @@ function projParams(stored, json, text) {
34
46
  json(stored),
35
47
  ];
36
48
  }
49
+ /** 摘要行 → core BackgroundAgentRowSummary(缺席键省略,core toSummary 形逐字;usage 从 usage_json)。 */
37
50
  function rowToSummary(r) {
38
51
  return {
39
52
  handle: String(r.handle),
@@ -53,6 +66,7 @@ function rowToSummary(r) {
53
66
  : {}),
54
67
  };
55
68
  }
69
+ /** core reap 的 staleRunning 翻转形(逐字段照抄 InMemory:summary 只在缺席时补默认句)。 */
56
70
  function flippedForm(live, now) {
57
71
  return {
58
72
  ...live,
@@ -63,6 +77,7 @@ function flippedForm(live, now) {
63
77
  updatedAt: now,
64
78
  };
65
79
  }
80
+ /** terminal doomed 集合(core reap 的 maxAge + keep 两策略;rows 已按 spawnedAt DESC)。 */
66
81
  function doomedHandles(rows, now, opts) {
67
82
  const doomed = new Set();
68
83
  if (opts.maxAgeMs !== undefined) {
@@ -77,6 +92,11 @@ function doomedHandles(rows, now, opts) {
77
92
  }
78
93
  return doomed;
79
94
  }
95
+ /** core 1.383([1565] 提货单,design/153 件3a)`updateIf` 的三个可选守卫条件——dialect-neutral 描述,
96
+ * 各方言自行渲染成 `?`(TiDB)或 `$N`(PG)占位符。core InMemoryBackgroundAgentStore.updateIf 逐字同源
97
+ * (background-agent-store.js:175-187)语义:`value === null` = 该列当前**必须缺席**(SQL `IS NULL`,
98
+ * 对应 core `live.X !== undefined` 的取反);`value` 是字符串 = 必须**字面相等**;字段整体缺席
99
+ * (expect.X === undefined,不在返回数组里)= 不设该条守卫,与该列当前值无关。 */
80
100
  function parkGuardConditions(expect) {
81
101
  const out = [];
82
102
  if (expect.status !== undefined)
@@ -91,12 +111,38 @@ function reapInert(opts) {
91
111
  return !opts || (opts.maxAgeMs === undefined && opts.keep === undefined && opts.staleRunningMaxAgeMs === undefined);
92
112
  }
93
113
  function limitClause(limit) {
114
+ // #2 病态边界与 core slice 语义对齐:Infinity=全量(无 LIMIT),NaN/-Infinity=空(LIMIT 0)
94
115
  if (limit === undefined || limit === Infinity)
95
116
  return "";
96
117
  const n = Math.floor(limit);
97
118
  return ` LIMIT ${Number.isFinite(n) ? Math.max(0, n) : 0}`;
98
119
  }
120
+ /* ┌─ 历史:曾经的运行期 ADD COLUMN 迁移腿(已折进 CREATE,2026-07-26 clay 裁「删库重建」)──────────┐
121
+ * 两个 ensure 曾在每次 boot 给 `background_agents` 补三列;现已全部折叠为 CREATE TABLE 里的列定义。
122
+ * 保留由来,因为它解释了这些列**为什么存在**,以及当年为什么 TiDB 腿用探针而不是 `IF NOT EXISTS`:
123
+ *
124
+ * ① root_session_id(core 1.367 δ,additive):1.248 建的存量表没有这列,靠迁移补。
125
+ * ② parked_checkpoint_token / park_claim_id(core 1.383,[1565] 提货单 / design/153 件3a,ε,additive):
126
+ * 1.382 前建的表没有这两列。它们**必须是专用列**(不能只躺在 record_json 里)的理由见 {@link PROJ_COLS}
127
+ * 顶注:`updateIf` 的 CAS 守卫要在数据库层面对当前行状态原子求值。
128
+ *
129
+ * ③ 为什么 TiDB 腿曾是 `information_schema.COLUMNS` **探针 + ALTER** 两步,而不是一条
130
+ * `ADD COLUMN IF NOT EXISTS`(复审 F2):**MySQL 8 没有该语法**(TiDB/MariaDB 才有),而本仓
131
+ * MySQL-protocol 口径要求可移植——tidb-pool.ts COLUMN_MIGRATIONS 同姿势。PG 腿则本来就用 IF NOT EXISTS。
132
+ * ⚠️ 这个探针姿势有一个真实缺陷([1522] LOW5,当年如实记并判定可接受):探针→ALTER **不是原子的**,
133
+ * 多副本并发首 boot 存在竞态窗——两个副本双双探到缺列、一方 ALTER 赢、另一方 ALTER 撞
134
+ * 「column exists」砖**当次** boot;下次 boot 探到列在即自愈。
135
+ * → 折叠后该窗**消失**:新库列由 CREATE TABLE 一次建全,不存在「探到缺列再补」的第二步;
136
+ * `CREATE TABLE IF NOT EXISTS` 本身在服务端是单条原子 DDL,并发首 boot 只有「建了」/「已存在」两态。
137
+ *
138
+ * ④ 语义位:当年 `root_session_id IS NULL` 兼有「pre-δ 行」一义。删库重建后**不再产生**这类行——NULL
139
+ * 只剩「该记录没有 root 锚」一义。读侧因此**仍然可达**、一律不动:{@link rowToSummary} 的
140
+ * root_session_id 条件展开、listBySession 的 `root_session_id = ?` 第二臂(NULL 行恒不命中)、
141
+ * 以及 parked 两列的 `IS NULL` 守卫({@link parkGuardConditions}:那是 core 的 CAS 语义,不是迁移遗留)。
142
+ * └────────────────────────────────────────────────────────────────────────────────────────────┘ */
99
143
  export async function ensureTiDBBackgroundAgentSchema(pool) {
144
+ // VARBINARY 键列(roster F2 同案):scope/handle 的 '=' 必须是纯字节——utf8mb4_bin 仍 PAD SPACE,
145
+ // 'x' 与 'x ' 会同键。record_json LONGTEXT:终报快照(finalOutput/recentSteps)可超 64K TEXT。
100
146
  await pool.query(`CREATE TABLE IF NOT EXISTS ${BACKGROUND_AGENT_TABLE} (
101
147
  scope_key VARBINARY(190) NOT NULL,
102
148
  handle VARBINARY(190) NOT NULL,
@@ -127,6 +173,7 @@ export async function ensureTiDBBackgroundAgentSchema(pool) {
127
173
  ) COLLATE utf8mb4_bin`);
128
174
  }
129
175
  export async function ensurePgBackgroundAgentSchema(q) {
176
+ // COLLATE "C"(roster F2 双方言对齐):隔离键列的 '=' 必须字节等价。
130
177
  await q(`CREATE TABLE IF NOT EXISTS ${BACKGROUND_AGENT_TABLE} (
131
178
  scope_key VARCHAR(190) COLLATE "C" NOT NULL,
132
179
  handle VARCHAR(190) COLLATE "C" NOT NULL,
@@ -183,6 +230,9 @@ export class TiDBBackgroundAgentStore {
183
230
  WHERE scope_key = ? AND handle = ? AND rev = ?`, [...projParams(stored, JSON.stringify, (t) => t), scope, handle, expect.rev]));
184
231
  return res.affectedRows === 1;
185
232
  }
233
+ /** core 1.383([1565] 提货单)REQUIRED 新方法——原子守卫 CAS(claim 的 reservation/rollback/finalize
234
+ * 绑定字段态非仅 rev:rev 可因无关写移动而字段义已变)。语义逐字对齐 core InMemoryBackgroundAgentStore
235
+ * ({@link parkGuardConditions} 顶注)。 */
186
236
  async updateIf(handle, scope, record, expect) {
187
237
  const stored = storedForm(record, handle, scope, expect.rev + 1);
188
238
  const guards = parkGuardConditions(expect);
@@ -206,6 +256,18 @@ export class TiDBBackgroundAgentStore {
206
256
  const [res] = (await this.pool.query(`DELETE FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ? AND handle = ?${expect !== undefined ? " AND rev = ?" : ""}`, expect !== undefined ? [scope, handle, expect.rev] : [scope, handle]));
207
257
  return res.affectedRows === 1;
208
258
  }
259
+ /** 🔴 返回值语义 = **受影响的不同行数**,不是「翻转数 + 删除数」的和(core 1.411 起)。
260
+ *
261
+ * core 1.411 把自己 InMemory 的 `return touched + doomed.size` 改成
262
+ * `return new Set([...flippedKeys, ...doomed]).size` —— 因为一行可能在**同一轮** reap 里既被翻转
263
+ * (stale running → failed)、又因为翻转后变成终态而落进 maxAge/keep 的删除集,旧写法把它算了两次。
264
+ * 这条被真双库对拆套件当场抓到(升 core 1.411 后 `tidb: reaped 计数: expected 4 to be 3`,core 1.404
265
+ * 下同文件全绿 —— A/B 判明是 core 的语义修,我们跟上)。
266
+ *
267
+ * ⚠️ 两个引擎**机制不同但语义一致**:PG 有 `DELETE … RETURNING handle`,可以单语句拿到真被删的 handle
268
+ * 直接并入同一个 Set;TiDB/MySQL **没有 RETURNING**,`affectedRows` 只给数量不给身份,所以按「是否已在
269
+ * 翻转集里」把 doomed 拆两批删 —— 重叠那批照样真删、只是不再计数。两边都不能用「doomed.size 当删除数」
270
+ * 近似:并发写者(真心跳复活)会让 `status <> 'running'` 守卫少删几行,那时 doomed.size 会虚高。 */
209
271
  async reap(scope, now, opts) {
210
272
  if (reapInert(opts))
211
273
  return 0;
@@ -214,14 +276,22 @@ export class TiDBBackgroundAgentStore {
214
276
  const [stale] = (await this.pool.query(`SELECT handle, record_json FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ? AND status = 'running' AND updated_at_ms < ?`, [scope, now - opts.staleRunningMaxAgeMs]));
215
277
  for (const row of stale) {
216
278
  const live = JSON.parse(String(row.record_json));
279
+ // rev-CAS 翻转:并发写者(真心跳复活/别副本先翻)赢了就跳过不计——core 单进程无此窗,SQL 侧诚实
217
280
  if (await this.update(String(row.handle), scope, flippedForm(live, now), { rev: live.rev }))
218
281
  flipped.add(String(row.handle));
219
282
  }
220
283
  }
284
+ // core 1.382([1561] 提货单③;InMemoryBackgroundAgentStore.reap 逐字同源,background-agent-store.js:206
285
+ // `r.status !== "running" && r.status !== "parked"`):doomed 候选池排除 parked——parked 是非活非终态
286
+ // (durably suspended pending approval),`doomedHandles` 只按 spawnedAt/settledAt 判定年龄/keep 排名,
287
+ // 完全不看 status,所以排除必须在这条 SQL WHERE 里做到,不能指望 JS 侧再筛一次(它收到的行里根本
288
+ // 没带 status 字段)。
221
289
  const [term] = (await this.pool.query(`SELECT handle, spawned_at_ms, settled_at_ms FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = ? AND status <> 'running' AND status <> 'parked' ORDER BY spawned_at_ms DESC, handle DESC`, [scope]));
222
290
  const doomed = doomedHandles(term.map((r) => ({ handle: String(r.handle), spawnedAt: Number(r.spawned_at_ms), settledAt: r.settled_at_ms === null ? null : Number(r.settled_at_ms) })), now, opts);
223
291
  if (doomed.size === 0)
224
292
  return flipped.size;
293
+ // 去重(见方法头注):本轮已翻转过的 handle 若同时落进 doomed,删它但**不再计数**。
294
+ // 无 RETURNING ⇒ 拆两批发,靠「哪一批」而不是靠返回的身份来分辨。
225
295
  const alreadyCounted = [...doomed].filter((h) => flipped.has(h));
226
296
  const newlyAffected = [...doomed].filter((h) => !flipped.has(h));
227
297
  const del = async (handles) => {
@@ -230,10 +300,16 @@ export class TiDBBackgroundAgentStore {
230
300
  };
231
301
  const deletedNew = newlyAffected.length > 0 ? await del(newlyAffected) : 0;
232
302
  if (alreadyCounted.length > 0)
233
- await del(alreadyCounted);
303
+ await del(alreadyCounted); // 真删,计数已由 flipped 承担
234
304
  return flipped.size + deletedNew;
235
305
  }
306
+ /** core 1.368([1516]②)可选接口成员落位(原 server 扩展 listReapScopes 更名对齐):当前持行
307
+ * scope 枚举——reaper 的 per-scope 联合 reap 入口。JS 侧 sort 与 core InMemory 逐字节同序
308
+ * (SQL ORDER BY 按 collation,非码元序)。 */
236
309
  async listScopes() {
310
+ // [1522] MED1(两路复审撞车坐实):枚举必须走 **scope_key 字节列**——utf8mb4_bin 的 VARCHAR DISTINCT
311
+ // 仍 PAD SPACE,尾空格近撞 scope 被塌缩成一个,塌缩掉的分区永不被 reap(行/转录无界)。键纪律
312
+ // 贯彻到枚举面;VARBINARY 读回 Buffer → utf8 归一。
237
313
  const [rows] = (await this.pool.query(`SELECT DISTINCT scope_key FROM ${BACKGROUND_AGENT_TABLE}`));
238
314
  return rows.map((r) => (Buffer.isBuffer(r.scope_key) ? r.scope_key.toString("utf8") : String(r.scope_key))).sort();
239
315
  }
@@ -246,6 +322,8 @@ export class PgBackgroundAgentStore {
246
322
  async put(record) {
247
323
  assertScope(record);
248
324
  const stored = storedForm(record, record.handle, record.scope, record.rev ?? 0);
325
+ // create-once:ON CONFLICT DO NOTHING + RETURNING——空 rows = 键已存在(PgQueryFn 无 rowCount,
326
+ // RETURNING 是双方言里 PG 侧的 affected 判别;错误码路径留给真障碍)。
249
327
  const { rows } = await this.q(`INSERT INTO ${BACKGROUND_AGENT_TABLE} (scope_key, handle, ${PROJ_COLS})
250
328
  VALUES (${Array.from({ length: PROJ_N + 2 }, (_, i) => `$${i + 1}`).join(", ")})
251
329
  ON CONFLICT (scope_key, handle) DO NOTHING RETURNING handle`, [stored.scope, stored.handle, ...projParams(stored, pgSafeJsonStringify, pgSanitizeText)]);
@@ -263,11 +341,13 @@ export class PgBackgroundAgentStore {
263
341
  WHERE scope_key = $${PROJ_N + 1} AND handle = $${PROJ_N + 2} AND rev = $${PROJ_N + 3} RETURNING handle`, [...projParams(stored, pgSafeJsonStringify, pgSanitizeText), scope, handle, expect.rev]);
264
342
  return rows.length === 1;
265
343
  }
344
+ /** core 1.383([1565] 提货单)REQUIRED 新方法(TiDB twin 同案注释)。PG 侧占位符是显式编号 `$N`
345
+ * (不像 TiDB `?` 靠数组顺序)——`n` 是跑动计数器,只在真正消耗一个占位符(非 IS NULL 分支)时前增。 */
266
346
  async updateIf(handle, scope, record, expect) {
267
347
  const stored = storedForm(record, handle, scope, expect.rev + 1);
268
348
  const setParams = projParams(stored, pgSafeJsonStringify, pgSanitizeText);
269
349
  const guards = parkGuardConditions(expect);
270
- let n = setParams.length + 3;
350
+ let n = setParams.length + 3; // + scope_key/handle/rev,三条基础守卫已固定占用 setParams.length+1..+3
271
351
  const guardSql = guards.map((g) => (g.value === null ? `${g.column} IS NULL` : `${g.column} = $${++n}`)).join(" AND ");
272
352
  const guardParams = guards.filter((g) => g.value !== null).map((g) => g.value);
273
353
  const { rows } = await this.q(`UPDATE ${BACKGROUND_AGENT_TABLE} SET ${PROJ_COLS.split(", ").map((c, i) => `${c} = $${i + 1}`).join(", ")}
@@ -288,6 +368,9 @@ export class PgBackgroundAgentStore {
288
368
  const { rows } = await this.q(`DELETE FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = $1 AND handle = $2${expect !== undefined ? " AND rev = $3" : ""} RETURNING handle`, expect !== undefined ? [scope, handle, expect.rev] : [scope, handle]);
289
369
  return rows.length === 1;
290
370
  }
371
+ /** 返回值 = 受影响的**不同行数**(core 1.411 语义;TiDB twin 的方法头注写了完整由来 + A/B 取证)。
372
+ * PG 这半有 `DELETE … RETURNING handle`,所以真被删的 handle 直接并进同一个 Set —— 不需要 TiDB 那边的
373
+ * 「拆两批发」变通,但两边的**结果**必须逐个相等(真双库对拆套件按 core InMemory 的计数逐字比)。 */
291
374
  async reap(scope, now, opts) {
292
375
  if (reapInert(opts))
293
376
  return 0;
@@ -300,16 +383,20 @@ export class PgBackgroundAgentStore {
300
383
  affected.add(String(row.handle));
301
384
  }
302
385
  }
386
+ // core 1.382([1561] 提货单③;TiDB twin 同案注释)——doomed 候选池排除 parked。
303
387
  const { rows: term } = await this.q(`SELECT handle, spawned_at_ms, settled_at_ms FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = $1 AND status <> 'running' AND status <> 'parked' ORDER BY spawned_at_ms DESC, handle DESC`, [scope]);
304
388
  const doomed = doomedHandles(term.map((r) => ({ handle: String(r.handle), spawnedAt: Number(r.spawned_at_ms), settledAt: r.settled_at_ms === null ? null : Number(r.settled_at_ms) })), now, opts);
305
389
  if (doomed.size === 0)
306
390
  return affected.size;
307
391
  const { rows: deleted } = await this.q(`DELETE FROM ${BACKGROUND_AGENT_TABLE} WHERE scope_key = $1 AND status <> 'running' AND status <> 'parked' AND handle IN (${[...doomed].map((_, i) => `$${i + 2}`).join(", ")}) RETURNING handle`, [scope, ...doomed]);
308
392
  for (const r of deleted)
309
- affected.add(String(r.handle));
393
+ affected.add(String(r.handle)); // 同一 Set ⇒ 翻转+删除的同一行只算一次
310
394
  return affected.size;
311
395
  }
396
+ /** core 1.368 可选接口成员(TiDB twin 同案注释)。 */
312
397
  async listScopes() {
398
+ // [1522] MED1(TiDB twin 同案注):枚举走 COLLATE "C" 的 scope_key 键列——非确定性缺省 collation
399
+ // 部署下 VARCHAR DISTINCT 同构塌缩,键列字节确定。
313
400
  const { rows } = await this.q(`SELECT DISTINCT scope_key FROM ${BACKGROUND_AGENT_TABLE}`);
314
401
  return rows.map((r) => String(r.scope_key)).sort();
315
402
  }