@iowarp/clio-coder 0.3.9 → 0.4.1

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 (733) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/NOTICE +33 -0
  3. package/README.md +121 -16
  4. package/dist/{acp-7LOELQFP.js → acp-ZILU3AUO.js} +19 -18
  5. package/dist/{agents-FIBG2SHA.js → agents-HYWGBGQR.js} +53 -39
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-OI4LIH2I.js → auth-N3QT7CBO.js} +24 -21
  8. package/dist/{builtins-AD25UL3C.js → builtins-UJLMOVOV.js} +6 -6
  9. package/dist/{chunk-I4HZDVNP.js → chunk-2LEAKD4C.js} +2 -3
  10. package/dist/{chunk-465CC7FK.js → chunk-2NM363SV.js} +60 -64
  11. package/dist/{chunk-4H6ULJ3H.js → chunk-2VG7KLYV.js} +1046 -1370
  12. package/dist/{chunk-SST6Z5JA.js → chunk-336NSSKI.js} +1 -1
  13. package/dist/{chunk-NZU6YDNV.js → chunk-3I5NY75V.js} +5 -5
  14. package/dist/{chunk-47CMYGET.js → chunk-3QSOM6PA.js} +2 -2
  15. package/dist/chunk-42FMPA75.js +101 -0
  16. package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
  17. package/dist/{chunk-IHKBWSXF.js → chunk-5QIAJV2D.js} +3 -3
  18. package/dist/chunk-5YHDIDBP.js +97 -0
  19. package/dist/chunk-6CCS4G3W.js +1529 -0
  20. package/dist/{chunk-2JDWVJND.js → chunk-6DWBAZ5U.js} +2 -2
  21. package/dist/{chunk-BVDVID7E.js → chunk-6FN3E6KX.js} +6 -6
  22. package/dist/{chunk-DG4M6ZUE.js → chunk-6HMJX2VU.js} +59 -59
  23. package/dist/{chunk-3MRC2YSQ.js → chunk-6I5ILFOF.js} +3 -2
  24. package/dist/chunk-6NJQITNH.js +1412 -0
  25. package/dist/chunk-6VC4OV3Z.js +365 -0
  26. package/dist/{chunk-LU7P4LHA.js → chunk-72GZI5EV.js} +2 -2
  27. package/dist/{chunk-5T7RBWN2.js → chunk-74YWRRU5.js} +9 -8
  28. package/dist/{chunk-VPTUJU4P.js → chunk-7EPLI7VL.js} +2 -2
  29. package/dist/{chunk-CA42X6KT.js → chunk-7OBGU7UB.js} +7 -7
  30. package/dist/{chunk-56KB5IJP.js → chunk-7PWAODYW.js} +3 -3
  31. package/dist/chunk-7RY5VZPH.js +573 -0
  32. package/dist/{chunk-OEDBCISO.js → chunk-ABLSQ6JX.js} +3 -3
  33. package/dist/{chunk-5HFBWUMU.js → chunk-AFKWHWXF.js} +13 -23
  34. package/dist/{chunk-FWDFM5ZU.js → chunk-AK5XEFVZ.js} +3 -3
  35. package/dist/{chunk-WXCJ7VME.js → chunk-AKB4GYDL.js} +7 -7
  36. package/dist/chunk-AP73CFDC.js +308 -0
  37. package/dist/{chunk-4LJX2PUC.js → chunk-BBTJOK6Y.js} +5 -5
  38. package/dist/{chunk-QQ7EKM72.js → chunk-BDPT6GTK.js} +13 -61
  39. package/dist/chunk-BJGUKIG4.js +302 -0
  40. package/dist/chunk-BMS5RKQY.js +27 -0
  41. package/dist/{chunk-TD7UE2L5.js → chunk-BTGG6BG2.js} +81 -40
  42. package/dist/chunk-BTK3SSSK.js +67 -0
  43. package/dist/{chunk-M6L6IDJG.js → chunk-BWH6N36D.js} +1 -1
  44. package/dist/chunk-BWW4HLO4.js +301 -0
  45. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  46. package/dist/{chunk-HJB5IUKP.js → chunk-C537JADH.js} +18 -18
  47. package/dist/{chunk-774ILSRL.js → chunk-CDNVLKUX.js} +5 -4
  48. package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
  49. package/dist/{chunk-AEYBF3TB.js → chunk-CWVRRIEI.js} +2 -2
  50. package/dist/chunk-D4CJV46V.js +462 -0
  51. package/dist/chunk-D7E7WCDF.js +27 -0
  52. package/dist/{chunk-5PVQ4SRS.js → chunk-E67WX76H.js} +10 -126
  53. package/dist/{chunk-FQ4SKYE4.js → chunk-EBY63B7G.js} +2 -5
  54. package/dist/chunk-EIMVLWB3.js +1842 -0
  55. package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
  56. package/dist/{chunk-RAPCMZL4.js → chunk-FSP7CMNU.js} +9 -1
  57. package/dist/{chunk-3DUR4WUA.js → chunk-FYUN5KZ3.js} +25 -25
  58. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  59. package/dist/chunk-GIZNH63R.js +40 -0
  60. package/dist/chunk-GPPB3JBE.js +688 -0
  61. package/dist/{chunk-B5XRQOLB.js → chunk-GVQJ5CCZ.js} +17 -11
  62. package/dist/chunk-HHV2GANA.js +88 -0
  63. package/dist/chunk-HJWWJ6IL.js +184 -0
  64. package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
  65. package/dist/chunk-HRQMFLJP.js +75 -0
  66. package/dist/{chunk-AD7Y7STJ.js → chunk-HUAS7ITX.js} +107 -97
  67. package/dist/{chunk-O6I4CIEU.js → chunk-I66EAJFY.js} +6 -6
  68. package/dist/{chunk-S4COXYBG.js → chunk-IG7BCQBA.js} +129 -106
  69. package/dist/{chunk-N3PBVRTZ.js → chunk-IXJT6DCX.js} +9 -7
  70. package/dist/chunk-J4HBWF6Y.js +2635 -0
  71. package/dist/chunk-J5LZHVIT.js +284 -0
  72. package/dist/{chunk-GAYUJ7LE.js → chunk-JA5QWE4Z.js} +544 -227
  73. package/dist/{chunk-B5CSFE7B.js → chunk-JIEGK6UF.js} +6 -174
  74. package/dist/{chunk-VKBMFOYV.js → chunk-JZWT5J3Y.js} +23 -27
  75. package/dist/chunk-K6BF4U2H.js +1785 -0
  76. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  77. package/dist/{chunk-YSEHGPCT.js → chunk-MA3H6DM5.js} +6 -6
  78. package/dist/{chunk-EBOC7MT3.js → chunk-MOPSG2X7.js} +9 -9
  79. package/dist/{chunk-KV2AOLDF.js → chunk-MQXIVJ35.js} +18 -29
  80. package/dist/{chunk-P3JGPQFL.js → chunk-N5UK64DP.js} +4 -4
  81. package/dist/chunk-NDDGJUXD.js +81 -0
  82. package/dist/chunk-NEKRRTYW.js +56 -0
  83. package/dist/{chunk-4ZG3XFUR.js → chunk-NLFAQR7Z.js} +12 -1
  84. package/dist/{chunk-VO2LKSTM.js → chunk-O42A54GG.js} +2 -2
  85. package/dist/{chunk-T3Z6VAAF.js → chunk-OJTRZGR3.js} +53 -92
  86. package/dist/{chunk-5DQRIYDZ.js → chunk-PBP4B7XR.js} +3 -3
  87. package/dist/{chunk-NQ6UCCOD.js → chunk-PJJ6MY27.js} +8 -8
  88. package/dist/chunk-PMZCIOCJ.js +25 -0
  89. package/dist/{chunk-ZGVHUX3M.js → chunk-PMZVFWSR.js} +2 -2
  90. package/dist/{chunk-7C6RYZGQ.js → chunk-PVAMAVBB.js} +5 -4
  91. package/dist/chunk-QDC3K2U3.js +262 -0
  92. package/dist/{chunk-JEQQR47K.js → chunk-QKIFBZKT.js} +173 -14
  93. package/dist/{chunk-R7LNVMCS.js → chunk-QMXC4JB7.js} +70 -78
  94. package/dist/chunk-QSNYB6ZV.js +195 -0
  95. package/dist/chunk-QTFGO774.js +644 -0
  96. package/dist/{chunk-ODFEOB4F.js → chunk-R23Z6K6I.js} +27 -9
  97. package/dist/{chunk-3UUY7R3Z.js → chunk-R32CLGZ6.js} +14 -14
  98. package/dist/chunk-R346GLFC.js +31 -0
  99. package/dist/chunk-RKSR6VSF.js +132 -0
  100. package/dist/{chunk-N5XKWMDW.js → chunk-SCYB3HA4.js} +73 -27
  101. package/dist/{chunk-PZ4I4JE2.js → chunk-SINK3QR6.js} +135 -125
  102. package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
  103. package/dist/chunk-T56WDKA5.js +183 -0
  104. package/dist/{chunk-ECUO3KDP.js → chunk-TD3PGPQA.js} +87 -26
  105. package/dist/{chunk-HKO36JWF.js → chunk-THYWACCR.js} +31 -3
  106. package/dist/{chunk-LXPJXFM5.js → chunk-TVH4ONAM.js} +18 -12
  107. package/dist/chunk-TVHHYFHE.js +255 -0
  108. package/dist/chunk-U2WB7TZS.js +271 -0
  109. package/dist/{chunk-NNNWO6F2.js → chunk-UAPGZHYC.js} +237 -33
  110. package/dist/{chunk-MIX5N5AC.js → chunk-UEDMSP56.js} +415 -160
  111. package/dist/{chunk-WIE7ZOSW.js → chunk-UXN6JT4W.js} +3 -3
  112. package/dist/{chunk-CMZWFGD2.js → chunk-VAVR4IJY.js} +19 -15
  113. package/dist/{chunk-MV2VUEJC.js → chunk-VEGN6WIQ.js} +2 -2
  114. package/dist/chunk-VKFQTNDV.js +66 -0
  115. package/dist/{chunk-FALJGAWU.js → chunk-VKRH2TCS.js} +2 -2
  116. package/dist/{chunk-A2NJGIB3.js → chunk-VN3SHNBN.js} +2 -2
  117. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  118. package/dist/{chunk-UOSL25KY.js → chunk-VXMFAE2W.js} +1 -1
  119. package/dist/{chunk-EQ63NRB7.js → chunk-WNP7O5WZ.js} +3 -3
  120. package/dist/chunk-WR6U3OVP.js +45 -0
  121. package/dist/{chunk-TEO2TLVN.js → chunk-XKA2ICR3.js} +1051 -1196
  122. package/dist/{chunk-3V5AYSEQ.js → chunk-Y45G3AXC.js} +72 -65
  123. package/dist/{chunk-J5Q24KAG.js → chunk-Y4CAGMM6.js} +2 -2
  124. package/dist/chunk-YAKGTXEV.js +17 -0
  125. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  126. package/dist/{chunk-YW7UVM5V.js → chunk-ZAZB4JMW.js} +55 -11
  127. package/dist/{chunk-HCBCAYZU.js → chunk-ZJLUDYFY.js} +11 -11
  128. package/dist/{chunk-HKIYEGME.js → chunk-ZTJUVLTU.js} +2 -2
  129. package/dist/{chunk-IFBNV6H6.js → chunk-ZW55JB7N.js} +3 -3
  130. package/dist/cli/index.js +48 -37
  131. package/dist/clio-IT3G3VQH.js +25 -0
  132. package/dist/{code-nav-LMW275PA.js → code-nav-RK6S7F6E.js} +8 -8
  133. package/dist/codewiki/build-worker.js +1 -1
  134. package/dist/{compile-cache-CVJMMODC.js → compile-cache-OQAVB7Y5.js} +3 -5
  135. package/dist/{components-ZFA3SAER.js → components-UBWCQSRW.js} +6 -6
  136. package/dist/{config-RXS5T3JT.js → config-3QZRWZJF.js} +73 -61
  137. package/dist/configure-FL7Y3KJF.js +54 -0
  138. package/dist/{context-MVOORGMF.js → context-5HE7ODYK.js} +61 -49
  139. package/dist/{context-I3BTOTCS.js → context-KYQFRVDC.js} +11 -11
  140. package/dist/{context-PALKKQYL.js → context-XNHL75JV.js} +43 -28
  141. package/dist/{context-clear-N2WOYZ2K.js → context-clear-N545L53A.js} +60 -48
  142. package/dist/{context-working-set-MIEVECVZ.js → context-working-set-QHKXSV2F.js} +15 -13
  143. package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-RGIE5PCT.js} +109 -72
  144. package/dist/{docs-7LQ23DLM.js → docs-5NAF6AU7.js} +7 -13
  145. package/dist/doctor-ZGPEGHIP.js +772 -0
  146. package/dist/{eval-IJ5VEZDJ.js → eval-GXLL44RD.js} +106 -86
  147. package/dist/eval-inventory-HBWSWQOK.js +315 -0
  148. package/dist/{evidence-L5APPXNV.js → evidence-HWLBRH3Q.js} +209 -51
  149. package/dist/{evolve-RGNKFJ52.js → evolve-FTZBMNVW.js} +57 -45
  150. package/dist/{extensions-7WYWUX5A.js → extensions-VHRBEID7.js} +7 -7
  151. package/dist/{fleet-6CNVBZZP.js → fleet-CKZHJWZJ.js} +215 -114
  152. package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-EXDXBMV6.js} +13 -12
  153. package/dist/fleet-decisions-OTHB6KRL.js +157 -0
  154. package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-YTEZUCUT.js} +20 -17
  155. package/dist/fleet-inspect-SS6YMDCK.js +445 -0
  156. package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-PBY4VYOM.js} +6 -9
  157. package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-KMEM5L3S.js} +25 -20
  158. package/dist/fleet-verify-QD5M7E7Q.js +178 -0
  159. package/dist/fleet-view-WAMJYNDT.js +94 -0
  160. package/dist/{init-VBN2ACVA.js → init-5XQRBOFV.js} +79 -66
  161. package/dist/install-NKNG7KD4.js +13 -0
  162. package/dist/interop-34TVO25M.js +116 -0
  163. package/dist/{library-JHGUMLY2.js → library-3QY6KF57.js} +25 -21
  164. package/dist/{memory-K4OQIYWG.js → memory-L4UTIIIW.js} +59 -47
  165. package/dist/{models-2NCZUWDD.js → models-ZVX3QOWE.js} +47 -37
  166. package/dist/{monitor-MMVTJABD.js → monitor-CEKVSYTS.js} +67 -57
  167. package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-77BAP6BC.js} +7633 -7699
  168. package/dist/panes-7STHOAUJ.js +103 -0
  169. package/dist/panes-SHAUIRXY.js +60 -0
  170. package/dist/{paths-DBXMZMDU.js → paths-L7LGY6RN.js} +6 -6
  171. package/dist/{reset-DD5JGOY3.js → reset-EOLM7GVE.js} +15 -14
  172. package/dist/resources-74GKTLSF.js +115 -0
  173. package/dist/{run-QEGNX7FL.js → run-HBAUJNNZ.js} +139 -98
  174. package/dist/{share-JKD3BQMW.js → share-G3APVLVP.js} +25 -21
  175. package/dist/{skills-LMQIKDOZ.js → skills-35HHUKCR.js} +37 -27
  176. package/dist/{skills-eval-I7X2774U.js → skills-eval-QN4HSHDC.js} +80 -73
  177. package/dist/skills-inventory-J357J34F.js +129 -0
  178. package/dist/slash-commands-JZZCQA32.js +79 -0
  179. package/dist/{steer-CF5TDANS.js → steer-XAVHJM22.js} +4 -4
  180. package/dist/{support-I7LOJLIF.js → support-U7QOWY26.js} +7 -7
  181. package/dist/{targets-RUSR6B5Z.js → targets-DSM6CY3M.js} +67 -59
  182. package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-JOPFUVEM.js} +15 -10
  183. package/dist/tools-MKNWVPBH.js +29 -0
  184. package/dist/{trace-ODOQIVIW.js → trace-ECQ7TIYZ.js} +194 -7
  185. package/dist/tui-primitives-K5PASC3O.js +17 -0
  186. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-N34PCTGJ.js} +6 -8
  187. package/dist/{upgrade-XANW3FXB.js → upgrade-H7TOM7YL.js} +37 -62
  188. package/dist/{usage-4H7ZRXQT.js → usage-X52N3IDJ.js} +70 -59
  189. package/dist/{verifiers-UZXNBZEB.js → verifiers-EJTVVSMA.js} +174 -14
  190. package/dist/{verify-BVKWTNDL.js → verify-YJL6XET2.js} +12 -11
  191. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-MPIFL3LL.js} +3 -4
  192. package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-4NDZTQ4B.js} +86 -75
  193. package/dist/with-panes-OBOBFIIR.js +818 -0
  194. package/dist/worker/entry.js +131 -91
  195. package/docs/README.md +3 -2
  196. package/docs/acp.md +25 -4
  197. package/docs/alcf-provider.md +1 -1
  198. package/docs/architecture.md +2 -2
  199. package/docs/artifact-versions.md +4 -1
  200. package/docs/built-in-agents.md +40 -32
  201. package/docs/capacity-and-scheduling.md +43 -5
  202. package/docs/commands-and-modes.md +39 -4
  203. package/docs/config-knobs-audit.md +3 -0
  204. package/docs/configuration-and-targets.md +138 -167
  205. package/docs/context-engine.md +1 -1
  206. package/docs/development-pipeline.md +19 -0
  207. package/docs/dispatch-typed-intent.md +385 -0
  208. package/docs/documentation-coverage.md +2 -2
  209. package/docs/documentation-guide.md +1 -1
  210. package/docs/environment-variables.md +3 -1
  211. package/docs/eval-runner.md +1 -1
  212. package/docs/evals-internal.md +1 -1
  213. package/docs/evidence-and-memory.md +2 -2
  214. package/docs/evolution.md +1 -1
  215. package/docs/exit-codes-and-output.md +3 -1
  216. package/docs/extensions-and-sharing.md +25 -1
  217. package/docs/fleet-demo-runbook.md +6 -3
  218. package/docs/fleet-dispatch.md +30 -7
  219. package/docs/installation-and-lifecycle.md +16 -3
  220. package/docs/middleware-and-components.md +1 -1
  221. package/docs/model-catalog.md +10 -1
  222. package/docs/observability.md +3 -3
  223. package/docs/proactive-memory.md +1 -1
  224. package/docs/prompt-envelope-and-tools.md +1 -1
  225. package/docs/provider-adapter-cookbook.md +1 -1
  226. package/docs/release-cut-checklist.md +164 -177
  227. package/docs/safety-model.md +2 -2
  228. package/docs/scientific-validation.md +3 -3
  229. package/docs/session-lifecycle.md +1 -1
  230. package/docs/skills-marketplace.md +1 -1
  231. package/docs/tool-usage.md +18 -8
  232. package/docs/trace-store.md +16 -11
  233. package/docs/troubleshooting.md +2 -2
  234. package/docs/tui-design.md +1 -1
  235. package/docs/worker-dispatch-mechanics.md +1 -1
  236. package/evals/README.md +5 -0
  237. package/evals/behavioral-corpus-grader.mjs +103 -0
  238. package/evals/behavioral-machinery-baseline.json +2276 -0
  239. package/evals/behavioral-machinery-driver.ts +250 -0
  240. package/evals/behavioral-machinery-support.ts +253 -0
  241. package/evals/behavioral-machinery.yaml +614 -0
  242. package/evals/behavioral-model-negative-control.yaml +68 -0
  243. package/evals/behavioral-model.yaml +216 -0
  244. package/evals/fixtures/behavioral-facts.txt +3 -0
  245. package/evals/fixtures/behavioral-main.ts +7 -0
  246. package/evals/fixtures/decoy/note.txt +2 -0
  247. package/evals/fixtures/private/credentials.txt +1 -0
  248. package/evals/tracked-metrics-baseline.yaml +229 -0
  249. package/evals/tracked-metrics-grader.mjs +129 -0
  250. package/package.json +8 -16
  251. package/skills/README.md +6 -6
  252. package/skills/coding/ast-grep/SKILL.md +8 -2
  253. package/skills/coding/coding-standards/SKILL.md +8 -2
  254. package/skills/coding/prototype/SKILL.md +8 -2
  255. package/skills/coding/tdd/SKILL.md +8 -2
  256. package/skills/context/context-handoff/SKILL.md +12 -3
  257. package/skills/context/context-handoff/evals.md +3 -3
  258. package/skills/context/context-prime/SKILL.md +8 -2
  259. package/skills/context/context-prime/evals.md +2 -2
  260. package/skills/git/file-ticket/SKILL.md +9 -4
  261. package/skills/git/file-ticket/evals.md +2 -2
  262. package/skills/git/fix-issue/SKILL.md +12 -5
  263. package/skills/git/fix-issue/evals.md +4 -4
  264. package/skills/git/resolve-merge-conflicts/SKILL.md +8 -2
  265. package/skills/git/resolve-merge-conflicts/evals.md +2 -2
  266. package/skills/git/ship/SKILL.md +9 -3
  267. package/skills/git/ship/evals.md +4 -4
  268. package/skills/git/worktree-create/SKILL.md +7 -2
  269. package/skills/git/worktree-create/evals.md +6 -6
  270. package/skills/git/worktree-merge/SKILL.md +7 -2
  271. package/skills/git/worktree-merge/evals.md +6 -6
  272. package/skills/meta/{clio-dev → clio-coder-dev}/SKILL.md +14 -8
  273. package/skills/meta/{clio-dev → clio-coder-dev}/evals.md +3 -3
  274. package/skills/meta/{clio-test → clio-coder-test}/SKILL.md +11 -5
  275. package/skills/meta/{clio-test → clio-coder-test}/evals.md +1 -1
  276. package/skills/meta/credentials/SKILL.md +9 -2
  277. package/skills/meta/find-skills/SKILL.md +8 -2
  278. package/skills/meta/herdr/SKILL.md +8 -2
  279. package/skills/meta/skill-craft/SKILL.md +21 -4
  280. package/skills/planning/architecture/SKILL.md +9 -3
  281. package/skills/planning/backlog/SKILL.md +8 -2
  282. package/skills/planning/prd/SKILL.md +8 -2
  283. package/skills/planning/product-intent/SKILL.md +8 -2
  284. package/skills/planning/tech-spec/SKILL.md +9 -3
  285. package/skills/registry.yaml +66 -66
  286. package/skills/research/arxiv-literature/SKILL.md +8 -2
  287. package/skills/research/experiment-protocol/SKILL.md +9 -2
  288. package/skills/research/scientific-debugging/SKILL.md +9 -2
  289. package/skills/research/scientific-debugging/evals.md +2 -2
  290. package/skills/research/scientific-modernization/SKILL.md +9 -2
  291. package/skills/skill-marketplace.json +257 -38
  292. package/skills/workflow/cut-it/SKILL.md +10 -2
  293. package/skills/workflow/design-council/SKILL.md +11 -2
  294. package/skills/workflow/grill-me/SKILL.md +8 -2
  295. package/skills/workflow/workflow-distiller/SKILL.md +8 -2
  296. package/src/cli/acp.ts +8 -4
  297. package/src/cli/argv.ts +25 -0
  298. package/src/cli/auth.ts +2 -2
  299. package/src/cli/bootstrap-generate.ts +5 -5
  300. package/src/cli/clio.ts +6 -1
  301. package/src/cli/config-inspect.ts +3 -3
  302. package/src/cli/configure-interop.ts +1 -1
  303. package/src/cli/configure.ts +50 -48
  304. package/src/cli/default-target.ts +1 -1
  305. package/src/cli/docs.ts +6 -6
  306. package/src/cli/doctor-naming.ts +323 -0
  307. package/src/cli/doctor-panes.ts +156 -0
  308. package/src/cli/doctor-toolchain.ts +78 -0
  309. package/src/cli/doctor.ts +34 -1
  310. package/src/cli/eval-inventory.ts +436 -0
  311. package/src/cli/eval.ts +13 -0
  312. package/src/cli/evidence-detail.ts +88 -0
  313. package/src/cli/evidence-inventory.ts +183 -0
  314. package/src/cli/evidence.ts +30 -5
  315. package/src/cli/fleet-decisions.ts +69 -0
  316. package/src/cli/fleet-inspect.ts +334 -0
  317. package/src/cli/fleet-preflight.ts +1 -1
  318. package/src/cli/fleet-verify.ts +133 -0
  319. package/src/cli/fleet-view.ts +827 -0
  320. package/src/cli/fleet.ts +154 -40
  321. package/src/cli/index.ts +14 -2
  322. package/src/cli/interop-inspect.ts +128 -0
  323. package/src/cli/interop.ts +34 -0
  324. package/src/cli/models.ts +3 -3
  325. package/src/cli/modes/print.ts +1 -1
  326. package/src/cli/panes.ts +35 -0
  327. package/src/cli/reset.ts +6 -3
  328. package/src/cli/run.ts +67 -13
  329. package/src/cli/share.ts +1 -1
  330. package/src/cli/skills-eval.ts +11 -11
  331. package/src/cli/skills-inventory.ts +185 -0
  332. package/src/cli/skills.ts +16 -13
  333. package/src/cli/targets.ts +23 -24
  334. package/src/cli/tools.ts +321 -0
  335. package/src/cli/trace-inspect.ts +252 -0
  336. package/src/cli/trace.ts +64 -2
  337. package/src/cli/uninstall.ts +2 -2
  338. package/src/cli/upgrade.ts +29 -34
  339. package/src/cli/usage.ts +2 -2
  340. package/src/cli/validate-model.ts +1 -1
  341. package/src/cli/verifiers-inspect.ts +347 -0
  342. package/src/cli/verifiers.ts +9 -0
  343. package/src/cli/wiki-generate.ts +5 -5
  344. package/src/core/bash-exec.ts +1 -1
  345. package/src/core/boot-trace.ts +3 -3
  346. package/src/core/bus-events.ts +4 -0
  347. package/src/core/bus-trace.ts +2 -2
  348. package/src/core/compile-cache.ts +1 -1
  349. package/src/core/config.ts +1070 -845
  350. package/src/core/defaults.ts +342 -463
  351. package/src/core/domain-loader.ts +6 -4
  352. package/src/core/event-bus.ts +1 -1
  353. package/src/core/file-references.ts +78 -88
  354. package/src/core/git-commit-attribution.ts +3 -9
  355. package/src/core/guardrails.ts +24 -4
  356. package/src/core/naming-compat.ts +45 -0
  357. package/src/core/naming-events.ts +62 -0
  358. package/src/core/recent-models.ts +1 -7
  359. package/src/core/residency-protection.ts +4 -8
  360. package/src/core/resolve-config-value.ts +1 -1
  361. package/src/core/response-model-id.ts +0 -14
  362. package/src/core/response-schema.ts +44 -4
  363. package/src/core/session-routing.ts +84 -74
  364. package/src/core/skill-activation.ts +18 -0
  365. package/src/core/termination.ts +4 -4
  366. package/src/core/theme-token-hex.ts +43 -0
  367. package/src/core/timers.ts +2 -12
  368. package/src/core/tool-names.ts +2 -1
  369. package/src/core/xdg.ts +1 -1
  370. package/src/domains/agents/catalog.ts +2 -12
  371. package/src/domains/agents/extension.ts +3 -3
  372. package/src/domains/agents/fleets/build-review.md +0 -3
  373. package/src/domains/agents/fleets/build-test.md +0 -3
  374. package/src/domains/agents/registry.ts +7 -7
  375. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  376. package/src/domains/agents/result-contract.ts +165 -36
  377. package/src/domains/agents/spec.ts +0 -11
  378. package/src/domains/config/classify.ts +37 -46
  379. package/src/domains/config/extension.ts +5 -5
  380. package/src/domains/config/keybindings.ts +48 -47
  381. package/src/domains/config/watcher.ts +1 -1
  382. package/src/domains/context/codewiki/indexer.ts +1 -1
  383. package/src/domains/context/extension.ts +3 -3
  384. package/src/domains/context/prompt-context.ts +1 -1
  385. package/src/domains/context/wiki/assemble.ts +2 -2
  386. package/src/domains/context/working-set/replay/metrics.ts +0 -4
  387. package/src/domains/context/working-set/replay/report.ts +1 -1
  388. package/src/domains/context/working-set/replay/synthetic.ts +2 -2
  389. package/src/domains/dispatch/active-route-planner.ts +2 -2
  390. package/src/domains/dispatch/admission-error.ts +9 -0
  391. package/src/domains/dispatch/admission.ts +22 -21
  392. package/src/domains/dispatch/agent-candidates.ts +1 -1
  393. package/src/domains/dispatch/agent-ledger-store.ts +1 -1
  394. package/src/domains/dispatch/agent-ledger.ts +1 -1
  395. package/src/domains/dispatch/assignment.ts +5 -5
  396. package/src/domains/dispatch/capacity-lease.ts +23 -6
  397. package/src/domains/dispatch/code-step.ts +1 -1
  398. package/src/domains/dispatch/contract.ts +2 -2
  399. package/src/domains/dispatch/council-topology.ts +398 -0
  400. package/src/domains/dispatch/extension.ts +287 -111
  401. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  402. package/src/domains/dispatch/fleet-plan.ts +7 -2
  403. package/src/domains/dispatch/fleet-preflight.ts +8 -17
  404. package/src/domains/dispatch/fleet-run.ts +59 -3
  405. package/src/domains/dispatch/gate-decisions.ts +36 -15
  406. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  407. package/src/domains/dispatch/gate-topology.ts +289 -0
  408. package/src/domains/dispatch/index.ts +19 -0
  409. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  410. package/src/domains/dispatch/intent.ts +85 -1
  411. package/src/domains/dispatch/joint-route-resolver.ts +1 -1
  412. package/src/domains/dispatch/path-scope.ts +0 -61
  413. package/src/domains/dispatch/placement.ts +2 -2
  414. package/src/domains/dispatch/receipt-findings.ts +1 -1
  415. package/src/domains/dispatch/receipt-integrity.ts +20 -8
  416. package/src/domains/dispatch/reservation-store.ts +24 -27
  417. package/src/domains/dispatch/route-decision.ts +1 -1
  418. package/src/domains/dispatch/route-history.ts +1 -1
  419. package/src/domains/dispatch/route-policy.ts +1 -1
  420. package/src/domains/dispatch/route-readiness.ts +0 -28
  421. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  422. package/src/domains/dispatch/run-event-journal.ts +599 -0
  423. package/src/domains/dispatch/state.ts +45 -1
  424. package/src/domains/dispatch/tool-stats.ts +1 -1
  425. package/src/domains/dispatch/transport.ts +2 -19
  426. package/src/domains/dispatch/types.ts +16 -3
  427. package/src/domains/dispatch/validation.ts +33 -8
  428. package/src/domains/dispatch/worker-protocol.ts +2 -2
  429. package/src/domains/eval/artifacts/store.ts +5 -4
  430. package/src/domains/eval/compare/compare.ts +3 -16
  431. package/src/domains/eval/compare/thresholds.ts +0 -8
  432. package/src/domains/eval/execution-provenance.ts +1 -1
  433. package/src/domains/eval/inventory.ts +113 -0
  434. package/src/domains/eval/metrics/token-stream.ts +0 -7
  435. package/src/domains/eval/report.ts +1 -1
  436. package/src/domains/eval/runner.ts +1 -1
  437. package/src/domains/eval/runners/clio-run.ts +7 -6
  438. package/src/domains/eval/runners/external-command.ts +4 -3
  439. package/src/domains/eval/schema/artifact.ts +1 -1
  440. package/src/domains/eval/schema/behavioral-metrics.ts +3 -2
  441. package/src/domains/eval/schema/behavioral.ts +7 -6
  442. package/src/domains/eval/schema/execution-envelope.ts +4 -2
  443. package/src/domains/eval/schema/naming.ts +10 -0
  444. package/src/domains/eval/schema/serving.ts +31 -0
  445. package/src/domains/eval/schema/suite.ts +2 -2
  446. package/src/domains/eval/schema/validate.ts +9 -5
  447. package/src/domains/eval/schema/verdict.ts +4 -2
  448. package/src/domains/eval/store.ts +12 -2
  449. package/src/domains/eval/suites/run.ts +12 -6
  450. package/src/domains/eval/types.ts +1 -1
  451. package/src/domains/eval/workspaces/git.ts +1 -1
  452. package/src/domains/eval/workspaces/temp-copy.ts +1 -1
  453. package/src/domains/evidence/build.ts +13 -5
  454. package/src/domains/evidence/store.ts +6 -0
  455. package/src/domains/interop/consent.ts +7 -7
  456. package/src/domains/interop/extension.ts +1 -1
  457. package/src/domains/lifecycle/doctor.ts +27 -21
  458. package/src/domains/lifecycle/migrations/2026-09-01-clio-coder-naming.ts +155 -0
  459. package/src/domains/lifecycle/migrations/2026-09-01-retire-panes-knobs.ts +59 -0
  460. package/src/domains/lifecycle/migrations/2026-09-01-settings-v2.ts +338 -0
  461. package/src/domains/lifecycle/migrations/index.ts +20 -1
  462. package/src/domains/lifecycle/naming-history.ts +153 -0
  463. package/src/domains/lifecycle/naming-mutable-state.ts +104 -0
  464. package/src/domains/lifecycle/naming-resources.ts +261 -0
  465. package/src/domains/lifecycle/naming-tool-markers.ts +139 -0
  466. package/src/domains/lifecycle/naming-yazi.ts +81 -0
  467. package/src/domains/lifecycle/upgrade-notice.ts +1 -1
  468. package/src/domains/memory/index.ts +0 -1
  469. package/src/domains/memory/task-memory-handoff.ts +6 -3
  470. package/src/domains/memory/task-memory-policy.ts +6 -10
  471. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  472. package/src/domains/middleware/index.ts +7 -0
  473. package/src/domains/middleware/marketplace-offer.ts +339 -0
  474. package/src/domains/middleware/memory-intervention.ts +33 -21
  475. package/src/domains/middleware/runtime.ts +2 -2
  476. package/src/domains/mux/contract.ts +566 -0
  477. package/src/domains/mux/detect.ts +182 -0
  478. package/src/domains/mux/dock-controller.ts +365 -0
  479. package/src/domains/mux/extension.ts +53 -0
  480. package/src/domains/mux/index.ts +116 -0
  481. package/src/domains/mux/manifest.ts +6 -0
  482. package/src/domains/mux/operations.ts +192 -0
  483. package/src/domains/mux/pane-registry.ts +90 -0
  484. package/src/domains/mux/protocol.ts +62 -0
  485. package/src/domains/mux/socket-client.ts +964 -0
  486. package/src/domains/mux/types.ts +265 -0
  487. package/src/domains/mux/viewer-command.ts +59 -0
  488. package/src/domains/mux/yazi/assets/init.lua +2 -0
  489. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  490. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  491. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  492. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  493. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  494. package/src/domains/mux/yazi/event-stream.ts +182 -0
  495. package/src/domains/mux/yazi/profile.ts +299 -0
  496. package/src/domains/mux/yazi/session.ts +251 -0
  497. package/src/domains/mux/yazi/theme.ts +30 -0
  498. package/src/domains/observability/cost.ts +21 -0
  499. package/src/domains/observability/extension.ts +1 -1
  500. package/src/domains/observability/index.ts +2 -0
  501. package/src/domains/observability/out-of-turn-usage.ts +2 -2
  502. package/src/domains/observability/projection.ts +1 -1
  503. package/src/domains/observability/trace-store.ts +63 -11
  504. package/src/domains/prompts/compiler.ts +2 -2
  505. package/src/domains/prompts/extension.ts +3 -3
  506. package/src/domains/prompts/fragments/identity/clio-worker.md +1 -1
  507. package/src/domains/prompts/fragments/operating/skills.md +25 -18
  508. package/src/domains/providers/endpoint-capacity.ts +154 -22
  509. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  510. package/src/domains/providers/extension.ts +25 -8
  511. package/src/domains/providers/index.ts +22 -0
  512. package/src/domains/providers/knowledge-base-path.ts +1 -1
  513. package/src/domains/providers/model-runtime-capabilities.ts +46 -2
  514. package/src/domains/providers/models/cloud-models/alcf.yaml +1 -1
  515. package/src/domains/providers/models/local-models/{clio-local-coding-targets.yaml → clio-coder-local-coding-targets.yaml} +4 -4
  516. package/src/domains/providers/plugins.ts +1 -1
  517. package/src/domains/providers/resolver.ts +1 -1
  518. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  519. package/src/domains/providers/runtimes/builtins.ts +2 -0
  520. package/src/domains/providers/runtimes/cloud/alcf.ts +4 -4
  521. package/src/domains/providers/runtimes/common/lmstudio-http.ts +1 -5
  522. package/src/domains/providers/runtimes/common/local-synth.ts +3 -3
  523. package/src/domains/providers/runtimes/common/probe-helpers.ts +2 -2
  524. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  525. package/src/domains/providers/runtimes/local-native/lmstudio.ts +2 -9
  526. package/src/domains/providers/runtimes/protocol/litellm.ts +285 -0
  527. package/src/domains/providers/support.ts +1 -0
  528. package/src/domains/providers/target-model-cache.ts +124 -0
  529. package/src/domains/providers/types/knowledge-base.ts +13 -0
  530. package/src/domains/providers/types/local-model-quirks.ts +2 -2
  531. package/src/domains/providers/types/target-descriptor.ts +3 -3
  532. package/src/domains/resources/extension.ts +1 -1
  533. package/src/domains/resources/index.ts +3 -0
  534. package/src/domains/resources/library.ts +6 -6
  535. package/src/domains/resources/prompts/loader.ts +1 -1
  536. package/src/domains/resources/skills/content-hash.ts +7 -6
  537. package/src/domains/resources/skills/evals.ts +1 -1
  538. package/src/domains/resources/skills/install.ts +36 -12
  539. package/src/domains/resources/skills/loader.ts +72 -13
  540. package/src/domains/resources/skills/marketplace.ts +21 -2
  541. package/src/domains/resources/skills/promotion.ts +214 -0
  542. package/src/domains/safety/action-classifier.ts +6 -0
  543. package/src/domains/safety/audit.ts +2 -2
  544. package/src/domains/safety/finish-contract.ts +2 -1
  545. package/src/domains/safety/run-effects.ts +1 -1
  546. package/src/domains/scheduling/extension.ts +4 -4
  547. package/src/domains/session/archive-readers.ts +9 -1
  548. package/src/domains/session/compaction/branch-summary.ts +4 -0
  549. package/src/domains/session/compaction/compact.ts +1 -1
  550. package/src/domains/session/compaction/tokens.ts +4 -0
  551. package/src/domains/session/context-accounting.ts +1 -1
  552. package/src/domains/session/decision-board.ts +1 -1
  553. package/src/domains/session/extension.ts +1 -1
  554. package/src/domains/session/handoff.ts +2 -2
  555. package/src/domains/session/prompt-manifest.ts +0 -5
  556. package/src/domains/session/protected-artifact-journal.ts +12 -6
  557. package/src/domains/session/retry.ts +1 -1
  558. package/src/domains/share/archive.ts +85 -36
  559. package/src/domains/toolchain/archive.ts +175 -0
  560. package/src/domains/toolchain/contract.ts +28 -0
  561. package/src/domains/toolchain/extension.ts +47 -0
  562. package/src/domains/toolchain/index.ts +39 -0
  563. package/src/domains/toolchain/install.ts +327 -0
  564. package/src/domains/toolchain/manifest.ts +8 -0
  565. package/src/domains/toolchain/paths.ts +34 -0
  566. package/src/domains/toolchain/registry.ts +265 -0
  567. package/src/domains/toolchain/remove.ts +218 -0
  568. package/src/domains/toolchain/resolve.ts +182 -0
  569. package/src/domains/toolchain/types.ts +113 -0
  570. package/src/domains/toolchain/version.ts +88 -0
  571. package/src/engine/acp/adapter.ts +3 -3
  572. package/src/engine/acp/event-mapper.ts +3 -3
  573. package/src/engine/acp/server.ts +440 -96
  574. package/src/engine/acp/tool-mediator.ts +2 -2
  575. package/src/engine/acp/types.ts +20 -2
  576. package/src/engine/alcf-oauth.ts +5 -5
  577. package/src/engine/antigravity/subprocess-runtime.ts +2 -2
  578. package/src/engine/api-registry.ts +1 -1
  579. package/src/engine/apis/llamacpp-residency.ts +1 -19
  580. package/src/engine/apis/lmstudio.ts +1 -6
  581. package/src/engine/apis/ollama-native.ts +3 -3
  582. package/src/engine/apis/openai-completions.ts +2 -2
  583. package/src/engine/apis/residency.ts +3 -9
  584. package/src/engine/claude/sdk-module.ts +87 -0
  585. package/src/engine/claude/sdk-runtime.ts +25 -17
  586. package/src/engine/claude/subprocess-runtime.ts +1 -1
  587. package/src/engine/claude/tool-safety.ts +4 -4
  588. package/src/engine/loop-guard.ts +0 -5
  589. package/src/engine/session.ts +14 -7
  590. package/src/engine/tui-primitives.ts +21 -0
  591. package/src/engine/tui.ts +1 -0
  592. package/src/engine/worker-events.ts +6 -6
  593. package/src/engine/worker-runtime.ts +16 -26
  594. package/src/entry/boot-options.ts +2 -0
  595. package/src/entry/orchestrator.ts +207 -90
  596. package/src/entry/panes-activation.ts +31 -0
  597. package/src/entry/with-panes.ts +20 -0
  598. package/src/interactive/application-controller.ts +21 -21
  599. package/src/interactive/bus-notices.ts +2 -2
  600. package/src/interactive/chat-loop.ts +8 -8
  601. package/src/interactive/chat-panel.ts +1 -1
  602. package/src/interactive/clio-editor.ts +105 -1
  603. package/src/interactive/context-activity.ts +8 -6
  604. package/src/interactive/context-meter.ts +3 -6
  605. package/src/interactive/context-overlay.ts +2 -1
  606. package/src/interactive/cost-overlay.ts +3 -1
  607. package/src/interactive/council-grid.ts +3 -5
  608. package/src/interactive/council.ts +11 -0
  609. package/src/interactive/dispatch-board.ts +56 -49
  610. package/src/interactive/editor-bash.ts +21 -0
  611. package/src/interactive/editor-steer.ts +9 -4
  612. package/src/interactive/editor-submit.ts +24 -18
  613. package/src/interactive/external-editor.ts +1 -1
  614. package/src/interactive/file-reference-completion.ts +360 -0
  615. package/src/interactive/footer/dashboard.ts +15 -17
  616. package/src/interactive/footer/notifications.ts +14 -8
  617. package/src/interactive/footer/widgets.ts +2 -44
  618. package/src/interactive/interactive-application.ts +229 -62
  619. package/src/interactive/interactive-event-projection.ts +1 -1
  620. package/src/interactive/interactive-input-runtime.ts +9 -2
  621. package/src/interactive/interactive-presentation.ts +21 -16
  622. package/src/interactive/interactive-shell.ts +2 -2
  623. package/src/interactive/interactive-slash-runtime.ts +99 -23
  624. package/src/interactive/interactive-subscriptions.ts +1 -1
  625. package/src/interactive/interactive-tickers.ts +1 -1
  626. package/src/interactive/keybinding-manager.ts +4 -4
  627. package/src/interactive/layout.ts +1 -1
  628. package/src/interactive/memory-overlay.ts +3 -2
  629. package/src/interactive/modal-marker.ts +151 -0
  630. package/src/interactive/mutation-preview.ts +1 -1
  631. package/src/interactive/mux-bridge.ts +214 -0
  632. package/src/interactive/overlay-frame.ts +62 -14
  633. package/src/interactive/overlay-general-openers.ts +7 -2
  634. package/src/interactive/overlay-key-routing.ts +21 -10
  635. package/src/interactive/overlay-lifecycle.ts +20 -4
  636. package/src/interactive/overlay-model-selectors.ts +43 -6
  637. package/src/interactive/overlay-permission-lifecycle.ts +90 -22
  638. package/src/interactive/overlay-transitions.ts +18 -4
  639. package/src/interactive/overlays/agents.ts +3 -2
  640. package/src/interactive/overlays/ask-user.ts +161 -41
  641. package/src/interactive/overlays/auth-dialog.ts +1 -0
  642. package/src/interactive/overlays/context-reset.ts +29 -4
  643. package/src/interactive/overlays/cwd-fallback.ts +29 -4
  644. package/src/interactive/overlays/decisions.ts +15 -13
  645. package/src/interactive/overlays/extensions.ts +1 -0
  646. package/src/interactive/overlays/fleet-run-approval.ts +4 -3
  647. package/src/interactive/overlays/handoff-review.ts +3 -2
  648. package/src/interactive/overlays/help-reference.ts +2 -0
  649. package/src/interactive/overlays/interop.ts +2 -1
  650. package/src/interactive/overlays/library-install-confirm.ts +2 -1
  651. package/src/interactive/overlays/library-tabs.ts +28 -0
  652. package/src/interactive/overlays/list-overlay.ts +10 -1
  653. package/src/interactive/overlays/message-picker.ts +2 -1
  654. package/src/interactive/overlays/model-scope.ts +86 -0
  655. package/src/interactive/overlays/model-selector.ts +40 -31
  656. package/src/interactive/overlays/prompts.ts +11 -4
  657. package/src/interactive/overlays/session-selector.ts +3 -2
  658. package/src/interactive/overlays/settings-sections.ts +30 -0
  659. package/src/interactive/overlays/settings.ts +849 -268
  660. package/src/interactive/overlays/side-question.ts +3 -2
  661. package/src/interactive/overlays/skills-hub.ts +11 -19
  662. package/src/interactive/overlays/tree-selector.ts +3 -2
  663. package/src/interactive/pane-policy.ts +46 -0
  664. package/src/interactive/panes-runtime.ts +318 -0
  665. package/src/interactive/permission-overlay.ts +2 -2
  666. package/src/interactive/renderers/compaction-summary.ts +29 -0
  667. package/src/interactive/renderers/retry-status.ts +12 -4
  668. package/src/interactive/renderers/tool-execution.ts +4 -4
  669. package/src/interactive/renderers/worker-entry.ts +122 -14
  670. package/src/interactive/session-transcript.ts +3 -3
  671. package/src/interactive/slash-autocomplete.ts +454 -165
  672. package/src/interactive/slash-commands.ts +521 -189
  673. package/src/interactive/slash-spec.ts +160 -17
  674. package/src/interactive/status/controller.ts +2 -2
  675. package/src/interactive/stream-pacing-policy.ts +1 -1
  676. package/src/interactive/tasks-overlay.ts +21 -13
  677. package/src/interactive/terminal-lease.ts +6 -7
  678. package/src/interactive/theme/tokens.ts +3 -14
  679. package/src/interactive/turn-context.ts +58 -13
  680. package/src/interactive/turn-persistence.ts +2 -2
  681. package/src/interactive/turn-prewarm.ts +2 -2
  682. package/src/interactive/turn-runtime.ts +8 -8
  683. package/src/interactive/turn-state.ts +1 -1
  684. package/src/interactive/view/artifacts.ts +120 -9
  685. package/src/interactive/view/view-overlay.ts +14 -10
  686. package/src/interactive/watch-pane.ts +180 -0
  687. package/src/interactive/welcome-dashboard.ts +15 -15
  688. package/src/interactive/worker-progress.ts +11 -5
  689. package/src/interactive/worker-receipts.ts +20 -2
  690. package/src/interactive/worker-stream.ts +6 -4
  691. package/src/interactive/yazi-bridge.ts +451 -0
  692. package/src/tools/ask-user.ts +3 -3
  693. package/src/tools/bash.ts +1 -1
  694. package/src/tools/bootstrap.ts +26 -2
  695. package/src/tools/builtin-tool-catalog.ts +16 -1
  696. package/src/tools/compete-worktrees.ts +108 -17
  697. package/src/tools/context/index.ts +15 -1
  698. package/src/tools/core-bootstrap.ts +2 -1
  699. package/src/tools/dispatch-admission.ts +18 -5
  700. package/src/tools/dispatch-arguments.ts +21 -21
  701. package/src/tools/dispatch-plan.ts +0 -11
  702. package/src/tools/dispatch-run-events.ts +145 -28
  703. package/src/tools/dispatch-runner.ts +40 -13
  704. package/src/tools/dispatch-types.ts +15 -3
  705. package/src/tools/dispatch.ts +2 -2
  706. package/src/tools/executables.ts +17 -14
  707. package/src/tools/find.ts +1 -6
  708. package/src/tools/monitor.ts +4 -4
  709. package/src/tools/observation.ts +54 -4
  710. package/src/tools/observers.ts +0 -33
  711. package/src/tools/panes-surface.ts +38 -0
  712. package/src/tools/panes.ts +112 -0
  713. package/src/tools/policy.ts +10 -1
  714. package/src/tools/presentation.ts +1 -0
  715. package/src/tools/task-worktree.ts +28 -7
  716. package/src/tools/verify/authoring.ts +3 -6
  717. package/src/tools/verify/frontend.ts +1 -1
  718. package/src/tools/web-fetch.ts +1 -1
  719. package/src/worker/entry.ts +4 -3
  720. package/src/worker/protocol.ts +16 -14
  721. package/src/worker/spec-contract.ts +2 -2
  722. package/src/worker/stdin-demux.ts +2 -2
  723. package/dist/chunk-D73KXYPF.js +0 -37
  724. package/dist/chunk-MLOK6ZOS.js +0 -2888
  725. package/dist/chunk-PNY46YEY.js +0 -1581
  726. package/dist/chunk-XDOQXGFO.js +0 -515
  727. package/dist/chunk-YKOFT37S.js +0 -279
  728. package/dist/clio-LT5V7SSZ.js +0 -26
  729. package/dist/configure-2WYWSCSD.js +0 -49
  730. package/dist/doctor-TWBWFK5V.js +0 -165
  731. package/src/engine/apis/degraded-inference.ts +0 -95
  732. /package/skills/meta/{clio-test → clio-coder-test}/references/harness.md +0 -0
  733. /package/skills/meta/{clio-test → clio-coder-test}/references/test-map.md +0 -0
@@ -3,10 +3,19 @@ import {
3
3
  type ClioSettings,
4
4
  removeFleetProfileFromSettings,
5
5
  removeTargetFromSettings,
6
+ SETTINGS_V1_PATH_MOVES,
6
7
  setFleetProfileInSettings,
7
8
  useTargetInSettings,
8
9
  } from "../../core/config.js";
9
- import { DEFAULT_SETTINGS, THINKING_LEVELS } from "../../core/defaults.js";
10
+ import {
11
+ ACTIVE_ROUTING_POSTURES,
12
+ ACTIVE_ROUTING_ROLES,
13
+ type ActiveRoutingPosture,
14
+ type ActiveRoutingRole,
15
+ DEFAULT_SETTINGS,
16
+ THINKING_LEVELS,
17
+ type WorkerEscalationSettings,
18
+ } from "../../core/defaults.js";
10
19
  import { getAtPath, isRoutingPath } from "../../core/session-routing.js";
11
20
  import { MAX_TIMER_DELAY_MS } from "../../core/timers.js";
12
21
  import { capacityLeaseUsage } from "../../domains/dispatch/capacity-lease.js";
@@ -48,14 +57,15 @@ import { clockLocal } from "../format-time.js";
48
57
  import { buildHint, DEFAULT_SELECT_THEME, showClioOverlayFrame } from "../overlay-frame.js";
49
58
  import { barSep, clioTheme, GLYPH, padAnsi, rule, screenTitle } from "../theme/index.js";
50
59
  import { modelsForTarget } from "./model-selector.js";
60
+ import { SETTINGS_SECTIONS, type SettingsSectionId } from "./settings-sections.js";
51
61
 
52
62
  export const SETTINGS_OVERLAY_WIDTH = "100%";
53
63
  export const SETTINGS_OVERLAY_MAX_HEIGHT = "100%";
54
64
  export const SETTINGS_OVERLAY_MARGIN = {
55
65
  top: 1,
56
- right: 2,
66
+ right: 0,
57
67
  bottom: 1,
58
- left: 2,
68
+ left: 0,
59
69
  } as const;
60
70
 
61
71
  const SECTION_LANE_WIDTH = 24;
@@ -80,19 +90,19 @@ const ULTRAWIDE_LAYOUT_MIN_WIDTH = 112;
80
90
  */
81
91
  const WIDE_LAYOUT_MIN_WIDTH = 72;
82
92
  const DROP_PATH_COLUMN_WIDTH = 52;
83
- /**
84
- * Terminal width below which Settings drops its two-cell side margins.
85
- *
86
- * The engine composites an overlay only across the columns its region covers,
87
- * and a left/right margin shrinks that region, so at 40 columns four of them
88
- * kept showing the transcript beside a modal that owns the keyboard. Above this
89
- * width the margins are cheap and the box reads better inset; below it the
90
- * overlay claims every terminal column instead.
91
- */
92
- const ULTRA_NARROW_TERMINAL_WIDTH = 60;
93
93
  /** Shown when no runtime is resolvable, so it offers the full vocabulary. */
94
94
  const FALLBACK_THINKING_VALUES: ReadonlyArray<string> = THINKING_LEVELS;
95
95
  const ROW_GAP = " ";
96
+ /**
97
+ * `workers.escalation` and `workers.resilienceCooldownMs` are optional keys, so
98
+ * the row that shows them and the mutation that writes them both need a
99
+ * definite shipped value to fall back to.
100
+ */
101
+ const SHIPPED_ESCALATION: WorkerEscalationSettings = DEFAULT_SETTINGS.fleet.permissions.escalation ?? {
102
+ timeoutMs: 120_000,
103
+ fallback: "deny",
104
+ };
105
+ const SHIPPED_RESILIENCE_COOLDOWN_MS = DEFAULT_SETTINGS.fleet.retry.routeCooldownMs ?? 15_000;
96
106
  /**
97
107
  * The product's cut marker, as the help center and every list overlay use it.
98
108
  *
@@ -126,36 +136,27 @@ const RESTART_REQUIRED_IDS = new Set<string>([
126
136
  "runtimePlugins",
127
137
  "terminal.tuiMode",
128
138
  "terminal.fullscreenScrollbar",
139
+ // The mux domain runs its detection ladder once, at boot, from the value this
140
+ // key held then. Changing the rung mid-session would leave the contract on
141
+ // the rung it resolved to, so the row says restart rather than lying.
142
+ "panes.enabled",
129
143
  ]);
130
144
 
131
- export const SETTINGS_SECTIONS = [
132
- { id: "safety", label: "Autonomy & Safety", group: "CORE" },
133
- { id: "orchestrator", label: "Orchestrator", group: "CORE" },
134
- { id: "fleet", label: "Fleet", group: "ROUTING" },
135
- { id: "targets", label: "Targets", group: "ROUTING" },
136
- { id: "models", label: "Models", group: "ROUTING" },
137
- { id: "budget", label: "Budget", group: "RUNTIME" },
138
- { id: "compaction", label: "Compaction", group: "RUNTIME" },
139
- { id: "retry", label: "Retry", group: "RUNTIME" },
140
- { id: "terminal", label: "Terminal", group: "EXPERIENCE" },
141
- { id: "watchdog", label: "Watchdog", group: "EXPERIENCE" },
142
- { id: "advanced", label: "Advanced", group: "EXPERIENCE" },
143
- ] as const;
144
-
145
- export type SettingsSectionId = (typeof SETTINGS_SECTIONS)[number]["id"];
145
+ export { SETTINGS_SECTIONS, type SettingsSectionId };
146
146
 
147
147
  const SETTINGS_SECTION_DESCRIPTIONS = {
148
- safety: "How freely Clio acts, and how delegated agents' tools are governed.",
149
- orchestrator: "Interactive chat routing and the optional proactive-memory model plane.",
150
- fleet: "Defaults, profiles, and agent bindings applied to dispatched workers, and where they run.",
148
+ safety: "How freely Clio acts, how a worker's approval ask resolves, and how delegated agents' tools are governed.",
149
+ orchestrator: "Interactive chat routing, prompt pre-warm, and the optional proactive-memory model plane.",
150
+ fleet: "Defaults, profiles, agent bindings, and route activation for dispatched workers, and where they run.",
151
151
  targets: "Configured inference targets: which one chat and the fleet use, and whether each answers.",
152
152
  models: "The /model picker, favorites, and Alt+J / Alt+K cycling.",
153
- budget: "Cost ceiling, per-turn output budget, and worker concurrency.",
154
- compaction: "When and how the context window is summarized under pressure.",
155
- retry: "Automatic recovery from transient provider and network errors.",
153
+ budget: "Cost ceiling, per-turn output budget, worker concurrency, and the guardrail backstops.",
154
+ compaction: "When and how the context window is evicted and summarized under pressure.",
155
+ retry: "Automatic recovery from transient provider, network, and stalled-stream errors.",
156
156
  terminal: "Terminal integration and the Clio color palette.",
157
157
  watchdog: "The opt-in turn-end verifier run, where it is routed, and whether it also fires mid-turn.",
158
- advanced: "Commit provenance, runtime plugins, delegation timeouts, and links to other surfaces.",
158
+ advanced:
159
+ "Commit provenance, runtime plugins, delegation timeouts, the resource library, and links to other surfaces.",
159
160
  } as const satisfies Record<SettingsSectionId, string>;
160
161
 
161
162
  export const SETTINGS_LABELS_BY_ID = {
@@ -165,6 +166,8 @@ export const SETTINGS_LABELS_BY_ID = {
165
166
  // section headed Fleet whose rows read "Worker profiles" and "Worker
166
167
  // retries" made one setting look like two subsystems.
167
168
  "workers.onPermission": "Fleet approvals routing",
169
+ "workers.escalation.timeoutMs": "Escalation timeout (ms)",
170
+ "workers.escalation.fallback": "Escalation fallback",
168
171
  "delegation.defaults.toolGovernance": "Delegation governance",
169
172
  "skills.trustProjectCompatRoots": "Trust project skill roots",
170
173
  "attribution.gitCommits": "Clio commit provenance",
@@ -174,31 +177,56 @@ export const SETTINGS_LABELS_BY_ID = {
174
177
  "orchestrator.model": "Model",
175
178
  "background.target": "Memory target",
176
179
  "background.model": "Memory model",
177
- "background.thinkingLevel": "Memory thinking level",
178
180
  "memory.intervention.enabled": "Proactive memory",
179
181
  "memory.intervention.everyNTools": "Memory cadence (tools)",
180
182
  "memory.intervention.windowSteps": "Memory trajectory steps",
181
183
  "memory.intervention.maxTokens": "Memory reminder tokens",
182
184
  "memory.intervention.timeoutMs": "Memory timeout (ms)",
185
+ "prewarm.enabled": "Prompt pre-warm",
183
186
  "workers.default.target": "Default target",
184
187
  "workers.default.model": "Default model",
185
188
  "workers.default.thinkingLevel": "Default thinking level",
186
189
  "workers.profiles": "Add profile",
187
190
  "workers.agentBindings": "Add agent route",
188
191
  "workers.maxRetries": "Fleet retries",
192
+ "workers.resilienceCooldownMs": "Resilience cooldown (ms)",
193
+ "routing.activeRoles": "Active routing roles",
194
+ "routing.activePostures": "Active routing postures",
195
+ "routing.agentAutomation.activeAgentRoles": "Active agent routes",
196
+ "panes.enabled": "Panes",
197
+ "panes.notifications": "Pane notifications",
198
+ "panes.layout": "Boot layout",
199
+ "panes.workers.ratio": "Workers dock share",
200
+ "panes.journal": "Run event journal",
201
+ "panes.yazi.enabled": "Files pane",
202
+ "panes.yazi.mode": "Files pane mode",
203
+ "panes.yazi.profile": "Yazi profile",
204
+ "panes.yazi.followCwd": "Follow conversation cwd",
205
+ "panes.yazi.ratio": "Files dock share",
189
206
  scope: "Model cycle set",
190
207
  "modelSelector.recentLimit": "Recent models kept",
191
208
  "modelSelector.favorites": "Pinned favorites",
192
209
  "budget.sessionCeilingUsd": "Session ceiling (USD)",
193
210
  "defaults.maxTokens": "Output budget (tokens)",
194
211
  "budget.concurrency": "Fleet concurrency",
212
+ "guardrails.turnToolCallBudget": "Turn tool-call budget",
213
+ "guardrails.workerToolCallCap": "Worker tool-call cap",
214
+ "guardrails.maxDispatchRuns": "Run ledger retention",
215
+ "guardrails.readMaxBytes": "Read byte cap",
216
+ "guardrails.observationTurnBudgetBytes": "Observation byte pool",
217
+ "guardrails.internalDispatchTimeoutMs": "Internal dispatch timeout (ms)",
195
218
  "compaction.auto": "Auto-compact",
196
- "compaction.excludeLastTurns": "Protected recent turns",
197
219
  "compaction.threshold": "Compaction threshold",
220
+ "context.workingSet.enabled": "Working-set eviction",
221
+ "context.workingSet.policy": "Eviction policy",
222
+ "context.workingSet.target": "Eviction target pressure",
223
+ "context.workingSet.protectLastTurns": "Turns protected from eviction",
224
+ "context.workingSet.minEvictableTokens": "Minimum evictable tokens",
198
225
  "retry.enabled": "Retry transient errors",
199
226
  "retry.maxRetries": "Max retries",
200
227
  "retry.baseDelayMs": "Base delay (ms)",
201
228
  "retry.maxDelayMs": "Max delay (ms)",
229
+ "retry.streamStallMs": "Stream stall timeout (ms)",
202
230
  "terminal.showTerminalProgress": "Terminal progress badges",
203
231
  "terminal.outputVerbosity": "Output detail",
204
232
  "terminal.tuiMode": "TUI mode",
@@ -208,7 +236,6 @@ export const SETTINGS_LABELS_BY_ID = {
208
236
  "watchdog.enabled": "Turn-end watchdog",
209
237
  "watchdog.target": "Watchdog target",
210
238
  "watchdog.cadenceToolCalls": "Watchdog cadence (tools)",
211
- theme: "Theme",
212
239
  runtimePlugins: "Runtime plugins",
213
240
  "compaction.model": "Compaction model",
214
241
  "compaction.systemPrompt": "Compaction prompt",
@@ -218,6 +245,10 @@ export const SETTINGS_LABELS_BY_ID = {
218
245
  targets: "Configured targets",
219
246
  keybindings: "Keybinding overrides",
220
247
  "delegation.agents": "Delegation agents",
248
+ "library.catalog": "Library catalog path",
249
+ "library.remote": "Library remote",
250
+ "library.confirmedRemote": "Confirmed library remote",
251
+ "library.sync": "Library sync",
221
252
  } as const;
222
253
 
223
254
  /**
@@ -231,9 +262,20 @@ type EntrySettingId =
231
262
  | `fleet.nodes.${string}`
232
263
  | `fleet.endpoints.${string}`;
233
264
  export type EditableSettingId = keyof typeof SETTINGS_LABELS_BY_ID | EntrySettingId;
234
- type FleetGroupHeaderId = `fleet.group.${"defaults" | "profiles" | "agent-routes" | "placement"}`;
265
+ type FleetGroupHeaderId =
266
+ `fleet.group.${"defaults" | "profiles" | "agent-routes" | "route-activation" | "placement" | "panes"}`;
267
+ type TerminalGroupHeaderId = "terminal.group.files-pane";
268
+ type BudgetGroupHeaderId = "budget.group.guardrails";
269
+ type CompactionGroupHeaderId = "compaction.group.working-set";
270
+ type AdvancedGroupHeaderId = "advanced.group.library";
271
+ type GroupHeaderId =
272
+ | FleetGroupHeaderId
273
+ | TerminalGroupHeaderId
274
+ | BudgetGroupHeaderId
275
+ | CompactionGroupHeaderId
276
+ | AdvancedGroupHeaderId;
235
277
  type TargetsCtaId = "targets.add-cta";
236
- export type SettingsCenterRowId = EditableSettingId | FleetGroupHeaderId | TargetsCtaId;
278
+ export type SettingsCenterRowId = EditableSettingId | GroupHeaderId | TargetsCtaId;
237
279
  const REMOVE_PROFILE_CHOICE = "(remove profile)";
238
280
  const UNBIND_CHOICE = "(unbind)";
239
281
  const AUTO_PLACEMENT_CHOICE = "(auto placement)";
@@ -244,6 +286,8 @@ export const SETTINGS_SECTION_ROWS = {
244
286
  safety: [
245
287
  "autonomy",
246
288
  "workers.onPermission",
289
+ "workers.escalation.timeoutMs",
290
+ "workers.escalation.fallback",
247
291
  "delegation.defaults.toolGovernance",
248
292
  "skills.trustProjectCompatRoots",
249
293
  "safetyNet",
@@ -254,25 +298,52 @@ export const SETTINGS_SECTION_ROWS = {
254
298
  "orchestrator.model",
255
299
  "background.target",
256
300
  "background.model",
257
- "background.thinkingLevel",
258
301
  "memory.intervention.enabled",
259
302
  "memory.intervention.everyNTools",
260
303
  "memory.intervention.windowSteps",
261
304
  "memory.intervention.maxTokens",
262
305
  "memory.intervention.timeoutMs",
306
+ "prewarm.enabled",
263
307
  ],
264
308
  fleet: [
265
309
  "workers.default.target",
266
310
  "workers.default.model",
267
311
  "workers.default.thinkingLevel",
268
312
  "workers.maxRetries",
313
+ "workers.resilienceCooldownMs",
269
314
  "workers.profiles",
270
315
  "workers.agentBindings",
316
+ "routing.activeRoles",
317
+ "routing.activePostures",
318
+ "routing.agentAutomation.activeAgentRoles",
319
+ "panes.enabled",
320
+ "panes.notifications",
321
+ "panes.layout",
322
+ "panes.workers.ratio",
323
+ "panes.journal",
271
324
  ],
272
325
  models: ["scope", "modelSelector.recentLimit", "modelSelector.favorites"],
273
- budget: ["budget.sessionCeilingUsd", "defaults.maxTokens", "budget.concurrency"],
274
- compaction: ["compaction.auto", "compaction.threshold", "compaction.excludeLastTurns"],
275
- retry: ["retry.enabled", "retry.maxRetries", "retry.baseDelayMs", "retry.maxDelayMs"],
326
+ budget: [
327
+ "budget.sessionCeilingUsd",
328
+ "defaults.maxTokens",
329
+ "budget.concurrency",
330
+ "guardrails.turnToolCallBudget",
331
+ "guardrails.workerToolCallCap",
332
+ "guardrails.maxDispatchRuns",
333
+ "guardrails.readMaxBytes",
334
+ "guardrails.observationTurnBudgetBytes",
335
+ "guardrails.internalDispatchTimeoutMs",
336
+ ],
337
+ compaction: [
338
+ "compaction.auto",
339
+ "compaction.threshold",
340
+ "context.workingSet.enabled",
341
+ "context.workingSet.policy",
342
+ "context.workingSet.target",
343
+ "context.workingSet.protectLastTurns",
344
+ "context.workingSet.minEvictableTokens",
345
+ ],
346
+ retry: ["retry.enabled", "retry.maxRetries", "retry.baseDelayMs", "retry.maxDelayMs", "retry.streamStallMs"],
276
347
  terminal: [
277
348
  "terminal.showTerminalProgress",
278
349
  "terminal.outputVerbosity",
@@ -280,7 +351,11 @@ export const SETTINGS_SECTION_ROWS = {
280
351
  "terminal.fullscreenScrollbar",
281
352
  "terminal.smoothStreaming",
282
353
  "terminal.notify",
283
- "theme",
354
+ "panes.yazi.enabled",
355
+ "panes.yazi.mode",
356
+ "panes.yazi.profile",
357
+ "panes.yazi.followCwd",
358
+ "panes.yazi.ratio",
284
359
  ],
285
360
  watchdog: ["watchdog.enabled", "watchdog.target", "watchdog.cadenceToolCalls"],
286
361
  advanced: [
@@ -293,6 +368,10 @@ export const SETTINGS_SECTION_ROWS = {
293
368
  "delegation.defaults.permissionTimeoutMs",
294
369
  "keybindings",
295
370
  "delegation.agents",
371
+ "library.catalog",
372
+ "library.remote",
373
+ "library.confirmedRemote",
374
+ "library.sync",
296
375
  ],
297
376
  targets: ["targets"],
298
377
  } as const satisfies Record<SettingsSectionId, readonly EditableSettingId[]>;
@@ -301,6 +380,8 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
301
380
  autonomy: "How freely Clio acts; the safety net always applies.",
302
381
  "workers.onPermission":
303
382
  "How a worker resolves an approval ask: deny the call, fail the run, or escalate to this session.",
383
+ "workers.escalation.timeoutMs": "How long an escalated worker approval waits for you before the fallback applies.",
384
+ "workers.escalation.fallback": "What an escalated approval becomes when nobody answers inside the timeout.",
304
385
  "delegation.defaults.toolGovernance": "Tool policy for delegated external agents.",
305
386
  "skills.trustProjectCompatRoots": "Whether third-party project skill roots are loaded.",
306
387
  "attribution.gitCommits":
@@ -311,31 +392,58 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
311
392
  "orchestrator.model": "Active chat wire model id.",
312
393
  "background.target": "Optional target for LLM memory steps; unset keeps rules-only memory.",
313
394
  "background.model": "Small non-reasoning model used only for task memory steps.",
314
- "background.thinkingLevel": "Unused: memory steps always request thinking off.",
315
395
  "memory.intervention.enabled": "Master switch for rules-only and model-backed task memory.",
316
396
  "memory.intervention.everyNTools": "Maximum tool executions between prompted memory steps.",
317
397
  "memory.intervention.windowSteps": "Recent completed tool steps visible to the memory policy.",
318
398
  "memory.intervention.maxTokens": "Hard cap for one visible memory reminder.",
319
399
  "memory.intervention.timeoutMs": "Hard deadline for one background-model memory call.",
400
+ "prewarm.enabled": "Send the next turn's known prefix ahead of time so a local server has already prefilled it.",
320
401
  "workers.default.target": "Default /run target id.",
321
402
  "workers.default.model": "Default /run wire model id.",
322
403
  "workers.default.thinkingLevel": "Reasoning budget for dispatched workers.",
323
404
  "workers.profiles": "Named target/model/thinking choices that native workers can use. Enter adds one.",
324
405
  "workers.agentBindings": "Pins native Clio agents, including shadow agents, to worker profiles. Enter adds one.",
325
406
  "workers.maxRetries": "Automatic retries for a retryable worker outcome.",
407
+ "workers.resilienceCooldownMs":
408
+ "How long a failing target, runtime, and model route is skipped before it is tried again.",
409
+ "routing.activeRoles": "Execution roles whose joint route selection may act instead of only shadowing.",
410
+ "routing.activePostures": "Route postures whose selection may act instead of only shadowing.",
411
+ "routing.agentAutomation.activeAgentRoles": "Exact agent and execution-role pairs whose agent choice may act.",
412
+ "panes.enabled": "Default panes activation for new sessions; `--with-panes` / `--no-panes` beat it.",
413
+ "panes.notifications": "Which terminal run states raise a pane-host toast.",
414
+ "panes.layout": "What composes itself at interactive boot: nothing, the workers dock, or workers plus files.",
415
+ "panes.workers.ratio": "Share of the width the workers dock takes, at most half.",
416
+ "panes.journal": "Whether every dispatched run's event tail is written to disk for `clio-coder fleet view`.",
417
+ "panes.yazi.enabled": "Whether `/panes open yazi` may open the files pane or one-shot chooser.",
418
+ "panes.yazi.mode": "Keep Yazi beside the conversation, or close it after one selection.",
419
+ "panes.yazi.profile": "Use Clio's managed pick profile or leave the operator's Yazi profile untouched.",
420
+ "panes.yazi.followCwd": "Push the conversation directory into an already-open companion pane.",
421
+ "panes.yazi.ratio": "Share of the height the files dock takes, at most half.",
326
422
  scope: "Alt+J and Alt+K model cycle set.",
327
423
  "modelSelector.recentLimit": "How many recently used models /model remembers.",
328
424
  "modelSelector.favorites": "Exact target/model refs pinned in /model.",
329
425
  "budget.sessionCeilingUsd": "Per-session cost cap.",
330
426
  "defaults.maxTokens": "Output tokens requested per turn, applied to every target.",
331
427
  "budget.concurrency": "Parallel workers allowed during dispatch.",
428
+ "guardrails.turnToolCallBudget": "Soft per-turn tool-call budget for this chat loop.",
429
+ "guardrails.workerToolCallCap": "Lifetime ceiling on tool calls one dispatched worker may execute.",
430
+ "guardrails.maxDispatchRuns": "How many finished runs the dispatch ledger keeps before the oldest are dropped.",
431
+ "guardrails.readMaxBytes": "Per-call byte cap for the read tool.",
432
+ "guardrails.observationTurnBudgetBytes": "Shared per-turn byte pool across every observation-producing tool.",
433
+ "guardrails.internalDispatchTimeoutMs": "Wall-clock cap for one internal generator dispatch.",
332
434
  "compaction.auto": "Auto-compact before a turn when context crosses the threshold.",
333
- "compaction.excludeLastTurns": "Recent user turns protected from observation masking.",
334
435
  "compaction.threshold": "Pressure at which compaction masks stale observations, then summarizes.",
436
+ "context.workingSet.enabled": "Non-destructive eviction of stale tool results before a summary is ever needed.",
437
+ "context.workingSet.policy": "Which candidates the eviction pass selects.",
438
+ "context.workingSet.target": "Context pressure an applied eviction batch brings the session down to.",
439
+ "context.workingSet.protectLastTurns": "Recent user turns whose observations are never evicted.",
440
+ "context.workingSet.minEvictableTokens":
441
+ "Results below this token estimate stay; the marker would cost more than it saves.",
335
442
  "retry.enabled": "Retry transient provider errors on the next submit.",
336
443
  "retry.maxRetries": "Retry attempts after the initial failure.",
337
444
  "retry.baseDelayMs": "Initial retry delay in milliseconds.",
338
445
  "retry.maxDelayMs": "Maximum retry delay in milliseconds.",
446
+ "retry.streamStallMs": "Silence on an in-flight stream past this long is treated as a wedged backend.",
339
447
  "terminal.showTerminalProgress": "Emit OSC 9;4 progress badges during agent turns.",
340
448
  "terminal.outputVerbosity": "How much reasoning, tool input, and live tool output appears in the transcript.",
341
449
  "terminal.tuiMode": "Use regular terminal scrollback or a fullscreen transcript with a sticky composer and footer.",
@@ -346,7 +454,6 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
346
454
  "watchdog.enabled": "When enabled, a turn that changed the tree is reviewed by one read-only verifier run.",
347
455
  "watchdog.target": "Set target to route the run at a cheap local model; blank uses the session's active target.",
348
456
  "watchdog.cadenceToolCalls": "Also fire every N tool calls inside a turn; blank fires at turn end only.",
349
- theme: "Color palette. Clio ships a single tuned palette.",
350
457
  runtimePlugins: "npm packages exporting clioRuntimes: RuntimeDescriptor[].",
351
458
  "compaction.model": "Dedicated summarization model; blank uses the orchestrator.",
352
459
  "compaction.systemPrompt": "Path to a compaction prompt override; blank uses the built-in.",
@@ -356,6 +463,10 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
356
463
  targets: "Inference targets available for chat and workers. Add one with `clio-coder targets add`.",
357
464
  keybindings: "Custom key overrides layered on the defaults.",
358
465
  "delegation.agents": "External ACP agents available to /delegate.",
466
+ "library.catalog": "Path to the private resource catalog; blank uses the one in your config directory.",
467
+ "library.remote": "Git remote the catalog syncs with; blank keeps the library entirely local.",
468
+ "library.confirmedRemote": "The remote you confirmed. Sync refuses until it matches library.remote.",
469
+ "library.sync": "Whether `clio-coder library sync` and `push` may talk to the remote at all.",
359
470
  } as const satisfies Record<EditableSettingId, string>;
360
471
 
361
472
  /** Longer, optional guidance shown beneath the one-line description when there is room. */
@@ -366,6 +477,37 @@ const SETTINGS_HELP_BY_ID: Partial<Record<EditableSettingId, string>> = {
366
477
  "Clamped down to each model's max-output cap and the remaining context window. Set 0 to use per-model caps only.",
367
478
  "compaction.threshold":
368
479
  "pressure = estimated tokens ÷ context window. Higher keeps more history but risks overflow before a summary runs.",
480
+ "context.workingSet.enabled":
481
+ "Eviction moves stale tool-result bodies and thinking blocks out of the model's working set and records a ledger entry; history is never rewritten. Off skips eviction and goes straight to summary compaction. Legal values: true, false · default: true.",
482
+ "context.workingSet.policy":
483
+ "structural-v1 selects by message structure; age-horizon is the older age-based rule. Legal values: structural-v1, age-horizon · default: structural-v1.",
484
+ "context.workingSet.target":
485
+ "An applied eviction batch keeps evicting until pressure reaches this ratio, so it sits below compaction.threshold. Greater than 0 and less than 1 · default: 0.6.",
486
+ "context.workingSet.protectLastTurns": "Counted in user turns. Whole number of at least 1 · default: 6.",
487
+ "context.workingSet.minEvictableTokens":
488
+ "The floor sweep put marker break-even near 50 tokens; 200 is the churn guard. Whole number, 0 evicts anything · default: 200.",
489
+ "guardrails.turnToolCallBudget":
490
+ "Crossing it blocks further calls in the turn with a stop-and-summarize directive, and the hard interrupt ceiling sits a fixed margin above. A backstop against a model spraying unproductive calls, not a routine ceiling: a repo-wide audit legitimately runs dozens. Whole number of at least 1 · default: 60. Env override CLIO_CODER_TURN_TOOL_CALL_BUDGET beats this value.",
491
+ "guardrails.workerToolCallCap":
492
+ "Bounds admitted calls, not attempts: a call the harness refuses never ran and never spends the cap. Dispatch takes the smaller of this and the agent recipe's own budget, so the recipe normally binds. Whole number of at least 1 · default: 150. Env override CLIO_CODER_WORKER_TOOL_CALL_CAP beats this value.",
493
+ "guardrails.maxDispatchRuns":
494
+ "Runs leaving the ring also lose their event journal directory. Whole number of at least 1 · default: 1000. Env override CLIO_CODER_MAX_DISPATCH_RUNS beats this value.",
495
+ "guardrails.readMaxBytes":
496
+ "Clamped up to a 1KB floor at use. Whole number of bytes, at least 1 · default: 51200 (50KB). Env override CLIO_CODER_READ_MAX_BYTES beats this value.",
497
+ "guardrails.observationTurnBudgetBytes":
498
+ "One pool shared by every observation-producing tool in a turn, so a single verbose tool cannot starve the rest. Whole number of bytes, at least 1 · default: 196608 (192KB). Env override CLIO_CODER_OBSERVATION_TURN_BUDGET_BYTES beats this value.",
499
+ "guardrails.internalDispatchTimeoutMs":
500
+ "Covers the wiki documenter and the bootstrap scout. Continuous output satisfies the heartbeat watchdog and a run mid-generation spends no tool calls, so this is the only guard that ends a degenerate generator. Healthy runs finish in minutes. Whole milliseconds of at least 1 · default: 900000 (15 minutes). Env override CLIO_CODER_INTERNAL_DISPATCH_TIMEOUT_MS beats this value.",
501
+ "retry.streamStallMs":
502
+ "Measured from the last token received, not from the request, so a slow-but-alive stream is never aborted. The retry then follows the same enabled/maxRetries/delay settings above. Whole milliseconds · default: 180000 (three minutes).",
503
+ "library.catalog":
504
+ "Absolute path, or blank for the catalog in your config directory. The catalog is the index `clio-coder library` reads; installed resources land in the usual skill and resource roots either way. Default: blank.",
505
+ "library.remote":
506
+ "A Git remote URL, or blank to keep the library local. Setting it is not enough to sync: the remote must also be confirmed, and library.sync must be true. Default: blank.",
507
+ "library.confirmedRemote":
508
+ "Written by the confirm flow, not by hand, because confirming a remote here would be the record confirming itself. Sync refuses with library_remote_unconfirmed until this equals library.remote. Default: blank.",
509
+ "library.sync":
510
+ "Off means `clio-coder library sync` and `push` refuse before touching the network, whatever the remote says. Legal values: true, false · default: false.",
369
511
  "budget.concurrency": "auto sizes to your machine. A fixed number caps how many workers run at once.",
370
512
  "skills.trustProjectCompatRoots":
371
513
  "Project roots like .claude/skills and .codex/skills are untrusted by default; enabling exposes them to the model.",
@@ -373,10 +515,24 @@ const SETTINGS_HELP_BY_ID: Partial<Record<EditableSettingId, string>> = {
373
515
  "Role trailers are added only when Clio has trusted evidence for that role. Disabling leaves subsequent commit messages entirely unchanged.",
374
516
  "workers.onPermission":
375
517
  "deny turns the ask into a tool denial and the run continues; fail stops the run as permission_required; escalate forwards the ask to you and falls back per workers.escalation on timeout.",
518
+ "workers.escalation.timeoutMs":
519
+ "Only the escalate posture reads it, and it is what keeps that posture non-stall: a headless session has no operator to answer, so the fallback always governs there. Whole milliseconds of at least 1; default: 120000 (two minutes).",
520
+ "workers.escalation.fallback":
521
+ "deny turns the unanswered ask into a tool denial and the run continues; fail ends the run as permission_required. Legal values: deny, fail · default: deny.",
522
+ "workers.resilienceCooldownMs":
523
+ "Applied per target, runtime, and wire model after a failure class that trips the breaker; a clean run clears it immediately. Whole milliseconds, 0 disables the cooldown; default: 15000.",
524
+ "routing.activeRoles":
525
+ "Joint route selection stays shadow-only, recording what it would have picked, unless both the execution role and the requested posture are named as active. Legal values: researcher, verifier, reviewer, judge · default: none active.",
526
+ "routing.activePostures":
527
+ "Manual pins are exact rather than adaptive, so manual is never an activated posture. Legal values: quality, balanced, latency, economy · default: none active.",
528
+ "routing.agentAutomation.activeAgentRoles":
529
+ "Exact agentId and executionRole pairs, because independent agent and role lists would authorize their whole cross-product. Execution roles: builder, researcher, verifier, reviewer, judge; the agentId `auto` is reserved. Edit the pairs in settings.yaml; default: none active.",
530
+ "prewarm.enabled":
531
+ "Fires at session start, after a resume rebuilds the message array, and after a compaction settles, never while a turn or dispatch is running. Local-native targets and interactive sessions only, whatever this says. Legal values: true, false · default: true.",
376
532
  "workers.agentBindings":
377
533
  "Bind base, custom, and shadow native agents such as scout, researcher, and provenance to profiles. ACP delegation agents cannot be bound.",
378
534
  "delegation.defaults.toolGovernance":
379
- "clio-policy gates the agent through Clio's safety net; agent-managed trusts the agent; deny-all blocks every tool.",
535
+ "clio-coder-policy gates the agent through Clio's safety net; agent-managed trusts the agent; deny-all blocks every tool.",
380
536
  scope: "Choose target-level or exact target/model refs. Alt+J / Alt+K step the chat target through this list.",
381
537
  runtimePlugins: "Comma-separated package names, loaded at startup. Restart Clio after changing.",
382
538
  "terminal.notify":
@@ -408,8 +564,28 @@ const SETTINGS_VALUE_HELP_BY_ID: Partial<Record<EditableSettingId, Record<string
408
564
  escalate:
409
565
  "the ask is forwarded to this session's operator; on timeout it falls back to deny or fail per workers.escalation",
410
566
  },
567
+ "workers.escalation.fallback": {
568
+ deny: "an unanswered escalation becomes a tool denial and the run continues",
569
+ fail: "an unanswered escalation ends the run as permission_required",
570
+ },
571
+ "prewarm.enabled": {
572
+ true: "prefill the next turn's known prefix on local-native targets",
573
+ false: "never send a pre-warm request; the first turn pays the whole prefill",
574
+ },
575
+ "context.workingSet.enabled": {
576
+ true: "evict stale observations non-destructively before summarizing",
577
+ false: "skip eviction and go straight to summary compaction",
578
+ },
579
+ "context.workingSet.policy": {
580
+ "structural-v1": "select eviction candidates by message structure",
581
+ "age-horizon": "the older rule: select by age alone",
582
+ },
583
+ "library.sync": {
584
+ true: "allow `clio-coder library sync` and `push` to reach the confirmed remote",
585
+ false: "refuse every library network operation",
586
+ },
411
587
  "delegation.defaults.toolGovernance": {
412
- "clio-policy": "Clio's safety policy gates the delegated agent's tools",
588
+ "clio-coder-policy": "Clio's safety policy gates the delegated agent's tools",
413
589
  "agent-managed": "the external agent governs its own tools",
414
590
  "deny-all": "block every tool the delegated agent requests",
415
591
  },
@@ -417,6 +593,16 @@ const SETTINGS_VALUE_HELP_BY_ID: Partial<Record<EditableSettingId, Record<string
417
593
  true: "compact automatically before a turn crosses the threshold",
418
594
  false: "context is only compacted when you run /context compact",
419
595
  },
596
+ // `embedded` is a declared rung with no implementation behind it, and a row
597
+ // that offered it beside two working values read as a third working value.
598
+ // It stays selectable, because refusing it outright would break a settings
599
+ // file that names it, but nothing here lets an operator pick it believing
600
+ // they will get panes.
601
+ "panes.enabled": {
602
+ auto: "detect a herdr session and join it as a guest; no pane host, no panes",
603
+ embedded: "NOT IMPLEMENTED (ships in phase 5): this session gets no panes at all, guest mode included",
604
+ off: "never detect or open a pane",
605
+ },
420
606
  "retry.enabled": {
421
607
  true: "retry transient provider errors automatically",
422
608
  false: "surface transient errors immediately without retrying",
@@ -491,7 +677,7 @@ export interface SettingsCenterItem extends SettingItem {
491
677
  label: string;
492
678
  description: string;
493
679
  section: SettingsSectionId;
494
- configPath: SettingsCenterRowId;
680
+ configPath: string;
495
681
  affordance: string;
496
682
  scope: SettingScope;
497
683
  readOnly: boolean;
@@ -589,7 +775,7 @@ function delegationAgentsAffordance(
589
775
  submenu: selectListSubmenu(
590
776
  "Delegation agents",
591
777
  choices,
592
- "An added peer runs under clio-policy governance and inherits projectContext: none.",
778
+ "An added peer runs under clio-coder-policy governance and inherits projectContext: none.",
593
779
  ),
594
780
  };
595
781
  }
@@ -733,6 +919,19 @@ const NUMBER_SETTING_RULES = {
733
919
  "delegation.defaults.connectTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
734
920
  "delegation.defaults.turnTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
735
921
  "delegation.defaults.permissionTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
922
+ // The escalation deadline is armed as a timer, so it shares the delegation
923
+ // bounds; the rest mirror the config validator's own integer bounds.
924
+ "workers.escalation.timeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
925
+ "workers.resilienceCooldownMs": { min: 0, integer: true },
926
+ "guardrails.turnToolCallBudget": { min: 1, integer: true },
927
+ "guardrails.workerToolCallCap": { min: 1, integer: true },
928
+ "guardrails.maxDispatchRuns": { min: 1, integer: true },
929
+ "guardrails.readMaxBytes": { min: 1, integer: true },
930
+ "guardrails.observationTurnBudgetBytes": { min: 1, integer: true },
931
+ "guardrails.internalDispatchTimeoutMs": { min: 1, integer: true },
932
+ // The config validator's bounds for both dock shares; half the axis is the cap.
933
+ "panes.workers.ratio": { min: 0.05, max: 0.5, integer: false },
934
+ "panes.yazi.ratio": { min: 0.05, max: 0.5, integer: false },
736
935
  } as const satisfies Partial<Record<EditableSettingId, NumberSettingRule>>;
737
936
 
738
937
  export type NumberSettingId = keyof typeof NUMBER_SETTING_RULES;
@@ -768,16 +967,6 @@ function parseNumberSetting(value: string, rule: NumberSettingRule): NumberSetti
768
967
  return { value: parsed };
769
968
  }
770
969
 
771
- /**
772
- * The reason a number row would refuse `value`, or null when it would apply.
773
- * This is the same check the editor and `applySettingChange` run, exposed so a
774
- * caller that bypasses the editor can still name the bound it hit.
775
- */
776
- export function describeNumberSettingRefusal(id: NumberSettingId, value: string): string | null {
777
- const outcome = parseNumberSetting(value, NUMBER_SETTING_RULES[id]);
778
- return "refusal" in outcome ? outcome.refusal : null;
779
- }
780
-
781
970
  function numberSettingNote(rule: NumberSettingRule): string {
782
971
  const noun = rule.integer ? "a whole number" : "a number";
783
972
  const clears = rule.allowBlank === true ? "; blank clears it" : "";
@@ -818,6 +1007,51 @@ function editNumberSubmenu(title: string, id: NumberSettingId): SettingSubmenuBu
818
1007
  };
819
1008
  }
820
1009
 
1010
+ /**
1011
+ * The comma-separated set an activation list holds, refused in place when an
1012
+ * entry is not one of the legal values. Silently dropping the unknown entry
1013
+ * would leave the operator looking at a list they thought they had activated,
1014
+ * so the editor stays open and names both the entry and the vocabulary.
1015
+ */
1016
+ function parseEnumListSetting(value: string, legal: readonly string[]): { values: string[] } | { refusal: string } {
1017
+ const entries = value
1018
+ .split(",")
1019
+ .map((entry) => entry.trim())
1020
+ .filter((entry) => entry.length > 0);
1021
+ const unknown = entries.find((entry) => !legal.includes(entry));
1022
+ if (unknown !== undefined) {
1023
+ return { refusal: `Not applied: '${unknown}' is not one of ${legal.join(", ")}.` };
1024
+ }
1025
+ const duplicate = entries.find((entry, index) => entries.indexOf(entry) !== index);
1026
+ if (duplicate !== undefined) return { refusal: `Not applied: '${duplicate}' is listed twice.` };
1027
+ return { values: entries };
1028
+ }
1029
+
1030
+ function editEnumListSubmenu(title: string, legal: readonly string[]): SettingSubmenuBuilder {
1031
+ return (currentValue: string, done: (val?: string) => void) => {
1032
+ const input = new Input();
1033
+ input.setValue(currentValue);
1034
+ input.focused = true;
1035
+ const wrapper = new SubmenuWrapper(
1036
+ title,
1037
+ input,
1038
+ buildHint([{ key: "Enter", verb: "confirm" }], "back"),
1039
+ `Comma-separated, from ${legal.join(", ")}; blank activates none.`,
1040
+ );
1041
+ input.onSubmit = (val) => {
1042
+ const outcome = parseEnumListSetting(val, legal);
1043
+ if ("refusal" in outcome) {
1044
+ wrapper.setProblem(outcome.refusal);
1045
+ return;
1046
+ }
1047
+ wrapper.setProblem(null);
1048
+ done(outcome.values.join(", "));
1049
+ };
1050
+ input.onEscape = () => done();
1051
+ return wrapper;
1052
+ };
1053
+ }
1054
+
821
1055
  function selectListSubmenu(
822
1056
  title: string,
823
1057
  items: ReadonlyArray<{
@@ -1126,7 +1360,7 @@ function profileTargetChoices(
1126
1360
  }
1127
1361
 
1128
1362
  function profileNameChoices(live: () => Readonly<ClioSettings>): Array<{ value: string; label: string }> {
1129
- const names = Object.keys(live().workers.profiles).sort();
1363
+ const names = Object.keys(live().fleet.profiles).sort();
1130
1364
  return names.map((name) => ({ value: name, label: name }));
1131
1365
  }
1132
1366
 
@@ -1240,13 +1474,45 @@ function scopeForId(id: EditableSettingId): SettingScope {
1240
1474
 
1241
1475
  /** Shipped default as a display string, for the "default: X" hint and the modified marker. */
1242
1476
  function defaultValueFor(id: EditableSettingId): string | undefined {
1243
- if (isRoutingPath(id)) return undefined;
1244
- const raw = getAtPath(DEFAULT_SETTINGS, id);
1477
+ const path = settingsV2PathForRow(id);
1478
+ if (isRoutingPath(path)) return undefined;
1479
+ const raw = getAtPath(DEFAULT_SETTINGS, path);
1245
1480
  if (raw === null || raw === undefined || typeof raw === "object") return undefined;
1246
1481
  if (id === "attribution.gitCommits") return raw === true ? "enabled" : "disabled";
1247
1482
  return String(raw);
1248
1483
  }
1249
1484
 
1485
+ const SETTINGS_CENTER_V2_PATH_OVERRIDES: Readonly<Record<string, string>> = {
1486
+ "workers.profiles": "fleet.profiles",
1487
+ "workers.agentBindings": "fleet.agentProfiles",
1488
+ "routing.agentAutomation.activeAgentRoles": "fleet.adaptiveRouting.agentRoles",
1489
+ "panes.enabled": "interface.panes.enabled",
1490
+ "panes.notifications": "interface.panes.notifications",
1491
+ "panes.journal": "fleet.history.journal",
1492
+ "panes.yazi.enabled": "interface.panes.files.enabled",
1493
+ "panes.yazi.mode": "interface.panes.files.mode",
1494
+ "panes.yazi.profile": "interface.panes.files.profile",
1495
+ "panes.yazi.followCwd": "interface.panes.files.followCwd",
1496
+ "panes.yazi.ratio": "interface.panes.files.ratio",
1497
+ "panes.layout": "interface.panes.layout",
1498
+ "panes.workers.ratio": "interface.panes.workers.ratio",
1499
+ "delegation.agents": "integrations.externalAgents.entries",
1500
+ keybindings: "interface.keybindings",
1501
+ };
1502
+
1503
+ /** Keep the existing Center navigation while showing and persisting only canonical v2 paths. */
1504
+ function settingsV2PathForRow(id: EditableSettingId): string {
1505
+ if (id.startsWith("workers.profiles.")) return `fleet.profiles.${id.slice("workers.profiles.".length)}`;
1506
+ if (id.startsWith("workers.agentBindings.")) return `fleet.agentProfiles.${id.slice("workers.agentBindings.".length)}`;
1507
+ const override = SETTINGS_CENTER_V2_PATH_OVERRIDES[id];
1508
+ if (override !== undefined) return override;
1509
+ for (const [v1Path, v2Path] of SETTINGS_V1_PATH_MOVES) {
1510
+ if (v1Path === id) return v2Path;
1511
+ if (v1Path === `${id}[]` && v2Path.endsWith("[]")) return v2Path.slice(0, -2);
1512
+ }
1513
+ return id;
1514
+ }
1515
+
1250
1516
  function settingItem(
1251
1517
  id: EditableSettingId,
1252
1518
  currentValue: string,
@@ -1269,7 +1535,7 @@ function settingItem(
1269
1535
  currentValue,
1270
1536
  description: options.description ?? SETTINGS_DESCRIPTIONS_BY_ID[id as keyof typeof SETTINGS_LABELS_BY_ID],
1271
1537
  section: sectionForSetting(id),
1272
- configPath: id,
1538
+ configPath: settingsV2PathForRow(id),
1273
1539
  affordance: options.affordance ?? (options.values ? cycleAffordance(options.values) : "opens picker"),
1274
1540
  scope: scopeForId(id),
1275
1541
  readOnly: options.readOnly ?? false,
@@ -1288,13 +1554,23 @@ function settingItem(
1288
1554
  return item;
1289
1555
  }
1290
1556
 
1291
- function fleetGroupHeader(id: FleetGroupHeaderId, label: string): SettingsCenterItem {
1557
+ /**
1558
+ * A heading row that names the block of settings under it. The id carries the
1559
+ * section it belongs to, so one builder serves every section that groups its
1560
+ * rows rather than a near-duplicate per section.
1561
+ */
1562
+ function groupHeader(
1563
+ id: GroupHeaderId,
1564
+ section: SettingsSectionId,
1565
+ label: string,
1566
+ description: string,
1567
+ ): SettingsCenterItem {
1292
1568
  return {
1293
1569
  id,
1294
1570
  label,
1295
1571
  currentValue: "",
1296
- description: `${label} in the Fleet workbench.`,
1297
- section: "fleet",
1572
+ description,
1573
+ section,
1298
1574
  configPath: id,
1299
1575
  affordance: "group heading",
1300
1576
  scope: "live",
@@ -1304,6 +1580,14 @@ function fleetGroupHeader(id: FleetGroupHeaderId, label: string): SettingsCenter
1304
1580
  };
1305
1581
  }
1306
1582
 
1583
+ function fleetGroupHeader(id: FleetGroupHeaderId, label: string): SettingsCenterItem {
1584
+ return groupHeader(id, "fleet", label, `${label} in the Fleet workbench.`);
1585
+ }
1586
+
1587
+ function terminalGroupHeader(id: TerminalGroupHeaderId, label: string): SettingsCenterItem {
1588
+ return groupHeader(id, "terminal", label, `${label} behavior in the terminal.`);
1589
+ }
1590
+
1307
1591
  function targetAddCta(): SettingsCenterItem {
1308
1592
  return {
1309
1593
  id: "targets.add-cta",
@@ -1324,7 +1608,7 @@ function thinkingChoices(
1324
1608
  providers: ProvidersContract | undefined,
1325
1609
  target: string | null,
1326
1610
  model: string | null,
1327
- level: ClioSettings["orchestrator"]["thinkingLevel"],
1611
+ level: ClioSettings["chat"]["thinkingLevel"],
1328
1612
  ): { display: string; values: readonly string[] } {
1329
1613
  const resolved = providers
1330
1614
  ? resolveModelRuntimeCapabilitiesForProviders(providers, target, model, level ?? "off")?.thinking
@@ -1341,36 +1625,28 @@ function thinkingChoices(
1341
1625
  * read-only pointer rows (targets, keybindings, favorites, safety net) name the
1342
1626
  * surface that owns them so the Center has no dead-but-tappable rows.
1343
1627
  */
1344
- export function buildSettingItems(
1345
- settings: Readonly<ClioSettings>,
1346
- options?: BuildSettingItemsOptions,
1347
- ): SettingsCenterItem[] {
1628
+ function buildSettingItems(settings: Readonly<ClioSettings>, options?: BuildSettingItemsOptions): SettingsCenterItem[] {
1348
1629
  const live = options?.getSettings ?? ((): Readonly<ClioSettings> => settings);
1349
- const scopeList = settings.scope ?? [];
1630
+ const scopeList = settings.chat.modelPicker.cycleSet ?? [];
1350
1631
  const scopeText = scopeList.length > 0 ? scopeList.join(", ") : "(empty)";
1351
- const compaction = settings.compaction;
1352
- const retry = settings.retry;
1353
- const terminal = settings.terminal;
1354
- const watchdog = settings.watchdog;
1632
+ const compaction = settings.context.compaction;
1633
+ const retry = settings.chat.retry;
1634
+ const terminal = settings.interface;
1635
+ const watchdog = settings.safety.review;
1636
+ const panes = settings.interface.panes;
1355
1637
  const orchestratorThinking = thinkingChoices(
1356
1638
  options?.providers,
1357
- settings.orchestrator.target,
1358
- settings.orchestrator.model,
1359
- settings.orchestrator.thinkingLevel,
1639
+ settings.chat.target,
1640
+ settings.chat.model,
1641
+ settings.chat.thinkingLevel,
1360
1642
  );
1361
1643
  const workerThinking = thinkingChoices(
1362
1644
  options?.providers,
1363
- settings.workers.default.target,
1364
- settings.workers.default.model,
1365
- settings.workers.default.thinkingLevel,
1645
+ settings.fleet.default.target,
1646
+ settings.fleet.default.model,
1647
+ settings.fleet.default.thinkingLevel,
1366
1648
  );
1367
- const backgroundThinking = thinkingChoices(
1368
- options?.providers,
1369
- settings.background.target,
1370
- settings.background.model,
1371
- settings.background.thinkingLevel,
1372
- );
1373
- const profileCount = Object.keys(settings.workers.profiles ?? {}).length;
1649
+ const profileCount = Object.keys(settings.fleet.profiles ?? {}).length;
1374
1650
  const addProfileSubmenu = chainSubmenus(
1375
1651
  textInputSubmenu("New profile name"),
1376
1652
  () => selectListSubmenu("Select the profile's target", profileTargetChoices(live, options?.providers)),
@@ -1383,31 +1659,48 @@ export function buildSettingItems(
1383
1659
  );
1384
1660
  const targetSubmenu = options?.providers ? selectTargetSubmenu(options.providers) : editTextSubmenu("Type target id");
1385
1661
  const orchestratorModelSubmenu = options?.providers
1386
- ? selectModelSubmenu(options.providers, () => live().orchestrator.target ?? undefined)
1662
+ ? selectModelSubmenu(options.providers, () => live().chat.target ?? undefined)
1387
1663
  : editTextSubmenu("Type model name");
1388
1664
  const workerModelSubmenu = options?.providers
1389
- ? selectModelSubmenu(options.providers, () => live().workers.default.target ?? undefined)
1665
+ ? selectModelSubmenu(options.providers, () => live().fleet.default.target ?? undefined)
1390
1666
  : editTextSubmenu("Type model name");
1391
1667
  const backgroundTargetSubmenu = options?.providers
1392
1668
  ? selectOptionalBackgroundTargetSubmenu(options.providers)
1393
1669
  : editTextSubmenu("Type memory target id", "Leave blank for rules-only memory.");
1394
1670
  const backgroundModelSubmenu = options?.providers
1395
- ? selectModelSubmenu(options.providers, () => live().background.target ?? undefined)
1671
+ ? selectModelSubmenu(options.providers, () => live().context.memory.target ?? undefined)
1396
1672
  : editTextSubmenu("Type memory model name");
1397
- const favorites = settings.modelSelector?.favorites ?? [];
1398
- const agents = settings.delegation?.agents ?? [];
1399
- const keybindingCount = Object.keys(settings.keybindings ?? {}).length;
1673
+ // Both are optional in the settings type, so the row falls back to the
1674
+ // shipped value rather than rendering a blank for a key that is in force.
1675
+ const escalation = settings.fleet.permissions.escalation ?? SHIPPED_ESCALATION;
1676
+ const resilienceCooldownMs = settings.fleet.retry.routeCooldownMs ?? SHIPPED_RESILIENCE_COOLDOWN_MS;
1677
+ const workingSet = settings.context.workingSet;
1678
+ const safetyLimits = settings.safety.limits;
1679
+ const fleetLimits = settings.fleet.limits;
1680
+ const routing = settings.fleet.adaptiveRouting;
1681
+ const library = settings.integrations.library;
1682
+ const activeAgentRoles = routing.agentRoles;
1683
+ const favorites = settings.chat.modelPicker?.favorites ?? [];
1684
+ const agents = settings.integrations.externalAgents?.entries ?? [];
1685
+ const keybindingCount = Object.keys(settings.interface.keybindings ?? {}).length;
1400
1686
  return [
1401
- settingItem("autonomy", settings.autonomy, {
1687
+ settingItem("autonomy", settings.safety.autonomy, {
1402
1688
  values: ["read-only", "suggest", "auto-edit", "full-auto"],
1403
1689
  }),
1404
- settingItem("workers.onPermission", settings.workers.onPermission ?? "deny", {
1690
+ settingItem("workers.onPermission", settings.fleet.permissions.mode ?? "deny", {
1405
1691
  values: ["deny", "fail", "escalate"],
1406
1692
  }),
1407
- settingItem("delegation.defaults.toolGovernance", settings.delegation.defaults.toolGovernance, {
1408
- values: ["clio-policy", "agent-managed", "deny-all"],
1693
+ settingItem("workers.escalation.timeoutMs", String(escalation.timeoutMs), {
1694
+ submenu: editNumberSubmenu("Edit escalation timeout (ms)", "workers.escalation.timeoutMs"),
1695
+ affordance: "free text",
1696
+ }),
1697
+ settingItem("workers.escalation.fallback", escalation.fallback, {
1698
+ values: ["deny", "fail"],
1699
+ }),
1700
+ settingItem("delegation.defaults.toolGovernance", settings.integrations.externalAgents.defaults.toolGovernance, {
1701
+ values: ["clio-coder-policy", "agent-managed", "deny-all"],
1409
1702
  }),
1410
- settingItem("skills.trustProjectCompatRoots", String(settings.skills.trustProjectCompatRoots), {
1703
+ settingItem("skills.trustProjectCompatRoots", String(settings.integrations.projectResources.trustProjectImports), {
1411
1704
  values: ["false", "true"],
1412
1705
  }),
1413
1706
  settingItem("safetyNet", "always on", {
@@ -1417,55 +1710,59 @@ export function buildSettingItems(
1417
1710
  settingItem("orchestrator.thinkingLevel", orchestratorThinking.display, {
1418
1711
  values: orchestratorThinking.values,
1419
1712
  }),
1420
- settingItem("orchestrator.target", settings.orchestrator.target ?? "(unset)", {
1713
+ settingItem("orchestrator.target", settings.chat.target ?? "(unset)", {
1421
1714
  submenu: targetSubmenu,
1422
1715
  affordance: options?.providers ? "opens picker" : "free text",
1423
1716
  }),
1424
- settingItem("orchestrator.model", settings.orchestrator.model ?? "(unset)", {
1717
+ settingItem("orchestrator.model", settings.chat.model ?? "(unset)", {
1425
1718
  submenu: orchestratorModelSubmenu,
1426
1719
  affordance: options?.providers ? "opens picker" : "free text",
1427
1720
  }),
1428
- settingItem("background.target", settings.background.target ?? "(unset — rules-only)", {
1721
+ settingItem("background.target", settings.context.memory.target ?? "(unset — rules-only)", {
1429
1722
  submenu: backgroundTargetSubmenu,
1430
1723
  affordance: options?.providers ? "opens picker" : "free text",
1431
1724
  }),
1432
- settingItem("background.model", settings.background.model ?? "(unset)", {
1725
+ settingItem("background.model", settings.context.memory.model ?? "(unset)", {
1433
1726
  submenu: backgroundModelSubmenu,
1434
1727
  affordance: options?.providers ? "opens picker" : "free text",
1435
1728
  }),
1436
- settingItem("background.thinkingLevel", backgroundThinking.display, {
1437
- values: backgroundThinking.values,
1438
- }),
1439
- settingItem("memory.intervention.enabled", String(settings.memory.intervention.enabled), {
1729
+ settingItem("memory.intervention.enabled", String(settings.context.memory.enabled), {
1440
1730
  values: ["true", "false"],
1441
1731
  }),
1442
- settingItem("memory.intervention.everyNTools", String(settings.memory.intervention.everyNTools), {
1732
+ settingItem("memory.intervention.everyNTools", String(settings.context.memory.cadenceToolCalls), {
1443
1733
  values: ["5", "10", "20", "30"],
1444
1734
  }),
1445
- settingItem("memory.intervention.windowSteps", String(settings.memory.intervention.windowSteps), {
1735
+ settingItem("memory.intervention.windowSteps", String(settings.context.memory.trajectorySteps), {
1446
1736
  values: ["4", "8", "12", "20"],
1447
1737
  }),
1448
- settingItem("memory.intervention.maxTokens", String(settings.memory.intervention.maxTokens), {
1738
+ settingItem("memory.intervention.maxTokens", String(settings.context.memory.maxOutputTokens), {
1449
1739
  values: ["100", "200", "400", "800"],
1450
1740
  }),
1451
- settingItem("memory.intervention.timeoutMs", String(settings.memory.intervention.timeoutMs), {
1741
+ settingItem("memory.intervention.timeoutMs", String(settings.context.memory.timeoutMs), {
1452
1742
  values: ["5000", "10000", "20000", "30000", "60000"],
1453
1743
  }),
1744
+ settingItem("prewarm.enabled", String(settings.chat.prewarm), {
1745
+ values: ["true", "false"],
1746
+ }),
1454
1747
  fleetGroupHeader("fleet.group.defaults", "Defaults"),
1455
- settingItem("workers.default.target", settings.workers.default.target ?? "(unset)", {
1748
+ settingItem("workers.default.target", settings.fleet.default.target ?? "(unset)", {
1456
1749
  submenu: targetSubmenu,
1457
1750
  affordance: options?.providers ? "opens picker" : "free text",
1458
1751
  }),
1459
- settingItem("workers.default.model", settings.workers.default.model ?? "(unset)", {
1752
+ settingItem("workers.default.model", settings.fleet.default.model ?? "(unset)", {
1460
1753
  submenu: workerModelSubmenu,
1461
1754
  affordance: options?.providers ? "opens picker" : "free text",
1462
1755
  }),
1463
1756
  settingItem("workers.default.thinkingLevel", workerThinking.display, {
1464
1757
  values: workerThinking.values,
1465
1758
  }),
1466
- settingItem("workers.maxRetries", String(settings.workers.maxRetries), {
1759
+ settingItem("workers.maxRetries", String(settings.fleet.retry.maxRetries), {
1467
1760
  values: ["0", "1", "2", "3", "5", "8"],
1468
1761
  }),
1762
+ settingItem("workers.resilienceCooldownMs", String(resilienceCooldownMs), {
1763
+ submenu: editNumberSubmenu("Edit resilience cooldown (ms)", "workers.resilienceCooldownMs"),
1764
+ affordance: "free text",
1765
+ }),
1469
1766
  fleetGroupHeader("fleet.group.profiles", "Profiles"),
1470
1767
  ...fleetProfileRows(settings, live, options),
1471
1768
  settingItem("workers.profiles", "", {
@@ -1484,9 +1781,36 @@ export function buildSettingItems(
1484
1781
  presentationKind: profileCount > 0 ? "action" : "read-only-fact",
1485
1782
  valueSegments: [],
1486
1783
  }),
1784
+ fleetGroupHeader("fleet.group.route-activation", "Route activation"),
1785
+ settingItem("routing.activeRoles", routing.roles.length > 0 ? routing.roles.join(", ") : "(none)", {
1786
+ submenu: editEnumListSubmenu("Edit active routing roles", ACTIVE_ROUTING_ROLES),
1787
+ affordance: `free text from ${ACTIVE_ROUTING_ROLES.join(", ")}`,
1788
+ editValue: routing.roles.join(", "),
1789
+ }),
1790
+ settingItem("routing.activePostures", routing.postures.length > 0 ? routing.postures.join(", ") : "(none)", {
1791
+ submenu: editEnumListSubmenu("Edit active routing postures", ACTIVE_ROUTING_POSTURES),
1792
+ affordance: `free text from ${ACTIVE_ROUTING_POSTURES.join(", ")}`,
1793
+ editValue: routing.postures.join(", "),
1794
+ }),
1795
+ settingItem(
1796
+ "routing.agentAutomation.activeAgentRoles",
1797
+ activeAgentRoles.length > 0
1798
+ ? activeAgentRoles.map((pair) => `${pair.agentId}/${pair.executionRole}`).join(", ")
1799
+ : "(none)",
1800
+ { affordance: "edit settings.yaml", readOnly: true },
1801
+ ),
1487
1802
  fleetGroupHeader("fleet.group.placement", "Placement"),
1488
1803
  ...fleetNodeRows(options?.getFleetNodes?.() ?? []),
1489
1804
  ...fleetEndpointRows(options?.providers),
1805
+ fleetGroupHeader("fleet.group.panes", "Panes"),
1806
+ settingItem("panes.enabled", panes.enabled, { values: ["auto", "embedded", "off"] }),
1807
+ settingItem("panes.notifications", panes.notifications, { values: ["failures", "all", "off"] }),
1808
+ settingItem("panes.layout", panes.layout, { values: ["off", "workers", "cockpit"] }),
1809
+ settingItem("panes.workers.ratio", formatThreshold(panes.workers.ratio, "0.34"), {
1810
+ submenu: editNumberSubmenu("Edit workers dock share (0.05–0.5)", "panes.workers.ratio"),
1811
+ affordance: "free text",
1812
+ }),
1813
+ settingItem("panes.journal", String(settings.fleet.history.journal), { values: ["true", "false"] }),
1490
1814
  settingItem("targets", "", {
1491
1815
  description: "Live inference target inventory and routing roles.",
1492
1816
  affordance: "column heading",
@@ -1500,32 +1824,80 @@ export function buildSettingItems(
1500
1824
  submenu: scopedModelsSubmenu(scopeList, options?.providers),
1501
1825
  affordance: "opens provider-backed checklist",
1502
1826
  }),
1503
- settingItem("modelSelector.recentLimit", String(settings.modelSelector.recentLimit), {
1827
+ settingItem("modelSelector.recentLimit", String(settings.chat.modelPicker.recentLimit), {
1504
1828
  values: ["6", "12", "20", "50"],
1505
1829
  }),
1506
1830
  settingItem("modelSelector.favorites", favorites.length > 0 ? `${favorites.length} pinned` : "(none)", {
1507
1831
  affordance: "manage in /model",
1508
1832
  readOnly: true,
1509
1833
  }),
1510
- settingItem("budget.sessionCeilingUsd", String(settings.budget.sessionCeilingUsd), {
1834
+ settingItem("budget.sessionCeilingUsd", String(settings.safety.limits.sessionCostUsd), {
1511
1835
  submenu: editNumberSubmenu("Edit session cost ceiling USD", "budget.sessionCeilingUsd"),
1512
1836
  affordance: "free text",
1513
1837
  }),
1514
- settingItem("defaults.maxTokens", String(settings.defaults.maxTokens), {
1838
+ settingItem("defaults.maxTokens", String(settings.chat.maxOutputTokens), {
1515
1839
  values: ["0", "4096", "8192", "16384", "32768", "65536", "131072"],
1516
1840
  }),
1517
- settingItem("budget.concurrency", String(settings.budget.concurrency), {
1841
+ settingItem("budget.concurrency", String(settings.fleet.concurrency), {
1518
1842
  values: ["auto", "1", "2", "4", "8"],
1519
1843
  }),
1844
+ groupHeader(
1845
+ "budget.group.guardrails",
1846
+ "budget",
1847
+ "Guardrails",
1848
+ "Numeric backstops that bound runaway agent behavior.",
1849
+ ),
1850
+ settingItem("guardrails.turnToolCallBudget", String(safetyLimits.chatToolCallsPerTurn), {
1851
+ submenu: editNumberSubmenu("Edit turn tool-call budget", "guardrails.turnToolCallBudget"),
1852
+ affordance: "free text",
1853
+ }),
1854
+ settingItem("guardrails.workerToolCallCap", String(fleetLimits.toolCallsPerRun), {
1855
+ submenu: editNumberSubmenu("Edit worker tool-call cap", "guardrails.workerToolCallCap"),
1856
+ affordance: "free text",
1857
+ }),
1858
+ settingItem("guardrails.maxDispatchRuns", String(settings.fleet.history.maxRuns), {
1859
+ submenu: editNumberSubmenu("Edit run ledger retention", "guardrails.maxDispatchRuns"),
1860
+ affordance: "free text",
1861
+ }),
1862
+ settingItem("guardrails.readMaxBytes", String(safetyLimits.readBytesPerCall), {
1863
+ submenu: editNumberSubmenu("Edit read byte cap", "guardrails.readMaxBytes"),
1864
+ affordance: "free text",
1865
+ }),
1866
+ settingItem("guardrails.observationTurnBudgetBytes", String(safetyLimits.observationBytesPerTurn), {
1867
+ submenu: editNumberSubmenu("Edit observation byte pool", "guardrails.observationTurnBudgetBytes"),
1868
+ affordance: "free text",
1869
+ }),
1870
+ settingItem("guardrails.internalDispatchTimeoutMs", String(fleetLimits.internalRunTimeoutMs), {
1871
+ submenu: editNumberSubmenu("Edit internal dispatch timeout (ms)", "guardrails.internalDispatchTimeoutMs"),
1872
+ affordance: "free text",
1873
+ }),
1520
1874
  settingItem("compaction.auto", String(compaction.auto), {
1521
1875
  values: ["true", "false"],
1522
1876
  }),
1523
1877
  settingItem("compaction.threshold", formatThreshold(compaction.threshold), {
1524
1878
  values: ["0.7", "0.8", "0.85", "0.9"],
1525
1879
  }),
1526
- settingItem("compaction.excludeLastTurns", String(compaction.excludeLastTurns), {
1880
+ groupHeader(
1881
+ "compaction.group.working-set",
1882
+ "compaction",
1883
+ "Working set",
1884
+ "The non-destructive eviction layer that runs before a summary is needed.",
1885
+ ),
1886
+ settingItem("context.workingSet.enabled", String(workingSet.enabled), {
1887
+ values: ["true", "false"],
1888
+ }),
1889
+ settingItem("context.workingSet.policy", workingSet.policy, {
1890
+ values: ["structural-v1", "age-horizon"],
1891
+ }),
1892
+ settingItem("context.workingSet.target", formatThreshold(workingSet.target, "0.6"), {
1893
+ values: ["0.4", "0.5", "0.6", "0.7"],
1894
+ }),
1895
+ settingItem("context.workingSet.protectLastTurns", String(workingSet.protectLastTurns), {
1527
1896
  values: ["3", "6", "10", "15"],
1528
1897
  }),
1898
+ settingItem("context.workingSet.minEvictableTokens", String(workingSet.minEvictableTokens), {
1899
+ values: ["0", "100", "200", "500", "1000"],
1900
+ }),
1529
1901
  settingItem("retry.enabled", String(retry.enabled), {
1530
1902
  values: ["true", "false"],
1531
1903
  }),
@@ -1538,13 +1910,16 @@ export function buildSettingItems(
1538
1910
  settingItem("retry.maxDelayMs", String(retry.maxDelayMs), {
1539
1911
  values: ["10000", "30000", "60000", "120000", "300000"],
1540
1912
  }),
1541
- settingItem("terminal.showTerminalProgress", String(terminal.showTerminalProgress), {
1913
+ settingItem("retry.streamStallMs", String(retry.streamStallMs), {
1914
+ values: ["60000", "120000", "180000", "300000", "600000"],
1915
+ }),
1916
+ settingItem("terminal.showTerminalProgress", String(terminal.terminalProgress), {
1542
1917
  values: ["false", "true"],
1543
1918
  }),
1544
- settingItem("terminal.outputVerbosity", terminal.outputVerbosity, {
1919
+ settingItem("terminal.outputVerbosity", terminal.outputDetail, {
1545
1920
  values: ["minimal", "default", "verbose"],
1546
1921
  }),
1547
- settingItem("terminal.tuiMode", terminal.tuiMode, {
1922
+ settingItem("terminal.tuiMode", terminal.mode, {
1548
1923
  values: ["regular", "fullscreen"],
1549
1924
  }),
1550
1925
  settingItem("terminal.fullscreenScrollbar", terminal.fullscreenScrollbar, {
@@ -1553,12 +1928,17 @@ export function buildSettingItems(
1553
1928
  settingItem("terminal.smoothStreaming", terminal.smoothStreaming, {
1554
1929
  values: ["off", "auto", "on"],
1555
1930
  }),
1556
- settingItem("terminal.notify", String(terminal.notify), {
1931
+ settingItem("terminal.notify", String(terminal.desktopNotifications), {
1557
1932
  values: ["false", "true"],
1558
1933
  }),
1559
- settingItem("theme", settings.theme, {
1560
- affordance: "single clio-coder palette",
1561
- readOnly: true,
1934
+ terminalGroupHeader("terminal.group.files-pane", "Files pane"),
1935
+ settingItem("panes.yazi.enabled", String(panes.files.enabled), { values: ["true", "false"] }),
1936
+ settingItem("panes.yazi.mode", panes.files.mode, { values: ["companion", "chooser"] }),
1937
+ settingItem("panes.yazi.profile", panes.files.profile, { values: ["managed", "user"] }),
1938
+ settingItem("panes.yazi.followCwd", String(panes.files.followCwd), { values: ["true", "false"] }),
1939
+ settingItem("panes.yazi.ratio", formatThreshold(panes.files.ratio, "0.3"), {
1940
+ submenu: editNumberSubmenu("Edit files dock share (0.05–0.5)", "panes.yazi.ratio"),
1941
+ affordance: "free text",
1562
1942
  }),
1563
1943
  settingItem("watchdog.enabled", String(watchdog.enabled), {
1564
1944
  values: ["false", "true"],
@@ -1583,11 +1963,15 @@ export function buildSettingItems(
1583
1963
  editValue: watchdog.cadenceToolCalls === undefined ? "" : String(watchdog.cadenceToolCalls),
1584
1964
  },
1585
1965
  ),
1586
- settingItem("runtimePlugins", settings.runtimePlugins.length > 0 ? settings.runtimePlugins.join(", ") : "(none)", {
1587
- submenu: editTextSubmenu("Edit runtime plugins comma-separated list", "Restart Clio to load changes."),
1588
- affordance: "free text",
1589
- }),
1590
- settingItem("attribution.gitCommits", settings.attribution.gitCommits ? "enabled" : "disabled", {
1966
+ settingItem(
1967
+ "runtimePlugins",
1968
+ settings.integrations.runtimePlugins.length > 0 ? settings.integrations.runtimePlugins.join(", ") : "(none)",
1969
+ {
1970
+ submenu: editTextSubmenu("Edit runtime plugins comma-separated list", "Restart Clio to load changes."),
1971
+ affordance: "free text",
1972
+ },
1973
+ ),
1974
+ settingItem("attribution.gitCommits", settings.integrations.git.commitAttribution ? "enabled" : "disabled", {
1591
1975
  values: ["enabled", "disabled"],
1592
1976
  }),
1593
1977
  settingItem("compaction.model", compaction.model ?? "(orchestrator target)", {
@@ -1598,24 +1982,61 @@ export function buildSettingItems(
1598
1982
  submenu: editTextSubmenu("Edit compaction prompt path; blank uses the built-in"),
1599
1983
  affordance: "free text",
1600
1984
  }),
1601
- settingItem("delegation.defaults.connectTimeoutMs", String(settings.delegation.defaults.connectTimeoutMs), {
1602
- submenu: editNumberSubmenu("Edit delegate connect timeout (ms)", "delegation.defaults.connectTimeoutMs"),
1603
- affordance: "free text",
1985
+ settingItem(
1986
+ "delegation.defaults.connectTimeoutMs",
1987
+ String(settings.integrations.externalAgents.defaults.connectTimeoutMs),
1988
+ {
1989
+ submenu: editNumberSubmenu("Edit delegate connect timeout (ms)", "delegation.defaults.connectTimeoutMs"),
1990
+ affordance: "free text",
1991
+ },
1992
+ ),
1993
+ settingItem(
1994
+ "delegation.defaults.turnTimeoutMs",
1995
+ String(settings.integrations.externalAgents.defaults.turnTimeoutMs),
1996
+ {
1997
+ submenu: editNumberSubmenu("Edit delegate turn timeout (ms)", "delegation.defaults.turnTimeoutMs"),
1998
+ affordance: "free text",
1999
+ },
2000
+ ),
2001
+ settingItem(
2002
+ "delegation.defaults.permissionTimeoutMs",
2003
+ String(settings.integrations.externalAgents.defaults.permissionTimeoutMs),
2004
+ {
2005
+ submenu: editNumberSubmenu("Edit delegate permission timeout (ms)", "delegation.defaults.permissionTimeoutMs"),
2006
+ affordance: "free text",
2007
+ },
2008
+ ),
2009
+ settingItem("keybindings", keybindingCount > 0 ? `${keybindingCount} override(s)` : "(defaults)", {
2010
+ affordance: "edit settings.yaml",
2011
+ readOnly: true,
1604
2012
  }),
1605
- settingItem("delegation.defaults.turnTimeoutMs", String(settings.delegation.defaults.turnTimeoutMs), {
1606
- submenu: editNumberSubmenu("Edit delegate turn timeout (ms)", "delegation.defaults.turnTimeoutMs"),
2013
+ settingItem("delegation.agents", agents.length > 0 ? `${agents.length} agent(s)` : "(none)", {
2014
+ ...delegationAgentsAffordance(agents, options?.getInteropProposals?.() ?? []),
2015
+ }),
2016
+ groupHeader(
2017
+ "advanced.group.library",
2018
+ "advanced",
2019
+ "Library",
2020
+ "The private resource catalog and the remote it may sync with.",
2021
+ ),
2022
+ settingItem("library.catalog", library.catalog ?? "(config directory)", {
2023
+ submenu: editTextSubmenu("Edit library catalog path; blank uses the config directory"),
1607
2024
  affordance: "free text",
2025
+ editValue: library.catalog ?? "",
1608
2026
  }),
1609
- settingItem("delegation.defaults.permissionTimeoutMs", String(settings.delegation.defaults.permissionTimeoutMs), {
1610
- submenu: editNumberSubmenu("Edit delegate permission timeout (ms)", "delegation.defaults.permissionTimeoutMs"),
2027
+ settingItem("library.remote", library.remote ?? "(local only)", {
2028
+ submenu: editTextSubmenu("Edit library remote URL; blank keeps the library local"),
1611
2029
  affordance: "free text",
2030
+ editValue: library.remote ?? "",
1612
2031
  }),
1613
- settingItem("keybindings", keybindingCount > 0 ? `${keybindingCount} override(s)` : "(defaults)", {
1614
- affordance: "edit settings.yaml",
2032
+ // Confirming a remote from the settings row would be the trust record
2033
+ // confirming itself, so this row reports what the confirm flow wrote.
2034
+ settingItem("library.confirmedRemote", library.confirmedRemote ?? "(unconfirmed)", {
2035
+ affordance: "set by `clio-coder library` on confirmation",
1615
2036
  readOnly: true,
1616
2037
  }),
1617
- settingItem("delegation.agents", agents.length > 0 ? `${agents.length} agent(s)` : "(none)", {
1618
- ...delegationAgentsAffordance(agents, options?.getInteropProposals?.() ?? []),
2038
+ settingItem("library.sync", String(library.sync), {
2039
+ values: ["true", "false"],
1619
2040
  }),
1620
2041
  ];
1621
2042
  }
@@ -1626,7 +2047,7 @@ function fleetProfileRows(
1626
2047
  options: BuildSettingItemsOptions | undefined,
1627
2048
  ): SettingsCenterItem[] {
1628
2049
  const providers = options?.providers;
1629
- return Object.entries(settings.workers.profiles)
2050
+ return Object.entries(settings.fleet.profiles)
1630
2051
  .sort(([a], [b]) => a.localeCompare(b))
1631
2052
  .map(([name, profile]) => {
1632
2053
  const thinking = thinkingChoices(providers, profile.target, profile.model, profile.thinkingLevel);
@@ -1636,7 +2057,7 @@ function fleetProfileRows(
1636
2057
  const summary = `${target}/${model} ${thinking.display} ${placement}`;
1637
2058
  const compactThinking = truncateProfileFact(thinking.display, 7);
1638
2059
  const compactPlacement = truncateProfileFact(placement, 7);
1639
- const route = target === settings.workers.default.target ? model : `${target}/${model}`;
2060
+ const route = target === settings.fleet.default.target ? model : `${target}/${model}`;
1640
2061
  const routeBudget = Math.max(
1641
2062
  8,
1642
2063
  PROFILE_SUMMARY_VALUE_BUDGET -
@@ -1648,7 +2069,7 @@ function fleetProfileRows(
1648
2069
  return settingItem(`workers.profiles.${name}`, summary, {
1649
2070
  label: name,
1650
2071
  description: `Profile ${name}. Enter to edit its target, model, thinking level, placement, or remove it.`,
1651
- help: ["target", "model", "thinkingLevel", "node"].map((field) => `workers.profiles.${name}.${field}`).join(" · "),
2072
+ help: ["target", "model", "thinkingLevel", "node"].map((field) => `fleet.profiles.${name}.${field}`).join(" · "),
1652
2073
  submenu: profileWorkbenchSubmenu(name, settings, live, options),
1653
2074
  affordance: "Enter: drill into profile fields",
1654
2075
  valueSegments: [
@@ -1684,7 +2105,7 @@ function profileWorkbenchSubmenu(
1684
2105
  done();
1685
2106
  return;
1686
2107
  }
1687
- const profile = live().workers.profiles[name];
2108
+ const profile = live().fleet.profiles[name];
1688
2109
  if (!profile) {
1689
2110
  done();
1690
2111
  return;
@@ -1705,7 +2126,7 @@ function profileWorkbenchSubmenu(
1705
2126
  }
1706
2127
  if (field === "model") {
1707
2128
  const submenu = options?.providers
1708
- ? selectModelSubmenu(options.providers, () => live().workers.profiles[name]?.target ?? undefined)
2129
+ ? selectModelSubmenu(options.providers, () => live().fleet.profiles[name]?.target ?? undefined)
1709
2130
  : editTextSubmenu("Type model name");
1710
2131
  active = submenu(profile.model ?? "(unset)", finish);
1711
2132
  return;
@@ -1753,10 +2174,10 @@ function profileNodeChoices(
1753
2174
  }
1754
2175
 
1755
2176
  function agentBindingRows(settings: Readonly<ClioSettings>, live: () => Readonly<ClioSettings>): SettingsCenterItem[] {
1756
- return Object.entries(settings.workers.agentBindings)
2177
+ return Object.entries(settings.fleet.agentProfiles)
1757
2178
  .sort(([a], [b]) => a.localeCompare(b))
1758
2179
  .map(([agentId, profileName]) => {
1759
- const missing = !settings.workers.profiles[profileName];
2180
+ const missing = !settings.fleet.profiles[profileName];
1760
2181
  return settingItem(`workers.agentBindings.${agentId}`, profileName, {
1761
2182
  label: `${agentId} · profile`,
1762
2183
  description: missing
@@ -1779,9 +2200,9 @@ function targetRows(
1779
2200
  return settings.targets.map((target) => {
1780
2201
  const status = statuses.get(target.id);
1781
2202
  const roles = [
1782
- settings.orchestrator.target === target.id ? "chat" : null,
1783
- settings.workers.default.target === target.id ? "fleet" : null,
1784
- settings.background.target === target.id ? "memory" : null,
2203
+ settings.chat.target === target.id ? "chat" : null,
2204
+ settings.fleet.default.target === target.id ? "fleet" : null,
2205
+ settings.context.memory.target === target.id ? "memory" : null,
1785
2206
  ].filter((role) => role !== null);
1786
2207
  const health = status?.health.status ?? "unknown";
1787
2208
  const operation = options?.getTargetOperation?.(target.id) ?? null;
@@ -1894,7 +2315,7 @@ function fleetEndpointRows(providers: ProvidersContract | undefined): SettingsCe
1894
2315
  });
1895
2316
  }
1896
2317
 
1897
- export function buildSettingsSections(items: readonly SettingsCenterItem[]): SettingsCenterSection[] {
2318
+ function buildSettingsSections(items: readonly SettingsCenterItem[]): SettingsCenterSection[] {
1898
2319
  return SETTINGS_SECTIONS.map((section) => ({
1899
2320
  id: section.id,
1900
2321
  label: section.label,
@@ -1935,8 +2356,8 @@ function refreshSettingItemsInPlace(items: SettingsCenterItem[], next: readonly
1935
2356
  }
1936
2357
  }
1937
2358
 
1938
- function formatThreshold(value: number): string {
1939
- return Number.isFinite(value) ? String(value) : "0.8";
2359
+ function formatThreshold(value: number, fallback = "0.8"): string {
2360
+ return Number.isFinite(value) ? String(value) : fallback;
1940
2361
  }
1941
2362
 
1942
2363
  function applyNonNegativeInteger(value: string, set: (next: number) => void): void {
@@ -1961,48 +2382,48 @@ function applyNumberSetting(id: NumberSettingId, value: string, set: (next: numb
1961
2382
  function applyDelegationAgentChange(settings: ClioSettings, value: string): void {
1962
2383
  if (value.startsWith(DELEGATION_REMOVE_PREFIX)) {
1963
2384
  const id = value.slice(DELEGATION_REMOVE_PREFIX.length);
1964
- settings.delegation.agents = settings.delegation.agents.filter((agent) => agent.id !== id);
2385
+ settings.integrations.externalAgents.entries = settings.integrations.externalAgents.entries.filter(
2386
+ (agent) => agent.id !== id,
2387
+ );
1965
2388
  return;
1966
2389
  }
1967
2390
  if (!value.startsWith(DELEGATION_ADD_PREFIX)) return;
1968
2391
  const kind = interopAgentKind(value.slice(DELEGATION_ADD_PREFIX.length) as InteropAgentId);
1969
2392
  if (kind?.acp === undefined) return;
1970
- const entry = delegationEntryForKind(kind, settings.delegation.defaults);
1971
- if (settings.delegation.agents.some((agent) => agent.id === entry.id)) return;
1972
- settings.delegation.agents.push(entry);
2393
+ const entry = delegationEntryForKind(kind, settings.integrations.externalAgents.defaults);
2394
+ if (settings.integrations.externalAgents.entries.some((agent) => agent.id === entry.id)) return;
2395
+ settings.integrations.externalAgents.entries.push(entry);
1973
2396
  }
1974
2397
 
1975
- export function applySettingChange(settings: ClioSettings, id: string, value: string): void {
2398
+ function applySettingChange(settings: ClioSettings, id: string, value: string): void {
1976
2399
  if (applyEntrySettingChange(settings, id, value)) return;
1977
2400
  switch (id) {
1978
2401
  case "autonomy":
1979
2402
  if (value === "read-only" || value === "suggest" || value === "auto-edit" || value === "full-auto")
1980
- settings.autonomy = value;
2403
+ settings.safety.autonomy = value;
1981
2404
  return;
1982
2405
  case "workers.onPermission":
1983
- if (value === "deny" || value === "fail" || value === "escalate") settings.workers.onPermission = value;
2406
+ if (value === "deny" || value === "fail" || value === "escalate") settings.fleet.permissions.mode = value;
1984
2407
  return;
1985
2408
  case "delegation.defaults.toolGovernance":
1986
- if (value === "clio-policy" || value === "agent-managed" || value === "deny-all")
1987
- settings.delegation.defaults.toolGovernance = value;
2409
+ if (value === "clio-coder-policy" || value === "agent-managed" || value === "deny-all")
2410
+ settings.integrations.externalAgents.defaults.toolGovernance = value;
1988
2411
  return;
1989
2412
  case "delegation.agents":
1990
2413
  applyDelegationAgentChange(settings, value);
1991
2414
  return;
1992
2415
  case "skills.trustProjectCompatRoots":
1993
- if (value === "true" || value === "false") settings.skills.trustProjectCompatRoots = value === "true";
2416
+ if (value === "true" || value === "false")
2417
+ settings.integrations.projectResources.trustProjectImports = value === "true";
1994
2418
  return;
1995
2419
  case "attribution.gitCommits":
1996
- if (value === "enabled" || value === "disabled") settings.attribution.gitCommits = value === "enabled";
2420
+ if (value === "enabled" || value === "disabled") settings.integrations.git.commitAttribution = value === "enabled";
1997
2421
  return;
1998
2422
  case "orchestrator.thinkingLevel":
1999
- settings.orchestrator.thinkingLevel = thinkingLevelFromChoiceLabel(value) ?? settings.orchestrator.thinkingLevel;
2000
- return;
2001
- case "background.thinkingLevel":
2002
- settings.background.thinkingLevel = thinkingLevelFromChoiceLabel(value) ?? settings.background.thinkingLevel;
2423
+ settings.chat.thinkingLevel = thinkingLevelFromChoiceLabel(value) ?? settings.chat.thinkingLevel;
2003
2424
  return;
2004
2425
  case "memory.intervention.enabled":
2005
- if (value === "true" || value === "false") settings.memory.intervention.enabled = value === "true";
2426
+ if (value === "true" || value === "false") settings.context.memory.enabled = value === "true";
2006
2427
  return;
2007
2428
  case "memory.intervention.everyNTools":
2008
2429
  case "memory.intervention.windowSteps":
@@ -2010,102 +2431,139 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
2010
2431
  case "memory.intervention.timeoutMs":
2011
2432
  applyNonNegativeInteger(value, (next) => {
2012
2433
  if (next >= 1) {
2013
- const key = id.slice("memory.intervention.".length) as "everyNTools" | "windowSteps" | "maxTokens" | "timeoutMs";
2014
- settings.memory.intervention[key] = next;
2434
+ // The v1 row ids are the presentation vocabulary; the v2 fields they
2435
+ // write have their own names.
2436
+ const key = (
2437
+ {
2438
+ "memory.intervention.everyNTools": "cadenceToolCalls",
2439
+ "memory.intervention.windowSteps": "trajectorySteps",
2440
+ "memory.intervention.maxTokens": "maxOutputTokens",
2441
+ "memory.intervention.timeoutMs": "timeoutMs",
2442
+ } as const
2443
+ )[id];
2444
+ if (key) settings.context.memory[key] = next;
2015
2445
  }
2016
2446
  });
2017
2447
  return;
2018
2448
  case "workers.default.thinkingLevel":
2019
- settings.workers.default.thinkingLevel =
2020
- thinkingLevelFromChoiceLabel(value) ?? settings.workers.default.thinkingLevel;
2449
+ settings.fleet.default.thinkingLevel = thinkingLevelFromChoiceLabel(value) ?? settings.fleet.default.thinkingLevel;
2021
2450
  return;
2022
2451
  case "workers.maxRetries":
2023
2452
  applyNonNegativeInteger(value, (next) => {
2024
- settings.workers.maxRetries = next;
2453
+ settings.fleet.retry.maxRetries = next;
2454
+ });
2455
+ return;
2456
+ case "panes.enabled":
2457
+ if (value === "auto" || value === "embedded" || value === "off") settings.interface.panes.enabled = value;
2458
+ return;
2459
+ case "panes.notifications":
2460
+ if (value === "failures" || value === "all" || value === "off") settings.interface.panes.notifications = value;
2461
+ return;
2462
+ case "panes.layout":
2463
+ if (value === "off" || value === "workers" || value === "cockpit") settings.interface.panes.layout = value;
2464
+ return;
2465
+ case "panes.workers.ratio":
2466
+ applyNumberSetting(id, value, (next) => {
2467
+ if (next !== null) settings.interface.panes.workers.ratio = next;
2025
2468
  });
2026
2469
  return;
2470
+ case "panes.yazi.ratio":
2471
+ applyNumberSetting(id, value, (next) => {
2472
+ if (next !== null) settings.interface.panes.files.ratio = next;
2473
+ });
2474
+ return;
2475
+ case "panes.journal":
2476
+ if (value === "true" || value === "false") settings.fleet.history.journal = value === "true";
2477
+ return;
2478
+ case "panes.yazi.enabled":
2479
+ if (value === "true" || value === "false") settings.interface.panes.files.enabled = value === "true";
2480
+ return;
2481
+ case "panes.yazi.mode":
2482
+ if (value === "companion" || value === "chooser") settings.interface.panes.files.mode = value;
2483
+ return;
2484
+ case "panes.yazi.profile":
2485
+ if (value === "managed" || value === "user") settings.interface.panes.files.profile = value;
2486
+ return;
2487
+ case "panes.yazi.followCwd":
2488
+ if (value === "true" || value === "false") settings.interface.panes.files.followCwd = value === "true";
2489
+ return;
2027
2490
  case "modelSelector.recentLimit":
2028
2491
  applyNonNegativeInteger(value, (next) => {
2029
- if (next >= 1) settings.modelSelector.recentLimit = next;
2492
+ if (next >= 1) settings.chat.modelPicker.recentLimit = next;
2030
2493
  });
2031
2494
  return;
2032
2495
  case "defaults.maxTokens":
2033
2496
  applyNonNegativeInteger(value, (next) => {
2034
- settings.defaults.maxTokens = next;
2497
+ settings.chat.maxOutputTokens = next;
2035
2498
  });
2036
2499
  return;
2037
2500
  case "budget.concurrency": {
2038
2501
  if (value === "auto") {
2039
- settings.budget.concurrency = "auto";
2502
+ settings.fleet.concurrency = "auto";
2040
2503
  return;
2041
2504
  }
2042
2505
  applyNonNegativeInteger(value, (next) => {
2043
- if (next >= 1) settings.budget.concurrency = next;
2506
+ if (next >= 1) settings.fleet.concurrency = next;
2044
2507
  });
2045
2508
  return;
2046
2509
  }
2047
2510
  case "compaction.auto":
2048
- if (value === "true" || value === "false") settings.compaction.auto = value === "true";
2049
- return;
2050
- case "compaction.excludeLastTurns":
2051
- applyNonNegativeInteger(value, (next) => {
2052
- if (next > 0) settings.compaction.excludeLastTurns = next;
2053
- });
2511
+ if (value === "true" || value === "false") settings.context.compaction.auto = value === "true";
2054
2512
  return;
2055
2513
  case "compaction.threshold": {
2056
2514
  const parsed = Number(value);
2057
- if (Number.isFinite(parsed) && parsed >= 0 && parsed <= 1) settings.compaction.threshold = parsed;
2515
+ if (Number.isFinite(parsed) && parsed >= 0 && parsed <= 1) settings.context.compaction.threshold = parsed;
2058
2516
  return;
2059
2517
  }
2060
2518
  case "compaction.model": {
2061
2519
  const trimmed = value.trim();
2062
- if (trimmed) settings.compaction.model = trimmed;
2063
- else delete settings.compaction.model;
2520
+ if (trimmed) settings.context.compaction.model = trimmed;
2521
+ else delete settings.context.compaction.model;
2064
2522
  return;
2065
2523
  }
2066
2524
  case "compaction.systemPrompt": {
2067
2525
  const trimmed = value.trim();
2068
- if (trimmed) settings.compaction.systemPrompt = trimmed;
2069
- else delete settings.compaction.systemPrompt;
2526
+ if (trimmed) settings.context.compaction.systemPrompt = trimmed;
2527
+ else delete settings.context.compaction.systemPrompt;
2070
2528
  return;
2071
2529
  }
2072
2530
  case "retry.enabled":
2073
- if (value === "true" || value === "false") settings.retry.enabled = value === "true";
2531
+ if (value === "true" || value === "false") settings.chat.retry.enabled = value === "true";
2074
2532
  return;
2075
2533
  case "retry.maxRetries":
2076
2534
  applyNonNegativeInteger(value, (next) => {
2077
- settings.retry.maxRetries = next;
2535
+ settings.chat.retry.maxRetries = next;
2078
2536
  });
2079
2537
  return;
2080
2538
  case "retry.baseDelayMs":
2081
2539
  applyNonNegativeInteger(value, (next) => {
2082
- settings.retry.baseDelayMs = next;
2540
+ settings.chat.retry.baseDelayMs = next;
2083
2541
  });
2084
2542
  return;
2085
2543
  case "retry.maxDelayMs":
2086
2544
  applyNonNegativeInteger(value, (next) => {
2087
- settings.retry.maxDelayMs = next;
2545
+ settings.chat.retry.maxDelayMs = next;
2088
2546
  });
2089
2547
  return;
2090
2548
  case "terminal.showTerminalProgress":
2091
- if (value === "true" || value === "false") settings.terminal.showTerminalProgress = value === "true";
2549
+ if (value === "true" || value === "false") settings.interface.terminalProgress = value === "true";
2092
2550
  return;
2093
2551
  case "terminal.outputVerbosity":
2094
- if (value === "minimal" || value === "default" || value === "verbose") settings.terminal.outputVerbosity = value;
2552
+ if (value === "minimal" || value === "default" || value === "verbose") settings.interface.outputDetail = value;
2095
2553
  return;
2096
2554
  case "terminal.tuiMode":
2097
- if (value === "regular" || value === "fullscreen") settings.terminal.tuiMode = value;
2555
+ if (value === "regular" || value === "fullscreen") settings.interface.mode = value;
2098
2556
  return;
2099
2557
  case "terminal.fullscreenScrollbar":
2100
2558
  if (value === "hidden" || value === "auto" || value === "always") {
2101
- settings.terminal.fullscreenScrollbar = value;
2559
+ settings.interface.fullscreenScrollbar = value;
2102
2560
  }
2103
2561
  return;
2104
2562
  case "terminal.notify":
2105
- if (value === "true" || value === "false") settings.terminal.notify = value === "true";
2563
+ if (value === "true" || value === "false") settings.interface.desktopNotifications = value === "true";
2106
2564
  return;
2107
2565
  case "watchdog.enabled":
2108
- if (value === "true" || value === "false") settings.watchdog.enabled = value === "true";
2566
+ if (value === "true" || value === "false") settings.safety.review.enabled = value === "true";
2109
2567
  return;
2110
2568
  // Both watchdog options are absent-by-default, and an empty submission is
2111
2569
  // how the operator says "go back to the default" from a text row. Deleting
@@ -2113,82 +2571,80 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
2113
2571
  // config validator accepts.
2114
2572
  case "watchdog.target": {
2115
2573
  const trimmed = value.trim();
2116
- if (trimmed) settings.watchdog.target = trimmed;
2117
- else delete settings.watchdog.target;
2574
+ if (trimmed) settings.safety.review.target = trimmed;
2575
+ else delete settings.safety.review.target;
2118
2576
  return;
2119
2577
  }
2120
2578
  case "watchdog.cadenceToolCalls":
2121
2579
  applyNumberSetting("watchdog.cadenceToolCalls", value, (next) => {
2122
- if (next === null) delete settings.watchdog.cadenceToolCalls;
2123
- else settings.watchdog.cadenceToolCalls = next;
2580
+ if (next === null) delete settings.safety.review.cadenceToolCalls;
2581
+ else settings.safety.review.cadenceToolCalls = next;
2124
2582
  });
2125
2583
  return;
2126
2584
  case "terminal.smoothStreaming":
2127
- if (value === "off" || value === "auto" || value === "on") settings.terminal.smoothStreaming = value;
2585
+ if (value === "off" || value === "auto" || value === "on") settings.interface.smoothStreaming = value;
2128
2586
  return;
2129
2587
  case "runtimePlugins":
2130
- settings.runtimePlugins = value
2588
+ settings.integrations.runtimePlugins = value
2131
2589
  .split(",")
2132
2590
  .map((entry) => entry.trim())
2133
2591
  .filter(Boolean);
2134
2592
  return;
2135
2593
  case "delegation.defaults.connectTimeoutMs":
2136
2594
  applyNumberSetting("delegation.defaults.connectTimeoutMs", value, (next) => {
2137
- if (next !== null) settings.delegation.defaults.connectTimeoutMs = next;
2595
+ if (next !== null) settings.integrations.externalAgents.defaults.connectTimeoutMs = next;
2138
2596
  });
2139
2597
  return;
2140
2598
  case "delegation.defaults.turnTimeoutMs":
2141
2599
  applyNumberSetting("delegation.defaults.turnTimeoutMs", value, (next) => {
2142
- if (next !== null) settings.delegation.defaults.turnTimeoutMs = next;
2600
+ if (next !== null) settings.integrations.externalAgents.defaults.turnTimeoutMs = next;
2143
2601
  });
2144
2602
  return;
2145
2603
  case "delegation.defaults.permissionTimeoutMs":
2146
2604
  applyNumberSetting("delegation.defaults.permissionTimeoutMs", value, (next) => {
2147
- if (next !== null) settings.delegation.defaults.permissionTimeoutMs = next;
2605
+ if (next !== null) settings.integrations.externalAgents.defaults.permissionTimeoutMs = next;
2148
2606
  });
2149
2607
  return;
2150
2608
  case "orchestrator.target": {
2151
2609
  const target = value === "(unset)" || value === "" ? null : value;
2152
2610
  // Switching targets re-bases the model on the new target default.
2153
- if (target !== settings.orchestrator.target) {
2154
- settings.orchestrator.model = target
2155
- ? (settings.targets.find((entry) => entry.id === target)?.defaultModel ?? null)
2156
- : null;
2611
+ if (target !== settings.chat.target) {
2612
+ settings.chat.model = target ? (settings.targets.find((entry) => entry.id === target)?.defaultModel ?? null) : null;
2157
2613
  }
2158
- settings.orchestrator.target = target;
2614
+ settings.chat.target = target;
2159
2615
  return;
2160
2616
  }
2161
2617
  case "orchestrator.model":
2162
- settings.orchestrator.model = value === "(unset)" || value === "" ? null : value;
2618
+ settings.chat.model = value === "(unset)" || value === "" ? null : value;
2163
2619
  return;
2164
2620
  case "background.target": {
2165
2621
  const target = value.startsWith("(unset") || value === "" ? null : value;
2166
- if (target !== settings.background.target) {
2167
- settings.background.model = target
2622
+ if (target !== settings.context.memory.target) {
2623
+ settings.context.memory.model = target
2168
2624
  ? (settings.targets.find((entry) => entry.id === target)?.defaultModel ?? null)
2169
2625
  : null;
2170
2626
  }
2171
- settings.background.target = target;
2627
+ settings.context.memory.target = target;
2172
2628
  return;
2173
2629
  }
2174
2630
  case "background.model":
2175
- settings.background.model = value === "(unset)" || value === "" ? null : value;
2631
+ settings.context.memory.model = value === "(unset)" || value === "" ? null : value;
2176
2632
  return;
2177
2633
  case "workers.default.target": {
2178
2634
  const target = value === "(unset)" || value === "" ? null : value;
2179
- if (target !== settings.workers.default.target) {
2180
- settings.workers.default.model = target
2635
+ if (target !== settings.fleet.default.target) {
2636
+ settings.fleet.default.model = target
2181
2637
  ? (settings.targets.find((entry) => entry.id === target)?.defaultModel ?? null)
2182
2638
  : null;
2183
2639
  }
2184
- settings.workers.default.target = target;
2640
+ settings.fleet.default.target = target;
2185
2641
  return;
2186
2642
  }
2187
2643
  case "workers.default.model":
2188
- settings.workers.default.model = value === "(unset)" || value === "" ? null : value;
2644
+ settings.fleet.default.model = value === "(unset)" || value === "" ? null : value;
2189
2645
  return;
2190
2646
  case "scope":
2191
- settings.scope =
2647
+ settings.chat.modelPicker.cycleSet =
2192
2648
  parseScopedModelSelection(value) ??
2193
2649
  value
2194
2650
  .split(",")
@@ -2197,9 +2653,98 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
2197
2653
  return;
2198
2654
  case "budget.sessionCeilingUsd":
2199
2655
  applyNumberSetting("budget.sessionCeilingUsd", value, (next) => {
2200
- if (next !== null) settings.budget.sessionCeilingUsd = next;
2656
+ if (next !== null) settings.safety.limits.sessionCostUsd = next;
2657
+ });
2658
+ return;
2659
+ case "workers.escalation.timeoutMs":
2660
+ applyNumberSetting("workers.escalation.timeoutMs", value, (next) => {
2661
+ if (next === null) return;
2662
+ settings.fleet.permissions.escalation = {
2663
+ ...(settings.fleet.permissions.escalation ?? SHIPPED_ESCALATION),
2664
+ timeoutMs: next,
2665
+ };
2666
+ });
2667
+ return;
2668
+ case "workers.escalation.fallback": {
2669
+ if (value !== "deny" && value !== "fail") return;
2670
+ settings.fleet.permissions.escalation = {
2671
+ ...(settings.fleet.permissions.escalation ?? SHIPPED_ESCALATION),
2672
+ fallback: value,
2673
+ };
2674
+ return;
2675
+ }
2676
+ case "workers.resilienceCooldownMs":
2677
+ applyNumberSetting("workers.resilienceCooldownMs", value, (next) => {
2678
+ if (next !== null) settings.fleet.retry.routeCooldownMs = next;
2679
+ });
2680
+ return;
2681
+ case "routing.activeRoles": {
2682
+ const outcome = parseEnumListSetting(value, ACTIVE_ROUTING_ROLES);
2683
+ if ("values" in outcome) settings.fleet.adaptiveRouting.roles = outcome.values as ActiveRoutingRole[];
2684
+ return;
2685
+ }
2686
+ case "routing.activePostures": {
2687
+ const outcome = parseEnumListSetting(value, ACTIVE_ROUTING_POSTURES);
2688
+ if ("values" in outcome) settings.fleet.adaptiveRouting.postures = outcome.values as ActiveRoutingPosture[];
2689
+ return;
2690
+ }
2691
+ case "prewarm.enabled":
2692
+ if (value === "true" || value === "false") settings.chat.prewarm = value === "true";
2693
+ return;
2694
+ case "guardrails.turnToolCallBudget":
2695
+ case "guardrails.workerToolCallCap":
2696
+ case "guardrails.maxDispatchRuns":
2697
+ case "guardrails.readMaxBytes":
2698
+ case "guardrails.observationTurnBudgetBytes":
2699
+ case "guardrails.internalDispatchTimeoutMs":
2700
+ applyNumberSetting(id, value, (next) => {
2701
+ if (next === null) return;
2702
+ if (id === "guardrails.turnToolCallBudget") settings.safety.limits.chatToolCallsPerTurn = next;
2703
+ else if (id === "guardrails.workerToolCallCap") settings.fleet.limits.toolCallsPerRun = next;
2704
+ else if (id === "guardrails.maxDispatchRuns") settings.fleet.history.maxRuns = next;
2705
+ else if (id === "guardrails.readMaxBytes") settings.safety.limits.readBytesPerCall = next;
2706
+ else if (id === "guardrails.observationTurnBudgetBytes") settings.safety.limits.observationBytesPerTurn = next;
2707
+ else settings.fleet.limits.internalRunTimeoutMs = next;
2708
+ });
2709
+ return;
2710
+ case "context.workingSet.enabled":
2711
+ if (value === "true" || value === "false") settings.context.workingSet.enabled = value === "true";
2712
+ return;
2713
+ case "context.workingSet.policy":
2714
+ if (value === "structural-v1" || value === "age-horizon") settings.context.workingSet.policy = value;
2715
+ return;
2716
+ case "context.workingSet.target": {
2717
+ // The validator's bound is exclusive at both ends, so an eviction target
2718
+ // of 0 or 1 is refused here rather than written and rejected at boot.
2719
+ const parsed = Number(value);
2720
+ if (Number.isFinite(parsed) && parsed > 0 && parsed < 1) settings.context.workingSet.target = parsed;
2721
+ return;
2722
+ }
2723
+ case "context.workingSet.protectLastTurns":
2724
+ applyNonNegativeInteger(value, (next) => {
2725
+ if (next >= 1) settings.context.workingSet.protectLastTurns = next;
2726
+ });
2727
+ return;
2728
+ case "context.workingSet.minEvictableTokens":
2729
+ applyNonNegativeInteger(value, (next) => {
2730
+ settings.context.workingSet.minEvictableTokens = next;
2201
2731
  });
2202
2732
  return;
2733
+ case "retry.streamStallMs":
2734
+ applyNonNegativeInteger(value, (next) => {
2735
+ settings.chat.retry.streamStallMs = next;
2736
+ });
2737
+ return;
2738
+ case "library.catalog":
2739
+ case "library.remote": {
2740
+ const key = id.slice("library.".length) as "catalog" | "remote";
2741
+ const trimmed = value.trim();
2742
+ settings.integrations.library[key] = trimmed.length > 0 ? trimmed : null;
2743
+ return;
2744
+ }
2745
+ case "library.sync":
2746
+ if (value === "true" || value === "false") settings.integrations.library.sync = value === "true";
2747
+ return;
2203
2748
  }
2204
2749
  }
2205
2750
 
@@ -2219,11 +2764,11 @@ function applyEntrySettingChange(settings: ClioSettings, id: string, value: stri
2219
2764
  const legacySegments = id.split(".");
2220
2765
  const legacyField = legacySegments.at(-1);
2221
2766
  const usesLegacyField =
2222
- !settings.workers.profiles[summaryName] && ["target", "model", "thinkingLevel", "node"].includes(legacyField ?? "");
2767
+ !settings.fleet.profiles[summaryName] && ["target", "model", "thinkingLevel", "node"].includes(legacyField ?? "");
2223
2768
  const field = usesLegacyField ? legacyField : encoded.shift();
2224
2769
  const name = usesLegacyField ? legacySegments.slice(2, -1).join(".") : summaryName;
2225
2770
  const fieldValue = usesLegacyField ? value : encoded.join(PROFILE_FIELD_SEPARATOR);
2226
- const profile = settings.workers.profiles[name];
2771
+ const profile = settings.fleet.profiles[name];
2227
2772
  if (!profile) return true;
2228
2773
  if (value === REMOVE_PROFILE_CHOICE) {
2229
2774
  removeFleetProfileFromSettings(settings, name);
@@ -2241,7 +2786,7 @@ function applyEntrySettingChange(settings: ClioSettings, id: string, value: stri
2241
2786
  }
2242
2787
  if (id.startsWith("workers.agentBindings.")) {
2243
2788
  const agentId = id.slice("workers.agentBindings.".length);
2244
- if (value === UNBIND_CHOICE) delete settings.workers.agentBindings[agentId];
2789
+ if (value === UNBIND_CHOICE) delete settings.fleet.agentProfiles[agentId];
2245
2790
  else bindAgentProfileInSettings(settings, agentId, value);
2246
2791
  return true;
2247
2792
  }
@@ -2265,7 +2810,7 @@ function changedLeafPaths(before: Readonly<ClioSettings>, after: Readonly<ClioSe
2265
2810
  v !== null && typeof v === "object" && !Array.isArray(v);
2266
2811
  const visit = (a: unknown, b: unknown, path: string): void => {
2267
2812
  if (a === b) return;
2268
- const profileLeaf = path.startsWith("workers.profiles.") && path.split(".").length === 3;
2813
+ const profileLeaf = path.startsWith("fleet.profiles.") && path.split(".").length === 3;
2269
2814
  if (!isRecord(a) || !isRecord(b) || profileLeaf) {
2270
2815
  if (JSON.stringify(a) !== JSON.stringify(b)) out.push(path);
2271
2816
  return;
@@ -2325,9 +2870,9 @@ function propagationTiming(
2325
2870
  if (restartRequired) return "next-session";
2326
2871
  if (id.startsWith("targets.")) {
2327
2872
  if (selectedValue === "remove") return "next-dispatch";
2328
- return path.startsWith("orchestrator.") ? "now" : "next-dispatch";
2873
+ return path.startsWith("chat.") ? "now" : "next-dispatch";
2329
2874
  }
2330
- if (path.startsWith("workers.")) return "next-dispatch";
2875
+ if (path.startsWith("fleet.")) return "next-dispatch";
2331
2876
  return "now";
2332
2877
  }
2333
2878
 
@@ -2340,7 +2885,7 @@ function impactFor(propagation: SettingsChangePlan["propagation"]): string {
2340
2885
  return "takes effect now";
2341
2886
  }
2342
2887
 
2343
- export function createSettingsChangePlan(
2888
+ function createSettingsChangePlan(
2344
2889
  settings: Readonly<ClioSettings>,
2345
2890
  item: Pick<SettingsCenterItem, "id" | "label" | "currentValue" | "scope">,
2346
2891
  selectedValue: string,
@@ -3135,8 +3680,8 @@ export class SettingsCenter implements Component {
3135
3680
  list.onCancel = () => finish("cancel");
3136
3681
  const title = (width: number): string => formatScopeConfirmTitle(plan, width);
3137
3682
  const affectedBindings = plan.leaves
3138
- .filter((leaf) => leaf.path.startsWith("workers.agentBindings."))
3139
- .map((leaf) => leaf.path.slice("workers.agentBindings.".length));
3683
+ .filter((leaf) => leaf.path.startsWith("fleet.agentProfiles."))
3684
+ .map((leaf) => leaf.path.slice("fleet.agentProfiles.".length));
3140
3685
  const bindingPreflight =
3141
3686
  plan.selectedValue === REMOVE_PROFILE_CHOICE
3142
3687
  ? `Affected agent routes: ${affectedBindings.length > 0 ? affectedBindings.join(", ") : "none"} · `
@@ -3145,10 +3690,10 @@ export class SettingsCenter implements Component {
3145
3690
  if (!plan.rowId.startsWith("targets.") || plan.selectedValue !== "remove") return "";
3146
3691
  const paths = (prefix: string): string[] =>
3147
3692
  plan.leaves.filter((leaf) => leaf.path.startsWith(prefix)).map((leaf) => leaf.path);
3148
- const profiles = paths("workers.profiles.").map((path) => path.slice("workers.profiles.".length));
3693
+ const profiles = paths("fleet.profiles.").map((path) => path.slice("fleet.profiles.".length));
3149
3694
  const describe = (label: string, affected: readonly string[]): string =>
3150
3695
  `Affected ${label}: ${affected.length > 0 ? affected.join(", ") : "none"}`;
3151
- return `${describe("chat route", paths("orchestrator."))} · ${describe("fleet route", paths("workers.default."))} · ${describe("memory route", paths("background."))} · ${describe("profiles", profiles)} · `;
3696
+ return `${describe("chat route", paths("chat."))} · ${describe("fleet route", paths("fleet.default."))} · ${describe("memory route", paths("context.memory."))} · ${describe("profiles", profiles)} · `;
3152
3697
  })();
3153
3698
  const note = `${bindingPreflight}${targetRemovalPreflight}Affects ${plan.leaves.map((leaf) => leaf.path).join(", ")} · ${plan.impact}`;
3154
3699
  this.submenuComponent = new SubmenuWrapper(title, list, buildHint([{ key: "Enter", verb: "choose" }], "back"), note);
@@ -3297,12 +3842,15 @@ export class SettingsCenter implements Component {
3297
3842
  }
3298
3843
 
3299
3844
  /**
3300
- * The footer is help, and the list is the work. A fixed six-line ceiling spent
3301
- * a third of a 20-row body on prose, so the budget is what the body can spare
3302
- * once the list keeps six rows.
3845
+ * The footer is the selected setting's detail surface at two-lane widths, so
3846
+ * it may use every row the list does not need. A fixed four-line ceiling left
3847
+ * a dozen blank body rows at an 80-column terminal while cutting the autonomy
3848
+ * explanation after its first sentence. Keeping six list rows makes the full
3849
+ * prose reachable when height is available and still degrades to no footer on
3850
+ * a short terminal.
3303
3851
  */
3304
3852
  private footerBudget(bodyHeight: number): number {
3305
- return Math.min(4, Math.max(0, bodyHeight - 6));
3853
+ return Math.max(0, bodyHeight - 6);
3306
3854
  }
3307
3855
 
3308
3856
  private renderSectionLane(width: number, height: number): string[] {
@@ -3468,15 +4016,27 @@ export class SettingsCenter implements Component {
3468
4016
  const query = this.filterQuery.trim().length > 0 ? ` ${theme.fg("accent", `/${this.filterQuery}`)}` : "";
3469
4017
  const positionText = `${theme.fg("dim", `section ${position}/${sections.length}`)}${query}`;
3470
4018
 
4019
+ // The note is prose the operator acts on ("choose session, global, or
4020
+ // cancel"), so it wraps under the same rule as the description above it. A
4021
+ // truncated instruction is the one line in this footer that cannot afford
4022
+ // to be a fragment.
4023
+ const noteLines = (text: string, tone: "dim" | "muted"): string[] =>
4024
+ wrapTextWithAnsi(theme.fg(tone, text), safeWidth);
4025
+
3471
4026
  if (this.level === "sections") {
3472
4027
  const breadcrumb = `${screenTitle(theme, section.label)} ${theme.fg("dim", "·")} ${positionText}`;
3473
4028
  const body = wrapTextWithAnsi(theme.fg("muted", SETTINGS_SECTION_DESCRIPTIONS[section.id]), safeWidth);
3474
- const note = theme.fg("dim", "Tab or → to edit its settings");
3475
- return this.assembleFooter([separator, breadcrumb], body, note, maxFooterLines, safeWidth);
4029
+ return this.assembleFooter(
4030
+ [separator, breadcrumb],
4031
+ body,
4032
+ noteLines(this.footerNoteText(), "dim"),
4033
+ maxFooterLines,
4034
+ safeWidth,
4035
+ );
3476
4036
  }
3477
4037
 
3478
4038
  if (!item) {
3479
- return this.assembleFooter([separator], [], theme.fg("muted", "No setting selected."), maxFooterLines, safeWidth);
4039
+ return this.assembleFooter([separator], [], noteLines(this.footerNoteText(), "muted"), maxFooterLines, safeWidth);
3480
4040
  }
3481
4041
 
3482
4042
  const breadcrumb = `${screenTitle(theme, section.label)} ${theme.fg("dim", "›")} ${theme.style("accent", item.label, { bold: true })} ${theme.fg("dim", "·")} ${positionText}`;
@@ -3485,8 +4045,27 @@ export class SettingsCenter implements Component {
3485
4045
  if (item.help) contentLines.push(...wrapTextWithAnsi(theme.fg("dim", item.help), safeWidth));
3486
4046
  const detail = this.footerDetail(item, theme);
3487
4047
  if (detail) contentLines.push(...wrapTextWithAnsi(detail, safeWidth));
3488
- const note = theme.fg("dim", truncateToWidth(this.footerScopeNote(item), safeWidth, ELLIPSIS, true));
3489
- return this.assembleFooter([separator, breadcrumb], contentLines, note, maxFooterLines, safeWidth);
4048
+ return this.assembleFooter(
4049
+ [separator, breadcrumb],
4050
+ contentLines,
4051
+ noteLines(this.footerScopeNote(item), "dim"),
4052
+ maxFooterLines,
4053
+ safeWidth,
4054
+ );
4055
+ }
4056
+
4057
+ /**
4058
+ * The note text renderFooter will show, without theming. `footerBudget` needs
4059
+ * its wrapped height before the footer is rendered, so both read it here and
4060
+ * the ceiling can never disagree with what lands.
4061
+ */
4062
+ private footerNoteText(): string {
4063
+ const section = this.currentSection();
4064
+ if (!section) return "";
4065
+ if (this.level === "sections") return "Tab or → to edit its settings";
4066
+ const item = this.selectedItem();
4067
+ if (!item) return "No setting selected.";
4068
+ return this.footerScopeNote(item);
3490
4069
  }
3491
4070
 
3492
4071
  private footerDetail(item: SettingsCenterItem, theme: ReturnType<typeof clioTheme>): string {
@@ -3523,7 +4102,7 @@ export class SettingsCenter implements Component {
3523
4102
  private assembleFooter(
3524
4103
  top: readonly string[],
3525
4104
  middle: readonly string[],
3526
- note: string,
4105
+ note: readonly string[],
3527
4106
  maxFooterLines: number,
3528
4107
  width: number,
3529
4108
  ): string[] {
@@ -3532,7 +4111,11 @@ export class SettingsCenter implements Component {
3532
4111
  if (maxFooterLines <= top.length) {
3533
4112
  out = top.slice(0, maxFooterLines).map(fit);
3534
4113
  } else {
3535
- const middleBudget = Math.max(0, maxFooterLines - top.length - 1);
4114
+ // The note owns the rows it wrapped to, up to whatever is left under the
4115
+ // breadcrumb; the explanation fills the rest. Dropping a wrapped note
4116
+ // line loses less than cutting the sentence mid-word did.
4117
+ const noteKept = note.slice(0, Math.max(1, maxFooterLines - top.length));
4118
+ const middleBudget = Math.max(0, maxFooterLines - top.length - noteKept.length);
3536
4119
  // The explanation is wrapped, so a short terminal drops whole lines off
3537
4120
  // its end rather than cutting one. At 40 columns the autonomy help
3538
4121
  // stopped at "read-only observes; suggest" and read as the whole
@@ -3541,7 +4124,7 @@ export class SettingsCenter implements Component {
3541
4124
  const last = kept.at(-1);
3542
4125
  const marked =
3543
4126
  middle.length > kept.length && last !== undefined ? [...kept.slice(0, -1), `${last}${ELLIPSIS}`] : kept;
3544
- out = [...top.map(fit), ...marked.map(fit), fit(note)];
4127
+ out = [...top.map(fit), ...marked.map(fit), ...noteKept.map(fit)];
3545
4128
  }
3546
4129
  while (out.length < maxFooterLines) out.push("");
3547
4130
  return out.slice(0, maxFooterLines);
@@ -3635,25 +4218,22 @@ export function openSettingsOverlay(tui: TUI, deps: OpenSettingsOverlayDeps): Se
3635
4218
  center.refreshItems();
3636
4219
  tui.requestRender();
3637
4220
  };
3638
- // The engine composites an overlay across the columns its region covers, and
3639
- // side margins shrink that region, so this margin is re-read every frame and
3640
- // drops to zero on an ultra-narrow terminal rather than leaving transcript
3641
- // columns beside a modal that owns the keyboard.
3642
- const margin: { top: number; right: number; bottom: number; left: number } = { ...SETTINGS_OVERLAY_MARGIN };
3643
4221
  const handle = showClioOverlayFrame(tui, center, {
3644
4222
  anchor: "top-left",
3645
4223
  width: SETTINGS_OVERLAY_WIDTH,
3646
4224
  maxHeight: SETTINGS_OVERLAY_MAX_HEIGHT,
3647
- margin,
3648
- visible: (terminalWidth) => {
3649
- const side = terminalWidth < ULTRA_NARROW_TERMINAL_WIDTH ? 0 : SETTINGS_OVERLAY_MARGIN.left;
3650
- margin.left = side;
3651
- margin.right = side;
3652
- return true;
3653
- },
4225
+ // A capturing overlay must own every horizontal cell. Leaving even a
4226
+ // two-column compositor margin exposes arbitrary transcript and footer
4227
+ // fragments beside the frame, where they read as modal content.
4228
+ margin: SETTINGS_OVERLAY_MARGIN,
4229
+ markerId: "settings",
3654
4230
  title: "Settings",
3655
- footerHint: (innerWidth) =>
3656
- innerWidth < WIDE_LAYOUT_MIN_WIDTH
4231
+ footerHint: (innerWidth) => {
4232
+ // The submenu renders its own authoritative choose/back legend. Keeping
4233
+ // the parent row controls in the outer border at the same time advertises
4234
+ // actions (preview, open, filter) that this input depth does not route.
4235
+ if (center.getSelection().submenuOpen) return undefined;
4236
+ return innerWidth < WIDE_LAYOUT_MIN_WIDTH
3657
4237
  ? buildHint(
3658
4238
  [
3659
4239
  { key: "↑↓", verb: "move" },
@@ -3670,7 +4250,8 @@ export function openSettingsOverlay(tui: TUI, deps: OpenSettingsOverlayDeps): Se
3670
4250
  { key: "/", verb: "filter" },
3671
4251
  ],
3672
4252
  "back",
3673
- ),
4253
+ );
4254
+ },
3674
4255
  });
3675
4256
  return Object.assign(handle, { refreshRows });
3676
4257
  }