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