superlocalmemory 2.8.6 → 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 (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 +1 -1
  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,365 @@
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
+ """V3 API endpoints for the SuperLocalMemory dashboard."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ from pathlib import Path
12
+ from fastapi import APIRouter, Request
13
+ from fastapi.responses import JSONResponse
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ router = APIRouter(prefix="/api/v3", tags=["v3"])
18
+
19
+
20
+ # ── Dashboard ────────────────────────────────────────────────
21
+
22
+ @router.get("/dashboard")
23
+ async def dashboard(request: Request):
24
+ """Dashboard summary: mode, memory count, health score, recent activity."""
25
+ try:
26
+ from superlocalmemory.core.config import SLMConfig
27
+ config = SLMConfig.load()
28
+
29
+ # Get basic stats from engine if available
30
+ engine = getattr(request.app.state, "engine", None)
31
+ memory_count = 0
32
+ fact_count = 0
33
+ if engine and engine._db:
34
+ try:
35
+ rows = engine._db.execute("SELECT COUNT(*) FROM atomic_facts")
36
+ if rows:
37
+ fact_count = rows[0][0] if isinstance(rows[0], (list, tuple)) else dict(rows[0]).get("COUNT(*)", 0)
38
+ except Exception:
39
+ pass
40
+ try:
41
+ rows = engine._db.execute("SELECT COUNT(*) FROM memories")
42
+ if rows:
43
+ memory_count = rows[0][0] if isinstance(rows[0], (list, tuple)) else dict(rows[0]).get("COUNT(*)", 0)
44
+ except Exception:
45
+ pass
46
+
47
+ return {
48
+ "mode": config.mode.value,
49
+ "mode_name": {"a": "Local Guardian", "b": "Smart Local", "c": "Full Power"}.get(config.mode.value, "Unknown"),
50
+ "provider": config.llm.provider or "none",
51
+ "model": config.llm.model or "",
52
+ "memory_count": memory_count,
53
+ "fact_count": fact_count,
54
+ "profile": config.active_profile,
55
+ "base_dir": str(config.base_dir),
56
+ "version": "3.0.0",
57
+ }
58
+ except Exception as e:
59
+ return JSONResponse({"error": str(e)}, status_code=500)
60
+
61
+
62
+ # ── Mode ─────────────────────────────────────────────────────
63
+
64
+ @router.get("/mode")
65
+ async def get_mode():
66
+ """Get current operating mode."""
67
+ try:
68
+ from superlocalmemory.core.config import SLMConfig
69
+ config = SLMConfig.load()
70
+ modes = {
71
+ "a": {"name": "Local Guardian", "description": "Zero cloud. Your data never leaves your machine.", "llm": False, "eu_compliant": True},
72
+ "b": {"name": "Smart Local", "description": "Local LLM via Ollama. Still fully private.", "llm": "local", "eu_compliant": True},
73
+ "c": {"name": "Full Power", "description": "Cloud LLM for maximum accuracy.", "llm": "cloud", "eu_compliant": False},
74
+ }
75
+ current = config.mode.value
76
+ return {"current": current, "details": modes.get(current, {}), "all_modes": modes}
77
+ except Exception as e:
78
+ return JSONResponse({"error": str(e)}, status_code=500)
79
+
80
+
81
+ @router.put("/mode")
82
+ async def set_mode(request: Request):
83
+ """Switch operating mode. Body: {"mode": "a"|"b"|"c"}"""
84
+ try:
85
+ body = await request.json()
86
+ new_mode = body.get("mode", "").lower()
87
+ if new_mode not in ("a", "b", "c"):
88
+ return JSONResponse({"error": "Invalid mode. Use a, b, or c."}, status_code=400)
89
+
90
+ from superlocalmemory.core.config import SLMConfig
91
+ from superlocalmemory.storage.models import Mode
92
+ old_config = SLMConfig.load()
93
+ new_config = SLMConfig.for_mode(
94
+ Mode(new_mode),
95
+ llm_provider=old_config.llm.provider,
96
+ llm_model=old_config.llm.model,
97
+ llm_api_key=old_config.llm.api_key,
98
+ llm_api_base=old_config.llm.api_base,
99
+ )
100
+ new_config.active_profile = old_config.active_profile
101
+ new_config.save()
102
+
103
+ # Reset engine to pick up new config
104
+ if hasattr(request.app.state, "engine"):
105
+ request.app.state.engine = None
106
+
107
+ return {"success": True, "mode": new_mode}
108
+ except Exception as e:
109
+ return JSONResponse({"error": str(e)}, status_code=500)
110
+
111
+
112
+ # ── Provider ─────────────────────────────────────────────────
113
+
114
+ @router.get("/providers")
115
+ async def list_providers():
116
+ """List available LLM providers with presets."""
117
+ try:
118
+ from superlocalmemory.core.config import SLMConfig
119
+ return {"providers": SLMConfig.provider_presets()}
120
+ except Exception as exc:
121
+ return {"error": str(exc), "providers": []}
122
+
123
+
124
+ @router.get("/provider")
125
+ async def get_provider():
126
+ """Get current provider configuration (API key masked)."""
127
+ try:
128
+ from superlocalmemory.core.config import SLMConfig
129
+ config = SLMConfig.load()
130
+ key = config.llm.api_key
131
+ masked = f"****{key[-4:]}" if len(key) > 8 else "****" if key else ""
132
+ return {
133
+ "provider": config.llm.provider or "none",
134
+ "model": config.llm.model,
135
+ "base_url": config.llm.api_base,
136
+ "api_key_masked": masked,
137
+ "has_key": bool(key),
138
+ }
139
+ except Exception as exc:
140
+ return {"error": str(exc), "provider": "unknown"}
141
+
142
+
143
+ @router.put("/provider")
144
+ async def set_provider(request: Request):
145
+ """Set LLM provider. Body: {"provider": "openai", "api_key": "...", "model": "..."}"""
146
+ try:
147
+ body = await request.json()
148
+ provider = body.get("provider", "")
149
+ api_key = body.get("api_key", "")
150
+ model = body.get("model", "")
151
+ base_url = body.get("base_url", "")
152
+
153
+ from superlocalmemory.core.config import SLMConfig
154
+ from superlocalmemory.storage.models import Mode
155
+ config = SLMConfig.load()
156
+
157
+ # Use preset base_url if not provided
158
+ if not base_url:
159
+ presets = SLMConfig.provider_presets()
160
+ preset = presets.get(provider, {})
161
+ base_url = preset.get("base_url", "")
162
+ if not model:
163
+ model = preset.get("model", "")
164
+
165
+ new_config = SLMConfig.for_mode(
166
+ config.mode,
167
+ llm_provider=provider,
168
+ llm_model=model,
169
+ llm_api_key=api_key,
170
+ llm_api_base=base_url,
171
+ )
172
+ new_config.active_profile = config.active_profile
173
+ new_config.save()
174
+
175
+ return {"success": True, "provider": provider, "model": model}
176
+ except Exception as e:
177
+ return JSONResponse({"error": str(e)}, status_code=500)
178
+
179
+
180
+ # ── Recall Trace ─────────────────────────────────────────────
181
+
182
+ @router.post("/recall/trace")
183
+ async def recall_trace(request: Request):
184
+ """Recall with per-channel score breakdown."""
185
+ try:
186
+ body = await request.json()
187
+ query = body.get("query", "")
188
+ limit = body.get("limit", 10)
189
+
190
+ engine = getattr(request.app.state, "engine", None)
191
+ if not engine:
192
+ return JSONResponse({"error": "Engine not initialized"}, status_code=503)
193
+
194
+ response = engine.recall(query, limit=limit)
195
+ results = []
196
+ for r in response.results[:limit]:
197
+ results.append({
198
+ "fact_id": r.fact.fact_id,
199
+ "content": r.fact.content[:300],
200
+ "score": round(r.score, 4),
201
+ "confidence": round(r.confidence, 4),
202
+ "trust_score": round(r.trust_score, 4),
203
+ "channel_scores": {k: round(v, 4) for k, v in (r.channel_scores or {}).items()},
204
+ })
205
+
206
+ return {
207
+ "query": query,
208
+ "query_type": response.query_type,
209
+ "result_count": len(results),
210
+ "retrieval_time_ms": round(response.retrieval_time_ms, 1),
211
+ "results": results,
212
+ }
213
+ except Exception as e:
214
+ return JSONResponse({"error": str(e)}, status_code=500)
215
+
216
+
217
+ # ── Trust Dashboard ──────────────────────────────────────────
218
+
219
+ @router.get("/trust/dashboard")
220
+ async def trust_dashboard(request: Request):
221
+ """Trust overview: per-agent scores, alerts."""
222
+ try:
223
+ engine = getattr(request.app.state, "engine", None)
224
+ if not engine or not engine._trust_scorer:
225
+ return {"agents": [], "alerts": [], "message": "Trust scorer not available"}
226
+
227
+ from superlocalmemory.core.config import SLMConfig
228
+ config = SLMConfig.load()
229
+ scores = engine._trust_scorer.get_all_scores(config.active_profile)
230
+
231
+ agents = []
232
+ for s in scores:
233
+ if isinstance(s, dict):
234
+ agents.append(s)
235
+ else:
236
+ agents.append({
237
+ "target_id": s.target_id,
238
+ "target_type": s.target_type,
239
+ "trust_score": round(s.trust_score, 3),
240
+ "evidence_count": s.evidence_count,
241
+ })
242
+
243
+ return {"agents": agents, "alerts": [], "profile": config.active_profile}
244
+ except Exception as e:
245
+ return JSONResponse({"error": str(e)}, status_code=500)
246
+
247
+
248
+ # ── Math Health ──────────────────────────────────────────────
249
+
250
+ @router.get("/math/health")
251
+ async def math_health(request: Request):
252
+ """Mathematical layer health: Fisher, sheaf, Langevin status."""
253
+ try:
254
+ engine = getattr(request.app.state, "engine", None)
255
+
256
+ health = {
257
+ "fisher": {"status": "active", "description": "Fisher-Rao information geometry for similarity"},
258
+ "sheaf": {"status": "active", "description": "Sheaf cohomology for consistency detection"},
259
+ "langevin": {"status": "active", "description": "Riemannian Langevin dynamics for lifecycle"},
260
+ }
261
+
262
+ # Check if math layers are configured
263
+ if engine:
264
+ from superlocalmemory.core.config import SLMConfig
265
+ config = SLMConfig.load()
266
+ health["fisher"]["mode"] = config.math.fisher_mode
267
+ health["sheaf"]["threshold"] = config.math.sheaf_contradiction_threshold
268
+ health["langevin"]["temperature"] = config.math.langevin_temperature
269
+
270
+ return {"health": health, "overall": "healthy"}
271
+ except Exception as e:
272
+ return JSONResponse({"error": str(e)}, status_code=500)
273
+
274
+
275
+ # ── Auto-Capture / Auto-Recall Config ────────────────────────
276
+
277
+ @router.get("/auto-capture/config")
278
+ async def get_auto_capture_config():
279
+ """Get auto-capture configuration."""
280
+ try:
281
+ from superlocalmemory.hooks.rules_engine import RulesEngine
282
+ from superlocalmemory.core.config import DEFAULT_BASE_DIR
283
+ rules = RulesEngine(config_path=DEFAULT_BASE_DIR / "config.json")
284
+ return {"config": rules.get_capture_config()}
285
+ except Exception as exc:
286
+ return {"error": str(exc), "config": {}}
287
+
288
+
289
+ @router.put("/auto-capture/config")
290
+ async def set_auto_capture_config(request: Request):
291
+ """Update auto-capture config. Body: {"enabled": true, "capture_decisions": true, ...}"""
292
+ try:
293
+ body = await request.json()
294
+ from superlocalmemory.hooks.rules_engine import RulesEngine
295
+ from superlocalmemory.core.config import DEFAULT_BASE_DIR
296
+ config_path = DEFAULT_BASE_DIR / "config.json"
297
+ rules = RulesEngine(config_path=config_path)
298
+ for key, value in body.items():
299
+ rules.update_rule("auto_capture", key, value)
300
+ rules.save(config_path)
301
+ return {"success": True, "config": rules.get_capture_config()}
302
+ except Exception as e:
303
+ return JSONResponse({"error": str(e)}, status_code=500)
304
+
305
+
306
+ @router.get("/auto-recall/config")
307
+ async def get_auto_recall_config():
308
+ """Get auto-recall configuration."""
309
+ try:
310
+ from superlocalmemory.hooks.rules_engine import RulesEngine
311
+ from superlocalmemory.core.config import DEFAULT_BASE_DIR
312
+ rules = RulesEngine(config_path=DEFAULT_BASE_DIR / "config.json")
313
+ return {"config": rules.get_recall_config()}
314
+ except Exception as exc:
315
+ return {"error": str(exc), "config": {}}
316
+
317
+
318
+ @router.put("/auto-recall/config")
319
+ async def set_auto_recall_config(request: Request):
320
+ """Update auto-recall config."""
321
+ try:
322
+ body = await request.json()
323
+ from superlocalmemory.hooks.rules_engine import RulesEngine
324
+ from superlocalmemory.core.config import DEFAULT_BASE_DIR
325
+ config_path = DEFAULT_BASE_DIR / "config.json"
326
+ rules = RulesEngine(config_path=config_path)
327
+ for key, value in body.items():
328
+ rules.update_rule("auto_recall", key, value)
329
+ rules.save(config_path)
330
+ return {"success": True, "config": rules.get_recall_config()}
331
+ except Exception as e:
332
+ return JSONResponse({"error": str(e)}, status_code=500)
333
+
334
+
335
+ # ── IDE Status ───────────────────────────────────────────────
336
+
337
+ @router.get("/ide/status")
338
+ async def ide_status():
339
+ """Get IDE connection status."""
340
+ try:
341
+ from superlocalmemory.hooks.ide_connector import IDEConnector
342
+ connector = IDEConnector()
343
+ return {"ides": connector.get_status()}
344
+ except Exception as exc:
345
+ return {"error": str(exc), "ides": []}
346
+
347
+
348
+ @router.post("/ide/connect")
349
+ async def ide_connect(request: Request):
350
+ """Connect an IDE. Body: {"ide": "cursor"} or {} for all."""
351
+ try:
352
+ body = await request.json()
353
+ ide = body.get("ide", "")
354
+
355
+ from superlocalmemory.hooks.ide_connector import IDEConnector
356
+ connector = IDEConnector()
357
+
358
+ if ide:
359
+ success = connector.connect(ide)
360
+ return {"success": success, "ide": ide}
361
+ else:
362
+ results = connector.connect_all()
363
+ return {"results": results}
364
+ except Exception as e:
365
+ return JSONResponse({"error": str(e)}, status_code=500)
@@ -0,0 +1,82 @@
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
+ """SuperLocalMemory V3 - WebSocket Routes
5
+ - MIT License
6
+
7
+ Routes: /ws/updates
8
+ """
9
+ from typing import Set
10
+ from datetime import datetime
11
+
12
+ from fastapi import APIRouter, WebSocket, WebSocketDisconnect
13
+
14
+ router = APIRouter()
15
+
16
+
17
+ class ConnectionManager:
18
+ """Manages WebSocket connections for real-time updates."""
19
+
20
+ def __init__(self):
21
+ self.active_connections: Set[WebSocket] = set()
22
+
23
+ async def connect(self, websocket: WebSocket):
24
+ await websocket.accept()
25
+ self.active_connections.add(websocket)
26
+
27
+ def disconnect(self, websocket: WebSocket):
28
+ self.active_connections.discard(websocket)
29
+
30
+ async def broadcast(self, message: dict):
31
+ disconnected = set()
32
+ for connection in self.active_connections:
33
+ try:
34
+ await connection.send_json(message)
35
+ except Exception:
36
+ disconnected.add(connection)
37
+ self.active_connections -= disconnected
38
+
39
+
40
+ manager = ConnectionManager()
41
+
42
+
43
+ @router.websocket("/ws/updates")
44
+ async def websocket_updates(websocket: WebSocket):
45
+ """WebSocket endpoint for real-time memory updates."""
46
+ await manager.connect(websocket)
47
+
48
+ try:
49
+ await websocket.send_json({
50
+ "type": "connected",
51
+ "message": "WebSocket connection established",
52
+ "timestamp": datetime.now().isoformat(),
53
+ })
54
+
55
+ while True:
56
+ try:
57
+ data = await websocket.receive_json()
58
+
59
+ if data.get('type') == 'ping':
60
+ await websocket.send_json({
61
+ "type": "pong",
62
+ "timestamp": datetime.now().isoformat(),
63
+ })
64
+
65
+ elif data.get('type') == 'get_stats':
66
+ await websocket.send_json({
67
+ "type": "stats_update",
68
+ "message": "Use /api/stats endpoint for stats",
69
+ "timestamp": datetime.now().isoformat(),
70
+ })
71
+
72
+ except WebSocketDisconnect:
73
+ break
74
+ except Exception as e:
75
+ await websocket.send_json({
76
+ "type": "error",
77
+ "message": str(e),
78
+ "timestamp": datetime.now().isoformat(),
79
+ })
80
+
81
+ finally:
82
+ manager.disconnect(websocket)
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
4
+ """Security headers middleware for FastAPI servers.
5
+
6
+ Adds comprehensive security headers to all HTTP responses:
7
+ - X-Content-Type-Options: Prevents MIME type sniffing
8
+ - X-Frame-Options: Prevents clickjacking attacks
9
+ - X-XSS-Protection: Enables browser XSS filters
10
+ - Content-Security-Policy: Restricts resource loading
11
+ - Referrer-Policy: Controls referrer information leakage
12
+ """
13
+
14
+ from starlette.middleware.base import BaseHTTPMiddleware
15
+ from starlette.requests import Request
16
+ from starlette.responses import Response
17
+
18
+
19
+ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
20
+ """Add security headers to all HTTP responses."""
21
+
22
+ async def dispatch(self, request: Request, call_next) -> Response:
23
+ """Process request and add security headers to response."""
24
+ response = await call_next(request)
25
+
26
+ # Prevent MIME type sniffing
27
+ response.headers["X-Content-Type-Options"] = "nosniff"
28
+
29
+ # Prevent clickjacking attacks
30
+ response.headers["X-Frame-Options"] = "DENY"
31
+
32
+ # Enable browser XSS filter (legacy, but doesn't hurt)
33
+ response.headers["X-XSS-Protection"] = "1; mode=block"
34
+
35
+ # Content Security Policy
36
+ # Note: 'unsafe-inline' is needed for Bootstrap and inline scripts
37
+ # For production, consider moving inline scripts to separate files
38
+ csp_directives = [
39
+ "default-src 'self'",
40
+ "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://unpkg.com https://d3js.org",
41
+ "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://unpkg.com",
42
+ "font-src 'self' https://cdn.jsdelivr.net",
43
+ "img-src 'self' data: https:",
44
+ "connect-src 'self' ws://localhost:* ws://127.0.0.1:*",
45
+ "frame-ancestors 'none'",
46
+ ]
47
+ response.headers["Content-Security-Policy"] = "; ".join(csp_directives)
48
+
49
+ # Control referrer information leakage
50
+ response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
51
+
52
+ # Prevent caching of sensitive data (for API endpoints)
53
+ if request.url.path.startswith("/api/"):
54
+ response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate"
55
+ response.headers["Pragma"] = "no-cache"
56
+
57
+ return response