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,204 @@
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
+ """Attribute-Based Access Control for memory operations.
6
+
7
+ Evaluates policies: (agent_id, profile_id, action) -> allow/deny.
8
+ Default: allow all (open access). Policies restrict specific agents.
9
+
10
+ Actions: "read", "write", "delete", "admin".
11
+ Policies are stored in-memory (loaded from DB on init).
12
+ Simple deny-list approach: if a deny policy matches, access is blocked.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ import sqlite3
19
+ from typing import Any, Optional
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # Valid ABAC actions
24
+ VALID_ACTIONS = frozenset({"read", "write", "delete", "admin"})
25
+
26
+ _POLICY_TABLE_SQL = """
27
+ CREATE TABLE IF NOT EXISTS abac_policies (
28
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
29
+ profile_id TEXT NOT NULL,
30
+ agent_id TEXT NOT NULL,
31
+ action TEXT NOT NULL,
32
+ deny INTEGER NOT NULL DEFAULT 1,
33
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
34
+ )
35
+ """
36
+
37
+
38
+ class AccessDenied(PermissionError):
39
+ """Raised when ABAC denies an operation."""
40
+
41
+
42
+ class ABACEngine:
43
+ """Attribute-Based Access Control for memory operations.
44
+
45
+ Evaluates policies: (agent_id, profile_id, action) -> allow/deny.
46
+ Default: allow all (open access). Deny policies restrict specific
47
+ agent+profile+action combinations.
48
+ """
49
+
50
+ def __init__(self, db: Optional[sqlite3.Connection] = None) -> None:
51
+ self._policies: list[dict[str, Any]] = []
52
+ self._db = db
53
+ if db is not None:
54
+ self._load_policies_from_db(db)
55
+
56
+ # ------------------------------------------------------------------
57
+ # Policy loading
58
+ # ------------------------------------------------------------------
59
+
60
+ def _load_policies_from_db(self, db: sqlite3.Connection) -> None:
61
+ """Load policies from the abac_policies table."""
62
+ try:
63
+ db.execute(_POLICY_TABLE_SQL)
64
+ db.commit()
65
+ rows = db.execute(
66
+ "SELECT profile_id, agent_id, action, deny "
67
+ "FROM abac_policies"
68
+ ).fetchall()
69
+ for row in rows:
70
+ self._policies.append({
71
+ "profile_id": row[0],
72
+ "agent_id": row[1],
73
+ "action": row[2],
74
+ "deny": bool(row[3]),
75
+ })
76
+ logger.info("Loaded %d ABAC policies from DB", len(self._policies))
77
+ except sqlite3.OperationalError as exc:
78
+ logger.warning("Failed to load ABAC policies: %s", exc)
79
+
80
+ # ------------------------------------------------------------------
81
+ # Policy management
82
+ # ------------------------------------------------------------------
83
+
84
+ def add_policy(
85
+ self,
86
+ profile_id: str,
87
+ agent_id: str,
88
+ action: str,
89
+ deny: bool = True,
90
+ ) -> None:
91
+ """Add an access control policy.
92
+
93
+ Args:
94
+ profile_id: Profile this policy applies to.
95
+ agent_id: Agent this policy applies to.
96
+ action: The action to control (read/write/delete/admin).
97
+ deny: If True, this is a deny policy. Default True.
98
+ """
99
+ if action not in VALID_ACTIONS:
100
+ raise ValueError(f"Invalid action '{action}'. Must be one of {VALID_ACTIONS}")
101
+ policy = {
102
+ "profile_id": profile_id,
103
+ "agent_id": agent_id,
104
+ "action": action,
105
+ "deny": deny,
106
+ }
107
+ self._policies.append(policy)
108
+ self._persist_policy(policy)
109
+
110
+ def remove_policy(
111
+ self,
112
+ profile_id: str,
113
+ agent_id: str,
114
+ action: str,
115
+ ) -> None:
116
+ """Remove a policy matching profile_id + agent_id + action."""
117
+ self._policies = [
118
+ p for p in self._policies
119
+ if not (
120
+ p["profile_id"] == profile_id
121
+ and p["agent_id"] == agent_id
122
+ and p["action"] == action
123
+ )
124
+ ]
125
+ if self._db is not None:
126
+ try:
127
+ self._db.execute(
128
+ "DELETE FROM abac_policies WHERE profile_id = ? AND agent_id = ? AND action = ?",
129
+ (profile_id, agent_id, action),
130
+ )
131
+ self._db.commit()
132
+ except sqlite3.OperationalError:
133
+ pass
134
+
135
+ def _persist_policy(self, policy: dict[str, Any]) -> None:
136
+ """Write a policy to DB so it survives restart."""
137
+ if self._db is None:
138
+ return
139
+ try:
140
+ self._db.execute(
141
+ "INSERT INTO abac_policies (profile_id, agent_id, action, deny) "
142
+ "VALUES (?, ?, ?, ?)",
143
+ (policy["profile_id"], policy["agent_id"], policy["action"], int(policy["deny"])),
144
+ )
145
+ self._db.commit()
146
+ except sqlite3.OperationalError as exc:
147
+ logger.warning("Failed to persist ABAC policy: %s", exc)
148
+
149
+ # ------------------------------------------------------------------
150
+ # Access evaluation
151
+ # ------------------------------------------------------------------
152
+
153
+ def check(self, agent_id: str, profile_id: str, action: str) -> bool:
154
+ """Evaluate access. Returns True if allowed, False if denied.
155
+
156
+ Default: allow if no deny policy matches the request.
157
+ Deny-first semantics: any matching deny policy blocks access.
158
+ """
159
+ for policy in self._policies:
160
+ if not policy.get("deny", True):
161
+ continue
162
+ if (
163
+ policy["agent_id"] == agent_id
164
+ and policy["profile_id"] == profile_id
165
+ and policy["action"] == action
166
+ ):
167
+ return False
168
+ return True
169
+
170
+ def check_or_raise(
171
+ self,
172
+ agent_id: str,
173
+ profile_id: str,
174
+ action: str,
175
+ ) -> None:
176
+ """Like check() but raises AccessDenied if denied."""
177
+ if not self.check(agent_id, profile_id, action):
178
+ raise AccessDenied(
179
+ f"Agent '{agent_id}' denied '{action}' "
180
+ f"on profile '{profile_id}'"
181
+ )
182
+
183
+ # ------------------------------------------------------------------
184
+ # Policy listing
185
+ # ------------------------------------------------------------------
186
+
187
+ def list_policies(
188
+ self,
189
+ profile_id: Optional[str] = None,
190
+ ) -> list[dict[str, Any]]:
191
+ """List all policies, optionally filtered by profile.
192
+
193
+ Args:
194
+ profile_id: If provided, only return policies for this profile.
195
+
196
+ Returns:
197
+ List of policy dicts with keys: profile_id, agent_id, action, deny.
198
+ """
199
+ if profile_id is None:
200
+ return [dict(p) for p in self._policies]
201
+ return [
202
+ dict(p) for p in self._policies
203
+ if p["profile_id"] == profile_id
204
+ ]
@@ -0,0 +1,314 @@
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
+ """Tamper-proof hash-chain audit log for compliance.
6
+
7
+ Every operation is logged with a SHA-256 hash that includes the previous
8
+ entry's hash, creating a chain. Tampering with any entry breaks the chain
9
+ and is detectable via verify_integrity().
10
+
11
+ The audit chain uses its OWN sqlite3 connection (not shared DB manager)
12
+ for independence — audit must survive even if the main DB is corrupted.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import hashlib
18
+ import json
19
+ import logging
20
+ import sqlite3
21
+ import threading
22
+ from datetime import datetime, timezone
23
+ from pathlib import Path
24
+ from typing import Any, Optional, Union
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ _GENESIS_HASH = "genesis"
29
+
30
+ _SCHEMA = """
31
+ CREATE TABLE IF NOT EXISTS audit_chain (
32
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
33
+ timestamp TEXT NOT NULL,
34
+ operation TEXT NOT NULL,
35
+ agent_id TEXT DEFAULT '',
36
+ profile_id TEXT DEFAULT '',
37
+ content_hash TEXT DEFAULT '',
38
+ prev_hash TEXT DEFAULT '',
39
+ event_hash TEXT NOT NULL,
40
+ metadata TEXT DEFAULT '{}'
41
+ );
42
+ """
43
+
44
+
45
+ def _compute_hash(
46
+ prev_hash: str,
47
+ operation: str,
48
+ agent_id: str,
49
+ profile_id: str,
50
+ content_hash: str,
51
+ timestamp: str,
52
+ ) -> str:
53
+ """Compute SHA-256 hash for an audit entry.
54
+
55
+ The hash incorporates: prev_hash + operation + agent_id +
56
+ profile_id + content_hash + timestamp.
57
+ """
58
+ payload = (
59
+ f"{prev_hash}{operation}{agent_id}"
60
+ f"{profile_id}{content_hash}{timestamp}"
61
+ )
62
+ return hashlib.sha256(payload.encode("utf-8")).hexdigest()
63
+
64
+
65
+ class AuditChain:
66
+ """Tamper-proof hash-chain audit log for compliance.
67
+
68
+ Every operation is logged with a hash that includes the previous hash,
69
+ creating a chain. Tampering with any entry breaks the chain.
70
+
71
+ Uses its own SQLite connection for independence from main DB.
72
+ """
73
+
74
+ def __init__(self, db_path: Optional[Union[str, Path]] = None) -> None:
75
+ self._db_path = str(db_path) if db_path else ":memory:"
76
+ self._is_memory = self._db_path == ":memory:"
77
+ self._lock = threading.Lock()
78
+ # For in-memory DBs, keep a persistent connection (each connect()
79
+ # to ":memory:" creates a separate empty database).
80
+ self._persistent_conn: Optional[sqlite3.Connection] = None
81
+ if self._is_memory:
82
+ self._persistent_conn = self._make_conn()
83
+ self._init_db()
84
+
85
+ # ------------------------------------------------------------------
86
+ # Internal helpers
87
+ # ------------------------------------------------------------------
88
+
89
+ @staticmethod
90
+ def _make_conn_from_path(path: str) -> sqlite3.Connection:
91
+ """Create a configured SQLite connection."""
92
+ conn = sqlite3.connect(path)
93
+ conn.execute("PRAGMA journal_mode=WAL")
94
+ conn.row_factory = sqlite3.Row
95
+ return conn
96
+
97
+ def _make_conn(self) -> sqlite3.Connection:
98
+ """Create a new configured connection to the audit database."""
99
+ return self._make_conn_from_path(self._db_path)
100
+
101
+ def _get_conn(self) -> sqlite3.Connection:
102
+ """Get a connection to the audit database.
103
+
104
+ For in-memory databases, returns the persistent connection.
105
+ For file databases, creates a new connection each time.
106
+ """
107
+ if self._is_memory and self._persistent_conn is not None:
108
+ return self._persistent_conn
109
+ return self._make_conn()
110
+
111
+ def _release_conn(self, conn: sqlite3.Connection) -> None:
112
+ """Release a connection. Only closes file-based connections."""
113
+ if not self._is_memory:
114
+ conn.close()
115
+
116
+ def _init_db(self) -> None:
117
+ """Initialize the audit_chain table."""
118
+ conn = self._get_conn()
119
+ try:
120
+ conn.executescript(_SCHEMA)
121
+ conn.commit()
122
+ finally:
123
+ self._release_conn(conn)
124
+
125
+ def _get_last_hash(self, conn: sqlite3.Connection) -> str:
126
+ """Get the hash of the most recent entry, or genesis."""
127
+ row = conn.execute(
128
+ "SELECT event_hash FROM audit_chain "
129
+ "ORDER BY id DESC LIMIT 1"
130
+ ).fetchone()
131
+ return row["event_hash"] if row else _GENESIS_HASH
132
+
133
+ # ------------------------------------------------------------------
134
+ # Public API
135
+ # ------------------------------------------------------------------
136
+
137
+ def log(
138
+ self,
139
+ operation: str,
140
+ agent_id: str = "",
141
+ profile_id: str = "",
142
+ content_hash: str = "",
143
+ metadata: Optional[dict[str, Any]] = None,
144
+ ) -> str:
145
+ """Log an audit event. Returns the event hash.
146
+
147
+ Args:
148
+ operation: Type of operation (store, recall, delete, etc.).
149
+ agent_id: ID of the agent performing the operation.
150
+ profile_id: ID of the profile being accessed.
151
+ content_hash: Hash of the content involved (optional).
152
+ metadata: Additional metadata dict (optional).
153
+
154
+ Returns:
155
+ The SHA-256 event hash for this entry.
156
+ """
157
+ ts = datetime.now(timezone.utc).isoformat()
158
+ metadata_str = json.dumps(metadata or {}, sort_keys=True)
159
+
160
+ with self._lock:
161
+ conn = self._get_conn()
162
+ try:
163
+ prev_hash = self._get_last_hash(conn)
164
+ event_hash = _compute_hash(
165
+ prev_hash, operation, agent_id,
166
+ profile_id, content_hash, ts,
167
+ )
168
+ conn.execute(
169
+ "INSERT INTO audit_chain "
170
+ "(timestamp, operation, agent_id, profile_id, "
171
+ " content_hash, prev_hash, event_hash, metadata) "
172
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
173
+ (
174
+ ts, operation, agent_id, profile_id,
175
+ content_hash, prev_hash, event_hash, metadata_str,
176
+ ),
177
+ )
178
+ conn.commit()
179
+ return event_hash
180
+ finally:
181
+ self._release_conn(conn)
182
+
183
+ def query(
184
+ self,
185
+ profile_id: Optional[str] = None,
186
+ agent_id: Optional[str] = None,
187
+ operation: Optional[str] = None,
188
+ start_date: Optional[str] = None,
189
+ end_date: Optional[str] = None,
190
+ limit: int = 100,
191
+ ) -> list[dict[str, Any]]:
192
+ """Query audit events with filters.
193
+
194
+ Args:
195
+ profile_id: Filter by profile ID.
196
+ agent_id: Filter by agent ID.
197
+ operation: Filter by operation type.
198
+ start_date: ISO date string for range start.
199
+ end_date: ISO date string for range end.
200
+ limit: Maximum number of events to return.
201
+
202
+ Returns:
203
+ List of event dicts ordered by id descending.
204
+ """
205
+ clauses: list[str] = []
206
+ params: list[Any] = []
207
+
208
+ if profile_id is not None:
209
+ clauses.append("profile_id = ?")
210
+ params.append(profile_id)
211
+ if agent_id is not None:
212
+ clauses.append("agent_id = ?")
213
+ params.append(agent_id)
214
+ if operation is not None:
215
+ clauses.append("operation = ?")
216
+ params.append(operation)
217
+ if start_date is not None:
218
+ clauses.append("timestamp >= ?")
219
+ params.append(start_date)
220
+ if end_date is not None:
221
+ clauses.append("timestamp <= ?")
222
+ params.append(end_date)
223
+
224
+ where = f"WHERE {' AND '.join(clauses)}" if clauses else ""
225
+ sql = (
226
+ f"SELECT id, timestamp, operation, agent_id, profile_id, "
227
+ f"content_hash, prev_hash, event_hash, metadata "
228
+ f"FROM audit_chain {where} "
229
+ f"ORDER BY id DESC LIMIT ?"
230
+ )
231
+ params.append(limit)
232
+
233
+ with self._lock:
234
+ conn = self._get_conn()
235
+ try:
236
+ rows = conn.execute(sql, params).fetchall()
237
+ return [dict(r) for r in rows]
238
+ finally:
239
+ self._release_conn(conn)
240
+
241
+ def verify_integrity(self) -> bool:
242
+ """Verify the entire hash chain. Returns True if chain is intact.
243
+
244
+ Walks every entry in order, recomputes each hash, and verifies
245
+ it matches the stored hash. Any mismatch means tampering.
246
+ """
247
+ conn = self._get_conn()
248
+ try:
249
+ rows = conn.execute(
250
+ "SELECT id, timestamp, operation, agent_id, profile_id, "
251
+ "content_hash, prev_hash, event_hash "
252
+ "FROM audit_chain ORDER BY id"
253
+ ).fetchall()
254
+ finally:
255
+ self._release_conn(conn)
256
+
257
+ if not rows:
258
+ return True
259
+
260
+ expected_prev = _GENESIS_HASH
261
+ for row in rows:
262
+ row_dict = dict(row)
263
+
264
+ # Check the prev_hash link
265
+ if row_dict["prev_hash"] != expected_prev:
266
+ logger.warning(
267
+ "Audit chain prev_hash mismatch at entry %d",
268
+ row_dict["id"],
269
+ )
270
+ return False
271
+
272
+ # Recompute the entry hash
273
+ computed = _compute_hash(
274
+ row_dict["prev_hash"],
275
+ row_dict["operation"],
276
+ row_dict["agent_id"],
277
+ row_dict["profile_id"],
278
+ row_dict["content_hash"],
279
+ row_dict["timestamp"],
280
+ )
281
+ if computed != row_dict["event_hash"]:
282
+ logger.warning(
283
+ "Audit chain event_hash mismatch at entry %d",
284
+ row_dict["id"],
285
+ )
286
+ return False
287
+
288
+ expected_prev = row_dict["event_hash"]
289
+
290
+ return True
291
+
292
+ def get_stats(self) -> dict[str, int]:
293
+ """Get audit statistics (event counts by operation type).
294
+
295
+ Returns:
296
+ Dict mapping operation names to their counts, plus
297
+ a 'total' key with the chain length.
298
+ """
299
+ conn = self._get_conn()
300
+ try:
301
+ rows = conn.execute(
302
+ "SELECT operation, COUNT(*) AS cnt "
303
+ "FROM audit_chain GROUP BY operation"
304
+ ).fetchall()
305
+ stats: dict[str, int] = {}
306
+ total = 0
307
+ for row in rows:
308
+ count = row["cnt"]
309
+ stats[row["operation"]] = count
310
+ total += count
311
+ stats["total"] = total
312
+ return stats
313
+ finally:
314
+ self._release_conn(conn)
@@ -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
+ ]