superlocalmemory 2.8.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +9 -1
  3. package/NOTICE +63 -0
  4. package/README.md +165 -480
  5. package/bin/slm +17 -449
  6. package/bin/slm-npm +2 -2
  7. package/bin/slm.bat +4 -2
  8. package/conftest.py +5 -0
  9. package/docs/api-reference.md +284 -0
  10. package/docs/architecture.md +149 -0
  11. package/docs/auto-memory.md +150 -0
  12. package/docs/cli-reference.md +276 -0
  13. package/docs/compliance.md +191 -0
  14. package/docs/configuration.md +182 -0
  15. package/docs/getting-started.md +102 -0
  16. package/docs/ide-setup.md +261 -0
  17. package/docs/mcp-tools.md +220 -0
  18. package/docs/migration-from-v2.md +170 -0
  19. package/docs/profiles.md +173 -0
  20. package/docs/troubleshooting.md +310 -0
  21. package/{configs → ide/configs}/antigravity-mcp.json +3 -3
  22. package/ide/configs/chatgpt-desktop-mcp.json +16 -0
  23. package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
  24. package/{configs → ide/configs}/codex-mcp.toml +4 -4
  25. package/{configs → ide/configs}/continue-mcp.yaml +4 -3
  26. package/{configs → ide/configs}/continue-skills.yaml +6 -6
  27. package/ide/configs/cursor-mcp.json +15 -0
  28. package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
  29. package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
  30. package/{configs → ide/configs}/opencode-mcp.json +2 -2
  31. package/{configs → ide/configs}/perplexity-mcp.json +2 -2
  32. package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
  33. package/{configs → ide/configs}/windsurf-mcp.json +3 -3
  34. package/{configs → ide/configs}/zed-mcp.json +2 -2
  35. package/{hooks → ide/hooks}/context-hook.js +9 -20
  36. package/ide/hooks/memory-list-skill.js +70 -0
  37. package/ide/hooks/memory-profile-skill.js +101 -0
  38. package/ide/hooks/memory-recall-skill.js +62 -0
  39. package/ide/hooks/memory-remember-skill.js +68 -0
  40. package/ide/hooks/memory-reset-skill.js +160 -0
  41. package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
  42. package/ide/integrations/langchain/README.md +106 -0
  43. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
  44. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
  45. package/ide/integrations/langchain/pyproject.toml +38 -0
  46. package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
  47. package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
  48. package/ide/integrations/langchain/tests/test_security.py +117 -0
  49. package/ide/integrations/llamaindex/README.md +81 -0
  50. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
  51. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
  52. package/ide/integrations/llamaindex/pyproject.toml +43 -0
  53. package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
  54. package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
  55. package/ide/integrations/llamaindex/tests/test_security.py +241 -0
  56. package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
  57. package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
  58. package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
  59. package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
  60. package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
  61. package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
  62. package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
  63. package/package.json +13 -22
  64. package/pyproject.toml +85 -0
  65. package/scripts/build-dmg.sh +417 -0
  66. package/scripts/install-skills.ps1 +334 -0
  67. package/{install.ps1 → scripts/install.ps1} +36 -4
  68. package/{install.sh → scripts/install.sh} +14 -13
  69. package/scripts/postinstall.js +2 -2
  70. package/scripts/start-dashboard.ps1 +52 -0
  71. package/scripts/start-dashboard.sh +41 -0
  72. package/scripts/sync-wiki.ps1 +127 -0
  73. package/scripts/sync-wiki.sh +82 -0
  74. package/scripts/test-dmg.sh +161 -0
  75. package/scripts/test-npm-package.ps1 +252 -0
  76. package/scripts/test-npm-package.sh +207 -0
  77. package/scripts/verify-install.ps1 +294 -0
  78. package/scripts/verify-install.sh +266 -0
  79. package/src/superlocalmemory/__init__.py +0 -0
  80. package/src/superlocalmemory/attribution/__init__.py +9 -0
  81. package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
  82. package/src/superlocalmemory/attribution/signer.py +153 -0
  83. package/src/superlocalmemory/attribution/watermark.py +189 -0
  84. package/src/superlocalmemory/cli/__init__.py +5 -0
  85. package/src/superlocalmemory/cli/commands.py +245 -0
  86. package/src/superlocalmemory/cli/main.py +89 -0
  87. package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
  88. package/src/superlocalmemory/cli/post_install.py +99 -0
  89. package/src/superlocalmemory/cli/setup_wizard.py +129 -0
  90. package/src/superlocalmemory/compliance/__init__.py +0 -0
  91. package/src/superlocalmemory/compliance/abac.py +204 -0
  92. package/src/superlocalmemory/compliance/audit.py +314 -0
  93. package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
  94. package/src/superlocalmemory/compliance/gdpr.py +294 -0
  95. package/src/superlocalmemory/compliance/lifecycle.py +158 -0
  96. package/src/superlocalmemory/compliance/retention.py +232 -0
  97. package/src/superlocalmemory/compliance/scheduler.py +148 -0
  98. package/src/superlocalmemory/core/__init__.py +0 -0
  99. package/src/superlocalmemory/core/config.py +391 -0
  100. package/src/superlocalmemory/core/embeddings.py +293 -0
  101. package/src/superlocalmemory/core/engine.py +701 -0
  102. package/src/superlocalmemory/core/hooks.py +65 -0
  103. package/src/superlocalmemory/core/maintenance.py +172 -0
  104. package/src/superlocalmemory/core/modes.py +140 -0
  105. package/src/superlocalmemory/core/profiles.py +234 -0
  106. package/src/superlocalmemory/core/registry.py +117 -0
  107. package/src/superlocalmemory/dynamics/__init__.py +0 -0
  108. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
  109. package/src/superlocalmemory/encoding/__init__.py +0 -0
  110. package/src/superlocalmemory/encoding/consolidator.py +485 -0
  111. package/src/superlocalmemory/encoding/emotional.py +125 -0
  112. package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
  113. package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
  114. package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
  115. package/src/superlocalmemory/encoding/foresight.py +91 -0
  116. package/src/superlocalmemory/encoding/graph_builder.py +302 -0
  117. package/src/superlocalmemory/encoding/observation_builder.py +160 -0
  118. package/src/superlocalmemory/encoding/scene_builder.py +183 -0
  119. package/src/superlocalmemory/encoding/signal_inference.py +90 -0
  120. package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
  121. package/src/superlocalmemory/encoding/type_router.py +235 -0
  122. package/src/superlocalmemory/hooks/__init__.py +3 -0
  123. package/src/superlocalmemory/hooks/auto_capture.py +111 -0
  124. package/src/superlocalmemory/hooks/auto_recall.py +93 -0
  125. package/src/superlocalmemory/hooks/ide_connector.py +204 -0
  126. package/src/superlocalmemory/hooks/rules_engine.py +99 -0
  127. package/src/superlocalmemory/infra/__init__.py +3 -0
  128. package/src/superlocalmemory/infra/auth_middleware.py +82 -0
  129. package/src/superlocalmemory/infra/backup.py +317 -0
  130. package/src/superlocalmemory/infra/cache_manager.py +267 -0
  131. package/src/superlocalmemory/infra/event_bus.py +381 -0
  132. package/src/superlocalmemory/infra/rate_limiter.py +135 -0
  133. package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
  134. package/src/superlocalmemory/learning/__init__.py +0 -0
  135. package/src/superlocalmemory/learning/adaptive.py +172 -0
  136. package/src/superlocalmemory/learning/behavioral.py +490 -0
  137. package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
  138. package/src/superlocalmemory/learning/bootstrap.py +298 -0
  139. package/src/superlocalmemory/learning/cross_project.py +399 -0
  140. package/src/superlocalmemory/learning/database.py +376 -0
  141. package/src/superlocalmemory/learning/engagement.py +323 -0
  142. package/src/superlocalmemory/learning/features.py +138 -0
  143. package/src/superlocalmemory/learning/feedback.py +316 -0
  144. package/src/superlocalmemory/learning/outcomes.py +255 -0
  145. package/src/superlocalmemory/learning/project_context.py +366 -0
  146. package/src/superlocalmemory/learning/ranker.py +155 -0
  147. package/src/superlocalmemory/learning/source_quality.py +303 -0
  148. package/src/superlocalmemory/learning/workflows.py +309 -0
  149. package/src/superlocalmemory/llm/__init__.py +0 -0
  150. package/src/superlocalmemory/llm/backbone.py +316 -0
  151. package/src/superlocalmemory/math/__init__.py +0 -0
  152. package/src/superlocalmemory/math/fisher.py +356 -0
  153. package/src/superlocalmemory/math/langevin.py +398 -0
  154. package/src/superlocalmemory/math/sheaf.py +257 -0
  155. package/src/superlocalmemory/mcp/__init__.py +0 -0
  156. package/src/superlocalmemory/mcp/resources.py +245 -0
  157. package/src/superlocalmemory/mcp/server.py +61 -0
  158. package/src/superlocalmemory/mcp/tools.py +18 -0
  159. package/src/superlocalmemory/mcp/tools_core.py +305 -0
  160. package/src/superlocalmemory/mcp/tools_v28.py +223 -0
  161. package/src/superlocalmemory/mcp/tools_v3.py +286 -0
  162. package/src/superlocalmemory/retrieval/__init__.py +0 -0
  163. package/src/superlocalmemory/retrieval/agentic.py +295 -0
  164. package/src/superlocalmemory/retrieval/ann_index.py +223 -0
  165. package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
  166. package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
  167. package/src/superlocalmemory/retrieval/engine.py +390 -0
  168. package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
  169. package/src/superlocalmemory/retrieval/fusion.py +78 -0
  170. package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
  171. package/src/superlocalmemory/retrieval/reranker.py +154 -0
  172. package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
  173. package/src/superlocalmemory/retrieval/strategy.py +96 -0
  174. package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
  175. package/src/superlocalmemory/server/__init__.py +1 -0
  176. package/src/superlocalmemory/server/api.py +248 -0
  177. package/src/superlocalmemory/server/routes/__init__.py +4 -0
  178. package/src/superlocalmemory/server/routes/agents.py +107 -0
  179. package/src/superlocalmemory/server/routes/backup.py +91 -0
  180. package/src/superlocalmemory/server/routes/behavioral.py +127 -0
  181. package/src/superlocalmemory/server/routes/compliance.py +160 -0
  182. package/src/superlocalmemory/server/routes/data_io.py +188 -0
  183. package/src/superlocalmemory/server/routes/events.py +183 -0
  184. package/src/superlocalmemory/server/routes/helpers.py +85 -0
  185. package/src/superlocalmemory/server/routes/learning.py +273 -0
  186. package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
  187. package/src/superlocalmemory/server/routes/memories.py +399 -0
  188. package/src/superlocalmemory/server/routes/profiles.py +219 -0
  189. package/src/superlocalmemory/server/routes/stats.py +346 -0
  190. package/src/superlocalmemory/server/routes/v3_api.py +365 -0
  191. package/src/superlocalmemory/server/routes/ws.py +82 -0
  192. package/src/superlocalmemory/server/security_middleware.py +57 -0
  193. package/src/superlocalmemory/server/ui.py +245 -0
  194. package/src/superlocalmemory/storage/__init__.py +0 -0
  195. package/src/superlocalmemory/storage/access_control.py +182 -0
  196. package/src/superlocalmemory/storage/database.py +594 -0
  197. package/src/superlocalmemory/storage/migrations.py +303 -0
  198. package/src/superlocalmemory/storage/models.py +406 -0
  199. package/src/superlocalmemory/storage/schema.py +726 -0
  200. package/src/superlocalmemory/storage/v2_migrator.py +317 -0
  201. package/src/superlocalmemory/trust/__init__.py +0 -0
  202. package/src/superlocalmemory/trust/gate.py +130 -0
  203. package/src/superlocalmemory/trust/provenance.py +124 -0
  204. package/src/superlocalmemory/trust/scorer.py +347 -0
  205. package/src/superlocalmemory/trust/signals.py +153 -0
  206. package/ui/index.html +278 -5
  207. package/ui/js/auto-settings.js +70 -0
  208. package/ui/js/dashboard.js +90 -0
  209. package/ui/js/fact-detail.js +92 -0
  210. package/ui/js/feedback.js +2 -2
  211. package/ui/js/ide-status.js +102 -0
  212. package/ui/js/math-health.js +98 -0
  213. package/ui/js/recall-lab.js +127 -0
  214. package/ui/js/settings.js +2 -2
  215. package/ui/js/trust-dashboard.js +73 -0
  216. package/api_server.py +0 -724
  217. package/bin/aider-smart +0 -72
  218. package/bin/superlocalmemoryv2-learning +0 -4
  219. package/bin/superlocalmemoryv2-list +0 -3
  220. package/bin/superlocalmemoryv2-patterns +0 -4
  221. package/bin/superlocalmemoryv2-profile +0 -3
  222. package/bin/superlocalmemoryv2-recall +0 -3
  223. package/bin/superlocalmemoryv2-remember +0 -3
  224. package/bin/superlocalmemoryv2-reset +0 -3
  225. package/bin/superlocalmemoryv2-status +0 -3
  226. package/configs/chatgpt-desktop-mcp.json +0 -16
  227. package/configs/cursor-mcp.json +0 -15
  228. package/docs/SECURITY-QUICK-REFERENCE.md +0 -214
  229. package/hooks/memory-list-skill.js +0 -139
  230. package/hooks/memory-profile-skill.js +0 -273
  231. package/hooks/memory-recall-skill.js +0 -114
  232. package/hooks/memory-remember-skill.js +0 -127
  233. package/hooks/memory-reset-skill.js +0 -274
  234. package/mcp_server.py +0 -1800
  235. package/requirements-core.txt +0 -22
  236. package/requirements-learning.txt +0 -12
  237. package/requirements.txt +0 -12
  238. package/src/agent_registry.py +0 -411
  239. package/src/auth_middleware.py +0 -61
  240. package/src/auto_backup.py +0 -459
  241. package/src/behavioral/__init__.py +0 -49
  242. package/src/behavioral/behavioral_listener.py +0 -203
  243. package/src/behavioral/behavioral_patterns.py +0 -275
  244. package/src/behavioral/cross_project_transfer.py +0 -206
  245. package/src/behavioral/outcome_inference.py +0 -194
  246. package/src/behavioral/outcome_tracker.py +0 -193
  247. package/src/behavioral/tests/__init__.py +0 -4
  248. package/src/behavioral/tests/test_behavioral_integration.py +0 -108
  249. package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
  250. package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
  251. package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
  252. package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
  253. package/src/behavioral/tests/test_outcome_inference.py +0 -107
  254. package/src/behavioral/tests/test_outcome_tracker.py +0 -96
  255. package/src/cache_manager.py +0 -518
  256. package/src/compliance/__init__.py +0 -48
  257. package/src/compliance/abac_engine.py +0 -149
  258. package/src/compliance/abac_middleware.py +0 -116
  259. package/src/compliance/audit_db.py +0 -215
  260. package/src/compliance/audit_logger.py +0 -148
  261. package/src/compliance/retention_manager.py +0 -289
  262. package/src/compliance/retention_scheduler.py +0 -186
  263. package/src/compliance/tests/__init__.py +0 -4
  264. package/src/compliance/tests/test_abac_enforcement.py +0 -95
  265. package/src/compliance/tests/test_abac_engine.py +0 -124
  266. package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
  267. package/src/compliance/tests/test_audit_db.py +0 -123
  268. package/src/compliance/tests/test_audit_logger.py +0 -98
  269. package/src/compliance/tests/test_mcp_audit.py +0 -128
  270. package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
  271. package/src/compliance/tests/test_retention_manager.py +0 -131
  272. package/src/compliance/tests/test_retention_scheduler.py +0 -99
  273. package/src/compression/__init__.py +0 -25
  274. package/src/compression/cli.py +0 -150
  275. package/src/compression/cold_storage.py +0 -217
  276. package/src/compression/config.py +0 -72
  277. package/src/compression/orchestrator.py +0 -133
  278. package/src/compression/tier2_compressor.py +0 -228
  279. package/src/compression/tier3_compressor.py +0 -153
  280. package/src/compression/tier_classifier.py +0 -148
  281. package/src/db_connection_manager.py +0 -536
  282. package/src/embedding_engine.py +0 -63
  283. package/src/embeddings/__init__.py +0 -47
  284. package/src/embeddings/cache.py +0 -70
  285. package/src/embeddings/cli.py +0 -113
  286. package/src/embeddings/constants.py +0 -47
  287. package/src/embeddings/database.py +0 -91
  288. package/src/embeddings/engine.py +0 -247
  289. package/src/embeddings/model_loader.py +0 -145
  290. package/src/event_bus.py +0 -562
  291. package/src/graph/__init__.py +0 -36
  292. package/src/graph/build_helpers.py +0 -74
  293. package/src/graph/cli.py +0 -87
  294. package/src/graph/cluster_builder.py +0 -188
  295. package/src/graph/cluster_summary.py +0 -148
  296. package/src/graph/constants.py +0 -47
  297. package/src/graph/edge_builder.py +0 -162
  298. package/src/graph/entity_extractor.py +0 -95
  299. package/src/graph/graph_core.py +0 -226
  300. package/src/graph/graph_search.py +0 -231
  301. package/src/graph/hierarchical.py +0 -207
  302. package/src/graph/schema.py +0 -99
  303. package/src/graph_engine.py +0 -52
  304. package/src/hnsw_index.py +0 -628
  305. package/src/hybrid_search.py +0 -46
  306. package/src/learning/__init__.py +0 -217
  307. package/src/learning/adaptive_ranker.py +0 -682
  308. package/src/learning/bootstrap/__init__.py +0 -69
  309. package/src/learning/bootstrap/constants.py +0 -93
  310. package/src/learning/bootstrap/db_queries.py +0 -316
  311. package/src/learning/bootstrap/sampling.py +0 -82
  312. package/src/learning/bootstrap/text_utils.py +0 -71
  313. package/src/learning/cross_project_aggregator.py +0 -857
  314. package/src/learning/db/__init__.py +0 -40
  315. package/src/learning/db/constants.py +0 -44
  316. package/src/learning/db/schema.py +0 -279
  317. package/src/learning/engagement_tracker.py +0 -628
  318. package/src/learning/feature_extractor.py +0 -708
  319. package/src/learning/feedback_collector.py +0 -806
  320. package/src/learning/learning_db.py +0 -915
  321. package/src/learning/project_context_manager.py +0 -572
  322. package/src/learning/ranking/__init__.py +0 -33
  323. package/src/learning/ranking/constants.py +0 -84
  324. package/src/learning/ranking/helpers.py +0 -278
  325. package/src/learning/source_quality_scorer.py +0 -676
  326. package/src/learning/synthetic_bootstrap.py +0 -755
  327. package/src/learning/tests/test_adaptive_ranker.py +0 -325
  328. package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
  329. package/src/learning/tests/test_aggregator.py +0 -306
  330. package/src/learning/tests/test_auto_retrain_v28.py +0 -35
  331. package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
  332. package/src/learning/tests/test_feature_extractor_v28.py +0 -93
  333. package/src/learning/tests/test_feedback_collector.py +0 -294
  334. package/src/learning/tests/test_learning_db.py +0 -602
  335. package/src/learning/tests/test_learning_db_v28.py +0 -110
  336. package/src/learning/tests/test_learning_init_v28.py +0 -48
  337. package/src/learning/tests/test_outcome_signals.py +0 -48
  338. package/src/learning/tests/test_project_context.py +0 -292
  339. package/src/learning/tests/test_schema_migration.py +0 -319
  340. package/src/learning/tests/test_signal_inference.py +0 -397
  341. package/src/learning/tests/test_source_quality.py +0 -351
  342. package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
  343. package/src/learning/tests/test_workflow_miner.py +0 -318
  344. package/src/learning/workflow_pattern_miner.py +0 -655
  345. package/src/lifecycle/__init__.py +0 -54
  346. package/src/lifecycle/bounded_growth.py +0 -239
  347. package/src/lifecycle/compaction_engine.py +0 -226
  348. package/src/lifecycle/lifecycle_engine.py +0 -355
  349. package/src/lifecycle/lifecycle_evaluator.py +0 -257
  350. package/src/lifecycle/lifecycle_scheduler.py +0 -130
  351. package/src/lifecycle/retention_policy.py +0 -285
  352. package/src/lifecycle/tests/test_bounded_growth.py +0 -193
  353. package/src/lifecycle/tests/test_compaction.py +0 -179
  354. package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
  355. package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
  356. package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
  357. package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
  358. package/src/lifecycle/tests/test_mcp_compact.py +0 -149
  359. package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
  360. package/src/lifecycle/tests/test_retention_policy.py +0 -162
  361. package/src/mcp_tools_v28.py +0 -281
  362. package/src/memory/__init__.py +0 -36
  363. package/src/memory/cli.py +0 -205
  364. package/src/memory/constants.py +0 -39
  365. package/src/memory/helpers.py +0 -28
  366. package/src/memory/schema.py +0 -166
  367. package/src/memory-profiles.py +0 -595
  368. package/src/memory-reset.py +0 -491
  369. package/src/memory_compression.py +0 -989
  370. package/src/memory_store_v2.py +0 -1155
  371. package/src/migrate_v1_to_v2.py +0 -629
  372. package/src/pattern_learner.py +0 -34
  373. package/src/patterns/__init__.py +0 -24
  374. package/src/patterns/analyzers.py +0 -251
  375. package/src/patterns/learner.py +0 -271
  376. package/src/patterns/scoring.py +0 -171
  377. package/src/patterns/store.py +0 -225
  378. package/src/patterns/terminology.py +0 -140
  379. package/src/provenance_tracker.py +0 -312
  380. package/src/qualixar_attribution.py +0 -139
  381. package/src/qualixar_watermark.py +0 -78
  382. package/src/query_optimizer.py +0 -511
  383. package/src/rate_limiter.py +0 -83
  384. package/src/search/__init__.py +0 -20
  385. package/src/search/cli.py +0 -77
  386. package/src/search/constants.py +0 -26
  387. package/src/search/engine.py +0 -241
  388. package/src/search/fusion.py +0 -122
  389. package/src/search/index_loader.py +0 -114
  390. package/src/search/methods.py +0 -162
  391. package/src/search_engine_v2.py +0 -401
  392. package/src/setup_validator.py +0 -482
  393. package/src/subscription_manager.py +0 -391
  394. package/src/tree/__init__.py +0 -59
  395. package/src/tree/builder.py +0 -185
  396. package/src/tree/nodes.py +0 -202
  397. package/src/tree/queries.py +0 -257
  398. package/src/tree/schema.py +0 -80
  399. package/src/tree_manager.py +0 -19
  400. package/src/trust/__init__.py +0 -45
  401. package/src/trust/constants.py +0 -66
  402. package/src/trust/queries.py +0 -157
  403. package/src/trust/schema.py +0 -95
  404. package/src/trust/scorer.py +0 -299
  405. package/src/trust/signals.py +0 -95
  406. package/src/trust_scorer.py +0 -44
  407. package/ui/app.js +0 -1588
  408. package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
  409. package/ui/js/graph-cytoscape.js +0 -1168
  410. package/ui/js/graph-d3-backup.js +0 -32
  411. package/ui/js/graph.js +0 -32
  412. package/ui_server.py +0 -266
  413. /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
  414. /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
  415. /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
  416. /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
  417. /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
  418. /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
  419. /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
  420. /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
  421. /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
  422. /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
  423. /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
  424. /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
  425. /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
  426. /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
  427. /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
  428. /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
  429. /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
  430. /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
  431. /package/{completions → ide/completions}/slm.bash +0 -0
  432. /package/{completions → ide/completions}/slm.zsh +0 -0
  433. /package/{configs → ide/configs}/cody-commands.json +0 -0
  434. /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
@@ -0,0 +1,99 @@
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
+ """Rules engine for configurable auto-capture and auto-recall behavior."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ DEFAULT_RULES = {
17
+ "auto_recall": {
18
+ "enabled": True,
19
+ "on_session_start": True,
20
+ "on_every_prompt": False,
21
+ "max_memories_injected": 10,
22
+ "relevance_threshold": 0.3,
23
+ },
24
+ "auto_capture": {
25
+ "enabled": True,
26
+ "capture_decisions": True,
27
+ "capture_bugs": True,
28
+ "capture_preferences": True,
29
+ "capture_session_summary": True,
30
+ "min_confidence": 0.5,
31
+ },
32
+ }
33
+
34
+
35
+ class RulesEngine:
36
+ """Manage configurable rules for auto-capture and auto-recall."""
37
+
38
+ def __init__(self, config: dict | None = None, config_path: Path | None = None):
39
+ if config:
40
+ self._rules = {**DEFAULT_RULES, **config}
41
+ elif config_path and config_path.exists():
42
+ data = json.loads(config_path.read_text())
43
+ self._rules = {**DEFAULT_RULES, **data.get("rules", {})}
44
+ else:
45
+ self._rules = dict(DEFAULT_RULES)
46
+
47
+ def should_capture(self, category: str, confidence: float) -> bool:
48
+ """Check if a category should be captured based on rules."""
49
+ capture_rules = self._rules.get("auto_capture", {})
50
+ if not capture_rules.get("enabled", True):
51
+ return False
52
+
53
+ category_key = f"capture_{category}s" if not category.endswith("s") else f"capture_{category}"
54
+ if not capture_rules.get(category_key, True):
55
+ return False
56
+
57
+ min_conf = capture_rules.get("min_confidence", 0.5)
58
+ return confidence >= min_conf
59
+
60
+ def should_recall(self, trigger: str) -> bool:
61
+ """Check if auto-recall should fire for a trigger."""
62
+ recall_rules = self._rules.get("auto_recall", {})
63
+ if not recall_rules.get("enabled", True):
64
+ return False
65
+
66
+ if trigger == "session_start":
67
+ return recall_rules.get("on_session_start", True)
68
+ if trigger == "every_prompt":
69
+ return recall_rules.get("on_every_prompt", False)
70
+
71
+ return True
72
+
73
+ def get_recall_config(self) -> dict:
74
+ """Get auto-recall configuration."""
75
+ return dict(self._rules.get("auto_recall", DEFAULT_RULES["auto_recall"]))
76
+
77
+ def get_capture_config(self) -> dict:
78
+ """Get auto-capture configuration."""
79
+ return dict(self._rules.get("auto_capture", DEFAULT_RULES["auto_capture"]))
80
+
81
+ def update_rule(self, section: str, key: str, value: Any) -> None:
82
+ """Update a specific rule."""
83
+ if section not in self._rules:
84
+ self._rules[section] = {}
85
+ self._rules[section][key] = value
86
+
87
+ def save(self, config_path: Path) -> None:
88
+ """Save rules to config file."""
89
+ if config_path.exists():
90
+ data = json.loads(config_path.read_text())
91
+ else:
92
+ data = {}
93
+ data["rules"] = self._rules
94
+ config_path.parent.mkdir(parents=True, exist_ok=True)
95
+ config_path.write_text(json.dumps(data, indent=2))
96
+
97
+ def to_dict(self) -> dict:
98
+ """Export rules as dict."""
99
+ return dict(self._rules)
@@ -0,0 +1,3 @@
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
@@ -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
+ """Opt-in API-key authentication middleware.
5
+
6
+ When ``~/.superlocalmemory/api_key`` exists, write endpoints require the
7
+ ``X-SLM-API-Key`` header. Read endpoints remain open for backward
8
+ compatibility. If the key file is absent auth is completely disabled --
9
+ all requests pass.
10
+
11
+ V3 change: base directory moved from ``~/.claude-memory/`` to
12
+ ``~/.superlocalmemory/``.
13
+ """
14
+
15
+ import hashlib
16
+ import logging
17
+ from pathlib import Path
18
+ from typing import Optional
19
+
20
+ logger = logging.getLogger("superlocalmemory.auth")
21
+
22
+ # V3 base directory
23
+ MEMORY_DIR = Path.home() / ".superlocalmemory"
24
+ API_KEY_FILE = MEMORY_DIR / "api_key"
25
+
26
+
27
+ def _load_api_key_hash(key_file: Optional[Path] = None) -> Optional[str]:
28
+ """Load and hash the API key from disk.
29
+
30
+ Args:
31
+ key_file: Override path (useful for testing).
32
+
33
+ Returns:
34
+ SHA-256 hex digest of the stored key, or ``None`` when auth is
35
+ not configured.
36
+ """
37
+ path = key_file or API_KEY_FILE
38
+ if not path.exists():
39
+ return None
40
+ try:
41
+ key = path.read_text().strip()
42
+ if not key:
43
+ return None
44
+ return hashlib.sha256(key.encode()).hexdigest()
45
+ except Exception as exc:
46
+ logger.warning("Failed to load API key: %s", exc)
47
+ return None
48
+
49
+
50
+ def check_api_key(
51
+ request_headers: dict,
52
+ is_write: bool = False,
53
+ key_file: Optional[Path] = None,
54
+ ) -> bool:
55
+ """Authorize a request against the stored API key.
56
+
57
+ Returns ``True`` when:
58
+ * No key file exists (auth not configured -- backward compatible).
59
+ * The request is a read operation (reads always allowed).
60
+ * The ``X-SLM-API-Key`` header matches the stored key.
61
+
62
+ Args:
63
+ request_headers: Mapping of HTTP header names to values.
64
+ is_write: ``True`` for mutating operations that require auth.
65
+ key_file: Override key-file path (testing).
66
+ """
67
+ key_hash = _load_api_key_hash(key_file)
68
+
69
+ # No key file = auth disabled
70
+ if key_hash is None:
71
+ return True
72
+
73
+ # Reads are always permitted
74
+ if not is_write:
75
+ return True
76
+
77
+ # Writes require a matching key
78
+ provided = request_headers.get("x-slm-api-key", "")
79
+ if not provided:
80
+ return False
81
+
82
+ return hashlib.sha256(provided.encode()).hexdigest() == key_hash
@@ -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
+ """Automated backup manager for SuperLocalMemory V3.
5
+
6
+ Provides:
7
+ * Configurable interval (daily / weekly)
8
+ * Timestamped SQLite-safe backups via the ``sqlite3.backup()`` API
9
+ * Retention policy (keeps last *N* backups)
10
+ * Restore with automatic pre-restore safety snapshot
11
+
12
+ V3 change: base directory is ``~/.superlocalmemory/`` (was ``~/.claude-memory/``).
13
+ """
14
+
15
+ import json
16
+ import logging
17
+ import sqlite3
18
+ from datetime import datetime, timedelta
19
+ from pathlib import Path
20
+ from typing import Dict, List, Optional
21
+
22
+ logger = logging.getLogger("superlocalmemory.backup")
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # V3 paths
26
+ # ---------------------------------------------------------------------------
27
+ MEMORY_DIR = Path.home() / ".superlocalmemory"
28
+ DB_PATH = MEMORY_DIR / "memory.db"
29
+ BACKUP_DIR = MEMORY_DIR / "backups"
30
+ CONFIG_FILE = MEMORY_DIR / "backup_config.json"
31
+
32
+ # Defaults
33
+ DEFAULT_INTERVAL_HOURS = 168 # 7 days
34
+ DEFAULT_MAX_BACKUPS = 10
35
+ MIN_INTERVAL_HOURS = 1
36
+
37
+
38
+ class BackupManager:
39
+ """Automated backup manager for SuperLocalMemory V3.
40
+
41
+ Args:
42
+ db_path: Path to the primary database file.
43
+ backup_dir: Directory where backup files are stored.
44
+ base_dir: Base SLM directory (used for config file + learning DB).
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ db_path: Optional[Path] = None,
50
+ backup_dir: Optional[Path] = None,
51
+ base_dir: Optional[Path] = None,
52
+ ) -> None:
53
+ self.base_dir = base_dir or MEMORY_DIR
54
+ self.db_path = db_path or (self.base_dir / "memory.db")
55
+ self.backup_dir = backup_dir or (self.base_dir / "backups")
56
+ self._config_file = self.base_dir / "backup_config.json"
57
+ self.config = self._load_config()
58
+ self._ensure_backup_dir()
59
+
60
+ # ------------------------------------------------------------------
61
+ # Config management
62
+ # ------------------------------------------------------------------
63
+
64
+ def _ensure_backup_dir(self) -> None:
65
+ self.backup_dir.mkdir(parents=True, exist_ok=True)
66
+
67
+ def _load_config(self) -> Dict:
68
+ if self._config_file.exists():
69
+ try:
70
+ raw = json.loads(self._config_file.read_text())
71
+ defaults = self._default_config()
72
+ for k in defaults:
73
+ raw.setdefault(k, defaults[k])
74
+ return raw
75
+ except (json.JSONDecodeError, IOError):
76
+ pass
77
+ return self._default_config()
78
+
79
+ @staticmethod
80
+ def _default_config() -> Dict:
81
+ return {
82
+ "enabled": True,
83
+ "interval_hours": DEFAULT_INTERVAL_HOURS,
84
+ "max_backups": DEFAULT_MAX_BACKUPS,
85
+ "last_backup": None,
86
+ "last_backup_file": None,
87
+ }
88
+
89
+ def _save_config(self) -> None:
90
+ try:
91
+ self._config_file.parent.mkdir(parents=True, exist_ok=True)
92
+ self._config_file.write_text(json.dumps(self.config, indent=2))
93
+ except IOError as exc:
94
+ logger.error("Failed to save backup config: %s", exc)
95
+
96
+ def configure(
97
+ self,
98
+ interval_hours: Optional[int] = None,
99
+ max_backups: Optional[int] = None,
100
+ enabled: Optional[bool] = None,
101
+ ) -> Dict:
102
+ """Update backup configuration and return current status."""
103
+ if interval_hours is not None:
104
+ self.config["interval_hours"] = max(MIN_INTERVAL_HOURS, interval_hours)
105
+ if max_backups is not None:
106
+ self.config["max_backups"] = max(1, max_backups)
107
+ if enabled is not None:
108
+ self.config["enabled"] = enabled
109
+ self._save_config()
110
+ return self.get_status()
111
+
112
+ # ------------------------------------------------------------------
113
+ # Scheduling helpers
114
+ # ------------------------------------------------------------------
115
+
116
+ def is_backup_due(self) -> bool:
117
+ """Return ``True`` when a backup should be taken."""
118
+ if not self.config.get("enabled", True):
119
+ return False
120
+ last = self.config.get("last_backup")
121
+ if not last:
122
+ return True
123
+ try:
124
+ last_dt = datetime.fromisoformat(last)
125
+ interval = timedelta(hours=self.config.get("interval_hours", DEFAULT_INTERVAL_HOURS))
126
+ return datetime.now() >= last_dt + interval
127
+ except (ValueError, TypeError):
128
+ return True
129
+
130
+ def check_and_backup(self) -> Optional[str]:
131
+ """Create a backup only when one is due. Returns filename or ``None``."""
132
+ if not self.is_backup_due():
133
+ return None
134
+ return self.create_backup()
135
+
136
+ # ------------------------------------------------------------------
137
+ # Core backup / restore
138
+ # ------------------------------------------------------------------
139
+
140
+ def create_backup(self, label: Optional[str] = None) -> str:
141
+ """Create a timestamped backup via the SQLite online-backup API.
142
+
143
+ Returns:
144
+ Backup filename on success, empty string on failure.
145
+ """
146
+ if not self.db_path.exists():
147
+ logger.warning("No database to backup at %s", self.db_path)
148
+ return ""
149
+
150
+ self._ensure_backup_dir()
151
+
152
+ timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
153
+ suffix = f"-{label}" if label else ""
154
+ backup_name = f"memory-{timestamp}{suffix}.db"
155
+ backup_path = self.backup_dir / backup_name
156
+
157
+ try:
158
+ source = sqlite3.connect(str(self.db_path))
159
+ dest = sqlite3.connect(str(backup_path))
160
+ try:
161
+ source.backup(dest)
162
+ finally:
163
+ dest.close()
164
+ source.close()
165
+
166
+ size_mb = backup_path.stat().st_size / (1024 * 1024)
167
+ self.config["last_backup"] = datetime.now().isoformat()
168
+ self.config["last_backup_file"] = backup_name
169
+ self._save_config()
170
+ logger.info("Backup created: %s (%.1f MB)", backup_name, size_mb)
171
+
172
+ # Also backup learning.db if present
173
+ self._backup_learning_db(timestamp, suffix)
174
+
175
+ self._enforce_retention()
176
+ return backup_name
177
+
178
+ except Exception as exc:
179
+ logger.error("Backup failed: %s", exc)
180
+ if backup_path.exists():
181
+ backup_path.unlink()
182
+ return ""
183
+
184
+ def _backup_learning_db(self, timestamp: str, suffix: str) -> None:
185
+ """Best-effort backup of ``learning.db`` alongside the main DB."""
186
+ learning_db = self.db_path.parent / "learning.db"
187
+ if not learning_db.exists():
188
+ return
189
+ try:
190
+ name = f"learning-{timestamp}{suffix}.db"
191
+ path = self.backup_dir / name
192
+ src = sqlite3.connect(str(learning_db))
193
+ dst = sqlite3.connect(str(path))
194
+ try:
195
+ src.backup(dst)
196
+ finally:
197
+ dst.close()
198
+ src.close()
199
+ logger.info("Learning backup: %s (%.1f MB)", name, path.stat().st_size / (1024 * 1024))
200
+ except Exception as exc:
201
+ logger.warning("Learning DB backup failed (non-critical): %s", exc)
202
+
203
+ def _enforce_retention(self) -> None:
204
+ """Remove old backups exceeding the configured max."""
205
+ max_backups = self.config.get("max_backups", DEFAULT_MAX_BACKUPS)
206
+ for pattern in ("memory-*.db", "learning-*.db"):
207
+ backups = sorted(
208
+ self.backup_dir.glob(pattern),
209
+ key=lambda f: f.stat().st_mtime,
210
+ )
211
+ while len(backups) > max_backups:
212
+ oldest = backups.pop(0)
213
+ try:
214
+ oldest.unlink()
215
+ logger.info("Removed old backup: %s", oldest.name)
216
+ except OSError as exc:
217
+ logger.error("Failed to remove backup %s: %s", oldest.name, exc)
218
+
219
+ def restore_backup(self, filename: str) -> bool:
220
+ """Restore the database from *filename*.
221
+
222
+ A safety snapshot of the current state is taken first.
223
+ """
224
+ backup_path = self.backup_dir / filename
225
+ if not backup_path.exists():
226
+ logger.error("Backup not found: %s", filename)
227
+ return False
228
+
229
+ try:
230
+ self.create_backup(label="pre-restore")
231
+
232
+ target = (
233
+ self.db_path.parent / "learning.db"
234
+ if filename.startswith("learning-")
235
+ else self.db_path
236
+ )
237
+
238
+ src = sqlite3.connect(str(backup_path))
239
+ dst = sqlite3.connect(str(target))
240
+ try:
241
+ src.backup(dst)
242
+ finally:
243
+ dst.close()
244
+ src.close()
245
+
246
+ logger.info("Restored: %s -> %s", filename, target.name)
247
+ return True
248
+
249
+ except Exception as exc:
250
+ logger.error("Restore failed: %s", exc)
251
+ return False
252
+
253
+ # ------------------------------------------------------------------
254
+ # Listing / status
255
+ # ------------------------------------------------------------------
256
+
257
+ def list_backups(self) -> List[Dict]:
258
+ """Return metadata for all available backups (newest first)."""
259
+ if not self.backup_dir.exists():
260
+ return []
261
+
262
+ result: List[Dict] = []
263
+ for pattern in ("memory-*.db", "learning-*.db"):
264
+ for f in sorted(self.backup_dir.glob(pattern), key=lambda p: p.stat().st_mtime, reverse=True):
265
+ st = f.stat()
266
+ db_type = "learning" if f.name.startswith("learning-") else "memory"
267
+ result.append({
268
+ "filename": f.name,
269
+ "path": str(f),
270
+ "size_mb": round(st.st_size / (1024 * 1024), 2),
271
+ "created": datetime.fromtimestamp(st.st_mtime).isoformat(),
272
+ "age_hours": round(
273
+ (datetime.now() - datetime.fromtimestamp(st.st_mtime)).total_seconds() / 3600, 1
274
+ ),
275
+ "type": db_type,
276
+ })
277
+ result.sort(key=lambda b: b["created"], reverse=True)
278
+ return result
279
+
280
+ def get_status(self) -> Dict:
281
+ """Return a status summary of the backup system."""
282
+ backups = self.list_backups()
283
+ next_backup = None
284
+
285
+ if self.config.get("enabled") and self.config.get("last_backup"):
286
+ try:
287
+ last_dt = datetime.fromisoformat(self.config["last_backup"])
288
+ interval = timedelta(hours=self.config.get("interval_hours", DEFAULT_INTERVAL_HOURS))
289
+ nxt = last_dt + interval
290
+ next_backup = nxt.isoformat() if nxt > datetime.now() else "overdue"
291
+ except (ValueError, TypeError):
292
+ next_backup = "unknown"
293
+
294
+ hours = self.config.get("interval_hours", DEFAULT_INTERVAL_HOURS)
295
+ if hours >= 168:
296
+ display = f"{hours // 168} week(s)"
297
+ elif hours >= 24:
298
+ display = f"{hours // 24} day(s)"
299
+ else:
300
+ display = f"{hours} hour(s)"
301
+
302
+ mem_bk = [b for b in backups if b.get("type") == "memory"]
303
+ learn_bk = [b for b in backups if b.get("type") == "learning"]
304
+
305
+ return {
306
+ "enabled": self.config.get("enabled", True),
307
+ "interval_hours": hours,
308
+ "interval_display": display,
309
+ "max_backups": self.config.get("max_backups", DEFAULT_MAX_BACKUPS),
310
+ "last_backup": self.config.get("last_backup"),
311
+ "last_backup_file": self.config.get("last_backup_file"),
312
+ "next_backup": next_backup,
313
+ "backup_count": len(mem_bk),
314
+ "learning_backup_count": len(learn_bk),
315
+ "total_size_mb": round(sum(b["size_mb"] for b in backups), 2),
316
+ "backups": backups,
317
+ }