workflow 5.0.0-beta.43 → 5.0.0-beta.46
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/README.md +6 -4
- package/dist/internal/builtins.d.ts +4 -4
- package/dist/internal/builtins.js +6 -6
- package/docs/ai/chat-session-modeling.mdx +23 -24
- package/docs/ai/defining-tools.mdx +5 -6
- package/docs/ai/human-in-the-loop.mdx +11 -11
- package/docs/ai/index.mdx +20 -20
- package/docs/ai/message-queueing.mdx +6 -6
- package/docs/ai/meta.json +1 -0
- package/docs/ai/resumable-streams.mdx +28 -28
- package/docs/ai/sleep-and-delays.mdx +9 -9
- package/docs/ai/streaming-updates-from-tools.mdx +4 -4
- package/docs/api-reference/vitest/index.mdx +8 -8
- package/docs/api-reference/workflow/create-hook.mdx +15 -15
- package/docs/api-reference/workflow/create-webhook.mdx +15 -15
- package/docs/api-reference/workflow/define-hook.mdx +10 -10
- package/docs/api-reference/workflow/fatal-error.mdx +2 -2
- package/docs/api-reference/workflow/fetch.mdx +7 -7
- package/docs/api-reference/workflow/get-step-metadata.mdx +2 -2
- package/docs/api-reference/workflow/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/workflow/get-writable.mdx +7 -7
- package/docs/api-reference/workflow/index.mdx +1 -1
- package/docs/api-reference/workflow/retryable-error.mdx +1 -1
- package/docs/api-reference/workflow/set-attributes.mdx +2 -2
- package/docs/api-reference/workflow/sleep.mdx +3 -3
- package/docs/api-reference/workflow-ai/durable-agent.mdx +42 -42
- package/docs/api-reference/workflow-ai/index.mdx +3 -3
- package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +28 -28
- package/docs/api-reference/workflow-api/get-hook-by-token.mdx +11 -11
- package/docs/api-reference/workflow-api/get-run.mdx +10 -10
- package/docs/api-reference/workflow-api/index.mdx +2 -4
- package/docs/api-reference/workflow-api/resume-hook.mdx +16 -16
- package/docs/api-reference/workflow-api/resume-webhook.mdx +4 -4
- package/docs/api-reference/workflow-api/start.mdx +16 -15
- package/docs/api-reference/workflow-astro/workflow.mdx +3 -3
- package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
- package/docs/api-reference/workflow-errors/index.mdx +6 -6
- package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +9 -9
- package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/throttle-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/too-early-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/workflow-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +5 -6
- package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +6 -6
- package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
- package/docs/api-reference/workflow-globals.mdx +12 -11
- package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +2 -2
- package/docs/api-reference/workflow-nest/nest-local-builder.mdx +4 -4
- package/docs/api-reference/workflow-nest/workflow-controller.mdx +1 -1
- package/docs/api-reference/workflow-nest/workflow-module.mdx +4 -4
- package/docs/api-reference/workflow-next/with-workflow.mdx +16 -16
- package/docs/api-reference/workflow-nitro/index.mdx +6 -6
- package/docs/api-reference/workflow-nuxt/index.mdx +4 -4
- package/docs/api-reference/workflow-observability/hydrate-data.mdx +5 -5
- package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +4 -4
- package/docs/api-reference/workflow-observability/index.mdx +6 -8
- package/docs/api-reference/workflow-observability/observability-revivers.mdx +2 -2
- package/docs/api-reference/workflow-observability/parse-class-name.mdx +3 -3
- package/docs/api-reference/workflow-observability/parse-step-name.mdx +4 -4
- package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +4 -4
- package/docs/api-reference/workflow-runtime/create-world.mdx +8 -8
- package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +6 -6
- package/docs/api-reference/workflow-runtime/get-world.mdx +4 -4
- package/docs/api-reference/workflow-runtime/health-check.mdx +1 -1
- package/docs/api-reference/workflow-runtime/index.mdx +2 -4
- package/docs/api-reference/workflow-runtime/set-world.mdx +15 -13
- package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +6 -5
- package/docs/api-reference/workflow-runtime/world/analytics.mdx +9 -9
- package/docs/api-reference/workflow-runtime/world/index.mdx +5 -3
- package/docs/api-reference/workflow-runtime/world/queue.mdx +11 -11
- package/docs/api-reference/workflow-runtime/world/storage.mdx +62 -28
- package/docs/api-reference/workflow-runtime/world/streams.mdx +7 -7
- package/docs/api-reference/workflow-serde/index.mdx +1 -1
- package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +2 -2
- package/docs/api-reference/workflow-serde/workflow-serialize.mdx +7 -7
- package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +3 -3
- package/docs/api-reference/workflow-vite/workflow.mdx +5 -5
- package/docs/changelog/attributes-mvp.mdx +39 -39
- package/docs/changelog/batched-event-writes.mdx +12 -12
- package/docs/changelog/eager-processing.mdx +63 -63
- package/docs/changelog/index.mdx +4 -3
- package/docs/changelog/lazy-event-creation.md +27 -27
- package/docs/changelog/lazy-hook-resume.mdx +51 -0
- package/docs/changelog/meta.json +1 -0
- package/docs/changelog/resilient-resume.mdx +13 -5
- package/docs/changelog/resilient-start.mdx +14 -14
- package/docs/changelog/step-message-ownership.mdx +47 -47
- package/docs/changelog/turbo-mode.md +20 -20
- package/docs/comparisons/index.mdx +13 -13
- package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +15 -15
- package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +12 -12
- package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +11 -11
- package/docs/comparisons/workflow-sdk-vs-inngest.mdx +19 -19
- package/docs/comparisons/workflow-sdk-vs-temporal.mdx +23 -23
- package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +18 -17
- package/docs/configuration/build-and-diagnostics.mdx +5 -5
- package/docs/configuration/cli-and-web-ui.mdx +4 -4
- package/docs/configuration/runtime-tuning.mdx +87 -23
- package/docs/configuration/worlds.mdx +28 -14
- package/docs/cookbook/advanced/child-workflows.mdx +25 -25
- package/docs/cookbook/advanced/publishing-libraries.mdx +40 -40
- package/docs/cookbook/advanced/serializable-steps.mdx +21 -21
- package/docs/cookbook/advanced/upgrading-workflows.mdx +31 -31
- package/docs/cookbook/agent-patterns/agent-cancellation.mdx +20 -20
- package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +22 -22
- package/docs/cookbook/common-patterns/batching.mdx +14 -14
- package/docs/cookbook/common-patterns/idempotency.mdx +9 -9
- package/docs/cookbook/common-patterns/rate-limiting.mdx +3 -3
- package/docs/cookbook/common-patterns/saga.mdx +19 -19
- package/docs/cookbook/common-patterns/scheduling.mdx +23 -23
- package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +26 -26
- package/docs/cookbook/common-patterns/timeouts.mdx +23 -23
- package/docs/cookbook/common-patterns/webhooks.mdx +6 -6
- package/docs/cookbook/common-patterns/workflow-composition.mdx +19 -19
- package/docs/cookbook/index.mdx +22 -22
- package/docs/cookbook/integrations/ai-sdk.mdx +43 -41
- package/docs/cookbook/integrations/chat-sdk.mdx +34 -34
- package/docs/cookbook/integrations/sandbox.mdx +46 -46
- package/docs/deploying.mdx +15 -15
- package/docs/errors/abort-signal-timeout-in-workflow.mdx +12 -12
- package/docs/errors/corrupted-event-log.mdx +11 -11
- package/docs/errors/deployment-mismatch.mdx +14 -14
- package/docs/errors/fetch-in-workflow.mdx +8 -8
- package/docs/errors/hook-conflict.mdx +11 -11
- package/docs/errors/index.mdx +1 -1
- package/docs/errors/node-js-module-in-workflow.mdx +5 -5
- package/docs/errors/replay-divergence.mdx +2 -2
- package/docs/errors/runtime-decryption-failed.mdx +12 -12
- package/docs/errors/serialization-failed.mdx +40 -12
- package/docs/errors/start-invalid-workflow-function.mdx +5 -5
- package/docs/errors/step-executed-multiple-times.mdx +2 -2
- package/docs/errors/step-not-registered.mdx +5 -5
- package/docs/errors/timeout-in-workflow.mdx +8 -8
- package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
- package/docs/errors/webhook-response-not-sent.mdx +16 -16
- package/docs/errors/workflow-not-registered.mdx +5 -5
- package/docs/foundations/cancellation.mdx +31 -31
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +35 -35
- package/docs/foundations/idempotency.mdx +9 -9
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +36 -37
- package/docs/foundations/streaming.mdx +46 -41
- package/docs/foundations/versioning.mdx +3 -3
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/getting-started/astro.mdx +16 -16
- package/docs/getting-started/express.mdx +8 -8
- package/docs/getting-started/fastify.mdx +8 -8
- package/docs/getting-started/hono.mdx +8 -8
- package/docs/getting-started/nestjs.mdx +18 -17
- package/docs/getting-started/next.mdx +11 -11
- package/docs/getting-started/nitro.mdx +16 -16
- package/docs/getting-started/nuxt.mdx +8 -8
- package/docs/getting-started/python.mdx +4 -4
- package/docs/getting-started/react-router/v7.mdx +1 -1
- package/docs/getting-started/react-router/v8.mdx +1 -1
- package/docs/getting-started/sveltekit.mdx +14 -14
- package/docs/getting-started/tanstack-start.mdx +12 -12
- package/docs/getting-started/vite.mdx +8 -8
- package/docs/how-it-works/cancellation.mdx +62 -62
- package/docs/how-it-works/code-transform.mdx +66 -54
- package/docs/how-it-works/encryption.mdx +25 -21
- package/docs/how-it-works/event-sourcing.mdx +53 -35
- package/docs/how-it-works/framework-integrations.mdx +12 -12
- package/docs/how-it-works/understanding-directives.mdx +21 -21
- package/docs/internal/index.mdx +6 -6
- package/docs/internal/nitro-native-build.mdx +2 -2
- package/docs/internal/nitro-web-ui.mdx +4 -4
- package/docs/internal/serializable-abort-controller.mdx +7 -7
- package/docs/observability/attributes.mdx +3 -3
- package/docs/observability/index.mdx +14 -10
- package/docs/observability/tracing.mdx +10 -10
- package/docs/testing/index.mdx +33 -33
- package/docs/testing/server-based.mdx +10 -10
- package/docs/whats-new.mdx +185 -0
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
<h1>Workflow SDK</h1>
|
|
9
9
|
|
|
10
10
|
<a href="https://vercel.com"><img alt="Vercel logo" src="https://img.shields.io/badge/MADE%20BY%20Vercel-000000.svg?style=for-the-badge&logo=Vercel&labelColor=000"></a>
|
|
11
|
-
<a href="https://www.npmjs.com/package/workflow"><img alt="
|
|
11
|
+
<a href="https://www.npmjs.com/package/workflow"><img alt="npm version" src="https://img.shields.io/npm/v/workflow?style=for-the-badge&labelColor=000000"></a>
|
|
12
12
|
<a href="https://github.com/vercel/workflow/blob/main/LICENSE.md"><img alt="License" src="https://img.shields.io/npm/l/workflow.svg?style=for-the-badge&labelColor=000000"></a>
|
|
13
|
-
<a href="https://github.com/vercel/workflow/discussions"><img alt="Join the community on GitHub" src="https://img.shields.io/badge/Join%20the%20community-blueviolet.svg?style=for-the-badge&logo=
|
|
13
|
+
<a href="https://github.com/vercel/workflow/discussions"><img alt="Join the community on GitHub" src="https://img.shields.io/badge/Join%20the%20community-blueviolet.svg?style=for-the-badge&logo=GitHub&labelColor=000000&logoWidth=20"></a>
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
@@ -70,7 +70,9 @@ Vercel for managed storage, queuing, scaling, and observability. To self-host,
|
|
|
70
70
|
use the Postgres backend or implement a custom
|
|
71
71
|
[World](https://workflow-sdk.dev/docs/deploying).
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
The [Worlds page](https://workflow-sdk.dev/worlds) lists maintainer-curated
|
|
74
|
+
third-party Worlds, including self-hosted and managed options. Submit your World
|
|
75
|
+
by updating the [Worlds Manifest](https://github.com/vercel/workflow/blob/main/worlds-manifest.json).
|
|
74
76
|
|
|
75
77
|
## Community
|
|
76
78
|
|
|
@@ -88,7 +90,7 @@ with the team and wider community. By participating, you agree to our
|
|
|
88
90
|
|
|
89
91
|
## Security
|
|
90
92
|
|
|
91
|
-
If you
|
|
93
|
+
If you find a security vulnerability in Workflow SDK, **_disclose it responsibly instead of opening a public issue_**.
|
|
92
94
|
|
|
93
95
|
To participate in our Open Source Software Bug Bounty program, please email
|
|
94
96
|
[responsible.disclosure@vercel.com](mailto:responsible.disclosure@vercel.com).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Built-in steps available in the workflow scope. Like a standard library,
|
|
3
|
+
* they are available alongside user-defined steps, but users do not import
|
|
4
|
+
* them. The runtime uses these steps internally.
|
|
5
5
|
*/
|
|
6
6
|
export declare function __builtin_response_array_buffer(this: Request | Response): Promise<ArrayBuffer>;
|
|
7
7
|
export declare function __builtin_response_json(this: Request | Response): Promise<unknown>;
|
|
@@ -12,7 +12,7 @@ export declare function __builtin_response_text(this: Request | Response): Promi
|
|
|
12
12
|
* runs in normal Node context with full world access.
|
|
13
13
|
*
|
|
14
14
|
* The dispatch reads the world and current run id directly from
|
|
15
|
-
* `globalThis` symbols populated by the workflow/step runtime
|
|
15
|
+
* `globalThis` symbols populated by the workflow/step runtime. This
|
|
16
16
|
* intentionally avoids importing `@workflow/core` so the Next.js
|
|
17
17
|
* deferred-entries discoverer can't walk a chain into world adapters
|
|
18
18
|
* and `@vercel/queue` from this step file.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Built-in steps available in the workflow scope. Like a standard library,
|
|
3
|
+
* they are available alongside user-defined steps, but users do not import
|
|
4
|
+
* them. The runtime uses these steps internally.
|
|
5
5
|
*/
|
|
6
6
|
export async function __builtin_response_array_buffer() {
|
|
7
7
|
'use step';
|
|
@@ -33,7 +33,7 @@ function formatUnknownError(error) {
|
|
|
33
33
|
* runs in normal Node context with full world access.
|
|
34
34
|
*
|
|
35
35
|
* The dispatch reads the world and current run id directly from
|
|
36
|
-
* `globalThis` symbols populated by the workflow/step runtime
|
|
36
|
+
* `globalThis` symbols populated by the workflow/step runtime. This
|
|
37
37
|
* intentionally avoids importing `@workflow/core` so the Next.js
|
|
38
38
|
* deferred-entries discoverer can't walk a chain into world adapters
|
|
39
39
|
* and `@vercel/queue` from this step file.
|
|
@@ -50,7 +50,7 @@ export async function __builtin_set_attributes(changes, options) {
|
|
|
50
50
|
: INTERNAL_ATTRIBUTES_MAX_ATTEMPTS;
|
|
51
51
|
const world = g[Symbol.for('@workflow/world//cache')];
|
|
52
52
|
if (typeof world?.runs?.experimentalSetAttributes !== 'function') {
|
|
53
|
-
// World adapter doesn't implement attributes yet
|
|
53
|
+
// World adapter doesn't implement attributes yet, so no-op the call,
|
|
54
54
|
// but emit one process-wide warning so users know their writes are
|
|
55
55
|
// being dropped. The VM-side validation already ran so the input
|
|
56
56
|
// is well-formed.
|
|
@@ -79,4 +79,4 @@ export async function __builtin_set_attributes(changes, options) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
__builtin_set_attributes.maxRetries = INTERNAL_ATTRIBUTES_MAX_ATTEMPTS - 1;
|
|
82
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
82
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVpbHRpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW50ZXJuYWwvYnVpbHRpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUVILE1BQU0sQ0FBQyxLQUFLLFVBQVUsK0JBQStCO0lBR25ELFVBQVUsQ0FBQztJQUNYLE9BQU8sSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQzVCLENBQUM7QUFFRCxNQUFNLENBQUMsS0FBSyxVQUFVLHVCQUF1QjtJQUMzQyxVQUFVLENBQUM7SUFDWCxPQUFPLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUNyQixDQUFDO0FBRUQsTUFBTSxDQUFDLEtBQUssVUFBVSx1QkFBdUI7SUFDM0MsVUFBVSxDQUFDO0lBQ1gsT0FBTyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7QUFDckIsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sd0JBQXdCLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FDekMsaURBQWlELENBQ2xELENBQUM7QUFFRixNQUFNLGdDQUFnQyxHQUFHLENBQUMsQ0FBQztBQUUzQyxTQUFTLGtCQUFrQixDQUFDLEtBQWM7SUFDeEMsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7UUFDM0IsT0FBTyxLQUFLLENBQUMsS0FBSyxJQUFJLEdBQUcsS0FBSyxDQUFDLElBQUksS0FBSyxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDMUQsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQ3ZCLENBQUM7QUFFRDs7Ozs7Ozs7OztHQVVHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSx3QkFBd0IsQ0FDNUMsT0FBcUQsRUFDckQsT0FBK0M7SUFFL0MsVUFBVSxDQUFDO0lBQ1gsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPO0lBQ2pDLE1BQU0sQ0FBQyxHQUFHLFVBQXFDLENBQUM7SUFFaEQsTUFBTSxjQUFjLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsK0JBQStCLENBQUMsQ0FTdkQsQ0FBQztJQUNkLE1BQU0sS0FBSyxHQUFHLGNBQWMsRUFBRSxRQUFRLEVBQUUsRUFBRSxDQUFDO0lBQzNDLE1BQU0sT0FBTyxHQUNYLE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxPQUFPLEtBQUssUUFBUTtRQUM5QyxDQUFDLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxPQUFPO1FBQzVCLENBQUMsQ0FBQyxnQ0FBZ0MsQ0FBQztJQUV2QyxNQUFNLEtBQUssR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyx3QkFBd0IsQ0FBQyxDQVd2QyxDQUFDO0lBQ2QsSUFBSSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUseUJBQXlCLEtBQUssVUFBVSxFQUFFLENBQUM7UUFDakUscUVBQXFFO1FBQ3JFLG1FQUFtRTtRQUNuRSxpRUFBaUU7UUFDakUsa0JBQWtCO1FBQ2xCLElBQUksQ0FBQyxDQUFDLENBQUMsd0JBQXdCLENBQUMsRUFBRSxDQUFDO1lBQ2pDLENBQUMsQ0FBQyx3QkFBd0IsQ0FBQyxHQUFHLElBQUksQ0FBQztZQUNuQyxNQUFNLFNBQVMsR0FBRyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLEtBQUssQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQ3hELE9BQU8sQ0FBQyxJQUFJLENBQ1YsNkRBQTZELFNBQVMscU1BQXFNLENBQzVRLENBQUM7UUFDSixDQUFDO1FBQ0QsT0FBTztJQUNULENBQUM7SUFFRCxJQUFJLENBQUM7UUFDSCxNQUFNLEtBQUssR0FBRyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsYUFBYSxDQUFDO1FBQ3JELElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNYLE1BQU0sSUFBSSxLQUFLLENBQ2Isd0VBQXdFLENBQ3pFLENBQUM7UUFDSixDQUFDO1FBRUQsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLHlCQUF5QixDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDdEUsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixJQUFJLE9BQU8sR0FBRyxnQ0FBZ0MsRUFBRSxDQUFDO1lBQy9DLE1BQU0sS0FBSyxDQUFDO1FBQ2QsQ0FBQztRQUVELDRFQUE0RTtRQUM1RSxzRUFBc0U7UUFDdEUsK0RBQStEO1FBQy9ELE9BQU8sQ0FBQyxLQUFLLENBQ1gsdURBQXVELGdDQUFnQyxxREFBcUQsa0JBQWtCLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FDeEssQ0FBQztJQUNKLENBQUM7QUFDSCxDQUFDO0FBR0Msd0JBR0QsQ0FBQyxVQUFVLEdBQUcsZ0NBQWdDLEdBQUcsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBCdWlsdC1pbiBzdGVwcyBhdmFpbGFibGUgaW4gdGhlIHdvcmtmbG93IHNjb3BlLiBMaWtlIGEgc3RhbmRhcmQgbGlicmFyeSxcbiAqIHRoZXkgYXJlIGF2YWlsYWJsZSBhbG9uZ3NpZGUgdXNlci1kZWZpbmVkIHN0ZXBzLCBidXQgdXNlcnMgZG8gbm90IGltcG9ydFxuICogdGhlbS4gVGhlIHJ1bnRpbWUgdXNlcyB0aGVzZSBzdGVwcyBpbnRlcm5hbGx5LlxuICovXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBfX2J1aWx0aW5fcmVzcG9uc2VfYXJyYXlfYnVmZmVyKFxuICB0aGlzOiBSZXF1ZXN0IHwgUmVzcG9uc2Vcbikge1xuICAndXNlIHN0ZXAnO1xuICByZXR1cm4gdGhpcy5hcnJheUJ1ZmZlcigpO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gX19idWlsdGluX3Jlc3BvbnNlX2pzb24odGhpczogUmVxdWVzdCB8IFJlc3BvbnNlKSB7XG4gICd1c2Ugc3RlcCc7XG4gIHJldHVybiB0aGlzLmpzb24oKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIF9fYnVpbHRpbl9yZXNwb25zZV90ZXh0KHRoaXM6IFJlcXVlc3QgfCBSZXNwb25zZSkge1xuICAndXNlIHN0ZXAnO1xuICByZXR1cm4gdGhpcy50ZXh0KCk7XG59XG5cbi8qKlxuICogUHJvY2Vzcy13aWRlIGRlZHVwZSBmb3IgdGhlIHVuc3VwcG9ydGVkLXdvcmxkIHdhcm5pbmcgc28gaGlnaC12b2x1bWVcbiAqIGNhbGxlcnMgZG9uJ3QgZmxvb2QgbG9ncy5cbiAqL1xuY29uc3QgVU5TVVBQT1JURURfV09STERfV0FSTkVEID0gU3ltYm9sLmZvcihcbiAgJ0B3b3JrZmxvdy9zZXRBdHRyaWJ1dGVzLy91bnN1cHBvcnRlZFdvcmxkV2FybmVkJ1xuKTtcblxuY29uc3QgSU5URVJOQUxfQVRUUklCVVRFU19NQVhfQVRURU1QVFMgPSAzO1xuXG5mdW5jdGlvbiBmb3JtYXRVbmtub3duRXJyb3IoZXJyb3I6IHVua25vd24pIHtcbiAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICByZXR1cm4gZXJyb3Iuc3RhY2sgPz8gYCR7ZXJyb3IubmFtZX06ICR7ZXJyb3IubWVzc2FnZX1gO1xuICB9XG4gIHJldHVybiBTdHJpbmcoZXJyb3IpO1xufVxuXG4vKipcbiAqIFN0ZXAgYnJpZGdlIGZvciB3b3JrZmxvdy1ib2R5IGBzZXRBdHRyaWJ1dGVzYCBjYWxscy4gVGhlIFZNLXNpZGVcbiAqIGhlbHBlciB2YWxpZGF0ZXMgaW5wdXQgYW5kIGRpc3BhdGNoZXMgaGVyZSB2aWEgYHVzZVN0ZXBgLiBUaGlzIHN0ZXBcbiAqIHJ1bnMgaW4gbm9ybWFsIE5vZGUgY29udGV4dCB3aXRoIGZ1bGwgd29ybGQgYWNjZXNzLlxuICpcbiAqIFRoZSBkaXNwYXRjaCByZWFkcyB0aGUgd29ybGQgYW5kIGN1cnJlbnQgcnVuIGlkIGRpcmVjdGx5IGZyb21cbiAqIGBnbG9iYWxUaGlzYCBzeW1ib2xzIHBvcHVsYXRlZCBieSB0aGUgd29ya2Zsb3cvc3RlcCBydW50aW1lLiBUaGlzXG4gKiBpbnRlbnRpb25hbGx5IGF2b2lkcyBpbXBvcnRpbmcgYEB3b3JrZmxvdy9jb3JlYCBzbyB0aGUgTmV4dC5qc1xuICogZGVmZXJyZWQtZW50cmllcyBkaXNjb3ZlcmVyIGNhbid0IHdhbGsgYSBjaGFpbiBpbnRvIHdvcmxkIGFkYXB0ZXJzXG4gKiBhbmQgYEB2ZXJjZWwvcXVldWVgIGZyb20gdGhpcyBzdGVwIGZpbGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBfX2J1aWx0aW5fc2V0X2F0dHJpYnV0ZXMoXG4gIGNoYW5nZXM6IEFycmF5PHsga2V5OiBzdHJpbmc7IHZhbHVlOiBzdHJpbmcgfCBudWxsIH0+LFxuICBvcHRpb25zPzogeyBhbGxvd1Jlc2VydmVkQXR0cmlidXRlcz86IGJvb2xlYW4gfVxuKSB7XG4gICd1c2Ugc3RlcCc7XG4gIGlmIChjaGFuZ2VzLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuICBjb25zdCBnID0gZ2xvYmFsVGhpcyBhcyBSZWNvcmQ8c3ltYm9sLCB1bmtub3duPjtcblxuICBjb25zdCBjb250ZXh0U3RvcmFnZSA9IGdbU3ltYm9sLmZvcignV09SS0ZMT1dfU1RFUF9DT05URVhUX1NUT1JBR0UnKV0gYXNcbiAgICB8IHtcbiAgICAgICAgZ2V0U3RvcmU6ICgpID0+XG4gICAgICAgICAgfCB7XG4gICAgICAgICAgICAgIHN0ZXBNZXRhZGF0YT86IHsgYXR0ZW1wdD86IG51bWJlciB9O1xuICAgICAgICAgICAgICB3b3JrZmxvd01ldGFkYXRhPzogeyB3b3JrZmxvd1J1bklkPzogc3RyaW5nIH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfCB1bmRlZmluZWQ7XG4gICAgICB9XG4gICAgfCB1bmRlZmluZWQ7XG4gIGNvbnN0IHN0b3JlID0gY29udGV4dFN0b3JhZ2U/LmdldFN0b3JlPy4oKTtcbiAgY29uc3QgYXR0ZW1wdCA9XG4gICAgdHlwZW9mIHN0b3JlPy5zdGVwTWV0YWRhdGE/LmF0dGVtcHQgPT09ICdudW1iZXInXG4gICAgICA/IHN0b3JlLnN0ZXBNZXRhZGF0YS5hdHRlbXB0XG4gICAgICA6IElOVEVSTkFMX0FUVFJJQlVURVNfTUFYX0FUVEVNUFRTO1xuXG4gIGNvbnN0IHdvcmxkID0gZ1tTeW1ib2wuZm9yKCdAd29ya2Zsb3cvd29ybGQvL2NhY2hlJyldIGFzXG4gICAgfCB7XG4gICAgICAgIG5hbWU/OiBzdHJpbmc7XG4gICAgICAgIHJ1bnM/OiB7XG4gICAgICAgICAgZXhwZXJpbWVudGFsU2V0QXR0cmlidXRlcz86IChcbiAgICAgICAgICAgIHJ1bklkOiBzdHJpbmcsXG4gICAgICAgICAgICBjaGFuZ2VzOiBBcnJheTx7IGtleTogc3RyaW5nOyB2YWx1ZTogc3RyaW5nIHwgbnVsbCB9PixcbiAgICAgICAgICAgIG9wdGlvbnM/OiB7IGFsbG93UmVzZXJ2ZWRBdHRyaWJ1dGVzPzogYm9vbGVhbiB9XG4gICAgICAgICAgKSA9PiBQcm9taXNlPHVua25vd24+O1xuICAgICAgICB9O1xuICAgICAgfVxuICAgIHwgdW5kZWZpbmVkO1xuICBpZiAodHlwZW9mIHdvcmxkPy5ydW5zPy5leHBlcmltZW50YWxTZXRBdHRyaWJ1dGVzICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgLy8gV29ybGQgYWRhcHRlciBkb2Vzbid0IGltcGxlbWVudCBhdHRyaWJ1dGVzIHlldCwgc28gbm8tb3AgdGhlIGNhbGwsXG4gICAgLy8gYnV0IGVtaXQgb25lIHByb2Nlc3Mtd2lkZSB3YXJuaW5nIHNvIHVzZXJzIGtub3cgdGhlaXIgd3JpdGVzIGFyZVxuICAgIC8vIGJlaW5nIGRyb3BwZWQuIFRoZSBWTS1zaWRlIHZhbGlkYXRpb24gYWxyZWFkeSByYW4gc28gdGhlIGlucHV0XG4gICAgLy8gaXMgd2VsbC1mb3JtZWQuXG4gICAgaWYgKCFnW1VOU1VQUE9SVEVEX1dPUkxEX1dBUk5FRF0pIHtcbiAgICAgIGdbVU5TVVBQT1JURURfV09STERfV0FSTkVEXSA9IHRydWU7XG4gICAgICBjb25zdCB3b3JsZE5hbWUgPSB3b3JsZD8ubmFtZSA/IGAgKCR7d29ybGQubmFtZX0pYCA6ICcnO1xuICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICBgW3dvcmtmbG93XSBzZXRBdHRyaWJ1dGVzOiB0aGUgY3VycmVudCB3b3JsZCBpbXBsZW1lbnRhdGlvbiR7d29ybGROYW1lfSBkb2VzIG5vdCBpbXBsZW1lbnQgZXhwZXJpbWVudGFsU2V0QXR0cmlidXRlczsgdGhpcyBjYWxsIChhbmQgYW55IHN1YnNlcXVlbnQgc2V0QXR0cmlidXRlcyBjYWxscyBpbiB0aGlzIHByb2Nlc3MpIGlzIGEgbm8tb3AuIEF0dHJpYnV0ZXMgd2lsbCBiZWNvbWUgYXZhaWxhYmxlIG9uY2UgdGhlIHdvcmxkIGFkYXB0ZXIgYWRkcyBzdXBwb3J0LmBcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgY29uc3QgcnVuSWQgPSBzdG9yZT8ud29ya2Zsb3dNZXRhZGF0YT8ud29ya2Zsb3dSdW5JZDtcbiAgICBpZiAoIXJ1bklkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdfX2J1aWx0aW5fc2V0X2F0dHJpYnV0ZXM6IG5vIHdvcmtmbG93IHJ1biBpZCBhdmFpbGFibGUgaW4gc3RlcCBjb250ZXh0J1xuICAgICAgKTtcbiAgICB9XG5cbiAgICBhd2FpdCB3b3JsZC5ydW5zLmV4cGVyaW1lbnRhbFNldEF0dHJpYnV0ZXMocnVuSWQsIGNoYW5nZXMsIG9wdGlvbnMpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmIChhdHRlbXB0IDwgSU5URVJOQUxfQVRUUklCVVRFU19NQVhfQVRURU1QVFMpIHtcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cblxuICAgIC8vIEZhaWxpbmcgdG8gcG9zdCB0YWdzIHNob3VsZCBub3QgZmFpbCBhIHJ1biBkdXJpbmcgdGhlIGV4cGVyaW1lbnRhbCBwaGFzZS5cbiAgICAvLyBBZnRlciB0aHJlZSBhdHRlbXB0cywgbG9nIGFuZCBsZXQgdGhlIGludGVybmFsIHN0ZXAgY29tcGxldGUgc28gdGhlXG4gICAgLy8gcnVudGltZSBkb2VzIG5vdCBjb252ZXJ0IHJldHJ5IGV4aGF1c3Rpb24gaW50byBhIEZhdGFsRXJyb3IuXG4gICAgY29uc29sZS5lcnJvcihcbiAgICAgIGBbd29ya2Zsb3ddIHNldEF0dHJpYnV0ZXM6IGZhaWxlZCB0byBwb3N0IHRhZ3MgYWZ0ZXIgJHtJTlRFUk5BTF9BVFRSSUJVVEVTX01BWF9BVFRFTVBUU30gYXR0ZW1wdHM7IGRyb3BwaW5nIHRoZSBpbnRlcm5hbCBhdHRyaWJ1dGUgd3JpdGUuICR7Zm9ybWF0VW5rbm93bkVycm9yKGVycm9yKX1gXG4gICAgKTtcbiAgfVxufVxuXG4oXG4gIF9fYnVpbHRpbl9zZXRfYXR0cmlidXRlcyBhcyB0eXBlb2YgX19idWlsdGluX3NldF9hdHRyaWJ1dGVzICYge1xuICAgIG1heFJldHJpZXM6IG51bWJlcjtcbiAgfVxuKS5tYXhSZXRyaWVzID0gSU5URVJOQUxfQVRUUklCVVRFU19NQVhfQVRURU1QVFMgLSAxO1xuIl19
|
|
@@ -22,7 +22,7 @@ Chat sessions in AI agents can be modeled at different layers of your architectu
|
|
|
22
22
|
|
|
23
23
|
While there are many ways to model chat sessions, the two most common categories are single-turn and multi-turn.
|
|
24
24
|
|
|
25
|
-
## Single-
|
|
25
|
+
## Single-turn workflows
|
|
26
26
|
|
|
27
27
|
Each user message triggers a new workflow run. The client or API route owns the conversation history and sends the full message array with each request.
|
|
28
28
|
|
|
@@ -81,7 +81,7 @@ export async function POST(req: Request) {
|
|
|
81
81
|
|
|
82
82
|
<Tab value="Client">
|
|
83
83
|
|
|
84
|
-
Chat messages need to be stored somewhere
|
|
84
|
+
Chat messages need to be stored somewhere, typically a database. In this example, we assume a route like `/chats/:id` passes the session ID, allowing us to fetch existing messages and persist new ones.
|
|
85
85
|
|
|
86
86
|
```typescript title="app/chats/[id]/page.tsx" lineNumbers
|
|
87
87
|
"use client";
|
|
@@ -138,14 +138,13 @@ This is the pattern used in the [Building Durable AI Agents](/docs/ai) guide.
|
|
|
138
138
|
|
|
139
139
|
In this pattern, the client owns conversation state, with the latest turn managed by the AI SDK's `useChat`, and past turns persisted to a user-managed database.
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
Persist the turn through one of these methods:
|
|
142
142
|
|
|
143
|
-
-
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
- Note that user messages are not persisted to the stream by default, and need to be explicitly persisted separately
|
|
143
|
+
- Run a workflow step after `agent.stream()` that takes the message history from the agent return value (either `messages: ModelMessage[]` or `uiMessages: UIMessage[]`).
|
|
144
|
+
- Use a `useChat` client hook that calls an API to persist state, such as on every new message or in `onFinish`.
|
|
145
|
+
- Use the resumable stream attached to the workflow (see [Resumable streams](/docs/ai/resumable-streams)). User messages are not persisted to the stream by default, so persist them separately.
|
|
147
146
|
|
|
148
|
-
## Multi-
|
|
147
|
+
## Multi-turn workflows
|
|
149
148
|
|
|
150
149
|
A single workflow handles the entire conversation session across multiple turns, and owns the current conversation state. The clients/API routes inject new messages via hooks. The workflow run ID serves as the session identifier.
|
|
151
150
|
|
|
@@ -191,7 +190,7 @@ export async function chat(initialMessages: UIMessage[]) {
|
|
|
191
190
|
tools: flightBookingTools,
|
|
192
191
|
});
|
|
193
192
|
|
|
194
|
-
// Use run ID as the hook token for
|
|
193
|
+
// Use run ID as the hook token for resumption
|
|
195
194
|
const hook = chatMessageHook.create({ token: runId });
|
|
196
195
|
let turnNumber = 0;
|
|
197
196
|
|
|
@@ -254,7 +253,7 @@ export async function writeStreamClose(writable: WritableStream<UIMessageChunk>)
|
|
|
254
253
|
|
|
255
254
|
<Tab value="API Routes">
|
|
256
255
|
|
|
257
|
-
|
|
256
|
+
Use three endpoints to start a session, send follow-up messages, and reconnect to the stream.
|
|
258
257
|
|
|
259
258
|
```typescript title="app/api/chat/route.ts" lineNumbers
|
|
260
259
|
import { createUIMessageStreamResponse, type UIMessage } from "ai";
|
|
@@ -493,13 +492,13 @@ In this pattern, the workflow owns the entire conversation session. All messages
|
|
|
493
492
|
|
|
494
493
|
The client hook processes these markers by:
|
|
495
494
|
|
|
496
|
-
1.
|
|
497
|
-
2. When a `user-message` marker is found,
|
|
498
|
-
3.
|
|
495
|
+
1. Iterate through message parts in order.
|
|
496
|
+
2. When a `user-message` marker is found, flush any accumulated assistant content and insert the user message.
|
|
497
|
+
3. Deduplicate against optimistic sends from the initial message.
|
|
499
498
|
|
|
500
499
|
This ensures the conversation displays as User → AI → User → AI regardless of whether viewing live or replaying from the stream.
|
|
501
500
|
|
|
502
|
-
## Choosing a
|
|
501
|
+
## Choosing a pattern
|
|
503
502
|
|
|
504
503
|
| Consideration | Single-Turn | Multi-Turn |
|
|
505
504
|
|--------------|-------------|------------|
|
|
@@ -509,13 +508,13 @@ This ensures the conversation displays as User → AI → User → AI regardless
|
|
|
509
508
|
| Workflow time horizon | Minutes | Hours to indefinitely |
|
|
510
509
|
| Observability scope | Per-turn traces | Full session traces |
|
|
511
510
|
|
|
512
|
-
**Multi-turn is recommended for most production use
|
|
511
|
+
**Multi-turn is recommended for most production use cases.** For new applications, use multi-turn workflows. The workflow's built-in persistence maintains the chat history and supports native message injection and full-session observability.
|
|
513
512
|
|
|
514
|
-
**Single-turn works well when adapting existing architectures.** If you already have a system for managing message state
|
|
513
|
+
**Single-turn works well when adapting existing architectures.** If you already have a system for managing message state and want to adopt durable agents incrementally, single-turn workflows require fewer changes. Each turn maps to an independent workflow run.
|
|
515
514
|
|
|
516
|
-
## Multiplayer
|
|
515
|
+
## Multiplayer chat sessions
|
|
517
516
|
|
|
518
|
-
The multi-turn pattern also
|
|
517
|
+
The multi-turn pattern also enables multiplayer chat sessions. Messages can come from system events, external services, and other users. A `hook` can inject messages into a workflow at any point, while clients reconnect to one stream containing the entire history.
|
|
519
518
|
|
|
520
519
|
<Tabs items={['System Event', 'External Service', 'Multiple Users']}>
|
|
521
520
|
|
|
@@ -542,7 +541,7 @@ export async function POST(req: Request) {
|
|
|
542
541
|
|
|
543
542
|
<Tab value="External Service">
|
|
544
543
|
|
|
545
|
-
External webhooks from third-party services
|
|
544
|
+
External webhooks from third-party services, such as Stripe and Twilio, can notify the conversation of events.
|
|
546
545
|
|
|
547
546
|
```typescript title="app/api/webhooks/payment/route.ts" lineNumbers
|
|
548
547
|
import { chatMessageHook } from "@/workflows/chat/hooks/chat-message";
|
|
@@ -591,9 +590,9 @@ export async function POST(
|
|
|
591
590
|
|
|
592
591
|
</Tabs>
|
|
593
592
|
|
|
594
|
-
## Related
|
|
593
|
+
## Related documentation
|
|
595
594
|
|
|
596
|
-
- [Building Durable AI Agents](/docs/ai)
|
|
597
|
-
- [Message Queueing](/docs/ai/message-queueing)
|
|
598
|
-
- [`defineHook()` API
|
|
599
|
-
- [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent)
|
|
595
|
+
- [Building Durable AI Agents](/docs/ai): Foundation guide for durable agents
|
|
596
|
+
- [Message Queueing](/docs/ai/message-queueing): Queueing messages during tool execution
|
|
597
|
+
- [`defineHook()` API reference](/docs/api-reference/workflow/define-hook): Hook configuration options
|
|
598
|
+
- [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent): AI SDK API for durable, resumable agents
|
|
@@ -14,16 +14,15 @@ related:
|
|
|
14
14
|
|
|
15
15
|
This page covers the details for some common patterns when defining tools for AI agents using Workflow SDK.
|
|
16
16
|
|
|
17
|
-
Using WorkflowAgent, we model most tools as steps. These can
|
|
17
|
+
Using WorkflowAgent, we model most tools as steps. These can range from a single function call to an entire multi-day workflow.
|
|
18
18
|
|
|
19
19
|
## Accessing message context in tools
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
As with regular AI SDK tool definitions, tools in WorkflowAgent receive the tool's input parameters as the first argument and the tool call context as the second.
|
|
22
22
|
|
|
23
23
|
When you tool needs access to the full message history, you can access it via the `messages` property of the tool call context:
|
|
24
24
|
|
|
25
25
|
```typescript title="tools.ts" lineNumbers
|
|
26
|
-
import { Experimental_Agent as Agent } from "ai";
|
|
27
26
|
import type { ModelMessage } from "ai";
|
|
28
27
|
|
|
29
28
|
async function getWeather(
|
|
@@ -34,9 +33,9 @@ async function getWeather(
|
|
|
34
33
|
}
|
|
35
34
|
```
|
|
36
35
|
|
|
37
|
-
## Writing to
|
|
36
|
+
## Writing to streams
|
|
38
37
|
|
|
39
|
-
As discussed in [Streaming Updates from Tools](/docs/ai/streaming-updates-from-tools), it's common to use a step
|
|
38
|
+
As discussed in [Streaming Updates from Tools](/docs/ai/streaming-updates-from-tools), it's common to use a step only to call `getWritable()` for writing custom data parts to the stream.
|
|
40
39
|
|
|
41
40
|
This can be made generic, by creating a helper step function to write arbitrary data to the stream:
|
|
42
41
|
|
|
@@ -53,7 +52,7 @@ async function writeToStream(data: any) {
|
|
|
53
52
|
}
|
|
54
53
|
```
|
|
55
54
|
|
|
56
|
-
## Step-
|
|
55
|
+
## Step-level vs workflow-level tools
|
|
57
56
|
|
|
58
57
|
Tools can be implemented either at the step level or the workflow level, with different capabilities and constraints.
|
|
59
58
|
|
|
@@ -19,7 +19,7 @@ Workflow SDK's [webhook](/docs/api-reference/workflow/create-webhook) and [hook]
|
|
|
19
19
|
|
|
20
20
|
If you need to react to external events programmatically, see the [hooks](/docs/foundations/hooks) documentation for more information. This part of the guide will focus on the human-in-the-loop pattern, which is a subset of the more general hook pattern.
|
|
21
21
|
|
|
22
|
-
## How
|
|
22
|
+
## How it works
|
|
23
23
|
|
|
24
24
|
<Steps>
|
|
25
25
|
|
|
@@ -45,9 +45,9 @@ The workflow receives the approval data and resumes execution.
|
|
|
45
45
|
|
|
46
46
|
</Steps>
|
|
47
47
|
|
|
48
|
-
While this demo
|
|
48
|
+
While this demo uses a client-side button for human approval, you could instead create a webhook and send the approval link over email or Slack to resume the agent.
|
|
49
49
|
|
|
50
|
-
## Creating a
|
|
50
|
+
## Creating a booking approval tool
|
|
51
51
|
|
|
52
52
|
Add a tool that allows the agent to deliberately pause execution until a human approves or rejects a flight booking:
|
|
53
53
|
|
|
@@ -55,7 +55,7 @@ Add a tool that allows the agent to deliberately pause execution until a human a
|
|
|
55
55
|
|
|
56
56
|
<Step>
|
|
57
57
|
|
|
58
|
-
### Define the
|
|
58
|
+
### Define the hook
|
|
59
59
|
|
|
60
60
|
Create a typed hook with a Zod schema for validation:
|
|
61
61
|
|
|
@@ -78,7 +78,7 @@ export const bookingApprovalHook = defineHook({
|
|
|
78
78
|
|
|
79
79
|
<Step>
|
|
80
80
|
|
|
81
|
-
### Implement the
|
|
81
|
+
### Implement the tool
|
|
82
82
|
|
|
83
83
|
Create a tool that creates a hook instance using the tool call ID as the token. The UI will use this ID to submit the approval.
|
|
84
84
|
|
|
@@ -126,14 +126,14 @@ export const flightBookingTools = {
|
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
<Callout type="info">
|
|
129
|
-
|
|
129
|
+
Call `defineHook().create()` from within a workflow context, not from within a step. `executeBookingApproval` does not have `"use step"` because it runs in the workflow context where hooks are available.
|
|
130
130
|
</Callout>
|
|
131
131
|
|
|
132
132
|
</Step>
|
|
133
133
|
|
|
134
134
|
<Step>
|
|
135
135
|
|
|
136
|
-
### Create the API
|
|
136
|
+
### Create the API route
|
|
137
137
|
|
|
138
138
|
Create a new API endpoint that the UI will call to submit the approval decision:
|
|
139
139
|
|
|
@@ -158,7 +158,7 @@ export async function POST(request: Request) {
|
|
|
158
158
|
|
|
159
159
|
<Step>
|
|
160
160
|
|
|
161
|
-
### Create the
|
|
161
|
+
### Create the approval component
|
|
162
162
|
|
|
163
163
|
Build a new component that reacts to the tool call data, and allows the user to approve or reject the booking:
|
|
164
164
|
|
|
@@ -253,7 +253,7 @@ export function BookingApproval({ toolCallId, input, output }: BookingApprovalPr
|
|
|
253
253
|
|
|
254
254
|
<Step>
|
|
255
255
|
|
|
256
|
-
### Show the
|
|
256
|
+
### Show the tool status in the UI
|
|
257
257
|
|
|
258
258
|
Use the component we just created to render the tool call and approval controls in your chat interface:
|
|
259
259
|
|
|
@@ -332,7 +332,7 @@ export default function ChatPage() {
|
|
|
332
332
|
|
|
333
333
|
</Steps>
|
|
334
334
|
|
|
335
|
-
## Using
|
|
335
|
+
## Using webhooks directly
|
|
336
336
|
|
|
337
337
|
For simpler cases where you don't need type-safe validation or programmatic resumption, you can use [`createWebhook()`](/docs/api-reference/workflow/create-webhook) directly. This generates a unique URL that can be called to resume the workflow:
|
|
338
338
|
|
|
@@ -367,7 +367,7 @@ The webhook URL can be called directly with a POST request containing the approv
|
|
|
367
367
|
- Payment provider callbacks
|
|
368
368
|
- Email-based approval links
|
|
369
369
|
|
|
370
|
-
## Related
|
|
370
|
+
## Related documentation
|
|
371
371
|
|
|
372
372
|
- [Hooks & Webhooks](/docs/foundations/hooks) - Complete guide to hooks and webhooks
|
|
373
373
|
- [`createWebhook()` API Reference](/docs/api-reference/workflow/create-webhook) - Webhook configuration options
|
package/docs/ai/index.mdx
CHANGED
|
@@ -22,7 +22,7 @@ Workflow SDK makes your agents production-ready, by turning them into durable, r
|
|
|
22
22
|
|
|
23
23
|
This guide walks you through converting a basic AI chat app into a durable AI agent using Workflow SDK.
|
|
24
24
|
|
|
25
|
-
## Why
|
|
25
|
+
## Why durable agents?
|
|
26
26
|
|
|
27
27
|
Aside from the usual challenges of getting your long-running tasks to be production-ready, building mature AI agents typically requires solving several **additional challenges**:
|
|
28
28
|
|
|
@@ -31,22 +31,22 @@ Aside from the usual challenges of getting your long-running tasks to be product
|
|
|
31
31
|
- **Resumability**: Resuming streams requires not just storing your messages, but also storing streams, and piping them across services.
|
|
32
32
|
- **Human-in-the-loop**: Your client, API, and async job orchestration need to work together to create, track, route to, and display human approval requests, or similar webhook operations.
|
|
33
33
|
|
|
34
|
-
Workflow SDK provides all of these capabilities
|
|
34
|
+
Workflow SDK provides all of these capabilities without additional infrastructure. Your agent becomes a workflow, your tools become steps, and the framework handles interplay with your existing infrastructure.
|
|
35
35
|
|
|
36
|
-
## Getting
|
|
36
|
+
## Getting started
|
|
37
37
|
|
|
38
38
|
To make an Agent durable, we first need an Agent, which we'll be setting up here. If you already have an app you'd like to follow along with, you can skip this section.
|
|
39
39
|
|
|
40
|
-
For our example, we'll need an app with a
|
|
40
|
+
For our example, we'll need an app with a basic chat interface and an API route calling an LLM, so that we can add Workflow SDK to it. We'll use the [Flight Booking Agent](https://github.com/vercel/workflow-examples/tree/main/flight-booking-app) example as a starting point, which comes with a chat interface built using Next.js, AI SDK, and Shadcn UI.
|
|
41
41
|
|
|
42
42
|
<Steps>
|
|
43
43
|
|
|
44
44
|
<Step>
|
|
45
45
|
### Clone example app
|
|
46
46
|
|
|
47
|
-
We'll need an app with a
|
|
47
|
+
We'll need an app with a basic chat interface and an API route calling an LLM, so that we can add Workflow SDK to it. For the follow-along steps, we'll use the [Flight Booking Agent](https://github.com/vercel/workflow-examples/tree/main/flight-booking-app) example as a starting point, which comes with a chat interface built using Next.js, AI SDK, and Shadcn UI.
|
|
48
48
|
|
|
49
|
-
If you have your own project,
|
|
49
|
+
If you have your own project, skip this step and apply the changes in the following steps to your project.
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
52
|
git clone https://github.com/vercel/workflow-examples -b plain-ai-sdk
|
|
@@ -59,7 +59,7 @@ cd workflow-examples/flight-booking-app
|
|
|
59
59
|
|
|
60
60
|
### Set up API keys
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
To connect to an LLM, set up an API key. You can use Vercel Gateway, which works with all providers at zero markup, or configure a custom provider.
|
|
63
63
|
<Tabs items={['Gateway', 'Custom Provider']}>
|
|
64
64
|
|
|
65
65
|
<Tab value="Gateway">
|
|
@@ -113,15 +113,15 @@ export async function POST(req: Request) {
|
|
|
113
113
|
|
|
114
114
|
### Get familiar with the code
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
Run the app with `npm run dev` and open [http://localhost:3000](http://localhost:3000) in your browser. You should see a basic chat interface to test.
|
|
117
117
|
|
|
118
|
-
The
|
|
118
|
+
The following sections break down the core code. You don't need to make changes yet.
|
|
119
119
|
|
|
120
120
|
<Tabs items={['API Route', 'Tools', 'Client']}>
|
|
121
121
|
|
|
122
122
|
<Tab value="API Route">
|
|
123
123
|
|
|
124
|
-
Our API route
|
|
124
|
+
Our API route calls [AI SDK's `ToolLoopAgent` class](https://ai-sdk.dev/docs/agents/overview), which encapsulates the LLM call, tool execution loop, and stopping conditions on top of [AI SDK's `streamText` function](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text#streamtext). This is also where we pass tools to the agent.
|
|
125
125
|
|
|
126
126
|
```typescript title="app/api/chat/route.ts" lineNumbers
|
|
127
127
|
import { ToolLoopAgent } from "ai";
|
|
@@ -170,7 +170,7 @@ async function searchFlights({ from, to, date }: { from: string; to: string; dat
|
|
|
170
170
|
|
|
171
171
|
<Tab value="Client">
|
|
172
172
|
|
|
173
|
-
Our `ChatPage` component
|
|
173
|
+
Our `ChatPage` component contains logic for displaying chat messages, but its core responsibility is managing input and output for the [`useChat` hook](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat#usechat) from AI SDK.
|
|
174
174
|
|
|
175
175
|
```typescript title="app/chat.tsx" lineNumbers
|
|
176
176
|
"use client";
|
|
@@ -228,7 +228,7 @@ Now that we have a basic agent using AI SDK, we can modify it to make it durable
|
|
|
228
228
|
<Steps>
|
|
229
229
|
<Step>
|
|
230
230
|
|
|
231
|
-
### Install
|
|
231
|
+
### Install dependencies
|
|
232
232
|
|
|
233
233
|
Add the Workflow SDK packages to your project:
|
|
234
234
|
|
|
@@ -253,7 +253,7 @@ export default withWorkflow(nextConfig);
|
|
|
253
253
|
|
|
254
254
|
<Step>
|
|
255
255
|
|
|
256
|
-
### Create a
|
|
256
|
+
### Create a workflow function
|
|
257
257
|
|
|
258
258
|
Move the agent logic into a separate function, which will serve as our workflow definition.
|
|
259
259
|
|
|
@@ -272,7 +272,7 @@ export async function chatWorkflow(messages: UIMessage[]) {
|
|
|
272
272
|
|
|
273
273
|
const agent = new WorkflowAgent({ // [!code highlight]
|
|
274
274
|
|
|
275
|
-
// If using AI Gateway,
|
|
275
|
+
// If using AI Gateway, specify the model name as a string:
|
|
276
276
|
model: "bedrock/claude-4-5-haiku-20251001-v1", // [!code highlight]
|
|
277
277
|
|
|
278
278
|
// ELSE if using a custom provider, pass the provider call as an argument:
|
|
@@ -300,7 +300,7 @@ Key changes:
|
|
|
300
300
|
</Step>
|
|
301
301
|
|
|
302
302
|
<Step>
|
|
303
|
-
### Update the API
|
|
303
|
+
### Update the API route
|
|
304
304
|
|
|
305
305
|
Remove the agent call that we just extracted, and replace it with a call to `start()` to run the workflow:
|
|
306
306
|
|
|
@@ -330,7 +330,7 @@ Key changes:
|
|
|
330
330
|
</Step>
|
|
331
331
|
|
|
332
332
|
<Step>
|
|
333
|
-
### Convert
|
|
333
|
+
### Convert tools to steps
|
|
334
334
|
|
|
335
335
|
Mark all tool definitions with `"use step"` to make them durable. This enables automatic retries and observability for each tool call:
|
|
336
336
|
|
|
@@ -389,7 +389,7 @@ With `"use step"`:
|
|
|
389
389
|
|
|
390
390
|
</Steps>
|
|
391
391
|
|
|
392
|
-
|
|
392
|
+
Your basic AI SDK agent is now durable. Run your development server and send a chat message. The agent should respond as before, with added durability and observability.
|
|
393
393
|
|
|
394
394
|
## Observability
|
|
395
395
|
|
|
@@ -401,7 +401,7 @@ npx workflow web
|
|
|
401
401
|
|
|
402
402
|
This opens a local dashboard showing all workflow runs and their status, as well as a trace viewer to inspect the workflow in detail, including retry attempts, and the data being passed between steps.
|
|
403
403
|
|
|
404
|
-
## Next
|
|
404
|
+
## Next steps
|
|
405
405
|
|
|
406
406
|
Now that you have a basic durable agent, it's a only a short step to add these additional features:
|
|
407
407
|
|
|
@@ -420,11 +420,11 @@ Now that you have a basic durable agent, it's a only a short step to add these a
|
|
|
420
420
|
</Card>
|
|
421
421
|
</Cards>
|
|
422
422
|
|
|
423
|
-
## Complete
|
|
423
|
+
## Complete example
|
|
424
424
|
|
|
425
425
|
A complete example that includes all of the above, plus all of the "next steps" features is available on the main branch of the [Flight Booking Agent](https://github.com/vercel/workflow-examples/tree/main/flight-booking-app) example.
|
|
426
426
|
|
|
427
|
-
## Related
|
|
427
|
+
## Related documentation
|
|
428
428
|
|
|
429
429
|
- [Tools](/docs/ai/defining-tools) - Patterns for defining tools for your agent
|
|
430
430
|
- [`WorkflowAgent`](https://ai-sdk.dev/v7/docs/agents/workflow-agent#workflowagent) - AI SDK API for durable, resumable agents
|
|
@@ -15,7 +15,7 @@ When using [multi-turn workflows](/docs/ai/chat-session-modeling#multi-turn-work
|
|
|
15
15
|
|
|
16
16
|
`WorkflowAgent`'s `prepareStep` callback enables this by running before each step in the agent loop, giving you a chance to inject queued messages into the conversation. `prepareStep` also allows you to modify the model choice and existing messages mid-turn, see AI SDK's [prepareStep callback](https://ai-sdk.dev/docs/agents/loop-control#prepare-step) for more details.
|
|
17
17
|
|
|
18
|
-
## When to
|
|
18
|
+
## When to use this
|
|
19
19
|
|
|
20
20
|
Message queueing is useful when:
|
|
21
21
|
|
|
@@ -24,10 +24,10 @@ Message queueing is useful when:
|
|
|
24
24
|
- You want messages to influence the agent's next step rather than waiting for the current turn to complete
|
|
25
25
|
|
|
26
26
|
<Callout type="info">
|
|
27
|
-
If you
|
|
27
|
+
If you need basic multi-turn conversations where messages arrive between turns, see [Chat Session Modeling](/docs/ai/chat-session-modeling). This guide covers the more advanced case of injecting messages *during* turns.
|
|
28
28
|
</Callout>
|
|
29
29
|
|
|
30
|
-
## The `prepareStep`
|
|
30
|
+
## The `prepareStep` callback
|
|
31
31
|
|
|
32
32
|
The `prepareStep` callback runs before each step in the agent loop. It receives the current state and can modify the messages sent to the model:
|
|
33
33
|
|
|
@@ -47,7 +47,7 @@ interface PrepareStepResult {
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
## Injecting
|
|
50
|
+
## Injecting queued messages
|
|
51
51
|
|
|
52
52
|
Once you have a [multi-turn workflow](/docs/ai/chat-session-modeling#multi-turn-workflows), you can combine a message queue with `prepareStep` to inject messages that arrive during processing:
|
|
53
53
|
|
|
@@ -106,7 +106,7 @@ Messages sent via `chatMessageHook.resume()` accumulate in the queue and get inj
|
|
|
106
106
|
The `prepareStep` callback receives messages in `ModelMessage[]` format (with content arrays), which is the internal format used by the AI SDK.
|
|
107
107
|
</Callout>
|
|
108
108
|
|
|
109
|
-
## Combining with
|
|
109
|
+
## Combining with multi-turn sessions
|
|
110
110
|
|
|
111
111
|
You can also combine message queueing with the standard multi-turn pattern:
|
|
112
112
|
|
|
@@ -169,7 +169,7 @@ export async function chat(initialMessages: ModelMessage[]) {
|
|
|
169
169
|
}
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
## Related
|
|
172
|
+
## Related documentation
|
|
173
173
|
|
|
174
174
|
- [Chat Session Modeling](/docs/ai/chat-session-modeling) - Single-turn vs multi-turn patterns
|
|
175
175
|
- [Building Durable AI Agents](/docs/ai) - Complete guide to creating durable agents
|