claude-memory-layer 1.0.27 → 1.0.29

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 (331) hide show
  1. package/.env.example +7 -0
  2. package/AGENTS.md +11 -0
  3. package/README.md +374 -49
  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 +12 -5
  56. package/scripts/build.ts +25 -8
  57. package/scripts/generate-session-qrels.ts +126 -0
  58. package/scripts/postinstall-embedding-backend.cjs +142 -0
  59. package/scripts/replay-retrieval-benchmark.ts +69 -0
  60. package/specs/thin-core-refactor/context.md +275 -0
  61. package/specs/thin-core-refactor/plan.md +536 -0
  62. package/specs/thin-core-refactor/spec.md +465 -0
  63. package/src/adapters/claude/capture/index.ts +3 -0
  64. package/src/adapters/claude/context/index.ts +3 -0
  65. package/src/adapters/claude/hooks/index.ts +21 -0
  66. package/src/adapters/claude/hooks/post-tool-use.ts +239 -0
  67. package/src/adapters/claude/hooks/prompt-injection-policy.ts +104 -0
  68. package/src/adapters/claude/hooks/semantic-daemon-client.ts +209 -0
  69. package/src/adapters/claude/hooks/semantic-daemon.ts +283 -0
  70. package/src/adapters/claude/hooks/session-end.ts +59 -0
  71. package/src/adapters/claude/hooks/session-start.ts +73 -0
  72. package/src/adapters/claude/hooks/stop.ts +128 -0
  73. package/src/adapters/claude/hooks/user-prompt-submit.ts +361 -0
  74. package/src/adapters/claude/index.ts +4 -0
  75. package/src/adapters/claude/transcript/index.ts +4 -0
  76. package/src/adapters/claude/transcript/transcript-reader.ts +57 -0
  77. package/src/adapters/claude/transcript/turn-reconstructor.ts +65 -0
  78. package/src/apps/cli/claude-settings-hooks.ts +138 -0
  79. package/src/apps/cli/codex-import-runner.ts +125 -0
  80. package/src/apps/cli/codex-validation-output.ts +95 -0
  81. package/src/apps/cli/hermes-import-runner.ts +130 -0
  82. package/src/apps/cli/hermes-validation-output.ts +91 -0
  83. package/src/apps/cli/index.ts +1731 -0
  84. package/src/apps/cli/mcp-install.ts +106 -0
  85. package/src/apps/cli/retrieval-disclosure-output.ts +196 -0
  86. package/src/apps/dashboard/assets/js/bootstrap.js +244 -0
  87. package/src/apps/dashboard/assets/js/chat.js +373 -0
  88. package/src/apps/dashboard/assets/js/disclosure.js +232 -0
  89. package/src/apps/dashboard/assets/js/modals.js +298 -0
  90. package/src/apps/dashboard/assets/js/overview.js +655 -0
  91. package/src/apps/dashboard/assets/js/state.js +72 -0
  92. package/src/apps/dashboard/assets/js/views.js +468 -0
  93. package/src/{ui → apps/dashboard}/index.html +43 -1
  94. package/src/apps/dashboard/index.ts +3 -0
  95. package/src/{ui → apps/dashboard}/style.css +222 -0
  96. package/src/apps/index.ts +5 -0
  97. package/src/apps/server/api/chat.ts +244 -0
  98. package/src/apps/server/api/citations.ts +105 -0
  99. package/src/apps/server/api/events.ts +137 -0
  100. package/src/apps/server/api/health.ts +53 -0
  101. package/src/apps/server/api/index.ts +26 -0
  102. package/src/apps/server/api/projects.ts +74 -0
  103. package/src/apps/server/api/search.ts +184 -0
  104. package/src/apps/server/api/sessions.ts +115 -0
  105. package/src/apps/server/api/stats.ts +723 -0
  106. package/src/apps/server/api/turns.ts +143 -0
  107. package/src/apps/server/api/utils.ts +65 -0
  108. package/src/apps/server/index.ts +111 -0
  109. package/src/cli/index.ts +2 -1311
  110. package/src/cli/retrieval-disclosure-output.ts +2 -0
  111. package/src/compat/index.ts +5 -0
  112. package/src/core/derive/fact-deriver.ts +170 -0
  113. package/src/core/derive/index.ts +2 -0
  114. package/src/core/derive/summary-deriver.ts +76 -0
  115. package/src/core/embedder.ts +4 -152
  116. package/src/core/engine/embedding-maintenance-service.ts +187 -0
  117. package/src/core/engine/endless-memory-services.ts +4 -0
  118. package/src/core/engine/index.ts +19 -0
  119. package/src/core/engine/memory-engine-services.ts +170 -0
  120. package/src/core/engine/memory-ingest-service.ts +317 -0
  121. package/src/core/engine/memory-query-service.ts +173 -0
  122. package/src/core/engine/memory-runtime-service.ts +162 -0
  123. package/src/core/engine/memory-service-composition.ts +231 -0
  124. package/src/core/engine/retrieval-analytics-service.ts +181 -0
  125. package/src/core/engine/retrieval-disclosure-service.ts +420 -0
  126. package/src/core/engine/retrieval-orchestrator.ts +377 -0
  127. package/src/core/engine/retrieval-services.ts +176 -0
  128. package/src/core/engine/shared-memory-services.ts +4 -0
  129. package/src/core/entity-repo.ts +1 -3
  130. package/src/core/event-store.ts +3 -3
  131. package/src/core/evidence-aligner.ts +2 -2
  132. package/src/core/external-market-context.ts +582 -0
  133. package/src/core/graduation.ts +2 -3
  134. package/src/core/index.ts +21 -0
  135. package/src/core/matcher.ts +2 -4
  136. package/src/core/model/memory-fact.ts +30 -0
  137. package/src/core/model/memory-rule.ts +14 -0
  138. package/src/core/model/memory-summary.ts +21 -0
  139. package/src/core/model/raw-event.ts +28 -0
  140. package/src/core/model/retrieval-result.ts +35 -0
  141. package/src/core/privacy/filter.ts +21 -10
  142. package/src/core/product-validation-matrix.ts +314 -0
  143. package/src/core/progressive-retriever.ts +1 -2
  144. package/src/core/registry/project-path.ts +54 -0
  145. package/src/core/registry/session-registry.ts +69 -0
  146. package/src/core/replay-evaluator.ts +625 -0
  147. package/src/core/retrieval-benchmark.ts +117 -0
  148. package/src/core/retrieval-quality.ts +109 -0
  149. package/src/core/retriever.ts +53 -15
  150. package/src/core/session-qrels.ts +360 -0
  151. package/src/core/shared-event-store.ts +1 -1
  152. package/src/core/sqlite-event-store.ts +35 -11
  153. package/src/core/task/blocker-resolver.ts +2 -2
  154. package/src/core/task/task-resolver.ts +0 -1
  155. package/src/core/vector-outbox.ts +1 -10
  156. package/src/core/vector-worker.ts +1 -1
  157. package/src/extensions/endless-memory/endless-memory-services.ts +350 -0
  158. package/src/extensions/endless-memory/index.ts +1 -0
  159. package/src/extensions/index.ts +5 -0
  160. package/src/extensions/mcp/handlers.ts +960 -0
  161. package/src/extensions/mcp/index.ts +48 -0
  162. package/src/extensions/mcp/tools.ts +252 -0
  163. package/src/extensions/shared-memory/index.ts +1 -0
  164. package/src/extensions/shared-memory/shared-memory-services.ts +211 -0
  165. package/src/extensions/vector/embedder.ts +197 -0
  166. package/src/extensions/vector/index.ts +1 -0
  167. package/src/hooks/post-tool-use.ts +3 -236
  168. package/src/hooks/semantic-daemon-client.ts +1 -208
  169. package/src/hooks/semantic-daemon.ts +6 -271
  170. package/src/hooks/session-end.ts +4 -79
  171. package/src/hooks/session-start.ts +4 -73
  172. package/src/hooks/stop.ts +3 -173
  173. package/src/hooks/user-prompt-submit.ts +3 -338
  174. package/src/index.ts +13 -0
  175. package/src/mcp/handlers.ts +2 -212
  176. package/src/mcp/index.ts +3 -46
  177. package/src/mcp/tools.ts +2 -78
  178. package/src/server/api/chat.ts +2 -244
  179. package/src/server/api/citations.ts +2 -105
  180. package/src/server/api/events.ts +2 -137
  181. package/src/server/api/health.ts +2 -53
  182. package/src/server/api/index.ts +2 -26
  183. package/src/server/api/projects.ts +2 -74
  184. package/src/server/api/search.ts +2 -102
  185. package/src/server/api/sessions.ts +2 -115
  186. package/src/server/api/stats.ts +2 -724
  187. package/src/server/api/turns.ts +2 -143
  188. package/src/server/api/utils.ts +2 -46
  189. package/src/server/index.ts +2 -100
  190. package/src/services/bootstrap-organizer.ts +46 -26
  191. package/src/services/codex-session-history-importer.ts +521 -29
  192. package/src/services/hermes-session-history-importer.ts +733 -0
  193. package/src/services/memory-service-config.ts +36 -0
  194. package/src/services/memory-service-registry.ts +150 -0
  195. package/src/services/memory-service.ts +211 -1325
  196. package/src/services/session-history-importer.ts +58 -14
  197. package/tests/README.md +23 -0
  198. package/tests/adapters/claude/claude-semantic-daemon-adapter.test.ts +54 -0
  199. package/tests/adapters/claude/claude-transcript-reconstructor.test.ts +98 -0
  200. package/tests/adapters/claude-hook-prompt-injection-policy.test.ts +99 -0
  201. package/tests/apps/app-layer-boundary.test.ts +48 -0
  202. package/tests/apps/claude-settings-hooks.test.ts +107 -0
  203. package/tests/apps/cli-disclosure-output.test.ts +212 -0
  204. package/tests/apps/codex-import-runner.test.ts +99 -0
  205. package/tests/apps/codex-validation-output.test.ts +100 -0
  206. package/tests/apps/hermes-import-runner.test.ts +99 -0
  207. package/tests/apps/mcp-install-command.test.ts +59 -0
  208. package/tests/apps/package-build-entrypoints.test.ts +30 -0
  209. package/tests/apps/postinstall-embedding-backend.test.ts +167 -0
  210. package/tests/apps/search-api-disclosure.test.ts +162 -0
  211. package/tests/apps/stats-api-lightweight.test.ts +67 -0
  212. package/tests/apps/ui-disclosure-output.test.ts +140 -0
  213. package/tests/{bootstrap-organizer.test.ts → core/bootstrap-organizer.test.ts} +1 -1
  214. package/tests/{canonical-key.test.ts → core/canonical-key.test.ts} +1 -1
  215. package/tests/core/codex-session-history-importer-validation.test.ts +185 -0
  216. package/tests/{consolidation-worker.test.ts → core/consolidation-worker.test.ts} +2 -2
  217. package/tests/core/embedding-maintenance-service.test.ts +282 -0
  218. package/tests/{evidence-aligner.test.ts → core/evidence-aligner.test.ts} +1 -1
  219. package/tests/core/external-market-context.test.ts +209 -0
  220. package/tests/core/fact-deriver.test.ts +79 -0
  221. package/tests/core/hermes-session-history-importer-validation.test.ts +609 -0
  222. package/tests/{ingest-interceptor.test.ts → core/ingest-interceptor.test.ts} +1 -1
  223. package/tests/{markdown-mirror.test.ts → core/markdown-mirror.test.ts} +2 -2
  224. package/tests/{matcher.test.ts → core/matcher.test.ts} +1 -1
  225. package/tests/{md-mirror.test.ts → core/md-mirror.test.ts} +2 -2
  226. package/tests/core/memory-engine-services.test.ts +240 -0
  227. package/tests/core/memory-ingest-service.test.ts +296 -0
  228. package/tests/core/memory-query-service.test.ts +129 -0
  229. package/tests/core/memory-runtime-service.test.ts +201 -0
  230. package/tests/core/memory-service-composition.test.ts +192 -0
  231. package/tests/core/memory-service-config.test.ts +41 -0
  232. package/tests/core/memory-service-facade.test.ts +30 -0
  233. package/tests/core/memory-service-registry.test.ts +206 -0
  234. package/tests/core/product-validation-matrix.test.ts +61 -0
  235. package/tests/core/project-registry.test.ts +78 -0
  236. package/tests/core/replay-evaluator.test.ts +181 -0
  237. package/tests/core/retrieval-analytics-service.test.ts +210 -0
  238. package/tests/core/retrieval-benchmark.test.ts +93 -0
  239. package/tests/core/retrieval-disclosure-service.test.ts +264 -0
  240. package/tests/core/retrieval-orchestrator.test.ts +403 -0
  241. package/tests/core/retrieval-quality.test.ts +31 -0
  242. package/tests/core/retrieval-services.test.ts +185 -0
  243. package/tests/{retriever-fallback-chain.test.ts → core/retriever-fallback-chain.test.ts} +3 -3
  244. package/tests/{retriever-strategy-scope.test.ts → core/retriever-strategy-scope.test.ts} +70 -3
  245. package/tests/{retriever.memu-adoption.test.ts → core/retriever.memu-adoption.test.ts} +3 -3
  246. package/tests/core/session-history-importer-filter.test.ts +78 -0
  247. package/tests/core/session-qrels.test.ts +250 -0
  248. package/tests/{sqlite-event-store-replication.test.ts → core/sqlite-event-store-replication.test.ts} +36 -1
  249. package/tests/core/summary-deriver.test.ts +66 -0
  250. package/tests/extensions/embedder-warning-suppression.test.ts +53 -0
  251. package/tests/extensions/endless-memory-extension-boundary.test.ts +17 -0
  252. package/tests/extensions/endless-memory-services.test.ts +325 -0
  253. package/tests/extensions/mcp-context-tools.test.ts +905 -0
  254. package/tests/extensions/mcp-extension-boundary.test.ts +21 -0
  255. package/tests/extensions/mcp-package-build.test.ts +22 -0
  256. package/tests/extensions/mcp-project-aware-tools.test.ts +102 -0
  257. package/tests/extensions/shared-memory-extension-boundary.test.ts +24 -0
  258. package/tests/extensions/shared-memory-services.test.ts +309 -0
  259. package/tests/extensions/vector-extension-boundary.test.ts +21 -0
  260. package/.claude/settings.local.json +0 -25
  261. package/.npm-cache/_cacache/content-v2/sha512/04/76/c098f88dfe584a2b80870bff7421b05d17d3d9ee1027f77772332a22d3f93a9a57101a2855107f6ad82077a818bba912b2bc317f2361b5ddb09ad284d9ce +0 -0
  262. package/.npm-cache/_cacache/content-v2/sha512/60/25/d2ecd39cfc7cab58351162814be77f935c6d6491c10c3745d456da7ddb2117ffd90c10e53fe3c0f1ed16b403307841543634504398b16ee4e6b6dd8e0c45 +0 -0
  263. package/.npm-cache/_cacache/index-v5/2b/9a/7f8f40206ed8a2e0a84efaa953ccaed1f5d001e14b931083f2e7a0738007 +0 -2
  264. package/.npm-cache/_cacache/index-v5/2e/d9/fcfa5c6a6abdc2a3644ab84a95936047298c465a2f47ee03db8f7fe1e946 +0 -3
  265. package/.npm-cache/_cacache/index-v5/a9/42/e519633356d12d3d2f19da66a8301016d496c8f5c3e0554124aaa62dc043 +0 -2
  266. package/.npm-cache/_logs/2026-02-26T12_04_52_729Z-debug-0.log +0 -256
  267. package/.npm-cache/_logs/2026-02-26T12_05_36_835Z-debug-0.log +0 -18
  268. package/.npm-cache/_logs/2026-02-26T12_05_45_982Z-debug-0.log +0 -32
  269. package/.npm-cache/_logs/2026-02-26T12_05_48_515Z-debug-0.log +0 -260
  270. package/.npm-cache/_logs/2026-02-26T12_05_53_567Z-debug-0.log +0 -69
  271. package/.npm-cache/_update-notifier-last-checked +0 -0
  272. package/bootstrap-kb/decisions/decisions.md +0 -244
  273. package/bootstrap-kb/glossary/glossary.md +0 -46
  274. package/bootstrap-kb/modules/.claude-plugin.md +0 -22
  275. package/bootstrap-kb/modules/agents.md.md +0 -15
  276. package/bootstrap-kb/modules/claude.md.md +0 -15
  277. package/bootstrap-kb/modules/context.md.md +0 -15
  278. package/bootstrap-kb/modules/docs.md +0 -18
  279. package/bootstrap-kb/modules/handoff.md.md +0 -15
  280. package/bootstrap-kb/modules/package-lock.json.md +0 -15
  281. package/bootstrap-kb/modules/package.json.md +0 -15
  282. package/bootstrap-kb/modules/plan.md.md +0 -15
  283. package/bootstrap-kb/modules/readme.md.md +0 -15
  284. package/bootstrap-kb/modules/scripts.md +0 -26
  285. package/bootstrap-kb/modules/spec.md.md +0 -15
  286. package/bootstrap-kb/modules/specs.md +0 -20
  287. package/bootstrap-kb/modules/src.md +0 -51
  288. package/bootstrap-kb/modules/tests.md +0 -42
  289. package/bootstrap-kb/modules/tsconfig.json.md +0 -15
  290. package/bootstrap-kb/modules/vitest.config.ts.md +0 -15
  291. package/bootstrap-kb/overview/overview.md +0 -40
  292. package/bootstrap-kb/sources/manifest.json +0 -950
  293. package/bootstrap-kb/sources/manifest.md +0 -227
  294. package/bootstrap-kb/timeline/timeline.md +0 -57
  295. package/claude-memory-layer-1.0.14.tgz +0 -0
  296. package/d.sh +0 -3
  297. package/deploy.sh +0 -3
  298. package/dist/ui/app.js +0 -2101
  299. package/memory/.claude-plugin/commands/2026-02-25.md +0 -263
  300. package/memory/_index.md +0 -419
  301. package/memory/agent_response/uncategorized/2026-02-26.md +0 -176
  302. package/memory/agent_response/uncategorized/2026-03-03.md +0 -14
  303. package/memory/agent_response/uncategorized/2026-03-04.md +0 -1421
  304. package/memory/agent_response/uncategorized/2026-03-05.md +0 -157
  305. package/memory/default/uncategorized/2026-02-25.md +0 -4839
  306. package/memory/session_summary/uncategorized/2026-02-26.md +0 -13
  307. package/memory/session_summary/uncategorized/2026-03-03.md +0 -5
  308. package/memory/session_summary/uncategorized/2026-03-04.md +0 -50
  309. package/memory/specs/20260207-dashboard-upgrade/2026-02-25.md +0 -142
  310. package/memory/specs/citations-system/2026-02-25.md +0 -1121
  311. package/memory/specs/endless-mode/2026-02-25.md +0 -1392
  312. package/memory/specs/entity-edge-model/2026-02-25.md +0 -1263
  313. package/memory/specs/evidence-aligner-v2/2026-02-25.md +0 -1028
  314. package/memory/specs/mcp-desktop-integration/2026-02-25.md +0 -1334
  315. package/memory/specs/post-tool-use-hook/2026-02-25.md +0 -1164
  316. package/memory/specs/private-tags/2026-02-25.md +0 -1057
  317. package/memory/specs/progressive-disclosure/2026-02-25.md +0 -1436
  318. package/memory/specs/task-entity-system/2026-02-25.md +0 -924
  319. package/memory/specs/vector-outbox-v2/2026-02-25.md +0 -1510
  320. package/memory/specs/web-viewer-ui/2026-02-25.md +0 -1709
  321. package/memory/tool_observation/uncategorized/2026-02-26.md +0 -209
  322. package/memory/tool_observation/uncategorized/2026-03-03.md +0 -21
  323. package/memory/tool_observation/uncategorized/2026-03-04.md +0 -1033
  324. package/memory/tool_observation/uncategorized/2026-03-05.md +0 -33
  325. package/memory/user_prompt/uncategorized/2026-02-26.md +0 -25
  326. package/memory/user_prompt/uncategorized/2026-03-04.md +0 -634
  327. package/memory/user_prompt/uncategorized/2026-03-05.md +0 -6
  328. package/specs/optional-duckdb/context.md +0 -77
  329. package/specs/optional-duckdb/plan.md +0 -142
  330. package/specs/optional-duckdb/spec.md +0 -35
  331. package/src/ui/app.js +0 -2101
@@ -0,0 +1,2 @@
1
+ /** Compatibility re-exports for the CLI disclosure formatters. */
2
+ export * from '../apps/cli/retrieval-disclosure-output.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Compatibility layer for incremental thin-core migration.
3
+ */
4
+
5
+ export {};
@@ -0,0 +1,170 @@
1
+ import { createHash } from 'crypto';
2
+
3
+ import type { MemoryEvent } from '../types.js';
4
+ import type { MemoryFact, MemoryFactType } from '../model/memory-fact.js';
5
+
6
+ export interface FactDerivationOptions {
7
+ /** Fallback project hash when the source event metadata does not carry scope.project.hash. */
8
+ projectHash?: string;
9
+ /** Optional current time hook for deterministic tests. */
10
+ now?: Date;
11
+ }
12
+
13
+ const DEFAULT_PROJECT_HASH = 'default';
14
+ const MAX_FACT_TEXT_LENGTH = 600;
15
+
16
+ /**
17
+ * Create a stable, rebuild-safe fact id for an event-derived fact.
18
+ *
19
+ * Format intentionally avoids UUID randomness so derived fact stores can be
20
+ * dropped and rebuilt without producing duplicate logical facts.
21
+ */
22
+ export function makeEventDerivedFactId(
23
+ eventId: string,
24
+ factType: MemoryFactType,
25
+ ordinal = 0
26
+ ): string {
27
+ const digest = createHash('sha1')
28
+ .update(`${eventId}:${factType}:${ordinal}`)
29
+ .digest('hex')
30
+ .slice(0, 16);
31
+
32
+ return `fact:event:${eventId}:${factType}:${ordinal}:${digest}`;
33
+ }
34
+
35
+ export class FactDeriver {
36
+ deriveFromEvent(event: MemoryEvent, options: FactDerivationOptions = {}): MemoryFact[] {
37
+ const text = this.toFactText(event);
38
+ if (!text) return [];
39
+
40
+ const factType = this.inferFactType(event);
41
+ const now = (options.now ?? new Date()).toISOString();
42
+ const metadata = this.asRecord(event.metadata);
43
+ const projectHash = this.getProjectHash(metadata) ?? options.projectHash ?? DEFAULT_PROJECT_HASH;
44
+ const tags = this.getTags(metadata);
45
+
46
+ return [{
47
+ factId: makeEventDerivedFactId(event.id, factType),
48
+ projectHash,
49
+ factType,
50
+ text,
51
+ derivedFromEventIds: [event.id],
52
+ sourceKind: this.getSourceKind(event),
53
+ confidence: this.getConfidence(event),
54
+ importance: this.getImportance(event),
55
+ tags,
56
+ ...(this.getFileRefs(metadata).length > 0 ? { fileRefs: this.getFileRefs(metadata) } : {}),
57
+ createdAt: now,
58
+ updatedAt: now
59
+ }];
60
+ }
61
+
62
+ private toFactText(event: MemoryEvent): string | null {
63
+ const content = event.content.trim().replace(/\s+/g, ' ');
64
+ if (!content) return null;
65
+
66
+ const clipped = content.length > MAX_FACT_TEXT_LENGTH
67
+ ? `${content.slice(0, MAX_FACT_TEXT_LENGTH - 1)}…`
68
+ : content;
69
+
70
+ switch (event.eventType) {
71
+ case 'user_prompt':
72
+ return `User asked: ${clipped}`;
73
+ case 'agent_response':
74
+ return `Assistant responded: ${clipped}`;
75
+ case 'session_summary':
76
+ return `Session summary: ${clipped}`;
77
+ case 'tool_observation': {
78
+ const metadata = this.asRecord(event.metadata);
79
+ const toolName = typeof metadata.toolName === 'string' ? metadata.toolName : 'unknown_tool';
80
+ const success = typeof metadata.success === 'boolean'
81
+ ? metadata.success
82
+ : undefined;
83
+ const status = success === undefined ? '' : success ? ' succeeded' : ' failed';
84
+ return `Tool ${toolName}${status}: ${clipped}`;
85
+ }
86
+ }
87
+ }
88
+
89
+ private inferFactType(event: MemoryEvent): MemoryFactType {
90
+ if (event.eventType === 'tool_observation') return 'tool_observation';
91
+ if (event.eventType === 'session_summary') return 'summary_fact';
92
+
93
+ const content = event.content.toLowerCase();
94
+ if (/\b(decided|decision|선택|결정)\b/.test(content)) return 'decision';
95
+ if (/\b(must|should not|constraint|requirement|제약|필수|금지)\b/.test(content)) return 'constraint';
96
+ if (/\b(todo|task|next|pending|해야|작업|진행)\b/.test(content)) return 'task_state';
97
+ if (/\b(prefer|preference|선호)\b/.test(content)) return 'preference';
98
+ if (/\b(src\/|tests\/|\.ts|\.tsx|\.js|\.py|function|class)\b/.test(content)) return 'code_context';
99
+
100
+ return 'task_state';
101
+ }
102
+
103
+ private getSourceKind(event: MemoryEvent): MemoryFact['sourceKind'] {
104
+ switch (event.eventType) {
105
+ case 'user_prompt':
106
+ return 'prompt';
107
+ case 'agent_response':
108
+ case 'session_summary':
109
+ return 'assistant';
110
+ case 'tool_observation':
111
+ return 'tool';
112
+ }
113
+ }
114
+
115
+ private getConfidence(event: MemoryEvent): number {
116
+ switch (event.eventType) {
117
+ case 'session_summary':
118
+ return 0.8;
119
+ case 'tool_observation':
120
+ return 0.75;
121
+ case 'agent_response':
122
+ return 0.7;
123
+ case 'user_prompt':
124
+ return 0.65;
125
+ }
126
+ }
127
+
128
+ private getImportance(event: MemoryEvent): number {
129
+ const metadata = this.asRecord(event.metadata);
130
+ const importance = metadata.importance;
131
+ if (typeof importance === 'number' && Number.isFinite(importance)) {
132
+ return Math.max(0, Math.min(1, importance));
133
+ }
134
+
135
+ if (event.eventType === 'session_summary') return 0.8;
136
+ if (event.eventType === 'tool_observation') return 0.6;
137
+ return 0.5;
138
+ }
139
+
140
+ private getProjectHash(metadata: Record<string, unknown>): string | undefined {
141
+ const scope = this.asRecord(metadata.scope);
142
+ const project = this.asRecord(scope.project);
143
+ return typeof project.hash === 'string' && project.hash.length > 0
144
+ ? project.hash
145
+ : undefined;
146
+ }
147
+
148
+ private getTags(metadata: Record<string, unknown>): string[] {
149
+ return Array.isArray(metadata.tags)
150
+ ? metadata.tags.filter((tag): tag is string => typeof tag === 'string' && tag.length > 0)
151
+ : [];
152
+ }
153
+
154
+ private getFileRefs(metadata: Record<string, unknown>): string[] {
155
+ const refs = metadata.fileRefs ?? metadata.files;
156
+ return Array.isArray(refs)
157
+ ? refs.filter((ref): ref is string => typeof ref === 'string' && ref.length > 0)
158
+ : [];
159
+ }
160
+
161
+ private asRecord(value: unknown): Record<string, unknown> {
162
+ return value && typeof value === 'object' && !Array.isArray(value)
163
+ ? value as Record<string, unknown>
164
+ : {};
165
+ }
166
+ }
167
+
168
+ export function createFactDeriver(): FactDeriver {
169
+ return new FactDeriver();
170
+ }
@@ -0,0 +1,2 @@
1
+ export * from './fact-deriver.js';
2
+ export * from './summary-deriver.js';
@@ -0,0 +1,76 @@
1
+ import type { MemoryEvent } from '../types.js';
2
+
3
+ export interface SessionSummaryDerivation {
4
+ text: string;
5
+ metadata: {
6
+ generated: 'rule-based';
7
+ eventCount: number;
8
+ };
9
+ }
10
+
11
+ const MAX_FIRST_PROMPT_LENGTH = 120;
12
+ const MAX_TOOL_NAMES = 6;
13
+
14
+ export class SummaryDeriver {
15
+ /**
16
+ * Derive the current lightweight rule-based session summary from raw events.
17
+ *
18
+ * The deriver is intentionally pure: callers own persistence and lifecycle
19
+ * orchestration, while this class owns summary text and metadata decisions.
20
+ */
21
+ deriveSessionSummary(events: MemoryEvent[]): SessionSummaryDerivation | null {
22
+ if (events.length < 3) return null;
23
+ if (events.some((event) => event.eventType === 'session_summary')) return null;
24
+
25
+ const prompts = events.filter((event) => event.eventType === 'user_prompt');
26
+ const toolObservations = events.filter((event) => event.eventType === 'tool_observation');
27
+ const toolNames = Array.from(new Set(
28
+ toolObservations
29
+ .map((event) => this.asRecord(event.metadata).toolName)
30
+ .filter((toolName): toolName is string => typeof toolName === 'string' && toolName.length > 0)
31
+ ));
32
+ const errorObservations = toolObservations.filter((event) => this.isErrorObservation(event));
33
+
34
+ const datePart = events[0].timestamp.toISOString().split('T')[0];
35
+ const parts: string[] = [`[${datePart}] ${prompts.length}턴 세션`];
36
+
37
+ if (prompts.length > 0) {
38
+ parts.push(`주요 작업: ${this.firstPromptPreview(prompts[0].content)}`);
39
+ }
40
+ if (toolNames.length > 0) {
41
+ parts.push(`사용 툴: ${toolNames.slice(0, MAX_TOOL_NAMES).join(', ')}`);
42
+ }
43
+ if (errorObservations.length > 0) {
44
+ parts.push(`오류 ${errorObservations.length}건 발생`);
45
+ }
46
+
47
+ return {
48
+ text: parts.join('. '),
49
+ metadata: { generated: 'rule-based', eventCount: events.length }
50
+ };
51
+ }
52
+
53
+ private firstPromptPreview(content: string): string {
54
+ return content.slice(0, MAX_FIRST_PROMPT_LENGTH).replace(/\r?\n/g, ' ');
55
+ }
56
+
57
+ private isErrorObservation(event: MemoryEvent): boolean {
58
+ const metadata = this.asRecord(event.metadata);
59
+
60
+ if (metadata.exitCode !== undefined) {
61
+ return metadata.exitCode !== 0;
62
+ }
63
+
64
+ return metadata.success === false;
65
+ }
66
+
67
+ private asRecord(value: unknown): Record<string, unknown> {
68
+ return value && typeof value === 'object' && !Array.isArray(value)
69
+ ? value as Record<string, unknown>
70
+ : {};
71
+ }
72
+ }
73
+
74
+ export function createSummaryDeriver(): SummaryDeriver {
75
+ return new SummaryDeriver();
76
+ }
@@ -1,152 +1,4 @@
1
- /**
2
- * Local Embedding Generator using @xenova/transformers
3
- * AXIOMMIND Principle 7: Standard JSON format for vectors
4
- */
5
-
6
- import { pipeline, Pipeline } from '@huggingface/transformers';
7
-
8
- export interface EmbeddingResult {
9
- vector: number[];
10
- model: string;
11
- dimensions: number;
12
- }
13
-
14
- export class Embedder {
15
- private pipeline: Pipeline | null = null;
16
- private readonly modelName: string;
17
- private activeModelName: string;
18
- private initialized = false;
19
-
20
- constructor(modelName: string = 'jinaai/jina-embeddings-v5-text-nano-text-matching') {
21
- this.modelName = modelName;
22
- this.activeModelName = modelName;
23
- }
24
-
25
- /**
26
- * Initialize the embedding pipeline
27
- */
28
- async initialize(): Promise<void> {
29
- if (this.initialized) return;
30
-
31
- try {
32
- this.pipeline = await pipeline('feature-extraction', this.modelName);
33
- this.activeModelName = this.modelName;
34
- this.initialized = true;
35
- return;
36
- } catch (primaryError) {
37
- const fallbackModel = process.env.CLAUDE_MEMORY_EMBEDDING_FALLBACK_MODEL || 'onnx-community/embeddinggemma-300m-ONNX';
38
- if (fallbackModel === this.modelName) {
39
- throw primaryError;
40
- }
41
-
42
- console.warn(`[Embedder] Primary model failed (${this.modelName}). Falling back to ${fallbackModel}`);
43
- this.pipeline = await pipeline('feature-extraction', fallbackModel);
44
- this.activeModelName = fallbackModel;
45
- this.initialized = true;
46
- }
47
- }
48
-
49
- // ~4 chars per token; 512 tokens * 4 = 2048, use 2000 to be safe
50
- private static readonly MAX_CHARS = 2000;
51
-
52
- private truncate(text: string): string {
53
- return text.length > Embedder.MAX_CHARS ? text.slice(0, Embedder.MAX_CHARS) : text;
54
- }
55
-
56
- /**
57
- * Generate embedding for a single text
58
- */
59
- async embed(text: string): Promise<EmbeddingResult> {
60
- await this.initialize();
61
-
62
- if (!this.pipeline) {
63
- throw new Error('Embedding pipeline not initialized');
64
- }
65
-
66
- const output = await this.pipeline(this.truncate(text), {
67
- pooling: 'mean',
68
- normalize: true,
69
- truncation: true,
70
- max_length: 512
71
- });
72
-
73
- const vector = Array.from(output.data as Float32Array);
74
-
75
- return {
76
- vector,
77
- model: this.activeModelName,
78
- dimensions: vector.length
79
- };
80
- }
81
-
82
- /**
83
- * Generate embeddings for multiple texts in batch
84
- */
85
- async embedBatch(texts: string[]): Promise<EmbeddingResult[]> {
86
- await this.initialize();
87
-
88
- if (!this.pipeline) {
89
- throw new Error('Embedding pipeline not initialized');
90
- }
91
-
92
- const results: EmbeddingResult[] = [];
93
-
94
- // Process in batches of 32 for memory efficiency
95
- const batchSize = 32;
96
- for (let i = 0; i < texts.length; i += batchSize) {
97
- const batch = texts.slice(i, i + batchSize);
98
-
99
- for (const text of batch) {
100
- const output = await this.pipeline(this.truncate(text), {
101
- pooling: 'mean',
102
- normalize: true,
103
- truncation: true,
104
- max_length: 512
105
- });
106
-
107
- const vector = Array.from(output.data as Float32Array);
108
-
109
- results.push({
110
- vector,
111
- model: this.activeModelName,
112
- dimensions: vector.length
113
- });
114
- }
115
- }
116
-
117
- return results;
118
- }
119
-
120
- /**
121
- * Get embedding dimensions for the current model
122
- */
123
- async getDimensions(): Promise<number> {
124
- const result = await this.embed('test');
125
- return result.dimensions;
126
- }
127
-
128
- /**
129
- * Check if embedder is ready
130
- */
131
- isReady(): boolean {
132
- return this.initialized && this.pipeline !== null;
133
- }
134
-
135
- /**
136
- * Get model name
137
- */
138
- getModelName(): string {
139
- return this.activeModelName;
140
- }
141
- }
142
-
143
- // Singleton instance for reuse
144
- let defaultEmbedder: Embedder | null = null;
145
-
146
- export function getDefaultEmbedder(): Embedder {
147
- const envModel = process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
148
- if (!defaultEmbedder) {
149
- defaultEmbedder = new Embedder(envModel || undefined);
150
- }
151
- return defaultEmbedder;
152
- }
1
+ // Compatibility re-export. Embedding is an optional vector extension, but
2
+ // existing core/engine imports still resolve through this path during the
3
+ // strangler migration.
4
+ export * from '../extensions/vector/index.js';
@@ -0,0 +1,187 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ export interface EmbeddingMaintenanceEvent {
5
+ id: string;
6
+ content: string;
7
+ }
8
+
9
+ export interface EmbeddingMaintenanceEventStore {
10
+ clearEmbeddingOutbox(): Promise<void>;
11
+ getEventsPage(limit: number, offset: number): Promise<EmbeddingMaintenanceEvent[]>;
12
+ enqueueForEmbedding(eventId: string, content: string): Promise<void>;
13
+ }
14
+
15
+ export interface EmbeddingMaintenanceVectorStore {
16
+ count(): Promise<number>;
17
+ clearAll(): Promise<void>;
18
+ }
19
+
20
+ export interface EmbeddingMaintenanceVectorWorker {
21
+ isRunning(): boolean;
22
+ stop(): void;
23
+ start(): void;
24
+ }
25
+
26
+ export interface EmbeddingMaintenanceFileSystem {
27
+ existsSync(targetPath: string): boolean;
28
+ readFileSync(targetPath: string, encoding: BufferEncoding): string;
29
+ writeFileSync(targetPath: string, content: string): void;
30
+ }
31
+
32
+ export interface EmbeddingModelMaintenanceOptions {
33
+ autoMigrate?: boolean;
34
+ }
35
+
36
+ export interface EmbeddingModelMaintenanceResult {
37
+ changed: boolean;
38
+ previousModel: string | null;
39
+ currentModel: string;
40
+ enqueued: number;
41
+ reason?: string;
42
+ }
43
+
44
+ export interface EmbeddingMaintenanceServiceOptions {
45
+ storagePath: string;
46
+ initialize: () => Promise<void>;
47
+ getEmbeddingModelName: () => string;
48
+ vectorStore: EmbeddingMaintenanceVectorStore;
49
+ eventStore: EmbeddingMaintenanceEventStore;
50
+ getVectorWorker: () => EmbeddingMaintenanceVectorWorker | null;
51
+ fileSystem?: EmbeddingMaintenanceFileSystem;
52
+ }
53
+
54
+ export interface EmbeddingMaintenanceService {
55
+ getEmbeddingModelName(): string;
56
+ ensureEmbeddingModelForImport(options?: EmbeddingModelMaintenanceOptions): Promise<EmbeddingModelMaintenanceResult>;
57
+ }
58
+
59
+ const DEFAULT_PAGE_SIZE = 1000;
60
+
61
+ const defaultFileSystem: EmbeddingMaintenanceFileSystem = {
62
+ existsSync: fs.existsSync,
63
+ readFileSync: (targetPath, encoding) => fs.readFileSync(targetPath, encoding),
64
+ writeFileSync: (targetPath, content) => fs.writeFileSync(targetPath, content)
65
+ };
66
+
67
+ class DefaultEmbeddingMaintenanceService implements EmbeddingMaintenanceService {
68
+ private readonly fileSystem: EmbeddingMaintenanceFileSystem;
69
+
70
+ constructor(private readonly options: EmbeddingMaintenanceServiceOptions) {
71
+ this.fileSystem = options.fileSystem ?? defaultFileSystem;
72
+ }
73
+
74
+ getEmbeddingModelName(): string {
75
+ return this.options.getEmbeddingModelName();
76
+ }
77
+
78
+ async ensureEmbeddingModelForImport(
79
+ options?: EmbeddingModelMaintenanceOptions
80
+ ): Promise<EmbeddingModelMaintenanceResult> {
81
+ await this.options.initialize();
82
+
83
+ const currentModel = this.getEmbeddingModelName();
84
+ const metaPath = path.join(this.options.storagePath, 'embedding-meta.json');
85
+ const previousModel = this.readPreviousModel(metaPath);
86
+ const vectorCount = await this.options.vectorStore.count();
87
+ const hasExistingVectors = vectorCount > 0;
88
+
89
+ // First-time metadata write (no migration needed unless legacy vectors exist)
90
+ if (!previousModel && !hasExistingVectors) {
91
+ this.fileSystem.writeFileSync(
92
+ metaPath,
93
+ JSON.stringify({ model: currentModel, updatedAt: new Date().toISOString() }, null, 2)
94
+ );
95
+ return { changed: false, previousModel: null, currentModel, enqueued: 0, reason: 'initialized-meta' };
96
+ }
97
+
98
+ const modelChanged = previousModel !== currentModel;
99
+ const legacyUnknownButVectorsExist = !previousModel && hasExistingVectors;
100
+
101
+ if (!modelChanged && !legacyUnknownButVectorsExist) {
102
+ return { changed: false, previousModel, currentModel, enqueued: 0 };
103
+ }
104
+
105
+ if (options?.autoMigrate === false) {
106
+ return {
107
+ changed: true,
108
+ previousModel,
109
+ currentModel,
110
+ enqueued: 0,
111
+ reason: legacyUnknownButVectorsExist ? 'legacy-vectors-without-meta' : 'model-mismatch'
112
+ };
113
+ }
114
+
115
+ const worker = this.options.getVectorWorker();
116
+ const wasRunning = worker?.isRunning() || false;
117
+ if (wasRunning) worker?.stop();
118
+
119
+ await this.options.vectorStore.clearAll();
120
+ await this.options.eventStore.clearEmbeddingOutbox();
121
+
122
+ const enqueued = await this.reenqueueAllEvents();
123
+
124
+ this.fileSystem.writeFileSync(
125
+ metaPath,
126
+ JSON.stringify(
127
+ {
128
+ model: currentModel,
129
+ previousModel,
130
+ migratedAt: new Date().toISOString(),
131
+ enqueued
132
+ },
133
+ null,
134
+ 2
135
+ )
136
+ );
137
+
138
+ if (wasRunning) worker?.start();
139
+
140
+ return {
141
+ changed: true,
142
+ previousModel,
143
+ currentModel,
144
+ enqueued,
145
+ reason: legacyUnknownButVectorsExist ? 'legacy-vectors-without-meta' : 'model-mismatch'
146
+ };
147
+ }
148
+
149
+ private readPreviousModel(metaPath: string): string | null {
150
+ try {
151
+ if (this.fileSystem.existsSync(metaPath)) {
152
+ const parsed = JSON.parse(this.fileSystem.readFileSync(metaPath, 'utf-8')) as { model?: string };
153
+ return parsed?.model || null;
154
+ }
155
+ } catch {
156
+ return null;
157
+ }
158
+
159
+ return null;
160
+ }
161
+
162
+ private async reenqueueAllEvents(): Promise<number> {
163
+ let offset = 0;
164
+ let enqueued = 0;
165
+
166
+ while (true) {
167
+ const page = await this.options.eventStore.getEventsPage(DEFAULT_PAGE_SIZE, offset);
168
+ if (page.length === 0) break;
169
+
170
+ for (const event of page) {
171
+ await this.options.eventStore.enqueueForEmbedding(event.id, event.content);
172
+ enqueued += 1;
173
+ }
174
+
175
+ offset += page.length;
176
+ if (page.length < DEFAULT_PAGE_SIZE) break;
177
+ }
178
+
179
+ return enqueued;
180
+ }
181
+ }
182
+
183
+ export function createEmbeddingMaintenanceService(
184
+ options: EmbeddingMaintenanceServiceOptions
185
+ ): EmbeddingMaintenanceService {
186
+ return new DefaultEmbeddingMaintenanceService(options);
187
+ }
@@ -0,0 +1,4 @@
1
+ // Compatibility re-export. Endless memory is an optional extension, but existing
2
+ // engine/facade imports still resolve through this path during the strangler
3
+ // migration.
4
+ export * from '../../extensions/endless-memory/index.js';
@@ -0,0 +1,19 @@
1
+ export * from './memory-ingest-service.js';
2
+ export * from './memory-query-service.js';
3
+ export * from './memory-engine-services.js';
4
+ export * from './retrieval-orchestrator.js';
5
+ export * from './retrieval-disclosure-service.js';
6
+ export * from './retrieval-analytics-service.js';
7
+ export * from './shared-memory-services.js';
8
+ export * from './endless-memory-services.js';
9
+ export * from './embedding-maintenance-service.js';
10
+ export * from './memory-runtime-service.js';
11
+ export {
12
+ createRetrievalServices
13
+ } from './retrieval-services.js';
14
+ export type {
15
+ CreateRetrieverFn,
16
+ RetrievalEventStore,
17
+ RetrievalServices,
18
+ RetrievalServicesDeps
19
+ } from './retrieval-services.js';