@iowarp/clio-coder 0.3.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (552) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/NOTICE +33 -0
  3. package/README.md +12 -3
  4. package/dist/{acp-U67UHUK2.js → acp-G5WJBNCT.js} +13 -12
  5. package/dist/{agents-YU6SGALZ.js → agents-FMV2Q5G4.js} +41 -36
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5ZPJOIVG.js → auth-3IDSJEIK.js} +18 -18
  8. package/dist/{builtins-C6JMZVV6.js → builtins-XCZWXSC7.js} +5 -5
  9. package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
  10. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  11. package/dist/{chunk-XWSF374K.js → chunk-2OQE55CK.js} +3 -3
  12. package/dist/{chunk-5H3GB5BO.js → chunk-32KWKNSF.js} +8 -384
  13. package/dist/{chunk-KTYTFRMB.js → chunk-36EJLSQQ.js} +34 -36
  14. package/dist/{chunk-FHJEP5SW.js → chunk-3BT2XMV4.js} +19 -13
  15. package/dist/chunk-3DPEIQKN.js +113 -0
  16. package/dist/{chunk-GU2UIAFZ.js → chunk-3URVFKWK.js} +7 -7
  17. package/dist/{chunk-WEH5XRJQ.js → chunk-3XML7CDN.js} +3 -3
  18. package/dist/chunk-42FMPA75.js +101 -0
  19. package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
  20. package/dist/{chunk-VCBR6CU7.js → chunk-5PSMVOLM.js} +2 -2
  21. package/dist/{chunk-NMPKI6XL.js → chunk-6DB53AJS.js} +203 -25
  22. package/dist/{chunk-3BINW3FP.js → chunk-76ONBSIA.js} +2 -2
  23. package/dist/{chunk-26LEYJZH.js → chunk-7MCTRUCE.js} +2 -2
  24. package/dist/{chunk-TYPGUK6W.js → chunk-AB44T6BB.js} +111 -5
  25. package/dist/{chunk-K4XHGFR5.js → chunk-B7OBL7PK.js} +317 -721
  26. package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
  27. package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
  28. package/dist/{chunk-ZVJ5BLO2.js → chunk-BKFJHQCA.js} +154 -16
  29. package/dist/chunk-BKFM6EJV.js +462 -0
  30. package/dist/chunk-BMS5RKQY.js +27 -0
  31. package/dist/{chunk-ZNLWCMVZ.js → chunk-BPKCPIL7.js} +2 -2
  32. package/dist/{chunk-TLQJPP24.js → chunk-BUMFYQFY.js} +1394 -1349
  33. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  34. package/dist/chunk-C2LTL2W6.js +2447 -0
  35. package/dist/{chunk-ME6CCNFO.js → chunk-CODPRO7Q.js} +8 -8
  36. package/dist/{chunk-E77JEWSD.js → chunk-CTJ4RNAA.js} +7 -37
  37. package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
  38. package/dist/{chunk-7RFXX52T.js → chunk-DQITNCXG.js} +642 -172
  39. package/dist/{chunk-TTHACPOM.js → chunk-DYJP44XW.js} +578 -117
  40. package/dist/{chunk-2HFZQUHL.js → chunk-F4CKPOEQ.js} +18 -8
  41. package/dist/{chunk-DGSYXYMX.js → chunk-FEFIFZTL.js} +3 -3
  42. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  43. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  44. package/dist/chunk-GKF55TAZ.js +391 -0
  45. package/dist/{chunk-VWZOAB7K.js → chunk-GR5G2PVF.js} +9 -8
  46. package/dist/{chunk-4DGYLA73.js → chunk-GXNLGKAB.js} +80 -9
  47. package/dist/chunk-HHV2GANA.js +88 -0
  48. package/dist/{chunk-IIZWH4XA.js → chunk-HI63TFOG.js} +5 -4
  49. package/dist/{chunk-WLFILSD5.js → chunk-HJJTYUHX.js} +113 -83
  50. package/dist/{chunk-TT36MB5S.js → chunk-HLW2MRKE.js} +3 -1
  51. package/dist/{chunk-PMDBGQSJ.js → chunk-HWHKMHUA.js} +7 -7
  52. package/dist/chunk-HZHHCK24.js +1631 -0
  53. package/dist/{chunk-WSB3FPX7.js → chunk-I5VEOC6I.js} +39 -143
  54. package/dist/chunk-IBEBSCYA.js +564 -0
  55. package/dist/chunk-IQ7KR472.js +362 -0
  56. package/dist/{chunk-A3WNZD3P.js → chunk-J4W7KFM7.js} +949 -972
  57. package/dist/{chunk-TB5666IT.js → chunk-JDG2WCRO.js} +5 -5
  58. package/dist/{chunk-N22QMJKY.js → chunk-K5C3NCBD.js} +4 -4
  59. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  60. package/dist/{chunk-5C3AQNDW.js → chunk-KFZI4NIL.js} +216 -38
  61. package/dist/chunk-KMVISBZR.js +132 -0
  62. package/dist/{chunk-WXY7KU3G.js → chunk-LDQ2ZF2M.js} +2 -2
  63. package/dist/{chunk-XN3L4EYL.js → chunk-LQ3DZAMX.js} +3 -3
  64. package/dist/{chunk-U6MBIEMB.js → chunk-LY4S7GJC.js} +173 -144
  65. package/dist/{chunk-4SPRNWDE.js → chunk-MLKNTWH2.js} +19 -19
  66. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  67. package/dist/chunk-NEKRRTYW.js +56 -0
  68. package/dist/{chunk-SPULKLCF.js → chunk-NHCZP4K7.js} +3 -3
  69. package/dist/chunk-NHLBIGRH.js +1506 -0
  70. package/dist/chunk-NQQH3YT7.js +302 -0
  71. package/dist/chunk-NYS75XW5.js +15 -0
  72. package/dist/{chunk-GN57SG4G.js → chunk-O4XIVISU.js} +10 -8
  73. package/dist/{chunk-EMYUUSFG.js → chunk-O6TL7WWY.js} +6 -6
  74. package/dist/chunk-OQBA45DZ.js +97 -0
  75. package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
  76. package/dist/chunk-PMZCIOCJ.js +25 -0
  77. package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
  78. package/dist/{chunk-J3YUBZWY.js → chunk-QBJA7R7N.js} +62 -6
  79. package/dist/chunk-QDC3K2U3.js +262 -0
  80. package/dist/{chunk-2HEJ2F35.js → chunk-QLFS5GO2.js} +22 -10
  81. package/dist/{chunk-7RGZWPB6.js → chunk-QLL7ILRG.js} +95 -32
  82. package/dist/{chunk-YS5VLNH5.js → chunk-QREDIESB.js} +6 -6
  83. package/dist/chunk-QSNYB6ZV.js +195 -0
  84. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  85. package/dist/chunk-RKKLTLYB.js +45 -0
  86. package/dist/{chunk-P43ETTHK.js → chunk-SJ5ZKQ4S.js} +2 -2
  87. package/dist/{chunk-GPIEI3LY.js → chunk-SP2RXXYO.js} +6 -54
  88. package/dist/chunk-SUCTJL45.js +45 -0
  89. package/dist/{chunk-DYIM5TJT.js → chunk-SUW5DORT.js} +263 -7
  90. package/dist/chunk-T56WDKA5.js +183 -0
  91. package/dist/chunk-TVHHYFHE.js +255 -0
  92. package/dist/{chunk-FJ3H4MN5.js → chunk-TZ3SGWZZ.js} +3 -3
  93. package/dist/{chunk-MXKJU4JB.js → chunk-U77AMWDL.js} +91 -10
  94. package/dist/{chunk-5DHKRSMQ.js → chunk-ULC6OTWO.js} +11 -7
  95. package/dist/{chunk-RWSI4YD7.js → chunk-UM7N4G5A.js} +33 -12
  96. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  97. package/dist/{chunk-IGWKHNIQ.js → chunk-UXMFQ54G.js} +44 -37
  98. package/dist/{chunk-HFSBBKSQ.js → chunk-V5DHCITQ.js} +171 -3
  99. package/dist/{chunk-5WIGXA4T.js → chunk-VAZSBTKF.js} +111 -4
  100. package/dist/{chunk-VHN4MY6O.js → chunk-VEO4AP2K.js} +2 -2
  101. package/dist/{chunk-IJ7RPIYJ.js → chunk-VFA6GDY5.js} +65 -4
  102. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  103. package/dist/chunk-VO67MWHC.js +75 -0
  104. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  105. package/dist/{chunk-TANS5ZJS.js → chunk-VYMXRQI6.js} +36 -22
  106. package/dist/{chunk-WWCZ5F23.js → chunk-W5VSYASO.js} +77 -16
  107. package/dist/{chunk-WNIJTQQK.js → chunk-WZR7K7ZX.js} +72 -116
  108. package/dist/{chunk-5Q2VVUKB.js → chunk-X3YGUTOB.js} +4 -4
  109. package/dist/chunk-X75E3D2N.js +686 -0
  110. package/dist/{chunk-VAWNZU7Z.js → chunk-YDFRH54B.js} +4 -4
  111. package/dist/chunk-YJX4SHTD.js +40 -0
  112. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  113. package/dist/chunk-Z2RR6MAK.js +127 -0
  114. package/dist/{chunk-FBVTI2TJ.js → chunk-Z4TXYIEG.js} +12 -131
  115. package/dist/cli/index.js +47 -36
  116. package/dist/{clio-QVTYJ57A.js → clio-2JXHBBY5.js} +7 -7
  117. package/dist/{code-nav-FGGFIE7L.js → code-nav-3YYRMYNF.js} +8 -8
  118. package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
  119. package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
  120. package/dist/{config-LW5IJFQN.js → config-QZPCMYSO.js} +99 -63
  121. package/dist/{configure-7XIZCOU4.js → configure-TEGEBYCA.js} +23 -22
  122. package/dist/{context-Y6Y7QPR6.js → context-AV7OEZ4D.js} +12 -12
  123. package/dist/{context-L3WL3X7K.js → context-E6H5RNMC.js} +56 -47
  124. package/dist/{context-N52ZA626.js → context-GSXUE4CT.js} +29 -27
  125. package/dist/{context-clear-MBQRLSDQ.js → context-clear-SHIBYK6T.js} +55 -46
  126. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  127. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-5ZGKPGZQ.js} +13 -13
  128. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-EFMJT4LD.js} +93 -64
  129. package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
  130. package/dist/doctor-QOA5FNY5.js +313 -0
  131. package/dist/{eval-BEC2WHDA.js → eval-TFBYQH4H.js} +2032 -156
  132. package/dist/eval-inventory-SXH7PDKX.js +316 -0
  133. package/dist/{evidence-REJUMSKM.js → evidence-ERGESKGN.js} +203 -48
  134. package/dist/{evolve-PY5ZBA5K.js → evolve-VDXTSYCJ.js} +52 -43
  135. package/dist/{extensions-HVKU65YU.js → extensions-7BGBHN57.js} +13 -7
  136. package/dist/{fleet-7WZEWRFA.js → fleet-2RRVDF2V.js} +228 -108
  137. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-VJ726XIA.js} +11 -11
  138. package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
  139. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-JF5QOATM.js} +18 -15
  140. package/dist/fleet-inspect-VLY4S7QM.js +442 -0
  141. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-AIZUEJOY.js} +5 -5
  142. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-AJRPDMDV.js} +22 -19
  143. package/dist/fleet-verify-JFEL2L3H.js +175 -0
  144. package/dist/fleet-view-ZCON35AG.js +102 -0
  145. package/dist/{init-OG3TPGQG.js → init-DN2WWLFE.js} +72 -62
  146. package/dist/install-XGLBQY5E.js +13 -0
  147. package/dist/interop-OZBKXAYL.js +114 -0
  148. package/dist/{library-CNTMPLRF.js → library-YWZG7IMW.js} +21 -18
  149. package/dist/{memory-6IS7F275.js → memory-I4C4HMLW.js} +54 -45
  150. package/dist/{models-ENRJDA5W.js → models-CEYXJBO6.js} +33 -30
  151. package/dist/{monitor-XLDVO7TN.js → monitor-NZ6GCI3P.js} +59 -52
  152. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-GCGQ4N5I.js} +7827 -7328
  153. package/dist/panes-HMABYVO4.js +58 -0
  154. package/dist/panes-KY6W3V2E.js +103 -0
  155. package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
  156. package/dist/{reset-RZ4ER727.js → reset-DQ6FGCSH.js} +13 -11
  157. package/dist/resources-BB3MVJMD.js +111 -0
  158. package/dist/{run-Y2CNK5RU.js → run-H2GQDUER.js} +119 -87
  159. package/dist/{share-A55GYP6Z.js → share-GTJN6A5O.js} +20 -17
  160. package/dist/{skills-ALC5J6AT.js → skills-L55TEW6R.js} +33 -24
  161. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-XVXPH2JI.js} +67 -56
  162. package/dist/skills-inventory-S4MXPJFV.js +126 -0
  163. package/dist/slash-commands-ZSGASKJC.js +77 -0
  164. package/dist/{steer-GGWFUJUD.js → steer-RZGSCY4R.js} +4 -4
  165. package/dist/{support-MIETYA5E.js → support-PKEUNNQL.js} +6 -6
  166. package/dist/{targets-VGNXIR3S.js → targets-NCPZ644J.js} +68 -40
  167. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-44SV3YCN.js} +6 -4
  168. package/dist/tools-DAF3DI3C.js +27 -0
  169. package/dist/{trace-PNCASAXC.js → trace-FYVW2MQA.js} +207 -10
  170. package/dist/tui-primitives-2AKXQNZK.js +13 -0
  171. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
  172. package/dist/{upgrade-FUSUAGHR.js → upgrade-3XPP6OQL.js} +27 -24
  173. package/dist/{usage-N4MKVHKD.js → usage-3NLHGTU2.js} +114 -62
  174. package/dist/{verifiers-YAWOJ3H2.js → verifiers-SSQONKRT.js} +172 -13
  175. package/dist/{verify-LTDHYBGY.js → verify-3U6J7FZI.js} +10 -10
  176. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
  177. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-CEHYGPGQ.js} +75 -65
  178. package/dist/with-panes-MKB46MPQ.js +782 -0
  179. package/dist/worker/entry.js +106 -75
  180. package/docs/README.md +3 -2
  181. package/docs/acp.md +24 -3
  182. package/docs/alcf-provider.md +1 -1
  183. package/docs/architecture.md +2 -2
  184. package/docs/artifact-versions.md +9 -1
  185. package/docs/built-in-agents.md +1 -1
  186. package/docs/capacity-and-scheduling.md +62 -3
  187. package/docs/commands-and-modes.md +31 -2
  188. package/docs/configuration-and-targets.md +69 -12
  189. package/docs/context-engine.md +63 -4
  190. package/docs/development-pipeline.md +19 -0
  191. package/docs/dispatch-typed-intent.md +385 -0
  192. package/docs/documentation-coverage.md +5 -5
  193. package/docs/documentation-guide.md +1 -1
  194. package/docs/environment-variables.md +3 -0
  195. package/docs/eval-runner.md +262 -11
  196. package/docs/evals-internal.md +72 -2
  197. package/docs/evidence-and-memory.md +12 -11
  198. package/docs/evolution.md +1 -1
  199. package/docs/exit-codes-and-output.md +1 -1
  200. package/docs/extensions-and-sharing.md +27 -1
  201. package/docs/fleet-dispatch.md +25 -4
  202. package/docs/installation-and-lifecycle.md +15 -2
  203. package/docs/middleware-and-components.md +1 -1
  204. package/docs/model-catalog.md +10 -1
  205. package/docs/observability.md +55 -4
  206. package/docs/proactive-memory.md +127 -14
  207. package/docs/prompt-envelope-and-tools.md +19 -1
  208. package/docs/provider-adapter-cookbook.md +1 -1
  209. package/docs/release-cut-checklist.md +19 -3
  210. package/docs/safety-model.md +2 -2
  211. package/docs/scientific-validation.md +3 -3
  212. package/docs/session-lifecycle.md +1 -1
  213. package/docs/skills-marketplace.md +1 -1
  214. package/docs/tool-usage.md +18 -8
  215. package/docs/trace-store.md +1 -1
  216. package/docs/troubleshooting.md +88 -1
  217. package/docs/tui-design.md +1 -1
  218. package/docs/worker-dispatch-mechanics.md +1 -1
  219. package/package.json +5 -2
  220. package/src/cli/acp.ts +6 -2
  221. package/src/cli/agents.ts +1 -1
  222. package/src/cli/argv.ts +25 -0
  223. package/src/cli/config-inspect.ts +33 -6
  224. package/src/cli/config.ts +1 -1
  225. package/src/cli/configure.ts +23 -21
  226. package/src/cli/doctor-panes.ts +124 -0
  227. package/src/cli/doctor-state-size.ts +82 -0
  228. package/src/cli/doctor-toolchain.ts +57 -0
  229. package/src/cli/doctor.ts +22 -1
  230. package/src/cli/eval-inventory.ts +436 -0
  231. package/src/cli/eval.ts +93 -16
  232. package/src/cli/evidence-detail.ts +88 -0
  233. package/src/cli/evidence-inventory.ts +183 -0
  234. package/src/cli/evidence.ts +30 -5
  235. package/src/cli/extensions.ts +5 -1
  236. package/src/cli/fleet-decisions.ts +69 -0
  237. package/src/cli/fleet-inspect.ts +334 -0
  238. package/src/cli/fleet-verify.ts +133 -0
  239. package/src/cli/fleet-view.ts +810 -0
  240. package/src/cli/fleet.ts +179 -39
  241. package/src/cli/index.ts +14 -2
  242. package/src/cli/interop-inspect.ts +128 -0
  243. package/src/cli/interop.ts +34 -0
  244. package/src/cli/panes.ts +35 -0
  245. package/src/cli/reset.ts +5 -2
  246. package/src/cli/run.ts +58 -0
  247. package/src/cli/skills-inventory.ts +185 -0
  248. package/src/cli/skills.ts +16 -13
  249. package/src/cli/targets.ts +44 -13
  250. package/src/cli/tools.ts +321 -0
  251. package/src/cli/trace-inspect.ts +252 -0
  252. package/src/cli/trace.ts +85 -5
  253. package/src/cli/usage.ts +63 -14
  254. package/src/cli/verifiers-inspect.ts +347 -0
  255. package/src/cli/verifiers.ts +9 -0
  256. package/src/core/bus-events.ts +33 -1
  257. package/src/core/cache-telemetry.ts +42 -0
  258. package/src/core/config.ts +67 -0
  259. package/src/core/defaults.ts +124 -8
  260. package/src/core/endpoint-key.ts +27 -0
  261. package/src/core/residency-target-key.ts +25 -0
  262. package/src/core/response-schema.ts +80 -6
  263. package/src/core/theme-token-hex.ts +43 -0
  264. package/src/core/tool-names.ts +2 -1
  265. package/src/core/xdg.ts +1 -1
  266. package/src/domains/agents/fleets/build-review.md +0 -3
  267. package/src/domains/agents/fleets/build-test.md +0 -3
  268. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  269. package/src/domains/agents/result-contract.ts +164 -35
  270. package/src/domains/config/classify.ts +6 -0
  271. package/src/domains/context/codewiki/coordinator.ts +12 -4
  272. package/src/domains/dispatch/admission-error.ts +9 -0
  273. package/src/domains/dispatch/admission.ts +52 -14
  274. package/src/domains/dispatch/capacity-lease.ts +118 -9
  275. package/src/domains/dispatch/contract.ts +11 -0
  276. package/src/domains/dispatch/council-topology.ts +398 -0
  277. package/src/domains/dispatch/execution-plan.ts +44 -4
  278. package/src/domains/dispatch/extension.ts +378 -82
  279. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  280. package/src/domains/dispatch/fleet-plan.ts +7 -2
  281. package/src/domains/dispatch/fleet-run.ts +87 -4
  282. package/src/domains/dispatch/gate-decisions.ts +11 -1
  283. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  284. package/src/domains/dispatch/gate-topology.ts +289 -0
  285. package/src/domains/dispatch/heartbeat.ts +32 -8
  286. package/src/domains/dispatch/index.ts +22 -0
  287. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  288. package/src/domains/dispatch/intent.ts +85 -1
  289. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  290. package/src/domains/dispatch/reservation-store.ts +139 -11
  291. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  292. package/src/domains/dispatch/run-event-journal.ts +598 -0
  293. package/src/domains/dispatch/state.ts +49 -1
  294. package/src/domains/dispatch/types.ts +13 -0
  295. package/src/domains/dispatch/validation.ts +33 -8
  296. package/src/domains/dispatch/worker-spawn.ts +25 -11
  297. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  298. package/src/domains/dispatch/write-boundary.ts +62 -1
  299. package/src/domains/eval/artifacts/store.ts +62 -0
  300. package/src/domains/eval/compare/behavioral.ts +224 -0
  301. package/src/domains/eval/compare/compare.ts +342 -2
  302. package/src/domains/eval/compare/envelope.ts +128 -0
  303. package/src/domains/eval/compare/gates.ts +24 -6
  304. package/src/domains/eval/compare/thresholds.ts +30 -3
  305. package/src/domains/eval/execution-provenance.ts +240 -0
  306. package/src/domains/eval/inventory.ts +113 -0
  307. package/src/domains/eval/metrics/aggregate.ts +136 -0
  308. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  309. package/src/domains/eval/metrics/tracked.ts +413 -0
  310. package/src/domains/eval/provenance.ts +117 -0
  311. package/src/domains/eval/reports/comparison.ts +128 -0
  312. package/src/domains/eval/reports/junit.ts +17 -3
  313. package/src/domains/eval/reports/markdown.ts +3 -3
  314. package/src/domains/eval/reports/text.ts +14 -0
  315. package/src/domains/eval/run-compare.ts +20 -0
  316. package/src/domains/eval/runners/clio-run.ts +127 -0
  317. package/src/domains/eval/runners/external-command.ts +28 -3
  318. package/src/domains/eval/schema/adapter.ts +111 -0
  319. package/src/domains/eval/schema/artifact.ts +20 -0
  320. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  321. package/src/domains/eval/schema/behavioral.ts +520 -0
  322. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  323. package/src/domains/eval/schema/serving.ts +105 -0
  324. package/src/domains/eval/schema/suite.ts +38 -8
  325. package/src/domains/eval/schema/validate.ts +58 -3
  326. package/src/domains/eval/schema/verdict.ts +237 -0
  327. package/src/domains/eval/suites/resolve.ts +2 -0
  328. package/src/domains/eval/suites/run.ts +264 -33
  329. package/src/domains/eval/verifiers/command.ts +2 -1
  330. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  331. package/src/domains/evidence/build.ts +2 -13
  332. package/src/domains/evidence/eval.ts +2 -12
  333. package/src/domains/evidence/findings-markdown.ts +33 -0
  334. package/src/domains/evidence/run-trust.ts +7 -113
  335. package/src/domains/evidence/store.ts +6 -0
  336. package/src/domains/evidence/trust-projection.ts +2 -2
  337. package/src/domains/extensions/compatibility.ts +285 -0
  338. package/src/domains/extensions/discovery.ts +38 -3
  339. package/src/domains/extensions/resources.ts +1 -1
  340. package/src/domains/extensions/state.ts +12 -3
  341. package/src/domains/extensions/types.ts +2 -0
  342. package/src/domains/lifecycle/doctor.ts +69 -1
  343. package/src/domains/memory/index.ts +14 -1
  344. package/src/domains/memory/task-bank-promotion.ts +64 -0
  345. package/src/domains/memory/task-memory-policy.ts +82 -17
  346. package/src/domains/memory/task-memory-spend.ts +131 -0
  347. package/src/domains/memory/task-memory-status.ts +7 -0
  348. package/src/domains/memory/task-memory-telemetry.ts +3 -0
  349. package/src/domains/middleware/index.ts +1 -0
  350. package/src/domains/middleware/memory-intervention.ts +97 -21
  351. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  352. package/src/domains/mux/contract.ts +434 -0
  353. package/src/domains/mux/detect.ts +158 -0
  354. package/src/domains/mux/extension.ts +47 -0
  355. package/src/domains/mux/index.ts +96 -0
  356. package/src/domains/mux/manifest.ts +6 -0
  357. package/src/domains/mux/operations.ts +164 -0
  358. package/src/domains/mux/pane-registry.ts +90 -0
  359. package/src/domains/mux/protocol.ts +49 -0
  360. package/src/domains/mux/socket-client.ts +816 -0
  361. package/src/domains/mux/types.ts +222 -0
  362. package/src/domains/mux/viewer-command.ts +59 -0
  363. package/src/domains/mux/yazi/assets/init.lua +2 -0
  364. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  365. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  366. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  367. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  368. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  369. package/src/domains/mux/yazi/event-stream.ts +180 -0
  370. package/src/domains/mux/yazi/profile.ts +299 -0
  371. package/src/domains/mux/yazi/session.ts +228 -0
  372. package/src/domains/mux/yazi/theme.ts +30 -0
  373. package/src/domains/observability/background-memory-usage.ts +140 -0
  374. package/src/domains/observability/cost.ts +22 -1
  375. package/src/domains/observability/index.ts +9 -0
  376. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  377. package/src/domains/observability/trace-store.ts +234 -2
  378. package/src/domains/prompts/compiler.ts +100 -13
  379. package/src/domains/providers/endpoint-capacity.ts +228 -0
  380. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  381. package/src/domains/providers/extension.ts +20 -3
  382. package/src/domains/providers/index.ts +32 -0
  383. package/src/domains/providers/model-runtime-capabilities.ts +32 -0
  384. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  385. package/src/domains/providers/runtime-resolution.ts +8 -1
  386. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  387. package/src/domains/providers/runtimes/builtins.ts +2 -0
  388. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  389. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  390. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  391. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  392. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  393. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  394. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  395. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  396. package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
  397. package/src/domains/providers/support.ts +1 -0
  398. package/src/domains/providers/target-model-cache.ts +124 -0
  399. package/src/domains/providers/types/capability-flags.ts +2 -0
  400. package/src/domains/providers/types/target-descriptor.ts +2 -0
  401. package/src/domains/resources/index.ts +3 -0
  402. package/src/domains/resources/prompts/loader.ts +95 -33
  403. package/src/domains/resources/skills/loader.ts +33 -0
  404. package/src/domains/safety/action-classifier.ts +6 -0
  405. package/src/domains/safety/call-target.ts +52 -0
  406. package/src/domains/safety/run-effects.ts +35 -4
  407. package/src/domains/session/context-accounting.ts +52 -1
  408. package/src/domains/session/context-ledger.ts +37 -13
  409. package/src/domains/session/index.ts +6 -0
  410. package/src/domains/session/prompt-cache.ts +140 -0
  411. package/src/domains/session/prompt-manifest.ts +42 -0
  412. package/src/domains/toolchain/archive.ts +175 -0
  413. package/src/domains/toolchain/contract.ts +28 -0
  414. package/src/domains/toolchain/extension.ts +47 -0
  415. package/src/domains/toolchain/index.ts +39 -0
  416. package/src/domains/toolchain/install.ts +327 -0
  417. package/src/domains/toolchain/manifest.ts +8 -0
  418. package/src/domains/toolchain/paths.ts +34 -0
  419. package/src/domains/toolchain/registry.ts +265 -0
  420. package/src/domains/toolchain/remove.ts +218 -0
  421. package/src/domains/toolchain/resolve.ts +182 -0
  422. package/src/domains/toolchain/types.ts +113 -0
  423. package/src/domains/toolchain/version.ts +88 -0
  424. package/src/engine/acp/adapter.ts +18 -3
  425. package/src/engine/acp/server.ts +413 -70
  426. package/src/engine/acp/types.ts +19 -1
  427. package/src/engine/ai.ts +35 -0
  428. package/src/engine/apis/llamacpp-residency.ts +55 -3
  429. package/src/engine/apis/lmstudio.ts +25 -5
  430. package/src/engine/apis/ollama-native.ts +2 -1
  431. package/src/engine/apis/openai-completions.ts +80 -17
  432. package/src/engine/apis/residency-lock.ts +3 -1
  433. package/src/engine/apis/residency.ts +34 -1
  434. package/src/engine/claude/sdk-module.ts +98 -0
  435. package/src/engine/claude/sdk-runtime.ts +19 -11
  436. package/src/engine/provider-payload.ts +29 -1
  437. package/src/engine/tui-primitives.ts +21 -0
  438. package/src/engine/tui.ts +1 -0
  439. package/src/engine/worker-runtime.ts +2 -13
  440. package/src/entry/boot-options.ts +2 -0
  441. package/src/entry/orchestrator.ts +279 -35
  442. package/src/entry/panes-activation.ts +31 -0
  443. package/src/entry/with-panes.ts +20 -0
  444. package/src/interactive/chat-loop-messages.ts +26 -7
  445. package/src/interactive/chat-loop.ts +318 -41
  446. package/src/interactive/chat-panel.ts +62 -8
  447. package/src/interactive/clio-editor.ts +45 -8
  448. package/src/interactive/context-activity.ts +5 -1
  449. package/src/interactive/context-meter.ts +1 -1
  450. package/src/interactive/context-overlay.ts +41 -10
  451. package/src/interactive/cost-overlay.ts +66 -6
  452. package/src/interactive/council-grid.ts +1 -3
  453. package/src/interactive/council.ts +11 -0
  454. package/src/interactive/dispatch-board.ts +102 -20
  455. package/src/interactive/fleet-run-preview.ts +41 -15
  456. package/src/interactive/handoff-round.ts +41 -2
  457. package/src/interactive/interactive-application.ts +177 -7
  458. package/src/interactive/interactive-input-runtime.ts +15 -0
  459. package/src/interactive/interactive-presentation.ts +4 -0
  460. package/src/interactive/interactive-shell.ts +20 -17
  461. package/src/interactive/interactive-slash-runtime.ts +63 -10
  462. package/src/interactive/memory-overlay.ts +9 -0
  463. package/src/interactive/modal-marker.ts +170 -0
  464. package/src/interactive/mutation-preview.ts +295 -0
  465. package/src/interactive/mux-bridge.ts +214 -0
  466. package/src/interactive/overlay-frame.ts +58 -2
  467. package/src/interactive/overlay-general-openers.ts +17 -0
  468. package/src/interactive/overlay-key-routing.ts +52 -3
  469. package/src/interactive/overlay-lifecycle.ts +56 -7
  470. package/src/interactive/overlay-model-selectors.ts +40 -3
  471. package/src/interactive/overlay-permission-lifecycle.ts +112 -24
  472. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  473. package/src/interactive/overlay-transitions.ts +18 -4
  474. package/src/interactive/overlays/agents.ts +1 -0
  475. package/src/interactive/overlays/ask-user.ts +227 -49
  476. package/src/interactive/overlays/auth-dialog.ts +1 -0
  477. package/src/interactive/overlays/context-reset.ts +1 -0
  478. package/src/interactive/overlays/cwd-fallback.ts +1 -0
  479. package/src/interactive/overlays/decisions.ts +11 -11
  480. package/src/interactive/overlays/extensions.ts +1 -0
  481. package/src/interactive/overlays/fleet-run-approval.ts +1 -0
  482. package/src/interactive/overlays/handoff-review.ts +1 -0
  483. package/src/interactive/overlays/help-reference.ts +6 -0
  484. package/src/interactive/overlays/interop.ts +1 -0
  485. package/src/interactive/overlays/library-install-confirm.ts +1 -0
  486. package/src/interactive/overlays/library-tabs.ts +28 -0
  487. package/src/interactive/overlays/list-overlay.ts +10 -1
  488. package/src/interactive/overlays/message-picker.ts +1 -0
  489. package/src/interactive/overlays/model-scope.ts +86 -0
  490. package/src/interactive/overlays/model-selector.ts +1 -0
  491. package/src/interactive/overlays/prompts.ts +12 -1
  492. package/src/interactive/overlays/session-selector.ts +1 -0
  493. package/src/interactive/overlays/settings-sections.ts +30 -0
  494. package/src/interactive/overlays/settings.ts +614 -45
  495. package/src/interactive/overlays/side-question.ts +1 -0
  496. package/src/interactive/overlays/skills-hub.ts +3 -11
  497. package/src/interactive/overlays/tree-selector.ts +1 -0
  498. package/src/interactive/pane-policy.ts +46 -0
  499. package/src/interactive/panes-runtime.ts +292 -0
  500. package/src/interactive/permission-hint.ts +34 -2
  501. package/src/interactive/permission-overlay.ts +159 -9
  502. package/src/interactive/prewarm.ts +197 -0
  503. package/src/interactive/render-trace.ts +162 -15
  504. package/src/interactive/renderers/compaction-summary.ts +29 -0
  505. package/src/interactive/renderers/tool-execution.ts +4 -0
  506. package/src/interactive/renderers/worker-entry.ts +122 -14
  507. package/src/interactive/side-question.ts +58 -1
  508. package/src/interactive/slash-commands.ts +251 -15
  509. package/src/interactive/status/controller.ts +11 -0
  510. package/src/interactive/status/state-machine.ts +54 -2
  511. package/src/interactive/status/types.ts +7 -0
  512. package/src/interactive/tasks-overlay.ts +1 -0
  513. package/src/interactive/terminal-lease.ts +2 -0
  514. package/src/interactive/theme/tokens.ts +3 -14
  515. package/src/interactive/turn-context.ts +346 -33
  516. package/src/interactive/turn-persistence.ts +14 -4
  517. package/src/interactive/turn-prewarm.ts +364 -0
  518. package/src/interactive/turn-queues.ts +7 -4
  519. package/src/interactive/turn-runtime.ts +8 -1
  520. package/src/interactive/turn-state.ts +23 -0
  521. package/src/interactive/view/artifacts.ts +109 -1
  522. package/src/interactive/view/view-overlay.ts +29 -3
  523. package/src/interactive/watch-pane.ts +152 -0
  524. package/src/interactive/worker-progress.ts +7 -1
  525. package/src/interactive/worker-receipts.ts +19 -1
  526. package/src/interactive/worker-stream.ts +5 -0
  527. package/src/interactive/yazi-bridge.ts +444 -0
  528. package/src/tools/ask-user.ts +43 -2
  529. package/src/tools/bootstrap.ts +26 -2
  530. package/src/tools/builtin-tool-catalog.ts +15 -0
  531. package/src/tools/compete-worktrees.ts +83 -2
  532. package/src/tools/core-bootstrap.ts +2 -1
  533. package/src/tools/dispatch-admission.ts +14 -3
  534. package/src/tools/dispatch-arguments.ts +20 -20
  535. package/src/tools/dispatch-plan.ts +17 -9
  536. package/src/tools/dispatch-run-events.ts +134 -19
  537. package/src/tools/dispatch-runner.ts +29 -7
  538. package/src/tools/dispatch-scout.ts +1 -1
  539. package/src/tools/dispatch-types.ts +15 -3
  540. package/src/tools/dispatch.ts +1 -1
  541. package/src/tools/executables.ts +17 -14
  542. package/src/tools/observation.ts +54 -4
  543. package/src/tools/panes-surface.ts +38 -0
  544. package/src/tools/panes.ts +112 -0
  545. package/src/tools/policy.ts +10 -1
  546. package/src/tools/presentation.ts +1 -0
  547. package/src/tools/registry.ts +16 -0
  548. package/dist/chunk-AOCYTWAV.js +0 -449
  549. package/dist/chunk-HLE42MG7.js +0 -37
  550. package/dist/chunk-HWUFFB6L.js +0 -83
  551. package/dist/chunk-JOZYP4GM.js +0 -279
  552. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -6,9 +6,19 @@ import {
6
6
  setFleetProfileInSettings,
7
7
  useTargetInSettings,
8
8
  } from "../../core/config.js";
9
- import { DEFAULT_SETTINGS, THINKING_LEVELS } from "../../core/defaults.js";
9
+ import {
10
+ ACTIVE_ROUTING_POSTURES,
11
+ ACTIVE_ROUTING_ROLES,
12
+ type ActiveRoutingPosture,
13
+ type ActiveRoutingRole,
14
+ DEFAULT_SETTINGS,
15
+ THINKING_LEVELS,
16
+ type WorkerEscalationSettings,
17
+ } from "../../core/defaults.js";
18
+ import type { GuardrailValues } from "../../core/guardrails.js";
10
19
  import { getAtPath, isRoutingPath } from "../../core/session-routing.js";
11
20
  import { MAX_TIMER_DELAY_MS } from "../../core/timers.js";
21
+ import { capacityLeaseUsage } from "../../domains/dispatch/capacity-lease.js";
12
22
  import {
13
23
  delegationEntryForKind,
14
24
  type InteropAgentId,
@@ -17,6 +27,8 @@ import {
17
27
  } from "../../domains/interop/index.js";
18
28
  import {
19
29
  type CapabilityFlags,
30
+ endpointCapacitiesForStatuses,
31
+ endpointCapacityForStatus,
20
32
  isDispatchEligibleRuntime,
21
33
  isOrchestratorEligibleRuntime,
22
34
  type ProvidersContract,
@@ -45,6 +57,7 @@ import { clockLocal } from "../format-time.js";
45
57
  import { buildHint, DEFAULT_SELECT_THEME, showClioOverlayFrame } from "../overlay-frame.js";
46
58
  import { barSep, clioTheme, GLYPH, padAnsi, rule, screenTitle } from "../theme/index.js";
47
59
  import { modelsForTarget } from "./model-selector.js";
60
+ import { SETTINGS_SECTIONS, type SettingsSectionId } from "./settings-sections.js";
48
61
 
49
62
  export const SETTINGS_OVERLAY_WIDTH = "100%";
50
63
  export const SETTINGS_OVERLAY_MAX_HEIGHT = "100%";
@@ -90,6 +103,16 @@ const ULTRA_NARROW_TERMINAL_WIDTH = 60;
90
103
  /** Shown when no runtime is resolvable, so it offers the full vocabulary. */
91
104
  const FALLBACK_THINKING_VALUES: ReadonlyArray<string> = THINKING_LEVELS;
92
105
  const ROW_GAP = " ";
106
+ /**
107
+ * `workers.escalation` and `workers.resilienceCooldownMs` are optional keys, so
108
+ * the row that shows them and the mutation that writes them both need a
109
+ * definite shipped value to fall back to.
110
+ */
111
+ const SHIPPED_ESCALATION: WorkerEscalationSettings = DEFAULT_SETTINGS.workers.escalation ?? {
112
+ timeoutMs: 120_000,
113
+ fallback: "deny",
114
+ };
115
+ const SHIPPED_RESILIENCE_COOLDOWN_MS = DEFAULT_SETTINGS.workers.resilienceCooldownMs ?? 15_000;
93
116
  /**
94
117
  * The product's cut marker, as the help center and every list overlay use it.
95
118
  *
@@ -123,36 +146,27 @@ const RESTART_REQUIRED_IDS = new Set<string>([
123
146
  "runtimePlugins",
124
147
  "terminal.tuiMode",
125
148
  "terminal.fullscreenScrollbar",
149
+ // The mux domain runs its detection ladder once, at boot, from the value this
150
+ // key held then. Changing the rung mid-session would leave the contract on
151
+ // the rung it resolved to, so the row says restart rather than lying.
152
+ "panes.enabled",
126
153
  ]);
127
154
 
128
- export const SETTINGS_SECTIONS = [
129
- { id: "safety", label: "Autonomy & Safety", group: "CORE" },
130
- { id: "orchestrator", label: "Orchestrator", group: "CORE" },
131
- { id: "fleet", label: "Fleet", group: "ROUTING" },
132
- { id: "targets", label: "Targets", group: "ROUTING" },
133
- { id: "models", label: "Models", group: "ROUTING" },
134
- { id: "budget", label: "Budget", group: "RUNTIME" },
135
- { id: "compaction", label: "Compaction", group: "RUNTIME" },
136
- { id: "retry", label: "Retry", group: "RUNTIME" },
137
- { id: "terminal", label: "Terminal", group: "EXPERIENCE" },
138
- { id: "watchdog", label: "Watchdog", group: "EXPERIENCE" },
139
- { id: "advanced", label: "Advanced", group: "EXPERIENCE" },
140
- ] as const;
141
-
142
- export type SettingsSectionId = (typeof SETTINGS_SECTIONS)[number]["id"];
155
+ export { SETTINGS_SECTIONS, type SettingsSectionId };
143
156
 
144
157
  const SETTINGS_SECTION_DESCRIPTIONS = {
145
- safety: "How freely Clio acts, and how delegated agents' tools are governed.",
146
- orchestrator: "Interactive chat routing and the optional proactive-memory model plane.",
147
- fleet: "Defaults, profiles, and agent bindings applied to dispatched workers, and where they run.",
158
+ safety: "How freely Clio acts, how a worker's approval ask resolves, and how delegated agents' tools are governed.",
159
+ orchestrator: "Interactive chat routing, prompt pre-warm, and the optional proactive-memory model plane.",
160
+ fleet: "Defaults, profiles, agent bindings, and route activation for dispatched workers, and where they run.",
148
161
  targets: "Configured inference targets: which one chat and the fleet use, and whether each answers.",
149
162
  models: "The /model picker, favorites, and Alt+J / Alt+K cycling.",
150
- budget: "Cost ceiling, per-turn output budget, and worker concurrency.",
151
- compaction: "When and how the context window is summarized under pressure.",
152
- retry: "Automatic recovery from transient provider and network errors.",
163
+ budget: "Cost ceiling, per-turn output budget, worker concurrency, and the guardrail backstops.",
164
+ compaction: "When and how the context window is evicted and summarized under pressure.",
165
+ retry: "Automatic recovery from transient provider, network, and stalled-stream errors.",
153
166
  terminal: "Terminal integration and the Clio color palette.",
154
167
  watchdog: "The opt-in turn-end verifier run, where it is routed, and whether it also fires mid-turn.",
155
- advanced: "Commit provenance, runtime plugins, delegation timeouts, and links to other surfaces.",
168
+ advanced:
169
+ "Commit provenance, runtime plugins, delegation timeouts, the resource library, and links to other surfaces.",
156
170
  } as const satisfies Record<SettingsSectionId, string>;
157
171
 
158
172
  export const SETTINGS_LABELS_BY_ID = {
@@ -162,6 +176,8 @@ export const SETTINGS_LABELS_BY_ID = {
162
176
  // section headed Fleet whose rows read "Worker profiles" and "Worker
163
177
  // retries" made one setting look like two subsystems.
164
178
  "workers.onPermission": "Fleet approvals routing",
179
+ "workers.escalation.timeoutMs": "Escalation timeout (ms)",
180
+ "workers.escalation.fallback": "Escalation fallback",
165
181
  "delegation.defaults.toolGovernance": "Delegation governance",
166
182
  "skills.trustProjectCompatRoots": "Trust project skill roots",
167
183
  "attribution.gitCommits": "Clio commit provenance",
@@ -177,25 +193,49 @@ export const SETTINGS_LABELS_BY_ID = {
177
193
  "memory.intervention.windowSteps": "Memory trajectory steps",
178
194
  "memory.intervention.maxTokens": "Memory reminder tokens",
179
195
  "memory.intervention.timeoutMs": "Memory timeout (ms)",
196
+ "prewarm.enabled": "Prompt pre-warm",
180
197
  "workers.default.target": "Default target",
181
198
  "workers.default.model": "Default model",
182
199
  "workers.default.thinkingLevel": "Default thinking level",
183
200
  "workers.profiles": "Add profile",
184
201
  "workers.agentBindings": "Add agent route",
185
202
  "workers.maxRetries": "Fleet retries",
203
+ "workers.resilienceCooldownMs": "Resilience cooldown (ms)",
204
+ "routing.activeRoles": "Active routing roles",
205
+ "routing.activePostures": "Active routing postures",
206
+ "routing.agentAutomation.activeAgentRoles": "Active agent routes",
207
+ "panes.enabled": "Panes",
208
+ "panes.notifications": "Pane notifications",
209
+ "panes.journal": "Run event journal",
210
+ "panes.yazi.enabled": "Files pane",
211
+ "panes.yazi.mode": "Files pane mode",
212
+ "panes.yazi.profile": "Yazi profile",
213
+ "panes.yazi.followCwd": "Follow conversation cwd",
186
214
  scope: "Model cycle set",
187
215
  "modelSelector.recentLimit": "Recent models kept",
188
216
  "modelSelector.favorites": "Pinned favorites",
189
217
  "budget.sessionCeilingUsd": "Session ceiling (USD)",
190
218
  "defaults.maxTokens": "Output budget (tokens)",
191
219
  "budget.concurrency": "Fleet concurrency",
220
+ "guardrails.turnToolCallBudget": "Turn tool-call budget",
221
+ "guardrails.workerToolCallCap": "Worker tool-call cap",
222
+ "guardrails.maxDispatchRuns": "Run ledger retention",
223
+ "guardrails.readMaxBytes": "Read byte cap",
224
+ "guardrails.observationTurnBudgetBytes": "Observation byte pool",
225
+ "guardrails.internalDispatchTimeoutMs": "Internal dispatch timeout (ms)",
192
226
  "compaction.auto": "Auto-compact",
193
227
  "compaction.excludeLastTurns": "Protected recent turns",
194
228
  "compaction.threshold": "Compaction threshold",
229
+ "context.workingSet.enabled": "Working-set eviction",
230
+ "context.workingSet.policy": "Eviction policy",
231
+ "context.workingSet.target": "Eviction target pressure",
232
+ "context.workingSet.protectLastTurns": "Turns protected from eviction",
233
+ "context.workingSet.minEvictableTokens": "Minimum evictable tokens",
195
234
  "retry.enabled": "Retry transient errors",
196
235
  "retry.maxRetries": "Max retries",
197
236
  "retry.baseDelayMs": "Base delay (ms)",
198
237
  "retry.maxDelayMs": "Max delay (ms)",
238
+ "retry.streamStallMs": "Stream stall timeout (ms)",
199
239
  "terminal.showTerminalProgress": "Terminal progress badges",
200
240
  "terminal.outputVerbosity": "Output detail",
201
241
  "terminal.tuiMode": "TUI mode",
@@ -215,6 +255,10 @@ export const SETTINGS_LABELS_BY_ID = {
215
255
  targets: "Configured targets",
216
256
  keybindings: "Keybinding overrides",
217
257
  "delegation.agents": "Delegation agents",
258
+ "library.catalog": "Library catalog path",
259
+ "library.remote": "Library remote",
260
+ "library.confirmedRemote": "Confirmed library remote",
261
+ "library.sync": "Library sync",
218
262
  } as const;
219
263
 
220
264
  /**
@@ -225,11 +269,23 @@ type EntrySettingId =
225
269
  | `workers.profiles.${string}`
226
270
  | `workers.agentBindings.${string}`
227
271
  | `targets.${string}`
228
- | `fleet.nodes.${string}`;
272
+ | `fleet.nodes.${string}`
273
+ | `fleet.endpoints.${string}`;
229
274
  export type EditableSettingId = keyof typeof SETTINGS_LABELS_BY_ID | EntrySettingId;
230
- type FleetGroupHeaderId = `fleet.group.${"defaults" | "profiles" | "agent-routes" | "placement"}`;
275
+ type FleetGroupHeaderId =
276
+ `fleet.group.${"defaults" | "profiles" | "agent-routes" | "route-activation" | "placement" | "panes"}`;
277
+ type TerminalGroupHeaderId = "terminal.group.files-pane";
278
+ type BudgetGroupHeaderId = "budget.group.guardrails";
279
+ type CompactionGroupHeaderId = "compaction.group.working-set";
280
+ type AdvancedGroupHeaderId = "advanced.group.library";
281
+ type GroupHeaderId =
282
+ | FleetGroupHeaderId
283
+ | TerminalGroupHeaderId
284
+ | BudgetGroupHeaderId
285
+ | CompactionGroupHeaderId
286
+ | AdvancedGroupHeaderId;
231
287
  type TargetsCtaId = "targets.add-cta";
232
- export type SettingsCenterRowId = EditableSettingId | FleetGroupHeaderId | TargetsCtaId;
288
+ export type SettingsCenterRowId = EditableSettingId | GroupHeaderId | TargetsCtaId;
233
289
  const REMOVE_PROFILE_CHOICE = "(remove profile)";
234
290
  const UNBIND_CHOICE = "(unbind)";
235
291
  const AUTO_PLACEMENT_CHOICE = "(auto placement)";
@@ -240,6 +296,8 @@ export const SETTINGS_SECTION_ROWS = {
240
296
  safety: [
241
297
  "autonomy",
242
298
  "workers.onPermission",
299
+ "workers.escalation.timeoutMs",
300
+ "workers.escalation.fallback",
243
301
  "delegation.defaults.toolGovernance",
244
302
  "skills.trustProjectCompatRoots",
245
303
  "safetyNet",
@@ -256,19 +314,46 @@ export const SETTINGS_SECTION_ROWS = {
256
314
  "memory.intervention.windowSteps",
257
315
  "memory.intervention.maxTokens",
258
316
  "memory.intervention.timeoutMs",
317
+ "prewarm.enabled",
259
318
  ],
260
319
  fleet: [
261
320
  "workers.default.target",
262
321
  "workers.default.model",
263
322
  "workers.default.thinkingLevel",
264
323
  "workers.maxRetries",
324
+ "workers.resilienceCooldownMs",
265
325
  "workers.profiles",
266
326
  "workers.agentBindings",
327
+ "routing.activeRoles",
328
+ "routing.activePostures",
329
+ "routing.agentAutomation.activeAgentRoles",
330
+ "panes.enabled",
331
+ "panes.notifications",
332
+ "panes.journal",
267
333
  ],
268
334
  models: ["scope", "modelSelector.recentLimit", "modelSelector.favorites"],
269
- budget: ["budget.sessionCeilingUsd", "defaults.maxTokens", "budget.concurrency"],
270
- compaction: ["compaction.auto", "compaction.threshold", "compaction.excludeLastTurns"],
271
- retry: ["retry.enabled", "retry.maxRetries", "retry.baseDelayMs", "retry.maxDelayMs"],
335
+ budget: [
336
+ "budget.sessionCeilingUsd",
337
+ "defaults.maxTokens",
338
+ "budget.concurrency",
339
+ "guardrails.turnToolCallBudget",
340
+ "guardrails.workerToolCallCap",
341
+ "guardrails.maxDispatchRuns",
342
+ "guardrails.readMaxBytes",
343
+ "guardrails.observationTurnBudgetBytes",
344
+ "guardrails.internalDispatchTimeoutMs",
345
+ ],
346
+ compaction: [
347
+ "compaction.auto",
348
+ "compaction.threshold",
349
+ "compaction.excludeLastTurns",
350
+ "context.workingSet.enabled",
351
+ "context.workingSet.policy",
352
+ "context.workingSet.target",
353
+ "context.workingSet.protectLastTurns",
354
+ "context.workingSet.minEvictableTokens",
355
+ ],
356
+ retry: ["retry.enabled", "retry.maxRetries", "retry.baseDelayMs", "retry.maxDelayMs", "retry.streamStallMs"],
272
357
  terminal: [
273
358
  "terminal.showTerminalProgress",
274
359
  "terminal.outputVerbosity",
@@ -277,6 +362,10 @@ export const SETTINGS_SECTION_ROWS = {
277
362
  "terminal.smoothStreaming",
278
363
  "terminal.notify",
279
364
  "theme",
365
+ "panes.yazi.enabled",
366
+ "panes.yazi.mode",
367
+ "panes.yazi.profile",
368
+ "panes.yazi.followCwd",
280
369
  ],
281
370
  watchdog: ["watchdog.enabled", "watchdog.target", "watchdog.cadenceToolCalls"],
282
371
  advanced: [
@@ -289,6 +378,10 @@ export const SETTINGS_SECTION_ROWS = {
289
378
  "delegation.defaults.permissionTimeoutMs",
290
379
  "keybindings",
291
380
  "delegation.agents",
381
+ "library.catalog",
382
+ "library.remote",
383
+ "library.confirmedRemote",
384
+ "library.sync",
292
385
  ],
293
386
  targets: ["targets"],
294
387
  } as const satisfies Record<SettingsSectionId, readonly EditableSettingId[]>;
@@ -297,6 +390,8 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
297
390
  autonomy: "How freely Clio acts; the safety net always applies.",
298
391
  "workers.onPermission":
299
392
  "How a worker resolves an approval ask: deny the call, fail the run, or escalate to this session.",
393
+ "workers.escalation.timeoutMs": "How long an escalated worker approval waits for you before the fallback applies.",
394
+ "workers.escalation.fallback": "What an escalated approval becomes when nobody answers inside the timeout.",
300
395
  "delegation.defaults.toolGovernance": "Tool policy for delegated external agents.",
301
396
  "skills.trustProjectCompatRoots": "Whether third-party project skill roots are loaded.",
302
397
  "attribution.gitCommits":
@@ -313,25 +408,51 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
313
408
  "memory.intervention.windowSteps": "Recent completed tool steps visible to the memory policy.",
314
409
  "memory.intervention.maxTokens": "Hard cap for one visible memory reminder.",
315
410
  "memory.intervention.timeoutMs": "Hard deadline for one background-model memory call.",
411
+ "prewarm.enabled": "Send the next turn's known prefix ahead of time so a local server has already prefilled it.",
316
412
  "workers.default.target": "Default /run target id.",
317
413
  "workers.default.model": "Default /run wire model id.",
318
414
  "workers.default.thinkingLevel": "Reasoning budget for dispatched workers.",
319
415
  "workers.profiles": "Named target/model/thinking choices that native workers can use. Enter adds one.",
320
416
  "workers.agentBindings": "Pins native Clio agents, including shadow agents, to worker profiles. Enter adds one.",
321
417
  "workers.maxRetries": "Automatic retries for a retryable worker outcome.",
418
+ "workers.resilienceCooldownMs":
419
+ "How long a failing target, runtime, and model route is skipped before it is tried again.",
420
+ "routing.activeRoles": "Execution roles whose joint route selection may act instead of only shadowing.",
421
+ "routing.activePostures": "Route postures whose selection may act instead of only shadowing.",
422
+ "routing.agentAutomation.activeAgentRoles": "Exact agent and execution-role pairs whose agent choice may act.",
423
+ "panes.enabled": "Default panes activation for new sessions; `--with-panes` / `--no-panes` beat it.",
424
+ "panes.notifications": "Which terminal run states raise a pane-host toast.",
425
+ "panes.journal": "Whether every dispatched run's event tail is written to disk for `clio-coder fleet view`.",
426
+ "panes.yazi.enabled": "Whether `/panes open yazi` may open the files pane or one-shot chooser.",
427
+ "panes.yazi.mode": "Keep Yazi beside the conversation, or close it after one selection.",
428
+ "panes.yazi.profile": "Use Clio's managed pick profile or leave the operator's Yazi profile untouched.",
429
+ "panes.yazi.followCwd": "Push the conversation directory into an already-open companion pane.",
322
430
  scope: "Alt+J and Alt+K model cycle set.",
323
431
  "modelSelector.recentLimit": "How many recently used models /model remembers.",
324
432
  "modelSelector.favorites": "Exact target/model refs pinned in /model.",
325
433
  "budget.sessionCeilingUsd": "Per-session cost cap.",
326
434
  "defaults.maxTokens": "Output tokens requested per turn, applied to every target.",
327
435
  "budget.concurrency": "Parallel workers allowed during dispatch.",
436
+ "guardrails.turnToolCallBudget": "Soft per-turn tool-call budget for this chat loop.",
437
+ "guardrails.workerToolCallCap": "Lifetime ceiling on tool calls one dispatched worker may execute.",
438
+ "guardrails.maxDispatchRuns": "How many finished runs the dispatch ledger keeps before the oldest are dropped.",
439
+ "guardrails.readMaxBytes": "Per-call byte cap for the read tool.",
440
+ "guardrails.observationTurnBudgetBytes": "Shared per-turn byte pool across every observation-producing tool.",
441
+ "guardrails.internalDispatchTimeoutMs": "Wall-clock cap for one internal generator dispatch.",
328
442
  "compaction.auto": "Auto-compact before a turn when context crosses the threshold.",
329
443
  "compaction.excludeLastTurns": "Recent user turns protected from observation masking.",
330
444
  "compaction.threshold": "Pressure at which compaction masks stale observations, then summarizes.",
445
+ "context.workingSet.enabled": "Non-destructive eviction of stale tool results before a summary is ever needed.",
446
+ "context.workingSet.policy": "Which candidates the eviction pass selects.",
447
+ "context.workingSet.target": "Context pressure an applied eviction batch brings the session down to.",
448
+ "context.workingSet.protectLastTurns": "Recent user turns whose observations are never evicted.",
449
+ "context.workingSet.minEvictableTokens":
450
+ "Results below this token estimate stay; the marker would cost more than it saves.",
331
451
  "retry.enabled": "Retry transient provider errors on the next submit.",
332
452
  "retry.maxRetries": "Retry attempts after the initial failure.",
333
453
  "retry.baseDelayMs": "Initial retry delay in milliseconds.",
334
454
  "retry.maxDelayMs": "Maximum retry delay in milliseconds.",
455
+ "retry.streamStallMs": "Silence on an in-flight stream past this long is treated as a wedged backend.",
335
456
  "terminal.showTerminalProgress": "Emit OSC 9;4 progress badges during agent turns.",
336
457
  "terminal.outputVerbosity": "How much reasoning, tool input, and live tool output appears in the transcript.",
337
458
  "terminal.tuiMode": "Use regular terminal scrollback or a fullscreen transcript with a sticky composer and footer.",
@@ -352,6 +473,10 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
352
473
  targets: "Inference targets available for chat and workers. Add one with `clio-coder targets add`.",
353
474
  keybindings: "Custom key overrides layered on the defaults.",
354
475
  "delegation.agents": "External ACP agents available to /delegate.",
476
+ "library.catalog": "Path to the private resource catalog; blank uses the one in your config directory.",
477
+ "library.remote": "Git remote the catalog syncs with; blank keeps the library entirely local.",
478
+ "library.confirmedRemote": "The remote you confirmed. Sync refuses until it matches library.remote.",
479
+ "library.sync": "Whether `clio-coder library sync` and `push` may talk to the remote at all.",
355
480
  } as const satisfies Record<EditableSettingId, string>;
356
481
 
357
482
  /** Longer, optional guidance shown beneath the one-line description when there is room. */
@@ -362,6 +487,38 @@ const SETTINGS_HELP_BY_ID: Partial<Record<EditableSettingId, string>> = {
362
487
  "Clamped down to each model's max-output cap and the remaining context window. Set 0 to use per-model caps only.",
363
488
  "compaction.threshold":
364
489
  "pressure = estimated tokens ÷ context window. Higher keeps more history but risks overflow before a summary runs.",
490
+ "context.workingSet.enabled":
491
+ "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.",
492
+ "context.workingSet.policy":
493
+ "structural-v1 selects by message structure; age-horizon is the older age-based rule. Legal values: structural-v1, age-horizon · default: structural-v1.",
494
+ "context.workingSet.target":
495
+ "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.",
496
+ "context.workingSet.protectLastTurns":
497
+ "Counted in user turns, the same unit compaction.excludeLastTurns uses. Whole number of at least 1 · default: 6.",
498
+ "context.workingSet.minEvictableTokens":
499
+ "The floor sweep put marker break-even near 50 tokens; 200 is the churn guard. Whole number, 0 evicts anything · default: 200.",
500
+ "guardrails.turnToolCallBudget":
501
+ "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.",
502
+ "guardrails.workerToolCallCap":
503
+ "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.",
504
+ "guardrails.maxDispatchRuns":
505
+ "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.",
506
+ "guardrails.readMaxBytes":
507
+ "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.",
508
+ "guardrails.observationTurnBudgetBytes":
509
+ "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.",
510
+ "guardrails.internalDispatchTimeoutMs":
511
+ "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.",
512
+ "retry.streamStallMs":
513
+ "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).",
514
+ "library.catalog":
515
+ "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.",
516
+ "library.remote":
517
+ "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.",
518
+ "library.confirmedRemote":
519
+ "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.",
520
+ "library.sync":
521
+ "Off means `clio-coder library sync` and `push` refuse before touching the network, whatever the remote says. Legal values: true, false · default: false.",
365
522
  "budget.concurrency": "auto sizes to your machine. A fixed number caps how many workers run at once.",
366
523
  "skills.trustProjectCompatRoots":
367
524
  "Project roots like .claude/skills and .codex/skills are untrusted by default; enabling exposes them to the model.",
@@ -369,6 +526,20 @@ const SETTINGS_HELP_BY_ID: Partial<Record<EditableSettingId, string>> = {
369
526
  "Role trailers are added only when Clio has trusted evidence for that role. Disabling leaves subsequent commit messages entirely unchanged.",
370
527
  "workers.onPermission":
371
528
  "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.",
529
+ "workers.escalation.timeoutMs":
530
+ "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).",
531
+ "workers.escalation.fallback":
532
+ "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.",
533
+ "workers.resilienceCooldownMs":
534
+ "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.",
535
+ "routing.activeRoles":
536
+ "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.",
537
+ "routing.activePostures":
538
+ "Manual pins are exact rather than adaptive, so manual is never an activated posture. Legal values: quality, balanced, latency, economy · default: none active.",
539
+ "routing.agentAutomation.activeAgentRoles":
540
+ "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.",
541
+ "prewarm.enabled":
542
+ "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.",
372
543
  "workers.agentBindings":
373
544
  "Bind base, custom, and shadow native agents such as scout, researcher, and provenance to profiles. ACP delegation agents cannot be bound.",
374
545
  "delegation.defaults.toolGovernance":
@@ -404,6 +575,26 @@ const SETTINGS_VALUE_HELP_BY_ID: Partial<Record<EditableSettingId, Record<string
404
575
  escalate:
405
576
  "the ask is forwarded to this session's operator; on timeout it falls back to deny or fail per workers.escalation",
406
577
  },
578
+ "workers.escalation.fallback": {
579
+ deny: "an unanswered escalation becomes a tool denial and the run continues",
580
+ fail: "an unanswered escalation ends the run as permission_required",
581
+ },
582
+ "prewarm.enabled": {
583
+ true: "prefill the next turn's known prefix on local-native targets",
584
+ false: "never send a pre-warm request; the first turn pays the whole prefill",
585
+ },
586
+ "context.workingSet.enabled": {
587
+ true: "evict stale observations non-destructively before summarizing",
588
+ false: "skip eviction and go straight to summary compaction",
589
+ },
590
+ "context.workingSet.policy": {
591
+ "structural-v1": "select eviction candidates by message structure",
592
+ "age-horizon": "the older rule: select by age alone",
593
+ },
594
+ "library.sync": {
595
+ true: "allow `clio-coder library sync` and `push` to reach the confirmed remote",
596
+ false: "refuse every library network operation",
597
+ },
407
598
  "delegation.defaults.toolGovernance": {
408
599
  "clio-policy": "Clio's safety policy gates the delegated agent's tools",
409
600
  "agent-managed": "the external agent governs its own tools",
@@ -729,6 +920,16 @@ const NUMBER_SETTING_RULES = {
729
920
  "delegation.defaults.connectTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
730
921
  "delegation.defaults.turnTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
731
922
  "delegation.defaults.permissionTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
923
+ // The escalation deadline is armed as a timer, so it shares the delegation
924
+ // bounds; the rest mirror the config validator's own integer bounds.
925
+ "workers.escalation.timeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
926
+ "workers.resilienceCooldownMs": { min: 0, integer: true },
927
+ "guardrails.turnToolCallBudget": { min: 1, integer: true },
928
+ "guardrails.workerToolCallCap": { min: 1, integer: true },
929
+ "guardrails.maxDispatchRuns": { min: 1, integer: true },
930
+ "guardrails.readMaxBytes": { min: 1, integer: true },
931
+ "guardrails.observationTurnBudgetBytes": { min: 1, integer: true },
932
+ "guardrails.internalDispatchTimeoutMs": { min: 1, integer: true },
732
933
  } as const satisfies Partial<Record<EditableSettingId, NumberSettingRule>>;
733
934
 
734
935
  export type NumberSettingId = keyof typeof NUMBER_SETTING_RULES;
@@ -814,6 +1015,51 @@ function editNumberSubmenu(title: string, id: NumberSettingId): SettingSubmenuBu
814
1015
  };
815
1016
  }
816
1017
 
1018
+ /**
1019
+ * The comma-separated set an activation list holds, refused in place when an
1020
+ * entry is not one of the legal values. Silently dropping the unknown entry
1021
+ * would leave the operator looking at a list they thought they had activated,
1022
+ * so the editor stays open and names both the entry and the vocabulary.
1023
+ */
1024
+ function parseEnumListSetting(value: string, legal: readonly string[]): { values: string[] } | { refusal: string } {
1025
+ const entries = value
1026
+ .split(",")
1027
+ .map((entry) => entry.trim())
1028
+ .filter((entry) => entry.length > 0);
1029
+ const unknown = entries.find((entry) => !legal.includes(entry));
1030
+ if (unknown !== undefined) {
1031
+ return { refusal: `Not applied: '${unknown}' is not one of ${legal.join(", ")}.` };
1032
+ }
1033
+ const duplicate = entries.find((entry, index) => entries.indexOf(entry) !== index);
1034
+ if (duplicate !== undefined) return { refusal: `Not applied: '${duplicate}' is listed twice.` };
1035
+ return { values: entries };
1036
+ }
1037
+
1038
+ function editEnumListSubmenu(title: string, legal: readonly string[]): SettingSubmenuBuilder {
1039
+ return (currentValue: string, done: (val?: string) => void) => {
1040
+ const input = new Input();
1041
+ input.setValue(currentValue);
1042
+ input.focused = true;
1043
+ const wrapper = new SubmenuWrapper(
1044
+ title,
1045
+ input,
1046
+ buildHint([{ key: "Enter", verb: "confirm" }], "back"),
1047
+ `Comma-separated, from ${legal.join(", ")}; blank activates none.`,
1048
+ );
1049
+ input.onSubmit = (val) => {
1050
+ const outcome = parseEnumListSetting(val, legal);
1051
+ if ("refusal" in outcome) {
1052
+ wrapper.setProblem(outcome.refusal);
1053
+ return;
1054
+ }
1055
+ wrapper.setProblem(null);
1056
+ done(outcome.values.join(", "));
1057
+ };
1058
+ input.onEscape = () => done();
1059
+ return wrapper;
1060
+ };
1061
+ }
1062
+
817
1063
  function selectListSubmenu(
818
1064
  title: string,
819
1065
  items: ReadonlyArray<{
@@ -1284,13 +1530,23 @@ function settingItem(
1284
1530
  return item;
1285
1531
  }
1286
1532
 
1287
- function fleetGroupHeader(id: FleetGroupHeaderId, label: string): SettingsCenterItem {
1533
+ /**
1534
+ * A heading row that names the block of settings under it. The id carries the
1535
+ * section it belongs to, so one builder serves every section that groups its
1536
+ * rows rather than a near-duplicate per section.
1537
+ */
1538
+ function groupHeader(
1539
+ id: GroupHeaderId,
1540
+ section: SettingsSectionId,
1541
+ label: string,
1542
+ description: string,
1543
+ ): SettingsCenterItem {
1288
1544
  return {
1289
1545
  id,
1290
1546
  label,
1291
1547
  currentValue: "",
1292
- description: `${label} in the Fleet workbench.`,
1293
- section: "fleet",
1548
+ description,
1549
+ section,
1294
1550
  configPath: id,
1295
1551
  affordance: "group heading",
1296
1552
  scope: "live",
@@ -1300,6 +1556,14 @@ function fleetGroupHeader(id: FleetGroupHeaderId, label: string): SettingsCenter
1300
1556
  };
1301
1557
  }
1302
1558
 
1559
+ function fleetGroupHeader(id: FleetGroupHeaderId, label: string): SettingsCenterItem {
1560
+ return groupHeader(id, "fleet", label, `${label} in the Fleet workbench.`);
1561
+ }
1562
+
1563
+ function terminalGroupHeader(id: TerminalGroupHeaderId, label: string): SettingsCenterItem {
1564
+ return groupHeader(id, "terminal", label, `${label} behavior in the terminal.`);
1565
+ }
1566
+
1303
1567
  function targetAddCta(): SettingsCenterItem {
1304
1568
  return {
1305
1569
  id: "targets.add-cta",
@@ -1348,6 +1612,7 @@ export function buildSettingItems(
1348
1612
  const retry = settings.retry;
1349
1613
  const terminal = settings.terminal;
1350
1614
  const watchdog = settings.watchdog;
1615
+ const panes = settings.panes;
1351
1616
  const orchestratorThinking = thinkingChoices(
1352
1617
  options?.providers,
1353
1618
  settings.orchestrator.target,
@@ -1390,6 +1655,15 @@ export function buildSettingItems(
1390
1655
  const backgroundModelSubmenu = options?.providers
1391
1656
  ? selectModelSubmenu(options.providers, () => live().background.target ?? undefined)
1392
1657
  : editTextSubmenu("Type memory model name");
1658
+ // Both are optional in the settings type, so the row falls back to the
1659
+ // shipped value rather than rendering a blank for a key that is in force.
1660
+ const escalation = settings.workers.escalation ?? SHIPPED_ESCALATION;
1661
+ const resilienceCooldownMs = settings.workers.resilienceCooldownMs ?? SHIPPED_RESILIENCE_COOLDOWN_MS;
1662
+ const workingSet = settings.context.workingSet;
1663
+ const guardrails = settings.guardrails;
1664
+ const routing = settings.routing;
1665
+ const library = settings.library;
1666
+ const activeAgentRoles = routing.agentAutomation.activeAgentRoles;
1393
1667
  const favorites = settings.modelSelector?.favorites ?? [];
1394
1668
  const agents = settings.delegation?.agents ?? [];
1395
1669
  const keybindingCount = Object.keys(settings.keybindings ?? {}).length;
@@ -1400,6 +1674,13 @@ export function buildSettingItems(
1400
1674
  settingItem("workers.onPermission", settings.workers.onPermission ?? "deny", {
1401
1675
  values: ["deny", "fail", "escalate"],
1402
1676
  }),
1677
+ settingItem("workers.escalation.timeoutMs", String(escalation.timeoutMs), {
1678
+ submenu: editNumberSubmenu("Edit escalation timeout (ms)", "workers.escalation.timeoutMs"),
1679
+ affordance: "free text",
1680
+ }),
1681
+ settingItem("workers.escalation.fallback", escalation.fallback, {
1682
+ values: ["deny", "fail"],
1683
+ }),
1403
1684
  settingItem("delegation.defaults.toolGovernance", settings.delegation.defaults.toolGovernance, {
1404
1685
  values: ["clio-policy", "agent-managed", "deny-all"],
1405
1686
  }),
@@ -1447,6 +1728,9 @@ export function buildSettingItems(
1447
1728
  settingItem("memory.intervention.timeoutMs", String(settings.memory.intervention.timeoutMs), {
1448
1729
  values: ["5000", "10000", "20000", "30000", "60000"],
1449
1730
  }),
1731
+ settingItem("prewarm.enabled", String(settings.prewarm.enabled), {
1732
+ values: ["true", "false"],
1733
+ }),
1450
1734
  fleetGroupHeader("fleet.group.defaults", "Defaults"),
1451
1735
  settingItem("workers.default.target", settings.workers.default.target ?? "(unset)", {
1452
1736
  submenu: targetSubmenu,
@@ -1462,6 +1746,10 @@ export function buildSettingItems(
1462
1746
  settingItem("workers.maxRetries", String(settings.workers.maxRetries), {
1463
1747
  values: ["0", "1", "2", "3", "5", "8"],
1464
1748
  }),
1749
+ settingItem("workers.resilienceCooldownMs", String(resilienceCooldownMs), {
1750
+ submenu: editNumberSubmenu("Edit resilience cooldown (ms)", "workers.resilienceCooldownMs"),
1751
+ affordance: "free text",
1752
+ }),
1465
1753
  fleetGroupHeader("fleet.group.profiles", "Profiles"),
1466
1754
  ...fleetProfileRows(settings, live, options),
1467
1755
  settingItem("workers.profiles", "", {
@@ -1480,8 +1768,35 @@ export function buildSettingItems(
1480
1768
  presentationKind: profileCount > 0 ? "action" : "read-only-fact",
1481
1769
  valueSegments: [],
1482
1770
  }),
1771
+ fleetGroupHeader("fleet.group.route-activation", "Route activation"),
1772
+ settingItem("routing.activeRoles", routing.activeRoles.length > 0 ? routing.activeRoles.join(", ") : "(none)", {
1773
+ submenu: editEnumListSubmenu("Edit active routing roles", ACTIVE_ROUTING_ROLES),
1774
+ affordance: `free text from ${ACTIVE_ROUTING_ROLES.join(", ")}`,
1775
+ editValue: routing.activeRoles.join(", "),
1776
+ }),
1777
+ settingItem(
1778
+ "routing.activePostures",
1779
+ routing.activePostures.length > 0 ? routing.activePostures.join(", ") : "(none)",
1780
+ {
1781
+ submenu: editEnumListSubmenu("Edit active routing postures", ACTIVE_ROUTING_POSTURES),
1782
+ affordance: `free text from ${ACTIVE_ROUTING_POSTURES.join(", ")}`,
1783
+ editValue: routing.activePostures.join(", "),
1784
+ },
1785
+ ),
1786
+ settingItem(
1787
+ "routing.agentAutomation.activeAgentRoles",
1788
+ activeAgentRoles.length > 0
1789
+ ? activeAgentRoles.map((pair) => `${pair.agentId}/${pair.executionRole}`).join(", ")
1790
+ : "(none)",
1791
+ { affordance: "edit settings.yaml", readOnly: true },
1792
+ ),
1483
1793
  fleetGroupHeader("fleet.group.placement", "Placement"),
1484
1794
  ...fleetNodeRows(options?.getFleetNodes?.() ?? []),
1795
+ ...fleetEndpointRows(options?.providers),
1796
+ fleetGroupHeader("fleet.group.panes", "Panes"),
1797
+ settingItem("panes.enabled", panes.enabled, { values: ["auto", "embedded", "off"] }),
1798
+ settingItem("panes.notifications", panes.notifications, { values: ["failures", "all", "off"] }),
1799
+ settingItem("panes.journal", String(panes.journal), { values: ["true", "false"] }),
1485
1800
  settingItem("targets", "", {
1486
1801
  description: "Live inference target inventory and routing roles.",
1487
1802
  affordance: "column heading",
@@ -1512,6 +1827,36 @@ export function buildSettingItems(
1512
1827
  settingItem("budget.concurrency", String(settings.budget.concurrency), {
1513
1828
  values: ["auto", "1", "2", "4", "8"],
1514
1829
  }),
1830
+ groupHeader(
1831
+ "budget.group.guardrails",
1832
+ "budget",
1833
+ "Guardrails",
1834
+ "Numeric backstops that bound runaway agent behavior.",
1835
+ ),
1836
+ settingItem("guardrails.turnToolCallBudget", String(guardrails.turnToolCallBudget), {
1837
+ submenu: editNumberSubmenu("Edit turn tool-call budget", "guardrails.turnToolCallBudget"),
1838
+ affordance: "free text",
1839
+ }),
1840
+ settingItem("guardrails.workerToolCallCap", String(guardrails.workerToolCallCap), {
1841
+ submenu: editNumberSubmenu("Edit worker tool-call cap", "guardrails.workerToolCallCap"),
1842
+ affordance: "free text",
1843
+ }),
1844
+ settingItem("guardrails.maxDispatchRuns", String(guardrails.maxDispatchRuns), {
1845
+ submenu: editNumberSubmenu("Edit run ledger retention", "guardrails.maxDispatchRuns"),
1846
+ affordance: "free text",
1847
+ }),
1848
+ settingItem("guardrails.readMaxBytes", String(guardrails.readMaxBytes), {
1849
+ submenu: editNumberSubmenu("Edit read byte cap", "guardrails.readMaxBytes"),
1850
+ affordance: "free text",
1851
+ }),
1852
+ settingItem("guardrails.observationTurnBudgetBytes", String(guardrails.observationTurnBudgetBytes), {
1853
+ submenu: editNumberSubmenu("Edit observation byte pool", "guardrails.observationTurnBudgetBytes"),
1854
+ affordance: "free text",
1855
+ }),
1856
+ settingItem("guardrails.internalDispatchTimeoutMs", String(guardrails.internalDispatchTimeoutMs), {
1857
+ submenu: editNumberSubmenu("Edit internal dispatch timeout (ms)", "guardrails.internalDispatchTimeoutMs"),
1858
+ affordance: "free text",
1859
+ }),
1515
1860
  settingItem("compaction.auto", String(compaction.auto), {
1516
1861
  values: ["true", "false"],
1517
1862
  }),
@@ -1521,6 +1866,27 @@ export function buildSettingItems(
1521
1866
  settingItem("compaction.excludeLastTurns", String(compaction.excludeLastTurns), {
1522
1867
  values: ["3", "6", "10", "15"],
1523
1868
  }),
1869
+ groupHeader(
1870
+ "compaction.group.working-set",
1871
+ "compaction",
1872
+ "Working set",
1873
+ "The non-destructive eviction layer that runs before a summary is needed.",
1874
+ ),
1875
+ settingItem("context.workingSet.enabled", String(workingSet.enabled), {
1876
+ values: ["true", "false"],
1877
+ }),
1878
+ settingItem("context.workingSet.policy", workingSet.policy, {
1879
+ values: ["structural-v1", "age-horizon"],
1880
+ }),
1881
+ settingItem("context.workingSet.target", formatThreshold(workingSet.target, "0.6"), {
1882
+ values: ["0.4", "0.5", "0.6", "0.7"],
1883
+ }),
1884
+ settingItem("context.workingSet.protectLastTurns", String(workingSet.protectLastTurns), {
1885
+ values: ["3", "6", "10", "15"],
1886
+ }),
1887
+ settingItem("context.workingSet.minEvictableTokens", String(workingSet.minEvictableTokens), {
1888
+ values: ["0", "100", "200", "500", "1000"],
1889
+ }),
1524
1890
  settingItem("retry.enabled", String(retry.enabled), {
1525
1891
  values: ["true", "false"],
1526
1892
  }),
@@ -1533,6 +1899,9 @@ export function buildSettingItems(
1533
1899
  settingItem("retry.maxDelayMs", String(retry.maxDelayMs), {
1534
1900
  values: ["10000", "30000", "60000", "120000", "300000"],
1535
1901
  }),
1902
+ settingItem("retry.streamStallMs", String(retry.streamStallMs), {
1903
+ values: ["60000", "120000", "180000", "300000", "600000"],
1904
+ }),
1536
1905
  settingItem("terminal.showTerminalProgress", String(terminal.showTerminalProgress), {
1537
1906
  values: ["false", "true"],
1538
1907
  }),
@@ -1555,6 +1924,11 @@ export function buildSettingItems(
1555
1924
  affordance: "single clio-coder palette",
1556
1925
  readOnly: true,
1557
1926
  }),
1927
+ terminalGroupHeader("terminal.group.files-pane", "Files pane"),
1928
+ settingItem("panes.yazi.enabled", String(panes.yazi.enabled), { values: ["true", "false"] }),
1929
+ settingItem("panes.yazi.mode", panes.yazi.mode, { values: ["companion", "chooser"] }),
1930
+ settingItem("panes.yazi.profile", panes.yazi.profile, { values: ["managed", "user"] }),
1931
+ settingItem("panes.yazi.followCwd", String(panes.yazi.followCwd), { values: ["true", "false"] }),
1558
1932
  settingItem("watchdog.enabled", String(watchdog.enabled), {
1559
1933
  values: ["false", "true"],
1560
1934
  }),
@@ -1612,6 +1986,31 @@ export function buildSettingItems(
1612
1986
  settingItem("delegation.agents", agents.length > 0 ? `${agents.length} agent(s)` : "(none)", {
1613
1987
  ...delegationAgentsAffordance(agents, options?.getInteropProposals?.() ?? []),
1614
1988
  }),
1989
+ groupHeader(
1990
+ "advanced.group.library",
1991
+ "advanced",
1992
+ "Library",
1993
+ "The private resource catalog and the remote it may sync with.",
1994
+ ),
1995
+ settingItem("library.catalog", library.catalog ?? "(config directory)", {
1996
+ submenu: editTextSubmenu("Edit library catalog path; blank uses the config directory"),
1997
+ affordance: "free text",
1998
+ editValue: library.catalog ?? "",
1999
+ }),
2000
+ settingItem("library.remote", library.remote ?? "(local only)", {
2001
+ submenu: editTextSubmenu("Edit library remote URL; blank keeps the library local"),
2002
+ affordance: "free text",
2003
+ editValue: library.remote ?? "",
2004
+ }),
2005
+ // Confirming a remote from the settings row would be the trust record
2006
+ // confirming itself, so this row reports what the confirm flow wrote.
2007
+ settingItem("library.confirmedRemote", library.confirmedRemote ?? "(unconfirmed)", {
2008
+ affordance: "set by `clio-coder library` on confirmation",
2009
+ readOnly: true,
2010
+ }),
2011
+ settingItem("library.sync", String(library.sync), {
2012
+ values: ["true", "false"],
2013
+ }),
1615
2014
  ];
1616
2015
  }
1617
2016
 
@@ -1860,6 +2259,35 @@ function fleetNodeRows(nodes: ReadonlyArray<FleetNodeSnapshot>): SettingsCenterI
1860
2259
  });
1861
2260
  }
1862
2261
 
2262
+ /** Read-only endpoint rows show the independent inference scheduler dimension. */
2263
+ function fleetEndpointRows(providers: ProvidersContract | undefined): SettingsCenterItem[] {
2264
+ if (providers === undefined) return [];
2265
+ let usage: Readonly<Record<string, number>> = {};
2266
+ try {
2267
+ usage = capacityLeaseUsage().endpoints;
2268
+ } catch {
2269
+ // A damaged capacity file belongs to admission. Settings still shows the configured limits.
2270
+ }
2271
+ const capacities = endpointCapacitiesForStatuses(providers.list());
2272
+ const targetsByEndpoint = new Map<string, string[]>();
2273
+ for (const status of providers.list()) {
2274
+ const endpoint = endpointCapacityForStatus(status);
2275
+ if (endpoint === null) continue;
2276
+ targetsByEndpoint.set(endpoint.key, [...(targetsByEndpoint.get(endpoint.key) ?? []), status.target.id]);
2277
+ }
2278
+ return Object.entries(capacities).map(([key, endpoint]) => {
2279
+ const slots = `slots ${usage[key] ?? 0}/${endpoint.limit}`;
2280
+ return settingItem(`fleet.endpoints.${key}`, slots, {
2281
+ label: `endpoint ${endpoint.label}`,
2282
+ description: `Targets: ${(targetsByEndpoint.get(key) ?? []).join(", ")} · Limit source: ${endpoint.source}`,
2283
+ affordance: "read-only inference endpoint capacity",
2284
+ readOnly: true,
2285
+ presentationKind: "status",
2286
+ valueSegments: [{ text: slots, tone: "neutral" }],
2287
+ });
2288
+ });
2289
+ }
2290
+
1863
2291
  export function buildSettingsSections(items: readonly SettingsCenterItem[]): SettingsCenterSection[] {
1864
2292
  return SETTINGS_SECTIONS.map((section) => ({
1865
2293
  id: section.id,
@@ -1901,8 +2329,8 @@ function refreshSettingItemsInPlace(items: SettingsCenterItem[], next: readonly
1901
2329
  }
1902
2330
  }
1903
2331
 
1904
- function formatThreshold(value: number): string {
1905
- return Number.isFinite(value) ? String(value) : "0.8";
2332
+ function formatThreshold(value: number, fallback = "0.8"): string {
2333
+ return Number.isFinite(value) ? String(value) : fallback;
1906
2334
  }
1907
2335
 
1908
2336
  function applyNonNegativeInteger(value: string, set: (next: number) => void): void {
@@ -1990,6 +2418,27 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
1990
2418
  settings.workers.maxRetries = next;
1991
2419
  });
1992
2420
  return;
2421
+ case "panes.enabled":
2422
+ if (value === "auto" || value === "embedded" || value === "off") settings.panes.enabled = value;
2423
+ return;
2424
+ case "panes.notifications":
2425
+ if (value === "failures" || value === "all" || value === "off") settings.panes.notifications = value;
2426
+ return;
2427
+ case "panes.journal":
2428
+ if (value === "true" || value === "false") settings.panes.journal = value === "true";
2429
+ return;
2430
+ case "panes.yazi.enabled":
2431
+ if (value === "true" || value === "false") settings.panes.yazi.enabled = value === "true";
2432
+ return;
2433
+ case "panes.yazi.mode":
2434
+ if (value === "companion" || value === "chooser") settings.panes.yazi.mode = value;
2435
+ return;
2436
+ case "panes.yazi.profile":
2437
+ if (value === "managed" || value === "user") settings.panes.yazi.profile = value;
2438
+ return;
2439
+ case "panes.yazi.followCwd":
2440
+ if (value === "true" || value === "false") settings.panes.yazi.followCwd = value === "true";
2441
+ return;
1993
2442
  case "modelSelector.recentLimit":
1994
2443
  applyNonNegativeInteger(value, (next) => {
1995
2444
  if (next >= 1) settings.modelSelector.recentLimit = next;
@@ -2166,6 +2615,83 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
2166
2615
  if (next !== null) settings.budget.sessionCeilingUsd = next;
2167
2616
  });
2168
2617
  return;
2618
+ case "workers.escalation.timeoutMs":
2619
+ applyNumberSetting("workers.escalation.timeoutMs", value, (next) => {
2620
+ if (next === null) return;
2621
+ settings.workers.escalation = { ...(settings.workers.escalation ?? SHIPPED_ESCALATION), timeoutMs: next };
2622
+ });
2623
+ return;
2624
+ case "workers.escalation.fallback": {
2625
+ if (value !== "deny" && value !== "fail") return;
2626
+ settings.workers.escalation = { ...(settings.workers.escalation ?? SHIPPED_ESCALATION), fallback: value };
2627
+ return;
2628
+ }
2629
+ case "workers.resilienceCooldownMs":
2630
+ applyNumberSetting("workers.resilienceCooldownMs", value, (next) => {
2631
+ if (next !== null) settings.workers.resilienceCooldownMs = next;
2632
+ });
2633
+ return;
2634
+ case "routing.activeRoles": {
2635
+ const outcome = parseEnumListSetting(value, ACTIVE_ROUTING_ROLES);
2636
+ if ("values" in outcome) settings.routing.activeRoles = outcome.values as ActiveRoutingRole[];
2637
+ return;
2638
+ }
2639
+ case "routing.activePostures": {
2640
+ const outcome = parseEnumListSetting(value, ACTIVE_ROUTING_POSTURES);
2641
+ if ("values" in outcome) settings.routing.activePostures = outcome.values as ActiveRoutingPosture[];
2642
+ return;
2643
+ }
2644
+ case "prewarm.enabled":
2645
+ if (value === "true" || value === "false") settings.prewarm.enabled = value === "true";
2646
+ return;
2647
+ case "guardrails.turnToolCallBudget":
2648
+ case "guardrails.workerToolCallCap":
2649
+ case "guardrails.maxDispatchRuns":
2650
+ case "guardrails.readMaxBytes":
2651
+ case "guardrails.observationTurnBudgetBytes":
2652
+ case "guardrails.internalDispatchTimeoutMs":
2653
+ applyNumberSetting(id, value, (next) => {
2654
+ if (next !== null) settings.guardrails[id.slice("guardrails.".length) as keyof GuardrailValues] = next;
2655
+ });
2656
+ return;
2657
+ case "context.workingSet.enabled":
2658
+ if (value === "true" || value === "false") settings.context.workingSet.enabled = value === "true";
2659
+ return;
2660
+ case "context.workingSet.policy":
2661
+ if (value === "structural-v1" || value === "age-horizon") settings.context.workingSet.policy = value;
2662
+ return;
2663
+ case "context.workingSet.target": {
2664
+ // The validator's bound is exclusive at both ends, so an eviction target
2665
+ // of 0 or 1 is refused here rather than written and rejected at boot.
2666
+ const parsed = Number(value);
2667
+ if (Number.isFinite(parsed) && parsed > 0 && parsed < 1) settings.context.workingSet.target = parsed;
2668
+ return;
2669
+ }
2670
+ case "context.workingSet.protectLastTurns":
2671
+ applyNonNegativeInteger(value, (next) => {
2672
+ if (next >= 1) settings.context.workingSet.protectLastTurns = next;
2673
+ });
2674
+ return;
2675
+ case "context.workingSet.minEvictableTokens":
2676
+ applyNonNegativeInteger(value, (next) => {
2677
+ settings.context.workingSet.minEvictableTokens = next;
2678
+ });
2679
+ return;
2680
+ case "retry.streamStallMs":
2681
+ applyNonNegativeInteger(value, (next) => {
2682
+ settings.retry.streamStallMs = next;
2683
+ });
2684
+ return;
2685
+ case "library.catalog":
2686
+ case "library.remote": {
2687
+ const key = id.slice("library.".length) as "catalog" | "remote";
2688
+ const trimmed = value.trim();
2689
+ settings.library[key] = trimmed.length > 0 ? trimmed : null;
2690
+ return;
2691
+ }
2692
+ case "library.sync":
2693
+ if (value === "true" || value === "false") settings.library.sync = value === "true";
2694
+ return;
2169
2695
  }
2170
2696
  }
2171
2697
 
@@ -3246,7 +3772,7 @@ export class SettingsCenter implements Component {
3246
3772
  if (this.sections().length === 0) return [...head, ...this.emptyFilterLines(width, available)];
3247
3773
  // A detail page owns the work area, and the footer describes the row the
3248
3774
  // operator has already left, so it is suppressed while a submenu is open.
3249
- const footer = this.submenuComponent ? [] : this.renderFooter(width, this.footerBudget(available));
3775
+ const footer = this.submenuComponent ? [] : this.renderFooter(width, this.footerBudget(available, width));
3250
3776
  const contentHeight = Math.max(1, available - footer.length);
3251
3777
  const leftWidth = Math.min(SECTION_LANE_WIDTH, Math.max(16, Math.floor(width * 0.28)));
3252
3778
  const separator = barSep(clioTheme());
@@ -3266,9 +3792,16 @@ export class SettingsCenter implements Component {
3266
3792
  * The footer is help, and the list is the work. A fixed six-line ceiling spent
3267
3793
  * a third of a 20-row body on prose, so the budget is what the body can spare
3268
3794
  * once the list keeps six rows.
3795
+ *
3796
+ * The ceiling follows the scope note, which wraps rather than cuts: at 90
3797
+ * columns that note needs two rows, and the flat four-line ceiling then paid
3798
+ * for the second row out of the explanation above it. The note buys its own
3799
+ * rows instead. A note that fits on one line leaves the ceiling at four, so
3800
+ * every width above the wrap point renders exactly as it did.
3269
3801
  */
3270
- private footerBudget(bodyHeight: number): number {
3271
- return Math.min(4, Math.max(0, bodyHeight - 6));
3802
+ private footerBudget(bodyHeight: number, width: number): number {
3803
+ const noteLines = wrapTextWithAnsi(this.footerNoteText(), Math.max(1, width)).length;
3804
+ return Math.min(2 + noteLines + 1, Math.max(0, bodyHeight - 6));
3272
3805
  }
3273
3806
 
3274
3807
  private renderSectionLane(width: number, height: number): string[] {
@@ -3434,15 +3967,27 @@ export class SettingsCenter implements Component {
3434
3967
  const query = this.filterQuery.trim().length > 0 ? ` ${theme.fg("accent", `/${this.filterQuery}`)}` : "";
3435
3968
  const positionText = `${theme.fg("dim", `section ${position}/${sections.length}`)}${query}`;
3436
3969
 
3970
+ // The note is prose the operator acts on ("choose session, global, or
3971
+ // cancel"), so it wraps under the same rule as the description above it. A
3972
+ // truncated instruction is the one line in this footer that cannot afford
3973
+ // to be a fragment.
3974
+ const noteLines = (text: string, tone: "dim" | "muted"): string[] =>
3975
+ wrapTextWithAnsi(theme.fg(tone, text), safeWidth);
3976
+
3437
3977
  if (this.level === "sections") {
3438
3978
  const breadcrumb = `${screenTitle(theme, section.label)} ${theme.fg("dim", "·")} ${positionText}`;
3439
3979
  const body = wrapTextWithAnsi(theme.fg("muted", SETTINGS_SECTION_DESCRIPTIONS[section.id]), safeWidth);
3440
- const note = theme.fg("dim", "Tab or → to edit its settings");
3441
- return this.assembleFooter([separator, breadcrumb], body, note, maxFooterLines, safeWidth);
3980
+ return this.assembleFooter(
3981
+ [separator, breadcrumb],
3982
+ body,
3983
+ noteLines(this.footerNoteText(), "dim"),
3984
+ maxFooterLines,
3985
+ safeWidth,
3986
+ );
3442
3987
  }
3443
3988
 
3444
3989
  if (!item) {
3445
- return this.assembleFooter([separator], [], theme.fg("muted", "No setting selected."), maxFooterLines, safeWidth);
3990
+ return this.assembleFooter([separator], [], noteLines(this.footerNoteText(), "muted"), maxFooterLines, safeWidth);
3446
3991
  }
3447
3992
 
3448
3993
  const breadcrumb = `${screenTitle(theme, section.label)} ${theme.fg("dim", "›")} ${theme.style("accent", item.label, { bold: true })} ${theme.fg("dim", "·")} ${positionText}`;
@@ -3451,8 +3996,27 @@ export class SettingsCenter implements Component {
3451
3996
  if (item.help) contentLines.push(...wrapTextWithAnsi(theme.fg("dim", item.help), safeWidth));
3452
3997
  const detail = this.footerDetail(item, theme);
3453
3998
  if (detail) contentLines.push(...wrapTextWithAnsi(detail, safeWidth));
3454
- const note = theme.fg("dim", truncateToWidth(this.footerScopeNote(item), safeWidth, ELLIPSIS, true));
3455
- return this.assembleFooter([separator, breadcrumb], contentLines, note, maxFooterLines, safeWidth);
3999
+ return this.assembleFooter(
4000
+ [separator, breadcrumb],
4001
+ contentLines,
4002
+ noteLines(this.footerScopeNote(item), "dim"),
4003
+ maxFooterLines,
4004
+ safeWidth,
4005
+ );
4006
+ }
4007
+
4008
+ /**
4009
+ * The note text renderFooter will show, without theming. `footerBudget` needs
4010
+ * its wrapped height before the footer is rendered, so both read it here and
4011
+ * the ceiling can never disagree with what lands.
4012
+ */
4013
+ private footerNoteText(): string {
4014
+ const section = this.currentSection();
4015
+ if (!section) return "";
4016
+ if (this.level === "sections") return "Tab or → to edit its settings";
4017
+ const item = this.selectedItem();
4018
+ if (!item) return "No setting selected.";
4019
+ return this.footerScopeNote(item);
3456
4020
  }
3457
4021
 
3458
4022
  private footerDetail(item: SettingsCenterItem, theme: ReturnType<typeof clioTheme>): string {
@@ -3489,7 +4053,7 @@ export class SettingsCenter implements Component {
3489
4053
  private assembleFooter(
3490
4054
  top: readonly string[],
3491
4055
  middle: readonly string[],
3492
- note: string,
4056
+ note: readonly string[],
3493
4057
  maxFooterLines: number,
3494
4058
  width: number,
3495
4059
  ): string[] {
@@ -3498,7 +4062,11 @@ export class SettingsCenter implements Component {
3498
4062
  if (maxFooterLines <= top.length) {
3499
4063
  out = top.slice(0, maxFooterLines).map(fit);
3500
4064
  } else {
3501
- const middleBudget = Math.max(0, maxFooterLines - top.length - 1);
4065
+ // The note owns the rows it wrapped to, up to whatever is left under the
4066
+ // breadcrumb; the explanation fills the rest. Dropping a wrapped note
4067
+ // line loses less than cutting the sentence mid-word did.
4068
+ const noteKept = note.slice(0, Math.max(1, maxFooterLines - top.length));
4069
+ const middleBudget = Math.max(0, maxFooterLines - top.length - noteKept.length);
3502
4070
  // The explanation is wrapped, so a short terminal drops whole lines off
3503
4071
  // its end rather than cutting one. At 40 columns the autonomy help
3504
4072
  // stopped at "read-only observes; suggest" and read as the whole
@@ -3507,7 +4075,7 @@ export class SettingsCenter implements Component {
3507
4075
  const last = kept.at(-1);
3508
4076
  const marked =
3509
4077
  middle.length > kept.length && last !== undefined ? [...kept.slice(0, -1), `${last}${ELLIPSIS}`] : kept;
3510
- out = [...top.map(fit), ...marked.map(fit), fit(note)];
4078
+ out = [...top.map(fit), ...marked.map(fit), ...noteKept.map(fit)];
3511
4079
  }
3512
4080
  while (out.length < maxFooterLines) out.push("");
3513
4081
  return out.slice(0, maxFooterLines);
@@ -3617,6 +4185,7 @@ export function openSettingsOverlay(tui: TUI, deps: OpenSettingsOverlayDeps): Se
3617
4185
  margin.right = side;
3618
4186
  return true;
3619
4187
  },
4188
+ markerId: "settings",
3620
4189
  title: "Settings",
3621
4190
  footerHint: (innerWidth) =>
3622
4191
  innerWidth < WIDE_LAYOUT_MIN_WIDTH