superlocalmemory 2.8.6 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (431) hide show
  1. package/LICENSE +9 -1
  2. package/NOTICE +63 -0
  3. package/README.md +165 -480
  4. package/bin/slm +17 -449
  5. package/bin/slm-npm +1 -1
  6. package/conftest.py +5 -0
  7. package/docs/api-reference.md +284 -0
  8. package/docs/architecture.md +149 -0
  9. package/docs/auto-memory.md +150 -0
  10. package/docs/cli-reference.md +276 -0
  11. package/docs/compliance.md +191 -0
  12. package/docs/configuration.md +182 -0
  13. package/docs/getting-started.md +102 -0
  14. package/docs/ide-setup.md +261 -0
  15. package/docs/mcp-tools.md +220 -0
  16. package/docs/migration-from-v2.md +170 -0
  17. package/docs/profiles.md +173 -0
  18. package/docs/troubleshooting.md +310 -0
  19. package/{configs → ide/configs}/antigravity-mcp.json +3 -3
  20. package/ide/configs/chatgpt-desktop-mcp.json +16 -0
  21. package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
  22. package/{configs → ide/configs}/codex-mcp.toml +4 -4
  23. package/{configs → ide/configs}/continue-mcp.yaml +4 -3
  24. package/{configs → ide/configs}/continue-skills.yaml +6 -6
  25. package/ide/configs/cursor-mcp.json +15 -0
  26. package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
  27. package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
  28. package/{configs → ide/configs}/opencode-mcp.json +2 -2
  29. package/{configs → ide/configs}/perplexity-mcp.json +2 -2
  30. package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
  31. package/{configs → ide/configs}/windsurf-mcp.json +3 -3
  32. package/{configs → ide/configs}/zed-mcp.json +2 -2
  33. package/{hooks → ide/hooks}/context-hook.js +9 -20
  34. package/ide/hooks/memory-list-skill.js +70 -0
  35. package/ide/hooks/memory-profile-skill.js +101 -0
  36. package/ide/hooks/memory-recall-skill.js +62 -0
  37. package/ide/hooks/memory-remember-skill.js +68 -0
  38. package/ide/hooks/memory-reset-skill.js +160 -0
  39. package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
  40. package/ide/integrations/langchain/README.md +106 -0
  41. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
  42. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
  43. package/ide/integrations/langchain/pyproject.toml +38 -0
  44. package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
  45. package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
  46. package/ide/integrations/langchain/tests/test_security.py +117 -0
  47. package/ide/integrations/llamaindex/README.md +81 -0
  48. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
  49. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
  50. package/ide/integrations/llamaindex/pyproject.toml +43 -0
  51. package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
  52. package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
  53. package/ide/integrations/llamaindex/tests/test_security.py +241 -0
  54. package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
  55. package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
  56. package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
  57. package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
  58. package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
  59. package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
  60. package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
  61. package/package.json +13 -22
  62. package/pyproject.toml +85 -0
  63. package/scripts/build-dmg.sh +417 -0
  64. package/scripts/install-skills.ps1 +334 -0
  65. package/scripts/postinstall.js +2 -2
  66. package/scripts/start-dashboard.ps1 +52 -0
  67. package/scripts/start-dashboard.sh +41 -0
  68. package/scripts/sync-wiki.ps1 +127 -0
  69. package/scripts/sync-wiki.sh +82 -0
  70. package/scripts/test-dmg.sh +161 -0
  71. package/scripts/test-npm-package.ps1 +252 -0
  72. package/scripts/test-npm-package.sh +207 -0
  73. package/scripts/verify-install.ps1 +294 -0
  74. package/scripts/verify-install.sh +266 -0
  75. package/src/superlocalmemory/__init__.py +0 -0
  76. package/src/superlocalmemory/attribution/__init__.py +9 -0
  77. package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
  78. package/src/superlocalmemory/attribution/signer.py +153 -0
  79. package/src/superlocalmemory/attribution/watermark.py +189 -0
  80. package/src/superlocalmemory/cli/__init__.py +5 -0
  81. package/src/superlocalmemory/cli/commands.py +245 -0
  82. package/src/superlocalmemory/cli/main.py +89 -0
  83. package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
  84. package/src/superlocalmemory/cli/post_install.py +99 -0
  85. package/src/superlocalmemory/cli/setup_wizard.py +129 -0
  86. package/src/superlocalmemory/compliance/__init__.py +0 -0
  87. package/src/superlocalmemory/compliance/abac.py +204 -0
  88. package/src/superlocalmemory/compliance/audit.py +314 -0
  89. package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
  90. package/src/superlocalmemory/compliance/gdpr.py +294 -0
  91. package/src/superlocalmemory/compliance/lifecycle.py +158 -0
  92. package/src/superlocalmemory/compliance/retention.py +232 -0
  93. package/src/superlocalmemory/compliance/scheduler.py +148 -0
  94. package/src/superlocalmemory/core/__init__.py +0 -0
  95. package/src/superlocalmemory/core/config.py +391 -0
  96. package/src/superlocalmemory/core/embeddings.py +293 -0
  97. package/src/superlocalmemory/core/engine.py +701 -0
  98. package/src/superlocalmemory/core/hooks.py +65 -0
  99. package/src/superlocalmemory/core/maintenance.py +172 -0
  100. package/src/superlocalmemory/core/modes.py +140 -0
  101. package/src/superlocalmemory/core/profiles.py +234 -0
  102. package/src/superlocalmemory/core/registry.py +117 -0
  103. package/src/superlocalmemory/dynamics/__init__.py +0 -0
  104. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
  105. package/src/superlocalmemory/encoding/__init__.py +0 -0
  106. package/src/superlocalmemory/encoding/consolidator.py +485 -0
  107. package/src/superlocalmemory/encoding/emotional.py +125 -0
  108. package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
  109. package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
  110. package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
  111. package/src/superlocalmemory/encoding/foresight.py +91 -0
  112. package/src/superlocalmemory/encoding/graph_builder.py +302 -0
  113. package/src/superlocalmemory/encoding/observation_builder.py +160 -0
  114. package/src/superlocalmemory/encoding/scene_builder.py +183 -0
  115. package/src/superlocalmemory/encoding/signal_inference.py +90 -0
  116. package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
  117. package/src/superlocalmemory/encoding/type_router.py +235 -0
  118. package/src/superlocalmemory/hooks/__init__.py +3 -0
  119. package/src/superlocalmemory/hooks/auto_capture.py +111 -0
  120. package/src/superlocalmemory/hooks/auto_recall.py +93 -0
  121. package/src/superlocalmemory/hooks/ide_connector.py +204 -0
  122. package/src/superlocalmemory/hooks/rules_engine.py +99 -0
  123. package/src/superlocalmemory/infra/__init__.py +3 -0
  124. package/src/superlocalmemory/infra/auth_middleware.py +82 -0
  125. package/src/superlocalmemory/infra/backup.py +317 -0
  126. package/src/superlocalmemory/infra/cache_manager.py +267 -0
  127. package/src/superlocalmemory/infra/event_bus.py +381 -0
  128. package/src/superlocalmemory/infra/rate_limiter.py +135 -0
  129. package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
  130. package/src/superlocalmemory/learning/__init__.py +0 -0
  131. package/src/superlocalmemory/learning/adaptive.py +172 -0
  132. package/src/superlocalmemory/learning/behavioral.py +490 -0
  133. package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
  134. package/src/superlocalmemory/learning/bootstrap.py +298 -0
  135. package/src/superlocalmemory/learning/cross_project.py +399 -0
  136. package/src/superlocalmemory/learning/database.py +376 -0
  137. package/src/superlocalmemory/learning/engagement.py +323 -0
  138. package/src/superlocalmemory/learning/features.py +138 -0
  139. package/src/superlocalmemory/learning/feedback.py +316 -0
  140. package/src/superlocalmemory/learning/outcomes.py +255 -0
  141. package/src/superlocalmemory/learning/project_context.py +366 -0
  142. package/src/superlocalmemory/learning/ranker.py +155 -0
  143. package/src/superlocalmemory/learning/source_quality.py +303 -0
  144. package/src/superlocalmemory/learning/workflows.py +309 -0
  145. package/src/superlocalmemory/llm/__init__.py +0 -0
  146. package/src/superlocalmemory/llm/backbone.py +316 -0
  147. package/src/superlocalmemory/math/__init__.py +0 -0
  148. package/src/superlocalmemory/math/fisher.py +356 -0
  149. package/src/superlocalmemory/math/langevin.py +398 -0
  150. package/src/superlocalmemory/math/sheaf.py +257 -0
  151. package/src/superlocalmemory/mcp/__init__.py +0 -0
  152. package/src/superlocalmemory/mcp/resources.py +245 -0
  153. package/src/superlocalmemory/mcp/server.py +61 -0
  154. package/src/superlocalmemory/mcp/tools.py +18 -0
  155. package/src/superlocalmemory/mcp/tools_core.py +305 -0
  156. package/src/superlocalmemory/mcp/tools_v28.py +223 -0
  157. package/src/superlocalmemory/mcp/tools_v3.py +286 -0
  158. package/src/superlocalmemory/retrieval/__init__.py +0 -0
  159. package/src/superlocalmemory/retrieval/agentic.py +295 -0
  160. package/src/superlocalmemory/retrieval/ann_index.py +223 -0
  161. package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
  162. package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
  163. package/src/superlocalmemory/retrieval/engine.py +390 -0
  164. package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
  165. package/src/superlocalmemory/retrieval/fusion.py +78 -0
  166. package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
  167. package/src/superlocalmemory/retrieval/reranker.py +154 -0
  168. package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
  169. package/src/superlocalmemory/retrieval/strategy.py +96 -0
  170. package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
  171. package/src/superlocalmemory/server/__init__.py +1 -0
  172. package/src/superlocalmemory/server/api.py +248 -0
  173. package/src/superlocalmemory/server/routes/__init__.py +4 -0
  174. package/src/superlocalmemory/server/routes/agents.py +107 -0
  175. package/src/superlocalmemory/server/routes/backup.py +91 -0
  176. package/src/superlocalmemory/server/routes/behavioral.py +127 -0
  177. package/src/superlocalmemory/server/routes/compliance.py +160 -0
  178. package/src/superlocalmemory/server/routes/data_io.py +188 -0
  179. package/src/superlocalmemory/server/routes/events.py +183 -0
  180. package/src/superlocalmemory/server/routes/helpers.py +85 -0
  181. package/src/superlocalmemory/server/routes/learning.py +273 -0
  182. package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
  183. package/src/superlocalmemory/server/routes/memories.py +399 -0
  184. package/src/superlocalmemory/server/routes/profiles.py +219 -0
  185. package/src/superlocalmemory/server/routes/stats.py +346 -0
  186. package/src/superlocalmemory/server/routes/v3_api.py +365 -0
  187. package/src/superlocalmemory/server/routes/ws.py +82 -0
  188. package/src/superlocalmemory/server/security_middleware.py +57 -0
  189. package/src/superlocalmemory/server/ui.py +245 -0
  190. package/src/superlocalmemory/storage/__init__.py +0 -0
  191. package/src/superlocalmemory/storage/access_control.py +182 -0
  192. package/src/superlocalmemory/storage/database.py +594 -0
  193. package/src/superlocalmemory/storage/migrations.py +303 -0
  194. package/src/superlocalmemory/storage/models.py +406 -0
  195. package/src/superlocalmemory/storage/schema.py +726 -0
  196. package/src/superlocalmemory/storage/v2_migrator.py +317 -0
  197. package/src/superlocalmemory/trust/__init__.py +0 -0
  198. package/src/superlocalmemory/trust/gate.py +130 -0
  199. package/src/superlocalmemory/trust/provenance.py +124 -0
  200. package/src/superlocalmemory/trust/scorer.py +347 -0
  201. package/src/superlocalmemory/trust/signals.py +153 -0
  202. package/ui/index.html +278 -5
  203. package/ui/js/auto-settings.js +70 -0
  204. package/ui/js/dashboard.js +90 -0
  205. package/ui/js/fact-detail.js +92 -0
  206. package/ui/js/feedback.js +2 -2
  207. package/ui/js/ide-status.js +102 -0
  208. package/ui/js/math-health.js +98 -0
  209. package/ui/js/recall-lab.js +127 -0
  210. package/ui/js/settings.js +2 -2
  211. package/ui/js/trust-dashboard.js +73 -0
  212. package/api_server.py +0 -724
  213. package/bin/aider-smart +0 -72
  214. package/bin/superlocalmemoryv2-learning +0 -4
  215. package/bin/superlocalmemoryv2-list +0 -3
  216. package/bin/superlocalmemoryv2-patterns +0 -4
  217. package/bin/superlocalmemoryv2-profile +0 -3
  218. package/bin/superlocalmemoryv2-recall +0 -3
  219. package/bin/superlocalmemoryv2-remember +0 -3
  220. package/bin/superlocalmemoryv2-reset +0 -3
  221. package/bin/superlocalmemoryv2-status +0 -3
  222. package/configs/chatgpt-desktop-mcp.json +0 -16
  223. package/configs/cursor-mcp.json +0 -15
  224. package/hooks/memory-list-skill.js +0 -139
  225. package/hooks/memory-profile-skill.js +0 -273
  226. package/hooks/memory-recall-skill.js +0 -114
  227. package/hooks/memory-remember-skill.js +0 -127
  228. package/hooks/memory-reset-skill.js +0 -274
  229. package/mcp_server.py +0 -1808
  230. package/requirements-core.txt +0 -22
  231. package/requirements-learning.txt +0 -12
  232. package/requirements.txt +0 -12
  233. package/src/agent_registry.py +0 -411
  234. package/src/auth_middleware.py +0 -61
  235. package/src/auto_backup.py +0 -459
  236. package/src/behavioral/__init__.py +0 -49
  237. package/src/behavioral/behavioral_listener.py +0 -203
  238. package/src/behavioral/behavioral_patterns.py +0 -275
  239. package/src/behavioral/cross_project_transfer.py +0 -206
  240. package/src/behavioral/outcome_inference.py +0 -194
  241. package/src/behavioral/outcome_tracker.py +0 -193
  242. package/src/behavioral/tests/__init__.py +0 -4
  243. package/src/behavioral/tests/test_behavioral_integration.py +0 -108
  244. package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
  245. package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
  246. package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
  247. package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
  248. package/src/behavioral/tests/test_outcome_inference.py +0 -107
  249. package/src/behavioral/tests/test_outcome_tracker.py +0 -96
  250. package/src/cache_manager.py +0 -518
  251. package/src/compliance/__init__.py +0 -48
  252. package/src/compliance/abac_engine.py +0 -149
  253. package/src/compliance/abac_middleware.py +0 -116
  254. package/src/compliance/audit_db.py +0 -215
  255. package/src/compliance/audit_logger.py +0 -148
  256. package/src/compliance/retention_manager.py +0 -289
  257. package/src/compliance/retention_scheduler.py +0 -186
  258. package/src/compliance/tests/__init__.py +0 -4
  259. package/src/compliance/tests/test_abac_enforcement.py +0 -95
  260. package/src/compliance/tests/test_abac_engine.py +0 -124
  261. package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
  262. package/src/compliance/tests/test_audit_db.py +0 -123
  263. package/src/compliance/tests/test_audit_logger.py +0 -98
  264. package/src/compliance/tests/test_mcp_audit.py +0 -128
  265. package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
  266. package/src/compliance/tests/test_retention_manager.py +0 -131
  267. package/src/compliance/tests/test_retention_scheduler.py +0 -99
  268. package/src/compression/__init__.py +0 -25
  269. package/src/compression/cli.py +0 -150
  270. package/src/compression/cold_storage.py +0 -217
  271. package/src/compression/config.py +0 -72
  272. package/src/compression/orchestrator.py +0 -133
  273. package/src/compression/tier2_compressor.py +0 -228
  274. package/src/compression/tier3_compressor.py +0 -153
  275. package/src/compression/tier_classifier.py +0 -148
  276. package/src/db_connection_manager.py +0 -536
  277. package/src/embedding_engine.py +0 -63
  278. package/src/embeddings/__init__.py +0 -47
  279. package/src/embeddings/cache.py +0 -70
  280. package/src/embeddings/cli.py +0 -113
  281. package/src/embeddings/constants.py +0 -47
  282. package/src/embeddings/database.py +0 -91
  283. package/src/embeddings/engine.py +0 -247
  284. package/src/embeddings/model_loader.py +0 -145
  285. package/src/event_bus.py +0 -562
  286. package/src/graph/__init__.py +0 -36
  287. package/src/graph/build_helpers.py +0 -74
  288. package/src/graph/cli.py +0 -87
  289. package/src/graph/cluster_builder.py +0 -188
  290. package/src/graph/cluster_summary.py +0 -148
  291. package/src/graph/constants.py +0 -47
  292. package/src/graph/edge_builder.py +0 -162
  293. package/src/graph/entity_extractor.py +0 -95
  294. package/src/graph/graph_core.py +0 -226
  295. package/src/graph/graph_search.py +0 -231
  296. package/src/graph/hierarchical.py +0 -207
  297. package/src/graph/schema.py +0 -99
  298. package/src/graph_engine.py +0 -52
  299. package/src/hnsw_index.py +0 -628
  300. package/src/hybrid_search.py +0 -46
  301. package/src/learning/__init__.py +0 -217
  302. package/src/learning/adaptive_ranker.py +0 -682
  303. package/src/learning/bootstrap/__init__.py +0 -69
  304. package/src/learning/bootstrap/constants.py +0 -93
  305. package/src/learning/bootstrap/db_queries.py +0 -316
  306. package/src/learning/bootstrap/sampling.py +0 -82
  307. package/src/learning/bootstrap/text_utils.py +0 -71
  308. package/src/learning/cross_project_aggregator.py +0 -857
  309. package/src/learning/db/__init__.py +0 -40
  310. package/src/learning/db/constants.py +0 -44
  311. package/src/learning/db/schema.py +0 -279
  312. package/src/learning/engagement_tracker.py +0 -628
  313. package/src/learning/feature_extractor.py +0 -708
  314. package/src/learning/feedback_collector.py +0 -806
  315. package/src/learning/learning_db.py +0 -915
  316. package/src/learning/project_context_manager.py +0 -572
  317. package/src/learning/ranking/__init__.py +0 -33
  318. package/src/learning/ranking/constants.py +0 -84
  319. package/src/learning/ranking/helpers.py +0 -278
  320. package/src/learning/source_quality_scorer.py +0 -676
  321. package/src/learning/synthetic_bootstrap.py +0 -755
  322. package/src/learning/tests/test_adaptive_ranker.py +0 -325
  323. package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
  324. package/src/learning/tests/test_aggregator.py +0 -306
  325. package/src/learning/tests/test_auto_retrain_v28.py +0 -35
  326. package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
  327. package/src/learning/tests/test_feature_extractor_v28.py +0 -93
  328. package/src/learning/tests/test_feedback_collector.py +0 -294
  329. package/src/learning/tests/test_learning_db.py +0 -602
  330. package/src/learning/tests/test_learning_db_v28.py +0 -110
  331. package/src/learning/tests/test_learning_init_v28.py +0 -48
  332. package/src/learning/tests/test_outcome_signals.py +0 -48
  333. package/src/learning/tests/test_project_context.py +0 -292
  334. package/src/learning/tests/test_schema_migration.py +0 -319
  335. package/src/learning/tests/test_signal_inference.py +0 -397
  336. package/src/learning/tests/test_source_quality.py +0 -351
  337. package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
  338. package/src/learning/tests/test_workflow_miner.py +0 -318
  339. package/src/learning/workflow_pattern_miner.py +0 -655
  340. package/src/lifecycle/__init__.py +0 -54
  341. package/src/lifecycle/bounded_growth.py +0 -239
  342. package/src/lifecycle/compaction_engine.py +0 -226
  343. package/src/lifecycle/lifecycle_engine.py +0 -355
  344. package/src/lifecycle/lifecycle_evaluator.py +0 -257
  345. package/src/lifecycle/lifecycle_scheduler.py +0 -130
  346. package/src/lifecycle/retention_policy.py +0 -285
  347. package/src/lifecycle/tests/test_bounded_growth.py +0 -193
  348. package/src/lifecycle/tests/test_compaction.py +0 -179
  349. package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
  350. package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
  351. package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
  352. package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
  353. package/src/lifecycle/tests/test_mcp_compact.py +0 -149
  354. package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
  355. package/src/lifecycle/tests/test_retention_policy.py +0 -162
  356. package/src/mcp_tools_v28.py +0 -281
  357. package/src/memory/__init__.py +0 -36
  358. package/src/memory/cli.py +0 -205
  359. package/src/memory/constants.py +0 -39
  360. package/src/memory/helpers.py +0 -28
  361. package/src/memory/schema.py +0 -166
  362. package/src/memory-profiles.py +0 -595
  363. package/src/memory-reset.py +0 -491
  364. package/src/memory_compression.py +0 -989
  365. package/src/memory_store_v2.py +0 -1155
  366. package/src/migrate_v1_to_v2.py +0 -629
  367. package/src/pattern_learner.py +0 -34
  368. package/src/patterns/__init__.py +0 -24
  369. package/src/patterns/analyzers.py +0 -251
  370. package/src/patterns/learner.py +0 -271
  371. package/src/patterns/scoring.py +0 -171
  372. package/src/patterns/store.py +0 -225
  373. package/src/patterns/terminology.py +0 -140
  374. package/src/provenance_tracker.py +0 -312
  375. package/src/qualixar_attribution.py +0 -139
  376. package/src/qualixar_watermark.py +0 -78
  377. package/src/query_optimizer.py +0 -511
  378. package/src/rate_limiter.py +0 -83
  379. package/src/search/__init__.py +0 -20
  380. package/src/search/cli.py +0 -77
  381. package/src/search/constants.py +0 -26
  382. package/src/search/engine.py +0 -241
  383. package/src/search/fusion.py +0 -122
  384. package/src/search/index_loader.py +0 -114
  385. package/src/search/methods.py +0 -162
  386. package/src/search_engine_v2.py +0 -401
  387. package/src/setup_validator.py +0 -482
  388. package/src/subscription_manager.py +0 -391
  389. package/src/tree/__init__.py +0 -59
  390. package/src/tree/builder.py +0 -185
  391. package/src/tree/nodes.py +0 -202
  392. package/src/tree/queries.py +0 -257
  393. package/src/tree/schema.py +0 -80
  394. package/src/tree_manager.py +0 -19
  395. package/src/trust/__init__.py +0 -45
  396. package/src/trust/constants.py +0 -66
  397. package/src/trust/queries.py +0 -157
  398. package/src/trust/schema.py +0 -95
  399. package/src/trust/scorer.py +0 -299
  400. package/src/trust/signals.py +0 -95
  401. package/src/trust_scorer.py +0 -44
  402. package/ui/app.js +0 -1588
  403. package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
  404. package/ui/js/graph-cytoscape.js +0 -1168
  405. package/ui/js/graph-d3-backup.js +0 -32
  406. package/ui/js/graph.js +0 -32
  407. package/ui_server.py +0 -286
  408. /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
  409. /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
  410. /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
  411. /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
  412. /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
  413. /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
  414. /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
  415. /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
  416. /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
  417. /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
  418. /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
  419. /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
  420. /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
  421. /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
  422. /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
  423. /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
  424. /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
  425. /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
  426. /package/{completions → ide/completions}/slm.bash +0 -0
  427. /package/{completions → ide/completions}/slm.zsh +0 -0
  428. /package/{configs → ide/configs}/cody-commands.json +0 -0
  429. /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
  430. /package/{install.ps1 → scripts/install.ps1} +0 -0
  431. /package/{install.sh → scripts/install.sh} +0 -0
@@ -0,0 +1,232 @@
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 — Semantic Retrieval Channel.
6
+
7
+ Fisher-aware semantic search: uses Fisher-Rao geodesic distance when
8
+ variance data is available, falls back to cosine similarity otherwise.
9
+
10
+ The Fisher distance is meaningful when memories accumulate evidence
11
+ (repeated confirmation narrows variance). For fresh benchmark data
12
+ where all variances are identical, Fisher distance degenerates to a
13
+ monotonic transform of Euclidean distance — same ranking as cosine.
14
+
15
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
16
+ License: MIT
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ import math
23
+ from typing import TYPE_CHECKING
24
+
25
+ import numpy as np
26
+
27
+ if TYPE_CHECKING:
28
+ from superlocalmemory.storage.database import DatabaseManager
29
+ from superlocalmemory.storage.models import AtomicFact
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+ # Minimum variance floor to prevent division-by-zero in Fisher distance
34
+ _VARIANCE_FLOOR: float = 1e-6
35
+
36
+
37
+ def _cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
38
+ """Cosine similarity in [-1, 1]. Returns 0.0 on zero vectors."""
39
+ norm_a = np.linalg.norm(a)
40
+ norm_b = np.linalg.norm(b)
41
+ if norm_a < _VARIANCE_FLOOR or norm_b < _VARIANCE_FLOOR:
42
+ return 0.0
43
+ return float(np.dot(a, b) / (norm_a * norm_b))
44
+
45
+
46
+ def _fisher_rao_similarity(
47
+ mu_q: np.ndarray,
48
+ mu_f: np.ndarray,
49
+ var_f: np.ndarray,
50
+ temperature: float = 15.0,
51
+ ) -> float:
52
+ """Fisher-Rao geodesic similarity on diagonal Gaussian manifold.
53
+
54
+ d_FR^2 = sum_i [ (mu_q_i - mu_f_i)^2 / max(var_f_i, eps) ]
55
+ similarity = exp(-d_FR^2 / temperature)
56
+
57
+ When all variances are equal, this is equivalent to a scaled
58
+ Euclidean distance — same ranking as cosine on normalized vectors.
59
+ The advantage appears when variances differ across memories.
60
+ """
61
+ diff = mu_q - mu_f
62
+ var_safe = np.maximum(var_f, _VARIANCE_FLOOR)
63
+ d_sq = float(np.sum(diff * diff / var_safe))
64
+ return math.exp(-d_sq / temperature)
65
+
66
+
67
+ class SemanticChannel:
68
+ """Dense semantic retrieval via embedding similarity.
69
+
70
+ Scans all facts for a profile. Uses a GRADUATED Fisher-Rao ramp:
71
+ fresh facts (low access_count) use cosine, frequently-accessed facts
72
+ transition to Fisher-Rao distance for uncertainty-aware similarity.
73
+
74
+ Graduated ramp: weight = min(1.2, access_count / 10 * 1.2)
75
+ Final sim = fisher_weight * fisher_sim + (1 - fisher_weight) * cosine_sim
76
+ """
77
+
78
+ def __init__(
79
+ self,
80
+ db: DatabaseManager,
81
+ fisher_temperature: float = 15.0,
82
+ embedder: object | None = None,
83
+ fisher_mode: str = "simplified",
84
+ ) -> None:
85
+ self._db = db
86
+ self._temperature = fisher_temperature
87
+ self._embedder = embedder
88
+ self._fisher_mode = fisher_mode if fisher_mode in ("simplified", "full") else "simplified"
89
+ # Lazily instantiated full metric (avoids import cost when not needed)
90
+ self._full_metric: object | None = None
91
+
92
+ def search(
93
+ self,
94
+ query_embedding: list[float],
95
+ profile_id: str,
96
+ top_k: int = 50,
97
+ ) -> list[tuple[str, float]]:
98
+ """Search for semantically similar facts.
99
+
100
+ Uses graduated Fisher-Rao ramp: access_count < 1 = pure cosine,
101
+ access_count >= 10 = full Fisher-Rao (1.2x weight).
102
+
103
+ Args:
104
+ query_embedding: Dense vector for the query.
105
+ profile_id: Scope to this profile.
106
+ top_k: Maximum results to return.
107
+
108
+ Returns:
109
+ List of (fact_id, score) sorted by score descending.
110
+ Score is in [0, 1] range.
111
+ """
112
+ if not query_embedding:
113
+ return []
114
+
115
+ q_vec = np.array(query_embedding, dtype=np.float32)
116
+
117
+ # Compute query Fisher params for Bayesian comparison (F45 fix)
118
+ q_mean: np.ndarray | None = None
119
+ q_var: np.ndarray | None = None
120
+ if self._embedder and hasattr(self._embedder, 'compute_fisher_params'):
121
+ qm, qv = self._embedder.compute_fisher_params(query_embedding)
122
+ q_mean = np.array(qm, dtype=np.float32)
123
+ q_var = np.array(qv, dtype=np.float32)
124
+
125
+ facts = self._db.get_all_facts(profile_id)
126
+
127
+ scored: list[tuple[str, float]] = []
128
+ for fact in facts:
129
+ if fact.embedding is None:
130
+ continue
131
+
132
+ f_vec = np.array(fact.embedding, dtype=np.float32)
133
+ if f_vec.shape != q_vec.shape:
134
+ continue
135
+
136
+ # Cosine baseline (always computed)
137
+ cos_sim = (_cosine_similarity(q_vec, f_vec) + 1.0) / 2.0
138
+
139
+ # Graduated Fisher-Rao ramp (F37, F108)
140
+ fisher_weight = min(1.2, (fact.access_count or 0) / 10.0 * 1.2)
141
+
142
+ if (fisher_weight > 0.01
143
+ and fact.fisher_variance is not None
144
+ and len(fact.fisher_variance) == len(q_vec)):
145
+ var_vec = np.array(fact.fisher_variance, dtype=np.float32)
146
+ f_sim = self._compute_fisher_sim(
147
+ q_vec, f_vec, var_vec, fact, q_mean, q_var,
148
+ )
149
+ capped_w = min(1.0, fisher_weight)
150
+ sim = capped_w * f_sim + (1.0 - capped_w) * cos_sim
151
+ else:
152
+ sim = cos_sim
153
+
154
+ if sim > 0.3:
155
+ scored.append((fact.fact_id, sim))
156
+
157
+ scored.sort(key=lambda x: x[1], reverse=True)
158
+ return scored[:top_k]
159
+
160
+ # ------------------------------------------------------------------
161
+ # Fisher similarity dispatch
162
+ # ------------------------------------------------------------------
163
+
164
+ def _compute_fisher_sim(
165
+ self,
166
+ q_vec: np.ndarray,
167
+ f_vec: np.ndarray,
168
+ var_vec: np.ndarray,
169
+ fact: AtomicFact,
170
+ q_mean: np.ndarray | None,
171
+ q_var: np.ndarray | None,
172
+ ) -> float:
173
+ """Compute Fisher-Rao similarity using simplified or full metric.
174
+
175
+ Simplified (default): Mahalanobis-like distance using only fact variance.
176
+ Full: Atkinson-Mitchell geodesic via FisherRaoMetric.similarity(),
177
+ requires both query and fact (mean, variance) pairs.
178
+
179
+ Falls back to simplified if full metric cannot be applied (e.g.
180
+ missing fisher_mean on the fact, or missing query variance).
181
+ """
182
+ if self._fisher_mode == "full":
183
+ return self._compute_full_fisher_sim(
184
+ q_vec, f_vec, var_vec, fact, q_mean, q_var,
185
+ )
186
+ return _fisher_rao_similarity(q_vec, f_vec, var_vec, self._temperature)
187
+
188
+ def _compute_full_fisher_sim(
189
+ self,
190
+ q_vec: np.ndarray,
191
+ f_vec: np.ndarray,
192
+ var_vec: np.ndarray,
193
+ fact: AtomicFact,
194
+ q_mean: np.ndarray | None,
195
+ q_var: np.ndarray | None,
196
+ ) -> float:
197
+ """Full Atkinson-Mitchell geodesic via FisherRaoMetric.
198
+
199
+ Requires fisher_mean on the fact AND query variance. If either is
200
+ missing, falls back to the simplified local computation so that
201
+ the graduated ramp still produces a score.
202
+ """
203
+ # Need fact fisher_mean for the full metric
204
+ fact_mean = fact.fisher_mean
205
+ if fact_mean is None or len(fact_mean) != len(f_vec):
206
+ # No stored mean — fall back to simplified
207
+ return _fisher_rao_similarity(q_vec, f_vec, var_vec, self._temperature)
208
+
209
+ # Need query variance for the full metric
210
+ if q_mean is None or q_var is None:
211
+ return _fisher_rao_similarity(q_vec, f_vec, var_vec, self._temperature)
212
+
213
+ if len(q_mean) != len(fact_mean) or len(q_var) != len(var_vec):
214
+ return _fisher_rao_similarity(q_vec, f_vec, var_vec, self._temperature)
215
+
216
+ metric = self._get_full_metric()
217
+ try:
218
+ return metric.similarity(
219
+ q_mean.tolist(), q_var.tolist(),
220
+ list(fact_mean), list(var_vec),
221
+ )
222
+ except (ValueError, FloatingPointError):
223
+ # Numerical issue — fall back gracefully
224
+ logger.debug("Full Fisher metric raised; falling back to simplified")
225
+ return _fisher_rao_similarity(q_vec, f_vec, var_vec, self._temperature)
226
+
227
+ def _get_full_metric(self) -> "FisherRaoMetric": # noqa: F821
228
+ """Lazy-load FisherRaoMetric to avoid import-time cost."""
229
+ if self._full_metric is None:
230
+ from superlocalmemory.math.fisher import FisherRaoMetric
231
+ self._full_metric = FisherRaoMetric(temperature=self._temperature)
232
+ return self._full_metric # type: ignore[return-value]
@@ -0,0 +1,96 @@
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 — Query-Adaptive Strategy.
6
+
7
+ Classifies query type and returns per-type channel weights.
8
+ V1 had this code (strategy_learner.py) but never wired it in.
9
+
10
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
11
+ License: MIT
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import re
16
+ from dataclasses import dataclass, field
17
+
18
+ STRATEGY_PRESETS: dict[str, dict[str, float]] = {
19
+ "temporal": {"semantic": 0.8, "bm25": 1.5, "entity_graph": 0.8, "temporal": 2.0},
20
+ "multi_hop": {"semantic": 1.0, "bm25": 0.8, "entity_graph": 2.0, "temporal": 0.5},
21
+ "aggregation": {"semantic": 1.2, "bm25": 1.5, "entity_graph": 1.0, "temporal": 0.5},
22
+ "opinion": {"semantic": 1.8, "bm25": 0.6, "entity_graph": 0.8, "temporal": 0.3},
23
+ "factual": {"semantic": 1.2, "bm25": 1.4, "entity_graph": 1.0, "temporal": 0.6},
24
+ "entity": {"semantic": 1.0, "bm25": 1.5, "entity_graph": 1.2, "temporal": 0.5},
25
+ "general": {},
26
+ }
27
+
28
+ _TEMPORAL_WORDS: frozenset[str] = frozenset({
29
+ "when", "date", "time", "year", "month", "ago", "before", "after",
30
+ "during", "last", "next", "recently", "earlier", "later", "since",
31
+ "until", "while", "between", "january", "february", "march",
32
+ "april", "may", "june", "july", "august", "september", "october",
33
+ "november", "december",
34
+ })
35
+
36
+ _MULTI_HOP_PHRASES: tuple[str, ...] = (
37
+ "and then", "after that", "because", "how did",
38
+ "as a result", "led to", "connection between", "relationship between",
39
+ )
40
+
41
+ _AGGREGATION_WORDS: frozenset[str] = frozenset({
42
+ "all", "list", "every", "everything", "various", "different",
43
+ "many", "several", "multiple", "summarize", "overview",
44
+ })
45
+
46
+ _OPINION_WORDS: tuple[str, ...] = (
47
+ "think", "feel", "opinion", "prefer", "favorite", "best", "worst",
48
+ "believe", "like about", "dislike", "enjoy", "hate", "love",
49
+ )
50
+
51
+
52
+ @dataclass
53
+ class QueryStrategy:
54
+ """Classified query type + adapted weights."""
55
+ query_type: str = "general"
56
+ weights: dict[str, float] = field(default_factory=dict)
57
+ confidence: float = 0.5
58
+
59
+
60
+ class QueryStrategyClassifier:
61
+ """Classifies queries and produces adaptive channel weights."""
62
+
63
+ def classify(self, query: str, base_weights: dict[str, float]) -> QueryStrategy:
64
+ """Classify query and return adapted weights."""
65
+ qtype = self._detect_type(query)
66
+ adapted = dict(base_weights)
67
+ for ch, w in STRATEGY_PRESETS.get(qtype, {}).items():
68
+ adapted[ch] = base_weights.get(ch, 1.0) * w
69
+ return QueryStrategy(qtype, adapted, 0.7 if qtype != "general" else 0.5)
70
+
71
+ def _detect_type(self, query: str) -> str:
72
+ q = query.lower()
73
+ # Strip punctuation from words so "january?" matches "january"
74
+ words = set(re.sub(r"[^\w\s'-]", "", q).split())
75
+
76
+ # Check multi_hop BEFORE temporal — phrases like "connection between"
77
+ # must not be short-circuited by the word "between" in _TEMPORAL_WORDS.
78
+ if any(p in q for p in _MULTI_HOP_PHRASES):
79
+ return "multi_hop"
80
+ if words & _TEMPORAL_WORDS:
81
+ return "temporal"
82
+ if words & _AGGREGATION_WORDS:
83
+ return "aggregation"
84
+ if any(w in q for w in _OPINION_WORDS):
85
+ return "opinion"
86
+ # Proper nouns — exclude common sentence-initial words
87
+ _SENTENCE_STARTERS = {"What", "Where", "Who", "Which", "How", "When",
88
+ "Does", "Did", "Can", "Could", "Would", "Should",
89
+ "Are", "Is", "Was", "Were", "Has", "Have", "The", "Tell"}
90
+ proper_nouns = [m for m in re.findall(r"\b[A-Z][a-z]{1,}\b", query)
91
+ if m not in _SENTENCE_STARTERS]
92
+ if len(proper_nouns) >= 2:
93
+ return "entity"
94
+ if q.startswith(("what ", "where ", "who ", "which ", "how ")):
95
+ return "factual"
96
+ return "general"
@@ -0,0 +1,175 @@
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 — Temporal Retrieval Channel (3-Date Model).
6
+
7
+ Searches by referenced_date (NOT just created_at like V1).
8
+ Returns empty when query has no temporal signal (no recency noise).
9
+
10
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
11
+ License: MIT
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ import math
17
+ from datetime import datetime
18
+ from typing import TYPE_CHECKING
19
+
20
+ from dateutil.parser import parse as dateutil_parse, ParserError
21
+
22
+ from superlocalmemory.encoding.temporal_parser import TemporalParser
23
+
24
+ if TYPE_CHECKING:
25
+ from superlocalmemory.storage.database import DatabaseManager
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ _MAX_PROXIMITY_DAYS: float = 365.0
30
+
31
+
32
+ def _parse_iso(s: str | None) -> datetime | None:
33
+ if not s:
34
+ return None
35
+ try:
36
+ return dateutil_parse(s)
37
+ except (ParserError, ValueError, OverflowError):
38
+ return None
39
+
40
+
41
+ def _proximity_score(q: datetime, e: datetime) -> float:
42
+ """Gaussian proximity: same day=1.0, 30d=0.61, 90d=0.11."""
43
+ dist = abs((q - e).total_seconds()) / 86400.0
44
+ if dist > _MAX_PROXIMITY_DAYS:
45
+ return 0.0
46
+ return math.exp(-(dist * dist) / (2.0 * 30.0 * 30.0))
47
+
48
+
49
+ class TemporalChannel:
50
+ """Date-aware retrieval using the 3-date temporal model."""
51
+
52
+ def __init__(self, db: DatabaseManager) -> None:
53
+ self._db = db
54
+
55
+ def search(self, query: str, profile_id: str, top_k: int = 30) -> list[tuple[str, float]]:
56
+ """Search for temporally relevant facts.
57
+
58
+ Two strategies:
59
+ 1. Date proximity: scores events by date closeness to query date.
60
+ 2. Entity-temporal: filters events by entity name in query,
61
+ returns ALL their temporal facts (metadata-first approach).
62
+
63
+ Returns empty only when query has no temporal signal AND no
64
+ entity-temporal matches.
65
+ """
66
+ parser = TemporalParser()
67
+ dates = parser.extract_dates_from_text(query)
68
+ query_dt = _parse_iso(dates.get("referenced_date"))
69
+ if query_dt is None:
70
+ query_dt = self._try_parse(query)
71
+
72
+ # Strategy 1: Entity-temporal metadata search
73
+ # "When did Alice...?" → find all temporal events for Alice
74
+ entity_results = self._entity_temporal_search(query, profile_id)
75
+
76
+ # Strategy 2: Date proximity search
77
+ if query_dt is None and not entity_results:
78
+ return []
79
+
80
+ events = self._load_events(profile_id)
81
+ scored: dict[str, float] = {}
82
+
83
+ # Include entity-temporal results with high base score
84
+ for fid, score in entity_results:
85
+ scored[fid] = max(scored.get(fid, 0.0), score)
86
+
87
+ if query_dt is not None:
88
+ for ev in events:
89
+ best = 0.0
90
+ ref = _parse_iso(ev.get("referenced_date"))
91
+ if ref is not None:
92
+ best = max(best, _proximity_score(query_dt, ref))
93
+
94
+ obs = _parse_iso(ev.get("observation_date"))
95
+ if obs is not None:
96
+ best = max(best, _proximity_score(query_dt, obs) * 0.8)
97
+
98
+ i_start = _parse_iso(ev.get("interval_start"))
99
+ i_end = _parse_iso(ev.get("interval_end"))
100
+ if i_start and i_end:
101
+ if i_start <= query_dt <= i_end:
102
+ best = max(best, 1.0)
103
+ else:
104
+ best = max(best, max(
105
+ _proximity_score(query_dt, i_start),
106
+ _proximity_score(query_dt, i_end),
107
+ ) * 0.9)
108
+
109
+ if best > 0.0:
110
+ fid = ev["fact_id"]
111
+ scored[fid] = max(scored.get(fid, 0.0), best)
112
+
113
+ results = sorted(scored.items(), key=lambda x: x[1], reverse=True)
114
+ return results[:top_k]
115
+
116
+ def _entity_temporal_search(
117
+ self, query: str, profile_id: str,
118
+ ) -> list[tuple[str, float]]:
119
+ """Metadata-first: find temporal events for entities mentioned in query.
120
+
121
+ "When did Alice do X?" → SQL filter by entity_id for Alice → return
122
+ all temporal facts about Alice. High precision for entity+time queries.
123
+ """
124
+ import re
125
+ _PROPER_RE = re.compile(r"\b([A-Z][a-z]+)\b")
126
+ names = [m.group(1) for m in _PROPER_RE.finditer(query)]
127
+ if not names:
128
+ return []
129
+
130
+ results: list[tuple[str, float]] = []
131
+ seen: set[str] = set()
132
+
133
+ for name in names[:3]: # Limit to first 3 entity mentions
134
+ # Look up entity ID
135
+ entity = self._db.get_entity_by_name(name, profile_id)
136
+ if entity is None:
137
+ continue
138
+
139
+ # Find all temporal events for this entity
140
+ rows = self._db.execute(
141
+ "SELECT fact_id FROM temporal_events "
142
+ "WHERE profile_id = ? AND entity_id = ?",
143
+ (profile_id, entity.entity_id),
144
+ )
145
+ for row in rows:
146
+ fid = dict(row)["fact_id"]
147
+ if fid not in seen:
148
+ seen.add(fid)
149
+ results.append((fid, 0.85)) # High base score for entity-temporal
150
+
151
+ return results
152
+
153
+ def _load_events(self, profile_id: str) -> list[dict]:
154
+ rows = self._db.execute(
155
+ "SELECT fact_id, observation_date, referenced_date, "
156
+ "interval_start, interval_end "
157
+ "FROM temporal_events WHERE profile_id = ?",
158
+ (profile_id,),
159
+ )
160
+ return [dict(r) for r in rows]
161
+
162
+ @staticmethod
163
+ def _try_parse(text: str) -> datetime | None:
164
+ """Fuzzy date parse with safety guards.
165
+
166
+ dateutil fuzzy=True is exponential on long non-date text.
167
+ Guard: only attempt on short strings (< 60 chars) that contain
168
+ at least one digit (dates always have numbers).
169
+ """
170
+ if len(text) > 60 or not any(c.isdigit() for c in text):
171
+ return None
172
+ try:
173
+ return dateutil_parse(text, fuzzy=True)
174
+ except (ParserError, ValueError, OverflowError):
175
+ return None
@@ -0,0 +1 @@
1
+ # Server package — API and UI servers