Crash Course
Features
Section titled “Features”- 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.
When to Use agentOS
Section titled “When to Use agentOS”- 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.
Minimal Project
Section titled “Minimal Project”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 eventsagent.on("sessionEvent", (data) => { console.log(data.event);});
// Create a session and send a promptconst 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 createdconst content = await agent.readFile("/home/user/hello.js");console.log(new TextDecoder().decode(content));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 vm = agentOs({ options: { software: [common, pi] },});
export const registry = setup({ use: { vm } });registry.start();After the quickstart, customize your agent with the Registry.
Quick Reference
Section titled “Quick Reference”Sessions & Transcripts
Section titled “Sessions & Transcripts”Create agent sessions, send prompts, and stream responses in realtime. Transcripts are persisted automatically across sleep/wake cycles.
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 arriveagent.on("sessionEvent", (data) => { console.log(data.event.method, data.event);});
// Create a session with MCP serversconst 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 responseconst response = await agent.sendPrompt( session.sessionId, "List all files in the home directory",);console.log(response);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 vm = agentOs({ options: { software: [common, pi] },});
export const registry = setup({ use: { vm } });registry.start();Permissions
Section titled “Permissions”Approve or deny agent tool use with human-in-the-loop patterns or auto-approve for trusted workloads.
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-sideconst 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();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"]);
// Or handle permissions client-side for human-in-the-loopagent.on("permissionRequest", async (data) => { console.log("Permission requested:", data.request); // "once" | "always" | "reject" await agent.respondPermission(data.sessionId, data.request.permissionId, "once");});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 highFilesystem
Section titled “Filesystem”Read, write, and manage files inside the VM. The /home/user directory is persisted automatically across sleep/wake cycles.
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 fileawait agent.writeFile("/home/user/config.json", JSON.stringify({ key: "value" }));
// Read a fileconst content = await agent.readFile("/home/user/config.json");console.log(new TextDecoder().decode(content));
// List directory contents recursivelyconst files = await agent.readdirRecursive("/home/user", { maxDepth: 2 });console.log(files);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 vm = agentOs({ options: { software: [common, pi] },});
export const registry = setup({ use: { vm } });registry.start();Processes & Shell
Section titled “Processes & Shell”Execute commands, spawn long-running processes, and open interactive shells.
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 executionconst result = await agent.exec("echo hello && ls /home/user");console.log("stdout:", result.stdout);console.log("exit code:", result.exitCode);
// Spawn a long-running processagent.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);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 vm = agentOs({ options: { software: [common, pi] },});
export const registry = setup({ use: { vm } });registry.start();Networking & Previews
Section titled “Networking & Previews”Proxy HTTP requests into VMs with vmFetch. Create preview URLs for port forwarding VM services to shareable public URLs.
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 VMconst 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));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 vm = agentOs({ options: { software: [common, pi] },});
export const registry = setup({ use: { vm } });registry.start();Cron Jobs
Section titled “Cron Jobs”Schedule recurring commands and agent sessions with cron expressions.
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 hourawait agent.scheduleCron({ schedule: "0 * * * *", action: { type: "exec", command: "rm", args: ["-rf", "/tmp/cache/*"] },});
// Schedule an agent session daily at 9 AMawait 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", },});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 vm = agentOs({ options: { software: [common, pi] },});
export const registry = setup({ use: { vm } });registry.start();Sandbox Mounting
Section titled “Sandbox Mounting”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();Multiplayer & Realtime
Section titled “Multiplayer & Realtime”Connect multiple clients to the same agent VM. All subscribers see session output, process logs, and shell data in realtime.
import { createClient } from "rivetkit/client";import type { registry } from "./server";
// Client A: creates the session and sends promptsconst 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 Aimport { 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 vm = agentOs({ options: { software: [common, pi] },});
export const registry = setup({ use: { vm } });registry.start();Agent-to-Agent
Section titled “Agent-to-Agent”Compose specialized agents into pipelines. Each agent gets its own isolated VM and filesystem.
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();import { createClient } from "rivetkit/client";import type { registry } from "./server";
const client = createClient<typeof registry>("http://localhost:6420");
// Coder writes the featureconst coderAgent = client.coder.getOrCreate(["feature-auth"]);const coderSession = await coderAgent.createSession("pi", { env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },});await coderAgent.sendPrompt(coderSession.sessionId, "Implement the login feature");
// Pass files to the reviewerconst src = await coderAgent.readFile("/home/user/src/auth.ts");const reviewerAgent = client.reviewer.getOrCreate(["feature-auth"]);await reviewerAgent.writeFile("/home/user/src/auth.ts", src);
// Reviewer checks the codeconst reviewSession = await reviewerAgent.createSession("pi", { env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },});await reviewerAgent.sendPrompt( reviewSession.sessionId, "Review auth.ts for security issues",);Workflows
Section titled “Workflows”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();SQLite
Section titled “SQLite”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}%`, ); }, },});