Pi Durable is the separate experimental package for making a Pi agent run durably. It commits transcript entries, application documents, and tasks before showing their state, then resumes unfinished work when a new process reopens the same storage and calls harness.resume(). Pi resends an interrupted model request, but reruns a tool only when that tool declares replay: "safe".
Key facts
- On 2026-10-01, Earendil announced Pi 1.0 and Pi Durable together. Pi Durable is experimental, and both are MIT-licensed.
- Pi 1.0 is the terminal-based coding agent. Pi Durable is a separate framework for long-running agentic applications, including coding agents.
- Pi Durable ships
MemoryStorage, SQLite throughopenNodeSqliteStorage, and JSONL throughopenNodeJsonlStorage. - A
pi.generationtask handles each model request and owns thepi.tooltasks for that request's tool calls. - Reusing an input's
requestIdreturns the existing submission instead of submitting the input twice. - SQLite uses WAL mode with
synchronous = NORMAL. The README says this protects commits from process crashes, but the newest commit may still be lost after a power or host failure.
How Pi Durable checkpoints and resumes a run
Pi Durable treats each part of a run as a durable task. The task writes a checkpoint before it moves to the next step. The README describes one atomic commit line for changes and says clients see state only after its commit is stored.
submit(input) → pi.user
pi.generation → pi.assistant (tool calls)
pi.tool × n → pi.tool-result × n
pi.generation → pi.assistant (answer) → submission doneThe recovery path is straightforward:
submit()durably admits the input and starts a built-in generation task.pi.generationcalls the model. If the stream stops during a crash, Pi keeps the partial answer marked as aborted and sends the model request again after reopen.- Each tool call records its intent before
execute()runs. A safe tool can run again. An unsafe tool returns aninterruptedresult to the model instead of running automatically. - A new process opens the same SQLite or JSONL storage and calls
harness.resume(). The scheduler continues pending tasks from their stored checkpoints.
The requestId protects the boundary around a client retry. If the client submits the same input again after a restart, Pi returns the original submission. It does not create a second run.
MemoryStorage quick start
MemoryStorage is useful for a test or a transient agent because it keeps state in memory. It does not survive process exit. This is the smallest complete setup from the Pi Durable README:
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { createModels } from "@earendil-works/pi-ai/models";
import { openaiProvider } from "@earendil-works/pi-ai/providers/openai";
import {
AssistantEntry,
createRegistry,
Harness,
MemoryStorage,
} from "@earendil-works/pi-durable";
const context = BACKGROUND_CONTEXT;
const models = createModels();
models.setProvider(openaiProvider());
const harness = await Harness.open(
new MemoryStorage(),
{ models, registry: createRegistry() },
context,
);
const root = await harness.root(context, {
agent: { model: { provider: "openai", modelId: "gpt-6-sol" } },
});
const submission = await root.submit(
{ type: "input", content: "What is the capital of France?" },
context,
);
const settled = await submission.wait(context);
if (settled.status === "done" && settled.type === "input") {
const answer = await root.commit(
(tx) => tx.entry(AssistantEntry, settled.answer),
context,
);
console.log(answer?.model?.[0]);
}
await harness.close(context);Use SQLite or JSONL when the transcript must survive a restart. MemoryStorage is not a smaller form of durable recovery. It is an in-memory backend.
Pi Durable's safe-tool replay rule
Pi Durable requires the tool author to make replay behavior explicit. A read-only or idempotent tool can opt into automatic replay:
import { Type } from "@earendil-works/pi-ai";
import { defineTool } from "@earendil-works/pi-durable";
const searchIssues = defineTool({
name: "search_issues",
description: "Search the issue tracker",
parameters: Type.Object({ query: Type.String() }),
replay: "safe",
execute: async (args, api) => {
api.output(`searching for ${args.query}\n`);
return {
content: [{ type: "text", text: await tracker.search(args.query) }],
};
},
});Leave replay: "safe" off a tool that charges a card, places an order, edits an already-edited file, or deploys a version. If such a call is interrupted, Pi gives the model the committed partial output and an interrupted result. The model decides whether to retry.
Pi Durable and Pi 1.0 after the 2026-10-01 release
Pi's changelog lists Pi 1.0.4 on 2026-10-05. That release adds wildcard patterns for --tools and --exclude-tools, plus --no-mcp, to the terminal agent.
Cloudflare's Agents documentation documents PiHarness as a beta way to run Pi Durable in a Durable Object. It stores Pi transcripts, inbox entries, and tasks in the object's SQLite database. A lifecycle wake-up restarts the object and continues the run after an eviction. The documentation still labels Pi Durable experimental and requires version 1.0 or later of the Pi packages.
Pi Durable limits and current status
The Pi Durable README labels the API experimental and says it can change without notice. MemoryStorage loses all state when the process exits. SQLite's WAL and synchronous = NORMAL are process-crash protection, not a promise that the newest commit survives power loss. One process owns a storage at a time; the README says there is no cross-process locking.
Sources
- Pi 1.0 announcement: https://earendil.com/posts/pi-1-0/ (read 2026-10-06)
- Pi Durable architecture and recovery: https://earendil.com/posts/pi-durable/ (read 2026-10-06)
- Pi Durable README, quick start, tasks, replay, resume, and storage: https://github.com/earendil-works/pi/blob/main/packages/durable/README.md (read 2026-10-06)
- Pi changelog, including Pi 1.0.4 on 2026-10-05: https://pi.dev/changelog (read 2026-10-06)
- Cloudflare Agents
PiHarnessdocumentation: https://developers.cloudflare.com/agents/harnesses/pi/ (read 2026-10-06)
Last verified: 2026-10-06.