Concepts: Pi Durable

Last updated 29 October 2026

View .md

Read this before you run Pi Durable on Tonbo: it names what the Harness keeps, how another machine resumes it, and why one process runs it at a time.

Pi Durable on Tonbo is Pi’s own SqliteStorage, unmodified. The difference is that its SQLite file is a Tonbo database, so the conversation is not owned by the machine that runs it.

How it differs from Pi’s own storage

MemoryStorageSQLite on this diskA Tonbo database
A commit resolvesIn memoryOn this diskOnce two of three replicas, each in its own zone, hold it
The process diesThe conversation is goneThe next process on this machine resumes itThe next process on any machine resumes it
The machine is lostThe conversation is goneThe conversation is goneThe next process on any machine resumes it

The conversation

Pi Durable commits an agent’s conversation, its model turns and its tool calls, to its storage as it goes. On Tonbo that storage is one database, named when you open it, such as harness-1: give each Harness a database of its own (see Concepts for what a database is).

A turn and when it is durable

Every Pi transaction resolves only once synced() did, so a turn the Harness has recorded is durable: Tonbo holds it, in two of three replicas in different zones. From Pi 1.2, a long outage makes a Harness call wait rather than reject; on Pi 1.0 and 1.1 it rejects with Pi’s StorageRejected (see backlog_full). A reset rejects it (see Resets in a Harness).

Resume

harness.resume() continues any run the last process left unfinished. A new process, on this machine or another, opens the database by name, the local copy rebuilds from Tonbo where its disk has none, and resume() continues the run. Record a tool call’s effect outside the process, such as an email, before making it, so a resumed run does not repeat it (see Run side effects once).

One writer

One process runs a Harness at a time: the last one to open its database. The one before it is refused from then on: its next Harness call rejects with UrsulaResetError, action stop, and it should stop and alert rather than open the database again, which would take it back from the new process. Run one instance per database (see Rules: one writer).

How it reaches your program

openUrsulaPiStorage("harness-1") opens the database by name with your API key and hands Pi its SqliteStorage; Harness.open() runs on it as on any storage, and harness.close() closes it too (see the Pi Durable quickstart).

  MACHINE A                               MACHINE B, later
  +------------------------------+        +------------------------------+
  |  the Harness takes a turn    |        |  opens "harness-1" by name   |
  |  --commit--> the local copy  |        |  harness.resume(): the run   |
  |  it resolves once Tonbo      |        |  continues from every turn   |
  |  holds it                    |        |  A recorded                  |
  +--------------+---------------+        +--------------+---------------+
                 |                                       ^
                 v                                       |
  TONBO, in the database's region: two of three zones hold every turn

  A, if it still runs once B opens: its next call rejects with
  action stop, and A stops

A turn resolves only once two of three replicas, each in its own zone of the region, hold it, which is why a process on another machine that opens the same database by name and calls harness.resume() continues from every turn the Harness recorded (see Guarantees).