Reference
Core Package
Use @rivet-dev/agent-os-core standalone for direct VM control without the Rivet Actor runtime.
- Standalone SDK for programmatic VM management without the actor runtime
- ID-based flat API where sessions are referenced by ID strings
- No persistence by default. Filesystem and sessions live in memory only.
- No event broadcasting, sleep/wake, or preview URLs. You manage the lifecycle.
agentOS vs agentOS Core
The agentOs() actor (from rivetkit/agent-os) wraps the core package and adds:
Core (@rivet-dev/agent-os-core) | Actor (rivetkit/agent-os) | |
|---|---|---|
| Persistence | In-memory by default (pluggable via mounts) | Persistent filesystem and sessions |
| Sleep/wake | Manual dispose() / create() | Automatic with 15 min grace period |
| Events | Direct callbacks | Broadcasted to all connected clients |
| Preview URLs | None | Built-in signed URL server |
| Sessions | ID-based (vm.prompt(sessionId, ...)) | ID-based flat API over RPC |
| Multiplayer | N/A | Multiple clients on same actor |
| Orchestration | N/A | Workflows, queues, cron |
Use the core package when you want direct VM control in a script, CLI tool, or custom runtime. Use the actor when you want persistence, multiplayer, and the full Rivet platform.
Install
npm install @rivet-dev/agent-os-core
Boot a VM
import { AgentOs } from "@rivet-dev/agent-os-core";
import common from "@rivet-dev/agent-os-common";
const vm = await AgentOs.create({
software: [common],
});
// Run a command
const result = await vm.exec("echo hello");
console.log(result.stdout); // "hello\n"
await vm.dispose();
Filesystem
import { AgentOs } from "@rivet-dev/agent-os-core";
import common from "@rivet-dev/agent-os-common";
const vm = await AgentOs.create({ software: [common] });
await vm.writeFile("/home/user/hello.txt", "Hello, world!");
const content = await vm.readFile("/home/user/hello.txt");
console.log(new TextDecoder().decode(content));
await vm.mkdir("/home/user/src");
await vm.writeFiles([
{ path: "/home/user/src/index.ts", content: "console.log('hi');" },
{ path: "/home/user/src/utils.ts", content: "export const add = (a: number, b: number) => a + b;" },
]);
const entries = await vm.readdirRecursive("/home/user");
for (const entry of entries) {
console.log(entry.type, entry.path);
}
await vm.dispose();
Processes
import { AgentOs } from "@rivet-dev/agent-os-core";
import common from "@rivet-dev/agent-os-common";
const vm = await AgentOs.create({ software: [common] });
// One-shot execution
const result = await vm.exec("ls -la /home/user");
console.log(result.stdout);
// Long-running process with streaming output
await vm.writeFile("/tmp/server.mjs", 'import http from "http"; http.createServer((req, res) => res.end("ok")).listen(3000); console.log("listening");');
const proc = vm.spawn("node", ["/tmp/server.mjs"]);
vm.onProcessStdout(proc.pid, (data) => {
console.log("stdout:", new TextDecoder().decode(data));
});
vm.onProcessExit(proc.pid, (code) => {
console.log("exited:", code);
});
// Write to stdin
vm.writeProcessStdin(proc.pid, "some input\n");
// Stop or kill
vm.stopProcess(proc.pid);
await vm.dispose();
Agent sessions
The core package returns a sessionId string. All session operations are called on the vm instance with the session ID.
import { AgentOs } from "@rivet-dev/agent-os-core";
import common from "@rivet-dev/agent-os-common";
const vm = await AgentOs.create({ software: [common] });
const { sessionId } = await vm.createSession("pi", {
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
// Stream events
vm.onSessionEvent(sessionId, (event) => {
console.log(event.method, event.params);
});
// Handle permissions
vm.onPermissionRequest(sessionId, (request) => {
console.log("Permission:", request.description);
vm.respondPermission(sessionId, request.permissionId, "once");
});
// Send a prompt
const response = await vm.prompt(sessionId, "Write a hello world script");
console.log(response);
// Configure the session
await vm.setSessionModel(sessionId, "claude-sonnet-4-6");
await vm.setSessionMode(sessionId, "plan");
// Event history (returns SequencedEvent[] with .sequenceNumber and .notification)
const events = vm.getSessionEvents(sessionId);
for (const event of events) {
console.log(event.sequenceNumber, event.notification.method);
}
vm.closeSession(sessionId);
await vm.dispose();
Interactive shell
import { AgentOs } from "@rivet-dev/agent-os-core";
import common from "@rivet-dev/agent-os-common";
const vm = await AgentOs.create({ software: [common] });
const { shellId } = vm.openShell();
vm.onShellData(shellId, (data) => {
process.stdout.write(new TextDecoder().decode(data));
});
vm.writeShell(shellId, "echo hello from shell\n");
// Resize terminal
vm.resizeShell(shellId, 120, 40);
vm.closeShell(shellId);
await vm.dispose();
Networking
import { AgentOs } from "@rivet-dev/agent-os-core";
import common from "@rivet-dev/agent-os-common";
const vm = await AgentOs.create({ software: [common] });
// Start a server inside the VM
await vm.writeFile("/tmp/app.mjs", 'import http from "http"; http.createServer((req, res) => res.end("hello")).listen(3000);');
vm.spawn("node", ["/tmp/app.mjs"]);
// Fetch from it
const response = await vm.fetch(3000, new Request("http://localhost/"));
console.log(await response.text());
await vm.dispose();
Cron jobs
The core package supports a "callback" action type in addition to "exec" and "session".
import { AgentOs } from "@rivet-dev/agent-os-core";
import common from "@rivet-dev/agent-os-common";
const vm = await AgentOs.create({ software: [common] });
const job = vm.scheduleCron({
id: "cleanup",
schedule: "0 * * * *",
action: { type: "exec", command: "rm", args: ["-rf", "/tmp/cache"] },
});
// Or use a callback (not available in the actor wrapper)
vm.scheduleCron({
schedule: "*/5 * * * *",
action: {
type: "callback",
fn: async () => {
console.log("Custom logic every 5 minutes");
},
},
});
vm.onCronEvent((event) => {
if (event.type === "cron:fire") console.log("Job fired:", event.jobId);
if (event.type === "cron:complete") console.log("Job done:", event.jobId, event.durationMs, "ms");
if (event.type === "cron:error") console.error("Job error:", event.error);
});
console.log(vm.listCronJobs());
job.cancel();
await vm.dispose();
Mounts
Configure filesystem backends at boot time.
import { AgentOs, createHostDirBackend, createInMemoryFileSystem } from "@rivet-dev/agent-os-core";
import { createS3Backend } from "@rivet-dev/agent-os-s3";
import common from "@rivet-dev/agent-os-common";
const vm = await AgentOs.create({
software: [common],
mounts: [
// Host directory (read-only)
{ path: "/mnt/code", driver: createHostDirBackend({ hostPath: "/path/to/repo" }), readOnly: true },
// S3 bucket
{ path: "/mnt/data", driver: createS3Backend({ bucket: "my-bucket", prefix: "agent/" }) },
// In-memory scratch space
{ path: "/mnt/scratch", driver: createInMemoryFileSystem() },
],
});
const files = await vm.readdir("/mnt/code");
console.log(files);
await vm.dispose();
What you give up without the actor
- No built-in persistence. The default filesystem is in-memory and lost on
dispose(). You can configure your own mounts (S3, host directories, etc.) for persistence. - No sleep/wake. You manage the full VM lifecycle yourself.
- No event broadcasting. Events are local callbacks, not distributed to remote clients.
- No preview URLs. No built-in HTTP server for sharing VM services.
- No multiplayer. Single-process, single-client only.
- No orchestration. No workflows, queues, or scheduling integration.
- No session persistence. Session history is lost on dispose.
If you need any of these, use the agentOs() actor instead.