@goondocks/myco 0.6.2 → 0.6.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 (98) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/{chunk-LDKXXKF6.js → chunk-2ZIBCEYO.js} +4 -4
  4. package/dist/{chunk-PQWQC3RF.js → chunk-4XVKZ3WA.js} +137 -146
  5. package/dist/chunk-4XVKZ3WA.js.map +1 -0
  6. package/dist/{chunk-MWW62YZP.js → chunk-7WHF2OIZ.js} +21 -19
  7. package/dist/chunk-7WHF2OIZ.js.map +1 -0
  8. package/dist/{chunk-JSK7L46L.js → chunk-ERG2IEWX.js} +22 -4
  9. package/dist/{chunk-JSK7L46L.js.map → chunk-ERG2IEWX.js.map} +1 -1
  10. package/dist/{chunk-RXJHB7W4.js → chunk-FPRXMJLT.js} +2 -2
  11. package/dist/{chunk-RY76WEN3.js → chunk-GENQ5QGP.js} +2 -2
  12. package/dist/{chunk-YG6MLLGL.js → chunk-HYVT345Y.js} +2 -2
  13. package/dist/{chunk-WBLTISAK.js → chunk-J4D4CROB.js} +32 -6
  14. package/dist/chunk-J4D4CROB.js.map +1 -0
  15. package/dist/{chunk-IWBWZQK6.js → chunk-MDLSAFPP.js} +2 -2
  16. package/dist/{chunk-HRGHDMYI.js → chunk-NL6WQO56.js} +2 -2
  17. package/dist/{chunk-V5R6O6RP.js → chunk-NLUE6CYG.js} +3 -3
  18. package/dist/{chunk-CQ4RKK67.js → chunk-O6PERU7U.js} +2 -2
  19. package/dist/{chunk-XNAM6Z4O.js → chunk-P723N2LP.js} +2 -2
  20. package/dist/{chunk-CK24O5YQ.js → chunk-QN4W3JUA.js} +2 -2
  21. package/dist/{chunk-ALBVNGCF.js → chunk-UP4P4OAA.js} +55 -44
  22. package/dist/{chunk-ALBVNGCF.js.map → chunk-UP4P4OAA.js.map} +1 -1
  23. package/dist/{chunk-CPVXNRGW.js → chunk-YIQLYIHW.js} +4 -4
  24. package/dist/{chunk-25DJSF2K.js → chunk-YTFXA4RX.js} +3 -3
  25. package/dist/{chunk-RNWALAFP.js → chunk-Z74SDEKE.js} +2 -2
  26. package/dist/chunk-Z74SDEKE.js.map +1 -0
  27. package/dist/{cli-LMBBPV2D.js → cli-IHILSS6N.js} +20 -20
  28. package/dist/{client-FDKJ4BY7.js → client-AGFNR2S4.js} +5 -5
  29. package/dist/{config-HDUFDOQN.js → config-IBS6KOLQ.js} +3 -3
  30. package/dist/{curate-DYE4VCBJ.js → curate-3D4GHKJH.js} +9 -10
  31. package/dist/{curate-DYE4VCBJ.js.map → curate-3D4GHKJH.js.map} +1 -1
  32. package/dist/{detect-providers-I2QQFDJW.js → detect-providers-XEP4QA3R.js} +3 -3
  33. package/dist/{digest-PNHFM7JJ.js → digest-7HLJXL77.js} +11 -11
  34. package/dist/{init-7N7F6W6U.js → init-ARQ53JOR.js} +8 -8
  35. package/dist/{main-3JZDUJLU.js → main-6AGPIMH2.js} +1972 -374
  36. package/dist/main-6AGPIMH2.js.map +1 -0
  37. package/dist/{rebuild-WXKQ5HZO.js → rebuild-Q2ACEB6F.js} +9 -10
  38. package/dist/{rebuild-WXKQ5HZO.js.map → rebuild-Q2ACEB6F.js.map} +1 -1
  39. package/dist/{reprocess-PKRDV67L.js → reprocess-CDEFGQOV.js} +11 -11
  40. package/dist/{restart-WSJRHRHI.js → restart-XCMILOL5.js} +6 -6
  41. package/dist/{search-SWMJ4MZ3.js → search-7W25SKCB.js} +6 -6
  42. package/dist/{server-NTRVB5ZM.js → server-6UDN35QN.js} +11 -11
  43. package/dist/{session-start-KQ4KCQMZ.js → session-start-K6IGAC7H.js} +9 -9
  44. package/dist/setup-digest-X5PN27F4.js +15 -0
  45. package/dist/setup-llm-S5OHQJXK.js +15 -0
  46. package/dist/src/cli.js +4 -4
  47. package/dist/src/daemon/main.js +4 -4
  48. package/dist/src/hooks/post-tool-use.js +5 -5
  49. package/dist/src/hooks/session-end.js +5 -5
  50. package/dist/src/hooks/session-start.js +4 -4
  51. package/dist/src/hooks/stop.js +7 -7
  52. package/dist/src/hooks/user-prompt-submit.js +5 -5
  53. package/dist/src/mcp/server.js +4 -4
  54. package/dist/src/prompts/extraction.md +4 -4
  55. package/dist/{stats-2OUQSEZO.js → stats-TTSDXGJV.js} +6 -6
  56. package/dist/ui/assets/index-08wKT7wS.css +1 -0
  57. package/dist/ui/assets/index-CMSMi4Jb.js +369 -0
  58. package/dist/ui/index.html +2 -2
  59. package/dist/{verify-MG5O7SBU.js → verify-TOWQHPBX.js} +6 -6
  60. package/dist/{version-NKOECSVH.js → version-36RVCQA6.js} +4 -4
  61. package/package.json +1 -1
  62. package/dist/chunk-MWW62YZP.js.map +0 -1
  63. package/dist/chunk-PQWQC3RF.js.map +0 -1
  64. package/dist/chunk-RNWALAFP.js.map +0 -1
  65. package/dist/chunk-WBLTISAK.js.map +0 -1
  66. package/dist/main-3JZDUJLU.js.map +0 -1
  67. package/dist/setup-digest-BOYOSM4B.js +0 -15
  68. package/dist/setup-llm-PCZ64ALK.js +0 -15
  69. package/dist/ui/assets/index-Bk4X_8-Z.css +0 -1
  70. package/dist/ui/assets/index-D3SY7ZHY.js +0 -299
  71. /package/dist/{chunk-LDKXXKF6.js.map → chunk-2ZIBCEYO.js.map} +0 -0
  72. /package/dist/{chunk-RXJHB7W4.js.map → chunk-FPRXMJLT.js.map} +0 -0
  73. /package/dist/{chunk-RY76WEN3.js.map → chunk-GENQ5QGP.js.map} +0 -0
  74. /package/dist/{chunk-YG6MLLGL.js.map → chunk-HYVT345Y.js.map} +0 -0
  75. /package/dist/{chunk-IWBWZQK6.js.map → chunk-MDLSAFPP.js.map} +0 -0
  76. /package/dist/{chunk-HRGHDMYI.js.map → chunk-NL6WQO56.js.map} +0 -0
  77. /package/dist/{chunk-V5R6O6RP.js.map → chunk-NLUE6CYG.js.map} +0 -0
  78. /package/dist/{chunk-CQ4RKK67.js.map → chunk-O6PERU7U.js.map} +0 -0
  79. /package/dist/{chunk-XNAM6Z4O.js.map → chunk-P723N2LP.js.map} +0 -0
  80. /package/dist/{chunk-CK24O5YQ.js.map → chunk-QN4W3JUA.js.map} +0 -0
  81. /package/dist/{chunk-CPVXNRGW.js.map → chunk-YIQLYIHW.js.map} +0 -0
  82. /package/dist/{chunk-25DJSF2K.js.map → chunk-YTFXA4RX.js.map} +0 -0
  83. /package/dist/{cli-LMBBPV2D.js.map → cli-IHILSS6N.js.map} +0 -0
  84. /package/dist/{client-FDKJ4BY7.js.map → client-AGFNR2S4.js.map} +0 -0
  85. /package/dist/{config-HDUFDOQN.js.map → config-IBS6KOLQ.js.map} +0 -0
  86. /package/dist/{detect-providers-I2QQFDJW.js.map → detect-providers-XEP4QA3R.js.map} +0 -0
  87. /package/dist/{digest-PNHFM7JJ.js.map → digest-7HLJXL77.js.map} +0 -0
  88. /package/dist/{init-7N7F6W6U.js.map → init-ARQ53JOR.js.map} +0 -0
  89. /package/dist/{reprocess-PKRDV67L.js.map → reprocess-CDEFGQOV.js.map} +0 -0
  90. /package/dist/{restart-WSJRHRHI.js.map → restart-XCMILOL5.js.map} +0 -0
  91. /package/dist/{search-SWMJ4MZ3.js.map → search-7W25SKCB.js.map} +0 -0
  92. /package/dist/{server-NTRVB5ZM.js.map → server-6UDN35QN.js.map} +0 -0
  93. /package/dist/{session-start-KQ4KCQMZ.js.map → session-start-K6IGAC7H.js.map} +0 -0
  94. /package/dist/{setup-digest-BOYOSM4B.js.map → setup-digest-X5PN27F4.js.map} +0 -0
  95. /package/dist/{setup-llm-PCZ64ALK.js.map → setup-llm-S5OHQJXK.js.map} +0 -0
  96. /package/dist/{stats-2OUQSEZO.js.map → stats-TTSDXGJV.js.map} +0 -0
  97. /package/dist/{verify-MG5O7SBU.js.map → verify-TOWQHPBX.js.map} +0 -0
  98. /package/dist/{version-NKOECSVH.js.map → version-36RVCQA6.js.map} +0 -0
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Myco</title>
8
- <script type="module" crossorigin src="/assets/index-D3SY7ZHY.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-Bk4X_8-Z.css">
8
+ <script type="module" crossorigin src="/assets/index-CMSMi4Jb.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-08wKT7wS.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -2,13 +2,13 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
2
2
  import {
3
3
  createEmbeddingProvider,
4
4
  createLlmProvider
5
- } from "./chunk-V5R6O6RP.js";
6
- import "./chunk-MWW62YZP.js";
5
+ } from "./chunk-NLUE6CYG.js";
6
+ import "./chunk-7WHF2OIZ.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-YG6MLLGL.js";
10
- import "./chunk-JSK7L46L.js";
11
- import "./chunk-WBLTISAK.js";
9
+ } from "./chunk-HYVT345Y.js";
10
+ import "./chunk-ERG2IEWX.js";
11
+ import "./chunk-J4D4CROB.js";
12
12
  import "./chunk-PZUWP5VK.js";
13
13
 
14
14
  // src/cli/verify.ts
@@ -47,4 +47,4 @@ async function run(_args, vaultDir) {
47
47
  export {
48
48
  run
49
49
  };
50
- //# sourceMappingURL=verify-MG5O7SBU.js.map
50
+ //# sourceMappingURL=verify-TOWQHPBX.js.map
@@ -1,11 +1,11 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  getPluginVersion
4
- } from "./chunk-CK24O5YQ.js";
5
- import "./chunk-RNWALAFP.js";
6
- import "./chunk-WBLTISAK.js";
4
+ } from "./chunk-QN4W3JUA.js";
5
+ import "./chunk-Z74SDEKE.js";
6
+ import "./chunk-J4D4CROB.js";
7
7
  import "./chunk-PZUWP5VK.js";
8
8
  export {
9
9
  getPluginVersion
10
10
  };
11
- //# sourceMappingURL=version-NKOECSVH.js.map
11
+ //# sourceMappingURL=version-36RVCQA6.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goondocks/myco",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Collective agent intelligence — Claude Code plugin",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1 +0,0 @@
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 { 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 defaultMaxTokens: number;\n private contextWindow: number | undefined;\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.defaultMaxTokens = config?.max_tokens ?? 1024;\n this.contextWindow = config?.context_window;\n }\n\n async summarize(prompt: string, opts?: LlmRequestOptions): Promise<LlmResponse> {\n const maxTokens = opts?.maxTokens ?? this.defaultMaxTokens;\n\n // Send num_ctx from config or per-call override. Ollama reloads the model\n // on num_ctx changes, but consistent values (same num_ctx every call)\n // only cause one reload on first use. Without this, Ollama falls back to\n // its model default (often 2048), ignoring the user's configured context.\n const contextLength = opts?.contextLength ?? this.contextWindow;\n const options: Record<string, unknown> = { num_predict: maxTokens };\n if (contextLength) {\n options.num_ctx = contextLength;\n }\n\n const body: Record<string, unknown> = {\n model: this.model,\n prompt,\n stream: true,\n options,\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 return this.readStream(response);\n }\n\n /** Read an Ollama streaming response (newline-delimited JSON) and accumulate the result. */\n private async readStream(response: Response): Promise<LlmResponse> {\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let text = '';\n let model = this.model;\n let buffer = '';\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.trim()) continue;\n const chunk = JSON.parse(line) as { response?: string; model?: string; error?: string };\n if (chunk.error) throw new Error(`Ollama stream error: ${chunk.error}`);\n text += chunk.response ?? '';\n if (chunk.model) model = chunk.model;\n }\n }\n\n // Process remaining buffer\n if (buffer.trim()) {\n const chunk = JSON.parse(buffer) as { response?: string; model?: string; error?: string };\n if (chunk.error) throw new Error(`Ollama stream error: ${chunk.error}`);\n text += chunk.response ?? '';\n if (chunk.model) model = chunk.model;\n }\n } finally {\n reader.releaseLock();\n }\n\n return { text, 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_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 instanceId: 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 * Routes to our specific instance by ID when available, with model name +\n * context_length as fallback. This ensures correct routing when multiple\n * daemons share the same LM Studio, and graceful degradation when our\n * instance is evicted by idle TTL.\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\n const body: Record<string, unknown> = {\n model: this.instanceId ?? this.model,\n input: prompt,\n max_output_tokens: maxTokens,\n store: false,\n };\n\n // Always send context_length — even when routing by instance ID.\n // If our instance was evicted and LM Studio auto-loads, this ensures\n // the replacement gets the correct context window.\n if (contextLength) {\n body.context_length = contextLength;\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 // If our instance was evicted, clear the ID so ensureLoaded\n // reloads on the next cycle instead of hitting a stale ID repeatedly\n if (response.status === 404 && this.instanceId) {\n this.instanceId = null;\n }\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 * Called every digest cycle (not cached) so it recovers from idle TTL eviction.\n *\n * The load API is necessary to control offload_kv_cache_to_gpu — a load-time\n * setting that cannot be set per-request via the chat API.\n *\n * Multi-daemon safe: finds or loads our own compatible instance without\n * touching instances from other daemons/projects. Routes by instance ID.\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 // Look for a compatible instance we can reuse (ours or anyone's)\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.instanceId = instance.id;\n return;\n }\n }\n\n // No compatible instance — load our own (don't touch others)\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 const loadResult = await response.json() as Record<string, unknown>;\n const id = (loadResult.instance_id ?? loadResult.id ?? loadResult.model_instance_id) as string | undefined;\n if (id) {\n this.instanceId = id;\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 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":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/daemon/digest.ts","../src/daemon/trace.ts","../src/daemon/processor.ts","../src/capture/transcript-miner.ts","../src/vault/observations.ts","../src/services/vault-ops.ts","../src/intelligence/batch.ts"],"sourcesContent":["/**\n * DigestEngine — synthesizes vault knowledge into tiered context extracts.\n * Metabolism — adaptive timer that throttles digest cycles based on activity.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport YAML from 'yaml';\n\nimport type { MycoIndex, IndexedNote } from '@myco/index/sqlite.js';\nimport type { LlmProvider, LlmRequestOptions } from '@myco/intelligence/llm.js';\nimport type { MycoConfig } from '@myco/config/schema.js';\nimport { loadPrompt } from '@myco/prompts/index.js';\nimport { stripReasoningTokens } from '@myco/intelligence/response.js';\nimport { stripFrontmatter } from '@myco/vault/frontmatter.js';\nimport { readLastTimestamp, appendTraceRecord } from './trace.js';\nimport {\n estimateTokens,\n CHARS_PER_TOKEN,\n DIGEST_TIER_MIN_CONTEXT,\n DIGEST_SUBSTRATE_TYPE_WEIGHTS,\n DIGEST_LLM_REQUEST_TIMEOUT_MS,\n LLM_REASONING_MODE,\n} from '@myco/constants.js';\n\n// --- Interfaces ---\n\nexport interface DigestCycleResult {\n cycleId: string;\n timestamp: string;\n substrate: {\n sessions: string[];\n spores: string[];\n plans: string[];\n artifacts: string[];\n team: string[];\n };\n tiersGenerated: number[];\n model: string;\n durationMs: number;\n tokensUsed: number;\n}\n\n/** Simple log function signature for digest progress reporting. */\nexport type DigestLogFn = (level: 'debug' | 'info' | 'warn', message: string, data?: Record<string, unknown>) => void;\n\nexport interface DigestCycleOptions {\n /** Process all substrate regardless of last cycle timestamp. */\n fullReprocess?: boolean;\n /** Only generate these tiers (default: all eligible). */\n tiers?: number[];\n /** Skip previous extract — start from clean slate. */\n cleanSlate?: boolean;\n}\n\nexport interface DigestEngineConfig {\n vaultDir: string;\n index: MycoIndex;\n llmProvider: LlmProvider;\n config: MycoConfig;\n log?: DigestLogFn;\n}\n\n// --- Constants ---\n\n/** Token overhead estimate for previous extract section wrapper. */\nconst PREVIOUS_EXTRACT_OVERHEAD_TOKENS = 50;\n\n/** Safety margin for context window — our CHARS_PER_TOKEN=4 heuristic significantly\n * underestimates real token counts (observed ~3.2 chars/token for mixed content).\n * 0.70 provides a safe buffer: 32K * 0.70 = 22.4K usable tokens. */\nconst CONTEXT_SAFETY_MARGIN = 0.70;\n\n/** Types that are digest output — excluded from substrate to avoid self-digestion. */\nconst EXTRACT_TYPE = 'extract';\n\n// --- DigestEngine ---\n\nexport class DigestEngine {\n private vaultDir: string;\n private index: MycoIndex;\n private llm: LlmProvider;\n private config: MycoConfig;\n private log: DigestLogFn;\n private lastCycleTimestampCache: string | null | undefined = undefined;\n private cycleInProgress = false;\n\n /** Hooks that run before each digest cycle (e.g., consolidation). */\n private prePassHooks: Array<{ name: string; fn: () => Promise<void> }> = [];\n\n /** Hooks that run after each successful digest cycle. */\n private postPassHooks: Array<{ name: string; fn: (result: DigestCycleResult) => Promise<void> }> = [];\n\n constructor(engineConfig: DigestEngineConfig) {\n this.vaultDir = engineConfig.vaultDir;\n this.index = engineConfig.index;\n this.llm = engineConfig.llmProvider;\n this.config = engineConfig.config;\n this.log = engineConfig.log ?? (() => {});\n }\n\n /** Register a hook that runs before each digest cycle. Best-effort — errors are logged, not thrown. */\n registerPrePass(name: string, fn: () => Promise<void>): void {\n this.prePassHooks.push({ name, fn });\n }\n\n /** Register a hook that runs after each successful digest cycle. Best-effort — errors are logged, not thrown. */\n registerPostPass(name: string, fn: (result: DigestCycleResult) => Promise<void>): void {\n this.postPassHooks.push({ name, fn });\n }\n\n /**\n * Query index for recent vault notes to feed into the digest.\n * Filters out extract notes (our own output) and caps at max_notes_per_cycle.\n */\n discoverSubstrate(lastCycleTimestamp: string | null): IndexedNote[] {\n const maxNotes = this.config.digest.substrate.max_notes_per_cycle;\n\n const notes = lastCycleTimestamp\n ? this.index.query({ updatedSince: lastCycleTimestamp, limit: maxNotes })\n : this.index.query({ limit: maxNotes });\n\n // Guard against self-digestion: extract files are not currently indexed,\n // but this filter prevents feedback loops if they ever are (e.g., via rebuild)\n const filtered = notes\n .filter((n) => n.type !== EXTRACT_TYPE)\n .filter((n) => {\n if (n.type !== 'spore') return true;\n const status = n.frontmatter.status as string | undefined;\n return !status || status === 'active';\n });\n\n // Sort by type weight (descending) then by recency (descending)\n filtered.sort((a, b) => {\n const weightA = DIGEST_SUBSTRATE_TYPE_WEIGHTS[a.type] ?? 0;\n const weightB = DIGEST_SUBSTRATE_TYPE_WEIGHTS[b.type] ?? 0;\n if (weightB !== weightA) return weightB - weightA;\n // More recent first — created is ISO string, lexicographic sort works\n return b.created.localeCompare(a.created);\n });\n\n return filtered.slice(0, maxNotes);\n }\n\n /**\n * Filter configured tiers by the context window available.\n * Only tiers whose minimum context requirement is met are eligible.\n */\n getEligibleTiers(): number[] {\n const contextWindow = this.config.digest.intelligence.context_window;\n return this.config.digest.tiers.filter((tier) => {\n const minContext = DIGEST_TIER_MIN_CONTEXT[tier];\n return minContext !== undefined && minContext <= contextWindow;\n });\n }\n\n /**\n * Format notes compactly for inclusion in the digest prompt.\n * Stops adding notes once the token budget is exceeded.\n */\n formatSubstrate(notes: IndexedNote[], tokenBudget: number): string {\n const charBudget = tokenBudget * CHARS_PER_TOKEN;\n const parts: string[] = [];\n let usedChars = 0;\n\n for (const note of notes) {\n const entry = `### [${note.type}] ${note.id} — \"${note.title}\"\\n${note.content}`;\n if (usedChars + entry.length > charBudget && parts.length > 0) break;\n parts.push(entry);\n usedChars += entry.length;\n }\n\n return parts.join('\\n\\n');\n }\n\n /**\n * Read a previously generated extract for a given tier.\n * Returns the body (stripped of YAML frontmatter), or null if not found.\n */\n readPreviousExtract(tier: number): string | null {\n const extractPath = path.join(this.vaultDir, 'digest', `extract-${tier}.md`);\n let content: string;\n try {\n content = fs.readFileSync(extractPath, 'utf-8');\n } catch {\n return null;\n }\n\n return stripFrontmatter(content).body;\n }\n\n /**\n * Write a digest extract to the vault with YAML frontmatter.\n * Uses atomic write pattern (temp file + rename).\n */\n writeExtract(\n tier: number,\n body: string,\n cycleId: string,\n model: string,\n substrateCount: number,\n ): void {\n const digestDir = path.join(this.vaultDir, 'digest');\n fs.mkdirSync(digestDir, { recursive: true });\n\n const frontmatter: Record<string, unknown> = {\n type: EXTRACT_TYPE,\n tier,\n generated: new Date().toISOString(),\n cycle_id: cycleId,\n substrate_count: substrateCount,\n model,\n };\n\n const fmYaml = YAML.stringify(frontmatter, {\n defaultStringType: 'QUOTE_DOUBLE',\n defaultKeyType: 'PLAIN',\n }).trim();\n const file = `---\\n${fmYaml}\\n---\\n\\n${body}\\n`;\n\n const fullPath = path.join(digestDir, `extract-${tier}.md`);\n const tmpPath = `${fullPath}.tmp`;\n fs.writeFileSync(tmpPath, file, 'utf-8');\n fs.renameSync(tmpPath, fullPath);\n }\n\n /**\n * Append a digest cycle result as a JSON line to trace.jsonl.\n */\n appendTrace(record: DigestCycleResult): void {\n const tracePath = path.join(this.vaultDir, 'digest', 'trace.jsonl');\n appendTraceRecord(tracePath, record as unknown as Record<string, unknown>);\n this.lastCycleTimestampCache = record.timestamp;\n }\n\n /**\n * Read the last cycle timestamp from trace.jsonl.\n * Cached in memory after first read — subsequent calls are O(1).\n */\n getLastCycleTimestamp(): string | null {\n if (this.lastCycleTimestampCache !== undefined) return this.lastCycleTimestampCache;\n\n const tracePath = path.join(this.vaultDir, 'digest', 'trace.jsonl');\n this.lastCycleTimestampCache = readLastTimestamp(tracePath);\n return this.lastCycleTimestampCache;\n }\n\n /**\n * Run a full digest cycle: discover substrate, generate extracts for each tier.\n * Returns the cycle result, or null if no substrate was found.\n */\n async runCycle(opts?: DigestCycleOptions): Promise<DigestCycleResult | null> {\n if (this.cycleInProgress) {\n this.log('debug', 'Cycle already in progress — skipping');\n return null;\n }\n this.cycleInProgress = true;\n\n try {\n // Ensure model is loaded BEFORE pre-pass hooks (e.g., consolidation).\n // Pre-pass hooks share the digest LLM provider — without ensureLoaded first,\n // requests go with just the model name (no instance ID), causing LM Studio\n // to spawn duplicate instances for each concurrent request.\n if (this.llm.ensureLoaded) {\n const { context_window: contextWindow, gpu_kv_cache: gpuKvCache } = this.config.digest.intelligence;\n this.log('debug', 'Verifying digest model', { contextWindow, gpuKvCache });\n await this.llm.ensureLoaded(contextWindow, gpuKvCache);\n }\n\n // Run pre-pass hooks (e.g., consolidation) before discovering substrate\n for (const hook of this.prePassHooks) {\n try {\n await hook.fn();\n } catch (err) {\n this.log('warn', `Pre-pass hook \"${hook.name}\" failed`, { error: (err as Error).message });\n }\n }\n\n return await this.runCycleInternal(opts);\n } finally {\n this.cycleInProgress = false;\n }\n }\n\n private async runCycleInternal(opts?: DigestCycleOptions): Promise<DigestCycleResult | null> {\n\n const startTime = Date.now();\n const fullReprocess = opts?.fullReprocess ?? false;\n const lastTimestamp = fullReprocess ? null : this.getLastCycleTimestamp();\n const substrate = this.discoverSubstrate(lastTimestamp);\n\n this.log('debug', 'Discovering substrate', { lastTimestamp: lastTimestamp ?? 'full reprocess', substrateCount: substrate.length });\n if (substrate.length === 0) {\n this.log('debug', 'No substrate found — skipping cycle');\n return null;\n }\n\n this.log('info', `Starting digest cycle`, { substrateCount: substrate.length, fullReprocess });\n const cycleId = crypto.randomUUID();\n const allEligible = this.getEligibleTiers();\n const eligibleTiers = opts?.tiers\n ? allEligible.filter((t) => opts.tiers!.includes(t))\n : allEligible;\n this.log('debug', `Eligible tiers: [${eligibleTiers.join(', ')}]`);\n const tiersGenerated: number[] = [];\n let totalTokensUsed = 0;\n let model = '';\n\n // Categorize substrate by type for the result\n const typeToKey: Record<string, keyof DigestCycleResult['substrate']> = {\n session: 'sessions',\n spore: 'spores',\n plan: 'plans',\n artifact: 'artifacts',\n 'team-member': 'team',\n };\n const substrateIndex: DigestCycleResult['substrate'] = {\n sessions: [],\n spores: [],\n plans: [],\n artifacts: [],\n team: [],\n };\n for (const note of substrate) {\n const key = typeToKey[note.type];\n if (key) {\n substrateIndex[key].push(note.id);\n }\n }\n\n // Record the cycle timestamp NOW, before tier processing. This ensures the\n // timestamp advances even if LLM calls fail, preventing the same substrate\n // from being rediscovered on every subsequent timer fire.\n const cycleTimestamp = new Date().toISOString();\n\n const systemPrompt = loadPrompt('digest-system');\n\n for (const tier of eligibleTiers) {\n const tierPrompt = loadPrompt(`digest-${tier}`);\n const previousExtract = opts?.cleanSlate ? null : this.readPreviousExtract(tier);\n\n // Calculate token budget for substrate:\n // (context_window * safety_margin) - output - system_prompt - tier_prompt - previous_extract\n const contextWindow = this.config.digest.intelligence.context_window;\n const systemPromptTokens = estimateTokens(systemPrompt);\n const tierPromptTokens = estimateTokens(tierPrompt);\n const previousExtractTokens = previousExtract\n ? estimateTokens(previousExtract) + PREVIOUS_EXTRACT_OVERHEAD_TOKENS\n : 0;\n const availableTokens = Math.floor(contextWindow * CONTEXT_SAFETY_MARGIN);\n const substrateBudget = availableTokens - tier - systemPromptTokens - tierPromptTokens - previousExtractTokens;\n\n if (substrateBudget <= 0) continue;\n\n const formattedSubstrate = this.formatSubstrate(substrate, substrateBudget);\n\n // Build user prompt (system prompt sent separately via LlmRequestOptions)\n const promptParts = [tierPrompt];\n\n if (previousExtract) {\n promptParts.push('', '## Previous Synthesis', '', previousExtract);\n }\n\n promptParts.push('', '## New Substrate', '', formattedSubstrate);\n promptParts.push(\n '',\n '---',\n 'Produce your updated synthesis now. Stay within the token budget specified above.',\n );\n\n const userPrompt = promptParts.join('\\n');\n const promptTokens = estimateTokens(systemPrompt + userPrompt);\n this.log('debug', `Tier ${tier}: sending LLM request`, { promptTokens, maxTokens: tier, substrateBudget });\n\n try {\n const tierStart = Date.now();\n const digestConfig = this.config.digest.intelligence;\n const opts: LlmRequestOptions = {\n maxTokens: tier,\n timeoutMs: DIGEST_LLM_REQUEST_TIMEOUT_MS,\n contextLength: contextWindow,\n reasoning: LLM_REASONING_MODE,\n systemPrompt,\n keepAlive: digestConfig.keep_alive ?? undefined,\n };\n const response = await this.llm.summarize(userPrompt, opts);\n const tierDuration = Date.now() - tierStart;\n\n // Strip reasoning tokens if present (some models output chain-of-thought)\n const extractText = stripReasoningTokens(response.text);\n model = response.model;\n const responseTokens = estimateTokens(extractText);\n totalTokensUsed += promptTokens + responseTokens;\n\n this.log('info', `Tier ${tier}: completed`, { durationMs: tierDuration, responseTokens, model: response.model });\n this.writeExtract(tier, extractText, cycleId, response.model, substrate.length);\n tiersGenerated.push(tier);\n } catch (err) {\n this.log('warn', `Tier ${tier}: failed`, { error: (err as Error).message });\n }\n }\n\n const result: DigestCycleResult = {\n cycleId,\n timestamp: cycleTimestamp,\n substrate: substrateIndex,\n tiersGenerated,\n model,\n durationMs: Date.now() - startTime,\n tokensUsed: totalTokensUsed,\n };\n\n this.appendTrace(result);\n\n // Run post-pass hooks after successful digest\n for (const hook of this.postPassHooks) {\n try {\n await hook.fn(result);\n } catch (err) {\n this.log('warn', `Post-pass hook \"${hook.name}\" failed`, { error: (err as Error).message });\n }\n }\n\n return result;\n }\n}\n\n// --- Metabolism (Adaptive Timer) ---\n\nexport type MetabolismState = 'active' | 'cooling' | 'dormant';\n\n/** Milliseconds per second for config conversion. */\nconst MS_PER_SECOND = 1000;\n\nexport class Metabolism {\n state: MetabolismState = 'active';\n currentIntervalMs: number;\n\n private cooldownStep = 0;\n private lastSubstrateTime: number;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private activeIntervalMs: number;\n private cooldownIntervalsMs: number[];\n private dormancyThresholdMs: number;\n\n constructor(config: MycoConfig['digest']['metabolism']) {\n this.activeIntervalMs = config.active_interval * MS_PER_SECOND;\n this.cooldownIntervalsMs = config.cooldown_intervals.map((s) => s * MS_PER_SECOND);\n this.dormancyThresholdMs = config.dormancy_threshold * MS_PER_SECOND;\n this.currentIntervalMs = this.activeIntervalMs;\n this.lastSubstrateTime = Date.now();\n }\n\n /** Reset to active state when new substrate is found. */\n onSubstrateFound(): void {\n this.state = 'active';\n this.cooldownStep = 0;\n this.currentIntervalMs = this.activeIntervalMs;\n this.lastSubstrateTime = Date.now();\n }\n\n /** Advance cooldown when a cycle finds no new substrate. */\n onEmptyCycle(): void {\n if (this.state === 'dormant') return;\n\n this.state = 'cooling';\n if (this.cooldownStep < this.cooldownIntervalsMs.length) {\n this.currentIntervalMs = this.cooldownIntervalsMs[this.cooldownStep];\n this.cooldownStep++;\n }\n\n this.checkDormancy();\n }\n\n /** Enter dormant state if enough time has elapsed since last substrate. */\n checkDormancy(): void {\n const elapsed = Date.now() - this.lastSubstrateTime;\n if (elapsed >= this.dormancyThresholdMs) {\n this.state = 'dormant';\n // Keep the last cooldown interval as the dormant polling rate\n }\n }\n\n /** Return to active from any state, resetting timers and rescheduling immediately. */\n activate(): void {\n this.onSubstrateFound();\n // Reschedule with the new active interval — without this, the old\n // (possibly dormant) timer continues ticking at the wrong rate\n if (this.callback) {\n this.reschedule();\n }\n }\n\n /** Set lastSubstrateTime explicitly (for testing). */\n markLastSubstrate(time: number): void {\n this.lastSubstrateTime = time;\n }\n\n /** Begin scheduling digest cycles with adaptive intervals. */\n start(callback: () => Promise<void>): void {\n this.callback = callback;\n this.reschedule();\n }\n\n /** Stop the timer. */\n stop(): void {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private callback: (() => Promise<void>) | null = null;\n\n private reschedule(): void {\n this.stop();\n if (!this.callback) return;\n const cb = this.callback;\n const schedule = (): void => {\n this.timer = setTimeout(async () => {\n await cb();\n schedule();\n }, this.currentIntervalMs);\n this.timer.unref();\n };\n schedule();\n }\n}\n","/**\n * Shared JSONL trace file utilities.\n *\n * Used by DigestEngine and ConsolidationEngine to read/write append-only\n * trace records with timestamp caching.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/** Read the last JSON record's `timestamp` field from a JSONL file. Returns null if file is missing or empty. */\nexport function readLastTimestamp(filePath: string): string | null {\n let content: string;\n try {\n content = fs.readFileSync(filePath, 'utf-8').trim();\n } catch {\n return null;\n }\n\n if (!content) return null;\n\n const lines = content.split('\\n');\n const lastLine = lines[lines.length - 1];\n try {\n const record = JSON.parse(lastLine) as { timestamp: string };\n return record.timestamp ?? null;\n } catch {\n return null;\n }\n}\n\n/** Append a JSON record to a JSONL file, creating parent directories if needed. */\nexport function appendTraceRecord(filePath: string, record: Record<string, unknown>): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.appendFileSync(filePath, JSON.stringify(record) + '\\n', 'utf-8');\n}\n","import { z } from 'zod';\nimport type { LlmProvider } from '../intelligence/llm.js';\nimport { ARTIFACT_TYPES } from '../vault/types.js';\nimport { estimateTokens, CHARS_PER_TOKEN, PROMPT_PREVIEW_CHARS, AI_RESPONSE_PREVIEW_CHARS, COMMAND_PREVIEW_CHARS, LLM_REASONING_MODE } from '../constants.js';\nimport type { MycoConfig } from '../config/schema.js';\nimport type { ObservationType, ArtifactType } from '../vault/types.js';\nimport { buildExtractionPrompt, buildSummaryPrompt, buildTitlePrompt, buildClassificationPrompt } from '../prompts/index.js';\nimport { extractJson, stripReasoningTokens } from '../intelligence/response.js';\n\n/** Marker substring in failed summary text. Used by reprocess --failed to detect failures. */\nexport const SUMMARIZATION_FAILED_MARKER = 'summarization failed';\n\nexport interface Observation {\n type: ObservationType;\n title: string;\n content: string;\n tags: string[];\n root_cause?: string;\n fix?: string;\n rationale?: string;\n alternatives_rejected?: string;\n gained?: string;\n sacrificed?: string;\n}\n\nexport interface ProcessorResult {\n summary: string;\n observations: Observation[];\n degraded: boolean;\n}\n\nexport interface ClassifiedArtifact {\n source_path: string;\n artifact_type: ArtifactType;\n title: string;\n tags: string[];\n}\n\nconst ClassificationResponseSchema = z.object({\n artifacts: z.array(z.object({\n source_path: z.string(),\n artifact_type: z.enum(ARTIFACT_TYPES),\n title: z.string(),\n tags: z.array(z.string()).default([]),\n })).default([]),\n});\n\nexport class BufferProcessor {\n private extractionMaxTokens: number;\n private summaryMaxTokens: number;\n private titleMaxTokens: number;\n private classificationMaxTokens: number;\n\n constructor(private backend: LlmProvider, private contextWindow: number = 8192, captureConfig?: MycoConfig['capture']) {\n this.extractionMaxTokens = captureConfig?.extraction_max_tokens ?? 2048;\n this.summaryMaxTokens = captureConfig?.summary_max_tokens ?? 512;\n this.titleMaxTokens = captureConfig?.title_max_tokens ?? 32;\n this.classificationMaxTokens = captureConfig?.classification_max_tokens ?? 1024;\n }\n\n private truncateForContext(data: string, maxTokens: number): string {\n const available = this.contextWindow - maxTokens;\n const dataTokens = estimateTokens(data);\n if (dataTokens <= available) return data;\n const charBudget = available * CHARS_PER_TOKEN;\n return data.slice(0, charBudget);\n }\n\n async process(\n events: Array<Record<string, unknown>>,\n sessionId: string,\n ): Promise<ProcessorResult> {\n const rawPrompt = this.buildPromptForExtraction(events, sessionId);\n const prompt = this.truncateForContext(rawPrompt, this.extractionMaxTokens);\n\n try {\n const response = await this.backend.summarize(prompt, { maxTokens: this.extractionMaxTokens, reasoning: LLM_REASONING_MODE });\n const parsed = extractJson(response.text) as {\n summary: string;\n observations: Observation[];\n };\n\n return {\n summary: parsed.summary,\n observations: parsed.observations ?? [],\n degraded: false,\n };\n } catch (error) {\n return {\n summary: `LLM processing failed for session ${sessionId}. ${events.length} events captured. Error: ${(error as Error).message}`,\n observations: [],\n degraded: true,\n };\n }\n }\n\n private buildPromptForExtraction(\n events: Array<Record<string, unknown>>,\n sessionId: string,\n ): string {\n const toolSummary = this.summarizeEvents(events);\n return buildExtractionPrompt(sessionId, events.length, toolSummary, this.extractionMaxTokens);\n }\n\n async summarizeSession(\n conversationMarkdown: string,\n sessionId: string,\n user?: string,\n ): Promise<{ summary: string; title: string }> {\n const truncatedContent = this.truncateForContext(conversationMarkdown, this.summaryMaxTokens);\n const summaryPrompt = buildSummaryPrompt(sessionId, user ?? 'unknown', truncatedContent, this.summaryMaxTokens);\n\n let summaryText: string;\n try {\n const response = await this.backend.summarize(summaryPrompt, { maxTokens: this.summaryMaxTokens, reasoning: LLM_REASONING_MODE });\n summaryText = stripReasoningTokens(response.text);\n } catch (error) {\n summaryText = `Session ${sessionId} — ${SUMMARIZATION_FAILED_MARKER}: ${(error as Error).message}`;\n }\n\n const titlePrompt = buildTitlePrompt(summaryText, sessionId);\n let title: string;\n try {\n const response = await this.backend.summarize(titlePrompt, { maxTokens: this.titleMaxTokens, reasoning: LLM_REASONING_MODE });\n title = stripReasoningTokens(response.text).trim();\n } catch {\n title = `Session ${sessionId}`;\n }\n\n return { summary: summaryText, title };\n }\n\n async classifyArtifacts(\n candidates: Array<{ path: string; content: string }>,\n sessionId: string,\n ): Promise<ClassifiedArtifact[]> {\n if (candidates.length === 0) return [];\n\n const prompt = this.buildPromptForClassification(candidates, sessionId);\n const response = await this.backend.summarize(prompt, { maxTokens: this.classificationMaxTokens, reasoning: LLM_REASONING_MODE });\n const raw = extractJson(response.text);\n const parsed = ClassificationResponseSchema.parse(raw);\n return parsed.artifacts;\n }\n\n private buildPromptForClassification(\n candidates: Array<{ path: string; content: string }>,\n sessionId: string,\n ): string {\n return buildClassificationPrompt(sessionId, candidates, this.classificationMaxTokens);\n }\n\n private summarizeEvents(events: Array<Record<string, unknown>>): string {\n const toolCounts = new Map<string, number>();\n const filesAccessed = new Set<string>();\n const prompts: string[] = [];\n const aiResponses: string[] = [];\n\n for (const event of events) {\n if (event.type === 'user_prompt') {\n const prompt = String(event.prompt ?? '');\n if (prompt) prompts.push(prompt.slice(0, PROMPT_PREVIEW_CHARS));\n continue;\n }\n\n if (event.type === 'ai_response') {\n const content = String(event.content ?? '');\n if (content) aiResponses.push(content.slice(0, AI_RESPONSE_PREVIEW_CHARS));\n continue;\n }\n\n // Hooks send tool_name/tool_input; also support legacy tool/input\n const tool = String(event.tool_name ?? event.tool ?? 'unknown');\n toolCounts.set(tool, (toolCounts.get(tool) ?? 0) + 1);\n\n const input = (event.tool_input ?? event.input) as Record<string, unknown> | undefined;\n if (input?.path) filesAccessed.add(String(input.path));\n if (input?.file_path) filesAccessed.add(String(input.file_path));\n if (input?.command) filesAccessed.add(`[cmd] ${String(input.command).slice(0, COMMAND_PREVIEW_CHARS)}`);\n }\n\n const lines: string[] = [];\n\n if (prompts.length > 0) {\n lines.push('### User Prompts');\n for (const p of prompts) {\n lines.push(`- \"${p}\"`);\n }\n }\n\n lines.push('\\n### Tool Usage');\n for (const [tool, count] of toolCounts) {\n lines.push(`- ${tool}: ${count} calls`);\n }\n\n if (filesAccessed.size > 0) {\n lines.push('\\n### Files Accessed');\n for (const file of filesAccessed) {\n lines.push(`- ${file}`);\n }\n }\n\n if (aiResponses.length > 0) {\n lines.push('\\n### AI Responses');\n for (const r of aiResponses) {\n lines.push(`- \"${r}\"`);\n }\n }\n\n return lines.join('\\n');\n }\n}\n","import { AgentRegistry } from '../agents/registry.js';\nimport type { AgentAdapter } from '../agents/adapter.js';\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\nimport fs from 'node:fs';\n\n// Re-export TranscriptTurn from its canonical home in agents/adapter.ts\nexport type { TranscriptTurn } from '../agents/adapter.js';\nimport type { TranscriptTurn } from '../agents/adapter.js';\n\ninterface TranscriptConfig {\n /** Additional agent adapters to register (useful for testing or custom agents) */\n additionalAdapters?: AgentAdapter[];\n}\n\nexport class TranscriptMiner {\n private registry: AgentRegistry;\n\n constructor(config?: TranscriptConfig) {\n this.registry = new AgentRegistry(config?.additionalAdapters);\n }\n\n /**\n * Extract all conversation turns for a session.\n * Convenience wrapper — delegates to getAllTurnsWithSource.\n */\n getAllTurns(sessionId: string): TranscriptTurn[] {\n return this.getAllTurnsWithSource(sessionId).turns;\n }\n\n /**\n * Extract turns using the hook-provided transcript path first (fast, no scanning),\n * then fall back to adapter registry scanning if the path isn't provided.\n */\n getAllTurnsWithSource(sessionId: string, transcriptPath?: string): { turns: TranscriptTurn[]; source: string } {\n // Primary: use the path provided by the hook (no directory scanning needed)\n if (transcriptPath) {\n const result = this.registry.parseTurnsFromPath(transcriptPath);\n if (result) return result;\n }\n\n // Fallback: scan known agent directories\n const result = this.registry.getTranscriptTurns(sessionId);\n if (result) return result;\n return { turns: [], source: 'none' };\n }\n}\n\n/**\n * Build turns from buffer events — the fallback when no agent transcript is available.\n * Buffer events come from hooks (user_prompt, tool_use) and lack AI responses.\n * Turns will have prompts and tool counts but no aiResponse.\n */\nexport function extractTurnsFromBuffer(events: Array<Record<string, unknown>>): TranscriptTurn[] {\n const turns: TranscriptTurn[] = [];\n let current: TranscriptTurn | null = null;\n\n for (const event of events) {\n const type = event.type as string;\n if (type === 'user_prompt') {\n if (current) turns.push(current);\n current = {\n prompt: String(event.prompt ?? '').slice(0, PROMPT_PREVIEW_CHARS),\n toolCount: 0,\n timestamp: String(event.timestamp ?? new Date().toISOString()),\n };\n } else if (type === 'tool_use') {\n if (current) current.toolCount++;\n }\n }\n if (current) turns.push(current);\n return turns;\n}\n","import { formatSporeBody } from '../obsidian/formatter.js';\nimport { sessionNoteId } from './session-id.js';\nimport { indexNote } from '../index/rebuild.js';\nimport type { Observation } from '../daemon/processor.js';\nimport type { VaultWriter } from './writer.js';\nimport type { MycoIndex } from '../index/sqlite.js';\n\nexport interface WrittenNote {\n id: string;\n path: string;\n observation: Observation;\n}\n\nexport function writeObservationNotes(\n observations: Observation[],\n sessionId: string,\n writer: VaultWriter,\n index: MycoIndex,\n vaultDir: string,\n): WrittenNote[] {\n const results: WrittenNote[] = [];\n\n for (const obs of observations) {\n const obsId = `${obs.type}-${sessionId.slice(-6)}-${Date.now()}`;\n const body = formatSporeBody({\n title: obs.title,\n observationType: obs.type,\n content: obs.content,\n sessionId,\n root_cause: obs.root_cause,\n fix: obs.fix,\n rationale: obs.rationale,\n alternatives_rejected: obs.alternatives_rejected,\n gained: obs.gained,\n sacrificed: obs.sacrificed,\n tags: obs.tags,\n });\n const relativePath = writer.writeSpore({\n id: obsId,\n observation_type: obs.type,\n session: sessionNoteId(sessionId),\n tags: obs.tags,\n content: body,\n });\n indexNote(index, vaultDir, relativePath);\n results.push({ id: obsId, path: relativePath, observation: obs });\n }\n\n return results;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { MycoIndex } from '../index/sqlite.js';\nimport type { VectorIndex } from '../index/vectors.js';\nimport type { MycoConfig } from '../config/schema.js';\nimport type { LlmProvider, EmbeddingProvider } from '../intelligence/llm.js';\nimport type { DigestCycleResult, DigestLogFn } from '../daemon/digest.js';\nimport { rebuildIndex, indexNote } from '../index/rebuild.js';\nimport { initFts } from '../index/fts.js';\nimport { generateEmbedding } from '../intelligence/embeddings.js';\nimport { batchExecute, LLM_BATCH_CONCURRENCY, EMBEDDING_BATCH_CONCURRENCY } from '../intelligence/batch.js';\nimport { DigestEngine } from '../daemon/digest.js';\nimport type { DigestCycleOptions } from '../daemon/digest.js';\nimport { BufferProcessor, SUMMARIZATION_FAILED_MARKER } from '../daemon/processor.js';\nimport { TranscriptMiner } from '../capture/transcript-miner.js';\nimport { VaultWriter } from '../vault/writer.js';\nimport { writeObservationNotes } from '../vault/observations.js';\nimport { createPerProjectAdapter } from '../agents/adapter.js';\nimport { claudeCodeAdapter } from '../agents/claude-code.js';\nimport { sessionNoteId, bareSessionId } from '../vault/session-id.js';\nimport { callout, extractSection, CONVERSATION_HEADING } from '../obsidian/formatter.js';\nimport matter from 'gray-matter';\nimport {\n EMBEDDING_INPUT_LIMIT,\n CURATION_CLUSTER_SIMILARITY,\n SUPERSESSION_MAX_TOKENS,\n LLM_REASONING_MODE,\n} from '../constants.js';\nimport { stripReasoningTokens } from '../intelligence/response.js';\nimport { loadPrompt, formatNoteForPrompt, formatNotesForPrompt } from '../prompts/index.js';\nimport { supersedeSpore, supersededIdsSchema, isActiveSpore } from '../vault/curation.js';\n\nexport interface OperationContext {\n vaultDir: string;\n config: MycoConfig;\n index: MycoIndex;\n vectorIndex?: VectorIndex;\n log?: (level: string, message: string, data?: Record<string, unknown>) => void;\n}\n\n// --- Rebuild ---\n\nexport interface RebuildResult {\n ftsCount: number;\n embeddedCount: number;\n failedCount: number;\n skippedCount: number;\n}\n\n/**\n * Rebuild FTS index and re-embed all active notes.\n * Core logic shared between CLI (`myco rebuild`) and daemon API (`POST /api/rebuild`).\n */\nexport async function runRebuild(\n ctx: OperationContext,\n embeddingProvider: EmbeddingProvider,\n onProgress?: (done: number, total: number) => void,\n): Promise<RebuildResult> {\n const { index, vaultDir } = ctx;\n\n // Phase 1: FTS rebuild\n initFts(index);\n const ftsCount = rebuildIndex(index, vaultDir);\n\n // Phase 2: Vector embeddings\n if (!ctx.vectorIndex) {\n return { ftsCount, embeddedCount: 0, failedCount: 0, skippedCount: 0 };\n }\n\n const allNotes = index.query({});\n // Skip superseded/archived spores\n const activeNotes = allNotes.filter((n) => {\n const status = (n.frontmatter as Record<string, unknown>)?.status as string | undefined;\n return status !== 'superseded' && status !== 'archived';\n });\n const skippedCount = allNotes.length - activeNotes.length;\n\n const vec = ctx.vectorIndex;\n const result = await batchExecute(\n activeNotes,\n async (note) => {\n const text = `${note.title}\\n${note.content}`.slice(0, EMBEDDING_INPUT_LIMIT);\n const emb = await generateEmbedding(embeddingProvider, text);\n vec.upsert(note.id, emb.embedding, {\n type: note.type,\n session_id: (note.frontmatter as Record<string, unknown>)?.session as string ?? '',\n });\n },\n {\n concurrency: EMBEDDING_BATCH_CONCURRENCY,\n onProgress,\n },\n );\n\n return {\n ftsCount,\n embeddedCount: result.succeeded,\n failedCount: result.failed,\n skippedCount,\n };\n}\n\n// --- Digest ---\n\nexport interface DigestOptions {\n tier?: number;\n full?: boolean;\n}\n\n/**\n * Run a single digest cycle.\n * Core logic shared between CLI (`myco digest`) and daemon API (`POST /api/digest`).\n */\nexport async function runDigest(\n ctx: OperationContext,\n llmProvider: LlmProvider,\n options?: DigestOptions,\n): Promise<DigestCycleResult | null> {\n const { config, vaultDir, index } = ctx;\n\n const log: DigestLogFn = ctx.log\n ? (level, message, data) => ctx.log!(level, message, data)\n : () => {};\n\n const engine = new DigestEngine({\n vaultDir,\n index,\n llmProvider,\n config,\n log,\n });\n\n const opts: DigestCycleOptions = {};\n const isReprocess = options?.full || options?.tier !== undefined;\n\n if (isReprocess) {\n opts.fullReprocess = true;\n opts.cleanSlate = true;\n }\n if (options?.tier !== undefined) {\n const eligible = engine.getEligibleTiers();\n if (!eligible.includes(options.tier)) {\n throw new Error(`Tier ${options.tier} is not eligible. Eligible tiers: [${eligible.join(', ')}]`);\n }\n opts.tiers = [options.tier];\n }\n\n return engine.runCycle(opts);\n}\n\n// --- Curation ---\n\nexport interface CurationDeps {\n vaultDir: string;\n config: MycoConfig;\n index: MycoIndex;\n vectorIndex: VectorIndex;\n llmProvider: LlmProvider;\n embeddingProvider: EmbeddingProvider;\n log?: (level: string, message: string, data?: Record<string, unknown>) => void;\n}\n\nexport interface CurationResult {\n scanned: number;\n clustersEvaluated: number;\n superseded: number;\n}\n\n// --- Curation internals ---\n\n/** Max concurrent embedding requests to avoid overwhelming the provider. */\nconst CURATION_EMBEDDING_BATCH_SIZE = 10;\n\ninterface SporeWithEmbedding {\n id: string;\n path: string;\n title: string;\n content: string;\n created: string;\n frontmatter: Record<string, unknown>;\n embedding: number[];\n}\n\ninterface Cluster {\n spores: SporeWithEmbedding[];\n centroid: number[];\n}\n\nfunction cosineSimilarity(a: number[], b: number[]): number {\n let dot = 0, normA = 0, normB = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n return dot / (Math.sqrt(normA) * Math.sqrt(normB));\n}\n\nfunction updateCentroid(spores: SporeWithEmbedding[]): number[] {\n if (spores.length === 0) return [];\n const dim = spores[0].embedding.length;\n const centroid = new Array<number>(dim).fill(0);\n for (const s of spores) {\n for (let i = 0; i < dim; i++) {\n centroid[i] += s.embedding[i];\n }\n }\n for (let i = 0; i < dim; i++) {\n centroid[i] /= spores.length;\n }\n return centroid;\n}\n\nfunction clusterSpores(spores: SporeWithEmbedding[]): Cluster[] {\n const clusters: Cluster[] = [];\n for (const spore of spores) {\n let bestCluster: Cluster | null = null;\n let bestSimilarity = -1;\n for (const cluster of clusters) {\n const sim = cosineSimilarity(spore.embedding, cluster.centroid);\n if (sim > bestSimilarity) {\n bestSimilarity = sim;\n bestCluster = cluster;\n }\n }\n if (bestCluster !== null && bestSimilarity >= CURATION_CLUSTER_SIMILARITY) {\n bestCluster.spores.push(spore);\n bestCluster.centroid = updateCentroid(bestCluster.spores);\n } else {\n clusters.push({ spores: [spore], centroid: [...spore.embedding] });\n }\n }\n return clusters;\n}\n\n/**\n * Run vault curation: embed spores, cluster, ask LLM which are outdated, supersede.\n * Core logic shared between CLI (`myco curate`) and daemon API (`POST /api/curate`).\n */\nexport async function runCuration(\n deps: CurationDeps,\n dryRun: boolean,\n): Promise<CurationResult> {\n const { index, vectorIndex, llmProvider, embeddingProvider, vaultDir } = deps;\n const log = deps.log ?? (() => {});\n\n // 1. Query all spores and filter for active ones\n const allSpores = index.query({ type: 'spore' });\n const activeSpores = allSpores.filter((n) => isActiveSpore(n.frontmatter));\n\n if (activeSpores.length === 0) {\n return { scanned: 0, clustersEvaluated: 0, superseded: 0 };\n }\n\n // 2. Embed all active spores (batched for concurrency)\n const sporesWithEmbeddings: SporeWithEmbedding[] = [];\n let embedFailures = 0;\n\n for (let i = 0; i < activeSpores.length; i += CURATION_EMBEDDING_BATCH_SIZE) {\n const batch = activeSpores.slice(i, i + CURATION_EMBEDDING_BATCH_SIZE);\n const results = await Promise.allSettled(\n batch.map(async (spore) => {\n const text = spore.content.slice(0, EMBEDDING_INPUT_LIMIT);\n const result = await generateEmbedding(embeddingProvider, text);\n return { spore, embedding: result.embedding };\n }),\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled') {\n const { spore, embedding } = result.value;\n sporesWithEmbeddings.push({\n id: spore.id,\n path: spore.path,\n title: spore.title,\n content: spore.content,\n created: spore.created,\n frontmatter: spore.frontmatter,\n embedding,\n });\n } else {\n embedFailures++;\n }\n }\n }\n\n if (embedFailures > 0) {\n log('warn', `${embedFailures} spore(s) could not be embedded and were skipped`);\n }\n\n // 3. Group by observation_type\n const byType = new Map<string, SporeWithEmbedding[]>();\n for (const spore of sporesWithEmbeddings) {\n const obsType = (spore.frontmatter['observation_type'] as string | undefined) ?? 'unknown';\n if (!byType.has(obsType)) byType.set(obsType, []);\n byType.get(obsType)!.push(spore);\n }\n\n // 4. Cluster within each type group\n const template = loadPrompt('supersession');\n let totalClusters = 0;\n let totalSuperseded = 0;\n\n for (const [obsType, typeSpores] of byType) {\n const clusters = clusterSpores(typeSpores);\n const multiSpore = clusters.filter((c) => c.spores.length >= 2);\n if (multiSpore.length === 0) continue;\n\n log('info', `Type: ${obsType} — ${typeSpores.length} spores, ${multiSpore.length} cluster(s) to evaluate`);\n totalClusters += multiSpore.length;\n\n for (const cluster of multiSpore) {\n const sorted = [...cluster.spores].sort((a, b) => a.created.localeCompare(b.created));\n const newest = sorted[sorted.length - 1];\n const candidates = sorted.slice(0, sorted.length - 1);\n\n // Build supersession prompt\n const newSporeText = formatNoteForPrompt(newest);\n const candidatesText = formatNotesForPrompt(candidates);\n\n const prompt = template\n .replace('{{new_spore}}', newSporeText)\n .replace('{{candidates}}', candidatesText);\n\n // Ask LLM which candidates are outdated\n let responseText: string;\n try {\n const response = await llmProvider.summarize(prompt, {\n maxTokens: SUPERSESSION_MAX_TOKENS,\n reasoning: LLM_REASONING_MODE,\n });\n responseText = stripReasoningTokens(response.text);\n } catch (err) {\n log('warn', `LLM call failed for cluster in ${obsType}: ${String(err)}`);\n continue;\n }\n\n // Parse response\n let rawIds: unknown;\n try {\n rawIds = JSON.parse(responseText);\n } catch {\n log('warn', `Could not parse LLM response for cluster in ${obsType}`);\n continue;\n }\n\n const parsed = supersededIdsSchema.safeParse(rawIds);\n if (!parsed.success) {\n log('warn', `LLM response schema invalid for cluster in ${obsType}`);\n continue;\n }\n\n // Validate IDs against actual candidates\n const candidateMap = new Map(candidates.map((c) => [c.id, c]));\n const validIds = parsed.data.filter((id) => candidateMap.has(id));\n if (validIds.length === 0) continue;\n\n for (const id of validIds) {\n const candidate = candidateMap.get(id)!;\n\n if (dryRun) {\n log('info', `[dry-run] Would supersede: ${candidate.title} (${id}) by ${newest.title} (${newest.id})`);\n totalSuperseded++;\n continue;\n }\n\n const wrote = supersedeSpore(id, newest.id, candidate.path, { index, vectorIndex, vaultDir });\n if (!wrote) {\n log('warn', `File not found for ${id}, skipping write`);\n continue;\n }\n\n log('info', `Superseded: ${candidate.title} (${id}) by ${newest.title} (${newest.id})`);\n totalSuperseded++;\n }\n }\n }\n\n return {\n scanned: activeSpores.length,\n clustersEvaluated: totalClusters,\n superseded: totalSuperseded,\n };\n}\n\n// --- Reprocess ---\n\nexport interface ReprocessOptions {\n /** Filter to sessions matching this substring. */\n session?: string;\n /** Filter to sessions from a specific date (YYYY-MM-DD). */\n date?: string;\n /** Only reprocess sessions with failed summaries. */\n failed?: boolean;\n /** Skip LLM calls — re-index and re-embed only. */\n indexOnly?: boolean;\n}\n\nexport interface ReprocessResult {\n sessionsFound: number;\n sessionsProcessed: number;\n observationsExtracted: number;\n summariesRegenerated: number;\n embeddingsQueued: number;\n}\n\n\n/**\n * Replace the title (first `# ` line) and summary callout in a session note body.\n */\nfunction updateTitleAndSummary(body: string, newTitle: string, newNarrative: string): string {\n let updated = body.replace(/^# .*/m, `# ${newTitle}`);\n const summaryCallout = callout('abstract', 'Summary', newNarrative);\n updated = updated.replace(/> \\[!abstract\\] Summary\\n(?:> .*\\n?)*/m, summaryCallout + '\\n');\n return updated;\n}\n\n/**\n * Reprocess sessions: re-extract observations, regenerate summaries, re-index.\n * Core logic shared between CLI (`myco reprocess`) and daemon API (`POST /api/reprocess`).\n */\nexport async function runReprocess(\n ctx: OperationContext,\n llmProvider: LlmProvider | null,\n embeddingProvider: EmbeddingProvider,\n options?: ReprocessOptions,\n onProgress?: (phase: string, done: number, total: number) => void,\n): Promise<ReprocessResult> {\n const { vaultDir, config, index } = ctx;\n const log = ctx.log ?? (() => {});\n\n const sessionFilter = options?.session;\n const dateFilter = options?.date;\n const failedOnly = options?.failed ?? false;\n const skipLlm = options?.indexOnly ?? false;\n\n const effectiveLlm = skipLlm ? null : llmProvider;\n const processor = effectiveLlm\n ? new BufferProcessor(effectiveLlm, config.intelligence.llm.context_window, config.capture)\n : null;\n const writer = new VaultWriter(vaultDir);\n const miner = new TranscriptMiner({\n additionalAdapters: config.capture.transcript_paths.map((p: string) =>\n createPerProjectAdapter(p, claudeCodeAdapter.parseTurns),\n ),\n });\n\n // Find sessions\n const sessionsDir = path.join(vaultDir, 'sessions');\n if (!fs.existsSync(sessionsDir)) {\n return { sessionsFound: 0, sessionsProcessed: 0, observationsExtracted: 0, summariesRegenerated: 0, embeddingsQueued: 0 };\n }\n\n const sessionFiles: Array<{ relativePath: string; sessionId: string }> = [];\n for (const dateDir of fs.readdirSync(sessionsDir)) {\n if (dateFilter && dateDir !== dateFilter) continue;\n const datePath = path.join(sessionsDir, dateDir);\n if (!fs.statSync(datePath).isDirectory()) continue;\n for (const file of fs.readdirSync(datePath)) {\n if (!file.startsWith('session-') || !file.endsWith('.md')) continue;\n const sessionId = file.replace('session-', '').replace('.md', '');\n if (sessionFilter && !sessionId.includes(sessionFilter)) continue;\n sessionFiles.push({ relativePath: path.join('sessions', dateDir, file), sessionId });\n }\n }\n\n if (sessionFiles.length === 0) {\n return { sessionsFound: 0, sessionsProcessed: 0, observationsExtracted: 0, summariesRegenerated: 0, embeddingsQueued: 0 };\n }\n\n // Prepare tasks\n interface SessionTask {\n relativePath: string;\n sessionId: string;\n bare: string;\n frontmatter: Record<string, unknown>;\n frontmatterBlock: string;\n body: string;\n conversationSection: string;\n batchEvents: Array<Record<string, unknown>> | null;\n turnCount: number;\n hasFailed: boolean;\n }\n\n const tasks: SessionTask[] = [];\n for (const { relativePath, sessionId } of sessionFiles) {\n const rawContent = fs.readFileSync(path.join(vaultDir, relativePath), 'utf-8');\n\n // Quick pre-screen before expensive parsing when filtering to failed sessions only\n const hasFailed = rawContent.includes(SUMMARIZATION_FAILED_MARKER);\n if (failedOnly && !hasFailed) continue;\n\n const { data: frontmatter, content: body } = matter(rawContent);\n const bare = bareSessionId(sessionId);\n const turnsResult = miner.getAllTurnsWithSource(bare);\n const conversationSection = extractSection(body, CONVERSATION_HEADING);\n const fmEnd = rawContent.indexOf('---', 4);\n const frontmatterBlock = rawContent.slice(0, fmEnd + 3);\n\n const batchEvents = turnsResult && turnsResult.turns.length > 0\n ? turnsResult.turns.map((t) => ({\n type: 'turn' as const,\n prompt: t.prompt,\n tool_count: t.toolCount,\n response: t.aiResponse ?? '',\n timestamp: t.timestamp,\n }))\n : null;\n\n tasks.push({ relativePath, sessionId, bare, frontmatter, frontmatterBlock, body, conversationSection, batchEvents, turnCount: turnsResult?.turns.length ?? 0, hasFailed });\n }\n\n if (tasks.length === 0) {\n return { sessionsFound: sessionFiles.length, sessionsProcessed: 0, observationsExtracted: 0, summariesRegenerated: 0, embeddingsQueued: 0 };\n }\n\n log('info', `Reprocessing ${tasks.length} session(s)`, { filters: { session: sessionFilter, date: dateFilter, failed: failedOnly, indexOnly: skipLlm } });\n\n // Fire-and-forget embedding helper — pipelines embeddings as data is produced\n // instead of accumulating all jobs in memory for a separate phase.\n let embeddingsQueued = 0;\n const embedPending: Promise<void>[] = [];\n const fireEmbed = (id: string, text: string, metadata: Record<string, string>) => {\n if (!ctx.vectorIndex) return;\n embeddingsQueued++;\n const vec = ctx.vectorIndex;\n const p = generateEmbedding(embeddingProvider, text)\n .then((emb) => { vec.upsert(id, emb.embedding, metadata); })\n .catch((err) => { log('warn', `Embedding failed for ${id}`, { error: (err as Error).message }); });\n embedPending.push(p);\n };\n\n // Phase 1: Extraction + FTS re-indexing + inline embeddings\n let totalObservations = 0;\n\n const extractionResult = await batchExecute(\n tasks,\n async (task) => {\n let obs = 0;\n\n if (processor && task.batchEvents) {\n const result = await processor.process(task.batchEvents, task.bare);\n if (result.observations.length > 0) {\n writeObservationNotes(result.observations, task.bare, writer, index, vaultDir);\n obs = result.observations.length;\n for (const o of result.observations) {\n fireEmbed(\n `${o.type}-${task.bare.slice(-6)}-${Date.now()}`,\n `${o.title}\\n${o.content}`.slice(0, EMBEDDING_INPUT_LIMIT),\n { type: 'spore', session_id: task.bare },\n );\n }\n }\n }\n\n indexNote(index, vaultDir, task.relativePath);\n\n const embText = `${task.frontmatter.title ?? ''}\\n${task.frontmatter.summary ?? ''}`.slice(0, EMBEDDING_INPUT_LIMIT);\n if (embText.trim()) {\n fireEmbed(sessionNoteId(task.bare), embText, { type: 'session', session_id: task.bare });\n }\n\n return obs;\n },\n {\n concurrency: LLM_BATCH_CONCURRENCY,\n onProgress: (done, total) => onProgress?.('extraction', done, total),\n },\n );\n\n for (const r of extractionResult.results) {\n if (r.status === 'fulfilled') totalObservations += r.value;\n }\n\n // Phase 2: Resummarize\n let summarized = 0;\n if (processor) {\n const summarizableTasks = tasks.filter((t) => t.conversationSection);\n if (summarizableTasks.length > 0) {\n const summaryResult = await batchExecute(\n summarizableTasks,\n async (task) => {\n const user = typeof task.frontmatter.user === 'string' ? task.frontmatter.user : undefined;\n const result = await processor.summarizeSession(task.conversationSection, task.bare, user);\n\n if (result.summary.includes(SUMMARIZATION_FAILED_MARKER)) {\n log('warn', `Summarization failed for ${task.sessionId.slice(0, 12)}`);\n return false;\n }\n\n const updatedBody = updateTitleAndSummary(task.body, result.title, result.summary);\n fs.writeFileSync(path.join(vaultDir, task.relativePath), task.frontmatterBlock + updatedBody);\n indexNote(index, vaultDir, task.relativePath);\n return true;\n },\n {\n concurrency: LLM_BATCH_CONCURRENCY,\n onProgress: (done, total) => onProgress?.('summarization', done, total),\n },\n );\n\n for (const r of summaryResult.results) {\n if (r.status === 'fulfilled' && r.value) summarized++;\n }\n }\n }\n\n // Wait for all pipelined embeddings to settle\n await Promise.allSettled(embedPending);\n\n log('info', 'Reprocess completed', {\n sessions: tasks.length,\n observations: totalObservations,\n summaries: summarized,\n embeddings: embeddingsQueued,\n });\n\n return {\n sessionsFound: sessionFiles.length,\n sessionsProcessed: tasks.length,\n observationsExtracted: totalObservations,\n summariesRegenerated: summarized,\n embeddingsQueued,\n };\n}\n","/**\n * Batch execution utilities for LLM and embedding operations.\n *\n * Provides concurrency-limited parallel execution for bulk operations\n * like reprocessing, rebuilding, and any future batch pipeline.\n */\n\n/** Default concurrency for LLM calls (heavier, single-threaded backends). */\nexport const LLM_BATCH_CONCURRENCY = 3;\n/** Default concurrency for embedding calls (lighter, can run more in parallel). */\nexport const EMBEDDING_BATCH_CONCURRENCY = 4;\n\nexport interface BatchResult<T> {\n succeeded: number;\n failed: number;\n results: Array<{ status: 'fulfilled'; value: T } | { status: 'rejected'; reason: string }>;\n}\n\n/**\n * Execute async tasks with a concurrency limit.\n * Reports progress via an optional callback.\n */\nexport async function batchExecute<I, O>(\n items: I[],\n fn: (item: I) => Promise<O>,\n options: {\n concurrency: number;\n onProgress?: (completed: number, total: number) => void;\n },\n): Promise<BatchResult<O>> {\n const { concurrency, onProgress } = options;\n let succeeded = 0;\n let failed = 0;\n const results: BatchResult<O>['results'] = [];\n\n for (let i = 0; i < items.length; i += concurrency) {\n const batch = items.slice(i, i + concurrency);\n const settled = await Promise.allSettled(batch.map(fn));\n\n for (const result of settled) {\n if (result.status === 'fulfilled') {\n succeeded++;\n results.push({ status: 'fulfilled', value: result.value });\n } else {\n failed++;\n results.push({ status: 'rejected', reason: (result.reason as Error)?.message ?? String(result.reason) });\n }\n }\n\n onProgress?.(succeeded + failed, items.length);\n }\n\n return { succeeded, failed, results };\n}\n"],"mappings":"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","names":["fs","path","path","fs","YAML","opts","result","fs","path","path","fs","matter"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agents/adapter.ts","../src/agents/claude-code.ts","../src/agents/cursor.ts","../src/agents/registry.ts"],"sourcesContent":["/**\n * Agent adapter interface — declares what each coding agent provides to Myco.\n *\n * Each supported agent (Claude Code, Cursor, Cline, etc.) has an adapter that\n * tells Myco where to find transcripts, how to parse them, and what capabilities\n * the agent supports. The daemon uses these adapters at runtime to read the\n * authoritative conversation record.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/** An image attached to a conversation turn */\nexport interface TranscriptImage {\n /** Base64-encoded image data */\n data: string;\n /** MIME type (e.g., image/png) */\n mediaType: string;\n}\n\n/** A single conversation turn extracted from an agent's transcript */\nexport interface TranscriptTurn {\n prompt: string;\n toolCount: number;\n aiResponse?: string;\n timestamp: string;\n /** Images attached to this turn's user prompt */\n images?: TranscriptImage[];\n}\n\n/**\n * Maps agent-specific hook field names to normalized names.\n * Each agent's hook system uses different field names for the same data.\n */\nexport interface HookFieldNames {\n /** Field name for the transcript file path (e.g., 'transcript_path') */\n transcriptPath: string;\n /** Field name for the last AI response text (e.g., 'last_assistant_message') */\n lastResponse: string;\n /** Field name for the session ID (e.g., 'session_id') */\n sessionId: string;\n}\n\nexport interface AgentAdapter {\n /** Agent identifier (matches plugin directory names) */\n readonly name: string;\n /** Human-readable display name */\n readonly displayName: string;\n /** Environment variable for the plugin root directory */\n readonly pluginRootEnvVar: string;\n /** Maps agent-specific hook body field names to normalized names */\n readonly hookFields: HookFieldNames;\n\n /**\n * Find the transcript file for a given session ID.\n * Returns the absolute path if found, null otherwise.\n */\n findTranscript(sessionId: string): string | null;\n\n /**\n * Parse a transcript file's content into normalized turns.\n * Each adapter handles its agent's specific format.\n */\n parseTurns(content: string): TranscriptTurn[];\n\n /**\n * Write MYCO_VAULT_DIR into this agent's project-level config file.\n * Called during init when the vault is outside the project root.\n * Returns true if the config was written, false if not applicable.\n */\n configureVaultEnv(projectRoot: string, vaultDir: string): boolean;\n}\n\n/**\n * Scan subdirectories of baseDir for a JSONL transcript file matching sessionId.\n * Shared by claude-code, cursor, custom adapters, and tests.\n */\nexport function findJsonlInSubdirs(baseDir: string, sessionId: string): string | null {\n try {\n for (const entry of fs.readdirSync(baseDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const candidate = path.join(baseDir, entry.name, `${sessionId}.jsonl`);\n try {\n fs.accessSync(candidate);\n return candidate;\n } catch { /* not here */ }\n }\n } catch { /* baseDir doesn't exist or unreadable */ }\n return null;\n}\n\n/**\n * Factory for creating simple per-project adapters from a base directory.\n * Used for user-configured transcript_paths and testing.\n */\nexport function createPerProjectAdapter(\n baseDir: string,\n parseTurns: AgentAdapter['parseTurns'],\n name?: string,\n): AgentAdapter {\n return {\n name: name ?? `custom:${path.basename(baseDir)}`,\n displayName: `Custom (${baseDir})`,\n pluginRootEnvVar: '',\n hookFields: { transcriptPath: 'transcript_path', lastResponse: 'last_assistant_message', sessionId: 'session_id' },\n findTranscript: (sessionId) => findJsonlInSubdirs(baseDir, sessionId),\n parseTurns,\n configureVaultEnv: () => false,\n };\n}\n\n/** Map MIME type to file extension */\nconst MIME_TO_EXT: Record<string, string> = {\n 'image/jpeg': 'jpg',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n 'image/png': 'png',\n};\n\nexport function extensionForMimeType(mimeType: string): string {\n return MIME_TO_EXT[mimeType] ?? 'png';\n}\n\n/** Map file extension to MIME type */\nconst EXT_TO_MIME: Record<string, string> = {\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.png': 'image/png',\n};\n\nexport function mimeTypeForExtension(ext: string): string {\n return EXT_TO_MIME[ext.toLowerCase()] ?? 'image/png';\n}\n\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\n\n/** Claude Code injects [Image: source: /path] text alongside base64 image blocks. Strip these since the actual images are captured as Obsidian embeds. */\nconst IMAGE_TEXT_REF_PATTERN = /\\[Image: source: [^\\]]+\\]\\n*/g;\n\nexport interface ParseJsonlOptions {\n /** Field name containing the message role ('type' for Claude Code, 'role' for Cursor) */\n roleField: 'type' | 'role';\n /** Whether entries have a timestamp field to extract */\n extractTimestamp: boolean;\n /** Whether to check for text-only user messages (Claude Code has tool_result user messages to skip) */\n skipToolResultUsers: boolean;\n /** Whether to strip [Image: source: ...] text references from prompts (Claude Code-specific) */\n stripImageTextRefs: boolean;\n}\n\n/**\n * Shared JSONL transcript parser — used by both Claude Code and Cursor adapters.\n * Handles user/assistant role detection, text/image extraction, and tool counting.\n */\nexport function parseJsonlTurns(content: string, opts: ParseJsonlOptions): TranscriptTurn[] {\n const lines = content.split('\\n').filter(Boolean);\n const turns: TranscriptTurn[] = [];\n let current: TranscriptTurn | null = null;\n\n for (const line of lines) {\n let entry: Record<string, unknown>;\n try { entry = JSON.parse(line); } catch { continue; }\n\n const role = entry[opts.roleField] as string;\n const timestamp = opts.extractTimestamp ? (entry.timestamp as string ?? '') : '';\n\n if (role === 'user') {\n const msg = entry.message as { content?: Array<{ type: string; text?: string; source?: { type?: string; data?: string; media_type?: string } }> } | undefined;\n const blocks = Array.isArray(msg?.content) ? msg!.content : [];\n const hasText = blocks.some((b) => b.type === 'text' && b.text?.trim());\n\n if (!hasText && opts.skipToolResultUsers) continue;\n if (!hasText) continue;\n\n if (current) turns.push(current);\n\n const rawPrompt = blocks\n .filter((b) => b.type === 'text' && b.text)\n .map((b) => b.text!)\n .join('\\n');\n\n const promptText = (opts.stripImageTextRefs ? rawPrompt.replace(IMAGE_TEXT_REF_PATTERN, '') : rawPrompt)\n .trim()\n .slice(0, PROMPT_PREVIEW_CHARS);\n\n const images: TranscriptImage[] = blocks\n .filter((b) => b.type === 'image' && b.source?.type === 'base64' && b.source.data)\n .map((b) => ({ data: b.source!.data!, mediaType: b.source!.media_type ?? 'image/png' }));\n\n current = { prompt: promptText, toolCount: 0, timestamp, ...(images.length > 0 ? { images } : {}) };\n } else if (role === 'assistant' && current) {\n const msg = entry.message as { content?: Array<{ type: string; text?: string }> } | undefined;\n if (Array.isArray(msg?.content)) {\n const textParts = msg!.content.filter((b) => b.type === 'text' && b.text).map((b) => b.text!);\n const text = textParts.join('\\n').trim();\n if (text) current.aiResponse = text;\n current.toolCount += msg!.content.filter((b) => b.type === 'tool_use').length;\n }\n }\n }\n\n if (current) turns.push(current);\n return turns;\n}\n","import type { AgentAdapter } from './adapter.js';\nimport { findJsonlInSubdirs, parseJsonlTurns } from './adapter.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst TRANSCRIPT_BASE = path.join(os.homedir(), '.claude', 'projects');\n\nexport const claudeCodeAdapter: AgentAdapter = {\n name: 'claude-code',\n displayName: 'Claude Code',\n pluginRootEnvVar: 'CLAUDE_PLUGIN_ROOT',\n hookFields: {\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n sessionId: 'session_id',\n },\n\n findTranscript: (sessionId) => findJsonlInSubdirs(TRANSCRIPT_BASE, sessionId),\n\n parseTurns: (content) => parseJsonlTurns(content, {\n roleField: 'type',\n extractTimestamp: true,\n skipToolResultUsers: true,\n stripImageTextRefs: true,\n }),\n\n configureVaultEnv: (projectRoot, vaultDir) => {\n const settingsDir = path.join(projectRoot, '.claude');\n if (!fs.existsSync(settingsDir)) return false;\n\n // Write to settings.json — Claude Code only injects env vars from this\n // file into hook processes (settings.user.json env is not propagated).\n // The caller passes the collapsed ~/... form so the committed path\n // doesn't leak the user's home directory.\n const settingsPath = path.join(settingsDir, 'settings.json');\n let settings: Record<string, unknown> = {};\n if (fs.existsSync(settingsPath)) {\n try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')); } catch { /* fresh */ }\n }\n const env = (settings.env ?? {}) as Record<string, string>;\n env.MYCO_VAULT_DIR = vaultDir;\n settings.env = env;\n fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n', 'utf-8');\n return true;\n },\n};\n","import type { AgentAdapter } from './adapter.js';\nimport type { TranscriptTurn, TranscriptImage } from './adapter.js';\nimport { mimeTypeForExtension, parseJsonlTurns } from './adapter.js';\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\n/**\n * Cursor stores conversation transcripts in:\n * ~/.cursor/projects/<project-path>/agent-transcripts/<session-id>.txt\n *\n * Images are saved as files in:\n * ~/.cursor/projects/<project-path>/assets/<filename>.png\n *\n * Transcript format is plain text with role markers on their own line:\n * user: — human prompt (may contain <image_files> and <user_query> tags)\n * assistant: — assistant response (may contain [Tool call] and [Thinking] blocks)\n */\n\nconst USER_MARKER = '\\nuser:\\n';\nconst ASSISTANT_MARKER = '\\nassistant:\\n';\nconst TOOL_CALL_MARKER = '[Tool call]';\nconst TOOL_RESULT_MARKER = '[Tool result]';\nconst THINKING_MARKER = '[Thinking]';\n\nfunction getCursorProjectsBase(): string {\n return path.join(os.homedir(), '.cursor', 'projects');\n}\n\nconst CURSOR_PROJECTS = getCursorProjectsBase();\n\nexport const cursorAdapter: AgentAdapter = {\n name: 'cursor',\n displayName: 'Cursor',\n pluginRootEnvVar: 'CURSOR_PLUGIN_ROOT',\n hookFields: {\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n sessionId: 'conversation_id',\n },\n\n findTranscript(sessionId: string): string | null {\n try {\n for (const project of fs.readdirSync(CURSOR_PROJECTS, { withFileTypes: true })) {\n if (!project.isDirectory()) continue;\n const transcriptsDir = path.join(CURSOR_PROJECTS, project.name, 'agent-transcripts');\n // Try .txt (older Cursor) then .jsonl inside session directory (newer Cursor)\n for (const candidate of [\n path.join(transcriptsDir, `${sessionId}.txt`),\n path.join(transcriptsDir, sessionId, `${sessionId}.jsonl`),\n ]) {\n try {\n fs.accessSync(candidate);\n return candidate;\n } catch { /* not here */ }\n }\n }\n } catch { /* projects dir doesn't exist */ }\n return null;\n },\n\n parseTurns(content: string): TranscriptTurn[] {\n // Detect format: JSONL (starts with '{') or plain text (starts with 'user:')\n const trimmed = content.trimStart();\n if (trimmed.startsWith('{')) {\n return parseCursorJsonl(content);\n }\n return parseCursorText(content);\n },\n\n configureVaultEnv(projectRoot: string, vaultDir: string): boolean {\n const mcpPath = path.join(projectRoot, '.cursor', 'mcp.json');\n if (!fs.existsSync(mcpPath)) return false;\n\n try {\n const config = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));\n if (config.mcpServers?.myco) {\n config.mcpServers.myco.env = { ...config.mcpServers.myco.env, MYCO_VAULT_DIR: vaultDir };\n fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n return true;\n }\n } catch { /* malformed config */ }\n return false;\n },\n};\n\n/** Parse Cursor's newer JSONL format — same structure as Claude's but uses 'role' field */\nfunction parseCursorJsonl(content: string): TranscriptTurn[] {\n return parseJsonlTurns(content, {\n roleField: 'role',\n extractTimestamp: false,\n skipToolResultUsers: false,\n stripImageTextRefs: false,\n });\n}\n\n/** Parse Cursor's older plain-text transcript format. */\nfunction parseCursorText(content: string): TranscriptTurn[] {\n const turns: TranscriptTurn[] = [];\n // Split on user marker — each block is a new human turn.\n const sections = ('\\n' + content).split(USER_MARKER).slice(1);\n\n for (const section of sections) {\n // Extract user query from <user_query> tags or raw text before first assistant response\n let promptText = '';\n const queryMatch = section.match(/<user_query>\\s*([\\s\\S]*?)\\s*<\\/user_query>/);\n if (queryMatch) {\n promptText = queryMatch[1].trim().slice(0, PROMPT_PREVIEW_CHARS);\n } else {\n // No tags — take text before the first assistant response.\n const beforeAssistant = section.split(ASSISTANT_MARKER)[0];\n promptText = beforeAssistant.replace(/<[^>]+>[\\s\\S]*?<\\/[^>]+>/g, '').trim().slice(0, PROMPT_PREVIEW_CHARS);\n }\n\n // Extract image references from <image_files> tags\n const images: TranscriptImage[] = [];\n const imageFilesMatch = section.match(/<image_files>([\\s\\S]*?)<\\/image_files>/);\n if (imageFilesMatch) {\n const imageBlock = imageFilesMatch[1];\n const pathMatches = imageBlock.matchAll(/^\\d+\\.\\s+(.+\\.(?:png|jpg|jpeg|gif|webp))\\s*$/gmi);\n for (const match of pathMatches) {\n const imagePath = match[1].trim();\n try {\n const data = fs.readFileSync(imagePath).toString('base64');\n const mediaType = mimeTypeForExtension(path.extname(imagePath));\n images.push({ data, mediaType });\n } catch {\n // Image file not accessible — skip\n }\n }\n }\n\n // Count tool calls in assistant sections\n const toolCallCount = section.split(TOOL_CALL_MARKER).length - 1;\n\n // Extract the last meaningful assistant text response.\n // Scan assistant blocks (split on \\nA:\\n) from the end.\n // A block is \"meaningful\" if it contains lines that aren't tool calls/results/thinking.\n let aiResponse: string | undefined;\n const assistantBlocks = section.split(ASSISTANT_MARKER).slice(1);\n for (let j = assistantBlocks.length - 1; j >= 0; j--) {\n const lines = assistantBlocks[j].split('\\n');\n const textLines: string[] = [];\n let skip = false;\n for (const line of lines) {\n // Skip tool calls, tool results, and thinking blocks\n if (line.startsWith(TOOL_CALL_MARKER) || line.startsWith(TOOL_RESULT_MARKER) || line.startsWith(THINKING_MARKER)) {\n skip = true;\n continue;\n }\n // Resume after a blank line following a skipped block\n if (skip && line.trim() === '') continue;\n if (skip && !line.startsWith(' ')) skip = false; // End of indented tool args\n if (skip) continue;\n textLines.push(line);\n }\n const text = textLines.join('\\n').trim();\n if (text) {\n aiResponse = text;\n break;\n }\n }\n\n if (promptText || images.length > 0) {\n turns.push({\n prompt: promptText,\n toolCount: toolCallCount,\n timestamp: '',\n ...(aiResponse ? { aiResponse } : {}),\n ...(images.length > 0 ? { images } : {}),\n });\n }\n }\n\n return turns;\n}\n","import type { AgentAdapter, TranscriptTurn } from './adapter.js';\nimport { claudeCodeAdapter } from './claude-code.js';\nimport { cursorAdapter } from './cursor.js';\nimport fs from 'node:fs';\n\n/**\n * All known agent adapters, ordered by priority.\n * When searching for a transcript, adapters are tried in order.\n * Add new adapters here as agent support grows.\n */\nconst ALL_ADAPTERS: AgentAdapter[] = [\n claudeCodeAdapter,\n cursorAdapter,\n];\n\nexport class AgentRegistry {\n private adapters: AgentAdapter[];\n\n constructor(additionalAdapters: AgentAdapter[] = []) {\n this.adapters = [...ALL_ADAPTERS, ...additionalAdapters];\n }\n\n /**\n * Find and parse transcript turns for a session.\n * Tries each adapter in priority order. Returns the first match.\n */\n getTranscriptTurns(sessionId: string): { turns: TranscriptTurn[]; source: string } | null {\n for (const adapter of this.adapters) {\n const filePath = adapter.findTranscript(sessionId);\n if (!filePath) continue;\n\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const turns = adapter.parseTurns(content);\n if (turns.length > 0) {\n return { turns, source: adapter.name };\n }\n } catch {\n // Adapter found a path but read/parse failed — try next\n }\n }\n return null;\n }\n\n /** List all registered adapter names */\n get adapterNames(): string[] {\n return this.adapters.map((a) => a.name);\n }\n\n /** Get a specific adapter by name */\n getAdapter(name: string): AgentAdapter | undefined {\n return this.adapters.find((a) => a.name === name);\n }\n\n /** Detect which agent is currently active based on environment variables */\n detectActiveAgent(): AgentAdapter | undefined {\n for (const adapter of this.adapters) {\n if (process.env[adapter.pluginRootEnvVar]) {\n return adapter;\n }\n }\n return undefined;\n }\n\n /**\n * Parse turns from a known transcript file path (provided by hook).\n * Tries each adapter's parseTurns until one produces results.\n * Skips directory scanning entirely — the path is already known.\n */\n parseTurnsFromPath(filePath: string): { turns: TranscriptTurn[]; source: string } | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n // Try the active agent's parser first, then fall back to others\n const active = this.detectActiveAgent();\n const orderedAdapters = active\n ? [active, ...this.adapters.filter((a) => a !== active)]\n : this.adapters;\n\n for (const adapter of orderedAdapters) {\n const turns = adapter.parseTurns(content);\n if (turns.length > 0) {\n return { turns, source: `${adapter.name}:direct` };\n }\n }\n } catch {\n // File unreadable — caller will fall back to directory scanning\n }\n return null;\n }\n\n /**\n * Resolve the plugin root directory from the active agent's environment variable.\n * Returns undefined if no agent env var is set (e.g., running from CLI directly).\n */\n resolvePluginRoot(): string | undefined {\n for (const adapter of this.adapters) {\n const value = process.env[adapter.pluginRootEnvVar];\n if (value) return value;\n }\n return undefined;\n }\n}\n"],"mappings":"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","names":["fs","path","fs","path","os","fs"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Shared constants for the Myco codebase.\n * Per CLAUDE.md: \"No Magic Literals — Numeric and string constants\n * MUST NOT appear inline in logic.\"\n */\n\n// --- Token estimation ---\n/** Approximate characters per token for the chars/4 heuristic. */\nexport const CHARS_PER_TOKEN = 4;\n\n/** Estimate token count from character length using the CHARS_PER_TOKEN heuristic. */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / CHARS_PER_TOKEN);\n}\n\n// --- Embedding ---\n/** Max characters of text sent to the embedding model. */\nexport const EMBEDDING_INPUT_LIMIT = 8000;\n\n// --- Truncation limits (display/preview) ---\n/** Max chars for a user prompt preview in event summaries. */\nexport const PROMPT_PREVIEW_CHARS = 300;\n/** Max chars for an AI response preview in event summaries. */\nexport const AI_RESPONSE_PREVIEW_CHARS = 500;\n/** Max chars for a command string preview. */\nexport const COMMAND_PREVIEW_CHARS = 80;\n/** Max chars for a content snippet in search results. */\nexport const CONTENT_SNIPPET_CHARS = 120;\n/** Max chars for a tool output preview in hooks. */\nexport const TOOL_OUTPUT_PREVIEW_CHARS = 200;\n/** Max chars for a session summary preview in MCP tools. */\nexport const SESSION_SUMMARY_PREVIEW_CHARS = 300;\n/** Max chars for a recall summary preview. */\nexport const RECALL_SUMMARY_PREVIEW_CHARS = 200;\n\n// --- Context injection layer budgets (chars, not tokens — used with .slice()) ---\nexport const CONTEXT_PLAN_PREVIEW_CHARS = 100;\nexport const CONTEXT_SESSION_PREVIEW_CHARS = 80;\nexport const CONTEXT_SPORE_PREVIEW_CHARS = 80;\n\n// --- Processor maxTokens budgets ---\n/** Response token budget for observation extraction. */\nexport const EXTRACTION_MAX_TOKENS = 2048;\n/** Response token budget for session summary. */\nexport const SUMMARY_MAX_TOKENS = 512;\n/** Response token budget for session title generation. */\nexport const TITLE_MAX_TOKENS = 32;\n/** Response token budget for artifact classification. */\nexport const CLASSIFICATION_MAX_TOKENS = 1024;\n\n// --- Timeouts ---\n/** Daemon client HTTP request timeout (ms). */\nexport const DAEMON_CLIENT_TIMEOUT_MS = 2000;\n/** Health check timeout (ms) — fail fast if daemon isn't responding. */\nexport const DAEMON_HEALTH_CHECK_TIMEOUT_MS = 500;\n/** LLM request timeout (ms). All LLM calls are background daemon work — no need to be aggressive. */\nexport const LLM_REQUEST_TIMEOUT_MS = 180_000;\n/** Embedding request timeout (ms). Embeddings run in background batch processing — generous timeout. */\nexport const EMBEDDING_REQUEST_TIMEOUT_MS = 60_000;\n/** Digest LLM request timeout (ms). Digest cycles use large context windows and may need model loading time. */\nexport const DIGEST_LLM_REQUEST_TIMEOUT_MS = 600_000;\n/** Stdin read timeout for hooks (ms). */\nexport const STDIN_TIMEOUT_MS = 100;\n/** Chokidar write stability threshold (ms). */\nexport const FILE_WATCH_STABILITY_MS = 1000;\n/** Provider detection timeout for detect-providers CLI command (ms). */\nexport const PROVIDER_DETECT_TIMEOUT_MS = 3000;\n\n// --- Buffer cleanup ---\n/** Max age for stale buffer files before cleanup (ms). */\nexport const STALE_BUFFER_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n\n// --- Retry backoff ---\n/** Retry delays for daemon health check (ms). */\nexport const DAEMON_HEALTH_RETRY_DELAYS = [100, 200, 400, 800, 1500];\n\n/** Grace period after daemon.json is written before stale checks can trigger a restart (ms).\n * Prevents rapid restart loops from concurrent hooks or session reloads. */\nexport const DAEMON_STALE_GRACE_PERIOD_MS = 60_000;\n\n/** Grace period for SIGTERM before escalating to SIGKILL (ms).\n * Gives the old daemon a chance to shut down cleanly, but force-kills\n * to guarantee the configured port is reclaimed. */\nexport const DAEMON_EVICT_TIMEOUT_MS = 3000;\n/** Poll interval when waiting for an evicted daemon to die (ms). */\nexport const DAEMON_EVICT_POLL_MS = 100;\n\n// --- Slug limits ---\n/** Max length for slugified artifact IDs. */\nexport const MAX_SLUG_LENGTH = 100;\n\n// --- Content preview for classification prompt ---\n/** Max chars of file content per candidate in classification prompt. */\nexport const CANDIDATE_CONTENT_PREVIEW = 2000;\n\n// --- Transcript mining ---\n/** Minimum content length to consider a transcript entry meaningful. */\nexport const MIN_TRANSCRIPT_CONTENT_LENGTH = 10;\n\n// --- Query limits ---\n/** Max recent sessions to check for lineage heuristics. */\nexport const LINEAGE_RECENT_SESSIONS_LIMIT = 5;\n/** Max related spores to query for session notes. */\nexport const RELATED_SPORES_LIMIT = 50;\n\n// --- Context injection ---\n/** Max active plans to inject at session start. */\nexport const SESSION_CONTEXT_MAX_PLANS = 3;\n/** Max spores to inject per prompt. */\nexport const PROMPT_CONTEXT_MAX_SPORES = 3;\n/** Minimum similarity score for prompt context injection (0-1). */\nexport const PROMPT_CONTEXT_MIN_SIMILARITY = 0.3;\n/** Max token budget for session-start context injection. */\nexport const SESSION_CONTEXT_MAX_TOKENS = 500;\n/** Max token budget for per-prompt context injection. */\nexport const PROMPT_CONTEXT_MAX_TOKENS = 300;\n/** Minimum prompt length to trigger context search. */\nexport const PROMPT_CONTEXT_MIN_LENGTH = 10;\n\n// --- MCP tool defaults ---\n/** Default result limit for myco_search. */\nexport const MCP_SEARCH_DEFAULT_LIMIT = 10;\n/** Default result limit for myco_sessions. */\nexport const MCP_SESSIONS_DEFAULT_LIMIT = 20;\n/** Default result limit for myco_logs. */\nexport const MCP_LOGS_DEFAULT_LIMIT = 50;\n\n// --- Digest — Tiers ---\n/** Available token-budget tiers for digest synthesis. */\nexport const DIGEST_TIERS = [1500, 3000, 5000, 7500, 10000] as const;\nexport type DigestTier = (typeof DIGEST_TIERS)[number];\n\n// --- Digest — Context window minimums per tier ---\n/** Minimum context window (tokens) required to run a digest at a given tier. */\nexport const DIGEST_TIER_MIN_CONTEXT: Record<number, number> = {\n 1500: 6500,\n 3000: 11500,\n 5000: 18500,\n 7500: 24500,\n 10000: 30500,\n};\n\n// --- Digest — Substrate ---\n/** Scoring weights by note type when selecting substrate for synthesis. */\nexport const DIGEST_SUBSTRATE_TYPE_WEIGHTS: Record<string, number> = {\n session: 3,\n spore: 3,\n plan: 2,\n artifact: 1,\n team: 1,\n};\n\n// --- LLM reasoning control ---\n/** Reasoning mode for all Myco LLM calls. Suppresses chain-of-thought tokens from reasoning models. */\nexport const LLM_REASONING_MODE = 'off' as const;\n\n// --- Digest — System prompt overhead estimate ---\n\n// --- Vault curation ---\n/** Max candidate spores after post-filtering for supersession check. */\nexport const SUPERSESSION_CANDIDATE_LIMIT = 5;\n\n/** Over-fetch from vector index before post-filtering by status/type. */\nexport const SUPERSESSION_VECTOR_FETCH_LIMIT = 20;\n\n/** Max output tokens for supersession LLM evaluation. */\nexport const SUPERSESSION_MAX_TOKENS = 256;\n\n/** Similarity threshold for clustering related spores in batch curation. */\nexport const CURATION_CLUSTER_SIMILARITY = 0.75;\n\n// --- Automatic consolidation ---\n/** Minimum cluster size required before asking LLM to consolidate. */\nexport const CONSOLIDATION_MIN_CLUSTER_SIZE = 3;\n\n/** Over-fetch from vector index before post-filtering by status/type. */\nexport const CONSOLIDATION_VECTOR_FETCH_LIMIT = 20;\n\n/** Max output tokens for consolidation LLM synthesis.\n * Must be large enough for the full JSON response including content field. */\nexport const CONSOLIDATION_MAX_TOKENS = 2048;\n"],"mappings":";;;AAQO,IAAM,kBAAkB;AAGxB,SAAS,eAAe,MAAsB;AACnD,SAAO,KAAK,KAAK,KAAK,SAAS,eAAe;AAChD;AAIO,IAAM,wBAAwB;AAI9B,IAAM,uBAAuB;AAE7B,IAAM,4BAA4B;AAElC,IAAM,wBAAwB;AAE9B,IAAM,wBAAwB;AAE9B,IAAM,4BAA4B;AAElC,IAAM,gCAAgC;AAEtC,IAAM,+BAA+B;AAGrC,IAAM,6BAA6B;AACnC,IAAM,gCAAgC;AACtC,IAAM,8BAA8B;AAcpC,IAAM,2BAA2B;AAEjC,IAAM,iCAAiC;AAEvC,IAAM,yBAAyB;AAE/B,IAAM,+BAA+B;AAErC,IAAM,gCAAgC;AAEtC,IAAM,mBAAmB;AAEzB,IAAM,0BAA0B;AAEhC,IAAM,6BAA6B;AAInC,IAAM,0BAA0B,KAAK,KAAK,KAAK;AAI/C,IAAM,6BAA6B,CAAC,KAAK,KAAK,KAAK,KAAK,IAAI;AAI5D,IAAM,+BAA+B;AAKrC,IAAM,0BAA0B;AAEhC,IAAM,uBAAuB;AAI7B,IAAM,kBAAkB;AAIxB,IAAM,4BAA4B;AAQlC,IAAM,gCAAgC;AAEtC,IAAM,uBAAuB;AAI7B,IAAM,4BAA4B;AAElC,IAAM,4BAA4B;AAElC,IAAM,gCAAgC;AAMtC,IAAM,4BAA4B;AAIlC,IAAM,2BAA2B;AAEjC,IAAM,6BAA6B;AAEnC,IAAM,yBAAyB;AAI/B,IAAM,eAAe,CAAC,MAAM,KAAM,KAAM,MAAM,GAAK;AAKnD,IAAM,0BAAkD;AAAA,EAC7D,MAAM;AAAA,EACN,KAAM;AAAA,EACN,KAAM;AAAA,EACN,MAAM;AAAA,EACN,KAAO;AACT;AAIO,IAAM,gCAAwD;AAAA,EACnE,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AACR;AAIO,IAAM,qBAAqB;AAM3B,IAAM,+BAA+B;AAGrC,IAAM,kCAAkC;AAGxC,IAAM,0BAA0B;AAGhC,IAAM,8BAA8B;AAIpC,IAAM,iCAAiC;AAGvC,IAAM,mCAAmC;AAIzC,IAAM,2BAA2B;","names":[]}