@caupulican/pi-adaptative 0.85.3 → 0.85.4

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 (192) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/core/agent-session-services.d.ts.map +1 -1
  3. package/dist/core/agent-session-services.js +2 -0
  4. package/dist/core/agent-session-services.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +6 -5
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +76 -87
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/auth-guidance.d.ts.map +1 -1
  10. package/dist/core/auth-guidance.js +3 -0
  11. package/dist/core/auth-guidance.js.map +1 -1
  12. package/dist/core/autonomy/lane-tool-surface.d.ts +2 -0
  13. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  14. package/dist/core/autonomy/lane-tool-surface.js +4 -3
  15. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  16. package/dist/core/background-lane-controller.d.ts +4 -0
  17. package/dist/core/background-lane-controller.d.ts.map +1 -1
  18. package/dist/core/background-lane-controller.js +2 -0
  19. package/dist/core/background-lane-controller.js.map +1 -1
  20. package/dist/core/background-tool-task-controller.d.ts +12 -7
  21. package/dist/core/background-tool-task-controller.d.ts.map +1 -1
  22. package/dist/core/background-tool-task-controller.js +55 -19
  23. package/dist/core/background-tool-task-controller.js.map +1 -1
  24. package/dist/core/bedrock-scope.d.ts +26 -0
  25. package/dist/core/bedrock-scope.d.ts.map +1 -0
  26. package/dist/core/bedrock-scope.js +178 -0
  27. package/dist/core/bedrock-scope.js.map +1 -0
  28. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  29. package/dist/core/delegation/worker-delegation-controller.js +6 -0
  30. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  31. package/dist/core/extensions/loader.d.ts.map +1 -1
  32. package/dist/core/extensions/loader.js +3 -0
  33. package/dist/core/extensions/loader.js.map +1 -1
  34. package/dist/core/foreground-recovery-controller.d.ts +25 -2
  35. package/dist/core/foreground-recovery-controller.d.ts.map +1 -1
  36. package/dist/core/foreground-recovery-controller.js +111 -2
  37. package/dist/core/foreground-recovery-controller.js.map +1 -1
  38. package/dist/core/foreground-terminal-handoff-controller.d.ts +27 -0
  39. package/dist/core/foreground-terminal-handoff-controller.d.ts.map +1 -0
  40. package/dist/core/foreground-terminal-handoff-controller.js +56 -0
  41. package/dist/core/foreground-terminal-handoff-controller.js.map +1 -0
  42. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  43. package/dist/core/goal-loop-controller.js +5 -0
  44. package/dist/core/goal-loop-controller.js.map +1 -1
  45. package/dist/core/goals/goal-auto-continue-controller.d.ts +3 -0
  46. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -1
  47. package/dist/core/goals/goal-auto-continue-controller.js +28 -7
  48. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -1
  49. package/dist/core/human-input-controller.d.ts +9 -0
  50. package/dist/core/human-input-controller.d.ts.map +1 -1
  51. package/dist/core/human-input-controller.js +60 -5
  52. package/dist/core/human-input-controller.js.map +1 -1
  53. package/dist/core/human-input.d.ts +12 -0
  54. package/dist/core/human-input.d.ts.map +1 -1
  55. package/dist/core/human-input.js +59 -12
  56. package/dist/core/human-input.js.map +1 -1
  57. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  58. package/dist/core/local-runtime-controller.js +15 -9
  59. package/dist/core/local-runtime-controller.js.map +1 -1
  60. package/dist/core/memory/providers/file-store.d.ts +1 -0
  61. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  62. package/dist/core/memory/providers/file-store.js +252 -125
  63. package/dist/core/memory/providers/file-store.js.map +1 -1
  64. package/dist/core/model-capability.d.ts +15 -1
  65. package/dist/core/model-capability.d.ts.map +1 -1
  66. package/dist/core/model-capability.js +24 -1
  67. package/dist/core/model-capability.js.map +1 -1
  68. package/dist/core/model-registry.d.ts +5 -0
  69. package/dist/core/model-registry.d.ts.map +1 -1
  70. package/dist/core/model-registry.js +18 -0
  71. package/dist/core/model-registry.js.map +1 -1
  72. package/dist/core/model-router-controller.d.ts +6 -6
  73. package/dist/core/model-router-controller.d.ts.map +1 -1
  74. package/dist/core/model-router-controller.js +5 -5
  75. package/dist/core/model-router-controller.js.map +1 -1
  76. package/dist/core/models/context-sizing.d.ts +1 -0
  77. package/dist/core/models/context-sizing.d.ts.map +1 -1
  78. package/dist/core/models/context-sizing.js +4 -1
  79. package/dist/core/models/context-sizing.js.map +1 -1
  80. package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
  81. package/dist/core/models/default-model-suggestions.js +20 -10
  82. package/dist/core/models/default-model-suggestions.js.map +1 -1
  83. package/dist/core/models/llamacpp-runtime.d.ts +32 -11
  84. package/dist/core/models/llamacpp-runtime.d.ts.map +1 -1
  85. package/dist/core/models/llamacpp-runtime.js +79 -28
  86. package/dist/core/models/llamacpp-runtime.js.map +1 -1
  87. package/dist/core/models/local-execution-planner.d.ts +24 -0
  88. package/dist/core/models/local-execution-planner.d.ts.map +1 -0
  89. package/dist/core/models/local-execution-planner.js +119 -0
  90. package/dist/core/models/local-execution-planner.js.map +1 -0
  91. package/dist/core/models/local-inference-profile.d.ts +34 -0
  92. package/dist/core/models/local-inference-profile.d.ts.map +1 -0
  93. package/dist/core/models/local-inference-profile.js +74 -0
  94. package/dist/core/models/local-inference-profile.js.map +1 -0
  95. package/dist/core/models/local-runtime.d.ts +23 -4
  96. package/dist/core/models/local-runtime.d.ts.map +1 -1
  97. package/dist/core/models/local-runtime.js +11 -8
  98. package/dist/core/models/local-runtime.js.map +1 -1
  99. package/dist/core/models/managed-ollama-model.d.ts +26 -0
  100. package/dist/core/models/managed-ollama-model.d.ts.map +1 -0
  101. package/dist/core/models/managed-ollama-model.js +59 -0
  102. package/dist/core/models/managed-ollama-model.js.map +1 -0
  103. package/dist/core/models/model-ref.js +11 -11
  104. package/dist/core/models/model-ref.js.map +1 -1
  105. package/dist/core/models/prism-llamacpp-lifecycle.d.ts +10 -20
  106. package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -1
  107. package/dist/core/models/prism-llamacpp-lifecycle.js +11 -31
  108. package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -1
  109. package/dist/core/models/prism-llamacpp-server-probe.d.ts +10 -0
  110. package/dist/core/models/prism-llamacpp-server-probe.d.ts.map +1 -0
  111. package/dist/core/models/prism-llamacpp-server-probe.js +36 -0
  112. package/dist/core/models/prism-llamacpp-server-probe.js.map +1 -0
  113. package/dist/core/models/runtime-arbiter.d.ts +3 -0
  114. package/dist/core/models/runtime-arbiter.d.ts.map +1 -1
  115. package/dist/core/models/runtime-arbiter.js +2 -1
  116. package/dist/core/models/runtime-arbiter.js.map +1 -1
  117. package/dist/core/research/research-lane-controller.d.ts.map +1 -1
  118. package/dist/core/research/research-lane-controller.js +14 -5
  119. package/dist/core/research/research-lane-controller.js.map +1 -1
  120. package/dist/core/runtime-builder.d.ts +5 -1
  121. package/dist/core/runtime-builder.d.ts.map +1 -1
  122. package/dist/core/runtime-builder.js +37 -4
  123. package/dist/core/runtime-builder.js.map +1 -1
  124. package/dist/core/scout-controller.d.ts +3 -2
  125. package/dist/core/scout-controller.d.ts.map +1 -1
  126. package/dist/core/scout-controller.js +1 -0
  127. package/dist/core/scout-controller.js.map +1 -1
  128. package/dist/core/sdk.d.ts.map +1 -1
  129. package/dist/core/sdk.js +10 -8
  130. package/dist/core/sdk.js.map +1 -1
  131. package/dist/core/settings-manager.d.ts +13 -0
  132. package/dist/core/settings-manager.d.ts.map +1 -1
  133. package/dist/core/settings-manager.js +59 -0
  134. package/dist/core/settings-manager.js.map +1 -1
  135. package/dist/core/system-prompt-builder.d.ts +5 -2
  136. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  137. package/dist/core/system-prompt-builder.js +22 -10
  138. package/dist/core/system-prompt-builder.js.map +1 -1
  139. package/dist/core/system-prompt.d.ts +3 -0
  140. package/dist/core/system-prompt.d.ts.map +1 -1
  141. package/dist/core/system-prompt.js +181 -55
  142. package/dist/core/system-prompt.js.map +1 -1
  143. package/dist/core/tool-protocol-controller.d.ts.map +1 -1
  144. package/dist/core/tool-protocol-controller.js +5 -3
  145. package/dist/core/tool-protocol-controller.js.map +1 -1
  146. package/dist/core/tools/edit-diff.d.ts +2 -0
  147. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  148. package/dist/core/tools/edit-diff.js +9 -1
  149. package/dist/core/tools/edit-diff.js.map +1 -1
  150. package/dist/core/tools/edit.d.ts +5 -8
  151. package/dist/core/tools/edit.d.ts.map +1 -1
  152. package/dist/core/tools/edit.js +88 -55
  153. package/dist/core/tools/edit.js.map +1 -1
  154. package/dist/core/tools/file-mutation-intent.d.ts +42 -15
  155. package/dist/core/tools/file-mutation-intent.d.ts.map +1 -1
  156. package/dist/core/tools/file-mutation-intent.js +271 -77
  157. package/dist/core/tools/file-mutation-intent.js.map +1 -1
  158. package/dist/core/tools/index.d.ts +1 -1
  159. package/dist/core/tools/index.d.ts.map +1 -1
  160. package/dist/core/tools/index.js +1 -1
  161. package/dist/core/tools/index.js.map +1 -1
  162. package/dist/core/tools/write.d.ts +5 -10
  163. package/dist/core/tools/write.d.ts.map +1 -1
  164. package/dist/core/tools/write.js +92 -73
  165. package/dist/core/tools/write.js.map +1 -1
  166. package/dist/index.d.ts +1 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +1 -1
  169. package/dist/index.js.map +1 -1
  170. package/dist/modes/interactive/auth-dialogs-controller.d.ts +6 -0
  171. package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
  172. package/dist/modes/interactive/auth-dialogs-controller.js +59 -12
  173. package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
  174. package/dist/modes/interactive/local-model-commands.d.ts +1 -1
  175. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  176. package/dist/modes/interactive/local-model-commands.js +51 -8
  177. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  178. package/docs/providers.md +34 -10
  179. package/docs/tool-boundary-performance-roadmap-2026-08-02.md +21 -18
  180. package/examples/extensions/built-in-tool-renderer.ts +1 -1
  181. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  182. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  183. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  184. package/examples/extensions/minimal-mode.ts +1 -1
  185. package/examples/extensions/prompt-customizer.ts +2 -2
  186. package/examples/extensions/sandbox/package-lock.json +2 -2
  187. package/examples/extensions/sandbox/package.json +1 -1
  188. package/examples/extensions/ssh.ts +17 -0
  189. package/examples/extensions/with-deps/package-lock.json +2 -2
  190. package/examples/extensions/with-deps/package.json +1 -1
  191. package/npm-shrinkwrap.json +240 -260
  192. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"model-ref.js","sourceRoot":"","sources":["../../../src/core/models/model-ref.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model source normalizer (local-model-lifecycle-design.md, \"one panel, three types\"):\n * everything a user can paste — an ollama tag, an hf.co GGUF ref, a full HuggingFace URL, a\n * copied `ollama pull ...` install command, or an API `provider/model` name — normalizes to ONE\n * typed source. Pure string work: pasted install commands are PARSED for their reference and\n * NEVER executed as shell; unknown forms are rejected with the reason.\n */\n\nimport { BONSAI_27B } from \"./llamacpp-runtime.ts\";\n\nexport type ModelSource =\n\t| { type: \"api\"; ref: string }\n\t| { type: \"local\"; pullRef: string }\n\t| { type: \"transformers\"; modelId: string; ref: string }\n\t| { type: \"prism-llamacpp\"; modelId: string; ref: string }\n\t| { type: \"needle\"; ref: string }\n\t| { type: \"rejected\"; reason: string };\n\nconst OLLAMA_TAG = /^[a-z0-9][a-z0-9._-]*(?::[A-Za-z0-9._-]+)?$/;\nconst HF_REF = /^hf\\.co\\/([\\w.-]+)\\/([\\w.-]+)(?::([\\w.-]+))?$/i;\nconst HF_URL = /^https?:\\/\\/(?:www\\.)?huggingface\\.co\\/([\\w.-]+)\\/([\\w.-]+)(?:\\/.*)?$/i;\nconst SHELL_METACHARS = /[;&|`$<>(){}\\\\]/;\nconst PI_MANAGED_TRANSFORMERS_MODEL_IDS = new Set([\"openbmb/MiniCPM5-1B\"]);\n/**\n * Curated prism-ml models that require pi's managed prism llama.cpp fork (see llamacpp-runtime.ts's\n * module docstring: stock llama.cpp/Ollama cannot serve these weights). These GGUF refs route to the\n * \"prism-llamacpp\" source instead of Ollama's local pull path regardless of a supplied quant suffix\n * — the curated descriptor already pins the exact quant file to fetch, so a quant on the input ref is\n * accepted but not required. Keyed lowercase for case-insensitive matching; values are the\n * canonical-cased modelId, sourced from the descriptor itself so the two never drift apart.\n */\nconst PRISM_LLAMACPP_MODEL_IDS = new Map<string, string>([[BONSAI_27B.repo.toLowerCase(), BONSAI_27B.repo]]);\n/**\n * The single curated needle model id (see needle-runtime.ts) — a standalone function-call test\n * bench with no GGUF/quant concept at all (a pip-installed Python package + one pinned pickle\n * checkpoint), so unlike the prism route above, a quant suffix on the input is NOT accepted — it\n * falls through to the ordinary local/Ollama route instead, mirroring the transformers route's\n * `!quant` gate. Only this exact repo routes to \"needle\"; any other Cactus-Compute/* ref keeps the\n * ordinary local route.\n */\nconst NEEDLE_MODEL_ID = \"Cactus-Compute/needle\";\n\nfunction normalizeHuggingFaceReference(org: string, repo: string, quant?: string): ModelSource {\n\tconst modelId = `${org}/${repo}`;\n\tconst prismModelId = PRISM_LLAMACPP_MODEL_IDS.get(modelId.toLowerCase());\n\tif (prismModelId) {\n\t\treturn { type: \"prism-llamacpp\", modelId: prismModelId, ref: `hf.co/${prismModelId}` };\n\t}\n\tif (!quant && modelId.toLowerCase() === NEEDLE_MODEL_ID.toLowerCase()) {\n\t\treturn { type: \"needle\", ref: `hf.co/${NEEDLE_MODEL_ID}` };\n\t}\n\tif (!quant && PI_MANAGED_TRANSFORMERS_MODEL_IDS.has(modelId)) {\n\t\treturn { type: \"transformers\", modelId, ref: `hf.co/${modelId}` };\n\t}\n\treturn { type: \"local\", pullRef: `hf.co/${modelId}${quant ? `:${quant}` : \"\"}` };\n}\n\nexport function normalizeModelSource(rawInput: string): ModelSource {\n\tconst input = rawInput.trim();\n\tif (input.length === 0) return { type: \"rejected\", reason: \"empty input\" };\n\tif (input.length > 500) return { type: \"rejected\", reason: \"input too long to be a model reference\" };\n\n\t// Pasted install command: extract the reference, never execute anything.\n\tconst installCommand = /^ollama\\s+(?:pull|run)\\s+(.+)$/i.exec(input);\n\tif (installCommand) {\n\t\tconst argument = installCommand[1]!.trim().split(/\\s+/)[0] ?? \"\";\n\t\tif (SHELL_METACHARS.test(argument)) {\n\t\t\treturn { type: \"rejected\", reason: \"install command argument contains shell metacharacters\" };\n\t\t}\n\t\tconst inner = normalizeModelSource(argument);\n\t\tif (inner.type === \"local\") return inner;\n\t\treturn { type: \"rejected\", reason: `could not extract a model reference from the install command` };\n\t}\n\n\tif (SHELL_METACHARS.test(input) || /\\s/.test(input)) {\n\t\treturn { type: \"rejected\", reason: \"not a recognized model reference (contains spaces or shell characters)\" };\n\t}\n\n\t// Full HuggingFace URL -> either a pi-managed Transformers model (curated full-base ids) or an\n\t// Ollama hf.co pull ref (GGUF/quant refs still belong to Ollama and keep their :quant suffix).\n\tconst hfUrl = HF_URL.exec(input);\n\tif (hfUrl) {\n\t\treturn normalizeHuggingFaceReference(hfUrl[1]!, hfUrl[2]!);\n\t}\n\n\t// hf.co/org/repo[:quant]\n\tconst hfRef = HF_REF.exec(input);\n\tif (hfRef) {\n\t\treturn normalizeHuggingFaceReference(hfRef[1]!, hfRef[2]!, hfRef[3]);\n\t}\n\n\tif (input.includes(\"://\")) {\n\t\treturn { type: \"rejected\", reason: \"only huggingface.co URLs are recognized as local model links\" };\n\t}\n\n\t// provider/model -> API-registered model (nothing to install; auth + selection only).\n\tif (input.includes(\"/\")) {\n\t\tconst [provider, ...rest] = input.split(\"/\");\n\t\tconst model = rest.join(\"/\");\n\t\tif (provider && model && !model.includes(\"/\")) {\n\t\t\treturn { type: \"api\", ref: `${provider}/${model}` };\n\t\t}\n\t\treturn { type: \"rejected\", reason: \"expected provider/model or hf.co/org/repo[:quant]\" };\n\t}\n\n\t// Bare ollama tag (\"qwen3:1.7b\", \"pi-lifter:latest\", \"llama3\").\n\tif (OLLAMA_TAG.test(input)) {\n\t\treturn { type: \"local\", pullRef: input };\n\t}\n\n\treturn { type: \"rejected\", reason: \"not a recognized model reference\" };\n}\n\n/**\n * Whether an installed-model listing entry (`installedName`, as `OllamaRuntime.list()` reports it)\n * is the same model as `ref` (a user-typed reference, or a `models.json`-registered id). Ollama\n * always reports installed models with an explicit tag — a pull with no tag is normalized to\n * `:latest` server-side — but a `ref` is frequently bare (\"llama3\"), so an exact string compare\n * alone misses that pairing. Exact match always counts; otherwise treat a bare ref against its\n * `:latest`-tagged listing as the same model, and the reverse (a `ref` explicitly pinned to\n * `:latest` against a bare listing), since the local-runtime interface is kept runtime-agnostic\n * (see local-runtime.ts's module docstring) and a future/alternate runtime could list bare names.\n * Shared by every ref-vs-installed-list comparison — see `AgentSession._warnIfManualModelChoiceIsRisky`\n * and `removeLocalModel` in interactive-mode.ts, which had the identical bug independently.\n */\nexport function matchesInstalledLocalModel(ref: string, installedName: string): boolean {\n\tif (ref === installedName) return true;\n\tconst hasTag = (value: string): boolean => value.includes(\":\");\n\tif (!hasTag(ref) && installedName === `${ref}:latest`) return true;\n\tif (!hasTag(installedName) && ref === `${installedName}:latest`) return true;\n\treturn false;\n}\n"]}
1
+ {"version":3,"file":"model-ref.js","sourceRoot":"","sources":["../../../src/core/models/model-ref.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model source normalizer (local-model-lifecycle-design.md, \"one panel, three types\"):\n * everything a user can paste — an ollama tag, an hf.co GGUF ref, a full HuggingFace URL, a\n * copied `ollama pull ...` install command, or an API `provider/model` name — normalizes to ONE\n * typed source. Pure string work: pasted install commands are PARSED for their reference and\n * NEVER executed as shell; unknown forms are rejected with the reason.\n */\n\nimport { resolvePrismLocalModelDescriptor } from \"./local-execution-planner.ts\";\n\nexport type ModelSource =\n\t| { type: \"api\"; ref: string }\n\t| { type: \"local\"; pullRef: string }\n\t| { type: \"transformers\"; modelId: string; ref: string }\n\t| { type: \"prism-llamacpp\"; modelId: string; ref: string }\n\t| { type: \"needle\"; ref: string }\n\t| { type: \"rejected\"; reason: string };\n\nconst OLLAMA_TAG = /^[a-z0-9][a-z0-9._-]*(?::[A-Za-z0-9._-]+)?$/;\nconst HF_REF = /^hf\\.co\\/([\\w.-]+)\\/([\\w.-]+)(?::([\\w.-]+))?$/i;\nconst HF_URL = /^https?:\\/\\/(?:www\\.)?huggingface\\.co\\/([\\w.-]+)\\/([\\w.-]+)(?:\\/.*)?$/i;\nconst SHELL_METACHARS = /[;&|`$<>(){}\\\\]/;\nconst PI_MANAGED_TRANSFORMERS_MODEL_IDS = new Set([\"openbmb/MiniCPM5-1B\"]);\n/**\n * Curated prism-ml models route through the managed Prism llama.cpp adapter instead of Ollama's\n * generic pull path. The execution planner owns the exact artifact and case-insensitive identity;\n * a user-provided quant suffix is accepted as input but cannot override that validated artifact.\n */\n/**\n * The single curated needle model id (see needle-runtime.ts) — a standalone function-call test\n * bench with no GGUF/quant concept at all (a pip-installed Python package + one pinned pickle\n * checkpoint), so unlike the prism route above, a quant suffix on the input is NOT accepted — it\n * falls through to the ordinary local/Ollama route instead, mirroring the transformers route's\n * `!quant` gate. Only this exact repo routes to \"needle\"; any other Cactus-Compute/* ref keeps the\n * ordinary local route.\n */\nconst NEEDLE_MODEL_ID = \"Cactus-Compute/needle\";\n\nfunction normalizeHuggingFaceReference(org: string, repo: string, quant?: string): ModelSource {\n\tconst modelId = `${org}/${repo}`;\n\tconst prismDescriptor = resolvePrismLocalModelDescriptor(modelId);\n\tif (prismDescriptor) {\n\t\treturn {\n\t\t\ttype: \"prism-llamacpp\",\n\t\t\tmodelId: prismDescriptor.repo,\n\t\t\tref: `hf.co/${prismDescriptor.repo}`,\n\t\t};\n\t}\n\tif (!quant && modelId.toLowerCase() === NEEDLE_MODEL_ID.toLowerCase()) {\n\t\treturn { type: \"needle\", ref: `hf.co/${NEEDLE_MODEL_ID}` };\n\t}\n\tif (!quant && PI_MANAGED_TRANSFORMERS_MODEL_IDS.has(modelId)) {\n\t\treturn { type: \"transformers\", modelId, ref: `hf.co/${modelId}` };\n\t}\n\treturn { type: \"local\", pullRef: `hf.co/${modelId}${quant ? `:${quant}` : \"\"}` };\n}\n\nexport function normalizeModelSource(rawInput: string): ModelSource {\n\tconst input = rawInput.trim();\n\tif (input.length === 0) return { type: \"rejected\", reason: \"empty input\" };\n\tif (input.length > 500) return { type: \"rejected\", reason: \"input too long to be a model reference\" };\n\n\t// Pasted install command: extract the reference, never execute anything.\n\tconst installCommand = /^ollama\\s+(?:pull|run)\\s+(.+)$/i.exec(input);\n\tif (installCommand) {\n\t\tconst argument = installCommand[1]!.trim().split(/\\s+/)[0] ?? \"\";\n\t\tif (SHELL_METACHARS.test(argument)) {\n\t\t\treturn { type: \"rejected\", reason: \"install command argument contains shell metacharacters\" };\n\t\t}\n\t\tconst inner = normalizeModelSource(argument);\n\t\tif (inner.type === \"local\") return inner;\n\t\treturn { type: \"rejected\", reason: `could not extract a model reference from the install command` };\n\t}\n\n\tif (SHELL_METACHARS.test(input) || /\\s/.test(input)) {\n\t\treturn { type: \"rejected\", reason: \"not a recognized model reference (contains spaces or shell characters)\" };\n\t}\n\n\t// Full HuggingFace URL -> either a pi-managed Transformers model (curated full-base ids) or an\n\t// Ollama hf.co pull ref (GGUF/quant refs still belong to Ollama and keep their :quant suffix).\n\tconst hfUrl = HF_URL.exec(input);\n\tif (hfUrl) {\n\t\treturn normalizeHuggingFaceReference(hfUrl[1]!, hfUrl[2]!);\n\t}\n\n\t// hf.co/org/repo[:quant]\n\tconst hfRef = HF_REF.exec(input);\n\tif (hfRef) {\n\t\treturn normalizeHuggingFaceReference(hfRef[1]!, hfRef[2]!, hfRef[3]);\n\t}\n\n\tif (input.includes(\"://\")) {\n\t\treturn { type: \"rejected\", reason: \"only huggingface.co URLs are recognized as local model links\" };\n\t}\n\n\t// provider/model -> API-registered model (nothing to install; auth + selection only).\n\tif (input.includes(\"/\")) {\n\t\tconst [provider, ...rest] = input.split(\"/\");\n\t\tconst model = rest.join(\"/\");\n\t\tif (provider && model && !model.includes(\"/\")) {\n\t\t\treturn { type: \"api\", ref: `${provider}/${model}` };\n\t\t}\n\t\treturn { type: \"rejected\", reason: \"expected provider/model or hf.co/org/repo[:quant]\" };\n\t}\n\n\t// Bare ollama tag (\"qwen3:1.7b\", \"pi-lifter:latest\", \"llama3\").\n\tif (OLLAMA_TAG.test(input)) {\n\t\treturn { type: \"local\", pullRef: input };\n\t}\n\n\treturn { type: \"rejected\", reason: \"not a recognized model reference\" };\n}\n\n/**\n * Whether an installed-model listing entry (`installedName`, as `OllamaRuntime.list()` reports it)\n * is the same model as `ref` (a user-typed reference, or a `models.json`-registered id). Ollama\n * always reports installed models with an explicit tag — a pull with no tag is normalized to\n * `:latest` server-side — but a `ref` is frequently bare (\"llama3\"), so an exact string compare\n * alone misses that pairing. Exact match always counts; otherwise treat a bare ref against its\n * `:latest`-tagged listing as the same model, and the reverse (a `ref` explicitly pinned to\n * `:latest` against a bare listing), since the local-runtime interface is kept runtime-agnostic\n * (see local-runtime.ts's module docstring) and a future/alternate runtime could list bare names.\n * Shared by every ref-vs-installed-list comparison — see `AgentSession._warnIfManualModelChoiceIsRisky`\n * and `removeLocalModel` in interactive-mode.ts, which had the identical bug independently.\n */\nexport function matchesInstalledLocalModel(ref: string, installedName: string): boolean {\n\tif (ref === installedName) return true;\n\tconst hasTag = (value: string): boolean => value.includes(\":\");\n\tif (!hasTag(ref) && installedName === `${ref}:latest`) return true;\n\tif (!hasTag(installedName) && ref === `${installedName}:latest`) return true;\n\treturn false;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Core lifecycle logic for pi-managed prism llama.cpp models (Bonsai-27B first), shared between the
2
+ * Core lifecycle logic for Pi-managed Prism llama.cpp models, shared between the
3
3
  * `/models add` install pipeline (modes/interactive/local-model-commands.ts) and the router's
4
4
  * readiness gate (local-runtime-controller.ts). ONE place owns "ensure both GGUF files exist, then
5
5
  * serve" so a turn routed to a cold Bonsai-27B and a fresh `/models add` both go through the
@@ -9,18 +9,19 @@
9
9
  * sessions and isolated/background lanes alike — never depends on the interactive-only UI layer;
10
10
  * mirrors the existing local-runtime.ts (mechanics) / local-runtime-controller.ts (gate) split.
11
11
  */
12
- import { type PrismLlamaCppRuntime, type PrismModelDescriptor } from "./llamacpp-runtime.ts";
12
+ import type { PrismLlamaCppRuntime, PrismModelDescriptor } from "./llamacpp-runtime.ts";
13
+ export { PRISM_LLAMACPP_DESCRIPTORS } from "./local-execution-planner.ts";
14
+ export { probePrismLlamaCppServer } from "./prism-llamacpp-server-probe.ts";
13
15
  /**
14
- * Curated prism llama.cpp descriptors, keyed by repo id — matches the modelId model-ref.ts's
15
- * curated `PRISM_LLAMACPP_MODEL_IDS` route returns, and the id `registerPrismLlamaCppModel` writes
16
- * as the model's id in models.json. Add future curated prism-ml models to both places together.
16
+ * Curated Prism llama.cpp descriptors, keyed by repo id — the same catalog owns model-ref routing
17
+ * and the id `registerPrismLlamaCppModel` writes to models.json. Future curated Prism models enter
18
+ * through that one catalog rather than parallel identifier lists.
17
19
  *
18
20
  * Also the pi-managed-vs-user-owned discriminator (see {@link isPiManagedPrismLlamaCppModel}): a
19
21
  * `llama-cpp` provider model is gated/self-healed by pi ONLY when its id is a key here — a user's
20
22
  * own hand-configured entry (e.g. the built-in `llama-cpp/local` catalog model on port 8080,
21
23
  * pointing at a server the user runs themselves) is never touched.
22
24
  */
23
- export declare const PRISM_LLAMACPP_DESCRIPTORS: Record<string, PrismModelDescriptor>;
24
25
  /**
25
26
  * True only for a `llama-cpp` provider model pi itself registered (id present in
26
27
  * {@link PRISM_LLAMACPP_DESCRIPTORS}) — never true for a user's own hand-configured llama-cpp
@@ -63,27 +64,16 @@ export type EnsurePrismModelServedResult = {
63
64
  * The SOLE path allowed to call `runtime.serve()` for a prism llama.cpp model — no caller may spawn
64
65
  * llama-server without first passing through here (both `addPrismLlamaCppModel`'s install pipeline
65
66
  * and the readiness gate's self-heal path call this, never `runtime.serve()` directly).
66
- * Unconditionally re-verifies BOTH GGUF files via `runtime.downloadModel()` immediately before every
67
+ * Unconditionally re-verifies every required GGUF file via `runtime.downloadModel()` immediately before every
67
68
  * serve — that call is idempotent (skips a re-download when the local file already matches the
68
69
  * remote size, so the happy path costs one stat) and self-heals a file that went missing from disk
69
70
  * (deleted, a partial download from a prior crash, host cleanup, etc.) by re-fetching it instead of
70
71
  * ever starting llama-server without it. The vision projector is mandatory whenever the descriptor
71
- * declares one (Bonsai-27B always does — see BONSAI_27B.mmprojFile): a download failure on EITHER
72
- * file means llama-server is never spawned. There is no text-only fallback Bonsai-27B never
73
- * silently degrades to a vision-less serve because the projector went missing.
72
+ * declares one (the 27B descriptors do): a download failure on any required file means
73
+ * llama-server is never spawned. There is no text-only fallback for a vision descriptor.
74
74
  */
75
75
  export declare function ensurePrismModelFilesThenServe(runtime: PrismLlamaCppRuntime, descriptor: PrismModelDescriptor, args: {
76
76
  port: number;
77
77
  numCtx: number;
78
78
  }, onProgress: (message: string) => void): Promise<EnsurePrismModelServedResult>;
79
- /**
80
- * Direct HTTP reachability probe against a prism llama.cpp server root (NOT the `/v1` OpenAI-compat
81
- * suffix models.json registers — same root/health split as llama-server's own health endpoint used
82
- * internally by PrismLlamaCppRuntime#serve). PrismLlamaCppRuntime has no public method for this (its
83
- * own equivalent, `_healthUp`, is private and scoped to a `serve()` call in progress), so the
84
- * readiness gate needs its own — this is deliberately independent of any single runtime instance:
85
- * it answers "is a server already alive at this URL", true even for a server this session never
86
- * spawned (started earlier, or by a different process).
87
- */
88
- export declare function isPrismLlamaCppServerHealthy(serverUrl: string, fetchFn?: typeof fetch, timeoutMs?: number): Promise<boolean>;
89
79
  //# sourceMappingURL=prism-llamacpp-lifecycle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prism-llamacpp-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/core/models/prism-llamacpp-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAc,KAAK,oBAAoB,EAAE,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGzG;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAE3E,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAE9F;AAED;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAMvE;AAED,MAAM,MAAM,4BAA4B,GACrC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvF;;;;;;;;;;;;GAYG;AACH,wBAAsB,8BAA8B,CACnD,OAAO,EAAE,oBAAoB,EAC7B,UAAU,EAAE,oBAAoB,EAChC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACtC,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACnC,OAAO,CAAC,4BAA4B,CAAC,CA6BvC;AAID;;;;;;;;GAQG;AACH,wBAAsB,4BAA4B,CACjD,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,OAAO,KAAa,EAC7B,SAAS,GAAE,MAAwC,GACjD,OAAO,CAAC,OAAO,CAAC,CAOlB"}
1
+ {"version":3,"file":"prism-llamacpp-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/core/models/prism-llamacpp-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAIxF,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E;;;;;;;;;GASG;AACH;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAE9F;AAED;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAMvE;AAED,MAAM,MAAM,4BAA4B,GACrC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvF;;;;;;;;;;;GAWG;AACH,wBAAsB,8BAA8B,CACnD,OAAO,EAAE,oBAAoB,EAC7B,UAAU,EAAE,oBAAoB,EAChC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACtC,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACnC,OAAO,CAAC,4BAA4B,CAAC,CA8BvC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Core lifecycle logic for pi-managed prism llama.cpp models (Bonsai-27B first), shared between the
2
+ * Core lifecycle logic for Pi-managed Prism llama.cpp models, shared between the
3
3
  * `/models add` install pipeline (modes/interactive/local-model-commands.ts) and the router's
4
4
  * readiness gate (local-runtime-controller.ts). ONE place owns "ensure both GGUF files exist, then
5
5
  * serve" so a turn routed to a cold Bonsai-27B and a fresh `/models add` both go through the
@@ -9,21 +9,20 @@
9
9
  * sessions and isolated/background lanes alike — never depends on the interactive-only UI layer;
10
10
  * mirrors the existing local-runtime.ts (mechanics) / local-runtime-controller.ts (gate) split.
11
11
  */
12
- import { BONSAI_27B } from "./llamacpp-runtime.js";
12
+ import { PRISM_LLAMACPP_DESCRIPTORS } from "./local-execution-planner.js";
13
13
  import { PRISM_LLAMACPP_PROVIDER } from "./local-registration.js";
14
+ export { PRISM_LLAMACPP_DESCRIPTORS } from "./local-execution-planner.js";
15
+ export { probePrismLlamaCppServer } from "./prism-llamacpp-server-probe.js";
14
16
  /**
15
- * Curated prism llama.cpp descriptors, keyed by repo id — matches the modelId model-ref.ts's
16
- * curated `PRISM_LLAMACPP_MODEL_IDS` route returns, and the id `registerPrismLlamaCppModel` writes
17
- * as the model's id in models.json. Add future curated prism-ml models to both places together.
17
+ * Curated Prism llama.cpp descriptors, keyed by repo id — the same catalog owns model-ref routing
18
+ * and the id `registerPrismLlamaCppModel` writes to models.json. Future curated Prism models enter
19
+ * through that one catalog rather than parallel identifier lists.
18
20
  *
19
21
  * Also the pi-managed-vs-user-owned discriminator (see {@link isPiManagedPrismLlamaCppModel}): a
20
22
  * `llama-cpp` provider model is gated/self-healed by pi ONLY when its id is a key here — a user's
21
23
  * own hand-configured entry (e.g. the built-in `llama-cpp/local` catalog model on port 8080,
22
24
  * pointing at a server the user runs themselves) is never touched.
23
25
  */
24
- export const PRISM_LLAMACPP_DESCRIPTORS = {
25
- [BONSAI_27B.repo]: BONSAI_27B,
26
- };
27
26
  /**
28
27
  * True only for a `llama-cpp` provider model pi itself registered (id present in
29
28
  * {@link PRISM_LLAMACPP_DESCRIPTORS}) — never true for a user's own hand-configured llama-cpp
@@ -66,14 +65,13 @@ export function derivePrismLlamaCppNumCtx(totalMemBytes) {
66
65
  * The SOLE path allowed to call `runtime.serve()` for a prism llama.cpp model — no caller may spawn
67
66
  * llama-server without first passing through here (both `addPrismLlamaCppModel`'s install pipeline
68
67
  * and the readiness gate's self-heal path call this, never `runtime.serve()` directly).
69
- * Unconditionally re-verifies BOTH GGUF files via `runtime.downloadModel()` immediately before every
68
+ * Unconditionally re-verifies every required GGUF file via `runtime.downloadModel()` immediately before every
70
69
  * serve — that call is idempotent (skips a re-download when the local file already matches the
71
70
  * remote size, so the happy path costs one stat) and self-heals a file that went missing from disk
72
71
  * (deleted, a partial download from a prior crash, host cleanup, etc.) by re-fetching it instead of
73
72
  * ever starting llama-server without it. The vision projector is mandatory whenever the descriptor
74
- * declares one (Bonsai-27B always does — see BONSAI_27B.mmprojFile): a download failure on EITHER
75
- * file means llama-server is never spawned. There is no text-only fallback Bonsai-27B never
76
- * silently degrades to a vision-less serve because the projector went missing.
73
+ * declares one (the 27B descriptors do): a download failure on any required file means
74
+ * llama-server is never spawned. There is no text-only fallback for a vision descriptor.
77
75
  */
78
76
  export async function ensurePrismModelFilesThenServe(runtime, descriptor, args, onProgress) {
79
77
  const downloadedModel = await runtime.downloadModel({ repo: descriptor.repo, file: descriptor.file }, (progress) => onProgress(` ${descriptor.file}: ${progress}`));
@@ -91,6 +89,7 @@ export async function ensurePrismModelFilesThenServe(runtime, descriptor, args,
91
89
  onProgress(`Starting ${descriptor.displayName} on 127.0.0.1:${args.port} (context ${args.numCtx})…`);
92
90
  const served = await runtime.serve({
93
91
  modelPath: downloadedModel.path,
92
+ modelAlias: descriptor.repo,
94
93
  mmprojPath,
95
94
  port: args.port,
96
95
  numCtx: args.numCtx,
@@ -99,23 +98,4 @@ export async function ensurePrismModelFilesThenServe(runtime, descriptor, args,
99
98
  return { ok: false, stage: "serve", error: served.error };
100
99
  return { ok: true, baseUrl: served.baseUrl };
101
100
  }
102
- const DEFAULT_HEALTH_CHECK_TIMEOUT_MS = 2_000;
103
- /**
104
- * Direct HTTP reachability probe against a prism llama.cpp server root (NOT the `/v1` OpenAI-compat
105
- * suffix models.json registers — same root/health split as llama-server's own health endpoint used
106
- * internally by PrismLlamaCppRuntime#serve). PrismLlamaCppRuntime has no public method for this (its
107
- * own equivalent, `_healthUp`, is private and scoped to a `serve()` call in progress), so the
108
- * readiness gate needs its own — this is deliberately independent of any single runtime instance:
109
- * it answers "is a server already alive at this URL", true even for a server this session never
110
- * spawned (started earlier, or by a different process).
111
- */
112
- export async function isPrismLlamaCppServerHealthy(serverUrl, fetchFn = fetch, timeoutMs = DEFAULT_HEALTH_CHECK_TIMEOUT_MS) {
113
- try {
114
- const response = await fetchFn(`${serverUrl}/health`, { signal: AbortSignal.timeout(timeoutMs) });
115
- return response.ok;
116
- }
117
- catch {
118
- return false;
119
- }
120
- }
121
101
  //# sourceMappingURL=prism-llamacpp-lifecycle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prism-llamacpp-lifecycle.js","sourceRoot":"","sources":["../../../src/core/models/prism-llamacpp-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core lifecycle logic for pi-managed prism llama.cpp models (Bonsai-27B first), shared between the\n * `/models add` install pipeline (modes/interactive/local-model-commands.ts) and the router's\n * readiness gate (local-runtime-controller.ts). ONE place owns \"ensure both GGUF files exist, then\n * serve\" so a turn routed to a cold Bonsai-27B and a fresh `/models add` both go through the\n * identical self-healing path instead of two parallel, potentially-diverging implementations.\n *\n * Lives in core/models/ (not modes/) so the headless readiness gate — used by print/RPC/interactive\n * sessions and isolated/background lanes alike — never depends on the interactive-only UI layer;\n * mirrors the existing local-runtime.ts (mechanics) / local-runtime-controller.ts (gate) split.\n */\n\nimport { BONSAI_27B, type PrismLlamaCppRuntime, type PrismModelDescriptor } from \"./llamacpp-runtime.ts\";\nimport { PRISM_LLAMACPP_PROVIDER } from \"./local-registration.ts\";\n\n/**\n * Curated prism llama.cpp descriptors, keyed by repo id — matches the modelId model-ref.ts's\n * curated `PRISM_LLAMACPP_MODEL_IDS` route returns, and the id `registerPrismLlamaCppModel` writes\n * as the model's id in models.json. Add future curated prism-ml models to both places together.\n *\n * Also the pi-managed-vs-user-owned discriminator (see {@link isPiManagedPrismLlamaCppModel}): a\n * `llama-cpp` provider model is gated/self-healed by pi ONLY when its id is a key here — a user's\n * own hand-configured entry (e.g. the built-in `llama-cpp/local` catalog model on port 8080,\n * pointing at a server the user runs themselves) is never touched.\n */\nexport const PRISM_LLAMACPP_DESCRIPTORS: Record<string, PrismModelDescriptor> = {\n\t[BONSAI_27B.repo]: BONSAI_27B,\n};\n\n/**\n * True only for a `llama-cpp` provider model pi itself registered (id present in\n * {@link PRISM_LLAMACPP_DESCRIPTORS}) — never true for a user's own hand-configured llama-cpp\n * model, including the built-in `llama-cpp/local` catalog entry. This is the gate that must run\n * before ANY self-heal/serve action so pi never touches a server it doesn't own.\n */\nexport function isPiManagedPrismLlamaCppModel(model: { provider: string; id: string }): boolean {\n\treturn model.provider === PRISM_LLAMACPP_PROVIDER && Object.hasOwn(PRISM_LLAMACPP_DESCRIPTORS, model.id);\n}\n\n/**\n * Fixed local port for pi's own managed prism llama.cpp server. Deliberately NOT the generic\n * built-in `llama-cpp/local` catalog model's conventional port 8080 (models.generated.ts) — that\n * port is the convention for a user's OWN manually started llama-server pi merely points at; pi's\n * managed instance must never collide with it.\n */\nexport const PRISM_LLAMACPP_SERVE_PORT = 8090;\n\n/**\n * Conservative RAM-only context cap for the pi-managed prism llama.cpp server, used at FIRST\n * install time only (`/models add`) — a later self-heal re-serve reuses the model's already\n * registered `contextWindow` instead (see the readiness gate in local-runtime-controller.ts), never\n * re-deriving from current RAM, so a served context size can't drift from what pi already told the\n * rest of the session (compaction, etc.) to expect. PrismLlamaCppRuntime has no `/api/show`-\n * equivalent GGUF metadata endpoint (unlike Ollama, which context-sizing.ts's\n * deriveLocalContextSizing depends on), so this is a coarse rung table mirroring\n * context-sizing.ts's CONTEXT_RUNGS, hard-capped at 32768 regardless of headroom: Bonsai-27B\n * advertises a 262K-class context, but that is not a realistic KV budget for a CPU-served model on\n * consumer hardware, and there is no larger validated rung to grow into yet.\n */\nexport function derivePrismLlamaCppNumCtx(totalMemBytes: number): number {\n\tconst totalGb = totalMemBytes / 1e9;\n\tif (totalGb >= 64) return 32_768;\n\tif (totalGb >= 32) return 16_384;\n\tif (totalGb >= 16) return 8_192;\n\treturn 4_096;\n}\n\nexport type EnsurePrismModelServedResult =\n\t| { ok: true; baseUrl: string }\n\t| { ok: false; stage: \"model-download\" | \"mmproj-download\" | \"serve\"; error: string };\n\n/**\n * The SOLE path allowed to call `runtime.serve()` for a prism llama.cpp model — no caller may spawn\n * llama-server without first passing through here (both `addPrismLlamaCppModel`'s install pipeline\n * and the readiness gate's self-heal path call this, never `runtime.serve()` directly).\n * Unconditionally re-verifies BOTH GGUF files via `runtime.downloadModel()` immediately before every\n * serve — that call is idempotent (skips a re-download when the local file already matches the\n * remote size, so the happy path costs one stat) and self-heals a file that went missing from disk\n * (deleted, a partial download from a prior crash, host cleanup, etc.) by re-fetching it instead of\n * ever starting llama-server without it. The vision projector is mandatory whenever the descriptor\n * declares one (Bonsai-27B always does — see BONSAI_27B.mmprojFile): a download failure on EITHER\n * file means llama-server is never spawned. There is no text-only fallback — Bonsai-27B never\n * silently degrades to a vision-less serve because the projector went missing.\n */\nexport async function ensurePrismModelFilesThenServe(\n\truntime: PrismLlamaCppRuntime,\n\tdescriptor: PrismModelDescriptor,\n\targs: { port: number; numCtx: number },\n\tonProgress: (message: string) => void,\n): Promise<EnsurePrismModelServedResult> {\n\tconst downloadedModel = await runtime.downloadModel({ repo: descriptor.repo, file: descriptor.file }, (progress) =>\n\t\tonProgress(` ${descriptor.file}: ${progress}`),\n\t);\n\tif (!downloadedModel.ok || !downloadedModel.path) {\n\t\treturn { ok: false, stage: \"model-download\", error: downloadedModel.error ?? \"unknown error\" };\n\t}\n\n\tlet mmprojPath: string | undefined;\n\tif (descriptor.mmprojFile) {\n\t\tconst downloadedMmproj = await runtime.downloadModel(\n\t\t\t{ repo: descriptor.repo, file: descriptor.mmprojFile },\n\t\t\t(progress) => onProgress(` ${descriptor.mmprojFile}: ${progress}`),\n\t\t);\n\t\tif (!downloadedMmproj.ok || !downloadedMmproj.path) {\n\t\t\treturn { ok: false, stage: \"mmproj-download\", error: downloadedMmproj.error ?? \"unknown error\" };\n\t\t}\n\t\tmmprojPath = downloadedMmproj.path;\n\t}\n\n\tonProgress(`Starting ${descriptor.displayName} on 127.0.0.1:${args.port} (context ${args.numCtx})…`);\n\tconst served = await runtime.serve({\n\t\tmodelPath: downloadedModel.path,\n\t\tmmprojPath,\n\t\tport: args.port,\n\t\tnumCtx: args.numCtx,\n\t});\n\tif (!served.ok) return { ok: false, stage: \"serve\", error: served.error };\n\treturn { ok: true, baseUrl: served.baseUrl };\n}\n\nconst DEFAULT_HEALTH_CHECK_TIMEOUT_MS = 2_000;\n\n/**\n * Direct HTTP reachability probe against a prism llama.cpp server root (NOT the `/v1` OpenAI-compat\n * suffix models.json registers — same root/health split as llama-server's own health endpoint used\n * internally by PrismLlamaCppRuntime#serve). PrismLlamaCppRuntime has no public method for this (its\n * own equivalent, `_healthUp`, is private and scoped to a `serve()` call in progress), so the\n * readiness gate needs its own — this is deliberately independent of any single runtime instance:\n * it answers \"is a server already alive at this URL\", true even for a server this session never\n * spawned (started earlier, or by a different process).\n */\nexport async function isPrismLlamaCppServerHealthy(\n\tserverUrl: string,\n\tfetchFn: typeof fetch = fetch,\n\ttimeoutMs: number = DEFAULT_HEALTH_CHECK_TIMEOUT_MS,\n): Promise<boolean> {\n\ttry {\n\t\tconst response = await fetchFn(`${serverUrl}/health`, { signal: AbortSignal.timeout(timeoutMs) });\n\t\treturn response.ok;\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
1
+ {"version":3,"file":"prism-llamacpp-lifecycle.js","sourceRoot":"","sources":["../../../src/core/models/prism-llamacpp-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core lifecycle logic for Pi-managed Prism llama.cpp models, shared between the\n * `/models add` install pipeline (modes/interactive/local-model-commands.ts) and the router's\n * readiness gate (local-runtime-controller.ts). ONE place owns \"ensure both GGUF files exist, then\n * serve\" so a turn routed to a cold Bonsai-27B and a fresh `/models add` both go through the\n * identical self-healing path instead of two parallel, potentially-diverging implementations.\n *\n * Lives in core/models/ (not modes/) so the headless readiness gate — used by print/RPC/interactive\n * sessions and isolated/background lanes alike — never depends on the interactive-only UI layer;\n * mirrors the existing local-runtime.ts (mechanics) / local-runtime-controller.ts (gate) split.\n */\n\nimport type { PrismLlamaCppRuntime, PrismModelDescriptor } from \"./llamacpp-runtime.ts\";\nimport { PRISM_LLAMACPP_DESCRIPTORS } from \"./local-execution-planner.ts\";\nimport { PRISM_LLAMACPP_PROVIDER } from \"./local-registration.ts\";\n\nexport { PRISM_LLAMACPP_DESCRIPTORS } from \"./local-execution-planner.ts\";\nexport { probePrismLlamaCppServer } from \"./prism-llamacpp-server-probe.ts\";\n\n/**\n * Curated Prism llama.cpp descriptors, keyed by repo id — the same catalog owns model-ref routing\n * and the id `registerPrismLlamaCppModel` writes to models.json. Future curated Prism models enter\n * through that one catalog rather than parallel identifier lists.\n *\n * Also the pi-managed-vs-user-owned discriminator (see {@link isPiManagedPrismLlamaCppModel}): a\n * `llama-cpp` provider model is gated/self-healed by pi ONLY when its id is a key here — a user's\n * own hand-configured entry (e.g. the built-in `llama-cpp/local` catalog model on port 8080,\n * pointing at a server the user runs themselves) is never touched.\n */\n/**\n * True only for a `llama-cpp` provider model pi itself registered (id present in\n * {@link PRISM_LLAMACPP_DESCRIPTORS}) — never true for a user's own hand-configured llama-cpp\n * model, including the built-in `llama-cpp/local` catalog entry. This is the gate that must run\n * before ANY self-heal/serve action so pi never touches a server it doesn't own.\n */\nexport function isPiManagedPrismLlamaCppModel(model: { provider: string; id: string }): boolean {\n\treturn model.provider === PRISM_LLAMACPP_PROVIDER && Object.hasOwn(PRISM_LLAMACPP_DESCRIPTORS, model.id);\n}\n\n/**\n * Fixed local port for pi's own managed prism llama.cpp server. Deliberately NOT the generic\n * built-in `llama-cpp/local` catalog model's conventional port 8080 (models.generated.ts) — that\n * port is the convention for a user's OWN manually started llama-server pi merely points at; pi's\n * managed instance must never collide with it.\n */\nexport const PRISM_LLAMACPP_SERVE_PORT = 8090;\n\n/**\n * Conservative RAM-only context cap for the pi-managed prism llama.cpp server, used at FIRST\n * install time only (`/models add`) — a later self-heal re-serve reuses the model's already\n * registered `contextWindow` instead (see the readiness gate in local-runtime-controller.ts), never\n * re-deriving from current RAM, so a served context size can't drift from what pi already told the\n * rest of the session (compaction, etc.) to expect. PrismLlamaCppRuntime has no `/api/show`-\n * equivalent GGUF metadata endpoint (unlike Ollama, which context-sizing.ts's\n * deriveLocalContextSizing depends on), so this is a coarse rung table mirroring\n * context-sizing.ts's CONTEXT_RUNGS, hard-capped at 32768 regardless of headroom: Bonsai-27B\n * advertises a 262K-class context, but that is not a realistic KV budget for a CPU-served model on\n * consumer hardware, and there is no larger validated rung to grow into yet.\n */\nexport function derivePrismLlamaCppNumCtx(totalMemBytes: number): number {\n\tconst totalGb = totalMemBytes / 1e9;\n\tif (totalGb >= 64) return 32_768;\n\tif (totalGb >= 32) return 16_384;\n\tif (totalGb >= 16) return 8_192;\n\treturn 4_096;\n}\n\nexport type EnsurePrismModelServedResult =\n\t| { ok: true; baseUrl: string }\n\t| { ok: false; stage: \"model-download\" | \"mmproj-download\" | \"serve\"; error: string };\n\n/**\n * The SOLE path allowed to call `runtime.serve()` for a prism llama.cpp model — no caller may spawn\n * llama-server without first passing through here (both `addPrismLlamaCppModel`'s install pipeline\n * and the readiness gate's self-heal path call this, never `runtime.serve()` directly).\n * Unconditionally re-verifies every required GGUF file via `runtime.downloadModel()` immediately before every\n * serve — that call is idempotent (skips a re-download when the local file already matches the\n * remote size, so the happy path costs one stat) and self-heals a file that went missing from disk\n * (deleted, a partial download from a prior crash, host cleanup, etc.) by re-fetching it instead of\n * ever starting llama-server without it. The vision projector is mandatory whenever the descriptor\n * declares one (the 27B descriptors do): a download failure on any required file means\n * llama-server is never spawned. There is no text-only fallback for a vision descriptor.\n */\nexport async function ensurePrismModelFilesThenServe(\n\truntime: PrismLlamaCppRuntime,\n\tdescriptor: PrismModelDescriptor,\n\targs: { port: number; numCtx: number },\n\tonProgress: (message: string) => void,\n): Promise<EnsurePrismModelServedResult> {\n\tconst downloadedModel = await runtime.downloadModel({ repo: descriptor.repo, file: descriptor.file }, (progress) =>\n\t\tonProgress(` ${descriptor.file}: ${progress}`),\n\t);\n\tif (!downloadedModel.ok || !downloadedModel.path) {\n\t\treturn { ok: false, stage: \"model-download\", error: downloadedModel.error ?? \"unknown error\" };\n\t}\n\n\tlet mmprojPath: string | undefined;\n\tif (descriptor.mmprojFile) {\n\t\tconst downloadedMmproj = await runtime.downloadModel(\n\t\t\t{ repo: descriptor.repo, file: descriptor.mmprojFile },\n\t\t\t(progress) => onProgress(` ${descriptor.mmprojFile}: ${progress}`),\n\t\t);\n\t\tif (!downloadedMmproj.ok || !downloadedMmproj.path) {\n\t\t\treturn { ok: false, stage: \"mmproj-download\", error: downloadedMmproj.error ?? \"unknown error\" };\n\t\t}\n\t\tmmprojPath = downloadedMmproj.path;\n\t}\n\n\tonProgress(`Starting ${descriptor.displayName} on 127.0.0.1:${args.port} (context ${args.numCtx})…`);\n\tconst served = await runtime.serve({\n\t\tmodelPath: downloadedModel.path,\n\t\tmodelAlias: descriptor.repo,\n\t\tmmprojPath,\n\t\tport: args.port,\n\t\tnumCtx: args.numCtx,\n\t});\n\tif (!served.ok) return { ok: false, stage: \"serve\", error: served.error };\n\treturn { ok: true, baseUrl: served.baseUrl };\n}\n"]}
@@ -0,0 +1,10 @@
1
+ export type PrismLlamaCppServerProbe = {
2
+ status: "down";
3
+ servedModelIds: [];
4
+ } | {
5
+ status: "matching" | "conflict";
6
+ servedModelIds: string[];
7
+ };
8
+ /** Health alone is insufficient on a shared port: readiness requires the exact requested alias. */
9
+ export declare function probePrismLlamaCppServer(serverUrl: string, expectedModelId: string, fetchFn?: typeof fetch, timeoutMs?: number): Promise<PrismLlamaCppServerProbe>;
10
+ //# sourceMappingURL=prism-llamacpp-server-probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prism-llamacpp-server-probe.d.ts","sourceRoot":"","sources":["../../../src/core/models/prism-llamacpp-server-probe.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GACjC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,EAAE,CAAA;CAAE,GACtC;IAAE,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAMjE,mGAAmG;AACnG,wBAAsB,wBAAwB,CAC7C,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,OAAO,KAAa,EAC7B,SAAS,GAAE,MAAiC,GAC1C,OAAO,CAAC,wBAAwB,CAAC,CA6BnC"}
@@ -0,0 +1,36 @@
1
+ const DEFAULT_PROBE_TIMEOUT_MS = 2_000;
2
+ const MAX_REPORTED_MODEL_IDS = 8;
3
+ const MAX_MODEL_ID_CHARS = 256;
4
+ /** Health alone is insufficient on a shared port: readiness requires the exact requested alias. */
5
+ export async function probePrismLlamaCppServer(serverUrl, expectedModelId, fetchFn = fetch, timeoutMs = DEFAULT_PROBE_TIMEOUT_MS) {
6
+ try {
7
+ const health = await fetchFn(`${serverUrl}/health`, { signal: AbortSignal.timeout(timeoutMs) });
8
+ if (!health.ok)
9
+ return { status: "down", servedModelIds: [] };
10
+ }
11
+ catch {
12
+ return { status: "down", servedModelIds: [] };
13
+ }
14
+ try {
15
+ const models = await fetchFn(`${serverUrl}/v1/models`, { signal: AbortSignal.timeout(timeoutMs) });
16
+ if (!models.ok)
17
+ return { status: "conflict", servedModelIds: [] };
18
+ const payload = (await models.json());
19
+ const servedModelIds = Array.isArray(payload.data)
20
+ ? payload.data
21
+ .map((entry) => typeof entry === "object" && entry !== null && typeof entry.id === "string"
22
+ ? entry.id.slice(0, MAX_MODEL_ID_CHARS)
23
+ : undefined)
24
+ .filter((id) => id !== undefined)
25
+ .slice(0, MAX_REPORTED_MODEL_IDS)
26
+ : [];
27
+ return {
28
+ status: servedModelIds.includes(expectedModelId) ? "matching" : "conflict",
29
+ servedModelIds,
30
+ };
31
+ }
32
+ catch {
33
+ return { status: "conflict", servedModelIds: [] };
34
+ }
35
+ }
36
+ //# sourceMappingURL=prism-llamacpp-server-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prism-llamacpp-server-probe.js","sourceRoot":"","sources":["../../../src/core/models/prism-llamacpp-server-probe.ts"],"names":[],"mappings":"AAIA,MAAM,wBAAwB,GAAG,KAAK,CAAC;AACvC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACjC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC7C,SAAiB,EACjB,eAAuB,EACvB,OAAO,GAAiB,KAAK,EAC7B,SAAS,GAAW,wBAAwB;IAE5C,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,SAAS,SAAS,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAChG,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,SAAS,YAAY,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACnG,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;QAClE,MAAM,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAuB,CAAC;QAC5D,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;YACjD,CAAC,CAAC,OAAO,CAAC,IAAI;iBACX,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACd,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAQ,KAA0B,CAAC,EAAE,KAAK,QAAQ;gBAChG,CAAC,CAAE,KAAwB,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC;gBAC3D,CAAC,CAAC,SAAS,CACZ;iBACA,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC;iBAC9C,KAAK,CAAC,CAAC,EAAE,sBAAsB,CAAC;YACnC,CAAC,CAAC,EAAE,CAAC;QACN,OAAO;YACN,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;YAC1E,cAAc;SACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IACnD,CAAC;AACF,CAAC","sourcesContent":["export type PrismLlamaCppServerProbe =\n\t| { status: \"down\"; servedModelIds: [] }\n\t| { status: \"matching\" | \"conflict\"; servedModelIds: string[] };\n\nconst DEFAULT_PROBE_TIMEOUT_MS = 2_000;\nconst MAX_REPORTED_MODEL_IDS = 8;\nconst MAX_MODEL_ID_CHARS = 256;\n\n/** Health alone is insufficient on a shared port: readiness requires the exact requested alias. */\nexport async function probePrismLlamaCppServer(\n\tserverUrl: string,\n\texpectedModelId: string,\n\tfetchFn: typeof fetch = fetch,\n\ttimeoutMs: number = DEFAULT_PROBE_TIMEOUT_MS,\n): Promise<PrismLlamaCppServerProbe> {\n\ttry {\n\t\tconst health = await fetchFn(`${serverUrl}/health`, { signal: AbortSignal.timeout(timeoutMs) });\n\t\tif (!health.ok) return { status: \"down\", servedModelIds: [] };\n\t} catch {\n\t\treturn { status: \"down\", servedModelIds: [] };\n\t}\n\n\ttry {\n\t\tconst models = await fetchFn(`${serverUrl}/v1/models`, { signal: AbortSignal.timeout(timeoutMs) });\n\t\tif (!models.ok) return { status: \"conflict\", servedModelIds: [] };\n\t\tconst payload = (await models.json()) as { data?: unknown };\n\t\tconst servedModelIds = Array.isArray(payload.data)\n\t\t\t? payload.data\n\t\t\t\t\t.map((entry) =>\n\t\t\t\t\t\ttypeof entry === \"object\" && entry !== null && typeof (entry as { id?: unknown }).id === \"string\"\n\t\t\t\t\t\t\t? (entry as { id: string }).id.slice(0, MAX_MODEL_ID_CHARS)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t)\n\t\t\t\t\t.filter((id): id is string => id !== undefined)\n\t\t\t\t\t.slice(0, MAX_REPORTED_MODEL_IDS)\n\t\t\t: [];\n\t\treturn {\n\t\t\tstatus: servedModelIds.includes(expectedModelId) ? \"matching\" : \"conflict\",\n\t\t\tservedModelIds,\n\t\t};\n\t} catch {\n\t\treturn { status: \"conflict\", servedModelIds: [] };\n\t}\n}\n"]}
@@ -16,6 +16,9 @@ export interface RuntimeLoadRequest {
16
16
  role: RuntimeRole;
17
17
  priority: number;
18
18
  nowMs: number;
19
+ /** Refuse an immediate reload of a model that was just evicted for the current resident. */
20
+ antiThrashWindowMs?: number;
21
+ /** Protect every newly-used resident from eviction for this duration. */
19
22
  minDwellMs?: number;
20
23
  pinActiveModel?: string;
21
24
  /** Adapter identity for this request; set by RuntimeResidencyArbiter before planning. */
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-arbiter.d.ts","sourceRoot":"","sources":["../../../src/core/models/runtime-arbiter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime-arbiter.d.ts","sourceRoot":"","sources":["../../../src/core/models/runtime-arbiter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE7E,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,YAAY,GAAG,UAAU,CAAC;AAClE,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,gBAAgB,CAAC;CACnC;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,4FAA4F;IAC5F,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACpC,eAAe,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAC1C,sGAAsG;IACtG,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,oBAAoB,GAC7B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC7F;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnH,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,IAAI,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACxC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,qBAAa,uBAAuB;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;IAEhE,YAAY,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,uBAAuB,EAAE,CAAA;KAAE,EAG7E;IAEK,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA6BlG;CACD;AAED,qBAAa,6BAA8B,YAAW,uBAAuB;IAC5E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,gBAAgB,EAAG,YAAY,CAAU;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmF;IAC3G,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC,YACC,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,oBAAoB,GAAG,gBAAgB,GAAG,iBAAiB,CAAC,EACzF,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO,EAKtC;IAEK,IAAI,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAU5C;IAEK,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjD;IAEK,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1C;CACD;AAED,qBAAa,mCAAoC,YAAW,uBAAuB;IAClF,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,gBAAgB,EAAG,MAAM,CAAU;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyD;IACjF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC,YACC,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC,EAC/D,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO,EAOtC;IAEK,IAAI,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAa5C;IAEK,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlD;IAEK,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3C;CACD;AAED,qBAAa,+BAAgC,YAAW,uBAAuB;IAC9E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,gBAAgB,EAAG,UAAU,CAAU;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IAEnD,YAAY,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,oBAAoB,EAAE,WAAW,GAAG,kBAAkB,CAAC,EAAE,EAOhG;IAEK,IAAI,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAE5C;IAEK,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAG;IAEhD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAG;CAC/C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3C,OAAO,EAAE,kBAAkB,CAAC;CAC5B,GAAG,oBAAoB,CAsEvB"}
@@ -165,10 +165,11 @@ export function planRuntimeResidency(args) {
165
165
  };
166
166
  }
167
167
  function hasPingPongRisk(request) {
168
+ const windowMs = request.antiThrashWindowMs ?? request.minDwellMs ?? 0;
168
169
  return (request.recentEvictions ?? []).some((record) => record.evicted === request.model &&
169
170
  (request.adapterId === undefined ||
170
171
  record.evictedAdapterId === undefined ||
171
172
  record.evictedAdapterId === request.adapterId) &&
172
- request.nowMs - record.atMs <= (request.minDwellMs ?? 0));
173
+ request.nowMs - record.atMs <= windowMs);
173
174
  }
174
175
  //# sourceMappingURL=runtime-arbiter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-arbiter.js","sourceRoot":"","sources":["../../../src/core/models/runtime-arbiter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OllamaRuntime, TransformersRuntime } from \"./local-runtime.ts\";\n\nexport type ResidencyControl = \"full\" | \"keep-alive\" | \"advisory\";\nexport type RuntimeRole = \"active\" | \"router\" | \"bench\" | \"probe\";\n\nexport interface RuntimeResidentModel {\n\tadapterId: string;\n\tmodel: string;\n\tbytes: number;\n\tlastUsedAtMs: number;\n\tpriority: number;\n\tpinned?: boolean;\n\tresidencyControl: ResidencyControl;\n}\n\nexport interface RuntimeLoadRequest {\n\tmodel: string;\n\tbytes: number;\n\trole: RuntimeRole;\n\tpriority: number;\n\tnowMs: number;\n\tminDwellMs?: number;\n\tpinActiveModel?: string;\n\t/** Adapter identity for this request; set by RuntimeResidencyArbiter before planning. */\n\tadapterId?: string;\n\t/** When pinActiveModel is set, constrain that pin to this adapter when present. */\n\tpinActiveAdapterId?: string;\n\treservations?: RuntimeReservation[];\n\trecentEvictions?: RuntimeEvictionRecord[];\n\t/** False performs admission/eviction only; the real model request owns cold loading. Default true. */\n\tloadModel?: boolean;\n}\n\nexport interface RuntimeReservation {\n\tmodel: string;\n\tbytes: number;\n\tpriority: number;\n\t/** Optional runtime identity; omitted means any adapter serving this model. */\n\tadapterId?: string;\n}\n\nexport interface RuntimeEvictionRecord {\n\tevicted: string;\n\tloaded: string;\n\tatMs: number;\n\t/** Optional identities preserve anti-thrash separation across runtime adapters. */\n\tevictedAdapterId?: string;\n\tloadedAdapterId?: string;\n}\n\nexport type RuntimeResidencyPlan =\n\t| { status: \"fits\"; evict: RuntimeResidentModel[]; requiredBytes: number; budgetBytes: number }\n\t| { status: \"refuse\"; reason: string; evict: RuntimeResidentModel[]; requiredBytes: number; budgetBytes: number };\n\nexport interface RuntimeResidencyAdapter {\n\tid: string;\n\tresidencyControl: ResidencyControl;\n\tlist(): Promise<RuntimeResidentModel[]>;\n\tensureResident(model: string): Promise<void>;\n\trelease(model: string): Promise<void>;\n}\n\nexport class RuntimeResidencyArbiter {\n\tprivate readonly budgetBytes: number;\n\tprivate readonly adapters: Map<string, RuntimeResidencyAdapter>;\n\n\tconstructor(args: { budgetBytes: number; adapters: RuntimeResidencyAdapter[] }) {\n\t\tthis.budgetBytes = args.budgetBytes;\n\t\tthis.adapters = new Map(args.adapters.map((adapter) => [adapter.id, adapter]));\n\t}\n\n\tasync ensureResident(adapterId: string, request: RuntimeLoadRequest): Promise<RuntimeResidencyPlan> {\n\t\tconst adapter = this.adapters.get(adapterId);\n\t\tconst residents = (await Promise.all([...this.adapters.values()].map((entry) => entry.list()))).flat();\n\t\tif (!adapter) {\n\t\t\treturn {\n\t\t\t\tstatus: \"refuse\",\n\t\t\t\treason: \"unknown-adapter\",\n\t\t\t\tevict: [],\n\t\t\t\trequiredBytes: request.bytes,\n\t\t\t\tbudgetBytes: this.budgetBytes,\n\t\t\t};\n\t\t}\n\t\tconst requestForAdapter: RuntimeLoadRequest = {\n\t\t\t...request,\n\t\t\tadapterId,\n\t\t\t...(request.pinActiveModel !== undefined && request.pinActiveAdapterId === undefined\n\t\t\t\t? { pinActiveAdapterId: adapterId }\n\t\t\t\t: {}),\n\t\t};\n\t\tconst plan = planRuntimeResidency({ budgetBytes: this.budgetBytes, residents, request: requestForAdapter });\n\t\tif (plan.status !== \"fits\") return plan;\n\t\tfor (const resident of plan.evict) {\n\t\t\tawait this.adapters.get(resident.adapterId)?.release(resident.model);\n\t\t}\n\t\tconst alreadyResident = residents.some(\n\t\t\t(resident) => resident.adapterId === adapterId && resident.model === request.model,\n\t\t);\n\t\tif (request.loadModel !== false && !alreadyResident) await adapter.ensureResident(request.model);\n\t\treturn plan;\n\t}\n}\n\nexport class OllamaRuntimeResidencyAdapter implements RuntimeResidencyAdapter {\n\treadonly id: string;\n\treadonly residencyControl = \"keep-alive\" as const;\n\tprivate readonly runtime: Pick<OllamaRuntime, \"listResidentModels\" | \"ensureResident\" | \"releaseResident\">;\n\tprivate readonly nowMs: () => number;\n\n\tconstructor(\n\t\tid: string,\n\t\truntime: Pick<OllamaRuntime, \"listResidentModels\" | \"ensureResident\" | \"releaseResident\">,\n\t\toptions: { nowMs?: () => number } = {},\n\t) {\n\t\tthis.id = id;\n\t\tthis.runtime = runtime;\n\t\tthis.nowMs = options.nowMs ?? Date.now;\n\t}\n\n\tasync list(): Promise<RuntimeResidentModel[]> {\n\t\tconst now = this.nowMs();\n\t\treturn (await this.runtime.listResidentModels()).map((model) => ({\n\t\t\tadapterId: this.id,\n\t\t\tmodel: model.name,\n\t\t\tbytes: model.sizeBytes,\n\t\t\tlastUsedAtMs: now,\n\t\t\tpriority: 10,\n\t\t\tresidencyControl: this.residencyControl,\n\t\t}));\n\t}\n\n\tasync ensureResident(model: string): Promise<void> {\n\t\tconst result = await this.runtime.ensureResident(model);\n\t\tif (!result.ok) throw new Error(result.error);\n\t}\n\n\tasync release(model: string): Promise<void> {\n\t\tawait this.runtime.releaseResident(model);\n\t}\n}\n\nexport class TransformersRuntimeResidencyAdapter implements RuntimeResidencyAdapter {\n\treadonly id: string;\n\treadonly residencyControl = \"full\" as const;\n\tprivate readonly runtime: Pick<TransformersRuntime, \"detect\" | \"start\" | \"stop\">;\n\tprivate readonly model: string;\n\tprivate readonly bytes: number;\n\tprivate readonly nowMs: () => number;\n\n\tconstructor(\n\t\tid: string,\n\t\truntime: Pick<TransformersRuntime, \"detect\" | \"start\" | \"stop\">,\n\t\tmodel: string,\n\t\tbytes: number,\n\t\toptions: { nowMs?: () => number } = {},\n\t) {\n\t\tthis.id = id;\n\t\tthis.runtime = runtime;\n\t\tthis.model = model;\n\t\tthis.bytes = bytes;\n\t\tthis.nowMs = options.nowMs ?? Date.now;\n\t}\n\n\tasync list(): Promise<RuntimeResidentModel[]> {\n\t\tconst status = await this.runtime.detect();\n\t\tif (!status.serverUp) return [];\n\t\treturn [\n\t\t\t{\n\t\t\t\tadapterId: this.id,\n\t\t\t\tmodel: this.model,\n\t\t\t\tbytes: this.bytes,\n\t\t\t\tlastUsedAtMs: this.nowMs(),\n\t\t\t\tpriority: 10,\n\t\t\t\tresidencyControl: this.residencyControl,\n\t\t\t},\n\t\t];\n\t}\n\n\tasync ensureResident(_model: string): Promise<void> {\n\t\tconst result = await this.runtime.start();\n\t\tif (!result.started && result.reason !== \"already_running\") throw new Error(result.reason);\n\t}\n\n\tasync release(_model: string): Promise<void> {\n\t\tthis.runtime.stop();\n\t}\n}\n\nexport class AdvisoryRuntimeResidencyAdapter implements RuntimeResidencyAdapter {\n\treadonly id: string;\n\treadonly residencyControl = \"advisory\" as const;\n\tprivate readonly residents: RuntimeResidentModel[];\n\n\tconstructor(id: string, residents: Omit<RuntimeResidentModel, \"adapterId\" | \"residencyControl\">[]) {\n\t\tthis.id = id;\n\t\tthis.residents = residents.map((resident) => ({\n\t\t\t...resident,\n\t\t\tadapterId: id,\n\t\t\tresidencyControl: this.residencyControl,\n\t\t}));\n\t}\n\n\tasync list(): Promise<RuntimeResidentModel[]> {\n\t\treturn [...this.residents];\n\t}\n\n\tasync ensureResident(_model: string): Promise<void> {}\n\n\tasync release(_model: string): Promise<void> {}\n}\n\nexport function planRuntimeResidency(args: {\n\tbudgetBytes: number;\n\tresidents: readonly RuntimeResidentModel[];\n\trequest: RuntimeLoadRequest;\n}): RuntimeResidencyPlan {\n\tconst requestedModels = new Map<string, { model: string; adapterId?: string; bytes: number; priority: number }>();\n\tconst addRequestedModel = (entry: { model: string; adapterId?: string; bytes: number; priority: number }): void => {\n\t\tconst key = `${entry.adapterId ?? \"*\"}\\0${entry.model}`;\n\t\tconst existing = requestedModels.get(key);\n\t\tif (!existing || entry.bytes > existing.bytes) requestedModels.set(key, entry);\n\t};\n\taddRequestedModel(args.request);\n\tfor (const reservation of args.request.reservations ?? []) {\n\t\taddRequestedModel({\n\t\t\t...reservation,\n\t\t\tadapterId:\n\t\t\t\treservation.adapterId ?? (reservation.model === args.request.model ? args.request.adapterId : undefined),\n\t\t});\n\t}\n\n\tconst isResidentForRequest = (\n\t\tresident: RuntimeResidentModel,\n\t\trequested: { model: string; adapterId?: string },\n\t): boolean =>\n\t\tresident.model === requested.model &&\n\t\t(requested.adapterId === undefined || resident.adapterId === requested.adapterId);\n\tconst residentBytes = args.residents.reduce((sum, resident) => sum + resident.bytes, 0);\n\tconst missingBytes = [...requestedModels.values()].reduce((sum, requested) => {\n\t\treturn args.residents.some((resident) => isResidentForRequest(resident, requested)) ? sum : sum + requested.bytes;\n\t}, 0);\n\tconst requiredBytes = residentBytes + missingBytes;\n\tif (requiredBytes <= args.budgetBytes) {\n\t\treturn { status: \"fits\", evict: [], requiredBytes, budgetBytes: args.budgetBytes };\n\t}\n\n\tif (hasPingPongRisk(args.request)) {\n\t\treturn { status: \"refuse\", reason: \"anti-thrash\", evict: [], requiredBytes, budgetBytes: args.budgetBytes };\n\t}\n\n\tlet projectedBytes = requiredBytes;\n\tconst evict: RuntimeResidentModel[] = [];\n\tconst candidates = args.residents\n\t\t.filter(\n\t\t\t(resident) => ![...requestedModels.values()].some((requested) => isResidentForRequest(resident, requested)),\n\t\t)\n\t\t.filter(\n\t\t\t(resident) =>\n\t\t\t\t!resident.pinned &&\n\t\t\t\t!(\n\t\t\t\t\tresident.model === args.request.pinActiveModel &&\n\t\t\t\t\t(args.request.pinActiveAdapterId === undefined || resident.adapterId === args.request.pinActiveAdapterId)\n\t\t\t\t),\n\t\t)\n\t\t.filter((resident) => resident.residencyControl !== \"advisory\")\n\t\t.filter(\n\t\t\t(resident) =>\n\t\t\t\targs.request.minDwellMs === undefined ||\n\t\t\t\targs.request.nowMs - resident.lastUsedAtMs >= args.request.minDwellMs,\n\t\t)\n\t\t.sort((left, right) => left.priority - right.priority || left.lastUsedAtMs - right.lastUsedAtMs);\n\tfor (const candidate of candidates) {\n\t\tevict.push(candidate);\n\t\tprojectedBytes -= candidate.bytes;\n\t\tif (projectedBytes <= args.budgetBytes) {\n\t\t\treturn { status: \"fits\", evict, requiredBytes, budgetBytes: args.budgetBytes };\n\t\t}\n\t}\n\treturn {\n\t\tstatus: \"refuse\",\n\t\treason: \"insufficient-evictable-memory\",\n\t\tevict,\n\t\trequiredBytes,\n\t\tbudgetBytes: args.budgetBytes,\n\t};\n}\n\nfunction hasPingPongRisk(request: RuntimeLoadRequest): boolean {\n\treturn (request.recentEvictions ?? []).some(\n\t\t(record) =>\n\t\t\trecord.evicted === request.model &&\n\t\t\t(request.adapterId === undefined ||\n\t\t\t\trecord.evictedAdapterId === undefined ||\n\t\t\t\trecord.evictedAdapterId === request.adapterId) &&\n\t\t\trequest.nowMs - record.atMs <= (request.minDwellMs ?? 0),\n\t);\n}\n"]}
1
+ {"version":3,"file":"runtime-arbiter.js","sourceRoot":"","sources":["../../../src/core/models/runtime-arbiter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OllamaRuntime, TransformersRuntime } from \"./local-runtime.ts\";\n\nexport type ResidencyControl = \"full\" | \"keep-alive\" | \"advisory\";\nexport type RuntimeRole = \"active\" | \"router\" | \"bench\" | \"probe\";\n\nexport interface RuntimeResidentModel {\n\tadapterId: string;\n\tmodel: string;\n\tbytes: number;\n\tlastUsedAtMs: number;\n\tpriority: number;\n\tpinned?: boolean;\n\tresidencyControl: ResidencyControl;\n}\n\nexport interface RuntimeLoadRequest {\n\tmodel: string;\n\tbytes: number;\n\trole: RuntimeRole;\n\tpriority: number;\n\tnowMs: number;\n\t/** Refuse an immediate reload of a model that was just evicted for the current resident. */\n\tantiThrashWindowMs?: number;\n\t/** Protect every newly-used resident from eviction for this duration. */\n\tminDwellMs?: number;\n\tpinActiveModel?: string;\n\t/** Adapter identity for this request; set by RuntimeResidencyArbiter before planning. */\n\tadapterId?: string;\n\t/** When pinActiveModel is set, constrain that pin to this adapter when present. */\n\tpinActiveAdapterId?: string;\n\treservations?: RuntimeReservation[];\n\trecentEvictions?: RuntimeEvictionRecord[];\n\t/** False performs admission/eviction only; the real model request owns cold loading. Default true. */\n\tloadModel?: boolean;\n}\n\nexport interface RuntimeReservation {\n\tmodel: string;\n\tbytes: number;\n\tpriority: number;\n\t/** Optional runtime identity; omitted means any adapter serving this model. */\n\tadapterId?: string;\n}\n\nexport interface RuntimeEvictionRecord {\n\tevicted: string;\n\tloaded: string;\n\tatMs: number;\n\t/** Optional identities preserve anti-thrash separation across runtime adapters. */\n\tevictedAdapterId?: string;\n\tloadedAdapterId?: string;\n}\n\nexport type RuntimeResidencyPlan =\n\t| { status: \"fits\"; evict: RuntimeResidentModel[]; requiredBytes: number; budgetBytes: number }\n\t| { status: \"refuse\"; reason: string; evict: RuntimeResidentModel[]; requiredBytes: number; budgetBytes: number };\n\nexport interface RuntimeResidencyAdapter {\n\tid: string;\n\tresidencyControl: ResidencyControl;\n\tlist(): Promise<RuntimeResidentModel[]>;\n\tensureResident(model: string): Promise<void>;\n\trelease(model: string): Promise<void>;\n}\n\nexport class RuntimeResidencyArbiter {\n\tprivate readonly budgetBytes: number;\n\tprivate readonly adapters: Map<string, RuntimeResidencyAdapter>;\n\n\tconstructor(args: { budgetBytes: number; adapters: RuntimeResidencyAdapter[] }) {\n\t\tthis.budgetBytes = args.budgetBytes;\n\t\tthis.adapters = new Map(args.adapters.map((adapter) => [adapter.id, adapter]));\n\t}\n\n\tasync ensureResident(adapterId: string, request: RuntimeLoadRequest): Promise<RuntimeResidencyPlan> {\n\t\tconst adapter = this.adapters.get(adapterId);\n\t\tconst residents = (await Promise.all([...this.adapters.values()].map((entry) => entry.list()))).flat();\n\t\tif (!adapter) {\n\t\t\treturn {\n\t\t\t\tstatus: \"refuse\",\n\t\t\t\treason: \"unknown-adapter\",\n\t\t\t\tevict: [],\n\t\t\t\trequiredBytes: request.bytes,\n\t\t\t\tbudgetBytes: this.budgetBytes,\n\t\t\t};\n\t\t}\n\t\tconst requestForAdapter: RuntimeLoadRequest = {\n\t\t\t...request,\n\t\t\tadapterId,\n\t\t\t...(request.pinActiveModel !== undefined && request.pinActiveAdapterId === undefined\n\t\t\t\t? { pinActiveAdapterId: adapterId }\n\t\t\t\t: {}),\n\t\t};\n\t\tconst plan = planRuntimeResidency({ budgetBytes: this.budgetBytes, residents, request: requestForAdapter });\n\t\tif (plan.status !== \"fits\") return plan;\n\t\tfor (const resident of plan.evict) {\n\t\t\tawait this.adapters.get(resident.adapterId)?.release(resident.model);\n\t\t}\n\t\tconst alreadyResident = residents.some(\n\t\t\t(resident) => resident.adapterId === adapterId && resident.model === request.model,\n\t\t);\n\t\tif (request.loadModel !== false && !alreadyResident) await adapter.ensureResident(request.model);\n\t\treturn plan;\n\t}\n}\n\nexport class OllamaRuntimeResidencyAdapter implements RuntimeResidencyAdapter {\n\treadonly id: string;\n\treadonly residencyControl = \"keep-alive\" as const;\n\tprivate readonly runtime: Pick<OllamaRuntime, \"listResidentModels\" | \"ensureResident\" | \"releaseResident\">;\n\tprivate readonly nowMs: () => number;\n\n\tconstructor(\n\t\tid: string,\n\t\truntime: Pick<OllamaRuntime, \"listResidentModels\" | \"ensureResident\" | \"releaseResident\">,\n\t\toptions: { nowMs?: () => number } = {},\n\t) {\n\t\tthis.id = id;\n\t\tthis.runtime = runtime;\n\t\tthis.nowMs = options.nowMs ?? Date.now;\n\t}\n\n\tasync list(): Promise<RuntimeResidentModel[]> {\n\t\tconst now = this.nowMs();\n\t\treturn (await this.runtime.listResidentModels()).map((model) => ({\n\t\t\tadapterId: this.id,\n\t\t\tmodel: model.name,\n\t\t\tbytes: model.sizeBytes,\n\t\t\tlastUsedAtMs: now,\n\t\t\tpriority: 10,\n\t\t\tresidencyControl: this.residencyControl,\n\t\t}));\n\t}\n\n\tasync ensureResident(model: string): Promise<void> {\n\t\tconst result = await this.runtime.ensureResident(model);\n\t\tif (!result.ok) throw new Error(result.error);\n\t}\n\n\tasync release(model: string): Promise<void> {\n\t\tawait this.runtime.releaseResident(model);\n\t}\n}\n\nexport class TransformersRuntimeResidencyAdapter implements RuntimeResidencyAdapter {\n\treadonly id: string;\n\treadonly residencyControl = \"full\" as const;\n\tprivate readonly runtime: Pick<TransformersRuntime, \"detect\" | \"start\" | \"stop\">;\n\tprivate readonly model: string;\n\tprivate readonly bytes: number;\n\tprivate readonly nowMs: () => number;\n\n\tconstructor(\n\t\tid: string,\n\t\truntime: Pick<TransformersRuntime, \"detect\" | \"start\" | \"stop\">,\n\t\tmodel: string,\n\t\tbytes: number,\n\t\toptions: { nowMs?: () => number } = {},\n\t) {\n\t\tthis.id = id;\n\t\tthis.runtime = runtime;\n\t\tthis.model = model;\n\t\tthis.bytes = bytes;\n\t\tthis.nowMs = options.nowMs ?? Date.now;\n\t}\n\n\tasync list(): Promise<RuntimeResidentModel[]> {\n\t\tconst status = await this.runtime.detect();\n\t\tif (!status.serverUp) return [];\n\t\treturn [\n\t\t\t{\n\t\t\t\tadapterId: this.id,\n\t\t\t\tmodel: this.model,\n\t\t\t\tbytes: this.bytes,\n\t\t\t\tlastUsedAtMs: this.nowMs(),\n\t\t\t\tpriority: 10,\n\t\t\t\tresidencyControl: this.residencyControl,\n\t\t\t},\n\t\t];\n\t}\n\n\tasync ensureResident(_model: string): Promise<void> {\n\t\tconst result = await this.runtime.start();\n\t\tif (!result.started && result.reason !== \"already_running\") throw new Error(result.reason);\n\t}\n\n\tasync release(_model: string): Promise<void> {\n\t\tthis.runtime.stop();\n\t}\n}\n\nexport class AdvisoryRuntimeResidencyAdapter implements RuntimeResidencyAdapter {\n\treadonly id: string;\n\treadonly residencyControl = \"advisory\" as const;\n\tprivate readonly residents: RuntimeResidentModel[];\n\n\tconstructor(id: string, residents: Omit<RuntimeResidentModel, \"adapterId\" | \"residencyControl\">[]) {\n\t\tthis.id = id;\n\t\tthis.residents = residents.map((resident) => ({\n\t\t\t...resident,\n\t\t\tadapterId: id,\n\t\t\tresidencyControl: this.residencyControl,\n\t\t}));\n\t}\n\n\tasync list(): Promise<RuntimeResidentModel[]> {\n\t\treturn [...this.residents];\n\t}\n\n\tasync ensureResident(_model: string): Promise<void> {}\n\n\tasync release(_model: string): Promise<void> {}\n}\n\nexport function planRuntimeResidency(args: {\n\tbudgetBytes: number;\n\tresidents: readonly RuntimeResidentModel[];\n\trequest: RuntimeLoadRequest;\n}): RuntimeResidencyPlan {\n\tconst requestedModels = new Map<string, { model: string; adapterId?: string; bytes: number; priority: number }>();\n\tconst addRequestedModel = (entry: { model: string; adapterId?: string; bytes: number; priority: number }): void => {\n\t\tconst key = `${entry.adapterId ?? \"*\"}\\0${entry.model}`;\n\t\tconst existing = requestedModels.get(key);\n\t\tif (!existing || entry.bytes > existing.bytes) requestedModels.set(key, entry);\n\t};\n\taddRequestedModel(args.request);\n\tfor (const reservation of args.request.reservations ?? []) {\n\t\taddRequestedModel({\n\t\t\t...reservation,\n\t\t\tadapterId:\n\t\t\t\treservation.adapterId ?? (reservation.model === args.request.model ? args.request.adapterId : undefined),\n\t\t});\n\t}\n\n\tconst isResidentForRequest = (\n\t\tresident: RuntimeResidentModel,\n\t\trequested: { model: string; adapterId?: string },\n\t): boolean =>\n\t\tresident.model === requested.model &&\n\t\t(requested.adapterId === undefined || resident.adapterId === requested.adapterId);\n\tconst residentBytes = args.residents.reduce((sum, resident) => sum + resident.bytes, 0);\n\tconst missingBytes = [...requestedModels.values()].reduce((sum, requested) => {\n\t\treturn args.residents.some((resident) => isResidentForRequest(resident, requested)) ? sum : sum + requested.bytes;\n\t}, 0);\n\tconst requiredBytes = residentBytes + missingBytes;\n\tif (requiredBytes <= args.budgetBytes) {\n\t\treturn { status: \"fits\", evict: [], requiredBytes, budgetBytes: args.budgetBytes };\n\t}\n\n\tif (hasPingPongRisk(args.request)) {\n\t\treturn { status: \"refuse\", reason: \"anti-thrash\", evict: [], requiredBytes, budgetBytes: args.budgetBytes };\n\t}\n\n\tlet projectedBytes = requiredBytes;\n\tconst evict: RuntimeResidentModel[] = [];\n\tconst candidates = args.residents\n\t\t.filter(\n\t\t\t(resident) => ![...requestedModels.values()].some((requested) => isResidentForRequest(resident, requested)),\n\t\t)\n\t\t.filter(\n\t\t\t(resident) =>\n\t\t\t\t!resident.pinned &&\n\t\t\t\t!(\n\t\t\t\t\tresident.model === args.request.pinActiveModel &&\n\t\t\t\t\t(args.request.pinActiveAdapterId === undefined || resident.adapterId === args.request.pinActiveAdapterId)\n\t\t\t\t),\n\t\t)\n\t\t.filter((resident) => resident.residencyControl !== \"advisory\")\n\t\t.filter(\n\t\t\t(resident) =>\n\t\t\t\targs.request.minDwellMs === undefined ||\n\t\t\t\targs.request.nowMs - resident.lastUsedAtMs >= args.request.minDwellMs,\n\t\t)\n\t\t.sort((left, right) => left.priority - right.priority || left.lastUsedAtMs - right.lastUsedAtMs);\n\tfor (const candidate of candidates) {\n\t\tevict.push(candidate);\n\t\tprojectedBytes -= candidate.bytes;\n\t\tif (projectedBytes <= args.budgetBytes) {\n\t\t\treturn { status: \"fits\", evict, requiredBytes, budgetBytes: args.budgetBytes };\n\t\t}\n\t}\n\treturn {\n\t\tstatus: \"refuse\",\n\t\treason: \"insufficient-evictable-memory\",\n\t\tevict,\n\t\trequiredBytes,\n\t\tbudgetBytes: args.budgetBytes,\n\t};\n}\n\nfunction hasPingPongRisk(request: RuntimeLoadRequest): boolean {\n\tconst windowMs = request.antiThrashWindowMs ?? request.minDwellMs ?? 0;\n\treturn (request.recentEvictions ?? []).some(\n\t\t(record) =>\n\t\t\trecord.evicted === request.model &&\n\t\t\t(request.adapterId === undefined ||\n\t\t\t\trecord.evictedAdapterId === undefined ||\n\t\t\t\trecord.evictedAdapterId === request.adapterId) &&\n\t\t\trequest.nowMs - record.atMs <= windowMs,\n\t);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"research-lane-controller.d.ts","sourceRoot":"","sources":["../../../src/core/research/research-lane-controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"research-lane-controller.d.ts","sourceRoot":"","sources":["../../../src/core/research/research-lane-controller.ts"],"names":[],"mappings":"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"}
@@ -107,6 +107,7 @@ export class ResearchLaneController {
107
107
  const startedRecord = this.lanes.start({ type: "research", goalId: demand.goalId });
108
108
  this._persistedRunCount++;
109
109
  const deregisterInFlight = registerInFlightWork(this.deps.getAgentDir(), "lane", `research:${startedRecord.laneId}`);
110
+ let toolSurface;
110
111
  try {
111
112
  let spentUsage;
112
113
  const workspaceSources = await this.deps.collectWorkspaceSources({
@@ -115,17 +116,18 @@ export class ResearchLaneController {
115
116
  maxSources: settings.maxSources,
116
117
  });
117
118
  const maxUsd = clampLaneMaxUsd(settings.maxUsd, this.deps.getCapabilityEnvelope()?.maxEstimatedUsd);
118
- const toolSurface = createLaneToolSurface({
119
+ const activeToolSurface = createLaneToolSurface({
119
120
  cwd: this.deps.getCwd(),
120
121
  profile: laneProfile,
121
122
  deniedPaths: getPrivateLaneDeniedPaths(this.deps.getCwd(), this.deps.getAgentDir()),
122
123
  });
123
- this.warnUnboundToolGrants(laneProfile, toolSurface);
124
+ toolSurface = activeToolSurface;
125
+ this.warnUnboundToolGrants(laneProfile, activeToolSurface);
124
126
  const result = await runResearch({
125
127
  query: demand.query,
126
128
  context: demand.context,
127
129
  sources: workspaceSources,
128
- envelope: this.buildEnvelope(maxUsd, laneProfile, toolSurface),
130
+ envelope: this.buildEnvelope(maxUsd, laneProfile, activeToolSurface),
129
131
  maxUsd,
130
132
  maxSources: settings.maxSources,
131
133
  maxFindings: settings.maxFindings,
@@ -142,9 +144,9 @@ export class ResearchLaneController {
142
144
  model,
143
145
  thinkingLevel: resolveModelThinkingLevel(model, laneProfile?.thinking),
144
146
  maxTokens: laneCapability.laneMaxOutputTokens,
145
- tools: toolSurface.tools,
147
+ tools: activeToolSurface.tools,
146
148
  maxTurns: 6,
147
- beforeToolCall: toolSurface.beforeToolCall,
149
+ beforeToolCall: activeToolSurface.beforeToolCall,
148
150
  signal,
149
151
  cacheRetention: "short",
150
152
  laneKind: "research",
@@ -199,6 +201,13 @@ export class ResearchLaneController {
199
201
  return { started: true, record };
200
202
  }
201
203
  finally {
204
+ try {
205
+ await toolSurface?.dispose();
206
+ }
207
+ catch (error) {
208
+ const message = error instanceof Error ? error.message : String(error);
209
+ this.deps.emit({ type: "warning", message: `Research lane payload cleanup failed: ${message}` });
210
+ }
202
211
  this._isRunning = false;
203
212
  deregisterInFlight();
204
213
  }