Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/agent-to-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Run two agents in separate isolated VMs and let one delegate to the other. The w

## How it works

Both agents are independent `agentOS` VMs registered under one `setup`. The writer is given a `review` binding: a host-side tool the agent can invoke by name. When the writer runs `agentos-review submit --path ...`, the binding's `execute` runs on the host, where it reads the file out of the writer's VM, copies it into the reviewer's VM, opens a reviewer session, and prompts the reviewer to review the code. The review text is returned to the writer as the binding's result. The two VMs never touch directly — the host bridge is the only path between them.
Both agents are independent `agentOS` VMs registered under one `setup`. The writer is given a `review` binding: a host-side function the agent can invoke by name. When the writer runs `agentos-review submit --path ...`, the binding's `execute` runs on the host, where it reads the file out of the writer's VM, copies it into the reviewer's VM, opens a reviewer session, and prompts the reviewer to review the code. The review text is returned to the writer as the binding's result. The two VMs never touch directly — the host bridge is the only path between them.

## Run it

Expand Down
2 changes: 1 addition & 1 deletion examples/agent-to-agent/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const sessionId = await writerAgent.createSession("claude", {
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});

// The writer calls the `review` toolkit, which bridges to the reviewer VM.
// The writer calls the `review` binding, which bridges to the reviewer VM.
await writerAgent.sendPrompt(
sessionId,
"Write a small REST API, then send it to the review agent for review.",
Expand Down
6 changes: 3 additions & 3 deletions examples/agent-to-agent/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ async function reviewCode(code: string): Promise<string> {
return result.text;
}

// The writer agent gets a `review` toolkit. When the writer runs
// The writer agent gets a `review` binding group. When the writer runs
// `agentos-review submit`, the bridge above executes on the host.
const writer = agentOS({
toolKits: [
bindings: [
{
name: "review",
description: "Send code to the reviewer agent and get back a review.",
tools: {
bindings: {
submit: {
description:
"Submit the full contents of a file to the reviewer agent for review. Returns the reviewer's feedback as text.",
Expand Down
4 changes: 2 additions & 2 deletions examples/bindings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ category: "Reference"
order: 3
---

Give an agent access to your own host code—API calls, database lookups, internal services—without writing a tool from scratch. Reach for bindings when the agent needs to call back into your application and you want type-safe inputs plus an auto-generated CLI surface inside the VM.
Give an agent access to your own host code—API calls, database lookups, internal services—without writing a binding from scratch. Reach for bindings when the agent needs to call back into your application and you want type-safe inputs plus an auto-generated CLI surface inside the VM.

## How it works

A binding group bundles a `name`, a `description`, and a map of named tools. Each tool declares a Zod `inputSchema`, an `execute` handler that runs on the host, and optional `examples`. You pass the groups to `agentOS({ toolKits: [...] })`, and Agent OS exposes every group to the agent as a CLI command at `/usr/local/bin/agentos-{name}` inside the VM. When the agent invokes the command, the Zod schema validates the arguments and the handler executes host-side, returning the result back to the guest. The client side stays thin: create a session and send a prompt, and the agent decides when to call the binding.
A binding group bundles a `name`, a `description`, and a map of named bindings. Each binding declares a Zod `inputSchema`, an `execute` handler that runs on the host, and optional `examples`. You pass the groups to `agentOS({ bindings: [...] })`, and Agent OS exposes every group to the agent as a CLI command at `/usr/local/bin/agentos-{name}` inside the VM. When the agent invokes the command, the Zod schema validates the arguments and the handler executes host-side, returning the result back to the guest. The client side stays thin: create a session and send a prompt, and the agent decides when to call the binding.

## Run it

Expand Down
6 changes: 3 additions & 3 deletions examples/bindings/server.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { agentOS, setup } from "@rivet-dev/agentos";
import { z } from "zod";

// Define a group of bindings (host functions). Each tool has a Zod input
// Define a group of bindings (host functions). Each binding has a Zod input
// schema and an `execute` handler that runs on the host. The group is exposed to
// the agent as a CLI command at /usr/local/bin/agentos-{name} inside the VM.
const weatherBindings = {
name: "weather",
description: "Weather data bindings",
tools: {
bindings: {
forecast: {
description: "Get the weather forecast for a city",
inputSchema: z.object({
Expand All @@ -28,7 +28,7 @@ const weatherBindings = {
};

const vm = agentOS({
toolKits: [weatherBindings],
bindings: [weatherBindings],
});

export const registry = setup({ use: { vm } });
Expand Down
5 changes: 2 additions & 3 deletions examples/browser-terminal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,8 @@ Override the web→server endpoint with `VITE_AGENTOS_ENDPOINT` (default

## Notes

- Software: `@agentos-software/common` (provides `sh` + coreutils) plus `git`,
`curl`, `ripgrep`, `jq`, and `sqlite3`. Agent OS has no vim/editor package, so
there is no in-VM editor.
- Software: `@agentos-software/common` provides `sh` plus the standard shell
tools. Agent OS has no vim/editor package, so there is no in-VM editor.
- The shipped actor has no `listShells` action and keeps no server-side
scrollback, so reconnect re-adopts saved shell ids and resumes **live** output
only (history from before the reload is not replayed). Stale ids (VM recreated)
Expand Down
5 changes: 0 additions & 5 deletions examples/browser-terminal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@
},
"dependencies": {
"@agentos-software/common": "link:../../../secure-exec/registry/software/common",
"@agentos-software/curl": "link:../../../secure-exec/registry/software/curl",
"@agentos-software/git": "link:../../../secure-exec/registry/software/git",
"@agentos-software/jq": "link:../../../secure-exec/registry/software/jq",
"@agentos-software/ripgrep": "link:../../../secure-exec/registry/software/ripgrep",
"@agentos-software/sqlite3": "link:../../../secure-exec/registry/software/sqlite3",
"@rivet-dev/agentos": "workspace:*",
"@rivetkit/react": "catalog:rivetkit",
"@xterm/addon-fit": "^0.10.0",
Expand Down
2 changes: 0 additions & 2 deletions examples/browser-terminal/server.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import common from "@agentos-software/common";
import { agentOS, setup } from "@rivet-dev/agentos";

// TEMP (local debug): only `common` (sha 42d8146) has valid projected packs;
// `sqlite3` (efc374f) is missing its `dist/package`, so it's dropped for now.
const shellVm = agentOS({
software: [common],
});
Expand Down
10 changes: 7 additions & 3 deletions examples/browser-terminal/src/ActorView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ interface ShellExitPayload {
shellId: string;
}

interface ShellEventConnection {
on(name: "shellData", cb: (payload: ShellDataPayload) => void): () => void;
on(name: "shellStderr", cb: (payload: ShellDataPayload) => void): () => void;
on(name: "shellExit", cb: (payload: ShellExitPayload) => void): () => void;
}

function toBytes(data: unknown): Uint8Array {
if (data instanceof Uint8Array) return data;
if (data instanceof ArrayBuffer) return new Uint8Array(data);
Expand Down Expand Up @@ -89,9 +95,7 @@ export function ActorView({ actorId }: { actorId: string }) {

useEffect(() => {
if (!conn) return;
const events = conn as unknown as {
on(name: string, cb: (p: never) => void): () => void;
};
const events = conn as ShellEventConnection;
const offData = events.on("shellData", (p: ShellDataPayload) =>
dispatchData(p.shellId, toBytes(p.data)),
);
Expand Down
3 changes: 2 additions & 1 deletion examples/browser-terminal/src/rivet.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { createRivetKit } from "@rivetkit/react";
import type { registry } from "../server";

const ENDPOINT =
(import.meta.env.VITE_AGENTOS_ENDPOINT as string | undefined) ??
"http://localhost:6420";

export const { useActor } = createRivetKit<any>(ENDPOINT);
export const { useActor } = createRivetKit<typeof registry>(ENDPOINT);

export const ACTOR_NAME = "shellVm";
8 changes: 7 additions & 1 deletion examples/browser-terminal/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
"esModuleInterop": true,
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": false
"verbatimModuleSyntax": false,
"baseUrl": ".",
"paths": {
"@rivet-dev/agentos": ["../../packages/agentos/src/index.ts"],
"@rivet-dev/agentos/client": ["../../packages/agentos/src/client.ts"],
"@rivet-dev/agentos/react": ["../../packages/agentos/src/react.ts"]
}
},
"include": ["server.ts", "vite.config.ts", "src"]
}
6 changes: 3 additions & 3 deletions examples/crash-course/agent-to-agent-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import pi from "@agentos-software/pi";
// The reviewer is its own isolated agent VM.
const reviewer = agentOS({ software: [pi] });

// The coder gets a `review` toolkit it can call itself: it copies a file from the
// The coder gets a `review` binding it can call itself: it copies a file from the
// coder's VM into the reviewer's VM and asks the reviewer to review it.
const coder = agentOS({
software: [pi],
toolKits: [
bindings: [
{
name: "review",
description: "Send a file to the reviewer agent and get back a review.",
tools: {
bindings: {
submit: {
description: "Submit a file path for review by the reviewer agent.",
inputSchema: z.object({ path: z.string() }),
Expand Down
56 changes: 0 additions & 56 deletions examples/crash-course/sandbox-stubs.d.ts

This file was deleted.

24 changes: 15 additions & 9 deletions examples/crash-course/sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import { agentOS, setup } from "@rivet-dev/agentos";
import { createSandboxFs, createSandboxBindings } from "@rivet-dev/agentos-sandbox";
import { AgentOs } from "@rivet-dev/agentos-core";
import {
createSandboxBindings,
createSandboxFs,
} from "@rivet-dev/agentos-sandbox";
import { SandboxAgent } from "sandbox-agent";
import { docker } from "sandbox-agent/docker";

const sandbox = await SandboxAgent.start({ sandbox: docker() });

const vm = agentOS({
// Toolkits let the agent control the sandbox
toolKits: [createSandboxBindings({ client: sandbox })],
// Mounts let the agent read the sandbox filesystem (optional)
const vm = await AgentOs.create({
// Bindings let the agent control the sandbox.
bindings: [createSandboxBindings({ client: sandbox })],
// Mounts let the agent read the sandbox filesystem.
mounts: [
{ path: "/home/agentos/sandbox", plugin: createSandboxFs({ client: sandbox }) },
{
path: "/home/agentos/sandbox",
plugin: createSandboxFs({ client: sandbox }),
},
],
});

export const registry = setup({ use: { vm } });
registry.start();
await vm.dispose();
await sandbox.dispose();
2 changes: 1 addition & 1 deletion examples/filesystem/isolation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { existsSync } from "node:fs";
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";

Expand Down Expand Up @@ -26,7 +27,6 @@ const bytes = await agent.readFile("/home/agentos/seed.json");
console.log("host readFile:", new TextDecoder().decode(bytes));

// The same path on the real host disk does not exist. The VFS is isolated.
const { existsSync } = await import("node:fs");
console.log(
"host disk sees /home/agentos/seed.json?",
existsSync("/home/agentos/seed.json") ? "YES (unexpected!)" : "NO - isolated from host",
Expand Down
25 changes: 25 additions & 0 deletions examples/quickstart/bindings/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: "Bindings"
description: "Define host-side bindings callable from inside the VM as CLI commands."
category: "Quickstart"
order: 9
---

Expose host-side functions to code running inside the VM. Reach for this when guest code needs to call back out to capabilities you implement on the host — weather lookups, calculators, database access — without granting it direct host access.

## How it works

You declare binding groups with `bindingGroup`, where each `binding` pairs a Zod `inputSchema` with an `execute` function that runs on the host. Pass the groups to `AgentOs.create({ bindings })` and Agent OS installs CLI commands inside the VM. This example wires up `weather` and `calc`, then invokes each through `agentos-weather` and `agentos-calc`.

## Run it

```bash
npm install
npx tsx index.ts
```

Prints the weather and calculator results returned from the host.

## Source

View the source on GitHub: https://github.com/rivet-dev/agent-os/tree/main/examples/quickstart/bindings
61 changes: 61 additions & 0 deletions examples/quickstart/bindings/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Bindings: define functions that execute on the host and are callable
// from inside the VM as CLI commands.

import { AgentOs, binding, bindingGroup } from "@rivet-dev/agentos-core";
import { z } from "zod";

const weatherBindings = bindingGroup({
name: "weather",
description: "Look up weather information for cities.",
bindings: {
get: binding({
description: "Get the current weather for a city.",
inputSchema: z.object({
city: z.string().describe("City name (e.g. 'London')."),
}),
execute: async (input) => {
const { city } = input;
return {
city,
temperature: 18,
conditions: "partly cloudy",
humidity: 65,
};
},
examples: [
{ description: "Get London weather", input: { city: "London" } },
],
}),
},
});

const calcBindings = bindingGroup({
name: "calc",
description: "Simple calculator operations.",
bindings: {
add: binding({
description: "Add two numbers.",
inputSchema: z.object({ a: z.number(), b: z.number() }),
execute: (input) => ({ result: input.a + input.b }),
}),
},
});

const vm = await AgentOs.create({
bindings: [weatherBindings, calcBindings],
permissions: {
fs: "allow",
network: "allow",
childProcess: "allow",
env: "allow",
binding: "allow",
},
});

const weather = await vm.exec("agentos-weather get --city London");
console.log("Weather:", JSON.stringify(weather));

const sum = await vm.exec("agentos-calc add --a 10 --b 32");
console.log("Sum:", JSON.stringify(sum));

await vm.dispose();
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@rivet-dev/agentos-example-quickstart-tools",
"name": "@rivet-dev/agentos-example-quickstart-bindings",
"version": "0.1.0",
"private": true,
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion examples/quickstart/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"processes": "node --import tsx src/processes.ts",
"network": "node --import tsx src/network.ts",
"cron": "node --import tsx src/cron.ts",
"tools": "node --import tsx src/tools.ts",
"bindings": "node --import tsx bindings/index.ts",
"agent-session": "node --import tsx src/agent-session.ts",
"sandbox": "node --import tsx src/sandbox.ts",
"nodejs": "node --import tsx src/nodejs.ts",
Expand Down
Loading
Loading