Skip to content

Commit 0689f9a

Browse files
committed
Add Agent class internals documentation
Synced from cloudflare/agents PR #636 This adds comprehensive documentation explaining the internal architecture of the Agent class, including its layered structure (DurableObject -> Server -> Agent) and detailed explanations of all core features and APIs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 3f7d354 commit 0689f9a

File tree

1 file changed

+40
-40
lines changed

1 file changed

+40
-40
lines changed

src/content/docs/agents/concepts/agent-class.mdx

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
11
---
2-
title: Understanding the Agent class
2+
title: Agent class internals
33
pcx_content_type: concept
44
sidebar:
5-
order: 10
5+
order: 5
66
---
77

8-
import { Render } from "~/components";
8+
The core of the `agents` library is the exported `Agent` class. Following the pattern from [Durable Objects](/durable-objects/api/), the main API for developers is to extend the `Agent` so those classes inherit all the built-in features. While this effectively is a supercharged primitive that allows developers to only write the logic they need in their agents, it obscures the inner workings.
99

10-
The core of the `agents` library is the exported `Agent` class. Following the pattern from [Durable Objects](/durable-objects/api/), the main API for developers is to extend the `Agent` class so those classes inherit all the built-in features. While this effectively is a supercharged primitive that allows developers to only write the logic they need in their agents, it obscures the inner workings.
10+
This document tries to bridge that gap, empowering any developer aiming to get started writing agents to get the full picture and avoid common pitfalls. The snippets shown here are primarily illustrative and don't necessarily represent best practices. For a more in-depth look at the inner workings of the `Agent` class, check out the [API reference](/agents/api-reference/) and the [source code](https://github.com/cloudflare/agents/blob/main/packages/agents/src/index.ts).
1111

12-
This document tries to bridge that gap, empowering any developer aiming to get started writing agents to get the full picture and avoid common pitfalls. The snippets shown here are primarily illustrative and don't necessarily represent best practices.
13-
14-
## What is the Agent class?
12+
## What is the Agent?
1513

1614
The `Agent` class is an extension of `DurableObject`. That is to say, they _are_ **Durable Objects**. If you're not familiar with Durable Objects, it is highly recommended that you read [What are Durable Objects](/durable-objects/) but at their core, Durable Objects are globally addressable (each instance has a unique ID) single-threaded compute instances with long term storage (KV/SQLite).
1715

1816
That being said, `Agent` does **not** extend `DurableObject` directly but instead `Server`. `Server` is a class provided by [PartyKit](https://github.com/cloudflare/partykit/tree/main/packages/partyserver).
1917

20-
You can visualize the logic as a Matryoshka doll: **DurableObject** **Server** **Agent**.
18+
You can visualize the logic as a Matryoshka doll: **DurableObject** -> **Server** -> **Agent**.
2119

2220
## Layer 0: Durable Object
2321

2422
This won't cover Durable Objects in detail, but it's good to know what primitives they expose so we understand how the outer layers make use of them. The Durable Object class comes with:
2523

26-
### constructor
24+
### `constructor`
2725

2826
```ts
2927
constructor(ctx: DurableObjectState, env: Env) {}
@@ -48,7 +46,7 @@ const stub = env.MY_DO.getByName("foo");
4846
await stub.bar();
4947
```
5048

51-
### fetch()
49+
### `fetch()`
5250

5351
Durable Objects can take a `Request` from a Worker and send a `Response` back. This can **only** be done through the [`fetch`](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/#invoking-the-fetch-handler) method (which the developer must implement).
5452

@@ -81,17 +79,17 @@ export class MyDurableObject extends DurableObject {
8179
}
8280
```
8381

84-
### alarm()
82+
### `alarm()`
8583

86-
HTTP and RPC requests are not the only entrypoints for a DO. Alarms allow developers to schedule tasks to run at a later time. Whenever the runtime knows an alarm is due, it will call the `alarm()` method, which is left to the developer to implement.
84+
HTTP and RPC requests are not the only entrypoints for a DO. Alarms allow developers to schedule an event to trigger at a later time. Whenever the next alarm is due, the runtime will call the `alarm()` method, which is left to the developer to implement.
8785

8886
To schedule an alarm, you can use the `this.ctx.storage.setAlarm()` method. For more information, refer to [Alarms](/durable-objects/api/alarms/).
8987

90-
### this.ctx
88+
### `this.ctx`
9189

9290
The base `DurableObject` class sets the [DurableObjectState](/durable-objects/api/state/) into `this.ctx`. There are a lot of interesting methods and properties, but we'll focus on `this.ctx.storage`.
9391

94-
### this.ctx.storage
92+
### `this.ctx.storage`
9593

9694
[DurableObjectStorage](/durable-objects/api/sqlite-storage-api/) is the main interface with the DO's persistence mechanisms, which include both a KV and SQLITE **synchronous** APIs.
9795

@@ -106,15 +104,15 @@ const rows = sql.exec("SELECT * FROM contacts WHERE country = ?", "US");
106104
const token = kv.get("someToken");
107105
```
108106

109-
### this.ctx.env
107+
### `this.ctx.env`
110108

111-
Lastly, it's worth mentioning that the DO also has the Worker `Env` in `this.env`. Read more about [bindings](/workers/runtime-apis/bindings).
109+
Lastly, it's worth mentioning that the DO also has the Worker `Env` in `this.env`. Read more in [Bindings](/workers/runtime-apis/bindings).
112110

113-
## Layer 1: PartyKit Server
111+
## Layer 1: Partykit `Server`
114112

115113
Now that you've seen what Durable Objects come with out-of-the-box, what [PartyKit](https://github.com/cloudflare/partykit)'s `Server` (package `partyserver`) implements will be clearer. It's an **opinionated `DurableObject` wrapper that improves DX by hiding away DO primitives in favor of more developer friendly callbacks**.
116114

117-
An important note is that `Server` **does NOT make use any of the DO storage** so you will not see extra operations.
115+
An important note is that `Server` **does NOT persist to the DO storage** so you will not see extra storage operations by using it.
118116

119117
### Addressing
120118

@@ -133,20 +131,20 @@ await stub.bar();
133131
Since we have a URL addressing scheme, we also get access to `routePartykitRequest()`.
134132

135133
```ts
136-
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
137-
// Behind the scenes, PartyKit normalizes your DO binding names
138-
// and tries to do some pattern matching.
139-
const res = await routePartykitRequest(request, env);
134+
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
135+
// Behind the scenes, PartyKit normalizes your DO binding names
136+
// and tries to do some pattern matching.
137+
const res = await routePartykitRequest(request, env);
140138

141-
if (res) return res;
139+
if (res) return res;
142140

143-
return Response("Not found", { status: 404 });
144-
}
141+
return Response("Not found", { status: 404 });
142+
}
145143
```
146144

147145
You can have a look at [the implementation](https://github.com/cloudflare/partykit/blob/main/packages/partyserver/src/index.ts#L122) if you're interested.
148146

149-
### onStart
147+
### `onStart`
150148

151149
The extra plumbing that `Server` includes on addressing allows it to expose an `onStart` callback that is **executed every time the DO starts up** (the DO was evicted, hibernated or never created at all) and **before any `fetch` or RPC**.
152150

@@ -161,7 +159,7 @@ class MyServer extends Server {
161159
}
162160
```
163161

164-
### onRequest and onConnect
162+
### `onRequest` and `onConnect`
165163

166164
`Server` already implements `fetch` for the underlying Durable Object and exposes 2 different callbacks that developers can make use of, `onRequest` and `onConnect` for HTTP requests and incoming WS connections, respectively (**WebSocket connections are accepted by default**).
167165

@@ -189,15 +187,15 @@ Just as `onConnect` is the callback for every new connection, `Server` also prov
189187

190188
There's also `this.broadcast` that sends a WS message to all connected clients (no magic, just a loop over `this.getConnections()`!).
191189

192-
### this.name
190+
### `this.name`
193191

194-
It's hard to get a Durable Object's `name` from within it. `partyserver` tries to make it available in `this.name` but it's not a perfect solution. Read more about it [here](https://github.com/cloudflare/workerd/issues/2240).
192+
It's hard to get a Durable Object's `name` from within it. `partyserver` tries to make it available in `this.name` but it's not a perfect solution. Read more about it in [this GitHub issue](https://github.com/cloudflare/workerd/issues/2240).
195193

196194
## Layer 2: Agent
197195

198196
Now finally, the `Agent` class. `Agent` extends `Server` and provides opinionated primitives for stateful, schedulable, and observable agents that can communicate via RPC, WebSockets, and (even!) email.
199197

200-
### this.state and this.setState()
198+
### `this.state` and `this.setState()`
201199

202200
One of the core features of `Agent` is **automatic state persistence**. Developers define the shape of their state via the generic parameter and `initialState` (which is only used if no state exists in storage), and the Agent handles loading, saving, and broadcasting state changes (check `Server`'s `this.broadcast()` above).
203201

@@ -221,7 +219,7 @@ class MyAgent extends Agent<Env, { count: number }> {
221219

222220
State is stored in the `cf_agents_state` SQL table. State messages are sent with `type: "cf_agent_state"` (both from the client and the server). Since the `agents` provides [JS and React clients](/agents/api-reference/store-and-sync-state/#synchronizing-state), real-time state updates are available out of the box.
223221

224-
### this.sql
222+
### `this.sql`
225223

226224
The Agent provides a convenient `sql` template tag for executing queries against the Durable Object's SQL storage. It constructs parameterized queries and executes them. This uses the **synchronous** SQL API from `this.ctx.storage.sql`.
227225

@@ -249,7 +247,7 @@ class MyAgent extends Agent {
249247

250248
### RPC and Callable Methods
251249

252-
`agents` take Durable Objects RPC one step forward by implementing RPC through WebSockets, so clients can also call methods on the Agent directly. To make a method callable, developers can use the `@callable` decorator. Methods can return a serializable value or a stream (when using `@callable({ stream: true })`).
250+
`agents` take Durable Objects RPC one step forward by implementing RPC through WebSockets, so clients can also call methods on the Agent directly. To make a method callable through WS, developers can use the `@callable` decorator. Methods can return a serializable value or a stream (when using `@callable({ stream: true })`).
253251

254252
```ts
255253
class MyAgent extends Agent {
@@ -279,7 +277,7 @@ const result = await stub.add(2, 3);
279277
console.log(result); // 5
280278
```
281279

282-
### this.queue and friends
280+
### `this.queue` and friends
283281

284282
Agents include a built-in task queue for deferred execution. This is useful for offloading work or retrying operations. The available methods are `this.queue`, `this.dequeue`, `this.dequeueAll`, `this.dequeueAllByCallback`, `this.getQueue`, and `this.getQueues`.
285283

@@ -298,13 +296,15 @@ class MyAgent extends Agent {
298296

299297
Tasks are stored in the `cf_agents_queues` SQL table and are automatically flushed in sequence. If a task succeeds, it's automatically dequeued.
300298

301-
### this.schedule and friends
299+
### `this.schedule` and friends
302300

303301
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).
304302

303+
Since DOs only allow one alarm at a time, the `Agent` class works around this by managing multiple schedules in SQL and using a single alarm.
304+
305305
```ts
306306
class MyAgent extends Agent {
307-
async onStart() {
307+
async foo() {
308308
// Schedule at a specific time
309309
await this.schedule(new Date("2025-12-25T00:00:00Z"), "sendGreeting", {
310310
message: "Merry Christmas!"
@@ -333,9 +333,9 @@ class MyAgent extends Agent {
333333

334334
Schedules are stored in the `cf_agents_schedules` SQL table. Cron schedules automatically reschedule themselves after execution, while one-time schedules are deleted.
335335

336-
### this.mcp and friends
336+
### `this.mcp` and friends
337337

338-
`Agent` includes a multi-server MCP client. This enables your Agent to interact with external services that expose MCP interfaces. The MCP client is documented in detail at [MCP Client API](/agents/model-context-protocol/mcp-client-api/).
338+
`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](/agents/model-context-protocol/mcp-client-api/).
339339

340340
```ts
341341
class MyAgent extends Agent {
@@ -407,7 +407,7 @@ function someUtilityFunction() {
407407
}
408408
```
409409

410-
### this.onError
410+
### `this.onError`
411411

412412
`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.
413413

@@ -428,9 +428,9 @@ class MyAgent extends Agent {
428428
}
429429
```
430430

431-
### this.destroy
431+
### `this.destroy`
432432

433-
`destroy()` drops all tables, deletes alarms, clears storage, and aborts the context. The `this.abort` method that is called by `this.destroy` comes from `DurableObject` and ensures that the Agent is fully evicted. In order to do so, it throws an uncatchable error that will show up in your logs (read more at [abort()](/durable-objects/api/state/#abort)).
433+
`this.destroy()` drops all tables, deletes alarms, clears storage, and aborts the context. To ensure that the DO is fully evicted, `this.ctx.abort()` is called, which throws an uncatchable error that will show up in your logs (read more about it in [abort()](/durable-objects/api/state/#abort)).
434434

435435
```ts
436436
class MyAgent extends Agent {

0 commit comments

Comments
 (0)