@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.
- package/.claude-plugin/marketplace.json +4 -4
- package/CHANGELOG.md +35 -0
- package/CONTRIBUTING.md +9 -0
- package/README.md +12 -9
- package/ROADMAP.md +30 -5
- package/dist/{acp-KS7ARGV6.js → acp-F6PGOFZ4.js} +2 -2
- package/dist/{agents-YOGJ2YEJ.js → agents-ZHP4R6EX.js} +22 -21
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-CEDCO6O6.js → auth-N75U2H3Z.js} +4 -4
- package/dist/{chunk-D3KWFOUT.js → chunk-23UIXH6Z.js} +3 -3
- package/dist/{chunk-ZR5YA6ZF.js → chunk-36XVK5P5.js} +5 -5
- package/dist/{chunk-AVSR2HKI.js → chunk-3DZ4LPLS.js} +6 -6
- package/dist/{chunk-LYJJ6546.js → chunk-3GSPCWFX.js} +12 -12
- package/dist/{chunk-CRHPCPZP.js → chunk-4KC4CHKJ.js} +2 -2
- package/dist/{chunk-4VDHLCF5.js → chunk-4WJBWKMI.js} +2 -2
- package/dist/{chunk-KBUTSJSR.js → chunk-4XBYHXGK.js} +2 -2
- package/dist/{chunk-6W75D37F.js → chunk-53P775GE.js} +6 -3
- package/dist/{chunk-XZWSP67B.js → chunk-5APYAXCB.js} +2 -2
- package/dist/{chunk-D65E25JG.js → chunk-67HPYNTJ.js} +2 -2
- package/dist/{chunk-5JQR5B4D.js → chunk-6KV4KCQZ.js} +4 -4
- package/dist/{chunk-V4XJWDHR.js → chunk-6UKFRTXI.js} +2 -1
- package/dist/{chunk-NDPF2N2L.js → chunk-7DW7CJW2.js} +2 -2
- package/dist/{chunk-5KQEUIWM.js → chunk-7LUEQODV.js} +4 -4
- package/dist/{chunk-LNOXCGN2.js → chunk-7UFDIOBT.js} +3 -3
- package/dist/{chunk-DXSETN72.js → chunk-7WUGNN43.js} +2 -2
- package/dist/{chunk-V35KZDUZ.js → chunk-AA3GRZWS.js} +2 -2
- package/dist/{chunk-X4CEFOSG.js → chunk-ADO3OQIC.js} +4 -4
- package/dist/{chunk-UX2L5CUD.js → chunk-AI36V3ON.js} +3 -3
- package/dist/{chunk-B7GY6QER.js → chunk-B26OUMJW.js} +2 -2
- package/dist/{chunk-LAR7DX5E.js → chunk-B5TASTCK.js} +153 -62
- package/dist/{chunk-TGWZXJ7L.js → chunk-BEMQK5Q7.js} +92 -84
- package/dist/{chunk-RNDCROSG.js → chunk-CNXU7TR2.js} +3 -3
- package/dist/{chunk-3JLTRPN7.js → chunk-CVMUXT64.js} +35 -66
- package/dist/{chunk-733ZBT5P.js → chunk-E74MJF6G.js} +4 -4
- package/dist/{chunk-T2MDZES3.js → chunk-EBMTBE76.js} +3 -3
- package/dist/{chunk-OZ76TLGU.js → chunk-EEITU7S6.js} +2 -2
- package/dist/{chunk-GRHOXDKB.js → chunk-ELDJWJKM.js} +97 -14
- package/dist/{chunk-GCQFN7YI.js → chunk-EMYEQLWO.js} +2 -2
- package/dist/{chunk-LTWH2ACR.js → chunk-EQPFEDFV.js} +222 -127
- package/dist/{chunk-TDGYTWM6.js → chunk-F4I3H56H.js} +2 -2
- package/dist/chunk-F6KCJO3U.js +523 -0
- package/dist/{chunk-FGSQHUCK.js → chunk-F6VKWTTY.js} +5 -5
- package/dist/{chunk-UJD5MPP6.js → chunk-FLNXQQ5B.js} +2 -2
- package/dist/{chunk-EX57VVWW.js → chunk-FOSRJ2VZ.js} +4 -4
- package/dist/{chunk-ZARZ4POR.js → chunk-G5ZDQQE3.js} +3 -3
- package/dist/{chunk-HQVN7G4F.js → chunk-G62JBPXN.js} +7 -7
- package/dist/{chunk-ZJPORZTC.js → chunk-GIAI7A2K.js} +2 -2
- package/dist/chunk-GPTR6OTU.js +2551 -0
- package/dist/{chunk-EZD2BXGP.js → chunk-GUFQPTQK.js} +2 -2
- package/dist/{chunk-OPD7GL6F.js → chunk-H6FQVNZC.js} +8 -7
- package/dist/chunk-H7REJ7L5.js +80 -0
- package/dist/{chunk-EYTLV3W3.js → chunk-HTWTORF7.js} +2 -2
- package/dist/{chunk-DF6PI6GN.js → chunk-I6UKQPGS.js} +3 -3
- package/dist/{chunk-ISHUS7HC.js → chunk-JRW236JT.js} +3 -3
- package/dist/{chunk-DE2LU267.js → chunk-KKAJGT3B.js} +3 -2
- package/dist/{chunk-OV62D6KO.js → chunk-KQMYL5GR.js} +3 -3
- package/dist/{chunk-QDUUOZ3S.js → chunk-KWMFC7FN.js} +3 -3
- package/dist/{chunk-R7FGRZNF.js → chunk-LXYHCCIZ.js} +9 -9
- package/dist/{chunk-NFSK2VNU.js → chunk-LZMTVVN4.js} +2 -2
- package/dist/{chunk-62UK7AGW.js → chunk-M5PMRZSR.js} +397 -44
- package/dist/chunk-MMZMM6SW.js +131 -0
- package/dist/{chunk-FFYTC2KQ.js → chunk-MNABNEBX.js} +2 -2
- package/dist/{chunk-XMDBTYCH.js → chunk-N3CWOGVF.js} +3 -3
- package/dist/{chunk-VYRQHORQ.js → chunk-NMQSO6Z6.js} +4 -3
- package/dist/{chunk-R2UUNSNQ.js → chunk-O3ZAF7UY.js} +18 -5
- package/dist/{chunk-4SYHGVDE.js → chunk-OQNCLZI4.js} +4 -4
- package/dist/{chunk-EG4ARXPC.js → chunk-OX6QTA4N.js} +3 -3
- package/dist/{chunk-P4GNZPXO.js → chunk-OYUS3UZP.js} +2 -2
- package/dist/{chunk-OQ2EOAHA.js → chunk-PF36KOGI.js} +291 -112
- package/dist/{chunk-55RMDDFM.js → chunk-PGQG7ZMW.js} +2 -2
- package/dist/{chunk-CBAQPTDU.js → chunk-PIU6BXXW.js} +2 -2
- package/dist/{chunk-EKV4UBCM.js → chunk-PZBQUJ2F.js} +2 -2
- package/dist/{chunk-7RET77VB.js → chunk-Q2HKY32Y.js} +2 -2
- package/dist/{chunk-U6YTUVOX.js → chunk-Q4RMNWMZ.js} +4 -4
- package/dist/chunk-Q6FRR3AQ.js +48 -0
- package/dist/{chunk-6RJMYYIE.js → chunk-QJSIAJBY.js} +3 -3
- package/dist/{chunk-DOUISWEE.js → chunk-QOJ4ERMU.js} +5 -5
- package/dist/{chunk-IFPDAD7R.js → chunk-RWSRKXLT.js} +3 -3
- package/dist/{chunk-T33IYTZM.js → chunk-SAASVMIY.js} +3 -3
- package/dist/{chunk-2DTIWSAW.js → chunk-SEQDH6JC.js} +3 -3
- package/dist/{chunk-BRCOSE7O.js → chunk-SZUO5BSE.js} +2 -2
- package/dist/{chunk-HOEDA42N.js → chunk-T7HZ7KJF.js} +2 -2
- package/dist/{chunk-OJXOA6YU.js → chunk-TL6LNERH.js} +2 -2
- package/dist/{chunk-UCINIRHW.js → chunk-TPCZXWLT.js} +3 -3
- package/dist/{chunk-ZUKUCZYZ.js → chunk-TQ2KTH4A.js} +2 -2
- package/dist/{chunk-4WOOTKFD.js → chunk-U3FETNQB.js} +341 -20
- package/dist/{chunk-5B3RTYAK.js → chunk-UGDSB4AN.js} +2 -2
- package/dist/{chunk-VKHLUZNO.js → chunk-UZ7YBL43.js} +3 -3
- package/dist/{chunk-QNUQ7K7D.js → chunk-VKMSEO7Y.js} +4 -4
- package/dist/{chunk-IN7DGBVS.js → chunk-VLX5VZ35.js} +5 -5
- package/dist/{chunk-SJGS3GDI.js → chunk-WAGBMMNX.js} +5 -5
- package/dist/{chunk-BFOSV5EZ.js → chunk-WX2YCH7F.js} +2 -2
- package/dist/{chunk-3LT34CAM.js → chunk-XBIGUILU.js} +32 -17
- package/dist/{chunk-XDHUDE5K.js → chunk-XKYBFRWR.js} +5 -5
- package/dist/{chunk-U5QU5ZOD.js → chunk-XYPWFSU5.js} +3 -3
- package/dist/{chunk-YBUECLAF.js → chunk-YEJQDODA.js} +2 -2
- package/dist/{chunk-YQ6XEFVK.js → chunk-Z74OGONW.js} +39 -7
- package/dist/{chunk-I4ELN5BX.js → chunk-ZTNOEJRI.js} +2 -2
- package/dist/cli/index.js +26 -26
- package/dist/{clio-K2PBVCEM.js → clio-4IKFE25F.js} +2 -2
- package/dist/{clio-context-tools-GR4OHFSO.js → clio-context-tools-OHJXYX4R.js} +25 -23
- package/dist/{code-nav-4X3OI6DK.js → code-nav-XPM3MY5I.js} +7 -7
- package/dist/{config-Q5J7UBAB.js → config-343YS25T.js} +43 -41
- package/dist/{config-graph-P3555574.js → config-graph-WI263KDX.js} +43 -41
- package/dist/{configure-KIJ32ZO6.js → configure-GZDG4HER.js} +21 -21
- package/dist/{context-LFBQOSXT.js → context-4OFO7N7Y.js} +12 -12
- package/dist/{context-WBH3KSVR.js → context-JLK2RLOG.js} +42 -40
- package/dist/{context-NNLBES2Y.js → context-O6DIWCF6.js} +23 -21
- package/dist/{context-clear-YOHMQ6SQ.js → context-clear-MR7KOGQI.js} +42 -40
- package/dist/{context-working-set-MRAEVDNV.js → context-working-set-YZAD3KWO.js} +14 -13
- package/dist/{data-tool-JJPBGMQJ.js → data-tool-EKD7DFHG.js} +7 -7
- package/dist/{detail-RHJOTGN4.js → detail-YZPHFEVJ.js} +43 -41
- package/dist/{dispatch-runner-NC7PYNIO.js → dispatch-runner-VKVXVTWJ.js} +44 -42
- package/dist/{doctor-S5KZ2GTN.js → doctor-53AVQXAO.js} +17 -16
- package/dist/{doctor-deep-2EJQSHIZ.js → doctor-deep-BLRB2NCG.js} +5 -5
- package/dist/{eval-3D6M36N7.js → eval-P3BQSLQE.js} +21 -20
- package/dist/{evidence-XNVPPLVK.js → evidence-6LLI6SZH.js} +44 -42
- package/dist/{evidence-6IYN52ET.js → evidence-HTN3JLUO.js} +42 -40
- package/dist/{evidence-JVECPI3F.js → evidence-NJRE3R5H.js} +42 -40
- package/dist/{evolve-TLPUDJDM.js → evolve-WWZHJVJP.js} +42 -40
- package/dist/{fleet-WKVBF5SZ.js → fleet-GGZ4BVJE.js} +64 -62
- package/dist/{fleet-6RJWIOEO.js → fleet-QG3IIWWM.js} +44 -42
- package/dist/{fleet-commands-L6WFT7KZ.js → fleet-commands-J2ZIKISQ.js} +7 -7
- package/dist/{fleet-decisions-BTQMWU5A.js → fleet-decisions-Y2Q7375Q.js} +8 -8
- package/dist/{fleet-graph-553DZ67Q.js → fleet-graph-TCBCCCBP.js} +11 -11
- package/dist/{fleet-inspect-236FZPRB.js → fleet-inspect-KOLKBC56.js} +44 -42
- package/dist/{fleet-preflight-NZQHABWO.js → fleet-preflight-44UNCC4Y.js} +27 -25
- package/dist/{fleet-validate-7NLAE26R.js → fleet-validate-K2FXRZVA.js} +13 -13
- package/dist/{fleet-verify-E3RO6A5W.js → fleet-verify-AS444WDW.js} +42 -40
- package/dist/{fleet-view-KMZQY7YJ.js → fleet-view-X4JU4J6O.js} +43 -41
- package/dist/gui/ops-worker.js +9 -9
- package/dist/gui/reads-worker.js +9 -8
- package/dist/{init-DV5B6HQG.js → init-VSOWFHVL.js} +54 -52
- package/dist/{interactive-OXGYZYAS.js → interactive-JRCBENLZ.js} +1733 -577
- package/dist/{interop-J7UOBOSF.js → interop-JR6BCY7R.js} +10 -10
- package/dist/{inventory-OEGIL6PY.js → inventory-3JUGLVJI.js} +43 -41
- package/dist/{library-ROZAMUIL.js → library-7BOH3Y7N.js} +15 -15
- package/dist/{library-B3UPGY4V.js → library-7PFONXUI.js} +9 -9
- package/dist/{library-MXQA5DLT.js → library-TUYZMFIA.js} +7 -7
- package/dist/{library-import-2LTWOVBD.js → library-import-TJVXKLDK.js} +10 -10
- package/dist/{library-inventory-LJ3XHLWO.js → library-inventory-MD3AYTP4.js} +9 -9
- package/dist/{library-validation-DARHHUNW.js → library-validation-ZDLITEA3.js} +7 -7
- package/dist/{mcp-2GSPYQ54.js → mcp-EFCWUPUR.js} +3 -3
- package/dist/{memory-J3STUUIK.js → memory-Q2YVOKK5.js} +42 -40
- package/dist/{models-Y3GMFJ2V.js → models-M7BOCW7F.js} +17 -16
- package/dist/{monitor-LHNH577J.js → monitor-2HD3MC67.js} +47 -45
- package/dist/{orchestrator-DTAPXULA.js → orchestrator-OUW3ZN6Y.js} +1894 -226
- package/dist/{panes-A743ONZ5.js → panes-TTPPSYCL.js} +3 -3
- package/dist/{preload-OJBMAIXF.js → preload-A47C2NUF.js} +42 -40
- package/dist/{providers-CMETPZNS.js → providers-T43W7EL4.js} +4 -4
- package/dist/{resources-FY6XYFN2.js → resources-TP6X2V6W.js} +20 -11
- package/dist/{run-H64MEVTA.js → run-LDSBPMCS.js} +61 -59
- package/dist/{share-CAFMMQLM.js → share-URHKKMHR.js} +9 -9
- package/dist/{skills-ERKQUC7L.js → skills-E477MOOO.js} +13 -11
- package/dist/{skills-eval-CQ23MYQK.js → skills-eval-AIBR2L7H.js} +583 -93
- package/dist/{skills-inventory-TVUYJDVN.js → skills-inventory-HLMKY3R5.js} +13 -11
- package/dist/{slash-commands-ROG3KIFJ.js → slash-commands-LIIZKA5Y.js} +28 -26
- package/dist/{startup-background-KDWCQMWT.js → startup-background-JR2KDK6R.js} +43 -41
- package/dist/{steer-KKYXJSH4.js → steer-D7RTAPCM.js} +3 -3
- package/dist/{system-ZDW44ZGX.js → system-X2P5N4IY.js} +14 -13
- package/dist/{targets-AJWA6E7Z.js → targets-VFFAKUFM.js} +25 -24
- package/dist/{tasks-6L2JJRC5.js → tasks-ERAWZA22.js} +6 -6
- package/dist/{terminal-lease-YFUSDPAD.js → terminal-lease-V2N2BIKQ.js} +2 -2
- package/dist/{trace-LA5MVR4Y.js → trace-MA5BT7XY.js} +3 -3
- package/dist/{usage-XSILDHKF.js → usage-WYFY2L6M.js} +50 -48
- package/dist/{verifiers-ECA5GIHL.js → verifiers-TYIW2XKF.js} +7 -7
- package/dist/{verify-R6QQCCEB.js → verify-242UFYTP.js} +6 -6
- package/dist/{web-fetch-PVZNMBOB.js → web-fetch-LRAGYU6Y.js} +3 -3
- package/dist/{wiki-generate-RP22WP5K.js → wiki-generate-OFKOX2T6.js} +53 -51
- package/dist/worker/entry.js +31 -29
- package/docs/architecture/architecture.md +1 -0
- package/docs/architecture/context-engine.md +4 -2
- package/docs/architecture/observability.md +6 -4
- package/docs/architecture/prompt-envelope-and-tools.md +1 -1
- package/docs/architecture/session-lifecycle.md +1 -1
- package/docs/architecture/tui-design.md +12 -9
- package/docs/gui/parity/02-slash-and-surfaces.md +1 -1
- package/docs/guide/commands-and-modes.md +91 -5
- package/docs/guide/configuration-and-targets.md +2 -2
- package/docs/guide/context-continuity.md +49 -0
- package/docs/guide/proactive-memory.md +6 -3
- package/library/registry.yaml +292 -32
- package/library/skills/README.md +27 -2
- package/library/skills/context/context-handoff/SKILL.md +10 -4
- package/library/skills/meta/clio-coder-dev/SKILL.md +92 -88
- package/library/skills/meta/clio-coder-dev/evals.md +51 -46
- package/library/skills/meta/clio-coder-dev/plugin.json +2 -2
- package/library/skills/meta/clio-coder-dev/references/change-map.md +49 -0
- package/library/skills/meta/clio-coder-test/SKILL.md +89 -142
- package/library/skills/meta/clio-coder-test/evals.md +58 -62
- package/library/skills/meta/clio-coder-test/plugin.json +2 -2
- package/library/skills/meta/clio-coder-test/references/harness.md +3 -3
- package/library/skills/meta/clio-coder-test/references/lifecycle-validation.md +30 -0
- package/library/skills/meta/clio-coder-test/references/test-map.md +60 -89
- package/library/skills/registry.yaml +5 -5
- package/library/skills/skill-marketplace.json +12 -14
- package/package.json +1 -1
- package/src/cli/skills-eval.ts +1064 -54
- package/src/cli/usage.ts +2 -2
- package/src/core/clio-repo.ts +3 -0
- package/src/core/tool-names.ts +1 -0
- package/src/domains/context/budget/inspection.ts +9 -0
- package/src/domains/context/budget/live-view.ts +418 -0
- package/src/domains/context/budget/pressure.ts +393 -0
- package/src/domains/context/budget/request-fit.ts +15 -0
- package/src/domains/evidence/build.ts +24 -0
- package/src/domains/gateway/mcp/client.ts +1 -1
- package/src/domains/memory/commit-state.ts +160 -0
- package/src/domains/memory/operations.ts +18 -10
- package/src/domains/memory/prompt-cache.ts +95 -0
- package/src/domains/memory/prompt-section.ts +50 -7
- package/src/domains/memory/relevance.ts +93 -0
- package/src/domains/memory/restoration.ts +78 -0
- package/src/domains/memory/store.ts +39 -1
- package/src/domains/middleware/memory-intervention.ts +116 -24
- package/src/domains/observability/background-memory-usage.ts +1 -1
- package/src/domains/observability/cost.ts +3 -3
- package/src/domains/observability/extension.ts +1 -1
- package/src/domains/observability/metrics.ts +1 -1
- package/src/domains/prompts/compiler.ts +1 -1
- package/src/domains/prompts/extension.ts +38 -1
- package/src/domains/quota/anthropic-max-provider.ts +134 -0
- package/src/domains/quota/anthropic-usage.ts +220 -0
- package/src/domains/quota/antigravity-provider.ts +339 -0
- package/src/domains/quota/cache.ts +87 -0
- package/src/domains/quota/claude-code-provider.ts +169 -0
- package/src/domains/quota/codex-provider.ts +237 -0
- package/src/domains/quota/presentation.ts +240 -0
- package/src/domains/quota/registry.ts +23 -0
- package/src/domains/quota/service.ts +93 -0
- package/src/domains/quota/summary-feed.ts +86 -0
- package/src/domains/quota/types.ts +84 -0
- package/src/domains/resources/index.ts +11 -0
- package/src/domains/resources/skills/catalog-view.ts +571 -0
- package/src/domains/resources/skills/lexical-match.ts +136 -0
- package/src/domains/resources/skills/loader.ts +38 -0
- package/src/domains/resources/skills/promotion.ts +1 -55
- package/src/domains/resources/skills/provenance-pin.ts +50 -20
- package/src/domains/safety/action-classifier.ts +1 -0
- package/src/domains/session/compaction/branch-summary.ts +4 -1
- package/src/domains/session/compaction/compact.ts +18 -2
- package/src/domains/session/compaction/cut-point.ts +20 -1
- package/src/domains/session/compaction/tokens.ts +48 -3
- package/src/domains/session/context-accounting.ts +8 -1
- package/src/domains/session/continuity/carry.ts +59 -0
- package/src/domains/session/continuity/contract.ts +592 -0
- package/src/domains/session/continuity/evidence.ts +290 -0
- package/src/domains/session/continuity/fold.ts +1075 -0
- package/src/domains/session/continuity/note.ts +79 -0
- package/src/domains/session/continuity/operator-request.ts +104 -0
- package/src/domains/session/continuity/persistence.ts +408 -0
- package/src/domains/session/continuity/ports.ts +231 -0
- package/src/domains/session/continuity/projection.ts +538 -0
- package/src/domains/session/continuity/validate.ts +352 -0
- package/src/domains/session/entries.ts +50 -3
- package/src/domains/session/index.ts +48 -0
- package/src/domains/session/migrations/index.ts +7 -3
- package/src/domains/session/tree/fork.ts +15 -1
- package/src/domains/session/usage.ts +2 -2
- package/src/engine/acp/commands.ts +1 -1
- package/src/engine/agent.ts +133 -9
- package/src/engine/session.ts +13 -6
- package/src/entry/orchestrator.ts +116 -33
- package/src/interactive/chat-loop-messages.ts +2 -2
- package/src/interactive/chat-loop.ts +219 -63
- package/src/interactive/chat-renderer.ts +99 -8
- package/src/interactive/context-overlay.ts +24 -3
- package/src/interactive/continuity-controller.ts +526 -0
- package/src/interactive/dispatch-board.ts +21 -3
- package/src/interactive/footer/dashboard.ts +25 -1
- package/src/interactive/footer/key-hints.ts +2 -2
- package/src/interactive/footer/pages.ts +70 -27
- package/src/interactive/footer/widgets.ts +32 -7
- package/src/interactive/footer-panel.ts +1 -1
- package/src/interactive/interactive-application.ts +7 -2
- package/src/interactive/interactive-input-runtime.ts +2 -2
- package/src/interactive/interactive-presentation.ts +15 -0
- package/src/interactive/interactive-slash-runtime.ts +45 -8
- package/src/interactive/interactive-tickers.ts +7 -1
- package/src/interactive/model-session-replay.ts +130 -3
- package/src/interactive/output-reserve.ts +35 -0
- package/src/interactive/overlay-general-openers.ts +12 -8
- package/src/interactive/overlay-key-routing.ts +6 -9
- package/src/interactive/overlay-lifecycle.ts +10 -6
- package/src/interactive/overlay-session-lifecycle.ts +21 -14
- package/src/interactive/quota-view.ts +229 -0
- package/src/interactive/session-last-turn.ts +1 -1
- package/src/interactive/session-transcript.ts +2 -1
- package/src/interactive/session-usage-reseed.ts +2 -2
- package/src/interactive/side-question.ts +2 -2
- package/src/interactive/slash-commands.ts +28 -9
- package/src/interactive/turn-context.ts +686 -63
- package/src/interactive/turn-middleware.ts +39 -6
- package/src/interactive/turn-persistence.ts +15 -2
- package/src/interactive/turn-prewarm.ts +1 -1
- package/src/interactive/turn-runtime.ts +48 -5
- package/src/interactive/{cost-overlay.ts → usage-overlay.ts} +121 -33
- package/src/interactive/welcome-dashboard.ts +26 -3
- package/src/tools/agent-tools.ts +3 -1
- package/src/tools/bootstrap.ts +6 -0
- package/src/tools/builtin-tool-catalog.ts +9 -0
- package/src/tools/context/index.ts +148 -113
- package/src/tools/context/surface.ts +11 -7
- package/src/tools/core-bootstrap.ts +4 -0
- package/src/tools/observation.ts +8 -2
- package/src/tools/policy.ts +3 -0
- package/src/tools/self-compact.ts +31 -0
- package/src/tools/surface.ts +1 -0
- package/src/tools/tasks.ts +1 -1
- package/dist/chunk-CTFPFW3H.js +0 -44
- package/dist/chunk-EOJPDNUP.js +0 -1325
- package/dist/chunk-I4Y4WKZR.js +0 -386
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ClioSettings } from "../../core/config.js";
|
|
2
2
|
import { readClioVersion } from "../../core/package-root.js";
|
|
3
|
+
import type { LiveBudgetView } from "../../domains/context/budget/live-view.js";
|
|
3
4
|
import type { ContextState } from "../../domains/context/index.js";
|
|
4
5
|
import type { TaskMemoryOperatorStatus } from "../../domains/memory/index.js";
|
|
5
6
|
import {
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
resolveModelCapabilities,
|
|
15
16
|
resolveModelRuntimeCapabilitiesForProviders,
|
|
16
17
|
} from "../../domains/providers/index.js";
|
|
18
|
+
import type { UsageSnapshot } from "../../domains/quota/types.js";
|
|
17
19
|
import type { LocalCapacity } from "../../domains/scheduling/local-capacity.js";
|
|
18
20
|
import type { ContextUsageSnapshot } from "../../domains/session/context-accounting.js";
|
|
19
21
|
import type { ContextLedger } from "../../domains/session/context-ledger.js";
|
|
@@ -88,6 +90,7 @@ export type { ToolTallySnapshot } from "./widgets.js";
|
|
|
88
90
|
export type FooterDashboardMode = "compact" | "expanded";
|
|
89
91
|
|
|
90
92
|
export interface FooterDashboardDeps {
|
|
93
|
+
getQuotaSnapshots?: () => ReadonlyArray<UsageSnapshot>;
|
|
91
94
|
getConnections?: () => { mcp: string[]; plugins: string[] };
|
|
92
95
|
getExtensionStatus?: () => ReadonlyArray<string>;
|
|
93
96
|
providers: ProvidersContract;
|
|
@@ -98,7 +101,8 @@ export interface FooterDashboardDeps {
|
|
|
98
101
|
getSessionTokens?: () => UsageBreakdown;
|
|
99
102
|
getTokenThroughput?: () => TokenThroughputSnapshot | null;
|
|
100
103
|
getSessionCost?: () => CostAggregate;
|
|
101
|
-
getContextUsage?: () => ContextUsageSnapshot
|
|
104
|
+
getContextUsage?: () => ContextUsageSnapshot &
|
|
105
|
+
Partial<Pick<LiveBudgetView, "revision" | "historical" | "inputSource">>;
|
|
102
106
|
getContextLedger?: () => ContextLedger;
|
|
103
107
|
getDispatchRows?: () => ReadonlyArray<DispatchBoardRow>;
|
|
104
108
|
getTaskBoard?: () => TaskBoardSnapshot | null;
|
|
@@ -127,6 +131,8 @@ export interface FooterDashboardDeps {
|
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
export interface FooterDashboardRenderState {
|
|
134
|
+
quota?: ReadonlyArray<UsageSnapshot>;
|
|
135
|
+
quotaRoute?: Pick<DispatchBoardRow, "runtimeId" | "wireModelId" | "node">;
|
|
130
136
|
demoHint?: string | null;
|
|
131
137
|
resources?: LocalMachineMetrics | null;
|
|
132
138
|
connections?: { mcp: string[]; plugins: string[] };
|
|
@@ -413,6 +419,15 @@ export function buildFooterDashboard(deps: FooterDashboardDeps): FooterDashboard
|
|
|
413
419
|
|
|
414
420
|
return {
|
|
415
421
|
resources: machine.snapshot(),
|
|
422
|
+
quota: deps.getQuotaSnapshots?.() ?? [],
|
|
423
|
+
...(current?.runtime && wireModelId
|
|
424
|
+
? {
|
|
425
|
+
quotaRoute: {
|
|
426
|
+
runtimeId: current.runtime.id,
|
|
427
|
+
wireModelId,
|
|
428
|
+
},
|
|
429
|
+
}
|
|
430
|
+
: {}),
|
|
416
431
|
...(deps.getConnections ? { connections: deps.getConnections() } : {}),
|
|
417
432
|
...(settings ? { costCeilingUsd: settings.safety.limits.sessionCostUsd } : {}),
|
|
418
433
|
|
|
@@ -445,6 +460,15 @@ export function buildFooterDashboard(deps: FooterDashboardDeps): FooterDashboard
|
|
|
445
460
|
: null,
|
|
446
461
|
},
|
|
447
462
|
context: {
|
|
463
|
+
...(contextUsage?.revision
|
|
464
|
+
? {
|
|
465
|
+
budget: {
|
|
466
|
+
revision: contextUsage.revision,
|
|
467
|
+
historical: contextUsage.historical ?? true,
|
|
468
|
+
inputSource: contextUsage.inputSource ?? "unknown",
|
|
469
|
+
},
|
|
470
|
+
}
|
|
471
|
+
: {}),
|
|
448
472
|
label: null,
|
|
449
473
|
used: contextUsage?.tokens ?? null,
|
|
450
474
|
contextWindow: contextUsage?.contextWindow ?? null,
|
|
@@ -14,8 +14,8 @@ export function footerKeyHint(now: number, narrow = false): string | null {
|
|
|
14
14
|
}),
|
|
15
15
|
];
|
|
16
16
|
const count = narrow ? 1 : 2;
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
if (!entries.length) return null;
|
|
18
|
+
const page = Math.floor(now / 12_000) % Math.ceil(entries.length / count);
|
|
19
19
|
const start = page * count;
|
|
20
20
|
return entries.slice(start, start + count).join(" · ");
|
|
21
21
|
}
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
import { costAggregateForAmount, formatCostAggregate } from "../../domains/observability/index.js";
|
|
2
|
+
import type { UsageSnapshot } from "../../domains/quota/types.js";
|
|
2
3
|
/** Expanded footer pages: dense inspection, separate from transcript output style. */
|
|
3
4
|
import { sanitizeCallTargetText } from "../../domains/safety/call-target.js";
|
|
4
5
|
import { redactSecretString } from "../../domains/safety/redaction.js";
|
|
5
6
|
import { getKeybindings, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../../engine/tui.js";
|
|
6
|
-
import { contextCategorySwatch,
|
|
7
|
+
import { contextCategorySwatch, renderContextMeterGrid } from "../context-meter.js";
|
|
7
8
|
import { type DispatchBoardRow, dispatchStatusPresentation, renderDispatchActivity } from "../dispatch-board.js";
|
|
8
9
|
import { formatFooterTokens } from "../footer-panel.js";
|
|
10
|
+
import { renderQuotaAccounts, routeWeeklyQuota } from "../quota-view.js";
|
|
9
11
|
import { previewRows } from "../renderers/preview.js";
|
|
10
12
|
import { clioTheme, formatCompactMs, rule } from "../theme/index.js";
|
|
11
13
|
import { fitIdentityLabel } from "../theme/labels.js";
|
|
12
14
|
import type { FooterDashboardRenderState } from "./dashboard.js";
|
|
13
15
|
import { footerKeyHint } from "./key-hints.js";
|
|
14
|
-
import { activityQuadrant, contextQuadrant, zipColumns } from "./widgets.js";
|
|
16
|
+
import { activityQuadrant, contextOccupancyBar, contextQuadrant, contextUsageText, zipColumns } from "./widgets.js";
|
|
15
17
|
|
|
16
18
|
export const DASHBOARD_PAGES = ["Activity", "Context", "Status"] as const;
|
|
17
19
|
export type DashboardPage = (typeof DASHBOARD_PAGES)[number];
|
|
18
20
|
const ACTIVE_AGENT_STATUSES = new Set(["running", "enqueued", "cancelling", "retrying", "stale"]);
|
|
19
21
|
const clean = (value: string) => sanitizeCallTargetText(redactSecretString(value));
|
|
20
22
|
|
|
21
|
-
function agentCard(
|
|
23
|
+
function agentCard(
|
|
24
|
+
row: DispatchBoardRow,
|
|
25
|
+
width: number,
|
|
26
|
+
compact = false,
|
|
27
|
+
quota: ReadonlyArray<UsageSnapshot> = [],
|
|
28
|
+
): string[] {
|
|
22
29
|
const theme = clioTheme();
|
|
23
30
|
const presentation = dispatchStatusPresentation(row.status, { compact: false });
|
|
24
31
|
const name = clean(row.agentId).replace(
|
|
@@ -34,6 +41,9 @@ function agentCard(row: DispatchBoardRow, width: number, compact = false): strin
|
|
|
34
41
|
const field = (label: string, value: string) =>
|
|
35
42
|
wrapTextWithAnsi(`${theme.fg("dim", `${label} `)}${clean(value)}`, width);
|
|
36
43
|
lines.push(...previewRows(field("Route", `${row.targetId}/${row.wireModelId}`), 2, width));
|
|
44
|
+
const weekly = routeWeeklyQuota(row, quota);
|
|
45
|
+
if (weekly)
|
|
46
|
+
lines.push(...previewRows(field("Account", `Shared ${weekly.account} · ${weekly.label}`), compact ? 1 : 2, width));
|
|
37
47
|
if (row.taskSummary)
|
|
38
48
|
lines.push(...(compact ? previewRows(field("Task", row.taskSummary), 2, width) : field("Task", row.taskSummary)));
|
|
39
49
|
const activity = renderDispatchActivity(row, width);
|
|
@@ -107,8 +117,8 @@ function activityPage(state: FooterDashboardRenderState, width: number, budget:
|
|
|
107
117
|
if (first && second) {
|
|
108
118
|
cards.push(
|
|
109
119
|
...zipColumns(
|
|
110
|
-
agentCard(first, col, true),
|
|
111
|
-
agentCard(second, width - col - 5, true),
|
|
120
|
+
agentCard(first, col, true, state.quota),
|
|
121
|
+
agentCard(second, width - col - 5, true, state.quota),
|
|
112
122
|
col,
|
|
113
123
|
width - col - 5,
|
|
114
124
|
` ${theme.fg("frame", "│")} `,
|
|
@@ -119,7 +129,7 @@ function activityPage(state: FooterDashboardRenderState, width: number, budget:
|
|
|
119
129
|
}
|
|
120
130
|
} else
|
|
121
131
|
for (const row of active) {
|
|
122
|
-
const card = agentCard(row, width, active.length > 1 || cardBudget < 18);
|
|
132
|
+
const card = agentCard(row, width, active.length > 1 || cardBudget < 18, state.quota);
|
|
123
133
|
if (cards.length + card.length + 2 > cardBudget && shown > 0) break;
|
|
124
134
|
cards.push(...card, "");
|
|
125
135
|
shown++;
|
|
@@ -173,16 +183,19 @@ function contextPage(state: FooterDashboardRenderState, width: number): string[]
|
|
|
173
183
|
...wrapTextWithAnsi(
|
|
174
184
|
theme.style(
|
|
175
185
|
"accent",
|
|
176
|
-
|
|
186
|
+
state.context.budget
|
|
187
|
+
? `${contextUsageText(state.context)} tokens`
|
|
188
|
+
: `${formatFooterTokens(ledger.usedTokens)} / ${ledger.contextWindow > 0 ? formatFooterTokens(ledger.contextWindow) : "unknown window"} tokens${ledger.percent === null ? "" : ` · ${ledger.percent.toFixed(1)}% occupied`}`,
|
|
177
189
|
{ bold: true },
|
|
178
190
|
),
|
|
179
191
|
width,
|
|
180
192
|
),
|
|
181
193
|
...wrapTextWithAnsi(
|
|
182
|
-
|
|
194
|
+
`${state.context.budget ? "Capture diagnostics: " : ""}Free ${ledger.contextWindow > 0 ? formatFooterTokens(ledger.freeTokens) : "unknown"} · reserved ${formatFooterTokens(ledger.reserveTokens)} · ${ledger.toolCount} tool definitions · compaction ${ledger.compactionAuto ? `auto${ledger.compactionThreshold === null ? "" : ` @${Math.round(ledger.compactionThreshold * 100)}%`}` : "manual"}`,
|
|
183
195
|
width,
|
|
184
196
|
),
|
|
185
197
|
];
|
|
198
|
+
out.push(theme.fg("dim", "Context occupancy is per request; subscription limits are account-wide · /usage"));
|
|
186
199
|
const gridWidth = Math.max(12, Math.min(64, width >= 76 ? Math.floor(width * 0.38) : width));
|
|
187
200
|
const gridHeight = width >= 76 ? 8 : 3;
|
|
188
201
|
const grid = renderContextMeterGrid(ledger, gridWidth, gridHeight, theme);
|
|
@@ -195,7 +208,13 @@ function contextPage(state: FooterDashboardRenderState, width: number): string[]
|
|
|
195
208
|
legendWidth,
|
|
196
209
|
),
|
|
197
210
|
);
|
|
198
|
-
out.push(
|
|
211
|
+
out.push(
|
|
212
|
+
"",
|
|
213
|
+
rule(theme, width, {
|
|
214
|
+
left: state.context.budget ? "CAPTURE DIAGNOSTICS" : "CONTEXT COMPOSITION",
|
|
215
|
+
leftToken: "accent",
|
|
216
|
+
}),
|
|
217
|
+
);
|
|
199
218
|
out.push(...(width >= 76 ? zipColumns(grid, legend, gridWidth, legendWidth, " ") : [...grid, "", ...legend]));
|
|
200
219
|
out.push(
|
|
201
220
|
...wrapTextWithAnsi(
|
|
@@ -248,7 +267,7 @@ function contextPage(state: FooterDashboardRenderState, width: number): string[]
|
|
|
248
267
|
return out;
|
|
249
268
|
}
|
|
250
269
|
|
|
251
|
-
/**
|
|
270
|
+
/** Model and context above; persistent workspace and a rotating hint area below. */
|
|
252
271
|
export function renderCompactDashboard(state: FooterDashboardRenderState, width: number): string[] {
|
|
253
272
|
const theme = clioTheme();
|
|
254
273
|
const w = Math.max(1, width);
|
|
@@ -259,11 +278,24 @@ export function renderCompactDashboard(state: FooterDashboardRenderState, width:
|
|
|
259
278
|
const activity = `${theme.fg("accent", phase)}${workers ? theme.fg("agent", ` · ${workers} active`) : ""}`;
|
|
260
279
|
const identity = clean(state.session.target ?? "No model selected");
|
|
261
280
|
const thinking = theme.fg("reason", `think ${clean(state.session.thinking ?? "off")}`);
|
|
262
|
-
const usage =
|
|
263
|
-
const context = `${ledger ?
|
|
281
|
+
const usage = contextUsageText(state.context);
|
|
282
|
+
const context = `${ledger || state.context.budget ? contextOccupancyBar(state.context, w >= 100 ? 14 : 8, theme) : ""} ${usage}`;
|
|
264
283
|
const rightWidth = Math.min(Math.floor(w * 0.48), visibleWidth(context));
|
|
265
|
-
|
|
266
|
-
const
|
|
284
|
+
|
|
285
|
+
const weekly = state.quotaRoute ? routeWeeklyQuota(state.quotaRoute, state.quota ?? []) : null;
|
|
286
|
+
const leftRoom = Math.max(1, w - rightWidth - 3);
|
|
287
|
+
const activityLabel = fit(activity, Math.min(visibleWidth(activity), Math.max(5, Math.floor(leftRoom / 3))));
|
|
288
|
+
const badge =
|
|
289
|
+
weekly && leftRoom - visibleWidth(activityLabel) - visibleWidth(weekly.label) >= 16
|
|
290
|
+
? theme.fg(
|
|
291
|
+
weekly.severity === "critical" ? "error" : weekly.severity === "normal" ? "muted" : "warning",
|
|
292
|
+
weekly.label,
|
|
293
|
+
)
|
|
294
|
+
: "";
|
|
295
|
+
const baseRoom = leftRoom - visibleWidth(activityLabel) - (badge ? visibleWidth(badge) + 3 : 0) - 5;
|
|
296
|
+
const showThinking = baseRoom - visibleWidth(thinking) - 3 >= 12;
|
|
297
|
+
const identityRoom = Math.max(1, baseRoom - (showThinking ? visibleWidth(thinking) + 3 : 0));
|
|
298
|
+
const left = `${activityLabel} · ${theme.fg("muted", fitIdentityLabel(identity, identityRoom))}${badge ? ` · ${badge}` : ""}${showThinking ? ` · ${thinking}` : ""}`;
|
|
267
299
|
const pair = (l: string, r: string, rw: number) => `${fit(l, w - rw - 3)} ${fit(r, rw)}`;
|
|
268
300
|
const notice = [...state.notices]
|
|
269
301
|
.filter((n) => n.expiresAt === null || n.expiresAt > state.now)
|
|
@@ -283,21 +315,31 @@ export function renderCompactDashboard(state: FooterDashboardRenderState, width:
|
|
|
283
315
|
)
|
|
284
316
|
: state.demoHint
|
|
285
317
|
? `${theme.fg("accent", "Tip")} ${theme.fg("muted", clean(state.demoHint))}`
|
|
286
|
-
: theme.fg(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
318
|
+
: theme.fg("dim", footerKeyHint(state.now, w < 120) ?? `${key} Dashboard`);
|
|
319
|
+
const hintWidth = Math.min(Math.floor(w * 0.48), visibleWidth(foot));
|
|
320
|
+
const workspaceWidth = Math.max(1, w - hintWidth - 3);
|
|
321
|
+
const git = `${clean(state.workspace.branch ?? "no Git branch")}${state.workspace.dirty ? " *" : ""}`;
|
|
322
|
+
const gitWidth = Math.min(visibleWidth(git), Math.max(4, Math.floor(workspaceWidth * 0.45)));
|
|
323
|
+
const cwdWidth = Math.max(1, workspaceWidth - gitWidth - 3);
|
|
324
|
+
const workspace = theme.fg(
|
|
325
|
+
"dim",
|
|
326
|
+
`${fitIdentityLabel(clean(state.workspace.cwd), cwdWidth)} · ${fitIdentityLabel(git, gitWidth)}`,
|
|
327
|
+
);
|
|
328
|
+
return [fit(pair(left, context, rightWidth)), fit(pair(workspace, foot, hintWidth))];
|
|
296
329
|
}
|
|
297
330
|
|
|
298
331
|
function statusPage(state: FooterDashboardRenderState, width: number): string[] {
|
|
299
332
|
const theme = clioTheme();
|
|
300
333
|
|
|
334
|
+
const quotaRows = [
|
|
335
|
+
theme.style(
|
|
336
|
+
"accent",
|
|
337
|
+
`SESSION · ${formatFooterTokens(state.sessionTokens?.totalTokens ?? 0)} recorded tokens · ${formatCostAggregate(state.sessionCost) ?? "cost not yet priced"}`,
|
|
338
|
+
{ bold: true },
|
|
339
|
+
),
|
|
340
|
+
...renderQuotaAccounts(state.quota ?? [], width, { compact: true, now: state.now }),
|
|
341
|
+
];
|
|
342
|
+
|
|
301
343
|
const active = state.dispatchRows.filter((row) => ACTIVE_AGENT_STATUSES.has(row.status));
|
|
302
344
|
const completed = state.dispatchRows.filter((row) => row.status === "completed").length;
|
|
303
345
|
const failed = state.dispatchRows.filter((row) => ["failed", "dead", "aborted"].includes(row.status)).length;
|
|
@@ -334,7 +376,6 @@ function statusPage(state: FooterDashboardRenderState, width: number): string[]
|
|
|
334
376
|
["Target", state.session.target ?? "not selected"],
|
|
335
377
|
["Tracked cost", formatCostAggregate(state.sessionCost) ?? "not yet priced"],
|
|
336
378
|
["Clio ceiling", state.costCeilingUsd === undefined ? "unknown" : `$${state.costCeilingUsd} · tracked pricing only`],
|
|
337
|
-
["Provider quota", "not reported by provider"],
|
|
338
379
|
["MCP connected", names(state.connections?.mcp)],
|
|
339
380
|
["Plugins active", names(state.connections?.plugins)],
|
|
340
381
|
[
|
|
@@ -404,6 +445,7 @@ function statusPage(state: FooterDashboardRenderState, width: number): string[]
|
|
|
404
445
|
];
|
|
405
446
|
if (width < 76)
|
|
406
447
|
return [
|
|
448
|
+
...quotaRows,
|
|
407
449
|
...section("COST & CONNECTIONS", left, width),
|
|
408
450
|
...section("LOCAL MACHINE", right, width),
|
|
409
451
|
theme.fg("accent", "CONTEXT ENGINE"),
|
|
@@ -414,6 +456,7 @@ function statusPage(state: FooterDashboardRenderState, width: number): string[]
|
|
|
414
456
|
];
|
|
415
457
|
const col = Math.floor((width - 5) / 2);
|
|
416
458
|
return [
|
|
459
|
+
...quotaRows,
|
|
417
460
|
...zipColumns(
|
|
418
461
|
section("COST & CONNECTIONS", left, col),
|
|
419
462
|
section("LOCAL MACHINE", right, width - col - 5),
|
|
@@ -457,7 +500,7 @@ export function renderDashboardPage(
|
|
|
457
500
|
const hint = truncateToWidth(
|
|
458
501
|
theme.fg(
|
|
459
502
|
"muted",
|
|
460
|
-
`${cycleKey || "Dashboard"} → ${next} · ${page === "Status" ? "/
|
|
503
|
+
`${cycleKey || "Dashboard"} → ${next} · ${page === "Status" ? "/usage · /mcp · /library" : "composer stays active"}`,
|
|
461
504
|
),
|
|
462
505
|
safeWidth,
|
|
463
506
|
"…",
|
|
@@ -475,7 +518,7 @@ export function renderDashboardPage(
|
|
|
475
518
|
? `${getKeybindings().getKeys("clio-coder.dispatchBoard.toggle").join("/") || "Fleet Runs"} · /view`
|
|
476
519
|
: page === "Context"
|
|
477
520
|
? "/context"
|
|
478
|
-
: "/
|
|
521
|
+
: "/usage · /context";
|
|
479
522
|
content = [
|
|
480
523
|
...content.slice(0, available - 1),
|
|
481
524
|
truncateToWidth(theme.fg("dim", `More detail: ${detail}`), safeWidth, "…", true),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { OutputStyle } from "../../core/defaults.js";
|
|
2
2
|
import { ToolNames } from "../../core/tool-names.js";
|
|
3
|
+
import type { LiveBudgetView } from "../../domains/context/budget/live-view.js";
|
|
3
4
|
import {
|
|
4
5
|
type CostAggregate,
|
|
5
6
|
formatCostAggregate,
|
|
@@ -99,6 +100,8 @@ export interface SessionFacts {
|
|
|
99
100
|
|
|
100
101
|
/** Context engine telemetry. */
|
|
101
102
|
export interface ContextEngineFacts {
|
|
103
|
+
/** Identifies the published accounting used by the meter; ledger stays diagnostic. */
|
|
104
|
+
budget?: Pick<LiveBudgetView, "revision" | "historical" | "inputSource">;
|
|
102
105
|
label: string | null;
|
|
103
106
|
used: number | null;
|
|
104
107
|
contextWindow: number | null;
|
|
@@ -393,14 +396,36 @@ function contextComposition(context: ContextEngineFacts): {
|
|
|
393
396
|
system: finiteNonNegative(breakdown?.systemPromptTokens),
|
|
394
397
|
tools: finiteNonNegative(breakdown?.toolSchemaTokens),
|
|
395
398
|
chat: finiteNonNegative(breakdown?.messageTokens) + finiteNonNegative(breakdown?.pendingUserTokens),
|
|
396
|
-
free:
|
|
399
|
+
free:
|
|
400
|
+
window > 0 && typeof context.used === "number" && Number.isFinite(context.used) ? Math.max(0, window - used) : null,
|
|
397
401
|
};
|
|
398
402
|
}
|
|
399
403
|
|
|
404
|
+
/** Short truthful occupancy label shared by compact and expanded surfaces. */
|
|
405
|
+
export function contextUsageText(context: ContextEngineFacts): string {
|
|
406
|
+
const used = context.budget ? context.used : (context.ledger?.usedTokens ?? context.used);
|
|
407
|
+
const window = context.budget ? context.contextWindow : (context.ledger?.contextWindow ?? context.contextWindow);
|
|
408
|
+
const source = context.budget?.inputSource === "historical" ? "saved " : context.budget && used !== null ? "~" : "";
|
|
409
|
+
return `${source}${used === null ? "?" : formatFooterTokens(used)} / ${window ? formatFooterTokens(window) : "?"}`;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Reads published numbers only; never refreshes accounting from a renderer. */
|
|
413
|
+
export function contextOccupancyBar(context: ContextEngineFacts, cells: number, theme: ClioTheme): string {
|
|
414
|
+
if (!context.budget && context.ledger) return renderContextMeterBar(context.ledger, cells, theme);
|
|
415
|
+
return buildSegmentedContextBar(
|
|
416
|
+
theme,
|
|
417
|
+
cells,
|
|
418
|
+
context.contextWindow ?? 0,
|
|
419
|
+
context.used === null ? undefined : contextBreakdownForBar(context),
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
400
423
|
/** The live window after the percent; null when unknown or the row is too narrow to carry it. */
|
|
401
424
|
export function compactContextWindowLabel(context: ContextEngineFacts, width: number): string | null {
|
|
402
425
|
if (width < 72) return null;
|
|
403
|
-
const window = finiteNonNegative(
|
|
426
|
+
const window = finiteNonNegative(
|
|
427
|
+
context.budget ? context.contextWindow : (context.ledger?.contextWindow ?? context.contextWindow),
|
|
428
|
+
);
|
|
404
429
|
return window > 0 ? formatFooterTokens(window) : null;
|
|
405
430
|
}
|
|
406
431
|
|
|
@@ -427,7 +452,7 @@ export function compactSecondaryLine(
|
|
|
427
452
|
const safeWidth = Math.max(1, Math.floor(width));
|
|
428
453
|
const barCells = compactContextBarWidth(safeWidth);
|
|
429
454
|
let left = "";
|
|
430
|
-
if (context.ledger) {
|
|
455
|
+
if (context.ledger && !context.budget) {
|
|
431
456
|
// The row follows the kv grammar: dim key, muted value. An unmeasured
|
|
432
457
|
// percent renders the ?% placeholder dim because it is scaffolding for a
|
|
433
458
|
// number that has not arrived, not a measurement.
|
|
@@ -437,7 +462,7 @@ export function compactSecondaryLine(
|
|
|
437
462
|
);
|
|
438
463
|
left = `${theme.fg("dim", "ctx")} ${renderContextMeterBar(context.ledger, barCells, theme)} ${percent}`;
|
|
439
464
|
} else {
|
|
440
|
-
left =
|
|
465
|
+
left = `${context.budget?.inputSource === "historical" ? theme.fg("dim", "saved ") : ""}${contextOccupancyBar(context, barCells, theme)}`;
|
|
441
466
|
}
|
|
442
467
|
// A percentage alone does not say whether the window is 128k or 1M, so at
|
|
443
468
|
// widths that can afford it the live window follows the percent. It comes
|
|
@@ -713,7 +738,7 @@ function ledgerBar(theme: ClioTheme, ledger: ContextLedger, cells: number): stri
|
|
|
713
738
|
export function contextQuadrant(facts: ContextEngineFacts, options: ExpandedQuadrantOptions = {}): string[] {
|
|
714
739
|
const theme = clioTheme();
|
|
715
740
|
const ledger = facts.ledger ?? null;
|
|
716
|
-
const hasLedger = ledger !== null && ledger.contextWindow > 0;
|
|
741
|
+
const hasLedger = !facts.budget && ledger !== null && ledger.contextWindow > 0;
|
|
717
742
|
const barCells = expandedContextBarCells(options.width);
|
|
718
743
|
|
|
719
744
|
let bar: string;
|
|
@@ -737,7 +762,7 @@ export function contextQuadrant(facts: ContextEngineFacts, options: ExpandedQuad
|
|
|
737
762
|
? theme.style("frame", `free ${formatFooterTokens(composition.free)}`, { dim: true })
|
|
738
763
|
: null,
|
|
739
764
|
]);
|
|
740
|
-
bar =
|
|
765
|
+
bar = contextOccupancyBar(facts, barCells, theme);
|
|
741
766
|
const filledChar = visibleWidth(GLYPH.contextFull) === 1 ? GLYPH.contextFull : GLYPH.barFull;
|
|
742
767
|
const freeChar = visibleWidth(GLYPH.contextFree) === 1 ? GLYPH.contextFree : GLYPH.barEmpty;
|
|
743
768
|
legendRows = [
|
|
@@ -752,7 +777,7 @@ export function contextQuadrant(facts: ContextEngineFacts, options: ExpandedQuad
|
|
|
752
777
|
"Context",
|
|
753
778
|
[
|
|
754
779
|
statusRow(bar),
|
|
755
|
-
kv("used", formatUsedWindow(usedTokens, windowTokens)),
|
|
780
|
+
kv("used", facts.budget ? contextUsageText(facts) : formatUsedWindow(usedTokens, windowTokens)),
|
|
756
781
|
fill ? styledKv("fill", fill) : statusRow(null),
|
|
757
782
|
chatFree ? styledKv("budget", chatFree) : statusRow(null),
|
|
758
783
|
kv("compact", formatCompaction(facts)),
|
|
@@ -30,7 +30,7 @@ export function formatFooterTokens(n: number): string {
|
|
|
30
30
|
* Build the token-counter footer segment. Returns `null` when no usage has
|
|
31
31
|
* landed yet so the footer stays uncluttered at session start. Cache-read
|
|
32
32
|
* tokens are omitted here to keep the line scannable; reasoning tokens are
|
|
33
|
-
* shown only when the provider exposes them. The `/
|
|
33
|
+
* shown only when the provider exposes them. The `/usage` overlay exposes the
|
|
34
34
|
* full breakdown.
|
|
35
35
|
*/
|
|
36
36
|
export function tokensSegment(usage: UsageBreakdown | null | undefined): string | null {
|
|
@@ -252,6 +252,7 @@ export interface InteractiveDeps {
|
|
|
252
252
|
* engine, and persists a compactionSummary entry.
|
|
253
253
|
*/
|
|
254
254
|
onCompact?: (instructions: string | undefined) => Promise<void>;
|
|
255
|
+
onRecoverHandoff?: (handoffId: string, action: "reduce" | "deliver") => Promise<void>;
|
|
255
256
|
/** Run /context init for the current working directory. */
|
|
256
257
|
onInit?: (options: InitCommandOptions, io?: RunIo) => Promise<void>;
|
|
257
258
|
/** Run /context reset for the current working directory. */
|
|
@@ -827,6 +828,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
827
828
|
...(deps.onSelectModel ? { onSelectModel: deps.onSelectModel } : {}),
|
|
828
829
|
...(deps.onSetThinkingLevel ? { onSetThinkingLevel: deps.onSetThinkingLevel } : {}),
|
|
829
830
|
...(deps.onCompact ? { onCompact: deps.onCompact } : {}),
|
|
831
|
+
...(deps.onRecoverHandoff ? { onRecoverHandoff: deps.onRecoverHandoff } : {}),
|
|
830
832
|
...(deps.onInit ? { onInit: deps.onInit } : {}),
|
|
831
833
|
...(deps.onContextClear ? { onContextClear: deps.onContextClear } : {}),
|
|
832
834
|
...(deps.onContextRefresh ? { onContextRefresh: deps.onContextRefresh } : {}),
|
|
@@ -840,7 +842,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
840
842
|
expandSubmit: (text) => expandInteractiveSubmitAsync(text, deps.resources),
|
|
841
843
|
openAskUser: (questions, options) => openAskUserOverlayState(questions, options),
|
|
842
844
|
openSkillsHub: (request) => openSkillsHubState(request),
|
|
843
|
-
|
|
845
|
+
openUsage: () => openUsageOverlayState(),
|
|
844
846
|
openSideQuestion: (question) => openSideQuestionOverlayState(question),
|
|
845
847
|
startHandoff: (goal) => startHandoffState(goal),
|
|
846
848
|
startFleetRun: (name, vars) => startFleetRunState(name, vars),
|
|
@@ -901,6 +903,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
901
903
|
editor.onSubmit = editorSubmit.submitEditorText;
|
|
902
904
|
|
|
903
905
|
const interactiveTickers = createInteractiveTickers({
|
|
906
|
+
getQuotaSnapshots: presentation.getQuotaSnapshots,
|
|
904
907
|
tui,
|
|
905
908
|
dispatchBoardStore,
|
|
906
909
|
contextActivityStore,
|
|
@@ -937,6 +940,8 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
937
940
|
resetTranscript();
|
|
938
941
|
};
|
|
939
942
|
overlayLifecycle = createOverlayLifecycle({
|
|
943
|
+
getQuotaSnapshots: presentation.getQuotaSnapshots,
|
|
944
|
+
getDispatchRows: () => dispatchBoardStore.rows(),
|
|
940
945
|
app: deps,
|
|
941
946
|
tui,
|
|
942
947
|
footer,
|
|
@@ -972,7 +977,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
972
977
|
const {
|
|
973
978
|
closeOverlay,
|
|
974
979
|
openAskUserOverlayState,
|
|
975
|
-
|
|
980
|
+
openUsageOverlayState,
|
|
976
981
|
openSideQuestionOverlayState,
|
|
977
982
|
startHandoffState,
|
|
978
983
|
startFleetRunState,
|
|
@@ -326,7 +326,7 @@ export function createInteractiveInputRuntime(deps: InteractiveInputRuntimeDeps)
|
|
|
326
326
|
},
|
|
327
327
|
cancelFocusedOwner: () => {
|
|
328
328
|
const state = deps.overlay.getState();
|
|
329
|
-
if (["permission-confirm", "
|
|
329
|
+
if (["permission-confirm", "usage", "context-view", "side-question", "dispatch-board", "auth"].includes(state))
|
|
330
330
|
deps.overlay.closeOverlay();
|
|
331
331
|
else if (focused()?.handleInput) {
|
|
332
332
|
focused()?.handleInput?.("\x1b");
|
|
@@ -531,7 +531,7 @@ function overlayScopeLabel(state: OverlayState): string {
|
|
|
531
531
|
"permission-confirm": "Permission",
|
|
532
532
|
"dispatch-board": "Workers",
|
|
533
533
|
auth: "Authentication",
|
|
534
|
-
|
|
534
|
+
usage: "Usage",
|
|
535
535
|
"context-view": "Context",
|
|
536
536
|
"context-reset": "Context reset",
|
|
537
537
|
tasks: "Tasks",
|
|
@@ -13,6 +13,8 @@ import type {
|
|
|
13
13
|
TokenThroughputSnapshot,
|
|
14
14
|
} from "../domains/observability/index.js";
|
|
15
15
|
import { type ProvidersContract, resolveModelRuntimeCapabilitiesForProviders } from "../domains/providers/index.js";
|
|
16
|
+
import { createQuotaSummaryFeed } from "../domains/quota/summary-feed.js";
|
|
17
|
+
import type { UsageSnapshot } from "../domains/quota/types.js";
|
|
16
18
|
import type { ResourcesContract } from "../domains/resources/index.js";
|
|
17
19
|
import type { LocalCapacity } from "../domains/scheduling/local-capacity.js";
|
|
18
20
|
import { ceilChars, contentChars } from "../domains/session/context-accounting.js";
|
|
@@ -160,6 +162,7 @@ export interface InteractivePresentation {
|
|
|
160
162
|
changeOutputStyle(mutation: () => void): void;
|
|
161
163
|
setLocalBashRunning(running: boolean): void;
|
|
162
164
|
getObservabilitySnapshot(): ObservabilitySnapshot;
|
|
165
|
+
getQuotaSnapshots(): ReadonlyArray<UsageSnapshot>;
|
|
163
166
|
/** Fold one raw chat event into the ephemeral throughput shown only while this turn is active. */
|
|
164
167
|
recordChatEvent(event: ChatLoopEvent): void;
|
|
165
168
|
recordToolStart(toolCallId: string, toolName: string): void;
|
|
@@ -234,6 +237,12 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
234
237
|
return key === undefined || key.length === 0 ? null : formatKeyLabel(key, "");
|
|
235
238
|
};
|
|
236
239
|
|
|
240
|
+
const quotaSummary = createQuotaSummaryFeed({
|
|
241
|
+
onUpdate: () => {
|
|
242
|
+
footer?.refresh();
|
|
243
|
+
requestRender();
|
|
244
|
+
},
|
|
245
|
+
});
|
|
237
246
|
const banner = factories.createBanner({
|
|
238
247
|
providers: deps.providers,
|
|
239
248
|
...(deps.agents ? { getAgentCount: () => deps.agents?.listSpecs().filter(isUserVisibleAgent).length ?? 0 } : {}),
|
|
@@ -248,6 +257,9 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
248
257
|
return key ? formatKeyLabel(key, "") : null;
|
|
249
258
|
},
|
|
250
259
|
onFactsRefreshed: requestRender,
|
|
260
|
+
// Subscription usage is read lazily off the render path and cached, so the
|
|
261
|
+
// banner reads a string and never awaits a provider.
|
|
262
|
+
getQuotaSummary: () => quotaSummary.peek(),
|
|
251
263
|
...(deps.getSettings ? { getSettings: deps.getSettings } : {}),
|
|
252
264
|
});
|
|
253
265
|
// A boot-time `--continue`/`--session` resume opens onto an existing
|
|
@@ -391,6 +403,7 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
391
403
|
getTokenThroughput: () =>
|
|
392
404
|
liveThroughput === null ? observabilitySnapshot.session.latestThroughput : currentLiveThroughput(),
|
|
393
405
|
getSessionCost: () => observabilitySnapshot.session.cost,
|
|
406
|
+
getQuotaSnapshots: () => quotaSummary.peekSnapshots(),
|
|
394
407
|
getContextUsage: () => deps.chat.contextUsage(),
|
|
395
408
|
getContextLedger: () => deps.chat.contextLedger(),
|
|
396
409
|
getDispatchRows: () => dispatchBoardStore.rows(),
|
|
@@ -622,6 +635,7 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
622
635
|
beforeStatusDisposed = true;
|
|
623
636
|
// Before the subscriptions go: a project-context refresh already in flight
|
|
624
637
|
// must not land afterwards and ask a torn-down presentation for a frame.
|
|
638
|
+
quotaSummary.dispose();
|
|
625
639
|
banner.dispose();
|
|
626
640
|
footer.dispose();
|
|
627
641
|
unsubscribeObservability();
|
|
@@ -664,6 +678,7 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
664
678
|
changeOutputStyle: (mutation) =>
|
|
665
679
|
chatRenderer.mutate(() => preserveTranscriptScroll(transcriptView, deps.terminal.columns, mutation), "output-style"),
|
|
666
680
|
getObservabilitySnapshot: () => observabilitySnapshot,
|
|
681
|
+
getQuotaSnapshots: () => quotaSummary.peekSnapshots(),
|
|
667
682
|
recordChatEvent,
|
|
668
683
|
recordToolStart: (toolCallId, toolName) => {
|
|
669
684
|
footerActiveTools.add(toolCallId);
|
|
@@ -36,6 +36,7 @@ import { appendNotice, appendOperatorAside, appendOperatorCommand, appendReferen
|
|
|
36
36
|
import { runOperatorRecall } from "./context-recall-command.js";
|
|
37
37
|
import { renderSessionHtml } from "./export-html/index.js";
|
|
38
38
|
import { dateLocal } from "./format-time.js";
|
|
39
|
+
import { withContinuityReplay } from "./model-session-replay.js";
|
|
39
40
|
import type { OracleDigestSources } from "./oracle.js";
|
|
40
41
|
import type { PendingModelScope } from "./overlays/model-scope.js";
|
|
41
42
|
import { renderDoctorReport } from "./renderers/doctor-report.js";
|
|
@@ -57,6 +58,15 @@ import type { WorkerEntryState } from "./worker-stream.js";
|
|
|
57
58
|
|
|
58
59
|
const EXPORT_RENDER_WIDTH = 100;
|
|
59
60
|
|
|
61
|
+
/** Longest run of consecutive backticks in the rendered transcript, for fence sizing. */
|
|
62
|
+
function longestBacktickRun(lines: ReadonlyArray<string>): number {
|
|
63
|
+
let longest = 0;
|
|
64
|
+
for (const line of lines) {
|
|
65
|
+
for (const run of line.match(/`+/gu) ?? []) longest = Math.max(longest, run.length);
|
|
66
|
+
}
|
|
67
|
+
return longest;
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
export interface InteractiveSlashSubmitExpansion {
|
|
61
71
|
text: string;
|
|
62
72
|
images: ImageContent[];
|
|
@@ -104,6 +114,7 @@ export interface InteractiveSlashRuntimeDeps {
|
|
|
104
114
|
onSelectModel?: (ref: { target: string; model: string }, scope: "session" | "global") => void;
|
|
105
115
|
onSetThinkingLevel?: (level: ThinkingLevel, scope?: "session" | "global") => void;
|
|
106
116
|
onCompact?: (instructions: string | undefined) => Promise<void>;
|
|
117
|
+
onRecoverHandoff?: (handoffId: string, action: "reduce" | "deliver") => Promise<void>;
|
|
107
118
|
onInit?: (options: InitCommandOptions, io?: RunIo) => Promise<void>;
|
|
108
119
|
onContextClear?: (options: ContextClearCommandOptions, io?: RunIo) => Promise<void>;
|
|
109
120
|
onContextRefresh?: (io?: RunIo) => Promise<void>;
|
|
@@ -127,7 +138,7 @@ export interface InteractiveSlashRuntimeDeps {
|
|
|
127
138
|
expandSubmit: (text: string) => Promise<InteractiveSlashSubmitExpansion>;
|
|
128
139
|
openAskUser: AskUserHandler;
|
|
129
140
|
openSkillsHub: (request?: LibraryBrowseRequest) => void;
|
|
130
|
-
|
|
141
|
+
openUsage: () => void;
|
|
131
142
|
/** Run one `/btw` side question in its own overlay. */
|
|
132
143
|
openSideQuestion: (question: string) => void;
|
|
133
144
|
/** Run one `/handoff <goal>`: extract, review, and seed a successor session. */
|
|
@@ -461,7 +472,7 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
461
472
|
? deps.share.planImport(resolve(archivePath), importOptions)
|
|
462
473
|
: deps.share.importArchive(resolve(archivePath), importOptions);
|
|
463
474
|
},
|
|
464
|
-
|
|
475
|
+
openUsage: deps.openUsage,
|
|
465
476
|
openSideQuestion: deps.openSideQuestion,
|
|
466
477
|
// `/oracle` is refused, never queued, while a turn streams: the digest it
|
|
467
478
|
// packs describes the record as it stands.
|
|
@@ -584,6 +595,17 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
584
595
|
deps.setEditorText(text);
|
|
585
596
|
deps.requestRender();
|
|
586
597
|
},
|
|
598
|
+
runHandoffRecovery: (handoffId, action) => {
|
|
599
|
+
if (!deps.onRecoverHandoff) {
|
|
600
|
+
appendCommandNotice("error", "[/context recover] native handoff recovery is unavailable");
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
void deps
|
|
604
|
+
.onRecoverHandoff(handoffId, action)
|
|
605
|
+
.catch((error) =>
|
|
606
|
+
appendCommandNotice("error", `[/context recover] ${error instanceof Error ? error.message : String(error)}`),
|
|
607
|
+
);
|
|
608
|
+
},
|
|
587
609
|
runCompact: (instructions) => {
|
|
588
610
|
runCompactWithNotice(deps.onCompact, appendCommandNotice, instructions);
|
|
589
611
|
},
|
|
@@ -608,10 +630,19 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
608
630
|
// rehydrate exported it as ordinary history (issue #109). No session
|
|
609
631
|
// contract means no pin can exist for this reader, so the file replays whole.
|
|
610
632
|
const leafTurnId = deps.session?.tree(sessionId).leafId ?? null;
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
633
|
+
// The export shows the same continuity the model sees, resolved from
|
|
634
|
+
// the same ledger through the same helper, so the two cannot drift.
|
|
635
|
+
// It is labelled as assistant handoff text and is never rendered as an
|
|
636
|
+
// operator turn.
|
|
637
|
+
rehydrateChatPanelFromTurns(
|
|
638
|
+
exportPanel,
|
|
639
|
+
turns,
|
|
640
|
+
withContinuityReplay(
|
|
641
|
+
turns,
|
|
642
|
+
{ unboundedToolBodies: true, ...(leafTurnId ? { activeLeafTurnId: leafTurnId } : {}) },
|
|
643
|
+
deps.session,
|
|
644
|
+
),
|
|
645
|
+
);
|
|
615
646
|
const ansiLines = exportPanel.render(EXPORT_RENDER_WIDTH);
|
|
616
647
|
// The operator names this file by the day they ran the export, so the
|
|
617
648
|
// date in it is their calendar date. The header below keeps the ISO
|
|
@@ -626,8 +657,14 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
626
657
|
mkdirSync(resolve(target, ".."), { recursive: true });
|
|
627
658
|
if (markdown) {
|
|
628
659
|
const lines = ansiLines.map(stripTerminalSequences);
|
|
629
|
-
|
|
630
|
-
|
|
660
|
+
// The fence has to be longer than the longest backtick run the
|
|
661
|
+
// transcript contains, or a body that includes its own fence closes
|
|
662
|
+
// the block early and the rest of the session renders as Markdown.
|
|
663
|
+
// A handoff note is assistant-authored prose that routinely carries
|
|
664
|
+
// fenced code, so this is reachable content, not a hypothetical.
|
|
665
|
+
const fence = "`".repeat(Math.max(3, longestBacktickRun(lines) + 1));
|
|
666
|
+
const header = [`# Clio session ${sessionId}`, "", `Exported ${exportedAt.toISOString()}`, "", `${fence}text`];
|
|
667
|
+
writeFileSync(target, `${[...header, ...lines, fence, ""].join("\n")}`, "utf8");
|
|
631
668
|
} else {
|
|
632
669
|
writeFileSync(target, renderSessionHtml({ sessionId, exportedAt: exportedAt.toISOString(), ansiLines }), "utf8");
|
|
633
670
|
}
|