@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,7 +1,14 @@
|
|
|
1
|
+
import type { LiveBudgetView } from "../domains/context/budget/live-view.js";
|
|
1
2
|
import type { WorkerContextSnapshot } from "../domains/context/worker/contract.js";
|
|
2
3
|
import { captureWorkerContext } from "../domains/context/worker/snapshot.js";
|
|
3
|
-
import {
|
|
4
|
+
import type { MemoryPromptRequest } from "../domains/memory/prompt-cache.js";
|
|
5
|
+
import type { MemoryInterventionRegistration } from "../domains/middleware/memory-intervention.js";
|
|
6
|
+
import { estimateAgentMessageTokens } from "../domains/session/context-accounting.js";
|
|
7
|
+
import { createContinuityPersistencePorts } from "../domains/session/continuity/ports.js";
|
|
8
|
+
import { continuityReplayText, resolveContinuityProjection } from "../domains/session/continuity/projection.js";
|
|
9
|
+
import { continueEngineWithoutInput, replaceEngineMessages } from "../engine/agent.js";
|
|
4
10
|
import { isLockedSynthesisFallbackOnly, lockedSynthesisRepromptMessages } from "../engine/loop-guard.js";
|
|
11
|
+
import { ContinuityController } from "./continuity-controller.js";
|
|
5
12
|
/**
|
|
6
13
|
* The chat loop: one turn's state machine.
|
|
7
14
|
*
|
|
@@ -31,6 +38,8 @@ import {
|
|
|
31
38
|
} from "../core/skill-activation.js";
|
|
32
39
|
import { snapshotTurnConstraints, type TurnConstraints } from "../core/turn-constraints.js";
|
|
33
40
|
import { clioStateDir } from "../core/xdg.js";
|
|
41
|
+
import type { BudgetInspection } from "../domains/context/budget/inspection.js";
|
|
42
|
+
import { requestFits } from "../domains/context/budget/request-fit.js";
|
|
34
43
|
import {
|
|
35
44
|
createMiddlewareToolChoiceControl,
|
|
36
45
|
type MiddlewareContract,
|
|
@@ -52,8 +61,7 @@ import {
|
|
|
52
61
|
import { type AutonomyLevel, modelMayActivateSkills } from "../domains/safety/autonomy.js";
|
|
53
62
|
import type { ProtectedArtifactState } from "../domains/safety/protected-artifacts.js";
|
|
54
63
|
import type { CompactInput, CompactResult } from "../domains/session/compaction/compact.js";
|
|
55
|
-
import type { ContextSnapshot
|
|
56
|
-
import { ceilChars, snapshotInputTokens } from "../domains/session/context-accounting.js";
|
|
64
|
+
import type { ContextSnapshot } from "../domains/session/context-accounting.js";
|
|
57
65
|
import type { ContextLedger } from "../domains/session/context-ledger.js";
|
|
58
66
|
import type { SessionContract } from "../domains/session/contract.js";
|
|
59
67
|
import {
|
|
@@ -67,7 +75,6 @@ import { protectedArtifactStateFromSessionEntries } from "../domains/session/pro
|
|
|
67
75
|
import { isRetryableErrorMessage, type RetrySettings } from "../domains/session/retry.js";
|
|
68
76
|
import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
|
|
69
77
|
import { createEngineAgent } from "../engine/agent.js";
|
|
70
|
-
import { resolveReservedOutputTokens } from "../engine/apis/output-budget.js";
|
|
71
78
|
import { cwdHash } from "../engine/session.js";
|
|
72
79
|
import type { AgentEvent, AgentMessage, ImageContent, Usage } from "../engine/types.js";
|
|
73
80
|
import { resolveSessionTools } from "../tools/agent-tools.js";
|
|
@@ -90,7 +97,7 @@ import type { ApprovalRequestView } from "./permission-overlay.js";
|
|
|
90
97
|
import type { runPrewarmRound } from "./prewarm.js";
|
|
91
98
|
import { runSideQuestion, type SideQuestionResult, sideQuestionUsage } from "./side-question.js";
|
|
92
99
|
import type { AgentStatusEvent } from "./status/types.js";
|
|
93
|
-
import { createTurnContext } from "./turn-context.js";
|
|
100
|
+
import { createTurnContext, type LiveContextUsage } from "./turn-context.js";
|
|
94
101
|
import { createTurnMiddleware } from "./turn-middleware.js";
|
|
95
102
|
import { createTurnPersistence } from "./turn-persistence.js";
|
|
96
103
|
import { createTurnPrewarm, type PrewarmOutcome, subscribePrewarmToCompaction } from "./turn-prewarm.js";
|
|
@@ -338,7 +345,7 @@ export interface ChatLoop {
|
|
|
338
345
|
turnPreparation(): { phase: TurnPreparationPhase; since: number };
|
|
339
346
|
/** Fires on every preparation transition, including back to `idle`. */
|
|
340
347
|
onTurnPreparation(handler: (phase: TurnPreparationPhase) => void): () => void;
|
|
341
|
-
contextUsage():
|
|
348
|
+
contextUsage(): LiveContextUsage;
|
|
342
349
|
/**
|
|
343
350
|
* Categorized context-window ledger for the `/context` overlay: where every
|
|
344
351
|
* occupied token lives (system prompt, tools, agents, skills, memory,
|
|
@@ -346,6 +353,21 @@ export interface ChatLoop {
|
|
|
346
353
|
* estimate with the current turn's prompt segment manifest.
|
|
347
354
|
*/
|
|
348
355
|
contextLedger(): ContextLedger;
|
|
356
|
+
/**
|
|
357
|
+
* The published live budget for the next request: one immutable view with
|
|
358
|
+
* the effective window, the structural accounting, the real output
|
|
359
|
+
* reservation, headroom, pressure, and an opaque revision every consumer
|
|
360
|
+
* quotes. A pure read of what the producer last published; it never rescans
|
|
361
|
+
* the conversation, calls a model, persists anything, or triggers reduction.
|
|
362
|
+
*/
|
|
363
|
+
liveBudget(): LiveBudgetView;
|
|
364
|
+
inspectLiveBudget(): BudgetInspection;
|
|
365
|
+
/**
|
|
366
|
+
* Republish the live budget before making a decision from it, for a consumer
|
|
367
|
+
* that may be observing results appended since the last publication. The
|
|
368
|
+
* only thing it mutates is the accounting cache.
|
|
369
|
+
*/
|
|
370
|
+
refreshLiveBudget(): LiveBudgetView;
|
|
349
371
|
/**
|
|
350
372
|
* Force-run the compaction flow for the current session, swap the agent's
|
|
351
373
|
* in-memory `state.messages` for a single bridge message carrying the
|
|
@@ -356,13 +378,15 @@ export interface ChatLoop {
|
|
|
356
378
|
* notice so the `/context compact` handler does not have to mirror the logic.
|
|
357
379
|
*/
|
|
358
380
|
compact(instructions?: string): Promise<void>;
|
|
381
|
+
requestSelfCompact(note: unknown, toolCallId: string, signal?: AbortSignal): Promise<string>;
|
|
382
|
+
recoverHandoff(handoffId: string, action: "reduce" | "deliver"): Promise<void>;
|
|
359
383
|
/**
|
|
360
384
|
* `/btw`: answer one side question against the session's active target,
|
|
361
385
|
* model, and compiled message history without starting a turn.
|
|
362
386
|
*
|
|
363
387
|
* Nothing this produces reaches the session ledger, the transcript, the
|
|
364
388
|
* context ledger, or the footer token counters; the message history is read,
|
|
365
|
-
* never mutated. The round's provider usage is still reported to `/
|
|
389
|
+
* never mutated. The round's provider usage is still reported to `/usage`,
|
|
366
390
|
* labeled as a side question, because money was spent. Refused outright
|
|
367
391
|
* while a turn is in flight rather than queued.
|
|
368
392
|
*/
|
|
@@ -404,6 +428,7 @@ export interface ChatLoop {
|
|
|
404
428
|
}
|
|
405
429
|
|
|
406
430
|
export interface CreateChatLoopDeps {
|
|
431
|
+
memoryCommitBridge?: MemoryInterventionRegistration | undefined;
|
|
407
432
|
interactiveGuidance?: boolean;
|
|
408
433
|
getSettings: () => Readonly<ClioSettings>;
|
|
409
434
|
/**
|
|
@@ -470,7 +495,15 @@ export interface CreateChatLoopDeps {
|
|
|
470
495
|
autoCompact?: (
|
|
471
496
|
instructions?: string,
|
|
472
497
|
trigger?: CompactionTrigger,
|
|
473
|
-
budget?: Pick<
|
|
498
|
+
budget?: Pick<
|
|
499
|
+
CompactInput,
|
|
500
|
+
| "keepRecentTokens"
|
|
501
|
+
| "preserveUserTurnId"
|
|
502
|
+
| "skillContextState"
|
|
503
|
+
| "signal"
|
|
504
|
+
| "beforeSummaryCall"
|
|
505
|
+
| "checkpointForSummary"
|
|
506
|
+
>,
|
|
474
507
|
) => Promise<CompactResult | null>;
|
|
475
508
|
/** Optional observability sink for orchestrator chat token usage. */
|
|
476
509
|
observability?: ObservabilityContract;
|
|
@@ -519,7 +552,7 @@ export interface CreateChatLoopDeps {
|
|
|
519
552
|
* compiler injects via the memory dynamic fragment. Optional so unit
|
|
520
553
|
* tests omit it when memory is irrelevant.
|
|
521
554
|
*/
|
|
522
|
-
getMemorySection?: () => string;
|
|
555
|
+
getMemorySection?: (request: MemoryPromptRequest) => string;
|
|
523
556
|
getReadySkillCount?: () => number;
|
|
524
557
|
/** Structured, redacted task-bank export supplied only to an explicit context-handoff skill request. */
|
|
525
558
|
getTaskMemoryHandoffSource?: () => string;
|
|
@@ -528,7 +561,7 @@ export interface CreateChatLoopDeps {
|
|
|
528
561
|
* observers produce after their turn boundary closed. Called once during
|
|
529
562
|
* composition; the loop owns the buffer the reminder lands in.
|
|
530
563
|
*/
|
|
531
|
-
registerDeferredReminderSink?: (sink: (message: string) => void) => void;
|
|
564
|
+
registerDeferredReminderSink?: (sink: (message: string, isCurrent?: () => boolean) => void) => void;
|
|
532
565
|
/**
|
|
533
566
|
* The same seam for findings that are for the operator rather than the model.
|
|
534
567
|
* The watchdog uses it: its run settles after the turn it reviewed, and its
|
|
@@ -780,6 +813,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
780
813
|
});
|
|
781
814
|
|
|
782
815
|
const middleware = createTurnMiddleware({
|
|
816
|
+
memoryContentGuard: deps.memoryCommitBridge?.isContentCurrent,
|
|
783
817
|
state,
|
|
784
818
|
middleware: deps.middleware,
|
|
785
819
|
session: deps.session,
|
|
@@ -789,7 +823,9 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
789
823
|
});
|
|
790
824
|
|
|
791
825
|
try {
|
|
792
|
-
deps.registerDeferredReminderSink?.((message) =>
|
|
826
|
+
deps.registerDeferredReminderSink?.((message, isCurrent) =>
|
|
827
|
+
middleware.injectDeferredReminder(message, "advisory", isCurrent),
|
|
828
|
+
);
|
|
793
829
|
deps.registerDeferredNoticeSink?.((text) => middleware.emitDeferredNotice(text));
|
|
794
830
|
} catch {
|
|
795
831
|
// A background observer losing its delivery path must not stop the loop
|
|
@@ -809,11 +845,126 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
809
845
|
readSessionEntries: deps.readSessionEntries,
|
|
810
846
|
autoCompact: deps.autoCompact,
|
|
811
847
|
getMemorySection: deps.getMemorySection,
|
|
848
|
+
memoryCommitBridge: deps.memoryCommitBridge,
|
|
812
849
|
getReadySkillCount: deps.getReadySkillCount,
|
|
850
|
+
getPendingHandoff: () => {
|
|
851
|
+
const sessionId = deps.session?.current()?.id;
|
|
852
|
+
if (!sessionId) return null;
|
|
853
|
+
const fold = resolveContinuityProjection({
|
|
854
|
+
entries: filterEntriesToActivePath(deps.readSessionEntries?.() ?? [], state.lastTurnId ?? undefined),
|
|
855
|
+
sessionId,
|
|
856
|
+
}).current;
|
|
857
|
+
return fold?.identity && fold.phase !== "acknowledged"
|
|
858
|
+
? {
|
|
859
|
+
id: fold.identity.handoffId,
|
|
860
|
+
preparedAt: fold.policy ? new Date(fold.policy.preparedAtMs).toISOString() : null,
|
|
861
|
+
sourceRevision: fold.identity.sourceRevision,
|
|
862
|
+
}
|
|
863
|
+
: null;
|
|
864
|
+
},
|
|
865
|
+
getLastOutcome: () => {
|
|
866
|
+
const sessionId = deps.session?.current()?.id;
|
|
867
|
+
if (!sessionId) return null;
|
|
868
|
+
const fold = resolveContinuityProjection({
|
|
869
|
+
entries: filterEntriesToActivePath(deps.readSessionEntries?.() ?? [], state.lastTurnId ?? undefined),
|
|
870
|
+
sessionId,
|
|
871
|
+
}).current;
|
|
872
|
+
return fold?.identity ? { outcome: fold.phase, at: null, detail: fold.commit?.outcome ?? null } : null;
|
|
873
|
+
},
|
|
813
874
|
middleware,
|
|
814
875
|
emitNotice,
|
|
815
876
|
});
|
|
816
877
|
|
|
878
|
+
let continuityReplayInstalled = false;
|
|
879
|
+
const continuity = new ContinuityController({
|
|
880
|
+
captureOrigin: () => {
|
|
881
|
+
continuityReplayInstalled = false;
|
|
882
|
+
const session = deps.session;
|
|
883
|
+
const meta = session?.current();
|
|
884
|
+
const runtime = state.runtime;
|
|
885
|
+
const leaf = state.lastTurnId;
|
|
886
|
+
const operator = state.activeUserTurnId;
|
|
887
|
+
if (!session || !meta || !runtime || !leaf || !operator || context.inspectLiveBudget().status !== "available") {
|
|
888
|
+
throw new Error("Self-compaction requires a native runtime and a persisted operator turn.");
|
|
889
|
+
}
|
|
890
|
+
const revision = context.navigationRevision();
|
|
891
|
+
const route = `${deps.getSettings().chat.target}/${deps.getSettings().chat.model}`;
|
|
892
|
+
return {
|
|
893
|
+
sessionId: meta.id,
|
|
894
|
+
leafTurnId: leaf,
|
|
895
|
+
initiatingTurnId: operator,
|
|
896
|
+
sourceRevision: context.refreshLiveBudget().revision,
|
|
897
|
+
ports: createContinuityPersistencePorts({
|
|
898
|
+
session,
|
|
899
|
+
origin: {
|
|
900
|
+
sessionId: meta.id,
|
|
901
|
+
cwdHash: meta.cwdHash,
|
|
902
|
+
stillCurrent: () =>
|
|
903
|
+
context.navigationRevision() === revision &&
|
|
904
|
+
state.runtime === runtime &&
|
|
905
|
+
state.activeUserTurnId === operator &&
|
|
906
|
+
`${deps.getSettings().chat.target}/${deps.getSettings().chat.model}` === route,
|
|
907
|
+
},
|
|
908
|
+
}),
|
|
909
|
+
};
|
|
910
|
+
},
|
|
911
|
+
entries: () => filterEntriesToActivePath(deps.readSessionEntries?.() ?? [], state.lastTurnId ?? undefined),
|
|
912
|
+
leaf: () => state.lastTurnId,
|
|
913
|
+
admitNote: (accepted) => {
|
|
914
|
+
const view = context.refreshLiveBudget();
|
|
915
|
+
if (!view.breakdown || view.historical) return false;
|
|
916
|
+
const messages = state.runtime?.agent.state.messages ?? [];
|
|
917
|
+
const operator = [...messages].reverse().find((message) => message.role === "user");
|
|
918
|
+
const note = continuityReplayText({
|
|
919
|
+
note: accepted.note,
|
|
920
|
+
handoffId: "pending",
|
|
921
|
+
commitId: "pending",
|
|
922
|
+
originSessionId: view.sessionId ?? "",
|
|
923
|
+
phase: "prepared",
|
|
924
|
+
authority: "execution",
|
|
925
|
+
});
|
|
926
|
+
// Include the complete skill messages and tool receipts already in the
|
|
927
|
+
// protected suffix. The final guard still prices the actual replay.
|
|
928
|
+
const protectedSkills = messages
|
|
929
|
+
.filter((message) => message.role === "toolResult" && message.toolName === "context")
|
|
930
|
+
.reduce((sum, message) => sum + estimateAgentMessageTokens(message), 0);
|
|
931
|
+
const floor =
|
|
932
|
+
view.breakdown.systemPromptTokens +
|
|
933
|
+
view.breakdown.toolSchemaTokens +
|
|
934
|
+
protectedSkills +
|
|
935
|
+
(operator ? estimateAgentMessageTokens(operator) : 0) +
|
|
936
|
+
estimateAgentMessageTokens({ role: "assistant", content: [{ type: "text", text: note }] }) +
|
|
937
|
+
512;
|
|
938
|
+
return requestFits(floor, view.outputReserveTokens, view.effectiveWindow);
|
|
939
|
+
},
|
|
940
|
+
fits: () => {
|
|
941
|
+
const view = context.refreshLiveBudget();
|
|
942
|
+
return requestFits(view.inputTokens, view.outputReserveTokens, view.effectiveWindow);
|
|
943
|
+
},
|
|
944
|
+
inputTokens: () => context.refreshLiveBudget().inputTokens ?? 0,
|
|
945
|
+
reduce: async (hooks, signal) => {
|
|
946
|
+
if (!state.runtime) throw new Error("Native runtime was detached.");
|
|
947
|
+
continuityReplayInstalled = await context.runAutoCompact(
|
|
948
|
+
state.runtime,
|
|
949
|
+
true,
|
|
950
|
+
undefined,
|
|
951
|
+
"overflow",
|
|
952
|
+
undefined,
|
|
953
|
+
undefined,
|
|
954
|
+
signal,
|
|
955
|
+
hooks,
|
|
956
|
+
);
|
|
957
|
+
},
|
|
958
|
+
installReplay: () => {
|
|
959
|
+
if (state.runtime && !continuityReplayInstalled) {
|
|
960
|
+
context.refreshAgentMessagesFromSession(state.runtime);
|
|
961
|
+
continuityReplayInstalled = true;
|
|
962
|
+
}
|
|
963
|
+
},
|
|
964
|
+
onCommit: (commitId, outcome) => context.notifyMemoryCommit(commitId, "continuity", outcome),
|
|
965
|
+
notice: emitNotice,
|
|
966
|
+
});
|
|
967
|
+
|
|
817
968
|
const persistence = createTurnPersistence({
|
|
818
969
|
state,
|
|
819
970
|
session: deps.session,
|
|
@@ -851,6 +1002,8 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
851
1002
|
knownTargets: deps.knownTargets,
|
|
852
1003
|
observability: deps.observability,
|
|
853
1004
|
createAgent,
|
|
1005
|
+
continuity,
|
|
1006
|
+
hasQueuedSteering: () => queues.queuedMessages().steer.length > 0,
|
|
854
1007
|
middlewareToolChoice,
|
|
855
1008
|
persistence,
|
|
856
1009
|
context,
|
|
@@ -967,12 +1120,12 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
967
1120
|
deps.recordOutOfTurnUsageRow ?? ((row: OutOfTurnUsageRow): void => appendOutOfTurnUsageRow(clioStateDir(), row));
|
|
968
1121
|
|
|
969
1122
|
/**
|
|
970
|
-
* Report an out-of-turn round's provider usage. Money was spent, so `/
|
|
1123
|
+
* Report an out-of-turn round's provider usage. Money was spent, so `/usage`
|
|
971
1124
|
* says so under its own label; turn persistence, the working-set ledger,
|
|
972
1125
|
* compaction inputs, and the footer counters never see it.
|
|
973
1126
|
*
|
|
974
1127
|
* The same call is also appended to the out-of-turn usage store under the
|
|
975
|
-
* state dir. That store exists because `/
|
|
1128
|
+
* state dir. That store exists because `/usage` only knows what this process
|
|
976
1129
|
* spent: the round appends nothing to the session JSONL by design, so an
|
|
977
1130
|
* archive reader such as `clio-coder usage report` had no record of the
|
|
978
1131
|
* spend at all once the process exited. The session ledger stays untouched.
|
|
@@ -1174,6 +1327,11 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1174
1327
|
const pendingSkillRequests =
|
|
1175
1328
|
state.currentTurnConstraints?.skills === "disabled" ? [] : (options.pendingSkillRequests ?? []);
|
|
1176
1329
|
context.addWorkingContextPaths(options.workingContextPaths ?? []);
|
|
1330
|
+
context.prepareMemoryTurn(agentRuntime, {
|
|
1331
|
+
taskText: text,
|
|
1332
|
+
continuation: options.requestContinuation === true,
|
|
1333
|
+
images,
|
|
1334
|
+
});
|
|
1177
1335
|
// A skill the operator activated narrows the tools for the workflow
|
|
1178
1336
|
// it started, and that workflow outlives the turn it began in. A
|
|
1179
1337
|
// fresh /skill this turn replaces the armed surface; otherwise the
|
|
@@ -1205,7 +1363,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1205
1363
|
// the user message: persisted in the ledger, no hidden prompt
|
|
1206
1364
|
// machinery.
|
|
1207
1365
|
middleware.fireTurnStart(agentRuntime, text, pendingSkillRequests.length, options.requestContinuation === true);
|
|
1208
|
-
const
|
|
1366
|
+
const reminderProjection = middleware.takePendingReminderProjection();
|
|
1209
1367
|
// Pending skill requests are plain visible text in the user message
|
|
1210
1368
|
// itself: persisted in the ledger, no hidden prompt machinery.
|
|
1211
1369
|
const skillPreamble = pendingSkillRequestPreamble(pendingSkillRequests);
|
|
@@ -1218,9 +1376,9 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1218
1376
|
// the explicitly requested skill turn.
|
|
1219
1377
|
}
|
|
1220
1378
|
}
|
|
1221
|
-
const
|
|
1222
|
-
.filter((part) => part.length > 0)
|
|
1223
|
-
|
|
1379
|
+
const composeSubmittedText = () =>
|
|
1380
|
+
[reminderProjection(), skillPreamble, taskMemoryHandoffSource, text].filter((part) => part.length > 0).join("\n\n");
|
|
1381
|
+
let submittedText = composeSubmittedText();
|
|
1224
1382
|
|
|
1225
1383
|
// 2. Pre-submit auto-compaction trigger
|
|
1226
1384
|
const forceNow = process.env.CLIO_CODER_FORCE_COMPACT === "1";
|
|
@@ -1236,6 +1394,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1236
1394
|
|
|
1237
1395
|
// 3. Ensure the session prompt (compiles only on explicit events)
|
|
1238
1396
|
const compiledPrompt = await context.ensureSessionPrompt(agentRuntime);
|
|
1397
|
+
submittedText = composeSubmittedText();
|
|
1239
1398
|
|
|
1240
1399
|
// 4. Preflight overflow check, before the user turn is committed.
|
|
1241
1400
|
// A blocked request must not leave a dangling user entry that the
|
|
@@ -1252,63 +1411,27 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1252
1411
|
toolSignature,
|
|
1253
1412
|
});
|
|
1254
1413
|
|
|
1255
|
-
const effectiveWindow = agentRuntime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
|
|
1256
|
-
const outputForInput = (inputTokens: number): number =>
|
|
1257
|
-
resolveReservedOutputTokens(agentRuntime.runtimeResolution.capabilityDecisions.maxTokens, {
|
|
1258
|
-
api: agentRuntime.agent.state.model?.api ?? "",
|
|
1259
|
-
contextWindow: effectiveWindow,
|
|
1260
|
-
inputTokens,
|
|
1261
|
-
});
|
|
1262
|
-
const pendingInputTokens = ceilChars(submittedText.length);
|
|
1263
1414
|
let turnSnapshot = captureTurnSnapshot("pending");
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
// is what the request will actually cost, so the overflow guard reads
|
|
1267
|
-
// the higher of the two (issue #227). Both already carry the pending
|
|
1268
|
-
// user text and the tool schema estimate.
|
|
1269
|
-
const budgetedPromptTokens = (snapshot: ContextSnapshot): number =>
|
|
1270
|
-
Math.max(
|
|
1271
|
-
snapshotInputTokens(snapshot) + pendingInputTokens,
|
|
1272
|
-
context.liveContextEstimate(agentRuntime, submittedText).tokens,
|
|
1273
|
-
);
|
|
1274
|
-
const inputEstimate = budgetedPromptTokens(turnSnapshot);
|
|
1275
|
-
const reservedOutput = outputForInput(inputEstimate);
|
|
1276
|
-
const totalEstimate = inputEstimate + reservedOutput;
|
|
1277
|
-
|
|
1278
|
-
if (effectiveWindow > 0 && totalEstimate > effectiveWindow) {
|
|
1279
|
-
emitNotice(
|
|
1280
|
-
`[Clio Coder] Estimated request size ${totalEstimate} tokens (input ${totalEstimate - reservedOutput} + output budget ${reservedOutput}) exceeds the effective context window of ${effectiveWindow} tokens. Running compaction before sending...`,
|
|
1281
|
-
);
|
|
1415
|
+
let admission = context.refreshLiveBudget(submittedText);
|
|
1416
|
+
if (!requestFits(admission.inputTokens, admission.outputReserveTokens, admission.effectiveWindow)) {
|
|
1282
1417
|
setTurnPreparation("compacting");
|
|
1283
|
-
let
|
|
1284
|
-
|
|
1418
|
+
let failure: string | undefined;
|
|
1419
|
+
await context
|
|
1285
1420
|
.runAutoCompact(agentRuntime, true, undefined, "overflow", submittedText, pendingSkillPolicy)
|
|
1286
1421
|
.catch((error: unknown) => {
|
|
1287
|
-
|
|
1288
|
-
return false;
|
|
1422
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
1289
1423
|
})
|
|
1290
1424
|
.finally(endPreparationCompaction);
|
|
1291
|
-
|
|
1425
|
+
submittedText = composeSubmittedText();
|
|
1426
|
+
admission = context.refreshLiveBudget(submittedText);
|
|
1427
|
+
if (!requestFits(admission.inputTokens, admission.outputReserveTokens, admission.effectiveWindow)) {
|
|
1292
1428
|
emitAdmissionNotice(
|
|
1293
|
-
`[Clio Coder] Request exceeds the context window
|
|
1429
|
+
`[Clio Coder] Request exceeds the available context window (input ${admission.inputTokens ?? "unknown"} + output ${admission.outputReserveTokens ?? "unknown"}, window ${admission.effectiveWindow ?? "unknown"}).${failure ? ` ${failure}` : ""} Trim the prompt or reduce active tools.`,
|
|
1294
1430
|
"context-window-exceeded",
|
|
1295
1431
|
);
|
|
1296
1432
|
return;
|
|
1297
1433
|
}
|
|
1298
|
-
context.refreshAgentMessagesFromSession(agentRuntime);
|
|
1299
1434
|
turnSnapshot = captureTurnSnapshot("pending");
|
|
1300
|
-
const postInputEstimate = budgetedPromptTokens(turnSnapshot);
|
|
1301
|
-
const postTotalEstimate = postInputEstimate + outputForInput(postInputEstimate);
|
|
1302
|
-
if (postTotalEstimate > effectiveWindow) {
|
|
1303
|
-
emitAdmissionNotice(
|
|
1304
|
-
`[Clio Coder] Request still exceeds the effective window after compaction (${postTotalEstimate} > ${effectiveWindow}). Request blocked.`,
|
|
1305
|
-
"context-window-exceeded",
|
|
1306
|
-
);
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
emitNotice(
|
|
1310
|
-
`[Clio Coder] Context budget check passed post-compaction (${postTotalEstimate} <= ${effectiveWindow}). Proceeding.`,
|
|
1311
|
-
);
|
|
1312
1435
|
}
|
|
1313
1436
|
|
|
1314
1437
|
// 5. Append the user turn, then stamp and persist the snapshot.
|
|
@@ -1322,6 +1445,8 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1322
1445
|
text,
|
|
1323
1446
|
options.display?.text,
|
|
1324
1447
|
);
|
|
1448
|
+
context.installMemoryRestoration(agentRuntime, submittedText);
|
|
1449
|
+
context.commitMemoryTurn(agentRuntime);
|
|
1325
1450
|
// An interrupt was submitted while a run was active, so no caller drew
|
|
1326
1451
|
// it in the transcript; render it here, after the cancel notice and the
|
|
1327
1452
|
// cancelled run's leftovers, which is the order the ledger has.
|
|
@@ -1509,6 +1634,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1509
1634
|
},
|
|
1510
1635
|
|
|
1511
1636
|
cancel(options?: ChatCancelOptions): void {
|
|
1637
|
+
continuity.cancel();
|
|
1512
1638
|
const wasStreaming = state.streaming;
|
|
1513
1639
|
context.cancelCompaction();
|
|
1514
1640
|
recovery.cancelRetryCountdown();
|
|
@@ -1581,10 +1707,15 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1581
1707
|
contextUsage: () => context.contextUsage(),
|
|
1582
1708
|
currentTurnConstraints: () => state.currentTurnConstraints,
|
|
1583
1709
|
contextLedger: () => context.contextLedger(),
|
|
1710
|
+
liveBudget: () => context.liveBudget(),
|
|
1711
|
+
inspectLiveBudget: () => context.inspectLiveBudget(),
|
|
1712
|
+
refreshLiveBudget: () => context.refreshLiveBudget(),
|
|
1584
1713
|
whenSettled: () => activeSubmit,
|
|
1585
1714
|
whenPrewarmSettled: () => prewarm.settled(),
|
|
1586
1715
|
|
|
1587
1716
|
resetForSession(leafTurnId: string | null, replayMessages?: ReadonlyArray<AgentMessage>): void {
|
|
1717
|
+
continuity.cancel();
|
|
1718
|
+
void continuity.pause();
|
|
1588
1719
|
state.currentTurnConstraints = undefined;
|
|
1589
1720
|
if (state.runtime) {
|
|
1590
1721
|
state.runtime.agent.abort();
|
|
@@ -1597,7 +1728,10 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1597
1728
|
middlewareToolChoice.reset();
|
|
1598
1729
|
state.lastTurnId = leafTurnId;
|
|
1599
1730
|
state.lastRunSnapshot = null;
|
|
1600
|
-
|
|
1731
|
+
// The incoming leaf is the advisory branch identity for the new session.
|
|
1732
|
+
// Ordinary appends advance `state.lastTurnId` from here without re-arming
|
|
1733
|
+
// an advisory; arriving here at all is the navigation that does.
|
|
1734
|
+
context.resetForSession(leafTurnId);
|
|
1601
1735
|
// The resumed ledger renders before the first new turn (issue #189),
|
|
1602
1736
|
// and the window it renders against should be the probed one rather
|
|
1603
1737
|
// than the catalog's, so the live capability probe the first submit
|
|
@@ -1704,6 +1838,28 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1704
1838
|
return result.aborted ? { status: "aborted", text: result.text } : { status: "answered", text: result.text };
|
|
1705
1839
|
},
|
|
1706
1840
|
|
|
1841
|
+
requestSelfCompact: (note, toolCallId, signal) => continuity.request(note, toolCallId, signal),
|
|
1842
|
+
async recoverHandoff(handoffId, action) {
|
|
1843
|
+
if (state.streaming || state.turnPreparation !== "idle")
|
|
1844
|
+
throw new Error("Wait for the current turn to settle before recovery.");
|
|
1845
|
+
const runtime = turnRuntime.ensureRuntime();
|
|
1846
|
+
if (!runtime) throw new Error("No native runtime is configured.");
|
|
1847
|
+
state.activeInterruptReason = null;
|
|
1848
|
+
if (!state.activeUserTurnId) {
|
|
1849
|
+
const operator = [...filterEntriesToActivePath(deps.readSessionEntries?.() ?? [], state.lastTurnId ?? undefined)]
|
|
1850
|
+
.reverse()
|
|
1851
|
+
.find((entry) => entry.kind === "message" && entry.role === "user");
|
|
1852
|
+
state.activeUserTurnId = operator?.turnId ?? null;
|
|
1853
|
+
}
|
|
1854
|
+
state.streaming = true;
|
|
1855
|
+
try {
|
|
1856
|
+
await continuity.recover(handoffId, action);
|
|
1857
|
+
await continueEngineWithoutInput(runtime.agent);
|
|
1858
|
+
} finally {
|
|
1859
|
+
state.streaming = false;
|
|
1860
|
+
await continuity.pause();
|
|
1861
|
+
}
|
|
1862
|
+
},
|
|
1707
1863
|
async compact(instructions?: string): Promise<void> {
|
|
1708
1864
|
// Session check runs BEFORE orchestrator-configuration so a fresh
|
|
1709
1865
|
// TUI with nothing configured still reports the actionable "no
|
|
@@ -365,6 +365,48 @@ export interface RehydrateChatPanelOptions {
|
|
|
365
365
|
* every failure, so a session whose receipts are gone still replays.
|
|
366
366
|
*/
|
|
367
367
|
readWorkerReceipt?: WorkerReceiptReader;
|
|
368
|
+
/**
|
|
369
|
+
* Already-rendered continuity blocks, in order, from
|
|
370
|
+
* `continuityReplayBlocks`. Each is emitted verbatim exactly once and never
|
|
371
|
+
* routed through `appendContextMessage`, whose trim and replay cap would
|
|
372
|
+
* crop an accepted note; §4 forbids trimming, normalizing or regenerating
|
|
373
|
+
* accepted text. Resolving which blocks these are needs the session and fork
|
|
374
|
+
* facts a renderer does not have, so the caller supplies the finished text
|
|
375
|
+
* and this module only places it.
|
|
376
|
+
*/
|
|
377
|
+
continuityBlocks?: ReadonlyArray<string>;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* The active-path slice a replay works from, **before** the compaction cut.
|
|
382
|
+
*
|
|
383
|
+
* Exported because the continuity fold needs exactly this array: evidence for a
|
|
384
|
+
* transaction routinely sits older than the cut, and `selectReplayEntries`
|
|
385
|
+
* would both drop it and renumber every surviving position. A caller that
|
|
386
|
+
* resolves a projection folds this, then renders, so the two agree on one
|
|
387
|
+
* ordered input.
|
|
388
|
+
*/
|
|
389
|
+
export function activeEntriesBeforeCompactionCut(
|
|
390
|
+
turns: ReadonlyArray<SessionEntry>,
|
|
391
|
+
options: RehydrateChatPanelOptions = {},
|
|
392
|
+
/**
|
|
393
|
+
* Whether to apply `uptoTurnId`'s positional truncation.
|
|
394
|
+
*
|
|
395
|
+
* Display replay always does. Continuity does not, unless the selection is a
|
|
396
|
+
* genuine historical cut, because `uptoTurnId` carries two different meanings
|
|
397
|
+
* through one option: a live `/tree` switch passes it to stop the transcript
|
|
398
|
+
* at the selected message, and a historical fork passes it to describe a
|
|
399
|
+
* moment that really had no later records. Truncating for the first case
|
|
400
|
+
* physically discards a pause, a delivery or an acknowledgement anchored
|
|
401
|
+
* after the selected message, and a fold cannot restore a record it was never
|
|
402
|
+
* shown: leaving a `historical` flag false does not undo a cut already made.
|
|
403
|
+
* The branch selection honors the leaf either way; only the positional cut
|
|
404
|
+
* is conditional.
|
|
405
|
+
*/
|
|
406
|
+
truncateAtSelection = true,
|
|
407
|
+
): SessionEntry[] {
|
|
408
|
+
const active = filterEntriesToActivePath(turns, options.activeLeafTurnId ?? options.uptoTurnId);
|
|
409
|
+
return truncateAtSelection ? truncateAtTurn(active, options.uptoTurnId) : active;
|
|
368
410
|
}
|
|
369
411
|
|
|
370
412
|
function extractTurnText(payload: unknown): string {
|
|
@@ -1105,15 +1147,23 @@ export function buildReplayAgentMessagesFromTurns(
|
|
|
1105
1147
|
): AgentMessage[] {
|
|
1106
1148
|
const out = createOrderedReplaySink();
|
|
1107
1149
|
const seenToolCalls = new Set<string>();
|
|
1108
|
-
const activeEntries =
|
|
1109
|
-
filterEntriesToActivePath(turns, options.activeLeafTurnId ?? options.uptoTurnId),
|
|
1110
|
-
options.uptoTurnId,
|
|
1111
|
-
);
|
|
1150
|
+
const activeEntries = activeEntriesBeforeCompactionCut(turns, options);
|
|
1112
1151
|
const skillState = latestSkillContextState(activeEntries);
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1152
|
+
// One emission point for the continuity blocks, tracked by a flag rather than
|
|
1153
|
+
// by counting occurrences: the note has to appear exactly once whether the
|
|
1154
|
+
// replay window holds a carrying summary, an eviction-only commit, or
|
|
1155
|
+
// neither. `selectReplayEntries` keeps at most one compaction summary, so
|
|
1156
|
+
// the in-loop emission below cannot fire twice.
|
|
1157
|
+
let continuityEmitted = false;
|
|
1158
|
+
const emitContinuity = (timestamp: string): void => {
|
|
1159
|
+
if (continuityEmitted) return;
|
|
1160
|
+
continuityEmitted = true;
|
|
1161
|
+
// Verbatim. The accepted note is agent-authored handoff text and is
|
|
1162
|
+
// carried as data; it is never presented as an operator turn and never
|
|
1163
|
+
// passes through the trimming/truncating context helper.
|
|
1164
|
+
for (const block of options.continuityBlocks ?? []) out.push(makeTextMessage("user", block, timestamp));
|
|
1165
|
+
};
|
|
1166
|
+
const evidence = activeEntriesBeforeCompactionCut(options.skillContextEntries ?? turns, options);
|
|
1117
1167
|
const verified = captureSkillContext(evidence, skillState);
|
|
1118
1168
|
for (const entry of selectReplayEntries(turns, options)) {
|
|
1119
1169
|
switch (entry.kind) {
|
|
@@ -1181,6 +1231,10 @@ export function buildReplayAgentMessagesFromTurns(
|
|
|
1181
1231
|
for (const block of skill.content) out.push(makeTextMessage("user", block.text, entry.timestamp));
|
|
1182
1232
|
}
|
|
1183
1233
|
}
|
|
1234
|
+
// The reduction boundary is where the handoff happened, so the note
|
|
1235
|
+
// sits directly after the summary that replaced the history it
|
|
1236
|
+
// describes, whether or not this summary is the one that carried it.
|
|
1237
|
+
emitContinuity(entry.timestamp);
|
|
1184
1238
|
break;
|
|
1185
1239
|
case "skillActivation":
|
|
1186
1240
|
if (entry.activation.runId !== undefined || (skillState && !skillState.activationRefs.includes(entry.turnId)))
|
|
@@ -1213,9 +1267,20 @@ export function buildReplayAgentMessagesFromTurns(
|
|
|
1213
1267
|
// entries themselves never become messages.
|
|
1214
1268
|
case "contextEviction":
|
|
1215
1269
|
case "contextRecall":
|
|
1270
|
+
// Continuity records are bookkeeping. The chain and the commit payload
|
|
1271
|
+
// never become model messages; the one thing that reaches the model is
|
|
1272
|
+
// the accepted note, projected once through `continuityBlocks`. Handling
|
|
1273
|
+
// each commit here instead would emit a copy per record and would still
|
|
1274
|
+
// miss a commit that sits before the cut.
|
|
1275
|
+
case "handoffTransaction":
|
|
1276
|
+
case "continuityCommit":
|
|
1216
1277
|
break;
|
|
1217
1278
|
}
|
|
1218
1279
|
}
|
|
1280
|
+
// No compaction summary in the replay window: an eviction-only or
|
|
1281
|
+
// continuity-only outcome still has a note to carry, so it lands at the end
|
|
1282
|
+
// of the replayed history rather than being dropped.
|
|
1283
|
+
emitContinuity(activeEntries[activeEntries.length - 1]?.timestamp ?? new Date(0).toISOString());
|
|
1219
1284
|
out.flush();
|
|
1220
1285
|
return out.messages;
|
|
1221
1286
|
}
|
|
@@ -1412,6 +1477,21 @@ export function rehydrateChatPanelFromTurns(
|
|
|
1412
1477
|
chatPanel.applyWorkerState(state);
|
|
1413
1478
|
break;
|
|
1414
1479
|
}
|
|
1480
|
+
// One bounded provenance line each. The transcript says a handoff
|
|
1481
|
+
// happened and where it stands; the note itself is rendered once,
|
|
1482
|
+
// below, under its own label, rather than once per record.
|
|
1483
|
+
case "handoffTransaction":
|
|
1484
|
+
appendReplayLine(
|
|
1485
|
+
chatPanel,
|
|
1486
|
+
`[continuity] ${entry.event.phase} handoff=${entry.identity.handoffId} seq=${entry.transition.sequence} attempt=${entry.transition.attempt}`,
|
|
1487
|
+
);
|
|
1488
|
+
break;
|
|
1489
|
+
case "continuityCommit":
|
|
1490
|
+
appendReplayLine(
|
|
1491
|
+
chatPanel,
|
|
1492
|
+
`[continuity] commit handoff=${entry.continuity.identity.handoffId} outcome=${entry.continuity.commit.outcome} tokens=${entry.continuity.commit.tokensBefore}->${entry.continuity.commit.tokensAfter}`,
|
|
1493
|
+
);
|
|
1494
|
+
break;
|
|
1415
1495
|
case "label":
|
|
1416
1496
|
case "taskLedger":
|
|
1417
1497
|
case "decisionLedger":
|
|
@@ -1420,6 +1500,17 @@ export function rehydrateChatPanelFromTurns(
|
|
|
1420
1500
|
break;
|
|
1421
1501
|
}
|
|
1422
1502
|
}
|
|
1503
|
+
// The transcript shows the note as what it is: assistant-authored handoff
|
|
1504
|
+
// text, labelled, never rendered as an operator turn.
|
|
1505
|
+
//
|
|
1506
|
+
// This is a presentation view, not the durable text. The panel wraps to its
|
|
1507
|
+
// width and strips terminal control sequences, so a rendered line is not
|
|
1508
|
+
// byte-identical to the note; §12 permits an export transformation exactly
|
|
1509
|
+
// when it is labelled, and the exact bytes stay in the ledger and in the
|
|
1510
|
+
// model replay the same blocks feed.
|
|
1511
|
+
for (const block of options.continuityBlocks ?? []) {
|
|
1512
|
+
for (const line of block.split("\n")) appendReplayLine(chatPanel, line);
|
|
1513
|
+
}
|
|
1423
1514
|
for (const pendingId of pendingToolIds) {
|
|
1424
1515
|
chatPanel.applyEvent({
|
|
1425
1516
|
type: "tool_execution_end",
|