Until now, a gene started every run from nothing. Whatever it learned about a user, a task or a recurring input was gone when the run ended.
v0.9.4 gives every agent a memory. Its genes can read and write it between runs, through four calls, under rules the protocol defines: what kind of entry it is, who may read it, how much room there is, and how long it is kept.
v0.9.4 is the protocol version (spec §38, Agent Memory). It shipped in @rotifer/playground 0.33.0 on 2026-11-01.
Memory belongs to the agent, not to a gene
An agent is a set of genes. Genes get replaced: a better formatter comes along, a gene gets a new version. If memory lived inside a gene, every swap would wipe what the agent had learned.
So memory belongs to the agent. When you replace or update a gene, the agent's memory stays where it was, and the next gene can pick it up.
Here is a formatter that remembers the indent width a user asked for last time:
import type { GeneMemoryAccess } from "@rotifer/playground/memory";
// reindent() is the gene's own formatting logic, not shown here.
const KEY = "formatter/indent";
export function express(
input: { code: string; indent?: number },
ctx: { memory: GeneMemoryAccess },
): { formatted: string; indent: number } {
let indent = input.indent;
if (indent === undefined) {
const saved = ctx.memory.readMemory(KEY, "PREFERENCE");
indent = saved ? Number(new TextDecoder().decode(saved.value)) : 2;
} else {
ctx.memory.writeMemory(
KEY,
new TextEncoder().encode(String(indent)),
"PREFERENCE",
);
}
return { formatted: reindent(input.code, indent), indent };
}
The entry gets the default access policy, so other formatting genes in the same domain can read it. Swap this gene for a better formatter in the same domain, and the new one finds the saved indent where the old one left it.
Four kinds of entry, and what gets cleared first
Every entry has one of four categories:
| Category | What it holds | Cleared when memory is full? |
|---|---|---|
PREFERENCE |
What the agent has learned about a user | Never |
PATTERN |
Patterns noticed across repeated runs | Yes, after CACHE
|
CONTEXT |
Knowledge built up about a task or domain | Yes, after PATTERN
|
CACHE |
Results that can be computed again | Yes, first |
Each agent has a quota, 10 MB by default. Set another one when you create the agent:
rotifer agent create my-agent --genes my-gene --memory-quota=50MB
When a write would go over the quota, the agent makes room by removing the oldest CACHE entries, then PATTERN, then CONTEXT. PREFERENCE entries are never removed this way. If there is still not enough room, the write fails with MEMORY_QUOTA_EXCEEDED and nothing is written.
Who can read what
An entry says which genes may read it and which may change or delete it. The options are the gene that created it, genes in the creator's domain, or every gene in the agent. The default is the creator's domain (SAME_DOMAIN).
The check runs before every read, write and delete. A gene that is not allowed gets MEMORY_ACCESS_DENIED, and the refusal is written to the agent's audit log.
How long entries are kept
An entry can carry maxRetentionDays. Once that many days have passed since it was last written, it is gone: reads return nothing, queries leave it out, and it no longer counts toward the quota. Each removal is logged.
Seeing and clearing an agent's memory
As the person who deployed the agent, you can look at its memory and clear it:
rotifer agent memory list <agent> [--category <category>]
rotifer agent memory delete <agent> (--key <key> | --category <category>)
list shows each entry's key, category, size, creating gene and last update. It never prints values. delete removes one key or a whole category, whatever the entries' access policies say, and each deletion is logged.
It stays on your machine
Memory is stored in your project's .rotifer/playground.db. Every change, and every refused access, adds a line to .rotifer/audit.jsonl. Both files stay on your machine; the CLI sends neither of them anywhere.
Which genes get memory
-
TypeScript genes that run on Node.js get the four calls as
ctx.memory, as in the example above. -
Genes that bring their own WASM (Rust, AssemblyScript, WAT) call the
rotifer.readMemory,writeMemory,queryMemoryanddeleteMemoryhost functions. - TypeScript compiled to WASM cannot use memory yet. If a TypeScript gene needs memory, run it on Node.js.
Memory only works inside an agent (rotifer agent run). To try a gene that uses it, run rotifer test: it gives the gene an empty memory of its own for the length of the test and then deletes it, so your agents' memory is not touched.
The Memory API reference has the four calls, the error codes, the host-function interface and the size limits.
The Cloud side: built and checked, not deployed
The same memory model is implemented for Rotifer Cloud: a database schema and an agent-memory edge function, in the rotifer-playground repository. We ran the same set of operations through the CLI and through the Cloud implementation (against a local Supabase) and compared the results entry by entry: 39 of 39 matched in the main scenario and 32 of 32 in the expiry scenario.
It is not deployed. Rotifer Cloud does not store agent memory today, and there is no command that moves an agent's memory to the cloud. It will be deployed when rotifer.ai's cloud agents connect to it. That is not part of this release, and it has no date yet.
Also in this release
-
rotifer runchecks the schema. Input is checked against the gene'sinputSchemabefore it runs, and output againstoutputSchemaafter. Input that does not match now exits 1;--no-validateturns both checks off. -
rotifer compileprintsGene ID (v2), the id memory records as an entry's creator, and labels the content hash as not an identity. -
Fixes: wrapped genes compile again; a
Par,CondorTryagent that cannot run as written stops and says why instead of quietly running asSeq; a gene stopped by its time limit is reported as a timeout;rotifer runworks on Windows. - τ is paused in the CLI too, and wasmtime is updated to 48.0.8 for the 2026-10-29 RustSec advisories.
The full list is in the changelog.
Where this sits
The aim is agents that get better at their job the longer they run, while the genes inside them keep being replaced by better ones. That needs memory that outlives any single gene and rules about who can touch it. This release puts that in place on your machine.
Not in this release:
- Cloud memory is not live. The Cloud implementation exists and matches the CLI, but it is not deployed.
- TypeScript genes compiled to WASM cannot use memory yet.
-
autoDeleteOnTerminationis stored but has no effect yet. - No command moves an agent or its memory to another binding.
Install / Upgrade
npm install -g @rotifer/playground@0.33.0
This article was originally published on rotifer.dev. Follow the project on GitHub or install the CLI: npm i -g @rotifer/playground.
Top comments (0)