Agent class internals·续
async processTask(payload: { userId: string }, queueItem: QueueItem) { console.log("Processing task for user:", payload.userId); } }
Tasks are stored in the `cf_agents_queues` SQL table and are automatically flushed in sequence. If a task succeeds, it is automatically dequeued.
### `this.schedule` and friends
Agents support scheduled execution of methods by wrapping the Durable Object's `alarm()`. The available methods are `this.schedule`, `this.getSchedule`, `this.getSchedules`, `this.cancelSchedule`. Schedules can be one-time, delayed, or recurring (using cron expressions).
Since Durable Objects only allow one alarm at a time, the `Agent` class works around this by managing multiple schedules in SQL and using a single alarm.
```ts
class MyAgent extends Agent {
async foo() {
// Schedule at a specific time
await this.schedule(new Date("2025-12-25T00:00:00Z"), "sendGreeting", {
message: "Merry Christmas!",
});
// Schedule with a delay (in seconds)
await this.schedule(60, "checkStatus", { check: "health" });
// Schedule with a cron expression
await this.schedule("0 0 * * *", "dailyTask", { type: "cleanup" });
}
async sendGreeting(payload: { message: string }) {
console.log(payload.message);
}
async checkStatus(payload: { check: string }) {
console.log("Running check:", payload.check);
}
async dailyTask(payload: { type: string }) {
console.log("Daily task:", payload.type);
}
}
Schedules are stored in the cf_agents_schedules SQL table. Cron schedules automatically reschedule themselves after execution, while one-time schedules are deleted.
this.mcp and friends
Agent includes a multi-server MCP client. This enables your Agent to interact with external services that expose MCP interfaces. The MCP client is properly documented in MCP client API.
class MyAgent extends Agent {
async onStart() {
// Add an HTTP MCP server (callbackHost only needed for OAuth servers)
await this.addMcpServer("GitHub", "https://mcp.github.com/mcp", {
callbackHost: "https://my-worker.example.workers.dev",
});
// Add an MCP server via RPC (Durable Object binding, no HTTP overhead)
await this.addMcpServer("internal-tools", this.env.MyMCP);
}
}
Email Handling
Agents can receive and reply to emails using Cloudflare's Email Routing.
class MyAgent extends Agent {
async onEmail(email: AgentEmail) {
console.log("Received email from:", email.from);
console.log("Subject:", email.headers.get("subject"));
const raw = await email.getRaw();
console.log("Raw email size:", raw.length);
// Reply to the email
await this.replyToEmail(email, {
fromName: "My Agent",
subject: "Re: " + email.headers.get("subject"),
body: "Thanks for your email!",
contentType: "text/plain",
});
}
}
To route emails to your Agent, use routeAgentEmail in your Worker's email handler:
export default {
async email(message, env, ctx) {
await routeAgentEmail(message, env, {
resolver: createAddressBasedEmailResolver("my-agent"),
});
},
} satisfies ExportedHandler<Env>;
Context Management
agents wraps all your methods with an AsyncLocalStorage to maintain context throughout the request lifecycle. This allows you to access the current agent, connection, request, or email (depending on what event is being handled) from anywhere in your code:
import { getCurrentAgent } from "agents";
function someUtilityFunction() {
const { agent, connection, request, email } = getCurrentAgent();
if (agent) {
console.log("Current agent:", agent.name);
}
if (connection) {
console.log("WebSocket connection ID:", connection.id);
}
}
this.onError
Agent extends Server's onError so it can be used to handle errors that are not necessarily WebSocket errors. It is called with a Connection or unknown error.
class MyAgent extends Agent {
onError(connectionOrError: Connection | unknown, error?: unknown) {
if (error) {
// WebSocket connection error
console.error("Connection error:", error);
} else {
// Server error
console.error("Server error:", connectionOrError);
}
// Optionally throw to propagate the error
throw connectionOrError;
}
}
this.destroy
this.destroy() drops all tables, deletes alarms, clears storage, and aborts the context. To ensure that the Durable Object is fully evicted, this.ctx.abort() is called asynchronously using setTimeout() to allow any currently executing handlers (like scheduled tasks) to complete their cleanup operations before the context is aborted.
This means this.ctx.abort() throws an uncatchable error that will show up in your logs, but it does so after yielding to the event loop (read more about it in abort()).
The destroy() method can be safely called within scheduled tasks. When called from within a schedule callback, the Agent sets an internal flag to skip any remaining database updates, and yields ctx.abort() to the event loop to ensure the alarm handler completes cleanly before the Agent is evicted.
class MyAgent extends Agent {
async onStart() {
console.log("Agent is starting up...");
// Initialize your agent
}
async cleanup() {
// This wipes everything!
await this.destroy();
}
async selfDestruct() {
// Safe to call from within a scheduled task
await this.schedule(60, "destroyAfterDelay", {});
}
async destroyAfterDelay() {
// This will safely destroy the Agent even when
// called from within the alarm handler
await this.destroy();
}
}
Using destroy() in scheduled tasks
You can safely call this.destroy() from within a scheduled task callback. The Agent SDK sets an internal flag to prevent database updates after destruction and defers the context abort to ensure the alarm handler completes cleanly.
static options
Configure agent behavior by overriding static options on your class. All fields are optional — defaults are applied at runtime.
export class MyAgent extends Agent {
static options = {
hibernate: true,
sendIdentityOnConnect: false,
retry: { maxAttempts: 5, baseDelayMs: 200, maxDelayMs: 5000 },
};
}
| Option | Type | Default | Description |
|---|---|---|---|
hibernate |
boolean |
true |
Whether the agent hibernates when inactive. WebSocket connections stay open while the DO sleeps |
sendIdentityOnConnect |
boolean |
true |
Send identity (agent name, instance name) to clients on WebSocket connect. Set to false to hide sensitive instance names |
hungScheduleTimeoutSeconds |
number |
30 |
Timeout before a running interval schedule is considered hung and force-reset. Increase for long-running callbacks |
keepAliveIntervalMs |
number |
30000 |
Interval in milliseconds for keepAlive() alarm heartbeats. Lower values mean faster recovery but more frequent alarms |
retry |
RetryOptions |
{ maxAttempts: 3, baseDelayMs: 100, maxDelayMs: 3000 } |
Default retry options for schedule(), queue(), and this.retry(). Per-task options override these defaults |
this.keepAlive() and this.keepAliveWhile()
Durable Objects are evicted after a period of inactivity (typically 70–140 seconds with no incoming requests, WebSocket messages, or alarms). During long-running operations — streaming LLM responses, waiting on external APIs, running multi-step computations — the agent can be evicted mid-flight.
keepAlive() creates an alarm heartbeat that prevents eviction. keepAliveWhile() wraps an async function and guarantees cleanup.
class MyAgent extends Agent {
async handleLongTask() {
// Option 1: manual dispose
const dispose = await this.keepAlive();
try {
await longRunningComputation();
} finally {
dispose();
}
// Option 2: automatic cleanup (recommended)
const result = await this.keepAliveWhile(async () => {
return await longRunningComputation();
});
}
}
AIChatAgent uses keepAliveWhile internally to keep the agent alive during streaming LLM responses. For more details, refer to Schedule tasks — Keeping the agent alive.
Routing
The Agent class re-exports the addressing helpers as getAgentByName and routeAgentRequest.
const stub = await getAgentByName(env.MY_DO, "foo");
await stub.someMethod();
const res = await routeAgentRequest(request, env);
if (res) return res;
return new Response("Not found", { status: 404 });
Layer 3: AIChatAgent
The AIChatAgent class from @cloudflare/ai-chat extends Agent with an opinionated layer for AI chat. It adds automatic message persistence to SQLite, resumable streaming, tool support (server-side, client-side, and human-in-the-loop), and a React hook (useAgentChat) for building chat UIs.
The full hierarchy is: DurableObject > Server > Agent > AIChatAgent.
If you are building a chat agent, start with AIChatAgent. If you need lower-level control or are not building a chat interface, use Agent directly.
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agents/runtime/lifecycle/agent-class/#page","headline":"Agent class internals","description":"Explore how the Agent class extends Durable Objects to provide state, WebSockets, scheduling, and RPC.","url":"https://developers.cloudflare.com/agents/runtime/lifecycle/agent-class/","inLanguage":"en","image":"https://developers.cloudflare.com/agents/runtime/lifecycle/agent-class/og.png?v=a31f520b4b74a2c7","dateModified":"2026-08-17","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: Expose Agent methods to external clients over WebSocket RPC using the @callable() decorator. title: Callable methods image: https://developers.cloudflare.com/agents/runtime/lifecycle/callable-methods/og.png?v=30f5b86891924c7a
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/agents/llms.txt
Use this file to discover all available pages before exploring further.
Docs