workflow 5.0.0-beta.12 → 5.0.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/ai/index.mdx CHANGED
@@ -92,7 +92,7 @@ Then modify your API endpoint to use the OpenAI provider:
92
92
  {/* @skip-typecheck: incomplete code sample */}
93
93
  ```typescript title="app/api/chat/route.ts" lineNumbers
94
94
  // ...
95
- import { openai } from "@workflow/ai/openai"; // [!code highlight]
95
+ import { openai } from "@ai-sdk/openai"; // [!code highlight]
96
96
 
97
97
  export async function POST(req: Request) {
98
98
  // ...
@@ -232,7 +232,7 @@ Now that we have a basic agent using AI SDK, we can modify it to make it durable
232
232
  Add the Workflow SDK packages to your project:
233
233
 
234
234
  ```package-install
235
- npm i workflow @workflow/ai
235
+ npm i workflow @ai-sdk/workflow
236
236
  ```
237
237
 
238
238
  and extend the Next.js config to transform your workflow code (see [Getting Started](/docs/getting-started/next) for more details).
@@ -258,18 +258,18 @@ Move the agent logic into a separate function, which will serve as our workflow
258
258
 
259
259
  {/* @skip-typecheck: Shows two mutually exclusive model options */}
260
260
  ```typescript title="workflows/chat/workflow.ts" lineNumbers
261
- import { DurableAgent } from "@workflow/ai/agent"; // [!code highlight]
261
+ import { WorkflowAgent, type ModelCallStreamPart } from "@ai-sdk/workflow"; // [!code highlight]
262
262
  import { getWritable } from "workflow"; // [!code highlight]
263
263
  import { tools } from "@/ai/tools";
264
- import { openai } from "@workflow/ai/openai";
265
- import type { ModelMessage, UIMessageChunk } from "ai";
264
+ import { openai } from "@ai-sdk/openai";
265
+ import { convertToModelMessages, type UIMessage } from "ai";
266
266
 
267
- export async function chatWorkflow(messages: ModelMessage[]) {
267
+ export async function chatWorkflow(messages: UIMessage[]) {
268
268
  "use workflow"; // [!code highlight]
269
269
 
270
- const writable = getWritable<UIMessageChunk>(); // [!code highlight]
270
+ const writable = getWritable<ModelCallStreamPart>(); // [!code highlight]
271
271
 
272
- const agent = new DurableAgent({ // [!code highlight]
272
+ const agent = new WorkflowAgent({ // [!code highlight]
273
273
 
274
274
  // If using AI Gateway, just specify the model name as a string:
275
275
  model: "bedrock/claude-4-5-haiku-20251001-v1", // [!code highlight]
@@ -281,8 +281,10 @@ export async function chatWorkflow(messages: ModelMessage[]) {
281
281
  tools: flightBookingTools,
282
282
  });
283
283
 
284
+ const modelMessages = await convertToModelMessages(messages); // [!code highlight]
285
+
284
286
  await agent.stream({ // [!code highlight]
285
- messages,
287
+ messages: modelMessages,
286
288
  writable,
287
289
  });
288
290
  }
@@ -291,8 +293,9 @@ export async function chatWorkflow(messages: ModelMessage[]) {
291
293
  Key changes:
292
294
 
293
295
  - Add the `"use workflow"` directive to mark our Agent as a workflow function
294
- - Replaced `Agent` with [`DurableAgent`](/docs/api-reference/workflow-ai/durable-agent) from `@workflow/ai/agent`. This ensures that all calls to the LLM are executed as "steps", and results are aggregated within the workflow context (see [Workflows and Steps](/docs/foundations/workflows-and-steps) for more details on how workflows/steps are defined).
295
- - Use [`getWritable()`](/docs/api-reference/workflow/get-writable) to get a stream for agent output. This stream is persistent, and API endpoints can read from a run's stream at any time.
296
+ - Replace the in-memory agent with [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) from `@ai-sdk/workflow`. This runs the agent loop inside a workflow, persists state across step boundaries, and lets tool executions marked with `"use step"` retry automatically.
297
+ - Convert AI SDK `UIMessage` values to model messages inside the workflow before calling `agent.stream()`.
298
+ - Use [`getWritable()`](/docs/api-reference/workflow/get-writable) to get a stream for agent output. `WorkflowAgent` writes `ModelCallStreamPart` chunks to this persistent stream, and API endpoints can read from a run's stream at any time.
296
299
  </Step>
297
300
 
298
301
  <Step>
@@ -301,19 +304,18 @@ Key changes:
301
304
  Remove the agent call that we just extracted, and replace it with a call to `start()` to run the workflow:
302
305
 
303
306
  ```typescript title="app/api/chat/route.ts" lineNumbers
304
- import type { UIMessage } from "ai";
305
- import { convertToModelMessages, createUIMessageStreamResponse } from "ai";
307
+ import { createModelCallToUIChunkTransform } from "@ai-sdk/workflow";
308
+ import { createUIMessageStreamResponse, type UIMessage } from "ai";
306
309
  import { start } from "workflow/api";
307
310
  import { chatWorkflow } from "@/workflows/chat/workflow";
308
311
 
309
312
  export async function POST(req: Request) {
310
313
  const { messages }: { messages: UIMessage[] } = await req.json();
311
- const modelMessages = await convertToModelMessages(messages);
312
314
 
313
- const run = await start(chatWorkflow, [modelMessages]); // [!code highlight]
315
+ const run = await start(chatWorkflow, [messages]); // [!code highlight]
314
316
 
315
317
  return createUIMessageStreamResponse({
316
- stream: run.readable, // [!code highlight]
318
+ stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()), // [!code highlight]
317
319
  });
318
320
  }
319
321
  ```
@@ -321,7 +323,8 @@ export async function POST(req: Request) {
321
323
  Key changes:
322
324
 
323
325
  - Call `start()` to run the workflow function. This returns a `Run` object, which contains the run ID and the readable stream (see [Starting Workflows](/docs/foundations/starting-workflows) for more details on the `Run` object).
324
- - Pass the `writable` to `agent.stream()` instead of returning a stream directly, ensuring all the Agent output is written to to the run's stream.
326
+ - Pass the `writable` to `agent.stream()` instead of returning a stream directly, ensuring all the Agent output is written to the run's stream.
327
+ - Pipe the readable stream through `createModelCallToUIChunkTransform()` so the raw model-call chunks become AI SDK UI message chunks before they are returned to the client.
325
328
 
326
329
  </Step>
327
330
 
@@ -423,7 +426,7 @@ A complete example that includes all of the above, plus all of the "next steps"
423
426
  ## Related Documentation
424
427
 
425
428
  - [Tools](/docs/ai/defining-tools) - Patterns for defining tools for your agent
426
- - [`DurableAgent` API Reference](/docs/api-reference/workflow-ai/durable-agent) - Full API documentation
429
+ - [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) - AI SDK API for durable, resumable agents
427
430
  - [Workflows and Steps](/docs/foundations/workflows-and-steps) - Core concepts
428
431
  - [Streaming](/docs/foundations/streaming) - In-depth streaming guide
429
432
  - [Errors and Retries](/docs/foundations/errors-and-retries) - Error handling patterns
@@ -1,59 +1,21 @@
1
1
  ---
2
2
  title: DurableAgent
3
- description: Create AI agents that maintain state, call tools, and handle interruptions gracefully.
3
+ description: Deprecated DurableAgent API reference; use WorkflowAgent for new durable agents.
4
4
  type: reference
5
- summary: Use DurableAgent to build AI agents that maintain state across steps and survive interruptions.
5
+ summary: "Deprecated: use AI SDK's WorkflowAgent instead of DurableAgent."
6
6
  prerequisites:
7
7
  - /docs/ai
8
8
  related:
9
9
  - /docs/ai/defining-tools
10
10
  ---
11
11
 
12
- The `DurableAgent` class enables you to create AI-powered agents that can maintain state across workflow steps, call tools, and gracefully handle interruptions and resumptions.
13
-
14
- Tool calls can be implemented as workflow steps for automatic retries, or as regular workflow-level logic utilizing core library features such as [`sleep()`](/docs/api-reference/workflow/sleep) and [Hooks](/docs/foundations/hooks).
15
-
16
- ```typescript lineNumbers
17
- import { DurableAgent } from "@workflow/ai/agent";
18
- import { getWritable } from "workflow";
19
- import { z } from "zod";
20
- import type { UIMessageChunk } from "ai";
21
-
22
- async function getWeather({ city }: { city: string }) {
23
- "use step";
24
-
25
- return `Weather in ${city} is sunny`;
26
- }
27
-
28
- async function myAgent() {
29
- "use workflow";
30
-
31
- const agent = new DurableAgent({
32
- model: "anthropic/claude-haiku-4.5",
33
- instructions: "You are a helpful weather assistant.",
34
- temperature: 0.7,
35
- tools: {
36
- getWeather: {
37
- description: "Get weather for a city",
38
- inputSchema: z.object({ city: z.string() }),
39
- execute: getWeather,
40
- },
41
- },
42
- });
43
-
44
- // The agent will stream its output to the workflow
45
- // run's default output stream
46
- const writable = getWritable<UIMessageChunk>();
12
+ <Callout type="warn">
13
+ `DurableAgent` is deprecated. Use AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) for new durable agents and migration guidance.
14
+ </Callout>
47
15
 
48
- const result = await agent.stream({
49
- messages: [{ role: "user", content: "How is the weather in San Francisco?" }],
50
- writable,
51
- });
16
+ This reference is kept for existing applications that still import `DurableAgent` from `@workflow/ai/agent`. Do not use `DurableAgent` for new code.
52
17
 
53
- // result contains messages, steps, and optional structured output
54
- console.log(result.messages);
55
- }
56
- ```
18
+ For current examples and implementation guidance, see AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) docs. For legacy code, the API surface below documents the existing `DurableAgent` exports.
57
19
 
58
20
  ## API Signature
59
21
 
@@ -1,154 +1,18 @@
1
1
  ---
2
- title: Durable Agent
3
- description: Replace a stateless AI agent with a durable one that survives crashes, retries tool calls, and streams output.
2
+ title: DurableAgent is now WorkflowAgent
3
+ description: Use AI SDK v7's WorkflowAgent for durable, resumable AI agents.
4
4
  type: guide
5
- summary: Convert an AI SDK Agent into a DurableAgent backed by a workflow, with tools as retryable steps.
5
+ summary: Build durable, resumable AI agents with AI SDK v7's WorkflowAgent.
6
6
  ---
7
7
 
8
- Use this pattern to make any AI SDK agent durable. The agent becomes a workflow, tools become steps, and the framework handles retries, streaming, and state persistence automatically.
8
+ ## WorkflowAgent from AI SDK v7
9
9
 
10
- ## When to use this
10
+ Use AI SDK v7's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) for new durable agent work. It replaces `DurableAgent` in v5 and keeps the current agent pattern in the AI SDK package.
11
11
 
12
- - Any AI agent with tool calls that should survive crashes and restarts
13
- - Agents where tool calls hit external APIs that need automatic retries
14
- - Long-running agent sessions where losing progress is unacceptable
15
- - Agents that need per-step observability in the workflow event log
12
+ - Import `WorkflowAgent` from `@ai-sdk/workflow` and run it inside a `"use workflow"` function.
13
+ - Stream `ModelCallStreamPart` chunks with `getWritable()`, then convert the run stream to UI message chunks with `createModelCallToUIChunkTransform()` in your route.
14
+ - Mark tool `execute` functions with `"use step"` when they should run as durable workflow steps with retry and observability behavior.
16
15
 
17
- <Callout type="info">
18
- A durable agent run stays on the deployment that started it. For multi-turn agents that should pick up newer code between turns, see [Versioning](/docs/foundations/versioning) for patterns that start the next turn or next session run with `deploymentId: "latest"`.
16
+ <Callout type="warn">
17
+ `DurableAgent` is deprecated in v5 and remains documented for existing code only. Existing `DurableAgent` users can refer to the [`DurableAgent` API reference](/docs/api-reference/workflow-ai/durable-agent) while migrating.
19
18
  </Callout>
20
-
21
- ## Pattern
22
-
23
- Replace `Agent` with `DurableAgent`, wrap the function in `"use workflow"`, mark each tool with `"use step"`, and stream output through `getWritable()`.
24
-
25
- ### Workflow
26
-
27
- ```typescript
28
- import { DurableAgent } from "@workflow/ai/agent";
29
- import { getWritable } from "workflow";
30
- import { z } from "zod";
31
- import type { ModelMessage, UIMessageChunk } from "ai";
32
-
33
- async function searchFlights({ from, to, date }: {
34
- from: string;
35
- to: string;
36
- date: string;
37
- }) {
38
- "use step"; // [!code highlight]
39
- const res = await fetch(
40
- `https://api.example.com/flights?from=${from}&to=${to}&date=${date}`
41
- );
42
- if (!res.ok) throw new Error(`Search failed: ${res.status}`);
43
- return res.json();
44
- }
45
-
46
- async function bookFlight({ flightId, passenger }: {
47
- flightId: string;
48
- passenger: string;
49
- }) {
50
- "use step"; // [!code highlight]
51
- const res = await fetch("https://api.example.com/bookings", {
52
- method: "POST",
53
- headers: { "Content-Type": "application/json" },
54
- body: JSON.stringify({ flightId, passenger }),
55
- });
56
- if (!res.ok) throw new Error(`Booking failed: ${res.status}`);
57
- return res.json();
58
- }
59
-
60
- async function checkWeather({ city }: { city: string }) {
61
- "use step"; // [!code highlight]
62
- const res = await fetch(`https://api.weather.com/forecast?city=${city}`);
63
- return res.json();
64
- }
65
-
66
- export async function flightAgent(messages: ModelMessage[]) {
67
- "use workflow";
68
-
69
- const agent = new DurableAgent({ // [!code highlight]
70
- model: "anthropic/claude-haiku-4.5",
71
- instructions: "You are a helpful flight booking assistant.",
72
- tools: {
73
- searchFlights: {
74
- description: "Search for available flights between two airports",
75
- inputSchema: z.object({
76
- from: z.string().describe("Departure airport code"),
77
- to: z.string().describe("Arrival airport code"),
78
- date: z.string().describe("Travel date (YYYY-MM-DD)"),
79
- }),
80
- execute: searchFlights,
81
- },
82
- bookFlight: {
83
- description: "Book a specific flight for a passenger",
84
- inputSchema: z.object({
85
- flightId: z.string().describe("Flight ID from search results"),
86
- passenger: z.string().describe("Passenger full name"),
87
- }),
88
- execute: bookFlight,
89
- },
90
- checkWeather: {
91
- description: "Check the weather forecast for a city",
92
- inputSchema: z.object({
93
- city: z.string().describe("City name"),
94
- }),
95
- execute: checkWeather,
96
- },
97
- },
98
- });
99
-
100
- const result = await agent.stream({ // [!code highlight]
101
- messages,
102
- writable: getWritable<UIMessageChunk>(), // [!code highlight]
103
- maxSteps: 10,
104
- });
105
-
106
- return { messages: result.messages };
107
- }
108
- ```
109
-
110
- ### API route
111
-
112
- ```typescript
113
- import type { UIMessage } from "ai";
114
- import { convertToModelMessages, createUIMessageStreamResponse } from "ai";
115
- import { start } from "workflow/api";
116
- import { flightAgent } from "@/app/workflows/flight-agent";
117
-
118
- export async function POST(req: Request) {
119
- const { messages }: { messages: UIMessage[] } = await req.json();
120
- const modelMessages = await convertToModelMessages(messages); // [!code highlight]
121
-
122
- const run = await start(flightAgent, [modelMessages]); // [!code highlight]
123
-
124
- return createUIMessageStreamResponse({ // [!code highlight]
125
- stream: run.readable,
126
- headers: {
127
- "x-workflow-run-id": run.runId,
128
- },
129
- });
130
- }
131
- ```
132
-
133
- ## How it works
134
-
135
- 1. **DurableAgent wraps Agent** — same API as AI SDK's `Agent`, but backed by a workflow. If the process crashes, the agent resumes from the last completed step on replay.
136
- 2. **Tools as steps** — each tool's `execute` function uses `"use step"`, giving it automatic retries, full Node.js access, and an entry in the workflow event log.
137
- 3. **Streaming** — `getWritable<UIMessageChunk>()` streams the agent's output (text chunks, tool calls, tool results) to the client in real time via `createUIMessageStreamResponse`.
138
- 4. **maxSteps** — limits the total number of LLM calls the agent can make, preventing runaway tool loops.
139
-
140
- ## Adapting to your use case
141
-
142
- - **Change the model** — replace `"anthropic/claude-haiku-4.5"` with any AI Gateway model string (e.g. `"openai/gpt-4o"`, `"anthropic/claude-sonnet-4-5"`).
143
- - **Add tools** — define a new `"use step"` function with a Zod schema. Each tool automatically gets retries and persistence.
144
- - **Workflow-level tools** — if a tool needs workflow primitives like `sleep()` or `createHook()`, omit `"use step"` so it runs in the workflow context instead.
145
- - **Multi-turn** — pass `result.messages` plus new user messages to subsequent `agent.stream()` calls for multi-turn conversations.
146
- - **Client integration** — use `useChat()` from `@ai-sdk/react` with `WorkflowChatTransport` from `@workflow/ai` for a full chat UI with reconnection support.
147
-
148
- ## Key APIs
149
-
150
- - [`"use workflow"`](/docs/api-reference/workflow/use-workflow) — declares the orchestrator function
151
- - [`"use step"`](/docs/api-reference/workflow/use-step) — declares step functions with retries and full Node.js access
152
- - [`DurableAgent`](/docs/api-reference/workflow-ai/durable-agent) — durable wrapper around AI SDK's Agent
153
- - [`getWritable()`](/docs/api-reference/workflow/get-writable) — streams agent output to the client
154
- - [`start()`](/docs/api-reference/workflow-api/start) — starts a workflow run from an API route
@@ -8,7 +8,7 @@ A curated collection of workflow patterns with clean, copy-paste code examples f
8
8
 
9
9
  ## Agent Patterns
10
10
 
11
- - [**Durable Agent**](/cookbook/agent-patterns/durable-agent) — Replace a stateless AI agent with one that survives crashes and retries tool calls
11
+ - [**WorkflowAgent**](/cookbook/agent-patterns/durable-agent) — Build durable, resumable AI agents with AI SDK's WorkflowAgent
12
12
  - [**Human-in-the-Loop**](/cookbook/agent-patterns/human-in-the-loop) — Pause an agent for human approval, then resume based on the decision
13
13
  - [**Agent Cancellation**](/cookbook/agent-patterns/agent-cancellation) — Stop a running agent immediately via `run.cancel()` or gracefully via a hook + `Promise.race`
14
14
 
@@ -43,6 +43,9 @@ Fix common mistakes when creating and executing workflows in the **Workflow SDK*
43
43
  <Card href="/docs/errors/step-not-registered" title="step-not-registered">
44
44
  Resolve step not registered errors caused by deployment mismatches.
45
45
  </Card>
46
+ <Card href="/docs/errors/step-executed-multiple-times" title="Step executed multiple times">
47
+ Diagnose duplicate step_started events from function crashes, timeouts, or OOMs.
48
+ </Card>
46
49
  <Card href="/docs/errors/workflow-not-registered" title="workflow-not-registered">
47
50
  Resolve workflow not registered errors caused by deployment mismatches.
48
51
  </Card>
@@ -0,0 +1,23 @@
1
+ ---
2
+ title: Step executed multiple times
3
+ description: A step ran more than once because its function invocation crashed before it could report a result.
4
+ type: troubleshooting
5
+ summary: Diagnose duplicate step_started events caused by function timeouts, OOMs, or network issues.
6
+ prerequisites:
7
+ - /docs/foundations/workflows-and-steps
8
+ related:
9
+ - /docs/observability
10
+ - /docs/foundations/errors-and-retries
11
+ ---
12
+
13
+ There may be cases where you see multiple `step_started` events for the same step in a workflow run. This happens if the function invocation executing the step crashes unexpectedly, and the step can not report the error. The step will be re-tried according to your retry policy in this case, but no error will be visible in the [Observability UI](/docs/observability).
14
+
15
+ ## Common Causes
16
+
17
+ - **Function timeouts**: if your step code runs longer than the configured maximum function duration, it will be killed. Compare the gap between the `step_started` events to your configured function duration to be sure.
18
+ - **Out of memory (OOM)**: if your step code loads enough data into memory, especially if the step is invoked concurrently, the function invocation might run out of memory. You can see your function's peak memory use by going to the [Observability Query page](https://vercel.com/docs/observability) and showing the **Function Invocation Peak Memory** metric, then filtering down the **Route** to `/.well-known/workflow` endpoints.
19
+ - **Network issues**: persistent firewall, network stability, and related issues might prevent your function from reporting results or errors. This should be temporary.
20
+
21
+ ## Getting Help
22
+
23
+ If you consistently see multiple `step_started` events and have ruled out function timeouts, OOMs, and firewall issues, please [contact support](https://vercel.com/help).
@@ -345,29 +345,19 @@ export async function batchProcessingWorkflow(items: string[]) {
345
345
  }
346
346
  ```
347
347
 
348
- ### Streaming AI Responses with `DurableAgent`
348
+ ### Streaming AI Responses with `WorkflowAgent`
349
349
 
350
- Stream AI-generated content using [`DurableAgent`](/docs/api-reference/workflow-ai/durable-agent) from `@workflow/ai`. Tools can also emit progress updates to the same stream using [data chunks](https://ai-sdk.dev/docs/ai-sdk-ui/streaming-data#streaming-custom-data) with the [`UIMessageChunk`](https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol) type from the AI SDK:
350
+ Stream AI-generated content using AI SDK's [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) from `@ai-sdk/workflow`. The agent writes `ModelCallStreamPart` chunks to the workflow stream, and route handlers convert them to UI message chunks with `createModelCallToUIChunkTransform()` before returning the response:
351
351
 
352
352
  ```typescript title="workflows/ai-assistant.ts" lineNumbers
353
- import { DurableAgent } from "@workflow/ai/agent";
353
+ import { WorkflowAgent, type ModelCallStreamPart } from "@ai-sdk/workflow";
354
+ import { tool } from "ai";
354
355
  import { getWritable } from "workflow";
355
356
  import { z } from "zod";
356
- import type { UIMessageChunk } from "ai";
357
357
 
358
358
  async function searchFlights({ query }: { query: string }) {
359
359
  "use step";
360
360
 
361
- // Tools can emit progress updates to the stream
362
- const writable = getWritable<UIMessageChunk>(); // [!code highlight]
363
- const writer = writable.getWriter(); // [!code highlight]
364
- await writer.write({ // [!code highlight]
365
- type: "data-progress", // [!code highlight]
366
- data: { message: `Searching flights for ${query}...` }, // [!code highlight]
367
- transient: true, // [!code highlight]
368
- }); // [!code highlight]
369
- writer.releaseLock(); // [!code highlight]
370
-
371
361
  // ... search logic ...
372
362
  return { flights: [/* results */] };
373
363
  }
@@ -375,27 +365,28 @@ async function searchFlights({ query }: { query: string }) {
375
365
  export async function aiAssistantWorkflow(userMessage: string) {
376
366
  "use workflow";
377
367
 
378
- const agent = new DurableAgent({
368
+ const agent = new WorkflowAgent({
379
369
  model: "anthropic/claude-haiku-4.5",
380
- system: "You are a helpful flight assistant.",
370
+ instructions: "You are a helpful flight assistant.",
381
371
  tools: {
382
- searchFlights: {
372
+ searchFlights: tool({
383
373
  description: "Search for flights",
384
374
  inputSchema: z.object({ query: z.string() }),
385
375
  execute: searchFlights,
386
- },
376
+ }),
387
377
  },
388
378
  });
389
379
 
390
380
  // LLM response will be streamed to the run's writable
391
381
  await agent.stream({
392
382
  messages: [{ role: "user", content: userMessage }],
393
- writable: getWritable<UIMessageChunk>(), // [!code highlight]
383
+ writable: getWritable<ModelCallStreamPart>(), // [!code highlight]
394
384
  });
395
385
  }
396
386
  ```
397
387
 
398
388
  ```typescript title="app/api/ai-assistant/route.ts" lineNumbers
389
+ import { createModelCallToUIChunkTransform } from "@ai-sdk/workflow";
399
390
  import { createUIMessageStreamResponse } from "ai";
400
391
  import { start } from "workflow/api";
401
392
  import { aiAssistantWorkflow } from "./workflows/ai";
@@ -406,13 +397,13 @@ export async function POST(request: Request) {
406
397
  const run = await start(aiAssistantWorkflow, [message]);
407
398
 
408
399
  return createUIMessageStreamResponse({
409
- stream: run.readable,
400
+ stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()), // [!code highlight]
410
401
  });
411
402
  }
412
403
  ```
413
404
 
414
405
  <Callout type="info">
415
- For a complete implementation, see the [flight booking example](https://github.com/vercel/workflow-examples/tree/main/flight-booking-app) which demonstrates streaming AI responses with tool progress updates.
406
+ For the full agent API and migration notes, see the [`WorkflowAgent` documentation](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent).
416
407
  </Callout>
417
408
 
418
409
  ### Streaming Between Steps
@@ -594,7 +585,7 @@ Stream errors don't trigger automatic retries for the producer step. Design your
594
585
  - [`start()` API Reference](/docs/api-reference/workflow-api/start) - Start workflows and access the `Run` object
595
586
  - [`getRun()` API Reference](/docs/api-reference/workflow-api/get-run) - Retrieve runs and their streams later
596
587
  - [world.streams](/docs/api-reference/workflow-api/world/streams) - Low-level stream read/write/close via World SDK
597
- - [DurableAgent](/docs/api-reference/workflow-ai/durable-agent) - AI agents with built-in streaming support
588
+ - [WorkflowAgent](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) - AI agents with durable, resumable streaming support
598
589
  - [Errors and Retries](/docs/foundations/errors-and-retries) - Understanding error handling and retry behavior
599
590
  - [Serialization](/docs/foundations/serialization) - Understanding what data types can be passed in workflows
600
591
  - [Workflows and Steps](/docs/foundations/workflows-and-steps) - Core concepts of workflow execution
@@ -16,4 +16,6 @@ This page is only visible on preview deployments and local development. It does
16
16
 
17
17
  Changelog entries staged here for review before publishing to the Vercel website.
18
18
 
19
+ - [Local web UI in Nitro dev](/docs/internal/nitro-web-ui) — unreleased (next beta)
20
+ - [Native Nitro v3 bundling for workflows](/docs/internal/nitro-native-build) — May 22, 2026
19
21
  - [Serializable AbortController and AbortSignal](/docs/internal/serializable-abort-controller) — March 12, 2026
@@ -1,5 +1,10 @@
1
1
  {
2
2
  "title": "Internal",
3
- "pages": ["index", "serializable-abort-controller"],
3
+ "pages": [
4
+ "index",
5
+ "nitro-web-ui",
6
+ "nitro-native-build",
7
+ "serializable-abort-controller"
8
+ ],
4
9
  "defaultOpen": false
5
10
  }
@@ -0,0 +1,38 @@
1
+ ---
2
+ title: Native Nitro v3 bundling for workflows
3
+ description: Workflow routes are now bundled by Nitro v3, so steps run in your app's runtime and can call any server-side Nitro API.
4
+ type: overview
5
+ ---
6
+
7
+ # Native Nitro v3 bundling for workflows
8
+
9
+ <span className="text-sm text-fd-muted-foreground">May 22, 2026</span>
10
+
11
+ Workflow routes are now handled as Nitro v3 handlers and bundled by Nitro, rather than built separately for the Vercel Build Output API. Your workflows are part of the same bundle as the rest of your app.
12
+
13
+ ## What's new
14
+
15
+ - **Call any server-side Nitro API from a step.** Workflow steps now run inside the same bundled runtime as the rest of your Nitro app, so you can call `useStorage()`, `useDatabase()`, `useRuntimeConfig()`, virtual imports, and any other server-side Nitro API directly from a `"use step"` function.
16
+ - **Workflows are part of your Nitro bundle.** Workflow routes are bundled by Nitro alongside your application code, instead of being built into a separate output. There's nothing extra to configure.
17
+ - **Minimal runtime output.** During bundling, Nitro automatically traces native dependencies and tree-shakes unused code to produce a minimal runtime output.
18
+ - **Nitro v2 is unchanged.** This applies to Nitro v3. Apps on Nitro v2 keep their existing behavior.
19
+
20
+ ## Using Nitro APIs from a step
21
+
22
+ Because steps run inside your Nitro runtime, server-side utilities work directly inside a `"use step"` function:
23
+
24
+ ```typescript
25
+ export async function cacheResult(key: string, value: string) {
26
+ "use step";
27
+
28
+ const storage = useStorage("cache");
29
+ await storage.setItem(key, value);
30
+
31
+ return { cached: true };
32
+ }
33
+ ```
34
+
35
+ ## Learn more
36
+
37
+ - [Nitro](/docs/getting-started/nitro) — Set up Workflow SDK in a Nitro v3 app
38
+ - [Deploying](/docs/deploying) — How workflow bundles are deployed
@@ -0,0 +1,24 @@
1
+ ---
2
+ title: Local web UI in Nitro dev
3
+ description: Inspect, monitor, and debug your workflow runs from the /_workflow route during Nitro development.
4
+ type: overview
5
+ ---
6
+
7
+ # Local web UI in Nitro dev
8
+
9
+ {/* TODO: unreleased — changeset .changeset/nitro-dashboard-route.md is pending; ships in the next @workflow/nitro beta (5.0.0-beta.12). Update this date on publish. */}
10
+ <span className="text-sm text-fd-muted-foreground">June 2, 2026</span>
11
+
12
+ The Workflow SDK web UI is now built into the Nitro dev server. During development, open `/_workflow` in your browser to inspect, monitor, and debug your workflow runs.
13
+
14
+ ## What's new
15
+
16
+ - **Built-in `/_workflow` route in development.** The route starts the local web UI and redirects to it — no separate command or process required.
17
+ - **Inspect runs in place.** Inspect, monitor, and debug your workflow runs directly from the dev server you're already running.
18
+
19
+ ![Workflow SDK web UI on the /_workflow route](/local-web-ui.png)
20
+
21
+ ## Learn more
22
+
23
+ - [Observability](/docs/observability) — Inspect runs with the web UI and CLI
24
+ - [Nitro](/docs/getting-started/nitro) — Set up Workflow SDK in a Nitro v3 app
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workflow",
3
- "version": "5.0.0-beta.12",
3
+ "version": "5.0.0-beta.14",
4
4
  "description": "Workflow SDK - Build durable, resilient, and observable workflows",
5
5
  "main": "dist/typescript-plugin.cjs",
6
6
  "type": "module",
@@ -57,18 +57,18 @@
57
57
  },
58
58
  "dependencies": {
59
59
  "ms": "2.1.3",
60
- "@workflow/astro": "5.0.0-beta.12",
61
- "@workflow/cli": "5.0.0-beta.12",
62
- "@workflow/core": "5.0.0-beta.12",
60
+ "@workflow/astro": "5.0.0-beta.14",
61
+ "@workflow/cli": "5.0.0-beta.14",
62
+ "@workflow/core": "5.0.0-beta.14",
63
63
  "@workflow/errors": "5.0.0-beta.7",
64
64
  "@workflow/typescript-plugin": "5.0.0-beta.4",
65
65
  "@workflow/utils": "5.0.0-beta.3",
66
- "@workflow/next": "5.0.0-beta.12",
67
- "@workflow/nest": "5.0.0-beta.12",
68
- "@workflow/nitro": "5.0.0-beta.12",
69
- "@workflow/nuxt": "5.0.0-beta.12",
70
- "@workflow/sveltekit": "5.0.0-beta.12",
71
- "@workflow/rollup": "5.0.0-beta.12"
66
+ "@workflow/next": "5.0.0-beta.14",
67
+ "@workflow/nest": "5.0.0-beta.14",
68
+ "@workflow/nitro": "5.0.0-beta.14",
69
+ "@workflow/nuxt": "5.0.0-beta.14",
70
+ "@workflow/sveltekit": "5.0.0-beta.14",
71
+ "@workflow/rollup": "5.0.0-beta.14"
72
72
  },
73
73
  "devDependencies": {
74
74
  "@types/ms": "2.1.0",