Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5b2fe1d
add task management system with lifecycle tracking
whoiskatrin Nov 27, 2025
a735b0b
update task runner example; add workflow integration
whoiskatrin Nov 27, 2025
f1fef29
fixed the example
whoiskatrin Nov 27, 2025
959894a
Merge main and resolve package-lock.json conflict
whoiskatrin Nov 27, 2025
4a18dc0
fix timeouts and cancellations
whoiskatrin Nov 27, 2025
9689e8f
formatting
whoiskatrin Nov 27, 2025
999c4c7
refactor task runner and agent code
whoiskatrin Nov 27, 2025
2f93bbe
more fixes to this
whoiskatrin Nov 27, 2025
a076799
updated task execution logic in Agent
whoiskatrin Dec 14, 2025
7b09f18
Merge origin/main and resolve conflict in react.tsx
whoiskatrin Dec 14, 2025
fd47f5d
Fix lint: use template literals, remove unused imports
whoiskatrin Dec 14, 2025
e16fde5
Remove deprecated annotation from getWorkflowInfo - still needed for …
whoiskatrin Dec 14, 2025
955b8ba
Clarify durable task behavior: document that @task({ durable: true })…
whoiskatrin Dec 14, 2025
d172574
add tests and fix some types
whoiskatrin Dec 14, 2025
df0eb60
nits
whoiskatrin Dec 14, 2025
16189ff
more updates
whoiskatrin Dec 14, 2025
164abd9
refactor TaskTracker and DurableTaskWorkflow
whoiskatrin Dec 14, 2025
150c5aa
\emove Task system and related types, update agent and react componen…
whoiskatrin Dec 24, 2025
7d76413
update agent and React components to utilize Cloudflare Workflows for…
whoiskatrin Dec 24, 2025
80d5bab
Merge main and resolve package-lock.json conflict
whoiskatrin Dec 29, 2025
80fbcd1
Update dependencies in package-lock.json and task-runner package.json
whoiskatrin Dec 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions examples/task-runner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# Workflow Example

Demonstrates Cloudflare Workflows integration with Agents SDK.

## Overview

This example shows two approaches for running background work:

1. **Quick Analysis** - Runs directly in the Agent using `ctx.waitUntil()`, good for < 30s operations
2. **Deep Analysis** - Uses Cloudflare Workflows for durable, long-running operations

## Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│ Client (React) │
│ │
│ await agent.call("quickAnalysis", [{ repoUrl }]) │
│ await agent.call("startAnalysis", [{ repoUrl }]) │
│ │ │
└───────────────────────────┼─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Agent (Durable Object) │
│ │
│ quickAnalysis() │ startAnalysis() │
│ - Uses waitUntil() │ - Creates workflow instance │
│ - Fast, simple │ - env.ANALYSIS_WORKFLOW.create() │
│ - < 30s tasks │ - Returns task ID │
│ │ │ │ │
└─────────┼────────────────┴───────────┼──────────────────────────┘
│ │
▼ ▼
[Executes [Dispatches to
in Agent] Workflow]
┌─────────────────────────────────────────────────────────────────┐
│ Cloudflare Workflow │
│ │
│ class AnalysisWorkflow extends WorkflowEntrypoint { │
│ async run(event, step) { │
│ await step.do("fetch", ...); // Durable step │
│ await step.sleep("1 hour"); // Survives restarts │
│ await step.do("analyze", ...); // Auto-retry on failure │
│ } │
│ } │
│ │
│ // Sends updates back to Agent via RPC │
│ agent.handleWorkflowUpdate({ taskId, progress, event }) │
└─────────────────────────────────────────────────────────────────┘
```

## When to Use Each

| Feature | waitUntil() | Cloudflare Workflow |
| ---------- | ------------------- | ------------------------ |
| Duration | Seconds to minutes | Minutes to days |
| Execution | In Durable Object | Separate Workflow engine |
| Durability | Lost on DO eviction | Survives restarts |
| Retries | Manual | Automatic per-step |
| Sleep | Not durable | Durable (can wait hours) |
| Complexity | Simple | More setup required |

## Setup

1. Install dependencies:

```bash
npm install
```

2. Create `.dev.vars`:

```
OPENAI_API_KEY=sk-...
```

3. Run development server:

```bash
npm run dev
```

4. Open http://localhost:5173

## Server Implementation

```typescript
// Quick task - runs in Agent
@callable()
async quickAnalysis(input: { repoUrl: string }): Promise<{ id: string }> {
const taskId = `task_${crypto.randomUUID().slice(0, 12)}`;

// Track in state for UI updates
this.updateTask(taskId, { status: "running" });

// Run in background
this.ctx.waitUntil(this.runAnalysis(taskId, input.repoUrl));

return { id: taskId };
}

// Durable task - dispatches to Workflow
@callable()
async startAnalysis(input: { repoUrl: string }) {
const taskId = `task_${crypto.randomUUID().slice(0, 12)}`;

// Create workflow instance
const instance = await this.env.ANALYSIS_WORKFLOW.create({
id: taskId,
params: { repoUrl: input.repoUrl, _agentBinding: "task-runner", _agentName: this.name }
});

// Track in state
this.updateTask(taskId, { status: "pending", workflowInstanceId: instance.id });

return { id: taskId, workflowInstanceId: instance.id };
}
```

## Workflow Implementation

```typescript
// src/workflows/analysis.ts
import {
WorkflowEntrypoint,
WorkflowStep,
WorkflowEvent
} from "cloudflare:workers";

export class AnalysisWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
// Durable step - persisted, auto-retry on failure
const files = await step.do("fetch-repo", async () => {
// This survives worker restarts
return await fetchFiles(event.payload.repoUrl);
});

// Durable sleep - can wait for hours
await step.sleep("rate-limit", "1 hour");

// Step with retry config
const analysis = await step.do(
"analyze",
{ retries: { limit: 3, backoff: "exponential" } },
async () => analyzeFiles(files)
);

return analysis;
}
}
```

## Configuration (wrangler.jsonc)

```jsonc
{
"durable_objects": {
"bindings": [{ "name": "task-runner", "class_name": "TaskRunner" }]
},
"workflows": [
{
"name": "analysis-workflow",
"binding": "ANALYSIS_WORKFLOW",
"class_name": "AnalysisWorkflow"
}
]
}
```

## Files

- `src/server.ts` - Agent with quick and workflow-based analysis
- `src/workflows/analysis.ts` - Durable workflow implementation
- `src/App.tsx` - React UI demonstrating both modes
- `wrangler.jsonc` - Cloudflare configuration
23 changes: 23 additions & 0 deletions examples/task-runner/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Task Runner - Agents SDK Example</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: #f5f5f5;
min-height: 100vh;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
26 changes: 26 additions & 0 deletions examples/task-runner/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "task-runner-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"deploy": "wrangler deploy"
},
"dependencies": {
"openai": "^4.77.0",
"react": "^19.2.3",
"react-dom": "^19.2.3"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^1.19.0",
"@cloudflare/workers-types": "^4.20251221.0",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"typescript": "^5.9.3",
"vite": "^7.3.0",
"wrangler": "^4.56.0"
}
}
Loading
Loading