@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
|
@@ -22,6 +22,7 @@ import { extractText, hasStructuredToolCall, toolNamesFromAgentState } from "./c
|
|
|
22
22
|
import type { AgentRuntime, ChatTurnState } from "./turn-state.js";
|
|
23
23
|
|
|
24
24
|
export interface TurnMiddlewareDeps {
|
|
25
|
+
memoryContentGuard?: (() => () => boolean) | undefined;
|
|
25
26
|
state: ChatTurnState;
|
|
26
27
|
middleware?: MiddlewareContract | undefined;
|
|
27
28
|
session?: SessionContract | undefined;
|
|
@@ -50,6 +51,7 @@ export interface TurnMiddleware {
|
|
|
50
51
|
tokensBefore?: number,
|
|
51
52
|
): void;
|
|
52
53
|
flushPendingReminders(): string;
|
|
54
|
+
takePendingReminderProjection(): () => string;
|
|
53
55
|
clearPendingReminders(): void;
|
|
54
56
|
/**
|
|
55
57
|
* Deliver a reminder produced after its own turn boundary already closed.
|
|
@@ -57,7 +59,7 @@ export interface TurnMiddleware {
|
|
|
57
59
|
* value; the reminder joins the same buffer, ledger, and transcript path a
|
|
58
60
|
* turn_end reminder takes.
|
|
59
61
|
*/
|
|
60
|
-
injectDeferredReminder(message: string, severity?: MiddlewareReminderSeverity): void;
|
|
62
|
+
injectDeferredReminder(message: string, severity?: MiddlewareReminderSeverity, isCurrent?: () => boolean): void;
|
|
61
63
|
/**
|
|
62
64
|
* Deliver an operator-facing finding produced after its turn boundary closed.
|
|
63
65
|
* The watchdog seam: its run settles well after the turn it reviewed, and
|
|
@@ -76,11 +78,29 @@ export function createTurnMiddleware(deps: TurnMiddlewareDeps): TurnMiddleware {
|
|
|
76
78
|
// injections). The next accepted prompt flushes them into the model
|
|
77
79
|
// request as one system-reminder block. Completed background reminders
|
|
78
80
|
// can also drain at a native tool-batch boundary; session switches clear all.
|
|
79
|
-
const pendingReminders: Array<{
|
|
81
|
+
const pendingReminders: Array<{
|
|
82
|
+
message: string;
|
|
83
|
+
severity: MiddlewareReminderSeverity;
|
|
84
|
+
deferred: boolean;
|
|
85
|
+
isCurrent?: (() => boolean) | undefined;
|
|
86
|
+
}> = [];
|
|
87
|
+
const dropStale = () => {
|
|
88
|
+
for (let index = pendingReminders.length - 1; index >= 0; index--) {
|
|
89
|
+
if (pendingReminders[index]?.isCurrent?.() === false) pendingReminders.splice(index, 1);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
80
92
|
|
|
81
|
-
const bufferReminder = (
|
|
93
|
+
const bufferReminder = (
|
|
94
|
+
message: string,
|
|
95
|
+
severity: MiddlewareReminderSeverity,
|
|
96
|
+
deferred = false,
|
|
97
|
+
isCurrent?: () => boolean,
|
|
98
|
+
): void => {
|
|
99
|
+
dropStale();
|
|
100
|
+
isCurrent ??= message.startsWith("Memory:") ? deps.memoryContentGuard?.() : undefined;
|
|
101
|
+
if (isCurrent?.() === false) return;
|
|
82
102
|
if (pendingReminders.some((entry) => entry.message === message && entry.severity === severity)) return;
|
|
83
|
-
pendingReminders.push({ message, severity, deferred });
|
|
103
|
+
pendingReminders.push({ message, severity, deferred, isCurrent });
|
|
84
104
|
};
|
|
85
105
|
|
|
86
106
|
const runMiddlewareTurnHook = (input: MiddlewareHookInput): ReadonlyArray<MiddlewareEffect> => {
|
|
@@ -194,6 +214,7 @@ export function createTurnMiddleware(deps: TurnMiddlewareDeps): TurnMiddleware {
|
|
|
194
214
|
[],
|
|
195
215
|
);
|
|
196
216
|
if (!current()) return false;
|
|
217
|
+
dropStale();
|
|
197
218
|
const ready = pendingReminders.filter((entry) => entry.deferred);
|
|
198
219
|
if (ready.length === 0) return false;
|
|
199
220
|
const content = `<system-reminder>\n${[...new Set(ready.map((entry) => entry.message))].join("\n\n")}\n</system-reminder>`;
|
|
@@ -314,7 +335,17 @@ export function createTurnMiddleware(deps: TurnMiddlewareDeps): TurnMiddleware {
|
|
|
314
335
|
});
|
|
315
336
|
},
|
|
316
337
|
|
|
338
|
+
takePendingReminderProjection(): () => string {
|
|
339
|
+
const entries = pendingReminders.splice(0);
|
|
340
|
+
return () => {
|
|
341
|
+
const messages = [
|
|
342
|
+
...new Set(entries.filter((entry) => entry.isCurrent?.() !== false).map((entry) => entry.message)),
|
|
343
|
+
];
|
|
344
|
+
return messages.length ? `<system-reminder>\n${messages.join("\n\n")}\n</system-reminder>` : "";
|
|
345
|
+
};
|
|
346
|
+
},
|
|
317
347
|
flushPendingReminders(): string {
|
|
348
|
+
dropStale();
|
|
318
349
|
if (pendingReminders.length === 0) return "";
|
|
319
350
|
// Severity affects host actions, not the text delivered to the model.
|
|
320
351
|
// A paired continuation/info and reminder/warn still needs one copy.
|
|
@@ -327,14 +358,16 @@ export function createTurnMiddleware(deps: TurnMiddlewareDeps): TurnMiddleware {
|
|
|
327
358
|
pendingReminders.length = 0;
|
|
328
359
|
},
|
|
329
360
|
|
|
330
|
-
injectDeferredReminder(message, severity = "advisory"): void {
|
|
361
|
+
injectDeferredReminder(message, severity = "advisory", isCurrent): void {
|
|
362
|
+
dropStale();
|
|
363
|
+
if (isCurrent?.() === false) return;
|
|
331
364
|
const text = message.trim();
|
|
332
365
|
if (text.length === 0) return;
|
|
333
366
|
// A deferred reminder never carries hard-block authority: the turn it
|
|
334
367
|
// could have interrupted is already over.
|
|
335
368
|
const level: MiddlewareReminderSeverity = severity === "hard-block" ? "advisory" : severity;
|
|
336
369
|
if (pendingReminders.some((entry) => entry.message === text)) return;
|
|
337
|
-
bufferReminder(text, level, true);
|
|
370
|
+
bufferReminder(text, level, true, isCurrent);
|
|
338
371
|
appendMiddlewareReminderEntry(text, level);
|
|
339
372
|
deps.emitNotice(text);
|
|
340
373
|
},
|
|
@@ -60,7 +60,11 @@ export interface TurnPersistenceDeps {
|
|
|
60
60
|
export interface TurnPersistence {
|
|
61
61
|
/** True when this exact assistant message object was already persisted. */
|
|
62
62
|
wasPersisted(message: unknown): boolean;
|
|
63
|
-
appendAssistantTurn(
|
|
63
|
+
appendAssistantTurn(
|
|
64
|
+
message: AgentMessage,
|
|
65
|
+
timing?: AssistantCallTiming | null,
|
|
66
|
+
deliveryId?: string,
|
|
67
|
+
): string | undefined;
|
|
64
68
|
appendQueuedUserTurn(message: AgentMessage): void;
|
|
65
69
|
appendToolCallTurn(event: Extract<AgentEvent, { type: "tool_execution_start" }>): void;
|
|
66
70
|
appendToolResultTurn(
|
|
@@ -226,7 +230,11 @@ export function createTurnPersistence(deps: TurnPersistenceDeps): TurnPersistenc
|
|
|
226
230
|
};
|
|
227
231
|
};
|
|
228
232
|
|
|
229
|
-
const appendAssistantTurn = (
|
|
233
|
+
const appendAssistantTurn = (
|
|
234
|
+
message: AgentMessage,
|
|
235
|
+
timing?: AssistantCallTiming | null,
|
|
236
|
+
deliveryId?: string,
|
|
237
|
+
): string | undefined => {
|
|
230
238
|
if (message?.role !== "assistant") return;
|
|
231
239
|
if (persistedAssistantMessages.has(message)) return;
|
|
232
240
|
// A loop-guard interrupt already persisted a durable closing turn with the
|
|
@@ -237,6 +245,10 @@ export function createTurnPersistence(deps: TurnPersistenceDeps): TurnPersistenc
|
|
|
237
245
|
const failure = terminalFailureFromAssistantMessage(message);
|
|
238
246
|
const payload = assistantSessionPayload(message, failure);
|
|
239
247
|
if (timing) payload.timing = timing;
|
|
248
|
+
if (deliveryId) {
|
|
249
|
+
payload.continuityDeliveryId = deliveryId;
|
|
250
|
+
payload.stopReason = message.stopReason;
|
|
251
|
+
}
|
|
240
252
|
const usage = (message as { usage?: Usage }).usage;
|
|
241
253
|
if (usage && typeof usage === "object") {
|
|
242
254
|
const backendTimings = (message as { backendTimings?: BackendCompletionTimings }).backendTimings;
|
|
@@ -279,6 +291,7 @@ export function createTurnPersistence(deps: TurnPersistenceDeps): TurnPersistenc
|
|
|
279
291
|
tokens: traceUsage?.totalTokens ?? null,
|
|
280
292
|
});
|
|
281
293
|
if (!turnContinues) finishTracedTurn(failure ? "fail" : "success", failure?.errorMessage ?? null);
|
|
294
|
+
return turn.id;
|
|
282
295
|
};
|
|
283
296
|
|
|
284
297
|
return {
|
|
@@ -72,7 +72,7 @@ export interface TurnPrewarmDeps {
|
|
|
72
72
|
) => Promise<{ ok: true; runtime: AgentRuntime; apiKey: string | undefined } | { ok: false; reason: string }>;
|
|
73
73
|
/** Freeze the session tool surface onto the agent, exactly as a submit does. */
|
|
74
74
|
applySessionTools: (runtime: AgentRuntime) => void;
|
|
75
|
-
/** Report the round's provider usage under its own `/
|
|
75
|
+
/** Report the round's provider usage under its own `/usage` label. */
|
|
76
76
|
recordUsage: (runtime: AgentRuntime, usage: Usage | null) => void;
|
|
77
77
|
/**
|
|
78
78
|
* Claim one in-flight request on the runtime's endpoint for the duration of
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { requestFits } from "../domains/context/budget/request-fit.js";
|
|
2
|
+
import { estimateInputTokensFromContext } from "../engine/apis/output-budget.js";
|
|
1
3
|
import { resolvedRequestContext } from "../engine/context.js";
|
|
4
|
+
import type { ContinuityController } from "./continuity-controller.js";
|
|
5
|
+
import { resolveTurnOutputReserve } from "./output-reserve.js";
|
|
2
6
|
/**
|
|
3
7
|
* Turn runtime ownership: orchestrator target resolution, model synthesis,
|
|
4
8
|
* live-agent construction and model hot-swap, thinking-level reconciliation,
|
|
@@ -133,6 +137,8 @@ export interface TurnRuntimeDeps {
|
|
|
133
137
|
middlewareToolChoice: MiddlewareToolChoiceControl;
|
|
134
138
|
persistence: TurnPersistence;
|
|
135
139
|
context: TurnContext;
|
|
140
|
+
continuity?: ContinuityController;
|
|
141
|
+
hasQueuedSteering?: () => boolean;
|
|
136
142
|
middleware: TurnMiddleware;
|
|
137
143
|
retrySettings: () => RetrySettings;
|
|
138
144
|
/** Stable Clio session id forwarded only to gateway runtimes that understand it. */
|
|
@@ -512,6 +518,37 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
|
|
|
512
518
|
},
|
|
513
519
|
maxRetryDelayMs: deps.retrySettings().maxDelayMs,
|
|
514
520
|
...(gatewaySessionId ? { sessionId: gatewaySessionId } : {}),
|
|
521
|
+
beforeToolBatch: ({ assistantMessage }) => {
|
|
522
|
+
const calls = assistantMessage.content.filter((block) => block.type === "toolCall");
|
|
523
|
+
if (calls.some((call) => call.name === "self_compact") && calls.length !== 1) {
|
|
524
|
+
return { reason: "self_compact must be the only call in its batch; no sibling was executed." };
|
|
525
|
+
}
|
|
526
|
+
return undefined;
|
|
527
|
+
},
|
|
528
|
+
beforeStreamRequest: ({ context: request }) => {
|
|
529
|
+
setGlobalDefaultMaxOutputTokens(deps.getSettings().chat.maxOutputTokens);
|
|
530
|
+
if (state.activeInterruptReason !== null || state.runtime !== localRuntime) {
|
|
531
|
+
return { block: true, reason: "Request ownership changed before invocation." };
|
|
532
|
+
}
|
|
533
|
+
if (deps.hasQueuedSteering?.() && deps.continuity?.admission().block === false) {
|
|
534
|
+
const handoff = deps.continuity.admission();
|
|
535
|
+
if (!handoff.block && handoff.correlationId)
|
|
536
|
+
return { block: true, reason: "Operator steering arrived during handoff preparation; handoff paused." };
|
|
537
|
+
}
|
|
538
|
+
const view = context.refreshLiveBudget();
|
|
539
|
+
const resolved = resolvedRequestContext(request);
|
|
540
|
+
const actual = state.synthesisToolLock
|
|
541
|
+
? { ...resolved, systemPrompt: lockedSynthesisSystemPrompt(resolved.systemPrompt ?? "") }
|
|
542
|
+
: request;
|
|
543
|
+
const input = Math.max(estimateInputTokensFromContext(actual), view.inputTokens ?? 0);
|
|
544
|
+
const output = resolveTurnOutputReserve(localRuntime, input);
|
|
545
|
+
return requestFits(input, output, view.effectiveWindow)
|
|
546
|
+
? (deps.continuity?.admission() ?? { block: false })
|
|
547
|
+
: {
|
|
548
|
+
block: true,
|
|
549
|
+
reason: `Context window exceeded: input ${input} + output ${output}, window ${view.effectiveWindow ?? "unknown"}.`,
|
|
550
|
+
};
|
|
551
|
+
},
|
|
515
552
|
onStreamInvocation: () => {
|
|
516
553
|
setGlobalDefaultMaxOutputTokens(deps.getSettings().chat.maxOutputTokens);
|
|
517
554
|
apiCallStartedAt = performance.now();
|
|
@@ -634,19 +671,22 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
|
|
|
634
671
|
stallTimer.unref?.();
|
|
635
672
|
};
|
|
636
673
|
|
|
637
|
-
handle.agent.
|
|
674
|
+
handle.agent.prepareNextTurnWithContext = async (_completed, signal?: AbortSignal) => {
|
|
638
675
|
stallSuspendDepth += 1;
|
|
639
676
|
try {
|
|
640
677
|
if (signal?.aborted || state.activeInterruptReason !== null) return undefined;
|
|
641
|
-
|
|
678
|
+
if (deps.hasQueuedSteering?.()) await deps.continuity?.pause();
|
|
679
|
+
const handoffChanged = (await deps.continuity?.settle(signal)) ?? false;
|
|
680
|
+
const contextChanged = (await middleware.prepareToolContinuation(localRuntime, signal)) || handoffChanged;
|
|
642
681
|
const update = await context.postToolContinuationGuard(localRuntime, signal, contextChanged);
|
|
682
|
+
const restored = context.installMemoryRestoration(localRuntime);
|
|
643
683
|
if (signal?.aborted || state.activeInterruptReason !== null) return undefined;
|
|
644
684
|
// Compaction owns any replacement snapshot. Without one, publish the
|
|
645
685
|
// already-accounted reminder through context, not message events that
|
|
646
686
|
// would manufacture a new operator turn and reset task authority.
|
|
647
687
|
return (
|
|
648
|
-
update ??
|
|
649
|
-
(contextChanged
|
|
688
|
+
(!restored ? update : undefined) ??
|
|
689
|
+
(contextChanged || restored
|
|
650
690
|
? {
|
|
651
691
|
context: {
|
|
652
692
|
messages: [...localRuntime.agent.state.messages],
|
|
@@ -950,7 +990,9 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
|
|
|
950
990
|
apiMs: Math.round(Math.max(0, eventClock - apiCallStartedAt)),
|
|
951
991
|
}
|
|
952
992
|
: null;
|
|
953
|
-
|
|
993
|
+
const correlationId = event.type === "message_end" ? handle.requestCorrelationId(event.message) : undefined;
|
|
994
|
+
const persistedId = persistence.appendAssistantTurn(enrichedEvent.message, timing, correlationId);
|
|
995
|
+
await deps.continuity?.response(persistedId, correlationId);
|
|
954
996
|
if (isAssistant) apiCallStartedAt = null;
|
|
955
997
|
const usage = (enrichedEvent.message as { usage?: Usage }).usage;
|
|
956
998
|
if (isAssistant && usage && typeof usage === "object" && runFirstCallVerdict === null) {
|
|
@@ -974,6 +1016,7 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
|
|
|
974
1016
|
: null;
|
|
975
1017
|
}
|
|
976
1018
|
if (enrichedEvent.type === "agent_end") {
|
|
1019
|
+
await deps.continuity?.pause();
|
|
977
1020
|
clearStallTimer();
|
|
978
1021
|
const terminal = pendingTerminalToolResult;
|
|
979
1022
|
pendingTerminalToolResult = null;
|
|
@@ -11,19 +11,22 @@ import {
|
|
|
11
11
|
type ObservabilityContract,
|
|
12
12
|
type ObservabilitySnapshot,
|
|
13
13
|
} from "../domains/observability/index.js";
|
|
14
|
+
import type { UsageSnapshot } from "../domains/quota/types.js";
|
|
14
15
|
import {
|
|
15
16
|
foldPromptCacheTelemetry,
|
|
16
17
|
hasPromptCacheTelemetry,
|
|
17
18
|
type PromptCacheTelemetry,
|
|
18
19
|
type SessionEntry,
|
|
19
20
|
} from "../domains/session/index.js";
|
|
20
|
-
import type
|
|
21
|
-
import {
|
|
21
|
+
import { type Component, matchesKey, type OverlayHandle, type TUI, wrapTextWithAnsi } from "../engine/tui.js";
|
|
22
|
+
import type { DispatchBoardRow } from "./dispatch-board.js";
|
|
23
|
+
import { buildResponsiveHint, showClioOverlayFrame } from "./overlay-frame.js";
|
|
24
|
+
import { quotaMeter, renderQuotaAccounts, renderWorkerUsage } from "./quota-view.js";
|
|
22
25
|
import { clioTheme, rule } from "./theme/index.js";
|
|
23
26
|
|
|
24
|
-
const DEFAULT_CONTENT_WIDTH =
|
|
27
|
+
const DEFAULT_CONTENT_WIDTH = 104;
|
|
25
28
|
|
|
26
|
-
export const
|
|
29
|
+
export const USAGE_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH + 4;
|
|
27
30
|
|
|
28
31
|
export interface CostRow {
|
|
29
32
|
providerId: string;
|
|
@@ -295,19 +298,9 @@ function modelBlock(row: CostRow): string[] {
|
|
|
295
298
|
]);
|
|
296
299
|
}
|
|
297
300
|
|
|
298
|
-
function
|
|
299
|
-
totalCost: CostAggregate,
|
|
300
|
-
totalTokens: number,
|
|
301
|
-
rows: ReadonlyArray<CostRow>,
|
|
302
|
-
contentWidth: number,
|
|
303
|
-
promptCache: PromptCacheTelemetry | null = null,
|
|
304
|
-
): string[] {
|
|
301
|
+
function modelDetailLines(rows: ReadonlyArray<CostRow>, contentWidth: number): string[] {
|
|
305
302
|
const theme = clioTheme();
|
|
306
|
-
const lines: string[] = [];
|
|
307
|
-
for (const line of summaryBlock(totalCost, totalTokens, rows, promptCache)) {
|
|
308
|
-
lines.push(line);
|
|
309
|
-
}
|
|
310
|
-
lines.push(rule(theme, contentWidth));
|
|
303
|
+
const lines: string[] = [rule(theme, contentWidth)];
|
|
311
304
|
if (rows.length === 0) {
|
|
312
305
|
lines.push(theme.fg("muted", "no token usage recorded for this session"));
|
|
313
306
|
} else {
|
|
@@ -352,32 +345,116 @@ function buildCostSnapshot(
|
|
|
352
345
|
};
|
|
353
346
|
}
|
|
354
347
|
|
|
355
|
-
export interface
|
|
348
|
+
export interface OpenUsageOverlayOptions {
|
|
349
|
+
getDispatchRows?: () => ReadonlyArray<DispatchBoardRow>;
|
|
350
|
+
getQuotaSnapshots?: () => ReadonlyArray<UsageSnapshot>;
|
|
356
351
|
sessionId?: string | null;
|
|
357
352
|
/** Session ledger, read on every render so newly settled and branched calls appear. */
|
|
358
353
|
getSessionEntries?: () => ReadonlyArray<SessionEntry>;
|
|
359
354
|
}
|
|
360
355
|
|
|
361
|
-
|
|
362
|
-
|
|
356
|
+
const USAGE_TABS = ["Accounts", "Session", "Models", "Workers"] as const;
|
|
357
|
+
|
|
358
|
+
class UsageOverlayBody implements Component {
|
|
359
|
+
private tab = 0;
|
|
360
|
+
private offsets = [0, 0, 0, 0];
|
|
361
|
+
private height = 20;
|
|
362
|
+
private pageHeight = 16;
|
|
363
|
+
private lineCount = 0;
|
|
364
|
+
|
|
365
|
+
constructor(
|
|
366
|
+
private readonly getSnapshot: () => CostSnapshot,
|
|
367
|
+
private readonly getQuotaSnapshots: () => ReadonlyArray<UsageSnapshot>,
|
|
368
|
+
private readonly getDispatchRows: () => ReadonlyArray<DispatchBoardRow>,
|
|
369
|
+
private readonly requestRender: () => void,
|
|
370
|
+
) {}
|
|
371
|
+
|
|
372
|
+
setHeight(rows: number): void {
|
|
373
|
+
this.height = Math.max(1, rows - 3);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
handleInput(data: string): void {
|
|
377
|
+
const selected = (["1", "2", "3", "4"] as const).findIndex((key) => matchesKey(data, key));
|
|
378
|
+
if (selected >= 0) this.tab = selected;
|
|
379
|
+
else if (matchesKey(data, "tab") || matchesKey(data, "right")) this.tab = (this.tab + 1) % USAGE_TABS.length;
|
|
380
|
+
else if (matchesKey(data, "shift+tab") || matchesKey(data, "left"))
|
|
381
|
+
this.tab = (this.tab + USAGE_TABS.length - 1) % USAGE_TABS.length;
|
|
382
|
+
else {
|
|
383
|
+
let offset = this.offsets[this.tab] ?? 0;
|
|
384
|
+
if (matchesKey(data, "up")) offset--;
|
|
385
|
+
else if (matchesKey(data, "down")) offset++;
|
|
386
|
+
else if (matchesKey(data, "pageUp")) offset -= this.pageHeight;
|
|
387
|
+
else if (matchesKey(data, "pageDown")) offset += this.pageHeight;
|
|
388
|
+
else if (matchesKey(data, "home") || matchesKey(data, "ctrl+home")) offset = 0;
|
|
389
|
+
else if (matchesKey(data, "end") || matchesKey(data, "ctrl+end")) offset = this.lineCount;
|
|
390
|
+
else return;
|
|
391
|
+
this.offsets[this.tab] = Math.max(0, Math.min(offset, this.lineCount - this.pageHeight));
|
|
392
|
+
}
|
|
393
|
+
this.requestRender();
|
|
394
|
+
}
|
|
363
395
|
|
|
364
396
|
render(width: number): string[] {
|
|
365
397
|
const contentWidth = Math.max(1, Math.floor(width));
|
|
366
398
|
const snapshot = this.getSnapshot();
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
399
|
+
const quota = this.getQuotaSnapshots();
|
|
400
|
+
const theme = clioTheme();
|
|
401
|
+
const tabs = wrapTextWithAnsi(
|
|
402
|
+
USAGE_TABS.map((name, index) =>
|
|
403
|
+
index === this.tab
|
|
404
|
+
? theme.style("accent", `${index + 1} ${name}`, { bold: true, underline: true })
|
|
405
|
+
: theme.fg("dim", `${index + 1} ${name}`),
|
|
406
|
+
).join(" "),
|
|
371
407
|
contentWidth,
|
|
372
|
-
snapshot.promptCache,
|
|
373
408
|
);
|
|
409
|
+
let body: string[];
|
|
410
|
+
if (this.tab === 0)
|
|
411
|
+
body = [
|
|
412
|
+
theme.fg("dim", "Account-wide limits · filled = used · shared across sessions and devices"),
|
|
413
|
+
"",
|
|
414
|
+
...renderQuotaAccounts(quota, contentWidth),
|
|
415
|
+
];
|
|
416
|
+
else if (this.tab === 1)
|
|
417
|
+
body = [
|
|
418
|
+
theme.style("accent", "Session tokens & cost", { bold: true }),
|
|
419
|
+
theme.fg("dim", "Recorded calls in this session · estimates are marked in the cost totals"),
|
|
420
|
+
"",
|
|
421
|
+
...summaryBlock(snapshot.totalCost, snapshot.totalTokens, snapshot.rows, snapshot.promptCache),
|
|
422
|
+
"",
|
|
423
|
+
rule(theme, contentWidth),
|
|
424
|
+
theme.fg("dim", "Session tokens cannot be converted to subscription percentages."),
|
|
425
|
+
...(snapshot.rows.length ? [] : ["no token usage recorded for this session"]),
|
|
426
|
+
];
|
|
427
|
+
else if (this.tab === 2) {
|
|
428
|
+
body = [
|
|
429
|
+
theme.style("accent", "Model activity · this session", { bold: true }),
|
|
430
|
+
theme.fg("dim", "Share of recorded processed tokens, including cache traffic; not account quota."),
|
|
431
|
+
"",
|
|
432
|
+
];
|
|
433
|
+
for (const row of [...snapshot.rows].sort((a, b) => b.tokens - a.tokens)) {
|
|
434
|
+
const share = snapshot.totalTokens > 0 ? (row.tokens / snapshot.totalTokens) * 100 : 0;
|
|
435
|
+
body.push(
|
|
436
|
+
`${row.providerId} · ${row.attributedModelId}`,
|
|
437
|
+
`${quotaMeter(share, Math.min(24, contentWidth), "normal")} ${share.toFixed(1)}% · ${formatTokens(row.tokens)} tokens`,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
body.push("", ...modelDetailLines(snapshot.rows, contentWidth));
|
|
441
|
+
} else body = renderWorkerUsage(this.getDispatchRows(), quota, contentWidth);
|
|
442
|
+
const lines = body.flatMap((line) => wrapTextWithAnsi(line, contentWidth));
|
|
443
|
+
this.lineCount = lines.length;
|
|
444
|
+
this.pageHeight = Math.max(1, this.height - tabs.length - 1);
|
|
445
|
+
const offset = Math.max(0, Math.min(this.offsets[this.tab] ?? 0, lines.length - this.pageHeight));
|
|
446
|
+
this.offsets[this.tab] = offset;
|
|
447
|
+
const visible = [...tabs, "", ...lines.slice(offset, offset + this.pageHeight)];
|
|
448
|
+
if (lines.length > this.pageHeight)
|
|
449
|
+
visible.push(theme.fg("dim", `${offset + 1}–${Math.min(lines.length, offset + this.pageHeight)} / ${lines.length}`));
|
|
450
|
+
return visible;
|
|
374
451
|
}
|
|
375
452
|
|
|
376
453
|
invalidate(): void {}
|
|
377
454
|
}
|
|
378
455
|
|
|
379
456
|
/**
|
|
380
|
-
* Mount
|
|
457
|
+
* Mount the read-only usage overlay. Quota comes from the shared presentation feed. The running USD total comes from the
|
|
381
458
|
* observability projection's `snapshot().session.costUsd`, while the
|
|
382
459
|
* per-provider/model rows fold `observability.costEntries()`. The overlay is
|
|
383
460
|
* kept live by `observability.subscribe()`: the projection already folds the
|
|
@@ -385,23 +462,34 @@ class CostOverlayBody implements Component {
|
|
|
385
462
|
* update, so a single subscription replaces the former DispatchCompleted /
|
|
386
463
|
* DispatchFailed / chat-turn refresh wiring. `hide()` unsubscribes.
|
|
387
464
|
*/
|
|
388
|
-
export function
|
|
465
|
+
export function openUsageOverlay(
|
|
389
466
|
tui: TUI,
|
|
390
467
|
observability: ObservabilityContract,
|
|
391
|
-
options?:
|
|
468
|
+
options?: OpenUsageOverlayOptions,
|
|
392
469
|
): OverlayHandle {
|
|
393
470
|
const sessionId = options?.sessionId ?? null;
|
|
394
471
|
let latest: ObservabilitySnapshot = observability.snapshot();
|
|
395
|
-
const body = new
|
|
396
|
-
buildCostSnapshot(observability, sessionId, latest, options?.getSessionEntries),
|
|
472
|
+
const body = new UsageOverlayBody(
|
|
473
|
+
() => buildCostSnapshot(observability, sessionId, latest, options?.getSessionEntries),
|
|
474
|
+
options?.getQuotaSnapshots ?? (() => []),
|
|
475
|
+
options?.getDispatchRows ?? (() => []),
|
|
476
|
+
() => tui.requestRender(),
|
|
397
477
|
);
|
|
398
478
|
const handle = showClioOverlayFrame(tui, body, {
|
|
399
479
|
anchor: "center",
|
|
400
|
-
width:
|
|
480
|
+
width: USAGE_OVERLAY_WIDTH,
|
|
401
481
|
// Not derived from the title: that one carries the session id.
|
|
402
|
-
markerId: "
|
|
403
|
-
title: sessionId && sessionId.length > 0 ? `
|
|
404
|
-
footerHint:
|
|
482
|
+
markerId: "usage",
|
|
483
|
+
title: sessionId && sessionId.length > 0 ? `Usage (${sessionId})` : "Usage",
|
|
484
|
+
footerHint: buildResponsiveHint([
|
|
485
|
+
{ key: "1–4 / Tab", verb: "view", critical: true },
|
|
486
|
+
{ key: "↑↓", verb: "scroll" },
|
|
487
|
+
{ key: "PgUp/PgDn", verb: "page" },
|
|
488
|
+
]),
|
|
489
|
+
visible: (_width, height) => {
|
|
490
|
+
body.setHeight(height);
|
|
491
|
+
return true;
|
|
492
|
+
},
|
|
405
493
|
});
|
|
406
494
|
|
|
407
495
|
// subscribe() fires immediately with the current snapshot, then on each
|
|
@@ -4,7 +4,7 @@ import type { ContextState } from "../domains/context/index.js";
|
|
|
4
4
|
import type { ProvidersContract, TargetStatus } from "../domains/providers/index.js";
|
|
5
5
|
import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
6
6
|
import type { WorkspaceSnapshot } from "../domains/session/workspace/index.js";
|
|
7
|
-
import { type Component, type Keybinding, truncateToWidth, visibleWidth } from "../engine/tui.js";
|
|
7
|
+
import { type Component, type Keybinding, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../engine/tui.js";
|
|
8
8
|
import {
|
|
9
9
|
brandMark,
|
|
10
10
|
type ClioTheme,
|
|
@@ -55,6 +55,12 @@ export interface WelcomeDashboardDeps {
|
|
|
55
55
|
getSubmitKeyLabel?: () => string | null;
|
|
56
56
|
/** Effective shortcut labels; null hides an unbound action. */
|
|
57
57
|
getKeyLabel?: (action: Keybinding) => string | null;
|
|
58
|
+
/**
|
|
59
|
+
* Connected subscription quota, already summarized, or null when nothing is
|
|
60
|
+
* connected yet. Read synchronously on every frame, so the supplier must
|
|
61
|
+
* serve a cached value and do its own refreshing off the render path.
|
|
62
|
+
*/
|
|
63
|
+
getQuotaSummary?: () => string | null;
|
|
58
64
|
/** Called when an off-render refresh lands, so the frame owner can ask for one. */
|
|
59
65
|
onFactsRefreshed?: () => void;
|
|
60
66
|
/**
|
|
@@ -82,6 +88,8 @@ export interface WelcomeDashboardStats {
|
|
|
82
88
|
autonomy: string;
|
|
83
89
|
targets: string;
|
|
84
90
|
fleet: string;
|
|
91
|
+
/** One line of connected subscription usage; null hides the row entirely. */
|
|
92
|
+
quota: string | null;
|
|
85
93
|
}
|
|
86
94
|
|
|
87
95
|
export type WelcomeDashboardMode = "launchpad" | "session";
|
|
@@ -536,13 +544,24 @@ function buildWelcomeDashboardLines(
|
|
|
536
544
|
theme.fg("dim", "Ask Clio how to use or extend her."),
|
|
537
545
|
theme.fg("dim", "Targets"),
|
|
538
546
|
theme.fg("muted", stats.targets),
|
|
547
|
+
// Keep the subscription field beside the wordmark, between Targets and
|
|
548
|
+
// Fleet. Wrap at the actual detail-column width instead of clipping the
|
|
549
|
+
// last accounts (especially Local) off a single concatenated line.
|
|
550
|
+
...(stats.quota === null
|
|
551
|
+
? []
|
|
552
|
+
: wrapTextWithAnsi(
|
|
553
|
+
field("Subscriptions", theme.fg("muted", sanitizeCallTargetText(stats.quota))),
|
|
554
|
+
Math.max(1, detailWidth),
|
|
555
|
+
)),
|
|
539
556
|
theme.fg("dim", "Fleet"),
|
|
540
557
|
theme.fg("muted", stats.fleet),
|
|
541
558
|
];
|
|
559
|
+
|
|
542
560
|
return [
|
|
543
561
|
heading,
|
|
544
|
-
...
|
|
545
|
-
const
|
|
562
|
+
...Array.from({ length: Math.max(details.length, hints.length, sideBySide ? wordmark.length : 0) }, (_, index) => {
|
|
563
|
+
const detail = details[index] ?? "";
|
|
564
|
+
const art = sideBySide ? `${padAnsi(theme.fg("accent", wordmark[index] ?? ""), artWidth)} ` : "";
|
|
546
565
|
const content = `${art}${truncateToWidth(detail, detailWidth, GLYPH.ellipsis, false)}`;
|
|
547
566
|
return row(
|
|
548
567
|
showHints
|
|
@@ -577,6 +596,7 @@ function statsSignature(stats: WelcomeDashboardStats): string {
|
|
|
577
596
|
stats.autonomy,
|
|
578
597
|
stats.targets,
|
|
579
598
|
stats.fleet,
|
|
599
|
+
stats.quota,
|
|
580
600
|
].join("\0");
|
|
581
601
|
}
|
|
582
602
|
|
|
@@ -707,6 +727,7 @@ export class WelcomeDashboard implements WelcomeDashboardComponent {
|
|
|
707
727
|
projectContext: launchpad ? this.projectContext(cwd) : "checking",
|
|
708
728
|
submitKeyLabel: launchpad ? (this.deps.getSubmitKeyLabel?.() ?? null) : null,
|
|
709
729
|
autonomy: settings?.safety?.autonomy ?? "auto-edit",
|
|
730
|
+
quota: launchpad ? (this.deps.getQuotaSummary?.() ?? null) : null,
|
|
710
731
|
...inventory(settings, statuses, this.deps.getAgentCount?.()),
|
|
711
732
|
};
|
|
712
733
|
}
|
|
@@ -804,6 +825,8 @@ export function createBootWelcome(
|
|
|
804
825
|
routeReason: null,
|
|
805
826
|
projectContext: "checking",
|
|
806
827
|
submitKeyLabel,
|
|
828
|
+
// Boot paints before any provider has been read, so the row stays hidden.
|
|
829
|
+
quota: null,
|
|
807
830
|
autonomy: settings.safety.autonomy,
|
|
808
831
|
...inventory(settings),
|
|
809
832
|
};
|
package/src/tools/agent-tools.ts
CHANGED
|
@@ -497,5 +497,7 @@ export function resolveSessionTools(
|
|
|
497
497
|
supportsImages: runtime.runtimeResolution.capabilityDecisions.vision === true,
|
|
498
498
|
});
|
|
499
499
|
if (telemetry) input.telemetry = telemetry;
|
|
500
|
-
return resolveAgentTools(input)
|
|
500
|
+
return resolveAgentTools(input).filter(
|
|
501
|
+
(tool) => tool.name !== ToolNames.SelfCompact || !runtime.runtimeResolution.runtime.externalAgentLoop,
|
|
502
|
+
);
|
|
501
503
|
}
|
package/src/tools/bootstrap.ts
CHANGED
|
@@ -22,11 +22,13 @@ import { lazyTool } from "./lazy-tool.js";
|
|
|
22
22
|
import { monitorToolSurface } from "./monitor-surface.js";
|
|
23
23
|
import { panesToolSurface } from "./panes-surface.js";
|
|
24
24
|
import type { ToolRegistry } from "./registry.js";
|
|
25
|
+
import { createSelfCompactTool, type RequestSelfCompact } from "./self-compact.js";
|
|
25
26
|
import { steerToolSurface } from "./steer-surface.js";
|
|
26
27
|
|
|
27
28
|
export { toolPromptHintsForNames };
|
|
28
29
|
|
|
29
30
|
export interface ToolBootstrapDeps extends Omit<CoreToolBootstrapDeps, "mcpCapabilities"> {
|
|
31
|
+
requestSelfCompact?: RequestSelfCompact;
|
|
30
32
|
captureWorkerContext?: () => WorkerContextSnapshot | null;
|
|
31
33
|
dispatch?: DispatchContract;
|
|
32
34
|
bus?: SafeEventBus;
|
|
@@ -77,6 +79,10 @@ export function registerAllTools(registry: ToolRegistry, deps: ToolBootstrapDeps
|
|
|
77
79
|
...coreDeps,
|
|
78
80
|
...(mcpCapabilities ? { mcpCapabilities } : {}),
|
|
79
81
|
});
|
|
82
|
+
if (deps.requestSelfCompact)
|
|
83
|
+
registry.register(
|
|
84
|
+
builtin(createSelfCompactTool(deps.requestSelfCompact), { path: "src/tools/self-compact.ts", scope: "core" }),
|
|
85
|
+
);
|
|
80
86
|
if (deps.dispatch) {
|
|
81
87
|
const dispatch = deps.dispatch;
|
|
82
88
|
// Display tail only. In a composed process the dispatch domain writes the
|
|
@@ -100,6 +100,15 @@ const TOOL_METADATA: Readonly<Record<string, ToolMetadata>> = {
|
|
|
100
100
|
promptHint:
|
|
101
101
|
"Use code_nav with source=workspace (default) for project code and source=clio for Clio's shipped code map; modes: symbol, path, entries, outline, deps, dependents, wiki (workspace only).",
|
|
102
102
|
},
|
|
103
|
+
[ToolNames.SelfCompact]: {
|
|
104
|
+
objective: "Save an exact assistant handoff and reduce native session context.",
|
|
105
|
+
uiLabel: "Compact",
|
|
106
|
+
retrySafety: "not_retry_safe",
|
|
107
|
+
resultSizePolicy: { kind: "exact", maxBytes: 2048 },
|
|
108
|
+
costLatency: "local_fast",
|
|
109
|
+
promptHint:
|
|
110
|
+
'Use self_compact alone at a safe boundary. Preserve decisions, evidence, paths, and next actions in note_to_self. Inspect context(scope="budget") for current pressure; recovery belongs to the operator.',
|
|
111
|
+
},
|
|
103
112
|
[ToolNames.Context]: {
|
|
104
113
|
objective: "Return workspace, effective configuration, skill, or recall context.",
|
|
105
114
|
uiLabel: "Context",
|