DEV Community

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

Posted on Originally published at rotifer.dev

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 };
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>)
Enter fullscreen mode Exit fullscreen mode

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, queryMemory and deleteMemory host 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 run checks the schema. Input is checked against the gene's inputSchema before it runs, and output against outputSchema after. Input that does not match now exits 1; --no-validate turns both checks off.
  • rotifer compile prints Gene 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, Cond or Try agent that cannot run as written stops and says why instead of quietly running as Seq; a gene stopped by its time limit is reported as a timeout; rotifer run works 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.
  • autoDeleteOnTermination is 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
Enter fullscreen mode Exit fullscreen mode

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)