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,317 @@
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
+ """V2 to V3 database migration.
6
+
7
+ Detects V2 installations, backs up data, extends schema with V3 tables,
8
+ and creates backward-compatible symlinks.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ import shutil
15
+ import sqlite3
16
+ from datetime import datetime, UTC
17
+ from pathlib import Path
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ V2_BASE = Path.home() / ".claude-memory"
22
+ V3_BASE = Path.home() / ".superlocalmemory"
23
+ V2_DB_NAME = "memory.db"
24
+ BACKUP_NAME = "memory-v2-backup.db"
25
+
26
+ # V3 tables to add during migration
27
+ V3_TABLES_SQL = [
28
+ """CREATE TABLE IF NOT EXISTS semantic_facts (
29
+ fact_id TEXT PRIMARY KEY,
30
+ memory_id TEXT,
31
+ profile_id TEXT NOT NULL DEFAULT 'default',
32
+ content TEXT NOT NULL,
33
+ fact_type TEXT DEFAULT 'world',
34
+ confidence REAL DEFAULT 0.7,
35
+ speaker TEXT DEFAULT '',
36
+ embedding BLOB,
37
+ fisher_mean BLOB,
38
+ fisher_variance BLOB,
39
+ access_count INTEGER DEFAULT 0,
40
+ observation_date TEXT,
41
+ referenced_date TEXT,
42
+ interval_start TEXT,
43
+ interval_end TEXT,
44
+ canonical_entities TEXT DEFAULT '[]',
45
+ created_at TEXT,
46
+ updated_at TEXT
47
+ )""",
48
+ """CREATE TABLE IF NOT EXISTS kg_nodes (
49
+ node_id TEXT PRIMARY KEY,
50
+ profile_id TEXT NOT NULL DEFAULT 'default',
51
+ entity_name TEXT NOT NULL,
52
+ entity_type TEXT DEFAULT 'unknown',
53
+ aliases TEXT DEFAULT '[]',
54
+ fact_count INTEGER DEFAULT 0,
55
+ created_at TEXT
56
+ )""",
57
+ """CREATE TABLE IF NOT EXISTS memory_edges (
58
+ edge_id TEXT PRIMARY KEY,
59
+ profile_id TEXT NOT NULL DEFAULT 'default',
60
+ source_id TEXT NOT NULL,
61
+ target_id TEXT NOT NULL,
62
+ edge_type TEXT DEFAULT 'semantic',
63
+ weight REAL DEFAULT 1.0,
64
+ created_at TEXT
65
+ )""",
66
+ """CREATE TABLE IF NOT EXISTS memory_scenes (
67
+ scene_id TEXT PRIMARY KEY,
68
+ profile_id TEXT NOT NULL DEFAULT 'default',
69
+ label TEXT DEFAULT '',
70
+ fact_ids TEXT DEFAULT '[]',
71
+ created_at TEXT
72
+ )""",
73
+ """CREATE TABLE IF NOT EXISTS bm25_tokens (
74
+ fact_id TEXT NOT NULL,
75
+ profile_id TEXT NOT NULL DEFAULT 'default',
76
+ tokens TEXT NOT NULL,
77
+ doc_length INTEGER DEFAULT 0,
78
+ PRIMARY KEY (fact_id, profile_id)
79
+ )""",
80
+ """CREATE TABLE IF NOT EXISTS temporal_events (
81
+ event_id TEXT PRIMARY KEY,
82
+ profile_id TEXT NOT NULL DEFAULT 'default',
83
+ entity_id TEXT,
84
+ fact_id TEXT,
85
+ observation_date TEXT,
86
+ referenced_date TEXT,
87
+ interval_start TEXT,
88
+ interval_end TEXT,
89
+ description TEXT DEFAULT ''
90
+ )""",
91
+ """CREATE TABLE IF NOT EXISTS memory_observations (
92
+ obs_id TEXT PRIMARY KEY,
93
+ profile_id TEXT NOT NULL DEFAULT 'default',
94
+ entity_id TEXT NOT NULL,
95
+ observation TEXT NOT NULL,
96
+ source_fact_id TEXT,
97
+ created_at TEXT
98
+ )""",
99
+ """CREATE TABLE IF NOT EXISTS contradictions (
100
+ contradiction_id TEXT PRIMARY KEY,
101
+ profile_id TEXT NOT NULL DEFAULT 'default',
102
+ fact_id_a TEXT NOT NULL,
103
+ fact_id_b TEXT NOT NULL,
104
+ severity REAL DEFAULT 0.5,
105
+ resolved INTEGER DEFAULT 0,
106
+ created_at TEXT
107
+ )""",
108
+ """CREATE TABLE IF NOT EXISTS langevin_state (
109
+ fact_id TEXT PRIMARY KEY,
110
+ profile_id TEXT NOT NULL DEFAULT 'default',
111
+ position REAL DEFAULT 0.5,
112
+ velocity REAL DEFAULT 0.0,
113
+ updated_at TEXT
114
+ )""",
115
+ """CREATE TABLE IF NOT EXISTS sheaf_sections (
116
+ section_id TEXT PRIMARY KEY,
117
+ profile_id TEXT NOT NULL DEFAULT 'default',
118
+ fact_id TEXT NOT NULL,
119
+ section_data BLOB,
120
+ created_at TEXT
121
+ )""",
122
+ """CREATE TABLE IF NOT EXISTS v3_config (
123
+ key TEXT PRIMARY KEY,
124
+ value TEXT NOT NULL,
125
+ updated_at TEXT
126
+ )""",
127
+ ]
128
+
129
+ # Indexes for V3 tables
130
+ V3_INDEXES_SQL = [
131
+ "CREATE INDEX IF NOT EXISTS idx_facts_profile ON semantic_facts(profile_id)",
132
+ "CREATE INDEX IF NOT EXISTS idx_facts_memory ON semantic_facts(memory_id)",
133
+ "CREATE INDEX IF NOT EXISTS idx_nodes_profile ON kg_nodes(profile_id)",
134
+ "CREATE INDEX IF NOT EXISTS idx_edges_source ON memory_edges(source_id)",
135
+ "CREATE INDEX IF NOT EXISTS idx_edges_target ON memory_edges(target_id)",
136
+ "CREATE INDEX IF NOT EXISTS idx_scenes_profile ON memory_scenes(profile_id)",
137
+ "CREATE INDEX IF NOT EXISTS idx_temporal_entity ON temporal_events(entity_id)",
138
+ "CREATE INDEX IF NOT EXISTS idx_observations_entity ON memory_observations(entity_id)",
139
+ ]
140
+
141
+
142
+ class V2Migrator:
143
+ """Migrate V2 database to V3 schema."""
144
+
145
+ def __init__(self, home: Path | None = None):
146
+ self._home = home or Path.home()
147
+ self._v2_base = self._home / ".claude-memory"
148
+ self._v3_base = self._home / ".superlocalmemory"
149
+ self._v2_db = self._v2_base / V2_DB_NAME
150
+ self._v3_db = self._v3_base / V2_DB_NAME
151
+ self._backup_db = self._v3_base / BACKUP_NAME
152
+
153
+ def detect_v2(self) -> bool:
154
+ """Check if a V2 installation exists."""
155
+ return self._v2_db.exists() and self._v2_db.is_file()
156
+
157
+ def is_already_migrated(self) -> bool:
158
+ """Check if migration has already been performed."""
159
+ if not self._v3_db.exists():
160
+ return False
161
+ try:
162
+ conn = sqlite3.connect(str(self._v3_db))
163
+ try:
164
+ tables = [r[0] for r in conn.execute(
165
+ "SELECT name FROM sqlite_master WHERE type='table'"
166
+ ).fetchall()]
167
+ return "semantic_facts" in tables and "v3_config" in tables
168
+ finally:
169
+ conn.close()
170
+ except Exception:
171
+ return False
172
+
173
+ def get_v2_stats(self) -> dict:
174
+ """Get statistics about the V2 database."""
175
+ if not self.detect_v2():
176
+ return {"exists": False}
177
+ conn = None
178
+ try:
179
+ conn = sqlite3.connect(str(self._v2_db))
180
+ memory_count = conn.execute("SELECT COUNT(*) FROM memories").fetchone()[0]
181
+ tables = [r[0] for r in conn.execute(
182
+ "SELECT name FROM sqlite_master WHERE type='table'"
183
+ ).fetchall()]
184
+ # Check for profiles
185
+ profile_count = 1
186
+ try:
187
+ profiles = conn.execute(
188
+ "SELECT DISTINCT profile FROM memories WHERE profile IS NOT NULL"
189
+ ).fetchall()
190
+ profile_count = max(len(profiles), 1)
191
+ except Exception:
192
+ pass
193
+ return {
194
+ "exists": True,
195
+ "memory_count": memory_count,
196
+ "profile_count": profile_count,
197
+ "table_count": len(tables),
198
+ "db_path": str(self._v2_db),
199
+ "db_size_mb": round(self._v2_db.stat().st_size / 1024 / 1024, 2),
200
+ }
201
+ except Exception as exc:
202
+ return {"exists": True, "error": str(exc)}
203
+ finally:
204
+ if conn is not None:
205
+ conn.close()
206
+
207
+ def migrate(self) -> dict:
208
+ """Run the full V2 to V3 migration.
209
+
210
+ Steps:
211
+ 1. Create V3 directory
212
+ 2. Backup V2 database
213
+ 3. Copy database to V3 location
214
+ 4. Extend schema with V3 tables
215
+ 5. Create symlink for backward compat
216
+ 6. Mark migration complete
217
+
218
+ Returns dict with migration stats.
219
+ """
220
+ if not self.detect_v2():
221
+ return {"success": False, "error": "No V2 installation found"}
222
+
223
+ if self.is_already_migrated():
224
+ return {"success": True, "message": "Already migrated"}
225
+
226
+ stats = {"steps": []}
227
+
228
+ try:
229
+ # Step 1: Create V3 directory
230
+ self._v3_base.mkdir(parents=True, exist_ok=True)
231
+ (self._v3_base / "embeddings").mkdir(exist_ok=True)
232
+ (self._v3_base / "models").mkdir(exist_ok=True)
233
+ stats["steps"].append("Created V3 directory")
234
+
235
+ # Step 2: Backup
236
+ shutil.copy2(str(self._v2_db), str(self._backup_db))
237
+ stats["steps"].append(f"Backed up to {self._backup_db}")
238
+
239
+ # Step 3: Copy to V3 location
240
+ shutil.copy2(str(self._v2_db), str(self._v3_db))
241
+ stats["steps"].append("Copied database to V3 location")
242
+
243
+ # Step 4: Extend schema
244
+ conn = sqlite3.connect(str(self._v3_db))
245
+ for sql in V3_TABLES_SQL:
246
+ conn.execute(sql)
247
+ for sql in V3_INDEXES_SQL:
248
+ conn.execute(sql)
249
+ # Mark migration
250
+ conn.execute(
251
+ "INSERT OR REPLACE INTO v3_config (key, value, updated_at) VALUES (?, ?, ?)",
252
+ ("migration_date", datetime.now(UTC).isoformat(), datetime.now(UTC).isoformat()),
253
+ )
254
+ conn.execute(
255
+ "INSERT OR REPLACE INTO v3_config (key, value, updated_at) VALUES (?, ?, ?)",
256
+ ("migration_version", "3.0.0", datetime.now(UTC).isoformat()),
257
+ )
258
+ conn.commit()
259
+ conn.close()
260
+ stats["steps"].append(f"Extended schema ({len(V3_TABLES_SQL)} tables, {len(V3_INDEXES_SQL)} indexes)")
261
+
262
+ # Step 5: Symlink (only if .claude-memory is not already a symlink)
263
+ if not self._v2_base.is_symlink():
264
+ # Rename original to .claude-memory-v2-original
265
+ original_backup = self._home / ".claude-memory-v2-original"
266
+ if not original_backup.exists():
267
+ self._v2_base.rename(original_backup)
268
+ self._v2_base.symlink_to(self._v3_base)
269
+ stats["steps"].append("Created symlink: .claude-memory -> .superlocalmemory")
270
+ else:
271
+ stats["steps"].append("Symlink skipped (backup dir already exists)")
272
+ else:
273
+ stats["steps"].append("Symlink already exists")
274
+
275
+ stats["success"] = True
276
+ stats["v3_db"] = str(self._v3_db)
277
+ stats["backup_db"] = str(self._backup_db)
278
+
279
+ except Exception as exc:
280
+ stats["success"] = False
281
+ stats["error"] = str(exc)
282
+ logger.error("Migration failed: %s", exc)
283
+
284
+ return stats
285
+
286
+ def rollback(self) -> dict:
287
+ """Rollback migration -- restore V2 state.
288
+
289
+ Returns dict with rollback stats.
290
+ """
291
+ stats = {"steps": []}
292
+
293
+ try:
294
+ # Remove symlink
295
+ if self._v2_base.is_symlink():
296
+ self._v2_base.unlink()
297
+ stats["steps"].append("Removed symlink")
298
+
299
+ # Restore original V2 directory
300
+ original_backup = self._home / ".claude-memory-v2-original"
301
+ if original_backup.exists():
302
+ if not self._v2_base.exists():
303
+ original_backup.rename(self._v2_base)
304
+ stats["steps"].append("Restored original .claude-memory")
305
+ elif self._backup_db.exists():
306
+ # Restore from backup
307
+ self._v2_base.mkdir(parents=True, exist_ok=True)
308
+ shutil.copy2(str(self._backup_db), str(self._v2_db))
309
+ stats["steps"].append("Restored database from backup")
310
+
311
+ stats["success"] = True
312
+
313
+ except Exception as exc:
314
+ stats["success"] = False
315
+ stats["error"] = str(exc)
316
+
317
+ return stats
File without changes
@@ -0,0 +1,130 @@
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 — Trust Gate (Pre-Operation Checks).
6
+
7
+ Enforces minimum trust thresholds before allowing write/delete operations.
8
+ Read operations always pass but are logged for audit purposes.
9
+
10
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ from typing import TYPE_CHECKING
17
+
18
+ if TYPE_CHECKING:
19
+ from superlocalmemory.trust.scorer import TrustScorer
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class TrustError(PermissionError):
25
+ """Raised when an agent fails a trust check.
26
+
27
+ Attributes:
28
+ agent_id: The agent that failed the check.
29
+ trust_score: The agent's current trust score.
30
+ threshold: The minimum required trust score.
31
+ operation: The operation that was attempted.
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ agent_id: str,
37
+ trust_score: float,
38
+ threshold: float,
39
+ operation: str,
40
+ ) -> None:
41
+ self.agent_id = agent_id
42
+ self.trust_score = trust_score
43
+ self.threshold = threshold
44
+ self.operation = operation
45
+ super().__init__(
46
+ f"Agent '{agent_id}' trust {trust_score:.3f} below "
47
+ f"{operation} threshold {threshold:.3f}"
48
+ )
49
+
50
+
51
+ class TrustGate:
52
+ """Pre-operation trust checks.
53
+
54
+ Operations are gated by minimum trust thresholds:
55
+ - write: agent must have trust >= write_threshold (default 0.3)
56
+ - delete: agent must have trust >= delete_threshold (default 0.5)
57
+ - read: always passes (logged for audit trail)
58
+
59
+ Raises TrustError if the agent's trust is too low.
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ scorer: TrustScorer,
65
+ write_threshold: float = 0.3,
66
+ delete_threshold: float = 0.5,
67
+ ) -> None:
68
+ if write_threshold < 0 or write_threshold > 1:
69
+ raise ValueError("write_threshold must be in [0, 1]")
70
+ if delete_threshold < 0 or delete_threshold > 1:
71
+ raise ValueError("delete_threshold must be in [0, 1]")
72
+
73
+ self._scorer = scorer
74
+ self._write_threshold = write_threshold
75
+ self._delete_threshold = delete_threshold
76
+
77
+ @property
78
+ def write_threshold(self) -> float:
79
+ return self._write_threshold
80
+
81
+ @property
82
+ def delete_threshold(self) -> float:
83
+ return self._delete_threshold
84
+
85
+ def check_write(self, agent_id: str, profile_id: str) -> None:
86
+ """Check if agent is trusted enough to write.
87
+
88
+ Raises:
89
+ TrustError: If agent trust is below write_threshold.
90
+ """
91
+ score = self._scorer.get_agent_trust(agent_id, profile_id)
92
+ logger.debug(
93
+ "trust gate write: agent=%s trust=%.3f threshold=%.3f",
94
+ agent_id, score, self._write_threshold,
95
+ )
96
+ if score < self._write_threshold:
97
+ raise TrustError(
98
+ agent_id, score, self._write_threshold, "write"
99
+ )
100
+
101
+ def check_delete(self, agent_id: str, profile_id: str) -> None:
102
+ """Check if agent is trusted enough to delete.
103
+
104
+ Delete requires higher trust than write because it is destructive.
105
+
106
+ Raises:
107
+ TrustError: If agent trust is below delete_threshold.
108
+ """
109
+ score = self._scorer.get_agent_trust(agent_id, profile_id)
110
+ logger.debug(
111
+ "trust gate delete: agent=%s trust=%.3f threshold=%.3f",
112
+ agent_id, score, self._delete_threshold,
113
+ )
114
+ if score < self._delete_threshold:
115
+ raise TrustError(
116
+ agent_id, score, self._delete_threshold, "delete"
117
+ )
118
+
119
+ def check_read(self, agent_id: str, profile_id: str) -> None:
120
+ """Read check — always passes. Logged for audit trail.
121
+
122
+ Reads are never blocked because denying read access could break
123
+ agent functionality. However, logging read access enables
124
+ anomaly detection and compliance auditing.
125
+ """
126
+ score = self._scorer.get_agent_trust(agent_id, profile_id)
127
+ logger.debug(
128
+ "trust gate read (always pass): agent=%s trust=%.3f",
129
+ agent_id, score,
130
+ )
@@ -0,0 +1,124 @@
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 — Provenance Tracking.
6
+
7
+ Records who/what created each memory and how.
8
+ V1 had provenance as WRITE-ONLY (stored but never read).
9
+ Innovation wires reads into trust scoring and compliance audit.
10
+
11
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from datetime import UTC, datetime
18
+
19
+ from superlocalmemory.storage.models import ProvenanceRecord
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class ProvenanceTracker:
25
+ """Track provenance of all stored facts.
26
+
27
+ Every fact gets a provenance record at creation time.
28
+ Provenance feeds into:
29
+ - Trust scoring (sources with high-quality provenance get trust boost)
30
+ - Compliance audit (GDPR right-to-know: who stored what data?)
31
+ - Debugging (which extraction pipeline created this fact?)
32
+ """
33
+
34
+ def __init__(self, db) -> None:
35
+ self._db = db
36
+
37
+ def record(
38
+ self,
39
+ fact_id: str,
40
+ profile_id: str,
41
+ source_type: str,
42
+ source_id: str = "",
43
+ created_by: str = "",
44
+ ) -> ProvenanceRecord:
45
+ """Record provenance for a newly stored fact.
46
+
47
+ Args:
48
+ fact_id: The fact being tracked.
49
+ profile_id: Active profile.
50
+ source_type: "conversation", "import", "consolidation", "migration".
51
+ source_id: Session ID, import batch ID, etc.
52
+ created_by: Agent ID or user identifier.
53
+ """
54
+ record = ProvenanceRecord(
55
+ profile_id=profile_id,
56
+ fact_id=fact_id,
57
+ source_type=source_type,
58
+ source_id=source_id,
59
+ created_by=created_by,
60
+ timestamp=datetime.now(UTC).isoformat(),
61
+ )
62
+ self._db.execute(
63
+ "INSERT INTO provenance "
64
+ "(provenance_id, profile_id, fact_id, source_type, source_id, "
65
+ "created_by, timestamp) VALUES (?,?,?,?,?,?,?)",
66
+ (record.provenance_id, record.profile_id, record.fact_id,
67
+ record.source_type, record.source_id, record.created_by,
68
+ record.timestamp),
69
+ )
70
+ return record
71
+
72
+ def get_provenance(self, fact_id: str, profile_id: str) -> ProvenanceRecord | None:
73
+ """Get provenance for a specific fact."""
74
+ rows = self._db.execute(
75
+ "SELECT * FROM provenance WHERE fact_id = ? AND profile_id = ?",
76
+ (fact_id, profile_id),
77
+ )
78
+ if not rows:
79
+ return None
80
+ d = dict(rows[0])
81
+ return ProvenanceRecord(
82
+ provenance_id=d["provenance_id"],
83
+ profile_id=d["profile_id"],
84
+ fact_id=d["fact_id"],
85
+ source_type=d["source_type"],
86
+ source_id=d.get("source_id", ""),
87
+ created_by=d.get("created_by", ""),
88
+ timestamp=d["timestamp"],
89
+ )
90
+
91
+ def get_facts_by_source(
92
+ self, source_type: str, profile_id: str, limit: int = 100
93
+ ) -> list[ProvenanceRecord]:
94
+ """Get all facts from a specific source type."""
95
+ rows = self._db.execute(
96
+ "SELECT * FROM provenance WHERE source_type = ? AND profile_id = ? "
97
+ "ORDER BY timestamp DESC LIMIT ?",
98
+ (source_type, profile_id, limit),
99
+ )
100
+ return [self._row_to_record(r) for r in rows]
101
+
102
+ def get_provenance_for_profile(
103
+ self, profile_id: str, limit: int = 100
104
+ ) -> list[ProvenanceRecord]:
105
+ """Get all provenance records for a profile (compliance audit)."""
106
+ rows = self._db.execute(
107
+ "SELECT * FROM provenance WHERE profile_id = ? "
108
+ "ORDER BY timestamp DESC LIMIT ?",
109
+ (profile_id, limit),
110
+ )
111
+ return [self._row_to_record(r) for r in rows]
112
+
113
+ @staticmethod
114
+ def _row_to_record(row) -> ProvenanceRecord:
115
+ d = dict(row)
116
+ return ProvenanceRecord(
117
+ provenance_id=d["provenance_id"],
118
+ profile_id=d["profile_id"],
119
+ fact_id=d["fact_id"],
120
+ source_type=d["source_type"],
121
+ source_id=d.get("source_id", ""),
122
+ created_by=d.get("created_by", ""),
123
+ timestamp=d["timestamp"],
124
+ )