Custom Agents
Bring your own coding agent to agentOS by speaking the Agent Client Protocol (ACP) inside the VM.
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)
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
There are two shapes, depending on whether the agent runs in the ACP process or in its own.
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 opencode from "@agentos-software/opencode";
// OpenCode *is* the ACP process: it speaks ACP on stdio itself, so there is no
// separate adapter to spawn the agent. The published @agentos-software/opencode
// descriptor already encodes the right entrypoint and env, so use it as-is.
export default opencode;
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 packaged agent bundles both — its agentos-package.json names pi-acp as the acpEntrypoint and points it at the pi CLI via agent.env.
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.
// The agent block lives in the package's agentos-package.json, generated by `agentos-toolchain pack --agent`.
export default defineSoftware({ packageDir });
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({
packageDir, // the packaged agent directory; its agentos-package.json carries the agent block
});
const vm = agentOS({ software: [myAgent] });
export const registry = setup({ use: { vm } });
registry.start();
See Sessions for creating and driving sessions. Package your adapter with agentos-toolchain pack --agent my-agent-acp so its dependencies are bundled into the self-contained package directory and the agent block (naming the bin/ ACP entrypoint) is written into the package’s agentos-package.json, rather than shipping it as a loose file.
All built-in agents are defined exactly this way. Browse them for reference on GitHub.
Read more
- Defining software packages: the full descriptor reference, including the
agentos-package.jsonschema and everyagentfield (acpEntrypoint,env,launchArgs,snapshot). - Building binaries: compile WASM command binaries and use the registry.
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.