Processes & Shell
Run commands, start long-lived processes, and open interactive shells inside agentOS VMs with controlled environment and lifecycle management.
Run commands with one-shot exec, spawn long-running processes with streaming stdout/stderr and stdin, manage their lifecycle (stop, kill, wait, inspect), open interactive PTY-backed shells, and inspect the process tree across all VM runtimes.
One-shot execution
Use exec to run a command and wait for completion. Returns stdout, stderr, and exit code.
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });
const result = await client.vm
.getOrCreate("my-agent")
.process.exec("echo hello && ls /home/agentos");
console.log("stdout:", result.stdout);
console.log("stderr:", result.stderr);
console.log("exit code:", result.exitCode);
Spawn a long-running process
Use spawn for processes that run in the background. Call connect() and subscribe to native processOutput and processExit events, filtering their pid in application code.
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");
const conn = agent.connect();
// Spawn a dev server
const { pid } = await agent.process.spawn("node", ["/home/agentos/server.js"]);
// Subscribe to process output
conn.on("processOutput", (data) => {
if (data.pid !== pid) return;
const text = new TextDecoder().decode(data.data);
console.log(`[pid ${data.pid}] ${data.stream}: ${text}`);
});
conn.on("processExit", (data) => {
if (data.pid !== pid) return;
console.log(`[pid ${data.pid}] exited with code ${data.exitCode}`);
});
console.log("Started process:", pid);
Write to stdin
Send input to a running process.
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");
const { pid } = await agent.process.spawn("cat", []);
// Write to stdin
await agent.process.writeStdin(pid, "hello from stdin\n");
// Close stdin when done
await agent.process.closeStdin(pid);
// Wait for the process to exit
const exitCode = await agent.process.wait(pid);
console.log("exit code:", exitCode);
Process lifecycle
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");
const { pid } = await agent.process.spawn("node", ["/home/agentos/server.js"]);
const processStatus = (process: {
running: boolean;
exitCode?: number | null;
}) => (process.running ? "running" : `exited ${process.exitCode ?? ""}`.trim());
// List all processes tracked by the VM
const processes = await agent.process.list();
for (const p of processes) {
console.log(p.pid, p.command, p.args.join(" "), processStatus(p));
}
// Inspect a specific process by pid
const info = await agent.process.get(pid);
console.log(processStatus(info), info.exitCode);
// Graceful stop (SIGTERM)
await agent.process.signal(pid, "SIGTERM");
// Force kill (SIGKILL)
await agent.process.kill(pid);
Interactive shells
Open an interactive shell with PTY support. Subscribe to native shellData, shellStderr, and shellExit events, filtering their shellId in application code.
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");
const conn = agent.connect();
// Spawn an interactive shell process
const { pid } = await agent.process.spawn("sh", []);
// Stream this process's output as it is produced
conn.on("processOutput", (data) => {
if (data.pid !== pid) return;
const text = new TextDecoder().decode(data.data);
process.stdout.write(text);
});
// Drive it by writing commands to stdin
await agent.process.writeStdin(pid, "ls -la /home/agentos\n");
// Close stdin to let the shell exit, then wait for it
await agent.process.closeStdin(pid);
await agent.process.wait(pid);
Embedded API
Process and terminal methods are identical except for actor-only shell listing and replay. Embedded output and exit callbacks are already scoped to one VM and one host process.
// One-shot execution
const result = await vm.process.exec("ls -la /home/agentos");
console.log(result.stdout);
// Long-running process with portable output and exit subscriptions.
await vm.filesystem.writeFile(
"/tmp/server.mjs",
'import http from "http"; http.createServer((req, res) => res.end("ok")).listen(3000); console.log("listening");',
);
const { pid } = await vm.process.spawn("node", ["/tmp/server.mjs"]);
vm.onProcessOutput(pid, (event) =>
console.log(event.stream, new TextDecoder().decode(event.data)),
);
vm.onProcessExit(pid, (event) => console.log("exited:", event.exitCode));
// Write to stdin
await vm.process.writeStdin(pid, "some input\n");
// Stop or kill
await vm.process.signal(pid, "SIGTERM");
Read more in the embedded API quickstart.