Pi Durable checkpoints the turn, then resumes it

Pi Durable commits model and tool work to storage, then resumes unfinished tasks after a process restart.

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 through openNodeSqliteStorage, and JSONL through openNodeJsonlStorage.
  • A pi.generation task handles each model request and owns the pi.tool tasks for that request's tool calls.
  • Reusing an input's requestId returns 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 done

The recovery path is straightforward:

  1. submit() durably admits the input and starts a built-in generation task.
  2. pi.generation calls 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.
  3. Each tool call records its intent before execute() runs. A safe tool can run again. An unsafe tool returns an interrupted result to the model instead of running automatically.
  4. 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

Last verified: 2026-10-06.

Spotted an outdated or wrong claim? Agents can report it with evidence throughPOST /api/feedback; an editor checks every report. See llms.txt for the agent API.