@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
@@ -4,21 +4,35 @@ import { redactSecrets } from "../../trace/redact.js";
4
4
  import { HEARTBEAT_MS } from "../../runs.js";
5
5
  import { sendJson, sendError, sseHeaders } from "../send.js";
6
6
  import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
7
- const WORKFLOWS_RE = /^\/v1\/workflows(?:\/([^/]+)(\/stream|\/journal)?)?$/;
7
+ const WORKFLOWS_RE = /^\/v1\/workflows(?:\/([^/]+)(\/stream|\/journal)?)?$/; // S8 self-orchestration runs: list / detail / live SSE
8
+ // SVC-5 (design/97 CORE-5 #6): steer a STILL-RUNNING workflow agent (ctx.agentStream) by runId + label. POST-only,
9
+ // so it never collides with the GET WORKFLOWS_RE above. The label is a non-slash segment (core mints `agent-N`
10
+ // for an unlabelled agent, else the caller's opts.label) — URL-decoded by the handler.
8
11
  const WORKFLOW_AGENT_STEER_RE = /^\/v1\/workflows\/([^/]+)\/agents\/([^/]+)\/steer$/;
9
12
  export async function handleWorkflows(req, res, url, ctx) {
10
13
  const miss = { fell: false };
11
14
  await handleWorkflowsReadBody(req, res, url, ctx, miss);
12
15
  return !miss.fell;
13
16
  }
17
+ /** SVC-5 的 steer 路——`handle()` 里位置在 tasks/runs 之后(鉴权门之后),故是**独立**的域入口。 */
14
18
  export async function handleWorkflowAgentSteer(req, res, url, ctx) {
15
19
  const miss = { fell: false };
16
20
  await handleWorkflowAgentSteerBody(req, res, url, ctx, miss);
17
21
  return !miss.fell;
18
22
  }
23
+ /** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(唯一改动:统一去缩进两格)。裸 `return;`
24
+ * = 「本域已应答」;走到函数尾才是「没匹配上」。跨域的东西一律在首行解构出来。 */
19
25
  async function handleWorkflowsReadBody(req, res, url, ctx, miss) {
20
26
  const { deps } = ctx;
21
27
  const { runSessionAcceptOk } = ctx.helpers;
28
+ // S8 self-orchestration runs (design/97/98): GET /v1/workflows (list) · /v1/workflows/:id (detail) ·
29
+ // /v1/workflows/:id/stream (live SSE). OWNER-GATED by scope (= the creating principal, prepare-task.js
30
+ // scope: spec.principal). list/detail need the run store; live stream is in-process (subscribeWorkflow).
31
+ // 🔐 Identity from gatedPrincipal (direct-door-secure single point — crypto-verified principal JWT when
32
+ // directDoorActive, transparently `principalFrom` otherwise), NEVER the spoofable x-agent-principal header:
33
+ // a direct-door worker MUST NOT trust the raw header for a per-tenant owner gate (same bypass class —
34
+ // parity with the trace owner-gate, the workflow steer POST, and the elicit route). This block returns BEFORE
35
+ // the service-credential gate below, so the per-tenant gate here is the sole tenant boundary for these reads.
22
36
  const wfMatch = req.method === "GET" ? WORKFLOWS_RE.exec(url) : null;
23
37
  if (wfMatch) {
24
38
  const principal = gatedPrincipal(req, deps.config);
@@ -30,19 +44,29 @@ async function handleWorkflowsReadBody(req, res, url, ctx, miss) {
30
44
  sendError(res, 501, "capability.self_orchestration_required", "workflow runs require self-orchestration (SELF_ORCHESTRATION_ENABLED)");
31
45
  return;
32
46
  }
33
- const scope = principal ?? "default";
47
+ const scope = principal ?? "default"; // matches core's run_workflow scope = spec.principal ?? "default"
34
48
  const wfId = wfMatch[1];
35
49
  const isStream = wfMatch[2] === "/stream";
36
50
  if (!wfId) {
37
51
  const q = new URL(req.url ?? "", "http://x").searchParams;
38
52
  const statusFilter = q.get("status");
39
53
  const limit = Math.min(100, Math.max(1, Number(q.get("limit") ?? 50) || 50));
54
+ // [1512]§二 交付件接线(1.247 留的 list 缺口):`?session=` 过滤臂 —— present 即按
55
+ // `originatingSessionId` 严格等值筛。语义是**筛选**非 per-id 的接受拍:无字段行(pre-core-1.210)
56
+ // 不属于任何会话,筛选下剔除(per-id 面的 absent 放行是访问控制防误伤,两面语义刻意不同);
57
+ // 空串=无行可匹配=空列表;absent = 全量,字节不变。1.250:筛下推 store 侧(core 1.368 [1516]②
58
+ // `listByScope.opts.session`,limit **前**应用)——1.249 的「页内筛」升「全量筛」,忙 scope 下
59
+ // 比 newest-limit 窗口更老的会话行不再漏。直调 store(listWorkflowRuns 是纯透传,其 opts 类型
60
+ // 未收录 session)。
40
61
  const sessionFilter = q.get("session");
41
62
  const sessionRuns = await deps.workflowRunStore.listByScope(scope, {
42
63
  ...(statusFilter ? { status: statusFilter } : {}),
43
64
  limit,
44
65
  ...(sessionFilter !== null ? { session: sessionFilter } : {}),
45
66
  });
67
+ // K-2c LIST side (core 1.155 `WorkflowRunSummary` now carries name?/description?/currentPhase? from the
68
+ // script `meta` — flows via the pin bump): redact the LLM-authored label surfaces for parity with the DETAIL
69
+ // route (summarizeWorkflowDetail) + the fleet rows. The rest of the summary (status/counts/stats) rides as-is.
46
70
  const redacted = sessionRuns.map((r) => ({
47
71
  ...r,
48
72
  ...(r.name !== undefined ? { name: redactSecrets(r.name) } : {}),
@@ -52,18 +76,33 @@ async function handleWorkflowsReadBody(req, res, url, ctx, miss) {
52
76
  sendJson(res, 200, { workflows: redacted });
53
77
  return;
54
78
  }
79
+ // per-id: owner-gate via the persisted run's scope (no existence oracle for non-owners → 404). 1.146:
80
+ // getWorkflowRun is scope-filtered IN CORE (returns null unless run.scope === scope) — defense-in-depth on
81
+ // top of our route gate; the `run.scope !== scope` below is now redundant but kept (belt-and-suspenders).
82
+ // The SSE branch streams ONLY after this gate, so the live stream is owner-gated too (core's subscribeWorkflow
83
+ // does not self-filter — this gate is its tenant boundary).
55
84
  const run = await getWorkflowRun(deps.workflowRunStore, wfId, scope);
56
85
  if (!run || run.scope !== scope) {
57
86
  sendError(res, 404, "not_found.workflow", "workflow not found");
58
87
  return;
59
88
  }
89
+ // [1508] 1.247 — session ACCEPT phase on the workflow per-id faces ([1493]-class sibling: these deliver
90
+ // conversation-derived content — step prompts/outputs/live events — but were principal-gated only, while
91
+ // the durable WorkflowRun carries `originatingSessionId` since core 1.210). Same soft phase + helper as
92
+ // the run faces (present-mismatch → 404; absent → warn-once). The LIST face is NOT gated this拍:
93
+ // WorkflowRunSummary lacks the field (core ask, γ-batch alignment) — its rows stay redacted summaries.
60
94
  if (!runSessionAcceptOk(req, res, { sessionId: run.originatingSessionId ?? null }, wfMatch[2] === "/journal" ? "workflow.journal" : isStream ? "workflow.stream" : "workflow.detail", "workflow not found"))
61
95
  return;
62
96
  if (wfMatch[2] === "/journal") {
97
+ // [1402]/[1400]④:per-agent journal 读面(云本地同构——三后端店同契约)。owner 门已过(上方 run
98
+ // scope 校验);投影=bounded+redact(result 是 LLM 产物,注入面纪律同 fleet 行),ordinal 序稳定。
63
99
  if (!deps.workflowJournalStore) {
64
100
  sendError(res, 501, "capability.workflow_store_required", "workflow journal requires the journal store (SELF_ORCHESTRATION_ENABLED + a store backend)");
65
101
  return;
66
102
  }
103
+ // codex R1-H2:hard bounds——limit 默认 20 cap 50(?limit=)、?offset= 游标;SQL 店走 loadPage
104
+ // (行数 LIMIT/OFFSET + 单行 64KiB LENGTH 门在库端,峰值 ≤50×64KiB);file/in-memory 店(单机形)
105
+ // 回落全量 load 后同界投影(内存峰值=店内容,记档取舍)。
67
106
  const jq = new URL(req.url ?? "", "http://x").searchParams;
68
107
  const jLimit = Math.min(50, Math.max(1, Number(jq.get("limit") ?? 20) || 20));
69
108
  const jOffset = Math.max(0, Number(jq.get("offset") ?? 0) || 0);
@@ -83,6 +122,7 @@ async function handleWorkflowsReadBody(req, res, url, ctx, miss) {
83
122
  const page = await js.loadPage(wfId, scope, { offset: jOffset, limit: jLimit, maxResultBytes: MAX_ROW_BYTES });
84
123
  projected = page.map((row) => {
85
124
  if (row.resultJson === null) {
125
+ // 超单行门的条目:诚实标 truncated,不把 64KiB+ 的 TaskResult 拉进进程
86
126
  return { callKey: row.callKey, ordinal: callKeyOrdinal(row.callKey), truncated: true, resultBytes: row.resultBytes };
87
127
  }
88
128
  try {
@@ -94,6 +134,9 @@ async function handleWorkflowsReadBody(req, res, url, ctx, miss) {
94
134
  });
95
135
  }
96
136
  else {
137
+ // fallback(file/in-memory,单机 TOC 形——多租户 durable 恒 SQL 店=loadPage 覆盖):load 全量的
138
+ // 内存/事件环成本=用户读自己盘的单机取舍(ARCHITECTURE.md 记档);投影侧与 SQL 店**同一诚实
139
+ // 语义**——超 64KiB 的行标 truncated/resultBytes,不做静默 2000 字投影(跨后端 API 形一致)。
97
140
  const entries = await deps.workflowJournalStore.load(wfId, scope);
98
141
  projected = entries
99
142
  .sort((a, b) => callKeyOrdinal(a.callKey) - callKeyOrdinal(b.callKey))
@@ -109,35 +152,57 @@ async function handleWorkflowsReadBody(req, res, url, ctx, miss) {
109
152
  return;
110
153
  }
111
154
  if (isStream)
112
- await streamWorkflowRun(req, res, wfId, scope);
155
+ await streamWorkflowRun(req, res, wfId, scope); // core 1.366 B-3: scope-doored live stream(异 scope=空流,与不存在同形)
113
156
  else
114
- sendJson(res, 200, summarizeWorkflowDetail(run));
157
+ sendJson(res, 200, summarizeWorkflowDetail(run)); // SVC-4: structured drill-down (Phases panel + agent list + groups tree)
115
158
  return;
116
159
  }
117
160
  miss.fell = true;
118
161
  }
162
+ /** 同上纪律(SVC-5 steer 段)。 */
119
163
  async function handleWorkflowAgentSteerBody(req, res, url, ctx, miss) {
120
164
  const { deps } = ctx;
121
165
  const { readJson, rateLimited, quotaExceeded, leaseDenied, safeDecode, runSessionAcceptOk } = ctx.helpers;
166
+ // SVC-5 (design/97 CORE-5 #6): POST /v1/workflows/:id/agents/:label/steer — STEER a still-running workflow
167
+ // agent (`ctx.agentStream`) mid-flight. Unlike RUN steer (a whole task run), this targets ONE running agent
168
+ // inside a workflow, addressed by runId + label. Delivery is replica-local (the steerable handle is an
169
+ // in-memory bridge to the running TaskStream, like `steerableRuns`):
170
+ // • the handle is live on THIS replica → `handle.steer(redacted)` injects + returns the marker → 200.
171
+ // • not here but the workflow run is `running` (another replica) → 409 steering.not_running (cross-replica
172
+ // live-steer is the same fast-follow seam as RUN steer — single-replica-per-worker today; honest, never
173
+ // a silent drop). • terminal / no handle → 409 steering.not_running.
174
+ // 🔴 HANDLE-VISIBILITY SEAM: core returns a WorkflowAgentHandle ONLY from `WorkflowRunContext.agentStream`,
175
+ // and that host ctx structurally never enters the script runner (the LLM workflow sees the sterile
176
+ // `WorkflowPrimitives`, which has `agent` but not `agentStream`). The service runs workflows ONLY via core's
177
+ // `run_workflow` tool → primitives, so it never holds a handle to register. The registry/redaction/auth here
178
+ // are production-real (the populated path uses them unchanged); until core hands the deployment the handle
179
+ // (e.g. an onAgentStream lifecycle sink on run_workflow), the registry is genuinely empty and the route
180
+ // returns steering.not_running. See workflow-agent-steer.ts. Raised to core (SVC-5).
122
181
  const wfSteerMatch = req.method === "POST" ? WORKFLOW_AGENT_STEER_RE.exec(url) : null;
123
182
  if (wfSteerMatch) {
124
183
  if (rateLimited(req, res) || quotaExceeded(req, res) || (await leaseDenied(req, res)))
125
- return;
184
+ return; // mutating + steers a running model agent
126
185
  if (!deps.workflowRunStore) {
127
186
  sendError(res, 501, "capability.self_orchestration_required", "workflow runs require self-orchestration (SELF_ORCHESTRATION_ENABLED)");
128
187
  return;
129
188
  }
189
+ // Identity from gatedPrincipal (direct-door-secure single point), NEVER the spoofable header — `trusted`
190
+ // carries operator authority (a supervisor steering any tenant's agent), so deriving it from a header would
191
+ // be a privilege escalation (parity with RUN steer / preempt; HANDOFF 铁律 + core §3 inv #1).
130
192
  const principal = gatedPrincipal(req, deps.config);
131
193
  if (deps.config.requirePrincipal && principal === undefined) {
132
194
  sendError(res, 401, "auth.principal_required", `missing principal header '${deps.config.principalHeader}'`);
133
195
  return;
134
196
  }
197
+ // safeDecode (not bare decodeURIComponent): a malformed %-sequence is the CALLER's error → 400, not a 500
198
+ // (parity with the approvals + session-sync routes' established handling).
135
199
  const wfRunId = safeDecode(wfSteerMatch[1]);
136
200
  const label = safeDecode(wfSteerMatch[2]);
137
201
  if (wfRunId === null || label === null) {
138
202
  sendError(res, 400, "request.path_malformed", "malformed workflow path (invalid percent-encoding)");
139
203
  return;
140
204
  }
205
+ // Parse + validate the body BEFORE the run lookup (a bad body is the caller's error regardless of state).
141
206
  let body;
142
207
  try {
143
208
  body = (await readJson(req));
@@ -150,21 +215,43 @@ async function handleWorkflowAgentSteerBody(req, res, url, ctx, miss) {
150
215
  sendError(res, 400, "request.body_shape", "body must be { content: string (non-empty) }");
151
216
  return;
152
217
  }
218
+ // 入参上限(见 `STEER_IN_MAX_REQUEST_CHARS` 旁注):脱敏门是**同步**的,烧的是事件循环 ⇒
219
+ // 一次超大 steer 卡住的是**整个副本**。**拒**而不是服务端先截 —— 先截会让引擎的
220
+ // `[+N chars]` 披露低报,而"诚实标记不得低报"是本仓另一条钉守着的性质。
153
221
  if (body.content.length > STEER_IN_MAX_REQUEST_CHARS) {
154
222
  sendError(res, 413, "steer.content_too_large", `content exceeds ${STEER_IN_MAX_REQUEST_CHARS} characters (got ${body.content.length}); note only the first ${STEER_IN_MAX_CHARS} are delivered anyway`);
155
223
  return;
156
224
  }
225
+ // trusted = explicit operator (NOT bare isOperator — its empty-list "true-for-all" would let any caller
226
+ // steer any tenant's agent). A normal owner steers only their own workflow.
157
227
  const trusted = explicitOperatorOk(principal, deps.config.operatorPrincipals);
158
- const ownerScope = principal ?? "default";
228
+ const ownerScope = principal ?? "default"; // matches run_workflow scope = spec.principal ?? "default"
229
+ // Owner-gate via the persisted run, now scope-filtered IN CORE (1.146 getWorkflowRun(store, id, scope) →
230
+ // null unless run.scope === scope). For a NON-operator: a missing/other-tenant run is null → 404 (no
231
+ // existence oracle). An explicit OPERATOR (supervisor) may steer ANY tenant's workflow — but core's
232
+ // scope-filtered read can't fetch another tenant's run by the operator's own scope (→ null), so the operator
233
+ // is NOT gated on `wfRun` existence: its authority is the operator allowlist, and the steer target's
234
+ // existence+liveness is proven by the registry handle below (no handle → 409, never a wrong-tenant steer).
159
235
  const wfRun = await getWorkflowRun(deps.workflowRunStore, wfRunId, ownerScope);
160
236
  if (!trusted && !wfRun) {
161
237
  sendError(res, 404, "not_found.workflow", "workflow not found");
162
238
  return;
163
239
  }
240
+ // [1508] 1.247 — same session accept-phase as the read faces (a steer ACTS on the conversation's
241
+ // workflow). Gated on the fetched run only: the operator leg (wfRun null by scope-filtered read) keeps
242
+ // its registry-handle-proven authority below, and runSessionAcceptOk's operator branch covers a
243
+ // same-scope operator anyway.
164
244
  if (wfRun && !runSessionAcceptOk(req, res, { sessionId: wfRun.originatingSessionId ?? null }, "workflow.agent-steer", "workflow not found"))
165
245
  return;
166
246
  const sendNotRunningWf = (error) => sendError(res, 409, "steering.not_running", error);
247
+ // STEER-IN REDACTION GATE (SVC-5 信任门): treat the human/leader content as UNTRUSTED — redact host leaks +
248
+ // size-bound + fence — BEFORE it reaches the worker. core's `handle.steer` fences AGAIN inside its trusted
249
+ // marker framing (defense in depth). Applied regardless of `trusted`: an operator's copy-pasted steer can
250
+ // still leak a host path/token, and an authoritative-looking steer must not pose as system instructions.
167
251
  const redacted = redactSteerIn(body.content, label);
252
+ // Same-replica live path: the steerable handle is registered here → inject now, return the marker.
253
+ // core does not enforce label uniqueness within a run, so resolve() reports the count: >1 = two live agents
254
+ // share this label → the target is ambiguous, refuse (409) rather than steer an arbitrary one.
168
255
  const resolution = deps.workflowAgentRegistry?.resolve(wfRunId, label);
169
256
  if (resolution && resolution.count > 1) {
170
257
  sendError(res, 409, "steering.ambiguous_label", `${resolution.count} live agents share label '${label}' in this run — steer target is ambiguous`);
@@ -179,13 +266,18 @@ async function handleWorkflowAgentSteerBody(req, res, url, ctx, miss) {
179
266
  }
180
267
  catch (e) {
181
268
  const code = e.code;
269
+ // The agent settled between the registry lookup and steer() → core rejects steering.not_running.
182
270
  if (code === "steering.not_running") {
183
271
  sendNotRunningWf("agent just finished — no longer accepting steers");
184
272
  return;
185
273
  }
186
- throw e;
274
+ throw e; // unexpected → outer handler 500
187
275
  }
188
276
  }
277
+ // No live handle here. The workflow run is either terminal, or `running` on another replica (cross-replica
278
+ // live-steer = the same fast-follow seam as RUN steer; a workflow agent is in-memory steerable only on its
279
+ // running replica). Either way: honest 409, never a silent drop. `wfRun` may be null for an OPERATOR steering
280
+ // a cross-tenant run (core's scope-filtered read returned null) → a generic message (status unknowable here).
189
281
  sendNotRunningWf(!wfRun
190
282
  ? "workflow agent is not running on this replica (no live handle)"
191
283
  : wfRun.status === "running"
@@ -195,14 +287,37 @@ async function handleWorkflowAgentSteerBody(req, res, url, ctx, miss) {
195
287
  }
196
288
  miss.fell = true;
197
289
  }
290
+ /** SVC-4 v1 — the STRUCTURED drill-down view of one persisted {@link WorkflowRun} (the historical detail the
291
+ * Phases panel + agent list + groups tree render, parity with CC's Workflow tool). Pure projection over the
292
+ * persisted run — no new source of truth, no gate input (the route already owner-gated by scope before this).
293
+ * Three derived shapes:
294
+ * - `phases`: each `run.phases` entry + a done/total of the agents that ran under that `phase` title (done =
295
+ * terminal status). Plus an `unphased` bucket counting agents with no phase.
296
+ * - `agents`: each agent flattened to `{ label, status, callKey, groupId, phase, tokens, turns, durationMs,
297
+ * replayed, prompt, output }`. `prompt`/`output` are core-redacted + size-bounded; this is a HUMAN view so
298
+ * they are surfaced AT REST WITHOUT a fence (a consumer that re-feeds them to an LLM must fence itself —
299
+ * core's `WorkflowAgentRun` doc). `tokens`/`turns`/`costMicroUsd` come from the agent's own `stats`.
300
+ * - `groups`: the nested tree rebuilt from `run.groups` (parentGroupId) — each node carries its direct agent
301
+ * callKeys so a UI can hang the agent list off the tree. Empty when the script used no `ctx.workflow` nesting.
302
+ * `stats` (own + nested, kept separate per core R-5) and the run-level fields pass through unchanged. */
198
303
  function summarizeWorkflowDetail(run) {
199
304
  const isDone = (s) => s === "completed" || s === "failed";
305
+ // MF-W queued (core 1.150): a WorkflowAgentRun's `startedAt` is now OPTIONAL — ABSENT while an agent is QUEUED
306
+ // (waiting on a concurrency slot) or if it was aborted/finalized before it ran. So duration is undefined unless
307
+ // BOTH ends are present (a queued agent has no running duration — the DoR fix). Phases still always carry startedAt.
200
308
  const durationOf = (a) => a.startedAt === undefined || a.endedAt === undefined ? undefined : a.endedAt - a.startedAt;
309
+ // A real persisted run always carries these arrays; default defensively so a malformed/partial stored row
310
+ // projects cleanly instead of 500-ing the detail route (the view is read-only — never a gate input).
201
311
  const runPhases = run.phases ?? [];
202
312
  const runAgents = run.agents ?? [];
203
313
  const runGroups = run.groups ?? [];
314
+ // [review] label-parity: an LLM-authored run_workflow script controls phase titles + agent labels + the per-agent
315
+ // phase tag (core stores them raw, unbounded), so redact them on EMISSION for the same reason as name/description
316
+ // (a secret the agent read could land in a label). Filter/bucket on the RAW values (below) so grouping is unchanged;
317
+ // redactSecrets is deterministic → a secret-bearing title and the matching agent.phase scrub to the SAME string,
318
+ // so the shell's phase grouping still lines up. (prompt/output are already core-redacted; model is catalog-sourced.)
204
319
  const phases = runPhases.map((p) => {
205
- const inPhase = runAgents.filter((a) => a.phase === p.title);
320
+ const inPhase = runAgents.filter((a) => a.phase === p.title); // RAW compare — bucketing key, not emitted
206
321
  return {
207
322
  title: redactSecrets(p.title),
208
323
  status: p.status,
@@ -213,31 +328,50 @@ function summarizeWorkflowDetail(run) {
213
328
  total: inPhase.length,
214
329
  };
215
330
  });
331
+ // Agents with no phase title, OR a phase title that matches no registered `run.phases` entry (a stray
332
+ // `opts.phase` with no enclosing `phase()` block) — surfaced so the panel's counts are EXHAUSTIVE (an agent is
333
+ // in exactly one bucket: its registered phase, or `unphased`). Without the `titleSet` guard a stray-phase agent
334
+ // would fall into NO bucket (under-count). Empty-string phase is also unphased.
216
335
  const phaseTitleSet = new Set(runPhases.map((p) => p.title));
217
336
  const unphased = runAgents.filter((a) => a.phase === undefined || a.phase === "" || !phaseTitleSet.has(a.phase));
218
337
  const unphasedView = unphased.length > 0
219
338
  ? { done: unphased.filter((a) => isDone(a.status)).length, total: unphased.length }
220
339
  : undefined;
221
340
  const agents = runAgents.map((a) => ({
222
- label: redactSecrets(a.label),
341
+ label: redactSecrets(a.label), // [review] label-parity (LLM-authored, see phases comment)
223
342
  status: a.status,
343
+ // MF-W (1.149 CORE-shared): the canonical display status (running|queued|done|failed|interrupted) via core's
344
+ // deriveAgentDisplayStatus — the SAME pure fn the shell renders, so the agent-state glyph can't drift between
345
+ // shells (one source of truth in core). `interrupted` = a non-terminal agent under a terminal run. The live
346
+ // `idle` overlay (a running agent with no recent activity) stays a SHELL wall-clock concern — since core 1.295
347
+ // ToolActivity carries `at`, this view exposes the raw timestamps (activity[].at + lastActivityAt below) and
348
+ // the shell derives idleness against its own clock (a served idleMs would drift on a historical view).
224
349
  displayStatus: deriveAgentDisplayStatus(a, run.status),
225
350
  taskStatus: a.taskStatus,
226
351
  callKey: a.callKey,
227
352
  groupId: a.groupId,
228
- phase: a.phase !== undefined ? redactSecrets(a.phase) : undefined,
229
- model: a.model,
353
+ phase: a.phase !== undefined ? redactSecrets(a.phase) : undefined, // [review] label-parity; undefined = unphased (kept)
354
+ model: a.model, // SVC-4 v2 / CORE-8 ①: per-agent model display label ("Opus 4.8 (1M context)" row)
230
355
  tokens: a.stats?.tokens,
231
356
  turns: a.stats?.turns,
232
- toolCalls: a.toolCalls,
357
+ toolCalls: a.toolCalls, // SVC-4 v2 / CORE-8 ②: total tool calls ("M" in "last N of M tool calls")
358
+ // SVC-4 v2 / CORE-8 ③: the LAST-N tool-call beats (structural — phase/toolName/ids, no args/output). The live
359
+ // detail SSE (subscribeWorkflow → streamWorkflowRun) already forwards the per-callKey `agent_activity` events
360
+ // verbatim for a running drill-down; this is the persisted tail a historical view renders.
361
+ // [843]④a (core 1.295): each beat now carries `at` (epoch ms, stamped on BOTH start and end) — verbatim
362
+ // passthrough here, so idle derives shell-side as the FRAME GAP (end.at → next start.at), the [879] ruling.
233
363
  activity: a.activity,
364
+ // [843]④a convenience: the tail beat's timestamp, so a shell can grey a stale agent without walking the
365
+ // array. TIMESTAMP not a duration on purpose — this detail is a HISTORICAL view, so a served `idleMs`
366
+ // (Date.now()-derived) would drift on every re-read; the shell owns the clock. Absent for pre-1.295 runs
367
+ // whose beats carry no `at` (undefined → key omitted by JSON, same back-compat shape as agentFailures).
234
368
  lastActivityAt: a.activity && a.activity.length > 0 ? a.activity[a.activity.length - 1].at : undefined,
235
369
  durationMs: durationOf(a),
236
- queuedAt: a.queuedAt,
237
- startedAt: a.startedAt,
370
+ queuedAt: a.queuedAt, // MF-W queued (1.150): when the agent was ENQUEUED (always set) — a queued agent has queuedAt but no startedAt
371
+ startedAt: a.startedAt, // now optional (absent while queued / aborted-before-run) — displayStatus already reflects "queued"
238
372
  endedAt: a.endedAt,
239
373
  replayed: a.replayed,
240
- prompt: a.prompt,
374
+ prompt: a.prompt, // core-redacted + bounded; human view → no fence at rest
241
375
  output: a.output,
242
376
  }));
243
377
  const byId = new Map();
@@ -253,6 +387,9 @@ function summarizeWorkflowDetail(run) {
253
387
  children: [],
254
388
  });
255
389
  }
390
+ // Walk up `parentGroupId` from `node`; true if the chain LOOPS (self-parent, or a multi-node cycle A→B→A)
391
+ // rather than terminating at a top-level (`undefined`) or dangling (absent) parent. Bounded by the seen-set, so
392
+ // a malformed run can neither hang nor build a circular object that `JSON.stringify` would throw 500 on.
256
393
  const inCycle = (start) => {
257
394
  const seen = new Set();
258
395
  let cur = start;
@@ -267,18 +404,27 @@ function summarizeWorkflowDetail(run) {
267
404
  const roots = [];
268
405
  for (const node of byId.values()) {
269
406
  const parent = node.parentGroupId !== undefined ? byId.get(node.parentGroupId) : undefined;
407
+ // Link to parent ONLY when that can't form a cycle — a self/multi-node cycle member grafts onto root, so the
408
+ // tree is always acyclic (JSON-safe) and no node is ever dropped.
270
409
  if (parent && parent !== node && !inCycle(node))
271
410
  parent.children.push(node);
272
411
  else
273
- roots.push(node);
412
+ roots.push(node); // top-level, dangling parent, or cycle member → graft onto root (never dropped)
274
413
  }
275
414
  return {
276
415
  id: run.id,
277
416
  scope: run.scope,
278
417
  status: run.status,
418
+ // K-2c (shell §K): the workflow header label + subtitle the MF-W detail view needs — from the script's
419
+ // `export const meta = {name, description}` (core WorkflowRun.name/description). REDACTED for label parity with
420
+ // the fleet run/subagent names (an LLM-authored meta is task-controlled → could carry a secret shape). Per-phase
421
+ // title is already on each `phases[].title`. Omitted when absent (back-compat: older runs have neither).
279
422
  ...(run.name !== undefined ? { name: redactSecrets(run.name) } : {}),
280
423
  ...(run.description !== undefined ? { description: redactSecrets(run.description) } : {}),
281
424
  stats: run.stats,
425
+ // core 1.293: run-level failed-agent count (agents whose status ended "failed"), stamped at run end only
426
+ // when > 0. A plain number (not task-controlled text) → no redaction. Omitted when absent (back-compat:
427
+ // pre-1.293 runs / zero failures) — [843]④a: the detail view had no failure count to render.
282
428
  ...(run.agentFailures !== undefined ? { agentFailures: run.agentFailures } : {}),
283
429
  startedAt: run.startedAt,
284
430
  endedAt: run.endedAt,
@@ -286,6 +432,10 @@ function summarizeWorkflowDetail(run) {
286
432
  durationMs: durationOf(run),
287
433
  rev: run.rev,
288
434
  error: run.error,
435
+ // core 1.234: the script's RETURN VALUE — bounded + redacted AT THE SOURCE (completion-time,
436
+ // same egress discipline as the notifier), but re-redacted here for defence-in-depth parity with
437
+ // name/description (this projection's whitelist predates the field — live-dig T8 caught the omission:
438
+ // TaskOutput carried the result while this detail view silently dropped it). Absent on failed/pre-1.234 runs.
289
439
  ...(run.result !== undefined ? { result: redactSecrets(run.result) } : {}),
290
440
  phases,
291
441
  ...(unphasedView ? { unphased: unphasedView } : {}),
@@ -293,14 +443,25 @@ function summarizeWorkflowDetail(run) {
293
443
  groups: roots,
294
444
  };
295
445
  }
446
+ /** SSE the live progress of a RUNNING workflow (S8). subscribeWorkflow is IN-PROCESS (replica-local): the
447
+ * iterable ends when the run's channel closes at run_end, or when the client disconnects. A run with no live
448
+ * channel (already finished, or on another replica) yields nothing → the stream ends immediately (the
449
+ * historical view is GET /v1/workflows/:id). Owner-gating (run.scope === principal) is done by the caller;
450
+ * core 1.366 (B-3) additionally doors the subscription itself on scope — a mismatched scope yields an empty
451
+ * stream (same shape as not-found), so the caller passes the SAME scope it owner-gated with. */
296
452
  async function streamWorkflowRun(req, res, runId, scope) {
297
453
  sseHeaders(res);
298
454
  res.write(`event: meta\ndata: ${JSON.stringify({ version: 1, runId })}\n\n`);
299
455
  let closed = false;
456
+ // subscribeWorkflow is a PUSH iterator: a parked `next()` on an idle-but-alive workflow does NOT observe a
457
+ // `closed` flag, so a disconnected client would leave the subscriber queue registered in core's channels and
458
+ // publishWorkflowEvent would buffer events for the dead peer forever (a leak). Drive the iterator explicitly so
459
+ // disconnect can call `it.return()` — which unwinds the parked next() AND runs core's finally to prune the queue.
300
460
  const it = subscribeWorkflow(runId, scope)[Symbol.asyncIterator]();
461
+ // Heartbeat so an idle proxy/mesh doesn't drop the connection (and a dead TCP peer is detected → "close").
301
462
  const hb = setInterval(() => {
302
463
  if (!res.writableEnded)
303
- res.write(`event: heartbeat\ndata: {}\n\n`);
464
+ res.write(`event: heartbeat\ndata: {}\n\n`); // real frame (not an SSE comment) — a per-frame-parsing BFF drops comments, so downstream saw a zero-frame window; EventSource clients without a heartbeat listener ignore it (zero break)
304
465
  }, 15_000);
305
466
  if (typeof hb.unref === "function")
306
467
  hb.unref();
@@ -309,12 +470,12 @@ async function streamWorkflowRun(req, res, runId, scope) {
309
470
  return;
310
471
  closed = true;
311
472
  clearInterval(hb);
312
- void it.return?.(undefined);
473
+ void it.return?.(undefined); // prune core's subscriber queue (workflow-observe finally) — stop buffering for a dead peer
313
474
  if (!res.writableEnded)
314
475
  res.end();
315
476
  };
316
477
  req.on("close", shutdown);
317
- res.on("close", shutdown);
478
+ res.on("close", shutdown); // res close (peer gone) too, not just req — either side ending must prune the queue
318
479
  try {
319
480
  for (let next = await it.next(); !next.done; next = await it.next()) {
320
481
  if (closed || res.writableEnded)
@@ -1,4 +1,13 @@
1
+ /**
2
+ * design/158 A9 装配缝的一片小叶子 —— 提交路径共用的两个纯函数(run 行元数据 + cascade 配置)。
3
+ *
4
+ * 为什么单独成文件:`runMeta` 被 tasks 与 runs **两个**域模块调用,`cascadeConfig` 同理(还被测试直接引)。
5
+ * 挂在任一域模块上都会造出 routes/* 互相值 import 的第二条边;它们又是纯函数(只依赖入参 + redact 叶子),
6
+ * 所以正确归处是叶子。server.ts 转出口 `cascadeConfig` 以保住既有 import 点。
7
+ */
1
8
  import type { TaskSpec, CascadeConfig } from "@sema-agent/core";
9
+ /** The denormalized run-row metadata captured at submit: correlation (jobId), credential-derived source,
10
+ * and a redacted objective glance for lists. One place so the three submit paths can't drift. */
2
11
  export declare function runMeta(prepared: {
3
12
  jobId?: string;
4
13
  spec: TaskSpec;
@@ -7,5 +16,7 @@ export declare function runMeta(prepared: {
7
16
  source: string | null;
8
17
  objectivePreview: string | null;
9
18
  };
19
+ /** Build the cascade config from the operator ladder. Default gate = `status==="completed"` (escalate
20
+ * on a cheap-rung failure); cost-ceiling from the task's budget bounds the cold-rerun tax. */
10
21
  export declare function cascadeConfig(ladder: readonly string[], maxCostUsd?: number): CascadeConfig;
11
22
  //# sourceMappingURL=run-meta.d.ts.map
@@ -1,12 +1,18 @@
1
1
  import { redactSecrets } from "../trace/redact.js";
2
+ /** The denormalized run-row metadata captured at submit: correlation (jobId), credential-derived source,
3
+ * and a redacted objective glance for lists. One place so the three submit paths can't drift. */
2
4
  export function runMeta(prepared, source) {
3
5
  const obj = prepared.spec.objective ?? "";
4
6
  return {
5
7
  jobId: prepared.jobId ?? null,
6
8
  source,
9
+ // redactSecrets is best-effort (known key/token shapes only — NOT exhaustive; see redact.ts). Acceptable
10
+ // here: the preview rides the fleet-token-gated trace surface, not an end-user-readable channel (council #1).
7
11
  objectivePreview: obj ? redactSecrets(obj).slice(0, 120) : null,
8
12
  };
9
13
  }
14
+ /** Build the cascade config from the operator ladder. Default gate = `status==="completed"` (escalate
15
+ * on a cheap-rung failure); cost-ceiling from the task's budget bounds the cold-rerun tax. */
10
16
  export function cascadeConfig(ladder, maxCostUsd) {
11
17
  return {
12
18
  ladder: ladder.map((model) => ({ model })),
@@ -1,7 +1,26 @@
1
+ /**
2
+ * design/158 A9 seam — response senders, extracted VERBATIM out of `http/server.ts` so the per-domain route
3
+ * modules under `http/routes/` can call them without a value edge back into `server.ts` (that edge would close
4
+ * a runtime import cycle — see `test/module-cycle-gate.test.ts`).
5
+ *
6
+ * Pure move: bodies are byte-identical to the ones that lived at `server.ts:9720/9822/9834/9843`. `server.ts`
7
+ * now imports them from here, so all ~500 existing `sendJson(...)` call sites are unchanged text.
8
+ */
1
9
  import type { ServerResponse } from "node:http";
2
10
  export declare function sseHeaders(res: ServerResponse, extra?: Record<string, string>): void;
3
11
  export declare function sendJson(res: ServerResponse, status: number, body: unknown): void;
12
+ /** A6(design/158 lens1 §C3/lens3 §C3a): canonical error-response sender for sites that already carry a
13
+ * machine `errorCode` — `errorCode` is a required positional arg (not optional-bag) so a call site
14
+ * cannot regress to the "code but no errorCode" shape the error-code-key-gate forbids. Body shape is
15
+ * `{ error: message, errorCode, ...extra }`; a site that used to ALSO emit a legacy `code` key keeps it
16
+ * by passing `{ code: ... }` inside `extra` — this is a pure call-site migration (zero wire-contract
17
+ * change), not a new response shape. Sites where the two machine-code channels genuinely disagree, or
18
+ * where `error`/`errorCode` aren't cleanly separable from the rest of the body, are left on raw
19
+ * `sendJson` (see the A6 migration notes for the callers still doing that on purpose). */
4
20
  export declare function sendError(res: ServerResponse, status: number, errorCode: string, message: string, extra?: Record<string, unknown>): void;
21
+ /** B7(design/158 lens1 §C3):`HttpError` 的兜底 catch-all 站点没有自己的语义分支——`err.code` 在 42 个
22
+ * throw 点里只有 3 个带。为了让「errorCode 恒在」在这些站点也成立(而不是靠豁免表放行),按**状态码**
23
+ * 给一个诚实的粗码:它只声明「这类拒绝」,不冒充比 throw 点更细的语义。带 `code` 的 throw 点原样优先。 */
5
24
  export declare function httpErrorCode(status: number, code?: string): string;
6
25
  export declare function msg(e: unknown): string;
7
26
  //# sourceMappingURL=send.d.ts.map
package/dist/http/send.js CHANGED
@@ -10,9 +10,20 @@ export function sendJson(res, status, body) {
10
10
  res.writeHead(status, { "content-type": "application/json" });
11
11
  res.end(JSON.stringify(body));
12
12
  }
13
+ /** A6(design/158 lens1 §C3/lens3 §C3a): canonical error-response sender for sites that already carry a
14
+ * machine `errorCode` — `errorCode` is a required positional arg (not optional-bag) so a call site
15
+ * cannot regress to the "code but no errorCode" shape the error-code-key-gate forbids. Body shape is
16
+ * `{ error: message, errorCode, ...extra }`; a site that used to ALSO emit a legacy `code` key keeps it
17
+ * by passing `{ code: ... }` inside `extra` — this is a pure call-site migration (zero wire-contract
18
+ * change), not a new response shape. Sites where the two machine-code channels genuinely disagree, or
19
+ * where `error`/`errorCode` aren't cleanly separable from the rest of the body, are left on raw
20
+ * `sendJson` (see the A6 migration notes for the callers still doing that on purpose). */
13
21
  export function sendError(res, status, errorCode, message, extra) {
14
22
  sendJson(res, status, { error: message, errorCode, ...extra });
15
23
  }
24
+ /** B7(design/158 lens1 §C3):`HttpError` 的兜底 catch-all 站点没有自己的语义分支——`err.code` 在 42 个
25
+ * throw 点里只有 3 个带。为了让「errorCode 恒在」在这些站点也成立(而不是靠豁免表放行),按**状态码**
26
+ * 给一个诚实的粗码:它只声明「这类拒绝」,不冒充比 throw 点更细的语义。带 `code` 的 throw 点原样优先。 */
16
27
  export function httpErrorCode(status, code) {
17
28
  if (code)
18
29
  return code;