livmem
v0.4.0-beta.4
Published
A memory for AI agents that learns which facts actually helped, from task outcomes alone.
Maintainers
Readme
Living Spatial Memory
MVP documentation: Read the complete installation, agent integration, operation, security, and evaluation guide.
Two layers, built and measured together:
- A memory for AI agents that learns which facts actually helped — from task outcomes alone, with no labelling.
- A packed 3D spatial field underneath it, where a cell is two bytes, links become geometry, and a corrupted cell is located and repaired by the shape of the lattice itself.
Either can be used on its own. Together they are one system: the memory learns what belongs near what, and the field is where that lives.
Vector search answers "what looks similar to this?". That is a good question, and it is not the one an agent needs. An agent needs "what has actually helped me before in a situation like this?" — and the only honest source for that answer is whether the task worked.
Install the current research alpha from the public npm registry:
npm install livmemGlobal agent CLI
Install one command for Codex, Claude Code, and other coding agents:
npm install -g livmemGlobal installation makes the CLI available everywhere; it does not merge repository data.
Every initialized project keeps its own .living-memory/memory.json. This isolation is the
recommended MVP model. Install with --save-dev instead when a team also needs the exact CLI
version pinned in that project's lockfile.
Then enter any repository and connect every supported agent convention:
livmem init --target allThe shorter lm command is an alias for livmem. Initialization creates an inspectable
.living-memory/memory.json, writes the universal protocol to .living-memory/AGENT.md, and adds
an idempotent managed section to AGENTS.md for Codex and to CLAUDE.md for Claude Code. It also
registers a required project MCP server in .codex/config.toml and an
always-loaded server in .mcp.json. Existing instructions and unrelated MCP servers are preserved.
Restart the agent once after initialization and approve the project MCP server when prompted. Then verify the complete integration:
livmem doctorlivmem status
livmem log --action "Ran the complete test suite" --kind verification --actor codex
livmem actions --limit 20
livmem remember --fact "Use server authority for competitive balances" --related "economy"
livmem recall --query "server economy" --limit 5
livmem recall --query "database migration" --track
livmem outcome --episode-id <id> --successCommands are non-interactive and return JSON by default so any agent can call them reliably. Use
--human for formatted output, livmem --help for the machine-readable command contract, or
livmem --brief for a compact description. For tools that do not read AGENTS.md or CLAUDE.md,
point the agent at .living-memory/AGENT.md.
The generated protocol and first-party MCP server tell compatible agents to run a task loop:
tracked recall at the start, concise action logging during work, durable-fact writeback after
verification, and a real success/failure outcome at the end. Actions live in a separate journal
inside the same project memory and never pollute recalled facts. Codex and Claude Code receive both
repository instructions and discoverable memory tools. Another agent must support MCP or project
instructions; livmem cannot silently control an agent that ignores both.
Use npx livmem init --target all instead if you prefer a repository-local installation.
Installation summary:
| Goal | Command |
|---|---|
| Global CLI for many repositories | npm install -g livmem |
| Current beta channel | npm install -g livmem@beta |
| Exact released version | Choose a version from GitHub Releases and run npm install -g livmem@<version> |
| Team-pinned developer dependency | npm install --save-dev livmem |
| One-time setup | npx --yes livmem@latest init --target all |
| JavaScript/TypeScript library | npm install livmem |
| Public GitHub source | npm install git+https://github.com/Huliichuk/living-spatial-memory.git |
See the MVP guide for individual Codex, Claude Code, and generic-agent setup, the JSON CLI contract, tracked outcome workflow, data model, security boundaries, troubleshooting, and a suggested personal evaluation plan. Public version history is recorded in the changelog and GitHub Releases.
After merging approved work into main, one local command performs the complete release without
GitHub Actions:
npm run release:betaIt requires authenticated npm and gh CLIs, verifies that local main exactly matches
origin/main, runs the complete test suite and build, increments 0.4.0-beta.N, commits the
version, pushes an immutable tag, publishes both the npm latest and beta channels, and marks
the matching GitHub Release as Latest. Re-running it repairs an incomplete release instead of
inventing another version.
Any release can also be installed from its exact public GitHub tag. For example:
npm install git+https://github.com/Huliichuk/living-spatial-memory.git#v0.4.0-beta.3It works in any Node.js 20-or-newer ESM project. The local MCP transport uses the official Model Context Protocol server library and requires no hosted service, database, account, or API key.
Agent-visible MCP tools
livmem mcp serves project memory over stdio. Agent hosts discover these tools after initialization:
| MCP tool | Agent behavior |
|---|---|
| memory_start_task | Required tracked recall at the beginning of a task. |
| memory_recall | Read-only lookup without opening an outcome episode. |
| memory_log / memory_actions | Write or read meaningful observable work history. |
| memory_remember | Store a durable verified fact with routing topics. |
| memory_complete_task | Close the tracked episode with its real success or failure. |
| memory_status | Inspect facts, actions, pending episodes, outcomes, and misses. |
The MCP initialization response carries server-wide workflow instructions. Codex supports
project-scoped MCP configuration and reads server
instructions; Claude Code loads the project server from
.mcp.json. Project trust approval remains controlled by
the agent host.
Three verbs
import { AgentMemory } from "livmem";
const memory = AgentMemory.open(); // .living-memory/memory.json
// 1. Store what happened.
memory.remember("stripe webhooks need the raw body, not parsed json", {
topics: ["billing", "stripe"],
});
// Keep observable work history separate from reusable knowledge.
memory.recordAction("Verified the webhook tests", {
kind: "verification",
actor: "custom-agent",
});
// 2. Retrieve what might help, before doing the work.
const facts = memory.recall("billing");
// -> [{ id, text, relevance }, ...]
// 3. Say whether it worked. This is the part that makes it learn.
memory.recordOutcome(true);
memory.save();Facts that keep appearing before successes become easier to reach. Facts that appear before failures fade. Nobody labels anything.
When recalls can overlap, keep their episode ids so each outcome teaches the right activation path:
const first = memory.recallEpisode("billing");
const second = memory.recallEpisode("deploy");
memory.recordOutcome(true, first.id);
memory.recordOutcome(false, second.id);
memory.save();The recent outcome history and still-open episodes are stored with the graph,
so this correlation survives a save and process restart. Plain recall() plus
recordOutcome(worked) remains the simple single-task API.
An exact known topic remains the preferred seed. If a query is not an exact topic, the memory now uses an IDF-weighted lexical fallback to find up to five starting cells; this makes ordinary questions usable without pretending that word overlap is semantic understanding.
Why the third verb matters
remember and recall alone are an index. recordOutcome is what turns an
index into a memory:
- Credit is contrasted between the recalls that preceded successes and those that preceded failures — so a fact that is merely popular gains nothing.
- Credit is written along the activation path, not just onto the query's own links. This is what lets one task's lesson help a different task that reaches the same shared structure.
- A small amount of exploration is built into
recall. A memory whose answer never varies produces outcomes that never differ, and can never learn it was wrong. Setexplore: 0if you need a strictly deterministic reader.
Every one of those three choices is the result of a measurement, not a
preference. The experiments that produced them — including the ones that
failed — are in docs/research-log.md.
API
| Method | What it does |
|---|---|
| AgentMemory.open({ path?, learningRate?, explore?, seed? }) | Opens or creates a memory file. |
| remember(fact, { relatedTo?, topics?, strength?, importance? }) | Stores a fact; topics are routing-only and never returned. |
| markTopic(topic) | Reclassifies a legacy routing cell so it is no longer returned as a fact. |
| recall(about, { limit?, depth?, explore? }) | Returns related facts, strongest first. |
| recallEpisode(about, options?) | Returns { id, facts } for concurrent or restartable work. |
| recordOutcome(worked, episodeId?) | Reports whether the named episode, or otherwise the last recall, helped. |
| recordAction(action, { kind?, actor?, at? }) | Appends observable work to the separate project action journal. |
| recentActions(limit?) | Reads the newest journal entries in chronological order. |
| forget(rate?) | Weakens what has not been used. Call occasionally. |
| save() | Atomically writes the graph, learning history and open episodes as readable JSON. |
| size | { facts, topics, links, actions }. |
The memory file is plain JSON you can open, read and diff. A memory you cannot inspect is a memory you cannot trust with your project's history.
Honest scope
What this is. An episodic memory layer and local MCP server for agents, plus a packed spatial field to lay it on. Both are measured, inspectable, and local-first.
What this is not. It is not a language model and does not understand text — it stores the strings you give it and reasons over their connections. It is not a replacement for vector search when you genuinely need semantic similarity; the two answer different questions and compose well.
What the current evidence says. Prediction tasks split into three bands:
trivial (raw frequency already answers), impossible (the past never contained
the answer), and a middle band where the answer is present but buried. On the
frozen project-history corpus, untaught memory ties Adamic–Adar in that middle
band (23% hit@5 each), while the current outcome-learning rule lowers the score
to 16%. This is a useful retrieval substrate, not yet a demonstrated learning
advantage. Details in docs/lsm-experiment-020.md.
The spatial layer
SpatialField3D is a packed three-dimensional field: one Uint16Array where
every cell carries six fields — value, strength, energy, weight, route class and
control — in two bytes.
import { SpatialField3D, packSpatialSymbol16 } from "livmem";
const open = packSpatialSymbol16({
value: 1, strength: 7, energy: 15, weight: 15, routeClass: 0, control: true,
});
const field = SpatialField3D.filled(32, 32, 32, open); // 64 KB, 32768 cells
field.setBlocked({ x: 5, y: 5, z: 5 }, true);
const path = field.findPath({ x: 0, y: 0, z: 0 }, { x: 31, y: 31, z: 31 },
{ x: 1, y: 1, z: 1, xy: 0.9, xz: 0.9, yz: 0.9, xyz: 0.8 },
{ algorithm: "a-star", cornerPolicy: "require-clearance" });
const reachable = field.runProgram("reachability", DIRECTIONS);
const influence = field.runProgram("sum-product", DIRECTIONS);
const bytes = field.toBinary(); // portable, versionedOne state, several programs — navigation, reachability and influence run over the same packed cells with no conversion between them.
What the measurements established:
| Property | Result | |---|---| | Pathfinding correctness | Identical optimum and explored set to a conventional implicit A* | | Links as geometry | A placement turns most of a graph's link weight into pure adjacency, needing no storage at all | | Error localisation | Two checks per unit cube name the corrupted cell exactly, at every tiling size | | Repair | Single-fault repair restores the original value; multi-fault syndromes are refused, never guessed — 0 wrong writes in 240,000 trials | | Code capacity | A cube whose links carry 2-bit labels holds exactly 2.000 bits per step; capacity and error detection trade on a measured curve |
Honest limit, measured and recorded: against a minimal conventional
representation the packed field is competitive, not smaller — the win is that
one state serves several programs and repairs itself locally, reading nine
neighbouring cells rather than the whole structure. See
docs/spatial-field-api.md and
docs/ddse-experiment-007.md.
Research
This started as an experiment in spatial encoding and became a memory. The
docs/ directory holds the full record — 39 experiments, each with its
protocol, its limits and its result, including the null ones:
- How relevance is learned —
lsm-experiment-003.md…009 - Where learning meets geometry —
lsm-experiment-010.md…013 - The memory tuning itself —
lsm-experiment-014.md…016 - Real-data retrieval and scaling limits —
lsm-experiment-017.md,018,020,021 - The packed spatial layer —
ddse-experiment-002.md…022: routes over one buffer, the minimal weighted cube, four programs on one state, implicit pathfinding against A*, bucket search, wavefront and out-of-core shadow fields, the cube as an edge-labelled code, and localisation and repair from tiled geometry
Reproduce any of them:
npm test
npm run experiment:lsm:008 # transfer through shared structure
npm run experiment:lsm:014 # the memory picking its own operating point
npm run experiment:lsm:018 # the dose scan on a real repository
npm run experiment:lsm:020 # frozen project-graph retrieval benchmark
npm run experiment:lsm:021 # warm recall and returned-context scaling
npm run experiment:ddse:005 # packed A* against a conventional implicit A*
npm run experiment:ddse:020 # integer bucket A* and fair baseline ladder
npm run experiment:ddse:021 # lattice paths against a wavefront solver
npm run experiment:ddse:022 # resident block shadow and block-lazy search
npm run experiment:ddse:010 # localising a corrupted cell from shared vertices
npm run experiment:ddse:011 # repair, and what happens when two cells breakStatus
Public 0.4.x MVP beta. The library, universal agent CLI, isolated project memory, and action journal
are ready for dogfood evaluation; the internals are still moving. Node.js 20 or newer. Run
npm view livmem version for the current published build.
License
Apache-2.0.
