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,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';