website/src/content/docs/agent-os/permissions.mdx
When an agent wants to use a tool (e.g. write a file, run a command), it emits a permissionRequest event. Your code responds with respondPermission to approve or deny.
const client = createClient<typeof registry>("http://localhost:6420"); const agent = client.vm.getOrCreate(["my-agent"]);
// Listen for permission requests agent.on("permissionRequest", async (data) => { console.log("Permission requested:", data.request);
// Approve this single request await agent.respondPermission( data.sessionId, data.request.permissionId, "once", ); });
const session = await agent.createSession("pi", { env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! }, }); await agent.sendPrompt(session.sessionId, "Create a new file at /home/user/output.txt");
```ts @nocheck server.ts
import { agentOs } from "rivetkit/agent-os";
import { setup } from "rivetkit";
import common from "@rivet-dev/agent-os-common";
import pi from "@rivet-dev/agent-os-pi";
const vm = agentOs({
options: { software: [common, pi] },
});
export const registry = setup({ use: { vm } });
registry.start();
| Reply | Behavior |
|---|---|
"once" | Approve this single request |
"always" | Approve this and all future requests of the same type |
"reject" | Deny the request |
Use the onPermissionRequest hook in the actor config to approve permissions server-side without client involvement. This is useful for fully automated pipelines.
const vm = agentOs({ onPermissionRequest: async (c, sessionId, request) => { // Auto-approve all file operations await c.respondPermission(sessionId, request.permissionId, "always"); }, options: { software: [common, pi] }, });
export const registry = setup({ use: { vm } }); registry.start();
```ts @nocheck client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>("http://localhost:6420");
const agent = client.vm.getOrCreate(["my-agent"]);
// No need to handle permissions on the client. The server auto-approves.
const session = await agent.createSession("pi", {
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
await agent.sendPrompt(session.sessionId, "Write files as needed");
Inspect the permission request to make approval decisions based on the tool or path.
<CodeGroup stacked> ```ts @nocheck server.ts import { agentOs } from "rivetkit/agent-os"; import { setup } from "rivetkit"; import common from "@rivet-dev/agent-os-common"; import pi from "@rivet-dev/agent-os-pi";const vm = agentOs({ onPermissionRequest: async (c, sessionId, request) => { // Auto-approve reads, require manual approval for writes const toolName = (request as any).toolName ?? ""; if (toolName.includes("read")) { await c.respondPermission(sessionId, request.permissionId, "always"); } // Writes are forwarded to the client via the permissionRequest event }, options: { software: [common, pi] }, });
export const registry = setup({ use: { vm } }); registry.start();
```ts @nocheck client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>("http://localhost:6420");
const agent = client.vm.getOrCreate(["my-agent"]);
// Only write permissions reach the client
agent.on("permissionRequest", async (data) => {
const approved = confirm(`Allow write: ${JSON.stringify(data.request)}?`);
await agent.respondPermission(
data.sessionId,
data.request.permissionId,
approved ? "once" : "reject",
);
});
const session = await agent.createSession("pi", {
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
await agent.sendPrompt(session.sessionId, "Read config.json and update it");
"always" sparingly. It approves all future requests of that type for the session lifetime.onPermissionRequest hook to auto-approve without client round-trips.permissionRequest on the client and build an approval UI.