@iowarp/clio-coder 0.3.4 → 0.3.7

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 (376) hide show
  1. package/CHANGELOG.md +65 -2
  2. package/CONTRIBUTING.md +6 -6
  3. package/README.md +16 -5
  4. package/dist/{acp-S5R4RR5B.js → acp-SK4MD6MM.js} +11 -11
  5. package/dist/{agents-P6DMMVZY.js → agents-2FN2K6ME.js} +33 -26
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-2XCZLPKS.js → auth-QIYZWM5I.js} +15 -15
  8. package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
  9. package/dist/{chunk-VAWWTKDP.js → chunk-3HAPLH5M.js} +11 -11
  10. package/dist/{chunk-YCWGATWI.js → chunk-465YSENW.js} +3 -3
  11. package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
  12. package/dist/{chunk-UZHIZC5S.js → chunk-4DWFMQDR.js} +61 -76
  13. package/dist/{chunk-ZWMF7253.js → chunk-5C3AQNDW.js} +328 -9
  14. package/dist/{chunk-22NAGB7X.js → chunk-5C77SEEY.js} +5 -94
  15. package/dist/{chunk-WPQLXFOZ.js → chunk-5FR74PWO.js} +3 -2
  16. package/dist/{chunk-35MKKU5R.js → chunk-5UJ6ECTS.js} +18 -10
  17. package/dist/{chunk-BRXQQJFP.js → chunk-6M7VS3J3.js} +571 -50
  18. package/dist/{chunk-QQK64KLB.js → chunk-6TUKSZVF.js} +141 -23
  19. package/dist/{chunk-N4CZJQRK.js → chunk-AB4XIIVB.js} +8 -6
  20. package/dist/{chunk-KRPY7NTG.js → chunk-BMWK7ZIZ.js} +14 -20
  21. package/dist/{chunk-4BPJXDWC.js → chunk-C4JBQ5SR.js} +30 -14
  22. package/dist/{chunk-ZYKPLLNQ.js → chunk-CEYBNUGC.js} +821 -83
  23. package/dist/{chunk-VEZEGCGW.js → chunk-D4MDIG46.js} +20 -18
  24. package/dist/chunk-DJNLUABN.js +843 -0
  25. package/dist/{chunk-BP4OYD6A.js → chunk-DMD2AGVS.js} +21 -2
  26. package/dist/{chunk-KOHPCX4K.js → chunk-DOOEX22V.js} +2 -2
  27. package/dist/chunk-DQA7QLMD.js +123 -0
  28. package/dist/chunk-DR52UMZW.js +21 -0
  29. package/dist/{chunk-3HZ5RWN2.js → chunk-EBEFWSGL.js} +9 -7
  30. package/dist/{chunk-EDRHSCIE.js → chunk-EELBMBT6.js} +128 -13
  31. package/dist/{chunk-HV5X7OR2.js → chunk-EOOQZZDE.js} +16 -14
  32. package/dist/{chunk-WR67VIZY.js → chunk-FOT2FX5J.js} +63 -5
  33. package/dist/{chunk-BPGS2WCQ.js → chunk-GEYXPTRF.js} +2 -1
  34. package/dist/{chunk-FYYLNIL5.js → chunk-GH5622CP.js} +2 -2
  35. package/dist/{chunk-BEY543CS.js → chunk-GOXNB3AO.js} +5 -2
  36. package/dist/chunk-GWS3VEIW.js +195 -0
  37. package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
  38. package/dist/chunk-HWUFFB6L.js +83 -0
  39. package/dist/{chunk-X6COSD2O.js → chunk-J7PIKKWC.js} +8 -436
  40. package/dist/{chunk-NILBFAPG.js → chunk-JNXPYBB4.js} +2 -2
  41. package/dist/{chunk-4VP4KH3K.js → chunk-JRIO5UD2.js} +4 -4
  42. package/dist/{chunk-K6WL7QZT.js → chunk-JTSEDYVQ.js} +7 -7
  43. package/dist/{chunk-QKMUKYO7.js → chunk-KCMKRQX4.js} +236 -84
  44. package/dist/chunk-KZ2H5X4G.js +1026 -0
  45. package/dist/{chunk-A2GZF7DC.js → chunk-LADCF22A.js} +13 -13
  46. package/dist/chunk-LCGCVYZ4.js +57 -0
  47. package/dist/chunk-M4AKACEO.js +382 -0
  48. package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
  49. package/dist/{chunk-4JUF2NNX.js → chunk-MXI6J5JF.js} +7 -7
  50. package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
  51. package/dist/{chunk-TTNYS3EA.js → chunk-OB5HIGJY.js} +1 -1
  52. package/dist/{chunk-7RXG6QRZ.js → chunk-OBMAI2DP.js} +61 -840
  53. package/dist/{chunk-5M54SPOL.js → chunk-ODFEOB4F.js} +161 -5
  54. package/dist/chunk-PD3MESLB.js +242 -0
  55. package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
  56. package/dist/{chunk-VMNQ6OZA.js → chunk-QCTRSGHQ.js} +963 -786
  57. package/dist/chunk-RVG5JXAL.js +41 -0
  58. package/dist/{chunk-RD5U66HV.js → chunk-SROCI7ZU.js} +7 -7
  59. package/dist/{chunk-MFFY33HR.js → chunk-THKY7CD7.js} +466 -205
  60. package/dist/{chunk-34475P3I.js → chunk-TSHXZTOQ.js} +5 -4
  61. package/dist/{chunk-PCZJO5TI.js → chunk-UFQ3F4FW.js} +13 -178
  62. package/dist/{chunk-AD2SYQYC.js → chunk-UHXRNZ2J.js} +121 -3
  63. package/dist/chunk-UND3GU2L.js +103 -0
  64. package/dist/{chunk-QQL5RT5M.js → chunk-UUANF5CR.js} +2323 -2114
  65. package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
  66. package/dist/chunk-UVDSQ6LW.js +472 -0
  67. package/dist/{chunk-QWU7ZBO7.js → chunk-VQNODYQ4.js} +215 -56
  68. package/dist/chunk-VREKEFLL.js +37 -0
  69. package/dist/{chunk-2TZWSW76.js → chunk-WHGPSPT5.js} +2 -2
  70. package/dist/{chunk-TW3WDMVS.js → chunk-WHJYKASB.js} +2 -2
  71. package/dist/{chunk-MEQ45TQ4.js → chunk-WJHBC77E.js} +21 -7
  72. package/dist/{chunk-HXG4IURW.js → chunk-X2KV5FXT.js} +2 -2
  73. package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
  74. package/dist/{chunk-2LZI5CAG.js → chunk-XEGB6BCN.js} +228 -36
  75. package/dist/{chunk-E25LMLRW.js → chunk-YD734TPH.js} +2 -2
  76. package/dist/{verifiers-4UUM6TEE.js → chunk-YTYFXUI3.js} +121 -372
  77. package/dist/{chunk-3JLKSKD7.js → chunk-ZGH7FGS5.js} +17 -7
  78. package/dist/{chunk-VSNATDE6.js → chunk-ZZMN5OM4.js} +2 -2
  79. package/dist/cli/index.js +34 -32
  80. package/dist/{clio-J5JIOIDS.js → clio-WBVQEBKO.js} +7 -7
  81. package/dist/{code-nav-AXCXSBHX.js → code-nav-FGGFIE7L.js} +7 -7
  82. package/dist/codewiki/build-worker.js +4 -4
  83. package/dist/{components-KELWS457.js → components-F7OEATSO.js} +5 -5
  84. package/dist/{config-OEBMIN2U.js → config-TRBL3RCF.js} +48 -41
  85. package/dist/{configure-PUQOSIXQ.js → configure-OLCVPHNM.js} +17 -17
  86. package/dist/{context-URSXPBCK.js → context-MJIJ6GOX.js} +12 -12
  87. package/dist/{context-EKDCKUUZ.js → context-WFPKQSM6.js} +26 -9
  88. package/dist/{context-MGSE4Z2T.js → context-XEWE3MOJ.js} +44 -37
  89. package/dist/{context-clear-KDAJRNUK.js → context-clear-KNOS2JPB.js} +44 -37
  90. package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
  91. package/dist/{context-working-set-SBKMPPI2.js → context-working-set-EUXAZI6N.js} +14 -13
  92. package/dist/{dispatch-runner-MSWN72NK.js → dispatch-runner-B7MTOVKL.js} +321 -60
  93. package/dist/{docs-2C2LTVT2.js → docs-FLJTIDSE.js} +5 -5
  94. package/dist/{doctor-7BSE27PJ.js → doctor-RN4YKO2X.js} +15 -15
  95. package/dist/{eval-IZGDOO4H.js → eval-RUBJVSNQ.js} +52 -236
  96. package/dist/{evidence-SR7WXB5B.js → evidence-JZNBUOQZ.js} +39 -33
  97. package/dist/{evolve-K7VE2CBX.js → evolve-FJVC4KKI.js} +39 -33
  98. package/dist/{extensions-QVDOHDGJ.js → extensions-IQL36S7K.js} +5 -5
  99. package/dist/{fleet-7XMJNQNF.js → fleet-BDKYJFCP.js} +243 -370
  100. package/dist/fleet-commands-ZFIWZSB3.js +70 -0
  101. package/dist/fleet-graph-Y6HPXIVF.js +125 -0
  102. package/dist/fleet-new-RDVJLHHH.js +48 -0
  103. package/dist/{fleet-preflight-AQNAH644.js → fleet-preflight-BHSNPBMH.js} +2 -2
  104. package/dist/fleet-validate-BIYREGIK.js +79 -0
  105. package/dist/{init-JGNPAYXT.js → init-LQUB5COQ.js} +57 -48
  106. package/dist/library-NJAHIGG4.js +217 -0
  107. package/dist/memory-OG6HOYKM.js +472 -0
  108. package/dist/{models-ZMMLFJNN.js → models-5ZG5XY7J.js} +23 -22
  109. package/dist/{monitor-2F3T5KHP.js → monitor-TJ7AMTGB.js} +69 -35
  110. package/dist/{orchestrator-ORHT43JB.js → orchestrator-WZYB54DM.js} +4868 -1189
  111. package/dist/{paths-UXLN5YYZ.js → paths-XUC7GS6E.js} +5 -5
  112. package/dist/{reset-NXGTYNUO.js → reset-PXQT45IY.js} +8 -8
  113. package/dist/{run-RF4WJGMT.js → run-FQ74YF62.js} +82 -62
  114. package/dist/{share-UT3W6E4M.js → share-FW7SVCL3.js} +34 -10
  115. package/dist/{skills-PSACKC5Q.js → skills-7E7IRB3R.js} +25 -9
  116. package/dist/{skills-eval-WJSI55RZ.js → skills-eval-LI75W6OK.js} +43 -35
  117. package/dist/{targets-PIIRAOYS.js → targets-4CIFKCTW.js} +27 -24
  118. package/dist/{terminal-lease-ULWXWNVY.js → terminal-lease-WUZY7ZV5.js} +5 -4
  119. package/dist/{uninstall-FZCQCDKC.js → uninstall-7FV7IP4E.js} +5 -5
  120. package/dist/{upgrade-346TZ6AV.js → upgrade-K2HVIVMQ.js} +21 -20
  121. package/dist/{usage-6KKXR32N.js → usage-GTZELZQX.js} +159 -59
  122. package/dist/verifiers-RLAHT27O.js +336 -0
  123. package/dist/{verify-X5HDROLA.js → verify-BX3BRKH5.js} +7 -6
  124. package/dist/{wiki-generate-7STOCIFZ.js → wiki-generate-ASIFASCN.js} +58 -48
  125. package/dist/worker/entry.js +98 -84
  126. package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
  127. package/docs/README.md +4 -3
  128. package/docs/acp.md +1 -1
  129. package/docs/alcf-provider.md +1 -1
  130. package/docs/architecture.md +2 -2
  131. package/docs/artifact-placement.md +1 -2
  132. package/docs/artifact-versions.md +10 -6
  133. package/docs/built-in-agents.md +26 -2
  134. package/docs/capacity-and-scheduling.md +1 -1
  135. package/docs/commands-and-modes.md +90 -8
  136. package/docs/configuration-and-targets.md +90 -2
  137. package/docs/context-engine.md +4 -2
  138. package/docs/context-working-set.md +4 -4
  139. package/docs/development-pipeline.md +1 -1
  140. package/docs/dispatch-architecture-rationale.md +1 -1
  141. package/docs/documentation-coverage.md +4 -4
  142. package/docs/documentation-guide.md +4 -4
  143. package/docs/eval-runner.md +1 -1
  144. package/docs/evals-internal.md +4 -45
  145. package/docs/evidence-and-memory.md +70 -10
  146. package/docs/evolution.md +1 -1
  147. package/docs/exit-codes-and-output.md +4 -1
  148. package/docs/extensions-and-sharing.md +6 -2
  149. package/docs/fleet-demo-runbook.md +2 -2
  150. package/docs/fleet-dispatch.md +224 -11
  151. package/docs/git-commit-provenance.md +2 -2
  152. package/docs/glossary.md +1 -1
  153. package/docs/installation-and-lifecycle.md +2 -2
  154. package/docs/middleware-and-components.md +20 -2
  155. package/docs/model-catalog.md +1 -1
  156. package/docs/observability.md +55 -8
  157. package/docs/proactive-memory.md +26 -16
  158. package/docs/prompt-envelope-and-tools.md +4 -2
  159. package/docs/provider-adapter-cookbook.md +1 -1
  160. package/docs/release-cut-checklist.md +83 -65
  161. package/docs/resource-library.md +59 -0
  162. package/docs/safety-model.md +29 -7
  163. package/docs/scientific-validation.md +3 -3
  164. package/docs/session-lifecycle.md +37 -1
  165. package/docs/skills-marketplace.md +16 -3
  166. package/docs/tool-usage.md +14 -7
  167. package/docs/trace-store.md +1 -1
  168. package/docs/troubleshooting.md +1 -1
  169. package/docs/tui-design.md +38 -4
  170. package/docs/worker-dispatch-mechanics.md +3 -3
  171. package/package.json +7 -4
  172. package/src/cli/agents.ts +2 -3
  173. package/src/cli/argv.ts +14 -1
  174. package/src/cli/fleet-commands.ts +37 -0
  175. package/src/cli/fleet-graph.ts +102 -0
  176. package/src/cli/fleet-new.ts +36 -0
  177. package/src/cli/fleet-preflight.ts +121 -0
  178. package/src/cli/fleet-validate.ts +30 -0
  179. package/src/cli/fleet.ts +188 -335
  180. package/src/cli/index.ts +4 -2
  181. package/src/cli/library.ts +190 -0
  182. package/src/cli/memory.ts +272 -10
  183. package/src/cli/modes/json-stream.ts +2 -2
  184. package/src/cli/modes/print.ts +12 -1
  185. package/src/cli/run.ts +22 -2
  186. package/src/cli/share.ts +13 -1
  187. package/src/cli/targets.ts +12 -3
  188. package/src/cli/usage.ts +160 -20
  189. package/src/core/bus-events.ts +7 -0
  190. package/src/core/commit-attribution.ts +4 -4
  191. package/src/core/config.ts +130 -0
  192. package/src/core/defaults.ts +81 -0
  193. package/src/core/response-model-id.ts +134 -0
  194. package/src/core/toml.ts +62 -0
  195. package/src/core/workspace-files.ts +0 -1
  196. package/src/domains/agents/builtins/architect.md +2 -1
  197. package/src/domains/agents/builtins/oracle.md +33 -0
  198. package/src/domains/agents/catalog.ts +18 -5
  199. package/src/domains/agents/fleet-contract.ts +278 -16
  200. package/src/domains/agents/index.ts +14 -0
  201. package/src/domains/agents/recipe.ts +54 -14
  202. package/src/domains/agents/result-contract.ts +242 -5
  203. package/src/domains/config/classify.ts +4 -0
  204. package/src/domains/context/bootstrap.ts +36 -27
  205. package/src/domains/context/project-metadata.ts +19 -63
  206. package/src/domains/context/prompt-context.ts +8 -0
  207. package/src/domains/context/working-set/policies/index.ts +3 -4
  208. package/src/domains/dispatch/active-route-planner.ts +14 -0
  209. package/src/domains/dispatch/backoff.ts +2 -1
  210. package/src/domains/dispatch/budget-envelope.ts +396 -0
  211. package/src/domains/dispatch/capability-match.ts +1 -0
  212. package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
  213. package/src/domains/dispatch/contract.ts +36 -0
  214. package/src/domains/dispatch/delegation-plan.ts +167 -0
  215. package/src/domains/dispatch/execution-plan.ts +76 -5
  216. package/src/domains/dispatch/execution-role.ts +3 -1
  217. package/src/domains/dispatch/execution-scheduler.ts +183 -67
  218. package/src/domains/dispatch/extension.ts +339 -36
  219. package/src/domains/dispatch/fleet-gate.ts +14 -0
  220. package/src/domains/dispatch/fleet-plan.ts +63 -3
  221. package/src/domains/dispatch/fleet-run.ts +737 -0
  222. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  223. package/src/domains/dispatch/host-verification.ts +178 -0
  224. package/src/domains/dispatch/index.ts +38 -0
  225. package/src/domains/dispatch/intent.ts +159 -0
  226. package/src/domains/dispatch/orphan-recovery.ts +1 -0
  227. package/src/domains/dispatch/receipt-integrity.ts +12 -4
  228. package/src/domains/dispatch/state.ts +37 -3
  229. package/src/domains/dispatch/types.ts +61 -9
  230. package/src/domains/dispatch/validation.ts +80 -6
  231. package/src/domains/dispatch/worker-spawn.ts +14 -3
  232. package/src/domains/eval/metrics/evidence.ts +0 -116
  233. package/src/domains/eval/metrics/invariants.ts +1 -1
  234. package/src/domains/eval/runners/clio-run.ts +1 -10
  235. package/src/domains/eval/runners/external-command.ts +2 -29
  236. package/src/domains/eval/schema/suite.ts +0 -7
  237. package/src/domains/eval/suites/run.ts +1 -7
  238. package/src/domains/evidence/trust-status.ts +10 -1
  239. package/src/domains/memory/index.ts +22 -0
  240. package/src/domains/memory/operations.ts +58 -1
  241. package/src/domains/memory/promotion.ts +281 -0
  242. package/src/domains/memory/prompt-section.ts +25 -5
  243. package/src/domains/memory/proposal.ts +51 -7
  244. package/src/domains/memory/task-bank.ts +3 -2
  245. package/src/domains/memory/task-memory-handoff.ts +181 -24
  246. package/src/domains/memory/task-memory-policy.ts +3 -1
  247. package/src/domains/memory/types.ts +37 -0
  248. package/src/domains/memory/validate.ts +178 -0
  249. package/src/domains/middleware/index.ts +15 -0
  250. package/src/domains/middleware/memory-intervention.ts +35 -25
  251. package/src/domains/middleware/runtime.ts +6 -0
  252. package/src/domains/middleware/skills-reminder.ts +19 -4
  253. package/src/domains/middleware/stalled-turn.ts +43 -1
  254. package/src/domains/middleware/types.ts +10 -0
  255. package/src/domains/middleware/watchdog.ts +281 -0
  256. package/src/domains/observability/contract.ts +9 -2
  257. package/src/domains/observability/cost.ts +31 -4
  258. package/src/domains/observability/extension.ts +2 -2
  259. package/src/domains/observability/index.ts +10 -0
  260. package/src/domains/observability/out-of-turn-usage.ts +223 -0
  261. package/src/domains/providers/index.ts +3 -0
  262. package/src/domains/providers/model-discovery.ts +9 -0
  263. package/src/domains/providers/runtime-resolution.ts +38 -1
  264. package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
  265. package/src/domains/providers/types/context-window-slots.ts +18 -0
  266. package/src/domains/providers/types/runtime-descriptor.ts +3 -1
  267. package/src/domains/resources/index.ts +20 -0
  268. package/src/domains/resources/library.ts +326 -0
  269. package/src/domains/resources/skills/marketplace.ts +37 -12
  270. package/src/domains/safety/call-target.ts +211 -14
  271. package/src/domains/safety/decision-presentation.ts +268 -0
  272. package/src/domains/safety/redaction.ts +73 -0
  273. package/src/domains/session/context-ledger.ts +10 -1
  274. package/src/domains/session/decision-board.ts +4 -0
  275. package/src/domains/session/entries.ts +3 -0
  276. package/src/domains/session/handoff.ts +629 -0
  277. package/src/domains/session/history.ts +68 -19
  278. package/src/domains/session/usage.ts +24 -7
  279. package/src/domains/share/archive.ts +67 -2
  280. package/src/engine/acp/event-mapper.ts +7 -0
  281. package/src/engine/acp/server.ts +29 -2
  282. package/src/engine/apis/lmstudio.ts +25 -4
  283. package/src/engine/apis/openai-completions.ts +147 -22
  284. package/src/engine/claude/sdk-runtime.ts +8 -2
  285. package/src/engine/claude/tool-safety.ts +13 -0
  286. package/src/engine/loop-guard.ts +27 -3
  287. package/src/engine/worker-events.ts +4 -3
  288. package/src/engine/worker-runtime.ts +59 -54
  289. package/src/entry/orchestrator.ts +55 -1
  290. package/src/interactive/bus-notices.ts +26 -0
  291. package/src/interactive/chat-loop-messages.ts +22 -0
  292. package/src/interactive/chat-loop.ts +248 -1
  293. package/src/interactive/chat-renderer.ts +41 -3
  294. package/src/interactive/clio-editor.ts +44 -7
  295. package/src/interactive/context-overlay.ts +43 -5
  296. package/src/interactive/cost-overlay.ts +70 -11
  297. package/src/interactive/council-dispatch.ts +30 -0
  298. package/src/interactive/council-grid.ts +213 -0
  299. package/src/interactive/council.ts +99 -0
  300. package/src/interactive/dispatch-board.ts +471 -50
  301. package/src/interactive/fleet-run-preview.ts +307 -0
  302. package/src/interactive/footer/notifications.ts +219 -0
  303. package/src/interactive/footer/widgets.ts +13 -0
  304. package/src/interactive/handoff-round.ts +56 -0
  305. package/src/interactive/interactive-application.ts +49 -2
  306. package/src/interactive/interactive-event-projection.ts +9 -1
  307. package/src/interactive/interactive-input-runtime.ts +11 -1
  308. package/src/interactive/interactive-presentation.ts +11 -1
  309. package/src/interactive/interactive-slash-runtime.ts +52 -2
  310. package/src/interactive/interactive-subscriptions.ts +14 -2
  311. package/src/interactive/memory-overlay.ts +89 -4
  312. package/src/interactive/oracle.ts +179 -0
  313. package/src/interactive/overlay-ask-user-lifecycle.ts +7 -1
  314. package/src/interactive/overlay-frame.ts +5 -2
  315. package/src/interactive/overlay-general-openers.ts +230 -2
  316. package/src/interactive/overlay-key-routing.ts +58 -2
  317. package/src/interactive/overlay-lifecycle.ts +52 -5
  318. package/src/interactive/overlay-permission-lifecycle.ts +33 -8
  319. package/src/interactive/overlay-resource-openers.ts +11 -3
  320. package/src/interactive/overlay-session-lifecycle.ts +234 -2
  321. package/src/interactive/overlay-transitions.ts +11 -0
  322. package/src/interactive/overlays/ask-user.ts +74 -30
  323. package/src/interactive/overlays/decisions.ts +3 -1
  324. package/src/interactive/overlays/fleet-run-approval.ts +208 -0
  325. package/src/interactive/overlays/handoff-review.ts +185 -0
  326. package/src/interactive/overlays/library-install-confirm.ts +151 -0
  327. package/src/interactive/overlays/list-overlay.ts +168 -2
  328. package/src/interactive/overlays/settings.ts +101 -4
  329. package/src/interactive/overlays/side-question.ts +139 -0
  330. package/src/interactive/overlays/skills-hub.ts +401 -15
  331. package/src/interactive/permission-hint.ts +35 -0
  332. package/src/interactive/permission-overlay.ts +95 -45
  333. package/src/interactive/renderers/tool-execution.ts +19 -49
  334. package/src/interactive/session-last-turn.ts +8 -1
  335. package/src/interactive/session-usage-reseed.ts +36 -10
  336. package/src/interactive/side-question.ts +171 -0
  337. package/src/interactive/slash-commands.ts +434 -7
  338. package/src/interactive/slash-spec.ts +19 -6
  339. package/src/interactive/status/summary.ts +5 -0
  340. package/src/interactive/status/types.ts +5 -0
  341. package/src/interactive/terminal-lease.ts +1 -0
  342. package/src/interactive/theme/tokens.ts +30 -0
  343. package/src/interactive/turn-context.ts +96 -23
  344. package/src/interactive/turn-middleware.ts +16 -1
  345. package/src/interactive/turn-runtime.ts +37 -8
  346. package/src/interactive/turn-state.ts +3 -0
  347. package/src/interactive/watchdog-run.ts +75 -0
  348. package/src/interactive/worker-progress.ts +440 -0
  349. package/src/interactive/worker-share.ts +56 -1
  350. package/src/interactive/worker-stream.ts +58 -110
  351. package/src/tools/agent-tools.ts +28 -3
  352. package/src/tools/ask-user.ts +21 -1
  353. package/src/tools/bootstrap.ts +3 -0
  354. package/src/tools/compete-worktrees.ts +13 -79
  355. package/src/tools/context/index.ts +2 -2
  356. package/src/tools/dispatch-admission.ts +242 -8
  357. package/src/tools/dispatch-arguments.ts +65 -1
  358. package/src/tools/dispatch-event-text.ts +19 -0
  359. package/src/tools/dispatch-plan.ts +136 -6
  360. package/src/tools/dispatch-runner.ts +319 -13
  361. package/src/tools/dispatch-types.ts +20 -1
  362. package/src/tools/dispatch.ts +96 -3
  363. package/src/tools/monitor.ts +31 -0
  364. package/src/tools/profiles.ts +18 -4
  365. package/src/tools/registry.ts +15 -5
  366. package/src/tools/result-disposition.ts +156 -0
  367. package/src/tools/result-shaping.ts +59 -1
  368. package/src/tools/task-worktree.ts +238 -0
  369. package/src/tools/verify/authoring.ts +116 -55
  370. package/src/tools/verify/scripts.ts +62 -0
  371. package/src/tools/worker-evidence.ts +21 -1
  372. package/src/worker/spec-contract.ts +44 -3
  373. package/dist/chunk-EFADSJET.js +0 -18
  374. package/dist/chunk-HC4CLZ2Y.js +0 -68
  375. package/dist/memory-4ALKDJ4Q.js +0 -246
  376. package/src/domains/eval/metrics/chaos-stream.ts +0 -93
package/src/cli/fleet.ts CHANGED
@@ -2,6 +2,9 @@
2
2
  * `clio-coder fleet` operator surface.
3
3
  *
4
4
  * clio-coder fleet list enumerate .clio-coder/fleets/*.md with validity
5
+ * clio-coder fleet new <name> --from <name> copy a shipped contract into the project
6
+ * clio-coder fleet validate|graph <name> inspect a contract without executing it
7
+ * clio-coder fleet commands init draft a repository command registry
5
8
  * clio-coder fleet run <name> --var k=v ... preflight + execute a fleet contract
6
9
  * clio-coder fleet status [--json] runtime snapshot from the durable ledger
7
10
  * clio-coder fleet drain|resume [--json] close or reopen durable dispatch admission
@@ -15,7 +18,7 @@
15
18
  import { randomBytes } from "node:crypto";
16
19
  import { readFileSync } from "node:fs";
17
20
  import { join } from "node:path";
18
- import { attributeCommitMessage } from "../core/commit-attribution.js";
21
+ import { type ClioSettings, readSettings } from "../core/config.js";
19
22
  import { loadDomains } from "../core/domain-loader.js";
20
23
  import { clioStateDir } from "../core/xdg.js";
21
24
  import type { AgentsContract } from "../domains/agents/contract.js";
@@ -32,41 +35,34 @@ import {
32
35
  loadFleetCommands,
33
36
  loadFleetContract,
34
37
  renderFleetPrompt,
35
- resultContractAuthorship,
36
38
  } from "../domains/agents/index.js";
37
39
  import type { ConfigContract } from "../domains/config/contract.js";
38
40
  import { ConfigDomainModule } from "../domains/config/index.js";
39
41
  import { ContextDomainModule } from "../domains/context/runtime.js";
40
- import { type CapacityDrain, capacityDrain, setCapacityDraining } from "../domains/dispatch/capacity-lease.js";
41
- import { runCodeStep } from "../domains/dispatch/code-step.js";
42
- import { codeStepDir, writeCodeStepRecord } from "../domains/dispatch/code-step-store.js";
43
- import type { DispatchContract, DispatchRequest } from "../domains/dispatch/contract.js";
44
- import {
45
- type ExecutionPlan,
46
- type ExecutionPlanCodeStep,
47
- executionPlanAncestors,
48
- } from "../domains/dispatch/execution-plan.js";
49
42
  import {
50
- agentRoleFactsResolver,
51
- gateRouteCorrelation,
52
- requestExecutionRole,
53
- withAttemptRole,
54
- } from "../domains/dispatch/execution-role.js";
55
- import { type ExecutionPlanResult, executePlan } from "../domains/dispatch/execution-scheduler.js";
56
- import { deriveFleetCommitAttribution } from "../domains/dispatch/fleet-commit-attribution.js";
43
+ formatBudgetPolicy,
44
+ formatBudgetReasons,
45
+ formatBudgetRequest,
46
+ formatEffectiveBudget,
47
+ type RunToolBudgetEnvelope,
48
+ } from "../domains/dispatch/budget-envelope.js";
49
+ import { type CapacityDrain, capacityDrain, setCapacityDraining } from "../domains/dispatch/capacity-lease.js";
50
+ import type { DispatchContract } from "../domains/dispatch/contract.js";
51
+ import type { ExecutionPlan } from "../domains/dispatch/execution-plan.js";
52
+ import { agentRoleFactsResolver, requestExecutionRole, withAttemptRole } from "../domains/dispatch/execution-role.js";
57
53
  import { compileFleetExecutionPlan } from "../domains/dispatch/fleet-plan.js";
58
54
  import {
59
- decideReviewGate,
60
- type GateDecisionCorrelation,
61
- materializePendingGateDecision,
62
- stagePendingGateDecision,
63
- } from "../domains/dispatch/gate-decisions.js";
64
- import { DispatchDomainModule } from "../domains/dispatch/index.js";
65
- import { verifyReceiptIntegrity } from "../domains/dispatch/receipt-integrity.js";
55
+ DispatchDomainModule,
56
+ type ExecuteFleetRunInput,
57
+ executeFleetRun,
58
+ type FleetRunOutcome,
59
+ planFleetResume,
60
+ readFleetRun,
61
+ } from "../domains/dispatch/index.js";
66
62
  import { openLedger } from "../domains/dispatch/state.js";
67
63
  import type { RunEnvelope, RunReceipt } from "../domains/dispatch/types.js";
68
64
  import { WRITE_BOUNDARY_VIOLATION_REASON } from "../domains/dispatch/write-boundary.js";
69
- import { createWriteBoundaryEnforcer, preflightWriteBoundaries } from "../domains/dispatch/write-boundary-enforcer.js";
65
+ import { preflightWriteBoundaries } from "../domains/dispatch/write-boundary-enforcer.js";
70
66
  import { ensureClioState, LifecycleDomainModule } from "../domains/lifecycle/index.js";
71
67
  import { MiddlewareDomainModule } from "../domains/middleware/index.js";
72
68
  import { ObservabilityDomainModule } from "../domains/observability/index.js";
@@ -85,7 +81,12 @@ Repo-owned fleet contracts and the dispatch status surface.
85
81
 
86
82
  Subcommands:
87
83
  list list .clio-coder/fleets/*.md contracts with validation status
84
+ new <name> --from <builtin> copy build-review, build-test, or sdlc into this repository
85
+ validate <name> [--json] run the fleet execution preflight without side effects
86
+ graph <name> [--json] print compiled waves, loops, scopes, and write boundaries
87
+ commands init draft a commented command registry from declared project entries
88
88
  run <name> [--var k=v ...] preflight and execute a fleet contract
89
+ [--resume <runId>] replay a completed prefix from a prior run of the same plan
89
90
  [--json] emit step receipts as JSON
90
91
  status [--json] show running, retrying, and total dispatch state
91
92
  drain [--json] deny new execution starts for up to one hour
@@ -104,6 +105,11 @@ function fail(message: string): number {
104
105
  return 2;
105
106
  }
106
107
 
108
+ function refuse(message: string): number {
109
+ process.stderr.write(`clio-coder fleet: ${message}\n`);
110
+ return 1;
111
+ }
112
+
107
113
  function parseVars(args: ReadonlyArray<string>): { vars: Record<string, string>; rest: string[]; error?: string } {
108
114
  const vars: Record<string, string> = {};
109
115
  const rest: string[] = [];
@@ -127,25 +133,6 @@ function parseVars(args: ReadonlyArray<string>): { vars: Record<string, string>;
127
133
  return { vars, rest };
128
134
  }
129
135
 
130
- /** Sealed independence facts between the work under review and its reviewer. */
131
- function fleetGateCorrelation(subject: RunReceipt, decider: RunReceipt): GateDecisionCorrelation {
132
- const facts = (receipt: RunReceipt) => ({
133
- agentId: receipt.agentId,
134
- targetId: receipt.targetId,
135
- wireModelId: receipt.wireModelId,
136
- runtimeId: receipt.runtimeId,
137
- nodeId: receipt.node?.id ?? "local",
138
- });
139
- const { agent, target, modelFamily, runtime, node, independent } = gateRouteCorrelation(
140
- facts(subject),
141
- facts(decider),
142
- );
143
- return { agent, target, modelFamily, runtime, node, independent };
144
- }
145
-
146
- /** Owner id for a plan with no model runs; it holds nothing to release. */
147
- const NO_WORKER_RESERVATION = "fleet-no-worker-reservation";
148
-
149
136
  function newFleetRootId(): string {
150
137
  return `fleet-${randomBytes(6).toString("hex")}`;
151
138
  }
@@ -157,7 +144,14 @@ function newFleetRootId(): string {
157
144
  function renderStep(step: FleetContractStep): string {
158
145
  if (step.kind === "code") return `code:${step.command}[${step.scope}]`;
159
146
  if (step.kind === "agent") return `${step.agent}[${step.scope}]`;
160
- const check = step.check.kind === "code" ? `code:${step.check.command}` : step.check.agent;
147
+ if (step.kind === "gate") return `gate:${step.path}[${step.run}]`;
148
+ if (step.kind === "plan") return `plan:${step.agent}[${step.roster.join(",")}; max ${step.maxTasks}]`;
149
+ const check =
150
+ step.check.kind === "code"
151
+ ? `code:${step.check.command}`
152
+ : step.check.kind === "gate"
153
+ ? `gate:${step.check.gate}`
154
+ : step.check.agent;
161
155
  return `loop:${step.id}(${check} -> ${step.repair.agent} x${step.maxAttempts})`;
162
156
  }
163
157
 
@@ -209,6 +203,7 @@ interface FleetPreflightDeps {
209
203
  agents: AgentsContract;
210
204
  safety: SafetyContract;
211
205
  scheduling: SchedulingContract | undefined;
206
+ settings: Readonly<ClioSettings>;
212
207
  }
213
208
 
214
209
  /** Every agent a contract dispatches, including both halves of every loop. */
@@ -218,7 +213,11 @@ function contractAgents(
218
213
  const agents: Array<{ id: string; agent: string; scope: "readonly" | "workspace" }> = [];
219
214
  for (const step of contract.steps) {
220
215
  if (step.kind === "agent") agents.push({ id: step.id, agent: step.agent, scope: step.scope });
221
- else if (step.kind === "loop") {
216
+ else if (step.kind === "gate") agents.push({ id: step.id, agent: step.agent, scope: "workspace" });
217
+ else if (step.kind === "plan") {
218
+ agents.push({ id: step.id, agent: step.agent, scope: step.scope });
219
+ for (const agent of step.roster) agents.push({ id: `${step.id}.roster`, agent, scope: "readonly" });
220
+ } else if (step.kind === "loop") {
222
221
  if (step.check.kind === "agent") {
223
222
  agents.push({ id: `${step.id}.check`, agent: step.check.agent, scope: step.check.scope });
224
223
  }
@@ -229,6 +228,28 @@ function contractAgents(
229
228
  }
230
229
 
231
230
  function preflightFleet(contract: FleetContract, deps: FleetPreflightDeps): string | null {
231
+ const targets = new Set(deps.settings.targets.map((target) => target.id));
232
+ const profiles = new Set(Object.keys(deps.settings.workers?.profiles ?? {}));
233
+ const checkRoute = (id: string, route: { target?: string; profile?: string }): string | null => {
234
+ if (route.target !== undefined && !targets.has(route.target))
235
+ return `unknown target '${route.target}' at step '${id}'`;
236
+ if (route.profile !== undefined && !profiles.has(route.profile))
237
+ return `unknown profile '${route.profile}' at step '${id}'`;
238
+ return null;
239
+ };
240
+ for (const step of contract.steps) {
241
+ if (step.kind === "agent" || step.kind === "gate" || step.kind === "plan") {
242
+ const error = checkRoute(step.id, step);
243
+ if (error !== null) return error;
244
+ }
245
+ if (step.kind !== "loop") continue;
246
+ if (step.check.kind === "agent") {
247
+ const error = checkRoute(`${step.id}.check`, step.check);
248
+ if (error !== null) return error;
249
+ }
250
+ const error = checkRoute(`${step.id}.repair`, step.repair);
251
+ if (error !== null) return error;
252
+ }
232
253
  for (const step of contractAgents(contract)) {
233
254
  if (!deps.agents.get(step.agent)) {
234
255
  return `unknown agent '${step.agent}' (step '${step.id}' must name a recipe from 'clio-coder agents')`;
@@ -257,23 +278,21 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
257
278
  const { vars, rest, error } = parseVars(args);
258
279
  if (error !== undefined) return fail(error);
259
280
  const json = rest.includes("--json");
281
+ const resumeIndex = rest.indexOf("--resume");
282
+ const resumeId = resumeIndex === -1 ? undefined : rest[resumeIndex + 1];
283
+ if (resumeIndex !== -1 && (resumeId === undefined || resumeId.startsWith("-")))
284
+ return fail("--resume requires a run id");
260
285
  const name = rest.find((arg) => !arg.startsWith("-"));
261
- if (!name) return fail("usage: clio-coder fleet run <name> [--var key=value ...] [--json]");
286
+ if (!name) return fail("usage: clio-coder fleet run <name> [--var key=value ...] [--resume <runId>] [--json]");
262
287
 
263
- // Phase 1: zero-side-effect validation. Parse and render strictly before
264
- // any domain boots or any process spawns.
265
288
  let contract: FleetContract;
266
289
  let prompt: string;
267
290
  let commands: FleetCommandRegistry | null;
268
291
  try {
269
292
  contract = loadFleetContract(process.cwd(), name);
270
293
  prompt = renderFleetPrompt(contract.body, vars);
271
- // loadFleetContract already refused any unregistered command id; this
272
- // read is the binding the runner executes.
273
294
  commands = loadFleetCommands(process.cwd());
274
295
  } catch (err) {
275
- // The listing calls this state `setup` and says how to leave it; the run
276
- // path is where a user meets it with intent, so it says the same thing.
277
296
  if (err instanceof FleetCommandRegistryMissingError) {
278
297
  process.stderr.write(`clio-coder fleet: ${err.message}\n ${FLEET_COMMANDS_REMEDY}\n`);
279
298
  return 2;
@@ -306,16 +325,19 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
306
325
  await loaded.stop();
307
326
  return fail("required domains unavailable (dispatch/agents/safety)");
308
327
  }
309
-
310
328
  const roleFacts = agentRoleFactsResolver((id) => agents.getSpec(id));
311
- const preflightError = preflightFleet(contract, { agents, safety, scheduling });
329
+ const preflightError = preflightFleet(contract, {
330
+ agents,
331
+ safety,
332
+ scheduling,
333
+ settings: config?.get() ?? readSettings(),
334
+ });
312
335
  if (preflightError !== null) {
313
336
  await loaded.stop();
314
337
  return fail(`preflight failed: ${preflightError}`);
315
338
  }
316
339
 
317
340
  const fleetRootId = newFleetRootId();
318
- let planOutcome: ExecutionPlanResult;
319
341
  let plan: ExecutionPlan;
320
342
  try {
321
343
  plan = compileFleetExecutionPlan({
@@ -334,9 +356,14 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
334
356
  return {
335
357
  requestedAuthority: spec.capabilityClass,
336
358
  approvedAuthority: spec.capabilityClass,
337
- // A gate decider answers the coordinator's question, so its
338
- // postcondition is the gate result contract, not its recipe's.
339
- expectedResultContract: context.gateRole === "reviewer" ? "verifier-report" : spec.resultContract.kind,
359
+ expectedResultContract:
360
+ context.planRole === true
361
+ ? "delegation-plan"
362
+ : context.gateAuthorRole === true
363
+ ? "artifact-report"
364
+ : context.gateRole === "reviewer"
365
+ ? "verifier-report"
366
+ : spec.resultContract.kind,
340
367
  executionRole: withAttemptRole(requestRole, context.attempt),
341
368
  };
342
369
  },
@@ -345,268 +372,91 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
345
372
  await loaded.stop();
346
373
  return fail(err instanceof Error ? err.message : String(err));
347
374
  }
348
- // A declared boundary is verified against the checkout, so the checkout has
349
- // to be one this can read. Refused here, before any spawn, rather than
350
- // discovered as an unenforceable claim halfway through the run.
351
375
  try {
352
376
  preflightWriteBoundaries(plan, process.cwd());
353
377
  } catch (err) {
354
378
  await loaded.stop();
355
379
  return fail(`preflight failed: ${err instanceof Error ? err.message : String(err)}`);
356
380
  }
357
- const boundaryByStep = new Map(plan.steps.map((step) => [step.id, step.writes]));
358
- const boundaryEnforcer = createWriteBoundaryEnforcer({
359
- root: process.cwd(),
360
- rootId: fleetRootId,
361
- boundaryFor: (stepId) => boundaryByStep.get(stepId),
362
- onVerdict(verdict, path) {
363
- if (verdict.reason === null) return;
364
- process.stderr.write(`write boundary ${verdict.window}: ${verdict.detail ?? verdict.reason} record=${path}\n`);
365
- },
366
- });
381
+
382
+ let resume: ExecuteFleetRunInput["resume"];
383
+ if (resumeId !== undefined) {
384
+ const record = readFleetRun(resumeId);
385
+ if (record === null) {
386
+ await loaded.stop();
387
+ return refuse(`resume run '${resumeId}' was not found in the durable fleet ledger`);
388
+ }
389
+ const planned = planFleetResume(record, plan, contract, vars);
390
+ if (!planned.ok) {
391
+ if (planned.reason === "fleet-name") {
392
+ await loaded.stop();
393
+ return refuse(`resume run '${resumeId}' belongs to fleet '${planned.priorFleet}', not '${planned.currentFleet}'`);
394
+ }
395
+ if (planned.reason === "vars") {
396
+ await loaded.stop();
397
+ return refuse(`resume run '${resumeId}' used different --var values`);
398
+ }
399
+ process.stderr.write(
400
+ `clio-coder fleet: resume plan hash differs: prior=${planned.priorHash} current=${planned.currentHash}\n`,
401
+ );
402
+ for (const entry of planned.diff) {
403
+ process.stderr.write(` step ${entry.index + 1}: ${entry.priorId} -> ${entry.currentId}\n`);
404
+ process.stderr.write(` prior: ${JSON.stringify(entry.prior)}\n`);
405
+ process.stderr.write(` current: ${JSON.stringify(entry.current)}\n`);
406
+ }
407
+ await loaded.stop();
408
+ return 1;
409
+ }
410
+ resume = { record, replayed: planned.replayed };
411
+ }
412
+
367
413
  process.stderr.write(
368
414
  `fleet ${contract.name}: root=${fleetRootId} plan=${plan.hash} steps=${plan.steps.length} loops=${plan.loops.length}\n`,
369
415
  );
370
- const receipts: RunReceipt[] = [];
371
- const receiptsByStep = new Map<string, RunReceipt>();
372
- const planStep = (stepId: string) => plan.steps.find((entry) => entry.id === stepId);
373
- const attributionEnabled = config?.get().attribution.gitCommits ?? true;
374
- // Freshness is coordinator-owned execution state. Worker prose cannot set
375
- // either flag: a successful deterministic verification sets the first, and
376
- // an integrity-valid independent gate decision sets the second. Any later
377
- // workspace agent invalidates both before a commit may read them.
378
- let validationFresh = false;
379
- let independentReviewFresh = false;
380
-
381
- /**
382
- * The commit message is the words of the agent that produced the work, and
383
- * only that agent's. Candidates are consulted newest first; when none of
384
- * them authored one, code writes a deterministic line rather than inventing
385
- * a sentence or reusing somebody else's.
386
- */
387
- const commitMessageFor = (
388
- step: ExecutionPlanCodeStep,
389
- priorResults: ReadonlyMap<string, { output: string }>,
390
- ): string => {
391
- for (const candidate of step.commitFrom ?? []) {
392
- const source = planStep(candidate);
393
- const result = priorResults.get(candidate);
394
- if (source === undefined || source.kind !== "agent" || result === undefined) continue;
395
- const resultContract = agents.getSpec(source.agentId)?.resultContract ?? null;
396
- if (resultContract === null) continue;
397
- const authored = resultContractAuthorship(resultContract, result.output);
398
- if (authored.commitMessage !== null) return authored.commitMessage;
399
- if (authored.summary !== null) return `clio(${fleetRootId}): ${authored.summary}`;
400
- }
401
- return `clio(${fleetRootId}): ${contract.name} ${step.id}`;
402
- };
416
+ let outcome: FleetRunOutcome;
403
417
  try {
404
- planOutcome = await executePlan(plan, {
405
- preflight(step) {
406
- const request: DispatchRequest = {
407
- agentId: step.agentId,
408
- executionRole: step.executionRole,
409
- task: step.task,
410
- requestOrigin: "user",
411
- lineage: { parentRunId: fleetRootId, rootRunId: fleetRootId, attempt: 0, depth: 1 },
412
- ...(step.scope === "readonly" ? { autonomy: "read-only" as const } : {}),
413
- };
414
- const resolution = dispatch.preview?.(request);
415
- if (!resolution) throw new Error(`fleet preflight cannot resolve step '${step.id}'`);
416
- return { step, costUpperBoundUsd: resolution.costUpperBoundUsd, nodeId: resolution.node.id };
417
- },
418
- reserve(_plan, admissions) {
419
- // A fleet of deterministic steps admits no worker and holds no
420
- // capacity. The reservation authority refuses an empty reservation,
421
- // correctly: there is nothing to reserve, so nothing is asked of it.
422
- if (admissions.length === 0) return { ownerId: NO_WORKER_RESERVATION };
423
- const reservation = dispatch.reservations?.prepare({
424
- topology: "parallel",
425
- tasks: admissions.map((admission) => ({
426
- memberId: admission.step.id,
427
- wave: plan.waves.findIndex((wave) => wave.includes(admission.step.id)),
428
- resolution: dispatch.preview?.({
429
- agentId: admission.step.agentId,
430
- executionRole: admission.step.executionRole,
431
- task: admission.step.task,
432
- }) as NonNullable<ReturnType<NonNullable<DispatchContract["preview"]>>>,
433
- })),
434
- });
435
- if (!reservation) throw new Error("fleet whole-plan reservation is unavailable");
436
- return { ownerId: reservation.ownerId };
437
- },
438
- async run(step, handoffs, reservation, ledger) {
439
- // A loop repair is attempt n of the same logical work, so it enters
440
- // the run ledger as one: recovery evidence, not a fresh observation.
441
- const attempt = step.loop?.role === "repair" ? step.loop.attempt : 0;
442
- const request: DispatchRequest = {
443
- agentId: step.agentId,
444
- executionRole: step.executionRole,
445
- task: step.task,
446
- predecessorHandoffs: handoffs,
447
- requestOrigin: "user",
448
- lineage: { parentRunId: fleetRootId, rootRunId: fleetRootId, attempt, depth: 1 },
449
- reservation,
450
- ...(ledger !== undefined ? { ledger } : {}),
451
- ...(step.loop?.role === "check"
452
- ? {
453
- gate: {
454
- role: "reviewer" as const,
455
- group: `${fleetRootId}:${step.loop.loopId}`,
456
- cycle: step.loop.attempt,
457
- subjects: handoffs.map((handoff) => ({
458
- runId: handoff.terminalRunId,
459
- digest: handoff.receiptDigest,
460
- })),
461
- },
462
- }
463
- : {}),
464
- ...(step.scope === "readonly" ? { autonomy: "read-only" as const } : {}),
465
- };
466
- const handle = await dispatch.dispatch(request);
467
- void (async () => {
468
- for await (const _event of handle.events) {
469
- /* background display drain */
470
- }
471
- })().catch(() => {});
472
- return {
473
- assignmentId: handle.runId,
474
- result: handle.finalPromise.then((receipt) => {
475
- if (step.scope === "workspace") {
476
- validationFresh = false;
477
- independentReviewFresh = false;
478
- }
479
- receipts.push(receipt);
480
- receiptsByStep.set(step.id, receipt);
481
- const envelope = dispatch.getRun(receipt.runId);
482
- const integrityValid = envelope !== null && verifyReceiptIntegrity(receipt, envelope).ok;
483
- const succeeded = receipt.exitCode === 0 && (receipt.outcome === undefined || receipt.outcome === "succeeded");
484
- if (json) process.stdout.write(`${JSON.stringify(receipt)}\n`);
485
- else
486
- process.stdout.write(
487
- `step ${step.id} ${step.agentId}: ${succeeded ? "succeeded" : "failed"} assignment=${handle.runId} terminal-run=${receipt.runId} cost=$${receipt.costUsd.toFixed(4)}\n`,
488
- );
489
- return {
490
- stepId: step.id,
491
- assignmentId: handle.runId,
492
- terminalRunId: receipt.runId,
493
- receiptDigest: receipt.integrity.digest,
494
- output: receipt.output?.state === "final" ? receipt.output.text : "",
495
- succeeded,
496
- integrityValid,
497
- };
498
- }),
499
- };
500
- },
501
- async runCode(step, _handoffs, signal, priorResults) {
502
- const command = commands?.commands.get(step.commandId);
503
- if (command === undefined) throw new Error(`fleet code step '${step.id}' has no registered command`);
504
- const isCommit = step.commitFrom !== undefined;
505
- const evidence = isCommit
506
- ? deriveFleetCommitAttribution({
507
- plan,
508
- step,
509
- priorResults,
510
- validationFresh,
511
- independentReviewFresh,
512
- })
513
- : null;
514
- if (!isCommit && step.scope === "workspace" && step.verification !== true) {
515
- validationFresh = false;
516
- independentReviewFresh = false;
418
+ outcome = await executeFleetRun({
419
+ plan,
420
+ contractName: contract.name,
421
+ commands,
422
+ workspaceRoot: process.cwd(),
423
+ fleetRootId,
424
+ dispatch,
425
+ agents: { getSpec: (agentId) => agents.getSpec(agentId) },
426
+ attributionEnabled: config?.get().attribution.gitCommits ?? true,
427
+ vars,
428
+ ...(resume !== undefined ? { resume } : {}),
429
+ onStepSettled(step) {
430
+ if (step.replayed === true && step.result !== undefined) {
431
+ if (json)
432
+ process.stdout.write(
433
+ `${JSON.stringify({ stepId: step.stepId, status: "replayed", receipt: { runId: step.result.terminalRunId, digest: step.result.receiptDigest } })}\n`,
434
+ );
435
+ else
436
+ process.stdout.write(
437
+ `step ${step.stepId}: replayed terminal-run=${step.result.terminalRunId} receipt=${step.result.receiptDigest}\n`,
438
+ );
439
+ return;
517
440
  }
518
- const originalMessage = isCommit ? commitMessageFor(step, priorResults) : null;
519
- const outcome = await runCodeStep({
520
- stepId: step.id,
521
- command,
522
- workspaceRoot: process.cwd(),
523
- artifactDir: codeStepDir(fleetRootId),
524
- signal,
525
- ...(isCommit && evidence !== null && originalMessage !== null
526
- ? {
527
- substitutions: {
528
- commitMessage: attributeCommitMessage(originalMessage, evidence, attributionEnabled),
529
- },
530
- requireWorkspaceChanges: true,
531
- commitAttribution: { enabled: attributionEnabled, evidence },
532
- }
533
- : {}),
534
- });
535
- if (step.verification === true) validationFresh = outcome.report.passed;
536
- const recordPath = await writeCodeStepRecord(fleetRootId, outcome.record);
537
- if (json) process.stdout.write(`${JSON.stringify({ codeStep: outcome.record })}\n`);
538
- else
539
- process.stdout.write(
540
- `step ${step.id} code:${command.id}: ${outcome.report.passed ? "passed" : "failed"} exit=${outcome.report.exitCode} ${outcome.record.durationMs}ms record=${recordPath}\n`,
541
- );
542
- return {
543
- stepId: step.id,
544
- assignmentId: outcome.record.runId,
545
- terminalRunId: outcome.record.runId,
546
- receiptDigest: outcome.record.reportDigest,
547
- output: outcome.output,
548
- succeeded: outcome.report.passed,
549
- // The runner authored this report itself, so its provenance is
550
- // valid by construction; only the command's verdict can be red.
551
- integrityValid: true,
552
- };
553
- },
554
- async decideLoop({ loop, step, attempt, terminalAttempt, result }) {
555
- // Reuse the coordinator's existing review policy verbatim: pass ends
556
- // the loop, a failure with attempts left earns one repair, and the
557
- // same failure at the bound is simply the terminal answer. `revise`
558
- // is never a verdict the reviewed model authors.
559
- const decider = receiptsByStep.get(step.id);
560
- // The subject is the work under review: the most recent agent run
561
- // upstream of this verification. A direct dependency is not enough,
562
- // because a loop's declared dependency is another loop's
563
- // deterministic check, which produced no receipt at all.
564
- const ancestors = executionPlanAncestors(plan, step.id);
565
- let subject: RunReceipt | undefined;
566
- for (const [stepId, receipt] of receiptsByStep) {
567
- if (ancestors.has(stepId)) subject = receipt;
441
+ if (step.receipt !== undefined) {
442
+ if (json) process.stdout.write(`${JSON.stringify(step.receipt)}\n`);
443
+ else
444
+ process.stdout.write(
445
+ `step ${step.stepId} ${step.agentId}: ${step.succeeded ? "succeeded" : "failed"}${step.failureReason !== undefined ? ` reason=${step.failureReason}` : ""} assignment=${step.assignmentId} terminal-run=${step.terminalRunId} cost=$${step.costUsd.toFixed(4)}\n`,
446
+ );
447
+ return;
568
448
  }
569
- if (decider === undefined || subject === undefined) {
570
- return {
571
- resolved: false,
572
- findings: null,
573
- needsDecision: `loop '${loop.id}' cycle ${attempt} has no sealed reviewer or subject receipt`,
574
- };
449
+ if (step.codeStep !== undefined) {
450
+ if (json) process.stdout.write(`${JSON.stringify({ codeStep: step.codeStep })}\n`);
451
+ else
452
+ process.stdout.write(
453
+ `step ${step.stepId} code:${step.commandId}: ${step.succeeded ? "passed" : "failed"} exit=${step.codeStep.exitCode} ${step.codeStep.durationMs}ms record=${step.recordPath}\n`,
454
+ );
575
455
  }
576
- const correlation = fleetGateCorrelation(subject, decider);
577
- const decided = decideReviewGate({
578
- group: `${fleetRootId}:${loop.id}`,
579
- cycle: attempt,
580
- terminalCycle: terminalAttempt,
581
- subjects: [{ runId: subject.runId, digest: subject.integrity.digest }],
582
- decider: { runId: decider.runId, digest: decider.integrity.digest },
583
- correlation,
584
- output: result.output,
585
- });
586
- independentReviewFresh = decided.verdict === "pass" && correlation.independent;
587
- const staged = stagePendingGateDecision(decided.draft);
588
- const { path: decisionPath } = materializePendingGateDecision(staged);
589
- if (!json) {
590
- process.stdout.write(
591
- `loop ${loop.id} cycle ${attempt}: ${decided.draft.outcome}${decided.draft.detail === undefined ? "" : ` (${decided.draft.detail})`} decision=${decisionPath}\n`,
592
- );
593
- }
594
- return {
595
- resolved: decided.verdict === "pass",
596
- findings: decided.findings,
597
- needsDecision: decided.needsDecision,
598
- };
599
- },
600
- beginWriteBoundary: (window, stepIds) => boundaryEnforcer.begin(window, stepIds),
601
- verifyWriteBoundary: (window, stepIds) => boundaryEnforcer.verify(window, stepIds),
602
- cancel: (assignmentId) => dispatch.abort(assignmentId),
603
- release: (ownerId) => {
604
- if (ownerId === NO_WORKER_RESERVATION) return;
605
- dispatch.reservations?.rollbackUnconsumed(ownerId);
606
456
  },
607
- releaseUnconsumed: (ownerId) => {
608
- if (ownerId === NO_WORKER_RESERVATION) return;
609
- dispatch.reservations?.rollbackUnconsumed(ownerId);
457
+ onNotice(text, kind) {
458
+ if (kind === "write-boundary") process.stderr.write(`${text}\n`);
459
+ else if (kind === "gate" && !json) process.stdout.write(`${text}\n`);
610
460
  },
611
461
  });
612
462
  } catch (err) {
@@ -616,53 +466,40 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
616
466
  }
617
467
  await dispatch.drain();
618
468
  await loaded.stop();
619
- // A loop is judged by whether it converged, not attempt by attempt: a red
620
- // verification that a later attempt fixed is the loop working as declared,
621
- // and a node a resolved loop made unnecessary never had to run at all.
622
- const required = plan.steps.filter((step) => step.loop === undefined && !planOutcome.unneeded.includes(step.id));
623
- const failedLoops = planOutcome.loops.filter((loop) => !loop.resolved);
624
- const succeededSteps = required.filter((step) => planOutcome.results.get(step.id)?.succeeded === true).length;
625
- const resolvedLoops = planOutcome.loops.length - failedLoops.length;
469
+
626
470
  if (json) {
627
471
  process.stdout.write(
628
472
  `${JSON.stringify({
629
473
  fleet: contract.name,
630
474
  rootId: fleetRootId,
631
475
  planHash: plan.hash,
632
- loops: planOutcome.loops,
633
- revalidated: planOutcome.revalidated,
634
- unneeded: planOutcome.unneeded,
635
- skipped: planOutcome.skipped,
636
- needsDecision: planOutcome.needsDecision,
637
- writeBoundaries: planOutcome.writeBoundaries,
476
+ loops: outcome.result.loops,
477
+ revalidated: outcome.result.revalidated,
478
+ unneeded: outcome.result.unneeded,
479
+ skipped: outcome.result.skipped,
480
+ needsDecision: outcome.result.needsDecision,
481
+ writeBoundaries: outcome.result.writeBoundaries,
638
482
  })}\n`,
639
483
  );
640
484
  } else {
641
- const spentUsd = receipts.reduce((sum, receipt) => sum + receipt.costUsd, 0);
642
485
  process.stdout.write(
643
- `fleet ${contract.name}: ${succeededSteps}/${required.length} steps succeeded, ${resolvedLoops}/${planOutcome.loops.length} loops resolved, total cost $${spentUsd.toFixed(4)}\n`,
486
+ `fleet ${contract.name}: ${outcome.succeededStepCount}/${outcome.requiredStepCount} steps succeeded, ${outcome.resolvedLoopCount}/${outcome.result.loops.length} loops resolved, total cost $${outcome.totalCostUsd.toFixed(4)}\n`,
644
487
  );
645
- for (const loop of planOutcome.loops) {
488
+ for (const loop of outcome.result.loops)
646
489
  process.stdout.write(
647
490
  ` loop ${loop.loopId}: ${loop.reason} after ${loop.attempts} verification(s) and ${loop.repairs} repair(s)\n`,
648
491
  );
649
- }
650
- if (planOutcome.revalidated.length > 0) {
492
+ if (outcome.result.revalidated.length > 0)
651
493
  process.stdout.write(
652
- ` staleness: re-ran ${planOutcome.revalidated.join(", ")} because a workspace step landed after the last green\n`,
494
+ ` staleness: re-ran ${outcome.result.revalidated.join(", ")} because a workspace step landed after the last green\n`,
653
495
  );
654
- }
655
- for (const message of planOutcome.needsDecision) process.stdout.write(` needs operator decision: ${message}\n`);
656
- for (const boundary of planOutcome.writeBoundaries) {
496
+ for (const message of outcome.result.needsDecision) process.stdout.write(` needs operator decision: ${message}\n`);
497
+ for (const boundary of outcome.result.writeBoundaries) {
657
498
  if (!boundary.violated) continue;
658
499
  process.stdout.write(` write boundary ${boundary.window}: ${boundary.detail ?? WRITE_BOUNDARY_VIOLATION_REASON}\n`);
659
500
  }
660
501
  }
661
- const cleanRun =
662
- failedLoops.length === 0 &&
663
- planOutcome.skipped.length === 0 &&
664
- required.every((step) => planOutcome.results.get(step.id)?.succeeded === true);
665
- return cleanRun ? 0 : 1;
502
+ return outcome.cleanRun ? 0 : 1;
666
503
  }
667
504
 
668
505
  // ---------------------------------------------------------------------------
@@ -740,6 +577,7 @@ export function statusSnapshot(): {
740
577
  return {
741
578
  runId: row.id,
742
579
  agentId: row.agentId,
580
+ budget: row.budget ?? null,
743
581
  runtimeKind: row.runtimeKind,
744
582
  outcomePhase: row.status,
745
583
  heartbeat: rowHeartbeat(row),
@@ -798,6 +636,13 @@ function runStatus(args: ReadonlyArray<string>): number {
798
636
  process.stdout.write(
799
637
  ` ${row.runId} ${row.agentId} node=${row.node} ${row.heartbeat} attempt=${lineage.attempt} depth=${lineage.depth} ${Math.round((row.elapsedMs as number) / 1000)}s $${(row.costUsd as number).toFixed(4)}\n`,
800
638
  );
639
+ const budget = row.budget as RunToolBudgetEnvelope | null;
640
+ if (budget !== null) {
641
+ process.stdout.write(` recipe policy: ${formatBudgetPolicy(budget)}\n`);
642
+ process.stdout.write(` requested envelope: ${formatBudgetRequest(budget)}\n`);
643
+ process.stdout.write(` effective envelope: ${formatEffectiveBudget(budget)}\n`);
644
+ process.stdout.write(` clamp or escalation reason: ${formatBudgetReasons(budget)}\n`);
645
+ }
801
646
  }
802
647
  }
803
648
  if (snapshot.retrying.length === 0) {
@@ -852,6 +697,14 @@ export async function runFleetCommand(args: ReadonlyArray<string>): Promise<numb
852
697
  switch (sub) {
853
698
  case "list":
854
699
  return runList(args.slice(1));
700
+ case "new":
701
+ return (await import("./fleet-new.js")).runFleetNew(args.slice(1));
702
+ case "validate":
703
+ return (await import("./fleet-validate.js")).runFleetValidate(args.slice(1));
704
+ case "graph":
705
+ return (await import("./fleet-graph.js")).runFleetGraph(args.slice(1));
706
+ case "commands":
707
+ return (await import("./fleet-commands.js")).runFleetCommands(args.slice(1));
855
708
  case "run":
856
709
  return runFleet(args.slice(1));
857
710
  case "status":