memorymaster 3.24.0__tar.gz → 3.26.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.
- {memorymaster-3.24.0/memorymaster.egg-info → memorymaster-3.26.0}/PKG-INFO +2 -2
- {memorymaster-3.24.0 → memorymaster-3.26.0}/README.md +1 -1
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/__init__.py +1 -1
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/contradiction_probe.py +76 -11
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/query_cache.py +25 -1
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/rule_miner.py +43 -3
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/steward.py +9 -1
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/verbatim_cleanup.py +12 -3
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/verbatim_store.py +80 -56
- {memorymaster-3.24.0 → memorymaster-3.26.0/memorymaster.egg-info}/PKG-INFO +2 -2
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster.egg-info/SOURCES.txt +12 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/pyproject.toml +1 -1
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/bench_longmemeval.py +3 -2
- memorymaster-3.26.0/tests/test_bench_answer_prompt.py +75 -0
- memorymaster-3.26.0/tests/test_cli_handlers_extra.py +308 -0
- memorymaster-3.26.0/tests/test_context_hook_extra.py +643 -0
- memorymaster-3.26.0/tests/test_contradiction_probe.py +313 -0
- memorymaster-3.26.0/tests/test_db_merge_extra.py +237 -0
- memorymaster-3.26.0/tests/test_dream_bridge_extra.py +229 -0
- memorymaster-3.26.0/tests/test_entity_registry_extra.py +271 -0
- memorymaster-3.26.0/tests/test_llm_provider_extra.py +673 -0
- memorymaster-3.26.0/tests/test_observability_extra.py +335 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_query_cache.py +39 -0
- memorymaster-3.26.0/tests/test_retrieval_extra.py +320 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_rule_miner.py +72 -4
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_service_coverage.py +96 -1
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_steward_contradiction_phase.py +70 -0
- memorymaster-3.26.0/tests/test_storage_extra.py +154 -0
- memorymaster-3.26.0/tests/test_vault_linter_extra.py +290 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_verbatim_cleanup.py +80 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_verbatim_dedup.py +48 -0
- memorymaster-3.26.0/tests/test_wiki_engine_extra.py +410 -0
- memorymaster-3.24.0/tests/test_contradiction_probe.py +0 -153
- {memorymaster-3.24.0 → memorymaster-3.26.0}/LICENSE +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/benchmarks/longmemeval_runner.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/benchmarks/longmemeval_vector_runner.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/benchmarks/perf_smoke.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/__main__.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/_storage_lifecycle.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/_storage_read.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/_storage_schema.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/_storage_shared.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/_storage_sources.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/_storage_write_claims.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/access_control.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/action_exporters.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/action_extractor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/atlas_claim_extractor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/atlas_contract.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/auto_extractor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/auto_resolver.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/candidate_dedupe.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/claim_edges.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/claim_verifier.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/cli.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/cli_handlers_basic.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/cli_handlers_curation.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/cli_helpers.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/closets.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/claude-md-append.md +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/conflict_resolver.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/connectors/__init__.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/connectors/whatsapp.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/context_hook.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/context_optimizer.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/daily_notes.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/dashboard.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/dashboard_auth.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/db_merge.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/delta_sync.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/dream_bridge.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/embeddings.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/entity_extractor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/entity_graph.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/entity_registry.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/federated_graphify.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/feedback.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/graph_store.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/hook_log.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/__init__.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/calibration.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/compact_summaries.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/compactor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/daydream_ingest.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/decay.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/dedup.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/deterministic.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/entity_graph_export.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/extractor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/staleness.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/jobs/validator.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/key_rotator.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/lifecycle.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/llm_budget.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/llm_provider.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/llm_rerank.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/llm_steward.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/mcp_path_policy.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/mcp_server.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/mcp_usage.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/media_processing.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/media_providers.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/metrics_exporter.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/migrations/0001_initial.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/migrations/0002_miner_state.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/migrations/0003_contradiction_verdicts.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/migrations/0004_query_cache.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/migrations/__init__.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/migrations/runner.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/models.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/observability.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/operator.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/operator_queue.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/plugins.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/policy.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/postgres_store.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/qdrant_backend.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/qdrant_recall_fallback.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/qmd_bridge.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/query_classifier.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/query_expansion.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/recall_fusion.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/recall_tokenizer.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/retrieval.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/retry.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/review.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/rl_trainer.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/rules.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/scheduler.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/schema.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/schema.sql +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/schema_postgres.sql +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/scope_utils.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/security.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/service.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/session_tracker.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/setup_hooks.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/skill_evolver.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/snapshot.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/steward_classifier.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/steward_features.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/storage.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/store_factory.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/transcript_miner.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/turn_schema.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/vault_bases.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/vault_curator.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/vault_exporter.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/vault_linter.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/vault_log.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/vault_query_capture.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/vault_synthesis.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/verbatim_recall.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/webhook.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/wiki_engine.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/wiki_freshness.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/wiki_similarity.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/wiki_suggest.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster/wiki_validate.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster.egg-info/dependency_links.txt +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster.egg-info/entry_points.txt +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster.egg-info/requires.txt +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/memorymaster.egg-info/top_level.txt +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/agg_recall_latency.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/alert_operator_metrics.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/audit_dedupe_precision.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/autoresearch_daemon.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/backfill_entity_extraction.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/backfill_graph_store.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/backfill_stop_hook_citations.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/backtest_steward_classifier.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/build_steward_training_set.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/check_hook_template_drift.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/claude_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/codex_live_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/compaction_edge_cases.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/compaction_trace_report.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/compaction_trace_validate.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/confusion_matrix_eval.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/conversation_importer.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/conversation_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/e2e_operator.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/email_live_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/eval_bm25_sweep.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/eval_classify_f1.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/eval_memorymaster.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/eval_recall_precision_at_5.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/eval_recall_quality.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/eval_steward_pareto.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/eval_verbatim_recall.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/expand_recall_eval.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/generate_drill_signoff.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/git_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/github_live_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/gitnexus_to_claims.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/grid_recall_weights.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/index_claims_to_qdrant.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/ingest_planning_docs.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/jira_live_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/label_prompts_with_judge.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/llm_benchmark.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/measure_dedupe_thresholds.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/merge_scope_variants.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/messages_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/operator_metrics.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/precompute_candidates.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/recurring_incident_drill.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/release_readiness.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/run_codex_autologger.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/run_incident_drill.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/scheduled_ingest.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/setup-hooks.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/slack_live_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/sync_hook_templates.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/tickets_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/train_steward_classifier.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/scripts/webhook_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/setup.cfg +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/conftest.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_access_control.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_action_exporters.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_action_extractor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_atlas_claim_extractor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_atlas_contract.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_atlas_source_schema.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_auto_extractor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_auto_ingest_hook_citations.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_auto_ingest_hook_schema.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_auto_resolver.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_auto_validate.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_backend_parity.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_bm25_per_field.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_calibration.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_calibration_priors_applied.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_candidate_dedupe.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_claim_edges.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_claim_links.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_claim_type_ranking.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_classify_hook_f1.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_classify_hook_latency.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_claude_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_cli_dry_run.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_cli_json_flag.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_cli_ready.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_cli_review_queue.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_cli_subcommands.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_closets.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_closets_recall_integration.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_compact_summaries.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_compact_summaries_sensitivity.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_compaction_trace.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_compactor_artifact_order.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_config.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_conflict_resolver.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_confusion_matrix_eval.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_connection_retry.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_connectors.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_context_hook.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_context_optimizer.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_context_optimizer_provider.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_conversation_to_turns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dashboard.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dashboard_auth.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dashboard_coverage.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dashboard_latency.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dashboard_lineage.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dashboard_review_queue.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_daydream_ingest.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_db_merge_confidence_conflict.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_db_merge_coverage_v2.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_decay_coverage.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_decay_respects_pinned.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dedup.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dedup_cli.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dedup_conflict_disambiguation.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_delta_sync.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_deterministic_predicates.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dream_bridge_coverage_v2.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_dream_bridge_sensitivity.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_embeddings_coverage.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_entity_extractor.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_entity_extractor_llm.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_entity_graph.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_entity_graph_export.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_entity_new_kinds.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_entity_regex_v3.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_entity_registry.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_eval_harness.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_events_schema.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_extract_llm_ollama.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_federated_graphify_mcp.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_federated_query_safety.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_feedback.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_floor_gate.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_fts5_search.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_graph_distance.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_graph_store.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_handler_regressions.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_hook_env_isolation.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_human_id.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_incident_drill_runner.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_integration_workflows.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_key_rotator.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_lifecycle.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_lifecycle_supersede_invariant.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_llm_budget.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_llm_fallback.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_llm_provider_claude_cli.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_llm_provider_key_rotation.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_llm_steward_coverage.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_llm_steward_key_rotation.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_mcp_filter_bypass.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_mcp_helpers.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_mcp_path_policy.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_mcp_rate_limit.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_mcp_server_validation.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_mcp_usage.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_media_processing.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_meta_decisions.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_metrics_exporter.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_migrations.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_observability.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_obsidian_mind_patterns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_operator.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_operator_queue.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_perf_smoke_config.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_plugins.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_policy_coverage.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_policy_mode_env.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_postgres_parity.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_qdrant_backend.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_qmd_bridge.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_qrels_regression.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_query_classifier.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_query_expansion.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_recall_entity_fanout.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_recall_fusion.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_recall_latency.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_recall_precision_at_5.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_recall_tokenizer.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_recall_vector_fallback.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_reliability_hardening.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_resolvers_concurrent_supersede.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_retrieval_profile.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_retrieval_profiles.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_retrieval_rrf_tiebreaker.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_retrieval_weights.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_review.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_rl_trainer.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_rrf_auto_gate.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_rule_claims.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_scheduler.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_schema.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_scope_boost.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_scope_utils.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_security_access.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_security_patterns.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_sensitivity_filter_t07.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_session_tracker.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_snapshot.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_snapshot_roundtrip.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_sqlite_core.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_staleness.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_stealth_mode.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_steward.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_steward_classifier.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_steward_daydream_hook.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_steward_features.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_steward_features_v3.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_steward_resolution_parity.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_storage_parity.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_store_factory.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_tenant_isolation.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_turn_schema.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_two_pass_recall.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_v311_fixes.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_v313_e2e.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_v390_e2e.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_v391_strict_warnings.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_vault_exporter.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_vault_linter_orphan.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_vector_search.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_verbatim_recall.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_verbatim_store.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_verbatim_store_qdrant.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_webhook.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_webhook_hmac.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_whatsapp_importer.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_wiki_autopromote.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_wiki_binding.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_wiki_engine_idempotency.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_wiki_freshness.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_wiki_similarity_multiscope.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.0}/tests/test_wiki_suggest.py +0 -0
- {memorymaster-3.24.0 → memorymaster-3.26.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.
|
|
3
|
+
Version: 3.26.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)
|
|
54
54
|
[](https://www.python.org/downloads/)
|
|
55
|
-
[]()
|
|
56
56
|
[]()
|
|
57
57
|
[]()
|
|
58
58
|
[](https://pypi.org/project/memorymaster/)
|
|
@@ -6,7 +6,7 @@ Lifecycle-managed claims with citations, conflict detection, steward governance,
|
|
|
6
6
|
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://www.python.org/downloads/)
|
|
9
|
-
[]()
|
|
10
10
|
[]()
|
|
11
11
|
[]()
|
|
12
12
|
[](https://pypi.org/project/memorymaster/)
|
|
@@ -77,9 +77,23 @@ def _canonical_pair(a_id: int, b_id: int) -> tuple[int, int]:
|
|
|
77
77
|
return (a_id, b_id) if a_id <= b_id else (b_id, a_id)
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
_ensured_verdict_dbs: set[str] = set()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _ensure_verdict_table(conn: sqlite3.Connection, *, db_key: str | None = None) -> None:
|
|
84
|
+
"""Create the verdict cache table if absent.
|
|
85
|
+
|
|
86
|
+
``db_key`` lets per-claim callers skip the redundant ``CREATE TABLE IF NOT
|
|
87
|
+
EXISTS`` + ``commit`` on every claim in a steward cycle: once a given DB has
|
|
88
|
+
been ensured in this process we never re-issue the DDL. ``run_probe`` (one
|
|
89
|
+
DDL per whole run) passes no key and always ensures, preserving its semantics.
|
|
90
|
+
"""
|
|
91
|
+
if db_key is not None and db_key in _ensured_verdict_dbs:
|
|
92
|
+
return
|
|
81
93
|
conn.execute(_VERDICT_DDL)
|
|
82
94
|
conn.commit()
|
|
95
|
+
if db_key is not None:
|
|
96
|
+
_ensured_verdict_dbs.add(db_key)
|
|
83
97
|
|
|
84
98
|
|
|
85
99
|
def _cache_get(conn: sqlite3.Connection, a_id: int, b_id: int, model: str) -> dict | None:
|
|
@@ -164,13 +178,23 @@ def sample_candidate_pairs(
|
|
|
164
178
|
return []
|
|
165
179
|
embeddings = [provider.embed(_embed_text(c)) for c in usable]
|
|
166
180
|
pairs: list[tuple[Claim, Claim, float]] = []
|
|
181
|
+
# The cosine sweep is O(n^2). Once we have already collected ``limit`` in-band
|
|
182
|
+
# pairs there is no point paying for the rest of the quadratic scan — the
|
|
183
|
+
# caller only ever consumes the first ``limit`` after the sort below, so break
|
|
184
|
+
# early instead of building (then discarding) thousands of extra pairs.
|
|
185
|
+
done = False
|
|
167
186
|
for i in range(len(usable)):
|
|
187
|
+
if done:
|
|
188
|
+
break
|
|
168
189
|
for j in range(i + 1, len(usable)):
|
|
169
190
|
if _prefiltered(usable[i], usable[j]):
|
|
170
191
|
continue
|
|
171
192
|
sim = cosine_similarity(embeddings[i], embeddings[j])
|
|
172
193
|
if sim_low <= sim < sim_high:
|
|
173
194
|
pairs.append((usable[i], usable[j], round(sim, 4)))
|
|
195
|
+
if limit is not None and len(pairs) >= limit:
|
|
196
|
+
done = True
|
|
197
|
+
break
|
|
174
198
|
pairs.sort(key=lambda p: -p[2])
|
|
175
199
|
if limit is not None:
|
|
176
200
|
pairs = pairs[:limit]
|
|
@@ -193,7 +217,11 @@ def _judge_llm(a: Claim, b: Claim) -> dict | None:
|
|
|
193
217
|
if isinstance(item, dict) and "contradicts" in item:
|
|
194
218
|
return {
|
|
195
219
|
"contradicts": bool(item.get("contradicts")),
|
|
196
|
-
|
|
220
|
+
# Floor empty/off-vocabulary severity to "medium" at the source
|
|
221
|
+
# (the JSON contract is advisory). Defaulting to "low" here would
|
|
222
|
+
# silently bury a real contradiction in the least-actionable tier
|
|
223
|
+
# and made the "medium" default downstream dead code.
|
|
224
|
+
"severity": _coerce_severity(item.get("severity")),
|
|
197
225
|
"reason": (item.get("reason") or "").strip(),
|
|
198
226
|
"cached": False,
|
|
199
227
|
}
|
|
@@ -206,6 +234,36 @@ def _model_key() -> str:
|
|
|
206
234
|
return f"{provider}:{model}"
|
|
207
235
|
|
|
208
236
|
|
|
237
|
+
_VALID_SEVERITIES = {"low", "medium", "high"}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _coerce_severity(value: Any) -> str:
|
|
241
|
+
"""Normalize a verdict severity to a canonical bucket.
|
|
242
|
+
|
|
243
|
+
The LLM judge and verdict cache can both yield an empty or off-vocabulary
|
|
244
|
+
severity (the JSON contract is advisory, not enforced). When that happens we
|
|
245
|
+
floor to ``"medium"`` rather than ``"low"`` so a contradiction surfaced by
|
|
246
|
+
the probe is never silently downgraded to the least-actionable tier.
|
|
247
|
+
"""
|
|
248
|
+
sev = str(value or "").strip().lower()
|
|
249
|
+
return sev if sev in _VALID_SEVERITIES else "medium"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _safe_judge_reason(reason: Any) -> str | None:
|
|
253
|
+
"""Return the raw judge reason unless it leaks sensitive content.
|
|
254
|
+
|
|
255
|
+
Both ``probe_for_claim`` and ``run_probe(apply=True)`` persist/surface the
|
|
256
|
+
LLM judge reason. Routing both through this guard ensures a reason that the
|
|
257
|
+
sensitivity filter flags is dropped (returns ``None``) on EVERY path — the
|
|
258
|
+
events table must never receive an unredacted secret.
|
|
259
|
+
"""
|
|
260
|
+
text = str(reason or "")
|
|
261
|
+
_, leaks = redact_text(text)
|
|
262
|
+
if leaks:
|
|
263
|
+
return None
|
|
264
|
+
return text
|
|
265
|
+
|
|
266
|
+
|
|
209
267
|
# ---------------------------------------------------------------------------
|
|
210
268
|
# Wilson confidence interval
|
|
211
269
|
# ---------------------------------------------------------------------------
|
|
@@ -303,15 +361,19 @@ def run_probe(
|
|
|
303
361
|
if verdict["contradicts"]:
|
|
304
362
|
stats["contradictions"] += 1
|
|
305
363
|
loser, winner = (a, b) if a.confidence <= b.confidence else (b, a)
|
|
364
|
+
# Same sensitivity guard the per-claim path uses: never let an
|
|
365
|
+
# unredacted judge reason reach the events table on apply.
|
|
366
|
+
safe_reason = _safe_judge_reason(verdict.get("reason"))
|
|
306
367
|
stats["found"].append({
|
|
307
368
|
"claim_a_id": a.id, "claim_b_id": b.id, "similarity": sim,
|
|
308
|
-
"severity": verdict["severity"], "reason": verdict
|
|
369
|
+
"severity": verdict["severity"], "reason": verdict.get("reason", ""),
|
|
309
370
|
"flag_candidate_id": loser.id,
|
|
310
371
|
})
|
|
311
372
|
if apply:
|
|
373
|
+
detail = f" ({safe_reason})" if safe_reason else ""
|
|
312
374
|
transition_claim(
|
|
313
375
|
service.store, loser.id, "conflicted",
|
|
314
|
-
reason=f"contradiction_probe: contradicts claim {winner.id}
|
|
376
|
+
reason=f"contradiction_probe: contradicts claim {winner.id}{detail}",
|
|
315
377
|
event_type="transition",
|
|
316
378
|
)
|
|
317
379
|
stats["flagged_conflicted"] += 1
|
|
@@ -357,7 +419,7 @@ def probe_for_claim(
|
|
|
357
419
|
metrics: dict[str, Any] = {
|
|
358
420
|
"pairs_checked": 0, "contradictions": 0,
|
|
359
421
|
"cache_hits": 0, "llm_calls": 0, "errors": 0,
|
|
360
|
-
"timed_out": False, "duration_ms": 0.0,
|
|
422
|
+
"timed_out": False, "budget_exhausted": False, "duration_ms": 0.0,
|
|
361
423
|
}
|
|
362
424
|
reasons: list[dict[str, Any]] = []
|
|
363
425
|
|
|
@@ -415,7 +477,7 @@ def probe_for_claim(
|
|
|
415
477
|
|
|
416
478
|
conn = sqlite3.connect(str(db_path))
|
|
417
479
|
try:
|
|
418
|
-
_ensure_verdict_table(conn)
|
|
480
|
+
_ensure_verdict_table(conn, db_key=str(db_path))
|
|
419
481
|
model = _model_key()
|
|
420
482
|
for peer, sim in candidates:
|
|
421
483
|
metrics["pairs_checked"] += 1
|
|
@@ -426,7 +488,11 @@ def probe_for_claim(
|
|
|
426
488
|
try:
|
|
427
489
|
verdict = _judge_llm(claim, peer)
|
|
428
490
|
except llm_budget.LLMBudgetExceeded:
|
|
429
|
-
|
|
491
|
+
# Budget exhaustion is an expected guardrail, NOT a probe
|
|
492
|
+
# failure — the steward must not count it toward the circuit
|
|
493
|
+
# breaker (which would disable the probe for the rest of the
|
|
494
|
+
# cycle). Flag it distinctly from a genuine timeout.
|
|
495
|
+
metrics["budget_exhausted"] = True
|
|
430
496
|
break
|
|
431
497
|
metrics["llm_calls"] += 1
|
|
432
498
|
if verdict is None:
|
|
@@ -435,14 +501,13 @@ def probe_for_claim(
|
|
|
435
501
|
_cache_put(conn, claim.id, peer.id, model, verdict)
|
|
436
502
|
if not verdict.get("contradicts"):
|
|
437
503
|
continue
|
|
438
|
-
verdict_reason = verdict.get("reason"
|
|
439
|
-
|
|
440
|
-
if leaks:
|
|
504
|
+
verdict_reason = _safe_judge_reason(verdict.get("reason"))
|
|
505
|
+
if verdict_reason is None:
|
|
441
506
|
continue # drop rather than surface a sensitive judge-reason
|
|
442
507
|
metrics["contradictions"] += 1
|
|
443
508
|
reasons.append({
|
|
444
509
|
"code": "contradiction_probe.semantic_pair",
|
|
445
|
-
"severity": verdict.get("severity"
|
|
510
|
+
"severity": _coerce_severity(verdict.get("severity")),
|
|
446
511
|
"detail": f"Semantic contradiction with claim {peer.id}: {verdict_reason}",
|
|
447
512
|
"evidence": {
|
|
448
513
|
"peer_claim_id": int(peer.id),
|
|
@@ -116,7 +116,14 @@ def read(db_path: str, cache_key: str) -> list[dict] | None:
|
|
|
116
116
|
"SELECT result_json, generation FROM query_cache WHERE cache_key = ?",
|
|
117
117
|
(cache_key,),
|
|
118
118
|
).fetchone()
|
|
119
|
-
if row is None
|
|
119
|
+
if row is None:
|
|
120
|
+
return None
|
|
121
|
+
if int(row["generation"]) != gen:
|
|
122
|
+
try:
|
|
123
|
+
conn.execute("DELETE FROM query_cache WHERE cache_key = ?", (cache_key,))
|
|
124
|
+
conn.commit()
|
|
125
|
+
except sqlite3.Error:
|
|
126
|
+
pass
|
|
120
127
|
return None
|
|
121
128
|
return json.loads(row["result_json"])
|
|
122
129
|
except (sqlite3.Error, json.JSONDecodeError, ValueError) as exc:
|
|
@@ -126,6 +133,23 @@ def read(db_path: str, cache_key: str) -> list[dict] | None:
|
|
|
126
133
|
conn.close()
|
|
127
134
|
|
|
128
135
|
|
|
136
|
+
def evict_stale(db_path: str) -> None:
|
|
137
|
+
"""Delete query cache rows older than the current corpus generation."""
|
|
138
|
+
try:
|
|
139
|
+
conn = _connect(db_path)
|
|
140
|
+
except sqlite3.Error as exc:
|
|
141
|
+
logger.warning("query_cache.evict_stale connect failed: %s", exc)
|
|
142
|
+
return
|
|
143
|
+
try:
|
|
144
|
+
gen = current_generation(conn)
|
|
145
|
+
conn.execute("DELETE FROM query_cache WHERE generation < ?", (gen,))
|
|
146
|
+
conn.commit()
|
|
147
|
+
except (sqlite3.Error, ValueError) as exc:
|
|
148
|
+
logger.warning("query_cache.evict_stale failed: %s", exc)
|
|
149
|
+
finally:
|
|
150
|
+
conn.close()
|
|
151
|
+
|
|
152
|
+
|
|
129
153
|
def write(db_path: str, cache_key: str, stub_rows: list[dict], generation: int) -> None:
|
|
130
154
|
"""Store result stubs tagged with ``generation``. Best-effort.
|
|
131
155
|
|
|
@@ -48,6 +48,11 @@ _CORRECTION_KEYWORDS = (
|
|
|
48
48
|
"wrong", "not what", "that's not", "thats not", "revert", "undo",
|
|
49
49
|
"should have", "shouldn't", "why did you", "no need", "stop ",
|
|
50
50
|
)
|
|
51
|
+
_CORRECTION_FTS_MATCH = (
|
|
52
|
+
'"no" OR "don" OR "do" OR "dont" OR "instead" OR "actually" OR '
|
|
53
|
+
'"wrong" OR "not" OR "revert" OR "undo" OR "should" OR "shouldn" OR '
|
|
54
|
+
'"why" OR "stop"'
|
|
55
|
+
)
|
|
51
56
|
|
|
52
57
|
_MINER_STATE_DDL = """
|
|
53
58
|
CREATE TABLE IF NOT EXISTS miner_state (
|
|
@@ -132,6 +137,18 @@ def _candidate_batch(
|
|
|
132
137
|
params: list[Any] = [since_id]
|
|
133
138
|
params.extend(f"%{kw}%" for kw in _CORRECTION_KEYWORDS)
|
|
134
139
|
params.append(batch_size)
|
|
140
|
+
try:
|
|
141
|
+
return conn.execute(
|
|
142
|
+
f"""SELECT v.id, v.session_id, v.content, v.scope FROM verbatim_fts f
|
|
143
|
+
JOIN verbatim_memories v ON v.id = f.rowid
|
|
144
|
+
WHERE verbatim_fts MATCH ?
|
|
145
|
+
AND v.role = 'user' AND v.id > ? AND ({like_clause})
|
|
146
|
+
ORDER BY v.id ASC LIMIT ?""",
|
|
147
|
+
[_CORRECTION_FTS_MATCH, *params],
|
|
148
|
+
).fetchall()
|
|
149
|
+
except sqlite3.Error as exc:
|
|
150
|
+
logger.debug("rule_miner: verbatim_fts pre-filter failed, using LIKE scan: %s", exc)
|
|
151
|
+
|
|
135
152
|
return conn.execute(
|
|
136
153
|
f"""SELECT id, session_id, content, scope FROM verbatim_memories
|
|
137
154
|
WHERE role = 'user' AND id > ? AND ({like_clause})
|
|
@@ -158,12 +175,26 @@ def _build_window(assistant_content: str, user_content: str) -> str:
|
|
|
158
175
|
return f"ASSISTANT: {asst}\nUSER: {user}"
|
|
159
176
|
|
|
160
177
|
|
|
178
|
+
class TransientLLMError(RuntimeError):
|
|
179
|
+
"""The LLM provider returned an empty response — almost always a transient
|
|
180
|
+
failure (outage, timeout, rate-limit returning ''), NOT a genuine "no
|
|
181
|
+
correction" verdict. Callers must distinguish the two: advancing the
|
|
182
|
+
watermark past a candidate that was never actually judged would permanently
|
|
183
|
+
skip real corrections during a provider outage. (audit:
|
|
184
|
+
mine-rules-silent-watermark-advance)"""
|
|
185
|
+
|
|
186
|
+
|
|
161
187
|
def _extract_rule(window: str) -> dict[str, str] | None:
|
|
162
188
|
"""Ask the LLM to distill a rule. Returns ``{trigger, action, rationale}``
|
|
163
|
-
or ``None`` when
|
|
189
|
+
or ``None`` when the model responded but found no correction.
|
|
190
|
+
|
|
191
|
+
Raises ``LLMBudgetExceeded`` if the per-cycle cap is hit, and
|
|
192
|
+
``TransientLLMError`` if the provider returned nothing (so the caller can
|
|
193
|
+
avoid advancing the watermark). A non-empty response that simply doesn't
|
|
194
|
+
parse to a rule is a genuine "no correction" (``None``), not transient."""
|
|
164
195
|
raw = llm_provider.call_llm(_CORRECTION_PROMPT, window)
|
|
165
196
|
if not raw or not raw.strip():
|
|
166
|
-
|
|
197
|
+
raise TransientLLMError("empty LLM response")
|
|
167
198
|
for item in llm_provider.parse_json_response(raw):
|
|
168
199
|
if not isinstance(item, dict):
|
|
169
200
|
continue
|
|
@@ -300,6 +331,12 @@ def _process_candidate(
|
|
|
300
331
|
except llm_budget.LLMBudgetExceeded as exc:
|
|
301
332
|
stats["aborted_reason"] = exc.reason
|
|
302
333
|
return "aborted"
|
|
334
|
+
except TransientLLMError:
|
|
335
|
+
# Provider returned empty (likely transient outage). Abort the run WITHOUT
|
|
336
|
+
# advancing the watermark past this row, so the candidate is retried next
|
|
337
|
+
# run instead of silently skipped. Stopping is safe: mining is a batch job.
|
|
338
|
+
stats["aborted_reason"] = "llm_transient_failure"
|
|
339
|
+
return "aborted"
|
|
303
340
|
|
|
304
341
|
stats["llm_calls"] += 1
|
|
305
342
|
if rule is None or _is_sensitive_rule(rule):
|
|
@@ -412,7 +449,10 @@ def mine_transcript_rules(
|
|
|
412
449
|
stats["windows"] += 1
|
|
413
450
|
try:
|
|
414
451
|
rule = _extract_rule(_build_window(asst_text, user_text))
|
|
415
|
-
except llm_budget.LLMBudgetExceeded:
|
|
452
|
+
except (llm_budget.LLMBudgetExceeded, TransientLLMError):
|
|
453
|
+
# Budget hit or provider returned empty — stop this best-effort
|
|
454
|
+
# Stop-hook pass. No watermark here, so nothing to skip; a
|
|
455
|
+
# later session re-mines its own corrections.
|
|
416
456
|
break
|
|
417
457
|
stats["llm_calls"] += 1
|
|
418
458
|
if rule is None or _is_sensitive_rule(rule):
|
|
@@ -9,7 +9,7 @@ import subprocess
|
|
|
9
9
|
import time
|
|
10
10
|
from typing import Any, Literal
|
|
11
11
|
|
|
12
|
-
from memorymaster import observability
|
|
12
|
+
from memorymaster import llm_budget, observability
|
|
13
13
|
from memorymaster.lifecycle import transition_claim
|
|
14
14
|
from memorymaster.security import is_sensitive_claim
|
|
15
15
|
from memorymaster.service import MemoryService
|
|
@@ -891,6 +891,14 @@ def _make_circuit_open_result(probe: ProbeSpec, probe_type: str, probe_failure_t
|
|
|
891
891
|
)
|
|
892
892
|
|
|
893
893
|
|
|
894
|
+
# Open ONE per-cycle LLM budget scope for the whole cycle so
|
|
895
|
+
# MEMORYMASTER_MAX_LLM_CALLS_PER_CYCLE (and the token / provider-failure caps)
|
|
896
|
+
# apply across every LLM-using probe this cycle — notably the contradiction
|
|
897
|
+
# probe's judge. cycle_scope is a @contextmanager, so it doubles as a context
|
|
898
|
+
# decorator and recreates a fresh scope per call. Without this the cap was a
|
|
899
|
+
# no-op for the steward and probe_for_claim's LLMBudgetExceeded handler was dead
|
|
900
|
+
# in production. (audit: probe-for-claim-budget-no-scope)
|
|
901
|
+
@llm_budget.cycle_scope()
|
|
894
902
|
def _run_cycle(
|
|
895
903
|
service: MemoryService,
|
|
896
904
|
*,
|
|
@@ -132,10 +132,19 @@ def cleanup(
|
|
|
132
132
|
|
|
133
133
|
if dedup:
|
|
134
134
|
# Identify ids to drop: for each (session_id, content) group, keep
|
|
135
|
-
# the smallest id (oldest), drop the rest.
|
|
135
|
+
# the smallest id (oldest), drop the rest. NOT EXISTS against a
|
|
136
|
+
# correlated "is there an older twin?" probe avoids the O(n^2)
|
|
137
|
+
# full-table anti-join that `id NOT IN (SELECT MIN(id) ...)` forces
|
|
138
|
+
# SQLite into on the cold CLI path. Results are identical: a row is
|
|
139
|
+
# dropped iff another row in the same (session_id, content) group has
|
|
140
|
+
# a strictly smaller id.
|
|
136
141
|
rows = conn.execute(
|
|
137
|
-
"""SELECT id FROM verbatim_memories
|
|
138
|
-
|
|
142
|
+
"""SELECT id FROM verbatim_memories AS v
|
|
143
|
+
WHERE EXISTS (
|
|
144
|
+
SELECT 1 FROM verbatim_memories AS older
|
|
145
|
+
WHERE older.session_id IS v.session_id
|
|
146
|
+
AND older.content = v.content
|
|
147
|
+
AND older.id < v.id
|
|
139
148
|
)"""
|
|
140
149
|
).fetchall()
|
|
141
150
|
ids = [int(r[0]) for r in rows]
|
|
@@ -22,11 +22,11 @@ from datetime import datetime, timezone
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import Any
|
|
24
24
|
|
|
25
|
+
# Credential detection delegated to the canonical filter in memorymaster.security.
|
|
26
|
+
from memorymaster.security import redact_text as _redact_text
|
|
27
|
+
|
|
25
28
|
logger = logging.getLogger(__name__)
|
|
26
29
|
|
|
27
|
-
# Credential detection delegated to the canonical filter in
|
|
28
|
-
# memorymaster.security — single source of truth.
|
|
29
|
-
from memorymaster.security import redact_text as _redact_text
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def _contains_sensitive(text: str) -> bool:
|
|
@@ -76,6 +76,33 @@ def store_verbatim(
|
|
|
76
76
|
timestamp: str | None = None,
|
|
77
77
|
) -> int | None:
|
|
78
78
|
"""Store a verbatim conversation turn. Returns row ID or None if filtered."""
|
|
79
|
+
# closing() guarantees the connection (and its WAL write lock) is released
|
|
80
|
+
# even if an INSERT/commit raises (e.g. "database is locked" under
|
|
81
|
+
# concurrent MCP/Stop-hook writers) - this is the hottest write path.
|
|
82
|
+
with closing(_connect(db_path)) as conn:
|
|
83
|
+
row_id = _store_verbatim_conn(
|
|
84
|
+
conn,
|
|
85
|
+
session_id,
|
|
86
|
+
role,
|
|
87
|
+
content,
|
|
88
|
+
scope,
|
|
89
|
+
source_agent,
|
|
90
|
+
timestamp,
|
|
91
|
+
)
|
|
92
|
+
conn.commit()
|
|
93
|
+
return row_id
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _store_verbatim_conn(
|
|
97
|
+
conn: sqlite3.Connection,
|
|
98
|
+
session_id: str,
|
|
99
|
+
role: str,
|
|
100
|
+
content: str,
|
|
101
|
+
scope: str = "project",
|
|
102
|
+
source_agent: str = "",
|
|
103
|
+
timestamp: str | None = None,
|
|
104
|
+
) -> int | None:
|
|
105
|
+
"""Store one turn using an existing connection without committing."""
|
|
79
106
|
if not content or len(content) < 20:
|
|
80
107
|
return None
|
|
81
108
|
if _row_has_sensitive_field(role or "", source_agent or "", content):
|
|
@@ -83,37 +110,32 @@ def store_verbatim(
|
|
|
83
110
|
|
|
84
111
|
now = timestamp or datetime.now(timezone.utc).isoformat()
|
|
85
112
|
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
113
|
+
# Dedup by exact content within the same session. Uses idx_verbatim_session
|
|
114
|
+
# so the lookup is O(rows-in-session), not table-scan. The previous FTS5-based
|
|
115
|
+
# dedup query passed a sha256 hex prefix to MATCH which never resolves -
|
|
116
|
+
# the FTS5 index stores the content text, not its hash. Result: 9M+ rows
|
|
117
|
+
# accumulated for orchestrator sessions because every Stop event re-inserted
|
|
118
|
+
# every message. Fixed 2026-05-03; see mm-0c43.
|
|
119
|
+
existing = conn.execute(
|
|
120
|
+
"SELECT id FROM verbatim_memories WHERE session_id = ? AND content = ? LIMIT 1",
|
|
121
|
+
(session_id, content),
|
|
122
|
+
).fetchone()
|
|
123
|
+
if existing:
|
|
124
|
+
return None
|
|
109
125
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
cur = conn.execute(
|
|
127
|
+
"""INSERT INTO verbatim_memories (session_id, role, content, scope, timestamp, source_agent)
|
|
128
|
+
VALUES (?, ?, ?, ?, ?, ?)""",
|
|
129
|
+
(session_id, role, content, scope, now, source_agent),
|
|
130
|
+
)
|
|
131
|
+
row_id = cur.lastrowid
|
|
132
|
+
|
|
133
|
+
# Update FTS
|
|
134
|
+
conn.execute(
|
|
135
|
+
"INSERT INTO verbatim_fts(rowid, content) VALUES (?, ?)",
|
|
136
|
+
(row_id, content),
|
|
137
|
+
)
|
|
138
|
+
return row_id
|
|
117
139
|
|
|
118
140
|
|
|
119
141
|
def _extract_role_content(entry: dict) -> tuple[str, str]:
|
|
@@ -164,29 +186,31 @@ def store_transcript(
|
|
|
164
186
|
stats = {"stored": 0, "skipped": 0}
|
|
165
187
|
session_id = path.stem # Use filename as session ID
|
|
166
188
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
with closing(_connect(db_path)) as conn:
|
|
190
|
+
for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
|
|
191
|
+
if not line.strip():
|
|
192
|
+
continue
|
|
193
|
+
try:
|
|
194
|
+
entry = json.loads(line)
|
|
195
|
+
except json.JSONDecodeError:
|
|
196
|
+
continue
|
|
197
|
+
if not isinstance(entry, dict):
|
|
198
|
+
continue
|
|
199
|
+
|
|
200
|
+
role, content = _extract_role_content(entry)
|
|
201
|
+
if role not in ("user", "assistant"):
|
|
202
|
+
stats["skipped"] += 1
|
|
203
|
+
continue
|
|
204
|
+
if not content or len(content) < 20:
|
|
205
|
+
stats["skipped"] += 1
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
row_id = _store_verbatim_conn(conn, session_id, role, content, scope, source_agent)
|
|
209
|
+
if row_id:
|
|
210
|
+
stats["stored"] += 1
|
|
211
|
+
else:
|
|
212
|
+
stats["skipped"] += 1
|
|
213
|
+
conn.commit()
|
|
190
214
|
|
|
191
215
|
return stats
|
|
192
216
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memorymaster
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.26.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)
|
|
54
54
|
[](https://www.python.org/downloads/)
|
|
55
|
-
[]()
|
|
56
56
|
[]()
|
|
57
57
|
[]()
|
|
58
58
|
[](https://pypi.org/project/memorymaster/)
|
|
@@ -218,6 +218,7 @@ tests/test_auto_ingest_hook_schema.py
|
|
|
218
218
|
tests/test_auto_resolver.py
|
|
219
219
|
tests/test_auto_validate.py
|
|
220
220
|
tests/test_backend_parity.py
|
|
221
|
+
tests/test_bench_answer_prompt.py
|
|
221
222
|
tests/test_bm25_per_field.py
|
|
222
223
|
tests/test_calibration.py
|
|
223
224
|
tests/test_calibration_priors_applied.py
|
|
@@ -229,6 +230,7 @@ tests/test_classify_hook_f1.py
|
|
|
229
230
|
tests/test_classify_hook_latency.py
|
|
230
231
|
tests/test_claude_to_turns.py
|
|
231
232
|
tests/test_cli_dry_run.py
|
|
233
|
+
tests/test_cli_handlers_extra.py
|
|
232
234
|
tests/test_cli_json_flag.py
|
|
233
235
|
tests/test_cli_ready.py
|
|
234
236
|
tests/test_cli_review_queue.py
|
|
@@ -245,6 +247,7 @@ tests/test_confusion_matrix_eval.py
|
|
|
245
247
|
tests/test_connection_retry.py
|
|
246
248
|
tests/test_connectors.py
|
|
247
249
|
tests/test_context_hook.py
|
|
250
|
+
tests/test_context_hook_extra.py
|
|
248
251
|
tests/test_context_optimizer.py
|
|
249
252
|
tests/test_context_optimizer_provider.py
|
|
250
253
|
tests/test_contradiction_probe.py
|
|
@@ -258,6 +261,7 @@ tests/test_dashboard_review_queue.py
|
|
|
258
261
|
tests/test_daydream_ingest.py
|
|
259
262
|
tests/test_db_merge_confidence_conflict.py
|
|
260
263
|
tests/test_db_merge_coverage_v2.py
|
|
264
|
+
tests/test_db_merge_extra.py
|
|
261
265
|
tests/test_decay_coverage.py
|
|
262
266
|
tests/test_decay_respects_pinned.py
|
|
263
267
|
tests/test_dedup.py
|
|
@@ -266,6 +270,7 @@ tests/test_dedup_conflict_disambiguation.py
|
|
|
266
270
|
tests/test_delta_sync.py
|
|
267
271
|
tests/test_deterministic_predicates.py
|
|
268
272
|
tests/test_dream_bridge_coverage_v2.py
|
|
273
|
+
tests/test_dream_bridge_extra.py
|
|
269
274
|
tests/test_dream_bridge_sensitivity.py
|
|
270
275
|
tests/test_embeddings_coverage.py
|
|
271
276
|
tests/test_entity_extractor.py
|
|
@@ -275,6 +280,7 @@ tests/test_entity_graph_export.py
|
|
|
275
280
|
tests/test_entity_new_kinds.py
|
|
276
281
|
tests/test_entity_regex_v3.py
|
|
277
282
|
tests/test_entity_registry.py
|
|
283
|
+
tests/test_entity_registry_extra.py
|
|
278
284
|
tests/test_eval_harness.py
|
|
279
285
|
tests/test_events_schema.py
|
|
280
286
|
tests/test_extract_llm_ollama.py
|
|
@@ -296,6 +302,7 @@ tests/test_lifecycle_supersede_invariant.py
|
|
|
296
302
|
tests/test_llm_budget.py
|
|
297
303
|
tests/test_llm_fallback.py
|
|
298
304
|
tests/test_llm_provider_claude_cli.py
|
|
305
|
+
tests/test_llm_provider_extra.py
|
|
299
306
|
tests/test_llm_provider_key_rotation.py
|
|
300
307
|
tests/test_llm_steward_coverage.py
|
|
301
308
|
tests/test_llm_steward_key_rotation.py
|
|
@@ -310,6 +317,7 @@ tests/test_meta_decisions.py
|
|
|
310
317
|
tests/test_metrics_exporter.py
|
|
311
318
|
tests/test_migrations.py
|
|
312
319
|
tests/test_observability.py
|
|
320
|
+
tests/test_observability_extra.py
|
|
313
321
|
tests/test_obsidian_mind_patterns.py
|
|
314
322
|
tests/test_operator.py
|
|
315
323
|
tests/test_operator_queue.py
|
|
@@ -332,6 +340,7 @@ tests/test_recall_tokenizer.py
|
|
|
332
340
|
tests/test_recall_vector_fallback.py
|
|
333
341
|
tests/test_reliability_hardening.py
|
|
334
342
|
tests/test_resolvers_concurrent_supersede.py
|
|
343
|
+
tests/test_retrieval_extra.py
|
|
335
344
|
tests/test_retrieval_profile.py
|
|
336
345
|
tests/test_retrieval_profiles.py
|
|
337
346
|
tests/test_retrieval_rrf_tiebreaker.py
|
|
@@ -364,6 +373,7 @@ tests/test_steward_daydream_hook.py
|
|
|
364
373
|
tests/test_steward_features.py
|
|
365
374
|
tests/test_steward_features_v3.py
|
|
366
375
|
tests/test_steward_resolution_parity.py
|
|
376
|
+
tests/test_storage_extra.py
|
|
367
377
|
tests/test_storage_parity.py
|
|
368
378
|
tests/test_store_factory.py
|
|
369
379
|
tests/test_tenant_isolation.py
|
|
@@ -375,6 +385,7 @@ tests/test_v313_run_cycle_dedupe.py
|
|
|
375
385
|
tests/test_v390_e2e.py
|
|
376
386
|
tests/test_v391_strict_warnings.py
|
|
377
387
|
tests/test_vault_exporter.py
|
|
388
|
+
tests/test_vault_linter_extra.py
|
|
378
389
|
tests/test_vault_linter_orphan.py
|
|
379
390
|
tests/test_vector_search.py
|
|
380
391
|
tests/test_verbatim_cleanup.py
|
|
@@ -387,6 +398,7 @@ tests/test_webhook_hmac.py
|
|
|
387
398
|
tests/test_whatsapp_importer.py
|
|
388
399
|
tests/test_wiki_autopromote.py
|
|
389
400
|
tests/test_wiki_binding.py
|
|
401
|
+
tests/test_wiki_engine_extra.py
|
|
390
402
|
tests/test_wiki_engine_idempotency.py
|
|
391
403
|
tests/test_wiki_explored_and_contradictions.py
|
|
392
404
|
tests/test_wiki_freshness.py
|