website/src/content/docs/agent-os/index.mdx
import { faRocket } from "@rivet-gg/icons";
<Note> agentOS is in preview and the API is subject to change. If you run into issues, please [report them on GitHub](https://github.com/rivet-dev/rivet/issues) or [join our Discord](https://rivet.dev/discord). </Note>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));
```ts @nocheck 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 vm = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { vm } });
registry.start();
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.
<CodeGroup> ```ts @nocheck 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);
```ts @nocheck 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 vm = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { vm } });
registry.start();
Approve or deny agent tool use with human-in-the-loop patterns or auto-approve for trusted workloads.
<CodeGroup stacked> ```ts @nocheck 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();
```ts @nocheck 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"]);
// 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");
});
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
Read, write, and manage files inside the VM. The /home/user directory is persisted automatically across sleep/wake cycles.
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);
```ts @nocheck 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 vm = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { vm } });
registry.start();
Execute commands, spawn long-running processes, and open interactive shells.
<CodeGroup> ```ts @nocheck 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([${data.processId}], data.output);
});
const proc = await agent.spawn("node", ["server.js"]); console.log("Process ID:", proc.processId);
```ts @nocheck 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 vm = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { vm } });
registry.start();
Proxy HTTP requests into VMs with vmFetch. Create preview URLs for port forwarding VM services to shareable public URLs.
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));
```ts @nocheck 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 vm = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { vm } });
registry.start();
Schedule recurring commands and agent sessions with cron expressions.
<CodeGroup> ```ts @nocheck 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", agent: "pi", prompt: "Review the codebase for security issues and write a report to /home/user/audit.md", }, });
```ts @nocheck 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 vm = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { vm } });
registry.start();
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();
Connect multiple clients to the same agent VM. All subscribers see session output, process logs, and shell data in realtime.
<CodeGroup> ```ts @nocheck 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
```ts @nocheck 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 vm = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { vm } });
registry.start();
Compose specialized agents into pipelines. Each agent gets its own isolated VM and filesystem.
<CodeGroup stacked> ```ts @nocheck 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();
```ts @nocheck client.ts
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",
);
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();
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}%`,
);
},
},
});