@goondocks/myco 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +10 -11
  4. package/dist/{chunk-2YBUL3IL.js → chunk-25DJSF2K.js} +3 -3
  5. package/dist/{chunk-24DOZEUJ.js → chunk-ALBVNGCF.js} +591 -27
  6. package/dist/chunk-ALBVNGCF.js.map +1 -0
  7. package/dist/{chunk-E7OBRBCQ.js → chunk-CK24O5YQ.js} +12 -2
  8. package/dist/chunk-CK24O5YQ.js.map +1 -0
  9. package/dist/{chunk-2GSX3BK2.js → chunk-CPVXNRGW.js} +4 -4
  10. package/dist/{chunk-L25U7PIG.js → chunk-CQ4RKK67.js} +2 -2
  11. package/dist/{chunk-ZMYNRTTD.js → chunk-HRGHDMYI.js} +4 -3
  12. package/dist/chunk-HRGHDMYI.js.map +1 -0
  13. package/dist/{chunk-5FNZ7AMX.js → chunk-IWBWZQK6.js} +2 -2
  14. package/dist/{chunk-MQSYSQ6T.js → chunk-JSK7L46L.js} +11 -6
  15. package/dist/{chunk-MQSYSQ6T.js.map → chunk-JSK7L46L.js.map} +1 -1
  16. package/dist/{chunk-KUMVJIJW.js → chunk-LDKXXKF6.js} +6 -10
  17. package/dist/{chunk-KUMVJIJW.js.map → chunk-LDKXXKF6.js.map} +1 -1
  18. package/dist/{chunk-QGJ2ZIUZ.js → chunk-MWW62YZP.js} +37 -5
  19. package/dist/chunk-MWW62YZP.js.map +1 -0
  20. package/dist/{chunk-2ZBB3MQT.js → chunk-PQWQC3RF.js} +444 -21
  21. package/dist/chunk-PQWQC3RF.js.map +1 -0
  22. package/dist/{chunk-5QWZT4AB.js → chunk-RNWALAFP.js} +2 -2
  23. package/dist/{chunk-3EM23DMD.js → chunk-RXJHB7W4.js} +2 -2
  24. package/dist/{chunk-GNR3QAER.js → chunk-RY76WEN3.js} +2 -2
  25. package/dist/{chunk-GDYYJTTT.js → chunk-V5R6O6RP.js} +3 -3
  26. package/dist/{chunk-6BSDCZ5Q.js → chunk-WBLTISAK.js} +8 -3
  27. package/dist/chunk-WBLTISAK.js.map +1 -0
  28. package/dist/{chunk-YTANWAGE.js → chunk-XNAM6Z4O.js} +2 -2
  29. package/dist/{chunk-P3WO3N3I.js → chunk-YG6MLLGL.js} +19 -3
  30. package/dist/{chunk-P3WO3N3I.js.map → chunk-YG6MLLGL.js.map} +1 -1
  31. package/dist/{cli-K7SUTP7A.js → cli-LMBBPV2D.js} +20 -20
  32. package/dist/{client-YJMNTITQ.js → client-FDKJ4BY7.js} +5 -5
  33. package/dist/{config-G5GGT5A6.js → config-HDUFDOQN.js} +3 -3
  34. package/dist/{curate-6T5NKVXK.js → curate-DYE4VCBJ.js} +10 -11
  35. package/dist/{curate-6T5NKVXK.js.map → curate-DYE4VCBJ.js.map} +1 -1
  36. package/dist/{detect-providers-S3M5TAMW.js → detect-providers-I2QQFDJW.js} +3 -3
  37. package/dist/{digest-O35VHYFP.js → digest-PNHFM7JJ.js} +11 -13
  38. package/dist/{digest-O35VHYFP.js.map → digest-PNHFM7JJ.js.map} +1 -1
  39. package/dist/{init-TFLSATB3.js → init-7N7F6W6U.js} +8 -8
  40. package/dist/{main-JEUQS3BY.js → main-3JZDUJLU.js} +177 -40
  41. package/dist/main-3JZDUJLU.js.map +1 -0
  42. package/dist/{rebuild-7SH5GSNX.js → rebuild-WXKQ5HZO.js} +10 -11
  43. package/dist/{rebuild-7SH5GSNX.js.map → rebuild-WXKQ5HZO.js.map} +1 -1
  44. package/dist/reprocess-PKRDV67L.js +79 -0
  45. package/dist/reprocess-PKRDV67L.js.map +1 -0
  46. package/dist/{restart-NLJLB52D.js → restart-WSJRHRHI.js} +6 -6
  47. package/dist/{search-2BVRF54H.js → search-SWMJ4MZ3.js} +6 -6
  48. package/dist/{server-4AMZNP4F.js → server-NTRVB5ZM.js} +14 -18
  49. package/dist/{server-4AMZNP4F.js.map → server-NTRVB5ZM.js.map} +1 -1
  50. package/dist/{session-start-AZAF3DTE.js → session-start-KQ4KCQMZ.js} +9 -9
  51. package/dist/setup-digest-BOYOSM4B.js +15 -0
  52. package/dist/setup-llm-PCZ64ALK.js +15 -0
  53. package/dist/src/cli.js +4 -4
  54. package/dist/src/daemon/main.js +4 -4
  55. package/dist/src/hooks/post-tool-use.js +5 -5
  56. package/dist/src/hooks/session-end.js +5 -5
  57. package/dist/src/hooks/session-start.js +4 -4
  58. package/dist/src/hooks/stop.js +7 -7
  59. package/dist/src/hooks/user-prompt-submit.js +5 -5
  60. package/dist/src/mcp/server.js +4 -4
  61. package/dist/src/prompts/consolidation.md +2 -0
  62. package/dist/src/prompts/digest-7500.md +68 -0
  63. package/dist/{stats-MKDIZFIQ.js → stats-2OUQSEZO.js} +6 -6
  64. package/dist/ui/assets/index-Bk4X_8-Z.css +1 -0
  65. package/dist/ui/assets/index-D3SY7ZHY.js +299 -0
  66. package/dist/ui/index.html +2 -2
  67. package/dist/{verify-7DW7LAND.js → verify-MG5O7SBU.js} +6 -6
  68. package/dist/{version-RQLD7VBP.js → version-NKOECSVH.js} +4 -4
  69. package/package.json +3 -3
  70. package/dist/chunk-24DOZEUJ.js.map +0 -1
  71. package/dist/chunk-2ZBB3MQT.js.map +0 -1
  72. package/dist/chunk-3JCXYLHD.js +0 -33
  73. package/dist/chunk-3JCXYLHD.js.map +0 -1
  74. package/dist/chunk-6BSDCZ5Q.js.map +0 -1
  75. package/dist/chunk-B5UZSHQV.js +0 -250
  76. package/dist/chunk-B5UZSHQV.js.map +0 -1
  77. package/dist/chunk-E7OBRBCQ.js.map +0 -1
  78. package/dist/chunk-KC7ENQTN.js +0 -436
  79. package/dist/chunk-KC7ENQTN.js.map +0 -1
  80. package/dist/chunk-QGJ2ZIUZ.js.map +0 -1
  81. package/dist/chunk-UVGAVYWZ.js +0 -157
  82. package/dist/chunk-UVGAVYWZ.js.map +0 -1
  83. package/dist/chunk-ZMYNRTTD.js.map +0 -1
  84. package/dist/main-JEUQS3BY.js.map +0 -1
  85. package/dist/reprocess-Q4YH2ZBK.js +0 -268
  86. package/dist/reprocess-Q4YH2ZBK.js.map +0 -1
  87. package/dist/setup-digest-YLZZGSSR.js +0 -15
  88. package/dist/setup-llm-JOXBSLXC.js +0 -15
  89. package/dist/ui/assets/index-D37IoDXS.css +0 -1
  90. package/dist/ui/assets/index-DA61Ial2.js +0 -289
  91. /package/dist/{chunk-2YBUL3IL.js.map → chunk-25DJSF2K.js.map} +0 -0
  92. /package/dist/{chunk-2GSX3BK2.js.map → chunk-CPVXNRGW.js.map} +0 -0
  93. /package/dist/{chunk-L25U7PIG.js.map → chunk-CQ4RKK67.js.map} +0 -0
  94. /package/dist/{chunk-5FNZ7AMX.js.map → chunk-IWBWZQK6.js.map} +0 -0
  95. /package/dist/{chunk-5QWZT4AB.js.map → chunk-RNWALAFP.js.map} +0 -0
  96. /package/dist/{chunk-3EM23DMD.js.map → chunk-RXJHB7W4.js.map} +0 -0
  97. /package/dist/{chunk-GNR3QAER.js.map → chunk-RY76WEN3.js.map} +0 -0
  98. /package/dist/{chunk-GDYYJTTT.js.map → chunk-V5R6O6RP.js.map} +0 -0
  99. /package/dist/{chunk-YTANWAGE.js.map → chunk-XNAM6Z4O.js.map} +0 -0
  100. /package/dist/{cli-K7SUTP7A.js.map → cli-LMBBPV2D.js.map} +0 -0
  101. /package/dist/{client-YJMNTITQ.js.map → client-FDKJ4BY7.js.map} +0 -0
  102. /package/dist/{config-G5GGT5A6.js.map → config-HDUFDOQN.js.map} +0 -0
  103. /package/dist/{detect-providers-S3M5TAMW.js.map → detect-providers-I2QQFDJW.js.map} +0 -0
  104. /package/dist/{init-TFLSATB3.js.map → init-7N7F6W6U.js.map} +0 -0
  105. /package/dist/{restart-NLJLB52D.js.map → restart-WSJRHRHI.js.map} +0 -0
  106. /package/dist/{search-2BVRF54H.js.map → search-SWMJ4MZ3.js.map} +0 -0
  107. /package/dist/{session-start-AZAF3DTE.js.map → session-start-KQ4KCQMZ.js.map} +0 -0
  108. /package/dist/{setup-digest-YLZZGSSR.js.map → setup-digest-BOYOSM4B.js.map} +0 -0
  109. /package/dist/{setup-llm-JOXBSLXC.js.map → setup-llm-PCZ64ALK.js.map} +0 -0
  110. /package/dist/{stats-MKDIZFIQ.js.map → stats-2OUQSEZO.js.map} +0 -0
  111. /package/dist/{verify-7DW7LAND.js.map → verify-MG5O7SBU.js.map} +0 -0
  112. /package/dist/{version-RQLD7VBP.js.map → version-NKOECSVH.js.map} +0 -0
@@ -1,23 +1,19 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
- VaultWriter
4
- } from "./chunk-KC7ENQTN.js";
5
- import {
3
+ VaultWriter,
4
+ indexNote,
6
5
  supersedeSpore
7
- } from "./chunk-UVGAVYWZ.js";
8
- import {
9
- indexNote
10
- } from "./chunk-24DOZEUJ.js";
6
+ } from "./chunk-ALBVNGCF.js";
11
7
  import {
12
8
  generateEmbedding
13
9
  } from "./chunk-RGVBGTD6.js";
14
10
  import {
15
11
  stripFrontmatter
16
- } from "./chunk-GNR3QAER.js";
12
+ } from "./chunk-RY76WEN3.js";
17
13
  import {
18
14
  DIGEST_TIERS,
19
15
  EMBEDDING_INPUT_LIMIT
20
- } from "./chunk-6BSDCZ5Q.js";
16
+ } from "./chunk-WBLTISAK.js";
21
17
 
22
18
  // src/mcp/tools/context.ts
23
19
  import fs from "fs";
@@ -114,4 +110,4 @@ export {
114
110
  consolidateSpores,
115
111
  handleMycoContext
116
112
  };
117
- //# sourceMappingURL=chunk-KUMVJIJW.js.map
113
+ //# sourceMappingURL=chunk-LDKXXKF6.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp/tools/context.ts","../src/vault/consolidation.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { stripFrontmatter } from '../../vault/frontmatter.js';\nimport { DIGEST_TIERS } from '../../constants.js';\n\n/** Default tier when none is requested. */\nconst DEFAULT_CONTEXT_TIER = 3000;\n\ninterface ContextInput {\n tier?: number;\n}\n\nexport interface ContextResult {\n content: string;\n tier: number;\n fallback: boolean;\n generated?: string;\n}\n\n/**\n * Try to read a digest extract file. Returns null if the file doesn't exist.\n * Strips YAML frontmatter and extracts the generated timestamp.\n */\nfunction tryReadExtract(filePath: string, tier: number, fallback: boolean): ContextResult | null {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n\n const { body, frontmatter } = stripFrontmatter(raw);\n const generated = frontmatter.generated as string | undefined;\n\n return {\n content: body,\n tier,\n fallback,\n generated,\n };\n}\n\nexport function handleMycoContext(vaultDir: string, input: ContextInput): ContextResult {\n const requestedTier = input.tier ?? DEFAULT_CONTEXT_TIER;\n const digestDir = path.join(vaultDir, 'digest');\n\n // Try exact tier first\n const exact = tryReadExtract(path.join(digestDir, `extract-${requestedTier}.md`), requestedTier, false);\n if (exact) return exact;\n\n // Fall back to nearest available tier\n const candidates = [...DIGEST_TIERS]\n .sort((a, b) => Math.abs(a - requestedTier) - Math.abs(b - requestedTier));\n\n for (const tier of candidates) {\n const result = tryReadExtract(path.join(digestDir, `extract-${tier}.md`), tier, true);\n if (result) return result;\n }\n\n return {\n content: 'Digest context is not yet available. The first digest cycle has not completed.',\n tier: requestedTier,\n fallback: false,\n };\n}\n","/**\n * Shared consolidation core — creates a wisdom note from a set of source spores\n * and marks each source as superseded.\n *\n * Used by both the MCP tool (`myco_consolidate`) and the daemon's automatic\n * consolidation pass so the logic lives in exactly one place.\n */\n\nimport { VaultWriter } from './writer.js';\nimport { supersedeSpore } from './curation.js';\nimport { indexNote } from '../index/rebuild.js';\nimport type { MycoIndex } from '../index/sqlite.js';\nimport type { VectorIndex } from '../index/vectors.js';\nimport type { EmbeddingProvider } from '../intelligence/llm.js';\nimport { generateEmbedding } from '../intelligence/embeddings.js';\nimport { EMBEDDING_INPUT_LIMIT } from '../constants.js';\nimport { randomBytes } from 'node:crypto';\n\nexport interface ConsolidateInput {\n sourceSporeIds: string[];\n consolidatedContent: string;\n observationType: string;\n tags?: string[];\n}\n\nexport interface ConsolidateResult {\n wisdom_id: string;\n wisdom_path: string;\n sources_archived: number;\n}\n\nexport interface ConsolidateDeps {\n vaultDir: string;\n index: MycoIndex;\n vectorIndex: VectorIndex | null;\n embeddingProvider: EmbeddingProvider | null;\n}\n\n/**\n * Create a consolidated wisdom note from a list of source spore IDs, then\n * mark each source spore as superseded.\n *\n * - The wisdom note is written via VaultWriter.writeSpore() with 'wisdom' and\n * 'consolidated' tags appended.\n * - `consolidated_from` is added to the wisdom note's frontmatter.\n * - Each source spore is updated atomically via `supersedeSpore` (frontmatter\n * update + notice append + re-index + vector deletion).\n * - The wisdom note is indexed and, if deps are available, embedded with\n * importance 'high' (fire-and-forget).\n *\n * Source spores missing from the index are silently skipped.\n */\nexport async function consolidateSpores(\n input: ConsolidateInput,\n deps: ConsolidateDeps,\n): Promise<ConsolidateResult> {\n const { vaultDir, index, vectorIndex, embeddingProvider } = deps;\n const writer = new VaultWriter(vaultDir);\n\n const wisdomId = `${input.observationType}-wisdom-${randomBytes(4).toString('hex')}`;\n\n // Build content with a ## Sources section containing Obsidian wikilinks\n const sourceLinks = input.sourceSporeIds.map((id) => `- [[${id}]]`).join('\\n');\n const sourcesSection = input.sourceSporeIds.length > 0\n ? `\\n\\n## Sources\\n\\nConsolidated from:\\n${sourceLinks}`\n : '';\n const fullContent = `${input.consolidatedContent}${sourcesSection}`;\n\n // Write the wisdom spore note\n const wisdomPath = writer.writeSpore({\n id: wisdomId,\n observation_type: input.observationType,\n tags: [...(input.tags ?? []), 'wisdom', 'consolidated'],\n content: fullContent,\n });\n\n // Add consolidated_from to the wisdom note's frontmatter\n writer.updateNoteFrontmatter(wisdomPath, {\n consolidated_from: input.sourceSporeIds,\n }, true);\n\n // Supersede each source spore (atomic frontmatter update + notice + re-index + vector delete)\n const sourceNotes = index.queryByIds(input.sourceSporeIds);\n const sourceNoteMap = new Map(sourceNotes.map((n) => [n.id, n]));\n let sourcesArchived = 0;\n for (const sourceId of input.sourceSporeIds) {\n const note = sourceNoteMap.get(sourceId);\n if (!note) continue;\n\n const superseded = supersedeSpore(sourceId, wisdomId, note.path, {\n index,\n vectorIndex,\n vaultDir,\n });\n\n if (superseded) sourcesArchived++;\n }\n\n // Index the new wisdom note\n indexNote(index, vaultDir, wisdomPath);\n\n // Embed the wisdom note (fire-and-forget — embedding failure is non-fatal)\n if (vectorIndex && embeddingProvider) {\n generateEmbedding(embeddingProvider, fullContent.slice(0, EMBEDDING_INPUT_LIMIT))\n .then((emb) =>\n vectorIndex.upsert(wisdomId, emb.embedding, {\n type: 'spore',\n observation_type: input.observationType,\n importance: 'high',\n }),\n )\n .catch(() => { /* embedding failure is non-fatal */ });\n }\n\n return {\n wisdom_id: wisdomId,\n wisdom_path: wisdomPath,\n sources_archived: sourcesArchived,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/mcp/tools/context.ts","../src/vault/consolidation.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { stripFrontmatter } from '../../vault/frontmatter.js';\nimport { DIGEST_TIERS } from '../../constants.js';\n\n/** Default tier when none is requested. */\nconst DEFAULT_CONTEXT_TIER = 3000;\n\ninterface ContextInput {\n tier?: number;\n}\n\nexport interface ContextResult {\n content: string;\n tier: number;\n fallback: boolean;\n generated?: string;\n}\n\n/**\n * Try to read a digest extract file. Returns null if the file doesn't exist.\n * Strips YAML frontmatter and extracts the generated timestamp.\n */\nfunction tryReadExtract(filePath: string, tier: number, fallback: boolean): ContextResult | null {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n\n const { body, frontmatter } = stripFrontmatter(raw);\n const generated = frontmatter.generated as string | undefined;\n\n return {\n content: body,\n tier,\n fallback,\n generated,\n };\n}\n\nexport function handleMycoContext(vaultDir: string, input: ContextInput): ContextResult {\n const requestedTier = input.tier ?? DEFAULT_CONTEXT_TIER;\n const digestDir = path.join(vaultDir, 'digest');\n\n // Try exact tier first\n const exact = tryReadExtract(path.join(digestDir, `extract-${requestedTier}.md`), requestedTier, false);\n if (exact) return exact;\n\n // Fall back to nearest available tier\n const candidates = [...DIGEST_TIERS]\n .sort((a, b) => Math.abs(a - requestedTier) - Math.abs(b - requestedTier));\n\n for (const tier of candidates) {\n const result = tryReadExtract(path.join(digestDir, `extract-${tier}.md`), tier, true);\n if (result) return result;\n }\n\n return {\n content: 'Digest context is not yet available. The first digest cycle has not completed.',\n tier: requestedTier,\n fallback: false,\n };\n}\n","/**\n * Shared consolidation core — creates a wisdom note from a set of source spores\n * and marks each source as superseded.\n *\n * Used by both the MCP tool (`myco_consolidate`) and the daemon's automatic\n * consolidation pass so the logic lives in exactly one place.\n */\n\nimport { VaultWriter } from './writer.js';\nimport { supersedeSpore } from './curation.js';\nimport { indexNote } from '../index/rebuild.js';\nimport type { MycoIndex } from '../index/sqlite.js';\nimport type { VectorIndex } from '../index/vectors.js';\nimport type { EmbeddingProvider } from '../intelligence/llm.js';\nimport { generateEmbedding } from '../intelligence/embeddings.js';\nimport { EMBEDDING_INPUT_LIMIT } from '../constants.js';\nimport { randomBytes } from 'node:crypto';\n\nexport interface ConsolidateInput {\n sourceSporeIds: string[];\n consolidatedContent: string;\n observationType: string;\n tags?: string[];\n}\n\nexport interface ConsolidateResult {\n wisdom_id: string;\n wisdom_path: string;\n sources_archived: number;\n}\n\nexport interface ConsolidateDeps {\n vaultDir: string;\n index: MycoIndex;\n vectorIndex: VectorIndex | null;\n embeddingProvider: EmbeddingProvider | null;\n}\n\n/**\n * Create a consolidated wisdom note from a list of source spore IDs, then\n * mark each source spore as superseded.\n *\n * - The wisdom note is written via VaultWriter.writeSpore() with 'wisdom' and\n * 'consolidated' tags appended.\n * - `consolidated_from` is added to the wisdom note's frontmatter.\n * - Each source spore is updated atomically via `supersedeSpore` (frontmatter\n * update + notice append + re-index + vector deletion).\n * - The wisdom note is indexed and, if deps are available, embedded with\n * importance 'high' (fire-and-forget).\n *\n * Source spores missing from the index are silently skipped.\n */\nexport async function consolidateSpores(\n input: ConsolidateInput,\n deps: ConsolidateDeps,\n): Promise<ConsolidateResult> {\n const { vaultDir, index, vectorIndex, embeddingProvider } = deps;\n const writer = new VaultWriter(vaultDir);\n\n const wisdomId = `${input.observationType}-wisdom-${randomBytes(4).toString('hex')}`;\n\n // Build content with a ## Sources section containing Obsidian wikilinks\n const sourceLinks = input.sourceSporeIds.map((id) => `- [[${id}]]`).join('\\n');\n const sourcesSection = input.sourceSporeIds.length > 0\n ? `\\n\\n## Sources\\n\\nConsolidated from:\\n${sourceLinks}`\n : '';\n const fullContent = `${input.consolidatedContent}${sourcesSection}`;\n\n // Write the wisdom spore note\n const wisdomPath = writer.writeSpore({\n id: wisdomId,\n observation_type: input.observationType,\n tags: [...(input.tags ?? []), 'wisdom', 'consolidated'],\n content: fullContent,\n });\n\n // Add consolidated_from to the wisdom note's frontmatter\n writer.updateNoteFrontmatter(wisdomPath, {\n consolidated_from: input.sourceSporeIds,\n }, true);\n\n // Supersede each source spore (atomic frontmatter update + notice + re-index + vector delete)\n const sourceNotes = index.queryByIds(input.sourceSporeIds);\n const sourceNoteMap = new Map(sourceNotes.map((n) => [n.id, n]));\n let sourcesArchived = 0;\n for (const sourceId of input.sourceSporeIds) {\n const note = sourceNoteMap.get(sourceId);\n if (!note) continue;\n\n const superseded = supersedeSpore(sourceId, wisdomId, note.path, {\n index,\n vectorIndex,\n vaultDir,\n });\n\n if (superseded) sourcesArchived++;\n }\n\n // Index the new wisdom note\n indexNote(index, vaultDir, wisdomPath);\n\n // Embed the wisdom note (fire-and-forget — embedding failure is non-fatal)\n if (vectorIndex && embeddingProvider) {\n generateEmbedding(embeddingProvider, fullContent.slice(0, EMBEDDING_INPUT_LIMIT))\n .then((emb) =>\n vectorIndex.upsert(wisdomId, emb.embedding, {\n type: 'spore',\n observation_type: input.observationType,\n importance: 'high',\n }),\n )\n .catch(() => { /* embedding failure is non-fatal */ });\n }\n\n return {\n wisdom_id: wisdomId,\n wisdom_path: wisdomPath,\n sources_archived: sourcesArchived,\n };\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  DAEMON_CLIENT_TIMEOUT_MS,
4
4
  EMBEDDING_REQUEST_TIMEOUT_MS,
5
5
  LLM_REQUEST_TIMEOUT_MS
6
- } from "./chunk-6BSDCZ5Q.js";
6
+ } from "./chunk-WBLTISAK.js";
7
7
 
8
8
  // src/intelligence/ollama.ts
9
9
  var ENDPOINT_GENERATE = "/api/generate";
@@ -32,7 +32,7 @@ var OllamaBackend = class _OllamaBackend {
32
32
  const body = {
33
33
  model: this.model,
34
34
  prompt,
35
- stream: false,
35
+ stream: true,
36
36
  options
37
37
  };
38
38
  if (opts?.systemPrompt) {
@@ -54,8 +54,40 @@ var OllamaBackend = class _OllamaBackend {
54
54
  const errorBody = await response.text().catch(() => "");
55
55
  throw new Error(`Ollama summarize failed: ${response.status} ${errorBody.slice(0, 500)}`);
56
56
  }
57
- const data = await response.json();
58
- return { text: data.response, model: data.model };
57
+ return this.readStream(response);
58
+ }
59
+ /** Read an Ollama streaming response (newline-delimited JSON) and accumulate the result. */
60
+ async readStream(response) {
61
+ const reader = response.body.getReader();
62
+ const decoder = new TextDecoder();
63
+ let text = "";
64
+ let model = this.model;
65
+ let buffer = "";
66
+ try {
67
+ for (; ; ) {
68
+ const { done, value } = await reader.read();
69
+ if (done) break;
70
+ buffer += decoder.decode(value, { stream: true });
71
+ const lines = buffer.split("\n");
72
+ buffer = lines.pop() ?? "";
73
+ for (const line of lines) {
74
+ if (!line.trim()) continue;
75
+ const chunk = JSON.parse(line);
76
+ if (chunk.error) throw new Error(`Ollama stream error: ${chunk.error}`);
77
+ text += chunk.response ?? "";
78
+ if (chunk.model) model = chunk.model;
79
+ }
80
+ }
81
+ if (buffer.trim()) {
82
+ const chunk = JSON.parse(buffer);
83
+ if (chunk.error) throw new Error(`Ollama stream error: ${chunk.error}`);
84
+ text += chunk.response ?? "";
85
+ if (chunk.model) model = chunk.model;
86
+ }
87
+ } finally {
88
+ reader.releaseLock();
89
+ }
90
+ return { text, model };
59
91
  }
60
92
  async embed(text) {
61
93
  const response = await fetch(`${this.baseUrl}${ENDPOINT_EMBED}`, {
@@ -273,4 +305,4 @@ export {
273
305
  OllamaBackend,
274
306
  LmStudioBackend
275
307
  };
276
- //# sourceMappingURL=chunk-QGJ2ZIUZ.js.map
308
+ //# sourceMappingURL=chunk-MWW62YZP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/intelligence/ollama.ts","../src/intelligence/lm-studio.ts"],"sourcesContent":["import type { LlmProvider, EmbeddingProvider, LlmResponse, EmbeddingResponse, LlmRequestOptions } from './llm.js';\nimport { 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":[]}