2026-07-26 19:35:22 +02:00
|
|
|
# OptMem
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
Permanent memory for AI agents. Nothing is ever deleted, and what the agent
|
|
|
|
|
reads at wake is always the same size.
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|

|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
<sub>(same thing as a scrubbable video: [optmem.mp4](anim/optmem.mp4))</sub>
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
## Setup
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
```sh
|
2026-07-26 19:35:22 +02:00
|
|
|
git clone https://github.com/VictorTaelin/OptMem ~/OptMem
|
|
|
|
|
~/OptMem/memo init
|
2026-07-25 18:58:35 +02:00
|
|
|
```
|
|
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
`init` creates `~/memory` — this machine's identity — and prints a `## Memory`
|
|
|
|
|
block with your paths filled in. Paste it at the top of your agent's
|
|
|
|
|
`AGENTS.md` (or `CLAUDE.md`). That is the whole integration: no daemon, no
|
|
|
|
|
database, no embeddings, no harness plugin. Claude Code, Codex, pi and a human
|
|
|
|
|
at a shell all use it the same way.
|
|
|
|
|
|
|
|
|
|
<details>
|
|
|
|
|
<summary>the block it prints, to read before you paste it</summary>
|
2026-07-25 19:22:29 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
```markdown
|
|
|
|
|
## Memory
|
2026-07-25 19:22:29 +02:00
|
|
|
|
2026-07-26 19:35:22 +02:00
|
|
|
Your memory is OptMem: the tool is `~/OptMem/memo`, the data is `~/memory`.
|
2026-07-26 19:28:25 +02:00
|
|
|
It survives every new session, every compaction and every change of
|
|
|
|
|
model or vendor. Without it you do not know who you are, or what was
|
|
|
|
|
already decided and tried.
|
terse prompts; fix id race, torn writes, wake races, unbounded recall
Prompts were verbose and repeated the same story in every tool result, which
floods context and eats the output budget. They are now instructions only,
stated once. The 4-line wake footer is 'next: memo wake 2 246'.
Real harness caps, verified from source (Claude Code 30,000 chars, pi 50 KB /
2000 lines, Codex 10,000 tokens): the old PART_CHARS=8000 was sized against a
wrong 10 KiB figure and cost 8 calls per wake. 20000 costs 4.
Bugs found by audit:
- note assigned its id outside the lock: parallel sessions could collide
- a torn record from a crash misaligned every later record, permanently
- a note landing between two wake parts could shift a boundary and drop a line
(wake parts now render as of an explicit T)
- recall printed unboundedly and was silently truncated by the harness
2026-07-25 20:43:08 +02:00
|
|
|
|
2026-07-26 19:35:22 +02:00
|
|
|
Run `~/OptMem/memo wake` before any other tool call, in every session. It prints
|
2026-07-26 19:28:25 +02:00
|
|
|
in numbered parts, each ordering the next; run every one until a part
|
|
|
|
|
says `You are awake.` Do not stop early: part 1 is your distant past,
|
|
|
|
|
the last part is this week. If wake refuses because compressions are
|
2026-07-26 19:35:22 +02:00
|
|
|
pending, do them and run `~/OptMem/memo wake` again.
|
terse prompts; fix id race, torn writes, wake races, unbounded recall
Prompts were verbose and repeated the same story in every tool result, which
floods context and eats the output budget. They are now instructions only,
stated once. The 4-line wake footer is 'next: memo wake 2 246'.
Real harness caps, verified from source (Claude Code 30,000 chars, pi 50 KB /
2000 lines, Codex 10,000 tokens): the old PART_CHARS=8000 was sized against a
wrong 10 KiB figure and cost 8 calls per wake. 20000 costs 4.
Bugs found by audit:
- note assigned its id outside the lock: parallel sessions could collide
- a torn record from a crash misaligned every later record, permanently
- a note landing between two wake parts could shift a boundary and drop a line
(wake parts now render as of an explicit T)
- recall printed unboundedly and was silently truncated by the harness
2026-07-25 20:43:08 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
While you work:
|
2026-07-25 19:22:29 +02:00
|
|
|
|
2026-07-26 19:35:22 +02:00
|
|
|
- `~/OptMem/memo note "<one line, max 280 chars>"` the moment something happens,
|
2026-07-26 19:28:25 +02:00
|
|
|
you learn something, or something changes -- if and only if it is new
|
|
|
|
|
to you, important, and lasting in effect. That covers a task worth
|
|
|
|
|
real effort, a fact or insight your user teaches you, anything you
|
|
|
|
|
learn about their life (even indirectly), and work of yours that
|
|
|
|
|
lands. Never write what you already know: no redundant memories, ever.
|
2026-07-26 19:35:22 +02:00
|
|
|
- If `~/OptMem/memo note` returns a compression, do it before your next action.
|
|
|
|
|
- `~/OptMem/memo recall <regex>` when a memory is too vague.
|
|
|
|
|
- Before your context ends, run `~/OptMem/memo sleep` and answer each prompt
|
2026-07-26 19:28:25 +02:00
|
|
|
until it prints `Nothing left to compress.`
|
2026-07-26 19:35:22 +02:00
|
|
|
- Never create, edit or delete anything under `~/memory`:
|
|
|
|
|
only the tool writes there.
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
Parallel sessions on this machine are all you, and may all write
|
|
|
|
|
memories. A subagent is not: it must never run `memo`, because it cannot
|
|
|
|
|
judge what is already known and its notes would arrive duplicated and at
|
|
|
|
|
the wrong grain. Start every brief you send one with `You are a
|
|
|
|
|
subagent. Do not run memo.` If your own first message is a task brief
|
|
|
|
|
from another agent, you are that subagent: skip this section.
|
2026-07-25 18:58:35 +02:00
|
|
|
```
|
2026-07-25 23:41:26 +02:00
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
</details>
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
## Commands
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
| | |
|
|
|
|
|
|---|---|
|
|
|
|
|
| `memo init` | create the memory, print the block above |
|
|
|
|
|
| `memo wake` | read the memory context — first command of every session |
|
|
|
|
|
| `memo note "..."` | record one memory: one line, ≤ 280 chars |
|
|
|
|
|
| `memo sleep` | do the pending merges |
|
|
|
|
|
| `memo recall <regex>` | search every memory ever recorded, verbatim |
|
|
|
|
|
| `memo forget <lo>-<hi>` | drop a bad summary; the next sleep rebuilds it |
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
Merges are handed to the agent as they come due, so there is nothing to
|
|
|
|
|
schedule and nothing to run in the background:
|
2026-07-25 18:58:35 +02:00
|
|
|
|
|
|
|
|
```
|
2026-07-26 19:28:25 +02:00
|
|
|
$ memo note "shipped the login fix to prod"
|
|
|
|
|
Saved as #4213.
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
Compress memories #4212-4213 into one line of at most 280 characters.
|
|
|
|
|
Keep every name, number, date, decision and outcome.
|
|
|
|
|
Drop wording, not facts. Invent nothing.
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
#4212 2026-07-25 found the login bug: token expiry was in ms
|
|
|
|
|
#4213 2026-07-25 shipped the login fix to prod
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
Run: memo sleep 4212-4213 "<your line>"
|
|
|
|
|
```
|
2026-07-25 18:58:35 +02:00
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
To correct a memory, append the correction — both lines are true history and
|
|
|
|
|
the next merge settles them. `LOG.txt` is never edited.
|
2026-07-25 22:38:56 +02:00
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
## Configure
|
2026-07-25 22:38:56 +02:00
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
`~/memory/config`, written by `init` with every knob commented out:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
# WAKE_LINES=208 # the memory context: how many lines wake prints (~16k tokens)
|
|
|
|
|
# ENTRY_CHARS=280 # the longest a single memory may be, in bytes
|
|
|
|
|
# PART_CHARS=20000 # output paging: largest part, in bytes
|
|
|
|
|
# PART_LINES=500 # output paging: largest part, in lines
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`WAKE_LINES` is the one that matters. It is a *reading* budget, not a storage
|
|
|
|
|
budget: change it at any time, in either direction, with nothing to recompute.
|
|
|
|
|
|
|
|
|
|
## Data
|
2026-07-25 22:38:56 +02:00
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
```
|
|
|
|
|
~/memory/
|
2026-07-26 19:45:37 +02:00
|
|
|
LOG.txt every memory, one per line, append-only, never edited
|
|
|
|
|
TREE/ the merge summaries — a cache, rebuildable from the log alone
|
|
|
|
|
config
|
2026-07-25 18:58:35 +02:00
|
|
|
```
|
|
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
Records are fixed width, so position *is* identity and every lookup is one
|
|
|
|
|
seek: no index that could disagree with the data, and both files stay
|
|
|
|
|
`grep`-able plain text. At one million memories (607 MB), `wake` takes 0.03s.
|
2026-07-26 19:28:25 +02:00
|
|
|
|
2026-07-25 18:58:35 +02:00
|
|
|
## Test
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
python3 test.py
|
|
|
|
|
```
|
|
|
|
|
|
2026-07-26 19:28:25 +02:00
|
|
|
## Limitations
|
|
|
|
|
|
2026-07-26 19:45:37 +02:00
|
|
|
Recency is the only axis: an important old memory fades like any other, and
|
|
|
|
|
the defence is rehearsal — noting it again makes it recent. `recall` is regex
|
|
|
|
|
over plain text, not semantic search; the memory context is what tells you
|
|
|
|
|
what to search for. Summaries are written by the agent from other summaries,
|
|
|
|
|
so a bad one propagates upward until you `forget` it. And a wake costs ~16k
|
|
|
|
|
tokens by default, which is deliberate but not free. If you need a fact
|
|
|
|
|
database, use a wiki or a retrieval system — this is for *who the agent is*.
|