@iowarp/clio-coder 0.4.4 → 0.4.6

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 (598) hide show
  1. package/CHANGELOG.md +116 -2
  2. package/CONTRIBUTING.md +39 -24
  3. package/NOTICE +16 -0
  4. package/README.md +343 -403
  5. package/dist/{acp-WNAYYF4F.js → acp-UVEVWSDL.js} +25 -24
  6. package/dist/{agents-3OKXHLOI.js → agents-VWY37DTK.js} +78 -77
  7. package/dist/assets/codewiki.json +1 -1
  8. package/dist/{auth-VKNNMGPU.js → auth-PCV2XQDV.js} +38 -38
  9. package/dist/builtins-PD652MTK.js +22 -0
  10. package/dist/{chunk-TQAHXW6Y.js → chunk-22PQ4TX4.js} +4 -4
  11. package/dist/{chunk-NRO2BJRH.js → chunk-24T2YZ7A.js} +10686 -4464
  12. package/dist/{chunk-Y55JBDO5.js → chunk-2AKYFREM.js} +38 -23
  13. package/dist/{chunk-NEDJ26B5.js → chunk-2GPGAE5W.js} +6 -51
  14. package/dist/{chunk-IKCO5N3L.js → chunk-2QPI3TR3.js} +3 -3
  15. package/dist/{chunk-HRQMFLJP.js → chunk-32KJPXAM.js} +6 -6
  16. package/dist/{chunk-65DEGPJ6.js → chunk-3A3CUMKA.js} +6 -4
  17. package/dist/{chunk-UXCU4E3T.js → chunk-3BJGMT2D.js} +35 -14
  18. package/dist/{chunk-5ICU3EUH.js → chunk-3HQVBIKD.js} +5 -5
  19. package/dist/{chunk-GWZNEVM2.js → chunk-3JLFK5MW.js} +9 -9
  20. package/dist/{chunk-XXYSBZIQ.js → chunk-3O2ZAUGQ.js} +4 -4
  21. package/dist/{chunk-GX5WYQO4.js → chunk-3QKXHCGS.js} +8 -8
  22. package/dist/{chunk-JCI2ROMZ.js → chunk-3T4DXWP6.js} +7 -7
  23. package/dist/{chunk-DR52UMZW.js → chunk-3UW3KU4A.js} +2 -2
  24. package/dist/{chunk-OEJSLEPW.js → chunk-3WBALRQH.js} +337 -337
  25. package/dist/{chunk-ELWDPP3Y.js → chunk-4DU2TVI4.js} +8 -8
  26. package/dist/{chunk-JSD46VO2.js → chunk-4U4TKZFR.js} +43 -36
  27. package/dist/{chunk-FEAXX7B6.js → chunk-4VMV6OS6.js} +4 -4
  28. package/dist/{chunk-2ZSONWVL.js → chunk-536HXRFE.js} +13 -13
  29. package/dist/{chunk-W54I7H25.js → chunk-5A6XR3RJ.js} +44 -19
  30. package/dist/{chunk-YPC6ZR5L.js → chunk-5CMARGPF.js} +119 -5
  31. package/dist/{chunk-KXDSS5WJ.js → chunk-5CY4Q6QI.js} +4 -4
  32. package/dist/{chunk-D4CJV46V.js → chunk-5GZICIYO.js} +3 -3
  33. package/dist/{chunk-4IUZQIJ3.js → chunk-5PHVPU4G.js} +2 -2
  34. package/dist/{chunk-Q4WO54TA.js → chunk-5QRX7SLS.js} +11 -11
  35. package/dist/{chunk-3ODX73FK.js → chunk-5VTJP36Q.js} +4 -4
  36. package/dist/chunk-5ZSZEYJK.js +119 -0
  37. package/dist/{chunk-EBY63B7G.js → chunk-6EXUUOSN.js} +2 -2
  38. package/dist/{chunk-DT3LWJOB.js → chunk-6HAO6X5B.js} +24 -6
  39. package/dist/{chunk-SVNXH4L2.js → chunk-6ICZKDUC.js} +2 -2
  40. package/dist/{chunk-K6T2ZAMZ.js → chunk-6OII3LOK.js} +6 -6
  41. package/dist/{chunk-36CT5VVL.js → chunk-6RYDAVQT.js} +138 -89
  42. package/dist/{chunk-I7XBWTYH.js → chunk-6VRRJT7L.js} +5 -5
  43. package/dist/{chunk-DQOVN6KV.js → chunk-6YA46L5A.js} +4 -4
  44. package/dist/{chunk-NZMNUPZZ.js → chunk-77CZRFTE.js} +3 -3
  45. package/dist/{chunk-NAI6ZFCY.js → chunk-7JSSKXG2.js} +122 -11
  46. package/dist/{chunk-2VTFPG5O.js → chunk-7WLRF2U5.js} +2 -2
  47. package/dist/chunk-7WUZEB2M.js +165 -0
  48. package/dist/{chunk-3UUXNFEX.js → chunk-7YJWEF2L.js} +61 -34
  49. package/dist/{chunk-3EBYEESD.js → chunk-A46ILRS2.js} +5 -5
  50. package/dist/{chunk-IXIY2H4R.js → chunk-ACCKVSHU.js} +5 -5
  51. package/dist/{chunk-UOIZ7DA4.js → chunk-AP7HDJOP.js} +2 -2
  52. package/dist/{chunk-JEIYHLOR.js → chunk-B3MOMLJ4.js} +12 -12
  53. package/dist/{chunk-APJ265NV.js → chunk-B5GGDIHT.js} +146 -146
  54. package/dist/{chunk-PMZVFWSR.js → chunk-B5ZTR6KZ.js} +2 -2
  55. package/dist/{chunk-I5FWO7L5.js → chunk-B6UM3OZC.js} +2 -2
  56. package/dist/{chunk-2CU2H6KE.js → chunk-BBV5TKE6.js} +5 -5
  57. package/dist/{chunk-23L32XTI.js → chunk-BLTEAUCB.js} +68 -39
  58. package/dist/{chunk-4O6MANBS.js → chunk-BS4GAMUB.js} +14 -10
  59. package/dist/{chunk-XULDXHTN.js → chunk-BT7HGDOX.js} +105 -53
  60. package/dist/{chunk-S66XZJOF.js → chunk-C27U2TYG.js} +2 -2
  61. package/dist/{chunk-QZWQA4DE.js → chunk-CBSKDFWL.js} +3 -3
  62. package/dist/{chunk-2LEAKD4C.js → chunk-CKK25WN3.js} +3 -3
  63. package/dist/{chunk-BYP5D4HI.js → chunk-CTTI7G3T.js} +2 -2
  64. package/dist/{chunk-XAKHZX5N.js → chunk-CVDAFGPW.js} +4 -4
  65. package/dist/{chunk-RKKLTLYB.js → chunk-CXQ37WJH.js} +3 -3
  66. package/dist/{chunk-GTUD2WMY.js → chunk-DBREN25X.js} +44 -9
  67. package/dist/{chunk-NDDGJUXD.js → chunk-DNCE23JJ.js} +2 -2
  68. package/dist/{chunk-HVDIIIQW.js → chunk-DNHVRBH4.js} +3 -3
  69. package/dist/{chunk-D7E7WCDF.js → chunk-DVNAPPXU.js} +2 -2
  70. package/dist/{chunk-6UINWWS6.js → chunk-DXLUVUD4.js} +16 -16
  71. package/dist/{chunk-YXLYO42X.js → chunk-DXVMLM3Z.js} +3 -2
  72. package/dist/{chunk-5MEZN6CB.js → chunk-DZK63TZJ.js} +6 -6
  73. package/dist/{chunk-IWHMRKLL.js → chunk-E3LEPLQ2.js} +2 -2
  74. package/dist/{chunk-JT2RFCC5.js → chunk-E7RF4KBO.js} +5 -5
  75. package/dist/{chunk-3R73A4XB.js → chunk-ECBHH7LU.js} +3 -3
  76. package/dist/chunk-ECOMVOHP.js +139 -0
  77. package/dist/chunk-EFSEIZE6.js +56 -0
  78. package/dist/{chunk-4NSRCOYP.js → chunk-EHOSNVVM.js} +3 -2
  79. package/dist/chunk-EM2WNQS2.js +93 -0
  80. package/dist/{chunk-57XXR6DR.js → chunk-F5Z7EWMN.js} +967 -246
  81. package/dist/{chunk-HAXOFFRH.js → chunk-FQOOGXBH.js} +7 -7
  82. package/dist/{chunk-ZTJUVLTU.js → chunk-FXFVDPWY.js} +2 -2
  83. package/dist/{chunk-XPLRXC72.js → chunk-GD55RTIW.js} +3 -3
  84. package/dist/{chunk-5B2AEOW5.js → chunk-GH5ZR6JP.js} +472 -472
  85. package/dist/{chunk-TOIVGRUX.js → chunk-GJ24ODAX.js} +5 -5
  86. package/dist/{chunk-EXLD33WO.js → chunk-GLYBJ4RQ.js} +24 -9
  87. package/dist/{chunk-26QSH3EJ.js → chunk-HHVJNVUJ.js} +5 -5
  88. package/dist/{chunk-F4EKGO4N.js → chunk-HL26ADLH.js} +4 -2
  89. package/dist/{chunk-FFUPXJC4.js → chunk-HPTJF6DG.js} +32 -56
  90. package/dist/{chunk-ID64D7PE.js → chunk-HXHFDHB6.js} +24 -24
  91. package/dist/{chunk-CUSRQKPU.js → chunk-HZ2T2YED.js} +299 -89
  92. package/dist/{chunk-YD5GIKET.js → chunk-HZFBVU5X.js} +49 -580
  93. package/dist/{chunk-6QOTUPRG.js → chunk-I2K2NCZQ.js} +65 -17
  94. package/dist/{chunk-SZAA6XDG.js → chunk-IFDRAD6F.js} +2 -2
  95. package/dist/{chunk-I2DWJ4GM.js → chunk-IGLVVNEG.js} +54 -42
  96. package/dist/{chunk-6Q3CYFD3.js → chunk-IKRWLEDA.js} +186 -35
  97. package/dist/{chunk-2JH2WHGE.js → chunk-J2FYEQ5L.js} +4 -4
  98. package/dist/{chunk-BMS5RKQY.js → chunk-JABWPKFY.js} +3 -3
  99. package/dist/{chunk-2DSOYNFC.js → chunk-JMPL7NUB.js} +7 -7
  100. package/dist/{chunk-QDC3K2U3.js → chunk-K2BLQC5E.js} +3 -3
  101. package/dist/{chunk-AUPNRN7C.js → chunk-K2HL3GBL.js} +4 -4
  102. package/dist/{chunk-54X7T7DK.js → chunk-KAFMW7WM.js} +72 -81
  103. package/dist/{chunk-OZNBF4L3.js → chunk-KECNXMDE.js} +2 -2
  104. package/dist/{chunk-BTK3SSSK.js → chunk-KMF54VH3.js} +2 -2
  105. package/dist/chunk-KPRLAMP4.js +32 -0
  106. package/dist/{chunk-ECH6PKUQ.js → chunk-KQ6IDQIB.js} +2 -2
  107. package/dist/{chunk-3UNOLWNZ.js → chunk-KTFSFCVN.js} +20 -9
  108. package/dist/{chunk-IRXAATOX.js → chunk-L4N3DWNW.js} +3 -3
  109. package/dist/{chunk-JQLNNIKT.js → chunk-LIN2K3EL.js} +6 -6
  110. package/dist/{chunk-2STGEDFV.js → chunk-LK2FN5JY.js} +2 -2
  111. package/dist/{chunk-LCGCVYZ4.js → chunk-LQTKDAUO.js} +2 -2
  112. package/dist/{chunk-U6TMQNSI.js → chunk-LTR5QCLS.js} +4 -4
  113. package/dist/chunk-LUL5Z66S.js +221 -0
  114. package/dist/{chunk-ELZVTCGV.js → chunk-LVFANMOH.js} +151 -327
  115. package/dist/{chunk-H7IXIC72.js → chunk-LY4N4TTL.js} +17 -3
  116. package/dist/{chunk-VKPAQYEB.js → chunk-LZBMXNLP.js} +5 -5
  117. package/dist/{chunk-UL3WSD3F.js → chunk-M4KFUXS7.js} +2 -2
  118. package/dist/{chunk-NXYCB2VD.js → chunk-M6TAYK66.js} +6 -6
  119. package/dist/{chunk-CFGTUFWB.js → chunk-MCJKEGHO.js} +2 -2
  120. package/dist/{chunk-6XLNIQDB.js → chunk-MYI32N4R.js} +2 -2
  121. package/dist/{chunk-4WR7VSYB.js → chunk-N2OKBREN.js} +23 -9
  122. package/dist/{chunk-3GY4F45V.js → chunk-NC4KV7EL.js} +6 -6
  123. package/dist/{chunk-LLXSDWXS.js → chunk-NF3BK5WR.js} +5 -5
  124. package/dist/{chunk-4M6Z5QVF.js → chunk-NNWJYUNO.js} +11 -11
  125. package/dist/chunk-NTK3WTHM.js +141 -0
  126. package/dist/{chunk-GYV6VZOC.js → chunk-NTTS3O53.js} +2 -2
  127. package/dist/{chunk-4BJ5BYCE.js → chunk-O3VV6IVK.js} +2 -2
  128. package/dist/{chunk-W6GROXXM.js → chunk-O5F25NIE.js} +2 -2
  129. package/dist/{chunk-NXIMQY5W.js → chunk-ODUTYZIU.js} +5 -5
  130. package/dist/{chunk-KFV5L5SK.js → chunk-OQM5IVJY.js} +118 -232
  131. package/dist/{chunk-75W7L2E2.js → chunk-P5JMBPDJ.js} +1052 -171
  132. package/dist/{chunk-XRZT5WY5.js → chunk-P5SC4DLJ.js} +4 -4
  133. package/dist/{chunk-S6PYF2XF.js → chunk-P6M7RKMI.js} +4 -4
  134. package/dist/{chunk-R6Q67RJH.js → chunk-PECBKCDK.js} +34 -6
  135. package/dist/{chunk-33YXPOE3.js → chunk-PPEZJWUG.js} +2 -2
  136. package/dist/{chunk-RXTN6AKH.js → chunk-PWWSIB2A.js} +5 -5
  137. package/dist/{chunk-DXKJURES.js → chunk-PZN2LQY6.js} +14 -11
  138. package/dist/{chunk-N56KALIC.js → chunk-Q7NNUHQV.js} +62 -35
  139. package/dist/{chunk-RQCKCSRL.js → chunk-QCOV5DFY.js} +22 -22
  140. package/dist/{chunk-3DPEIQKN.js → chunk-QH3ZTUOA.js} +2 -2
  141. package/dist/{chunk-NEKRRTYW.js → chunk-QJMRYTCX.js} +8 -8
  142. package/dist/{chunk-YNFKXPEC.js → chunk-QNWRHOMF.js} +35 -66
  143. package/dist/{chunk-YAKGTXEV.js → chunk-QRFXEO6Y.js} +2 -2
  144. package/dist/{chunk-35MSIRKH.js → chunk-QUIJZSDW.js} +2 -2
  145. package/dist/{chunk-72YIHOZQ.js → chunk-QXFBABFJ.js} +10 -10
  146. package/dist/chunk-R2CT5YDO.js +66 -0
  147. package/dist/{chunk-HHV2GANA.js → chunk-R2P2U3ZS.js} +2 -2
  148. package/dist/{chunk-IGLP3ODT.js → chunk-RGZNQ5CT.js} +15 -15
  149. package/dist/chunk-SD7SHNDW.js +15 -0
  150. package/dist/chunk-SL2RDHKO.js +19 -0
  151. package/dist/{chunk-NUGM5KR6.js → chunk-SRCEQKQS.js} +2 -2
  152. package/dist/{chunk-OMQNJVKW.js → chunk-SRZOW5L2.js} +43 -13
  153. package/dist/{chunk-UEPWCCTY.js → chunk-SXQKNKRA.js} +12 -12
  154. package/dist/{chunk-3M6DQK6S.js → chunk-T334FW2H.js} +7 -7
  155. package/dist/{chunk-V76WTFTW.js → chunk-T52TPHYC.js} +6 -6
  156. package/dist/{chunk-5636DCO5.js → chunk-TJFDL26W.js} +59 -12
  157. package/dist/{chunk-3I7MS7N2.js → chunk-TK2BMCBZ.js} +2 -2
  158. package/dist/{chunk-5OIVVPHF.js → chunk-TN5FW4XY.js} +13 -6
  159. package/dist/{chunk-LL4KHSZI.js → chunk-U2MSJ57E.js} +2 -2
  160. package/dist/{chunk-FTMGRKEF.js → chunk-U44YYLVV.js} +4 -4
  161. package/dist/{chunk-YECAMM3D.js → chunk-U7H5WRG5.js} +5 -5
  162. package/dist/{chunk-5TUB6SLS.js → chunk-UFYNOQK4.js} +7 -7
  163. package/dist/{chunk-R346GLFC.js → chunk-UHW5V7HC.js} +2 -2
  164. package/dist/{chunk-VIA6RFQZ.js → chunk-UQIVHSE4.js} +4 -4
  165. package/dist/{chunk-RAY4OVGZ.js → chunk-URBGEOH3.js} +5 -5
  166. package/dist/chunk-USZA7GWL.js +341 -0
  167. package/dist/{chunk-ZA4VCIGV.js → chunk-UUQ5G6EZ.js} +60 -41
  168. package/dist/{chunk-VG7TBQIY.js → chunk-V552WL53.js} +68 -4
  169. package/dist/{chunk-7UGL4MB5.js → chunk-VAHLPXPY.js} +10 -10
  170. package/dist/{chunk-25QBEXRS.js → chunk-VANN3GDP.js} +7 -26
  171. package/dist/{chunk-BWH6N36D.js → chunk-VHKTOLEW.js} +272 -42
  172. package/dist/{chunk-QUFRYSWI.js → chunk-VHMX7SXF.js} +129 -48
  173. package/dist/{chunk-336NSSKI.js → chunk-VNBICSS2.js} +2 -2
  174. package/dist/{chunk-VAVR4IJY.js → chunk-VQD7NH7Z.js} +3 -3
  175. package/dist/{chunk-LBMZMYH2.js → chunk-W2RJEH6Q.js} +3 -3
  176. package/dist/{chunk-7CR24IG7.js → chunk-WDV62RKY.js} +2 -2
  177. package/dist/{chunk-BJVFZO5U.js → chunk-WJIMKGD6.js} +7 -7
  178. package/dist/{chunk-USR47QNF.js → chunk-WKUYQBCG.js} +14 -14
  179. package/dist/{chunk-GHS5EBTQ.js → chunk-WNGF5AJX.js} +36 -22
  180. package/dist/{chunk-MXHC5QYU.js → chunk-WTQGRBHV.js} +5 -144
  181. package/dist/{chunk-6OSVSQL5.js → chunk-WV6TDAPN.js} +41 -23
  182. package/dist/chunk-WXA2CFJO.js +23 -0
  183. package/dist/{chunk-2ASED4PZ.js → chunk-X3BI7HTV.js} +26 -26
  184. package/dist/{chunk-QSNYB6ZV.js → chunk-XCUDZQLX.js} +3 -3
  185. package/dist/{chunk-RZDWV63N.js → chunk-XWMSPJYN.js} +6 -6
  186. package/dist/chunk-YGDMBK7O.js +567 -0
  187. package/dist/{chunk-6PAZTBPA.js → chunk-YPRSNUTC.js} +15 -6
  188. package/dist/{chunk-2IR2NMPA.js → chunk-ZIDFQJRB.js} +2 -2
  189. package/dist/{chunk-LTIKRKFL.js → chunk-ZS43Y2FT.js} +4 -4
  190. package/dist/{chunk-6EJMN2Y3.js → chunk-ZTNFYMIO.js} +2 -2
  191. package/dist/{chunk-WEPFGWHJ.js → chunk-ZZFWWYLX.js} +2 -2
  192. package/dist/cli/index.js +44 -44
  193. package/dist/clio-QQCMTUZE.js +26 -0
  194. package/dist/{code-nav-IJR2DBPR.js → code-nav-GUPM6766.js} +48 -32
  195. package/dist/codewiki/build-worker.js +76 -29
  196. package/dist/{compile-cache-OQAVB7Y5.js → compile-cache-WJEPUZEJ.js} +4 -4
  197. package/dist/{components-2TGAI2RC.js → components-TNIKXCBH.js} +13 -13
  198. package/dist/{config-IUA6OYNS.js → config-IIM7YOD3.js} +211 -124
  199. package/dist/configure-4ZM5HWJX.js +73 -0
  200. package/dist/{context-BOYF5EJM.js → context-44BZLJ3J.js} +62 -31
  201. package/dist/{context-4MPR7WKB.js → context-AIDVQKMW.js} +126 -121
  202. package/dist/{context-2DKHWH2T.js → context-OL5P3HEP.js} +92 -62
  203. package/dist/context-clear-OQV76MYP.js +184 -0
  204. package/dist/{context-index-HNG3MOME.js → context-index-HP2JFUWA.js} +13 -11
  205. package/dist/{context-map-COB37XXN.js → context-map-INTXD54D.js} +71 -21
  206. package/dist/{context-working-set-3I3FYX6Y.js → context-working-set-ZUW3EGLS.js} +32 -31
  207. package/dist/detail-UXPWRANG.js +115 -0
  208. package/dist/{dispatch-runner-RJ5I2F2O.js → dispatch-runner-P3ZW6G77.js} +192 -116
  209. package/dist/{docs-SPOV3BAN.js → docs-BL7ROUFN.js} +4 -4
  210. package/dist/{doctor-DKICC2SN.js → doctor-JJVGPZWT.js} +59 -58
  211. package/dist/{eval-OQOQUDHK.js → eval-V2RTN3UH.js} +476 -170
  212. package/dist/{eval-inventory-Y6QRFOH5.js → eval-inventory-QOYZERXJ.js} +7 -7
  213. package/dist/evidence-35PYWGSO.js +225 -0
  214. package/dist/{evidence-4DQ25GUQ.js → evidence-CIGGKI6J.js} +111 -94
  215. package/dist/{evolve-GSS52E5J.js → evolve-33SM3NJP.js} +108 -91
  216. package/dist/{extensions-G7MFLYHT.js → extensions-YVRHX6ZI.js} +16 -16
  217. package/dist/{fleet-Q37YHHAQ.js → fleet-LFO7SIPC.js} +147 -146
  218. package/dist/{fleet-commands-G7E4N7SM.js → fleet-commands-RLK72GU3.js} +27 -26
  219. package/dist/{fleet-decisions-O7M6QBA2.js → fleet-decisions-3SBSCTCW.js} +16 -15
  220. package/dist/{fleet-graph-TOUBW6OW.js → fleet-graph-JZIBCGMW.js} +39 -38
  221. package/dist/{fleet-inspect-SW33JJNI.js → fleet-inspect-2L5YGGZJ.js} +108 -91
  222. package/dist/{fleet-new-RDVJLHHH.js → fleet-new-RCKGSRDV.js} +3 -3
  223. package/dist/{fleet-preflight-CV2655TW.js → fleet-preflight-QRSHIIDE.js} +10 -9
  224. package/dist/{fleet-validate-KESZX2YH.js → fleet-validate-LIAVKDUU.js} +46 -42
  225. package/dist/{fleet-verify-UQPMTVE3.js → fleet-verify-JIK3OQZR.js} +106 -89
  226. package/dist/fleet-view-IWHYGQKK.js +118 -0
  227. package/dist/{init-PXEXQSBF.js → init-NJEDOTQW.js} +124 -123
  228. package/dist/{install-NKNG7KD4.js → install-3RZDDP23.js} +5 -5
  229. package/dist/{interop-ZG5T62U3.js → interop-RVCAYECG.js} +25 -25
  230. package/dist/inventory-74UL7UHU.js +118 -0
  231. package/dist/{library-B2W4N74O.js → library-CTKZA7K3.js} +45 -44
  232. package/dist/{memory-YCANYS5A.js → memory-I76I5VUH.js} +108 -93
  233. package/dist/{models-GERTU3YI.js → models-D4YDIQZQ.js} +64 -62
  234. package/dist/{monitor-CPNIUULB.js → monitor-NKVD55HX.js} +137 -107
  235. package/dist/{orchestrator-J4BSH4WQ.js → orchestrator-I6OATVPK.js} +1715 -1687
  236. package/dist/{panes-BOHAEGYC.js → panes-7BTYH3XQ.js} +8 -8
  237. package/dist/{panes-NXSLDQZ2.js → panes-WPOMEMRH.js} +22 -22
  238. package/dist/{paths-VSUWNC22.js → paths-J3KN7UAO.js} +12 -12
  239. package/dist/preload-EJAWJEIO.js +116 -0
  240. package/dist/{registry-LG64LTF4.js → registry-DESGAXWK.js} +3 -3
  241. package/dist/{reset-TNWTB5LU.js → reset-4XMQKDNZ.js} +22 -20
  242. package/dist/{resources-4PXNMD5G.js → resources-TVYVXK3Q.js} +41 -40
  243. package/dist/{run-D6XJ34CN.js → run-OWQNNSJU.js} +164 -157
  244. package/dist/{share-2NWMJJEE.js → share-NPZZMUXG.js} +43 -42
  245. package/dist/{skills-KR7WON5G.js → skills-OXFEAWMT.js} +47 -46
  246. package/dist/{skills-eval-O2ZNOLDS.js → skills-eval-J5TCKSJA.js} +128 -107
  247. package/dist/{skills-inventory-ZZOUBK7O.js → skills-inventory-7II4DM57.js} +41 -40
  248. package/dist/slash-commands-MDSDSWYF.js +92 -0
  249. package/dist/{steer-XA25PSCS.js → steer-S7D7IKPU.js} +7 -7
  250. package/dist/{support-7EMVWYG2.js → support-5VAEC32W.js} +14 -14
  251. package/dist/{targets-OMH2XCSN.js → targets-KILZEOCE.js} +161 -94
  252. package/dist/tasks-R6P6OV22.js +36 -0
  253. package/dist/{terminal-lease-C2J3JYRE.js → terminal-lease-LZCG2KN3.js} +12 -11
  254. package/dist/tools-6OCQ6SVB.js +28 -0
  255. package/dist/{trace-FXMXUZUF.js → trace-JHLXWUMN.js} +18 -17
  256. package/dist/{tree-sitter-HGKH6LG4.js → tree-sitter-AQUUOSYM.js} +6 -6
  257. package/dist/{tui-primitives-K5PASC3O.js → tui-primitives-RCY2HN7G.js} +3 -3
  258. package/dist/{uninstall-HALS6BLF.js → uninstall-FXYMFFRT.js} +17 -17
  259. package/dist/{upgrade-MS72RJEP.js → upgrade-D2OZH6IQ.js} +42 -41
  260. package/dist/{usage-NHG6MCJM.js → usage-PIASNUS4.js} +137 -105
  261. package/dist/{verifiers-7AUNVXDY.js → verifiers-LTHUY2PX.js} +28 -27
  262. package/dist/verify-HYF7KBVE.js +30 -0
  263. package/dist/{version-KGEG7EPO.js → version-RFPKO6T7.js} +4 -4
  264. package/dist/{web-fetch-V4FKSDAV.js → web-fetch-MQN4FH3X.js} +169 -18
  265. package/dist/wiki-generate-RK7JQ5NP.js +615 -0
  266. package/dist/{with-panes-BDQEWBRT.js → with-panes-OFTNAR4Y.js} +34 -20
  267. package/dist/worker/entry.js +108 -95
  268. package/dist/{workspace-ZJ6BFM3Q.js → workspace-DZ7JADBG.js} +6 -6
  269. package/docs/README.md +7 -6
  270. package/docs/architecture/acp.md +10 -0
  271. package/docs/architecture/architecture.md +5 -5
  272. package/docs/architecture/context-engine.md +45 -60
  273. package/docs/architecture/context-working-set.md +7 -9
  274. package/docs/architecture/dispatch-architecture-rationale.md +1 -1
  275. package/docs/architecture/evidence-and-memory.md +10 -0
  276. package/docs/architecture/pi-boundary.md +37 -7
  277. package/docs/architecture/prompt-envelope-and-tools.md +7 -7
  278. package/docs/architecture/session-lifecycle.md +4 -4
  279. package/docs/architecture/tui-design.md +45 -30
  280. package/docs/architecture/worker-dispatch-mechanics.md +4 -0
  281. package/docs/guide/commands-and-modes.md +75 -61
  282. package/docs/guide/configuration-and-targets.md +126 -26
  283. package/docs/guide/configuration-reference.md +22 -7
  284. package/docs/guide/environment-variables.md +6 -2
  285. package/docs/guide/fleet-dispatch.md +13 -6
  286. package/docs/guide/glossary.md +1 -1
  287. package/docs/guide/installation-and-lifecycle.md +79 -20
  288. package/docs/guide/proactive-memory.md +1 -1
  289. package/docs/guide/skills-marketplace.md +11 -3
  290. package/docs/guide/tool-usage.md +9 -2
  291. package/docs/process/development-pipeline.md +11 -11
  292. package/docs/process/documentation-coverage.md +1 -1
  293. package/docs/process/documentation-guide.md +2 -2
  294. package/docs/process/eval-runner.md +15 -0
  295. package/docs/process/evals-internal.md +1 -1
  296. package/docs/process/release-cut-checklist.md +24 -12
  297. package/evals/README.md +4 -0
  298. package/evals/behavioral-corpus-grader.mjs +147 -14
  299. package/evals/behavioral-model-negative-control.yaml +1 -0
  300. package/evals/behavioral-model.yaml +266 -1
  301. package/evals/fixtures/docs-retrieval.json +102 -0
  302. package/evals/fixtures/scalar-decision.json +17 -0
  303. package/evals/fixtures/scout-citation-pipeline.json +55 -0
  304. package/evals/fixtures/source-explanation.json +74 -0
  305. package/evals/fixtures/tool-arguments.json +18 -0
  306. package/evals/fixtures/tool-surface/read.txt +4 -0
  307. package/evals/fixtures/tool-surface/subdir/keep.txt +1 -0
  308. package/evals/no-edit-memory-grader.mjs +149 -0
  309. package/evals/scalar-decision-grader.py +41 -0
  310. package/evals/scalar-decision-machinery.ts +145 -0
  311. package/evals/source-explanation-grader.mjs +118 -0
  312. package/evals/tool-surface-grader.mjs +53 -0
  313. package/evals/tool-surface-model.yaml +274 -0
  314. package/package.json +14 -14
  315. package/skills/README.md +6 -6
  316. package/skills/registry.yaml +1 -1
  317. package/skills/skill-marketplace.json +1 -1
  318. package/src/cli/ask.ts +10 -10
  319. package/src/cli/bootstrap-generate.ts +6 -0
  320. package/src/cli/config-inspect.ts +24 -7
  321. package/src/cli/config-trust.ts +62 -0
  322. package/src/cli/config.ts +8 -0
  323. package/src/cli/configure-editor.ts +51 -0
  324. package/src/cli/configure-interop.ts +1 -1
  325. package/src/cli/configure-oauth.ts +1 -1
  326. package/src/cli/configure-onboarding.ts +232 -96
  327. package/src/cli/configure-prompts.ts +76 -0
  328. package/src/cli/configure-quick.ts +274 -0
  329. package/src/cli/configure-target.ts +14 -2
  330. package/src/cli/configure.ts +413 -174
  331. package/src/cli/context-map.ts +75 -14
  332. package/src/cli/context.ts +46 -17
  333. package/src/cli/eval.ts +17 -3
  334. package/src/cli/modes/print.ts +32 -4
  335. package/src/cli/oauth-manual-input.ts +1 -1
  336. package/src/cli/reset.ts +2 -0
  337. package/src/cli/run.ts +16 -14
  338. package/src/cli/select.ts +35 -4
  339. package/src/cli/skills-eval.ts +10 -8
  340. package/src/cli/steer-channel.ts +31 -18
  341. package/src/cli/targets.ts +83 -31
  342. package/src/cli/tasks.ts +10 -1
  343. package/src/cli/upgrade.ts +11 -8
  344. package/src/cli/usage.ts +24 -0
  345. package/src/cli/wiki-generate.ts +239 -96
  346. package/src/core/bash-exec.ts +43 -15
  347. package/src/core/bus-events.ts +15 -0
  348. package/src/core/config.ts +39 -15
  349. package/src/core/defaults.ts +38 -15
  350. package/src/{interactive → core}/external-editor.ts +4 -4
  351. package/src/core/file-references.ts +11 -1
  352. package/src/core/incomplete-installation.ts +3 -1
  353. package/src/core/settings-layers.ts +21 -5
  354. package/src/core/termination.ts +26 -9
  355. package/src/core/untrusted-content.ts +16 -0
  356. package/src/core/workspace-trust.ts +179 -0
  357. package/src/domains/agents/builtins/coder.md +4 -2
  358. package/src/domains/agents/builtins/context-bootstrap.md +3 -1
  359. package/src/domains/agents/builtins/documenter.md +7 -4
  360. package/src/domains/agents/builtins/researcher.md +1 -0
  361. package/src/domains/agents/builtins/scout.md +7 -1
  362. package/src/domains/agents/result-contract.ts +199 -26
  363. package/src/domains/config/classify.ts +1 -1
  364. package/src/domains/config/keybindings.ts +3 -31
  365. package/src/domains/context/bootstrap-prompt.ts +11 -9
  366. package/src/domains/context/bootstrap.ts +74 -39
  367. package/src/domains/context/clio-md.ts +88 -26
  368. package/src/domains/context/codewiki/build-operation.ts +121 -0
  369. package/src/domains/context/codewiki/build-worker-protocol.ts +37 -3
  370. package/src/domains/context/codewiki/build-worker.ts +3 -35
  371. package/src/domains/context/codewiki/coordinator.ts +36 -6
  372. package/src/domains/context/codewiki/indexer.ts +59 -19
  373. package/src/domains/context/contract.ts +6 -2
  374. package/src/domains/context/extension.ts +72 -10
  375. package/src/domains/context/fingerprint.ts +39 -34
  376. package/src/domains/context/index.ts +7 -1
  377. package/src/domains/context/prompt-context.ts +55 -14
  378. package/src/domains/context/refresh.ts +9 -57
  379. package/src/domains/context/wiki/assemble.ts +14 -10
  380. package/src/domains/context/wiki/evidence.ts +209 -0
  381. package/src/domains/context/wiki/generate.ts +201 -67
  382. package/src/domains/context/wiki/meta.ts +10 -2
  383. package/src/domains/context/wiki/plan-store.ts +70 -14
  384. package/src/domains/context/wiki/plan.ts +71 -14
  385. package/src/domains/context/wiki/prompts.ts +42 -1
  386. package/src/domains/context/wiki/source-content.ts +106 -0
  387. package/src/domains/context/wiki/staleness.ts +33 -22
  388. package/src/domains/context/working-set/contract.ts +8 -2
  389. package/src/domains/context/working-set/engine.ts +17 -4
  390. package/src/domains/context/working-set/fold.ts +15 -2
  391. package/src/domains/context/working-set/marker.ts +2 -1
  392. package/src/domains/context/working-set/project.ts +18 -7
  393. package/src/domains/context/working-set/recall.ts +82 -56
  394. package/src/domains/dispatch/active-route-planner.ts +6 -3
  395. package/src/domains/dispatch/admission-queue.ts +19 -7
  396. package/src/domains/dispatch/admission.ts +3 -3
  397. package/src/domains/dispatch/budget-envelope.ts +47 -82
  398. package/src/domains/dispatch/capacity-lease.ts +57 -19
  399. package/src/domains/dispatch/contract.ts +10 -2
  400. package/src/domains/dispatch/event-pump.ts +28 -7
  401. package/src/domains/dispatch/execution-role.ts +49 -2
  402. package/src/domains/dispatch/execution-scheduler.ts +2 -0
  403. package/src/domains/dispatch/extension.ts +190 -105
  404. package/src/domains/dispatch/fleet-run.ts +20 -0
  405. package/src/domains/dispatch/gate-role-prompts.ts +4 -2
  406. package/src/domains/dispatch/parallel-writers.ts +34 -0
  407. package/src/domains/dispatch/reservation-store.ts +8 -34
  408. package/src/domains/dispatch/route-approval.ts +3 -3
  409. package/src/domains/dispatch/route-observer.ts +4 -2
  410. package/src/domains/dispatch/route-quality.ts +6 -1
  411. package/src/domains/dispatch/types.ts +4 -3
  412. package/src/domains/dispatch/worker-spawn.ts +58 -21
  413. package/src/domains/dispatch/workspace-checkpoint.ts +57 -0
  414. package/src/domains/eval/compare/behavioral.ts +81 -14
  415. package/src/domains/eval/compare/compare.ts +15 -5
  416. package/src/domains/eval/compare/envelope.ts +12 -5
  417. package/src/domains/eval/metrics/context.ts +24 -1
  418. package/src/domains/eval/metrics/tracked.ts +4 -28
  419. package/src/domains/eval/reports/comparison.ts +9 -9
  420. package/src/domains/eval/runners/clio-run.ts +5 -1
  421. package/src/domains/eval/schema/suite.ts +2 -0
  422. package/src/domains/eval/schema/validate.ts +1 -0
  423. package/src/domains/eval/suites/run.ts +189 -13
  424. package/src/domains/eval/verifiers/integrity.ts +39 -0
  425. package/src/domains/eval/verifiers/patch.ts +58 -2
  426. package/src/domains/eval/workspaces/temp-copy.ts +3 -0
  427. package/src/domains/evidence/build.ts +4 -2
  428. package/src/domains/interop/registry.ts +2 -2
  429. package/src/domains/middleware/hooks-io.ts +45 -26
  430. package/src/domains/middleware/hooks.ts +6 -0
  431. package/src/domains/middleware/injection-screen.ts +23 -0
  432. package/src/domains/middleware/rules.ts +5 -1
  433. package/src/domains/middleware/stalled-turn.ts +18 -3
  434. package/src/domains/middleware/task-board-reminder.ts +4 -2
  435. package/src/domains/middleware/task-nudge.ts +4 -1
  436. package/src/domains/mux/contract.ts +34 -1
  437. package/src/domains/mux/dock-controller.ts +8 -1
  438. package/src/domains/mux/index.ts +1 -0
  439. package/src/domains/mux/socket-client.ts +13 -3
  440. package/src/domains/observability/contract.ts +2 -2
  441. package/src/domains/observability/projection.ts +30 -9
  442. package/src/domains/prompts/compiler.ts +10 -2
  443. package/src/domains/prompts/contract.ts +7 -4
  444. package/src/domains/prompts/extension.ts +28 -64
  445. package/src/domains/prompts/fragments/identity/clio.md +23 -0
  446. package/src/domains/prompts/fragments/operating/contract.md +10 -0
  447. package/src/domains/prompts/fragments/operating/delegation.md +5 -0
  448. package/src/domains/prompts/fragments/wiki/page.md +28 -15
  449. package/src/domains/prompts/fragments/wiki/plan.md +4 -4
  450. package/src/domains/prompts/preload-prefix.ts +31 -0
  451. package/src/domains/prompts/preload.ts +135 -61
  452. package/src/domains/providers/auth/backend-file.ts +15 -11
  453. package/src/domains/providers/auth/storage.ts +3 -5
  454. package/src/domains/providers/catalog.ts +18 -3
  455. package/src/domains/providers/probe/fingerprint.ts +13 -6
  456. package/src/domains/providers/probe/reasoning.ts +2 -0
  457. package/src/domains/providers/runtime-resolution.ts +11 -9
  458. package/src/domains/providers/runtimes/cloud/openrouter.ts +2 -0
  459. package/src/domains/providers/runtimes/common/probe-helpers.ts +23 -7
  460. package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +7 -2
  461. package/src/domains/providers/runtimes/protocol/openai-compat.ts +10 -4
  462. package/src/domains/resources/skills/promotion.ts +8 -1
  463. package/src/domains/safety/action-classifier.ts +9 -0
  464. package/src/domains/safety/autonomy.ts +10 -1
  465. package/src/domains/safety/contract.ts +5 -1
  466. package/src/domains/safety/default-path-policy.ts +11 -0
  467. package/src/domains/safety/extension.ts +11 -2
  468. package/src/domains/safety/policy-engine.ts +189 -32
  469. package/src/domains/safety/project-policy.ts +7 -2
  470. package/src/domains/safety/workspace-trust.ts +43 -0
  471. package/src/domains/session/compaction/compact.ts +384 -21
  472. package/src/domains/session/compaction/cut-point.ts +42 -5
  473. package/src/domains/session/compaction/tokens.ts +9 -1
  474. package/src/domains/session/decision-board.ts +43 -33
  475. package/src/domains/session/entries.ts +169 -4
  476. package/src/domains/session/prompt-manifest.ts +47 -8
  477. package/src/domains/session/task-board.ts +21 -3
  478. package/src/domains/user-tasks/handoff.ts +16 -0
  479. package/src/engine/acp/adapter.ts +44 -12
  480. package/src/engine/acp/server.ts +1 -0
  481. package/src/engine/acp/transport.ts +2 -1
  482. package/src/engine/acp/types.ts +6 -1
  483. package/src/engine/antigravity/subprocess-runtime.ts +20 -4
  484. package/src/engine/api-registry.ts +19 -2
  485. package/src/engine/apis/openai-completions.ts +13 -6
  486. package/src/engine/apis/output-budget.ts +26 -10
  487. package/src/engine/claude/sdk-runtime.ts +4 -3
  488. package/src/engine/env-api-keys.ts +2 -1
  489. package/src/engine/external-subprocess.ts +14 -3
  490. package/src/engine/loop-guard.ts +19 -2
  491. package/src/engine/provider-diagnostics.ts +129 -0
  492. package/src/engine/provider-payload.ts +3 -1
  493. package/src/engine/worker-runtime.ts +13 -2
  494. package/src/engine/worker-tools.ts +5 -1
  495. package/src/entry/extension-reload.ts +1 -0
  496. package/src/entry/orchestrator.ts +53 -15
  497. package/src/interactive/application-controller.ts +1 -31
  498. package/src/interactive/chat-loop-messages.ts +12 -4
  499. package/src/interactive/chat-loop.ts +115 -45
  500. package/src/interactive/chat-panel.ts +163 -637
  501. package/src/interactive/chat-renderer.ts +73 -30
  502. package/src/interactive/context-recall-command.ts +2 -1
  503. package/src/interactive/dispatch-board.ts +18 -5
  504. package/src/interactive/editor-submit.ts +21 -31
  505. package/src/interactive/footer/dashboard.ts +8 -3
  506. package/src/interactive/footer/widgets.ts +50 -91
  507. package/src/interactive/interactive-application.ts +24 -44
  508. package/src/interactive/interactive-event-projection.ts +9 -45
  509. package/src/interactive/interactive-input-runtime.ts +3 -28
  510. package/src/interactive/interactive-presentation.ts +45 -16
  511. package/src/interactive/interactive-slash-runtime.ts +5 -40
  512. package/src/interactive/layout.ts +31 -5
  513. package/src/interactive/model-session-replay.ts +2 -2
  514. package/src/interactive/overlay-frame.ts +1 -0
  515. package/src/interactive/overlay-general-openers.ts +5 -1
  516. package/src/interactive/overlay-lifecycle.ts +1 -0
  517. package/src/interactive/overlay-session-lifecycle.ts +7 -1
  518. package/src/interactive/overlays/ask-user.ts +1 -0
  519. package/src/interactive/overlays/message-picker.ts +1 -1
  520. package/src/interactive/overlays/settings.ts +9 -8
  521. package/src/interactive/renderers/preview.ts +16 -0
  522. package/src/interactive/renderers/tool-execution.ts +84 -161
  523. package/src/interactive/renderers/worker-entry.ts +74 -43
  524. package/src/interactive/slash-autocomplete.ts +1 -6
  525. package/src/interactive/slash-commands.ts +16 -46
  526. package/src/interactive/status/controller.ts +0 -5
  527. package/src/interactive/status/state-machine.ts +44 -26
  528. package/src/interactive/status/types.ts +3 -0
  529. package/src/interactive/status/verbs.ts +13 -12
  530. package/src/interactive/transcript-detail.ts +50 -112
  531. package/src/interactive/turn-context.ts +221 -53
  532. package/src/interactive/turn-middleware.ts +3 -1
  533. package/src/interactive/turn-persistence.ts +3 -0
  534. package/src/interactive/turn-runtime.ts +11 -2
  535. package/src/interactive/turn-state.ts +10 -9
  536. package/src/interactive/view/artifacts.ts +4 -0
  537. package/src/interactive/view/view-overlay.ts +44 -34
  538. package/src/interactive/watch-pane.ts +18 -4
  539. package/src/interactive/worker-stream.ts +7 -7
  540. package/src/tools/agent-tools.ts +6 -3
  541. package/src/tools/bash.ts +1 -1
  542. package/src/tools/builtin-tool-catalog.ts +9 -2
  543. package/src/tools/codewiki/code-nav.ts +4 -10
  544. package/src/tools/compete-judge-task.ts +71 -0
  545. package/src/tools/compete-worktrees.ts +34 -0
  546. package/src/tools/context/index.ts +32 -7
  547. package/src/tools/context/surface.ts +15 -4
  548. package/src/tools/decide.ts +1 -1
  549. package/src/tools/dispatch-admission.ts +12 -1
  550. package/src/tools/dispatch-arguments.ts +38 -1
  551. package/src/tools/dispatch-plan.ts +7 -7
  552. package/src/tools/dispatch-runner.ts +48 -32
  553. package/src/tools/dispatch-schema.ts +30 -23
  554. package/src/tools/dispatch-scout-admission.ts +10 -44
  555. package/src/tools/dispatch-scout.ts +2 -3
  556. package/src/tools/find.ts +13 -4
  557. package/src/tools/grep.ts +13 -1
  558. package/src/tools/ls.ts +7 -1
  559. package/src/tools/monitor.ts +26 -11
  560. package/src/tools/network-policy.ts +11 -24
  561. package/src/tools/observation.ts +39 -2
  562. package/src/tools/presentation.ts +1 -1
  563. package/src/tools/read.ts +112 -15
  564. package/src/tools/registry.ts +55 -18
  565. package/src/tools/result-shaping.ts +110 -2
  566. package/src/tools/tasks.ts +33 -7
  567. package/src/tools/verify/frontend.ts +67 -30
  568. package/src/tools/verify/numeric.ts +10 -5
  569. package/src/tools/web-fetch-network.ts +179 -0
  570. package/src/tools/web-fetch-surface.ts +2 -1
  571. package/src/tools/web-fetch.ts +15 -13
  572. package/src/tools/worker-evidence.ts +27 -0
  573. package/src/worker/spec-contract.ts +12 -8
  574. package/dist/builtins-WGALA46I.js +0 -22
  575. package/dist/chunk-2WZ546HR.js +0 -792
  576. package/dist/chunk-5PSMVOLM.js +0 -56
  577. package/dist/chunk-EL24TAU4.js +0 -1560
  578. package/dist/chunk-IZXGRF7P.js +0 -4048
  579. package/dist/chunk-JVCV3ICN.js +0 -47
  580. package/dist/chunk-MNJGS2IN.js +0 -33
  581. package/dist/chunk-O5CVSAG5.js +0 -122
  582. package/dist/chunk-ODGTEFFI.js +0 -50
  583. package/dist/chunk-OQE5J4C6.js +0 -73
  584. package/dist/chunk-UUVG37B4.js +0 -244
  585. package/dist/chunk-V6HJFQZE.js +0 -228
  586. package/dist/clio-QLICPCF5.js +0 -25
  587. package/dist/configure-VEPX4NMX.js +0 -55
  588. package/dist/context-clear-S4ZJCQUX.js +0 -182
  589. package/dist/detail-A7JAVSIG.js +0 -98
  590. package/dist/evidence-4F5USFKH.js +0 -208
  591. package/dist/fleet-view-MN2VG4MR.js +0 -101
  592. package/dist/inventory-C26CFDRR.js +0 -101
  593. package/dist/preload-P6DGH2PZ.js +0 -13
  594. package/dist/slash-commands-ZXPJD64J.js +0 -92
  595. package/dist/tasks-IPAGMEIX.js +0 -36
  596. package/dist/tools-EFFEAIDP.js +0 -28
  597. package/dist/verify-FWYGPKMR.js +0 -29
  598. package/dist/wiki-generate-743CIGJW.js +0 -476
@@ -1,25 +1,23 @@
1
1
  import { performance } from "node:perf_hooks";
2
- import type { OutputVerbosity } from "../core/defaults.js";
2
+ import type { OutputStyle } from "../core/defaults.js";
3
3
  import { SKILL_SUGGESTION_PREFIX } from "../core/skill-activation.js";
4
4
  import { rawDurationMs } from "../core/timers.js";
5
+ import { redactSecretString } from "../domains/safety/redaction.js";
5
6
  import { type Component, Markdown, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../engine/tui.js";
6
7
  import type { AgentMessage } from "../engine/types.js";
7
- import { toolPresentationPolicy } from "../tools/presentation.js";
8
- import { toolResultPresentationPolicy } from "../tools/result-disposition.js";
9
8
  import type { ChatLoopEvent, RetryStatusPayload } from "./chat-loop.js";
10
9
  import { extractText, isSelfExplainingAbort } from "./chat-loop-messages.js";
11
10
  import type { ApprovalRequestView } from "./permission-overlay.js";
12
11
  import { codeInk } from "./renderers/code-ink.js";
13
12
  import { createMermaidMarkdownTransform } from "./renderers/mermaid.js";
14
13
  import { styleTaggedNotice } from "./renderers/notice.js";
14
+ import { previewBudget, previewRows } from "./renderers/preview.js";
15
15
  import { formatRetryStatus } from "./renderers/retry-status.js";
16
16
  import {
17
+ canGroupObservation,
17
18
  renderToolAwaitingApproval,
18
- renderToolCallHeader,
19
19
  renderToolExecution,
20
- renderToolRunningStatus,
21
- renderToolStreamingExecution,
22
- renderToolSubline,
20
+ renderToolPreview,
23
21
  } from "./renderers/tool-execution.js";
24
22
  import { renderWorkerEntryLines } from "./renderers/worker-entry.js";
25
23
  import {
@@ -28,28 +26,15 @@ import {
28
26
  foldMessageIntoRunTally,
29
27
  formatReasoningChip,
30
28
  formatReasoningLabel,
31
- INLINE_STATUS_INDENT_COLS,
32
29
  type ReasoningTokenProvenance,
33
30
  type ReasoningUsageView,
34
31
  reasoningFromTally,
35
- type StatusPhase,
36
32
  UNMEASURED_REASONING,
37
- type VerbRender,
38
33
  } from "./status/index.js";
39
34
  import { clioTheme, fgSequence, GLYPH, markdownTheme, SGR_DIM, SGR_RESET } from "./theme/index.js";
40
- import {
41
- type Fold,
42
- type FoldOverride,
43
- policyRunningToolFold,
44
- policyThinkingFold,
45
- policyToolFold,
46
- policyWorkerFold,
47
- resolveFold,
48
- type TranscriptDetailPolicy,
49
- toggledFold,
50
- transcriptDetail,
51
- } from "./transcript-detail.js";
52
- import { type WorkerEntryState, workerAskedByModel } from "./worker-stream.js";
35
+ import { type TranscriptDetailPolicy, transcriptDetail } from "./transcript-detail.js";
36
+ import type { ViewArtifact } from "./view/artifacts.js";
37
+ import type { WorkerEntryState } from "./worker-stream.js";
53
38
 
54
39
  // Fenced code reaches the screen through pi-tui's Markdown component, which
55
40
  // exposes the MarkdownTheme.highlightCode hook: it hands over the raw fence
@@ -73,8 +58,6 @@ const RESET = SGR_RESET;
73
58
  const DIM = SGR_DIM;
74
59
  const TEAL = fgSequence("accent");
75
60
  const BLUE_REASON = fgSequence("reason");
76
- const GREEN_OK = fgSequence("success");
77
- const AMBER = fgSequence("warning");
78
61
  const RED_CRIT = fgSequence("error");
79
62
  const AGENT_GLYPH = GLYPH.agent;
80
63
  const USER_GLYPH = GLYPH.user;
@@ -97,6 +80,8 @@ const USER_GLYPH = GLYPH.user;
97
80
  export type { ReasoningTokenProvenance } from "./status/index.js";
98
81
 
99
82
  export interface ChatPanelTurnUsage {
83
+ elapsedMs?: number;
84
+ outcome?: string;
100
85
  inputTokens: number;
101
86
  outputTokens: number;
102
87
  cacheReadTokens: number;
@@ -164,13 +149,6 @@ type ToolSegment = {
164
149
  settledWithoutResult?: boolean | undefined;
165
150
  /** True when the finished result was an error. Meaningful only after `finished`. */
166
151
  isError: boolean;
167
- /**
168
- * The operator's explicit fold for this block, or none. The effective state
169
- * is this override when set, else what the transcript detail policy gives
170
- * the call (through the tool's presentation once it has finished). Cleared
171
- * when `/output` changes and when a session is switched in.
172
- */
173
- fold?: FoldOverride;
174
152
  /** Wall-clock start time captured by the chat panel for live duration display. */
175
153
  startedAtMs?: number;
176
154
  /** Completed call duration in milliseconds (event-supplied or measured locally). */
@@ -239,17 +217,13 @@ type ThinkingSegment = {
239
217
  finalized: boolean;
240
218
  /** Panel clock when the first delta of this stretch arrived. */
241
219
  startedAtMs?: number;
242
- /** The operator's explicit fold for this stretch, or none. */
243
- fold?: FoldOverride;
244
220
  };
245
221
  type AssistantSegment = TextSegment | ToolSegment | ErrorSegment | ThinkingSegment;
246
222
  /**
247
223
  * A caller-rendered block receives the frame's transcript detail policy so a
248
- * block that owns fold state (the operator's `!` bash row) resolves it the same
249
- * way the panel resolves a model call. Blocks that ignore it are unaffected.
224
+ * block such as the operator's `!` bash row follows the same preset as the panel. Blocks that ignore it are unaffected.
250
225
  */
251
- type ReplayBlockRenderer = (width: number, detail: TranscriptDetailPolicy) => string[];
252
- type AssistantStatusLine = { phase: StatusPhase; verb: string; toneHint: VerbRender["toneHint"] };
226
+ type ReplayBlockRenderer = (width: number, detail: TranscriptDetailPolicy, unbounded?: boolean) => string[];
253
227
 
254
228
  type TranscriptEntry =
255
229
  | { role: "user"; text: string; status?: () => UserTurnStatus }
@@ -266,17 +240,15 @@ type TranscriptEntry =
266
240
  */
267
241
  messageStartSegmentIndex?: number | undefined;
268
242
  pending: boolean;
269
- statusLine?: AssistantStatusLine | null | undefined;
270
243
  isError: boolean;
271
244
  turnUsage?: ChatPanelTurnUsage;
272
245
  }
273
246
  /**
274
- * A dispatched worker's attributed block. The panel owns only the fold
275
- * override; `state` is the live object the worker-stream reducer mutates, so
247
+ * A dispatched worker's attributed block. `state` is the live object the worker-stream reducer mutates, so
276
248
  * a streaming delta reaches the screen without copying the entry per frame.
277
249
  * The panel is told when that happened through `applyWorkerState`.
278
250
  */
279
- | { role: "worker"; state: WorkerEntryState; fold?: FoldOverride }
251
+ | { role: "worker"; state: WorkerEntryState }
280
252
  /**
281
253
  * A block the caller renders itself. Most are settled the moment they are
282
254
  * appended, but a few (the operator's `!` bash row) keep mutating the state
@@ -288,26 +260,10 @@ type TranscriptEntry =
288
260
  role: "replayBlock";
289
261
  renderBlock: ReplayBlockRenderer;
290
262
  isLive?: (() => boolean) | undefined;
291
- fold?: ReplayBlockFoldControl | undefined;
292
263
  };
293
264
 
294
265
  type WorkerTranscriptEntry = Extract<TranscriptEntry, { role: "worker" }>;
295
266
 
296
- /**
297
- * Fold state a caller-rendered block owns. The panel never stores it: the
298
- * block's closure reads it when rendering, so the panel only needs to resolve
299
- * and flip it when the operator uses an expand/collapse key, and to clear it
300
- * when `/output` changes. Same tri-state as a tool segment: an override, or
301
- * none, over the fold the policy gives the block.
302
- */
303
- export interface ReplayBlockFoldControl {
304
- /** The fold the policy gives this block when no override is set. */
305
- policyFold(detail: TranscriptDetailPolicy): Fold;
306
- /** The operator's override, or none. */
307
- fold(): FoldOverride;
308
- setFold(fold: FoldOverride): void;
309
- }
310
-
311
267
  /**
312
268
  * Whether a painted operator turn exists in the ledger yet. `pending` is the
313
269
  * window between the editor being consumed and admission; `refused` is a
@@ -327,7 +283,7 @@ export interface ChatPanel extends Component {
327
283
  * that keeps changing after the append, so the panel keeps re-rendering it
328
284
  * instead of treating the first frame as final.
329
285
  */
330
- appendReplayBlock(renderBlock: ReplayBlockRenderer, isLive?: () => boolean, fold?: ReplayBlockFoldControl): void;
286
+ appendReplayBlock(renderBlock: ReplayBlockRenderer, isLive?: () => boolean): void;
331
287
  applyEvent(event: ChatLoopEvent): void;
332
288
  /** Mark a just-rehydrated tool segment so its mutation diff remains plain. */
333
289
  markToolReplayed?(toolCallId: string): void;
@@ -344,57 +300,17 @@ export interface ChatPanel extends Component {
344
300
  * so what the operator can share is exactly what the operator can see.
345
301
  */
346
302
  workerStates(): ReadonlyArray<WorkerEntryState>;
347
- setStatusLine(line: AssistantStatusLine | null): void;
348
- /**
349
- * Publish the live run tally's reasoning projection. The pending entry's
350
- * tail line reads this; passing null returns it to unmeasured, which is what
351
- * an idle or just-started turn is.
352
- */
353
- setLiveReasoning(view: ReasoningUsageView | null): void;
354
- /**
355
- * Flip the newest foldable block (tool call, worker card, or fold-owning
356
- * replay block) away from its effective state. The flip is an override
357
- * over the transcript detail policy: under `/output verbose` it folds an
358
- * open block, under `/output minimal` it opens a folded one.
359
- */
360
- toggleLastToolExpanded(): boolean;
361
- /** Set an explicit override on every tool, worker, and fold-owning block at once. */
362
- toggleAllToolsExpanded(): boolean;
363
- /**
364
- * Drop every operator override so each block returns to what the transcript
365
- * detail policy gives it. `/output` changes and session switches do this:
366
- * the operator asked for a new baseline, and what they had opened belonged
367
- * to the transcript they left. Idempotent.
368
- */
369
- clearFoldOverrides(): void;
370
- /**
371
- * Flip the newest thinking stretch between the one-line dim marker
372
- * and the full rail-prefixed body, as an override over the policy. A turn
373
- * with no thinking yet is left alone; a new stretch inherits the policy.
374
- */
375
- toggleLastThinking(): boolean;
376
- toggleAllThinking(): boolean;
377
- /** Whether live thinking would render open this frame, which presentation pacing consults. */
303
+ inspectionArtifacts(): ViewArtifact[];
304
+ /** Whether the current preset shows supplied reasoning, for stream pacing. */
378
305
  isThinkingExpanded(): boolean;
379
- /** Toggle whether expanded live tool bodies include cumulative partial output. */
380
- toggleLiveToolOutput(): boolean;
381
306
  /** Clears the visible transcript. /new uses this after rotating the session. */
382
307
  reset(): void;
383
308
  }
384
309
 
385
310
  export interface ChatPanelOptions {
386
- /**
387
- * Resolves the user-visible key string for the `clio-coder.tool.expand`
388
- * action, which folds and unfolds the newest tool call or worker block.
389
- * Returning a non-empty string surfaces a dim ` (<key>)` hint on the one
390
- * surface the key would act on: the latest finished collapsed tool subline,
391
- * or the newest folded worker card. Returning undefined or an empty string
392
- * suppresses the hint. Called per render so live keybinding changes flow
393
- * through.
394
- */
395
- getToolExpandKey?: () => string | undefined;
311
+ getTerminalRows?: () => number;
396
312
  /** Live transcript detail mode. Settings changes take effect on the next frame. */
397
- getOutputVerbosity?: () => OutputVerbosity;
313
+ getOutputStyle?: () => OutputStyle;
398
314
  /** Receives measured panel render cost; no FPS claim is made by the panel. */
399
315
  onRenderMetrics?: (metrics: ChatPanelRenderMetrics) => void;
400
316
  /** Clock injection for deterministic duration tests. Defaults to Date.now. */
@@ -556,10 +472,6 @@ function scopeTerminalErrorAfterSuccessfulTool(
556
472
  return `[error] ${modelFailure}${detachedDispatchSucceeded ? "; detached runs continue" : ""}`;
557
473
  }
558
474
 
559
- function hasThinking(entry: Extract<TranscriptEntry, { role: "assistant" }>): boolean {
560
- return entry.segments.some((seg) => seg.kind === "thinking" && seg.text.length > 0);
561
- }
562
-
563
475
  /** The thinking segment still receiving deltas, which is always the tail. */
564
476
  function openThinkingSegment(entry: Extract<TranscriptEntry, { role: "assistant" }>): ThinkingSegment | null {
565
477
  const tail = entry.segments[entry.segments.length - 1];
@@ -597,11 +509,6 @@ function lastAssistantIndex(
597
509
  return null;
598
510
  }
599
511
 
600
- function hasStreamingText(entry: Extract<TranscriptEntry, { role: "assistant" }>): boolean {
601
- const tail = entry.segments[entry.segments.length - 1];
602
- return tail?.kind === "text" && !tail.finalized && tail.text.trim().length > 0;
603
- }
604
-
605
512
  /**
606
513
  * Locate the skill-suggestion protocol line inside a reply.
607
514
  *
@@ -765,9 +672,7 @@ function appendUserRowTail(rendered: string[], tail: string, width: number): voi
765
672
  * Static marker used when thinking is folded. This matches pi-coding-agent's
766
673
  * hidden-thinking presentation and avoids previewing reasoning content.
767
674
  */
768
- const THINKING_HIDDEN_LABEL = `Thinking${GLYPH.ellipsis}`;
769
- const THINKING_LINE_LIMIT = 12;
770
-
675
+ const THINKING_HIDDEN_LABEL = "Thinking · /view";
771
676
  function dimLine(text: string, width: number): string {
772
677
  return `${DIM}${truncateToWidth(text, Math.max(1, width), GLYPH.ellipsis, false)}${RESET}`;
773
678
  }
@@ -786,62 +691,12 @@ function renderSettledThinkingMarker(view: ReasoningUsageView, width: number): s
786
691
  );
787
692
  }
788
693
 
789
- /**
790
- * The live turn's one reasoning line, rendered where the open thinking segment
791
- * sits, which is the tail of the entry by construction: the first text, tool,
792
- * or message_end closes it. Anchoring reasoning at the head put it above every
793
- * streamed segment, so on a long turn the only progress indicator scrolled off
794
- * the top; pinning one line at the tail put it below prose that streamed in
795
- * after the model had already moved on, so the transcript read out of order.
796
- *
797
- * The count is whatever the run tally has folded so far, so between model calls
798
- * the line states elapsed and nothing else. Visible thinking text is never
799
- * counted: that number moved with how much reasoning the provider chose to
800
- * display, not with what the turn spent.
801
- */
802
- function renderLiveReasoningLine(view: ReasoningUsageView, elapsedMs: number | undefined, width: number): string {
803
- const chip = formatReasoningChip(view, compactReasoningTokens);
804
- const head = chip === null ? THINKING_HIDDEN_LABEL : `Thinking · ${chip} ${formatReasoningLabel(view)}`;
805
- const seconds = elapsedMs === undefined ? 0 : Math.floor(Math.max(0, elapsedMs) / 1000);
806
- return dimLine(seconds > 0 ? `${head} · ${seconds}s` : head, width);
807
- }
808
-
809
- /**
810
- * Render the expanded thinking body: the text dimmed behind a dim `│ ` rail,
811
- * capped at `THINKING_LINE_LIMIT` lines. A streaming turn keeps the tail (the
812
- * reasoning still arriving); a settled one keeps the head with a
813
- * `... N more lines hidden` overflow message. Mirrors the tool toggle's
814
- * lab-notebook minimalism: no colored glyphs, no boxes.
815
- */
816
- function renderThinkingRail(thinking: string, width: number, streaming: boolean, unbounded = false): string[] {
817
- if (thinking.length === 0) return [];
818
- const splitLines = thinking.split("\n");
819
- let visible: string[];
820
- if (unbounded) {
821
- // /export reproduces the whole transcript; the live cap is a screen budget.
822
- visible = splitLines;
823
- } else if (streaming) {
824
- if (splitLines.length > THINKING_LINE_LIMIT) {
825
- const hiddenCount = splitLines.length - THINKING_LINE_LIMIT;
826
- visible = [`… ${hiddenCount} earlier lines hidden`, ...splitLines.slice(-THINKING_LINE_LIMIT)];
827
- } else {
828
- visible = splitLines;
829
- }
830
- } else {
831
- visible =
832
- splitLines.length > THINKING_LINE_LIMIT
833
- ? [...splitLines.slice(0, THINKING_LINE_LIMIT), `... ${splitLines.length - THINKING_LINE_LIMIT} more lines hidden`]
834
- : splitLines;
835
- }
836
- const out: string[] = [];
837
- const bodyWidth = Math.max(1, width - 2);
838
- for (const raw of visible) {
839
- const wrappedLines = raw.length === 0 ? [""] : wrapTextWithAnsi(raw, bodyWidth);
840
- for (const wrapped of wrappedLines) {
841
- out.push(`${BLUE_REASON}│ ${RESET}${DIM}${wrapped}${RESET}`);
842
- }
843
- }
844
- return out;
694
+ /** Wrap first, then keep the same tail both while streaming and after settlement. */
695
+ function renderThinkingRail(thinking: string, width: number, limit: number, unbounded = false): string[] {
696
+ const rows = wrapTextWithAnsi(redactSecretString(thinking), Math.max(1, width - 2)).map(
697
+ (row) => `${BLUE_REASON}│ ${RESET}${DIM}${row}${RESET}`,
698
+ );
699
+ return unbounded ? rows : previewRows(rows, limit, width, true);
845
700
  }
846
701
 
847
702
  /**
@@ -860,7 +715,7 @@ function renderTurnUsageLine(
860
715
  if (receipt === "none") return [];
861
716
  if (receipt === "compact") {
862
717
  const receipt = truncateToWidth(
863
- ` turn · in ${usage.inputTokens} · out ${usage.outputTokens}`,
718
+ ` ${usage.outcome ?? "Done"}${usage.elapsedMs === undefined ? "" : ` · ${Math.round(usage.elapsedMs / 1000)}s`}`,
864
719
  width,
865
720
  GLYPH.ellipsis,
866
721
  false,
@@ -897,138 +752,65 @@ function renderTurnUsageLine(
897
752
  );
898
753
  }
899
754
 
900
- /** The indent resolveInlineVerb budgets against when it fits the verb to the terminal. */
901
- const STATUS_INDENT = " ".repeat(INLINE_STATUS_INDENT_COLS);
902
-
903
- function styleStatusVerb(text: string, toneHint: VerbRender["toneHint"]): string {
904
- if (toneHint === "error") return `${RED_CRIT}${text}${RESET}`;
905
- if (toneHint === "warn") return `${AMBER}${text}${RESET}`;
906
- if (toneHint === "ok") return `${GREEN_OK}${text}${RESET}`;
907
- return `${DIM}${text}${RESET}`;
908
- }
909
-
910
- /**
911
- * The fold the policy gives a tool segment this frame: the running-tool rule
912
- * while in flight, the tool body rule (through the tool's own presentation)
913
- * once finished, and the error rule for a finished failure.
914
- */
915
- function policySegmentFold(seg: ToolSegment, detail: TranscriptDetailPolicy): Fold {
916
- if (!seg.finished) return policyRunningToolFold(detail);
917
- if (seg.isError && detail.errors === "body") return "expanded";
918
- return policyToolFold(detail, toolResultPresentationPolicy(seg.result) ?? toolPresentationPolicy(seg.name, seg.args));
919
- }
920
-
921
- /** Effective state of a tool segment: the operator's override, else the policy. */
922
- function toolSegmentExpanded(seg: ToolSegment, detail: TranscriptDetailPolicy): boolean {
923
- return resolveFold(seg.fold, policySegmentFold(seg, detail)) === "expanded";
924
- }
925
-
926
755
  function renderToolSegmentLines(
927
756
  seg: ToolSegment,
928
757
  width: number,
929
- expandKey: string | undefined,
930
- latestHintToolId: string | null,
931
758
  nowMs: number,
932
- unboundedToolBodies: boolean,
759
+ unbounded: boolean,
933
760
  detail: TranscriptDetailPolicy,
934
- liveToolOutput: boolean,
761
+ terminalRows: number,
935
762
  ): string[] {
936
- const hintKey = seg.id === latestHintToolId ? expandKey : undefined;
937
- const expanded = toolSegmentExpanded(seg, detail);
938
- const elapsedMs = seg.startedAtMs !== undefined ? Math.max(0, rawDurationMs(seg.startedAtMs, nowMs)) : undefined;
939
- const phase: "forming" | "ready" | "running" = seg.executionStarted
940
- ? "running"
941
- : seg.argsComplete
942
- ? "ready"
943
- : "forming";
944
- // A parked call is not executing: the awaiting-approval line replaces the
945
- // counting elapsed spinner in both collapsed and expanded form (there is no
946
- // body or partial output to expand while the call sits at the gate).
947
- if (!seg.finished && seg.awaitingApproval === true) {
948
- return renderToolAwaitingApproval(
949
- { toolCallId: seg.id, toolName: seg.name, args: seg.args },
950
- width,
951
- seg.approvalView,
952
- );
953
- }
954
- if (!expanded) {
955
- return renderToolSubline(
956
- seg.finished
957
- ? {
958
- toolCallId: seg.id,
959
- toolName: seg.name,
960
- args: seg.args,
961
- result: seg.result,
962
- isError: seg.isError,
963
- durationMs: seg.durationMs,
964
- resultSummary: seg.resultSummary,
965
- outcome: seg.settlement,
966
- blockReason: seg.blockReason,
967
- evictedReason: seg.evictedReason,
968
- }
969
- : { toolCallId: seg.id, toolName: seg.name, args: seg.args, elapsedMs, phase },
970
- width,
971
- hintKey,
972
- {
973
- diffStyle: seg.replayed === true ? "plain" : "color",
974
- foldedExtras: detail.toolBody === "folded" ? "none" : "per-tool",
975
- },
976
- );
977
- }
978
- if (!seg.finished) {
979
- if (liveToolOutput && seg.partialResult !== undefined) {
980
- return renderToolStreamingExecution(
981
- { toolCallId: seg.id, toolName: seg.name, args: seg.args, elapsedMs, phase },
982
- width,
983
- seg.partialResult,
984
- );
985
- }
986
- const call = { toolCallId: seg.id, toolName: seg.name, args: seg.args, elapsedMs, phase };
987
- return liveToolOutput ? renderToolCallHeader(call, width) : renderToolRunningStatus(call, width);
988
- }
989
- return renderToolExecution(
990
- {
991
- toolCallId: seg.id,
992
- toolName: seg.name,
993
- args: seg.args,
994
- result: seg.result,
995
- isError: seg.isError,
996
- durationMs: seg.durationMs,
997
- resultSummary: seg.resultSummary,
998
- outcome: seg.settlement,
999
- blockReason: seg.blockReason,
1000
- evictedReason: seg.evictedReason,
1001
- },
1002
- width,
1003
- { unbounded: unboundedToolBodies, diffStyle: seg.replayed === true ? "plain" : "color" },
1004
- );
1005
- }
1006
-
1007
- /**
1008
- * Whether a worker block draws its one-line card this frame: the operator's
1009
- * override when set, else the policy's worker rule, which under the balanced
1010
- * level is the origin default (a run the model asked for folds).
1011
- */
1012
- function workerEntryFolded(entry: WorkerTranscriptEntry, detail: TranscriptDetailPolicy): boolean {
1013
- return resolveFold(entry.fold, policyWorkerFold(detail, workerAskedByModel(entry.state))) === "folded";
763
+ const call = {
764
+ toolCallId: seg.id,
765
+ toolName: seg.name,
766
+ args: seg.args,
767
+ elapsedMs: seg.startedAtMs === undefined ? undefined : Math.max(0, rawDurationMs(seg.startedAtMs, nowMs)),
768
+ phase: seg.executionStarted ? ("running" as const) : seg.argsComplete ? ("ready" as const) : ("forming" as const),
769
+ };
770
+ if (!seg.finished && seg.awaitingApproval) return renderToolAwaitingApproval(call, width, seg.approvalView);
771
+ // Argument fragments are not separate actions. Reveal the row once the call is formed.
772
+ if (!seg.finished && !seg.argsComplete && !seg.executionStarted) return [];
773
+ const finished = {
774
+ ...call,
775
+ result: seg.result,
776
+ isError: seg.isError,
777
+ durationMs: seg.durationMs,
778
+ resultSummary: seg.resultSummary,
779
+ outcome: seg.settlement,
780
+ blockReason: seg.blockReason,
781
+ evictedReason: seg.evictedReason,
782
+ };
783
+ const options = { unbounded, diffStyle: seg.replayed ? ("plain" as const) : ("color" as const) };
784
+ if (unbounded && seg.finished) return renderToolExecution(finished, width, options);
785
+ return renderToolPreview(seg.finished ? finished : call, width, detail, {
786
+ ...options,
787
+ terminalRows,
788
+ partialResult: seg.partialResult,
789
+ });
1014
790
  }
1015
791
 
1016
- /** Effective state of one thinking stretch: the operator's override, else the policy. */
1017
- function thinkingExpanded(segment: ThinkingSegment, detail: TranscriptDetailPolicy): boolean {
1018
- return resolveFold(segment.fold, policyThinkingFold(detail)) === "expanded";
792
+ function observation(seg: AssistantSegment | undefined): string | null {
793
+ if (seg?.kind !== "tool" || !seg.finished) return null;
794
+ return canGroupObservation({
795
+ toolCallId: seg.id,
796
+ toolName: seg.name,
797
+ result: seg.result,
798
+ isError: seg.isError,
799
+ outcome: seg.settlement,
800
+ evictedReason: seg.evictedReason,
801
+ resultSummary: seg.resultSummary,
802
+ })
803
+ ? seg.name
804
+ : null;
1019
805
  }
1020
806
 
1021
807
  function renderEntryLines(
1022
808
  entry: TranscriptEntry,
1023
809
  width: number,
1024
- expandKey: string | undefined,
1025
- latestHintToolId: string | null,
1026
- latestFoldedWorkerId: string | null,
1027
810
  nowMs: number,
1028
811
  unboundedToolBodies: boolean,
1029
812
  detail: TranscriptDetailPolicy,
1030
- liveToolOutput: boolean,
1031
- liveReasoning: ReasoningUsageView,
813
+ terminalRows: number,
1032
814
  ): string[] {
1033
815
  if (entry.role === "replayBlock") {
1034
816
  return entry.renderBlock(width, detail);
@@ -1048,11 +830,7 @@ function renderEntryLines(
1048
830
  return wrapTextWithAnsi(formatRetryStatus(entry.status), width);
1049
831
  }
1050
832
  if (entry.role === "worker") {
1051
- return renderWorkerEntryLines(entry.state, width, {
1052
- folded: workerEntryFolded(entry, detail),
1053
- ...(expandKey !== undefined && entry.state.assignmentId === latestFoldedWorkerId ? { expandKey } : {}),
1054
- unbounded: unboundedToolBodies,
1055
- });
833
+ return renderWorkerEntryLines(entry.state, width, { detail, terminalRows, unbounded: unboundedToolBodies });
1056
834
  }
1057
835
  // A settled assistant entry that rendered nothing at all contributes nothing.
1058
836
  // A mid-turn notice splits the transcript, so the events after it open a
@@ -1062,59 +840,36 @@ function renderEntryLines(
1062
840
  return [];
1063
841
  }
1064
842
  const lines: string[] = [];
1065
- // Reasoning renders in stream order, between the text and tool segments it
1066
- // came between. A closed stretch is a folded marker (or a head-anchored rail
1067
- // when expanded); the stretch still open while the turn is pending is the
1068
- // live indicator, with the tally's count and its own elapsed. The turn's
1069
- // settled count chip rides on the last marker once the turn has settled.
843
+ // Reasoning stays between the prose and actions that surround it.
1070
844
  const chipIndex = entry.pending ? -1 : lastThinkingIndex(entry);
1071
845
  const clioPrefix = entry.isError ? CLIO_PREFIX_ERROR : CLIO_PREFIX;
1072
846
  const proseWidth = Math.max(1, width - PROSE_GUTTER_WIDTH);
1073
847
  let labeled = false;
1074
- let liveIndicatorShown = false;
1075
- let latestThinkingExpanded = policyThinkingFold(detail) === "expanded";
1076
848
  for (let segIndex = 0; segIndex < entry.segments.length; segIndex += 1) {
1077
849
  const seg = entry.segments[segIndex];
1078
850
  if (seg === undefined) continue;
1079
851
  if (seg.kind === "thinking") {
1080
852
  if (seg.text.length === 0) continue;
1081
- const thinkingExpandedNow = thinkingExpanded(seg, detail);
1082
- latestThinkingExpanded = thinkingExpandedNow;
1083
- const live = entry.pending && !seg.finalized;
1084
- if (thinkingExpandedNow) lines.push(...renderThinkingRail(seg.text, width, live, unboundedToolBodies));
1085
- if (live) {
1086
- liveIndicatorShown = true;
1087
- // The bare marker level states that the model is thinking and
1088
- // nothing else: no count, no elapsed. An operator who opened the
1089
- // stretch anyway gets the progress line under the rail.
853
+ if (detail.reasoningRows > 0 || unboundedToolBodies) {
1090
854
  lines.push(
1091
- detail.thinking === "marker" && !thinkingExpandedNow
1092
- ? dimLine(THINKING_HIDDEN_LABEL, width)
1093
- : renderLiveReasoningLine(
1094
- liveReasoning,
1095
- seg.startedAtMs === undefined ? undefined : Math.max(0, nowMs - seg.startedAtMs),
1096
- width,
1097
- ),
855
+ ...renderThinkingRail(seg.text, width, previewBudget(detail.reasoningRows, terminalRows), unboundedToolBodies),
1098
856
  );
1099
- } else if (!thinkingExpandedNow) {
857
+ } else {
1100
858
  const view = segIndex === chipIndex ? reasoningFromTurnUsage(entry.turnUsage) : UNMEASURED_REASONING;
1101
859
  lines.push(renderSettledThinkingMarker(view, width));
1102
860
  }
1103
861
  continue;
1104
862
  }
1105
863
  if (seg.kind === "tool") {
1106
- lines.push(
1107
- ...renderToolSegmentLines(
1108
- seg,
1109
- width,
1110
- expandKey,
1111
- latestHintToolId,
1112
- nowMs,
1113
- unboundedToolBodies,
1114
- detail,
1115
- liveToolOutput,
1116
- ),
1117
- );
864
+ const kind = detail.style === "compact" && !unboundedToolBodies ? observation(seg) : null;
865
+ let count = 1;
866
+ if (kind) {
867
+ while (entry.segments[segIndex + count] && observation(entry.segments[segIndex + count]) === kind) count++;
868
+ }
869
+ if (count > 1) {
870
+ lines.push(dimLine(`✓ ${kind === "read" ? `Read ${count} files` : `${count} ${kind} actions`} · /view`, width));
871
+ segIndex += count - 1;
872
+ } else lines.push(...renderToolSegmentLines(seg, width, nowMs, unboundedToolBodies, detail, terminalRows));
1118
873
  continue;
1119
874
  }
1120
875
  // Text and error segments share the reply-prefix bookkeeping: the first
@@ -1144,32 +899,7 @@ function renderEntryLines(
1144
899
  lines.push(...hangProseLines(rendered));
1145
900
  }
1146
901
  }
1147
- // A thinking stretch closes when text or a tool follows it so the historical
1148
- // marker stays in stream order. The turn-level reasoning projection is still
1149
- // live, though, and must remain beside the current tail instead of scrolling
1150
- // away with that marker. An open tail stretch already rendered this line.
1151
- if (entry.pending && hasThinking(entry) && !liveIndicatorShown) {
1152
- lines.push(
1153
- detail.thinking === "marker" && !latestThinkingExpanded
1154
- ? dimLine(THINKING_HIDDEN_LABEL, width)
1155
- : renderLiveReasoningLine(liveReasoning, undefined, width),
1156
- );
1157
- liveIndicatorShown = true;
1158
- }
1159
902
  if (entry.turnUsage && !entry.pending) lines.push(...renderTurnUsageLine(entry.turnUsage, width, detail.receipt));
1160
- // The open thinking segment's line is the one that speaks for reasoning while
1161
- // the turn runs. The generic thinking verb is suppressed while it shows so
1162
- // the entry never carries two indicators for the same thing.
1163
- const shouldRenderStatus =
1164
- entry.pending &&
1165
- entry.statusLine !== null &&
1166
- entry.statusLine !== undefined &&
1167
- !(entry.statusLine.phase === "writing" && hasStreamingText(entry)) &&
1168
- !(entry.statusLine.phase === "thinking" && liveIndicatorShown);
1169
- if (!labeled && !hasVisibleOutput(entry)) lines.push(clioPrefix.trimEnd());
1170
- if (shouldRenderStatus) {
1171
- lines.push(`${STATUS_INDENT}${styleStatusVerb(entry.statusLine?.verb ?? "", entry.statusLine?.toneHint ?? "muted")}`);
1172
- }
1173
903
  return lines;
1174
904
  }
1175
905
 
@@ -1178,18 +908,12 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1178
908
  /** Assignment to its placed block, in placement order, so a streaming delta is O(1) to route. */
1179
909
  const workerEntries = new Map<string, WorkerTranscriptEntry>();
1180
910
  let dirty = true;
911
+ let runStartedAt: number | undefined;
1181
912
  let cachedWidth: number | undefined;
1182
913
  let cachedLines: string[] = [];
1183
- let cachedExpandKey: string | undefined;
1184
914
  let cachedDetail: TranscriptDetailPolicy | undefined;
1185
- let cachedLiveToolOutput: boolean | undefined;
1186
- /**
1187
- * The verbosity the last frame rendered under, or null before any frame.
1188
- * A change between frames is the operator asking for a new baseline
1189
- * (`/output`, or Settings → Terminal), and every override goes with it.
1190
- */
1191
- let lastVerbosity: OutputVerbosity | undefined | null = null;
1192
915
  let cachedTick = 0;
916
+ let cachedTerminalRows = 0;
1193
917
  /**
1194
918
  * Did the last executed render put a counting elapsed line on screen? It is
1195
919
  * what decides whether the render key carries a time tick at all, so a
@@ -1202,7 +926,7 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1202
926
  * entries, which re-rendered everything past the cap on every dirty frame
1203
927
  * (10 ms/frame at 800 entries). The ceiling bounds worst-case memory at
1204
928
  * roughly 4096 rendered entries; past it, the excess only re-renders on
1205
- * full-rebuild events (width change, expand-all), which the frozen prefix
929
+ * full-rebuild events (width or style change), which the frozen prefix
1206
930
  * below makes rare rather than per-frame.
1207
931
  */
1208
932
  const MIN_ENTRY_RENDER_CACHE = 256;
@@ -1220,13 +944,6 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1220
944
  * the render key changes.
1221
945
  */
1222
946
  let frozen: { lines: string[]; through: number; key: string } | null = null;
1223
- let liveToolOutput = true;
1224
- /**
1225
- * The run tally's reasoning, projected. It is panel-level rather than
1226
- * per-entry because only the pending tail entry ever renders it, and that
1227
- * entry is by definition unfrozen and uncached.
1228
- */
1229
- let liveReasoning: ReasoningUsageView = UNMEASURED_REASONING;
1230
947
  const unboundedToolBodies = options.unboundedToolBodies === true;
1231
948
 
1232
949
  const markDirty = (): void => {
@@ -1250,39 +967,8 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1250
967
  if (frozen !== null && frozen.through >= transcript.length) frozen = null;
1251
968
  };
1252
969
 
1253
- const resolveExpandKey = (): string | undefined => {
1254
- const key = options.getToolExpandKey?.();
1255
- if (typeof key !== "string" || key.length === 0) return undefined;
1256
- return key;
1257
- };
1258
-
1259
970
  const now = (): number => options.now?.() ?? Date.now();
1260
-
1261
- /** The policy for a frame or a keypress, from whatever the settings say right now. */
1262
- const currentDetail = (): TranscriptDetailPolicy => transcriptDetail(options.getOutputVerbosity?.());
1263
-
1264
- /**
1265
- * Drop every operator override. Shared by the panel method and the
1266
- * verbosity-change path in render, so both leave the same state behind.
1267
- */
1268
- const dropFoldOverrides = (): void => {
1269
- for (const entry of transcript) {
1270
- if (entry.role === "replayBlock") {
1271
- entry.fold?.setFold(undefined);
1272
- continue;
1273
- }
1274
- if (entry.role === "worker") {
1275
- entry.fold = undefined;
1276
- continue;
1277
- }
1278
- if (entry.role !== "assistant") continue;
1279
- for (const seg of entry.segments) {
1280
- if (seg.kind === "tool" || seg.kind === "thinking") seg.fold = undefined;
1281
- }
1282
- }
1283
- clearRenderCaches();
1284
- markDirty();
1285
- };
971
+ const currentDetail = (): TranscriptDetailPolicy => transcriptDetail(options.getOutputStyle?.());
1286
972
 
1287
973
  /**
1288
974
  * Force an in-flight tool segment to a settled error line. A call blocked at
@@ -1460,37 +1146,6 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1460
1146
  entry.segments.push({ kind: "error", text });
1461
1147
  };
1462
1148
 
1463
- /**
1464
- * Who advertises the fold key this frame: the newest worker card whose
1465
- * effective state is folded, or the newest finished tool subline whose
1466
- * effective state is folded with no worker card behind it. Effective means
1467
- * override-or-policy, so the hint follows the block whatever the verbosity.
1468
- * One surface at most, because the key reaches the newest foldable thing of
1469
- * either kind, and a chord shown anywhere else would open something the
1470
- * operator was not looking at. An already-open newest card advertises
1471
- * nothing, since folding it again needs no invitation.
1472
- */
1473
- const expandHintOwner = (detail: TranscriptDetailPolicy): { toolId: string | null; workerId: string | null } => {
1474
- let workerMayOwn = true;
1475
- for (let entryIndex = transcript.length - 1; entryIndex >= 0; entryIndex -= 1) {
1476
- const entry = transcript[entryIndex];
1477
- if (entry?.role === "worker") {
1478
- return {
1479
- toolId: null,
1480
- workerId: workerMayOwn && workerEntryFolded(entry, detail) ? entry.state.assignmentId : null,
1481
- };
1482
- }
1483
- if (entry?.role !== "assistant") continue;
1484
- for (let segIndex = entry.segments.length - 1; segIndex >= 0; segIndex -= 1) {
1485
- const seg = entry.segments[segIndex];
1486
- if (seg?.kind !== "tool") continue;
1487
- if (seg.finished && !toolSegmentExpanded(seg, detail)) return { toolId: seg.id, workerId: null };
1488
- workerMayOwn = false;
1489
- }
1490
- }
1491
- return { toolId: null, workerId: null };
1492
- };
1493
-
1494
1149
  /**
1495
1150
  * Where a newly seen worker block belongs. An agent-origin run nests under
1496
1151
  * the assistant entry holding the tool call that spawned it, behind any
@@ -1511,12 +1166,6 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1511
1166
  return index >= transcript.length ? null : index;
1512
1167
  };
1513
1168
 
1514
- /** True when the entry owns the tool the expand hint currently points at. */
1515
- const entryContainsHint = (entry: TranscriptEntry, latestHintToolId: string | null): boolean =>
1516
- latestHintToolId !== null &&
1517
- entry.role === "assistant" &&
1518
- entry.segments.some((segment) => segment.kind === "tool" && segment.id === latestHintToolId);
1519
-
1520
1169
  /** True when the entry renders at least one counting elapsed line this frame. */
1521
1170
  const entryHasRunningTool = (entry: TranscriptEntry): boolean =>
1522
1171
  (entry.role === "assistant" &&
@@ -1546,13 +1195,8 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1546
1195
 
1547
1196
  const render = (width: number): string[] => {
1548
1197
  const startedAt = performance.now();
1549
- const expandKey = resolveExpandKey();
1550
- const verbosity = options.getOutputVerbosity?.();
1551
- // A new verbosity is a new baseline: the operator's per-block overrides
1552
- // were answers to the old one, so they go before the frame is built.
1553
- if (lastVerbosity !== null && verbosity !== lastVerbosity) dropFoldOverrides();
1554
- lastVerbosity = verbosity;
1555
- const detail = transcriptDetail(verbosity);
1198
+ const detail = currentDetail();
1199
+ const terminalRows = options.getTerminalRows?.() ?? 40;
1556
1200
  const nowMs = now();
1557
1201
  // `dirty` is set on mutation and never on a tick, so without time in the
1558
1202
  // key a running tool's elapsed counter advanced only when something
@@ -1562,36 +1206,23 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1562
1206
  // pinned to 0 whenever nothing is counting so a settled transcript
1563
1207
  // re-renders no more often than it did before.
1564
1208
  const tick = renderedRunningTool ? Math.floor(nowMs / 100) : 0;
1565
- // The hit guard runs before any transcript scan. expandHintOwner
1566
- // walks the whole transcript when the newest tool is expanded or absent, and
1567
- // it is not part of the panel-level key, so computing it above the guard cost
1568
- // a full scan per frame for a value the early return discards.
1569
1209
  if (
1570
1210
  !dirty &&
1571
1211
  cachedWidth === width &&
1572
- cachedExpandKey === expandKey &&
1212
+ cachedTerminalRows === terminalRows &&
1573
1213
  cachedDetail === detail &&
1574
- cachedLiveToolOutput === liveToolOutput &&
1575
1214
  cachedTick === tick
1576
1215
  ) {
1577
1216
  options.onRenderMetrics?.({ durationMs: performance.now() - startedAt, cacheHit: true, entriesRendered: 0 });
1578
1217
  return cachedLines;
1579
1218
  }
1580
- const { toolId: latestHintToolId, workerId: latestFoldedWorkerId } = expandHintOwner(detail);
1581
- // The hint id is deliberately NOT part of the shared key: it changes on
1582
- // every finished collapsed tool, and keying every entry on it re-rendered
1583
- // the entire transcript per tool completion. Only the entry that contains
1584
- // the hint tool renders differently, so only that entry's key carries it.
1585
- // The tick stays out of the entry key: a settled entry renders the same
1586
- // bytes at every tick, and keying it on time would drop the entry cache
1587
- // and the frozen prefix ten times a second. Only the panel-level guard
1588
- // above is time-keyed, so a tick re-renders the live tail and nothing else.
1589
- const baseKey = `${width}|${expandKey ?? ""}|${detail.toolBody}:${detail.runningTool}:${detail.thinking}:${detail.worker}:${detail.receipt}:${detail.errors}|${liveToolOutput}`;
1219
+ // Stable entries cache by width, height budget, and preset.
1220
+ const baseKey = `${width}|${terminalRows}|${detail.style}`;
1590
1221
  const capacity = entryCacheCapacity();
1591
1222
  if (frozen !== null && frozen.key !== baseKey) frozen = null;
1592
1223
  const out: string[] = frozen === null ? [] : frozen.lines.slice();
1593
1224
  const startIndex = frozen === null ? 0 : frozen.through;
1594
- // The freeze extends over the contiguous run of stable, hint-free leading
1225
+ // The freeze extends over the contiguous run of stable leading
1595
1226
  // entries; it is captured after the loop from what this frame rendered.
1596
1227
  let freezeThrough = startIndex;
1597
1228
  let freezeLineCount = out.length;
@@ -1609,16 +1240,9 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1609
1240
  // five scouts costs five rows, which is what makes the folded default
1610
1241
  // worth having. Anything else keeps the blank line between entries.
1611
1242
  const previous = i > 0 ? transcript[i - 1] : undefined;
1612
- const stacksOnPrevious =
1613
- entry.role === "worker" &&
1614
- previous?.role === "worker" &&
1615
- workerEntryFolded(entry, detail) &&
1616
- workerEntryFolded(previous, detail);
1243
+ const stacksOnPrevious = entry.role === "worker" && previous?.role === "worker" && detail.style === "compact";
1617
1244
  if (i > 0 && !stacksOnPrevious) out.push("");
1618
- const containsHint =
1619
- entryContainsHint(entry, latestHintToolId) ||
1620
- (entry.role === "worker" && entry.state.assignmentId === latestFoldedWorkerId);
1621
- const entryKey = containsHint ? `${baseKey}|hint:${latestHintToolId}|${latestFoldedWorkerId}` : baseKey;
1245
+ const entryKey = baseKey;
1622
1246
  const cached = entryRenderCache.get(entry);
1623
1247
  const cacheable = i >= transcript.length - capacity && entry.role !== "replayBlock" && entryIsStable(entry);
1624
1248
  if (cacheable && cached?.key === entryKey) {
@@ -1627,18 +1251,7 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1627
1251
  for (const line of cached.lines) out.push(line);
1628
1252
  } else {
1629
1253
  entriesRendered += 1;
1630
- const renderedEntry = renderEntryLines(
1631
- entry,
1632
- width,
1633
- expandKey,
1634
- latestHintToolId,
1635
- latestFoldedWorkerId,
1636
- nowMs,
1637
- unboundedToolBodies,
1638
- detail,
1639
- liveToolOutput,
1640
- liveReasoning,
1641
- );
1254
+ const renderedEntry = renderEntryLines(entry, width, nowMs, unboundedToolBodies, detail, terminalRows);
1642
1255
  for (const line of renderedEntry) out.push(line);
1643
1256
  if (cacheable) {
1644
1257
  entryRenderCache.set(entry, { key: entryKey, lines: renderedEntry });
@@ -1649,7 +1262,7 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1649
1262
  }
1650
1263
  }
1651
1264
  }
1652
- if (freezeOpen && i === freezeThrough && entryIsStable(entry) && !containsHint) {
1265
+ if (freezeOpen && i === freezeThrough && entryIsStable(entry)) {
1653
1266
  freezeThrough = i + 1;
1654
1267
  freezeLineCount = out.length;
1655
1268
  } else {
@@ -1659,9 +1272,8 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1659
1272
  frozen = freezeThrough > 0 ? { lines: out.slice(0, freezeLineCount), through: freezeThrough, key: baseKey } : null;
1660
1273
  cachedLines = out;
1661
1274
  cachedWidth = width;
1662
- cachedExpandKey = expandKey;
1663
1275
  cachedDetail = detail;
1664
- cachedLiveToolOutput = liveToolOutput;
1276
+ cachedTerminalRows = terminalRows;
1665
1277
  cachedTick = tick;
1666
1278
  renderedRunningTool = sawRunningTool;
1667
1279
  dirty = false;
@@ -1674,8 +1286,8 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1674
1286
  transcript.push({ role: "user", text, ...(status ? { status } : {}) });
1675
1287
  markDirty();
1676
1288
  },
1677
- appendReplayBlock(renderBlock: ReplayBlockRenderer, isLive?: () => boolean, fold?: ReplayBlockFoldControl): void {
1678
- transcript.push({ role: "replayBlock", renderBlock, isLive, fold });
1289
+ appendReplayBlock(renderBlock: ReplayBlockRenderer, isLive?: () => boolean): void {
1290
+ transcript.push({ role: "replayBlock", renderBlock, isLive });
1679
1291
  markDirty();
1680
1292
  },
1681
1293
  applyWorkerState(state: WorkerEntryState): void {
@@ -1703,135 +1315,58 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1703
1315
  workerStates(): ReadonlyArray<WorkerEntryState> {
1704
1316
  return [...workerEntries.values()].map((entry) => entry.state);
1705
1317
  },
1706
- toggleLastToolExpanded(): boolean {
1707
- // The key owns the newest foldable thing, whichever kind it is. A worker
1708
- // block the operator just watched land is what they mean by "expand
1709
- // that", not the tool call two screens up that spawned it. Every flip
1710
- // is an override away from the block's effective state, so the same
1711
- // key opens a folded block under minimal and folds an open one under
1712
- // verbose.
1713
- const detail = currentDetail();
1714
- for (let entryIndex = transcript.length - 1; entryIndex >= 0; entryIndex -= 1) {
1715
- const entry = transcript[entryIndex];
1716
- // A caller-rendered block that owns fold state (the operator's own
1717
- // `!` bash row) is foldable too, and it is usually the newest thing
1718
- // on screen when the key is pressed.
1719
- if (entry?.role === "replayBlock" && entry.fold !== undefined) {
1720
- entry.fold.setFold(toggledFold(resolveFold(entry.fold.fold(), entry.fold.policyFold(detail))));
1721
- clearRenderCaches();
1722
- markDirty();
1723
- return true;
1724
- }
1725
- if (entry?.role === "worker") {
1726
- entry.fold = workerEntryFolded(entry, detail) ? "expanded" : "folded";
1727
- clearRenderCaches();
1728
- markDirty();
1729
- return true;
1730
- }
1731
- if (entry?.role !== "assistant") continue;
1732
- for (let segIndex = entry.segments.length - 1; segIndex >= 0; segIndex -= 1) {
1733
- const seg = entry.segments[segIndex];
1734
- if (seg?.kind !== "tool") continue;
1735
- seg.fold = toolSegmentExpanded(seg, detail) ? "folded" : "expanded";
1736
- clearRenderCaches();
1737
- markDirty();
1738
- return true;
1739
- }
1740
- }
1741
- return false;
1742
- },
1743
- toggleAllToolsExpanded(): boolean {
1744
- const detail = currentDetail();
1745
- const tools: ToolSegment[] = [];
1746
- const workers: WorkerTranscriptEntry[] = [];
1747
- const blocks: ReplayBlockFoldControl[] = [];
1748
- for (const entry of transcript) {
1749
- if (entry.role === "replayBlock") {
1750
- if (entry.fold !== undefined) blocks.push(entry.fold);
1751
- continue;
1752
- }
1753
- if (entry.role === "worker") {
1754
- workers.push(entry);
1755
- continue;
1756
- }
1757
- if (entry.role !== "assistant") continue;
1758
- for (const seg of entry.segments) {
1759
- if (seg.kind === "tool") tools.push(seg);
1760
- }
1761
- }
1762
- if (tools.length === 0 && workers.length === 0 && blocks.length === 0) return false;
1763
- // One folded block anywhere means "open everything"; otherwise fold
1764
- // everything. Either way every block gets an explicit override.
1765
- const expand =
1766
- tools.some((seg) => !toolSegmentExpanded(seg, detail)) ||
1767
- workers.some((entry) => workerEntryFolded(entry, detail)) ||
1768
- blocks.some((fold) => resolveFold(fold.fold(), fold.policyFold(detail)) === "folded");
1769
- const next: Fold = expand ? "expanded" : "folded";
1770
- for (const seg of tools) seg.fold = next;
1771
- for (const entry of workers) entry.fold = next;
1772
- for (const fold of blocks) fold.setFold(next);
1773
- clearRenderCaches();
1774
- markDirty();
1775
- return true;
1776
- },
1777
- clearFoldOverrides(): void {
1778
- dropFoldOverrides();
1779
- },
1780
- toggleLastThinking(): boolean {
1781
- const detail = currentDetail();
1782
- for (let entryIndex = transcript.length - 1; entryIndex >= 0; entryIndex -= 1) {
1783
- const entry = transcript[entryIndex];
1784
- if (entry?.role !== "assistant") continue;
1785
- for (let segIndex = entry.segments.length - 1; segIndex >= 0; segIndex -= 1) {
1786
- const segment = entry.segments[segIndex];
1787
- if (segment?.kind !== "thinking" || segment.text.length === 0) continue;
1788
- segment.fold = thinkingExpanded(segment, detail) ? "folded" : "expanded";
1789
- clearRenderCaches();
1790
- markDirty();
1791
- return true;
1792
- }
1793
- }
1794
- return false;
1795
- },
1796
- toggleAllThinking(): boolean {
1797
- const detail = currentDetail();
1798
- const segments: ThinkingSegment[] = [];
1318
+ inspectionArtifacts(): ViewArtifact[] {
1319
+ const artifacts: ViewArtifact[] = [];
1320
+ const add = (title: string, render: () => string[]) => {
1321
+ const index = artifacts.length;
1322
+ artifacts.push({
1323
+ id: `transcript:${index + 1}`,
1324
+ category: "transcript",
1325
+ title,
1326
+ timestamp: now() + index,
1327
+ searchText: [title],
1328
+ load: async () => ({ format: "text", lines: render().map(redactSecretString) }),
1329
+ });
1330
+ };
1799
1331
  for (const entry of transcript) {
1800
- if (entry.role !== "assistant") continue;
1801
- for (const segment of entry.segments) {
1802
- if (segment.kind === "thinking" && segment.text.length > 0) segments.push(segment);
1332
+ if (entry.role === "assistant") {
1333
+ for (const seg of entry.segments) {
1334
+ if (seg.kind === "thinking" && seg.text) add("Thinking · supplied reasoning", () => seg.text.split("\n"));
1335
+ if (seg.kind === "tool")
1336
+ add(`${seg.name} · ${seg.id}`, () =>
1337
+ renderToolExecution(
1338
+ {
1339
+ toolCallId: seg.id,
1340
+ toolName: seg.name,
1341
+ args: seg.args,
1342
+ result: seg.result ?? seg.partialResult,
1343
+ isError: seg.isError,
1344
+ outcome: seg.settlement,
1345
+ blockReason: seg.blockReason,
1346
+ resultSummary: seg.resultSummary,
1347
+ },
1348
+ 120,
1349
+ { unbounded: true, diffStyle: "plain" },
1350
+ ),
1351
+ );
1352
+ }
1353
+ } else if (entry.role === "worker") {
1354
+ add(`${entry.state.agentId} · worker ${entry.state.runId}`, () =>
1355
+ renderWorkerEntryLines(entry.state, 120, { unbounded: true }),
1356
+ );
1357
+ } else if (entry.role === "replayBlock") {
1358
+ add("Session action", () => entry.renderBlock(120, transcriptDetail("detailed"), true));
1803
1359
  }
1804
1360
  }
1805
- if (segments.length === 0) return false;
1806
- const expand = segments.some((segment) => !thinkingExpanded(segment, detail));
1807
- for (const segment of segments) segment.fold = expand ? "expanded" : "folded";
1808
- clearRenderCaches();
1809
- markDirty();
1810
- return true;
1361
+ return artifacts;
1811
1362
  },
1812
1363
  isThinkingExpanded(): boolean {
1813
- // The live stretch lives on the newest assistant entry; its own override,
1814
- // if any, is the one that applies. Before a stretch arrives, the policy answers.
1815
- const detail = currentDetail();
1816
- const index = lastAssistantIndex(transcript);
1817
- const entry = index === null ? undefined : transcript[index];
1818
- if (entry?.role === "assistant") {
1819
- for (let segIndex = entry.segments.length - 1; segIndex >= 0; segIndex -= 1) {
1820
- const segment = entry.segments[segIndex];
1821
- if (segment?.kind === "thinking" && segment.text.length > 0) return thinkingExpanded(segment, detail);
1822
- }
1823
- }
1824
- return policyThinkingFold(detail) === "expanded";
1825
- },
1826
- toggleLiveToolOutput(): boolean {
1827
- liveToolOutput = !liveToolOutput;
1828
- markDirty();
1829
- return liveToolOutput;
1364
+ return currentDetail().reasoningRows > 0;
1830
1365
  },
1831
1366
  reset(): void {
1832
1367
  transcript.length = 0;
1368
+ runStartedAt = undefined;
1833
1369
  workerEntries.clear();
1834
- liveReasoning = UNMEASURED_REASONING;
1835
1370
  clearRenderCaches();
1836
1371
  markDirty();
1837
1372
  },
@@ -1843,6 +1378,10 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
1843
1378
  markDirty();
1844
1379
  },
1845
1380
  applyEvent(event: ChatLoopEvent): void {
1381
+ if (event.type === "agent_start") {
1382
+ runStartedAt = now();
1383
+ return;
1384
+ }
1846
1385
  if (event.type === "agent_status") {
1847
1386
  return;
1848
1387
  }
@@ -2149,7 +1688,21 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
2149
1688
  const target = index === null ? undefined : transcript[index];
2150
1689
  if (target?.role === "assistant") {
2151
1690
  invalidateEntryCache(target);
2152
- target.turnUsage = runUsage;
1691
+ const stop = event.messages.filter((message) => message.role === "assistant").at(-1) as
1692
+ | { stopReason?: string }
1693
+ | undefined;
1694
+ target.turnUsage = {
1695
+ ...runUsage,
1696
+ ...(runStartedAt === undefined ? {} : { elapsedMs: Math.max(0, now() - runStartedAt) }),
1697
+ outcome:
1698
+ stop?.stopReason === "error"
1699
+ ? "Failed"
1700
+ : stop?.stopReason === "aborted"
1701
+ ? "Cancelled"
1702
+ : stop?.stopReason === "length"
1703
+ ? "Output limit"
1704
+ : "Done",
1705
+ };
2153
1706
  }
2154
1707
  for (let after = (index ?? -1) + 1; after < transcript.length; after += 1) {
2155
1708
  const later = transcript[after];
@@ -2183,39 +1736,12 @@ export function createChatPanel(options: ChatPanelOptions = {}): ChatPanel {
2183
1736
  invalidateEntryCache(entry);
2184
1737
  closeOpenThinking(entry);
2185
1738
  entry.pending = false;
2186
- entry.statusLine = null;
2187
1739
  entry.messageStartSegmentIndex = undefined;
2188
1740
  }
2189
1741
  }
2190
1742
  markDirty();
2191
1743
  }
2192
1744
  },
2193
- setLiveReasoning(view: ReasoningUsageView | null): void {
2194
- const next = view ?? UNMEASURED_REASONING;
2195
- if (next.tokens === liveReasoning.tokens && next.provenance === liveReasoning.provenance) return;
2196
- liveReasoning = next;
2197
- // The line lives on the pending tail entry, which the freeze may already
2198
- // cover if nothing has mutated since the last settle.
2199
- unfreezeTail();
2200
- const last = transcript[transcript.length - 1];
2201
- if (last !== undefined) invalidateEntryCache(last);
2202
- markDirty();
2203
- },
2204
- setStatusLine(line): void {
2205
- if (line) {
2206
- const assistant = ensureAssistant();
2207
- assistant.pending = true;
2208
- assistant.statusLine = line;
2209
- markDirty();
2210
- return;
2211
- }
2212
- const last = transcript[transcript.length - 1];
2213
- if (last && last.role === "assistant") {
2214
- unfreezeTail();
2215
- last.statusLine = null;
2216
- markDirty();
2217
- }
2218
- },
2219
1745
  render,
2220
1746
  invalidate(): void {
2221
1747
  markDirty();