@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,37 +1,169 @@
1
+ /**
2
+ * Durable `FileSnapshotStore` (E19 rewind-files) — SINGLE-FILE DUAL-DIALECT (design/158 A12 定型半场).
3
+ * ONE implementation, TWO dialects; the historical `TiDBFileSnapshotStore` / `PgFileSnapshotStore` class
4
+ * names survive as thin ctor subclasses so every consumer (store-backend.ts, the migration script, the
5
+ * contract/integration/minio-equivalence suites) is untouched.
6
+ *
7
+ * Cross-replica working-tree snapshots keyed by `(scope=sessionId, key=SessionTreeEntry.id)`, the SAME
8
+ * entryId anchor as E18 resume-at. Used for (a) rewind (restore a PAST turn's tree in the same env) and
9
+ * (b) the seamless local↔cloud work-artifact transfer (2c: a snapshot synced to the cloud, restored in the
10
+ * other environment — conversation + files in lockstep).
11
+ *
12
+ * The env-operating + security-critical half (the tree walk + the symlink-neutralizing restore
13
+ * convergence) is core's `captureManifest`/`applyManifest` — REUSED verbatim (re-implementing risks
14
+ * re-introducing the symlink-escape / fail-closed bugs core's dual-review fixed). This store provides ONLY
15
+ * the durable persistence: content-addressed blobs (sha256 → bytes, deduped across all snapshots, via the
16
+ * shared {@link BlobBackend} seam — SQL by default, MinIO when configured) + a per-`(scope,key)` manifest
17
+ * (relPath → blobHash) that stays in SQL either way (`snapshot_manifest`).
18
+ *
19
+ * ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
20
+ * - `?` placeholders vs `$n` on every `snapshot_manifest` statement (exists-check, insert, selects, the
21
+ * scoped/keyed deletes) — every statement stays written out at the call site via `q()`, never a single
22
+ * text with a numbering loop.
23
+ * - result unwrapping (`rows`/`affected`) and transaction control (mysql2 native begin/commit/rollback vs
24
+ * PG statement form) go through the shared `SqlDriver`/`SqlTxConn` seam (sql-driver.ts) — same
25
+ * normalization the other A12 collapses use; NOT a store-specific concern.
26
+ * - the default {@link BlobBackend} (`SqlBlobBackend`) and `blobSizes` (`sqlBlobSizes`) take the RAW pool
27
+ * + a `"tidb"|"pg"` dialect string directly (blob-backend.ts predates/sits outside this seam — it is a
28
+ * sibling module, not part of this collapse), so this store keeps BOTH the `SqlDriver` (for its own
29
+ * `snapshot_manifest` statements) and the raw native pool (to hand to blob-backend.ts unchanged).
30
+ * - in-flight guard key separator: the TiDB twin used `\x00`, the PG twin used a plain space — NOT a
31
+ * dialect requirement (this key never leaves the process or touches SQL), just accidental drift.
32
+ * Harmonized to `\x00` (can't collide with a normal scope/key string) — zero observable behavior change
33
+ * either way, since the key only dedups concurrent calls on THIS replica within THIS process.
34
+ * - `gcOrphanBlobs`'s orphan-SET query text is IDENTICAL for both dialects (no placeholders) — kept as one
35
+ * shared statement, not a `q()` pair.
36
+ */
1
37
  import { type FileSnapshotBounds, type FileSnapshotStore, type FileSnapshotResult, type ExecutionEnv } from "@sema-agent/core";
2
38
  import type { Pool as MySqlPool } from "mysql2/promise";
3
39
  import type { Pool as PgPool } from "pg";
4
40
  import { type BlobBackend } from "./blob-backend.js";
5
41
  import { type SqlDriver } from "./sql-driver.js";
42
+ /** Dual-dialect durable FileSnapshotStore. See the file header for the dialect-delta ledger. */
6
43
  export declare class SqlFileSnapshotStore implements FileSnapshotStore {
7
44
  protected readonly db: SqlDriver;
45
+ /** The RAW native pool — handed to blob-backend.ts (SqlBlobBackend / sqlBlobSizes), which sits OUTSIDE
46
+ * the SqlDriver seam and dialect-switches on its own `"tidb"|"pg"` string. Not used for this class's OWN
47
+ * `snapshot_manifest` statements (those go through `db`). */
8
48
  protected readonly pool: MySqlPool | PgPool;
9
49
  protected readonly bounds: FileSnapshotBounds;
50
+ /** In-flight create-once guard (per process) — mirrors InMemoryFileSnapshotStore: a concurrent 2nd
51
+ * snapshot/import of the same (scope,key) on THIS replica is a no-op, not a re-capture. CROSS-REPLICA dup
52
+ * is HARMLESS here: the key is an immutable SessionTreeEntry.id (one settled turn → one fixed tree), so
53
+ * two replicas capture the SAME relPath→hash rows → restore's Map dedups them identically. (A
54
+ * snapshot_header(scope,snap_key) claim row would serialize one winner + drop dup rows — a follow-on
55
+ * hardening for if same-session-multi-replica ever happens; v1 is single-replica affinity so it doesn't.) */
10
56
  private readonly inFlight;
57
+ /** The content-addressed BYTE store (clay 2026-06-26 object-store offload). DEFAULT = the SqlBlobBackend
58
+ * over THIS pool — so a MINIO-unset deployment is byte-identical to before this refactor (the
59
+ * `snapshot_blob` table). When MinIO is configured the StoreBackend passes a MinioBlobBackend, moving ONLY
60
+ * the heavy bytes off-DB; the MANIFEST (snapshot_manifest) + the orphan-reference computation STAY in SQL. */
11
61
  private readonly blobs;
62
+ /** [805]③a/[806]④: core 1.287's captureManifest counts enumerate→read-window vanished files it TOLERATED
63
+ * (E19 fix — skipped, not failed). The count rides ONLY the captureManifest return value; without this
64
+ * sink it is silently dropped. Optional observer set by main.ts (metrics face) — never throws into the
65
+ * seam. codex F8: the type admits an ASYNC sink (`void | Promise<void>`) — the call site swallows a
66
+ * rejected promise too (a bare try/catch only caught sync throws, so an async observer's rejection
67
+ * surfaced as an unhandledRejection and could pierce the never-throw posture process-wide). */
12
68
  onSkippedVanished: ((count: number) => void | Promise<void>) | undefined;
13
- constructor(db: SqlDriver, pool: MySqlPool | PgPool, blobBackend?: BlobBackend, bounds?: FileSnapshotBounds);
69
+ constructor(db: SqlDriver,
70
+ /** The RAW native pool — handed to blob-backend.ts (SqlBlobBackend / sqlBlobSizes), which sits OUTSIDE
71
+ * the SqlDriver seam and dialect-switches on its own `"tidb"|"pg"` string. Not used for this class's OWN
72
+ * `snapshot_manifest` statements (those go through `db`). */
73
+ pool: MySqlPool | PgPool, blobBackend?: BlobBackend, bounds?: FileSnapshotBounds);
74
+ /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
14
75
  private q;
76
+ /** THE ONE content-addressed blob write — the SINGLE source `snapshot()`, `importManifest()`, and the
77
+ * `putBlob` seam all funnel through (no drift across the three writers). Routes the bytes through the
78
+ * BlobBackend (SQL default or MinIO); the SqlBlobBackend's upsert REFRESHES `created_at` even on a dedup
79
+ * hit so a re-used/just-written blob is "touched=now" and GC-grace-protected until a manifest row commits
80
+ * (closes the delete-in-use race). */
15
81
  private putBlobRow;
82
+ /** 2c session-sync PUSH side — upload ONE content-addressed blob to the cloud's snapshot store AHEAD of a
83
+ * bundle import (§5 streamed blob negotiation: the local peer PUTs only the blobs the cloud is missing,
84
+ * then commits the bundle via importSession, whose getBlob reads the cloud's OWN store). A SERVICE method
85
+ * (NOT on core's FileSnapshotStore interface — core was asked, non-blocking; the route feature-detects
86
+ * `typeof fs.putBlob`). Reuses {@link putBlobRow} VERBATIM (the SAME created_at-refreshing upsert
87
+ * importManifest uses), so a PUT and a concurrent reap can't race: the just-PUT blob's created_at=now
88
+ * keeps it inside the GC grace window until the import references it from a manifest. Idempotent
89
+ * (content-addressed). The route verifies sha256(bytes)===hash BEFORE this. core 1.142.0 made this a
90
+ * fail-closed never-throw seam (Promise<FileSnapshotResult>): a BlobBackend write error (e.g. a
91
+ * MinioBlobBackend non-2xx PUT) is caught + encoded as `restore_failed`, NEVER thrown out of the seam. */
16
92
  putBlob(hash: string, bytes: Uint8Array): Promise<FileSnapshotResult>;
93
+ /** CREATE-ONCE: an entryId's tree is immutable, so a 2nd snapshot for an existing key is a no-op {ok:true}. */
17
94
  snapshot(scope: string, key: string, env: ExecutionEnv, root: string, signal?: AbortSignal): Promise<FileSnapshotResult>;
95
+ /** 2c session-sync IMPORT side — the inverse of `exportManifest`/`getBlob`: persist a snapshot's content
96
+ * (manifest + content-addressed blobs) that was EXPORTED from ANOTHER backend (the local→cloud transfer: a src
97
+ * snapshot's manifest+blobs land here as a NEW `(scope,key)`). Unlike `snapshot()` this does NOT walk an env — the
98
+ * manifest is given; bytes come from `srcGetBlob(blobHash)`. Mirrors core's InMemory/File `importManifest` fail-closed
99
+ * matrix verbatim (core 1.141.0). Reuses snapshot()'s EXACT putBlob (created_at-refreshing upsert, GC-grace-safe)
100
+ * + manifest-tx (re-check under txn) patterns.
101
+ *
102
+ * NEVER-throws + FAIL-CLOSED (the matrix core's dual-review fixed, point-named to the durable twins):
103
+ * - CREATE-ONCE: an entryId's tree is immutable → an existing OR in-flight `(scope,key)` is a no-op `{ok:true}`.
104
+ * - `srcGetBlob` is a CROSS-BACKEND fetch (remote/IPC) that CAN REJECT (timeout/ECONNRESET/query error) — caught and
105
+ * encoded as `read_failed`, NEVER thrown out of the seam.
106
+ * - a MISSING blob (`bytes===undefined`) → `read_failed`.
107
+ * - content-address INTEGRITY: `sha256(bytes) !== hash` → `read_failed` (a 2c transfer can cross net/IPC; a corrupt
108
+ * blob stored under a mismatched hash would let a later restore silently write WRONG bytes — verify, don't trust).
109
+ * - ANY blob failure → EARLY-RETURN `{ok:false}` and the manifest tx is NEVER run, so `has()` stays false (no
110
+ * half-snapshot). The DISTINCT blobs already staged before the failing one are orphans (no manifest references
111
+ * them) — their snapshot_blob INDEX rows are reaped (and, under MinIO, the objects deleted) by the async grace-
112
+ * window sweepOrphanBlobs reaper; they never make this `(scope,key)` visible. */
18
113
  importManifest(scope: string, key: string, manifest: Map<string, string>, srcGetBlob: (hash: string) => Promise<Uint8Array | undefined>): Promise<FileSnapshotResult>;
114
+ /** Converge `root` back to the snapshot (applyManifest: PHASE-1 remove symlinks/strays, PHASE-2 write captured). */
19
115
  restore(scope: string, key: string, env: ExecutionEnv, root: string, signal?: AbortSignal): Promise<FileSnapshotResult>;
20
116
  has(scope: string, key: string): Promise<boolean>;
117
+ /** 2c session-sync: enumerate every snapshot key (entryId) in a scope — the SAME enumeration reap()
118
+ * does (`SELECT DISTINCT snap_key`), exposed so 2c can export a session's full set of file snapshots. */
21
119
  listKeys(scope: string): Promise<string[]>;
120
+ /** 2c session-sync: the manifest (relPath → blobHash) for one snapshot — to EXPORT its content to another
121
+ * backend byte-for-byte (paired with getBlob for content-addressed transfer). Reads the STORED manifest rows (never
122
+ * re-hashes). 0 rows = no snapshot (a real snapshot always has ≥1 manifest row) → null (parity with InMemory). */
22
123
  exportManifest(scope: string, key: string): Promise<Map<string, string> | null>;
124
+ /** 2c session-sync: the content-addressed bytes for `hash` (undefined if absent) — reads via the BlobBackend
125
+ * (SQL default or MinIO, where getBlob also verifies content-address integrity). */
23
126
  getBlob(hash: string): Promise<Uint8Array | undefined>;
127
+ /** The subset of `hashes` already present in the blob store — reads the SQL INDEX (one IN(...) for BOTH backends),
128
+ * so the §10 import blob-presence pre-check is a cheap index probe, NOT N full-object getBlob GETs (finding 12). */
24
129
  hasBlobs(hashes: string[]): Promise<Set<string>>;
130
+ /** workspace 浏览面(#3,[1894]②):per-blob 字节数——读 SQL INDEX 的 byte_len(单 IN 查询;
131
+ * MinIO 后端的索引行同带 byte_len,两后端同源)。tree 的 size 投影 + file 面「超限先拒后拉」用。 */
25
132
  blobSizes(hashes: string[]): Promise<Map<string, number>>;
133
+ /** GC: drop every snapshot in `scope` whose key is NOT in keepKeys. Drops ONLY the SQL manifest rows synchronously —
134
+ * the now-unreferenced blobs become ORPHANS (their snapshot_blob INDEX rows survive) and are collected LATER by the
135
+ * async sweepOrphanBlobs reaper (NEVER a synchronous external-store delete that could block/fail this reap on a MinIO
136
+ * blip — clay 2026-06-26 async-purge design). */
26
137
  reap(scope: string, keepKeys: string[]): Promise<number>;
138
+ /** E21 purge — drop ALL snapshots for a session (every key). Drops ONLY the SQL manifest rows synchronously → the
139
+ * session is immediately gone + its blobs become orphans; the actual MinIO/SQL BYTE deletion is the async
140
+ * sweepOrphanBlobs reaper's job (bounded-window EVENTUAL CONSISTENCY satisfies the E21 privacy/compliance "really
141
+ * delete the bytes" requirement WITHOUT a fragile synchronous object-store delete on the purge path). Scoped by sessionId. */
27
142
  deleteBySession(scope: string): Promise<number>;
143
+ /** Content-addressed blobs are shared across snapshots → GC only those NO manifest references. The orphan-hash SET is
144
+ * computed from the `snapshot_blob` INDEX in SQL (reference tracking stays in the DB — works for BOTH the SQL backend
145
+ * AND MinIO, whose index rows are kept bytes-NULL); the BlobBackend's deleteBlobs applies the grace window (a just-
146
+ * written blob a concurrent in-flight snapshot needs is never delete-in-use) and, for MinIO, deletes the objects.
147
+ * Returns the count ACTUALLY deleted (the grace-passed subset deleteBlobs reports — NOT the pre-grace orphan count,
148
+ * finding 13). Called ONLY from the async sweepOrphanBlobs reaper (never the synchronous purge path).
149
+ *
150
+ * The orphan-SET query text is IDENTICAL for both dialects (no placeholders) — a single shared statement, not a `q()`
151
+ * pair. A query error degrades to "zero orphans this sweep" (both twins already treated it that way — the reaper
152
+ * retries next tick, so under-collecting once is harmless; never surfaced as a hard failure). */
28
153
  private gcOrphanBlobs;
154
+ /** 2c session-sync DoS bound + the ASYNC byte-GC for reap/deleteBySession orphans — GLOBALLY GC grace-passed
155
+ * orphan blobs (standalone PUTs never imported AND the blobs a purge/reap left orphaned). The driver is the main.ts
156
+ * reaper (scheduled, error-tolerant: a per-object MinIO delete failure is skipped + retried next sweep → bounded-
157
+ * window eventual consistency). Reuses {@link gcOrphanBlobs} VERBATIM; returns the count actually deleted this sweep. */
29
158
  sweepOrphanBlobs(): Promise<number>;
159
+ /** Run statements atomically; rolls back on failure. */
30
160
  private tx;
31
161
  }
162
+ /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
32
163
  export declare class TiDBFileSnapshotStore extends SqlFileSnapshotStore {
33
164
  constructor(pool: MySqlPool, blobBackend?: BlobBackend, bounds?: FileSnapshotBounds);
34
165
  }
166
+ /** PostgreSQL binding — historical class name + ctor shape preserved. */
35
167
  export declare class PgFileSnapshotStore extends SqlFileSnapshotStore {
36
168
  constructor(pool: PgPool, blobBackend?: BlobBackend, bounds?: FileSnapshotBounds);
37
169
  }
@@ -1,27 +1,103 @@
1
+ /**
2
+ * Durable `FileSnapshotStore` (E19 rewind-files) — SINGLE-FILE DUAL-DIALECT (design/158 A12 定型半场).
3
+ * ONE implementation, TWO dialects; the historical `TiDBFileSnapshotStore` / `PgFileSnapshotStore` class
4
+ * names survive as thin ctor subclasses so every consumer (store-backend.ts, the migration script, the
5
+ * contract/integration/minio-equivalence suites) is untouched.
6
+ *
7
+ * Cross-replica working-tree snapshots keyed by `(scope=sessionId, key=SessionTreeEntry.id)`, the SAME
8
+ * entryId anchor as E18 resume-at. Used for (a) rewind (restore a PAST turn's tree in the same env) and
9
+ * (b) the seamless local↔cloud work-artifact transfer (2c: a snapshot synced to the cloud, restored in the
10
+ * other environment — conversation + files in lockstep).
11
+ *
12
+ * The env-operating + security-critical half (the tree walk + the symlink-neutralizing restore
13
+ * convergence) is core's `captureManifest`/`applyManifest` — REUSED verbatim (re-implementing risks
14
+ * re-introducing the symlink-escape / fail-closed bugs core's dual-review fixed). This store provides ONLY
15
+ * the durable persistence: content-addressed blobs (sha256 → bytes, deduped across all snapshots, via the
16
+ * shared {@link BlobBackend} seam — SQL by default, MinIO when configured) + a per-`(scope,key)` manifest
17
+ * (relPath → blobHash) that stays in SQL either way (`snapshot_manifest`).
18
+ *
19
+ * ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
20
+ * - `?` placeholders vs `$n` on every `snapshot_manifest` statement (exists-check, insert, selects, the
21
+ * scoped/keyed deletes) — every statement stays written out at the call site via `q()`, never a single
22
+ * text with a numbering loop.
23
+ * - result unwrapping (`rows`/`affected`) and transaction control (mysql2 native begin/commit/rollback vs
24
+ * PG statement form) go through the shared `SqlDriver`/`SqlTxConn` seam (sql-driver.ts) — same
25
+ * normalization the other A12 collapses use; NOT a store-specific concern.
26
+ * - the default {@link BlobBackend} (`SqlBlobBackend`) and `blobSizes` (`sqlBlobSizes`) take the RAW pool
27
+ * + a `"tidb"|"pg"` dialect string directly (blob-backend.ts predates/sits outside this seam — it is a
28
+ * sibling module, not part of this collapse), so this store keeps BOTH the `SqlDriver` (for its own
29
+ * `snapshot_manifest` statements) and the raw native pool (to hand to blob-backend.ts unchanged).
30
+ * - in-flight guard key separator: the TiDB twin used `\x00`, the PG twin used a plain space — NOT a
31
+ * dialect requirement (this key never leaves the process or touches SQL), just accidental drift.
32
+ * Harmonized to `\x00` (can't collide with a normal scope/key string) — zero observable behavior change
33
+ * either way, since the key only dedups concurrent calls on THIS replica within THIS process.
34
+ * - `gcOrphanBlobs`'s orphan-SET query text is IDENTICAL for both dialects (no placeholders) — kept as one
35
+ * shared statement, not a `q()` pair.
36
+ */
1
37
  import { captureManifest, applyManifest, DEFAULT_SNAPSHOT_BOUNDS } from "@sema-agent/core";
2
38
  import { createHash } from "node:crypto";
3
39
  import { SqlBlobBackend, sqlBlobSizes } from "./blob-backend.js";
4
40
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
41
+ /** Dual-dialect durable FileSnapshotStore. See the file header for the dialect-delta ledger. */
5
42
  export class SqlFileSnapshotStore {
6
43
  db;
7
44
  pool;
8
45
  bounds;
46
+ /** In-flight create-once guard (per process) — mirrors InMemoryFileSnapshotStore: a concurrent 2nd
47
+ * snapshot/import of the same (scope,key) on THIS replica is a no-op, not a re-capture. CROSS-REPLICA dup
48
+ * is HARMLESS here: the key is an immutable SessionTreeEntry.id (one settled turn → one fixed tree), so
49
+ * two replicas capture the SAME relPath→hash rows → restore's Map dedups them identically. (A
50
+ * snapshot_header(scope,snap_key) claim row would serialize one winner + drop dup rows — a follow-on
51
+ * hardening for if same-session-multi-replica ever happens; v1 is single-replica affinity so it doesn't.) */
9
52
  inFlight = new Set();
53
+ /** The content-addressed BYTE store (clay 2026-06-26 object-store offload). DEFAULT = the SqlBlobBackend
54
+ * over THIS pool — so a MINIO-unset deployment is byte-identical to before this refactor (the
55
+ * `snapshot_blob` table). When MinIO is configured the StoreBackend passes a MinioBlobBackend, moving ONLY
56
+ * the heavy bytes off-DB; the MANIFEST (snapshot_manifest) + the orphan-reference computation STAY in SQL. */
10
57
  blobs;
11
- onSkippedVanished = undefined;
12
- constructor(db, pool, blobBackend, bounds = DEFAULT_SNAPSHOT_BOUNDS) {
58
+ /** [805]③a/[806]④: core 1.287's captureManifest counts enumerate→read-window vanished files it TOLERATED
59
+ * (E19 fix — skipped, not failed). The count rides ONLY the captureManifest return value; without this
60
+ * sink it is silently dropped. Optional observer set by main.ts (metrics face) — never throws into the
61
+ * seam. codex F8: the type admits an ASYNC sink (`void | Promise<void>`) — the call site swallows a
62
+ * rejected promise too (a bare try/catch only caught sync throws, so an async observer's rejection
63
+ * surfaced as an unhandledRejection and could pierce the never-throw posture process-wide). */
64
+ onSkippedVanished = undefined; // explicit initializer: the key must EXIST on the instance (main.ts feature-detects via `in`)
65
+ constructor(db,
66
+ /** The RAW native pool — handed to blob-backend.ts (SqlBlobBackend / sqlBlobSizes), which sits OUTSIDE
67
+ * the SqlDriver seam and dialect-switches on its own `"tidb"|"pg"` string. Not used for this class's OWN
68
+ * `snapshot_manifest` statements (those go through `db`). */
69
+ pool, blobBackend, bounds = DEFAULT_SNAPSHOT_BOUNDS) {
13
70
  this.db = db;
14
71
  this.pool = pool;
15
72
  this.bounds = bounds;
16
73
  this.blobs = blobBackend ?? new SqlBlobBackend(this.db.dialect, this.pool);
17
74
  }
75
+ /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
18
76
  q(tidb, pg) {
19
77
  return this.db.dialect === "tidb" ? tidb : pg;
20
78
  }
79
+ /** THE ONE content-addressed blob write — the SINGLE source `snapshot()`, `importManifest()`, and the
80
+ * `putBlob` seam all funnel through (no drift across the three writers). Routes the bytes through the
81
+ * BlobBackend (SQL default or MinIO); the SqlBlobBackend's upsert REFRESHES `created_at` even on a dedup
82
+ * hit so a re-used/just-written blob is "touched=now" and GC-grace-protected until a manifest row commits
83
+ * (closes the delete-in-use race). */
21
84
  async putBlobRow(hash, bytes) {
22
85
  await this.blobs.putBlob(hash, bytes);
23
86
  }
87
+ /** 2c session-sync PUSH side — upload ONE content-addressed blob to the cloud's snapshot store AHEAD of a
88
+ * bundle import (§5 streamed blob negotiation: the local peer PUTs only the blobs the cloud is missing,
89
+ * then commits the bundle via importSession, whose getBlob reads the cloud's OWN store). A SERVICE method
90
+ * (NOT on core's FileSnapshotStore interface — core was asked, non-blocking; the route feature-detects
91
+ * `typeof fs.putBlob`). Reuses {@link putBlobRow} VERBATIM (the SAME created_at-refreshing upsert
92
+ * importManifest uses), so a PUT and a concurrent reap can't race: the just-PUT blob's created_at=now
93
+ * keeps it inside the GC grace window until the import references it from a manifest. Idempotent
94
+ * (content-addressed). The route verifies sha256(bytes)===hash BEFORE this. core 1.142.0 made this a
95
+ * fail-closed never-throw seam (Promise<FileSnapshotResult>): a BlobBackend write error (e.g. a
96
+ * MinioBlobBackend non-2xx PUT) is caught + encoded as `restore_failed`, NEVER thrown out of the seam. */
24
97
  async putBlob(hash, bytes) {
98
+ // M3(接入审计 2026-07-08):sha256 自校验是契约的【store 级】强制不变量("VERIFIES content-address
99
+ // integrity … a mismatch → read_failed, nothing stored")——不外包给调用方(HTTP 路由前置校验保留=双保险;
100
+ // 绕过路由的内部新调用方不再能用错配 hash 污染 content-addressed store)。镜像 core InMemory 参考实现。
25
101
  if (createHash("sha256").update(bytes).digest("hex") !== hash) {
26
102
  return { ok: false, error: { code: "read_failed", message: `putBlob ${hash} fails content-address integrity — nothing stored` } };
27
103
  }
@@ -30,20 +106,35 @@ export class SqlFileSnapshotStore {
30
106
  return { ok: true };
31
107
  }
32
108
  catch (err) {
109
+ // A WRITE/upload failure. core's FileSnapshotError code enum has no dedicated write code; `restore_failed` is the
110
+ // code core's OWN File backend uses for a putBlob PERSIST failure (vs `read_failed` = content-address integrity),
111
+ // so we match it + make the MESSAGE explicit that this is a write/upload failure (finding 19).
33
112
  return { ok: false, error: { code: "restore_failed", message: `putBlob ${hash} write/upload failed: ${err.message}` } };
34
113
  }
35
114
  }
115
+ /** CREATE-ONCE: an entryId's tree is immutable, so a 2nd snapshot for an existing key is a no-op {ok:true}. */
36
116
  async snapshot(scope, key, env, root, signal) {
37
- const flightKey = `${scope}\x00${key}`;
117
+ const flightKey = `${scope}\x00${key}`; // harmonized separator — see file header (not a dialect concern)
38
118
  if (this.inFlight.has(flightKey))
39
119
  return { ok: true };
40
120
  this.inFlight.add(flightKey);
41
121
  try {
122
+ // M4(接入审计):create-once 前置 has() 探针必须在 try 【内】——DB 瞬态错(连接池抖动)曾从这里
123
+ // reject 逃逸 never-throw seam,击穿 core 引擎的裸 await。
42
124
  if (await this.has(scope, key))
43
125
  return { ok: true };
126
+ // captureManifest walks `root` via the env, hands each file's bytes to putBlob (content-addressed dedup), and
127
+ // returns relPath→hash. Fail-CLOSED: on an enumerate/read/bound error we DON'T persist a partial manifest.
128
+ // NEVER-THROW: captureManifest invokes the putBlob callback UN-try/caught, so a throwing BlobBackend (a MinIO
129
+ // non-2xx PUT / network blip — far likelier than a DB error) would escape this never-throw seam. Wrap the whole
130
+ // capture: a putBlob/MinIO throw → {ok:false, restore_failed}, the contract every caller (E19 auto-snapshot, the
131
+ // rewind/2c path) relies on (findings 4/9). The already-PUT distinct blobs are orphans (no manifest references
132
+ // them) → collected by the async sweepOrphanBlobs reaper.
44
133
  let cap;
45
134
  try {
46
135
  cap = await captureManifest(env, root, this.bounds, signal, async (hash, bytes) => {
136
+ // The shared content-addressed blob write (created_at-refreshing upsert) — so a re-used old blob this in-flight
137
+ // snapshot needs is "touched" to now and protected by the GC grace window until its manifest row commits.
47
138
  await this.putBlobRow(hash, bytes);
48
139
  });
49
140
  }
@@ -52,16 +143,21 @@ export class SqlFileSnapshotStore {
52
143
  }
53
144
  if (!cap.ok)
54
145
  return { ok: false, error: cap.error };
146
+ // Observability tail ([805]③a): surface tolerated-vanished counts (metric sink; observer errors swallowed —
147
+ // an observer must never break the never-throw snapshot seam).
55
148
  if (cap.skippedVanished > 0 && this.onSkippedVanished) {
149
+ // codex F8: Promise.resolve(...) absorbs an ASYNC sink's rejection (else it's an unhandledRejection);
150
+ // the outer try still absorbs a SYNC throw. Observer errors never break the never-throw seam either way.
56
151
  try {
57
152
  void Promise.resolve(this.onSkippedVanished(cap.skippedVanished)).catch(() => { });
58
153
  }
59
- catch { }
154
+ catch { /* observer must not break the seam */ }
60
155
  }
156
+ // Persist the manifest atomically (re-check has() under the txn so a concurrent winner isn't duplicated).
61
157
  await this.tx(async (conn) => {
62
158
  const exists = await conn.query(this.q("SELECT 1 FROM snapshot_manifest WHERE scope = ? AND snap_key = ? LIMIT 1", "SELECT 1 FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2 LIMIT 1"), [scope, key]);
63
159
  if (exists.rows[0])
64
- return;
160
+ return; // a concurrent snapshot won — create-once
65
161
  for (const [relPath, hash] of cap.value) {
66
162
  await conn.query(this.q("INSERT INTO snapshot_manifest (scope, snap_key, rel_path, blob_hash) VALUES (?,?,?,?)", "INSERT INTO snapshot_manifest (scope, snap_key, rel_path, blob_hash) VALUES ($1,$2,$3,$4)"), [scope, key, relPath, hash]);
67
163
  }
@@ -69,39 +165,68 @@ export class SqlFileSnapshotStore {
69
165
  return { ok: true };
70
166
  }
71
167
  catch (err) {
168
+ // The manifest-persist tx (or has()) raised — encode it (never throw out of the seam), parity with importManifest.
72
169
  return { ok: false, error: { code: "restore_failed", message: `snapshot persist failed: ${err.message}` } };
73
170
  }
74
171
  finally {
75
172
  this.inFlight.delete(flightKey);
76
173
  }
77
174
  }
175
+ /** 2c session-sync IMPORT side — the inverse of `exportManifest`/`getBlob`: persist a snapshot's content
176
+ * (manifest + content-addressed blobs) that was EXPORTED from ANOTHER backend (the local→cloud transfer: a src
177
+ * snapshot's manifest+blobs land here as a NEW `(scope,key)`). Unlike `snapshot()` this does NOT walk an env — the
178
+ * manifest is given; bytes come from `srcGetBlob(blobHash)`. Mirrors core's InMemory/File `importManifest` fail-closed
179
+ * matrix verbatim (core 1.141.0). Reuses snapshot()'s EXACT putBlob (created_at-refreshing upsert, GC-grace-safe)
180
+ * + manifest-tx (re-check under txn) patterns.
181
+ *
182
+ * NEVER-throws + FAIL-CLOSED (the matrix core's dual-review fixed, point-named to the durable twins):
183
+ * - CREATE-ONCE: an entryId's tree is immutable → an existing OR in-flight `(scope,key)` is a no-op `{ok:true}`.
184
+ * - `srcGetBlob` is a CROSS-BACKEND fetch (remote/IPC) that CAN REJECT (timeout/ECONNRESET/query error) — caught and
185
+ * encoded as `read_failed`, NEVER thrown out of the seam.
186
+ * - a MISSING blob (`bytes===undefined`) → `read_failed`.
187
+ * - content-address INTEGRITY: `sha256(bytes) !== hash` → `read_failed` (a 2c transfer can cross net/IPC; a corrupt
188
+ * blob stored under a mismatched hash would let a later restore silently write WRONG bytes — verify, don't trust).
189
+ * - ANY blob failure → EARLY-RETURN `{ok:false}` and the manifest tx is NEVER run, so `has()` stays false (no
190
+ * half-snapshot). The DISTINCT blobs already staged before the failing one are orphans (no manifest references
191
+ * them) — their snapshot_blob INDEX rows are reaped (and, under MinIO, the objects deleted) by the async grace-
192
+ * window sweepOrphanBlobs reaper; they never make this `(scope,key)` visible. */
78
193
  async importManifest(scope, key, manifest, srcGetBlob) {
194
+ // flightKey 分隔符统一 \x00(与 snapshot 一致;此前 pg 双胞用空格=两方法 in-flight 互斥键空间分裂,
195
+ // 并发 snapshot/import 同一 (scope,key) 不互斥——create-once tx 兜底故无损坏,但键空间应当同一)。
79
196
  const flightKey = `${scope}\x00${key}`;
80
197
  if (this.inFlight.has(flightKey))
81
- return { ok: true };
198
+ return { ok: true }; // create-once (mirrors snapshot)
82
199
  this.inFlight.add(flightKey);
83
200
  try {
201
+ // M4(接入审计):同 snapshot——create-once 前置 has() 探针在 try 内,DB 瞬态错不逃逸 never-throw seam。
84
202
  if (await this.has(scope, key))
85
203
  return { ok: true };
204
+ // STAGE every DISTINCT blob first (content-addressed: fetch each hash once). Any failure here EARLY-RETURNS before
205
+ // the manifest tx runs → fail-closed: no manifest row ⇒ has() false ⇒ never a half-snapshot a restore would
206
+ // silently truncate. Upsert REFRESHES created_at (incl dedup hits) → blobs touched=now, GC-grace-protected.
86
207
  for (const hash of new Set(manifest.values())) {
87
208
  let bytes;
88
209
  try {
89
210
  bytes = await srcGetBlob(hash);
90
211
  }
91
212
  catch (err) {
213
+ // NEVER throw out of the seam — a rejecting cross-backend fetch is `read_failed` (parity with InMemory/File).
92
214
  return { ok: false, error: { code: "read_failed", message: `source blob ${hash} fetch failed: ${err.message}` } };
93
215
  }
94
216
  if (!bytes)
95
217
  return { ok: false, error: { code: "read_failed", message: `missing source blob ${hash}` } };
218
+ // Content-address integrity — verify ONCE here (the durable backend doesn't blindly trust the transferred bytes).
96
219
  if (createHash("sha256").update(bytes).digest("hex") !== hash) {
97
220
  return { ok: false, error: { code: "read_failed", message: `source blob ${hash} fails content-address integrity` } };
98
221
  }
99
- await this.putBlobRow(hash, bytes);
222
+ await this.putBlobRow(hash, bytes); // the SAME created_at-refreshing upsert snapshot()/putBlob use (no SQL drift)
100
223
  }
224
+ // Commit the manifest atomically ONLY after every distinct blob is staged+verified (re-check has() under the txn so
225
+ // a concurrent winner isn't duplicated) — the SAME tx pattern as snapshot().
101
226
  await this.tx(async (conn) => {
102
227
  const exists = await conn.query(this.q("SELECT 1 FROM snapshot_manifest WHERE scope = ? AND snap_key = ? LIMIT 1", "SELECT 1 FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2 LIMIT 1"), [scope, key]);
103
228
  if (exists.rows[0])
104
- return;
229
+ return; // a concurrent import/snapshot won — create-once
105
230
  for (const [relPath, hash] of manifest) {
106
231
  await conn.query(this.q("INSERT INTO snapshot_manifest (scope, snap_key, rel_path, blob_hash) VALUES (?,?,?,?)", "INSERT INTO snapshot_manifest (scope, snap_key, rel_path, blob_hash) VALUES ($1,$2,$3,$4)"), [scope, key, relPath, hash]);
107
232
  }
@@ -109,13 +234,19 @@ export class SqlFileSnapshotStore {
109
234
  return { ok: true };
110
235
  }
111
236
  catch (err) {
237
+ // A persist (manifest tx) fs/db error must NOT throw out of the never-throw seam either — encode it. `restore_failed`
238
+ // mirrors core's File backend's outer catch (the manifest-PERSIST failure code, distinct from the fetch-side read_failed).
112
239
  return { ok: false, error: { code: "restore_failed", message: `import persist failed: ${err.message}` } };
113
240
  }
114
241
  finally {
115
242
  this.inFlight.delete(flightKey);
116
243
  }
117
244
  }
245
+ /** Converge `root` back to the snapshot (applyManifest: PHASE-1 remove symlinks/strays, PHASE-2 write captured). */
118
246
  async restore(scope, key, env, root, signal) {
247
+ // never-throw seam 全覆盖(L5,接入审计):入口 SELECT 也在 try 内——DB 层异常编码为 restore_failed。
248
+ // applyManifest calls getBlob UN-try/caught; a TRANSIENT MinIO fault THROWS from getBlob (so a restore fails LOUD,
249
+ // not silently-as-missing — finding 11). The same wrap turns that throw into {ok:false, restore_failed}.
119
250
  try {
120
251
  const { rows } = await this.db.query(this.q("SELECT rel_path, blob_hash FROM snapshot_manifest WHERE scope = ? AND snap_key = ?", "SELECT rel_path, blob_hash FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2"), [scope, key]);
121
252
  if (rows.length === 0)
@@ -131,25 +262,40 @@ export class SqlFileSnapshotStore {
131
262
  const { rows } = await this.db.query(this.q("SELECT 1 FROM snapshot_manifest WHERE scope = ? AND snap_key = ? LIMIT 1", "SELECT 1 FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2 LIMIT 1"), [scope, key]);
132
263
  return Boolean(rows[0]);
133
264
  }
265
+ /** 2c session-sync: enumerate every snapshot key (entryId) in a scope — the SAME enumeration reap()
266
+ * does (`SELECT DISTINCT snap_key`), exposed so 2c can export a session's full set of file snapshots. */
134
267
  async listKeys(scope) {
135
268
  const { rows } = await this.db.query(this.q("SELECT DISTINCT snap_key FROM snapshot_manifest WHERE scope = ?", "SELECT DISTINCT snap_key FROM snapshot_manifest WHERE scope = $1"), [scope]);
136
269
  return rows.map((r) => String(r.snap_key));
137
270
  }
271
+ /** 2c session-sync: the manifest (relPath → blobHash) for one snapshot — to EXPORT its content to another
272
+ * backend byte-for-byte (paired with getBlob for content-addressed transfer). Reads the STORED manifest rows (never
273
+ * re-hashes). 0 rows = no snapshot (a real snapshot always has ≥1 manifest row) → null (parity with InMemory). */
138
274
  async exportManifest(scope, key) {
139
275
  const { rows } = await this.db.query(this.q("SELECT rel_path, blob_hash FROM snapshot_manifest WHERE scope = ? AND snap_key = ?", "SELECT rel_path, blob_hash FROM snapshot_manifest WHERE scope = $1 AND snap_key = $2"), [scope, key]);
140
276
  if (rows.length === 0)
141
- return null;
277
+ return null; // absent (an empty-tree snapshot still has ≥1 row; 0 rows ⇒ no such snapshot)
142
278
  return new Map(rows.map((r) => [String(r.rel_path), String(r.blob_hash)]));
143
279
  }
280
+ /** 2c session-sync: the content-addressed bytes for `hash` (undefined if absent) — reads via the BlobBackend
281
+ * (SQL default or MinIO, where getBlob also verifies content-address integrity). */
144
282
  async getBlob(hash) {
145
283
  return this.blobs.getBlob(hash);
146
284
  }
285
+ /** The subset of `hashes` already present in the blob store — reads the SQL INDEX (one IN(...) for BOTH backends),
286
+ * so the §10 import blob-presence pre-check is a cheap index probe, NOT N full-object getBlob GETs (finding 12). */
147
287
  async hasBlobs(hashes) {
148
288
  return this.blobs.hasBlobs(hashes);
149
289
  }
290
+ /** workspace 浏览面(#3,[1894]②):per-blob 字节数——读 SQL INDEX 的 byte_len(单 IN 查询;
291
+ * MinIO 后端的索引行同带 byte_len,两后端同源)。tree 的 size 投影 + file 面「超限先拒后拉」用。 */
150
292
  async blobSizes(hashes) {
151
293
  return sqlBlobSizes(this.db.dialect, this.pool, hashes);
152
294
  }
295
+ /** GC: drop every snapshot in `scope` whose key is NOT in keepKeys. Drops ONLY the SQL manifest rows synchronously —
296
+ * the now-unreferenced blobs become ORPHANS (their snapshot_blob INDEX rows survive) and are collected LATER by the
297
+ * async sweepOrphanBlobs reaper (NEVER a synchronous external-store delete that could block/fail this reap on a MinIO
298
+ * blip — clay 2026-06-26 async-purge design). */
153
299
  async reap(scope, keepKeys) {
154
300
  return this.tx(async (conn) => {
155
301
  const { rows: keys } = await conn.query(this.q("SELECT DISTINCT snap_key FROM snapshot_manifest WHERE scope = ?", "SELECT DISTINCT snap_key FROM snapshot_manifest WHERE scope = $1"), [scope]);
@@ -160,10 +306,24 @@ export class SqlFileSnapshotStore {
160
306
  return drop.length;
161
307
  });
162
308
  }
309
+ /** E21 purge — drop ALL snapshots for a session (every key). Drops ONLY the SQL manifest rows synchronously → the
310
+ * session is immediately gone + its blobs become orphans; the actual MinIO/SQL BYTE deletion is the async
311
+ * sweepOrphanBlobs reaper's job (bounded-window EVENTUAL CONSISTENCY satisfies the E21 privacy/compliance "really
312
+ * delete the bytes" requirement WITHOUT a fragile synchronous object-store delete on the purge path). Scoped by sessionId. */
163
313
  async deleteBySession(scope) {
164
314
  const { affected } = await this.db.query(this.q("DELETE FROM snapshot_manifest WHERE scope = ?", "DELETE FROM snapshot_manifest WHERE scope = $1"), [scope]);
165
315
  return affected;
166
316
  }
317
+ /** Content-addressed blobs are shared across snapshots → GC only those NO manifest references. The orphan-hash SET is
318
+ * computed from the `snapshot_blob` INDEX in SQL (reference tracking stays in the DB — works for BOTH the SQL backend
319
+ * AND MinIO, whose index rows are kept bytes-NULL); the BlobBackend's deleteBlobs applies the grace window (a just-
320
+ * written blob a concurrent in-flight snapshot needs is never delete-in-use) and, for MinIO, deletes the objects.
321
+ * Returns the count ACTUALLY deleted (the grace-passed subset deleteBlobs reports — NOT the pre-grace orphan count,
322
+ * finding 13). Called ONLY from the async sweepOrphanBlobs reaper (never the synchronous purge path).
323
+ *
324
+ * The orphan-SET query text is IDENTICAL for both dialects (no placeholders) — a single shared statement, not a `q()`
325
+ * pair. A query error degrades to "zero orphans this sweep" (both twins already treated it that way — the reaper
326
+ * retries next tick, so under-collecting once is harmless; never surfaced as a hard failure). */
167
327
  async gcOrphanBlobs() {
168
328
  const { rows } = await this.db
169
329
  .query("SELECT blob_hash FROM snapshot_blob WHERE blob_hash NOT IN (SELECT blob_hash FROM snapshot_manifest)")
@@ -171,11 +331,16 @@ export class SqlFileSnapshotStore {
171
331
  const orphans = rows.map((r) => String(r.blob_hash));
172
332
  if (orphans.length === 0)
173
333
  return 0;
174
- return this.blobs.deleteBlobs(orphans);
334
+ return this.blobs.deleteBlobs(orphans); // grace-passed subset actually deleted (bounded concurrency inside)
175
335
  }
336
+ /** 2c session-sync DoS bound + the ASYNC byte-GC for reap/deleteBySession orphans — GLOBALLY GC grace-passed
337
+ * orphan blobs (standalone PUTs never imported AND the blobs a purge/reap left orphaned). The driver is the main.ts
338
+ * reaper (scheduled, error-tolerant: a per-object MinIO delete failure is skipped + retried next sweep → bounded-
339
+ * window eventual consistency). Reuses {@link gcOrphanBlobs} VERBATIM; returns the count actually deleted this sweep. */
176
340
  async sweepOrphanBlobs() {
177
341
  return this.gcOrphanBlobs();
178
342
  }
343
+ /** Run statements atomically; rolls back on failure. */
179
344
  async tx(fn) {
180
345
  const conn = await this.db.connect();
181
346
  try {
@@ -189,6 +354,7 @@ export class SqlFileSnapshotStore {
189
354
  await conn.rollback();
190
355
  }
191
356
  catch {
357
+ /* ignore */
192
358
  }
193
359
  throw err;
194
360
  }
@@ -197,11 +363,13 @@ export class SqlFileSnapshotStore {
197
363
  }
198
364
  }
199
365
  }
366
+ /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
200
367
  export class TiDBFileSnapshotStore extends SqlFileSnapshotStore {
201
368
  constructor(pool, blobBackend, bounds = DEFAULT_SNAPSHOT_BOUNDS) {
202
369
  super(mysqlDriver(pool), pool, blobBackend, bounds);
203
370
  }
204
371
  }
372
+ /** PostgreSQL binding — historical class name + ctor shape preserved. */
205
373
  export class PgFileSnapshotStore extends SqlFileSnapshotStore {
206
374
  constructor(pool, blobBackend, bounds = DEFAULT_SNAPSHOT_BOUNDS) {
207
375
  super(pgDriver(pool), pool, blobBackend, bounds);
@@ -1,2 +1,12 @@
1
+ /**
2
+ * SVC-2 — FILE-backed durable WorkflowJournalStore for the LOCAL (TOC) backend.
3
+ *
4
+ * Now a RE-EXPORT of core's copy (the write-here-then-core-adopts 剧本): the implementation was written here for server
5
+ * 1.89.0, then adopted verbatim by core (`@sema-agent/core` 1.223.0, file
6
+ * `src/stores/file/workflow-journal-store.ts` — crash-safe per-run JSONL, scope-guarded [CORE-9],
7
+ * idempotent-per-ordinal, oversize skip-journal, traversal-runId load degrade, 0o600/0o700). One source of
8
+ * truth in core; the SQL twins (tidb/pg) stay service-side (dialect = deployment glue). The service test
9
+ * suite (test/file-workflow-journal-store.test.ts) keeps pinning the contract through this re-export.
10
+ */
1
11
  export { FileWorkflowJournalStore } from "@sema-agent/core";
2
12
  //# sourceMappingURL=file-workflow-journal-store.d.ts.map
@@ -1,2 +1,12 @@
1
+ /**
2
+ * SVC-2 — FILE-backed durable WorkflowJournalStore for the LOCAL (TOC) backend.
3
+ *
4
+ * Now a RE-EXPORT of core's copy (the write-here-then-core-adopts 剧本): the implementation was written here for server
5
+ * 1.89.0, then adopted verbatim by core (`@sema-agent/core` 1.223.0, file
6
+ * `src/stores/file/workflow-journal-store.ts` — crash-safe per-run JSONL, scope-guarded [CORE-9],
7
+ * idempotent-per-ordinal, oversize skip-journal, traversal-runId load degrade, 0o600/0o700). One source of
8
+ * truth in core; the SQL twins (tidb/pg) stay service-side (dialect = deployment glue). The service test
9
+ * suite (test/file-workflow-journal-store.test.ts) keeps pinning the contract through this re-export.
10
+ */
1
11
  export { FileWorkflowJournalStore } from "@sema-agent/core";
2
12
  //# sourceMappingURL=file-workflow-journal-store.js.map