@iowarp/clio-coder 0.3.1 → 0.3.3

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 (601) hide show
  1. package/CHANGELOG.md +233 -373
  2. package/CONTRIBUTING.md +23 -23
  3. package/README.md +284 -613
  4. package/dist/{acp-FPR54DGL.js → acp-P2AQILE2.js} +43 -53
  5. package/dist/{agents-OGPIHPJH.js → agents-72W3BI7I.js} +43 -26
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-IC3K6NIZ.js → auth-5TWEIYDN.js} +20 -12
  8. package/dist/chunk-2DJ2KNFG.js +2095 -0
  9. package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
  10. package/dist/chunk-2SFS6XQE.js +122 -0
  11. package/dist/{chunk-PV4JUBVJ.js → chunk-2TLUCQVG.js} +40 -21
  12. package/dist/chunk-2VTFPG5O.js +48 -0
  13. package/dist/chunk-4BJ5BYCE.js +61 -0
  14. package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
  15. package/dist/chunk-4VP4KH3K.js +962 -0
  16. package/dist/chunk-4XUGQOHA.js +797 -0
  17. package/dist/chunk-4ZG3XFUR.js +77 -0
  18. package/dist/chunk-5B2AEOW5.js +5407 -0
  19. package/dist/{chunk-K2ITRMHZ.js → chunk-5TSRNF4G.js} +6 -138
  20. package/dist/{chunk-4QKXUHSR.js → chunk-5UFT4SUX.js} +70 -20
  21. package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
  22. package/dist/chunk-65DEGPJ6.js +52 -0
  23. package/dist/chunk-6EJMN2Y3.js +17 -0
  24. package/dist/chunk-6N5PTWMY.js +136 -0
  25. package/dist/chunk-6SGHMWE3.js +277 -0
  26. package/dist/chunk-6XLNIQDB.js +27 -0
  27. package/dist/chunk-7CR24IG7.js +242 -0
  28. package/dist/chunk-7MNJORFF.js +22 -0
  29. package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
  30. package/dist/chunk-AGYYIBLL.js +1069 -0
  31. package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
  32. package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
  33. package/dist/chunk-CBCAPZAA.js +229 -0
  34. package/dist/chunk-CMZWFGD2.js +352 -0
  35. package/dist/chunk-COU2UHX6.js +400 -0
  36. package/dist/chunk-DSELYM6W.js +1077 -0
  37. package/dist/chunk-DUYJ5IO6.js +644 -0
  38. package/dist/chunk-ECH6PKUQ.js +39 -0
  39. package/dist/chunk-ED4KHGC3.js +143 -0
  40. package/dist/chunk-EKMEHE4H.js +340 -0
  41. package/dist/chunk-FCSXB6T2.js +338 -0
  42. package/dist/chunk-FJ3H4MN5.js +48 -0
  43. package/dist/{chunk-RPTR2H26.js → chunk-FNTMWMX5.js} +21 -15
  44. package/dist/chunk-FQ4SKYE4.js +29 -0
  45. package/dist/chunk-G4BMMOKF.js +182 -0
  46. package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
  47. package/dist/chunk-HC4CLZ2Y.js +68 -0
  48. package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
  49. package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
  50. package/dist/chunk-I4HZDVNP.js +73 -0
  51. package/dist/chunk-IKCO5N3L.js +162 -0
  52. package/dist/chunk-IR4CFBFN.js +56 -0
  53. package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
  54. package/dist/chunk-J7CWMCQD.js +255 -0
  55. package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
  56. package/dist/chunk-KZWTDYJF.js +217 -0
  57. package/dist/chunk-LBMZMYH2.js +285 -0
  58. package/dist/{chunk-G34LV2PF.js → chunk-LM5TQCJZ.js} +84 -170
  59. package/dist/chunk-LW6DSM3M.js +5135 -0
  60. package/dist/chunk-LWLEKMDQ.js +3482 -0
  61. package/dist/{chunk-H6F6BYOH.js → chunk-LZSJBIVT.js} +7003 -7434
  62. package/dist/{chunk-HQQID6OA.js → chunk-M6SHUN7Q.js} +5 -5
  63. package/dist/{chunk-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
  64. package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
  65. package/dist/chunk-OAO4GE4M.js +619 -0
  66. package/dist/{chunk-Q3RUPKEJ.js → chunk-OC7FIQPC.js} +58 -189
  67. package/dist/chunk-OKGUZO2U.js +34 -0
  68. package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
  69. package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
  70. package/dist/chunk-OQE5J4C6.js +73 -0
  71. package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
  72. package/dist/{chunk-MAR7Y6HW.js → chunk-PAJK6MAQ.js} +23 -16
  73. package/dist/{chunk-M5T5VO65.js → chunk-PIWWS5BL.js} +837 -635
  74. package/dist/chunk-POHLU5DW.js +1186 -0
  75. package/dist/chunk-QKMUKYO7.js +4961 -0
  76. package/dist/chunk-SRDMMSEP.js +16405 -0
  77. package/dist/chunk-SST6Z5JA.js +80 -0
  78. package/dist/chunk-STBPMHSX.js +2456 -0
  79. package/dist/chunk-T6YILFSB.js +80 -0
  80. package/dist/chunk-TZK7PACC.js +174 -0
  81. package/dist/chunk-TZTZS7QK.js +227 -0
  82. package/dist/{chunk-ASND7OZK.js → chunk-UFIIWP2H.js} +13 -13
  83. package/dist/chunk-UOV2BYIW.js +107 -0
  84. package/dist/{chunk-PFEFKVGL.js → chunk-V6RTAOC2.js} +13 -11
  85. package/dist/chunk-VAKQQHWR.js +434 -0
  86. package/dist/chunk-VG7TBQIY.js +128 -0
  87. package/dist/chunk-VJWL6YS5.js +244 -0
  88. package/dist/{chunk-EYOKLTMF.js → chunk-VPAYEGVX.js} +17 -3
  89. package/dist/chunk-WEH5XRJQ.js +32 -0
  90. package/dist/chunk-X4RCMKVQ.js +641 -0
  91. package/dist/{chunk-TEKV33Q5.js → chunk-X6IAEBZR.js} +65 -33
  92. package/dist/chunk-XBXAASKX.js +18 -0
  93. package/dist/chunk-XN3L4EYL.js +46 -0
  94. package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
  95. package/dist/chunk-YHZX5GEU.js +193 -0
  96. package/dist/chunk-YXLYO42X.js +91 -0
  97. package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
  98. package/dist/chunk-ZI647VB5.js +37 -0
  99. package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
  100. package/dist/chunk-ZWMF7253.js +1882 -0
  101. package/dist/cli/index.js +62 -54
  102. package/dist/clio-JOU4FXVA.js +25 -0
  103. package/dist/code-nav-7AX6FYE6.js +600 -0
  104. package/dist/codewiki/build-worker.js +66 -0
  105. package/dist/compile-cache-CVJMMODC.js +18 -0
  106. package/dist/{components-DMAOEKFB.js → components-KELWS457.js} +11 -6
  107. package/dist/{config-IRUQ7SE4.js → config-XCDVKR23.js} +92 -55
  108. package/dist/configure-4GAP54ZW.js +42 -0
  109. package/dist/{context-5RADCKTR.js → context-4UOGGLQ5.js} +71 -35
  110. package/dist/context-5VKGUVJJ.js +866 -0
  111. package/dist/{context-3KWFLHJG.js → context-77FM5DV5.js} +15 -13
  112. package/dist/{context-clear-7TSNPAAI.js → context-clear-XXJRLCJJ.js} +54 -28
  113. package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
  114. package/dist/dispatch-runner-QPRDDBDX.js +1997 -0
  115. package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
  116. package/dist/{doctor-UC5NAJYQ.js → doctor-HR46URBJ.js} +27 -17
  117. package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
  118. package/dist/{evidence-YEGUW4L3.js → evidence-6HG2PY2B.js} +46 -26
  119. package/dist/{evolve-TXARCTPG.js → evolve-K7YU3NCY.js} +45 -25
  120. package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
  121. package/dist/{fleet-6G3DHNYE.js → fleet-VY3HHKN6.js} +163 -54
  122. package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
  123. package/dist/{init-KZ5QTF6M.js → init-JYGXI3FK.js} +69 -32
  124. package/dist/{memory-73ESV5YC.js → memory-WFZMGYHX.js} +48 -27
  125. package/dist/{models-A4PVNWJK.js → models-I5QWSEOM.js} +39 -25
  126. package/dist/monitor-GE4ID3IA.js +661 -0
  127. package/dist/{chunk-FCIH3BIZ.js → orchestrator-EM5MC3HM.js} +15979 -12407
  128. package/dist/{paths-C4H6IV77.js → paths-UXLN5YYZ.js} +10 -5
  129. package/dist/{preload-6WVMHX3A.js → preload-P6DGH2PZ.js} +2 -2
  130. package/dist/{reset-BGW6OGMV.js → reset-L2FQEE3E.js} +16 -10
  131. package/dist/{run-YTPEYQOH.js → run-ZU3QMZPZ.js} +101 -61
  132. package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
  133. package/dist/{skills-2V6RA3OQ.js → skills-X5VXCRNQ.js} +34 -14
  134. package/dist/{skills-eval-S2TVJO4F.js → skills-eval-WKIHWTHR.js} +70 -34
  135. package/dist/steer-GGWFUJUD.js +77 -0
  136. package/dist/{targets-TYXLPB23.js → targets-SNCPI2NR.js} +43 -27
  137. package/dist/terminal-lease-BNAHVHBS.js +395 -0
  138. package/dist/{trace-GGOJ6Q6Z.js → trace-PNCASAXC.js} +41 -16
  139. package/dist/{chunk-N6F52NLF.js → tree-sitter-HGKH6LG4.js} +28 -2306
  140. package/dist/{uninstall-LLLT4F4W.js → uninstall-FZCQCDKC.js} +10 -5
  141. package/dist/{upgrade-33G2LMM5.js → upgrade-JQHHPQ4K.js} +45 -25
  142. package/dist/{usage-ZAFSXKKG.js → usage-OR4O5SMZ.js} +62 -31
  143. package/dist/verify-375KUB3Y.js +716 -0
  144. package/dist/web-fetch-2YHJ3KTG.js +638 -0
  145. package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-UEXP2ARI.js} +74 -34
  146. package/dist/worker/entry.js +221 -36
  147. package/dist/workspace-G4ZWUIPR.js +22 -0
  148. package/docs/README.md +22 -17
  149. package/docs/acp.md +168 -16
  150. package/docs/alcf-provider.md +1 -1
  151. package/docs/architecture.md +136 -7
  152. package/docs/artifact-versions.md +1 -1
  153. package/docs/built-in-agents.md +1 -1
  154. package/docs/capacity-and-scheduling.md +1 -1
  155. package/docs/commands-and-modes.md +114 -71
  156. package/docs/config-knobs-audit.md +1 -3
  157. package/docs/configuration-and-targets.md +174 -46
  158. package/docs/context-engine.md +29 -6
  159. package/docs/development-pipeline.md +26 -1
  160. package/docs/dispatch-architecture-rationale.md +1 -1
  161. package/docs/documentation-coverage.md +2 -2
  162. package/docs/documentation-guide.md +1 -1
  163. package/docs/environment-variables.md +13 -5
  164. package/docs/eval-runner.md +1 -1
  165. package/docs/evals-internal.md +1 -1
  166. package/docs/evidence-and-memory.md +6 -2
  167. package/docs/evolution.md +2 -2
  168. package/docs/exit-codes-and-output.md +15 -9
  169. package/docs/extensions-and-sharing.md +9 -9
  170. package/docs/fleet-dispatch.md +7 -5
  171. package/docs/git-commit-provenance.md +120 -0
  172. package/docs/glossary.md +1 -1
  173. package/docs/installation-and-lifecycle.md +34 -27
  174. package/docs/middleware-and-components.md +1 -1
  175. package/docs/model-catalog.md +45 -14
  176. package/docs/observability.md +8 -5
  177. package/docs/performance-methodology.md +491 -0
  178. package/docs/pi-boundary.md +72 -0
  179. package/docs/proactive-memory.md +3 -3
  180. package/docs/prompt-envelope-and-tools.md +24 -3
  181. package/docs/provider-adapter-cookbook.md +57 -4
  182. package/docs/release-cut-checklist.md +129 -115
  183. package/docs/safety-model.md +9 -5
  184. package/docs/scientific-validation.md +3 -3
  185. package/docs/session-lifecycle.md +55 -12
  186. package/docs/skills-marketplace.md +12 -8
  187. package/docs/time-conventions.md +1 -1
  188. package/docs/tool-usage.md +3 -3
  189. package/docs/trace-store.md +1 -1
  190. package/docs/troubleshooting.md +10 -7
  191. package/docs/tui-design.md +47 -10
  192. package/docs/worker-dispatch-mechanics.md +1 -1
  193. package/package.json +19 -22
  194. package/skills/coding/ast-grep/SKILL.md +136 -0
  195. package/skills/coding/ast-grep/evals.md +56 -0
  196. package/skills/coding/ast-grep/references/rule_reference.md +297 -0
  197. package/skills/coding/coding-standards/SKILL.md +113 -0
  198. package/skills/coding/coding-standards/evals.md +34 -0
  199. package/skills/coding/prototype/SKILL.md +86 -0
  200. package/skills/coding/prototype/evals.md +42 -0
  201. package/skills/coding/prototype/references/LOGIC.md +67 -0
  202. package/skills/coding/prototype/references/UI.md +112 -0
  203. package/skills/coding/tdd/SKILL.md +101 -0
  204. package/skills/coding/tdd/evals.md +41 -0
  205. package/skills/coding/tdd/references/mocking.md +59 -0
  206. package/skills/coding/tdd/references/tests.md +77 -0
  207. package/skills/context/context-handoff/SKILL.md +126 -0
  208. package/skills/context/context-handoff/evals.md +57 -0
  209. package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
  210. package/skills/context/context-prime/SKILL.md +95 -0
  211. package/skills/context/context-prime/evals.md +54 -0
  212. package/skills/meta/clio-dev/SKILL.md +91 -0
  213. package/skills/meta/clio-dev/evals.md +45 -0
  214. package/skills/meta/clio-test/SKILL.md +130 -0
  215. package/skills/meta/clio-test/evals.md +43 -0
  216. package/skills/meta/clio-test/references/harness.md +97 -0
  217. package/skills/meta/clio-test/references/test-map.md +59 -0
  218. package/skills/meta/credentials/SKILL.md +125 -0
  219. package/skills/meta/credentials/evals.md +104 -0
  220. package/skills/meta/find-skills/SKILL.md +72 -0
  221. package/skills/meta/find-skills/evals.md +47 -0
  222. package/skills/meta/herdr/SKILL.md +127 -0
  223. package/skills/meta/herdr/evals.md +38 -0
  224. package/skills/meta/skill-craft/SKILL.md +102 -0
  225. package/skills/meta/skill-craft/evals.md +41 -0
  226. package/skills/planning/architecture/SKILL.md +129 -0
  227. package/skills/planning/architecture/evals.md +36 -0
  228. package/skills/planning/backlog/SKILL.md +90 -0
  229. package/skills/planning/backlog/evals.md +43 -0
  230. package/skills/planning/prd/SKILL.md +82 -0
  231. package/skills/planning/prd/evals.md +49 -0
  232. package/skills/planning/product-intent/SKILL.md +112 -0
  233. package/skills/planning/product-intent/evals.md +36 -0
  234. package/skills/planning/tech-spec/SKILL.md +115 -0
  235. package/skills/planning/tech-spec/evals.md +47 -0
  236. package/skills/registry.yaml +136 -0
  237. package/skills/research/arxiv-literature/SKILL.md +104 -0
  238. package/skills/research/arxiv-literature/evals.md +58 -0
  239. package/skills/research/experiment-protocol/SKILL.md +122 -0
  240. package/skills/research/experiment-protocol/evals.md +91 -0
  241. package/skills/research/scientific-debugging/SKILL.md +119 -0
  242. package/skills/research/scientific-debugging/evals.md +138 -0
  243. package/skills/research/scientific-modernization/SKILL.md +138 -0
  244. package/skills/research/scientific-modernization/evals.md +84 -0
  245. package/skills/workflow/design-council/SKILL.md +139 -0
  246. package/skills/workflow/design-council/evals.md +97 -0
  247. package/skills/workflow/grill-me/SKILL.md +186 -0
  248. package/skills/workflow/grill-me/evals.md +78 -0
  249. package/skills/workflow/workflow-distiller/SKILL.md +136 -0
  250. package/skills/workflow/workflow-distiller/evals.md +107 -0
  251. package/src/cli/acp.ts +31 -4
  252. package/src/cli/clio.ts +68 -6
  253. package/src/cli/config-inspect.ts +28 -22
  254. package/src/cli/configure.ts +47 -9
  255. package/src/cli/context-clear.ts +2 -2
  256. package/src/cli/context-index.ts +21 -23
  257. package/src/cli/context.ts +13 -8
  258. package/src/cli/default-target.ts +9 -17
  259. package/src/cli/docs.ts +11 -5
  260. package/src/cli/evidence.ts +4 -1
  261. package/src/cli/extensions.ts +10 -1
  262. package/src/cli/fleet.ts +47 -6
  263. package/src/cli/index.ts +55 -26
  264. package/src/cli/memory.ts +3 -1
  265. package/src/cli/models.ts +1 -1
  266. package/src/cli/modes/json-stream.ts +37 -1
  267. package/src/cli/modes/print.ts +24 -9
  268. package/src/cli/run.ts +2 -2
  269. package/src/cli/skills-eval.ts +23 -8
  270. package/src/cli/skills.ts +19 -4
  271. package/src/cli/targets.ts +4 -0
  272. package/src/cli/text-layout.ts +15 -5
  273. package/src/cli/trace.ts +62 -14
  274. package/src/cli/upgrade.ts +18 -2
  275. package/src/cli/usage.ts +10 -3
  276. package/src/cli/wiki-generate.ts +2 -1
  277. package/src/core/agent-environment.ts +7 -0
  278. package/src/core/bash-exec.ts +72 -1
  279. package/src/core/boot-trace.ts +9 -4
  280. package/src/core/bus-events.ts +20 -4
  281. package/src/core/commit-attribution.ts +157 -0
  282. package/src/core/compile-cache.ts +159 -0
  283. package/src/core/config.ts +131 -2
  284. package/src/core/defaults.ts +39 -5
  285. package/src/core/domain-loader.ts +12 -5
  286. package/src/core/git-commit-attribution.ts +387 -0
  287. package/src/core/incomplete-installation.ts +45 -0
  288. package/src/core/response-schema.ts +1 -1
  289. package/src/core/safe-exec.ts +13 -1
  290. package/src/core/settings-layers.ts +155 -21
  291. package/src/core/skill-activation.ts +1 -1
  292. package/src/core/startup-timer.ts +3 -3
  293. package/src/core/state-file-lock.ts +13 -1
  294. package/src/core/termination.ts +78 -5
  295. package/src/domains/config/classify.ts +15 -3
  296. package/src/domains/config/extension.ts +19 -13
  297. package/src/domains/config/index.ts +10 -0
  298. package/src/domains/config/keybindings.ts +45 -9
  299. package/src/domains/context/bootstrap-prompt.ts +1 -1
  300. package/src/domains/context/bootstrap.ts +111 -18
  301. package/src/domains/context/clear.ts +16 -11
  302. package/src/domains/context/clio-md.ts +111 -9
  303. package/src/domains/context/codewiki/artifact.ts +400 -0
  304. package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
  305. package/src/domains/context/codewiki/build-worker.ts +54 -0
  306. package/src/domains/context/codewiki/coordinator.ts +182 -0
  307. package/src/domains/context/codewiki/indexer.ts +59 -144
  308. package/src/domains/context/codewiki/paths.ts +67 -0
  309. package/src/domains/context/codewiki/schema.ts +80 -0
  310. package/src/domains/context/codewiki/tree-sitter.ts +1 -1
  311. package/src/domains/context/contract.ts +11 -5
  312. package/src/domains/context/extension.ts +94 -143
  313. package/src/domains/context/fingerprint.ts +3 -1
  314. package/src/domains/context/index.ts +12 -22
  315. package/src/domains/context/project-metadata.ts +19 -0
  316. package/src/domains/context/prompt-context.ts +9 -10
  317. package/src/domains/context/refresh.ts +29 -21
  318. package/src/domains/context/runtime.ts +17 -0
  319. package/src/domains/context/wiki/generate.ts +39 -34
  320. package/src/domains/context/wiki/plan.ts +1 -1
  321. package/src/domains/context/wiki/prompts.ts +21 -8
  322. package/src/domains/dispatch/code-step.ts +20 -1
  323. package/src/domains/dispatch/extension.ts +158 -21
  324. package/src/domains/dispatch/failure-classification.ts +6 -0
  325. package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
  326. package/src/domains/dispatch/orphan-recovery.ts +50 -8
  327. package/src/domains/dispatch/receipt-integrity.ts +5 -0
  328. package/src/domains/dispatch/state.ts +31 -6
  329. package/src/domains/dispatch/transport.ts +2 -1
  330. package/src/domains/dispatch/types.ts +14 -0
  331. package/src/domains/dispatch/worker-spawn.ts +21 -2
  332. package/src/domains/eval/metrics/context.ts +1 -1
  333. package/src/domains/eval/types.ts +0 -1
  334. package/src/domains/evidence/build.ts +41 -1
  335. package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
  336. package/src/domains/lifecycle/migrations/index.ts +24 -4
  337. package/src/domains/middleware/hooks-io.ts +12 -0
  338. package/src/domains/middleware/skills-reminder.ts +30 -15
  339. package/src/domains/prompts/compiler.ts +142 -84
  340. package/src/domains/prompts/contract.ts +18 -2
  341. package/src/domains/prompts/extension.ts +39 -7
  342. package/src/domains/prompts/fragment-loader.ts +0 -1
  343. package/src/domains/prompts/fragments/identity/clio.md +2 -4
  344. package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
  345. package/src/domains/prompts/fragments/identity/self-awareness.md +1 -45
  346. package/src/domains/prompts/fragments/operating/contract.md +4 -50
  347. package/src/domains/prompts/fragments/operating/delegation.md +42 -0
  348. package/src/domains/prompts/fragments/operating/skills.md +26 -0
  349. package/src/domains/prompts/fragments/operating/worker.md +16 -0
  350. package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
  351. package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
  352. package/src/domains/prompts/fragments/safety/read-only.md +4 -4
  353. package/src/domains/prompts/fragments/safety/suggest.md +2 -2
  354. package/src/domains/prompts/fragments/wiki/page.md +10 -0
  355. package/src/domains/prompts/fragments/wiki/plan.md +10 -0
  356. package/src/domains/prompts/preload.ts +3 -3
  357. package/src/domains/providers/auth/api-key.ts +1 -1
  358. package/src/domains/providers/auth/backend-file.ts +20 -10
  359. package/src/domains/providers/auth/backend-memory.ts +59 -4
  360. package/src/domains/providers/auth/boot-status.ts +65 -0
  361. package/src/domains/providers/auth/oauth.ts +2 -1
  362. package/src/domains/providers/auth/storage.ts +97 -38
  363. package/src/domains/providers/capabilities.ts +12 -4
  364. package/src/domains/providers/contract.ts +15 -4
  365. package/src/domains/providers/extension.ts +18 -6
  366. package/src/domains/providers/model-runtime-capabilities.ts +15 -4
  367. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +118 -35
  368. package/src/domains/providers/plugins.ts +5 -3
  369. package/src/domains/providers/probe/fingerprint.ts +25 -5
  370. package/src/domains/providers/registry.ts +31 -10
  371. package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
  372. package/src/domains/providers/runtimes/builtins.ts +2 -2
  373. package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
  374. package/src/domains/providers/runtimes/common/local-synth.ts +6 -7
  375. package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
  376. package/src/domains/providers/support.ts +6 -3
  377. package/src/domains/providers/types/local-model-quirks.ts +7 -9
  378. package/src/domains/providers/types/runtime-descriptor.ts +12 -1
  379. package/src/domains/providers/types/target-descriptor.ts +22 -0
  380. package/src/domains/resources/contract.ts +0 -1
  381. package/src/domains/resources/extension.ts +1 -3
  382. package/src/domains/resources/loader.ts +3 -4
  383. package/src/domains/resources/prompts/loader.ts +16 -2
  384. package/src/domains/resources/prompts/substitute.ts +1 -65
  385. package/src/domains/resources/skills/content-hash.ts +2 -0
  386. package/src/domains/resources/skills/install.ts +17 -0
  387. package/src/domains/resources/skills/loader.ts +17 -10
  388. package/src/domains/resources/skills/marketplace.ts +55 -9
  389. package/src/domains/safety/action-classifier.ts +4 -2
  390. package/src/domains/safety/audit.ts +8 -2
  391. package/src/domains/safety/extension.ts +1 -1
  392. package/src/domains/session/compaction/branch-summary.ts +3 -2
  393. package/src/domains/session/compaction/cut-point.ts +2 -1
  394. package/src/domains/session/compaction/tokens.ts +2 -1
  395. package/src/domains/session/context-ledger.ts +14 -0
  396. package/src/domains/session/contract.ts +15 -0
  397. package/src/domains/session/decision-board.ts +190 -0
  398. package/src/domains/session/entries.ts +66 -3
  399. package/src/domains/session/extension.ts +93 -12
  400. package/src/domains/session/retry.ts +10 -18
  401. package/src/domains/session/session-artifacts.ts +107 -0
  402. package/src/domains/session/task-board.ts +207 -13
  403. package/src/domains/session/tree/active-path.ts +44 -5
  404. package/src/domains/session/tree/fork.ts +26 -27
  405. package/src/domains/session/tree/preview.ts +2 -2
  406. package/src/domains/session/workspace/git-probe.ts +17 -11
  407. package/src/domains/user-tasks/store.ts +297 -0
  408. package/src/engine/acp/errors.ts +96 -0
  409. package/src/engine/acp/server.ts +1728 -146
  410. package/src/engine/acp/transport.ts +135 -14
  411. package/src/engine/acp/types.ts +26 -0
  412. package/src/engine/agent.ts +3 -3
  413. package/src/engine/ai.ts +32 -27
  414. package/src/engine/alcf-oauth.ts +26 -19
  415. package/src/engine/api-registry.ts +223 -0
  416. package/src/engine/apis/index.ts +3 -7
  417. package/src/engine/apis/llamacpp-residency.ts +49 -9
  418. package/src/engine/apis/lmstudio-residency.ts +5 -21
  419. package/src/engine/apis/lmstudio.ts +243 -0
  420. package/src/engine/apis/ollama-native.ts +24 -3
  421. package/src/engine/apis/openai-completions.ts +170 -91
  422. package/src/engine/apis/residency.ts +139 -3
  423. package/src/engine/apis/types.ts +16 -0
  424. package/src/engine/env-api-keys.ts +98 -0
  425. package/src/engine/gemma-channel-filter.ts +223 -0
  426. package/src/engine/instrumented-tui.ts +192 -0
  427. package/src/engine/messages.ts +14 -0
  428. package/src/engine/models.ts +42 -0
  429. package/src/engine/oauth.ts +16 -12
  430. package/src/engine/prompt-templates.ts +1 -0
  431. package/src/engine/provider-payload.ts +16 -59
  432. package/src/engine/strip-tokenizer-sentinels.ts +1 -1
  433. package/src/engine/truncate.ts +9 -0
  434. package/src/engine/tui.ts +17 -9
  435. package/src/engine/types.ts +3 -6
  436. package/src/engine/worker-runtime-capabilities.ts +5 -0
  437. package/src/engine/worker-runtime.ts +1 -1
  438. package/src/engine/worker-tools.ts +9 -4
  439. package/src/entry/boot-options.ts +50 -0
  440. package/src/entry/orchestrator.ts +288 -150
  441. package/src/interactive/application-controller.ts +89 -2
  442. package/src/interactive/chat-loop.ts +266 -41
  443. package/src/interactive/chat-panel.ts +715 -278
  444. package/src/interactive/chat-renderer.ts +306 -85
  445. package/src/interactive/clio-editor.ts +3 -8
  446. package/src/interactive/command-fallbacks.ts +2 -2
  447. package/src/interactive/context-overlay.ts +27 -1
  448. package/src/interactive/editor-submit.ts +253 -24
  449. package/src/interactive/export-html/ansi-to-html.ts +161 -0
  450. package/src/interactive/export-html/index.ts +51 -0
  451. package/src/interactive/export-html/template.ts +45 -0
  452. package/src/interactive/export-html/tool-renderer.ts +54 -0
  453. package/src/interactive/footer/dashboard.ts +4 -0
  454. package/src/interactive/footer/notifications.ts +1 -1
  455. package/src/interactive/footer/widgets.ts +42 -22
  456. package/src/interactive/footer-panel.ts +8 -3
  457. package/src/interactive/format-time.ts +14 -2
  458. package/src/interactive/interactive-application.ts +203 -17
  459. package/src/interactive/interactive-event-projection.ts +18 -1
  460. package/src/interactive/interactive-input-runtime.ts +50 -4
  461. package/src/interactive/interactive-presentation.ts +151 -19
  462. package/src/interactive/interactive-shell.ts +268 -14
  463. package/src/interactive/interactive-slash-runtime.ts +184 -117
  464. package/src/interactive/interactive-tickers.ts +38 -7
  465. package/src/interactive/keybinding-manager.ts +1 -1
  466. package/src/interactive/layout.ts +40 -3
  467. package/src/interactive/overlay-frame.ts +1 -1
  468. package/src/interactive/overlay-general-openers.ts +58 -1
  469. package/src/interactive/overlay-key-routing.ts +3 -0
  470. package/src/interactive/overlay-lifecycle.ts +13 -0
  471. package/src/interactive/overlay-permission-lifecycle.ts +2 -1
  472. package/src/interactive/overlay-session-lifecycle.ts +69 -12
  473. package/src/interactive/overlays/ask-user.ts +146 -24
  474. package/src/interactive/overlays/decisions.ts +300 -0
  475. package/src/interactive/overlays/help-reference.ts +15 -10
  476. package/src/interactive/overlays/model-selector.ts +34 -16
  477. package/src/interactive/overlays/session-selector.ts +18 -0
  478. package/src/interactive/overlays/settings.ts +105 -17
  479. package/src/interactive/overlays/skills-hub.ts +4 -4
  480. package/src/interactive/overlays/tree-selector.ts +41 -6
  481. package/src/interactive/render-trace.ts +499 -90
  482. package/src/interactive/renderers/compaction-summary.ts +2 -2
  483. package/src/interactive/renderers/diff.ts +115 -104
  484. package/src/interactive/renderers/mermaid.ts +53 -0
  485. package/src/interactive/renderers/tool-execution.ts +516 -168
  486. package/src/interactive/renderers/worker-entry.ts +20 -4
  487. package/src/interactive/session-switch-settlement.ts +10 -0
  488. package/src/interactive/slash-autocomplete.ts +6 -114
  489. package/src/interactive/slash-commands.ts +135 -47
  490. package/src/interactive/slash-spec.ts +9 -38
  491. package/src/interactive/status/controller.ts +5 -1
  492. package/src/interactive/status/index.ts +12 -1
  493. package/src/interactive/status/reasoning.ts +87 -0
  494. package/src/interactive/status/summary.ts +13 -2
  495. package/src/interactive/stdout-backpressure.ts +99 -0
  496. package/src/interactive/stream-pacer.ts +530 -0
  497. package/src/interactive/stream-pacing-policy.ts +66 -0
  498. package/src/interactive/tasks-overlay.ts +368 -14
  499. package/src/interactive/terminal-lease.ts +485 -0
  500. package/src/interactive/theme/tokens.ts +1 -1
  501. package/src/interactive/transcript-detail.ts +120 -0
  502. package/src/interactive/turn-context.ts +4 -3
  503. package/src/interactive/turn-persistence.ts +30 -13
  504. package/src/interactive/turn-queues.ts +12 -0
  505. package/src/interactive/turn-recovery.ts +25 -8
  506. package/src/interactive/turn-runtime.ts +79 -12
  507. package/src/interactive/turn-state.ts +10 -0
  508. package/src/interactive/view/artifacts.ts +114 -4
  509. package/src/interactive/view/view-overlay.ts +3 -0
  510. package/src/interactive/welcome-dashboard.ts +17 -16
  511. package/src/interactive/worker-receipts.ts +52 -3
  512. package/src/interactive/worker-stream.ts +5 -1
  513. package/src/tools/agent-tools.ts +23 -3
  514. package/src/tools/artifact.ts +2 -2
  515. package/src/tools/ask-user.ts +23 -13
  516. package/src/tools/bash.ts +30 -2
  517. package/src/tools/bootstrap.ts +34 -431
  518. package/src/tools/builtin-tool-catalog.ts +271 -0
  519. package/src/tools/codewiki/code-nav-surface.ts +29 -0
  520. package/src/tools/codewiki/code-nav.ts +8 -22
  521. package/src/tools/codewiki/shared.ts +41 -38
  522. package/src/tools/context/docs-engine.ts +14 -3
  523. package/src/tools/context/index.ts +107 -28
  524. package/src/tools/context/surface.ts +19 -0
  525. package/src/tools/core-bootstrap.ts +168 -0
  526. package/src/tools/credential-present.ts +5 -5
  527. package/src/tools/dispatch-admission.ts +533 -0
  528. package/src/tools/dispatch-background.ts +54 -0
  529. package/src/tools/dispatch-event-text.ts +6 -0
  530. package/src/tools/dispatch-plan.ts +9 -4
  531. package/src/tools/dispatch-run-events.ts +238 -0
  532. package/src/tools/dispatch-runner.ts +2370 -0
  533. package/src/tools/dispatch-scout-admission.ts +295 -0
  534. package/src/tools/dispatch-types.ts +77 -0
  535. package/src/tools/dispatch.ts +67 -3161
  536. package/src/tools/find.ts +4 -2
  537. package/src/tools/grep.ts +2 -2
  538. package/src/tools/lazy-tool.ts +60 -0
  539. package/src/tools/ledger.ts +3 -3
  540. package/src/tools/monitor-surface.ts +36 -0
  541. package/src/tools/monitor.ts +2 -32
  542. package/src/tools/observers.ts +2 -2
  543. package/src/tools/presentation.ts +107 -0
  544. package/src/tools/registry.ts +45 -27
  545. package/src/tools/safe-exec.ts +2 -2
  546. package/src/tools/steer-surface.ts +17 -0
  547. package/src/tools/steer.ts +2 -13
  548. package/src/tools/tasks.ts +108 -11
  549. package/src/tools/truncate.ts +25 -184
  550. package/src/tools/verify/frontend.ts +3 -1
  551. package/src/tools/verify/index.ts +3 -38
  552. package/src/tools/verify/surface.ts +46 -0
  553. package/src/tools/web-fetch-surface.ts +23 -0
  554. package/src/tools/web-fetch.ts +2 -20
  555. package/src/tools/write.ts +7 -2
  556. package/src/worker/entry.ts +39 -2
  557. package/src/worker/spec-contract.ts +26 -5
  558. package/dist/chunk-7SS2CTV2.js +0 -61361
  559. package/dist/chunk-DKGKUHFA.js +0 -924
  560. package/dist/chunk-GEP36Y4X.js +0 -12796
  561. package/dist/chunk-XYWBQRDM.js +0 -137
  562. package/dist/clio-BZVGEUFJ.js +0 -58
  563. package/dist/configure-S7S6F6CL.js +0 -32
  564. package/docs/html/agents_blueprint.html +0 -936
  565. package/docs/html/alcf_blueprint.html +0 -324
  566. package/docs/html/architecture_blueprint.html +0 -850
  567. package/docs/html/commands_blueprint.html +0 -939
  568. package/docs/html/config_knobs_audit_blueprint.html +0 -178
  569. package/docs/html/configuration_blueprint.html +0 -1080
  570. package/docs/html/context_blueprint.html +0 -603
  571. package/docs/html/documentation_blueprint.html +0 -832
  572. package/docs/html/environment_blueprint.html +0 -404
  573. package/docs/html/eval_blueprint.html +0 -743
  574. package/docs/html/evals_internal_blueprint.html +0 -190
  575. package/docs/html/evolution_blueprint.html +0 -674
  576. package/docs/html/extensions_blueprint.html +0 -2065
  577. package/docs/html/fleet_dispatch_blueprint.html +0 -286
  578. package/docs/html/index.html +0 -919
  579. package/docs/html/lifecycle_blueprint.html +0 -723
  580. package/docs/html/memory_blueprint.html +0 -699
  581. package/docs/html/middleware_blueprint.html +0 -664
  582. package/docs/html/models_blueprint.html +0 -2366
  583. package/docs/html/observability_blueprint.html +0 -683
  584. package/docs/html/provider_adapter_blueprint.html +0 -245
  585. package/docs/html/safety_blueprint.html +0 -1386
  586. package/docs/html/shared.css +0 -571
  587. package/docs/html/shared.js +0 -143
  588. package/docs/html/skills_blueprint.html +0 -671
  589. package/docs/html/soak_blueprint.html +0 -182
  590. package/docs/html/tool_usage_blueprint.html +0 -350
  591. package/docs/html/tools_blueprint.html +0 -2249
  592. package/docs/html/trace_blueprint.html +0 -235
  593. package/docs/html/tui_design_blueprint.html +0 -374
  594. package/docs/html/validation_blueprint.html +0 -961
  595. package/docs/html/worker_dispatch_blueprint.html +0 -231
  596. package/src/core/release.ts +0 -2
  597. package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
  598. package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
  599. package/src/engine/apis/lmstudio-native.ts +0 -1438
  600. package/src/engine/apis/thinking-replay.ts +0 -11
  601. package/src/tools/string-enum.ts +0 -15
@@ -0,0 +1,1997 @@
1
+ import { createRequire as __clioCreateRequire } from "node:module"; const require = __clioCreateRequire(import.meta.url);
2
+ import {
3
+ SPOT_CHECK_GUIDANCE,
4
+ receiptEvidenceLabels,
5
+ workerTextLabel,
6
+ workerTextNonEvidenceNotices
7
+ } from "./chunk-6N5PTWMY.js";
8
+ import {
9
+ createDispatchRunEventRegistry
10
+ } from "./chunk-6SGHMWE3.js";
11
+ import "./chunk-XBXAASKX.js";
12
+ import {
13
+ UNVERIFIABLE_RECEIPT_VERIFICATION,
14
+ candidateDiffStat,
15
+ claimCompeteGroup,
16
+ cleanupCompeteGroup,
17
+ closeAgentLedger,
18
+ commitCandidateWork,
19
+ compileExecutionPlan,
20
+ createCandidateWorktree,
21
+ decideReviewGate,
22
+ durableAssistantTextFromEvent,
23
+ executePlan,
24
+ extractRunProvenance,
25
+ finalizePendingGateDecision,
26
+ isGitRepository,
27
+ loadCompeteGroup,
28
+ markCompeteGroupCleanupReady,
29
+ markCompeteGroupWinnerPreserved,
30
+ materializePendingGateDecision,
31
+ mergeWinnerBranch,
32
+ openAgentLedger,
33
+ parseCompeteGateResult,
34
+ preflightPendingGateDecisionMaterialization,
35
+ preparePendingGateDecisionRecovery,
36
+ protectedPathsChangedByCompeteBranch,
37
+ provenanceCompactSuffix,
38
+ readGateDecisionArtifacts,
39
+ recoverCleanupReadyCompeteGroups,
40
+ registerCompeteGroupRun,
41
+ removeCandidateWorktree,
42
+ renderAgentLedgerBoard,
43
+ requireAgentSteps,
44
+ resolvePendingGateDecision,
45
+ settleCompeteGroupRun,
46
+ settleRecoveredCompeteDecision,
47
+ stagePendingGateDecision,
48
+ stagePendingGateOutput
49
+ } from "./chunk-SRDMMSEP.js";
50
+ import "./chunk-4BJ5BYCE.js";
51
+ import "./chunk-QKMUKYO7.js";
52
+ import {
53
+ verifyReceiptIntegrity
54
+ } from "./chunk-AGYYIBLL.js";
55
+ import "./chunk-OOJYHWRB.js";
56
+ import "./chunk-TXOTCRLG.js";
57
+ import "./chunk-G4BMMOKF.js";
58
+ import {
59
+ RESOLVED_DISPATCH_PLAN_ARGUMENT,
60
+ describeDispatchPlan,
61
+ withResolvedPlanTaskPin
62
+ } from "./chunk-DUYJ5IO6.js";
63
+ import {
64
+ COMPETE_STANCES,
65
+ JUDGE_GATE_PROMPT,
66
+ REVIEWER_GATE_PROMPT
67
+ } from "./chunk-LWLEKMDQ.js";
68
+ import {
69
+ parseScoutResult,
70
+ resultContractSourceId
71
+ } from "./chunk-STBPMHSX.js";
72
+ import {
73
+ explainRouteDecision,
74
+ gateDeciderAgentId,
75
+ gateRouteCorrelation
76
+ } from "./chunk-GGXXDWE4.js";
77
+ import "./chunk-5UUP6MWO.js";
78
+ import "./chunk-CMZWFGD2.js";
79
+ import "./chunk-I4HZDVNP.js";
80
+ import "./chunk-7MNJORFF.js";
81
+ import "./chunk-OQE5J4C6.js";
82
+ import "./chunk-ECH6PKUQ.js";
83
+ import "./chunk-T6YILFSB.js";
84
+ import "./chunk-VAKQQHWR.js";
85
+ import {
86
+ truncateUtf8
87
+ } from "./chunk-6XLNIQDB.js";
88
+ import "./chunk-5B2AEOW5.js";
89
+ import "./chunk-CGKSTWHD.js";
90
+ import "./chunk-YHZX5GEU.js";
91
+ import "./chunk-EKMEHE4H.js";
92
+ import "./chunk-LZSJBIVT.js";
93
+ import "./chunk-A3CYT5EX.js";
94
+ import "./chunk-ZDOOVTXZ.js";
95
+ import "./chunk-4VP4KH3K.js";
96
+ import "./chunk-M6SHUN7Q.js";
97
+ import "./chunk-R346GLFC.js";
98
+ import "./chunk-COU2UHX6.js";
99
+ import "./chunk-FQ4SKYE4.js";
100
+ import "./chunk-IKCO5N3L.js";
101
+ import "./chunk-3I7MS7N2.js";
102
+ import "./chunk-IHXBNWMM.js";
103
+ import "./chunk-SST6Z5JA.js";
104
+ import "./chunk-4ZG3XFUR.js";
105
+ import "./chunk-APJ265NV.js";
106
+ import "./chunk-FCSXB6T2.js";
107
+ import "./chunk-YXLYO42X.js";
108
+ import {
109
+ clioStateDir
110
+ } from "./chunk-BNAZZHFG.js";
111
+ import "./chunk-6EJMN2Y3.js";
112
+ import "./chunk-WEPFGWHJ.js";
113
+ import {
114
+ BusChannels
115
+ } from "./chunk-OQ33BKR3.js";
116
+ import "./chunk-7CR24IG7.js";
117
+ import "./chunk-IWHMRKLL.js";
118
+ import {
119
+ init_esm_shims
120
+ } from "./chunk-3R73A4XB.js";
121
+
122
+ // src/tools/dispatch-runner.ts
123
+ init_esm_shims();
124
+ import { randomBytes } from "node:crypto";
125
+ import { existsSync, readFileSync } from "node:fs";
126
+ import { join } from "node:path";
127
+
128
+ // src/tools/dispatch-scout.ts
129
+ init_esm_shims();
130
+ function validateScoutSource(receipt, envelope, agentSpecs) {
131
+ const integrity = verifyReceiptIntegrity(receipt, envelope);
132
+ if (!integrity.ok) throw new Error(`dispatch: Scout source receipt failed integrity: ${integrity.reason}`);
133
+ if (receipt.exitCode !== 0 || receipt.outcome !== void 0 && receipt.outcome !== "succeeded") {
134
+ throw new Error("dispatch: Scout source did not succeed");
135
+ }
136
+ const spec = agentSpecs.find((candidate) => candidate.id === receipt.agentId);
137
+ if (spec === void 0 || spec.resultContract.kind !== "scout-report") {
138
+ throw new Error("dispatch: Scout source agent no longer declares scout-report");
139
+ }
140
+ const fact = receipt.quality.resultContract;
141
+ if (fact === null || fact.conformance !== "pass" || fact.sourceId !== resultContractSourceId(spec.resultContract)) {
142
+ throw new Error("dispatch: Scout source lacks an exact passing result-contract fact");
143
+ }
144
+ if (receipt.output?.state !== "final") throw new Error("dispatch: Scout source has no sealed final output");
145
+ const scout = parseScoutResult(receipt.output.text);
146
+ if (scout === null) throw new Error("dispatch: Scout source output does not match the current strict contract");
147
+ return { envelope, receipt, scout, spec };
148
+ }
149
+ function scoutTransitionDetail(input) {
150
+ try {
151
+ const source = validateScoutSource(input.receipt, input.envelope, input.agentSpecs);
152
+ return source.scout.needsSplit ? {
153
+ kind: "proposed",
154
+ sourceRunId: source.receipt.runId,
155
+ sourceReceiptDigest: source.receipt.integrity.digest,
156
+ subtaskCount: source.scout.proposedSubtasks.length,
157
+ continueWith: {
158
+ from_scout: { run_id: source.receipt.runId, receipt_digest: source.receipt.integrity.digest }
159
+ }
160
+ } : {
161
+ kind: "settled",
162
+ sourceRunId: source.receipt.runId,
163
+ sourceReceiptDigest: source.receipt.integrity.digest,
164
+ findings: source.scout.findings.map((finding) => ({ ...finding }))
165
+ };
166
+ } catch {
167
+ return null;
168
+ }
169
+ }
170
+ function scoutPlanAuthorityGranted(artifact, operatorApproved, fullAuto) {
171
+ return artifact.tasks.every(
172
+ (task) => task.authorityGrant !== null && (task.authorityGrant.basis === "operator-plan-approval" && operatorApproved || task.authorityGrant.basis === "full-auto-policy" && fullAuto)
173
+ );
174
+ }
175
+ async function runScoutContinuationPlan(input) {
176
+ if (input.artifact.deadlineMs === null) throw new Error("Scout dependency plan has no whole-plan deadline");
177
+ const assignmentDeadlineAt = Date.now() + input.artifact.deadlineMs;
178
+ const completedByStep = /* @__PURE__ */ new Map();
179
+ const byStep = new Map(
180
+ input.artifact.tasks.map((task, index) => [task.stepId, { task, request: input.requests[index] }])
181
+ );
182
+ const result = await executePlan(
183
+ input.plan,
184
+ {
185
+ preflight(step) {
186
+ const bound = byStep.get(step.id);
187
+ if (bound?.request === void 0) throw new Error(`Scout step '${step.id}' has no trusted request`);
188
+ const resolution = input.dispatch.preview?.(bound.request);
189
+ if (resolution === void 0) throw new Error(`Scout step '${step.id}' cannot be preflighted`);
190
+ const approved = bound.task.agentDecision?.selected;
191
+ if (approved === void 0 || resolution.agentId !== approved.agentId || resolution.specFingerprint !== approved.specFingerprint || resolution.targetId !== approved.targetId || resolution.wireModelId !== approved.modelId || resolution.runtimeId !== approved.runtimeId || resolution.node.id !== approved.nodeId || resolution.node.kind !== bound.task.nodeKind || resolution.node.host !== bound.task.nodeHost || resolution.thinkingLevel !== (approved.thinkingLevel ?? null) || resolution.toolSignature !== approved.toolSignature || resolution.endpointIdentityHash !== approved.endpointIdentityHash || resolution.settingsFingerprint !== approved.settingsFingerprint) {
192
+ throw new Error(`Scout step '${step.id}' drifted from its approved route`);
193
+ }
194
+ return { step, costUpperBoundUsd: resolution.costUpperBoundUsd, nodeId: resolution.node.id };
195
+ },
196
+ reserve(_plan, admissions) {
197
+ const cost = admissions.reduce((sum, admission) => sum + admission.costUpperBoundUsd, 0);
198
+ if (!Number.isFinite(cost) || cost > input.artifact.costCeilingUsd) {
199
+ throw new Error("Scout dependency plan drifted above its approved cost ceiling");
200
+ }
201
+ return { ownerId: input.reservationOwnerId };
202
+ },
203
+ async run(step, handoffs, reservation, ledger) {
204
+ const bound = byStep.get(step.id);
205
+ if (bound?.request === void 0) throw new Error(`Scout step '${step.id}' has no trusted request`);
206
+ const request = {
207
+ ...bound.request,
208
+ predecessorHandoffs: handoffs,
209
+ reservation,
210
+ assignmentDeadlineAt,
211
+ ...ledger !== void 0 ? { ledger } : {}
212
+ };
213
+ const handle = await input.dispatch.dispatch(request);
214
+ return {
215
+ assignmentId: handle.runId,
216
+ result: input.register(handle, request.agentId).then(({ receipt, summary }) => {
217
+ const completed = input.complete(receipt, summary);
218
+ completedByStep.set(step.id, completed.value);
219
+ return {
220
+ stepId: step.id,
221
+ assignmentId: handle.runId,
222
+ terminalRunId: receipt.runId,
223
+ receiptDigest: receipt.integrity.digest,
224
+ output: receipt.output?.state === "final" ? receipt.output.text : "",
225
+ succeeded: receipt.exitCode === 0 && (receipt.outcome === void 0 || receipt.outcome === "succeeded"),
226
+ integrityValid: completed.integrityValid
227
+ };
228
+ })
229
+ };
230
+ },
231
+ cancel: (assignmentId) => input.dispatch.abort(assignmentId),
232
+ release: (ownerId) => input.dispatch.reservations?.rollbackUnconsumed(ownerId),
233
+ releaseUnconsumed: (ownerId) => input.dispatch.reservations?.rollbackUnconsumed(ownerId)
234
+ },
235
+ input.signal
236
+ );
237
+ return {
238
+ runs: input.plan.steps.flatMap((step) => {
239
+ const completed = completedByStep.get(step.id);
240
+ return completed === void 0 ? [] : [completed];
241
+ }),
242
+ skipped: result.skipped
243
+ };
244
+ }
245
+
246
+ // src/tools/dispatch-runner.ts
247
+ var TRUNCATION_MARKER = "\n[agent output truncated]";
248
+ function createBackgroundSwitch() {
249
+ let fired = false;
250
+ let signal = () => {
251
+ };
252
+ const requested = new Promise((resolve) => {
253
+ signal = resolve;
254
+ });
255
+ return {
256
+ requested,
257
+ fire: () => {
258
+ if (fired) return;
259
+ fired = true;
260
+ signal();
261
+ }
262
+ };
263
+ }
264
+ function isRecord(value) {
265
+ return typeof value === "object" && value !== null && !Array.isArray(value);
266
+ }
267
+ function rawAssistantTextFromEvent(event) {
268
+ return durableAssistantTextFromEvent(event);
269
+ }
270
+ function normalizedAssistantText(summary) {
271
+ return summary.lastAssistantText.trim();
272
+ }
273
+ function fallbackProgressBus(deps) {
274
+ return deps.dispatch.ownsProgressBus?.(deps.bus) === true ? void 0 : deps.bus;
275
+ }
276
+ async function consumeGateSensitiveDispatchEvents(deps, runId, agentId, events) {
277
+ const summary = { count: 0, types: [], lastAssistantText: "", terminalAttemptRunId: runId };
278
+ const bus = fallbackProgressBus(deps);
279
+ for await (const event of events) {
280
+ summary.count += 1;
281
+ const type = isRecord(event) && typeof event.type === "string" ? event.type : "unknown";
282
+ summary.types.push(type);
283
+ if (type === "attempt_start" && isRecord(event) && typeof event.runId === "string" && event.runId.length > 0) {
284
+ summary.terminalAttemptRunId = event.runId;
285
+ }
286
+ const text = rawAssistantTextFromEvent(event);
287
+ if (text.trim().length > 0) summary.lastAssistantText = text;
288
+ deps.runEvents.recordEvent(runId, agentId, event);
289
+ if (type !== "heartbeat") bus?.emit(BusChannels.DispatchProgress, { runId, agentId, event });
290
+ }
291
+ return summary;
292
+ }
293
+ async function runDetached(deps, requests, sessionId) {
294
+ const detached = deps.dispatch.detached;
295
+ if (!detached) {
296
+ return { kind: "error", message: "dispatch: detach is not supported in this context" };
297
+ }
298
+ const ledgerId = requests.length >= 2 ? newGateGroupId("ledger") : null;
299
+ if (ledgerId !== null) await openAgentLedger(ledgerId);
300
+ const ledgered = ledgerId === null ? requests : requests.map((request) => ({ ...request, ledger: { id: ledgerId, sequence: 0 } }));
301
+ const handle = await deps.dispatch.dispatchBatch(ledgered);
302
+ const registered = deps.runEvents.registerBatch(
303
+ handle,
304
+ ledgered.map((request) => request.agentId),
305
+ fallbackProgressBus(deps)
306
+ );
307
+ const assignmentIds = handle.assignmentIds;
308
+ const runs = handle.assignmentIds.map((runId, index) => ({
309
+ runId,
310
+ assignmentId: assignmentIds[index] ?? runId,
311
+ agentId: requests[index]?.agentId ?? "unknown"
312
+ }));
313
+ registered.completion.catch(() => {
314
+ });
315
+ await deps.dispatch.assignments?.flushWrites?.();
316
+ try {
317
+ await detached.register({
318
+ batchId: handle.batchId,
319
+ runs,
320
+ sessionId,
321
+ ...ledgerId !== null ? { ledgerId } : {}
322
+ });
323
+ } catch (err) {
324
+ const message = err instanceof Error ? err.message : String(err);
325
+ return {
326
+ kind: "error",
327
+ message: `dispatch: detached assignments started (batch=${handle.batchId}, assignments=${handle.assignmentIds.join(", ")}) but the durable batch record failed: ${message}`,
328
+ details: { mode: "detached", batchId: handle.batchId, assignmentIds: [...handle.assignmentIds] }
329
+ };
330
+ }
331
+ const lines = [
332
+ `dispatch (detached) batch=${handle.batchId} started ${runs.length} run(s)`,
333
+ ...runs.map((run) => `- ${run.assignmentId} agent=${run.agentId}`),
334
+ "",
335
+ `Runs continue in the background. Collect results with monitor(mode="collect", batch_id="${handle.batchId}"); block on one run with monitor(mode="wait", run_id=<id>).`
336
+ ];
337
+ return {
338
+ kind: "ok",
339
+ output: lines.join("\n"),
340
+ details: {
341
+ mode: "detached",
342
+ batchId: handle.batchId,
343
+ assignmentIds: [...handle.assignmentIds],
344
+ runs: runs.map((run) => ({ runId: run.runId, assignmentId: run.assignmentId, agentId: run.agentId }))
345
+ }
346
+ };
347
+ }
348
+ function registerBackgroundControl(deps, toolCallId, plan) {
349
+ const registry = deps.background;
350
+ if (registry === void 0 || toolCallId === void 0 || toolCallId.length === 0) return () => {
351
+ };
352
+ let release = () => {
353
+ };
354
+ release = registry.register({
355
+ toolCallId,
356
+ label: plan.label,
357
+ convert: () => {
358
+ if (plan.refusal !== null) {
359
+ return { ok: false, message: `${plan.label} cannot be backgrounded: ${plan.refusal}` };
360
+ }
361
+ release();
362
+ plan.fire();
363
+ return { ok: true, message: `${plan.label} is moving to the background as a detached batch` };
364
+ }
365
+ });
366
+ return () => release();
367
+ }
368
+ var DispatchBackgroundedError = class extends Error {
369
+ constructor(batchId, live, settled, undispatched, ledgerId = null) {
370
+ super("dispatch: backgrounded by operator");
371
+ this.batchId = batchId;
372
+ this.live = live;
373
+ this.settled = settled;
374
+ this.undispatched = undispatched;
375
+ this.ledgerId = ledgerId;
376
+ this.name = "DispatchBackgroundedError";
377
+ }
378
+ batchId;
379
+ live;
380
+ settled;
381
+ undispatched;
382
+ ledgerId;
383
+ };
384
+ async function backgroundedDispatchResult(deps, converted, mode, sessionId) {
385
+ const liveIds = converted.live.map((run) => run.assignmentId);
386
+ const detached = deps.dispatch.detached;
387
+ if (!detached) {
388
+ return {
389
+ kind: "error",
390
+ message: `dispatch: the operator moved this call to the background, but detached batch records are unavailable in this context; ${liveIds.join(", ")} keep running and can only be reached with monitor(mode="wait", run_id=<id>)`,
391
+ details: { mode: "detached", assignmentIds: liveIds }
392
+ };
393
+ }
394
+ await deps.dispatch.assignments?.flushWrites?.();
395
+ try {
396
+ await detached.register({
397
+ batchId: converted.batchId,
398
+ runs: converted.live,
399
+ sessionId,
400
+ ...converted.ledgerId !== null ? { ledgerId: converted.ledgerId } : {}
401
+ });
402
+ } catch (err) {
403
+ const message = err instanceof Error ? err.message : String(err);
404
+ return {
405
+ kind: "error",
406
+ message: `dispatch: the operator moved this call to the background (batch=${converted.batchId}, assignments=${liveIds.join(", ")}) but the durable batch record failed: ${message}`,
407
+ details: { mode: "detached", batchId: converted.batchId, assignmentIds: liveIds }
408
+ };
409
+ }
410
+ const lines = [
411
+ `dispatch (${mode}) was moved to the background by the operator: batch=${converted.batchId} holds ${converted.live.length} still-running assignment(s)`,
412
+ ...converted.live.map((run) => `- ${run.assignmentId} agent=${run.agentId}`)
413
+ ];
414
+ if (converted.settled.length > 0) {
415
+ lines.push(
416
+ `${converted.settled.length} earlier ${mode} step(s) had already finished and are not in this batch: ${converted.settled.map((run) => run.receipt.runId).join(", ")}. Read them with monitor(mode="receipt", run_id=<id>).`
417
+ );
418
+ }
419
+ if (converted.undispatched.length > 0) {
420
+ lines.push(
421
+ `${converted.undispatched.length} later ${mode} step(s) were never dispatched (agents: ${converted.undispatched.join(", ")}); re-issue them if they are still wanted.`
422
+ );
423
+ }
424
+ lines.push(
425
+ "",
426
+ `Runs continue in the background. Collect results with monitor(mode="collect", batch_id="${converted.batchId}"); block on one run with monitor(mode="wait", run_id=<id>).`
427
+ );
428
+ return {
429
+ kind: "ok",
430
+ output: lines.join("\n"),
431
+ details: {
432
+ mode: "detached",
433
+ conversion: "operator-backgrounded",
434
+ batchId: converted.batchId,
435
+ assignmentIds: liveIds,
436
+ runs: converted.live.map((run) => ({ runId: run.runId, assignmentId: run.assignmentId, agentId: run.agentId })),
437
+ settledRunIds: converted.settled.map((run) => run.receipt.runId),
438
+ undispatchedAgentIds: [...converted.undispatched]
439
+ }
440
+ };
441
+ }
442
+ function successNote(receipt) {
443
+ if (receipt.outcome !== void 0 && receipt.outcome !== "succeeded") return null;
444
+ if (receipt.exitCode !== 0) return null;
445
+ return receipt.outcomeDetail ?? null;
446
+ }
447
+ function completeRun(deps, receipt, summary, pendingGate) {
448
+ const envelope = deps.dispatch.getRun(receipt.runId);
449
+ return {
450
+ receipt,
451
+ receiptPath: envelope?.receiptPath ?? null,
452
+ summary,
453
+ integrity: envelope === null ? { ok: false, reason: "run ledger envelope unavailable" } : verifyReceiptIntegrity(receipt, envelope),
454
+ ...pendingGate !== void 0 ? { pendingGate } : {}
455
+ };
456
+ }
457
+ var PipelineHaltError = class extends Error {
458
+ constructor(message, runs) {
459
+ super(message);
460
+ this.runs = runs;
461
+ this.name = "PipelineHaltError";
462
+ }
463
+ runs;
464
+ };
465
+ function integrityFailureBanner(run) {
466
+ if (run.integrity.ok) return null;
467
+ return `RECEIPT INTEGRITY FAILED for ${run.receipt.runId} (${run.integrity.reason}); treat this run's receipt fields and worker text as untrusted.`;
468
+ }
469
+ function withAgentLedgerBoard(body, board) {
470
+ return board === null ? body : `${body}
471
+
472
+ ${board}`;
473
+ }
474
+ function formatDispatchOutput(mode, runs, maxOutputBytes, board = null) {
475
+ const failed = runs.filter((run) => run.receipt.exitCode !== 0);
476
+ const perRunOutputBytes = Math.max(1024, Math.floor(maxOutputBytes / Math.max(1, runs.length)));
477
+ const integrityBanners = runs.map((run) => integrityFailureBanner(run)).filter((banner) => banner !== null);
478
+ const needsSpotCheck = runs.some((run) => {
479
+ const state = run.receipt.verification.state;
480
+ return state === "unverified" || state === "unknown";
481
+ });
482
+ const lines = [
483
+ `dispatch (${mode}) total=${runs.length} failed=${failed.length}`,
484
+ `runs=${runs.map((run) => run.receipt.runId).join(", ")}`,
485
+ ...integrityBanners,
486
+ ...needsSpotCheck ? [SPOT_CHECK_GUIDANCE] : [],
487
+ "",
488
+ ...runs.flatMap((run, index) => {
489
+ const { receipt, receiptPath } = run;
490
+ const note = successNote(receipt);
491
+ const noteSuffix = note !== null ? ` note=${note}` : "";
492
+ const outcomeSuffix = receipt.outcome !== void 0 && receipt.outcome !== "succeeded" ? ` outcome=${receipt.outcome}${receipt.outcomeDetail ? ` detail=${receipt.outcomeDetail}` : ""}` : "";
493
+ const failure = receipt.failureMessage ? ` failure=${receipt.failureMessage}` : "";
494
+ const stepLabel = mode === "pipeline" ? `step ${index + 1}/${runs.length} ` : "";
495
+ const provenance = provenanceCompactSuffix(extractRunProvenance(receipt));
496
+ const verification = run.integrity.ok ? receipt.verification : UNVERIFIABLE_RECEIPT_VERIFICATION;
497
+ const evidenceSuffix = ` ${receiptEvidenceLabels(receipt, verification, run.integrity).join(" ")}${run.integrity.ok ? "" : " evidence_verification=unknown/receipt-integrity-failed"}`;
498
+ const routingSuffix = run.integrity.ok && receipt.routeDecision !== void 0 && receipt.routingIntent !== void 0 ? ` route_decision=${receipt.routeDecision.decisionHash} route_mode=${receipt.routeDecision.mode}` : "";
499
+ const answerText = run.integrity.ok && (receipt.output?.state === "final" || receipt.outcome !== void 0 && receipt.outcome !== "succeeded") ? receipt.output?.text ?? "" : "";
500
+ const output = answerText.length > 0 ? truncateUtf8(answerText, perRunOutputBytes, TRUNCATION_MARKER) : run.integrity.ok ? "(no receipt-sealed assistant text captured)" : "(worker text withheld because receipt integrity failed)";
501
+ return [
502
+ `- ${stepLabel}${receipt.runId} agent=${receipt.agentId} exit=${receipt.exitCode} target=${receipt.targetId} model=${receipt.wireModelId} tokens=${receipt.tokenCount} receipt=${receiptPath ?? "n/a"}${evidenceSuffix}${outcomeSuffix}${noteSuffix}${failure}${provenance}${routingSuffix}`,
503
+ ` ${workerTextLabel(verification)}`,
504
+ ...output.split("\n").map((line) => ` ${line}`),
505
+ ...workerTextNonEvidenceNotices(receipt, verification, answerText).map((notice) => ` ${notice}`)
506
+ ];
507
+ })
508
+ ];
509
+ if (board === null) return truncateUtf8(lines.join("\n"), maxOutputBytes, TRUNCATION_MARKER);
510
+ const runBudget = Math.max(1024, maxOutputBytes - Buffer.byteLength(board, "utf8") - 2);
511
+ return withAgentLedgerBoard(truncateUtf8(lines.join("\n"), runBudget, TRUNCATION_MARKER), board);
512
+ }
513
+ function dispatchDetails(deps, mode, runs, board = null) {
514
+ const failed = runs.filter((run) => run.receipt.exitCode !== 0);
515
+ let transition = null;
516
+ for (const run of runs) {
517
+ const envelope = deps.dispatch.getRun(run.receipt.runId);
518
+ const candidate = run.integrity.ok && envelope !== null ? scoutTransitionDetail({ receipt: run.receipt, envelope, agentSpecs: deps.getAgentSpecs() }) : null;
519
+ if (candidate === null) continue;
520
+ if (transition !== null) {
521
+ transition = null;
522
+ break;
523
+ }
524
+ transition = candidate;
525
+ }
526
+ return {
527
+ mode,
528
+ assignmentIds: runs.map((run) => run.receipt.lineage?.rootRunId ?? run.receipt.runId),
529
+ terminalRunIds: runs.map((run) => run.receipt.runId),
530
+ receiptCount: runs.length,
531
+ failedCount: failed.length,
532
+ // Same text the output carries, under a stable key, so a surface that
533
+ // renders details can show the board without re-reading the store.
534
+ ...board !== null ? { agentLedgerBoard: board } : {},
535
+ ...transition !== null ? { scoutTransition: transition } : {},
536
+ runs: runs.map(({ receipt, receiptPath, summary, integrity }) => {
537
+ const provenance = extractRunProvenance(receipt);
538
+ return {
539
+ runId: receipt.runId,
540
+ agentId: receipt.agentId,
541
+ exitCode: receipt.exitCode,
542
+ receiptPath,
543
+ eventCount: summary.count,
544
+ // Structured evidence state for downstream consumers: mirrors the
545
+ // sealed receipt read-only, plus the integrity check so a tampered
546
+ // receipt is machine-visible here too.
547
+ verification: integrity.ok ? receipt.verification : UNVERIFIABLE_RECEIPT_VERIFICATION,
548
+ receiptIntegrity: integrity,
549
+ ...receipt.outcome !== void 0 && receipt.outcome !== "succeeded" ? { outcome: receipt.outcome, outcomeDetail: receipt.outcomeDetail ?? null } : {},
550
+ ...provenance.pipeline !== void 0 ? { pipeline: provenance.pipeline } : {},
551
+ ...provenance.personaOverride !== void 0 ? { personaOverride: provenance.personaOverride } : {},
552
+ ...provenance.escalation !== void 0 ? { escalation: provenance.escalation } : {},
553
+ ...provenance.autonomyEnforcement !== void 0 ? { autonomyEnforcement: provenance.autonomyEnforcement } : {},
554
+ ...integrity.ok && receipt.routeDecision !== void 0 && receipt.routingIntent !== void 0 ? { routing: explainRouteDecision(receipt.routeDecision, receipt.routingIntent) } : {}
555
+ };
556
+ })
557
+ };
558
+ }
559
+ function newGateGroupId(prefix) {
560
+ return `${prefix}-${Date.now().toString(36)}-${randomBytes(3).toString("hex")}`;
561
+ }
562
+ function subjectRef(receipt) {
563
+ return { runId: receipt.runId, digest: receipt.integrity?.digest ?? null };
564
+ }
565
+ function gateCorrelationOf(subject, decider) {
566
+ const facts = (receipt) => ({
567
+ agentId: receipt.agentId,
568
+ targetId: receipt.targetId,
569
+ wireModelId: receipt.wireModelId,
570
+ runtimeId: receipt.runtimeId,
571
+ nodeId: receipt.node?.id ?? "local"
572
+ });
573
+ const { agent, target, modelFamily, runtime, node, independent } = gateRouteCorrelation(
574
+ facts(subject),
575
+ facts(decider)
576
+ );
577
+ return { agent, target, modelFamily, runtime, node, independent };
578
+ }
579
+ function competeCorrelation(candidates, decider) {
580
+ const subject = candidates[0]?.receipt;
581
+ if (subject === void 0) throw new Error("compete decision requires at least one candidate receipt");
582
+ return gateCorrelationOf(subject, decider);
583
+ }
584
+ function reviewerTask(originalTask, builderRunId, cycle) {
585
+ return [
586
+ `Review the work of builder run ${builderRunId} (review cycle ${cycle}).`,
587
+ "The builder's final answer is provided as input data; verify it against the workspace, do not trust it blindly.",
588
+ "Original task the builder was given:",
589
+ originalTask
590
+ ].join("\n\n");
591
+ }
592
+ var REVIEW_SINGLE_TASK_MESSAGE = "dispatch: review supports exactly one task; run the fan-out without review, then dispatch one integration task with review to gate the combined result";
593
+ var COMPETE_SINGLE_TASK_MESSAGE = "dispatch: compete requires exactly one task";
594
+ var COMPETE_NO_REVIEW_MESSAGE = "dispatch: compete has its own judge and cannot combine with review";
595
+ async function runReviewGated(deps, base, review, timeoutMs, signal) {
596
+ const group = newGateGroupId("review");
597
+ const runs = [];
598
+ const decisions = [];
599
+ const recordDecision = (draft, pending) => {
600
+ const decision = pending ? finalizePendingGateDecision(pending, draft) : materializePendingGateDecision(stagePendingGateDecision(draft));
601
+ decisions.push(decision);
602
+ return decision;
603
+ };
604
+ let expired = false;
605
+ let activeRunId = null;
606
+ const abortActive = (bySignal) => {
607
+ expired = true;
608
+ if (activeRunId !== null) {
609
+ deps.dispatch.abort(
610
+ activeRunId,
611
+ bySignal ? void 0 : { cause: "timeout", detail: `timed out after ${timeoutMs}ms` }
612
+ );
613
+ }
614
+ };
615
+ const onSignalAbort = () => abortActive(true);
616
+ const timer = timeoutMs !== void 0 ? setTimeout(() => abortActive(false), timeoutMs) : null;
617
+ signal?.addEventListener("abort", onSignalAbort, { once: true });
618
+ const runOne = async (request) => {
619
+ if (expired || signal?.aborted) {
620
+ throw new Error(
621
+ `review dispatch stopped after ${runs.length} run(s): ${signal?.aborted ? "aborted" : `timed out after ${timeoutMs}ms`}`
622
+ );
623
+ }
624
+ const handle = await deps.dispatch.dispatch(request);
625
+ activeRunId = handle.runId;
626
+ const summary = await consumeGateSensitiveDispatchEvents(deps, handle.runId, request.agentId, handle.events);
627
+ let pendingGate;
628
+ if (request.gate?.role === "reviewer") {
629
+ try {
630
+ pendingGate = stagePendingGateOutput({
631
+ group: request.gate.group,
632
+ topology: "review",
633
+ cycle: request.gate.cycle,
634
+ subjects: request.gate.subjects ?? [],
635
+ deciderRunId: summary.terminalAttemptRunId,
636
+ finalOutput: normalizedAssistantText(summary),
637
+ terminalCycle: request.gate.cycle === review.maxCycles
638
+ });
639
+ } catch (error) {
640
+ try {
641
+ deps.dispatch.abort(handle.runId);
642
+ } catch {
643
+ }
644
+ await Promise.allSettled([handle.finalPromise]);
645
+ activeRunId = null;
646
+ throw error;
647
+ }
648
+ }
649
+ let receipt;
650
+ try {
651
+ receipt = await handle.finalPromise;
652
+ } finally {
653
+ activeRunId = null;
654
+ }
655
+ const completed = completeRun(deps, receipt, summary, pendingGate);
656
+ runs.push(completed);
657
+ return completed;
658
+ };
659
+ try {
660
+ let findings = null;
661
+ for (let cycle = 1; cycle <= review.maxCycles; cycle += 1) {
662
+ const builderGate = {
663
+ role: "builder",
664
+ group,
665
+ cycle,
666
+ ...findings !== null ? { subjects: [subjectRef(findings.reviewer.receipt)], verdict: "revise" } : {}
667
+ };
668
+ const builderRequest = {
669
+ ...base,
670
+ executionRole: "builder",
671
+ gate: builderGate,
672
+ ...findings !== null ? { pipelineInput: { fromRunId: findings.reviewer.receipt.runId, position: cycle, text: findings.text } } : {}
673
+ };
674
+ const builder = await runOne(
675
+ withResolvedPlanTaskPin(
676
+ builderRequest,
677
+ review.resolvedTasks?.find((task) => task.role === "builder" && task.position === cycle)
678
+ )
679
+ );
680
+ if (isPipelineStepFailure(builder.receipt)) {
681
+ recordDecision({
682
+ group,
683
+ topology: "review",
684
+ cycle,
685
+ outcome: "exhausted",
686
+ subjects: [subjectRef(builder.receipt)],
687
+ detail: `builder ended ${pipelineFailureReason(builder.receipt)}`
688
+ });
689
+ return {
690
+ runs,
691
+ decisions,
692
+ verdict: null,
693
+ cycles: cycle,
694
+ needsDecision: `builder run ${builder.receipt.runId} ended ${pipelineFailureReason(builder.receipt)} in cycle ${cycle}`
695
+ };
696
+ }
697
+ const reviewerRequest = {
698
+ agentId: gateDeciderAgentId(review.reviewer),
699
+ executionRole: "reviewer",
700
+ task: reviewerTask(base.task, builder.receipt.runId, cycle),
701
+ systemPrompt: REVIEWER_GATE_PROMPT,
702
+ autonomy: "read-only",
703
+ gate: { role: "reviewer", group, cycle, subjects: [subjectRef(builder.receipt)] },
704
+ pipelineInput: {
705
+ fromRunId: builder.receipt.runId,
706
+ position: cycle,
707
+ text: normalizedAssistantText(builder.summary)
708
+ },
709
+ ...base.cwd !== void 0 ? { cwd: base.cwd } : {},
710
+ ...review.node !== void 0 ? { node: review.node } : {},
711
+ ...review.model !== void 0 ? { model: review.model } : {},
712
+ ...review.target !== void 0 ? { target: review.target } : {},
713
+ ...base.plan !== void 0 ? { plan: base.plan } : {},
714
+ ...base.parentToolCallId !== void 0 ? { parentToolCallId: base.parentToolCallId } : {},
715
+ ...base.reservation !== void 0 ? { reservation: { ownerId: base.reservation.ownerId, memberId: base.reservation.memberId } } : {}
716
+ };
717
+ const reviewer = await runOne(
718
+ withResolvedPlanTaskPin(
719
+ reviewerRequest,
720
+ review.resolvedTasks?.find((task) => task.role === "reviewer" && task.position === cycle),
721
+ { pinTask: false }
722
+ )
723
+ );
724
+ if (isPipelineStepFailure(reviewer.receipt)) {
725
+ recordDecision(
726
+ {
727
+ group,
728
+ topology: "review",
729
+ cycle,
730
+ outcome: "exhausted",
731
+ subjects: [subjectRef(builder.receipt)],
732
+ decider: subjectRef(reviewer.receipt),
733
+ correlation: gateCorrelationOf(builder.receipt, reviewer.receipt),
734
+ detail: `reviewer ended ${pipelineFailureReason(reviewer.receipt)}`
735
+ },
736
+ reviewer.pendingGate
737
+ );
738
+ return {
739
+ runs,
740
+ decisions,
741
+ verdict: null,
742
+ cycles: cycle,
743
+ needsDecision: `reviewer run ${reviewer.receipt.runId} ended ${pipelineFailureReason(reviewer.receipt)} in cycle ${cycle}`
744
+ };
745
+ }
746
+ const decided = decideReviewGate({
747
+ group,
748
+ cycle,
749
+ terminalCycle: cycle === review.maxCycles,
750
+ subjects: [subjectRef(builder.receipt)],
751
+ decider: subjectRef(reviewer.receipt),
752
+ correlation: gateCorrelationOf(builder.receipt, reviewer.receipt),
753
+ output: normalizedAssistantText(reviewer.summary)
754
+ });
755
+ recordDecision(decided.draft, reviewer.pendingGate);
756
+ if (decided.findings === null) {
757
+ return {
758
+ runs,
759
+ decisions,
760
+ verdict: decided.verdict,
761
+ cycles: cycle,
762
+ ...decided.needsDecision !== null ? { needsDecision: decided.needsDecision } : {}
763
+ };
764
+ }
765
+ findings = { reviewer, text: decided.findings };
766
+ }
767
+ throw new Error("review gate exhausted without terminal decision evidence");
768
+ } finally {
769
+ if (timer) clearTimeout(timer);
770
+ signal?.removeEventListener("abort", onSignalAbort);
771
+ }
772
+ }
773
+ function judgeTask(originalTask, candidates, stats) {
774
+ const lines = [
775
+ `Rank ${candidates.length} candidate implementations of this task and pick the best one.`,
776
+ "Original task:",
777
+ originalTask,
778
+ "Candidates:",
779
+ ...candidates.map(
780
+ (candidate, index) => ` ${candidate.index}. branch=${candidate.branch} worktree=${candidate.path} (${stats[index] ?? "?"})`
781
+ )
782
+ ];
783
+ return lines.join("\n\n");
784
+ }
785
+ function readVerifiedGateReceipt(deps, runId) {
786
+ const envelope = deps.dispatch.getRun(runId);
787
+ if (envelope === null) return null;
788
+ const path = envelope.receiptPath ?? join(clioStateDir(), "receipts", `${runId}.json`);
789
+ if (!existsSync(path)) return null;
790
+ let receipt;
791
+ try {
792
+ receipt = JSON.parse(readFileSync(path, "utf8"));
793
+ } catch (error) {
794
+ throw new Error(`pending gate receipt ${runId} is unreadable: ${competeErrorMessage(error)}`);
795
+ }
796
+ if (receipt.runId !== runId) throw new Error(`pending gate receipt path for ${runId} contains ${receipt.runId}`);
797
+ const verification = verifyReceiptIntegrity(receipt, envelope);
798
+ if (!verification.ok) throw new Error(`pending gate receipt ${runId} failed integrity: ${verification.reason}`);
799
+ if (receipt.integrity?.digest === void 0) throw new Error(`pending gate receipt ${runId} has no integrity digest`);
800
+ return receipt;
801
+ }
802
+ function recoveredCorrelation(deps, subjects, decider) {
803
+ const first = subjects[0];
804
+ const subject = first === void 0 ? null : readVerifiedGateReceipt(deps, first.runId);
805
+ if (subject === null) throw new Error("recovered gate decision has no verified subject receipt for correlation");
806
+ return gateCorrelationOf(subject, decider);
807
+ }
808
+ function settlePendingCompeteResource(handle, draft) {
809
+ if (draft.topology !== "compete" || draft.outcome !== "winner" && draft.outcome !== "no-winner") return;
810
+ const root = handle.record.resourceRoot ?? process.cwd();
811
+ settleRecoveredCompeteDecision(root, draft.group, draft.outcome === "winner" ? draft.winner?.index ?? null : null);
812
+ }
813
+ function recoverPendingGateEvidence(deps) {
814
+ const recovery = preparePendingGateDecisionRecovery();
815
+ const resolved = [];
816
+ for (const handle of recovery.unresolved) {
817
+ if (handle.record.kind !== "output") continue;
818
+ const record = handle.record;
819
+ const deciderReceipt = readVerifiedGateReceipt(deps, record.deciderRunId);
820
+ if (deciderReceipt === null) {
821
+ throw new Error(
822
+ `pending ${record.topology} decision ${record.id} has no verified decider receipt for ${record.deciderRunId}`
823
+ );
824
+ }
825
+ const decider = subjectRef(deciderReceipt);
826
+ let draft;
827
+ if (record.topology === "review") {
828
+ if (isPipelineStepFailure(deciderReceipt)) {
829
+ draft = {
830
+ group: record.group,
831
+ topology: "review",
832
+ cycle: record.cycle,
833
+ outcome: "exhausted",
834
+ subjects: record.subjects,
835
+ decider,
836
+ correlation: recoveredCorrelation(deps, record.subjects, deciderReceipt),
837
+ detail: `reviewer ended ${pipelineFailureReason(deciderReceipt)}`
838
+ };
839
+ } else {
840
+ draft = decideReviewGate({
841
+ group: record.group,
842
+ cycle: record.cycle,
843
+ terminalCycle: record.terminalCycle === true,
844
+ subjects: record.subjects,
845
+ decider,
846
+ correlation: recoveredCorrelation(deps, record.subjects, deciderReceipt),
847
+ output: record.finalOutput
848
+ }).draft;
849
+ }
850
+ resolved.push(resolvePendingGateDecision(handle, draft));
851
+ continue;
852
+ }
853
+ const judged = isPipelineStepFailure(deciderReceipt) ? null : parseCompeteGateResult(record.finalOutput, record.subjects.length);
854
+ const pick = judged?.ok ? judged.result.winner : null;
855
+ const pickedSubject = pick === null ? void 0 : record.subjects[pick - 1];
856
+ const candidateReceipt = pickedSubject === void 0 ? null : readVerifiedGateReceipt(deps, pickedSubject.runId);
857
+ if (pickedSubject !== void 0 && candidateReceipt === null) {
858
+ throw new Error(
859
+ `pending compete decision ${record.id} has no verified candidate receipt for ${pickedSubject.runId}`
860
+ );
861
+ }
862
+ let blockedProtected = [];
863
+ if (pick !== null && pickedSubject !== void 0 && candidateReceipt !== null) {
864
+ const root = record.resourceRoot ?? process.cwd();
865
+ const protectedPaths = deps.dispatch.protectedArtifactState?.().artifacts.map((artifact) => artifact.path) ?? [];
866
+ blockedProtected = protectedPathsChangedByCompeteBranch(
867
+ root,
868
+ `clio/compete/${record.group}/${pick}`,
869
+ protectedPaths
870
+ );
871
+ }
872
+ const validWinner = pick !== null && pickedSubject !== void 0 && candidateReceipt !== null && !isPipelineStepFailure(candidateReceipt) && blockedProtected.length === 0;
873
+ if (validWinner && pick !== null && pickedSubject !== void 0) {
874
+ draft = {
875
+ group: record.group,
876
+ topology: "compete",
877
+ cycle: record.cycle,
878
+ outcome: "winner",
879
+ subjects: record.subjects,
880
+ decider,
881
+ correlation: recoveredCorrelation(deps, record.subjects, deciderReceipt),
882
+ winner: {
883
+ index: pick,
884
+ subject: pickedSubject,
885
+ branch: `clio/compete/${record.group}/${pick}`
886
+ }
887
+ };
888
+ } else {
889
+ draft = {
890
+ group: record.group,
891
+ topology: "compete",
892
+ cycle: record.cycle,
893
+ outcome: "no-winner",
894
+ subjects: record.subjects,
895
+ decider,
896
+ correlation: recoveredCorrelation(deps, record.subjects, deciderReceipt),
897
+ detail: isPipelineStepFailure(deciderReceipt) ? `judge ended ${pipelineFailureReason(deciderReceipt)}` : judged !== null && !judged.ok ? judged.reason : blockedProtected.length > 0 ? `judge-selected candidate ${pick} changes protected artifact(s): ${blockedProtected.join(", ")}` : `judge picked failed or missing candidate ${pick}`
898
+ };
899
+ }
900
+ resolved.push(resolvePendingGateDecision(handle, draft));
901
+ }
902
+ const ready = [...recovery.ready, ...resolved];
903
+ preflightPendingGateDecisionMaterialization(ready);
904
+ for (const handle of ready) {
905
+ if (handle.record.kind !== "decision") continue;
906
+ settlePendingCompeteResource(handle, handle.record.decision);
907
+ materializePendingGateDecision(handle);
908
+ }
909
+ const durable = readGateDecisionArtifacts();
910
+ const confirmedGroups = new Set(
911
+ durable.filter(
912
+ ({ artifact }) => artifact.topology === "compete" && (artifact.outcome === "operator-confirmed" || artifact.outcome === "full-auto-applied")
913
+ ).map(({ artifact }) => artifact.group)
914
+ );
915
+ for (const { artifact } of durable) {
916
+ if (artifact.topology !== "compete" || artifact.outcome !== "winner" || artifact.winner === void 0 || confirmedGroups.has(artifact.group)) {
917
+ continue;
918
+ }
919
+ settleRecoveredCompeteDecision(process.cwd(), artifact.group, artifact.winner.index);
920
+ }
921
+ }
922
+ function competeErrorMessage(err) {
923
+ return err instanceof Error ? err.message : String(err);
924
+ }
925
+ function dispatchErrorMessage(err) {
926
+ const message = competeErrorMessage(err);
927
+ return message.startsWith("dispatch: ") ? message : `dispatch: ${message}`;
928
+ }
929
+ function rejectedReasons(results) {
930
+ return results.filter((result) => result.status === "rejected").map((result) => result.reason);
931
+ }
932
+ async function runCompete(deps, base, compete, autonomy, timeoutMs, signal) {
933
+ const requestedRoot = base.cwd !== void 0 && base.cwd.length > 0 ? base.cwd : process.cwd();
934
+ if (!isGitRepository(requestedRoot)) {
935
+ throw new Error(`compete requires a git repository at ${requestedRoot}; scratch worktrees isolate the candidates`);
936
+ }
937
+ const protectedPaths = deps.dispatch.protectedArtifactState?.().artifacts.map((artifact) => artifact.path) ?? [];
938
+ recoverCleanupReadyCompeteGroups(requestedRoot);
939
+ const group = newGateGroupId("compete");
940
+ const ledgerId = newGateGroupId("ledger");
941
+ const ledger = { id: ledgerId, sequence: 0 };
942
+ await openAgentLedger(ledgerId);
943
+ let ownership = null;
944
+ const worktrees = [];
945
+ const runs = [];
946
+ const decisions = [];
947
+ const recordDecision = (draft, pending) => {
948
+ const decision = pending ? finalizePendingGateDecision(pending, draft) : materializePendingGateDecision(stagePendingGateDecision(draft, { resourceRoot: requestedRoot }));
949
+ decisions.push(decision);
950
+ return decision;
951
+ };
952
+ const ownedRuns = [];
953
+ const abortErrors = [];
954
+ let stop = null;
955
+ let primaryError = null;
956
+ let winner = null;
957
+ const currentWinner = () => winner;
958
+ let timer = null;
959
+ let signalListenerInstalled = false;
960
+ const abortOwnedRun = (run) => {
961
+ if (run.settled || run.abortRequested || stop === null) return;
962
+ run.abortRequested = true;
963
+ try {
964
+ deps.dispatch.abort(run.runId, stop.reason);
965
+ } catch (err) {
966
+ abortErrors.push(err);
967
+ }
968
+ };
969
+ const requestStop = (next) => {
970
+ if (stop === null) stop = next;
971
+ for (const run of ownedRuns) abortOwnedRun(run);
972
+ };
973
+ const onSignalAbort = () => requestStop({ message: "compete dispatch aborted" });
974
+ const throwIfStopped = () => {
975
+ if (stop !== null) throw new Error(stop.message);
976
+ };
977
+ const settleRun = async (handle, request) => {
978
+ const summaryPromise = consumeGateSensitiveDispatchEvents(deps, handle.runId, request.agentId, handle.events).then(
979
+ (summary) => {
980
+ const pendingGate = request.gate?.role === "judge" ? stagePendingGateOutput({
981
+ group: request.gate.group,
982
+ topology: "compete",
983
+ cycle: request.gate.cycle,
984
+ subjects: request.gate.subjects ?? [],
985
+ deciderRunId: summary.terminalAttemptRunId,
986
+ finalOutput: normalizedAssistantText(summary),
987
+ ...request.cwd !== void 0 ? { resourceRoot: request.cwd } : {}
988
+ }) : void 0;
989
+ return { summary, pendingGate };
990
+ }
991
+ );
992
+ const [summaryResult, receiptResult] = await Promise.allSettled([summaryPromise, handle.finalPromise]);
993
+ const failures = rejectedReasons([summaryResult, receiptResult]);
994
+ if (failures.length > 0) {
995
+ throw new Error(`run ${handle.runId} failed to settle: ${failures.map(competeErrorMessage).join("; ")}`);
996
+ }
997
+ const summaryWithGate = summaryResult.status === "fulfilled" ? summaryResult.value : null;
998
+ const receipt = receiptResult.status === "fulfilled" ? receiptResult.value : null;
999
+ if (summaryWithGate === null || receipt === null) throw new Error(`run ${handle.runId} produced no settled result`);
1000
+ return completeRun(deps, receipt, summaryWithGate.summary, summaryWithGate.pendingGate);
1001
+ };
1002
+ const admitOwnedRun = async (request, label) => {
1003
+ try {
1004
+ const handle = await deps.dispatch.dispatch(request, {
1005
+ onAdmitted(admission) {
1006
+ if (ownership === null) throw new Error(`compete ${group} has no active ownership claim`);
1007
+ registerCompeteGroupRun(ownership, admission);
1008
+ }
1009
+ });
1010
+ const owned = {
1011
+ runId: handle.runId,
1012
+ settled: false,
1013
+ abortRequested: false,
1014
+ settlement: Promise.resolve(null)
1015
+ };
1016
+ ownedRuns.push(owned);
1017
+ owned.settlement = (async () => {
1018
+ try {
1019
+ return await settleRun(handle, request);
1020
+ } catch (err) {
1021
+ requestStop({ message: `${label} failed to settle: ${competeErrorMessage(err)}` });
1022
+ throw err;
1023
+ } finally {
1024
+ owned.settled = true;
1025
+ if (ownership !== null) settleCompeteGroupRun(ownership, owned.runId);
1026
+ }
1027
+ })();
1028
+ owned.settlement.catch(() => {
1029
+ });
1030
+ if (stop !== null) abortOwnedRun(owned);
1031
+ return owned;
1032
+ } catch (err) {
1033
+ requestStop({ message: `${label} admission failed: ${competeErrorMessage(err)}` });
1034
+ throw err;
1035
+ }
1036
+ };
1037
+ const settledCompletedRuns = async (owned, phase) => {
1038
+ const results = await Promise.allSettled(owned.map((run) => run.settlement));
1039
+ const failures = rejectedReasons(results);
1040
+ if (failures.length > 0) {
1041
+ throw new Error(`${phase} failed: ${failures.map(competeErrorMessage).join("; ")}`);
1042
+ }
1043
+ return results.map((result) => {
1044
+ if (result.status !== "fulfilled") throw new Error(`${phase} produced no completed run`);
1045
+ return result.value;
1046
+ });
1047
+ };
1048
+ let outcome = null;
1049
+ try {
1050
+ outcome = await (async () => {
1051
+ ownership = claimCompeteGroup(requestedRoot, group);
1052
+ const root = ownership.root;
1053
+ const createCandidate = deps.competeWorktrees?.createCandidate ?? createCandidateWorktree;
1054
+ for (let index = 1; index <= compete.candidates; index += 1) {
1055
+ throwIfStopped();
1056
+ worktrees.push(createCandidate(ownership, index, "HEAD"));
1057
+ }
1058
+ if (timeoutMs !== void 0) {
1059
+ timer = setTimeout(
1060
+ () => requestStop({
1061
+ message: `compete dispatch timed out after ${timeoutMs}ms`,
1062
+ reason: { cause: "timeout", detail: `timed out after ${timeoutMs}ms` }
1063
+ }),
1064
+ timeoutMs
1065
+ );
1066
+ }
1067
+ signal?.addEventListener("abort", onSignalAbort, { once: true });
1068
+ signalListenerInstalled = signal !== void 0;
1069
+ if (signal?.aborted) onSignalAbort();
1070
+ throwIfStopped();
1071
+ const admissionResults = await Promise.allSettled(
1072
+ worktrees.map((worktree) => {
1073
+ const request = {
1074
+ ...base,
1075
+ executionRole: "builder",
1076
+ cwd: worktree.path,
1077
+ protectedArtifactRemap: { sourceRoot: root, workerRoot: worktree.path },
1078
+ gate: { role: "candidate", group, cycle: worktree.index },
1079
+ ledger,
1080
+ competeStance: COMPETE_STANCES[(worktree.index - 1) % COMPETE_STANCES.length]
1081
+ };
1082
+ return admitOwnedRun(
1083
+ withResolvedPlanTaskPin(
1084
+ request,
1085
+ compete.resolvedTasks?.find((task) => task.role === "candidate" && task.position === worktree.index)
1086
+ ),
1087
+ `candidate ${worktree.index}`
1088
+ );
1089
+ })
1090
+ );
1091
+ const admissionFailures = rejectedReasons(admissionResults);
1092
+ if (admissionFailures.length > 0) {
1093
+ const admissionMessage = `compete candidate admission failed: ${admissionFailures.map(competeErrorMessage).join("; ")}`;
1094
+ requestStop({ message: admissionMessage });
1095
+ await Promise.allSettled(ownedRuns.map((run) => run.settlement));
1096
+ throw new Error(admissionMessage);
1097
+ }
1098
+ const candidateOwned = admissionResults.map((result) => {
1099
+ if (result.status !== "fulfilled") throw new Error("compete candidate admission produced no handle");
1100
+ return result.value;
1101
+ });
1102
+ const candidateRuns = await settledCompletedRuns(candidateOwned, "candidate settlement");
1103
+ runs.push(...candidateRuns);
1104
+ throwIfStopped();
1105
+ const stats = worktrees.map((worktree, index) => {
1106
+ const receipt = candidateRuns[index]?.receipt;
1107
+ const failed = receipt !== void 0 && isPipelineStepFailure(receipt);
1108
+ if (failed) return "builder failed";
1109
+ commitCandidateWork(worktree, `clio-coder compete ${group} candidate ${worktree.index}`);
1110
+ return candidateDiffStat(root, worktree.branch);
1111
+ });
1112
+ if (candidateRuns.every((run) => isPipelineStepFailure(run.receipt))) {
1113
+ recordDecision({
1114
+ group,
1115
+ topology: "compete",
1116
+ cycle: 1,
1117
+ outcome: "no-winner",
1118
+ subjects: candidateRuns.map((run) => subjectRef(run.receipt)),
1119
+ detail: "every candidate builder failed; nothing to judge"
1120
+ });
1121
+ return { runs, decisions, group, winner: null, needsDecision: "every candidate builder failed; nothing to judge" };
1122
+ }
1123
+ const judgeRequest = {
1124
+ agentId: gateDeciderAgentId(compete.judge?.agent),
1125
+ executionRole: "judge",
1126
+ task: judgeTask(base.task, worktrees, stats),
1127
+ systemPrompt: JUDGE_GATE_PROMPT,
1128
+ autonomy: "read-only",
1129
+ cwd: root,
1130
+ gate: {
1131
+ role: "judge",
1132
+ group,
1133
+ cycle: 1,
1134
+ subjects: candidateRuns.map((run) => subjectRef(run.receipt))
1135
+ },
1136
+ ledger,
1137
+ ...compete.judge?.model !== void 0 ? { model: compete.judge.model } : {},
1138
+ ...compete.judge?.target !== void 0 ? { target: compete.judge.target } : {},
1139
+ ...compete.judge?.node !== void 0 ? { node: compete.judge.node } : {},
1140
+ ...base.plan !== void 0 ? { plan: base.plan } : {},
1141
+ ...base.parentToolCallId !== void 0 ? { parentToolCallId: base.parentToolCallId } : {},
1142
+ ...base.reservation !== void 0 ? { reservation: { ownerId: base.reservation.ownerId, memberId: base.reservation.memberId } } : {}
1143
+ };
1144
+ throwIfStopped();
1145
+ const judgeOwned = await admitOwnedRun(
1146
+ withResolvedPlanTaskPin(
1147
+ judgeRequest,
1148
+ compete.resolvedTasks?.find((task) => task.role === "judge"),
1149
+ { pinTask: false }
1150
+ ),
1151
+ "judge"
1152
+ );
1153
+ const judgeRun = (await settledCompletedRuns([judgeOwned], "judge settlement"))[0];
1154
+ if (judgeRun === void 0) throw new Error("judge produced no completed run");
1155
+ runs.push(judgeRun);
1156
+ throwIfStopped();
1157
+ const judgeReceipt = judgeRun.receipt;
1158
+ const judgeSummary = judgeRun.summary;
1159
+ const noWinner = (detail) => ({
1160
+ group,
1161
+ topology: "compete",
1162
+ cycle: 1,
1163
+ outcome: "no-winner",
1164
+ subjects: candidateRuns.map((run) => subjectRef(run.receipt)),
1165
+ decider: subjectRef(judgeReceipt),
1166
+ correlation: competeCorrelation(candidateRuns, judgeReceipt),
1167
+ detail
1168
+ });
1169
+ if (isPipelineStepFailure(judgeReceipt)) {
1170
+ recordDecision(noWinner(`judge ended ${pipelineFailureReason(judgeReceipt)}`), judgeRun.pendingGate);
1171
+ return {
1172
+ runs,
1173
+ decisions,
1174
+ group,
1175
+ winner: null,
1176
+ needsDecision: `judge run ${judgeReceipt.runId} ended ${pipelineFailureReason(judgeReceipt)}; candidate worktrees were cleaned, their receipts remain; re-run compete or build directly`
1177
+ };
1178
+ }
1179
+ const judged = parseCompeteGateResult(normalizedAssistantText(judgeSummary), compete.candidates);
1180
+ if (!judged.ok) {
1181
+ recordDecision(noWinner(judged.reason), judgeRun.pendingGate);
1182
+ return {
1183
+ runs,
1184
+ decisions,
1185
+ group,
1186
+ winner: null,
1187
+ needsDecision: `${judged.reason}; candidate worktrees were cleaned, their receipts remain; re-run compete or build directly`
1188
+ };
1189
+ }
1190
+ const pick = judged.result.winner;
1191
+ const pickedWorktree = worktrees.find((worktree) => worktree.index === pick);
1192
+ const pickedRun = candidateRuns[pick - 1];
1193
+ if (!pickedWorktree || pickedRun === void 0 || isPipelineStepFailure(pickedRun.receipt)) {
1194
+ recordDecision(noWinner(`judge picked failed or missing candidate ${pick}`), judgeRun.pendingGate);
1195
+ return {
1196
+ runs,
1197
+ decisions,
1198
+ group,
1199
+ winner: null,
1200
+ needsDecision: `judge picked candidate ${pick}, whose builder failed; the operator decides`
1201
+ };
1202
+ }
1203
+ const protectedChanges = protectedPathsChangedByCompeteBranch(root, pickedWorktree.branch, protectedPaths);
1204
+ if (protectedChanges.length > 0) {
1205
+ recordDecision(
1206
+ noWinner(`judge-selected candidate ${pick} changes protected artifact(s): ${protectedChanges.join(", ")}`),
1207
+ judgeRun.pendingGate
1208
+ );
1209
+ return {
1210
+ runs,
1211
+ decisions,
1212
+ group,
1213
+ winner: null,
1214
+ needsDecision: `candidate ${pick} changes protected artifact(s) and cannot be applied: ${protectedChanges.join(", ")}`
1215
+ };
1216
+ }
1217
+ const winnerRef = {
1218
+ index: pick,
1219
+ subject: subjectRef(pickedRun.receipt),
1220
+ branch: pickedWorktree.branch
1221
+ };
1222
+ const winnerDecision = recordDecision(
1223
+ {
1224
+ group,
1225
+ topology: "compete",
1226
+ cycle: 1,
1227
+ outcome: "winner",
1228
+ subjects: candidateRuns.map((run) => subjectRef(run.receipt)),
1229
+ decider: subjectRef(judgeReceipt),
1230
+ correlation: competeCorrelation(candidateRuns, judgeReceipt),
1231
+ winner: winnerRef
1232
+ },
1233
+ judgeRun.pendingGate
1234
+ );
1235
+ if (autonomy === "full-auto") {
1236
+ const merge = (deps.competeWorktrees?.mergeWinner ?? mergeWinnerBranch)(root, pickedWorktree.branch);
1237
+ if (!merge.ok) {
1238
+ winner = { index: pick, branch: pickedWorktree.branch, applied: false };
1239
+ return {
1240
+ runs,
1241
+ decisions,
1242
+ group,
1243
+ winner,
1244
+ needsDecision: `winner candidate ${pick} could not be merged (${merge.reason}); its branch ${pickedWorktree.branch} is preserved`
1245
+ };
1246
+ }
1247
+ try {
1248
+ recordDecision({
1249
+ group,
1250
+ topology: "compete",
1251
+ cycle: 1,
1252
+ outcome: "full-auto-applied",
1253
+ subjects: [winnerRef.subject],
1254
+ winner: winnerRef,
1255
+ confirmation: {
1256
+ id: winnerDecision.artifact.id,
1257
+ digest: winnerDecision.artifact.integrity.digest
1258
+ },
1259
+ detail: `full-auto applied ${pickedWorktree.branch} under dispatch plan ${base.plan?.hash ?? "unavailable"}`
1260
+ });
1261
+ } catch (error) {
1262
+ winner = { index: pick, branch: pickedWorktree.branch, applied: false };
1263
+ throw new Error(
1264
+ `winner ${pickedWorktree.branch} merged but full-auto application evidence failed: ${competeErrorMessage(error)}; branch preserved for recovery`
1265
+ );
1266
+ }
1267
+ winner = { index: pick, branch: pickedWorktree.branch, applied: true };
1268
+ return { runs, decisions, group, winner };
1269
+ }
1270
+ winner = { index: pick, branch: pickedWorktree.branch, applied: false };
1271
+ return { runs, decisions, group, winner };
1272
+ })();
1273
+ } catch (err) {
1274
+ primaryError = err;
1275
+ }
1276
+ if (timer) clearTimeout(timer);
1277
+ if (signalListenerInstalled) signal?.removeEventListener("abort", onSignalAbort);
1278
+ if (ownedRuns.some((run) => !run.settled)) {
1279
+ requestStop({ message: "compete lifecycle exited before every worker settled" });
1280
+ }
1281
+ const finalizationErrors = [];
1282
+ await Promise.allSettled(ownedRuns.map((run) => run.settlement));
1283
+ const board = renderAgentLedgerBoard(ledgerId);
1284
+ await closeAgentLedger(ledgerId);
1285
+ finalizationErrors.push(...abortErrors);
1286
+ const winnerAtFinalization = currentWinner();
1287
+ if (ownership !== null) {
1288
+ if (winnerAtFinalization !== null && !winnerAtFinalization.applied) {
1289
+ try {
1290
+ ownership = markCompeteGroupWinnerPreserved(ownership, winnerAtFinalization.index);
1291
+ for (const worktree of worktrees) {
1292
+ if (worktree.index === winnerAtFinalization.index) continue;
1293
+ try {
1294
+ removeCandidateWorktree(ownership, worktree, true);
1295
+ } catch (err) {
1296
+ finalizationErrors.push(err);
1297
+ }
1298
+ }
1299
+ } catch (err) {
1300
+ finalizationErrors.push(err);
1301
+ }
1302
+ } else {
1303
+ try {
1304
+ ownership = markCompeteGroupCleanupReady(ownership);
1305
+ } catch (err) {
1306
+ finalizationErrors.push(err);
1307
+ }
1308
+ try {
1309
+ const cleanupGroup = deps.competeWorktrees?.cleanupGroup ?? cleanupCompeteGroup;
1310
+ cleanupGroup(ownership);
1311
+ } catch (err) {
1312
+ finalizationErrors.push(err);
1313
+ }
1314
+ }
1315
+ }
1316
+ if (finalizationErrors.length > 0) {
1317
+ const unique = [...new Set(finalizationErrors)];
1318
+ const suffix = unique.map(competeErrorMessage).join("; ");
1319
+ if (primaryError !== null) {
1320
+ throw new Error(`${competeErrorMessage(primaryError)}; compete finalization also failed: ${suffix}`);
1321
+ }
1322
+ throw new Error(`compete finalization failed: ${suffix}`);
1323
+ }
1324
+ if (primaryError !== null) throw primaryError;
1325
+ if (outcome === null) throw new Error("compete lifecycle produced no outcome");
1326
+ return { ...outcome, board };
1327
+ }
1328
+ function runApplyWinner(input, planHash, authority, mergeWinner = mergeWinnerBranch, protectedPaths = []) {
1329
+ if (authority === null) {
1330
+ return {
1331
+ kind: "error",
1332
+ message: "dispatch: apply_winner requires a registry-authenticated operator approval or full-auto authority"
1333
+ };
1334
+ }
1335
+ const branch = input.branch;
1336
+ const match = /^clio\/compete\/([A-Za-z0-9][A-Za-z0-9._-]{0,127})\/([1-9]\d*)$/.exec(branch);
1337
+ if (!match) {
1338
+ return { kind: "error", message: "dispatch: apply_winner.branch must be a clio/compete/<group>/<n> branch" };
1339
+ }
1340
+ const group = match[1] ?? "";
1341
+ const winnerIndex = Number.parseInt(match[2] ?? "", 10);
1342
+ const root = input.cwd;
1343
+ if (!isGitRepository(root)) {
1344
+ return { kind: "error", message: `dispatch: apply_winner requires a git repository at ${root}` };
1345
+ }
1346
+ recoverCleanupReadyCompeteGroups(root);
1347
+ const ownership = loadCompeteGroup(root, group);
1348
+ if (ownership === null) {
1349
+ return {
1350
+ kind: "error",
1351
+ message: `dispatch: winner branch ${branch} has no matching compete ownership manifest; refusing to merge or delete unproven state`
1352
+ };
1353
+ }
1354
+ if (ownership.state !== "winner-preserved" || ownership.winnerIndex !== winnerIndex) {
1355
+ return {
1356
+ kind: "error",
1357
+ message: `dispatch: winner branch ${branch} does not match the preserved winner recorded for compete group ${group}`
1358
+ };
1359
+ }
1360
+ const winnerDecision = readGateDecisionArtifacts(group).filter(
1361
+ (entry) => entry.artifact.outcome === "winner" && entry.artifact.winner?.index === winnerIndex && entry.artifact.winner.branch === branch
1362
+ ).sort(
1363
+ (left, right) => left.artifact.createdAt.localeCompare(right.artifact.createdAt) || left.artifact.id.localeCompare(right.artifact.id)
1364
+ ).at(-1);
1365
+ if (winnerDecision === void 0 || winnerDecision.artifact.winner === void 0) {
1366
+ return {
1367
+ kind: "error",
1368
+ message: `dispatch: winner branch ${branch} has no integrity-valid judge decision; refusing an unauditable confirmation`
1369
+ };
1370
+ }
1371
+ const winnerRef = winnerDecision.artifact.winner;
1372
+ const protectedChanges = protectedPathsChangedByCompeteBranch(root, branch, protectedPaths);
1373
+ if (protectedChanges.length > 0) {
1374
+ return {
1375
+ kind: "error",
1376
+ message: `dispatch: winner branch ${branch} changes protected artifact(s) and cannot be merged: ${protectedChanges.join(", ")}`
1377
+ };
1378
+ }
1379
+ const writeConfirmation = () => materializePendingGateDecision(
1380
+ stagePendingGateDecision({
1381
+ group,
1382
+ topology: "compete",
1383
+ cycle: 1,
1384
+ outcome: authority.outcome,
1385
+ subjects: [winnerRef.subject],
1386
+ winner: winnerRef,
1387
+ confirmation: {
1388
+ id: winnerDecision.artifact.id,
1389
+ digest: winnerDecision.artifact.integrity.digest
1390
+ },
1391
+ detail: authority.outcome === "operator-confirmed" ? `operator confirmation ${authority.requestId} (${authority.requestedBy}) approved ${branch} under dispatch plan ${planHash}` : `full-auto applied ${branch} under dispatch plan ${planHash}`
1392
+ })
1393
+ );
1394
+ let confirmation = null;
1395
+ if (authority.outcome === "operator-confirmed") {
1396
+ try {
1397
+ confirmation = writeConfirmation();
1398
+ } catch (err) {
1399
+ return {
1400
+ kind: "error",
1401
+ message: `dispatch: could not persist operator confirmation for ${branch}: ${competeErrorMessage(err)}`
1402
+ };
1403
+ }
1404
+ }
1405
+ const merge = mergeWinner(root, branch);
1406
+ if (!merge.ok) {
1407
+ return {
1408
+ kind: "error",
1409
+ message: `dispatch: winner branch ${branch} could not be merged: ${merge.reason}; the branch is preserved`,
1410
+ ...confirmation !== null ? { details: { confirmationPath: confirmation.path, confirmationId: confirmation.artifact.id } } : {}
1411
+ };
1412
+ }
1413
+ if (authority.outcome === "full-auto-applied") {
1414
+ try {
1415
+ confirmation = writeConfirmation();
1416
+ } catch (err) {
1417
+ return {
1418
+ kind: "error",
1419
+ message: `dispatch: winner branch ${branch} was merged, but full-auto application evidence failed: ${competeErrorMessage(err)}; branch is preserved for recovery`,
1420
+ details: { mode: "apply_winner", branch, group, applied: true, evidencePending: true }
1421
+ };
1422
+ }
1423
+ }
1424
+ if (confirmation === null) {
1425
+ return { kind: "error", message: `dispatch: winner branch ${branch} was merged without confirmation evidence` };
1426
+ }
1427
+ try {
1428
+ const cleanupOwnership = markCompeteGroupCleanupReady(ownership);
1429
+ cleanupCompeteGroup(cleanupOwnership);
1430
+ } catch (err) {
1431
+ return {
1432
+ kind: "error",
1433
+ message: `dispatch: winner branch ${branch} was merged, but compete cleanup failed: ${competeErrorMessage(err)}`,
1434
+ details: {
1435
+ mode: "apply_winner",
1436
+ branch,
1437
+ group,
1438
+ applied: true,
1439
+ cleanupPending: true,
1440
+ confirmationPath: confirmation.path,
1441
+ confirmationId: confirmation.artifact.id
1442
+ }
1443
+ };
1444
+ }
1445
+ return {
1446
+ kind: "ok",
1447
+ output: `winner ${branch} merged into the current branch; compete group ${group} cleaned up`,
1448
+ details: {
1449
+ mode: "apply_winner",
1450
+ branch,
1451
+ group,
1452
+ confirmationPath: confirmation.path,
1453
+ confirmationId: confirmation.artifact.id
1454
+ }
1455
+ };
1456
+ }
1457
+ function reviewGateResult(deps, outcome, maxOutputBytes) {
1458
+ const body = formatDispatchOutput("review", outcome.runs, maxOutputBytes);
1459
+ const details = {
1460
+ ...dispatchDetails(deps, "review", outcome.runs),
1461
+ gate: {
1462
+ verdict: outcome.verdict,
1463
+ cycles: outcome.cycles,
1464
+ decisions: outcome.decisions.map(({ artifact, path }) => ({
1465
+ id: artifact.id,
1466
+ outcome: artifact.outcome,
1467
+ path,
1468
+ digest: artifact.integrity.digest
1469
+ })),
1470
+ ...outcome.needsDecision !== void 0 ? { needsDecision: outcome.needsDecision } : {}
1471
+ }
1472
+ };
1473
+ if (outcome.verdict === "pass") {
1474
+ return { kind: "ok", output: `review gate passed after ${outcome.cycles} cycle(s)
1475
+
1476
+ ${body}`, details };
1477
+ }
1478
+ if (outcome.verdict === "fail") {
1479
+ return { kind: "error", message: `dispatch: review gate failed (reviewer verdict fail)
1480
+
1481
+ ${body}`, details };
1482
+ }
1483
+ return {
1484
+ kind: "error",
1485
+ message: `dispatch: review gate needs an operator decision: ${outcome.needsDecision ?? "no verdict"}
1486
+
1487
+ ${body}`,
1488
+ details
1489
+ };
1490
+ }
1491
+ function competeResult(deps, outcome, autonomy, maxOutputBytes) {
1492
+ const board = outcome.board ?? null;
1493
+ const body = formatDispatchOutput("compete", outcome.runs, maxOutputBytes, board);
1494
+ const details = {
1495
+ ...dispatchDetails(deps, "compete", outcome.runs, board),
1496
+ compete: {
1497
+ group: outcome.group,
1498
+ winner: outcome.winner,
1499
+ decisions: outcome.decisions.map(({ artifact, path }) => ({
1500
+ id: artifact.id,
1501
+ outcome: artifact.outcome,
1502
+ path,
1503
+ digest: artifact.integrity.digest
1504
+ })),
1505
+ ...outcome.needsDecision !== void 0 ? { needsDecision: outcome.needsDecision } : {}
1506
+ }
1507
+ };
1508
+ if (outcome.winner?.applied === true) {
1509
+ return {
1510
+ kind: "ok",
1511
+ output: `compete winner candidate ${outcome.winner.index} applied (branch ${outcome.winner.branch} merged)
1512
+
1513
+ ${body}`,
1514
+ details
1515
+ };
1516
+ }
1517
+ if (outcome.winner !== null && outcome.needsDecision === void 0) {
1518
+ const lines = [
1519
+ `compete winner: candidate ${outcome.winner.index} (branch ${outcome.winner.branch}), preserved for confirmation at autonomy ${autonomy}`,
1520
+ `Apply it with dispatch apply_winner={branch: "${outcome.winner.branch}"}; the approval prompt is the winner confirmation. Losing candidates were cleaned up.`
1521
+ ];
1522
+ return { kind: "ok", output: `${lines.join("\n")}
1523
+
1524
+ ${body}`, details };
1525
+ }
1526
+ return {
1527
+ kind: "error",
1528
+ message: `dispatch: compete needs an operator decision: ${outcome.needsDecision ?? "no winner"}
1529
+
1530
+ ${body}`,
1531
+ details
1532
+ };
1533
+ }
1534
+ async function runDispatchTool(inputDeps, admissionState, args, options) {
1535
+ const deps = {
1536
+ ...inputDeps,
1537
+ runEvents: inputDeps.runEvents ?? createDispatchRunEventRegistry()
1538
+ };
1539
+ const snapshot = admissionState.trustedExecutionSnapshots.get(args);
1540
+ if (snapshot?.kind === "list") {
1541
+ const catalog = deps.getAgentCatalog?.().trim() ?? "";
1542
+ if (catalog.length === 0) {
1543
+ return { kind: "error", message: "dispatch: no agent catalog is available in this context" };
1544
+ }
1545
+ return { kind: "ok", output: catalog };
1546
+ }
1547
+ try {
1548
+ recoverPendingGateEvidence(deps);
1549
+ } catch (error) {
1550
+ return {
1551
+ kind: "error",
1552
+ message: `dispatch: pending gate evidence recovery failed closed: ${competeErrorMessage(error)}`
1553
+ };
1554
+ }
1555
+ if (snapshot?.kind === "apply-winner") {
1556
+ const trustedWinnerPlan = admissionState.trustedResolvedPlans.get(args);
1557
+ if (trustedWinnerPlan === void 0) {
1558
+ return { kind: "error", message: "dispatch: trusted winner-application plan is unavailable" };
1559
+ }
1560
+ const confirmation = trustedWinnerPlan.confirmation;
1561
+ if (confirmation !== void 0) {
1562
+ if (snapshot.branch !== confirmation.branch || confirmation.cwd !== void 0 && snapshot.cwd !== confirmation.cwd) {
1563
+ return { kind: "error", message: "dispatch: winner destination differs from the approved plan" };
1564
+ }
1565
+ }
1566
+ const approval = options?.approval;
1567
+ const authority = approval?.actionClass === "dispatch" ? {
1568
+ outcome: "operator-confirmed",
1569
+ requestId: approval.requestId,
1570
+ requestedBy: approval.requestedBy
1571
+ } : deps.getAutonomy?.() === "full-auto" ? { outcome: "full-auto-applied" } : null;
1572
+ let protectedPaths;
1573
+ try {
1574
+ protectedPaths = deps.dispatch.protectedArtifactState?.().artifacts.map((artifact) => artifact.path) ?? [];
1575
+ } catch (error) {
1576
+ return {
1577
+ kind: "error",
1578
+ message: `dispatch: protected artifact state is unavailable: ${competeErrorMessage(error)}`
1579
+ };
1580
+ }
1581
+ const planHash = describeDispatchPlan({
1582
+ apply_winner: { branch: snapshot.branch, cwd: snapshot.cwd },
1583
+ [RESOLVED_DISPATCH_PLAN_ARGUMENT]: trustedWinnerPlan
1584
+ }).hash;
1585
+ return runApplyWinner(snapshot, planHash, authority, deps.competeWorktrees?.mergeWinner, protectedPaths);
1586
+ }
1587
+ if (snapshot?.kind !== "dispatch") {
1588
+ return { kind: "error", message: "dispatch: trusted execution admission snapshot is unavailable" };
1589
+ }
1590
+ const mode = snapshot.mode;
1591
+ if (options?.signal?.aborted) return { kind: "error", message: "dispatch: aborted" };
1592
+ const maxOutputBytes = snapshot.maxOutputBytes;
1593
+ const timeoutMs = snapshot.timeoutMs;
1594
+ let review = snapshot.review === void 0 ? void 0 : structuredClone(snapshot.review);
1595
+ const autonomy = deps.getAutonomy?.() ?? "auto-edit";
1596
+ const authenticatedApproval = options?.approval?.actionClass === "dispatch" ? options.approval : void 0;
1597
+ const trustedResolvedPlan = admissionState.trustedResolvedPlans.get(args) ?? null;
1598
+ const planView = snapshot.planView;
1599
+ const resolvedPlan = trustedResolvedPlan;
1600
+ const reservationOwnerId = admissionState.trustedReservationOwners.get(args);
1601
+ const parentToolCallId = options?.toolCallId;
1602
+ let requests = parentToolCallId === void 0 || parentToolCallId.length === 0 ? structuredClone(snapshot.requests) : snapshot.requests.map((request) => ({ ...structuredClone(request), parentToolCallId }));
1603
+ if (planView.planScale && deps.dispatch.preview !== void 0 && resolvedPlan === null) {
1604
+ return { kind: "error", message: "dispatch: resolved plan is missing after admission" };
1605
+ }
1606
+ if (planView.planScale && autonomy !== "full-auto" && authenticatedApproval === void 0) {
1607
+ return {
1608
+ kind: "error",
1609
+ message: "dispatch: resolved plan requires a registry-authenticated operator approval"
1610
+ };
1611
+ }
1612
+ if (resolvedPlan !== null) {
1613
+ const primaryRole = review !== void 0 ? "builder" : mode === "compete" ? "candidate" : "task";
1614
+ const primaryTasks = resolvedPlan.tasks.filter((task) => task.role === primaryRole);
1615
+ const executionTasks = primaryRole === "task" ? primaryTasks : primaryTasks.slice(0, 1);
1616
+ if (executionTasks.length !== requests.length) {
1617
+ return {
1618
+ kind: "error",
1619
+ message: `dispatch: resolved plan has ${executionTasks.length} primary task(s), expected ${requests.length}`
1620
+ };
1621
+ }
1622
+ requests = requests.map((request, index) => {
1623
+ const task = executionTasks[index];
1624
+ const pinned = withResolvedPlanTaskPin(request, task);
1625
+ return reservationOwnerId !== void 0 && task?.role !== void 0 && task.position !== void 0 ? {
1626
+ ...pinned,
1627
+ reservation: {
1628
+ ownerId: reservationOwnerId,
1629
+ memberId: task.stepId ?? `${task.role}-${task.position}`
1630
+ }
1631
+ } : pinned;
1632
+ });
1633
+ if (review !== void 0) {
1634
+ const reviewer = resolvedPlan.tasks.find((task) => task.role === "reviewer");
1635
+ if (reviewer === void 0) {
1636
+ return { kind: "error", message: "dispatch: resolved review plan has no reviewer task" };
1637
+ }
1638
+ review = {
1639
+ ...review,
1640
+ reviewer: reviewer.agent,
1641
+ target: reviewer.target,
1642
+ model: reviewer.model,
1643
+ node: reviewer.node,
1644
+ resolvedTasks: resolvedPlan.tasks
1645
+ };
1646
+ }
1647
+ }
1648
+ if (planView.planScale) {
1649
+ const plan = {
1650
+ hash: planView.hash,
1651
+ topology: planView.topology,
1652
+ taskCount: planView.taskCount,
1653
+ approval: authenticatedApproval !== void 0 ? "operator" : "full-auto",
1654
+ source: resolvedPlan?.source === null || resolvedPlan?.source === void 0 ? null : {
1655
+ kind: "scout-transition",
1656
+ runId: resolvedPlan.source.runId,
1657
+ receiptDigest: resolvedPlan.source.receiptDigest,
1658
+ executionPlanHash: resolvedPlan.source.executionPlanHash
1659
+ },
1660
+ ...authenticatedApproval !== void 0 ? {
1661
+ approvalRequestId: authenticatedApproval.requestId,
1662
+ approvalRequestedBy: authenticatedApproval.requestedBy
1663
+ } : {},
1664
+ ...planView.costCeilingUsd !== void 0 ? { costCeilingUsd: planView.costCeilingUsd } : {}
1665
+ };
1666
+ requests = requests.map((request) => ({ ...request, plan }));
1667
+ }
1668
+ const backgroundRefusal = resolvedPlan?.topology === "fleet" ? "a Scout dependency plan drives its stages from this turn" : mode === "compete" ? "compete holds its judge gate in this turn" : review !== void 0 ? "a review gate holds its cycle state in this turn" : mode === "pipeline" && requests.length > 1 ? "a pipeline threads each step's output through this turn" : timeoutMs !== void 0 ? `this call set timeout_ms=${timeoutMs} and nobody would be left to enforce the deadline` : deps.dispatch.detached === void 0 ? "detached batch records are unavailable in this context" : null;
1669
+ const background = createBackgroundSwitch();
1670
+ const backgroundLabel = resolvedPlan?.topology === "fleet" ? "fleet" : review !== void 0 ? "review" : mode;
1671
+ const releaseBackground = registerBackgroundControl(deps, options?.toolCallId, {
1672
+ label: `dispatch (${backgroundLabel})`,
1673
+ refusal: backgroundRefusal,
1674
+ fire: () => background.fire()
1675
+ });
1676
+ try {
1677
+ if (resolvedPlan?.topology === "fleet") {
1678
+ const executionPlan = admissionState.trustedExecutionPlans.get(args);
1679
+ if (executionPlan === void 0 || resolvedPlan.source === null || resolvedPlan.deadlineMs === null || reservationOwnerId === void 0) {
1680
+ return { kind: "error", message: "dispatch: trusted Scout dependency plan is unavailable" };
1681
+ }
1682
+ if (executionPlan.hash !== resolvedPlan.source.executionPlanHash) {
1683
+ return { kind: "error", message: "dispatch: Scout dependency plan hash drifted after admission" };
1684
+ }
1685
+ if (!scoutPlanAuthorityGranted(resolvedPlan, authenticatedApproval !== void 0, autonomy === "full-auto")) {
1686
+ return { kind: "error", message: "dispatch: Scout dependency plan lacks authenticated authority grants" };
1687
+ }
1688
+ const executable = compileExecutionPlan({
1689
+ topology: executionPlan.topology,
1690
+ rootTask: executionPlan.rootTask,
1691
+ maxWorkers: executionPlan.maxWorkers,
1692
+ onFailure: executionPlan.onFailure,
1693
+ steps: requireAgentSteps(executionPlan.steps).map((step) => ({
1694
+ ...step,
1695
+ approvedAuthority: step.requestedAuthority
1696
+ }))
1697
+ });
1698
+ try {
1699
+ const outcome = await runScoutContinuationPlan({
1700
+ dispatch: deps.dispatch,
1701
+ plan: executable,
1702
+ artifact: resolvedPlan,
1703
+ requests,
1704
+ reservationOwnerId,
1705
+ ...options?.signal === void 0 ? {} : { signal: options.signal },
1706
+ register: (handle, agentId) => deps.runEvents.registerSingle(handle, agentId, fallbackProgressBus(deps)).completion,
1707
+ complete: (receipt, summary) => {
1708
+ const completed = completeRun(deps, receipt, summary);
1709
+ return { value: completed, integrityValid: completed.integrity.ok };
1710
+ }
1711
+ });
1712
+ const output = formatDispatchOutput("fleet", outcome.runs, maxOutputBytes);
1713
+ const details = {
1714
+ ...dispatchDetails(deps, "fleet", outcome.runs),
1715
+ executionPlanHash: executable.hash,
1716
+ skippedSteps: outcome.skipped
1717
+ };
1718
+ const failed = outcome.runs.some((run) => run.receipt.exitCode !== 0) || outcome.skipped.length > 0;
1719
+ return failed ? { kind: "error", message: output, details } : { kind: "ok", output, details };
1720
+ } catch (error) {
1721
+ return {
1722
+ kind: "error",
1723
+ message: `dispatch: Scout dependency plan failed: ${error instanceof Error ? error.message : String(error)}`
1724
+ };
1725
+ }
1726
+ }
1727
+ if (snapshot.detach) {
1728
+ if (mode !== "parallel") {
1729
+ return { kind: "error", message: `dispatch: detach only supports parallel mode; got '${mode}'` };
1730
+ }
1731
+ if (review !== void 0) {
1732
+ return {
1733
+ kind: "error",
1734
+ message: "dispatch: detach cannot combine with a review gate; run gated dispatch attached"
1735
+ };
1736
+ }
1737
+ if (timeoutMs !== void 0) {
1738
+ return {
1739
+ kind: "error",
1740
+ message: 'dispatch: detach cannot enforce timeout_ms because no caller waits on the runs; monitor(mode="wait") only observes for a bounded time, and steer(action="cancel") stops a run'
1741
+ };
1742
+ }
1743
+ try {
1744
+ return await runDetached(deps, requests, options?.sessionId ?? null);
1745
+ } catch (err) {
1746
+ return { kind: "error", message: dispatchErrorMessage(err) };
1747
+ }
1748
+ }
1749
+ if (mode === "compete") {
1750
+ if (requests.length !== 1 || requests[0] === void 0) {
1751
+ return { kind: "error", message: COMPETE_SINGLE_TASK_MESSAGE };
1752
+ }
1753
+ if (review !== void 0) {
1754
+ return { kind: "error", message: COMPETE_NO_REVIEW_MESSAGE };
1755
+ }
1756
+ if (snapshot.compete === void 0) {
1757
+ return { kind: "error", message: "dispatch: trusted compete settings are unavailable" };
1758
+ }
1759
+ let compete = structuredClone(snapshot.compete);
1760
+ if (resolvedPlan !== null) {
1761
+ const judge = resolvedPlan.tasks.find((task) => task.role === "judge");
1762
+ if (judge === void 0) {
1763
+ return { kind: "error", message: "dispatch: resolved compete plan has no judge task" };
1764
+ }
1765
+ compete = {
1766
+ ...compete,
1767
+ judge: { agent: judge.agent, target: judge.target, model: judge.model, node: judge.node },
1768
+ resolvedTasks: resolvedPlan.tasks
1769
+ };
1770
+ }
1771
+ try {
1772
+ const outcome = await runCompete(deps, requests[0], compete, autonomy, timeoutMs, options?.signal);
1773
+ return competeResult(deps, outcome, autonomy, maxOutputBytes);
1774
+ } catch (err) {
1775
+ return { kind: "error", message: dispatchErrorMessage(err) };
1776
+ }
1777
+ }
1778
+ if (review !== void 0) {
1779
+ if (requests.length !== 1 || requests[0] === void 0) {
1780
+ return { kind: "error", message: REVIEW_SINGLE_TASK_MESSAGE };
1781
+ }
1782
+ if (mode !== "parallel") {
1783
+ return { kind: "error", message: `dispatch: review does not combine with mode=${mode}` };
1784
+ }
1785
+ try {
1786
+ const outcome = await runReviewGated(deps, requests[0], review, timeoutMs, options?.signal);
1787
+ return reviewGateResult(deps, outcome, maxOutputBytes);
1788
+ } catch (err) {
1789
+ return { kind: "error", message: dispatchErrorMessage(err) };
1790
+ }
1791
+ }
1792
+ try {
1793
+ let runs;
1794
+ let board = null;
1795
+ if (mode === "pipeline" && requests.length > 1) {
1796
+ runs = await runPipeline(deps, requests, timeoutMs, options?.signal);
1797
+ } else if (mode === "sequential" || mode === "pipeline" || requests.length === 1) {
1798
+ runs = await runSequential(deps, requests, mode, timeoutMs, options?.signal, background);
1799
+ } else {
1800
+ const batch = await runBatch(deps, requests, timeoutMs, options?.signal, background);
1801
+ runs = batch.runs;
1802
+ board = batch.board;
1803
+ }
1804
+ const output = formatDispatchOutput(mode, runs, maxOutputBytes, board);
1805
+ const details = dispatchDetails(deps, mode, runs, board);
1806
+ const failed = runs.filter((run) => run.receipt.exitCode !== 0);
1807
+ if (failed.length > 0) return { kind: "error", message: output, details };
1808
+ return { kind: "ok", output, details };
1809
+ } catch (err) {
1810
+ if (err instanceof DispatchBackgroundedError) {
1811
+ return await backgroundedDispatchResult(deps, err, mode, options?.sessionId ?? null);
1812
+ }
1813
+ if (err instanceof PipelineHaltError) {
1814
+ const haltMessage = `dispatch: ${err.message}`;
1815
+ const output = formatDispatchOutput("pipeline", err.runs, maxOutputBytes);
1816
+ return {
1817
+ kind: "error",
1818
+ message: `${haltMessage}
1819
+
1820
+ ${output}`,
1821
+ details: dispatchDetails(deps, "pipeline", err.runs)
1822
+ };
1823
+ }
1824
+ return { kind: "error", message: dispatchErrorMessage(err) };
1825
+ }
1826
+ } finally {
1827
+ releaseBackground();
1828
+ }
1829
+ }
1830
+ async function runSequential(deps, requests, mode, timeoutMs, signal, background) {
1831
+ const runs = [];
1832
+ let expired = false;
1833
+ let activeRunId = null;
1834
+ const abortActive = (bySignal) => {
1835
+ expired = true;
1836
+ if (activeRunId !== null) {
1837
+ deps.dispatch.abort(
1838
+ activeRunId,
1839
+ bySignal ? void 0 : { cause: "timeout", detail: `timed out after ${timeoutMs}ms` }
1840
+ );
1841
+ }
1842
+ };
1843
+ const onSignalAbort = () => abortActive(true);
1844
+ const timer = timeoutMs !== void 0 ? setTimeout(() => abortActive(false), timeoutMs) : null;
1845
+ signal?.addEventListener("abort", onSignalAbort, { once: true });
1846
+ try {
1847
+ for (const [index, request] of requests.entries()) {
1848
+ if (expired || signal?.aborted) {
1849
+ throw new Error(
1850
+ `${mode} dispatch stopped after ${runs.length}/${requests.length} task(s): ${signal?.aborted ? "aborted" : `timed out after ${timeoutMs}ms`}`
1851
+ );
1852
+ }
1853
+ const handle = await deps.dispatch.dispatch(request);
1854
+ activeRunId = handle.runId;
1855
+ const registered = deps.runEvents.registerSingle(handle, request.agentId, fallbackProgressBus(deps));
1856
+ const completion = registered.completion.then((value) => ({ kind: "completed", value }));
1857
+ const settled = await Promise.race([completion, background.requested.then(() => ({ kind: "background" }))]);
1858
+ if (settled.kind === "background") {
1859
+ completion.catch(() => {
1860
+ });
1861
+ throw new DispatchBackgroundedError(
1862
+ newGateGroupId("batch"),
1863
+ [{ runId: handle.runId, assignmentId: handle.runId, agentId: request.agentId }],
1864
+ [...runs],
1865
+ requests.slice(index + 1).map((later) => later.agentId)
1866
+ );
1867
+ }
1868
+ activeRunId = null;
1869
+ runs.push(completeRun(deps, settled.value.receipt, settled.value.summary));
1870
+ }
1871
+ return runs;
1872
+ } finally {
1873
+ if (timer) clearTimeout(timer);
1874
+ signal?.removeEventListener("abort", onSignalAbort);
1875
+ }
1876
+ }
1877
+ function isPipelineStepFailure(receipt) {
1878
+ if (receipt.exitCode !== 0) return true;
1879
+ return receipt.outcome !== void 0 && receipt.outcome !== "succeeded";
1880
+ }
1881
+ function pipelineFailureReason(receipt) {
1882
+ if (receipt.outcome !== void 0 && receipt.outcome !== "succeeded") return `outcome=${receipt.outcome}`;
1883
+ return `exit=${receipt.exitCode}`;
1884
+ }
1885
+ async function runPipeline(deps, requests, timeoutMs, signal) {
1886
+ const runs = [];
1887
+ let expired = false;
1888
+ let activeRunId = null;
1889
+ const abortActive = (bySignal) => {
1890
+ expired = true;
1891
+ if (activeRunId !== null) {
1892
+ deps.dispatch.abort(
1893
+ activeRunId,
1894
+ bySignal ? void 0 : { cause: "timeout", detail: `timed out after ${timeoutMs}ms` }
1895
+ );
1896
+ }
1897
+ };
1898
+ const onSignalAbort = () => abortActive(true);
1899
+ const timer = timeoutMs !== void 0 ? setTimeout(() => abortActive(false), timeoutMs) : null;
1900
+ signal?.addEventListener("abort", onSignalAbort, { once: true });
1901
+ try {
1902
+ let previous = null;
1903
+ for (const [index, base] of requests.entries()) {
1904
+ if (expired || signal?.aborted) {
1905
+ throw new Error(
1906
+ `pipeline dispatch stopped after ${runs.length}/${requests.length} task(s): ${signal?.aborted ? "aborted" : `timed out after ${timeoutMs}ms`}`
1907
+ );
1908
+ }
1909
+ const request = previous === null ? base : { ...base, pipelineInput: { fromRunId: previous.runId, position: index + 1, text: previous.text } };
1910
+ const handle = await deps.dispatch.dispatch(request);
1911
+ activeRunId = handle.runId;
1912
+ const registered = deps.runEvents.registerSingle(handle, request.agentId, fallbackProgressBus(deps));
1913
+ const { receipt, summary } = await registered.completion;
1914
+ activeRunId = null;
1915
+ runs.push(completeRun(deps, receipt, summary));
1916
+ if (isPipelineStepFailure(receipt)) {
1917
+ const skipped = requests.length - (index + 1);
1918
+ throw new PipelineHaltError(
1919
+ `pipeline dispatch halted at step ${index + 1}/${requests.length} (run ${receipt.runId}, ${pipelineFailureReason(receipt)}); skipped ${skipped} later step(s)`,
1920
+ [...runs]
1921
+ );
1922
+ }
1923
+ previous = {
1924
+ runId: receipt.runId,
1925
+ text: receipt.output?.state === "final" ? receipt.output.text : ""
1926
+ };
1927
+ }
1928
+ return runs;
1929
+ } finally {
1930
+ if (timer) clearTimeout(timer);
1931
+ signal?.removeEventListener("abort", onSignalAbort);
1932
+ }
1933
+ }
1934
+ async function runBatch(deps, requests, timeoutMs, signal, background) {
1935
+ const ledgerId = newGateGroupId("ledger");
1936
+ await openAgentLedger(ledgerId);
1937
+ const ledgered = requests.map((request) => ({ ...request, ledger: { id: ledgerId, sequence: 0 } }));
1938
+ const handle = await deps.dispatch.dispatchBatch(ledgered);
1939
+ const registered = deps.runEvents.registerBatch(
1940
+ handle,
1941
+ ledgered.map((request) => request.agentId),
1942
+ fallbackProgressBus(deps)
1943
+ );
1944
+ const abort = (bySignal) => {
1945
+ const reason = bySignal ? void 0 : { cause: "timeout", detail: `timed out after ${timeoutMs}ms` };
1946
+ for (const runId of handle.assignmentIds) deps.dispatch.abort(runId, reason);
1947
+ };
1948
+ const onSignalAbort = () => abort(true);
1949
+ const timer = timeoutMs !== void 0 ? setTimeout(() => abort(false), timeoutMs) : null;
1950
+ signal?.addEventListener("abort", onSignalAbort, { once: true });
1951
+ let backgrounded = false;
1952
+ try {
1953
+ const completion = registered.completion.then((value) => ({ kind: "completed", value }));
1954
+ const settled = await Promise.race([completion, background.requested.then(() => ({ kind: "background" }))]);
1955
+ if (settled.kind === "background") {
1956
+ completion.catch(() => {
1957
+ });
1958
+ backgrounded = true;
1959
+ throw new DispatchBackgroundedError(
1960
+ handle.batchId,
1961
+ handle.assignmentIds.map((runId, index) => ({
1962
+ runId,
1963
+ assignmentId: runId,
1964
+ agentId: ledgered[index]?.agentId ?? "unknown"
1965
+ })),
1966
+ [],
1967
+ [],
1968
+ ledgerId
1969
+ );
1970
+ }
1971
+ const { summaries, receipts } = settled.value;
1972
+ const board = renderAgentLedgerBoard(ledgerId);
1973
+ return {
1974
+ runs: receipts.map(
1975
+ (receipt) => completeRun(
1976
+ deps,
1977
+ receipt,
1978
+ summaries.get(receipt.runId) ?? {
1979
+ count: 0,
1980
+ types: [],
1981
+ lastAssistantText: "",
1982
+ terminalAttemptRunId: receipt.runId
1983
+ }
1984
+ )
1985
+ ),
1986
+ board
1987
+ };
1988
+ } finally {
1989
+ if (timer) clearTimeout(timer);
1990
+ signal?.removeEventListener("abort", onSignalAbort);
1991
+ if (!backgrounded) await closeAgentLedger(ledgerId);
1992
+ }
1993
+ }
1994
+ export {
1995
+ runDispatchTool
1996
+ };
1997
+ //# sourceMappingURL=dispatch-runner-QPRDDBDX.js.map