nineAdd Nine to Slack
← All articles

Making Agent-Written Code Durable

How Nine uses WebAssembly and replay to let JavaScript outlive its workers.

A replay log carries recorded random input and a clock reading from an interrupted worker to a replacement worker running the same JavaScript.

An agent's work rarely finishes in a single burst. A task might run for a few seconds, wait an hour for an external system or a human reply, and then run again. During that hour, the task is still in flight, but it needs almost no compute.

Meanwhile, the cluster around it is moving. Traffic spikes spin up new workers; quiet periods scale them back down. Deployments roll through, and occasionally a process dies.

If a task only lives in the memory of the worker that started it, every one of those events becomes a problem. You either pin workers open while they wait, or you lose in-flight work whenever capacity shifts.

To scale cleanly, Nine needed a simple rule: the work must outlive the worker.

Animated timeline showing one task running on Worker A, waiting without an active worker, and resuming on Worker B while extra workers handle other tasks.
Worker A starts the task and is released during the wait; Worker B resumes and finishes it once the external response arrives.

What should survive the worker?

For a replacement worker to pick up an interrupted task, something has to carry the progress across. There are four common ways to do it:

  • Keep the process alive: Local variables and the call stack stay in memory. This is the simplest model for short jobs, but long waits tie up capacity, and a crash still loses everything.
  • Save application checkpoints: Persist state at explicit steps so a new worker can resume from the last marker. The saved state is easy to inspect, but someone has to define the steps and what to save—awkward when an agent is writing fresh code on the fly with its own variables and control flow.
  • Snapshot the runtime: Capture the memory of the engine or VM and restore it elsewhere. That avoids custom checkpoint code, at the cost of moving larger snapshots and keeping runtime environments compatible.
  • Replay from recorded inputs: Save only the external inputs the program observed, then run the same code again in a fresh runtime to rebuild its state.
Four durability approaches: keep state in a live process, save application checkpoints, capture a runtime snapshot, or rebuild state by replaying code against recorded observations. Each diagram shows a benefit and a trade-off.
Checkpoints and snapshots persist state directly; replay persists only external inputs and reconstructs state by re-running the code.

Each approach fits a different problem. For agent-written JavaScript, Nine uses replay. Source code is small, and running it again reconstructs local variables and control flow automatically—provided the runtime can feed the code the exact same inputs it saw the first time.

A three-line script shows why that boundary matters.

Recovering the same ID and timestamp after a restart

Suppose an agent writes and runs this JavaScript in Nine:

const id = crypto.randomUUID();
const time = new Date().toISOString();
console.log(id, time);

Line one generates a UUID. Line two reads the clock at noon on November 6, 2026. Before line three can print, the worker stops.

With the worker gone, id and time vanish. If a replacement worker simply reruns the script thirty seconds later, crypto.randomUUID() produces a different ID and new Date() returns a later time. In a real task, that drift breaks things: an ID might track work already started, and a timestamp might decide which records belong in a report.

To recover the original run, the second attempt has to observe the same world as the first.

Nine makes this work by running QuickJS compiled to WebAssembly. Because the host controls the WebAssembly boundary, Nine supplies the engine's clock and random source—and records every value that crosses that boundary in a durable log before JavaScript sees it:

  • Random bytes requested by crypto.randomUUID().
  • The clock reading requested by new Date().

The agent still writes ordinary JavaScript. It does not need special checkpointing APIs or durableDate() wrappers.

When the worker stops, the log survives. A replacement worker creates a fresh QuickJS runtime and starts the same script from line one.

Animation showing random input and a clock value being recorded, a worker restart, and the same values being read from the replay log before printing.
On the first run, the host logs random bytes and the clock reading before passing them to QuickJS. After the crash, the new worker feeds those logged values back to reconstruct id and time.

This time, when crypto.randomUUID() asks for random bytes, the host hands back the bytes from the log. When new Date() checks the clock, the host returns the recorded noon timestamp, even though real time has moved on.

By line three, the new runtime has rebuilt id and time purely by executing the code. No heap snapshot or saved call stack was needed. The script reaches console.log and prints:

7c92f068-9a14-4f20-a802-63e8d51ae401 2026-11-06T12:00:00.000Z

It is the exact output the first worker was about to produce.

Order is strict during replay: each host call must match the next entry in the log, or the runtime halts with a replay mismatch rather than feeding a saved timestamp into a random-byte read. Once execution passes the end of the log, it catches up to the present—a new clock read later in the script observes the current time and appends a fresh entry.

In this example, the worker stopped before console.log. Crashes after an external action raise a separate question—whether that action already happened—so replaying inputs is only one piece of recovery, not a claim of exactly-once output on its own.

Ordinary code on replaceable workers

A three-line script is a small example, but it shows the property Nine needs for long-running work: execution state can survive a change of worker.

When a worker is replaced—by a deployment, a scale-down, or a failure—another worker can rebuild the script's state from the log. Because recovery happens in the runtime, Nine does not need to spend an extra model turn asking the agent to recover its old ID or guess when the task began.

That keeps the division of labor clean. The agent writes ordinary code, the runtime makes its observations reproducible, and the work keeps moving while the capacity underneath it changes.

Your first
useful handoff.

Join the early-access list. Tell us what you’d like Nine to do and which tools it needs.

Join Nine’s early-access list

No purchase commitment. This joins the list; it doesn’t create an account or book a call.