Skip to main content
Loop Memory

Loop Memory

A general-purpose memory system for every AI agent you run locally.

Auto-captures every Codex / Claude / Hermes / OpenClaw conversation, distils them into a tight wiki of stable knowledge, and lets the agent recall what matters on demand.

CI PyPI License: MIT Release guide Python Zero deps


What it does

Loop Memory gives every agent you use a single, persistent brain that outlives any one conversation. Each agent (Codex CLI, Claude Code, Hermes, OpenClaw / clawx, …) drops its transcripts onto disk; Loop Memory quietly catches them, scores every fragment by importance × recency × usage × feedback, distils the long tail into a curated wiki, and re-injects the relevant pieces into the next session.

flowchart LR
    subgraph Capture
        A1[Codex CLI] --> Store
        A2[Claude Code] --> Store
        A3[Hermes] --> Store
        A4[OpenClaw / clawx] --> Store
        A5[Any watcher] --> Store
    end
    Store[(SQLite
sessions + memories)]
    Store --> Score[Signal-aware
scoring]
    Score --> Cluster[Semantic
clustering]
    Cluster --> Distill[Per-cluster
distillation]
    Distill --> Wiki[(Curated wiki
preferences / decisions /
projects / domain)]
    Wiki --> Recall[Next-session recall
via MCP / hooks]
    Recall --> A1
    Recall --> A2
    Recall --> A3
    Recall --> A4

One loop, many agents, one evolving wiki.


Install

pip install loop-memory                          # core: zero deps
pip install 'loop-memory[serve]'                  # + FastAPI web UI
pip install 'loop-memory[openai]'                 # + OpenAI client
pip install 'loop-memory[all]'                    # everything

Quickstart

# 1. Import everything that already lives on your disk
loop-memory ingest codex          # ~/.codex/sessions/*.json
loop-memory ingest claude         # ~/.claude/**/*.jsonl
loop-memory ingest hermes         # ~/.hermes/**/*.jsonl

# 2. Look at it
loop-memory serve --port 7767     # open http://127.0.0.1:7767

# 3. Make it run on a timer
#    (see docs/auto-capture.md for launchd / systemd / cron snippets)
loop-memory consolidate          # rescore + GC + dedupe

Why Loop Memory vs. every other agent-memory project

We surveyed the open-source memory systems for AI agents that came up in 2026 (Mem0, Hindsight, OpenViking, A-MEM) and kept what worked. Loop Memory is the smallest system that still ships all of the following — every other project we looked at lacks at least one:

Capability Loop Memory Mem0 v3 Hindsight OpenViking A-MEM
Multi-source capture (Codex / Claude / Hermes / OpenClaw) ✅ out of the box ⚠ requires plugin per client ⚠ hosted only ⚠ SDK + companion app
Local-first SQLite (zero external services) ❌ Postgres + Qdrant ❌ Postgres + Qdrant ⚠ file-system + cloud ⚠ ChromaDB
Hybrid recall: BM25 + semantic + entity (RRF) ⚠ entity-only
Temporal reasoning in retrieval (boost / suppress by date intent)
Tiered loading L0/L1/L2 (titles / summary / body)
Per-client wiki scope (global vs. source-specific) ⚠ user-level ⚠ tenant-level
Distillation that prefers completeness over compression
Distillation that runs on a schedule and on demand ✅ both ✅ schedule ✅ both ✅ schedule
Knowledge graph (entities + relations) ✅ light ✅ Neo4j ✅ native graph ✅ ChromaDB
Cognitive sleep with auditable cleanup ✅ v7
Git-friendly MEMORY.md export / fork ✅ v7 ⚠ hosted ✅ file-based
Universal SDK + HTTP + MCP contract ✅ v7
OpenAI-compatible multi-provider LLM (incl. MiniMax)
Open-source, MIT, no hosted tier required ✅ (cloud SKUs dominant) ⚠ AGPLv3

The honest gap: we don't have Mem0's hosted platform (managed multi-tenant scaling, byte-benchmarked vector indexes), and we don't ship OpenViking's companion desktop app. What we do ship is the smallest set of moving parts that lets you run the same memory loop across every locally-installed agent without sending your transcripts anywhere.

If you want raw scale, Mem0's cloud SKU will beat us. If you want a local-first single-user brain that every offline agent (Codex, Claude, Hermes, clawx) can read and write, we built this for you.


Architecture & docs

Doc What's in it
docs/architecture.md Layered view of the subpackages, the 5-stage evolution pipeline, the request lifecycle, and how secrets and settings are separated between the SQLite store and a local permission-restricted secrets file
docs/api.md HTTP API reference — every route, request body, and response shape the UI consumes
docs/agent-memory-api.md Stable four-verb SDK / HTTP / MCP contract for any Agent
docs/universal-agent-memory.md v7 graph memory, cognitive sleep, portable bundles, namespaces, and MCP/CLI extensions
docs/providers.md LLM provider reference — built-in providers, defaults, base URLs, and how to add a new one
docs/auto-capture.md Hooking Codex / Claude / Hermes / OpenClaw watchers (filesystem, launchd, systemd, cron)
CONTRIBUTING.md Local dev loop, pytest, where secrets live, how to add a provider/source
CHANGELOG.md Per-release notes

The live interactive OpenAPI document is at http://127.0.0.1:7767/docs once the server is running.


After install: 30-second setup

# Show me what's installed, what's wired, what's broken.
loop-memory doctor

# Auto-configure MCP + SessionStart hooks for every detected CLI
# (Codex CLI, Claude Code, Hermes).
loop-memory install-hooks

# Install the openclaw/clawx auto-ingest watcher (launchd on macOS).
loop-memory openclaw-setup

# Run it on a schedule — web UI → ⚙ Model → set "every day 03:00".
loop-memory serve --port 7767   # → http://127.0.0.1:7767

The web UI also has a 🔍 Run doctor panel under the kebab menu (⌘D) that shows the same green/red diagnostic screen inline.


Auto-capture (after every conversation)

A new conversation ends → its transcript file lands in a watched directory → the watcher ingests it → it shows up in the UI. Three flavors:

Tool Watch
Codex CLI loop-memory hook --source codex --watch ~/.codex/sessions
Claude Code loop-memory hook --source claude --watch ~/.claude
Hermes loop-memory hook --source hermes --watch ~/.hermes
OpenClaw (clawx) loop-memory hook --source openclaw --watch ~/.openclaw/agents/main/sessions — also ingests workspace/memory/*.md daily logs

Three of these in a tmux session, or persisted via launchd, keeps your memory store fresh without any clicks. Run loop-memory consolidate on an hourly cron to keep the scoring healthy.

See docs/auto-capture.md for ready-to-paste launchd + systemd + cron snippets.

Dashboard + Evolution consolidator (看板 + 进化式蒸馏)

The Dashboard tab gives you a live, at-a-glance view of the memory pipeline and lets you steer it.

  • 4 KPI cards — raw memory count, distilled wiki count, average score, total recall events (real-time, auto-refresh every 8s).
  • 5-stage data-flow animation — score → cluster → distill → wiki → memo. Click any node to drill into the items that flowed through it last run. The wave path on top pulses to suggest motion; nodes pulse on hover.
  • Drill-down panel — every item has 👍 / 👎 buttons that feed the evolution loop. Negative feedback lowers the memory's importance; positive bumps it. Both update the "most recalled memories" list.
  • Evolution run button — invokes the 5-stage Evolution Consolidator with whatever provider is currently configured.

Evolution Consolidator (replaces the old single-pass one)

A hierarchical, signal-aware distillation pipeline designed to keep your knowledge base tight and increasingly aligned with your real preferences over time.

Stage What it does
1. Signal-Aware Scoring Blends importance × recency with recall_count (+0..0.10) and negative feedback (-0..0.15), so items the user actually uses float to the top.
2. Semantic Batching Greedy cosine clustering using a hashed embedding; clusters ≤15 items each, threshold 0.35.
3. Per-Cluster Distillation LLM returns per-row keep / importance / distill / tags actions. Row-level rewrites only when the LLM is confident.
4. Hierarchical Wiki Cluster summaries + existing wiki + the evolution memo feed the LLM, which produces / updates pages bucketed into preferences / decisions / projects / domain / feedback. Slugs are stable, so re-running merges.
5. Evolution Memo Persists {rescored, dropped, wiki_created, wiki_updated, notes} for the last run; next run's Stage-4 prompt includes it so the LLM keeps learning the user's preferences across runs.

Run it manually:

loop-memory consolidate           # legacy single-pass
curl -X POST http://127.0.0.1:7767/api/admin/evolution/run   # 5-stage

Scoring v2: time × usage × feedback

The score of every memory is a weighted blend of four components, not just importance × recency:

Component Weight What it measures
importance 0.40 Original LLM/original importance in [0, 1]
recency 0.25 Time decay: ½^(age / half_life), default half_life 30 days
usage 0.25 log1p(recall_count)/log1p(100) × recency_of_last_recall
feedback 0.10 tanh((positive - negative) / 3) — sticky (no time decay)

The blend is normalised to [0, 1]. What this means in practice: recent + useful memories float up; old + unused memories sink; memories the user explicitly 👍 stay high; 👎 ones stay low even if they were popular once.

API endpoints to inspect the breakdown:

curl localhost:7767/api/memories/<id>/score              # 4 components
curl localhost:7767/api/pipeline/score-distribution     # 10-bin histogram
curl localhost:7767/api/pipeline/decay-stats            # age buckets × avg score
curl -XPOST 'localhost:7767/api/admin/bump-recall?ids=<id>'  # simulate LLM recall

Dashboard v2: real animation, real charts

The Dashboard tab has been rebuilt end-to-end:

  • 5 KPI cards with live sparklines (60-sample rolling history).
  • Active-stage card — shows the pipeline stage currently running, switching automatically as pipeline_runs update.
  • Animated data flow — particle dots travel left-to-right along the SVG path whenever a stage is running; nodes pulse with the active stage highlighted. Click any node to drill down.
  • Score distribution histogram — 10 bins of v2 score, hover for exact counts.
  • Time-decay chart — bars are count per age bucket, the line on top plots average score so you can see the decay curve.
  • Per-memory score breakdown — every drill-down item has a "why?" button that expands a 4-bar breakdown (importance / recency / usage / feedback).
  • ↻ bump button on each item — lets you mark a memory as "just consulted by the LLM" so its usage component goes up and it ranks higher next time.

Feedback loop

User signals close the loop:

  • 👍 on a drill-down item → positive++, importance += 0.05
  • 👎 → negative++, importance -= 0.05
  • Every recall() / search bumps recall_count on the returned rows so the next Stage-1 ranks them higher.

Auto-feedback into every LLM client (反哺)

Distilled knowledge is only useful if your LLM tools can actually read it. Loop Memory ships with three zero-dep commands that wire the memory store into Codex CLI, Claude Code and Hermes automatically:

Command What it does
loop-memory install-hooks Auto-detect ~/.codex, ~/.claude, ~/.hermes and write MCP + SessionStart hook configs in place. Idempotent — re-run any time.
loop-memory inject [query] Print a # Long-term memory context markdown block (distilled wiki + recent relevant memories) for a SessionStart hook.
loop-memory mcp Run the stdio MCP server with memory, graph, and cognitive tools (recall, remember, forget, feedback, remember_edge, subgraph, cognitive_sleep, audit, and wiki tools).

Quick setup on a fresh machine:

pip install loop-memory
loop-memory install-hooks       # writes ~/.codex/config.toml + ~/.claude/{mcp.json,settings.json} + ~/.hermes/mcp.json
# restart Codex / Claude Code / Hermes and the next session will:
#   1) auto-inject the distilled wiki as the first user message (SessionStart hook)
#   2) expose `recall` / `list_wiki` / `get_wiki` MCP tools so the model can pull more on demand

Manual smoke-test without restarting the client:

loop-memory inject                       # dumps the warm-start block to stdout
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}\n{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"wiki_summary"}}\n' \
  | loop-memory mcp                      # round-trips JSON-RPC over stdio

The MCP server speaks JSON-RPC 2.0 over newline-delimited stdin/stdout, uses no third-party deps, and is safe to launch per-client (Claude Code, Codex CLI, Hermes each spawn their own process). OpenClaw is detected but currently needs loop-memory hook --source openclaw --watch ~/.openclaw/sessions & to start its watcher.


Web UI

loop-memory serve opens a small local page at http://127.0.0.1:7767 with four primary views:

  • Timeline: searchable session history and scored memories from every client.
  • Dashboard: lifecycle, source health, distillation progress, weekly report, contradictions, audit data, and the end-to-end memory architecture.
  • Wiki: distilled, editable knowledge pages with export and Ask workflows.
  • Knowledge graph: an interactive globe built from distilled Wiki knowledge.

Top-right actions provide one-click import, re-scoring, AI consolidation, model configuration, scheduling, language switching, and light/dark themes.


Time-weighted scoring

Every memory carries a score ∈ [0, 1] recomputed from:

score = 0.35 · importance + 0.65 · recency
recency = ½ ^ (age / half_life)

half_life defaults to 30 days, configurable via consolidate(half_life_days=...). The UI shows the score as a percentage; use ?min_score=0.85 to see only high-relevance memories.


Programmatic use

from loop_memory import MemoryStore
from loop_memory.ingest.loader import get_loader
from loop_memory.ingest.pipeline import MemoryPipeline
from loop_memory.backends.embedding import HashingEmbedder
from loop_memory.jobs.consolidate import Consolidator

store = MemoryStore("~/.loop_memory/loop_memory.db")
pipeline = MemoryPipeline(store, embedder=HashingEmbedder(dim=128))

loader = get_loader("claude")
for path in loader.discover():
    session = loader.load_one(path)
    if session:
        pipeline.run(session)

# background-style consolidation
report = Consolidator(store, embedder=HashingEmbedder(dim=128)).run()
print(report)
# ConsolidateReport(rescored=15, gc_removed=0, merged=0, elapsed_ms=2.88)

Or just keep using the engine inside a Python process:

from loop_memory import LoopEngine, EchoLLM, HashingEmbedder
engine = LoopEngine(llm=EchoLLM(), embedder=HashingEmbedder(dim=128))
print(engine.turn("Hi! I'm Mia and I love matcha.").reply)

The four-stage loop

Even though v0.2 is built around local storage, the original Retrieve → Generate → Reflect → Store loop engine is still here:

Stage Default impl Replace with
RETRIEVE cosine + importance × recency any VectorStore (Chroma, FAISS…)
GENERATE any LLMClient OpenAI, Anthropic, local, …
REFLECT regex fact extractor an LLM-based reflector
STORE short-term + episodic + LTM persistent store via extras

Project layout

loop_memory/
  loop_memory/
    memory/types.py            # MemoryItem + 4 tiers
    backends/embedding.py      # BaseEmbedder, HashingEmbedder, IdentityEmbedder
    backends/vector_store.py   # VectorStore protocol + InMemory / Chroma
    backends/sentence_embedder.py  # optional sentence-transformers
    llm/base.py                # LLMClient protocol + EchoLLM + helpers
    llm/openai_adapter.py      # optional OpenAI client
    engine/loop.py             # the Retrieve → Generate → Reflect → Store loop
    engine/reflect.py          # reflection & summarization passes
    storage/sqlite_store.py    # persistent SQLite-backed MemoryStore
    ingest/loader.py           # CodexLoader, ClaudeLoader, HermesLoader
    ingest/pipeline.py         # session → MemoryStore
    jobs/consolidate.py        # background rescore + GC + dedupe
    serve/app.py               # FastAPI app for the local web UI
    serve/static/index.html    # the page
    serve/watcher.py           # filesystem watcher for auto-capture
    cli/main.py                # CLI entrypoint (chat / stats / ingest / consolidate / serve / hook)
    examples/demo.py           # runnable, zero-API-key demo
    py.typed
  tests/                       # 92 unit tests, zero deps
  docs/auto-capture.md         # launchd / systemd / cron recipes

Security & auth token

Loop Memory ships with a CSP deny-all + Origin-bound CSRF policy on every state-changing request, parameterised SQL throughout, and a Keychain-backed secret store (~/.loop_memory/secrets.json mode 0600 on Linux). The web UI also auto-sanitises any markdown it renders (DOMParser + tag allowlist + URL scheme scrubber — see loop_memory/serve/static/js/lib/sanitize.js).

Auth token (recommended on first run):

# Generates a 256-bit URL-safe token, stored hashed in the settings table.
# The server stays authenticated forever — there is no "disable" path.
curl -X POST http://127.0.0.1:7767/api/admin/auth/token | tee token.txt

# Rotate later (use this when you suspect the token has leaked):
curl -X DELETE http://127.0.0.1:7767/api/admin/auth/token \
     -H "Authorization: Bearer $(cat token.txt)" | tee token.txt

Pass the token as Authorization: Bearer … header on every admin call. The web UI stores it in localStorage under loop_auth_token and attaches it automatically; non-browser clients (curl, MCP, SDK) must opt in by passing it explicitly.

When to set a token:

  • ✅ Always, even on loopback. The default of "no token" exists only as a TOFU bootstrap path; an unconfigured server trusts any browser on localhost to mutate state (CSRF still rejects cross-origin POSTs from a remote page, but a malicious local app can still call the API).
  • ✅ Especially if you ever bind to anything other than 127.0.0.1 (loop-memory serve --host 0.0.0.0 now prints a security warning).
  • ❌ Never DELETE /api/admin/auth/token to "disable" auth — it rotates to a fresh token instead (the audit found that fully disabling auth was the easiest way back to the no-token state).

Threat model notes:

  • install-hooks runs Python that touches ~/.* — it's gated by the bearer token like every other POST /api/admin/* route.
  • The <private>...</private> span stripping in the privacy layer keeps user-marked secrets out of long-term storage; the regex redaction layer then catches API keys / tokens / private keys / JWTs / generic high-entropy blobs before they reach SQLite.
  • The bundled weekly-report Markdown is rendered through the sanitizeHtml sanitizer (see tests-js/test_sanitize.test.mjs for the bypass coverage).

Wiki scope auto-classification

New wiki pages use a local, deterministic scope evaluator by default:

  • Universal security guidance (for example, rotating API keys, never pasting secrets, or using parameterised SQL) is automatically promoted to scope="global" when the classifier has enough security and cross-client signals.
  • Preferences, personal facts, project incidents, and other knowledge default to the client that supplied the evidence (codex, claude, hermes, or openclaw). Pages with no source metadata use the privacy-preserving codex fallback rather than being shared with every client.
  • An explicit scope always wins. Use scope="auto" (or omit it) to ask the evaluator for a recommendation. Existing pages are not migrated, and an update that omits scope preserves its current manual scope.
  • Every decision is stored in the page's auto_classification audit object; inspect it with GET /api/wiki/{page_id}/classification-history or preview a decision with POST /api/wiki/classify.

The behavior is controlled by GET/PUT /api/admin/wiki/scope: {"enabled": true, "mode": "pattern"} is the default. mode="off" keeps new pages client-scoped without automatic global promotion. The classifier is local and makes no model or network request on a wiki write.


Run the tests

# Python suite (memory + SDK + serve + CLI)
python -m pytest -q
python -m unittest discover -s tests -v

# Frontend sanitizer bypass suite (jsdom)
npm test

Using distilled knowledge in your clients

After running loop-memory consolidate (or letting the scheduler do it), your memories get distilled into durable wiki pages. Three ways to use them in Claude / Codex / Hermes / OpenClaw:

1. Quick paste — loop-memory ask

Works from any terminal, no server required:

loop-memory ask "what does the user prefer for X?"

Prints a paste-ready context block to stdout. Put it as the first message of a new session in any LLM client.

2. Whole wiki export

# Legacy single-file markdown export (kept for existing scripts)
loop-memory export
loop-memory export --out ~/Notes/user.md --q "preferences"

# v7 portable bundle: MEMORY.md + pages + memories + graph + metadata
loop-memory export ~/Notes/loop-memory-bundle
loop-memory export-bundle ~/Notes/loop-memory-bundle

Or in the UI: open the Wiki tab → click ⇩ Export. A markdown file downloads; paste it into your daily journal or as a system prompt.

3. Per-page "Copy as context" in the UI

Each wiki card has a button that copies a single distilled page formatted as background context — ready to paste as the system prompt of a fresh Codex / Claude / Hermes session.

4. Auto-context (MCP-aware clients)

If you ran loop-memory install-hooks, Codex / Claude Code / Hermes will automatically pull relevant memories via the MCP server. OpenClaw does not support MCP — use loop-memory ask instead.

Manual trigger

Click the ⚡ Run now button (top-right) or run:

loop-memory consolidate-now    # ask the running server to start a pass right now

This uses your configured model, batch size, and provider — same as the scheduled runs.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

loop_memory-0.4.0.tar.gz (337.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

loop_memory-0.4.0-py3-none-any.whl (298.1 kB view details)

Uploaded Python 3

File details

Details for the file loop_memory-0.4.0.tar.gz.

File metadata

  • Download URL: loop_memory-0.4.0.tar.gz
  • Upload date:
  • Size: 337.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for loop_memory-0.4.0.tar.gz
Algorithm Hash digest
SHA256 65e2ab0fcfdad3a8f60424c98bc93245cde4294d2e38075579bc40572e6e7c38
MD5 5ed2903f53e71d1434f568db7b4c23e7
BLAKE2b-256 b138ab718bedfc665957cd768589c354cae94efd91ebd5d820fd177a7baf805c

See more details on using hashes here.

Provenance

The following attestation bundles were made for loop_memory-0.4.0.tar.gz:

Publisher: publish.yml on smartfind/loop-memory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loop_memory-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: loop_memory-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 298.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for loop_memory-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 890178bf5ab2fe840b196f5f81ed66521ef65b02262aebd13aca9759f4e83d2e
MD5 bc627e73e240eaa3dd18a0193e7a1f90
BLAKE2b-256 67736e4e447c52540944f4e96ba9665c7551a75823e61ee79f126038870d60a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for loop_memory-0.4.0-py3-none-any.whl:

Publisher: publish.yml on smartfind/loop-memory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page