@iowarp/clio-coder 0.3.8 → 0.3.9
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/CHANGELOG.md +49 -0
- package/README.md +7 -3
- package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
- package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
- package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
- package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
- package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
- package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
- package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
- package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
- package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
- package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
- package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
- package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
- package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
- package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
- package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
- package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
- package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
- package/dist/chunk-7C6RYZGQ.js +391 -0
- package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
- package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
- package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
- package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
- package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
- package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
- package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
- package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
- package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
- package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
- package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
- package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
- package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
- package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
- package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
- package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
- package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
- package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
- package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
- package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
- package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
- package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
- package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
- package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
- package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
- package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
- package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
- package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
- package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
- package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
- package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
- package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
- package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
- package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
- package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
- package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
- package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
- package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
- package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
- package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
- package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
- package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
- package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
- package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
- package/dist/chunk-YSEHGPCT.js +127 -0
- package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
- package/dist/cli/index.js +27 -27
- package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
- package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
- package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
- package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
- package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
- package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
- package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
- package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
- package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
- package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
- package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
- package/dist/doctor-TWBWFK5V.js +165 -0
- package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
- package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
- package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
- package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
- package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
- package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
- package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
- package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
- package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
- package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
- package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
- package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
- package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
- package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
- package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
- package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
- package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
- package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
- package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
- package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
- package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
- package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
- package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
- package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
- package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
- package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
- package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
- package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
- package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
- package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
- package/dist/worker/entry.js +29 -33
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +6 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +23 -2
- package/docs/commands-and-modes.md +1 -1
- package/docs/configuration-and-targets.md +30 -3
- package/docs/context-engine.md +63 -4
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +2 -0
- package/docs/eval-runner.md +262 -11
- package/docs/evals-internal.md +72 -2
- package/docs/evidence-and-memory.md +11 -10
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +3 -1
- package/docs/fleet-dispatch.md +4 -4
- package/docs/installation-and-lifecycle.md +1 -1
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +53 -2
- package/docs/proactive-memory.md +127 -14
- package/docs/prompt-envelope-and-tools.md +19 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +19 -3
- package/docs/safety-model.md +1 -1
- package/docs/scientific-validation.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +1 -1
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +87 -0
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +2 -1
- package/src/cli/agents.ts +1 -1
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor.ts +3 -1
- package/src/cli/eval.ts +80 -16
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet.ts +32 -3
- package/src/cli/targets.ts +44 -13
- package/src/cli/trace.ts +63 -4
- package/src/cli/usage.ts +63 -14
- package/src/core/bus-events.ts +29 -1
- package/src/core/cache-telemetry.ts +42 -0
- package/src/core/config.ts +18 -0
- package/src/core/defaults.ts +36 -6
- package/src/core/endpoint-key.ts +27 -0
- package/src/core/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +36 -2
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/dispatch/admission.ts +40 -3
- package/src/domains/dispatch/capacity-lease.ts +98 -9
- package/src/domains/dispatch/contract.ts +11 -0
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/extension.ts +166 -42
- package/src/domains/dispatch/fleet-run.ts +23 -3
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +3 -0
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/reservation-store.ts +116 -8
- package/src/domains/dispatch/state.ts +4 -0
- package/src/domains/dispatch/worker-spawn.ts +25 -11
- package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
- package/src/domains/dispatch/write-boundary.ts +62 -1
- package/src/domains/eval/artifacts/store.ts +62 -0
- package/src/domains/eval/compare/behavioral.ts +224 -0
- package/src/domains/eval/compare/compare.ts +355 -2
- package/src/domains/eval/compare/envelope.ts +128 -0
- package/src/domains/eval/compare/gates.ts +24 -6
- package/src/domains/eval/compare/thresholds.ts +30 -3
- package/src/domains/eval/execution-provenance.ts +240 -0
- package/src/domains/eval/metrics/aggregate.ts +136 -0
- package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
- package/src/domains/eval/metrics/tracked.ts +413 -0
- package/src/domains/eval/provenance.ts +117 -0
- package/src/domains/eval/reports/comparison.ts +128 -0
- package/src/domains/eval/reports/junit.ts +17 -3
- package/src/domains/eval/reports/markdown.ts +3 -3
- package/src/domains/eval/reports/text.ts +14 -0
- package/src/domains/eval/run-compare.ts +20 -0
- package/src/domains/eval/runners/clio-run.ts +127 -0
- package/src/domains/eval/runners/external-command.ts +28 -3
- package/src/domains/eval/schema/adapter.ts +111 -0
- package/src/domains/eval/schema/artifact.ts +20 -0
- package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
- package/src/domains/eval/schema/behavioral.ts +520 -0
- package/src/domains/eval/schema/execution-envelope.ts +194 -0
- package/src/domains/eval/schema/serving.ts +74 -0
- package/src/domains/eval/schema/suite.ts +38 -8
- package/src/domains/eval/schema/validate.ts +58 -3
- package/src/domains/eval/schema/verdict.ts +237 -0
- package/src/domains/eval/suites/resolve.ts +2 -0
- package/src/domains/eval/suites/run.ts +264 -33
- package/src/domains/eval/verifiers/command.ts +2 -1
- package/src/domains/eval/workspaces/temp-copy.ts +145 -13
- package/src/domains/evidence/build.ts +2 -13
- package/src/domains/evidence/eval.ts +2 -12
- package/src/domains/evidence/findings-markdown.ts +33 -0
- package/src/domains/evidence/run-trust.ts +7 -113
- package/src/domains/evidence/trust-projection.ts +2 -2
- package/src/domains/extensions/compatibility.ts +285 -0
- package/src/domains/extensions/discovery.ts +38 -3
- package/src/domains/extensions/resources.ts +1 -1
- package/src/domains/extensions/state.ts +12 -3
- package/src/domains/extensions/types.ts +2 -0
- package/src/domains/lifecycle/doctor.ts +69 -1
- package/src/domains/memory/index.ts +14 -0
- package/src/domains/memory/task-bank-promotion.ts +64 -0
- package/src/domains/memory/task-memory-policy.ts +77 -8
- package/src/domains/memory/task-memory-spend.ts +131 -0
- package/src/domains/memory/task-memory-status.ts +7 -0
- package/src/domains/memory/task-memory-telemetry.ts +2 -0
- package/src/domains/middleware/index.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +69 -5
- package/src/domains/middleware/memory-step-endpoint.ts +71 -0
- package/src/domains/observability/background-memory-usage.ts +140 -0
- package/src/domains/observability/cost.ts +1 -1
- package/src/domains/observability/index.ts +7 -0
- package/src/domains/observability/out-of-turn-usage.ts +51 -2
- package/src/domains/observability/trace-store.ts +192 -2
- package/src/domains/prompts/compiler.ts +100 -13
- package/src/domains/providers/endpoint-capacity.ts +96 -0
- package/src/domains/providers/index.ts +10 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
- package/src/domains/providers/runtime-resolution.ts +8 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
- package/src/domains/providers/types/capability-flags.ts +2 -0
- package/src/domains/providers/types/target-descriptor.ts +2 -0
- package/src/domains/resources/prompts/loader.ts +95 -33
- package/src/domains/safety/call-target.ts +52 -0
- package/src/domains/safety/run-effects.ts +35 -4
- package/src/domains/session/context-accounting.ts +52 -1
- package/src/domains/session/context-ledger.ts +37 -13
- package/src/domains/session/index.ts +6 -0
- package/src/domains/session/prompt-cache.ts +140 -0
- package/src/domains/session/prompt-manifest.ts +42 -0
- package/src/engine/acp/adapter.ts +18 -3
- package/src/engine/ai.ts +35 -0
- package/src/engine/apis/llamacpp-residency.ts +55 -3
- package/src/engine/apis/lmstudio.ts +25 -5
- package/src/engine/apis/ollama-native.ts +2 -1
- package/src/engine/apis/openai-completions.ts +80 -17
- package/src/engine/apis/residency-lock.ts +3 -1
- package/src/engine/apis/residency.ts +34 -1
- package/src/engine/provider-payload.ts +29 -1
- package/src/entry/orchestrator.ts +176 -30
- package/src/interactive/chat-loop-messages.ts +26 -7
- package/src/interactive/chat-loop.ts +318 -41
- package/src/interactive/chat-panel.ts +62 -8
- package/src/interactive/clio-editor.ts +45 -8
- package/src/interactive/context-activity.ts +5 -1
- package/src/interactive/context-meter.ts +1 -1
- package/src/interactive/context-overlay.ts +40 -10
- package/src/interactive/cost-overlay.ts +64 -6
- package/src/interactive/dispatch-board.ts +84 -12
- package/src/interactive/fleet-run-preview.ts +41 -15
- package/src/interactive/handoff-round.ts +41 -2
- package/src/interactive/interactive-application.ts +24 -1
- package/src/interactive/interactive-input-runtime.ts +8 -0
- package/src/interactive/interactive-presentation.ts +4 -0
- package/src/interactive/interactive-shell.ts +20 -17
- package/src/interactive/interactive-slash-runtime.ts +27 -4
- package/src/interactive/memory-overlay.ts +8 -0
- package/src/interactive/mutation-preview.ts +295 -0
- package/src/interactive/overlay-general-openers.ts +16 -0
- package/src/interactive/overlay-key-routing.ts +38 -0
- package/src/interactive/overlay-lifecycle.ts +38 -5
- package/src/interactive/overlay-permission-lifecycle.ts +22 -2
- package/src/interactive/overlay-session-lifecycle.ts +73 -9
- package/src/interactive/overlays/ask-user.ts +91 -19
- package/src/interactive/overlays/help-reference.ts +4 -0
- package/src/interactive/overlays/prompts.ts +11 -1
- package/src/interactive/overlays/settings.ts +35 -1
- package/src/interactive/permission-hint.ts +34 -2
- package/src/interactive/permission-overlay.ts +159 -9
- package/src/interactive/prewarm.ts +197 -0
- package/src/interactive/render-trace.ts +162 -15
- package/src/interactive/renderers/tool-execution.ts +4 -0
- package/src/interactive/side-question.ts +58 -1
- package/src/interactive/status/controller.ts +11 -0
- package/src/interactive/status/state-machine.ts +54 -2
- package/src/interactive/status/types.ts +7 -0
- package/src/interactive/terminal-lease.ts +2 -0
- package/src/interactive/turn-context.ts +299 -31
- package/src/interactive/turn-persistence.ts +14 -4
- package/src/interactive/turn-prewarm.ts +364 -0
- package/src/interactive/turn-queues.ts +7 -4
- package/src/interactive/turn-runtime.ts +8 -1
- package/src/interactive/turn-state.ts +23 -0
- package/src/interactive/view/view-overlay.ts +28 -3
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/dispatch-plan.ts +17 -9
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/registry.ts +16 -0
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-R346GLFC.js +0 -31
- package/dist/doctor-M7YEDGAE.js +0 -91
|
@@ -29,7 +29,13 @@ import { appendOutOfTurnUsageRow, type OutOfTurnUsageRow } from "../domains/obse
|
|
|
29
29
|
import type { PromptsContract } from "../domains/prompts/contract.js";
|
|
30
30
|
import { toContextOverflowError } from "../domains/providers/errors.js";
|
|
31
31
|
import type { ProvidersContract } from "../domains/providers/index.js";
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
canonicalEndpointKey,
|
|
34
|
+
normalizeCostProvenance,
|
|
35
|
+
registerForegroundStream,
|
|
36
|
+
runtimeTargetSnapshot,
|
|
37
|
+
targetRequiresAuth,
|
|
38
|
+
} from "../domains/providers/index.js";
|
|
33
39
|
import type { ProtectedArtifactState } from "../domains/safety/protected-artifacts.js";
|
|
34
40
|
import type { CompactResult } from "../domains/session/compaction/compact.js";
|
|
35
41
|
import type { ContextSnapshot, ContextUsageSnapshot } from "../domains/session/context-accounting.js";
|
|
@@ -42,7 +48,7 @@ import { isRetryableErrorMessage, type RetrySettings } from "../domains/session/
|
|
|
42
48
|
import { createEngineAgent } from "../engine/agent.js";
|
|
43
49
|
import { resolveReservedOutputTokens } from "../engine/apis/output-budget.js";
|
|
44
50
|
import { cwdHash } from "../engine/session.js";
|
|
45
|
-
import type { AgentEvent, AgentMessage, ImageContent } from "../engine/types.js";
|
|
51
|
+
import type { AgentEvent, AgentMessage, ImageContent, Usage } from "../engine/types.js";
|
|
46
52
|
import { resolveSessionTools } from "../tools/agent-tools.js";
|
|
47
53
|
import { finalizeAskUserInterviewForHost } from "../tools/ask-user.js";
|
|
48
54
|
import type { AskUserToolPolicy, ToolInvokeOptions, ToolRegistry } from "../tools/registry.js";
|
|
@@ -57,13 +63,15 @@ import {
|
|
|
57
63
|
toolSignatureFromState,
|
|
58
64
|
} from "./chat-loop-messages.js";
|
|
59
65
|
import { normalizeRetrySettings } from "./chat-loop-policy.js";
|
|
60
|
-
import { runHandoffRound } from "./handoff-round.js";
|
|
66
|
+
import { type HandoffRepairInput, runHandoffRound } from "./handoff-round.js";
|
|
61
67
|
import type { ApprovalRequestView } from "./permission-overlay.js";
|
|
62
|
-
import {
|
|
68
|
+
import type { runPrewarmRound } from "./prewarm.js";
|
|
69
|
+
import { runSideQuestion, type SideQuestionResult, sideQuestionUsage } from "./side-question.js";
|
|
63
70
|
import type { AgentStatusEvent } from "./status/types.js";
|
|
64
71
|
import { createTurnContext } from "./turn-context.js";
|
|
65
72
|
import { createTurnMiddleware } from "./turn-middleware.js";
|
|
66
73
|
import { createTurnPersistence } from "./turn-persistence.js";
|
|
74
|
+
import { createTurnPrewarm, type PrewarmOutcome, subscribePrewarmToCompaction } from "./turn-prewarm.js";
|
|
67
75
|
import {
|
|
68
76
|
createTurnQueues,
|
|
69
77
|
DEFAULT_STEERING_MODE,
|
|
@@ -78,13 +86,18 @@ import {
|
|
|
78
86
|
rewriteStallAbortMessage,
|
|
79
87
|
} from "./turn-recovery.js";
|
|
80
88
|
import { type AssistantDeltaEvent, createTurnRuntime, TurnAdmissionError } from "./turn-runtime.js";
|
|
81
|
-
import {
|
|
89
|
+
import {
|
|
90
|
+
type AgentRuntime,
|
|
91
|
+
type ChatLoopRunSnapshot,
|
|
92
|
+
createTurnState,
|
|
93
|
+
type TurnPreparationPhase,
|
|
94
|
+
} from "./turn-state.js";
|
|
82
95
|
import { isWorkerShareNote } from "./worker-share.js";
|
|
83
96
|
|
|
84
97
|
export type { QueuedChatMessage, QueuedMessageKind, QueuedMessagesSnapshot, SteeringMode } from "./turn-queues.js";
|
|
85
98
|
export type { RetryStatusEvent, RetryStatusPayload, RetryStatusPhase } from "./turn-recovery.js";
|
|
86
99
|
export type { AssistantDeltaEvent } from "./turn-runtime.js";
|
|
87
|
-
export type { ChatLoopRunSnapshot } from "./turn-state.js";
|
|
100
|
+
export type { ChatLoopRunSnapshot, TurnPreparationPhase } from "./turn-state.js";
|
|
88
101
|
|
|
89
102
|
export interface QueueUpdateEvent {
|
|
90
103
|
type: "queue_update";
|
|
@@ -182,6 +195,13 @@ export interface ChatSubmitOptions {
|
|
|
182
195
|
* for the two states in which it degrades to `next-slot` instead.
|
|
183
196
|
*/
|
|
184
197
|
steering?: SteeringMode;
|
|
198
|
+
/**
|
|
199
|
+
* Commits the presentation of a consumed prompt after preparation opens and
|
|
200
|
+
* before admission work can make the turn durable. Interactive uses this to
|
|
201
|
+
* guarantee that even a fast probe or prompt compile paints one pending
|
|
202
|
+
* frame instead of opening and closing entirely between renderer ticks.
|
|
203
|
+
*/
|
|
204
|
+
onPreparationVisible?: () => Promise<void>;
|
|
185
205
|
/**
|
|
186
206
|
* Fires once this submit owns the live turn (`isStreaming()` is true) and
|
|
187
207
|
* the next queued submit may use streaming queue routing. Stage 0 replay
|
|
@@ -232,6 +252,15 @@ export interface SideQuestionOptions {
|
|
|
232
252
|
onDelta?: (partialText: string) => void;
|
|
233
253
|
}
|
|
234
254
|
|
|
255
|
+
export interface HandoffRoundOptions extends SideQuestionOptions {
|
|
256
|
+
/**
|
|
257
|
+
* Run the second and last extraction round, quoting the parser's complaint
|
|
258
|
+
* and what the first round returned. Both rounds bill through the same
|
|
259
|
+
* out-of-turn usage store (issue #223).
|
|
260
|
+
*/
|
|
261
|
+
repair?: HandoffRepairInput;
|
|
262
|
+
}
|
|
263
|
+
|
|
235
264
|
/**
|
|
236
265
|
* How a `/btw` round ended. `refused` is a round that never started (a turn was
|
|
237
266
|
* in flight, or no orchestrator target is configured); `failed` is a round that
|
|
@@ -263,6 +292,15 @@ export interface ChatLoop {
|
|
|
263
292
|
getSessionId(): string | null;
|
|
264
293
|
lastRunSnapshot?(): ChatLoopRunSnapshot | null;
|
|
265
294
|
isStreaming(): boolean;
|
|
295
|
+
/**
|
|
296
|
+
* Where a consumed prompt currently is between the editor and the stream.
|
|
297
|
+
* The composer and the footer read it so the window in which the prompt has
|
|
298
|
+
* been taken but the turn has not been admitted is never rendered as idle
|
|
299
|
+
* (issue #251).
|
|
300
|
+
*/
|
|
301
|
+
turnPreparation(): { phase: TurnPreparationPhase; since: number };
|
|
302
|
+
/** Fires on every preparation transition, including back to `idle`. */
|
|
303
|
+
onTurnPreparation(handler: (phase: TurnPreparationPhase) => void): () => void;
|
|
266
304
|
contextUsage(): ContextUsageSnapshot;
|
|
267
305
|
/**
|
|
268
306
|
* Categorized context-window ledger for the `/context` overlay: where every
|
|
@@ -302,7 +340,7 @@ export interface ChatLoop {
|
|
|
302
340
|
* caller; this method only owns the provider call. Refused outright while a
|
|
303
341
|
* turn is in flight rather than queued.
|
|
304
342
|
*/
|
|
305
|
-
extractHandoff(goal: string, options?:
|
|
343
|
+
extractHandoff(goal: string, options?: HandoffRoundOptions): Promise<SideQuestionOutcome>;
|
|
306
344
|
/**
|
|
307
345
|
* Drop or replace the chat-loop's in-memory state after a session switch
|
|
308
346
|
* (/resume, /fork, /new). `leafTurnId` is the id the next user turn
|
|
@@ -310,6 +348,12 @@ export interface ChatLoop {
|
|
|
310
348
|
* from the selected session entries; omit it for a fresh session.
|
|
311
349
|
*/
|
|
312
350
|
resetForSession(leafTurnId: string | null, replayMessages?: ReadonlyArray<AgentMessage>): void;
|
|
351
|
+
/**
|
|
352
|
+
* Resolves once the queued or in-flight session pre-warm has settled, with
|
|
353
|
+
* the outcome, or null when none ran. Diagnostics and contracts only: no
|
|
354
|
+
* turn path waits on a pre-warm.
|
|
355
|
+
*/
|
|
356
|
+
whenPrewarmSettled(): Promise<PrewarmOutcome | null>;
|
|
313
357
|
/** Abort the live agent and release pi-ai session-scoped resources before shutdown. */
|
|
314
358
|
dispose(): void;
|
|
315
359
|
/**
|
|
@@ -453,6 +497,34 @@ export interface CreateChatLoopDeps {
|
|
|
453
497
|
* background registry, which holds exactly the attached calls.
|
|
454
498
|
*/
|
|
455
499
|
hasAttachedDispatch?: () => boolean;
|
|
500
|
+
/**
|
|
501
|
+
* True while any dispatched worker run is outstanding, attached or detached.
|
|
502
|
+
* The session pre-warm stands down in that state so it never competes for the
|
|
503
|
+
* endpoint a worker is already using. Defaults to `hasAttachedDispatch`, which
|
|
504
|
+
* is the narrower fact a bare composition has.
|
|
505
|
+
*/
|
|
506
|
+
hasActiveDispatch?: () => boolean;
|
|
507
|
+
/**
|
|
508
|
+
* True on a surface where a person is about to type the next turn. The
|
|
509
|
+
* orchestrator wires this false for headless `run`: the pre-warm buys latency
|
|
510
|
+
* that an unattended run never spends. Defaults to true.
|
|
511
|
+
*/
|
|
512
|
+
isLatencySurface?: () => boolean;
|
|
513
|
+
/** The session pre-warm round. Injectable for the same reason `runSideQuestion` is. */
|
|
514
|
+
runPrewarm?: typeof runPrewarmRound;
|
|
515
|
+
/**
|
|
516
|
+
* Whether a submit aborts the in-flight pre-warm's request or only lets go of
|
|
517
|
+
* it. Defaults to what the measured local backend does with a cancelled
|
|
518
|
+
* request; see `ABORT_ROUND_ON_SUBMIT` in `turn-prewarm.ts`.
|
|
519
|
+
*/
|
|
520
|
+
abortPrewarmOnSubmit?: boolean;
|
|
521
|
+
/**
|
|
522
|
+
* Claim one in-flight request on the pre-warm's endpoint for the duration of
|
|
523
|
+
* the round. Wired from the endpoint-capacity registry once #250 lands, so a
|
|
524
|
+
* pre-warm counts against the same per-endpoint bound the orchestrator's
|
|
525
|
+
* streaming turn does; see `registerEndpointSlot` in `turn-prewarm.ts`.
|
|
526
|
+
*/
|
|
527
|
+
registerPrewarmEndpointSlot?: (runtime: AgentRuntime) => (() => void) | null;
|
|
456
528
|
}
|
|
457
529
|
|
|
458
530
|
export function reloadProtectedArtifactsForSession(
|
|
@@ -478,6 +550,56 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
478
550
|
const state = createTurnState(deps.getSettings().orchestrator.thinkingLevel ?? "off");
|
|
479
551
|
const toolStartTimes = new Map<string, number>();
|
|
480
552
|
|
|
553
|
+
const preparationListeners = new Set<(phase: TurnPreparationPhase) => void>();
|
|
554
|
+
/**
|
|
555
|
+
* Move the consumed prompt to a new preparation phase and tell everything
|
|
556
|
+
* that renders it. Idempotent, so the two compaction sites and the two
|
|
557
|
+
* clearing sites (admission and settlement, because a refusal never reaches
|
|
558
|
+
* admission) can all set the phase they mean without ordering rules.
|
|
559
|
+
*/
|
|
560
|
+
const setTurnPreparation = (phase: TurnPreparationPhase): void => {
|
|
561
|
+
if (state.turnPreparation === phase) return;
|
|
562
|
+
// `since` is the age of the whole window, not of its current sub-state:
|
|
563
|
+
// what the operator is judging is how long ago they pressed Enter.
|
|
564
|
+
if (state.turnPreparation === "idle") state.turnPreparationSince = Date.now();
|
|
565
|
+
if (phase === "idle") state.turnPreparationSince = 0;
|
|
566
|
+
state.turnPreparation = phase;
|
|
567
|
+
for (const listener of preparationListeners) {
|
|
568
|
+
try {
|
|
569
|
+
listener(phase);
|
|
570
|
+
} catch {
|
|
571
|
+
// Preparation observers are presentation only and cannot fail a turn.
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Submits currently holding a consumed prompt. The FIFO gate lets a second
|
|
578
|
+
* submit open its window while the first is still settling, so the phase is
|
|
579
|
+
* refcounted rather than owned: the last one out turns the light off.
|
|
580
|
+
*/
|
|
581
|
+
let preparingSubmits = 0;
|
|
582
|
+
const enterPreparation = (): void => {
|
|
583
|
+
preparingSubmits += 1;
|
|
584
|
+
// Opening a window never narrows one that is already open: a second submit
|
|
585
|
+
// arriving while the first is compacting must not flip the composer from
|
|
586
|
+
// COMPACTING back to PREPARING while the compaction is still running.
|
|
587
|
+
if (state.turnPreparation === "idle") setTurnPreparation("preparing");
|
|
588
|
+
};
|
|
589
|
+
const leavePreparation = (): void => {
|
|
590
|
+
preparingSubmits = Math.max(0, preparingSubmits - 1);
|
|
591
|
+
if (preparingSubmits === 0) setTurnPreparation("idle");
|
|
592
|
+
};
|
|
593
|
+
/**
|
|
594
|
+
* Return to the plain preparing state after a compaction inside the window.
|
|
595
|
+
* Refcount-aware, because two submits share the window and the slower one's
|
|
596
|
+
* compaction can finish after the window has already closed; restoring
|
|
597
|
+
* `preparing` there would re-open it with nothing left to prepare.
|
|
598
|
+
*/
|
|
599
|
+
const endPreparationCompaction = (): void => {
|
|
600
|
+
setTurnPreparation(preparingSubmits > 0 ? "preparing" : "idle");
|
|
601
|
+
};
|
|
602
|
+
|
|
481
603
|
const emit = (event: ChatLoopEvent): void => {
|
|
482
604
|
for (const listener of listeners) {
|
|
483
605
|
listener(event);
|
|
@@ -589,7 +711,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
589
711
|
middlewareToolChoice,
|
|
590
712
|
consumePersistedEcho: (text) => queues.consumePersistedEcho(text),
|
|
591
713
|
removeQueuedMirrorEntry: (text) => queues.removeQueuedMirrorEntry(text),
|
|
592
|
-
promptCachePayloadForAssistant: (usage) => context.promptCachePayloadForAssistant(usage),
|
|
714
|
+
promptCachePayloadForAssistant: (usage, backend) => context.promptCachePayloadForAssistant(usage, backend),
|
|
593
715
|
promptSideTokens: () => context.promptSideTokens(),
|
|
594
716
|
observability: deps.observability,
|
|
595
717
|
});
|
|
@@ -702,6 +824,23 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
702
824
|
}
|
|
703
825
|
};
|
|
704
826
|
|
|
827
|
+
/**
|
|
828
|
+
* Run one out-of-turn round while holding a slot on the endpoint it streams
|
|
829
|
+
* to, exactly as the turn and the pre-warm do. A `/btw` or `/handoff` round
|
|
830
|
+
* is a full request against the same inference scheduler, so endpoint
|
|
831
|
+
* capacity (#250) has to count it for as long as it is out, and the
|
|
832
|
+
* background-memory tier has to read that endpoint as busy (#229).
|
|
833
|
+
*/
|
|
834
|
+
const withEndpointSlot = async <T>(runtime: AgentRuntime, round: () => Promise<T>): Promise<T> => {
|
|
835
|
+
const endpointKey = canonicalEndpointKey(runtime.runtimeResolution.target);
|
|
836
|
+
const release = endpointKey === null ? () => {} : registerForegroundStream(endpointKey);
|
|
837
|
+
try {
|
|
838
|
+
return await round();
|
|
839
|
+
} finally {
|
|
840
|
+
release();
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
|
|
705
844
|
const writeOutOfTurnUsageRow =
|
|
706
845
|
deps.recordOutOfTurnUsageRow ?? ((row: OutOfTurnUsageRow): void => appendOutOfTurnUsageRow(clioStateDir(), row));
|
|
707
846
|
|
|
@@ -764,6 +903,50 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
764
903
|
});
|
|
765
904
|
};
|
|
766
905
|
|
|
906
|
+
// A submit owns the turn state machine from the moment it starts running,
|
|
907
|
+
// which is well before `state.streaming` flips: the probe, auto-compaction,
|
|
908
|
+
// and the prompt compile all happen first. The pre-warm reads this flag, not
|
|
909
|
+
// `streaming`, so it can never be the request the operator's turn queues
|
|
910
|
+
// behind.
|
|
911
|
+
let turnActive = false;
|
|
912
|
+
|
|
913
|
+
const prewarm = createTurnPrewarm({
|
|
914
|
+
state,
|
|
915
|
+
getSettings: deps.getSettings,
|
|
916
|
+
providers: deps.providers,
|
|
917
|
+
context,
|
|
918
|
+
bus: deps.bus,
|
|
919
|
+
...(deps.session ? { session: deps.session } : {}),
|
|
920
|
+
isLatencySurface: () => deps.isLatencySurface?.() !== false,
|
|
921
|
+
isTurnActive: () => turnActive,
|
|
922
|
+
hasActiveDispatch: () => (deps.hasActiveDispatch ?? deps.hasAttachedDispatch)?.() === true,
|
|
923
|
+
prepareRuntime: async (signal) => {
|
|
924
|
+
// The same probe a submit awaits, so the pre-warm resolves the model the
|
|
925
|
+
// next turn will resolve rather than a stale catalog entry.
|
|
926
|
+
await turnRuntime.ensureLiveCapabilitiesForSelectedModel().catch(() => {});
|
|
927
|
+
return prepareOutOfTurnRound("a turn is in flight", signal);
|
|
928
|
+
},
|
|
929
|
+
applySessionTools: (runtime) => {
|
|
930
|
+
runtime.agent.state.tools = resolveSessionTools(
|
|
931
|
+
runtime,
|
|
932
|
+
deps.toolRegistry,
|
|
933
|
+
currentToolInvokeOptions,
|
|
934
|
+
turnRuntime.toolTelemetry,
|
|
935
|
+
);
|
|
936
|
+
},
|
|
937
|
+
recordUsage: (runtime, usage: Usage | null) => {
|
|
938
|
+
recordOutOfTurnUsage(runtime, sideQuestionUsage(usage), "prewarm");
|
|
939
|
+
},
|
|
940
|
+
...(deps.runPrewarm ? { runPrewarm: deps.runPrewarm } : {}),
|
|
941
|
+
...(deps.abortPrewarmOnSubmit === undefined ? {} : { abortRoundOnSubmit: deps.abortPrewarmOnSubmit }),
|
|
942
|
+
...(deps.registerPrewarmEndpointSlot ? { registerEndpointSlot: deps.registerPrewarmEndpointSlot } : {}),
|
|
943
|
+
});
|
|
944
|
+
const unsubscribePrewarmCompaction = subscribePrewarmToCompaction(deps.bus, prewarm);
|
|
945
|
+
// The prompt this process will send is known now: the session prompt compiles
|
|
946
|
+
// against the configured target, and a boot-time resume rebuilds the message
|
|
947
|
+
// array in the same tick, which the scheduler collapses onto one round.
|
|
948
|
+
prewarm.schedule("session-start");
|
|
949
|
+
|
|
767
950
|
const api: ChatLoop = {
|
|
768
951
|
steer: (text) => queues.steer(text),
|
|
769
952
|
queueFollowUp: (text) => queues.queueFollowUp(text),
|
|
@@ -813,8 +996,11 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
813
996
|
// lands as a user message before the next model turn.
|
|
814
997
|
// alt+enter (queueFollowUp) keeps the after-this-run intent.
|
|
815
998
|
if (isWorkerShareNote(trimmed)) state.turnSharedWorkerNote = true;
|
|
816
|
-
|
|
817
|
-
|
|
999
|
+
// The queue carries the submitted bytes, not the trimmed copy the
|
|
1000
|
+
// guard above reads: a steer is a model-facing turn and the
|
|
1001
|
+
// payload contract applies to it too (issue #244).
|
|
1002
|
+
if (mode === "end-of-turn") queues.queueFollowUp(text);
|
|
1003
|
+
else queues.steer(text);
|
|
818
1004
|
return;
|
|
819
1005
|
}
|
|
820
1006
|
emitNotice("[Clio Coder] response already in progress. Press Esc to cancel the active run.");
|
|
@@ -822,7 +1008,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
822
1008
|
}
|
|
823
1009
|
}
|
|
824
1010
|
|
|
825
|
-
state.lastRunSnapshot
|
|
1011
|
+
const previousRunSnapshot = state.lastRunSnapshot;
|
|
826
1012
|
let agentRuntime: AgentRuntime | null;
|
|
827
1013
|
try {
|
|
828
1014
|
await turnRuntime.ensureLiveCapabilitiesForSelectedModel();
|
|
@@ -858,6 +1044,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
858
1044
|
currentToolInvokeOptions,
|
|
859
1045
|
turnRuntime.toolTelemetry,
|
|
860
1046
|
);
|
|
1047
|
+
const toolSignature = toolSignatureFromState(agentRuntime.agent.state.tools);
|
|
861
1048
|
const askUserPolicy = createAskUserToolPolicy(agentRuntime.agent.state.tools);
|
|
862
1049
|
// turn_start: the prompt is accepted; registrations may inject
|
|
863
1050
|
// context for this request. Accumulated reminders (turn_end
|
|
@@ -887,9 +1074,12 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
887
1074
|
// 2. Pre-submit auto-compaction trigger
|
|
888
1075
|
const forceNow = process.env.CLIO_CODER_FORCE_COMPACT === "1";
|
|
889
1076
|
try {
|
|
1077
|
+
setTurnPreparation("compacting");
|
|
890
1078
|
await context.runAutoCompact(agentRuntime, forceNow, undefined, undefined, submittedText);
|
|
891
1079
|
} catch (err) {
|
|
892
1080
|
emitNotice(`[Clio Coder] auto-compaction failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1081
|
+
} finally {
|
|
1082
|
+
endPreparationCompaction();
|
|
893
1083
|
}
|
|
894
1084
|
|
|
895
1085
|
// 3. Ensure the session prompt (compiles only on explicit events)
|
|
@@ -907,20 +1097,33 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
907
1097
|
pendingUserInput: submittedText,
|
|
908
1098
|
images,
|
|
909
1099
|
promptHash: compiledPrompt?.systemPromptHash,
|
|
910
|
-
toolSignature
|
|
1100
|
+
toolSignature,
|
|
911
1101
|
});
|
|
912
1102
|
|
|
913
1103
|
const reservedOutput = resolveReservedOutputTokens(agentRuntime.runtimeResolution.capabilityDecisions.maxTokens);
|
|
914
1104
|
const effectiveWindow = agentRuntime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
|
|
915
1105
|
const pendingInputTokens = ceilChars(submittedText.length);
|
|
916
1106
|
let turnSnapshot = captureTurnSnapshot("pending");
|
|
917
|
-
|
|
1107
|
+
// The snapshot prices the prompt at chars/4. When the provider has
|
|
1108
|
+
// already attested a larger prompt for this conversation, that figure
|
|
1109
|
+
// is what the request will actually cost, so the overflow guard reads
|
|
1110
|
+
// the higher of the two (issue #227). Both already carry the pending
|
|
1111
|
+
// user text and the tool schema estimate.
|
|
1112
|
+
const budgetedPromptTokens = (snapshot: ContextSnapshot): number =>
|
|
1113
|
+
Math.max(
|
|
1114
|
+
snapshotInputTokens(snapshot) + pendingInputTokens,
|
|
1115
|
+
context.liveContextEstimate(agentRuntime, submittedText).tokens,
|
|
1116
|
+
);
|
|
1117
|
+
const totalEstimate = budgetedPromptTokens(turnSnapshot) + reservedOutput;
|
|
918
1118
|
|
|
919
1119
|
if (effectiveWindow > 0 && totalEstimate > effectiveWindow) {
|
|
920
1120
|
emitNotice(
|
|
921
|
-
`[Clio Coder] Estimated request size ${totalEstimate} tokens (input ${
|
|
1121
|
+
`[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...`,
|
|
922
1122
|
);
|
|
923
|
-
|
|
1123
|
+
setTurnPreparation("compacting");
|
|
1124
|
+
const compacted = await context
|
|
1125
|
+
.runAutoCompact(agentRuntime, true, undefined, undefined, submittedText)
|
|
1126
|
+
.finally(endPreparationCompaction);
|
|
924
1127
|
if (!compacted) {
|
|
925
1128
|
emitNotice(
|
|
926
1129
|
"[Clio Coder] Compaction could not reclaim enough space. Request blocked; trim the prompt, reduce active tools, or start a fresh session.",
|
|
@@ -929,7 +1132,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
929
1132
|
}
|
|
930
1133
|
context.refreshAgentMessagesFromSession(agentRuntime);
|
|
931
1134
|
turnSnapshot = captureTurnSnapshot("pending");
|
|
932
|
-
const postTotalEstimate =
|
|
1135
|
+
const postTotalEstimate = budgetedPromptTokens(turnSnapshot) + reservedOutput;
|
|
933
1136
|
if (postTotalEstimate > effectiveWindow) {
|
|
934
1137
|
emitNotice(
|
|
935
1138
|
`[Clio Coder] Request still exceeds the effective window after compaction (${postTotalEstimate} > ${effectiveWindow}). Request blocked.`,
|
|
@@ -956,6 +1159,16 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
956
1159
|
// cancelled run's leftovers, which is the order the ledger has.
|
|
957
1160
|
if (interrupted) emit({ type: "queued_user_turn", text, kind: "interrupt" });
|
|
958
1161
|
context.logPromptCompileIfPending();
|
|
1162
|
+
const previousThinkingLevel = previousRunSnapshot?.runtimeResolution?.effectiveThinkingLevel;
|
|
1163
|
+
if (
|
|
1164
|
+
previousThinkingLevel !== undefined &&
|
|
1165
|
+
previousThinkingLevel !== agentRuntime.runtimeResolution.effectiveThinkingLevel
|
|
1166
|
+
) {
|
|
1167
|
+
context.noteColdReason("thinking_change");
|
|
1168
|
+
}
|
|
1169
|
+
if (typeof previousRunSnapshot?.toolSignature === "string" && previousRunSnapshot.toolSignature !== toolSignature) {
|
|
1170
|
+
context.noteColdReason("tool_surface_change");
|
|
1171
|
+
}
|
|
959
1172
|
turnSnapshot = { ...turnSnapshot, turnId: userTurnId ?? "unknown" };
|
|
960
1173
|
context.setCurrentSnapshot(turnSnapshot);
|
|
961
1174
|
context.persistContextSnapshot(turnSnapshot);
|
|
@@ -970,7 +1183,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
970
1183
|
compiledPromptHash: promptHash,
|
|
971
1184
|
staticCompositionHash: promptHash,
|
|
972
1185
|
promptSignature: promptHash,
|
|
973
|
-
toolSignature
|
|
1186
|
+
toolSignature,
|
|
974
1187
|
runtimeResolution: runtimeTargetSnapshot(agentRuntime.runtimeResolution),
|
|
975
1188
|
sessionId: deps.session?.current()?.id ?? null,
|
|
976
1189
|
cwd: process.cwd(),
|
|
@@ -988,6 +1201,8 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
988
1201
|
|
|
989
1202
|
// 7. Run the prompt, then route the settled state through recovery.
|
|
990
1203
|
state.streaming = true;
|
|
1204
|
+
const endpointKey = canonicalEndpointKey(agentRuntime.runtimeResolution.target);
|
|
1205
|
+
const releaseForeground = endpointKey === null ? () => {} : registerForegroundStream(endpointKey);
|
|
991
1206
|
try {
|
|
992
1207
|
options.onAdmitted?.();
|
|
993
1208
|
} catch {
|
|
@@ -1051,6 +1266,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1051
1266
|
}
|
|
1052
1267
|
await recovery.runCompactAndRetry(agentRuntime, runtimePromptText, overflow, images);
|
|
1053
1268
|
} finally {
|
|
1269
|
+
releaseForeground();
|
|
1054
1270
|
if (askUserPolicy) {
|
|
1055
1271
|
try {
|
|
1056
1272
|
await finalizeAskUserInterviewForHost(
|
|
@@ -1151,9 +1367,21 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1151
1367
|
return state.streaming;
|
|
1152
1368
|
},
|
|
1153
1369
|
|
|
1370
|
+
turnPreparation() {
|
|
1371
|
+
return { phase: state.turnPreparation, since: state.turnPreparationSince };
|
|
1372
|
+
},
|
|
1373
|
+
|
|
1374
|
+
onTurnPreparation(handler) {
|
|
1375
|
+
preparationListeners.add(handler);
|
|
1376
|
+
return () => {
|
|
1377
|
+
preparationListeners.delete(handler);
|
|
1378
|
+
};
|
|
1379
|
+
},
|
|
1380
|
+
|
|
1154
1381
|
contextUsage: () => context.contextUsage(),
|
|
1155
1382
|
contextLedger: () => context.contextLedger(),
|
|
1156
1383
|
whenSettled: () => activeSubmit,
|
|
1384
|
+
whenPrewarmSettled: () => prewarm.settled(),
|
|
1157
1385
|
|
|
1158
1386
|
resetForSession(leafTurnId: string | null, replayMessages?: ReadonlyArray<AgentMessage>): void {
|
|
1159
1387
|
if (state.runtime) {
|
|
@@ -1166,6 +1394,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1166
1394
|
middleware.clearPendingReminders();
|
|
1167
1395
|
middlewareToolChoice.reset();
|
|
1168
1396
|
state.lastTurnId = leafTurnId;
|
|
1397
|
+
state.lastRunSnapshot = null;
|
|
1169
1398
|
context.resetForSession();
|
|
1170
1399
|
// The resumed ledger renders before the first new turn (issue #189),
|
|
1171
1400
|
// and the window it renders against should be the probed one rather
|
|
@@ -1180,11 +1409,17 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1180
1409
|
if (deps.protectedArtifacts) {
|
|
1181
1410
|
reloadProtectedArtifactsForSession(deps.protectedArtifacts, deps.readSessionEntries);
|
|
1182
1411
|
}
|
|
1412
|
+
// A resume replays a history the backend has never seen; a fresh session
|
|
1413
|
+
// replays nothing but still compiles a prompt and a tool surface. Both
|
|
1414
|
+
// leave a prefix the next turn will pay for unless it is sent now.
|
|
1415
|
+
prewarm.schedule(replayMessages && replayMessages.length > 0 ? "resume" : "session-start");
|
|
1183
1416
|
},
|
|
1184
1417
|
|
|
1185
1418
|
dispose(): void {
|
|
1186
1419
|
unsubscribeConfigReload?.();
|
|
1187
1420
|
unsubscribeSynthesisLock?.();
|
|
1421
|
+
unsubscribePrewarmCompaction();
|
|
1422
|
+
prewarm.dispose();
|
|
1188
1423
|
context.dispose();
|
|
1189
1424
|
if (state.runtime) {
|
|
1190
1425
|
state.runtime.agent.abort();
|
|
@@ -1211,16 +1446,18 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1211
1446
|
if (!prepared.ok) return { status: "refused", reason: prepared.reason };
|
|
1212
1447
|
let result: SideQuestionResult;
|
|
1213
1448
|
try {
|
|
1214
|
-
result = await
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1449
|
+
result = await withEndpointSlot(prepared.runtime, () =>
|
|
1450
|
+
sideQuestionRound({
|
|
1451
|
+
model: prepared.runtime.agent.state.model,
|
|
1452
|
+
// Read-only: runSideQuestion copies before appending its own
|
|
1453
|
+
// message, so the live agent's history is untouched.
|
|
1454
|
+
messages: prepared.runtime.agent.state.messages,
|
|
1455
|
+
question: text,
|
|
1456
|
+
...(prepared.apiKey !== undefined ? { apiKey: prepared.apiKey } : {}),
|
|
1457
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
1458
|
+
...(options.onDelta ? { onDelta: options.onDelta } : {}),
|
|
1459
|
+
}),
|
|
1460
|
+
);
|
|
1224
1461
|
} catch (err) {
|
|
1225
1462
|
return { status: "failed", reason: err instanceof Error ? err.message : String(err) };
|
|
1226
1463
|
}
|
|
@@ -1228,7 +1465,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1228
1465
|
return result.aborted ? { status: "aborted", text: result.text } : { status: "answered", text: result.text };
|
|
1229
1466
|
},
|
|
1230
1467
|
|
|
1231
|
-
async extractHandoff(goal: string, options:
|
|
1468
|
+
async extractHandoff(goal: string, options: HandoffRoundOptions = {}): Promise<SideQuestionOutcome> {
|
|
1232
1469
|
const text = goal.trim();
|
|
1233
1470
|
if (text.length === 0) {
|
|
1234
1471
|
return { status: "refused", reason: "a handoff needs a goal" };
|
|
@@ -1243,14 +1480,20 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1243
1480
|
if (!prepared.ok) return { status: "refused", reason: prepared.reason };
|
|
1244
1481
|
let result: SideQuestionResult;
|
|
1245
1482
|
try {
|
|
1246
|
-
result = await
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1483
|
+
result = await withEndpointSlot(prepared.runtime, () =>
|
|
1484
|
+
handoffRound({
|
|
1485
|
+
model: prepared.runtime.agent.state.model,
|
|
1486
|
+
// Read-only, exactly as the side-question round treats it.
|
|
1487
|
+
messages: prepared.runtime.agent.state.messages,
|
|
1488
|
+
goal: text,
|
|
1489
|
+
// The runtime id is what decides whether the schema can be bound
|
|
1490
|
+
// on the wire rather than only stated in the prompt.
|
|
1491
|
+
runtimeId: prepared.runtime.runtimeResolution.runtime.id,
|
|
1492
|
+
...(prepared.apiKey !== undefined ? { apiKey: prepared.apiKey } : {}),
|
|
1493
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
1494
|
+
...(options.repair ? { repair: options.repair } : {}),
|
|
1495
|
+
}),
|
|
1496
|
+
);
|
|
1254
1497
|
} catch (err) {
|
|
1255
1498
|
return { status: "failed", reason: err instanceof Error ? err.message : String(err) };
|
|
1256
1499
|
}
|
|
@@ -1295,7 +1538,13 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1295
1538
|
const submitInner = api.submit.bind(api);
|
|
1296
1539
|
const submitTracked: ChatLoop["submit"] = (text, options) => {
|
|
1297
1540
|
priorSubmit = activeSubmit;
|
|
1298
|
-
|
|
1541
|
+
// The flag brackets the whole submit, including the early returns an
|
|
1542
|
+
// admission failure takes, so a refused turn does not leave the pre-warm
|
|
1543
|
+
// believing a turn is still running.
|
|
1544
|
+
turnActive = true;
|
|
1545
|
+
const run = submitInner(text, options).finally(() => {
|
|
1546
|
+
turnActive = false;
|
|
1547
|
+
});
|
|
1299
1548
|
activeSubmit = run.catch(() => {});
|
|
1300
1549
|
return run;
|
|
1301
1550
|
};
|
|
@@ -1312,6 +1561,11 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1312
1561
|
// released at admission, not settlement, so steering stays immediate.
|
|
1313
1562
|
let admissionTail: Promise<void> | null = null;
|
|
1314
1563
|
api.submit = (text, options = {}) => {
|
|
1564
|
+
// The operator owns the slot from the keystroke, not from admission. The
|
|
1565
|
+
// prefix the pre-warm already pushed through stays in it either way, so
|
|
1566
|
+
// aborting here costs nothing and stops the real turn from queueing behind
|
|
1567
|
+
// a request nobody is waiting on.
|
|
1568
|
+
prewarm.cancel();
|
|
1315
1569
|
const previous = admissionTail;
|
|
1316
1570
|
let release: () => void = () => {};
|
|
1317
1571
|
const ticket = new Promise<void>((resolve) => {
|
|
@@ -1322,15 +1576,38 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1322
1576
|
release();
|
|
1323
1577
|
if (admissionTail === ticket) admissionTail = null;
|
|
1324
1578
|
};
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1579
|
+
// The window opens here, not at admission: the caller has already cleared
|
|
1580
|
+
// the editor and painted the prompt, and a submit that queues behind the
|
|
1581
|
+
// FIFO gate is still a prompt Clio is holding. It closes once, at
|
|
1582
|
+
// whichever of admission and settlement comes first, so a refused probe
|
|
1583
|
+
// and a blocked overflow preflight close it as reliably as a turn that
|
|
1584
|
+
// reaches the stream.
|
|
1585
|
+
enterPreparation();
|
|
1586
|
+
let leftPreparation = false;
|
|
1587
|
+
const leaveOnce = (): void => {
|
|
1588
|
+
if (leftPreparation) return;
|
|
1589
|
+
leftPreparation = true;
|
|
1590
|
+
leavePreparation();
|
|
1591
|
+
};
|
|
1592
|
+
const start = async (): Promise<void> => {
|
|
1593
|
+
const { onPreparationVisible, ...submitOptions } = options;
|
|
1594
|
+
try {
|
|
1595
|
+
await onPreparationVisible?.();
|
|
1596
|
+
} catch {
|
|
1597
|
+
// A presentation observer cannot refuse a turn after the editor has
|
|
1598
|
+
// consumed it. The ordinary render path can recover on its next tick.
|
|
1599
|
+
}
|
|
1600
|
+
return submitTracked(text, {
|
|
1601
|
+
...submitOptions,
|
|
1328
1602
|
onAdmitted: () => {
|
|
1329
1603
|
releaseTicket();
|
|
1604
|
+
leaveOnce();
|
|
1330
1605
|
options.onAdmitted?.();
|
|
1331
1606
|
},
|
|
1332
1607
|
}).finally(releaseTicket);
|
|
1333
|
-
|
|
1608
|
+
};
|
|
1609
|
+
const run = previous ? previous.then(start) : start();
|
|
1610
|
+
return run.finally(leaveOnce);
|
|
1334
1611
|
};
|
|
1335
1612
|
|
|
1336
1613
|
return api;
|