Custom Agents
A custom agent is a program that runs inside the VM to drive a coding agent. agentOS spawns it when you call createSession() and talks to it over the Agent Client Protocol. You ship it as a software package, exactly like the built-in agents.
Agent Client Protocol (ACP)
Section titled “Agent Client Protocol (ACP)”agentOS speaks the Agent Client Protocol (ACP) to every agent: JSON-RPC over stdio. The agent reads protocol messages on stdin and writes them on stdout, so stdout is reserved for ACP and stderr is used for logs. Your program only needs to speak ACP; how it runs the underlying model is up to you. See the ACP documentation for the full protocol.
Two ways to build an agent
Section titled “Two ways to build an agent”There are two shapes, depending on whether the agent runs in the ACP process or in its own.
Single process (embedded)
Section titled “Single process (embedded)”The ACP adapter embeds the agent SDK and runs it in the same process. One process inside the VM, lower memory footprint.
For example, an adapter to run OpenCode, which speaks ACP natively. One package is both the ACP process and the agent, so there’s no separate adapter and nothing else is spawned.
import { defineSoftware } from "@rivet-dev/agentos";import { dirname, resolve } from "node:path";import { fileURLToPath } from "node:url";
const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
// Example: an adapter to run OpenCode.export default defineSoftware({ name: "opencode", type: "agent", packageDir, // A single package provides everything that runs in the VM. requires: ["@agentos-software/opencode"], agent: { id: "opencode", // Same package for both: OpenCode *is* the ACP process. It speaks ACP // on stdio itself, so there is no separate adapter to spawn the agent. acpAdapter: "@agentos-software/opencode", agentPackage: "@agentos-software/opencode", staticEnv: { OPENCODE_DISABLE_CONFIG_DEP_INSTALL: "1", OPENCODE_DISABLE_EMBEDDED_WEB_UI: "1", }, },});ACP adapter (separate agent)
Section titled “ACP adapter (separate agent)”The ACP adapter is a thin bridge that spawns the real agent as its own process (a CLI or SDK) and translates between it and ACP. Full agent feature set, higher memory.
For example, an adapter to run Pi: the pi CLI doesn’t speak ACP, so pi-acp speaks ACP and spawns the CLI as a separate process. The descriptor names two packages, the adapter and the agent.
import { defineSoftware } from "@rivet-dev/agentos";import { dirname, resolve } from "node:path";import { fileURLToPath } from "node:url";
const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
// Example: a pi-acp adapter that runs the Pi CLI.export default defineSoftware({ name: "pi-cli", type: "agent", packageDir, // Two separate packages: the ACP adapter, and the agent it drives. requires: ["pi-acp", "@mariozechner/pi-coding-agent"], agent: { id: "pi-cli", // `pi-acp` is a thin ACP adapter. It does NOT run the agent itself: // it speaks ACP on stdio and spawns the `pi` CLI as a separate child // process, translating between ACP and the CLI. acpAdapter: "pi-acp", // The actual agent, launched by pi-acp as its own process. agentPackage: "@mariozechner/pi-coding-agent", // Tell the adapter where to find the `pi` CLI inside the VM // (resolved at boot to a guest path under /root/node_modules). env: (ctx) => ({ PI_ACP_PI_COMMAND: ctx.resolveBin("@mariozechner/pi-coding-agent", "pi"), }), },});Use your agent
Section titled “Use your agent”Register the package on the server with software. Sessions are then created from the client by id, exactly like any built-in agent.
import { agentOS, setup, defineSoftware } from "@rivet-dev/agentos";
const myAgent = defineSoftware({ type: "agent", /* ...name, packageDir, requires, agent (see above)... */});
const vm = agentOS({ software: [myAgent] });
export const registry = setup({ use: { vm } });registry.start();See Sessions for creating and driving sessions. Ship your adapter as a package so its dependencies resolve from node_modules/ (via requires) inside the VM, rather than as a loose file.
All built-in agents are defined exactly this way. Browse them for reference on GitHub.
Read more
Section titled “Read more”- Defining software packages: the full descriptor reference, including every
agentfield (staticEnv,env,launchArgs), theSoftwareContexthelpers, and the tool and WASM-command software types. - Building binaries: compile WASM command binaries and use the registry.
Debugging
Section titled “Debugging”When a custom agent exits mid-turn or a tool call fails, capture the agent’s stderr with the onAgentStderr hook on AgentOs.create(). The agent uses stdout for ACP, so stderr carries its logs and crash output. See Debugging for that hook and the runtime (sidecar) logs.