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,197 @@
1
+ /**
2
+ * Local Embedding Generator using @huggingface/transformers
3
+ * AXIOMMIND Principle 7: Standard JSON format for vectors
4
+ */
5
+
6
+ export interface EmbeddingResult {
7
+ vector: number[];
8
+ model: string;
9
+ dimensions: number;
10
+ }
11
+
12
+ type FeatureExtractionPipelineFactory = (
13
+ task: 'feature-extraction',
14
+ model: string
15
+ ) => Promise<NonNullable<Embedder['pipeline']>>;
16
+
17
+ export class Embedder {
18
+ private pipeline: ((input: string, options?: Record<string, unknown>) => Promise<{ data: Float32Array }>) | null = null;
19
+ private readonly modelName: string;
20
+ private activeModelName: string;
21
+ private initialized = false;
22
+
23
+ constructor(modelName: string = 'jinaai/jina-embeddings-v5-text-nano-text-matching') {
24
+ this.modelName = modelName;
25
+ this.activeModelName = modelName;
26
+ }
27
+
28
+ /**
29
+ * Initialize the embedding pipeline
30
+ */
31
+ async initialize(): Promise<void> {
32
+ if (this.initialized) return;
33
+
34
+ const pipeline = await withSuppressedKnownTransformersWarnings(() => loadTransformersPipeline());
35
+
36
+ try {
37
+ this.pipeline = await withSuppressedKnownTransformersWarnings(() => pipeline('feature-extraction', this.modelName));
38
+ this.activeModelName = this.modelName;
39
+ this.initialized = true;
40
+ return;
41
+ } catch (primaryError) {
42
+ const fallbackModel = process.env.CLAUDE_MEMORY_EMBEDDING_FALLBACK_MODEL || 'onnx-community/embeddinggemma-300m-ONNX';
43
+ if (fallbackModel === this.modelName) {
44
+ throw primaryError;
45
+ }
46
+
47
+ console.warn(`[Embedder] Primary model failed (${this.modelName}). Falling back to ${fallbackModel}`);
48
+ this.pipeline = await withSuppressedKnownTransformersWarnings(() => pipeline('feature-extraction', fallbackModel));
49
+ this.activeModelName = fallbackModel;
50
+ this.initialized = true;
51
+ }
52
+ }
53
+
54
+ // ~4 chars per token; 512 tokens * 4 = 2048, use 2000 to be safe
55
+ private static readonly MAX_CHARS = 2000;
56
+
57
+ private truncate(text: string): string {
58
+ return text.length > Embedder.MAX_CHARS ? text.slice(0, Embedder.MAX_CHARS) : text;
59
+ }
60
+
61
+ /**
62
+ * Generate embedding for a single text
63
+ */
64
+ async embed(text: string): Promise<EmbeddingResult> {
65
+ await this.initialize();
66
+
67
+ if (!this.pipeline) {
68
+ throw new Error('Embedding pipeline not initialized');
69
+ }
70
+
71
+ const output = await this.pipeline(this.truncate(text), {
72
+ pooling: 'mean',
73
+ normalize: true,
74
+ truncation: true,
75
+ max_length: 512
76
+ });
77
+
78
+ const vector = Array.from(output.data);
79
+
80
+ return {
81
+ vector,
82
+ model: this.activeModelName,
83
+ dimensions: vector.length
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Generate embeddings for multiple texts in batch
89
+ */
90
+ async embedBatch(texts: string[]): Promise<EmbeddingResult[]> {
91
+ await this.initialize();
92
+
93
+ if (!this.pipeline) {
94
+ throw new Error('Embedding pipeline not initialized');
95
+ }
96
+
97
+ const results: EmbeddingResult[] = [];
98
+
99
+ // Process in batches of 32 for memory efficiency
100
+ const batchSize = 32;
101
+ for (let i = 0; i < texts.length; i += batchSize) {
102
+ const batch = texts.slice(i, i + batchSize);
103
+
104
+ for (const text of batch) {
105
+ const output = await this.pipeline(this.truncate(text), {
106
+ pooling: 'mean',
107
+ normalize: true,
108
+ truncation: true,
109
+ max_length: 512
110
+ });
111
+
112
+ const vector = Array.from(output.data);
113
+
114
+ results.push({
115
+ vector,
116
+ model: this.activeModelName,
117
+ dimensions: vector.length
118
+ });
119
+ }
120
+ }
121
+
122
+ return results;
123
+ }
124
+
125
+ /**
126
+ * Get embedding dimensions for the current model
127
+ */
128
+ async getDimensions(): Promise<number> {
129
+ const result = await this.embed('test');
130
+ return result.dimensions;
131
+ }
132
+
133
+ /**
134
+ * Check if embedder is ready
135
+ */
136
+ isReady(): boolean {
137
+ return this.initialized && this.pipeline !== null;
138
+ }
139
+
140
+ /**
141
+ * Get model name
142
+ */
143
+ getModelName(): string {
144
+ return this.activeModelName;
145
+ }
146
+ }
147
+
148
+ // Singleton instance for reuse
149
+ let defaultEmbedder: Embedder | null = null;
150
+
151
+ export function getDefaultEmbedder(): Embedder {
152
+ const envModel = process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
153
+ if (!defaultEmbedder) {
154
+ defaultEmbedder = new Embedder(envModel || undefined);
155
+ }
156
+ return defaultEmbedder;
157
+ }
158
+
159
+ let transformersWarningSuppressionDepth = 0;
160
+ let originalConsoleWarn: typeof console.warn | null = null;
161
+
162
+ export async function withSuppressedKnownTransformersWarnings<T>(fn: () => Promise<T>): Promise<T> {
163
+ if (transformersWarningSuppressionDepth === 0) {
164
+ originalConsoleWarn = console.warn;
165
+ console.warn = (...args: unknown[]) => {
166
+ const message = args.map(String).join(' ');
167
+ if (isKnownBenignTransformersWarning(message)) return;
168
+ (originalConsoleWarn ?? console.warn)(...args);
169
+ };
170
+ }
171
+ transformersWarningSuppressionDepth += 1;
172
+
173
+ try {
174
+ return await fn();
175
+ } finally {
176
+ transformersWarningSuppressionDepth -= 1;
177
+ if (transformersWarningSuppressionDepth === 0 && originalConsoleWarn) {
178
+ console.warn = originalConsoleWarn;
179
+ originalConsoleWarn = null;
180
+ }
181
+ }
182
+ }
183
+
184
+ export function isKnownBenignTransformersWarning(message: string): boolean {
185
+ return message.includes('Unknown model class "eurobert"') ||
186
+ message.includes('dtype not specified for "model"');
187
+ }
188
+
189
+ async function loadTransformersPipeline(): Promise<FeatureExtractionPipelineFactory> {
190
+ // Keep @huggingface/transformers lazy so importing MemoryService or pure
191
+ // adapter helpers does not eagerly dlopen onnxruntime native bindings.
192
+ const dynamicImport = new Function('specifier', 'return import(specifier)') as (
193
+ specifier: string
194
+ ) => Promise<{ pipeline: unknown }>;
195
+ const transformers = await dynamicImport('@huggingface/transformers');
196
+ return transformers.pipeline as FeatureExtractionPipelineFactory;
197
+ }
@@ -0,0 +1 @@
1
+ export * from './embedder.js';
@@ -1,242 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * PostToolUse Hook
4
- * Called after each tool execution - stores tool observations
3
+ * Compatibility entrypoint for the Claude post-tool-use hook.
5
4
  *
6
- * Actual Claude Code input format:
7
- * {
8
- * session_id, tool_name, tool_input, tool_use_id,
9
- * tool_response: { stdout?, stderr?, content?, interrupted?, isImage? },
10
- * cwd, transcript_path, permission_mode, hook_event_name
11
- * }
5
+ * Implementation lives in the Claude adapter layer so core stays platform-agnostic.
12
6
  */
13
-
14
- import { getLightweightMemoryService } from '../services/memory-service.js';
15
- import { applyPrivacyFilter, maskSensitiveInput, truncateOutput } from '../core/privacy/index.js';
16
- import { extractMetadata } from '../core/metadata-extractor.js';
17
- import { readTurnState } from '../core/turn-state.js';
18
- import type { PostToolUseInput, ToolObservationPayload, Config } from '../core/types.js';
19
-
20
- // Default config
21
- const DEFAULT_CONFIG: Config['toolObservation'] = {
22
- enabled: true,
23
- excludedTools: [
24
- // Trivial meta tools
25
- 'TodoWrite', 'TodoRead',
26
- // Reproducible query tools (no storage value)
27
- 'Read', 'Grep', 'Glob',
28
- 'ToolSearch', 'WebFetch', 'WebSearch', 'NotebookRead',
29
- // Low-value system tools
30
- 'Skill', 'EnterPlanMode',
31
- ],
32
- minOutputLength: parseInt(process.env.CLAUDE_MEMORY_TOOL_MIN_OUTPUT_LEN || '100'),
33
- maxOutputLength: 10000,
34
- maxOutputLines: 100,
35
- storeOnlyOnSuccess: false
36
- };
37
-
38
- // Tools that are always stored regardless of output length
39
- const ALWAYS_STORE_TOOLS = new Set([
40
- 'Write', 'Edit', 'MultiEdit', 'Agent', 'Task', 'ExitPlanMode'
41
- ]);
42
-
43
- // Keywords that indicate a Bash output is worth storing
44
- const IMPORTANT_BASH_KEYWORDS = [
45
- 'error', 'failed', 'exception', 'traceback', 'panic',
46
- 'warning', 'deprecated',
47
- 'test passed', 'test failed', 'tests passed', 'tests failed',
48
- 'coverage', 'assert',
49
- 'published', 'deployed', 'built successfully', 'build complete',
50
- 'successfully installed', 'successfully created',
51
- ];
52
-
53
- /**
54
- * For Bash commands, only store output that is significant:
55
- * - Has stderr content
56
- * - Contains important keywords (errors, test results, deploy events)
57
- * - Output is very long (> 2000 chars), indicating meaningful work
58
- */
59
- function isBashSignificant(output: string, response: PostToolUseInput['tool_response']): boolean {
60
- if (response?.stderr && response.stderr.trim().length > 20) return true;
61
- const lower = output.toLowerCase();
62
- if (IMPORTANT_BASH_KEYWORDS.some((kw) => lower.includes(kw))) return true;
63
- return output.trim().length > 2000;
64
- }
65
-
66
- /**
67
- * Determine if a tool output is significant enough to store.
68
- * Always-store tools bypass the length check.
69
- * Bash uses keyword-based significance detection.
70
- * Other tools require non-empty stderr or output length >= minLen.
71
- */
72
- function hasSignificantOutput(
73
- toolName: string,
74
- output: string,
75
- response: PostToolUseInput['tool_response'],
76
- minLen: number
77
- ): boolean {
78
- if (ALWAYS_STORE_TOOLS.has(toolName)) return true;
79
- if (toolName === 'Bash') return isBashSignificant(output, response);
80
- if (response?.stderr && response.stderr.trim().length > 0) return true;
81
- return output.trim().length >= minLen;
82
- }
83
-
84
- const DEFAULT_PRIVACY_CONFIG: Config['privacy'] = {
85
- excludePatterns: ['password', 'secret', 'api_key', 'token', 'bearer'],
86
- anonymize: false,
87
- privateTags: {
88
- enabled: true,
89
- marker: '[PRIVATE]',
90
- preserveLineCount: false,
91
- supportedFormats: ['xml']
92
- }
93
- };
94
-
95
- /**
96
- * Extract text output from tool_response object
97
- */
98
- function extractToolOutput(response: PostToolUseInput['tool_response']): string {
99
- if (!response) return '';
100
-
101
- // Bash tools: stdout + stderr
102
- if (response.stdout !== undefined) {
103
- const parts: string[] = [];
104
- if (response.stdout) parts.push(response.stdout);
105
- if (response.stderr) parts.push(`[stderr] ${response.stderr}`);
106
- return parts.join('\n') || '';
107
- }
108
-
109
- // Other tools may have content field
110
- if (response.content !== undefined) {
111
- return typeof response.content === 'string'
112
- ? response.content
113
- : JSON.stringify(response.content);
114
- }
115
-
116
- // Fallback: stringify the whole response
117
- return JSON.stringify(response);
118
- }
119
-
120
- /**
121
- * Determine if the tool execution was successful
122
- */
123
- function isToolSuccess(response: PostToolUseInput['tool_response']): boolean {
124
- if (!response) return false;
125
- if (response.interrupted) return false;
126
- // If stderr has content but stdout also has content, still consider success
127
- return true;
128
- }
129
-
130
- async function main(): Promise<void> {
131
- // Read input from stdin
132
- const inputData = await readStdin();
133
- const input: PostToolUseInput = JSON.parse(inputData);
134
-
135
- const config = { ...DEFAULT_CONFIG };
136
- const privacyConfig = DEFAULT_PRIVACY_CONFIG;
137
-
138
- // Allow env-based blocklist override
139
- const envBlocklist = process.env.CLAUDE_MEMORY_TOOL_BLOCKLIST;
140
- if (envBlocklist !== undefined) {
141
- config.excludedTools = envBlocklist.split(',').map((s) => s.trim()).filter(Boolean);
142
- }
143
-
144
- // 1. Check if tool observation is enabled
145
- if (!config.enabled) {
146
- console.log(JSON.stringify({}));
147
- return;
148
- }
149
-
150
- // 2. Check if tool is excluded
151
- if (config.excludedTools?.includes(input.tool_name)) {
152
- console.log(JSON.stringify({}));
153
- return;
154
- }
155
-
156
- // 3. Extract output from tool_response object
157
- const toolOutput = extractToolOutput(input.tool_response);
158
- const success = isToolSuccess(input.tool_response);
159
-
160
- // 4. Check success filter
161
- if (!success && config.storeOnlyOnSuccess) {
162
- console.log(JSON.stringify({}));
163
- return;
164
- }
165
-
166
- // 4.5. Output-level filter: skip low-signal outputs
167
- if (!hasSignificantOutput(
168
- input.tool_name, toolOutput, input.tool_response,
169
- config.minOutputLength ?? 100
170
- )) {
171
- console.log(JSON.stringify({}));
172
- return;
173
- }
174
-
175
- try {
176
- const memoryService = getLightweightMemoryService(input.session_id);
177
-
178
- // 5. Mask sensitive data in input
179
- const maskedInput = maskSensitiveInput(input.tool_input);
180
-
181
- // 6. Apply privacy filter to output
182
- const filterResult = applyPrivacyFilter(toolOutput, privacyConfig);
183
- const maskedOutput = filterResult.content;
184
-
185
- // 7. Truncate output
186
- const truncatedOutput = truncateOutput(maskedOutput, {
187
- maxLength: config.maxOutputLength,
188
- maxLines: config.maxOutputLines
189
- });
190
-
191
- // 8. Extract metadata
192
- const metadata = extractMetadata(
193
- input.tool_name,
194
- maskedInput,
195
- toolOutput,
196
- success
197
- );
198
-
199
- // 8.5. Read current turn_id from state file
200
- const turnId = readTurnState(input.session_id);
201
-
202
- // 9. Create payload (include turnId in metadata for grouping)
203
- const payload: ToolObservationPayload = {
204
- toolName: input.tool_name,
205
- toolInput: maskedInput,
206
- toolOutput: truncatedOutput,
207
- durationMs: 0, // Claude Code doesn't provide timing info
208
- success,
209
- errorMessage: input.tool_response?.stderr || undefined,
210
- metadata: {
211
- ...metadata,
212
- ...(turnId ? { turnId } : {})
213
- }
214
- };
215
-
216
- // 10. Store observation
217
- await memoryService.storeToolObservation(input.session_id, payload);
218
-
219
- // Output empty (hook doesn't return context)
220
- console.log(JSON.stringify({}));
221
- } catch (error) {
222
- if (process.env.CLAUDE_MEMORY_DEBUG) {
223
- console.error('PostToolUse hook error:', error);
224
- }
225
- console.log(JSON.stringify({}));
226
- }
227
- }
228
-
229
- function readStdin(): Promise<string> {
230
- return new Promise((resolve) => {
231
- let data = '';
232
- process.stdin.setEncoding('utf8');
233
- process.stdin.on('data', (chunk) => {
234
- data += chunk;
235
- });
236
- process.stdin.on('end', () => {
237
- resolve(data);
238
- });
239
- });
240
- }
7
+ import { main } from '../adapters/claude/hooks/post-tool-use.js';
241
8
 
242
9
  main().catch(console.error);
@@ -1,208 +1 @@
1
- import { spawn } from 'child_process';
2
- import * as fs from 'fs';
3
- import * as net from 'net';
4
- import * as os from 'os';
5
- import * as path from 'path';
6
-
7
- interface SemanticRequest {
8
- sessionId: string;
9
- prompt: string;
10
- topK: number;
11
- minScore: number;
12
- }
13
-
14
- interface SemanticMemory {
15
- type: string;
16
- content: string;
17
- id?: string;
18
- score?: number;
19
- }
20
-
21
- interface SemanticDaemonRequest {
22
- type: 'retrieve';
23
- sessionId: string;
24
- prompt: string;
25
- topK: number;
26
- minScore: number;
27
- }
28
-
29
- interface SemanticDaemonResponse {
30
- ok: boolean;
31
- memories?: SemanticMemory[];
32
- error?: string;
33
- }
34
-
35
- const DEFAULT_SOCKET_PATH = path.join(
36
- os.homedir(),
37
- '.claude-code',
38
- 'memory',
39
- 'semantic-daemon.sock'
40
- );
41
-
42
- const DAEMON_SOCKET_PATH = process.env.CLAUDE_MEMORY_SEMANTIC_SOCKET || DEFAULT_SOCKET_PATH;
43
- const DAEMON_START_TIMEOUT_MS = parseInt(process.env.CLAUDE_MEMORY_SEMANTIC_DAEMON_START_MS || '1500');
44
-
45
- let daemonStartPromise: Promise<void> | null = null;
46
-
47
- export async function retrieveSemanticMemories(
48
- request: SemanticRequest,
49
- timeoutMs: number
50
- ): Promise<SemanticMemory[]> {
51
- const payload: SemanticDaemonRequest = {
52
- type: 'retrieve',
53
- sessionId: request.sessionId,
54
- prompt: request.prompt,
55
- topK: request.topK,
56
- minScore: request.minScore
57
- };
58
-
59
- try {
60
- return await requestFromDaemon(payload, timeoutMs);
61
- } catch (error) {
62
- if (!isConnectionError(error)) {
63
- throw error;
64
- }
65
-
66
- await ensureDaemonRunning();
67
- return requestFromDaemon(payload, timeoutMs).catch((retryError) => {
68
- if (process.env.CLAUDE_MEMORY_DEBUG) {
69
- console.error('[semantic-client] retry failed after daemon start:', retryError);
70
- }
71
- throw retryError;
72
- });
73
- }
74
- }
75
-
76
- function requestFromDaemon(
77
- payload: SemanticDaemonRequest,
78
- timeoutMs: number
79
- ): Promise<SemanticMemory[]> {
80
- return new Promise((resolve, reject) => {
81
- const client = net.createConnection(DAEMON_SOCKET_PATH);
82
- client.setEncoding('utf8');
83
-
84
- let settled = false;
85
- let responseRaw = '';
86
- const timer = setTimeout(() => {
87
- const timeoutError = new Error(`semantic daemon timeout (${timeoutMs}ms)`);
88
- (timeoutError as NodeJS.ErrnoException).code = 'ETIMEDOUT';
89
- settle(timeoutError);
90
- client.destroy();
91
- }, timeoutMs);
92
-
93
- const settle = (error?: Error, memories?: SemanticMemory[]) => {
94
- if (settled) return;
95
- settled = true;
96
- clearTimeout(timer);
97
- if (error) {
98
- reject(error);
99
- } else {
100
- resolve(memories || []);
101
- }
102
- };
103
-
104
- client.on('connect', () => {
105
- client.end(JSON.stringify(payload));
106
- });
107
-
108
- client.on('data', (chunk) => {
109
- responseRaw += chunk;
110
- if (responseRaw.length > 4 * 1024 * 1024) {
111
- settle(new Error('semantic daemon response too large'));
112
- client.destroy();
113
- }
114
- });
115
-
116
- client.on('end', () => {
117
- try {
118
- const parsed = JSON.parse(responseRaw || '{}') as SemanticDaemonResponse;
119
- if (!parsed.ok) {
120
- settle(new Error(parsed.error || 'semantic daemon error'));
121
- return;
122
- }
123
- settle(undefined, parsed.memories || []);
124
- } catch (error) {
125
- settle(error as Error);
126
- }
127
- });
128
-
129
- client.on('error', (error) => {
130
- settle(error as Error);
131
- });
132
- });
133
- }
134
-
135
- export async function ensureDaemonRunning(): Promise<void> {
136
- if (daemonStartPromise) {
137
- return daemonStartPromise;
138
- }
139
-
140
- daemonStartPromise = (async () => {
141
- if (await canConnect()) {
142
- return;
143
- }
144
-
145
- const daemonScriptPath = getDaemonScriptPath();
146
- if (!fs.existsSync(daemonScriptPath)) {
147
- throw new Error(`semantic daemon script not found: ${daemonScriptPath}`);
148
- }
149
-
150
- const daemonDir = path.dirname(DAEMON_SOCKET_PATH);
151
- if (!fs.existsSync(daemonDir)) {
152
- fs.mkdirSync(daemonDir, { recursive: true });
153
- }
154
-
155
- const child = spawn(process.execPath, [daemonScriptPath], {
156
- detached: true,
157
- stdio: 'ignore',
158
- env: process.env
159
- });
160
- child.unref();
161
-
162
- const startDeadline = Date.now() + DAEMON_START_TIMEOUT_MS;
163
- while (Date.now() < startDeadline) {
164
- if (await canConnect()) {
165
- return;
166
- }
167
- await sleep(60);
168
- }
169
-
170
- throw new Error(`semantic daemon start timeout (${DAEMON_START_TIMEOUT_MS}ms)`);
171
- })();
172
-
173
- try {
174
- await daemonStartPromise;
175
- } finally {
176
- daemonStartPromise = null;
177
- }
178
- }
179
-
180
- function getDaemonScriptPath(): string {
181
- return path.join(path.dirname(new URL(import.meta.url).pathname), 'semantic-daemon.js');
182
- }
183
-
184
- function canConnect(): Promise<boolean> {
185
- return new Promise((resolve) => {
186
- let settled = false;
187
- const client = net.createConnection(DAEMON_SOCKET_PATH);
188
- const finalize = (ok: boolean) => {
189
- if (settled) return;
190
- settled = true;
191
- client.destroy();
192
- resolve(ok);
193
- };
194
-
195
- client.on('connect', () => finalize(true));
196
- client.on('error', () => finalize(false));
197
- setTimeout(() => finalize(false), 120).unref();
198
- });
199
- }
200
-
201
- function isConnectionError(error: unknown): boolean {
202
- const code = (error as NodeJS.ErrnoException | undefined)?.code;
203
- return code === 'ENOENT' || code === 'ECONNREFUSED' || code === 'EPIPE' || code === 'ECONNRESET';
204
- }
205
-
206
- function sleep(ms: number): Promise<void> {
207
- return new Promise((resolve) => setTimeout(resolve, ms));
208
- }
1
+ export * from '../adapters/claude/hooks/semantic-daemon-client.js';