@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
@@ -36,6 +36,7 @@ interface WorkerEscalationEntry {
36
36
  axis: ApprovalRequestView["axis"];
37
37
  reason: string;
38
38
  target?: string;
39
+ fallback: "deny" | "fail";
39
40
  }
40
41
 
41
42
  export interface OverlayPermissionLifecycleDeps {
@@ -190,6 +191,7 @@ function workerEscalationEntry(payload: PermissionRequestedPayload, autonomy: st
190
191
  ...(typeof payload.target === "string" && payload.target.length > 0
191
192
  ? { target: sanitizeCallTargetText(payload.target).slice(0, 200) }
192
193
  : {}),
194
+ fallback: payload.fallback === "fail" ? "fail" : "deny",
193
195
  };
194
196
  }
195
197
 
@@ -207,42 +209,87 @@ function workerApprovalRequestView(entry: WorkerEscalationEntry): ApprovalReques
207
209
 
208
210
  export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycleDeps): OverlayPermissionLifecycle {
209
211
  let pendingPermission: { call: ClassifierCall; decision: SafetyDecision; meta?: PermissionRequiredMeta } | null = null;
210
- let pendingWorker: Pick<WorkerEscalationEntry, "agentId" | "requestId" | "runId"> | null = null;
212
+ let pendingWorker: WorkerEscalationEntry | null = null;
211
213
  const workerQueue: WorkerEscalationEntry[] = [];
212
214
  const announcedRequestIds = new Set<string>();
213
215
  /** Worker escalations already surfaced, so one request parks the operator once. */
214
216
  const announcedWorkerRequestIds = new Set<string>();
217
+ /** Expiry notices already printed, including a decision that lost the terminal-state race. */
218
+ const expiredWorkerRequestIds = new Set<string>();
215
219
  let confirmed = false;
216
220
  let stopping = false;
221
+ let withdrawingWorker = false;
217
222
 
218
- const openWorker = (entry: WorkerEscalationEntry): void => {
219
- if (deps.getOverlayState() !== "closed") return;
220
- if (!deps.openPermissionOverlay(workerApprovalRequestView(entry))) return;
221
- pendingWorker = { runId: entry.runId, requestId: entry.requestId, agentId: entry.agentId };
223
+ const openWorker = (entry: WorkerEscalationEntry): boolean => {
224
+ if (!deps.openPermissionOverlay(workerApprovalRequestView(entry))) return false;
225
+ pendingWorker = entry;
222
226
  pendingPermission = null;
223
227
  confirmed = false;
228
+ return true;
224
229
  };
225
230
  const maybeOpenWorker = (): void => {
226
- if (deps.getOverlayState() !== "closed") return;
227
- const next = workerQueue.shift();
228
- if (next) openWorker(next);
231
+ const next = workerQueue[0];
232
+ if (next && openWorker(next)) workerQueue.shift();
229
233
  };
230
234
  const retryPending = (): void => {
231
235
  maybeOpenWorker();
232
- if (deps.getOverlayState() === "closed" && deps.toolRegistry?.hasParkedCalls()) {
236
+ if (deps.getOverlayState() !== "permission-confirm" && deps.toolRegistry?.hasParkedCalls()) {
233
237
  deps.toolRegistry.renotifyHead();
234
238
  }
235
239
  };
240
+ const announceWorkerExpiry = (
241
+ entry: WorkerEscalationEntry,
242
+ fallback: "deny" | "fail",
243
+ cause: "timeout" | "terminal" | "late-answer",
244
+ ): void => {
245
+ if (!markPermissionRequestSurfaced(expiredWorkerRequestIds, entry.requestId)) return;
246
+ const why =
247
+ cause === "timeout"
248
+ ? "expired after its escalation timeout"
249
+ : cause === "terminal"
250
+ ? "expired when its run ended"
251
+ : "expired before the operator decision arrived";
252
+ deps.appendNotice(
253
+ "warn",
254
+ `Worker approval request ${entry.requestId} for ${entry.agentId} (${entry.runId}) ${why}; fallback ${fallback} applied.`,
255
+ );
256
+ };
257
+ const removeQueuedWorkers = (matches: (entry: WorkerEscalationEntry) => boolean): WorkerEscalationEntry[] => {
258
+ const removed: WorkerEscalationEntry[] = [];
259
+ for (let index = workerQueue.length - 1; index >= 0; index -= 1) {
260
+ const entry = workerQueue[index];
261
+ if (entry === undefined || !matches(entry)) continue;
262
+ removed.unshift(entry);
263
+ workerQueue.splice(index, 1);
264
+ }
265
+ return removed;
266
+ };
267
+ const withdrawWorkers = (
268
+ matches: (entry: WorkerEscalationEntry) => boolean,
269
+ cause: "timeout" | "terminal",
270
+ fallback?: "deny" | "fail",
271
+ ): void => {
272
+ const active = pendingWorker !== null && matches(pendingWorker) ? pendingWorker : null;
273
+ const expired = [...(active === null ? [] : [active]), ...removeQueuedWorkers(matches)];
274
+ if (expired.length === 0) return;
275
+ if (active !== null) {
276
+ pendingWorker = null;
277
+ confirmed = false;
278
+ withdrawingWorker = true;
279
+ deps.closeOverlay();
280
+ }
281
+ for (const entry of expired) announceWorkerExpiry(entry, fallback ?? entry.fallback, cause);
282
+ deps.requestRender();
283
+ };
236
284
 
237
285
  const unsubscribePermission =
238
286
  deps.toolRegistry?.onPermissionRequired((call, decision, meta) => {
239
287
  const autonomy = deps.getAutonomy();
240
288
  const view = mainApprovalRequestView(call, decision, meta, autonomy, deps.toolRegistry?.parkedCount());
241
- // The dialog states the tool, the target, the action class, the axis
242
- // that asked, and the keys that answer it. While it is on screen the
243
- // notice repeats all of that one line above the transcript, so it is
244
- // emitted only when no dialog could open (another overlay holds the
245
- // screen), which is the case where it is the operator's only signal.
289
+ // The dialog states the tool, target, action class, asking axis, and
290
+ // decision keys. Another modal is interrupted so this prompt can take
291
+ // focus. The notice remains for a second queued approval or a frame that
292
+ // could not mount, when it is the operator's only additional signal.
246
293
  const announceParked = (): void => {
247
294
  if (!markPermissionRequestSurfaced(announcedRequestIds, meta.requestId)) return;
248
295
  const notice = approvalParkedNotice(call.tool, decision, autonomy);
@@ -256,10 +303,6 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
256
303
  view,
257
304
  });
258
305
  }
259
- if (deps.getOverlayState() !== "closed") {
260
- announceParked();
261
- return;
262
- }
263
306
  if (!deps.openPermissionOverlay(view, mainMutationInspector(call, view))) {
264
307
  announceParked();
265
308
  return;
@@ -280,6 +323,18 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
280
323
  workerQueue.push(entry);
281
324
  maybeOpenWorker();
282
325
  });
326
+ const unsubscribeWorkerResolution = deps.bus.on(BusChannels.PermissionResolved, (payload) => {
327
+ if (typeof payload.requestId !== "string") return;
328
+ if (payload.decidedBy !== "timeout" && payload.status !== "expired") return;
329
+ const fallback = payload.fallback === "fail" ? "fail" : "deny";
330
+ withdrawWorkers((entry) => entry.requestId === payload.requestId, "timeout", fallback);
331
+ });
332
+ const withdrawTerminalRun = (payload: { runId?: unknown }): void => {
333
+ if (typeof payload.runId !== "string") return;
334
+ withdrawWorkers((entry) => entry.runId === payload.runId, "terminal");
335
+ };
336
+ const unsubscribeWorkerCompleted = deps.bus.on(BusChannels.DispatchCompleted, withdrawTerminalRun);
337
+ const unsubscribeWorkerFailed = deps.bus.on(BusChannels.DispatchFailed, withdrawTerminalRun);
283
338
 
284
339
  const unsubscribeAutonomy =
285
340
  deps.toolRegistry?.onAutonomyDenied((_call, decision, level) => {
@@ -289,6 +344,12 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
289
344
  }) ?? (() => {});
290
345
 
291
346
  const onPermissionOverlayClosed = (): void => {
347
+ if (withdrawingWorker) {
348
+ withdrawingWorker = false;
349
+ confirmed = false;
350
+ retryPending();
351
+ return;
352
+ }
292
353
  const permission = pendingPermission;
293
354
  const worker = pendingWorker;
294
355
  pendingPermission = null;
@@ -299,10 +360,14 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
299
360
  try {
300
361
  deps.dispatch.resolveWorkerPermission?.(worker.runId, worker.requestId, wasConfirmed ? "approve" : "deny");
301
362
  } catch (error) {
302
- deps.appendNotice(
303
- "warn",
304
- `Could not deliver permission decision to run ${worker.runId}: ${error instanceof Error ? error.message : String(error)}`,
305
- );
363
+ const message = error instanceof Error ? error.message : String(error);
364
+ if (
365
+ /run or assignment '.+' is not active|run '.+' is (?:aborting|terminating)|no longer accepts input/u.test(message)
366
+ ) {
367
+ announceWorkerExpiry(worker, worker.fallback, "late-answer");
368
+ } else {
369
+ deps.appendNotice("warn", `Could not deliver permission decision to run ${worker.runId}: ${message}`);
370
+ }
306
371
  }
307
372
  } else if (wasConfirmed && permission) {
308
373
  deps.bus.emit(BusChannels.PermissionResolved, {
@@ -384,6 +449,9 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
384
449
  dispose: () => {
385
450
  unsubscribePermission();
386
451
  unsubscribeWorker();
452
+ unsubscribeWorkerResolution();
453
+ unsubscribeWorkerCompleted();
454
+ unsubscribeWorkerFailed();
387
455
  unsubscribeAutonomy();
388
456
  },
389
457
  };
@@ -20,12 +20,15 @@ export interface OverlayTransitionsDeps {
20
20
  export interface OverlayTransitions {
21
21
  state: OverlayState;
22
22
  handle: OverlayHandle | null;
23
+ /** Raise a permission prompt while preserving the modal it interrupted. */
24
+ showPermission(handle: OverlayHandle): boolean;
23
25
  close(): void;
24
26
  }
25
27
 
26
28
  export function createOverlayTransitions(deps: OverlayTransitionsDeps): OverlayTransitions {
27
29
  let state: OverlayState = "closed";
28
30
  let handle: OverlayHandle | null = null;
31
+ let interrupted: { state: OverlayState; handle: OverlayHandle | null } | null = null;
29
32
 
30
33
  return {
31
34
  get state() {
@@ -44,6 +47,13 @@ export function createOverlayTransitions(deps: OverlayTransitionsDeps): OverlayT
44
47
  set handle(next) {
45
48
  handle = next;
46
49
  },
50
+ showPermission(nextHandle) {
51
+ if (state === "permission-confirm") return false;
52
+ interrupted = state === "closed" ? null : { state, handle };
53
+ state = "permission-confirm";
54
+ handle = nextHandle;
55
+ return true;
56
+ },
47
57
  close,
48
58
  };
49
59
 
@@ -55,12 +65,16 @@ export function createOverlayTransitions(deps: OverlayTransitionsDeps): OverlayT
55
65
  return;
56
66
  }
57
67
  const leaving = state;
58
- state = "closed";
59
- deps.stopDispatchBoardTicker();
60
- handle?.hide();
61
- handle = null;
68
+ const leavingHandle = handle;
69
+ const restoring = leaving === "permission-confirm" ? interrupted : null;
70
+ interrupted = null;
71
+ state = restoring?.state ?? "closed";
72
+ handle = restoring?.handle ?? null;
73
+ if (restoring === null) deps.stopDispatchBoardTicker();
74
+ leavingHandle?.hide();
62
75
  if (leaving === "permission-confirm") deps.onPermissionOverlayClosed();
63
76
  else deps.onOverlayClosed?.();
77
+ restoring?.handle?.focus();
64
78
  deps.renderContextIsland();
65
79
  deps.renderTaskIsland();
66
80
  deps.requestRender();
@@ -60,6 +60,7 @@ export function openAgentsOverlay(tui: TUI, ctx: SlashCommandContext, onClose: (
60
60
  const items = [...fleetItems, ...delegationItems];
61
61
 
62
62
  return openListOverlay(tui, {
63
+ markerId: "agents",
63
64
  title: "Agents Reference",
64
65
  items,
65
66
  filterable: true,
@@ -18,7 +18,13 @@ import {
18
18
  } from "../../engine/tui.js";
19
19
  import type { AskUserAnswer, AskUserQuestion, AskUserResult } from "../../tools/ask-user.js";
20
20
  import { ASK_USER_OTHER_LABEL, cancelledAskUserResult } from "../../tools/ask-user.js";
21
- import { buildHint, DEFAULT_SELECT_THEME, type HintEntry, showClioOverlayFrame } from "../overlay-frame.js";
21
+ import {
22
+ buildHint,
23
+ DEFAULT_SELECT_THEME,
24
+ type HintEntry,
25
+ type OverlayEscVerb,
26
+ showClioOverlayFrame,
27
+ } from "../overlay-frame.js";
22
28
  import { type ClioToken, clioTheme, dotSep, GLYPH, screenTitle } from "../theme/index.js";
23
29
  import { nextContentScrollOffset, type ViewScrollAction } from "../view/view-overlay.js";
24
30
 
@@ -91,6 +97,8 @@ const MIN_VISIBLE_OPTIONS = 3;
91
97
  /** Interview rows spent on chrome before an option row: strip, header, blanks, status, one question row. */
92
98
  const INTERVIEW_CHROME_ROWS = 7;
93
99
  const MAX_VISIBLE_OPTIONS = 12;
100
+ /** Below this the second column is too narrow to wrap a sentence into. */
101
+ const MIN_DESCRIPTION_COLUMN = 28;
94
102
  const ELLIPSIS = "…";
95
103
 
96
104
  /**
@@ -511,16 +519,24 @@ class AskUserOverlayView implements Component {
511
519
  this.presentation.requiredActions.find((action) => action.id === "record-answer")?.label.toLowerCase() ??
512
520
  "record answer";
513
521
  if (state.mode === "text") {
522
+ // Esc means two different things depending on what is behind the field.
523
+ // A question with options has a list to fall back to, so the key goes
524
+ // back; a question without one has nothing behind it and the key still
525
+ // leaves the interview. The footer says which one this is.
526
+ const escapeVerb: OverlayEscVerb = questionHasOptions(question) ? "back" : "close";
514
527
  return this.questions.length > 1
515
- ? this.withScrollHint([
516
- { key: "Enter", verb: recordAnswer },
517
- { key: "Alt+Left/Right", verb: "question" },
518
- ])
519
- : this.withScrollHint([{ key: "Enter", verb: recordAnswer }]);
528
+ ? this.withScrollHint(
529
+ [
530
+ { key: "Enter", verb: recordAnswer },
531
+ { key: "Alt+Left/Right", verb: "question" },
532
+ ],
533
+ escapeVerb,
534
+ )
535
+ : this.withScrollHint([{ key: "Enter", verb: recordAnswer }], escapeVerb);
520
536
  }
521
537
  // The classified action label distinguishes recording an answer from
522
- // granting authority. Esc keeps the product's one word for the way out,
523
- // since it closes the prompt and the interview together.
538
+ // granting authority. Esc on the option list is the way out of the whole
539
+ // interview, which is the one place the product spells `close`.
524
540
  // `t` is on every select footer because the operator cannot tell from a
525
541
  // label whether the option needs a figure attached until they read it.
526
542
  const addText: HintEntry = { key: "t", verb: "add text", short: "text" };
@@ -558,9 +574,9 @@ class AskUserOverlayView implements Component {
558
574
  * scroll. A footer that always advertised PgUp would be advertising a key that
559
575
  * does nothing on most rounds.
560
576
  */
561
- private withScrollHint(entries: ReadonlyArray<HintEntry>): string {
562
- if (this.surface !== "interview" || !this.contentOverflows) return buildHint(entries);
563
- return buildHint([...entries, { key: "PgUp/PgDn", verb: "scroll" }]);
577
+ private withScrollHint(entries: ReadonlyArray<HintEntry>, esc: OverlayEscVerb = "close"): string {
578
+ if (this.surface !== "interview" || !this.contentOverflows) return buildHint(entries, esc);
579
+ return buildHint([...entries, { key: "PgUp/PgDn", verb: "scroll" }], esc);
564
580
  }
565
581
 
566
582
  private setSurface(next: AskUserSurface): void {
@@ -784,17 +800,29 @@ class AskUserOverlayView implements Component {
784
800
  0,
785
801
  items.findIndex((item) => item.value === selectedItem.value),
786
802
  );
787
- const visibleCount = Math.min(this.maxVisibleOptions(), Math.max(1, items.length));
803
+ const primaryColumnWidth = this.primaryColumnWidth(items, width);
804
+ const acceptKey = question.multi_select === true ? "Space" : "Enter";
805
+ // The focused option's description wraps in full, so the rows it needs come
806
+ // out of the list rather than out of the sentence. Options the operator
807
+ // cannot currently read are what a windowed list is for, and one arrow key
808
+ // brings any of them back; a cut explanation has no such recovery.
809
+ const focusedRows = this.renderOptionRow(
810
+ items[selectedIndex] as SelectItem,
811
+ true,
812
+ width,
813
+ primaryColumnWidth,
814
+ acceptKey,
815
+ ).length;
816
+ const budget = Math.max(1, this.maxVisibleOptions() - (focusedRows - 1));
817
+ const visibleCount = Math.min(budget, Math.max(1, items.length));
788
818
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCount / 2), items.length - visibleCount));
789
819
  const end = Math.min(items.length, start + visibleCount);
790
- const primaryColumnWidth = this.primaryColumnWidth(items, width);
791
820
  const lines: string[] = [];
792
821
 
793
- const acceptKey = question.multi_select === true ? "Space" : "Enter";
794
822
  for (let index = start; index < end; index += 1) {
795
823
  const item = items[index];
796
824
  if (!item) continue;
797
- lines.push(this.renderOptionRow(item, index === selectedIndex, width, primaryColumnWidth, acceptKey));
825
+ lines.push(...this.renderOptionRow(item, index === selectedIndex, width, primaryColumnWidth, acceptKey));
798
826
  }
799
827
 
800
828
  if (start > 0 || end < items.length) {
@@ -809,13 +837,22 @@ class AskUserOverlayView implements Component {
809
837
  return Math.max(1, Math.min(bounded, Math.max(1, width - 4)));
810
838
  }
811
839
 
840
+ /**
841
+ * One option, as the rows it needs.
842
+ *
843
+ * The focused option's description wraps under its own column and is never
844
+ * cut: a description is what the operator is choosing between, and half of
845
+ * one reads as the whole thing. An unfocused row stays a one-line table cell
846
+ * whose ellipsis says there is more, which the operator reads by moving to
847
+ * it.
848
+ */
812
849
  private renderOptionRow(
813
850
  item: SelectItem,
814
851
  selected: boolean,
815
852
  width: number,
816
853
  primaryColumnWidth: number,
817
854
  acceptKey: string,
818
- ): string {
855
+ ): string[] {
819
856
  const theme = clioTheme();
820
857
  const prefix = selected ? theme.fg("accent", `${GLYPH.cursor} `) : " ";
821
858
  // The key that answers rides on the focused row. An operator whose eyes are
@@ -825,22 +862,40 @@ class AskUserOverlayView implements Component {
825
862
  const available = Math.max(1, width - 2 - visibleWidth(affordance));
826
863
  const description = item.description?.replace(/[\r\n]+/g, " ").trim();
827
864
 
828
- let body: string;
829
- if (description && width > 40) {
830
- const labelWidth = Math.max(1, Math.min(primaryColumnWidth - 2, available - 4));
831
- const label = truncateToWidth(item.label, labelWidth, ELLIPSIS, false);
832
- const spacing = " ".repeat(Math.max(1, primaryColumnWidth - visibleWidth(label)));
833
- const descriptionWidth = Math.max(1, available - visibleWidth(label) - visibleWidth(spacing));
834
- const desc = truncateToWidth(description, descriptionWidth, ELLIPSIS, false);
835
- body = selected
836
- ? theme.style("accent", `${label}${spacing}${desc}`, { bold: true })
837
- : `${label}${theme.fg("muted", `${spacing}${desc}`)}`;
838
- } else {
865
+ if (!description || width <= 40) {
839
866
  const label = truncateToWidth(item.label, available, ELLIPSIS, false);
840
- body = selected ? theme.style("accent", label, { bold: true }) : label;
867
+ const body = selected ? theme.style("accent", label, { bold: true }) : label;
868
+ return [fitLine(`${prefix}${body}${affordance}`, width)];
841
869
  }
842
870
 
843
- return fitLine(`${prefix}${body}${affordance}`, width);
871
+ const labelWidth = Math.max(1, Math.min(primaryColumnWidth - 2, available - 4));
872
+ const label = truncateToWidth(item.label, labelWidth, ELLIPSIS, false);
873
+ const spacing = " ".repeat(Math.max(1, primaryColumnWidth - visibleWidth(label)));
874
+ const descriptionWidth = Math.max(1, available - visibleWidth(label) - visibleWidth(spacing));
875
+ if (!selected) {
876
+ const desc = truncateToWidth(description, descriptionWidth, ELLIPSIS, false);
877
+ return [fitLine(`${prefix}${label}${theme.fg("muted", `${spacing}${desc}`)}`, width)];
878
+ }
879
+ // A gutter this narrow wraps a sentence into slivers and breaks words, so
880
+ // below the floor the description drops to its own full-width lines under
881
+ // the label instead of squeezing into the second column.
882
+ if (descriptionWidth < MIN_DESCRIPTION_COLUMN) {
883
+ const indent = " ";
884
+ const body = theme.style("accent", label, { bold: true });
885
+ return [
886
+ fitLine(`${prefix}${body}${affordance}`, width),
887
+ ...wrapTextWithAnsi(description, Math.max(1, width - indent.length)).map((line) =>
888
+ fitLine(`${indent}${theme.style("accent", line, { bold: true })}`, width),
889
+ ),
890
+ ];
891
+ }
892
+ const wrapped = wrapTextWithAnsi(description, descriptionWidth);
893
+ const head = wrapped[0] ?? "";
894
+ const indent = " ".repeat(visibleWidth(prefix) + visibleWidth(label) + visibleWidth(spacing));
895
+ return [
896
+ fitLine(`${prefix}${theme.style("accent", `${label}${spacing}${head}`, { bold: true })}${affordance}`, width),
897
+ ...wrapped.slice(1).map((line) => fitLine(`${indent}${theme.style("accent", line, { bold: true })}`, width)),
898
+ ];
844
899
  }
845
900
 
846
901
  private ensureControl(): void {
@@ -889,7 +944,7 @@ class AskUserOverlayView implements Component {
889
944
  state.answer = questionHasOptions(question) ? answerText(question, state.selected, state.customAnswer) : answer;
890
945
  this.finishIfCompleteOrAdvance();
891
946
  };
892
- activeInput.onEscape = () => this.cancel();
947
+ activeInput.onEscape = () => this.escapeFromTextInput(question, state);
893
948
  this.input = activeInput;
894
949
  this.list = null;
895
950
  }
@@ -960,6 +1015,53 @@ class AskUserOverlayView implements Component {
960
1015
  return 0;
961
1016
  }
962
1017
 
1018
+ /**
1019
+ * Esc in the text field.
1020
+ *
1021
+ * The field used to resolve the whole round as cancelled, which made a typed
1022
+ * draft unrecoverable: a single-question round had no other question to answer
1023
+ * first, so the only way off `t` was to abandon the interview (issue #260). A
1024
+ * question with options has a surface behind the field, so Esc goes back to it
1025
+ * and drops the draft. A question with no options has nothing behind it, so Esc
1026
+ * keeps its old meaning and leaves the interview; the footer says which.
1027
+ *
1028
+ * The draft is dropped rather than parked because Esc is the discard gesture.
1029
+ * It never reached `answer`, `options`, or `value` in the first place, since
1030
+ * only a submit writes those, so #228's clearing rule holds either way.
1031
+ */
1032
+ private escapeFromTextInput(question: AskUserQuestion, state: QuestionState): void {
1033
+ if (!questionHasOptions(question)) {
1034
+ this.cancel();
1035
+ return;
1036
+ }
1037
+ state.inputValue = "";
1038
+ state.mode = "select";
1039
+ this.status = "";
1040
+ this.rebuildSelectList(question, state);
1041
+ this.deps.requestRender();
1042
+ }
1043
+
1044
+ /**
1045
+ * Leave a question the operator is walking away from on its option list.
1046
+ *
1047
+ * The mode used to belong to the question for the rest of the round, so a
1048
+ * question opened with `t` came back as a prefilled field however the operator
1049
+ * returned to it, and the option list was gone for good. Navigating away is not
1050
+ * the discard gesture, so the draft stays in `inputValue` and `t` finds it
1051
+ * again; what does not survive is the field being the thing that greets them.
1052
+ *
1053
+ * A question whose value was actually submitted keeps its field, because
1054
+ * coming back to a recorded figure is how it gets revised (issue #228).
1055
+ */
1056
+ private parkTextMode(): void {
1057
+ const question = this.currentQuestion();
1058
+ const state = this.currentState();
1059
+ if (!question || !state) return;
1060
+ if (state.mode !== "text" || !questionHasOptions(question)) return;
1061
+ if (state.rawValue.length > 0) return;
1062
+ state.mode = "select";
1063
+ }
1064
+
963
1065
  private openTextInput(nextStatus: string): void {
964
1066
  const state = this.currentState();
965
1067
  const question = this.currentQuestion();
@@ -1077,6 +1179,7 @@ class AskUserOverlayView implements Component {
1077
1179
  private goToRelativeQuestion(delta: -1 | 1): void {
1078
1180
  if (this.questions.length <= 1) return;
1079
1181
  this.syncActiveControl();
1182
+ this.parkTextMode();
1080
1183
  this.index = (this.index + delta + this.questions.length) % this.questions.length;
1081
1184
  this.status = "";
1082
1185
  this.resetContentScroll();
@@ -1128,6 +1231,9 @@ export function openAskUserOverlay(tui: TUI, deps: OpenAskUserOverlayDeps): AskU
1128
1231
  // the terminal has left after the margins.
1129
1232
  ...(geometry.maxInnerRows > 0 ? { maxHeight: geometry.maxInnerRows + ASK_USER_FRAME_ROWS } : {}),
1130
1233
  margin: geometry.margin,
1234
+ // Not derived from the title: this modal swaps between a waiting title
1235
+ // and a classified decision title without ever changing hands.
1236
+ markerId: "ask-user",
1131
1237
  title: () => (view.isDecisionPending() ? view.decisionTitle() : ASK_USER_WAITING_TITLE),
1132
1238
  tone: () => (view.isDecisionPending() ? view.decisionTone() : undefined),
1133
1239
  footerHint: () => view.footerHint(),
@@ -211,6 +211,7 @@ export function openAuthDialog(tui: TUI, title: string, onCancel: () => void): A
211
211
  const handle = showClioOverlayFrame(tui, box, {
212
212
  anchor: "center",
213
213
  width: AUTH_DIALOG_WIDTH,
214
+ markerId: "auth",
214
215
  title: "Authentication",
215
216
  footerHint: getHint,
216
217
  });
@@ -102,6 +102,7 @@ export function openContextResetOverlay(tui: TUI, deps: OpenContextResetOverlayD
102
102
  return showClioOverlayFrame(tui, new FocusBox(list), {
103
103
  anchor: "center",
104
104
  width: CONTEXT_RESET_OVERLAY_WIDTH,
105
+ markerId: "context-reset",
105
106
  title: "Reset Project Context",
106
107
  footerHint: buildHint([{ key: "Enter", verb: "select" }]),
107
108
  });
@@ -135,6 +135,7 @@ export function openCwdFallbackOverlay(tui: TUI, deps: OpenCwdFallbackOverlayDep
135
135
  return showClioOverlayFrame(tui, box, {
136
136
  anchor: "center",
137
137
  width: CWD_FALLBACK_OVERLAY_WIDTH,
138
+ markerId: "cwd-fallback",
138
139
  title: "Session Working Directory",
139
140
  footerHint: buildHint([{ key: "Enter", verb: "select" }]),
140
141
  });
@@ -94,22 +94,21 @@ function decisionLines(decision: DecisionRecord, selected: boolean, expanded: bo
94
94
  const label = decision.status === "superseded" ? theme.fg("dim", labelText) : theme.fg("muted", labelText);
95
95
  const value = decision.status === "superseded" ? theme.fg("dim", decision.value) : decision.value;
96
96
  const lines = [fitLine(`${cursor} ${status} ${label}${theme.fg("dim", ":")} ${value}`, width)];
97
+ // The indent belongs to the container, so this text wraps inside what is left
98
+ // of the row. Wrapping to the full width and then indenting cost every line
99
+ // its last six columns to a cut that landed mid-sentence.
100
+ const indent = " ";
101
+ const indented = Math.max(1, width - indent.length);
102
+ const wrapIndented = (text: string): string[] =>
103
+ wrapTextWithAnsi(text, indented).map((line) => fitLine(`${indent}${line}`, width));
97
104
  if (decision.status === "superseded" && decision.correction) {
98
- lines.push(fitLine(` ${theme.fg("dim", "correction")} ${theme.fg("muted", decision.correction)}`, width));
105
+ lines.push(...wrapIndented(`${theme.fg("dim", "correction")} ${theme.fg("muted", decision.correction)}`));
99
106
  }
100
107
  if (selected && expanded) {
101
108
  if (decision.source_question) {
102
- lines.push(
103
- ...wrapTextWithAnsi(`${theme.fg("dim", "question")} ${theme.fg("muted", decision.source_question)}`, width).map(
104
- (line) => fitLine(` ${line}`, width),
105
- ),
106
- );
109
+ lines.push(...wrapIndented(`${theme.fg("dim", "question")} ${theme.fg("muted", decision.source_question)}`));
107
110
  }
108
- lines.push(
109
- ...wrapTextWithAnsi(`${theme.fg("dim", "answer")} ${theme.fg("muted", decision.value)}`, width).map((line) =>
110
- fitLine(` ${line}`, width),
111
- ),
112
- );
111
+ lines.push(...wrapIndented(`${theme.fg("dim", "answer")} ${theme.fg("muted", decision.value)}`));
113
112
  }
114
113
  return lines;
115
114
  }
@@ -296,6 +295,7 @@ export function openDecisionsOverlay(
296
295
  return showClioOverlayFrame(tui, body, {
297
296
  anchor: "center",
298
297
  width: DECISIONS_OVERLAY_WIDTH,
298
+ markerId: "decisions",
299
299
  title: () => "Decisions",
300
300
  footerHint: () => body.footerHint(),
301
301
  });
@@ -45,6 +45,7 @@ export function openExtensionsOverlay(tui: TUI, ctx: SlashCommandContext, onClos
45
45
  });
46
46
 
47
47
  return openListOverlay(tui, {
48
+ markerId: "extensions",
48
49
  title: "Extensions Reference",
49
50
  items,
50
51
  filterable: true,
@@ -191,6 +191,7 @@ export function openFleetRunApprovalOverlay(tui: TUI, options: OpenFleetRunAppro
191
191
  const handle = showClioOverlayFrame(tui, focus, {
192
192
  anchor: "center",
193
193
  width: fleetRunApprovalOverlayWidth(options.columns),
194
+ markerId: "fleet-run-approval",
194
195
  title: FLEET_RUN_APPROVAL_OVERLAY_TITLE,
195
196
  footerHint: buildResponsiveHint(
196
197
  [...(acceptable ? [{ key: "Enter", verb: "dispatch" }] : []), { key: "↑↓", verb: "scroll" }],
@@ -163,6 +163,7 @@ export function openHandoffReviewOverlay(
163
163
  const handle = showClioOverlayFrame(tui, focus, {
164
164
  anchor: "center",
165
165
  width: handoffReviewOverlayWidth(options.columns),
166
+ markerId: "handoff-review",
166
167
  title: HANDOFF_REVIEW_OVERLAY_TITLE,
167
168
  footerHint: buildResponsiveHint(
168
169
  [
@@ -94,6 +94,7 @@ export function openHelpOverlay(
94
94
  detail: () => [
95
95
  "# Fleet runs & steering",
96
96
  "**Fleet Runs board**: open it with the configured Dispatch Board key (Alt+W by default). Use Up/Down or `j`/`k` to select a live or recent run.",
97
+ "**Enter**: on a live run in a `--with-panes` session, Enter opens (or retargets) the watch pane beside Clio, rendering that run's stream; the arrow keys then move it between runs. On a finished run, or without panes, Enter toggles the inline worker-progress detail instead.",
97
98
  "**Steer**: press `s` on a live native run to close the board and prefill `@<runId> `. Add guidance and submit it normally. The first notice means queued; a received notice confirms worker delivery.",
98
99
  "**Cancel**: press `x` on a running, stale, queued, or retry-waiting run. The row changes to cancelling while the worker or retry is being stopped.",
99
100
  "**Capabilities**: ACP delegation runs cannot accept live steering. The board footer only advertises actions supported by the selected row.",
@@ -140,6 +141,7 @@ export function openHelpOverlay(
140
141
  const items = [...commands, ...keys, ...topics];
141
142
 
142
143
  return openListOverlay(tui, {
144
+ markerId: "help",
143
145
  title: "Help Center",
144
146
  items,
145
147
  filterable: true,
@@ -120,6 +120,7 @@ export function openInteropOverlay(tui: TUI, ctx: SlashCommandContext, onClose:
120
120
  };
121
121
 
122
122
  const handle: ListOverlayHandle = openListOverlay(tui, {
123
+ markerId: "interop",
123
124
  title: "Interop",
124
125
  items: buildItems(deps),
125
126
  filterable: true,
@@ -137,6 +137,7 @@ export function openLibraryInstallConfirmOverlay(
137
137
  const handle = showClioOverlayFrame(tui, focus, {
138
138
  anchor: "center",
139
139
  width: confirmOverlayWidth(options.columns),
140
+ markerId: "library-install",
140
141
  title: LIBRARY_INSTALL_CONFIRM_TITLE,
141
142
  footerHint: buildResponsiveHint([{ key: "Enter", verb: "install" }], { key: "Esc", verb: "cancel" }),
142
143
  });