@iowarp/clio-coder 0.5.0 → 0.5.2

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 (312) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/CHANGELOG.md +35 -0
  3. package/CONTRIBUTING.md +9 -0
  4. package/README.md +12 -9
  5. package/ROADMAP.md +30 -5
  6. package/dist/{acp-KS7ARGV6.js → acp-F6PGOFZ4.js} +2 -2
  7. package/dist/{agents-YOGJ2YEJ.js → agents-ZHP4R6EX.js} +22 -21
  8. package/dist/assets/codewiki.json +1 -1
  9. package/dist/{auth-CEDCO6O6.js → auth-N75U2H3Z.js} +4 -4
  10. package/dist/{chunk-D3KWFOUT.js → chunk-23UIXH6Z.js} +3 -3
  11. package/dist/{chunk-ZR5YA6ZF.js → chunk-36XVK5P5.js} +5 -5
  12. package/dist/{chunk-AVSR2HKI.js → chunk-3DZ4LPLS.js} +6 -6
  13. package/dist/{chunk-LYJJ6546.js → chunk-3GSPCWFX.js} +12 -12
  14. package/dist/{chunk-CRHPCPZP.js → chunk-4KC4CHKJ.js} +2 -2
  15. package/dist/{chunk-4VDHLCF5.js → chunk-4WJBWKMI.js} +2 -2
  16. package/dist/{chunk-KBUTSJSR.js → chunk-4XBYHXGK.js} +2 -2
  17. package/dist/{chunk-6W75D37F.js → chunk-53P775GE.js} +6 -3
  18. package/dist/{chunk-XZWSP67B.js → chunk-5APYAXCB.js} +2 -2
  19. package/dist/{chunk-D65E25JG.js → chunk-67HPYNTJ.js} +2 -2
  20. package/dist/{chunk-5JQR5B4D.js → chunk-6KV4KCQZ.js} +4 -4
  21. package/dist/{chunk-V4XJWDHR.js → chunk-6UKFRTXI.js} +2 -1
  22. package/dist/{chunk-NDPF2N2L.js → chunk-7DW7CJW2.js} +2 -2
  23. package/dist/{chunk-5KQEUIWM.js → chunk-7LUEQODV.js} +4 -4
  24. package/dist/{chunk-LNOXCGN2.js → chunk-7UFDIOBT.js} +3 -3
  25. package/dist/{chunk-DXSETN72.js → chunk-7WUGNN43.js} +2 -2
  26. package/dist/{chunk-V35KZDUZ.js → chunk-AA3GRZWS.js} +2 -2
  27. package/dist/{chunk-X4CEFOSG.js → chunk-ADO3OQIC.js} +4 -4
  28. package/dist/{chunk-UX2L5CUD.js → chunk-AI36V3ON.js} +3 -3
  29. package/dist/{chunk-B7GY6QER.js → chunk-B26OUMJW.js} +2 -2
  30. package/dist/{chunk-LAR7DX5E.js → chunk-B5TASTCK.js} +153 -62
  31. package/dist/{chunk-TGWZXJ7L.js → chunk-BEMQK5Q7.js} +92 -84
  32. package/dist/{chunk-RNDCROSG.js → chunk-CNXU7TR2.js} +3 -3
  33. package/dist/{chunk-3JLTRPN7.js → chunk-CVMUXT64.js} +35 -66
  34. package/dist/{chunk-733ZBT5P.js → chunk-E74MJF6G.js} +4 -4
  35. package/dist/{chunk-T2MDZES3.js → chunk-EBMTBE76.js} +3 -3
  36. package/dist/{chunk-OZ76TLGU.js → chunk-EEITU7S6.js} +2 -2
  37. package/dist/{chunk-GRHOXDKB.js → chunk-ELDJWJKM.js} +97 -14
  38. package/dist/{chunk-GCQFN7YI.js → chunk-EMYEQLWO.js} +2 -2
  39. package/dist/{chunk-LTWH2ACR.js → chunk-EQPFEDFV.js} +222 -127
  40. package/dist/{chunk-TDGYTWM6.js → chunk-F4I3H56H.js} +2 -2
  41. package/dist/chunk-F6KCJO3U.js +523 -0
  42. package/dist/{chunk-FGSQHUCK.js → chunk-F6VKWTTY.js} +5 -5
  43. package/dist/{chunk-UJD5MPP6.js → chunk-FLNXQQ5B.js} +2 -2
  44. package/dist/{chunk-EX57VVWW.js → chunk-FOSRJ2VZ.js} +4 -4
  45. package/dist/{chunk-ZARZ4POR.js → chunk-G5ZDQQE3.js} +3 -3
  46. package/dist/{chunk-HQVN7G4F.js → chunk-G62JBPXN.js} +7 -7
  47. package/dist/{chunk-ZJPORZTC.js → chunk-GIAI7A2K.js} +2 -2
  48. package/dist/chunk-GPTR6OTU.js +2551 -0
  49. package/dist/{chunk-EZD2BXGP.js → chunk-GUFQPTQK.js} +2 -2
  50. package/dist/{chunk-OPD7GL6F.js → chunk-H6FQVNZC.js} +8 -7
  51. package/dist/chunk-H7REJ7L5.js +80 -0
  52. package/dist/{chunk-EYTLV3W3.js → chunk-HTWTORF7.js} +2 -2
  53. package/dist/{chunk-DF6PI6GN.js → chunk-I6UKQPGS.js} +3 -3
  54. package/dist/{chunk-ISHUS7HC.js → chunk-JRW236JT.js} +3 -3
  55. package/dist/{chunk-DE2LU267.js → chunk-KKAJGT3B.js} +3 -2
  56. package/dist/{chunk-OV62D6KO.js → chunk-KQMYL5GR.js} +3 -3
  57. package/dist/{chunk-QDUUOZ3S.js → chunk-KWMFC7FN.js} +3 -3
  58. package/dist/{chunk-R7FGRZNF.js → chunk-LXYHCCIZ.js} +9 -9
  59. package/dist/{chunk-NFSK2VNU.js → chunk-LZMTVVN4.js} +2 -2
  60. package/dist/{chunk-62UK7AGW.js → chunk-M5PMRZSR.js} +397 -44
  61. package/dist/chunk-MMZMM6SW.js +131 -0
  62. package/dist/{chunk-FFYTC2KQ.js → chunk-MNABNEBX.js} +2 -2
  63. package/dist/{chunk-XMDBTYCH.js → chunk-N3CWOGVF.js} +3 -3
  64. package/dist/{chunk-VYRQHORQ.js → chunk-NMQSO6Z6.js} +4 -3
  65. package/dist/{chunk-R2UUNSNQ.js → chunk-O3ZAF7UY.js} +18 -5
  66. package/dist/{chunk-4SYHGVDE.js → chunk-OQNCLZI4.js} +4 -4
  67. package/dist/{chunk-EG4ARXPC.js → chunk-OX6QTA4N.js} +3 -3
  68. package/dist/{chunk-P4GNZPXO.js → chunk-OYUS3UZP.js} +2 -2
  69. package/dist/{chunk-OQ2EOAHA.js → chunk-PF36KOGI.js} +291 -112
  70. package/dist/{chunk-55RMDDFM.js → chunk-PGQG7ZMW.js} +2 -2
  71. package/dist/{chunk-CBAQPTDU.js → chunk-PIU6BXXW.js} +2 -2
  72. package/dist/{chunk-EKV4UBCM.js → chunk-PZBQUJ2F.js} +2 -2
  73. package/dist/{chunk-7RET77VB.js → chunk-Q2HKY32Y.js} +2 -2
  74. package/dist/{chunk-U6YTUVOX.js → chunk-Q4RMNWMZ.js} +4 -4
  75. package/dist/chunk-Q6FRR3AQ.js +48 -0
  76. package/dist/{chunk-6RJMYYIE.js → chunk-QJSIAJBY.js} +3 -3
  77. package/dist/{chunk-DOUISWEE.js → chunk-QOJ4ERMU.js} +5 -5
  78. package/dist/{chunk-IFPDAD7R.js → chunk-RWSRKXLT.js} +3 -3
  79. package/dist/{chunk-T33IYTZM.js → chunk-SAASVMIY.js} +3 -3
  80. package/dist/{chunk-2DTIWSAW.js → chunk-SEQDH6JC.js} +3 -3
  81. package/dist/{chunk-BRCOSE7O.js → chunk-SZUO5BSE.js} +2 -2
  82. package/dist/{chunk-HOEDA42N.js → chunk-T7HZ7KJF.js} +2 -2
  83. package/dist/{chunk-OJXOA6YU.js → chunk-TL6LNERH.js} +2 -2
  84. package/dist/{chunk-UCINIRHW.js → chunk-TPCZXWLT.js} +3 -3
  85. package/dist/{chunk-ZUKUCZYZ.js → chunk-TQ2KTH4A.js} +2 -2
  86. package/dist/{chunk-4WOOTKFD.js → chunk-U3FETNQB.js} +341 -20
  87. package/dist/{chunk-5B3RTYAK.js → chunk-UGDSB4AN.js} +2 -2
  88. package/dist/{chunk-VKHLUZNO.js → chunk-UZ7YBL43.js} +3 -3
  89. package/dist/{chunk-QNUQ7K7D.js → chunk-VKMSEO7Y.js} +4 -4
  90. package/dist/{chunk-IN7DGBVS.js → chunk-VLX5VZ35.js} +5 -5
  91. package/dist/{chunk-SJGS3GDI.js → chunk-WAGBMMNX.js} +5 -5
  92. package/dist/{chunk-BFOSV5EZ.js → chunk-WX2YCH7F.js} +2 -2
  93. package/dist/{chunk-3LT34CAM.js → chunk-XBIGUILU.js} +32 -17
  94. package/dist/{chunk-XDHUDE5K.js → chunk-XKYBFRWR.js} +5 -5
  95. package/dist/{chunk-U5QU5ZOD.js → chunk-XYPWFSU5.js} +3 -3
  96. package/dist/{chunk-YBUECLAF.js → chunk-YEJQDODA.js} +2 -2
  97. package/dist/{chunk-YQ6XEFVK.js → chunk-Z74OGONW.js} +39 -7
  98. package/dist/{chunk-I4ELN5BX.js → chunk-ZTNOEJRI.js} +2 -2
  99. package/dist/cli/index.js +26 -26
  100. package/dist/{clio-K2PBVCEM.js → clio-4IKFE25F.js} +2 -2
  101. package/dist/{clio-context-tools-GR4OHFSO.js → clio-context-tools-OHJXYX4R.js} +25 -23
  102. package/dist/{code-nav-4X3OI6DK.js → code-nav-XPM3MY5I.js} +7 -7
  103. package/dist/{config-Q5J7UBAB.js → config-343YS25T.js} +43 -41
  104. package/dist/{config-graph-P3555574.js → config-graph-WI263KDX.js} +43 -41
  105. package/dist/{configure-KIJ32ZO6.js → configure-GZDG4HER.js} +21 -21
  106. package/dist/{context-LFBQOSXT.js → context-4OFO7N7Y.js} +12 -12
  107. package/dist/{context-WBH3KSVR.js → context-JLK2RLOG.js} +42 -40
  108. package/dist/{context-NNLBES2Y.js → context-O6DIWCF6.js} +23 -21
  109. package/dist/{context-clear-YOHMQ6SQ.js → context-clear-MR7KOGQI.js} +42 -40
  110. package/dist/{context-working-set-MRAEVDNV.js → context-working-set-YZAD3KWO.js} +14 -13
  111. package/dist/{data-tool-JJPBGMQJ.js → data-tool-EKD7DFHG.js} +7 -7
  112. package/dist/{detail-RHJOTGN4.js → detail-YZPHFEVJ.js} +43 -41
  113. package/dist/{dispatch-runner-NC7PYNIO.js → dispatch-runner-VKVXVTWJ.js} +44 -42
  114. package/dist/{doctor-S5KZ2GTN.js → doctor-53AVQXAO.js} +17 -16
  115. package/dist/{doctor-deep-2EJQSHIZ.js → doctor-deep-BLRB2NCG.js} +5 -5
  116. package/dist/{eval-3D6M36N7.js → eval-P3BQSLQE.js} +21 -20
  117. package/dist/{evidence-XNVPPLVK.js → evidence-6LLI6SZH.js} +44 -42
  118. package/dist/{evidence-6IYN52ET.js → evidence-HTN3JLUO.js} +42 -40
  119. package/dist/{evidence-JVECPI3F.js → evidence-NJRE3R5H.js} +42 -40
  120. package/dist/{evolve-TLPUDJDM.js → evolve-WWZHJVJP.js} +42 -40
  121. package/dist/{fleet-WKVBF5SZ.js → fleet-GGZ4BVJE.js} +64 -62
  122. package/dist/{fleet-6RJWIOEO.js → fleet-QG3IIWWM.js} +44 -42
  123. package/dist/{fleet-commands-L6WFT7KZ.js → fleet-commands-J2ZIKISQ.js} +7 -7
  124. package/dist/{fleet-decisions-BTQMWU5A.js → fleet-decisions-Y2Q7375Q.js} +8 -8
  125. package/dist/{fleet-graph-553DZ67Q.js → fleet-graph-TCBCCCBP.js} +11 -11
  126. package/dist/{fleet-inspect-236FZPRB.js → fleet-inspect-KOLKBC56.js} +44 -42
  127. package/dist/{fleet-preflight-NZQHABWO.js → fleet-preflight-44UNCC4Y.js} +27 -25
  128. package/dist/{fleet-validate-7NLAE26R.js → fleet-validate-K2FXRZVA.js} +13 -13
  129. package/dist/{fleet-verify-E3RO6A5W.js → fleet-verify-AS444WDW.js} +42 -40
  130. package/dist/{fleet-view-KMZQY7YJ.js → fleet-view-X4JU4J6O.js} +43 -41
  131. package/dist/gui/ops-worker.js +9 -9
  132. package/dist/gui/reads-worker.js +9 -8
  133. package/dist/{init-DV5B6HQG.js → init-VSOWFHVL.js} +54 -52
  134. package/dist/{interactive-OXGYZYAS.js → interactive-JRCBENLZ.js} +1733 -577
  135. package/dist/{interop-J7UOBOSF.js → interop-JR6BCY7R.js} +10 -10
  136. package/dist/{inventory-OEGIL6PY.js → inventory-3JUGLVJI.js} +43 -41
  137. package/dist/{library-ROZAMUIL.js → library-7BOH3Y7N.js} +15 -15
  138. package/dist/{library-B3UPGY4V.js → library-7PFONXUI.js} +9 -9
  139. package/dist/{library-MXQA5DLT.js → library-TUYZMFIA.js} +7 -7
  140. package/dist/{library-import-2LTWOVBD.js → library-import-TJVXKLDK.js} +10 -10
  141. package/dist/{library-inventory-LJ3XHLWO.js → library-inventory-MD3AYTP4.js} +9 -9
  142. package/dist/{library-validation-DARHHUNW.js → library-validation-ZDLITEA3.js} +7 -7
  143. package/dist/{mcp-2GSPYQ54.js → mcp-EFCWUPUR.js} +3 -3
  144. package/dist/{memory-J3STUUIK.js → memory-Q2YVOKK5.js} +42 -40
  145. package/dist/{models-Y3GMFJ2V.js → models-M7BOCW7F.js} +17 -16
  146. package/dist/{monitor-LHNH577J.js → monitor-2HD3MC67.js} +47 -45
  147. package/dist/{orchestrator-DTAPXULA.js → orchestrator-OUW3ZN6Y.js} +1894 -226
  148. package/dist/{panes-A743ONZ5.js → panes-TTPPSYCL.js} +3 -3
  149. package/dist/{preload-OJBMAIXF.js → preload-A47C2NUF.js} +42 -40
  150. package/dist/{providers-CMETPZNS.js → providers-T43W7EL4.js} +4 -4
  151. package/dist/{resources-FY6XYFN2.js → resources-TP6X2V6W.js} +20 -11
  152. package/dist/{run-H64MEVTA.js → run-LDSBPMCS.js} +61 -59
  153. package/dist/{share-CAFMMQLM.js → share-URHKKMHR.js} +9 -9
  154. package/dist/{skills-ERKQUC7L.js → skills-E477MOOO.js} +13 -11
  155. package/dist/{skills-eval-CQ23MYQK.js → skills-eval-AIBR2L7H.js} +583 -93
  156. package/dist/{skills-inventory-TVUYJDVN.js → skills-inventory-HLMKY3R5.js} +13 -11
  157. package/dist/{slash-commands-ROG3KIFJ.js → slash-commands-LIIZKA5Y.js} +28 -26
  158. package/dist/{startup-background-KDWCQMWT.js → startup-background-JR2KDK6R.js} +43 -41
  159. package/dist/{steer-KKYXJSH4.js → steer-D7RTAPCM.js} +3 -3
  160. package/dist/{system-ZDW44ZGX.js → system-X2P5N4IY.js} +14 -13
  161. package/dist/{targets-AJWA6E7Z.js → targets-VFFAKUFM.js} +25 -24
  162. package/dist/{tasks-6L2JJRC5.js → tasks-ERAWZA22.js} +6 -6
  163. package/dist/{terminal-lease-YFUSDPAD.js → terminal-lease-V2N2BIKQ.js} +2 -2
  164. package/dist/{trace-LA5MVR4Y.js → trace-MA5BT7XY.js} +3 -3
  165. package/dist/{usage-XSILDHKF.js → usage-WYFY2L6M.js} +50 -48
  166. package/dist/{verifiers-ECA5GIHL.js → verifiers-TYIW2XKF.js} +7 -7
  167. package/dist/{verify-R6QQCCEB.js → verify-242UFYTP.js} +6 -6
  168. package/dist/{web-fetch-PVZNMBOB.js → web-fetch-LRAGYU6Y.js} +3 -3
  169. package/dist/{wiki-generate-RP22WP5K.js → wiki-generate-OFKOX2T6.js} +53 -51
  170. package/dist/worker/entry.js +31 -29
  171. package/docs/architecture/architecture.md +1 -0
  172. package/docs/architecture/context-engine.md +4 -2
  173. package/docs/architecture/observability.md +6 -4
  174. package/docs/architecture/prompt-envelope-and-tools.md +1 -1
  175. package/docs/architecture/session-lifecycle.md +1 -1
  176. package/docs/architecture/tui-design.md +12 -9
  177. package/docs/gui/parity/02-slash-and-surfaces.md +1 -1
  178. package/docs/guide/commands-and-modes.md +91 -5
  179. package/docs/guide/configuration-and-targets.md +2 -2
  180. package/docs/guide/context-continuity.md +49 -0
  181. package/docs/guide/proactive-memory.md +6 -3
  182. package/library/registry.yaml +292 -32
  183. package/library/skills/README.md +27 -2
  184. package/library/skills/context/context-handoff/SKILL.md +10 -4
  185. package/library/skills/meta/clio-coder-dev/SKILL.md +92 -88
  186. package/library/skills/meta/clio-coder-dev/evals.md +51 -46
  187. package/library/skills/meta/clio-coder-dev/plugin.json +2 -2
  188. package/library/skills/meta/clio-coder-dev/references/change-map.md +49 -0
  189. package/library/skills/meta/clio-coder-test/SKILL.md +89 -142
  190. package/library/skills/meta/clio-coder-test/evals.md +58 -62
  191. package/library/skills/meta/clio-coder-test/plugin.json +2 -2
  192. package/library/skills/meta/clio-coder-test/references/harness.md +3 -3
  193. package/library/skills/meta/clio-coder-test/references/lifecycle-validation.md +30 -0
  194. package/library/skills/meta/clio-coder-test/references/test-map.md +60 -89
  195. package/library/skills/registry.yaml +5 -5
  196. package/library/skills/skill-marketplace.json +12 -14
  197. package/package.json +1 -1
  198. package/src/cli/skills-eval.ts +1064 -54
  199. package/src/cli/usage.ts +2 -2
  200. package/src/core/clio-repo.ts +3 -0
  201. package/src/core/tool-names.ts +1 -0
  202. package/src/domains/context/budget/inspection.ts +9 -0
  203. package/src/domains/context/budget/live-view.ts +418 -0
  204. package/src/domains/context/budget/pressure.ts +393 -0
  205. package/src/domains/context/budget/request-fit.ts +15 -0
  206. package/src/domains/evidence/build.ts +24 -0
  207. package/src/domains/gateway/mcp/client.ts +1 -1
  208. package/src/domains/memory/commit-state.ts +160 -0
  209. package/src/domains/memory/operations.ts +18 -10
  210. package/src/domains/memory/prompt-cache.ts +95 -0
  211. package/src/domains/memory/prompt-section.ts +50 -7
  212. package/src/domains/memory/relevance.ts +93 -0
  213. package/src/domains/memory/restoration.ts +78 -0
  214. package/src/domains/memory/store.ts +39 -1
  215. package/src/domains/middleware/memory-intervention.ts +116 -24
  216. package/src/domains/observability/background-memory-usage.ts +1 -1
  217. package/src/domains/observability/cost.ts +3 -3
  218. package/src/domains/observability/extension.ts +1 -1
  219. package/src/domains/observability/metrics.ts +1 -1
  220. package/src/domains/prompts/compiler.ts +1 -1
  221. package/src/domains/prompts/extension.ts +38 -1
  222. package/src/domains/quota/anthropic-max-provider.ts +134 -0
  223. package/src/domains/quota/anthropic-usage.ts +220 -0
  224. package/src/domains/quota/antigravity-provider.ts +339 -0
  225. package/src/domains/quota/cache.ts +87 -0
  226. package/src/domains/quota/claude-code-provider.ts +169 -0
  227. package/src/domains/quota/codex-provider.ts +237 -0
  228. package/src/domains/quota/presentation.ts +240 -0
  229. package/src/domains/quota/registry.ts +23 -0
  230. package/src/domains/quota/service.ts +93 -0
  231. package/src/domains/quota/summary-feed.ts +86 -0
  232. package/src/domains/quota/types.ts +84 -0
  233. package/src/domains/resources/index.ts +11 -0
  234. package/src/domains/resources/skills/catalog-view.ts +571 -0
  235. package/src/domains/resources/skills/lexical-match.ts +136 -0
  236. package/src/domains/resources/skills/loader.ts +38 -0
  237. package/src/domains/resources/skills/promotion.ts +1 -55
  238. package/src/domains/resources/skills/provenance-pin.ts +50 -20
  239. package/src/domains/safety/action-classifier.ts +1 -0
  240. package/src/domains/session/compaction/branch-summary.ts +4 -1
  241. package/src/domains/session/compaction/compact.ts +18 -2
  242. package/src/domains/session/compaction/cut-point.ts +20 -1
  243. package/src/domains/session/compaction/tokens.ts +48 -3
  244. package/src/domains/session/context-accounting.ts +8 -1
  245. package/src/domains/session/continuity/carry.ts +59 -0
  246. package/src/domains/session/continuity/contract.ts +592 -0
  247. package/src/domains/session/continuity/evidence.ts +290 -0
  248. package/src/domains/session/continuity/fold.ts +1075 -0
  249. package/src/domains/session/continuity/note.ts +79 -0
  250. package/src/domains/session/continuity/operator-request.ts +104 -0
  251. package/src/domains/session/continuity/persistence.ts +408 -0
  252. package/src/domains/session/continuity/ports.ts +231 -0
  253. package/src/domains/session/continuity/projection.ts +538 -0
  254. package/src/domains/session/continuity/validate.ts +352 -0
  255. package/src/domains/session/entries.ts +50 -3
  256. package/src/domains/session/index.ts +48 -0
  257. package/src/domains/session/migrations/index.ts +7 -3
  258. package/src/domains/session/tree/fork.ts +15 -1
  259. package/src/domains/session/usage.ts +2 -2
  260. package/src/engine/acp/commands.ts +1 -1
  261. package/src/engine/agent.ts +133 -9
  262. package/src/engine/session.ts +13 -6
  263. package/src/entry/orchestrator.ts +116 -33
  264. package/src/interactive/chat-loop-messages.ts +2 -2
  265. package/src/interactive/chat-loop.ts +219 -63
  266. package/src/interactive/chat-renderer.ts +99 -8
  267. package/src/interactive/context-overlay.ts +24 -3
  268. package/src/interactive/continuity-controller.ts +526 -0
  269. package/src/interactive/dispatch-board.ts +21 -3
  270. package/src/interactive/footer/dashboard.ts +25 -1
  271. package/src/interactive/footer/key-hints.ts +2 -2
  272. package/src/interactive/footer/pages.ts +70 -27
  273. package/src/interactive/footer/widgets.ts +32 -7
  274. package/src/interactive/footer-panel.ts +1 -1
  275. package/src/interactive/interactive-application.ts +7 -2
  276. package/src/interactive/interactive-input-runtime.ts +2 -2
  277. package/src/interactive/interactive-presentation.ts +15 -0
  278. package/src/interactive/interactive-slash-runtime.ts +45 -8
  279. package/src/interactive/interactive-tickers.ts +7 -1
  280. package/src/interactive/model-session-replay.ts +130 -3
  281. package/src/interactive/output-reserve.ts +35 -0
  282. package/src/interactive/overlay-general-openers.ts +12 -8
  283. package/src/interactive/overlay-key-routing.ts +6 -9
  284. package/src/interactive/overlay-lifecycle.ts +10 -6
  285. package/src/interactive/overlay-session-lifecycle.ts +21 -14
  286. package/src/interactive/quota-view.ts +229 -0
  287. package/src/interactive/session-last-turn.ts +1 -1
  288. package/src/interactive/session-transcript.ts +2 -1
  289. package/src/interactive/session-usage-reseed.ts +2 -2
  290. package/src/interactive/side-question.ts +2 -2
  291. package/src/interactive/slash-commands.ts +28 -9
  292. package/src/interactive/turn-context.ts +686 -63
  293. package/src/interactive/turn-middleware.ts +39 -6
  294. package/src/interactive/turn-persistence.ts +15 -2
  295. package/src/interactive/turn-prewarm.ts +1 -1
  296. package/src/interactive/turn-runtime.ts +48 -5
  297. package/src/interactive/{cost-overlay.ts → usage-overlay.ts} +121 -33
  298. package/src/interactive/welcome-dashboard.ts +26 -3
  299. package/src/tools/agent-tools.ts +3 -1
  300. package/src/tools/bootstrap.ts +6 -0
  301. package/src/tools/builtin-tool-catalog.ts +9 -0
  302. package/src/tools/context/index.ts +148 -113
  303. package/src/tools/context/surface.ts +11 -7
  304. package/src/tools/core-bootstrap.ts +4 -0
  305. package/src/tools/observation.ts +8 -2
  306. package/src/tools/policy.ts +3 -0
  307. package/src/tools/self-compact.ts +31 -0
  308. package/src/tools/surface.ts +1 -0
  309. package/src/tools/tasks.ts +1 -1
  310. package/dist/chunk-CTFPFW3H.js +0 -44
  311. package/dist/chunk-EOJPDNUP.js +0 -1325
  312. package/dist/chunk-I4Y4WKZR.js +0 -386
@@ -11,7 +11,7 @@ import {
11
11
  parseEditorBashCommand,
12
12
  unguardPastedEditorOperator,
13
13
  writeInputWedgeDump
14
- } from "./chunk-R2UUNSNQ.js";
14
+ } from "./chunk-O3ZAF7UY.js";
15
15
  import {
16
16
  OperatorExtensionRuntime
17
17
  } from "./chunk-C7AQTPCE.js";
@@ -39,7 +39,10 @@ import {
39
39
  clockLocal,
40
40
  compactReasoningTokens,
41
41
  contextCategorySwatch,
42
+ contextOccupancyBar,
42
43
  contextQuadrant,
44
+ contextUsageText,
45
+ continuityContextFromSession,
43
46
  createAssistantGenerationTiming,
44
47
  createCoalescingChatRenderer,
45
48
  createDefaultArtifactProviders,
@@ -68,6 +71,7 @@ import {
68
71
  isDispatchBoardRowSteerable,
69
72
  listRecentModels,
70
73
  listViewArtifacts,
74
+ localQuotaSnapshot,
71
75
  loopBlockedAuditReason,
72
76
  loopBlockedStopReason,
73
77
  mergeHandoffDecisions,
@@ -83,23 +87,27 @@ import {
83
87
  previewBudget,
84
88
  previewRows,
85
89
  providerErrorEvidence,
90
+ quotaMeter,
91
+ quotaSummaryLine,
86
92
  readWorkerReceiptFacts,
87
93
  reasoningFromTally,
88
94
  rehydrateChatPanelFromTurns,
89
95
  relative,
90
96
  renderBashTranscriptExecution,
91
- renderContextMeterBar,
92
97
  renderContextMeterGrid,
93
98
  renderDispatchActivity,
94
99
  renderEvictedTokensLine,
95
100
  renderHandoffDocument,
101
+ renderQuotaAccounts,
96
102
  renderRetryStatus,
97
103
  renderToolArguments,
98
104
  renderToolAwaitingApproval,
99
105
  renderToolExecution,
100
106
  renderToolPreview,
107
+ renderWorkerUsage,
101
108
  resolveFooterVerb,
102
109
  restartRequiredNotice,
110
+ routeWeeklyQuota,
103
111
  safeWorkerAnswerText,
104
112
  safetyBlockedNotice,
105
113
  sessionQuadrant,
@@ -114,18 +122,19 @@ import {
114
122
  validateHandoffFiles,
115
123
  validateHandoffGoal,
116
124
  verifyReceiptFileReport,
125
+ withContinuityReplay,
117
126
  workerEscalationNotice,
118
127
  workerRunEntryFields,
119
128
  workspaceQuadrant,
120
129
  zipColumnBlocks,
121
130
  zipColumns
122
- } from "./chunk-62UK7AGW.js";
123
- import "./chunk-YQ6XEFVK.js";
131
+ } from "./chunk-M5PMRZSR.js";
132
+ import "./chunk-Z74OGONW.js";
124
133
  import {
125
134
  extractText,
126
135
  isSelfExplainingAbort
127
- } from "./chunk-ZR5YA6ZF.js";
128
- import "./chunk-ZJPORZTC.js";
136
+ } from "./chunk-36XVK5P5.js";
137
+ import "./chunk-GIAI7A2K.js";
129
138
  import {
130
139
  CLIO_APP_KEYBINDINGS,
131
140
  CLIO_APP_KEYBINDING_IDS,
@@ -181,7 +190,7 @@ import {
181
190
  planLibraryImport,
182
191
  releaseLibraryImport,
183
192
  renderProposalEntry
184
- } from "./chunk-T2MDZES3.js";
193
+ } from "./chunk-EBMTBE76.js";
185
194
  import {
186
195
  detectInteropAgents
187
196
  } from "./chunk-4A6YRGV4.js";
@@ -199,23 +208,23 @@ import {
199
208
  parseSlashCommand,
200
209
  renderArgsSpec,
201
210
  workerAskedByModel
202
- } from "./chunk-3LT34CAM.js";
211
+ } from "./chunk-XBIGUILU.js";
203
212
  import "./chunk-DGC6X6XR.js";
204
213
  import {
205
214
  resolveExtensionCommands
206
215
  } from "./chunk-RFCKPS2V.js";
207
216
  import "./chunk-QUIJZSDW.js";
208
- import "./chunk-UCINIRHW.js";
209
- import "./chunk-OJXOA6YU.js";
217
+ import "./chunk-TPCZXWLT.js";
218
+ import "./chunk-TL6LNERH.js";
210
219
  import "./chunk-GGPF6LLE.js";
211
220
  import {
212
221
  formatUserTaskHandoff
213
- } from "./chunk-ISHUS7HC.js";
214
- import "./chunk-HQVN7G4F.js";
222
+ } from "./chunk-JRW236JT.js";
223
+ import "./chunk-G62JBPXN.js";
215
224
  import {
216
225
  appendOutOfTurnUsageRow
217
- } from "./chunk-V35KZDUZ.js";
218
- import "./chunk-HOEDA42N.js";
226
+ } from "./chunk-AA3GRZWS.js";
227
+ import "./chunk-T7HZ7KJF.js";
219
228
  import {
220
229
  SETTINGS_DESCRIPTIONS_BY_ID,
221
230
  SETTINGS_HELP_BY_ID,
@@ -233,10 +242,10 @@ import {
233
242
  recallErrorMessage,
234
243
  recallParentTurnId,
235
244
  resolveRecall
236
- } from "./chunk-EG4ARXPC.js";
245
+ } from "./chunk-OX6QTA4N.js";
237
246
  import {
238
247
  foldWorkingSet
239
- } from "./chunk-EZD2BXGP.js";
248
+ } from "./chunk-GUFQPTQK.js";
240
249
  import {
241
250
  getAtPath,
242
251
  isRoutingPath,
@@ -248,7 +257,7 @@ import {
248
257
  settingsGroupForPath,
249
258
  settingsSectionForPath
250
259
  } from "./chunk-LJRBX7QR.js";
251
- import "./chunk-4WOOTKFD.js";
260
+ import "./chunk-U3FETNQB.js";
252
261
  import "./chunk-Y3JU5RPK.js";
253
262
  import {
254
263
  CombinedAutocompleteProvider,
@@ -287,20 +296,20 @@ import {
287
296
  loadMemoryRecordsSync,
288
297
  proposeMemoryPromotion,
289
298
  renderCostAggregate
290
- } from "./chunk-LTWH2ACR.js";
299
+ } from "./chunk-EQPFEDFV.js";
291
300
  import "./chunk-PECBKCDK.js";
292
- import "./chunk-OV62D6KO.js";
293
- import "./chunk-I4ELN5BX.js";
301
+ import "./chunk-KQMYL5GR.js";
302
+ import "./chunk-ZTNOEJRI.js";
294
303
  import {
295
304
  preflightWriteBoundaries
296
- } from "./chunk-DXSETN72.js";
305
+ } from "./chunk-7WUGNN43.js";
297
306
  import {
298
307
  bindExecutionPlanEndpoints,
299
308
  compileFleetExecutionPlan
300
- } from "./chunk-NFSK2VNU.js";
309
+ } from "./chunk-LZMTVVN4.js";
301
310
  import {
302
311
  trustStateWord
303
- } from "./chunk-OZ76TLGU.js";
312
+ } from "./chunk-EEITU7S6.js";
304
313
  import {
305
314
  addResponseModelIdObservationCount,
306
315
  addResponseModelIdObservationCounts,
@@ -309,12 +318,12 @@ import {
309
318
  ledgerUsageCalls,
310
319
  responseModelIdObservationCountsLabel,
311
320
  responseModelIdObservationFromRecord
312
- } from "./chunk-EOJPDNUP.js";
321
+ } from "./chunk-GPTR6OTU.js";
313
322
  import "./chunk-BOBCCDJ6.js";
314
323
  import {
315
324
  describeLocalCapacity
316
325
  } from "./chunk-E6IPKW2E.js";
317
- import "./chunk-XMDBTYCH.js";
326
+ import "./chunk-N3CWOGVF.js";
318
327
  import "./chunk-UQIVHSE4.js";
319
328
  import "./chunk-BYFHHCFV.js";
320
329
  import {
@@ -325,7 +334,7 @@ import "./chunk-LQTKDAUO.js";
325
334
  import "./chunk-ZU4JPCPR.js";
326
335
  import {
327
336
  foldDecisionBoard
328
- } from "./chunk-U5QU5ZOD.js";
337
+ } from "./chunk-XYPWFSU5.js";
329
338
  import {
330
339
  emptyWorkspaceSnapshot,
331
340
  probeGitStatusAsync,
@@ -334,9 +343,9 @@ import {
334
343
  import {
335
344
  filterEntriesToActivePath
336
345
  } from "./chunk-O5F25NIE.js";
337
- import "./chunk-U6YTUVOX.js";
346
+ import "./chunk-Q4RMNWMZ.js";
338
347
  import "./chunk-SD7SHNDW.js";
339
- import "./chunk-7RET77VB.js";
348
+ import "./chunk-Q2HKY32Y.js";
340
349
  import "./chunk-YQ3GFEUY.js";
341
350
  import "./chunk-WTQGRBHV.js";
342
351
  import "./chunk-GLIQT7SU.js";
@@ -351,23 +360,23 @@ import {
351
360
  } from "./chunk-XISLDHEL.js";
352
361
  import {
353
362
  collectSessionEntries
354
- } from "./chunk-RNDCROSG.js";
363
+ } from "./chunk-CNXU7TR2.js";
355
364
  import {
356
365
  applyLibraryLifecycle,
357
366
  libraryImportOutcome,
358
367
  planLibraryLifecycle,
359
368
  releaseLibraryLifecycle,
360
369
  retryLibraryRefresh
361
- } from "./chunk-5KQEUIWM.js";
370
+ } from "./chunk-7LUEQODV.js";
362
371
  import {
363
372
  inspectLibraryCopy,
364
373
  readLibraryInventory
365
- } from "./chunk-5JQR5B4D.js";
366
- import "./chunk-ZARZ4POR.js";
367
- import "./chunk-UX2L5CUD.js";
374
+ } from "./chunk-6KV4KCQZ.js";
375
+ import "./chunk-G5ZDQQE3.js";
376
+ import "./chunk-AI36V3ON.js";
368
377
  import {
369
378
  installSkill
370
- } from "./chunk-LAR7DX5E.js";
379
+ } from "./chunk-B5TASTCK.js";
371
380
  import "./chunk-DNCE23JJ.js";
372
381
  import "./chunk-CDQ2UC6W.js";
373
382
  import {
@@ -381,11 +390,11 @@ import {
381
390
  validateFleetGraph
382
391
  } from "./chunk-5LP5FNNR.js";
383
392
  import "./chunk-NVPXXW2H.js";
384
- import "./chunk-R7FGRZNF.js";
385
- import "./chunk-DOUISWEE.js";
393
+ import "./chunk-LXYHCCIZ.js";
394
+ import "./chunk-QOJ4ERMU.js";
386
395
  import {
387
396
  retiredIntegrityVersionOf
388
- } from "./chunk-VKHLUZNO.js";
397
+ } from "./chunk-UZ7YBL43.js";
389
398
  import {
390
399
  askAxis,
391
400
  askUserExposure,
@@ -396,14 +405,14 @@ import {
396
405
  decisionFactsForAnswer,
397
406
  openDetachedBatchViews,
398
407
  runBashCommand
399
- } from "./chunk-OQ2EOAHA.js";
408
+ } from "./chunk-PF36KOGI.js";
400
409
  import {
401
410
  foldSessionTaskHistory,
402
411
  foldTaskBoard,
403
412
  taskBoardCounts
404
- } from "./chunk-NDPF2N2L.js";
413
+ } from "./chunk-7DW7CJW2.js";
405
414
  import "./chunk-7QXCO3AF.js";
406
- import "./chunk-T33IYTZM.js";
415
+ import "./chunk-SAASVMIY.js";
407
416
  import "./chunk-OH3YYA3B.js";
408
417
  import {
409
418
  extensionPanelText
@@ -416,31 +425,32 @@ import {
416
425
  sanitizeCallTargetText,
417
426
  sanitizeMultilineDisplayText
418
427
  } from "./chunk-XNIZBOFV.js";
419
- import "./chunk-XZWSP67B.js";
428
+ import "./chunk-5APYAXCB.js";
420
429
  import {
421
430
  agentRoleFactsResolver,
422
431
  requestExecutionRole,
423
432
  withAttemptRole
424
- } from "./chunk-BRCOSE7O.js";
425
- import "./chunk-55RMDDFM.js";
433
+ } from "./chunk-SZUO5BSE.js";
434
+ import "./chunk-PGQG7ZMW.js";
426
435
  import "./chunk-L4N3DWNW.js";
427
- import "./chunk-TDGYTWM6.js";
428
- import "./chunk-CTFPFW3H.js";
429
- import "./chunk-DE2LU267.js";
436
+ import "./chunk-F4I3H56H.js";
437
+ import "./chunk-Q6FRR3AQ.js";
438
+ import "./chunk-KKAJGT3B.js";
439
+ import "./chunk-H7REJ7L5.js";
430
440
  import "./chunk-YYLCPDCC.js";
431
441
  import {
432
442
  redactSecretString
433
443
  } from "./chunk-EFSEIZE6.js";
434
- import "./chunk-KBUTSJSR.js";
444
+ import "./chunk-4XBYHXGK.js";
435
445
  import {
436
446
  expandInlineFileReferencesAsync
437
- } from "./chunk-YBUECLAF.js";
438
- import "./chunk-CRHPCPZP.js";
439
- import "./chunk-FFYTC2KQ.js";
440
- import "./chunk-UJD5MPP6.js";
447
+ } from "./chunk-YEJQDODA.js";
448
+ import "./chunk-4KC4CHKJ.js";
449
+ import "./chunk-MNABNEBX.js";
450
+ import "./chunk-FLNXQQ5B.js";
441
451
  import "./chunk-DZLR4UEH.js";
442
- import "./chunk-OPD7GL6F.js";
443
- import "./chunk-733ZBT5P.js";
452
+ import "./chunk-H6FQVNZC.js";
453
+ import "./chunk-E74MJF6G.js";
444
454
  import "./chunk-GHXGMFQ5.js";
445
455
  import "./chunk-KECNXMDE.js";
446
456
  import "./chunk-WXA2CFJO.js";
@@ -456,7 +466,7 @@ import {
456
466
  resolveRuntimeTarget,
457
467
  thinkingLevelChoiceLabel,
458
468
  thinkingLevelFromChoiceLabel
459
- } from "./chunk-EYTLV3W3.js";
469
+ } from "./chunk-HTWTORF7.js";
460
470
  import "./chunk-MCJKEGHO.js";
461
471
  import "./chunk-DVTBERFY.js";
462
472
  import {
@@ -472,7 +482,7 @@ import {
472
482
  ceilChars,
473
483
  contentChars,
474
484
  extractReasoningTokens
475
- } from "./chunk-6W75D37F.js";
485
+ } from "./chunk-53P775GE.js";
476
486
  import {
477
487
  formatContextWindowSlots
478
488
  } from "./chunk-77DCC6JZ.js";
@@ -480,6 +490,7 @@ import {
480
490
  resolveProviderReference
481
491
  } from "./chunk-SYF7FVBL.js";
482
492
  import {
493
+ openAuthStorage,
483
494
  targetRequiresAuth
484
495
  } from "./chunk-Z4GLJ6J4.js";
485
496
  import "./chunk-VNBICSS2.js";
@@ -494,25 +505,26 @@ import "./chunk-L7G7K4I2.js";
494
505
  import "./chunk-ALJWT6O6.js";
495
506
  import "./chunk-4WV4PI66.js";
496
507
  import "./chunk-HMN2C6YL.js";
508
+ import "./chunk-F6KCJO3U.js";
509
+ import "./chunk-UZVYEF57.js";
497
510
  import {
498
511
  SKILL_SUGGESTION_PREFIX
499
- } from "./chunk-I4Y4WKZR.js";
500
- import "./chunk-UZVYEF57.js";
512
+ } from "./chunk-MMZMM6SW.js";
501
513
  import "./chunk-O3VV6IVK.js";
502
514
  import {
503
515
  openSession,
504
516
  sessionPaths
505
- } from "./chunk-EKV4UBCM.js";
517
+ } from "./chunk-PZBQUJ2F.js";
506
518
  import "./chunk-UHW5V7HC.js";
507
519
  import {
508
520
  isUserVisibleAgent
509
- } from "./chunk-LNOXCGN2.js";
521
+ } from "./chunk-7UFDIOBT.js";
510
522
  import "./chunk-BJH6WFIB.js";
511
523
  import {
512
524
  interopAgentKind
513
525
  } from "./chunk-5EQO5NWP.js";
514
- import "./chunk-VYRQHORQ.js";
515
- import "./chunk-P4GNZPXO.js";
526
+ import "./chunk-NMQSO6Z6.js";
527
+ import "./chunk-OYUS3UZP.js";
516
528
  import "./chunk-R2P2U3ZS.js";
517
529
  import "./chunk-NTTS3O53.js";
518
530
  import "./chunk-C2CPOIH2.js";
@@ -520,7 +532,7 @@ import "./chunk-MGLRTOCK.js";
520
532
  import "./chunk-M4KFUXS7.js";
521
533
  import {
522
534
  ToolNames
523
- } from "./chunk-V4XJWDHR.js";
535
+ } from "./chunk-6UKFRTXI.js";
524
536
  import {
525
537
  SettingsValidationError,
526
538
  bindAgentProfileInSettings,
@@ -3113,12 +3125,32 @@ function renderContextLedgerLines(ledger, contentWidth, workingSet, workingSetCo
3113
3125
  var STREAMING_FALLBACK_TICK_MS = 1e3;
3114
3126
  function openContextOverlay(tui, getLedger, options) {
3115
3127
  let contentWidth = DEFAULT_CONTENT_WIDTH;
3116
- const render2 = () => renderContextLedgerLines(
3117
- getLedger(),
3118
- contentWidth,
3119
- options?.getWorkingSet?.() ?? null,
3120
- options?.getWorkingSetConfig?.() ?? null
3121
- ).join("\n");
3128
+ const render2 = () => {
3129
+ const lines = renderContextLedgerLines(
3130
+ getLedger(),
3131
+ contentWidth,
3132
+ options?.getWorkingSet?.() ?? null,
3133
+ options?.getWorkingSetConfig?.() ?? null
3134
+ );
3135
+ const inspection = options?.chat?.inspectLiveBudget?.();
3136
+ if (inspection?.status === "available") {
3137
+ const view = inspection.view;
3138
+ const count = (value) => value === null ? "unknown" : formatTokens(value);
3139
+ lines.push(
3140
+ "",
3141
+ `Next request: ${count(view.inputTokens)} input + ${count(view.outputReserveTokens)} output`,
3142
+ `Headroom: ${count(view.headroomTokens)} \xB7 ${view.phase}`
3143
+ );
3144
+ if (view.pendingHandoff) {
3145
+ lines.push(
3146
+ `Handoff: ${view.lastOutcome?.outcome ?? "pending"}`,
3147
+ view.pendingHandoff.id,
3148
+ `/context recover ${view.pendingHandoff.id} reduce|deliver`
3149
+ );
3150
+ } else if (view.lastOutcome) lines.push(`Last handoff: ${view.lastOutcome.outcome}`);
3151
+ }
3152
+ return lines.join("\n");
3153
+ };
3122
3154
  const text = new Text(render2(), 0, 0);
3123
3155
  const body = {
3124
3156
  render(width) {
@@ -3188,273 +3220,6 @@ function openContextOverlay(tui, getLedger, options) {
3188
3220
  };
3189
3221
  }
3190
3222
 
3191
- // src/interactive/cost-overlay.ts
3192
- init_esm_shims();
3193
- var DEFAULT_CONTENT_WIDTH2 = 80;
3194
- var COST_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH2 + 4;
3195
- function formatTokens2(n) {
3196
- return n.toLocaleString("en-US");
3197
- }
3198
- function aggregateCostEntries(entries) {
3199
- const grouped = /* @__PURE__ */ new Map();
3200
- for (const entry of entries) {
3201
- const key = `${entry.providerId}::${entry.attributedModelId}`;
3202
- const existing = grouped.get(key);
3203
- if (existing) {
3204
- existing.row.runs += 1;
3205
- existing.row.tokens += entry.tokens;
3206
- existing.row.input += entry.input;
3207
- existing.row.output += entry.output;
3208
- existing.row.cacheRead += entry.cacheRead;
3209
- existing.row.cacheWrite += entry.cacheWrite;
3210
- existing.row.reasoningTokens += entry.reasoningTokens;
3211
- existing.row.apiCalls += entry.apiCalls ?? 1;
3212
- if (entry.label === "side-question") existing.row.sideQuestions += 1;
3213
- if (entry.label === "handoff") existing.row.handoffs += 1;
3214
- if (entry.label === "prewarm") existing.row.prewarms += 1;
3215
- if (entry.label === "background-memory") existing.row.backgroundMemory += 1;
3216
- if (entry.label === "failed-compaction") existing.row.failedCompaction += 1;
3217
- for (const requestedModelId of entry.requestedModelIds) existing.requestedModelIds.add(requestedModelId);
3218
- addResponseModelIdObservationCounts(
3219
- existing.responseModelIdObservationCounts,
3220
- entry.responseModelIdObservationCounts
3221
- );
3222
- existing.entries.push(entry);
3223
- continue;
3224
- }
3225
- grouped.set(key, {
3226
- row: {
3227
- providerId: entry.providerId,
3228
- attributedModelId: entry.attributedModelId,
3229
- runs: 1,
3230
- tokens: entry.tokens,
3231
- input: entry.input,
3232
- output: entry.output,
3233
- cacheRead: entry.cacheRead,
3234
- cacheWrite: entry.cacheWrite,
3235
- reasoningTokens: entry.reasoningTokens,
3236
- apiCalls: entry.apiCalls ?? 1,
3237
- sideQuestions: entry.label === "side-question" ? 1 : 0,
3238
- handoffs: entry.label === "handoff" ? 1 : 0,
3239
- prewarms: entry.label === "prewarm" ? 1 : 0,
3240
- backgroundMemory: entry.label === "background-memory" ? 1 : 0,
3241
- failedCompaction: entry.label === "failed-compaction" ? 1 : 0
3242
- },
3243
- requestedModelIds: new Set(entry.requestedModelIds),
3244
- responseModelIdObservationCounts: { ...entry.responseModelIdObservationCounts },
3245
- entries: [entry]
3246
- });
3247
- }
3248
- const rows = Array.from(grouped.values(), ({ row: row2, entries: entries2, requestedModelIds, responseModelIdObservationCounts }) => ({
3249
- ...row2,
3250
- requestedModelIds: [...requestedModelIds].sort(),
3251
- responseModelIdObservationCounts,
3252
- cost: aggregateCostAmounts(entries2.map((entry) => ({ usd: entry.usd, provenance: entry.provenance })))
3253
- }));
3254
- rows.sort((a, b) => {
3255
- if (a.providerId !== b.providerId) return a.providerId < b.providerId ? -1 : 1;
3256
- if (a.attributedModelId !== b.attributedModelId) return a.attributedModelId < b.attributedModelId ? -1 : 1;
3257
- return 0;
3258
- });
3259
- return rows;
3260
- }
3261
- function sumRows(rows) {
3262
- return rows.reduce(
3263
- (acc, row2) => ({
3264
- runs: acc.runs + row2.runs,
3265
- tokens: acc.tokens + row2.tokens,
3266
- input: acc.input + row2.input,
3267
- output: acc.output + row2.output,
3268
- cacheRead: acc.cacheRead + row2.cacheRead,
3269
- cacheWrite: acc.cacheWrite + row2.cacheWrite,
3270
- reasoningTokens: acc.reasoningTokens + row2.reasoningTokens,
3271
- apiCalls: acc.apiCalls + row2.apiCalls,
3272
- sideQuestions: acc.sideQuestions + row2.sideQuestions,
3273
- handoffs: acc.handoffs + row2.handoffs,
3274
- prewarms: acc.prewarms + row2.prewarms,
3275
- backgroundMemory: acc.backgroundMemory + row2.backgroundMemory,
3276
- failedCompaction: acc.failedCompaction + row2.failedCompaction
3277
- }),
3278
- {
3279
- runs: 0,
3280
- tokens: 0,
3281
- input: 0,
3282
- output: 0,
3283
- cacheRead: 0,
3284
- cacheWrite: 0,
3285
- reasoningTokens: 0,
3286
- apiCalls: 0,
3287
- sideQuestions: 0,
3288
- handoffs: 0,
3289
- prewarms: 0,
3290
- backgroundMemory: 0,
3291
- failedCompaction: 0
3292
- }
3293
- );
3294
- }
3295
- function cacheReadValue(cacheRead, apiCalls) {
3296
- if (apiCalls > 1 && cacheRead > 0) {
3297
- return [formatTokens2(cacheRead), `(avg/call ${formatTokens2(Math.round(cacheRead / apiCalls))})`];
3298
- }
3299
- return [formatTokens2(cacheRead)];
3300
- }
3301
- function reasoningValue(reasoningTokens) {
3302
- return [formatTokens2(reasoningTokens), "provider-reported only"];
3303
- }
3304
- function kvBlock(entries) {
3305
- const theme2 = clioTheme();
3306
- const keyWidth = entries.reduce((max, [key]) => Math.max(max, key.length), 0);
3307
- const valueWidth = entries.reduce((max, [, value]) => Math.max(max, value.length), 0);
3308
- return entries.map(([key, value, annotation]) => {
3309
- const row2 = `${theme2.fg("dim", key.padEnd(keyWidth))} ${theme2.fg("muted", value.padStart(valueWidth))}`;
3310
- return annotation ? `${row2} ${theme2.fg("dim", annotation)}` : row2;
3311
- });
3312
- }
3313
- function summaryBlock(totalCost, totalTokens, rows, promptCache) {
3314
- const totals = sumRows(rows);
3315
- const resolvedTotal = totalTokens > 0 ? totalTokens : totals.tokens;
3316
- const cost = formatCostAggregate(totalCost);
3317
- const cacheRows = promptCache !== null && hasPromptCacheTelemetry(promptCache) ? [
3318
- [
3319
- "uncached prefill",
3320
- promptCache.uncachedPrefillTokens === null ? "not reported" : formatTokens2(promptCache.uncachedPrefillTokens)
3321
- ],
3322
- [
3323
- "cache verdicts",
3324
- `hot ${promptCache.verdictCounts.hot} \xB7 partial ${promptCache.verdictCounts.partial} \xB7 cold ${promptCache.verdictCounts.cold} \xB7 small ${promptCache.verdictCounts.small} \xB7 unknown ${promptCache.verdictCounts.unknown}`
3325
- ]
3326
- ] : [];
3327
- return kvBlock([
3328
- [
3329
- "turns",
3330
- formatTokens2(
3331
- totals.runs - totals.sideQuestions - totals.handoffs - totals.prewarms - totals.backgroundMemory - totals.failedCompaction
3332
- )
3333
- ],
3334
- ["model calls", formatTokens2(totals.apiCalls)],
3335
- ...totals.sideQuestions > 0 ? [["side questions", formatTokens2(totals.sideQuestions)]] : [],
3336
- ...totals.handoffs > 0 ? [["handoffs", formatTokens2(totals.handoffs)]] : [],
3337
- ...totals.prewarms > 0 ? [["pre-warms", formatTokens2(totals.prewarms)]] : [],
3338
- ...totals.backgroundMemory > 0 ? [["memory steps", formatTokens2(totals.backgroundMemory)]] : [],
3339
- ...totals.failedCompaction > 0 ? [
3340
- ["failed compaction calls", formatTokens2(totals.failedCompaction)],
3341
- ["compaction usage", "known subtotals; missing usage is unknown"]
3342
- ] : [],
3343
- ...cost === null ? [] : [["cost", cost]],
3344
- ["input", formatTokens2(totals.input)],
3345
- ["output", formatTokens2(totals.output)],
3346
- ["reasoning", ...reasoningValue(totals.reasoningTokens)],
3347
- ["cache read", ...cacheReadValue(totals.cacheRead, totals.apiCalls)],
3348
- ["cache write", formatTokens2(totals.cacheWrite)],
3349
- ...cacheRows,
3350
- ["processed", `${formatTokens2(resolvedTotal)} tokens`]
3351
- ]);
3352
- }
3353
- function modelBlock(row2) {
3354
- const cost = formatCostAggregate(row2.cost);
3355
- return kvBlock([
3356
- ["requested model ids", row2.requestedModelIds.join(", ")],
3357
- ["response model id observation", responseModelIdObservationCountsLabel(row2.responseModelIdObservationCounts)],
3358
- [
3359
- "turns",
3360
- formatTokens2(
3361
- row2.runs - row2.sideQuestions - row2.handoffs - row2.prewarms - row2.backgroundMemory - row2.failedCompaction
3362
- )
3363
- ],
3364
- ["model calls", formatTokens2(row2.apiCalls)],
3365
- ...row2.sideQuestions > 0 ? [["side questions", formatTokens2(row2.sideQuestions)]] : [],
3366
- ...row2.handoffs > 0 ? [["handoffs", formatTokens2(row2.handoffs)]] : [],
3367
- ...row2.prewarms > 0 ? [["pre-warms", formatTokens2(row2.prewarms)]] : [],
3368
- ...row2.backgroundMemory > 0 ? [["memory steps", formatTokens2(row2.backgroundMemory)]] : [],
3369
- ...row2.failedCompaction > 0 ? [["failed compaction calls", formatTokens2(row2.failedCompaction)]] : [],
3370
- ...cost === null ? [] : [["cost", cost]],
3371
- ["input", formatTokens2(row2.input)],
3372
- ["output", formatTokens2(row2.output)],
3373
- ["reasoning", ...reasoningValue(row2.reasoningTokens)],
3374
- ["cache read", ...cacheReadValue(row2.cacheRead, row2.apiCalls)],
3375
- ["cache write", formatTokens2(row2.cacheWrite)],
3376
- ["processed", `${formatTokens2(row2.tokens)} tokens`]
3377
- ]);
3378
- }
3379
- function formatCostOverlayBodyLines(totalCost, totalTokens, rows, contentWidth, promptCache = null) {
3380
- const theme2 = clioTheme();
3381
- const lines = [];
3382
- for (const line of summaryBlock(totalCost, totalTokens, rows, promptCache)) {
3383
- lines.push(line);
3384
- }
3385
- lines.push(rule(theme2, contentWidth));
3386
- if (rows.length === 0) {
3387
- lines.push(theme2.fg("muted", "no token usage recorded for this session"));
3388
- } else {
3389
- for (const [index, row2] of rows.entries()) {
3390
- if (index > 0) lines.push("");
3391
- lines.push(theme2.style("accent", `${row2.providerId} \xB7 attributed model ${row2.attributedModelId}`, { bold: true }));
3392
- for (const line of modelBlock(row2)) {
3393
- lines.push(line);
3394
- }
3395
- }
3396
- }
3397
- return lines;
3398
- }
3399
- function buildCostSnapshot(observability, sessionId, snapshot, getSessionEntries) {
3400
- const entries = observability.costEntries();
3401
- const rows = aggregateCostEntries(entries);
3402
- const totalTokens = rows.reduce((sum, r) => sum + r.tokens, 0);
3403
- return {
3404
- sessionId,
3405
- totalCost: snapshot?.session.cost ?? observability.sessionCostSummary(),
3406
- totalTokens,
3407
- rows,
3408
- promptCache: getSessionEntries ? foldPromptCacheTelemetry(getSessionEntries()) : null
3409
- };
3410
- }
3411
- var CostOverlayBody = class {
3412
- constructor(getSnapshot) {
3413
- this.getSnapshot = getSnapshot;
3414
- }
3415
- getSnapshot;
3416
- render(width) {
3417
- const contentWidth = Math.max(1, Math.floor(width));
3418
- const snapshot = this.getSnapshot();
3419
- return formatCostOverlayBodyLines(
3420
- snapshot.totalCost,
3421
- snapshot.totalTokens,
3422
- snapshot.rows,
3423
- contentWidth,
3424
- snapshot.promptCache
3425
- );
3426
- }
3427
- invalidate() {
3428
- }
3429
- };
3430
- function openCostOverlay(tui, observability, options) {
3431
- const sessionId = options?.sessionId ?? null;
3432
- let latest = observability.snapshot();
3433
- const body = new CostOverlayBody(
3434
- () => buildCostSnapshot(observability, sessionId, latest, options?.getSessionEntries)
3435
- );
3436
- const handle = showClioOverlayFrame(tui, body, {
3437
- anchor: "center",
3438
- width: COST_OVERLAY_WIDTH,
3439
- // Not derived from the title: that one carries the session id.
3440
- markerId: "cost",
3441
- title: sessionId && sessionId.length > 0 ? `Session usage (${sessionId})` : "Session usage",
3442
- footerHint: buildHint([])
3443
- });
3444
- const unsubscribe = observability.subscribe((snapshot) => {
3445
- latest = snapshot;
3446
- body.invalidate();
3447
- tui.requestRender();
3448
- });
3449
- return {
3450
- ...handle,
3451
- hide() {
3452
- unsubscribe();
3453
- handle.hide();
3454
- }
3455
- };
3456
- }
3457
-
3458
3223
  // src/interactive/fleet-run-preview.ts
3459
3224
  init_esm_shims();
3460
3225
  function defaultLoad(workspaceRoot, name) {
@@ -4338,9 +4103,9 @@ function openListOverlay(tui, options) {
4338
4103
  }
4339
4104
 
4340
4105
  // src/interactive/memory-overlay.ts
4341
- var DEFAULT_CONTENT_WIDTH3 = 96;
4106
+ var DEFAULT_CONTENT_WIDTH2 = 96;
4342
4107
  var REFRESH_MS = 1e3;
4343
- var MEMORY_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH3 + 4;
4108
+ var MEMORY_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH2 + 4;
4344
4109
  var EMPTY_MESSAGE = "no approved lessons, task-bank entries, or memory steps captured yet.";
4345
4110
  function formatMemoryStatusLine(status, contentWidth) {
4346
4111
  const theme2 = clioTheme();
@@ -4707,8 +4472,8 @@ function openContextResetOverlay(tui, deps) {
4707
4472
 
4708
4473
  // src/interactive/overlays/decisions.ts
4709
4474
  init_esm_shims();
4710
- var DEFAULT_CONTENT_WIDTH4 = 88;
4711
- var DECISIONS_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH4 + 4;
4475
+ var DEFAULT_CONTENT_WIDTH3 = 88;
4476
+ var DECISIONS_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH3 + 4;
4712
4477
  function formatDecisionCorrectionTurn(selection, correction) {
4713
4478
  return `Decision "${selection.label}" (previously: ${selection.value}) is superseded by the operator. New direction: ${correction}. Acknowledge and adjust the plan.`;
4714
4479
  }
@@ -4772,7 +4537,7 @@ function decisionLines(decision, selected, expanded, width) {
4772
4537
  }
4773
4538
  return lines;
4774
4539
  }
4775
- function formatDecisionsOverlayBodyLines(interviews, selectedIndex = 0, expandedKey = null, contentWidth = DEFAULT_CONTENT_WIDTH4, now = Date.now()) {
4540
+ function formatDecisionsOverlayBodyLines(interviews, selectedIndex = 0, expandedKey = null, contentWidth = DEFAULT_CONTENT_WIDTH3, now = Date.now()) {
4776
4541
  const width = Math.max(1, Math.floor(contentWidth));
4777
4542
  const theme2 = clioTheme();
4778
4543
  if (interviews.length === 0) {
@@ -5171,9 +4936,9 @@ function openSideQuestionOverlay(tui, options) {
5171
4936
  // src/interactive/tasks-overlay.ts
5172
4937
  init_esm_shims();
5173
4938
  import { isAbsolute, relative as relative3, resolve } from "node:path";
5174
- var DEFAULT_CONTENT_WIDTH5 = 84;
4939
+ var DEFAULT_CONTENT_WIDTH4 = 84;
5175
4940
  var REFRESH_MS2 = 1e3;
5176
- var TASKS_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH5 + 4;
4941
+ var TASKS_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH4 + 4;
5177
4942
  var STATUS_PRESENTATION = {
5178
4943
  pending: { glyph: GLYPH.queued, token: "dim", label: "pending" },
5179
4944
  active: { glyph: GLYPH.running, token: "accent", label: "active" },
@@ -5243,7 +5008,7 @@ function taskReceiptRows(task, width) {
5243
5008
  }
5244
5009
  return [];
5245
5010
  }
5246
- function formatTasksOverlayBodyLines(board, contentWidth = DEFAULT_CONTENT_WIDTH5, selectedTaskId) {
5011
+ function formatTasksOverlayBodyLines(board, contentWidth = DEFAULT_CONTENT_WIDTH4, selectedTaskId) {
5247
5012
  const theme2 = clioTheme();
5248
5013
  const width = Math.max(1, Math.floor(contentWidth));
5249
5014
  if (!board || board.tasks.length === 0) {
@@ -5326,7 +5091,7 @@ var USER_TASK_PRESENTATION = {
5326
5091
  done: { glyph: GLYPH.ok, token: "success" },
5327
5092
  dropped: { glyph: GLYPH.cancelled, token: "dim" }
5328
5093
  };
5329
- function formatCompositeTasksOverlayBodyLines(state, contentWidth = DEFAULT_CONTENT_WIDTH5) {
5094
+ function formatCompositeTasksOverlayBodyLines(state, contentWidth = DEFAULT_CONTENT_WIDTH4) {
5330
5095
  const width = Math.max(1, Math.floor(contentWidth));
5331
5096
  const theme2 = clioTheme();
5332
5097
  const rows = selectableRows2(state);
@@ -5541,40 +5306,391 @@ var TasksOverlayBody = class {
5541
5306
  this.status = "";
5542
5307
  this.requestRender();
5543
5308
  }
5544
- mutateUserTask(status, id, mutation) {
5545
- if (!mutation) {
5546
- this.status = "Operator task actions are unavailable.";
5547
- this.requestRender();
5548
- return;
5549
- }
5550
- try {
5551
- mutation(id);
5552
- this.refreshUserTasks();
5553
- this.status = `Operator task ${id} ${status}.`;
5554
- } catch (error) {
5555
- this.status = error instanceof Error ? error.message : String(error);
5309
+ mutateUserTask(status, id, mutation) {
5310
+ if (!mutation) {
5311
+ this.status = "Operator task actions are unavailable.";
5312
+ this.requestRender();
5313
+ return;
5314
+ }
5315
+ try {
5316
+ mutation(id);
5317
+ this.refreshUserTasks();
5318
+ this.status = `Operator task ${id} ${status}.`;
5319
+ } catch (error) {
5320
+ this.status = error instanceof Error ? error.message : String(error);
5321
+ }
5322
+ this.requestRender();
5323
+ }
5324
+ requestRender() {
5325
+ this.options.requestRender?.();
5326
+ }
5327
+ };
5328
+ function openTasksOverlay(tui, getBoard, options = {}) {
5329
+ const body = new TasksOverlayBody(getBoard, options);
5330
+ const handle = showClioOverlayFrame(tui, body, {
5331
+ anchor: "center",
5332
+ width: TASKS_OVERLAY_WIDTH,
5333
+ markerId: "tasks",
5334
+ title: () => "Tasks",
5335
+ footerHint: () => body.footerHint()
5336
+ });
5337
+ const timer = setInterval(() => tui.requestRender(), REFRESH_MS2);
5338
+ timer.unref?.();
5339
+ return {
5340
+ ...handle,
5341
+ hide() {
5342
+ clearInterval(timer);
5343
+ handle.hide();
5344
+ }
5345
+ };
5346
+ }
5347
+
5348
+ // src/interactive/usage-overlay.ts
5349
+ init_esm_shims();
5350
+ var DEFAULT_CONTENT_WIDTH5 = 104;
5351
+ var USAGE_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH5 + 4;
5352
+ function formatTokens2(n) {
5353
+ return n.toLocaleString("en-US");
5354
+ }
5355
+ function aggregateCostEntries(entries) {
5356
+ const grouped = /* @__PURE__ */ new Map();
5357
+ for (const entry of entries) {
5358
+ const key = `${entry.providerId}::${entry.attributedModelId}`;
5359
+ const existing = grouped.get(key);
5360
+ if (existing) {
5361
+ existing.row.runs += 1;
5362
+ existing.row.tokens += entry.tokens;
5363
+ existing.row.input += entry.input;
5364
+ existing.row.output += entry.output;
5365
+ existing.row.cacheRead += entry.cacheRead;
5366
+ existing.row.cacheWrite += entry.cacheWrite;
5367
+ existing.row.reasoningTokens += entry.reasoningTokens;
5368
+ existing.row.apiCalls += entry.apiCalls ?? 1;
5369
+ if (entry.label === "side-question") existing.row.sideQuestions += 1;
5370
+ if (entry.label === "handoff") existing.row.handoffs += 1;
5371
+ if (entry.label === "prewarm") existing.row.prewarms += 1;
5372
+ if (entry.label === "background-memory") existing.row.backgroundMemory += 1;
5373
+ if (entry.label === "failed-compaction") existing.row.failedCompaction += 1;
5374
+ for (const requestedModelId of entry.requestedModelIds) existing.requestedModelIds.add(requestedModelId);
5375
+ addResponseModelIdObservationCounts(
5376
+ existing.responseModelIdObservationCounts,
5377
+ entry.responseModelIdObservationCounts
5378
+ );
5379
+ existing.entries.push(entry);
5380
+ continue;
5381
+ }
5382
+ grouped.set(key, {
5383
+ row: {
5384
+ providerId: entry.providerId,
5385
+ attributedModelId: entry.attributedModelId,
5386
+ runs: 1,
5387
+ tokens: entry.tokens,
5388
+ input: entry.input,
5389
+ output: entry.output,
5390
+ cacheRead: entry.cacheRead,
5391
+ cacheWrite: entry.cacheWrite,
5392
+ reasoningTokens: entry.reasoningTokens,
5393
+ apiCalls: entry.apiCalls ?? 1,
5394
+ sideQuestions: entry.label === "side-question" ? 1 : 0,
5395
+ handoffs: entry.label === "handoff" ? 1 : 0,
5396
+ prewarms: entry.label === "prewarm" ? 1 : 0,
5397
+ backgroundMemory: entry.label === "background-memory" ? 1 : 0,
5398
+ failedCompaction: entry.label === "failed-compaction" ? 1 : 0
5399
+ },
5400
+ requestedModelIds: new Set(entry.requestedModelIds),
5401
+ responseModelIdObservationCounts: { ...entry.responseModelIdObservationCounts },
5402
+ entries: [entry]
5403
+ });
5404
+ }
5405
+ const rows = Array.from(grouped.values(), ({ row: row2, entries: entries2, requestedModelIds, responseModelIdObservationCounts }) => ({
5406
+ ...row2,
5407
+ requestedModelIds: [...requestedModelIds].sort(),
5408
+ responseModelIdObservationCounts,
5409
+ cost: aggregateCostAmounts(entries2.map((entry) => ({ usd: entry.usd, provenance: entry.provenance })))
5410
+ }));
5411
+ rows.sort((a, b) => {
5412
+ if (a.providerId !== b.providerId) return a.providerId < b.providerId ? -1 : 1;
5413
+ if (a.attributedModelId !== b.attributedModelId) return a.attributedModelId < b.attributedModelId ? -1 : 1;
5414
+ return 0;
5415
+ });
5416
+ return rows;
5417
+ }
5418
+ function sumRows(rows) {
5419
+ return rows.reduce(
5420
+ (acc, row2) => ({
5421
+ runs: acc.runs + row2.runs,
5422
+ tokens: acc.tokens + row2.tokens,
5423
+ input: acc.input + row2.input,
5424
+ output: acc.output + row2.output,
5425
+ cacheRead: acc.cacheRead + row2.cacheRead,
5426
+ cacheWrite: acc.cacheWrite + row2.cacheWrite,
5427
+ reasoningTokens: acc.reasoningTokens + row2.reasoningTokens,
5428
+ apiCalls: acc.apiCalls + row2.apiCalls,
5429
+ sideQuestions: acc.sideQuestions + row2.sideQuestions,
5430
+ handoffs: acc.handoffs + row2.handoffs,
5431
+ prewarms: acc.prewarms + row2.prewarms,
5432
+ backgroundMemory: acc.backgroundMemory + row2.backgroundMemory,
5433
+ failedCompaction: acc.failedCompaction + row2.failedCompaction
5434
+ }),
5435
+ {
5436
+ runs: 0,
5437
+ tokens: 0,
5438
+ input: 0,
5439
+ output: 0,
5440
+ cacheRead: 0,
5441
+ cacheWrite: 0,
5442
+ reasoningTokens: 0,
5443
+ apiCalls: 0,
5444
+ sideQuestions: 0,
5445
+ handoffs: 0,
5446
+ prewarms: 0,
5447
+ backgroundMemory: 0,
5448
+ failedCompaction: 0
5449
+ }
5450
+ );
5451
+ }
5452
+ function cacheReadValue(cacheRead, apiCalls) {
5453
+ if (apiCalls > 1 && cacheRead > 0) {
5454
+ return [formatTokens2(cacheRead), `(avg/call ${formatTokens2(Math.round(cacheRead / apiCalls))})`];
5455
+ }
5456
+ return [formatTokens2(cacheRead)];
5457
+ }
5458
+ function reasoningValue(reasoningTokens) {
5459
+ return [formatTokens2(reasoningTokens), "provider-reported only"];
5460
+ }
5461
+ function kvBlock(entries) {
5462
+ const theme2 = clioTheme();
5463
+ const keyWidth = entries.reduce((max, [key]) => Math.max(max, key.length), 0);
5464
+ const valueWidth = entries.reduce((max, [, value]) => Math.max(max, value.length), 0);
5465
+ return entries.map(([key, value, annotation]) => {
5466
+ const row2 = `${theme2.fg("dim", key.padEnd(keyWidth))} ${theme2.fg("muted", value.padStart(valueWidth))}`;
5467
+ return annotation ? `${row2} ${theme2.fg("dim", annotation)}` : row2;
5468
+ });
5469
+ }
5470
+ function summaryBlock(totalCost, totalTokens, rows, promptCache) {
5471
+ const totals = sumRows(rows);
5472
+ const resolvedTotal = totalTokens > 0 ? totalTokens : totals.tokens;
5473
+ const cost = formatCostAggregate(totalCost);
5474
+ const cacheRows = promptCache !== null && hasPromptCacheTelemetry(promptCache) ? [
5475
+ [
5476
+ "uncached prefill",
5477
+ promptCache.uncachedPrefillTokens === null ? "not reported" : formatTokens2(promptCache.uncachedPrefillTokens)
5478
+ ],
5479
+ [
5480
+ "cache verdicts",
5481
+ `hot ${promptCache.verdictCounts.hot} \xB7 partial ${promptCache.verdictCounts.partial} \xB7 cold ${promptCache.verdictCounts.cold} \xB7 small ${promptCache.verdictCounts.small} \xB7 unknown ${promptCache.verdictCounts.unknown}`
5482
+ ]
5483
+ ] : [];
5484
+ return kvBlock([
5485
+ [
5486
+ "turns",
5487
+ formatTokens2(
5488
+ totals.runs - totals.sideQuestions - totals.handoffs - totals.prewarms - totals.backgroundMemory - totals.failedCompaction
5489
+ )
5490
+ ],
5491
+ ["model calls", formatTokens2(totals.apiCalls)],
5492
+ ...totals.sideQuestions > 0 ? [["side questions", formatTokens2(totals.sideQuestions)]] : [],
5493
+ ...totals.handoffs > 0 ? [["handoffs", formatTokens2(totals.handoffs)]] : [],
5494
+ ...totals.prewarms > 0 ? [["pre-warms", formatTokens2(totals.prewarms)]] : [],
5495
+ ...totals.backgroundMemory > 0 ? [["memory steps", formatTokens2(totals.backgroundMemory)]] : [],
5496
+ ...totals.failedCompaction > 0 ? [
5497
+ ["failed compaction calls", formatTokens2(totals.failedCompaction)],
5498
+ ["compaction usage", "known subtotals; missing usage is unknown"]
5499
+ ] : [],
5500
+ ...cost === null ? [] : [["cost", cost]],
5501
+ ["input", formatTokens2(totals.input)],
5502
+ ["output", formatTokens2(totals.output)],
5503
+ ["reasoning", ...reasoningValue(totals.reasoningTokens)],
5504
+ ["cache read", ...cacheReadValue(totals.cacheRead, totals.apiCalls)],
5505
+ ["cache write", formatTokens2(totals.cacheWrite)],
5506
+ ...cacheRows,
5507
+ ["processed", `${formatTokens2(resolvedTotal)} tokens`]
5508
+ ]);
5509
+ }
5510
+ function modelBlock(row2) {
5511
+ const cost = formatCostAggregate(row2.cost);
5512
+ return kvBlock([
5513
+ ["requested model ids", row2.requestedModelIds.join(", ")],
5514
+ ["response model id observation", responseModelIdObservationCountsLabel(row2.responseModelIdObservationCounts)],
5515
+ [
5516
+ "turns",
5517
+ formatTokens2(
5518
+ row2.runs - row2.sideQuestions - row2.handoffs - row2.prewarms - row2.backgroundMemory - row2.failedCompaction
5519
+ )
5520
+ ],
5521
+ ["model calls", formatTokens2(row2.apiCalls)],
5522
+ ...row2.sideQuestions > 0 ? [["side questions", formatTokens2(row2.sideQuestions)]] : [],
5523
+ ...row2.handoffs > 0 ? [["handoffs", formatTokens2(row2.handoffs)]] : [],
5524
+ ...row2.prewarms > 0 ? [["pre-warms", formatTokens2(row2.prewarms)]] : [],
5525
+ ...row2.backgroundMemory > 0 ? [["memory steps", formatTokens2(row2.backgroundMemory)]] : [],
5526
+ ...row2.failedCompaction > 0 ? [["failed compaction calls", formatTokens2(row2.failedCompaction)]] : [],
5527
+ ...cost === null ? [] : [["cost", cost]],
5528
+ ["input", formatTokens2(row2.input)],
5529
+ ["output", formatTokens2(row2.output)],
5530
+ ["reasoning", ...reasoningValue(row2.reasoningTokens)],
5531
+ ["cache read", ...cacheReadValue(row2.cacheRead, row2.apiCalls)],
5532
+ ["cache write", formatTokens2(row2.cacheWrite)],
5533
+ ["processed", `${formatTokens2(row2.tokens)} tokens`]
5534
+ ]);
5535
+ }
5536
+ function modelDetailLines(rows, contentWidth) {
5537
+ const theme2 = clioTheme();
5538
+ const lines = [rule(theme2, contentWidth)];
5539
+ if (rows.length === 0) {
5540
+ lines.push(theme2.fg("muted", "no token usage recorded for this session"));
5541
+ } else {
5542
+ for (const [index, row2] of rows.entries()) {
5543
+ if (index > 0) lines.push("");
5544
+ lines.push(theme2.style("accent", `${row2.providerId} \xB7 attributed model ${row2.attributedModelId}`, { bold: true }));
5545
+ for (const line of modelBlock(row2)) {
5546
+ lines.push(line);
5547
+ }
5548
+ }
5549
+ }
5550
+ return lines;
5551
+ }
5552
+ function buildCostSnapshot(observability, sessionId, snapshot, getSessionEntries) {
5553
+ const entries = observability.costEntries();
5554
+ const rows = aggregateCostEntries(entries);
5555
+ const totalTokens = rows.reduce((sum, r) => sum + r.tokens, 0);
5556
+ return {
5557
+ sessionId,
5558
+ totalCost: snapshot?.session.cost ?? observability.sessionCostSummary(),
5559
+ totalTokens,
5560
+ rows,
5561
+ promptCache: getSessionEntries ? foldPromptCacheTelemetry(getSessionEntries()) : null
5562
+ };
5563
+ }
5564
+ var USAGE_TABS = ["Accounts", "Session", "Models", "Workers"];
5565
+ var UsageOverlayBody = class {
5566
+ constructor(getSnapshot, getQuotaSnapshots, getDispatchRows, requestRender) {
5567
+ this.getSnapshot = getSnapshot;
5568
+ this.getQuotaSnapshots = getQuotaSnapshots;
5569
+ this.getDispatchRows = getDispatchRows;
5570
+ this.requestRender = requestRender;
5571
+ }
5572
+ getSnapshot;
5573
+ getQuotaSnapshots;
5574
+ getDispatchRows;
5575
+ requestRender;
5576
+ tab = 0;
5577
+ offsets = [0, 0, 0, 0];
5578
+ height = 20;
5579
+ pageHeight = 16;
5580
+ lineCount = 0;
5581
+ setHeight(rows) {
5582
+ this.height = Math.max(1, rows - 3);
5583
+ }
5584
+ handleInput(data) {
5585
+ const selected = ["1", "2", "3", "4"].findIndex((key) => matchesKey(data, key));
5586
+ if (selected >= 0) this.tab = selected;
5587
+ else if (matchesKey(data, "tab") || matchesKey(data, "right")) this.tab = (this.tab + 1) % USAGE_TABS.length;
5588
+ else if (matchesKey(data, "shift+tab") || matchesKey(data, "left"))
5589
+ this.tab = (this.tab + USAGE_TABS.length - 1) % USAGE_TABS.length;
5590
+ else {
5591
+ let offset = this.offsets[this.tab] ?? 0;
5592
+ if (matchesKey(data, "up")) offset--;
5593
+ else if (matchesKey(data, "down")) offset++;
5594
+ else if (matchesKey(data, "pageUp")) offset -= this.pageHeight;
5595
+ else if (matchesKey(data, "pageDown")) offset += this.pageHeight;
5596
+ else if (matchesKey(data, "home") || matchesKey(data, "ctrl+home")) offset = 0;
5597
+ else if (matchesKey(data, "end") || matchesKey(data, "ctrl+end")) offset = this.lineCount;
5598
+ else return;
5599
+ this.offsets[this.tab] = Math.max(0, Math.min(offset, this.lineCount - this.pageHeight));
5556
5600
  }
5557
5601
  this.requestRender();
5558
5602
  }
5559
- requestRender() {
5560
- this.options.requestRender?.();
5603
+ render(width) {
5604
+ const contentWidth = Math.max(1, Math.floor(width));
5605
+ const snapshot = this.getSnapshot();
5606
+ const quota = this.getQuotaSnapshots();
5607
+ const theme2 = clioTheme();
5608
+ const tabs = wrapTextWithAnsi(
5609
+ USAGE_TABS.map(
5610
+ (name, index) => index === this.tab ? theme2.style("accent", `${index + 1} ${name}`, { bold: true, underline: true }) : theme2.fg("dim", `${index + 1} ${name}`)
5611
+ ).join(" "),
5612
+ contentWidth
5613
+ );
5614
+ let body;
5615
+ if (this.tab === 0)
5616
+ body = [
5617
+ theme2.fg("dim", "Account-wide limits \xB7 filled = used \xB7 shared across sessions and devices"),
5618
+ "",
5619
+ ...renderQuotaAccounts(quota, contentWidth)
5620
+ ];
5621
+ else if (this.tab === 1)
5622
+ body = [
5623
+ theme2.style("accent", "Session tokens & cost", { bold: true }),
5624
+ theme2.fg("dim", "Recorded calls in this session \xB7 estimates are marked in the cost totals"),
5625
+ "",
5626
+ ...summaryBlock(snapshot.totalCost, snapshot.totalTokens, snapshot.rows, snapshot.promptCache),
5627
+ "",
5628
+ rule(theme2, contentWidth),
5629
+ theme2.fg("dim", "Session tokens cannot be converted to subscription percentages."),
5630
+ ...snapshot.rows.length ? [] : ["no token usage recorded for this session"]
5631
+ ];
5632
+ else if (this.tab === 2) {
5633
+ body = [
5634
+ theme2.style("accent", "Model activity \xB7 this session", { bold: true }),
5635
+ theme2.fg("dim", "Share of recorded processed tokens, including cache traffic; not account quota."),
5636
+ ""
5637
+ ];
5638
+ for (const row2 of [...snapshot.rows].sort((a, b) => b.tokens - a.tokens)) {
5639
+ const share = snapshot.totalTokens > 0 ? row2.tokens / snapshot.totalTokens * 100 : 0;
5640
+ body.push(
5641
+ `${row2.providerId} \xB7 ${row2.attributedModelId}`,
5642
+ `${quotaMeter(share, Math.min(24, contentWidth), "normal")} ${share.toFixed(1)}% \xB7 ${formatTokens2(row2.tokens)} tokens`
5643
+ );
5644
+ }
5645
+ body.push("", ...modelDetailLines(snapshot.rows, contentWidth));
5646
+ } else body = renderWorkerUsage(this.getDispatchRows(), quota, contentWidth);
5647
+ const lines = body.flatMap((line) => wrapTextWithAnsi(line, contentWidth));
5648
+ this.lineCount = lines.length;
5649
+ this.pageHeight = Math.max(1, this.height - tabs.length - 1);
5650
+ const offset = Math.max(0, Math.min(this.offsets[this.tab] ?? 0, lines.length - this.pageHeight));
5651
+ this.offsets[this.tab] = offset;
5652
+ const visible = [...tabs, "", ...lines.slice(offset, offset + this.pageHeight)];
5653
+ if (lines.length > this.pageHeight)
5654
+ visible.push(theme2.fg("dim", `${offset + 1}\u2013${Math.min(lines.length, offset + this.pageHeight)} / ${lines.length}`));
5655
+ return visible;
5656
+ }
5657
+ invalidate() {
5561
5658
  }
5562
5659
  };
5563
- function openTasksOverlay(tui, getBoard, options = {}) {
5564
- const body = new TasksOverlayBody(getBoard, options);
5660
+ function openUsageOverlay(tui, observability, options) {
5661
+ const sessionId = options?.sessionId ?? null;
5662
+ let latest = observability.snapshot();
5663
+ const body = new UsageOverlayBody(
5664
+ () => buildCostSnapshot(observability, sessionId, latest, options?.getSessionEntries),
5665
+ options?.getQuotaSnapshots ?? (() => []),
5666
+ options?.getDispatchRows ?? (() => []),
5667
+ () => tui.requestRender()
5668
+ );
5565
5669
  const handle = showClioOverlayFrame(tui, body, {
5566
5670
  anchor: "center",
5567
- width: TASKS_OVERLAY_WIDTH,
5568
- markerId: "tasks",
5569
- title: () => "Tasks",
5570
- footerHint: () => body.footerHint()
5671
+ width: USAGE_OVERLAY_WIDTH,
5672
+ // Not derived from the title: that one carries the session id.
5673
+ markerId: "usage",
5674
+ title: sessionId && sessionId.length > 0 ? `Usage (${sessionId})` : "Usage",
5675
+ footerHint: buildResponsiveHint([
5676
+ { key: "1\u20134 / Tab", verb: "view", critical: true },
5677
+ { key: "\u2191\u2193", verb: "scroll" },
5678
+ { key: "PgUp/PgDn", verb: "page" }
5679
+ ]),
5680
+ visible: (_width, height) => {
5681
+ body.setHeight(height);
5682
+ return true;
5683
+ }
5684
+ });
5685
+ const unsubscribe = observability.subscribe((snapshot) => {
5686
+ latest = snapshot;
5687
+ body.invalidate();
5688
+ tui.requestRender();
5571
5689
  });
5572
- const timer = setInterval(() => tui.requestRender(), REFRESH_MS2);
5573
- timer.unref?.();
5574
5690
  return {
5575
5691
  ...handle,
5576
5692
  hide() {
5577
- clearInterval(timer);
5693
+ unsubscribe();
5578
5694
  handle.hide();
5579
5695
  }
5580
5696
  };
@@ -5582,7 +5698,7 @@ function openTasksOverlay(tui, getBoard, options = {}) {
5582
5698
 
5583
5699
  // src/interactive/overlay-general-openers.ts
5584
5700
  function createOverlayGeneralOpeners(deps) {
5585
- const openCostOverlayFactory = deps.openCostOverlay ?? openCostOverlay;
5701
+ const openUsageOverlayFactory = deps.openUsageOverlay ?? openUsageOverlay;
5586
5702
  const openContextOverlayFactory = deps.openContextOverlay ?? openContextOverlay;
5587
5703
  const openContextResetOverlayFactory = deps.openContextResetOverlay ?? openContextResetOverlay;
5588
5704
  const openTasksOverlayFactory = deps.openTasksOverlay ?? openTasksOverlay;
@@ -5591,11 +5707,13 @@ function createOverlayGeneralOpeners(deps) {
5591
5707
  const openViewOverlayFactory = deps.openViewOverlay ?? openViewOverlay;
5592
5708
  const showOverlayFrameFactory = deps.showOverlayFrame ?? showClioOverlayFrame;
5593
5709
  const openSideQuestionOverlayFactory = deps.openSideQuestionOverlay ?? openSideQuestionOverlay;
5594
- const openCost = () => {
5710
+ const openUsage = () => {
5595
5711
  if (deps.transitions.state !== "closed") return;
5596
- deps.transitions.state = "cost";
5597
- deps.transitions.handle = openCostOverlayFactory(deps.tui, deps.observability, {
5712
+ deps.transitions.state = "usage";
5713
+ deps.transitions.handle = openUsageOverlayFactory(deps.tui, deps.observability, {
5598
5714
  sessionId: deps.getSessionId?.() ?? null,
5715
+ ...deps.getQuotaSnapshots ? { getQuotaSnapshots: deps.getQuotaSnapshots } : {},
5716
+ ...deps.getDispatchRows ? { getDispatchRows: deps.getDispatchRows } : {},
5599
5717
  ...deps.readSessionEntries ? {
5600
5718
  getSessionEntries: () => deps.readSessionEntries?.() ?? []
5601
5719
  } : {}
@@ -5805,16 +5923,16 @@ function createOverlayGeneralOpeners(deps) {
5805
5923
  });
5806
5924
  deps.transitions.handle = session;
5807
5925
  deps.requestRender();
5808
- void ask(question, { signal: controller.signal, onDelta: (partialText) => session.setAnswer(partialText) }).then((outcome) => {
5809
- if (outcome.status === "answered") {
5810
- session.settle({ kind: "answered", text: outcome.text });
5926
+ void ask(question, { signal: controller.signal, onDelta: (partialText) => session.setAnswer(partialText) }).then((outcome2) => {
5927
+ if (outcome2.status === "answered") {
5928
+ session.settle({ kind: "answered", text: outcome2.text });
5811
5929
  return;
5812
5930
  }
5813
- if (outcome.status === "aborted") {
5814
- session.settle({ kind: "aborted", text: outcome.text });
5931
+ if (outcome2.status === "aborted") {
5932
+ session.settle({ kind: "aborted", text: outcome2.text });
5815
5933
  return;
5816
5934
  }
5817
- session.settle({ kind: "error", reason: outcome.reason });
5935
+ session.settle({ kind: "error", reason: outcome2.reason });
5818
5936
  }).catch((error) => {
5819
5937
  session.settle({ kind: "error", reason: error instanceof Error ? error.message : String(error) });
5820
5938
  });
@@ -5905,13 +6023,13 @@ function createOverlayGeneralOpeners(deps) {
5905
6023
  },
5906
6024
  onNotice: (text) => deps.stderr(`[fleet ${preview.name}] ${text}
5907
6025
  `)
5908
- }).then((outcome) => {
6026
+ }).then((outcome2) => {
5909
6027
  deps.notify(
5910
- outcome.cleanRun ? "success" : "warning",
6028
+ outcome2.cleanRun ? "success" : "warning",
5911
6029
  // `totalCost` rather than the bare `totalCostUsd` sum: a run whose
5912
6030
  // receipts were all unpriced reads as not measured instead of as a
5913
6031
  // measured $0.0000 nobody put a number on.
5914
- `fleet ${preview.name}: ${outcome.succeededStepCount}/${outcome.requiredStepCount} steps succeeded, cost ${renderCostAggregate(outcome.totalCost)}`,
6032
+ `fleet ${preview.name}: ${outcome2.succeededStepCount}/${outcome2.requiredStepCount} steps succeeded, cost ${renderCostAggregate(outcome2.totalCost)}`,
5915
6033
  "fleet-run:settled"
5916
6034
  );
5917
6035
  }).catch((error) => {
@@ -5923,7 +6041,7 @@ function createOverlayGeneralOpeners(deps) {
5923
6041
  });
5924
6042
  };
5925
6043
  return {
5926
- openCost,
6044
+ openUsage,
5927
6045
  openContextView,
5928
6046
  openContextReset,
5929
6047
  toggleFooter,
@@ -7033,13 +7151,13 @@ function editNumberSubmenu(title, id) {
7033
7151
  numberSettingNote(rule2)
7034
7152
  );
7035
7153
  input.onSubmit = (val) => {
7036
- const outcome = parseNumberSetting(val, rule2);
7037
- if ("refusal" in outcome) {
7038
- wrapper.setProblem(outcome.refusal);
7154
+ const outcome2 = parseNumberSetting(val, rule2);
7155
+ if ("refusal" in outcome2) {
7156
+ wrapper.setProblem(outcome2.refusal);
7039
7157
  return;
7040
7158
  }
7041
7159
  wrapper.setProblem(null);
7042
- done(outcome.value === null ? "" : val.trim());
7160
+ done(outcome2.value === null ? "" : val.trim());
7043
7161
  };
7044
7162
  input.onEscape = () => done();
7045
7163
  return wrapper;
@@ -7067,13 +7185,13 @@ function editEnumListSubmenu(title, legal) {
7067
7185
  `Comma-separated, from ${legal.join(", ")}; blank activates none.`
7068
7186
  );
7069
7187
  input.onSubmit = (val) => {
7070
- const outcome = parseEnumListSetting(val, legal);
7071
- if ("refusal" in outcome) {
7072
- wrapper.setProblem(outcome.refusal);
7188
+ const outcome2 = parseEnumListSetting(val, legal);
7189
+ if ("refusal" in outcome2) {
7190
+ wrapper.setProblem(outcome2.refusal);
7073
7191
  return;
7074
7192
  }
7075
7193
  wrapper.setProblem(null);
7076
- done(outcome.values.join(", "));
7194
+ done(outcome2.values.join(", "));
7077
7195
  };
7078
7196
  input.onEscape = () => done();
7079
7197
  return wrapper;
@@ -7289,12 +7407,12 @@ function scopedModelsSubmenu(selectedRefs, providers) {
7289
7407
  "Target entries cycle by target, using its default model when switching targets. Model entries pin one exact target/model reference."
7290
7408
  );
7291
7409
  }
7292
- function chainSubmenus(first, second, join3) {
7410
+ function chainSubmenus(first, second, join6) {
7293
7411
  return (_currentValue, done) => {
7294
7412
  let active = first("", (head) => {
7295
7413
  const trimmed = head?.trim() ?? "";
7296
7414
  if (!trimmed) return done();
7297
- active = second(trimmed)("", (tail) => done(tail?.trim() ? join3(trimmed, tail.trim()) : void 0));
7415
+ active = second(trimmed)("", (tail) => done(tail?.trim() ? join6(trimmed, tail.trim()) : void 0));
7298
7416
  });
7299
7417
  return {
7300
7418
  render: (width) => active.render(width),
@@ -8277,9 +8395,9 @@ function applyNonNegativeInteger(value, set) {
8277
8395
  if (Number.isFinite(parsed) && parsed >= 0) set(Math.floor(parsed));
8278
8396
  }
8279
8397
  function applyNumberSetting(id, value, set) {
8280
- const outcome = parseNumberSetting(value, NUMBER_SETTING_RULES[id]);
8281
- if ("refusal" in outcome) return;
8282
- set(outcome.value);
8398
+ const outcome2 = parseNumberSetting(value, NUMBER_SETTING_RULES[id]);
8399
+ if ("refusal" in outcome2) return;
8400
+ set(outcome2.value);
8283
8401
  }
8284
8402
  function applyDelegationAgentChange(settings, value) {
8285
8403
  if (value.startsWith(DELEGATION_REMOVE_PREFIX)) {
@@ -8567,13 +8685,13 @@ function applySettingChange(settings, id, value) {
8567
8685
  });
8568
8686
  return;
8569
8687
  case "routing.activeRoles": {
8570
- const outcome = parseEnumListSetting(value, ACTIVE_ROUTING_ROLES);
8571
- if ("values" in outcome) settings.fleet.adaptiveRouting.roles = outcome.values;
8688
+ const outcome2 = parseEnumListSetting(value, ACTIVE_ROUTING_ROLES);
8689
+ if ("values" in outcome2) settings.fleet.adaptiveRouting.roles = outcome2.values;
8572
8690
  return;
8573
8691
  }
8574
8692
  case "routing.activePostures": {
8575
- const outcome = parseEnumListSetting(value, ACTIVE_ROUTING_POSTURES);
8576
- if ("values" in outcome) settings.fleet.adaptiveRouting.postures = outcome.values;
8693
+ const outcome2 = parseEnumListSetting(value, ACTIVE_ROUTING_POSTURES);
8694
+ if ("values" in outcome2) settings.fleet.adaptiveRouting.postures = outcome2.values;
8577
8695
  return;
8578
8696
  }
8579
8697
  case "prewarm.enabled":
@@ -8692,12 +8810,12 @@ function applyEntrySettingChange(settings, id, value) {
8692
8810
  }
8693
8811
  function changedLeafPaths(before, after) {
8694
8812
  const out = [];
8695
- const isRecord = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
8813
+ const isRecord6 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
8696
8814
  const visit = (a, b, path) => {
8697
8815
  if (a === b) return;
8698
8816
  const profileLeaf = path.startsWith("fleet.profiles.") && path.split(".").length === 3;
8699
8817
  const collectionLeaf = path === "interface.keybindings" || path === "fleet.rosters";
8700
- if (!isRecord(a) || !isRecord(b) || profileLeaf || collectionLeaf) {
8818
+ if (!isRecord6(a) || !isRecord6(b) || profileLeaf || collectionLeaf) {
8701
8819
  if (JSON.stringify(a) !== JSON.stringify(b)) out.push(path);
8702
8820
  return;
8703
8821
  }
@@ -11435,28 +11553,28 @@ function evidenceLabel(evidence, refresh) {
11435
11553
  return "resource evidence from before refresh; current session admission is unverified because refresh failed";
11436
11554
  return "resource evidence from before refresh; no session refresh applied";
11437
11555
  }
11438
- function outcomeLines(outcome, width, refresh) {
11556
+ function outcomeLines(outcome2, width, refresh) {
11439
11557
  const theme2 = clioTheme();
11440
- const token = outcome.status === "committed" ? "success" : outcome.status === "failed" ? "error" : "dim";
11558
+ const token = outcome2.status === "committed" ? "success" : outcome2.status === "failed" ? "error" : "dim";
11441
11559
  const rows = wrap(
11442
- theme2.fg(token, `${outcome.status}: ${outcome.operation} ${outcome.identity.ref} (${outcome.identity.scope})`),
11560
+ theme2.fg(token, `${outcome2.status}: ${outcome2.operation} ${outcome2.identity.ref} (${outcome2.identity.scope})`),
11443
11561
  width
11444
11562
  );
11445
- if (outcome.error) {
11446
- rows.push(...wrap(theme2.fg("error", `${outcome.error.code}: ${outcome.error.message}`), width));
11447
- if (outcome.error.changed)
11563
+ if (outcome2.error) {
11564
+ rows.push(...wrap(theme2.fg("error", `${outcome2.error.code}: ${outcome2.error.message}`), width));
11565
+ if (outcome2.error.changed)
11448
11566
  rows.push(
11449
11567
  ...wrap(
11450
11568
  theme2.fg(
11451
11569
  "dim",
11452
- `changed fact ${outcome.error.changed.fact}: reviewed ${JSON.stringify(outcome.error.changed.expected)}, observed ${JSON.stringify(outcome.error.changed.observed)}`
11570
+ `changed fact ${outcome2.error.changed.fact}: reviewed ${JSON.stringify(outcome2.error.changed.expected)}, observed ${JSON.stringify(outcome2.error.changed.observed)}`
11453
11571
  ),
11454
11572
  width
11455
11573
  )
11456
11574
  );
11457
- rows.push(...wrap(theme2.fg("info", outcome.error.next), width));
11575
+ rows.push(...wrap(theme2.fg("info", outcome2.error.next), width));
11458
11576
  }
11459
- const verification = outcome.verification;
11577
+ const verification = outcome2.verification;
11460
11578
  if (verification) {
11461
11579
  rows.push(...wrap(theme2.fg("dim", `disk: tree ${verification.tree}, record ${verification.record}`), width));
11462
11580
  rows.push(
@@ -11480,11 +11598,11 @@ function outcomeLines(outcome, width, refresh) {
11480
11598
  )
11481
11599
  );
11482
11600
  }
11483
- if (outcome.recovery?.packageBackup)
11484
- rows.push(...wrap(theme2.fg("info", `recovered content: ${outcome.recovery.packageBackup}`), width));
11485
- if (outcome.recovery?.stateBackup)
11486
- rows.push(...wrap(theme2.fg("dim", `state backup: ${outcome.recovery.stateBackup}`), width));
11487
- for (const diagnostic of outcome.diagnostics) rows.push(...wrap(theme2.fg("dim", diagnostic), width));
11601
+ if (outcome2.recovery?.packageBackup)
11602
+ rows.push(...wrap(theme2.fg("info", `recovered content: ${outcome2.recovery.packageBackup}`), width));
11603
+ if (outcome2.recovery?.stateBackup)
11604
+ rows.push(...wrap(theme2.fg("dim", `state backup: ${outcome2.recovery.stateBackup}`), width));
11605
+ for (const diagnostic of outcome2.diagnostics) rows.push(...wrap(theme2.fg("dim", diagnostic), width));
11488
11606
  return rows;
11489
11607
  }
11490
11608
  function formatLibraryOutcome(result, width) {
@@ -11497,7 +11615,7 @@ function formatLibraryOutcome(result, width) {
11497
11615
  width
11498
11616
  );
11499
11617
  rows.push(rule(theme2, width));
11500
- for (const outcome of result.outcomes) rows.push(...outcomeLines(outcome, width, result.refresh));
11618
+ for (const outcome2 of result.outcomes) rows.push(...outcomeLines(outcome2, width, result.refresh));
11501
11619
  rows.push(rule(theme2, width));
11502
11620
  rows.push(...wrap(refreshLine(result.refresh), width));
11503
11621
  if (result.unattempted > 0)
@@ -11533,7 +11651,7 @@ function formatLibraryImportReview(plan, width, options = {}) {
11533
11651
  ...wrap(
11534
11652
  theme2.fg(
11535
11653
  "info",
11536
- `projects ${plan.outcomes.length} resource${plan.outcomes.length === 1 ? "" : "s"}${plan.outcomes.length > 0 ? `: ${plan.outcomes.map((outcome) => `${outcome.kind}:${outcome.name} (${outcome.status})`).join(", ")}` : ""}`
11654
+ `projects ${plan.outcomes.length} resource${plan.outcomes.length === 1 ? "" : "s"}${plan.outcomes.length > 0 ? `: ${plan.outcomes.map((outcome2) => `${outcome2.kind}:${outcome2.name} (${outcome2.status})`).join(", ")}` : ""}`
11537
11655
  ),
11538
11656
  width
11539
11657
  )
@@ -11631,8 +11749,8 @@ function openReviewOverlay(tui, spec) {
11631
11749
  if (matchesKey(data, "enter")) {
11632
11750
  if (!spec.applicable) return;
11633
11751
  settled = true;
11634
- const outcome = spec.commit();
11635
- body.outcome = () => outcome(width);
11752
+ const outcome2 = spec.commit();
11753
+ body.outcome = () => outcome2(width);
11636
11754
  body.reset();
11637
11755
  tui.requestRender();
11638
11756
  return;
@@ -11935,15 +12053,15 @@ function openLibraryOverlay(tui, deps) {
11935
12053
  columns: columns(),
11936
12054
  commit: (accepted) => (deps.applyImport ?? applyLibraryImport)(accepted, true),
11937
12055
  outcome: (accepted, result) => {
11938
- const outcome = libraryImportOutcome(accepted, result);
11939
- const committed = outcome.status === "committed";
12056
+ const outcome2 = libraryImportOutcome(accepted, result);
12057
+ const committed = outcome2.status === "committed";
11940
12058
  return {
11941
12059
  planId: accepted.reviewFingerprint,
11942
12060
  dryRun: false,
11943
- outcomes: [outcome],
12061
+ outcomes: [outcome2],
11944
12062
  committed: committed ? 1 : 0,
11945
- failed: outcome.status === "failed" ? 1 : 0,
11946
- unattempted: outcome.status === "unattempted" ? 1 : 0,
12063
+ failed: outcome2.status === "failed" ? 1 : 0,
12064
+ unattempted: outcome2.status === "unattempted" ? 1 : 0,
11947
12065
  refresh: committed ? deps.lifecycle.retryRefresh(cwd) : { status: "not-applicable", reason: "nothing was imported" }
11948
12066
  };
11949
12067
  },
@@ -13456,7 +13574,7 @@ function createOverlaySessionLifecycle(deps) {
13456
13574
  try {
13457
13575
  const turns = deps.readStructuredEntries(sessionId);
13458
13576
  const leafTurnId = session.tree(sessionId).leafId;
13459
- const replayOptions = leafTurnId ? { activeLeafTurnId: leafTurnId } : {};
13577
+ const replayOptions = withContinuityReplay(turns, leafTurnId ? { activeLeafTurnId: leafTurnId } : {}, session);
13460
13578
  deps.resetTranscript();
13461
13579
  rehydrateChatPanelFromTurns(deps.chatPanel, turns, replayOptions);
13462
13580
  const replayMessages = buildModelReplayAgentMessagesFromTurns(turns, replayOptions);
@@ -13508,8 +13626,9 @@ function createOverlaySessionLifecycle(deps) {
13508
13626
  if (!sessionId) throw new Error("no current session after turn switch");
13509
13627
  const turns = deps.readStructuredEntries(sessionId);
13510
13628
  deps.resetTranscript();
13511
- rehydrateChatPanelFromTurns(deps.chatPanel, turns, { uptoTurnId: turnId });
13512
- const replayMessages = buildModelReplayAgentMessagesFromTurns(turns, { uptoTurnId: turnId });
13629
+ const replayOptions = withContinuityReplay(turns, { uptoTurnId: turnId }, session);
13630
+ rehydrateChatPanelFromTurns(deps.chatPanel, turns, replayOptions);
13631
+ const replayMessages = buildModelReplayAgentMessagesFromTurns(turns, replayOptions);
13513
13632
  deps.chat.resetForSession(turnId, replayMessages);
13514
13633
  rescopeToBranch(session, turns, turnId);
13515
13634
  } catch (error) {
@@ -13580,12 +13699,10 @@ function createOverlaySessionLifecycle(deps) {
13580
13699
  function replayFork(forkedSessionId, parentTurnId, session) {
13581
13700
  try {
13582
13701
  const turns = deps.readStructuredEntries(forkedSessionId);
13583
- rehydrateChatPanelFromTurns(deps.chatPanel, turns);
13584
13702
  const leafTurnId = session.tree(forkedSessionId).leafId ?? parentTurnId;
13585
- const replayMessages = buildModelReplayAgentMessagesFromTurns(
13586
- turns,
13587
- leafTurnId ? { activeLeafTurnId: leafTurnId } : {}
13588
- );
13703
+ const replayOptions = withContinuityReplay(turns, leafTurnId ? { activeLeafTurnId: leafTurnId } : {}, session);
13704
+ rehydrateChatPanelFromTurns(deps.chatPanel, turns, replayOptions);
13705
+ const replayMessages = buildModelReplayAgentMessagesFromTurns(turns, replayOptions);
13589
13706
  deps.chat.resetForSession(leafTurnId, replayMessages);
13590
13707
  rescopeToBranch(session, turns, leafTurnId);
13591
13708
  } catch (error) {
@@ -13808,8 +13925,9 @@ function createOverlaySessionLifecycle(deps) {
13808
13925
  try {
13809
13926
  const turns = deps.readStructuredEntries(toSessionId);
13810
13927
  deps.resetTranscript();
13811
- rehydrateChatPanelFromTurns(deps.chatPanel, turns);
13812
- deps.chat.resetForSession(null, buildModelReplayAgentMessagesFromTurns(turns));
13928
+ const replayOptions = withContinuityReplay(turns, {}, session);
13929
+ rehydrateChatPanelFromTurns(deps.chatPanel, turns, replayOptions);
13930
+ deps.chat.resetForSession(null, buildModelReplayAgentMessagesFromTurns(turns, replayOptions));
13813
13931
  rescopeToBranch(session, turns, null);
13814
13932
  } catch (error) {
13815
13933
  deps.stderr(`[/handoff] seeding replay failed: ${error instanceof Error ? error.message : String(error)}
@@ -13969,7 +14087,7 @@ function routeAuthOverlayKey(data, deps) {
13969
14087
  }
13970
14088
  return false;
13971
14089
  }
13972
- function routeCostOverlayKey(data, deps) {
14090
+ function routeReadOnlyOverlayKey(data, deps) {
13973
14091
  if (isEscapeKey(data)) {
13974
14092
  deps.closeOverlay();
13975
14093
  return true;
@@ -13999,16 +14117,13 @@ function routeOverlayKey(data, overlayState, deps, matches) {
13999
14117
  return true;
14000
14118
  }
14001
14119
  if (overlayState === "auth") return routeAuthOverlayKey(data, deps);
14002
- if (overlayState === "cost") {
14003
- routeCostOverlayKey(data, deps);
14004
- return true;
14005
- }
14120
+ if (overlayState === "usage") return routeReadOnlyOverlayKey(data, deps);
14006
14121
  if (overlayState === "context-view") {
14007
- routeCostOverlayKey(data, deps);
14122
+ routeReadOnlyOverlayKey(data, deps);
14008
14123
  return true;
14009
14124
  }
14010
14125
  if (overlayState === "side-question") {
14011
- routeCostOverlayKey(data, deps);
14126
+ routeReadOnlyOverlayKey(data, deps);
14012
14127
  return true;
14013
14128
  }
14014
14129
  if (overlayState === "handoff-review") return false;
@@ -14072,7 +14187,7 @@ function createOverlayLifecycle(deps) {
14072
14187
  openTreeOverlay: openTreeOverlayFactory,
14073
14188
  openMessagePickerOverlay: openMessagePickerOverlayFactory,
14074
14189
  openCwdFallbackOverlay: openCwdFallbackOverlayFactory,
14075
- openCostOverlay: openCostOverlayFactory,
14190
+ openUsageOverlay: openUsageOverlayFactory,
14076
14191
  openContextOverlay: openContextOverlayFactory,
14077
14192
  openContextResetOverlay: openContextResetOverlayFactory,
14078
14193
  openTasksOverlay: openTasksOverlayFactory,
@@ -14264,6 +14379,8 @@ function createOverlayLifecycle(deps) {
14264
14379
  tui,
14265
14380
  transitions: overlayTransitions,
14266
14381
  observability: deps.app.observability,
14382
+ ...deps.getQuotaSnapshots ? { getQuotaSnapshots: deps.getQuotaSnapshots } : {},
14383
+ ...deps.getDispatchRows ? { getDispatchRows: deps.getDispatchRows } : {},
14267
14384
  ...deps.app.getSessionId ? { getSessionId: deps.app.getSessionId } : {},
14268
14385
  getContextLedger: () => deps.app.chat.contextLedger(),
14269
14386
  contextChat: deps.app.chat,
@@ -14292,7 +14409,7 @@ function createOverlayLifecycle(deps) {
14292
14409
  startDispatchBoardTicker: () => interactiveTickers.startDispatchBoardTicker(),
14293
14410
  closeOverlay,
14294
14411
  showOverlayFrame,
14295
- ...openCostOverlayFactory ? { openCostOverlay: openCostOverlayFactory } : {},
14412
+ ...openUsageOverlayFactory ? { openUsageOverlay: openUsageOverlayFactory } : {},
14296
14413
  ...openContextOverlayFactory ? { openContextOverlay: openContextOverlayFactory } : {},
14297
14414
  ...openContextResetOverlayFactory ? { openContextResetOverlay: openContextResetOverlayFactory } : {},
14298
14415
  ...openTasksOverlayFactory ? { openTasksOverlay: openTasksOverlayFactory } : {},
@@ -14308,7 +14425,7 @@ function createOverlayLifecycle(deps) {
14308
14425
  const openResumeOverlayState = overlaySessions.openResume;
14309
14426
  const openTreeOverlayState = overlaySessions.openTree;
14310
14427
  const openMessagePickerOverlayState = overlaySessions.openMessagePicker;
14311
- const openCostOverlayState = overlayGeneralOpeners.openCost;
14428
+ const openUsageOverlayState = overlayGeneralOpeners.openUsage;
14312
14429
  const openContextViewOverlayState = overlayGeneralOpeners.openContextView;
14313
14430
  const openContextResetOverlayState = overlayGeneralOpeners.openContextReset;
14314
14431
  const toggleFooterDashboardState = overlayGeneralOpeners.toggleFooter;
@@ -14329,7 +14446,7 @@ function createOverlayLifecycle(deps) {
14329
14446
  isAskUserWaiting: overlayAskUser.isWaiting,
14330
14447
  resetAskUserCancellation: overlayAskUser.resetCancellation,
14331
14448
  refreshSettingsOverlay: overlayModelSelectors.refreshSettingsOverlay,
14332
- openCostOverlayState,
14449
+ openUsageOverlayState,
14333
14450
  openContextViewOverlayState,
14334
14451
  openContextResetOverlayState,
14335
14452
  toggleFooterDashboardState,
@@ -15553,10 +15670,10 @@ function createInteractiveEventProjection(deps) {
15553
15670
  deps.bus.on(BusChannels.ContextPruned, (payload) => {
15554
15671
  const event = payload;
15555
15672
  if (event && typeof event === "object" && typeof event.tokensBefore === "number" && typeof event.tokensAfter === "number") {
15556
- const outcome = event.tokensAfter < event.tokensBefore ? "Reclaimed context" : "Context updated";
15673
+ const outcome2 = event.tokensAfter < event.tokensBefore ? "Reclaimed context" : "Context updated";
15557
15674
  deps.notify(
15558
15675
  "info",
15559
- `[Compaction] ${outcome}: ${event.tokensBefore} -> ${event.tokensAfter} tokens (${event.stage})`,
15676
+ `[Compaction] ${outcome2}: ${event.tokensBefore} -> ${event.tokensAfter} tokens (${event.stage})`,
15560
15677
  "compaction-notice"
15561
15678
  );
15562
15679
  }
@@ -15960,7 +16077,7 @@ function createInteractiveInputRuntime(deps) {
15960
16077
  },
15961
16078
  cancelFocusedOwner: () => {
15962
16079
  const state = deps.overlay.getState();
15963
- if (["permission-confirm", "cost", "context-view", "side-question", "dispatch-board", "auth"].includes(state))
16080
+ if (["permission-confirm", "usage", "context-view", "side-question", "dispatch-board", "auth"].includes(state))
15964
16081
  deps.overlay.closeOverlay();
15965
16082
  else if (focused()?.handleInput) {
15966
16083
  focused()?.handleInput?.("\x1B");
@@ -16078,89 +16195,1041 @@ function createInteractiveInputRuntime(deps) {
16078
16195
  deps.onShutdownArmedChange?.(armed);
16079
16196
  deps.refreshFooter();
16080
16197
  },
16081
- closeOverlay: () => deps.overlay.closeOverlay(),
16082
- listNotifications: () => deps.notifications.list(),
16083
- dismissNotification: (id) => deps.notifications.dismiss(id),
16084
- dismissAllNotifications: () => deps.notifications.dismissAll(),
16085
- shutdownDisposers: [
16086
- () => deps.shutdown.stopTickers(),
16087
- () => leaderKeys.dispose(),
16088
- () => removeReload?.(),
16089
- () => deps.shutdown.disposeInteractiveTickers(),
16090
- () => deps.shutdown.disposeBeforeStatus(),
16091
- () => deps.shutdown.disposeProjectionPrimary(),
16092
- () => deps.shutdown.disposeStatus(),
16093
- () => deps.shutdown.disposeProjectionRemaining(),
16094
- () => deps.shutdown.disposeOverlay(),
16095
- () => deps.shutdown.stopAgentProgress(),
16096
- () => deps.shutdown.disposeChat(),
16097
- () => deps.shutdown.disposeSubscriptions()
16098
- ],
16099
- stopUi: deps.stopUi,
16100
- ...deps.beforeStopUi ? { beforeStopUi: deps.beforeStopUi } : {},
16101
- cancelParkedCalls: deps.cancelParkedCalls,
16102
- onShutdown: deps.onShutdown,
16103
- registerTerminalTeardown: deps.registerTerminalTeardown ?? ((teardown) => getTerminationCoordinator().onDrain(teardown)),
16104
- // stderr for the same reason src/core/termination.ts uses it for a failed
16105
- // hook: by the time teardown fails there is no UI left to carry a notice.
16106
- reportShutdownFailure: deps.reportShutdownFailure ?? ((step, error) => {
16107
- process.stderr.write(
16108
- `[clio-coder:interactive] ${step} failed: ${error instanceof Error ? error.message : String(error)}
16109
- `
16110
- );
16111
- })
16198
+ closeOverlay: () => deps.overlay.closeOverlay(),
16199
+ listNotifications: () => deps.notifications.list(),
16200
+ dismissNotification: (id) => deps.notifications.dismiss(id),
16201
+ dismissAllNotifications: () => deps.notifications.dismissAll(),
16202
+ shutdownDisposers: [
16203
+ () => deps.shutdown.stopTickers(),
16204
+ () => leaderKeys.dispose(),
16205
+ () => removeReload?.(),
16206
+ () => deps.shutdown.disposeInteractiveTickers(),
16207
+ () => deps.shutdown.disposeBeforeStatus(),
16208
+ () => deps.shutdown.disposeProjectionPrimary(),
16209
+ () => deps.shutdown.disposeStatus(),
16210
+ () => deps.shutdown.disposeProjectionRemaining(),
16211
+ () => deps.shutdown.disposeOverlay(),
16212
+ () => deps.shutdown.stopAgentProgress(),
16213
+ () => deps.shutdown.disposeChat(),
16214
+ () => deps.shutdown.disposeSubscriptions()
16215
+ ],
16216
+ stopUi: deps.stopUi,
16217
+ ...deps.beforeStopUi ? { beforeStopUi: deps.beforeStopUi } : {},
16218
+ cancelParkedCalls: deps.cancelParkedCalls,
16219
+ onShutdown: deps.onShutdown,
16220
+ registerTerminalTeardown: deps.registerTerminalTeardown ?? ((teardown) => getTerminationCoordinator().onDrain(teardown)),
16221
+ // stderr for the same reason src/core/termination.ts uses it for a failed
16222
+ // hook: by the time teardown fails there is no UI left to carry a notice.
16223
+ reportShutdownFailure: deps.reportShutdownFailure ?? ((step, error) => {
16224
+ process.stderr.write(
16225
+ `[clio-coder:interactive] ${step} failed: ${error instanceof Error ? error.message : String(error)}
16226
+ `
16227
+ );
16228
+ })
16229
+ });
16230
+ deps.registerInputListener((data) => {
16231
+ deps.onInputIngress?.(classifyInputAction(data, deps), data);
16232
+ return controller.handleInput(data);
16233
+ });
16234
+ return controller;
16235
+ }
16236
+ function classifyInputAction(data, deps) {
16237
+ if (data.length === 0 || isKeyRelease(data)) return "no-visual-change";
16238
+ if (deps.keybindings.matches(data, "tui.input.submit")) return "submit";
16239
+ const sgrMousePrefix = `${String.fromCharCode(27)}[<`;
16240
+ if (data.startsWith(`${sgrMousePrefix}64;`) || data.startsWith(`${sgrMousePrefix}65;`) || deps.keybindings.matches(data, "tui.altScreen.pageUp") || deps.keybindings.matches(data, "tui.altScreen.pageDown") || deps.keybindings.matches(data, "tui.editor.pageUp") || deps.keybindings.matches(data, "tui.editor.pageDown")) {
16241
+ return "scroll";
16242
+ }
16243
+ if (deps.overlay.getState() !== "closed") return "overlay";
16244
+ return "editor";
16245
+ }
16246
+ function overlayScopeLabel(state) {
16247
+ const names = {
16248
+ closed: "Composer",
16249
+ "permission-confirm": "Permission",
16250
+ "dispatch-board": "Workers",
16251
+ auth: "Authentication",
16252
+ usage: "Usage",
16253
+ "context-view": "Context",
16254
+ "context-reset": "Context reset",
16255
+ tasks: "Tasks",
16256
+ decisions: "Decisions",
16257
+ memory: "Memory",
16258
+ view: "View",
16259
+ model: "Model",
16260
+ "model-scope": "Model cycle set",
16261
+ settings: "Settings",
16262
+ resume: "Resume",
16263
+ tree: "Session tree",
16264
+ "message-picker": "Messages",
16265
+ "cwd-fallback": "Working directory",
16266
+ "ask-user": "Interview",
16267
+ help: "Help",
16268
+ extensions: "Extensions",
16269
+ interop: "Interop",
16270
+ "skills-hub": "Library",
16271
+ "side-question": "Side question",
16272
+ "handoff-review": "Handoff review",
16273
+ "fleet-run-approval": "Run approval"
16274
+ };
16275
+ return names[state];
16276
+ }
16277
+
16278
+ // src/interactive/interactive-presentation.ts
16279
+ init_esm_shims();
16280
+ import { performance as performance3 } from "node:perf_hooks";
16281
+
16282
+ // src/domains/quota/summary-feed.ts
16283
+ init_esm_shims();
16284
+
16285
+ // src/domains/quota/service.ts
16286
+ init_esm_shims();
16287
+
16288
+ // src/domains/quota/cache.ts
16289
+ init_esm_shims();
16290
+ var DEFAULT_QUOTA_CACHE_TTL_MS = 5 * 6e4;
16291
+ function createQuotaCache(options = {}) {
16292
+ const ttlMs = options.ttlMs ?? DEFAULT_QUOTA_CACHE_TTL_MS;
16293
+ const now = options.now ?? Date.now;
16294
+ const entries = /* @__PURE__ */ new Map();
16295
+ const fresh = (entry) => entry !== void 0 && now() - entry.storedAtMs < ttlMs;
16296
+ return {
16297
+ read(providerId) {
16298
+ const entry = entries.get(providerId);
16299
+ return fresh(entry) && entry !== void 0 ? entry.snapshot : null;
16300
+ },
16301
+ readLastGood(providerId) {
16302
+ const entry = entries.get(providerId);
16303
+ if (entry === void 0) return null;
16304
+ return fresh(entry) ? entry.snapshot : { ...entry.snapshot, stale: true };
16305
+ },
16306
+ write(snapshot) {
16307
+ entries.set(snapshot.providerId, { snapshot, storedAtMs: now() });
16308
+ },
16309
+ clear(providerId) {
16310
+ if (providerId === void 0) entries.clear();
16311
+ else entries.delete(providerId);
16312
+ },
16313
+ async resolve(provider) {
16314
+ const cached = entries.get(provider.id);
16315
+ if (fresh(cached) && cached !== void 0) return cached.snapshot;
16316
+ const snapshot = await provider.fetch();
16317
+ if (snapshot.status === "ok") {
16318
+ entries.set(provider.id, { snapshot, storedAtMs: now() });
16319
+ return snapshot;
16320
+ }
16321
+ if (cached === void 0) return snapshot;
16322
+ return {
16323
+ ...cached.snapshot,
16324
+ stale: true,
16325
+ message: snapshot.message ?? cached.snapshot.message ?? null
16326
+ };
16327
+ }
16328
+ };
16329
+ }
16330
+
16331
+ // src/domains/quota/registry.ts
16332
+ init_esm_shims();
16333
+
16334
+ // src/domains/quota/anthropic-max-provider.ts
16335
+ init_esm_shims();
16336
+
16337
+ // src/domains/quota/anthropic-usage.ts
16338
+ init_esm_shims();
16339
+
16340
+ // src/domains/quota/types.ts
16341
+ init_esm_shims();
16342
+ function parseRetryAfterSeconds(value) {
16343
+ if (!value) return null;
16344
+ const seconds = Number.parseFloat(value.trim());
16345
+ if (!Number.isFinite(seconds)) return null;
16346
+ return Math.max(0, seconds);
16347
+ }
16348
+ function formatPlan(value) {
16349
+ if (!value) return null;
16350
+ const words = value.replace(/_/g, " ").trim();
16351
+ if (!words) return null;
16352
+ return words.replace(/\S+/g, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
16353
+ }
16354
+
16355
+ // src/domains/quota/anthropic-usage.ts
16356
+ var ANTHROPIC_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
16357
+ var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
16358
+ var WINDOW_ORDER = { session: 0, weekly: 1 };
16359
+ function outcome(status, message, extra = {}) {
16360
+ return { status, message, windows: [], credits: null, retryAfterSeconds: null, ...extra };
16361
+ }
16362
+ async function fetchAnthropicUsage(token, dependencies) {
16363
+ const controller = new AbortController();
16364
+ const timer = setTimeout(() => controller.abort(), dependencies.timeoutMs);
16365
+ let response;
16366
+ try {
16367
+ response = await dependencies.fetch(ANTHROPIC_USAGE_URL, {
16368
+ method: "GET",
16369
+ headers: {
16370
+ Authorization: `Bearer ${token}`,
16371
+ "anthropic-beta": ANTHROPIC_OAUTH_BETA
16372
+ },
16373
+ signal: controller.signal
16374
+ });
16375
+ } catch (error) {
16376
+ return outcome("error", describeError2(error, dependencies.timeoutMs));
16377
+ } finally {
16378
+ clearTimeout(timer);
16379
+ }
16380
+ if (response.status === 401 || response.status === 403) {
16381
+ await discardBody(response);
16382
+ return outcome("expired", null);
16383
+ }
16384
+ if (response.status === 429) {
16385
+ await discardBody(response);
16386
+ return outcome("error", "HTTP 429", {
16387
+ retryAfterSeconds: parseRetryAfterSeconds(response.headers.get("Retry-After"))
16388
+ });
16389
+ }
16390
+ if (response.status !== 200) {
16391
+ await discardBody(response);
16392
+ return outcome("error", `HTTP ${response.status}`);
16393
+ }
16394
+ let body;
16395
+ try {
16396
+ body = await response.json();
16397
+ } catch {
16398
+ return outcome("error", "Unexpected usage response");
16399
+ }
16400
+ if (!isRecord(body)) {
16401
+ return outcome("error", "Unexpected usage response");
16402
+ }
16403
+ return outcome("ok", null, { windows: usageWindows(body), credits: usageCredits(body.spend) });
16404
+ }
16405
+ function usageWindows(body) {
16406
+ const fromLimits = windowsFromLimits(body.limits);
16407
+ const windows = fromLimits.length > 0 ? fromLimits : windowsFromFlat(body);
16408
+ return windows.sort((left, right) => {
16409
+ const order = (WINDOW_ORDER[left.key] ?? 2) - (WINDOW_ORDER[right.key] ?? 2);
16410
+ return order !== 0 ? order : left.label.localeCompare(right.label);
16411
+ });
16412
+ }
16413
+ function windowsFromLimits(raw) {
16414
+ if (!Array.isArray(raw)) return [];
16415
+ const windows = [];
16416
+ for (const entry of raw) {
16417
+ if (!isRecord(entry)) continue;
16418
+ const percent = asNumber(entry.percent);
16419
+ if (percent === null) continue;
16420
+ const common = {
16421
+ usedPct: percent,
16422
+ resetsAt: asIsoString(entry.resets_at)
16423
+ };
16424
+ if (typeof entry.severity === "string") common.severity = entry.severity;
16425
+ if (entry.is_active === true) common.active = true;
16426
+ if (entry.kind === "session") {
16427
+ windows.push({ key: "session", label: "5h", short: "5h", ...common });
16428
+ } else if (entry.kind === "weekly_all") {
16429
+ windows.push({ key: "weekly", label: "Weekly", short: "wk", ...common });
16430
+ } else if (entry.kind === "weekly_scoped") {
16431
+ const name = scopeName(entry.scope);
16432
+ if (name === null) continue;
16433
+ windows.push({ key: `weekly_scoped.${slug(name)}`, label: name, scope: name, ...common });
16434
+ }
16435
+ }
16436
+ return windows;
16437
+ }
16438
+ function windowsFromFlat(body) {
16439
+ const windows = [];
16440
+ const fiveHour = bucketWindow(body.five_hour, "session", "5h", "5h");
16441
+ if (fiveHour !== null) windows.push(fiveHour);
16442
+ const sevenDay = bucketWindow(body.seven_day, "weekly", "Weekly", "wk");
16443
+ if (sevenDay !== null) windows.push(sevenDay);
16444
+ return windows;
16445
+ }
16446
+ function bucketWindow(raw, key, label, short) {
16447
+ if (!isRecord(raw)) return null;
16448
+ const usedPct = asNumber(raw.utilization);
16449
+ if (usedPct === null) return null;
16450
+ return { key, label, short, usedPct, resetsAt: asIsoString(raw.resets_at) };
16451
+ }
16452
+ function scopeName(raw) {
16453
+ if (!isRecord(raw)) return null;
16454
+ const model = raw.model;
16455
+ if (!isRecord(model)) return null;
16456
+ const name = typeof model.display_name === "string" ? model.display_name.trim() : "";
16457
+ if (name.length === 0) return null;
16458
+ return slug(name) === "all-models" || slug(name) === "all" ? null : name;
16459
+ }
16460
+ function usageCredits(raw) {
16461
+ if (!isRecord(raw) || raw.enabled !== true) return null;
16462
+ const percent = asNumber(raw.percent);
16463
+ const limit = majorAmount(raw.limit);
16464
+ if (limit === null) {
16465
+ return percent === null ? null : { display: `${percent.toFixed(0)}% used`, usedPct: percent };
16466
+ }
16467
+ const used = majorAmount(raw.used);
16468
+ if (used === null) return null;
16469
+ const currency = isRecord(raw.limit) && typeof raw.limit.currency === "string" ? raw.limit.currency : "USD";
16470
+ const symbol = currency === "USD" ? "$" : "";
16471
+ const usedPct = percent ?? (limit > 0 ? used / limit * 100 : null);
16472
+ return { display: `${symbol}${used.toFixed(2)} / ${symbol}${limit.toFixed(2)}`, usedPct };
16473
+ }
16474
+ function majorAmount(raw) {
16475
+ if (!isRecord(raw)) return null;
16476
+ const minor = asNumber(raw.amount_minor);
16477
+ if (minor === null) return null;
16478
+ const exponent = asNumber(raw.exponent) ?? 0;
16479
+ return minor / 10 ** exponent;
16480
+ }
16481
+ function isRecord(value) {
16482
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16483
+ }
16484
+ function asNumber(value) {
16485
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
16486
+ if (typeof value === "string") {
16487
+ const parsed = Number.parseFloat(value.trim());
16488
+ return Number.isFinite(parsed) ? parsed : null;
16489
+ }
16490
+ return null;
16491
+ }
16492
+ function asIsoString(value) {
16493
+ if (typeof value !== "string" || value.trim().length === 0) return null;
16494
+ const parsed = new Date(value);
16495
+ return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
16496
+ }
16497
+ function slug(value) {
16498
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
16499
+ }
16500
+ function describeError2(error, timeoutMs) {
16501
+ if (error instanceof Error) {
16502
+ return error.name === "AbortError" ? `timeout after ${timeoutMs}ms` : error.message;
16503
+ }
16504
+ return String(error);
16505
+ }
16506
+ async function discardBody(response) {
16507
+ try {
16508
+ await response.body?.cancel();
16509
+ } catch {
16510
+ return;
16511
+ }
16512
+ }
16513
+
16514
+ // src/domains/quota/anthropic-max-provider.ts
16515
+ var ANTHROPIC_MAX_QUOTA_PROVIDER_ID = "anthropic-max";
16516
+ var AUTH_PROVIDER_ID = "anthropic";
16517
+ var DISPLAY_NAME = "Anthropic Max";
16518
+ var REQUEST_TIMEOUT_MS = 15e3;
16519
+ function parseStoredAnthropicCredential(raw) {
16520
+ if (!isRecord2(raw) || raw.type !== "oauth") return null;
16521
+ const access = raw.access;
16522
+ if (typeof access !== "string" || access.length === 0) return null;
16523
+ const refresh = raw.refresh;
16524
+ return {
16525
+ accessToken: access,
16526
+ expiresAtMs: asNumber2(raw.expires),
16527
+ hasRefreshToken: typeof refresh === "string" && refresh.length > 0
16528
+ };
16529
+ }
16530
+ async function readStoredCredential() {
16531
+ try {
16532
+ return parseStoredAnthropicCredential(openAuthStorage().get(AUTH_PROVIDER_ID));
16533
+ } catch {
16534
+ return null;
16535
+ }
16536
+ }
16537
+ function createAnthropicMaxQuotaProvider(options = {}) {
16538
+ const readCredentials = options.readCredentials ?? readStoredCredential;
16539
+ const doFetch = options.fetch ?? globalThis.fetch;
16540
+ const timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS;
16541
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
16542
+ const snapshot = (status, message, extra = {}) => ({
16543
+ providerId: ANTHROPIC_MAX_QUOTA_PROVIDER_ID,
16544
+ displayName: DISPLAY_NAME,
16545
+ status,
16546
+ windows: [],
16547
+ message,
16548
+ fetchedAt: now().toISOString(),
16549
+ ...extra
16550
+ });
16551
+ return {
16552
+ id: ANTHROPIC_MAX_QUOTA_PROVIDER_ID,
16553
+ displayName: DISPLAY_NAME,
16554
+ async detect() {
16555
+ return await readCredentials() !== null;
16556
+ },
16557
+ async fetch() {
16558
+ const credentials = await readCredentials();
16559
+ if (credentials === null) {
16560
+ return snapshot("no_credentials", "Clio holds no Anthropic OAuth credential");
16561
+ }
16562
+ if (credentials.expiresAtMs !== null && credentials.expiresAtMs <= now().getTime()) {
16563
+ return snapshot(
16564
+ "expired",
16565
+ credentials.hasRefreshToken ? "Clio's Anthropic token expired; the next turn refreshes it" : "Run clio-coder auth login anthropic again"
16566
+ );
16567
+ }
16568
+ const usage = await fetchAnthropicUsage(credentials.accessToken, { fetch: doFetch, timeoutMs });
16569
+ if (usage.status !== "ok") {
16570
+ return snapshot(usage.status, usage.message ?? "Run clio-coder auth login anthropic again", {
16571
+ retryAfterSeconds: usage.retryAfterSeconds
16572
+ });
16573
+ }
16574
+ return snapshot("ok", null, { windows: usage.windows, credits: usage.credits });
16575
+ }
16576
+ };
16577
+ }
16578
+ function isRecord2(value) {
16579
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16580
+ }
16581
+ function asNumber2(value) {
16582
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
16583
+ if (typeof value === "string") {
16584
+ const parsed = Number.parseFloat(value.trim());
16585
+ return Number.isFinite(parsed) ? parsed : null;
16586
+ }
16587
+ return null;
16588
+ }
16589
+
16590
+ // src/domains/quota/antigravity-provider.ts
16591
+ init_esm_shims();
16592
+ import { readFile } from "node:fs/promises";
16593
+ import { homedir as homedir2 } from "node:os";
16594
+ import { join } from "node:path";
16595
+ var ANTIGRAVITY_QUOTA_PROVIDER_ID = "antigravity";
16596
+ var DISPLAY_NAME2 = "Antigravity";
16597
+ var USER_AGENT = "antigravity";
16598
+ var REQUEST_TIMEOUT_MS2 = 2e4;
16599
+ var EXPIRY_SKEW_MS = 6e4;
16600
+ var WINDOW_ORDER2 = { session: 0, weekly: 1 };
16601
+ var MODEL_PREFIXES = ["gemini", "claude", "gpt", "image", "imagen"];
16602
+ var ENDPOINTS = [
16603
+ "https://daily-cloudcode-pa.googleapis.com",
16604
+ "https://daily-cloudcode-pa.sandbox.googleapis.com",
16605
+ "https://cloudcode-pa.googleapis.com"
16606
+ ];
16607
+ function defaultCredentialsPath() {
16608
+ return join(homedir2(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
16609
+ }
16610
+ function parseCredentials(raw) {
16611
+ if (!isRecord3(raw)) return null;
16612
+ const token = raw.token;
16613
+ if (!isRecord3(token)) return null;
16614
+ const accessToken = token.access_token;
16615
+ if (typeof accessToken !== "string" || accessToken.length === 0) return null;
16616
+ const refreshToken = token.refresh_token;
16617
+ return {
16618
+ accessToken,
16619
+ refreshToken: typeof refreshToken === "string" && refreshToken.length > 0 ? refreshToken : null,
16620
+ expiryMs: asEpochMs(token.expiry)
16621
+ };
16622
+ }
16623
+ async function readCredentialsFile(path) {
16624
+ let text;
16625
+ try {
16626
+ text = await readFile(path, "utf8");
16627
+ } catch {
16628
+ return null;
16629
+ }
16630
+ try {
16631
+ return parseCredentials(JSON.parse(text));
16632
+ } catch {
16633
+ return null;
16634
+ }
16635
+ }
16636
+ function createAntigravityQuotaProvider(options = {}) {
16637
+ const credentialsPath = options.credentialsPath ?? defaultCredentialsPath();
16638
+ const readCredentials = options.readCredentials ?? (() => readCredentialsFile(credentialsPath));
16639
+ const doFetch = options.fetch ?? globalThis.fetch;
16640
+ const timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS2;
16641
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
16642
+ const snapshot = (status, message, extra = {}) => ({
16643
+ providerId: ANTIGRAVITY_QUOTA_PROVIDER_ID,
16644
+ displayName: DISPLAY_NAME2,
16645
+ status,
16646
+ windows: [],
16647
+ message,
16648
+ fetchedAt: now().toISOString(),
16649
+ ...extra
16650
+ });
16651
+ const post = async (url, token, body) => {
16652
+ const controller = new AbortController();
16653
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
16654
+ let response;
16655
+ try {
16656
+ response = await doFetch(url, {
16657
+ method: "POST",
16658
+ headers: {
16659
+ Authorization: `Bearer ${token}`,
16660
+ "Content-Type": "application/json",
16661
+ "User-Agent": USER_AGENT
16662
+ },
16663
+ body: JSON.stringify(body),
16664
+ signal: controller.signal
16665
+ });
16666
+ } catch (error) {
16667
+ return { kind: "error", message: describeError3(error, timeoutMs) };
16668
+ } finally {
16669
+ clearTimeout(timer);
16670
+ }
16671
+ if (response.status === 401 || response.status === 403) {
16672
+ await discardBody2(response);
16673
+ return { kind: "auth" };
16674
+ }
16675
+ if (response.status !== 200) {
16676
+ await discardBody2(response);
16677
+ return { kind: "error", message: `HTTP ${response.status}` };
16678
+ }
16679
+ let data;
16680
+ try {
16681
+ data = await response.json();
16682
+ } catch {
16683
+ return { kind: "error", message: "Unexpected usage response" };
16684
+ }
16685
+ return isRecord3(data) ? { kind: "ok", data } : { kind: "error", message: "Unexpected usage response" };
16686
+ };
16687
+ const collect = async (base, token) => {
16688
+ const load = await post(`${base}/v1internal:loadCodeAssist`, token, { metadata: { ideType: "ANTIGRAVITY" } });
16689
+ if (load.kind !== "ok") return load;
16690
+ const project = typeof load.data.cloudaicompanionProject === "string" ? load.data.cloudaicompanionProject : null;
16691
+ const plan = tierPlan(load.data.currentTier);
16692
+ if (project !== null) {
16693
+ const summary = await post(`${base}/v1internal:retrieveUserQuotaSummary`, token, { project });
16694
+ if (summary.kind === "auth") return summary;
16695
+ if (summary.kind === "ok") {
16696
+ const windows = windowsFromSummary(summary.data);
16697
+ if (windows.length > 0) return { kind: "ok", windows, plan };
16698
+ }
16699
+ }
16700
+ const models = await post(`${base}/v1internal:fetchAvailableModels`, token, project === null ? {} : { project });
16701
+ if (models.kind !== "ok") return models;
16702
+ return { kind: "ok", windows: modelWindows(models.data), plan };
16703
+ };
16704
+ return {
16705
+ id: ANTIGRAVITY_QUOTA_PROVIDER_ID,
16706
+ displayName: DISPLAY_NAME2,
16707
+ async detect() {
16708
+ return await readCredentials() !== null;
16709
+ },
16710
+ async fetch() {
16711
+ const credentials = await readCredentials();
16712
+ if (credentials === null) {
16713
+ return snapshot("no_credentials", "No Antigravity credentials found");
16714
+ }
16715
+ if (credentials.expiryMs !== null && credentials.expiryMs <= now().getTime() + EXPIRY_SKEW_MS) {
16716
+ return snapshot("expired", "Antigravity's stored token expired; run the agy command once to refresh it");
16717
+ }
16718
+ let sawAuthFailure = false;
16719
+ let lastError = null;
16720
+ for (const base of ENDPOINTS) {
16721
+ const result = await collect(base, credentials.accessToken);
16722
+ if (result.kind === "auth") {
16723
+ sawAuthFailure = true;
16724
+ continue;
16725
+ }
16726
+ if (result.kind === "error") {
16727
+ lastError = result.message;
16728
+ continue;
16729
+ }
16730
+ if (result.windows.length > 0) {
16731
+ return snapshot("ok", null, { windows: result.windows, plan: result.plan });
16732
+ }
16733
+ }
16734
+ if (sawAuthFailure) return snapshot("expired", "Sign in with Antigravity again");
16735
+ if (lastError !== null) return snapshot("error", lastError);
16736
+ return snapshot("error", "Antigravity returned no quota");
16737
+ }
16738
+ };
16739
+ }
16740
+ function tierPlan(raw) {
16741
+ if (!isRecord3(raw)) return null;
16742
+ const plan = formatPlan(typeof raw.name === "string" ? raw.name : null);
16743
+ if (plan === null) return null;
16744
+ return plan.toLowerCase() === DISPLAY_NAME2.toLowerCase() ? null : plan;
16745
+ }
16746
+ function windowsFromSummary(data) {
16747
+ if (!Array.isArray(data.groups)) return [];
16748
+ const groups = data.groups.filter(isRecord3).sort((a, b) => Number(isGeminiGroup(b)) - Number(isGeminiGroup(a)));
16749
+ return groups.flatMap((group, index) => {
16750
+ const scope = typeof group.displayName === "string" && group.displayName.trim() ? group.displayName.trim() : `Model group ${index + 1}`;
16751
+ return windowsFromGroup(group).map((window) => ({
16752
+ ...window,
16753
+ key: index === 0 ? window.key : `group.${index}.${window.key}`,
16754
+ scope
16755
+ }));
16112
16756
  });
16113
- deps.registerInputListener((data) => {
16114
- deps.onInputIngress?.(classifyInputAction(data, deps), data);
16115
- return controller.handleInput(data);
16757
+ }
16758
+ function windowsFromGroup(group) {
16759
+ if (!Array.isArray(group.buckets)) return [];
16760
+ const windows = [];
16761
+ for (const bucket of group.buckets) {
16762
+ if (!isRecord3(bucket)) continue;
16763
+ const window = typeof bucket.window === "string" ? bucket.window.toLowerCase() : null;
16764
+ const usedPct = usedFromRemaining(bucket.remainingFraction);
16765
+ if (usedPct === null) continue;
16766
+ if (window === "5h") {
16767
+ windows.push({ key: "session", label: "5h", short: "5h", usedPct, resetsAt: asIsoString2(bucket.resetTime) });
16768
+ } else if (window === "weekly") {
16769
+ windows.push({ key: "weekly", label: "Weekly", short: "wk", usedPct, resetsAt: asIsoString2(bucket.resetTime) });
16770
+ }
16771
+ }
16772
+ return windows.sort((left, right) => (WINDOW_ORDER2[left.key] ?? 2) - (WINDOW_ORDER2[right.key] ?? 2));
16773
+ }
16774
+ function isGeminiGroup(group) {
16775
+ if (mentionsGemini(group.displayName) || mentionsGemini(group.description)) return true;
16776
+ if (!Array.isArray(group.buckets)) return false;
16777
+ return group.buckets.some((bucket) => {
16778
+ if (!isRecord3(bucket)) return false;
16779
+ const id = typeof bucket.bucketId === "string" ? bucket.bucketId.toLowerCase() : "";
16780
+ return id.startsWith("gemini-") || mentionsGemini(bucket.displayName);
16116
16781
  });
16117
- return controller;
16118
16782
  }
16119
- function classifyInputAction(data, deps) {
16120
- if (data.length === 0 || isKeyRelease(data)) return "no-visual-change";
16121
- if (deps.keybindings.matches(data, "tui.input.submit")) return "submit";
16122
- const sgrMousePrefix = `${String.fromCharCode(27)}[<`;
16123
- if (data.startsWith(`${sgrMousePrefix}64;`) || data.startsWith(`${sgrMousePrefix}65;`) || deps.keybindings.matches(data, "tui.altScreen.pageUp") || deps.keybindings.matches(data, "tui.altScreen.pageDown") || deps.keybindings.matches(data, "tui.editor.pageUp") || deps.keybindings.matches(data, "tui.editor.pageDown")) {
16124
- return "scroll";
16783
+ function mentionsGemini(value) {
16784
+ return typeof value === "string" && value.toLowerCase().includes("gemini");
16785
+ }
16786
+ function modelWindows(data) {
16787
+ if (!isRecord3(data.models)) return [];
16788
+ const windows = [];
16789
+ for (const [name, info] of Object.entries(data.models)) {
16790
+ if (!isRecord3(info) || !isRecord3(info.quotaInfo)) continue;
16791
+ if (!MODEL_PREFIXES.some((prefix) => name.toLowerCase().startsWith(prefix))) continue;
16792
+ const usedPct = usedFromRemaining(info.quotaInfo.remainingFraction);
16793
+ if (usedPct === null) continue;
16794
+ windows.push({
16795
+ key: `model.${name}`,
16796
+ label: "Model quota",
16797
+ short: name,
16798
+ scope: name,
16799
+ usedPct,
16800
+ resetsAt: asIsoString2(info.quotaInfo.resetTime)
16801
+ });
16125
16802
  }
16126
- if (deps.overlay.getState() !== "closed") return "overlay";
16127
- return "editor";
16803
+ return windows;
16128
16804
  }
16129
- function overlayScopeLabel(state) {
16130
- const names = {
16131
- closed: "Composer",
16132
- "permission-confirm": "Permission",
16133
- "dispatch-board": "Workers",
16134
- auth: "Authentication",
16135
- cost: "Usage",
16136
- "context-view": "Context",
16137
- "context-reset": "Context reset",
16138
- tasks: "Tasks",
16139
- decisions: "Decisions",
16140
- memory: "Memory",
16141
- view: "View",
16142
- model: "Model",
16143
- "model-scope": "Model cycle set",
16144
- settings: "Settings",
16145
- resume: "Resume",
16146
- tree: "Session tree",
16147
- "message-picker": "Messages",
16148
- "cwd-fallback": "Working directory",
16149
- "ask-user": "Interview",
16150
- help: "Help",
16151
- extensions: "Extensions",
16152
- interop: "Interop",
16153
- "skills-hub": "Library",
16154
- "side-question": "Side question",
16155
- "handoff-review": "Handoff review",
16156
- "fleet-run-approval": "Run approval"
16805
+ function usedFromRemaining(raw) {
16806
+ const remaining = asNumber3(raw);
16807
+ if (remaining === null) return null;
16808
+ return (1 - Math.min(1, Math.max(0, remaining))) * 100;
16809
+ }
16810
+ function isRecord3(value) {
16811
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16812
+ }
16813
+ function asNumber3(value) {
16814
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
16815
+ if (typeof value === "string") {
16816
+ const parsed = Number.parseFloat(value.trim());
16817
+ return Number.isFinite(parsed) ? parsed : null;
16818
+ }
16819
+ return null;
16820
+ }
16821
+ function asIsoString2(value) {
16822
+ if (typeof value !== "string" || value.trim().length === 0) return null;
16823
+ const parsed = new Date(value);
16824
+ return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
16825
+ }
16826
+ function asEpochMs(value) {
16827
+ if (typeof value !== "string" || value.trim().length === 0) return null;
16828
+ const parsed = Date.parse(value);
16829
+ return Number.isNaN(parsed) ? null : parsed;
16830
+ }
16831
+ function describeError3(error, timeoutMs) {
16832
+ if (error instanceof Error) {
16833
+ return error.name === "AbortError" ? `timeout after ${timeoutMs}ms` : error.message;
16834
+ }
16835
+ return String(error);
16836
+ }
16837
+ async function discardBody2(response) {
16838
+ try {
16839
+ await response.body?.cancel();
16840
+ } catch {
16841
+ return;
16842
+ }
16843
+ }
16844
+
16845
+ // src/domains/quota/claude-code-provider.ts
16846
+ init_esm_shims();
16847
+ import { readFile as readFile2 } from "node:fs/promises";
16848
+ import { homedir as homedir3 } from "node:os";
16849
+ import { join as join2 } from "node:path";
16850
+ var CLAUDE_CODE_QUOTA_PROVIDER_ID = "claude-code";
16851
+ var DISPLAY_NAME3 = "Claude Code";
16852
+ var REQUEST_TIMEOUT_MS3 = 15e3;
16853
+ function defaultClaudeCredentialsPath() {
16854
+ return join2(homedir3(), ".claude", ".credentials.json");
16855
+ }
16856
+ function parseClaudeCredentials(raw) {
16857
+ if (!isRecord4(raw)) return null;
16858
+ const oauth = raw.claudeAiOauth;
16859
+ if (!isRecord4(oauth)) return null;
16860
+ const token = oauth.accessToken;
16861
+ if (typeof token !== "string" || token.length === 0) return null;
16862
+ const subscription = oauth.subscriptionType;
16863
+ const refreshToken = oauth.refreshToken;
16864
+ return {
16865
+ accessToken: token,
16866
+ subscriptionType: typeof subscription === "string" ? subscription : null,
16867
+ expiresAtMs: asNumber4(oauth.expiresAt),
16868
+ refreshTokenExpiresAtMs: asNumber4(oauth.refreshTokenExpiresAt),
16869
+ hasRefreshToken: typeof refreshToken === "string" && refreshToken.length > 0
16157
16870
  };
16158
- return names[state];
16871
+ }
16872
+ function expiryMessage(credentials, nowMs) {
16873
+ const { expiresAtMs, refreshTokenExpiresAtMs, hasRefreshToken } = credentials;
16874
+ if (expiresAtMs === null || expiresAtMs > nowMs) return null;
16875
+ const refreshUsable = hasRefreshToken && (refreshTokenExpiresAtMs === null || refreshTokenExpiresAtMs > nowMs);
16876
+ return refreshUsable ? "Claude Code's stored token expired; run the claude command once to refresh it" : "Sign in with Claude Code again";
16877
+ }
16878
+ async function readCredentialsFile2(path) {
16879
+ let text;
16880
+ try {
16881
+ text = await readFile2(path, "utf8");
16882
+ } catch {
16883
+ return null;
16884
+ }
16885
+ try {
16886
+ return parseClaudeCredentials(JSON.parse(text));
16887
+ } catch {
16888
+ return null;
16889
+ }
16890
+ }
16891
+ function createClaudeCodeQuotaProvider(options = {}) {
16892
+ const credentialsPath = options.credentialsPath ?? defaultClaudeCredentialsPath();
16893
+ const readCredentials = options.readCredentials ?? (() => readCredentialsFile2(credentialsPath));
16894
+ const doFetch = options.fetch ?? globalThis.fetch;
16895
+ const timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS3;
16896
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
16897
+ const snapshot = (status, message, extra = {}) => ({
16898
+ providerId: CLAUDE_CODE_QUOTA_PROVIDER_ID,
16899
+ displayName: DISPLAY_NAME3,
16900
+ status,
16901
+ windows: [],
16902
+ message,
16903
+ fetchedAt: now().toISOString(),
16904
+ ...extra
16905
+ });
16906
+ return {
16907
+ id: CLAUDE_CODE_QUOTA_PROVIDER_ID,
16908
+ displayName: DISPLAY_NAME3,
16909
+ async detect() {
16910
+ return await readCredentials() !== null;
16911
+ },
16912
+ async fetch() {
16913
+ const credentials = await readCredentials();
16914
+ if (credentials === null) {
16915
+ return snapshot("no_credentials", "No Claude Code credentials found");
16916
+ }
16917
+ const expired = expiryMessage(credentials, now().getTime());
16918
+ if (expired !== null) {
16919
+ return snapshot("expired", expired);
16920
+ }
16921
+ const usage = await fetchAnthropicUsage(credentials.accessToken, { fetch: doFetch, timeoutMs });
16922
+ if (usage.status !== "ok") {
16923
+ return snapshot(usage.status, usage.message ?? "Sign in with Claude Code again", {
16924
+ retryAfterSeconds: usage.retryAfterSeconds
16925
+ });
16926
+ }
16927
+ return snapshot("ok", null, {
16928
+ windows: usage.windows,
16929
+ credits: usage.credits,
16930
+ plan: formatPlan(credentials.subscriptionType)
16931
+ });
16932
+ }
16933
+ };
16934
+ }
16935
+ function isRecord4(value) {
16936
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16937
+ }
16938
+ function asNumber4(value) {
16939
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
16940
+ if (typeof value === "string") {
16941
+ const parsed = Number.parseFloat(value.trim());
16942
+ return Number.isFinite(parsed) ? parsed : null;
16943
+ }
16944
+ return null;
16159
16945
  }
16160
16946
 
16161
- // src/interactive/interactive-presentation.ts
16947
+ // src/domains/quota/codex-provider.ts
16162
16948
  init_esm_shims();
16163
- import { performance as performance3 } from "node:perf_hooks";
16949
+ import { readFile as readFile3 } from "node:fs/promises";
16950
+ import { homedir as homedir4 } from "node:os";
16951
+ import { join as join3 } from "node:path";
16952
+ var CODEX_QUOTA_PROVIDER_ID = "codex";
16953
+ var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
16954
+ var DISPLAY_NAME4 = "Codex";
16955
+ var REQUEST_TIMEOUT_MS4 = 15e3;
16956
+ var WINDOW_ORDER3 = { session: 0, weekly: 1 };
16957
+ var WEEKLY_THRESHOLD_SECONDS = 86400;
16958
+ function defaultCodexCredentialsPath() {
16959
+ const codexHome = process.env.CODEX_HOME;
16960
+ return codexHome ? join3(codexHome, "auth.json") : join3(homedir4(), ".codex", "auth.json");
16961
+ }
16962
+ function parseCodexCredentials(raw) {
16963
+ if (!isRecord5(raw)) return null;
16964
+ const tokens = raw.tokens;
16965
+ if (!isRecord5(tokens)) return null;
16966
+ const accessToken = tokens.access_token;
16967
+ if (typeof accessToken !== "string" || accessToken.length === 0) return null;
16968
+ const accountId = tokens.account_id;
16969
+ return {
16970
+ accessToken,
16971
+ accountId: typeof accountId === "string" ? accountId : null
16972
+ };
16973
+ }
16974
+ async function readCredentialsFile3(path) {
16975
+ let text;
16976
+ try {
16977
+ text = await readFile3(path, "utf8");
16978
+ } catch {
16979
+ return null;
16980
+ }
16981
+ try {
16982
+ return parseCodexCredentials(JSON.parse(text));
16983
+ } catch {
16984
+ return null;
16985
+ }
16986
+ }
16987
+ function createCodexQuotaProvider(options = {}) {
16988
+ const credentialsPath = options.credentialsPath ?? defaultCodexCredentialsPath();
16989
+ const readCredentials = options.readCredentials ?? (() => readCredentialsFile3(credentialsPath));
16990
+ const doFetch = options.fetch ?? globalThis.fetch;
16991
+ const timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS4;
16992
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
16993
+ const snapshot = (status, message, extra = {}) => ({
16994
+ providerId: CODEX_QUOTA_PROVIDER_ID,
16995
+ displayName: DISPLAY_NAME4,
16996
+ status,
16997
+ windows: [],
16998
+ message,
16999
+ fetchedAt: now().toISOString(),
17000
+ ...extra
17001
+ });
17002
+ return {
17003
+ id: CODEX_QUOTA_PROVIDER_ID,
17004
+ displayName: DISPLAY_NAME4,
17005
+ async detect() {
17006
+ return await readCredentials() !== null;
17007
+ },
17008
+ async fetch() {
17009
+ const credentials = await readCredentials();
17010
+ if (credentials === null) {
17011
+ return snapshot("no_credentials", "No Codex credentials found");
17012
+ }
17013
+ const headers = {
17014
+ Authorization: `Bearer ${credentials.accessToken}`,
17015
+ "User-Agent": "codex-cli"
17016
+ };
17017
+ if (credentials.accountId !== null) {
17018
+ headers["ChatGPT-Account-Id"] = credentials.accountId;
17019
+ }
17020
+ const controller = new AbortController();
17021
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
17022
+ let response;
17023
+ try {
17024
+ response = await doFetch(CODEX_USAGE_URL, {
17025
+ method: "GET",
17026
+ headers,
17027
+ signal: controller.signal
17028
+ });
17029
+ } catch (error) {
17030
+ return snapshot("error", describeError4(error, timeoutMs));
17031
+ } finally {
17032
+ clearTimeout(timer);
17033
+ }
17034
+ if (response.status === 401 || response.status === 403) {
17035
+ await discardBody3(response);
17036
+ return snapshot("expired", "Sign in with Codex again");
17037
+ }
17038
+ if (response.status !== 200) {
17039
+ await discardBody3(response);
17040
+ return snapshot("error", `HTTP ${response.status}`);
17041
+ }
17042
+ let body;
17043
+ try {
17044
+ body = await response.json();
17045
+ } catch {
17046
+ return snapshot("error", "Unexpected usage response");
17047
+ }
17048
+ if (!isRecord5(body)) {
17049
+ return snapshot("error", "Unexpected usage response");
17050
+ }
17051
+ const planType = typeof body.plan_type === "string" ? body.plan_type : null;
17052
+ return snapshot("ok", null, {
17053
+ windows: usageWindows2(body.rate_limit),
17054
+ credits: usageCredits2(body.credits),
17055
+ plan: formatPlan(planType)
17056
+ });
17057
+ }
17058
+ };
17059
+ }
17060
+ function usageWindows2(raw) {
17061
+ if (!isRecord5(raw)) return [];
17062
+ const windows = [];
17063
+ const primary = buildWindow(raw.primary_window, "session");
17064
+ if (primary !== null) windows.push(primary);
17065
+ const secondary = buildWindow(raw.secondary_window, "weekly");
17066
+ if (secondary !== null) windows.push(secondary);
17067
+ return windows.sort((left, right) => {
17068
+ const order = (WINDOW_ORDER3[left.key] ?? 2) - (WINDOW_ORDER3[right.key] ?? 2);
17069
+ return order !== 0 ? order : left.label.localeCompare(right.label);
17070
+ });
17071
+ }
17072
+ function buildWindow(raw, defaultKey) {
17073
+ if (!isRecord5(raw)) return null;
17074
+ const usedPct = asNumber5(raw.used_percent);
17075
+ if (usedPct === null) return null;
17076
+ const key = windowKey(raw.limit_window_seconds, defaultKey);
17077
+ const label = key === "weekly" ? "Weekly" : "5h";
17078
+ const short = key === "weekly" ? "wk" : "5h";
17079
+ return { key, label, short, usedPct, resetsAt: epochSecondsToIso(raw.reset_at) };
17080
+ }
17081
+ function windowKey(limitWindowSeconds, defaultKey) {
17082
+ const seconds = asNumber5(limitWindowSeconds);
17083
+ if (seconds === null) return defaultKey;
17084
+ return seconds >= WEEKLY_THRESHOLD_SECONDS ? "weekly" : "session";
17085
+ }
17086
+ function usageCredits2(raw) {
17087
+ if (!isRecord5(raw)) return null;
17088
+ if (raw.unlimited === true) return { display: "Unlimited", usedPct: null };
17089
+ if (raw.has_credits === true && typeof raw.balance === "string" && raw.balance.length > 0) {
17090
+ return { display: raw.balance, usedPct: null };
17091
+ }
17092
+ return null;
17093
+ }
17094
+ function isRecord5(value) {
17095
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17096
+ }
17097
+ function asNumber5(value) {
17098
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
17099
+ if (typeof value === "string") {
17100
+ const parsed = Number.parseFloat(value.trim());
17101
+ return Number.isFinite(parsed) ? parsed : null;
17102
+ }
17103
+ return null;
17104
+ }
17105
+ function epochSecondsToIso(value) {
17106
+ const seconds = asNumber5(value);
17107
+ if (seconds === null) return null;
17108
+ const date = new Date(seconds * 1e3);
17109
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
17110
+ }
17111
+ function describeError4(error, timeoutMs) {
17112
+ if (error instanceof Error) {
17113
+ return error.name === "AbortError" ? `timeout after ${timeoutMs}ms` : error.message;
17114
+ }
17115
+ return String(error);
17116
+ }
17117
+ async function discardBody3(response) {
17118
+ try {
17119
+ await response.body?.cancel();
17120
+ } catch {
17121
+ return;
17122
+ }
17123
+ }
17124
+
17125
+ // src/domains/quota/registry.ts
17126
+ function buildQuotaProviders() {
17127
+ return [
17128
+ createAnthropicMaxQuotaProvider(),
17129
+ createClaudeCodeQuotaProvider(),
17130
+ createCodexQuotaProvider(),
17131
+ createAntigravityQuotaProvider()
17132
+ // The copilot adapter attaches here once its credential shape is confirmed.
17133
+ ];
17134
+ }
17135
+
17136
+ // src/domains/quota/service.ts
17137
+ function createQuotaService(options = {}) {
17138
+ const providers = options.providers ?? buildQuotaProviders();
17139
+ const cache = options.cache ?? createQuotaCache({ ttlMs: options.ttlMs ?? DEFAULT_QUOTA_CACHE_TTL_MS });
17140
+ const includeLocal = options.includeLocal ?? true;
17141
+ const withLocal = (snapshots) => {
17142
+ if (!includeLocal) return snapshots;
17143
+ const local = options.localRuntimeLabel ? localQuotaSnapshot({ runtimeLabel: options.localRuntimeLabel }) : localQuotaSnapshot();
17144
+ return [...snapshots, local];
17145
+ };
17146
+ return {
17147
+ peek() {
17148
+ const cached = [];
17149
+ for (const provider of providers) {
17150
+ const snapshot = cache.readLastGood(provider.id);
17151
+ if (snapshot !== null) cached.push(snapshot);
17152
+ }
17153
+ return withLocal(cached);
17154
+ },
17155
+ async read() {
17156
+ const settled = await Promise.all(
17157
+ providers.map(async (provider) => {
17158
+ try {
17159
+ return await cache.resolve(provider);
17160
+ } catch (error) {
17161
+ return {
17162
+ providerId: provider.id,
17163
+ displayName: provider.displayName,
17164
+ status: "error",
17165
+ windows: [],
17166
+ message: error instanceof Error ? error.message : String(error),
17167
+ fetchedAt: null
17168
+ };
17169
+ }
17170
+ })
17171
+ );
17172
+ return withLocal(settled.filter((snapshot) => snapshot.status !== "no_credentials"));
17173
+ },
17174
+ async detected() {
17175
+ const flags = await Promise.all(
17176
+ providers.map(async (provider) => {
17177
+ try {
17178
+ return await provider.detect();
17179
+ } catch {
17180
+ return false;
17181
+ }
17182
+ })
17183
+ );
17184
+ return providers.filter((_, index) => flags[index] === true);
17185
+ }
17186
+ };
17187
+ }
17188
+
17189
+ // src/domains/quota/summary-feed.ts
17190
+ var QUOTA_SUMMARY_TTL_MS = 5 * 6e4;
17191
+ function createQuotaSummaryFeed(options = {}) {
17192
+ const service = options.service ?? createQuotaService();
17193
+ const ttlMs = options.ttlMs ?? QUOTA_SUMMARY_TTL_MS;
17194
+ const now = options.now ?? Date.now;
17195
+ let line = null;
17196
+ let snapshots = [];
17197
+ let readAt = null;
17198
+ let inFlight = false;
17199
+ let disposed = false;
17200
+ const refresh = () => {
17201
+ if (inFlight || disposed) return;
17202
+ inFlight = true;
17203
+ void Promise.resolve().then(() => disposed ? snapshots : service.read()).then((nextSnapshots) => {
17204
+ if (disposed) return;
17205
+ const next = quotaSummaryLine(nextSnapshots);
17206
+ const changed = JSON.stringify(nextSnapshots) !== JSON.stringify(snapshots);
17207
+ snapshots = nextSnapshots;
17208
+ readAt = now();
17209
+ if (changed) {
17210
+ line = next;
17211
+ options.onUpdate?.();
17212
+ }
17213
+ }).catch(() => {
17214
+ readAt = now();
17215
+ }).finally(() => {
17216
+ inFlight = false;
17217
+ });
17218
+ };
17219
+ return {
17220
+ peek() {
17221
+ if (!disposed && (readAt === null || now() - readAt >= ttlMs)) refresh();
17222
+ return line;
17223
+ },
17224
+ peekSnapshots() {
17225
+ if (!disposed && (readAt === null || now() - readAt >= ttlMs)) refresh();
17226
+ return snapshots;
17227
+ },
17228
+ dispose() {
17229
+ disposed = true;
17230
+ }
17231
+ };
17232
+ }
16164
17233
 
16165
17234
  // src/interactive/chat-panel.ts
16166
17235
  init_esm_shims();
@@ -16515,8 +17584,8 @@ function isMermaid(token) {
16515
17584
  }
16516
17585
  function codeSpan(line) {
16517
17586
  const content = line || "\xA0";
16518
- const longestBacktickRun = Math.max(0, ...Array.from(content.matchAll(/`+/g), (match) => match[0].length));
16519
- const fence = "`".repeat(longestBacktickRun + 1);
17587
+ const longestBacktickRun2 = Math.max(0, ...Array.from(content.matchAll(/`+/g), (match) => match[0].length));
17588
+ const fence = "`".repeat(longestBacktickRun2 + 1);
16520
17589
  const padding = content.startsWith("`") || content.endsWith("`") ? " " : "";
16521
17590
  return `${fence}${padding}${content}${padding}${fence}`;
16522
17591
  }
@@ -18041,8 +19110,8 @@ function footerKeyHint(now, narrow = false) {
18041
19110
  })
18042
19111
  ];
18043
19112
  const count = narrow ? 1 : 2;
18044
- const page = Math.floor(now / 12e3) % (Math.ceil(entries.length / count) + 1);
18045
- if (page === Math.ceil(entries.length / count)) return null;
19113
+ if (!entries.length) return null;
19114
+ const page = Math.floor(now / 12e3) % Math.ceil(entries.length / count);
18046
19115
  const start = page * count;
18047
19116
  return entries.slice(start, start + count).join(" \xB7 ");
18048
19117
  }
@@ -18051,7 +19120,7 @@ function footerKeyHint(now, narrow = false) {
18051
19120
  var DASHBOARD_PAGES = ["Activity", "Context", "Status"];
18052
19121
  var ACTIVE_AGENT_STATUSES = /* @__PURE__ */ new Set(["running", "enqueued", "cancelling", "retrying", "stale"]);
18053
19122
  var clean = (value) => sanitizeCallTargetText(redactSecretString(value));
18054
- function agentCard(row2, width, compact = false) {
19123
+ function agentCard(row2, width, compact = false, quota = []) {
18055
19124
  const theme2 = clioTheme();
18056
19125
  const presentation = dispatchStatusPresentation(row2.status, { compact: false });
18057
19126
  const name = clean(row2.agentId).replace(
@@ -18066,6 +19135,9 @@ function agentCard(row2, width, compact = false) {
18066
19135
  );
18067
19136
  const field = (label, value) => wrapTextWithAnsi(`${theme2.fg("dim", `${label} `)}${clean(value)}`, width);
18068
19137
  lines.push(...previewRows(field("Route", `${row2.targetId}/${row2.wireModelId}`), 2, width));
19138
+ const weekly = routeWeeklyQuota(row2, quota);
19139
+ if (weekly)
19140
+ lines.push(...previewRows(field("Account", `Shared ${weekly.account} \xB7 ${weekly.label}`), compact ? 1 : 2, width));
18069
19141
  if (row2.taskSummary)
18070
19142
  lines.push(...compact ? previewRows(field("Task", row2.taskSummary), 2, width) : field("Task", row2.taskSummary));
18071
19143
  const activity = renderDispatchActivity(row2, width);
@@ -18136,8 +19208,8 @@ function activityPage(state, width, budget) {
18136
19208
  if (first && second) {
18137
19209
  cards.push(
18138
19210
  ...zipColumns(
18139
- agentCard(first, col, true),
18140
- agentCard(second, width - col - 5, true),
19211
+ agentCard(first, col, true, state.quota),
19212
+ agentCard(second, width - col - 5, true, state.quota),
18141
19213
  col,
18142
19214
  width - col - 5,
18143
19215
  ` ${theme2.fg("frame", "\u2502")} `
@@ -18148,7 +19220,7 @@ function activityPage(state, width, budget) {
18148
19220
  }
18149
19221
  } else
18150
19222
  for (const row2 of active) {
18151
- const card = agentCard(row2, width, active.length > 1 || cardBudget < 18);
19223
+ const card = agentCard(row2, width, active.length > 1 || cardBudget < 18, state.quota);
18152
19224
  if (cards.length + card.length + 2 > cardBudget && shown > 0) break;
18153
19225
  cards.push(...card, "");
18154
19226
  shown++;
@@ -18198,16 +19270,17 @@ function contextPage(state, width) {
18198
19270
  ...wrapTextWithAnsi(
18199
19271
  theme2.style(
18200
19272
  "accent",
18201
- `${formatFooterTokens(ledger.usedTokens)} / ${ledger.contextWindow > 0 ? formatFooterTokens(ledger.contextWindow) : "unknown window"} tokens${ledger.percent === null ? "" : ` \xB7 ${ledger.percent.toFixed(1)}% occupied`}`,
19273
+ state.context.budget ? `${contextUsageText(state.context)} tokens` : `${formatFooterTokens(ledger.usedTokens)} / ${ledger.contextWindow > 0 ? formatFooterTokens(ledger.contextWindow) : "unknown window"} tokens${ledger.percent === null ? "" : ` \xB7 ${ledger.percent.toFixed(1)}% occupied`}`,
18202
19274
  { bold: true }
18203
19275
  ),
18204
19276
  width
18205
19277
  ),
18206
19278
  ...wrapTextWithAnsi(
18207
- `Free ${ledger.contextWindow > 0 ? formatFooterTokens(ledger.freeTokens) : "unknown"} \xB7 reserved ${formatFooterTokens(ledger.reserveTokens)} \xB7 ${ledger.toolCount} tool definitions \xB7 compaction ${ledger.compactionAuto ? `auto${ledger.compactionThreshold === null ? "" : ` @${Math.round(ledger.compactionThreshold * 100)}%`}` : "manual"}`,
19279
+ `${state.context.budget ? "Capture diagnostics: " : ""}Free ${ledger.contextWindow > 0 ? formatFooterTokens(ledger.freeTokens) : "unknown"} \xB7 reserved ${formatFooterTokens(ledger.reserveTokens)} \xB7 ${ledger.toolCount} tool definitions \xB7 compaction ${ledger.compactionAuto ? `auto${ledger.compactionThreshold === null ? "" : ` @${Math.round(ledger.compactionThreshold * 100)}%`}` : "manual"}`,
18208
19280
  width
18209
19281
  )
18210
19282
  ];
19283
+ out.push(theme2.fg("dim", "Context occupancy is per request; subscription limits are account-wide \xB7 /usage"));
18211
19284
  const gridWidth = Math.max(12, Math.min(64, width >= 76 ? Math.floor(width * 0.38) : width));
18212
19285
  const gridHeight = width >= 76 ? 8 : 3;
18213
19286
  const grid = renderContextMeterGrid(ledger, gridWidth, gridHeight, theme2);
@@ -18218,7 +19291,13 @@ function contextPage(state, width) {
18218
19291
  legendWidth
18219
19292
  )
18220
19293
  );
18221
- out.push("", rule(theme2, width, { left: "CONTEXT COMPOSITION", leftToken: "accent" }));
19294
+ out.push(
19295
+ "",
19296
+ rule(theme2, width, {
19297
+ left: state.context.budget ? "CAPTURE DIAGNOSTICS" : "CONTEXT COMPOSITION",
19298
+ leftToken: "accent"
19299
+ })
19300
+ );
18222
19301
  out.push(...width >= 76 ? zipColumns(grid, legend, gridWidth, legendWidth, " ") : [...grid, "", ...legend]);
18223
19302
  out.push(
18224
19303
  ...wrapTextWithAnsi(
@@ -18278,11 +19357,20 @@ function renderCompactDashboard(state, width) {
18278
19357
  const activity = `${theme2.fg("accent", phase)}${workers ? theme2.fg("agent", ` \xB7 ${workers} active`) : ""}`;
18279
19358
  const identity = clean(state.session.target ?? "No model selected");
18280
19359
  const thinking = theme2.fg("reason", `think ${clean(state.session.thinking ?? "off")}`);
18281
- const usage = `${formatFooterTokens(ledger?.usedTokens ?? state.context.used ?? 0)} / ${ledger?.contextWindow ?? state.context.contextWindow ? formatFooterTokens(ledger?.contextWindow ?? state.context.contextWindow ?? 0) : "?"}`;
18282
- const context = `${ledger ? renderContextMeterBar(ledger, w >= 100 ? 14 : 8, theme2) : ""} ${usage}`;
19360
+ const usage = contextUsageText(state.context);
19361
+ const context = `${ledger || state.context.budget ? contextOccupancyBar(state.context, w >= 100 ? 14 : 8, theme2) : ""} ${usage}`;
18283
19362
  const rightWidth = Math.min(Math.floor(w * 0.48), visibleWidth(context));
18284
- const identityRoom = Math.max(8, w - rightWidth - visibleWidth(thinking) - visibleWidth(activity) - 10);
18285
- const left = `${activity} \xB7 ${theme2.fg("muted", fitIdentityLabel(identity, identityRoom))} \xB7 ${thinking}`;
19363
+ const weekly = state.quotaRoute ? routeWeeklyQuota(state.quotaRoute, state.quota ?? []) : null;
19364
+ const leftRoom = Math.max(1, w - rightWidth - 3);
19365
+ const activityLabel = fit(activity, Math.min(visibleWidth(activity), Math.max(5, Math.floor(leftRoom / 3))));
19366
+ const badge = weekly && leftRoom - visibleWidth(activityLabel) - visibleWidth(weekly.label) >= 16 ? theme2.fg(
19367
+ weekly.severity === "critical" ? "error" : weekly.severity === "normal" ? "muted" : "warning",
19368
+ weekly.label
19369
+ ) : "";
19370
+ const baseRoom = leftRoom - visibleWidth(activityLabel) - (badge ? visibleWidth(badge) + 3 : 0) - 5;
19371
+ const showThinking = baseRoom - visibleWidth(thinking) - 3 >= 12;
19372
+ const identityRoom = Math.max(1, baseRoom - (showThinking ? visibleWidth(thinking) + 3 : 0));
19373
+ const left = `${activityLabel} \xB7 ${theme2.fg("muted", fitIdentityLabel(identity, identityRoom))}${badge ? ` \xB7 ${badge}` : ""}${showThinking ? ` \xB7 ${thinking}` : ""}`;
18286
19374
  const pair = (l, r, rw) => `${fit(l, w - rw - 3)} ${fit(r, rw)}`;
18287
19375
  const notice = [...state.notices].filter((n) => n.expiresAt === null || n.expiresAt > state.now).sort((a, b) => b.addedAt - a.addedAt)[0];
18288
19376
  const key = getKeybindings().getKeys("clio-coder.status.toggle").join("/") || "Dashboard";
@@ -18290,18 +19378,28 @@ function renderCompactDashboard(state, width) {
18290
19378
  const foot = urgent ? theme2.fg("warning", urgent) : notice ? theme2.fg(
18291
19379
  notice.level === "error" ? "error" : notice.level === "warning" ? "warning" : "muted",
18292
19380
  `\u2022 ${clean(notice.text)}`
18293
- ) : state.demoHint ? `${theme2.fg("accent", "Tip")} ${theme2.fg("muted", clean(state.demoHint))}` : theme2.fg(
19381
+ ) : state.demoHint ? `${theme2.fg("accent", "Tip")} ${theme2.fg("muted", clean(state.demoHint))}` : theme2.fg("dim", footerKeyHint(state.now, w < 120) ?? `${key} Dashboard`);
19382
+ const hintWidth = Math.min(Math.floor(w * 0.48), visibleWidth(foot));
19383
+ const workspaceWidth = Math.max(1, w - hintWidth - 3);
19384
+ const git = `${clean(state.workspace.branch ?? "no Git branch")}${state.workspace.dirty ? " *" : ""}`;
19385
+ const gitWidth = Math.min(visibleWidth(git), Math.max(4, Math.floor(workspaceWidth * 0.45)));
19386
+ const cwdWidth = Math.max(1, workspaceWidth - gitWidth - 3);
19387
+ const workspace = theme2.fg(
18294
19388
  "dim",
18295
- footerKeyHint(state.now, w < 80) ?? `${clean(state.workspace.cwd)} \xB7 ${clean(state.workspace.branch ?? "no Git branch")}${state.workspace.dirty ? " *" : ""}`
19389
+ `${fitIdentityLabel(clean(state.workspace.cwd), cwdWidth)} \xB7 ${fitIdentityLabel(git, gitWidth)}`
18296
19390
  );
18297
- const hint = theme2.fg("muted", `${state.session.throughput ? `${state.session.throughput} \xB7 ` : ""}${key}`);
18298
- return [
18299
- fit(pair(left, context, rightWidth)),
18300
- fit(pair(foot, hint, Math.min(Math.floor(w * 0.4), visibleWidth(hint))))
18301
- ];
19391
+ return [fit(pair(left, context, rightWidth)), fit(pair(workspace, foot, hintWidth))];
18302
19392
  }
18303
19393
  function statusPage(state, width) {
18304
19394
  const theme2 = clioTheme();
19395
+ const quotaRows = [
19396
+ theme2.style(
19397
+ "accent",
19398
+ `SESSION \xB7 ${formatFooterTokens(state.sessionTokens?.totalTokens ?? 0)} recorded tokens \xB7 ${formatCostAggregate(state.sessionCost) ?? "cost not yet priced"}`,
19399
+ { bold: true }
19400
+ ),
19401
+ ...renderQuotaAccounts(state.quota ?? [], width, { compact: true, now: state.now })
19402
+ ];
18305
19403
  const active = state.dispatchRows.filter((row2) => ACTIVE_AGENT_STATUSES.has(row2.status));
18306
19404
  const completed = state.dispatchRows.filter((row2) => row2.status === "completed").length;
18307
19405
  const failed = state.dispatchRows.filter((row2) => ["failed", "dead", "aborted"].includes(row2.status)).length;
@@ -18331,7 +19429,6 @@ function statusPage(state, width) {
18331
19429
  ["Target", state.session.target ?? "not selected"],
18332
19430
  ["Tracked cost", formatCostAggregate(state.sessionCost) ?? "not yet priced"],
18333
19431
  ["Clio ceiling", state.costCeilingUsd === void 0 ? "unknown" : `$${state.costCeilingUsd} \xB7 tracked pricing only`],
18334
- ["Provider quota", "not reported by provider"],
18335
19432
  ["MCP connected", names(state.connections?.mcp)],
18336
19433
  ["Plugins active", names(state.connections?.plugins)],
18337
19434
  [
@@ -18385,6 +19482,7 @@ function statusPage(state, width) {
18385
19482
  ];
18386
19483
  if (width < 76)
18387
19484
  return [
19485
+ ...quotaRows,
18388
19486
  ...section("COST & CONNECTIONS", left, width),
18389
19487
  ...section("LOCAL MACHINE", right, width),
18390
19488
  theme2.fg("accent", "CONTEXT ENGINE"),
@@ -18395,6 +19493,7 @@ function statusPage(state, width) {
18395
19493
  ];
18396
19494
  const col = Math.floor((width - 5) / 2);
18397
19495
  return [
19496
+ ...quotaRows,
18398
19497
  ...zipColumns(
18399
19498
  section("COST & CONNECTIONS", left, col),
18400
19499
  section("LOCAL MACHINE", right, width - col - 5),
@@ -18428,7 +19527,7 @@ function renderDashboardPage(state, page, width, terminalRows, cycleKey) {
18428
19527
  const hint = truncateToWidth(
18429
19528
  theme2.fg(
18430
19529
  "muted",
18431
- `${cycleKey || "Dashboard"} \u2192 ${next} \xB7 ${page === "Status" ? "/cost \xB7 /mcp \xB7 /library" : "composer stays active"}`
19530
+ `${cycleKey || "Dashboard"} \u2192 ${next} \xB7 ${page === "Status" ? "/usage \xB7 /mcp \xB7 /library" : "composer stays active"}`
18432
19531
  ),
18433
19532
  safeWidth,
18434
19533
  "\u2026",
@@ -18441,7 +19540,7 @@ function renderDashboardPage(state, page, width, terminalRows, cycleKey) {
18441
19540
  else content = statusPage(state, safeWidth);
18442
19541
  content = content.flatMap((line) => visibleWidth(line) > safeWidth ? wrapTextWithAnsi(line, safeWidth) : [line]);
18443
19542
  if (content.length > available) {
18444
- const detail = page === "Activity" ? `${getKeybindings().getKeys("clio-coder.dispatchBoard.toggle").join("/") || "Fleet Runs"} \xB7 /view` : page === "Context" ? "/context" : "/cost \xB7 /context";
19543
+ const detail = page === "Activity" ? `${getKeybindings().getKeys("clio-coder.dispatchBoard.toggle").join("/") || "Fleet Runs"} \xB7 /view` : page === "Context" ? "/context" : "/usage \xB7 /context";
18445
19544
  content = [
18446
19545
  ...content.slice(0, available - 1),
18447
19546
  truncateToWidth(theme2.fg("dim", `More detail: ${detail}`), safeWidth, "\u2026", true)
@@ -18453,7 +19552,7 @@ function renderDashboardPage(state, page, width, terminalRows, cycleKey) {
18453
19552
 
18454
19553
  // src/interactive/footer/system-metrics.ts
18455
19554
  init_esm_shims();
18456
- import { readdir, readFile } from "node:fs/promises";
19555
+ import { readdir, readFile as readFile4 } from "node:fs/promises";
18457
19556
  import { cpus, freemem, platform, release, totalmem } from "node:os";
18458
19557
  function counterRate(current, previous, elapsedMs) {
18459
19558
  return elapsedMs > 0 && current >= previous && Number.isFinite(current) && Number.isFinite(previous) ? (current - previous) * 1e3 / elapsedMs : null;
@@ -18492,7 +19591,7 @@ function busiest(current, previous, elapsed) {
18492
19591
  });
18493
19592
  return rates.sort((a, b) => b.first + b.second - a.first - a.second)[0] ?? null;
18494
19593
  }
18495
- var read = (path) => readFile(path, "utf8").catch(() => null);
19594
+ var read = (path) => readFile4(path, "utf8").catch(() => null);
18496
19595
  var numeric = async (path) => {
18497
19596
  const raw = await read(path);
18498
19597
  if (raw === null || raw.trim() === "") return null;
@@ -18795,6 +19894,13 @@ function buildFooterDashboard(deps) {
18795
19894
  const toolProfile = settings?.integrations.externalAgents?.defaults?.toolGovernance ?? "clio-coder-policy";
18796
19895
  return {
18797
19896
  resources: machine.snapshot(),
19897
+ quota: deps.getQuotaSnapshots?.() ?? [],
19898
+ ...current?.runtime && wireModelId ? {
19899
+ quotaRoute: {
19900
+ runtimeId: current.runtime.id,
19901
+ wireModelId
19902
+ }
19903
+ } : {},
18798
19904
  ...deps.getConnections ? { connections: deps.getConnections() } : {},
18799
19905
  ...settings ? { costCeilingUsd: settings.safety.limits.sessionCostUsd } : {},
18800
19906
  workspace: workspaceFacts(deps, branchSlot),
@@ -18824,6 +19930,13 @@ function buildFooterDashboard(deps) {
18824
19930
  } : null
18825
19931
  },
18826
19932
  context: {
19933
+ ...contextUsage?.revision ? {
19934
+ budget: {
19935
+ revision: contextUsage.revision,
19936
+ historical: contextUsage.historical ?? true,
19937
+ inputSource: contextUsage.inputSource ?? "unknown"
19938
+ }
19939
+ } : {},
18827
19940
  label: null,
18828
19941
  used: contextUsage?.tokens ?? null,
18829
19942
  contextWindow: contextUsage?.contextWindow ?? null,
@@ -19005,8 +20118,8 @@ init_esm_shims();
19005
20118
  init_esm_shims();
19006
20119
  import { execFile as execFile2 } from "node:child_process";
19007
20120
  import { open, readdir as readdir2, stat } from "node:fs/promises";
19008
- import { homedir as homedir2 } from "node:os";
19009
- import { basename as basename2, dirname, join, relative as relative4, resolve as resolve3, sep } from "node:path";
20121
+ import { homedir as homedir5 } from "node:os";
20122
+ import { basename as basename2, dirname, join as join4, relative as relative4, resolve as resolve3, sep } from "node:path";
19010
20123
  var MAX_COMPLETION_ROWS = 40;
19011
20124
  var PREVIEW_BYTES = 2048;
19012
20125
  var PREVIEW_CHARS = 120;
@@ -19054,7 +20167,7 @@ function buildWorkspaceSnapshot(basePath, visibleFiles, trackedFiles) {
19054
20167
  const tracked = trackedFiles.has(path);
19055
20168
  files.push({
19056
20169
  path,
19057
- absolutePath: join(basePath, path),
20170
+ absolutePath: join4(basePath, path),
19058
20171
  isDirectory: false,
19059
20172
  tracked,
19060
20173
  descendantFiles: 0,
@@ -19072,7 +20185,7 @@ function buildWorkspaceSnapshot(basePath, visibleFiles, trackedFiles) {
19072
20185
  const directories = [...directoryFacts].map(
19073
20186
  ([path, facts]) => ({
19074
20187
  path,
19075
- absolutePath: join(basePath, path),
20188
+ absolutePath: join4(basePath, path),
19076
20189
  isDirectory: true,
19077
20190
  tracked: facts.tracked,
19078
20191
  descendantFiles: facts.descendantFiles,
@@ -19129,7 +20242,7 @@ function explicitPathQuery(query) {
19129
20242
  return query.startsWith("../") || query.startsWith("~/") || query.startsWith("/");
19130
20243
  }
19131
20244
  function resolveDisplayBase(basePath, displayBase) {
19132
- if (displayBase.startsWith("~/")) return resolve3(homedir2(), displayBase.slice(2));
20245
+ if (displayBase.startsWith("~/")) return resolve3(homedir5(), displayBase.slice(2));
19133
20246
  if (displayBase.startsWith("/")) return resolve3(displayBase);
19134
20247
  return resolve3(basePath, displayBase);
19135
20248
  }
@@ -19147,7 +20260,7 @@ async function explicitCandidates(basePath, query, trackedFiles) {
19147
20260
  const candidates = [];
19148
20261
  for (const entry of await readdir2(directory, { withFileTypes: true })) {
19149
20262
  if (entry.name === ".git" || /[\r\n\0]/u.test(entry.name)) continue;
19150
- const absolutePath = join(directory, entry.name);
20263
+ const absolutePath = join4(directory, entry.name);
19151
20264
  let isDirectory = entry.isDirectory();
19152
20265
  let isFile = entry.isFile();
19153
20266
  if (!isDirectory && !isFile && entry.isSymbolicLink()) {
@@ -19799,6 +20912,12 @@ function createInteractivePresentation(deps) {
19799
20912
  const key = keybindings.getKeys("tui.input.submit")[0];
19800
20913
  return key === void 0 || key.length === 0 ? null : formatKeyLabel(key, "");
19801
20914
  };
20915
+ const quotaSummary = createQuotaSummaryFeed({
20916
+ onUpdate: () => {
20917
+ footer?.refresh();
20918
+ requestRender();
20919
+ }
20920
+ });
19802
20921
  const banner = factories.createBanner({
19803
20922
  providers: deps.providers,
19804
20923
  ...deps.agents ? { getAgentCount: () => deps.agents?.listSpecs().filter(isUserVisibleAgent).length ?? 0 } : {},
@@ -19813,6 +20932,9 @@ function createInteractivePresentation(deps) {
19813
20932
  return key ? formatKeyLabel(key, "") : null;
19814
20933
  },
19815
20934
  onFactsRefreshed: requestRender,
20935
+ // Subscription usage is read lazily off the render path and cached, so the
20936
+ // banner reads a string and never awaits a provider.
20937
+ getQuotaSummary: () => quotaSummary.peek(),
19816
20938
  ...deps.getSettings ? { getSettings: deps.getSettings } : {}
19817
20939
  });
19818
20940
  if (deps.startsResumed === true) banner.collapseToSessionHeader();
@@ -19945,6 +21067,7 @@ function createInteractivePresentation(deps) {
19945
21067
  getSessionTokens: () => observabilitySnapshot.session.tokens,
19946
21068
  getTokenThroughput: () => liveThroughput === null ? observabilitySnapshot.session.latestThroughput : currentLiveThroughput(),
19947
21069
  getSessionCost: () => observabilitySnapshot.session.cost,
21070
+ getQuotaSnapshots: () => quotaSummary.peekSnapshots(),
19948
21071
  getContextUsage: () => deps.chat.contextUsage(),
19949
21072
  getContextLedger: () => deps.chat.contextLedger(),
19950
21073
  getDispatchRows: () => dispatchBoardStore.rows(),
@@ -20145,6 +21268,7 @@ function createInteractivePresentation(deps) {
20145
21268
  const disposeBeforeStatus = () => {
20146
21269
  if (beforeStatusDisposed) return;
20147
21270
  beforeStatusDisposed = true;
21271
+ quotaSummary.dispose();
20148
21272
  banner.dispose();
20149
21273
  footer.dispose();
20150
21274
  unsubscribeObservability();
@@ -20186,6 +21310,7 @@ function createInteractivePresentation(deps) {
20186
21310
  },
20187
21311
  changeOutputStyle: (mutation) => chatRenderer.mutate(() => preserveTranscriptScroll(transcriptView, deps.terminal.columns, mutation), "output-style"),
20188
21312
  getObservabilitySnapshot: () => observabilitySnapshot,
21313
+ getQuotaSnapshots: () => quotaSummary.peekSnapshots(),
20189
21314
  recordChatEvent,
20190
21315
  recordToolStart: (toolCallId, toolName) => {
20191
21316
  footerActiveTools.add(toolCallId);
@@ -20223,7 +21348,7 @@ function createInteractivePresentation(deps) {
20223
21348
  // src/interactive/interactive-slash-runtime.ts
20224
21349
  init_esm_shims();
20225
21350
  import { mkdirSync, writeFileSync } from "node:fs";
20226
- import { join as join2, resolve as resolve4 } from "node:path";
21351
+ import { join as join5, resolve as resolve4 } from "node:path";
20227
21352
 
20228
21353
  // src/interactive/context-recall-command.ts
20229
21354
  init_esm_shims();
@@ -20560,6 +21685,13 @@ function renderDoctorReport(findings, width) {
20560
21685
 
20561
21686
  // src/interactive/interactive-slash-runtime.ts
20562
21687
  var EXPORT_RENDER_WIDTH = 100;
21688
+ function longestBacktickRun(lines) {
21689
+ let longest = 0;
21690
+ for (const line of lines) {
21691
+ for (const run of line.match(/`+/gu) ?? []) longest = Math.max(longest, run.length);
21692
+ }
21693
+ return longest;
21694
+ }
20563
21695
  function resolveThinkingCapability(providers, settings) {
20564
21696
  const resolved = resolveModelRuntimeCapabilitiesForProviders(
20565
21697
  providers,
@@ -20754,7 +21886,7 @@ function createInteractiveSlashRuntime(deps) {
20754
21886
  deps.requestRender();
20755
21887
  },
20756
21888
  runDoctor: async ({ deep }) => {
20757
- const { collectDoctorFindings, doctorNotice } = await import("./doctor-S5KZ2GTN.js");
21889
+ const { collectDoctorFindings, doctorNotice } = await import("./doctor-53AVQXAO.js");
20758
21890
  const autonomy = deps.getSettings?.().safety.autonomy;
20759
21891
  const findings = await collectDoctorFindings({
20760
21892
  workspaceRoot: cwd(),
@@ -20792,7 +21924,7 @@ function createInteractiveSlashRuntime(deps) {
20792
21924
  };
20793
21925
  return options.dryRun ? deps.share.planImport(resolve4(archivePath), importOptions) : deps.share.importArchive(resolve4(archivePath), importOptions);
20794
21926
  },
20795
- openCost: deps.openCost,
21927
+ openUsage: deps.openUsage,
20796
21928
  openSideQuestion: deps.openSideQuestion,
20797
21929
  // `/oracle` is refused, never queued, while a turn streams: the digest it
20798
21930
  // packs describes the record as it stands.
@@ -20898,6 +22030,15 @@ function createInteractiveSlashRuntime(deps) {
20898
22030
  deps.setEditorText(text);
20899
22031
  deps.requestRender();
20900
22032
  },
22033
+ runHandoffRecovery: (handoffId, action) => {
22034
+ if (!deps.onRecoverHandoff) {
22035
+ appendCommandNotice("error", "[/context recover] native handoff recovery is unavailable");
22036
+ return;
22037
+ }
22038
+ void deps.onRecoverHandoff(handoffId, action).catch(
22039
+ (error) => appendCommandNotice("error", `[/context recover] ${error instanceof Error ? error.message : String(error)}`)
22040
+ );
22041
+ },
20901
22042
  runCompact: (instructions) => {
20902
22043
  runCompactWithNotice(deps.onCompact, appendCommandNotice, instructions);
20903
22044
  },
@@ -20911,23 +22052,29 @@ function createInteractiveSlashRuntime(deps) {
20911
22052
  const turns = deps.readStructuredEntries(sessionId);
20912
22053
  const exportPanel = createChatPanel({ unboundedToolBodies: true, getOutputStyle: () => "detailed" });
20913
22054
  const leafTurnId = deps.session?.tree(sessionId).leafId ?? null;
20914
- rehydrateChatPanelFromTurns(exportPanel, turns, {
20915
- unboundedToolBodies: true,
20916
- ...leafTurnId ? { activeLeafTurnId: leafTurnId } : {}
20917
- });
22055
+ rehydrateChatPanelFromTurns(
22056
+ exportPanel,
22057
+ turns,
22058
+ withContinuityReplay(
22059
+ turns,
22060
+ { unboundedToolBodies: true, ...leafTurnId ? { activeLeafTurnId: leafTurnId } : {} },
22061
+ deps.session
22062
+ )
22063
+ );
20918
22064
  const ansiLines = exportPanel.render(EXPORT_RENDER_WIDTH);
20919
22065
  const exportedAt = deps.now?.() ?? /* @__PURE__ */ new Date();
20920
22066
  const date = dateLocal(exportedAt);
20921
22067
  const requestedPath = pathArg?.trim() ?? "";
20922
22068
  const markdown = requestedPath.toLowerCase().endsWith(".md");
20923
22069
  const target = resolve4(
20924
- requestedPath.length > 0 ? requestedPath : join2(".clio-coder", "exports", `${sessionId}-${date}.html`)
22070
+ requestedPath.length > 0 ? requestedPath : join5(".clio-coder", "exports", `${sessionId}-${date}.html`)
20925
22071
  );
20926
22072
  mkdirSync(resolve4(target, ".."), { recursive: true });
20927
22073
  if (markdown) {
20928
22074
  const lines = ansiLines.map(stripTerminalSequences);
20929
- const header = [`# Clio session ${sessionId}`, "", `Exported ${exportedAt.toISOString()}`, "", "```text"];
20930
- writeFileSync(target, `${[...header, ...lines, "```", ""].join("\n")}`, "utf8");
22075
+ const fence = "`".repeat(Math.max(3, longestBacktickRun(lines) + 1));
22076
+ const header = [`# Clio session ${sessionId}`, "", `Exported ${exportedAt.toISOString()}`, "", `${fence}text`];
22077
+ writeFileSync(target, `${[...header, ...lines, fence, ""].join("\n")}`, "utf8");
20931
22078
  } else {
20932
22079
  writeFileSync(target, renderSessionHtml({ sessionId, exportedAt: exportedAt.toISOString(), ansiLines }), "utf8");
20933
22080
  }
@@ -20970,7 +22117,7 @@ function createInteractiveSlashRuntime(deps) {
20970
22117
  runContextRecall: (ref) => {
20971
22118
  const session = deps.session;
20972
22119
  if (!session) return;
20973
- const outcome = runOperatorRecall(ref, {
22120
+ const outcome2 = runOperatorRecall(ref, {
20974
22121
  hasSession: () => session.current() !== null,
20975
22122
  readEntries: () => {
20976
22123
  const sessionId = deps.chat.getSessionId();
@@ -20984,12 +22131,12 @@ function createInteractiveSlashRuntime(deps) {
20984
22131
  onRecalled: (payload) => deps.bus.emit(BusChannels.ContextRecalled, payload),
20985
22132
  ...deps.now ? { now: () => (deps.now?.() ?? /* @__PURE__ */ new Date()).getTime() } : {}
20986
22133
  });
20987
- if (!outcome.ok) {
20988
- appendCommandNotice("error", outcome.message);
22134
+ if (!outcome2.ok) {
22135
+ appendCommandNotice("error", outcome2.message);
20989
22136
  return;
20990
22137
  }
20991
- appendCommandNotice("success", outcome.headline);
20992
- deps.io.stdout(`${outcome.body}
22138
+ appendCommandNotice("success", outcome2.headline);
22139
+ deps.io.stdout(`${outcome2.body}
20993
22140
  `);
20994
22141
  deps.requestRender();
20995
22142
  }
@@ -21188,7 +22335,11 @@ function createInteractiveTickers(deps) {
21188
22335
  taskIslandHidden = hidden;
21189
22336
  if (rows.length > 0)
21190
22337
  taskIsland.setText(
21191
- formatTaskIslandLines(rows, Math.max(1, Math.min(4, Math.floor((taskIslandHeight - 8) / 9)))).join("\n")
22338
+ formatTaskIslandLines(
22339
+ rows,
22340
+ Math.max(1, Math.min(4, Math.floor((taskIslandHeight - 8) / 10))),
22341
+ deps.getQuotaSnapshots?.() ?? []
22342
+ ).join("\n")
21192
22343
  );
21193
22344
  else if (board) taskIsland.setText(formatTaskBoardIslandLines(board).join("\n"));
21194
22345
  taskIsland.invalidate();
@@ -21297,7 +22448,8 @@ function createSessionTranscript(deps) {
21297
22448
  deps.chat.resetForSession(
21298
22449
  leafTurnId,
21299
22450
  buildModelReplayAgentMessagesFromTurns(turns, {
21300
- ...leafTurnId ? { activeLeafTurnId: leafTurnId } : {}
22451
+ ...leafTurnId ? { activeLeafTurnId: leafTurnId } : {},
22452
+ continuity: continuityContextFromSession(deps.session)
21301
22453
  })
21302
22454
  );
21303
22455
  deps.refreshStatus();
@@ -21810,6 +22962,7 @@ async function createInteractiveApplication(deps) {
21810
22962
  ...deps.onSelectModel ? { onSelectModel: deps.onSelectModel } : {},
21811
22963
  ...deps.onSetThinkingLevel ? { onSetThinkingLevel: deps.onSetThinkingLevel } : {},
21812
22964
  ...deps.onCompact ? { onCompact: deps.onCompact } : {},
22965
+ ...deps.onRecoverHandoff ? { onRecoverHandoff: deps.onRecoverHandoff } : {},
21813
22966
  ...deps.onInit ? { onInit: deps.onInit } : {},
21814
22967
  ...deps.onContextClear ? { onContextClear: deps.onContextClear } : {},
21815
22968
  ...deps.onContextRefresh ? { onContextRefresh: deps.onContextRefresh } : {},
@@ -21823,7 +22976,7 @@ async function createInteractiveApplication(deps) {
21823
22976
  expandSubmit: (text) => expandInteractiveSubmitAsync(text, deps.resources),
21824
22977
  openAskUser: (questions, options) => openAskUserOverlayState(questions, options),
21825
22978
  openSkillsHub: (request) => openSkillsHubState(request),
21826
- openCost: () => openCostOverlayState(),
22979
+ openUsage: () => openUsageOverlayState(),
21827
22980
  openSideQuestion: (question) => openSideQuestionOverlayState(question),
21828
22981
  startHandoff: (goal) => startHandoffState(goal),
21829
22982
  startFleetRun: (name, vars) => startFleetRunState(name, vars),
@@ -21879,6 +23032,7 @@ async function createInteractiveApplication(deps) {
21879
23032
  getTerminationCoordinator().onDrain(() => editorSubmit.shutdownEditorBash(), { timeoutMs: EDITOR_BASH_SHUTDOWN_MS });
21880
23033
  editor.onSubmit = editorSubmit.submitEditorText;
21881
23034
  const interactiveTickers = createInteractiveTickers({
23035
+ getQuotaSnapshots: presentation.getQuotaSnapshots,
21882
23036
  tui,
21883
23037
  dispatchBoardStore,
21884
23038
  contextActivityStore,
@@ -21897,6 +23051,8 @@ async function createInteractiveApplication(deps) {
21897
23051
  resetTranscript();
21898
23052
  };
21899
23053
  overlayLifecycle = createOverlayLifecycle({
23054
+ getQuotaSnapshots: presentation.getQuotaSnapshots,
23055
+ getDispatchRows: () => dispatchBoardStore.rows(),
21900
23056
  app: deps,
21901
23057
  tui,
21902
23058
  footer,
@@ -21932,7 +23088,7 @@ async function createInteractiveApplication(deps) {
21932
23088
  const {
21933
23089
  closeOverlay,
21934
23090
  openAskUserOverlayState,
21935
- openCostOverlayState,
23091
+ openUsageOverlayState,
21936
23092
  openSideQuestionOverlayState,
21937
23093
  startHandoffState,
21938
23094
  startFleetRunState,
@@ -21999,11 +23155,11 @@ async function createInteractiveApplication(deps) {
21999
23155
  );
22000
23156
  };
22001
23157
  const backgroundActiveDispatch = () => {
22002
- const outcome = deps.onBackgroundDispatch?.() ?? {
23158
+ const outcome2 = deps.onBackgroundDispatch?.() ?? {
22003
23159
  ok: false,
22004
23160
  message: "background: dispatch backgrounding is unavailable in this session"
22005
23161
  };
22006
- notify(outcome.ok ? "success" : "info", outcome.message, "dispatch:background");
23162
+ notify(outcome2.ok ? "success" : "info", outcome2.message, "dispatch:background");
22007
23163
  };
22008
23164
  const mux = deps.mux;
22009
23165
  const muxBridge = mux && mux.mode !== "none" && deps.createMuxBridge ? deps.createMuxBridge({
@@ -22285,7 +23441,7 @@ async function createInteractiveApplication(deps) {
22285
23441
  });
22286
23442
  const settings = deps.getSettings?.();
22287
23443
  if (settings)
22288
- void import("./startup-background-KDWCQMWT.js").then(
23444
+ void import("./startup-background-JR2KDK6R.js").then(
22289
23445
  ({ prepareWorkerTargets }) => prepareWorkerTargets({
22290
23446
  settings,
22291
23447
  providers: deps.providers,
@@ -22378,4 +23534,4 @@ export {
22378
23534
  routeOverlayKey,
22379
23535
  startInteractive
22380
23536
  };
22381
- //# sourceMappingURL=interactive-OXGYZYAS.js.map
23537
+ //# sourceMappingURL=interactive-JRCBENLZ.js.map