@iowarp/clio-coder 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (416) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/NOTICE +33 -0
  3. package/README.md +7 -2
  4. package/dist/{acp-7LOELQFP.js → acp-G5WJBNCT.js} +13 -12
  5. package/dist/{agents-FIBG2SHA.js → agents-FMV2Q5G4.js} +39 -36
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-OI4LIH2I.js → auth-3IDSJEIK.js} +16 -15
  8. package/dist/{builtins-AD25UL3C.js → builtins-XCZWXSC7.js} +3 -3
  9. package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
  10. package/dist/{chunk-56KB5IJP.js → chunk-2OQE55CK.js} +3 -3
  11. package/dist/{chunk-N3PBVRTZ.js → chunk-32KWKNSF.js} +9 -7
  12. package/dist/{chunk-VKBMFOYV.js → chunk-36EJLSQQ.js} +23 -27
  13. package/dist/{chunk-465CC7FK.js → chunk-3BT2XMV4.js} +16 -13
  14. package/dist/{chunk-NQ6UCCOD.js → chunk-3URVFKWK.js} +7 -7
  15. package/dist/{chunk-WIE7ZOSW.js → chunk-3XML7CDN.js} +2 -2
  16. package/dist/chunk-42FMPA75.js +101 -0
  17. package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
  18. package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
  19. package/dist/{chunk-JEQQR47K.js → chunk-6DB53AJS.js} +168 -9
  20. package/dist/{chunk-KV2AOLDF.js → chunk-76ONBSIA.js} +5 -25
  21. package/dist/{chunk-FALJGAWU.js → chunk-7MCTRUCE.js} +2 -2
  22. package/dist/{chunk-5T7RBWN2.js → chunk-AB44T6BB.js} +5 -5
  23. package/dist/chunk-B7OBL7PK.js +1405 -0
  24. package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
  25. package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
  26. package/dist/{chunk-O6I4CIEU.js → chunk-BKFJHQCA.js} +6 -6
  27. package/dist/chunk-BKFM6EJV.js +462 -0
  28. package/dist/chunk-BMS5RKQY.js +27 -0
  29. package/dist/{chunk-MV2VUEJC.js → chunk-BPKCPIL7.js} +2 -2
  30. package/dist/{chunk-TEO2TLVN.js → chunk-BUMFYQFY.js} +964 -1120
  31. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  32. package/dist/chunk-C2LTL2W6.js +2447 -0
  33. package/dist/{chunk-EBOC7MT3.js → chunk-CODPRO7Q.js} +8 -8
  34. package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
  35. package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
  36. package/dist/{chunk-MIX5N5AC.js → chunk-DQITNCXG.js} +394 -154
  37. package/dist/{chunk-S4COXYBG.js → chunk-DYJP44XW.js} +129 -106
  38. package/dist/{chunk-N5XKWMDW.js → chunk-F4CKPOEQ.js} +6 -6
  39. package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
  40. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  41. package/dist/{chunk-7C6RYZGQ.js → chunk-GKF55TAZ.js} +3 -3
  42. package/dist/{chunk-B5XRQOLB.js → chunk-GR5G2PVF.js} +9 -8
  43. package/dist/{chunk-XDOQXGFO.js → chunk-GXNLGKAB.js} +64 -8
  44. package/dist/chunk-HHV2GANA.js +88 -0
  45. package/dist/{chunk-774ILSRL.js → chunk-HI63TFOG.js} +4 -3
  46. package/dist/{chunk-R7LNVMCS.js → chunk-HJJTYUHX.js} +64 -72
  47. package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
  48. package/dist/{chunk-CA42X6KT.js → chunk-HWHKMHUA.js} +6 -6
  49. package/dist/chunk-HZHHCK24.js +1631 -0
  50. package/dist/{chunk-HJB5IUKP.js → chunk-I5VEOC6I.js} +12 -12
  51. package/dist/chunk-IBEBSCYA.js +564 -0
  52. package/dist/chunk-IQ7KR472.js +362 -0
  53. package/dist/{chunk-4H6ULJ3H.js → chunk-J4W7KFM7.js} +921 -990
  54. package/dist/{chunk-4LJX2PUC.js → chunk-JDG2WCRO.js} +5 -5
  55. package/dist/{chunk-NZU6YDNV.js → chunk-K5C3NCBD.js} +4 -4
  56. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  57. package/dist/{chunk-NNNWO6F2.js → chunk-KFZI4NIL.js} +93 -3
  58. package/dist/chunk-KMVISBZR.js +132 -0
  59. package/dist/{chunk-BVDVID7E.js → chunk-LDQ2ZF2M.js} +2 -2
  60. package/dist/{chunk-OEDBCISO.js → chunk-LQ3DZAMX.js} +2 -2
  61. package/dist/{chunk-PZ4I4JE2.js → chunk-LY4S7GJC.js} +124 -116
  62. package/dist/{chunk-3DUR4WUA.js → chunk-MLKNTWH2.js} +19 -19
  63. package/dist/chunk-NEKRRTYW.js +56 -0
  64. package/dist/{chunk-5DQRIYDZ.js → chunk-NHCZP4K7.js} +2 -2
  65. package/dist/chunk-NHLBIGRH.js +1506 -0
  66. package/dist/chunk-NQQH3YT7.js +302 -0
  67. package/dist/chunk-NYS75XW5.js +15 -0
  68. package/dist/{chunk-TD7UE2L5.js → chunk-O4XIVISU.js} +7 -7
  69. package/dist/{chunk-WXCJ7VME.js → chunk-O6TL7WWY.js} +6 -6
  70. package/dist/chunk-OQBA45DZ.js +97 -0
  71. package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
  72. package/dist/chunk-PMZCIOCJ.js +25 -0
  73. package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
  74. package/dist/{chunk-HKO36JWF.js → chunk-QBJA7R7N.js} +31 -3
  75. package/dist/chunk-QDC3K2U3.js +262 -0
  76. package/dist/{chunk-5HFBWUMU.js → chunk-QLFS5GO2.js} +4 -4
  77. package/dist/{chunk-GAYUJ7LE.js → chunk-QLL7ILRG.js} +41 -32
  78. package/dist/{chunk-LXPJXFM5.js → chunk-QREDIESB.js} +7 -7
  79. package/dist/chunk-QSNYB6ZV.js +195 -0
  80. package/dist/chunk-R346GLFC.js +31 -0
  81. package/dist/{chunk-VPTUJU4P.js → chunk-SJ5ZKQ4S.js} +2 -2
  82. package/dist/{chunk-QQ7EKM72.js → chunk-SP2RXXYO.js} +6 -54
  83. package/dist/chunk-SUCTJL45.js +45 -0
  84. package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
  85. package/dist/chunk-T56WDKA5.js +183 -0
  86. package/dist/chunk-TVHHYFHE.js +255 -0
  87. package/dist/{chunk-IHKBWSXF.js → chunk-TZ3SGWZZ.js} +2 -2
  88. package/dist/{chunk-ECUO3KDP.js → chunk-U77AMWDL.js} +47 -7
  89. package/dist/{chunk-3UUY7R3Z.js → chunk-ULC6OTWO.js} +7 -7
  90. package/dist/{chunk-AEYBF3TB.js → chunk-UM7N4G5A.js} +2 -2
  91. package/dist/{chunk-3V5AYSEQ.js → chunk-UXMFQ54G.js} +43 -36
  92. package/dist/{chunk-YW7UVM5V.js → chunk-V5DHCITQ.js} +34 -1
  93. package/dist/{chunk-47CMYGET.js → chunk-VAZSBTKF.js} +2 -2
  94. package/dist/{chunk-3MRC2YSQ.js → chunk-VEO4AP2K.js} +2 -2
  95. package/dist/{chunk-PNY46YEY.js → chunk-VFA6GDY5.js} +47 -3
  96. package/dist/chunk-VO67MWHC.js +75 -0
  97. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  98. package/dist/{chunk-AD7Y7STJ.js → chunk-VYMXRQI6.js} +36 -22
  99. package/dist/{chunk-T3Z6VAAF.js → chunk-W5VSYASO.js} +19 -17
  100. package/dist/{chunk-5PVQ4SRS.js → chunk-WZR7K7ZX.js} +7 -123
  101. package/dist/{chunk-DG4M6ZUE.js → chunk-X3YGUTOB.js} +4 -4
  102. package/dist/chunk-X75E3D2N.js +686 -0
  103. package/dist/{chunk-P3JGPQFL.js → chunk-YDFRH54B.js} +4 -4
  104. package/dist/chunk-YJX4SHTD.js +40 -0
  105. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  106. package/dist/{chunk-YSEHGPCT.js → chunk-Z2RR6MAK.js} +3 -3
  107. package/dist/{chunk-HCBCAYZU.js → chunk-Z4TXYIEG.js} +5 -5
  108. package/dist/cli/index.js +47 -36
  109. package/dist/{clio-LT5V7SSZ.js → clio-2JXHBBY5.js} +7 -7
  110. package/dist/{code-nav-LMW275PA.js → code-nav-3YYRMYNF.js} +6 -6
  111. package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
  112. package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
  113. package/dist/{config-RXS5T3JT.js → config-QZPCMYSO.js} +58 -50
  114. package/dist/{configure-2WYWSCSD.js → configure-TEGEBYCA.js} +21 -19
  115. package/dist/{context-I3BTOTCS.js → context-AV7OEZ4D.js} +11 -11
  116. package/dist/{context-MVOORGMF.js → context-E6H5RNMC.js} +49 -41
  117. package/dist/{context-PALKKQYL.js → context-GSXUE4CT.js} +25 -23
  118. package/dist/{context-clear-N2WOYZ2K.js → context-clear-SHIBYK6T.js} +48 -40
  119. package/dist/{context-working-set-MIEVECVZ.js → context-working-set-5ZGKPGZQ.js} +11 -10
  120. package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-EFMJT4LD.js} +88 -60
  121. package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
  122. package/dist/doctor-QOA5FNY5.js +313 -0
  123. package/dist/{eval-IJ5VEZDJ.js → eval-TFBYQH4H.js} +66 -64
  124. package/dist/eval-inventory-SXH7PDKX.js +316 -0
  125. package/dist/{evidence-L5APPXNV.js → evidence-ERGESKGN.js} +198 -44
  126. package/dist/{evolve-RGNKFJ52.js → evolve-VDXTSYCJ.js} +46 -38
  127. package/dist/{extensions-7WYWUX5A.js → extensions-7BGBHN57.js} +6 -6
  128. package/dist/{fleet-6CNVBZZP.js → fleet-2RRVDF2V.js} +189 -101
  129. package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-VJ726XIA.js} +10 -10
  130. package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
  131. package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-JF5QOATM.js} +16 -15
  132. package/dist/fleet-inspect-VLY4S7QM.js +442 -0
  133. package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-AIZUEJOY.js} +6 -5
  134. package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-AJRPDMDV.js} +20 -18
  135. package/dist/fleet-verify-JFEL2L3H.js +175 -0
  136. package/dist/fleet-view-ZCON35AG.js +102 -0
  137. package/dist/{init-VBN2ACVA.js → init-DN2WWLFE.js} +64 -56
  138. package/dist/install-XGLBQY5E.js +13 -0
  139. package/dist/interop-OZBKXAYL.js +114 -0
  140. package/dist/{library-JHGUMLY2.js → library-YWZG7IMW.js} +19 -18
  141. package/dist/{memory-K4OQIYWG.js → memory-I4C4HMLW.js} +48 -40
  142. package/dist/{models-2NCZUWDD.js → models-CEYXJBO6.js} +30 -28
  143. package/dist/{monitor-MMVTJABD.js → monitor-NZ6GCI3P.js} +53 -47
  144. package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-GCGQ4N5I.js} +6311 -7145
  145. package/dist/panes-HMABYVO4.js +58 -0
  146. package/dist/panes-KY6W3V2E.js +103 -0
  147. package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
  148. package/dist/{reset-DD5JGOY3.js → reset-DQ6FGCSH.js} +13 -11
  149. package/dist/resources-BB3MVJMD.js +111 -0
  150. package/dist/{run-QEGNX7FL.js → run-H2GQDUER.js} +113 -82
  151. package/dist/{share-JKD3BQMW.js → share-GTJN6A5O.js} +18 -17
  152. package/dist/{skills-LMQIKDOZ.js → skills-L55TEW6R.js} +31 -24
  153. package/dist/{skills-eval-I7X2774U.js → skills-eval-XVXPH2JI.js} +61 -52
  154. package/dist/skills-inventory-S4MXPJFV.js +126 -0
  155. package/dist/slash-commands-ZSGASKJC.js +77 -0
  156. package/dist/{steer-CF5TDANS.js → steer-RZGSCY4R.js} +3 -3
  157. package/dist/{support-I7LOJLIF.js → support-PKEUNNQL.js} +4 -4
  158. package/dist/{targets-RUSR6B5Z.js → targets-NCPZ644J.js} +31 -29
  159. package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-44SV3YCN.js} +4 -4
  160. package/dist/tools-DAF3DI3C.js +27 -0
  161. package/dist/{trace-ODOQIVIW.js → trace-FYVW2MQA.js} +149 -7
  162. package/dist/tui-primitives-2AKXQNZK.js +13 -0
  163. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
  164. package/dist/{upgrade-XANW3FXB.js → upgrade-3XPP6OQL.js} +26 -24
  165. package/dist/{usage-4H7ZRXQT.js → usage-3NLHGTU2.js} +57 -49
  166. package/dist/{verifiers-UZXNBZEB.js → verifiers-SSQONKRT.js} +171 -12
  167. package/dist/{verify-BVKWTNDL.js → verify-3U6J7FZI.js} +9 -9
  168. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
  169. package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-CEHYGPGQ.js} +67 -59
  170. package/dist/with-panes-MKB46MPQ.js +782 -0
  171. package/dist/worker/entry.js +101 -66
  172. package/docs/README.md +3 -2
  173. package/docs/acp.md +24 -3
  174. package/docs/alcf-provider.md +1 -1
  175. package/docs/architecture.md +2 -2
  176. package/docs/artifact-versions.md +4 -1
  177. package/docs/built-in-agents.md +1 -1
  178. package/docs/capacity-and-scheduling.md +43 -5
  179. package/docs/commands-and-modes.md +31 -2
  180. package/docs/configuration-and-targets.md +40 -10
  181. package/docs/context-engine.md +1 -1
  182. package/docs/development-pipeline.md +19 -0
  183. package/docs/dispatch-typed-intent.md +385 -0
  184. package/docs/documentation-coverage.md +2 -2
  185. package/docs/documentation-guide.md +1 -1
  186. package/docs/environment-variables.md +1 -0
  187. package/docs/eval-runner.md +1 -1
  188. package/docs/evals-internal.md +1 -1
  189. package/docs/evidence-and-memory.md +2 -2
  190. package/docs/evolution.md +1 -1
  191. package/docs/exit-codes-and-output.md +1 -1
  192. package/docs/extensions-and-sharing.md +25 -1
  193. package/docs/fleet-dispatch.md +22 -1
  194. package/docs/installation-and-lifecycle.md +15 -2
  195. package/docs/middleware-and-components.md +1 -1
  196. package/docs/model-catalog.md +10 -1
  197. package/docs/observability.md +3 -3
  198. package/docs/proactive-memory.md +1 -1
  199. package/docs/prompt-envelope-and-tools.md +1 -1
  200. package/docs/provider-adapter-cookbook.md +1 -1
  201. package/docs/safety-model.md +2 -2
  202. package/docs/scientific-validation.md +3 -3
  203. package/docs/session-lifecycle.md +1 -1
  204. package/docs/skills-marketplace.md +1 -1
  205. package/docs/tool-usage.md +18 -8
  206. package/docs/trace-store.md +1 -1
  207. package/docs/troubleshooting.md +1 -1
  208. package/docs/tui-design.md +1 -1
  209. package/docs/worker-dispatch-mechanics.md +1 -1
  210. package/package.json +4 -2
  211. package/src/cli/acp.ts +6 -2
  212. package/src/cli/argv.ts +25 -0
  213. package/src/cli/configure.ts +23 -21
  214. package/src/cli/doctor-panes.ts +124 -0
  215. package/src/cli/doctor-toolchain.ts +57 -0
  216. package/src/cli/doctor.ts +20 -1
  217. package/src/cli/eval-inventory.ts +436 -0
  218. package/src/cli/eval.ts +13 -0
  219. package/src/cli/evidence-detail.ts +88 -0
  220. package/src/cli/evidence-inventory.ts +183 -0
  221. package/src/cli/evidence.ts +30 -5
  222. package/src/cli/fleet-decisions.ts +69 -0
  223. package/src/cli/fleet-inspect.ts +334 -0
  224. package/src/cli/fleet-verify.ts +133 -0
  225. package/src/cli/fleet-view.ts +810 -0
  226. package/src/cli/fleet.ts +148 -37
  227. package/src/cli/index.ts +14 -2
  228. package/src/cli/interop-inspect.ts +128 -0
  229. package/src/cli/interop.ts +34 -0
  230. package/src/cli/panes.ts +35 -0
  231. package/src/cli/reset.ts +5 -2
  232. package/src/cli/run.ts +58 -0
  233. package/src/cli/skills-inventory.ts +185 -0
  234. package/src/cli/skills.ts +16 -13
  235. package/src/cli/tools.ts +321 -0
  236. package/src/cli/trace-inspect.ts +252 -0
  237. package/src/cli/trace.ts +23 -2
  238. package/src/cli/verifiers-inspect.ts +347 -0
  239. package/src/cli/verifiers.ts +9 -0
  240. package/src/core/bus-events.ts +4 -0
  241. package/src/core/config.ts +49 -0
  242. package/src/core/defaults.ts +93 -7
  243. package/src/core/response-schema.ts +44 -4
  244. package/src/core/theme-token-hex.ts +43 -0
  245. package/src/core/tool-names.ts +2 -1
  246. package/src/core/xdg.ts +1 -1
  247. package/src/domains/agents/fleets/build-review.md +0 -3
  248. package/src/domains/agents/fleets/build-test.md +0 -3
  249. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  250. package/src/domains/agents/result-contract.ts +164 -35
  251. package/src/domains/config/classify.ts +3 -0
  252. package/src/domains/dispatch/admission-error.ts +9 -0
  253. package/src/domains/dispatch/admission.ts +22 -21
  254. package/src/domains/dispatch/capacity-lease.ts +21 -1
  255. package/src/domains/dispatch/council-topology.ts +398 -0
  256. package/src/domains/dispatch/extension.ts +222 -50
  257. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  258. package/src/domains/dispatch/fleet-plan.ts +7 -2
  259. package/src/domains/dispatch/fleet-run.ts +64 -1
  260. package/src/domains/dispatch/gate-decisions.ts +11 -1
  261. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  262. package/src/domains/dispatch/gate-topology.ts +289 -0
  263. package/src/domains/dispatch/index.ts +19 -0
  264. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  265. package/src/domains/dispatch/intent.ts +85 -1
  266. package/src/domains/dispatch/reservation-store.ts +24 -4
  267. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  268. package/src/domains/dispatch/run-event-journal.ts +598 -0
  269. package/src/domains/dispatch/state.ts +45 -1
  270. package/src/domains/dispatch/types.ts +13 -0
  271. package/src/domains/dispatch/validation.ts +33 -8
  272. package/src/domains/eval/compare/compare.ts +3 -16
  273. package/src/domains/eval/inventory.ts +113 -0
  274. package/src/domains/eval/schema/serving.ts +31 -0
  275. package/src/domains/evidence/store.ts +6 -0
  276. package/src/domains/memory/index.ts +0 -1
  277. package/src/domains/memory/task-memory-policy.ts +6 -10
  278. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  279. package/src/domains/middleware/memory-intervention.ts +33 -21
  280. package/src/domains/mux/contract.ts +434 -0
  281. package/src/domains/mux/detect.ts +158 -0
  282. package/src/domains/mux/extension.ts +47 -0
  283. package/src/domains/mux/index.ts +96 -0
  284. package/src/domains/mux/manifest.ts +6 -0
  285. package/src/domains/mux/operations.ts +164 -0
  286. package/src/domains/mux/pane-registry.ts +90 -0
  287. package/src/domains/mux/protocol.ts +49 -0
  288. package/src/domains/mux/socket-client.ts +816 -0
  289. package/src/domains/mux/types.ts +222 -0
  290. package/src/domains/mux/viewer-command.ts +59 -0
  291. package/src/domains/mux/yazi/assets/init.lua +2 -0
  292. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  293. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  294. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  295. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  296. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  297. package/src/domains/mux/yazi/event-stream.ts +180 -0
  298. package/src/domains/mux/yazi/profile.ts +299 -0
  299. package/src/domains/mux/yazi/session.ts +228 -0
  300. package/src/domains/mux/yazi/theme.ts +30 -0
  301. package/src/domains/observability/cost.ts +21 -0
  302. package/src/domains/observability/index.ts +2 -0
  303. package/src/domains/observability/trace-store.ts +42 -0
  304. package/src/domains/providers/endpoint-capacity.ts +154 -22
  305. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  306. package/src/domains/providers/extension.ts +20 -3
  307. package/src/domains/providers/index.ts +22 -0
  308. package/src/domains/providers/model-runtime-capabilities.ts +32 -0
  309. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  310. package/src/domains/providers/runtimes/builtins.ts +2 -0
  311. package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
  312. package/src/domains/providers/support.ts +1 -0
  313. package/src/domains/providers/target-model-cache.ts +124 -0
  314. package/src/domains/resources/index.ts +3 -0
  315. package/src/domains/resources/skills/loader.ts +33 -0
  316. package/src/domains/safety/action-classifier.ts +6 -0
  317. package/src/domains/toolchain/archive.ts +175 -0
  318. package/src/domains/toolchain/contract.ts +28 -0
  319. package/src/domains/toolchain/extension.ts +47 -0
  320. package/src/domains/toolchain/index.ts +39 -0
  321. package/src/domains/toolchain/install.ts +327 -0
  322. package/src/domains/toolchain/manifest.ts +8 -0
  323. package/src/domains/toolchain/paths.ts +34 -0
  324. package/src/domains/toolchain/registry.ts +265 -0
  325. package/src/domains/toolchain/remove.ts +218 -0
  326. package/src/domains/toolchain/resolve.ts +182 -0
  327. package/src/domains/toolchain/types.ts +113 -0
  328. package/src/domains/toolchain/version.ts +88 -0
  329. package/src/engine/acp/server.ts +413 -70
  330. package/src/engine/acp/types.ts +19 -1
  331. package/src/engine/claude/sdk-module.ts +98 -0
  332. package/src/engine/claude/sdk-runtime.ts +19 -11
  333. package/src/engine/tui-primitives.ts +21 -0
  334. package/src/engine/tui.ts +1 -0
  335. package/src/engine/worker-runtime.ts +2 -13
  336. package/src/entry/boot-options.ts +2 -0
  337. package/src/entry/orchestrator.ts +103 -5
  338. package/src/entry/panes-activation.ts +31 -0
  339. package/src/entry/with-panes.ts +20 -0
  340. package/src/interactive/context-overlay.ts +1 -0
  341. package/src/interactive/cost-overlay.ts +2 -0
  342. package/src/interactive/council-grid.ts +1 -3
  343. package/src/interactive/council.ts +11 -0
  344. package/src/interactive/dispatch-board.ts +26 -16
  345. package/src/interactive/interactive-application.ts +153 -6
  346. package/src/interactive/interactive-input-runtime.ts +7 -0
  347. package/src/interactive/interactive-slash-runtime.ts +36 -6
  348. package/src/interactive/memory-overlay.ts +1 -0
  349. package/src/interactive/modal-marker.ts +170 -0
  350. package/src/interactive/mux-bridge.ts +214 -0
  351. package/src/interactive/overlay-frame.ts +58 -2
  352. package/src/interactive/overlay-general-openers.ts +1 -0
  353. package/src/interactive/overlay-key-routing.ts +14 -3
  354. package/src/interactive/overlay-lifecycle.ts +19 -3
  355. package/src/interactive/overlay-model-selectors.ts +40 -3
  356. package/src/interactive/overlay-permission-lifecycle.ts +90 -22
  357. package/src/interactive/overlay-transitions.ts +18 -4
  358. package/src/interactive/overlays/agents.ts +1 -0
  359. package/src/interactive/overlays/ask-user.ts +136 -30
  360. package/src/interactive/overlays/auth-dialog.ts +1 -0
  361. package/src/interactive/overlays/context-reset.ts +1 -0
  362. package/src/interactive/overlays/cwd-fallback.ts +1 -0
  363. package/src/interactive/overlays/decisions.ts +11 -11
  364. package/src/interactive/overlays/extensions.ts +1 -0
  365. package/src/interactive/overlays/fleet-run-approval.ts +1 -0
  366. package/src/interactive/overlays/handoff-review.ts +1 -0
  367. package/src/interactive/overlays/help-reference.ts +2 -0
  368. package/src/interactive/overlays/interop.ts +1 -0
  369. package/src/interactive/overlays/library-install-confirm.ts +1 -0
  370. package/src/interactive/overlays/library-tabs.ts +28 -0
  371. package/src/interactive/overlays/list-overlay.ts +10 -1
  372. package/src/interactive/overlays/message-picker.ts +1 -0
  373. package/src/interactive/overlays/model-scope.ts +86 -0
  374. package/src/interactive/overlays/model-selector.ts +1 -0
  375. package/src/interactive/overlays/prompts.ts +1 -0
  376. package/src/interactive/overlays/session-selector.ts +1 -0
  377. package/src/interactive/overlays/settings-sections.ts +30 -0
  378. package/src/interactive/overlays/settings.ts +579 -44
  379. package/src/interactive/overlays/side-question.ts +1 -0
  380. package/src/interactive/overlays/skills-hub.ts +3 -11
  381. package/src/interactive/overlays/tree-selector.ts +1 -0
  382. package/src/interactive/pane-policy.ts +46 -0
  383. package/src/interactive/panes-runtime.ts +292 -0
  384. package/src/interactive/renderers/compaction-summary.ts +29 -0
  385. package/src/interactive/renderers/worker-entry.ts +122 -14
  386. package/src/interactive/slash-commands.ts +251 -15
  387. package/src/interactive/tasks-overlay.ts +1 -0
  388. package/src/interactive/theme/tokens.ts +3 -14
  389. package/src/interactive/turn-context.ts +47 -2
  390. package/src/interactive/view/artifacts.ts +109 -1
  391. package/src/interactive/view/view-overlay.ts +1 -0
  392. package/src/interactive/watch-pane.ts +152 -0
  393. package/src/interactive/worker-progress.ts +7 -1
  394. package/src/interactive/worker-receipts.ts +19 -1
  395. package/src/interactive/worker-stream.ts +5 -0
  396. package/src/interactive/yazi-bridge.ts +444 -0
  397. package/src/tools/bootstrap.ts +26 -2
  398. package/src/tools/builtin-tool-catalog.ts +15 -0
  399. package/src/tools/compete-worktrees.ts +83 -2
  400. package/src/tools/core-bootstrap.ts +2 -1
  401. package/src/tools/dispatch-admission.ts +14 -3
  402. package/src/tools/dispatch-arguments.ts +20 -20
  403. package/src/tools/dispatch-run-events.ts +134 -19
  404. package/src/tools/dispatch-runner.ts +29 -7
  405. package/src/tools/dispatch-types.ts +15 -3
  406. package/src/tools/dispatch.ts +1 -1
  407. package/src/tools/executables.ts +17 -14
  408. package/src/tools/observation.ts +54 -4
  409. package/src/tools/panes-surface.ts +38 -0
  410. package/src/tools/panes.ts +112 -0
  411. package/src/tools/policy.ts +10 -1
  412. package/src/tools/presentation.ts +1 -0
  413. package/dist/chunk-D73KXYPF.js +0 -37
  414. package/dist/chunk-MLOK6ZOS.js +0 -2888
  415. package/dist/chunk-YKOFT37S.js +0 -279
  416. package/dist/doctor-TWBWFK5V.js +0 -165
@@ -112,6 +112,7 @@ export function openSideQuestionOverlay(tui: TUI, options: OpenSideQuestionOverl
112
112
  const handle = showClioOverlayFrame(tui, body, {
113
113
  anchor: "center",
114
114
  width: sideQuestionOverlayWidth(options.columns),
115
+ markerId: "side-question",
115
116
  title: SIDE_QUESTION_OVERLAY_TITLE,
116
117
  // Esc means two things here and the footer names the live one: it aborts a
117
118
  // round that is still streaming, and it closes one that has settled.
@@ -26,6 +26,7 @@ import type { NoticeLevel } from "../command-output.js";
26
26
  import { clioTheme, GLYPH } from "../theme/index.js";
27
27
  import type { LibraryInstallConfirmSubject } from "./library-install-confirm.js";
28
28
  import { openLibraryInstallConfirmOverlay } from "./library-install-confirm.js";
29
+ import { isLibraryTab, LIBRARY_TABS } from "./library-tabs.js";
29
30
  import { type ListOverlayItem, type ListOverlayTab, openListOverlay } from "./list-overlay.js";
30
31
 
31
32
  /**
@@ -53,23 +54,13 @@ const GROUP_DIAGNOSTICS = "Diagnostics";
53
54
  const GROUP_INSTALLED = "Installed";
54
55
  const GROUP_AVAILABLE = "Available";
55
56
 
56
- /** The hub's tabs, in the order ←/→ walks them. `skill` is the original view. */
57
- export const LIBRARY_TABS: ReadonlyArray<{ id: LibraryEntryKind; label: string }> = [
58
- { id: "skill", label: "Skills" },
59
- { id: "agent", label: "Agents" },
60
- { id: "prompt", label: "Prompts" },
61
- { id: "fleet", label: "Fleets" },
62
- ];
57
+ export { isLibraryTab, LIBRARY_TABS };
63
58
 
64
59
  export const SKILLS_HUB_TITLE = "Skills Hub";
65
60
 
66
61
  /** Row id prefix for a library row, which is how an action tells the tabs apart. */
67
62
  export const LIBRARY_ROW_PREFIX = "library:";
68
63
 
69
- export function isLibraryTab(value: string): value is LibraryEntryKind {
70
- return LIBRARY_TABS.some((tab) => tab.id === value);
71
- }
72
-
73
64
  /** @internal exported for contract tests */
74
65
  export const SKILLS_HUB_EMPTY =
75
66
  "no skills installed and no local marketplace configured. install one with `clio-coder skills install <path|github-url>`, or point CLIO_CODER_SKILL_CATALOG_DIR at a skills/ catalog.";
@@ -557,6 +548,7 @@ export function openSkillsHub(tui: TUI, deps: SkillsHubDeps): OverlayHandle {
557
548
  };
558
549
 
559
550
  handle = openListOverlay(tui, {
551
+ markerId: "skills-hub",
560
552
  title: SKILLS_HUB_TITLE,
561
553
  items: [],
562
554
  tabs,
@@ -416,6 +416,7 @@ export function openTreeOverlay(tui: TUI, deps: OpenTreeOverlayDeps): OverlayHan
416
416
  return showClioOverlayFrame(tui, box, {
417
417
  anchor: "center",
418
418
  width: TREE_OVERLAY_WIDTH,
419
+ markerId: "tree",
419
420
  title: "Session Tree",
420
421
  footerHint: () => view.getHint(),
421
422
  });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The one decision function for opening a run-watching pane.
3
+ *
4
+ * Policy, stated whole: panes are pulled by the operator, never pushed by the
5
+ * fleet. A dispatch starting, detaching, backgrounding, or failing opens
6
+ * nothing; the native surfaces (transcript, Alt+W board, footer) already carry
7
+ * that state, and the v0.3/v0.4 behavior of opening a hidden viewer pane per
8
+ * dispatch produced panes nobody had asked for. A pane opens only when the
9
+ * operator names a run: Enter in the workers view, `/panes show`, or the
10
+ * `panes` tool acting on the conversation's behalf.
11
+ *
12
+ * The `dispatch` source is the seam for ever marking a dispatch watchable at
13
+ * enqueue time. It exists so the decision stays in this one function; today it
14
+ * always refuses, and any future exception must be argued here, not in a
15
+ * config key.
16
+ *
17
+ * This module is a leaf on purpose: `panes-runtime.ts` is a boundaries seam
18
+ * whose value closure must stay off the render graph, so the policy cannot
19
+ * live beside the dispatch board that shares its status vocabulary.
20
+ */
21
+
22
+ export type PaneWatchSource = "workers-view" | "slash" | "tool" | "dispatch";
23
+
24
+ /** Statuses with a live process behind them, in the dispatch board's vocabulary. */
25
+ const WATCHABLE_STATUSES = new Set(["running", "stale", "cancelling", "enqueued", "retrying"]);
26
+
27
+ export interface PaneWatchRequest {
28
+ source: PaneWatchSource;
29
+ /** The run's lifecycle status; anything unrecognized reads as terminal. */
30
+ runStatus: string;
31
+ }
32
+
33
+ export type PaneWatchDecision = { open: true } | { open: false; reason: string };
34
+
35
+ export function paneWatchDecision(request: PaneWatchRequest): PaneWatchDecision {
36
+ if (request.source === "dispatch") {
37
+ return { open: false, reason: "dispatches run headless; watch a run from the workers view or /panes show" };
38
+ }
39
+ if (!WATCHABLE_STATUSES.has(request.runStatus)) {
40
+ return {
41
+ open: false,
42
+ reason: `run is ${request.runStatus}; use \`clio-coder fleet view <runId>\` for the post-mortem`,
43
+ };
44
+ }
45
+ return { open: true };
46
+ }
@@ -0,0 +1,292 @@
1
+ /**
2
+ * The one implementation of `PanesOperations`, shared by `/panes` and the
3
+ * `panes` orchestrator tool.
4
+ *
5
+ * Two rules run through it. Presets probe their binary through the toolchain
6
+ * ladder *before* the pane is split: `openUtilityPane` delivers its command by
7
+ * sending `exec <argv>` into the pane's shell, so a binary that is not there
8
+ * kills the pane the instant it appears, and the operator sees a flicker
9
+ * instead of an install hint (phase 1 report, open question 3). And every
10
+ * mutation is scoped to panes Clio created, because the contract's registry
11
+ * refuses anything else.
12
+ *
13
+ * `show` targets running dispatches, not panes: it resolves a fuzzy agent id
14
+ * or run-id prefix against the live dispatch snapshot and points the shared
15
+ * watch pane at the match, under the one policy decision in
16
+ * `pane-policy.ts`. There is no per-run pane inventory to search any more.
17
+ */
18
+
19
+ import type { ClioSettings } from "../core/config.js";
20
+ import type { DispatchSnapshot } from "../domains/dispatch/contract.js";
21
+ import {
22
+ newestRunEventJournalRunId,
23
+ runEventJournalPath,
24
+ runEventJournalRoot,
25
+ } from "../domains/dispatch/run-event-journal.js";
26
+ import type { MuxContract, MuxPaneRecord } from "../domains/mux/index.js";
27
+ import {
28
+ PANES_PRESETS,
29
+ type PanesCloseResult,
30
+ type PanesInventoryEntry,
31
+ type PanesOpenResult,
32
+ type PanesOperations,
33
+ type PanesShowResult,
34
+ type PanesStatus,
35
+ type PanesWatchController,
36
+ type PanesYaziController,
37
+ type PanesYaziStatus,
38
+ } from "../domains/mux/operations.js";
39
+ import { resolveBinary } from "../tools/executables.js";
40
+ import { type PaneWatchSource, paneWatchDecision } from "./pane-policy.js";
41
+
42
+ export interface PanesRuntimeDeps {
43
+ mux: MuxContract;
44
+ getSettings: () => Readonly<ClioSettings>;
45
+ /** Live dispatch state, used to turn a fuzzy agent id into a run id. */
46
+ getDispatchSnapshot: () => DispatchSnapshot;
47
+ getCwd: () => string;
48
+ /** Injection seam for tests; production uses the toolchain ladder. */
49
+ resolveBinaryPath?: (name: string) => string | null;
50
+ /** Journal root override for tests. */
51
+ journalRoot?: () => string;
52
+ /** Newest run whose journal exists, for the `logs` preset. */
53
+ newestJournalRunId?: () => string | null;
54
+ }
55
+
56
+ const UNAVAILABLE = "the pane layer is not available in this session";
57
+
58
+ function inventory(records: ReadonlyArray<MuxPaneRecord>): ReadonlyArray<PanesInventoryEntry> {
59
+ return records.map((record) => ({
60
+ paneId: record.ref.paneId,
61
+ tabId: record.ref.tabId,
62
+ purpose: record.purpose,
63
+ label: record.label,
64
+ adopted: record.adopted === true,
65
+ }));
66
+ }
67
+
68
+ export function createPanesRuntime(deps: PanesRuntimeDeps): PanesOperations {
69
+ const probe = deps.resolveBinaryPath ?? resolveBinary;
70
+ const journalRoot = deps.journalRoot ?? runEventJournalRoot;
71
+ let yaziController: PanesYaziController | null = null;
72
+ let watchController: PanesWatchController | null = null;
73
+ let nextPendingOpen = 0;
74
+ const pendingOpens = new Map<string, PanesInventoryEntry>();
75
+ const beginPendingOpen = (label: string): string => {
76
+ nextPendingOpen += 1;
77
+ const id = `pending:${nextPendingOpen}`;
78
+ pendingOpens.set(id, {
79
+ paneId: id,
80
+ tabId: "pending",
81
+ purpose: "utility",
82
+ label,
83
+ adopted: false,
84
+ pending: true,
85
+ });
86
+ return id;
87
+ };
88
+ const closedYaziStatus = (): PanesYaziStatus => ({
89
+ mode: "closed",
90
+ paneId: null,
91
+ paneCwd: null,
92
+ lastLineAt: null,
93
+ droppedLines: 0,
94
+ });
95
+
96
+ /**
97
+ * Resolve one operator- or model-supplied target to a live dispatched run.
98
+ *
99
+ * Fuzzy agent id first, then runId prefix, most recent run winning: "the
100
+ * tester" is what an operator types; a run id is what a tool call carries.
101
+ * Retrying runs count as live; the watch pane shows them coming back.
102
+ */
103
+ const matchRun = (target: string): { runId: string; agentId: string; status: string } | null => {
104
+ const needle = target.trim().toLowerCase();
105
+ if (needle.length === 0) return null;
106
+ const snapshot = deps.getDispatchSnapshot();
107
+ const live = [
108
+ ...snapshot.running.map((run) => ({ runId: run.runId, agentId: run.agentId, status: "running" })),
109
+ ...snapshot.retrying.map((run) => ({ runId: run.runId, agentId: run.agentId, status: "retrying" })),
110
+ ];
111
+ // Newest first, so "the tester" means the tester the operator just started.
112
+ const newestFirst = [...live].reverse();
113
+ const byAgent = newestFirst.find((run) => run.agentId.toLowerCase().includes(needle));
114
+ if (byAgent) return byAgent;
115
+ return newestFirst.find((run) => run.runId.toLowerCase().startsWith(needle)) ?? null;
116
+ };
117
+
118
+ /** argv for one preset, once its binary has been found. */
119
+ const presetArgv = (presetId: string, binaryPath: string): ReadonlyArray<string> | null => {
120
+ if (presetId === "shell") return [binaryPath, "-l"];
121
+ if (presetId === "logs") {
122
+ const runId = (deps.newestJournalRunId ?? (() => newestRunEventJournalRunId(journalRoot())))();
123
+ if (runId === null) return null;
124
+ // `-F` rather than `-f`: the journal is recreated when its run's
125
+ // directory is pruned, and a viewer following an inode would silently
126
+ // stop at that point.
127
+ return [binaryPath, "-n", "200", "-F", runEventJournalPath(runId, journalRoot())];
128
+ }
129
+ return null;
130
+ };
131
+
132
+ const show = async (target: string, source: PaneWatchSource): Promise<PanesShowResult> => {
133
+ if (!deps.mux.available()) return { status: "unavailable", reason: UNAVAILABLE };
134
+ const match = matchRun(target);
135
+ if (match === null) {
136
+ // The candidate list is what turns a miss into a next step: it names
137
+ // the runs that are actually live right now.
138
+ const snapshot = deps.getDispatchSnapshot();
139
+ const candidates = [
140
+ ...new Set([...snapshot.running.map((run) => run.agentId), ...snapshot.retrying.map((run) => run.agentId)]),
141
+ ];
142
+ return { status: "not-found", target, candidates };
143
+ }
144
+ const decision = paneWatchDecision({ source, runStatus: match.status });
145
+ if (!decision.open) return { status: "refused", reason: decision.reason };
146
+ if (watchController === null) {
147
+ return { status: "unavailable", reason: "the watch pane is not wired in this session" };
148
+ }
149
+ const watched = await watchController.watch(match.runId);
150
+ if (watched.status !== "watching") return { status: "unavailable", reason: watched.reason };
151
+ return { status: "watching", runId: match.runId, agentId: match.agentId, opened: watched.opened };
152
+ };
153
+
154
+ return {
155
+ status(): PanesStatus {
156
+ const detection = deps.mux.detection();
157
+ const panes = deps.getSettings().panes;
158
+ return {
159
+ mode: deps.mux.mode,
160
+ available: deps.mux.available(),
161
+ reason: detection.reason,
162
+ socketPath: detection.socketPath,
163
+ server: detection.server,
164
+ settings: {
165
+ enabled: panes.enabled,
166
+ notifications: panes.notifications,
167
+ journal: panes.journal,
168
+ yazi: { ...panes.yazi },
169
+ },
170
+ yazi: yaziController?.status() ?? closedYaziStatus(),
171
+ panes: [...inventory(deps.mux.list()), ...pendingOpens.values()],
172
+ };
173
+ },
174
+
175
+ show(target: string): Promise<PanesShowResult> {
176
+ // The slash command and the tool share this implementation; the policy
177
+ // treats both as operator pull, so the source only matters for logs.
178
+ return show(target, "slash");
179
+ },
180
+
181
+ async open(request: { preset?: string; argv?: ReadonlyArray<string>; once?: boolean }): Promise<PanesOpenResult> {
182
+ const cwd = deps.getCwd();
183
+ if (request.preset !== undefined) {
184
+ const preset = PANES_PRESETS.find((entry) => entry.id === request.preset);
185
+ if (!preset) {
186
+ return { status: "refused", reason: `unknown preset: ${request.preset}` };
187
+ }
188
+ if (preset.id === "yazi") {
189
+ if (!deps.getSettings().panes.yazi.enabled) {
190
+ return { status: "refused", reason: "the files pane is disabled by panes.yazi.enabled" };
191
+ }
192
+ if (!yaziController) {
193
+ return { status: "unavailable", reason: "the files-pane return path is not ready" };
194
+ }
195
+ const pendingId = beginPendingOpen(preset.id);
196
+ try {
197
+ const result = await yaziController.open(request.once ? { once: true } : undefined);
198
+ if (result.status === "opened") {
199
+ return { status: "opened", label: preset.id, paneId: result.paneId };
200
+ }
201
+ if (result.status === "missing-binary") {
202
+ return {
203
+ status: "missing-binary",
204
+ preset: preset.id,
205
+ binary: result.binary,
206
+ installHint: preset.installHint,
207
+ detail: result.detail,
208
+ };
209
+ }
210
+ return {
211
+ status: result.status === "profile-error" ? "refused" : "unavailable",
212
+ reason: result.reason,
213
+ };
214
+ } finally {
215
+ pendingOpens.delete(pendingId);
216
+ }
217
+ }
218
+ if (!deps.mux.available()) return { status: "unavailable", reason: UNAVAILABLE };
219
+ const binaryPath = probe(preset.binary);
220
+ if (binaryPath === null) {
221
+ return {
222
+ status: "missing-binary",
223
+ preset: preset.id,
224
+ binary: preset.binary,
225
+ installHint: preset.installHint,
226
+ detail: `${preset.binary} was not found (install with \`${preset.installHint}\`)`,
227
+ };
228
+ }
229
+ const argv = presetArgv(preset.id, binaryPath);
230
+ if (argv === null) {
231
+ return { status: "refused", reason: `preset ${preset.id} has nothing to show yet` };
232
+ }
233
+ const pendingId = beginPendingOpen(preset.id);
234
+ try {
235
+ const ref = await deps.mux.openUtilityPane({ argv, cwd, label: preset.id });
236
+ if (ref === null) return { status: "unavailable", reason: `pane host refused to open ${preset.id}` };
237
+ return { status: "opened", label: preset.id, paneId: ref.paneId };
238
+ } finally {
239
+ pendingOpens.delete(pendingId);
240
+ }
241
+ }
242
+ if (!deps.mux.available()) return { status: "unavailable", reason: UNAVAILABLE };
243
+ const argv = request.argv ?? [];
244
+ if (argv.length === 0) return { status: "refused", reason: "nothing to run" };
245
+ const label = argv[0] ?? "pane";
246
+ const pendingId = beginPendingOpen(label);
247
+ try {
248
+ const ref = await deps.mux.openUtilityPane({ argv, cwd, label });
249
+ if (ref === null) return { status: "unavailable", reason: `pane host refused to open ${label}` };
250
+ return { status: "opened", label, paneId: ref.paneId };
251
+ } finally {
252
+ pendingOpens.delete(pendingId);
253
+ }
254
+ },
255
+
256
+ async close(target: string): Promise<PanesCloseResult> {
257
+ if (!deps.mux.available()) return { status: "unavailable", reason: UNAVAILABLE };
258
+ const owned = deps.mux.list();
259
+ if (target === "all") {
260
+ const labels: string[] = [];
261
+ // Snapshot first: closing mutates the registry the list came from.
262
+ for (const record of [...owned]) {
263
+ if (await deps.mux.closePane(record.ref.paneId)) labels.push(record.label);
264
+ }
265
+ return { status: "closed", closed: labels.length, labels };
266
+ }
267
+ const needle = target.trim().toLowerCase();
268
+ const newestFirst = [...owned].reverse();
269
+ const match =
270
+ newestFirst.find((record) => record.ref.paneId.toLowerCase() === needle) ??
271
+ newestFirst.find((record) => record.label.toLowerCase().includes(needle)) ??
272
+ newestFirst.find((record) => record.purpose === needle);
273
+ if (!match) return { status: "not-found", target };
274
+ const closed = await deps.mux.closePane(match.ref.paneId);
275
+ return closed ? { status: "closed", closed: 1, labels: [match.label] } : { status: "not-found", target };
276
+ },
277
+
278
+ attachYazi(controller: PanesYaziController): () => void {
279
+ yaziController = controller;
280
+ return () => {
281
+ if (yaziController === controller) yaziController = null;
282
+ };
283
+ },
284
+
285
+ attachWatch(controller: PanesWatchController): () => void {
286
+ watchController = controller;
287
+ return () => {
288
+ if (watchController === controller) watchController = null;
289
+ };
290
+ },
291
+ };
292
+ }
@@ -49,6 +49,35 @@ export function renderCompactionSummaryLine(input: CompactionSummaryLineInput):
49
49
  return `[context engine] llm_summary: ${input.messagesSummarized} messages summarized to ${input.summaryChars} chars; ~${input.tokensBefore} tokens before${tail}`;
50
50
  }
51
51
 
52
+ /** Why the non-destructive stage had nothing to do before a summary ran. */
53
+ export type EvictionSkipReason = "all-protected" | "nothing-evictable" | "disabled";
54
+
55
+ export interface EvictionSkipLineInput {
56
+ reason: EvictionSkipReason;
57
+ /** Turn starts in the visible slice the policy was offered. */
58
+ turns: number;
59
+ protectLastTurns: number;
60
+ policyId: string;
61
+ }
62
+
63
+ /**
64
+ * One line saying the working-set stage was considered and declined, in the
65
+ * same `[context engine] working set:` voice its eviction notice uses. Without
66
+ * it a short session falls from the pressure threshold straight into the
67
+ * destructive summary with nothing in the transcript explaining why the cheap
68
+ * stage did not run (smoke pass 2, G1).
69
+ */
70
+ export function renderEvictionSkipLine(input: EvictionSkipLineInput): string {
71
+ const turnWord = input.turns === 1 ? "turn" : "turns";
72
+ const cause =
73
+ input.reason === "disabled"
74
+ ? "eviction is off (context.workingSet.enabled false)"
75
+ : input.reason === "all-protected"
76
+ ? `nothing evictable, all ${input.turns} ${turnWord} are inside the protected window (protectLastTurns ${input.protectLastTurns})`
77
+ : `nothing evictable by ${input.policyId} above the protected window (protectLastTurns ${input.protectLastTurns})`;
78
+ return `[context engine] working set: ${cause}; llm_summary runs instead`;
79
+ }
80
+
52
81
  export interface RenderCompactionSummaryOptions {
53
82
  /** Override the default markdown theme. Defaults to the local theme above. */
54
83
  theme?: MarkdownTheme;
@@ -21,10 +21,16 @@
21
21
  * Pure: no I/O, no module-level mutable state beyond the shared theme handle.
22
22
  */
23
23
 
24
+ import { parseJsonObjectPayload } from "../../core/json-payload.js";
24
25
  import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../../engine/tui.js";
25
26
  import { formatFooterTokens } from "../footer-panel.js";
26
27
  import { type ClioToken, clioTheme, fitUnits, formatCompactMs, GLYPH } from "../theme/index.js";
27
- import { type WorkerEntryState, type WorkerReceiptSummary, workerAskedByModel } from "../worker-stream.js";
28
+ import {
29
+ type WorkerEntryState,
30
+ type WorkerPresentedResultContract,
31
+ type WorkerReceiptSummary,
32
+ workerAskedByModel,
33
+ } from "../worker-stream.js";
28
34
 
29
35
  const theme = clioTheme();
30
36
  const dim = (text: string): string => theme.fg("dim", text);
@@ -110,7 +116,7 @@ function isPending(entry: WorkerEntryState): boolean {
110
116
  * rendered as zero: an ACP peer reports no tokens at all, so its footer names
111
117
  * the tool calls it mediated instead of claiming it spent nothing.
112
118
  */
113
- function footerUnits(receipt: WorkerReceiptSummary): string[] {
119
+ function footerUnits(entry: WorkerEntryState, receipt: WorkerReceiptSummary): string[] {
114
120
  const units = [outcomeUnit(receipt, true)];
115
121
  if (receipt.exitCode !== undefined && receipt.exitCode !== 0) {
116
122
  units.push(theme.fg("error", `exit=${receipt.exitCode}`));
@@ -122,6 +128,8 @@ function footerUnits(receipt: WorkerReceiptSummary): string[] {
122
128
  }
123
129
  if (receipt.durationMs !== undefined) units.push(dim(formatCompactMs(receipt.durationMs)));
124
130
  if (receipt.contract !== undefined) units.push(dim(`contract ${receipt.contract}`));
131
+ const presentation = presentedContractAnswer(entry);
132
+ if (presentation?.footer !== undefined) units.push(dim(presentation.footer));
125
133
  if (receipt.abandonedDetail !== undefined) units.push(theme.fg("warning", receipt.abandonedDetail));
126
134
  else if (receipt.receiptUnavailable === true) units.push(theme.fg("warning", "receipt unavailable"));
127
135
  return units;
@@ -133,23 +141,120 @@ function railLines(text: string, token: ClioToken, width: number): string[] {
133
141
  return wrapTextWithAnsi(text, contentWidth).map((row) => `${dim(RAIL)}${theme.fg(token, row)}`);
134
142
  }
135
143
 
136
- /** A worker's terminal answer when it is one JSON object, as a structured result contract asks for. */
144
+ /** A worker's terminal JSON payload, using the same tolerant reader as its result contract. */
137
145
  function structuredAnswer(text: string): Record<string, unknown> | null {
138
- const trimmed = text.trim();
139
- if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return null;
140
- try {
141
- const parsed: unknown = JSON.parse(trimmed);
142
- return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
143
- ? (parsed as Record<string, unknown>)
144
- : null;
145
- } catch {
146
- return null;
147
- }
146
+ const parsed = parseJsonObjectPayload(text);
147
+ return parsed.ok ? parsed.value : null;
148
148
  }
149
149
 
150
150
  const isStringArray = (value: unknown): value is string[] =>
151
151
  Array.isArray(value) && value.every((entry) => typeof entry === "string");
152
152
 
153
+ function reportString(value: unknown): string | null {
154
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
155
+ }
156
+
157
+ interface PresentedContractAnswer {
158
+ lines: string[];
159
+ footer?: string;
160
+ }
161
+
162
+ function debuggerReport(value: Record<string, unknown>): PresentedContractAnswer | null {
163
+ const diagnosis = reportString(value.diagnosis);
164
+ const reproduction = value.reproduction;
165
+ if (
166
+ diagnosis === null ||
167
+ (reproduction !== "reproduced" && reproduction !== "not-reproduced" && reproduction !== "unknown") ||
168
+ !isStringArray(value.evidence)
169
+ ) {
170
+ return null;
171
+ }
172
+ return {
173
+ lines: [
174
+ diagnosis,
175
+ ...(value.evidence.length === 0 ? ["Evidence: none"] : ["Evidence:", ...value.evidence.map((item) => `- ${item}`)]),
176
+ ],
177
+ footer: `reproduction ${reproduction}`,
178
+ };
179
+ }
180
+
181
+ function verifierReport(value: Record<string, unknown>): PresentedContractAnswer | null {
182
+ if ((value.verdict !== "pass" && value.verdict !== "fail") || !Array.isArray(value.checks)) return null;
183
+ const lines: string[] = [];
184
+ for (const raw of value.checks) {
185
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
186
+ const check = raw as Record<string, unknown>;
187
+ const name = reportString(check.name);
188
+ const evidence = reportString(check.evidence);
189
+ if (name === null || evidence === null || typeof check.passed !== "boolean") return null;
190
+ lines.push(`${check.passed ? GLYPH.ok : GLYPH.error} ${name}: ${evidence}`);
191
+ }
192
+ return { lines: lines.length === 0 ? ["No checks reported."] : lines, footer: `verdict ${value.verdict}` };
193
+ }
194
+
195
+ function researchReport(value: Record<string, unknown>): PresentedContractAnswer | null {
196
+ if ((value.source !== "local" && value.source !== "external") || !Array.isArray(value.findings)) return null;
197
+ const lines: string[] = [];
198
+ for (const raw of value.findings) {
199
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
200
+ const finding = raw as Record<string, unknown>;
201
+ const claim = reportString(finding.claim);
202
+ const evidence = reportString(finding.evidence);
203
+ if (claim === null || evidence === null) return null;
204
+ lines.push(`- ${claim}`, ` citation: ${evidence}`);
205
+ }
206
+ return { lines: lines.length === 0 ? ["No findings reported."] : lines, footer: `source ${value.source}` };
207
+ }
208
+
209
+ function scoutReport(value: Record<string, unknown>): PresentedContractAnswer | null {
210
+ if (!Array.isArray(value.findings) || typeof value.needsSplit !== "boolean") return null;
211
+ const lines: string[] = [];
212
+ for (const raw of value.findings) {
213
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
214
+ const finding = raw as Record<string, unknown>;
215
+ const claim = reportString(finding.claim);
216
+ if (claim === null) return null;
217
+ const path = reportString(finding.path);
218
+ const line = typeof finding.line === "number" && Number.isSafeInteger(finding.line) ? finding.line : null;
219
+ lines.push(path !== null && line !== null ? `- ${claim} — ${path}:${line}` : `- ${claim} (ungrounded lead)`);
220
+ }
221
+ if (value.needsSplit) {
222
+ if (!Array.isArray(value.proposedSubtasks)) return null;
223
+ lines.push("Split recommended:");
224
+ for (const raw of value.proposedSubtasks) {
225
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
226
+ const subtask = raw as Record<string, unknown>;
227
+ const task = reportString(subtask.task);
228
+ const id = reportString(subtask.id);
229
+ if (task === null || id === null) return null;
230
+ lines.push(`- ${id}: ${task}`);
231
+ }
232
+ }
233
+ return {
234
+ lines: lines.length === 0 ? ["No findings reported."] : lines,
235
+ footer: value.needsSplit ? "split needed" : "no split needed",
236
+ };
237
+ }
238
+
239
+ function presentedContractAnswer(entry: WorkerEntryState): PresentedContractAnswer | null {
240
+ if (entry.receipt?.contract !== "pass" || entry.receipt.contractKind === undefined || entry.droppedLines !== 0) {
241
+ return null;
242
+ }
243
+ const value = structuredAnswer(entry.text);
244
+ if (value === null) return null;
245
+ const kind: WorkerPresentedResultContract = entry.receipt.contractKind;
246
+ switch (kind) {
247
+ case "debugger-report":
248
+ return debuggerReport(value);
249
+ case "verifier-report":
250
+ return verifierReport(value);
251
+ case "research-report":
252
+ return researchReport(value);
253
+ case "scout-report":
254
+ return scoutReport(value);
255
+ }
256
+ }
257
+
153
258
  /**
154
259
  * A mutation report as prose: the paths it changed, each validation with its
155
260
  * verdict and evidence, then the summary and commit line. Null when the object
@@ -184,6 +289,8 @@ function mutationReportLines(value: Record<string, unknown>): string[] | null {
184
289
  function bodySourceLines(entry: WorkerEntryState): string[] {
185
290
  const structured = entry.droppedLines === 0 ? structuredAnswer(entry.text) : null;
186
291
  if (structured === null) return entry.text.split("\n");
292
+ const presented = presentedContractAnswer(entry);
293
+ if (presented !== null) return presented.lines;
187
294
  return mutationReportLines(structured) ?? JSON.stringify(structured, null, 2).split("\n");
188
295
  }
189
296
 
@@ -236,7 +343,8 @@ function failureLines(entry: WorkerEntryState, width: number): string[] {
236
343
 
237
344
  /** The receipt line, whole units only; a unit that would not fit is dropped behind a dim ellipsis. */
238
345
  function footerLine(entry: WorkerEntryState, width: number): string {
239
- const units = isPending(entry) || entry.receipt === undefined ? [pendingUnit(entry)] : footerUnits(entry.receipt);
346
+ const units =
347
+ isPending(entry) || entry.receipt === undefined ? [pendingUnit(entry)] : footerUnits(entry, entry.receipt);
240
348
  return fitUnits(theme, dim(FOOTER), units, width);
241
349
  }
242
350