Skip to main content
Agents

Pi

Run the Pi coding agent as a durable Rivet Actor.

This integration is in beta. APIs may change between releases.

@rivet-dev/pi runs the Pi coding agent in a Rivet Actor. The agent loop runs in the actor. Pi’s file and shell tools run in a separate sandbox.

Each actor holds one Pi session and saves it in its SQLite database, so a conversation survives sleep and restarts.

YOUR BACKENDSANDBOXESPi actorShell + filesagent loop + SQLiteSleepingPausedTool callsModel callsModel provider

Quickstart

Install

npm add @rivet-dev/pi rivetkit

Define the actor

import { pi } from "@rivet-dev/pi";
import { setup } from "rivetkit";

const agent = pi({ model: "anthropic/claude-opus-5-5" });

export const registry = setup({
	use: { agent },
});

registry.start();

Set the key for your model provider, such as ANTHROPIC_API_KEY.

Send a prompt

import { createClient } from "rivetkit/client";
import type { registry } from "./server";

const client = createClient<typeof registry>("http://localhost:6420");
const agent = client.agent.getOrCreate(["support", "customer-123"]);

const conn = agent.connect();

await conn.prompt("Inspect the project and summarize its test failures.");
console.log(await conn.getLastAssistantText());

await conn.dispose();

The client waits for Pi to finish, then reads the full reply with getLastAssistantText().

Deploy

By default, Rivet stores Actor state on the local file system.

To scale Rivet in production, pick how much of it you want to run yourself:

If you are running your own workers, follow the guide for your hosting provider:

Configuration

pi() accepts the same config as actor() and Pi’s session options, plus:

OptionDescription
modelModel for a new session.
scopedModelsModels a client may switch to with setModel.
apiKeysAPI keys by provider. Otherwise keys come from the environment.
providersCustom providers, in the shape of Pi’s models.json.
credentialsLogins your application stores, such as subscriptions. See Bring your subscription.
sandboxWhere Pi’s file and shell tools run. Without it they are disabled.
import { agentOS } from "@rivet-dev/agentos";
import { pi } from "@rivet-dev/pi";
import { agentOSProvider } from "@rivet-dev/sandbox-adapter/agentos";
import { setup } from "rivetkit";

const agent = pi({
	model: "anthropic/claude-opus-5-5",
	sandbox: agentOSProvider({ actor: "vm" }),
});

export const registry = setup({ use: { agent, vm: agentOS() } });

For E2B, Daytona, or Modal, use sandboxAgentProvider() from @rivet-dev/sandbox-adapter/sandbox-agent. Provider credentials never enter the sandbox.

Tracing

Each run is an invoke_agent pi span under the prompt action span, with OpenTelemetry GenAI attributes. Prompts, tool arguments, and tool results are not recorded. See OpenTelemetry to export actor traces.

Bring your subscription

Your users can run Pi on their own Claude or ChatGPT subscription instead of your API key. Pi supports headless OAuth login for these subscriptions.

Pass the logins you store to Pi with credentials:

import { pi } from "@rivet-dev/pi";

const agent = pi({
	model: "openai-codex/gpt-6-sol",
	credentials: (c) => ({
		list: () => logins.list(c.key[0]),
		read: (provider) => logins.read(c.key[0], provider),
		refresh: (provider) => logins.refresh(c.key[0], provider),
	}),
});

logins is your own storage. See PiCredentialSource for what each method returns. Pi never receives the refresh token.

The credentials example stores logins in a credentials actor and runs the login in a terminal.