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,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