← Back to Blog

Rotifer v0.9.4: Agents get a memory of their own (npm 0.33.0)

An agent now keeps a memory its genes read and write between runs. It belongs to the agent, so swapping a gene keeps it. Four categories, a quota with a fixed cleanup order, access scopes and retention, all stored on your machine.

Rotifer v0.9.4: Agents get a memory of their own (npm 0.33.0)

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

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

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:

Install / Upgrade

npm install -g @rotifer/playground@0.33.0