@sema-agent/server 1.323.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (539) hide show
  1. package/dist/approval-hmac.d.ts +40 -0
  2. package/dist/approval-hmac.js +67 -0
  3. package/dist/approval.d.ts +31 -0
  4. package/dist/approval.js +72 -4
  5. package/dist/audit.d.ts +26 -0
  6. package/dist/audit.js +40 -1
  7. package/dist/auth-bridge.d.ts +10 -0
  8. package/dist/auth-bridge.js +47 -4
  9. package/dist/auth-keys.d.ts +19 -0
  10. package/dist/auth-keys.js +13 -0
  11. package/dist/bake-runner/main.d.ts +5 -0
  12. package/dist/bake-runner/main.js +43 -4
  13. package/dist/bake-runner/protocol.d.ts +57 -0
  14. package/dist/bake-runner/protocol.js +58 -3
  15. package/dist/bake-runner/runner.d.ts +54 -0
  16. package/dist/bake-runner/runner.js +87 -5
  17. package/dist/bench/l8/artifact.d.ts +63 -0
  18. package/dist/bench/l8/artifact.js +61 -2
  19. package/dist/bench/l8/escape.d.ts +36 -0
  20. package/dist/bench/l8/escape.js +25 -0
  21. package/dist/bench/l8/index.d.ts +14 -0
  22. package/dist/bench/l8/index.js +14 -0
  23. package/dist/bench/l8/probes.d.ts +74 -0
  24. package/dist/bench/l8/probes.js +60 -0
  25. package/dist/bench/l8/run-probes.d.ts +43 -0
  26. package/dist/bench/l8/run-probes.js +45 -2
  27. package/dist/bench/s1/arms.d.ts +126 -0
  28. package/dist/bench/s1/arms.js +188 -13
  29. package/dist/bench/s1/live-deps.d.ts +66 -0
  30. package/dist/bench/s1/live-deps.js +249 -11
  31. package/dist/bench/s1/oracle.d.ts +49 -0
  32. package/dist/bench/s1/oracle.js +36 -1
  33. package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
  34. package/dist/bench/s1/repair-oracle-adapter.js +30 -0
  35. package/dist/bench/s1/reviewer.d.ts +38 -0
  36. package/dist/bench/s1/reviewer.js +34 -0
  37. package/dist/bench/s1/row.d.ts +77 -0
  38. package/dist/bench/s1/row.js +62 -0
  39. package/dist/bench/s1/run-firm.d.ts +47 -0
  40. package/dist/bench/s1/run-firm.js +70 -4
  41. package/dist/bench/s1/runner-ctx.d.ts +71 -0
  42. package/dist/bench/s1/runner-ctx.js +36 -0
  43. package/dist/bench/s1/tasks.d.ts +108 -0
  44. package/dist/bench/s1/tasks.js +115 -0
  45. package/dist/boot-reclaim.d.ts +19 -0
  46. package/dist/boot-reclaim.js +20 -0
  47. package/dist/brain.d.ts +25 -0
  48. package/dist/brain.js +61 -1
  49. package/dist/budget.d.ts +66 -0
  50. package/dist/budget.js +128 -4
  51. package/dist/capabilities/builtin-tools.d.ts +3 -0
  52. package/dist/capabilities/builtin-tools.js +3 -0
  53. package/dist/capabilities/center-plugins.d.ts +17 -1
  54. package/dist/capabilities/center-plugins.js +45 -4
  55. package/dist/capabilities/center-prompts.d.ts +46 -0
  56. package/dist/capabilities/center-prompts.js +57 -3
  57. package/dist/capabilities/code-review-council.d.ts +28 -0
  58. package/dist/capabilities/code-review-council.js +40 -7
  59. package/dist/capabilities/collab-wire.d.ts +2 -0
  60. package/dist/capabilities/collab-workflows.d.ts +7 -0
  61. package/dist/capabilities/collab-workflows.js +44 -1
  62. package/dist/capabilities/oa-tools.d.ts +6 -0
  63. package/dist/capabilities/oa-tools.js +10 -0
  64. package/dist/capabilities/prompt.d.ts +14 -0
  65. package/dist/capabilities/prompt.js +14 -0
  66. package/dist/capabilities/prompts/code-review.d.ts +14 -0
  67. package/dist/capabilities/prompts/code-review.js +14 -0
  68. package/dist/capabilities/prompts/identity.d.ts +6 -0
  69. package/dist/capabilities/prompts/identity.js +6 -0
  70. package/dist/capabilities/prompts/team.d.ts +4 -0
  71. package/dist/capabilities/prompts/team.js +4 -0
  72. package/dist/capabilities/repo-tools.d.ts +11 -0
  73. package/dist/capabilities/repo-tools.js +8 -0
  74. package/dist/capabilities/sandbox-file-send.d.ts +95 -0
  75. package/dist/capabilities/sandbox-file-send.js +91 -3
  76. package/dist/capabilities/scenarios.d.ts +130 -0
  77. package/dist/capabilities/scenarios.js +180 -7
  78. package/dist/capabilities/select-environment-tool.d.ts +7 -0
  79. package/dist/capabilities/select-environment-tool.js +30 -0
  80. package/dist/capabilities/send-user-file-tool.d.ts +33 -0
  81. package/dist/capabilities/send-user-file-tool.js +65 -1
  82. package/dist/capabilities/skills.d.ts +29 -0
  83. package/dist/capabilities/skills.js +13 -2
  84. package/dist/capabilities/team.d.ts +22 -0
  85. package/dist/capabilities/team.js +22 -2
  86. package/dist/capabilities/tool-defer.d.ts +26 -0
  87. package/dist/capabilities/tool-defer.js +3 -0
  88. package/dist/config-center/apply-effective.d.ts +65 -0
  89. package/dist/config-center/apply-effective.js +204 -12
  90. package/dist/config-center/http-client.d.ts +34 -0
  91. package/dist/config-center/http-client.js +68 -0
  92. package/dist/config-center/restart-signal.d.ts +22 -0
  93. package/dist/config-center/restart-signal.js +39 -0
  94. package/dist/config-center/skills-mcp.d.ts +20 -1
  95. package/dist/config-center/skills-mcp.js +42 -7
  96. package/dist/config-center/types.d.ts +92 -1
  97. package/dist/config-lkg.d.ts +12 -0
  98. package/dist/config-lkg.js +41 -0
  99. package/dist/config-provider.d.ts +78 -0
  100. package/dist/config-provider.js +165 -2
  101. package/dist/config-types.d.ts +546 -0
  102. package/dist/config.d.ts +64 -0
  103. package/dist/config.js +455 -60
  104. package/dist/degenerate-instrument.d.ts +38 -0
  105. package/dist/degenerate-instrument.js +55 -3
  106. package/dist/elicitation.d.ts +39 -0
  107. package/dist/elicitation.js +87 -3
  108. package/dist/env-facts.d.ts +61 -0
  109. package/dist/env-facts.js +73 -7
  110. package/dist/fleet/fleet-bus.d.ts +185 -0
  111. package/dist/fleet/fleet-bus.js +333 -27
  112. package/dist/fleet/subagent-tail-bus.d.ts +11 -0
  113. package/dist/fleet/subagent-tail-bus.js +33 -2
  114. package/dist/fleet-client.d.ts +38 -0
  115. package/dist/fleet-client.js +44 -9
  116. package/dist/fleet-lease.d.ts +46 -0
  117. package/dist/fleet-lease.js +73 -5
  118. package/dist/hooks/branch-transcript.d.ts +8 -0
  119. package/dist/hooks/branch-transcript.js +33 -0
  120. package/dist/hooks/cc-stop-prompt.d.ts +42 -0
  121. package/dist/hooks/cc-stop-prompt.js +43 -1
  122. package/dist/hooks/hook-llm.d.ts +15 -0
  123. package/dist/hooks/hook-llm.js +47 -0
  124. package/dist/hooks/hook-runner.d.ts +89 -0
  125. package/dist/hooks/hook-runner.js +327 -18
  126. package/dist/http/idempotency.d.ts +31 -0
  127. package/dist/http/idempotency.js +34 -0
  128. package/dist/http/principal-gate.d.ts +17 -0
  129. package/dist/http/principal-gate.js +35 -3
  130. package/dist/http/route-ctx.d.ts +36 -0
  131. package/dist/http/routes/approvals-assistant.d.ts +25 -0
  132. package/dist/http/routes/approvals-assistant.js +253 -23
  133. package/dist/http/routes/attachments.js +22 -4
  134. package/dist/http/routes/capabilities.d.ts +7 -0
  135. package/dist/http/routes/capabilities.js +202 -1
  136. package/dist/http/routes/fleet.d.ts +6 -0
  137. package/dist/http/routes/fleet.js +94 -5
  138. package/dist/http/routes/images.d.ts +17 -0
  139. package/dist/http/routes/images.js +222 -14
  140. package/dist/http/routes/leader.d.ts +7 -0
  141. package/dist/http/routes/leader.js +10 -0
  142. package/dist/http/routes/memory-policy.d.ts +6 -0
  143. package/dist/http/routes/memory-policy.js +38 -6
  144. package/dist/http/routes/notify-wake.d.ts +10 -0
  145. package/dist/http/routes/notify-wake.js +19 -3
  146. package/dist/http/routes/observability.d.ts +6 -0
  147. package/dist/http/routes/observability.js +19 -2
  148. package/dist/http/routes/runs.d.ts +15 -0
  149. package/dist/http/routes/runs.js +464 -41
  150. package/dist/http/routes/session-sync.d.ts +15 -0
  151. package/dist/http/routes/session-sync.js +229 -19
  152. package/dist/http/routes/sessions-list.d.ts +6 -0
  153. package/dist/http/routes/sessions-list.js +35 -4
  154. package/dist/http/routes/sessions.d.ts +14 -0
  155. package/dist/http/routes/sessions.js +176 -31
  156. package/dist/http/routes/side-query.d.ts +8 -0
  157. package/dist/http/routes/side-query.js +26 -1
  158. package/dist/http/routes/tasks.d.ts +18 -0
  159. package/dist/http/routes/tasks.js +501 -38
  160. package/dist/http/routes/trace-usage.d.ts +10 -0
  161. package/dist/http/routes/trace-usage.js +66 -14
  162. package/dist/http/routes/workflows.d.ts +12 -0
  163. package/dist/http/routes/workflows.js +180 -19
  164. package/dist/http/run-meta.d.ts +11 -0
  165. package/dist/http/run-meta.js +6 -0
  166. package/dist/http/send.d.ts +19 -0
  167. package/dist/http/send.js +11 -0
  168. package/dist/http/server.d.ts +255 -0
  169. package/dist/http/server.js +921 -75
  170. package/dist/http/sse-log.d.ts +15 -0
  171. package/dist/http/sse-log.js +16 -0
  172. package/dist/http/tar.d.ts +9 -0
  173. package/dist/http/tar.js +21 -5
  174. package/dist/http/wire-gate.d.ts +9 -0
  175. package/dist/http/wire-gate.js +9 -0
  176. package/dist/http/wire-types.d.ts +187 -0
  177. package/dist/http/workspace-content.d.ts +8 -0
  178. package/dist/http/workspace-content.js +10 -0
  179. package/dist/images/bake-validate.d.ts +63 -0
  180. package/dist/images/bake-validate.js +71 -1
  181. package/dist/images/manifest.d.ts +19 -0
  182. package/dist/images/manifest.js +6 -1
  183. package/dist/index.d.ts +1 -5
  184. package/dist/index.js +10 -5
  185. package/dist/key-resolver.d.ts +23 -0
  186. package/dist/key-resolver.js +27 -2
  187. package/dist/leader/diffout.d.ts +23 -0
  188. package/dist/leader/diffout.js +8 -1
  189. package/dist/leader/diffup.d.ts +32 -0
  190. package/dist/leader/diffup.js +43 -0
  191. package/dist/leader/endpoint.d.ts +20 -0
  192. package/dist/leader/endpoint.js +25 -2
  193. package/dist/leader/fanout.d.ts +60 -0
  194. package/dist/leader/fanout.js +29 -3
  195. package/dist/leader/grader-env-factory.d.ts +74 -0
  196. package/dist/leader/grader-env-factory.js +62 -3
  197. package/dist/leader/leader.d.ts +97 -0
  198. package/dist/leader/leader.js +151 -4
  199. package/dist/leader/merge.d.ts +94 -0
  200. package/dist/leader/merge.js +112 -9
  201. package/dist/leader/planner.d.ts +52 -0
  202. package/dist/leader/planner.js +47 -3
  203. package/dist/leader/repair-oracle.d.ts +61 -0
  204. package/dist/leader/repair-oracle.js +59 -1
  205. package/dist/leader/repair-wire.d.ts +78 -0
  206. package/dist/leader/repair-wire.js +79 -2
  207. package/dist/leader/wire.d.ts +79 -0
  208. package/dist/leader/wire.js +255 -8
  209. package/dist/lsp/e2b-bridge.d.ts +16 -0
  210. package/dist/lsp/e2b-bridge.js +32 -4
  211. package/dist/lsp/e2b-manager.d.ts +10 -0
  212. package/dist/lsp/e2b-manager.js +18 -2
  213. package/dist/lsp/lsp-frames.d.ts +12 -0
  214. package/dist/lsp/lsp-frames.js +12 -0
  215. package/dist/lsp/manager.d.ts +12 -0
  216. package/dist/lsp/manager.js +35 -2
  217. package/dist/lsp/types.d.ts +10 -0
  218. package/dist/lsp/ws-transport.d.ts +12 -0
  219. package/dist/lsp/ws-transport.js +53 -5
  220. package/dist/lsp-evict.d.ts +14 -0
  221. package/dist/lsp-evict.js +15 -1
  222. package/dist/main.js +2132 -96
  223. package/dist/memory-export.d.ts +1 -0
  224. package/dist/memory-export.js +4 -0
  225. package/dist/memory-scope.d.ts +49 -0
  226. package/dist/memory-scope.js +87 -4
  227. package/dist/memory-sync-client.d.ts +24 -0
  228. package/dist/memory-sync-client.js +41 -2
  229. package/dist/memory-sync.d.ts +23 -0
  230. package/dist/memory-sync.js +70 -1
  231. package/dist/model-select.d.ts +39 -0
  232. package/dist/model-select.js +42 -1
  233. package/dist/observability/cost-quota.d.ts +20 -0
  234. package/dist/observability/cost-quota.js +4 -0
  235. package/dist/observability/cost-taxonomy.d.ts +45 -0
  236. package/dist/observability/cost-taxonomy.js +34 -0
  237. package/dist/observability/logger.d.ts +1 -0
  238. package/dist/observability/logger.js +6 -0
  239. package/dist/observability/metrics.d.ts +13 -0
  240. package/dist/observability/metrics.js +63 -0
  241. package/dist/observability/otel-exporter.d.ts +9 -0
  242. package/dist/observability/otel-exporter.js +11 -3
  243. package/dist/observability/principal-context.d.ts +9 -0
  244. package/dist/observability/principal-context.js +9 -0
  245. package/dist/observability/prompt-manifest.d.ts +37 -0
  246. package/dist/observability/prompt-manifest.js +35 -1
  247. package/dist/observability/rate-limit.d.ts +10 -0
  248. package/dist/observability/rate-limit.js +1 -0
  249. package/dist/observability/tool-trace.d.ts +24 -0
  250. package/dist/observability/tool-trace.js +41 -1
  251. package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
  252. package/dist/orchestration/hardened-vm-runner.js +119 -4
  253. package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
  254. package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
  255. package/dist/orchestration/hardened-vm-worker.js +27 -0
  256. package/dist/orchestration/subagent-steer.d.ts +44 -0
  257. package/dist/orchestration/subagent-steer.js +45 -1
  258. package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
  259. package/dist/orchestration/workflow-agent-steer.js +80 -1
  260. package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
  261. package/dist/orchestration/workflow-completion-inbox.js +283 -32
  262. package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
  263. package/dist/orchestration/workflow-notify-journal.js +187 -13
  264. package/dist/parked-decide.d.ts +61 -0
  265. package/dist/parked-decide.js +58 -1
  266. package/dist/per-task-image.d.ts +54 -0
  267. package/dist/per-task-image.js +41 -0
  268. package/dist/plan-cache-probe.d.ts +35 -0
  269. package/dist/plan-cache-probe.js +18 -3
  270. package/dist/plugins/approval-exemption-store.d.ts +7 -0
  271. package/dist/plugins/approval-exemption-store.js +28 -2
  272. package/dist/plugins/approval-store-sql.d.ts +64 -0
  273. package/dist/plugins/approval-store-sql.js +32 -0
  274. package/dist/plugins/background-agent-store-sql.d.ts +55 -0
  275. package/dist/plugins/background-agent-store-sql.js +91 -4
  276. package/dist/plugins/background-shell-support.d.ts +98 -0
  277. package/dist/plugins/background-shell-support.js +117 -13
  278. package/dist/plugins/blob-backend.d.ts +104 -0
  279. package/dist/plugins/blob-backend.js +163 -6
  280. package/dist/plugins/breaker-state-sql.d.ts +65 -1
  281. package/dist/plugins/breaker-state-sql.js +46 -8
  282. package/dist/plugins/caching-session-store.d.ts +42 -0
  283. package/dist/plugins/caching-session-store.js +63 -3
  284. package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
  285. package/dist/plugins/checkpoint-store-sql.js +239 -11
  286. package/dist/plugins/file-outcome-sink.d.ts +4 -0
  287. package/dist/plugins/file-outcome-sink.js +9 -0
  288. package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
  289. package/dist/plugins/file-resume-anchor-store.js +43 -4
  290. package/dist/plugins/file-run-store.d.ts +90 -0
  291. package/dist/plugins/file-run-store.js +145 -13
  292. package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
  293. package/dist/plugins/file-snapshot-store-sql.js +178 -10
  294. package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
  295. package/dist/plugins/file-workflow-journal-store.js +10 -0
  296. package/dist/plugins/fork-routing-session-store.d.ts +47 -0
  297. package/dist/plugins/fork-routing-session-store.js +51 -3
  298. package/dist/plugins/host-platform.d.ts +69 -0
  299. package/dist/plugins/host-platform.js +81 -2
  300. package/dist/plugins/image-bake-store-sql.d.ts +136 -1
  301. package/dist/plugins/image-bake-store-sql.js +241 -7
  302. package/dist/plugins/image-index-sql.d.ts +46 -0
  303. package/dist/plugins/image-index-sql.js +113 -4
  304. package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
  305. package/dist/plugins/k8s-bg-scripts.js +88 -4
  306. package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
  307. package/dist/plugins/k8s-exec-protocol.js +41 -0
  308. package/dist/plugins/local-checkpoint-store.d.ts +40 -0
  309. package/dist/plugins/local-checkpoint-store.js +86 -7
  310. package/dist/plugins/local-session-store.d.ts +157 -0
  311. package/dist/plugins/local-session-store.js +289 -34
  312. package/dist/plugins/local-task-attachment-store.js +14 -2
  313. package/dist/plugins/mailbox-store-sql.d.ts +38 -0
  314. package/dist/plugins/mailbox-store-sql.js +39 -6
  315. package/dist/plugins/memory-engine-pg.d.ts +30 -0
  316. package/dist/plugins/memory-engine-pg.js +122 -4
  317. package/dist/plugins/memory-engine-tidb.d.ts +26 -0
  318. package/dist/plugins/memory-engine-tidb.js +115 -5
  319. package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
  320. package/dist/plugins/memory-engine-vector-util.js +5 -0
  321. package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
  322. package/dist/plugins/memory-resume-anchor-store.js +17 -0
  323. package/dist/plugins/memory-run-store.d.ts +63 -0
  324. package/dist/plugins/memory-run-store.js +73 -8
  325. package/dist/plugins/memory-session-policy-store.d.ts +17 -0
  326. package/dist/plugins/memory-session-policy-store.js +17 -0
  327. package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
  328. package/dist/plugins/memory-sync-store-pg.js +45 -2
  329. package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
  330. package/dist/plugins/memory-sync-store-tidb.js +31 -1
  331. package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
  332. package/dist/plugins/outcome-ledger-sql.js +97 -3
  333. package/dist/plugins/pg-approval-store.d.ts +7 -0
  334. package/dist/plugins/pg-approval-store.js +7 -0
  335. package/dist/plugins/pg-breaker-state.d.ts +6 -0
  336. package/dist/plugins/pg-breaker-state.js +6 -0
  337. package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
  338. package/dist/plugins/pg-checkpoint-store.js +8 -0
  339. package/dist/plugins/pg-cost-quota.d.ts +17 -0
  340. package/dist/plugins/pg-cost-quota.js +6 -0
  341. package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
  342. package/dist/plugins/pg-file-snapshot-store.js +6 -0
  343. package/dist/plugins/pg-image-bake.d.ts +9 -0
  344. package/dist/plugins/pg-image-bake.js +9 -0
  345. package/dist/plugins/pg-image-index.d.ts +9 -0
  346. package/dist/plugins/pg-image-index.js +9 -0
  347. package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
  348. package/dist/plugins/pg-outcome-ledger.js +9 -0
  349. package/dist/plugins/pg-pool.d.ts +50 -0
  350. package/dist/plugins/pg-pool.js +78 -0
  351. package/dist/plugins/pg-query.d.ts +11 -0
  352. package/dist/plugins/pg-rate-limiter.d.ts +23 -0
  353. package/dist/plugins/pg-rate-limiter.js +12 -2
  354. package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
  355. package/dist/plugins/pg-resume-anchor-store.js +5 -0
  356. package/dist/plugins/pg-run-store.d.ts +7 -0
  357. package/dist/plugins/pg-run-store.js +7 -0
  358. package/dist/plugins/pg-safe-json.d.ts +13 -0
  359. package/dist/plugins/pg-safe-json.js +40 -4
  360. package/dist/plugins/pg-session-policy-store.d.ts +5 -0
  361. package/dist/plugins/pg-session-policy-store.js +5 -0
  362. package/dist/plugins/pg-session-storage.d.ts +72 -0
  363. package/dist/plugins/pg-session-storage.js +152 -20
  364. package/dist/plugins/pg-session-store.d.ts +10 -0
  365. package/dist/plugins/pg-session-store.js +10 -0
  366. package/dist/plugins/pg-tool-result-store.d.ts +7 -0
  367. package/dist/plugins/pg-tool-result-store.js +7 -0
  368. package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
  369. package/dist/plugins/pg-workflow-journal-store.js +7 -0
  370. package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
  371. package/dist/plugins/pg-workflow-run-store.js +7 -0
  372. package/dist/plugins/posix-shell-fs.d.ts +3 -0
  373. package/dist/plugins/posix-shell-fs.js +31 -1
  374. package/dist/plugins/remote-env-adb.d.ts +25 -0
  375. package/dist/plugins/remote-env-adb.js +95 -12
  376. package/dist/plugins/remote-env-e2b.d.ts +146 -0
  377. package/dist/plugins/remote-env-e2b.js +371 -40
  378. package/dist/plugins/remote-env-file-error.d.ts +34 -0
  379. package/dist/plugins/remote-env-file-error.js +37 -0
  380. package/dist/plugins/remote-env-host.d.ts +151 -0
  381. package/dist/plugins/remote-env-host.js +468 -70
  382. package/dist/plugins/remote-env-k8s.d.ts +135 -0
  383. package/dist/plugins/remote-env-k8s.js +318 -32
  384. package/dist/plugins/remote-env-local-docker.d.ts +65 -0
  385. package/dist/plugins/remote-env-local-docker.js +149 -24
  386. package/dist/plugins/remote-env-ssh.d.ts +35 -0
  387. package/dist/plugins/remote-env-ssh.js +117 -20
  388. package/dist/plugins/remote-scratchpad.d.ts +38 -0
  389. package/dist/plugins/remote-scratchpad.js +32 -2
  390. package/dist/plugins/remote-shell.d.ts +22 -0
  391. package/dist/plugins/remote-shell.js +27 -1
  392. package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
  393. package/dist/plugins/resume-anchor-store-sql.js +11 -0
  394. package/dist/plugins/roster-store-sql.d.ts +27 -0
  395. package/dist/plugins/roster-store-sql.js +54 -0
  396. package/dist/plugins/run-store-sql.d.ts +194 -0
  397. package/dist/plugins/run-store-sql.js +210 -9
  398. package/dist/plugins/s3-presign.d.ts +20 -0
  399. package/dist/plugins/s3-presign.js +49 -2
  400. package/dist/plugins/scheduler-support.d.ts +12 -0
  401. package/dist/plugins/scheduler-support.js +75 -3
  402. package/dist/plugins/send-file-ledger.d.ts +15 -0
  403. package/dist/plugins/send-file-ledger.js +53 -4
  404. package/dist/plugins/send-user-file.d.ts +77 -0
  405. package/dist/plugins/send-user-file.js +92 -4
  406. package/dist/plugins/session-policy-store-sql.d.ts +15 -0
  407. package/dist/plugins/session-policy-store-sql.js +75 -3
  408. package/dist/plugins/session-store.d.ts +45 -0
  409. package/dist/plugins/session-store.js +51 -0
  410. package/dist/plugins/sql-driver.d.ts +77 -0
  411. package/dist/plugins/sql-driver.js +4 -0
  412. package/dist/plugins/sql-escape.d.ts +8 -0
  413. package/dist/plugins/sql-escape.js +8 -0
  414. package/dist/plugins/sql-row-helpers.d.ts +25 -0
  415. package/dist/plugins/sql-row-helpers.js +25 -0
  416. package/dist/plugins/store-backend.d.ts +103 -0
  417. package/dist/plugins/store-backend.js +120 -27
  418. package/dist/plugins/store-contracts.d.ts +62 -0
  419. package/dist/plugins/store-contracts.js +3 -0
  420. package/dist/plugins/task-attachment-store.d.ts +60 -0
  421. package/dist/plugins/task-attachment-store.js +49 -3
  422. package/dist/plugins/task-list-store-sql.d.ts +25 -0
  423. package/dist/plugins/task-list-store-sql.js +21 -0
  424. package/dist/plugins/tidb-approval-store.d.ts +6 -0
  425. package/dist/plugins/tidb-approval-store.js +6 -0
  426. package/dist/plugins/tidb-breaker-state.d.ts +5 -0
  427. package/dist/plugins/tidb-breaker-state.js +5 -0
  428. package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
  429. package/dist/plugins/tidb-checkpoint-store.js +7 -0
  430. package/dist/plugins/tidb-cost-quota.d.ts +6 -0
  431. package/dist/plugins/tidb-cost-quota.js +1 -0
  432. package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
  433. package/dist/plugins/tidb-file-snapshot-store.js +6 -0
  434. package/dist/plugins/tidb-image-bake.d.ts +8 -0
  435. package/dist/plugins/tidb-image-bake.js +8 -0
  436. package/dist/plugins/tidb-image-index.d.ts +8 -0
  437. package/dist/plugins/tidb-image-index.js +8 -0
  438. package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
  439. package/dist/plugins/tidb-outcome-ledger.js +10 -0
  440. package/dist/plugins/tidb-pool.d.ts +56 -0
  441. package/dist/plugins/tidb-pool.js +181 -1
  442. package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
  443. package/dist/plugins/tidb-rate-limiter.js +5 -2
  444. package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
  445. package/dist/plugins/tidb-resume-anchor-store.js +5 -0
  446. package/dist/plugins/tidb-run-store.d.ts +7 -0
  447. package/dist/plugins/tidb-run-store.js +7 -0
  448. package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
  449. package/dist/plugins/tidb-session-policy-store.js +5 -0
  450. package/dist/plugins/tidb-session-storage.d.ts +43 -0
  451. package/dist/plugins/tidb-session-storage.js +62 -3
  452. package/dist/plugins/tidb-session-store.d.ts +142 -0
  453. package/dist/plugins/tidb-session-store.js +264 -16
  454. package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
  455. package/dist/plugins/tidb-tool-result-store.js +7 -0
  456. package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
  457. package/dist/plugins/tidb-workflow-journal-store.js +7 -0
  458. package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
  459. package/dist/plugins/tidb-workflow-run-store.js +8 -0
  460. package/dist/plugins/tool-result-store-sql.d.ts +58 -0
  461. package/dist/plugins/tool-result-store-sql.js +38 -2
  462. package/dist/plugins/web-search.d.ts +35 -0
  463. package/dist/plugins/web-search.js +45 -9
  464. package/dist/plugins/workflow-journal-limits.d.ts +10 -0
  465. package/dist/plugins/workflow-journal-limits.js +10 -0
  466. package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
  467. package/dist/plugins/workflow-journal-store-sql.js +58 -2
  468. package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
  469. package/dist/plugins/workflow-run-store-sql.js +84 -6
  470. package/dist/plugins/worktree-isolation.d.ts +45 -0
  471. package/dist/plugins/worktree-isolation.js +105 -6
  472. package/dist/plugins/write-behind-counter.d.ts +56 -1
  473. package/dist/plugins/write-behind-counter.js +53 -14
  474. package/dist/principal-jwt.d.ts +24 -0
  475. package/dist/principal-jwt.js +60 -5
  476. package/dist/project-memory.d.ts +22 -0
  477. package/dist/project-memory.js +144 -15
  478. package/dist/prompts-domain-validate.d.ts +28 -0
  479. package/dist/prompts-domain-validate.js +42 -1
  480. package/dist/question.d.ts +33 -0
  481. package/dist/question.js +69 -2
  482. package/dist/resource-suspend.d.ts +21 -0
  483. package/dist/resource-suspend.js +16 -0
  484. package/dist/router/route-orchestration.d.ts +78 -0
  485. package/dist/router/route-orchestration.js +77 -0
  486. package/dist/run-local.d.ts +32 -0
  487. package/dist/run-local.js +172 -8
  488. package/dist/runs.d.ts +189 -4
  489. package/dist/runs.js +465 -24
  490. package/dist/runtime-caps-resolver.d.ts +106 -0
  491. package/dist/runtime-caps-resolver.js +89 -4
  492. package/dist/runtime-governance.d.ts +84 -0
  493. package/dist/runtime-governance.js +127 -1
  494. package/dist/sandbox-pkg-source.d.ts +23 -0
  495. package/dist/sandbox-pkg-source.js +37 -0
  496. package/dist/sealed-key.d.ts +54 -0
  497. package/dist/sealed-key.js +68 -3
  498. package/dist/security.d.ts +108 -0
  499. package/dist/security.js +145 -6
  500. package/dist/sema-registry.d.ts +21 -0
  501. package/dist/sema-registry.js +21 -0
  502. package/dist/session-leaf-bus.d.ts +31 -0
  503. package/dist/session-leaf-bus.js +32 -0
  504. package/dist/session-sync-content.d.ts +46 -0
  505. package/dist/session-sync-content.js +47 -1
  506. package/dist/session-sync-kernel.d.ts +89 -0
  507. package/dist/session-sync-kernel.js +48 -3
  508. package/dist/session-sync.d.ts +125 -0
  509. package/dist/session-sync.js +181 -8
  510. package/dist/session-titler.d.ts +26 -0
  511. package/dist/session-titler.js +41 -7
  512. package/dist/session-watch.d.ts +53 -1
  513. package/dist/session-watch.js +73 -9
  514. package/dist/sighup-idle.d.ts +30 -0
  515. package/dist/sighup-idle.js +12 -1
  516. package/dist/spec-fields.d.ts +91 -3
  517. package/dist/spec-fields.js +116 -4
  518. package/dist/task-cwd.d.ts +50 -0
  519. package/dist/task-cwd.js +68 -1
  520. package/dist/task-mcp.d.ts +47 -0
  521. package/dist/task-mcp.js +29 -0
  522. package/dist/task-settings.d.ts +175 -0
  523. package/dist/task-settings.js +226 -11
  524. package/dist/task-workflow.d.ts +67 -0
  525. package/dist/task-workflow.js +75 -8
  526. package/dist/tool-approval.d.ts +91 -0
  527. package/dist/tool-approval.js +215 -0
  528. package/dist/trace/artifacts.d.ts +9 -0
  529. package/dist/trace/artifacts.js +30 -5
  530. package/dist/trace/core-keyset-guard.d.ts +18 -0
  531. package/dist/trace/ledger-sink.d.ts +43 -0
  532. package/dist/trace/ledger-sink.js +64 -5
  533. package/dist/trace/project.d.ts +133 -0
  534. package/dist/trace/project.js +189 -2
  535. package/dist/trace/redact.d.ts +19 -0
  536. package/dist/trace/redact.js +66 -9
  537. package/dist/usage-analytics.d.ts +26 -0
  538. package/dist/usage-analytics.js +26 -3
  539. package/package.json +1 -1
@@ -1,8 +1,25 @@
1
+ /**
2
+ * v2 MVP M3 — merge(b), the hard bone (design/50 §7, V2-MVP-PLAN.md, search [25]/[26], council round-1).
3
+ *
4
+ * The deterministic Coordinator integrates N completed workers' branches under FULL isolation:
5
+ * 1. pull each worker's patch OUT of its container (M2 `pullDiff` — no creds go in).
6
+ * 2. provision an EPHEMERAL no-net/no-creds integration sandbox (base pre-provisioned by the dep).
7
+ * 3. `git apply --3way` each patch in sequence (partial-apply → capture `.rej`, quarantine — council#3).
8
+ * 4. run the full test suite IN the sandbox (the gate; untrusted diff never touches the control plane — [26]§16).
9
+ * 5. green → the **Coordinator** (trusted control plane, holds creds — NOT the sandbox, council#1) takes the
10
+ * integrated patch and pushes with CAS `--force-with-lease=<ref>:<baseSha>` (push race → retry — council#2).
11
+ * 6. the sandbox is destroyed on ALL exit paths (council#15).
12
+ *
13
+ * This module is the ORCHESTRATION/control-flow (deps injected → mock-tested). The real git mechanics
14
+ * (apply/rebase/test/push in a real E2B container) are validated by the M5 real-E2B joint dogfood.
15
+ */
1
16
  import type { WorkerReport } from "./fanout.js";
2
17
  import type { PullDiffResult } from "./diffout.js";
3
18
  import { type RepairTerminal } from "@sema-agent/core";
4
19
  import { type GraderHandle } from "./grader-env-factory.js";
20
+ /** A buffered command env for the integration sandbox (real E2B adapter adapts to this; mock in tests). */
5
21
  export interface IntegrationEnv {
22
+ /** Run a command; never throws — failures surface as a non-zero exitCode. */
6
23
  exec(command: string, opts?: {
7
24
  cwd?: string;
8
25
  }): Promise<{
@@ -12,8 +29,23 @@ export interface IntegrationEnv {
12
29
  }>;
13
30
  writeFile(path: string, content: string): Promise<void>;
14
31
  }
32
+ /**
33
+ * Bounded integration-repair (design: search 2026-06-14, Codex-reviewed). When the merged tree applies
34
+ * cleanly but the integration `testCmd` (the oracle gate) FAILS, the leader used to return terminal — a human
35
+ * read the build error and re-ran (the "11 runs / 6 fixes" loop was human-driven). This closes that gap: a
36
+ * bounded repair AGENT runs IN the already-merged sandbox (keeps the workers' good output + sees BOTH sides of
37
+ * a broken cross-worker contract, e.g. the ChannelShelf edge), edits the working tree (NOT commit/push), then
38
+ * mergeBranches re-runs `testCmd` to verify. Safe degradation: if repair exhausts rounds / errors, the result
39
+ * is today's `phase:"test"` failure plus repair metadata — never worse than terminal-fail. ⚠️ repair on a
40
+ * COMPILE-only `testCmd` can push green-but-incorrect code (oracle-quality, not repair, problem) — pair with a
41
+ * real hidden-oracle `measureCmd`/L3 when correctness (not just compile) must close.
42
+ */
15
43
  export interface IntegrationRepair {
44
+ /** Max repair attempts before giving up (each = one agent run + one testCmd re-run). */
16
45
  maxRounds: number;
46
+ /** Run the repair agent against the live merged tree. Edits the working tree to make `testCmd` pass; MUST
47
+ * NOT commit or push (mergeBranches squashes worker+repair edits into one commit at the end). Returns its
48
+ * spend + a short note; mergeBranches re-runs `testCmd` to decide if the fix actually worked. */
17
49
  run(ctx: {
18
50
  round: number;
19
51
  errorText: string;
@@ -24,8 +56,22 @@ export interface IntegrationRepair {
24
56
  note?: string;
25
57
  }>;
26
58
  }
59
+ /**
60
+ * Bounded merge-conflict resolution (search 2026-06-16). Symmetric with {@link IntegrationRepair}: when a worker's
61
+ * patch CONFLICTS on `git apply --3way` (overlapping edits a sibling already touched), run a bounded agent IN the
62
+ * integration sandbox to integrate THIS worker's patch — instead of the leader discarding ALL fan-out work and
63
+ * redoing the whole task as one fresh worker (collapseToSingle, leader.ts: "correctness over thrift; half-done
64
+ * adoption is future"). The agent merges the in-tree conflict markers + applies the `.rej` hunks; mergeBranches
65
+ * then VERIFIES no marker/.rej remains before continuing, and the downstream test gate still guards correctness.
66
+ * Wired by the caller (provisionIntegrationSandbox); when unset, an apply conflict stays terminal exactly as before
67
+ * (zero behaviour change). Reuses the IntegrationRepair sandbox + hands pattern → no new core/env seam (the seam
68
+ * design/68 §8 feared for "adopting N half-done workspaces" is unnecessary for the conflict case).
69
+ */
27
70
  export interface ConflictResolver {
71
+ /** Max resolution attempts before giving up (each = one agent run + one marker/.rej re-check). */
28
72
  maxRounds: number;
73
+ /** Run the resolver agent against the live conflicted tree. Edits the working tree to integrate the worker's
74
+ * patch + removes the `.rej`/`.orig` files; MUST NOT commit / push / add (the merge squashes at the end). */
29
75
  run(ctx: {
30
76
  round: number;
31
77
  workerId: string;
@@ -38,13 +84,22 @@ export interface ConflictResolver {
38
84
  }>;
39
85
  }
40
86
  export interface MergeDeps {
87
+ /** Pull one worker's patch from ITS container (M2 pullDiff bound to the worker's env). */
41
88
  pullWorkerDiff(report: WorkerReport): Promise<PullDiffResult>;
89
+ /** Provision a fresh ephemeral no-net/no-creds sandbox with the base repo at `baseSha` already in place.
90
+ * `repair` (optional) enables bounded integration-repair on a `testCmd` failure (see {@link IntegrationRepair});
91
+ * unset → a failing integration test is terminal (the pre-2026-06-14 behavior, unchanged). */
42
92
  provisionIntegrationSandbox(): Promise<{
43
93
  env: IntegrationEnv;
44
94
  destroy: () => Promise<void>;
45
95
  repair?: IntegrationRepair;
46
96
  conflictResolver?: ConflictResolver;
47
97
  }>;
98
+ /**
99
+ * The CREDENTIALED push, done by the Coordinator (control plane), NOT the sandbox (council#1). Takes the
100
+ * integrated patch (the sandbox's combined diff over base) + the base it forked from; applies to the
101
+ * Coordinator's own trusted clone and pushes `--force-with-lease=<ref>:<baseSha>`. `raced` = lease reject.
102
+ */
48
103
  push(integratedPatch: string, baseSha: string): Promise<{
49
104
  ok: true;
50
105
  ref: string;
@@ -53,16 +108,51 @@ export interface MergeDeps {
53
108
  raced?: boolean;
54
109
  error: string;
55
110
  }>;
111
+ /** Repo dir inside the sandbox (default /repo). */
56
112
  repoDir?: string;
113
+ /** Full test command run in the sandbox (the merge gate). */
57
114
  testCmd: string;
115
+ /** Optional truecorrect MEASURE (design/59): a thorough hidden-oracle command run IN the integration sandbox
116
+ * (E2B) right after the gate passes — offloads the measure from the CPU-contended control plane (the
117
+ * frontier-multi oracle-timeout fix). NOT a gate (does not affect `ok`); the result lands in MergeResult.measure
118
+ * for the caller to read as the objective truly-correct signal. Same place + env as the gate, just a thornier test. */
58
119
  measureCmd?: string;
120
+ /**
121
+ * LEADER-REPAIRLOOP-INTEGRATION §10.3 — push-hold-on-SIGNAL master flag (the wire sets it from
122
+ * `LEADER_MEASURE_GATES`, default OFF). When OFF (the default) the push path is BYTE-IDENTICAL to today: the
123
+ * `measureCmd` stays observe-only and a compile-only-green merge auto-pushes. When ON, a configured strong
124
+ * oracle that is NOT green HOLDS the push (→ `candidate_only`, no push): the hold keys on the SIGNAL, not a
125
+ * caller field — `strongOracleSeeded` (an oracleFiles-backed hidden held-out oracle is in the sandbox) OR
126
+ * (`measureCmd` present AND its measure did not pass). Auto-push only when every configured strong signal is
127
+ * GREEN; absent BOTH → legacy compile-only auto-push (§10.3). */
59
128
  measureGates?: boolean;
129
+ /**
130
+ * §10.3 — set by the wire when an oracleFiles-backed hidden held-out oracle was seeded into the integration
131
+ * sandbox (`wire.ts` `injectOracles`). Under `measureGates`, a seeded-but-only-`testCmd`-green merge is a
132
+ * strong-oracle-UNCONFIRMED result → the push is HELD (`candidate_only`). The merge module cannot see the
133
+ * oracleFiles itself (a wire concern), so the wire reports their presence here. Default unset/false → today's
134
+ * behavior. */
60
135
  strongOracleSeeded?: boolean;
136
+ /**
137
+ * §10.4 measure-drives-repair — the out-of-process measure grader (R2) + the changed-files allowlist. When set
138
+ * (and `measureGates` ON) a `measure`-FAIL re-seeds this SINGLE minted grader (`git reset --hard <integBase>` +
139
+ * `git clean -fdx`, re-apply the integrated patch) and re-runs the trusted `measureCmd` THERE — never in the
140
+ * repair-edited (possibly poisoned) merge sandbox (§10.4(i)). Before re-measuring, the integrated patch's
141
+ * changed-file list is mechanically checked against `allowlist` (the workers' planned `files` ∪ trusted spec
142
+ * dirs): an out-of-allowlist / build-config touch downgrades the candidate to `needs_human_oracle` (§10.4(ii)).
143
+ * The grader handle is bound by the wire (a re-seedable integration-equivalent sandbox). Unset → no measure
144
+ * grader; the measure stays observe-only (today's path). */
61
145
  measureDrivesRepair?: {
146
+ /** The out-of-process grader handle (re-seedable; mirrors the integration sandbox, oracle-seeded). */
62
147
  grader: GraderHandle;
148
+ /** The trusted, spec-derived measure steps run in the grader (NEVER worker-authored). */
63
149
  measureSteps: import("@sema-agent/core").ExecStep[];
150
+ /** §10.4(ii): the changed-files allowlist = the workers' planned `files` ∪ trusted spec dirs. */
64
151
  allowlist: string[];
152
+ /** Bounded measure-repair rounds (re-seed + re-measure). Each round optionally drives the existing
153
+ * integration `repair` agent first; 0/unset → grade once, no repair. */
65
154
  maxRounds?: number;
155
+ /** Parse the integrated patch into a changed-file list (the wire binds `git apply --numstat` / name-only). */
66
156
  changedFiles: (integratedPatch: string) => Promise<string[]>;
67
157
  };
68
158
  logger?: {
@@ -70,6 +160,9 @@ export interface MergeDeps {
70
160
  info?: (m: string, x?: Record<string, unknown>) => void;
71
161
  };
72
162
  }
163
+ /** Integration-repair outcome (when `MergeDeps.provisionIntegrationSandbox` supplied a `repair`): how many
164
+ * rounds ran, whether the post-repair `testCmd` finally passed, and the repair spend. `outsidePlannedFiles`
165
+ * is honest accounting — repaired files are NOT attributed to the original disjoint workers. */
73
166
  export interface RepairMeta {
74
167
  rounds: number;
75
168
  fixed: boolean;
@@ -105,5 +198,6 @@ export type MergeResult = {
105
198
  reason?: string;
106
199
  };
107
200
  };
201
+ /** Integrate completed workers' branches. Never throws — failures are typed MergeResult. */
108
202
  export declare function mergeBranches(reports: WorkerReport[], baseSha: string, deps: MergeDeps): Promise<MergeResult>;
109
203
  //# sourceMappingURL=merge.d.ts.map
@@ -1,15 +1,23 @@
1
1
  import { delimitUntrusted } from "@sema-agent/core";
2
2
  import { applyAndGrade, changedFilesOutOfAllowlist } from "./grader-env-factory.js";
3
- const Q = (s) => `'${s.replace(/'/g, "'\\''")}'`;
3
+ const Q = (s) => `'${s.replace(/'/g, "'\\''")}'`; // single-quote for shell safety
4
+ /** Integrate completed workers' branches. Never throws — failures are typed MergeResult. */
4
5
  export async function mergeBranches(reports, baseSha, deps) {
5
6
  const repo = deps.repoDir ?? "/repo";
6
7
  const mergeable = reports.filter((r) => r.status === "completed");
7
8
  if (mergeable.length === 0) {
8
9
  return { ok: false, phase: "no-workers", reason: "no completed workers to merge", quarantine: reports.map((r) => r.workerId) };
9
10
  }
11
+ // The whole pipeline is wrapped so this function honours its "never throws" contract AND never leaks the
12
+ // integration sandbox: provisioning (which connects+seeds a billable VM) and pullWorkerDiff used to run
13
+ // OUTSIDE the try, so a throw there (e.g. seed failure) escaped as an exception and — worse — left a
14
+ // connected sandbox with no `destroy` ever called. `destroy` is captured the instant provisioning resolves,
15
+ // and the finally tears it down on every path (success / typed failure / thrown error).
10
16
  let destroy;
17
+ // Track the current stage so a THROWN error is attributed to the right phase (not always "provision").
11
18
  let phase = "diff-out";
12
19
  try {
20
+ // 1. pull each worker's diff OUT of its container (M2). A failed diff quarantines that worker, aborts merge.
13
21
  const patches = [];
14
22
  for (const r of mergeable) {
15
23
  const d = await deps.pullWorkerDiff(r);
@@ -18,24 +26,35 @@ export async function mergeBranches(reports, baseSha, deps) {
18
26
  }
19
27
  patches.push({ workerId: r.workerId, patch: d.patch });
20
28
  }
29
+ // 2. ephemeral no-creds integration sandbox (destroyed in finally — council#15).
21
30
  phase = "provision";
22
31
  const integ = await deps.provisionIntegrationSandbox();
23
32
  destroy = integ.destroy;
24
33
  const { env } = integ;
34
+ // The sandbox's base commit ≠ the durable remote's `baseSha` (different repos). Capture the sandbox's
35
+ // pre-apply HEAD so `format-patch <integBase>..HEAD` captures the integration commit even though the
36
+ // commit advances the sandbox's branch (dogfood-surfaced: `main..HEAD` was empty). `baseSha` is used
37
+ // ONLY for the durable push CAS below.
25
38
  const integBase = (await env.exec("git rev-parse HEAD", { cwd: repo })).stdout.trim();
39
+ // 3. apply each patch --3way (council#3: keeps conflict context; capture .rej on failure, quarantine).
26
40
  phase = "apply";
27
41
  const conflictsResolved = [];
28
- let conflictResolverCostUsd = 0;
42
+ let conflictResolverCostUsd = 0; // 🔴 capture the bounded conflict-resolver agent's spend (was dropped → S1 TEAM-cost undercount)
29
43
  for (const p of patches) {
30
44
  const patchPath = `/tmp/${p.workerId}.patch`;
31
45
  await env.writeFile(patchPath, p.patch);
32
46
  const ap = await env.exec(`git apply --3way --whitespace=nowarn ${Q(patchPath)}`, { cwd: repo });
33
47
  if (ap.exitCode !== 0) {
34
48
  const rej = await env.exec(`find . -name '*.rej' -print -exec cat {} +`, { cwd: repo }).catch(() => ({ stdout: "", stderr: "", exitCode: -1 }));
49
+ // Bounded conflict-resolution (search 2026-06-16): a --3way conflict left markers/.rej in the tree. Instead
50
+ // of discarding ALL fan-out work + redoing single (leader collapseToSingle), run a bounded agent IN this
51
+ // sandbox to integrate THIS worker's patch (siblings already applied clean), gated by a marker/.rej re-check
52
+ // and the downstream test gate. Symmetric with integration-repair; OFF unless a conflictResolver is wired;
53
+ // degrades to the original apply-failure (collapse) on any leftover/agent-error — never worse than baseline.
35
54
  const cr = integ.conflictResolver && integ.conflictResolver.maxRounds > 0
36
55
  ? await resolveApplyConflict(env, repo, p, rej.stdout, ap.stderr, integ.conflictResolver, deps)
37
56
  : { resolved: false, costUsd: 0 };
38
- conflictResolverCostUsd += cr.costUsd;
57
+ conflictResolverCostUsd += cr.costUsd; // count the spend even when resolution failed (tokens were spent)
39
58
  if (!cr.resolved) {
40
59
  deps.logger?.warn?.("merge_apply_failed", { workerId: p.workerId, exitCode: ap.exitCode });
41
60
  return { ok: false, phase: "apply", reason: `git apply --3way failed for ${p.workerId} (conflict)`, quarantine: [p.workerId], rej: rej.stdout, details: ap.stderr.slice(0, 800), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
@@ -43,8 +62,15 @@ export async function mergeBranches(reports, baseSha, deps) {
43
62
  conflictsResolved.push(p.workerId);
44
63
  }
45
64
  }
65
+ // 4. full test suite IN the sandbox = the merge gate (untrusted diff stays sandboxed — [26]§16).
66
+ // testCmd is a caller-provided COMMAND LINE (e.g. "cd x && pytest"), run verbatim by design — NOT a single
67
+ // arg, so it is intentionally not Q()-quoted (council round-2 noted the asymmetry). Blast radius = the
68
+ // ephemeral no-creds sandbox; the caller owns the command (trusted control-plane config).
46
69
  phase = "test";
47
70
  let test = await env.exec(deps.testCmd, { cwd: repo });
71
+ // Bounded integration-repair (search 2026-06-14): a clean-merge-but-failing-build used to be terminal.
72
+ // If the sandbox supplied a `repair`, run the repair agent IN this merged tree (it edits the working tree;
73
+ // the squash commit below captures worker patches + repair together), then re-run testCmd. Up to maxRounds.
48
74
  let repairMeta;
49
75
  if (test.exitCode !== 0 && integ.repair && integ.repair.maxRounds > 0) {
50
76
  let cost = 0;
@@ -58,7 +84,7 @@ export async function mergeBranches(reports, baseSha, deps) {
58
84
  }
59
85
  catch (e) {
60
86
  deps.logger?.warn?.("integration_repair_error", { round: round + 1, err: e instanceof Error ? e.message : String(e) });
61
- break;
87
+ break; // a thrown repair agent → stop repairing; fall through to the (still-failing) terminal return
62
88
  }
63
89
  test = await env.exec(deps.testCmd, { cwd: repo });
64
90
  }
@@ -68,14 +94,29 @@ export async function mergeBranches(reports, baseSha, deps) {
68
94
  if (test.exitCode !== 0) {
69
95
  return { ok: false, phase: "test", reason: `integration test failed (exit ${test.exitCode})`, details: (test.stdout + "\n" + test.stderr).slice(-2000), ...(repairMeta ? { repair: repairMeta } : {}), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
70
96
  }
97
+ // 4b. truecorrect MEASURE (design/59) — run the thorough hidden oracle IN this sandbox (E2B), where the merged
98
+ // code already lives, NOT on the CPU-contended control plane (fixes the frontier-multi oracle spawn-timeout
99
+ // that thinned n). Pure observation: never gates `ok`, just reports into MergeResult.measure.
71
100
  let measure;
72
101
  if (deps.measureCmd) {
73
102
  const mr = await env.exec(deps.measureCmd, { cwd: repo });
74
103
  measure = mr.exitCode === 0 ? { pass: true } : { pass: false, reason: (mr.stdout + " " + mr.stderr).slice(-200) };
75
104
  }
105
+ // 5. produce the integrated patch over base, hand to the Coordinator's credentialed push (council#1).
106
+ // Check the commit's exit code: if `git add -A`/commit fails, HEAD doesn't advance and `format-patch`
107
+ // still exits 0 with EMPTY output — pushing an empty patch would no-op or corrupt the merge silently.
76
108
  phase = "push";
109
+ // If integration-repair ran, a repair agent may have created commits despite the instruction not to. Flatten
110
+ // any such commits back to the working tree (Codex review #2) so the single `commit` below squashes worker
111
+ // patches + repair edits into ONE integration commit — otherwise format-patch would emit the repair commit
112
+ // PLUS an empty leader commit, and the Coordinator's `git am` can choke on the empty patch. No-op when no
113
+ // repair commits exist (HEAD already == integBase).
77
114
  if (repairMeta)
78
115
  await env.exec(`git reset --soft ${Q(integBase)}`, { cwd: repo });
116
+ // Cost legs that MUST ride EVERY push-phase failure return (wq64gmm5e HIGH: TEAM C1 undercount). A
117
+ // conflictResolver and/or integration-repair may already have spent real tokens BEFORE any push-phase
118
+ // failure; dropping these on the commit/format-patch/empty/race/push-fail returns made captureTeamC1
119
+ // undercount the arm. Mirror the success/hold/test-fail returns exactly.
79
120
  const costTail = { ...(repairMeta ? { repair: repairMeta } : {}), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
80
121
  const commit = await env.exec(`git add -A && git -c user.email=leader@local -c user.name=leader commit -m ${Q(`merge: integrate ${mergeable.map((r) => r.workerId).join("+")}`)} --allow-empty`, { cwd: repo });
81
122
  if (commit.exitCode !== 0) {
@@ -88,9 +129,17 @@ export async function mergeBranches(reports, baseSha, deps) {
88
129
  if (!integrated.stdout.trim()) {
89
130
  return { ok: false, phase: "push", reason: "integrated patch is empty (nothing applied over base) — refusing to push", quarantine: mergeable.map((r) => r.workerId), ...costTail };
90
131
  }
132
+ // ── §10.6 THE PUSH CHOKEPOINT + §10.3 push-hold-on-signal + §10.4 measure-drives-repair ──
133
+ // A single gate covering EVERY push path by construction (collapse `leader.ts:451` + N=1 `leader.ts:508`):
134
+ // the credentialed push fires ONLY when (a) no mergeable report carries a non-auto-accepting
135
+ // `repairTerminal` (§10.6 — the SAFE-tier `candidate_only` that the status collapse to "completed" would
136
+ // otherwise hide), AND (b) under `LEADER_MEASURE_GATES`, every configured strong oracle signal is GREEN
137
+ // (§10.3). When any of these HOLDS, the push is withheld and the held, DELIMITED candidate is surfaced for
138
+ // human acceptance (a candidate is never a silent push and never a silent drop — §5 invariant). With
139
+ // `measureGates` OFF and no report carrying a `repairTerminal`, this whole block is inert → byte-identical.
91
140
  const evald = await evaluatePushHold(env, repo, mergeable, measure, integrated.stdout, deps);
92
141
  if (evald.measure !== undefined)
93
- measure = evald.measure;
142
+ measure = evald.measure; // §10.4: the out-of-process re-measure supersedes the observe-only one
94
143
  if (evald.hold) {
95
144
  const h = evald.hold;
96
145
  deps.logger?.warn?.("merge_push_held", { terminal: h.terminal, reason: h.reason.slice(0, 200) });
@@ -113,51 +162,103 @@ export async function mergeBranches(reports, baseSha, deps) {
113
162
  return { ok: true, ref: push.ref, merged: mergeable.map((r) => r.workerId), ...(measure ? { measure } : {}), ...(repairMeta ? { repair: repairMeta } : {}), ...(conflictsResolved.length > 0 ? { conflictsResolved } : {}), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
114
163
  }
115
164
  catch (e) {
165
+ // Honour the "never throws" contract: a thrown pullWorkerDiff/provision/exec becomes a typed failure,
166
+ // attributed to the stage it threw in (not a blanket "provision").
116
167
  return { ok: false, phase, reason: `merge aborted in ${phase}: ${e instanceof Error ? e.message : String(e)}`, quarantine: mergeable.map((r) => r.workerId) };
117
168
  }
118
169
  finally {
170
+ // council#15: destroy the sandbox on EVERY exit path. Guarded because we may bail (diff-out) or throw
171
+ // BEFORE provisioning — `destroy` is only set once provisionIntegrationSandbox has resolved.
119
172
  if (destroy)
120
173
  await destroy().catch(() => { });
121
174
  }
122
175
  }
176
+ /**
177
+ * §10.6 + §10.3 + §10.4 — the push chokepoint decision (pure-ish: reads the reports + the optional measure grader;
178
+ * never throws — a grader error falls back to a HOLD, never a silent push). Returns `{ hold }` to WITHHOLD the
179
+ * credentialed push, surfacing the held candidate; `{}` to allow it. Three independent hold triggers:
180
+ *
181
+ * §10.6 — any mergeable report carries a `repairTerminal ∉ {undefined, "fixed"}`. A SAFE-tier `candidate_only`
182
+ * maps to core verdict "PASS" → the report status collapses to "completed", so WITHOUT this gate the
183
+ * merge would push `testCmd`-green-but-strong-oracle-UNCONFIRMED work. This fires regardless of
184
+ * `measureGates` (the single-agent repair loop is itself OFF-by-default behind LEADER_REPAIR_LOOP — once
185
+ * a report carries a repairTerminal, the operator opted in; the candidate MUST be held). Covers the
186
+ * collapse-to-single and N=1 leader paths by construction.
187
+ *
188
+ * §10.3 — under `measureGates` ONLY: a configured STRONG oracle that is not GREEN holds the push. The hold keys
189
+ * on the SIGNAL not a caller field: (`strongOracleSeeded`) OR (`measureCmd` present AND the measure did
190
+ * not pass). Absent BOTH strong signals → no §10.3 hold (legacy compile-only auto-push). With
191
+ * `measureGates` OFF this trigger is inert (today's observe-only measure).
192
+ *
193
+ * §10.4 — under `measureGates` AND `measureDrivesRepair`: re-seed the SINGLE out-of-process grader, mechanically
194
+ * check the integrated patch's changed files against the allowlist (out-of-allowlist / build-config →
195
+ * downgrade to `needs_human_oracle`), then re-measure THERE (never the repair-poisoned merge sandbox).
196
+ * A still-failing re-measure holds (`candidate_only`); a passing re-measure clears the §10.3 measure
197
+ * signal (so the §10.3 trigger no longer holds on it).
198
+ */
123
199
  async function evaluatePushHold(env, repo, mergeable, observeMeasure, integratedPatch, deps) {
200
+ // §10.7 — the integrated patch is worker-authored code reaching a human-read HTTP body / durable-approval row.
124
201
  const heldCandidate = () => delimitUntrusted("CANDIDATE PATCH (untrusted worker code)", integratedPatch);
202
+ // §10.6 — a non-auto-accepting single-agent repair terminal rode in on a report (status collapsed to
203
+ // "completed"). The strongest non-fixed terminal wins the label (it's the WHY surfaced to the human).
125
204
  const carried = mergeable.map((r) => r.repairTerminal).filter((t) => t !== undefined && t !== "fixed");
126
205
  if (carried.length > 0) {
127
- const terminal = carried[0];
206
+ const terminal = carried[0]; // every mergeable solo report shares one repairTerminal (N=1 / collapse-solo)
128
207
  return { hold: { reason: `single-agent repair terminal '${terminal}' — strong oracle unconfirmed, push held for human acceptance (§10.6)`, terminal, candidatePatch: heldCandidate() } };
129
208
  }
209
+ // §10.3/§10.4 are OFF unless the master measure-gates flag is on → byte-identical default path.
130
210
  if (!deps.measureGates)
131
211
  return {};
132
212
  let measure = observeMeasure;
213
+ // §10.4 measure-drives-repair: re-measure the integrated patch in a FRESH-seeded out-of-process grader, with the
214
+ // mechanical changed-files allowlist guard. Runs only when configured AND there is a measure to re-confirm.
133
215
  if (deps.measureDrivesRepair && deps.measureCmd) {
134
216
  const mdr = deps.measureDrivesRepair;
135
217
  try {
218
+ // §10.4(ii) — mechanical allowlist FIRST (a fresh sandbox is necessary-not-sufficient: poison can ride
219
+ // INSIDE the integrated patch). An out-of-allowlist / build-config touch → downgrade to needs_human_oracle.
136
220
  const changed = await mdr.changedFiles(integratedPatch);
137
221
  const offending = changedFilesOutOfAllowlist(changed, mdr.allowlist);
138
222
  if (offending.length > 0) {
139
223
  deps.logger?.warn?.("merge_measure_allowlist_denied", { offending: offending.slice(0, 20) });
140
224
  return { hold: { reason: `integrated patch touches out-of-allowlist / build-config paths (${offending.slice(0, 5).join(", ")}) — needs_human_oracle (§10.4(ii))`, terminal: "needs_human_oracle", candidatePatch: heldCandidate() }, measure };
141
225
  }
226
+ // Re-seed the SINGLE grader + re-measure THERE (never the repair-edited merge sandbox — §10.4(i)). The
227
+ // existing in-sandbox `repair` agent already ran in `mergeBranches`; the grader re-measure is the
228
+ // authoritative correctness signal. (Bounded measure-repair rounds beyond the existing integration-repair
229
+ // are a future enable; v1 re-measures once.)
142
230
  const grade = await applyAndGrade(mdr.grader, integratedPatch, mdr.measureSteps);
143
231
  measure = grade.passed ? { pass: true } : { pass: false, reason: (grade.trace ?? "measure failed in out-of-process grader").slice(-200) };
144
232
  deps.logger?.info?.("merge_measure_grader", { passed: grade.passed, applied: grade.applied });
145
233
  }
146
234
  catch (e) {
235
+ // Fail-CLOSED (never a silent push): a grader transport/exec error holds the push as needs_human_oracle.
147
236
  deps.logger?.warn?.("merge_measure_grader_error", { err: e instanceof Error ? e.message : String(e) });
237
+ // §10.7 — surfaced reason is a trusted constant; the untrusted error detail is in the warn log above.
148
238
  return { hold: { reason: `measure grader errored — needs_human_oracle (§10.4)`, terminal: "needs_human_oracle", candidatePatch: heldCandidate() }, measure };
149
239
  }
150
240
  }
241
+ // §10.3 — push-hold on the SIGNAL. Auto-push only when every configured strong signal is GREEN.
151
242
  const measureFailing = deps.measureCmd !== undefined && measure?.pass !== true;
152
- const strongOracleUnconfirmed = deps.strongOracleSeeded === true;
243
+ const strongOracleUnconfirmed = deps.strongOracleSeeded === true; // a hidden held-out oracle was seeded but only testCmd cleared
153
244
  if (measureFailing || strongOracleUnconfirmed) {
245
+ // §10.7 — the surfaced `reason` reaches a human-read HTTP body / durable-approval row, so it is built from
246
+ // TRUSTED constants ONLY; the untrusted measure output tail (`measure.reason`) stays in the logs, NEVER
247
+ // interpolated into a human-/LLM-reachable string.
154
248
  const why = measureFailing ? "measure did not pass" : "hidden held-out oracle seeded but only the compile gate cleared";
155
249
  deps.logger?.warn?.("merge_push_held_signal", { why, measureReason: (measure?.reason ?? "").slice(0, 200) });
156
250
  return { hold: { reason: `strong oracle unconfirmed: ${why} — candidate_only, push held (§10.3)`, terminal: "candidate_only", candidatePatch: heldCandidate() }, measure };
157
251
  }
158
252
  return { measure };
159
253
  }
254
+ /** Bounded resolution of a `git apply --3way` conflict (search 2026-06-16). The failed apply left conflict markers
255
+ * in-tree and/or `.rej` files. Run the resolver agent up to maxRounds, then VERIFY the tree is conflict-free (no
256
+ * `<<<<<<<`/`>>>>>>>` markers, no `*.rej`). Returns true iff clean → the patch is treated as applied and the merge
257
+ * continues, KEEPING every other worker's clean work. Any agent error / leftover marker → false → the caller falls
258
+ * back to today's apply-failure (collapse-to-single). Never worse than the baseline. */
160
259
  async function resolveApplyConflict(env, repo, p, rejText, applyStderr, resolver, deps) {
260
+ // 🔴 Accumulate + RETURN the resolver's spend (it used to return only a boolean → the cost was dropped, the S1
261
+ // TEAM-cost-undercount bug). Counts every round, including the round that throws (the tokens were spent).
161
262
  let costUsd = 0;
162
263
  const conflictText = `git apply --3way could not cleanly apply worker '${p.workerId}'.\n--- apply stderr ---\n${applyStderr.slice(0, 1500)}\n--- reject hunks (*.rej) ---\n${rejText.slice(0, 3000)}`;
163
264
  for (let round = 0; round < resolver.maxRounds; round++) {
@@ -168,8 +269,10 @@ async function resolveApplyConflict(env, repo, p, rejText, applyStderr, resolver
168
269
  }
169
270
  catch (e) {
170
271
  deps.logger?.warn?.("merge_conflict_repair_error", { workerId: p.workerId, err: e instanceof Error ? e.message : String(e) });
171
- return { resolved: false, costUsd };
272
+ return { resolved: false, costUsd }; // a thrown resolver → give up, fall back to collapse
172
273
  }
274
+ // Resolved iff NO conflict markers and NO .rej files remain under the repo. `grep -rIlE` LEADS the command so a
275
+ // mock/impl can distinguish this verify from the `.rej` CAPTURE in mergeBranches; non-empty stdout = still dirty.
173
276
  const check = await env
174
277
  .exec(`grep -rIlE '^(<<<<<<<|>>>>>>>)' . 2>/dev/null; find . -name '*.rej' 2>/dev/null | head -20`, { cwd: repo })
175
278
  .catch(() => ({ exitCode: -1, stdout: "DIRTY", stderr: "" }));
@@ -179,6 +282,6 @@ async function resolveApplyConflict(env, repo, p, rejText, applyStderr, resolver
179
282
  return { resolved: true, costUsd };
180
283
  }
181
284
  }
182
- return { resolved: false, costUsd };
285
+ return { resolved: false, costUsd }; // still conflicted after maxRounds
183
286
  }
184
287
  //# sourceMappingURL=merge.js.map
@@ -1,23 +1,75 @@
1
+ /**
2
+ * v2 MVP — LLM Planner (design/50 §3: leader = LLM-planner that splits a DAG + deterministic Coordinator).
3
+ * Decomposes an objective into N **disjoint-file** sub-tasks via one model completion, then VALIDATES the plan
4
+ * deterministically (the trust boundary: an LLM proposes, the Coordinator enforces — service[19] leaderless-
5
+ * leader). Disjoint file-ownership is the invariant that lets workers run in parallel + merge without conflict.
6
+ *
7
+ * The model call is injected (`runPlan: prompt → text`) so this is pure + mock-tested; wire.ts binds it to a
8
+ * real Runner completion. Invalid/overlapping plans throw a clear error → the leader returns plan-failed
9
+ * (never a half-valid fan-out).
10
+ */
1
11
  import type { SubtaskSpec } from "./leader.js";
2
12
  export interface PlanOptions {
13
+ /** Max sub-tasks the planner may emit (guards a runaway fan-out). Default 6. */
3
14
  maxSubtasks?: number;
15
+ /** Extra repo/context guidance appended to the prompt (e.g. file layout, conventions). */
4
16
  context?: string;
17
+ /** Deployment knob (design/68 §11 A6, correctness-first): `false` = never fan out — `routePlan` skips the
18
+ * router model call entirely and returns the deterministic single route (zero routing cost, zero risk of a
19
+ * false fan-out). Default (undefined/true) = the router decides. */
5
20
  fanoutEnabled?: boolean;
21
+ /** Economic guard threshold (design/68 §7, search[83] ruling): an objective SHORTER than this never reaches
22
+ * the router — deterministic single (a small task can't be economically decomposable; false-single is the
23
+ * cheap error side). Default 64 — deliberately below the route corpus's smallest fan-out case. */
6
24
  minFanoutChars?: number;
7
25
  }
26
+ /**
27
+ * Deterministic economic 防灾 guard (design/68 §7 / §11 D2, search [83] ruling (b)): the asymmetry is that a
28
+ * false fan-out is the -98% disaster while a false single merely runs slower — so every case decidable WITHOUT
29
+ * the model resolves to single, for free. Pure function (unit-testable); the semantic judgments (sequential/
30
+ * coupled/uncertain) stay with the router LLM, and "估不准回 single" is the collapse fallback (B3).
31
+ */
8
32
  export declare function economicGuard(objective: string, opts?: PlanOptions): {
9
33
  single: true;
10
34
  reason: string;
11
35
  } | {
12
36
  single: false;
13
37
  };
38
+ /** The deterministic SINGLE route: one sub-task owning the whole objective (the same shape the router emits
39
+ * for its single decision — N=1 runs through the identical leader pipeline). */
14
40
  export declare function singleRoute(objective: string): SubtaskSpec[];
41
+ /** The planning prompt: asks for a strict JSON array of disjoint-file sub-tasks. */
15
42
  export declare function planPrompt(objective: string, opts?: PlanOptions): string;
43
+ /** Parse + VALIDATE a planner completion into SubtaskSpec[]. Throws on any violation (disjointness etc.). */
16
44
  export declare function parsePlan(raw: string, opts?: PlanOptions): SubtaskSpec[];
45
+ /**
46
+ * Deterministically validate an array of sub-tasks (the trust boundary — see file header). Used both for the
47
+ * LLM planner output AND for caller-supplied `body.subtasks`, which would otherwise skip every invariant: the
48
+ * `branch` is interpolated into a worker-container shell command (`git checkout -b <branch>`), so an
49
+ * unvalidated branch (`x; curl …|sh`, `` `reboot` ``, `--upload-pack=…`) would inject; and the disjoint-file /
50
+ * unique-id invariants are what the Coordinator's parallel-merge depends on. Throws on any violation.
51
+ */
17
52
  export declare function validateSubtasks(arr: unknown, opts?: PlanOptions): SubtaskSpec[];
53
+ /** Run the planner: one model completion → validated disjoint-file sub-tasks. */
18
54
  export declare function llmPlan(objective: string, runPlan: (prompt: string) => Promise<string>, opts?: PlanOptions): Promise<SubtaskSpec[]>;
55
+ /**
56
+ * The DIFFICULTY-ROUTER prompt (search [43], planner-light). Same disjoint-subtask contract as `planPrompt`,
57
+ * but the model FIRST routes: default to a SINGLE agent (one sub-task owning the whole task) and only fan out
58
+ * when the objective is CLEARLY + ECONOMICALLY decomposable (disjoint files, independent/non-sequential, each
59
+ * part buildable without most of the same context). The campaign proved forcing fan-out on a sequential/coupled
60
+ * task is a -98% disaster, so the bias is firmly toward single. The output is still a plain disjoint-file
61
+ * sub-task array — a 1-element array IS the single route (the leader runs N=1 through the same pipeline).
62
+ */
19
63
  export declare function routePrompt(objective: string, opts?: PlanOptions): string;
64
+ /** Run the difficulty router: one model completion → a routed plan (1 sub-task = single route, N = fan-out),
65
+ * validated by `parsePlan` (the same disjointness/shape invariants the Coordinator depends on). */
20
66
  export declare function routePlan(objective: string, runRoute: (prompt: string) => Promise<string>, opts?: PlanOptions): Promise<SubtaskSpec[]>;
67
+ /**
68
+ * Decomposer quality gate (design/68 §5/§11 B1-B3): run the router, and on a rejected/unparseable plan retry
69
+ * the LLM AT MOST ONCE, then COLLAPSE TO SINGLE (never fail the task on a bad plan — single is almost always
70
+ * the right fallback, and a false fan-out is the -98% disaster; a plan failure must not be louder than that).
71
+ * `collapsed` carries the reason (observability: a collapse is a router-quality signal, not silent).
72
+ */
21
73
  export declare function routePlanWithFallback(objective: string, runRoute: (prompt: string) => Promise<string>, opts?: PlanOptions): Promise<{
22
74
  subs: SubtaskSpec[];
23
75
  collapsed?: string;
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Deterministic economic 防灾 guard (design/68 §7 / §11 D2, search [83] ruling (b)): the asymmetry is that a
3
+ * false fan-out is the -98% disaster while a false single merely runs slower — so every case decidable WITHOUT
4
+ * the model resolves to single, for free. Pure function (unit-testable); the semantic judgments (sequential/
5
+ * coupled/uncertain) stay with the router LLM, and "估不准回 single" is the collapse fallback (B3).
6
+ */
1
7
  export function economicGuard(objective, opts = {}) {
2
8
  if (opts.fanoutEnabled === false)
3
9
  return { single: true, reason: "fan-out disabled by deployment (A6)" };
@@ -8,9 +14,12 @@ export function economicGuard(objective, opts = {}) {
8
14
  }
9
15
  return { single: false };
10
16
  }
17
+ /** The deterministic SINGLE route: one sub-task owning the whole objective (the same shape the router emits
18
+ * for its single decision — N=1 runs through the identical leader pipeline). */
11
19
  export function singleRoute(objective) {
12
20
  return [{ workerId: "main", branch: "worker/main", files: ["."], spec: { objective } }];
13
21
  }
22
+ /** The planning prompt: asks for a strict JSON array of disjoint-file sub-tasks. */
14
23
  export function planPrompt(objective, opts = {}) {
15
24
  const max = opts.maxSubtasks ?? 6;
16
25
  return [
@@ -24,6 +33,7 @@ export function planPrompt(objective, opts = {}) {
24
33
  `Rules: workerId unique + url-safe; files non-empty + disjoint across ALL sub-tasks; objective self-contained.`,
25
34
  ].filter(Boolean).join(" ");
26
35
  }
36
+ /** Extract a JSON array from a model completion (tolerates ```json fences / surrounding prose). */
27
37
  function extractJsonArray(raw) {
28
38
  const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
29
39
  const candidate = (fenced ? fenced[1] : raw).trim();
@@ -38,11 +48,20 @@ function extractJsonArray(raw) {
38
48
  throw new Error("planner output is not valid JSON");
39
49
  }
40
50
  }
51
+ /** Parse + VALIDATE a planner completion into SubtaskSpec[]. Throws on any violation (disjointness etc.). */
41
52
  export function parsePlan(raw, opts = {}) {
42
53
  return validateSubtasks(extractJsonArray(raw), opts);
43
54
  }
55
+ /**
56
+ * Deterministically validate an array of sub-tasks (the trust boundary — see file header). Used both for the
57
+ * LLM planner output AND for caller-supplied `body.subtasks`, which would otherwise skip every invariant: the
58
+ * `branch` is interpolated into a worker-container shell command (`git checkout -b <branch>`), so an
59
+ * unvalidated branch (`x; curl …|sh`, `` `reboot` ``, `--upload-pack=…`) would inject; and the disjoint-file /
60
+ * unique-id invariants are what the Coordinator's parallel-merge depends on. Throws on any violation.
61
+ */
44
62
  export function validateSubtasks(arr, opts = {}) {
45
63
  const max = opts.maxSubtasks ?? 6;
64
+ // "plan" not "planner" — this also validates caller-supplied body.subtasks, not just LLM output.
46
65
  if (!Array.isArray(arr) || arr.length === 0)
47
66
  throw new Error("plan must be a non-empty array of sub-tasks");
48
67
  if (arr.length > max)
@@ -70,6 +89,8 @@ export function validateSubtasks(arr, opts = {}) {
70
89
  seenFiles.add(norm);
71
90
  }
72
91
  const branch = typeof e.branch === "string" && e.branch.trim() ? e.branch.trim() : `worker/${workerId}`;
92
+ // Reject a leading "-" (would be parsed as an option by `git checkout -b <branch>`), ".." (ref escape), and
93
+ // an over-long ref (git rejects it anyway; cap keeps the value tidy). The char set is shell-safe.
73
94
  if (branch.length > 200 || !/^[A-Za-z0-9._/-]+$/.test(branch) || branch.startsWith("-") || branch.includes("..")) {
74
95
  throw new Error(`sub-task ${workerId}: invalid branch`);
75
96
  }
@@ -78,10 +99,19 @@ export function validateSubtasks(arr, opts = {}) {
78
99
  }
79
100
  return subs;
80
101
  }
102
+ /** Run the planner: one model completion → validated disjoint-file sub-tasks. */
81
103
  export async function llmPlan(objective, runPlan, opts = {}) {
82
104
  const raw = await runPlan(planPrompt(objective, opts));
83
105
  return parsePlan(raw, opts);
84
106
  }
107
+ /**
108
+ * The DIFFICULTY-ROUTER prompt (search [43], planner-light). Same disjoint-subtask contract as `planPrompt`,
109
+ * but the model FIRST routes: default to a SINGLE agent (one sub-task owning the whole task) and only fan out
110
+ * when the objective is CLEARLY + ECONOMICALLY decomposable (disjoint files, independent/non-sequential, each
111
+ * part buildable without most of the same context). The campaign proved forcing fan-out on a sequential/coupled
112
+ * task is a -98% disaster, so the bias is firmly toward single. The output is still a plain disjoint-file
113
+ * sub-task array — a 1-element array IS the single route (the leader runs N=1 through the same pipeline).
114
+ */
85
115
  export function routePrompt(objective, opts = {}) {
86
116
  const max = opts.maxSubtasks ?? 6;
87
117
  return [
@@ -104,25 +134,39 @@ export function routePrompt(objective, opts = {}) {
104
134
  `Rules: always a JSON ARRAY (SINGLE = one element, not a bare object); workerId unique + url-safe; files non-empty + DISJOINT across ALL elements; each objective self-contained.`,
105
135
  ].filter(Boolean).join(" ");
106
136
  }
137
+ /** Run the difficulty router: one model completion → a routed plan (1 sub-task = single route, N = fan-out),
138
+ * validated by `parsePlan` (the same disjointness/shape invariants the Coordinator depends on). */
107
139
  export async function routePlan(objective, runRoute, opts = {}) {
108
140
  if (opts.fanoutEnabled === false)
109
- return singleRoute(objective);
141
+ return singleRoute(objective); // A6: deploy-disabled fan-out → no router call
110
142
  const raw = await runRoute(routePrompt(objective, opts));
111
143
  return parsePlan(raw, opts);
112
144
  }
145
+ /**
146
+ * Decomposer quality gate (design/68 §5/§11 B1-B3): run the router, and on a rejected/unparseable plan retry
147
+ * the LLM AT MOST ONCE, then COLLAPSE TO SINGLE (never fail the task on a bad plan — single is almost always
148
+ * the right fallback, and a false fan-out is the -98% disaster; a plan failure must not be louder than that).
149
+ * `collapsed` carries the reason (observability: a collapse is a router-quality signal, not silent).
150
+ */
113
151
  export async function routePlanWithFallback(objective, runRoute, opts = {}) {
114
- const guard = economicGuard(objective, opts);
152
+ const guard = economicGuard(objective, opts); // deterministic 防灾经济 cases never pay for a router call
115
153
  if (guard.single)
116
154
  return { subs: singleRoute(objective) };
117
155
  let firstErr;
118
- let firstRaw = "";
156
+ let firstRaw = ""; // capture the raw completion of the FIRST failed attempt for diagnosis (see below)
119
157
  for (let attempt = 0; attempt < 2; attempt++) {
158
+ // §5: no more than ONE LLM re-plan
120
159
  const raw = await runRoute(routePrompt(objective, opts));
121
160
  try {
122
161
  return { subs: parsePlan(raw, opts) };
123
162
  }
124
163
  catch (e) {
125
164
  firstErr ??= e;
165
+ // Capturing the raw output on collapse is load-bearing for diagnosis: a collapse logs only "not valid
166
+ // JSON", and the router runs on an in-memory (non-durable) session, so the raw completion is otherwise
167
+ // GONE — task#2d's flaky collapse could not be root-caused because nothing recorded the bad output. Keep
168
+ // a bounded head+tail so the next collapse is diagnosable (truncated/unterminated vs unescaped-quote vs
169
+ // prose) without dumping a 32k-token runaway in full.
126
170
  if (!firstRaw)
127
171
  firstRaw = raw.length > 600 ? `${raw.slice(0, 300)}…[${raw.length}ch]…${raw.slice(-150)}` : raw;
128
172
  }