Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions src/content/docs/agents/api-reference/durable-execution.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ Sub-agents do not have independent alarm slots, so the top-level parent owns the

This keeps recovery local to the child while preserving the single physical alarm slot owned by the parent. A recovered continuation can use `schedule()` from inside the facet; the parent owns the physical alarm and routes the callback back to the child.

Workflows started from sub-agents follow the same locality model for tracking and callbacks. The workflow row lives in the sub-agent's SQLite database, and `AgentWorkflow.agent` routes RPC and workflow callbacks back to the originating facet.

#### Error during execution

```txt
Expand Down
66 changes: 54 additions & 12 deletions src/content/docs/agents/api-reference/run-workflows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
});

// Broadcast to connected WebSocket clients
this.broadcastToClients({ type: "update", taskId: params.taskId });
await this.broadcastToClients({ type: "update", taskId: params.taskId });

await step.do("save-results", async () => {
// Call Agent methods via RPC
Expand Down Expand Up @@ -184,7 +184,7 @@ Broadcast a message to all WebSocket clients connected to the Agent.
<TypeScriptExample>

```ts
this.broadcastToClients({ type: "update", data: result });
await this.broadcastToClients({ type: "update", data: result });
```

</TypeScriptExample>
Expand Down Expand Up @@ -234,17 +234,17 @@ Methods available on the `Agent` class for Workflow management.

### runWorkflow(workflowName, params, options?)

Start a workflow instance and track it in the Agent database.
Start a workflow instance and track it in the originating Agent's database.

**Parameters:**

| Parameter | Type | Description |
| ---------------------- | ------ | ----------------------------------------------------- |
| `workflowName` | string | Workflow binding name from `env` |
| `params` | object | Parameters to pass to the workflow |
| `options.id` | string | Custom workflow ID (auto-generated if not provided) |
| `options.metadata` | object | Metadata stored for querying (not passed to workflow) |
| `options.agentBinding` | string | Agent binding name (auto-detected if not provided) |
| Parameter | Type | Description |
| ---------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------- |
| `workflowName` | string | Workflow binding name from `env` |
| `params` | object | Parameters to pass to the workflow |
| `options.id` | string | Custom workflow ID (auto-generated if not provided) |
| `options.metadata` | object | Metadata stored for querying (not passed to workflow) |
| `options.agentBinding` | string | Agent binding name (auto-detected if not provided). When called from a sub-agent, this is the root Agent binding name. |

**Returns:** `Promise<string>` - Workflow instance ID

Expand All @@ -262,6 +262,46 @@ const instanceId = await this.runWorkflow(

</TypeScriptExample>

#### Sub-agents

Sub-agents can call `this.runWorkflow()` directly. The workflow is tracked in the originating sub-agent's SQLite database, and `this.agent` inside `AgentWorkflow` routes back to that same sub-agent for RPC calls, callbacks, state updates, and broadcasts.

Parent agents do not automatically list or control workflows that a sub-agent starts. `SubAgentStub<T>` only exposes user-defined methods, not inherited `Agent` methods such as `approveWorkflow()` or `getWorkflow()`. To control a child-started workflow from the parent, define small wrapper methods on the child and call those wrappers through the sub-agent stub.

<TypeScriptExample>

```ts
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);
}
}
```

</TypeScriptExample>

For sub-agent origins, `AgentWorkflow.agent` is an RPC-only stub. Use it to call Agent methods, but use `routeSubAgentRequest()` or the `/agents/{parent}/{name}/sub/{child}/{name}` URL shape for external HTTP or WebSocket routing instead of `this.agent.fetch()`.

### sendWorkflowEvent(workflowName, instanceId, event)

Send an event to a running workflow.
Expand Down Expand Up @@ -542,7 +582,7 @@ class MyAgent extends Agent {

## Workflow tracking

Workflows started with `runWorkflow()` are automatically tracked in the Agent's internal database. You can query, filter, and manage workflows using the methods described above (`getWorkflow()`, `getWorkflows()`, `deleteWorkflow()`, etc.).
Workflows started with `runWorkflow()` are automatically tracked in the originating Agent's internal database. If a sub-agent starts the workflow, the tracking record belongs to that sub-agent, not its parent. Query, filter, and manage those workflows from the originating Agent instance. From a parent, define child wrapper methods that call the workflow methods described above (`getWorkflow()`, `getWorkflows()`, `deleteWorkflow()`, etc.) inside the child.

### Status values

Expand Down Expand Up @@ -781,7 +821,7 @@ 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 });
await this.broadcastToClients({ type: "update", data });

// Durable callbacks via step (idempotent, won't repeat on retry)
await step.reportComplete(result);
Expand All @@ -795,6 +835,8 @@ await step.mergeAgentState({ progress: 0.5 });

</TypeScriptExample>

If a sub-agent started the workflow, `this.agent` routes RPC back to that originating facet. It does not support `.fetch()`; use `routeSubAgentRequest()` or the nested `/agents/.../sub/...` URL shape for external HTTP or WebSocket traffic.

### Agent to Workflow

<TypeScriptExample>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ class MyWorkflow extends Workflow<Env> {

</TypeScriptExample>

These are durable operations - they persist even if the workflow retries.
These are durable operations - they persist even if the workflow retries. They update the workflow's originating Agent. If a sub-agent started the workflow, the state update applies to that sub-agent facet and broadcasts to that facet's clients.

## SQL API

Expand Down
4 changes: 4 additions & 0 deletions src/content/docs/agents/api-reference/sub-agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class Agent {

Returns a `SubAgentStub<T>` — a typed RPC stub where every user-defined method on `T` is available as a Promise-returning remote call.

Sub-agents can start [Workflows](/agents/api-reference/run-workflows/) with `this.runWorkflow()`. Workflow tracking is local to the sub-agent's SQLite database, and `AgentWorkflow.agent` routes RPC, callbacks, state updates, and broadcasts back to the originating sub-agent. Parent agents do not automatically list or control child-started workflows. Because `SubAgentStub<T>` only exposes user-defined child methods, add child wrapper methods for controls such as `getWorkflow()`, `approveWorkflow()`, or `terminateWorkflow()`, then call those wrappers through `await this.subAgent(Child, name)`.

For sub-agent workflow origins, `AgentWorkflow.agent` is RPC-only. Use it to call Agent methods, but use `routeSubAgentRequest()` or the nested `/agents/{parent}/{name}/sub/{child}/{name}` URL shape for external HTTP or WebSocket routing instead of `this.agent.fetch()`. If you pass `runWorkflow(..., { agentBinding })` from a sub-agent, use the root Agent binding name, not a child binding name.

### SubAgentStub

The stub exposes all public instance methods you define on the child class. Methods inherited from `Agent` (lifecycle hooks, `setState`, `broadcast`, `sql`, and so on) are excluded — only your custom methods appear on the stub.
Expand Down
14 changes: 8 additions & 6 deletions src/content/docs/agents/concepts/human-in-the-loop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ Human-in-the-Loop (HITL) workflows integrate human judgment and oversight into a

The Agents SDK provides five patterns for human-in-the-loop. Choose based on your architecture:

| Use Case | Pattern | Best For |
| --- | --- | --- |
| Use Case | Pattern | Best For |
| ---------------------- | ----------------- | ------------------------------------------------------------------------- |
| Long-running workflows | Workflow Approval | Multi-step processes, durable approval gates that can wait hours or weeks |
| AIChatAgent tools | `needsApproval` | Chat-based tool calls with server-side approval before execution |
| Client-side tools | `onToolCall` | Tools that need browser APIs or user interaction before execution |
| MCP servers | Elicitation | MCP tools requesting structured user input during execution |
| Simple confirmations | State + WebSocket | Lightweight approval flows without AI chat or workflows |
| AIChatAgent tools | `needsApproval` | Chat-based tool calls with server-side approval before execution |
| Client-side tools | `onToolCall` | Tools that need browser APIs or user interaction before execution |
| MCP servers | Elicitation | MCP tools requesting structured user input during execution |
| Simple confirmations | State + WebSocket | Lightweight approval flows without AI chat or workflows |

### Decision tree

Expand All @@ -65,6 +65,8 @@ For durable, multi-step processes with approval gates that can wait hours, days,
- `approveWorkflow(workflowId, { reason?, metadata? })` — Approve a waiting workflow
- `rejectWorkflow(workflowId, { reason? })` — Reject a waiting workflow

If a sub-agent starts the workflow, approval and rejection are scoped to that sub-agent. Parent agents should resolve the child with `subAgent()` and call child-defined wrapper methods that run the workflow control methods inside the child.

**Best for:** Expense approvals, content publishing pipelines, data export requests

## Pattern 2: `needsApproval` (AI chat tools)
Expand Down
4 changes: 4 additions & 0 deletions src/content/docs/agents/concepts/long-running-agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ try {
| Minutes to hours | [Workflows](/agents/concepts/workflows/) |
| Hours to days | Async pattern: start job, hibernate, wake on completion |

If a sub-agent starts a workflow, tracking and workflow controls are scoped to that sub-agent. A parent can still coordinate the workflow by resolving the child with `subAgent()` and calling child-defined wrapper methods that run `getWorkflow()`, `approveWorkflow()`, `terminateWorkflow()`, or cleanup helpers inside the child.

## Surviving crashes: fibers and recovery

An agent can be evicted at any time — a deploy, a platform restart, or hitting resource limits. If the agent was mid-task, that work is lost unless it was checkpointed.
Expand Down Expand Up @@ -603,6 +605,8 @@ export class ProjectManager extends Agent<ProjectState> {
),
});

// Clean up old workflow tracking records started by this same agent.
// Child-started workflows must be cleaned up on the child facet.
this.deleteWorkflows({
status: ["complete", "errored"],
createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
Expand Down
4 changes: 3 additions & 1 deletion src/content/docs/agents/concepts/workflows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ This durability model means workflows are well-suited for tasks where partial co

## Workflow tracking

When an Agent starts a workflow using `runWorkflow()`, the workflow is automatically tracked in the Agent's internal database. This enables:
When an Agent or sub-agent starts a workflow using `runWorkflow()`, the workflow is automatically tracked in that originating Agent's internal database. If a sub-agent starts the workflow, parent agents do not automatically list or control that workflow. To manage it from the parent, define child methods that call `getWorkflow()`, `approveWorkflow()`, `terminateWorkflow()`, or other workflow methods inside the child, then call those methods through the sub-agent stub.

- Querying workflow status by ID, name, or metadata with cursor-based pagination
- Monitoring progress through lifecycle callbacks (`onWorkflowProgress`, `onWorkflowComplete`, `onWorkflowError`)
Expand Down Expand Up @@ -182,6 +182,8 @@ const result = await step.do(

A Workflow updates Agent state at key milestones using `step.updateAgentState()` or `step.mergeAgentState()`. These state changes broadcast to all connected clients, keeping UIs synchronized without polling.

If a sub-agent started the workflow, `AgentWorkflow.agent` routes RPC and state updates back to that originating facet. It is RPC-only for sub-agent origins; use sub-agent HTTP/WebSocket routing for external `fetch()` traffic.

## Related resources

<LinkCard
Expand Down
16 changes: 8 additions & 8 deletions src/content/docs/agents/guides/autonomous-responses.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -402,14 +402,14 @@ The `messageConcurrency` setting on `AIChatAgent` controls how overlapping user

## Combining with other Agent primitives

| Primitive | How to combine |
| ------------------ | --------------------------------------------------------------------------------------------- |
| `schedule()` | Schedule a callback that calls `saveMessages` — see the cron example above |
| `queue()` | Queue a method that calls `saveMessages` for deferred processing |
| `runWorkflow()` | Start a Workflow; use `AgentWorkflow.agent` RPC to call a method that triggers `saveMessages` |
| `onEmail()` | Convert email content to a chat message and call `saveMessages` |
| `onRequest()` | Handle webhooks and call `saveMessages` |
| `this.broadcast()` | Broadcast custom state from `onChatResponse` |
| Primitive | How to combine |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schedule()` | Schedule a callback that calls `saveMessages` — see the cron example above |
| `queue()` | Queue a method that calls `saveMessages` for deferred processing |
| `runWorkflow()` | Start a Workflow; use `AgentWorkflow.agent` RPC to call a method that triggers `saveMessages`. For workflows started by sub-agents, this stub routes back to the originating facet and is RPC-only; use sub-agent routing for HTTP/WebSocket traffic. |
| `onEmail()` | Convert email content to a chat message and call `saveMessages` |
| `onRequest()` | Handle webhooks and call `saveMessages` |
| `this.broadcast()` | Broadcast custom state from `onChatResponse` |

## Important notes

Expand Down
2 changes: 2 additions & 0 deletions src/content/docs/agents/guides/human-in-the-loop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ export class ExpenseAgent extends Agent<Env, ExpenseState> {

</TypeScriptExample>

If a sub-agent starts the workflow, approval and rejection methods are scoped to that sub-agent. Parent agents should resolve the child with `subAgent()` and call child-defined wrapper methods that run `approveWorkflow()` or `rejectWorkflow()` inside the child; `onWorkflowProgress()`, `onWorkflowComplete()`, and related callbacks also run on the originating sub-agent.

### Timeout handling

Set timeouts to prevent workflows from waiting indefinitely:
Expand Down
Loading