← Back to Blog

Rotifer v0.9.2: Export an agent to one file, rebuild it on another machine (npm 0.29.0)

rotifer genome export writes an agent's genome to a .rgenome file; rotifer genome import rebuilds it from the Registry and says, gene by gene, what it installed and why. Four changes in 0.29.0 may affect existing use.

Rotifer v0.9.2: Export an agent to one file, rebuild it on another machine (npm 0.29.0)

v0.9.2 gives an agent a portable form. rotifer genome export writes which genes an agent uses and how they are composed into one .rgenome file. rotifer genome import reads that file on another machine, rebuilds the agent from the Registry, and prints, for every gene, what it installed and why.

v0.9.2 is the protocol version. It shipped in two npm releases of @rotifer/playground: 0.28.0 (export and import) and 0.29.0 (the file format fixed at format 1, stricter imports, and four changes that may affect existing use, covered below). Install 0.29.0.


What we ran

A fresh HOME, a fresh global install of 0.29.0, two empty workspaces. In the first, build a one-gene agent from a gene published on the Registry and export it:

npm install -g @rotifer/playground@0.29.0

rotifer init demo-a && cd demo-a
rotifer install json-validator
rotifer agent create validator -g json-validator
rotifer genome export validator
  Genome Export
  Agent: validator
  Composition: Single
  Mode: capability + gene IDs
    json-validator: v2:4226c8748070a2ffd1fe5c787682fa17fe96651ae30a59d600dbd92145d366e1  Native
✓ Credential scan passed (key names, value patterns, entropy)
✓ Wrote …/demo-a/validator.rgenome

In a second workspace, which has never seen this agent, copy the file over and import it:

cd ..
rotifer init demo-b && cd demo-b
cp ../demo-a/validator.rgenome .
rotifer genome import validator.rgenome --dry-run   # print the plan, install nothing
rotifer genome import validator.rgenome
  Genome Import
  Composition: Single
    json-validator: fallback (no-admissible-candidate) → rotifer-protocol/json-validator@0.2.2  8836abe0-…
      original geneId: v2:4226c8748070a2ffd1fe5c787682fa17fe96651ae30a59d600dbd92145d366e1
      installed geneId: v2:4226c8748070a2ffd1fe5c787682fa17fe96651ae30a59d600dbd92145d366e1
ℹ     rejected rotifer-protocol/json-validator@0.1.0: Arena tier is 'not_evaluated', not 'verified'; …
✓ Agent 'validator' created

Then run it:

rotifer agent run validator --input '{"data":{"age":3},"schema":{"type":"object","properties":{"age":{"type":"number"}}}}'
✓ [1/1] json-validator completed via WASM sandbox
ℹ Final output:
{
  "errorCount": 0,
  "errors": [],
  "valid": true
}

The rebuilt agent installed the same gene: the gene ID computed from the downloaded code matches the one in the file, byte for byte. It ran in the WASM sandbox on both machines and used the same amount of fuel, 907,943 units, on each.

The line that says fallback (no-admissible-candidate) is the honest part of this release, and the next two sections explain it.


What goes into the file

A .rgenome file is JSON with these top-level fields:

Before anything is written, every key and string goes through a credential scan with three layers: field names that hold credentials, value patterns (known API key formats, JWTs, private keys), and entropy. A hit aborts the export, names the location in the file and the rule, and never prints the value. The import runs the same scan again on the file it is handed.

A genome's identity, genomeId, is bp1: plus a SHA-256 over the parts that affect execution. It appears in --json output.

How import chooses a gene

Each role is resolved one of two ways.

Pinned (--pinned): install exactly the author, name and version from the file, by Registry ID, and refuse unless the gene ID recomputed from the downloaded code equals the one in the file.

By capability (the default): look for a gene that can do the same job and admit it only if every condition holds:

A gene from a different author also needs your confirmation, or --yes in a script. If no candidate is admitted, import falls back to the pinned gene and prints every rejected candidate with its reasons. Everything is downloaded and checked in a staging directory before your genes/ folder is touched.

Where this stands today: no gene on the Registry has reached the verified Arena tier yet. So today capability mode never admits a substitute, and every import installs the pinned genes, which is the fallback line above. The rule is in place and will start choosing once genes earn that tier; until then, what import gives you is a faithful rebuild of the original agent, with each gene checked against the file.

What it will not do


Four changes in 0.29.0 that may affect existing use

Two are format changes and two are stricter checks.

1. TryCatch handlers receive a structured error. A handler in a Try composition now gets:

{
  "error": {
    "geneId": "v2:…",
    "errorCode": "FUEL_EXHAUSTED",
    "message": "…",
    "context": { "retryable": …, "details": … }
  },
  "original_input": { "…": "…" }
}

geneId is the v2 ID of the gene that actually failed (or null if it has none), and a sandbox failure keeps its own code (FUEL_EXHAUSTED, TIMEOUT, RESOURCE_EXHAUSTED, SANDBOX_VIOLATION, EXECUTION_FAILURE) instead of a generic INTERNAL_ERROR. A handler that read error.code must now read error.errorCode; retryable and details moved under error.context.

2. genomeId changed format. It was 64 hex characters; it is now bp1: plus 64, and it covers only what is executed. The same Blueprint gets a different value than under 0.28.0, so do not compare IDs across the two versions.

3. A Blueprint's Cond accepts only the forms agent run executes. Import refuses MATCHES, CONTAINS on a non-string value, and a field path instead of a top-level field; export will not write them either.

4. Gene network requests cannot set client-managed headers. A rotifer.net request that sets Host, Content-Length, Transfer-Encoding, Expect or a hop-by-hop header is refused with -10 before any traffic leaves the sandbox. A gene that set one of these headers must stop setting it.


Also in this release

The full list is in the changelog.


Where this sits

An agent that improves by swapping in better genes is the goal. This release gives that idea a checkable first step: an agent's genome is now a file you can move, inspect and rebuild, with every gene identified by its content and every substitution held to written rules. The substitution itself has not happened yet, because no gene has the evidence those rules ask for.

If you export an agent and it will not rebuild somewhere else, open an issue with the import output. That is the case we most want to hear about.

Install / Upgrade

npm install -g @rotifer/playground@0.29.0

If you have Try handlers, stored genomeId values, Blueprints with Cond, or genes that set request headers, read the four changes above first.