loop-memory 0.4.20__tar.gz → 0.4.21__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.
- {loop_memory-0.4.20 → loop_memory-0.4.21}/PKG-INFO +73 -79
- {loop_memory-0.4.20 → loop_memory-0.4.21}/README.md +72 -78
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/serve.py +19 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/doctor.py +75 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/sdk.py +67 -5
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/sdk_extensions.py +55 -13
- loop_memory-0.4.21/loop_memory/security/__init__.py +39 -0
- loop_memory-0.4.21/loop_memory/security/transport.py +339 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory.egg-info/PKG-INFO +73 -79
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory.egg-info/SOURCES.txt +2 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/pyproject.toml +1 -1
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_agent_memory_sdk.py +5 -1
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_doctor_2026_10_12.py +8 -7
- loop_memory-0.4.21/tests/test_transport_security_2026_11_08.py +456 -0
- loop_memory-0.4.20/loop_memory/security/__init__.py +0 -20
- {loop_memory-0.4.20 → loop_memory-0.4.21}/LICENSE +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/backends/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/backends/embedding.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/backends/sentence_embedder.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/backends/vector_store.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/bench/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/bench/fixtures/default.json +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/bench/runner.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/_common.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/bench.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/cognitive.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/diag.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/doctor.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/graph.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/hooks.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/read.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/rules.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/commands/write.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/cli/main.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/decorators.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/engine/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/engine/loop.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/engine/reflect.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/examples/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/examples/demo.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/export/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/export/memory_md.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/graph/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/graph/build.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/graph/extract.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/ingest/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/ingest/loader.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/ingest/pipeline.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/cognitive.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/compact.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/consolidate.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/contradiction.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/dream.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/evolution.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/graph.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/llm_compact_pass.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/llm_consolidate.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/jobs/scheduler.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/llm/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/llm/base.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/llm/openai_adapter.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/llm/providers.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/mcp/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/memory/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/memory/types.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/privacy/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/privacy/private.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/privacy/redact.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/py.typed +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/security/secrets.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/app.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/handlers.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/_shared.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/admin.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/cognitive.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/export.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/graph.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/insights.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/memories.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/sessions.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/system.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/routes/wiki.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/css/app.css +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/css/layout.css +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/i18n/en.json +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/i18n/zh.json +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/img/logo-light.svg +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/img/logo-mark.svg +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/img/logo.svg +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/index.html +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/App.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/api.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/Dashboard.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/Diagnostic.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/IngestPopover.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/KnowledgeGraph.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/RingMeter.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/RunStrip.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/SectionTitle.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/Settings.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/Sidebar.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/Tabs.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/Timeline.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/Toast.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/TopBar.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/Wiki.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/components/WikiEditor.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/composables/useI18n.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/lib/sanitize.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/lib/vue.esm-browser.prod.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/main.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/static/js/store.js +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/serve/watcher.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/storage/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/storage/lexical.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/storage/retrieval.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/storage/snapshot.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/storage/sqlite_store.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/wiki/__init__.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/wiki/backfill.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/wiki/classifier.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/wiki/prompts.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory/wiki/scope.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory.egg-info/dependency_links.txt +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory.egg-info/entry_points.txt +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory.egg-info/requires.txt +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/loop_memory.egg-info/top_level.txt +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/setup.cfg +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_admin_ingest_route.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_agent_memory_api.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_agents_init.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_audit_triggers_2026_09_27.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_auth_token_rotate.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_bench_2026_11_01.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_busy_timeout_2026_09_24.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cache_cap_2026_09_23.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_audit_supersede.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_export_okf.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_init.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_memory_stats.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_recall_as_of.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_recall_paths.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_recall_verbose.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_rules.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_snapshot.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_v7.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_cli_version.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_content_hash_dedup_2026_10_18.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_contradictions.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_decorators_2026_10_18.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_dream_2026_10_25.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_dream_cli_http_2026_10_25.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_evolution.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_evolution_quality.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_expanduser.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_export_ask.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_export_okf.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_graph.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_healthz_readyz_2026_09_24.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_import_okf_2026_09_27.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_ingest.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_latest_only_2026_10_25.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_lexical_ranking_2026_11_01.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_llm_consolidator.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_llm_fingerprint_not_persisted.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_llm_providers.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_llm_test_endpoint.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_loop.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_mcp.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_memories_pagination.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_memory_stats.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_openclaw_loader.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_package_version.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_packaging_2026_11_01.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_path_safety_2026_09_23.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_recall_as_of.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_recall_budget_2026_10_05.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_recall_paths.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_recall_provenance.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_recall_validity_http_2026_10_18.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_reflection.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_repo_framing.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_retrieval_channels_2026_10_18.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_routes_audit_2026_09_13.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_routes_audit_2026_09_20.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_score_api.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_scoring_v2.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_secrets.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_serve_app.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_serve_handlers.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_session_order.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_short_query.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_snapshot.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_store.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_subgraph_hops_2026_09_23.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_subject_key_2026_10_25.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_summarization.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_supersession_chain.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_universal_memory.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_vector_store.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_watcher.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_wiki_classifier.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_wiki_export_escape.py +0 -0
- {loop_memory-0.4.20 → loop_memory-0.4.21}/tests/test_wiki_prompts.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loop-memory
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.21
|
|
4
4
|
Summary: A general-purpose, local memory system for every AI agent you run. Loop Memory auto-captures conversations from Codex / Claude / Hermes / OpenClaw, scores them by importance × recency × usage × feedback, distils them into a curated wiki, and serves everything from a single web UI.
|
|
5
5
|
Author: Loop Memory contributors <loop-memory@users.noreply.github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -67,82 +67,39 @@ Dynamic: license-file
|
|
|
67
67
|
|
|
68
68
|
---
|
|
69
69
|
|
|
70
|
-
> **What's new in 0.4.
|
|
71
|
-
|
|
72
|
-
>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
[
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
(``/etc`` / ``/usr`` / ``/System``), or a sensitive dotfile
|
|
104
|
-
(``~/.ssh`` / ``~/.aws`` / ``~/.gnupg``). Also in this
|
|
105
|
-
release: a hard cap on the in-process LLM response cache
|
|
106
|
-
(was unbounded; now 256 entries with FIFO eviction) and a
|
|
107
|
-
fix for ``subgraph_for(max_hops=...)`` which used to ignore
|
|
108
|
-
the argument and always walk one hop (now BFS up to N hops,
|
|
109
|
-
hard-capped at 8). 29 new regression cases. [Full
|
|
110
|
-
CHANGELOG →](./CHANGELOG.md#0410---2026-09-23)
|
|
111
|
-
|
|
112
|
-
> **What's new in 0.4.9** — **OKF v0.2 export** + **bi-temporal `as_of` recall**. `loop-memory export-okf <dir>` writes an [Open Knowledge Format](https://github.com/okf/SPEC) v0.2 bundle — one `.md` file per wiki page with YAML frontmatter + an `index.md` index — so a Loop Memory store can be handed to any OKF-aware tool (Grep, Obsidian, akitaonrails/ai-memory, okf-agent-memory, ...) without re-parsing the body (audit 2026-09-20, Google OKF v0.2 spec + akitaonrails/ai-memory 2.0 + okf-memory/okf-agent-memory, all Apache-2.0/MIT, 2026-09-01 → 2026-09-06). `loop-memory recall <q> --as-of <ISO|epoch>` answers the bi-temporal question "what did we know about this query at that moment?" — a memory written after the moment is invisible, and a superseded memory walks its chain to find whether the invalidation had happened by then (audit 2026-09-20, loomcycle v1.33-v1.49 RFCs BL/BS/BU/BV/BW, Apache-2.0). Both shapes ship with HTTP routes (`POST /api/export/okf`, `GET /api/recall?as_of=...`). 34 new regression cases pin both shapes. [Full CHANGELOG →](./CHANGELOG.md#049---2026-09-20)
|
|
113
|
-
|
|
114
|
-
> **What's new in 0.4.8** — **L0 outline recall** +
|
|
115
|
-
> **per-agent identity bootstrap**. `loop-memory recall-paths <q>`
|
|
116
|
-
> (and the new `recall --outline` flag) returns only `id` +
|
|
117
|
-
> `abstract` + `score` + `why` for each hit — never the full
|
|
118
|
-
> body — so an agent can identify the top candidates cheaply
|
|
119
|
-
> before deciding which bodies to fetch (audit 2026-09-13,
|
|
120
|
-
> tigerless-labs/agent-memory v0.3.0 recall-ladder pattern).
|
|
121
|
-
> `loop-memory init --agent <name>` registers an agent in the
|
|
122
|
-
> new `agents` table and (with `--install-hooks`) also wires
|
|
123
|
-
> the MCP + SessionStart hooks for codex/claude/hermes; the
|
|
124
|
-
> index is bumped on every `install-hooks` run so a user can
|
|
125
|
-
> always answer "which CLIs are wired into this store?" with
|
|
126
|
-
> one SQL query (audit 2026-09-13, Mem0 CLI `init --agent`
|
|
127
|
-
> pattern). Both shapes ship with HTTP routes
|
|
128
|
-
> (`/api/recall/outline`, `/api/agents`, `/api/init/agent`).
|
|
129
|
-
> 57 new regression cases pin both shapes. [Full
|
|
130
|
-
> changelog →](CHANGELOG.md)
|
|
131
|
-
>
|
|
132
|
-
> **What's new in 0.4.7** — **per-memory lifetime stats** +
|
|
133
|
-
> **portable SQLite snapshot**. `loop-memory memory-stats <id>`
|
|
134
|
-
> returns a flat dict (recall_count, positive, negative, age,
|
|
135
|
-
> etc.) so you can ask "is anyone still using this memory?"
|
|
136
|
-
> without cracking open the SQLite file (audit 2026-09-06,
|
|
137
|
-
> agentmemory v1.3.0 pattern). `loop-memory snapshot <out>` /
|
|
138
|
-
> `loop-memory restore <in>` move a single-file, lossless
|
|
139
|
-
> snapshot of the entire store — including recall-quality
|
|
140
|
-
> signals and the supersession chain — to another machine
|
|
141
|
-
> (audit 2026-09-06, codexa-memory v0.2.0 pattern). Both shapes
|
|
142
|
-
> ship with HTTP routes (`/api/memories/{id}/stats`,
|
|
143
|
-
> `/api/snapshot`, `/api/snapshot/restore`). 33 new regression
|
|
144
|
-
> cases pin both shapes.
|
|
145
|
-
> [Full changelog →](CHANGELOG.md)
|
|
70
|
+
> **What's new in 0.4.21** — **the remote SDK can finally authenticate, and it refuses to send memory data over a plaintext connection** (audit 2026-11-08, weekly ecosystem sync). The audit for this release (2026-11-08) turned up a real hole rather than a hypothetical one. `MemoryClient.http(base_url=...)` had **no transport guard at all**: point it at `http://mem.example.com` and it opened a plain socket and shipped the user's memory text across the network — and because the HTTP backend had **no way to send the bearer token** (`sqlite_store.py` even carried the comment *"header when `MemoryClient.http(..., token=...)` is given"* for a parameter that did not exist), the only way for a remote client to talk to a token-protected `loop-memory serve` was to turn auth **off** — exactly the configuration in which both the memory text and the token cross the network in cleartext. Two upstream projects closed the same hole in the same week: `rohitg00/agentmemory` v0.9.32 (2026-11-04, Apache-2.0) made *"hooks send nothing to a remote plain `http://` server; use HTTPS for a remote server"* a **breaking change**, and `topoteretes/cognee` v1.6.7 (2026-11-06, Apache-2.0) refuses *"server file paths over HTTP"* by default — both are pattern references only, no code copied. **What shipped:** a new `loop_memory.security.transport` that classifies a base URL and refuses plaintext `http://` to a **non-loopback** host (plaintext to a **loopback** address stays allowed — that traffic never leaves the host, and it is the default deployment), refuses a URL with embedded credentials, and refuses an unsupported scheme; `Authorization: Bearer` support in the SDK and in the shared `http_*_json` helpers, so the documented remote path works against a protected server instead of requiring auth to be turned off; a redirect guard that refuses an `https://` → `http://` downgrade (urllib's stock handler follows it, which would undo the TLS guarantee one hop from the server) and refuses a cross-origin redirect while a token is attached (urllib replays the `Authorization` header onto the redirect request); and the server half — `loop-memory serve` records its bind address and a new `loop-memory doctor` check (`plaintext_exposure`) warns when the API is reachable from the network over plaintext. The rule is about the *destination*, not the scheme: a private tunnel or a VPN'd LAN is a legitimate deployment, so `allow_insecure=True` (or `LOOP_MEMORY_ALLOW_INSECURE=1`) exists — but it is never silent, it warns on every construction. 37 new regression cases; suite at **1041 passed / 4 skipped**. See [`docs/transport-security.md`](./docs/transport-security.md) for the full contract. [Full CHANGELOG →](./CHANGELOG.md#0421---2026-11-08)
|
|
71
|
+
|
|
72
|
+
<details>
|
|
73
|
+
<summary><b>Older releases</b> — 0.4.20 and earlier, one line each (the CHANGELOG has the full detail)</summary>
|
|
74
|
+
|
|
75
|
+
| Version | Date | What changed | Full notes |
|
|
76
|
+
| --- | --- | --- | --- |
|
|
77
|
+
| [0.4.20](./CHANGELOG.md#0420---2026-11-01) | 2026-11-01 | The packaging test works on the oldest supported Python (no `tomllib` on 3.10) |
|
|
78
|
+
| [0.4.19](./CHANGELOG.md#0419---2026-11-01) | 2026-11-01 | The wheel ships the whole dashboard and the benchmark fixture (recursive package-data globs) |
|
|
79
|
+
| [0.4.18](./CHANGELOG.md#0418---2026-11-01) | 2026-11-01 | The BM25 channel no longer publishes a score that collapses to zero — plus a retrieval benchmark |
|
|
80
|
+
| [0.4.17](./CHANGELOG.md#0417---2026-10-25) | 2026-10-25 | A dry-run dream pass no longer writes to the database |
|
|
81
|
+
| [0.4.16](./CHANGELOG.md#0416---2026-10-25) | 2026-10-25 | Subject-key logical fact slots, `latest_only` recall, and the sleep-time "dream" pass |
|
|
82
|
+
| [0.4.15](./CHANGELOG.md#0415---2026-10-18) | 2026-10-18 | Content-hash dedup, bi-temporal validity window, evidence chain |
|
|
83
|
+
| [0.4.14](./CHANGELOG.md#0414---2026-10-12) | 2026-10-12 | `loop-memory doctor` — a deep store-health check |
|
|
84
|
+
| [0.4.13](./CHANGELOG.md#0413---2026-10-05) | 2026-10-05 | Recall token / char budget cap and a unified `level=0|1|2` tiered-loader knob |
|
|
85
|
+
| [0.4.12](./CHANGELOG.md#0412---2026-09-27) | 2026-09-27 | OKF bundle import and DB-level audit enforcement (12 SQLite triggers) |
|
|
86
|
+
| [0.4.11](./CHANGELOG.md#0411---2026-09-24) | 2026-09-24 | Operational probes (`/api/healthz`, `/api/readyz`) and the SQLite write-contention fix |
|
|
87
|
+
| [0.4.10](./CHANGELOG.md#0410---2026-09-23) | 2026-09-23 | Path-safety guard for the five export / snapshot endpoints, LLM cache cap, `subgraph_for(max_hops=)` |
|
|
88
|
+
| [0.4.9](./CHANGELOG.md#049---2026-09-20) | 2026-09-20 | OKF v0.2 export and bi-temporal `as_of` recall |
|
|
89
|
+
| [0.4.8](./CHANGELOG.md#048---2026-09-13) | 2026-09-13 | L0 outline recall and per-agent identity bootstrap |
|
|
90
|
+
| [0.4.7](./CHANGELOG.md#047---2026-09-06) | 2026-09-06 | Per-memory lifetime stats and a portable SQLite snapshot |
|
|
91
|
+
| [0.4.6](./CHANGELOG.md#046---2026-08-30) | 2026-08-30 | The memory supersession chain (no hard deletes) |
|
|
92
|
+
| [0.4.5](./CHANGELOG.md#045---2026-08-23) | 2026-08-23 | `loop_memory.__version__` was stale at `"0.2.0"` |
|
|
93
|
+
| [0.4.4](./CHANGELOG.md#044---2026-08-23) | 2026-08-23 | `loop-memory rules --agent <name> --write` |
|
|
94
|
+
| [0.4.3](./CHANGELOG.md#043---2026-08-16) | 2026-08-16 | The cognitive sweep N+1 fix |
|
|
95
|
+
| [0.4.2](./CHANGELOG.md#042---2026-08-09) | 2026-08-09 | README refresh |
|
|
96
|
+
| [0.4.1](./CHANGELOG.md#041---2026-08-09) | 2026-08-09 | CLI dispatch hardened against common `--help` crashes |
|
|
97
|
+
| [0.4.0](./CHANGELOG.md#040---2026-08-09) | 2026-08-09 | OpenAI base URL env-var fallback |
|
|
98
|
+
| [0.3.0](./CHANGELOG.md#030--2026-07-11) | 2026-07-11 | The run progress strip is back |
|
|
99
|
+
| [0.2.0](./CHANGELOG.md#020--2026-07-10) | 2026-07-10 | The SQLite-backed `MemoryStore` |
|
|
100
|
+
| [0.1.0](./CHANGELOG.md#010--2026-07-10) | 2026-07-10 | The dashboard tab empty-data regression |
|
|
101
|
+
|
|
102
|
+
</details>
|
|
146
103
|
|
|
147
104
|
---
|
|
148
105
|
|
|
@@ -310,6 +267,7 @@ Hermes, clawx) can read and write, we built this for you.
|
|
|
310
267
|
| [docs/api.md](docs/api.md) | HTTP API reference — every route, request body, and response shape the UI consumes |
|
|
311
268
|
| [docs/agent-memory-api.md](docs/agent-memory-api.md) | Stable four-verb SDK / HTTP / MCP contract for any Agent |
|
|
312
269
|
| [docs/universal-agent-memory.md](docs/universal-agent-memory.md) | v7 graph memory, cognitive sleep, portable bundles, namespaces, and MCP/CLI extensions |
|
|
270
|
+
| [docs/transport-security.md](docs/transport-security.md) | How a remote client is allowed to talk to `loop-memory serve` — the transport guard, bearer-token auth, redirect hardening, and the `plaintext_exposure` doctor check (0.4.21) |
|
|
313
271
|
| [docs/providers.md](docs/providers.md) | LLM provider reference — built-in providers, defaults, base URLs, and how to add a new one |
|
|
314
272
|
| [docs/auto-capture.md](docs/auto-capture.md) | Hooking Codex / Claude / Hermes / OpenClaw watchers (filesystem, launchd, systemd, cron) |
|
|
315
273
|
| [CONTRIBUTING.md](CONTRIBUTING.md) | Local dev loop, pytest, where secrets live, how to add a provider/source |
|
|
@@ -663,7 +621,7 @@ loop_memory/
|
|
|
663
621
|
backends/ # embedding (hashing / sentence-transformers) + vector store (memory / chroma)
|
|
664
622
|
storage/ # SQLite-backed MemoryStore + migrations
|
|
665
623
|
privacy/ # <private> stripping + regex redaction
|
|
666
|
-
security/ # Keychain / 0600-file secrets wrapper
|
|
624
|
+
security/ # Keychain / 0600-file secrets wrapper + the transport guard
|
|
667
625
|
mcp/ # stdio JSON-RPC MCP server
|
|
668
626
|
serve/ # FastAPI app, watcher, web UI (Timeline / Dashboard / Wiki / Graph)
|
|
669
627
|
export/ # markdown + v7 portable bundle export/import
|
|
@@ -673,16 +631,18 @@ loop_memory/
|
|
|
673
631
|
memory/types.py # MemoryItem + 4 tiers
|
|
674
632
|
examples/demo.py # runnable, zero-API-key demo
|
|
675
633
|
py.typed
|
|
676
|
-
tests/ #
|
|
634
|
+
tests/ # 1041 unit tests across memory / SDK / serve / CLI / scripts
|
|
677
635
|
docs/
|
|
678
636
|
auto-capture.md # launchd / systemd / cron recipes
|
|
679
637
|
architecture.md # layered view + 5-stage evolution pipeline
|
|
680
638
|
api.md # HTTP API reference
|
|
639
|
+
transport-security.md # how a remote client may talk to `serve` (0.4.21)
|
|
681
640
|
agent-memory-api.md # four-verb SDK / HTTP / MCP contract
|
|
682
641
|
universal-agent-memory.md # v7 graph memory + cognitive sleep + bundles
|
|
683
642
|
providers.md # LLM provider reference
|
|
684
643
|
settings.md # settings table + secrets file
|
|
685
644
|
weekly-research-automation.md # how the project auto-evolves from upstream research
|
|
645
|
+
research/ # the weekly ecosystem notes each release is based on
|
|
686
646
|
```
|
|
687
647
|
|
|
688
648
|
---
|
|
@@ -712,6 +672,34 @@ The web UI stores it in `localStorage` under `loop_auth_token` and attaches
|
|
|
712
672
|
it automatically; non-browser clients (curl, MCP, SDK) must opt in by
|
|
713
673
|
passing it explicitly.
|
|
714
674
|
|
|
675
|
+
**Remote clients (SDK):** the HTTP SDK can send the token for you, and it
|
|
676
|
+
refuses to send it — or any memory text — over an unencrypted connection
|
|
677
|
+
to a remote host:
|
|
678
|
+
|
|
679
|
+
```python
|
|
680
|
+
client = MemoryClient.http(
|
|
681
|
+
"https://memory.example.com",
|
|
682
|
+
token=open("token.txt").read().strip(), # Authorization: Bearer …
|
|
683
|
+
)
|
|
684
|
+
client.remember("deploys go out on Tuesdays")
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
Plain `http://` is still fine for `127.0.0.1` / `localhost` / `::1` (the
|
|
688
|
+
traffic never leaves the machine — that is the default deployment), but
|
|
689
|
+
plain `http://` to **any other host is refused**: the memory text and the
|
|
690
|
+
bearer token would cross the network in cleartext. A private tunnel or a
|
|
691
|
+
VPN'd LAN is a legitimate deployment, so `allow_insecure=True` (or
|
|
692
|
+
`LOOP_MEMORY_ALLOW_INSECURE=1`) exists — but it is never silent, it
|
|
693
|
+
warns on every construction. Redirects that downgrade `https://` →
|
|
694
|
+
`http://`, and cross-origin redirects that would replay the token, are
|
|
695
|
+
refused too. The full contract is in
|
|
696
|
+
[docs/transport-security.md](docs/transport-security.md).
|
|
697
|
+
|
|
698
|
+
**Server side:** `loop-memory serve --host <non-loopback>` prints a
|
|
699
|
+
warning and records the bind address, so `loop-memory doctor` reports it
|
|
700
|
+
as a `plaintext_exposure` warning — the API is reachable from the network
|
|
701
|
+
over plaintext.
|
|
702
|
+
|
|
715
703
|
**When to set a token:**
|
|
716
704
|
|
|
717
705
|
- ✅ Always, even on loopback. The default of "no token" exists only as a
|
|
@@ -728,6 +716,12 @@ passing it explicitly.
|
|
|
728
716
|
|
|
729
717
|
- `install-hooks` runs Python that touches `~/.*` — it's gated by the
|
|
730
718
|
bearer token like every other `POST /api/admin/*` route.
|
|
719
|
+
- A remote client cannot leak memory text to the network in cleartext:
|
|
720
|
+
the SDK refuses a plaintext `http://` base URL that is not loopback,
|
|
721
|
+
refuses a URL with embedded credentials, and refuses redirects that
|
|
722
|
+
downgrade the scheme or would replay the `Authorization` header
|
|
723
|
+
(audit 2026-11-08, 0.4.21 — see
|
|
724
|
+
[docs/transport-security.md](docs/transport-security.md)).
|
|
731
725
|
- The `<private>...</private>` span stripping in the privacy layer keeps
|
|
732
726
|
user-marked secrets out of long-term storage; the regex redaction layer
|
|
733
727
|
then catches API keys / tokens / private keys / JWTs / generic
|
|
@@ -22,82 +22,39 @@
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
-
> **What's new in 0.4.
|
|
26
|
-
|
|
27
|
-
>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
[
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
(``/etc`` / ``/usr`` / ``/System``), or a sensitive dotfile
|
|
59
|
-
(``~/.ssh`` / ``~/.aws`` / ``~/.gnupg``). Also in this
|
|
60
|
-
release: a hard cap on the in-process LLM response cache
|
|
61
|
-
(was unbounded; now 256 entries with FIFO eviction) and a
|
|
62
|
-
fix for ``subgraph_for(max_hops=...)`` which used to ignore
|
|
63
|
-
the argument and always walk one hop (now BFS up to N hops,
|
|
64
|
-
hard-capped at 8). 29 new regression cases. [Full
|
|
65
|
-
CHANGELOG →](./CHANGELOG.md#0410---2026-09-23)
|
|
66
|
-
|
|
67
|
-
> **What's new in 0.4.9** — **OKF v0.2 export** + **bi-temporal `as_of` recall**. `loop-memory export-okf <dir>` writes an [Open Knowledge Format](https://github.com/okf/SPEC) v0.2 bundle — one `.md` file per wiki page with YAML frontmatter + an `index.md` index — so a Loop Memory store can be handed to any OKF-aware tool (Grep, Obsidian, akitaonrails/ai-memory, okf-agent-memory, ...) without re-parsing the body (audit 2026-09-20, Google OKF v0.2 spec + akitaonrails/ai-memory 2.0 + okf-memory/okf-agent-memory, all Apache-2.0/MIT, 2026-09-01 → 2026-09-06). `loop-memory recall <q> --as-of <ISO|epoch>` answers the bi-temporal question "what did we know about this query at that moment?" — a memory written after the moment is invisible, and a superseded memory walks its chain to find whether the invalidation had happened by then (audit 2026-09-20, loomcycle v1.33-v1.49 RFCs BL/BS/BU/BV/BW, Apache-2.0). Both shapes ship with HTTP routes (`POST /api/export/okf`, `GET /api/recall?as_of=...`). 34 new regression cases pin both shapes. [Full CHANGELOG →](./CHANGELOG.md#049---2026-09-20)
|
|
68
|
-
|
|
69
|
-
> **What's new in 0.4.8** — **L0 outline recall** +
|
|
70
|
-
> **per-agent identity bootstrap**. `loop-memory recall-paths <q>`
|
|
71
|
-
> (and the new `recall --outline` flag) returns only `id` +
|
|
72
|
-
> `abstract` + `score` + `why` for each hit — never the full
|
|
73
|
-
> body — so an agent can identify the top candidates cheaply
|
|
74
|
-
> before deciding which bodies to fetch (audit 2026-09-13,
|
|
75
|
-
> tigerless-labs/agent-memory v0.3.0 recall-ladder pattern).
|
|
76
|
-
> `loop-memory init --agent <name>` registers an agent in the
|
|
77
|
-
> new `agents` table and (with `--install-hooks`) also wires
|
|
78
|
-
> the MCP + SessionStart hooks for codex/claude/hermes; the
|
|
79
|
-
> index is bumped on every `install-hooks` run so a user can
|
|
80
|
-
> always answer "which CLIs are wired into this store?" with
|
|
81
|
-
> one SQL query (audit 2026-09-13, Mem0 CLI `init --agent`
|
|
82
|
-
> pattern). Both shapes ship with HTTP routes
|
|
83
|
-
> (`/api/recall/outline`, `/api/agents`, `/api/init/agent`).
|
|
84
|
-
> 57 new regression cases pin both shapes. [Full
|
|
85
|
-
> changelog →](CHANGELOG.md)
|
|
86
|
-
>
|
|
87
|
-
> **What's new in 0.4.7** — **per-memory lifetime stats** +
|
|
88
|
-
> **portable SQLite snapshot**. `loop-memory memory-stats <id>`
|
|
89
|
-
> returns a flat dict (recall_count, positive, negative, age,
|
|
90
|
-
> etc.) so you can ask "is anyone still using this memory?"
|
|
91
|
-
> without cracking open the SQLite file (audit 2026-09-06,
|
|
92
|
-
> agentmemory v1.3.0 pattern). `loop-memory snapshot <out>` /
|
|
93
|
-
> `loop-memory restore <in>` move a single-file, lossless
|
|
94
|
-
> snapshot of the entire store — including recall-quality
|
|
95
|
-
> signals and the supersession chain — to another machine
|
|
96
|
-
> (audit 2026-09-06, codexa-memory v0.2.0 pattern). Both shapes
|
|
97
|
-
> ship with HTTP routes (`/api/memories/{id}/stats`,
|
|
98
|
-
> `/api/snapshot`, `/api/snapshot/restore`). 33 new regression
|
|
99
|
-
> cases pin both shapes.
|
|
100
|
-
> [Full changelog →](CHANGELOG.md)
|
|
25
|
+
> **What's new in 0.4.21** — **the remote SDK can finally authenticate, and it refuses to send memory data over a plaintext connection** (audit 2026-11-08, weekly ecosystem sync). The audit for this release (2026-11-08) turned up a real hole rather than a hypothetical one. `MemoryClient.http(base_url=...)` had **no transport guard at all**: point it at `http://mem.example.com` and it opened a plain socket and shipped the user's memory text across the network — and because the HTTP backend had **no way to send the bearer token** (`sqlite_store.py` even carried the comment *"header when `MemoryClient.http(..., token=...)` is given"* for a parameter that did not exist), the only way for a remote client to talk to a token-protected `loop-memory serve` was to turn auth **off** — exactly the configuration in which both the memory text and the token cross the network in cleartext. Two upstream projects closed the same hole in the same week: `rohitg00/agentmemory` v0.9.32 (2026-11-04, Apache-2.0) made *"hooks send nothing to a remote plain `http://` server; use HTTPS for a remote server"* a **breaking change**, and `topoteretes/cognee` v1.6.7 (2026-11-06, Apache-2.0) refuses *"server file paths over HTTP"* by default — both are pattern references only, no code copied. **What shipped:** a new `loop_memory.security.transport` that classifies a base URL and refuses plaintext `http://` to a **non-loopback** host (plaintext to a **loopback** address stays allowed — that traffic never leaves the host, and it is the default deployment), refuses a URL with embedded credentials, and refuses an unsupported scheme; `Authorization: Bearer` support in the SDK and in the shared `http_*_json` helpers, so the documented remote path works against a protected server instead of requiring auth to be turned off; a redirect guard that refuses an `https://` → `http://` downgrade (urllib's stock handler follows it, which would undo the TLS guarantee one hop from the server) and refuses a cross-origin redirect while a token is attached (urllib replays the `Authorization` header onto the redirect request); and the server half — `loop-memory serve` records its bind address and a new `loop-memory doctor` check (`plaintext_exposure`) warns when the API is reachable from the network over plaintext. The rule is about the *destination*, not the scheme: a private tunnel or a VPN'd LAN is a legitimate deployment, so `allow_insecure=True` (or `LOOP_MEMORY_ALLOW_INSECURE=1`) exists — but it is never silent, it warns on every construction. 37 new regression cases; suite at **1041 passed / 4 skipped**. See [`docs/transport-security.md`](./docs/transport-security.md) for the full contract. [Full CHANGELOG →](./CHANGELOG.md#0421---2026-11-08)
|
|
26
|
+
|
|
27
|
+
<details>
|
|
28
|
+
<summary><b>Older releases</b> — 0.4.20 and earlier, one line each (the CHANGELOG has the full detail)</summary>
|
|
29
|
+
|
|
30
|
+
| Version | Date | What changed | Full notes |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| [0.4.20](./CHANGELOG.md#0420---2026-11-01) | 2026-11-01 | The packaging test works on the oldest supported Python (no `tomllib` on 3.10) |
|
|
33
|
+
| [0.4.19](./CHANGELOG.md#0419---2026-11-01) | 2026-11-01 | The wheel ships the whole dashboard and the benchmark fixture (recursive package-data globs) |
|
|
34
|
+
| [0.4.18](./CHANGELOG.md#0418---2026-11-01) | 2026-11-01 | The BM25 channel no longer publishes a score that collapses to zero — plus a retrieval benchmark |
|
|
35
|
+
| [0.4.17](./CHANGELOG.md#0417---2026-10-25) | 2026-10-25 | A dry-run dream pass no longer writes to the database |
|
|
36
|
+
| [0.4.16](./CHANGELOG.md#0416---2026-10-25) | 2026-10-25 | Subject-key logical fact slots, `latest_only` recall, and the sleep-time "dream" pass |
|
|
37
|
+
| [0.4.15](./CHANGELOG.md#0415---2026-10-18) | 2026-10-18 | Content-hash dedup, bi-temporal validity window, evidence chain |
|
|
38
|
+
| [0.4.14](./CHANGELOG.md#0414---2026-10-12) | 2026-10-12 | `loop-memory doctor` — a deep store-health check |
|
|
39
|
+
| [0.4.13](./CHANGELOG.md#0413---2026-10-05) | 2026-10-05 | Recall token / char budget cap and a unified `level=0|1|2` tiered-loader knob |
|
|
40
|
+
| [0.4.12](./CHANGELOG.md#0412---2026-09-27) | 2026-09-27 | OKF bundle import and DB-level audit enforcement (12 SQLite triggers) |
|
|
41
|
+
| [0.4.11](./CHANGELOG.md#0411---2026-09-24) | 2026-09-24 | Operational probes (`/api/healthz`, `/api/readyz`) and the SQLite write-contention fix |
|
|
42
|
+
| [0.4.10](./CHANGELOG.md#0410---2026-09-23) | 2026-09-23 | Path-safety guard for the five export / snapshot endpoints, LLM cache cap, `subgraph_for(max_hops=)` |
|
|
43
|
+
| [0.4.9](./CHANGELOG.md#049---2026-09-20) | 2026-09-20 | OKF v0.2 export and bi-temporal `as_of` recall |
|
|
44
|
+
| [0.4.8](./CHANGELOG.md#048---2026-09-13) | 2026-09-13 | L0 outline recall and per-agent identity bootstrap |
|
|
45
|
+
| [0.4.7](./CHANGELOG.md#047---2026-09-06) | 2026-09-06 | Per-memory lifetime stats and a portable SQLite snapshot |
|
|
46
|
+
| [0.4.6](./CHANGELOG.md#046---2026-08-30) | 2026-08-30 | The memory supersession chain (no hard deletes) |
|
|
47
|
+
| [0.4.5](./CHANGELOG.md#045---2026-08-23) | 2026-08-23 | `loop_memory.__version__` was stale at `"0.2.0"` |
|
|
48
|
+
| [0.4.4](./CHANGELOG.md#044---2026-08-23) | 2026-08-23 | `loop-memory rules --agent <name> --write` |
|
|
49
|
+
| [0.4.3](./CHANGELOG.md#043---2026-08-16) | 2026-08-16 | The cognitive sweep N+1 fix |
|
|
50
|
+
| [0.4.2](./CHANGELOG.md#042---2026-08-09) | 2026-08-09 | README refresh |
|
|
51
|
+
| [0.4.1](./CHANGELOG.md#041---2026-08-09) | 2026-08-09 | CLI dispatch hardened against common `--help` crashes |
|
|
52
|
+
| [0.4.0](./CHANGELOG.md#040---2026-08-09) | 2026-08-09 | OpenAI base URL env-var fallback |
|
|
53
|
+
| [0.3.0](./CHANGELOG.md#030--2026-07-11) | 2026-07-11 | The run progress strip is back |
|
|
54
|
+
| [0.2.0](./CHANGELOG.md#020--2026-07-10) | 2026-07-10 | The SQLite-backed `MemoryStore` |
|
|
55
|
+
| [0.1.0](./CHANGELOG.md#010--2026-07-10) | 2026-07-10 | The dashboard tab empty-data regression |
|
|
56
|
+
|
|
57
|
+
</details>
|
|
101
58
|
|
|
102
59
|
---
|
|
103
60
|
|
|
@@ -265,6 +222,7 @@ Hermes, clawx) can read and write, we built this for you.
|
|
|
265
222
|
| [docs/api.md](docs/api.md) | HTTP API reference — every route, request body, and response shape the UI consumes |
|
|
266
223
|
| [docs/agent-memory-api.md](docs/agent-memory-api.md) | Stable four-verb SDK / HTTP / MCP contract for any Agent |
|
|
267
224
|
| [docs/universal-agent-memory.md](docs/universal-agent-memory.md) | v7 graph memory, cognitive sleep, portable bundles, namespaces, and MCP/CLI extensions |
|
|
225
|
+
| [docs/transport-security.md](docs/transport-security.md) | How a remote client is allowed to talk to `loop-memory serve` — the transport guard, bearer-token auth, redirect hardening, and the `plaintext_exposure` doctor check (0.4.21) |
|
|
268
226
|
| [docs/providers.md](docs/providers.md) | LLM provider reference — built-in providers, defaults, base URLs, and how to add a new one |
|
|
269
227
|
| [docs/auto-capture.md](docs/auto-capture.md) | Hooking Codex / Claude / Hermes / OpenClaw watchers (filesystem, launchd, systemd, cron) |
|
|
270
228
|
| [CONTRIBUTING.md](CONTRIBUTING.md) | Local dev loop, pytest, where secrets live, how to add a provider/source |
|
|
@@ -618,7 +576,7 @@ loop_memory/
|
|
|
618
576
|
backends/ # embedding (hashing / sentence-transformers) + vector store (memory / chroma)
|
|
619
577
|
storage/ # SQLite-backed MemoryStore + migrations
|
|
620
578
|
privacy/ # <private> stripping + regex redaction
|
|
621
|
-
security/ # Keychain / 0600-file secrets wrapper
|
|
579
|
+
security/ # Keychain / 0600-file secrets wrapper + the transport guard
|
|
622
580
|
mcp/ # stdio JSON-RPC MCP server
|
|
623
581
|
serve/ # FastAPI app, watcher, web UI (Timeline / Dashboard / Wiki / Graph)
|
|
624
582
|
export/ # markdown + v7 portable bundle export/import
|
|
@@ -628,16 +586,18 @@ loop_memory/
|
|
|
628
586
|
memory/types.py # MemoryItem + 4 tiers
|
|
629
587
|
examples/demo.py # runnable, zero-API-key demo
|
|
630
588
|
py.typed
|
|
631
|
-
tests/ #
|
|
589
|
+
tests/ # 1041 unit tests across memory / SDK / serve / CLI / scripts
|
|
632
590
|
docs/
|
|
633
591
|
auto-capture.md # launchd / systemd / cron recipes
|
|
634
592
|
architecture.md # layered view + 5-stage evolution pipeline
|
|
635
593
|
api.md # HTTP API reference
|
|
594
|
+
transport-security.md # how a remote client may talk to `serve` (0.4.21)
|
|
636
595
|
agent-memory-api.md # four-verb SDK / HTTP / MCP contract
|
|
637
596
|
universal-agent-memory.md # v7 graph memory + cognitive sleep + bundles
|
|
638
597
|
providers.md # LLM provider reference
|
|
639
598
|
settings.md # settings table + secrets file
|
|
640
599
|
weekly-research-automation.md # how the project auto-evolves from upstream research
|
|
600
|
+
research/ # the weekly ecosystem notes each release is based on
|
|
641
601
|
```
|
|
642
602
|
|
|
643
603
|
---
|
|
@@ -667,6 +627,34 @@ The web UI stores it in `localStorage` under `loop_auth_token` and attaches
|
|
|
667
627
|
it automatically; non-browser clients (curl, MCP, SDK) must opt in by
|
|
668
628
|
passing it explicitly.
|
|
669
629
|
|
|
630
|
+
**Remote clients (SDK):** the HTTP SDK can send the token for you, and it
|
|
631
|
+
refuses to send it — or any memory text — over an unencrypted connection
|
|
632
|
+
to a remote host:
|
|
633
|
+
|
|
634
|
+
```python
|
|
635
|
+
client = MemoryClient.http(
|
|
636
|
+
"https://memory.example.com",
|
|
637
|
+
token=open("token.txt").read().strip(), # Authorization: Bearer …
|
|
638
|
+
)
|
|
639
|
+
client.remember("deploys go out on Tuesdays")
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
Plain `http://` is still fine for `127.0.0.1` / `localhost` / `::1` (the
|
|
643
|
+
traffic never leaves the machine — that is the default deployment), but
|
|
644
|
+
plain `http://` to **any other host is refused**: the memory text and the
|
|
645
|
+
bearer token would cross the network in cleartext. A private tunnel or a
|
|
646
|
+
VPN'd LAN is a legitimate deployment, so `allow_insecure=True` (or
|
|
647
|
+
`LOOP_MEMORY_ALLOW_INSECURE=1`) exists — but it is never silent, it
|
|
648
|
+
warns on every construction. Redirects that downgrade `https://` →
|
|
649
|
+
`http://`, and cross-origin redirects that would replay the token, are
|
|
650
|
+
refused too. The full contract is in
|
|
651
|
+
[docs/transport-security.md](docs/transport-security.md).
|
|
652
|
+
|
|
653
|
+
**Server side:** `loop-memory serve --host <non-loopback>` prints a
|
|
654
|
+
warning and records the bind address, so `loop-memory doctor` reports it
|
|
655
|
+
as a `plaintext_exposure` warning — the API is reachable from the network
|
|
656
|
+
over plaintext.
|
|
657
|
+
|
|
670
658
|
**When to set a token:**
|
|
671
659
|
|
|
672
660
|
- ✅ Always, even on loopback. The default of "no token" exists only as a
|
|
@@ -683,6 +671,12 @@ passing it explicitly.
|
|
|
683
671
|
|
|
684
672
|
- `install-hooks` runs Python that touches `~/.*` — it's gated by the
|
|
685
673
|
bearer token like every other `POST /api/admin/*` route.
|
|
674
|
+
- A remote client cannot leak memory text to the network in cleartext:
|
|
675
|
+
the SDK refuses a plaintext `http://` base URL that is not loopback,
|
|
676
|
+
refuses a URL with embedded credentials, and refuses redirects that
|
|
677
|
+
downgrade the scheme or would replay the `Authorization` header
|
|
678
|
+
(audit 2026-11-08, 0.4.21 — see
|
|
679
|
+
[docs/transport-security.md](docs/transport-security.md)).
|
|
686
680
|
- The `<private>...</private>` span stripping in the privacy layer keeps
|
|
687
681
|
user-marked secrets out of long-term storage; the regex redaction layer
|
|
688
682
|
then catches API keys / tokens / private keys / JWTs / generic
|
|
@@ -40,6 +40,25 @@ def run_serve(args) -> int:
|
|
|
40
40
|
" Press Ctrl-C now unless you know what you are doing.\n",
|
|
41
41
|
file=sys.stderr,
|
|
42
42
|
)
|
|
43
|
+
# Audit 2026-11-08 (loop-memory 0.4.21): record the bind address
|
|
44
|
+
# in the store so `loop-memory doctor` can report the network
|
|
45
|
+
# exposure of a non-loopback bind. The client-side transport
|
|
46
|
+
# guard (loop_memory.security.transport) refuses a plaintext
|
|
47
|
+
# remote URL; this setting is the server-side half of the same
|
|
48
|
+
# story — it tells the operator that the API they just exposed
|
|
49
|
+
# is reachable in cleartext from the network.
|
|
50
|
+
try:
|
|
51
|
+
from ...storage.sqlite_store import MemoryStore
|
|
52
|
+
import time as _time
|
|
53
|
+
MemoryStore(DEFAULT_DB).set_setting(
|
|
54
|
+
"loop_memory_serve_bind",
|
|
55
|
+
{"host": host, "port": int(port), "scheme": "http",
|
|
56
|
+
"recorded_at": _time.time()},
|
|
57
|
+
)
|
|
58
|
+
except Exception as _exc: # pragma: no cover - never block startup
|
|
59
|
+
print(f"[loop-memory] warning: could not record the bind address: {_exc}",
|
|
60
|
+
file=sys.stderr)
|
|
61
|
+
|
|
43
62
|
from ...serve.app import serve as _serve
|
|
44
63
|
print(f"[loop-memory] serving UI on http://{host}:{port}")
|
|
45
64
|
print(f"[loop-memory] db = {DEFAULT_DB}")
|