memorymaster 3.13.0__tar.gz → 3.14.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 (361) hide show
  1. {memorymaster-3.13.0/memorymaster.egg-info → memorymaster-3.14.0}/PKG-INFO +24 -28
  2. {memorymaster-3.13.0 → memorymaster-3.14.0}/README.md +23 -27
  3. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/_storage_lifecycle.py +60 -10
  4. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/auto_resolver.py +51 -45
  5. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/claim_verifier.py +0 -1
  6. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/cli.py +53 -4
  7. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/cli_handlers_basic.py +155 -3
  8. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config.py +76 -0
  9. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +2 -1
  10. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +3 -1
  11. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +2 -1
  12. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/conflict_resolver.py +113 -2
  13. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/context_optimizer.py +161 -19
  14. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/dashboard.py +391 -1
  15. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/db_merge.py +133 -11
  16. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/dream_bridge.py +12 -5
  17. memorymaster-3.14.0/memorymaster/jobs/calibration.py +120 -0
  18. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/jobs/compact_summaries.py +6 -2
  19. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/jobs/compactor.py +93 -12
  20. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/jobs/decay.py +62 -1
  21. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/jobs/dedup.py +155 -2
  22. memorymaster-3.14.0/memorymaster/jobs/entity_graph_export.py +210 -0
  23. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/lifecycle.py +31 -1
  24. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/llm_provider.py +73 -2
  25. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/mcp_server.py +378 -34
  26. memorymaster-3.14.0/memorymaster/mcp_usage.py +43 -0
  27. memorymaster-3.14.0/memorymaster/observability.py +183 -0
  28. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/postgres_store.py +16 -0
  29. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/schema.sql +9 -0
  30. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/schema_postgres.sql +9 -0
  31. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/security.py +7 -2
  32. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/service.py +311 -7
  33. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/setup_hooks.py +1 -1
  34. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/snapshot.py +64 -0
  35. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/steward.py +16 -14
  36. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/storage.py +0 -27
  37. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/vault_linter.py +164 -13
  38. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/vault_query_capture.py +1 -1
  39. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/verbatim_store.py +10 -4
  40. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/wiki_engine.py +238 -22
  41. memorymaster-3.14.0/memorymaster/wiki_suggest.py +270 -0
  42. {memorymaster-3.13.0 → memorymaster-3.14.0/memorymaster.egg-info}/PKG-INFO +24 -28
  43. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster.egg-info/SOURCES.txt +44 -0
  44. {memorymaster-3.13.0 → memorymaster-3.14.0}/pyproject.toml +1 -1
  45. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_auto_resolver.py +10 -18
  46. memorymaster-3.14.0/tests/test_calibration.py +134 -0
  47. memorymaster-3.14.0/tests/test_calibration_priors_applied.py +20 -0
  48. memorymaster-3.14.0/tests/test_cli_dry_run.py +142 -0
  49. memorymaster-3.14.0/tests/test_compact_summaries_sensitivity.py +69 -0
  50. memorymaster-3.14.0/tests/test_compactor_artifact_order.py +40 -0
  51. memorymaster-3.14.0/tests/test_context_optimizer_provider.py +145 -0
  52. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_dashboard.py +96 -1
  53. memorymaster-3.14.0/tests/test_dashboard_coverage.py +321 -0
  54. memorymaster-3.14.0/tests/test_dashboard_latency.py +111 -0
  55. memorymaster-3.14.0/tests/test_dashboard_lineage.py +70 -0
  56. memorymaster-3.14.0/tests/test_dashboard_review_queue.py +143 -0
  57. memorymaster-3.14.0/tests/test_db_merge_confidence_conflict.py +71 -0
  58. memorymaster-3.14.0/tests/test_db_merge_coverage_v2.py +260 -0
  59. memorymaster-3.14.0/tests/test_decay_coverage.py +212 -0
  60. memorymaster-3.14.0/tests/test_decay_respects_pinned.py +120 -0
  61. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_dedup.py +9 -0
  62. memorymaster-3.14.0/tests/test_dedup_cli.py +120 -0
  63. memorymaster-3.14.0/tests/test_dedup_conflict_disambiguation.py +52 -0
  64. memorymaster-3.14.0/tests/test_dream_bridge_coverage_v2.py +186 -0
  65. memorymaster-3.14.0/tests/test_dream_bridge_sensitivity.py +76 -0
  66. memorymaster-3.14.0/tests/test_entity_graph_export.py +147 -0
  67. memorymaster-3.14.0/tests/test_federated_query_safety.py +91 -0
  68. memorymaster-3.14.0/tests/test_lifecycle_supersede_invariant.py +87 -0
  69. memorymaster-3.14.0/tests/test_llm_provider_key_rotation.py +80 -0
  70. memorymaster-3.14.0/tests/test_mcp_filter_bypass.py +91 -0
  71. memorymaster-3.14.0/tests/test_mcp_rate_limit.py +101 -0
  72. memorymaster-3.14.0/tests/test_mcp_server_validation.py +49 -0
  73. memorymaster-3.14.0/tests/test_mcp_usage.py +95 -0
  74. memorymaster-3.14.0/tests/test_meta_decisions.py +125 -0
  75. memorymaster-3.14.0/tests/test_observability.py +47 -0
  76. memorymaster-3.14.0/tests/test_resolvers_concurrent_supersede.py +148 -0
  77. memorymaster-3.14.0/tests/test_retrieval_profile.py +120 -0
  78. memorymaster-3.14.0/tests/test_sensitivity_filter_t07.py +63 -0
  79. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_snapshot.py +2 -1
  80. memorymaster-3.14.0/tests/test_snapshot_roundtrip.py +168 -0
  81. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_sqlite_core.py +9 -0
  82. memorymaster-3.14.0/tests/test_storage_parity.py +67 -0
  83. memorymaster-3.14.0/tests/test_vault_linter_orphan.py +91 -0
  84. memorymaster-3.14.0/tests/test_verbatim_store.py +67 -0
  85. memorymaster-3.14.0/tests/test_verbatim_store_qdrant.py +120 -0
  86. memorymaster-3.14.0/tests/test_wiki_autopromote.py +101 -0
  87. memorymaster-3.14.0/tests/test_wiki_engine_idempotency.py +170 -0
  88. memorymaster-3.14.0/tests/test_wiki_suggest.py +124 -0
  89. {memorymaster-3.13.0 → memorymaster-3.14.0}/LICENSE +0 -0
  90. {memorymaster-3.13.0 → memorymaster-3.14.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
  91. {memorymaster-3.13.0 → memorymaster-3.14.0}/benchmarks/longmemeval_runner.py +0 -0
  92. {memorymaster-3.13.0 → memorymaster-3.14.0}/benchmarks/longmemeval_vector_runner.py +0 -0
  93. {memorymaster-3.13.0 → memorymaster-3.14.0}/benchmarks/perf_smoke.py +0 -0
  94. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/__init__.py +0 -0
  95. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/__main__.py +0 -0
  96. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/_storage_read.py +0 -0
  97. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/_storage_schema.py +0 -0
  98. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/_storage_shared.py +0 -0
  99. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/_storage_sources.py +0 -0
  100. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/_storage_write_claims.py +0 -0
  101. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/access_control.py +0 -0
  102. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/action_exporters.py +0 -0
  103. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/action_extractor.py +0 -0
  104. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/atlas_claim_extractor.py +0 -0
  105. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/atlas_contract.py +0 -0
  106. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/auto_extractor.py +0 -0
  107. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/candidate_dedupe.py +0 -0
  108. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/claim_edges.py +0 -0
  109. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/cli_handlers_curation.py +0 -0
  110. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/cli_helpers.py +0 -0
  111. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/closets.py +0 -0
  112. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/claude-md-append.md +0 -0
  113. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
  114. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
  115. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
  116. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
  117. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
  118. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
  119. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/connectors/__init__.py +0 -0
  120. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/connectors/whatsapp.py +0 -0
  121. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/context_hook.py +0 -0
  122. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/daily_notes.py +0 -0
  123. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/embeddings.py +0 -0
  124. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/entity_extractor.py +0 -0
  125. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/entity_graph.py +0 -0
  126. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/entity_registry.py +0 -0
  127. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/federated_graphify.py +0 -0
  128. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/feedback.py +0 -0
  129. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/graph_store.py +0 -0
  130. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/hook_log.py +0 -0
  131. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/jobs/__init__.py +0 -0
  132. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/jobs/deterministic.py +0 -0
  133. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/jobs/extractor.py +0 -0
  134. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/jobs/staleness.py +0 -0
  135. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/jobs/validator.py +0 -0
  136. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/key_rotator.py +0 -0
  137. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/llm_steward.py +0 -0
  138. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/media_processing.py +0 -0
  139. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/media_providers.py +0 -0
  140. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/metrics_exporter.py +0 -0
  141. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/models.py +0 -0
  142. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/operator.py +0 -0
  143. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/operator_queue.py +0 -0
  144. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/plugins.py +0 -0
  145. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/policy.py +0 -0
  146. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/qdrant_backend.py +0 -0
  147. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/qdrant_recall_fallback.py +0 -0
  148. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/qmd_bridge.py +0 -0
  149. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/query_classifier.py +0 -0
  150. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/query_expansion.py +0 -0
  151. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/recall_fusion.py +0 -0
  152. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/recall_tokenizer.py +0 -0
  153. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/retrieval.py +0 -0
  154. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/retry.py +0 -0
  155. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/review.py +0 -0
  156. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/rl_trainer.py +0 -0
  157. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/scheduler.py +0 -0
  158. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/schema.py +0 -0
  159. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/scope_utils.py +0 -0
  160. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/session_tracker.py +0 -0
  161. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/skill_evolver.py +0 -0
  162. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/steward_classifier.py +0 -0
  163. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/steward_features.py +0 -0
  164. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/store_factory.py +0 -0
  165. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/transcript_miner.py +0 -0
  166. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/turn_schema.py +0 -0
  167. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/vault_bases.py +0 -0
  168. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/vault_curator.py +0 -0
  169. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/vault_exporter.py +0 -0
  170. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/vault_log.py +0 -0
  171. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/vault_synthesis.py +0 -0
  172. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/verbatim_recall.py +0 -0
  173. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/webhook.py +0 -0
  174. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/wiki_freshness.py +0 -0
  175. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/wiki_similarity.py +0 -0
  176. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster/wiki_validate.py +0 -0
  177. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster.egg-info/dependency_links.txt +0 -0
  178. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster.egg-info/entry_points.txt +0 -0
  179. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster.egg-info/requires.txt +0 -0
  180. {memorymaster-3.13.0 → memorymaster-3.14.0}/memorymaster.egg-info/top_level.txt +0 -0
  181. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/agg_recall_latency.py +0 -0
  182. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/alert_operator_metrics.py +0 -0
  183. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/audit_dedupe_precision.py +0 -0
  184. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/autoresearch_daemon.py +0 -0
  185. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/backfill_entity_extraction.py +0 -0
  186. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/backfill_graph_store.py +0 -0
  187. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/backfill_stop_hook_citations.py +0 -0
  188. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/backtest_steward_classifier.py +0 -0
  189. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/build_steward_training_set.py +0 -0
  190. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/check_hook_template_drift.py +0 -0
  191. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/claude_to_turns.py +0 -0
  192. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/codex_live_to_turns.py +0 -0
  193. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/compaction_edge_cases.py +0 -0
  194. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/compaction_trace_report.py +0 -0
  195. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/compaction_trace_validate.py +0 -0
  196. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/confusion_matrix_eval.py +0 -0
  197. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/conversation_importer.py +0 -0
  198. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/conversation_to_turns.py +0 -0
  199. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/e2e_operator.py +0 -0
  200. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/email_live_to_turns.py +0 -0
  201. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/eval_bm25_sweep.py +0 -0
  202. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/eval_classify_f1.py +0 -0
  203. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/eval_memorymaster.py +0 -0
  204. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/eval_recall_precision_at_5.py +0 -0
  205. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/eval_recall_quality.py +0 -0
  206. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/eval_steward_pareto.py +0 -0
  207. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/eval_verbatim_recall.py +0 -0
  208. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/expand_recall_eval.py +0 -0
  209. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/generate_drill_signoff.py +0 -0
  210. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/git_to_turns.py +0 -0
  211. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/github_live_to_turns.py +0 -0
  212. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/gitnexus_to_claims.py +0 -0
  213. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/grid_recall_weights.py +0 -0
  214. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/index_claims_to_qdrant.py +0 -0
  215. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/ingest_planning_docs.py +0 -0
  216. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/jira_live_to_turns.py +0 -0
  217. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/label_prompts_with_judge.py +0 -0
  218. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/llm_benchmark.py +0 -0
  219. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/measure_dedupe_thresholds.py +0 -0
  220. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/merge_scope_variants.py +0 -0
  221. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/messages_to_turns.py +0 -0
  222. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/operator_metrics.py +0 -0
  223. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/precompute_candidates.py +0 -0
  224. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/recurring_incident_drill.py +0 -0
  225. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/release_readiness.py +0 -0
  226. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/run_codex_autologger.py +0 -0
  227. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/run_incident_drill.py +0 -0
  228. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/run_longmemeval.py +0 -0
  229. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/scheduled_ingest.py +0 -0
  230. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/setup-hooks.py +0 -0
  231. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/slack_live_to_turns.py +0 -0
  232. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/sync_hook_templates.py +0 -0
  233. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/tickets_to_turns.py +0 -0
  234. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/train_steward_classifier.py +0 -0
  235. {memorymaster-3.13.0 → memorymaster-3.14.0}/scripts/webhook_to_turns.py +0 -0
  236. {memorymaster-3.13.0 → memorymaster-3.14.0}/setup.cfg +0 -0
  237. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/conftest.py +0 -0
  238. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
  239. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_access_control.py +0 -0
  240. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_action_exporters.py +0 -0
  241. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_action_extractor.py +0 -0
  242. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_atlas_claim_extractor.py +0 -0
  243. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_atlas_contract.py +0 -0
  244. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_atlas_source_schema.py +0 -0
  245. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_auto_extractor.py +0 -0
  246. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_auto_ingest_hook_citations.py +0 -0
  247. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_auto_ingest_hook_schema.py +0 -0
  248. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_auto_validate.py +0 -0
  249. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_bm25_per_field.py +0 -0
  250. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_candidate_dedupe.py +0 -0
  251. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_claim_edges.py +0 -0
  252. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_claim_links.py +0 -0
  253. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_claim_type_ranking.py +0 -0
  254. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_classify_hook_f1.py +0 -0
  255. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_classify_hook_latency.py +0 -0
  256. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_claude_to_turns.py +0 -0
  257. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_cli_json_flag.py +0 -0
  258. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_cli_ready.py +0 -0
  259. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_cli_review_queue.py +0 -0
  260. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_cli_subcommands.py +0 -0
  261. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_closets.py +0 -0
  262. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_closets_recall_integration.py +0 -0
  263. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_compact_summaries.py +0 -0
  264. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_compaction_trace.py +0 -0
  265. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_config.py +0 -0
  266. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_conflict_resolver.py +0 -0
  267. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_confusion_matrix_eval.py +0 -0
  268. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_connection_retry.py +0 -0
  269. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_connectors.py +0 -0
  270. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_context_hook.py +0 -0
  271. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_context_optimizer.py +0 -0
  272. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_conversation_to_turns.py +0 -0
  273. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_deterministic_predicates.py +0 -0
  274. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_embeddings_coverage.py +0 -0
  275. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_entity_extractor.py +0 -0
  276. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_entity_extractor_llm.py +0 -0
  277. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_entity_graph.py +0 -0
  278. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_entity_new_kinds.py +0 -0
  279. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_entity_regex_v3.py +0 -0
  280. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_entity_registry.py +0 -0
  281. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_eval_harness.py +0 -0
  282. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_events_schema.py +0 -0
  283. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_extract_llm_ollama.py +0 -0
  284. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_federated_graphify_mcp.py +0 -0
  285. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_feedback.py +0 -0
  286. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_fts5_search.py +0 -0
  287. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_graph_distance.py +0 -0
  288. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_graph_store.py +0 -0
  289. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_handler_regressions.py +0 -0
  290. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_hook_env_isolation.py +0 -0
  291. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_human_id.py +0 -0
  292. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_incident_drill_runner.py +0 -0
  293. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_integration_workflows.py +0 -0
  294. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_key_rotator.py +0 -0
  295. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_lifecycle.py +0 -0
  296. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_llm_fallback.py +0 -0
  297. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_llm_provider_claude_cli.py +0 -0
  298. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_llm_steward_coverage.py +0 -0
  299. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_llm_steward_key_rotation.py +0 -0
  300. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_mcp_helpers.py +0 -0
  301. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_media_processing.py +0 -0
  302. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_metrics_exporter.py +0 -0
  303. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_obsidian_mind_patterns.py +0 -0
  304. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_operator.py +0 -0
  305. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_operator_queue.py +0 -0
  306. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_perf_smoke_config.py +0 -0
  307. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_plugins.py +0 -0
  308. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_policy_coverage.py +0 -0
  309. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_policy_mode_env.py +0 -0
  310. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_postgres_parity.py +0 -0
  311. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_qdrant_backend.py +0 -0
  312. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_qmd_bridge.py +0 -0
  313. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_query_classifier.py +0 -0
  314. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_query_expansion.py +0 -0
  315. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_recall_entity_fanout.py +0 -0
  316. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_recall_fusion.py +0 -0
  317. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_recall_latency.py +0 -0
  318. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_recall_precision_at_5.py +0 -0
  319. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_recall_tokenizer.py +0 -0
  320. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_recall_vector_fallback.py +0 -0
  321. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_reliability_hardening.py +0 -0
  322. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_review.py +0 -0
  323. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_rl_trainer.py +0 -0
  324. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_rrf_auto_gate.py +0 -0
  325. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_scheduler.py +0 -0
  326. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_schema.py +0 -0
  327. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_scope_boost.py +0 -0
  328. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_scope_utils.py +0 -0
  329. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_security_access.py +0 -0
  330. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_security_patterns.py +0 -0
  331. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
  332. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
  333. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_service_coverage.py +0 -0
  334. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_session_tracker.py +0 -0
  335. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_staleness.py +0 -0
  336. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_stealth_mode.py +0 -0
  337. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_steward.py +0 -0
  338. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_steward_classifier.py +0 -0
  339. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_steward_features.py +0 -0
  340. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_steward_features_v3.py +0 -0
  341. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_steward_resolution_parity.py +0 -0
  342. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_store_factory.py +0 -0
  343. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_tenant_isolation.py +0 -0
  344. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_turn_schema.py +0 -0
  345. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_two_pass_recall.py +0 -0
  346. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_v311_fixes.py +0 -0
  347. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_v313_e2e.py +0 -0
  348. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
  349. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_v390_e2e.py +0 -0
  350. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_v391_strict_warnings.py +0 -0
  351. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_vault_exporter.py +0 -0
  352. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_vector_search.py +0 -0
  353. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_verbatim_dedup.py +0 -0
  354. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_verbatim_recall.py +0 -0
  355. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_webhook.py +0 -0
  356. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_whatsapp_importer.py +0 -0
  357. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_wiki_binding.py +0 -0
  358. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
  359. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_wiki_freshness.py +0 -0
  360. {memorymaster-3.13.0 → memorymaster-3.14.0}/tests/test_wiki_similarity_multiscope.py +0 -0
  361. {memorymaster-3.13.0 → memorymaster-3.14.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.14.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
@@ -61,29 +61,23 @@ MemoryMaster prevents the #1 problem with agent memory: **drift, stale assumptio
61
61
 
62
62
  ## Architecture
63
63
 
64
+ MemoryMaster is layered around MCP/CLI entry points, the `MemoryService` facade, SQLite/Postgres
65
+ storage, optional Qdrant vector search, scheduled jobs, and the Obsidian wiki/vault layer. The
66
+ canonical ingest path is:
67
+
68
+ ```text
69
+ MCP/CLI -> sensitivity filter -> MemoryService.ingest -> store write -> FTS5 index
64
70
  ```
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
71
+
72
+ The query path is:
73
+
74
+ ```text
75
+ query_memory -> MemoryService.query -> storage reads + optional Qdrant candidates -> ranked context
85
76
  ```
86
77
 
78
+ See [docs/architecture.md](docs/architecture.md) for the current module map, data-flow details,
79
+ recent PR status, and sensitivity-filter invariants.
80
+
87
81
  ## Key features
88
82
 
89
83
  - **6-state lifecycle**: `candidate` → `confirmed` → `stale` → `superseded` → `conflicted` → `archived`
@@ -104,18 +98,20 @@ Full feature index lives in [`docs/handbook.md`](docs/handbook.md).
104
98
 
105
99
  ## Prerequisites
106
100
 
107
- **Required**
101
+ **Required (the package won't function without these)**
108
102
 
109
103
  - Python **3.10+** with `pip`
110
104
  - Claude Code, Codex, or any MCP-compatible agent
105
+ - **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.
106
+
107
+ **Strongly recommended (you'll lose ~80% of the value without these)**
108
+
109
+ - **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.
110
+ - **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
111
 
112
- **Optional**
112
+ **Optional (nice to have)**
113
113
 
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)
114
+ - **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
115
 
120
116
  ## Quick start
121
117
 
@@ -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
 
@@ -23,14 +23,14 @@ from memorymaster.models import (
23
23
  validate_transition_event_type,
24
24
  )
25
25
 
26
- logger = logging.getLogger(__name__)
27
-
28
26
  from memorymaster._storage_shared import (
29
27
  EVENT_HASH_ALGO,
30
28
  ConcurrentModificationError,
31
29
  utc_now,
32
30
  )
33
31
 
32
+ logger = logging.getLogger(__name__)
33
+
34
34
 
35
35
  class _LifecycleMixin:
36
36
 
@@ -102,14 +102,64 @@ class _LifecycleMixin:
102
102
  old_claim = self.get_claim(old_claim_id, include_citations=False)
103
103
  if old_claim is None:
104
104
  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)
105
+ if old_claim.status == "superseded" or old_claim.replaced_by_claim_id is not None:
106
+ raise ConcurrentModificationError(
107
+ f"Claim {old_claim_id} was already superseded. Reload and retry."
108
+ )
109
+
110
+ now = utc_now()
111
+ with self.connect() as conn:
112
+ target = conn.execute(
113
+ "SELECT supersedes_claim_id FROM claims WHERE id = ?",
114
+ (new_claim_id,),
115
+ ).fetchone()
116
+ if target is None:
117
+ raise ValueError(f"Replacement claim {new_claim_id} does not exist.")
118
+ if target["supersedes_claim_id"] not in {None, old_claim_id}:
119
+ raise ConcurrentModificationError(
120
+ f"Claim {new_claim_id} already supersedes another claim. Reload and retry."
121
+ )
122
+
123
+ cur = conn.execute(
124
+ """
125
+ UPDATE claims
126
+ SET status = 'superseded', updated_at = ?, replaced_by_claim_id = ?,
127
+ version = version + 1, valid_until = COALESCE(?, valid_until)
128
+ WHERE id = ? AND version = ? AND status != 'superseded'
129
+ AND replaced_by_claim_id IS NULL
130
+ """,
131
+ (now, new_claim_id, now, old_claim_id, old_claim.version),
132
+ )
133
+ if cur.rowcount == 0:
134
+ raise ConcurrentModificationError(
135
+ f"Claim {old_claim_id} was modified by another writer. Reload and retry."
136
+ )
137
+
138
+ cur = conn.execute(
139
+ """
140
+ UPDATE claims
141
+ SET supersedes_claim_id = ?, updated_at = ?
142
+ WHERE id = ? AND (supersedes_claim_id IS NULL OR supersedes_claim_id = ?)
143
+ """,
144
+ (old_claim_id, now, new_claim_id, old_claim_id),
145
+ )
146
+ if cur.rowcount == 0:
147
+ conn.rollback()
148
+ raise ConcurrentModificationError(
149
+ f"Claim {new_claim_id} was modified by another writer. Reload and retry."
150
+ )
151
+
152
+ self._insert_event_row(
153
+ conn,
154
+ claim_id=old_claim.id,
155
+ event_type="supersession",
156
+ from_status=old_claim.status,
157
+ to_status="superseded",
158
+ details=reason,
159
+ payload_json=json.dumps({"replaced_by_claim_id": new_claim_id}),
160
+ created_at=now,
161
+ )
162
+ conn.commit()
113
163
 
114
164
 
115
165
  def delete_old_events(self, retain_days: int) -> int:
@@ -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__)
@@ -13,13 +13,29 @@ from memorymaster.cli_helpers import ( # noqa: F401 — re-export for backward
13
13
  parse_citation,
14
14
  parse_scope_allowlist,
15
15
  )
16
- from memorymaster.context_optimizer import OUTPUT_FORMATS
16
+ from memorymaster.context_optimizer import OUTPUT_FORMATS, PROVIDERS
17
17
  from memorymaster.models import CLAIM_LINK_TYPES, CLAIM_STATUSES, VOLATILITY_LEVELS
18
18
  from memorymaster.retrieval import RETRIEVAL_MODES
19
- from memorymaster.service import MemoryService
19
+ from memorymaster.service import RETRIEVAL_PROFILES, MemoryService
20
20
 
21
21
  # Import dispatch table — this also triggers the late dispatch additions for daily/dream/ghost
22
22
  from memorymaster.cli_handlers_curation import COMMAND_HANDLERS
23
+ from memorymaster.cli_handlers_basic import (
24
+ _handle_decay,
25
+ _handle_entity_graph_export,
26
+ _handle_recompute_confidence_priors,
27
+ _handle_wiki_suggest_links,
28
+ handle_mcp_usage_report,
29
+ )
30
+
31
+
32
+ COMMAND_HANDLERS["entity-graph-export"] = _handle_entity_graph_export
33
+ COMMAND_HANDLERS["recompute-confidence-priors"] = _handle_recompute_confidence_priors
34
+ COMMAND_HANDLERS["wiki-suggest-links"] = _handle_wiki_suggest_links
35
+ COMMAND_HANDLERS["decay"] = _handle_decay
36
+ COMMAND_HANDLERS["mcp-usage-report"] = (
37
+ lambda args, service, parser, effective_db: handle_mcp_usage_report(args, effective_db)
38
+ )
23
39
 
24
40
 
25
41
  def build_parser() -> argparse.ArgumentParser:
@@ -147,6 +163,7 @@ def build_parser() -> argparse.ArgumentParser:
147
163
  query.add_argument("--exclude-conflicted", action="store_true", help="Only return confirmed/stale")
148
164
  query.add_argument("--include-candidates", action="store_true", help="Also search candidate (unverified) claims")
149
165
  query.add_argument("--retrieval-mode", choices=list(RETRIEVAL_MODES), default="legacy", help="Retrieval mode (legacy SQL ordering or hybrid lexical/confidence/freshness ranking)")
166
+ query.add_argument("--profile", choices=list(RETRIEVAL_PROFILES), default=None, help="Per-query hybrid retrieval profile")
150
167
  query.add_argument("--allow-sensitive", action="store_true", help="Include claims that look sensitive (default excludes them)")
151
168
  query.add_argument("--scope-allowlist", default="", help="Comma-separated scopes to include (e.g. project,team_x)")
152
169
  query.add_argument("--as-of", default="", help="Temporal query: show claims valid at this ISO timestamp")
@@ -156,11 +173,13 @@ def build_parser() -> argparse.ArgumentParser:
156
173
  context.add_argument("text", help="Query text describing what context is needed")
157
174
  context.add_argument("--budget", type=int, default=4000, help="Maximum token budget (default: 4000)")
158
175
  context.add_argument("--format", dest="output_format", choices=list(OUTPUT_FORMATS), default="text", help="Output format: text (human-readable), xml (system prompt), json (structured)")
176
+ context.add_argument("--provider", choices=list(PROVIDERS), default=None, help="Provider-aware packing strategy")
159
177
  context.add_argument("--limit", type=int, default=100, help="Max candidate claims to rank")
160
178
  context.add_argument("--exclude-stale", action="store_true", help="Exclude stale claims")
161
179
  context.add_argument("--exclude-conflicted", action="store_true", help="Exclude conflicted claims")
162
180
  context.add_argument("--include-candidates", action="store_true", help="Include candidate (unverified) claims")
163
181
  context.add_argument("--retrieval-mode", choices=list(RETRIEVAL_MODES), default="hybrid", help="Retrieval mode (default: hybrid)")
182
+ context.add_argument("--profile", choices=list(RETRIEVAL_PROFILES), default=None, help="Per-query hybrid retrieval profile")
164
183
  context.add_argument("--allow-sensitive", action="store_true", help="Include sensitive claims")
165
184
  context.add_argument("--scope-allowlist", default="", help="Comma-separated scopes to include")
166
185
 
@@ -180,6 +199,7 @@ def build_parser() -> argparse.ArgumentParser:
180
199
  compact = sub.add_parser("compact", help="Archive stale/superseded/conflicted claims and trim old events")
181
200
  compact.add_argument("--retain-days", type=int, default=30, help="Days before archiving stale/superseded/conflicted claims")
182
201
  compact.add_argument("--event-retain-days", type=int, default=60, help="Days to retain event history")
202
+ compact.add_argument("--dry-run", action="store_true", help="Preview compaction without archiving claims, deleting events, or writing artifacts")
183
203
 
184
204
  compact_sum = sub.add_parser("compact-summaries", help="Summarize groups of archived claims into higher-level summary claims using LLM")
185
205
  compact_sum.add_argument("--provider", default="gemini", choices=["gemini", "openai", "anthropic", "ollama", "custom"], help="LLM provider (default: gemini)")
@@ -197,10 +217,24 @@ def build_parser() -> argparse.ArgumentParser:
197
217
  dedup = sub.add_parser("dedup", help="Detect and merge duplicate claims using embedding similarity")
198
218
  dedup.add_argument("--threshold", type=float, default=0.92, help="Cosine similarity threshold for duplicate detection (default: 0.92)")
199
219
  dedup.add_argument("--min-text-overlap", type=float, default=0.3, help="Minimum word-level Jaccard overlap as secondary gate (default: 0.3)")
220
+ dedup.add_argument("--limit", type=int, default=None, help="Maximum claims to scan, oldest first")
221
+ dedup.add_argument("--scope", default=None, help="Only scan claims with this exact scope")
200
222
  dedup.add_argument("--dry-run", action="store_true", help="Preview duplicates without archiving")
201
223
 
224
+ decay_cmd = sub.add_parser("decay", help="Apply confidence decay and mark low-confidence claims stale")
225
+ decay_cmd.add_argument("--limit", type=int, default=200, help="Maximum confirmed claims to process")
226
+ decay_cmd.add_argument("--stale-threshold", type=float, default=None, help="Confidence threshold below which claims become stale")
227
+ decay_cmd.add_argument("--dry-run", action="store_true", help="Preview confidence decay and stale transitions without writing")
228
+
202
229
  sub.add_parser("recompute-tiers", help="Recompute memory tiers (core/working/peripheral) for all claims")
203
230
 
231
+ confidence_priors = sub.add_parser(
232
+ "recompute-confidence-priors",
233
+ help="Write recommended initial-confidence priors from recent validator events",
234
+ )
235
+ confidence_priors.add_argument("--window-days", type=int, default=90, help="Number of recent event days to read")
236
+ confidence_priors.add_argument("--output", required=True, help="JSON report path to write")
237
+
204
238
  list_claims = sub.add_parser("list-claims", help="List claims")
205
239
  list_claims.add_argument("--status", choices=list(CLAIM_STATUSES), help="Filter by claim status")
206
240
  list_claims.add_argument("--limit", type=int, default=50, help="Maximum rows")
@@ -221,6 +255,10 @@ def build_parser() -> argparse.ArgumentParser:
221
255
  export_metrics.add_argument("--out-prom", default="artifacts/metrics/metrics.prom", help="Output path for Prometheus text metrics")
222
256
  export_metrics.add_argument("--out-json", default="artifacts/metrics/metrics_snapshot.json", help="Output path for structured metrics JSON snapshot")
223
257
 
258
+ mcp_usage = sub.add_parser("mcp-usage-report", help="Export MCP tool usage for a time window")
259
+ mcp_usage.add_argument("--since", default="7d", help="Window start: 7d, 30d, 1d, or ISO date")
260
+ mcp_usage.add_argument("--format", default="csv", help="Output format (default: csv)")
261
+
224
262
  review_queue = sub.add_parser("review-queue", help="Build conflict/stale review queue")
225
263
  review_queue.add_argument("--limit", type=int, default=100, help="Maximum claims scanned for queue")
226
264
  review_queue.add_argument("--exclude-stale", action="store_true", help="Exclude stale claims from queue")
@@ -390,6 +428,12 @@ def build_parser() -> argparse.ArgumentParser:
390
428
  wiki_freshness.add_argument("--below", type=float, default=None, help="Only show articles with freshness_score below this threshold (0-1)")
391
429
  wiki_freshness.add_argument("--threshold-days", type=int, default=None, help="Only show articles older than N days since last absorb (alias for --below)")
392
430
 
431
+ wiki_suggest = sub.add_parser("wiki-suggest-links", help="Suggest wiki article links from paragraph entities")
432
+ wiki_suggest.add_argument("--text", required=True, help="Current paragraph text")
433
+ wiki_suggest.add_argument("--wiki-root", default="obsidian-vault/wiki", help="Wiki root to scan for article slugs")
434
+ wiki_suggest.add_argument("--limit", type=int, default=10, help="Maximum suggestions")
435
+ wiki_suggest.add_argument("--hops", type=int, default=2, help="Maximum entity graph hops")
436
+
393
437
  mine_cmd = sub.add_parser("mine-transcript", help="Parse Claude Code transcripts into claims")
394
438
  mine_cmd.add_argument("--input", required=True, help="JSONL transcript file or directory")
395
439
  mine_cmd.add_argument("--scope", default="project", help="Scope for ingested claims")
@@ -405,6 +449,11 @@ def build_parser() -> argparse.ArgumentParser:
405
449
 
406
450
  sub.add_parser("entity-stats", help="Show entity graph statistics")
407
451
 
452
+ entity_graph_export = sub.add_parser("entity-graph-export", help="Export entity graph relationships as DOT or GraphML")
453
+ entity_graph_export.add_argument("--format", choices=["dot", "graphml"], required=True, help="Graph output format")
454
+ entity_graph_export.add_argument("--output", required=True, help="Output file path")
455
+ entity_graph_export.add_argument("--scope", default=None, help="Only export graph data linked to this claim scope")
456
+
408
457
  sub.add_parser("feedback-stats", help="Show feedback tracking and quality score statistics")
409
458
 
410
459
  sub.add_parser("quality-scores", help="Recompute quality scores for all claims")
@@ -487,7 +536,7 @@ def build_parser() -> argparse.ArgumentParser:
487
536
  entity_aliases_cmd.add_argument("entity_id", type=int, help="Entity ID")
488
537
  entity_aliases_cmd.add_argument("--add", default="", help="Add this alias to the entity")
489
538
 
490
- entity_backfill = sub.add_parser("entity-backfill", help="Backfill entity_id on claims with subject but no entity")
539
+ sub.add_parser("entity-backfill", help="Backfill entity_id on claims with subject but no entity")
491
540
 
492
541
  return parser
493
542
 
@@ -499,7 +548,7 @@ def main(argv: list[str] | None = None) -> int:
499
548
  effective_db = _resolve_db_path(args)
500
549
 
501
550
  # Commands that don't need MemoryService run first; service is lazy-created once for all others.
502
- _NO_SERVICE_COMMANDS = {"stealth-status", "export-metrics", "wiki-freshness"}
551
+ _NO_SERVICE_COMMANDS = {"stealth-status", "export-metrics", "wiki-freshness", "mcp-usage-report"}
503
552
 
504
553
  try:
505
554
  handler = COMMAND_HANDLERS.get(args.command)