Overview
Run coding agents inside isolated VMs with full filesystem, process, and network control.
agentOS is in preview and the API is subject to change. If you run into issues, please report them on GitHub or join our Discord.
Quickstart
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
- 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
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));
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
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 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);
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
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-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();
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-loop
agent.on("permissionRequest", async (data) => {
console.log("Permission requested:", data.request);
// "once" | "always" | "reject"
await agent.respondPermission(data.sessionId, data.request.permissionId, "once");
});
Tools
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
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 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);
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
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 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(`[${data.processId}]`, data.output);
});
const proc = await agent.spawn("node", ["server.js"]);
console.log("Process ID:", proc.processId);
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
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 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));
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
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 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",
agent: "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 Extension
agentOS uses a hybrid model: agents run in a lightweight VM by default and spin up a full sandbox on demand for heavy workloads like browsers, compilation, and desktop automation.
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],
sandbox: {
enabled: true,
},
},
});
export const registry = setup({ use: { vm } });
registry.start();
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 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
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();
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 feature
const 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 reviewer
const 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 code
const 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
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 () => {
return agentHandle.exec(`git clone ${repo} /home/user/repo`);
});
await c.step("fix-bug", async () => {
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 () => {
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
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}%`,
);
},
},
});