---
title: "Core Package"
description: "Use @rivet-dev/agent-os-core standalone for direct VM control without the Rivet Actor runtime."
skill: true
---

## 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](#mounts)) | Persistent filesystem and sessions |
| Distributed state | Manage yourself | Built-in distributed statefulness |
| Stateful sandboxes | Complex to run yourself | Built into Rivet |
| Sleep/wake | Manual `dispose()` / `create()` | Automatic |
| Events | Direct callbacks | Broadcasted to all connected clients |
| Preview URLs | None | Built-in signed URL server |
| Multiplayer | N/A | Multiple clients on same actor |
| Orchestration | N/A | Workflows, queues, cron |
| Agent-to-agent communication | Custom | Built into [Rivet Actors](/docs/agent-os/agent-to-agent) |
| Authentication | Set up yourself | [Documentation](/docs/agent-os/authentication) |

We recommend using [Rivet Actors](/docs/actors) because they provide a portable way to run agentOS on any infrastructure with built-in persistence, networking, and orchestration. Use the core package if you need the most bare-bones implementation possible.

## Install

```bash
npm install @rivet-dev/agent-os-core
```

## Boot a VM

```ts @nocheck
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

```ts @nocheck
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

```ts @nocheck
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.

```ts @nocheck
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

```ts @nocheck
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

```ts @nocheck
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"`.

```ts @nocheck
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.

```ts @nocheck
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](#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](/docs/agent-os/quickstart) instead.
