# Agent memory that forgets and distorts on purpose

> Source: <https://github.com/jbsalles/Selmem>
> Published: 2026-09-07 07:50:48+00:00

Selective reconstructive memory for an LLM entity.

An LLM maps context to the next token. A stack of unmodified facts maximises coverage, not deviation: more evidence, same average path. SelMem sculpts a particular past — forgotten, gilded, anchored — so two instances can diverge. The aim is a non-average, path-dependent continuation, not a taller log. Selection, reconstruction, sleep, identity.

**Manifest:** [WHITEPAPER.md](/jbsalles/Selmem/blob/main/WHITEPAPER.md)
**Knobs:** [PARAMETERS.md](/jbsalles/Selmem/blob/main/PARAMETERS.md) — exploratory, not fitted.

Zero crates. Rust 1.75. SQLite via system `libsqlite3`.

```
src/
  core/       model, profile, store
  encode/     intake, scoring, embed, affect, identity
  recall/     retrieve, narrator, http
  dream/      night, drift, singularite
  persist/    file, sqlite
  net/        api, httpx, ui
  engine.rs   the loop
  bin/        selmemd, selmem-chat
experience → interpret → identity paint → gate
        ↓                         ↑
   lived book + sealed archive    |
        ↓                         |
 remember / speak (meaning can move)
        ↓
      sleep
 weather · rewrite · merge · extinguish
 motif → belief → trait → who_am_i
        ↓
   next experience is already colored
```

The model never sees the archive. Only gist, core, schema, affect, fidelity, mood, living axioms.

Claire and Silas are not characters. They are two sensitivities (`tender` / `austere`) on the same corpus. After nights they are not the same past.

Zero Cargo crates. Persistence is a vault, not the memory: the organ lives in RAM (`MemoryStore`); on `save` it is dumped, on `open` it is reloaded. The model never talks to the vault.

Two backends, same `Snapshot` (`profile`, `mood`, `store`):

| Path | Backend | 
|---|---|
| `.db` /`.sqlite` /`.sqlite3` | system `libsqlite3` (prepared statements,`BEGIN IMMEDIATE` ) | 
| anything else | flat `SELMEM1` file | 

IDs are `{prefix}_{pid}_{n}`. The counter is raised on load for both backends. Dropped events leave no archive. Orphans are pruned on sleep and SQLite load.

Link is dynamic. If `ld` cannot find `-lsqlite3` (no `-dev` package):

```
mkdir -p /tmp/selmem-libs
ln -sfn /usr/lib/x86_64-linux-gnu/libsqlite3.so.0 /tmp/selmem-libs/libsqlite3.so
export RUSTFLAGS="-L /tmp/selmem-libs"
```

`./run.sh` does that when needed, then execs `cargo`.

```
./run.sh test
./run.sh run --release --example compare
./run.sh run --release --example llm_night
./run.sh run --release --bin selmemd -- --help
```

Rust only. No Python suite.

| What | Where | 
|---|---|
| Narrative scenes (edit these) | `tests/cases/*.json` | 
| JSON runner | `tests/scenes.rs` | 
| Physiology — decay, anchors, SQLite, Ebbinghaus | `tests/engine.rs` | 

```
./run.sh test
./run.sh test --test scenes
./run.sh run --release --bin selmemd -- \
  --bind 0.0.0.0:7420 \
  --path claire.db \
  --name Claire \
  --profile tender \
  --token secret \
  --llm https://api.openai.com/v1/chat/completions \
  --model gpt-4o-mini \
  --api-key "$SELMEM_API_KEY"
```

UI: `http://IP:7420/` — paste the token at the top, talk.

Local Ollama: `--llm http://127.0.0.1:11434/v1/chat/completions --model llama3`

Behind nginx:

```
location / { proxy_pass http://127.0.0.1:7420; proxy_read_timeout 90s; }
export SELMEM_LLM=https://api.openai.com/v1/chat/completions
export SELMEM_MODEL=gpt-4o-mini
export SELMEM_API_KEY=sk-...
export SELMEM_EMBED=https://api.openai.com/v1/embeddings
export SELMEM_TOKEN=secret
```

No endpoint: `RuleNarrator` + hashed vectors. The organ still runs.

One thread per connection. `/health` and `/` do not take the memory lock. `/turn` is serialized on the organ (one writer). Auth is `Authorization: Bearer` only — not `?token=`.

| Method | Route | Role | 
|---|---|---|
| GET | `/health` | liveness (no token, no lock) | 
| GET | `/who` | living axioms (trait > belief > motif) | 
| GET | `/lineage?schema=` | history of a belief | 
| GET | `/mood` | mood | 
| GET | `/audit?id=` | sealed verbatim (human debug) | 
| POST | `/live` | encode | 
| POST | `/remember` | reconstruct | 
| POST | `/speak` | embodied reply | 
| POST | `/turn` | live + reply | 
| POST | `/sleep` | consolidate | 
| POST | `/save` | flush | 

- Two books: lived narrative / archive. The model never reads the second.
- Two channels: `self` is sculpted,`world` is not.
- Forgetting by default. Encoding threshold.
- Unlabelled events get lexical affect (FR+EN), then identity, then optional LLM `interpret` on the live sentence.
- Living axioms color the next encoding before the gate.
- Cherished memories embellish. Recalled disgust amplifies. Neglected disgust extinguishes.
- Recall can shift *meaning* (valence under current mood), not only wording.
- Nearby episodes fuse into myth. Heavy anchors do not merge.
- Axioms climb: 2 traces → motif, 3+ → belief, aligned beliefs → trait. Lineage stays.
- Two profiles on the same corpus diverge. Fingerprint uses founders, traits, contradictions.
- Proxy for originality: reconstruction sticks less than the unmodified log; clones are not interchangeable; `world` facts survive (`originality_is_path_dependent_not_a_taller_log` ).
- Persistence: `.db` (SQLite) or`.selmem` (flat file).

``` js
let mut mem = SelectiveMemory::open("claire.db", EntityProfile::tender("Claire"))?;
mem.live_with(input);
let recalled = mem.remember("that evening");
let _ = mem.speak("do you remember?");
mem.sleep();
mem.who_am_i();
mem.lineage("loyalty");
singularity_distance(&fingerprint(&a), &fingerprint(&b));
```

Not RAG. Not a vector database. Not a personality in a system prompt.

Not a neocortex and not a brain. An executive–autobiographical loop around a next-token transducer.

No local neural encoder ships in-tree: pass `--embed` if you have one.
