Skip to content

Commit aa1f0ce

Browse files
Sync documentation from PR #812
Updates: - Add scheduleEvery() interval scheduling documentation - Document custom routing with basePath and server-sent identity - Add identity change handling and security options - Update examples to follow Cloudflare docs style guide Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent b9d703f commit aa1f0ce

File tree

2 files changed

+198
-5
lines changed

2 files changed

+198
-5
lines changed

src/content/docs/agents/api-reference/calling-agents.mdx

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,139 @@ export class MyAgent extends Agent<Env> {
178178
```
179179
</TypeScriptExample>
180180

181-
Replace `userId` with `teamName`, `channel`, `companyName` as fits your Agents goals - and/or configure authentication to ensure Agents are only created for known, authenticated users.
181+
Replace `userId` with `teamName`, `channel`, `companyName` as fits your Agents goals - and configure authentication to ensure Agents are only created for known, authenticated users.
182+
183+
### Custom routing with basePath
184+
185+
For advanced use cases, you can bypass the default `/agents/:agent/:name` URL pattern and define custom routing. This is useful when the instance name should be determined server-side (for example, from authentication or session data) or when you need clean URLs without the `/agents/` prefix.
186+
187+
**Client-side with basePath:**
188+
189+
<TypeScriptExample>
190+
191+
```ts
192+
import { useAgent } from "agents/react";
193+
194+
// Connect to /user instead of /agents/user-agent/...
195+
const agent = useAgent({
196+
agent: "UserAgent", // Required but ignored when basePath is set
197+
basePath: "user" // Connects to /user
198+
});
199+
```
200+
201+
</TypeScriptExample>
202+
203+
**Server-side routing:**
204+
205+
When using `basePath`, your Worker must handle the custom path and forward requests to the appropriate agent instance:
206+
207+
<TypeScriptExample>
208+
209+
```ts
210+
import { Agent, getAgentByName, routeAgentRequest } from "agents";
211+
212+
export default {
213+
async fetch(request: Request, env: Env) {
214+
const url = new URL(request.url);
215+
216+
// Custom routing - server determines instance from session
217+
if (url.pathname === "/user") {
218+
const session = await getSession(request);
219+
const agent = await getAgentByName(env.UserAgent, session.userId);
220+
return agent.fetch(request); // Forward request directly to agent
221+
}
222+
223+
// Default routing for standard /agents/... paths
224+
return (
225+
routeAgentRequest(request, env) ||
226+
new Response("Not found", { status: 404 })
227+
);
228+
}
229+
};
230+
```
231+
232+
</TypeScriptExample>
233+
234+
**Server-sent identity:**
235+
236+
When using `basePath`, clients do not know which instance they connected to until the server sends the identity. Agents automatically send their identity on connection:
237+
238+
<TypeScriptExample>
239+
240+
```ts
241+
const agent = useAgent({
242+
agent: "UserAgent",
243+
basePath: "user",
244+
onIdentity: (name, agentType) => {
245+
console.log(`Connected to ${agentType} instance: ${name}`);
246+
// for example, "Connected to user-agent instance: user-123"
247+
}
248+
});
249+
250+
// Reactive state - re-renders when identity is received
251+
return (
252+
<div>
253+
{agent.identified ? `Connected to: ${agent.name}` : "Connecting..."}
254+
</div>
255+
);
256+
```
257+
258+
</TypeScriptExample>
259+
260+
**Handling identity changes:**
261+
262+
If the identity changes on reconnect (for example, session expired and user logs in as someone else), handle it with `onIdentityChange`:
263+
264+
<TypeScriptExample>
265+
266+
```ts
267+
const agent = useAgent({
268+
agent: "UserAgent",
269+
basePath: "user",
270+
onIdentityChange: (oldName, newName, oldAgent, newAgent) => {
271+
console.log(`Session changed: ${oldName} → ${newName}`);
272+
// Refresh state, show notification, and more
273+
}
274+
});
275+
```
276+
277+
</TypeScriptExample>
278+
279+
**Disabling identity for security:**
280+
281+
If your instance names contain sensitive data (session IDs or internal user IDs), disable identity sending:
282+
283+
<TypeScriptExample>
284+
285+
```ts
286+
export class SecureAgent extends Agent {
287+
// Do not expose instance names to clients
288+
static options = { sendIdentityOnConnect: false };
289+
}
290+
```
291+
292+
</TypeScriptExample>
293+
294+
When identity is disabled:
295+
- `agent.identified` stays `false`
296+
- `agent.ready` never resolves (use state updates instead)
297+
- `onIdentity` and `onIdentityChange` are never called
298+
299+
**Sub-paths with the path option:**
300+
301+
Append additional path segments to the URL:
302+
303+
<TypeScriptExample>
304+
305+
```ts
306+
// With basePath: /user/settings
307+
useAgent({ agent: "UserAgent", basePath: "user", path: "settings" });
308+
309+
// Standard routing: /agents/my-agent/room/settings
310+
useAgent({ agent: "MyAgent", name: "room", path: "settings" });
311+
```
312+
313+
</TypeScriptExample>
182314

183315
### Authenticating Agents
184316

src/content/docs/agents/api-reference/schedule-tasks.mdx

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ sidebar:
77

88
import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
99

10-
An Agent can schedule tasks to be run in the future by calling `this.schedule(when, callback, data)`, where `when` can be a delay, a `Date`, or a cron string; `callback` the function name to call, and `data` is an object of data to pass to the function.
10+
An Agent can schedule tasks to be run in the future by calling `this.schedule(when, callback, data)`, where `when` can be a delay, a `Date`, or a cron string; `callback` the function name to call, and `data` is an object of data to pass to the function. For fixed-interval recurring tasks, use `this.scheduleEvery(intervalSeconds, callback, data)`.
1111

1212
Scheduled tasks can do anything a request or message from a user can: make requests, query databases, send emails, read+write state: scheduled tasks can invoke any regular method on your Agent.
1313

@@ -63,19 +63,22 @@ let task = await this.schedule(10, "someTask", { message: "hello" });
6363
// schedule a task to run at a specific date
6464
let task = await this.schedule(new Date("2025-01-01"), "someTask", {});
6565

66-
// schedule a task to run every 10 minutes
66+
// schedule a task to run every 10 minutes (cron)
6767
let { id } = await this.schedule("*/10 * * * *", "someTask", { message: "hello" });
6868

6969
// schedule a task to run every 10 minutes, but only on Mondays
7070
let task = await this.schedule("*/10 * * * 1", "someTask", { message: "hello" });
7171

72+
// schedule a task to run every 30 seconds (interval)
73+
let task = await this.scheduleEvery(30, "pollData", { source: "api" });
74+
7275
// cancel a scheduled task
7376
this.cancelSchedule(task.id);
7477
```
7578

7679
</TypeScriptExample>
7780

78-
Calling `await this.schedule` returns a `Schedule`, which includes the task's randomly generated `id`. You can use this `id` to retrieve or cancel the task in the future. It also provides a `type` property that indicates the type of schedule, for example, one of `"scheduled" | "delayed" | "cron"`.
81+
Calling `await this.schedule` or `await this.scheduleEvery` returns a `Schedule`, which includes the task's randomly generated `id`. You can use this `id` to retrieve or cancel the task in the future. It also provides a `type` property that indicates the type of schedule, for example, one of `"scheduled" | "delayed" | "cron" | "interval"`.
7982

8083
:::note[Maximum scheduled tasks]
8184

@@ -89,6 +92,58 @@ You can safely call `this.destroy()` from within a scheduled task callback. The
8992

9093
:::
9194

95+
### Interval scheduling with scheduleEvery()
96+
97+
For tasks that need to run at fixed intervals, use `this.scheduleEvery()`. Unlike cron expressions (which have minute-level precision), intervals support sub-minute timing and arbitrary durations:
98+
99+
<TypeScriptExample>
100+
101+
```ts
102+
// Poll an API every 30 seconds
103+
await this.scheduleEvery(30, "pollApi", { endpoint: "/updates" });
104+
105+
// Health check every 45 seconds
106+
await this.scheduleEvery(45, "healthCheck", {});
107+
108+
// Sync data every 90 seconds (cannot be expressed in cron)
109+
await this.scheduleEvery(90, "syncData", { destination: "warehouse" });
110+
```
111+
112+
</TypeScriptExample>
113+
114+
**Key differences from cron:**
115+
116+
| Feature | Cron | Interval |
117+
| ------------------- | ------------------------------ | ---------------------- |
118+
| Minimum granularity | 1 minute | 1 second |
119+
| Arbitrary intervals | No (must fit cron pattern) | Yes |
120+
| Fixed schedule | Yes (for example, every day at 8am) | No (relative to start) |
121+
| Overlap prevention | No | Yes (built-in) |
122+
123+
**Overlap prevention**: If a callback takes longer than the interval, the next execution is skipped to prevent resource exhaustion. A warning is logged when this occurs.
124+
125+
**Error resilience**: If a callback throws an error, the interval continues running. Only that specific execution fails.
126+
127+
<TypeScriptExample>
128+
129+
```ts
130+
export class PollingAgent extends Agent {
131+
async onRequest() {
132+
// Start polling every 30 seconds
133+
await this.scheduleEvery(30, "poll", {});
134+
return new Response("Polling started");
135+
}
136+
137+
async poll() {
138+
// If this takes longer than 30 seconds, the next execution is skipped
139+
const data = await fetch("https://api.example.com/data");
140+
await this.processData(await data.json());
141+
}
142+
}
143+
```
144+
145+
</TypeScriptExample>
146+
92147
### Managing scheduled tasks
93148

94149
You can get, cancel and filter across scheduled tasks within an Agent using the scheduling API:
@@ -109,13 +164,19 @@ let tasks = this.getSchedules();
109164
await this.cancelSchedule(task.id);
110165

111166
// Filter for specific tasks
112-
// e.g. all tasks starting in the next hour
167+
// for example, all tasks starting in the next hour
113168
let tasks = this.getSchedules({
114169
timeRange: {
115170
start: new Date(Date.now()),
116171
end: new Date(Date.now() + 60 * 60 * 1000),
117172
}
118173
});
174+
175+
// Get only interval schedules
176+
let intervalTasks = this.getSchedules({ type: "interval" });
177+
178+
// Get only cron schedules
179+
let cronTasks = this.getSchedules({ type: "cron" });
119180
```
120181

121182
</TypeScriptExample>

0 commit comments

Comments
 (0)