Skip to main content
Glama
README.md
<div align="center">

# 🧬 Leptin

### Keep your coding agent's memory *correct* when decisions change.

**You switch `pnpm`β†’`bun` β€” and weeks later your agent is *still acting on the version you abandoned*. Leptin keeps long-term memory correct when decisions change: the current decision wins, lessons stick, and nothing is forgotten without proving recall survived.**

Not a store, not a compressor β€” a local control loop that runs on your agent's hooks, *alongside* whatever you use. (Today it's its own local SQLite store; a "governor mode" over your existing Mem0/pgvector is on the [roadmap](#roadmap), not shipped.)

[![CI](https://github.com/lionellau/leptin/actions/workflows/ci.yml/badge.svg)](https://github.com/lionellau/leptin/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/leptin-hlp?color=3fb950)](https://pypi.org/project/leptin-hlp/)
[![python](https://img.shields.io/badge/python-3.10%2B-58a6ff)](#install)
[![core deps](https://img.shields.io/badge/core%20deps-zero-58a6ff)](#design)
[![tests](https://img.shields.io/badge/tests-164%20passing-3fb950)](#testing)
[![license](https://img.shields.io/badge/license-MIT-8b98a9)](LICENSE)

[See it in 30s](#see-it-in-30-seconds) Β· [Don't I already have this?](#wait--dont-i-already-have-this) Β· [Quickstart](#quickstart) Β· [How it works](#how-it-works) Β· [Who it's for](#who-leptin-is-for) Β· [Security](#security)

<img src="assets/demo.svg" alt="Leptin: lessons injected at session start, recall under a token budget, verified forgetting" width="760"/>

</div>

---

Persistent memory is supposed to make your agent smarter over time. Ungoverned, it does the opposite: it piles up **duplicates, stale facts, and contradictions**, so the agent goes *confidently wrong* β€” recommending the framework you dropped, the region you migrated off, the auth scheme you replaced β€” and repeats mistakes you already corrected. The usual fix, blind pruning, just trades one problem for another: you can't see what was dropped or get it back.

**Leptin is the loop that keeps memory _correct_ β€” not another place to put it.** It plugs into the hooks your agent already fires and, every session, resolves contradictions so the *current* truth wins, keeps your lessons-learned permanent, retires what's cold, and never forgets a thing without first **proving recall didn't regress**.

## See it in 30 seconds

```bash
pip install "git+https://github.com/lionellau/leptin"   # PyPI: pip install leptin-hlp (publishing soon)
leptin demo                                              # runs in-memory; touches nothing on disk
```
```text
  β‘  The team is on pnpm β€” the agent recalls it correctly.
  β‘‘ You switch to bun:   remember("We use bun…")  β†’  superseded
  β‘’ Next session, the agent asks again:
       a naive store β†’  ['…bun', '…pnpm']     ← still serving the abandoned pnpm
       Leptin        β†’  ['…bun']              ← only the current truth
```
That's the whole product on one screen: **it keeps the current decision authoritative.** Want the measured version? `leptin bench` β†’ a naive store serves the *outdated* fact **100%** of the time after a reversal; Leptin **0%**, at **0% recall loss**.

## "Wait β€” don't I already have this?"

Probably not the part that bites. Leptin sits on a **different axis** from a store or a compressor, and runs *alongside* them β€” not instead:

| You might assume… | The reality |
|---|---|
| "Isn't this just **dedup**?" | Dedup removes *copies*. Leptin decides which of two *conflicting* facts is current (`pnpm` vs `bun`) and retires the old one β€” reversibly, auditably. |
| "Doesn't my **context-compressor** (e.g. Headroom) cover it?" | Different job. A compressor shrinks what the agent reads *this turn*; Leptin keeps the *stored* facts consistent *over weeks*. They barely overlap β€” run both. |
| "Isn't this just another **store** (e.g. Mem0)?" | A store retrieves by similarity; it doesn't adjudicate which fact is *currently true* or prove a prune didn't cost you something. Leptin is that correctness loop β€” today as its own local store; a governor mode over *your* store is roadmap. |
| "Does it need an **API key / a service**?" | No. Local-first, zero core deps; offline for lexical/antonym/numeric reversals, hosted embeddings for semantic ones. Your DB stays a single local file. |

> The token footprint also drops ~65%, but we report that *honestly*: most of it is budget-packing (the axis a compressor also helps with); the **correctness loop is the part nothing else does**. Don't take our word β€” reproduce both numbers with `leptin bench`, and see the [honest offline limits](#design).

---

## Why I built this

> *(The origin story β€” the "why" behind the code.)*

I run coding agents all day on long-lived projects, with a persistent-memory MCP server so the agent stops re-learning my stack every session. It worked β€” until the store grew. Then `recall` started handing back a noisy pile of half-relevant, sometimes *contradictory* memories: a decision we'd reversed, a convention we'd dropped. The agent acted on the stale one. And it kept making the same mistakes across sessions because nothing made "we learned X the hard way" stick.

The ecosystem is good and moving fast β€” plenty of tools store, compress, and dedup memory well. What I couldn't find was the piece that keeps memory **correct over time**: resolve contradictions so the *current* truth wins, retire cold facts safely, keep lessons permanent, and prove a prune never cost me a fact I'd query later. So I built it, for the way I work β€” a solo dev on a local store I'd rather not migrate off of β€” and published it in case your setup looks like mine.

*β€” [@lionellau](https://github.com/lionellau). PRs, issues, and "this stopped my agent repeating X" stories welcome.*

---

## How it works

Leptin is a loop, not a tool the model calls. Each turn of your agent's harness fires it: **session-start** injects lessons + current truth, **post-tool** captures mistakes the moment they happen, **pre-compact / stop** runs the guarded prune. The loop spans **three layers** β€” and the model-facing one is deliberately tiny.

<div align="center">
<img src="assets/loop.svg" alt="The Leptin control loop: inject β†’ act β†’ capture β†’ reconcile β†’ verify β†’ forget" width="760"/>
</div>

> Why the loop, not just an MCP tool? An MCP server is *pull* β€” it works only when the model remembers to call it. Memory hygiene can't depend on that. Leptin runs on the harness's *push* points (hooks) so the discipline happens every loop whether or not the model asks. The MCP surface is just the two tools (`recall`/`remember`) the model genuinely needs in-band. See [docs/loops.md](docs/loops.md).

<div align="center">
<img src="assets/architecture.svg" alt="Leptin architecture" width="900"/>
</div>

1. **Discipline layer (hooks + CLI/daemon) β€” not model-callable.** Decay, dedup, supersede, the recall guardrail, and self-tuning run automatically through lifecycle hooks and a background CLI. They are *not* exposed as tools the model has to remember to call (tool schemas are a per-request token tax β€” eight of them is a standing cost for work the model should never invoke).
2. **Query layer (MCP) β€” lean.** Only **`recall`** and **`remember`** are exposed to the model by default. (Need the rest as tools? `LEPTIN_MCP_TOOLS=all`.)
3. **Human layer (CLI + dashboard).** Receipts (tokens & $ saved), the evolution/guardrail audit, `leptin doctor`, tuning.

The defining mechanisms:

- **Memory typing.** Every memory has a type with its own decay: `fact` (normal), `procedural` (slow), `task` (fades with the ticket), and **`lesson` β€” never decays.**
- **Lessons-learned, auto-injected.** Store an anti-pattern once (`leptin lesson "..."`); it's re-injected at **every session start** via a hook, so the agent stops repeating it. No tool call required.
- **Contradiction resolution (supersede), graded.** When a newer fact *confidently* contradicts an older one (a negation flip, an antonym, a single-slot value swap like `pnpm`β†’`bun`, or a numeric change on the same statement), the newer wins; the old is kept, marked superseded with a **reversible window**, and stays auditable (`leptin superseded`). Recall returns the *current* truth, not both. When the conflict is **uncertain** (a deeper paraphrase), Leptin does *not* bury either β€” it flags them for review (`leptin conflicts`) rather than silently coexisting or wrongly deleting a true fact. *(Offline this detector is lexical; semantic reversals need hosted embeddings.)*
- **Verified, transactional forgetting (the guardrail).** Before any prune commits, Leptin re-runs a probe set against the post-prune store *inside a transaction*; if recall would regress, the whole prune **rolls back**. Nothing is silently lost, and everything is reversible.
- **Provenance anchoring.** Anchor a memory to its source (`linear:ABC-123`, `spec:auth.md#flow`, `commit:sha`). When the source changes, `leptin stale <ref>` flags it β€” stale memories are down-weighted, not blindly trusted ("a fact is confidently wrong the moment its source changes").
- **Auto mistake-capture (the post-tool loop).** When a tool call fails (a bad command, a broken build), the post-tool hook distills it into a never-decaying lesson automatically β€” no one has to remember to write it down. Next session it's re-injected, so the agent doesn't walk into the same wall twice.
- **Recall-usefulness flywheel.** Leptin separates *recurrence* (needed again in a later session β€” a weak ranking tiebreaker) from *usefulness* (an explicit "it helped" via `leptin feedback`, or the `record_feedback` MCP tool). Useful memories are reinforced; a single **harmful** mark only down-weights (reversible by a later "useful") and takes two before it's flagged β€” so one noisy signal can't bury a fact. Cold, never-useful memories simply age out by decay; a strong, genuinely-used one is never mistaken for noise. The store gets *more* relevant with use, not just bigger.
- **Memory-health score.** `leptin health` grades the store 0–100 (A–D) on stale rate, noise rate, and harmful hits, and flags drift before recall quality rots β€” an at-a-glance read on whether the loop is keeping memory clean.
- **Budgeted, packed recall + savings ledger.** Recall is capped and packed for relevance; every op logs tokens (and $) saved vs. a naive top-k dump.

---

## Quickstart

One command does it all β€” create the store, wire the hooks + MCP into your host config (backed up, idempotent), and verify it:

```bash
pip install "git+https://github.com/lionellau/leptin"   # PyPI: pip install leptin-hlp (soon)
leptin setup claude-code        # or: codex
leptin doctor --json            # confirm: "Host wiring" β†’ "ok"
```

That's it β€” restart the client and your agent has persistent, self-correcting memory. No account, no API key, nothing leaves your machine (a single SQLite file under `~/.leptin/`). Prefer to paste the config yourself? `leptin connect claude-code` prints it instead of writing.

### Let your *agent* set it up

Because the whole install is one verifiable command, you don't have to do it β€” your agent can. Tell it:

> *"You keep forgetting decisions we've changed. Give yourself persistent memory β€” read AGENTS.md in github.com/lionellau/leptin and set it up."*

It runs `pip install … && leptin setup claude-code && leptin doctor --json`, checks the result, and asks you to restart. [AGENTS.md](AGENTS.md) is written for exactly this.

### Then teach it a lesson, and watch the loop keep it correct
```bash
leptin lesson "Never run DB migrations on a Friday deploy."
# next session, that lesson is injected automatically β€” the agent won't repeat it
leptin remember "Auth uses JWT in cookies." --subject auth --source-ref spec:auth.md#tokens
leptin stale spec:auth.md#tokens     # when the spec changes, the memory is flagged
leptin feedback <id> --harmful       # close the loop: tell Leptin a recall misled the agent
leptin health                        # 0–100 score: is the loop keeping memory clean?
leptin dashboard                     # glass-box audit: what was superseded / forgotten / merged, and why
```

<div align="center"><img src="assets/dashboard.png" alt="Leptin dashboard" width="760"/></div>

---

## Who Leptin is for

**The beachhead: a developer (or small team) running a coding agent on one long-lived codebase for weeks.** That's where memory accumulates into hundreds of facts and where decisions *get reversed* β€” you switch `pnpm`β†’`bun`, move regions, change the auth scheme β€” and an ungoverned store keeps handing the agent the version you already abandoned. Leptin keeps the *current* decision authoritative and proves it never forgot what you still need. If that's you, this is built for you.

It also helps wherever memory **accumulates and gets queried a lot**:

- **Long-horizon research** β€” findings/sources pile up across sessions with updates and contradictions; Leptin keeps the knowledge base current and auditable.
- **Autonomous / looping / scheduled agents** β€” they write and recall constantly; verified pruning + never-decaying lessons keep memory from rotting or running away.
- **Small teams (2–10) working from specs + Linear tickets** β€” provenance anchoring ties memory to the ticket/spec so stale specs stop misleading the agent.

**You probably don't need Leptin if:** you do one-off Q&A / daily ops with no growing memory; your work fits a single session; or your store stays tiny.

---

## Where Leptin fits (vs / alongside other tools)

This isn't a teardown β€” the memory space is strong and these are good tools. Leptin sits on a **different axis**: most tools answer *"what do I store and how small can I make it?"* Leptin answers *"is what I'm storing still correct, and is it actually helping?"* β€” it's a control loop, not a store or a compressor, and it runs **alongside** them.

- **vs storage layers (Mem0, vector stores):** they store and retrieve by similarity β€” excellent at that. They don't adjudicate which conflicting fact is *currently true*, verify that a prune didn't cost you a fact, or learn which memories actually earned their place. Leptin is a **self-contained local store with that correctness loop built in**, and it runs *alongside* your existing one. *(A "governor mode" that drives the loop over an external store like Mem0/pgvector is on the roadmap β€” not shipped yet, so today Leptin governs its own SQLite.)*
- **vs context-compression layers (e.g. Headroom):** they shrink what the agent *reads each turn* (great at that) and keep everything. Leptin works on the other end β€” the long-term store's *correctness over time*: superseding stale truth, keeping lessons permanent, pruning proven-noise under a recall guardrail. Compress the stream **and** govern the store; they barely overlap.

If you need a managed, hosted memory platform, use one. Leptin is the small, local, auditable loop that keeps long-term memory **correct and useful** β€” and runs alongside whatever else you use.

---

## Design

- **Zero core dependencies** β€” engine, MCP server, hooks, guardrail, dashboard, benchmark, self-tuner all run on the Python stdlib. `pip install` is instant.
- **Offline by default, hosted by upgrade** β€” deterministic hashing embeddings + heuristic merge need no API key; `leptin-hlp[hosted]` adds OpenAI/Voyage embeddings + Claude/GPT merging (with retry + caching).
- **Glass box, reversible** β€” every merge/decay/forget/supersede/tune is logged with a reason; nothing is hard-deleted within the retention window; schema is versioned and migrates in place.

> **Honest limits (offline default).** The bundled hashing embedder is *lexical* β€” it matches near-identical text, not deep paraphrases. So offline, both **dedup** and **contradiction-supersede** fire on near-lexical / antonym / numeric reversals (`pnpm`β†’`bun`, `14 days`β†’`30 days`), and a semantic reversal (`"JWT in cookies"` β†’ `"session tokens in headers"`) is **flagged for review**, not auto-resolved. Configure hosted embeddings (`leptin-hlp[hosted]`) for semantic dedup/supersede; verify the detector yourself with `leptin bench --eval-contradiction` (bundled set: precision 1.0, recall ~0.87, zero true facts buried). Defaults always err toward **keeping** data.

---

## Self-tuning (optional)

Leptin can tune its own policy on your data: it replays under candidate configs and commits a change only if held-out evals prove a net win, else reverts β€” offline, **zero LLM calls**, fully reversible (`leptin tune`). The guardrail's safety knobs are locked and never tuned.

---

## Security

Leptin is **local-first**: the MCP server speaks stdio (no network listener); the dashboard binds to 127.0.0.1 with a Host-header guard; memory content is data, never executed; hosted calls read keys from env, never stored; a user's memory DB is git-ignored (`*.db`/`*.sqlite`). Report vulnerabilities privately β€” see [SECURITY.md](SECURITY.md).

---

## Reproducible benchmark

```bash
leptin bench                      # correctness first, then footprint (offline, deterministic)
leptin bench --eval-contradiction # precision/recall/F1 of the contradiction detector
```
Two claims, both reproducible offline:

- **Correctness (the wedge):** after a decision is reversed, a naive store serves the **outdated** fact **100%** of the time; Leptin serves it **0%**, with **0% recall loss**.
- **Footprint:** ~**65% fewer recall tokens** than an unbudgeted top-k dump β€” but the bench *splits* this honestly into **~57% packing** (budget + relevance floor β€” the axis a context-compressor also helps with) **+ ~19% governance** (dedup/supersede/decay β€” the correctness loop). The packing number is not the wedge; the governance + correctness numbers are.

Run it on real [LoCoMo](https://snap-research.github.io/locomo/) with `--dataset locomo.json --embedding-model text-embedding-3-small` (semantic recall needs hosted embeddings; the bundled corpus is lexical).

---

## Testing

```bash
uv venv && uv pip install -e ".[dev]" && pytest
```
155 tests cover the graded contradiction detector (a labeled precision/recall set β€” zero true facts buried), the non-circular guardrail (verify the *fact*, not just the id), memory typing + never-decaying lessons, budgeted session-start injection, reversible + discoverable supersede, the reframed flywheel (recurrence β‰  usefulness; graded harmful feedback), the candidate-lesson policy, embedder provenance + recovery, the memory-health/conflicts surfaces, provenance/staleness, the hook entrypoint, the lean vs `all` MCP surface (incl. a real `leptin serve` subprocess), schema migrations, the savings ledger, self-tuning (deterministic split), the dashboard HTTP layer, hosted integration + retry/degradation, and the correctness + footprint benchmarks. CI runs the suite, the benchmark, a clean wheel install, and the TS build on Python 3.10–3.13.

---

## CLI

```bash
leptin demo                        # 60-second before/after: a reversed decision (no setup)
leptin connect claude-code|codex   # wire hooks + lean MCP
leptin serve   --db PATH           # MCP server (stdio)
leptin hook    <event>             # lifecycle-hook entrypoint (used by connect)
leptin lesson  "..."               # store a never-decaying lesson
leptin remember "..." [--type fact|procedural|task|lesson] [--source-ref REF]
leptin stale   <source-ref>        # flag memories whose source changed
leptin feedback <id>... [--harmful]  # close the loop: reinforce or down-weight a memory
leptin conflicts                   # possible contradictions flagged for review
leptin superseded                  # what got replaced, by what, and why (reversible)
leptin health                      # 0–100 memory-health score (stale / noise / harmful / conflicts)
leptin reembed                     # re-vectorise after a hosted→local fallback
leptin recall  "..." [--budget N]
leptin compact [--dry-run]    leptin tune [--dry-run|--rollback|--history]
leptin doctor   Β·   leptin dashboard   Β·   leptin report   Β·   leptin bench
```

---

## Roadmap

**Shipped:** the full control loop on lifecycle hooks (Claude Code + Codex) Β· **graded contradiction-supersede** (confident β†’ auto-resolve, uncertain β†’ flag for review) with a labeled precision/recall eval Β· **non-circular recall guardrail** (verifies the fact, not just the id) Β· reversible + discoverable supersede Β· memory typing + never-decaying lessons Β· **bounded, demotable lessons** + gated auto mistake-capture Β· the **reframed flywheel** (recurrence β‰  usefulness; graded harmful feedback; `record_feedback` MCP tool) Β· **memory-health + conflicts** surfaces Β· embedder provenance + `reembed` recovery Β· provenance anchoring + staleness Β· the **correctness + footprint benchmark** (packing vs governance split) Β· lean MCP surface Β· self-tuning (deterministic) Β· local dashboard Β· `leptin doctor` Β· schema migrations Β· 155 tests + CI.

**Next (big bets):** a **governor mode** that runs the loop over an *external* store (Mem0 / pgvector) so you keep your backend β€” today Leptin governs its own SQLite Β· an **opt-in semantic offline path** (small local NLI/embedder) so paraphrase reversals resolve without a hosted key Β· a **`sqlite-vec` ANN fast path** + disk-backed scale-sweep bench for 10k–100k-row stores Β· more source adapters (Linear / GitHub / Jira) and host installers (Cursor, Gemini CLI).

---

## Contributing

PRs welcome β€” especially **source adapters** and **host installers**. See [CONTRIBUTING.md](CONTRIBUTING.md) and the [Code of Conduct](CODE_OF_CONDUCT.md). Keep the core dependency-free, add a test, and don't weaken the guardrail (its safety knobs stay locked).

## License

MIT β€” see [LICENSE](LICENSE).

<div align="center"><br/><i>If Leptin kept your agent's memory honest, a ⭐ helps others find it.</i></div>