superlocalmemory 2.8.6 → 3.0.0

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 (431) hide show
  1. package/LICENSE +9 -1
  2. package/NOTICE +63 -0
  3. package/README.md +165 -480
  4. package/bin/slm +17 -449
  5. package/bin/slm-npm +1 -1
  6. package/conftest.py +5 -0
  7. package/docs/api-reference.md +284 -0
  8. package/docs/architecture.md +149 -0
  9. package/docs/auto-memory.md +150 -0
  10. package/docs/cli-reference.md +276 -0
  11. package/docs/compliance.md +191 -0
  12. package/docs/configuration.md +182 -0
  13. package/docs/getting-started.md +102 -0
  14. package/docs/ide-setup.md +261 -0
  15. package/docs/mcp-tools.md +220 -0
  16. package/docs/migration-from-v2.md +170 -0
  17. package/docs/profiles.md +173 -0
  18. package/docs/troubleshooting.md +310 -0
  19. package/{configs → ide/configs}/antigravity-mcp.json +3 -3
  20. package/ide/configs/chatgpt-desktop-mcp.json +16 -0
  21. package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
  22. package/{configs → ide/configs}/codex-mcp.toml +4 -4
  23. package/{configs → ide/configs}/continue-mcp.yaml +4 -3
  24. package/{configs → ide/configs}/continue-skills.yaml +6 -6
  25. package/ide/configs/cursor-mcp.json +15 -0
  26. package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
  27. package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
  28. package/{configs → ide/configs}/opencode-mcp.json +2 -2
  29. package/{configs → ide/configs}/perplexity-mcp.json +2 -2
  30. package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
  31. package/{configs → ide/configs}/windsurf-mcp.json +3 -3
  32. package/{configs → ide/configs}/zed-mcp.json +2 -2
  33. package/{hooks → ide/hooks}/context-hook.js +9 -20
  34. package/ide/hooks/memory-list-skill.js +70 -0
  35. package/ide/hooks/memory-profile-skill.js +101 -0
  36. package/ide/hooks/memory-recall-skill.js +62 -0
  37. package/ide/hooks/memory-remember-skill.js +68 -0
  38. package/ide/hooks/memory-reset-skill.js +160 -0
  39. package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
  40. package/ide/integrations/langchain/README.md +106 -0
  41. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
  42. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
  43. package/ide/integrations/langchain/pyproject.toml +38 -0
  44. package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
  45. package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
  46. package/ide/integrations/langchain/tests/test_security.py +117 -0
  47. package/ide/integrations/llamaindex/README.md +81 -0
  48. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
  49. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
  50. package/ide/integrations/llamaindex/pyproject.toml +43 -0
  51. package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
  52. package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
  53. package/ide/integrations/llamaindex/tests/test_security.py +241 -0
  54. package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
  55. package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
  56. package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
  57. package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
  58. package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
  59. package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
  60. package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
  61. package/package.json +13 -22
  62. package/pyproject.toml +85 -0
  63. package/scripts/build-dmg.sh +417 -0
  64. package/scripts/install-skills.ps1 +334 -0
  65. package/scripts/postinstall.js +2 -2
  66. package/scripts/start-dashboard.ps1 +52 -0
  67. package/scripts/start-dashboard.sh +41 -0
  68. package/scripts/sync-wiki.ps1 +127 -0
  69. package/scripts/sync-wiki.sh +82 -0
  70. package/scripts/test-dmg.sh +161 -0
  71. package/scripts/test-npm-package.ps1 +252 -0
  72. package/scripts/test-npm-package.sh +207 -0
  73. package/scripts/verify-install.ps1 +294 -0
  74. package/scripts/verify-install.sh +266 -0
  75. package/src/superlocalmemory/__init__.py +0 -0
  76. package/src/superlocalmemory/attribution/__init__.py +9 -0
  77. package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
  78. package/src/superlocalmemory/attribution/signer.py +153 -0
  79. package/src/superlocalmemory/attribution/watermark.py +189 -0
  80. package/src/superlocalmemory/cli/__init__.py +5 -0
  81. package/src/superlocalmemory/cli/commands.py +245 -0
  82. package/src/superlocalmemory/cli/main.py +89 -0
  83. package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
  84. package/src/superlocalmemory/cli/post_install.py +99 -0
  85. package/src/superlocalmemory/cli/setup_wizard.py +129 -0
  86. package/src/superlocalmemory/compliance/__init__.py +0 -0
  87. package/src/superlocalmemory/compliance/abac.py +204 -0
  88. package/src/superlocalmemory/compliance/audit.py +314 -0
  89. package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
  90. package/src/superlocalmemory/compliance/gdpr.py +294 -0
  91. package/src/superlocalmemory/compliance/lifecycle.py +158 -0
  92. package/src/superlocalmemory/compliance/retention.py +232 -0
  93. package/src/superlocalmemory/compliance/scheduler.py +148 -0
  94. package/src/superlocalmemory/core/__init__.py +0 -0
  95. package/src/superlocalmemory/core/config.py +391 -0
  96. package/src/superlocalmemory/core/embeddings.py +293 -0
  97. package/src/superlocalmemory/core/engine.py +701 -0
  98. package/src/superlocalmemory/core/hooks.py +65 -0
  99. package/src/superlocalmemory/core/maintenance.py +172 -0
  100. package/src/superlocalmemory/core/modes.py +140 -0
  101. package/src/superlocalmemory/core/profiles.py +234 -0
  102. package/src/superlocalmemory/core/registry.py +117 -0
  103. package/src/superlocalmemory/dynamics/__init__.py +0 -0
  104. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
  105. package/src/superlocalmemory/encoding/__init__.py +0 -0
  106. package/src/superlocalmemory/encoding/consolidator.py +485 -0
  107. package/src/superlocalmemory/encoding/emotional.py +125 -0
  108. package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
  109. package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
  110. package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
  111. package/src/superlocalmemory/encoding/foresight.py +91 -0
  112. package/src/superlocalmemory/encoding/graph_builder.py +302 -0
  113. package/src/superlocalmemory/encoding/observation_builder.py +160 -0
  114. package/src/superlocalmemory/encoding/scene_builder.py +183 -0
  115. package/src/superlocalmemory/encoding/signal_inference.py +90 -0
  116. package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
  117. package/src/superlocalmemory/encoding/type_router.py +235 -0
  118. package/src/superlocalmemory/hooks/__init__.py +3 -0
  119. package/src/superlocalmemory/hooks/auto_capture.py +111 -0
  120. package/src/superlocalmemory/hooks/auto_recall.py +93 -0
  121. package/src/superlocalmemory/hooks/ide_connector.py +204 -0
  122. package/src/superlocalmemory/hooks/rules_engine.py +99 -0
  123. package/src/superlocalmemory/infra/__init__.py +3 -0
  124. package/src/superlocalmemory/infra/auth_middleware.py +82 -0
  125. package/src/superlocalmemory/infra/backup.py +317 -0
  126. package/src/superlocalmemory/infra/cache_manager.py +267 -0
  127. package/src/superlocalmemory/infra/event_bus.py +381 -0
  128. package/src/superlocalmemory/infra/rate_limiter.py +135 -0
  129. package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
  130. package/src/superlocalmemory/learning/__init__.py +0 -0
  131. package/src/superlocalmemory/learning/adaptive.py +172 -0
  132. package/src/superlocalmemory/learning/behavioral.py +490 -0
  133. package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
  134. package/src/superlocalmemory/learning/bootstrap.py +298 -0
  135. package/src/superlocalmemory/learning/cross_project.py +399 -0
  136. package/src/superlocalmemory/learning/database.py +376 -0
  137. package/src/superlocalmemory/learning/engagement.py +323 -0
  138. package/src/superlocalmemory/learning/features.py +138 -0
  139. package/src/superlocalmemory/learning/feedback.py +316 -0
  140. package/src/superlocalmemory/learning/outcomes.py +255 -0
  141. package/src/superlocalmemory/learning/project_context.py +366 -0
  142. package/src/superlocalmemory/learning/ranker.py +155 -0
  143. package/src/superlocalmemory/learning/source_quality.py +303 -0
  144. package/src/superlocalmemory/learning/workflows.py +309 -0
  145. package/src/superlocalmemory/llm/__init__.py +0 -0
  146. package/src/superlocalmemory/llm/backbone.py +316 -0
  147. package/src/superlocalmemory/math/__init__.py +0 -0
  148. package/src/superlocalmemory/math/fisher.py +356 -0
  149. package/src/superlocalmemory/math/langevin.py +398 -0
  150. package/src/superlocalmemory/math/sheaf.py +257 -0
  151. package/src/superlocalmemory/mcp/__init__.py +0 -0
  152. package/src/superlocalmemory/mcp/resources.py +245 -0
  153. package/src/superlocalmemory/mcp/server.py +61 -0
  154. package/src/superlocalmemory/mcp/tools.py +18 -0
  155. package/src/superlocalmemory/mcp/tools_core.py +305 -0
  156. package/src/superlocalmemory/mcp/tools_v28.py +223 -0
  157. package/src/superlocalmemory/mcp/tools_v3.py +286 -0
  158. package/src/superlocalmemory/retrieval/__init__.py +0 -0
  159. package/src/superlocalmemory/retrieval/agentic.py +295 -0
  160. package/src/superlocalmemory/retrieval/ann_index.py +223 -0
  161. package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
  162. package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
  163. package/src/superlocalmemory/retrieval/engine.py +390 -0
  164. package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
  165. package/src/superlocalmemory/retrieval/fusion.py +78 -0
  166. package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
  167. package/src/superlocalmemory/retrieval/reranker.py +154 -0
  168. package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
  169. package/src/superlocalmemory/retrieval/strategy.py +96 -0
  170. package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
  171. package/src/superlocalmemory/server/__init__.py +1 -0
  172. package/src/superlocalmemory/server/api.py +248 -0
  173. package/src/superlocalmemory/server/routes/__init__.py +4 -0
  174. package/src/superlocalmemory/server/routes/agents.py +107 -0
  175. package/src/superlocalmemory/server/routes/backup.py +91 -0
  176. package/src/superlocalmemory/server/routes/behavioral.py +127 -0
  177. package/src/superlocalmemory/server/routes/compliance.py +160 -0
  178. package/src/superlocalmemory/server/routes/data_io.py +188 -0
  179. package/src/superlocalmemory/server/routes/events.py +183 -0
  180. package/src/superlocalmemory/server/routes/helpers.py +85 -0
  181. package/src/superlocalmemory/server/routes/learning.py +273 -0
  182. package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
  183. package/src/superlocalmemory/server/routes/memories.py +399 -0
  184. package/src/superlocalmemory/server/routes/profiles.py +219 -0
  185. package/src/superlocalmemory/server/routes/stats.py +346 -0
  186. package/src/superlocalmemory/server/routes/v3_api.py +365 -0
  187. package/src/superlocalmemory/server/routes/ws.py +82 -0
  188. package/src/superlocalmemory/server/security_middleware.py +57 -0
  189. package/src/superlocalmemory/server/ui.py +245 -0
  190. package/src/superlocalmemory/storage/__init__.py +0 -0
  191. package/src/superlocalmemory/storage/access_control.py +182 -0
  192. package/src/superlocalmemory/storage/database.py +594 -0
  193. package/src/superlocalmemory/storage/migrations.py +303 -0
  194. package/src/superlocalmemory/storage/models.py +406 -0
  195. package/src/superlocalmemory/storage/schema.py +726 -0
  196. package/src/superlocalmemory/storage/v2_migrator.py +317 -0
  197. package/src/superlocalmemory/trust/__init__.py +0 -0
  198. package/src/superlocalmemory/trust/gate.py +130 -0
  199. package/src/superlocalmemory/trust/provenance.py +124 -0
  200. package/src/superlocalmemory/trust/scorer.py +347 -0
  201. package/src/superlocalmemory/trust/signals.py +153 -0
  202. package/ui/index.html +278 -5
  203. package/ui/js/auto-settings.js +70 -0
  204. package/ui/js/dashboard.js +90 -0
  205. package/ui/js/fact-detail.js +92 -0
  206. package/ui/js/feedback.js +2 -2
  207. package/ui/js/ide-status.js +102 -0
  208. package/ui/js/math-health.js +98 -0
  209. package/ui/js/recall-lab.js +127 -0
  210. package/ui/js/settings.js +2 -2
  211. package/ui/js/trust-dashboard.js +73 -0
  212. package/api_server.py +0 -724
  213. package/bin/aider-smart +0 -72
  214. package/bin/superlocalmemoryv2-learning +0 -4
  215. package/bin/superlocalmemoryv2-list +0 -3
  216. package/bin/superlocalmemoryv2-patterns +0 -4
  217. package/bin/superlocalmemoryv2-profile +0 -3
  218. package/bin/superlocalmemoryv2-recall +0 -3
  219. package/bin/superlocalmemoryv2-remember +0 -3
  220. package/bin/superlocalmemoryv2-reset +0 -3
  221. package/bin/superlocalmemoryv2-status +0 -3
  222. package/configs/chatgpt-desktop-mcp.json +0 -16
  223. package/configs/cursor-mcp.json +0 -15
  224. package/hooks/memory-list-skill.js +0 -139
  225. package/hooks/memory-profile-skill.js +0 -273
  226. package/hooks/memory-recall-skill.js +0 -114
  227. package/hooks/memory-remember-skill.js +0 -127
  228. package/hooks/memory-reset-skill.js +0 -274
  229. package/mcp_server.py +0 -1808
  230. package/requirements-core.txt +0 -22
  231. package/requirements-learning.txt +0 -12
  232. package/requirements.txt +0 -12
  233. package/src/agent_registry.py +0 -411
  234. package/src/auth_middleware.py +0 -61
  235. package/src/auto_backup.py +0 -459
  236. package/src/behavioral/__init__.py +0 -49
  237. package/src/behavioral/behavioral_listener.py +0 -203
  238. package/src/behavioral/behavioral_patterns.py +0 -275
  239. package/src/behavioral/cross_project_transfer.py +0 -206
  240. package/src/behavioral/outcome_inference.py +0 -194
  241. package/src/behavioral/outcome_tracker.py +0 -193
  242. package/src/behavioral/tests/__init__.py +0 -4
  243. package/src/behavioral/tests/test_behavioral_integration.py +0 -108
  244. package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
  245. package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
  246. package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
  247. package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
  248. package/src/behavioral/tests/test_outcome_inference.py +0 -107
  249. package/src/behavioral/tests/test_outcome_tracker.py +0 -96
  250. package/src/cache_manager.py +0 -518
  251. package/src/compliance/__init__.py +0 -48
  252. package/src/compliance/abac_engine.py +0 -149
  253. package/src/compliance/abac_middleware.py +0 -116
  254. package/src/compliance/audit_db.py +0 -215
  255. package/src/compliance/audit_logger.py +0 -148
  256. package/src/compliance/retention_manager.py +0 -289
  257. package/src/compliance/retention_scheduler.py +0 -186
  258. package/src/compliance/tests/__init__.py +0 -4
  259. package/src/compliance/tests/test_abac_enforcement.py +0 -95
  260. package/src/compliance/tests/test_abac_engine.py +0 -124
  261. package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
  262. package/src/compliance/tests/test_audit_db.py +0 -123
  263. package/src/compliance/tests/test_audit_logger.py +0 -98
  264. package/src/compliance/tests/test_mcp_audit.py +0 -128
  265. package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
  266. package/src/compliance/tests/test_retention_manager.py +0 -131
  267. package/src/compliance/tests/test_retention_scheduler.py +0 -99
  268. package/src/compression/__init__.py +0 -25
  269. package/src/compression/cli.py +0 -150
  270. package/src/compression/cold_storage.py +0 -217
  271. package/src/compression/config.py +0 -72
  272. package/src/compression/orchestrator.py +0 -133
  273. package/src/compression/tier2_compressor.py +0 -228
  274. package/src/compression/tier3_compressor.py +0 -153
  275. package/src/compression/tier_classifier.py +0 -148
  276. package/src/db_connection_manager.py +0 -536
  277. package/src/embedding_engine.py +0 -63
  278. package/src/embeddings/__init__.py +0 -47
  279. package/src/embeddings/cache.py +0 -70
  280. package/src/embeddings/cli.py +0 -113
  281. package/src/embeddings/constants.py +0 -47
  282. package/src/embeddings/database.py +0 -91
  283. package/src/embeddings/engine.py +0 -247
  284. package/src/embeddings/model_loader.py +0 -145
  285. package/src/event_bus.py +0 -562
  286. package/src/graph/__init__.py +0 -36
  287. package/src/graph/build_helpers.py +0 -74
  288. package/src/graph/cli.py +0 -87
  289. package/src/graph/cluster_builder.py +0 -188
  290. package/src/graph/cluster_summary.py +0 -148
  291. package/src/graph/constants.py +0 -47
  292. package/src/graph/edge_builder.py +0 -162
  293. package/src/graph/entity_extractor.py +0 -95
  294. package/src/graph/graph_core.py +0 -226
  295. package/src/graph/graph_search.py +0 -231
  296. package/src/graph/hierarchical.py +0 -207
  297. package/src/graph/schema.py +0 -99
  298. package/src/graph_engine.py +0 -52
  299. package/src/hnsw_index.py +0 -628
  300. package/src/hybrid_search.py +0 -46
  301. package/src/learning/__init__.py +0 -217
  302. package/src/learning/adaptive_ranker.py +0 -682
  303. package/src/learning/bootstrap/__init__.py +0 -69
  304. package/src/learning/bootstrap/constants.py +0 -93
  305. package/src/learning/bootstrap/db_queries.py +0 -316
  306. package/src/learning/bootstrap/sampling.py +0 -82
  307. package/src/learning/bootstrap/text_utils.py +0 -71
  308. package/src/learning/cross_project_aggregator.py +0 -857
  309. package/src/learning/db/__init__.py +0 -40
  310. package/src/learning/db/constants.py +0 -44
  311. package/src/learning/db/schema.py +0 -279
  312. package/src/learning/engagement_tracker.py +0 -628
  313. package/src/learning/feature_extractor.py +0 -708
  314. package/src/learning/feedback_collector.py +0 -806
  315. package/src/learning/learning_db.py +0 -915
  316. package/src/learning/project_context_manager.py +0 -572
  317. package/src/learning/ranking/__init__.py +0 -33
  318. package/src/learning/ranking/constants.py +0 -84
  319. package/src/learning/ranking/helpers.py +0 -278
  320. package/src/learning/source_quality_scorer.py +0 -676
  321. package/src/learning/synthetic_bootstrap.py +0 -755
  322. package/src/learning/tests/test_adaptive_ranker.py +0 -325
  323. package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
  324. package/src/learning/tests/test_aggregator.py +0 -306
  325. package/src/learning/tests/test_auto_retrain_v28.py +0 -35
  326. package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
  327. package/src/learning/tests/test_feature_extractor_v28.py +0 -93
  328. package/src/learning/tests/test_feedback_collector.py +0 -294
  329. package/src/learning/tests/test_learning_db.py +0 -602
  330. package/src/learning/tests/test_learning_db_v28.py +0 -110
  331. package/src/learning/tests/test_learning_init_v28.py +0 -48
  332. package/src/learning/tests/test_outcome_signals.py +0 -48
  333. package/src/learning/tests/test_project_context.py +0 -292
  334. package/src/learning/tests/test_schema_migration.py +0 -319
  335. package/src/learning/tests/test_signal_inference.py +0 -397
  336. package/src/learning/tests/test_source_quality.py +0 -351
  337. package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
  338. package/src/learning/tests/test_workflow_miner.py +0 -318
  339. package/src/learning/workflow_pattern_miner.py +0 -655
  340. package/src/lifecycle/__init__.py +0 -54
  341. package/src/lifecycle/bounded_growth.py +0 -239
  342. package/src/lifecycle/compaction_engine.py +0 -226
  343. package/src/lifecycle/lifecycle_engine.py +0 -355
  344. package/src/lifecycle/lifecycle_evaluator.py +0 -257
  345. package/src/lifecycle/lifecycle_scheduler.py +0 -130
  346. package/src/lifecycle/retention_policy.py +0 -285
  347. package/src/lifecycle/tests/test_bounded_growth.py +0 -193
  348. package/src/lifecycle/tests/test_compaction.py +0 -179
  349. package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
  350. package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
  351. package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
  352. package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
  353. package/src/lifecycle/tests/test_mcp_compact.py +0 -149
  354. package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
  355. package/src/lifecycle/tests/test_retention_policy.py +0 -162
  356. package/src/mcp_tools_v28.py +0 -281
  357. package/src/memory/__init__.py +0 -36
  358. package/src/memory/cli.py +0 -205
  359. package/src/memory/constants.py +0 -39
  360. package/src/memory/helpers.py +0 -28
  361. package/src/memory/schema.py +0 -166
  362. package/src/memory-profiles.py +0 -595
  363. package/src/memory-reset.py +0 -491
  364. package/src/memory_compression.py +0 -989
  365. package/src/memory_store_v2.py +0 -1155
  366. package/src/migrate_v1_to_v2.py +0 -629
  367. package/src/pattern_learner.py +0 -34
  368. package/src/patterns/__init__.py +0 -24
  369. package/src/patterns/analyzers.py +0 -251
  370. package/src/patterns/learner.py +0 -271
  371. package/src/patterns/scoring.py +0 -171
  372. package/src/patterns/store.py +0 -225
  373. package/src/patterns/terminology.py +0 -140
  374. package/src/provenance_tracker.py +0 -312
  375. package/src/qualixar_attribution.py +0 -139
  376. package/src/qualixar_watermark.py +0 -78
  377. package/src/query_optimizer.py +0 -511
  378. package/src/rate_limiter.py +0 -83
  379. package/src/search/__init__.py +0 -20
  380. package/src/search/cli.py +0 -77
  381. package/src/search/constants.py +0 -26
  382. package/src/search/engine.py +0 -241
  383. package/src/search/fusion.py +0 -122
  384. package/src/search/index_loader.py +0 -114
  385. package/src/search/methods.py +0 -162
  386. package/src/search_engine_v2.py +0 -401
  387. package/src/setup_validator.py +0 -482
  388. package/src/subscription_manager.py +0 -391
  389. package/src/tree/__init__.py +0 -59
  390. package/src/tree/builder.py +0 -185
  391. package/src/tree/nodes.py +0 -202
  392. package/src/tree/queries.py +0 -257
  393. package/src/tree/schema.py +0 -80
  394. package/src/tree_manager.py +0 -19
  395. package/src/trust/__init__.py +0 -45
  396. package/src/trust/constants.py +0 -66
  397. package/src/trust/queries.py +0 -157
  398. package/src/trust/schema.py +0 -95
  399. package/src/trust/scorer.py +0 -299
  400. package/src/trust/signals.py +0 -95
  401. package/src/trust_scorer.py +0 -44
  402. package/ui/app.js +0 -1588
  403. package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
  404. package/ui/js/graph-cytoscape.js +0 -1168
  405. package/ui/js/graph-d3-backup.js +0 -32
  406. package/ui/js/graph.js +0 -32
  407. package/ui_server.py +0 -286
  408. /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
  409. /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
  410. /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
  411. /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
  412. /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
  413. /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
  414. /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
  415. /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
  416. /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
  417. /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
  418. /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
  419. /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
  420. /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
  421. /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
  422. /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
  423. /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
  424. /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
  425. /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
  426. /package/{completions → ide/completions}/slm.bash +0 -0
  427. /package/{completions → ide/completions}/slm.zsh +0 -0
  428. /package/{configs → ide/configs}/cody-commands.json +0 -0
  429. /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
  430. /package/{install.ps1 → scripts/install.ps1} +0 -0
  431. /package/{install.sh → scripts/install.sh} +0 -0
@@ -1,145 +0,0 @@
1
- #!/usr/bin/env python3
2
- # SPDX-License-Identifier: MIT
3
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
4
- """Model loading and backend encoder methods for EmbeddingEngine.
5
- """
6
- import time
7
- import logging
8
- from typing import List
9
-
10
- import numpy as np
11
-
12
- from embeddings.constants import (
13
- SENTENCE_TRANSFORMERS_AVAILABLE,
14
- SKLEARN_AVAILABLE,
15
- )
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- class ModelLoaderMixin:
21
- """
22
- Mixin that handles model initialization and raw encoding backends.
23
-
24
- Expects the host class to have:
25
- - self.use_transformers: bool
26
- - self.model_cache_path: Path
27
- - self.model_name: str
28
- - self.device: str
29
- - self.model: Optional[SentenceTransformer]
30
- - self.dimension: int
31
- - self.tfidf_vectorizer
32
- - self.tfidf_fitted: bool
33
- """
34
-
35
- def _load_model(self):
36
- """Load sentence transformer model or fallback to TF-IDF."""
37
- if not self.use_transformers:
38
- logger.warning(
39
- "sentence-transformers unavailable. Install with: "
40
- "pip install sentence-transformers"
41
- )
42
- self._init_fallback()
43
- return
44
-
45
- try:
46
- from sentence_transformers import SentenceTransformer
47
-
48
- # Create model cache directory
49
- self.model_cache_path.mkdir(parents=True, exist_ok=True)
50
-
51
- logger.info(f"Loading model: {self.model_name}")
52
- start_time = time.time()
53
-
54
- # Load model with local cache
55
- self.model = SentenceTransformer(
56
- self.model_name,
57
- device=self.device,
58
- cache_folder=str(self.model_cache_path)
59
- )
60
-
61
- # Get actual dimension
62
- self.dimension = self.model.get_sentence_embedding_dimension()
63
-
64
- elapsed = time.time() - start_time
65
- logger.info(
66
- f"Loaded {self.model_name} ({self.dimension}D) in {elapsed:.2f}s"
67
- )
68
-
69
- except Exception as e:
70
- logger.error(f"Failed to load sentence transformer: {e}")
71
- logger.info("Falling back to TF-IDF")
72
- self.use_transformers = False
73
- self._init_fallback()
74
-
75
- def _init_fallback(self):
76
- """Initialize TF-IDF fallback."""
77
- if not SKLEARN_AVAILABLE:
78
- logger.error(
79
- "sklearn unavailable - no fallback available. "
80
- "Install: pip install scikit-learn"
81
- )
82
- return
83
-
84
- from sklearn.feature_extraction.text import TfidfVectorizer
85
-
86
- logger.info("Using TF-IDF fallback (dimension will be dynamic)")
87
- self.tfidf_vectorizer = TfidfVectorizer(
88
- max_features=384, # Match sentence transformer dimension
89
- stop_words='english',
90
- ngram_range=(1, 2),
91
- min_df=1
92
- )
93
- self.dimension = 384
94
-
95
- def _encode_transformer(
96
- self,
97
- texts: List[str],
98
- batch_size: int,
99
- show_progress: bool
100
- ) -> np.ndarray:
101
- """Generate embeddings using sentence transformer."""
102
- try:
103
- start_time = time.time()
104
-
105
- embeddings = self.model.encode(
106
- texts,
107
- batch_size=batch_size,
108
- show_progress_bar=show_progress,
109
- convert_to_numpy=True,
110
- normalize_embeddings=False # We'll normalize separately
111
- )
112
-
113
- elapsed = time.time() - start_time
114
- rate = len(texts) / elapsed if elapsed > 0 else 0
115
- logger.debug(f"Encoded {len(texts)} texts in {elapsed:.2f}s ({rate:.0f} texts/sec)")
116
-
117
- return embeddings
118
-
119
- except Exception as e:
120
- logger.error(f"Transformer encoding failed: {e}")
121
- raise
122
-
123
- def _encode_tfidf(self, texts: List[str]) -> np.ndarray:
124
- """Generate embeddings using TF-IDF fallback."""
125
- try:
126
- if not self.tfidf_fitted:
127
- # Fit on first use
128
- logger.info("Fitting TF-IDF vectorizer...")
129
- self.tfidf_vectorizer.fit(texts)
130
- self.tfidf_fitted = True
131
-
132
- embeddings = self.tfidf_vectorizer.transform(texts).toarray()
133
-
134
- # Pad or truncate to target dimension
135
- if embeddings.shape[1] < self.dimension:
136
- padding = np.zeros((embeddings.shape[0], self.dimension - embeddings.shape[1]))
137
- embeddings = np.hstack([embeddings, padding])
138
- elif embeddings.shape[1] > self.dimension:
139
- embeddings = embeddings[:, :self.dimension]
140
-
141
- return embeddings
142
-
143
- except Exception as e:
144
- logger.error(f"TF-IDF encoding failed: {e}")
145
- raise
package/src/event_bus.py DELETED
@@ -1,562 +0,0 @@
1
- #!/usr/bin/env python3
2
- # SPDX-License-Identifier: MIT
3
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
4
- """
5
- EventBus — Real-time event broadcasting for memory operations.
6
-
7
- Transforms SuperLocalMemory from passive storage (filing cabinet) to active
8
- coordination layer (nervous system). Every memory write, update, delete, or
9
- recall triggers an event that subscribed agents and the dashboard receive.
10
-
11
- Architecture:
12
- memory_store_v2.py (write) → EventBus.emit()
13
- ├── SQLite memory_events table (persistence)
14
- ├── In-memory listeners (real-time delivery)
15
- │ ├── SSE endpoint (dashboard, MCP clients)
16
- │ ├── WebSocket (real-time agents)
17
- │ └── Webhook dispatcher (external services)
18
- └── Tiered retention (hot → warm → cold → archive)
19
-
20
- Event Types:
21
- memory.created — New memory written
22
- memory.updated — Existing memory modified
23
- memory.deleted — Memory removed
24
- memory.recalled — Memory retrieved by an agent
25
- graph.updated — Knowledge graph rebuilt
26
- pattern.learned — New pattern detected
27
- agent.connected — New agent connects
28
- agent.disconnected — Agent disconnects
29
-
30
- Retention Tiers:
31
- Hot (0-48h, configurable) — Full events, fully queryable
32
- Warm (2-14d, configurable) — Key events only (importance >= 5)
33
- Cold (14-30d, configurable) — Daily aggregates only
34
- Archive (30d+) — Pruned, stats in pattern_learner
35
- """
36
-
37
- import json
38
- import logging
39
- import threading
40
- import time
41
- from collections import deque
42
- from datetime import datetime, timedelta
43
- from pathlib import Path
44
- from typing import Optional, List, Dict, Any, Callable
45
-
46
- logger = logging.getLogger("superlocalmemory.events")
47
-
48
- # Default retention windows (in hours)
49
- DEFAULT_HOT_HOURS = 48
50
- DEFAULT_WARM_HOURS = 14 * 24 # 14 days
51
- DEFAULT_COLD_HOURS = 30 * 24 # 30 days
52
-
53
- # In-memory buffer size for real-time delivery
54
- EVENT_BUFFER_SIZE = 200
55
-
56
- # Valid event types
57
- VALID_EVENT_TYPES = frozenset([
58
- "memory.created",
59
- "memory.updated",
60
- "memory.deleted",
61
- "memory.recalled",
62
- "graph.updated",
63
- "pattern.learned",
64
- "agent.connected",
65
- "agent.disconnected",
66
- ])
67
-
68
-
69
- class EventBus:
70
- """
71
- Central event bus for SuperLocalMemory.
72
-
73
- Singleton per database path. Emits events to persistent storage and
74
- in-memory listeners simultaneously.
75
-
76
- Thread-safe: emit() can be called from any thread.
77
- Listener callbacks run on the emitter's thread — keep them fast.
78
- For heavy work, listeners should enqueue to their own async queue.
79
- """
80
-
81
- _instances: Dict[str, "EventBus"] = {}
82
- _instances_lock = threading.Lock()
83
-
84
- @classmethod
85
- def get_instance(cls, db_path: Optional[Path] = None) -> "EventBus":
86
- """Get or create the singleton EventBus for a database path."""
87
- if db_path is None:
88
- db_path = Path.home() / ".claude-memory" / "memory.db"
89
-
90
- key = str(db_path)
91
- with cls._instances_lock:
92
- if key not in cls._instances:
93
- cls._instances[key] = cls(db_path)
94
- return cls._instances[key]
95
-
96
- @classmethod
97
- def reset_instance(cls, db_path: Optional[Path] = None) -> None:
98
- """Remove and close a singleton instance. Used for testing."""
99
- with cls._instances_lock:
100
- if db_path is None:
101
- cls._instances.clear()
102
- else:
103
- key = str(db_path)
104
- if key in cls._instances:
105
- del cls._instances[key]
106
-
107
- def __init__(self, db_path: Path):
108
- """Initialize EventBus. Use get_instance() instead of calling directly."""
109
- self.db_path = Path(db_path)
110
-
111
- # In-memory event buffer for real-time delivery (thread-safe deque)
112
- self._buffer: deque = deque(maxlen=EVENT_BUFFER_SIZE)
113
- self._buffer_lock = threading.Lock()
114
-
115
- # Event counter (monotonic, reset on restart — DB id is authoritative)
116
- self._event_counter = 0
117
- self._counter_lock = threading.Lock()
118
-
119
- # Listeners: list of callbacks called on every event
120
- # Signature: callback(event: dict) -> None
121
- self._listeners: List[Callable[[dict], None]] = []
122
- self._listeners_lock = threading.Lock()
123
-
124
- # Auto-prune tracking: lightweight heuristic trigger
125
- self._write_count = 0
126
- self._last_prune = datetime.now()
127
-
128
- # Initialize schema
129
- self._init_schema()
130
-
131
- logger.info("EventBus initialized: db=%s", self.db_path)
132
-
133
- def _init_schema(self):
134
- """Create the memory_events table if it doesn't exist."""
135
- try:
136
- from db_connection_manager import DbConnectionManager
137
- mgr = DbConnectionManager.get_instance(self.db_path)
138
-
139
- def _create_table(conn):
140
- cursor = conn.cursor()
141
- cursor.execute('''
142
- CREATE TABLE IF NOT EXISTS memory_events (
143
- id INTEGER PRIMARY KEY AUTOINCREMENT,
144
- event_type TEXT NOT NULL,
145
- memory_id INTEGER,
146
- source_agent TEXT DEFAULT 'user',
147
- source_protocol TEXT DEFAULT 'internal',
148
- payload TEXT,
149
- importance INTEGER DEFAULT 5,
150
- tier TEXT DEFAULT 'hot',
151
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
152
- )
153
- ''')
154
- # Index for efficient querying and pruning
155
- cursor.execute('''
156
- CREATE INDEX IF NOT EXISTS idx_events_type
157
- ON memory_events(event_type)
158
- ''')
159
- cursor.execute('''
160
- CREATE INDEX IF NOT EXISTS idx_events_created
161
- ON memory_events(created_at)
162
- ''')
163
- cursor.execute('''
164
- CREATE INDEX IF NOT EXISTS idx_events_tier
165
- ON memory_events(tier)
166
- ''')
167
- conn.commit()
168
-
169
- mgr.execute_write(_create_table)
170
- except ImportError:
171
- # Fallback: direct connection
172
- import sqlite3
173
- conn = sqlite3.connect(str(self.db_path))
174
- try:
175
- cursor = conn.cursor()
176
- cursor.execute('''
177
- CREATE TABLE IF NOT EXISTS memory_events (
178
- id INTEGER PRIMARY KEY AUTOINCREMENT,
179
- event_type TEXT NOT NULL,
180
- memory_id INTEGER,
181
- source_agent TEXT DEFAULT 'user',
182
- source_protocol TEXT DEFAULT 'internal',
183
- payload TEXT,
184
- importance INTEGER DEFAULT 5,
185
- tier TEXT DEFAULT 'hot',
186
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
187
- )
188
- ''')
189
- cursor.execute('CREATE INDEX IF NOT EXISTS idx_events_type ON memory_events(event_type)')
190
- cursor.execute('CREATE INDEX IF NOT EXISTS idx_events_created ON memory_events(created_at)')
191
- cursor.execute('CREATE INDEX IF NOT EXISTS idx_events_tier ON memory_events(tier)')
192
- conn.commit()
193
- finally:
194
- conn.close()
195
-
196
- # =========================================================================
197
- # Event Emission
198
- # =========================================================================
199
-
200
- def emit(
201
- self,
202
- event_type: str,
203
- payload: Optional[Dict[str, Any]] = None,
204
- memory_id: Optional[int] = None,
205
- source_agent: str = "user",
206
- source_protocol: str = "internal",
207
- importance: int = 5,
208
- ) -> Optional[int]:
209
- """
210
- Emit an event to all subscribers and persist to database.
211
-
212
- Args:
213
- event_type: One of VALID_EVENT_TYPES (e.g., "memory.created")
214
- payload: Event-specific data (dict, serialized to JSON)
215
- memory_id: Associated memory ID (if applicable)
216
- source_agent: Agent that triggered the event
217
- source_protocol: Protocol used (mcp, cli, rest, python)
218
- importance: Event importance 1-10 (affects retention)
219
-
220
- Returns:
221
- Event ID from database, or None if persistence failed
222
-
223
- Raises:
224
- ValueError: If event_type is not in VALID_EVENT_TYPES
225
- """
226
- if event_type not in VALID_EVENT_TYPES:
227
- raise ValueError(
228
- f"Invalid event type: {event_type}. "
229
- f"Valid types: {', '.join(sorted(VALID_EVENT_TYPES))}"
230
- )
231
-
232
- # Clamp importance
233
- importance = max(1, min(10, importance))
234
-
235
- # Build event dict
236
- now = datetime.now().isoformat()
237
- with self._counter_lock:
238
- self._event_counter += 1
239
- seq = self._event_counter
240
-
241
- event = {
242
- "seq": seq,
243
- "event_type": event_type,
244
- "memory_id": memory_id,
245
- "source_agent": source_agent,
246
- "source_protocol": source_protocol,
247
- "payload": payload or {},
248
- "importance": importance,
249
- "timestamp": now,
250
- }
251
-
252
- # 1. Persist to database (non-blocking if it fails)
253
- event_id = self._persist_event(event)
254
- if event_id:
255
- event["id"] = event_id
256
-
257
- # 2. Add to in-memory buffer
258
- with self._buffer_lock:
259
- self._buffer.append(event)
260
-
261
- # 3. Notify all listeners
262
- self._notify_listeners(event)
263
-
264
- logger.debug("Event emitted: type=%s, id=%s, memory_id=%s", event_type, event_id, memory_id)
265
-
266
- # Auto-prune every 100 events or every 24 hours, whichever comes first
267
- self._write_count += 1
268
- if self._write_count >= 100 or (datetime.now() - self._last_prune).total_seconds() > 86400:
269
- try:
270
- self.prune_events()
271
- self._write_count = 0
272
- self._last_prune = datetime.now()
273
- except Exception:
274
- pass # Don't let prune failures block event emission
275
-
276
- return event_id
277
-
278
- def _persist_event(self, event: dict) -> Optional[int]:
279
- """Persist event to memory_events table. Returns event ID or None."""
280
- try:
281
- from db_connection_manager import DbConnectionManager
282
- mgr = DbConnectionManager.get_instance(self.db_path)
283
-
284
- def _insert(conn):
285
- cursor = conn.cursor()
286
- cursor.execute('''
287
- INSERT INTO memory_events
288
- (event_type, memory_id, source_agent, source_protocol,
289
- payload, importance, tier, created_at)
290
- VALUES (?, ?, ?, ?, ?, ?, 'hot', ?)
291
- ''', (
292
- event["event_type"],
293
- event.get("memory_id"),
294
- event["source_agent"],
295
- event["source_protocol"],
296
- json.dumps(event["payload"]),
297
- event["importance"],
298
- event["timestamp"],
299
- ))
300
- conn.commit()
301
- return cursor.lastrowid
302
-
303
- return mgr.execute_write(_insert)
304
-
305
- except Exception as e:
306
- # Event persistence failure must NEVER break core operations
307
- logger.error("Failed to persist event: %s", e)
308
- return None
309
-
310
- # =========================================================================
311
- # Listener Management
312
- # =========================================================================
313
-
314
- def add_listener(self, callback: Callable[[dict], None]) -> None:
315
- """
316
- Register a listener that receives every emitted event.
317
-
318
- Callbacks run on the emitter's thread — keep them fast and non-blocking.
319
- For async/heavy work, the callback should enqueue to its own queue.
320
-
321
- Args:
322
- callback: Function(event_dict) called on every emit()
323
- """
324
- with self._listeners_lock:
325
- self._listeners.append(callback)
326
-
327
- def remove_listener(self, callback: Callable[[dict], None]) -> None:
328
- """Remove a previously registered listener."""
329
- with self._listeners_lock:
330
- try:
331
- self._listeners.remove(callback)
332
- except ValueError:
333
- pass
334
-
335
- def _notify_listeners(self, event: dict):
336
- """Call all registered listeners. Errors are logged, not raised."""
337
- with self._listeners_lock:
338
- listeners = list(self._listeners)
339
-
340
- for listener in listeners:
341
- try:
342
- listener(event)
343
- except Exception as e:
344
- logger.error("Event listener failed: %s", e)
345
-
346
- # =========================================================================
347
- # Event Retrieval (for replay, SSE, polling)
348
- # =========================================================================
349
-
350
- def get_recent_events(
351
- self,
352
- since_id: Optional[int] = None,
353
- limit: int = 50,
354
- event_type: Optional[str] = None,
355
- ) -> List[dict]:
356
- """
357
- Get recent events from the database.
358
-
359
- Used for:
360
- - SSE replay on reconnect (client sends Last-Event-ID)
361
- - Dashboard polling
362
- - Subscription replay (durable subscribers reconnecting)
363
-
364
- Args:
365
- since_id: Return events with ID greater than this (for replay)
366
- limit: Maximum events to return (default 50, max 200)
367
- event_type: Filter by event type (optional)
368
-
369
- Returns:
370
- List of event dicts, ordered by ID ascending
371
- """
372
- limit = min(limit, 200)
373
-
374
- try:
375
- from db_connection_manager import DbConnectionManager
376
- mgr = DbConnectionManager.get_instance(self.db_path)
377
-
378
- with mgr.read_connection() as conn:
379
- cursor = conn.cursor()
380
-
381
- query = "SELECT id, event_type, memory_id, source_agent, source_protocol, payload, importance, tier, created_at FROM memory_events WHERE 1=1"
382
- params = []
383
-
384
- if since_id is not None:
385
- query += " AND id > ?"
386
- params.append(since_id)
387
-
388
- if event_type:
389
- query += " AND event_type = ?"
390
- params.append(event_type)
391
-
392
- query += " ORDER BY id ASC LIMIT ?"
393
- params.append(limit)
394
-
395
- cursor.execute(query, params)
396
- rows = cursor.fetchall()
397
-
398
- events = []
399
- for row in rows:
400
- payload = row[5]
401
- try:
402
- payload = json.loads(payload) if payload else {}
403
- except (json.JSONDecodeError, TypeError):
404
- payload = {}
405
-
406
- events.append({
407
- "id": row[0],
408
- "event_type": row[1],
409
- "memory_id": row[2],
410
- "source_agent": row[3],
411
- "source_protocol": row[4],
412
- "payload": payload,
413
- "importance": row[6],
414
- "tier": row[7],
415
- "timestamp": row[8],
416
- })
417
-
418
- return events
419
-
420
- except Exception as e:
421
- logger.error("Failed to get recent events: %s", e)
422
- return []
423
-
424
- def get_buffered_events(self, since_seq: int = 0) -> List[dict]:
425
- """
426
- Get events from the in-memory buffer (fast, no DB hit).
427
-
428
- Used for real-time SSE/WebSocket delivery.
429
-
430
- Args:
431
- since_seq: Return events with seq > this value
432
-
433
- Returns:
434
- List of event dicts from the buffer
435
- """
436
- with self._buffer_lock:
437
- return [e for e in self._buffer if e.get("seq", 0) > since_seq]
438
-
439
- def get_event_stats(self) -> dict:
440
- """Get event system statistics."""
441
- try:
442
- from db_connection_manager import DbConnectionManager
443
- mgr = DbConnectionManager.get_instance(self.db_path)
444
-
445
- with mgr.read_connection() as conn:
446
- cursor = conn.cursor()
447
-
448
- cursor.execute("SELECT COUNT(*) FROM memory_events")
449
- total = cursor.fetchone()[0]
450
-
451
- cursor.execute("""
452
- SELECT event_type, COUNT(*) as count
453
- FROM memory_events
454
- GROUP BY event_type
455
- ORDER BY count DESC
456
- """)
457
- by_type = dict(cursor.fetchall())
458
-
459
- cursor.execute("""
460
- SELECT tier, COUNT(*) as count
461
- FROM memory_events
462
- GROUP BY tier
463
- """)
464
- by_tier = dict(cursor.fetchall())
465
-
466
- cursor.execute("""
467
- SELECT COUNT(*) FROM memory_events
468
- WHERE created_at >= datetime('now', '-24 hours')
469
- """)
470
- last_24h = cursor.fetchone()[0]
471
-
472
- return {
473
- "total_events": total,
474
- "events_last_24h": last_24h,
475
- "by_type": by_type,
476
- "by_tier": by_tier,
477
- "buffer_size": len(self._buffer),
478
- "listener_count": len(self._listeners),
479
- }
480
-
481
- except Exception as e:
482
- logger.error("Failed to get event stats: %s", e)
483
- return {"total_events": 0, "error": str(e)}
484
-
485
- # =========================================================================
486
- # Tiered Retention (pruning)
487
- # =========================================================================
488
-
489
- def prune_events(
490
- self,
491
- hot_hours: int = DEFAULT_HOT_HOURS,
492
- warm_hours: int = DEFAULT_WARM_HOURS,
493
- cold_hours: int = DEFAULT_COLD_HOURS,
494
- ) -> dict:
495
- """
496
- Apply tiered retention policy to events.
497
-
498
- Tiers:
499
- Hot (0-48h) — Keep all events
500
- Warm (2-14d) — Keep events with importance >= 5
501
- Cold (14-30d) — Keep daily aggregates only
502
- Archive (30d+) — Delete (stats preserved in pattern_learner)
503
-
504
- Args:
505
- hot_hours: Hours to keep all events (default 48)
506
- warm_hours: Hours before warm→cold transition (default 336 / 14 days)
507
- cold_hours: Hours before cold→archive (default 720 / 30 days)
508
-
509
- Returns:
510
- Dict with counts of events transitioned/pruned per tier
511
- """
512
- try:
513
- from db_connection_manager import DbConnectionManager
514
- mgr = DbConnectionManager.get_instance(self.db_path)
515
-
516
- stats = {"hot_to_warm": 0, "warm_to_cold": 0, "archived": 0}
517
-
518
- def _do_prune(conn):
519
- cursor = conn.cursor()
520
- now = datetime.now()
521
-
522
- # Hot → Warm: events older than hot_hours with importance < 5
523
- warm_cutoff = (now - timedelta(hours=hot_hours)).isoformat()
524
- cursor.execute("""
525
- UPDATE memory_events
526
- SET tier = 'warm'
527
- WHERE tier = 'hot'
528
- AND created_at < ?
529
- AND importance < 5
530
- """, (warm_cutoff,))
531
- stats["hot_to_warm"] = cursor.rowcount
532
-
533
- # Warm → Cold: events older than warm_hours
534
- cold_cutoff = (now - timedelta(hours=warm_hours)).isoformat()
535
- cursor.execute("""
536
- DELETE FROM memory_events
537
- WHERE tier = 'warm'
538
- AND created_at < ?
539
- """, (cold_cutoff,))
540
- stats["warm_to_cold"] = cursor.rowcount
541
-
542
- # Archive: delete events older than cold_hours
543
- archive_cutoff = (now - timedelta(hours=cold_hours)).isoformat()
544
- cursor.execute("""
545
- DELETE FROM memory_events
546
- WHERE created_at < ?
547
- """, (archive_cutoff,))
548
- stats["archived"] = cursor.rowcount
549
-
550
- conn.commit()
551
-
552
- mgr.execute_write(_do_prune)
553
-
554
- logger.info(
555
- "Event pruning complete: hot→warm=%d, warm→cold=%d, archived=%d",
556
- stats["hot_to_warm"], stats["warm_to_cold"], stats["archived"]
557
- )
558
- return stats
559
-
560
- except Exception as e:
561
- logger.error("Event pruning failed: %s", e)
562
- return {"error": str(e)}