memorymaster 3.23.0__tar.gz → 3.24.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 (399) hide show
  1. {memorymaster-3.23.0/memorymaster.egg-info → memorymaster-3.24.0}/PKG-INFO +2 -2
  2. {memorymaster-3.23.0 → memorymaster-3.24.0}/README.md +1 -1
  3. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/__init__.py +1 -1
  4. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +27 -31
  5. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/query_cache.py +40 -9
  6. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/security.py +21 -0
  7. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/service.py +12 -1
  8. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/verbatim_store.py +142 -133
  9. {memorymaster-3.23.0 → memorymaster-3.24.0/memorymaster.egg-info}/PKG-INFO +2 -2
  10. {memorymaster-3.23.0 → memorymaster-3.24.0}/pyproject.toml +1 -1
  11. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/bench_longmemeval.py +42 -3
  12. memorymaster-3.24.0/tests/test_auto_ingest_hook_citations.py +102 -0
  13. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_query_cache.py +26 -0
  14. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_sensitivity_filter_t07.py +42 -1
  15. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_verbatim_store_qdrant.py +28 -0
  16. memorymaster-3.23.0/tests/test_auto_ingest_hook_citations.py +0 -147
  17. {memorymaster-3.23.0 → memorymaster-3.24.0}/LICENSE +0 -0
  18. {memorymaster-3.23.0 → memorymaster-3.24.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
  19. {memorymaster-3.23.0 → memorymaster-3.24.0}/benchmarks/longmemeval_runner.py +0 -0
  20. {memorymaster-3.23.0 → memorymaster-3.24.0}/benchmarks/longmemeval_vector_runner.py +0 -0
  21. {memorymaster-3.23.0 → memorymaster-3.24.0}/benchmarks/perf_smoke.py +0 -0
  22. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/__main__.py +0 -0
  23. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/_storage_lifecycle.py +0 -0
  24. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/_storage_read.py +0 -0
  25. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/_storage_schema.py +0 -0
  26. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/_storage_shared.py +0 -0
  27. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/_storage_sources.py +0 -0
  28. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/_storage_write_claims.py +0 -0
  29. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/access_control.py +0 -0
  30. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/action_exporters.py +0 -0
  31. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/action_extractor.py +0 -0
  32. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/atlas_claim_extractor.py +0 -0
  33. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/atlas_contract.py +0 -0
  34. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/auto_extractor.py +0 -0
  35. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/auto_resolver.py +0 -0
  36. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/candidate_dedupe.py +0 -0
  37. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/claim_edges.py +0 -0
  38. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/claim_verifier.py +0 -0
  39. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/cli.py +0 -0
  40. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/cli_handlers_basic.py +0 -0
  41. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/cli_handlers_curation.py +0 -0
  42. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/cli_helpers.py +0 -0
  43. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/closets.py +0 -0
  44. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config.py +0 -0
  45. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/claude-md-append.md +0 -0
  46. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
  47. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
  48. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
  49. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
  50. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
  51. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
  52. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
  53. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
  54. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/conflict_resolver.py +0 -0
  55. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/connectors/__init__.py +0 -0
  56. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/connectors/whatsapp.py +0 -0
  57. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/context_hook.py +0 -0
  58. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/context_optimizer.py +0 -0
  59. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/contradiction_probe.py +0 -0
  60. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/daily_notes.py +0 -0
  61. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/dashboard.py +0 -0
  62. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/dashboard_auth.py +0 -0
  63. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/db_merge.py +0 -0
  64. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/delta_sync.py +0 -0
  65. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/dream_bridge.py +0 -0
  66. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/embeddings.py +0 -0
  67. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/entity_extractor.py +0 -0
  68. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/entity_graph.py +0 -0
  69. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/entity_registry.py +0 -0
  70. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/federated_graphify.py +0 -0
  71. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/feedback.py +0 -0
  72. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/graph_store.py +0 -0
  73. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/hook_log.py +0 -0
  74. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/__init__.py +0 -0
  75. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/calibration.py +0 -0
  76. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/compact_summaries.py +0 -0
  77. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/compactor.py +0 -0
  78. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/daydream_ingest.py +0 -0
  79. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/decay.py +0 -0
  80. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/dedup.py +0 -0
  81. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/deterministic.py +0 -0
  82. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/entity_graph_export.py +0 -0
  83. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/extractor.py +0 -0
  84. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/staleness.py +0 -0
  85. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/jobs/validator.py +0 -0
  86. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/key_rotator.py +0 -0
  87. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/lifecycle.py +0 -0
  88. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/llm_budget.py +0 -0
  89. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/llm_provider.py +0 -0
  90. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/llm_rerank.py +0 -0
  91. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/llm_steward.py +0 -0
  92. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/mcp_path_policy.py +0 -0
  93. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/mcp_server.py +0 -0
  94. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/mcp_usage.py +0 -0
  95. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/media_processing.py +0 -0
  96. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/media_providers.py +0 -0
  97. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/metrics_exporter.py +0 -0
  98. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/migrations/0001_initial.py +0 -0
  99. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/migrations/0002_miner_state.py +0 -0
  100. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/migrations/0003_contradiction_verdicts.py +0 -0
  101. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/migrations/0004_query_cache.py +0 -0
  102. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/migrations/__init__.py +0 -0
  103. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/migrations/runner.py +0 -0
  104. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/models.py +0 -0
  105. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/observability.py +0 -0
  106. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/operator.py +0 -0
  107. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/operator_queue.py +0 -0
  108. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/plugins.py +0 -0
  109. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/policy.py +0 -0
  110. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/postgres_store.py +0 -0
  111. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/qdrant_backend.py +0 -0
  112. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/qdrant_recall_fallback.py +0 -0
  113. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/qmd_bridge.py +0 -0
  114. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/query_classifier.py +0 -0
  115. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/query_expansion.py +0 -0
  116. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/recall_fusion.py +0 -0
  117. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/recall_tokenizer.py +0 -0
  118. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/retrieval.py +0 -0
  119. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/retry.py +0 -0
  120. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/review.py +0 -0
  121. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/rl_trainer.py +0 -0
  122. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/rule_miner.py +0 -0
  123. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/rules.py +0 -0
  124. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/scheduler.py +0 -0
  125. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/schema.py +0 -0
  126. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/schema.sql +0 -0
  127. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/schema_postgres.sql +0 -0
  128. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/scope_utils.py +0 -0
  129. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/session_tracker.py +0 -0
  130. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/setup_hooks.py +0 -0
  131. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/skill_evolver.py +0 -0
  132. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/snapshot.py +0 -0
  133. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/steward.py +0 -0
  134. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/steward_classifier.py +0 -0
  135. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/steward_features.py +0 -0
  136. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/storage.py +0 -0
  137. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/store_factory.py +0 -0
  138. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/transcript_miner.py +0 -0
  139. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/turn_schema.py +0 -0
  140. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/vault_bases.py +0 -0
  141. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/vault_curator.py +0 -0
  142. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/vault_exporter.py +0 -0
  143. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/vault_linter.py +0 -0
  144. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/vault_log.py +0 -0
  145. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/vault_query_capture.py +0 -0
  146. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/vault_synthesis.py +0 -0
  147. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/verbatim_cleanup.py +0 -0
  148. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/verbatim_recall.py +0 -0
  149. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/webhook.py +0 -0
  150. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/wiki_engine.py +0 -0
  151. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/wiki_freshness.py +0 -0
  152. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/wiki_similarity.py +0 -0
  153. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/wiki_suggest.py +0 -0
  154. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster/wiki_validate.py +0 -0
  155. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster.egg-info/SOURCES.txt +0 -0
  156. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster.egg-info/dependency_links.txt +0 -0
  157. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster.egg-info/entry_points.txt +0 -0
  158. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster.egg-info/requires.txt +0 -0
  159. {memorymaster-3.23.0 → memorymaster-3.24.0}/memorymaster.egg-info/top_level.txt +0 -0
  160. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/agg_recall_latency.py +0 -0
  161. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/alert_operator_metrics.py +0 -0
  162. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/audit_dedupe_precision.py +0 -0
  163. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/autoresearch_daemon.py +0 -0
  164. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/backfill_entity_extraction.py +0 -0
  165. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/backfill_graph_store.py +0 -0
  166. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/backfill_stop_hook_citations.py +0 -0
  167. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/backtest_steward_classifier.py +0 -0
  168. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/build_steward_training_set.py +0 -0
  169. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/check_hook_template_drift.py +0 -0
  170. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/claude_to_turns.py +0 -0
  171. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/codex_live_to_turns.py +0 -0
  172. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/compaction_edge_cases.py +0 -0
  173. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/compaction_trace_report.py +0 -0
  174. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/compaction_trace_validate.py +0 -0
  175. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/confusion_matrix_eval.py +0 -0
  176. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/conversation_importer.py +0 -0
  177. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/conversation_to_turns.py +0 -0
  178. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/e2e_operator.py +0 -0
  179. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/email_live_to_turns.py +0 -0
  180. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/eval_bm25_sweep.py +0 -0
  181. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/eval_classify_f1.py +0 -0
  182. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/eval_memorymaster.py +0 -0
  183. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/eval_recall_precision_at_5.py +0 -0
  184. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/eval_recall_quality.py +0 -0
  185. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/eval_steward_pareto.py +0 -0
  186. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/eval_verbatim_recall.py +0 -0
  187. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/expand_recall_eval.py +0 -0
  188. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/generate_drill_signoff.py +0 -0
  189. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/git_to_turns.py +0 -0
  190. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/github_live_to_turns.py +0 -0
  191. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/gitnexus_to_claims.py +0 -0
  192. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/grid_recall_weights.py +0 -0
  193. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/index_claims_to_qdrant.py +0 -0
  194. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/ingest_planning_docs.py +0 -0
  195. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/jira_live_to_turns.py +0 -0
  196. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/label_prompts_with_judge.py +0 -0
  197. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/llm_benchmark.py +0 -0
  198. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/measure_dedupe_thresholds.py +0 -0
  199. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/merge_scope_variants.py +0 -0
  200. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/messages_to_turns.py +0 -0
  201. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/operator_metrics.py +0 -0
  202. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/precompute_candidates.py +0 -0
  203. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/recurring_incident_drill.py +0 -0
  204. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/release_readiness.py +0 -0
  205. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/run_codex_autologger.py +0 -0
  206. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/run_incident_drill.py +0 -0
  207. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/scheduled_ingest.py +0 -0
  208. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/setup-hooks.py +0 -0
  209. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/slack_live_to_turns.py +0 -0
  210. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/sync_hook_templates.py +0 -0
  211. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/tickets_to_turns.py +0 -0
  212. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/train_steward_classifier.py +0 -0
  213. {memorymaster-3.23.0 → memorymaster-3.24.0}/scripts/webhook_to_turns.py +0 -0
  214. {memorymaster-3.23.0 → memorymaster-3.24.0}/setup.cfg +0 -0
  215. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/conftest.py +0 -0
  216. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
  217. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_access_control.py +0 -0
  218. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_action_exporters.py +0 -0
  219. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_action_extractor.py +0 -0
  220. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_atlas_claim_extractor.py +0 -0
  221. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_atlas_contract.py +0 -0
  222. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_atlas_source_schema.py +0 -0
  223. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_auto_extractor.py +0 -0
  224. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_auto_ingest_hook_schema.py +0 -0
  225. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_auto_resolver.py +0 -0
  226. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_auto_validate.py +0 -0
  227. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_backend_parity.py +0 -0
  228. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_bm25_per_field.py +0 -0
  229. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_calibration.py +0 -0
  230. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_calibration_priors_applied.py +0 -0
  231. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_candidate_dedupe.py +0 -0
  232. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_claim_edges.py +0 -0
  233. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_claim_links.py +0 -0
  234. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_claim_type_ranking.py +0 -0
  235. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_classify_hook_f1.py +0 -0
  236. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_classify_hook_latency.py +0 -0
  237. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_claude_to_turns.py +0 -0
  238. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_cli_dry_run.py +0 -0
  239. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_cli_json_flag.py +0 -0
  240. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_cli_ready.py +0 -0
  241. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_cli_review_queue.py +0 -0
  242. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_cli_subcommands.py +0 -0
  243. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_closets.py +0 -0
  244. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_closets_recall_integration.py +0 -0
  245. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_compact_summaries.py +0 -0
  246. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_compact_summaries_sensitivity.py +0 -0
  247. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_compaction_trace.py +0 -0
  248. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_compactor_artifact_order.py +0 -0
  249. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_config.py +0 -0
  250. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_conflict_resolver.py +0 -0
  251. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_confusion_matrix_eval.py +0 -0
  252. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_connection_retry.py +0 -0
  253. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_connectors.py +0 -0
  254. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_context_hook.py +0 -0
  255. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_context_optimizer.py +0 -0
  256. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_context_optimizer_provider.py +0 -0
  257. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_contradiction_probe.py +0 -0
  258. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_conversation_to_turns.py +0 -0
  259. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dashboard.py +0 -0
  260. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dashboard_auth.py +0 -0
  261. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dashboard_coverage.py +0 -0
  262. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dashboard_latency.py +0 -0
  263. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dashboard_lineage.py +0 -0
  264. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dashboard_review_queue.py +0 -0
  265. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_daydream_ingest.py +0 -0
  266. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_db_merge_confidence_conflict.py +0 -0
  267. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_db_merge_coverage_v2.py +0 -0
  268. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_decay_coverage.py +0 -0
  269. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_decay_respects_pinned.py +0 -0
  270. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dedup.py +0 -0
  271. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dedup_cli.py +0 -0
  272. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dedup_conflict_disambiguation.py +0 -0
  273. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_delta_sync.py +0 -0
  274. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_deterministic_predicates.py +0 -0
  275. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dream_bridge_coverage_v2.py +0 -0
  276. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_dream_bridge_sensitivity.py +0 -0
  277. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_embeddings_coverage.py +0 -0
  278. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_entity_extractor.py +0 -0
  279. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_entity_extractor_llm.py +0 -0
  280. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_entity_graph.py +0 -0
  281. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_entity_graph_export.py +0 -0
  282. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_entity_new_kinds.py +0 -0
  283. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_entity_regex_v3.py +0 -0
  284. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_entity_registry.py +0 -0
  285. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_eval_harness.py +0 -0
  286. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_events_schema.py +0 -0
  287. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_extract_llm_ollama.py +0 -0
  288. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_federated_graphify_mcp.py +0 -0
  289. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_federated_query_safety.py +0 -0
  290. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_feedback.py +0 -0
  291. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_floor_gate.py +0 -0
  292. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_fts5_search.py +0 -0
  293. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_graph_distance.py +0 -0
  294. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_graph_store.py +0 -0
  295. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_handler_regressions.py +0 -0
  296. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_hook_env_isolation.py +0 -0
  297. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_human_id.py +0 -0
  298. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_incident_drill_runner.py +0 -0
  299. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_integration_workflows.py +0 -0
  300. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_key_rotator.py +0 -0
  301. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_lifecycle.py +0 -0
  302. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_lifecycle_supersede_invariant.py +0 -0
  303. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_llm_budget.py +0 -0
  304. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_llm_fallback.py +0 -0
  305. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_llm_provider_claude_cli.py +0 -0
  306. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_llm_provider_key_rotation.py +0 -0
  307. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_llm_steward_coverage.py +0 -0
  308. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_llm_steward_key_rotation.py +0 -0
  309. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_mcp_filter_bypass.py +0 -0
  310. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_mcp_helpers.py +0 -0
  311. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_mcp_path_policy.py +0 -0
  312. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_mcp_rate_limit.py +0 -0
  313. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_mcp_server_validation.py +0 -0
  314. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_mcp_usage.py +0 -0
  315. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_media_processing.py +0 -0
  316. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_meta_decisions.py +0 -0
  317. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_metrics_exporter.py +0 -0
  318. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_migrations.py +0 -0
  319. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_observability.py +0 -0
  320. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_obsidian_mind_patterns.py +0 -0
  321. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_operator.py +0 -0
  322. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_operator_queue.py +0 -0
  323. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_perf_smoke_config.py +0 -0
  324. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_plugins.py +0 -0
  325. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_policy_coverage.py +0 -0
  326. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_policy_mode_env.py +0 -0
  327. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_postgres_parity.py +0 -0
  328. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_qdrant_backend.py +0 -0
  329. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_qmd_bridge.py +0 -0
  330. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_qrels_regression.py +0 -0
  331. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_query_classifier.py +0 -0
  332. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_query_expansion.py +0 -0
  333. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_recall_entity_fanout.py +0 -0
  334. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_recall_fusion.py +0 -0
  335. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_recall_latency.py +0 -0
  336. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_recall_precision_at_5.py +0 -0
  337. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_recall_tokenizer.py +0 -0
  338. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_recall_vector_fallback.py +0 -0
  339. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_reliability_hardening.py +0 -0
  340. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_resolvers_concurrent_supersede.py +0 -0
  341. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_retrieval_profile.py +0 -0
  342. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_retrieval_profiles.py +0 -0
  343. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_retrieval_rrf_tiebreaker.py +0 -0
  344. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_retrieval_weights.py +0 -0
  345. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_review.py +0 -0
  346. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_rl_trainer.py +0 -0
  347. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_rrf_auto_gate.py +0 -0
  348. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_rule_claims.py +0 -0
  349. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_rule_miner.py +0 -0
  350. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_scheduler.py +0 -0
  351. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_schema.py +0 -0
  352. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_scope_boost.py +0 -0
  353. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_scope_utils.py +0 -0
  354. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_security_access.py +0 -0
  355. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_security_patterns.py +0 -0
  356. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
  357. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
  358. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_service_coverage.py +0 -0
  359. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_session_tracker.py +0 -0
  360. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_snapshot.py +0 -0
  361. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_snapshot_roundtrip.py +0 -0
  362. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_sqlite_core.py +0 -0
  363. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_staleness.py +0 -0
  364. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_stealth_mode.py +0 -0
  365. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_steward.py +0 -0
  366. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_steward_classifier.py +0 -0
  367. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_steward_contradiction_phase.py +0 -0
  368. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_steward_daydream_hook.py +0 -0
  369. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_steward_features.py +0 -0
  370. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_steward_features_v3.py +0 -0
  371. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_steward_resolution_parity.py +0 -0
  372. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_storage_parity.py +0 -0
  373. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_store_factory.py +0 -0
  374. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_tenant_isolation.py +0 -0
  375. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_turn_schema.py +0 -0
  376. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_two_pass_recall.py +0 -0
  377. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_v311_fixes.py +0 -0
  378. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_v313_e2e.py +0 -0
  379. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
  380. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_v390_e2e.py +0 -0
  381. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_v391_strict_warnings.py +0 -0
  382. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_vault_exporter.py +0 -0
  383. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_vault_linter_orphan.py +0 -0
  384. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_vector_search.py +0 -0
  385. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_verbatim_cleanup.py +0 -0
  386. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_verbatim_dedup.py +0 -0
  387. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_verbatim_recall.py +0 -0
  388. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_verbatim_store.py +0 -0
  389. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_webhook.py +0 -0
  390. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_webhook_hmac.py +0 -0
  391. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_whatsapp_importer.py +0 -0
  392. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_wiki_autopromote.py +0 -0
  393. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_wiki_binding.py +0 -0
  394. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_wiki_engine_idempotency.py +0 -0
  395. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
  396. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_wiki_freshness.py +0 -0
  397. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_wiki_similarity_multiscope.py +0 -0
  398. {memorymaster-3.23.0 → memorymaster-3.24.0}/tests/test_wiki_suggest.py +0 -0
  399. {memorymaster-3.23.0 → memorymaster-3.24.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.23.0
3
+ Version: 3.24.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
@@ -52,7 +52,7 @@ Lifecycle-managed claims with citations, conflict detection, steward governance,
52
52
 
53
53
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
54
54
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
55
- [![Tests](https://img.shields.io/badge/tests-2224-green.svg)]()
55
+ [![Tests](https://img.shields.io/badge/tests-2230-green.svg)]()
56
56
  [![MCP Tools](https://img.shields.io/badge/MCP%20tools-24-purple.svg)]()
57
57
  [![CLI Commands](https://img.shields.io/badge/CLI%20commands-86-orange.svg)]()
58
58
  [![PyPI](https://img.shields.io/pypi/v/memorymaster.svg)](https://pypi.org/project/memorymaster/)
@@ -6,7 +6,7 @@ Lifecycle-managed claims with citations, conflict detection, steward governance,
6
6
 
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
8
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
9
- [![Tests](https://img.shields.io/badge/tests-2224-green.svg)]()
9
+ [![Tests](https://img.shields.io/badge/tests-2230-green.svg)]()
10
10
  [![MCP Tools](https://img.shields.io/badge/MCP%20tools-24-purple.svg)]()
11
11
  [![CLI Commands](https://img.shields.io/badge/CLI%20commands-86-orange.svg)]()
12
12
  [![PyPI](https://img.shields.io/pypi/v/memorymaster.svg)](https://pypi.org/project/memorymaster/)
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "3.23.0"
5
+ __version__ = "3.24.0"
@@ -10,9 +10,8 @@ import json
10
10
  import os
11
11
  import sys
12
12
  import re
13
- import sqlite3
14
13
  import hashlib
15
- from datetime import datetime, timezone
14
+ from datetime import datetime
16
15
  from pathlib import Path
17
16
 
18
17
  PROJECT_ROOT = "__MEMORYMASTER_PROJECT_ROOT__"
@@ -139,43 +138,40 @@ Only: bug root causes, decisions, gotchas, constraints. Never: credentials, IPs,
139
138
  return
140
139
 
141
140
  scope = "project:" + os.path.basename(cwd).lower().replace(" ", "-") if cwd else "global"
142
- now = datetime.now(timezone.utc).isoformat()
143
141
 
144
- conn = sqlite3.connect(DB_PATH)
142
+ # Route through MemoryService.ingest instead of raw SQL so claims gain
143
+ # the canonical ingest path: sensitivity sanitize (defense-in-depth on
144
+ # top of the _is_sensitive_claim drop above), content-hash + idempotency
145
+ # dedup, entity resolution, auto-citation, observability, and webhook.
146
+ # Mirrors _run_rule_extraction, which already uses the service.
147
+ from memorymaster.service import MemoryService
148
+ from memorymaster.models import CitationInput
149
+
150
+ svc = MemoryService(DB_PATH, workspace_root=Path(cwd or PROJECT_ROOT))
151
+ ingested = 0
145
152
  for c in claims:
146
153
  text = c.get("text", "")
147
154
  if not text or len(text) < 10:
148
155
  continue
149
- # Duplicate check by content hash
150
156
  text_hash = hashlib.sha256(text.strip().lower().encode()).hexdigest()[:16]
151
- idem = f"llm-stop-{text_hash}"
152
- if conn.execute("SELECT id FROM claims WHERE idempotency_key = ?", (idem,)).fetchone():
153
- continue
154
- cur = conn.execute(
155
- """INSERT INTO claims (text, idempotency_key, normalized_text, claim_type,
156
- subject, predicate, scope, status, confidence,
157
- source_agent, created_at, updated_at, tier, version, visibility,
158
- valid_from)
159
- VALUES (?, ?, ?, ?, ?, ?, ?, 'candidate', 0.6,
160
- 'llm-stop-hook', ?, ?, 'working', 1, 'public', ?)""",
161
- (text, idem, text.lower(), c.get("claim_type", "fact"),
162
- c.get("subject", "codebase"), c.get("predicate", "observation"),
163
- scope, now, now, now),
164
- )
165
- # Auto-citation: steward's min_citations>=1 gate requires at least one
166
- # row per claim. Without this, every llm-stop-hook claim is born
167
- # unpromotable. Source is the hook itself; locator = scope for
168
- # traceability; excerpt preserves the first 200 chars of the claim.
169
- conn.execute(
170
- """INSERT INTO citations (claim_id, source, locator, excerpt, created_at)
171
- VALUES (?, 'llm-stop-hook', ?, ?, ?)""",
172
- (cur.lastrowid, scope, text[:200], now),
173
- )
174
- conn.commit()
175
- conn.close()
157
+ try:
158
+ svc.ingest(
159
+ text=text,
160
+ citations=[CitationInput(source="llm-stop-hook", locator=scope, excerpt=text[:200])],
161
+ idempotency_key=f"llm-stop-{text_hash}",
162
+ claim_type=c.get("claim_type", "fact"),
163
+ subject=c.get("subject", "codebase"),
164
+ predicate=c.get("predicate", "observation"),
165
+ scope=scope,
166
+ confidence=0.6,
167
+ source_agent="llm-stop-hook",
168
+ )
169
+ ingested += 1
170
+ except Exception:
171
+ continue # one bad claim must not abort the rest
176
172
 
177
173
  provider = os.environ.get("MEMORYMASTER_LLM_PROVIDER", "google")
178
- sys.stderr.write(f"[MemoryMaster] {provider} extracted {len(claims)} learnings\n")
174
+ sys.stderr.write(f"[MemoryMaster] {provider} extracted {ingested} learnings\n")
179
175
  except Exception:
180
176
  pass
181
177
 
@@ -84,11 +84,31 @@ def current_generation(conn: sqlite3.Connection) -> int:
84
84
  return int(row[0]) if row else 0
85
85
 
86
86
 
87
+ def read_generation(db_path: str) -> int:
88
+ """Return the current corpus generation, or 0 on any error.
89
+
90
+ Callers capture this BEFORE reading the corpus they are about to compute a
91
+ result from, then pass it to ``write()`` — see the TOCTOU note there."""
92
+ try:
93
+ conn = _connect(db_path)
94
+ except sqlite3.Error as exc:
95
+ logger.warning("query_cache.read_generation connect failed: %s", exc)
96
+ return 0
97
+ try:
98
+ return current_generation(conn)
99
+ except (sqlite3.Error, ValueError) as exc:
100
+ logger.warning("query_cache.read_generation failed: %s", exc)
101
+ return 0
102
+ finally:
103
+ conn.close()
104
+
105
+
87
106
  def read(db_path: str, cache_key: str) -> list[dict] | None:
88
107
  """Return cached result stubs if present AND still fresh (generation match)."""
89
108
  try:
90
109
  conn = _connect(db_path)
91
- except sqlite3.Error:
110
+ except sqlite3.Error as exc:
111
+ logger.warning("query_cache.read connect failed: %s", exc)
92
112
  return None
93
113
  try:
94
114
  gen = current_generation(conn)
@@ -99,20 +119,31 @@ def read(db_path: str, cache_key: str) -> list[dict] | None:
99
119
  if row is None or int(row["generation"]) != gen:
100
120
  return None
101
121
  return json.loads(row["result_json"])
102
- except (sqlite3.Error, json.JSONDecodeError, ValueError):
122
+ except (sqlite3.Error, json.JSONDecodeError, ValueError) as exc:
123
+ logger.warning("query_cache.read failed (cache disabled for this query): %s", exc)
103
124
  return None
104
125
  finally:
105
126
  conn.close()
106
127
 
107
128
 
108
- def write(db_path: str, cache_key: str, stub_rows: list[dict]) -> None:
109
- """Store result stubs tagged with the current generation. Best-effort."""
129
+ def write(db_path: str, cache_key: str, stub_rows: list[dict], generation: int) -> None:
130
+ """Store result stubs tagged with ``generation``. Best-effort.
131
+
132
+ TOCTOU correctness: ``generation`` MUST be the corpus generation captured by
133
+ the caller BEFORE it read the candidates it computed ``stub_rows`` from. If
134
+ we re-read the generation here instead, a claim write that raced in between
135
+ the caller's corpus read and this write would have bumped the counter, and
136
+ we would tag a stale (generation-G) result as the new generation (G+1) — so
137
+ a subsequent read at G+1 would serve the stale ranking, defeating the
138
+ generation gate. Tagging with the compute-time generation guarantees any
139
+ racing write correctly invalidates this entry. (audit: qc-generation-toctou)
140
+ """
110
141
  try:
111
142
  conn = _connect(db_path)
112
- except sqlite3.Error:
143
+ except sqlite3.Error as exc:
144
+ logger.warning("query_cache.write connect failed: %s", exc)
113
145
  return
114
146
  try:
115
- gen = current_generation(conn)
116
147
  conn.execute(
117
148
  """INSERT INTO query_cache (cache_key, result_json, generation, created_at)
118
149
  VALUES (?, ?, ?, ?)
@@ -120,10 +151,10 @@ def write(db_path: str, cache_key: str, stub_rows: list[dict]) -> None:
120
151
  result_json = excluded.result_json,
121
152
  generation = excluded.generation,
122
153
  created_at = excluded.created_at""",
123
- (cache_key, json.dumps(stub_rows), gen, datetime.now(timezone.utc).isoformat()),
154
+ (cache_key, json.dumps(stub_rows), generation, datetime.now(timezone.utc).isoformat()),
124
155
  )
125
156
  conn.commit()
126
- except (sqlite3.Error, TypeError, ValueError):
127
- pass
157
+ except (sqlite3.Error, TypeError, ValueError) as exc:
158
+ logger.warning("query_cache.write failed (result not cached): %s", exc)
128
159
  finally:
129
160
  conn.close()
@@ -157,6 +157,8 @@ class SanitizedClaimInput:
157
157
  is_sensitive: bool
158
158
  findings: list[str]
159
159
  encrypted_payload: str | None
160
+ subject: str | None = None
161
+ predicate: str | None = None
160
162
 
161
163
 
162
164
  def _as_bool(value: object, *, field: str) -> bool:
@@ -320,6 +322,8 @@ def sanitize_claim_input(
320
322
  text: str,
321
323
  object_value: str | None,
322
324
  citations: list[CitationInput],
325
+ subject: str | None = None,
326
+ predicate: str | None = None,
323
327
  ) -> SanitizedClaimInput:
324
328
  redacted_text, findings = _redact(text)
325
329
  redacted_object = object_value
@@ -328,6 +332,19 @@ def sanitize_claim_input(
328
332
  redacted_object, object_findings = _redact(object_value)
329
333
  findings.extend(object_findings)
330
334
 
335
+ # subject/predicate are structured-claim fields that reach the store
336
+ # alongside text/object_value. They are exposed MCP ingest parameters, so a
337
+ # secret placed there must be caught by the ingest filter — the last line of
338
+ # defense — not only at display time. (audit: ingest-subject-skips-filter)
339
+ redacted_subject = subject
340
+ if subject:
341
+ redacted_subject, subject_findings = _redact(subject)
342
+ findings.extend(subject_findings)
343
+ redacted_predicate = predicate
344
+ if predicate:
345
+ redacted_predicate, predicate_findings = _redact(predicate)
346
+ findings.extend(predicate_findings)
347
+
331
348
  sanitized_citations: list[CitationInput] = []
332
349
  citation_findings: list[str] = []
333
350
  for cite in citations:
@@ -344,6 +361,8 @@ def sanitize_claim_input(
344
361
  {
345
362
  "text": text,
346
363
  "object_value": object_value,
364
+ "subject": subject,
365
+ "predicate": predicate,
347
366
  "citations": [asdict(c) for c in citations],
348
367
  }
349
368
  ) if is_sensitive else None
@@ -355,6 +374,8 @@ def sanitize_claim_input(
355
374
  is_sensitive=is_sensitive,
356
375
  findings=dedup_findings,
357
376
  encrypted_payload=encrypted_payload,
377
+ subject=redacted_subject,
378
+ predicate=redacted_predicate,
358
379
  )
359
380
 
360
381
 
@@ -251,9 +251,15 @@ class MemoryService:
251
251
  text=text.strip(),
252
252
  object_value=object_value,
253
253
  citations=citations,
254
+ subject=subject,
255
+ predicate=predicate,
254
256
  )
255
257
  if not sanitized.citations:
256
258
  raise ValueError("At least one citation is required.")
259
+ # Use the sanitized subject/predicate everywhere downstream so a secret
260
+ # placed in those fields is redacted at rest, not just at display time.
261
+ subject = sanitized.subject
262
+ predicate = sanitized.predicate
257
263
  # Resolve subject → canonical entity (GBrain-inspired entity registry)
258
264
  # and mine text for pattern-based entities (#127 Wave 3).
259
265
  entity_id = 0
@@ -667,6 +673,11 @@ class MemoryService:
667
673
  rows = self._rehydrate_cached_rows(cached)
668
674
  self._record_accesses(rows, query_text=query_text)
669
675
  return rows
676
+ # Capture the corpus generation BEFORE reading candidates so the cache
677
+ # entry is tagged with the generation it was actually computed against,
678
+ # not whatever it is after ranking/LLM-rerank (which a concurrent claim
679
+ # write could have bumped). See query_cache.write TOCTOU note.
680
+ cache_generation = query_cache.read_generation(cache_path) if cache_path else 0
670
681
  candidate_limit = max(limit * 6, 60, 50 if use_llm_rerank else 0)
671
682
  candidates = self.store.list_claims(
672
683
  limit=candidate_limit,
@@ -733,7 +744,7 @@ class MemoryService:
733
744
  "breakdown": r.get("breakdown"),
734
745
  }
735
746
  for r in results if r.get("claim") is not None
736
- ])
747
+ ], cache_generation)
737
748
  return results
738
749
 
739
750
  def _rehydrate_cached_rows(self, stubs: list[dict[str, Any]]) -> list[dict[str, Any]]:
@@ -17,6 +17,7 @@ import os
17
17
  import sqlite3
18
18
  import urllib.request
19
19
  import urllib.error
20
+ from contextlib import closing
20
21
  from datetime import datetime, timezone
21
22
  from pathlib import Path
22
23
  from typing import Any
@@ -48,7 +49,12 @@ def _row_has_sensitive_field(role: str, source_agent: str, content: str) -> bool
48
49
  _, findings = _redact_text(joined)
49
50
  return bool(findings)
50
51
 
51
- QDRANT_URL = os.environ.get("QDRANT_URL", "http://192.168.100.186:6333")
52
+ # Vector search is opt-in: an unset QDRANT_URL means "vector disabled", exactly
53
+ # like a missing OPENAI_API_KEY. NEVER hardcode a routable private LAN IP here —
54
+ # a home-lab RFC1918 default previously shipped to PyPI, violating the
55
+ # "never hardcode IPs" boundary and silently pointing installs at the author's
56
+ # network. Mirror qdrant_backend.py / service.py which use empty/localhost defaults.
57
+ QDRANT_URL = os.environ.get("QDRANT_URL", "").strip()
52
58
  QDRANT_COLLECTION = "memorymaster-verbatim"
53
59
  EMBED_DIM = 1536 # text-embedding-3-small
54
60
 
@@ -77,36 +83,37 @@ def store_verbatim(
77
83
 
78
84
  now = timestamp or datetime.now(timezone.utc).isoformat()
79
85
 
80
- conn = _connect(db_path)
81
- # Dedup by exact content within the same session. Uses idx_verbatim_session
82
- # so the lookup is O(rows-in-session), not table-scan. The previous FTS5-based
83
- # dedup query passed a sha256 hex prefix to MATCH which never resolves —
84
- # the FTS5 index stores the content text, not its hash. Result: 9M+ rows
85
- # accumulated for orchestrator sessions because every Stop event re-inserted
86
- # every message. Fixed 2026-05-03; see mm-0c43.
87
- existing = conn.execute(
88
- "SELECT id FROM verbatim_memories WHERE session_id = ? AND content = ? LIMIT 1",
89
- (session_id, content),
90
- ).fetchone()
91
- if existing:
92
- conn.close()
93
- return None
94
-
95
- cur = conn.execute(
96
- """INSERT INTO verbatim_memories (session_id, role, content, scope, timestamp, source_agent)
97
- VALUES (?, ?, ?, ?, ?, ?)""",
98
- (session_id, role, content, scope, now, source_agent),
99
- )
100
- row_id = cur.lastrowid
86
+ # closing() guarantees the connection (and its WAL write lock) is released
87
+ # even if an INSERT/commit raises (e.g. "database is locked" under
88
+ # concurrent MCP/Stop-hook writers) this is the hottest write path.
89
+ with closing(_connect(db_path)) as conn:
90
+ # Dedup by exact content within the same session. Uses idx_verbatim_session
91
+ # so the lookup is O(rows-in-session), not table-scan. The previous FTS5-based
92
+ # dedup query passed a sha256 hex prefix to MATCH which never resolves —
93
+ # the FTS5 index stores the content text, not its hash. Result: 9M+ rows
94
+ # accumulated for orchestrator sessions because every Stop event re-inserted
95
+ # every message. Fixed 2026-05-03; see mm-0c43.
96
+ existing = conn.execute(
97
+ "SELECT id FROM verbatim_memories WHERE session_id = ? AND content = ? LIMIT 1",
98
+ (session_id, content),
99
+ ).fetchone()
100
+ if existing:
101
+ return None
102
+
103
+ cur = conn.execute(
104
+ """INSERT INTO verbatim_memories (session_id, role, content, scope, timestamp, source_agent)
105
+ VALUES (?, ?, ?, ?, ?, ?)""",
106
+ (session_id, role, content, scope, now, source_agent),
107
+ )
108
+ row_id = cur.lastrowid
101
109
 
102
- # Update FTS
103
- conn.execute(
104
- "INSERT INTO verbatim_fts(rowid, content) VALUES (?, ?)",
105
- (row_id, content),
106
- )
107
- conn.commit()
108
- conn.close()
109
- return row_id
110
+ # Update FTS
111
+ conn.execute(
112
+ "INSERT INTO verbatim_fts(rowid, content) VALUES (?, ?)",
113
+ (row_id, content),
114
+ )
115
+ conn.commit()
116
+ return row_id
110
117
 
111
118
 
112
119
  def _extract_role_content(entry: dict) -> tuple[str, str]:
@@ -240,40 +247,41 @@ def search_verbatim(
240
247
 
241
248
  def _search_fts(db_path: str, query: str, scope: str | None, limit: int) -> list[dict]:
242
249
  """FTS5 keyword search over verbatim memories."""
243
- conn = _connect(db_path)
244
250
  # Clean query for FTS5
245
251
  clean_query = " ".join(w for w in query.split() if len(w) > 2)
246
252
  if not clean_query:
247
- conn.close()
248
253
  return []
249
254
 
250
- try:
251
- if scope:
252
- rows = conn.execute(
253
- """SELECT v.id, v.session_id, v.role, v.content, v.scope, v.timestamp,
254
- rank as score
255
- FROM verbatim_fts f
256
- JOIN verbatim_memories v ON v.id = f.rowid
257
- WHERE verbatim_fts MATCH ? AND v.scope LIKE ?
258
- ORDER BY rank
259
- LIMIT ?""",
260
- (clean_query, f"{scope}%", limit),
261
- ).fetchall()
262
- else:
263
- rows = conn.execute(
264
- """SELECT v.id, v.session_id, v.role, v.content, v.scope, v.timestamp,
265
- rank as score
266
- FROM verbatim_fts f
267
- JOIN verbatim_memories v ON v.id = f.rowid
268
- WHERE verbatim_fts MATCH ?
269
- ORDER BY rank
270
- LIMIT ?""",
271
- (clean_query, limit),
272
- ).fetchall()
273
- except sqlite3.OperationalError:
274
- rows = []
275
-
276
- conn.close()
255
+ # closing() guarantees the connection is released even if the JOIN raises a
256
+ # non-OperationalError (corrupt/locked DB, programming error) — the bare
257
+ # except below only catches OperationalError.
258
+ with closing(_connect(db_path)) as conn:
259
+ try:
260
+ if scope:
261
+ rows = conn.execute(
262
+ """SELECT v.id, v.session_id, v.role, v.content, v.scope, v.timestamp,
263
+ rank as score
264
+ FROM verbatim_fts f
265
+ JOIN verbatim_memories v ON v.id = f.rowid
266
+ WHERE verbatim_fts MATCH ? AND v.scope LIKE ?
267
+ ORDER BY rank
268
+ LIMIT ?""",
269
+ (clean_query, f"{scope}%", limit),
270
+ ).fetchall()
271
+ else:
272
+ rows = conn.execute(
273
+ """SELECT v.id, v.session_id, v.role, v.content, v.scope, v.timestamp,
274
+ rank as score
275
+ FROM verbatim_fts f
276
+ JOIN verbatim_memories v ON v.id = f.rowid
277
+ WHERE verbatim_fts MATCH ?
278
+ ORDER BY rank
279
+ LIMIT ?""",
280
+ (clean_query, limit),
281
+ ).fetchall()
282
+ except sqlite3.OperationalError:
283
+ rows = []
284
+
277
285
  return [
278
286
  {"id": r["id"], "session_id": r["session_id"], "role": r["role"],
279
287
  "content": r["content"], "scope": r["scope"], "timestamp": r["timestamp"],
@@ -284,6 +292,8 @@ def _search_fts(db_path: str, query: str, scope: str | None, limit: int) -> list
284
292
 
285
293
  def _search_vector(query: str, scope: str | None, limit: int) -> list[dict]:
286
294
  """Qdrant semantic search over verbatim memories."""
295
+ if not QDRANT_URL:
296
+ return []
287
297
  try:
288
298
  # Embed query with OpenAI
289
299
  api_key = os.environ.get("OPENAI_API_KEY", "")
@@ -337,85 +347,84 @@ def sync_to_qdrant(db_path: str, batch_size: int = 50) -> dict[str, int]:
337
347
  api_key = os.environ.get("OPENAI_API_KEY", "")
338
348
  if not api_key:
339
349
  return {"synced": 0, "error": "no OPENAI_API_KEY"}
350
+ if not QDRANT_URL:
351
+ return {"synced": 0, "error": "no QDRANT_URL"}
340
352
 
341
- conn = _connect(db_path)
342
- rows = conn.execute(
343
- "SELECT id, content, scope, session_id, role FROM verbatim_memories WHERE embedding_synced = 0 LIMIT ?",
344
- (batch_size,),
345
- ).fetchall()
353
+ # closing() guarantees the connection is released on every exit path,
354
+ # including the initial SELECT raising or the final UPDATE/commit raising.
355
+ with closing(_connect(db_path)) as conn:
356
+ rows = conn.execute(
357
+ "SELECT id, content, scope, session_id, role FROM verbatim_memories WHERE embedding_synced = 0 LIMIT ?",
358
+ (batch_size,),
359
+ ).fetchall()
346
360
 
347
- if not rows:
348
- conn.close()
349
- return {"synced": 0}
361
+ if not rows:
362
+ return {"synced": 0}
350
363
 
351
- # Ensure collection exists
352
- try:
353
- req = urllib.request.Request(f"{QDRANT_URL}/collections/{QDRANT_COLLECTION}")
354
- urllib.request.urlopen(req, timeout=5)
355
- except Exception:
356
- payload = {"vectors": {"size": EMBED_DIM, "distance": "Cosine"}}
357
- req = urllib.request.Request(
358
- f"{QDRANT_URL}/collections/{QDRANT_COLLECTION}",
359
- data=json.dumps(payload).encode(),
360
- headers={"Content-Type": "application/json"},
361
- method="PUT",
362
- )
364
+ # Ensure collection exists
363
365
  try:
364
- urllib.request.urlopen(req, timeout=60)
366
+ req = urllib.request.Request(f"{QDRANT_URL}/collections/{QDRANT_COLLECTION}")
367
+ urllib.request.urlopen(req, timeout=5)
368
+ except Exception:
369
+ payload = {"vectors": {"size": EMBED_DIM, "distance": "Cosine"}}
370
+ req = urllib.request.Request(
371
+ f"{QDRANT_URL}/collections/{QDRANT_COLLECTION}",
372
+ data=json.dumps(payload).encode(),
373
+ headers={"Content-Type": "application/json"},
374
+ method="PUT",
375
+ )
376
+ try:
377
+ urllib.request.urlopen(req, timeout=60)
378
+ except Exception as e:
379
+ return {"synced": 0, "error": str(e)}
380
+
381
+ # Embed in batches
382
+ texts = [r["content"][:2000] for r in rows]
383
+ try:
384
+ embed_url = "https://api.openai.com/v1/embeddings"
385
+ payload = {"model": "text-embedding-3-small", "input": texts}
386
+ req = urllib.request.Request(
387
+ embed_url,
388
+ data=json.dumps(payload).encode(),
389
+ headers={"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"},
390
+ method="POST",
391
+ )
392
+ with urllib.request.urlopen(req, timeout=30) as resp:
393
+ result = json.loads(resp.read().decode())
394
+ embeddings = [d["embedding"] for d in result["data"]]
365
395
  except Exception as e:
366
- conn.close()
367
396
  return {"synced": 0, "error": str(e)}
368
397
 
369
- # Embed in batches
370
- texts = [r["content"][:2000] for r in rows]
371
- try:
372
- embed_url = "https://api.openai.com/v1/embeddings"
373
- payload = {"model": "text-embedding-3-small", "input": texts}
374
- req = urllib.request.Request(
375
- embed_url,
376
- data=json.dumps(payload).encode(),
377
- headers={"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"},
378
- method="POST",
379
- )
380
- with urllib.request.urlopen(req, timeout=30) as resp:
381
- result = json.loads(resp.read().decode())
382
- embeddings = [d["embedding"] for d in result["data"]]
383
- except Exception as e:
384
- conn.close()
385
- return {"synced": 0, "error": str(e)}
386
-
387
- # Upsert to Qdrant
388
- points = []
389
- for i, (row, emb) in enumerate(zip(rows, embeddings)):
390
- points.append({
391
- "id": row["id"],
392
- "vector": emb,
393
- "payload": {
394
- "content": row["content"][:2000],
395
- "content_hash": hashlib.sha256(row["content"].encode()).hexdigest(),
396
- "scope": row["scope"],
397
- "session_id": row["session_id"],
398
- "role": row["role"],
399
- },
400
- })
398
+ # Upsert to Qdrant
399
+ points = []
400
+ for i, (row, emb) in enumerate(zip(rows, embeddings)):
401
+ points.append({
402
+ "id": row["id"],
403
+ "vector": emb,
404
+ "payload": {
405
+ "content": row["content"][:2000],
406
+ "content_hash": hashlib.sha256(row["content"].encode()).hexdigest(),
407
+ "scope": row["scope"],
408
+ "session_id": row["session_id"],
409
+ "role": row["role"],
410
+ },
411
+ })
401
412
 
402
- try:
403
- req = urllib.request.Request(
404
- f"{QDRANT_URL}/collections/{QDRANT_COLLECTION}/points",
405
- data=json.dumps({"points": points}).encode(),
406
- headers={"Content-Type": "application/json"},
407
- method="PUT",
408
- )
409
- urllib.request.urlopen(req, timeout=30)
410
- except Exception as e:
411
- conn.close()
412
- return {"synced": 0, "error": str(e)}
413
-
414
- # Mark as synced
415
- ids = [r["id"] for r in rows]
416
- placeholders = ",".join("?" for _ in ids)
417
- conn.execute(f"UPDATE verbatim_memories SET embedding_synced = 1 WHERE id IN ({placeholders})", ids)
418
- conn.commit()
419
- conn.close()
420
-
421
- return {"synced": len(rows)}
413
+ try:
414
+ req = urllib.request.Request(
415
+ f"{QDRANT_URL}/collections/{QDRANT_COLLECTION}/points",
416
+ data=json.dumps({"points": points}).encode(),
417
+ headers={"Content-Type": "application/json"},
418
+ method="PUT",
419
+ )
420
+ urllib.request.urlopen(req, timeout=30)
421
+ except Exception as e:
422
+ return {"synced": 0, "error": str(e)}
423
+
424
+ # Mark as synced
425
+ ids = [r["id"] for r in rows]
426
+ placeholders = ",".join("?" for _ in ids)
427
+ conn.execute(f"UPDATE verbatim_memories SET embedding_synced = 1 WHERE id IN ({placeholders})", ids)
428
+ conn.commit()
429
+
430
+ return {"synced": len(rows)}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memorymaster
3
- Version: 3.23.0
3
+ Version: 3.24.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
@@ -52,7 +52,7 @@ Lifecycle-managed claims with citations, conflict detection, steward governance,
52
52
 
53
53
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
54
54
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
55
- [![Tests](https://img.shields.io/badge/tests-2224-green.svg)]()
55
+ [![Tests](https://img.shields.io/badge/tests-2230-green.svg)]()
56
56
  [![MCP Tools](https://img.shields.io/badge/MCP%20tools-24-purple.svg)]()
57
57
  [![CLI Commands](https://img.shields.io/badge/CLI%20commands-86-orange.svg)]()
58
58
  [![PyPI](https://img.shields.io/pypi/v/memorymaster.svg)](https://pypi.org/project/memorymaster/)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memorymaster"
7
- version = "3.23.0"
7
+ version = "3.24.0"
8
8
  description = "Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration."
9
9
  license = {text = "MIT"}
10
10
  authors = [{name = "wolverin0"}]