Pi Durable, as statecharts
A working model of Pi Durable, Earendil’s durable agent harness, where every task is a statechart that commits each step to storage. Kill the process whenever you like: a new one continues every task from its checkpoint. Each chapter follows a section of the post; opens the passage.
Free play
Clients
From “Pi Durable” · What is a harness?
A harness is storage plus the machinery needed to run one or more conversations with large language models in parallel. It provides the tools those models call, and the execution environments the tools run in. (opens the post) Everything the harness runs, from calling the model to executing a tool, is a task. (opens the post)From “Pi Durable” · Long runs anywhere
In Pi Durable, a harness opens over a storage backend. (opens the post) One process owns a storage at a time, and other clients attach to that process. (opens the post) Your env function builds the environment for every tool call, from the conversation's working directory, so each conversation can run in a different place. (opens the post)From “Pi Durable” · Survives crashes
In Pi Durable, every step of a run is a task that stores a checkpoint before it moves on. If the process dies, a new process opens the same storage, finds the unfinished tasks, and continues each one from its last checkpoint. (opens the post) A model request that was cut off is sent again; the partial answer stays in the transcript, marked as aborted. A tool call that was cut off reruns if it is safe to; otherwise the model is told it was interrupted. (opens the post) Queued messages are still queued. A requestId makes a submission exactly-once, so a client that retries after a crash gets the original submission back instead of asking twice. (opens the post)From “Pi Durable” · Many conversations at once
A conversation starts fresh or forks another one at any point in its transcript, and sees the parent's history up to that point without copying it. (opens the post) A reviewer next to the main agent can use a cheaper model, read-only tools, and its own checkout. (opens the post)From “Pi Durable” · Extensions › System prompt sections
The system prompt is rebuilt from the sections of the conversation's extensions before every request, so a changed section is picked up by the next request. Pi Durable records what changed in the transcript, at the position where it changed, so a restart or a fork sees exactly what the model saw. (opens the post)From “Pi Durable” · Extensions › Tools
Every tool call runs as its own durable task, and its intent is stored before it runs. After a crash, a tool reruns only if it says that is safe. (opens the post) A tool creates a conversation it owns, gives it a smaller model and its own instructions, and waits for its answer. (opens the post) A tool with the same name in a later extension replaces the earlier one, for example a bash that runs inside a Python virtualenv. A wrap decorates whichever tool won, wherever the wrapping extension is selected. (opens the post)From “Pi Durable” · Extensions › Hooks
A hook can run again after a crash, so a hook that makes a decision stores it in a memo: a small value stored with the task, where the first write wins. (opens the post) Their hooks run as a chain, in the order the conversation selects the extensions, and each hook defines how its chain runs. (opens the post)From “Pi Durable” · Extensions › Tasks
A checkout that splits the bill across several cards charges every card at once. If one card is declined, the other payments are aborted and refund themselves (opens the post) Tasks and conversations form one ownership tree. Aborting a task aborts what it owns, bottom-up, so every task cleans up its own effects first, and a task only finishes once the work it owns has finished. (opens the post) A background task belongs to the conversation, but not to its current work. The conversation goes idle while it runs, and an ordinary abort leaves it and everything it owns alone. (opens the post)From “Pi Durable” · Compaction
When the context gets close to the model's limit, a background compaction summarizes the older messages, and the summary is placed at the next turn boundary. The conversation only waits for a summary when the next request would not fit otherwise. (opens the post) Because nothing is deleted, a second tool can still search everything before the handoff. (opens the post)From “Pi Durable” · Durable application state
Documents are typed JSON stored next to the transcript and changed in the same atomic commits, so the state never disagrees with the transcript that produced it. Each document says what a fork starts with: the parent's value at the fork point, its current value, or a fresh one. (opens the post)From “Pi Durable” · Malleable
Installing an extension under a name that is already installed replaces it in one step. A tool call that is already running finishes on the code it started with; the next call uses the new code. (opens the post) Conversations store extension and tool names, never code, so after a restart they pick up whatever the new process installs. (opens the post)From “Pi Durable” · Multiplayer
A client gets the current view first: the transcript, the answer being streamed, running tools and their output, queued messages, the agent, and usage. After that it only gets what changes. (opens the post) Any client can steer a running conversation or queue a follow-up. (opens the post)