superlocalmemory 2.8.6 → 3.0.1

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