claude-memory-layer 1.0.27 → 1.0.28

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 (329) hide show
  1. package/.env.example +7 -0
  2. package/AGENTS.md +11 -0
  3. package/README.md +184 -41
  4. package/benchmarks/replay/anonymized-real-sessions.json +48 -0
  5. package/dist/cli/index.js +10097 -6003
  6. package/dist/cli/index.js.map +4 -4
  7. package/dist/core/index.js +9745 -5587
  8. package/dist/core/index.js.map +4 -4
  9. package/dist/hooks/post-tool-use.js +6545 -5270
  10. package/dist/hooks/post-tool-use.js.map +4 -4
  11. package/dist/hooks/semantic-daemon.js +6646 -5354
  12. package/dist/hooks/semantic-daemon.js.map +4 -4
  13. package/dist/hooks/session-end.js +6618 -5347
  14. package/dist/hooks/session-end.js.map +4 -4
  15. package/dist/hooks/session-start.js +6619 -5354
  16. package/dist/hooks/session-start.js.map +4 -4
  17. package/dist/hooks/stop.js +6614 -5325
  18. package/dist/hooks/stop.js.map +4 -4
  19. package/dist/hooks/user-prompt-submit.js +6702 -5356
  20. package/dist/hooks/user-prompt-submit.js.map +4 -4
  21. package/dist/index.js +13537 -0
  22. package/dist/index.js.map +7 -0
  23. package/dist/mcp/index.js +20770 -0
  24. package/dist/mcp/index.js.map +7 -0
  25. package/dist/server/api/index.js +6632 -5319
  26. package/dist/server/api/index.js.map +4 -4
  27. package/dist/server/index.js +6667 -5340
  28. package/dist/server/index.js.map +4 -4
  29. package/dist/services/memory-service.js +6568 -5350
  30. package/dist/services/memory-service.js.map +4 -4
  31. package/dist/ui/assets/js/bootstrap.js +244 -0
  32. package/dist/ui/assets/js/chat.js +373 -0
  33. package/dist/ui/assets/js/disclosure.js +232 -0
  34. package/dist/ui/assets/js/modals.js +298 -0
  35. package/dist/ui/assets/js/overview.js +655 -0
  36. package/dist/ui/assets/js/state.js +72 -0
  37. package/dist/ui/assets/js/views.js +468 -0
  38. package/dist/ui/index.html +43 -1
  39. package/dist/ui/index.ts +3 -0
  40. package/dist/ui/style.css +222 -0
  41. package/docs/ARCHITECTURE_COMPARISON_AND_RECOMMENDATIONS.md +627 -0
  42. package/docs/HERMES_MEMORY_INGESTION_ANALYSIS.md +440 -0
  43. package/docs/MEMORY_USEFULNESS_AUDIT.md +371 -0
  44. package/docs/MEMORY_USEFULNESS_AUDIT_RAW.json +80 -0
  45. package/docs/MEMSEARCH_PROJECT_STRUCTURE_ANALYSIS.md +333 -0
  46. package/docs/PRODUCT_VALIDATION_MATRIX.md +82 -0
  47. package/docs/PROJECT_STRUCTURE_ANALYSIS.md +421 -0
  48. package/docs/REFACTORING_MILESTONES_AND_ISSUES.md +501 -0
  49. package/docs/REFACTORING_PLAN_THIN_CORE.md +414 -0
  50. package/docs/REFERENCE_PROJECT_ANALYSES.md +25 -0
  51. package/docs/SUPERLOCALMEMORY_PROJECT_STRUCTURE_ANALYSIS.md +452 -0
  52. package/docs/TARGET_ARCHITECTURE_AND_FOLDER_STRUCTURE.md +446 -0
  53. package/docs/architecture/comparison-index.md +47 -0
  54. package/docs/reports/codex-real-data-validation-20260505T040447Z.md +46 -0
  55. package/package.json +9 -5
  56. package/scripts/build.ts +25 -8
  57. package/scripts/generate-session-qrels.ts +126 -0
  58. package/scripts/replay-retrieval-benchmark.ts +69 -0
  59. package/specs/thin-core-refactor/context.md +275 -0
  60. package/specs/thin-core-refactor/plan.md +536 -0
  61. package/specs/thin-core-refactor/spec.md +465 -0
  62. package/src/adapters/claude/capture/index.ts +3 -0
  63. package/src/adapters/claude/context/index.ts +3 -0
  64. package/src/adapters/claude/hooks/index.ts +21 -0
  65. package/src/adapters/claude/hooks/post-tool-use.ts +239 -0
  66. package/src/adapters/claude/hooks/prompt-injection-policy.ts +104 -0
  67. package/src/adapters/claude/hooks/semantic-daemon-client.ts +209 -0
  68. package/src/adapters/claude/hooks/semantic-daemon.ts +283 -0
  69. package/src/adapters/claude/hooks/session-end.ts +59 -0
  70. package/src/adapters/claude/hooks/session-start.ts +73 -0
  71. package/src/adapters/claude/hooks/stop.ts +128 -0
  72. package/src/adapters/claude/hooks/user-prompt-submit.ts +361 -0
  73. package/src/adapters/claude/index.ts +4 -0
  74. package/src/adapters/claude/transcript/index.ts +4 -0
  75. package/src/adapters/claude/transcript/transcript-reader.ts +57 -0
  76. package/src/adapters/claude/transcript/turn-reconstructor.ts +65 -0
  77. package/src/apps/cli/claude-settings-hooks.ts +138 -0
  78. package/src/apps/cli/codex-import-runner.ts +125 -0
  79. package/src/apps/cli/codex-validation-output.ts +95 -0
  80. package/src/apps/cli/hermes-import-runner.ts +130 -0
  81. package/src/apps/cli/hermes-validation-output.ts +91 -0
  82. package/src/apps/cli/index.ts +1731 -0
  83. package/src/apps/cli/mcp-install.ts +106 -0
  84. package/src/apps/cli/retrieval-disclosure-output.ts +196 -0
  85. package/src/apps/dashboard/assets/js/bootstrap.js +244 -0
  86. package/src/apps/dashboard/assets/js/chat.js +373 -0
  87. package/src/apps/dashboard/assets/js/disclosure.js +232 -0
  88. package/src/apps/dashboard/assets/js/modals.js +298 -0
  89. package/src/apps/dashboard/assets/js/overview.js +655 -0
  90. package/src/apps/dashboard/assets/js/state.js +72 -0
  91. package/src/apps/dashboard/assets/js/views.js +468 -0
  92. package/src/{ui → apps/dashboard}/index.html +43 -1
  93. package/src/apps/dashboard/index.ts +3 -0
  94. package/src/{ui → apps/dashboard}/style.css +222 -0
  95. package/src/apps/index.ts +5 -0
  96. package/src/apps/server/api/chat.ts +244 -0
  97. package/src/apps/server/api/citations.ts +105 -0
  98. package/src/apps/server/api/events.ts +137 -0
  99. package/src/apps/server/api/health.ts +53 -0
  100. package/src/apps/server/api/index.ts +26 -0
  101. package/src/apps/server/api/projects.ts +74 -0
  102. package/src/apps/server/api/search.ts +184 -0
  103. package/src/apps/server/api/sessions.ts +115 -0
  104. package/src/apps/server/api/stats.ts +723 -0
  105. package/src/apps/server/api/turns.ts +143 -0
  106. package/src/apps/server/api/utils.ts +65 -0
  107. package/src/apps/server/index.ts +111 -0
  108. package/src/cli/index.ts +2 -1311
  109. package/src/cli/retrieval-disclosure-output.ts +2 -0
  110. package/src/compat/index.ts +5 -0
  111. package/src/core/derive/fact-deriver.ts +170 -0
  112. package/src/core/derive/index.ts +2 -0
  113. package/src/core/derive/summary-deriver.ts +76 -0
  114. package/src/core/embedder.ts +4 -152
  115. package/src/core/engine/embedding-maintenance-service.ts +187 -0
  116. package/src/core/engine/endless-memory-services.ts +4 -0
  117. package/src/core/engine/index.ts +19 -0
  118. package/src/core/engine/memory-engine-services.ts +170 -0
  119. package/src/core/engine/memory-ingest-service.ts +317 -0
  120. package/src/core/engine/memory-query-service.ts +173 -0
  121. package/src/core/engine/memory-runtime-service.ts +162 -0
  122. package/src/core/engine/memory-service-composition.ts +231 -0
  123. package/src/core/engine/retrieval-analytics-service.ts +181 -0
  124. package/src/core/engine/retrieval-disclosure-service.ts +420 -0
  125. package/src/core/engine/retrieval-orchestrator.ts +377 -0
  126. package/src/core/engine/retrieval-services.ts +176 -0
  127. package/src/core/engine/shared-memory-services.ts +4 -0
  128. package/src/core/entity-repo.ts +1 -3
  129. package/src/core/event-store.ts +3 -3
  130. package/src/core/evidence-aligner.ts +2 -2
  131. package/src/core/external-market-context.ts +582 -0
  132. package/src/core/graduation.ts +2 -3
  133. package/src/core/index.ts +21 -0
  134. package/src/core/matcher.ts +2 -4
  135. package/src/core/model/memory-fact.ts +30 -0
  136. package/src/core/model/memory-rule.ts +14 -0
  137. package/src/core/model/memory-summary.ts +21 -0
  138. package/src/core/model/raw-event.ts +28 -0
  139. package/src/core/model/retrieval-result.ts +35 -0
  140. package/src/core/privacy/filter.ts +21 -10
  141. package/src/core/product-validation-matrix.ts +314 -0
  142. package/src/core/progressive-retriever.ts +1 -2
  143. package/src/core/registry/project-path.ts +54 -0
  144. package/src/core/registry/session-registry.ts +69 -0
  145. package/src/core/replay-evaluator.ts +625 -0
  146. package/src/core/retrieval-benchmark.ts +117 -0
  147. package/src/core/retrieval-quality.ts +109 -0
  148. package/src/core/retriever.ts +53 -15
  149. package/src/core/session-qrels.ts +360 -0
  150. package/src/core/shared-event-store.ts +1 -1
  151. package/src/core/sqlite-event-store.ts +35 -11
  152. package/src/core/task/blocker-resolver.ts +2 -2
  153. package/src/core/task/task-resolver.ts +0 -1
  154. package/src/core/vector-outbox.ts +1 -10
  155. package/src/core/vector-worker.ts +1 -1
  156. package/src/extensions/endless-memory/endless-memory-services.ts +350 -0
  157. package/src/extensions/endless-memory/index.ts +1 -0
  158. package/src/extensions/index.ts +5 -0
  159. package/src/extensions/mcp/handlers.ts +960 -0
  160. package/src/extensions/mcp/index.ts +48 -0
  161. package/src/extensions/mcp/tools.ts +252 -0
  162. package/src/extensions/shared-memory/index.ts +1 -0
  163. package/src/extensions/shared-memory/shared-memory-services.ts +211 -0
  164. package/src/extensions/vector/embedder.ts +197 -0
  165. package/src/extensions/vector/index.ts +1 -0
  166. package/src/hooks/post-tool-use.ts +3 -236
  167. package/src/hooks/semantic-daemon-client.ts +1 -208
  168. package/src/hooks/semantic-daemon.ts +6 -271
  169. package/src/hooks/session-end.ts +4 -79
  170. package/src/hooks/session-start.ts +4 -73
  171. package/src/hooks/stop.ts +3 -173
  172. package/src/hooks/user-prompt-submit.ts +3 -338
  173. package/src/index.ts +13 -0
  174. package/src/mcp/handlers.ts +2 -212
  175. package/src/mcp/index.ts +3 -46
  176. package/src/mcp/tools.ts +2 -78
  177. package/src/server/api/chat.ts +2 -244
  178. package/src/server/api/citations.ts +2 -105
  179. package/src/server/api/events.ts +2 -137
  180. package/src/server/api/health.ts +2 -53
  181. package/src/server/api/index.ts +2 -26
  182. package/src/server/api/projects.ts +2 -74
  183. package/src/server/api/search.ts +2 -102
  184. package/src/server/api/sessions.ts +2 -115
  185. package/src/server/api/stats.ts +2 -724
  186. package/src/server/api/turns.ts +2 -143
  187. package/src/server/api/utils.ts +2 -46
  188. package/src/server/index.ts +2 -100
  189. package/src/services/bootstrap-organizer.ts +46 -26
  190. package/src/services/codex-session-history-importer.ts +521 -29
  191. package/src/services/hermes-session-history-importer.ts +733 -0
  192. package/src/services/memory-service-config.ts +36 -0
  193. package/src/services/memory-service-registry.ts +150 -0
  194. package/src/services/memory-service.ts +211 -1325
  195. package/src/services/session-history-importer.ts +58 -14
  196. package/tests/README.md +23 -0
  197. package/tests/adapters/claude/claude-semantic-daemon-adapter.test.ts +54 -0
  198. package/tests/adapters/claude/claude-transcript-reconstructor.test.ts +98 -0
  199. package/tests/adapters/claude-hook-prompt-injection-policy.test.ts +99 -0
  200. package/tests/apps/app-layer-boundary.test.ts +48 -0
  201. package/tests/apps/claude-settings-hooks.test.ts +107 -0
  202. package/tests/apps/cli-disclosure-output.test.ts +212 -0
  203. package/tests/apps/codex-import-runner.test.ts +99 -0
  204. package/tests/apps/codex-validation-output.test.ts +100 -0
  205. package/tests/apps/hermes-import-runner.test.ts +99 -0
  206. package/tests/apps/mcp-install-command.test.ts +59 -0
  207. package/tests/apps/package-build-entrypoints.test.ts +30 -0
  208. package/tests/apps/search-api-disclosure.test.ts +162 -0
  209. package/tests/apps/stats-api-lightweight.test.ts +67 -0
  210. package/tests/apps/ui-disclosure-output.test.ts +140 -0
  211. package/tests/{bootstrap-organizer.test.ts → core/bootstrap-organizer.test.ts} +1 -1
  212. package/tests/{canonical-key.test.ts → core/canonical-key.test.ts} +1 -1
  213. package/tests/core/codex-session-history-importer-validation.test.ts +185 -0
  214. package/tests/{consolidation-worker.test.ts → core/consolidation-worker.test.ts} +2 -2
  215. package/tests/core/embedding-maintenance-service.test.ts +282 -0
  216. package/tests/{evidence-aligner.test.ts → core/evidence-aligner.test.ts} +1 -1
  217. package/tests/core/external-market-context.test.ts +209 -0
  218. package/tests/core/fact-deriver.test.ts +79 -0
  219. package/tests/core/hermes-session-history-importer-validation.test.ts +609 -0
  220. package/tests/{ingest-interceptor.test.ts → core/ingest-interceptor.test.ts} +1 -1
  221. package/tests/{markdown-mirror.test.ts → core/markdown-mirror.test.ts} +2 -2
  222. package/tests/{matcher.test.ts → core/matcher.test.ts} +1 -1
  223. package/tests/{md-mirror.test.ts → core/md-mirror.test.ts} +2 -2
  224. package/tests/core/memory-engine-services.test.ts +240 -0
  225. package/tests/core/memory-ingest-service.test.ts +296 -0
  226. package/tests/core/memory-query-service.test.ts +129 -0
  227. package/tests/core/memory-runtime-service.test.ts +201 -0
  228. package/tests/core/memory-service-composition.test.ts +192 -0
  229. package/tests/core/memory-service-config.test.ts +41 -0
  230. package/tests/core/memory-service-facade.test.ts +30 -0
  231. package/tests/core/memory-service-registry.test.ts +206 -0
  232. package/tests/core/product-validation-matrix.test.ts +61 -0
  233. package/tests/core/project-registry.test.ts +78 -0
  234. package/tests/core/replay-evaluator.test.ts +181 -0
  235. package/tests/core/retrieval-analytics-service.test.ts +210 -0
  236. package/tests/core/retrieval-benchmark.test.ts +93 -0
  237. package/tests/core/retrieval-disclosure-service.test.ts +264 -0
  238. package/tests/core/retrieval-orchestrator.test.ts +403 -0
  239. package/tests/core/retrieval-quality.test.ts +31 -0
  240. package/tests/core/retrieval-services.test.ts +185 -0
  241. package/tests/{retriever-fallback-chain.test.ts → core/retriever-fallback-chain.test.ts} +3 -3
  242. package/tests/{retriever-strategy-scope.test.ts → core/retriever-strategy-scope.test.ts} +70 -3
  243. package/tests/{retriever.memu-adoption.test.ts → core/retriever.memu-adoption.test.ts} +3 -3
  244. package/tests/core/session-history-importer-filter.test.ts +78 -0
  245. package/tests/core/session-qrels.test.ts +250 -0
  246. package/tests/{sqlite-event-store-replication.test.ts → core/sqlite-event-store-replication.test.ts} +36 -1
  247. package/tests/core/summary-deriver.test.ts +66 -0
  248. package/tests/extensions/embedder-warning-suppression.test.ts +53 -0
  249. package/tests/extensions/endless-memory-extension-boundary.test.ts +17 -0
  250. package/tests/extensions/endless-memory-services.test.ts +325 -0
  251. package/tests/extensions/mcp-context-tools.test.ts +905 -0
  252. package/tests/extensions/mcp-extension-boundary.test.ts +21 -0
  253. package/tests/extensions/mcp-package-build.test.ts +22 -0
  254. package/tests/extensions/mcp-project-aware-tools.test.ts +102 -0
  255. package/tests/extensions/shared-memory-extension-boundary.test.ts +24 -0
  256. package/tests/extensions/shared-memory-services.test.ts +309 -0
  257. package/tests/extensions/vector-extension-boundary.test.ts +21 -0
  258. package/.claude/settings.local.json +0 -25
  259. package/.npm-cache/_cacache/content-v2/sha512/04/76/c098f88dfe584a2b80870bff7421b05d17d3d9ee1027f77772332a22d3f93a9a57101a2855107f6ad82077a818bba912b2bc317f2361b5ddb09ad284d9ce +0 -0
  260. package/.npm-cache/_cacache/content-v2/sha512/60/25/d2ecd39cfc7cab58351162814be77f935c6d6491c10c3745d456da7ddb2117ffd90c10e53fe3c0f1ed16b403307841543634504398b16ee4e6b6dd8e0c45 +0 -0
  261. package/.npm-cache/_cacache/index-v5/2b/9a/7f8f40206ed8a2e0a84efaa953ccaed1f5d001e14b931083f2e7a0738007 +0 -2
  262. package/.npm-cache/_cacache/index-v5/2e/d9/fcfa5c6a6abdc2a3644ab84a95936047298c465a2f47ee03db8f7fe1e946 +0 -3
  263. package/.npm-cache/_cacache/index-v5/a9/42/e519633356d12d3d2f19da66a8301016d496c8f5c3e0554124aaa62dc043 +0 -2
  264. package/.npm-cache/_logs/2026-02-26T12_04_52_729Z-debug-0.log +0 -256
  265. package/.npm-cache/_logs/2026-02-26T12_05_36_835Z-debug-0.log +0 -18
  266. package/.npm-cache/_logs/2026-02-26T12_05_45_982Z-debug-0.log +0 -32
  267. package/.npm-cache/_logs/2026-02-26T12_05_48_515Z-debug-0.log +0 -260
  268. package/.npm-cache/_logs/2026-02-26T12_05_53_567Z-debug-0.log +0 -69
  269. package/.npm-cache/_update-notifier-last-checked +0 -0
  270. package/bootstrap-kb/decisions/decisions.md +0 -244
  271. package/bootstrap-kb/glossary/glossary.md +0 -46
  272. package/bootstrap-kb/modules/.claude-plugin.md +0 -22
  273. package/bootstrap-kb/modules/agents.md.md +0 -15
  274. package/bootstrap-kb/modules/claude.md.md +0 -15
  275. package/bootstrap-kb/modules/context.md.md +0 -15
  276. package/bootstrap-kb/modules/docs.md +0 -18
  277. package/bootstrap-kb/modules/handoff.md.md +0 -15
  278. package/bootstrap-kb/modules/package-lock.json.md +0 -15
  279. package/bootstrap-kb/modules/package.json.md +0 -15
  280. package/bootstrap-kb/modules/plan.md.md +0 -15
  281. package/bootstrap-kb/modules/readme.md.md +0 -15
  282. package/bootstrap-kb/modules/scripts.md +0 -26
  283. package/bootstrap-kb/modules/spec.md.md +0 -15
  284. package/bootstrap-kb/modules/specs.md +0 -20
  285. package/bootstrap-kb/modules/src.md +0 -51
  286. package/bootstrap-kb/modules/tests.md +0 -42
  287. package/bootstrap-kb/modules/tsconfig.json.md +0 -15
  288. package/bootstrap-kb/modules/vitest.config.ts.md +0 -15
  289. package/bootstrap-kb/overview/overview.md +0 -40
  290. package/bootstrap-kb/sources/manifest.json +0 -950
  291. package/bootstrap-kb/sources/manifest.md +0 -227
  292. package/bootstrap-kb/timeline/timeline.md +0 -57
  293. package/claude-memory-layer-1.0.14.tgz +0 -0
  294. package/d.sh +0 -3
  295. package/deploy.sh +0 -3
  296. package/dist/ui/app.js +0 -2101
  297. package/memory/.claude-plugin/commands/2026-02-25.md +0 -263
  298. package/memory/_index.md +0 -419
  299. package/memory/agent_response/uncategorized/2026-02-26.md +0 -176
  300. package/memory/agent_response/uncategorized/2026-03-03.md +0 -14
  301. package/memory/agent_response/uncategorized/2026-03-04.md +0 -1421
  302. package/memory/agent_response/uncategorized/2026-03-05.md +0 -157
  303. package/memory/default/uncategorized/2026-02-25.md +0 -4839
  304. package/memory/session_summary/uncategorized/2026-02-26.md +0 -13
  305. package/memory/session_summary/uncategorized/2026-03-03.md +0 -5
  306. package/memory/session_summary/uncategorized/2026-03-04.md +0 -50
  307. package/memory/specs/20260207-dashboard-upgrade/2026-02-25.md +0 -142
  308. package/memory/specs/citations-system/2026-02-25.md +0 -1121
  309. package/memory/specs/endless-mode/2026-02-25.md +0 -1392
  310. package/memory/specs/entity-edge-model/2026-02-25.md +0 -1263
  311. package/memory/specs/evidence-aligner-v2/2026-02-25.md +0 -1028
  312. package/memory/specs/mcp-desktop-integration/2026-02-25.md +0 -1334
  313. package/memory/specs/post-tool-use-hook/2026-02-25.md +0 -1164
  314. package/memory/specs/private-tags/2026-02-25.md +0 -1057
  315. package/memory/specs/progressive-disclosure/2026-02-25.md +0 -1436
  316. package/memory/specs/task-entity-system/2026-02-25.md +0 -924
  317. package/memory/specs/vector-outbox-v2/2026-02-25.md +0 -1510
  318. package/memory/specs/web-viewer-ui/2026-02-25.md +0 -1709
  319. package/memory/tool_observation/uncategorized/2026-02-26.md +0 -209
  320. package/memory/tool_observation/uncategorized/2026-03-03.md +0 -21
  321. package/memory/tool_observation/uncategorized/2026-03-04.md +0 -1033
  322. package/memory/tool_observation/uncategorized/2026-03-05.md +0 -33
  323. package/memory/user_prompt/uncategorized/2026-02-26.md +0 -25
  324. package/memory/user_prompt/uncategorized/2026-03-04.md +0 -634
  325. package/memory/user_prompt/uncategorized/2026-03-05.md +0 -6
  326. package/specs/optional-duckdb/context.md +0 -77
  327. package/specs/optional-duckdb/plan.md +0 -142
  328. package/specs/optional-duckdb/spec.md +0 -35
  329. package/src/ui/app.js +0 -2101
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Memory Engine Services Bundle
3
+ *
4
+ * Owns construction and wiring for storage-backed engine services so
5
+ * MemoryService can stay focused on public facade/lifecycle behavior.
6
+ */
7
+
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+
11
+ import { Embedder, getDefaultEmbedder } from '../embedder.js';
12
+ import type { EventStore } from '../event-store.js';
13
+ import { createGraduationPipeline, type GraduationPipeline } from '../graduation.js';
14
+ import { getDefaultMatcher, type Matcher } from '../matcher.js';
15
+ import { MarkdownMirror } from '../md-mirror.js';
16
+ import type { Retriever } from '../retriever.js';
17
+ import { SQLiteEventStore } from '../sqlite-event-store.js';
18
+ import type { ToolObservationPayload } from '../types.js';
19
+ import { VectorStore } from '../vector-store.js';
20
+ import { MemoryIngestService } from './memory-ingest-service.js';
21
+ import { MemoryQueryService } from './memory-query-service.js';
22
+ import {
23
+ createRetrievalServices,
24
+ type RetrievalAnalyticsService,
25
+ type RetrievalDisclosureService,
26
+ type RetrievalDisclosureSharedStore,
27
+ type RetrievalEventStore,
28
+ type RetrievalOrchestrator,
29
+ type RetrievalServices,
30
+ type RetrievalServicesDeps
31
+ } from './retrieval-services.js';
32
+
33
+ export interface MemoryEngineServicesOptions {
34
+ storagePath: string;
35
+ readOnly: boolean;
36
+ embeddingModel?: string;
37
+ cwd?: string;
38
+ initialize: () => Promise<void>;
39
+ getProjectHash: () => string | null;
40
+ getProjectPath?: () => string | null;
41
+ hasSharedStore: () => boolean;
42
+ sharedStore?: RetrievalDisclosureSharedStore;
43
+ createToolObservationEmbedding: (payload: ToolObservationPayload) => string;
44
+ factories?: MemoryEngineServicesFactories;
45
+ }
46
+
47
+ export interface MemoryEngineServicesFactories {
48
+ createSQLiteEventStore?: (
49
+ dbPath: string,
50
+ options: { readonly: boolean; markdownMirrorRoot: string }
51
+ ) => SQLiteEventStore;
52
+ createVectorStore?: (vectorsPath: string) => VectorStore;
53
+ createEmbedder?: (model: string) => Embedder;
54
+ getDefaultEmbedder?: () => Embedder;
55
+ getDefaultMatcher?: () => Matcher;
56
+ createMarkdownMirror?: (cwd: string) => MarkdownMirror;
57
+ createGraduationPipeline?: (eventStore: EventStore) => GraduationPipeline;
58
+ createRetrievalServices?: (deps: RetrievalServicesDeps) => RetrievalServices;
59
+ }
60
+
61
+ export interface MemoryEngineServices {
62
+ storagePath: string;
63
+ sqliteStore: SQLiteEventStore;
64
+ vectorStore: VectorStore;
65
+ embedder: Embedder;
66
+ matcher: Matcher;
67
+ retriever: Retriever;
68
+ retrievalOrchestrator: RetrievalOrchestrator;
69
+ retrievalDisclosureService: RetrievalDisclosureService;
70
+ retrievalAnalyticsService: RetrievalAnalyticsService;
71
+ graduation: GraduationPipeline;
72
+ mdMirror: MarkdownMirror;
73
+ ingestService: MemoryIngestService;
74
+ queryService: MemoryQueryService;
75
+ }
76
+
77
+ export function createMemoryEngineServices(options: MemoryEngineServicesOptions): MemoryEngineServices {
78
+ const factories = options.factories ?? {};
79
+ const storagePath = options.storagePath;
80
+
81
+ if (!options.readOnly && !fs.existsSync(storagePath)) {
82
+ fs.mkdirSync(storagePath, { recursive: true });
83
+ }
84
+
85
+ const sqliteStore = (factories.createSQLiteEventStore ?? defaultCreateSQLiteEventStore)(
86
+ path.join(storagePath, 'events.sqlite'),
87
+ {
88
+ readonly: options.readOnly,
89
+ markdownMirrorRoot: storagePath
90
+ }
91
+ );
92
+ const vectorStore = (factories.createVectorStore ?? defaultCreateVectorStore)(
93
+ path.join(storagePath, 'vectors')
94
+ );
95
+ const embeddingModel = options.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
96
+ const embedder = embeddingModel
97
+ ? (factories.createEmbedder ?? defaultCreateEmbedder)(embeddingModel)
98
+ : (factories.getDefaultEmbedder ?? getDefaultEmbedder)();
99
+ const matcher = (factories.getDefaultMatcher ?? getDefaultMatcher)();
100
+ const mdMirror = (factories.createMarkdownMirror ?? defaultCreateMarkdownMirror)(
101
+ options.cwd ?? process.cwd()
102
+ );
103
+ const graduation = (factories.createGraduationPipeline ?? defaultCreateGraduationPipeline)(
104
+ sqliteStore as unknown as EventStore
105
+ );
106
+
107
+ const retrievalServices = (factories.createRetrievalServices ?? createRetrievalServices)({
108
+ initialize: options.initialize,
109
+ eventStore: sqliteStore as unknown as RetrievalEventStore,
110
+ vectorStore,
111
+ embedder,
112
+ matcher,
113
+ getProjectHash: options.getProjectHash,
114
+ hasSharedStore: options.hasSharedStore,
115
+ sharedStore: options.sharedStore
116
+ });
117
+
118
+ const ingestService = new MemoryIngestService({
119
+ initialize: options.initialize,
120
+ eventStore: sqliteStore,
121
+ markdownMirror: mdMirror,
122
+ createToolEmbedding: options.createToolObservationEmbedding,
123
+ getProjectHash: options.getProjectHash,
124
+ getProjectPath: options.getProjectPath
125
+ });
126
+ const queryService = new MemoryQueryService(
127
+ () => sqliteStore.initialize(),
128
+ sqliteStore,
129
+ { vectorStore, graduation }
130
+ );
131
+
132
+ return {
133
+ storagePath,
134
+ sqliteStore,
135
+ vectorStore,
136
+ embedder,
137
+ matcher,
138
+ retriever: retrievalServices.retriever,
139
+ retrievalOrchestrator: retrievalServices.retrievalOrchestrator,
140
+ retrievalDisclosureService: retrievalServices.retrievalDisclosureService,
141
+ retrievalAnalyticsService: retrievalServices.retrievalAnalyticsService,
142
+ graduation,
143
+ mdMirror,
144
+ ingestService,
145
+ queryService
146
+ };
147
+ }
148
+
149
+ function defaultCreateSQLiteEventStore(
150
+ dbPath: string,
151
+ options: { readonly: boolean; markdownMirrorRoot: string }
152
+ ): SQLiteEventStore {
153
+ return new SQLiteEventStore(dbPath, options);
154
+ }
155
+
156
+ function defaultCreateVectorStore(vectorsPath: string): VectorStore {
157
+ return new VectorStore(vectorsPath);
158
+ }
159
+
160
+ function defaultCreateEmbedder(model: string): Embedder {
161
+ return new Embedder(model);
162
+ }
163
+
164
+ function defaultCreateMarkdownMirror(cwd: string): MarkdownMirror {
165
+ return new MarkdownMirror(cwd);
166
+ }
167
+
168
+ function defaultCreateGraduationPipeline(eventStore: EventStore): GraduationPipeline {
169
+ return createGraduationPipeline(eventStore);
170
+ }
@@ -0,0 +1,317 @@
1
+ import {
2
+ IngestInterceptor,
3
+ IngestInterceptorRegistry,
4
+ mergeHierarchicalMetadata
5
+ } from '../ingest-interceptor.js';
6
+ import { normalizeTags } from '../tag-taxonomy.js';
7
+ import { createSummaryDeriver, type SummaryDeriver } from '../derive/summary-deriver.js';
8
+ import type { AppendResult, MemoryEvent, MemoryEventInput, ToolObservationPayload } from '../types.js';
9
+
10
+ interface SessionRecord {
11
+ id: string;
12
+ startedAt?: Date;
13
+ endedAt?: Date;
14
+ projectPath?: string;
15
+ summary?: string;
16
+ }
17
+
18
+ interface SessionUpsertStore {
19
+ upsertSession(session: SessionRecord): Promise<void>;
20
+ getSessionEvents(sessionId: string): Promise<MemoryEvent[]>;
21
+ getSessionsWithoutSummary(currentSessionId: string, limit?: number): Promise<string[]>;
22
+ }
23
+
24
+ interface IngestEventStore extends SessionUpsertStore {
25
+ append(input: MemoryEventInput): Promise<AppendResult>;
26
+ enqueueForEmbedding(eventId: string, content: string): Promise<unknown>;
27
+ }
28
+
29
+ interface IngestMarkdownMirror {
30
+ append(event: MemoryEventInput, eventId?: string): Promise<void>;
31
+ }
32
+
33
+ export type IngestOperation = 'user_prompt' | 'agent_response' | 'session_summary' | 'tool_observation';
34
+
35
+ export interface MemoryIngestServiceOptions {
36
+ initialize: () => Promise<void>;
37
+ eventStore: IngestEventStore;
38
+ markdownMirror: IngestMarkdownMirror;
39
+ createToolEmbedding: (payload: ToolObservationPayload) => string;
40
+ getProjectHash?: () => string | null;
41
+ getProjectPath?: () => string | null;
42
+ summaryDeriver?: SummaryDeriver;
43
+ }
44
+
45
+ /**
46
+ * Thin-core ingest service for session lifecycle and event writes.
47
+ *
48
+ * Owns the ingest normalization/interceptor/append pipeline so the public
49
+ * MemoryService facade can delegate ingest behavior without coordinating
50
+ * storage-side effects itself.
51
+ */
52
+ export class MemoryIngestService {
53
+ private readonly initialize: () => Promise<void>;
54
+ private readonly eventStore: IngestEventStore;
55
+ private readonly markdownMirror: IngestMarkdownMirror;
56
+ private readonly createToolEmbedding: (payload: ToolObservationPayload) => string;
57
+ private readonly getProjectHash: () => string | null;
58
+ private readonly getProjectPath: () => string | null;
59
+ private readonly summaryDeriver: SummaryDeriver;
60
+ private readonly ingestInterceptors = new IngestInterceptorRegistry();
61
+
62
+ constructor(options: MemoryIngestServiceOptions) {
63
+ this.initialize = options.initialize;
64
+ this.eventStore = options.eventStore;
65
+ this.markdownMirror = options.markdownMirror;
66
+ this.createToolEmbedding = options.createToolEmbedding;
67
+ this.getProjectHash = options.getProjectHash ?? (() => null);
68
+ this.getProjectPath = options.getProjectPath ?? (() => null);
69
+ this.summaryDeriver = options.summaryDeriver ?? createSummaryDeriver();
70
+ }
71
+
72
+ registerIngestBefore(interceptor: IngestInterceptor): () => void {
73
+ return this.ingestInterceptors.registerBefore(interceptor);
74
+ }
75
+
76
+ registerIngestAfter(interceptor: IngestInterceptor): () => void {
77
+ return this.ingestInterceptors.registerAfter(interceptor);
78
+ }
79
+
80
+ registerIngestOnError(interceptor: IngestInterceptor): () => void {
81
+ return this.ingestInterceptors.registerOnError(interceptor);
82
+ }
83
+
84
+ async startSession(sessionId: string, projectPath?: string): Promise<void> {
85
+ await this.initialize();
86
+
87
+ await this.eventStore.upsertSession({
88
+ id: sessionId,
89
+ startedAt: new Date(),
90
+ projectPath
91
+ });
92
+ }
93
+
94
+ async endSession(sessionId: string, summary?: string): Promise<void> {
95
+ await this.initialize();
96
+
97
+ await this.eventStore.upsertSession({
98
+ id: sessionId,
99
+ endedAt: new Date(),
100
+ summary
101
+ });
102
+ }
103
+
104
+ async storeUserPrompt(
105
+ sessionId: string,
106
+ content: string,
107
+ metadata?: Record<string, unknown>
108
+ ): Promise<AppendResult> {
109
+ return this.ingestEvent({
110
+ operation: 'user_prompt',
111
+ input: {
112
+ eventType: 'user_prompt',
113
+ sessionId,
114
+ timestamp: new Date(),
115
+ content,
116
+ metadata
117
+ },
118
+ embeddingContent: content
119
+ });
120
+ }
121
+
122
+ async storeAgentResponse(
123
+ sessionId: string,
124
+ content: string,
125
+ metadata?: Record<string, unknown>
126
+ ): Promise<AppendResult> {
127
+ return this.ingestEvent({
128
+ operation: 'agent_response',
129
+ input: {
130
+ eventType: 'agent_response',
131
+ sessionId,
132
+ timestamp: new Date(),
133
+ content,
134
+ metadata
135
+ },
136
+ embeddingContent: content
137
+ });
138
+ }
139
+
140
+ async storeSessionSummary(
141
+ sessionId: string,
142
+ summary: string,
143
+ metadata?: Record<string, unknown>
144
+ ): Promise<AppendResult> {
145
+ return this.ingestEvent({
146
+ operation: 'session_summary',
147
+ input: {
148
+ eventType: 'session_summary',
149
+ sessionId,
150
+ timestamp: new Date(),
151
+ content: summary,
152
+ metadata
153
+ },
154
+ embeddingContent: summary
155
+ });
156
+ }
157
+
158
+ /**
159
+ * Backfill session summaries for recent sessions that are missing them.
160
+ * Called from session-start hook to catch sessions that ended without Stop hook.
161
+ */
162
+ async backfillMissingSummaries(currentSessionId: string, limit = 5): Promise<void> {
163
+ await this.initialize();
164
+
165
+ const recentSessionIds = await this.eventStore.getSessionsWithoutSummary(currentSessionId, limit);
166
+ for (const sessionId of recentSessionIds) {
167
+ try {
168
+ await this.generateSessionSummary(sessionId);
169
+ } catch {
170
+ // non-critical backfill path
171
+ }
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Generate a rule-based session summary from stored events.
177
+ * Skips short sessions and sessions that already contain a summary event.
178
+ */
179
+ async generateSessionSummary(sessionId: string): Promise<void> {
180
+ await this.initialize();
181
+
182
+ const events = await this.eventStore.getSessionEvents(sessionId);
183
+ const summary = this.summaryDeriver.deriveSessionSummary(events);
184
+ if (!summary) return;
185
+
186
+ await this.storeSessionSummary(sessionId, summary.text, summary.metadata);
187
+ }
188
+
189
+ async storeToolObservation(
190
+ sessionId: string,
191
+ payload: ToolObservationPayload
192
+ ): Promise<AppendResult> {
193
+ const content = JSON.stringify(payload);
194
+ const turnId = (payload.metadata as Record<string, unknown> | undefined)?.turnId;
195
+
196
+ return this.ingestEvent({
197
+ operation: 'tool_observation',
198
+ input: {
199
+ eventType: 'tool_observation',
200
+ sessionId,
201
+ timestamp: new Date(),
202
+ content,
203
+ metadata: {
204
+ toolName: payload.toolName,
205
+ success: payload.success,
206
+ ...(typeof turnId === 'string' && turnId.length > 0 ? { turnId } : {})
207
+ }
208
+ },
209
+ embeddingContent: this.createToolEmbedding(payload)
210
+ });
211
+ }
212
+
213
+ private async ingestEvent(options: {
214
+ operation: IngestOperation;
215
+ input: MemoryEventInput;
216
+ embeddingContent?: string;
217
+ }): Promise<AppendResult> {
218
+ const normalizedInput = this.normalizeInput(options.operation, options.input);
219
+
220
+ await this.ingestInterceptors.run('before', {
221
+ operation: options.operation,
222
+ sessionId: normalizedInput.sessionId,
223
+ event: normalizedInput
224
+ });
225
+
226
+ try {
227
+ const result = await this.eventStore.append(normalizedInput);
228
+ if (result.success === false) {
229
+ await this.ingestInterceptors.run('error', {
230
+ operation: options.operation,
231
+ sessionId: normalizedInput.sessionId,
232
+ event: normalizedInput,
233
+ error: new Error(result.error)
234
+ });
235
+ return result;
236
+ }
237
+
238
+ if (!result.isDuplicate) {
239
+ if (options.embeddingContent) {
240
+ await this.eventStore.enqueueForEmbedding(result.eventId, options.embeddingContent);
241
+ }
242
+ try {
243
+ await this.markdownMirror.append(normalizedInput, result.eventId);
244
+ } catch {
245
+ // non-breaking markdown mirror write
246
+ }
247
+ }
248
+
249
+ await this.ingestInterceptors.run('after', {
250
+ operation: options.operation,
251
+ sessionId: normalizedInput.sessionId,
252
+ event: normalizedInput
253
+ });
254
+
255
+ return result;
256
+ } catch (error) {
257
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
258
+ await this.ingestInterceptors.run('error', {
259
+ operation: options.operation,
260
+ sessionId: normalizedInput.sessionId,
261
+ event: normalizedInput,
262
+ error: normalizedError
263
+ });
264
+ throw error;
265
+ }
266
+ }
267
+
268
+ private normalizeInput(operation: IngestOperation, input: MemoryEventInput): MemoryEventInput {
269
+ const projectHash = this.getProjectHash();
270
+ const projectPath = this.getProjectPath();
271
+ const normalizedInput: MemoryEventInput = {
272
+ ...input,
273
+ metadata: mergeHierarchicalMetadata(
274
+ {
275
+ ingest: {
276
+ operation,
277
+ pipeline: 'default',
278
+ ts: new Date().toISOString()
279
+ },
280
+ ...(projectHash
281
+ ? {
282
+ scope: {
283
+ project: {
284
+ hash: projectHash,
285
+ ...(projectPath ? { path: projectPath } : {})
286
+ }
287
+ },
288
+ tags: [`proj:${projectHash}`]
289
+ }
290
+ : {})
291
+ },
292
+ input.metadata
293
+ )
294
+ };
295
+
296
+ if (projectHash && normalizedInput.metadata) {
297
+ const meta = normalizedInput.metadata as Record<string, unknown>;
298
+ const currentTags = Array.isArray(meta.tags)
299
+ ? meta.tags.filter((x): x is string => typeof x === 'string')
300
+ : [];
301
+ const projectTag = `proj:${projectHash}`;
302
+ if (!currentTags.includes(projectTag)) {
303
+ meta.tags = [...currentTags, projectTag];
304
+ }
305
+ }
306
+
307
+ if (normalizedInput.metadata) {
308
+ const meta = normalizedInput.metadata as Record<string, unknown>;
309
+ const normalizedTags = normalizeTags(meta.tags);
310
+ if (normalizedTags.length > 0) {
311
+ meta.tags = normalizedTags;
312
+ }
313
+ }
314
+
315
+ return normalizedInput;
316
+ }
317
+ }
@@ -0,0 +1,173 @@
1
+ import type { MemoryEvent } from '../types.js';
2
+
3
+ interface RankedKeywordResult {
4
+ event: MemoryEvent;
5
+ rank: number;
6
+ }
7
+
8
+ export interface MemorySessionTurn {
9
+ turnId: string;
10
+ events: MemoryEvent[];
11
+ startedAt: Date;
12
+ promptPreview: string;
13
+ eventCount: number;
14
+ toolCount: number;
15
+ hasResponse: boolean;
16
+ }
17
+
18
+ export interface MemoryOutboxStats {
19
+ embedding: { pending: number; processing: number; failed: number; total: number };
20
+ vector: { pending: number; processing: number; failed: number; total: number };
21
+ }
22
+
23
+ export interface MemoryStats {
24
+ totalEvents: number;
25
+ vectorCount: number;
26
+ levelStats: Array<{ level: string; count: number }>;
27
+ }
28
+
29
+ interface QueryStore {
30
+ keywordSearch(query: string, topK: number): Promise<RankedKeywordResult[]>;
31
+ getSessionEvents(sessionId: string): Promise<MemoryEvent[]>;
32
+ getRecentEvents(limit: number): Promise<MemoryEvent[]>;
33
+ }
34
+
35
+ interface QueryMaintenanceStore extends QueryStore {
36
+ rebuildFtsIndex(): Promise<number>;
37
+ getOutboxStats(): Promise<MemoryOutboxStats>;
38
+ getEventsByLevel(level: string, options?: { limit?: number; offset?: number }): Promise<MemoryEvent[]>;
39
+ getEventLevel(eventId: string): Promise<string | null>;
40
+ getSessionTurns(sessionId: string, options?: { limit?: number; offset?: number }): Promise<MemorySessionTurn[]>;
41
+ getEventsByTurn(turnId: string): Promise<MemoryEvent[]>;
42
+ countSessionTurns(sessionId: string): Promise<number>;
43
+ backfillTurnIds(): Promise<number>;
44
+ deleteSessionEvents(sessionId: string): Promise<number>;
45
+ }
46
+
47
+ interface MemoryQueryServiceDeps {
48
+ vectorStore: { count(): Promise<number> };
49
+ graduation: { getStats(): Promise<Array<{ level: string; count: number }>> };
50
+ }
51
+
52
+ /**
53
+ * Thin-core query service for lightweight read and maintenance paths.
54
+ *
55
+ * Higher-level retrieval orchestration lives in RetrievalOrchestrator;
56
+ * this service keeps storage-backed read models and maintenance delegates separate.
57
+ */
58
+ export class MemoryQueryService {
59
+ constructor(
60
+ private readonly initialize: () => Promise<void>,
61
+ private readonly queryStore: QueryStore,
62
+ private readonly deps?: MemoryQueryServiceDeps
63
+ ) {}
64
+
65
+ async keywordSearch(
66
+ query: string,
67
+ options?: { topK?: number; minScore?: number }
68
+ ): Promise<Array<{ event: MemoryEvent; score: number }>> {
69
+ await this.initialize();
70
+
71
+ const results = await this.queryStore.keywordSearch(query, options?.topK ?? 10);
72
+ if (results.length === 0) return [];
73
+
74
+ const maxRank = Math.min(...results.map((r) => r.rank), -0.001);
75
+ const minRank = Math.max(...results.map((r) => r.rank), -1000);
76
+ const rankRange = maxRank - minRank || 1;
77
+
78
+ return results
79
+ .map((r) => ({
80
+ event: r.event,
81
+ score: 1 - (r.rank - minRank) / rankRange
82
+ }))
83
+ .filter((r) => !options?.minScore || r.score >= options.minScore);
84
+ }
85
+
86
+ async getSessionHistory(sessionId: string): Promise<MemoryEvent[]> {
87
+ await this.initialize();
88
+ return this.queryStore.getSessionEvents(sessionId);
89
+ }
90
+
91
+ async getRecentEvents(limit: number = 100): Promise<MemoryEvent[]> {
92
+ await this.initialize();
93
+ return this.queryStore.getRecentEvents(limit);
94
+ }
95
+
96
+ async rebuildFtsIndex(): Promise<number> {
97
+ await this.initialize();
98
+ return this.getMaintenanceStore('rebuildFtsIndex').rebuildFtsIndex();
99
+ }
100
+
101
+ async getOutboxStats(): Promise<MemoryOutboxStats> {
102
+ await this.initialize();
103
+ return this.getMaintenanceStore('getOutboxStats').getOutboxStats();
104
+ }
105
+
106
+ async getStats(): Promise<MemoryStats> {
107
+ await this.initialize();
108
+
109
+ const deps = this.getStatsDeps();
110
+ const recentEvents = await this.queryStore.getRecentEvents(10000);
111
+ const vectorCount = await deps.vectorStore.count();
112
+ const levelStats = await deps.graduation.getStats();
113
+
114
+ return {
115
+ totalEvents: recentEvents.length,
116
+ vectorCount,
117
+ levelStats
118
+ };
119
+ }
120
+
121
+ async getEventsByLevel(level: string, options?: { limit?: number; offset?: number }): Promise<MemoryEvent[]> {
122
+ await this.initialize();
123
+ return this.getMaintenanceStore('getEventsByLevel').getEventsByLevel(level, options);
124
+ }
125
+
126
+ async getEventLevel(eventId: string): Promise<string | null> {
127
+ await this.initialize();
128
+ return this.getMaintenanceStore('getEventLevel').getEventLevel(eventId);
129
+ }
130
+
131
+ async getSessionTurns(
132
+ sessionId: string,
133
+ options?: { limit?: number; offset?: number }
134
+ ): Promise<MemorySessionTurn[]> {
135
+ await this.initialize();
136
+ return this.getMaintenanceStore('getSessionTurns').getSessionTurns(sessionId, options);
137
+ }
138
+
139
+ async getEventsByTurn(turnId: string): Promise<MemoryEvent[]> {
140
+ await this.initialize();
141
+ return this.getMaintenanceStore('getEventsByTurn').getEventsByTurn(turnId);
142
+ }
143
+
144
+ async countSessionTurns(sessionId: string): Promise<number> {
145
+ await this.initialize();
146
+ return this.getMaintenanceStore('countSessionTurns').countSessionTurns(sessionId);
147
+ }
148
+
149
+ async backfillTurnIds(): Promise<number> {
150
+ await this.initialize();
151
+ return this.getMaintenanceStore('backfillTurnIds').backfillTurnIds();
152
+ }
153
+
154
+ async deleteSessionEvents(sessionId: string): Promise<number> {
155
+ await this.initialize();
156
+ return this.getMaintenanceStore('deleteSessionEvents').deleteSessionEvents(sessionId);
157
+ }
158
+
159
+ private getMaintenanceStore(method: keyof QueryMaintenanceStore): QueryMaintenanceStore {
160
+ const store = this.queryStore as QueryStore & Partial<QueryMaintenanceStore>;
161
+ if (typeof store[method] !== 'function') {
162
+ throw new Error(`MemoryQueryService requires queryStore.${String(method)}() for this operation`);
163
+ }
164
+ return store as QueryMaintenanceStore;
165
+ }
166
+
167
+ private getStatsDeps(): MemoryQueryServiceDeps {
168
+ if (!this.deps) {
169
+ throw new Error('MemoryQueryService requires vectorStore and graduation dependencies for getStats()');
170
+ }
171
+ return this.deps;
172
+ }
173
+ }