Agent
Sessions
Open durable ACP sessions, prompt them, read history, and restore adapters.
AgentOS sessions are durable records backed by the VM’s SQLite database.
- The public session ID is stable across VM sleep and adapter restarts.
- The adapter’s private ACP session ID stays internal.
Open a session
openSession creates or restores a session, completes ACP negotiation, and
resolves without a value.
- Choose and retain the
sessionIdbefore calling; omitted →main. getSession— call separately for durable metadata.- Idempotent to repeat; changing immutable creation options for an existing ID
returns
session_conflict.
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";
const client = createClient<typeof registry>({
endpoint: "http://localhost:6420",
});
const agent = client.vm.getOrCreate("my-agent");
await agent.sessions.open({
agent: "pi",
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
Input fields: agent, cwd, additionalDirectories, env, mcpServers,
permissionPolicy, skipOsInstructions, additionalInstructions.
- Omitted
cwd→/home/agentos. - Actor deployments inject their SQLite database automatically; standalone core clients must configure a VM SQLite file or descriptor.
MCP servers
- MCP config belongs to the session — its tools are part of the agent’s runtime context.
- Configure local child-process or remote servers before opening the session.
- Config path and transports come from the selected agent adapter (e.g. Pi
reads
.mcp.jsonfrom its AgentOS home). - Install local MCP server packages before opening the session so first-run package-manager output can’t corrupt a stdio handshake.
- See the agent guide for adapter specifics.
const mcpConfig = JSON.stringify({
mcpServers: {
filesystem: {
command: "npx",
args: [
"-y",
"@modelcontextprotocol/server-filesystem",
"/home/agentos",
],
},
remote: {
url: "https://mcp.example.com/sse",
headers: { Authorization: "Bearer my-token" },
},
},
});
await agent.filesystem.mkdir("/home/agentos/.pi/agent");
await agent.filesystem.writeFile("/home/agentos/.pi/agent/.mcp.json", mcpConfig);
await agent.sessions.open({
agent: "pi",
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
Prompt
promptaccepts native ACPContentBlock[]— not a special text format.- Never creates a missing session.
- The complete user message is committed before dispatch; a prompt whose delivery may have reached the adapter is never auto-replayed.
- Bounded by
limits.acp.maxPromptBytesandlimits.acp.maxPromptBlocks; limit errors name the field to raise. - An oversized durable update batch is rejected before it changes history.
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";
const client = createClient<typeof registry>({
endpoint: "http://localhost:6420",
});
const agent = client.vm.getOrCreate("my-agent");
await agent.sessions.open({
agent: "pi",
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
const response = await agent.sessions.prompt({
content: [
{
type: "text",
text: "Create a TypeScript function that checks if a number is prime",
},
],
});
console.log(response.message?.content ?? []);
idempotencyKey— use when the caller may retry. Reusing a key with different content fails; retrying while the first call is active waits and returns its committed result.
Events and history
sessionEvent is a flat discriminated union.
- Top-level
type= native ACPSessionUpdate.sessionUpdatevalue; ACP payload fields (content,toolCallId,entries) sit beside the durability envelope. No nestedupdatewrapper. durability: "ephemeral"— live agent-message/thought delta; not sequenced or stored.durability: "durable"— has a session sequence, emitted only after its SQLite commit. Completed/coalesced message chunks are durable.- Permission request/response variants use the same flat shape with top-level
options,toolCall, oroutcome.
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";
const client = createClient<typeof registry>({
endpoint: "http://localhost:6420",
});
const agent = client.vm.getOrCreate("my-agent");
const conn = agent.connect();
// Subscribe to session events before sending the prompt
conn.on("sessionEvent", (event) => {
console.log(`[${event.sessionId}]`, event.durability, event);
});
await agent.sessions.open({
agent: "pi",
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
await agent.sessions.prompt({
content: [{ type: "text", text: "Explain how async/await works" }],
});
readHistory({ sessionId, before, after, limit })— SQLite-only, never starts an adapter.before/afterare exclusive and mutually exclusive. Dedup live durable delivery by(sessionId, sequence).- Also SQLite-only reads:
getSession,listSessions,getSessionConfig,getSessionCapabilities,getSessionAgentInfo. Listing uses an opaque keyset cursor — not a frozen snapshot.
Restoration
After VM sleep, the next prompt transparently starts the adapter. AgentOS
tries, in order:
- Native ACP
session/resume. - Stable
session/load. - A fresh private ACP session with bounded continuation context from AgentOS history.
- Adapter replay during load is suppressed — SQLite is the sole history source of truth.
- Fallback transcript bounded by
limits.acp.maxFallbackContinuationBytes. - AgentOS stores its own exact ACP updates because ACP has no portable history-reading API and adapters restore inconsistently.
Permissions
permissionPolicy is reject_all, ask, or allow_all (default
allow_all).
- Controls how AgentOS answers native ACP permission requests — not VM permissions or adapter tool access.
- Set
askwhen opening the session; subscribing alone doesn’t change the immutable policy. ask— durably records the request as apermission_requestevent.allow_all— resolves automatically, emits no permission event.- Reply with the exact adapter-supplied
optionIdand explicit session ID:
await agent.sessions.respondPermission({
sessionId: request.sessionId,
requestId: request.requestId,
optionId: request.options[0].optionId,
});
- First valid response wins atomically.
- Requests don’t expire; cancellation, adapter exit, deletion, or VM shutdown records a terminal reason.
- Accepted responses are sequenced in durable history.
Cancel, unload, and delete
cancelPrompt— cooperative ACP cancellation; returnscancelledorno_active_prompt.unloadSession— releases the live adapter, keeps SQLite metadata/history; a later prompt restores it.deleteSession— permanently removes the session and history. Omitted ID →main; repeated deletion is idempotent.
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";
const client = createClient<typeof registry>({
endpoint: "http://localhost:6420",
});
const agent = client.vm.getOrCreate("my-agent");
await agent.sessions.open({
agent: "pi",
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
// Start a long-running prompt
const promptPromise = agent.sessions.prompt({
content: [
{
type: "text",
text: "Refactor the entire codebase to use TypeScript strict mode",
},
],
});
// Cancellation is cooperative and does not delete durable history.
setTimeout(async () => {
await agent.sessions.cancelPrompt();
}, 10_000);
const response = await promptPromise;
console.log(response.message?.content ?? []);
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";
const client = createClient<typeof registry>({
endpoint: "http://localhost:6420",
});
const agent = client.vm.getOrCreate("my-agent");
await agent.sessions.open({
agent: "pi",
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
// Release only the adapter. SQLite history remains and the next prompt restores it.
await agent.sessions.unload();
// Permanent deletion requires an explicit public session ID.
await agent.sessions.delete();
Runtime configuration
getSessionConfig— returns the negotiated native ACP config collection + a revision.setSessionConfigOption— may restore the adapter, lets ACP validate the value, then replaces the cached collection.
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";
const client = createClient<typeof registry>({
endpoint: "http://localhost:6420",
});
const agent = client.vm.getOrCreate("my-agent");
await agent.sessions.open({
agent: "pi",
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
const config = await agent.sessions.getConfig();
console.log(config.options);
await agent.sessions.setConfigOption({
configId: "model",
value: "anthropic/claude-sonnet-4",
});