@goondocks/myco 0.4.0 → 0.4.2

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 (75) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CONTRIBUTING.md +4 -5
  4. package/README.md +1 -1
  5. package/dist/{chunk-P7RNAYU7.js → chunk-67R6EMYD.js} +61 -9
  6. package/dist/chunk-67R6EMYD.js.map +1 -0
  7. package/dist/{chunk-G6ZMTQMJ.js → chunk-6UJWI4IW.js} +3 -1
  8. package/dist/{chunk-G6ZMTQMJ.js.map → chunk-6UJWI4IW.js.map} +1 -1
  9. package/dist/{chunk-S7EIHYE7.js → chunk-FPEDTLQ6.js} +3 -3
  10. package/dist/{chunk-S7EIHYE7.js.map → chunk-FPEDTLQ6.js.map} +1 -1
  11. package/dist/{chunk-XHWIIU5D.js → chunk-GFBG73P4.js} +2 -2
  12. package/dist/{chunk-TZDDXRHG.js → chunk-I7PNZEBO.js} +2 -2
  13. package/dist/{chunk-IVS5MYBL.js → chunk-IYFKPSRP.js} +2 -2
  14. package/dist/{chunk-NUA7UTIY.js → chunk-MIU3DKLN.js} +2 -2
  15. package/dist/{chunk-VYV5IFD6.js → chunk-T7OC6GH5.js} +2 -2
  16. package/dist/chunk-TBRZAJ7W.js +135 -0
  17. package/dist/chunk-TBRZAJ7W.js.map +1 -0
  18. package/dist/{chunk-4FCFRJIQ.js → chunk-UKWO26VI.js} +2 -2
  19. package/dist/{chunk-YZO22BBI.js → chunk-V2OWD2VV.js} +4 -4
  20. package/dist/{chunk-RDXTQ436.js → chunk-WBT5DWGC.js} +2 -2
  21. package/dist/{cli-ZN6VBA7V.js → cli-PMOFCZQL.js} +15 -15
  22. package/dist/{config-4GGMWGAF.js → config-5FGLQGCW.js} +3 -3
  23. package/dist/{detect-providers-5FU3BN5Q.js → detect-providers-IRL2TTLK.js} +2 -2
  24. package/dist/{init-7UXGDOFS.js → init-NUF5UBUJ.js} +6 -6
  25. package/dist/{main-6UPAIDGS.js → main-2XEBVUR6.js} +19 -10
  26. package/dist/{main-6UPAIDGS.js.map → main-2XEBVUR6.js.map} +1 -1
  27. package/dist/{rebuild-QDSYYCS7.js → rebuild-E6YFIRYZ.js} +6 -6
  28. package/dist/{reprocess-ZNUQCIS3.js → reprocess-7G7KQWCN.js} +9 -9
  29. package/dist/{restart-5UY2KV54.js → restart-ABW4ZK3P.js} +3 -3
  30. package/dist/{search-2VEN3XIG.js → search-MPD7SFK6.js} +5 -5
  31. package/dist/{server-OR5B4B7K.js → server-NZLZRITH.js} +9 -9
  32. package/dist/{session-start-TUITIUMB.js → session-start-YB4A4PZB.js} +4 -4
  33. package/dist/{setup-digest-ETCZAUIU.js → setup-digest-K732MGOJ.js} +5 -5
  34. package/dist/{setup-llm-DWEJE3JE.js → setup-llm-XCCH5LYD.js} +5 -5
  35. package/dist/src/cli.js +1 -1
  36. package/dist/src/daemon/main.js +1 -1
  37. package/dist/src/hooks/session-start.js +1 -1
  38. package/dist/src/hooks/stop.js +2 -2
  39. package/dist/src/mcp/server.js +1 -1
  40. package/dist/{stats-IVIXIKTS.js → stats-6G7SN5YZ.js} +3 -3
  41. package/dist/{verify-4H6CEE5T.js → verify-JFHQH55Z.js} +5 -5
  42. package/package.json +1 -1
  43. package/skills/myco/SKILL.md +5 -1
  44. package/skills/myco/references/cli-usage.md +322 -0
  45. package/skills/myco/references/vault-status.md +224 -0
  46. package/skills/setup/SKILL.md +146 -0
  47. package/skills/setup/references/model-recommendations.md +77 -0
  48. package/commands/init.md +0 -122
  49. package/commands/setup-llm.md +0 -114
  50. package/commands/status.md +0 -130
  51. package/dist/chunk-P7RNAYU7.js.map +0 -1
  52. package/dist/chunk-QQ36XEJP.js +0 -38
  53. package/dist/chunk-QQ36XEJP.js.map +0 -1
  54. /package/dist/{chunk-XHWIIU5D.js.map → chunk-GFBG73P4.js.map} +0 -0
  55. /package/dist/{chunk-TZDDXRHG.js.map → chunk-I7PNZEBO.js.map} +0 -0
  56. /package/dist/{chunk-IVS5MYBL.js.map → chunk-IYFKPSRP.js.map} +0 -0
  57. /package/dist/{chunk-NUA7UTIY.js.map → chunk-MIU3DKLN.js.map} +0 -0
  58. /package/dist/{chunk-VYV5IFD6.js.map → chunk-T7OC6GH5.js.map} +0 -0
  59. /package/dist/{chunk-4FCFRJIQ.js.map → chunk-UKWO26VI.js.map} +0 -0
  60. /package/dist/{chunk-YZO22BBI.js.map → chunk-V2OWD2VV.js.map} +0 -0
  61. /package/dist/{chunk-RDXTQ436.js.map → chunk-WBT5DWGC.js.map} +0 -0
  62. /package/dist/{cli-ZN6VBA7V.js.map → cli-PMOFCZQL.js.map} +0 -0
  63. /package/dist/{config-4GGMWGAF.js.map → config-5FGLQGCW.js.map} +0 -0
  64. /package/dist/{detect-providers-5FU3BN5Q.js.map → detect-providers-IRL2TTLK.js.map} +0 -0
  65. /package/dist/{init-7UXGDOFS.js.map → init-NUF5UBUJ.js.map} +0 -0
  66. /package/dist/{rebuild-QDSYYCS7.js.map → rebuild-E6YFIRYZ.js.map} +0 -0
  67. /package/dist/{reprocess-ZNUQCIS3.js.map → reprocess-7G7KQWCN.js.map} +0 -0
  68. /package/dist/{restart-5UY2KV54.js.map → restart-ABW4ZK3P.js.map} +0 -0
  69. /package/dist/{search-2VEN3XIG.js.map → search-MPD7SFK6.js.map} +0 -0
  70. /package/dist/{server-OR5B4B7K.js.map → server-NZLZRITH.js.map} +0 -0
  71. /package/dist/{session-start-TUITIUMB.js.map → session-start-YB4A4PZB.js.map} +0 -0
  72. /package/dist/{setup-digest-ETCZAUIU.js.map → setup-digest-K732MGOJ.js.map} +0 -0
  73. /package/dist/{setup-llm-DWEJE3JE.js.map → setup-llm-XCCH5LYD.js.map} +0 -0
  74. /package/dist/{stats-IVIXIKTS.js.map → stats-6G7SN5YZ.js.map} +0 -0
  75. /package/dist/{verify-4H6CEE5T.js.map → verify-JFHQH55Z.js.map} +0 -0
@@ -12,7 +12,7 @@
12
12
  "source": {
13
13
  "source": "npm",
14
14
  "package": "@goondocks/myco",
15
- "version": "0.3.7"
15
+ "version": "0.4.1"
16
16
  },
17
17
  "description": "Collective agent intelligence — captures session knowledge and serves it back via MCP",
18
18
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myco",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Collective agent intelligence — captures session knowledge and serves it back to your team via MCP",
5
5
  "author": {
6
6
  "name": "goondocks-co",
package/CONTRIBUTING.md CHANGED
@@ -12,7 +12,7 @@ claude plugin install myco@myco-plugins
12
12
  Then in any project:
13
13
 
14
14
  ```
15
- /myco-init
15
+ /myco-setup
16
16
  ```
17
17
 
18
18
  This sets up the vault, configures your LLM backend, and starts capturing session knowledge.
@@ -58,15 +58,15 @@ claude plugin install myco
58
58
  ### 3. Initialize the vault
59
59
 
60
60
  ```
61
- /myco-init
61
+ /myco-setup
62
62
  ```
63
63
 
64
64
  For dogfooding, the vault lives at `~/.myco/vaults/myco/` (configured via `MYCO_VAULT_DIR` in `.claude/settings.json`).
65
65
 
66
66
  ### 4. Verify
67
67
 
68
- ```
69
- /myco-status
68
+ ```bash
69
+ node dist/src/cli.js stats
70
70
  ```
71
71
 
72
72
  ## Development Workflow
@@ -95,7 +95,6 @@ myco/
95
95
  ├── .claude-plugin/ # Claude Code + VS Code plugin manifest + marketplace
96
96
  ├── .cursor-plugin/ # Cursor plugin manifest + marketplace
97
97
  ├── hooks/ # Hook registration shell scripts
98
- ├── commands/ # Slash commands (/myco-init, /myco-status, /myco-setup-llm)
99
98
  ├── skills/ # Agent skills
100
99
  ├── src/
101
100
  │ ├── agents/ # Agent adapters (Claude Code, Cursor) — transcript parsing + image capture
package/README.md CHANGED
@@ -23,7 +23,7 @@ claude plugin install myco@myco-plugins
23
23
 
24
24
  Then initialize in your project:
25
25
  ```
26
- > /myco-init
26
+ > /myco-setup
27
27
  ```
28
28
 
29
29
  The agent sets up your vault, configures intelligence, and starts capturing. Works with Claude Code and Cursor out of the box.
@@ -103,7 +103,9 @@ var OllamaBackend = class _OllamaBackend {
103
103
  // src/intelligence/lm-studio.ts
104
104
  var ENDPOINT_CHAT = "/api/v1/chat";
105
105
  var ENDPOINT_MODELS_LOAD = "/api/v1/models/load";
106
+ var ENDPOINT_MODELS_UNLOAD = "/api/v1/models/unload";
106
107
  var ENDPOINT_MODELS_LIST = "/v1/models";
108
+ var ENDPOINT_MODELS_NATIVE = "/api/v1/models";
107
109
  var ENDPOINT_EMBEDDINGS = "/v1/embeddings";
108
110
  var LmStudioBackend = class _LmStudioBackend {
109
111
  static DEFAULT_BASE_URL = "http://localhost:1234";
@@ -180,18 +182,29 @@ var LmStudioBackend = class _LmStudioBackend {
180
182
  return { embedding, model: data.model, dimensions: embedding.length };
181
183
  }
182
184
  /**
183
- * Load the model with specific settings for digest operations.
184
- * Creates a dedicated instance and captures the instance_id so subsequent
185
- * chat requests target it directly (avoiding auto-load side effects).
186
- * Does not unload other instances hooks and other providers may be
187
- * using the same model with different settings.
185
+ * Ensure a model instance is loaded with the desired settings.
186
+ * First checks for an existing compatible instance to reuse (prevents
187
+ * accumulation across daemon restarts), then loads a new one only if needed.
188
+ * Unloads incompatible instances of the same model to prevent resource exhaustion.
188
189
  */
189
190
  async ensureLoaded(contextLength, gpuKvCache) {
190
191
  const ctx = contextLength ?? this.contextWindow;
192
+ const kvCache = gpuKvCache ?? false;
193
+ const instances = await this.getLoadedInstances();
194
+ for (const instance of instances) {
195
+ const matchesContext = !ctx || instance.config.context_length === ctx;
196
+ const matchesKvCache = instance.config.offload_kv_cache_to_gpu === kvCache;
197
+ if (matchesContext && matchesKvCache) {
198
+ this.loadedInstanceId = instance.id;
199
+ await this.unloadIncompatibleInstances(instances, ctx, kvCache);
200
+ return;
201
+ }
202
+ }
203
+ await this.unloadIncompatibleInstances(instances, ctx, kvCache);
191
204
  const body = {
192
205
  model: this.model,
193
206
  flash_attention: true,
194
- offload_kv_cache_to_gpu: gpuKvCache ?? false
207
+ offload_kv_cache_to_gpu: kvCache
195
208
  };
196
209
  if (ctx) {
197
210
  body.context_length = ctx;
@@ -207,8 +220,47 @@ var LmStudioBackend = class _LmStudioBackend {
207
220
  throw new Error(`LM Studio model load failed: ${response.status} ${errorBody.slice(0, 200)}`);
208
221
  }
209
222
  const loadResult = await response.json();
210
- if (loadResult.instance_id) {
211
- this.loadedInstanceId = loadResult.instance_id;
223
+ const instanceId = loadResult.id ?? loadResult.instance_id ?? loadResult.model_instance_id;
224
+ if (instanceId) {
225
+ this.loadedInstanceId = instanceId;
226
+ }
227
+ }
228
+ /**
229
+ * Query the LM Studio native API for loaded instances of this model.
230
+ * Returns an empty array if the API is unavailable or the model has no loaded instances.
231
+ */
232
+ async getLoadedInstances() {
233
+ try {
234
+ const response = await fetch(`${this.baseUrl}${ENDPOINT_MODELS_NATIVE}`, {
235
+ signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS)
236
+ });
237
+ if (!response.ok) return [];
238
+ const data = await response.json();
239
+ const entry = data.models.find((m) => m.key === this.model);
240
+ return entry?.loaded_instances ?? [];
241
+ } catch {
242
+ return [];
243
+ }
244
+ }
245
+ /**
246
+ * Unload instances of this model that don't match the desired settings.
247
+ * Best-effort — failures are silently ignored to avoid blocking the load path.
248
+ */
249
+ async unloadIncompatibleInstances(instances, contextLength, gpuKvCache) {
250
+ for (const instance of instances) {
251
+ const matchesContext = !contextLength || instance.config.context_length === contextLength;
252
+ const matchesKvCache = instance.config.offload_kv_cache_to_gpu === gpuKvCache;
253
+ if (!matchesContext || !matchesKvCache) {
254
+ try {
255
+ await fetch(`${this.baseUrl}${ENDPOINT_MODELS_UNLOAD}`, {
256
+ method: "POST",
257
+ headers: { "Content-Type": "application/json" },
258
+ body: JSON.stringify({ model: instance.id }),
259
+ signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS)
260
+ });
261
+ } catch {
262
+ }
263
+ }
212
264
  }
213
265
  }
214
266
  async isAvailable() {
@@ -239,4 +291,4 @@ export {
239
291
  OllamaBackend,
240
292
  LmStudioBackend
241
293
  };
242
- //# sourceMappingURL=chunk-P7RNAYU7.js.map
294
+ //# sourceMappingURL=chunk-67R6EMYD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/intelligence/ollama.ts","../src/intelligence/lm-studio.ts"],"sourcesContent":["import type { LlmProvider, EmbeddingProvider, LlmResponse, EmbeddingResponse, LlmRequestOptions } from './llm.js';\nimport { estimateTokens, LLM_REQUEST_TIMEOUT_MS, EMBEDDING_REQUEST_TIMEOUT_MS, DAEMON_CLIENT_TIMEOUT_MS } from '../constants.js';\n\ninterface OllamaConfig {\n model?: string;\n base_url?: string;\n context_window?: number;\n max_tokens?: number;\n // Legacy fields (ignored, kept for backward compat during migration)\n embedding_model?: string;\n summary_model?: string;\n}\n\n// Ollama API endpoints\nconst ENDPOINT_GENERATE = '/api/generate';\nconst ENDPOINT_EMBED = '/api/embed';\nconst ENDPOINT_TAGS = '/api/tags';\n\nexport class OllamaBackend implements LlmProvider, EmbeddingProvider {\n static readonly DEFAULT_BASE_URL = 'http://localhost:11434';\n readonly name = 'ollama';\n private baseUrl: string;\n private model: string;\n private contextWindow: number;\n private defaultMaxTokens: number;\n\n constructor(config?: OllamaConfig) {\n this.baseUrl = config?.base_url ?? OllamaBackend.DEFAULT_BASE_URL;\n this.model = config?.model ?? config?.summary_model ?? 'llama3.2';\n this.contextWindow = config?.context_window ?? 8192;\n this.defaultMaxTokens = config?.max_tokens ?? 1024;\n }\n\n async summarize(prompt: string, opts?: LlmRequestOptions): Promise<LlmResponse> {\n const maxTokens = opts?.maxTokens ?? this.defaultMaxTokens;\n const contextLength = opts?.contextLength ?? this.contextWindow;\n const promptTokens = estimateTokens(prompt);\n const numCtx = Math.max(promptTokens + maxTokens, contextLength);\n\n const body: Record<string, unknown> = {\n model: this.model,\n prompt,\n stream: false,\n options: {\n num_ctx: numCtx,\n num_predict: maxTokens,\n },\n };\n\n // System prompt — sent as a separate field instead of concatenated into prompt\n if (opts?.systemPrompt) {\n body.system = opts.systemPrompt;\n }\n\n // Thinking control — false suppresses chain-of-thought for reasoning models\n if (opts?.reasoning) {\n body.think = opts.reasoning === 'off' ? false : opts.reasoning;\n }\n\n // Keep model loaded between requests (useful for digest cycles)\n if (opts?.keepAlive) {\n body.keep_alive = opts.keepAlive;\n }\n\n const response = await fetch(`${this.baseUrl}${ENDPOINT_GENERATE}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(opts?.timeoutMs ?? LLM_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`Ollama summarize failed: ${response.status} ${errorBody.slice(0, 500)}`);\n }\n\n const data = await response.json() as { response: string; model: string };\n return { text: data.response, model: data.model };\n }\n\n async embed(text: string): Promise<EmbeddingResponse> {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_EMBED}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.model,\n input: text,\n }),\n signal: AbortSignal.timeout(EMBEDDING_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n throw new Error(`Ollama embed failed: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json() as { embeddings: number[][]; model: string };\n const embedding = data.embeddings[0];\n return { embedding, model: data.model, dimensions: embedding.length };\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_TAGS}`, {\n signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /** List available models on this Ollama instance. */\n async listModels(timeoutMs?: number): Promise<string[]> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_TAGS}`, {\n signal: AbortSignal.timeout(timeoutMs ?? DAEMON_CLIENT_TIMEOUT_MS),\n });\n const data = await response.json() as { models: Array<{ name: string }> };\n return data.models.map((m) => m.name);\n } catch {\n return [];\n }\n }\n}\n","import type { LlmProvider, EmbeddingProvider, LlmResponse, EmbeddingResponse, LlmRequestOptions } from './llm.js';\nimport { LLM_REQUEST_TIMEOUT_MS, EMBEDDING_REQUEST_TIMEOUT_MS, DAEMON_CLIENT_TIMEOUT_MS } from '../constants.js';\n\ninterface LmStudioConfig {\n model?: string;\n base_url?: string;\n context_window?: number;\n max_tokens?: number;\n // Legacy fields\n embedding_model?: string;\n summary_model?: string;\n}\n\n// LM Studio API endpoints\nconst ENDPOINT_CHAT = '/api/v1/chat';\nconst ENDPOINT_MODELS_LOAD = '/api/v1/models/load';\nconst ENDPOINT_MODELS_UNLOAD = '/api/v1/models/unload';\nconst ENDPOINT_MODELS_LIST = '/v1/models';\nconst ENDPOINT_MODELS_NATIVE = '/api/v1/models';\nconst ENDPOINT_EMBEDDINGS = '/v1/embeddings';\n\n/** Shape of a loaded instance from the LM Studio native models API. */\ninterface NativeLoadedInstance {\n id: string;\n config: {\n context_length: number;\n flash_attention: boolean;\n offload_kv_cache_to_gpu: boolean;\n };\n}\n\n/** Shape of a model entry from the LM Studio native models API. */\ninterface NativeModelEntry {\n type: string;\n key: string;\n loaded_instances: NativeLoadedInstance[];\n}\n\nexport class LmStudioBackend implements LlmProvider, EmbeddingProvider {\n static readonly DEFAULT_BASE_URL = 'http://localhost:1234';\n readonly name = 'lm-studio';\n private baseUrl: string;\n private model: string;\n private loadedInstanceId: string | null = null;\n private contextWindow: number | undefined;\n private defaultMaxTokens: number;\n\n constructor(config?: LmStudioConfig) {\n this.baseUrl = config?.base_url ?? LmStudioBackend.DEFAULT_BASE_URL;\n this.model = config?.model ?? config?.summary_model ?? 'llama3.2';\n this.contextWindow = config?.context_window;\n this.defaultMaxTokens = config?.max_tokens ?? 1024;\n }\n\n /**\n * Generate text using LM Studio's native REST API (/api/v1/chat).\n * Supports per-request context_length, reasoning control, and system_prompt.\n */\n async summarize(prompt: string, opts?: LlmRequestOptions): Promise<LlmResponse> {\n const maxTokens = opts?.maxTokens ?? this.defaultMaxTokens;\n\n const body: Record<string, unknown> = {\n model: this.loadedInstanceId ?? this.model,\n input: prompt,\n max_output_tokens: maxTokens,\n store: false,\n };\n\n // Only set context_length if we haven't pre-loaded the model\n // (pre-loaded models already have the correct context via ensureLoaded)\n if (!this.loadedInstanceId) {\n const contextLength = opts?.contextLength ?? this.contextWindow;\n if (contextLength) {\n body.context_length = contextLength;\n }\n }\n\n // System prompt — sent separately from user content\n if (opts?.systemPrompt) {\n body.system_prompt = opts.systemPrompt;\n }\n\n // Reasoning control — 'off' suppresses chain-of-thought for reasoning models\n if (opts?.reasoning) {\n body.reasoning = opts.reasoning;\n }\n\n const response = await fetch(`${this.baseUrl}${ENDPOINT_CHAT}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(opts?.timeoutMs ?? LLM_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`LM Studio summarize failed: ${response.status} ${errorBody.slice(0, 500)}`);\n }\n\n const data = await response.json() as {\n model_instance_id: string;\n output: Array<{ type: string; content: string }>;\n };\n const messageOutput = data.output.find((o) => o.type === 'message');\n const text = messageOutput?.content ?? '';\n return { text, model: data.model_instance_id };\n }\n\n /**\n * Generate embeddings using LM Studio's OpenAI-compatible endpoint.\n * (The native API doesn't have an embedding endpoint — OpenAI-compat is fine here.)\n */\n async embed(text: string): Promise<EmbeddingResponse> {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_EMBEDDINGS}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.model,\n input: text,\n }),\n signal: AbortSignal.timeout(EMBEDDING_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n throw new Error(`LM Studio embed failed: ${response.status}`);\n }\n\n const data = await response.json() as {\n data: Array<{ embedding: number[] }>;\n model: string;\n };\n const embedding = data.data[0].embedding;\n return { embedding, model: data.model, dimensions: embedding.length };\n }\n\n /**\n * Ensure a model instance is loaded with the desired settings.\n * First checks for an existing compatible instance to reuse (prevents\n * accumulation across daemon restarts), then loads a new one only if needed.\n * Unloads incompatible instances of the same model to prevent resource exhaustion.\n */\n async ensureLoaded(contextLength?: number, gpuKvCache?: boolean): Promise<void> {\n const ctx = contextLength ?? this.contextWindow;\n const kvCache = gpuKvCache ?? false;\n\n // Query native API for existing loaded instances of this model\n const instances = await this.getLoadedInstances();\n\n // Check for a compatible instance we can reuse\n for (const instance of instances) {\n const matchesContext = !ctx || instance.config.context_length === ctx;\n const matchesKvCache = instance.config.offload_kv_cache_to_gpu === kvCache;\n if (matchesContext && matchesKvCache) {\n this.loadedInstanceId = instance.id;\n // Unload any incompatible instances (best effort, don't block on failure)\n await this.unloadIncompatibleInstances(instances, ctx, kvCache);\n return;\n }\n }\n\n // Unload incompatible instances before loading to free resources\n await this.unloadIncompatibleInstances(instances, ctx, kvCache);\n\n // No compatible instance found — load a new one\n const body: Record<string, unknown> = {\n model: this.model,\n flash_attention: true,\n offload_kv_cache_to_gpu: kvCache,\n };\n if (ctx) {\n body.context_length = ctx;\n }\n\n const response = await fetch(`${this.baseUrl}${ENDPOINT_MODELS_LOAD}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(LLM_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`LM Studio model load failed: ${response.status} ${errorBody.slice(0, 200)}`);\n }\n\n // Capture instance ID — LM Studio may return it under different field names\n const loadResult = await response.json() as Record<string, unknown>;\n const instanceId = (loadResult.id ?? loadResult.instance_id ?? loadResult.model_instance_id) as string | undefined;\n if (instanceId) {\n this.loadedInstanceId = instanceId;\n }\n }\n\n /**\n * Query the LM Studio native API for loaded instances of this model.\n * Returns an empty array if the API is unavailable or the model has no loaded instances.\n */\n private async getLoadedInstances(): Promise<NativeLoadedInstance[]> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_MODELS_NATIVE}`, {\n signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS),\n });\n if (!response.ok) return [];\n\n const data = await response.json() as { models: NativeModelEntry[] };\n const entry = data.models.find((m) => m.key === this.model);\n return entry?.loaded_instances ?? [];\n } catch {\n return [];\n }\n }\n\n /**\n * Unload instances of this model that don't match the desired settings.\n * Best-effort — failures are silently ignored to avoid blocking the load path.\n */\n private async unloadIncompatibleInstances(\n instances: NativeLoadedInstance[],\n contextLength: number | undefined,\n gpuKvCache: boolean,\n ): Promise<void> {\n for (const instance of instances) {\n const matchesContext = !contextLength || instance.config.context_length === contextLength;\n const matchesKvCache = instance.config.offload_kv_cache_to_gpu === gpuKvCache;\n if (!matchesContext || !matchesKvCache) {\n try {\n await fetch(`${this.baseUrl}${ENDPOINT_MODELS_UNLOAD}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ model: instance.id }),\n signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS),\n });\n } catch {\n // Best effort — don't fail the load if cleanup fails\n }\n }\n }\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_MODELS_LIST}`, {\n signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /** List available models on this LM Studio instance. */\n async listModels(timeoutMs?: number): Promise<string[]> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_MODELS_LIST}`, {\n signal: AbortSignal.timeout(timeoutMs ?? DAEMON_CLIENT_TIMEOUT_MS),\n });\n const data = await response.json() as { data: Array<{ id: string }> };\n return data.data.map((m) => m.id);\n } catch {\n return [];\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -21144,6 +21144,8 @@ var DigestSchema = external_exports.object({
21144
21144
  });
21145
21145
  var MycoConfigSchema = external_exports.object({
21146
21146
  version: external_exports.literal(2),
21147
+ /** Tracks which migrations have been applied. Managed automatically. */
21148
+ config_version: external_exports.number().int().nonnegative().default(0),
21147
21149
  intelligence: IntelligenceSchema.default(() => IntelligenceSchema.parse({})),
21148
21150
  daemon: DaemonSchema.default(() => DaemonSchema.parse({})),
21149
21151
  capture: CaptureSchema.default(() => CaptureSchema.parse({})),
@@ -21176,4 +21178,4 @@ export {
21176
21178
  external_exports,
21177
21179
  MycoConfigSchema
21178
21180
  };
21179
- //# sourceMappingURL=chunk-G6ZMTQMJ.js.map
21181
+ //# sourceMappingURL=chunk-6UJWI4IW.js.map