Cloudflare Workflows を Agents と統合すると、耐久性のある複数ステップのバックグラウンド処理を Workflows が担い、リアルタイム通信は Agents が担います。
元の Agent へ型付きでアクセスするには AgentWorkflow を継承します。
import { AgentWorkflow } from "agents/workflows";
export class ProcessingWorkflow extends AgentWorkflow {
async run(event, step) {
const params = event.payload;
const result = await step.do("process-data", async () => {
return processData(params.data);
});
// Non-durable: progress reporting (may repeat on retry)
await this.reportProgress({
step: "process",
status: "complete",
percent: 0.5,
});
// Broadcast to connected WebSocket clients
this.broadcastToClients({ type: "update", taskId: params.taskId });
await step.do("save-results", async () => {
// Call Agent methods via RPC
await this.agent.saveResult(params.taskId, result);
});
// Durable: idempotent, won't repeat on retry
await step.reportComplete(result);
return result;
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
import type { MyAgent } from "./agent";
type TaskParams = { taskId: string; data: string };
export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {
const params = event.payload;
const result = await step.do("process-data", async () => {
return processData(params.data);
});
// Non-durable: progress reporting (may repeat on retry)
await this.reportProgress({
step: "process",
status: "complete",
percent: 0.5,
});
// Broadcast to connected WebSocket clients
this.broadcastToClients({ type: "update", taskId: params.taskId });
await step.do("save-results", async () => {
// Call Agent methods via RPC
await this.agent.saveResult(params.taskId, result);
});
// Durable: idempotent, won't repeat on retry
await step.reportComplete(result);
return result;
}
}runWorkflow() でワークフローを開始し、追跡します。
import { Agent } from "agents";
export class MyAgent extends Agent {
async startTask(taskId, data) {
const instanceId = await this.runWorkflow("PROCESSING_WORKFLOW", {
taskId,
data,
});
return { instanceId };
}
async onWorkflowProgress(workflowName, instanceId, progress) {
this.broadcast(JSON.stringify({ type: "workflow-progress", progress }));
}
async onWorkflowComplete(workflowName, instanceId, result) {
console.log(`Workflow completed:`, result);
}
async saveResult(taskId, result) {
this
.sql`INSERT INTO results (task_id, data) VALUES (${taskId}, ${JSON.stringify(result)})`;
}
}import { Agent } from "agents";
export class MyAgent extends Agent {
async startTask(taskId: string, data: string) {
const instanceId = await this.runWorkflow("PROCESSING_WORKFLOW", {
taskId,
data,
});
return { instanceId };
}
async onWorkflowProgress(
workflowName: string,
instanceId: string,
progress: unknown,
) {
this.broadcast(JSON.stringify({ type: "workflow-progress", progress }));
}
async onWorkflowComplete(
workflowName: string,
instanceId: string,
result?: unknown,
) {
console.log(`Workflow completed:`, result);
}
async saveResult(taskId: string, result: unknown) {
this
.sql`INSERT INTO results (task_id, data) VALUES (${taskId}, ${JSON.stringify(result)})`;
}
}{
"name": "my-app",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-09-20",
"durable_objects": {
"bindings": [{ "name": "MY_AGENT", "class_name": "MyAgent" }],
},
"workflows": [
{
"name": "processing-workflow",
"binding": "PROCESSING_WORKFLOW",
"class_name": "ProcessingWorkflow",
},
],
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }],
}name = "my-app"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-09-20"
[[durable_objects.bindings]]
name = "MY_AGENT"
class_name = "MyAgent"
[[workflows]]
name = "processing-workflow"
binding = "PROCESSING_WORKFLOW"
class_name = "ProcessingWorkflow"
[[migrations]]
tag = "v1"
new_sqlite_classes = [ "MyAgent" ]Agents と統合する Workflows の基底クラスです。
| パラメーター | 説明 |
|---|---|
AgentType |
型付き RPC 用の Agent クラス型 |
Params |
ワークフローに渡すパラメーター |
ProgressType |
進捗報告の型(デフォルトは DefaultProgress) |
Env |
環境の型(デフォルトは Cloudflare.Env) |
| プロパティ | 型 | 説明 |
|---|---|---|
agent |
Stub | Agent メソッドを呼ぶための型付きスタブ。サブエージェントから開始したワークフローでは、元のファセットへ戻る RPC 専用スタブです。HTTP または WebSocket の fetch() トラフィックにはサブエージェントのルーティングを使います |
instanceId |
string | ワークフローインスタンス ID |
workflowName |
string | ワークフローのバインディング名 |
env |
Env | 環境バインディング |
これらのメソッドは再試行時に繰り返されることがあります。軽量で頻繁な更新に使います。
Agent へ進捗を報告します。onWorkflowProgress コールバックが走ります。
await this.reportProgress({
step: "processing",
status: "running",
percent: 0.5,
});await this.reportProgress({
step: "processing",
status: "running",
percent: 0.5,
});Agent に接続しているすべての WebSocket クライアントへメッセージをブロードキャストします。
this.broadcastToClients({ type: "update", data: result });this.broadcastToClients({ type: "update", data: result });承認イベントを待ちます。拒否されると WorkflowRejectedError を投げます。
const approval = await this.waitForApproval(step, {
timeout: "7 days",
});const approval = await this.waitForApproval<{ approvedBy: string }>(step, {
timeout: "7 days",
});これらのメソッドはべき等で、再試行時に繰り返しません。残すべき状態変更に使います。
| メソッド | 説明 |
|---|---|
step.reportComplete(result?) |
正常完了を報告する |
step.reportError(error) |
エラーを報告する |
step.sendEvent(event) |
Agent へカスタムイベントを送る |
step.updateAgentState(state) |
Agent の状態を置き換える(クライアントへブロードキャストする) |
step.mergeAgentState(partial) |
Agent の状態へマージする(クライアントへブロードキャストする) |
step.resetAgentState() |
Agent の状態を initialState に戻す |
type DefaultProgress = {
step?: string;
status?: "pending" | "running" | "complete" | "error";
message?: string;
percent?: number;
[key: string]: unknown;
};Workflow 管理のために Agent クラスで使えるメソッドです。
ワークフローインスタンスを開始し、Agent のデータベースで追跡します。
パラメーター:
| パラメーター | 型 | 説明 |
|---|---|---|
workflowName |
string | env のワークフローバインディング名 |
params |
object | ワークフローへ渡すパラメーター |
options.id |
string | カスタムワークフロー ID(未指定時は自動生成) |
options.metadata |
object | クエリ用に保存するメタデータ(ワークフローには渡さない) |
options.agentBinding |
string | Agent のバインディング名(未指定時は自動検出)。サブエージェントから呼ぶ場合はルート Agent のバインディング名 |
戻り値: Promise<string> — ワークフローインスタンス ID
const instanceId = await this.runWorkflow(
"MY_WORKFLOW",
{ taskId: "123" },
{
metadata: { userId: "user-456", priority: "high" },
},
);const instanceId = await this.runWorkflow(
"MY_WORKFLOW",
{ taskId: "123" },
{
metadata: { userId: "user-456", priority: "high" },
},
);サブエージェントは this.runWorkflow() を直接呼べます。ワークフローは、開始元サブエージェントの SQLite データベースで追跡されます。AgentWorkflow 内の this.agent は、RPC 呼び出し、コールバック、状態更新、ブロードキャストのために同じサブエージェントへ戻ります。
親エージェントは、サブエージェントが開始したワークフローを自動では一覧したり制御したりしません。SubAgentStub<T> が公開するのはユーザー定義メソッドだけです。approveWorkflow() や getWorkflow() のような継承された Agent メソッドは公開しません。子が開始したワークフローを親から制御するには、子側に小さなラッパーメソッドを定義し、サブエージェントスタブ経由でそれらを呼びます。
export class ParentAgent extends Agent {
async startChildWorkflow(childName, task) {
const child = await this.subAgent(ChildAgent, childName);
return child.startWorkflow(task);
}
async approveChildWorkflow(childName, workflowId) {
const child = await this.subAgent(ChildAgent, childName);
return child.approveChildWorkflow(workflowId);
}
}
export class ChildAgent extends Agent {
async startWorkflow(task) {
return this.runWorkflow("CHILD_WORKFLOW", { task });
}
async approveChildWorkflow(workflowId) {
return this.approveWorkflow(workflowId);
}
async getChildWorkflow(workflowId) {
return this.getWorkflow(workflowId);
}
}export class ParentAgent extends Agent {
async startChildWorkflow(childName: string, task: string) {
const child = await this.subAgent(ChildAgent, childName);
return child.startWorkflow(task);
}
async approveChildWorkflow(childName: string, workflowId: string) {
const child = await this.subAgent(ChildAgent, childName);
return child.approveChildWorkflow(workflowId);
}
}
export class ChildAgent extends Agent {
async startWorkflow(task: string) {
return this.runWorkflow("CHILD_WORKFLOW", { task });
}
async approveChildWorkflow(workflowId: string) {
return this.approveWorkflow(workflowId);
}
async getChildWorkflow(workflowId: string) {
return this.getWorkflow(workflowId);
}
}サブエージェント起点の場合、AgentWorkflow.agent は RPC 専用スタブです。Agent メソッドの呼び出しには使えます。外部の HTTP または WebSocket ルーティングには this.agent.fetch() ではなく、routeSubAgentRequest() または /agents/{parent}/{name}/sub/{child}/{name} の URL 形を使います。
開始元の識別情報はワークフローのパラメーターに耐久的に保存され、コールバックのたびに再生されます。そのため、サブエージェント起点でもトップレベルでも、すべてのワークフローに次の制約が適用されます。
- コールバックは名前で Agent を解決します。 ランタイムは
getAgentByName(...)で開始元 Agent を再解決します。名前ではなく生の Durable Object ID(idFromString/get(id))で Agent を指定した場合、コールバックは別インスタンスに届きます。ワークフローは名前でアドレスされる Agent から開始してください。 - クラス名はバンドル後も残る必要があります。 開始元パスは
constructor.nameでキー付けされます。進捗、完了、this.agentの RPC が正しいファセットへ戻れるよう、バンドラーでクラス名を保持してください(esbuild のkeepNames: true)。 agentBindingはルートのバインディングです。 サブエージェントからoptions.agentBindingを渡すときは、子のバインディングではなく ルート Agent の Durable Object バインディング名を使います。
実行中のワークフローへイベントを送ります。
await this.sendWorkflowEvent("MY_WORKFLOW", instanceId, {
type: "custom-event",
payload: { action: "proceed" },
});await this.sendWorkflowEvent("MY_WORKFLOW", instanceId, {
type: "custom-event",
payload: { action: "proceed" },
});ワークフローのステータスを取得し、追跡レコードを更新します。
const status = await this.getWorkflowStatus("MY_WORKFLOW", instanceId);
// { status: 'running', output: null, error: null }const status = await this.getWorkflowStatus("MY_WORKFLOW", instanceId);
// { status: 'running', output: null, error: null }ID で追跡中のワークフローを取得します。
const workflow = this.getWorkflow(instanceId);
// { instanceId, workflowName, status, metadata, error, createdAt, ... }const workflow = this.getWorkflow(instanceId);
// { instanceId, workflowName, status, metadata, error, createdAt, ... }カーソルベースのページネーションで追跡中のワークフローをクエリします。ワークフロー、総数、次ページ用カーソルを含む WorkflowPage を返します。
// Get running workflows (default limit is 50, max is 100)
const { workflows, total } = this.getWorkflows({ status: "running" });
// Filter by metadata
const { workflows: userWorkflows } = this.getWorkflows({
metadata: { userId: "user-456" },
});
// Pagination with cursor
const page1 = this.getWorkflows({
status: ["complete", "errored"],
limit: 20,
orderBy: "desc",
});
console.log(`Showing ${page1.workflows.length} of ${page1.total} workflows`);
// Get next page using cursor
if (page1.nextCursor) {
const page2 = this.getWorkflows({
status: ["complete", "errored"],
limit: 20,
orderBy: "desc",
cursor: page1.nextCursor,
});
}// Get running workflows (default limit is 50, max is 100)
const { workflows, total } = this.getWorkflows({ status: "running" });
// Filter by metadata
const { workflows: userWorkflows } = this.getWorkflows({
metadata: { userId: "user-456" },
});
// Pagination with cursor
const page1 = this.getWorkflows({
status: ["complete", "errored"],
limit: 20,
orderBy: "desc",
});
console.log(`Showing ${page1.workflows.length} of ${page1.total} workflows`);
// Get next page using cursor
if (page1.nextCursor) {
const page2 = this.getWorkflows({
status: ["complete", "errored"],
limit: 20,
orderBy: "desc",
cursor: page1.nextCursor,
});
}WorkflowPage 型:
type WorkflowPage = {
workflows: WorkflowInfo[];
total: number; // Total matching workflows
nextCursor: string | null; // null when no more pages
};ワークフローインスタンスの追跡レコードを 1 件削除します。削除できた場合は true、見つからない場合は false を返します。
条件に一致するワークフローインスタンスの追跡レコードを削除します。
// Delete completed workflow instances older than 7 days
this.deleteWorkflows({
status: "complete",
createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
});
// Delete all errored and terminated workflows
this.deleteWorkflows({
status: ["errored", "terminated"],
});// Delete completed workflow instances older than 7 days
this.deleteWorkflows({
status: "complete",
createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
});
// Delete all errored and terminated workflows
this.deleteWorkflows({
status: ["errored", "terminated"],
});実行中のワークフローをただちに終了します。ステータスは "terminated" になります。
await this.terminateWorkflow(instanceId);await this.terminateWorkflow(instanceId);実行中のワークフローを一時停止します。あとで resumeWorkflow() で再開できます。
await this.pauseWorkflow(instanceId);await this.pauseWorkflow(instanceId);一時停止したワークフローを再開します。
await this.resumeWorkflow(instanceId);await this.resumeWorkflow(instanceId);同じ ID でワークフローインスタンスを最初から再起動します。
// Reset tracking (default) - clears timestamps and error fields
await this.restartWorkflow(instanceId);
// Preserve original timestamps
await this.restartWorkflow(instanceId, { resetTracking: false });// Reset tracking (default) - clears timestamps and error fields
await this.restartWorkflow(instanceId);
// Preserve original timestamps
await this.restartWorkflow(instanceId, { resetTracking: false });待機中のワークフローを承認します。ワークフロー側の waitForApproval() と組み合わせます。
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});待機中のワークフローを拒否します。waitForApproval() は WorkflowRejectedError を投げます。
await this.rejectWorkflow(instanceId, { reason: "Request denied" });await this.rejectWorkflow(instanceId, { reason: "Request denied" });ワークフローバインディングの名前を変えたあと、追跡中のワークフローを移行します。
class MyAgent extends Agent {
async onStart() {
this.migrateWorkflowBinding("OLD_WORKFLOW", "NEW_WORKFLOW");
}
}class MyAgent extends Agent {
async onStart() {
this.migrateWorkflowBinding("OLD_WORKFLOW", "NEW_WORKFLOW");
}
}ワークフローイベントを扱うには、Agent でこれらのメソッドをオーバーライドします。
| コールバック | パラメーター | 説明 |
|---|---|---|
onWorkflowProgress |
workflowName, instanceId, progress |
ワークフローが進捗を報告したときに呼ばれる |
onWorkflowComplete |
workflowName, instanceId, result? |
ワークフローが完了したときに呼ばれる |
onWorkflowError |
workflowName, instanceId, error |
ワークフローがエラーになったときに呼ばれる |
onWorkflowEvent |
workflowName, instanceId, event |
ワークフローがイベントを送ったときに呼ばれる |
onWorkflowCallback |
callback: WorkflowCallback |
すべてのコールバック種別で呼ばれる |
class MyAgent extends Agent {
async onWorkflowProgress(workflowName, instanceId, progress) {
this.broadcast(
JSON.stringify({ type: "progress", workflowName, instanceId, progress }),
);
}
async onWorkflowComplete(workflowName, instanceId, result) {
console.log(`${workflowName}/${instanceId} completed`);
}
async onWorkflowError(workflowName, instanceId, error) {
console.error(`${workflowName}/${instanceId} failed:`, error);
}
}class MyAgent extends Agent {
async onWorkflowProgress(
workflowName: string,
instanceId: string,
progress: unknown,
) {
this.broadcast(
JSON.stringify({ type: "progress", workflowName, instanceId, progress }),
);
}
async onWorkflowComplete(
workflowName: string,
instanceId: string,
result?: unknown,
) {
console.log(`${workflowName}/${instanceId} completed`);
}
async onWorkflowError(
workflowName: string,
instanceId: string,
error: string,
) {
console.error(`${workflowName}/${instanceId} failed:`, error);
}
}runWorkflow() で開始したワークフローは、開始元 Agent の内部データベースで自動追跡されます。上記のメソッド(getWorkflow()、getWorkflows()、deleteWorkflow() など)でクエリ、フィルタ、管理できます。
| ステータス | 説明 |
|---|---|
queued |
開始待ち |
running |
実行中 |
paused |
ユーザーが一時停止 |
waiting |
イベント待ち |
complete |
正常終了 |
errored |
エラーで失敗 |
terminated |
手動で終了 |
runWorkflow() の metadata オプションで、あとから getWorkflows() でフィルタできる情報(ユーザー ID やタスク種別など)を保存します。
import { AgentWorkflow } from "agents/workflows";
export class ApprovalWorkflow extends AgentWorkflow {
async run(event, step) {
const request = await step.do("prepare", async () => {
return { ...event.payload, preparedAt: Date.now() };
});
await this.reportProgress({
step: "approval",
status: "pending",
message: "Awaiting approval",
});
// Throws WorkflowRejectedError if rejected
const approval = await this.waitForApproval(step, {
timeout: "7 days",
});
console.log("Approved by:", approval?.approvedBy);
const result = await step.do("execute", async () => {
return executeRequest(request);
});
await step.reportComplete(result);
return result;
}
}
class MyAgent extends Agent {
async handleApproval(instanceId, userId) {
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});
}
async handleRejection(instanceId, reason) {
await this.rejectWorkflow(instanceId, { reason });
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
export class ApprovalWorkflow extends AgentWorkflow<MyAgent, RequestParams> {
async run(event: AgentWorkflowEvent<RequestParams>, step: AgentWorkflowStep) {
const request = await step.do("prepare", async () => {
return { ...event.payload, preparedAt: Date.now() };
});
await this.reportProgress({
step: "approval",
status: "pending",
message: "Awaiting approval",
});
// Throws WorkflowRejectedError if rejected
const approval = await this.waitForApproval<{ approvedBy: string }>(step, {
timeout: "7 days",
});
console.log("Approved by:", approval?.approvedBy);
const result = await step.do("execute", async () => {
return executeRequest(request);
});
await step.reportComplete(result);
return result;
}
}
class MyAgent extends Agent {
async handleApproval(instanceId: string, userId: string) {
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});
}
async handleRejection(instanceId: string, reason: string) {
await this.rejectWorkflow(instanceId, { reason });
}
}import { AgentWorkflow } from "agents/workflows";
export class ResilientWorkflow extends AgentWorkflow {
async run(event, step) {
const result = await step.do(
"call-api",
{
retries: { limit: 5, delay: "10 seconds", backoff: "exponential" },
timeout: "5 minutes",
},
async () => {
const response = await fetch("https://api.example.com/process", {
method: "POST",
body: JSON.stringify(event.payload),
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
},
);
await step.reportComplete(result);
return result;
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
export class ResilientWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {
const result = await step.do(
"call-api",
{
retries: { limit: 5, delay: "10 seconds", backoff: "exponential" },
timeout: "5 minutes",
},
async () => {
const response = await fetch("https://api.example.com/process", {
method: "POST",
body: JSON.stringify(event.payload),
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
},
);
await step.reportComplete(result);
return result;
}
}ワークフローは step 経由で Agent の状態を耐久的に更新でき、接続中の全クライアントへ自動でブロードキャストされます。
import { AgentWorkflow } from "agents/workflows";
export class StatefulWorkflow extends AgentWorkflow {
async run(event, step) {
// Replace entire state (durable, broadcasts to clients)
await step.updateAgentState({
currentTask: {
id: event.payload.taskId,
status: "processing",
startedAt: Date.now(),
},
});
const result = await step.do("process", async () =>
processTask(event.payload),
);
// Merge partial state (durable, keeps existing fields)
await step.mergeAgentState({
currentTask: { status: "complete", result, completedAt: Date.now() },
});
await step.reportComplete(result);
return result;
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
export class StatefulWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {
// Replace entire state (durable, broadcasts to clients)
await step.updateAgentState({
currentTask: {
id: event.payload.taskId,
status: "processing",
startedAt: Date.now(),
},
});
const result = await step.do("process", async () =>
processTask(event.payload),
);
// Merge partial state (durable, keeps existing fields)
await step.mergeAgentState({
currentTask: { status: "complete", result, completedAt: Date.now() },
});
await step.reportComplete(result);
return result;
}
}ドメイン固有の報告向けに、カスタム進捗型を定義します。
import { AgentWorkflow } from "agents/workflows";
// Custom progress type for data pipeline
// Workflow with custom progress type (3rd type parameter)
export class ETLWorkflow extends AgentWorkflow {
async run(event, step) {
await this.reportProgress({
stage: "extract",
recordsProcessed: 0,
totalRecords: 1000,
currentTable: "users",
});
// ... processing
}
}
// Agent receives typed progress
class MyAgent extends Agent {
async onWorkflowProgress(workflowName, instanceId, progress) {
const p = progress;
console.log(`Stage: ${p.stage}, ${p.recordsProcessed}/${p.totalRecords}`);
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
// Custom progress type for data pipeline
type PipelineProgress = {
stage: "extract" | "transform" | "load";
recordsProcessed: number;
totalRecords: number;
currentTable?: string;
};
// Workflow with custom progress type (3rd type parameter)
export class ETLWorkflow extends AgentWorkflow<
MyAgent,
ETLParams,
PipelineProgress
> {
async run(event: AgentWorkflowEvent<ETLParams>, step: AgentWorkflowStep) {
await this.reportProgress({
stage: "extract",
recordsProcessed: 0,
totalRecords: 1000,
currentTable: "users",
});
// ... processing
}
}
// Agent receives typed progress
class MyAgent extends Agent {
async onWorkflowProgress(
workflowName: string,
instanceId: string,
progress: unknown,
) {
const p = progress as PipelineProgress;
console.log(`Stage: ${p.stage}, ${p.recordsProcessed}/${p.totalRecords}`);
}
}内部の cf_agents_workflows テーブルは上限なく増えることがあるので、保持ポリシーを実装します。
class MyAgent extends Agent {
// Option 1: Delete on completion
async onWorkflowComplete(workflowName, instanceId, result) {
// Process result first, then delete
this.deleteWorkflow(instanceId);
}
// Option 2: Scheduled cleanup (keep recent history)
async cleanupOldWorkflows() {
this.deleteWorkflows({
status: ["complete", "errored"],
createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
});
}
// Option 3: Keep all history for compliance/auditing
// Don't call deleteWorkflows() - query historical data as needed
}class MyAgent extends Agent {
// Option 1: Delete on completion
async onWorkflowComplete(
workflowName: string,
instanceId: string,
result?: unknown,
) {
// Process result first, then delete
this.deleteWorkflow(instanceId);
}
// Option 2: Scheduled cleanup (keep recent history)
async cleanupOldWorkflows() {
this.deleteWorkflows({
status: ["complete", "errored"],
createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
});
}
// Option 3: Keep all history for compliance/auditing
// Don't call deleteWorkflows() - query historical data as needed
}// Direct RPC call (typed)
await this.agent.updateTaskStatus(taskId, "processing");
const data = await this.agent.getData(taskId);
// Non-durable callbacks (may repeat on retry, use for frequent updates)
await this.reportProgress({ step: "process", percent: 0.5 });
this.broadcastToClients({ type: "update", data });
// Durable callbacks via step (idempotent, won't repeat on retry)
await step.reportComplete(result);
await step.reportError("Something went wrong");
await step.sendEvent({ type: "custom", data: {} });
// Durable state synchronization via step (broadcasts to clients)
await step.updateAgentState({ status: "processing" });
await step.mergeAgentState({ progress: 0.5 });// Direct RPC call (typed)
await this.agent.updateTaskStatus(taskId, "processing");
const data = await this.agent.getData(taskId);
// Non-durable callbacks (may repeat on retry, use for frequent updates)
await this.reportProgress({ step: "process", percent: 0.5 });
this.broadcastToClients({ type: "update", data });
// Durable callbacks via step (idempotent, won't repeat on retry)
await step.reportComplete(result);
await step.reportError("Something went wrong");
await step.sendEvent({ type: "custom", data: {} });
// Durable state synchronization via step (broadcasts to clients)
await step.updateAgentState({ status: "processing" });
await step.mergeAgentState({ progress: 0.5 });// Send event to waiting workflow
await this.sendWorkflowEvent("MY_WORKFLOW", instanceId, {
type: "custom-event",
payload: { action: "proceed" },
});
// Approve/reject workflows using convenience methods
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});
await this.rejectWorkflow(instanceId, { reason: "Request denied" });// Send event to waiting workflow
await this.sendWorkflowEvent("MY_WORKFLOW", instanceId, {
type: "custom-event",
payload: { action: "proceed" },
});
// Approve/reject workflows using convenience methods
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});
await this.rejectWorkflow(instanceId, { reason: "Request denied" });- ワークフローは焦点を絞る — 論理的なタスクごとに 1 ワークフロー
- 意味のあるステップ名を使う — デバッグと可観測性に役立つ
- 進捗を定期的に報告する — ユーザーに状況が伝わる
- エラーは丁寧に扱う — 投げる前に
reportError()を使う - 完了したワークフローを掃除する — 追跡テーブルに保持ポリシーを実装する
- ワークフローバインディングの改名を扱う —
wrangler.jsoncでバインディング名を変えたらmigrateWorkflowBinding()を使う
| 制約 | 上限 |
|---|---|
| 最大ステップ数 | ワークフローあたり 10,000(デフォルト)/ 最大 25,000 まで設定可能 |
| 状態サイズ | ワークフローあたり 10 MB |
| イベント待ち時間 | 最大 1 年 |
| ステップ実行時間 | ステップあたり 30 分 |
Workflows は WebSocket 接続を直接開けません。接続中のクライアントとの通信には、Agent 経由の broadcastToClients() を使います。