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,303 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (c) 2026 Qualixar / SuperLocalMemory (superlocalmemory.com)
4
+ # Part of Qualixar | Author: Varun Pratap Bhardwaj (qualixar.com | varunpratap.com)
5
+ """
6
+ SourceQualityScorer -- Beta-binomial source quality scoring for V3 learning.
7
+
8
+ Each memory source (agent, URL, manual, etc.) gets a quality score based on
9
+ how often its memories are confirmed vs contradicted or ignored.
10
+
11
+ Scoring (Beta-Binomial with Laplace smoothing):
12
+ quality = (alpha + positives) / (alpha + beta + total)
13
+
14
+ With alpha=1, beta=1 (uniform prior):
15
+ - New source, 0 evidence -> 1/2 = 0.50
16
+ - 8 positive out of 10 -> 9/12 = 0.75
17
+ - 1 positive out of 10 -> 2/12 = 0.17
18
+
19
+ Storage:
20
+ Uses direct sqlite3 with a self-contained ``source_quality`` table.
21
+ NOT coupled to V3 DatabaseManager.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ import sqlite3
28
+ import threading
29
+ from datetime import datetime, timezone
30
+ from pathlib import Path
31
+ from typing import Any, Dict, Optional
32
+
33
+ logger = logging.getLogger("superlocalmemory.learning.source_quality")
34
+
35
+ # Beta-Binomial prior (Laplace / uniform)
36
+ _ALPHA = 1.0
37
+ _BETA = 1.0
38
+
39
+ # Default quality for unknown sources = alpha / (alpha + beta)
40
+ DEFAULT_QUALITY = _ALPHA / (_ALPHA + _BETA) # 0.5
41
+
42
+ _CREATE_TABLE = """
43
+ CREATE TABLE IF NOT EXISTS source_quality (
44
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
45
+ profile_id TEXT NOT NULL,
46
+ source_id TEXT NOT NULL,
47
+ alpha REAL NOT NULL DEFAULT 1.0,
48
+ beta REAL NOT NULL DEFAULT 1.0,
49
+ updated_at TEXT NOT NULL
50
+ )
51
+ """
52
+
53
+ _CREATE_UNIQUE = """
54
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_sq_profile_source
55
+ ON source_quality (profile_id, source_id)
56
+ """
57
+
58
+
59
+ def _utcnow_iso() -> str:
60
+ """Return current UTC time as ISO-8601 string."""
61
+ return datetime.now(timezone.utc).isoformat()
62
+
63
+
64
+ class SourceQualityScorer:
65
+ """
66
+ Beta-binomial source quality scoring.
67
+
68
+ Maintains per-(profile, source) alpha/beta parameters. Positive
69
+ outcomes increment alpha; negative outcomes increment beta.
70
+ Quality = alpha / (alpha + beta).
71
+
72
+ Args:
73
+ db_path: Path to the sqlite3 database file.
74
+ """
75
+
76
+ def __init__(self, db_path: Path) -> None:
77
+ self._db_path = Path(db_path)
78
+ self._lock = threading.Lock()
79
+ self._ensure_schema()
80
+
81
+ # ------------------------------------------------------------------
82
+ # Schema
83
+ # ------------------------------------------------------------------
84
+
85
+ def _ensure_schema(self) -> None:
86
+ conn = self._connect()
87
+ try:
88
+ conn.execute(_CREATE_TABLE)
89
+ conn.execute(_CREATE_UNIQUE)
90
+ conn.commit()
91
+ finally:
92
+ conn.close()
93
+
94
+ def _connect(self) -> sqlite3.Connection:
95
+ conn = sqlite3.connect(str(self._db_path), timeout=10)
96
+ conn.execute("PRAGMA journal_mode=WAL")
97
+ conn.execute("PRAGMA busy_timeout=5000")
98
+ conn.row_factory = sqlite3.Row
99
+ return conn
100
+
101
+ # ------------------------------------------------------------------
102
+ # Public API: record outcome
103
+ # ------------------------------------------------------------------
104
+
105
+ def record_outcome(
106
+ self,
107
+ profile_id: str,
108
+ source_id: str,
109
+ outcome: str,
110
+ ) -> None:
111
+ """
112
+ Record an observation for a source.
113
+
114
+ Args:
115
+ profile_id: Profile context.
116
+ source_id: Identifier of the source (agent name, URL, etc.).
117
+ outcome: ``"positive"`` or ``"negative"``.
118
+
119
+ Raises:
120
+ ValueError: If outcome is not ``"positive"`` or ``"negative"``.
121
+ """
122
+ if outcome not in ("positive", "negative"):
123
+ raise ValueError(
124
+ f"outcome must be 'positive' or 'negative', got {outcome!r}"
125
+ )
126
+ if not profile_id or not source_id:
127
+ return
128
+
129
+ now = _utcnow_iso()
130
+
131
+ with self._lock:
132
+ conn = self._connect()
133
+ try:
134
+ # Ensure row exists (INSERT OR IGNORE with defaults)
135
+ conn.execute(
136
+ "INSERT OR IGNORE INTO source_quality "
137
+ "(profile_id, source_id, alpha, beta, updated_at) "
138
+ "VALUES (?, ?, ?, ?, ?)",
139
+ (profile_id, source_id, _ALPHA, _BETA, now),
140
+ )
141
+
142
+ # Update the appropriate parameter
143
+ if outcome == "positive":
144
+ conn.execute(
145
+ "UPDATE source_quality "
146
+ "SET alpha = alpha + 1.0, updated_at = ? "
147
+ "WHERE profile_id = ? AND source_id = ?",
148
+ (now, profile_id, source_id),
149
+ )
150
+ else:
151
+ conn.execute(
152
+ "UPDATE source_quality "
153
+ "SET beta = beta + 1.0, updated_at = ? "
154
+ "WHERE profile_id = ? AND source_id = ?",
155
+ (now, profile_id, source_id),
156
+ )
157
+
158
+ conn.commit()
159
+ finally:
160
+ conn.close()
161
+
162
+ # ------------------------------------------------------------------
163
+ # Public API: read quality
164
+ # ------------------------------------------------------------------
165
+
166
+ def get_quality(self, profile_id: str, source_id: str) -> float:
167
+ """
168
+ Get the quality score for a specific source.
169
+
170
+ Returns the Beta-binomial posterior mean:
171
+ quality = alpha / (alpha + beta)
172
+
173
+ If the source has never been observed, returns the prior
174
+ mean (0.5).
175
+
176
+ Args:
177
+ profile_id: Profile context.
178
+ source_id: Source identifier.
179
+
180
+ Returns:
181
+ Quality score in [0.0, 1.0].
182
+ """
183
+ conn = self._connect()
184
+ try:
185
+ row = conn.execute(
186
+ "SELECT alpha, beta FROM source_quality "
187
+ "WHERE profile_id = ? AND source_id = ?",
188
+ (profile_id, source_id),
189
+ ).fetchone()
190
+
191
+ if row is None:
192
+ return DEFAULT_QUALITY
193
+
194
+ alpha = float(row["alpha"])
195
+ beta = float(row["beta"])
196
+ denom = alpha + beta
197
+ if denom <= 0:
198
+ return DEFAULT_QUALITY
199
+ return alpha / denom
200
+ finally:
201
+ conn.close()
202
+
203
+ def get_all_qualities(self, profile_id: str) -> Dict[str, float]:
204
+ """
205
+ Get quality scores for all sources observed under a profile.
206
+
207
+ Args:
208
+ profile_id: Profile context.
209
+
210
+ Returns:
211
+ Dict mapping source_id -> quality score (0.0 to 1.0).
212
+ """
213
+ conn = self._connect()
214
+ try:
215
+ rows = conn.execute(
216
+ "SELECT source_id, alpha, beta FROM source_quality "
217
+ "WHERE profile_id = ?",
218
+ (profile_id,),
219
+ ).fetchall()
220
+
221
+ result: Dict[str, float] = {}
222
+ for r in rows:
223
+ alpha = float(r["alpha"])
224
+ beta = float(r["beta"])
225
+ denom = alpha + beta
226
+ score = alpha / denom if denom > 0 else DEFAULT_QUALITY
227
+ result[r["source_id"]] = score
228
+ return result
229
+ finally:
230
+ conn.close()
231
+
232
+ # ------------------------------------------------------------------
233
+ # Public API: diagnostics
234
+ # ------------------------------------------------------------------
235
+
236
+ def get_detailed(
237
+ self, profile_id: str, source_id: str,
238
+ ) -> Dict[str, Any]:
239
+ """
240
+ Get detailed quality information for a single source.
241
+
242
+ Returns:
243
+ Dict with alpha, beta, quality, updated_at.
244
+ Returns defaults if the source has not been observed.
245
+ """
246
+ conn = self._connect()
247
+ try:
248
+ row = conn.execute(
249
+ "SELECT alpha, beta, updated_at FROM source_quality "
250
+ "WHERE profile_id = ? AND source_id = ?",
251
+ (profile_id, source_id),
252
+ ).fetchone()
253
+
254
+ if row is None:
255
+ return {
256
+ "alpha": _ALPHA,
257
+ "beta": _BETA,
258
+ "quality": DEFAULT_QUALITY,
259
+ "updated_at": None,
260
+ }
261
+
262
+ alpha = float(row["alpha"])
263
+ beta = float(row["beta"])
264
+ denom = alpha + beta
265
+ return {
266
+ "alpha": alpha,
267
+ "beta": beta,
268
+ "quality": alpha / denom if denom > 0 else DEFAULT_QUALITY,
269
+ "updated_at": row["updated_at"],
270
+ }
271
+ finally:
272
+ conn.close()
273
+
274
+ def get_all_detailed(self, profile_id: str) -> Dict[str, Dict[str, Any]]:
275
+ """
276
+ Get detailed quality data for all sources under a profile.
277
+
278
+ Returns:
279
+ Dict mapping source_id -> detail dict.
280
+ """
281
+ conn = self._connect()
282
+ try:
283
+ rows = conn.execute(
284
+ "SELECT source_id, alpha, beta, updated_at "
285
+ "FROM source_quality WHERE profile_id = ? "
286
+ "ORDER BY (alpha / (alpha + beta)) DESC",
287
+ (profile_id,),
288
+ ).fetchall()
289
+
290
+ result: Dict[str, Dict[str, Any]] = {}
291
+ for r in rows:
292
+ alpha = float(r["alpha"])
293
+ beta = float(r["beta"])
294
+ denom = alpha + beta
295
+ result[r["source_id"]] = {
296
+ "alpha": alpha,
297
+ "beta": beta,
298
+ "quality": alpha / denom if denom > 0 else DEFAULT_QUALITY,
299
+ "updated_at": r["updated_at"],
300
+ }
301
+ return result
302
+ finally:
303
+ conn.close()
@@ -0,0 +1,309 @@
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
+ """Workflow pattern miner -- sliding-window sequence and temporal mining.
6
+
7
+ Detects repeating workflow sequences and time-of-day activity patterns
8
+ from memory creation timestamps and content. Uses n-gram sliding
9
+ windows (length 2-5) over a classified activity stream.
10
+
11
+ Seven activity types: docs, architecture, code, test, debug, deploy, config.
12
+
13
+ Ported from V2 WorkflowPatternMiner with V2 LearningDB deps removed.
14
+ Direct sqlite3 for storage.
15
+
16
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import json
22
+ import logging
23
+ import re
24
+ import sqlite3
25
+ from collections import Counter
26
+ from datetime import UTC, datetime
27
+ from pathlib import Path
28
+ from typing import Any, Optional
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ # ---------------------------------------------------------------------------
33
+ # Activity taxonomy (7 categories)
34
+ # ---------------------------------------------------------------------------
35
+
36
+ ACTIVITY_TYPES: dict[str, list[str]] = {
37
+ "docs": [
38
+ "documentation", "readme", "wiki", "spec", "prd",
39
+ "design doc", "changelog", "api doc",
40
+ ],
41
+ "architecture": [
42
+ "architecture", "diagram", "system design", "schema",
43
+ "api design", "data model", "erd",
44
+ ],
45
+ "code": [
46
+ "implement", "function", "class", "module", "refactor",
47
+ "code", "feature", "component",
48
+ ],
49
+ "test": [
50
+ "test", "pytest", "jest", "coverage", "assertion",
51
+ "mock", "spec", "unit test",
52
+ ],
53
+ "debug": [
54
+ "bug", "fix", "error", "stack trace", "debug",
55
+ "issue", "exception", "traceback",
56
+ ],
57
+ "deploy": [
58
+ "deploy", "docker", "ci/cd", "pipeline", "release",
59
+ "production", "staging", "build",
60
+ ],
61
+ "config": [
62
+ "config", "env", "settings", "setup", "install",
63
+ "dependency", "package", "requirements",
64
+ ],
65
+ }
66
+
67
+ # Pre-compiled regex per keyword for word-boundary matching
68
+ _KEYWORD_PATTERNS: list[tuple[str, re.Pattern]] = []
69
+ for _act, _kws in ACTIVITY_TYPES.items():
70
+ for _kw in _kws:
71
+ _KEYWORD_PATTERNS.append(
72
+ (_act, re.compile(r"\b" + re.escape(_kw) + r"\b", re.IGNORECASE))
73
+ )
74
+
75
+ # ---------------------------------------------------------------------------
76
+ # Schema for local action log
77
+ # ---------------------------------------------------------------------------
78
+
79
+ _SCHEMA = """
80
+ CREATE TABLE IF NOT EXISTS workflow_actions (
81
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
82
+ profile_id TEXT NOT NULL,
83
+ action TEXT NOT NULL,
84
+ metadata TEXT DEFAULT '{}',
85
+ created_at TEXT NOT NULL
86
+ );
87
+ CREATE INDEX IF NOT EXISTS idx_wf_profile
88
+ ON workflow_actions(profile_id, created_at);
89
+
90
+ CREATE TABLE IF NOT EXISTS workflow_patterns (
91
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
92
+ profile_id TEXT NOT NULL,
93
+ pattern_type TEXT NOT NULL,
94
+ pattern_key TEXT NOT NULL,
95
+ pattern_value TEXT DEFAULT '{}',
96
+ confidence REAL DEFAULT 0.0,
97
+ evidence_count INTEGER DEFAULT 0,
98
+ created_at TEXT NOT NULL
99
+ );
100
+ CREATE INDEX IF NOT EXISTS idx_wp_profile
101
+ ON workflow_patterns(profile_id, pattern_type);
102
+ """
103
+
104
+
105
+ class WorkflowMiner:
106
+ """Mine workflow sequences and temporal patterns from memory content.
107
+
108
+ Args:
109
+ db_path: Path to a sqlite database. If the file does not exist
110
+ it is created with the required schema.
111
+ """
112
+
113
+ def __init__(self, db_path: Path | str) -> None:
114
+ self._db_path = Path(db_path)
115
+ self._ensure_schema()
116
+
117
+ # ------------------------------------------------------------------
118
+ # Public API
119
+ # ------------------------------------------------------------------
120
+
121
+ def record_action(
122
+ self, profile_id: str, action: str, metadata: dict[str, Any] | None = None
123
+ ) -> None:
124
+ """Record a user action for later mining."""
125
+ now = datetime.now(UTC).isoformat()
126
+ meta_json = json.dumps(metadata or {})
127
+ conn = sqlite3.connect(str(self._db_path))
128
+ try:
129
+ conn.execute(
130
+ "INSERT INTO workflow_actions (profile_id, action, metadata, created_at) "
131
+ "VALUES (?, ?, ?, ?)",
132
+ (profile_id, action, meta_json, now),
133
+ )
134
+ conn.commit()
135
+ finally:
136
+ conn.close()
137
+
138
+ def mine(self, profile_id: str, min_support: float = 0.3) -> list[dict]:
139
+ """Mine workflow sequence patterns for a profile.
140
+
141
+ Returns a list of pattern dicts sorted by support descending.
142
+ """
143
+ actions = self._fetch_actions(profile_id)
144
+ if len(actions) < 2:
145
+ return []
146
+
147
+ activity_stream = [a["action"] for a in actions]
148
+ return self._mine_sequences(activity_stream, min_support)
149
+
150
+ def mine_from_memories(
151
+ self,
152
+ memories: list[dict],
153
+ min_support: float = 0.3,
154
+ ) -> list[dict]:
155
+ """Mine sequences from a pre-fetched list of memory dicts.
156
+
157
+ Each dict should have a ``content`` key.
158
+ """
159
+ stream: list[str] = []
160
+ for mem in memories:
161
+ activity = classify_activity(mem.get("content", ""))
162
+ if activity != "unknown":
163
+ stream.append(activity)
164
+ if len(stream) < 2:
165
+ return []
166
+ return self._mine_sequences(stream, min_support)
167
+
168
+ def mine_temporal(self, memories: list[dict]) -> dict[str, dict]:
169
+ """Detect time-of-day activity preferences.
170
+
171
+ Returns dict keyed by bucket (morning/afternoon/evening/night).
172
+ Buckets with < 5 evidence memories are omitted.
173
+ """
174
+ buckets: dict[str, Counter] = {
175
+ "morning": Counter(),
176
+ "afternoon": Counter(),
177
+ "evening": Counter(),
178
+ "night": Counter(),
179
+ }
180
+
181
+ for mem in memories:
182
+ activity = classify_activity(mem.get("content", ""))
183
+ if activity == "unknown":
184
+ continue
185
+ hour = _parse_hour(mem.get("created_at"))
186
+ if hour is None:
187
+ continue
188
+ bucket = _hour_to_bucket(hour)
189
+ buckets[bucket][activity] += 1
190
+
191
+ result: dict[str, dict] = {}
192
+ for bucket_name, counter in buckets.items():
193
+ total = sum(counter.values())
194
+ if total < 5:
195
+ continue
196
+ dominant, dom_count = counter.most_common(1)[0]
197
+ result[bucket_name] = {
198
+ "dominant_activity": dominant,
199
+ "confidence": round(dom_count / total, 4),
200
+ "evidence_count": total,
201
+ "distribution": dict(counter),
202
+ }
203
+ return result
204
+
205
+ # ------------------------------------------------------------------
206
+ # Internals
207
+ # ------------------------------------------------------------------
208
+
209
+ def _ensure_schema(self) -> None:
210
+ conn = sqlite3.connect(str(self._db_path))
211
+ try:
212
+ conn.executescript(_SCHEMA)
213
+ finally:
214
+ conn.close()
215
+
216
+ def _fetch_actions(self, profile_id: str) -> list[dict]:
217
+ conn = sqlite3.connect(str(self._db_path))
218
+ conn.row_factory = sqlite3.Row
219
+ try:
220
+ cur = conn.execute(
221
+ "SELECT action, created_at FROM workflow_actions "
222
+ "WHERE profile_id = ? ORDER BY created_at ASC LIMIT 500",
223
+ (profile_id,),
224
+ )
225
+ return [dict(r) for r in cur.fetchall()]
226
+ except sqlite3.OperationalError:
227
+ return []
228
+ finally:
229
+ conn.close()
230
+
231
+ @staticmethod
232
+ def _mine_sequences(
233
+ activity_stream: list[str], min_support: float
234
+ ) -> list[dict]:
235
+ """Extract n-gram sequences and filter by support."""
236
+ all_patterns: list[dict] = []
237
+
238
+ for n in range(2, 6):
239
+ if len(activity_stream) < n:
240
+ continue
241
+ ngram_counts: Counter = Counter()
242
+ total_windows = len(activity_stream) - n + 1
243
+
244
+ for i in range(total_windows):
245
+ ngram = tuple(activity_stream[i : i + n])
246
+ # Skip consecutive identical activities (noise)
247
+ if any(ngram[j] == ngram[j + 1] for j in range(len(ngram) - 1)):
248
+ continue
249
+ ngram_counts[ngram] += 1
250
+
251
+ for ngram, count in ngram_counts.items():
252
+ support = count / total_windows if total_windows > 0 else 0.0
253
+ if support >= min_support:
254
+ all_patterns.append({
255
+ "sequence": list(ngram),
256
+ "support": round(support, 4),
257
+ "count": count,
258
+ "length": n,
259
+ })
260
+
261
+ all_patterns.sort(key=lambda p: (-p["support"], -p["length"]))
262
+ return all_patterns[:20]
263
+
264
+
265
+ # ----------------------------------------------------------------------
266
+ # Module-level helpers
267
+ # ----------------------------------------------------------------------
268
+
269
+
270
+ def classify_activity(content: str) -> str:
271
+ """Classify content into one of 7 activity types or 'unknown'."""
272
+ if not content:
273
+ return "unknown"
274
+ scores: Counter = Counter()
275
+ for act_type, pattern in _KEYWORD_PATTERNS:
276
+ if pattern.search(content):
277
+ scores[act_type] += 1
278
+ if not scores:
279
+ return "unknown"
280
+ return scores.most_common(1)[0][0]
281
+
282
+
283
+ def _hour_to_bucket(hour: int) -> str:
284
+ if 6 <= hour <= 11:
285
+ return "morning"
286
+ if 12 <= hour <= 17:
287
+ return "afternoon"
288
+ if 18 <= hour <= 23:
289
+ return "evening"
290
+ return "night"
291
+
292
+
293
+ def _parse_hour(timestamp: str | None) -> int | None:
294
+ if not timestamp:
295
+ return None
296
+ for fmt in (
297
+ "%Y-%m-%dT%H:%M:%S",
298
+ "%Y-%m-%d %H:%M:%S",
299
+ "%Y-%m-%dT%H:%M:%S.%f",
300
+ "%Y-%m-%d %H:%M:%S.%f",
301
+ ):
302
+ try:
303
+ return datetime.strptime(timestamp, fmt).hour
304
+ except (ValueError, TypeError):
305
+ continue
306
+ try:
307
+ return datetime.fromisoformat(timestamp).hour
308
+ except (ValueError, TypeError):
309
+ return None
File without changes