AI Agent
IMPORTANT: Before doing anything, you MUST read in this skill's directory. It contains essential guidance on debugging, error handling, state management, deployment, and project setup. Those rules and patterns apply to all RivetKit work. Everything below assumes you have already read and understood it.
Working Examples
If you need a reference implementation, read the raw working example code in these templates:
Patterns for building AI agent backends with RivetKit, where each conversation is one Rivet Actor that owns its memory, its message queue, and its streaming output.
Starter Code
Start with one of the working examples on
GitHub and adapt it. The sections below describe the flagship
example unless a variant is called out explicitly.
| Variant | Starter Code | Use When |
|---|
| Queue-driven AI SDK agent | GitHub | You want a streaming chat agent where each conversation keeps its own persistent memory and processes one message at a time. |
| Sandbox coding agent | GitHub | The agent should run a coding agent (Codex by default) inside an isolated sandbox via Docker, Daytona, or E2B. |
| Durable streams agent (experimental) | GitHub | You want replayable, restart-safe prompt and response delivery through durable streams instead of actor state and events. |
| Agent with a workspace (agentOS) | GitHub | The agent needs its own persistent computer: a filesystem, processes, shells, and preview URLs. See the cookbook: AI Agent Workspaces. |
Conversation Memory
Use one actor per conversation, keyed by a conversation or agent id (see
Actor Keys). The agent actor's persistent
state is the conversation memory: in the
example,
and
live in JSON actor state and survive sleep and restarts with no external database. Every model call rebuilds the prompt from
plus a system prompt, so memory and inference input are the same data.
| Variant | Where Memory Lives | Persisted State Fields |
|---|
| JSON actor state | , |
| JSON actor state plus the sandbox ACP session | , , |
experimental-durable-streams-ai-agent
| Durable streams; the actor stores only its conversation id and a read cursor | , |
Message Handling
In the
example, the client pushes user input onto the agent's
queue with
agent.connection.send("message", { text, sender })
. This is a queue push, not an action call. The actor's
hook (see
Lifecycle) consumes the queue serially with
for await (const queued of c.queue.iter())
.
Serial queue consumption is the per-conversation concurrency guarantee: at most one in-flight model call per actor, with no extra locking. The
field (
while a model call is in flight) is UI signal only; the run loop is the actual lock. The loop also checks
inside the token stream so shutdown exits gracefully.
| Variant | Message Ingress | Serialization Guarantee |
|---|
| queue pushed via | hook pops one queued message at a time with . |
| action, no queue | Each call awaits the sandbox round trip before broadcasting the result. |
experimental-durable-streams-ai-agent
| Durable prompt stream long-polled from | is persisted per chunk, so restarts resume without reprocessing prompts. |
Streaming Responses
The
actor broadcasts a
event for every model text delta. The payload carries
, the per-token
, the cumulative
, and a
flag (plus
on failure), so clients can either append deltas or idempotently replace the message by
using
. The example frontend replaces by
, which tolerates dropped events. The terminal broadcast has an empty
, the full
, and
.
Because the assistant message object lives in
and is mutated in place during streaming, partial content persists if the actor restarts mid-stream. The example broadcasts once per AI SDK delta with no throttling; batching or throttling deltas is a recommended extension for high-traffic deployments, not something the example implements.
Variant differences:
sends a single
broadcast with
after the sandbox finishes (no incremental streaming), and
experimental-durable-streams-ai-agent
appends per-token chunks to a durable response stream, then broadcasts
or
.
Architecture
| Topic | Summary |
|---|
| Topology | singleton directory plus one actor per conversation. |
| Ingress | Client pushes payloads onto the agent's queue with . |
| Streaming | One broadcast per model delta, terminal broadcast with . |
| Memory | Full transcript and status in JSON actor state; no external database. |
The manager creates
records and warms each agent through
actor-to-actor communication:
calls
c.client<typeof registry>()
, then
client.agent.getOrCreate([info.id])
and awaits
so the conversation actor exists before the client connects. The sandbox variant extends this topology with a
actor that shares the agent's key (
codingSandbox.getOrCreate([c.key[0]])
), so the agent-to-sandbox mapping is implicit in the key space.
Actors
-
-
Responsibility: Directory actor. Creates
records, lists agents, and warms each agent actor via
.
-
Actions
-
Queues
-
State
-
-
Responsibility: One actor per conversation. Holds the full message history and status, consumes queued user messages in its
loop, calls the model via the AI SDK, and broadcasts streaming deltas.
-
Actions
-
Queues
-
Events
-
State
Lifecycle
mermaid
sequenceDiagram
participant C as Client
participant AM as agentManager
participant A as agent
participant LLM as Model API
C->>AM: createAgent(name)
AM->>A: getOrCreate([info.id]) + getStatus()
AM-->>C: AgentInfo
C->>A: connection.send("message", {text, sender})
Note over A: run loop pops queue via c.queue.iter()
A-->>C: messageAdded (user message)
A-->>C: messageAdded (assistant placeholder)
A-->>C: status (thinking)
A->>LLM: streamText(system prompt + history)
loop each text delta
LLM-->>A: delta
A-->>C: response {messageId, delta, content, done: false}
end
A-->>C: response {delta: "", content, done: true}
A-->>C: status (idle)
Security Checklist
The examples ship without auth so they stay minimal. Apply this baseline before exposing an agent backend.
- API keys stay server-side: (or ) is read by the AI SDK inside the actor process. The key never reaches the browser; clients only talk to the actor over RivetKit. The sandbox variant forwards keys into the sandbox env, never to the client.
- Add authentication: The examples have no auth, so anyone who reaches the server can create agents, list them, and message any agent whose key they can guess. Add or checks with scoped tokens as a recommended extension. See Authentication.
- Validate and rate-limit queue payloads: The example only skips bodies without a string . Enforce payload size limits, schema validation, and per-connection rate limits as a recommended extension.
- Derive sender identity server-side: The example trusts the client-supplied field verbatim. Bind sender identity to the authenticated connection instead.
- Cap or trim message history: The example sends the full transcript on every model call with no cap. Trim or summarize old messages as a recommended extension so prompts and state stay bounded.
- Set cost ceilings per conversation: Add per-agent token budgets and quotas as a recommended extension. The sandbox variant runs real compute, so also enforce per-user sandbox quotas and restrict sandbox network egress.
Reference Map
Actors
- Access Control
- Actions
- Actor Keys
- Actor Scheduling
- Actor Statuses
- AI and User-Generated Rivet Actors
- Authentication
- Communicating Between Actors
- Connections
- Custom Inspector Tabs
- Debugging
- Design Patterns
- Destroying Actors
- Errors
- Fetch and WebSocket Handler
- Helper Types
- Icons & Names
- Input Parameters
- Lifecycle
- Limits
- Low-Level HTTP Request Handler
- Low-Level KV Storage
- Low-Level WebSocket Handler
- Metadata
- Next.js Quickstart
- Node.js & Bun Quickstart
- Queues & Run Loops
- React Quickstart
- Realtime
- Rust Quickstart (Preview)
- Sandbox Actor
- Scaling & Concurrency
- Sharing and Joining State
- SQLite
- SQLite + Drizzle
- State & Storage
- Testing
- Troubleshooting
- Types
- Vanilla HTTP API
- Versions & Upgrades
- Workflows
Agent Os
- Agent-to-Agent Communication
- agentOS vs Sandbox
- Authentication
- Benchmarks
- Configuration
- Core Package
- Cron Jobs
- Deployment
- Embedded LLM Gateway
- Events
- Filesystem
- Limitations
- LLM Credentials
- Multiplayer
- Networking & Previews
- Overview
- Permissions
- Persistence & Sleep
- Pi
- Processes & Shell
- Queues
- Quickstart
- Sandbox Mounting
- Security & Auth
- Security Model
- Sessions
- Software
- SQLite
- System Prompt
- Tools
- Webhooks
- Workflow Automation
Clients
- Node.js & Bun
- React
- Swift
- SwiftUI
Connect
- Deploy To Amazon Web Services Lambda
- Deploying to AWS ECS
- Deploying to Cloudflare Workers
- Deploying to Freestyle
- Deploying to Google Cloud Run
- Deploying to Hetzner
- Deploying to Kubernetes
- Deploying to Railway
- Deploying to Rivet Compute
- Deploying to Supabase Functions
- Deploying to Vercel
- Deploying to VMs & Bare Metal
Cookbook
- AI Agent
- AI Agent Workspaces
- Chat Room
- Collaborative Text Editor
- Cron Jobs and Scheduled Tasks
- Database per Tenant
- Deploying Rivet in a VPC or Air-Gapped Network
- Live Cursors and Presence
- Multiplayer Game
General
- Actor Configuration
- Architecture
- Cross-Origin Resource Sharing
- Documentation for LLMs & AI
- Edge Networking
- Endpoints
- Environment Variables
- HTTP Server
- Logging
- Pool Configuration
- Production Checklist
- Registry Configuration
- Runtime Modes
Self Hosting
- Configuration
- Docker Compose
- Docker Container
- File System
- FoundationDB (Enterprise)
- Installing Rivet Engine
- Kubernetes
- Multi-Region
- PostgreSQL
- Production Checklist
- Railway Deployment
- Render Deployment
- TLS & Certificates