@sema-agent/server 1.323.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 +58 -2
  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
@@ -1,18 +1,73 @@
1
+ /**
2
+ * SendUserFile — the dual-track public-link ISSUER (design: sema-deploy docs/design/S3-PUBLIC-LINKS.md,
3
+ * settled cross-repo 2026-07-14; tool name SendUserFile is final).
4
+ *
5
+ * The LLM hands a user a file over the web session as a URL the browser fetches DIRECTLY from S3/MinIO
6
+ * (server relay was rejected: double bandwidth on third-party S3 + loses CDN). Two tracks, keyed on ttl:
7
+ *
8
+ * - ttl == 0 (PERMANENT, the default): upload to the anonymous-GET public bucket under an unguessable
9
+ * `<scopeSeg>/<uuidv7>/<name>` key (capability URL — GET-only bucket policy, List disabled, so the prefix IS
10
+ * the secret; scopeSeg = hashed tenant segment, see {@link scopeSegment} — multi-tenant list/revoke handle,
11
+ * 1.190.0 [768]②a). URL = plain `<publicEndpoint>/<bucket>/<key>` — no signature, never expires, CDN-cacheable.
12
+ * Revocation = delete the object.
13
+ * ⚠️ Bucket-policy footgun (live-verified 2026-07-14): `mc anonymous set download` ALSO enables
14
+ * ListBucket — an anonymous `GET /<bucket>/?list-type=2` then enumerates every uuidv7 key and the
15
+ * capability design collapses. Deployments MUST use a GetObject-only JSON policy
16
+ * (`mc anonymous set-json` with Action=["s3:GetObject"] on `arn:aws:s3:::<bucket>/*`): GET 200, LIST 403.
17
+ * - 0 < ttl ≤ 7d: upload to the PRIVATE bucket and return a SigV4 presigned GET. The signature is
18
+ * computed against the PUBLIC endpoint (SigV4 binds Host — signing with the internal host would 403
19
+ * the user's browser; the reverse proxy must pass Host through).
20
+ * - ttl > 7d: fail-loud — SigV4 caps presign at 7 days (physical limit); the caller should use ttl=0.
21
+ *
22
+ * Uploads always go through the INTERNAL endpoint (server-side PUT, its own signed request — Host binding
23
+ * is per-request, so internal-for-write + public-for-read coexist). Zero-dependency posture: presignS3Url
24
+ * + fetch, same as MinioBlobBackend / the k8s adapter.
25
+ */
1
26
  import { createHmac } from "node:crypto";
2
27
  import { uuidv7 } from "@sema-agent/core";
3
28
  import { presignS3CopyUrl, presignS3Url } from "./s3-presign.js";
29
+ /** SigV4 physical ceiling (AWS: X-Amz-Expires max 604800s = 7 days). */
4
30
  export const PRESIGN_MAX_TTL_SEC = 604_800;
31
+ /**
32
+ * Multi-tenant governance (clay ruling 2026-07-14, blackboard [768]② variant a with a HASHED segment): every
33
+ * object key carries a per-tenant SCOPE SEGMENT so tenants can be listed/revoked by prefix on the store itself,
34
+ * WITHOUT leaking the principal into the (user-visible, forwardable) URL. The segment is
35
+ * `HMAC-SHA256(secretKey, "sendfile-scope:" + scope)` hex, truncated to 32 chars:
36
+ * - keyed on the S3 SECRET KEY — server-held and stable, so the mapping is deterministic per deployment but
37
+ * unforgeable/unenumerable from outside (a URL holder cannot test principal guesses without the key);
38
+ * - the "sendfile-scope:" DOMAIN SEPARATOR pins this HMAC use — the same secret signs SigV4 requests, and a
39
+ * cross-protocol collision between "scope segment" and any other HMAC-of-attacker-controlled-string use is
40
+ * exactly what domain separation exists to prevent;
41
+ * - 32 hex chars (128 bits) is a NAMESPACE label, not a secret: the cross-tenant BIRTHDAY bound at even a
42
+ * billion tenants is ~n²/2^129 ≈ 2^-69 — negligible. (The earlier 16-hex/64-bit cut had a ~2.7% birthday
43
+ * collision probability at that scale — n²/2^65, NOT the per-pair 2^-32 the old note claimed; codex audit
44
+ * F4, widened pre-release at zero cost.) Capability secrecy still lives in the uuidv7 segment. Key budget
45
+ * stays comfortable: worst-case private-track key = 9 ("sendfile/") + 32 + 1 + 36 + 1 + 128 = 207 chars,
46
+ * under both sendfile_link.object_key VARCHAR(512) and S3's 1024-byte key ceiling.
47
+ * scope undefined/"" (single-user deployments have no principal) → the literal "_" sentinel (same sentinel the
48
+ * checkpoint scope column uses for anonymous), so the key SHAPE is uniform across deployments.
49
+ * ⚠️ Objects issued before 1.190.0 have NO scope segment (`<uuidv7>/<name>`); they stay valid — the segment is
50
+ * purely additive (URLs are capability-complete either way), zero migration.
51
+ */
5
52
  export function scopeSegment(secretKey, scope) {
6
53
  if (scope === undefined || scope === "")
7
54
  return "_";
8
55
  return createHmac("sha256", secretKey).update(`sendfile-scope:${scope}`).digest("hex").slice(0, 32);
9
56
  }
57
+ /**
58
+ * Collapse an arbitrary client-supplied name to ONE safe path segment: strip directories (both
59
+ * separators), control/bidi characters (the session-title sanitize class), leading dots (no hidden
60
+ * files / no "." ".."), and cap the length (S3 key segment budget; keep the extension when truncating).
61
+ * Empty in ⇒ "file".
62
+ */
10
63
  export function sanitizeSendFileName(name) {
11
64
  const base = (name ?? "").split(/[/\\]/).pop() ?? "";
12
65
  let clean = base.replace(/[\u0000-\u001f\u007f\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "").replace(/^\.+/, "").trim();
13
66
  if (clean.length > 128) {
14
67
  const dot = clean.lastIndexOf(".");
15
68
  const ext = dot > 0 && clean.length - dot <= 16 ? clean.slice(dot) : "";
69
+ // code-point 安全截断:UTF-16 slice 会切断代理对,留下 lone surrogate → encodeURIComponent 抛
70
+ // URIError(此时对象已上传,链接却铸不出来)。按 code point 截,预算=128-ext 的 UTF-16 units。
16
71
  const budget = 128 - ext.length;
17
72
  let head = "";
18
73
  for (const ch of clean) {
@@ -51,9 +106,11 @@ export function createSendUserFileIssuer(cfg, deps) {
51
106
  throw new Error(`SendUserFile ttl must be a non-negative INTEGER of seconds (0 = permanent; SigV4 X-Amz-Expires is integer-only); got ${ttl}`);
52
107
  const name = sanitizeSendFileName(filename);
53
108
  const publicBase = cfg.publicEndpoint.replace(/\/+$/, "");
54
- const seg = scopeSegment(cfg.secretKey, scope);
109
+ const seg = scopeSegment(cfg.secretKey, scope); // hashed tenant segment ("_" single-user) — see scopeSegment doc
55
110
  const scopeField = scope !== undefined && scope !== "" ? { scope } : {};
56
111
  if (ttl === 0) {
112
+ // Permanent track: capability URL on the anonymous-GET bucket. Key = <scopeSeg>/<uuidv7>/<name>
113
+ // (pre-1.190 objects have no scope segment — additive, both shapes stay valid).
57
114
  const key = `${seg}/${uuidv7()}/${name}`;
58
115
  await upload(cfg.publicBucket, key, bytes);
59
116
  const url = `${publicBase}/${cfg.publicBucket}/${key.split("/").map(encodeURIComponent).join("/")}`;
@@ -62,6 +119,7 @@ export function createSendUserFileIssuer(cfg, deps) {
62
119
  if (ttl > PRESIGN_MAX_TTL_SEC) {
63
120
  throw new Error(`SendUserFile ttl ${ttl}s exceeds the SigV4 presign ceiling (${PRESIGN_MAX_TTL_SEC}s = 7 days) — use ttl=0 for a permanent link`);
64
121
  }
122
+ // Presigned track: private bucket upload, GET signed against the PUBLIC endpoint (Host-bound).
65
123
  const key = `${cfg.privateKeyPrefix}${seg}/${uuidv7()}/${name}`;
66
124
  await upload(cfg.privateBucket, key, bytes);
67
125
  const url = presignS3Url({
@@ -77,6 +135,9 @@ export function createSendUserFileIssuer(cfg, deps) {
77
135
  });
78
136
  return { url, filename: name, size: bytes.byteLength, ttlSec: ttl, track: "presigned", bucket: cfg.privateBucket, key, ...scopeField };
79
137
  };
138
+ // v2 (sandbox direct-upload): same key/track/URL algebra as issue(), split into sign-then-finalize so
139
+ // the BYTES move sandbox→S3 directly (server never relays; creds never enter the sandbox — only a
140
+ // single-object, method-bound, 120s PUT capability on a STAGING key does; see PreparedDirectUpload).
80
141
  const prepareDirectUpload = (filename, ttlSec, scope) => {
81
142
  if (cfg.publicEndpoint === undefined) {
82
143
  throw new Error("SendUserFile is not configured for public links: set S3_PUBLIC_ENDPOINT (the S3/MinIO base URL users can reach; falls back to S3_ENDPOINT when that is already public)");
@@ -91,11 +152,21 @@ export function createSendUserFileIssuer(cfg, deps) {
91
152
  const publicBase = cfg.publicEndpoint.replace(/\/+$/, "");
92
153
  const putBase = (cfg.sandboxPutEndpoint ?? cfg.publicEndpoint).replace(/\/+$/, "");
93
154
  const bucket = ttl === 0 ? cfg.publicBucket : cfg.privateBucket;
155
+ // Same key algebra as issue() incl. the hashed tenant segment — prepare/finalize share ONE key, so the
156
+ // segment is fixed here (the PUT capability is already scoped to it).
94
157
  const seg = scopeSegment(cfg.secretKey, scope);
95
158
  const scopeField = scope !== undefined && scope !== "" ? { scope } : {};
96
159
  const key = ttl === 0 ? `${seg}/${uuidv7()}/${name}` : `${cfg.privateKeyPrefix}${seg}/${uuidv7()}/${name}`;
160
+ // 修3 staging two-phase: the sandbox-visible PUT capability is scoped to a STAGING key in the PRIVATE
161
+ // bucket (never anonymous-readable, never handed out as a URL) — the final key above gets its bytes via
162
+ // a server-side CopyObject only, so no /proc reader of the exec argv can ever rewrite what the user's
163
+ // link points at. The staging key keeps the tenant segment (orphan attribution on manual cleanup).
97
164
  const stagingBucket = cfg.privateBucket;
98
165
  const stagingKey = `${cfg.privateKeyPrefix}staging/${seg}/${uuidv7()}`;
166
+ // TTL 120s, not the uploader default 600: the URL rides the sandbox exec argv (visible to the sandbox's
167
+ // own processes / possibly an apiserver audit log) — anyone who reads it gains nothing they don't already
168
+ // have DURING the upload (the sandbox owns the file), and since 修3 the writable object is only the
169
+ // staging key anyway. Keep the window as short as a slow 100MiB upload allows.
99
170
  const putUrl = presignS3Url({
100
171
  endpoint: putBase,
101
172
  bucket: stagingBucket,
@@ -120,16 +191,21 @@ export function createSendUserFileIssuer(cfg, deps) {
120
191
  ...(cfg.region !== undefined ? { region: cfg.region } : {}),
121
192
  expiresSec: 60,
122
193
  });
194
+ // 修2: best-effort reclaim — any failure AFTER the object landed must delete it before throwing
195
+ // (a transient HEAD 5xx used to strand an unledgered, undelivered orphan in the bucket). Returns the
196
+ // TRUE outcome so error messages never claim "reclaimed" for a delete that itself failed (F2 posture).
123
197
  const reclaim = async (b, k) => {
124
198
  try {
125
199
  const r = await fetch(sign("DELETE", b, k), { method: "DELETE" });
126
- return r.ok || r.status === 404;
200
+ return r.ok || r.status === 404; // S3 DELETE is idempotent — a missing key IS reclaimed
127
201
  }
128
202
  catch {
129
203
  return false;
130
204
  }
131
205
  };
132
206
  const outcome = (ok, what) => (ok ? `${what} reclaimed` : `${what} reclaim FAILED (orphan; operator cleanup needed)`);
207
+ // ① HEAD the STAGING object (internal endpoint, adapter creds): existence + a cheap early cap gate
208
+ // (fail before copying a huge object). Advisory only for size — the authoritative reading is ④.
133
209
  let head;
134
210
  try {
135
211
  head = await fetch(sign("HEAD", stagingBucket, stagingKey), { method: "HEAD" });
@@ -141,7 +217,7 @@ export function createSendUserFileIssuer(cfg, deps) {
141
217
  if (head.status === 404)
142
218
  throw new Error("SendUserFile upload verification failed: object not found after upload (HEAD 404)");
143
219
  if (!head.ok) {
144
- const r = await reclaim(stagingBucket, stagingKey);
220
+ const r = await reclaim(stagingBucket, stagingKey); // transient 5xx ≠ missing — the object may be live; reclaim it
145
221
  throw new Error(`SendUserFile upload verification failed: store error (HEAD ${head.status}) — ${outcome(r, "staged object")}`);
146
222
  }
147
223
  const staged = Number.parseInt(head.headers.get("content-length") ?? "", 10);
@@ -153,6 +229,9 @@ export function createSendUserFileIssuer(cfg, deps) {
153
229
  await reclaim(stagingBucket, stagingKey);
154
230
  throw new Error(`file exceeds the ${maxBytes >= 1024 * 1024 ? `${Math.floor(maxBytes / (1024 * 1024))} MiB` : `${maxBytes} byte`} per-file cap (uploaded object was ${staged} bytes — deleted)`);
155
231
  }
232
+ // ② Server-side CopyObject staging→final (internal endpoint, signed x-amz-copy-source): the ONLY
233
+ // write the final key ever receives. NB: S3 can return 200 with an error BODY on slow copies — require
234
+ // the CopyObjectResult marker, not just the status.
156
235
  const copy = presignS3CopyUrl({
157
236
  endpoint: cfg.endpoint,
158
237
  bucket,
@@ -173,10 +252,15 @@ export function createSendUserFileIssuer(cfg, deps) {
173
252
  }
174
253
  catch (e) {
175
254
  const r1 = await reclaim(stagingBucket, stagingKey);
176
- const r2 = await reclaim(bucket, key);
255
+ const r2 = await reclaim(bucket, key); // the copy may have half-landed — reclaim both sides
177
256
  throw new Error(`SendUserFile upload verification failed: staging promotion failed (${e instanceof Error ? e.message : String(e)}) — ${outcome(r1 && r2, "uploaded object")}`);
178
257
  }
258
+ // ③ Staging object served its purpose — reclaim (best-effort: a leftover is a private-bucket blob
259
+ // whose write capability expires in ≤120s; it can't become a public link).
179
260
  await reclaim(stagingBucket, stagingKey);
261
+ // ④ Authoritative HEAD of the FINAL object — immutable to the sandbox, so THIS size is what the copy
262
+ // pinned and what the card must carry (re-checked against the cap: a /proc reader could have re-PUT
263
+ // staging between ① and ②, swapping in different bytes).
180
264
  let fin;
181
265
  try {
182
266
  fin = await fetch(sign("HEAD", bucket, key), { method: "HEAD" });
@@ -220,6 +304,10 @@ export function createSendUserFileIssuer(cfg, deps) {
220
304
  };
221
305
  return { putUrl, verifySize, finalize };
222
306
  };
307
+ // Ledger reclaim (fail-loud record path): delete an already-uploaded object whose ledger record failed —
308
+ // a public-track object with no ledger row would be unmanageable (no per-tenant list/revoke handle). Signed
309
+ // DELETE against the INTERNAL endpoint (adapter creds, same posture as verifySize). S3 DELETE is idempotent
310
+ // (204 on a missing key), so callers may retry safely.
223
311
  const deleteObject = async (bucket, key) => {
224
312
  if (deps?.remove)
225
313
  return deps.remove(bucket, key);
@@ -2,20 +2,35 @@ import { type SessionPolicyStore, type SessionPermissionRules, type StoredSessio
2
2
  import type { Pool as MySqlPool } from "mysql2/promise";
3
3
  import type { Pool as PgPool } from "pg";
4
4
  import { type SqlDriver } from "./sql-driver.js";
5
+ /** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
5
6
  export declare class SqlSessionPolicyStore implements SessionPolicyStore {
6
7
  private readonly db;
7
8
  constructor(db: SqlDriver);
9
+ /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
8
10
  private q;
9
11
  private isDupKey;
12
+ /** Read the stored rules for (session, principal-owner); null when none (parity with InMemory). */
10
13
  getRules(sessionId: string, principal?: string): Promise<StoredSessionRules | null>;
14
+ /** 2c session-sync: every (principal, rules) record for a session across ALL principals — getRules is
15
+ * per-(session,principal), but a cross-backend session EXPORT must bundle the WHOLE session's policy (the importer
16
+ * replays each via putRules). principal is read from the DENORMALIZED column (the sha256 policy_key is one-way);
17
+ * NULL → undefined = the session-wide row (parity with core's InMemory `principal ?? undefined`). Rules carry rev. */
11
18
  listBySession(sessionId: string): Promise<SessionRulesRecord[]>;
19
+ /** Operator/owner write: CAS + tighten-only (unless operator) + rev bump, atomic in one txn; first-write race retried. */
12
20
  putRules(sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts?: PutRulesOptions): Promise<StoredSessionRules>;
21
+ /** E21 purge — drop ALL policy rows for a session (every principal). Scoped by session_id (the route owner-gates).
22
+ * Return shape aligned to core 1.423's optional interface seam (`Promise<void>`) — the affectedRows count was
23
+ * incidental and unconsumed; the integration test asserts the post-state (listBySession empty) instead. */
13
24
  deleteBySession(sessionId: string): Promise<void>;
25
+ /** `begin()` (NOT `beginPessimistic()`) — matches both twins' original call shape: TiDB's native
26
+ * `beginTransaction()` (optimistic), PG's plain `BEGIN`. Normalized by SqlDriver's shared tx plumbing. */
14
27
  private tx;
15
28
  }
29
+ /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
16
30
  export declare class TiDBSessionPolicyStore extends SqlSessionPolicyStore {
17
31
  constructor(pool: MySqlPool);
18
32
  }
33
+ /** PostgreSQL binding — historical class name + ctor shape preserved. */
19
34
  export declare class PgSessionPolicyStore extends SqlSessionPolicyStore {
20
35
  constructor(pool: PgPool);
21
36
  }
@@ -1,14 +1,57 @@
1
+ /**
2
+ * Durable `SessionPolicyStore` (E6) — SINGLE-FILE DUAL-DIALECT (design/158 A12 定型半场). ONE
3
+ * implementation, TWO dialects; the historical `TiDBSessionPolicyStore` / `PgSessionPolicyStore` class
4
+ * names survive as thin ctor subclasses so every consumer (store-backend.ts, the session-sync
5
+ * fanout/push/pull suites, the E6 equivalence suite) is untouched.
6
+ *
7
+ * Operator-tightened per-session tool-permission rules (CAS-versioned, tighten-only) — the cross-replica
8
+ * twin of core's InMemorySessionPolicyStore (`@sema-agent/core` session-policy-store.js), backing core's
9
+ * `RunnerDeps.sessionPolicyStore`. core READS via getRules at prepare-time and folds the rules into the
10
+ * ToolPolicy FIRST (subtract-only); the service's operator endpoint WRITES via putRules.
11
+ *
12
+ * BYTE-MATCH discipline (the env-gated equivalence test vs InMemorySessionPolicyStore is the contract):
13
+ * - Key = `sha256(JSON.stringify([sessionId, principal ?? null]))` — the SHA of core's exact composite key. The hash
14
+ * (a) gives a FIXED 64-char PK regardless of how long / JSON-escapable the principal is (a VARCHAR(190) principal of
15
+ * control chars JSON-escapes past any fixed width — review finding), and (b) sidesteps PK collation pitfalls
16
+ * (trailing-space PAD). `principal` undefined/null collapse to one row, "" stays distinct (core's `?? null`).
17
+ * - putRules order is LOAD-BEARING: (1) CAS expectedRev (stale → `conflict`), (2) normalize+stripRev the incoming
18
+ * rules, (3) tighten gate via the REUSED core `loosenReasons` (non-operator loosen → `loosen_forbidden`), (4) rev
19
+ * bump (+1; first = 1). A missing row reads as rev 0 (so expectedRev:0 = create-once). The conflict + loosen_forbidden
20
+ * MESSAGES match core verbatim. read-check-write runs in ONE txn (SELECT…FOR UPDATE the prior so the loosen diff +
21
+ * CAS are atomic; the InMemory gets this free from single-threaded JS). The UPDATE additionally guards `AND rev=?`
22
+ * (defense beyond the row lock); a concurrent FIRST-write (no row → FOR UPDATE takes no lock) loses the PK race and
23
+ * is RETRIED (re-read takes the UPDATE path) so a no-expectedRev create matches core's serialized rev-2 outcome
24
+ * rather than a spurious conflict.
25
+ * - allowDirs is stored NORMALIZED (`..`-free) so a later read-back `prior` feeds loosenReasons in the same canonical
26
+ * shape as `clean` (the /work/../etc tighten-bypass blocker). The return is structuredClone'd (core's isolation).
27
+ * `operator` is honored verbatim — core never adjudicates it; the HTTP route FREEZES it from a verified principal
28
+ * (mirrors steer.trusted). `deleteBySession` is a service extension (E21 purge), not core's interface.
29
+ *
30
+ * ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
31
+ * - `?` placeholders vs `$n`
32
+ * - dup-key errno `1062`/`ER_DUP_ENTRY` vs SQLSTATE `23505`
33
+ * - PG-only genuinely-divergent ALGORITHM: `pgHasUnstorable` rejects rules containing NUL / lone-surrogate
34
+ * bytes (fail-loud, never rewritten) before the write; TiDB has no equivalent guard. Kept as an explicit
35
+ * `if (this.db.dialect === "pg")` branch in putRules — see the inline 🔴 comment there for the codex
36
+ * R2-H1→R3 rationale (rules are the authorization semantic body: reject-only, never silently rewritten).
37
+ * - transaction plumbing: TiDB's native `beginTransaction/commit/rollback` vs PG's statement-issued
38
+ * `BEGIN/COMMIT/ROLLBACK` — normalized by `SqlDriver`'s shared `connect()`/`begin()`/`commit()`/`rollback()`
39
+ * (see sql-driver.ts); this store's original `beginTransaction()` call shape maps onto the driver's plain
40
+ * `begin()` (NOT `beginPessimistic()` — the original TiDB store never opted into `BEGIN PESSIMISTIC`).
41
+ */
1
42
  import { createHash } from "node:crypto";
2
43
  import { loosenReasons, SessionPolicyError, normalizeRules, stripRev } from "@sema-agent/core";
3
44
  import { pgHasUnstorable } from "./pg-safe-json.js";
4
45
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
5
46
  const policyKey = (sessionId, principal) => createHash("sha256").update(JSON.stringify([sessionId, principal ?? null])).digest("hex");
6
- const parseRules = (v) => (typeof v === "string" ? JSON.parse(v) : v);
47
+ const parseRules = (v) => (typeof v === "string" ? JSON.parse(v) : v); // R7 终形:拒绝式下库内恒干净,直读
48
+ /** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
7
49
  export class SqlSessionPolicyStore {
8
50
  db;
9
51
  constructor(db) {
10
52
  this.db = db;
11
53
  }
54
+ /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
12
55
  q(tidb, pg) {
13
56
  return this.db.dialect === "tidb" ? tidb : pg;
14
57
  }
@@ -17,12 +60,17 @@ export class SqlSessionPolicyStore {
17
60
  ? e?.code === "ER_DUP_ENTRY" || e?.errno === 1062
18
61
  : e?.code === "23505";
19
62
  }
63
+ /** Read the stored rules for (session, principal-owner); null when none (parity with InMemory). */
20
64
  async getRules(sessionId, principal) {
21
65
  const { rows } = await this.db.query(this.q("SELECT rules, rev FROM session_policy WHERE policy_key = ?", "SELECT rules, rev FROM session_policy WHERE policy_key = $1"), [policyKey(sessionId, principal)]);
22
66
  if (!rows[0])
23
67
  return null;
24
68
  return { ...parseRules(rows[0].rules), rev: Number(rows[0].rev) };
25
69
  }
70
+ /** 2c session-sync: every (principal, rules) record for a session across ALL principals — getRules is
71
+ * per-(session,principal), but a cross-backend session EXPORT must bundle the WHOLE session's policy (the importer
72
+ * replays each via putRules). principal is read from the DENORMALIZED column (the sha256 policy_key is one-way);
73
+ * NULL → undefined = the session-wide row (parity with core's InMemory `principal ?? undefined`). Rules carry rev. */
26
74
  async listBySession(sessionId) {
27
75
  const { rows } = await this.db.query(this.q("SELECT principal, rules, rev FROM session_policy WHERE session_id = ?", "SELECT principal, rules, rev FROM session_policy WHERE session_id = $1"), [sessionId]);
28
76
  return rows.map((r) => ({
@@ -30,6 +78,7 @@ export class SqlSessionPolicyStore {
30
78
  rules: { ...parseRules(r.rules), rev: Number(r.rev) },
31
79
  }));
32
80
  }
81
+ /** Operator/owner write: CAS + tighten-only (unless operator) + rev bump, atomic in one txn; first-write race retried. */
33
82
  async putRules(sessionId, principal, rules, opts) {
34
83
  const key = policyKey(sessionId, principal);
35
84
  for (let attempt = 0;; attempt++) {
@@ -38,16 +87,24 @@ export class SqlSessionPolicyStore {
38
87
  const { rows } = await conn.query(this.q("SELECT rules, rev FROM session_policy WHERE policy_key = ? FOR UPDATE", "SELECT rules, rev FROM session_policy WHERE policy_key = $1 FOR UPDATE"), [key]);
39
88
  const prior = rows[0] ? { rules: parseRules(rows[0].rules), rev: Number(rows[0].rev) } : undefined;
40
89
  const priorRev = prior?.rev ?? 0;
90
+ // (1) CAS — checked BEFORE the tighten gate; a missing row is rev 0 (expectedRev:0 = create-once).
41
91
  if (opts?.expectedRev !== undefined && priorRev !== opts.expectedRev) {
42
92
  throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts.expectedRev}, have ${priorRev})`);
43
93
  }
94
+ // (2) normalize+stripRev the INCOMING rules (allowDirs `..`-free before store AND before the loosen diff).
44
95
  const clean = normalizeRules(stripRev(rules));
96
+ // (3) tighten gate — ONLY when a prior exists AND not an operator write (strict !== true). REUSE core loosenReasons.
45
97
  if (prior && opts?.operator !== true) {
46
98
  const loosens = loosenReasons(prior.rules, clean);
47
99
  if (loosens.length > 0) {
48
100
  throw new SessionPolicyError("loosen_forbidden", `a non-operator session-rule write may only tighten; refused: ${loosens.join("; ")}`);
49
101
  }
50
102
  }
103
+ // 🔴 PG-only genuinely-divergent ALGORITHM (not a syntax difference): codex R2-H1→R3(NUL 批终形):
104
+ // rules=授权语义本体,**拒绝式**——含不可存字节的规则是垃圾输入(NUL 路径本不可用),fail-loud
105
+ // 拒收;绝不改写(有损=写读不一致)、绝不信封(R3:滚动部署/回滚窗口老 replica 读信封=字段全缺=
106
+ // fail-open 放行,授权面不赌版本偏斜)。TiDB's VARBINARY/utf8mb4 storage never chokes on these bytes
107
+ // the way PG's protocol-byte encoding does, so it carries no equivalent guard.
51
108
  if (this.db.dialect === "pg" && pgHasUnstorable(clean)) {
52
109
  throw new SessionPolicyError("loosen_forbidden", "rules contain unstorable bytes (NUL / lone surrogate) — refused; strip them at the source (authorization rules are never rewritten by the store)");
53
110
  }
@@ -55,26 +112,38 @@ export class SqlSessionPolicyStore {
55
112
  const json = JSON.stringify(clean);
56
113
  const now = new Date();
57
114
  if (prior) {
115
+ // `principal` is enumeration-only (not a key, not CAS): write it on UPDATE too so a pre-migration
116
+ // row (principal NULL) SELF-HEALS to its real principal on the next write — never changes the rev/CAS path.
58
117
  const upd = await conn.query(this.q("UPDATE session_policy SET principal = ?, rules = ?, rev = ?, updated_at = ? WHERE policy_key = ? AND rev = ?", "UPDATE session_policy SET principal = $1, rules = $2::jsonb, rev = $3, updated_at = $4 WHERE policy_key = $5 AND rev = $6"), [principal ?? null, json, nextRev, now, key, priorRev]);
118
+ // Defense beyond the FOR UPDATE row lock: if rev moved under us, the CAS lost (treat as conflict).
59
119
  if (upd.affected !== 1)
60
120
  throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts?.expectedRev ?? priorRev}, have ${priorRev})`);
61
121
  }
62
122
  else {
123
+ // No prior → FOR UPDATE took no row lock; a concurrent first-writer can win the PK. Let the dup-key
124
+ // PROPAGATE (do NOT convert to conflict here) so the outer loop RETRIES — the re-read then finds the row
125
+ // and takes the UPDATE path (matching core's serialized rev-2 outcome / or an honest CAS conflict if
126
+ // expectedRev was supplied), instead of a spurious conflict. `principal ?? null` = the enumeration column.
63
127
  await conn.query(this.q("INSERT INTO session_policy (policy_key, session_id, principal, rules, rev, created_at, updated_at) VALUES (?,?,?,?,?,?,?)", "INSERT INTO session_policy (policy_key, session_id, principal, rules, rev, created_at, updated_at) VALUES ($1,$2,$3,$4::jsonb,$5,$6,$7)"), [key, sessionId, principal ?? null, json, nextRev, now, now]);
64
128
  }
65
- return structuredClone({ ...clean, rev: nextRev });
129
+ return structuredClone({ ...clean, rev: nextRev }); // core hands back a fully isolated tree
66
130
  });
67
131
  }
68
132
  catch (e) {
69
133
  if (this.isDupKey(e) && attempt < 4)
70
- continue;
134
+ continue; // concurrent first-write — re-read takes the UPDATE/CAS path
71
135
  throw e;
72
136
  }
73
137
  }
74
138
  }
139
+ /** E21 purge — drop ALL policy rows for a session (every principal). Scoped by session_id (the route owner-gates).
140
+ * Return shape aligned to core 1.423's optional interface seam (`Promise<void>`) — the affectedRows count was
141
+ * incidental and unconsumed; the integration test asserts the post-state (listBySession empty) instead. */
75
142
  async deleteBySession(sessionId) {
76
143
  await this.db.query(this.q("DELETE FROM session_policy WHERE session_id = ?", "DELETE FROM session_policy WHERE session_id = $1"), [sessionId]);
77
144
  }
145
+ /** `begin()` (NOT `beginPessimistic()`) — matches both twins' original call shape: TiDB's native
146
+ * `beginTransaction()` (optimistic), PG's plain `BEGIN`. Normalized by SqlDriver's shared tx plumbing. */
78
147
  async tx(fn) {
79
148
  const conn = await this.db.connect();
80
149
  try {
@@ -88,6 +157,7 @@ export class SqlSessionPolicyStore {
88
157
  await conn.rollback();
89
158
  }
90
159
  catch {
160
+ /* ignore */
91
161
  }
92
162
  throw err;
93
163
  }
@@ -96,11 +166,13 @@ export class SqlSessionPolicyStore {
96
166
  }
97
167
  }
98
168
  }
169
+ /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
99
170
  export class TiDBSessionPolicyStore extends SqlSessionPolicyStore {
100
171
  constructor(pool) {
101
172
  super(mysqlDriver(pool));
102
173
  }
103
174
  }
175
+ /** PostgreSQL binding — historical class name + ctor shape preserved. */
104
176
  export class PgSessionPolicyStore extends SqlSessionPolicyStore {
105
177
  constructor(pool) {
106
178
  super(pgDriver(pool));
@@ -1,10 +1,55 @@
1
1
  import { type SessionStore } from "@sema-agent/core";
2
2
  import type { ServiceConfig } from "../config-types.js";
3
3
  import type { StoreBackend } from "./store-backend.js";
4
+ /**
5
+ * Session-store plugin seam.
6
+ *
7
+ * - `memory` (default): core's in-memory `TtlSessionStore` (lost on restart, 7-day idle TTL).
8
+ * - `tidb`: the durable "Session event-log center" (L1) — append-only events + wake/resume +
9
+ * cross-instance CAS (F2), on whichever SQL engine `DB_BACKEND` selects (TiDB or PG, via the store backend).
10
+ * Optionally wrapped in a warm cache (reuse a hot session across turns; requires session affinity) —
11
+ * set `SESSION_CACHE_TTL_SEC=0` to disable.
12
+ */
4
13
  export declare function createSessionStore(config: ServiceConfig, backend?: StoreBackend, metrics?: {
5
14
  inc(name: string, labels?: Record<string, string>, n?: number): void;
6
15
  }): SessionStore;
16
+ /**
17
+ * core 1.382(design/153,[1561] 提货单①②)`SubagentToolOptions.background.ensureChildSessionDurable`
18
+ * 的实现体——从 main.ts 装配点抽出为可独立测试的纯函数(main.ts 里的其余装配闭包历来无单测覆盖,
19
+ * 这条因为承载真实的 park/veto 裁决,拆出来直接钉)。
20
+ *
21
+ * 只在 `sessionId` 已经真正路由到 HOST(durable)会话店、且**真有内容**时 resolve;其余(未路由到 host/
22
+ * 两店皆无/host 店只有一行空壳)一律 reject——veto,core 侧子代按 pre-153 settle failed,零回归,不是
23
+ * 危险失败模式(见函数体内注释)。
24
+ *
25
+ * 传入必须是 HOST 店本身(main.ts 里 `createSessionStore(...)` 的返回值),不能是 subRunner 用的
26
+ * `ForkRoutingSessionStore` 包装店——那层包装对 `acquire(id,{requireExisting:true})` 是 transient-优先
27
+ * (F-7 案:让"真会话在 transient"赢过"host 店里的同 id 空壳"),会把"只在 transient 店有真内容"误判成
28
+ * "host 店已经有了"。这里要问的是纯粹"host 店本身是否已经有这份真内容",必须绕开那层路由。
29
+ *
30
+ * 🔴 [1575] F3(cli 复查抓获,真红确认)调用方前提——**本函数假设 `hostStore.release()` 是"归还租约"
31
+ * 语义(不销毁内容)**,这对 core `TiDBSessionStore` 成立(`release()` 逐字注释「Durable history is
32
+ * retained; just drop any in-flight cache entry」),但对无 repo 构造的 `TtlSessionStore`(`createSessionStore`
33
+ * 在 `SESSION_BACKEND !== "tidb"` 时返回的那种)**不成立**——那个店形的 `release()` 是整删会话
34
+ * (`evictPolicy="delete"`)。本函数自己**不做**、也无法通用地做"这个 store 实例的 release 是否安全"的
35
+ * 判别(`SessionStore` 接口没有暴露这个信息,类型层面区分不了两种店形)——上文"误判方向永远偏保守"
36
+ * 只覆盖 `getLeafId` 那道校验,不覆盖"传了一个 release=delete 的店进来"这个前提违反。**调用方必须
37
+ * 保证只在真正 durable(release 非破坏性)的店上调用本函数**——server 侧的强制点在 main.ts 装配区:
38
+ * `ensureChildSessionDurable` 闭包只在 `config.sessionBackend === "tidb"` 时构造。
39
+ */
7
40
  export declare function ensureSessionDurable(hostStore: SessionStore, sessionId: string): Promise<void>;
41
+ /**
42
+ * [1593] 主动晋升版 ensureChildSessionDurable(cli TiDB 真机撞获:1.263 的只查不迁形在拆店生产形下
43
+ * 恒否决 park——bg 子会话住 subRunner 私有 TTL 店、从不在 host 店,core 收 veto 即 expire checkpoint
44
+ * (14ms~2s)+ settle failed,赎回窗口不存在):
45
+ * 1. 快路:已在 host 店且有真内容 → 直接通过(幂等/重入)。
46
+ * 2. 晋升:routing.promoteToHost 把子会话整树从私店拷进 host 店(拷贝→私店 forget→路由记 host,
47
+ * 三步见 ForkRoutingSessionStore.promoteToHost)。
48
+ * 3. 复验:同一把 `ensureSessionDurable` 严检(leaf 非空)——晋升产物必须达到与既有校验完全相同的
49
+ * durable 标准,任何一步失败=如实 reject=veto=pre-153 行为(不放大)。
50
+ * 调用方纪律与 {@link ensureSessionDurable} 同款:hostStore 必须是 release 非破坏性的真 durable 店
51
+ * (main.ts 的 `config.sessionBackend === "tidb"` 门不变)。
52
+ */
8
53
  export declare function ensureChildSessionDurableWithPromotion(hostStore: SessionStore, routing: {
9
54
  promoteToHost(sessionId: string): Promise<void>;
10
55
  }, sessionId: string): Promise<void>;
@@ -1,5 +1,14 @@
1
1
  import { TtlSessionStore } from "@sema-agent/core";
2
2
  import { CachingSessionStore } from "./caching-session-store.js";
3
+ /**
4
+ * Session-store plugin seam.
5
+ *
6
+ * - `memory` (default): core's in-memory `TtlSessionStore` (lost on restart, 7-day idle TTL).
7
+ * - `tidb`: the durable "Session event-log center" (L1) — append-only events + wake/resume +
8
+ * cross-instance CAS (F2), on whichever SQL engine `DB_BACKEND` selects (TiDB or PG, via the store backend).
9
+ * Optionally wrapped in a warm cache (reuse a hot session across turns; requires session affinity) —
10
+ * set `SESSION_CACHE_TTL_SEC=0` to disable.
11
+ */
3
12
  export function createSessionStore(config, backend, metrics) {
4
13
  if (config.sessionBackend === "tidb") {
5
14
  if (!backend)
@@ -11,9 +20,38 @@ export function createSessionStore(config, backend, metrics) {
11
20
  }
12
21
  return new TtlSessionStore({ defaultTtlDays: 7 });
13
22
  }
23
+ /**
24
+ * core 1.382(design/153,[1561] 提货单①②)`SubagentToolOptions.background.ensureChildSessionDurable`
25
+ * 的实现体——从 main.ts 装配点抽出为可独立测试的纯函数(main.ts 里的其余装配闭包历来无单测覆盖,
26
+ * 这条因为承载真实的 park/veto 裁决,拆出来直接钉)。
27
+ *
28
+ * 只在 `sessionId` 已经真正路由到 HOST(durable)会话店、且**真有内容**时 resolve;其余(未路由到 host/
29
+ * 两店皆无/host 店只有一行空壳)一律 reject——veto,core 侧子代按 pre-153 settle failed,零回归,不是
30
+ * 危险失败模式(见函数体内注释)。
31
+ *
32
+ * 传入必须是 HOST 店本身(main.ts 里 `createSessionStore(...)` 的返回值),不能是 subRunner 用的
33
+ * `ForkRoutingSessionStore` 包装店——那层包装对 `acquire(id,{requireExisting:true})` 是 transient-优先
34
+ * (F-7 案:让"真会话在 transient"赢过"host 店里的同 id 空壳"),会把"只在 transient 店有真内容"误判成
35
+ * "host 店已经有了"。这里要问的是纯粹"host 店本身是否已经有这份真内容",必须绕开那层路由。
36
+ *
37
+ * 🔴 [1575] F3(cli 复查抓获,真红确认)调用方前提——**本函数假设 `hostStore.release()` 是"归还租约"
38
+ * 语义(不销毁内容)**,这对 core `TiDBSessionStore` 成立(`release()` 逐字注释「Durable history is
39
+ * retained; just drop any in-flight cache entry」),但对无 repo 构造的 `TtlSessionStore`(`createSessionStore`
40
+ * 在 `SESSION_BACKEND !== "tidb"` 时返回的那种)**不成立**——那个店形的 `release()` 是整删会话
41
+ * (`evictPolicy="delete"`)。本函数自己**不做**、也无法通用地做"这个 store 实例的 release 是否安全"的
42
+ * 判别(`SessionStore` 接口没有暴露这个信息,类型层面区分不了两种店形)——上文"误判方向永远偏保守"
43
+ * 只覆盖 `getLeafId` 那道校验,不覆盖"传了一个 release=delete 的店进来"这个前提违反。**调用方必须
44
+ * 保证只在真正 durable(release 非破坏性)的店上调用本函数**——server 侧的强制点在 main.ts 装配区:
45
+ * `ensureChildSessionDurable` 闭包只在 `config.sessionBackend === "tidb"` 时构造。
46
+ */
14
47
  export async function ensureSessionDurable(hostStore, sessionId) {
15
48
  const acquired = await hostStore.acquire(sessionId, { requireExisting: true });
16
49
  try {
50
+ // security.ts 的 authorizer 对任意调用方自报的 body.sessionId(无主时)会经 `register()` 在 host 店
51
+ // 插一行 session_meta(leaf_id NULL,零内容)——那是 claim-create 的副作用,不是真正落过内容的会话。
52
+ // 裸 acquire 命中不足以证明"这份 durable 记录里有真内容可供跨进程恢复";真实会话到 park 这一步
53
+ // (需要模型已经产出过至少一次带工具调用的回复)恒已有 ≥1 条树内条目。校验失败的唯一后果是多一次
54
+ // reject=veto=pre-153 行为,不会放大权限或掩盖错误——误判方向永远偏保守。
17
55
  const leafId = await acquired.session.getLeafId();
18
56
  if (leafId === null)
19
57
  throw new Error(`session "${sessionId}" exists in the durable store but is empty (no leaf entry) — likely a claim-create shell row, not real durable content`);
@@ -22,12 +60,25 @@ export async function ensureSessionDurable(hostStore, sessionId) {
22
60
  await hostStore.release(acquired.sessionId);
23
61
  }
24
62
  }
63
+ /**
64
+ * [1593] 主动晋升版 ensureChildSessionDurable(cli TiDB 真机撞获:1.263 的只查不迁形在拆店生产形下
65
+ * 恒否决 park——bg 子会话住 subRunner 私有 TTL 店、从不在 host 店,core 收 veto 即 expire checkpoint
66
+ * (14ms~2s)+ settle failed,赎回窗口不存在):
67
+ * 1. 快路:已在 host 店且有真内容 → 直接通过(幂等/重入)。
68
+ * 2. 晋升:routing.promoteToHost 把子会话整树从私店拷进 host 店(拷贝→私店 forget→路由记 host,
69
+ * 三步见 ForkRoutingSessionStore.promoteToHost)。
70
+ * 3. 复验:同一把 `ensureSessionDurable` 严检(leaf 非空)——晋升产物必须达到与既有校验完全相同的
71
+ * durable 标准,任何一步失败=如实 reject=veto=pre-153 行为(不放大)。
72
+ * 调用方纪律与 {@link ensureSessionDurable} 同款:hostStore 必须是 release 非破坏性的真 durable 店
73
+ * (main.ts 的 `config.sessionBackend === "tidb"` 门不变)。
74
+ */
25
75
  export async function ensureChildSessionDurableWithPromotion(hostStore, routing, sessionId) {
26
76
  try {
27
77
  await ensureSessionDurable(hostStore, sessionId);
28
78
  return;
29
79
  }
30
80
  catch {
81
+ // 不在 host 店(或空壳行)→ 走晋升;晋升自身的失败在下方如实上浮
31
82
  }
32
83
  await routing.promoteToHost(sessionId);
33
84
  await ensureSessionDurable(hostStore, sessionId);