Skip to main content
General

Quickstart

Run your first Pi agent in a Rivet Actor. Its files live in the Actor's database, so there is nothing else to set up.

An agent is a Rivet Actor in your backend. Clients prompt it like any other Actor. Each step of a run is saved to the Actor’s SQLite database, so a run survives sleep, crashes, and upgrades.

Clientclient.tsAgent Actorserver.tsSandboxoptionalModel providerprompttool callsmodel calls

Install

npm add @rivet-dev/pi @earendil-works/pi-durable rivetkit

Define the agent

import { createRegistry, defineExtension } from "@earendil-works/pi-durable";
import { createEditTool, createReadTool, createWriteTool } from "@earendil-works/pi-durable/tools";
import { pi } from "@rivet-dev/pi";
import { setup } from "rivetkit";

// read, write, and edit. With no sandbox, the files live in the Actor's own database.
const extensions = createRegistry();
extensions.install(defineExtension({ name: "files", tools: [createReadTool(), createWriteTool(), createEditTool()] }));

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

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

registry.start();

Set ANTHROPIC_API_KEY. For another model provider, change model and set that provider’s key, such as OPENAI_API_KEY. See LLM API Keys.

The agent needs no sandbox. Its read, write, and edit tools work on files stored in the Actor’s own database.

Send a prompt

TypeScript

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

const client = createClient<typeof registry>();
const agent = client.agent.getOrCreate(["user-123"]);

const result = await agent.prompt(
	"Write a hello-world Rivet Actor to counter.ts, then read the file back to me.",
	{ requestId: crypto.randomUUID() },
);

console.log(result.status === "done" ? result.text : `Unanswered: ${result.reason}`);

curl

URL="http://localhost:6420/gateway/agent/action"
AGENT="rvt-namespace=default&rvt-method=getOrCreate&rvt-key=user-123&rvt-pool=default"

curl -X POST "$URL/prompt?$AGENT" \
  -H "Content-Type: application/json" \
  -d '{"args": ["Write a hello-world Rivet Actor to counter.ts, then read the file back to me.", {"requestId": "counter-1"}]}'

Each action is a POST to the gateway with its arguments in args. See Call Action.

The prompt action waits for the run to finish and returns { status, text }. A model error doesn’t reject it: status is unanswered, with Pi’s reason. Sending the same requestId again returns the first answer instead of running twice.

Add a sandbox (optional)

To let the agent run commands, give it a sandbox and Pi’s CodingTools, which add bash:

npm add @rivet-dev/sandbox-adapter e2b

Set E2B_API_KEY for E2B, or install @daytonaio/sdk and set DAYTONA_API_KEY for Daytona. See Sandboxes.

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:

Next: Pi, for how a run survives crashes and upgrades, and its known limitations.