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
@@ -0,0 +1,490 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """SuperLocalMemory V3 — Behavioral Pattern Store.
6
+
7
+ Stores, retrieves, and transfers behavioral patterns per profile.
8
+ Ported from V2's _store_patterns.py + cross_project_transfer.py
9
+ into a single unified module with direct sqlite3 access.
10
+
11
+ Key features:
12
+ - Record detected patterns (refinement, interest, archival, etc.)
13
+ - Query patterns by profile and type
14
+ - Summarize pattern counts by type
15
+ - Transfer patterns across profiles (cross-project learning)
16
+ - Confidence scoring: min(evidence/10, 1.0) * abs(rate - 0.5) * 2
17
+
18
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import json
24
+ import logging
25
+ import sqlite3
26
+ import threading
27
+ from datetime import datetime, timezone
28
+ from pathlib import Path
29
+ from typing import Any, Dict, List, Optional
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+ # Minimum observations before emitting a pattern
34
+ MIN_EVIDENCE = 3
35
+
36
+ # Transfer eligibility thresholds
37
+ TRANSFER_MIN_CONFIDENCE = 0.3
38
+ TRANSFER_MIN_EVIDENCE = 2
39
+
40
+ _CREATE_TABLE = """
41
+ CREATE TABLE IF NOT EXISTS _store_patterns (
42
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
43
+ profile_id TEXT NOT NULL,
44
+ pattern_type TEXT NOT NULL,
45
+ pattern_key TEXT DEFAULT '',
46
+ success_rate REAL DEFAULT 0.0,
47
+ evidence_count INTEGER DEFAULT 1,
48
+ confidence REAL DEFAULT 0.0,
49
+ metadata TEXT DEFAULT '{}',
50
+ created_at TEXT NOT NULL,
51
+ updated_at TEXT NOT NULL
52
+ )
53
+ """
54
+
55
+ _CREATE_INDEX = """
56
+ CREATE INDEX IF NOT EXISTS idx_sp_profile_type
57
+ ON _store_patterns(profile_id, pattern_type)
58
+ """
59
+
60
+
61
+ class BehavioralPatternStore:
62
+ """Store and query behavioral patterns per profile.
63
+
64
+ Uses direct sqlite3 for storage. Thread-safe via a lock.
65
+ Creates the _store_patterns table on first use.
66
+
67
+ Ported from V2's BehavioralPatternExtractor + CrossProjectTransfer,
68
+ unified into a single class with profile-scoped operations.
69
+ """
70
+
71
+ def __init__(self, db_path: str | Path) -> None:
72
+ self._db_path = str(db_path)
73
+ self._lock = threading.Lock()
74
+ self._ensure_table()
75
+
76
+ # ------------------------------------------------------------------
77
+ # Public API
78
+ # ------------------------------------------------------------------
79
+
80
+ def record_pattern(
81
+ self,
82
+ profile_id: str,
83
+ pattern_type: str,
84
+ data: Optional[Dict[str, Any]] = None,
85
+ success_rate: float = 0.0,
86
+ confidence: float = 0.0,
87
+ ) -> int:
88
+ """Store a detected behavioral pattern.
89
+
90
+ Args:
91
+ profile_id: Profile scope for the pattern.
92
+ pattern_type: Category (e.g. "refinement", "interest", "archival").
93
+ data: Arbitrary metadata dict (stored as JSON).
94
+ success_rate: Success rate if applicable (0.0-1.0).
95
+ confidence: Confidence score (0.0-1.0).
96
+
97
+ Returns:
98
+ The row ID of the inserted pattern.
99
+ """
100
+ now = datetime.now(timezone.utc).isoformat()
101
+ metadata_json = json.dumps(data or {})
102
+ pattern_key = (data or {}).get("topic", (data or {}).get("pattern_key", ""))
103
+
104
+ with self._lock:
105
+ conn = self._connect()
106
+ try:
107
+ # Check for existing pattern of same type+key for this profile
108
+ existing = conn.execute(
109
+ "SELECT id, evidence_count FROM _store_patterns "
110
+ "WHERE profile_id = ? AND pattern_type = ? AND pattern_key = ?",
111
+ (profile_id, pattern_type, pattern_key),
112
+ ).fetchone()
113
+
114
+ if existing:
115
+ new_count = existing[1] + 1
116
+ new_confidence = self._compute_confidence(
117
+ new_count, success_rate
118
+ ) if success_rate > 0 else min(1.0, new_count / 100.0)
119
+ conn.execute(
120
+ "UPDATE _store_patterns "
121
+ "SET evidence_count = ?, confidence = ?, "
122
+ " success_rate = ?, metadata = ?, updated_at = ? "
123
+ "WHERE id = ?",
124
+ (new_count, new_confidence, success_rate,
125
+ metadata_json, now, existing[0]),
126
+ )
127
+ conn.commit()
128
+ return existing[0]
129
+
130
+ initial_confidence = confidence or 0.01
131
+ cur = conn.execute(
132
+ "INSERT INTO _store_patterns "
133
+ "(profile_id, pattern_type, pattern_key, success_rate, "
134
+ " evidence_count, confidence, metadata, created_at, updated_at) "
135
+ "VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?)",
136
+ (profile_id, pattern_type, pattern_key, success_rate,
137
+ initial_confidence, metadata_json, now, now),
138
+ )
139
+ conn.commit()
140
+ return cur.lastrowid
141
+ finally:
142
+ conn.close()
143
+
144
+ def get_patterns(
145
+ self,
146
+ profile_id: str,
147
+ pattern_type: Optional[str] = None,
148
+ limit: int = 50,
149
+ min_confidence: float = 0.0,
150
+ ) -> List[Dict[str, Any]]:
151
+ """Get stored patterns for a profile.
152
+
153
+ Args:
154
+ profile_id: Profile to query.
155
+ pattern_type: If given, filter by type.
156
+ limit: Max rows to return.
157
+ min_confidence: Minimum confidence threshold.
158
+
159
+ Returns:
160
+ List of pattern dicts with deserialized metadata.
161
+ """
162
+ with self._lock:
163
+ conn = self._connect()
164
+ try:
165
+ query = (
166
+ "SELECT * FROM _store_patterns "
167
+ "WHERE profile_id = ? AND confidence >= ?"
168
+ )
169
+ params: List[Any] = [profile_id, min_confidence]
170
+
171
+ if pattern_type is not None:
172
+ query += " AND pattern_type = ?"
173
+ params.append(pattern_type)
174
+
175
+ query += " ORDER BY confidence DESC, updated_at DESC LIMIT ?"
176
+ params.append(limit)
177
+
178
+ rows = conn.execute(query, params).fetchall()
179
+ return [self._row_to_dict(r) for r in rows]
180
+ finally:
181
+ conn.close()
182
+
183
+ def get_summary(self, profile_id: str) -> Dict[str, int]:
184
+ """Get pattern counts by type for a profile.
185
+
186
+ Returns:
187
+ Dict mapping pattern_type -> count.
188
+ """
189
+ with self._lock:
190
+ conn = self._connect()
191
+ try:
192
+ rows = conn.execute(
193
+ "SELECT pattern_type, COUNT(*) as cnt "
194
+ "FROM _store_patterns "
195
+ "WHERE profile_id = ? "
196
+ "GROUP BY pattern_type",
197
+ (profile_id,),
198
+ ).fetchall()
199
+ return {row[0]: row[1] for row in rows}
200
+ finally:
201
+ conn.close()
202
+
203
+ def transfer_patterns(
204
+ self,
205
+ source_profile: str,
206
+ target_profile: str,
207
+ min_confidence: float = 0.0,
208
+ ) -> int:
209
+ """Copy eligible patterns from source to target profile.
210
+
211
+ Only metadata (type, key, success_rate, confidence) is transferred.
212
+ Memory content is never transferred. Creates new rows in the target
213
+ profile scope.
214
+
215
+ Args:
216
+ source_profile: Profile to copy patterns from.
217
+ target_profile: Profile to copy patterns to.
218
+ min_confidence: Only transfer patterns above this threshold.
219
+
220
+ Returns:
221
+ Number of patterns transferred.
222
+ """
223
+ if source_profile == target_profile:
224
+ return 0
225
+
226
+ source_patterns = self.get_patterns(
227
+ source_profile, min_confidence=min_confidence
228
+ )
229
+
230
+ transferred = 0
231
+ for pattern in source_patterns:
232
+ # Skip if target already has this pattern
233
+ existing = self.get_patterns(
234
+ target_profile,
235
+ pattern_type=pattern["pattern_type"],
236
+ )
237
+ already_exists = any(
238
+ p["pattern_key"] == pattern.get("pattern_key", "")
239
+ for p in existing
240
+ )
241
+ if already_exists:
242
+ continue
243
+
244
+ self.record_pattern(
245
+ profile_id=target_profile,
246
+ pattern_type=pattern["pattern_type"],
247
+ data={
248
+ "topic": pattern.get("pattern_key", ""),
249
+ "transferred_from": source_profile,
250
+ "original_confidence": pattern.get("confidence", 0.0),
251
+ },
252
+ success_rate=pattern.get("success_rate", 0.0),
253
+ confidence=pattern.get("confidence", 0.0) * 0.8,
254
+ )
255
+ transferred += 1
256
+
257
+ return transferred
258
+
259
+ def delete_patterns(
260
+ self,
261
+ profile_id: str,
262
+ pattern_type: Optional[str] = None,
263
+ ) -> int:
264
+ """Delete patterns for a profile. Optionally filter by type.
265
+
266
+ Returns:
267
+ Number of patterns deleted.
268
+ """
269
+ with self._lock:
270
+ conn = self._connect()
271
+ try:
272
+ if pattern_type:
273
+ cur = conn.execute(
274
+ "DELETE FROM _store_patterns "
275
+ "WHERE profile_id = ? AND pattern_type = ?",
276
+ (profile_id, pattern_type),
277
+ )
278
+ else:
279
+ cur = conn.execute(
280
+ "DELETE FROM _store_patterns WHERE profile_id = ?",
281
+ (profile_id,),
282
+ )
283
+ conn.commit()
284
+ return cur.rowcount
285
+ finally:
286
+ conn.close()
287
+
288
+ # ------------------------------------------------------------------
289
+ # Internal helpers
290
+ # ------------------------------------------------------------------
291
+
292
+ @staticmethod
293
+ def _compute_confidence(evidence_count: int, success_rate: float) -> float:
294
+ """Confidence = min(evidence/10, 1.0) * abs(rate - 0.5) * 2.
295
+
296
+ High confidence requires both sufficient evidence AND a success
297
+ rate that deviates significantly from the 50% baseline.
298
+ """
299
+ evidence_factor = min(evidence_count / 10.0, 1.0)
300
+ deviation_factor = abs(success_rate - 0.5) * 2.0
301
+ return round(evidence_factor * deviation_factor, 4)
302
+
303
+ def _connect(self) -> sqlite3.Connection:
304
+ """Open a connection with row factory enabled."""
305
+ conn = sqlite3.connect(self._db_path)
306
+ conn.row_factory = sqlite3.Row
307
+ return conn
308
+
309
+ def _ensure_table(self) -> None:
310
+ """Create the _store_patterns table if it does not exist."""
311
+ conn = self._connect()
312
+ try:
313
+ conn.execute(_CREATE_TABLE)
314
+ conn.execute(_CREATE_INDEX)
315
+ conn.commit()
316
+ finally:
317
+ conn.close()
318
+
319
+ @staticmethod
320
+ def _row_to_dict(row: sqlite3.Row) -> Dict[str, Any]:
321
+ """Convert a sqlite3.Row into a plain dict with parsed JSON."""
322
+ d = dict(row)
323
+ meta = d.get("metadata", "{}")
324
+ d["metadata"] = json.loads(meta) if isinstance(meta, str) else meta
325
+ return d
326
+
327
+
328
+ # ---------------------------------------------------------------------------
329
+ # V3 API — BehavioralTracker (uses DatabaseManager + V3 schema)
330
+ # ---------------------------------------------------------------------------
331
+
332
+ from superlocalmemory.storage.models import BehavioralPattern # noqa: E402
333
+
334
+
335
+ class BehavioralTracker:
336
+ """V3 behavioral pattern tracker using DatabaseManager.
337
+
338
+ Records query patterns (time of day, query type, entity preferences)
339
+ and provides analytics (active hours, type distribution, preferences).
340
+
341
+ Uses the ``behavioral_patterns`` table from the V3 schema.
342
+ """
343
+
344
+ def __init__(self, db) -> None:
345
+ self._db = db
346
+
347
+ # ------------------------------------------------------------------
348
+ # Recording
349
+ # ------------------------------------------------------------------
350
+
351
+ def record_query(
352
+ self,
353
+ query: str,
354
+ query_type: str,
355
+ entities: list[str],
356
+ profile_id: str,
357
+ ) -> None:
358
+ """Record a query and extract behavioral patterns.
359
+
360
+ Creates up to 3 pattern types per call:
361
+ - ``time_of_day``: hour_N for current hour
362
+ - ``query_type``: keyed by the query_type string
363
+ - ``entity_pref``: one per entity (max 5, lowercased)
364
+ """
365
+ # 1. Time of day
366
+ hour = datetime.now(timezone.utc).hour
367
+ self._upsert_pattern(profile_id, "time_of_day", f"hour_{hour}")
368
+
369
+ # 2. Query type
370
+ if query_type:
371
+ self._upsert_pattern(profile_id, "query_type", query_type)
372
+
373
+ # 3. Entity preferences (max 5, lowercased)
374
+ for entity in entities[:5]:
375
+ self._upsert_pattern(profile_id, "entity_pref", entity.lower())
376
+
377
+ # ------------------------------------------------------------------
378
+ # Querying
379
+ # ------------------------------------------------------------------
380
+
381
+ def get_patterns(
382
+ self,
383
+ pattern_type: str,
384
+ profile_id: str,
385
+ min_confidence: float = 0.0,
386
+ ) -> list[BehavioralPattern]:
387
+ """Get patterns filtered by type, profile, and min confidence."""
388
+ rows = self._db.execute(
389
+ "SELECT * FROM behavioral_patterns "
390
+ "WHERE profile_id = ? AND pattern_type = ? AND confidence >= ? "
391
+ "ORDER BY confidence DESC",
392
+ (profile_id, pattern_type, min_confidence),
393
+ )
394
+ return [self._row_to_pattern(r) for r in rows]
395
+
396
+ def get_entity_preferences(
397
+ self, profile_id: str, top_k: int = 10
398
+ ) -> list[str]:
399
+ """Top-K preferred entities by confidence, highest first."""
400
+ rows = self._db.execute(
401
+ "SELECT pattern_key FROM behavioral_patterns "
402
+ "WHERE profile_id = ? AND pattern_type = 'entity_pref' "
403
+ "ORDER BY confidence DESC, observation_count DESC LIMIT ?",
404
+ (profile_id, top_k),
405
+ )
406
+ return [dict(r)["pattern_key"] for r in rows]
407
+
408
+ def get_active_hours(self, profile_id: str) -> list[int]:
409
+ """Top 5 active hours by observation count."""
410
+ rows = self._db.execute(
411
+ "SELECT pattern_key FROM behavioral_patterns "
412
+ "WHERE profile_id = ? AND pattern_type = 'time_of_day' "
413
+ "ORDER BY observation_count DESC LIMIT 5",
414
+ (profile_id,),
415
+ )
416
+ result: list[int] = []
417
+ for r in rows:
418
+ key = dict(r)["pattern_key"]
419
+ if key.startswith("hour_"):
420
+ try:
421
+ result.append(int(key[5:]))
422
+ except ValueError:
423
+ pass
424
+ return result
425
+
426
+ def get_query_type_distribution(self, profile_id: str) -> dict[str, float]:
427
+ """Proportional distribution of query types."""
428
+ rows = self._db.execute(
429
+ "SELECT pattern_key, observation_count FROM behavioral_patterns "
430
+ "WHERE profile_id = ? AND pattern_type = 'query_type'",
431
+ (profile_id,),
432
+ )
433
+ counts: dict[str, int] = {}
434
+ for r in rows:
435
+ d = dict(r)
436
+ counts[d["pattern_key"]] = d["observation_count"]
437
+
438
+ total = sum(counts.values())
439
+ if total == 0:
440
+ return {}
441
+ return {k: round(v / total, 4) for k, v in counts.items()}
442
+
443
+ # ------------------------------------------------------------------
444
+ # Internal
445
+ # ------------------------------------------------------------------
446
+
447
+ def _upsert_pattern(
448
+ self, profile_id: str, pattern_type: str, pattern_key: str
449
+ ) -> None:
450
+ """Insert or increment a pattern. Confidence = min(count/100, 1.0)."""
451
+ from superlocalmemory.storage.models import _new_id, _now
452
+
453
+ rows = self._db.execute(
454
+ "SELECT pattern_id, observation_count FROM behavioral_patterns "
455
+ "WHERE profile_id = ? AND pattern_type = ? AND pattern_key = ?",
456
+ (profile_id, pattern_type, pattern_key),
457
+ )
458
+ if rows:
459
+ d = dict(rows[0])
460
+ new_count = d["observation_count"] + 1
461
+ new_conf = min(new_count / 100.0, 1.0)
462
+ self._db.execute(
463
+ "UPDATE behavioral_patterns "
464
+ "SET observation_count = ?, confidence = ?, last_updated = ? "
465
+ "WHERE pattern_id = ?",
466
+ (new_count, new_conf, _now(), d["pattern_id"]),
467
+ )
468
+ else:
469
+ self._db.execute(
470
+ "INSERT INTO behavioral_patterns "
471
+ "(pattern_id, profile_id, pattern_type, pattern_key, "
472
+ " pattern_value, confidence, observation_count, last_updated) "
473
+ "VALUES (?, ?, ?, ?, '', ?, 1, ?)",
474
+ (_new_id(), profile_id, pattern_type, pattern_key, 0.01, _now()),
475
+ )
476
+
477
+ @staticmethod
478
+ def _row_to_pattern(row) -> BehavioralPattern:
479
+ """Convert a DB row to BehavioralPattern."""
480
+ d = dict(row)
481
+ return BehavioralPattern(
482
+ pattern_id=d["pattern_id"],
483
+ profile_id=d["profile_id"],
484
+ pattern_type=d.get("pattern_type", ""),
485
+ pattern_key=d.get("pattern_key", ""),
486
+ pattern_value=d.get("pattern_value", ""),
487
+ confidence=d.get("confidence", 0.0),
488
+ observation_count=d.get("observation_count", 0),
489
+ last_updated=d.get("last_updated", ""),
490
+ )
@@ -0,0 +1,94 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """Behavioral listener — subscribes to event bus, captures patterns.
6
+
7
+ Connects to the V3 event bus and records all memory operations
8
+ for behavioral pattern mining.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ import time
15
+ from collections import deque
16
+ from typing import Any
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ # Max events kept in memory for pattern mining
21
+ MAX_EVENT_BUFFER = 500
22
+
23
+
24
+ class BehavioralListener:
25
+ """Subscribes to event bus and records behavioral data."""
26
+
27
+ def __init__(self, event_bus=None, db_path=None):
28
+ self._events = deque(maxlen=MAX_EVENT_BUFFER)
29
+ self._event_count = 0
30
+ self._db_path = db_path
31
+
32
+ if event_bus:
33
+ event_bus.subscribe(self._on_event)
34
+
35
+ @property
36
+ def event_count(self) -> int:
37
+ return self._event_count
38
+
39
+ def _on_event(self, event: dict) -> None:
40
+ """Handle incoming event from bus."""
41
+ self._events.append({
42
+ "event_type": event.get("event_type", "unknown"),
43
+ "data": event.get("data", {}),
44
+ "timestamp": time.time(),
45
+ })
46
+ self._event_count += 1
47
+
48
+ def get_recent_events(self, limit: int = 50) -> list[dict]:
49
+ """Get most recent behavioral events."""
50
+ return list(self._events)[-limit:]
51
+
52
+ def mine_patterns(self) -> list[dict]:
53
+ """Mine behavioral patterns from recent events.
54
+
55
+ Detects:
56
+ - store->recall->store = refinement pattern
57
+ - repeated recall of same topic = interest pattern
58
+ - store without recall = archival pattern
59
+ """
60
+ patterns = []
61
+ events = list(self._events)
62
+
63
+ # Detect store->recall->store (refinement)
64
+ for i in range(len(events) - 2):
65
+ if (events[i]["event_type"] == "memory.stored" and
66
+ events[i+1]["event_type"] == "memory.recalled" and
67
+ events[i+2]["event_type"] == "memory.stored"):
68
+ patterns.append({
69
+ "pattern_type": "refinement",
70
+ "timestamp": events[i+2]["timestamp"],
71
+ "events": [events[i], events[i+1], events[i+2]],
72
+ })
73
+
74
+ # Detect repeated recall (interest)
75
+ recall_topics = {}
76
+ for e in events:
77
+ if e["event_type"] == "memory.recalled":
78
+ topic = e["data"].get("query_preview", "")[:50]
79
+ recall_topics[topic] = recall_topics.get(topic, 0) + 1
80
+
81
+ for topic, count in recall_topics.items():
82
+ if count >= 3:
83
+ patterns.append({
84
+ "pattern_type": "interest",
85
+ "topic": topic,
86
+ "count": count,
87
+ })
88
+
89
+ return patterns
90
+
91
+ def clear(self) -> None:
92
+ """Clear event buffer."""
93
+ self._events.clear()
94
+ self._event_count = 0