@iowarp/clio-coder 0.3.1 → 0.3.3
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 +233 -373
- package/CONTRIBUTING.md +23 -23
- package/README.md +284 -613
- package/dist/{acp-FPR54DGL.js → acp-P2AQILE2.js} +43 -53
- package/dist/{agents-OGPIHPJH.js → agents-72W3BI7I.js} +43 -26
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-IC3K6NIZ.js → auth-5TWEIYDN.js} +20 -12
- package/dist/chunk-2DJ2KNFG.js +2095 -0
- package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
- package/dist/chunk-2SFS6XQE.js +122 -0
- package/dist/{chunk-PV4JUBVJ.js → chunk-2TLUCQVG.js} +40 -21
- package/dist/chunk-2VTFPG5O.js +48 -0
- package/dist/chunk-4BJ5BYCE.js +61 -0
- package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
- package/dist/chunk-4VP4KH3K.js +962 -0
- package/dist/chunk-4XUGQOHA.js +797 -0
- package/dist/chunk-4ZG3XFUR.js +77 -0
- package/dist/chunk-5B2AEOW5.js +5407 -0
- package/dist/{chunk-K2ITRMHZ.js → chunk-5TSRNF4G.js} +6 -138
- package/dist/{chunk-4QKXUHSR.js → chunk-5UFT4SUX.js} +70 -20
- package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
- package/dist/chunk-65DEGPJ6.js +52 -0
- package/dist/chunk-6EJMN2Y3.js +17 -0
- package/dist/chunk-6N5PTWMY.js +136 -0
- package/dist/chunk-6SGHMWE3.js +277 -0
- package/dist/chunk-6XLNIQDB.js +27 -0
- package/dist/chunk-7CR24IG7.js +242 -0
- package/dist/chunk-7MNJORFF.js +22 -0
- package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
- package/dist/chunk-AGYYIBLL.js +1069 -0
- package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
- package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
- package/dist/chunk-CBCAPZAA.js +229 -0
- package/dist/chunk-CMZWFGD2.js +352 -0
- package/dist/chunk-COU2UHX6.js +400 -0
- package/dist/chunk-DSELYM6W.js +1077 -0
- package/dist/chunk-DUYJ5IO6.js +644 -0
- package/dist/chunk-ECH6PKUQ.js +39 -0
- package/dist/chunk-ED4KHGC3.js +143 -0
- package/dist/chunk-EKMEHE4H.js +340 -0
- package/dist/chunk-FCSXB6T2.js +338 -0
- package/dist/chunk-FJ3H4MN5.js +48 -0
- package/dist/{chunk-RPTR2H26.js → chunk-FNTMWMX5.js} +21 -15
- package/dist/chunk-FQ4SKYE4.js +29 -0
- package/dist/chunk-G4BMMOKF.js +182 -0
- package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
- package/dist/chunk-HC4CLZ2Y.js +68 -0
- package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
- package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
- package/dist/chunk-I4HZDVNP.js +73 -0
- package/dist/chunk-IKCO5N3L.js +162 -0
- package/dist/chunk-IR4CFBFN.js +56 -0
- package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
- package/dist/chunk-J7CWMCQD.js +255 -0
- package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
- package/dist/chunk-KZWTDYJF.js +217 -0
- package/dist/chunk-LBMZMYH2.js +285 -0
- package/dist/{chunk-G34LV2PF.js → chunk-LM5TQCJZ.js} +84 -170
- package/dist/chunk-LW6DSM3M.js +5135 -0
- package/dist/chunk-LWLEKMDQ.js +3482 -0
- package/dist/{chunk-H6F6BYOH.js → chunk-LZSJBIVT.js} +7003 -7434
- package/dist/{chunk-HQQID6OA.js → chunk-M6SHUN7Q.js} +5 -5
- package/dist/{chunk-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
- package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
- package/dist/chunk-OAO4GE4M.js +619 -0
- package/dist/{chunk-Q3RUPKEJ.js → chunk-OC7FIQPC.js} +58 -189
- package/dist/chunk-OKGUZO2U.js +34 -0
- package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
- package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
- package/dist/chunk-OQE5J4C6.js +73 -0
- package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
- package/dist/{chunk-MAR7Y6HW.js → chunk-PAJK6MAQ.js} +23 -16
- package/dist/{chunk-M5T5VO65.js → chunk-PIWWS5BL.js} +837 -635
- package/dist/chunk-POHLU5DW.js +1186 -0
- package/dist/chunk-QKMUKYO7.js +4961 -0
- package/dist/chunk-SRDMMSEP.js +16405 -0
- package/dist/chunk-SST6Z5JA.js +80 -0
- package/dist/chunk-STBPMHSX.js +2456 -0
- package/dist/chunk-T6YILFSB.js +80 -0
- package/dist/chunk-TZK7PACC.js +174 -0
- package/dist/chunk-TZTZS7QK.js +227 -0
- package/dist/{chunk-ASND7OZK.js → chunk-UFIIWP2H.js} +13 -13
- package/dist/chunk-UOV2BYIW.js +107 -0
- package/dist/{chunk-PFEFKVGL.js → chunk-V6RTAOC2.js} +13 -11
- package/dist/chunk-VAKQQHWR.js +434 -0
- package/dist/chunk-VG7TBQIY.js +128 -0
- package/dist/chunk-VJWL6YS5.js +244 -0
- package/dist/{chunk-EYOKLTMF.js → chunk-VPAYEGVX.js} +17 -3
- package/dist/chunk-WEH5XRJQ.js +32 -0
- package/dist/chunk-X4RCMKVQ.js +641 -0
- package/dist/{chunk-TEKV33Q5.js → chunk-X6IAEBZR.js} +65 -33
- package/dist/chunk-XBXAASKX.js +18 -0
- package/dist/chunk-XN3L4EYL.js +46 -0
- package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
- package/dist/chunk-YHZX5GEU.js +193 -0
- package/dist/chunk-YXLYO42X.js +91 -0
- package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
- package/dist/chunk-ZI647VB5.js +37 -0
- package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
- package/dist/chunk-ZWMF7253.js +1882 -0
- package/dist/cli/index.js +62 -54
- package/dist/clio-JOU4FXVA.js +25 -0
- package/dist/code-nav-7AX6FYE6.js +600 -0
- package/dist/codewiki/build-worker.js +66 -0
- package/dist/compile-cache-CVJMMODC.js +18 -0
- package/dist/{components-DMAOEKFB.js → components-KELWS457.js} +11 -6
- package/dist/{config-IRUQ7SE4.js → config-XCDVKR23.js} +92 -55
- package/dist/configure-4GAP54ZW.js +42 -0
- package/dist/{context-5RADCKTR.js → context-4UOGGLQ5.js} +71 -35
- package/dist/context-5VKGUVJJ.js +866 -0
- package/dist/{context-3KWFLHJG.js → context-77FM5DV5.js} +15 -13
- package/dist/{context-clear-7TSNPAAI.js → context-clear-XXJRLCJJ.js} +54 -28
- package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
- package/dist/dispatch-runner-QPRDDBDX.js +1997 -0
- package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
- package/dist/{doctor-UC5NAJYQ.js → doctor-HR46URBJ.js} +27 -17
- package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
- package/dist/{evidence-YEGUW4L3.js → evidence-6HG2PY2B.js} +46 -26
- package/dist/{evolve-TXARCTPG.js → evolve-K7YU3NCY.js} +45 -25
- package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
- package/dist/{fleet-6G3DHNYE.js → fleet-VY3HHKN6.js} +163 -54
- package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
- package/dist/{init-KZ5QTF6M.js → init-JYGXI3FK.js} +69 -32
- package/dist/{memory-73ESV5YC.js → memory-WFZMGYHX.js} +48 -27
- package/dist/{models-A4PVNWJK.js → models-I5QWSEOM.js} +39 -25
- package/dist/monitor-GE4ID3IA.js +661 -0
- package/dist/{chunk-FCIH3BIZ.js → orchestrator-EM5MC3HM.js} +15979 -12407
- package/dist/{paths-C4H6IV77.js → paths-UXLN5YYZ.js} +10 -5
- package/dist/{preload-6WVMHX3A.js → preload-P6DGH2PZ.js} +2 -2
- package/dist/{reset-BGW6OGMV.js → reset-L2FQEE3E.js} +16 -10
- package/dist/{run-YTPEYQOH.js → run-ZU3QMZPZ.js} +101 -61
- package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
- package/dist/{skills-2V6RA3OQ.js → skills-X5VXCRNQ.js} +34 -14
- package/dist/{skills-eval-S2TVJO4F.js → skills-eval-WKIHWTHR.js} +70 -34
- package/dist/steer-GGWFUJUD.js +77 -0
- package/dist/{targets-TYXLPB23.js → targets-SNCPI2NR.js} +43 -27
- package/dist/terminal-lease-BNAHVHBS.js +395 -0
- package/dist/{trace-GGOJ6Q6Z.js → trace-PNCASAXC.js} +41 -16
- package/dist/{chunk-N6F52NLF.js → tree-sitter-HGKH6LG4.js} +28 -2306
- package/dist/{uninstall-LLLT4F4W.js → uninstall-FZCQCDKC.js} +10 -5
- package/dist/{upgrade-33G2LMM5.js → upgrade-JQHHPQ4K.js} +45 -25
- package/dist/{usage-ZAFSXKKG.js → usage-OR4O5SMZ.js} +62 -31
- package/dist/verify-375KUB3Y.js +716 -0
- package/dist/web-fetch-2YHJ3KTG.js +638 -0
- package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-UEXP2ARI.js} +74 -34
- package/dist/worker/entry.js +221 -36
- package/dist/workspace-G4ZWUIPR.js +22 -0
- package/docs/README.md +22 -17
- package/docs/acp.md +168 -16
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +136 -7
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +114 -71
- package/docs/config-knobs-audit.md +1 -3
- package/docs/configuration-and-targets.md +174 -46
- package/docs/context-engine.md +29 -6
- package/docs/development-pipeline.md +26 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +2 -2
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +13 -5
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +6 -2
- package/docs/evolution.md +2 -2
- package/docs/exit-codes-and-output.md +15 -9
- package/docs/extensions-and-sharing.md +9 -9
- package/docs/fleet-dispatch.md +7 -5
- package/docs/git-commit-provenance.md +120 -0
- package/docs/glossary.md +1 -1
- package/docs/installation-and-lifecycle.md +34 -27
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +45 -14
- package/docs/observability.md +8 -5
- package/docs/performance-methodology.md +491 -0
- package/docs/pi-boundary.md +72 -0
- package/docs/proactive-memory.md +3 -3
- package/docs/prompt-envelope-and-tools.md +24 -3
- package/docs/provider-adapter-cookbook.md +57 -4
- package/docs/release-cut-checklist.md +129 -115
- package/docs/safety-model.md +9 -5
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +55 -12
- package/docs/skills-marketplace.md +12 -8
- package/docs/time-conventions.md +1 -1
- package/docs/tool-usage.md +3 -3
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +10 -7
- package/docs/tui-design.md +47 -10
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +19 -22
- package/skills/coding/ast-grep/SKILL.md +136 -0
- package/skills/coding/ast-grep/evals.md +56 -0
- package/skills/coding/ast-grep/references/rule_reference.md +297 -0
- package/skills/coding/coding-standards/SKILL.md +113 -0
- package/skills/coding/coding-standards/evals.md +34 -0
- package/skills/coding/prototype/SKILL.md +86 -0
- package/skills/coding/prototype/evals.md +42 -0
- package/skills/coding/prototype/references/LOGIC.md +67 -0
- package/skills/coding/prototype/references/UI.md +112 -0
- package/skills/coding/tdd/SKILL.md +101 -0
- package/skills/coding/tdd/evals.md +41 -0
- package/skills/coding/tdd/references/mocking.md +59 -0
- package/skills/coding/tdd/references/tests.md +77 -0
- package/skills/context/context-handoff/SKILL.md +126 -0
- package/skills/context/context-handoff/evals.md +57 -0
- package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
- package/skills/context/context-prime/SKILL.md +95 -0
- package/skills/context/context-prime/evals.md +54 -0
- package/skills/meta/clio-dev/SKILL.md +91 -0
- package/skills/meta/clio-dev/evals.md +45 -0
- package/skills/meta/clio-test/SKILL.md +130 -0
- package/skills/meta/clio-test/evals.md +43 -0
- package/skills/meta/clio-test/references/harness.md +97 -0
- package/skills/meta/clio-test/references/test-map.md +59 -0
- package/skills/meta/credentials/SKILL.md +125 -0
- package/skills/meta/credentials/evals.md +104 -0
- package/skills/meta/find-skills/SKILL.md +72 -0
- package/skills/meta/find-skills/evals.md +47 -0
- package/skills/meta/herdr/SKILL.md +127 -0
- package/skills/meta/herdr/evals.md +38 -0
- package/skills/meta/skill-craft/SKILL.md +102 -0
- package/skills/meta/skill-craft/evals.md +41 -0
- package/skills/planning/architecture/SKILL.md +129 -0
- package/skills/planning/architecture/evals.md +36 -0
- package/skills/planning/backlog/SKILL.md +90 -0
- package/skills/planning/backlog/evals.md +43 -0
- package/skills/planning/prd/SKILL.md +82 -0
- package/skills/planning/prd/evals.md +49 -0
- package/skills/planning/product-intent/SKILL.md +112 -0
- package/skills/planning/product-intent/evals.md +36 -0
- package/skills/planning/tech-spec/SKILL.md +115 -0
- package/skills/planning/tech-spec/evals.md +47 -0
- package/skills/registry.yaml +136 -0
- package/skills/research/arxiv-literature/SKILL.md +104 -0
- package/skills/research/arxiv-literature/evals.md +58 -0
- package/skills/research/experiment-protocol/SKILL.md +122 -0
- package/skills/research/experiment-protocol/evals.md +91 -0
- package/skills/research/scientific-debugging/SKILL.md +119 -0
- package/skills/research/scientific-debugging/evals.md +138 -0
- package/skills/research/scientific-modernization/SKILL.md +138 -0
- package/skills/research/scientific-modernization/evals.md +84 -0
- package/skills/workflow/design-council/SKILL.md +139 -0
- package/skills/workflow/design-council/evals.md +97 -0
- package/skills/workflow/grill-me/SKILL.md +186 -0
- package/skills/workflow/grill-me/evals.md +78 -0
- package/skills/workflow/workflow-distiller/SKILL.md +136 -0
- package/skills/workflow/workflow-distiller/evals.md +107 -0
- package/src/cli/acp.ts +31 -4
- package/src/cli/clio.ts +68 -6
- package/src/cli/config-inspect.ts +28 -22
- package/src/cli/configure.ts +47 -9
- package/src/cli/context-clear.ts +2 -2
- package/src/cli/context-index.ts +21 -23
- package/src/cli/context.ts +13 -8
- package/src/cli/default-target.ts +9 -17
- package/src/cli/docs.ts +11 -5
- package/src/cli/evidence.ts +4 -1
- package/src/cli/extensions.ts +10 -1
- package/src/cli/fleet.ts +47 -6
- package/src/cli/index.ts +55 -26
- package/src/cli/memory.ts +3 -1
- package/src/cli/models.ts +1 -1
- package/src/cli/modes/json-stream.ts +37 -1
- package/src/cli/modes/print.ts +24 -9
- package/src/cli/run.ts +2 -2
- package/src/cli/skills-eval.ts +23 -8
- package/src/cli/skills.ts +19 -4
- package/src/cli/targets.ts +4 -0
- package/src/cli/text-layout.ts +15 -5
- package/src/cli/trace.ts +62 -14
- package/src/cli/upgrade.ts +18 -2
- package/src/cli/usage.ts +10 -3
- package/src/cli/wiki-generate.ts +2 -1
- package/src/core/agent-environment.ts +7 -0
- package/src/core/bash-exec.ts +72 -1
- package/src/core/boot-trace.ts +9 -4
- package/src/core/bus-events.ts +20 -4
- package/src/core/commit-attribution.ts +157 -0
- package/src/core/compile-cache.ts +159 -0
- package/src/core/config.ts +131 -2
- package/src/core/defaults.ts +39 -5
- package/src/core/domain-loader.ts +12 -5
- package/src/core/git-commit-attribution.ts +387 -0
- package/src/core/incomplete-installation.ts +45 -0
- package/src/core/response-schema.ts +1 -1
- package/src/core/safe-exec.ts +13 -1
- package/src/core/settings-layers.ts +155 -21
- package/src/core/skill-activation.ts +1 -1
- package/src/core/startup-timer.ts +3 -3
- package/src/core/state-file-lock.ts +13 -1
- package/src/core/termination.ts +78 -5
- package/src/domains/config/classify.ts +15 -3
- package/src/domains/config/extension.ts +19 -13
- package/src/domains/config/index.ts +10 -0
- package/src/domains/config/keybindings.ts +45 -9
- package/src/domains/context/bootstrap-prompt.ts +1 -1
- package/src/domains/context/bootstrap.ts +111 -18
- package/src/domains/context/clear.ts +16 -11
- package/src/domains/context/clio-md.ts +111 -9
- package/src/domains/context/codewiki/artifact.ts +400 -0
- package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
- package/src/domains/context/codewiki/build-worker.ts +54 -0
- package/src/domains/context/codewiki/coordinator.ts +182 -0
- package/src/domains/context/codewiki/indexer.ts +59 -144
- package/src/domains/context/codewiki/paths.ts +67 -0
- package/src/domains/context/codewiki/schema.ts +80 -0
- package/src/domains/context/codewiki/tree-sitter.ts +1 -1
- package/src/domains/context/contract.ts +11 -5
- package/src/domains/context/extension.ts +94 -143
- package/src/domains/context/fingerprint.ts +3 -1
- package/src/domains/context/index.ts +12 -22
- package/src/domains/context/project-metadata.ts +19 -0
- package/src/domains/context/prompt-context.ts +9 -10
- package/src/domains/context/refresh.ts +29 -21
- package/src/domains/context/runtime.ts +17 -0
- package/src/domains/context/wiki/generate.ts +39 -34
- package/src/domains/context/wiki/plan.ts +1 -1
- package/src/domains/context/wiki/prompts.ts +21 -8
- package/src/domains/dispatch/code-step.ts +20 -1
- package/src/domains/dispatch/extension.ts +158 -21
- package/src/domains/dispatch/failure-classification.ts +6 -0
- package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
- package/src/domains/dispatch/orphan-recovery.ts +50 -8
- package/src/domains/dispatch/receipt-integrity.ts +5 -0
- package/src/domains/dispatch/state.ts +31 -6
- package/src/domains/dispatch/transport.ts +2 -1
- package/src/domains/dispatch/types.ts +14 -0
- package/src/domains/dispatch/worker-spawn.ts +21 -2
- package/src/domains/eval/metrics/context.ts +1 -1
- package/src/domains/eval/types.ts +0 -1
- package/src/domains/evidence/build.ts +41 -1
- package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
- package/src/domains/lifecycle/migrations/index.ts +24 -4
- package/src/domains/middleware/hooks-io.ts +12 -0
- package/src/domains/middleware/skills-reminder.ts +30 -15
- package/src/domains/prompts/compiler.ts +142 -84
- package/src/domains/prompts/contract.ts +18 -2
- package/src/domains/prompts/extension.ts +39 -7
- package/src/domains/prompts/fragment-loader.ts +0 -1
- package/src/domains/prompts/fragments/identity/clio.md +2 -4
- package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
- package/src/domains/prompts/fragments/identity/self-awareness.md +1 -45
- package/src/domains/prompts/fragments/operating/contract.md +4 -50
- package/src/domains/prompts/fragments/operating/delegation.md +42 -0
- package/src/domains/prompts/fragments/operating/skills.md +26 -0
- package/src/domains/prompts/fragments/operating/worker.md +16 -0
- package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
- package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
- package/src/domains/prompts/fragments/safety/read-only.md +4 -4
- package/src/domains/prompts/fragments/safety/suggest.md +2 -2
- package/src/domains/prompts/fragments/wiki/page.md +10 -0
- package/src/domains/prompts/fragments/wiki/plan.md +10 -0
- package/src/domains/prompts/preload.ts +3 -3
- package/src/domains/providers/auth/api-key.ts +1 -1
- package/src/domains/providers/auth/backend-file.ts +20 -10
- package/src/domains/providers/auth/backend-memory.ts +59 -4
- package/src/domains/providers/auth/boot-status.ts +65 -0
- package/src/domains/providers/auth/oauth.ts +2 -1
- package/src/domains/providers/auth/storage.ts +97 -38
- package/src/domains/providers/capabilities.ts +12 -4
- package/src/domains/providers/contract.ts +15 -4
- package/src/domains/providers/extension.ts +18 -6
- package/src/domains/providers/model-runtime-capabilities.ts +15 -4
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +118 -35
- package/src/domains/providers/plugins.ts +5 -3
- package/src/domains/providers/probe/fingerprint.ts +25 -5
- package/src/domains/providers/registry.ts +31 -10
- package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
- package/src/domains/providers/runtimes/builtins.ts +2 -2
- package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
- package/src/domains/providers/runtimes/common/local-synth.ts +6 -7
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
- package/src/domains/providers/support.ts +6 -3
- package/src/domains/providers/types/local-model-quirks.ts +7 -9
- package/src/domains/providers/types/runtime-descriptor.ts +12 -1
- package/src/domains/providers/types/target-descriptor.ts +22 -0
- package/src/domains/resources/contract.ts +0 -1
- package/src/domains/resources/extension.ts +1 -3
- package/src/domains/resources/loader.ts +3 -4
- package/src/domains/resources/prompts/loader.ts +16 -2
- package/src/domains/resources/prompts/substitute.ts +1 -65
- package/src/domains/resources/skills/content-hash.ts +2 -0
- package/src/domains/resources/skills/install.ts +17 -0
- package/src/domains/resources/skills/loader.ts +17 -10
- package/src/domains/resources/skills/marketplace.ts +55 -9
- package/src/domains/safety/action-classifier.ts +4 -2
- package/src/domains/safety/audit.ts +8 -2
- package/src/domains/safety/extension.ts +1 -1
- package/src/domains/session/compaction/branch-summary.ts +3 -2
- package/src/domains/session/compaction/cut-point.ts +2 -1
- package/src/domains/session/compaction/tokens.ts +2 -1
- package/src/domains/session/context-ledger.ts +14 -0
- package/src/domains/session/contract.ts +15 -0
- package/src/domains/session/decision-board.ts +190 -0
- package/src/domains/session/entries.ts +66 -3
- package/src/domains/session/extension.ts +93 -12
- package/src/domains/session/retry.ts +10 -18
- package/src/domains/session/session-artifacts.ts +107 -0
- package/src/domains/session/task-board.ts +207 -13
- package/src/domains/session/tree/active-path.ts +44 -5
- package/src/domains/session/tree/fork.ts +26 -27
- package/src/domains/session/tree/preview.ts +2 -2
- package/src/domains/session/workspace/git-probe.ts +17 -11
- package/src/domains/user-tasks/store.ts +297 -0
- package/src/engine/acp/errors.ts +96 -0
- package/src/engine/acp/server.ts +1728 -146
- package/src/engine/acp/transport.ts +135 -14
- package/src/engine/acp/types.ts +26 -0
- package/src/engine/agent.ts +3 -3
- package/src/engine/ai.ts +32 -27
- package/src/engine/alcf-oauth.ts +26 -19
- package/src/engine/api-registry.ts +223 -0
- package/src/engine/apis/index.ts +3 -7
- package/src/engine/apis/llamacpp-residency.ts +49 -9
- package/src/engine/apis/lmstudio-residency.ts +5 -21
- package/src/engine/apis/lmstudio.ts +243 -0
- package/src/engine/apis/ollama-native.ts +24 -3
- package/src/engine/apis/openai-completions.ts +170 -91
- package/src/engine/apis/residency.ts +139 -3
- package/src/engine/apis/types.ts +16 -0
- package/src/engine/env-api-keys.ts +98 -0
- package/src/engine/gemma-channel-filter.ts +223 -0
- package/src/engine/instrumented-tui.ts +192 -0
- package/src/engine/messages.ts +14 -0
- package/src/engine/models.ts +42 -0
- package/src/engine/oauth.ts +16 -12
- package/src/engine/prompt-templates.ts +1 -0
- package/src/engine/provider-payload.ts +16 -59
- package/src/engine/strip-tokenizer-sentinels.ts +1 -1
- package/src/engine/truncate.ts +9 -0
- package/src/engine/tui.ts +17 -9
- package/src/engine/types.ts +3 -6
- package/src/engine/worker-runtime-capabilities.ts +5 -0
- package/src/engine/worker-runtime.ts +1 -1
- package/src/engine/worker-tools.ts +9 -4
- package/src/entry/boot-options.ts +50 -0
- package/src/entry/orchestrator.ts +288 -150
- package/src/interactive/application-controller.ts +89 -2
- package/src/interactive/chat-loop.ts +266 -41
- package/src/interactive/chat-panel.ts +715 -278
- package/src/interactive/chat-renderer.ts +306 -85
- package/src/interactive/clio-editor.ts +3 -8
- package/src/interactive/command-fallbacks.ts +2 -2
- package/src/interactive/context-overlay.ts +27 -1
- package/src/interactive/editor-submit.ts +253 -24
- package/src/interactive/export-html/ansi-to-html.ts +161 -0
- package/src/interactive/export-html/index.ts +51 -0
- package/src/interactive/export-html/template.ts +45 -0
- package/src/interactive/export-html/tool-renderer.ts +54 -0
- package/src/interactive/footer/dashboard.ts +4 -0
- package/src/interactive/footer/notifications.ts +1 -1
- package/src/interactive/footer/widgets.ts +42 -22
- package/src/interactive/footer-panel.ts +8 -3
- package/src/interactive/format-time.ts +14 -2
- package/src/interactive/interactive-application.ts +203 -17
- package/src/interactive/interactive-event-projection.ts +18 -1
- package/src/interactive/interactive-input-runtime.ts +50 -4
- package/src/interactive/interactive-presentation.ts +151 -19
- package/src/interactive/interactive-shell.ts +268 -14
- package/src/interactive/interactive-slash-runtime.ts +184 -117
- package/src/interactive/interactive-tickers.ts +38 -7
- package/src/interactive/keybinding-manager.ts +1 -1
- package/src/interactive/layout.ts +40 -3
- package/src/interactive/overlay-frame.ts +1 -1
- package/src/interactive/overlay-general-openers.ts +58 -1
- package/src/interactive/overlay-key-routing.ts +3 -0
- package/src/interactive/overlay-lifecycle.ts +13 -0
- package/src/interactive/overlay-permission-lifecycle.ts +2 -1
- package/src/interactive/overlay-session-lifecycle.ts +69 -12
- package/src/interactive/overlays/ask-user.ts +146 -24
- package/src/interactive/overlays/decisions.ts +300 -0
- package/src/interactive/overlays/help-reference.ts +15 -10
- package/src/interactive/overlays/model-selector.ts +34 -16
- package/src/interactive/overlays/session-selector.ts +18 -0
- package/src/interactive/overlays/settings.ts +105 -17
- package/src/interactive/overlays/skills-hub.ts +4 -4
- package/src/interactive/overlays/tree-selector.ts +41 -6
- package/src/interactive/render-trace.ts +499 -90
- package/src/interactive/renderers/compaction-summary.ts +2 -2
- package/src/interactive/renderers/diff.ts +115 -104
- package/src/interactive/renderers/mermaid.ts +53 -0
- package/src/interactive/renderers/tool-execution.ts +516 -168
- package/src/interactive/renderers/worker-entry.ts +20 -4
- package/src/interactive/session-switch-settlement.ts +10 -0
- package/src/interactive/slash-autocomplete.ts +6 -114
- package/src/interactive/slash-commands.ts +135 -47
- package/src/interactive/slash-spec.ts +9 -38
- package/src/interactive/status/controller.ts +5 -1
- package/src/interactive/status/index.ts +12 -1
- package/src/interactive/status/reasoning.ts +87 -0
- package/src/interactive/status/summary.ts +13 -2
- package/src/interactive/stdout-backpressure.ts +99 -0
- package/src/interactive/stream-pacer.ts +530 -0
- package/src/interactive/stream-pacing-policy.ts +66 -0
- package/src/interactive/tasks-overlay.ts +368 -14
- package/src/interactive/terminal-lease.ts +485 -0
- package/src/interactive/theme/tokens.ts +1 -1
- package/src/interactive/transcript-detail.ts +120 -0
- package/src/interactive/turn-context.ts +4 -3
- package/src/interactive/turn-persistence.ts +30 -13
- package/src/interactive/turn-queues.ts +12 -0
- package/src/interactive/turn-recovery.ts +25 -8
- package/src/interactive/turn-runtime.ts +79 -12
- package/src/interactive/turn-state.ts +10 -0
- package/src/interactive/view/artifacts.ts +114 -4
- package/src/interactive/view/view-overlay.ts +3 -0
- package/src/interactive/welcome-dashboard.ts +17 -16
- package/src/interactive/worker-receipts.ts +52 -3
- package/src/interactive/worker-stream.ts +5 -1
- package/src/tools/agent-tools.ts +23 -3
- package/src/tools/artifact.ts +2 -2
- package/src/tools/ask-user.ts +23 -13
- package/src/tools/bash.ts +30 -2
- package/src/tools/bootstrap.ts +34 -431
- package/src/tools/builtin-tool-catalog.ts +271 -0
- package/src/tools/codewiki/code-nav-surface.ts +29 -0
- package/src/tools/codewiki/code-nav.ts +8 -22
- package/src/tools/codewiki/shared.ts +41 -38
- package/src/tools/context/docs-engine.ts +14 -3
- package/src/tools/context/index.ts +107 -28
- package/src/tools/context/surface.ts +19 -0
- package/src/tools/core-bootstrap.ts +168 -0
- package/src/tools/credential-present.ts +5 -5
- package/src/tools/dispatch-admission.ts +533 -0
- package/src/tools/dispatch-background.ts +54 -0
- package/src/tools/dispatch-event-text.ts +6 -0
- package/src/tools/dispatch-plan.ts +9 -4
- package/src/tools/dispatch-run-events.ts +238 -0
- package/src/tools/dispatch-runner.ts +2370 -0
- package/src/tools/dispatch-scout-admission.ts +295 -0
- package/src/tools/dispatch-types.ts +77 -0
- package/src/tools/dispatch.ts +67 -3161
- package/src/tools/find.ts +4 -2
- package/src/tools/grep.ts +2 -2
- package/src/tools/lazy-tool.ts +60 -0
- package/src/tools/ledger.ts +3 -3
- package/src/tools/monitor-surface.ts +36 -0
- package/src/tools/monitor.ts +2 -32
- package/src/tools/observers.ts +2 -2
- package/src/tools/presentation.ts +107 -0
- package/src/tools/registry.ts +45 -27
- package/src/tools/safe-exec.ts +2 -2
- package/src/tools/steer-surface.ts +17 -0
- package/src/tools/steer.ts +2 -13
- package/src/tools/tasks.ts +108 -11
- package/src/tools/truncate.ts +25 -184
- package/src/tools/verify/frontend.ts +3 -1
- package/src/tools/verify/index.ts +3 -38
- package/src/tools/verify/surface.ts +46 -0
- package/src/tools/web-fetch-surface.ts +23 -0
- package/src/tools/web-fetch.ts +2 -20
- package/src/tools/write.ts +7 -2
- package/src/worker/entry.ts +39 -2
- package/src/worker/spec-contract.ts +26 -5
- package/dist/chunk-7SS2CTV2.js +0 -61361
- package/dist/chunk-DKGKUHFA.js +0 -924
- package/dist/chunk-GEP36Y4X.js +0 -12796
- package/dist/chunk-XYWBQRDM.js +0 -137
- package/dist/clio-BZVGEUFJ.js +0 -58
- package/dist/configure-S7S6F6CL.js +0 -32
- package/docs/html/agents_blueprint.html +0 -936
- package/docs/html/alcf_blueprint.html +0 -324
- package/docs/html/architecture_blueprint.html +0 -850
- package/docs/html/commands_blueprint.html +0 -939
- package/docs/html/config_knobs_audit_blueprint.html +0 -178
- package/docs/html/configuration_blueprint.html +0 -1080
- package/docs/html/context_blueprint.html +0 -603
- package/docs/html/documentation_blueprint.html +0 -832
- package/docs/html/environment_blueprint.html +0 -404
- package/docs/html/eval_blueprint.html +0 -743
- package/docs/html/evals_internal_blueprint.html +0 -190
- package/docs/html/evolution_blueprint.html +0 -674
- package/docs/html/extensions_blueprint.html +0 -2065
- package/docs/html/fleet_dispatch_blueprint.html +0 -286
- package/docs/html/index.html +0 -919
- package/docs/html/lifecycle_blueprint.html +0 -723
- package/docs/html/memory_blueprint.html +0 -699
- package/docs/html/middleware_blueprint.html +0 -664
- package/docs/html/models_blueprint.html +0 -2366
- package/docs/html/observability_blueprint.html +0 -683
- package/docs/html/provider_adapter_blueprint.html +0 -245
- package/docs/html/safety_blueprint.html +0 -1386
- package/docs/html/shared.css +0 -571
- package/docs/html/shared.js +0 -143
- package/docs/html/skills_blueprint.html +0 -671
- package/docs/html/soak_blueprint.html +0 -182
- package/docs/html/tool_usage_blueprint.html +0 -350
- package/docs/html/tools_blueprint.html +0 -2249
- package/docs/html/trace_blueprint.html +0 -235
- package/docs/html/tui_design_blueprint.html +0 -374
- package/docs/html/validation_blueprint.html +0 -961
- package/docs/html/worker_dispatch_blueprint.html +0 -231
- package/src/core/release.ts +0 -2
- package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
- package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
- package/src/engine/apis/lmstudio-native.ts +0 -1438
- package/src/engine/apis/thinking-replay.ts +0 -11
- package/src/tools/string-enum.ts +0 -15
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Api,
|
|
3
|
+
AssistantMessage,
|
|
4
|
+
AssistantMessageEventStream,
|
|
5
|
+
Context,
|
|
6
|
+
Model,
|
|
7
|
+
ProviderStreams,
|
|
8
|
+
SimpleStreamOptions,
|
|
9
|
+
StreamOptions,
|
|
10
|
+
} from "@earendil-works/pi-ai";
|
|
11
|
+
import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
|
|
12
|
+
import { azureOpenAIResponsesApi } from "@earendil-works/pi-ai/api/azure-openai-responses.lazy";
|
|
13
|
+
import { bedrockConverseStreamApi } from "@earendil-works/pi-ai/api/bedrock-converse-stream.lazy";
|
|
14
|
+
import { googleGenerativeAIApi } from "@earendil-works/pi-ai/api/google-generative-ai.lazy";
|
|
15
|
+
import { googleVertexApi } from "@earendil-works/pi-ai/api/google-vertex.lazy";
|
|
16
|
+
import { mistralConversationsApi } from "@earendil-works/pi-ai/api/mistral-conversations.lazy";
|
|
17
|
+
import { openAICodexResponsesApi } from "@earendil-works/pi-ai/api/openai-codex-responses.lazy";
|
|
18
|
+
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
19
|
+
import { openAIResponsesApi } from "@earendil-works/pi-ai/api/openai-responses.lazy";
|
|
20
|
+
import { piMessagesApi } from "@earendil-works/pi-ai/api/pi-messages.lazy";
|
|
21
|
+
import {
|
|
22
|
+
createFauxCore,
|
|
23
|
+
type FauxProviderRegistration,
|
|
24
|
+
type RegisterFauxProviderOptions,
|
|
25
|
+
} from "@earendil-works/pi-ai/providers/faux";
|
|
26
|
+
import "@earendil-works/pi-ai/providers/images/register-builtins";
|
|
27
|
+
|
|
28
|
+
import { getEngineEnvApiKey } from "./env-api-keys.js";
|
|
29
|
+
import { engineModels } from "./models.js";
|
|
30
|
+
|
|
31
|
+
export interface EngineRegisteredApiProvider extends ProviderStreams {
|
|
32
|
+
api: Api;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface RegistryEntry {
|
|
36
|
+
provider: EngineRegisteredApiProvider;
|
|
37
|
+
sourceId?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface CompatUniverse {
|
|
41
|
+
registerApiProvider(provider: EngineRegisteredApiProvider, sourceId?: string): void;
|
|
42
|
+
unregisterApiProviders(sourceId: string): void;
|
|
43
|
+
stream(model: Model<Api>, context: Context, options?: StreamOptions): AssistantMessageEventStream;
|
|
44
|
+
streamSimple(model: Model<Api>, context: Context, options?: SimpleStreamOptions): AssistantMessageEventStream;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const registry = new Map<Api, RegistryEntry>();
|
|
48
|
+
const builtinInstances = new Map<Api, EngineRegisteredApiProvider>();
|
|
49
|
+
let compatUniverse: CompatUniverse | undefined;
|
|
50
|
+
let compatUniversePromise: Promise<void> | undefined;
|
|
51
|
+
let builtinsRegistered = false;
|
|
52
|
+
|
|
53
|
+
function wrappedProvider(provider: EngineRegisteredApiProvider): EngineRegisteredApiProvider {
|
|
54
|
+
const { api } = provider;
|
|
55
|
+
return {
|
|
56
|
+
api,
|
|
57
|
+
stream(model, context, options) {
|
|
58
|
+
if (model.api !== api) throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
59
|
+
return provider.stream(model, context, options);
|
|
60
|
+
},
|
|
61
|
+
streamSimple(model, context, options) {
|
|
62
|
+
if (model.api !== api) throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
63
|
+
return provider.streamSimple(model, context, options);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function registerEngineApiProvider(provider: EngineRegisteredApiProvider, sourceId?: string): void {
|
|
69
|
+
const wrapped = wrappedProvider(provider);
|
|
70
|
+
registry.set(provider.api, { provider: wrapped, ...(sourceId === undefined ? {} : { sourceId }) });
|
|
71
|
+
compatUniverse?.registerApiProvider(provider, sourceId);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function unregisterEngineApiProviders(sourceId: string): void {
|
|
75
|
+
for (const [api, entry] of registry) {
|
|
76
|
+
if (entry.sourceId === sourceId) registry.delete(api);
|
|
77
|
+
}
|
|
78
|
+
compatUniverse?.unregisterApiProviders(sourceId);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function getEngineApiProvider(api: Api): EngineRegisteredApiProvider | undefined {
|
|
82
|
+
return registry.get(api)?.provider;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const BUILTIN_APIS: readonly (readonly [Api, ProviderStreams])[] = [
|
|
86
|
+
["anthropic-messages", anthropicMessagesApi()],
|
|
87
|
+
["openai-completions", openAICompletionsApi()],
|
|
88
|
+
["openai-responses", openAIResponsesApi()],
|
|
89
|
+
["openai-codex-responses", openAICodexResponsesApi()],
|
|
90
|
+
["azure-openai-responses", azureOpenAIResponsesApi()],
|
|
91
|
+
["google-generative-ai", googleGenerativeAIApi()],
|
|
92
|
+
["google-vertex", googleVertexApi()],
|
|
93
|
+
["mistral-conversations", mistralConversationsApi()],
|
|
94
|
+
["bedrock-converse-stream", bedrockConverseStreamApi()],
|
|
95
|
+
["pi-messages", piMessagesApi()],
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
export function registerEngineBuiltins(): void {
|
|
99
|
+
if (builtinsRegistered) return;
|
|
100
|
+
builtinsRegistered = true;
|
|
101
|
+
for (const [api, streams] of BUILTIN_APIS) {
|
|
102
|
+
if (!getEngineApiProvider(api)) registerEngineApiProvider({ api, ...streams });
|
|
103
|
+
const registered = getEngineApiProvider(api);
|
|
104
|
+
if (registered) builtinInstances.set(api, registered);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function withEnvApiKey<T extends StreamOptions | SimpleStreamOptions>(
|
|
109
|
+
model: Model<Api>,
|
|
110
|
+
options: T | undefined,
|
|
111
|
+
): T | undefined {
|
|
112
|
+
if (typeof options?.apiKey === "string" && options.apiKey.trim().length > 0) return options;
|
|
113
|
+
const apiKey = getEngineEnvApiKey(model.provider, options?.env);
|
|
114
|
+
if (!apiKey || apiKey === "<authenticated>") return options;
|
|
115
|
+
return { ...options, apiKey } as T;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function builtinProvider(model: Model<Api>) {
|
|
119
|
+
if (getEngineApiProvider(model.api) !== builtinInstances.get(model.api)) return undefined;
|
|
120
|
+
const provider = engineModels.getProvider(model.provider);
|
|
121
|
+
return provider?.getModels().some((candidate) => candidate.api === model.api) ? provider : undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function resolved(api: Api): EngineRegisteredApiProvider {
|
|
125
|
+
const provider = getEngineApiProvider(api);
|
|
126
|
+
if (!provider) throw new Error(`No API provider registered for api: ${api}`);
|
|
127
|
+
return provider;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function hasCloudflareAuth(options?: StreamOptions): boolean {
|
|
131
|
+
return (
|
|
132
|
+
(typeof options?.apiKey === "string" && options.apiKey.trim().length > 0) ||
|
|
133
|
+
typeof options?.headers?.["cf-aig-authorization"] === "string"
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function engineStream(
|
|
138
|
+
model: Model<Api>,
|
|
139
|
+
context: Context,
|
|
140
|
+
options?: StreamOptions,
|
|
141
|
+
): AssistantMessageEventStream {
|
|
142
|
+
registerEngineBuiltins();
|
|
143
|
+
if (compatUniverse) return compatUniverse.stream(model, context, options);
|
|
144
|
+
const provider = builtinProvider(model);
|
|
145
|
+
if (provider) {
|
|
146
|
+
if (model.provider.startsWith("cloudflare-") && !hasCloudflareAuth(options)) {
|
|
147
|
+
return engineModels.stream(model, context, options);
|
|
148
|
+
}
|
|
149
|
+
return provider.stream(model, context, withEnvApiKey(model, options));
|
|
150
|
+
}
|
|
151
|
+
return resolved(model.api).stream(model, context, withEnvApiKey(model, options));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function engineStreamSimple(
|
|
155
|
+
model: Model<Api>,
|
|
156
|
+
context: Context,
|
|
157
|
+
options?: SimpleStreamOptions,
|
|
158
|
+
): AssistantMessageEventStream {
|
|
159
|
+
registerEngineBuiltins();
|
|
160
|
+
if (compatUniverse) return compatUniverse.streamSimple(model, context, options);
|
|
161
|
+
const provider = builtinProvider(model);
|
|
162
|
+
if (provider) {
|
|
163
|
+
if (model.provider.startsWith("cloudflare-") && !hasCloudflareAuth(options)) {
|
|
164
|
+
return engineModels.streamSimple(model, context, options);
|
|
165
|
+
}
|
|
166
|
+
return provider.streamSimple(model, context, withEnvApiKey(model, options));
|
|
167
|
+
}
|
|
168
|
+
return resolved(model.api).streamSimple(model, context, withEnvApiKey(model, options));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function completeEngineSimple(
|
|
172
|
+
model: Model<Api>,
|
|
173
|
+
context: Context,
|
|
174
|
+
options?: SimpleStreamOptions,
|
|
175
|
+
): Promise<AssistantMessage> {
|
|
176
|
+
return engineStreamSimple(model, context, options).result();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function registerEngineFauxProvider(options: RegisterFauxProviderOptions = {}): FauxProviderRegistration {
|
|
180
|
+
registerEngineBuiltins();
|
|
181
|
+
const core = createFauxCore(options);
|
|
182
|
+
const sourceId = `faux-provider-${Math.random().toString(36).slice(2, 10)}`;
|
|
183
|
+
registerEngineApiProvider({ api: core.api, stream: core.stream, streamSimple: core.streamSimple }, sourceId);
|
|
184
|
+
return {
|
|
185
|
+
api: core.api,
|
|
186
|
+
models: core.models,
|
|
187
|
+
getModel: core.getModel,
|
|
188
|
+
state: core.state,
|
|
189
|
+
setResponses: core.setResponses,
|
|
190
|
+
appendResponses: core.appendResponses,
|
|
191
|
+
getPendingResponseCount: core.getPendingResponseCount,
|
|
192
|
+
unregister() {
|
|
193
|
+
unregisterEngineApiProviders(sourceId);
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Join Pi's process-global registry before importing an external runtime.
|
|
200
|
+
* This is intentionally the only dynamic /compat edge: no configured plugin,
|
|
201
|
+
* no aggregate module. Existing Clio overrides are mirrored before plugin
|
|
202
|
+
* evaluation so plugin registrations retain their historical last-writer wins.
|
|
203
|
+
*/
|
|
204
|
+
export async function activateExternalPluginApiBridge(): Promise<void> {
|
|
205
|
+
if (compatUniverse) return;
|
|
206
|
+
compatUniversePromise ??= (async () => {
|
|
207
|
+
registerEngineBuiltins();
|
|
208
|
+
const compat = (await import("@earendil-works/pi-ai/compat")) as CompatUniverse;
|
|
209
|
+
for (const [api, entry] of registry) {
|
|
210
|
+
// Pi's compat import has already installed its own built-ins and must keep
|
|
211
|
+
// those identities so provider-owned auth/header dispatch remains active.
|
|
212
|
+
if (entry.provider === builtinInstances.get(api)) continue;
|
|
213
|
+
compat.registerApiProvider(entry.provider, entry.sourceId);
|
|
214
|
+
}
|
|
215
|
+
compatUniverse = compat;
|
|
216
|
+
})().catch((error: unknown) => {
|
|
217
|
+
compatUniversePromise = undefined;
|
|
218
|
+
throw error;
|
|
219
|
+
});
|
|
220
|
+
await compatUniversePromise;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
registerEngineBuiltins();
|
package/src/engine/apis/index.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { lmstudioNativeApiProvider } from "./lmstudio-native.js";
|
|
1
|
+
import { registerEngineApiProvider } from "../api-registry.js";
|
|
4
2
|
import { ollamaNativeApiProvider } from "./ollama-native.js";
|
|
5
3
|
import { openAICompletionsApiProvider } from "./openai-completions.js";
|
|
6
4
|
|
|
7
|
-
export { lmstudioNativeApiProvider } from "./lmstudio-native.js";
|
|
8
5
|
export { ollamaNativeApiProvider } from "./ollama-native.js";
|
|
9
6
|
export { openAICompletionsApiProvider } from "./openai-completions.js";
|
|
10
7
|
export { setGlobalDefaultMaxOutputTokens } from "./output-budget.js";
|
|
@@ -14,7 +11,6 @@ let registered = false;
|
|
|
14
11
|
export function registerClioApiProviders(): void {
|
|
15
12
|
if (registered) return;
|
|
16
13
|
registered = true;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
registerApiProvider(lmstudioNativeApiProvider, "clio");
|
|
14
|
+
registerEngineApiProvider(openAICompletionsApiProvider, "clio");
|
|
15
|
+
registerEngineApiProvider(ollamaNativeApiProvider, "clio");
|
|
20
16
|
}
|
|
@@ -2,6 +2,7 @@ import { performance } from "node:perf_hooks";
|
|
|
2
2
|
import { sleep } from "../../core/timers.js";
|
|
3
3
|
import {
|
|
4
4
|
type ResidencyAdapter,
|
|
5
|
+
ResidencyPreconditionError,
|
|
5
6
|
reconcileResidency,
|
|
6
7
|
resetResidencyState,
|
|
7
8
|
residencyManaged,
|
|
@@ -42,7 +43,20 @@ export interface LlamaCppResidencyInput {
|
|
|
42
43
|
const LOAD_TIMEOUT_MS = 120_000;
|
|
43
44
|
const POLL_INTERVAL_MS = 500;
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
/**
|
|
47
|
+
* `sleeping` is a resident state, not an unloaded one. A router started with
|
|
48
|
+
* `--sleep-idle-seconds` parks an idle model's compute while it keeps the slot
|
|
49
|
+
* and its weights, and wakes it on the next inference request. Reading it as
|
|
50
|
+
* anything else costs twice: the model drops out of the resident set, so
|
|
51
|
+
* capacity math sees a free slot that does not exist, and the load path stops
|
|
52
|
+
* short-circuiting and re-requests a model the router is already running.
|
|
53
|
+
*/
|
|
54
|
+
type LlamaCppModelState = "loaded" | "loading" | "sleeping" | "unloaded" | "failed" | "unknown";
|
|
55
|
+
|
|
56
|
+
/** Router states that hold the model's slot on the server. */
|
|
57
|
+
function isResidentState(state: LlamaCppModelState): boolean {
|
|
58
|
+
return state === "loaded" || state === "loading" || state === "sleeping";
|
|
59
|
+
}
|
|
46
60
|
|
|
47
61
|
interface LlamaCppResidentModel {
|
|
48
62
|
id: string;
|
|
@@ -69,7 +83,9 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
69
83
|
function routerModelState(entry: Record<string, unknown>): LlamaCppModelState {
|
|
70
84
|
const status = entry.status;
|
|
71
85
|
const state = isRecord(status) ? status.value : status;
|
|
72
|
-
if (state === "loaded" || state === "loading" || state === "unloaded" || state === "failed")
|
|
86
|
+
if (state === "loaded" || state === "loading" || state === "sleeping" || state === "unloaded" || state === "failed") {
|
|
87
|
+
return state;
|
|
88
|
+
}
|
|
73
89
|
return "unknown";
|
|
74
90
|
}
|
|
75
91
|
|
|
@@ -87,10 +103,10 @@ function parseRouterModels(payload: unknown): LlamaCppRouterModel[] {
|
|
|
87
103
|
}
|
|
88
104
|
|
|
89
105
|
function residentModel(model: LlamaCppRouterModel): boolean {
|
|
90
|
-
return model.state
|
|
106
|
+
return isResidentState(model.state);
|
|
91
107
|
}
|
|
92
108
|
|
|
93
|
-
/** Extract resident (loaded or
|
|
109
|
+
/** Extract resident (loaded, loading, or sleeping) model records from a /v1/models payload. */
|
|
94
110
|
export function parseLlamaCppResidentModels(payload: unknown): LlamaCppResidentModel[] {
|
|
95
111
|
return parseRouterModels(payload)
|
|
96
112
|
.filter(residentModel)
|
|
@@ -169,7 +185,10 @@ async function waitForLoaded(input: LlamaCppResidencyInput, fetchImpl: typeof fe
|
|
|
169
185
|
while (performance.now() - started < LOAD_TIMEOUT_MS) {
|
|
170
186
|
const models = await fetchRouterModels(input, fetchImpl);
|
|
171
187
|
const model = models.find((entry) => entry.id === modelId);
|
|
172
|
-
|
|
188
|
+
// A model that goes straight back to sleep between polls is resident and
|
|
189
|
+
// serves the next request; waiting for it to read `loaded` would spin out
|
|
190
|
+
// the whole load timeout on an idle router.
|
|
191
|
+
if (model?.state === "loaded" || model?.state === "sleeping") return;
|
|
173
192
|
if (model?.state === "failed") throw new Error(`llama.cpp router reports '${modelId}' failed to load`);
|
|
174
193
|
await sleep(POLL_INTERVAL_MS);
|
|
175
194
|
}
|
|
@@ -182,7 +201,11 @@ async function ensureModelLoaded(
|
|
|
182
201
|
modelId: string,
|
|
183
202
|
knownState: LlamaCppModelState | undefined,
|
|
184
203
|
): Promise<void> {
|
|
185
|
-
|
|
204
|
+
// A sleeping model is already running and the router wakes it on the next
|
|
205
|
+
// inference request. Posting a load for it is not merely redundant: the
|
|
206
|
+
// router answers `400 model is already running`, which fails the turn before
|
|
207
|
+
// it reaches the model.
|
|
208
|
+
if (knownState === "loaded" || knownState === "sleeping") return;
|
|
186
209
|
if (knownState !== "loading") await postRouterModel(fetchImpl, loadUrl(input.baseUrl), modelId);
|
|
187
210
|
await waitForLoaded(input, fetchImpl, modelId);
|
|
188
211
|
}
|
|
@@ -206,14 +229,16 @@ async function restoreDisplacedPinned(
|
|
|
206
229
|
let changed = false;
|
|
207
230
|
for (const model of pinned) {
|
|
208
231
|
const state = current.find((entry) => entry.id === model.id)?.state;
|
|
209
|
-
if (state
|
|
232
|
+
if (state !== undefined && isResidentState(state)) continue;
|
|
210
233
|
changed = true;
|
|
211
234
|
await ensureModelLoaded(input, fetchImpl, model.id, state);
|
|
212
235
|
}
|
|
213
236
|
if (!changed) return;
|
|
214
237
|
const finalModels = await fetchRouterModels(input, fetchImpl);
|
|
215
238
|
const keep = finalModels.find((entry) => entry.id === input.keepModelId);
|
|
216
|
-
if (keep
|
|
239
|
+
if (keep === undefined || !isResidentState(keep.state)) {
|
|
240
|
+
throw new Error(`pinned-resident restore displaced '${input.keepModelId}'`);
|
|
241
|
+
}
|
|
217
242
|
}
|
|
218
243
|
|
|
219
244
|
/** Ensure the selected llama.cpp router model is resident before inference. */
|
|
@@ -235,15 +260,30 @@ export async function ensureLlamaCppResidency(input: LlamaCppResidencyInput): Pr
|
|
|
235
260
|
snapshot = models;
|
|
236
261
|
return models.filter(residentModel).map((entry) => ({ modelId: entry.id, tags: entry.tags }));
|
|
237
262
|
},
|
|
263
|
+
// The router enumerates every configured preset, resident or not, so a keep
|
|
264
|
+
// model missing from that listing is one this server cannot load at all.
|
|
265
|
+
// Reported from the snapshot listResident already fetched, so the check
|
|
266
|
+
// costs no extra round trip.
|
|
267
|
+
assertLoadable: async () => {
|
|
268
|
+
if (snapshot.some((entry) => entry.id === input.keepModelId)) return;
|
|
269
|
+
throw new ResidencyPreconditionError(
|
|
270
|
+
`llama.cpp router on '${input.targetId}' does not serve '${input.keepModelId}'; it lists ${snapshot.length} model(s). Residency left the target untouched. Fix the wire model id in settings, or add the preset to the server.`,
|
|
271
|
+
);
|
|
272
|
+
},
|
|
238
273
|
capacity: async () => (await fetchRouterProps(input, fetchImpl)).maxInstances,
|
|
239
274
|
keepModelTags: async () => snapshot.find((entry) => entry.id === input.keepModelId)?.tags,
|
|
240
275
|
unload: (modelId) => postRouterModel(fetchImpl, unloadUrl(input.baseUrl), modelId),
|
|
241
276
|
load: async (modelId) => {
|
|
242
277
|
const state = snapshot.find((entry) => entry.id === modelId)?.state;
|
|
243
|
-
if (state
|
|
278
|
+
if (state !== undefined && isResidentState(state) && state !== "loading") return;
|
|
244
279
|
await ensureModelLoaded(input, fetchImpl, modelId, state);
|
|
245
280
|
await restoreDisplacedPinned(input, fetchImpl, snapshot);
|
|
246
281
|
},
|
|
282
|
+
// `snapshot` predates the eviction and still calls this model resident, so
|
|
283
|
+
// the state is deliberately not read here: the reload has to be forced.
|
|
284
|
+
reloadEvicted: async (modelId) => {
|
|
285
|
+
await ensureModelLoaded(input, fetchImpl, modelId, undefined);
|
|
286
|
+
},
|
|
247
287
|
};
|
|
248
288
|
if (input.withLock) adapter.withLock = input.withLock;
|
|
249
289
|
if (input.now) adapter.now = input.now;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LM Studio-specific residency arithmetic, kept pure so the fit and duplicate
|
|
3
|
-
* rules are testable without a server
|
|
3
|
+
* rules are testable without a server connection.
|
|
4
4
|
*
|
|
5
|
-
* LM Studio exposes no
|
|
6
|
-
*
|
|
5
|
+
* LM Studio's `/api/v1/models` response exposes no total or free GPU memory,
|
|
6
|
+
* and a GPU offload cap does not prove that a load fits
|
|
7
7
|
* rather than failing an oversized load, so a request that does not fit is
|
|
8
8
|
* served from CPU at a fraction of the speed instead of erroring. Fit therefore
|
|
9
9
|
* cannot be computed from memory arithmetic; it is bounded by evidence.
|
|
10
10
|
*
|
|
11
|
-
* The evidence rule: while another model is resident on the same server,
|
|
12
|
-
*
|
|
11
|
+
* The evidence rule: while another model is resident on the same server, an
|
|
12
|
+
* explicit load is capped at {@link CO_RESIDENT_CONTEXT_CEILING} tokens.
|
|
13
13
|
* The KV cache of a long context is what actually overflows the card (measured
|
|
14
14
|
* on an RTX 5090: a 27B Q4 model loaded at its 262,144-token default beside a
|
|
15
15
|
* resident 26B model produced 25 tokens in 2m18s, and the same model at 131,072
|
|
@@ -74,19 +74,3 @@ export function fitLoadContextLength(input: ContextFitInput): ContextFitResult {
|
|
|
74
74
|
}
|
|
75
75
|
return { contextLength: ceiling, clampedFrom: input.requested, neighbours };
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Instances of `keepModelId` beyond the one Clio will use. LM Studio's load path
|
|
80
|
-
* happily creates a second instance of an already-resident model key (observed:
|
|
81
|
-
* `google/gemma-4-26b-a4b-qat:2`), and each instance holds a full weight copy
|
|
82
|
-
* and KV cache. The extras are the same model, so releasing them never takes a
|
|
83
|
-
* role's model away: whatever pointed at that key is still served by the
|
|
84
|
-
* survivor.
|
|
85
|
-
*/
|
|
86
|
-
export function duplicateInstances<T extends LmStudioResidentInstance>(
|
|
87
|
-
resident: ReadonlyArray<T>,
|
|
88
|
-
keepModelId: string,
|
|
89
|
-
): T[] {
|
|
90
|
-
const instances = resident.filter((entry) => entry.modelKey === keepModelId);
|
|
91
|
-
return instances.slice(1);
|
|
92
|
-
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type LmStudioCatalog,
|
|
5
|
+
type LmStudioLoadedInstance,
|
|
6
|
+
type LmStudioModelInfo,
|
|
7
|
+
listLmStudioModels,
|
|
8
|
+
lmStudioRootUrl,
|
|
9
|
+
requestLmStudioJson,
|
|
10
|
+
resolveLmStudioInstance,
|
|
11
|
+
} from "../../domains/providers/runtimes/common/lmstudio-http.js";
|
|
12
|
+
import type { TargetDescriptor } from "../../domains/providers/types/target-descriptor.js";
|
|
13
|
+
import { coResidentContextCeiling, fitLoadContextLength } from "./lmstudio-residency.js";
|
|
14
|
+
import { emitResidencyNotice, reconcileResidency, residencyManagedFor } from "./residency.js";
|
|
15
|
+
import { withResidencyLock } from "./residency-lock.js";
|
|
16
|
+
|
|
17
|
+
interface LmStudioModelMetadata {
|
|
18
|
+
clio?: {
|
|
19
|
+
targetId: string;
|
|
20
|
+
runtimeId: string;
|
|
21
|
+
lifecycle?: "user-managed" | "clio-managed";
|
|
22
|
+
lmstudio?: TargetDescriptor["lmstudio"];
|
|
23
|
+
lmstudioDefaultModel?: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const ownedInstancesByTarget = new Map<string, Set<string>>();
|
|
28
|
+
|
|
29
|
+
function ownedInstances(targetKey: string): Set<string> {
|
|
30
|
+
let owned = ownedInstancesByTarget.get(targetKey);
|
|
31
|
+
if (!owned) {
|
|
32
|
+
owned = new Set<string>();
|
|
33
|
+
ownedInstancesByTarget.set(targetKey, owned);
|
|
34
|
+
}
|
|
35
|
+
return owned;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function responseInstanceId(data: unknown): string | undefined {
|
|
39
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return undefined;
|
|
40
|
+
const id = (data as { instance_id?: unknown }).instance_id;
|
|
41
|
+
return typeof id === "string" && id.trim().length > 0 ? id.trim() : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function metadata(model: Model<Api>): NonNullable<LmStudioModelMetadata["clio"]> | undefined {
|
|
45
|
+
return (model as Model<Api> & LmStudioModelMetadata).clio;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function targetForModel(model: Model<"openai-completions">): TargetDescriptor | null {
|
|
49
|
+
const info = metadata(model);
|
|
50
|
+
if (info?.runtimeId !== "lmstudio" || !model.baseUrl) return null;
|
|
51
|
+
return {
|
|
52
|
+
id: info.targetId,
|
|
53
|
+
runtime: "lmstudio",
|
|
54
|
+
url: lmStudioRootUrl(model.baseUrl),
|
|
55
|
+
defaultModel: info.lmstudioDefaultModel ?? model.id,
|
|
56
|
+
...(model.headers ? { auth: { headers: model.headers } } : {}),
|
|
57
|
+
...(info.lifecycle ? { lifecycle: info.lifecycle } : {}),
|
|
58
|
+
...(info.lmstudio ? { lmstudio: info.lmstudio } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveModel(
|
|
63
|
+
catalog: LmStudioCatalog,
|
|
64
|
+
id: string,
|
|
65
|
+
): { model: LmStudioModelInfo; instance?: LmStudioLoadedInstance } | null {
|
|
66
|
+
for (const model of catalog.models) {
|
|
67
|
+
if (model.key === id) return { model, ...(model.loadedInstances[0] ? { instance: model.loadedInstances[0] } : {}) };
|
|
68
|
+
const instance = model.loadedInstances.find((entry) => entry.id === id);
|
|
69
|
+
if (instance) return { model, instance };
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function lmStudioLoadBody(
|
|
75
|
+
modelKey: string,
|
|
76
|
+
settings: NonNullable<NonNullable<TargetDescriptor["lmstudio"]>["load"]>,
|
|
77
|
+
): Record<string, unknown> {
|
|
78
|
+
const body: Record<string, unknown> = { model: modelKey, echo_load_config: true };
|
|
79
|
+
if (settings.contextLength !== undefined) body.context_length = settings.contextLength;
|
|
80
|
+
if (settings.flashAttention !== undefined) body.flash_attention = settings.flashAttention;
|
|
81
|
+
if (settings.evalBatchSize !== undefined) body.eval_batch_size = settings.evalBatchSize;
|
|
82
|
+
if (settings.numExperts !== undefined) body.num_experts = settings.numExperts;
|
|
83
|
+
if (settings.offloadKvCacheToGpu !== undefined) body.offload_kv_cache_to_gpu = settings.offloadKvCacheToGpu;
|
|
84
|
+
return body;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function post(
|
|
88
|
+
target: TargetDescriptor,
|
|
89
|
+
path: string,
|
|
90
|
+
body: Record<string, unknown>,
|
|
91
|
+
apiKey: string | undefined,
|
|
92
|
+
signal: AbortSignal | undefined,
|
|
93
|
+
): Promise<unknown> {
|
|
94
|
+
const headers: Record<string, string> = { "content-type": "application/json", ...(target.auth?.headers ?? {}) };
|
|
95
|
+
if (apiKey?.trim()) headers.authorization = `Bearer ${apiKey.trim()}`;
|
|
96
|
+
const response = await requestLmStudioJson(
|
|
97
|
+
`${lmStudioRootUrl(target.url ?? "")}${path}`,
|
|
98
|
+
{ method: "POST", headers, body: JSON.stringify(body) },
|
|
99
|
+
5_000,
|
|
100
|
+
signal,
|
|
101
|
+
);
|
|
102
|
+
if (!response.ok) throw new Error(response.error ?? `LM Studio ${path} returned HTTP ${response.status}`);
|
|
103
|
+
return response.data;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function unloadOwnedInstance(
|
|
107
|
+
target: TargetDescriptor,
|
|
108
|
+
targetKey: string,
|
|
109
|
+
instanceId: string,
|
|
110
|
+
apiKey: string | undefined,
|
|
111
|
+
signal: AbortSignal | undefined,
|
|
112
|
+
): Promise<boolean> {
|
|
113
|
+
const owned = ownedInstances(targetKey);
|
|
114
|
+
if (!owned.has(instanceId)) return false;
|
|
115
|
+
await post(target, "/api/v1/models/unload", { instance_id: instanceId }, apiKey, signal);
|
|
116
|
+
owned.delete(instanceId);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function loadOwnedInstance(
|
|
121
|
+
target: TargetDescriptor,
|
|
122
|
+
targetKey: string,
|
|
123
|
+
body: Record<string, unknown>,
|
|
124
|
+
apiKey: string | undefined,
|
|
125
|
+
signal: AbortSignal | undefined,
|
|
126
|
+
): Promise<string | undefined> {
|
|
127
|
+
const data = await post(target, "/api/v1/models/load", body, apiKey, signal);
|
|
128
|
+
const instanceId = responseInstanceId(data);
|
|
129
|
+
if (instanceId) ownedInstances(targetKey).add(instanceId);
|
|
130
|
+
return instanceId;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function ensureLmStudioResidency(
|
|
134
|
+
model: Model<"openai-completions">,
|
|
135
|
+
options: { apiKey?: string; signal?: AbortSignal } = {},
|
|
136
|
+
): Promise<string> {
|
|
137
|
+
const target = targetForModel(model);
|
|
138
|
+
if (!target) return model.id;
|
|
139
|
+
const info = metadata(model);
|
|
140
|
+
if (!info) return model.id;
|
|
141
|
+
const load = target.lmstudio?.load;
|
|
142
|
+
const ctx = {
|
|
143
|
+
credentialsPresent: new Set<string>(),
|
|
144
|
+
httpTimeoutMs: 5_000,
|
|
145
|
+
...(options.apiKey ? { authToken: options.apiKey } : {}),
|
|
146
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
147
|
+
};
|
|
148
|
+
const catalog = await listLmStudioModels(target, ctx);
|
|
149
|
+
if (!catalog.ok) throw new Error(catalog.error ?? "LM Studio model listing failed");
|
|
150
|
+
const resolution = resolveLmStudioInstance(target, catalog.models, model.id, info.lmstudioDefaultModel);
|
|
151
|
+
if (resolution.state === "unknown") {
|
|
152
|
+
const resident = catalog.models.flatMap((entry) => entry.loadedInstances.map((instance) => instance.id));
|
|
153
|
+
throw new Error(
|
|
154
|
+
`LM Studio target '${info.targetId}' does not advertise model '${model.id}'. Resident instances: ${resident.length > 0 ? resident.join(", ") : "none"}. Configure an explicit LM Studio load before requesting an unlisted model.`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
if (resolution.wireModelId !== model.id) {
|
|
158
|
+
emitResidencyNotice({
|
|
159
|
+
kind: "co-resident",
|
|
160
|
+
level: "info",
|
|
161
|
+
targetId: info.targetId,
|
|
162
|
+
runtimeId: "lmstudio",
|
|
163
|
+
model: model.id,
|
|
164
|
+
message: `LM Studio resolved '${model.id}' to loaded instance '${resolution.wireModelId}' on target '${info.targetId}'.`,
|
|
165
|
+
detail: { requestedModel: model.id, wireModel: resolution.wireModelId },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (resolution.peerTargets.length > 0) {
|
|
169
|
+
emitResidencyNotice({
|
|
170
|
+
kind: "co-resident",
|
|
171
|
+
level: "warning",
|
|
172
|
+
targetId: info.targetId,
|
|
173
|
+
runtimeId: "lmstudio",
|
|
174
|
+
model: resolution.wireModelId,
|
|
175
|
+
message: `LM Studio instance '${resolution.wireModelId}' is also loaded on ${resolution.peerTargets.join(", ")}; this request may be served by that LM Link peer.`,
|
|
176
|
+
detail: { peerTargets: resolution.peerTargets.join(", ") },
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (!load || Object.keys(load).length === 0 || resolution.instance) return resolution.wireModelId;
|
|
180
|
+
if (catalog.tier !== "0.4+") return resolution.wireModelId;
|
|
181
|
+
const selected = resolveModel(catalog, model.id);
|
|
182
|
+
const modelKey = selected?.model.key ?? model.id;
|
|
183
|
+
let instances = catalog.models.flatMap((entry) =>
|
|
184
|
+
entry.loadedInstances.map((instance) => ({ modelKey: entry.key, identifier: instance.id, instance })),
|
|
185
|
+
);
|
|
186
|
+
const managed = residencyManagedFor(info.lifecycle);
|
|
187
|
+
const targetKey = `lmstudio|${lmStudioRootUrl(target.url ?? model.baseUrl)}`;
|
|
188
|
+
const contextLength = target.lmstudio?.load?.contextLength;
|
|
189
|
+
const plan = await reconcileResidency({
|
|
190
|
+
targetKey,
|
|
191
|
+
targetId: info.targetId,
|
|
192
|
+
runtimeId: "lmstudio",
|
|
193
|
+
keepModelId: modelKey,
|
|
194
|
+
managed,
|
|
195
|
+
strategy: "jit",
|
|
196
|
+
...(contextLength !== undefined ? { contextLength } : {}),
|
|
197
|
+
...(model.contextWindow > 0 ? { modelMaxContext: model.contextWindow } : {}),
|
|
198
|
+
listResident: async () => [...new Set(instances.map((entry) => entry.modelKey))].map((modelId) => ({ modelId })),
|
|
199
|
+
unload: async (id) => {
|
|
200
|
+
const released = new Set<string>();
|
|
201
|
+
for (const entry of instances.filter((resident) => resident.modelKey === id)) {
|
|
202
|
+
if (await unloadOwnedInstance(target, targetKey, entry.identifier, options.apiKey, options.signal)) {
|
|
203
|
+
released.add(entry.identifier);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
instances = instances.filter((resident) => !released.has(resident.identifier));
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
let body = lmStudioLoadBody(modelKey, load);
|
|
210
|
+
if (load.contextLength !== undefined) {
|
|
211
|
+
const fit = fitLoadContextLength({
|
|
212
|
+
requested: load.contextLength,
|
|
213
|
+
resident: instances,
|
|
214
|
+
keepModelId: modelKey,
|
|
215
|
+
ceiling: coResidentContextCeiling(),
|
|
216
|
+
});
|
|
217
|
+
body = { ...body, context_length: fit.contextLength };
|
|
218
|
+
if (fit.clampedFrom !== undefined) {
|
|
219
|
+
emitResidencyNotice({
|
|
220
|
+
kind: "stress",
|
|
221
|
+
level: "warning",
|
|
222
|
+
targetId: info.targetId,
|
|
223
|
+
runtimeId: "lmstudio",
|
|
224
|
+
model: model.id,
|
|
225
|
+
message: `loading '${model.id}' alongside ${fit.neighbours.join(", ")}: context clamped ${fit.clampedFrom} to ${fit.contextLength} tokens`,
|
|
226
|
+
detail: { requestedContext: fit.clampedFrom, loadContext: fit.contextLength },
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
return (await loadOwnedInstance(target, targetKey, body, options.apiKey, options.signal)) ?? model.id;
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (plan.decision === "observe" || plan.fallbackEvict.length === 0) throw error;
|
|
234
|
+
return withResidencyLock(targetKey, async () => {
|
|
235
|
+
for (const candidate of plan.fallbackEvict) {
|
|
236
|
+
for (const entry of instances.filter((resident) => resident.modelKey === candidate.modelId)) {
|
|
237
|
+
await unloadOwnedInstance(target, targetKey, entry.identifier, options.apiKey, options.signal);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return (await loadOwnedInstance(target, targetKey, body, options.apiKey, options.signal)) ?? model.id;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|