Skip to content

Commit c6836a4

Browse files
docs: add task management and workflow integration documentation
Syncs documentation from cloudflare/agents PR #683 cloudflare/agents#683 Adds comprehensive documentation for the task system including: - Tasks: Tracked background operations with progress updates - Durable Tasks: Long-running operations backed by Cloudflare Workflows New pages: - /agents/api-reference/tasks/ - Task decorator, TaskContext API, and client updates - /agents/api-reference/durable-tasks/ - Durable task setup, workflow context methods, and custom workflows
1 parent e48bbb5 commit c6836a4

File tree

2 files changed

+369
-0
lines changed

2 files changed

+369
-0
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
---
2+
title: Durable Tasks
3+
pcx_content_type: concept
4+
sidebar:
5+
order: 12
6+
7+
---
8+
9+
import { Render, TypeScriptExample, WranglerConfig } from "~/components";
10+
11+
Durable tasks are long-running operations backed by [Cloudflare Workflows](/workflows/). They survive agent restarts and can run for hours or days.
12+
13+
## Setup
14+
15+
1. Export `DurableTaskWorkflow` from your worker:
16+
17+
<TypeScriptExample>
18+
19+
```ts
20+
// src/index.ts
21+
import { Agent, routeAgentRequest, DurableTaskWorkflow } from "agents";
22+
23+
export { DurableTaskWorkflow };
24+
25+
export class MyAgent extends Agent<Env> {
26+
// ...
27+
}
28+
```
29+
30+
</TypeScriptExample>
31+
32+
2. Add the workflow binding to `wrangler.jsonc`:
33+
34+
<WranglerConfig>
35+
36+
```jsonc
37+
{
38+
"workflows": [
39+
{
40+
"name": "durable-tasks",
41+
"binding": "DURABLE_TASKS_WORKFLOW",
42+
"class_name": "DurableTaskWorkflow"
43+
}
44+
]
45+
}
46+
```
47+
48+
</WranglerConfig>
49+
50+
## Usage
51+
52+
Mark a task as durable with `durable: true`:
53+
54+
<TypeScriptExample>
55+
56+
```ts
57+
import { Agent, task, type TaskContext } from "agents";
58+
59+
class MyAgent extends Agent<Env> {
60+
@task({ durable: true })
61+
async longAnalysis(input: { repoUrl: string }, ctx: TaskContext) {
62+
// Each step is checkpointed - survives restarts
63+
const files = await ctx.step("fetch", () => fetchRepoFiles(input.repoUrl));
64+
65+
// Durable sleep - can wait for hours or days
66+
await ctx.sleep("rate-limit", "1h");
67+
68+
const analysis = await ctx.step("analyze", () => analyzeFiles(files));
69+
70+
return analysis;
71+
}
72+
}
73+
```
74+
75+
</TypeScriptExample>
76+
77+
## Durable Context Methods
78+
79+
Durable tasks have additional `TaskContext` methods:
80+
81+
### ctx.step(name, fn)
82+
83+
Execute a checkpointed step. If the workflow restarts, completed steps are skipped and their results are replayed.
84+
85+
<TypeScriptExample>
86+
87+
```ts
88+
const data = await ctx.step("fetch-data", async () => {
89+
return await fetch(url).then((r) => r.json());
90+
});
91+
```
92+
93+
</TypeScriptExample>
94+
95+
### ctx.sleep(name, duration)
96+
97+
Pause execution for a duration. The workflow hibernates and resumes automatically.
98+
99+
<TypeScriptExample>
100+
101+
```ts
102+
await ctx.sleep("wait", "30m");
103+
await ctx.sleep("daily-check", "24h");
104+
```
105+
106+
</TypeScriptExample>
107+
108+
Accepts: `"30s"`, `"5m"`, `"1h"`, `"7d"`, or `"30 seconds"`, `"5 minutes"`.
109+
110+
### ctx.waitForEvent(name, options)
111+
112+
Wait for an external event (human approval, webhook).
113+
114+
<TypeScriptExample>
115+
116+
```ts
117+
const approval = await ctx.waitForEvent("approval", {
118+
type: "user-approved",
119+
timeout: "24h"
120+
});
121+
```
122+
123+
</TypeScriptExample>
124+
125+
:::note
126+
127+
`waitForEvent` is only available in durable tasks.
128+
129+
:::
130+
131+
## Retry Configuration
132+
133+
Configure automatic retries for durable tasks:
134+
135+
<TypeScriptExample>
136+
137+
```ts
138+
@task({
139+
durable: true,
140+
retry: {
141+
limit: 3,
142+
delay: "10s",
143+
backoff: "exponential"
144+
}
145+
})
146+
async unreliableTask(input: Input, ctx: TaskContext) {
147+
// Automatically retries on failure
148+
}
149+
```
150+
151+
</TypeScriptExample>
152+
153+
## When to Use Durable Tasks
154+
155+
Use `@task()` (simple) for:
156+
157+
- Operations under 30 seconds
158+
- Tasks that can restart from scratch
159+
160+
Use `@task({ durable: true })` for:
161+
162+
- Long-running operations (minutes to days)
163+
- Multi-step workflows with checkpoints
164+
- Operations that must survive restarts
165+
- Tasks requiring durable sleep or external events
166+
167+
## Custom Workflows
168+
169+
For advanced control, extend `AgentWorkflow` instead:
170+
171+
<TypeScriptExample>
172+
173+
```ts
174+
import { AgentWorkflow, type WorkflowTaskContext } from "agents";
175+
176+
export class CustomWorkflow extends AgentWorkflow<Env, { input: string }> {
177+
async run(ctx: WorkflowTaskContext<{ input: string }>) {
178+
ctx.emit("started");
179+
180+
const result = await ctx.step("process", async () => {
181+
return processInput(ctx.params.input);
182+
});
183+
184+
ctx.setProgress(100);
185+
return result;
186+
}
187+
}
188+
```
189+
190+
</TypeScriptExample>
191+
192+
Then dispatch it with `this.workflow()`:
193+
194+
<TypeScriptExample>
195+
196+
```ts
197+
class MyAgent extends Agent<Env> {
198+
@callable()
199+
async startCustom(input: { data: string }) {
200+
return this.workflow("CUSTOM_WORKFLOW", input);
201+
}
202+
}
203+
```
204+
205+
</TypeScriptExample>
206+
207+
## Example
208+
209+
Refer to the [task-runner example](https://github.com/cloudflare/agents/tree/main/examples/task-runner) for a complete implementation with both simple and durable tasks.
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
---
2+
title: Tasks
3+
pcx_content_type: concept
4+
sidebar:
5+
order: 11
6+
7+
---
8+
9+
import { Render, TypeScriptExample, WranglerConfig } from "~/components";
10+
11+
Tasks are tracked background operations with progress updates, cancellation support, and real-time synchronization to connected clients.
12+
13+
## Quick Start
14+
15+
Use the `@task()` decorator to mark a method as a tracked task:
16+
17+
<TypeScriptExample>
18+
19+
```ts
20+
import { Agent, task, type TaskContext } from "agents";
21+
22+
class MyAgent extends Agent<Env> {
23+
@task({ timeout: "5m" })
24+
async processData(input: { url: string }, ctx: TaskContext) {
25+
ctx.emit("started", { url: input.url });
26+
ctx.setProgress(25);
27+
28+
const data = await fetch(input.url);
29+
ctx.setProgress(75);
30+
31+
return { processed: true };
32+
}
33+
}
34+
```
35+
36+
</TypeScriptExample>
37+
38+
The client receives real-time updates automatically via WebSocket.
39+
40+
## TaskContext
41+
42+
Every task method receives a `TaskContext` with:
43+
44+
- `emit(type, data)` - Send events to connected clients
45+
- `setProgress(n)` - Set progress percentage (0-100)
46+
- `signal` - AbortSignal for cancellation
47+
48+
<TypeScriptExample>
49+
50+
```ts
51+
@task({ timeout: "2m" })
52+
async analyze(input: Input, ctx: TaskContext) {
53+
ctx.emit("phase", { name: "fetching" });
54+
55+
if (ctx.signal.aborted) {
56+
throw new Error("Cancelled");
57+
}
58+
59+
ctx.setProgress(50);
60+
// ...
61+
}
62+
```
63+
64+
</TypeScriptExample>
65+
66+
## Managing Tasks
67+
68+
Access tasks via `this.tasks`:
69+
70+
<TypeScriptExample>
71+
72+
```ts
73+
// Get a task
74+
const task = this.tasks.get(taskId);
75+
76+
// List all tasks
77+
const all = this.tasks.list();
78+
79+
// List by status
80+
const running = this.tasks.list({ status: "running" });
81+
82+
// Cancel a task
83+
await this.tasks.cancel(taskId, "User requested");
84+
85+
// Delete completed task
86+
this.tasks.delete(taskId);
87+
```
88+
89+
</TypeScriptExample>
90+
91+
## Client Updates
92+
93+
Tasks broadcast updates to all connected WebSocket clients:
94+
95+
<TypeScriptExample>
96+
97+
```tsx
98+
// React client
99+
const agent = useAgent({ agent: "my-agent", name: "default" });
100+
101+
agent.addEventListener("message", (event) => {
102+
const data = JSON.parse(event.data);
103+
if (data.type === "CF_AGENT_TASK_UPDATE") {
104+
const { taskId, task } = data;
105+
// task.status, task.progress, task.events, task.result
106+
}
107+
});
108+
```
109+
110+
</TypeScriptExample>
111+
112+
## Options
113+
114+
<TypeScriptExample>
115+
116+
```ts
117+
@task({
118+
timeout: "5m", // Cancel if exceeds duration
119+
})
120+
async myTask(input: Input, ctx: TaskContext) {
121+
// ...
122+
}
123+
```
124+
125+
</TypeScriptExample>
126+
127+
Timeout accepts: `"30s"`, `"5m"`, `"1h"`, or milliseconds.
128+
129+
## Task Status
130+
131+
Tasks transition through these states:
132+
133+
- `pending` - Created, waiting to run
134+
- `running` - Currently executing
135+
- `completed` - Finished successfully
136+
- `failed` - Threw an error
137+
- `aborted` - Cancelled or timed out
138+
139+
## Durable Tasks
140+
141+
For long-running operations that need to survive restarts, use durable tasks backed by [Cloudflare Workflows](/workflows/):
142+
143+
<TypeScriptExample>
144+
145+
```ts
146+
@task({ durable: true })
147+
async longProcess(input: Input, ctx: TaskContext) {
148+
// Durable step - survives restarts
149+
const data = await ctx.step("fetch", () => fetchData(input));
150+
151+
// Durable sleep - can sleep for days
152+
await ctx.sleep("rate-limit", "1h");
153+
154+
return await ctx.step("process", () => process(data));
155+
}
156+
```
157+
158+
</TypeScriptExample>
159+
160+
Refer to [Durable Tasks](/agents/api-reference/durable-tasks/) for setup and details.

0 commit comments

Comments
 (0)