@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
|
@@ -14,6 +14,21 @@ import type { SessionMeta } from "./contract.js";
|
|
|
14
14
|
* stay diffable without recompiling anything.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Layout version of the compiled prompt these records describe.
|
|
19
|
+
*
|
|
20
|
+
* 1: the order shipped through 0.3.8.
|
|
21
|
+
* 2: stable-prefix-first ordering with one `# Memory` header, and
|
|
22
|
+
* `contextWindowSource` recorded alongside the window the prompt states
|
|
23
|
+
* (issue #249).
|
|
24
|
+
*
|
|
25
|
+
* A record without a `version` field predates the field and is read as 1, so a
|
|
26
|
+
* 0.3.8 manifest still parses. Bump this whenever the compiled text moves for a
|
|
27
|
+
* reason other than its inputs: a resumed session then has the version in hand
|
|
28
|
+
* to explain the one `promptRecompiled` entry its first compile writes.
|
|
29
|
+
*/
|
|
30
|
+
export const PROMPT_MANIFEST_VERSION = 2;
|
|
31
|
+
|
|
17
32
|
export interface PromptManifestSection {
|
|
18
33
|
id: string;
|
|
19
34
|
tokenEstimate: number;
|
|
@@ -27,6 +42,8 @@ export interface PromptManifestFragment {
|
|
|
27
42
|
}
|
|
28
43
|
|
|
29
44
|
export interface SessionPromptCompileRecord {
|
|
45
|
+
/** Prompt layout version; see `PROMPT_MANIFEST_VERSION`. Absent on pre-0.3.9 records. */
|
|
46
|
+
version?: number;
|
|
30
47
|
/** ISO timestamp of the compile. */
|
|
31
48
|
at: string;
|
|
32
49
|
/** Hash of the previously served prompt, null on the first compile. */
|
|
@@ -35,6 +52,14 @@ export interface SessionPromptCompileRecord {
|
|
|
35
52
|
tokenEstimate: number;
|
|
36
53
|
/** Thinking dial applied to the live agent when this prompt was compiled. */
|
|
37
54
|
thinkingLevel: string | null;
|
|
55
|
+
/**
|
|
56
|
+
* The window the prompt's `Context window: N` states, and the layer that
|
|
57
|
+
* answered it. `loaded` is what the backend has this model open at; anything
|
|
58
|
+
* else means the figure can still move once a loaded window is observed, and
|
|
59
|
+
* a later recompile is then explained rather than mysterious.
|
|
60
|
+
*/
|
|
61
|
+
contextWindow?: number | null;
|
|
62
|
+
contextWindowSource?: string | null;
|
|
38
63
|
projectPreload: ProjectPreloadClass | null;
|
|
39
64
|
sections: PromptManifestSection[];
|
|
40
65
|
fragments: PromptManifestFragment[];
|
|
@@ -102,9 +127,21 @@ function isProjectPreloadClass(value: unknown): value is ProjectPreloadClass {
|
|
|
102
127
|
);
|
|
103
128
|
}
|
|
104
129
|
|
|
130
|
+
/** Optional fields added after 0.3.8: absent is valid, present must be well-typed. */
|
|
131
|
+
function isOptional<T>(value: unknown, guard: (candidate: unknown) => candidate is T): boolean {
|
|
132
|
+
return value === undefined || guard(value);
|
|
133
|
+
}
|
|
134
|
+
|
|
105
135
|
function isSessionPromptCompileRecord(value: unknown): value is SessionPromptCompileRecord {
|
|
106
136
|
if (!isRecord(value)) return false;
|
|
107
137
|
return (
|
|
138
|
+
isOptional(value.version, isNonNegativeInteger) &&
|
|
139
|
+
isOptional(value.contextWindow, (candidate): candidate is number | null =>
|
|
140
|
+
candidate === null ? true : isNonNegativeInteger(candidate),
|
|
141
|
+
) &&
|
|
142
|
+
isOptional(value.contextWindowSource, (candidate): candidate is string | null =>
|
|
143
|
+
candidate === null ? true : typeof candidate === "string",
|
|
144
|
+
) &&
|
|
108
145
|
isCanonicalUtcTimestamp(value.at) &&
|
|
109
146
|
(value.previousHash === null || isSha256(value.previousHash)) &&
|
|
110
147
|
isSha256(value.systemPromptHash) &&
|
|
@@ -118,6 +155,11 @@ function isSessionPromptCompileRecord(value: unknown): value is SessionPromptCom
|
|
|
118
155
|
);
|
|
119
156
|
}
|
|
120
157
|
|
|
158
|
+
/** A record written before the field existed describes the 0.3.8 layout. */
|
|
159
|
+
export function promptManifestVersion(record: SessionPromptCompileRecord): number {
|
|
160
|
+
return record.version ?? 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
121
163
|
export function getPromptManifestFilePath(meta: SessionMeta, stateDir?: string): string {
|
|
122
164
|
const safeMeta = {
|
|
123
165
|
...meta,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
1
2
|
import {
|
|
2
3
|
DEFAULT_DELEGATION_CONNECT_TIMEOUT_MS,
|
|
3
4
|
DEFAULT_DELEGATION_TURN_TIMEOUT_MS,
|
|
4
5
|
type DelegationAgentConfig,
|
|
5
6
|
} from "../../core/defaults.js";
|
|
7
|
+
import type { HeartbeatStamp } from "../../domains/dispatch/heartbeat.js";
|
|
6
8
|
import type { AutonomyLevel } from "../../domains/safety/autonomy.js";
|
|
7
9
|
import type { SafetyContract } from "../../domains/safety/contract.js";
|
|
8
10
|
import type { AgentEvent } from "../types.js";
|
|
@@ -40,6 +42,10 @@ export interface AcpDelegationRunInput {
|
|
|
40
42
|
terminationWaitMs?: number;
|
|
41
43
|
/** Grace for session/cancel before abort escalates into hard termination. */
|
|
42
44
|
cancelGraceMs?: number;
|
|
45
|
+
/** Wall clock for the persisted heartbeat anchor. */
|
|
46
|
+
now?: () => number;
|
|
47
|
+
/** Monotonic clock for event-inactivity spans. */
|
|
48
|
+
monotonicNow?: () => number;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
export interface AcpDelegationRunHandle {
|
|
@@ -54,7 +60,7 @@ export interface AcpDelegationRunHandle {
|
|
|
54
60
|
* final observation bound expires.
|
|
55
61
|
*/
|
|
56
62
|
kill(): void;
|
|
57
|
-
heartbeatAt:
|
|
63
|
+
heartbeatAt: HeartbeatStamp;
|
|
58
64
|
toolCallLog(): ReturnType<AcpToolMediator["snapshot"]>["toolCallLog"];
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -176,7 +182,14 @@ export function startAcpDelegationRun(input: AcpDelegationRunInput): AcpDelegati
|
|
|
176
182
|
// peer into an unbounded request.
|
|
177
183
|
const connectTimeoutMs = input.agent.connectTimeoutMs ?? DEFAULT_DELEGATION_CONNECT_TIMEOUT_MS;
|
|
178
184
|
const turnTimeoutMs = input.agent.turnTimeoutMs ?? DEFAULT_DELEGATION_TURN_TIMEOUT_MS;
|
|
179
|
-
const
|
|
185
|
+
const now = input.now ?? Date.now;
|
|
186
|
+
const monotonicNow = input.monotonicNow ?? (() => performance.now());
|
|
187
|
+
const heartbeatWallAnchor = now();
|
|
188
|
+
const heartbeatMonotonicAnchor = monotonicNow();
|
|
189
|
+
const heartbeatAt: HeartbeatStamp = {
|
|
190
|
+
current: heartbeatWallAnchor,
|
|
191
|
+
monotonic: heartbeatMonotonicAnchor,
|
|
192
|
+
};
|
|
180
193
|
const queue = new AsyncEventQueue<AcpRunEvent>();
|
|
181
194
|
const usage = emptyUsage();
|
|
182
195
|
const mapper = new AcpEventMapper();
|
|
@@ -202,7 +215,9 @@ export function startAcpDelegationRun(input: AcpDelegationRunInput): AcpDelegati
|
|
|
202
215
|
cancelTimer = null;
|
|
203
216
|
};
|
|
204
217
|
const emit = (event: AcpRunEvent): void => {
|
|
205
|
-
|
|
218
|
+
const stamp = monotonicNow();
|
|
219
|
+
heartbeatAt.monotonic = stamp;
|
|
220
|
+
heartbeatAt.current = heartbeatWallAnchor + (stamp - heartbeatMonotonicAnchor);
|
|
206
221
|
queue.push(event);
|
|
207
222
|
};
|
|
208
223
|
const mediator = new AcpToolMediator({
|
package/src/engine/ai.ts
CHANGED
|
@@ -27,9 +27,11 @@ import {
|
|
|
27
27
|
type ToolCall,
|
|
28
28
|
type Usage,
|
|
29
29
|
} from "@earendil-works/pi-ai";
|
|
30
|
+
import type { BackendCompletionTimings } from "../core/cache-telemetry.js";
|
|
30
31
|
import {
|
|
31
32
|
completeEngineSimple,
|
|
32
33
|
engineStream,
|
|
34
|
+
engineStreamSimple,
|
|
33
35
|
registerEngineBuiltins,
|
|
34
36
|
registerEngineFauxProvider,
|
|
35
37
|
} from "./api-registry.js";
|
|
@@ -40,6 +42,14 @@ export { StringEnum };
|
|
|
40
42
|
|
|
41
43
|
export const stream = engineStream;
|
|
42
44
|
|
|
45
|
+
/**
|
|
46
|
+
* The dispatcher a turn runs on. `createEngineAgent` hands this to pi-agent-core
|
|
47
|
+
* as its `streamFn`, so a caller that needs a request byte-identical to the one
|
|
48
|
+
* the next turn would send (the session pre-warm) sends it through here rather
|
|
49
|
+
* than through `stream`, whose bare-call path has to guess a thinking level.
|
|
50
|
+
*/
|
|
51
|
+
export const streamSimple = engineStreamSimple;
|
|
52
|
+
|
|
43
53
|
export interface EngineTextCompletionInput {
|
|
44
54
|
model: EngineModel;
|
|
45
55
|
systemPrompt: string;
|
|
@@ -55,6 +65,21 @@ export interface EngineTextCompletionResult {
|
|
|
55
65
|
text: string;
|
|
56
66
|
inputTokens: number;
|
|
57
67
|
outputTokens: number;
|
|
68
|
+
/**
|
|
69
|
+
* The provider's full usage record for the call, so a caller that has to
|
|
70
|
+
* account for the spend does not have to re-derive it from two token counts.
|
|
71
|
+
*/
|
|
72
|
+
usage: {
|
|
73
|
+
input: number;
|
|
74
|
+
output: number;
|
|
75
|
+
cacheRead: number;
|
|
76
|
+
cacheWrite: number;
|
|
77
|
+
reasoning: number;
|
|
78
|
+
totalTokens: number;
|
|
79
|
+
costUsd: number;
|
|
80
|
+
};
|
|
81
|
+
/** Backend prefill and prediction facts when the serving runtime reported them. */
|
|
82
|
+
backend: BackendCompletionTimings | null;
|
|
58
83
|
}
|
|
59
84
|
|
|
60
85
|
/** Run one tool-free completion while keeping pi message types at the engine boundary. */
|
|
@@ -83,6 +108,16 @@ export async function completeEngineText(input: EngineTextCompletionInput): Prom
|
|
|
83
108
|
.join(""),
|
|
84
109
|
inputTokens: response.usage.input,
|
|
85
110
|
outputTokens: response.usage.output,
|
|
111
|
+
usage: {
|
|
112
|
+
input: response.usage.input,
|
|
113
|
+
output: response.usage.output,
|
|
114
|
+
cacheRead: response.usage.cacheRead,
|
|
115
|
+
cacheWrite: response.usage.cacheWrite,
|
|
116
|
+
reasoning: response.usage.reasoning ?? 0,
|
|
117
|
+
totalTokens: response.usage.totalTokens,
|
|
118
|
+
costUsd: response.usage.cost.total,
|
|
119
|
+
},
|
|
120
|
+
backend: response.backendTimings ?? null,
|
|
86
121
|
};
|
|
87
122
|
}
|
|
88
123
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks";
|
|
2
|
+
import { residencyTargetKey } from "../../core/residency-target-key.js";
|
|
2
3
|
import { sleep } from "../../core/timers.js";
|
|
3
4
|
import {
|
|
4
5
|
type ResidencyAdapter,
|
|
@@ -169,6 +170,26 @@ async function fetchRouterModels(
|
|
|
169
170
|
return parseRouterModels(await response.json());
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
class RouterModelPostError extends Error {
|
|
174
|
+
constructor(
|
|
175
|
+
readonly url: string,
|
|
176
|
+
readonly status: number,
|
|
177
|
+
readonly responseBody: string,
|
|
178
|
+
) {
|
|
179
|
+
super(`llama.cpp router rejected ${url}: HTTP ${status}; response: ${responseBody}`);
|
|
180
|
+
this.name = "RouterModelPostError";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function routerErrorBody(response: Response): Promise<string> {
|
|
185
|
+
try {
|
|
186
|
+
const body = (await response.text()).trim();
|
|
187
|
+
return body.length > 0 ? body : "<empty body>";
|
|
188
|
+
} catch (error) {
|
|
189
|
+
return `<unreadable body: ${error instanceof Error ? error.message : String(error)}>`;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
172
193
|
async function postRouterModel(fetchImpl: typeof fetch, url: string, modelId: string): Promise<void> {
|
|
173
194
|
const response = await fetchImpl(url, {
|
|
174
195
|
method: "POST",
|
|
@@ -177,7 +198,38 @@ async function postRouterModel(fetchImpl: typeof fetch, url: string, modelId: st
|
|
|
177
198
|
signal: AbortSignal.timeout(LOAD_TIMEOUT_MS),
|
|
178
199
|
});
|
|
179
200
|
if (response.ok) return;
|
|
180
|
-
throw new
|
|
201
|
+
throw new RouterModelPostError(url, response.status, await routerErrorBody(response));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function alreadyRunningResponse(body: string): boolean {
|
|
205
|
+
return body.toLowerCase().includes("already running");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* A router wake and Clio's explicit load can cross between the listing and the
|
|
210
|
+
* POST. In that race the router rejects the duplicate request even though the
|
|
211
|
+
* requested model now owns a resident slot. Re-read the authoritative listing
|
|
212
|
+
* before treating a non-2xx as a load failure; only a definitively absent,
|
|
213
|
+
* unloaded, or failed model preserves the rejection.
|
|
214
|
+
*/
|
|
215
|
+
async function postRouterLoad(input: LlamaCppResidencyInput, fetchImpl: typeof fetch, modelId: string): Promise<void> {
|
|
216
|
+
try {
|
|
217
|
+
await postRouterModel(fetchImpl, loadUrl(input.baseUrl), modelId);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (!(error instanceof RouterModelPostError)) throw error;
|
|
220
|
+
let model: LlamaCppRouterModel | undefined;
|
|
221
|
+
try {
|
|
222
|
+
model = (await fetchRouterModels(input, fetchImpl)).find((entry) => entry.id === modelId);
|
|
223
|
+
} catch {
|
|
224
|
+
// The router's explicit "already running" answer is sufficient even if
|
|
225
|
+
// the confirming snapshot itself races or is temporarily unavailable.
|
|
226
|
+
if (alreadyRunningResponse(error.responseBody)) return;
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
if (alreadyRunningResponse(error.responseBody)) return;
|
|
230
|
+
if (model !== undefined && model.state !== "unloaded" && model.state !== "failed") return;
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
181
233
|
}
|
|
182
234
|
|
|
183
235
|
async function waitForLoaded(input: LlamaCppResidencyInput, fetchImpl: typeof fetch, modelId: string): Promise<void> {
|
|
@@ -206,7 +258,7 @@ async function ensureModelLoaded(
|
|
|
206
258
|
// router answers `400 model is already running`, which fails the turn before
|
|
207
259
|
// it reaches the model.
|
|
208
260
|
if (knownState === "loaded" || knownState === "sleeping") return;
|
|
209
|
-
if (knownState !== "loading") await
|
|
261
|
+
if (knownState !== "loading") await postRouterLoad(input, fetchImpl, modelId);
|
|
210
262
|
await waitForLoaded(input, fetchImpl, modelId);
|
|
211
263
|
}
|
|
212
264
|
|
|
@@ -246,7 +298,7 @@ export async function ensureLlamaCppResidency(input: LlamaCppResidencyInput): Pr
|
|
|
246
298
|
const fetchImpl = input.fetchImpl ?? fetch;
|
|
247
299
|
let snapshot: LlamaCppRouterModel[] = [];
|
|
248
300
|
const adapter: ResidencyAdapter = {
|
|
249
|
-
targetKey:
|
|
301
|
+
targetKey: residencyTargetKey("llamacpp", input.baseUrl),
|
|
250
302
|
targetId: input.targetId,
|
|
251
303
|
runtimeId: input.runtimeId,
|
|
252
304
|
keepModelId: input.keepModelId,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
2
|
|
|
3
|
+
import { residencyTargetKey } from "../../core/residency-target-key.js";
|
|
3
4
|
import {
|
|
4
5
|
type LmStudioCatalog,
|
|
5
6
|
type LmStudioLoadedInstance,
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
} from "../../domains/providers/runtimes/common/lmstudio-http.js";
|
|
12
13
|
import type { TargetDescriptor } from "../../domains/providers/types/target-descriptor.js";
|
|
13
14
|
import { coResidentContextCeiling, fitLoadContextLength } from "./lmstudio-residency.js";
|
|
14
|
-
import { emitResidencyNotice, reconcileResidency, residencyManagedFor } from "./residency.js";
|
|
15
|
+
import { emitResidencyMutation, emitResidencyNotice, reconcileResidency, residencyManagedFor } from "./residency.js";
|
|
15
16
|
import { withResidencyLock } from "./residency-lock.js";
|
|
16
17
|
|
|
17
18
|
interface LmStudioModelMetadata {
|
|
@@ -205,7 +206,7 @@ export async function ensureLmStudioResidency(
|
|
|
205
206
|
entry.loadedInstances.map((instance) => ({ modelKey: entry.key, identifier: instance.id, instance })),
|
|
206
207
|
);
|
|
207
208
|
const managed = residencyManagedFor(info.lifecycle);
|
|
208
|
-
const targetKey =
|
|
209
|
+
const targetKey = residencyTargetKey("lmstudio", target.url ?? model.baseUrl);
|
|
209
210
|
const contextLength = target.lmstudio?.load?.contextLength;
|
|
210
211
|
const plan = await reconcileResidency({
|
|
211
212
|
targetKey,
|
|
@@ -248,17 +249,36 @@ export async function ensureLmStudioResidency(
|
|
|
248
249
|
});
|
|
249
250
|
}
|
|
250
251
|
}
|
|
252
|
+
const loadAndReport = async (): Promise<string> => {
|
|
253
|
+
const instanceId = await loadOwnedInstance(target, targetKey, body, options.apiKey, options.signal);
|
|
254
|
+
emitResidencyMutation({
|
|
255
|
+
targetKey,
|
|
256
|
+
targetId: info.targetId,
|
|
257
|
+
runtimeId: "lmstudio",
|
|
258
|
+
model: modelKey,
|
|
259
|
+
operation: "load",
|
|
260
|
+
});
|
|
261
|
+
return instanceId ?? model.id;
|
|
262
|
+
};
|
|
251
263
|
try {
|
|
252
|
-
return
|
|
264
|
+
return await loadAndReport();
|
|
253
265
|
} catch (error) {
|
|
254
266
|
if (plan.decision === "observe" || plan.fallbackEvict.length === 0) throw error;
|
|
255
267
|
return withResidencyLock(targetKey, async () => {
|
|
256
268
|
for (const candidate of plan.fallbackEvict) {
|
|
257
269
|
for (const entry of instances.filter((resident) => resident.modelKey === candidate.modelId)) {
|
|
258
|
-
await unloadOwnedInstance(target, targetKey, entry.identifier, options.apiKey, options.signal)
|
|
270
|
+
if (await unloadOwnedInstance(target, targetKey, entry.identifier, options.apiKey, options.signal)) {
|
|
271
|
+
emitResidencyMutation({
|
|
272
|
+
targetKey,
|
|
273
|
+
targetId: info.targetId,
|
|
274
|
+
runtimeId: "lmstudio",
|
|
275
|
+
model: candidate.modelId,
|
|
276
|
+
operation: "evict",
|
|
277
|
+
});
|
|
278
|
+
}
|
|
259
279
|
}
|
|
260
280
|
}
|
|
261
|
-
return (
|
|
281
|
+
return loadAndReport();
|
|
262
282
|
});
|
|
263
283
|
}
|
|
264
284
|
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
type Tool as OllamaTool,
|
|
25
25
|
type ToolCall as OllamaToolCall,
|
|
26
26
|
} from "ollama";
|
|
27
|
+
import { residencyTargetKey } from "../../core/residency-target-key.js";
|
|
27
28
|
import {
|
|
28
29
|
type AppliedThinking,
|
|
29
30
|
resolveModelRuntimeCapabilitiesForModel,
|
|
@@ -75,7 +76,7 @@ async function reconcileOllamaResidency(model: Model<"ollama-native">, headers:
|
|
|
75
76
|
if (!baseUrl) return;
|
|
76
77
|
const metadata = (model as Model<"ollama-native"> & ClioRuntimeMetadata).clio;
|
|
77
78
|
const adapter: ResidencyAdapter = {
|
|
78
|
-
targetKey:
|
|
79
|
+
targetKey: residencyTargetKey("ollama-native", baseUrl),
|
|
79
80
|
targetId: ollamaTargetId(model),
|
|
80
81
|
runtimeId: "ollama-native",
|
|
81
82
|
keepModelId: model.id,
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type Usage,
|
|
17
17
|
} from "@earendil-works/pi-ai";
|
|
18
18
|
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
19
|
+
import type { BackendCompletionTimings, BackendTimingsSource } from "../../core/cache-telemetry.js";
|
|
19
20
|
import type { ResponseModelIdObservation } from "../../core/response-model-id.js";
|
|
20
21
|
import {
|
|
21
22
|
type AppliedThinking,
|
|
@@ -41,6 +42,8 @@ declare module "@earendil-works/pi-ai" {
|
|
|
41
42
|
interface AssistantMessage {
|
|
42
43
|
/** Direct observation of model-id presence in an OpenAI-compatible response. */
|
|
43
44
|
responseModelIdObservation?: ResponseModelIdObservation;
|
|
45
|
+
/** Backend-reported prefill and prediction timings when available. */
|
|
46
|
+
backendTimings?: BackendCompletionTimings;
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -95,13 +98,57 @@ function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
|
95
98
|
interface ResponseModelIdCapture {
|
|
96
99
|
reportedModelId: string | null;
|
|
97
100
|
observed: boolean;
|
|
98
|
-
|
|
101
|
+
modelIdDone: boolean;
|
|
102
|
+
backendTimings: BackendCompletionTimings | null;
|
|
103
|
+
backendTimingsSource: BackendTimingsSource | null;
|
|
99
104
|
buffer: string;
|
|
100
105
|
decoder: TextDecoder | null;
|
|
101
106
|
}
|
|
102
107
|
|
|
103
|
-
function
|
|
104
|
-
|
|
108
|
+
function nonnegativeFiniteNumber(value: unknown): number | null {
|
|
109
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The operator's llama.cpp router reported build b226-2115b73d8. Its nonstream
|
|
114
|
+
* response placed a `{ prompt_n, prompt_ms, predicted_n, predicted_ms, cache_n }`
|
|
115
|
+
* timing object at the top level. Its stream placed the timing object on the
|
|
116
|
+
* final SSE JSON event without `timings_per_token`. Setting
|
|
117
|
+
* `timings_per_token: true` repeated cumulative timing objects for individual
|
|
118
|
+
* tokens. LM Studio's OpenAI-compatible stream and nonstream responses omitted
|
|
119
|
+
* timings both with and without that flag. Its native runtime reported
|
|
120
|
+
* llama.cpp-win-x86_64-nvidia-cuda12-avx2 2.29.0.
|
|
121
|
+
*/
|
|
122
|
+
function backendCompletionTimings(value: unknown, source: BackendTimingsSource): BackendCompletionTimings | null {
|
|
123
|
+
if (!isPlainRecord(value)) return null;
|
|
124
|
+
const promptN = nonnegativeFiniteNumber(value.prompt_n);
|
|
125
|
+
const predictedN = nonnegativeFiniteNumber(value.predicted_n);
|
|
126
|
+
const promptMs = nonnegativeFiniteNumber(value.prompt_ms);
|
|
127
|
+
const predictedMs = nonnegativeFiniteNumber(value.predicted_ms);
|
|
128
|
+
if (promptN === null || predictedN === null || promptMs === null || predictedMs === null) return null;
|
|
129
|
+
const cacheN = nonnegativeFiniteNumber(value.cache_n);
|
|
130
|
+
return {
|
|
131
|
+
promptTokens: promptN + (cacheN ?? 0),
|
|
132
|
+
cachedTokens: cacheN,
|
|
133
|
+
predictedTokens: predictedN,
|
|
134
|
+
promptMs,
|
|
135
|
+
predictedMs,
|
|
136
|
+
source,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function backendTimingsSourceForModel(model: Model<Api>): BackendTimingsSource | null {
|
|
141
|
+
const metadata = (model as Model<Api> & ClioRuntimeMetadata).clio;
|
|
142
|
+
if (model.provider === "llamacpp" && metadata?.runtimeId === "llamacpp") return "llamacpp-timings";
|
|
143
|
+
if (model.provider === "lmstudio" && metadata?.runtimeId === "lmstudio") return "lmstudio-timings";
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function captureCanStopEarly(capture: ResponseModelIdCapture): boolean {
|
|
148
|
+
return capture.modelIdDone && capture.backendTimingsSource === null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function observeResponseMetadataLine(line: string, capture: ResponseModelIdCapture): void {
|
|
105
152
|
const normalized = line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
106
153
|
if (!normalized.startsWith("data:")) return;
|
|
107
154
|
const data = normalized.slice("data:".length).trimStart();
|
|
@@ -109,10 +156,16 @@ function observeResponseModelIdLine(line: string, capture: ResponseModelIdCaptur
|
|
|
109
156
|
try {
|
|
110
157
|
const payload = JSON.parse(data) as unknown;
|
|
111
158
|
if (!isPlainRecord(payload)) return;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
159
|
+
if (!capture.modelIdDone) {
|
|
160
|
+
const model = payload.model;
|
|
161
|
+
if (typeof model === "string" && model.trim().length > 0) {
|
|
162
|
+
capture.reportedModelId = model.trim();
|
|
163
|
+
capture.modelIdDone = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (capture.backendTimingsSource !== null) {
|
|
167
|
+
const timings = backendCompletionTimings(payload.timings, capture.backendTimingsSource);
|
|
168
|
+
if (timings !== null) capture.backendTimings = timings;
|
|
116
169
|
}
|
|
117
170
|
} catch {
|
|
118
171
|
// A partial or vendor-specific event is pi-ai's parsing concern. This
|
|
@@ -125,14 +178,14 @@ function observeResponseModelIdBytes(
|
|
|
125
178
|
capture: ResponseModelIdCapture,
|
|
126
179
|
flush = false,
|
|
127
180
|
): void {
|
|
128
|
-
if (
|
|
181
|
+
if (!capture.decoder) return;
|
|
129
182
|
capture.buffer += chunk ? capture.decoder.decode(chunk, { stream: !flush }) : capture.decoder.decode();
|
|
130
183
|
let newline = capture.buffer.indexOf("\n");
|
|
131
184
|
while (newline >= 0) {
|
|
132
185
|
const line = capture.buffer.slice(0, newline);
|
|
133
186
|
capture.buffer = capture.buffer.slice(newline + 1);
|
|
134
|
-
|
|
135
|
-
if (capture
|
|
187
|
+
observeResponseMetadataLine(line, capture);
|
|
188
|
+
if (captureCanStopEarly(capture)) {
|
|
136
189
|
capture.buffer = "";
|
|
137
190
|
capture.decoder = null;
|
|
138
191
|
return;
|
|
@@ -140,10 +193,10 @@ function observeResponseModelIdBytes(
|
|
|
140
193
|
newline = capture.buffer.indexOf("\n");
|
|
141
194
|
}
|
|
142
195
|
if (flush && capture.buffer.length > 0) {
|
|
143
|
-
|
|
196
|
+
observeResponseMetadataLine(capture.buffer, capture);
|
|
144
197
|
capture.buffer = "";
|
|
145
198
|
}
|
|
146
|
-
if (capture
|
|
199
|
+
if (captureCanStopEarly(capture) || flush) capture.decoder = null;
|
|
147
200
|
}
|
|
148
201
|
|
|
149
202
|
function captureResponseModelId(response: Response, capture: ResponseModelIdCapture): Response {
|
|
@@ -154,12 +207,12 @@ function captureResponseModelId(response: Response, capture: ResponseModelIdCapt
|
|
|
154
207
|
new TransformStream<Uint8Array, Uint8Array>({
|
|
155
208
|
transform(chunk, controller) {
|
|
156
209
|
capture.observed = true;
|
|
157
|
-
|
|
210
|
+
observeResponseModelIdBytes(chunk, capture);
|
|
158
211
|
controller.enqueue(chunk);
|
|
159
212
|
},
|
|
160
213
|
flush() {
|
|
161
214
|
capture.observed = true;
|
|
162
|
-
|
|
215
|
+
observeResponseModelIdBytes(undefined, capture, true);
|
|
163
216
|
},
|
|
164
217
|
}),
|
|
165
218
|
);
|
|
@@ -171,13 +224,16 @@ function captureResponseModelId(response: Response, capture: ResponseModelIdCapt
|
|
|
171
224
|
}
|
|
172
225
|
|
|
173
226
|
function withResponseModelIdCapture<TOptions extends StreamOptions>(
|
|
227
|
+
model: Model<Api>,
|
|
174
228
|
options: TOptions,
|
|
175
229
|
sourceFactory: (capturedOptions: TOptions) => AssistantMessageEventStream,
|
|
176
230
|
): AssistantMessageEventStream {
|
|
177
231
|
const capture: ResponseModelIdCapture = {
|
|
178
232
|
reportedModelId: null,
|
|
179
233
|
observed: false,
|
|
180
|
-
|
|
234
|
+
modelIdDone: false,
|
|
235
|
+
backendTimings: null,
|
|
236
|
+
backendTimingsSource: backendTimingsSourceForModel(model),
|
|
181
237
|
buffer: "",
|
|
182
238
|
decoder: new TextDecoder(),
|
|
183
239
|
};
|
|
@@ -197,8 +253,13 @@ function withResponseModelIdCapture<TOptions extends StreamOptions>(
|
|
|
197
253
|
? { state: "not-reported" }
|
|
198
254
|
: { state: "reported", reportedModelId: capture.reportedModelId }
|
|
199
255
|
: { state: "not-observed" };
|
|
200
|
-
if (event.type === "done")
|
|
201
|
-
|
|
256
|
+
if (event.type === "done") {
|
|
257
|
+
event.message.responseModelIdObservation = observation;
|
|
258
|
+
if (capture.backendTimings !== null) event.message.backendTimings = capture.backendTimings;
|
|
259
|
+
} else if (event.type === "error") {
|
|
260
|
+
event.error.responseModelIdObservation = observation;
|
|
261
|
+
if (capture.backendTimings !== null) event.error.backendTimings = capture.backendTimings;
|
|
262
|
+
}
|
|
202
263
|
annotated.push(event as AssistantMessageEvent);
|
|
203
264
|
}
|
|
204
265
|
annotated.end();
|
|
@@ -863,6 +924,7 @@ export const openAICompletionsApiProvider: EngineApiProvider<"openai-completions
|
|
|
863
924
|
stripNeverReasoningFromStream(
|
|
864
925
|
filterGemmaChannelStream(
|
|
865
926
|
withResponseModelIdCapture(
|
|
927
|
+
model,
|
|
866
928
|
withRemainingContextBudget(model, effectiveContext, withSamplers),
|
|
867
929
|
(capturedOptions) =>
|
|
868
930
|
withLocalResidency(
|
|
@@ -895,6 +957,7 @@ export const openAICompletionsApiProvider: EngineApiProvider<"openai-completions
|
|
|
895
957
|
stripNeverReasoningFromStream(
|
|
896
958
|
filterGemmaChannelStream(
|
|
897
959
|
withResponseModelIdCapture(
|
|
960
|
+
model,
|
|
898
961
|
withRemainingContextBudget(model, effectiveContext, withSamplers),
|
|
899
962
|
(capturedOptions) =>
|
|
900
963
|
withLocalResidency(
|
|
@@ -12,12 +12,14 @@
|
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { withStateFileLock } from "../../core/state-file-lock.js";
|
|
14
14
|
import { clioStatePath } from "../../core/xdg.js";
|
|
15
|
+
import { canonicalEndpointKey } from "../../domains/providers/endpoint-capacity.js";
|
|
15
16
|
|
|
16
17
|
/** Total time one process waits for another's mutation before proceeding unlocked. */
|
|
17
18
|
const LOCK_WAIT_MS = 120_000;
|
|
18
19
|
|
|
19
20
|
function lockTargetFor(targetKey: string): string {
|
|
20
|
-
|
|
21
|
+
const key = canonicalEndpointKey(targetKey) ?? targetKey;
|
|
22
|
+
return join(clioStatePath(), "residency-locks", key.replace(/[^a-zA-Z0-9._-]+/g, "_"));
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/** Run `fn` while holding the per-target residency lock. */
|
|
@@ -35,7 +35,12 @@
|
|
|
35
35
|
* unload/load against the same server.
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
BusChannels,
|
|
40
|
+
type ResidencyMutationPayload,
|
|
41
|
+
type RuntimeNoticeKind,
|
|
42
|
+
type RuntimeNoticePayload,
|
|
43
|
+
} from "../../core/bus-events.js";
|
|
39
44
|
import type { ProtectedModelRef, ResidencyRole } from "../../core/residency-protection.js";
|
|
40
45
|
import { getSharedBus } from "../../core/shared-bus.js";
|
|
41
46
|
import { withResidencyLock } from "./residency-lock.js";
|
|
@@ -73,6 +78,11 @@ export function emitResidencyNotice(notice: ResidencyNotice): void {
|
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
80
|
|
|
81
|
+
/** Publish one successful residency mutation to in-process cache telemetry. */
|
|
82
|
+
export function emitResidencyMutation(mutation: Omit<ResidencyMutationPayload, "at">): void {
|
|
83
|
+
getSharedBus().emit(BusChannels.ResidencyMutation, { ...mutation, at: Date.now() });
|
|
84
|
+
}
|
|
85
|
+
|
|
76
86
|
// --- configured-model protection ---------------------------------------------
|
|
77
87
|
|
|
78
88
|
// Wire model ids the operator's own configuration references, with the role
|
|
@@ -543,6 +553,13 @@ async function restoreEvictedModels(
|
|
|
543
553
|
try {
|
|
544
554
|
await adapter.reloadEvicted(entry.modelId);
|
|
545
555
|
restored.push(entry.modelId);
|
|
556
|
+
emitResidencyMutation({
|
|
557
|
+
targetKey: adapter.targetKey,
|
|
558
|
+
targetId: adapter.targetId,
|
|
559
|
+
runtimeId: adapter.runtimeId,
|
|
560
|
+
model: entry.modelId,
|
|
561
|
+
operation: "load",
|
|
562
|
+
});
|
|
546
563
|
} catch {
|
|
547
564
|
failed.push(entry.modelId);
|
|
548
565
|
}
|
|
@@ -705,6 +722,13 @@ export async function reconcileResidency(adapter: ResidencyAdapter): Promise<Rec
|
|
|
705
722
|
await adapter.unload(entry.modelId);
|
|
706
723
|
forgetClioLoaded(adapter.targetKey, entry.modelId);
|
|
707
724
|
evicted.push(entry);
|
|
725
|
+
emitResidencyMutation({
|
|
726
|
+
targetKey: adapter.targetKey,
|
|
727
|
+
targetId: adapter.targetId,
|
|
728
|
+
runtimeId: adapter.runtimeId,
|
|
729
|
+
model: entry.modelId,
|
|
730
|
+
operation: "evict",
|
|
731
|
+
});
|
|
708
732
|
} catch {
|
|
709
733
|
// Best-effort: a failed unload self-heals on the next reconcile.
|
|
710
734
|
}
|
|
@@ -712,6 +736,15 @@ export async function reconcileResidency(adapter: ResidencyAdapter): Promise<Rec
|
|
|
712
736
|
if (!adapter.load) return;
|
|
713
737
|
try {
|
|
714
738
|
await adapter.load(adapter.keepModelId);
|
|
739
|
+
if (!plan.keepResident) {
|
|
740
|
+
emitResidencyMutation({
|
|
741
|
+
targetKey: adapter.targetKey,
|
|
742
|
+
targetId: adapter.targetId,
|
|
743
|
+
runtimeId: adapter.runtimeId,
|
|
744
|
+
model: adapter.keepModelId,
|
|
745
|
+
operation: "load",
|
|
746
|
+
});
|
|
747
|
+
}
|
|
715
748
|
} catch (error) {
|
|
716
749
|
// The eviction bought nothing: the slot it freed is still empty and
|
|
717
750
|
// the target is now serving less than before the reconcile. Put back
|