From cf06f33d3b1a7e73aa12c975d754677ccbd49b61 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Wed, 8 Jul 2026 04:21:05 -0700 Subject: [PATCH] docs(core): use @rivet-dev/agentos-core in Core Package docs and examples The Core Package page and its examples demonstrated the actor package (@rivet-dev/agentos) instead of the core package. Rewrite the examples to drive AgentOs directly (create/exec/spawn/createSession/prompt/fetch/ scheduleCron) and update the doc prose and CodeSnippet references to match. --- examples/core/README.md | 30 ++++--- examples/core/config-reference.ts | 31 +++---- examples/core/hooks.ts | 21 +++-- examples/core/mounts.ts | 10 +-- examples/core/server.ts | 9 --- examples/core/{client.ts => vm.ts} | 108 +++++++++++-------------- website/src/content/docs/docs/core.mdx | 53 ++++++------ 7 files changed, 122 insertions(+), 140 deletions(-) delete mode 100644 examples/core/server.ts rename examples/core/{client.ts => vm.ts} (51%) diff --git a/examples/core/README.md b/examples/core/README.md index 1041ebffa9..d49faec680 100644 --- a/examples/core/README.md +++ b/examples/core/README.md @@ -1,30 +1,40 @@ --- title: "Core" -description: "Core AgentOs API: exec, config reference, lifecycle hooks, and mounts." +description: "Core AgentOs API: exec, config reference, lifecycle events, and mounts." category: "Reference" order: 1 --- -The core AgentOs API surface in one place: define a VM server, connect a typed client, and drive VMs for exec, filesystem, processes, agent sessions, networking, and cron. Reach for this when you want a reference of what a `handle` can do and how the server is configured. +The core `@rivet-dev/agentos-core` API surface in one place: boot a VM with +`AgentOs.create()` and drive it directly for exec, filesystem, processes, agent +sessions, networking, and cron — no actor runtime and no client/server split. +Reach for this when you want a reference of what an `AgentOs` instance can do and +how it is configured. ## How it works -`agentOS({ ... })` defines a VM with its mounts, software, lifecycle hooks, and preview/network settings, then `setup({ use: { vm } }).start()` exposes it over the wire. On the client, `createClient()` gives a typed `client`, and `client.vm.getOrCreate(id)` returns a `handle`. Everything runs through that handle: `exec`/`spawn` for processes, `readFile`/`writeFiles`/`readdirRecursive` for the filesystem, `createSession`/`sendPrompt` for agents, `openShell` for interactive terminals, `vmFetch` for in-VM servers, and `scheduleCron` for jobs. `handle.connect()` opens an event stream for process output, session events, permission requests, and cron events. - -- `server.ts` / `config-reference.ts` — VM definition and the full config surface (mounts, software, loopback exemptions, preview token lifetimes, hooks). -- `hooks.ts` — server-side lifecycle hooks like `onSessionEvent`. +`AgentOs.create({ ... })` boots a VM in-process with its mounts, software, and +network settings, and returns an `AgentOs` instance. Everything runs through that +instance: `exec`/`spawn` for processes, `readFile`/`writeFiles`/`readdirRecursive` +for the filesystem, `createSession`/`prompt` for agents, `fetch` for in-VM +servers, and `scheduleCron` for jobs. Process output and session/permission/cron +events are delivered through callbacks (`spawn({ onStdout })`, `onProcessExit`, +`onSessionEvent`, `onPermissionRequest`, `onCronEvent`). + +- `vm.ts` — boot a VM and every instance capability (exec, filesystem, + processes, sessions, networking, cron). +- `advanced.ts` — pin VMs to a dedicated sidecar process. +- `config-reference.ts` — the full `AgentOs.create()` config surface. +- `hooks.ts` — per-session event and permission observation. - `mounts.ts` — host-directory and S3 mount descriptors. -- `client.ts` — every client capability against a `handle`. ## Run it ```sh npm install -npx tsx server.ts # start the VM server, then run client.ts against it +npx tsx vm.ts ``` -The server listens on `http://localhost:6420`; the client connects, creates a VM, and exercises exec, filesystem, sessions, and more. - ## Source View the source on GitHub: https://github.com/rivet-dev/agent-os/tree/main/examples/core diff --git a/examples/core/config-reference.ts b/examples/core/config-reference.ts index bdf2f4cdb0..4740420f9f 100644 --- a/examples/core/config-reference.ts +++ b/examples/core/config-reference.ts @@ -1,31 +1,22 @@ -import { agentOS, nodeModulesMount, setup } from "@rivet-dev/agentos"; +import { AgentOs, nodeModulesMount } from "@rivet-dev/agentos-core"; import pi from "@agentos-software/pi"; -const vm = agentOS({ +// The full AgentOs.create() configuration surface. The agentOS() actor accepts +// this same options object and layers persistence, sleep/wake, and preview URLs +// on top. +const vm = await AgentOs.create({ // Filesystems to mount at boot. Use nodeModulesMount() to expose a host // node_modules tree at /root/node_modules. mounts: [nodeModulesMount("/path/to/project/node_modules")], // Software packages to install in the VM (see /docs/software) software: [pi], - // Ports exempt from SSRF checks + // Also install the default software bundle (sh + coreutils). Defaults to true; + // set false for a bare VM with only the software you list. + defaultSoftware: true, + // Ports exempt from SSRF checks (for testing against host-side mock servers) loopbackExemptPorts: [3000], // Extra instructions appended to agent system prompts additionalInstructions: "Always write tests first.", - - // Preview URL token lifetimes - preview: { - defaultExpiresInSeconds: 3600, // 1 hour (default) - maxExpiresInSeconds: 86400, // 24 hours (default) - }, - - // Lifecycle hooks (see below) - onSessionEvent: async (sessionId, event) => { - console.log("Session event:", sessionId, event.method); - }, - onPermissionRequest: async (sessionId, request) => { - console.log("Permission request:", sessionId, request.permissionId); - }, + // Sidecar placement — defaults to the shared `default` pool + sidecar: { kind: "shared" }, }); - -export const registry = setup({ use: { vm } }); -registry.start(); diff --git a/examples/core/hooks.ts b/examples/core/hooks.ts index 1769fa54f9..b33d8c85b1 100644 --- a/examples/core/hooks.ts +++ b/examples/core/hooks.ts @@ -1,8 +1,17 @@ -import { agentOS } from "@rivet-dev/agentos"; +import { AgentOs } from "@rivet-dev/agentos-core"; +import pi from "@agentos-software/pi"; -export const vm = agentOS({ - // Runs once per session event, server-side, for every session. - onSessionEvent: async (sessionId, event) => { - console.log("Session event:", sessionId, event.method); - }, +// With the core package, session events and permission requests are observed +// per-session on the AgentOs instance (there is no actor-factory hook). +const vm = await AgentOs.create({ software: [pi] }); +const { sessionId } = await vm.createSession("pi"); + +// Runs for every event on this session. +vm.onSessionEvent(sessionId, (event) => { + console.log("Session event:", sessionId, event.method); +}); + +// Fires when the agent requests permission. +vm.onPermissionRequest(sessionId, (request) => { + console.log("Permission request:", sessionId, request.permissionId); }); diff --git a/examples/core/mounts.ts b/examples/core/mounts.ts index 0e4ad1e758..ca304deb8b 100644 --- a/examples/core/mounts.ts +++ b/examples/core/mounts.ts @@ -1,6 +1,9 @@ -import { agentOS, setup } from "@rivet-dev/agentos"; +import { AgentOs } from "@rivet-dev/agentos-core"; -const vm = agentOS({ +// Configure filesystem backends at boot. Native mount plugins (host +// directories, S3, etc.) are passed via `plugin`, each identified by an `id` +// and a `config` object. +const vm = await AgentOs.create({ mounts: [ // Host directory (read-only) { @@ -15,6 +18,3 @@ const vm = agentOS({ }, ], }); - -export const registry = setup({ use: { vm } }); -registry.start(); diff --git a/examples/core/server.ts b/examples/core/server.ts deleted file mode 100644 index ba01a582be..0000000000 --- a/examples/core/server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { agentOS, setup } from "@rivet-dev/agentos"; -import pi from "@agentos-software/pi"; - -const vm = agentOS({ - software: [pi], -}); - -export const registry = setup({ use: { vm } }); -registry.start(); diff --git a/examples/core/client.ts b/examples/core/vm.ts similarity index 51% rename from examples/core/client.ts rename to examples/core/vm.ts index c16357107b..4a0d6c4221 100644 --- a/examples/core/client.ts +++ b/examples/core/vm.ts @@ -1,28 +1,29 @@ // docs:start boot -import { createClient } from "@rivet-dev/agentos/client"; -import type { registry } from "./server"; +import { AgentOs } from "@rivet-dev/agentos-core"; +import pi from "@agentos-software/pi"; -const client = createClient({ endpoint: "http://localhost:6420" }); -const handle = client.vm.getOrCreate("my-agent"); +// Create a VM directly with the core package — no actor runtime, no +// client/server split. `AgentOs.create()` boots the VM in-process. +const vm = await AgentOs.create({ software: [pi] }); -const result = await handle.exec("echo hello"); +const result = await vm.exec("echo hello"); console.log(result.stdout); // "hello\n" // docs:end boot // ── Filesystem ──────────────────────────────────────────────────── async function filesystem() { // docs:start filesystem - await handle.writeFile("/home/agentos/hello.txt", "Hello, world!"); - const content = await handle.readFile("/home/agentos/hello.txt"); + await vm.writeFile("/home/agentos/hello.txt", "Hello, world!"); + const content = await vm.readFile("/home/agentos/hello.txt"); console.log(new TextDecoder().decode(content)); - await handle.mkdir("/home/agentos/src"); - await handle.writeFiles([ + await vm.mkdir("/home/agentos/src"); + await vm.writeFiles([ { path: "/home/agentos/src/index.ts", content: "console.log('hi');" }, { path: "/home/agentos/src/utils.ts", content: "export const add = (a: number, b: number) => a + b;" }, ]); - const entries = await handle.readdirRecursive("/home/agentos"); + const entries = await vm.readdirRecursive("/home/agentos"); for (const entry of entries) { console.log(entry.type, entry.path); } @@ -33,60 +34,54 @@ async function filesystem() { async function processes() { // docs:start processes // One-shot execution - const result = await handle.exec("ls -la /home/agentos"); + const result = await vm.exec("ls -la /home/agentos"); console.log(result.stdout); - // Long-running process with streaming output - await handle.writeFile( + // Long-running process with streaming output. spawn() returns synchronously; + // stdout/stderr are delivered through the callbacks you pass in. + await vm.writeFile( "/tmp/server.mjs", 'import http from "http"; http.createServer((req, res) => res.end("ok")).listen(3000); console.log("listening");', ); - const { pid } = await handle.spawn("node", ["/tmp/server.mjs"]); - - const conn = handle.connect(); - conn.on("processOutput", (data) => { - if (data.pid === pid && data.stream === "stdout") { - console.log("stdout:", new TextDecoder().decode(data.data)); - } - }); - conn.on("processExit", (data) => { - if (data.pid === pid) console.log("exited:", data.exitCode); + const { pid } = vm.spawn("node", ["/tmp/server.mjs"], { + onStdout: (data) => console.log("stdout:", new TextDecoder().decode(data)), }); + vm.onProcessExit(pid, (exitCode) => console.log("exited:", exitCode)); + // Write to stdin - await handle.writeProcessStdin(pid, "some input\n"); + await vm.writeProcessStdin(pid, "some input\n"); // Stop or kill - await handle.stopProcess(pid); + vm.stopProcess(pid); // docs:end processes } // ── Agent sessions ──────────────────────────────────────────────── async function agentSessions() { // docs:start sessions - const conn = handle.connect(); - - // Stream session events (each event is a JSON-RPC notification) - conn.on("sessionEvent", (data) => { - console.log(data.sessionId, data.event.method, data.event.params); + // createSession() resolves to a session record. All session operations take + // its `sessionId`. + const { sessionId } = await vm.createSession("pi", { + env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, }); - // Observe permission requests from the agent - conn.on("permissionRequest", (data) => { - console.log("Permission:", data.sessionId, data.request.description); + // Stream session events (each event is a JSON-RPC notification). + vm.onSessionEvent(sessionId, (event) => { + console.log(event.method, event.params); }); - // createSession() resolves to the session ID string. - const sessionId = await handle.createSession("pi", { - env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, + // Observe permission requests from the agent. + vm.onPermissionRequest(sessionId, (request) => { + console.log("Permission:", request.description ?? request.permissionId); }); - // Send a prompt. sendPrompt() resolves to { response, text }, where `text` is - // the accumulated agent message text and `response` is the raw JSON-RPC response. - const { text } = await handle.sendPrompt(sessionId, "Write a hello world script"); + // Send a prompt. prompt() resolves to { response, text }, where `text` is the + // accumulated agent message text and `response` is the raw JSON-RPC response. + const { text } = await vm.prompt(sessionId, "Write a hello world script"); console.log(text); - await handle.closeSession(sessionId); + vm.closeSession(sessionId); // docs:end sessions } @@ -94,52 +89,45 @@ async function agentSessions() { async function networking() { // docs:start networking // Start a server inside the VM - await handle.writeFile( + await vm.writeFile( "/tmp/app.mjs", 'import http from "http"; http.createServer((req, res) => res.end("hello")).listen(3000);', ); - await handle.spawn("node", ["/tmp/app.mjs"]); + vm.spawn("node", ["/tmp/app.mjs"]); - // Fetch from it - const response = await handle.vmFetch(3000, "/"); - console.log(new TextDecoder().decode(response.body)); + // Fetch from it — fetch(port, Request) reaches services running in the VM. + const response = await vm.fetch(3000, new Request("http://vm/")); + console.log(await response.text()); // docs:end networking } // ── Cron jobs ───────────────────────────────────────────────────── async function cronJobs() { // docs:start cron - const { id } = await handle.scheduleCron({ + const job = vm.scheduleCron({ id: "cleanup", schedule: "0 * * * *", action: { type: "exec", command: "rm", args: ["-rf", "/tmp/cache"] }, }); - console.log("Scheduled:", id); + console.log("Scheduled:", job.id); // Run an agent session on a schedule - await handle.scheduleCron({ + vm.scheduleCron({ schedule: "0 9 * * *", action: { type: "session", agentType: "pi", prompt: "Review the logs and summarize any errors", - cwd: "/workspace", + options: { cwd: "/workspace" }, }, }); - const conn = handle.connect(); - conn.on("cronEvent", (data) => { - console.log("Cron event:", data.event.type, data.event.jobId); + vm.onCronEvent((event) => { + console.log("Cron event:", event.type, event.jobId); }); - console.log(await handle.listCronJobs()); + console.log(vm.listCronJobs()); // docs:end cron } -export { - filesystem, - processes, - agentSessions, - networking, - cronJobs, -}; +export { filesystem, processes, agentSessions, networking, cronJobs }; diff --git a/website/src/content/docs/docs/core.mdx b/website/src/content/docs/docs/core.mdx index f38f321453..f5ce023566 100644 --- a/website/src/content/docs/docs/core.mdx +++ b/website/src/content/docs/docs/core.mdx @@ -31,13 +31,9 @@ npm install @rivet-dev/agentos-core ## Boot a VM -Define the actor on the server: +Create a VM and drive it directly — no actor runtime, no client/server split. `AgentOs.create()` boots the VM in-process and returns a handle you call directly: - - -Then drive it from a typed client: - - + ## Sidecar process @@ -51,31 +47,33 @@ For advanced cases the core package exposes explicit sidecar handles so you can ## Filesystem - + ## Processes -Long-running process output is delivered over the live `processOutput` / `processExit` events on a connection rather than per-pid callbacks: +Long-running process output is delivered through the `onStdout`/`onStderr` callbacks you pass to `spawn`, and exit through `onProcessExit(pid, …)`: - + ## Agent sessions -`createSession` returns a session record. All session operations take its `sessionId`. Session events and permission requests are delivered over the live connection (`sessionEvent` / `permissionRequest`): +`createSession` resolves to a session record; all session operations take its `sessionId`. Session events and permission requests are delivered through per-session callbacks (`onSessionEvent` / `onPermissionRequest`): - + -Subscribe to `sessionEvent` before sending a prompt so you do not miss the live stream. Persisted history can be read back later with `getSessionEvents()`. +Register `onSessionEvent` right after `createSession` so you do not miss the live stream — core session events are live-only and are not replayed. ## Networking - +`fetch(port, request)` reaches a server running inside the VM: + + ## Cron jobs -Cron jobs run an `"exec"` command or a `"session"` prompt on a schedule. Fired jobs are surfaced over the live `cronEvent` connection: +Cron jobs run an `"exec"` command or a `"session"` prompt on a schedule. Fired jobs are surfaced through the `onCronEvent` callback: - + ## Mounts @@ -84,27 +82,22 @@ Configure filesystem backends at boot time. Native mount plugins (host directories, S3, etc.) are passed via `plugin`, each identified by an `id` and a `config` object. - - -## `agentOS()` configuration reference + -When you use the [`agentOS()` actor](/docs/quickstart), all VM configuration is passed to the factory as a single flat object. This is the consolidated config block to copy and adapt: +## Configuration reference - +All VM configuration is passed to `AgentOs.create()` as a single flat object. This is the consolidated config block to copy and adapt. The [`agentOS()` actor](/docs/quickstart) accepts the same options and layers persistence, sleep/wake, and preview URLs on top: -The top-level fields are documented inline above. See [Mounts](#mounts), [Software](/docs/software), and (for the hooks) [Approvals](/docs/approvals). + -### Lifecycle hooks +The top-level fields are documented inline above. See [Mounts](#mounts) and [Software](/docs/software). -`onPermissionRequest(sessionId, request)` fires when an agent requests permission. `onSessionEvent(sessionId, event)` is a server-side hook called once for every session event: unlike the client-side `sessionEvent` connection subscription, it runs in the actor for every event regardless of connected clients, making it the place for server-side logging, persistence, or side effects. +### Session events - +With the core package, session events and permission requests are observed per-session on the `AgentOs` instance. `onSessionEvent(sessionId, event)` fires once for every session event; `onPermissionRequest(sessionId, request)` fires when an agent requests permission. Both are live-only callbacks — register them right after `createSession`: -### Timeouts + -| Setting | Default | Description | -|---------|---------|-------------| -| Action timeout | 15 minutes | Maximum time for any single action | -| Sleep grace period | 15 minutes | Time before sleeping after all activity stops | +### Timeouts and sleep -These are set internally by the `agentOS()` factory and cannot be overridden per-call. See [Persistence & Sleep](/docs/persistence) for details on the sleep lifecycle. +Action timeouts and automatic sleep/wake are features of the [`agentOS()` actor](/docs/quickstart), not the core package. A core VM stays alive until you call `dispose()`. See [Persistence & Sleep](/docs/persistence) for the actor's sleep lifecycle.