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,131 @@
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 — EU AI Act Compliance Verification.
6
+
7
+ Verifies that each operating mode meets EU AI Act requirements.
8
+ Mode A and B: FULL compliance (zero cloud, zero generative AI / local only).
9
+ Mode C: NOT compliant (cloud LLM processing).
10
+
11
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from dataclasses import dataclass
18
+ from datetime import UTC, datetime
19
+
20
+ from superlocalmemory.core.modes import get_capabilities
21
+ from superlocalmemory.storage.models import Mode
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class ComplianceReport:
28
+ """EU AI Act compliance assessment for a specific mode."""
29
+
30
+ mode: Mode
31
+ compliant: bool
32
+ risk_category: str # "minimal" / "limited" / "high" / "unacceptable"
33
+ data_stays_local: bool
34
+ uses_generative_ai: bool
35
+ transparency_met: bool
36
+ human_oversight: bool
37
+ findings: list[str]
38
+ timestamp: str
39
+
40
+
41
+ class EUAIActChecker:
42
+ """Verify EU AI Act compliance for each operating mode.
43
+
44
+ EU AI Act (effective Aug 2025) classifies AI systems by risk:
45
+ - Minimal risk: No obligations (most AI systems)
46
+ - Limited risk: Transparency obligations
47
+ - High risk: Strict requirements (biometric, critical infra)
48
+ - Unacceptable: Banned
49
+
50
+ Memory systems are generally "minimal risk" UNLESS they process
51
+ personal data via cloud AI services (then "limited risk" with
52
+ transparency obligations).
53
+ """
54
+
55
+ def check_compliance(self, mode: Mode) -> ComplianceReport:
56
+ """Generate compliance report for a mode."""
57
+ caps = get_capabilities(mode)
58
+ findings: list[str] = []
59
+
60
+ # Data locality
61
+ data_local = caps.data_stays_local
62
+ if not data_local:
63
+ findings.append(
64
+ "Data leaves device for cloud LLM processing. "
65
+ "Requires Data Processing Agreement (DPA) with provider."
66
+ )
67
+
68
+ # Generative AI usage
69
+ uses_gen_ai = caps.llm_fact_extraction or caps.llm_answer_generation
70
+ local_gen_ai = uses_gen_ai and data_local
71
+
72
+ if uses_gen_ai and not data_local:
73
+ findings.append(
74
+ "Uses cloud generative AI. EU AI Act Art. 52 requires "
75
+ "transparency: users must be informed AI generates content."
76
+ )
77
+
78
+ # Transparency
79
+ transparency = True # We always disclose AI usage
80
+ findings.append("Transparency requirement MET: system identifies as AI-assisted.")
81
+
82
+ # Human oversight
83
+ human_oversight = True # User controls all memory operations
84
+ findings.append("Human oversight MET: user controls store/recall/delete.")
85
+
86
+ # Risk classification
87
+ if not uses_gen_ai:
88
+ risk = "minimal"
89
+ findings.append("Minimal risk: no generative AI, local processing only.")
90
+ elif local_gen_ai:
91
+ risk = "minimal"
92
+ findings.append("Minimal risk: generative AI is local-only (Ollama).")
93
+ else:
94
+ risk = "limited"
95
+ findings.append(
96
+ "Limited risk: cloud generative AI requires transparency "
97
+ "disclosure and DPA with cloud provider."
98
+ )
99
+
100
+ compliant = caps.eu_ai_act_compliant
101
+ if not compliant:
102
+ findings.append(
103
+ "Mode C is NOT EU AI Act compliant by design. "
104
+ "Use Mode A or B for EU-compliant deployments."
105
+ )
106
+
107
+ return ComplianceReport(
108
+ mode=mode,
109
+ compliant=compliant,
110
+ risk_category=risk,
111
+ data_stays_local=data_local,
112
+ uses_generative_ai=uses_gen_ai,
113
+ transparency_met=transparency,
114
+ human_oversight=human_oversight,
115
+ findings=findings,
116
+ timestamp=datetime.now(UTC).isoformat(),
117
+ )
118
+
119
+ def verify_all_modes(self) -> dict[str, ComplianceReport]:
120
+ """Generate compliance reports for all three modes."""
121
+ return {
122
+ mode.value: self.check_compliance(mode)
123
+ for mode in Mode
124
+ }
125
+
126
+ def get_compliant_modes(self) -> list[Mode]:
127
+ """Return list of EU AI Act compliant modes."""
128
+ return [
129
+ mode for mode in Mode
130
+ if get_capabilities(mode).eu_ai_act_compliant
131
+ ]
@@ -0,0 +1,294 @@
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 — GDPR Compliance.
6
+
7
+ Implements GDPR rights: right to access, right to erasure (forget),
8
+ right to data portability (export), and audit trail.
9
+ Profile-scoped. All operations logged to compliance_audit.
10
+
11
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import logging
18
+ from datetime import UTC, datetime
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class GDPRCompliance:
24
+ """GDPR compliance operations for memory data.
25
+
26
+ Supports:
27
+ - Right to Access (Art. 15): Export all data for a profile
28
+ - Right to Erasure (Art. 17): Delete all data for a profile/entity
29
+ - Right to Portability (Art. 20): Export in machine-readable format
30
+ - Audit Trail: Log all data operations
31
+ """
32
+
33
+ def __init__(self, db) -> None:
34
+ self._db = db
35
+
36
+ # -- Right to Access (Art. 15) -----------------------------------------
37
+
38
+ def export_profile_data(self, profile_id: str) -> dict:
39
+ """Export ALL data for a profile in machine-readable format.
40
+
41
+ Returns a dict containing all memories, facts, entities,
42
+ edges, trust scores, feedback, and behavioral patterns.
43
+ """
44
+ self._audit("export", "profile", profile_id, "Full data export")
45
+
46
+ data: dict = {"profile_id": profile_id, "exported_at": _now()}
47
+
48
+ # Memories
49
+ rows = self._db.execute(
50
+ "SELECT * FROM memories WHERE profile_id = ?", (profile_id,)
51
+ )
52
+ data["memories"] = [dict(r) for r in rows]
53
+
54
+ # Facts
55
+ rows = self._db.execute(
56
+ "SELECT * FROM atomic_facts WHERE profile_id = ?", (profile_id,)
57
+ )
58
+ data["facts"] = [dict(r) for r in rows]
59
+
60
+ # Entities
61
+ rows = self._db.execute(
62
+ "SELECT * FROM canonical_entities WHERE profile_id = ?", (profile_id,)
63
+ )
64
+ data["entities"] = [dict(r) for r in rows]
65
+
66
+ # Graph edges
67
+ rows = self._db.execute(
68
+ "SELECT * FROM graph_edges WHERE profile_id = ?", (profile_id,)
69
+ )
70
+ data["edges"] = [dict(r) for r in rows]
71
+
72
+ # Trust scores
73
+ rows = self._db.execute(
74
+ "SELECT * FROM trust_scores WHERE profile_id = ?", (profile_id,)
75
+ )
76
+ data["trust_scores"] = [dict(r) for r in rows]
77
+
78
+ # Feedback
79
+ rows = self._db.execute(
80
+ "SELECT * FROM feedback_records WHERE profile_id = ?", (profile_id,)
81
+ )
82
+ data["feedback"] = [dict(r) for r in rows]
83
+
84
+ # Entity profiles
85
+ rows = self._db.execute(
86
+ "SELECT * FROM entity_profiles WHERE profile_id = ?", (profile_id,)
87
+ )
88
+ data["entity_profiles"] = [dict(r) for r in rows]
89
+
90
+ # Memory scenes
91
+ rows = self._db.execute(
92
+ "SELECT * FROM memory_scenes WHERE profile_id = ?", (profile_id,)
93
+ )
94
+ data["scenes"] = [dict(r) for r in rows]
95
+
96
+ # Temporal events
97
+ rows = self._db.execute(
98
+ "SELECT * FROM temporal_events WHERE profile_id = ?", (profile_id,)
99
+ )
100
+ data["temporal_events"] = [dict(r) for r in rows]
101
+
102
+ # Consolidation log
103
+ rows = self._db.execute(
104
+ "SELECT * FROM consolidation_log WHERE profile_id = ?", (profile_id,)
105
+ )
106
+ data["consolidation_log"] = [dict(r) for r in rows]
107
+
108
+ # Behavioral patterns
109
+ rows = self._db.execute(
110
+ "SELECT * FROM behavioral_patterns WHERE profile_id = ?", (profile_id,)
111
+ )
112
+ data["behavioral_patterns"] = [dict(r) for r in rows]
113
+
114
+ # Action outcomes
115
+ rows = self._db.execute(
116
+ "SELECT * FROM action_outcomes WHERE profile_id = ?", (profile_id,)
117
+ )
118
+ data["action_outcomes"] = [dict(r) for r in rows]
119
+
120
+ # Compliance audit trail
121
+ rows = self._db.execute(
122
+ "SELECT * FROM compliance_audit WHERE profile_id = ?", (profile_id,)
123
+ )
124
+ data["compliance_audit"] = [dict(r) for r in rows]
125
+
126
+ # Provenance (data lineage — EU AI Act Art. 10)
127
+ rows = self._db.execute(
128
+ "SELECT * FROM provenance WHERE profile_id = ?", (profile_id,)
129
+ )
130
+ data["provenance"] = [dict(r) for r in rows]
131
+
132
+ # Entity aliases (indirect PII via entity relationships)
133
+ rows = self._db.execute(
134
+ "SELECT ea.* FROM entity_aliases ea "
135
+ "JOIN canonical_entities ce ON ea.entity_id = ce.entity_id "
136
+ "WHERE ce.profile_id = ?", (profile_id,)
137
+ )
138
+ data["entity_aliases"] = [dict(r) for r in rows]
139
+
140
+ # Profile record itself
141
+ rows = self._db.execute(
142
+ "SELECT * FROM profiles WHERE profile_id = ?", (profile_id,)
143
+ )
144
+ data["profile_record"] = [dict(r) for r in rows]
145
+
146
+ data["total_items"] = sum(
147
+ len(v) for v in data.values() if isinstance(v, list)
148
+ )
149
+
150
+ logger.info("Exported %d items for profile '%s'", data["total_items"], profile_id)
151
+ return data
152
+
153
+ # -- Right to Erasure (Art. 17) ----------------------------------------
154
+
155
+ def forget_profile(self, profile_id: str) -> dict:
156
+ """Delete ALL data for a profile (right to be forgotten).
157
+
158
+ CASCADE deletes handle most cleanup via foreign keys.
159
+ Returns counts of deleted items.
160
+ """
161
+ if profile_id == "default":
162
+ raise ValueError("Cannot delete the default profile via GDPR erasure. "
163
+ "Use profile deletion instead.")
164
+
165
+ self._audit("delete", "profile", profile_id, "GDPR erasure request")
166
+
167
+ counts: dict[str, int] = {}
168
+ tables = [
169
+ "compliance_audit", "action_outcomes", "behavioral_patterns",
170
+ "feedback_records", "trust_scores", "provenance",
171
+ "consolidation_log", "graph_edges", "temporal_events",
172
+ "memory_scenes", "entity_profiles", "bm25_tokens",
173
+ "atomic_facts", "memories", "canonical_entities",
174
+ ]
175
+ for table in tables:
176
+ rows = self._db.execute(
177
+ f"SELECT COUNT(*) AS c FROM {table} WHERE profile_id = ?",
178
+ (profile_id,),
179
+ )
180
+ counts[table] = int(dict(rows[0])["c"]) if rows else 0
181
+ self._db.execute(
182
+ f"DELETE FROM {table} WHERE profile_id = ?", (profile_id,)
183
+ )
184
+
185
+ # Delete entity aliases (orphan PII via entity relationships)
186
+ self._db.execute(
187
+ "DELETE FROM entity_aliases WHERE entity_id IN "
188
+ "(SELECT entity_id FROM canonical_entities WHERE profile_id = ?)",
189
+ (profile_id,),
190
+ )
191
+
192
+ # Delete profile itself
193
+ self._db.execute(
194
+ "DELETE FROM profiles WHERE profile_id = ?", (profile_id,)
195
+ )
196
+ counts["profiles"] = 1
197
+
198
+ # Erase learning database (separate DB file)
199
+ try:
200
+ from superlocalmemory.learning.database import LearningDatabase
201
+ from superlocalmemory.core.config import DEFAULT_BASE_DIR
202
+ learning_db = LearningDatabase(DEFAULT_BASE_DIR / "learning.db")
203
+ learning_db.reset(profile_id)
204
+ counts["learning_db"] = 1
205
+ except Exception:
206
+ pass
207
+
208
+ # VACUUM to remove deleted data from physical file
209
+ try:
210
+ self._db.execute("VACUUM")
211
+ except Exception:
212
+ pass
213
+
214
+ logger.info("GDPR erasure for '%s': %s", profile_id, counts)
215
+ return counts
216
+
217
+ def forget_entity(self, entity_name: str, profile_id: str) -> dict:
218
+ """Delete all data related to a specific entity.
219
+
220
+ Removes facts mentioning the entity, edges, temporal events,
221
+ and the entity itself. For targeted erasure requests.
222
+ """
223
+ self._audit("delete", "entity", entity_name,
224
+ f"GDPR entity erasure in profile {profile_id}",
225
+ profile_id=profile_id)
226
+
227
+ entity = self._db.get_entity_by_name(entity_name, profile_id)
228
+ if entity is None:
229
+ return {"deleted": 0, "entity": entity_name, "found": False}
230
+
231
+ eid = entity.entity_id
232
+ counts: dict[str, int] = {}
233
+
234
+ # Delete facts mentioning this entity
235
+ rows = self._db.execute(
236
+ "SELECT fact_id FROM atomic_facts WHERE profile_id = ? "
237
+ "AND canonical_entities_json LIKE ?",
238
+ (profile_id, f'%"{eid}"%'),
239
+ )
240
+ fact_ids = [dict(r)["fact_id"] for r in rows]
241
+ for fid in fact_ids:
242
+ self._db.delete_fact(fid)
243
+ counts["facts"] = len(fact_ids)
244
+
245
+ # Delete temporal events
246
+ self._db.execute(
247
+ "DELETE FROM temporal_events WHERE entity_id = ? AND profile_id = ?",
248
+ (eid, profile_id),
249
+ )
250
+
251
+ # Delete entity profile
252
+ self._db.execute(
253
+ "DELETE FROM entity_profiles WHERE entity_id = ? AND profile_id = ?",
254
+ (eid, profile_id),
255
+ )
256
+
257
+ # Delete aliases + entity
258
+ self._db.execute("DELETE FROM entity_aliases WHERE entity_id = ?", (eid,))
259
+ self._db.execute("DELETE FROM canonical_entities WHERE entity_id = ?", (eid,))
260
+ counts["entity"] = 1
261
+
262
+ logger.info("Entity erasure '%s' in '%s': %s", entity_name, profile_id, counts)
263
+ return counts
264
+
265
+ # -- Audit Trail -------------------------------------------------------
266
+
267
+ def get_audit_trail(
268
+ self, profile_id: str, limit: int = 100
269
+ ) -> list[dict]:
270
+ """Get compliance audit trail for a profile."""
271
+ rows = self._db.execute(
272
+ "SELECT * FROM compliance_audit WHERE profile_id = ? "
273
+ "ORDER BY timestamp DESC LIMIT ?",
274
+ (profile_id, limit),
275
+ )
276
+ return [dict(r) for r in rows]
277
+
278
+ def _audit(
279
+ self, action: str, target_type: str, target_id: str, details: str,
280
+ profile_id: str | None = None,
281
+ ) -> None:
282
+ """Log a compliance action."""
283
+ from superlocalmemory.storage.models import _new_id
284
+ pid = profile_id if profile_id is not None else target_id
285
+ self._db.execute(
286
+ "INSERT INTO compliance_audit "
287
+ "(audit_id, profile_id, action, target_type, target_id, details, timestamp) "
288
+ "VALUES (?,?,?,?,?,?,?)",
289
+ (_new_id(), pid, action, target_type, target_id, details, _now()),
290
+ )
291
+
292
+
293
+ def _now() -> str:
294
+ return datetime.now(UTC).isoformat()
@@ -0,0 +1,158 @@
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 — Memory Lifecycle Management.
6
+
7
+ Implements Active → Warm → Cold → Archived state machine.
8
+ Coupled with Langevin dynamics: positions naturally create lifecycle states.
9
+
10
+ Ported from V2.8 with Langevin coupling (Innovation's unique feature).
11
+
12
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ from datetime import UTC, datetime, timedelta
19
+
20
+ from superlocalmemory.storage.models import AtomicFact, MemoryLifecycle
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Lifecycle transition thresholds (days since last access)
25
+ _ACTIVE_MAX_DAYS = 7 # Active for 7 days after last access
26
+ _WARM_MAX_DAYS = 30 # Warm for 30 days
27
+ _COLD_MAX_DAYS = 90 # Cold for 90 days, then archived
28
+
29
+ # Langevin weight thresholds (if Langevin is available)
30
+ _ACTIVE_WEIGHT_MIN = 0.7
31
+ _WARM_WEIGHT_MIN = 0.4
32
+ _COLD_WEIGHT_MIN = 0.1
33
+
34
+
35
+ class LifecycleManager:
36
+ """Manage memory lifecycle states.
37
+
38
+ Two complementary strategies:
39
+ 1. Time-based: days since last access → state transition
40
+ 2. Langevin-based: position on Poincaré ball → lifecycle weight → state
41
+
42
+ When Langevin is available, it takes precedence (more nuanced).
43
+ Time-based is the fallback for Mode A (no dynamics).
44
+ """
45
+
46
+ def __init__(self, db, langevin=None) -> None:
47
+ self._db = db
48
+ self._langevin = langevin
49
+
50
+ def get_lifecycle_state(self, fact: AtomicFact) -> MemoryLifecycle:
51
+ """Determine current lifecycle state for a fact."""
52
+ # Strategy 1: Langevin-based (if available and position exists)
53
+ if self._langevin is not None and fact.langevin_position:
54
+ weight = self._langevin.compute_lifecycle_weight(fact.langevin_position)
55
+ return self._langevin.get_lifecycle_state(weight)
56
+
57
+ # Strategy 2: Time-based fallback
58
+ return self._time_based_state(fact)
59
+
60
+ def update_lifecycle(self, fact_id: str, profile_id: str) -> MemoryLifecycle:
61
+ """Recompute and persist lifecycle state for a fact."""
62
+ rows = self._db.execute(
63
+ "SELECT lifecycle, access_count, created_at FROM atomic_facts "
64
+ "WHERE fact_id = ? AND profile_id = ?",
65
+ (fact_id, profile_id),
66
+ )
67
+ if not rows:
68
+ return MemoryLifecycle.ACTIVE
69
+
70
+ d = dict(rows[0])
71
+ days = self._days_since(d.get("created_at", ""))
72
+ access = d.get("access_count", 0)
73
+
74
+ if access > 10 or days < _ACTIVE_MAX_DAYS:
75
+ state = MemoryLifecycle.ACTIVE
76
+ elif days < _WARM_MAX_DAYS:
77
+ state = MemoryLifecycle.WARM
78
+ elif days < _COLD_MAX_DAYS:
79
+ state = MemoryLifecycle.COLD
80
+ else:
81
+ state = MemoryLifecycle.ARCHIVED
82
+
83
+ current = d.get("lifecycle", "active")
84
+ if current != state.value:
85
+ self._db.update_fact(fact_id, {"lifecycle": state})
86
+ logger.debug("Fact %s: %s → %s", fact_id, current, state.value)
87
+
88
+ return state
89
+
90
+ def run_maintenance(self, profile_id: str) -> dict[str, int]:
91
+ """Run lifecycle maintenance on all facts in a profile.
92
+
93
+ If Langevin is available, evolve positions and update states.
94
+ Otherwise, use time-based transitions.
95
+ """
96
+ facts = self._db.get_all_facts(profile_id)
97
+ counts: dict[str, int] = {s.value: 0 for s in MemoryLifecycle}
98
+ transitions = 0
99
+
100
+ for fact in facts:
101
+ old_state = fact.lifecycle
102
+
103
+ if self._langevin is not None and fact.langevin_position:
104
+ # Langevin step
105
+ age = self._days_since(fact.created_at)
106
+ new_pos, weight = self._langevin.step(
107
+ fact.langevin_position, fact.access_count, age, fact.importance
108
+ )
109
+ new_state = self._langevin.get_lifecycle_state(weight)
110
+ # Persist position + state
111
+ self._db.update_fact(fact.fact_id, {
112
+ "langevin_position": new_pos,
113
+ "lifecycle": new_state,
114
+ })
115
+ else:
116
+ new_state = self._time_based_state(fact)
117
+ if new_state != old_state:
118
+ self._db.update_fact(fact.fact_id, {"lifecycle": new_state})
119
+
120
+ counts[new_state.value] = counts.get(new_state.value, 0) + 1
121
+ if new_state != old_state:
122
+ transitions += 1
123
+
124
+ counts["transitions"] = transitions
125
+ logger.info("Lifecycle maintenance for '%s': %s", profile_id, counts)
126
+ return counts
127
+
128
+ def get_archived_facts(self, profile_id: str) -> list[AtomicFact]:
129
+ """Get all archived facts (candidates for deletion/export)."""
130
+ rows = self._db.execute(
131
+ "SELECT * FROM atomic_facts WHERE profile_id = ? AND lifecycle = 'archived'",
132
+ (profile_id,),
133
+ )
134
+ return [self._db._row_to_fact(r) for r in rows]
135
+
136
+ # -- Internal ----------------------------------------------------------
137
+
138
+ def _time_based_state(self, fact: AtomicFact) -> MemoryLifecycle:
139
+ """Determine lifecycle state from time since creation + access count."""
140
+ days = self._days_since(fact.created_at)
141
+ if fact.access_count > 10 or days < _ACTIVE_MAX_DAYS:
142
+ return MemoryLifecycle.ACTIVE
143
+ if days < _WARM_MAX_DAYS:
144
+ return MemoryLifecycle.WARM
145
+ if days < _COLD_MAX_DAYS:
146
+ return MemoryLifecycle.COLD
147
+ return MemoryLifecycle.ARCHIVED
148
+
149
+ @staticmethod
150
+ def _days_since(iso_date: str) -> float:
151
+ """Days since an ISO date string."""
152
+ if not iso_date:
153
+ return 0.0
154
+ try:
155
+ dt = datetime.fromisoformat(iso_date)
156
+ return (datetime.now(UTC) - dt).total_seconds() / 86400.0
157
+ except (ValueError, TypeError):
158
+ return 0.0