@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
@@ -1,8 +1,13 @@
1
- import { resolve } from "node:path";
1
+ import { isAbsolute, relative, resolve } from "node:path";
2
2
  import type { DispatchPlanTaskResolution, DispatchRequest } from "../domains/dispatch/contract.js";
3
3
  import type { ExecutionPlan } from "../domains/dispatch/execution-plan.js";
4
4
  import { gateDeciderAgentId } from "../domains/dispatch/execution-role.js";
5
- import { JUDGE_GATE_PROMPT, REVIEWER_GATE_PROMPT } from "../domains/dispatch/gate-role-prompts.js";
5
+ import {
6
+ COUNCIL_JUDGE_PROMPT,
7
+ JUDGE_GATE_PROMPT,
8
+ REVIEWER_GATE_PROMPT,
9
+ } from "../domains/dispatch/gate-role-prompts.js";
10
+ import { normalizeDispatchIntent } from "../domains/dispatch/intent.js";
6
11
  import { approvedRouteCandidates } from "../domains/dispatch/route-approval.js";
7
12
  import { defaultRoutingIntent } from "../domains/dispatch/routing-intent.js";
8
13
  import type { DispatchFailoverMode } from "../domains/dispatch/validation.js";
@@ -29,12 +34,15 @@ import {
29
34
  } from "./dispatch-scout-admission.js";
30
35
  import type {
31
36
  DispatchCompeteSettings,
37
+ DispatchCouncilSettings,
32
38
  DispatchExecutionSnapshot,
33
39
  DispatchMode,
34
40
  DispatchReviewSettings,
35
41
  DispatchToolDeps,
36
42
  } from "./dispatch-types.js";
37
43
  import type { ToolSpec } from "./registry.js";
44
+ import { gitCheckoutRoot } from "./task-worktree.js";
45
+ import { discoverDeclaredChecks } from "./verify/scripts.js";
38
46
 
39
47
  /**
40
48
  * The six identity-sensitive stores which bind synchronous admission to the
@@ -68,6 +76,64 @@ const COMPETE_SINGLE_TASK_MESSAGE = "dispatch: compete requires exactly one task
68
76
  const COMPETE_NO_REVIEW_MESSAGE = "dispatch: compete has its own judge and cannot combine with review";
69
77
  const COMPETE_MIN_CANDIDATES = 2;
70
78
  const COMPETE_MAX_CANDIDATES = 4;
79
+ const COUNCIL_LABEL = /^[a-z][a-z0-9_-]{0,31}$/u;
80
+
81
+ function councilSettingsFromArgs(
82
+ args: Record<string, unknown>,
83
+ deps: DispatchToolDeps,
84
+ ): { ok: true; council: DispatchCouncilSettings } | { ok: false; message: string } {
85
+ const rosterName = stringArg(args, "roster");
86
+ const hasMembers = Object.hasOwn(args, "members");
87
+ if ((rosterName === undefined) === !hasMembers) {
88
+ return { ok: false, message: "dispatch: council requires exactly one of roster or members" };
89
+ }
90
+ let members: DispatchCouncilSettings["members"];
91
+ if (rosterName !== undefined) {
92
+ const roster = deps.getWorkerRosters?.()[rosterName];
93
+ if (roster === undefined) return { ok: false, message: `council_roster_unknown: ${rosterName}` };
94
+ members = roster.members.map((member) => ({ ...member }));
95
+ } else {
96
+ if (!Array.isArray(args.members)) return { ok: false, message: "council_members_out_of_range" };
97
+ members = [];
98
+ for (const raw of args.members) {
99
+ if (!isRecord(raw)) return { ok: false, message: "council_members_out_of_range" };
100
+ const label = stringArg(raw, "label");
101
+ const target = stringArg(raw, "target");
102
+ if (label === undefined || target === undefined || !COUNCIL_LABEL.test(label)) {
103
+ return { ok: false, message: "council_members_out_of_range" };
104
+ }
105
+ const model = stringArg(raw, "model");
106
+ const thinking = stringArg(raw, "thinking");
107
+ members.push({ label, target, ...(model ? { model } : {}), ...(thinking ? { thinking } : {}) });
108
+ }
109
+ }
110
+ if (members.length < 2 || members.length > 5) return { ok: false, message: "council_members_out_of_range" };
111
+ const labels = new Set<string>();
112
+ for (const member of members) {
113
+ if (labels.has(member.label)) return { ok: false, message: `council_member_label_duplicate: ${member.label}` };
114
+ labels.add(member.label);
115
+ }
116
+ const synthesis = args.synthesis === undefined ? "none" : args.synthesis;
117
+ if (synthesis !== "none" && synthesis !== "judge" && synthesis !== "vote") {
118
+ return { ok: false, message: "dispatch: synthesis must be none, judge, or vote" };
119
+ }
120
+ if (args.judge !== undefined && synthesis !== "judge") {
121
+ return { ok: false, message: "council_synthesis_requires_judge_settings" };
122
+ }
123
+ const rounds = args.rounds === undefined ? 1 : args.rounds;
124
+ if (!Number.isInteger(rounds) || Number(rounds) < 1 || Number(rounds) > 3) {
125
+ return { ok: false, message: "dispatch: rounds must be an integer 1..3" };
126
+ }
127
+ const judge = isRecord(args.judge)
128
+ ? Object.fromEntries(
129
+ ["agent", "model", "target", "node"].flatMap((key) => {
130
+ const value = stringArg(args.judge as Record<string, unknown>, key);
131
+ return value === undefined ? [] : [[key, value]];
132
+ }),
133
+ )
134
+ : undefined;
135
+ return { ok: true, council: { members, synthesis, rounds: Number(rounds), ...(judge ? { judge } : {}) } };
136
+ }
71
137
 
72
138
  function isRecord(value: unknown): value is Record<string, unknown> {
73
139
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -165,6 +231,28 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
165
231
  approvedAuthorities: ["read-only", "verification", "artifact-write", "workspace-edit"],
166
232
  authorityBasis: deps.getAutonomy?.() === "full-auto" ? "full-auto-policy" : "operator-plan-approval",
167
233
  },
234
+ resolveIntent(rawIntent, cwd) {
235
+ const discovery = discoverDeclaredChecks(cwd);
236
+ if (!discovery.ok) return { ok: false, message: `verification_catalog_invalid: ${discovery.reason}` };
237
+ const declared = discovery.sources.flatMap((source) => source.checks);
238
+ const byId = new Map(declared.map((check) => [check.id, check]));
239
+ const normalized = normalizeDispatchIntent(rawIntent, byId);
240
+ if (!normalized.ok) return { ok: false, message: `${normalized.reason}: ${normalized.message}` };
241
+ return {
242
+ ok: true,
243
+ intent: normalized.intent,
244
+ resolvedVerification: normalized.intent.verification.map((entry) => {
245
+ const check = byId.get(entry.check);
246
+ if (check === undefined) throw new Error(`resolved verification check '${entry.check}' disappeared`);
247
+ return {
248
+ check: entry.check,
249
+ argv: [...check.command],
250
+ cwd: resolve(process.cwd(), check.cwd),
251
+ timeoutMs: entry.timeoutMs,
252
+ };
253
+ }),
254
+ };
255
+ },
168
256
  });
169
257
  const trustExecution = (args: Record<string, unknown>, snapshot: DispatchExecutionSnapshot) => {
170
258
  state.trustedExecutionSnapshots.set(args, deepFreeze(structuredClone(snapshot)));
@@ -172,11 +260,15 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
172
260
  };
173
261
  const dispatchExecutionSnapshot = (
174
262
  args: Record<string, unknown>,
175
- fields: Pick<DispatchExecutionSnapshot & { kind: "dispatch" }, "requests" | "mode" | "review" | "compete">,
263
+ fields: Pick<
264
+ DispatchExecutionSnapshot & { kind: "dispatch" },
265
+ "requests" | "mode" | "review" | "compete" | "council"
266
+ >,
176
267
  ): DispatchExecutionSnapshot => ({
177
268
  kind: "dispatch",
178
269
  planView: describeDispatchPlan(args),
179
270
  ...fields,
271
+ writers: args.writers === 1 ? 1 : undefined,
180
272
  detach: args.detach === true,
181
273
  timeoutMs: timeoutMsArg(args),
182
274
  maxOutputBytes: maxOutputBytesArg(args),
@@ -217,6 +309,9 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
217
309
  ): ResolvedPlanTask => {
218
310
  const resolution = deps.dispatch.preview?.(request);
219
311
  if (resolution === undefined) throw new Error("dispatch preview is unavailable");
312
+ if (request.resolvedVerification?.length && resolution.runtimeId === "claude-code") {
313
+ throw new Error("verification_unsupported_runtime: claude-code subprocess dispatch cannot run host verification");
314
+ }
220
315
  const pinned = request.node !== undefined || request.target !== undefined || request.model !== undefined;
221
316
  const approvedCandidates =
222
317
  pinned || request.failover !== "approved"
@@ -229,6 +324,15 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
229
324
  agent: resolution.agentId,
230
325
  task: request.task,
231
326
  ...(request.briefing !== undefined ? { briefing: request.briefing } : {}),
327
+ ...(request.worktree === true
328
+ ? {
329
+ worktree: true as const,
330
+ apply: request.apply ?? "merge",
331
+ ...(gitCheckoutRoot(resolve(request.cwd ?? process.cwd())) === null
332
+ ? {}
333
+ : { worktreeDestination: gitCheckoutRoot(resolve(request.cwd ?? process.cwd())) as string }),
334
+ }
335
+ : {}),
232
336
  target: resolution.targetId,
233
337
  model: resolution.wireModelId,
234
338
  node: resolution.node.id,
@@ -248,6 +352,10 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
248
352
  ...(failover === "approved" ? { allowedCandidates: approvedCandidates.map((candidate) => ({ ...candidate })) } : {}),
249
353
  role,
250
354
  position,
355
+ ...(request.intent !== undefined ? { intent: structuredClone(request.intent) } : {}),
356
+ ...(request.resolvedVerification !== undefined
357
+ ? { resolvedVerification: request.resolvedVerification.map((check) => ({ ...check, argv: [...check.argv] })) }
358
+ : {}),
251
359
  };
252
360
  state.taskResolutions.set(task, resolution);
253
361
  return task;
@@ -269,6 +377,8 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
269
377
  if (topology === "fleet") return task.wave ?? index;
270
378
  if (topology === "parallel" || topology === "detached") return 0;
271
379
  if (topology === "compete") return task.role === "judge" ? 1 : 0;
380
+ if (topology === "council")
381
+ return task.role === "synthesis" ? (task.council?.rounds ?? 1) : (task.council?.round ?? 1) - 1;
272
382
  return index;
273
383
  };
274
384
  const markReservedPlan = (args: Record<string, unknown>, artifact: ResolvedDispatchPlanArtifact) => {
@@ -286,7 +396,7 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
286
396
  };
287
397
  });
288
398
  const reservation = deps.dispatch.reservations.prepare({
289
- topology: artifact.topology === "fleet" ? "parallel" : artifact.topology,
399
+ topology: artifact.topology === "fleet" || artifact.topology === "council" ? "parallel" : artifact.topology,
290
400
  tasks,
291
401
  });
292
402
  try {
@@ -333,6 +443,7 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
333
443
  mode: "parallel",
334
444
  review: undefined,
335
445
  compete: undefined,
446
+ council: undefined,
336
447
  }),
337
448
  );
338
449
  state.trustedExecutionPlans.set(marked, deepFreeze(structuredClone(prepared.executionPlan)));
@@ -375,10 +486,13 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
375
486
  }
376
487
  const parsed = parseRequests(args);
377
488
  if (!parsed.ok) return shapeRejection(args, parsed.message);
378
- if (args.mode !== undefined && !["parallel", "sequential", "pipeline", "compete"].includes(String(args.mode))) {
489
+ if (
490
+ args.mode !== undefined &&
491
+ !["parallel", "sequential", "pipeline", "compete", "council"].includes(String(args.mode))
492
+ ) {
379
493
  return shapeRejection(
380
494
  args,
381
- `dispatch: mode must be parallel, sequential, pipeline, or compete; got '${String(args.mode)}'`,
495
+ `dispatch: mode must be parallel, sequential, pipeline, compete, or council; got '${String(args.mode)}'`,
382
496
  );
383
497
  }
384
498
  const mode: DispatchMode =
@@ -388,7 +502,30 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
388
502
  ? "pipeline"
389
503
  : args.mode === "compete"
390
504
  ? "compete"
391
- : "parallel";
505
+ : args.mode === "council"
506
+ ? "council"
507
+ : "parallel";
508
+ if (args.writers !== undefined && args.writers !== 1) {
509
+ return shapeRejection(args, "dispatch: writers must be 1 when present");
510
+ }
511
+ if (args.writers === 1 && mode !== "parallel") {
512
+ return shapeRejection(args, "dispatch: writers is supported only for parallel dispatch");
513
+ }
514
+ const parentCheckout = gitCheckoutRoot(process.cwd());
515
+ for (const request of parsed.requests) {
516
+ if (request.worktree !== true) continue;
517
+ if (mode === "compete") return shapeRejection(args, "worktree_compete_incompatible");
518
+ if (parentCheckout === null) return shapeRejection(args, "worktree_non_git_checkout");
519
+ const spec = deps.getAgentSpecs().find((candidate) => candidate.id === request.agentId);
520
+ if (spec?.capabilityClass === "read-only") return shapeRejection(args, "worktree_read_only_agent");
521
+ if (request.cwd !== undefined) {
522
+ const requested = resolve(request.cwd);
523
+ const rel = relative(parentCheckout, requested);
524
+ if (rel === ".." || rel.startsWith("../") || isAbsolute(rel)) {
525
+ return shapeRejection(args, "worktree_cwd_outside_checkout");
526
+ }
527
+ }
528
+ }
392
529
  const reviewResult = reviewSettingsFromArgs(args);
393
530
  if (!reviewResult.ok) return shapeRejection(args, reviewResult.message);
394
531
  if (reviewResult.review !== undefined) {
@@ -397,17 +534,35 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
397
534
  if (parsed.requests.length !== 1 || parsed.requests[0] === undefined) {
398
535
  return shapeRejection(args, REVIEW_SINGLE_TASK_MESSAGE);
399
536
  }
537
+ if (parsed.requests.some((request) => (request.resolvedVerification?.length ?? 0) > 0)) {
538
+ return shapeRejection(args, "verification_unsupported_for_mode: review requests cannot run host verification");
539
+ }
400
540
  }
401
541
  if (mode === "compete" && (parsed.requests.length !== 1 || parsed.requests[0] === undefined)) {
402
542
  return shapeRejection(args, COMPETE_SINGLE_TASK_MESSAGE);
403
543
  }
544
+ if (mode === "compete" && parsed.requests.some((request) => (request.resolvedVerification?.length ?? 0) > 0)) {
545
+ return shapeRejection(args, "verification_unsupported_for_mode: compete requests cannot run host verification");
546
+ }
404
547
  const competeResult = mode === "compete" ? competeSettingsFromArgs(args) : undefined;
405
548
  if (competeResult !== undefined && !competeResult.ok) return shapeRejection(args, competeResult.message);
549
+ const councilResult = mode === "council" ? councilSettingsFromArgs(args, deps) : undefined;
550
+ if (councilResult !== undefined && !councilResult.ok) return shapeRejection(args, councilResult.message);
551
+ if (mode === "council") {
552
+ if (parsed.requests.length !== 1 || parsed.requests[0] === undefined) {
553
+ return shapeRejection(args, "dispatch: council requires exactly one task");
554
+ }
555
+ if (reviewResult.review !== undefined) return shapeRejection(args, "dispatch: council cannot combine with review");
556
+ if ((parsed.requests[0].resolvedVerification?.length ?? 0) > 0) {
557
+ return shapeRejection(args, "council_verification_unsupported");
558
+ }
559
+ }
406
560
  const snapshotFields = {
407
561
  requests: parsed.requests,
408
562
  mode,
409
563
  review: reviewResult.review,
410
564
  compete: competeResult?.ok === true ? competeResult.compete : undefined,
565
+ council: councilResult?.ok === true ? councilResult.council : undefined,
411
566
  };
412
567
  if (deps.dispatch.preview === undefined) {
413
568
  const marked = markPrepared(args);
@@ -480,6 +635,72 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
480
635
  1,
481
636
  ),
482
637
  );
638
+ } else if (mode === "council") {
639
+ const council = councilResult?.ok === true ? councilResult.council : undefined;
640
+ const base = parsed.requests[0];
641
+ if (council === undefined || base === undefined)
642
+ throw new Error("dispatch: council admission settings are unavailable");
643
+ const subjects: Array<{ runId: string; digest: null }> = [];
644
+ for (let round = 1; round <= council.rounds; round += 1) {
645
+ for (const [index, member] of council.members.entries()) {
646
+ const memberRequest: DispatchRequest = {
647
+ ...base,
648
+ executionRole: "researcher",
649
+ autonomy: "read-only",
650
+ toolProfile: "council-read-only",
651
+ target: member.target,
652
+ ...(member.model ? { model: member.model } : {}),
653
+ ...(member.thinking ? { thinkingLevel: member.thinking as NonNullable<DispatchRequest["thinkingLevel"]> } : {}),
654
+ gate: { role: "member", group: "plan-preview", cycle: round },
655
+ };
656
+ let task: ResolvedPlanTask;
657
+ try {
658
+ task = resolveTask(memberRequest, "member", (round - 1) * council.members.length + index + 1);
659
+ } catch (error) {
660
+ return shapeRejection(
661
+ args,
662
+ `council_member_target_unknown: ${member.label}: ${error instanceof Error ? error.message : String(error)}`,
663
+ );
664
+ }
665
+ if (task.node !== "local" || task.nodeKind !== "local") {
666
+ return shapeRejection(args, `council_member_remote_node: ${member.label}`);
667
+ }
668
+ task.council = {
669
+ label: member.label,
670
+ ...(member.color ? { color: member.color } : {}),
671
+ round,
672
+ rounds: council.rounds,
673
+ synthesis: council.synthesis,
674
+ };
675
+ if (round === council.rounds) subjects.push({ runId: `plan-member-${member.label}`, digest: null });
676
+ tasks.push(task);
677
+ }
678
+ }
679
+ if (council.synthesis === "judge") {
680
+ const judgeRequest: DispatchRequest = {
681
+ agentId: council.judge?.agent ?? base.agentId,
682
+ executionRole: "judge",
683
+ task: "Plan-time capability check for council synthesis.",
684
+ systemPrompt: COUNCIL_JUDGE_PROMPT,
685
+ autonomy: "read-only",
686
+ toolProfile: "council-read-only",
687
+ gate: { role: "synthesis", group: "plan-preview", cycle: council.rounds, subjects },
688
+ ...(council.judge?.node ? { node: council.judge.node } : {}),
689
+ ...(council.judge?.model ? { model: council.judge.model } : {}),
690
+ ...(council.judge?.target ? { target: council.judge.target } : {}),
691
+ };
692
+ const judgeTask = resolveTask(judgeRequest, "synthesis", 1);
693
+ if (judgeTask.node !== "local" || judgeTask.nodeKind !== "local") {
694
+ return shapeRejection(args, "council_member_remote_node: judge");
695
+ }
696
+ judgeTask.council = {
697
+ label: "synthesis",
698
+ round: council.rounds,
699
+ rounds: council.rounds,
700
+ synthesis: council.synthesis,
701
+ };
702
+ tasks.push(judgeTask);
703
+ }
483
704
  } else {
484
705
  for (const [index, request] of parsed.requests.entries()) tasks.push(resolveTask(request, "task", index + 1));
485
706
  }
@@ -490,7 +711,20 @@ export function createDispatchAdmissionController(deps: DispatchToolDeps): Dispa
490
711
  topology === "compete" ||
491
712
  tasks.some((task) => task.node !== "local" || task.failover === "approved");
492
713
  if (!planScale) {
493
- const marked = markPrepared(args);
714
+ const marked = parsed.requests.some((request) => request.intent !== undefined)
715
+ ? markPrepared(
716
+ withResolvedDispatchPlan(args, {
717
+ version: 3,
718
+ topology,
719
+ source: null,
720
+ maxWorkers: null,
721
+ onFailure: null,
722
+ tasks,
723
+ costCeilingUsd: resolvedCostCeiling(),
724
+ deadlineMs: null,
725
+ }),
726
+ )
727
+ : markPrepared(args);
494
728
  return trustExecution(marked, dispatchExecutionSnapshot(marked, snapshotFields));
495
729
  }
496
730
  const marked = markReservedPlan(args, {
@@ -2,13 +2,22 @@
2
2
 
3
3
  import type { AgentAutomationAuthority } from "../domains/agents/spec.js";
4
4
  import { type AgentTaskType, classifyAgentTask } from "../domains/dispatch/agent-candidates.js";
5
+ import { cloneDispatchBudgetRequest } from "../domains/dispatch/budget-envelope.js";
5
6
  import type { DispatchRequest } from "../domains/dispatch/contract.js";
6
7
  import { type AgentRoleFactsResolver, requestExecutionRole } from "../domains/dispatch/execution-role.js";
8
+ import type { DispatchIntent } from "../domains/dispatch/intent.js";
7
9
  import { parseRoutingIntent } from "../domains/dispatch/routing-intent.js";
8
10
  import { DISPATCH_BRIEFING_MAX_BYTES, type JobThinkingLevel } from "../domains/dispatch/validation.js";
9
11
  import { isToolProfileName, TOOL_PROFILE_NAMES } from "./profiles.js";
10
12
 
11
13
  const DEFAULT_AGENT_ID = "coder";
14
+ /**
15
+ * The agent a council seats when the caller names none. Council admission pins
16
+ * every member to the `council-read-only` tool profile, and `coder` requires a
17
+ * write tool, so the ordinary default would fail admission for every member.
18
+ * `researcher` is the builtin read-only answerer.
19
+ */
20
+ const COUNCIL_DEFAULT_AGENT_ID = "researcher";
12
21
 
13
22
  /**
14
23
  * Baseline recipe per task shape for `agent:"auto"`.
@@ -68,6 +77,12 @@ export interface DispatchArgumentParserOptions {
68
77
  approvedAuthorities: ReadonlyArray<AgentAutomationAuthority>;
69
78
  authorityBasis: "operator-plan-approval" | "full-auto-policy";
70
79
  };
80
+ resolveIntent?: (
81
+ rawIntent: unknown,
82
+ cwd: string | undefined,
83
+ ) =>
84
+ | { ok: true; intent: DispatchIntent; resolvedVerification: NonNullable<DispatchRequest["resolvedVerification"]> }
85
+ | { ok: false; message: string };
71
86
  }
72
87
 
73
88
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -99,7 +114,8 @@ function dispatchRequestFromArgs(
99
114
  const task = stringArg(args, "task");
100
115
  if (!task) return { ok: false, message: "missing task (pass list:true to see available agents)" };
101
116
  if (Object.hasOwn(args, "agent_id")) return { ok: false, message: "agent_id is unsupported; use agent" };
102
- const requestedAgent = stringArg(args, "agent") ?? DEFAULT_AGENT_ID;
117
+ const requestedAgent =
118
+ stringArg(args, "agent") ?? (args.mode === "council" ? COUNCIL_DEFAULT_AGENT_ID : DEFAULT_AGENT_ID);
103
119
  const auto = requestedAgent === "auto";
104
120
  const agentId = auto ? autoBaselineAgentId(task, options.hasAgent) : requestedAgent;
105
121
  const request: DispatchRequest = {
@@ -149,6 +165,47 @@ function dispatchRequestFromArgs(
149
165
  request.requiredCapabilities = routing.intent.requiredCapabilities;
150
166
  const cwd = stringArg(args, "cwd");
151
167
  if (cwd) request.cwd = cwd;
168
+ if (args.worktree !== undefined) {
169
+ if (args.worktree !== true) return { ok: false, message: "worktree must be true when present" };
170
+ request.worktree = true;
171
+ }
172
+ if (args.apply !== undefined) {
173
+ if (args.apply !== "merge" && args.apply !== "preserve")
174
+ return { ok: false, message: "apply must be merge or preserve" };
175
+ if (request.worktree !== true) return { ok: false, message: "apply requires worktree: true" };
176
+ request.apply = args.apply;
177
+ }
178
+ if (Object.hasOwn(args, "gate") && isRecord(args.intent) && Object.hasOwn(args.intent, "verification")) {
179
+ return { ok: false, message: "gate_and_intent_verification_conflict: gate cannot combine with intent.verification" };
180
+ }
181
+ let rawIntent = args.intent;
182
+ if (Object.hasOwn(args, "gate")) {
183
+ if (typeof args.gate !== "string" || args.gate.trim().length === 0) {
184
+ return { ok: false, message: "gate must be a non-empty declared check id" };
185
+ }
186
+ rawIntent = { ...(isRecord(rawIntent) ? rawIntent : {}), verification: [{ check: args.gate.trim() }] };
187
+ }
188
+ if (rawIntent !== undefined) {
189
+ if (options.resolveIntent === undefined) return { ok: false, message: "intent resolver is unavailable" };
190
+ const resolved = options.resolveIntent(rawIntent, cwd);
191
+ if (!resolved.ok) return resolved;
192
+ request.intent = resolved.intent;
193
+ if (resolved.resolvedVerification.length > 0) request.resolvedVerification = resolved.resolvedVerification;
194
+ if (resolved.intent.writeRoots.length > 0) {
195
+ const legacy = Array.isArray(args.writeRoots)
196
+ ? args.writeRoots.filter((entry): entry is string => typeof entry === "string")
197
+ : undefined;
198
+ if (legacy !== undefined) {
199
+ const left = [...new Set(legacy)].sort();
200
+ if (JSON.stringify(left) !== JSON.stringify(resolved.intent.writeRoots)) {
201
+ return { ok: false, message: "intent_write_roots_contradiction" };
202
+ }
203
+ request.writeRoots = legacy;
204
+ } else {
205
+ request.writeRoots = resolved.intent.writeRoots;
206
+ }
207
+ }
208
+ }
152
209
  if ("persona" in args && args.persona !== undefined) {
153
210
  if (typeof args.persona !== "string") return { ok: false, message: "persona must be a string" };
154
211
  const persona = args.persona.trim();
@@ -171,6 +228,13 @@ function dispatchRequestFromArgs(
171
228
  }
172
229
  request.thinkingLevel = thinkingLevel as JobThinkingLevel;
173
230
  }
231
+ if (args.budget !== undefined) {
232
+ try {
233
+ request.budget = cloneDispatchBudgetRequest(args.budget);
234
+ } catch (error) {
235
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
236
+ }
237
+ }
174
238
  return { ok: true, request };
175
239
  }
176
240
 
@@ -1,6 +1,25 @@
1
+ import { responseModelIdObservationFromRecord } from "../core/response-model-id.js";
1
2
  import { durableAssistantTextFromEvent } from "../domains/dispatch/event-pump.js";
3
+ import type { RunReceipt } from "../domains/dispatch/types.js";
2
4
 
3
5
  /** Return the durable final assistant text carried by a worker event. */
4
6
  export function assistantTextFromEvent(event: unknown): string {
5
7
  return durableAssistantTextFromEvent(event).trim();
6
8
  }
9
+
10
+ /** Human projection of the response model-id observations carried by a receipt. */
11
+ export function receiptResponseModelIdObservationLabel(receipt: Pick<RunReceipt, "upstreamResponses">): string | null {
12
+ const responses = receipt.upstreamResponses ?? [];
13
+ if (responses.length === 0) return null;
14
+ const labels = responses.map((response) => {
15
+ const observation = responseModelIdObservationFromRecord(
16
+ response as unknown as Record<string, unknown>,
17
+ "legacy-difference-only",
18
+ );
19
+ if (observation.state === "reported") return `reported:${observation.reportedModelId}`;
20
+ if (observation.state === "not-reported") return "not-reported";
21
+ if (observation.state === "not-observed") return "not-observed";
22
+ return `legacy-difference-only:${observation.differingModelId ?? "none"}`;
23
+ });
24
+ return `response_model_id_observation=${labels.join(",")}`;
25
+ }