superlocalmemory 2.8.5 → 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 (434) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +9 -1
  3. package/NOTICE +63 -0
  4. package/README.md +165 -480
  5. package/bin/slm +17 -449
  6. package/bin/slm-npm +2 -2
  7. package/bin/slm.bat +4 -2
  8. package/conftest.py +5 -0
  9. package/docs/api-reference.md +284 -0
  10. package/docs/architecture.md +149 -0
  11. package/docs/auto-memory.md +150 -0
  12. package/docs/cli-reference.md +276 -0
  13. package/docs/compliance.md +191 -0
  14. package/docs/configuration.md +182 -0
  15. package/docs/getting-started.md +102 -0
  16. package/docs/ide-setup.md +261 -0
  17. package/docs/mcp-tools.md +220 -0
  18. package/docs/migration-from-v2.md +170 -0
  19. package/docs/profiles.md +173 -0
  20. package/docs/troubleshooting.md +310 -0
  21. package/{configs → ide/configs}/antigravity-mcp.json +3 -3
  22. package/ide/configs/chatgpt-desktop-mcp.json +16 -0
  23. package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
  24. package/{configs → ide/configs}/codex-mcp.toml +4 -4
  25. package/{configs → ide/configs}/continue-mcp.yaml +4 -3
  26. package/{configs → ide/configs}/continue-skills.yaml +6 -6
  27. package/ide/configs/cursor-mcp.json +15 -0
  28. package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
  29. package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
  30. package/{configs → ide/configs}/opencode-mcp.json +2 -2
  31. package/{configs → ide/configs}/perplexity-mcp.json +2 -2
  32. package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
  33. package/{configs → ide/configs}/windsurf-mcp.json +3 -3
  34. package/{configs → ide/configs}/zed-mcp.json +2 -2
  35. package/{hooks → ide/hooks}/context-hook.js +9 -20
  36. package/ide/hooks/memory-list-skill.js +70 -0
  37. package/ide/hooks/memory-profile-skill.js +101 -0
  38. package/ide/hooks/memory-recall-skill.js +62 -0
  39. package/ide/hooks/memory-remember-skill.js +68 -0
  40. package/ide/hooks/memory-reset-skill.js +160 -0
  41. package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
  42. package/ide/integrations/langchain/README.md +106 -0
  43. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
  44. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
  45. package/ide/integrations/langchain/pyproject.toml +38 -0
  46. package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
  47. package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
  48. package/ide/integrations/langchain/tests/test_security.py +117 -0
  49. package/ide/integrations/llamaindex/README.md +81 -0
  50. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
  51. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
  52. package/ide/integrations/llamaindex/pyproject.toml +43 -0
  53. package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
  54. package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
  55. package/ide/integrations/llamaindex/tests/test_security.py +241 -0
  56. package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
  57. package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
  58. package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
  59. package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
  60. package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
  61. package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
  62. package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
  63. package/package.json +13 -22
  64. package/pyproject.toml +85 -0
  65. package/scripts/build-dmg.sh +417 -0
  66. package/scripts/install-skills.ps1 +334 -0
  67. package/{install.ps1 → scripts/install.ps1} +36 -4
  68. package/{install.sh → scripts/install.sh} +14 -13
  69. package/scripts/postinstall.js +2 -2
  70. package/scripts/start-dashboard.ps1 +52 -0
  71. package/scripts/start-dashboard.sh +41 -0
  72. package/scripts/sync-wiki.ps1 +127 -0
  73. package/scripts/sync-wiki.sh +82 -0
  74. package/scripts/test-dmg.sh +161 -0
  75. package/scripts/test-npm-package.ps1 +252 -0
  76. package/scripts/test-npm-package.sh +207 -0
  77. package/scripts/verify-install.ps1 +294 -0
  78. package/scripts/verify-install.sh +266 -0
  79. package/src/superlocalmemory/__init__.py +0 -0
  80. package/src/superlocalmemory/attribution/__init__.py +9 -0
  81. package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
  82. package/src/superlocalmemory/attribution/signer.py +153 -0
  83. package/src/superlocalmemory/attribution/watermark.py +189 -0
  84. package/src/superlocalmemory/cli/__init__.py +5 -0
  85. package/src/superlocalmemory/cli/commands.py +245 -0
  86. package/src/superlocalmemory/cli/main.py +89 -0
  87. package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
  88. package/src/superlocalmemory/cli/post_install.py +99 -0
  89. package/src/superlocalmemory/cli/setup_wizard.py +129 -0
  90. package/src/superlocalmemory/compliance/__init__.py +0 -0
  91. package/src/superlocalmemory/compliance/abac.py +204 -0
  92. package/src/superlocalmemory/compliance/audit.py +314 -0
  93. package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
  94. package/src/superlocalmemory/compliance/gdpr.py +294 -0
  95. package/src/superlocalmemory/compliance/lifecycle.py +158 -0
  96. package/src/superlocalmemory/compliance/retention.py +232 -0
  97. package/src/superlocalmemory/compliance/scheduler.py +148 -0
  98. package/src/superlocalmemory/core/__init__.py +0 -0
  99. package/src/superlocalmemory/core/config.py +391 -0
  100. package/src/superlocalmemory/core/embeddings.py +293 -0
  101. package/src/superlocalmemory/core/engine.py +701 -0
  102. package/src/superlocalmemory/core/hooks.py +65 -0
  103. package/src/superlocalmemory/core/maintenance.py +172 -0
  104. package/src/superlocalmemory/core/modes.py +140 -0
  105. package/src/superlocalmemory/core/profiles.py +234 -0
  106. package/src/superlocalmemory/core/registry.py +117 -0
  107. package/src/superlocalmemory/dynamics/__init__.py +0 -0
  108. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
  109. package/src/superlocalmemory/encoding/__init__.py +0 -0
  110. package/src/superlocalmemory/encoding/consolidator.py +485 -0
  111. package/src/superlocalmemory/encoding/emotional.py +125 -0
  112. package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
  113. package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
  114. package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
  115. package/src/superlocalmemory/encoding/foresight.py +91 -0
  116. package/src/superlocalmemory/encoding/graph_builder.py +302 -0
  117. package/src/superlocalmemory/encoding/observation_builder.py +160 -0
  118. package/src/superlocalmemory/encoding/scene_builder.py +183 -0
  119. package/src/superlocalmemory/encoding/signal_inference.py +90 -0
  120. package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
  121. package/src/superlocalmemory/encoding/type_router.py +235 -0
  122. package/src/superlocalmemory/hooks/__init__.py +3 -0
  123. package/src/superlocalmemory/hooks/auto_capture.py +111 -0
  124. package/src/superlocalmemory/hooks/auto_recall.py +93 -0
  125. package/src/superlocalmemory/hooks/ide_connector.py +204 -0
  126. package/src/superlocalmemory/hooks/rules_engine.py +99 -0
  127. package/src/superlocalmemory/infra/__init__.py +3 -0
  128. package/src/superlocalmemory/infra/auth_middleware.py +82 -0
  129. package/src/superlocalmemory/infra/backup.py +317 -0
  130. package/src/superlocalmemory/infra/cache_manager.py +267 -0
  131. package/src/superlocalmemory/infra/event_bus.py +381 -0
  132. package/src/superlocalmemory/infra/rate_limiter.py +135 -0
  133. package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
  134. package/src/superlocalmemory/learning/__init__.py +0 -0
  135. package/src/superlocalmemory/learning/adaptive.py +172 -0
  136. package/src/superlocalmemory/learning/behavioral.py +490 -0
  137. package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
  138. package/src/superlocalmemory/learning/bootstrap.py +298 -0
  139. package/src/superlocalmemory/learning/cross_project.py +399 -0
  140. package/src/superlocalmemory/learning/database.py +376 -0
  141. package/src/superlocalmemory/learning/engagement.py +323 -0
  142. package/src/superlocalmemory/learning/features.py +138 -0
  143. package/src/superlocalmemory/learning/feedback.py +316 -0
  144. package/src/superlocalmemory/learning/outcomes.py +255 -0
  145. package/src/superlocalmemory/learning/project_context.py +366 -0
  146. package/src/superlocalmemory/learning/ranker.py +155 -0
  147. package/src/superlocalmemory/learning/source_quality.py +303 -0
  148. package/src/superlocalmemory/learning/workflows.py +309 -0
  149. package/src/superlocalmemory/llm/__init__.py +0 -0
  150. package/src/superlocalmemory/llm/backbone.py +316 -0
  151. package/src/superlocalmemory/math/__init__.py +0 -0
  152. package/src/superlocalmemory/math/fisher.py +356 -0
  153. package/src/superlocalmemory/math/langevin.py +398 -0
  154. package/src/superlocalmemory/math/sheaf.py +257 -0
  155. package/src/superlocalmemory/mcp/__init__.py +0 -0
  156. package/src/superlocalmemory/mcp/resources.py +245 -0
  157. package/src/superlocalmemory/mcp/server.py +61 -0
  158. package/src/superlocalmemory/mcp/tools.py +18 -0
  159. package/src/superlocalmemory/mcp/tools_core.py +305 -0
  160. package/src/superlocalmemory/mcp/tools_v28.py +223 -0
  161. package/src/superlocalmemory/mcp/tools_v3.py +286 -0
  162. package/src/superlocalmemory/retrieval/__init__.py +0 -0
  163. package/src/superlocalmemory/retrieval/agentic.py +295 -0
  164. package/src/superlocalmemory/retrieval/ann_index.py +223 -0
  165. package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
  166. package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
  167. package/src/superlocalmemory/retrieval/engine.py +390 -0
  168. package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
  169. package/src/superlocalmemory/retrieval/fusion.py +78 -0
  170. package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
  171. package/src/superlocalmemory/retrieval/reranker.py +154 -0
  172. package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
  173. package/src/superlocalmemory/retrieval/strategy.py +96 -0
  174. package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
  175. package/src/superlocalmemory/server/__init__.py +1 -0
  176. package/src/superlocalmemory/server/api.py +248 -0
  177. package/src/superlocalmemory/server/routes/__init__.py +4 -0
  178. package/src/superlocalmemory/server/routes/agents.py +107 -0
  179. package/src/superlocalmemory/server/routes/backup.py +91 -0
  180. package/src/superlocalmemory/server/routes/behavioral.py +127 -0
  181. package/src/superlocalmemory/server/routes/compliance.py +160 -0
  182. package/src/superlocalmemory/server/routes/data_io.py +188 -0
  183. package/src/superlocalmemory/server/routes/events.py +183 -0
  184. package/src/superlocalmemory/server/routes/helpers.py +85 -0
  185. package/src/superlocalmemory/server/routes/learning.py +273 -0
  186. package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
  187. package/src/superlocalmemory/server/routes/memories.py +399 -0
  188. package/src/superlocalmemory/server/routes/profiles.py +219 -0
  189. package/src/superlocalmemory/server/routes/stats.py +346 -0
  190. package/src/superlocalmemory/server/routes/v3_api.py +365 -0
  191. package/src/superlocalmemory/server/routes/ws.py +82 -0
  192. package/src/superlocalmemory/server/security_middleware.py +57 -0
  193. package/src/superlocalmemory/server/ui.py +245 -0
  194. package/src/superlocalmemory/storage/__init__.py +0 -0
  195. package/src/superlocalmemory/storage/access_control.py +182 -0
  196. package/src/superlocalmemory/storage/database.py +594 -0
  197. package/src/superlocalmemory/storage/migrations.py +303 -0
  198. package/src/superlocalmemory/storage/models.py +406 -0
  199. package/src/superlocalmemory/storage/schema.py +726 -0
  200. package/src/superlocalmemory/storage/v2_migrator.py +317 -0
  201. package/src/superlocalmemory/trust/__init__.py +0 -0
  202. package/src/superlocalmemory/trust/gate.py +130 -0
  203. package/src/superlocalmemory/trust/provenance.py +124 -0
  204. package/src/superlocalmemory/trust/scorer.py +347 -0
  205. package/src/superlocalmemory/trust/signals.py +153 -0
  206. package/ui/index.html +278 -5
  207. package/ui/js/auto-settings.js +70 -0
  208. package/ui/js/dashboard.js +90 -0
  209. package/ui/js/fact-detail.js +92 -0
  210. package/ui/js/feedback.js +2 -2
  211. package/ui/js/ide-status.js +102 -0
  212. package/ui/js/math-health.js +98 -0
  213. package/ui/js/recall-lab.js +127 -0
  214. package/ui/js/settings.js +2 -2
  215. package/ui/js/trust-dashboard.js +73 -0
  216. package/api_server.py +0 -724
  217. package/bin/aider-smart +0 -72
  218. package/bin/superlocalmemoryv2-learning +0 -4
  219. package/bin/superlocalmemoryv2-list +0 -3
  220. package/bin/superlocalmemoryv2-patterns +0 -4
  221. package/bin/superlocalmemoryv2-profile +0 -3
  222. package/bin/superlocalmemoryv2-recall +0 -3
  223. package/bin/superlocalmemoryv2-remember +0 -3
  224. package/bin/superlocalmemoryv2-reset +0 -3
  225. package/bin/superlocalmemoryv2-status +0 -3
  226. package/configs/chatgpt-desktop-mcp.json +0 -16
  227. package/configs/cursor-mcp.json +0 -15
  228. package/docs/SECURITY-QUICK-REFERENCE.md +0 -214
  229. package/hooks/memory-list-skill.js +0 -139
  230. package/hooks/memory-profile-skill.js +0 -273
  231. package/hooks/memory-recall-skill.js +0 -114
  232. package/hooks/memory-remember-skill.js +0 -127
  233. package/hooks/memory-reset-skill.js +0 -274
  234. package/mcp_server.py +0 -1800
  235. package/requirements-core.txt +0 -22
  236. package/requirements-learning.txt +0 -12
  237. package/requirements.txt +0 -12
  238. package/src/agent_registry.py +0 -411
  239. package/src/auth_middleware.py +0 -61
  240. package/src/auto_backup.py +0 -459
  241. package/src/behavioral/__init__.py +0 -49
  242. package/src/behavioral/behavioral_listener.py +0 -203
  243. package/src/behavioral/behavioral_patterns.py +0 -275
  244. package/src/behavioral/cross_project_transfer.py +0 -206
  245. package/src/behavioral/outcome_inference.py +0 -194
  246. package/src/behavioral/outcome_tracker.py +0 -193
  247. package/src/behavioral/tests/__init__.py +0 -4
  248. package/src/behavioral/tests/test_behavioral_integration.py +0 -108
  249. package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
  250. package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
  251. package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
  252. package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
  253. package/src/behavioral/tests/test_outcome_inference.py +0 -107
  254. package/src/behavioral/tests/test_outcome_tracker.py +0 -96
  255. package/src/cache_manager.py +0 -518
  256. package/src/compliance/__init__.py +0 -48
  257. package/src/compliance/abac_engine.py +0 -149
  258. package/src/compliance/abac_middleware.py +0 -116
  259. package/src/compliance/audit_db.py +0 -215
  260. package/src/compliance/audit_logger.py +0 -148
  261. package/src/compliance/retention_manager.py +0 -289
  262. package/src/compliance/retention_scheduler.py +0 -186
  263. package/src/compliance/tests/__init__.py +0 -4
  264. package/src/compliance/tests/test_abac_enforcement.py +0 -95
  265. package/src/compliance/tests/test_abac_engine.py +0 -124
  266. package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
  267. package/src/compliance/tests/test_audit_db.py +0 -123
  268. package/src/compliance/tests/test_audit_logger.py +0 -98
  269. package/src/compliance/tests/test_mcp_audit.py +0 -128
  270. package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
  271. package/src/compliance/tests/test_retention_manager.py +0 -131
  272. package/src/compliance/tests/test_retention_scheduler.py +0 -99
  273. package/src/compression/__init__.py +0 -25
  274. package/src/compression/cli.py +0 -150
  275. package/src/compression/cold_storage.py +0 -217
  276. package/src/compression/config.py +0 -72
  277. package/src/compression/orchestrator.py +0 -133
  278. package/src/compression/tier2_compressor.py +0 -228
  279. package/src/compression/tier3_compressor.py +0 -153
  280. package/src/compression/tier_classifier.py +0 -148
  281. package/src/db_connection_manager.py +0 -536
  282. package/src/embedding_engine.py +0 -63
  283. package/src/embeddings/__init__.py +0 -47
  284. package/src/embeddings/cache.py +0 -70
  285. package/src/embeddings/cli.py +0 -113
  286. package/src/embeddings/constants.py +0 -47
  287. package/src/embeddings/database.py +0 -91
  288. package/src/embeddings/engine.py +0 -247
  289. package/src/embeddings/model_loader.py +0 -145
  290. package/src/event_bus.py +0 -562
  291. package/src/graph/__init__.py +0 -36
  292. package/src/graph/build_helpers.py +0 -74
  293. package/src/graph/cli.py +0 -87
  294. package/src/graph/cluster_builder.py +0 -188
  295. package/src/graph/cluster_summary.py +0 -148
  296. package/src/graph/constants.py +0 -47
  297. package/src/graph/edge_builder.py +0 -162
  298. package/src/graph/entity_extractor.py +0 -95
  299. package/src/graph/graph_core.py +0 -226
  300. package/src/graph/graph_search.py +0 -231
  301. package/src/graph/hierarchical.py +0 -207
  302. package/src/graph/schema.py +0 -99
  303. package/src/graph_engine.py +0 -52
  304. package/src/hnsw_index.py +0 -628
  305. package/src/hybrid_search.py +0 -46
  306. package/src/learning/__init__.py +0 -217
  307. package/src/learning/adaptive_ranker.py +0 -682
  308. package/src/learning/bootstrap/__init__.py +0 -69
  309. package/src/learning/bootstrap/constants.py +0 -93
  310. package/src/learning/bootstrap/db_queries.py +0 -316
  311. package/src/learning/bootstrap/sampling.py +0 -82
  312. package/src/learning/bootstrap/text_utils.py +0 -71
  313. package/src/learning/cross_project_aggregator.py +0 -857
  314. package/src/learning/db/__init__.py +0 -40
  315. package/src/learning/db/constants.py +0 -44
  316. package/src/learning/db/schema.py +0 -279
  317. package/src/learning/engagement_tracker.py +0 -628
  318. package/src/learning/feature_extractor.py +0 -708
  319. package/src/learning/feedback_collector.py +0 -806
  320. package/src/learning/learning_db.py +0 -915
  321. package/src/learning/project_context_manager.py +0 -572
  322. package/src/learning/ranking/__init__.py +0 -33
  323. package/src/learning/ranking/constants.py +0 -84
  324. package/src/learning/ranking/helpers.py +0 -278
  325. package/src/learning/source_quality_scorer.py +0 -676
  326. package/src/learning/synthetic_bootstrap.py +0 -755
  327. package/src/learning/tests/test_adaptive_ranker.py +0 -325
  328. package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
  329. package/src/learning/tests/test_aggregator.py +0 -306
  330. package/src/learning/tests/test_auto_retrain_v28.py +0 -35
  331. package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
  332. package/src/learning/tests/test_feature_extractor_v28.py +0 -93
  333. package/src/learning/tests/test_feedback_collector.py +0 -294
  334. package/src/learning/tests/test_learning_db.py +0 -602
  335. package/src/learning/tests/test_learning_db_v28.py +0 -110
  336. package/src/learning/tests/test_learning_init_v28.py +0 -48
  337. package/src/learning/tests/test_outcome_signals.py +0 -48
  338. package/src/learning/tests/test_project_context.py +0 -292
  339. package/src/learning/tests/test_schema_migration.py +0 -319
  340. package/src/learning/tests/test_signal_inference.py +0 -397
  341. package/src/learning/tests/test_source_quality.py +0 -351
  342. package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
  343. package/src/learning/tests/test_workflow_miner.py +0 -318
  344. package/src/learning/workflow_pattern_miner.py +0 -655
  345. package/src/lifecycle/__init__.py +0 -54
  346. package/src/lifecycle/bounded_growth.py +0 -239
  347. package/src/lifecycle/compaction_engine.py +0 -226
  348. package/src/lifecycle/lifecycle_engine.py +0 -355
  349. package/src/lifecycle/lifecycle_evaluator.py +0 -257
  350. package/src/lifecycle/lifecycle_scheduler.py +0 -130
  351. package/src/lifecycle/retention_policy.py +0 -285
  352. package/src/lifecycle/tests/test_bounded_growth.py +0 -193
  353. package/src/lifecycle/tests/test_compaction.py +0 -179
  354. package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
  355. package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
  356. package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
  357. package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
  358. package/src/lifecycle/tests/test_mcp_compact.py +0 -149
  359. package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
  360. package/src/lifecycle/tests/test_retention_policy.py +0 -162
  361. package/src/mcp_tools_v28.py +0 -281
  362. package/src/memory/__init__.py +0 -36
  363. package/src/memory/cli.py +0 -205
  364. package/src/memory/constants.py +0 -39
  365. package/src/memory/helpers.py +0 -28
  366. package/src/memory/schema.py +0 -166
  367. package/src/memory-profiles.py +0 -595
  368. package/src/memory-reset.py +0 -491
  369. package/src/memory_compression.py +0 -989
  370. package/src/memory_store_v2.py +0 -1155
  371. package/src/migrate_v1_to_v2.py +0 -629
  372. package/src/pattern_learner.py +0 -34
  373. package/src/patterns/__init__.py +0 -24
  374. package/src/patterns/analyzers.py +0 -251
  375. package/src/patterns/learner.py +0 -271
  376. package/src/patterns/scoring.py +0 -171
  377. package/src/patterns/store.py +0 -225
  378. package/src/patterns/terminology.py +0 -140
  379. package/src/provenance_tracker.py +0 -312
  380. package/src/qualixar_attribution.py +0 -139
  381. package/src/qualixar_watermark.py +0 -78
  382. package/src/query_optimizer.py +0 -511
  383. package/src/rate_limiter.py +0 -83
  384. package/src/search/__init__.py +0 -20
  385. package/src/search/cli.py +0 -77
  386. package/src/search/constants.py +0 -26
  387. package/src/search/engine.py +0 -241
  388. package/src/search/fusion.py +0 -122
  389. package/src/search/index_loader.py +0 -114
  390. package/src/search/methods.py +0 -162
  391. package/src/search_engine_v2.py +0 -401
  392. package/src/setup_validator.py +0 -482
  393. package/src/subscription_manager.py +0 -391
  394. package/src/tree/__init__.py +0 -59
  395. package/src/tree/builder.py +0 -185
  396. package/src/tree/nodes.py +0 -202
  397. package/src/tree/queries.py +0 -257
  398. package/src/tree/schema.py +0 -80
  399. package/src/tree_manager.py +0 -19
  400. package/src/trust/__init__.py +0 -45
  401. package/src/trust/constants.py +0 -66
  402. package/src/trust/queries.py +0 -157
  403. package/src/trust/schema.py +0 -95
  404. package/src/trust/scorer.py +0 -299
  405. package/src/trust/signals.py +0 -95
  406. package/src/trust_scorer.py +0 -44
  407. package/ui/app.js +0 -1588
  408. package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
  409. package/ui/js/graph-cytoscape.js +0 -1168
  410. package/ui/js/graph-d3-backup.js +0 -32
  411. package/ui/js/graph.js +0 -32
  412. package/ui_server.py +0 -266
  413. /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
  414. /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
  415. /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
  416. /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
  417. /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
  418. /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
  419. /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
  420. /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
  421. /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
  422. /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
  423. /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
  424. /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
  425. /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
  426. /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
  427. /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
  428. /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
  429. /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
  430. /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
  431. /package/{completions → ide/completions}/slm.bash +0 -0
  432. /package/{completions → ide/completions}/slm.zsh +0 -0
  433. /package/{configs → ide/configs}/cody-commands.json +0 -0
  434. /package/{install-skills.sh → scripts/install-skills.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)}