@goondocks/myco 0.2.6 → 0.2.8

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 (290) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CONTRIBUTING.md +1 -1
  3. package/commands/init.md +10 -26
  4. package/dist/chunk-4JML636J.js +52 -0
  5. package/dist/chunk-4JML636J.js.map +1 -0
  6. package/dist/chunk-AOMX45LH.js +8974 -0
  7. package/dist/chunk-AOMX45LH.js.map +1 -0
  8. package/dist/chunk-I7PMGO6S.js +58 -0
  9. package/dist/chunk-I7PMGO6S.js.map +1 -0
  10. package/dist/chunk-N33KUCFP.js +33 -0
  11. package/dist/chunk-N33KUCFP.js.map +1 -0
  12. package/dist/chunk-NYNEJ5QY.js +71 -0
  13. package/dist/chunk-NYNEJ5QY.js.map +1 -0
  14. package/dist/chunk-PA3VMINE.js +111 -0
  15. package/dist/chunk-PA3VMINE.js.map +1 -0
  16. package/dist/chunk-PZUWP5VK.js +44 -0
  17. package/dist/chunk-PZUWP5VK.js.map +1 -0
  18. package/dist/chunk-SVUINMDD.js +104 -0
  19. package/dist/chunk-SVUINMDD.js.map +1 -0
  20. package/dist/chunk-TH6GIBXG.js +91 -0
  21. package/dist/chunk-TH6GIBXG.js.map +1 -0
  22. package/dist/chunk-TWDS6MSU.js +354 -0
  23. package/dist/chunk-TWDS6MSU.js.map +1 -0
  24. package/dist/chunk-UIIZRTJU.js +21172 -0
  25. package/dist/chunk-UIIZRTJU.js.map +1 -0
  26. package/dist/chunk-YMYJ7FNH.js +19 -0
  27. package/dist/chunk-YMYJ7FNH.js.map +1 -0
  28. package/dist/chunk-ZJQ5G637.js +21 -0
  29. package/dist/chunk-ZJQ5G637.js.map +1 -0
  30. package/dist/chunk-ZTZVX5E6.js +421 -0
  31. package/dist/chunk-ZTZVX5E6.js.map +1 -0
  32. package/dist/cli-K5FSKLQC.js +625 -0
  33. package/dist/cli-K5FSKLQC.js.map +1 -0
  34. package/dist/client-4JMOYNKK.js +11 -0
  35. package/dist/client-4JMOYNKK.js.map +1 -0
  36. package/dist/main-5W4ADOBG.js +3224 -0
  37. package/dist/main-5W4ADOBG.js.map +1 -0
  38. package/dist/server-PIEPVUUH.js +14725 -0
  39. package/dist/server-PIEPVUUH.js.map +1 -0
  40. package/dist/session-start-2NNQHT5S.js +189 -0
  41. package/dist/session-start-2NNQHT5S.js.map +1 -0
  42. package/dist/src/cli.js +9 -582
  43. package/dist/src/cli.js.map +1 -1
  44. package/dist/src/daemon/main.js +9 -737
  45. package/dist/src/daemon/main.js.map +1 -1
  46. package/dist/src/hooks/post-tool-use.js +47 -35
  47. package/dist/src/hooks/post-tool-use.js.map +1 -1
  48. package/dist/src/hooks/session-end.js +29 -18
  49. package/dist/src/hooks/session-end.js.map +1 -1
  50. package/dist/src/hooks/session-start.js +9 -48
  51. package/dist/src/hooks/session-start.js.map +1 -1
  52. package/dist/src/hooks/stop.js +39 -30
  53. package/dist/src/hooks/stop.js.map +1 -1
  54. package/dist/src/hooks/user-prompt-submit.js +48 -40
  55. package/dist/src/hooks/user-prompt-submit.js.map +1 -1
  56. package/dist/src/mcp/server.js +9 -304
  57. package/dist/src/mcp/server.js.map +1 -1
  58. package/package.json +3 -2
  59. package/dist/src/agents/adapter.d.ts +0 -76
  60. package/dist/src/agents/adapter.d.ts.map +0 -1
  61. package/dist/src/agents/adapter.js +0 -124
  62. package/dist/src/agents/adapter.js.map +0 -1
  63. package/dist/src/agents/claude-code.d.ts +0 -3
  64. package/dist/src/agents/claude-code.d.ts.map +0 -1
  65. package/dist/src/agents/claude-code.js +0 -22
  66. package/dist/src/agents/claude-code.js.map +0 -1
  67. package/dist/src/agents/cursor.d.ts +0 -3
  68. package/dist/src/agents/cursor.d.ts.map +0 -1
  69. package/dist/src/agents/cursor.js +0 -154
  70. package/dist/src/agents/cursor.js.map +0 -1
  71. package/dist/src/agents/index.d.ts +0 -6
  72. package/dist/src/agents/index.d.ts.map +0 -1
  73. package/dist/src/agents/index.js +0 -5
  74. package/dist/src/agents/index.js.map +0 -1
  75. package/dist/src/agents/registry.d.ts +0 -34
  76. package/dist/src/agents/registry.d.ts.map +0 -1
  77. package/dist/src/agents/registry.js +0 -95
  78. package/dist/src/agents/registry.js.map +0 -1
  79. package/dist/src/artifacts/candidates.d.ts +0 -20
  80. package/dist/src/artifacts/candidates.d.ts.map +0 -1
  81. package/dist/src/artifacts/candidates.js +0 -84
  82. package/dist/src/artifacts/candidates.js.map +0 -1
  83. package/dist/src/artifacts/slugify.d.ts +0 -2
  84. package/dist/src/artifacts/slugify.d.ts.map +0 -1
  85. package/dist/src/artifacts/slugify.js +0 -22
  86. package/dist/src/artifacts/slugify.js.map +0 -1
  87. package/dist/src/capture/buffer.d.ts +0 -20
  88. package/dist/src/capture/buffer.d.ts.map +0 -1
  89. package/dist/src/capture/buffer.js +0 -55
  90. package/dist/src/capture/buffer.js.map +0 -1
  91. package/dist/src/capture/transcript-miner.d.ts +0 -31
  92. package/dist/src/capture/transcript-miner.d.ts.map +0 -1
  93. package/dist/src/capture/transcript-miner.js +0 -61
  94. package/dist/src/capture/transcript-miner.js.map +0 -1
  95. package/dist/src/cli.d.ts +0 -3
  96. package/dist/src/cli.d.ts.map +0 -1
  97. package/dist/src/config/loader.d.ts +0 -4
  98. package/dist/src/config/loader.d.ts.map +0 -1
  99. package/dist/src/config/loader.js +0 -32
  100. package/dist/src/config/loader.js.map +0 -1
  101. package/dist/src/config/schema.d.ts +0 -83
  102. package/dist/src/config/schema.d.ts.map +0 -1
  103. package/dist/src/config/schema.js +0 -55
  104. package/dist/src/config/schema.js.map +0 -1
  105. package/dist/src/constants.d.ts +0 -73
  106. package/dist/src/constants.d.ts.map +0 -1
  107. package/dist/src/constants.js +0 -86
  108. package/dist/src/constants.js.map +0 -1
  109. package/dist/src/context/injector.d.ts +0 -18
  110. package/dist/src/context/injector.d.ts.map +0 -1
  111. package/dist/src/context/injector.js +0 -71
  112. package/dist/src/context/injector.js.map +0 -1
  113. package/dist/src/context/relevance.d.ts +0 -13
  114. package/dist/src/context/relevance.d.ts.map +0 -1
  115. package/dist/src/context/relevance.js +0 -44
  116. package/dist/src/context/relevance.js.map +0 -1
  117. package/dist/src/daemon/batch.d.ts +0 -22
  118. package/dist/src/daemon/batch.d.ts.map +0 -1
  119. package/dist/src/daemon/batch.js +0 -38
  120. package/dist/src/daemon/batch.js.map +0 -1
  121. package/dist/src/daemon/lifecycle.d.ts +0 -27
  122. package/dist/src/daemon/lifecycle.d.ts.map +0 -1
  123. package/dist/src/daemon/lifecycle.js +0 -50
  124. package/dist/src/daemon/lifecycle.js.map +0 -1
  125. package/dist/src/daemon/lineage.d.ts +0 -42
  126. package/dist/src/daemon/lineage.d.ts.map +0 -1
  127. package/dist/src/daemon/lineage.js +0 -116
  128. package/dist/src/daemon/lineage.js.map +0 -1
  129. package/dist/src/daemon/logger.d.ts +0 -33
  130. package/dist/src/daemon/logger.d.ts.map +0 -1
  131. package/dist/src/daemon/logger.js +0 -88
  132. package/dist/src/daemon/logger.js.map +0 -1
  133. package/dist/src/daemon/main.d.ts +0 -2
  134. package/dist/src/daemon/main.d.ts.map +0 -1
  135. package/dist/src/daemon/processor.d.ts +0 -44
  136. package/dist/src/daemon/processor.d.ts.map +0 -1
  137. package/dist/src/daemon/processor.js +0 -142
  138. package/dist/src/daemon/processor.js.map +0 -1
  139. package/dist/src/daemon/server.d.ts +0 -24
  140. package/dist/src/daemon/server.d.ts.map +0 -1
  141. package/dist/src/daemon/server.js +0 -117
  142. package/dist/src/daemon/server.js.map +0 -1
  143. package/dist/src/daemon/watcher.d.ts +0 -29
  144. package/dist/src/daemon/watcher.d.ts.map +0 -1
  145. package/dist/src/daemon/watcher.js +0 -67
  146. package/dist/src/daemon/watcher.js.map +0 -1
  147. package/dist/src/hooks/client.d.ts +0 -20
  148. package/dist/src/hooks/client.d.ts.map +0 -1
  149. package/dist/src/hooks/client.js +0 -111
  150. package/dist/src/hooks/client.js.map +0 -1
  151. package/dist/src/hooks/post-tool-use.d.ts +0 -2
  152. package/dist/src/hooks/post-tool-use.d.ts.map +0 -1
  153. package/dist/src/hooks/read-stdin.d.ts +0 -2
  154. package/dist/src/hooks/read-stdin.d.ts.map +0 -1
  155. package/dist/src/hooks/read-stdin.js +0 -10
  156. package/dist/src/hooks/read-stdin.js.map +0 -1
  157. package/dist/src/hooks/session-end.d.ts +0 -2
  158. package/dist/src/hooks/session-end.d.ts.map +0 -1
  159. package/dist/src/hooks/session-start.d.ts +0 -2
  160. package/dist/src/hooks/session-start.d.ts.map +0 -1
  161. package/dist/src/hooks/stop.d.ts +0 -2
  162. package/dist/src/hooks/stop.d.ts.map +0 -1
  163. package/dist/src/hooks/user-prompt-submit.d.ts +0 -2
  164. package/dist/src/hooks/user-prompt-submit.d.ts.map +0 -1
  165. package/dist/src/index/fts.d.ts +0 -16
  166. package/dist/src/index/fts.d.ts.map +0 -1
  167. package/dist/src/index/fts.js +0 -53
  168. package/dist/src/index/fts.js.map +0 -1
  169. package/dist/src/index/rebuild.d.ts +0 -4
  170. package/dist/src/index/rebuild.d.ts.map +0 -1
  171. package/dist/src/index/rebuild.js +0 -40
  172. package/dist/src/index/rebuild.js.map +0 -1
  173. package/dist/src/index/sqlite.d.ts +0 -33
  174. package/dist/src/index/sqlite.d.ts.map +0 -1
  175. package/dist/src/index/sqlite.js +0 -99
  176. package/dist/src/index/sqlite.js.map +0 -1
  177. package/dist/src/index/vectors.d.ts +0 -24
  178. package/dist/src/index/vectors.d.ts.map +0 -1
  179. package/dist/src/index/vectors.js +0 -97
  180. package/dist/src/index/vectors.js.map +0 -1
  181. package/dist/src/intelligence/anthropic.d.ts +0 -17
  182. package/dist/src/intelligence/anthropic.d.ts.map +0 -1
  183. package/dist/src/intelligence/anthropic.js +0 -36
  184. package/dist/src/intelligence/anthropic.js.map +0 -1
  185. package/dist/src/intelligence/embeddings.d.ts +0 -3
  186. package/dist/src/intelligence/embeddings.d.ts.map +0 -1
  187. package/dist/src/intelligence/embeddings.js +0 -15
  188. package/dist/src/intelligence/embeddings.js.map +0 -1
  189. package/dist/src/intelligence/llm.d.ts +0 -33
  190. package/dist/src/intelligence/llm.d.ts.map +0 -1
  191. package/dist/src/intelligence/llm.js +0 -26
  192. package/dist/src/intelligence/llm.js.map +0 -1
  193. package/dist/src/intelligence/lm-studio.d.ts +0 -20
  194. package/dist/src/intelligence/lm-studio.d.ts.map +0 -1
  195. package/dist/src/intelligence/lm-studio.js +0 -59
  196. package/dist/src/intelligence/lm-studio.js.map +0 -1
  197. package/dist/src/intelligence/ollama.d.ts +0 -22
  198. package/dist/src/intelligence/ollama.d.ts.map +0 -1
  199. package/dist/src/intelligence/ollama.js +0 -64
  200. package/dist/src/intelligence/ollama.js.map +0 -1
  201. package/dist/src/intelligence/response.d.ts +0 -29
  202. package/dist/src/intelligence/response.d.ts.map +0 -1
  203. package/dist/src/intelligence/response.js +0 -71
  204. package/dist/src/intelligence/response.js.map +0 -1
  205. package/dist/src/logs/format.d.ts +0 -6
  206. package/dist/src/logs/format.d.ts.map +0 -1
  207. package/dist/src/logs/format.js +0 -46
  208. package/dist/src/logs/format.js.map +0 -1
  209. package/dist/src/logs/reader.d.ts +0 -28
  210. package/dist/src/logs/reader.d.ts.map +0 -1
  211. package/dist/src/logs/reader.js +0 -106
  212. package/dist/src/logs/reader.js.map +0 -1
  213. package/dist/src/mcp/server.d.ts +0 -16
  214. package/dist/src/mcp/server.d.ts.map +0 -1
  215. package/dist/src/mcp/tools/consolidate.d.ts +0 -15
  216. package/dist/src/mcp/tools/consolidate.d.ts.map +0 -1
  217. package/dist/src/mcp/tools/consolidate.js +0 -49
  218. package/dist/src/mcp/tools/consolidate.js.map +0 -1
  219. package/dist/src/mcp/tools/graph.d.ts +0 -30
  220. package/dist/src/mcp/tools/graph.d.ts.map +0 -1
  221. package/dist/src/mcp/tools/graph.js +0 -106
  222. package/dist/src/mcp/tools/graph.js.map +0 -1
  223. package/dist/src/mcp/tools/logs.d.ts +0 -3
  224. package/dist/src/mcp/tools/logs.d.ts.map +0 -1
  225. package/dist/src/mcp/tools/logs.js +0 -7
  226. package/dist/src/mcp/tools/logs.js.map +0 -1
  227. package/dist/src/mcp/tools/plans.d.ts +0 -23
  228. package/dist/src/mcp/tools/plans.d.ts.map +0 -1
  229. package/dist/src/mcp/tools/plans.js +0 -63
  230. package/dist/src/mcp/tools/plans.js.map +0 -1
  231. package/dist/src/mcp/tools/recall.d.ts +0 -30
  232. package/dist/src/mcp/tools/recall.d.ts.map +0 -1
  233. package/dist/src/mcp/tools/recall.js +0 -34
  234. package/dist/src/mcp/tools/recall.js.map +0 -1
  235. package/dist/src/mcp/tools/remember.d.ts +0 -15
  236. package/dist/src/mcp/tools/remember.d.ts.map +0 -1
  237. package/dist/src/mcp/tools/remember.js +0 -18
  238. package/dist/src/mcp/tools/remember.js.map +0 -1
  239. package/dist/src/mcp/tools/search.d.ts +0 -19
  240. package/dist/src/mcp/tools/search.d.ts.map +0 -1
  241. package/dist/src/mcp/tools/search.js +0 -59
  242. package/dist/src/mcp/tools/search.js.map +0 -1
  243. package/dist/src/mcp/tools/sessions.d.ts +0 -21
  244. package/dist/src/mcp/tools/sessions.d.ts.map +0 -1
  245. package/dist/src/mcp/tools/sessions.js +0 -36
  246. package/dist/src/mcp/tools/sessions.js.map +0 -1
  247. package/dist/src/mcp/tools/supersede.d.ts +0 -14
  248. package/dist/src/mcp/tools/supersede.d.ts.map +0 -1
  249. package/dist/src/mcp/tools/supersede.js +0 -30
  250. package/dist/src/mcp/tools/supersede.js.map +0 -1
  251. package/dist/src/mcp/tools/team.d.ts +0 -16
  252. package/dist/src/mcp/tools/team.d.ts.map +0 -1
  253. package/dist/src/mcp/tools/team.js +0 -32
  254. package/dist/src/mcp/tools/team.js.map +0 -1
  255. package/dist/src/obsidian/formatter.d.ts +0 -80
  256. package/dist/src/obsidian/formatter.d.ts.map +0 -1
  257. package/dist/src/obsidian/formatter.js +0 -227
  258. package/dist/src/obsidian/formatter.js.map +0 -1
  259. package/dist/src/prompts/index.d.ts +0 -13
  260. package/dist/src/prompts/index.d.ts.map +0 -1
  261. package/dist/src/prompts/index.js +0 -75
  262. package/dist/src/prompts/index.js.map +0 -1
  263. package/dist/src/vault/frontmatter.d.ts +0 -6
  264. package/dist/src/vault/frontmatter.d.ts.map +0 -1
  265. package/dist/src/vault/frontmatter.js +0 -10
  266. package/dist/src/vault/frontmatter.js.map +0 -1
  267. package/dist/src/vault/observations.d.ts +0 -10
  268. package/dist/src/vault/observations.d.ts.map +0 -1
  269. package/dist/src/vault/observations.js +0 -33
  270. package/dist/src/vault/observations.js.map +0 -1
  271. package/dist/src/vault/reader.d.ts +0 -10
  272. package/dist/src/vault/reader.d.ts.map +0 -1
  273. package/dist/src/vault/reader.js +0 -48
  274. package/dist/src/vault/reader.js.map +0 -1
  275. package/dist/src/vault/resolve.d.ts +0 -18
  276. package/dist/src/vault/resolve.d.ts.map +0 -1
  277. package/dist/src/vault/resolve.js +0 -51
  278. package/dist/src/vault/resolve.js.map +0 -1
  279. package/dist/src/vault/session-id.d.ts +0 -16
  280. package/dist/src/vault/session-id.d.ts.map +0 -1
  281. package/dist/src/vault/session-id.js +0 -29
  282. package/dist/src/vault/session-id.js.map +0 -1
  283. package/dist/src/vault/types.d.ts +0 -88
  284. package/dist/src/vault/types.d.ts.map +0 -1
  285. package/dist/src/vault/types.js +0 -94
  286. package/dist/src/vault/types.js.map +0 -1
  287. package/dist/src/vault/writer.d.ts +0 -66
  288. package/dist/src/vault/writer.d.ts.map +0 -1
  289. package/dist/src/vault/writer.js +0 -217
  290. package/dist/src/vault/writer.js.map +0 -1
@@ -1,738 +1,10 @@
1
- import { DaemonServer } from './server.js';
2
- import { SessionRegistry } from './lifecycle.js';
3
- import { DaemonLogger } from './logger.js';
4
- import { loadConfig } from '../config/loader.js';
5
- import { BatchManager } from './batch.js';
6
- import { BufferProcessor } from './processor.js';
7
- import { VaultWriter } from '../vault/writer.js';
8
- import { MycoIndex } from '../index/sqlite.js';
9
- import { indexNote, rebuildIndex } from '../index/rebuild.js';
10
- import { initFts } from '../index/fts.js';
11
- import { createLlmProvider, createEmbeddingProvider } from '../intelligence/llm.js';
12
- import { VectorIndex } from '../index/vectors.js';
13
- import { generateEmbedding } from '../intelligence/embeddings.js';
14
- import { LineageGraph, LINEAGE_SIMILARITY_THRESHOLD, LINEAGE_SIMILARITY_HIGH_CONFIDENCE, LINEAGE_SIMILARITY_CANDIDATES, LINEAGE_SIMILARITY_MAX_TOKENS } from './lineage.js';
15
- import { PlanWatcher } from './watcher.js';
16
- import { buildSimilarityPrompt } from '../prompts/index.js';
17
- import { extractNumber } from '../intelligence/response.js';
18
- import { EMBEDDING_INPUT_LIMIT, CONTENT_SNIPPET_CHARS, STALE_BUFFER_MAX_AGE_MS, LINEAGE_RECENT_SESSIONS_LIMIT, RELATED_MEMORIES_LIMIT, CANDIDATE_CONTENT_PREVIEW, SESSION_CONTEXT_MAX_PLANS, PROMPT_CONTEXT_MAX_MEMORIES, PROMPT_CONTEXT_MIN_SIMILARITY, PROMPT_CONTEXT_MIN_LENGTH, CONTEXT_SESSION_PREVIEW_CHARS } from '../constants.js';
19
- import { TranscriptMiner, extractTurnsFromBuffer } from '../capture/transcript-miner.js';
20
- import { createPerProjectAdapter, extensionForMimeType } from '../agents/adapter.js';
21
- import { claudeCodeAdapter } from '../agents/claude-code.js';
22
- import { EventBuffer } from '../capture/buffer.js';
23
- import { formatSessionBody } from '../obsidian/formatter.js';
24
- import { writeObservationNotes } from '../vault/observations.js';
25
- import { collectArtifactCandidates } from '../artifacts/candidates.js';
26
- import { slugifyPath } from '../artifacts/slugify.js';
27
- import { sessionNoteId, bareSessionId, sessionWikilink, sessionRelativePath } from '../vault/session-id.js';
28
- import { z } from 'zod';
29
- import YAML from 'yaml';
30
- import fs from 'node:fs';
31
- import path from 'node:path';
32
- import { fileURLToPath } from 'node:url';
33
- function indexAndEmbed(relativePath, noteId, embeddingText, metadata, deps) {
34
- indexNote(deps.index, deps.vaultDir, relativePath);
35
- if (deps.vectorIndex && embeddingText) {
36
- generateEmbedding(deps.embeddingProvider, embeddingText.slice(0, EMBEDDING_INPUT_LIMIT))
37
- .then((emb) => deps.vectorIndex.upsert(noteId, emb.embedding, metadata))
38
- .catch((err) => deps.logger.debug('embeddings', 'Embedding failed', { id: noteId, error: err.message }));
39
- }
40
- }
41
- function writeObservations(observations, sessionId, deps) {
42
- const written = writeObservationNotes(observations, sessionId, deps.vault, deps.index, deps.vaultDir);
43
- for (const note of written) {
44
- indexAndEmbed(note.path, note.id, `${note.observation.title}\n${note.observation.content}`, { type: 'memory', importance: 'high', session_id: sessionId }, deps);
45
- deps.logger.info('processor', 'Observation written', { type: note.observation.type, title: note.observation.title, session_id: sessionId });
46
- }
47
- }
48
- async function captureArtifacts(candidates, classified, sessionId, deps, lineage) {
49
- const candidateMap = new Map(candidates.map((c) => [c.path, c]));
50
- for (const artifact of classified) {
51
- const candidate = candidateMap.get(artifact.source_path);
52
- if (!candidate)
53
- continue;
54
- const artifactId = slugifyPath(artifact.source_path);
55
- const artifactPath = deps.vault.writeArtifact({
56
- id: artifactId,
57
- artifact_type: artifact.artifact_type,
58
- source_path: artifact.source_path,
59
- title: artifact.title,
60
- session: sessionId,
61
- tags: artifact.tags,
62
- content: candidate.content,
63
- });
64
- indexAndEmbed(artifactPath, artifactId, `${artifact.title}\n${candidate.content}`, { type: 'artifact', artifact_type: artifact.artifact_type, session_id: sessionId }, deps);
65
- deps.logger.info('processor', 'Artifact captured', {
66
- id: artifactId,
67
- type: artifact.artifact_type,
68
- source: artifact.source_path,
69
- });
70
- // Register artifact-session association for lineage detection.
71
- // Future sessions referencing this artifact ID link as children.
72
- lineage?.registerArtifactForSession(sessionId, artifactId);
73
- }
74
- }
75
- export function migrateMemoryFiles(vaultDir) {
76
- const memoriesDir = path.join(vaultDir, 'memories');
77
- if (!fs.existsSync(memoriesDir))
78
- return 0;
79
- let moved = 0;
80
- const entries = fs.readdirSync(memoriesDir);
81
- for (const entry of entries) {
82
- const fullPath = path.join(memoriesDir, entry);
83
- if (!entry.endsWith('.md'))
84
- continue;
85
- if (fs.statSync(fullPath).isDirectory())
86
- continue;
87
- try {
88
- const content = fs.readFileSync(fullPath, 'utf-8');
89
- const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
90
- if (!fmMatch)
91
- continue;
92
- const parsed = YAML.parse(fmMatch[1]);
93
- const obsType = parsed.observation_type;
94
- if (!obsType)
95
- continue;
96
- const normalizedType = obsType.replace(/_/g, '-');
97
- const targetDir = path.join(memoriesDir, normalizedType);
98
- fs.mkdirSync(targetDir, { recursive: true });
99
- const targetPath = path.join(targetDir, entry);
100
- fs.renameSync(fullPath, targetPath);
101
- // Touch the file so Obsidian detects the move and re-indexes backlinks
102
- const now = new Date();
103
- fs.utimesSync(targetPath, now, now);
104
- moved++;
105
- }
106
- catch {
107
- // Skip files that can't be read or parsed
108
- }
109
- }
110
- return moved;
111
- }
112
- async function main() {
113
- const vaultArg = process.argv.find((_, i) => process.argv[i - 1] === '--vault');
114
- if (!vaultArg) {
115
- process.stderr.write('Usage: mycod --vault <path>\n');
116
- process.exit(1);
117
- }
118
- const vaultDir = path.resolve(vaultArg);
119
- const config = loadConfig(vaultDir);
120
- const logger = new DaemonLogger(path.join(vaultDir, 'logs'), {
121
- level: config.daemon.log_level,
122
- maxSize: config.daemon.max_log_size,
123
- });
124
- const server = new DaemonServer({ vaultDir, logger });
125
- const registry = new SessionRegistry({
126
- gracePeriod: config.daemon.grace_period,
127
- onEmpty: async () => {
128
- logger.info('daemon', 'Grace period expired, shutting down');
129
- planWatcher.stopFileWatcher();
130
- await server.stop();
131
- vectorIndex?.close();
132
- index.close();
133
- logger.close();
134
- process.exit(0);
135
- },
136
- });
137
- // Batch processing setup
138
- const llmProvider = createLlmProvider(config.intelligence.llm);
139
- const embeddingProvider = createEmbeddingProvider(config.intelligence.embedding);
140
- let vectorIndex = null;
141
- try {
142
- const testEmbed = await embeddingProvider.embed('test');
143
- vectorIndex = new VectorIndex(path.join(vaultDir, 'vectors.db'), testEmbed.dimensions);
144
- logger.info('embeddings', 'Vector index initialized', { dimensions: testEmbed.dimensions });
145
- }
146
- catch (error) {
147
- logger.warn('embeddings', 'Vector index unavailable', { error: error.message });
148
- }
149
- const processor = new BufferProcessor(llmProvider, config.intelligence.llm.context_window);
150
- const vault = new VaultWriter(vaultDir);
151
- const index = new MycoIndex(path.join(vaultDir, 'index.db'));
152
- const lineageGraph = new LineageGraph(vaultDir);
153
- const transcriptMiner = new TranscriptMiner({
154
- additionalAdapters: config.capture.transcript_paths.map((p) => createPerProjectAdapter(p, claudeCodeAdapter.parseTurns)),
155
- });
156
- let activeStopProcessing = null;
157
- const indexDeps = { index, vaultDir, vectorIndex, embeddingProvider, logger };
158
- const bufferDir = path.join(vaultDir, 'buffer');
159
- const sessionBuffers = new Map();
160
- const sessionFilePaths = new Map();
161
- // Clean up stale buffer files (>24h) on startup
162
- if (fs.existsSync(bufferDir)) {
163
- const cutoff = Date.now() - STALE_BUFFER_MAX_AGE_MS;
164
- for (const file of fs.readdirSync(bufferDir)) {
165
- const filePath = path.join(bufferDir, file);
166
- const stat = fs.statSync(filePath);
167
- if (stat.mtimeMs < cutoff) {
168
- fs.unlinkSync(filePath);
169
- logger.debug('daemon', 'Cleaned stale buffer', { file });
170
- }
171
- }
172
- }
173
- // Migrate flat memory files into type subdirectories
174
- const migrated = migrateMemoryFiles(vaultDir);
175
- if (migrated > 0) {
176
- logger.info('daemon', 'Migrated memory files to type subdirectories', { count: migrated });
177
- // Rebuild FTS index to update stored paths (vectors are keyed by ID, unaffected)
178
- initFts(index);
179
- rebuildIndex(index, vaultDir);
180
- }
181
- // Route body schemas
182
- const RegisterBody = z.object({
183
- session_id: z.string(),
184
- branch: z.string().optional(),
185
- started_at: z.string().optional(),
186
- });
187
- const UnregisterBody = z.object({ session_id: z.string() });
188
- const EventBody = z.object({ type: z.string(), session_id: z.string() }).passthrough();
189
- const StopBody = z.object({
190
- session_id: z.string(),
191
- user: z.string().optional(),
192
- transcript_path: z.string().optional(),
193
- last_assistant_message: z.string().optional(),
194
- });
195
- const ContextBody = z.object({
196
- session_id: z.string().optional(),
197
- branch: z.string().optional(),
198
- files: z.array(z.string()).optional(),
199
- });
200
- const planWatcher = new PlanWatcher({
201
- projectRoot: process.cwd(),
202
- watchPaths: config.capture.artifact_watch,
203
- onPlan: (event) => {
204
- logger.info('watcher', 'Plan detected', { source: event.source, file: event.filePath });
205
- if (event.filePath) {
206
- try {
207
- const content = fs.readFileSync(event.filePath, 'utf-8');
208
- const relativePath = path.relative(vaultDir, event.filePath);
209
- const title = content.match(/^#\s+(.+)$/m)?.[1] ?? path.basename(event.filePath);
210
- const planId = `plan-${path.basename(event.filePath, '.md')}`;
211
- indexAndEmbed(relativePath, planId, `${title}\n${content}`, { type: 'plan' }, indexDeps);
212
- // Register plan-session association for lineage detection.
213
- // When a future session's first prompt mentions this plan ID,
214
- // detectHeuristicParent links it as a child of this session.
215
- if (event.sessionId) {
216
- lineageGraph.registerArtifactForSession(event.sessionId, planId);
217
- logger.debug('lineage', 'Plan registered for session', { planId, session: event.sessionId });
218
- }
219
- logger.info('watcher', 'Plan indexed', { path: relativePath });
220
- }
221
- catch (err) {
222
- logger.debug('watcher', 'Plan index failed', { error: err.message });
223
- }
224
- }
225
- },
226
- });
227
- planWatcher.startFileWatcher();
228
- const batchManager = new BatchManager(async (closedBatch) => {
229
- if (closedBatch.length === 0)
230
- return;
231
- const sessionId = closedBatch[0].session_id;
232
- // Extract observations from this batch
233
- const asRecords = closedBatch;
234
- const result = await processor.process(asRecords, sessionId);
235
- if (!result.degraded) {
236
- writeObservations(result.observations, sessionId, { vault, ...indexDeps });
237
- }
238
- logger.debug('processor', 'Batch processed', {
239
- session_id: sessionId,
240
- events: closedBatch.length,
241
- observations: result.observations.length,
242
- degraded: result.degraded,
243
- });
244
- });
245
- // Session routes
246
- server.registerRoute('POST', '/sessions/register', async (body) => {
247
- const { session_id, branch, started_at } = RegisterBody.parse(body);
248
- const resolvedStartedAt = started_at ?? new Date().toISOString();
249
- registry.register(session_id, { started_at: resolvedStartedAt, branch });
250
- server.updateDaemonJsonSessions(registry.sessions);
251
- // Heuristic lineage detection
252
- try {
253
- const recentSessions = index.query({ type: 'session', limit: LINEAGE_RECENT_SESSIONS_LIMIT })
254
- .map((n) => {
255
- const fm = n.frontmatter;
256
- return { id: bareSessionId(n.id), ended: fm.ended, branch: fm.branch };
257
- });
258
- const activeSessions = registry.sessions
259
- .filter((s) => s !== session_id)
260
- .map((s) => registry.getSession(s))
261
- .filter((s) => s !== undefined);
262
- const link = lineageGraph.detectHeuristicParent(session_id, {
263
- started_at: resolvedStartedAt,
264
- branch,
265
- }, recentSessions, activeSessions);
266
- if (link) {
267
- logger.info('lineage', 'Heuristic parent detected', { child: session_id, parent: link.parent, signal: link.signal });
268
- }
269
- }
270
- catch (err) {
271
- logger.debug('lineage', 'Heuristic detection failed', { error: err.message });
272
- }
273
- logger.info('lifecycle', 'Session registered', { session_id, branch });
274
- return { ok: true, sessions: registry.sessions };
275
- });
276
- server.registerRoute('POST', '/sessions/unregister', async (body) => {
277
- const { session_id } = UnregisterBody.parse(body);
278
- registry.unregister(session_id);
279
- // Note: we do NOT delete the buffer FILE for THIS session. Session reload
280
- // (SessionEnd → SessionStart) reuses the same session_id, and deleting
281
- // would wipe all prior events.
282
- // We DO opportunistically clean stale buffers for OTHER sessions (>24h).
283
- try {
284
- const cutoff = Date.now() - STALE_BUFFER_MAX_AGE_MS;
285
- for (const file of fs.readdirSync(bufferDir)) {
286
- if (!file.endsWith('.jsonl'))
287
- continue;
288
- const bufferSessionId = file.replace('.jsonl', '');
289
- if (bufferSessionId === session_id)
290
- continue; // skip current session
291
- const filePath = path.join(bufferDir, file);
292
- const stat = fs.statSync(filePath);
293
- if (stat.mtimeMs < cutoff) {
294
- fs.unlinkSync(filePath);
295
- logger.debug('daemon', 'Cleaned stale buffer', { file });
296
- }
297
- }
298
- }
299
- catch { /* buffer dir may not exist */ }
300
- // We DO prune the in-memory Map entries to avoid unbounded growth.
301
- sessionBuffers.delete(session_id);
302
- sessionFilePaths.delete(session_id);
303
- server.updateDaemonJsonSessions(registry.sessions);
304
- logger.info('lifecycle', 'Session unregistered', { session_id });
305
- return { ok: true, sessions: registry.sessions };
306
- });
307
- // Event routes
308
- server.registerRoute('POST', '/events', async (body) => {
309
- const validated = EventBody.parse(body);
310
- const event = { ...validated, timestamp: validated.timestamp ?? new Date().toISOString() };
311
- logger.debug('hooks', 'Event received', { type: event.type, session_id: event.session_id });
312
- // Ensure session is registered (idempotent — handles daemon restarts mid-session)
313
- if (!registry.getSession(event.session_id)) {
314
- registry.register(event.session_id, { started_at: event.timestamp });
315
- logger.debug('lifecycle', 'Auto-registered session from event', { session_id: event.session_id });
316
- }
317
- // Persist to disk so events survive daemon restarts
318
- if (!sessionBuffers.has(event.session_id)) {
319
- sessionBuffers.set(event.session_id, new EventBuffer(bufferDir, event.session_id));
320
- }
321
- sessionBuffers.get(event.session_id).append(event);
322
- batchManager.addEvent(event);
323
- if (validated.type === 'tool_use') {
324
- const v = validated;
325
- planWatcher.checkToolEvent({ tool_name: String(v.tool_name ?? ''), tool_input: v.tool_input, session_id: validated.session_id });
326
- const toolName = String(v.tool_name ?? '');
327
- if (toolName === 'Write' || toolName === 'Edit') {
328
- const filePath = v.tool_input?.file_path;
329
- if (filePath) {
330
- if (!sessionFilePaths.has(event.session_id)) {
331
- sessionFilePaths.set(event.session_id, new Set());
332
- }
333
- sessionFilePaths.get(event.session_id).add(filePath);
334
- }
335
- }
336
- }
337
- return { ok: true };
338
- });
339
- server.registerRoute('POST', '/events/stop', async (body) => {
340
- const { session_id: sessionId, user, transcript_path: hookTranscriptPath, last_assistant_message: lastAssistantMessage } = StopBody.parse(body);
341
- // Ensure session is registered (handles daemon restarts mid-session)
342
- if (!registry.getSession(sessionId)) {
343
- registry.register(sessionId, { started_at: new Date().toISOString() });
344
- logger.debug('lifecycle', 'Auto-registered session from stop event', { session_id: sessionId });
345
- }
346
- const sessionMeta = registry.getSession(sessionId);
347
- logger.info('hooks', 'Stop received', { session_id: sessionId, has_transcript_path: !!hookTranscriptPath, has_response: !!lastAssistantMessage });
348
- // Respond immediately — the hook should not block on LLM processing.
349
- // Serialize stop processing: if a previous stop is still running, chain
350
- // the new one to run after it completes. This prevents concurrent
351
- // processStopEvent calls from racing on the same session file.
352
- const run = () => processStopEvent(sessionId, user, sessionMeta, hookTranscriptPath, lastAssistantMessage).catch((err) => {
353
- logger.error('processor', 'Stop processing failed', { session_id: sessionId, error: err.message });
354
- });
355
- // Chain onto any in-flight processing. Only the tail of the chain clears the variable.
356
- const prev = activeStopProcessing ?? Promise.resolve();
357
- activeStopProcessing = prev.then(run).finally(() => { activeStopProcessing = null; });
358
- return { ok: true };
359
- });
360
- async function processStopEvent(sessionId, user, sessionMeta, hookTranscriptPath, lastAssistantMessage) {
361
- // --- Phase 1: Gather data (I/O only, no LLM) ---
362
- const lastBatch = batchManager.finalize(sessionId);
363
- // Tiered turn extraction:
364
- // 1. Read transcript for complete turns (with AI responses)
365
- // 2. Check buffer for prompts newer than the transcript's last turn
366
- // (captures turns the transcript missed, e.g., after context compaction)
367
- // 3. Fall back to buffer entirely if no transcript found
368
- const transcriptResult = transcriptMiner.getAllTurnsWithSource(sessionId, hookTranscriptPath);
369
- let allTurns = transcriptResult.turns;
370
- let turnSource = transcriptResult.source;
371
- const bufferEvents = sessionBuffers.get(sessionId)?.readAll() ?? [];
372
- if (allTurns.length === 0) {
373
- // No transcript — use buffer as primary source
374
- allTurns = extractTurnsFromBuffer(bufferEvents);
375
- turnSource = 'buffer';
376
- }
377
- else if (bufferEvents.length > 0) {
378
- // Transcript exists — check for buffer events newer than the last transcript turn.
379
- // These are prompts the transcript missed (e.g., after context compaction).
380
- const lastTranscriptTs = allTurns[allTurns.length - 1].timestamp;
381
- if (lastTranscriptTs) {
382
- const newerEvents = bufferEvents.filter((e) => String(e.timestamp ?? '') > lastTranscriptTs);
383
- if (newerEvents.length > 0) {
384
- const bufferTurns = extractTurnsFromBuffer(newerEvents);
385
- allTurns = [...allTurns, ...bufferTurns];
386
- turnSource = `${transcriptResult.source}+buffer`;
387
- logger.info('processor', 'Appended buffer turns missing from transcript', {
388
- session_id: sessionId, transcriptTurns: transcriptResult.turns.length, bufferTurns: bufferTurns.length,
389
- });
390
- }
391
- }
392
- }
393
- // Attach the last assistant message from the hook to the most recent turn
394
- // that doesn't already have an AI response. This captures the response
395
- // for turns the transcript missed (post-compaction) or buffer-sourced turns.
396
- if (lastAssistantMessage && allTurns.length > 0) {
397
- const lastTurn = allTurns[allTurns.length - 1];
398
- if (!lastTurn.aiResponse) {
399
- lastTurn.aiResponse = lastAssistantMessage;
400
- }
401
- }
402
- const ended = new Date().toISOString();
403
- let started = (allTurns.length > 0 && allTurns[0].timestamp) ? allTurns[0].timestamp : ended;
404
- // Find existing session file and clean up cross-date duplicates in one pass.
405
- const sessionsDir = path.join(vaultDir, 'sessions');
406
- const sessionFileName = `${sessionNoteId(sessionId)}.md`;
407
- let existingContent;
408
- const duplicatePaths = [];
409
- try {
410
- for (const dateDir of fs.readdirSync(sessionsDir)) {
411
- const candidate = path.join(sessionsDir, dateDir, sessionFileName);
412
- try {
413
- const content = fs.readFileSync(candidate, 'utf-8');
414
- if (!existingContent || content.length > existingContent.length) {
415
- existingContent = content;
416
- }
417
- duplicatePaths.push(candidate);
418
- }
419
- catch { /* file doesn't exist in this date dir */ }
420
- }
421
- }
422
- catch { /* sessions dir may not exist yet */ }
423
- let existingTurnCount = 0;
424
- if (existingContent) {
425
- const startedMatch = existingContent.match(/^started:\s*"?(.+?)"?\s*$/m);
426
- if (startedMatch)
427
- started = startedMatch[1];
428
- const turnMatches = existingContent.match(/^### Turn \d+/gm);
429
- existingTurnCount = turnMatches?.length ?? 0;
430
- }
431
- // Collect artifact candidates (no LLM yet)
432
- const writtenFiles = sessionFilePaths.get(sessionId) ?? new Set();
433
- const artifactCandidates = collectArtifactCandidates(writtenFiles, { artifact_extensions: config.capture.artifact_extensions }, process.cwd());
434
- // Guard: never overwrite an existing session with zero turns — the transcript
435
- // is temporarily unreadable (daemon restart, file locked, etc.)
436
- if (allTurns.length === 0 && existingTurnCount > 0) {
437
- logger.warn('processor', 'Transcript unreadable, skipping rewrite to preserve existing data', { session_id: sessionId, existingTurns: existingTurnCount });
438
- return;
439
- }
440
- // Skip if no new turns AND no batch to process AND no artifacts to classify
441
- if (allTurns.length > 0 && allTurns.length === existingTurnCount && lastBatch.length === 0 && artifactCandidates.length === 0) {
442
- logger.debug('processor', 'No new turns, skipping session rewrite', { session_id: sessionId, turns: allTurns.length });
443
- return;
444
- }
445
- // --- Phase 2: LLM calls in parallel ---
446
- // Build conversation text for summarization (pure string, no LLM)
447
- const conversationText = allTurns.map((t, i) => {
448
- const parts = [`### Turn ${i + 1}`];
449
- if (t.prompt)
450
- parts.push(`Prompt: ${t.prompt}`);
451
- if (t.toolCount > 0)
452
- parts.push(`Tools: ${t.toolCount} calls`);
453
- if (t.aiResponse)
454
- parts.push(`Response: ${t.aiResponse}`);
455
- return parts.join('\n');
456
- }).join('\n\n');
457
- const conversationSection = `## Conversation\n\n${conversationText}`;
458
- // Launch all LLM calls concurrently
459
- const observationPromise = lastBatch.length > 0
460
- ? processor.process(lastBatch, sessionId)
461
- .catch((err) => { logger.warn('processor', 'Observation extraction failed', { session_id: sessionId, error: err.message }); return null; })
462
- : Promise.resolve(null);
463
- const artifactPromise = artifactCandidates.length > 0
464
- ? processor.classifyArtifacts(artifactCandidates, sessionId)
465
- .then((classified) => captureArtifacts(artifactCandidates, classified, sessionId, { vault, ...indexDeps }, lineageGraph))
466
- .catch((err) => { logger.warn('processor', 'Artifact capture failed', { session_id: sessionId, error: err.message }); })
467
- : Promise.resolve();
468
- const summaryPromise = processor.summarizeSession(conversationSection, sessionId, user)
469
- .catch((err) => { logger.warn('processor', 'Session summarization failed', { session_id: sessionId, error: err.message }); return null; });
470
- // Wait for all LLM calls to complete
471
- const [observationResult, , summaryResult] = await Promise.all([observationPromise, artifactPromise, summaryPromise]);
472
- // --- Phase 3: Write results to vault ---
473
- // Write observations
474
- if (observationResult && !observationResult.degraded) {
475
- writeObservations(observationResult.observations, sessionId, { vault, ...indexDeps });
476
- }
477
- // Compute canonical path
478
- const date = started.slice(0, 10);
479
- const relativePath = sessionRelativePath(sessionId, date);
480
- const targetFullPath = path.join(vaultDir, relativePath);
481
- // Remove cross-date duplicates
482
- for (const dup of duplicatePaths) {
483
- if (dup !== targetFullPath) {
484
- try {
485
- fs.unlinkSync(dup);
486
- logger.debug('lifecycle', 'Removed duplicate session file', { path: dup });
487
- }
488
- catch { /* already gone */ }
489
- }
490
- }
491
- // Write images to attachments
492
- const attachmentsDir = path.join(vaultDir, 'attachments');
493
- const hasImages = allTurns.some((t) => t.images?.length);
494
- if (hasImages) {
495
- fs.mkdirSync(attachmentsDir, { recursive: true });
496
- }
497
- const turnImageNames = new Map();
498
- for (let i = 0; i < allTurns.length; i++) {
499
- const turn = allTurns[i];
500
- if (!turn.images?.length)
501
- continue;
502
- const names = [];
503
- for (let j = 0; j < turn.images.length; j++) {
504
- const img = turn.images[j];
505
- const ext = extensionForMimeType(img.mediaType);
506
- const filename = `${bareSessionId(sessionId)}-t${i + 1}-${j + 1}.${ext}`;
507
- const filePath = path.join(attachmentsDir, filename);
508
- if (!fs.existsSync(filePath)) {
509
- fs.writeFileSync(filePath, Buffer.from(img.data, 'base64'));
510
- logger.debug('processor', 'Image saved', { filename, turn: i + 1 });
511
- }
512
- names.push(filename);
513
- }
514
- turnImageNames.set(i, names);
515
- }
516
- // Build and write session note
517
- let title = `Session ${sessionId}`;
518
- let narrative = '';
519
- if (summaryResult) {
520
- title = summaryResult.title;
521
- narrative = summaryResult.summary;
522
- }
523
- // Query related memories for this session
524
- const relatedMemories = index.query({ type: 'memory', limit: RELATED_MEMORIES_LIMIT })
525
- .filter((n) => {
526
- const fm = n.frontmatter;
527
- return fm.session === sessionNoteId(sessionId) || fm.session === sessionId;
528
- })
529
- .map((n) => ({ id: n.id, title: n.title }));
530
- // The formatter always gets the full turn list — no more partial appending.
531
- // existingConversation is no longer needed; we rebuild from the transcript each time.
532
- const summary = formatSessionBody({
533
- title,
534
- narrative,
535
- sessionId,
536
- user,
537
- started,
538
- ended,
539
- branch: sessionMeta?.branch,
540
- relatedMemories,
541
- turns: allTurns.map((t, i) => ({
542
- prompt: t.prompt,
543
- toolCount: t.toolCount,
544
- aiResponse: t.aiResponse,
545
- images: turnImageNames.get(i),
546
- })),
547
- });
548
- const parentId = lineageGraph.getParent(sessionId);
549
- const parentLink = parentId ? lineageGraph.getLinks().find((l) => l.child === sessionId) : undefined;
550
- vault.writeSession({
551
- id: sessionId,
552
- user,
553
- started,
554
- ended,
555
- branch: sessionMeta?.branch,
556
- parent: parentId ? sessionWikilink(parentId) : undefined,
557
- parent_reason: parentLink?.signal,
558
- tools_used: allTurns.reduce((sum, t) => sum + t.toolCount, 0),
559
- summary,
560
- });
561
- indexAndEmbed(relativePath, sessionNoteId(sessionId), narrative, { type: 'session', session_id: sessionId }, indexDeps);
562
- logger.debug('processor', 'Session turns', { source: turnSource, total: allTurns.length });
563
- // Wait for artifact capture (started concurrently with session building)
564
- await artifactPromise;
565
- // Phase 2: LLM similarity detection (fire-and-forget, only if no heuristic parent)
566
- if (!parentId && vectorIndex && narrative) {
567
- generateEmbedding(embeddingProvider, narrative)
568
- .then(async (emb) => {
569
- const candidates = vectorIndex.search(emb.embedding, { limit: LINEAGE_SIMILARITY_CANDIDATES })
570
- .filter((r) => r.metadata.type === 'session' && r.id !== sessionNoteId(sessionId));
571
- if (candidates.length === 0)
572
- return;
573
- // Score all candidates in parallel
574
- const candidateNotes = index.queryByIds(candidates.map((c) => c.id));
575
- const noteMap = new Map(candidateNotes.map((n) => [n.id, n]));
576
- const scores = await Promise.all(candidates.map(async (candidate) => {
577
- const note = noteMap.get(candidate.id);
578
- if (!note)
579
- return { id: candidate.id, score: 0 };
580
- try {
581
- const prompt = buildSimilarityPrompt(narrative, note.content.slice(0, CANDIDATE_CONTENT_PREVIEW));
582
- const response = await llmProvider.summarize(prompt, { maxTokens: LINEAGE_SIMILARITY_MAX_TOKENS });
583
- const score = extractNumber(response.text);
584
- return { id: candidate.id, score: isNaN(score) ? 0 : score };
585
- }
586
- catch {
587
- return { id: candidate.id, score: 0 };
588
- }
589
- }));
590
- const best = scores.reduce((a, b) => (b.score > a.score ? b : a));
591
- if (best.score >= LINEAGE_SIMILARITY_THRESHOLD) {
592
- const bestParentId = bareSessionId(best.id);
593
- const confidence = best.score >= LINEAGE_SIMILARITY_HIGH_CONFIDENCE ? 'high' : 'medium';
594
- lineageGraph.addLink({
595
- parent: bestParentId,
596
- child: sessionId,
597
- signal: 'semantic_similarity',
598
- confidence: confidence,
599
- });
600
- // Retroactively update session frontmatter with parent + re-index
601
- try {
602
- vault.updateNoteFrontmatter(relativePath, {
603
- parent: sessionWikilink(bestParentId),
604
- parent_reason: 'semantic_similarity',
605
- });
606
- indexNote(index, vaultDir, relativePath);
607
- }
608
- catch { /* frontmatter update failed — link still in lineage.json */ }
609
- logger.info('lineage', 'LLM similarity parent detected', {
610
- child: sessionId, parent: bestParentId, score: best.score,
611
- });
612
- }
613
- })
614
- .catch((err) => logger.debug('lineage', 'Similarity detection failed', { error: err.message }));
615
- }
616
- logger.info('processor', 'Session note written', { session_id: sessionId, path: relativePath });
617
- }
618
- // Session-start context: structural facts only — active plans + parent session.
619
- // Memories are injected per-prompt (more targeted, less noise).
620
- server.registerRoute('POST', '/context', async (body) => {
621
- const { session_id, branch } = ContextBody.parse(body);
622
- logger.debug('hooks', 'Session context query', { session_id });
623
- try {
624
- const parts = [];
625
- // Active plans — the agent needs to know what's in flight
626
- const plans = index.query({ type: 'plan' });
627
- const activePlans = plans.filter((p) => {
628
- const status = p.frontmatter.status;
629
- return status === 'active' || status === 'in_progress';
630
- });
631
- if (activePlans.length > 0) {
632
- const planLines = activePlans.slice(0, SESSION_CONTEXT_MAX_PLANS).map((p) => {
633
- const status = p.frontmatter.status;
634
- return `- **${p.title}** (${status}) \`[${p.id}]\``;
635
- });
636
- parts.push(`### Active Plans\n${planLines.join('\n')}`);
637
- }
638
- // Parent session summary — lineage continuity
639
- if (session_id) {
640
- const parentId = lineageGraph.getParent(session_id);
641
- if (parentId) {
642
- const parentNotes = index.queryByIds([sessionNoteId(parentId)]);
643
- if (parentNotes.length > 0) {
644
- const parent = parentNotes[0];
645
- parts.push(`### Previous Session\n- **${parent.title}**: ${parent.content.slice(0, CONTEXT_SESSION_PREVIEW_CHARS)} \`[${parent.id}]\``);
646
- }
647
- }
648
- }
649
- // Branch info for awareness
650
- if (branch) {
651
- parts.push(`Branch:: \`${branch}\``);
652
- }
653
- if (parts.length > 0) {
654
- return { text: parts.join('\n\n') };
655
- }
656
- return { text: '' };
657
- }
658
- catch (error) {
659
- logger.error('daemon', 'Session context failed', { error: error.message });
660
- return { text: '' };
661
- }
662
- });
663
- // Per-prompt context: semantic search for memories relevant to THIS specific prompt.
664
- // This is the primary intelligence delivery — targeted, high-confidence, token-efficient.
665
- const PromptContextBody = z.object({
666
- prompt: z.string(),
667
- session_id: z.string().optional(),
668
- });
669
- server.registerRoute('POST', '/context/prompt', async (body) => {
670
- const { prompt, session_id } = PromptContextBody.parse(body);
671
- if (!prompt || prompt.length < PROMPT_CONTEXT_MIN_LENGTH || !vectorIndex) {
672
- return { text: '' };
673
- }
674
- try {
675
- const emb = await generateEmbedding(embeddingProvider, prompt.slice(0, EMBEDDING_INPUT_LIMIT));
676
- const results = vectorIndex.search(emb.embedding, {
677
- limit: PROMPT_CONTEXT_MAX_MEMORIES,
678
- type: 'memory',
679
- relativeThreshold: PROMPT_CONTEXT_MIN_SIMILARITY,
680
- });
681
- if (results.length === 0)
682
- return { text: '' };
683
- const noteMap = new Map(index.queryByIds(results.map((r) => r.id)).map((n) => [n.id, n]));
684
- const lines = [];
685
- for (const r of results) {
686
- const note = noteMap.get(r.id);
687
- if (!note)
688
- continue;
689
- const fm = note.frontmatter;
690
- if (fm.status === 'superseded' || fm.status === 'archived')
691
- continue;
692
- const obsType = fm.observation_type ?? 'note';
693
- lines.push(`- [${obsType}] ${note.title}: ${note.content.slice(0, CONTENT_SNIPPET_CHARS)} \`[${note.id}]\``);
694
- }
695
- if (lines.length === 0)
696
- return { text: '' };
697
- const injected = `**Relevant memories for this task:**\n${lines.join('\n')}`;
698
- logger.debug('context', 'Prompt context injected', {
699
- session_id,
700
- memories: lines.length,
701
- prompt_preview: prompt.slice(0, 50),
702
- });
703
- return { text: injected };
704
- }
705
- catch (err) {
706
- logger.debug('context', 'Prompt context failed', { error: err.message });
707
- return { text: '' };
708
- }
709
- });
710
- await server.start();
711
- logger.info('daemon', 'Daemon ready', { vault: vaultDir, port: server.port });
712
- const shutdown = async (signal) => {
713
- logger.info('daemon', `${signal} received`);
714
- // Wait for any active stop processing to finish before shutting down
715
- if (activeStopProcessing) {
716
- logger.info('daemon', 'Waiting for active stop processing to complete...');
717
- await activeStopProcessing;
718
- }
719
- planWatcher.stopFileWatcher();
720
- registry.destroy();
721
- await server.stop();
722
- vectorIndex?.close();
723
- index.close();
724
- logger.close();
725
- process.exit(0);
726
- };
727
- process.on('SIGTERM', () => shutdown('SIGTERM'));
728
- process.on('SIGINT', () => shutdown('SIGINT'));
729
- }
730
- // Entry point guard — only run when executed directly, not when imported in tests
731
- const __filename = fileURLToPath(import.meta.url);
732
- if (process.argv[1] === __filename) {
733
- main().catch((err) => {
734
- process.stderr.write(`[mycod] Fatal: ${err.message}\n`);
735
- process.exit(1);
736
- });
737
- }
1
+ import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
+ import {
3
+ ensureNativeDeps
4
+ } from "../../chunk-4JML636J.js";
5
+ import "../../chunk-PZUWP5VK.js";
6
+
7
+ // src/entries/daemon.ts
8
+ ensureNativeDeps();
9
+ await import("../../main-5W4ADOBG.js");
738
10
  //# sourceMappingURL=main.js.map