@sema-agent/server 1.322.0 → 2.0.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 (539) 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-reclaim.d.ts +19 -0
  46. package/dist/boot-reclaim.js +20 -0
  47. package/dist/brain.d.ts +25 -0
  48. package/dist/brain.js +61 -1
  49. package/dist/budget.d.ts +66 -0
  50. package/dist/budget.js +128 -4
  51. package/dist/capabilities/builtin-tools.d.ts +3 -0
  52. package/dist/capabilities/builtin-tools.js +3 -0
  53. package/dist/capabilities/center-plugins.d.ts +17 -1
  54. package/dist/capabilities/center-plugins.js +45 -4
  55. package/dist/capabilities/center-prompts.d.ts +46 -0
  56. package/dist/capabilities/center-prompts.js +57 -3
  57. package/dist/capabilities/code-review-council.d.ts +28 -0
  58. package/dist/capabilities/code-review-council.js +40 -7
  59. package/dist/capabilities/collab-wire.d.ts +2 -0
  60. package/dist/capabilities/collab-workflows.d.ts +7 -0
  61. package/dist/capabilities/collab-workflows.js +44 -1
  62. package/dist/capabilities/oa-tools.d.ts +6 -0
  63. package/dist/capabilities/oa-tools.js +10 -0
  64. package/dist/capabilities/prompt.d.ts +14 -0
  65. package/dist/capabilities/prompt.js +14 -0
  66. package/dist/capabilities/prompts/code-review.d.ts +14 -0
  67. package/dist/capabilities/prompts/code-review.js +14 -0
  68. package/dist/capabilities/prompts/identity.d.ts +6 -0
  69. package/dist/capabilities/prompts/identity.js +6 -0
  70. package/dist/capabilities/prompts/team.d.ts +4 -0
  71. package/dist/capabilities/prompts/team.js +4 -0
  72. package/dist/capabilities/repo-tools.d.ts +11 -0
  73. package/dist/capabilities/repo-tools.js +8 -0
  74. package/dist/capabilities/sandbox-file-send.d.ts +95 -0
  75. package/dist/capabilities/sandbox-file-send.js +91 -3
  76. package/dist/capabilities/scenarios.d.ts +130 -0
  77. package/dist/capabilities/scenarios.js +180 -7
  78. package/dist/capabilities/select-environment-tool.d.ts +7 -0
  79. package/dist/capabilities/select-environment-tool.js +30 -0
  80. package/dist/capabilities/send-user-file-tool.d.ts +33 -0
  81. package/dist/capabilities/send-user-file-tool.js +65 -1
  82. package/dist/capabilities/skills.d.ts +29 -0
  83. package/dist/capabilities/skills.js +13 -2
  84. package/dist/capabilities/team.d.ts +22 -0
  85. package/dist/capabilities/team.js +22 -2
  86. package/dist/capabilities/tool-defer.d.ts +26 -0
  87. package/dist/capabilities/tool-defer.js +3 -0
  88. package/dist/config-center/apply-effective.d.ts +65 -0
  89. package/dist/config-center/apply-effective.js +204 -12
  90. package/dist/config-center/http-client.d.ts +34 -0
  91. package/dist/config-center/http-client.js +68 -0
  92. package/dist/config-center/restart-signal.d.ts +22 -0
  93. package/dist/config-center/restart-signal.js +39 -0
  94. package/dist/config-center/skills-mcp.d.ts +20 -1
  95. package/dist/config-center/skills-mcp.js +42 -7
  96. package/dist/config-center/types.d.ts +92 -1
  97. package/dist/config-lkg.d.ts +12 -0
  98. package/dist/config-lkg.js +41 -0
  99. package/dist/config-provider.d.ts +78 -0
  100. package/dist/config-provider.js +165 -2
  101. package/dist/config-types.d.ts +546 -0
  102. package/dist/config.d.ts +64 -0
  103. package/dist/config.js +455 -60
  104. package/dist/degenerate-instrument.d.ts +38 -0
  105. package/dist/degenerate-instrument.js +55 -3
  106. package/dist/elicitation.d.ts +39 -0
  107. package/dist/elicitation.js +87 -3
  108. package/dist/env-facts.d.ts +61 -0
  109. package/dist/env-facts.js +73 -7
  110. package/dist/fleet/fleet-bus.d.ts +185 -0
  111. package/dist/fleet/fleet-bus.js +333 -27
  112. package/dist/fleet/subagent-tail-bus.d.ts +11 -0
  113. package/dist/fleet/subagent-tail-bus.js +33 -2
  114. package/dist/fleet-client.d.ts +38 -0
  115. package/dist/fleet-client.js +44 -9
  116. package/dist/fleet-lease.d.ts +46 -0
  117. package/dist/fleet-lease.js +73 -5
  118. package/dist/hooks/branch-transcript.d.ts +8 -0
  119. package/dist/hooks/branch-transcript.js +33 -0
  120. package/dist/hooks/cc-stop-prompt.d.ts +42 -0
  121. package/dist/hooks/cc-stop-prompt.js +43 -1
  122. package/dist/hooks/hook-llm.d.ts +15 -0
  123. package/dist/hooks/hook-llm.js +47 -0
  124. package/dist/hooks/hook-runner.d.ts +89 -0
  125. package/dist/hooks/hook-runner.js +327 -18
  126. package/dist/http/idempotency.d.ts +31 -0
  127. package/dist/http/idempotency.js +34 -0
  128. package/dist/http/principal-gate.d.ts +17 -0
  129. package/dist/http/principal-gate.js +35 -3
  130. package/dist/http/route-ctx.d.ts +36 -0
  131. package/dist/http/routes/approvals-assistant.d.ts +25 -0
  132. package/dist/http/routes/approvals-assistant.js +253 -23
  133. package/dist/http/routes/attachments.js +22 -4
  134. package/dist/http/routes/capabilities.d.ts +7 -0
  135. package/dist/http/routes/capabilities.js +202 -1
  136. package/dist/http/routes/fleet.d.ts +6 -0
  137. package/dist/http/routes/fleet.js +94 -5
  138. package/dist/http/routes/images.d.ts +17 -0
  139. package/dist/http/routes/images.js +222 -14
  140. package/dist/http/routes/leader.d.ts +7 -0
  141. package/dist/http/routes/leader.js +10 -0
  142. package/dist/http/routes/memory-policy.d.ts +6 -0
  143. package/dist/http/routes/memory-policy.js +38 -6
  144. package/dist/http/routes/notify-wake.d.ts +10 -0
  145. package/dist/http/routes/notify-wake.js +19 -3
  146. package/dist/http/routes/observability.d.ts +6 -0
  147. package/dist/http/routes/observability.js +19 -2
  148. package/dist/http/routes/runs.d.ts +15 -0
  149. package/dist/http/routes/runs.js +464 -41
  150. package/dist/http/routes/session-sync.d.ts +15 -0
  151. package/dist/http/routes/session-sync.js +229 -19
  152. package/dist/http/routes/sessions-list.d.ts +6 -0
  153. package/dist/http/routes/sessions-list.js +35 -4
  154. package/dist/http/routes/sessions.d.ts +14 -0
  155. package/dist/http/routes/sessions.js +176 -31
  156. package/dist/http/routes/side-query.d.ts +8 -0
  157. package/dist/http/routes/side-query.js +26 -1
  158. package/dist/http/routes/tasks.d.ts +18 -0
  159. package/dist/http/routes/tasks.js +501 -38
  160. package/dist/http/routes/trace-usage.d.ts +10 -0
  161. package/dist/http/routes/trace-usage.js +66 -14
  162. package/dist/http/routes/workflows.d.ts +12 -0
  163. package/dist/http/routes/workflows.js +180 -19
  164. package/dist/http/run-meta.d.ts +11 -0
  165. package/dist/http/run-meta.js +6 -0
  166. package/dist/http/send.d.ts +19 -0
  167. package/dist/http/send.js +11 -0
  168. package/dist/http/server.d.ts +255 -0
  169. package/dist/http/server.js +921 -75
  170. package/dist/http/sse-log.d.ts +15 -0
  171. package/dist/http/sse-log.js +16 -0
  172. package/dist/http/tar.d.ts +9 -0
  173. package/dist/http/tar.js +21 -5
  174. package/dist/http/wire-gate.d.ts +9 -0
  175. package/dist/http/wire-gate.js +9 -0
  176. package/dist/http/wire-types.d.ts +187 -0
  177. package/dist/http/workspace-content.d.ts +8 -0
  178. package/dist/http/workspace-content.js +10 -0
  179. package/dist/images/bake-validate.d.ts +63 -0
  180. package/dist/images/bake-validate.js +71 -1
  181. package/dist/images/manifest.d.ts +19 -0
  182. package/dist/images/manifest.js +6 -1
  183. package/dist/index.d.ts +1 -5
  184. package/dist/index.js +10 -5
  185. package/dist/key-resolver.d.ts +23 -0
  186. package/dist/key-resolver.js +27 -2
  187. package/dist/leader/diffout.d.ts +23 -0
  188. package/dist/leader/diffout.js +8 -1
  189. package/dist/leader/diffup.d.ts +32 -0
  190. package/dist/leader/diffup.js +43 -0
  191. package/dist/leader/endpoint.d.ts +20 -0
  192. package/dist/leader/endpoint.js +25 -2
  193. package/dist/leader/fanout.d.ts +60 -0
  194. package/dist/leader/fanout.js +29 -3
  195. package/dist/leader/grader-env-factory.d.ts +74 -0
  196. package/dist/leader/grader-env-factory.js +62 -3
  197. package/dist/leader/leader.d.ts +97 -0
  198. package/dist/leader/leader.js +151 -4
  199. package/dist/leader/merge.d.ts +94 -0
  200. package/dist/leader/merge.js +112 -9
  201. package/dist/leader/planner.d.ts +52 -0
  202. package/dist/leader/planner.js +47 -3
  203. package/dist/leader/repair-oracle.d.ts +61 -0
  204. package/dist/leader/repair-oracle.js +59 -1
  205. package/dist/leader/repair-wire.d.ts +78 -0
  206. package/dist/leader/repair-wire.js +79 -2
  207. package/dist/leader/wire.d.ts +79 -0
  208. package/dist/leader/wire.js +255 -8
  209. package/dist/lsp/e2b-bridge.d.ts +16 -0
  210. package/dist/lsp/e2b-bridge.js +32 -4
  211. package/dist/lsp/e2b-manager.d.ts +10 -0
  212. package/dist/lsp/e2b-manager.js +18 -2
  213. package/dist/lsp/lsp-frames.d.ts +12 -0
  214. package/dist/lsp/lsp-frames.js +12 -0
  215. package/dist/lsp/manager.d.ts +12 -0
  216. package/dist/lsp/manager.js +35 -2
  217. package/dist/lsp/types.d.ts +10 -0
  218. package/dist/lsp/ws-transport.d.ts +12 -0
  219. package/dist/lsp/ws-transport.js +53 -5
  220. package/dist/lsp-evict.d.ts +14 -0
  221. package/dist/lsp-evict.js +15 -1
  222. package/dist/main.js +2132 -96
  223. package/dist/memory-export.d.ts +1 -0
  224. package/dist/memory-export.js +4 -0
  225. package/dist/memory-scope.d.ts +49 -0
  226. package/dist/memory-scope.js +87 -4
  227. package/dist/memory-sync-client.d.ts +24 -0
  228. package/dist/memory-sync-client.js +41 -2
  229. package/dist/memory-sync.d.ts +23 -0
  230. package/dist/memory-sync.js +70 -1
  231. package/dist/model-select.d.ts +39 -0
  232. package/dist/model-select.js +42 -1
  233. package/dist/observability/cost-quota.d.ts +20 -0
  234. package/dist/observability/cost-quota.js +4 -0
  235. package/dist/observability/cost-taxonomy.d.ts +45 -0
  236. package/dist/observability/cost-taxonomy.js +34 -0
  237. package/dist/observability/logger.d.ts +1 -0
  238. package/dist/observability/logger.js +6 -0
  239. package/dist/observability/metrics.d.ts +13 -0
  240. package/dist/observability/metrics.js +63 -0
  241. package/dist/observability/otel-exporter.d.ts +9 -0
  242. package/dist/observability/otel-exporter.js +11 -3
  243. package/dist/observability/principal-context.d.ts +9 -0
  244. package/dist/observability/principal-context.js +9 -0
  245. package/dist/observability/prompt-manifest.d.ts +37 -0
  246. package/dist/observability/prompt-manifest.js +35 -1
  247. package/dist/observability/rate-limit.d.ts +10 -0
  248. package/dist/observability/rate-limit.js +1 -0
  249. package/dist/observability/tool-trace.d.ts +24 -0
  250. package/dist/observability/tool-trace.js +41 -1
  251. package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
  252. package/dist/orchestration/hardened-vm-runner.js +119 -4
  253. package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
  254. package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
  255. package/dist/orchestration/hardened-vm-worker.js +27 -0
  256. package/dist/orchestration/subagent-steer.d.ts +44 -0
  257. package/dist/orchestration/subagent-steer.js +45 -1
  258. package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
  259. package/dist/orchestration/workflow-agent-steer.js +80 -1
  260. package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
  261. package/dist/orchestration/workflow-completion-inbox.js +283 -32
  262. package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
  263. package/dist/orchestration/workflow-notify-journal.js +187 -13
  264. package/dist/parked-decide.d.ts +61 -0
  265. package/dist/parked-decide.js +58 -1
  266. package/dist/per-task-image.d.ts +54 -0
  267. package/dist/per-task-image.js +41 -0
  268. package/dist/plan-cache-probe.d.ts +35 -0
  269. package/dist/plan-cache-probe.js +18 -3
  270. package/dist/plugins/approval-exemption-store.d.ts +7 -0
  271. package/dist/plugins/approval-exemption-store.js +28 -2
  272. package/dist/plugins/approval-store-sql.d.ts +64 -0
  273. package/dist/plugins/approval-store-sql.js +32 -0
  274. package/dist/plugins/background-agent-store-sql.d.ts +55 -0
  275. package/dist/plugins/background-agent-store-sql.js +91 -4
  276. package/dist/plugins/background-shell-support.d.ts +98 -0
  277. package/dist/plugins/background-shell-support.js +117 -13
  278. package/dist/plugins/blob-backend.d.ts +104 -0
  279. package/dist/plugins/blob-backend.js +163 -6
  280. package/dist/plugins/breaker-state-sql.d.ts +65 -1
  281. package/dist/plugins/breaker-state-sql.js +46 -8
  282. package/dist/plugins/caching-session-store.d.ts +42 -0
  283. package/dist/plugins/caching-session-store.js +63 -3
  284. package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
  285. package/dist/plugins/checkpoint-store-sql.js +239 -11
  286. package/dist/plugins/file-outcome-sink.d.ts +4 -0
  287. package/dist/plugins/file-outcome-sink.js +9 -0
  288. package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
  289. package/dist/plugins/file-resume-anchor-store.js +43 -4
  290. package/dist/plugins/file-run-store.d.ts +90 -0
  291. package/dist/plugins/file-run-store.js +145 -13
  292. package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
  293. package/dist/plugins/file-snapshot-store-sql.js +178 -10
  294. package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
  295. package/dist/plugins/file-workflow-journal-store.js +10 -0
  296. package/dist/plugins/fork-routing-session-store.d.ts +47 -0
  297. package/dist/plugins/fork-routing-session-store.js +51 -3
  298. package/dist/plugins/host-platform.d.ts +69 -0
  299. package/dist/plugins/host-platform.js +81 -2
  300. package/dist/plugins/image-bake-store-sql.d.ts +136 -1
  301. package/dist/plugins/image-bake-store-sql.js +241 -7
  302. package/dist/plugins/image-index-sql.d.ts +46 -0
  303. package/dist/plugins/image-index-sql.js +113 -4
  304. package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
  305. package/dist/plugins/k8s-bg-scripts.js +88 -4
  306. package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
  307. package/dist/plugins/k8s-exec-protocol.js +41 -0
  308. package/dist/plugins/local-checkpoint-store.d.ts +40 -0
  309. package/dist/plugins/local-checkpoint-store.js +86 -7
  310. package/dist/plugins/local-session-store.d.ts +157 -0
  311. package/dist/plugins/local-session-store.js +289 -34
  312. package/dist/plugins/local-task-attachment-store.js +14 -2
  313. package/dist/plugins/mailbox-store-sql.d.ts +38 -0
  314. package/dist/plugins/mailbox-store-sql.js +39 -6
  315. package/dist/plugins/memory-engine-pg.d.ts +30 -0
  316. package/dist/plugins/memory-engine-pg.js +122 -4
  317. package/dist/plugins/memory-engine-tidb.d.ts +26 -0
  318. package/dist/plugins/memory-engine-tidb.js +115 -5
  319. package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
  320. package/dist/plugins/memory-engine-vector-util.js +5 -0
  321. package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
  322. package/dist/plugins/memory-resume-anchor-store.js +17 -0
  323. package/dist/plugins/memory-run-store.d.ts +63 -0
  324. package/dist/plugins/memory-run-store.js +73 -8
  325. package/dist/plugins/memory-session-policy-store.d.ts +17 -0
  326. package/dist/plugins/memory-session-policy-store.js +17 -0
  327. package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
  328. package/dist/plugins/memory-sync-store-pg.js +45 -2
  329. package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
  330. package/dist/plugins/memory-sync-store-tidb.js +31 -1
  331. package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
  332. package/dist/plugins/outcome-ledger-sql.js +97 -3
  333. package/dist/plugins/pg-approval-store.d.ts +7 -0
  334. package/dist/plugins/pg-approval-store.js +7 -0
  335. package/dist/plugins/pg-breaker-state.d.ts +6 -0
  336. package/dist/plugins/pg-breaker-state.js +6 -0
  337. package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
  338. package/dist/plugins/pg-checkpoint-store.js +8 -0
  339. package/dist/plugins/pg-cost-quota.d.ts +17 -0
  340. package/dist/plugins/pg-cost-quota.js +6 -0
  341. package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
  342. package/dist/plugins/pg-file-snapshot-store.js +6 -0
  343. package/dist/plugins/pg-image-bake.d.ts +9 -0
  344. package/dist/plugins/pg-image-bake.js +9 -0
  345. package/dist/plugins/pg-image-index.d.ts +9 -0
  346. package/dist/plugins/pg-image-index.js +9 -0
  347. package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
  348. package/dist/plugins/pg-outcome-ledger.js +9 -0
  349. package/dist/plugins/pg-pool.d.ts +50 -0
  350. package/dist/plugins/pg-pool.js +78 -0
  351. package/dist/plugins/pg-query.d.ts +11 -0
  352. package/dist/plugins/pg-rate-limiter.d.ts +23 -0
  353. package/dist/plugins/pg-rate-limiter.js +12 -2
  354. package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
  355. package/dist/plugins/pg-resume-anchor-store.js +5 -0
  356. package/dist/plugins/pg-run-store.d.ts +7 -0
  357. package/dist/plugins/pg-run-store.js +7 -0
  358. package/dist/plugins/pg-safe-json.d.ts +13 -0
  359. package/dist/plugins/pg-safe-json.js +40 -4
  360. package/dist/plugins/pg-session-policy-store.d.ts +5 -0
  361. package/dist/plugins/pg-session-policy-store.js +5 -0
  362. package/dist/plugins/pg-session-storage.d.ts +72 -0
  363. package/dist/plugins/pg-session-storage.js +152 -20
  364. package/dist/plugins/pg-session-store.d.ts +10 -0
  365. package/dist/plugins/pg-session-store.js +10 -0
  366. package/dist/plugins/pg-tool-result-store.d.ts +7 -0
  367. package/dist/plugins/pg-tool-result-store.js +7 -0
  368. package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
  369. package/dist/plugins/pg-workflow-journal-store.js +7 -0
  370. package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
  371. package/dist/plugins/pg-workflow-run-store.js +7 -0
  372. package/dist/plugins/posix-shell-fs.d.ts +3 -0
  373. package/dist/plugins/posix-shell-fs.js +31 -1
  374. package/dist/plugins/remote-env-adb.d.ts +25 -0
  375. package/dist/plugins/remote-env-adb.js +95 -12
  376. package/dist/plugins/remote-env-e2b.d.ts +146 -0
  377. package/dist/plugins/remote-env-e2b.js +371 -40
  378. package/dist/plugins/remote-env-file-error.d.ts +34 -0
  379. package/dist/plugins/remote-env-file-error.js +37 -0
  380. package/dist/plugins/remote-env-host.d.ts +151 -0
  381. package/dist/plugins/remote-env-host.js +468 -70
  382. package/dist/plugins/remote-env-k8s.d.ts +135 -0
  383. package/dist/plugins/remote-env-k8s.js +318 -32
  384. package/dist/plugins/remote-env-local-docker.d.ts +65 -0
  385. package/dist/plugins/remote-env-local-docker.js +149 -24
  386. package/dist/plugins/remote-env-ssh.d.ts +35 -0
  387. package/dist/plugins/remote-env-ssh.js +117 -20
  388. package/dist/plugins/remote-scratchpad.d.ts +38 -0
  389. package/dist/plugins/remote-scratchpad.js +32 -2
  390. package/dist/plugins/remote-shell.d.ts +22 -0
  391. package/dist/plugins/remote-shell.js +27 -1
  392. package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
  393. package/dist/plugins/resume-anchor-store-sql.js +11 -0
  394. package/dist/plugins/roster-store-sql.d.ts +27 -0
  395. package/dist/plugins/roster-store-sql.js +54 -0
  396. package/dist/plugins/run-store-sql.d.ts +194 -0
  397. package/dist/plugins/run-store-sql.js +210 -9
  398. package/dist/plugins/s3-presign.d.ts +20 -0
  399. package/dist/plugins/s3-presign.js +49 -2
  400. package/dist/plugins/scheduler-support.d.ts +12 -0
  401. package/dist/plugins/scheduler-support.js +75 -3
  402. package/dist/plugins/send-file-ledger.d.ts +15 -0
  403. package/dist/plugins/send-file-ledger.js +53 -4
  404. package/dist/plugins/send-user-file.d.ts +77 -0
  405. package/dist/plugins/send-user-file.js +92 -4
  406. package/dist/plugins/session-policy-store-sql.d.ts +15 -0
  407. package/dist/plugins/session-policy-store-sql.js +75 -3
  408. package/dist/plugins/session-store.d.ts +45 -0
  409. package/dist/plugins/session-store.js +51 -0
  410. package/dist/plugins/sql-driver.d.ts +77 -0
  411. package/dist/plugins/sql-driver.js +4 -0
  412. package/dist/plugins/sql-escape.d.ts +8 -0
  413. package/dist/plugins/sql-escape.js +8 -0
  414. package/dist/plugins/sql-row-helpers.d.ts +25 -0
  415. package/dist/plugins/sql-row-helpers.js +25 -0
  416. package/dist/plugins/store-backend.d.ts +103 -0
  417. package/dist/plugins/store-backend.js +120 -27
  418. package/dist/plugins/store-contracts.d.ts +62 -0
  419. package/dist/plugins/store-contracts.js +3 -0
  420. package/dist/plugins/task-attachment-store.d.ts +60 -0
  421. package/dist/plugins/task-attachment-store.js +49 -3
  422. package/dist/plugins/task-list-store-sql.d.ts +25 -0
  423. package/dist/plugins/task-list-store-sql.js +21 -0
  424. package/dist/plugins/tidb-approval-store.d.ts +6 -0
  425. package/dist/plugins/tidb-approval-store.js +6 -0
  426. package/dist/plugins/tidb-breaker-state.d.ts +5 -0
  427. package/dist/plugins/tidb-breaker-state.js +5 -0
  428. package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
  429. package/dist/plugins/tidb-checkpoint-store.js +7 -0
  430. package/dist/plugins/tidb-cost-quota.d.ts +6 -0
  431. package/dist/plugins/tidb-cost-quota.js +1 -0
  432. package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
  433. package/dist/plugins/tidb-file-snapshot-store.js +6 -0
  434. package/dist/plugins/tidb-image-bake.d.ts +8 -0
  435. package/dist/plugins/tidb-image-bake.js +8 -0
  436. package/dist/plugins/tidb-image-index.d.ts +8 -0
  437. package/dist/plugins/tidb-image-index.js +8 -0
  438. package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
  439. package/dist/plugins/tidb-outcome-ledger.js +10 -0
  440. package/dist/plugins/tidb-pool.d.ts +56 -0
  441. package/dist/plugins/tidb-pool.js +181 -1
  442. package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
  443. package/dist/plugins/tidb-rate-limiter.js +5 -2
  444. package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
  445. package/dist/plugins/tidb-resume-anchor-store.js +5 -0
  446. package/dist/plugins/tidb-run-store.d.ts +7 -0
  447. package/dist/plugins/tidb-run-store.js +7 -0
  448. package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
  449. package/dist/plugins/tidb-session-policy-store.js +5 -0
  450. package/dist/plugins/tidb-session-storage.d.ts +43 -0
  451. package/dist/plugins/tidb-session-storage.js +62 -3
  452. package/dist/plugins/tidb-session-store.d.ts +142 -0
  453. package/dist/plugins/tidb-session-store.js +264 -16
  454. package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
  455. package/dist/plugins/tidb-tool-result-store.js +7 -0
  456. package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
  457. package/dist/plugins/tidb-workflow-journal-store.js +7 -0
  458. package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
  459. package/dist/plugins/tidb-workflow-run-store.js +8 -0
  460. package/dist/plugins/tool-result-store-sql.d.ts +58 -0
  461. package/dist/plugins/tool-result-store-sql.js +38 -2
  462. package/dist/plugins/web-search.d.ts +35 -0
  463. package/dist/plugins/web-search.js +45 -9
  464. package/dist/plugins/workflow-journal-limits.d.ts +10 -0
  465. package/dist/plugins/workflow-journal-limits.js +10 -0
  466. package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
  467. package/dist/plugins/workflow-journal-store-sql.js +59 -3
  468. package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
  469. package/dist/plugins/workflow-run-store-sql.js +84 -6
  470. package/dist/plugins/worktree-isolation.d.ts +45 -0
  471. package/dist/plugins/worktree-isolation.js +105 -6
  472. package/dist/plugins/write-behind-counter.d.ts +56 -1
  473. package/dist/plugins/write-behind-counter.js +53 -14
  474. package/dist/principal-jwt.d.ts +24 -0
  475. package/dist/principal-jwt.js +60 -5
  476. package/dist/project-memory.d.ts +22 -0
  477. package/dist/project-memory.js +144 -15
  478. package/dist/prompts-domain-validate.d.ts +28 -0
  479. package/dist/prompts-domain-validate.js +42 -1
  480. package/dist/question.d.ts +33 -0
  481. package/dist/question.js +69 -2
  482. package/dist/resource-suspend.d.ts +21 -0
  483. package/dist/resource-suspend.js +16 -0
  484. package/dist/router/route-orchestration.d.ts +78 -0
  485. package/dist/router/route-orchestration.js +77 -0
  486. package/dist/run-local.d.ts +32 -0
  487. package/dist/run-local.js +172 -8
  488. package/dist/runs.d.ts +189 -4
  489. package/dist/runs.js +465 -24
  490. package/dist/runtime-caps-resolver.d.ts +106 -0
  491. package/dist/runtime-caps-resolver.js +89 -4
  492. package/dist/runtime-governance.d.ts +84 -0
  493. package/dist/runtime-governance.js +127 -1
  494. package/dist/sandbox-pkg-source.d.ts +23 -0
  495. package/dist/sandbox-pkg-source.js +37 -0
  496. package/dist/sealed-key.d.ts +54 -0
  497. package/dist/sealed-key.js +68 -3
  498. package/dist/security.d.ts +108 -0
  499. package/dist/security.js +145 -6
  500. package/dist/sema-registry.d.ts +21 -0
  501. package/dist/sema-registry.js +21 -0
  502. package/dist/session-leaf-bus.d.ts +31 -0
  503. package/dist/session-leaf-bus.js +32 -0
  504. package/dist/session-sync-content.d.ts +46 -0
  505. package/dist/session-sync-content.js +47 -1
  506. package/dist/session-sync-kernel.d.ts +89 -0
  507. package/dist/session-sync-kernel.js +48 -3
  508. package/dist/session-sync.d.ts +125 -0
  509. package/dist/session-sync.js +181 -8
  510. package/dist/session-titler.d.ts +26 -0
  511. package/dist/session-titler.js +41 -7
  512. package/dist/session-watch.d.ts +53 -1
  513. package/dist/session-watch.js +73 -9
  514. package/dist/sighup-idle.d.ts +30 -0
  515. package/dist/sighup-idle.js +12 -1
  516. package/dist/spec-fields.d.ts +91 -3
  517. package/dist/spec-fields.js +116 -4
  518. package/dist/task-cwd.d.ts +50 -0
  519. package/dist/task-cwd.js +68 -1
  520. package/dist/task-mcp.d.ts +47 -0
  521. package/dist/task-mcp.js +29 -0
  522. package/dist/task-settings.d.ts +175 -0
  523. package/dist/task-settings.js +226 -11
  524. package/dist/task-workflow.d.ts +67 -0
  525. package/dist/task-workflow.js +75 -8
  526. package/dist/tool-approval.d.ts +91 -0
  527. package/dist/tool-approval.js +215 -0
  528. package/dist/trace/artifacts.d.ts +9 -0
  529. package/dist/trace/artifacts.js +30 -5
  530. package/dist/trace/core-keyset-guard.d.ts +18 -0
  531. package/dist/trace/ledger-sink.d.ts +43 -0
  532. package/dist/trace/ledger-sink.js +64 -5
  533. package/dist/trace/project.d.ts +133 -0
  534. package/dist/trace/project.js +189 -2
  535. package/dist/trace/redact.d.ts +19 -0
  536. package/dist/trace/redact.js +66 -9
  537. package/dist/usage-analytics.d.ts +26 -0
  538. package/dist/usage-analytics.js +26 -3
  539. package/package.json +1 -1
@@ -2,38 +2,75 @@ import { BaseSessionStorage, type PromptEpochDescriptor, type AcquiredSession, t
2
2
  import type { Pool, PoolClient } from "pg";
3
3
  import type { SessionSummary } from "./store-contracts.js";
4
4
  import { type StagingHandle } from "../session-sync-kernel.js";
5
+ /**
6
+ * PG-dialect DDL for the L1 session log, translated from tidb-pool.ts SCHEMA_STATEMENTS:
7
+ * JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3), inline `KEY`/`UNIQUE KEY`→separate CREATE INDEX (UNIQUE for uk_entry).
8
+ *
9
+ * SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
10
+ * drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead.
11
+ */
5
12
  export declare const PG_SESSION_SCHEMA: string[];
13
+ /** Idempotent schema apply for this store's tables (the test calls this; production aggregation is separate). */
6
14
  export declare function ensureSchema(pool: Pool | PoolClient): Promise<void>;
15
+ /**
16
+ * PostgreSQL-backed {@link BaseSessionStorage} — the durable L1 event log for one session.
17
+ *
18
+ * Behaviourally identical to {@link TiDBSessionStorage}; see that file's class doc for the F2/F3 seams. The only
19
+ * differences are dialectal (placeholders, `IS NOT DISTINCT FROM`, jsonb, rowCount, transaction handle).
20
+ */
7
21
  export declare class PgSessionStorage extends BaseSessionStorage {
8
22
  private readonly pool;
9
23
  private readonly sessionId;
10
24
  private readonly floorSeq;
11
25
  constructor(pool: Pool, sessionId: string, metadata: SessionMetadata, entries: SessionTreeEntry[], leafId: string | null, floorEntryId?: string | null, floorSeq?: number);
26
+ /** @see TiDBSessionStorage.getEpochAnchor — [976] S3 RB-14 pull seam, PG dialect twin
27
+ * (keyset-paged to exhaustion: a bounded scan over restatement-less pre-epoch compactions would
28
+ * misreport an epoch-bearing session as pre-epoch — codex HIGH). */
12
29
  getEpochAnchor(): Promise<PromptEpochDescriptor | undefined>;
30
+ /** @see TiDBSessionStorage.wake — F3 bounded `[floor..leaf]` rebuild; null if the session does not exist. */
13
31
  static wake(pool: Pool, sessionId: string): Promise<PgSessionStorage | null>;
32
+ /** The bounded-window floor = last compaction's firstKeptEntryId (and its seq). @see TiDBSessionStorage.computeFloor */
14
33
  private static computeFloor;
34
+ /** Sub-floor lookups (e.g. a label target) aren't in the loaded tail — fall back to the durable log. */
15
35
  getEntry(id: string): Promise<SessionTreeEntry | undefined>;
16
36
  appendEntry(entry: SessionTreeEntry, opts?: SessionWriteOptions): Promise<void>;
17
37
  setLeafId(leafId: string | null, opts?: SessionWriteOptions): Promise<void>;
38
+ /**
39
+ * Atomically: CAS the meta leaf (+bump leaf_seq), then append the event at the new seq.
40
+ * The `(session_id, seq)` primary key is the backstop — a racing writer that grabbed the same
41
+ * seq makes our INSERT fail with a duplicate-key (23505), which we translate to a conflict too.
42
+ */
18
43
  private persist;
19
44
  }
45
+ /**
46
+ * PostgreSQL-backed {@link SessionStore} — the PG twin of {@link TiDBSessionStore}. Maps the
47
+ * `SessionStore` surface (acquire / register / ownerOf / touch / release) onto {@link PgSessionStorage}.
48
+ */
20
49
  export declare class PgSessionStore implements SessionStore {
21
50
  private readonly pool;
51
+ /** In-flight acquisitions keyed by id, so concurrent acquire(sameId) in one process share one. */
22
52
  private readonly pending;
23
53
  constructor(pool: Pool);
24
54
  acquire(sessionId?: string, opts?: {
25
55
  requireExisting?: boolean;
26
56
  }): Promise<AcquiredSession>;
27
57
  private load;
58
+ /** Idempotently create a session_meta row with an owner. Existing owner is never overwritten. */
28
59
  register(sessionId: string, owner: string | null): Promise<void>;
60
+ /** Owner principal of a session, `null` if registered ownerless, `undefined` if no such session. */
29
61
  ownerOf(sessionId: string): Promise<string | null | undefined>;
30
62
  touch(sessionId: string): Promise<void>;
63
+ /** E18 — the session's current leaf SessionTreeEntry.id (session_meta.leaf_id). @see TiDBSessionStore.getLeafId
64
+ * (single SELECT, no acquire/lock, cache-bypassing — safe to read mid-run for the resume-at turn-capture). */
31
65
  getLeafId(sessionId: string): Promise<string | null>;
66
+ /** [1196] 六轮复审:owner+leafId 单行原子快照(@see TiDBSessionStore.getHead——混世代组合防)。 */
32
67
  getHead(sessionId: string): Promise<{
33
68
  owner: string | null;
34
69
  leafId: string | null;
35
70
  } | undefined>;
71
+ /** Durable history is retained; just drop any in-flight cache entry. */
36
72
  release(sessionId: string): Promise<void>;
73
+ /** @see TiDBSessionStore.listSessions — session identity from session_meta (owner-filtered), task_run LEFT JOIN for preview. */
37
74
  listSessions(opts: {
38
75
  owner?: string;
39
76
  cursor?: {
@@ -43,21 +80,56 @@ export declare class PgSessionStore implements SessionStore {
43
80
  limit: number;
44
81
  q?: string;
45
82
  }): Promise<SessionSummary[]>;
83
+ /** @see TiDBSessionStore.probeTitle — the PG twin. */
46
84
  probeTitle(sessionId: string): Promise<"none" | "untitled" | "titled">;
85
+ /** @see TiDBSessionStore.setTitleIfNull — the PG twin (write-once auto-title). */
47
86
  setTitleIfNull(sessionId: string, title: string): Promise<boolean>;
87
+ /** @see TiDBSessionStore.fork — copy the WHOLE durable history (full session_event log, compaction floor bypassed,
88
+ * NOT wake's bounded tail) to a NEW uuidv7 session, re-seq dense from 0, ids/parents verbatim, owner=caller. */
48
89
  fork(sourceId: string, owner: string | null): Promise<string | null>;
90
+ /** @see TiDBSessionStore.exportEntries — 2c session-sync EXPORT: the FULL durable session_event log (compaction floor
91
+ * bypassed, NOT wake's tail) as `SessionTreeEntry[]`, oldest-first, verbatim. null if the source is unknown. */
49
92
  exportEntries(sessionId: string): Promise<SessionTreeEntry[] | null>;
93
+ /** @see TiDBSessionStore.listEntryIds — 2c P1d-α (PULL streaming): the IDS-ONLY projection of the full durable log,
94
+ * oldest-first (`SELECT entry_id … ORDER BY seq ASC`), no payloads. null if the source is unknown. */
50
95
  listEntryIds(sessionId: string): Promise<string[] | null>;
96
+ /** @see TiDBSessionStore.exportEntriesStream — 2c P1d-α (PULL streaming): KEYSET-paged async generator over the full
97
+ * durable log (node-pg also buffers a plain SELECT → page it). `… WHERE seq > cursor ORDER BY seq ASC LIMIT K`
98
+ * (K = batchSize ?? 500), advancing the cursor to the last seq each batch, ONE pooled `pool.query` per batch (no
99
+ * held connection across the stream → a slow consumer can't pin one). Start cursor = afterSeq ?? -1 (0-based dense).
100
+ * Resolves to null (NOT an empty iterable) for a missing session — eager `session_meta` probe (BIGINT seq comes
101
+ * back as a STRING in node-pg → always Number(...) the cursor advance). */
51
102
  exportEntriesStream(sessionId: string, opts?: {
52
103
  afterSeq?: number;
53
104
  batchSize?: number;
54
105
  }): Promise<AsyncIterable<SessionTreeEntry> | null>;
106
+ /** @see TiDBSessionStore.importEntries — 2c session-sync IMPORT: core `validateEntriesForImport` gate FIRST (fail-closed),
107
+ * then persist its verbatim output (re-seq dense from 0, ids/parents/payload/ts unchanged) in ONE txn with
108
+ * session_meta LAST as the commit point; owner = the RE-STAMPED authenticated principal (never the bundle's). */
55
109
  importEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
110
+ /** @see TiDBSessionStore.replaceEntries — 2c (§7/§8) IDEMPOTENT REPLACE: core `validateEntriesForImport` gate FIRST
111
+ * (fail-closed), then DELETE events + the owner-guarded meta delete (`owner IS NOT DISTINCT FROM $n` = the PG `<=>`,
112
+ * defense-in-depth), then re-insert via the SHARED body (entries + session_meta LAST as the commit point), ALL in
113
+ * ONE txn. Safe to run OVER an existing session (fast-forward / §8 retry) — unlike importEntries' fresh-only INSERT. */
56
114
  replaceEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
115
+ /** @see TiDBSessionStore.insertValidatedLog — shared insert half of importEntries / replaceEntries (ONE source, no
116
+ * drift): entries re-seq dense from 0 (ids/parents/payload/ts verbatim), THEN session_meta LAST as the commit point.
117
+ * Runs inside the caller's open txn (`conn` already in BEGIN); the caller owns COMMIT/ROLLBACK + the validate gate. */
57
118
  private insertValidatedLog;
119
+ /** @see TiDBSessionStore.beginImportStaging — 2c P1d-β PUSH staging: stream batches into the shadow id
120
+ * `${realSessionId}#stg-${token}` (route-minted token), then commit (atomic swap) / abort. */
58
121
  beginImportStaging(realSessionId: string, token: string): StagingHandle;
122
+ /** @see TiDBSessionStore.readStagedEntries — 2c P1d-β staged-row inspection: the staged rows under `stagingId`, oldest-
123
+ * first, no session_meta probe (a staging id has no meta row). No longer the import gate (the per-line
124
+ * StreamingImportValidator validates during the stream); a tests/diagnostics seam. Returns [] for an empty/unknown id. */
59
125
  readStagedEntries(stagingId: string): Promise<SessionTreeEntry[]>;
126
+ /** @see TiDBSessionStore.sweepStagingSessions — reaper for abandoned staging sessions: DELETE session_event rows
127
+ * under a `%#stg-%` id with NO session_meta whose oldest row is older than the grace window (in-flight stays fresh).
128
+ * Returns the affected row count. Driven by main.ts's reaper. */
60
129
  sweepStagingSessions(): Promise<number>;
130
+ /** @see TiDBSessionStore.deleteSession — purge session_meta + session_event; idempotent (false if absent).
131
+ * Owner-guarded on session_meta.owner (`IS NOT DISTINCT FROM` = the PG `<=>`); the event purge is gated on the
132
+ * owner-matched meta delete (session_event has no owner column). */
61
133
  deleteSession(sessionId: string, owner: string | null): Promise<boolean>;
62
134
  get size(): number;
63
135
  dispose(): void;
@@ -1,16 +1,45 @@
1
+ /**
2
+ * PostgreSQL-backed {@link BaseSessionStorage} — the PG twin of {@link TiDBSessionStorage}
3
+ * (src/plugins/tidb-session-storage.ts; clay 2026-06-21, the second session backend).
4
+ *
5
+ * A behavioural TWIN: identical class/method surface + identical semantics (the F2 optimistic-lock CAS on
6
+ * `leaf_id`/`leaf_seq`, the F3 bounded `[floor..leaf]` wake, the sub-floor durable getEntry fallback), only the
7
+ * SQL dialect differs. Kept in lock-step with the TiDB store by ONE shared real-DB integration suite
8
+ * (test/pg-session-storage-integration.test.ts) that drives the SAME scenarios against BOTH engines. Dialect deltas:
9
+ * - placeholders `$1..$n` (not `?`) - affected rows = `result.rowCount` (not affectedRows)
10
+ * - `leaf_id IS NOT DISTINCT FROM $n` (not `<=>`) - dup-key SQLSTATE `23505` (not ER_DUP_ENTRY)
11
+ * - `payload` is `jsonb`, bound `$n::jsonb`; node-pg PARSES jsonb → object on read (parseJson passes objects through)
12
+ * - BIGINT (`seq`/`leaf_seq`) comes back as a STRING → always `Number(...)`
13
+ * - transaction via `pool.connect()` + BEGIN/COMMIT/ROLLBACK/release (not getConnection/beginTransaction)
14
+ *
15
+ * Unlike the run/checkpoint PG stores, this store carries its OWN schema (PG_SESSION_SCHEMA + ensureSchema): the
16
+ * session_meta/session_event tables are DISJOINT from the centrally-owned run-store/checkpoint tables, so a
17
+ * self-contained CREATE IF NOT EXISTS cannot shadow another store's table. (Central aggregation into pg-pool is
18
+ * done separately afterward.)
19
+ */
1
20
  import { emitLeafAdvance } from "../session-leaf-bus.js";
21
+ // 🔴 core 1.403 迁移(2026-07-25):runtime 构造用法,`Session` → `StoredSession`(见 audit.ts 同款注释)。
2
22
  import { BaseSessionStorage, StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry, normalizePromptEpoch, } from "@sema-agent/core";
3
23
  import { escapeLike } from "./sql-escape.js";
4
24
  import { pgSafeJsonStringify } from "./pg-safe-json.js";
5
25
  import { toIso } from "./sql-row-helpers.js";
6
26
  import { classifySyncRelationshipByIds, SyncConflictError, stagingIdFor, STAGING_ID_MARKER, } from "../session-sync-kernel.js";
27
+ /** Staging-row reaper grace window (twin of the TiDB store's STAGING_GC_GRACE_MS) — see that store for the rationale. */
7
28
  const STAGING_GC_GRACE_MS = 3_600_000;
8
29
  function isDupKey(err) {
9
- return Boolean(err) && err.code === "23505";
30
+ return Boolean(err) && err.code === "23505"; // PG unique_violation
10
31
  }
32
+ /** node-pg parses jsonb → object already; only strings need JSON.parse, everything else passes through. */
11
33
  function parseEntry(p) {
12
34
  return (typeof p === "string" ? JSON.parse(p) : p);
13
35
  }
36
+ /**
37
+ * PG-dialect DDL for the L1 session log, translated from tidb-pool.ts SCHEMA_STATEMENTS:
38
+ * JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3), inline `KEY`/`UNIQUE KEY`→separate CREATE INDEX (UNIQUE for uk_entry).
39
+ *
40
+ * SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
41
+ * drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead.
42
+ */
14
43
  export const PG_SESSION_SCHEMA = [
15
44
  `CREATE TABLE IF NOT EXISTS session_meta (
16
45
  session_id VARCHAR(64) NOT NULL,
@@ -37,10 +66,17 @@ export const PG_SESSION_SCHEMA = [
37
66
  )`,
38
67
  `CREATE UNIQUE INDEX IF NOT EXISTS uk_session_event_entry ON session_event (session_id, entry_id)`,
39
68
  ];
69
+ /** Idempotent schema apply for this store's tables (the test calls this; production aggregation is separate). */
40
70
  export async function ensureSchema(pool) {
41
71
  for (const stmt of PG_SESSION_SCHEMA)
42
72
  await pool.query(stmt);
43
73
  }
74
+ /**
75
+ * PostgreSQL-backed {@link BaseSessionStorage} — the durable L1 event log for one session.
76
+ *
77
+ * Behaviourally identical to {@link TiDBSessionStorage}; see that file's class doc for the F2/F3 seams. The only
78
+ * differences are dialectal (placeholders, `IS NOT DISTINCT FROM`, jsonb, rowCount, transaction handle).
79
+ */
44
80
  export class PgSessionStorage extends BaseSessionStorage {
45
81
  pool;
46
82
  sessionId;
@@ -51,14 +87,17 @@ export class PgSessionStorage extends BaseSessionStorage {
51
87
  this.sessionId = sessionId;
52
88
  this.floorSeq = floorSeq;
53
89
  }
90
+ /** @see TiDBSessionStorage.getEpochAnchor — [976] S3 RB-14 pull seam, PG dialect twin
91
+ * (keyset-paged to exhaustion: a bounded scan over restatement-less pre-epoch compactions would
92
+ * misreport an epoch-bearing session as pre-epoch — codex HIGH). */
54
93
  async getEpochAnchor() {
55
94
  if (this.floorSeq <= 0)
56
- return undefined;
95
+ return undefined; // full load — the walk already reaches the root pin
57
96
  let cursor = this.floorSeq;
58
97
  for (;;) {
59
98
  const res = await this.pool.query("SELECT seq, payload FROM session_event WHERE session_id = $1 AND seq < $2 AND type IN ('prompt_epoch','compaction') ORDER BY seq DESC LIMIT 100", [this.sessionId, cursor]);
60
99
  if (res.rows.length === 0)
61
- return undefined;
100
+ return undefined; // exhausted — a genuine pre-epoch session
62
101
  for (const r of res.rows) {
63
102
  const e = parseEntry(r.payload);
64
103
  const d = e.type === "prompt_epoch" ? normalizePromptEpoch(e) : normalizePromptEpoch(e.details?.promptEpoch);
@@ -68,6 +107,7 @@ export class PgSessionStorage extends BaseSessionStorage {
68
107
  cursor = Number(res.rows[res.rows.length - 1].seq);
69
108
  }
70
109
  }
110
+ /** @see TiDBSessionStorage.wake — F3 bounded `[floor..leaf]` rebuild; null if the session does not exist. */
71
111
  static async wake(pool, sessionId) {
72
112
  const metaRes = await pool.query("SELECT created_at, leaf_id, leaf_seq FROM session_meta WHERE session_id = $1", [sessionId]);
73
113
  const meta = metaRes.rows[0];
@@ -79,6 +119,7 @@ export class PgSessionStorage extends BaseSessionStorage {
79
119
  const metadata = { id: sessionId, createdAt: toIso(meta.created_at) };
80
120
  return new PgSessionStorage(pool, sessionId, metadata, entries, meta.leaf_id ?? null, floorEntryId, floorSeq);
81
121
  }
122
+ /** The bounded-window floor = last compaction's firstKeptEntryId (and its seq). @see TiDBSessionStorage.computeFloor */
82
123
  static async computeFloor(pool, sessionId) {
83
124
  const compRes = await pool.query("SELECT payload FROM session_event WHERE session_id = $1 AND type = 'compaction' ORDER BY seq DESC LIMIT 1", [sessionId]);
84
125
  if (!compRes.rows[0])
@@ -88,10 +129,12 @@ export class PgSessionStorage extends BaseSessionStorage {
88
129
  if (!fk)
89
130
  return { floorEntryId: null, floorSeq: 0 };
90
131
  const seqRes = await pool.query("SELECT seq FROM session_event WHERE session_id = $1 AND entry_id = $2", [sessionId, fk]);
132
+ // firstKeptEntryId missing from the log (shouldn't happen) → degrade to full load.
91
133
  if (!seqRes.rows[0])
92
134
  return { floorEntryId: null, floorSeq: 0 };
93
135
  return { floorEntryId: fk, floorSeq: Number(seqRes.rows[0].seq) };
94
136
  }
137
+ /** Sub-floor lookups (e.g. a label target) aren't in the loaded tail — fall back to the durable log. */
95
138
  async getEntry(id) {
96
139
  const inMem = await super.getEntry(id);
97
140
  if (inMem)
@@ -107,10 +150,16 @@ export class PgSessionStorage extends BaseSessionStorage {
107
150
  this.recordEntry(entry);
108
151
  }
109
152
  async setLeafId(leafId, opts) {
153
+ // A leaf move is itself a persisted `leaf` entry whose parent is the current leaf.
110
154
  const leafEntry = this.createLeafEntry(leafId);
111
155
  await this.persist(leafEntry, opts);
112
156
  this.recordEntry(leafEntry);
113
157
  }
158
+ /**
159
+ * Atomically: CAS the meta leaf (+bump leaf_seq), then append the event at the new seq.
160
+ * The `(session_id, seq)` primary key is the backstop — a racing writer that grabbed the same
161
+ * seq makes our INSERT fail with a duplicate-key (23505), which we translate to a conflict too.
162
+ */
114
163
  async persist(entry, opts) {
115
164
  const newLeaf = leafIdAfterEntry(entry);
116
165
  const conditional = opts !== undefined && opts.expectedLeafId !== undefined;
@@ -127,16 +176,17 @@ export class PgSessionStorage extends BaseSessionStorage {
127
176
  }
128
177
  const seqRes = await conn.query("SELECT leaf_seq, owner FROM session_meta WHERE session_id = $1", [this.sessionId]);
129
178
  const seq = Number(seqRes.rows[0].leaf_seq);
130
- const commitOwner = seqRes.rows[0].owner ?? null;
179
+ const commitOwner = seqRes.rows[0].owner ?? null; // codex-2:S2 发射 owner=本事务当前行(tidb twin 同注)
131
180
  await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7)", [this.sessionId, seq, entry.id, entry.parentId ?? null, entry.type, pgSafeJsonStringify(entry), new Date(entry.timestamp)]);
132
181
  await conn.query("COMMIT");
133
- emitLeafAdvance(this.sessionId, newLeaf, commitOwner, seq);
182
+ emitLeafAdvance(this.sessionId, newLeaf, commitOwner, seq); // S2 fast path — post-commit only, fire-and-forget (session-leaf-bus 契约;tidb twin 同点)
134
183
  }
135
184
  catch (err) {
136
185
  try {
137
186
  await conn.query("ROLLBACK");
138
187
  }
139
188
  catch {
189
+ /* ignore rollback error */
140
190
  }
141
191
  if (err instanceof SessionError)
142
192
  throw err;
@@ -150,8 +200,14 @@ export class PgSessionStorage extends BaseSessionStorage {
150
200
  }
151
201
  }
152
202
  }
203
+ // ────────────────────────────────── PG twin of TiDBSessionStore ──────────────────────────────────
204
+ /**
205
+ * PostgreSQL-backed {@link SessionStore} — the PG twin of {@link TiDBSessionStore}. Maps the
206
+ * `SessionStore` surface (acquire / register / ownerOf / touch / release) onto {@link PgSessionStorage}.
207
+ */
153
208
  export class PgSessionStore {
154
209
  pool;
210
+ /** In-flight acquisitions keyed by id, so concurrent acquire(sameId) in one process share one. */
155
211
  pending = new Map();
156
212
  constructor(pool) {
157
213
  this.pool = pool;
@@ -162,6 +218,9 @@ export class PgSessionStore {
162
218
  await this.register(id, null);
163
219
  return this.load(id);
164
220
  }
221
+ // design/114 Phase3 (F1): a `requireExisting` acquire NEVER creates and can REJECT (not_found) — it MUST bypass
222
+ // the shared create-dedup `pending`, else a concurrent create-capable waiter awaiting that same promise would
223
+ // inherit the not_found rejection instead of creating-on-miss. (Twin of TiDBSessionStore.acquire.)
165
224
  if (opts?.requireExisting)
166
225
  return this.load(sessionId, true);
167
226
  const inflight = this.pending.get(sessionId);
@@ -174,6 +233,8 @@ export class PgSessionStore {
174
233
  async load(sessionId, requireExisting = false) {
175
234
  let storage = await PgSessionStorage.wake(this.pool, sessionId);
176
235
  if (!storage) {
236
+ // design/114 Phase3: `requireExisting` ⇒ FAIL LOUD on a genuinely missing session (no session_meta row),
237
+ // never the silent create-on-miss. core's prepare-task maps this `not_found` to `resume.session_not_found`.
177
238
  if (requireExisting)
178
239
  throw new SessionError("not_found", `session ${sessionId} not found`);
179
240
  await this.register(sessionId, null);
@@ -181,11 +242,13 @@ export class PgSessionStore {
181
242
  }
182
243
  return { session: new StoredSession(storage), sessionId };
183
244
  }
245
+ /** Idempotently create a session_meta row with an owner. Existing owner is never overwritten. */
184
246
  async register(sessionId, owner) {
185
247
  const now = new Date();
186
248
  await this.pool.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq) " +
187
249
  "VALUES ($1,$2,$3,$4,NULL,0) ON CONFLICT (session_id) DO NOTHING", [sessionId, owner, now, now]);
188
250
  }
251
+ /** Owner principal of a session, `null` if registered ownerless, `undefined` if no such session. */
189
252
  async ownerOf(sessionId) {
190
253
  const res = await this.pool.query("SELECT owner FROM session_meta WHERE session_id = $1", [sessionId]);
191
254
  if (res.rows.length === 0)
@@ -198,19 +261,27 @@ export class PgSessionStore {
198
261
  sessionId,
199
262
  ]);
200
263
  }
264
+ /** E18 — the session's current leaf SessionTreeEntry.id (session_meta.leaf_id). @see TiDBSessionStore.getLeafId
265
+ * (single SELECT, no acquire/lock, cache-bypassing — safe to read mid-run for the resume-at turn-capture). */
201
266
  async getLeafId(sessionId) {
202
267
  const r = await this.pool.query("SELECT leaf_id FROM session_meta WHERE session_id = $1", [sessionId]);
203
268
  return r.rows[0] ? (r.rows[0].leaf_id ?? null) : null;
204
269
  }
270
+ /** [1196] 六轮复审:owner+leafId 单行原子快照(@see TiDBSessionStore.getHead——混世代组合防)。 */
205
271
  async getHead(sessionId) {
206
272
  const r = await this.pool.query("SELECT owner, leaf_id FROM session_meta WHERE session_id = $1", [sessionId]);
207
273
  if (r.rows.length === 0)
208
274
  return undefined;
209
275
  return { owner: r.rows[0].owner ?? null, leafId: r.rows[0].leaf_id ?? null };
210
276
  }
277
+ /** Durable history is retained; just drop any in-flight cache entry. */
211
278
  async release(sessionId) {
212
279
  this.pending.delete(sessionId);
213
280
  }
281
+ // ───────────────────────── SessionRepo seam (§0.5) — PG twin of TiDBSessionStore ─────────────────────────
282
+ // Behaviourally identical to {@link TiDBSessionStore.listSessions/fork/deleteSession}; only the SQL dialect
283
+ // differs ($n placeholders, jsonb, rowCount, BEGIN/COMMIT handle). Kept in lock-step by the shared real-DB suite.
284
+ /** @see TiDBSessionStore.listSessions — session identity from session_meta (owner-filtered), task_run LEFT JOIN for preview. */
214
285
  async listSessions(opts) {
215
286
  const params = [];
216
287
  const ph = (v) => {
@@ -220,6 +291,7 @@ export class PgSessionStore {
220
291
  const where = [];
221
292
  if (opts.owner)
222
293
  where.push(`sm.owner = ${ph(opts.owner)}`);
294
+ // `?q=` — the PG twin of the TiDB EXISTS predicate (LOWER-LIKE + ESCAPE, dialect-mirrored).
223
295
  if (opts.q)
224
296
  where.push(`EXISTS (SELECT 1 FROM task_run tr WHERE tr.session_id = sm.session_id AND LOWER(tr.objective_preview) LIKE LOWER(${ph(`%${escapeLike(opts.q)}%`)}) ESCAPE '\\')`);
225
297
  if (opts.cursor) {
@@ -232,7 +304,7 @@ export class PgSessionStore {
232
304
  " COALESCE(r.run_count, 0) AS run_count, latest.objective_preview, latest.status, latest.task_id AS last_run_id " +
233
305
  "FROM session_meta sm " +
234
306
  "LEFT JOIN (SELECT session_id, COUNT(*) AS run_count FROM task_run GROUP BY session_id) r ON r.session_id = sm.session_id " +
235
- "LEFT JOIN (SELECT DISTINCT ON (session_id) session_id, objective_preview, status, task_id FROM task_run " +
307
+ "LEFT JOIN (SELECT DISTINCT ON (session_id) session_id, objective_preview, status, task_id FROM task_run " + // K-5c: latest run's task_id
236
308
  " ORDER BY session_id, created_at DESC, task_id DESC) latest ON latest.session_id = sm.session_id " +
237
309
  (where.length ? "WHERE " + where.join(" AND ") + " " : "") +
238
310
  `ORDER BY sm.updated_at DESC, sm.session_id DESC LIMIT ${ph(opts.limit)}`;
@@ -245,10 +317,11 @@ export class PgSessionStore {
245
317
  runCount: Number(r.run_count),
246
318
  objectivePreview: r.objective_preview ?? null,
247
319
  lastStatus: r.status == null ? "" : String(r.status),
248
- lastRunId: r.last_run_id ?? null,
320
+ lastRunId: r.last_run_id ?? null, // K-5c: latest run id = the live-tail re-attach anchor (null if no runs)
249
321
  title: r.title ?? null,
250
322
  }));
251
323
  }
324
+ /** @see TiDBSessionStore.probeTitle — the PG twin. */
252
325
  async probeTitle(sessionId) {
253
326
  const res = await this.pool.query("SELECT title FROM session_meta WHERE session_id = $1", [sessionId]);
254
327
  const row = res.rows[0];
@@ -256,15 +329,19 @@ export class PgSessionStore {
256
329
  return "none";
257
330
  return row.title == null ? "untitled" : "titled";
258
331
  }
332
+ /** @see TiDBSessionStore.setTitleIfNull — the PG twin (write-once auto-title). */
259
333
  async setTitleIfNull(sessionId, title) {
260
334
  const res = await this.pool.query("UPDATE session_meta SET title = $1 WHERE session_id = $2 AND title IS NULL", [title, sessionId]);
261
335
  return (res.rowCount ?? 0) > 0;
262
336
  }
337
+ /** @see TiDBSessionStore.fork — copy the WHOLE durable history (full session_event log, compaction floor bypassed,
338
+ * NOT wake's bounded tail) to a NEW uuidv7 session, re-seq dense from 0, ids/parents verbatim, owner=caller. */
263
339
  async fork(sourceId, owner) {
264
340
  const metaRes = await this.pool.query("SELECT leaf_id FROM session_meta WHERE session_id = $1", [sourceId]);
265
341
  if (!metaRes.rows[0])
266
342
  return null;
267
343
  const leafId = metaRes.rows[0].leaf_id ?? null;
344
+ // FULL durable history (compaction floor bypassed) — every entry, oldest first. node-pg parses jsonb → object.
268
345
  const eventRes = await this.pool.query("SELECT entry_id, parent_id, type, payload, ts FROM session_event WHERE session_id = $1 ORDER BY seq ASC", [sourceId]);
269
346
  const rows = eventRes.rows;
270
347
  const newId = uuidv7();
@@ -274,6 +351,8 @@ export class PgSessionStore {
274
351
  await conn.query("BEGIN");
275
352
  for (let seq = 0; seq < rows.length; seq++) {
276
353
  const r = rows[seq];
354
+ // Copy the payload blob VERBATIM; re-stringify only an object so $6::jsonb binds a JSON string (node-pg
355
+ // returns jsonb already parsed, so a string here means it was stored stringly — rare; pass either through).
277
356
  const payload = typeof r.payload === "string" ? r.payload : pgSafeJsonStringify(r.payload);
278
357
  await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7)", [newId, seq, r.entry_id, r.parent_id ?? null, r.type, payload, r.ts]);
279
358
  }
@@ -286,6 +365,7 @@ export class PgSessionStore {
286
365
  await conn.query("ROLLBACK");
287
366
  }
288
367
  catch {
368
+ /* ignore */
289
369
  }
290
370
  throw err instanceof SessionError ? err : new SessionError("storage", `fork of session ${sourceId} failed`, err instanceof Error ? err : undefined);
291
371
  }
@@ -293,44 +373,60 @@ export class PgSessionStore {
293
373
  conn.release();
294
374
  }
295
375
  }
376
+ /** @see TiDBSessionStore.exportEntries — 2c session-sync EXPORT: the FULL durable session_event log (compaction floor
377
+ * bypassed, NOT wake's tail) as `SessionTreeEntry[]`, oldest-first, verbatim. null if the source is unknown. */
296
378
  async exportEntries(sessionId) {
297
379
  const metaRes = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = $1", [sessionId]);
298
380
  if (metaRes.rows.length === 0)
299
381
  return null;
300
382
  const eventRes = await this.pool.query("SELECT payload FROM session_event WHERE session_id = $1 ORDER BY seq ASC", [sessionId]);
383
+ // node-pg parses jsonb → object already (parseEntry passes objects through; a string would be rare/legacy).
301
384
  return eventRes.rows.map((r) => parseEntry(r.payload));
302
385
  }
386
+ /** @see TiDBSessionStore.listEntryIds — 2c P1d-α (PULL streaming): the IDS-ONLY projection of the full durable log,
387
+ * oldest-first (`SELECT entry_id … ORDER BY seq ASC`), no payloads. null if the source is unknown. */
303
388
  async listEntryIds(sessionId) {
304
389
  const metaRes = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = $1", [sessionId]);
305
390
  if (metaRes.rows.length === 0)
306
- return null;
391
+ return null; // no such session (mirror exportEntries' null)
307
392
  const res = await this.pool.query("SELECT entry_id FROM session_event WHERE session_id = $1 ORDER BY seq ASC", [sessionId]);
308
393
  return res.rows.map((r) => String(r.entry_id));
309
394
  }
395
+ /** @see TiDBSessionStore.exportEntriesStream — 2c P1d-α (PULL streaming): KEYSET-paged async generator over the full
396
+ * durable log (node-pg also buffers a plain SELECT → page it). `… WHERE seq > cursor ORDER BY seq ASC LIMIT K`
397
+ * (K = batchSize ?? 500), advancing the cursor to the last seq each batch, ONE pooled `pool.query` per batch (no
398
+ * held connection across the stream → a slow consumer can't pin one). Start cursor = afterSeq ?? -1 (0-based dense).
399
+ * Resolves to null (NOT an empty iterable) for a missing session — eager `session_meta` probe (BIGINT seq comes
400
+ * back as a STRING in node-pg → always Number(...) the cursor advance). */
310
401
  async exportEntriesStream(sessionId, opts) {
311
402
  const metaRes = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = $1", [sessionId]);
312
403
  if (metaRes.rows.length === 0)
313
- return null;
404
+ return null; // no such session — NOT an empty iterable
314
405
  const pool = this.pool;
315
406
  const batchSize = opts?.batchSize ?? 500;
316
407
  async function* gen() {
317
- let cursor = opts?.afterSeq ?? -1;
408
+ let cursor = opts?.afterSeq ?? -1; // seq is 0-based dense → > -1 starts at seq 0
318
409
  for (;;) {
319
410
  const res = await pool.query("SELECT seq, payload FROM session_event WHERE session_id = $1 AND seq > $2 ORDER BY seq ASC LIMIT $3", [sessionId, cursor, batchSize]);
320
411
  for (const r of res.rows)
321
412
  yield parseEntry(r.payload);
322
413
  if (res.rows.length < batchSize)
323
- return;
324
- cursor = Number(res.rows[res.rows.length - 1].seq);
414
+ return; // short page = end of log
415
+ cursor = Number(res.rows[res.rows.length - 1].seq); // BIGINT seq → STRING in node-pg → Number(...)
325
416
  }
326
417
  }
327
418
  return gen();
328
419
  }
420
+ /** @see TiDBSessionStore.importEntries — 2c session-sync IMPORT: core `validateEntriesForImport` gate FIRST (fail-closed),
421
+ * then persist its verbatim output (re-seq dense from 0, ids/parents/payload/ts unchanged) in ONE txn with
422
+ * session_meta LAST as the commit point; owner = the RE-STAMPED authenticated principal (never the bundle's). */
329
423
  async importEntries(sessionId, owner, entries) {
330
- const validated = validateEntriesForImport(entries);
424
+ const validated = validateEntriesForImport(entries); // fail-closed invariant gate BEFORE any write
331
425
  const conn = await this.pool.connect();
332
426
  try {
333
427
  await conn.query("BEGIN");
428
+ // Shared insert body (entries re-seq dense from 0 + session_meta LAST = commit point). Plain INSERT (fresh-only);
429
+ // replaceEntries is the idempotent-over-existing path (DELETEs first inside the same txn). @see TiDB twin.
334
430
  await this.insertValidatedLog(conn, sessionId, owner, validated);
335
431
  await conn.query("COMMIT");
336
432
  }
@@ -339,22 +435,29 @@ export class PgSessionStore {
339
435
  await conn.query("ROLLBACK");
340
436
  }
341
437
  catch {
438
+ /* ignore */
342
439
  }
343
440
  if (err instanceof SessionError)
344
- throw err;
441
+ throw err; // invalid_session (gate) / conflict — pass through
345
442
  throw new SessionError("storage", `import of session ${sessionId} failed`, err instanceof Error ? err : undefined);
346
443
  }
347
444
  finally {
348
445
  conn.release();
349
446
  }
350
447
  }
448
+ /** @see TiDBSessionStore.replaceEntries — 2c (§7/§8) IDEMPOTENT REPLACE: core `validateEntriesForImport` gate FIRST
449
+ * (fail-closed), then DELETE events + the owner-guarded meta delete (`owner IS NOT DISTINCT FROM $n` = the PG `<=>`,
450
+ * defense-in-depth), then re-insert via the SHARED body (entries + session_meta LAST as the commit point), ALL in
451
+ * ONE txn. Safe to run OVER an existing session (fast-forward / §8 retry) — unlike importEntries' fresh-only INSERT. */
351
452
  async replaceEntries(sessionId, owner, entries) {
352
- const validated = validateEntriesForImport(entries);
453
+ const validated = validateEntriesForImport(entries); // fail-closed gate BEFORE any write (mirrors importEntries)
353
454
  const conn = await this.pool.connect();
354
455
  try {
355
456
  await conn.query("BEGIN");
457
+ // Purge first (idempotent: a missing session deletes 0 rows). Events, then the owner-guarded meta delete.
356
458
  await conn.query("DELETE FROM session_event WHERE session_id = $1", [sessionId]);
357
459
  await conn.query("DELETE FROM session_meta WHERE session_id = $1 AND owner IS NOT DISTINCT FROM $2", [sessionId, owner]);
460
+ // …then re-insert the validated log (entries + meta-as-commit-point) via the SHARED body.
358
461
  await this.insertValidatedLog(conn, sessionId, owner, validated);
359
462
  await conn.query("COMMIT");
360
463
  }
@@ -363,19 +466,23 @@ export class PgSessionStore {
363
466
  await conn.query("ROLLBACK");
364
467
  }
365
468
  catch {
469
+ /* ignore */
366
470
  }
367
471
  if (err instanceof SessionError)
368
- throw err;
472
+ throw err; // invalid_session (gate) / conflict — pass through
369
473
  throw new SessionError("storage", `replace of session ${sessionId} failed`, err instanceof Error ? err : undefined);
370
474
  }
371
475
  finally {
372
476
  conn.release();
373
477
  }
374
478
  }
479
+ /** @see TiDBSessionStore.insertValidatedLog — shared insert half of importEntries / replaceEntries (ONE source, no
480
+ * drift): entries re-seq dense from 0 (ids/parents/payload/ts verbatim), THEN session_meta LAST as the commit point.
481
+ * Runs inside the caller's open txn (`conn` already in BEGIN); the caller owns COMMIT/ROLLBACK + the validate gate. */
375
482
  async insertValidatedLog(conn, sessionId, owner, validated) {
376
483
  let leafId = null;
377
484
  for (const e of validated)
378
- leafId = leafIdAfterEntry(e);
485
+ leafId = leafIdAfterEntry(e); // replayed leaf — core's exported helper (no service copy)
379
486
  const now = new Date();
380
487
  for (let seq = 0; seq < validated.length; seq++) {
381
488
  const e = validated[seq];
@@ -383,13 +490,21 @@ export class PgSessionStore {
383
490
  }
384
491
  await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq) VALUES ($1,$2,$3,$4,$5,$6)", [sessionId, owner, now, now, leafId, validated.length]);
385
492
  }
493
+ /** @see TiDBSessionStore.beginImportStaging — 2c P1d-β PUSH staging: stream batches into the shadow id
494
+ * `${realSessionId}#stg-${token}` (route-minted token), then commit (atomic swap) / abort. */
386
495
  beginImportStaging(realSessionId, token) {
387
496
  return new PgStagingHandle(this.pool, realSessionId, stagingIdFor(realSessionId, token));
388
497
  }
498
+ /** @see TiDBSessionStore.readStagedEntries — 2c P1d-β staged-row inspection: the staged rows under `stagingId`, oldest-
499
+ * first, no session_meta probe (a staging id has no meta row). No longer the import gate (the per-line
500
+ * StreamingImportValidator validates during the stream); a tests/diagnostics seam. Returns [] for an empty/unknown id. */
389
501
  async readStagedEntries(stagingId) {
390
502
  const res = await this.pool.query("SELECT payload FROM session_event WHERE session_id = $1 ORDER BY seq ASC", [stagingId]);
391
503
  return res.rows.map((r) => parseEntry(r.payload));
392
504
  }
505
+ /** @see TiDBSessionStore.sweepStagingSessions — reaper for abandoned staging sessions: DELETE session_event rows
506
+ * under a `%#stg-%` id with NO session_meta whose oldest row is older than the grace window (in-flight stays fresh).
507
+ * Returns the affected row count. Driven by main.ts's reaper. */
393
508
  async sweepStagingSessions() {
394
509
  const cutoff = new Date(Date.now() - STAGING_GC_GRACE_MS);
395
510
  const cands = await this.pool.query("SELECT se.session_id FROM session_event se LEFT JOIN session_meta sm ON sm.session_id = se.session_id " +
@@ -402,6 +517,9 @@ export class PgSessionStore {
402
517
  }
403
518
  return removed;
404
519
  }
520
+ /** @see TiDBSessionStore.deleteSession — purge session_meta + session_event; idempotent (false if absent).
521
+ * Owner-guarded on session_meta.owner (`IS NOT DISTINCT FROM` = the PG `<=>`); the event purge is gated on the
522
+ * owner-matched meta delete (session_event has no owner column). */
405
523
  async deleteSession(sessionId, owner) {
406
524
  const conn = await this.pool.connect();
407
525
  try {
@@ -417,6 +535,7 @@ export class PgSessionStore {
417
535
  await conn.query("ROLLBACK");
418
536
  }
419
537
  catch {
538
+ /* ignore */
420
539
  }
421
540
  throw err;
422
541
  }
@@ -431,18 +550,28 @@ export class PgSessionStore {
431
550
  this.pending.clear();
432
551
  }
433
552
  }
553
+ /**
554
+ * 2c session-sync P1d-β — the PG twin of {@link TiDBStagingHandle} (behaviourally identical; only the SQL dialect
555
+ * differs: `$n` placeholders, `$n::jsonb`, `ON CONFLICT (session_id, entry_id) DO NOTHING`, rowCount, FOR UPDATE,
556
+ * BEGIN/COMMIT handle). Streams entry batches into a shadow session_event partition, then atomically RENAMEs them
557
+ * onto the real session id in ONE txn with session_meta LAST = the commit point.
558
+ */
434
559
  class PgStagingHandle {
435
560
  pool;
436
561
  realSessionId;
437
562
  stagingId;
438
563
  nextSeq = 0;
439
- seen = new Set();
564
+ seen = new Set(); // staged ids (resume dedup → dense seqs); @see TiDBStagingHandle
440
565
  constructor(pool, realSessionId, stagingId) {
441
566
  this.pool = pool;
442
567
  this.realSessionId = realSessionId;
443
568
  this.stagingId = stagingId;
444
569
  }
570
+ /** @see TiDBStagingHandle.appendBatch — DENSE re-seq (skip already-staged ids), idempotent ON CONFLICT (session_id,
571
+ * entry_id) DO NOTHING, one txn/batch. */
445
572
  async appendBatch(entries) {
573
+ // ONE multi-row INSERT per batch (bounded) — @see TiDBStagingHandle.appendBatch. Build $-placeholders for the NEW
574
+ // rows (skip already-staged ids → dense seqs), ON CONFLICT (session_id, entry_id) DO NOTHING as the durable backstop.
446
575
  const params = [];
447
576
  const tuples = [];
448
577
  for (const e of entries) {
@@ -450,7 +579,7 @@ class PgStagingHandle {
450
579
  continue;
451
580
  const b = params.length;
452
581
  tuples.push(`($${b + 1},$${b + 2},$${b + 3},$${b + 4},$${b + 5},$${b + 6}::jsonb,$${b + 7})`);
453
- params.push(this.stagingId, this.nextSeq, e.id, e.parentId ?? null, e.type, pgSafeJsonStringify(e), new Date());
582
+ params.push(this.stagingId, this.nextSeq, e.id, e.parentId ?? null, e.type, pgSafeJsonStringify(e), new Date() /* A-1: row-insert time, not source-history — reaper liveness; payload keeps e.timestamp */);
454
583
  this.seen.add(e.id);
455
584
  this.nextSeq++;
456
585
  }
@@ -464,6 +593,8 @@ class PgStagingHandle {
464
593
  throw err instanceof SessionError ? err : new SessionError("storage", `staged append for ${this.realSessionId} failed`, err instanceof Error ? err : undefined);
465
594
  }
466
595
  }
596
+ /** @see TiDBStagingHandle.commit — the atomic swap: in-commit re-classify (FOR UPDATE) → purge dst → rename
597
+ * staging→real → session_meta LAST. Throws SyncConflictError on an unconsented fork/stale flip. */
467
598
  async commit(owner, opts) {
468
599
  const conn = await this.pool.connect();
469
600
  try {
@@ -494,7 +625,7 @@ class PgStagingHandle {
494
625
  try {
495
626
  await conn.query("ROLLBACK");
496
627
  }
497
- catch { }
628
+ catch { /* ignore */ }
498
629
  if (err instanceof SyncConflictError || err instanceof SessionError)
499
630
  throw err;
500
631
  throw new SessionError("storage", `staged commit for ${this.realSessionId} failed`, err instanceof Error ? err : undefined);
@@ -503,6 +634,7 @@ class PgStagingHandle {
503
634
  conn.release();
504
635
  }
505
636
  }
637
+ /** @see TiDBStagingHandle.abort — DELETE the staging rows. Idempotent. */
506
638
  async abort() {
507
639
  await this.pool.query("DELETE FROM session_event WHERE session_id = $1", [this.stagingId]);
508
640
  }