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,701 @@
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 — Main Memory Engine.
6
+
7
+ Orchestrates the full memory lifecycle: store, encode, retrieve.
8
+ Single entry point for all memory operations.
9
+ Profile-scoped. Mode-aware (A/B/C).
10
+
11
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from typing import Any
18
+
19
+ from superlocalmemory.core.config import SLMConfig
20
+ from superlocalmemory.core.modes import get_capabilities
21
+ from superlocalmemory.storage.models import (
22
+ AtomicFact, FactType, MemoryRecord, Mode, RecallResponse,
23
+ )
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ from superlocalmemory.core.hooks import HookRegistry
28
+
29
+
30
+ class MemoryEngine:
31
+ """Main orchestrator for the SuperLocalMemory V3 memory system.
32
+
33
+ Wires encoding (fact extraction, entity resolution, graph building,
34
+ consolidation) with retrieval (4-channel search, RRF fusion,
35
+ reranking) and all supporting layers (trust, learning, compliance).
36
+
37
+ Usage::
38
+
39
+ config = SLMConfig.for_mode(Mode.A)
40
+ engine = MemoryEngine(config)
41
+ engine.store("Alice went to Paris last summer", session_id="s1")
42
+ response = engine.recall("Where did Alice go?")
43
+ """
44
+
45
+ def __init__(self, config: SLMConfig) -> None:
46
+ self._config = config
47
+ self._caps = get_capabilities(config.mode)
48
+ self._profile_id = config.active_profile
49
+ self._initialized = False
50
+
51
+ self._db = None
52
+ self._embedder = None
53
+ self._llm = None
54
+ self._fact_extractor = None
55
+ self._entity_resolver = None
56
+ self._temporal_parser = None
57
+ self._type_router = None
58
+ self._graph_builder = None
59
+ self._consolidator = None
60
+ self._observation_builder = None
61
+ self._scene_builder = None
62
+ self._entropy_gate = None
63
+ self._retrieval_engine = None
64
+ self._trust_scorer = None
65
+ self._ann_index = None
66
+ self._sheaf_checker = None
67
+ self._provenance = None
68
+ self._adaptive_learner = None
69
+ self._compliance_checker = None
70
+ self._hooks = HookRegistry()
71
+
72
+ def initialize(self) -> None:
73
+ """Initialize all components. Call once before use."""
74
+ if self._initialized:
75
+ return
76
+
77
+ from superlocalmemory.storage import schema
78
+ from superlocalmemory.storage.database import DatabaseManager
79
+ from superlocalmemory.core.embeddings import EmbeddingService
80
+ from superlocalmemory.llm.backbone import LLMBackbone
81
+
82
+ self._db = DatabaseManager(self._config.db_path)
83
+ self._db.initialize(schema)
84
+ self._embedder = EmbeddingService(self._config.embedding)
85
+
86
+ if self._caps.llm_fact_extraction:
87
+ self._llm = LLMBackbone(self._config.llm)
88
+ if not self._llm.is_available():
89
+ logger.warning("LLM not available. Falling back to Mode A extraction.")
90
+ self._llm = None
91
+
92
+ from superlocalmemory.trust.scorer import TrustScorer
93
+ from superlocalmemory.trust.provenance import ProvenanceTracker
94
+ from superlocalmemory.learning.adaptive import AdaptiveLearner
95
+ from superlocalmemory.compliance.eu_ai_act import EUAIActChecker
96
+
97
+ self._trust_scorer = TrustScorer(self._db)
98
+
99
+ self._init_encoding()
100
+ self._init_retrieval()
101
+
102
+ self._provenance = ProvenanceTracker(self._db)
103
+ self._adaptive_learner = AdaptiveLearner(self._db)
104
+ self._compliance_checker = EUAIActChecker()
105
+
106
+ # Wire lifecycle hooks
107
+ self._wire_hooks()
108
+
109
+ self._initialized = True
110
+ logger.info("MemoryEngine initialized: mode=%s profile=%s",
111
+ self._config.mode.value, self._profile_id)
112
+
113
+ def store(
114
+ self,
115
+ content: str,
116
+ session_id: str = "",
117
+ session_date: str | None = None,
118
+ speaker: str = "",
119
+ role: str = "user",
120
+ metadata: dict[str, Any] | None = None,
121
+ ) -> list[str]:
122
+ """Store content and extract structured facts. Returns fact_ids."""
123
+ self._ensure_init()
124
+
125
+ # Pre-operation hooks (trust gate, ABAC, rate limiter)
126
+ hook_ctx = {"operation": "store", "agent_id": metadata.get("agent_id", "unknown") if metadata else "unknown",
127
+ "profile_id": self._profile_id, "content_preview": content[:100]}
128
+ self._hooks.run_pre("store", hook_ctx)
129
+
130
+ if self._entropy_gate and not self._entropy_gate.should_pass(content):
131
+ return []
132
+
133
+ from superlocalmemory.encoding.temporal_parser import TemporalParser
134
+ parser = self._temporal_parser or TemporalParser()
135
+ parsed_date = parser.parse_session_date(session_date) if session_date else None
136
+
137
+ record = MemoryRecord(
138
+ profile_id=self._profile_id, content=content,
139
+ session_id=session_id, speaker=speaker, role=role,
140
+ session_date=parsed_date, metadata=metadata or {},
141
+ )
142
+ self._db.store_memory(record)
143
+
144
+ facts = self._fact_extractor.extract_facts(
145
+ turns=[content], session_id=session_id,
146
+ session_date=parsed_date, speaker_a=speaker,
147
+ )
148
+ if not facts:
149
+ return []
150
+
151
+ if self._type_router:
152
+ facts = self._type_router.route_facts(facts)
153
+
154
+ stored_ids: list[str] = []
155
+ for fact in facts:
156
+ fact = self._enrich_fact(fact, record)
157
+
158
+ if self._consolidator:
159
+ action = self._consolidator.consolidate(fact, self._profile_id)
160
+ if action.action_type.value == "noop":
161
+ continue
162
+
163
+ # Opinion confidence tracking: reinforce or decay
164
+ # When a new opinion aligns with existing, boost confidence.
165
+ # When contradicted (supersede), reduce old fact's confidence.
166
+ if fact.fact_type == FactType.OPINION and action.action_type.value == "update":
167
+ try:
168
+ existing = self._db.get_fact(action.new_fact_id)
169
+ if existing and existing.fact_type == FactType.OPINION:
170
+ new_conf = min(1.0, existing.confidence + 0.1)
171
+ self._db.update_fact(action.new_fact_id, {"confidence": new_conf})
172
+ except Exception:
173
+ pass
174
+ elif fact.fact_type == FactType.OPINION and action.action_type.value == "supersede":
175
+ try:
176
+ old_id = getattr(action, "old_fact_id", None)
177
+ if old_id:
178
+ old_fact = self._db.get_fact(old_id)
179
+ if old_fact:
180
+ new_conf = max(0.0, old_fact.confidence - 0.2)
181
+ self._db.update_fact(old_id, {"confidence": new_conf})
182
+ except Exception:
183
+ pass
184
+
185
+ if action.action_type.value in ("update", "supersede"):
186
+ # Run post-processing on updated facts
187
+ updated_fact = self._db.get_fact(action.new_fact_id)
188
+ if updated_fact:
189
+ if self._graph_builder:
190
+ self._graph_builder.build_edges(updated_fact, self._profile_id)
191
+ if self._observation_builder:
192
+ for eid in updated_fact.canonical_entities:
193
+ self._observation_builder.update_profile(
194
+ eid, updated_fact, self._profile_id,
195
+ )
196
+ stored_ids.append(action.new_fact_id)
197
+ continue
198
+ # ADD case: consolidator already stored the fact (F8 fix)
199
+ # Fall through to post-processing below
200
+ else:
201
+ self._db.store_fact(fact)
202
+
203
+ stored_ids.append(fact.fact_id)
204
+
205
+ if fact.embedding and self._ann_index:
206
+ self._ann_index.add(fact.fact_id, fact.embedding)
207
+ if self._graph_builder:
208
+ self._graph_builder.build_edges(fact, self._profile_id)
209
+
210
+ # Sheaf consistency check (runs after edges exist)
211
+ # Cap edge check to prevent O(N^2) hang on large graphs
212
+ if (self._sheaf_checker
213
+ and fact.embedding
214
+ and fact.canonical_entities):
215
+ from superlocalmemory.storage.models import EdgeType, GraphEdge
216
+ try:
217
+ edges_for_fact = self._db.get_edges_for_node(
218
+ fact.fact_id, self._profile_id,
219
+ )
220
+ # Only run sheaf if edge count is manageable
221
+ # At 18K+ edges, the coboundary computation becomes O(N*dim^2)
222
+ if len(edges_for_fact) < self._config.math.sheaf_max_edges_per_check:
223
+ contradictions = self._sheaf_checker.check_consistency(
224
+ fact, self._profile_id,
225
+ )
226
+ for c in contradictions:
227
+ if c.severity > 0.45:
228
+ edge = GraphEdge(
229
+ profile_id=self._profile_id,
230
+ source_id=fact.fact_id,
231
+ target_id=c.fact_id_b,
232
+ edge_type=EdgeType.SUPERSEDES,
233
+ weight=c.severity,
234
+ )
235
+ self._db.store_edge(edge)
236
+ except Exception as exc:
237
+ logger.debug("Sheaf check skipped: %s", exc)
238
+
239
+ if self._observation_builder:
240
+ for eid in fact.canonical_entities:
241
+ self._observation_builder.update_profile(eid, fact, self._profile_id)
242
+
243
+ # Increment fact_count for each linked canonical entity
244
+ for eid in fact.canonical_entities:
245
+ try:
246
+ self._db.increment_entity_fact_count(eid)
247
+ except Exception:
248
+ pass # Non-critical — entity may have been deleted
249
+ if self._scene_builder:
250
+ self._scene_builder.assign_to_scene(fact, self._profile_id)
251
+
252
+ # Populate temporal_events for temporal retrieval
253
+ has_dates = (fact.observation_date or fact.referenced_date
254
+ or fact.interval_start)
255
+ if fact.canonical_entities and has_dates:
256
+ from superlocalmemory.storage.models import TemporalEvent
257
+ for eid in fact.canonical_entities:
258
+ event = TemporalEvent(
259
+ profile_id=self._profile_id, entity_id=eid,
260
+ fact_id=fact.fact_id,
261
+ observation_date=fact.observation_date,
262
+ referenced_date=fact.referenced_date,
263
+ interval_start=fact.interval_start,
264
+ interval_end=fact.interval_end,
265
+ description=fact.content[:200],
266
+ )
267
+ self._db.store_temporal_event(event)
268
+
269
+ # Foresight: extract time-bounded predictions
270
+ try:
271
+ from superlocalmemory.encoding.foresight import extract_foresight_signals
272
+ from superlocalmemory.storage.models import TemporalEvent as _TE
273
+ foresight_signals = extract_foresight_signals(fact)
274
+ for sig in foresight_signals:
275
+ f_event = _TE(
276
+ profile_id=self._profile_id,
277
+ entity_id=sig.get("entity_id", ""),
278
+ fact_id=fact.fact_id,
279
+ interval_start=sig.get("start_time"),
280
+ interval_end=sig.get("end_time"),
281
+ description=sig.get("description", ""),
282
+ )
283
+ self._db.store_temporal_event(f_event)
284
+ except Exception as exc:
285
+ logger.debug("Foresight extraction: %s", exc)
286
+
287
+ # Persist BM25 tokens at ingestion
288
+ bm25 = getattr(self._retrieval_engine, '_bm25', None) if self._retrieval_engine else None
289
+ if bm25:
290
+ bm25.add(fact.fact_id, fact.content, self._profile_id)
291
+
292
+ # Record provenance for data lineage (EU AI Act Art. 10)
293
+ if self._provenance:
294
+ try:
295
+ self._provenance.record(
296
+ fact_id=fact.fact_id,
297
+ profile_id=self._profile_id,
298
+ source_type="store",
299
+ source_id=session_id,
300
+ created_by=speaker or "unknown",
301
+ )
302
+ except Exception:
303
+ pass
304
+
305
+ logger.info("Stored %d facts (session=%s)", len(stored_ids), session_id)
306
+
307
+ # Post-operation hooks (audit, trust signal, event bus)
308
+ hook_ctx["fact_ids"] = stored_ids
309
+ hook_ctx["fact_count"] = len(stored_ids)
310
+ self._hooks.run_post("store", hook_ctx)
311
+
312
+ return stored_ids
313
+
314
+ def recall(
315
+ self, query: str, profile_id: str | None = None,
316
+ mode: Mode | None = None, limit: int = 20,
317
+ agent_id: str = "unknown",
318
+ ) -> RecallResponse:
319
+ """Recall relevant facts for a query.
320
+
321
+ Pipeline: retrieval → agentic sufficiency (if configured) → post-recall updates.
322
+ Agentic sufficiency (sufficiency check): triggers 2-round re-retrieval when
323
+ initial results are insufficient. Mode C uses LLM judgment; Mode A uses
324
+ heuristic alias expansion.
325
+ """
326
+ self._ensure_init()
327
+
328
+ # Pre-operation hooks
329
+ hook_ctx = {"operation": "recall", "agent_id": agent_id,
330
+ "profile_id": profile_id or self._profile_id, "query_preview": query[:100]}
331
+ self._hooks.run_pre("recall", hook_ctx)
332
+
333
+ pid = profile_id or self._profile_id
334
+ m = mode or self._config.mode
335
+
336
+ response = self._retrieval_engine.recall(query, pid, m, limit)
337
+
338
+ # Agentic sufficiency verification
339
+ # Only trigger when: (a) configured rounds > 0, (b) results look weak
340
+ agentic_rounds = self._config.retrieval.agentic_max_rounds
341
+ if agentic_rounds > 0 and response.results:
342
+ max_score = max((r.score for r in response.results), default=0.0)
343
+ should_trigger = (
344
+ max_score < self._config.retrieval.agentic_confidence_threshold
345
+ or response.query_type == "multi_hop"
346
+ or len(response.results) < 3
347
+ )
348
+ if should_trigger:
349
+ try:
350
+ from superlocalmemory.retrieval.agentic import AgenticRetriever
351
+ agentic = AgenticRetriever(
352
+ confidence_threshold=self._config.retrieval.agentic_confidence_threshold,
353
+ db=self._db,
354
+ )
355
+ enhanced_facts = agentic.retrieve(
356
+ query=query, profile_id=pid,
357
+ retrieval_engine=self._retrieval_engine,
358
+ llm=self._llm,
359
+ top_k=limit,
360
+ query_type=response.query_type,
361
+ )
362
+ # Replace response results with enhanced facts if we got more
363
+ if len(enhanced_facts) > len(response.results):
364
+ from superlocalmemory.storage.models import RetrievalResult
365
+ enhanced_results = []
366
+ for i, f in enumerate(enhanced_facts):
367
+ # Look up real trust score for agentic results
368
+ fact_trust = 0.5
369
+ if self._trust_scorer:
370
+ try:
371
+ fact_trust = self._trust_scorer.get_fact_trust(
372
+ f.fact_id, pid,
373
+ )
374
+ except Exception:
375
+ pass
376
+ enhanced_results.append(RetrievalResult(
377
+ fact=f, score=1.0 / (i + 1),
378
+ channel_scores={"agentic": 1.0},
379
+ confidence=f.confidence,
380
+ evidence_chain=["agentic_round_2"],
381
+ trust_score=fact_trust,
382
+ ))
383
+ response = RecallResponse(
384
+ query=query, mode=m, results=enhanced_results[:limit],
385
+ query_type=response.query_type,
386
+ channel_weights=response.channel_weights,
387
+ total_candidates=response.total_candidates + len(enhanced_facts),
388
+ retrieval_time_ms=response.retrieval_time_ms,
389
+ )
390
+ except Exception as exc:
391
+ logger.debug("Agentic sufficiency skipped: %s", exc)
392
+
393
+ # Reconsolidation: access updates trust + count (neuroscience principle)
394
+ if self._trust_scorer:
395
+ for r in response.results:
396
+ self._trust_scorer.update_on_access("fact", r.fact.fact_id, pid)
397
+
398
+ # Fisher Bayesian update on recall
399
+ q_emb = self._embedder.embed(query) if self._embedder else None
400
+ q_var_arr = None
401
+ if self._embedder and q_emb:
402
+ _, q_var_list = self._embedder.compute_fisher_params(q_emb)
403
+ import numpy as _np
404
+ q_var_arr = _np.array(q_var_list, dtype=_np.float64)
405
+
406
+ for r in response.results:
407
+ updates: dict[str, object] = {
408
+ "access_count": r.fact.access_count + 1,
409
+ }
410
+ # Bayesian variance narrowing after 3+ accesses
411
+ if (q_var_arr is not None
412
+ and r.fact.fisher_variance is not None
413
+ and r.fact.access_count >= 3):
414
+ import numpy as _np
415
+ f_var = _np.array(r.fact.fisher_variance, dtype=_np.float64)
416
+ # Conjugate Gaussian update: 1/new_var = 1/f_var + 1/q_var
417
+ new_var = 1.0 / (1.0 / _np.maximum(f_var, 0.05) + 1.0 / _np.maximum(q_var_arr, 0.05))
418
+ new_var = _np.clip(new_var, 0.05, 2.0)
419
+ updates["fisher_variance"] = new_var.tolist()
420
+
421
+ self._db.update_fact(r.fact.fact_id, updates)
422
+
423
+ # Post-operation hooks (audit, trust signal, learning)
424
+ hook_ctx["result_count"] = len(response.results)
425
+ hook_ctx["query_type"] = response.query_type
426
+ self._hooks.run_post("recall", hook_ctx)
427
+
428
+ return response
429
+
430
+ def store_fact_direct(self, fact: AtomicFact) -> str:
431
+ """Store a pre-built fact with full enrichment.
432
+
433
+ Ensures embedding, Fisher params, canonical entities, BM25 tokens,
434
+ and graph edges are all populated — even for auxiliary data.
435
+ Creates a parent memory record to satisfy FK constraint.
436
+ """
437
+ self._ensure_init()
438
+
439
+ # Create parent memory record (FK: atomic_facts.memory_id → memories.memory_id)
440
+ if not fact.memory_id:
441
+ from superlocalmemory.storage.models import _new_id
442
+ record = MemoryRecord(
443
+ profile_id=self._profile_id,
444
+ content=fact.content[:500],
445
+ session_id=fact.session_id,
446
+ )
447
+ self._db.store_memory(record)
448
+ fact.memory_id = record.memory_id
449
+
450
+ if not fact.embedding and self._embedder:
451
+ fact.embedding = self._embedder.embed(fact.content)
452
+ if fact.embedding:
453
+ fact.fisher_mean, fact.fisher_variance = (
454
+ self._embedder.compute_fisher_params(fact.embedding)
455
+ )
456
+ if self._entity_resolver and fact.entities:
457
+ canonical = self._entity_resolver.resolve(
458
+ fact.entities, self._profile_id,
459
+ )
460
+ fact.canonical_entities = list(canonical.values())
461
+ self._db.store_fact(fact)
462
+ if fact.embedding and self._ann_index:
463
+ self._ann_index.add(fact.fact_id, fact.embedding)
464
+ if self._graph_builder:
465
+ self._graph_builder.build_edges(fact, self._profile_id)
466
+ # BM25 indexing
467
+ bm25 = getattr(self._retrieval_engine, '_bm25', None) if self._retrieval_engine else None
468
+ if bm25:
469
+ bm25.add(fact.fact_id, fact.content, self._profile_id)
470
+ return fact.fact_id
471
+
472
+ def create_speaker_entities(self, speaker_a: str, speaker_b: str) -> None:
473
+ """Pre-create canonical entities for conversation speakers."""
474
+ self._ensure_init()
475
+ if self._entity_resolver:
476
+ self._entity_resolver.create_speaker_entities(
477
+ speaker_a, speaker_b, self._profile_id,
478
+ )
479
+
480
+ def close_session(self, session_id: str) -> int:
481
+ """Create session-level temporal summary for session-level retrieval.
482
+
483
+ Aggregates facts from a completed session into temporal_events
484
+ with session scope. Enables temporal queries like "What happened
485
+ in session 3?"
486
+
487
+ Returns number of session summary events created.
488
+ """
489
+ self._ensure_init()
490
+ from superlocalmemory.storage.models import TemporalEvent
491
+
492
+ facts = self._db.get_all_facts(self._profile_id)
493
+ session_facts = [f for f in facts if f.session_id == session_id]
494
+ if not session_facts:
495
+ return 0
496
+
497
+ # Group by entity for session-level summaries
498
+ entity_facts: dict[str, list[AtomicFact]] = {}
499
+ for f in session_facts:
500
+ for eid in f.canonical_entities:
501
+ entity_facts.setdefault(eid, []).append(f)
502
+
503
+ count = 0
504
+ session_date = session_facts[0].observation_date or ""
505
+ for eid, efacts in entity_facts.items():
506
+ summary_parts = [f.content[:80] for f in efacts[:5]]
507
+ summary = f"Session {session_id}: " + "; ".join(summary_parts)
508
+ event = TemporalEvent(
509
+ profile_id=self._profile_id,
510
+ entity_id=eid,
511
+ fact_id=efacts[0].fact_id,
512
+ observation_date=session_date,
513
+ description=summary[:500],
514
+ )
515
+ self._db.store_temporal_event(event)
516
+ count += 1
517
+
518
+ logger.info(
519
+ "Session %s closed: %d summary events for %d facts",
520
+ session_id, count, len(session_facts),
521
+ )
522
+ return count
523
+
524
+ def close(self) -> None:
525
+ self._initialized = False
526
+
527
+ @property
528
+ def profile_id(self) -> str:
529
+ return self._profile_id
530
+
531
+ @profile_id.setter
532
+ def profile_id(self, value: str) -> None:
533
+ self._profile_id = value
534
+
535
+ @property
536
+ def fact_count(self) -> int:
537
+ self._ensure_init()
538
+ return self._db.get_fact_count(self._profile_id)
539
+
540
+ # -- Internal ----------------------------------------------------------
541
+
542
+ def _ensure_init(self) -> None:
543
+ if not self._initialized:
544
+ self.initialize()
545
+
546
+ def _init_encoding(self) -> None:
547
+ from superlocalmemory.encoding.fact_extractor import FactExtractor
548
+ from superlocalmemory.encoding.entity_resolver import EntityResolver
549
+ from superlocalmemory.encoding.temporal_parser import TemporalParser
550
+ from superlocalmemory.encoding.type_router import TypeRouter
551
+ from superlocalmemory.encoding.graph_builder import GraphBuilder
552
+ from superlocalmemory.encoding.consolidator import MemoryConsolidator
553
+ from superlocalmemory.encoding.observation_builder import ObservationBuilder
554
+ from superlocalmemory.encoding.scene_builder import SceneBuilder
555
+ from superlocalmemory.encoding.entropy_gate import EntropyGate
556
+ from superlocalmemory.retrieval.ann_index import ANNIndex
557
+
558
+ self._ann_index = ANNIndex(dimension=self._config.embedding.dimension)
559
+ self._fact_extractor = FactExtractor(
560
+ config=self._config.encoding, llm=self._llm,
561
+ embedder=self._embedder, mode=self._config.mode,
562
+ )
563
+ self._entity_resolver = EntityResolver(self._db, self._llm)
564
+ self._temporal_parser = TemporalParser()
565
+ self._type_router = TypeRouter(
566
+ mode=self._config.mode, embedder=self._embedder, llm=self._llm,
567
+ )
568
+ self._graph_builder = GraphBuilder(self._db, self._ann_index)
569
+ self._consolidator = MemoryConsolidator(
570
+ self._db, self._embedder, self._llm, self._config.encoding,
571
+ )
572
+ self._observation_builder = ObservationBuilder(self._db)
573
+ self._scene_builder = SceneBuilder(self._db, self._embedder)
574
+ self._entropy_gate = EntropyGate(
575
+ self._embedder, self._config.encoding.entropy_threshold,
576
+ )
577
+
578
+ # Wire Sheaf consistency checker
579
+ if self._config.math.sheaf_at_encoding:
580
+ from superlocalmemory.math.sheaf import SheafConsistencyChecker
581
+ self._sheaf_checker = SheafConsistencyChecker(
582
+ self._db, self._config.math.sheaf_contradiction_threshold,
583
+ )
584
+
585
+ def _init_retrieval(self) -> None:
586
+ from superlocalmemory.retrieval.engine import RetrievalEngine
587
+ from superlocalmemory.retrieval.semantic_channel import SemanticChannel
588
+ from superlocalmemory.retrieval.bm25_channel import BM25Channel
589
+ from superlocalmemory.retrieval.entity_channel import EntityGraphChannel
590
+ from superlocalmemory.retrieval.temporal_channel import TemporalChannel
591
+ from superlocalmemory.retrieval.reranker import CrossEncoderReranker
592
+ from superlocalmemory.retrieval.profile_channel import ProfileChannel
593
+ from superlocalmemory.retrieval.bridge_discovery import BridgeDiscovery
594
+
595
+ channels: dict = {
596
+ "semantic": SemanticChannel(
597
+ self._db,
598
+ fisher_temperature=self._config.math.fisher_temperature,
599
+ embedder=self._embedder,
600
+ fisher_mode=self._config.math.fisher_mode,
601
+ ),
602
+ "bm25": BM25Channel(self._db),
603
+ "entity_graph": EntityGraphChannel(self._db, self._entity_resolver),
604
+ "temporal": TemporalChannel(self._db),
605
+ }
606
+ reranker = None
607
+ if self._config.retrieval.use_cross_encoder:
608
+ reranker = CrossEncoderReranker(self._config.retrieval.cross_encoder_model)
609
+
610
+ profile_ch = ProfileChannel(self._db)
611
+ bridge = BridgeDiscovery(self._db)
612
+
613
+ self._retrieval_engine = RetrievalEngine(
614
+ db=self._db, config=self._config.retrieval, channels=channels,
615
+ embedder=self._embedder, reranker=reranker,
616
+ base_weights=self._config.channel_weights,
617
+ profile_channel=profile_ch,
618
+ bridge_discovery=bridge,
619
+ trust_scorer=self._trust_scorer,
620
+ )
621
+
622
+ def _wire_hooks(self) -> None:
623
+ """Wire trust, compliance, and event bus hooks into engine lifecycle."""
624
+ # -- Pre-store hooks (synchronous, can reject) --
625
+ if self._trust_scorer:
626
+ from superlocalmemory.trust.gate import TrustGate
627
+ gate = TrustGate(self._trust_scorer)
628
+ self._hooks.register_pre("store", lambda ctx: gate.check_write(
629
+ ctx.get("agent_id", "unknown"), ctx.get("profile_id", self._profile_id)))
630
+ self._hooks.register_pre("delete", lambda ctx: gate.check_delete(
631
+ ctx.get("agent_id", "unknown"), ctx.get("profile_id", self._profile_id)))
632
+
633
+ # -- Post-store hooks (async, never block) --
634
+ if self._trust_scorer:
635
+ self._hooks.register_post("store", lambda ctx: self._trust_scorer.record_signal(
636
+ ctx.get("agent_id", "unknown"), ctx.get("profile_id", self._profile_id), "store_success"))
637
+ self._hooks.register_post("recall", lambda ctx: self._trust_scorer.record_signal(
638
+ ctx.get("agent_id", "unknown"), ctx.get("profile_id", self._profile_id), "recall_hit"))
639
+
640
+ # -- Burst detection via SignalRecorder --
641
+ try:
642
+ from superlocalmemory.trust.signals import SignalRecorder
643
+ self._signal_recorder = SignalRecorder(self._db)
644
+ self._hooks.register_post("store", lambda ctx: self._signal_recorder.record(
645
+ ctx.get("agent_id", "unknown"), ctx.get("profile_id", self._profile_id), "store_success"))
646
+ except Exception:
647
+ self._signal_recorder = None
648
+
649
+ # -- Tamper-proof audit chain (all operations logged with hash chain) --
650
+ try:
651
+ from superlocalmemory.compliance.audit import AuditChain
652
+ audit_path = self._config.db_path.parent / "audit_chain.db"
653
+ self._audit_chain = AuditChain(audit_path)
654
+ for op in ("store", "recall", "delete"):
655
+ self._hooks.register_post(op, lambda ctx, _op=op: self._audit_chain.log(
656
+ operation=_op,
657
+ agent_id=ctx.get("agent_id", "unknown"),
658
+ profile_id=ctx.get("profile_id", self._profile_id),
659
+ content_hash=ctx.get("content_hash", ""),
660
+ ))
661
+ except Exception:
662
+ self._audit_chain = None
663
+
664
+ def _enrich_fact(self, fact: AtomicFact, record: MemoryRecord) -> AtomicFact:
665
+ """Enrich fact with embeddings, entities, temporal, emotional data."""
666
+ from superlocalmemory.encoding.emotional import tag_emotion, emotional_importance_boost
667
+ from superlocalmemory.encoding.signal_inference import infer_signal
668
+
669
+ embedding = self._embedder.embed(fact.content) if self._embedder else None
670
+ fisher_mean, fisher_variance = (None, None)
671
+ if self._embedder and embedding:
672
+ fisher_mean, fisher_variance = self._embedder.compute_fisher_params(embedding)
673
+
674
+ canonical = {}
675
+ if self._entity_resolver and fact.entities:
676
+ canonical = self._entity_resolver.resolve(fact.entities, self._profile_id)
677
+
678
+ temporal = {}
679
+ if self._temporal_parser:
680
+ temporal = self._temporal_parser.extract_dates_from_text(fact.content)
681
+
682
+ emotion = tag_emotion(fact.content)
683
+ signal = infer_signal(fact.content)
684
+
685
+ return AtomicFact(
686
+ fact_id=fact.fact_id, memory_id=record.memory_id,
687
+ profile_id=self._profile_id, content=fact.content,
688
+ fact_type=fact.fact_type, entities=fact.entities,
689
+ canonical_entities=list(canonical.values()),
690
+ observation_date=fact.observation_date or record.session_date,
691
+ referenced_date=fact.referenced_date or temporal.get("referenced_date"),
692
+ interval_start=fact.interval_start or temporal.get("interval_start"),
693
+ interval_end=fact.interval_end or temporal.get("interval_end"),
694
+ confidence=fact.confidence,
695
+ importance=min(1.0, fact.importance + emotional_importance_boost(emotion)),
696
+ evidence_count=fact.evidence_count,
697
+ source_turn_ids=fact.source_turn_ids, session_id=record.session_id,
698
+ embedding=embedding, fisher_mean=fisher_mean, fisher_variance=fisher_variance,
699
+ emotional_valence=emotion.valence, emotional_arousal=emotion.arousal,
700
+ signal_type=signal, created_at=fact.created_at,
701
+ )