@iowarp/clio-coder 0.3.8 → 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 (552) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/NOTICE +33 -0
  3. package/README.md +12 -3
  4. package/dist/{acp-U67UHUK2.js → acp-G5WJBNCT.js} +13 -12
  5. package/dist/{agents-YU6SGALZ.js → agents-FMV2Q5G4.js} +41 -36
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5ZPJOIVG.js → auth-3IDSJEIK.js} +18 -18
  8. package/dist/{builtins-C6JMZVV6.js → builtins-XCZWXSC7.js} +5 -5
  9. package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
  10. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  11. package/dist/{chunk-XWSF374K.js → chunk-2OQE55CK.js} +3 -3
  12. package/dist/{chunk-5H3GB5BO.js → chunk-32KWKNSF.js} +8 -384
  13. package/dist/{chunk-KTYTFRMB.js → chunk-36EJLSQQ.js} +34 -36
  14. package/dist/{chunk-FHJEP5SW.js → chunk-3BT2XMV4.js} +19 -13
  15. package/dist/chunk-3DPEIQKN.js +113 -0
  16. package/dist/{chunk-GU2UIAFZ.js → chunk-3URVFKWK.js} +7 -7
  17. package/dist/{chunk-WEH5XRJQ.js → chunk-3XML7CDN.js} +3 -3
  18. package/dist/chunk-42FMPA75.js +101 -0
  19. package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
  20. package/dist/{chunk-VCBR6CU7.js → chunk-5PSMVOLM.js} +2 -2
  21. package/dist/{chunk-NMPKI6XL.js → chunk-6DB53AJS.js} +203 -25
  22. package/dist/{chunk-3BINW3FP.js → chunk-76ONBSIA.js} +2 -2
  23. package/dist/{chunk-26LEYJZH.js → chunk-7MCTRUCE.js} +2 -2
  24. package/dist/{chunk-TYPGUK6W.js → chunk-AB44T6BB.js} +111 -5
  25. package/dist/{chunk-K4XHGFR5.js → chunk-B7OBL7PK.js} +317 -721
  26. package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
  27. package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
  28. package/dist/{chunk-ZVJ5BLO2.js → chunk-BKFJHQCA.js} +154 -16
  29. package/dist/chunk-BKFM6EJV.js +462 -0
  30. package/dist/chunk-BMS5RKQY.js +27 -0
  31. package/dist/{chunk-ZNLWCMVZ.js → chunk-BPKCPIL7.js} +2 -2
  32. package/dist/{chunk-TLQJPP24.js → chunk-BUMFYQFY.js} +1394 -1349
  33. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  34. package/dist/chunk-C2LTL2W6.js +2447 -0
  35. package/dist/{chunk-ME6CCNFO.js → chunk-CODPRO7Q.js} +8 -8
  36. package/dist/{chunk-E77JEWSD.js → chunk-CTJ4RNAA.js} +7 -37
  37. package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
  38. package/dist/{chunk-7RFXX52T.js → chunk-DQITNCXG.js} +642 -172
  39. package/dist/{chunk-TTHACPOM.js → chunk-DYJP44XW.js} +578 -117
  40. package/dist/{chunk-2HFZQUHL.js → chunk-F4CKPOEQ.js} +18 -8
  41. package/dist/{chunk-DGSYXYMX.js → chunk-FEFIFZTL.js} +3 -3
  42. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  43. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  44. package/dist/chunk-GKF55TAZ.js +391 -0
  45. package/dist/{chunk-VWZOAB7K.js → chunk-GR5G2PVF.js} +9 -8
  46. package/dist/{chunk-4DGYLA73.js → chunk-GXNLGKAB.js} +80 -9
  47. package/dist/chunk-HHV2GANA.js +88 -0
  48. package/dist/{chunk-IIZWH4XA.js → chunk-HI63TFOG.js} +5 -4
  49. package/dist/{chunk-WLFILSD5.js → chunk-HJJTYUHX.js} +113 -83
  50. package/dist/{chunk-TT36MB5S.js → chunk-HLW2MRKE.js} +3 -1
  51. package/dist/{chunk-PMDBGQSJ.js → chunk-HWHKMHUA.js} +7 -7
  52. package/dist/chunk-HZHHCK24.js +1631 -0
  53. package/dist/{chunk-WSB3FPX7.js → chunk-I5VEOC6I.js} +39 -143
  54. package/dist/chunk-IBEBSCYA.js +564 -0
  55. package/dist/chunk-IQ7KR472.js +362 -0
  56. package/dist/{chunk-A3WNZD3P.js → chunk-J4W7KFM7.js} +949 -972
  57. package/dist/{chunk-TB5666IT.js → chunk-JDG2WCRO.js} +5 -5
  58. package/dist/{chunk-N22QMJKY.js → chunk-K5C3NCBD.js} +4 -4
  59. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  60. package/dist/{chunk-5C3AQNDW.js → chunk-KFZI4NIL.js} +216 -38
  61. package/dist/chunk-KMVISBZR.js +132 -0
  62. package/dist/{chunk-WXY7KU3G.js → chunk-LDQ2ZF2M.js} +2 -2
  63. package/dist/{chunk-XN3L4EYL.js → chunk-LQ3DZAMX.js} +3 -3
  64. package/dist/{chunk-U6MBIEMB.js → chunk-LY4S7GJC.js} +173 -144
  65. package/dist/{chunk-4SPRNWDE.js → chunk-MLKNTWH2.js} +19 -19
  66. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  67. package/dist/chunk-NEKRRTYW.js +56 -0
  68. package/dist/{chunk-SPULKLCF.js → chunk-NHCZP4K7.js} +3 -3
  69. package/dist/chunk-NHLBIGRH.js +1506 -0
  70. package/dist/chunk-NQQH3YT7.js +302 -0
  71. package/dist/chunk-NYS75XW5.js +15 -0
  72. package/dist/{chunk-GN57SG4G.js → chunk-O4XIVISU.js} +10 -8
  73. package/dist/{chunk-EMYUUSFG.js → chunk-O6TL7WWY.js} +6 -6
  74. package/dist/chunk-OQBA45DZ.js +97 -0
  75. package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
  76. package/dist/chunk-PMZCIOCJ.js +25 -0
  77. package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
  78. package/dist/{chunk-J3YUBZWY.js → chunk-QBJA7R7N.js} +62 -6
  79. package/dist/chunk-QDC3K2U3.js +262 -0
  80. package/dist/{chunk-2HEJ2F35.js → chunk-QLFS5GO2.js} +22 -10
  81. package/dist/{chunk-7RGZWPB6.js → chunk-QLL7ILRG.js} +95 -32
  82. package/dist/{chunk-YS5VLNH5.js → chunk-QREDIESB.js} +6 -6
  83. package/dist/chunk-QSNYB6ZV.js +195 -0
  84. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  85. package/dist/chunk-RKKLTLYB.js +45 -0
  86. package/dist/{chunk-P43ETTHK.js → chunk-SJ5ZKQ4S.js} +2 -2
  87. package/dist/{chunk-GPIEI3LY.js → chunk-SP2RXXYO.js} +6 -54
  88. package/dist/chunk-SUCTJL45.js +45 -0
  89. package/dist/{chunk-DYIM5TJT.js → chunk-SUW5DORT.js} +263 -7
  90. package/dist/chunk-T56WDKA5.js +183 -0
  91. package/dist/chunk-TVHHYFHE.js +255 -0
  92. package/dist/{chunk-FJ3H4MN5.js → chunk-TZ3SGWZZ.js} +3 -3
  93. package/dist/{chunk-MXKJU4JB.js → chunk-U77AMWDL.js} +91 -10
  94. package/dist/{chunk-5DHKRSMQ.js → chunk-ULC6OTWO.js} +11 -7
  95. package/dist/{chunk-RWSI4YD7.js → chunk-UM7N4G5A.js} +33 -12
  96. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  97. package/dist/{chunk-IGWKHNIQ.js → chunk-UXMFQ54G.js} +44 -37
  98. package/dist/{chunk-HFSBBKSQ.js → chunk-V5DHCITQ.js} +171 -3
  99. package/dist/{chunk-5WIGXA4T.js → chunk-VAZSBTKF.js} +111 -4
  100. package/dist/{chunk-VHN4MY6O.js → chunk-VEO4AP2K.js} +2 -2
  101. package/dist/{chunk-IJ7RPIYJ.js → chunk-VFA6GDY5.js} +65 -4
  102. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  103. package/dist/chunk-VO67MWHC.js +75 -0
  104. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  105. package/dist/{chunk-TANS5ZJS.js → chunk-VYMXRQI6.js} +36 -22
  106. package/dist/{chunk-WWCZ5F23.js → chunk-W5VSYASO.js} +77 -16
  107. package/dist/{chunk-WNIJTQQK.js → chunk-WZR7K7ZX.js} +72 -116
  108. package/dist/{chunk-5Q2VVUKB.js → chunk-X3YGUTOB.js} +4 -4
  109. package/dist/chunk-X75E3D2N.js +686 -0
  110. package/dist/{chunk-VAWNZU7Z.js → chunk-YDFRH54B.js} +4 -4
  111. package/dist/chunk-YJX4SHTD.js +40 -0
  112. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  113. package/dist/chunk-Z2RR6MAK.js +127 -0
  114. package/dist/{chunk-FBVTI2TJ.js → chunk-Z4TXYIEG.js} +12 -131
  115. package/dist/cli/index.js +47 -36
  116. package/dist/{clio-QVTYJ57A.js → clio-2JXHBBY5.js} +7 -7
  117. package/dist/{code-nav-FGGFIE7L.js → code-nav-3YYRMYNF.js} +8 -8
  118. package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
  119. package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
  120. package/dist/{config-LW5IJFQN.js → config-QZPCMYSO.js} +99 -63
  121. package/dist/{configure-7XIZCOU4.js → configure-TEGEBYCA.js} +23 -22
  122. package/dist/{context-Y6Y7QPR6.js → context-AV7OEZ4D.js} +12 -12
  123. package/dist/{context-L3WL3X7K.js → context-E6H5RNMC.js} +56 -47
  124. package/dist/{context-N52ZA626.js → context-GSXUE4CT.js} +29 -27
  125. package/dist/{context-clear-MBQRLSDQ.js → context-clear-SHIBYK6T.js} +55 -46
  126. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  127. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-5ZGKPGZQ.js} +13 -13
  128. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-EFMJT4LD.js} +93 -64
  129. package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
  130. package/dist/doctor-QOA5FNY5.js +313 -0
  131. package/dist/{eval-BEC2WHDA.js → eval-TFBYQH4H.js} +2032 -156
  132. package/dist/eval-inventory-SXH7PDKX.js +316 -0
  133. package/dist/{evidence-REJUMSKM.js → evidence-ERGESKGN.js} +203 -48
  134. package/dist/{evolve-PY5ZBA5K.js → evolve-VDXTSYCJ.js} +52 -43
  135. package/dist/{extensions-HVKU65YU.js → extensions-7BGBHN57.js} +13 -7
  136. package/dist/{fleet-7WZEWRFA.js → fleet-2RRVDF2V.js} +228 -108
  137. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-VJ726XIA.js} +11 -11
  138. package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
  139. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-JF5QOATM.js} +18 -15
  140. package/dist/fleet-inspect-VLY4S7QM.js +442 -0
  141. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-AIZUEJOY.js} +5 -5
  142. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-AJRPDMDV.js} +22 -19
  143. package/dist/fleet-verify-JFEL2L3H.js +175 -0
  144. package/dist/fleet-view-ZCON35AG.js +102 -0
  145. package/dist/{init-OG3TPGQG.js → init-DN2WWLFE.js} +72 -62
  146. package/dist/install-XGLBQY5E.js +13 -0
  147. package/dist/interop-OZBKXAYL.js +114 -0
  148. package/dist/{library-CNTMPLRF.js → library-YWZG7IMW.js} +21 -18
  149. package/dist/{memory-6IS7F275.js → memory-I4C4HMLW.js} +54 -45
  150. package/dist/{models-ENRJDA5W.js → models-CEYXJBO6.js} +33 -30
  151. package/dist/{monitor-XLDVO7TN.js → monitor-NZ6GCI3P.js} +59 -52
  152. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-GCGQ4N5I.js} +7827 -7328
  153. package/dist/panes-HMABYVO4.js +58 -0
  154. package/dist/panes-KY6W3V2E.js +103 -0
  155. package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
  156. package/dist/{reset-RZ4ER727.js → reset-DQ6FGCSH.js} +13 -11
  157. package/dist/resources-BB3MVJMD.js +111 -0
  158. package/dist/{run-Y2CNK5RU.js → run-H2GQDUER.js} +119 -87
  159. package/dist/{share-A55GYP6Z.js → share-GTJN6A5O.js} +20 -17
  160. package/dist/{skills-ALC5J6AT.js → skills-L55TEW6R.js} +33 -24
  161. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-XVXPH2JI.js} +67 -56
  162. package/dist/skills-inventory-S4MXPJFV.js +126 -0
  163. package/dist/slash-commands-ZSGASKJC.js +77 -0
  164. package/dist/{steer-GGWFUJUD.js → steer-RZGSCY4R.js} +4 -4
  165. package/dist/{support-MIETYA5E.js → support-PKEUNNQL.js} +6 -6
  166. package/dist/{targets-VGNXIR3S.js → targets-NCPZ644J.js} +68 -40
  167. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-44SV3YCN.js} +6 -4
  168. package/dist/tools-DAF3DI3C.js +27 -0
  169. package/dist/{trace-PNCASAXC.js → trace-FYVW2MQA.js} +207 -10
  170. package/dist/tui-primitives-2AKXQNZK.js +13 -0
  171. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
  172. package/dist/{upgrade-FUSUAGHR.js → upgrade-3XPP6OQL.js} +27 -24
  173. package/dist/{usage-N4MKVHKD.js → usage-3NLHGTU2.js} +114 -62
  174. package/dist/{verifiers-YAWOJ3H2.js → verifiers-SSQONKRT.js} +172 -13
  175. package/dist/{verify-LTDHYBGY.js → verify-3U6J7FZI.js} +10 -10
  176. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
  177. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-CEHYGPGQ.js} +75 -65
  178. package/dist/with-panes-MKB46MPQ.js +782 -0
  179. package/dist/worker/entry.js +106 -75
  180. package/docs/README.md +3 -2
  181. package/docs/acp.md +24 -3
  182. package/docs/alcf-provider.md +1 -1
  183. package/docs/architecture.md +2 -2
  184. package/docs/artifact-versions.md +9 -1
  185. package/docs/built-in-agents.md +1 -1
  186. package/docs/capacity-and-scheduling.md +62 -3
  187. package/docs/commands-and-modes.md +31 -2
  188. package/docs/configuration-and-targets.md +69 -12
  189. package/docs/context-engine.md +63 -4
  190. package/docs/development-pipeline.md +19 -0
  191. package/docs/dispatch-typed-intent.md +385 -0
  192. package/docs/documentation-coverage.md +5 -5
  193. package/docs/documentation-guide.md +1 -1
  194. package/docs/environment-variables.md +3 -0
  195. package/docs/eval-runner.md +262 -11
  196. package/docs/evals-internal.md +72 -2
  197. package/docs/evidence-and-memory.md +12 -11
  198. package/docs/evolution.md +1 -1
  199. package/docs/exit-codes-and-output.md +1 -1
  200. package/docs/extensions-and-sharing.md +27 -1
  201. package/docs/fleet-dispatch.md +25 -4
  202. package/docs/installation-and-lifecycle.md +15 -2
  203. package/docs/middleware-and-components.md +1 -1
  204. package/docs/model-catalog.md +10 -1
  205. package/docs/observability.md +55 -4
  206. package/docs/proactive-memory.md +127 -14
  207. package/docs/prompt-envelope-and-tools.md +19 -1
  208. package/docs/provider-adapter-cookbook.md +1 -1
  209. package/docs/release-cut-checklist.md +19 -3
  210. package/docs/safety-model.md +2 -2
  211. package/docs/scientific-validation.md +3 -3
  212. package/docs/session-lifecycle.md +1 -1
  213. package/docs/skills-marketplace.md +1 -1
  214. package/docs/tool-usage.md +18 -8
  215. package/docs/trace-store.md +1 -1
  216. package/docs/troubleshooting.md +88 -1
  217. package/docs/tui-design.md +1 -1
  218. package/docs/worker-dispatch-mechanics.md +1 -1
  219. package/package.json +5 -2
  220. package/src/cli/acp.ts +6 -2
  221. package/src/cli/agents.ts +1 -1
  222. package/src/cli/argv.ts +25 -0
  223. package/src/cli/config-inspect.ts +33 -6
  224. package/src/cli/config.ts +1 -1
  225. package/src/cli/configure.ts +23 -21
  226. package/src/cli/doctor-panes.ts +124 -0
  227. package/src/cli/doctor-state-size.ts +82 -0
  228. package/src/cli/doctor-toolchain.ts +57 -0
  229. package/src/cli/doctor.ts +22 -1
  230. package/src/cli/eval-inventory.ts +436 -0
  231. package/src/cli/eval.ts +93 -16
  232. package/src/cli/evidence-detail.ts +88 -0
  233. package/src/cli/evidence-inventory.ts +183 -0
  234. package/src/cli/evidence.ts +30 -5
  235. package/src/cli/extensions.ts +5 -1
  236. package/src/cli/fleet-decisions.ts +69 -0
  237. package/src/cli/fleet-inspect.ts +334 -0
  238. package/src/cli/fleet-verify.ts +133 -0
  239. package/src/cli/fleet-view.ts +810 -0
  240. package/src/cli/fleet.ts +179 -39
  241. package/src/cli/index.ts +14 -2
  242. package/src/cli/interop-inspect.ts +128 -0
  243. package/src/cli/interop.ts +34 -0
  244. package/src/cli/panes.ts +35 -0
  245. package/src/cli/reset.ts +5 -2
  246. package/src/cli/run.ts +58 -0
  247. package/src/cli/skills-inventory.ts +185 -0
  248. package/src/cli/skills.ts +16 -13
  249. package/src/cli/targets.ts +44 -13
  250. package/src/cli/tools.ts +321 -0
  251. package/src/cli/trace-inspect.ts +252 -0
  252. package/src/cli/trace.ts +85 -5
  253. package/src/cli/usage.ts +63 -14
  254. package/src/cli/verifiers-inspect.ts +347 -0
  255. package/src/cli/verifiers.ts +9 -0
  256. package/src/core/bus-events.ts +33 -1
  257. package/src/core/cache-telemetry.ts +42 -0
  258. package/src/core/config.ts +67 -0
  259. package/src/core/defaults.ts +124 -8
  260. package/src/core/endpoint-key.ts +27 -0
  261. package/src/core/residency-target-key.ts +25 -0
  262. package/src/core/response-schema.ts +80 -6
  263. package/src/core/theme-token-hex.ts +43 -0
  264. package/src/core/tool-names.ts +2 -1
  265. package/src/core/xdg.ts +1 -1
  266. package/src/domains/agents/fleets/build-review.md +0 -3
  267. package/src/domains/agents/fleets/build-test.md +0 -3
  268. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  269. package/src/domains/agents/result-contract.ts +164 -35
  270. package/src/domains/config/classify.ts +6 -0
  271. package/src/domains/context/codewiki/coordinator.ts +12 -4
  272. package/src/domains/dispatch/admission-error.ts +9 -0
  273. package/src/domains/dispatch/admission.ts +52 -14
  274. package/src/domains/dispatch/capacity-lease.ts +118 -9
  275. package/src/domains/dispatch/contract.ts +11 -0
  276. package/src/domains/dispatch/council-topology.ts +398 -0
  277. package/src/domains/dispatch/execution-plan.ts +44 -4
  278. package/src/domains/dispatch/extension.ts +378 -82
  279. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  280. package/src/domains/dispatch/fleet-plan.ts +7 -2
  281. package/src/domains/dispatch/fleet-run.ts +87 -4
  282. package/src/domains/dispatch/gate-decisions.ts +11 -1
  283. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  284. package/src/domains/dispatch/gate-topology.ts +289 -0
  285. package/src/domains/dispatch/heartbeat.ts +32 -8
  286. package/src/domains/dispatch/index.ts +22 -0
  287. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  288. package/src/domains/dispatch/intent.ts +85 -1
  289. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  290. package/src/domains/dispatch/reservation-store.ts +139 -11
  291. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  292. package/src/domains/dispatch/run-event-journal.ts +598 -0
  293. package/src/domains/dispatch/state.ts +49 -1
  294. package/src/domains/dispatch/types.ts +13 -0
  295. package/src/domains/dispatch/validation.ts +33 -8
  296. package/src/domains/dispatch/worker-spawn.ts +25 -11
  297. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  298. package/src/domains/dispatch/write-boundary.ts +62 -1
  299. package/src/domains/eval/artifacts/store.ts +62 -0
  300. package/src/domains/eval/compare/behavioral.ts +224 -0
  301. package/src/domains/eval/compare/compare.ts +342 -2
  302. package/src/domains/eval/compare/envelope.ts +128 -0
  303. package/src/domains/eval/compare/gates.ts +24 -6
  304. package/src/domains/eval/compare/thresholds.ts +30 -3
  305. package/src/domains/eval/execution-provenance.ts +240 -0
  306. package/src/domains/eval/inventory.ts +113 -0
  307. package/src/domains/eval/metrics/aggregate.ts +136 -0
  308. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  309. package/src/domains/eval/metrics/tracked.ts +413 -0
  310. package/src/domains/eval/provenance.ts +117 -0
  311. package/src/domains/eval/reports/comparison.ts +128 -0
  312. package/src/domains/eval/reports/junit.ts +17 -3
  313. package/src/domains/eval/reports/markdown.ts +3 -3
  314. package/src/domains/eval/reports/text.ts +14 -0
  315. package/src/domains/eval/run-compare.ts +20 -0
  316. package/src/domains/eval/runners/clio-run.ts +127 -0
  317. package/src/domains/eval/runners/external-command.ts +28 -3
  318. package/src/domains/eval/schema/adapter.ts +111 -0
  319. package/src/domains/eval/schema/artifact.ts +20 -0
  320. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  321. package/src/domains/eval/schema/behavioral.ts +520 -0
  322. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  323. package/src/domains/eval/schema/serving.ts +105 -0
  324. package/src/domains/eval/schema/suite.ts +38 -8
  325. package/src/domains/eval/schema/validate.ts +58 -3
  326. package/src/domains/eval/schema/verdict.ts +237 -0
  327. package/src/domains/eval/suites/resolve.ts +2 -0
  328. package/src/domains/eval/suites/run.ts +264 -33
  329. package/src/domains/eval/verifiers/command.ts +2 -1
  330. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  331. package/src/domains/evidence/build.ts +2 -13
  332. package/src/domains/evidence/eval.ts +2 -12
  333. package/src/domains/evidence/findings-markdown.ts +33 -0
  334. package/src/domains/evidence/run-trust.ts +7 -113
  335. package/src/domains/evidence/store.ts +6 -0
  336. package/src/domains/evidence/trust-projection.ts +2 -2
  337. package/src/domains/extensions/compatibility.ts +285 -0
  338. package/src/domains/extensions/discovery.ts +38 -3
  339. package/src/domains/extensions/resources.ts +1 -1
  340. package/src/domains/extensions/state.ts +12 -3
  341. package/src/domains/extensions/types.ts +2 -0
  342. package/src/domains/lifecycle/doctor.ts +69 -1
  343. package/src/domains/memory/index.ts +14 -1
  344. package/src/domains/memory/task-bank-promotion.ts +64 -0
  345. package/src/domains/memory/task-memory-policy.ts +82 -17
  346. package/src/domains/memory/task-memory-spend.ts +131 -0
  347. package/src/domains/memory/task-memory-status.ts +7 -0
  348. package/src/domains/memory/task-memory-telemetry.ts +3 -0
  349. package/src/domains/middleware/index.ts +1 -0
  350. package/src/domains/middleware/memory-intervention.ts +97 -21
  351. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  352. package/src/domains/mux/contract.ts +434 -0
  353. package/src/domains/mux/detect.ts +158 -0
  354. package/src/domains/mux/extension.ts +47 -0
  355. package/src/domains/mux/index.ts +96 -0
  356. package/src/domains/mux/manifest.ts +6 -0
  357. package/src/domains/mux/operations.ts +164 -0
  358. package/src/domains/mux/pane-registry.ts +90 -0
  359. package/src/domains/mux/protocol.ts +49 -0
  360. package/src/domains/mux/socket-client.ts +816 -0
  361. package/src/domains/mux/types.ts +222 -0
  362. package/src/domains/mux/viewer-command.ts +59 -0
  363. package/src/domains/mux/yazi/assets/init.lua +2 -0
  364. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  365. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  366. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  367. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  368. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  369. package/src/domains/mux/yazi/event-stream.ts +180 -0
  370. package/src/domains/mux/yazi/profile.ts +299 -0
  371. package/src/domains/mux/yazi/session.ts +228 -0
  372. package/src/domains/mux/yazi/theme.ts +30 -0
  373. package/src/domains/observability/background-memory-usage.ts +140 -0
  374. package/src/domains/observability/cost.ts +22 -1
  375. package/src/domains/observability/index.ts +9 -0
  376. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  377. package/src/domains/observability/trace-store.ts +234 -2
  378. package/src/domains/prompts/compiler.ts +100 -13
  379. package/src/domains/providers/endpoint-capacity.ts +228 -0
  380. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  381. package/src/domains/providers/extension.ts +20 -3
  382. package/src/domains/providers/index.ts +32 -0
  383. package/src/domains/providers/model-runtime-capabilities.ts +32 -0
  384. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  385. package/src/domains/providers/runtime-resolution.ts +8 -1
  386. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  387. package/src/domains/providers/runtimes/builtins.ts +2 -0
  388. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  389. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  390. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  391. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  392. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  393. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  394. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  395. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  396. package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
  397. package/src/domains/providers/support.ts +1 -0
  398. package/src/domains/providers/target-model-cache.ts +124 -0
  399. package/src/domains/providers/types/capability-flags.ts +2 -0
  400. package/src/domains/providers/types/target-descriptor.ts +2 -0
  401. package/src/domains/resources/index.ts +3 -0
  402. package/src/domains/resources/prompts/loader.ts +95 -33
  403. package/src/domains/resources/skills/loader.ts +33 -0
  404. package/src/domains/safety/action-classifier.ts +6 -0
  405. package/src/domains/safety/call-target.ts +52 -0
  406. package/src/domains/safety/run-effects.ts +35 -4
  407. package/src/domains/session/context-accounting.ts +52 -1
  408. package/src/domains/session/context-ledger.ts +37 -13
  409. package/src/domains/session/index.ts +6 -0
  410. package/src/domains/session/prompt-cache.ts +140 -0
  411. package/src/domains/session/prompt-manifest.ts +42 -0
  412. package/src/domains/toolchain/archive.ts +175 -0
  413. package/src/domains/toolchain/contract.ts +28 -0
  414. package/src/domains/toolchain/extension.ts +47 -0
  415. package/src/domains/toolchain/index.ts +39 -0
  416. package/src/domains/toolchain/install.ts +327 -0
  417. package/src/domains/toolchain/manifest.ts +8 -0
  418. package/src/domains/toolchain/paths.ts +34 -0
  419. package/src/domains/toolchain/registry.ts +265 -0
  420. package/src/domains/toolchain/remove.ts +218 -0
  421. package/src/domains/toolchain/resolve.ts +182 -0
  422. package/src/domains/toolchain/types.ts +113 -0
  423. package/src/domains/toolchain/version.ts +88 -0
  424. package/src/engine/acp/adapter.ts +18 -3
  425. package/src/engine/acp/server.ts +413 -70
  426. package/src/engine/acp/types.ts +19 -1
  427. package/src/engine/ai.ts +35 -0
  428. package/src/engine/apis/llamacpp-residency.ts +55 -3
  429. package/src/engine/apis/lmstudio.ts +25 -5
  430. package/src/engine/apis/ollama-native.ts +2 -1
  431. package/src/engine/apis/openai-completions.ts +80 -17
  432. package/src/engine/apis/residency-lock.ts +3 -1
  433. package/src/engine/apis/residency.ts +34 -1
  434. package/src/engine/claude/sdk-module.ts +98 -0
  435. package/src/engine/claude/sdk-runtime.ts +19 -11
  436. package/src/engine/provider-payload.ts +29 -1
  437. package/src/engine/tui-primitives.ts +21 -0
  438. package/src/engine/tui.ts +1 -0
  439. package/src/engine/worker-runtime.ts +2 -13
  440. package/src/entry/boot-options.ts +2 -0
  441. package/src/entry/orchestrator.ts +279 -35
  442. package/src/entry/panes-activation.ts +31 -0
  443. package/src/entry/with-panes.ts +20 -0
  444. package/src/interactive/chat-loop-messages.ts +26 -7
  445. package/src/interactive/chat-loop.ts +318 -41
  446. package/src/interactive/chat-panel.ts +62 -8
  447. package/src/interactive/clio-editor.ts +45 -8
  448. package/src/interactive/context-activity.ts +5 -1
  449. package/src/interactive/context-meter.ts +1 -1
  450. package/src/interactive/context-overlay.ts +41 -10
  451. package/src/interactive/cost-overlay.ts +66 -6
  452. package/src/interactive/council-grid.ts +1 -3
  453. package/src/interactive/council.ts +11 -0
  454. package/src/interactive/dispatch-board.ts +102 -20
  455. package/src/interactive/fleet-run-preview.ts +41 -15
  456. package/src/interactive/handoff-round.ts +41 -2
  457. package/src/interactive/interactive-application.ts +177 -7
  458. package/src/interactive/interactive-input-runtime.ts +15 -0
  459. package/src/interactive/interactive-presentation.ts +4 -0
  460. package/src/interactive/interactive-shell.ts +20 -17
  461. package/src/interactive/interactive-slash-runtime.ts +63 -10
  462. package/src/interactive/memory-overlay.ts +9 -0
  463. package/src/interactive/modal-marker.ts +170 -0
  464. package/src/interactive/mutation-preview.ts +295 -0
  465. package/src/interactive/mux-bridge.ts +214 -0
  466. package/src/interactive/overlay-frame.ts +58 -2
  467. package/src/interactive/overlay-general-openers.ts +17 -0
  468. package/src/interactive/overlay-key-routing.ts +52 -3
  469. package/src/interactive/overlay-lifecycle.ts +56 -7
  470. package/src/interactive/overlay-model-selectors.ts +40 -3
  471. package/src/interactive/overlay-permission-lifecycle.ts +112 -24
  472. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  473. package/src/interactive/overlay-transitions.ts +18 -4
  474. package/src/interactive/overlays/agents.ts +1 -0
  475. package/src/interactive/overlays/ask-user.ts +227 -49
  476. package/src/interactive/overlays/auth-dialog.ts +1 -0
  477. package/src/interactive/overlays/context-reset.ts +1 -0
  478. package/src/interactive/overlays/cwd-fallback.ts +1 -0
  479. package/src/interactive/overlays/decisions.ts +11 -11
  480. package/src/interactive/overlays/extensions.ts +1 -0
  481. package/src/interactive/overlays/fleet-run-approval.ts +1 -0
  482. package/src/interactive/overlays/handoff-review.ts +1 -0
  483. package/src/interactive/overlays/help-reference.ts +6 -0
  484. package/src/interactive/overlays/interop.ts +1 -0
  485. package/src/interactive/overlays/library-install-confirm.ts +1 -0
  486. package/src/interactive/overlays/library-tabs.ts +28 -0
  487. package/src/interactive/overlays/list-overlay.ts +10 -1
  488. package/src/interactive/overlays/message-picker.ts +1 -0
  489. package/src/interactive/overlays/model-scope.ts +86 -0
  490. package/src/interactive/overlays/model-selector.ts +1 -0
  491. package/src/interactive/overlays/prompts.ts +12 -1
  492. package/src/interactive/overlays/session-selector.ts +1 -0
  493. package/src/interactive/overlays/settings-sections.ts +30 -0
  494. package/src/interactive/overlays/settings.ts +614 -45
  495. package/src/interactive/overlays/side-question.ts +1 -0
  496. package/src/interactive/overlays/skills-hub.ts +3 -11
  497. package/src/interactive/overlays/tree-selector.ts +1 -0
  498. package/src/interactive/pane-policy.ts +46 -0
  499. package/src/interactive/panes-runtime.ts +292 -0
  500. package/src/interactive/permission-hint.ts +34 -2
  501. package/src/interactive/permission-overlay.ts +159 -9
  502. package/src/interactive/prewarm.ts +197 -0
  503. package/src/interactive/render-trace.ts +162 -15
  504. package/src/interactive/renderers/compaction-summary.ts +29 -0
  505. package/src/interactive/renderers/tool-execution.ts +4 -0
  506. package/src/interactive/renderers/worker-entry.ts +122 -14
  507. package/src/interactive/side-question.ts +58 -1
  508. package/src/interactive/slash-commands.ts +251 -15
  509. package/src/interactive/status/controller.ts +11 -0
  510. package/src/interactive/status/state-machine.ts +54 -2
  511. package/src/interactive/status/types.ts +7 -0
  512. package/src/interactive/tasks-overlay.ts +1 -0
  513. package/src/interactive/terminal-lease.ts +2 -0
  514. package/src/interactive/theme/tokens.ts +3 -14
  515. package/src/interactive/turn-context.ts +346 -33
  516. package/src/interactive/turn-persistence.ts +14 -4
  517. package/src/interactive/turn-prewarm.ts +364 -0
  518. package/src/interactive/turn-queues.ts +7 -4
  519. package/src/interactive/turn-runtime.ts +8 -1
  520. package/src/interactive/turn-state.ts +23 -0
  521. package/src/interactive/view/artifacts.ts +109 -1
  522. package/src/interactive/view/view-overlay.ts +29 -3
  523. package/src/interactive/watch-pane.ts +152 -0
  524. package/src/interactive/worker-progress.ts +7 -1
  525. package/src/interactive/worker-receipts.ts +19 -1
  526. package/src/interactive/worker-stream.ts +5 -0
  527. package/src/interactive/yazi-bridge.ts +444 -0
  528. package/src/tools/ask-user.ts +43 -2
  529. package/src/tools/bootstrap.ts +26 -2
  530. package/src/tools/builtin-tool-catalog.ts +15 -0
  531. package/src/tools/compete-worktrees.ts +83 -2
  532. package/src/tools/core-bootstrap.ts +2 -1
  533. package/src/tools/dispatch-admission.ts +14 -3
  534. package/src/tools/dispatch-arguments.ts +20 -20
  535. package/src/tools/dispatch-plan.ts +17 -9
  536. package/src/tools/dispatch-run-events.ts +134 -19
  537. package/src/tools/dispatch-runner.ts +29 -7
  538. package/src/tools/dispatch-scout.ts +1 -1
  539. package/src/tools/dispatch-types.ts +15 -3
  540. package/src/tools/dispatch.ts +1 -1
  541. package/src/tools/executables.ts +17 -14
  542. package/src/tools/observation.ts +54 -4
  543. package/src/tools/panes-surface.ts +38 -0
  544. package/src/tools/panes.ts +112 -0
  545. package/src/tools/policy.ts +10 -1
  546. package/src/tools/presentation.ts +1 -0
  547. package/src/tools/registry.ts +16 -0
  548. package/dist/chunk-AOCYTWAV.js +0 -449
  549. package/dist/chunk-HLE42MG7.js +0 -37
  550. package/dist/chunk-HWUFFB6L.js +0 -83
  551. package/dist/chunk-JOZYP4GM.js +0 -279
  552. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -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
  /**
@@ -129,6 +137,14 @@ interface QuestionState {
129
137
  customAnswer: string;
130
138
  inputValue: string;
131
139
  answer: string;
140
+ /**
141
+ * The operator's typed text exactly as submitted, empty when they typed
142
+ * nothing. `answer` is the one-line rendering of the whole decision and gets
143
+ * trimmed and joined; this is the record of what they actually typed, and
144
+ * losing it is what made an interview re-ask for the same figures three times
145
+ * (issue #228).
146
+ */
147
+ rawValue: string;
132
148
  focusedValue?: string;
133
149
  }
134
150
 
@@ -154,6 +170,7 @@ function createQuestionState(question: AskUserQuestion): QuestionState {
154
170
  customAnswer: "",
155
171
  inputValue: "",
156
172
  answer: "",
173
+ rawValue: "",
157
174
  };
158
175
  }
159
176
 
@@ -205,12 +222,18 @@ function optionIndexFromValue(value: string): number | null {
205
222
  return Number.isInteger(index) && index >= 0 ? index : null;
206
223
  }
207
224
 
208
- function answerText(question: AskUserQuestion, selected: ReadonlySet<number>, customAnswer: string): string {
209
- const parts: string[] = [];
225
+ /** The labels the operator chose, in list order. */
226
+ function selectedOptionLabels(question: AskUserQuestion, selected: ReadonlySet<number>): string[] {
227
+ const labels: string[] = [];
210
228
  for (const index of [...selected].sort((a, b) => a - b)) {
211
229
  const label = question.options?.[index]?.label;
212
- if (label) parts.push(label);
230
+ if (label) labels.push(label);
213
231
  }
232
+ return labels;
233
+ }
234
+
235
+ function answerText(question: AskUserQuestion, selected: ReadonlySet<number>, customAnswer: string): string {
236
+ const parts = selectedOptionLabels(question, selected);
214
237
  const custom = customAnswer.trim();
215
238
  if (custom.length > 0) parts.push(custom);
216
239
  return parts.join("; ");
@@ -348,6 +371,10 @@ class AskUserOverlayView implements Component {
348
371
  this.commitMultiSelectOrOpenOther(state);
349
372
  return;
350
373
  }
374
+ if (this.isAddTextKey(data)) {
375
+ this.chooseFocusedAndOpenText(question, state);
376
+ return;
377
+ }
351
378
  this.list?.handleInput(data);
352
379
  }
353
380
 
@@ -492,34 +519,40 @@ class AskUserOverlayView implements Component {
492
519
  this.presentation.requiredActions.find((action) => action.id === "record-answer")?.label.toLowerCase() ??
493
520
  "record answer";
494
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";
495
527
  return this.questions.length > 1
496
- ? this.withScrollHint([
497
- { key: "Enter", verb: recordAnswer },
498
- { key: "Alt+Left/Right", verb: "question" },
499
- ])
500
- : 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);
501
536
  }
502
537
  // The classified action label distinguishes recording an answer from
503
- // granting authority. Esc keeps the product's one word for the way out,
504
- // 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`.
540
+ // `t` is on every select footer because the operator cannot tell from a
541
+ // label whether the option needs a figure attached until they read it.
542
+ const addText: HintEntry = { key: "t", verb: "add text", short: "text" };
505
543
  if (question.multi_select === true) {
506
544
  return this.questions.length > 1
507
545
  ? this.withScrollHint([
508
546
  { key: "Left/Right", verb: "question" },
509
547
  { key: "Space", verb: "toggle" },
548
+ addText,
510
549
  { key: "Enter", verb: recordAnswer },
511
550
  ])
512
- : this.withScrollHint([
513
- { key: "Space", verb: "toggle" },
514
- { key: "Enter", verb: recordAnswer },
515
- ]);
551
+ : this.withScrollHint([{ key: "Space", verb: "toggle" }, addText, { key: "Enter", verb: recordAnswer }]);
516
552
  }
517
553
  return this.questions.length > 1
518
- ? this.withScrollHint([
519
- { key: "Left/Right", verb: "question" },
520
- { key: "Enter", verb: recordAnswer },
521
- ])
522
- : this.withScrollHint([{ key: "Enter", verb: recordAnswer }]);
554
+ ? this.withScrollHint([{ key: "Left/Right", verb: "question" }, addText, { key: "Enter", verb: recordAnswer }])
555
+ : this.withScrollHint([addText, { key: "Enter", verb: recordAnswer }]);
523
556
  }
524
557
 
525
558
  private renderDecisionContext(width: number): string[] {
@@ -541,9 +574,9 @@ class AskUserOverlayView implements Component {
541
574
  * scroll. A footer that always advertised PgUp would be advertising a key that
542
575
  * does nothing on most rounds.
543
576
  */
544
- private withScrollHint(entries: ReadonlyArray<HintEntry>): string {
545
- if (this.surface !== "interview" || !this.contentOverflows) return buildHint(entries);
546
- 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);
547
580
  }
548
581
 
549
582
  private setSurface(next: AskUserSurface): void {
@@ -644,6 +677,34 @@ class AskUserOverlayView implements Component {
644
677
  return this.questions.length > 1 && (matchesKey(data, "alt+right") || matchesKey(data, "ctrl+right"));
645
678
  }
646
679
 
680
+ /**
681
+ * The key that adds typed text to the option under the cursor.
682
+ *
683
+ * Choosing an option used to be the whole answer, so a model that offered
684
+ * "Exact number - I'll type it" gave the operator a label and nowhere to put
685
+ * the number; the interview then spent two of its four rounds asking for the
686
+ * figures again (issue #228). The list itself only reads the arrows, Enter,
687
+ * and Esc, so a letter is free here.
688
+ */
689
+ private isAddTextKey(data: string): boolean {
690
+ return matchesKey(data, "t");
691
+ }
692
+
693
+ /**
694
+ * Take the focused option and open the text field for it, without committing.
695
+ * Submitting the text records the label and the text together.
696
+ */
697
+ private chooseFocusedAndOpenText(question: AskUserQuestion, state: QuestionState): void {
698
+ const current = this.list?.getSelectedItem();
699
+ const optionIndex = current && current.value !== "other" ? optionIndexFromValue(current.value) : null;
700
+ if (optionIndex !== null) {
701
+ if (question.multi_select === true) state.selected.add(optionIndex);
702
+ else state.selected = new Set<number>([optionIndex]);
703
+ state.focusedValue = `option:${optionIndex}`;
704
+ }
705
+ this.openTextInput(state.selected.size > 0 ? "Add your answer to the chosen option" : "Other answer");
706
+ }
707
+
647
708
  /**
648
709
  * The row above the question, or nothing.
649
710
  *
@@ -739,17 +800,29 @@ class AskUserOverlayView implements Component {
739
800
  0,
740
801
  items.findIndex((item) => item.value === selectedItem.value),
741
802
  );
742
- 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));
743
818
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCount / 2), items.length - visibleCount));
744
819
  const end = Math.min(items.length, start + visibleCount);
745
- const primaryColumnWidth = this.primaryColumnWidth(items, width);
746
820
  const lines: string[] = [];
747
821
 
748
- const acceptKey = question.multi_select === true ? "Space" : "Enter";
749
822
  for (let index = start; index < end; index += 1) {
750
823
  const item = items[index];
751
824
  if (!item) continue;
752
- lines.push(this.renderOptionRow(item, index === selectedIndex, width, primaryColumnWidth, acceptKey));
825
+ lines.push(...this.renderOptionRow(item, index === selectedIndex, width, primaryColumnWidth, acceptKey));
753
826
  }
754
827
 
755
828
  if (start > 0 || end < items.length) {
@@ -764,13 +837,22 @@ class AskUserOverlayView implements Component {
764
837
  return Math.max(1, Math.min(bounded, Math.max(1, width - 4)));
765
838
  }
766
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
+ */
767
849
  private renderOptionRow(
768
850
  item: SelectItem,
769
851
  selected: boolean,
770
852
  width: number,
771
853
  primaryColumnWidth: number,
772
854
  acceptKey: string,
773
- ): string {
855
+ ): string[] {
774
856
  const theme = clioTheme();
775
857
  const prefix = selected ? theme.fg("accent", `${GLYPH.cursor} `) : " ";
776
858
  // The key that answers rides on the focused row. An operator whose eyes are
@@ -780,22 +862,40 @@ class AskUserOverlayView implements Component {
780
862
  const available = Math.max(1, width - 2 - visibleWidth(affordance));
781
863
  const description = item.description?.replace(/[\r\n]+/g, " ").trim();
782
864
 
783
- let body: string;
784
- if (description && width > 40) {
785
- const labelWidth = Math.max(1, Math.min(primaryColumnWidth - 2, available - 4));
786
- const label = truncateToWidth(item.label, labelWidth, ELLIPSIS, false);
787
- const spacing = " ".repeat(Math.max(1, primaryColumnWidth - visibleWidth(label)));
788
- const descriptionWidth = Math.max(1, available - visibleWidth(label) - visibleWidth(spacing));
789
- const desc = truncateToWidth(description, descriptionWidth, ELLIPSIS, false);
790
- body = selected
791
- ? theme.style("accent", `${label}${spacing}${desc}`, { bold: true })
792
- : `${label}${theme.fg("muted", `${spacing}${desc}`)}`;
793
- } else {
865
+ if (!description || width <= 40) {
794
866
  const label = truncateToWidth(item.label, available, ELLIPSIS, false);
795
- 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)];
796
869
  }
797
870
 
798
- 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
+ ];
799
899
  }
800
900
 
801
901
  private ensureControl(): void {
@@ -829,16 +929,22 @@ class AskUserOverlayView implements Component {
829
929
  return;
830
930
  }
831
931
  state.inputValue = answer;
932
+ // Verbatim, before any joining or trimming the display line does. The
933
+ // typed text is the answer; the one-line `answer` is a rendering of it.
934
+ state.rawValue = value;
832
935
  if (questionHasOptions(question) && question.multi_select === true) {
833
936
  state.customAnswer = answer;
834
937
  this.commitCurrentAnswer();
835
- } else {
836
- state.customAnswer = questionHasOptions(question) ? answer : "";
837
- state.answer = answer;
838
- this.finishIfCompleteOrAdvance();
938
+ return;
839
939
  }
940
+ state.customAnswer = questionHasOptions(question) ? answer : "";
941
+ // The chosen labels and the typed text compose, so a question answered
942
+ // with an option that says "I will type it" keeps both. With nothing
943
+ // selected, which is the implicit Other path, this is the text alone.
944
+ state.answer = questionHasOptions(question) ? answerText(question, state.selected, state.customAnswer) : answer;
945
+ this.finishIfCompleteOrAdvance();
840
946
  };
841
- activeInput.onEscape = () => this.cancel();
947
+ activeInput.onEscape = () => this.escapeFromTextInput(question, state);
842
948
  this.input = activeInput;
843
949
  this.list = null;
844
950
  }
@@ -866,7 +972,14 @@ class AskUserOverlayView implements Component {
866
972
  return;
867
973
  }
868
974
  state.selected = new Set<number>([optionIndex]);
869
- state.answer = question.options?.[optionIndex]?.label ?? item.label;
975
+ // Choosing a plain option is a label-only answer, so the typed text
976
+ // clears with the choice: it was given for a label the operator has
977
+ // just moved off, and the composed answer runs through the same
978
+ // `answerText` every other path uses.
979
+ state.customAnswer = "";
980
+ state.rawValue = "";
981
+ state.inputValue = "";
982
+ state.answer = answerText(question, state.selected, state.customAnswer) || item.label;
870
983
  state.focusedValue = item.value;
871
984
  this.status = "";
872
985
  this.finishIfCompleteOrAdvance();
@@ -902,6 +1015,53 @@ class AskUserOverlayView implements Component {
902
1015
  return 0;
903
1016
  }
904
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
+
905
1065
  private openTextInput(nextStatus: string): void {
906
1066
  const state = this.currentState();
907
1067
  const question = this.currentQuestion();
@@ -992,12 +1152,26 @@ class AskUserOverlayView implements Component {
992
1152
  return this.states.length > 0 && this.states.every((state) => state.answer.trim().length > 0);
993
1153
  }
994
1154
 
1155
+ /**
1156
+ * What the round captured, as three separable facts per question: the one-line
1157
+ * answer, the labels chosen, and the text typed. A reader tells a label-only
1158
+ * answer from a label-plus-value one by whether `value` is there, which the
1159
+ * joined string alone could never say.
1160
+ */
995
1161
  private answers(): AskUserResult["answers"] {
996
1162
  const answers: AskUserResult["answers"] = [];
997
1163
  for (let index = 0; index < this.questions.length; index += 1) {
998
1164
  const question = this.questions[index];
999
- const answer = this.states[index]?.answer.trim();
1000
- if (question && answer && answer.length > 0) answers.push({ question: question.question, answer });
1165
+ const state = this.states[index];
1166
+ const answer = state?.answer.trim();
1167
+ if (!question || !state || !answer || answer.length === 0) continue;
1168
+ const options = selectedOptionLabels(question, state.selected);
1169
+ answers.push({
1170
+ question: question.question,
1171
+ answer,
1172
+ ...(options.length > 0 ? { options } : {}),
1173
+ ...(state.rawValue.length > 0 ? { value: state.rawValue } : {}),
1174
+ });
1001
1175
  }
1002
1176
  return answers;
1003
1177
  }
@@ -1005,6 +1179,7 @@ class AskUserOverlayView implements Component {
1005
1179
  private goToRelativeQuestion(delta: -1 | 1): void {
1006
1180
  if (this.questions.length <= 1) return;
1007
1181
  this.syncActiveControl();
1182
+ this.parkTextMode();
1008
1183
  this.index = (this.index + delta + this.questions.length) % this.questions.length;
1009
1184
  this.status = "";
1010
1185
  this.resetContentScroll();
@@ -1056,6 +1231,9 @@ export function openAskUserOverlay(tui: TUI, deps: OpenAskUserOverlayDeps): AskU
1056
1231
  // the terminal has left after the margins.
1057
1232
  ...(geometry.maxInnerRows > 0 ? { maxHeight: geometry.maxInnerRows + ASK_USER_FRAME_ROWS } : {}),
1058
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",
1059
1237
  title: () => (view.isDecisionPending() ? view.decisionTitle() : ASK_USER_WAITING_TITLE),
1060
1238
  tone: () => (view.isDecisionPending() ? view.decisionTone() : undefined),
1061
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.",
@@ -129,6 +130,10 @@ export function openHelpOverlay(
129
130
  "auto-edit parks unrecognized commands, and full-auto runs them.",
130
131
  "**Approvals**: a parked call waits for a one-shot operator decision; approving resumes only that call. " +
131
132
  "Workers resolve asks per `workers.onPermission` (Approvals Routing); headless runs auto-deny.",
133
+ "**Inspecting a mutation**: a parked `write` or `edit` card carries the target, the byte count, and a `sha256` digest of the exact call arguments. " +
134
+ "Press `v` to read the complete proposed content, or the complete effective diff against the file on disk, and `v` again to put it away; ↑/↓ and PageUp/PageDown scroll it. " +
135
+ "Enter still allows, `s` still stops, and Esc still denies while it is open. The mutation text is shown locally and never enters the transcript, a notice, a desktop notification, or the render trace. " +
136
+ "A worker escalation has no preview because the call's arguments never leave the worker, and its card says so.",
132
137
  ],
133
138
  },
134
139
  ];
@@ -136,6 +141,7 @@ export function openHelpOverlay(
136
141
  const items = [...commands, ...keys, ...topics];
137
142
 
138
143
  return openListOverlay(tui, {
144
+ markerId: "help",
139
145
  title: "Help Center",
140
146
  items,
141
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
  });
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The Skills Hub's tab table.
3
+ *
4
+ * It sits in its own module rather than inside `skills-hub.ts` because the slash
5
+ * registry parses `/library <kind>` against it, and the registry has to stay off
6
+ * the render module graph. Pulling the whole hub overlay in for four ids made
7
+ * `src/cli/run.ts` a second, disjoint reacher of the interactive theme and
8
+ * `src/engine/tui.ts`, which splits the merged chunk the Stage 0 instant shell
9
+ * depends on. See `tests/contracts/instant-shell-import-graph.test.ts`.
10
+ *
11
+ * Pure data plus its membership test: no rendering, no overlay state.
12
+ * `skills-hub.ts` re-exports both symbols, so every existing importer keeps the
13
+ * path it had.
14
+ */
15
+
16
+ import type { LibraryEntryKind } from "../../domains/resources/index.js";
17
+
18
+ /** The hub's tabs, in the order ←/→ walks them. `skill` is the original view. */
19
+ export const LIBRARY_TABS: ReadonlyArray<{ id: LibraryEntryKind; label: string }> = [
20
+ { id: "skill", label: "Skills" },
21
+ { id: "agent", label: "Agents" },
22
+ { id: "prompt", label: "Prompts" },
23
+ { id: "fleet", label: "Fleets" },
24
+ ];
25
+
26
+ export function isLibraryTab(value: string): value is LibraryEntryKind {
27
+ return LIBRARY_TABS.some((tab) => tab.id === value);
28
+ }
@@ -742,11 +742,20 @@ export interface ListOverlayHandle extends OverlayHandle {
742
742
  activeTabId(): string;
743
743
  }
744
744
 
745
- export function openListOverlay(tui: TUI, options: ListOverlayOptions): ListOverlayHandle {
745
+ /**
746
+ * The marker id rides on the opener rather than on `ListOverlayOptions` because
747
+ * it belongs to the mounted modal, not to the view: the view renders the same
748
+ * rows whether it is a modal or a body inside something else. It is named by
749
+ * the surface rather than derived from `title`, because a tabbed overlay's
750
+ * rendered title names the active tab while the modal holding the keyboard has
751
+ * not changed hands.
752
+ */
753
+ export function openListOverlay(tui: TUI, options: ListOverlayOptions & { markerId: string }): ListOverlayHandle {
746
754
  const view = new ListOverlayView(options, () => tui.requestRender());
747
755
  const handle = showClioOverlayFrame(tui, view, {
748
756
  anchor: "center",
749
757
  width: 100,
758
+ markerId: options.markerId,
750
759
  // A function, not the string: on a tabbed overlay the title names the tab
751
760
  // and must be re-read after every switch.
752
761
  title: () => view.title(),
@@ -149,6 +149,7 @@ export function openMessagePickerOverlay(tui: TUI, deps: OpenMessagePickerOverla
149
149
  return showClioOverlayFrame(tui, box, {
150
150
  anchor: "center",
151
151
  width: MESSAGE_PICKER_OVERLAY_WIDTH,
152
+ markerId: "message-picker",
152
153
  title: "Fork",
153
154
  footerHint: rows.length > 0 ? buildHint([{ key: "Enter", verb: "select" }]) : buildHint([]),
154
155
  });