@iowarp/clio-coder 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (416) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/NOTICE +33 -0
  3. package/README.md +7 -2
  4. package/dist/{acp-7LOELQFP.js → acp-G5WJBNCT.js} +13 -12
  5. package/dist/{agents-FIBG2SHA.js → agents-FMV2Q5G4.js} +39 -36
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-OI4LIH2I.js → auth-3IDSJEIK.js} +16 -15
  8. package/dist/{builtins-AD25UL3C.js → builtins-XCZWXSC7.js} +3 -3
  9. package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
  10. package/dist/{chunk-56KB5IJP.js → chunk-2OQE55CK.js} +3 -3
  11. package/dist/{chunk-N3PBVRTZ.js → chunk-32KWKNSF.js} +9 -7
  12. package/dist/{chunk-VKBMFOYV.js → chunk-36EJLSQQ.js} +23 -27
  13. package/dist/{chunk-465CC7FK.js → chunk-3BT2XMV4.js} +16 -13
  14. package/dist/{chunk-NQ6UCCOD.js → chunk-3URVFKWK.js} +7 -7
  15. package/dist/{chunk-WIE7ZOSW.js → chunk-3XML7CDN.js} +2 -2
  16. package/dist/chunk-42FMPA75.js +101 -0
  17. package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
  18. package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
  19. package/dist/{chunk-JEQQR47K.js → chunk-6DB53AJS.js} +168 -9
  20. package/dist/{chunk-KV2AOLDF.js → chunk-76ONBSIA.js} +5 -25
  21. package/dist/{chunk-FALJGAWU.js → chunk-7MCTRUCE.js} +2 -2
  22. package/dist/{chunk-5T7RBWN2.js → chunk-AB44T6BB.js} +5 -5
  23. package/dist/chunk-B7OBL7PK.js +1405 -0
  24. package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
  25. package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
  26. package/dist/{chunk-O6I4CIEU.js → chunk-BKFJHQCA.js} +6 -6
  27. package/dist/chunk-BKFM6EJV.js +462 -0
  28. package/dist/chunk-BMS5RKQY.js +27 -0
  29. package/dist/{chunk-MV2VUEJC.js → chunk-BPKCPIL7.js} +2 -2
  30. package/dist/{chunk-TEO2TLVN.js → chunk-BUMFYQFY.js} +964 -1120
  31. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  32. package/dist/chunk-C2LTL2W6.js +2447 -0
  33. package/dist/{chunk-EBOC7MT3.js → chunk-CODPRO7Q.js} +8 -8
  34. package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
  35. package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
  36. package/dist/{chunk-MIX5N5AC.js → chunk-DQITNCXG.js} +394 -154
  37. package/dist/{chunk-S4COXYBG.js → chunk-DYJP44XW.js} +129 -106
  38. package/dist/{chunk-N5XKWMDW.js → chunk-F4CKPOEQ.js} +6 -6
  39. package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
  40. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  41. package/dist/{chunk-7C6RYZGQ.js → chunk-GKF55TAZ.js} +3 -3
  42. package/dist/{chunk-B5XRQOLB.js → chunk-GR5G2PVF.js} +9 -8
  43. package/dist/{chunk-XDOQXGFO.js → chunk-GXNLGKAB.js} +64 -8
  44. package/dist/chunk-HHV2GANA.js +88 -0
  45. package/dist/{chunk-774ILSRL.js → chunk-HI63TFOG.js} +4 -3
  46. package/dist/{chunk-R7LNVMCS.js → chunk-HJJTYUHX.js} +64 -72
  47. package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
  48. package/dist/{chunk-CA42X6KT.js → chunk-HWHKMHUA.js} +6 -6
  49. package/dist/chunk-HZHHCK24.js +1631 -0
  50. package/dist/{chunk-HJB5IUKP.js → chunk-I5VEOC6I.js} +12 -12
  51. package/dist/chunk-IBEBSCYA.js +564 -0
  52. package/dist/chunk-IQ7KR472.js +362 -0
  53. package/dist/{chunk-4H6ULJ3H.js → chunk-J4W7KFM7.js} +921 -990
  54. package/dist/{chunk-4LJX2PUC.js → chunk-JDG2WCRO.js} +5 -5
  55. package/dist/{chunk-NZU6YDNV.js → chunk-K5C3NCBD.js} +4 -4
  56. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  57. package/dist/{chunk-NNNWO6F2.js → chunk-KFZI4NIL.js} +93 -3
  58. package/dist/chunk-KMVISBZR.js +132 -0
  59. package/dist/{chunk-BVDVID7E.js → chunk-LDQ2ZF2M.js} +2 -2
  60. package/dist/{chunk-OEDBCISO.js → chunk-LQ3DZAMX.js} +2 -2
  61. package/dist/{chunk-PZ4I4JE2.js → chunk-LY4S7GJC.js} +124 -116
  62. package/dist/{chunk-3DUR4WUA.js → chunk-MLKNTWH2.js} +19 -19
  63. package/dist/chunk-NEKRRTYW.js +56 -0
  64. package/dist/{chunk-5DQRIYDZ.js → chunk-NHCZP4K7.js} +2 -2
  65. package/dist/chunk-NHLBIGRH.js +1506 -0
  66. package/dist/chunk-NQQH3YT7.js +302 -0
  67. package/dist/chunk-NYS75XW5.js +15 -0
  68. package/dist/{chunk-TD7UE2L5.js → chunk-O4XIVISU.js} +7 -7
  69. package/dist/{chunk-WXCJ7VME.js → chunk-O6TL7WWY.js} +6 -6
  70. package/dist/chunk-OQBA45DZ.js +97 -0
  71. package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
  72. package/dist/chunk-PMZCIOCJ.js +25 -0
  73. package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
  74. package/dist/{chunk-HKO36JWF.js → chunk-QBJA7R7N.js} +31 -3
  75. package/dist/chunk-QDC3K2U3.js +262 -0
  76. package/dist/{chunk-5HFBWUMU.js → chunk-QLFS5GO2.js} +4 -4
  77. package/dist/{chunk-GAYUJ7LE.js → chunk-QLL7ILRG.js} +41 -32
  78. package/dist/{chunk-LXPJXFM5.js → chunk-QREDIESB.js} +7 -7
  79. package/dist/chunk-QSNYB6ZV.js +195 -0
  80. package/dist/chunk-R346GLFC.js +31 -0
  81. package/dist/{chunk-VPTUJU4P.js → chunk-SJ5ZKQ4S.js} +2 -2
  82. package/dist/{chunk-QQ7EKM72.js → chunk-SP2RXXYO.js} +6 -54
  83. package/dist/chunk-SUCTJL45.js +45 -0
  84. package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
  85. package/dist/chunk-T56WDKA5.js +183 -0
  86. package/dist/chunk-TVHHYFHE.js +255 -0
  87. package/dist/{chunk-IHKBWSXF.js → chunk-TZ3SGWZZ.js} +2 -2
  88. package/dist/{chunk-ECUO3KDP.js → chunk-U77AMWDL.js} +47 -7
  89. package/dist/{chunk-3UUY7R3Z.js → chunk-ULC6OTWO.js} +7 -7
  90. package/dist/{chunk-AEYBF3TB.js → chunk-UM7N4G5A.js} +2 -2
  91. package/dist/{chunk-3V5AYSEQ.js → chunk-UXMFQ54G.js} +43 -36
  92. package/dist/{chunk-YW7UVM5V.js → chunk-V5DHCITQ.js} +34 -1
  93. package/dist/{chunk-47CMYGET.js → chunk-VAZSBTKF.js} +2 -2
  94. package/dist/{chunk-3MRC2YSQ.js → chunk-VEO4AP2K.js} +2 -2
  95. package/dist/{chunk-PNY46YEY.js → chunk-VFA6GDY5.js} +47 -3
  96. package/dist/chunk-VO67MWHC.js +75 -0
  97. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  98. package/dist/{chunk-AD7Y7STJ.js → chunk-VYMXRQI6.js} +36 -22
  99. package/dist/{chunk-T3Z6VAAF.js → chunk-W5VSYASO.js} +19 -17
  100. package/dist/{chunk-5PVQ4SRS.js → chunk-WZR7K7ZX.js} +7 -123
  101. package/dist/{chunk-DG4M6ZUE.js → chunk-X3YGUTOB.js} +4 -4
  102. package/dist/chunk-X75E3D2N.js +686 -0
  103. package/dist/{chunk-P3JGPQFL.js → chunk-YDFRH54B.js} +4 -4
  104. package/dist/chunk-YJX4SHTD.js +40 -0
  105. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  106. package/dist/{chunk-YSEHGPCT.js → chunk-Z2RR6MAK.js} +3 -3
  107. package/dist/{chunk-HCBCAYZU.js → chunk-Z4TXYIEG.js} +5 -5
  108. package/dist/cli/index.js +47 -36
  109. package/dist/{clio-LT5V7SSZ.js → clio-2JXHBBY5.js} +7 -7
  110. package/dist/{code-nav-LMW275PA.js → code-nav-3YYRMYNF.js} +6 -6
  111. package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
  112. package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
  113. package/dist/{config-RXS5T3JT.js → config-QZPCMYSO.js} +58 -50
  114. package/dist/{configure-2WYWSCSD.js → configure-TEGEBYCA.js} +21 -19
  115. package/dist/{context-I3BTOTCS.js → context-AV7OEZ4D.js} +11 -11
  116. package/dist/{context-MVOORGMF.js → context-E6H5RNMC.js} +49 -41
  117. package/dist/{context-PALKKQYL.js → context-GSXUE4CT.js} +25 -23
  118. package/dist/{context-clear-N2WOYZ2K.js → context-clear-SHIBYK6T.js} +48 -40
  119. package/dist/{context-working-set-MIEVECVZ.js → context-working-set-5ZGKPGZQ.js} +11 -10
  120. package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-EFMJT4LD.js} +88 -60
  121. package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
  122. package/dist/doctor-QOA5FNY5.js +313 -0
  123. package/dist/{eval-IJ5VEZDJ.js → eval-TFBYQH4H.js} +66 -64
  124. package/dist/eval-inventory-SXH7PDKX.js +316 -0
  125. package/dist/{evidence-L5APPXNV.js → evidence-ERGESKGN.js} +198 -44
  126. package/dist/{evolve-RGNKFJ52.js → evolve-VDXTSYCJ.js} +46 -38
  127. package/dist/{extensions-7WYWUX5A.js → extensions-7BGBHN57.js} +6 -6
  128. package/dist/{fleet-6CNVBZZP.js → fleet-2RRVDF2V.js} +189 -101
  129. package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-VJ726XIA.js} +10 -10
  130. package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
  131. package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-JF5QOATM.js} +16 -15
  132. package/dist/fleet-inspect-VLY4S7QM.js +442 -0
  133. package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-AIZUEJOY.js} +6 -5
  134. package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-AJRPDMDV.js} +20 -18
  135. package/dist/fleet-verify-JFEL2L3H.js +175 -0
  136. package/dist/fleet-view-ZCON35AG.js +102 -0
  137. package/dist/{init-VBN2ACVA.js → init-DN2WWLFE.js} +64 -56
  138. package/dist/install-XGLBQY5E.js +13 -0
  139. package/dist/interop-OZBKXAYL.js +114 -0
  140. package/dist/{library-JHGUMLY2.js → library-YWZG7IMW.js} +19 -18
  141. package/dist/{memory-K4OQIYWG.js → memory-I4C4HMLW.js} +48 -40
  142. package/dist/{models-2NCZUWDD.js → models-CEYXJBO6.js} +30 -28
  143. package/dist/{monitor-MMVTJABD.js → monitor-NZ6GCI3P.js} +53 -47
  144. package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-GCGQ4N5I.js} +6311 -7145
  145. package/dist/panes-HMABYVO4.js +58 -0
  146. package/dist/panes-KY6W3V2E.js +103 -0
  147. package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
  148. package/dist/{reset-DD5JGOY3.js → reset-DQ6FGCSH.js} +13 -11
  149. package/dist/resources-BB3MVJMD.js +111 -0
  150. package/dist/{run-QEGNX7FL.js → run-H2GQDUER.js} +113 -82
  151. package/dist/{share-JKD3BQMW.js → share-GTJN6A5O.js} +18 -17
  152. package/dist/{skills-LMQIKDOZ.js → skills-L55TEW6R.js} +31 -24
  153. package/dist/{skills-eval-I7X2774U.js → skills-eval-XVXPH2JI.js} +61 -52
  154. package/dist/skills-inventory-S4MXPJFV.js +126 -0
  155. package/dist/slash-commands-ZSGASKJC.js +77 -0
  156. package/dist/{steer-CF5TDANS.js → steer-RZGSCY4R.js} +3 -3
  157. package/dist/{support-I7LOJLIF.js → support-PKEUNNQL.js} +4 -4
  158. package/dist/{targets-RUSR6B5Z.js → targets-NCPZ644J.js} +31 -29
  159. package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-44SV3YCN.js} +4 -4
  160. package/dist/tools-DAF3DI3C.js +27 -0
  161. package/dist/{trace-ODOQIVIW.js → trace-FYVW2MQA.js} +149 -7
  162. package/dist/tui-primitives-2AKXQNZK.js +13 -0
  163. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
  164. package/dist/{upgrade-XANW3FXB.js → upgrade-3XPP6OQL.js} +26 -24
  165. package/dist/{usage-4H7ZRXQT.js → usage-3NLHGTU2.js} +57 -49
  166. package/dist/{verifiers-UZXNBZEB.js → verifiers-SSQONKRT.js} +171 -12
  167. package/dist/{verify-BVKWTNDL.js → verify-3U6J7FZI.js} +9 -9
  168. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
  169. package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-CEHYGPGQ.js} +67 -59
  170. package/dist/with-panes-MKB46MPQ.js +782 -0
  171. package/dist/worker/entry.js +101 -66
  172. package/docs/README.md +3 -2
  173. package/docs/acp.md +24 -3
  174. package/docs/alcf-provider.md +1 -1
  175. package/docs/architecture.md +2 -2
  176. package/docs/artifact-versions.md +4 -1
  177. package/docs/built-in-agents.md +1 -1
  178. package/docs/capacity-and-scheduling.md +43 -5
  179. package/docs/commands-and-modes.md +31 -2
  180. package/docs/configuration-and-targets.md +40 -10
  181. package/docs/context-engine.md +1 -1
  182. package/docs/development-pipeline.md +19 -0
  183. package/docs/dispatch-typed-intent.md +385 -0
  184. package/docs/documentation-coverage.md +2 -2
  185. package/docs/documentation-guide.md +1 -1
  186. package/docs/environment-variables.md +1 -0
  187. package/docs/eval-runner.md +1 -1
  188. package/docs/evals-internal.md +1 -1
  189. package/docs/evidence-and-memory.md +2 -2
  190. package/docs/evolution.md +1 -1
  191. package/docs/exit-codes-and-output.md +1 -1
  192. package/docs/extensions-and-sharing.md +25 -1
  193. package/docs/fleet-dispatch.md +22 -1
  194. package/docs/installation-and-lifecycle.md +15 -2
  195. package/docs/middleware-and-components.md +1 -1
  196. package/docs/model-catalog.md +10 -1
  197. package/docs/observability.md +3 -3
  198. package/docs/proactive-memory.md +1 -1
  199. package/docs/prompt-envelope-and-tools.md +1 -1
  200. package/docs/provider-adapter-cookbook.md +1 -1
  201. package/docs/safety-model.md +2 -2
  202. package/docs/scientific-validation.md +3 -3
  203. package/docs/session-lifecycle.md +1 -1
  204. package/docs/skills-marketplace.md +1 -1
  205. package/docs/tool-usage.md +18 -8
  206. package/docs/trace-store.md +1 -1
  207. package/docs/troubleshooting.md +1 -1
  208. package/docs/tui-design.md +1 -1
  209. package/docs/worker-dispatch-mechanics.md +1 -1
  210. package/package.json +4 -2
  211. package/src/cli/acp.ts +6 -2
  212. package/src/cli/argv.ts +25 -0
  213. package/src/cli/configure.ts +23 -21
  214. package/src/cli/doctor-panes.ts +124 -0
  215. package/src/cli/doctor-toolchain.ts +57 -0
  216. package/src/cli/doctor.ts +20 -1
  217. package/src/cli/eval-inventory.ts +436 -0
  218. package/src/cli/eval.ts +13 -0
  219. package/src/cli/evidence-detail.ts +88 -0
  220. package/src/cli/evidence-inventory.ts +183 -0
  221. package/src/cli/evidence.ts +30 -5
  222. package/src/cli/fleet-decisions.ts +69 -0
  223. package/src/cli/fleet-inspect.ts +334 -0
  224. package/src/cli/fleet-verify.ts +133 -0
  225. package/src/cli/fleet-view.ts +810 -0
  226. package/src/cli/fleet.ts +148 -37
  227. package/src/cli/index.ts +14 -2
  228. package/src/cli/interop-inspect.ts +128 -0
  229. package/src/cli/interop.ts +34 -0
  230. package/src/cli/panes.ts +35 -0
  231. package/src/cli/reset.ts +5 -2
  232. package/src/cli/run.ts +58 -0
  233. package/src/cli/skills-inventory.ts +185 -0
  234. package/src/cli/skills.ts +16 -13
  235. package/src/cli/tools.ts +321 -0
  236. package/src/cli/trace-inspect.ts +252 -0
  237. package/src/cli/trace.ts +23 -2
  238. package/src/cli/verifiers-inspect.ts +347 -0
  239. package/src/cli/verifiers.ts +9 -0
  240. package/src/core/bus-events.ts +4 -0
  241. package/src/core/config.ts +49 -0
  242. package/src/core/defaults.ts +93 -7
  243. package/src/core/response-schema.ts +44 -4
  244. package/src/core/theme-token-hex.ts +43 -0
  245. package/src/core/tool-names.ts +2 -1
  246. package/src/core/xdg.ts +1 -1
  247. package/src/domains/agents/fleets/build-review.md +0 -3
  248. package/src/domains/agents/fleets/build-test.md +0 -3
  249. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  250. package/src/domains/agents/result-contract.ts +164 -35
  251. package/src/domains/config/classify.ts +3 -0
  252. package/src/domains/dispatch/admission-error.ts +9 -0
  253. package/src/domains/dispatch/admission.ts +22 -21
  254. package/src/domains/dispatch/capacity-lease.ts +21 -1
  255. package/src/domains/dispatch/council-topology.ts +398 -0
  256. package/src/domains/dispatch/extension.ts +222 -50
  257. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  258. package/src/domains/dispatch/fleet-plan.ts +7 -2
  259. package/src/domains/dispatch/fleet-run.ts +64 -1
  260. package/src/domains/dispatch/gate-decisions.ts +11 -1
  261. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  262. package/src/domains/dispatch/gate-topology.ts +289 -0
  263. package/src/domains/dispatch/index.ts +19 -0
  264. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  265. package/src/domains/dispatch/intent.ts +85 -1
  266. package/src/domains/dispatch/reservation-store.ts +24 -4
  267. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  268. package/src/domains/dispatch/run-event-journal.ts +598 -0
  269. package/src/domains/dispatch/state.ts +45 -1
  270. package/src/domains/dispatch/types.ts +13 -0
  271. package/src/domains/dispatch/validation.ts +33 -8
  272. package/src/domains/eval/compare/compare.ts +3 -16
  273. package/src/domains/eval/inventory.ts +113 -0
  274. package/src/domains/eval/schema/serving.ts +31 -0
  275. package/src/domains/evidence/store.ts +6 -0
  276. package/src/domains/memory/index.ts +0 -1
  277. package/src/domains/memory/task-memory-policy.ts +6 -10
  278. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  279. package/src/domains/middleware/memory-intervention.ts +33 -21
  280. package/src/domains/mux/contract.ts +434 -0
  281. package/src/domains/mux/detect.ts +158 -0
  282. package/src/domains/mux/extension.ts +47 -0
  283. package/src/domains/mux/index.ts +96 -0
  284. package/src/domains/mux/manifest.ts +6 -0
  285. package/src/domains/mux/operations.ts +164 -0
  286. package/src/domains/mux/pane-registry.ts +90 -0
  287. package/src/domains/mux/protocol.ts +49 -0
  288. package/src/domains/mux/socket-client.ts +816 -0
  289. package/src/domains/mux/types.ts +222 -0
  290. package/src/domains/mux/viewer-command.ts +59 -0
  291. package/src/domains/mux/yazi/assets/init.lua +2 -0
  292. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  293. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  294. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  295. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  296. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  297. package/src/domains/mux/yazi/event-stream.ts +180 -0
  298. package/src/domains/mux/yazi/profile.ts +299 -0
  299. package/src/domains/mux/yazi/session.ts +228 -0
  300. package/src/domains/mux/yazi/theme.ts +30 -0
  301. package/src/domains/observability/cost.ts +21 -0
  302. package/src/domains/observability/index.ts +2 -0
  303. package/src/domains/observability/trace-store.ts +42 -0
  304. package/src/domains/providers/endpoint-capacity.ts +154 -22
  305. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  306. package/src/domains/providers/extension.ts +20 -3
  307. package/src/domains/providers/index.ts +22 -0
  308. package/src/domains/providers/model-runtime-capabilities.ts +32 -0
  309. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  310. package/src/domains/providers/runtimes/builtins.ts +2 -0
  311. package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
  312. package/src/domains/providers/support.ts +1 -0
  313. package/src/domains/providers/target-model-cache.ts +124 -0
  314. package/src/domains/resources/index.ts +3 -0
  315. package/src/domains/resources/skills/loader.ts +33 -0
  316. package/src/domains/safety/action-classifier.ts +6 -0
  317. package/src/domains/toolchain/archive.ts +175 -0
  318. package/src/domains/toolchain/contract.ts +28 -0
  319. package/src/domains/toolchain/extension.ts +47 -0
  320. package/src/domains/toolchain/index.ts +39 -0
  321. package/src/domains/toolchain/install.ts +327 -0
  322. package/src/domains/toolchain/manifest.ts +8 -0
  323. package/src/domains/toolchain/paths.ts +34 -0
  324. package/src/domains/toolchain/registry.ts +265 -0
  325. package/src/domains/toolchain/remove.ts +218 -0
  326. package/src/domains/toolchain/resolve.ts +182 -0
  327. package/src/domains/toolchain/types.ts +113 -0
  328. package/src/domains/toolchain/version.ts +88 -0
  329. package/src/engine/acp/server.ts +413 -70
  330. package/src/engine/acp/types.ts +19 -1
  331. package/src/engine/claude/sdk-module.ts +98 -0
  332. package/src/engine/claude/sdk-runtime.ts +19 -11
  333. package/src/engine/tui-primitives.ts +21 -0
  334. package/src/engine/tui.ts +1 -0
  335. package/src/engine/worker-runtime.ts +2 -13
  336. package/src/entry/boot-options.ts +2 -0
  337. package/src/entry/orchestrator.ts +103 -5
  338. package/src/entry/panes-activation.ts +31 -0
  339. package/src/entry/with-panes.ts +20 -0
  340. package/src/interactive/context-overlay.ts +1 -0
  341. package/src/interactive/cost-overlay.ts +2 -0
  342. package/src/interactive/council-grid.ts +1 -3
  343. package/src/interactive/council.ts +11 -0
  344. package/src/interactive/dispatch-board.ts +26 -16
  345. package/src/interactive/interactive-application.ts +153 -6
  346. package/src/interactive/interactive-input-runtime.ts +7 -0
  347. package/src/interactive/interactive-slash-runtime.ts +36 -6
  348. package/src/interactive/memory-overlay.ts +1 -0
  349. package/src/interactive/modal-marker.ts +170 -0
  350. package/src/interactive/mux-bridge.ts +214 -0
  351. package/src/interactive/overlay-frame.ts +58 -2
  352. package/src/interactive/overlay-general-openers.ts +1 -0
  353. package/src/interactive/overlay-key-routing.ts +14 -3
  354. package/src/interactive/overlay-lifecycle.ts +19 -3
  355. package/src/interactive/overlay-model-selectors.ts +40 -3
  356. package/src/interactive/overlay-permission-lifecycle.ts +90 -22
  357. package/src/interactive/overlay-transitions.ts +18 -4
  358. package/src/interactive/overlays/agents.ts +1 -0
  359. package/src/interactive/overlays/ask-user.ts +136 -30
  360. package/src/interactive/overlays/auth-dialog.ts +1 -0
  361. package/src/interactive/overlays/context-reset.ts +1 -0
  362. package/src/interactive/overlays/cwd-fallback.ts +1 -0
  363. package/src/interactive/overlays/decisions.ts +11 -11
  364. package/src/interactive/overlays/extensions.ts +1 -0
  365. package/src/interactive/overlays/fleet-run-approval.ts +1 -0
  366. package/src/interactive/overlays/handoff-review.ts +1 -0
  367. package/src/interactive/overlays/help-reference.ts +2 -0
  368. package/src/interactive/overlays/interop.ts +1 -0
  369. package/src/interactive/overlays/library-install-confirm.ts +1 -0
  370. package/src/interactive/overlays/library-tabs.ts +28 -0
  371. package/src/interactive/overlays/list-overlay.ts +10 -1
  372. package/src/interactive/overlays/message-picker.ts +1 -0
  373. package/src/interactive/overlays/model-scope.ts +86 -0
  374. package/src/interactive/overlays/model-selector.ts +1 -0
  375. package/src/interactive/overlays/prompts.ts +1 -0
  376. package/src/interactive/overlays/session-selector.ts +1 -0
  377. package/src/interactive/overlays/settings-sections.ts +30 -0
  378. package/src/interactive/overlays/settings.ts +579 -44
  379. package/src/interactive/overlays/side-question.ts +1 -0
  380. package/src/interactive/overlays/skills-hub.ts +3 -11
  381. package/src/interactive/overlays/tree-selector.ts +1 -0
  382. package/src/interactive/pane-policy.ts +46 -0
  383. package/src/interactive/panes-runtime.ts +292 -0
  384. package/src/interactive/renderers/compaction-summary.ts +29 -0
  385. package/src/interactive/renderers/worker-entry.ts +122 -14
  386. package/src/interactive/slash-commands.ts +251 -15
  387. package/src/interactive/tasks-overlay.ts +1 -0
  388. package/src/interactive/theme/tokens.ts +3 -14
  389. package/src/interactive/turn-context.ts +47 -2
  390. package/src/interactive/view/artifacts.ts +109 -1
  391. package/src/interactive/view/view-overlay.ts +1 -0
  392. package/src/interactive/watch-pane.ts +152 -0
  393. package/src/interactive/worker-progress.ts +7 -1
  394. package/src/interactive/worker-receipts.ts +19 -1
  395. package/src/interactive/worker-stream.ts +5 -0
  396. package/src/interactive/yazi-bridge.ts +444 -0
  397. package/src/tools/bootstrap.ts +26 -2
  398. package/src/tools/builtin-tool-catalog.ts +15 -0
  399. package/src/tools/compete-worktrees.ts +83 -2
  400. package/src/tools/core-bootstrap.ts +2 -1
  401. package/src/tools/dispatch-admission.ts +14 -3
  402. package/src/tools/dispatch-arguments.ts +20 -20
  403. package/src/tools/dispatch-run-events.ts +134 -19
  404. package/src/tools/dispatch-runner.ts +29 -7
  405. package/src/tools/dispatch-types.ts +15 -3
  406. package/src/tools/dispatch.ts +1 -1
  407. package/src/tools/executables.ts +17 -14
  408. package/src/tools/observation.ts +54 -4
  409. package/src/tools/panes-surface.ts +38 -0
  410. package/src/tools/panes.ts +112 -0
  411. package/src/tools/policy.ts +10 -1
  412. package/src/tools/presentation.ts +1 -0
  413. package/dist/chunk-D73KXYPF.js +0 -37
  414. package/dist/chunk-MLOK6ZOS.js +0 -2888
  415. package/dist/chunk-YKOFT37S.js +0 -279
  416. package/dist/doctor-TWBWFK5V.js +0 -165
@@ -0,0 +1,214 @@
1
+ /**
2
+ * The dispatch-to-pane-host bridge.
3
+ *
4
+ * Two responsibilities, both projections of state the session already owns:
5
+ *
6
+ * - **Terminal-run notifications.** A finished dispatch raises a pane-host
7
+ * toast per the `panes.notifications` policy, and a failure additionally
8
+ * leaves a persistent transcript notice, which unlike a toast remains
9
+ * auditable in the TUI and does not depend on the pane host being alive.
10
+ *
11
+ * - **Self-report (SA-3).** Clio's own hosting pane, in guest mode, carries
12
+ * the interactive status machine's phase as a semantic agent state, on a
13
+ * trailing-edge throttle: herdr answers one request per connection, and an
14
+ * operator turn passes through several phases inside one window, of which
15
+ * only the last is worth a wire call.
16
+ *
17
+ * What this bridge deliberately no longer does is open panes. The phase 3/4
18
+ * integration projected every dispatch onto a viewer pane in a hidden Fleet
19
+ * tab (an `agents` policy knob, a keep-failed close policy, resume adoption,
20
+ * role-vocabulary sidebar labels). All of it duplicated the native fleet
21
+ * surfaces into panes the operator had not asked for; the one pane that
22
+ * renders runs is now the operator-pulled watch pane (`watch-pane.ts`), and
23
+ * the policy line lives in `pane-policy.ts`.
24
+ */
25
+
26
+ import { type AgentStatusChangedPayload, BusChannels, type DispatchRunIdentity } from "../core/bus-events.js";
27
+ import type { SafeEventBus } from "../core/event-bus.js";
28
+ import type { MuxContract, MuxLog } from "../domains/mux/index.js";
29
+ import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
30
+
31
+ /** Trailing-edge window for the self-report; matches `WAIT_POLL_MS`. */
32
+ export const MUX_BRIDGE_THROTTLE_MS = 250;
33
+
34
+ /**
35
+ * A success worth a sound. Below it a finished run is one of many and a chime
36
+ * per run is noise; above it the operator has moved on and wants telling.
37
+ */
38
+ const LONG_RUN_MS = 60_000;
39
+
40
+ /** How much terminal detail may reach a toast body or a notice line. */
41
+ const DETAIL_MAX_CHARS = 96;
42
+
43
+ export type PanesNotificationsPolicy = "failures" | "all" | "off";
44
+
45
+ /** Terminal outcomes that read as failure for the notification policy. */
46
+ type BridgeRunOutcome = "succeeded" | "failed" | "canceled" | "timed_out";
47
+
48
+ export interface MuxBridgeDeps {
49
+ bus: SafeEventBus;
50
+ mux: MuxContract;
51
+ /** Read live so a `/settings` change takes effect on the next terminal run. */
52
+ notificationsPolicy: () => PanesNotificationsPolicy;
53
+ /** Persistent transcript notice; unlike a herdr toast, this remains auditable in the TUI. */
54
+ notice?: (level: "error", text: string) => void;
55
+ log?: MuxLog;
56
+ now?: () => number;
57
+ throttleMs?: number;
58
+ setTimeoutFn?: typeof setTimeout;
59
+ clearTimeoutFn?: typeof clearTimeout;
60
+ }
61
+
62
+ export interface MuxBridge {
63
+ /** Settle every in-flight notification and self-report. Tests await it. */
64
+ flush(): Promise<void>;
65
+ dispose(): void;
66
+ }
67
+
68
+ function isFailure(outcome: BridgeRunOutcome): boolean {
69
+ return outcome === "failed" || outcome === "timed_out";
70
+ }
71
+
72
+ /** Clio's own pane state (SA-3), mapped from the interactive status machine. */
73
+ function selfStateFor(phase: AgentStatusChangedPayload["phase"]): "working" | "blocked" | "idle" {
74
+ if (phase === "tool_blocked") return "blocked";
75
+ // `ended` is the settle step on the way back to idle, so both read idle.
76
+ return phase === "idle" || phase === "ended" ? "idle" : "working";
77
+ }
78
+
79
+ /**
80
+ * Clio's seven-member run outcome taxonomy narrowed to the four states the
81
+ * notification policy distinguishes. `stalled`, `denied_by_policy`,
82
+ * `spawn_failed`, and the synthetic `retry_denied` all read as failure, which
83
+ * is the safe direction: an unrecognized terminal state must not silently
84
+ * skip the failure notice.
85
+ */
86
+ function terminalOutcome(outcome: string): BridgeRunOutcome {
87
+ if (outcome === "succeeded") return "succeeded";
88
+ if (outcome === "canceled") return "canceled";
89
+ if (outcome === "timed_out") return "timed_out";
90
+ return "failed";
91
+ }
92
+
93
+ export function createMuxBridge(deps: MuxBridgeDeps): MuxBridge {
94
+ const log = deps.log ?? ((): void => undefined);
95
+ const now = deps.now ?? Date.now;
96
+ const throttleMs = deps.throttleMs ?? MUX_BRIDGE_THROTTLE_MS;
97
+ const setTimeoutFn = deps.setTimeoutFn ?? setTimeout;
98
+ const clearTimeoutFn = deps.clearTimeoutFn ?? clearTimeout;
99
+
100
+ /** Start stamps, so a terminal event without a duration still gets one. */
101
+ const startedAt = new Map<string, number>();
102
+ let disposed = false;
103
+ let timer: ReturnType<typeof setTimeout> | null = null;
104
+ /** Serializes wire work: notifies and self-reports must not interleave. */
105
+ let draining: Promise<void> = Promise.resolve();
106
+ /** Pending self-report state, or null when nothing changed since the last flush. */
107
+ let pendingSelf: "working" | "blocked" | "idle" | null = null;
108
+ /** The last state actually reported, so a repeated phase costs no wire call. */
109
+ let reportedSelf: "working" | "blocked" | "idle" | null = null;
110
+
111
+ const schedule = (): void => {
112
+ if (disposed || timer !== null) return;
113
+ timer = setTimeoutFn(() => {
114
+ timer = null;
115
+ void drainSelf();
116
+ }, throttleMs);
117
+ timer.unref?.();
118
+ };
119
+
120
+ const drainSelf = (): Promise<void> => {
121
+ draining = draining.then(async () => {
122
+ if (disposed) return;
123
+ const self = pendingSelf;
124
+ pendingSelf = null;
125
+ if (self !== null && self !== reportedSelf) {
126
+ reportedSelf = self;
127
+ await deps.mux.reportSelf({ state: self }).catch(() => false);
128
+ }
129
+ });
130
+ return draining;
131
+ };
132
+
133
+ const boundedDetail = (value: string | null | undefined): string | null => {
134
+ if (typeof value !== "string" || value.length === 0) return null;
135
+ return sanitizeCallTargetText(value).slice(0, DETAIL_MAX_CHARS);
136
+ };
137
+
138
+ const onTerminal = (
139
+ identity: DispatchRunIdentity,
140
+ rawOutcome: string,
141
+ fields: { durationMs?: number | undefined; detail?: string | null | undefined },
142
+ ): void => {
143
+ const outcome = terminalOutcome(rawOutcome);
144
+ const begun = startedAt.get(identity.runId);
145
+ startedAt.delete(identity.runId);
146
+ const policy = deps.notificationsPolicy();
147
+ const failed = isFailure(outcome);
148
+ const qualifies = policy === "all" || (policy === "failures" && failed);
149
+ if (!qualifies) return;
150
+ const detail = boundedDetail(fields.detail);
151
+ const durationMs =
152
+ typeof fields.durationMs === "number" ? fields.durationMs : begun === undefined ? null : now() - begun;
153
+ if (failed) {
154
+ deps.notice?.("error", `${identity.agentId} ${outcome} (${identity.runId})${detail ? `: ${detail}` : ""}`);
155
+ }
156
+ if (!deps.mux.available()) return;
157
+ draining = draining.then(async () => {
158
+ if (disposed) return;
159
+ try {
160
+ await deps.mux.notify({
161
+ title: `${identity.agentId} ${outcome}`,
162
+ ...(detail ? { body: detail } : {}),
163
+ // A failure interrupts; a long success announces. A short success
164
+ // in `all` mode gets the toast without the chime.
165
+ sound: failed ? "request" : durationMs !== null && durationMs >= LONG_RUN_MS ? "done" : "none",
166
+ });
167
+ } catch (error) {
168
+ // The contract swallows its own failures; anything arriving here is a
169
+ // bug rather than a dead socket, and must still not kill the chain.
170
+ log("debug", `mux bridge notify failed: ${error instanceof Error ? error.message : String(error)}`);
171
+ }
172
+ });
173
+ };
174
+
175
+ const unsubscribers = [
176
+ deps.bus.on(BusChannels.DispatchStarted, (payload) => {
177
+ startedAt.set(payload.runId, now());
178
+ }),
179
+ deps.bus.on(BusChannels.DispatchCompleted, (payload) => {
180
+ onTerminal(payload, payload.outcome, { durationMs: payload.durationMs, detail: payload.outcomeDetail });
181
+ }),
182
+ deps.bus.on(BusChannels.DispatchFailed, (payload) => {
183
+ onTerminal(payload, payload.reason, { durationMs: payload.durationMs, detail: payload.outcomeDetail });
184
+ }),
185
+ // SA-3. The interactive status machine is the one surface that already
186
+ // knows when a turn starts, when it ends, and when a tool call is parked
187
+ // on an approval, so the self-report rides its transitions rather than
188
+ // growing a second turn-lifecycle listener.
189
+ deps.bus.on(BusChannels.AgentStatusChanged, (payload) => {
190
+ const state = selfStateFor(payload.phase);
191
+ if (state === reportedSelf && pendingSelf === null) return;
192
+ pendingSelf = state;
193
+ schedule();
194
+ }),
195
+ ];
196
+
197
+ return {
198
+ flush(): Promise<void> {
199
+ if (timer !== null) {
200
+ clearTimeoutFn(timer);
201
+ timer = null;
202
+ }
203
+ return drainSelf();
204
+ },
205
+ dispose(): void {
206
+ if (disposed) return;
207
+ disposed = true;
208
+ if (timer !== null) clearTimeoutFn(timer);
209
+ timer = null;
210
+ for (const unsubscribe of unsubscribers) unsubscribe();
211
+ startedAt.clear();
212
+ },
213
+ };
214
+ }
@@ -4,12 +4,14 @@ import {
4
4
  type Component,
5
5
  type OverlayHandle,
6
6
  type OverlayOptions,
7
+ type OverlayUnfocusOptions,
7
8
  type SelectListTheme,
8
9
  type SettingsListTheme,
9
10
  type TUI,
10
11
  truncateToWidth,
11
12
  visibleWidth,
12
13
  } from "../engine/tui.js";
14
+ import { enterModal, type ModalMarkerSink } from "./modal-marker.js";
13
15
  import { type ClioToken, clioTheme, padAnsi, screenTitle, selectListTheme, settingsListTheme } from "./theme/index.js";
14
16
 
15
17
  export const IDENTITY = (text: string): string => text;
@@ -464,6 +466,15 @@ export class ClioOverlayFrame implements Component {
464
466
  * screen at 40x12. It learns the terminal height through the `visible`
465
467
  * predicate, which the engine evaluates with the live dimensions in the same
466
468
  * pass that renders the overlay.
469
+ *
470
+ * This is also where the modal marker is claimed and released. Every framed
471
+ * overlay is modal and every modal is framed: the only two overlays Clio shows
472
+ * without a frame are the context and task islands, and both pass
473
+ * `nonCapturing: true` straight to the engine. Hanging the marker here rather
474
+ * than on the overlay lifecycle therefore covers the nested frames the
475
+ * lifecycle's single `OverlayState` cannot see, and it cannot drift out of
476
+ * sync with what actually owns the keyboard, because the same call that takes
477
+ * the screen takes the marker.
467
478
  */
468
479
  export function showClioOverlayFrame(
469
480
  tui: TUI,
@@ -473,12 +484,21 @@ export function showClioOverlayFrame(
473
484
  footerHint?: string | ((innerWidth: number) => string | undefined);
474
485
  /** Border and title token; omitted leaves the informational frame. */
475
486
  tone?: OverlayTone;
487
+ /**
488
+ * Stable id for the modal marker, published on the terminal title while
489
+ * this overlay owns the keys. Required, and deliberately not defaulted
490
+ * from `title`: several titles carry a session id, a decision
491
+ * classification, or the active tab, and an id that moves with the
492
+ * content is not an identifier. Use `modalMarkerId(...)` where the title
493
+ * really is fixed and surface-specific.
494
+ */
495
+ markerId: string;
476
496
  },
477
497
  ): OverlayHandle {
478
- const { title, footerHint, tone, width, visible, maxHeight, margin, ...overlayOptions } = options;
498
+ const { title, footerHint, tone, width, visible, maxHeight, margin, markerId, ...overlayOptions } = options;
479
499
  const boxWidth = typeof width === "number" ? width : 0;
480
500
  const frame = new ClioOverlayFrame(child, title, footerHint, boxWidth, frameAlignForAnchor(options.anchor), tone);
481
- return tui.showOverlay(frame, {
501
+ const handle = tui.showOverlay(frame, {
482
502
  ...overlayOptions,
483
503
  ...(margin !== undefined ? { margin } : {}),
484
504
  width: "100%",
@@ -493,6 +513,42 @@ export function showClioOverlayFrame(
493
513
  return shown;
494
514
  },
495
515
  });
516
+ const marker = enterModal(markerId, modalMarkerSink(tui));
517
+ return {
518
+ hide(): void {
519
+ marker.release();
520
+ handle.hide();
521
+ },
522
+ setHidden(hidden: boolean): void {
523
+ marker.setActive(!hidden);
524
+ handle.setHidden(hidden);
525
+ },
526
+ isHidden: (): boolean => handle.isHidden(),
527
+ focus(): void {
528
+ marker.raise();
529
+ handle.focus();
530
+ },
531
+ // A pure passthrough on purpose. The engine hands focus to the next
532
+ // visible capturing overlay, which is another modal, so the stack is
533
+ // unchanged; the surface that gives the keyboard back to the composer
534
+ // does it by hiding, which `setHidden` already reports.
535
+ unfocus: (unfocusOptions?: OverlayUnfocusOptions): void =>
536
+ unfocusOptions ? handle.unfocus(unfocusOptions) : handle.unfocus(),
537
+ isFocused: (): boolean => handle.isFocused(),
538
+ };
539
+ }
540
+
541
+ /**
542
+ * The title sink behind a TUI, or null when there is none.
543
+ *
544
+ * The engine's `Terminal` declares `setTitle`, but the stub TUIs the overlay
545
+ * tests mount against are structural casts that carry only what the overlay
546
+ * under test reads. Probing for the method keeps the marker inert on those
547
+ * rather than throwing inside a render path.
548
+ */
549
+ function modalMarkerSink(tui: TUI): ModalMarkerSink | null {
550
+ const terminal: Partial<ModalMarkerSink> | undefined = tui.terminal;
551
+ return typeof terminal?.setTitle === "function" ? (terminal as ModalMarkerSink) : null;
496
552
  }
497
553
 
498
554
  /** Rows from an overlay size value, matching how the engine reads one. */
@@ -334,6 +334,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
334
334
  deps.transitions.state = "dispatch-board";
335
335
  deps.dispatchBoard.resetSelection();
336
336
  deps.transitions.handle = showOverlayFrameFactory(deps.tui, deps.dispatchBoard, {
337
+ markerId: "dispatch-board",
337
338
  title: "Fleet Runs",
338
339
  footerHint: dispatchBoardHint,
339
340
  anchor: "center",
@@ -15,6 +15,7 @@ export type OverlayState =
15
15
  | "memory"
16
16
  | "view"
17
17
  | "model"
18
+ | "model-scope"
18
19
  | "settings"
19
20
  | "resume"
20
21
  | "tree"
@@ -63,6 +64,12 @@ export interface DispatchBoardOverlayKeyDeps {
63
64
  cancelSelectedDispatch: () => void;
64
65
  /** Open or close the selected run's worker-progress detail. */
65
66
  toggleSelectedDispatchDetail: () => void;
67
+ /**
68
+ * Open or retarget the watch pane for the selected run. True when the pane
69
+ * layer took the key; false hands Enter back to the inline detail toggle,
70
+ * which is the whole behavior when panes are off or the run is terminal.
71
+ */
72
+ watchSelectedDispatch: () => boolean;
66
73
  }
67
74
 
68
75
  interface CloseOverlayKeyDeps {
@@ -173,10 +180,11 @@ export function routeDispatchBoardOverlayKey(data: string, deps: DispatchBoardOv
173
180
  deps.cancelSelectedDispatch();
174
181
  return true;
175
182
  }
176
- // Worker progress is expanded detail the operator asks for. The default list
177
- // stays compact so a fan-out of scouts costs one card each.
183
+ // Enter on a live run enters it: the watch pane opens (or retargets) and
184
+ // renders the run's stream beside the board. Where panes are off, absent,
185
+ // or the run is terminal, Enter keeps its inline worker-progress detail.
178
186
  if (matchesKey(data, "enter")) {
179
- deps.toggleSelectedDispatchDetail();
187
+ if (!deps.watchSelectedDispatch()) deps.toggleSelectedDispatchDetail();
180
188
  return true;
181
189
  }
182
190
  return false;
@@ -280,6 +288,9 @@ export function routeOverlayKey(
280
288
  if (overlayState === "memory") return false;
281
289
  if (overlayState === "view") return false;
282
290
  if (overlayState === "model") return routeModelOverlayKey(data, deps);
291
+ // The scope dialog owns Esc itself: cancelling it must leave the model where
292
+ // it was, and a router-level close cannot say that to the caller waiting on it.
293
+ if (overlayState === "model-scope") return false;
283
294
  // Settings owns Esc itself: its stack is sections → rows → detail, and a
284
295
  // router-level close made every Esc leave the overlay from wherever the
285
296
  // operator was, so a cancelled picker and a finished visit looked the same.
@@ -22,6 +22,7 @@ import {
22
22
  export * from "./overlay-key-routing.js";
23
23
 
24
24
  import type { OverlayState } from "./overlay-key-routing.js";
25
+ import type { PendingModelScope } from "./overlays/model-scope.js";
25
26
  import type { SettingsCenterRowId, SettingsSectionId } from "./overlays/settings.js";
26
27
 
27
28
  // Runtime lifecycle construction lives beside the pure modal key router so the
@@ -48,6 +49,7 @@ export type OverlayLifecycleApplicationDeps = Pick<
48
49
  | "onNewSession"
49
50
  | "onResumeSession"
50
51
  | "onSelectModel"
52
+ | "onSetThinkingLevel"
51
53
  | "providers"
52
54
  | "readSessionEntries"
53
55
  | "registerAskUserHandler"
@@ -108,6 +110,7 @@ export interface OverlayLifecycleRuntimeDeps {
108
110
  openAuthDialog?: typeof import("./overlays/auth-dialog.js").openAuthDialog;
109
111
  openAskUserOverlay?: typeof import("./overlays/ask-user.js").openAskUserOverlay;
110
112
  openModelOverlay?: typeof import("./overlays/model-selector.js").openModelOverlay;
113
+ openModelScopeOverlay?: typeof import("./overlays/model-scope.js").openModelScopeOverlay;
111
114
  openSettingsOverlay?: typeof import("./overlays/settings.js").openSettingsOverlay;
112
115
  openSessionOverlay?: typeof import("./overlays/session-selector.js").openSessionOverlay;
113
116
  openTreeOverlay?: typeof import("./overlays/tree-selector.js").openTreeOverlay;
@@ -151,6 +154,8 @@ export interface OverlayLifecycleController {
151
154
  startHandoffState(goal: string): void;
152
155
  startFleetRunState(name: string, vars: Readonly<Record<string, string>>): void;
153
156
  openModelOverlayState(): void;
157
+ /** `/model <pattern>` and the picker both land here: choose session or global before anything applies. */
158
+ openModelScopeState(ref: PendingModelScope): void;
154
159
  openSettingsOverlayState(section?: SettingsSectionId, rowId?: SettingsCenterRowId): void;
155
160
  openResumeOverlayState(): void;
156
161
  openTreeOverlayState(): void;
@@ -196,6 +201,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
196
201
  openAuthDialog: openAuthDialogFactory,
197
202
  openAskUserOverlay: openAskUserOverlayFactory,
198
203
  openModelOverlay: openModelOverlayFactory,
204
+ openModelScopeOverlay: openModelScopeOverlayFactory,
199
205
  openSettingsOverlay: openSettingsOverlayFactory,
200
206
  openSessionOverlay: openSessionOverlayFactory,
201
207
  openTreeOverlay: openTreeOverlayFactory,
@@ -269,13 +275,15 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
269
275
  getAutonomy: () => deps.app.getSettings?.().autonomy ?? "auto-edit",
270
276
  getOverlayState: () => overlayTransitions.state,
271
277
  openPermissionOverlay: (view, inspect) => {
272
- if (overlayTransitions.state !== "closed") return false;
273
- overlayTransitions.state = "permission-confirm";
278
+ if (overlayTransitions.state === "permission-confirm") return false;
274
279
  const body = createPermissionOverlayBody(view, inspect);
275
280
  permissionBody = body;
276
- overlayTransitions.handle = showOverlayFrame(tui, body, {
281
+ const handle = showOverlayFrame(tui, body, {
277
282
  ...permissionOverlayPlacement(tui, editor, footer.view),
278
283
  width: PERMISSION_OVERLAY_WIDTH,
284
+ // Not derived from the title: that one is classified per decision
285
+ // axis and is one of five strings for the same modal.
286
+ markerId: "permission-confirm",
279
287
  title: permissionOverlayTitle(view),
280
288
  tone: permissionOverlayTone(view),
281
289
  // Read per frame: the footer names what Enter does right now, and
@@ -283,6 +291,11 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
283
291
  // the mutation is open.
284
292
  footerHint: (innerWidth) => permissionOverlayHint(innerWidth, editor.getText().length > 0, inspectionHint()),
285
293
  });
294
+ if (!overlayTransitions.showPermission(handle)) {
295
+ handle.hide();
296
+ permissionBody = null;
297
+ return false;
298
+ }
286
299
  tui.requestRender();
287
300
  return true;
288
301
  },
@@ -331,10 +344,12 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
331
344
  ...(deps.app.writeSettings ? { writeSettings: deps.app.writeSettings } : {}),
332
345
  ...(deps.app.commitSetting ? { commitSetting: deps.app.commitSetting } : {}),
333
346
  ...(deps.app.onSelectModel ? { onSelectModel: deps.app.onSelectModel } : {}),
347
+ ...(deps.app.onSetThinkingLevel ? { onSetThinkingLevel: deps.app.onSetThinkingLevel } : {}),
334
348
  ...(deps.app.getFleetNodes ? { getFleetNodes: deps.app.getFleetNodes } : {}),
335
349
  connectTarget: (targetId) => overlayAuth.openConnectFlow(targetId),
336
350
  ...(deps.app.interop ? { getInteropProposals: interopProposalsFor(deps.app.interop) } : {}),
337
351
  ...(openModelOverlayFactory ? { openModelOverlay: openModelOverlayFactory } : {}),
352
+ ...(openModelScopeOverlayFactory ? { openModelScopeOverlay: openModelScopeOverlayFactory } : {}),
338
353
  ...(openSettingsOverlayFactory ? { openSettingsOverlay: openSettingsOverlayFactory } : {}),
339
354
  });
340
355
 
@@ -466,6 +481,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
466
481
  startFleetRunState,
467
482
  startHandoffState,
468
483
  openModelOverlayState: overlayModelSelectors.openModelOverlayState,
484
+ openModelScopeState: overlayModelSelectors.openModelScopeState,
469
485
  openSettingsOverlayState: overlayModelSelectors.openSettingsOverlayState,
470
486
  openResumeOverlayState,
471
487
  openTreeOverlayState,
@@ -1,4 +1,5 @@
1
1
  import type { ClioSettings } from "../core/config.js";
2
+ import type { ThinkingLevel } from "../core/defaults.js";
2
3
  import type { SafeEventBus } from "../core/event-bus.js";
3
4
  import type { InteropProposal } from "../domains/interop/index.js";
4
5
  import type { ProvidersContract } from "../domains/providers/index.js";
@@ -7,6 +8,12 @@ import type { TUI } from "../engine/tui.js";
7
8
  import type { FooterDashboardPanel } from "./footer/dashboard.js";
8
9
  import type { InteractiveNoticeLevel } from "./interactive-subscriptions.js";
9
10
  import type { OverlayTransitions } from "./overlay-transitions.js";
11
+ import {
12
+ formatPendingModelScope,
13
+ type ModelScopeChoice,
14
+ openModelScopeOverlay,
15
+ type PendingModelScope,
16
+ } from "./overlays/model-scope.js";
10
17
  import { openModelOverlay } from "./overlays/model-selector.js";
11
18
  import { openSettingsOverlay, type SettingsCenterRowId, type SettingsSectionId } from "./overlays/settings.js";
12
19
 
@@ -21,7 +28,9 @@ export interface OverlayModelSelectorsDeps {
21
28
  getSettings?: () => Readonly<ClioSettings>;
22
29
  writeSettings?: (next: ClioSettings) => void;
23
30
  commitSetting?: (id: string, next: ClioSettings, scope: "session" | "global") => void;
24
- onSelectModel?: (ref: { target: string; model: string }) => void;
31
+ onSelectModel?: (ref: { target: string; model: string }, scope: ModelScopeChoice) => void;
32
+ /** Applied with the chosen scope when the swap named a thinking level. */
33
+ onSetThinkingLevel?: (level: ThinkingLevel, scope?: ModelScopeChoice) => void;
25
34
  /** Settings → Fleet shows live node placement health when the scheduler exposes it. */
26
35
  getFleetNodes?: () => ReadonlyArray<FleetNodeSnapshot>;
27
36
  /** Settings → Targets "connect" runs the auth flow over the open settings overlay. */
@@ -29,17 +38,21 @@ export interface OverlayModelSelectorsDeps {
29
38
  /** Settings → Advanced offers detected agents on the delegation.agents row. */
30
39
  getInteropProposals?: () => ReadonlyArray<InteropProposal>;
31
40
  openModelOverlay?: typeof openModelOverlay;
41
+ openModelScopeOverlay?: typeof openModelScopeOverlay;
32
42
  openSettingsOverlay?: typeof openSettingsOverlay;
33
43
  }
34
44
 
35
45
  export interface OverlayModelSelectors {
36
46
  openModelOverlayState(): void;
47
+ /** Ask where a resolved swap lands, then apply it. Nothing changes until the operator answers. */
48
+ openModelScopeState(ref: PendingModelScope): void;
37
49
  openSettingsOverlayState(section?: SettingsSectionId, rowId?: SettingsCenterRowId): void;
38
50
  refreshSettingsOverlay(): void;
39
51
  }
40
52
 
41
53
  export function createOverlayModelSelectors(deps: OverlayModelSelectorsDeps): OverlayModelSelectors {
42
54
  const openModel = deps.openModelOverlay ?? openModelOverlay;
55
+ const openModelScope = deps.openModelScopeOverlay ?? openModelScopeOverlay;
43
56
  const openSettings = deps.openSettingsOverlay ?? openSettingsOverlay;
44
57
  let settingsOverlayRefresh: (() => void) | null = null;
45
58
 
@@ -53,9 +66,11 @@ export function createOverlayModelSelectors(deps: OverlayModelSelectorsDeps): Ov
53
66
  ...(deps.getSettings ? { getSettings: deps.getSettings } : {}),
54
67
  providers: deps.providers,
55
68
  bus: deps.bus,
69
+ // The picker resolves what to run; the scope dialog resolves where it
70
+ // lands. Closing this overlay first keeps one modal on screen at a time.
56
71
  onSelect: (ref) => {
57
- deps.onSelectModel?.(ref);
58
- deps.refreshFooter();
72
+ deps.closeOverlay();
73
+ openModelScopeState(ref);
59
74
  },
60
75
  onToggleFavorite: (ref, favorite) => {
61
76
  if (!deps.getSettings || !deps.writeSettings) return;
@@ -76,6 +91,27 @@ export function createOverlayModelSelectors(deps: OverlayModelSelectorsDeps): Ov
76
91
  deps.tui.requestRender();
77
92
  };
78
93
 
94
+ const openModelScopeState = (ref: PendingModelScope): void => {
95
+ if (deps.transitions.state !== "closed") return;
96
+ deps.transitions.state = "model-scope";
97
+ deps.transitions.handle = openModelScope(deps.tui, {
98
+ ref,
99
+ onChoose: (scope) => {
100
+ deps.closeOverlay();
101
+ deps.onSelectModel?.({ target: ref.target, model: ref.model }, scope);
102
+ if (ref.thinkingLevel) deps.onSetThinkingLevel?.(ref.thinkingLevel, scope);
103
+ const swap = formatPendingModelScope(ref);
104
+ deps.notify("success", scope === "global" ? `active and saved globally: ${swap}` : `active this session: ${swap}`);
105
+ deps.refreshFooter();
106
+ },
107
+ onCancel: () => {
108
+ deps.closeOverlay();
109
+ deps.notify("info", "model unchanged");
110
+ },
111
+ });
112
+ deps.tui.requestRender();
113
+ };
114
+
79
115
  const openSettingsOverlayState = (section?: SettingsSectionId, rowId?: SettingsCenterRowId): void => {
80
116
  if (deps.transitions.state !== "closed" || !deps.getSettings || !deps.writeSettings) return;
81
117
  deps.transitions.state = "settings";
@@ -121,6 +157,7 @@ export function createOverlayModelSelectors(deps: OverlayModelSelectorsDeps): Ov
121
157
 
122
158
  return {
123
159
  openModelOverlayState,
160
+ openModelScopeState,
124
161
  openSettingsOverlayState,
125
162
  refreshSettingsOverlay: () => settingsOverlayRefresh?.(),
126
163
  };