Skip to content
GitHub Get Started
General

Crash Course

  • Isolated VMs: Each agent gets its own filesystem, processes, and networking. No shared state, no cross-contamination.
  • Multi-Agent Support: Run Amp, Claude Code, Codex, OpenCode, and PI with a unified API. Swap agents without changing your code.
  • Host Tools: Expose your JavaScript functions to agents as CLI commands. Direct binding with near-zero latency and automatic code mode for up to 80% token reduction.
  • Persistent State: Filesystem and transcripts survive sleep/wake cycles automatically. No external database needed.
  • Orchestration: Workflows, queues, cron jobs, and multi-agent coordination built on Rivet Actors.
  • Hybrid Sandboxes: Run agents in the lightweight VM by default. Spin up a full sandbox on demand for browsers, compilation, and desktop automation.
  • Coding agents: Run any coding agent with full OS access, file editing, shell execution, and tool use.
  • Automated pipelines: CI-like workflows where agents clone repos, fix bugs, run tests, and open PRs.
  • Multi-agent systems: Coordinators dispatching to specialized agents, review pipelines, planning chains.
  • Scheduled maintenance: Cron-based agents that audit code, update dependencies, or generate reports.
  • Collaborative workspaces: Multiple users observing and interacting with the same agent session in realtime.
client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>("http://localhost:6420");
const agent = client.vm.getOrCreate(["my-agent"]);
// Subscribe to streaming events
agent.on("sessionEvent", (data) => {
console.log(data.event);
});
// Create a session and send a prompt
const session = await agent.createSession("pi", {
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
const response = await agent.sendPrompt(
session.sessionId,
"Write a hello world script to /home/user/hello.js",
);
console.log(response);
// Read the file the agent created
const content = await agent.readFile("/home/user/hello.js");
console.log(new TextDecoder().decode(content));

After the quickstart, customize your agent with the Registry.

Create agent sessions, send prompts, and stream responses in realtime. Transcripts are persisted automatically across sleep/wake cycles.

client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>("http://localhost:6420");
const agent = client.vm.getOrCreate(["my-agent"]);
// Stream events as they arrive
agent.on("sessionEvent", (data) => {
console.log(data.event.method, data.event);
});
// Create a session with MCP servers
const session = await agent.createSession("pi", {
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
mcpServers: [
{
type: "local",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"],
env: {},
},
],
});
// Send a prompt and wait for the response
const response = await agent.sendPrompt(
session.sessionId,
"List all files in the home directory",
);
console.log(response);

Documentation

Approve or deny agent tool use with human-in-the-loop patterns or auto-approve for trusted workloads.

server.ts
import { agentOs } from "rivetkit/agent-os";
import { setup } from "rivetkit";
import common from "@rivet-dev/agent-os-common";
import pi from "@rivet-dev/agent-os-pi";
// Auto-approve all permissions server-side
const vm = agentOs({
onPermissionRequest: async (c, sessionId, request) => {
await c.respondPermission(sessionId, request.permissionId, "always");
},
options: { software: [common, pi] },
});
export const registry = setup({ use: { vm } });
registry.start();

Documentation

Expose your JavaScript functions to agents as CLI commands inside the VM. Agents call them as shell commands with auto-generated flags from Zod schemas.

import { toolKit, hostTool } from "@rivet-dev/agent-os-core";
import { z } from "zod";
const myTools = toolKit({
name: "myapp",
description: "Application tools",
tools: {
createTicket: hostTool({
description: "Create a ticket in the issue tracker",
inputSchema: z.object({
title: z.string().describe("Ticket title"),
priority: z.enum(["low", "medium", "high"]).describe("Priority level"),
}),
execute: async (input) => {
const ticket = await db.tickets.create(input);
return { id: ticket.id, url: ticket.url };
},
}),
},
});
// Agent calls: agentos-myapp createTicket --title "Fix login" --priority high

Documentation

Read, write, and manage files inside the VM. The /home/user directory is persisted automatically across sleep/wake cycles.

client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>("http://localhost:6420");
const agent = client.vm.getOrCreate(["my-agent"]);
// Write a file
await agent.writeFile("/home/user/config.json", JSON.stringify({ key: "value" }));
// Read a file
const content = await agent.readFile("/home/user/config.json");
console.log(new TextDecoder().decode(content));
// List directory contents recursively
const files = await agent.readdirRecursive("/home/user", { maxDepth: 2 });
console.log(files);

Documentation

Execute commands, spawn long-running processes, and open interactive shells.

client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>("http://localhost:6420");
const agent = client.vm.getOrCreate(["my-agent"]);
// One-shot execution
const result = await agent.exec("echo hello && ls /home/user");
console.log("stdout:", result.stdout);
console.log("exit code:", result.exitCode);
// Spawn a long-running process
agent.on("processOutput", (data) => {
console.log(`[pid ${data.pid}]`, new TextDecoder().decode(data.data));
});
const { pid } = await agent.spawn("node", ["server.js"]);
console.log("Process ID:", pid);

Documentation

Proxy HTTP requests into VMs with vmFetch. Create preview URLs for port forwarding VM services to shareable public URLs.

client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>("http://localhost:6420");
const agent = client.vm.getOrCreate(["my-agent"]);
// Fetch from a service running inside the VM
const response = await agent.vmFetch(3000, "/api/health");
console.log("Status:", response.status);
// Create a preview URL (port forwarding to a public URL)
const preview = await agent.createSignedPreviewUrl(3000);
console.log("Public URL:", preview.path);
console.log("Expires at:", new Date(preview.expiresAt));

Documentation

Schedule recurring commands and agent sessions with cron expressions.

client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>("http://localhost:6420");
const agent = client.vm.getOrCreate(["my-agent"]);
// Schedule a command every hour
await agent.scheduleCron({
schedule: "0 * * * *",
action: { type: "exec", command: "rm", args: ["-rf", "/tmp/cache/*"] },
});
// Schedule an agent session daily at 9 AM
await agent.scheduleCron({
schedule: "0 9 * * *",
action: {
type: "session",
agentType: "pi",
prompt: "Review the codebase for security issues and write a report to /home/user/audit.md",
},
});

Documentation

agentOS uses a hybrid model: agents run in a lightweight VM by default and mount a full sandbox on demand for heavy workloads like browsers, compilation, and desktop automation. Sandboxes are powered by Sandbox Agent, so you can swap providers without changing agent code. Mount the sandbox as a filesystem and expose its process management as host tools.

import { agentOs } from "rivetkit/agent-os";
import { setup } from "rivetkit";
import { SandboxAgent } from "sandbox-agent";
import { DockerProvider } from "sandbox-agent/docker";
import common from "@rivet-dev/agent-os-common";
import pi from "@rivet-dev/agent-os-pi";
import { createSandboxFs, createSandboxToolkit } from "@rivet-dev/agent-os-sandbox";
const sandbox = await SandboxAgent.start({
sandbox: new DockerProvider(),
});
const vm = agentOs({
options: {
software: [common, pi],
mounts: [
{
path: "/sandbox",
driver: createSandboxFs({ client: sandbox }),
},
],
toolKits: [createSandboxToolkit({ client: sandbox })],
},
});
export const registry = setup({ use: { vm } });
registry.start();

Documentation

Connect multiple clients to the same agent VM. All subscribers see session output, process logs, and shell data in realtime.

client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
// Client A: creates the session and sends prompts
const clientA = createClient<typeof registry>("http://localhost:6420");
const agentA = clientA.vm.getOrCreate(["shared-agent"]);
agentA.on("sessionEvent", (data) => console.log("[A]", data.event.method));
const session = await agentA.createSession("pi", {
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
await agentA.sendPrompt(session.sessionId, "Build a REST API");
// Client B: observes the same session (separate process)
const clientB = createClient<typeof registry>("http://localhost:6420");
const agentB = clientB.vm.getOrCreate(["shared-agent"]);
agentB.on("sessionEvent", (data) => console.log("[B]", data.event.method));
// Client B sees the same events as Client A

Documentation

Compose specialized agents into pipelines. Each agent gets its own isolated VM and filesystem.

server.ts
import { agentOs } from "rivetkit/agent-os";
import { setup } from "rivetkit";
import common from "@rivet-dev/agent-os-common";
import pi from "@rivet-dev/agent-os-pi";
const coder = agentOs({
options: { software: [common, pi] },
});
const reviewer = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { coder, reviewer } });
registry.start();

Documentation

Orchestrate multi-step agent tasks with durable workflows that survive crashes and restarts.

import { agentOs } from "rivetkit/agent-os";
import common from "@rivet-dev/agent-os-common";
import pi from "@rivet-dev/agent-os-pi";
import { actor, setup, workflow } from "rivetkit";
const automator = actor({
workflows: {
fixBug: workflow<{ repo: string; issue: string }>(),
},
run: async (c) => {
for await (const message of c.workflow.iter("fixBug")) {
const { repo, issue } = message.body;
const agentHandle = c.actors.vm.getOrCreate([`fix-${issue}`]);
await c.step("clone-repo", async (c) => {
return agentHandle.exec(`git clone ${repo} /home/user/repo`);
});
await c.step("fix-bug", async (c) => {
const session = await agentHandle.createSession("pi", {
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
const response = await agentHandle.sendPrompt(
session.sessionId,
`Fix the bug described in issue: ${issue}`,
);
await agentHandle.closeSession(session.sessionId);
return response;
});
await c.step("run-tests", async (c) => {
return agentHandle.exec("cd /home/user/repo && npm test");
});
await message.complete();
}
},
});
const vm = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { automator, vm } });
registry.start();

Documentation

Use actor-local SQLite as structured long-term memory that persists across sessions and sleep/wake cycles.

import { actor, setup } from "rivetkit";
import { db } from "rivetkit/db";
const memoryAgent = actor({
db: db({
onMigrate: async (db) => {
await db.execute(`
CREATE TABLE IF NOT EXISTS memories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
category TEXT NOT NULL,
content TEXT NOT NULL,
created_at INTEGER NOT NULL
);
`);
},
}),
actions: {
store: async (c, sessionId: string, category: string, content: string) => {
await c.db.execute(
"INSERT INTO memories (session_id, category, content, created_at) VALUES (?, ?, ?, ?)",
sessionId, category, content, Date.now(),
);
},
search: async (c, query: string) => {
return c.db.execute(
"SELECT category, content FROM memories WHERE content LIKE ? ORDER BY created_at DESC LIMIT 20",
`%${query}%`,
);
},
},
});

Documentation