memorymaster 3.19.0__tar.gz → 3.21.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.19.0/memorymaster.egg-info → memorymaster-3.21.0}/PKG-INFO +6 -3
- {memorymaster-3.19.0 → memorymaster-3.21.0}/README.md +5 -2
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/__init__.py +1 -1
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/cli.py +24 -1
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/cli_handlers_basic.py +92 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/cli_handlers_curation.py +26 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +24 -0
- memorymaster-3.21.0/memorymaster/delta_sync.py +172 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/mcp_server.py +51 -0
- memorymaster-3.21.0/memorymaster/migrations/0001_initial.py +25 -0
- memorymaster-3.21.0/memorymaster/migrations/0002_miner_state.py +38 -0
- memorymaster-3.21.0/memorymaster/migrations/__init__.py +41 -0
- memorymaster-3.21.0/memorymaster/migrations/runner.py +268 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/postgres_store.py +9 -0
- memorymaster-3.21.0/memorymaster/rule_miner.py +443 -0
- memorymaster-3.21.0/memorymaster/rules.py +104 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/service.py +39 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/storage.py +10 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/verbatim_store.py +41 -9
- {memorymaster-3.19.0 → memorymaster-3.21.0/memorymaster.egg-info}/PKG-INFO +6 -3
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster.egg-info/SOURCES.txt +12 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/pyproject.toml +1 -1
- memorymaster-3.21.0/tests/conftest.py +103 -0
- memorymaster-3.21.0/tests/test_backend_parity.py +167 -0
- memorymaster-3.21.0/tests/test_delta_sync.py +261 -0
- memorymaster-3.21.0/tests/test_migrations.py +292 -0
- memorymaster-3.21.0/tests/test_rule_claims.py +181 -0
- memorymaster-3.21.0/tests/test_rule_miner.py +297 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_verbatim_dedup.py +36 -0
- memorymaster-3.19.0/tests/conftest.py +0 -37
- {memorymaster-3.19.0 → memorymaster-3.21.0}/LICENSE +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/benchmarks/longmemeval_runner.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/benchmarks/longmemeval_vector_runner.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/benchmarks/perf_smoke.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/__main__.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/_storage_lifecycle.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/_storage_read.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/_storage_schema.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/_storage_shared.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/_storage_sources.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/_storage_write_claims.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/access_control.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/action_exporters.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/action_extractor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/atlas_claim_extractor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/atlas_contract.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/auto_extractor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/auto_resolver.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/candidate_dedupe.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/claim_edges.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/claim_verifier.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/cli_helpers.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/closets.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/claude-md-append.md +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/conflict_resolver.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/connectors/__init__.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/connectors/whatsapp.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/context_hook.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/context_optimizer.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/daily_notes.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/dashboard.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/dashboard_auth.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/db_merge.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/dream_bridge.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/embeddings.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/entity_extractor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/entity_graph.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/entity_registry.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/federated_graphify.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/feedback.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/graph_store.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/hook_log.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/__init__.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/calibration.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/compact_summaries.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/compactor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/daydream_ingest.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/decay.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/dedup.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/deterministic.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/entity_graph_export.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/extractor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/staleness.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/jobs/validator.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/key_rotator.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/lifecycle.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/llm_budget.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/llm_provider.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/llm_rerank.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/llm_steward.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/mcp_path_policy.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/mcp_usage.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/media_processing.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/media_providers.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/metrics_exporter.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/models.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/observability.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/operator.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/operator_queue.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/plugins.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/policy.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/qdrant_backend.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/qdrant_recall_fallback.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/qmd_bridge.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/query_classifier.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/query_expansion.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/recall_fusion.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/recall_tokenizer.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/retrieval.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/retry.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/review.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/rl_trainer.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/scheduler.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/schema.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/schema.sql +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/schema_postgres.sql +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/scope_utils.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/security.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/session_tracker.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/setup_hooks.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/skill_evolver.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/snapshot.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/steward.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/steward_classifier.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/steward_features.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/store_factory.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/transcript_miner.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/turn_schema.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/vault_bases.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/vault_curator.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/vault_exporter.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/vault_linter.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/vault_log.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/vault_query_capture.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/vault_synthesis.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/verbatim_recall.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/webhook.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/wiki_engine.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/wiki_freshness.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/wiki_similarity.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/wiki_suggest.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster/wiki_validate.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster.egg-info/dependency_links.txt +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster.egg-info/entry_points.txt +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster.egg-info/requires.txt +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/memorymaster.egg-info/top_level.txt +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/agg_recall_latency.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/alert_operator_metrics.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/audit_dedupe_precision.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/autoresearch_daemon.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/backfill_entity_extraction.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/backfill_graph_store.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/backfill_stop_hook_citations.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/backtest_steward_classifier.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/build_steward_training_set.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/check_hook_template_drift.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/claude_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/codex_live_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/compaction_edge_cases.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/compaction_trace_report.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/compaction_trace_validate.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/confusion_matrix_eval.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/conversation_importer.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/conversation_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/e2e_operator.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/email_live_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/eval_bm25_sweep.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/eval_classify_f1.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/eval_memorymaster.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/eval_recall_precision_at_5.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/eval_recall_quality.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/eval_steward_pareto.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/eval_verbatim_recall.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/expand_recall_eval.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/generate_drill_signoff.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/git_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/github_live_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/gitnexus_to_claims.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/grid_recall_weights.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/index_claims_to_qdrant.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/ingest_planning_docs.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/jira_live_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/label_prompts_with_judge.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/llm_benchmark.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/measure_dedupe_thresholds.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/merge_scope_variants.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/messages_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/operator_metrics.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/precompute_candidates.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/recurring_incident_drill.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/release_readiness.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/run_codex_autologger.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/run_incident_drill.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/scheduled_ingest.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/setup-hooks.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/slack_live_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/sync_hook_templates.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/tickets_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/train_steward_classifier.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/scripts/webhook_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/setup.cfg +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/bench_longmemeval.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_access_control.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_action_exporters.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_action_extractor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_atlas_claim_extractor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_atlas_contract.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_atlas_source_schema.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_auto_extractor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_auto_ingest_hook_citations.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_auto_ingest_hook_schema.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_auto_resolver.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_auto_validate.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_bm25_per_field.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_calibration.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_calibration_priors_applied.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_candidate_dedupe.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_claim_edges.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_claim_links.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_claim_type_ranking.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_classify_hook_f1.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_classify_hook_latency.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_claude_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_cli_dry_run.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_cli_json_flag.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_cli_ready.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_cli_review_queue.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_cli_subcommands.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_closets.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_closets_recall_integration.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_compact_summaries.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_compact_summaries_sensitivity.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_compaction_trace.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_compactor_artifact_order.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_config.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_conflict_resolver.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_confusion_matrix_eval.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_connection_retry.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_connectors.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_context_hook.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_context_optimizer.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_context_optimizer_provider.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_conversation_to_turns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dashboard.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dashboard_auth.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dashboard_coverage.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dashboard_latency.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dashboard_lineage.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dashboard_review_queue.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_daydream_ingest.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_db_merge_confidence_conflict.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_db_merge_coverage_v2.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_decay_coverage.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_decay_respects_pinned.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dedup.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dedup_cli.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dedup_conflict_disambiguation.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_deterministic_predicates.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dream_bridge_coverage_v2.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_dream_bridge_sensitivity.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_embeddings_coverage.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_entity_extractor.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_entity_extractor_llm.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_entity_graph.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_entity_graph_export.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_entity_new_kinds.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_entity_regex_v3.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_entity_registry.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_eval_harness.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_events_schema.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_extract_llm_ollama.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_federated_graphify_mcp.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_federated_query_safety.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_feedback.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_fts5_search.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_graph_distance.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_graph_store.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_handler_regressions.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_hook_env_isolation.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_human_id.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_incident_drill_runner.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_integration_workflows.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_key_rotator.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_lifecycle.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_lifecycle_supersede_invariant.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_llm_budget.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_llm_fallback.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_llm_provider_claude_cli.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_llm_provider_key_rotation.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_llm_steward_coverage.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_llm_steward_key_rotation.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_mcp_filter_bypass.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_mcp_helpers.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_mcp_path_policy.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_mcp_rate_limit.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_mcp_server_validation.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_mcp_usage.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_media_processing.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_meta_decisions.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_metrics_exporter.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_observability.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_obsidian_mind_patterns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_operator.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_operator_queue.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_perf_smoke_config.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_plugins.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_policy_coverage.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_policy_mode_env.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_postgres_parity.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_qdrant_backend.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_qmd_bridge.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_query_classifier.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_query_expansion.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_recall_entity_fanout.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_recall_fusion.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_recall_latency.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_recall_precision_at_5.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_recall_tokenizer.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_recall_vector_fallback.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_reliability_hardening.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_resolvers_concurrent_supersede.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_retrieval_profile.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_retrieval_profiles.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_retrieval_rrf_tiebreaker.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_retrieval_weights.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_review.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_rl_trainer.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_rrf_auto_gate.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_scheduler.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_schema.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_scope_boost.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_scope_utils.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_security_access.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_security_patterns.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_sensitivity_filter_t07.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_service_coverage.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_session_tracker.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_snapshot.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_snapshot_roundtrip.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_sqlite_core.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_staleness.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_stealth_mode.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_steward.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_steward_classifier.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_steward_daydream_hook.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_steward_features.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_steward_features_v3.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_steward_resolution_parity.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_storage_parity.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_store_factory.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_tenant_isolation.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_turn_schema.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_two_pass_recall.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_v311_fixes.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_v313_e2e.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_v390_e2e.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_v391_strict_warnings.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_vault_exporter.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_vault_linter_orphan.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_vector_search.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_verbatim_recall.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_verbatim_store.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_verbatim_store_qdrant.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_webhook.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_webhook_hmac.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_whatsapp_importer.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_wiki_autopromote.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_wiki_binding.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_wiki_engine_idempotency.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_wiki_freshness.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_wiki_similarity_multiscope.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.0}/tests/test_wiki_suggest.py +0 -0
- {memorymaster-3.19.0 → memorymaster-3.21.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.21.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/)
|
|
@@ -87,6 +87,9 @@ recent PR status, and sensitivity-filter invariants.
|
|
|
87
87
|
- **Hybrid retrieval**: vector (sentence-transformers / Gemini) + FTS5 + freshness + confidence
|
|
88
88
|
- **Context optimizer**: `query_for_context(budget=4000)` returns auto-curated memory that fits your token budget
|
|
89
89
|
- **Entity graph** with typed relationships and alias resolution
|
|
90
|
+
- **Rule-shaped claims** (new in v3.21.0): prescriptive `when <trigger>, do <action> because <rationale>` claims (`ingest_rule` / `query_rules`) — the shape an agent needs to actually change behaviour next time, not just recall a fact
|
|
91
|
+
- **Correction mining** (new in v3.21.0): `mine-rules` scans the verbatim transcript archive for user corrections and distills them into rule claims; the Stop hook also mines each session's latest correction automatically
|
|
92
|
+
- **Versioned schema migrations** (new in v3.20.0): `migrate` applies SQLite/Postgres migrations with sha256 drift detection; incremental `export-delta` ships small claim deltas for cheap cross-machine sync
|
|
90
93
|
- **Steward governance**: multi-probe validators (filesystem, format, citation, semantic, tool) with proposal review
|
|
91
94
|
- **Conflict resolution**: 5-tier auto (confidence > freshness > citations > LLM > manual)
|
|
92
95
|
- **Auto-redaction** at ingest: JWT, GitHub tokens, Bearer, AWS keys, SSH keys, custom patterns
|
|
@@ -187,7 +190,7 @@ For zero-cost offline use, install [Ollama](https://ollama.com), `ollama pull ll
|
|
|
187
190
|
}
|
|
188
191
|
```
|
|
189
192
|
|
|
190
|
-
|
|
193
|
+
24 MCP tools: `init_db`, `ingest_claim`, `ingest_rule`, `query_rules`, `run_cycle`, `run_steward`, `classify_query`, `query_memory`, `query_for_context`, `list_claims`, `redact_claim_payload`, `pin_claim`, `compact_memory`, `list_events`, `search_verbatim`, `open_dashboard`, `list_steward_proposals`, `resolve_steward_proposal`, `extract_entities`, `entity_stats`, `find_related_claims`, `quality_scores`, `recompute_tiers`, `federated_query`.
|
|
191
194
|
|
|
192
195
|
See [`.mcp.json.example`](.mcp.json.example) for the full template.
|
|
193
196
|
|
|
@@ -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/)
|
|
@@ -41,6 +41,9 @@ recent PR status, and sensitivity-filter invariants.
|
|
|
41
41
|
- **Hybrid retrieval**: vector (sentence-transformers / Gemini) + FTS5 + freshness + confidence
|
|
42
42
|
- **Context optimizer**: `query_for_context(budget=4000)` returns auto-curated memory that fits your token budget
|
|
43
43
|
- **Entity graph** with typed relationships and alias resolution
|
|
44
|
+
- **Rule-shaped claims** (new in v3.21.0): prescriptive `when <trigger>, do <action> because <rationale>` claims (`ingest_rule` / `query_rules`) — the shape an agent needs to actually change behaviour next time, not just recall a fact
|
|
45
|
+
- **Correction mining** (new in v3.21.0): `mine-rules` scans the verbatim transcript archive for user corrections and distills them into rule claims; the Stop hook also mines each session's latest correction automatically
|
|
46
|
+
- **Versioned schema migrations** (new in v3.20.0): `migrate` applies SQLite/Postgres migrations with sha256 drift detection; incremental `export-delta` ships small claim deltas for cheap cross-machine sync
|
|
44
47
|
- **Steward governance**: multi-probe validators (filesystem, format, citation, semantic, tool) with proposal review
|
|
45
48
|
- **Conflict resolution**: 5-tier auto (confidence > freshness > citations > LLM > manual)
|
|
46
49
|
- **Auto-redaction** at ingest: JWT, GitHub tokens, Bearer, AWS keys, SSH keys, custom patterns
|
|
@@ -141,7 +144,7 @@ For zero-cost offline use, install [Ollama](https://ollama.com), `ollama pull ll
|
|
|
141
144
|
}
|
|
142
145
|
```
|
|
143
146
|
|
|
144
|
-
|
|
147
|
+
24 MCP tools: `init_db`, `ingest_claim`, `ingest_rule`, `query_rules`, `run_cycle`, `run_steward`, `classify_query`, `query_memory`, `query_for_context`, `list_claims`, `redact_claim_payload`, `pin_claim`, `compact_memory`, `list_events`, `search_verbatim`, `open_dashboard`, `list_steward_proposals`, `resolve_steward_proposal`, `extract_entities`, `entity_stats`, `find_related_claims`, `quality_scores`, `recompute_tiers`, `federated_query`.
|
|
145
148
|
|
|
146
149
|
See [`.mcp.json.example`](.mcp.json.example) for the full template.
|
|
147
150
|
|
|
@@ -23,7 +23,9 @@ from memorymaster.cli_handlers_curation import COMMAND_HANDLERS
|
|
|
23
23
|
from memorymaster.cli_handlers_basic import (
|
|
24
24
|
_handle_decay,
|
|
25
25
|
_handle_entity_graph_export,
|
|
26
|
+
_handle_export_delta,
|
|
26
27
|
_handle_ingest_daydream,
|
|
28
|
+
_handle_migrate,
|
|
27
29
|
_handle_recompute_confidence_priors,
|
|
28
30
|
_handle_wiki_suggest_links,
|
|
29
31
|
handle_mcp_usage_report,
|
|
@@ -38,6 +40,8 @@ COMMAND_HANDLERS["ingest-daydream"] = _handle_ingest_daydream
|
|
|
38
40
|
COMMAND_HANDLERS["mcp-usage-report"] = (
|
|
39
41
|
lambda args, service, parser, effective_db: handle_mcp_usage_report(args, effective_db)
|
|
40
42
|
)
|
|
43
|
+
COMMAND_HANDLERS["migrate"] = _handle_migrate
|
|
44
|
+
COMMAND_HANDLERS["export-delta"] = _handle_export_delta
|
|
41
45
|
|
|
42
46
|
|
|
43
47
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -51,6 +55,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
51
55
|
|
|
52
56
|
sub.add_parser("init-db", help="Create schema in SQLite database")
|
|
53
57
|
|
|
58
|
+
migrate = sub.add_parser("migrate", help="Apply pending versioned schema migrations (v3.20.0+)")
|
|
59
|
+
migrate_mode = migrate.add_mutually_exclusive_group()
|
|
60
|
+
migrate_mode.add_argument("--list", action="store_true", help="List known migrations without touching the DB")
|
|
61
|
+
migrate_mode.add_argument("--status", action="store_true", help="Report applied vs pending per migration")
|
|
62
|
+
|
|
54
63
|
sub.add_parser("stealth-status", help="Show whether stealth mode is active and which DB is in use")
|
|
55
64
|
|
|
56
65
|
ingest = sub.add_parser("ingest", help="Ingest a raw claim with citations")
|
|
@@ -447,6 +456,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
447
456
|
mine_cmd.add_argument("--scope", default="project", help="Scope for ingested claims")
|
|
448
457
|
mine_cmd.add_argument("--max", type=int, default=100, help="Max claims to ingest")
|
|
449
458
|
|
|
459
|
+
mine_rules_cmd = sub.add_parser("mine-rules", help="Mine verbatim corrections into rule-shaped claims (v3.21.0-R1b)")
|
|
460
|
+
mine_rules_cmd.add_argument("--since-id", dest="since_id", type=int, default=None, help="Override the stored watermark; start scanning after this verbatim id")
|
|
461
|
+
mine_rules_cmd.add_argument("--limit", type=int, default=None, help="Max candidate windows to examine this run (caps LLM calls)")
|
|
462
|
+
mine_rules_cmd.add_argument("--batch-size", dest="batch_size", type=int, default=200, help="Rows fetched per SQL pre-filter page (default: 200)")
|
|
463
|
+
mine_rules_cmd.add_argument("--provider", default="claude_cli", help="LLM provider for this run (default: claude_cli)")
|
|
464
|
+
mine_rules_cmd.add_argument("--reset", action="store_true", help="Clear the stored watermark before running (re-scan from the start)")
|
|
465
|
+
|
|
450
466
|
verify_cmd = sub.add_parser("verify-claims", help="Cross-check claims against current codebase")
|
|
451
467
|
verify_cmd.add_argument("--scope", default="", help="Scope filter")
|
|
452
468
|
verify_cmd.add_argument("--limit", type=int, default=200, help="Max claims to check")
|
|
@@ -504,6 +520,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
504
520
|
merge_cmd = sub.add_parser("merge-db", help="Merge claims from a remote memorymaster DB (bidirectional sync)")
|
|
505
521
|
merge_cmd.add_argument("--source", required=True, help="Path to source DB file to merge from")
|
|
506
522
|
|
|
523
|
+
delta_cmd = sub.add_parser(
|
|
524
|
+
"export-delta",
|
|
525
|
+
help="Export claims changed since a watermark into a small SQLite delta file (incremental sync)",
|
|
526
|
+
)
|
|
527
|
+
delta_cmd.add_argument("--since", default="", help="ISO-8601 watermark; export claims with updated_at after this (empty = full export)")
|
|
528
|
+
delta_cmd.add_argument("--output", required=True, help="Path to write the delta SQLite file (overwritten if it exists)")
|
|
529
|
+
|
|
507
530
|
daily = sub.add_parser("daily-note", help="Generate a daily note summarizing today's activity")
|
|
508
531
|
daily.add_argument("--date", default="", help="Date to generate for (YYYY-MM-DD, default: today)")
|
|
509
532
|
daily.add_argument("--output", default="", help="Directory to save .md file (default: print to stdout)")
|
|
@@ -556,7 +579,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
556
579
|
effective_db = _resolve_db_path(args)
|
|
557
580
|
|
|
558
581
|
# Commands that don't need MemoryService run first; service is lazy-created once for all others.
|
|
559
|
-
_NO_SERVICE_COMMANDS = {"stealth-status", "export-metrics", "wiki-freshness", "mcp-usage-report"}
|
|
582
|
+
_NO_SERVICE_COMMANDS = {"stealth-status", "export-metrics", "wiki-freshness", "mcp-usage-report", "export-delta"}
|
|
560
583
|
|
|
561
584
|
try:
|
|
562
585
|
handler = COMMAND_HANDLERS.get(args.command)
|
|
@@ -1357,3 +1357,95 @@ def _handle_check_staleness(args: argparse.Namespace, service, parser: argparse.
|
|
|
1357
1357
|
return 0
|
|
1358
1358
|
|
|
1359
1359
|
|
|
1360
|
+
def _handle_migrate(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
|
|
1361
|
+
"""v3.20.0-S1: apply pending schema migrations, or report status.
|
|
1362
|
+
|
|
1363
|
+
Default (no flags): apply every pending migration in version order.
|
|
1364
|
+
--list: dump known migrations (version + description) without touching the DB.
|
|
1365
|
+
--status: query the DB and show applied vs pending per migration.
|
|
1366
|
+
"""
|
|
1367
|
+
from memorymaster.migrations import (
|
|
1368
|
+
MigrationRunner,
|
|
1369
|
+
discover_migrations,
|
|
1370
|
+
)
|
|
1371
|
+
from memorymaster.store_factory import is_postgres_dsn
|
|
1372
|
+
|
|
1373
|
+
# --list works without a DB connection at all.
|
|
1374
|
+
if getattr(args, "list", False):
|
|
1375
|
+
migrations = discover_migrations()
|
|
1376
|
+
if args.json_output:
|
|
1377
|
+
payload = [{"version": m.version, "description": m.description} for m in migrations]
|
|
1378
|
+
print(_json_envelope(payload))
|
|
1379
|
+
else:
|
|
1380
|
+
print(f"known migrations ({len(migrations)}):")
|
|
1381
|
+
for m in migrations:
|
|
1382
|
+
print(f" v{m.version:04d} {m.description}")
|
|
1383
|
+
return 0
|
|
1384
|
+
|
|
1385
|
+
backend = "postgres" if is_postgres_dsn(effective_db) else "sqlite"
|
|
1386
|
+
store = service.store
|
|
1387
|
+
with store.connect() as conn:
|
|
1388
|
+
runner = MigrationRunner(conn, backend=backend)
|
|
1389
|
+
|
|
1390
|
+
if getattr(args, "status", False):
|
|
1391
|
+
entries = runner.status()
|
|
1392
|
+
if args.json_output:
|
|
1393
|
+
payload = [
|
|
1394
|
+
{
|
|
1395
|
+
"version": e.version,
|
|
1396
|
+
"description": e.description,
|
|
1397
|
+
"applied": e.applied,
|
|
1398
|
+
"applied_at": e.applied_at,
|
|
1399
|
+
}
|
|
1400
|
+
for e in entries
|
|
1401
|
+
]
|
|
1402
|
+
print(_json_envelope(payload))
|
|
1403
|
+
else:
|
|
1404
|
+
print(f"backend={backend} db={effective_db}")
|
|
1405
|
+
for e in entries:
|
|
1406
|
+
marker = "[applied]" if e.applied else "[pending]"
|
|
1407
|
+
when = f" applied_at={e.applied_at}" if e.applied_at else ""
|
|
1408
|
+
print(f" v{e.version:04d} {marker} {e.description}{when}")
|
|
1409
|
+
return 0
|
|
1410
|
+
|
|
1411
|
+
# Default: apply pending
|
|
1412
|
+
newly = runner.apply_pending()
|
|
1413
|
+
if args.json_output:
|
|
1414
|
+
print(_json_envelope({"applied": newly, "backend": backend}))
|
|
1415
|
+
else:
|
|
1416
|
+
if not newly:
|
|
1417
|
+
print(f"migrate: nothing to apply (backend={backend}, db={effective_db})")
|
|
1418
|
+
else:
|
|
1419
|
+
print(f"migrate: applied {len(newly)} migration(s) on backend={backend}:")
|
|
1420
|
+
for v in newly:
|
|
1421
|
+
print(f" v{v:04d}")
|
|
1422
|
+
return 0
|
|
1423
|
+
|
|
1424
|
+
|
|
1425
|
+
def _handle_export_delta(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
|
|
1426
|
+
"""Export claims changed since a watermark into a small SQLite delta file.
|
|
1427
|
+
|
|
1428
|
+
The delta file is a valid `merge-db --source` input. Prints (or JSON-emits)
|
|
1429
|
+
the export counts and the new watermark — callers should record
|
|
1430
|
+
`max_updated_at` and pass it as `--since` on the next run.
|
|
1431
|
+
"""
|
|
1432
|
+
from memorymaster.delta_sync import export_delta
|
|
1433
|
+
|
|
1434
|
+
t0 = time.perf_counter()
|
|
1435
|
+
result = export_delta(effective_db, args.since, args.output)
|
|
1436
|
+
elapsed_ms = (time.perf_counter() - t0) * 1000
|
|
1437
|
+
if args.json_output:
|
|
1438
|
+
print(_json_envelope(result, query_ms=elapsed_ms))
|
|
1439
|
+
else:
|
|
1440
|
+
since_label = result["since"] or "(full export)"
|
|
1441
|
+
print(
|
|
1442
|
+
f"export-delta: {result['exported']} claims + {result['citations']} citations "
|
|
1443
|
+
f"since {since_label} -> {args.output}"
|
|
1444
|
+
)
|
|
1445
|
+
if result["max_updated_at"]:
|
|
1446
|
+
print(f" next watermark (--since): {result['max_updated_at']}")
|
|
1447
|
+
else:
|
|
1448
|
+
print(" delta is empty — nothing changed since the watermark")
|
|
1449
|
+
return 0
|
|
1450
|
+
|
|
1451
|
+
|
|
@@ -343,6 +343,31 @@ def _handle_mine_transcript(args: argparse.Namespace, service, parser: argparse.
|
|
|
343
343
|
return 0
|
|
344
344
|
|
|
345
345
|
|
|
346
|
+
def _handle_mine_rules(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
|
|
347
|
+
from memorymaster.rule_miner import mine_rules
|
|
348
|
+
t0 = time.perf_counter()
|
|
349
|
+
result = mine_rules(
|
|
350
|
+
effective_db,
|
|
351
|
+
service,
|
|
352
|
+
since_id=getattr(args, "since_id", None),
|
|
353
|
+
limit=getattr(args, "limit", None),
|
|
354
|
+
batch_size=getattr(args, "batch_size", 200),
|
|
355
|
+
provider=getattr(args, "provider", "claude_cli"),
|
|
356
|
+
reset=getattr(args, "reset", False),
|
|
357
|
+
)
|
|
358
|
+
elapsed_ms = (time.perf_counter() - t0) * 1000
|
|
359
|
+
if args.json_output:
|
|
360
|
+
print(_json_envelope(result, query_ms=elapsed_ms))
|
|
361
|
+
else:
|
|
362
|
+
abort = f", ABORTED ({result['aborted_reason']})" if result.get("aborted_reason") else ""
|
|
363
|
+
print(
|
|
364
|
+
f"Mined rules: {result['candidates']} candidates, {result['llm_calls']} llm calls, "
|
|
365
|
+
f"{result['ingested']} ingested, {result['duplicates']} dupes, {result['skipped']} skipped "
|
|
366
|
+
f"(watermark={result['last_id']}{abort}, {elapsed_ms:.0f}ms)"
|
|
367
|
+
)
|
|
368
|
+
return 0
|
|
369
|
+
|
|
370
|
+
|
|
346
371
|
def _handle_wiki_breakdown(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
|
|
347
372
|
from memorymaster.wiki_engine import breakdown
|
|
348
373
|
t0 = time.perf_counter()
|
|
@@ -703,6 +728,7 @@ COMMAND_HANDLERS: dict[str, object] = {
|
|
|
703
728
|
"wiki-freshness": _handle_wiki_freshness,
|
|
704
729
|
"bases-generate": _handle_bases_generate,
|
|
705
730
|
"mine-transcript": _handle_mine_transcript,
|
|
731
|
+
"mine-rules": _handle_mine_rules,
|
|
706
732
|
"verify-claims": _handle_verify_claims,
|
|
707
733
|
"extract-entities": _handle_extract_entities,
|
|
708
734
|
"entity-stats": _handle_entity_stats,
|
|
@@ -180,6 +180,27 @@ Only: bug root causes, decisions, gotchas, constraints. Never: credentials, IPs,
|
|
|
180
180
|
pass
|
|
181
181
|
|
|
182
182
|
|
|
183
|
+
def _run_rule_extraction(transcript_path, cwd):
|
|
184
|
+
"""R1b ongoing: mine the latest correction in this session into a rule claim.
|
|
185
|
+
|
|
186
|
+
Reuses memorymaster.rule_miner.mine_transcript_rules (single source of truth
|
|
187
|
+
for the correction->rule prompt + ingest path). Bounded to one window per
|
|
188
|
+
stop to keep the hook fast; rules land as low-confidence candidates."""
|
|
189
|
+
try:
|
|
190
|
+
if not transcript_path or not os.path.exists(transcript_path) or not os.path.exists(DB_PATH):
|
|
191
|
+
return
|
|
192
|
+
from memorymaster.rule_miner import mine_transcript_rules
|
|
193
|
+
from memorymaster.service import MemoryService
|
|
194
|
+
|
|
195
|
+
scope = "project:" + os.path.basename(cwd).lower().replace(" ", "-") if cwd else "global"
|
|
196
|
+
svc = MemoryService(DB_PATH, workspace_root=Path(cwd or PROJECT_ROOT))
|
|
197
|
+
stats = mine_transcript_rules(transcript_path, svc, scope=scope, max_windows=1)
|
|
198
|
+
if stats.get("ingested"):
|
|
199
|
+
sys.stderr.write(f"[MemoryMaster] mined {stats['ingested']} rule(s) from corrections\n")
|
|
200
|
+
except Exception:
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
|
|
183
204
|
def main():
|
|
184
205
|
try:
|
|
185
206
|
data = json.loads(sys.stdin.read() or "{}")
|
|
@@ -232,6 +253,9 @@ def main():
|
|
|
232
253
|
# Not time to block — run passive Gemini extraction
|
|
233
254
|
_run_gemini_extraction(transcript_path, cwd)
|
|
234
255
|
|
|
256
|
+
# R1b: mine the latest correction in this session into a rule claim
|
|
257
|
+
_run_rule_extraction(transcript_path, cwd)
|
|
258
|
+
|
|
235
259
|
sys.stdout.write(json.dumps({"decision": "approve"}))
|
|
236
260
|
|
|
237
261
|
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""Delta export for incremental memory sync.
|
|
2
|
+
|
|
3
|
+
Pre-existing sync (``scripts/openclaw-sync.sh``) copied the WHOLE database
|
|
4
|
+
file across the network twice per cycle — fine when the DB was small,
|
|
5
|
+
wasteful at 2.5 GB. The ``merge-db`` step was always append-only (it dedups
|
|
6
|
+
on ``idempotency_key`` + text-hash), but the *transport* moved everything.
|
|
7
|
+
|
|
8
|
+
``export_delta`` closes that gap: it writes a small SQLite file containing
|
|
9
|
+
ONLY the claims (and their citations) changed since a watermark timestamp.
|
|
10
|
+
That small file is a valid merge source — ``merge-db --source delta.db``
|
|
11
|
+
consumes it unchanged, because the merge engine only reads ``claims`` and
|
|
12
|
+
``citations`` and ignores everything else.
|
|
13
|
+
|
|
14
|
+
Sync loop with deltas:
|
|
15
|
+
|
|
16
|
+
1. each side: ``export-delta --since <last_sync> --output delta.db``
|
|
17
|
+
2. ship the small delta.db over the network (KB, not GB)
|
|
18
|
+
3. each side: ``merge-db --source <other-side-delta.db>``
|
|
19
|
+
4. record the new watermark (``max_updated_at`` from the export result)
|
|
20
|
+
|
|
21
|
+
The whole-DB file never crosses the network — which also removes the
|
|
22
|
+
SQLite-over-network-mount corruption risk entirely.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import logging
|
|
27
|
+
import sqlite3
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Tables a merge source must carry. The merge engine (db_merge.py) reads
|
|
34
|
+
# `claims` (SELECT *) and `citations` (source/locator/excerpt/created_at).
|
|
35
|
+
# Nothing else is needed — FTS5, events, entity tables are all rebuilt or
|
|
36
|
+
# ignored on the merge side.
|
|
37
|
+
_DELTA_TABLES = ("claims", "citations")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _copy_table_ddl(src: sqlite3.Connection, out: sqlite3.Connection, table: str) -> None:
|
|
41
|
+
"""Copy a table's CREATE statement verbatim from src into out."""
|
|
42
|
+
row = src.execute(
|
|
43
|
+
"SELECT sql FROM sqlite_master WHERE type='table' AND name=?",
|
|
44
|
+
(table,),
|
|
45
|
+
).fetchone()
|
|
46
|
+
if row is None or not row[0]:
|
|
47
|
+
raise ValueError(f"source DB has no '{table}' table — not a memorymaster DB?")
|
|
48
|
+
out.execute(row[0])
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def export_delta(
|
|
52
|
+
source_db: str | Path,
|
|
53
|
+
since: str,
|
|
54
|
+
output_path: str | Path,
|
|
55
|
+
) -> dict[str, object]:
|
|
56
|
+
"""Write claims changed since ``since`` into a small SQLite delta file.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
source_db: path to the full memorymaster DB to export from.
|
|
60
|
+
since: ISO-8601 timestamp watermark. Claims with ``updated_at`` strictly
|
|
61
|
+
greater than this are exported. Pass an empty string or a very
|
|
62
|
+
early date to export everything (full bootstrap).
|
|
63
|
+
output_path: where to write the delta SQLite file. Overwritten if it
|
|
64
|
+
exists.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
dict with ``exported`` (claim count), ``citations`` (citation count),
|
|
68
|
+
``since`` (echoed watermark), and ``max_updated_at`` (the newest
|
|
69
|
+
``updated_at`` seen — use this as the next watermark; None when the
|
|
70
|
+
delta is empty).
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
FileNotFoundError: source DB missing.
|
|
74
|
+
ValueError: source DB lacks the expected tables.
|
|
75
|
+
"""
|
|
76
|
+
source_db = str(source_db)
|
|
77
|
+
output_path = Path(output_path)
|
|
78
|
+
if not Path(source_db).exists():
|
|
79
|
+
raise FileNotFoundError(f"Source DB not found: {source_db}")
|
|
80
|
+
|
|
81
|
+
# Fresh output file every time — a stale delta would merge old rows again
|
|
82
|
+
# (harmless thanks to idempotent merge, but wasteful).
|
|
83
|
+
if output_path.exists():
|
|
84
|
+
output_path.unlink()
|
|
85
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
|
|
87
|
+
src = sqlite3.connect(source_db)
|
|
88
|
+
src.row_factory = sqlite3.Row
|
|
89
|
+
out = sqlite3.connect(str(output_path))
|
|
90
|
+
try:
|
|
91
|
+
for table in _DELTA_TABLES:
|
|
92
|
+
_copy_table_ddl(src, out, table)
|
|
93
|
+
|
|
94
|
+
# `since` empty => full export. SQLite string comparison on ISO-8601
|
|
95
|
+
# timestamps is chronological, so it works as a watermark.
|
|
96
|
+
#
|
|
97
|
+
# We use `>=`, not `>`. Strictly-after would SKIP any claim whose
|
|
98
|
+
# updated_at exactly equals the watermark — and multiple claims can
|
|
99
|
+
# share a timestamp (same-second ingest). Skipping a claim is silent
|
|
100
|
+
# data loss. `>=` instead re-exports the boundary claim(s); the merge
|
|
101
|
+
# engine is idempotent (dedups on idempotency_key + text-hash), so a
|
|
102
|
+
# re-export costs nothing but a few rows. Safe beats clean.
|
|
103
|
+
watermark = since.strip()
|
|
104
|
+
if watermark:
|
|
105
|
+
claim_rows = src.execute(
|
|
106
|
+
"SELECT * FROM claims WHERE updated_at >= ? ORDER BY updated_at",
|
|
107
|
+
(watermark,),
|
|
108
|
+
).fetchall()
|
|
109
|
+
else:
|
|
110
|
+
claim_rows = src.execute(
|
|
111
|
+
"SELECT * FROM claims ORDER BY updated_at"
|
|
112
|
+
).fetchall()
|
|
113
|
+
|
|
114
|
+
if not claim_rows:
|
|
115
|
+
out.commit()
|
|
116
|
+
return {
|
|
117
|
+
"exported": 0,
|
|
118
|
+
"citations": 0,
|
|
119
|
+
"since": watermark,
|
|
120
|
+
"max_updated_at": None,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
claim_cols = [c[1] for c in src.execute("PRAGMA table_info(claims)").fetchall()]
|
|
124
|
+
placeholders = ",".join("?" for _ in claim_cols)
|
|
125
|
+
col_list = ",".join(claim_cols)
|
|
126
|
+
insert_claim = f"INSERT INTO claims ({col_list}) VALUES ({placeholders})"
|
|
127
|
+
|
|
128
|
+
exported_ids: list[int] = []
|
|
129
|
+
max_updated = ""
|
|
130
|
+
for row in claim_rows:
|
|
131
|
+
out.execute(insert_claim, tuple(row[c] for c in claim_cols))
|
|
132
|
+
exported_ids.append(int(row["id"]))
|
|
133
|
+
updated = str(row["updated_at"] or "")
|
|
134
|
+
if updated > max_updated:
|
|
135
|
+
max_updated = updated
|
|
136
|
+
|
|
137
|
+
# Citations for exactly the exported claims. claim_id linkage is
|
|
138
|
+
# preserved because we keep original claim ids in the delta file.
|
|
139
|
+
cit_cols = [c[1] for c in src.execute("PRAGMA table_info(citations)").fetchall()]
|
|
140
|
+
cit_placeholders = ",".join("?" for _ in cit_cols)
|
|
141
|
+
cit_col_list = ",".join(cit_cols)
|
|
142
|
+
insert_cit = f"INSERT INTO citations ({cit_col_list}) VALUES ({cit_placeholders})"
|
|
143
|
+
|
|
144
|
+
citation_count = 0
|
|
145
|
+
# Chunk the IN clause — SQLite caps host parameters at 999.
|
|
146
|
+
for start in range(0, len(exported_ids), 900):
|
|
147
|
+
batch = exported_ids[start : start + 900]
|
|
148
|
+
qmarks = ",".join("?" for _ in batch)
|
|
149
|
+
cit_rows = src.execute(
|
|
150
|
+
f"SELECT * FROM citations WHERE claim_id IN ({qmarks})",
|
|
151
|
+
batch,
|
|
152
|
+
).fetchall()
|
|
153
|
+
for cit in cit_rows:
|
|
154
|
+
out.execute(insert_cit, tuple(cit[c] for c in cit_cols))
|
|
155
|
+
citation_count += 1
|
|
156
|
+
|
|
157
|
+
out.commit()
|
|
158
|
+
logger.info(
|
|
159
|
+
"export_delta: %d claims, %d citations since %r",
|
|
160
|
+
len(exported_ids),
|
|
161
|
+
citation_count,
|
|
162
|
+
watermark or "(full)",
|
|
163
|
+
)
|
|
164
|
+
return {
|
|
165
|
+
"exported": len(exported_ids),
|
|
166
|
+
"citations": citation_count,
|
|
167
|
+
"since": watermark,
|
|
168
|
+
"max_updated_at": max_updated or None,
|
|
169
|
+
}
|
|
170
|
+
finally:
|
|
171
|
+
src.close()
|
|
172
|
+
out.close()
|
|
@@ -1113,6 +1113,57 @@ if FastMCP is not None:
|
|
|
1113
1113
|
)
|
|
1114
1114
|
return {"ok": True, "rows": len(claims), "claims": [_claim_to_dict(c) for c in claims]}
|
|
1115
1115
|
|
|
1116
|
+
@mcp.tool()
|
|
1117
|
+
def ingest_rule(
|
|
1118
|
+
trigger: str,
|
|
1119
|
+
action: str,
|
|
1120
|
+
rationale: str = "",
|
|
1121
|
+
db: str = "memorymaster.db",
|
|
1122
|
+
workspace: str = ".",
|
|
1123
|
+
scope: str = "project",
|
|
1124
|
+
source_agent: str = "mcp",
|
|
1125
|
+
) -> dict[str, Any]:
|
|
1126
|
+
"""Ingest a prescriptive rule-shaped claim (v3.21.0-R1).
|
|
1127
|
+
|
|
1128
|
+
A rule captures "when <trigger>, do <action> because <rationale>" —
|
|
1129
|
+
the behavioural shape, distinct from descriptive fact claims. Stored
|
|
1130
|
+
as a claim_type='rule' claim; retrieve via query_rules.
|
|
1131
|
+
"""
|
|
1132
|
+
from memorymaster.rules import build_rule_fields
|
|
1133
|
+
|
|
1134
|
+
svc = _service(db, workspace)
|
|
1135
|
+
fields = build_rule_fields(trigger, action, rationale)
|
|
1136
|
+
# Auto-citation: a rule's provenance is the session that taught it.
|
|
1137
|
+
claim = svc.ingest(
|
|
1138
|
+
**fields,
|
|
1139
|
+
citations=[CitationInput(source=f"agent://{source_agent}", locator="rule", excerpt=action[:200])],
|
|
1140
|
+
scope=scope,
|
|
1141
|
+
source_agent=source_agent,
|
|
1142
|
+
)
|
|
1143
|
+
return {"ok": True, "claim_id": claim.id, "human_id": claim.human_id, "rule": fields["text"]}
|
|
1144
|
+
|
|
1145
|
+
@mcp.tool()
|
|
1146
|
+
def query_rules(
|
|
1147
|
+
query: str,
|
|
1148
|
+
db: str = "memorymaster.db",
|
|
1149
|
+
workspace: str = ".",
|
|
1150
|
+
limit: int = 10,
|
|
1151
|
+
allow_sensitive: bool = False,
|
|
1152
|
+
) -> dict[str, Any]:
|
|
1153
|
+
"""Retrieve rule-shaped claims matching a query, in prescriptive form.
|
|
1154
|
+
|
|
1155
|
+
Returns each rule's trigger / action / rationale / text, ranked by the
|
|
1156
|
+
hybrid retriever. Use this when you want only behavioural rules, not
|
|
1157
|
+
descriptive fact claims.
|
|
1158
|
+
"""
|
|
1159
|
+
resolve_allow_sensitive_access(
|
|
1160
|
+
allow_sensitive=allow_sensitive,
|
|
1161
|
+
context="mcp.query_rules",
|
|
1162
|
+
)
|
|
1163
|
+
svc = _service(db, workspace)
|
|
1164
|
+
rules = svc.query_rules(query, limit=limit, allow_sensitive=allow_sensitive)
|
|
1165
|
+
return {"ok": True, "rows": len(rules), "rules": rules}
|
|
1166
|
+
|
|
1116
1167
|
@mcp.tool()
|
|
1117
1168
|
def redact_claim_payload(
|
|
1118
1169
|
claim_id: int,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""0001_initial — baseline marker for the v3.20.0 migration framework.
|
|
2
|
+
|
|
3
|
+
This migration is intentionally a no-op. As of v3.20.0-S1 the pre-existing
|
|
4
|
+
schema (from ``schema.sql`` / ``schema_postgres.sql`` and the inline
|
|
5
|
+
``_ensure_*_schema`` helpers in storage layers) is treated as the baseline
|
|
6
|
+
v0001. The runner stamps this version into ``schema_versions`` on first
|
|
7
|
+
run so subsequent NEW migrations (v0002+) apply cleanly on top.
|
|
8
|
+
|
|
9
|
+
Future migrations starting at v0002 will contain real ``ALTER TABLE`` /
|
|
10
|
+
``CREATE INDEX`` / data-backfill DDL.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
VERSION = 1
|
|
15
|
+
DESCRIPTION = "baseline (existing schema as of v3.20.0)"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def apply_sqlite(conn) -> None: # noqa: ARG001 — intentional no-op baseline
|
|
19
|
+
"""No-op: existing schema is the baseline."""
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def apply_postgres(conn) -> None: # noqa: ARG001 — intentional no-op baseline
|
|
24
|
+
"""No-op: existing schema is the baseline."""
|
|
25
|
+
return None
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""0002_miner_state — KV table for resumable miner watermarks (v3.21.0-R1b).
|
|
2
|
+
|
|
3
|
+
The verbatim rule-miner (:mod:`memorymaster.rule_miner`) needs to remember
|
|
4
|
+
how far through ``verbatim_memories`` it has scanned so re-runs are
|
|
5
|
+
incremental. The watermark is a single integer, not a per-row fact, so it
|
|
6
|
+
lives in a small key-value table rather than a column on the 744k-row
|
|
7
|
+
verbatim table (a column would force a full backfill and contend with the
|
|
8
|
+
Stop hook's inserts under WAL).
|
|
9
|
+
|
|
10
|
+
A KV table also generalizes: future miners reuse it with different keys.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
VERSION = 2
|
|
15
|
+
DESCRIPTION = "miner_state KV table for resumable verbatim mining watermarks"
|
|
16
|
+
|
|
17
|
+
_DDL = """
|
|
18
|
+
CREATE TABLE IF NOT EXISTS miner_state (
|
|
19
|
+
key TEXT PRIMARY KEY,
|
|
20
|
+
value TEXT,
|
|
21
|
+
updated_at TEXT
|
|
22
|
+
)
|
|
23
|
+
""".strip()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def apply_sqlite(conn) -> None:
|
|
27
|
+
conn.execute(_DDL)
|
|
28
|
+
commit = getattr(conn, "commit", None)
|
|
29
|
+
if callable(commit):
|
|
30
|
+
commit()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def apply_postgres(conn) -> None:
|
|
34
|
+
cur = conn.cursor()
|
|
35
|
+
cur.execute(_DDL)
|
|
36
|
+
commit = getattr(conn, "commit", None)
|
|
37
|
+
if callable(commit):
|
|
38
|
+
commit()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Versioned schema migrations for MemoryMaster (v3.20.0-S1).
|
|
2
|
+
|
|
3
|
+
Pre-v3.20 schema evolved via opportunistic ``ALTER TABLE`` with try/except
|
|
4
|
+
sprinkled across storage modules — no version tracking, no rollback, silent
|
|
5
|
+
drift risk between SQLite and Postgres. This package replaces that pattern
|
|
6
|
+
with explicit versioned migrations.
|
|
7
|
+
|
|
8
|
+
Each migration is a Python module under this package named
|
|
9
|
+
``NNNN_short_description.py`` with four module-level attributes:
|
|
10
|
+
|
|
11
|
+
VERSION: int # 1, 2, 3, ...
|
|
12
|
+
DESCRIPTION: str # one-line human summary
|
|
13
|
+
def apply_sqlite(conn) -> None: ... # SQLite-backend DDL
|
|
14
|
+
def apply_postgres(conn) -> None: ... # Postgres-backend DDL
|
|
15
|
+
|
|
16
|
+
The ``MigrationRunner`` discovers all migration modules, sorts by VERSION,
|
|
17
|
+
and applies only the ones not yet recorded in the ``schema_versions``
|
|
18
|
+
table. Each applied migration's file source is sha256-checksummed and
|
|
19
|
+
stored alongside its version; on subsequent runs, a mismatch raises
|
|
20
|
+
``MigrationDriftError`` — migrations are immutable once applied.
|
|
21
|
+
|
|
22
|
+
Public API:
|
|
23
|
+
|
|
24
|
+
from memorymaster.migrations import MigrationRunner, MigrationDriftError
|
|
25
|
+
runner = MigrationRunner(conn, backend="sqlite")
|
|
26
|
+
runner.apply_pending()
|
|
27
|
+
runner.status() # -> list of (version, description, applied_at, status)
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from memorymaster.migrations.runner import (
|
|
32
|
+
MigrationDriftError,
|
|
33
|
+
MigrationRunner,
|
|
34
|
+
discover_migrations,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"MigrationDriftError",
|
|
39
|
+
"MigrationRunner",
|
|
40
|
+
"discover_migrations",
|
|
41
|
+
]
|