agents
Version:
A home for your AI agents
802 lines (602 loc) • 16.5 kB
Markdown
# Configuration
This guide covers everything you need to configure agents for local development and production deployment, including wrangler.jsonc setup, type generation, environment variables, and the Cloudflare dashboard.
## wrangler.jsonc
The `wrangler.jsonc` file configures your Cloudflare Worker and its bindings. Here's a complete example for an agents project:
```jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-agent-app",
"main": "src/server.ts",
"compatibility_date": "2025-01-01",
"compatibility_flags": ["nodejs_compat"],
// Static assets (optional)
"assets": {
"directory": "public",
"binding": "ASSETS"
},
// Durable Object bindings for agents
"durable_objects": {
"bindings": [
{
"name": "MyAgent",
"class_name": "MyAgent"
},
{
"name": "ChatAgent",
"class_name": "ChatAgent"
}
]
},
// Required: Enable SQLite storage for agents
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["MyAgent", "ChatAgent"]
}
],
// AI binding (optional, for Workers AI)
"ai": {
"binding": "AI"
}
}
```
### Key Fields
#### compatibility_flags
The `nodejs_compat` flag is **required** for agents:
```jsonc
"compatibility_flags": ["nodejs_compat"]
```
This enables Node.js compatibility mode, which agents depend on for crypto, streams, and other Node.js APIs.
#### durable_objects.bindings
Each agent class needs a binding:
```jsonc
"durable_objects": {
"bindings": [
{
"name": "Counter", // Property name on `env` (env.Counter)
"class_name": "Counter" // Exported class name (must match exactly)
}
]
}
```
| Field | Description |
| ------------ | ----------------------------------------------------------- |
| `name` | The property name on `env`. Use this in code: `env.Counter` |
| `class_name` | Must match the exported class name exactly |
**When name and class_name differ:**
```jsonc
{
"name": "COUNTER_DO", // env.COUNTER_DO
"class_name": "CounterAgent" // export class CounterAgent
}
```
This is useful when you want environment variable-style naming (`COUNTER_DO`) but more descriptive class names (`CounterAgent`).
#### migrations
Migrations tell Cloudflare how to set up storage for your Durable Objects:
```jsonc
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["MyAgent"]
}
]
```
| Field | Description |
| -------------------- | ------------------------------------------------------------- |
| `tag` | Version identifier (e.g., "v1", "v2"). Must be unique |
| `new_sqlite_classes` | Agent classes that use SQLite storage (state persistence) |
| `deleted_classes` | Classes being removed |
| `renamed_classes` | Classes being renamed (see [Migrations](#migrations-1) below) |
#### assets
For serving static files (HTML, CSS, JS):
```jsonc
"assets": {
"directory": "public", // Folder containing static files
"binding": "ASSETS" // Optional: binding for programmatic access
}
```
With a binding, you can serve assets programmatically:
```typescript
export default {
async fetch(request: Request, env: Env) {
// static assets are served by the worker automatically by default
// route the request to the appropriate agent
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;
// add your own routing logic here if you want to handle requests that are not for agents
return new Response("Not found", { status: 404 });
}
};
```
#### ai
For Workers AI integration:
```jsonc
"ai": {
"binding": "AI",
"remote": true // Mandatory: use remote inference (for local dev)
}
```
Access in your agent:
```typescript
const response = await this.env.AI.run("@cf/moonshotai/kimi-k2.7-code", {
prompt: "Hello!"
});
```
## TypeScript Configuration
The Agents SDK ships a shared `tsconfig.json` that sets all the compiler options needed for agents projects — including the `ES2021` target required for `@callable()` decorators, strict mode, bundler module resolution, and Workers types.
Extend it in your `tsconfig.json`:
```json
{
"extends": "agents/tsconfig"
}
```
This is equivalent to:
```json
{
"compilerOptions": {
"target": "ES2021",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"module": "ES2022",
"moduleResolution": "bundler",
"types": ["node", "@cloudflare/workers-types", "vite/client"],
"allowImportingTsExtensions": true,
"noEmit": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
```
You can override individual options as needed:
```json
{
"extends": "agents/tsconfig",
"compilerOptions": {
"jsx": "preserve"
}
}
```
> **Warning:** Do not set `"experimentalDecorators": true`. The Agents SDK uses [TC39 standard decorators](https://github.com/tc39/proposal-decorators), not TypeScript legacy decorators. Enabling `experimentalDecorators` applies an incompatible transform that silently breaks `@callable()` at runtime.
## Vite Configuration
The Agents SDK provides a Vite plugin that handles TC39 decorator transforms. Vite 8 uses Oxc for transpilation, which does not yet support TC39 decorators — without this plugin, `@callable()` and other decorators will fail at runtime.
Add the plugin to your `vite.config.ts`:
```typescript
import { cloudflare } from "@cloudflare/vite-plugin";
import react from "@vitejs/plugin-react";
import agents from "agents/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [agents(), react(), cloudflare()]
});
```
The `agents()` plugin is safe to include even if your project does not use decorators. It only runs the transform on files that contain `@` syntax.
### Turndown Stub
By default, the `agents()` Vite plugin also replaces imports of the `turndown`
package with a Worker-safe stub. This prevents `just-bash`, used by Think's
workspace `bash` tool and skill scripts, from pulling `turndown`'s Node DOM
fallback into the Worker bundle. That fallback expects Node's global `require()`,
which is not available in Workers.
If your app imports `turndown` directly for HTML-to-Markdown conversion, disable
the stub so your import resolves to the real package:
```typescript
export default defineConfig({
plugins: [agents({ stubTurndown: false }), react(), cloudflare()]
});
```
When the stub is enabled and app code calls `turndown()`, the stub throws an
error that points back to this option.
The starter template and all examples include this plugin by default.
## Generating Types
Wrangler can generate TypeScript types for your bindings.
### Automatic Generation
Run the types command:
```bash
npx wrangler types
```
This creates or updates `worker-configuration.d.ts` with your `Env` type.
### Custom Output Path
Specify a custom path:
```bash
npx wrangler types env.d.ts
```
### Without Runtime Types
For cleaner output (recommended for agents):
```bash
npx wrangler types env.d.ts --include-runtime false
```
This generates just your bindings without Cloudflare runtime types.
### Example Generated Output
```typescript
// env.d.ts (generated)
declare namespace Cloudflare {
interface Env {
OPENAI_API_KEY: string;
Counter: DurableObjectNamespace<import("./src/server").Counter>;
ChatAgent: DurableObjectNamespace<import("./src/server").ChatAgent>;
}
}
interface Env extends Cloudflare.Env {}
```
### Manual Type Definition
You can also define types manually:
```typescript
// env.d.ts
import type { Counter } from "./src/agents/counter";
import type { ChatAgent } from "./src/agents/chat";
interface Env {
// Secrets
OPENAI_API_KEY: string;
WEBHOOK_SECRET: string;
// Agent bindings
Counter: DurableObjectNamespace<Counter>;
ChatAgent: DurableObjectNamespace<ChatAgent>;
// Other bindings
AI: Ai;
ASSETS: Fetcher;
MY_KV: KVNamespace;
}
```
### Adding to package.json
Add a script for easy regeneration:
```json
{
"scripts": {
"types": "wrangler types env.d.ts --include-runtime false"
}
}
```
## Environment Variables & Secrets
### Local Development (.env)
Create a `.env` file for local secrets (add to `.gitignore`):
```bash
# .env
OPENAI_API_KEY=sk-...
GITHUB_WEBHOOK_SECRET=whsec_...
DATABASE_URL=postgres://...
```
Access in your agent:
```typescript
class MyAgent extends Agent<Env> {
async onStart() {
const apiKey = process.env.OPENAI_API_KEY;
}
}
```
### Production Secrets
Use `wrangler secret` for production:
```bash
# Add a secret
wrangler secret put OPENAI_API_KEY
# Enter value when prompted
# List secrets
wrangler secret list
# Delete a secret
wrangler secret delete OPENAI_API_KEY
```
### Non-Secret Variables
For non-sensitive configuration, use `vars` in wrangler.jsonc:
```jsonc
{
"vars": {
"API_BASE_URL": "https://api.example.com",
"MAX_RETRIES": "3",
"DEBUG_MODE": "false"
}
}
```
Note: All values must be strings. Parse numbers/booleans in code:
```typescript
const maxRetries = parseInt(process.env.MAX_RETRIES, 10);
const debugMode = process.env.DEBUG_MODE === "true";
```
### Environment-Specific Variables
Use `[env.{name}]` sections for different environments (e.g. staging, production):
```jsonc
{
"name": "my-agent",
"vars": {
"API_URL": "https://api.example.com"
},
"env": {
"staging": {
"vars": {
"API_URL": "https://staging-api.example.com"
}
},
"production": {
"vars": {
"API_URL": "https://api.example.com"
}
}
}
}
```
Deploy to specific environment:
```bash
wrangler deploy --env staging
wrangler deploy --env production
```
## Local Development
### Starting the Dev Server
With Vite (recommended for full stack apps):
```bash
npx vite dev
```
Without Vite:
```bash
npx wrangler dev
```
### Local State Persistence
Durable Object state is persisted locally in `.wrangler/state/`:
```
.wrangler/
└── state/
└── v3/
└── d1/
└── miniflare-D1DatabaseObject/
└── ... (SQLite files)
```
### Clearing Local State
To reset all local Durable Object state:
```bash
rm -rf .wrangler/state
```
Or restart with fresh state:
```bash
npx wrangler dev --persist-to=""
```
### Inspecting Local SQLite
You can inspect agent state directly:
```bash
# Find the SQLite file
ls .wrangler/state/v3/d1/
# Open with sqlite3
sqlite3 .wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite
```
## Dashboard Setup
### Automatic Resources
When you deploy, Cloudflare automatically creates:
- **Worker** - Your deployed code
- **Durable Object namespaces** - One per agent class
- **SQLite storage** - Attached to each namespace
### Viewing Durable Objects
1. Go to [dash.cloudflare.com](https://dash.cloudflare.com)
2. Select your account → Workers & Pages
3. Click your Worker
4. Go to **Durable Objects** tab
Here you can:
- See all Durable Object namespaces
- View individual object instances
- Inspect storage (keys and values)
- Delete objects
### Real-time Logs
View live logs from your agents:
```bash
npx wrangler tail
```
Or in the dashboard:
1. Go to your Worker
2. Click **Logs** tab
3. Enable real-time logs
Filter by:
- Status (success, error)
- Search text
- Sampling rate
### Analytics
The dashboard shows:
- Request count
- Error rate
- CPU time
- Duration percentiles
- Durable Object metrics
## Production Deployment
### Basic Deploy
```bash
npx wrangler deploy
```
This:
1. Bundles your code
2. Uploads to Cloudflare
3. Applies migrations
4. Makes it live on `*.workers.dev`
### Custom Domain
Add a route in wrangler.jsonc:
```jsonc
{
"routes": [
{
"pattern": "agents.example.com/*",
"zone_name": "example.com"
}
]
}
```
Or use a custom domain (simpler):
```jsonc
{
"routes": [
{
"pattern": "agents.example.com",
"custom_domain": true
}
]
}
```
### Preview Deployments
Deploy without affecting production:
```bash
npx wrangler deploy --dry-run # See what would be uploaded
npx wrangler versions upload # Upload new version
npx wrangler versions deploy # Gradually roll out
```
### Rollbacks
Roll back to a previous version:
```bash
npx wrangler rollback
```
## Multi-Environment Setup
### Environment Configuration
Define environments in wrangler.jsonc:
```jsonc
{
"name": "my-agent",
"main": "src/server.ts",
// Base configuration (shared)
"compatibility_date": "2025-01-01",
"compatibility_flags": ["nodejs_compat"],
"durable_objects": {
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }],
// Environment overrides
"env": {
"staging": {
"name": "my-agent-staging",
"durable_objects": {
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
},
"vars": {
"ENVIRONMENT": "staging"
}
},
"production": {
"name": "my-agent-production",
"durable_objects": {
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
},
"vars": {
"ENVIRONMENT": "production"
}
}
}
}
```
### Deploying to Environments
```bash
# Deploy to staging
npx wrangler deploy --env staging
# Deploy to production
npx wrangler deploy --env production
# Set secrets per environment
npx wrangler secret put OPENAI_API_KEY --env staging
npx wrangler secret put OPENAI_API_KEY --env production
```
### Separate Durable Objects
Durable Object bindings are not inherited by named environments, so repeat them in every environment as shown above. Each environment gets its own Durable Objects. Staging agents do not share state with production agents.
To explicitly separate:
```jsonc
{
"env": {
"staging": {
"durable_objects": {
"bindings": [
{
"name": "MyAgent",
"class_name": "MyAgent",
"script_name": "my-agent-staging" // Different namespace
}
]
}
}
}
}
```
## Migrations
Migrations manage Durable Object storage schema changes.
### Adding a New Agent
Add to `new_sqlite_classes` in a new migration:
```jsonc
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["ExistingAgent"]
},
{
"tag": "v2",
"new_sqlite_classes": ["NewAgent"]
}
]
```
### Renaming an Agent Class
Use `renamed_classes`:
```jsonc
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["OldName"]
},
{
"tag": "v2",
"renamed_classes": [
{
"from": "OldName",
"to": "NewName"
}
]
}
]
```
**Important:** Also update:
1. The class name in code
2. The `class_name` in bindings
3. Export statements
### Deleting an Agent Class
Use `deleted_classes`:
```jsonc
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["AgentToDelete", "AgentToKeep"]
},
{
"tag": "v2",
"deleted_classes": ["AgentToDelete"]
}
]
```
**Warning:** This permanently deletes all data for that class.
### Migration Best Practices
1. **Never modify existing migrations** - Always add new ones
2. **Use sequential tags** - v1, v2, v3 (or use dates: 2025-01-15)
3. **Test locally first** - Migrations run on deploy
4. **Back up production data** - Before renaming or deleting
## Troubleshooting
### "No such Durable Object class"
The class isn't in migrations:
```jsonc
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["MissingClassName"] // Add it here
}
]
```
### "Cannot find module" in types
Regenerate types:
```bash
npx wrangler types env.d.ts --include-runtime false
```
### Secrets not loading locally
Check that `.env` exists and contains the variable:
```bash
cat .env
# Should show: MY_SECRET=value
```
### Migration tag conflict
Migration tags must be unique. If you see conflicts:
```jsonc
// Wrong - duplicate tags
"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["A"] },
{ "tag": "v1", "new_sqlite_classes": ["B"] } // Error!
]
// Correct - sequential tags
"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["A"] },
{ "tag": "v2", "new_sqlite_classes": ["B"] }
]
```