@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.
Files changed (601) hide show
  1. package/CHANGELOG.md +233 -373
  2. package/CONTRIBUTING.md +23 -23
  3. package/README.md +284 -613
  4. package/dist/{acp-FPR54DGL.js → acp-P2AQILE2.js} +43 -53
  5. package/dist/{agents-OGPIHPJH.js → agents-72W3BI7I.js} +43 -26
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-IC3K6NIZ.js → auth-5TWEIYDN.js} +20 -12
  8. package/dist/chunk-2DJ2KNFG.js +2095 -0
  9. package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
  10. package/dist/chunk-2SFS6XQE.js +122 -0
  11. package/dist/{chunk-PV4JUBVJ.js → chunk-2TLUCQVG.js} +40 -21
  12. package/dist/chunk-2VTFPG5O.js +48 -0
  13. package/dist/chunk-4BJ5BYCE.js +61 -0
  14. package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
  15. package/dist/chunk-4VP4KH3K.js +962 -0
  16. package/dist/chunk-4XUGQOHA.js +797 -0
  17. package/dist/chunk-4ZG3XFUR.js +77 -0
  18. package/dist/chunk-5B2AEOW5.js +5407 -0
  19. package/dist/{chunk-K2ITRMHZ.js → chunk-5TSRNF4G.js} +6 -138
  20. package/dist/{chunk-4QKXUHSR.js → chunk-5UFT4SUX.js} +70 -20
  21. package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
  22. package/dist/chunk-65DEGPJ6.js +52 -0
  23. package/dist/chunk-6EJMN2Y3.js +17 -0
  24. package/dist/chunk-6N5PTWMY.js +136 -0
  25. package/dist/chunk-6SGHMWE3.js +277 -0
  26. package/dist/chunk-6XLNIQDB.js +27 -0
  27. package/dist/chunk-7CR24IG7.js +242 -0
  28. package/dist/chunk-7MNJORFF.js +22 -0
  29. package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
  30. package/dist/chunk-AGYYIBLL.js +1069 -0
  31. package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
  32. package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
  33. package/dist/chunk-CBCAPZAA.js +229 -0
  34. package/dist/chunk-CMZWFGD2.js +352 -0
  35. package/dist/chunk-COU2UHX6.js +400 -0
  36. package/dist/chunk-DSELYM6W.js +1077 -0
  37. package/dist/chunk-DUYJ5IO6.js +644 -0
  38. package/dist/chunk-ECH6PKUQ.js +39 -0
  39. package/dist/chunk-ED4KHGC3.js +143 -0
  40. package/dist/chunk-EKMEHE4H.js +340 -0
  41. package/dist/chunk-FCSXB6T2.js +338 -0
  42. package/dist/chunk-FJ3H4MN5.js +48 -0
  43. package/dist/{chunk-RPTR2H26.js → chunk-FNTMWMX5.js} +21 -15
  44. package/dist/chunk-FQ4SKYE4.js +29 -0
  45. package/dist/chunk-G4BMMOKF.js +182 -0
  46. package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
  47. package/dist/chunk-HC4CLZ2Y.js +68 -0
  48. package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
  49. package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
  50. package/dist/chunk-I4HZDVNP.js +73 -0
  51. package/dist/chunk-IKCO5N3L.js +162 -0
  52. package/dist/chunk-IR4CFBFN.js +56 -0
  53. package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
  54. package/dist/chunk-J7CWMCQD.js +255 -0
  55. package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
  56. package/dist/chunk-KZWTDYJF.js +217 -0
  57. package/dist/chunk-LBMZMYH2.js +285 -0
  58. package/dist/{chunk-G34LV2PF.js → chunk-LM5TQCJZ.js} +84 -170
  59. package/dist/chunk-LW6DSM3M.js +5135 -0
  60. package/dist/chunk-LWLEKMDQ.js +3482 -0
  61. package/dist/{chunk-H6F6BYOH.js → chunk-LZSJBIVT.js} +7003 -7434
  62. package/dist/{chunk-HQQID6OA.js → chunk-M6SHUN7Q.js} +5 -5
  63. package/dist/{chunk-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
  64. package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
  65. package/dist/chunk-OAO4GE4M.js +619 -0
  66. package/dist/{chunk-Q3RUPKEJ.js → chunk-OC7FIQPC.js} +58 -189
  67. package/dist/chunk-OKGUZO2U.js +34 -0
  68. package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
  69. package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
  70. package/dist/chunk-OQE5J4C6.js +73 -0
  71. package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
  72. package/dist/{chunk-MAR7Y6HW.js → chunk-PAJK6MAQ.js} +23 -16
  73. package/dist/{chunk-M5T5VO65.js → chunk-PIWWS5BL.js} +837 -635
  74. package/dist/chunk-POHLU5DW.js +1186 -0
  75. package/dist/chunk-QKMUKYO7.js +4961 -0
  76. package/dist/chunk-SRDMMSEP.js +16405 -0
  77. package/dist/chunk-SST6Z5JA.js +80 -0
  78. package/dist/chunk-STBPMHSX.js +2456 -0
  79. package/dist/chunk-T6YILFSB.js +80 -0
  80. package/dist/chunk-TZK7PACC.js +174 -0
  81. package/dist/chunk-TZTZS7QK.js +227 -0
  82. package/dist/{chunk-ASND7OZK.js → chunk-UFIIWP2H.js} +13 -13
  83. package/dist/chunk-UOV2BYIW.js +107 -0
  84. package/dist/{chunk-PFEFKVGL.js → chunk-V6RTAOC2.js} +13 -11
  85. package/dist/chunk-VAKQQHWR.js +434 -0
  86. package/dist/chunk-VG7TBQIY.js +128 -0
  87. package/dist/chunk-VJWL6YS5.js +244 -0
  88. package/dist/{chunk-EYOKLTMF.js → chunk-VPAYEGVX.js} +17 -3
  89. package/dist/chunk-WEH5XRJQ.js +32 -0
  90. package/dist/chunk-X4RCMKVQ.js +641 -0
  91. package/dist/{chunk-TEKV33Q5.js → chunk-X6IAEBZR.js} +65 -33
  92. package/dist/chunk-XBXAASKX.js +18 -0
  93. package/dist/chunk-XN3L4EYL.js +46 -0
  94. package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
  95. package/dist/chunk-YHZX5GEU.js +193 -0
  96. package/dist/chunk-YXLYO42X.js +91 -0
  97. package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
  98. package/dist/chunk-ZI647VB5.js +37 -0
  99. package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
  100. package/dist/chunk-ZWMF7253.js +1882 -0
  101. package/dist/cli/index.js +62 -54
  102. package/dist/clio-JOU4FXVA.js +25 -0
  103. package/dist/code-nav-7AX6FYE6.js +600 -0
  104. package/dist/codewiki/build-worker.js +66 -0
  105. package/dist/compile-cache-CVJMMODC.js +18 -0
  106. package/dist/{components-DMAOEKFB.js → components-KELWS457.js} +11 -6
  107. package/dist/{config-IRUQ7SE4.js → config-XCDVKR23.js} +92 -55
  108. package/dist/configure-4GAP54ZW.js +42 -0
  109. package/dist/{context-5RADCKTR.js → context-4UOGGLQ5.js} +71 -35
  110. package/dist/context-5VKGUVJJ.js +866 -0
  111. package/dist/{context-3KWFLHJG.js → context-77FM5DV5.js} +15 -13
  112. package/dist/{context-clear-7TSNPAAI.js → context-clear-XXJRLCJJ.js} +54 -28
  113. package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
  114. package/dist/dispatch-runner-QPRDDBDX.js +1997 -0
  115. package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
  116. package/dist/{doctor-UC5NAJYQ.js → doctor-HR46URBJ.js} +27 -17
  117. package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
  118. package/dist/{evidence-YEGUW4L3.js → evidence-6HG2PY2B.js} +46 -26
  119. package/dist/{evolve-TXARCTPG.js → evolve-K7YU3NCY.js} +45 -25
  120. package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
  121. package/dist/{fleet-6G3DHNYE.js → fleet-VY3HHKN6.js} +163 -54
  122. package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
  123. package/dist/{init-KZ5QTF6M.js → init-JYGXI3FK.js} +69 -32
  124. package/dist/{memory-73ESV5YC.js → memory-WFZMGYHX.js} +48 -27
  125. package/dist/{models-A4PVNWJK.js → models-I5QWSEOM.js} +39 -25
  126. package/dist/monitor-GE4ID3IA.js +661 -0
  127. package/dist/{chunk-FCIH3BIZ.js → orchestrator-EM5MC3HM.js} +15979 -12407
  128. package/dist/{paths-C4H6IV77.js → paths-UXLN5YYZ.js} +10 -5
  129. package/dist/{preload-6WVMHX3A.js → preload-P6DGH2PZ.js} +2 -2
  130. package/dist/{reset-BGW6OGMV.js → reset-L2FQEE3E.js} +16 -10
  131. package/dist/{run-YTPEYQOH.js → run-ZU3QMZPZ.js} +101 -61
  132. package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
  133. package/dist/{skills-2V6RA3OQ.js → skills-X5VXCRNQ.js} +34 -14
  134. package/dist/{skills-eval-S2TVJO4F.js → skills-eval-WKIHWTHR.js} +70 -34
  135. package/dist/steer-GGWFUJUD.js +77 -0
  136. package/dist/{targets-TYXLPB23.js → targets-SNCPI2NR.js} +43 -27
  137. package/dist/terminal-lease-BNAHVHBS.js +395 -0
  138. package/dist/{trace-GGOJ6Q6Z.js → trace-PNCASAXC.js} +41 -16
  139. package/dist/{chunk-N6F52NLF.js → tree-sitter-HGKH6LG4.js} +28 -2306
  140. package/dist/{uninstall-LLLT4F4W.js → uninstall-FZCQCDKC.js} +10 -5
  141. package/dist/{upgrade-33G2LMM5.js → upgrade-JQHHPQ4K.js} +45 -25
  142. package/dist/{usage-ZAFSXKKG.js → usage-OR4O5SMZ.js} +62 -31
  143. package/dist/verify-375KUB3Y.js +716 -0
  144. package/dist/web-fetch-2YHJ3KTG.js +638 -0
  145. package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-UEXP2ARI.js} +74 -34
  146. package/dist/worker/entry.js +221 -36
  147. package/dist/workspace-G4ZWUIPR.js +22 -0
  148. package/docs/README.md +22 -17
  149. package/docs/acp.md +168 -16
  150. package/docs/alcf-provider.md +1 -1
  151. package/docs/architecture.md +136 -7
  152. package/docs/artifact-versions.md +1 -1
  153. package/docs/built-in-agents.md +1 -1
  154. package/docs/capacity-and-scheduling.md +1 -1
  155. package/docs/commands-and-modes.md +114 -71
  156. package/docs/config-knobs-audit.md +1 -3
  157. package/docs/configuration-and-targets.md +174 -46
  158. package/docs/context-engine.md +29 -6
  159. package/docs/development-pipeline.md +26 -1
  160. package/docs/dispatch-architecture-rationale.md +1 -1
  161. package/docs/documentation-coverage.md +2 -2
  162. package/docs/documentation-guide.md +1 -1
  163. package/docs/environment-variables.md +13 -5
  164. package/docs/eval-runner.md +1 -1
  165. package/docs/evals-internal.md +1 -1
  166. package/docs/evidence-and-memory.md +6 -2
  167. package/docs/evolution.md +2 -2
  168. package/docs/exit-codes-and-output.md +15 -9
  169. package/docs/extensions-and-sharing.md +9 -9
  170. package/docs/fleet-dispatch.md +7 -5
  171. package/docs/git-commit-provenance.md +120 -0
  172. package/docs/glossary.md +1 -1
  173. package/docs/installation-and-lifecycle.md +34 -27
  174. package/docs/middleware-and-components.md +1 -1
  175. package/docs/model-catalog.md +45 -14
  176. package/docs/observability.md +8 -5
  177. package/docs/performance-methodology.md +491 -0
  178. package/docs/pi-boundary.md +72 -0
  179. package/docs/proactive-memory.md +3 -3
  180. package/docs/prompt-envelope-and-tools.md +24 -3
  181. package/docs/provider-adapter-cookbook.md +57 -4
  182. package/docs/release-cut-checklist.md +129 -115
  183. package/docs/safety-model.md +9 -5
  184. package/docs/scientific-validation.md +3 -3
  185. package/docs/session-lifecycle.md +55 -12
  186. package/docs/skills-marketplace.md +12 -8
  187. package/docs/time-conventions.md +1 -1
  188. package/docs/tool-usage.md +3 -3
  189. package/docs/trace-store.md +1 -1
  190. package/docs/troubleshooting.md +10 -7
  191. package/docs/tui-design.md +47 -10
  192. package/docs/worker-dispatch-mechanics.md +1 -1
  193. package/package.json +19 -22
  194. package/skills/coding/ast-grep/SKILL.md +136 -0
  195. package/skills/coding/ast-grep/evals.md +56 -0
  196. package/skills/coding/ast-grep/references/rule_reference.md +297 -0
  197. package/skills/coding/coding-standards/SKILL.md +113 -0
  198. package/skills/coding/coding-standards/evals.md +34 -0
  199. package/skills/coding/prototype/SKILL.md +86 -0
  200. package/skills/coding/prototype/evals.md +42 -0
  201. package/skills/coding/prototype/references/LOGIC.md +67 -0
  202. package/skills/coding/prototype/references/UI.md +112 -0
  203. package/skills/coding/tdd/SKILL.md +101 -0
  204. package/skills/coding/tdd/evals.md +41 -0
  205. package/skills/coding/tdd/references/mocking.md +59 -0
  206. package/skills/coding/tdd/references/tests.md +77 -0
  207. package/skills/context/context-handoff/SKILL.md +126 -0
  208. package/skills/context/context-handoff/evals.md +57 -0
  209. package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
  210. package/skills/context/context-prime/SKILL.md +95 -0
  211. package/skills/context/context-prime/evals.md +54 -0
  212. package/skills/meta/clio-dev/SKILL.md +91 -0
  213. package/skills/meta/clio-dev/evals.md +45 -0
  214. package/skills/meta/clio-test/SKILL.md +130 -0
  215. package/skills/meta/clio-test/evals.md +43 -0
  216. package/skills/meta/clio-test/references/harness.md +97 -0
  217. package/skills/meta/clio-test/references/test-map.md +59 -0
  218. package/skills/meta/credentials/SKILL.md +125 -0
  219. package/skills/meta/credentials/evals.md +104 -0
  220. package/skills/meta/find-skills/SKILL.md +72 -0
  221. package/skills/meta/find-skills/evals.md +47 -0
  222. package/skills/meta/herdr/SKILL.md +127 -0
  223. package/skills/meta/herdr/evals.md +38 -0
  224. package/skills/meta/skill-craft/SKILL.md +102 -0
  225. package/skills/meta/skill-craft/evals.md +41 -0
  226. package/skills/planning/architecture/SKILL.md +129 -0
  227. package/skills/planning/architecture/evals.md +36 -0
  228. package/skills/planning/backlog/SKILL.md +90 -0
  229. package/skills/planning/backlog/evals.md +43 -0
  230. package/skills/planning/prd/SKILL.md +82 -0
  231. package/skills/planning/prd/evals.md +49 -0
  232. package/skills/planning/product-intent/SKILL.md +112 -0
  233. package/skills/planning/product-intent/evals.md +36 -0
  234. package/skills/planning/tech-spec/SKILL.md +115 -0
  235. package/skills/planning/tech-spec/evals.md +47 -0
  236. package/skills/registry.yaml +136 -0
  237. package/skills/research/arxiv-literature/SKILL.md +104 -0
  238. package/skills/research/arxiv-literature/evals.md +58 -0
  239. package/skills/research/experiment-protocol/SKILL.md +122 -0
  240. package/skills/research/experiment-protocol/evals.md +91 -0
  241. package/skills/research/scientific-debugging/SKILL.md +119 -0
  242. package/skills/research/scientific-debugging/evals.md +138 -0
  243. package/skills/research/scientific-modernization/SKILL.md +138 -0
  244. package/skills/research/scientific-modernization/evals.md +84 -0
  245. package/skills/workflow/design-council/SKILL.md +139 -0
  246. package/skills/workflow/design-council/evals.md +97 -0
  247. package/skills/workflow/grill-me/SKILL.md +186 -0
  248. package/skills/workflow/grill-me/evals.md +78 -0
  249. package/skills/workflow/workflow-distiller/SKILL.md +136 -0
  250. package/skills/workflow/workflow-distiller/evals.md +107 -0
  251. package/src/cli/acp.ts +31 -4
  252. package/src/cli/clio.ts +68 -6
  253. package/src/cli/config-inspect.ts +28 -22
  254. package/src/cli/configure.ts +47 -9
  255. package/src/cli/context-clear.ts +2 -2
  256. package/src/cli/context-index.ts +21 -23
  257. package/src/cli/context.ts +13 -8
  258. package/src/cli/default-target.ts +9 -17
  259. package/src/cli/docs.ts +11 -5
  260. package/src/cli/evidence.ts +4 -1
  261. package/src/cli/extensions.ts +10 -1
  262. package/src/cli/fleet.ts +47 -6
  263. package/src/cli/index.ts +55 -26
  264. package/src/cli/memory.ts +3 -1
  265. package/src/cli/models.ts +1 -1
  266. package/src/cli/modes/json-stream.ts +37 -1
  267. package/src/cli/modes/print.ts +24 -9
  268. package/src/cli/run.ts +2 -2
  269. package/src/cli/skills-eval.ts +23 -8
  270. package/src/cli/skills.ts +19 -4
  271. package/src/cli/targets.ts +4 -0
  272. package/src/cli/text-layout.ts +15 -5
  273. package/src/cli/trace.ts +62 -14
  274. package/src/cli/upgrade.ts +18 -2
  275. package/src/cli/usage.ts +10 -3
  276. package/src/cli/wiki-generate.ts +2 -1
  277. package/src/core/agent-environment.ts +7 -0
  278. package/src/core/bash-exec.ts +72 -1
  279. package/src/core/boot-trace.ts +9 -4
  280. package/src/core/bus-events.ts +20 -4
  281. package/src/core/commit-attribution.ts +157 -0
  282. package/src/core/compile-cache.ts +159 -0
  283. package/src/core/config.ts +131 -2
  284. package/src/core/defaults.ts +39 -5
  285. package/src/core/domain-loader.ts +12 -5
  286. package/src/core/git-commit-attribution.ts +387 -0
  287. package/src/core/incomplete-installation.ts +45 -0
  288. package/src/core/response-schema.ts +1 -1
  289. package/src/core/safe-exec.ts +13 -1
  290. package/src/core/settings-layers.ts +155 -21
  291. package/src/core/skill-activation.ts +1 -1
  292. package/src/core/startup-timer.ts +3 -3
  293. package/src/core/state-file-lock.ts +13 -1
  294. package/src/core/termination.ts +78 -5
  295. package/src/domains/config/classify.ts +15 -3
  296. package/src/domains/config/extension.ts +19 -13
  297. package/src/domains/config/index.ts +10 -0
  298. package/src/domains/config/keybindings.ts +45 -9
  299. package/src/domains/context/bootstrap-prompt.ts +1 -1
  300. package/src/domains/context/bootstrap.ts +111 -18
  301. package/src/domains/context/clear.ts +16 -11
  302. package/src/domains/context/clio-md.ts +111 -9
  303. package/src/domains/context/codewiki/artifact.ts +400 -0
  304. package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
  305. package/src/domains/context/codewiki/build-worker.ts +54 -0
  306. package/src/domains/context/codewiki/coordinator.ts +182 -0
  307. package/src/domains/context/codewiki/indexer.ts +59 -144
  308. package/src/domains/context/codewiki/paths.ts +67 -0
  309. package/src/domains/context/codewiki/schema.ts +80 -0
  310. package/src/domains/context/codewiki/tree-sitter.ts +1 -1
  311. package/src/domains/context/contract.ts +11 -5
  312. package/src/domains/context/extension.ts +94 -143
  313. package/src/domains/context/fingerprint.ts +3 -1
  314. package/src/domains/context/index.ts +12 -22
  315. package/src/domains/context/project-metadata.ts +19 -0
  316. package/src/domains/context/prompt-context.ts +9 -10
  317. package/src/domains/context/refresh.ts +29 -21
  318. package/src/domains/context/runtime.ts +17 -0
  319. package/src/domains/context/wiki/generate.ts +39 -34
  320. package/src/domains/context/wiki/plan.ts +1 -1
  321. package/src/domains/context/wiki/prompts.ts +21 -8
  322. package/src/domains/dispatch/code-step.ts +20 -1
  323. package/src/domains/dispatch/extension.ts +158 -21
  324. package/src/domains/dispatch/failure-classification.ts +6 -0
  325. package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
  326. package/src/domains/dispatch/orphan-recovery.ts +50 -8
  327. package/src/domains/dispatch/receipt-integrity.ts +5 -0
  328. package/src/domains/dispatch/state.ts +31 -6
  329. package/src/domains/dispatch/transport.ts +2 -1
  330. package/src/domains/dispatch/types.ts +14 -0
  331. package/src/domains/dispatch/worker-spawn.ts +21 -2
  332. package/src/domains/eval/metrics/context.ts +1 -1
  333. package/src/domains/eval/types.ts +0 -1
  334. package/src/domains/evidence/build.ts +41 -1
  335. package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
  336. package/src/domains/lifecycle/migrations/index.ts +24 -4
  337. package/src/domains/middleware/hooks-io.ts +12 -0
  338. package/src/domains/middleware/skills-reminder.ts +30 -15
  339. package/src/domains/prompts/compiler.ts +142 -84
  340. package/src/domains/prompts/contract.ts +18 -2
  341. package/src/domains/prompts/extension.ts +39 -7
  342. package/src/domains/prompts/fragment-loader.ts +0 -1
  343. package/src/domains/prompts/fragments/identity/clio.md +2 -4
  344. package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
  345. package/src/domains/prompts/fragments/identity/self-awareness.md +1 -45
  346. package/src/domains/prompts/fragments/operating/contract.md +4 -50
  347. package/src/domains/prompts/fragments/operating/delegation.md +42 -0
  348. package/src/domains/prompts/fragments/operating/skills.md +26 -0
  349. package/src/domains/prompts/fragments/operating/worker.md +16 -0
  350. package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
  351. package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
  352. package/src/domains/prompts/fragments/safety/read-only.md +4 -4
  353. package/src/domains/prompts/fragments/safety/suggest.md +2 -2
  354. package/src/domains/prompts/fragments/wiki/page.md +10 -0
  355. package/src/domains/prompts/fragments/wiki/plan.md +10 -0
  356. package/src/domains/prompts/preload.ts +3 -3
  357. package/src/domains/providers/auth/api-key.ts +1 -1
  358. package/src/domains/providers/auth/backend-file.ts +20 -10
  359. package/src/domains/providers/auth/backend-memory.ts +59 -4
  360. package/src/domains/providers/auth/boot-status.ts +65 -0
  361. package/src/domains/providers/auth/oauth.ts +2 -1
  362. package/src/domains/providers/auth/storage.ts +97 -38
  363. package/src/domains/providers/capabilities.ts +12 -4
  364. package/src/domains/providers/contract.ts +15 -4
  365. package/src/domains/providers/extension.ts +18 -6
  366. package/src/domains/providers/model-runtime-capabilities.ts +15 -4
  367. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +118 -35
  368. package/src/domains/providers/plugins.ts +5 -3
  369. package/src/domains/providers/probe/fingerprint.ts +25 -5
  370. package/src/domains/providers/registry.ts +31 -10
  371. package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
  372. package/src/domains/providers/runtimes/builtins.ts +2 -2
  373. package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
  374. package/src/domains/providers/runtimes/common/local-synth.ts +6 -7
  375. package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
  376. package/src/domains/providers/support.ts +6 -3
  377. package/src/domains/providers/types/local-model-quirks.ts +7 -9
  378. package/src/domains/providers/types/runtime-descriptor.ts +12 -1
  379. package/src/domains/providers/types/target-descriptor.ts +22 -0
  380. package/src/domains/resources/contract.ts +0 -1
  381. package/src/domains/resources/extension.ts +1 -3
  382. package/src/domains/resources/loader.ts +3 -4
  383. package/src/domains/resources/prompts/loader.ts +16 -2
  384. package/src/domains/resources/prompts/substitute.ts +1 -65
  385. package/src/domains/resources/skills/content-hash.ts +2 -0
  386. package/src/domains/resources/skills/install.ts +17 -0
  387. package/src/domains/resources/skills/loader.ts +17 -10
  388. package/src/domains/resources/skills/marketplace.ts +55 -9
  389. package/src/domains/safety/action-classifier.ts +4 -2
  390. package/src/domains/safety/audit.ts +8 -2
  391. package/src/domains/safety/extension.ts +1 -1
  392. package/src/domains/session/compaction/branch-summary.ts +3 -2
  393. package/src/domains/session/compaction/cut-point.ts +2 -1
  394. package/src/domains/session/compaction/tokens.ts +2 -1
  395. package/src/domains/session/context-ledger.ts +14 -0
  396. package/src/domains/session/contract.ts +15 -0
  397. package/src/domains/session/decision-board.ts +190 -0
  398. package/src/domains/session/entries.ts +66 -3
  399. package/src/domains/session/extension.ts +93 -12
  400. package/src/domains/session/retry.ts +10 -18
  401. package/src/domains/session/session-artifacts.ts +107 -0
  402. package/src/domains/session/task-board.ts +207 -13
  403. package/src/domains/session/tree/active-path.ts +44 -5
  404. package/src/domains/session/tree/fork.ts +26 -27
  405. package/src/domains/session/tree/preview.ts +2 -2
  406. package/src/domains/session/workspace/git-probe.ts +17 -11
  407. package/src/domains/user-tasks/store.ts +297 -0
  408. package/src/engine/acp/errors.ts +96 -0
  409. package/src/engine/acp/server.ts +1728 -146
  410. package/src/engine/acp/transport.ts +135 -14
  411. package/src/engine/acp/types.ts +26 -0
  412. package/src/engine/agent.ts +3 -3
  413. package/src/engine/ai.ts +32 -27
  414. package/src/engine/alcf-oauth.ts +26 -19
  415. package/src/engine/api-registry.ts +223 -0
  416. package/src/engine/apis/index.ts +3 -7
  417. package/src/engine/apis/llamacpp-residency.ts +49 -9
  418. package/src/engine/apis/lmstudio-residency.ts +5 -21
  419. package/src/engine/apis/lmstudio.ts +243 -0
  420. package/src/engine/apis/ollama-native.ts +24 -3
  421. package/src/engine/apis/openai-completions.ts +170 -91
  422. package/src/engine/apis/residency.ts +139 -3
  423. package/src/engine/apis/types.ts +16 -0
  424. package/src/engine/env-api-keys.ts +98 -0
  425. package/src/engine/gemma-channel-filter.ts +223 -0
  426. package/src/engine/instrumented-tui.ts +192 -0
  427. package/src/engine/messages.ts +14 -0
  428. package/src/engine/models.ts +42 -0
  429. package/src/engine/oauth.ts +16 -12
  430. package/src/engine/prompt-templates.ts +1 -0
  431. package/src/engine/provider-payload.ts +16 -59
  432. package/src/engine/strip-tokenizer-sentinels.ts +1 -1
  433. package/src/engine/truncate.ts +9 -0
  434. package/src/engine/tui.ts +17 -9
  435. package/src/engine/types.ts +3 -6
  436. package/src/engine/worker-runtime-capabilities.ts +5 -0
  437. package/src/engine/worker-runtime.ts +1 -1
  438. package/src/engine/worker-tools.ts +9 -4
  439. package/src/entry/boot-options.ts +50 -0
  440. package/src/entry/orchestrator.ts +288 -150
  441. package/src/interactive/application-controller.ts +89 -2
  442. package/src/interactive/chat-loop.ts +266 -41
  443. package/src/interactive/chat-panel.ts +715 -278
  444. package/src/interactive/chat-renderer.ts +306 -85
  445. package/src/interactive/clio-editor.ts +3 -8
  446. package/src/interactive/command-fallbacks.ts +2 -2
  447. package/src/interactive/context-overlay.ts +27 -1
  448. package/src/interactive/editor-submit.ts +253 -24
  449. package/src/interactive/export-html/ansi-to-html.ts +161 -0
  450. package/src/interactive/export-html/index.ts +51 -0
  451. package/src/interactive/export-html/template.ts +45 -0
  452. package/src/interactive/export-html/tool-renderer.ts +54 -0
  453. package/src/interactive/footer/dashboard.ts +4 -0
  454. package/src/interactive/footer/notifications.ts +1 -1
  455. package/src/interactive/footer/widgets.ts +42 -22
  456. package/src/interactive/footer-panel.ts +8 -3
  457. package/src/interactive/format-time.ts +14 -2
  458. package/src/interactive/interactive-application.ts +203 -17
  459. package/src/interactive/interactive-event-projection.ts +18 -1
  460. package/src/interactive/interactive-input-runtime.ts +50 -4
  461. package/src/interactive/interactive-presentation.ts +151 -19
  462. package/src/interactive/interactive-shell.ts +268 -14
  463. package/src/interactive/interactive-slash-runtime.ts +184 -117
  464. package/src/interactive/interactive-tickers.ts +38 -7
  465. package/src/interactive/keybinding-manager.ts +1 -1
  466. package/src/interactive/layout.ts +40 -3
  467. package/src/interactive/overlay-frame.ts +1 -1
  468. package/src/interactive/overlay-general-openers.ts +58 -1
  469. package/src/interactive/overlay-key-routing.ts +3 -0
  470. package/src/interactive/overlay-lifecycle.ts +13 -0
  471. package/src/interactive/overlay-permission-lifecycle.ts +2 -1
  472. package/src/interactive/overlay-session-lifecycle.ts +69 -12
  473. package/src/interactive/overlays/ask-user.ts +146 -24
  474. package/src/interactive/overlays/decisions.ts +300 -0
  475. package/src/interactive/overlays/help-reference.ts +15 -10
  476. package/src/interactive/overlays/model-selector.ts +34 -16
  477. package/src/interactive/overlays/session-selector.ts +18 -0
  478. package/src/interactive/overlays/settings.ts +105 -17
  479. package/src/interactive/overlays/skills-hub.ts +4 -4
  480. package/src/interactive/overlays/tree-selector.ts +41 -6
  481. package/src/interactive/render-trace.ts +499 -90
  482. package/src/interactive/renderers/compaction-summary.ts +2 -2
  483. package/src/interactive/renderers/diff.ts +115 -104
  484. package/src/interactive/renderers/mermaid.ts +53 -0
  485. package/src/interactive/renderers/tool-execution.ts +516 -168
  486. package/src/interactive/renderers/worker-entry.ts +20 -4
  487. package/src/interactive/session-switch-settlement.ts +10 -0
  488. package/src/interactive/slash-autocomplete.ts +6 -114
  489. package/src/interactive/slash-commands.ts +135 -47
  490. package/src/interactive/slash-spec.ts +9 -38
  491. package/src/interactive/status/controller.ts +5 -1
  492. package/src/interactive/status/index.ts +12 -1
  493. package/src/interactive/status/reasoning.ts +87 -0
  494. package/src/interactive/status/summary.ts +13 -2
  495. package/src/interactive/stdout-backpressure.ts +99 -0
  496. package/src/interactive/stream-pacer.ts +530 -0
  497. package/src/interactive/stream-pacing-policy.ts +66 -0
  498. package/src/interactive/tasks-overlay.ts +368 -14
  499. package/src/interactive/terminal-lease.ts +485 -0
  500. package/src/interactive/theme/tokens.ts +1 -1
  501. package/src/interactive/transcript-detail.ts +120 -0
  502. package/src/interactive/turn-context.ts +4 -3
  503. package/src/interactive/turn-persistence.ts +30 -13
  504. package/src/interactive/turn-queues.ts +12 -0
  505. package/src/interactive/turn-recovery.ts +25 -8
  506. package/src/interactive/turn-runtime.ts +79 -12
  507. package/src/interactive/turn-state.ts +10 -0
  508. package/src/interactive/view/artifacts.ts +114 -4
  509. package/src/interactive/view/view-overlay.ts +3 -0
  510. package/src/interactive/welcome-dashboard.ts +17 -16
  511. package/src/interactive/worker-receipts.ts +52 -3
  512. package/src/interactive/worker-stream.ts +5 -1
  513. package/src/tools/agent-tools.ts +23 -3
  514. package/src/tools/artifact.ts +2 -2
  515. package/src/tools/ask-user.ts +23 -13
  516. package/src/tools/bash.ts +30 -2
  517. package/src/tools/bootstrap.ts +34 -431
  518. package/src/tools/builtin-tool-catalog.ts +271 -0
  519. package/src/tools/codewiki/code-nav-surface.ts +29 -0
  520. package/src/tools/codewiki/code-nav.ts +8 -22
  521. package/src/tools/codewiki/shared.ts +41 -38
  522. package/src/tools/context/docs-engine.ts +14 -3
  523. package/src/tools/context/index.ts +107 -28
  524. package/src/tools/context/surface.ts +19 -0
  525. package/src/tools/core-bootstrap.ts +168 -0
  526. package/src/tools/credential-present.ts +5 -5
  527. package/src/tools/dispatch-admission.ts +533 -0
  528. package/src/tools/dispatch-background.ts +54 -0
  529. package/src/tools/dispatch-event-text.ts +6 -0
  530. package/src/tools/dispatch-plan.ts +9 -4
  531. package/src/tools/dispatch-run-events.ts +238 -0
  532. package/src/tools/dispatch-runner.ts +2370 -0
  533. package/src/tools/dispatch-scout-admission.ts +295 -0
  534. package/src/tools/dispatch-types.ts +77 -0
  535. package/src/tools/dispatch.ts +67 -3161
  536. package/src/tools/find.ts +4 -2
  537. package/src/tools/grep.ts +2 -2
  538. package/src/tools/lazy-tool.ts +60 -0
  539. package/src/tools/ledger.ts +3 -3
  540. package/src/tools/monitor-surface.ts +36 -0
  541. package/src/tools/monitor.ts +2 -32
  542. package/src/tools/observers.ts +2 -2
  543. package/src/tools/presentation.ts +107 -0
  544. package/src/tools/registry.ts +45 -27
  545. package/src/tools/safe-exec.ts +2 -2
  546. package/src/tools/steer-surface.ts +17 -0
  547. package/src/tools/steer.ts +2 -13
  548. package/src/tools/tasks.ts +108 -11
  549. package/src/tools/truncate.ts +25 -184
  550. package/src/tools/verify/frontend.ts +3 -1
  551. package/src/tools/verify/index.ts +3 -38
  552. package/src/tools/verify/surface.ts +46 -0
  553. package/src/tools/web-fetch-surface.ts +23 -0
  554. package/src/tools/web-fetch.ts +2 -20
  555. package/src/tools/write.ts +7 -2
  556. package/src/worker/entry.ts +39 -2
  557. package/src/worker/spec-contract.ts +26 -5
  558. package/dist/chunk-7SS2CTV2.js +0 -61361
  559. package/dist/chunk-DKGKUHFA.js +0 -924
  560. package/dist/chunk-GEP36Y4X.js +0 -12796
  561. package/dist/chunk-XYWBQRDM.js +0 -137
  562. package/dist/clio-BZVGEUFJ.js +0 -58
  563. package/dist/configure-S7S6F6CL.js +0 -32
  564. package/docs/html/agents_blueprint.html +0 -936
  565. package/docs/html/alcf_blueprint.html +0 -324
  566. package/docs/html/architecture_blueprint.html +0 -850
  567. package/docs/html/commands_blueprint.html +0 -939
  568. package/docs/html/config_knobs_audit_blueprint.html +0 -178
  569. package/docs/html/configuration_blueprint.html +0 -1080
  570. package/docs/html/context_blueprint.html +0 -603
  571. package/docs/html/documentation_blueprint.html +0 -832
  572. package/docs/html/environment_blueprint.html +0 -404
  573. package/docs/html/eval_blueprint.html +0 -743
  574. package/docs/html/evals_internal_blueprint.html +0 -190
  575. package/docs/html/evolution_blueprint.html +0 -674
  576. package/docs/html/extensions_blueprint.html +0 -2065
  577. package/docs/html/fleet_dispatch_blueprint.html +0 -286
  578. package/docs/html/index.html +0 -919
  579. package/docs/html/lifecycle_blueprint.html +0 -723
  580. package/docs/html/memory_blueprint.html +0 -699
  581. package/docs/html/middleware_blueprint.html +0 -664
  582. package/docs/html/models_blueprint.html +0 -2366
  583. package/docs/html/observability_blueprint.html +0 -683
  584. package/docs/html/provider_adapter_blueprint.html +0 -245
  585. package/docs/html/safety_blueprint.html +0 -1386
  586. package/docs/html/shared.css +0 -571
  587. package/docs/html/shared.js +0 -143
  588. package/docs/html/skills_blueprint.html +0 -671
  589. package/docs/html/soak_blueprint.html +0 -182
  590. package/docs/html/tool_usage_blueprint.html +0 -350
  591. package/docs/html/tools_blueprint.html +0 -2249
  592. package/docs/html/trace_blueprint.html +0 -235
  593. package/docs/html/tui_design_blueprint.html +0 -374
  594. package/docs/html/validation_blueprint.html +0 -961
  595. package/docs/html/worker_dispatch_blueprint.html +0 -231
  596. package/src/core/release.ts +0 -2
  597. package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
  598. package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
  599. package/src/engine/apis/lmstudio-native.ts +0 -1438
  600. package/src/engine/apis/thinking-replay.ts +0 -11
  601. 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();
@@ -1,10 +1,7 @@
1
- import { registerApiProvider } from "@earendil-works/pi-ai/compat";
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
- registerApiProvider(openAICompletionsApiProvider, "clio");
18
- registerApiProvider(ollamaNativeApiProvider, "clio");
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
- type LlamaCppModelState = "loaded" | "loading" | "unloaded" | "failed" | "unknown";
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") return state;
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 === "loaded" || model.state === "loading";
106
+ return isResidentState(model.state);
91
107
  }
92
108
 
93
- /** Extract resident (loaded or loading) model records from a /v1/models payload. */
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
- if (model?.state === "loaded") return;
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
- if (knownState === "loaded") return;
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 === "loaded") continue;
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?.state !== "loaded") throw new Error(`pinned-resident restore displaced '${input.keepModelId}'`);
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 === "loaded") return;
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 or an SDK socket.
3
+ * rules are testable without a server connection.
4
4
  *
5
- * LM Studio exposes no VRAM telemetry: neither the SDK nor `/api/v1/models`
6
- * reports total or free GPU memory, and `gpuStrictVramCap` caps GPU offload
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, a
12
- * just-in-time load is capped at {@link CO_RESIDENT_CONTEXT_CEILING} tokens.
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
+ }