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,347 @@
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 — Bayesian Trust Scorer (Beta Distribution).
6
+
7
+ Per-agent and per-fact trust using conjugate Beta(alpha, beta) priors.
8
+ Trust = alpha / (alpha + beta). Prior decay toward uniform when idle.
9
+
10
+ Encoding into existing schema (no ALTER TABLE):
11
+ trust_score = alpha / (alpha + beta)
12
+ evidence_count = round(alpha + beta - 2) (subtract the default prior)
13
+ Reconstruct: alpha = trust_score * (evidence_count + 2)
14
+ beta = (1 - trust_score) * (evidence_count + 2)
15
+
16
+ Backward-compatible: update_on_confirmation / contradiction / access
17
+ delegate to record_signal internally.
18
+
19
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import logging
25
+ import math
26
+ from datetime import UTC, datetime
27
+
28
+ from superlocalmemory.storage.models import TrustScore
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ # Default Beta(1,1) = uniform prior => trust 0.5
33
+ _DEFAULT_ALPHA = 1.0
34
+ _DEFAULT_BETA = 1.0
35
+ _DEFAULT_TRUST = 0.5
36
+
37
+ # Signal strengths (how much each event shifts alpha/beta)
38
+ _SIGNAL_WEIGHTS: dict[str, tuple[float, float]] = {
39
+ # (delta_alpha, delta_beta)
40
+ "store_success": (1.0, 0.0),
41
+ "store_rejected": (0.0, 2.0),
42
+ "recall_hit": (0.5, 0.0),
43
+ "contradiction": (0.0, 3.0),
44
+ "deletion": (0.0, 1.5),
45
+ }
46
+
47
+ # Prior decay: days of inactivity before decay starts, and rate per day
48
+ _DECAY_IDLE_DAYS = 30
49
+ _DECAY_RATE = 0.05 # 5% per day toward prior
50
+
51
+
52
+ class TrustScorer:
53
+ """Bayesian trust scoring with Beta distribution.
54
+
55
+ Trust(agent) = alpha / (alpha + beta).
56
+ - Positive evidence increments alpha.
57
+ - Negative evidence increments beta.
58
+ - Idle decay shrinks both toward 1.0 (uniform prior).
59
+
60
+ Facts inherit their source agent's trust, penalized by contradictions.
61
+ """
62
+
63
+ def __init__(self, db) -> None:
64
+ self._db = db
65
+
66
+ # ------------------------------------------------------------------
67
+ # Public API: per-agent trust
68
+ # ------------------------------------------------------------------
69
+
70
+ def get_agent_trust(self, agent_id: str, profile_id: str) -> float:
71
+ """Get trust score for an agent. Returns 0.5 if unknown."""
72
+ alpha, beta = self._get_beta_params("agent", agent_id, profile_id)
73
+ return self._compute_trust(alpha, beta)
74
+
75
+ def get_fact_trust(self, fact_id: str, profile_id: str) -> float:
76
+ """Get trust for a fact. Inherits source agent trust, modified by
77
+ any contradiction evidence recorded directly against the fact."""
78
+ alpha, beta = self._get_beta_params("fact", fact_id, profile_id)
79
+ return self._compute_trust(alpha, beta)
80
+
81
+ def get_entity_trust(self, entity_id: str, profile_id: str) -> float:
82
+ """Convenience: get trust for an entity."""
83
+ return self.get_trust("entity", entity_id, profile_id)
84
+
85
+ def get_trust(
86
+ self, target_type: str, target_id: str, profile_id: str
87
+ ) -> float:
88
+ """Generic trust lookup — backward compatible with V3 Task 3."""
89
+ alpha, beta = self._get_beta_params(target_type, target_id, profile_id)
90
+ return self._compute_trust(alpha, beta)
91
+
92
+ # ------------------------------------------------------------------
93
+ # Public API: record signals
94
+ # ------------------------------------------------------------------
95
+
96
+ def record_signal(
97
+ self, agent_id: str, profile_id: str, signal_type: str
98
+ ) -> float:
99
+ """Record a trust signal and return updated trust score.
100
+
101
+ Args:
102
+ agent_id: The agent whose trust is being updated.
103
+ profile_id: Active profile scope.
104
+ signal_type: One of store_success, store_rejected, recall_hit,
105
+ contradiction, deletion.
106
+
107
+ Returns:
108
+ Updated trust score for the agent.
109
+ """
110
+ weights = _SIGNAL_WEIGHTS.get(signal_type, (0.0, 0.0))
111
+ delta_alpha, delta_beta = weights
112
+
113
+ alpha, beta = self._get_beta_params("agent", agent_id, profile_id)
114
+ new_alpha = alpha + delta_alpha
115
+ new_beta = beta + delta_beta
116
+
117
+ self._persist_beta("agent", agent_id, profile_id, new_alpha, new_beta)
118
+ score = self._compute_trust(new_alpha, new_beta)
119
+ logger.debug(
120
+ "trust signal: agent=%s signal=%s trust=%.3f (a=%.1f b=%.1f)",
121
+ agent_id, signal_type, score, new_alpha, new_beta,
122
+ )
123
+ return score
124
+
125
+ # ------------------------------------------------------------------
126
+ # Public API: propagation
127
+ # ------------------------------------------------------------------
128
+
129
+ def propagate_recall_trust(
130
+ self, agent_id: str, profile_id: str
131
+ ) -> float:
132
+ """Boost trust for agents whose memories are recalled.
133
+
134
+ A recall-hit means the system found the agent's data useful.
135
+ """
136
+ return self.record_signal(agent_id, profile_id, "recall_hit")
137
+
138
+ # ------------------------------------------------------------------
139
+ # Public API: direct set (testing & migration)
140
+ # ------------------------------------------------------------------
141
+
142
+ def set_trust(
143
+ self,
144
+ agent_id: str,
145
+ profile_id: str,
146
+ alpha: float = _DEFAULT_ALPHA,
147
+ beta_param: float = _DEFAULT_BETA,
148
+ ) -> float:
149
+ """Set trust directly via Beta params. Primarily for testing."""
150
+ alpha = max(0.01, alpha)
151
+ beta_param = max(0.01, beta_param)
152
+ self._persist_beta("agent", agent_id, profile_id, alpha, beta_param)
153
+ return self._compute_trust(alpha, beta_param)
154
+
155
+ # ------------------------------------------------------------------
156
+ # Public API: bulk query
157
+ # ------------------------------------------------------------------
158
+
159
+ def get_all_scores(self, profile_id: str) -> list[dict]:
160
+ """Get all trust scores for a profile as dicts.
161
+
162
+ Returns list of {target_type, target_id, trust_score,
163
+ alpha, beta_param, evidence_count, last_updated}.
164
+ """
165
+ rows = self._db.execute(
166
+ "SELECT * FROM trust_scores WHERE profile_id = ?",
167
+ (profile_id,),
168
+ )
169
+ results: list[dict] = []
170
+ for r in rows:
171
+ d = dict(r)
172
+ alpha, beta = self._decode_beta(
173
+ d["trust_score"], d["evidence_count"]
174
+ )
175
+ results.append({
176
+ "trust_id": d["trust_id"],
177
+ "target_type": d["target_type"],
178
+ "target_id": d["target_id"],
179
+ "trust_score": d["trust_score"],
180
+ "alpha": alpha,
181
+ "beta_param": beta,
182
+ "evidence_count": d["evidence_count"],
183
+ "last_updated": d["last_updated"],
184
+ })
185
+ return results
186
+
187
+ # ------------------------------------------------------------------
188
+ # Backward-compatible delegators
189
+ # ------------------------------------------------------------------
190
+
191
+ def update_on_confirmation(
192
+ self, target_type: str, target_id: str, profile_id: str
193
+ ) -> float:
194
+ """V3-compat: confirmation -> store_success signal."""
195
+ alpha, beta = self._get_beta_params(target_type, target_id, profile_id)
196
+ new_alpha = alpha + 1.0
197
+ self._persist_beta(target_type, target_id, profile_id, new_alpha, beta)
198
+ return self._compute_trust(new_alpha, beta)
199
+
200
+ def update_on_contradiction(
201
+ self, target_type: str, target_id: str, profile_id: str
202
+ ) -> float:
203
+ """V3-compat: contradiction -> contradiction signal."""
204
+ alpha, beta = self._get_beta_params(target_type, target_id, profile_id)
205
+ new_beta = beta + 3.0
206
+ self._persist_beta(target_type, target_id, profile_id, alpha, new_beta)
207
+ return self._compute_trust(alpha, new_beta)
208
+
209
+ def update_on_access(
210
+ self, target_type: str, target_id: str, profile_id: str
211
+ ) -> float:
212
+ """V3-compat: access -> recall_hit signal (small boost)."""
213
+ alpha, beta = self._get_beta_params(target_type, target_id, profile_id)
214
+ new_alpha = alpha + 0.5
215
+ self._persist_beta(
216
+ target_type, target_id, profile_id, new_alpha, beta
217
+ )
218
+ return self._compute_trust(new_alpha, beta)
219
+
220
+ # ------------------------------------------------------------------
221
+ # Prior decay
222
+ # ------------------------------------------------------------------
223
+
224
+ def apply_prior_decay(self, profile_id: str) -> int:
225
+ """Decay idle trust scores toward uniform prior Beta(1,1).
226
+
227
+ Called periodically (e.g., daily maintenance).
228
+ Returns number of scores decayed.
229
+ """
230
+ cutoff = datetime.now(UTC).isoformat()
231
+ rows = self._db.execute(
232
+ "SELECT * FROM trust_scores WHERE profile_id = ?",
233
+ (profile_id,),
234
+ )
235
+ decayed = 0
236
+ for r in rows:
237
+ d = dict(r)
238
+ last = d.get("last_updated", "")
239
+ if not last:
240
+ continue
241
+ try:
242
+ last_dt = datetime.fromisoformat(last)
243
+ idle_days = (
244
+ datetime.now(UTC) - last_dt.replace(tzinfo=UTC)
245
+ ).days
246
+ except (ValueError, TypeError):
247
+ continue
248
+
249
+ if idle_days < _DECAY_IDLE_DAYS:
250
+ continue
251
+
252
+ alpha, beta = self._decode_beta(
253
+ d["trust_score"], d["evidence_count"]
254
+ )
255
+ excess_days = idle_days - _DECAY_IDLE_DAYS
256
+ decay_factor = max(0.0, 1.0 - _DECAY_RATE * excess_days)
257
+
258
+ new_alpha = _DEFAULT_ALPHA + (alpha - _DEFAULT_ALPHA) * decay_factor
259
+ new_beta = _DEFAULT_BETA + (beta - _DEFAULT_BETA) * decay_factor
260
+
261
+ new_alpha = max(0.01, new_alpha)
262
+ new_beta = max(0.01, new_beta)
263
+
264
+ score = self._compute_trust(new_alpha, new_beta)
265
+ ev = max(0, round(new_alpha + new_beta - 2))
266
+ self._db.execute(
267
+ "UPDATE trust_scores SET trust_score = ?, evidence_count = ?, "
268
+ "last_updated = ? WHERE trust_id = ?",
269
+ (score, ev, cutoff, d["trust_id"]),
270
+ )
271
+ decayed += 1
272
+ return decayed
273
+
274
+ # ------------------------------------------------------------------
275
+ # Internal helpers
276
+ # ------------------------------------------------------------------
277
+
278
+ @staticmethod
279
+ def _compute_trust(alpha: float, beta: float) -> float:
280
+ """Trust = alpha / (alpha + beta), clamped to [0, 1]."""
281
+ total = alpha + beta
282
+ if total <= 0:
283
+ return _DEFAULT_TRUST
284
+ return max(0.0, min(1.0, alpha / total))
285
+
286
+ @staticmethod
287
+ def _decode_beta(
288
+ trust_score: float, evidence_count: int
289
+ ) -> tuple[float, float]:
290
+ """Reconstruct Beta params from encoded trust_score + evidence_count.
291
+
292
+ Encoding: trust_score = a/(a+b), evidence_count = round(a+b-2).
293
+ """
294
+ total = float(evidence_count) + 2.0
295
+ alpha = trust_score * total
296
+ beta = (1.0 - trust_score) * total
297
+ return max(0.01, alpha), max(0.01, beta)
298
+
299
+ def _get_beta_params(
300
+ self, target_type: str, target_id: str, profile_id: str
301
+ ) -> tuple[float, float]:
302
+ """Load Beta(alpha, beta) from DB, or return default prior."""
303
+ rows = self._db.execute(
304
+ "SELECT trust_score, evidence_count FROM trust_scores "
305
+ "WHERE target_type = ? AND target_id = ? AND profile_id = ?",
306
+ (target_type, target_id, profile_id),
307
+ )
308
+ if rows:
309
+ d = dict(rows[0])
310
+ return self._decode_beta(d["trust_score"], d["evidence_count"])
311
+ return _DEFAULT_ALPHA, _DEFAULT_BETA
312
+
313
+ def _persist_beta(
314
+ self,
315
+ target_type: str,
316
+ target_id: str,
317
+ profile_id: str,
318
+ alpha: float,
319
+ beta: float,
320
+ ) -> None:
321
+ """Encode and persist Beta params into existing schema."""
322
+ score = self._compute_trust(alpha, beta)
323
+ ev = max(0, round(alpha + beta - 2))
324
+ now = datetime.now(UTC).isoformat()
325
+
326
+ existing = self._db.execute(
327
+ "SELECT trust_id FROM trust_scores "
328
+ "WHERE target_type = ? AND target_id = ? AND profile_id = ?",
329
+ (target_type, target_id, profile_id),
330
+ )
331
+ if existing:
332
+ tid = dict(existing[0])["trust_id"]
333
+ self._db.execute(
334
+ "UPDATE trust_scores SET trust_score = ?, evidence_count = ?, "
335
+ "last_updated = ? WHERE trust_id = ?",
336
+ (score, ev, now, tid),
337
+ )
338
+ else:
339
+ from superlocalmemory.storage.models import _new_id
340
+
341
+ tid = _new_id()
342
+ self._db.execute(
343
+ "INSERT INTO trust_scores "
344
+ "(trust_id, profile_id, target_type, target_id, trust_score, "
345
+ "evidence_count, last_updated) VALUES (?,?,?,?,?,?,?)",
346
+ (tid, profile_id, target_type, target_id, score, ev, now),
347
+ )
@@ -0,0 +1,153 @@
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 Signal Recorder with Burst Detection.
6
+
7
+ Records trust signals and detects anomalous write patterns.
8
+ Burst detection uses an in-memory sliding window (collections.deque)
9
+ per agent. Signals are published via the event bus rather than a
10
+ separate table, keeping the storage footprint minimal.
11
+
12
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ import time
19
+ from collections import defaultdict, deque
20
+ from typing import Any
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Valid signal types (must match scorer._SIGNAL_WEIGHTS keys)
25
+ VALID_SIGNAL_TYPES = frozenset({
26
+ "store_success",
27
+ "store_rejected",
28
+ "recall_hit",
29
+ "contradiction",
30
+ "deletion",
31
+ })
32
+
33
+
34
+ class SignalRecorder:
35
+ """Records trust signals and detects anomalous write patterns.
36
+
37
+ Burst detection: tracks write timestamps in a per-agent deque.
38
+ If > threshold writes occur within the window, burst is flagged.
39
+ Burst-flagged signals are still recorded but logged as anomalous.
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ db: Any,
45
+ burst_window_seconds: int = 60,
46
+ burst_threshold: int = 20,
47
+ ) -> None:
48
+ self._db = db
49
+ self._burst_window = burst_window_seconds
50
+ self._burst_threshold = burst_threshold
51
+
52
+ # Per-agent sliding window: key = (agent_id, profile_id)
53
+ # Value = deque of timestamps (float, time.monotonic)
54
+ self._windows: dict[tuple[str, str], deque[float]] = defaultdict(
55
+ lambda: deque(maxlen=max(1, burst_threshold * 2))
56
+ )
57
+
58
+ # In-memory signal log for get_recent_signals
59
+ self._recent: dict[tuple[str, str], deque[dict]] = defaultdict(
60
+ lambda: deque(maxlen=200)
61
+ )
62
+
63
+ def record(
64
+ self,
65
+ agent_id: str,
66
+ profile_id: str,
67
+ signal_type: str,
68
+ context: dict[str, Any] | None = None,
69
+ ) -> bool:
70
+ """Record a trust signal.
71
+
72
+ Args:
73
+ agent_id: The agent generating the signal.
74
+ profile_id: Active profile scope.
75
+ signal_type: One of VALID_SIGNAL_TYPES.
76
+ context: Optional metadata about the signal.
77
+
78
+ Returns:
79
+ True if signal was accepted (even during burst).
80
+ False if signal_type is invalid.
81
+ """
82
+ if signal_type not in VALID_SIGNAL_TYPES:
83
+ logger.warning("invalid signal type: %s", signal_type)
84
+ return False
85
+
86
+ now = time.monotonic()
87
+ key = (agent_id, profile_id)
88
+
89
+ # Update sliding window
90
+ window = self._windows[key]
91
+ window.append(now)
92
+ self._prune_window(window, now)
93
+
94
+ # Check burst before recording
95
+ is_burst = len(window) >= self._burst_threshold
96
+ if is_burst:
97
+ logger.warning(
98
+ "burst detected: agent=%s profile=%s count=%d in %ds",
99
+ agent_id, profile_id, len(window), self._burst_window,
100
+ )
101
+
102
+ # Record signal in memory log
103
+ entry = {
104
+ "agent_id": agent_id,
105
+ "profile_id": profile_id,
106
+ "signal_type": signal_type,
107
+ "timestamp": now,
108
+ "burst_flagged": is_burst,
109
+ "context": context or {},
110
+ }
111
+ self._recent[key].append(entry)
112
+
113
+ return True
114
+
115
+ def is_burst_detected(self, agent_id: str, profile_id: str) -> bool:
116
+ """True if agent has exceeded burst_threshold writes in window."""
117
+ key = (agent_id, profile_id)
118
+ window = self._windows.get(key)
119
+ if window is None:
120
+ return False
121
+
122
+ now = time.monotonic()
123
+ self._prune_window(window, now)
124
+ return len(window) >= self._burst_threshold
125
+
126
+ def get_recent_signals(
127
+ self, agent_id: str, profile_id: str, limit: int = 50
128
+ ) -> list[dict]:
129
+ """Get recent signals for an agent, most recent first."""
130
+ key = (agent_id, profile_id)
131
+ signals = list(self._recent.get(key, []))
132
+ signals.reverse()
133
+ return signals[:limit]
134
+
135
+ def get_burst_status(self, profile_id: str) -> dict[str, bool]:
136
+ """Get burst status for all agents in a profile.
137
+
138
+ Returns dict mapping agent_id -> is_bursting.
139
+ """
140
+ result: dict[str, bool] = {}
141
+ now = time.monotonic()
142
+ for (aid, pid), window in self._windows.items():
143
+ if pid != profile_id:
144
+ continue
145
+ self._prune_window(window, now)
146
+ result[aid] = len(window) >= self._burst_threshold
147
+ return result
148
+
149
+ def _prune_window(self, window: deque[float], now: float) -> None:
150
+ """Remove timestamps older than the burst window."""
151
+ cutoff = now - self._burst_window
152
+ while window and window[0] < cutoff:
153
+ window.popleft()