@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
@@ -12,8 +12,16 @@ const MCP_STATUS_TTL_MS = 5_000;
12
12
  const MCP_MATERIALIZE_TIMEOUT_MS = 10_000;
13
13
  export function createSessionsLocal() {
14
14
  return {
15
+ // [1196] session-events SSE 活跃连接计数(连接帽谓词;帽=deps.sessionEventsMaxConnections ?? 256)。
15
16
  sseConnections: 0,
17
+ // 六轮复审:初始 owner 查探的**存储侧**并发许可——连接坑在 race 超时即还,但底层查询可能仍悬着
18
+ // (驱动级取消不可得);重连风暴下未决查询必须独立有界(64),超限=零存储触碰直接 503。
16
19
  sseOwnerLookups: 0,
20
+ // E9 (shell-host contract) — short-TTL single-flight cache for GET /v1/sessions/:id/mcp. materializeMcpTools connects to (or
21
+ // spawns) MCP servers; without this a burst of /mcp polls would re-connect every time (adversarial-review MEDIUM:
22
+ // resource-exhaustion on an unbounded panel-poll path). Keyed by (scenario, principal) so per-user RBAC stays
23
+ // correct; stores the in-flight PROMISE so concurrent first-polls collapse to ONE materialize. Bounded by TTL +
24
+ // a hard size cap (cleared on overflow). The cached value is the PROJECTED panel (no live connections held).
17
25
  mcpStatusCache: new Map(),
18
26
  };
19
27
  }
@@ -22,15 +30,22 @@ export async function handleSessions(req, res, url, ctx) {
22
30
  await handleSessionsBody(req, res, url, ctx, miss);
23
31
  return !miss.fell;
24
32
  }
33
+ /** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(去缩进两格;sessions 另有一处见文件头说明)。
34
+ * 裸 `return;` 的语义原样 =「本域已应答」;走到函数尾才是「没匹配上」,置 `miss.fell` 交回装配器继续下一个域。 */
25
35
  async function handleSessionsBody(req, res, url, ctx, miss) {
26
36
  const { deps } = ctx;
27
37
  const { readJson, safeDecode, runOwnerOk, sessionOwnerScope, sessionOwnerScopeForWrite } = ctx.helpers;
28
38
  const sse = ctx.local.sessions;
29
39
  const { mcpStatusCache } = ctx.local.sessions;
40
+ // E11 (shell-host contract): GET /v1/sessions/:id/init — the one-call startup bundle, so the shell renders its
41
+ // banner from ONE request instead of fanning out to /v1/models + /v1/policy + /v1/capabilities. Worker-global
42
+ // config surface (same posture as /v1/policy): principal OPTIONAL, enforced only under REQUIRE_PRINCIPAL.
43
+ // ADDITIVE — `model` + `mode` are the load-bearing banner fields; `tools`/`agents`/`commands`/`cwd` are omitted
44
+ // today (tolerate-absent — the shell falls back to its mock-fill). `cwd` is live-only (E13) and never durable here.
30
45
  {
31
46
  const im = /^\/v1\/sessions\/([^/]+)\/init$/.exec(url);
32
47
  if (im && req.method === "GET") {
33
- const principal = gatedPrincipal(req, deps.config);
48
+ const principal = gatedPrincipal(req, deps.config); // F-fix: VERIFIED presence gate (direct-door JWT, not the spoofable header)
34
49
  if (deps.config.requirePrincipal && !principal) {
35
50
  sendError(res, 401, "auth.principal_required", `missing principal header '${deps.config.principalHeader}'`);
36
51
  return;
@@ -43,10 +58,15 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
43
58
  return;
44
59
  }
45
60
  }
61
+ // E24 (shell-host contract): GET /v1/sessions/:id/settings — the effective merged settings view (CC /config read
62
+ // side). Worker-global config surface (principal optional unless REQUIRE_PRINCIPAL), same posture as /v1/policy +
63
+ // the E11 init bundle. `effective` = the resolved worker config the run obeys; `applied` echoes the resolved
64
+ // model. The PATCH half (runtime settings write) + per-source layering need a core seam (SERVICE-GAP-DOSSIER E24)
65
+ // — omitted today, so no `settings` write capability is advertised (honest: read route only).
46
66
  {
47
67
  const stm = /^\/v1\/sessions\/([^/]+)\/settings$/.exec(url);
48
68
  if (stm && req.method === "GET") {
49
- const principal = gatedPrincipal(req, deps.config);
69
+ const principal = gatedPrincipal(req, deps.config); // F-fix: VERIFIED presence gate (direct-door JWT, not the spoofable header)
50
70
  if (deps.config.requirePrincipal && !principal) {
51
71
  sendError(res, 401, "auth.principal_required", `missing principal header '${deps.config.principalHeader}'`);
52
72
  return;
@@ -64,25 +84,36 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
64
84
  return;
65
85
  }
66
86
  }
87
+ // E9 (shell-host contract, core 1.124.0): GET /v1/sessions/:id/mcp — per-server MCP status for the `/mcp` panel.
88
+ // 🔴 core does NOT persist MCP connections (task-scoped), so there is no stored session MCP health. This
89
+ // MATERIALIZES the worker's configured MCP servers ON-DEMAND (a fresh connect → list → dispose, fail-open) and
90
+ // returns the projected statuses with `asOf` = THIS materialization moment. It is materialization-time status,
91
+ // NOT live session health (core §E9) — the shell renders "as of <asOf>". Worker-global config surface
92
+ // (principal-optional like /policy); the caller's gatedPrincipal is threaded for per-user MCP RBAC
93
+ // (principalHeader injection). Uses the DEFAULT scenario's MCP set (per-session scenario tracking is a follow-up).
67
94
  {
68
95
  const mm = /^\/v1\/sessions\/([^/]+)\/mcp$/.exec(url);
69
96
  if (mm && req.method === "GET") {
97
+ // F-fix: VERIFIED presence gate (direct-door JWT, not the spoofable header); per-user MCP RBAC already uses gatedPrincipal.
70
98
  if (deps.config.requirePrincipal && !gatedPrincipal(req, deps.config)) {
71
99
  sendError(res, 401, "auth.principal_required", `missing principal header '${deps.config.principalHeader}'`);
72
100
  return;
73
101
  }
74
102
  const specs = mcpForScenario(deps.config.mcpServers, deps.config.defaultScenario);
75
103
  if (!specs || specs.length === 0) {
76
- sendJson(res, 200, { servers: [], asOf: new Date().toISOString() });
104
+ sendJson(res, 200, { servers: [], asOf: new Date().toISOString() }); // no MCP configured → honest empty panel
77
105
  return;
78
106
  }
79
- const principal = gatedPrincipal(req, deps.config);
107
+ const principal = gatedPrincipal(req, deps.config); // threaded for per-user MCP RBAC (principalHeader injection)
80
108
  const cacheKey = `${deps.config.defaultScenario}::${principal ?? ""}`;
81
109
  const cached = mcpStatusCache.get(cacheKey);
82
110
  if (!cached || Date.now() - cached.at > MCP_STATUS_TTL_MS) {
83
111
  if (mcpStatusCache.size > 1024)
84
- mcpStatusCache.clear();
112
+ mcpStatusCache.clear(); // hard size cap (per-principal keys) — crude but bounded
113
+ // Single-flight: store the in-flight promise so a burst of polls collapses to ONE materialize.
85
114
  const panel = (async () => {
115
+ // Bound the connect/list so a hanging MCP server can't pin the request; dispose the late resolver if it
116
+ // eventually completes after the timeout (no connection leak). materializeMcpTools is fail-open per server.
86
117
  const matP = materializeMcpTools(specs, principal);
87
118
  let timer;
88
119
  const mat = await Promise.race([matP.catch(() => null), new Promise((r) => { timer = setTimeout(() => r(null), MCP_MATERIALIZE_TIMEOUT_MS); })]);
@@ -90,7 +121,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
90
121
  clearTimeout(timer);
91
122
  if (!mat) {
92
123
  void matP.then((m) => m?.dispose().catch(() => undefined)).catch(() => undefined);
93
- return { servers: [], asOf: new Date().toISOString(), degraded: true };
124
+ return { servers: [], asOf: new Date().toISOString(), degraded: true }; // timed out / failed to materialize
94
125
  }
95
126
  try {
96
127
  return {
@@ -100,6 +131,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
100
131
  status: s.status,
101
132
  ...(s.serverInfo ? { serverInfo: s.serverInfo } : {}),
102
133
  ...(s.toolNames ? { toolNames: s.toolNames } : {}),
134
+ // E9 (review LOW): redact secret-shaped strings + bound length — raw MCP connect errors may carry
135
+ // internal paths/config the shell needn't see (same redaction posture as tool output).
103
136
  ...(s.error ? { error: redactSecrets(String(s.error)).slice(0, 200) } : {}),
104
137
  })),
105
138
  };
@@ -114,9 +147,16 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
114
147
  return;
115
148
  }
116
149
  }
150
+ // [1075]② 会话追随轻探针:GET /v1/sessions/:id/head → { leafId }(+ ETag/304)。leafId=会话树叶
151
+ // 指针,任何 append 都变=天然版本号——web 空闲期低成本轮询(If-None-Match 命中=304 零 body),
152
+ // 变了才拉 tail 增量。刻意不做 session 级 SSE(真订阅需跨副本通知面,写可发生在任意副本而流挂在
153
+ // 某一副本——设计件另立;探针轮询 1-2s 一次的 304 成本≈零,先解「必须刷新」的主诉)。
154
+ // 顶层路由(只依赖 sessionStorage,不依赖 sessionAudit——audit 缺席的部署探针照常)。
117
155
  {
118
156
  const hm = req.method === "GET" ? /^\/v1\/sessions\/([^/]+)\/head$/.exec(url) : null;
119
157
  if (hm) {
158
+ // 二轮复审([1196] S1 邻案连修):方法必须 bind 宿主——SESSION_CACHE_TTL_SEC=0 时 sessionStorage
159
+ // 是裸 PG/TiDB 类实例,getLeafId 内用 this.pool,裸引用调用=TypeError 500。
120
160
  const getLeafId = deps.sessionStorage?.getLeafId?.bind(deps.sessionStorage);
121
161
  const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
122
162
  if (!getLeafId || !ownerOf) {
@@ -124,6 +164,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
124
164
  return;
125
165
  }
126
166
  const headSession = decodeURIComponent(hm[1]);
167
+ // 七轮复审:store 有 getHead 时用单行原子快照(owner 鉴权与 leafId 同拍——两独立读在
168
+ // delete/reclaim 缝里可鉴旧主发新叶);无 getHead 的旧 store 保持两读旧形(退化面照旧)。
127
169
  const getHead = deps.sessionStorage?.getHead?.bind(deps.sessionStorage);
128
170
  let owner;
129
171
  let leafId;
@@ -158,9 +200,16 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
158
200
  return;
159
201
  }
160
202
  }
203
+ // [1196] session 级 SSE 真订阅:GET /v1/sessions/:id/events —— `event: head` 推送(latest-wins),
204
+ // `?lastLeafId=` 为游标(等价 If-None-Match:相同则无初始帧),25s 心跳注释行。鉴权/存储门与 /head
205
+ // 完全同门;连接帽超限=503+Retry-After(消费方回落 /head 轮询,capabilities.sessionEvents 广告)。
161
206
  {
162
207
  const em = req.method === "GET" ? /^\/v1\/sessions\/([^/]+)\/events$/.exec(url) : null;
163
208
  if (em) {
209
+ // 二轮复审单源化:本路由**不再自读基线**——所有帧(含首帧)由 registry 按 rev 单调序 fan-out
210
+ // (replay-on-subscribe 给已知条目即时首帧;新条目首帧=首拍探针 ≤hotMs)。双源(自读基线 vs
211
+ // registry 缓冲)必然产生不可判序的回退帧,单源后该类竞序结构性不存在。getLeafId 由 registry
212
+ // 内部使用(main.ts 注入时已绑定宿主);路由只需 ownerOf 门。
164
213
  const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
165
214
  const watch = deps.sessionWatch;
166
215
  if (!deps.sessionStorage?.getLeafId || !ownerOf || !watch) {
@@ -168,12 +217,14 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
168
217
  return;
169
218
  }
170
219
  const evSession = decodeURIComponent(em[1]);
220
+ // codex 一轮生命周期状态机:①连接坑与 close 守卫在任何 await 之前同步建立(读存储期间断连不再
221
+ // 漏清/漏还坑);②每个 await 之后查 closed;③所有拒绝/异常路径经同一 release。
171
222
  if (sse.sseConnections >= (deps.sessionEventsMaxConnections ?? 256)) {
172
223
  res.writeHead(503, { "retry-after": "5", "content-type": "application/json" });
173
224
  res.end(JSON.stringify({ error: "session-events connection cap reached — fall back to /head polling" }));
174
225
  return;
175
226
  }
176
- sse.sseConnections++;
227
+ sse.sseConnections++; // 同步占坑(查+占同拍,慢读并发不再超卖);此后一切路径必经 cleanup 还坑
177
228
  let hb;
178
229
  let unsubscribe;
179
230
  let closed = false;
@@ -189,6 +240,10 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
189
240
  res.on("close", cleanup);
190
241
  req.on("close", cleanup);
191
242
  try {
243
+ // 五轮复审:初始 ownerOf 也受 stall 界(存储悬死时不许无限占坑——256 坑被悬死请求耗尽=合法
244
+ // 客户端全 503);超时=可重试失败(503+Retry-After),坑经 cleanup 归还。六轮复审:查询本体的
245
+ // 并发另行有界(sseOwnerLookups,许可在底层 settle 时才还)——race 只还连接坑不还查询许可,
246
+ // 重连风暴不得无界堆积未决存储查询。
192
247
  const stallBound = deps.sessionEventsStallMs ?? 60_000;
193
248
  if (sse.sseOwnerLookups >= 64) {
194
249
  cleanup();
@@ -198,7 +253,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
198
253
  }
199
254
  sse.sseOwnerLookups++;
200
255
  const ownerP = ownerOf(evSession);
201
- void ownerP.catch(() => undefined).then(() => { sse.sseOwnerLookups--; });
256
+ void ownerP.catch(() => undefined).then(() => { sse.sseOwnerLookups--; }); // 许可随底层 settle 归还
257
+ // 七轮复审:race 计时器 settle 即清——高频开合不得每次拖 60s 计时器+闭包(unref 只保进程退出面)。
202
258
  let stallTimer;
203
259
  let owner;
204
260
  try {
@@ -212,7 +268,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
212
268
  clearTimeout(stallTimer);
213
269
  }
214
270
  if (closed)
215
- return;
271
+ return; // 读期断连:close 守卫已还坑,别再写已死响应
216
272
  if (owner === "__stall__") {
217
273
  cleanup();
218
274
  res.writeHead(503, { "retry-after": "5", "content-type": "application/json" });
@@ -229,14 +285,20 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
229
285
  return;
230
286
  }
231
287
  const q = new URL(req.url ?? url, "http://x").searchParams;
232
- const lastLeafId = q.get("lastLeafId");
288
+ const lastLeafId = q.get("lastLeafId"); // null=未带 ⇒ 恒发首帧;带了且首帧值相等 ⇒ 抑制(304 同义)
289
+ // 单源发射:全部帧来自 registry(rev 单调序),连接级 lastSent 去重;首帧抑制=首值命中
290
+ // lastLeafId 时只记不发(后续同值仍去重,更新值照发)。
233
291
  let first = true;
234
- let lastAccepted;
292
+ let lastAccepted; // 去重基准(单源 rev 序,最新接受值)
235
293
  let lastWritten;
294
+ // 三轮复审背压:res.write 返回 false 即停写,只保「最新一格」待冲(latest-wins 恰是本契约),
295
+ // drain 冲洗;阻塞超时(缺省 60s)=消费者死/僵——断流(client 重连自愈),绝不无界积压。
236
296
  let writeBlocked = false;
237
297
  let blockedAt = 0;
238
298
  let pendingHead;
239
299
  const stallMs = deps.sessionEventsStallMs ?? 60_000;
300
+ // 四轮复审:**全部**写(connected/心跳注释/head 帧)走同一背压感知写手——任何一次 false 都
301
+ // 进 blocked 态(心跳先塞住的流同样受 stall 断流约束,不再有旁路缓冲增长)。
240
302
  const writeRaw = (chunk) => {
241
303
  if (closed)
242
304
  return;
@@ -254,7 +316,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
254
316
  });
255
317
  }
256
318
  }
257
- catch { }
319
+ catch { /* torn socket: close 守卫清理 */ }
258
320
  };
259
321
  const doWrite = (leafId) => {
260
322
  lastWritten = leafId;
@@ -269,20 +331,22 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
269
331
  if (suppress) {
270
332
  lastWritten = leafId;
271
333
  return;
272
- }
334
+ } // 视作已发:重复抑制,真变更仍会推
273
335
  if (writeBlocked) {
274
- pendingHead = { v: leafId };
336
+ pendingHead = { v: leafId }; // latest-wins 单格
275
337
  if (Date.now() - blockedAt > stallMs) {
276
338
  cleanup();
277
339
  try {
278
340
  res.end();
279
341
  }
280
- catch { }
342
+ catch { /* noop */ }
281
343
  }
282
344
  return;
283
345
  }
284
346
  doWrite(leafId);
285
347
  };
348
+ // 订阅先于 SSE headers(帽拒绝要保住干净 503)——headers 前到达的 replay/live 帧存一格
349
+ // pre 缓冲(latest-wins 覆写,单源序不破),headers 后冲洗。
286
350
  let started = false;
287
351
  let preVal;
288
352
  const onHead = (leafId) => {
@@ -291,11 +355,13 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
291
355
  else
292
356
  emit(leafId);
293
357
  };
358
+ // dropSession(session 删除,三轮复审租户轮转围栏)→ 即刻断流:旧 owner 的订阅绝不跨越
359
+ // delete/reclaim 边界;跨副本删除由下方心跳期 owner 复核兜底(≤心跳一拍)。
294
360
  const onEnd = () => { cleanup(); try {
295
361
  res.end();
296
362
  }
297
- catch { } };
298
- unsubscribe = watch.subscribe(evSession, owner ?? null, onHead, onEnd);
363
+ catch { /* noop */ } };
364
+ unsubscribe = watch.subscribe(evSession, owner ?? null, onHead, onEnd); // owner 铸入条目(四轮跨副本围栏);已知条目=同步 replay 首帧;新条目=首拍探针 ≤hotMs
299
365
  if (!unsubscribe) {
300
366
  cleanup();
301
367
  res.writeHead(503, { "retry-after": "5", "content-type": "application/json" });
@@ -308,6 +374,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
308
374
  if (preVal !== undefined)
309
375
  emit(preVal.v);
310
376
  const hbMs = deps.sessionEventsHeartbeatMs ?? 25_000;
377
+ // 四轮复审:owner 复核 single-flight——上拍未归不再起新查(存储卡顿时 256 连接不得堆积
378
+ // 未决查询);未决超过 stallMs=复核面失效,fail-closed 断流(长活流不许在盲区里续命)。
311
379
  let ownerCheckPending = false;
312
380
  let ownerCheckStartedAt = 0;
313
381
  hb = setInterval(() => {
@@ -316,21 +384,22 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
316
384
  if (writeBlocked && Date.now() - blockedAt > stallMs) {
317
385
  onEnd();
318
386
  return;
319
- }
387
+ } // 僵消费者:无新帧也到点断
320
388
  if (!writeBlocked)
321
389
  writeRaw(": hb\n\n");
322
390
  if (ownerCheckPending) {
323
391
  if (Date.now() - ownerCheckStartedAt > stallMs)
324
- onEnd();
392
+ onEnd(); // 复核悬死=盲区,fail-closed
325
393
  return;
326
394
  }
395
+ // 三轮复审跨副本围栏:心跳拍复核 owner——session 被删(undefined)或换主(≠连接时 owner)即断流。
327
396
  ownerCheckPending = true;
328
397
  ownerCheckStartedAt = Date.now();
329
398
  void ownerOf(evSession).then((o) => {
330
399
  ownerCheckPending = false;
331
400
  if (!closed && o !== owner)
332
401
  onEnd();
333
- }).catch(() => { ownerCheckPending = false; });
402
+ }).catch(() => { ownerCheckPending = false; /* 瞬时读障不断流,下一拍再核 */ });
334
403
  }, hbMs);
335
404
  hb.unref?.();
336
405
  }
@@ -342,7 +411,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
342
411
  try {
343
412
  res.end();
344
413
  }
345
- catch { }
414
+ catch { /* noop */ }
346
415
  }
347
416
  return;
348
417
  }
@@ -357,6 +426,13 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
357
426
  }
358
427
  if (!runOwnerOk(req, res, audit.owner))
359
428
  return;
429
+ // web 会话区性能(additive — parameterless requests stay BYTE-IDENTICAL):
430
+ // ?tail=N → messages = last N (+ window {offset,total})
431
+ // ?before=<off>&limit=N → the page above a prior window (offsets stable within one floorEntryId
432
+ // generation — a compaction changes floorEntryId, the web refetches)
433
+ // ?blobCap=<bytes> → oversized string bodies truncated to a preview + message-level truncated:true
434
+ // (expand = refetch that ONE message via ?message=<index>, full size)
435
+ // ?message=<index> → SINGLE full message {message, index, floorEntryId} (the expand face)
360
436
  const q = new URL(req.url ?? url, "http://x").searchParams;
361
437
  const qi = (k) => {
362
438
  const v = q.get(k);
@@ -379,7 +455,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
379
455
  const before = qi("before");
380
456
  const blobCap = qi("blobCap");
381
457
  if (tail === undefined && before === undefined && blobCap === undefined) {
382
- sendJson(res, 200, audit);
458
+ sendJson(res, 200, audit); // additive contract: no params = the exact legacy payload
383
459
  return;
384
460
  }
385
461
  let messages = audit.messages;
@@ -395,6 +471,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
395
471
  return;
396
472
  }
397
473
  }
474
+ // E17 (§0.5) — POST /v1/sessions/:id/fork → {sessionId}. Branch a NEW session off the source's WHOLE history
475
+ // via the SessionRepo seam the service owns (sessionStorage.fork), owner-scoped exactly like GET /v1/sessions.
398
476
  {
399
477
  const fm = req.method === "POST" ? /^\/v1\/sessions\/([^/]+)\/fork$/.exec(url) : null;
400
478
  if (fm) {
@@ -410,6 +488,9 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
410
488
  return;
411
489
  }
412
490
  const sourceId = safeDecode(fm[1]);
491
+ // §0.5 — shape-validate the session id (uuidv7) so a crafted id can't reach the store ops as anything but
492
+ // a well-formed key (a second layer behind the tool-result LIKE-escape). Malformed (bad %-seq or non-uuidv7)
493
+ // ⇒ 400, never a store hit.
413
494
  if (sourceId === null || !isUuidV7(sourceId)) {
414
495
  sendError(res, 400, "request.id_invalid", "invalid session id (must be a uuidv7)");
415
496
  return;
@@ -419,20 +500,35 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
419
500
  sendError(res, 404, "not_found.session", "session not found");
420
501
  return;
421
502
  }
503
+ // Owner gate (no existence oracle for non-owners — 404, parity with runOwnerOk): a principal caller may
504
+ // only fork its OWN session; a fleet-wide (system/trace) caller may fork any (ops).
422
505
  if (!scope.fleetWide && owner !== scope.gateOwner) {
423
506
  sendError(res, 404, "not_found.session", "session not found");
424
507
  return;
425
508
  }
509
+ // New-session ownership — DELIBERATE (no security issue either way; documented per adversarial-review):
510
+ // • principal caller → the NEW session is owned by the CALLER (scope.gateOwner): you fork your own session,
511
+ // you own the copy.
512
+ // • fleet-wide (ops, fork-on-behalf) → the NEW session INHERITS the SOURCE owner (`owner ?? null`) so the
513
+ // fork is TENANT-owned (an ops/system fork-on-behalf hands the copy to the same tenant as the source,
514
+ // NOT to the ownerless/system pool) — a later attach is then governed by the normal authorizer for that
515
+ // tenant. (Chosen over null/system-owned: an ops fork is "give this tenant a branched copy", so tenant
516
+ // attribution is the intent; a system-submitted *run* forks ownerless, but a *session* fork-on-behalf
517
+ // is explicitly on the source tenant's behalf.)
426
518
  const newOwner = scope.fleetWide ? (owner ?? null) : scope.gateOwner;
427
519
  const newId = await fork(sourceId, newOwner);
428
520
  if (newId === null) {
429
521
  sendError(res, 404, "not_found.session", "session not found");
430
522
  return;
431
- }
523
+ } // raced delete between ownerOf and fork
432
524
  sendJson(res, 200, { sessionId: newId });
433
525
  return;
434
526
  }
435
527
  }
528
+ // E21 (§0.5) — DELETE /v1/sessions/:id → {deleted}. Purge the session's conversation history (session_meta/
529
+ // session_event) AND the service-owned run-ledger/checkpoint/tool-result rows for it. Auth-FIRST (the scope is
530
+ // computed before any store touch, mirroring GET to avoid a 501-before-auth existence oracle). Owner-scoped,
531
+ // idempotent ({deleted:false} when nothing existed). active-run guard → 409 (don't orphan the session lock).
436
532
  {
437
533
  const dm = req.method === "DELETE" ? /^\/v1\/sessions\/([^/]+)$/.exec(url) : null;
438
534
  if (dm) {
@@ -448,6 +544,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
448
544
  return;
449
545
  }
450
546
  const sessionId = safeDecode(dm[1]);
547
+ // §0.5 — shape-validate the session id (uuidv7) so a crafted id can't reach the purge ops (a second layer
548
+ // behind the tool-result LIKE-escape). Malformed (bad %-seq or non-uuidv7) ⇒ 400, never a store hit.
451
549
  if (sessionId === null || !isUuidV7(sessionId)) {
452
550
  sendError(res, 400, "request.id_invalid", "invalid session id (must be a uuidv7)");
453
551
  return;
@@ -456,11 +554,19 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
456
554
  if (owner === undefined) {
457
555
  sendJson(res, 200, { deleted: false });
458
556
  return;
459
- }
557
+ } // idempotent: already gone
558
+ // Foreign-owner ⇒ the SAME response as missing (a 404 here vs 200 above was an existence
559
+ // oracle — a prober could distinguish "absent" from "another tenant's"). deleted:false is also honest
560
+ // (nothing was deleted) and keeps the route idempotent-shaped for both cases.
460
561
  if (!scope.fleetWide && owner !== scope.gateOwner) {
461
562
  sendJson(res, 200, { deleted: false });
462
563
  return;
463
564
  }
565
+ // Don't orphan the single-active-run lock: refuse while a run is live on this session (409 over a
566
+ // half-purge that strands a task_active claim). A terminal/suspended/parked session deletes cleanly. This
567
+ // is a cheap PRE-check; the AUTHORITATIVE no-active-run gate is re-asserted transactionally inside the
568
+ // runs-ledger purge (FOR UPDATE on task_active), which closes the check→purge TOCTOU and also returns
569
+ // `{ active }` to 409 if a createRun raced in between.
464
570
  const activeTaskId = await deps.runStore?.getActiveTaskId(sessionId);
465
571
  if (activeTaskId) {
466
572
  sendError(res, 409, "conflict.session_active_run", "session has an active run; cancel it before deleting", { activeTaskId });
@@ -475,6 +581,12 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
475
581
  return;
476
582
  }
477
583
  }
584
+ // E6 — GET/PUT /v1/sessions/:id/policy: read / write operator-tightened per-session tool-permission rules (core
585
+ // reads them at prepare-time, SUBTRACT-only). Owner-gated + the operator flag FROZEN from the verified principal
586
+ // (explicitOperatorOk(gatedPrincipal), NEVER the spoofable header — the steer.trusted privilege-escalation rule):
587
+ // a non-operator may only TIGHTEN its OWN session (a loosen → loosen_forbidden); an operator may loosen + act
588
+ // cross-tenant. The target key = the session OWNER (the principal core reads rules under). SessionPolicyError →
589
+ // conflict:409 (stale expectedRev) / loosen_forbidden:403. 404 (not 403) on a non-owner — no existence oracle.
478
590
  {
479
591
  const pm = req.method === "GET" || req.method === "PUT" ? /^\/v1\/sessions\/([^/]+)\/policy$/.exec(url) : null;
480
592
  if (pm) {
@@ -498,7 +610,11 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
498
610
  if (owner === undefined) {
499
611
  sendError(res, 404, "not_found.session", "session not found");
500
612
  return;
501
- }
613
+ } // idempotent / no oracle
614
+ // E6 (review): rules MUST be keyed by the principal core reads at run time (getRules(sessionId, spec.principal)).
615
+ // For an OWNED session that is the owner (a task on it runs as the owner). An OWNERLESS session (owner=null,
616
+ // REQUIRE_PRINCIPAL=false) has no stable run-principal — a task attaching any principal would read a DIFFERENT
617
+ // key, silently bypassing the rule (fail-OPEN). Refuse rather than store a rule that won't apply.
502
618
  if (owner === null) {
503
619
  sendError(res, 409, "conflict.session_ownerless", "session policy requires an owned session (set REQUIRE_PRINCIPAL / attach a principal); this session is ownerless");
504
620
  return;
@@ -507,12 +623,16 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
507
623
  if (!operator && owner !== (principal ?? null)) {
508
624
  sendError(res, 404, "not_found.session", "session not found");
509
625
  return;
510
- }
511
- const target = owner;
626
+ } // non-owner → 404, no existence oracle
627
+ const target = owner; // the key core reads rules under = the session's owner (= spec.principal for its tasks)
512
628
  if (req.method === "GET") {
629
+ // BC-3 (shell §I): the 200 is ALWAYS a non-null StoredSessionRules with a load-bearing `rev` (the PUT CAS
630
+ // baseline). An owned session with NO policy yet → getRules null → return the empty-at-rev-0 record (the
631
+ // store reads a missing row as rev 0 = expectedRev:0 create-once), never `{rules:null}`.
513
632
  sendJson(res, 200, { rules: (await store.getRules(sessionId, target)) ?? { rev: 0 } });
514
633
  return;
515
634
  }
635
+ // PUT — validate + pick the 5 known string[] rule fields (reject malformed fail-loud); CAS + tighten gate in the store.
516
636
  let body;
517
637
  try {
518
638
  body = (await readJson(req));
@@ -554,6 +674,11 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
554
674
  return;
555
675
  }
556
676
  }
677
+ // ── workspace 浏览面(#3,docs/DESIGN-workspace-browse.md;[1882] 征询 → [1894] cli 表态四点细化)────────
678
+ // E19 快照店的只读投影:list/tree/file/archive 四端点,零新 store。owner 门=sync 面 PULL 同款
679
+ // (session 必须存在且属调用方,404 无 oracle);501 谓词=快照店三读面(listKeys/exportManifest/
680
+ // getBlob)+ ownerOf 齐(capabilities.workspace 同源)。`:key=latest` 别名=最新快照(键=entryId,
681
+ // uuidv7 时间序 ⇒ 字典序最大即最新)。
557
682
  {
558
683
  const wm = req.method === "GET" ? /^\/v1\/sessions\/([^/]+)\/workspace(?:\/([^/]+)(?:\/(tree|file|archive))?)?$/.exec(url) : null;
559
684
  if (wm) {
@@ -577,10 +702,12 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
577
702
  if (wsOwner === undefined || (!scope.fleetWide && wsOwner !== scope.gateOwner)) {
578
703
  sendError(res, 404, "not_found.session", "session not found");
579
704
  return;
580
- }
705
+ } // PULL 门:必须存在且属己,无 oracle
581
706
  const q = new URL(req.url ?? "", "http://x").searchParams;
582
- const keys = (await fs.listKeys(wsSession)).sort().reverse();
707
+ const keys = (await fs.listKeys(wsSession)).sort().reverse(); // uuidv7 entryId:字典序 desc = 时间序 新→旧
583
708
  const sub = wm[3];
709
+ // GET …/workspace — 列快照(新→旧;?limit 1..100 默认 20)。files=manifest 行数;bytes 仅在
710
+ // blobSizes 面在场时给(local 店缺席=诚实省略,[1894]② additive 口径)。
584
711
  if (wm[2] === undefined) {
585
712
  const limRaw = q.get("limit");
586
713
  if (limRaw !== null && !(/^[0-9]+$/.test(limRaw) && Number(limRaw) >= 1 && Number(limRaw) <= 100)) {
@@ -593,7 +720,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
593
720
  for (const k of page) {
594
721
  const m = await fs.exportManifest(wsSession, k);
595
722
  if (m === null)
596
- continue;
723
+ continue; // listKeys 与 manifest 读之间被 reap 的键:掉行不空报
597
724
  let bytes;
598
725
  if (typeof fs.blobSizes === "function") {
599
726
  const sizes = await fs.blobSizes([...new Set(m.values())]);
@@ -603,6 +730,9 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
603
730
  }
604
731
  snapshots.push({ key: k, files: m.size, ...(bytes !== undefined ? { bytes } : {}) });
605
732
  }
733
+ // 🔴 复审 A-MED:`latest` 与 `total` 必须与**展示行**同源——listKeys 与 exportManifest 之间
734
+ // 被 reap 的键(ghost)若恰是字典序最大者,原写法会让 latest 指向已消失的快照(latest 别名的
735
+ // tree/file/archive 全 404,尽管有有效的次新快照),且 total 计入 ghost 与 snapshots[] 不一致。
606
736
  sendJson(res, 200, {
607
737
  sessionId: wsSession,
608
738
  snapshots,
@@ -611,6 +741,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
611
741
  });
612
742
  return;
613
743
  }
744
+ // :key 解析(latest 别名);快照必须真存在(manifest 非 null)。
614
745
  const keyRaw = safeDecode(wm[2]);
615
746
  if (keyRaw === null) {
616
747
  sendError(res, 400, "request.path_malformed", "malformed snapshot key");
@@ -623,6 +754,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
623
754
  return;
624
755
  }
625
756
  if (sub === "tree") {
757
+ // [1894]②:entries 带 {path, size?, hash};?limit 默认 500 cap 2000、?offset 分页;path 序稳定。
626
758
  const limRaw = q.get("limit");
627
759
  if (limRaw !== null && !(/^[0-9]+$/.test(limRaw) && Number(limRaw) >= 1 && Number(limRaw) <= 2000)) {
628
760
  sendError(res, 400, "request.query_invalid", "limit must be a decimal integer in 1..2000");
@@ -659,6 +791,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
659
791
  return;
660
792
  }
661
793
  const cap = deps.config.workspaceFileMaxBytes ?? 8 * 1024 * 1024;
794
+ // [1894]①:超限=413 结构化信封(web 渲「文件过大,下载查看」卡)。size 可先知(SQL 索引)时
795
+ // 先拒后拉——绝不为判超限吸整块字节。
662
796
  if (typeof fs.blobSizes === "function") {
663
797
  const known = (await fs.blobSizes([hash])).get(hash);
664
798
  if (known !== undefined && known > cap) {
@@ -675,6 +809,8 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
675
809
  sendError(res, 413, "workspace_file_too_large", "file exceeds the single-file read limit — download it via the archive endpoint", { code: "workspace_file_too_large", sizeBytes: bytes.byteLength, limit: cap });
676
810
  return;
677
811
  }
812
+ // [1894]③:content-type 按扩展名(保守表,未知=octet-stream)+ 二进制判别头(首 8KiB NUL/控制
813
+ // 字节嗅探)。nosniff 恒带(内容是 agent 产物=不可信,绝不让浏览器自嗅成可执行类型)。
678
814
  const ct = workspaceContentType(pathParam);
679
815
  const bin = looksBinary(bytes);
680
816
  res.writeHead(200, {
@@ -687,6 +823,10 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
687
823
  return;
688
824
  }
689
825
  if (sub === "archive") {
826
+ // 流式 tar(ustar,src/http/tar.ts 零依赖)。开流前三道预检(头没发,还能诚实报错):
827
+ // ① 路径全部可表示(>255B/不可拆 ⇒ 422 点名);② blob 全在(hasBlobs 单查;缺 ⇒ 502 可重试);
828
+ // ③ 之后逐文件 getBlob 流式写+背压。中途 blob 消失(reap 竞态)⇒ 已在流中,destroy 连接
829
+ // (诚实截断——tar 尾块缺失,消费端可判损)。
690
830
  const entries = [...manifest.entries()].sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
691
831
  const bad = entries.filter(([p]) => splitTarPath(p) === null).map(([p]) => p);
692
832
  if (bad.length > 0) {
@@ -709,6 +849,10 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
709
849
  const write = async (buf) => {
710
850
  if (res.writableEnded || res.destroyed)
711
851
  throw new Error("client gone");
852
+ // 🔴 复审 F5:drain 必须与 close/error **竞速**——socket 被销毁后 `drain` 永不再来,只等
853
+ // 它会让本协程永久 pending(入口的 destroyed 守卫挡不住「已经卡在 await 里」这一态)。
854
+ // 今天的缓和事实是引用图随连接一起变垃圾、admittedInflight 由 res.on("close") 独立释放;
855
+ // 但任何将来加在这条腿上的 finally 清理都会静默不执行 —— 一行竞速根除这个类别。
712
856
  if (!res.write(buf)) {
713
857
  await new Promise((resolve, reject) => {
714
858
  const onDrain = () => { cleanup(); resolve(); };
@@ -733,10 +877,11 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
733
877
  res.end();
734
878
  }
735
879
  catch {
736
- res.destroy();
880
+ res.destroy(); // 头已发:诚实截断(缺 tar 尾块,消费端可判损),绝不补一个假完整尾
737
881
  }
738
882
  return;
739
883
  }
884
+ // 裸 :key 无动词 ⇒ 404 指路(不猜语义)。
740
885
  sendError(res, 404, "request.route_unsupported", "unknown workspace verb — use /tree, /file?path=, or /archive");
741
886
  return;
742
887
  }
@@ -1,3 +1,11 @@
1
+ /**
2
+ * design/158 A9 域模块 —— sideQuery 一次性问答面([1469],core 1.361)。
3
+ *
4
+ * 路由:`POST /v1/side-query` —— 无 session / 无工具执行 / 无 policy gate / 无引擎侧记账 / 无流式的
5
+ * 一次性 brain 路由问答(v1 刻意面 = core 规格 side-query.d.ts)。cli 壳侧十几个 utility(标题/摘要/
6
+ * 权限解释…)的传输层正解。位置在全局 service-credential 门**之后**、任务提交面之前;`isBillableSubmitPath`
7
+ * 已含本路径,故 no-token 503 / draining / model-ready 三闸在进本域前就已生效。本域零可变状态。
8
+ */
1
9
  import type { IncomingMessage, ServerResponse } from "node:http";
2
10
  import type { RouteCtx } from "../route-ctx.js";
3
11
  export declare function handleSideQuery(req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx): Promise<boolean>;