memorymaster 3.13.0__tar.gz → 3.15.0__tar.gz

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 (363) hide show
  1. {memorymaster-3.13.0/memorymaster.egg-info → memorymaster-3.15.0}/PKG-INFO +26 -28
  2. {memorymaster-3.13.0 → memorymaster-3.15.0}/README.md +23 -27
  3. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/_storage_lifecycle.py +84 -12
  4. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/_storage_read.py +5 -0
  5. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/_storage_schema.py +2 -1
  6. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/_storage_sources.py +17 -1
  7. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/_storage_write_claims.py +24 -3
  8. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/auto_resolver.py +51 -45
  9. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/claim_verifier.py +0 -1
  10. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/cli.py +53 -4
  11. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/cli_handlers_basic.py +155 -3
  12. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config.py +103 -0
  13. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +2 -1
  14. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +3 -1
  15. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +2 -1
  16. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/conflict_resolver.py +113 -2
  17. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/context_optimizer.py +161 -19
  18. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/dashboard.py +391 -1
  19. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/db_merge.py +133 -11
  20. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/dream_bridge.py +12 -5
  21. memorymaster-3.15.0/memorymaster/jobs/calibration.py +120 -0
  22. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/jobs/compact_summaries.py +6 -2
  23. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/jobs/compactor.py +93 -12
  24. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/jobs/decay.py +62 -1
  25. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/jobs/dedup.py +155 -2
  26. memorymaster-3.15.0/memorymaster/jobs/entity_graph_export.py +210 -0
  27. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/lifecycle.py +31 -1
  28. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/llm_provider.py +73 -2
  29. memorymaster-3.15.0/memorymaster/llm_rerank.py +216 -0
  30. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/llm_steward.py +5 -2
  31. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/mcp_server.py +378 -34
  32. memorymaster-3.15.0/memorymaster/mcp_usage.py +43 -0
  33. memorymaster-3.15.0/memorymaster/observability.py +183 -0
  34. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/operator.py +10 -10
  35. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/postgres_store.py +62 -32
  36. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/retrieval.py +31 -2
  37. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/scheduler.py +9 -0
  38. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/schema.sql +9 -0
  39. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/schema_postgres.sql +9 -0
  40. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/security.py +7 -2
  41. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/service.py +340 -17
  42. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/setup_hooks.py +1 -1
  43. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/snapshot.py +64 -0
  44. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/steward.py +16 -14
  45. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/storage.py +19 -35
  46. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/vault_linter.py +166 -14
  47. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/vault_query_capture.py +1 -1
  48. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/verbatim_store.py +10 -4
  49. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/wiki_engine.py +238 -22
  50. memorymaster-3.15.0/memorymaster/wiki_suggest.py +270 -0
  51. {memorymaster-3.13.0 → memorymaster-3.15.0/memorymaster.egg-info}/PKG-INFO +26 -28
  52. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster.egg-info/SOURCES.txt +46 -1
  53. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster.egg-info/requires.txt +2 -0
  54. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster.egg-info/top_level.txt +1 -0
  55. {memorymaster-3.13.0 → memorymaster-3.15.0}/pyproject.toml +10 -2
  56. memorymaster-3.15.0/tests/bench_longmemeval.py +921 -0
  57. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_auto_resolver.py +10 -18
  58. memorymaster-3.15.0/tests/test_calibration.py +134 -0
  59. memorymaster-3.15.0/tests/test_calibration_priors_applied.py +20 -0
  60. memorymaster-3.15.0/tests/test_cli_dry_run.py +142 -0
  61. memorymaster-3.15.0/tests/test_compact_summaries_sensitivity.py +69 -0
  62. memorymaster-3.15.0/tests/test_compactor_artifact_order.py +40 -0
  63. memorymaster-3.15.0/tests/test_context_optimizer_provider.py +145 -0
  64. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_dashboard.py +96 -1
  65. memorymaster-3.15.0/tests/test_dashboard_coverage.py +321 -0
  66. memorymaster-3.15.0/tests/test_dashboard_latency.py +111 -0
  67. memorymaster-3.15.0/tests/test_dashboard_lineage.py +70 -0
  68. memorymaster-3.15.0/tests/test_dashboard_review_queue.py +143 -0
  69. memorymaster-3.15.0/tests/test_db_merge_confidence_conflict.py +71 -0
  70. memorymaster-3.15.0/tests/test_db_merge_coverage_v2.py +260 -0
  71. memorymaster-3.15.0/tests/test_decay_coverage.py +212 -0
  72. memorymaster-3.15.0/tests/test_decay_respects_pinned.py +120 -0
  73. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_dedup.py +9 -0
  74. memorymaster-3.15.0/tests/test_dedup_cli.py +120 -0
  75. memorymaster-3.15.0/tests/test_dedup_conflict_disambiguation.py +52 -0
  76. memorymaster-3.15.0/tests/test_dream_bridge_coverage_v2.py +186 -0
  77. memorymaster-3.15.0/tests/test_dream_bridge_sensitivity.py +76 -0
  78. memorymaster-3.15.0/tests/test_entity_graph_export.py +147 -0
  79. memorymaster-3.15.0/tests/test_federated_query_safety.py +91 -0
  80. memorymaster-3.15.0/tests/test_lifecycle_supersede_invariant.py +87 -0
  81. memorymaster-3.15.0/tests/test_llm_provider_key_rotation.py +80 -0
  82. memorymaster-3.15.0/tests/test_mcp_filter_bypass.py +91 -0
  83. memorymaster-3.15.0/tests/test_mcp_rate_limit.py +101 -0
  84. memorymaster-3.15.0/tests/test_mcp_server_validation.py +49 -0
  85. memorymaster-3.15.0/tests/test_mcp_usage.py +95 -0
  86. memorymaster-3.15.0/tests/test_meta_decisions.py +125 -0
  87. memorymaster-3.15.0/tests/test_observability.py +47 -0
  88. memorymaster-3.15.0/tests/test_resolvers_concurrent_supersede.py +148 -0
  89. memorymaster-3.15.0/tests/test_retrieval_profile.py +120 -0
  90. memorymaster-3.15.0/tests/test_sensitivity_filter_t07.py +63 -0
  91. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_snapshot.py +2 -1
  92. memorymaster-3.15.0/tests/test_snapshot_roundtrip.py +168 -0
  93. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_sqlite_core.py +9 -0
  94. memorymaster-3.15.0/tests/test_storage_parity.py +287 -0
  95. memorymaster-3.15.0/tests/test_vault_linter_orphan.py +91 -0
  96. memorymaster-3.15.0/tests/test_verbatim_store.py +67 -0
  97. memorymaster-3.15.0/tests/test_verbatim_store_qdrant.py +120 -0
  98. memorymaster-3.15.0/tests/test_wiki_autopromote.py +101 -0
  99. memorymaster-3.15.0/tests/test_wiki_engine_idempotency.py +170 -0
  100. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_wiki_freshness.py +1 -1
  101. memorymaster-3.15.0/tests/test_wiki_suggest.py +124 -0
  102. memorymaster-3.13.0/scripts/run_longmemeval.py +0 -997
  103. {memorymaster-3.13.0 → memorymaster-3.15.0}/LICENSE +0 -0
  104. {memorymaster-3.13.0 → memorymaster-3.15.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
  105. {memorymaster-3.13.0 → memorymaster-3.15.0}/benchmarks/longmemeval_runner.py +0 -0
  106. {memorymaster-3.13.0 → memorymaster-3.15.0}/benchmarks/longmemeval_vector_runner.py +0 -0
  107. {memorymaster-3.13.0 → memorymaster-3.15.0}/benchmarks/perf_smoke.py +0 -0
  108. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/__init__.py +0 -0
  109. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/__main__.py +0 -0
  110. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/_storage_shared.py +0 -0
  111. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/access_control.py +0 -0
  112. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/action_exporters.py +0 -0
  113. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/action_extractor.py +0 -0
  114. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/atlas_claim_extractor.py +0 -0
  115. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/atlas_contract.py +0 -0
  116. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/auto_extractor.py +0 -0
  117. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/candidate_dedupe.py +0 -0
  118. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/claim_edges.py +0 -0
  119. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/cli_handlers_curation.py +0 -0
  120. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/cli_helpers.py +0 -0
  121. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/closets.py +0 -0
  122. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/claude-md-append.md +0 -0
  123. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
  124. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
  125. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
  126. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
  127. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
  128. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
  129. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/connectors/__init__.py +0 -0
  130. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/connectors/whatsapp.py +0 -0
  131. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/context_hook.py +0 -0
  132. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/daily_notes.py +0 -0
  133. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/embeddings.py +0 -0
  134. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/entity_extractor.py +0 -0
  135. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/entity_graph.py +0 -0
  136. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/entity_registry.py +0 -0
  137. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/federated_graphify.py +0 -0
  138. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/feedback.py +0 -0
  139. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/graph_store.py +0 -0
  140. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/hook_log.py +0 -0
  141. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/jobs/__init__.py +0 -0
  142. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/jobs/deterministic.py +0 -0
  143. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/jobs/extractor.py +0 -0
  144. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/jobs/staleness.py +0 -0
  145. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/jobs/validator.py +0 -0
  146. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/key_rotator.py +0 -0
  147. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/media_processing.py +0 -0
  148. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/media_providers.py +0 -0
  149. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/metrics_exporter.py +0 -0
  150. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/models.py +0 -0
  151. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/operator_queue.py +0 -0
  152. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/plugins.py +0 -0
  153. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/policy.py +0 -0
  154. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/qdrant_backend.py +0 -0
  155. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/qdrant_recall_fallback.py +0 -0
  156. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/qmd_bridge.py +0 -0
  157. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/query_classifier.py +0 -0
  158. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/query_expansion.py +0 -0
  159. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/recall_fusion.py +0 -0
  160. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/recall_tokenizer.py +0 -0
  161. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/retry.py +0 -0
  162. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/review.py +0 -0
  163. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/rl_trainer.py +0 -0
  164. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/schema.py +0 -0
  165. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/scope_utils.py +0 -0
  166. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/session_tracker.py +0 -0
  167. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/skill_evolver.py +0 -0
  168. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/steward_classifier.py +0 -0
  169. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/steward_features.py +0 -0
  170. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/store_factory.py +0 -0
  171. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/transcript_miner.py +0 -0
  172. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/turn_schema.py +0 -0
  173. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/vault_bases.py +0 -0
  174. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/vault_curator.py +0 -0
  175. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/vault_exporter.py +0 -0
  176. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/vault_log.py +0 -0
  177. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/vault_synthesis.py +0 -0
  178. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/verbatim_recall.py +0 -0
  179. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/webhook.py +0 -0
  180. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/wiki_freshness.py +0 -0
  181. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/wiki_similarity.py +0 -0
  182. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster/wiki_validate.py +0 -0
  183. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster.egg-info/dependency_links.txt +0 -0
  184. {memorymaster-3.13.0 → memorymaster-3.15.0}/memorymaster.egg-info/entry_points.txt +0 -0
  185. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/agg_recall_latency.py +0 -0
  186. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/alert_operator_metrics.py +0 -0
  187. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/audit_dedupe_precision.py +0 -0
  188. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/autoresearch_daemon.py +0 -0
  189. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/backfill_entity_extraction.py +0 -0
  190. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/backfill_graph_store.py +0 -0
  191. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/backfill_stop_hook_citations.py +0 -0
  192. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/backtest_steward_classifier.py +0 -0
  193. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/build_steward_training_set.py +0 -0
  194. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/check_hook_template_drift.py +0 -0
  195. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/claude_to_turns.py +0 -0
  196. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/codex_live_to_turns.py +0 -0
  197. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/compaction_edge_cases.py +0 -0
  198. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/compaction_trace_report.py +0 -0
  199. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/compaction_trace_validate.py +0 -0
  200. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/confusion_matrix_eval.py +0 -0
  201. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/conversation_importer.py +0 -0
  202. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/conversation_to_turns.py +0 -0
  203. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/e2e_operator.py +0 -0
  204. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/email_live_to_turns.py +0 -0
  205. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/eval_bm25_sweep.py +0 -0
  206. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/eval_classify_f1.py +0 -0
  207. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/eval_memorymaster.py +0 -0
  208. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/eval_recall_precision_at_5.py +0 -0
  209. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/eval_recall_quality.py +0 -0
  210. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/eval_steward_pareto.py +0 -0
  211. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/eval_verbatim_recall.py +0 -0
  212. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/expand_recall_eval.py +0 -0
  213. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/generate_drill_signoff.py +0 -0
  214. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/git_to_turns.py +0 -0
  215. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/github_live_to_turns.py +0 -0
  216. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/gitnexus_to_claims.py +0 -0
  217. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/grid_recall_weights.py +0 -0
  218. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/index_claims_to_qdrant.py +0 -0
  219. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/ingest_planning_docs.py +0 -0
  220. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/jira_live_to_turns.py +0 -0
  221. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/label_prompts_with_judge.py +0 -0
  222. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/llm_benchmark.py +0 -0
  223. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/measure_dedupe_thresholds.py +0 -0
  224. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/merge_scope_variants.py +0 -0
  225. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/messages_to_turns.py +0 -0
  226. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/operator_metrics.py +0 -0
  227. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/precompute_candidates.py +0 -0
  228. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/recurring_incident_drill.py +0 -0
  229. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/release_readiness.py +0 -0
  230. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/run_codex_autologger.py +0 -0
  231. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/run_incident_drill.py +0 -0
  232. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/scheduled_ingest.py +0 -0
  233. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/setup-hooks.py +0 -0
  234. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/slack_live_to_turns.py +0 -0
  235. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/sync_hook_templates.py +0 -0
  236. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/tickets_to_turns.py +0 -0
  237. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/train_steward_classifier.py +0 -0
  238. {memorymaster-3.13.0 → memorymaster-3.15.0}/scripts/webhook_to_turns.py +0 -0
  239. {memorymaster-3.13.0 → memorymaster-3.15.0}/setup.cfg +0 -0
  240. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/conftest.py +0 -0
  241. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
  242. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_access_control.py +0 -0
  243. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_action_exporters.py +0 -0
  244. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_action_extractor.py +0 -0
  245. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_atlas_claim_extractor.py +0 -0
  246. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_atlas_contract.py +0 -0
  247. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_atlas_source_schema.py +0 -0
  248. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_auto_extractor.py +0 -0
  249. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_auto_ingest_hook_citations.py +0 -0
  250. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_auto_ingest_hook_schema.py +0 -0
  251. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_auto_validate.py +0 -0
  252. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_bm25_per_field.py +0 -0
  253. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_candidate_dedupe.py +0 -0
  254. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_claim_edges.py +0 -0
  255. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_claim_links.py +0 -0
  256. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_claim_type_ranking.py +0 -0
  257. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_classify_hook_f1.py +0 -0
  258. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_classify_hook_latency.py +0 -0
  259. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_claude_to_turns.py +0 -0
  260. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_cli_json_flag.py +0 -0
  261. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_cli_ready.py +0 -0
  262. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_cli_review_queue.py +0 -0
  263. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_cli_subcommands.py +0 -0
  264. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_closets.py +0 -0
  265. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_closets_recall_integration.py +0 -0
  266. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_compact_summaries.py +0 -0
  267. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_compaction_trace.py +0 -0
  268. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_config.py +0 -0
  269. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_conflict_resolver.py +0 -0
  270. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_confusion_matrix_eval.py +0 -0
  271. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_connection_retry.py +0 -0
  272. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_connectors.py +0 -0
  273. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_context_hook.py +0 -0
  274. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_context_optimizer.py +0 -0
  275. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_conversation_to_turns.py +0 -0
  276. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_deterministic_predicates.py +0 -0
  277. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_embeddings_coverage.py +0 -0
  278. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_entity_extractor.py +0 -0
  279. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_entity_extractor_llm.py +0 -0
  280. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_entity_graph.py +0 -0
  281. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_entity_new_kinds.py +0 -0
  282. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_entity_regex_v3.py +0 -0
  283. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_entity_registry.py +0 -0
  284. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_eval_harness.py +0 -0
  285. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_events_schema.py +0 -0
  286. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_extract_llm_ollama.py +0 -0
  287. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_federated_graphify_mcp.py +0 -0
  288. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_feedback.py +0 -0
  289. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_fts5_search.py +0 -0
  290. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_graph_distance.py +0 -0
  291. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_graph_store.py +0 -0
  292. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_handler_regressions.py +0 -0
  293. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_hook_env_isolation.py +0 -0
  294. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_human_id.py +0 -0
  295. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_incident_drill_runner.py +0 -0
  296. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_integration_workflows.py +0 -0
  297. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_key_rotator.py +0 -0
  298. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_lifecycle.py +0 -0
  299. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_llm_fallback.py +0 -0
  300. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_llm_provider_claude_cli.py +0 -0
  301. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_llm_steward_coverage.py +0 -0
  302. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_llm_steward_key_rotation.py +0 -0
  303. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_mcp_helpers.py +0 -0
  304. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_media_processing.py +0 -0
  305. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_metrics_exporter.py +0 -0
  306. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_obsidian_mind_patterns.py +0 -0
  307. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_operator.py +0 -0
  308. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_operator_queue.py +0 -0
  309. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_perf_smoke_config.py +0 -0
  310. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_plugins.py +0 -0
  311. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_policy_coverage.py +0 -0
  312. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_policy_mode_env.py +0 -0
  313. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_postgres_parity.py +0 -0
  314. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_qdrant_backend.py +0 -0
  315. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_qmd_bridge.py +0 -0
  316. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_query_classifier.py +0 -0
  317. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_query_expansion.py +0 -0
  318. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_recall_entity_fanout.py +0 -0
  319. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_recall_fusion.py +0 -0
  320. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_recall_latency.py +0 -0
  321. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_recall_precision_at_5.py +0 -0
  322. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_recall_tokenizer.py +0 -0
  323. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_recall_vector_fallback.py +0 -0
  324. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_reliability_hardening.py +0 -0
  325. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_review.py +0 -0
  326. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_rl_trainer.py +0 -0
  327. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_rrf_auto_gate.py +0 -0
  328. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_scheduler.py +0 -0
  329. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_schema.py +0 -0
  330. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_scope_boost.py +0 -0
  331. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_scope_utils.py +0 -0
  332. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_security_access.py +0 -0
  333. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_security_patterns.py +0 -0
  334. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
  335. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
  336. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_service_coverage.py +0 -0
  337. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_session_tracker.py +0 -0
  338. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_staleness.py +0 -0
  339. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_stealth_mode.py +0 -0
  340. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_steward.py +0 -0
  341. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_steward_classifier.py +0 -0
  342. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_steward_features.py +0 -0
  343. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_steward_features_v3.py +0 -0
  344. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_steward_resolution_parity.py +0 -0
  345. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_store_factory.py +0 -0
  346. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_tenant_isolation.py +0 -0
  347. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_turn_schema.py +0 -0
  348. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_two_pass_recall.py +0 -0
  349. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_v311_fixes.py +0 -0
  350. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_v313_e2e.py +0 -0
  351. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
  352. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_v390_e2e.py +0 -0
  353. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_v391_strict_warnings.py +0 -0
  354. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_vault_exporter.py +0 -0
  355. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_vector_search.py +0 -0
  356. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_verbatim_dedup.py +0 -0
  357. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_verbatim_recall.py +0 -0
  358. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_webhook.py +0 -0
  359. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_whatsapp_importer.py +0 -0
  360. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_wiki_binding.py +0 -0
  361. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
  362. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_wiki_similarity_multiscope.py +0 -0
  363. {memorymaster-3.13.0 → memorymaster-3.15.0}/tests/test_wiki_validate_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memorymaster
3
- Version: 3.13.0
3
+ Version: 3.15.0
4
4
  Summary: Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration.
5
5
  Author: wolverin0
6
6
  License: MIT
@@ -17,6 +17,8 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
17
  Requires-Python: >=3.10
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
+ Requires-Dist: requests>=2.31
21
+ Requires-Dist: tenacity>=8.2
20
22
  Provides-Extra: postgres
21
23
  Requires-Dist: psycopg[binary]>=3.2; extra == "postgres"
22
24
  Provides-Extra: security
@@ -61,29 +63,23 @@ MemoryMaster prevents the #1 problem with agent memory: **drift, stale assumptio
61
63
 
62
64
  ## Architecture
63
65
 
66
+ MemoryMaster is layered around MCP/CLI entry points, the `MemoryService` facade, SQLite/Postgres
67
+ storage, optional Qdrant vector search, scheduled jobs, and the Obsidian wiki/vault layer. The
68
+ canonical ingest path is:
69
+
70
+ ```text
71
+ MCP/CLI -> sensitivity filter -> MemoryService.ingest -> store write -> FTS5 index
64
72
  ```
65
- ┌─────────────────────────────────────────────────────────────────┐
66
- │ Agent Runtime │
67
- │ (Claude Code / Codex / any MCP-compatible agent) │
68
- └────────────┬────────────────────────────────┬───────────────────┘
69
- MCP (24 tools) │ CLI (86 commands)
70
- v v
71
- ┌─────────────────────────────────────────────────────────────────┐
72
- │ MemoryMaster Core │
73
- │ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────────┐ │
74
- │ │ Ingestor │ │ Extractor │ │ Validator │ │ State Engine │ │
75
- │ │ (events) │->│ (claims) │->│ (probes) │->│ (6-state FSM) │ │
76
- │ └──────────┘ └───────────┘ └──────────┘ └───────────────┘ │
77
- │ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────────┐ │
78
- │ │ Retrieval│ │ Compactor │ │ Steward │ │ Dashboard │ │
79
- │ │ (hybrid) │ │ (archive) │ │ (govern) │ │ (HTML+SSE) │ │
80
- │ └──────────┘ └───────────┘ └──────────┘ └───────────────┘ │
81
- └────────┬──────────────┬──────────────┬──────────┬───────────────┘
82
- v v v v
83
- SQLite/Postgres Qdrant Ollama/CLI Claude Code
84
- (vectors) (LLM stack) Auto Dream + Vault
73
+
74
+ The query path is:
75
+
76
+ ```text
77
+ query_memory -> MemoryService.query -> storage reads + optional Qdrant candidates -> ranked context
85
78
  ```
86
79
 
80
+ See [docs/architecture.md](docs/architecture.md) for the current module map, data-flow details,
81
+ recent PR status, and sensitivity-filter invariants.
82
+
87
83
  ## Key features
88
84
 
89
85
  - **6-state lifecycle**: `candidate` → `confirmed` → `stale` → `superseded` → `conflicted` → `archived`
@@ -104,18 +100,20 @@ Full feature index lives in [`docs/handbook.md`](docs/handbook.md).
104
100
 
105
101
  ## Prerequisites
106
102
 
107
- **Required**
103
+ **Required (the package won't function without these)**
108
104
 
109
105
  - Python **3.10+** with `pip`
110
106
  - Claude Code, Codex, or any MCP-compatible agent
107
+ - **An LLM provider** — pick one: Claude Code OAuth (free if you're a subscriber, set `MEMORYMASTER_LLM_PROVIDER=claude_cli`), a free Gemini API key from [aistudio.google.com](https://aistudio.google.com), OpenAI, Anthropic API, or local Ollama. The steward, auto-ingest, and wiki-absorb cycles all need an LLM — without one, claims pile up as `candidate` and never get validated, deduped, or compiled into the wiki.
108
+
109
+ **Strongly recommended (you'll lose ~80% of the value without these)**
110
+
111
+ - **Node.js 18+** for [graphify](https://github.com/wolverin0/graphify) and [GitNexus](https://github.com/wolverin0/gitnexus) — these are the cached intelligence layers that make MemoryMaster cheap to query. Without them, every "what does this codebase do?" question burns tokens cold-exploring files the graph already mapped. The `intelligence-first` workflow in `CLAUDE.md` assumes both are installed.
112
+ - **Obsidian 1.6+** with the [Bases](https://help.obsidian.md/Plugins/Bases) core plugin — the wiki engine writes plain Markdown so any editor works, but Obsidian's backlinks, graph view, and Bases dashboards are how you actually navigate `wiki-absorb` output. Without Obsidian, the wiki is just a folder of files.
111
113
 
112
- **Optional**
114
+ **Optional (nice to have)**
113
115
 
114
- - **Already a Claude Code subscriber?** No API key needed set `MEMORYMASTER_LLM_PROVIDER=claude_cli` and the steward + auto-ingest hooks will use your existing OAuth via the local `claude --print` binary
115
- - A free Gemini API key from [aistudio.google.com](https://aistudio.google.com) — powers the auto-ingest hook at ~zero cost
116
- - **Node.js 18+** for graphify and GitNexus
117
- - **Obsidian 1.6+** with the Bases core plugin (for visual wiki browsing)
118
- - **Docker** for Qdrant (SQLite FTS5 is the default and works out of the box)
116
+ - **Docker** for Qdrant vector retrieval. SQLite FTS5 is the default and works out of the box; add Qdrant when you want semantic recall on top of keyword search.
119
117
 
120
118
  ## Quick start
121
119
 
@@ -17,29 +17,23 @@ MemoryMaster prevents the #1 problem with agent memory: **drift, stale assumptio
17
17
 
18
18
  ## Architecture
19
19
 
20
+ MemoryMaster is layered around MCP/CLI entry points, the `MemoryService` facade, SQLite/Postgres
21
+ storage, optional Qdrant vector search, scheduled jobs, and the Obsidian wiki/vault layer. The
22
+ canonical ingest path is:
23
+
24
+ ```text
25
+ MCP/CLI -> sensitivity filter -> MemoryService.ingest -> store write -> FTS5 index
20
26
  ```
21
- ┌─────────────────────────────────────────────────────────────────┐
22
- │ Agent Runtime │
23
- │ (Claude Code / Codex / any MCP-compatible agent) │
24
- └────────────┬────────────────────────────────┬───────────────────┘
25
- MCP (24 tools) │ CLI (86 commands)
26
- v v
27
- ┌─────────────────────────────────────────────────────────────────┐
28
- │ MemoryMaster Core │
29
- │ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────────┐ │
30
- │ │ Ingestor │ │ Extractor │ │ Validator │ │ State Engine │ │
31
- │ │ (events) │->│ (claims) │->│ (probes) │->│ (6-state FSM) │ │
32
- │ └──────────┘ └───────────┘ └──────────┘ └───────────────┘ │
33
- │ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────────┐ │
34
- │ │ Retrieval│ │ Compactor │ │ Steward │ │ Dashboard │ │
35
- │ │ (hybrid) │ │ (archive) │ │ (govern) │ │ (HTML+SSE) │ │
36
- │ └──────────┘ └───────────┘ └──────────┘ └───────────────┘ │
37
- └────────┬──────────────┬──────────────┬──────────┬───────────────┘
38
- v v v v
39
- SQLite/Postgres Qdrant Ollama/CLI Claude Code
40
- (vectors) (LLM stack) Auto Dream + Vault
27
+
28
+ The query path is:
29
+
30
+ ```text
31
+ query_memory -> MemoryService.query -> storage reads + optional Qdrant candidates -> ranked context
41
32
  ```
42
33
 
34
+ See [docs/architecture.md](docs/architecture.md) for the current module map, data-flow details,
35
+ recent PR status, and sensitivity-filter invariants.
36
+
43
37
  ## Key features
44
38
 
45
39
  - **6-state lifecycle**: `candidate` → `confirmed` → `stale` → `superseded` → `conflicted` → `archived`
@@ -60,18 +54,20 @@ Full feature index lives in [`docs/handbook.md`](docs/handbook.md).
60
54
 
61
55
  ## Prerequisites
62
56
 
63
- **Required**
57
+ **Required (the package won't function without these)**
64
58
 
65
59
  - Python **3.10+** with `pip`
66
60
  - Claude Code, Codex, or any MCP-compatible agent
61
+ - **An LLM provider** — pick one: Claude Code OAuth (free if you're a subscriber, set `MEMORYMASTER_LLM_PROVIDER=claude_cli`), a free Gemini API key from [aistudio.google.com](https://aistudio.google.com), OpenAI, Anthropic API, or local Ollama. The steward, auto-ingest, and wiki-absorb cycles all need an LLM — without one, claims pile up as `candidate` and never get validated, deduped, or compiled into the wiki.
62
+
63
+ **Strongly recommended (you'll lose ~80% of the value without these)**
64
+
65
+ - **Node.js 18+** for [graphify](https://github.com/wolverin0/graphify) and [GitNexus](https://github.com/wolverin0/gitnexus) — these are the cached intelligence layers that make MemoryMaster cheap to query. Without them, every "what does this codebase do?" question burns tokens cold-exploring files the graph already mapped. The `intelligence-first` workflow in `CLAUDE.md` assumes both are installed.
66
+ - **Obsidian 1.6+** with the [Bases](https://help.obsidian.md/Plugins/Bases) core plugin — the wiki engine writes plain Markdown so any editor works, but Obsidian's backlinks, graph view, and Bases dashboards are how you actually navigate `wiki-absorb` output. Without Obsidian, the wiki is just a folder of files.
67
67
 
68
- **Optional**
68
+ **Optional (nice to have)**
69
69
 
70
- - **Already a Claude Code subscriber?** No API key needed set `MEMORYMASTER_LLM_PROVIDER=claude_cli` and the steward + auto-ingest hooks will use your existing OAuth via the local `claude --print` binary
71
- - A free Gemini API key from [aistudio.google.com](https://aistudio.google.com) — powers the auto-ingest hook at ~zero cost
72
- - **Node.js 18+** for graphify and GitNexus
73
- - **Obsidian 1.6+** with the Bases core plugin (for visual wiki browsing)
74
- - **Docker** for Qdrant (SQLite FTS5 is the default and works out of the box)
70
+ - **Docker** for Qdrant vector retrieval. SQLite FTS5 is the default and works out of the box; add Qdrant when you want semantic recall on top of keyword search.
75
71
 
76
72
  ## Quick start
77
73
 
@@ -10,6 +10,7 @@ import json
10
10
  import logging
11
11
  import sqlite3
12
12
  from datetime import datetime, timedelta, timezone
13
+ from typing import TYPE_CHECKING, Any
13
14
 
14
15
  from memorymaster.embeddings import EmbeddingProvider, cosine_similarity
15
16
  from memorymaster.models import (
@@ -23,16 +24,37 @@ from memorymaster.models import (
23
24
  validate_transition_event_type,
24
25
  )
25
26
 
26
- logger = logging.getLogger(__name__)
27
-
28
27
  from memorymaster._storage_shared import (
29
28
  EVENT_HASH_ALGO,
30
29
  ConcurrentModificationError,
31
30
  utc_now,
32
31
  )
33
32
 
33
+ logger = logging.getLogger(__name__)
34
+
34
35
 
35
36
  class _LifecycleMixin:
37
+ if TYPE_CHECKING:
38
+ def connect(self) -> sqlite3.Connection: ...
39
+
40
+ def get_claim(self, claim_id: int, include_citations: bool = True) -> Claim | None: ...
41
+
42
+ def _ensure_event_integrity_schema(self, conn: sqlite3.Connection) -> None: ...
43
+
44
+ def _ensure_embeddings_schema(self, conn: sqlite3.Connection) -> None: ...
45
+
46
+ def _insert_event_row(
47
+ self,
48
+ conn: sqlite3.Connection,
49
+ *,
50
+ claim_id: int | None,
51
+ event_type: str,
52
+ from_status: str | None,
53
+ to_status: str | None,
54
+ details: str | None,
55
+ payload_json: str | None,
56
+ created_at: str,
57
+ ) -> int: ...
36
58
 
37
59
  def apply_status_transition(
38
60
  self,
@@ -102,14 +124,64 @@ class _LifecycleMixin:
102
124
  old_claim = self.get_claim(old_claim_id, include_citations=False)
103
125
  if old_claim is None:
104
126
  return
105
- self.apply_status_transition(
106
- old_claim,
107
- to_status="superseded",
108
- reason=reason,
109
- event_type="supersession",
110
- replaced_by_claim_id=new_claim_id,
111
- )
112
- self.set_supersedes(new_claim_id, old_claim_id)
127
+ if old_claim.status == "superseded" or old_claim.replaced_by_claim_id is not None:
128
+ raise ConcurrentModificationError(
129
+ f"Claim {old_claim_id} was already superseded. Reload and retry."
130
+ )
131
+
132
+ now = utc_now()
133
+ with self.connect() as conn:
134
+ target = conn.execute(
135
+ "SELECT supersedes_claim_id FROM claims WHERE id = ?",
136
+ (new_claim_id,),
137
+ ).fetchone()
138
+ if target is None:
139
+ raise ValueError(f"Replacement claim {new_claim_id} does not exist.")
140
+ if target["supersedes_claim_id"] not in {None, old_claim_id}:
141
+ raise ConcurrentModificationError(
142
+ f"Claim {new_claim_id} already supersedes another claim. Reload and retry."
143
+ )
144
+
145
+ cur = conn.execute(
146
+ """
147
+ UPDATE claims
148
+ SET status = 'superseded', updated_at = ?, replaced_by_claim_id = ?,
149
+ version = version + 1, valid_until = COALESCE(?, valid_until)
150
+ WHERE id = ? AND version = ? AND status != 'superseded'
151
+ AND replaced_by_claim_id IS NULL
152
+ """,
153
+ (now, new_claim_id, now, old_claim_id, old_claim.version),
154
+ )
155
+ if cur.rowcount == 0:
156
+ raise ConcurrentModificationError(
157
+ f"Claim {old_claim_id} was modified by another writer. Reload and retry."
158
+ )
159
+
160
+ cur = conn.execute(
161
+ """
162
+ UPDATE claims
163
+ SET supersedes_claim_id = ?, updated_at = ?
164
+ WHERE id = ? AND (supersedes_claim_id IS NULL OR supersedes_claim_id = ?)
165
+ """,
166
+ (old_claim_id, now, new_claim_id, old_claim_id),
167
+ )
168
+ if cur.rowcount == 0:
169
+ conn.rollback()
170
+ raise ConcurrentModificationError(
171
+ f"Claim {new_claim_id} was modified by another writer. Reload and retry."
172
+ )
173
+
174
+ self._insert_event_row(
175
+ conn,
176
+ claim_id=old_claim.id,
177
+ event_type="supersession",
178
+ from_status=old_claim.status,
179
+ to_status="superseded",
180
+ details=reason,
181
+ payload_json=json.dumps({"replaced_by_claim_id": new_claim_id}),
182
+ created_at=now,
183
+ )
184
+ conn.commit()
113
185
 
114
186
 
115
187
  def delete_old_events(self, retain_days: int) -> int:
@@ -117,8 +189,8 @@ class _LifecycleMixin:
117
189
  return 0
118
190
 
119
191
 
120
- def reconcile_integrity(self, *, fix: bool = False, limit: int = 500) -> dict[str, object]:
121
- report: dict[str, object] = {
192
+ def reconcile_integrity(self, *, fix: bool = False, limit: int = 500) -> dict[str, Any]:
193
+ report: dict[str, Any] = {
122
194
  "checked_at": utc_now(),
123
195
  "fix_mode": bool(fix),
124
196
  "issues": {},
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
  import logging
10
10
  import sqlite3
11
11
  from datetime import datetime, timedelta, timezone
12
+ from typing import TYPE_CHECKING
12
13
 
13
14
  from memorymaster.models import (
14
15
  Citation,
@@ -22,6 +23,10 @@ logger = logging.getLogger(__name__)
22
23
 
23
24
 
24
25
  class _ReadMixin:
26
+ if TYPE_CHECKING:
27
+ def connect(self) -> sqlite3.Connection: ...
28
+
29
+ def init_db(self) -> None: ...
25
30
 
26
31
  def _check_idempotency(self, conn: sqlite3.Connection, idempotency_key: str | None) -> Claim | None:
27
32
  """Check if a claim with this idempotency key already exists. Returns existing claim or None."""
@@ -338,7 +338,8 @@ class _SchemaMixin:
338
338
  predicate,
339
339
  object_value,
340
340
  content='claims',
341
- content_rowid='id'
341
+ content_rowid='id',
342
+ tokenize='porter unicode61'
342
343
  )
343
344
  """
344
345
  )
@@ -8,7 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  import json
10
10
  import sqlite3
11
- from typing import Any
11
+ from typing import TYPE_CHECKING, Any
12
12
 
13
13
  from memorymaster._storage_shared import utc_now
14
14
  from memorymaster.models import (
@@ -51,6 +51,22 @@ def _bounded_confidence(confidence: float | None) -> float | None:
51
51
 
52
52
 
53
53
  class _SourceItemsMixin:
54
+ if TYPE_CHECKING:
55
+ def connect(self) -> sqlite3.Connection: ...
56
+
57
+ def _insert_event_row(
58
+ self,
59
+ conn: sqlite3.Connection,
60
+ *,
61
+ claim_id: int | None,
62
+ event_type: str,
63
+ from_status: str | None,
64
+ to_status: str | None,
65
+ details: str | None,
66
+ payload_json: str | None,
67
+ created_at: str,
68
+ ) -> int: ...
69
+
54
70
  def upsert_external_source(
55
71
  self,
56
72
  *,
@@ -9,21 +9,42 @@ from __future__ import annotations
9
9
  import json
10
10
  import logging
11
11
  import sqlite3
12
+ from typing import TYPE_CHECKING
12
13
 
13
14
  from memorymaster.models import (
14
15
  CitationInput,
15
16
  Claim,
16
17
  validate_event_payload,
17
18
  )
18
-
19
- logger = logging.getLogger(__name__)
20
-
21
19
  from memorymaster._storage_shared import (
22
20
  utc_now,
23
21
  )
24
22
 
23
+ logger = logging.getLogger(__name__)
24
+
25
25
 
26
26
  class _WriteClaimsMixin:
27
+ if TYPE_CHECKING:
28
+ def connect(self) -> sqlite3.Connection: ...
29
+
30
+ def _check_idempotency(self, conn: sqlite3.Connection, idempotency_key: str | None) -> Claim | None: ...
31
+
32
+ def get_claim(self, claim_id: int, include_citations: bool = True) -> Claim | None: ...
33
+
34
+ def _allocate_human_id(self, conn: sqlite3.Connection, subject: str | None, text: str, claim_id: int) -> str: ...
35
+
36
+ def _insert_event_row(
37
+ self,
38
+ conn: sqlite3.Connection,
39
+ *,
40
+ claim_id: int | None,
41
+ event_type: str,
42
+ from_status: str | None,
43
+ to_status: str | None,
44
+ details: str | None,
45
+ payload_json: str | None,
46
+ created_at: str,
47
+ ) -> int: ...
27
48
 
28
49
  def create_claim(
29
50
  self,
@@ -3,27 +3,25 @@
3
3
  When two claims contradict (same subject/predicate, different object_value),
4
4
  asks an LLM to evaluate which one has stronger evidence and should be kept.
5
5
 
6
- Uses Ollama or any OpenAI-compatible endpoint. The loser gets superseded,
7
- not deleted preserving full audit trail.
6
+ Provider routing goes through `memorymaster.llm_provider.call_llm`, which
7
+ honors `MEMORYMASTER_LLM_PROVIDER` (claude_cli / google / openai / anthropic
8
+ / ollama) instead of the previous hardcoded Ollama-only path. The loser
9
+ gets superseded, not deleted — preserving full audit trail.
8
10
  """
9
11
 
10
12
  from __future__ import annotations
11
13
 
12
14
  import json
13
15
  import logging
14
- import os
15
- import urllib.error
16
- import urllib.request
17
16
  from typing import Any
18
17
 
18
+ from memorymaster._storage_shared import ConcurrentModificationError
19
19
  from memorymaster.lifecycle import transition_claim
20
+ from memorymaster.llm_provider import call_llm
20
21
  from memorymaster.models import Claim
21
22
 
22
23
  logger = logging.getLogger(__name__)
23
24
 
24
- DEFAULT_OLLAMA_URL = "http://localhost:11434"
25
- DEFAULT_MODEL = "deepseek-coder-v2:16b"
26
-
27
25
  RESOLUTION_PROMPT = """You are a memory quality evaluator. Two claims contradict each other.
28
26
  Decide which one should be KEPT based on:
29
27
  1. Recency (newer information is usually more accurate)
@@ -47,49 +45,34 @@ Return JSON only: {{"winner": "A" or "B", "reason": "brief explanation"}}"""
47
45
 
48
46
 
49
47
  def _llm_evaluate(prompt: str, model: str = "", base_url: str = "") -> dict:
50
- """Call LLM and parse JSON response.
48
+ """Ask the configured LLM to pick the winner and parse its JSON response.
51
49
 
52
- Returns empty dict {} if LLM returns invalid JSON or connection fails.
50
+ Returns empty dict {} if the LLM returns invalid JSON or the call fails.
51
+ The `model` and `base_url` kwargs are kept for backwards compatibility but
52
+ are no longer consulted — provider routing is centralized in llm_provider.
53
53
  """
54
- url = (base_url or os.environ.get("OLLAMA_URL") or DEFAULT_OLLAMA_URL).rstrip("/")
55
- mdl = model or os.environ.get("RESOLVER_LLM_MODEL") or DEFAULT_MODEL
56
-
57
- body = json.dumps({
58
- "model": mdl,
59
- "messages": [{"role": "user", "content": prompt}],
60
- "stream": False,
61
- "options": {"temperature": 0.1, "num_predict": 200},
62
- }).encode()
63
-
64
- req = urllib.request.Request(
65
- f"{url}/api/chat",
66
- data=body,
67
- headers={"Content-Type": "application/json"},
68
- method="POST",
69
- )
70
54
  try:
71
- with urllib.request.urlopen(req, timeout=30) as resp:
72
- result = json.loads(resp.read())
73
- raw = result.get("message", {}).get("content", "")
74
- # Parse JSON from response
75
- text = raw.strip()
76
- if text.startswith("```"):
77
- lines = text.split("\n")
78
- lines = [line for line in lines if not line.strip().startswith("```")]
79
- text = "\n".join(lines)
80
- try:
81
- parsed = json.loads(text)
82
- if not isinstance(parsed, dict):
83
- logger.warning("LLM returned non-dict JSON: %s", type(parsed))
84
- return {}
85
- return parsed
86
- except json.JSONDecodeError as exc:
87
- logger.warning("LLM returned invalid JSON: %s (text: %s)", exc, text[:100])
88
- return {}
55
+ raw = call_llm(prompt, "")
89
56
  except Exception as exc:
90
57
  logger.warning("LLM conflict evaluation failed: %s", exc)
91
58
  return {}
92
59
 
60
+ text = (raw or "").strip()
61
+ if text.startswith("```"):
62
+ lines = [line for line in text.split("\n") if not line.strip().startswith("```")]
63
+ text = "\n".join(lines)
64
+
65
+ try:
66
+ parsed = json.loads(text)
67
+ except json.JSONDecodeError as exc:
68
+ logger.warning("LLM returned invalid JSON: %s (text: %s)", exc, text[:100])
69
+ return {}
70
+
71
+ if not isinstance(parsed, dict):
72
+ logger.warning("LLM returned non-dict JSON: %s", type(parsed))
73
+ return {}
74
+ return parsed
75
+
93
76
 
94
77
  def _cite_summary(claim: Claim) -> str:
95
78
  """Summarize citations for LLM prompt."""
@@ -132,7 +115,7 @@ def resolve_conflict_pair(
132
115
  loser = claim_b if winner_letter == "A" else claim_a
133
116
 
134
117
  try:
135
- transition_claim(
118
+ updated = transition_claim(
136
119
  store,
137
120
  claim_id=loser.id,
138
121
  to_status="superseded",
@@ -140,12 +123,35 @@ def resolve_conflict_pair(
140
123
  event_type="validator",
141
124
  replaced_by_claim_id=winner.id,
142
125
  )
126
+ current_replacement_id = getattr(updated, "replaced_by_claim_id", None)
127
+ if current_replacement_id != winner.id:
128
+ return {
129
+ "resolved": False,
130
+ "reason": "lost_race",
131
+ "winner_id": winner.id,
132
+ "loser_id": loser.id,
133
+ "current_replacement_id": current_replacement_id,
134
+ }
135
+ if hasattr(store, "set_supersedes"):
136
+ store.set_supersedes(winner.id, loser.id)
143
137
  return {
144
138
  "resolved": True,
145
139
  "winner_id": winner.id,
146
140
  "loser_id": loser.id,
147
141
  "reason": reason,
148
142
  }
143
+ except ConcurrentModificationError as exc:
144
+ current = store.get_claim(loser.id, include_citations=False)
145
+ if current is not None and current.status == "superseded":
146
+ return {
147
+ "resolved": False,
148
+ "reason": "lost_race",
149
+ "winner_id": winner.id,
150
+ "loser_id": loser.id,
151
+ "current_replacement_id": current.replaced_by_claim_id,
152
+ }
153
+ logger.warning("Failed to resolve conflict %d vs %d: %s", claim_a.id, claim_b.id, exc)
154
+ return {"resolved": False, "reason": str(exc)}
149
155
  except Exception as exc:
150
156
  logger.warning("Failed to resolve conflict %d vs %d: %s", claim_a.id, claim_b.id, exc)
151
157
  return {"resolved": False, "reason": str(exc)}
@@ -16,7 +16,6 @@ import os
16
16
  import re
17
17
  import sqlite3
18
18
  import subprocess
19
- from pathlib import Path
20
19
  from typing import Any
21
20
 
22
21
  logger = logging.getLogger(__name__)