Crash Course
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 "@rivet-dev/agentos/client";import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });const agent = client.vm.getOrCreate("my-agent");
// Subscribe to streaming eventsconst conn = agent.connect();conn.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.text);
// Read the file the agent createdconst content = await agent.readFile("/home/user/hello.js");console.log(new TextDecoder().decode(content));After the quickstart, customize your agent with the Registry.
Agents
Section titled “Agents”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 "@rivet-dev/agentos/client";import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });const agent = client.vm.getOrCreate("my-agent");
// Stream events as they arriveconst conn = agent.connect();conn.on("sessionEvent", (data) => { console.log(data.event.method, data.event);});
// Create a sessionconst session = await agent.createSession("pi", { env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },});
// Send a prompt and wait for the responseconst response = await agent.sendPrompt( session.sessionId, "List all files in the home directory",);console.log(response.text);See Full Example or Documentation
Approvals
Section titled “Approvals”Approve or deny agent tool use with human-in-the-loop patterns or auto-approve for trusted workloads.
import { agentOS, setup } from "@rivet-dev/agentos";import pi from "@agentos-software/pi";
// Auto-approve all permissions server-sideconst vm = agentOS({ software: [pi], onPermissionRequest: async (sessionId, request) => { console.log("Auto-approving", sessionId, request.permissionId); },});
export const registry = setup({ use: { vm } });registry.start();See Full Example or Documentation
Bindings
Section titled “Bindings”Expose your JavaScript functions to agents as CLI commands inside the VM. Each binding group becomes a binary at /usr/local/bin/agentos-{name}, and each binding becomes a subcommand with flags auto-generated from its Zod input schema. The server below defines a weather binding group with a forecast binding; the client opens a session and prompts the agent, which calls the binding itself as a shell command.
import { agentOS, setup } from "@rivet-dev/agentos";import { z } from "zod";
// Define a group of bindings (host functions). Each binding has a Zod input// schema and an `execute` handler that runs on the host. Bindings are exposed to// the agent as CLI commands at /usr/local/bin/agentos-{name} inside the VM.const weatherBindings = { name: "weather", description: "Weather data bindings", bindings: { forecast: { description: "Get the weather forecast for a city", inputSchema: z.object({ city: z.string().describe("City name"), days: z.number().optional().describe("Number of days"), }), execute: async (input: { city: string; days?: number }) => { const res = await fetch( `https://api.weather.example/forecast?city=${input.city}&days=${input.days ?? 3}`, ); return res.json(); }, examples: [ { description: "3-day forecast for Paris", input: { city: "Paris", days: 3 } }, ], }, },};
const vm = agentOS({ bindings: [weatherBindings],});
export const registry = setup({ use: { vm } });registry.start();See Full Example or Documentation
Agent-to-Agent
Section titled “Agent-to-Agent”Let one agent call another through a binding. The coder gets a review binding it invokes itself, which bridges into the reviewer’s isolated VM.
import { agentOS, setup } from "@rivet-dev/agentos";import { createClient } from "@rivet-dev/agentos/client";import { z } from "zod";import pi from "@agentos-software/pi";
// The reviewer is its own isolated agent VM.const reviewer = agentOS({ software: [pi] });
// The coder gets a `review` binding it can call itself: it copies a file from the// coder's VM into the reviewer's VM and asks the reviewer to review it.const coder = agentOS({ software: [pi], bindings: [ { name: "review", description: "Send a file to the reviewer agent and get back a review.", bindings: { submit: { description: "Submit a file path for review by the reviewer agent.", inputSchema: z.object({ path: z.string() }), execute: async ({ path }: { path: string }) => { const client = createClient<typeof registry>({ endpoint: "http://localhost:6420", }); const content = await client.coder .getOrCreate("feature-auth") .readFile(path); const reviewerHandle = client.reviewer.getOrCreate("feature-auth"); await reviewerHandle.writeFile(path, content); const session = await reviewerHandle.createSession("pi", { env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, }); const result = await reviewerHandle.sendPrompt( session.sessionId, `Review ${path} for security issues`, ); return { review: result.text }; }, }, }, }, ],});
export const registry = setup({ use: { coder, reviewer } });registry.start();See Full Example or Documentation
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 "@rivet-dev/agentos/client";import type { registry } from "./server";
// Client A: creates the session and sends promptsconst clientA = createClient<typeof registry>({ endpoint: "http://localhost:6420" });const agentA = clientA.vm.getOrCreate("shared-agent");const connA = agentA.connect();connA.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>({ endpoint: "http://localhost:6420" });const connB = clientB.vm.getOrCreate("shared-agent").connect();connB.on("sessionEvent", (data) => console.log("[B]", data.event.method),);// Client B sees the same events as Client ASee Full Example or Documentation
Workflows
Section titled “Workflows”Orchestrate multi-step agent tasks with durable workflows that survive crashes and restarts.
import { agentOS, setup } from "@rivet-dev/agentos";import { actor, queue } from "rivetkit";import { workflow } from "rivetkit/workflow";import pi from "@agentos-software/pi";
const vm = agentOS({ software: [pi] });
// A durable workflow actor. Its `run` is built with `workflow()`, so every// `step(...)` is recorded, retried, and resumed: if the process crashes// mid-run, replay skips completed steps and continues where it left off.const bugFixer = actor({ queues: { fixBug: queue<{ repo: string; issue: string }>(), }, run: workflow(async (ctx) => { await ctx.loop("fix-bug-loop", async (loopCtx) => { // Wait durably for the next bug-fix request from the queue. const message = await loopCtx.queue.next("wait-fix-bug"); const { repo, issue } = message.body; const agent = loopCtx.client<typeof registry>().vm.getOrCreate("bug-fixer");
await loopCtx.step("clone-repo", () => agent.exec(`git clone ${repo} /home/user/repo`), );
await loopCtx.step("fix-bug", async () => { const session = await agent.createSession("claude", { env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, }); await agent.sendPrompt(session.sessionId, `Fix the bug in issue: ${issue}`); await agent.closeSession(session.sessionId); });
await loopCtx.step("run-tests", () => agent.exec("cd /home/user/repo && npm test"), ); }); }),});
export const registry = setup({ use: { vm, bugFixer } });registry.start();Operating System
Section titled “Operating System”Filesystem
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 "@rivet-dev/agentos/client";import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "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);See Full Example or Documentation
Processes & Shell
Section titled “Processes & Shell”Execute commands, spawn long-running processes, and open interactive shells.
import { createClient } from "@rivet-dev/agentos/client";import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "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 processconst conn = agent.connect();conn.on("processOutput", (data) => { console.log(`[pid ${data.pid}]`, new TextDecoder().decode(data.data));});
const { pid } = await agent.spawn("node", ["/home/user/server.js"]);console.log("Process ID:", pid);See Full Example or Documentation
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 "@rivet-dev/agentos/client";import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "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));See Full Example or Documentation
Cron Jobs
Section titled “Cron Jobs”Schedule recurring commands and agent sessions with cron expressions.
import { createClient } from "@rivet-dev/agentos/client";import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "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", },});See Full Example or Documentation
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 bindings.
import { agentOS, setup } from "@rivet-dev/agentos";import { createSandboxFs, createSandboxBindings } from "@rivet-dev/agentos-sandbox";import { SandboxAgent } from "sandbox-agent";import { docker } from "sandbox-agent/docker";
const sandbox = await SandboxAgent.start({ sandbox: docker() });
const vm = agentOS({ // Bindings let the agent control the sandbox bindings: [createSandboxBindings({ client: sandbox })], // Mounts let the agent read the sandbox filesystem (optional) mounts: [ { path: "/home/user/sandbox", plugin: createSandboxFs({ client: sandbox }) }, ],});
export const registry = setup({ use: { vm } });registry.start();