workflow 5.0.0-beta.3 → 5.0.0-beta.30

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.
Files changed (176) hide show
  1. package/dist/api-workflow.d.ts +1 -1
  2. package/dist/api-workflow.d.ts.map +1 -1
  3. package/dist/api-workflow.js +1 -1
  4. package/dist/api.d.ts +5 -1
  5. package/dist/api.d.ts.map +1 -1
  6. package/dist/api.js +12 -2
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +6 -1
  10. package/dist/internal/builtins.d.ts +17 -0
  11. package/dist/internal/builtins.d.ts.map +1 -1
  12. package/dist/internal/builtins.js +65 -1
  13. package/dist/observability.d.ts +1 -1
  14. package/dist/observability.js +2 -2
  15. package/dist/runtime.d.ts +2 -1
  16. package/dist/runtime.d.ts.map +1 -1
  17. package/dist/runtime.js +5 -2
  18. package/docs/ai/chat-session-modeling.mdx +7 -3
  19. package/docs/ai/defining-tools.mdx +2 -2
  20. package/docs/ai/index.mdx +28 -23
  21. package/docs/ai/message-queueing.mdx +10 -10
  22. package/docs/ai/resumable-streams.mdx +9 -1
  23. package/docs/api-reference/index.mdx +24 -0
  24. package/docs/api-reference/meta.json +8 -0
  25. package/docs/api-reference/vitest/index.mdx +28 -7
  26. package/docs/api-reference/workflow/create-hook.mdx +38 -0
  27. package/docs/api-reference/workflow/create-webhook.mdx +1 -0
  28. package/docs/api-reference/workflow/experimental-set-attributes.mdx +65 -0
  29. package/docs/api-reference/workflow/fetch.mdx +5 -0
  30. package/docs/api-reference/workflow/index.mdx +3 -0
  31. package/docs/api-reference/workflow-ai/durable-agent.mdx +7 -45
  32. package/docs/api-reference/workflow-ai/index.mdx +2 -2
  33. package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +44 -1
  34. package/docs/api-reference/workflow-api/get-hook-by-token.mdx +7 -0
  35. package/docs/api-reference/workflow-api/get-run.mdx +25 -0
  36. package/docs/api-reference/workflow-api/index.mdx +6 -8
  37. package/docs/api-reference/workflow-api/resume-hook.mdx +57 -0
  38. package/docs/api-reference/workflow-api/start.mdx +13 -5
  39. package/docs/api-reference/workflow-astro/index.mdx +18 -0
  40. package/docs/api-reference/workflow-astro/meta.json +4 -0
  41. package/docs/api-reference/workflow-astro/workflow.mdx +45 -0
  42. package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +60 -0
  43. package/docs/api-reference/workflow-errors/index.mdx +85 -0
  44. package/docs/api-reference/workflow-errors/meta.json +5 -0
  45. package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
  46. package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
  47. package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +16 -6
  48. package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
  49. package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
  50. package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +33 -0
  51. package/docs/api-reference/workflow-nest/index.mdx +31 -0
  52. package/docs/api-reference/workflow-nest/meta.json +9 -0
  53. package/docs/api-reference/workflow-nest/nest-local-builder.mdx +64 -0
  54. package/docs/api-reference/workflow-nest/workflow-controller.mdx +40 -0
  55. package/docs/api-reference/workflow-nest/workflow-module.mdx +74 -0
  56. package/docs/api-reference/workflow-next/with-workflow.mdx +56 -2
  57. package/docs/api-reference/workflow-nitro/index.mdx +60 -0
  58. package/docs/api-reference/workflow-nuxt/index.mdx +48 -0
  59. package/docs/api-reference/workflow-observability/hydrate-data.mdx +35 -0
  60. package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +62 -0
  61. package/docs/api-reference/workflow-observability/index.mdx +64 -0
  62. package/docs/api-reference/workflow-observability/meta.json +11 -0
  63. package/docs/api-reference/workflow-observability/observability-revivers.mdx +50 -0
  64. package/docs/api-reference/workflow-observability/parse-class-name.mdx +41 -0
  65. package/docs/api-reference/workflow-observability/parse-step-name.mdx +40 -0
  66. package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +55 -0
  67. package/docs/api-reference/workflow-runtime/create-world.mdx +39 -0
  68. package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +44 -0
  69. package/docs/api-reference/{workflow-api → workflow-runtime}/get-world.mdx +7 -10
  70. package/docs/api-reference/workflow-runtime/health-check.mdx +50 -0
  71. package/docs/api-reference/workflow-runtime/index.mdx +43 -0
  72. package/docs/api-reference/workflow-runtime/meta.json +12 -0
  73. package/docs/api-reference/workflow-runtime/set-world.mdx +49 -0
  74. package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +42 -0
  75. package/docs/api-reference/{workflow-api → workflow-runtime}/world/index.mdx +5 -8
  76. package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
  77. package/docs/api-reference/{workflow-api → workflow-runtime}/world/queue.mdx +4 -2
  78. package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +11 -4
  79. package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +2 -2
  80. package/docs/api-reference/workflow-serde/index.mdx +0 -1
  81. package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +1 -2
  82. package/docs/api-reference/workflow-serde/workflow-serialize.mdx +1 -2
  83. package/docs/api-reference/workflow-sveltekit/index.mdx +18 -0
  84. package/docs/api-reference/workflow-sveltekit/meta.json +4 -0
  85. package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +42 -0
  86. package/docs/api-reference/workflow-vite/index.mdx +18 -0
  87. package/docs/api-reference/workflow-vite/meta.json +4 -0
  88. package/docs/api-reference/workflow-vite/workflow.mdx +48 -0
  89. package/docs/changelog/attributes-mvp.mdx +380 -0
  90. package/docs/changelog/eager-processing.mdx +269 -0
  91. package/docs/changelog/index.mdx +2 -1
  92. package/docs/changelog/lazy-event-creation.md +127 -0
  93. package/docs/changelog/meta.json +8 -1
  94. package/docs/changelog/resilient-start.mdx +31 -283
  95. package/docs/changelog/step-message-ownership.mdx +360 -0
  96. package/docs/changelog/turbo-mode.md +87 -0
  97. package/docs/configuration/build-and-diagnostics.mdx +51 -0
  98. package/docs/configuration/cli-and-web-ui.mdx +154 -0
  99. package/docs/configuration/framework-options.mdx +165 -0
  100. package/docs/configuration/index.mdx +32 -0
  101. package/docs/configuration/meta.json +12 -0
  102. package/docs/configuration/runtime-tuning.mdx +156 -0
  103. package/docs/configuration/worlds.mdx +228 -0
  104. package/docs/cookbook/advanced/child-workflows.mdx +199 -256
  105. package/docs/cookbook/advanced/meta.json +1 -1
  106. package/docs/cookbook/advanced/publishing-libraries.mdx +13 -12
  107. package/docs/cookbook/advanced/serializable-steps.mdx +8 -4
  108. package/docs/cookbook/advanced/upgrading-workflows.mdx +195 -0
  109. package/docs/cookbook/agent-patterns/agent-cancellation.mdx +31 -76
  110. package/docs/cookbook/agent-patterns/durable-agent.mdx +11 -143
  111. package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +7 -3
  112. package/docs/cookbook/common-patterns/idempotency.mdx +36 -52
  113. package/docs/cookbook/common-patterns/rate-limiting.mdx +1 -1
  114. package/docs/cookbook/common-patterns/saga.mdx +2 -2
  115. package/docs/cookbook/common-patterns/scheduling.mdx +8 -0
  116. package/docs/cookbook/common-patterns/timeouts.mdx +2 -1
  117. package/docs/cookbook/common-patterns/workflow-composition.mdx +15 -16
  118. package/docs/cookbook/index.mdx +3 -3
  119. package/docs/cookbook/integrations/ai-sdk.mdx +60 -28
  120. package/docs/cookbook/integrations/chat-sdk.mdx +13 -0
  121. package/docs/cookbook/integrations/sandbox.mdx +13 -0
  122. package/docs/deploying/building-a-world.mdx +1 -1
  123. package/docs/deploying/index.mdx +1 -0
  124. package/docs/deploying/world/local-world.mdx +23 -6
  125. package/docs/deploying/world/postgres-world.mdx +79 -13
  126. package/docs/deploying/world/vercel-world.mdx +47 -12
  127. package/docs/errors/abort-signal-timeout-in-workflow.mdx +80 -0
  128. package/docs/errors/corrupted-event-log.mdx +5 -5
  129. package/docs/errors/hook-conflict.mdx +56 -4
  130. package/docs/errors/index.mdx +1 -35
  131. package/docs/errors/replay-divergence.mdx +27 -0
  132. package/docs/errors/runtime-decryption-failed.mdx +77 -0
  133. package/docs/errors/step-executed-multiple-times.mdx +23 -0
  134. package/docs/errors/step-not-registered.mdx +1 -1
  135. package/docs/foundations/cancellation.mdx +459 -0
  136. package/docs/foundations/errors-and-retries.mdx +7 -3
  137. package/docs/foundations/hooks.mdx +29 -0
  138. package/docs/foundations/idempotency.mdx +236 -11
  139. package/docs/foundations/index.mdx +1 -23
  140. package/docs/foundations/meta.json +3 -1
  141. package/docs/foundations/serialization.mdx +77 -41
  142. package/docs/foundations/starting-workflows.mdx +5 -1
  143. package/docs/foundations/streaming.mdx +14 -23
  144. package/docs/foundations/versioning.mdx +263 -0
  145. package/docs/getting-started/astro.mdx +6 -0
  146. package/docs/getting-started/index.mdx +6 -7
  147. package/docs/getting-started/meta.json +1 -0
  148. package/docs/getting-started/nestjs.mdx +9 -0
  149. package/docs/getting-started/next.mdx +5 -3
  150. package/docs/getting-started/nitro.mdx +22 -0
  151. package/docs/getting-started/sveltekit.mdx +6 -0
  152. package/docs/getting-started/tanstack-start.mdx +241 -0
  153. package/docs/how-it-works/cancellation.mdx +287 -0
  154. package/docs/how-it-works/code-transform.mdx +2 -2
  155. package/docs/how-it-works/encryption.mdx +2 -2
  156. package/docs/how-it-works/event-sourcing.mdx +2 -2
  157. package/docs/how-it-works/meta.json +2 -1
  158. package/docs/internal/index.mdx +21 -0
  159. package/docs/internal/meta.json +10 -0
  160. package/docs/internal/nitro-native-build.mdx +38 -0
  161. package/docs/internal/nitro-web-ui.mdx +24 -0
  162. package/docs/internal/serializable-abort-controller.mdx +148 -0
  163. package/docs/meta.json +1 -1
  164. package/docs/migration-guides/migrating-from-aws-step-functions.mdx +7 -12
  165. package/docs/migration-guides/migrating-from-inngest.mdx +7 -17
  166. package/docs/migration-guides/migrating-from-temporal.mdx +6 -11
  167. package/docs/migration-guides/migrating-from-trigger-dev.mdx +8 -17
  168. package/docs/observability/attributes.mdx +87 -0
  169. package/docs/observability/index.mdx +18 -1
  170. package/docs/observability/meta.json +1 -1
  171. package/docs/observability/tracing.mdx +119 -0
  172. package/docs/testing/index.mdx +2 -2
  173. package/package.json +14 -13
  174. package/docs/api-reference/workflow-api/world/meta.json +0 -4
  175. package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
  176. package/docs/cookbook/advanced/distributed-abort-controller.mdx +0 -318
@@ -38,7 +38,7 @@ Learn more in the [Observability](/docs/observability) documentation.
38
38
 
39
39
  ## Configuration
40
40
 
41
- The local world works with zero configuration, but you can customize behavior through environment variables or programmatically via `createLocalWorld()`.
41
+ The local world works with zero configuration, but you can customize behavior through environment variables or programmatically via `createWorld()`.
42
42
 
43
43
  ### `WORKFLOW_LOCAL_DATA_DIR`
44
44
 
@@ -56,22 +56,39 @@ Port resolution priority: `baseUrl` > `port` > `PORT` > auto-detected
56
56
 
57
57
  ### `WORKFLOW_LOCAL_QUEUE_CONCURRENCY`
58
58
 
59
- Maximum number of concurrent queue workers. Default: `100`
59
+ Maximum number of concurrent queue message handlers. Default: `1000`
60
+
61
+ ### `WORKFLOW_LOCAL_QUEUE_MAX_VISIBILITY`
62
+
63
+ Maximum number of seconds a local queue message can stay hidden before the handler rechecks the run. Default: unlimited.
64
+
65
+ ### `WORKFLOW_STREAM_FLUSH_INTERVAL_MS`
66
+
67
+ Flush interval, in milliseconds, for buffered stream writes. Default: `10`.
60
68
 
61
69
  ### Programmatic configuration
62
70
 
63
- {/* @skip-typecheck: incomplete code sample */}
64
- ```typescript title="workflow.config.ts" lineNumbers
65
- import { createLocalWorld } from "@workflow/world-local";
71
+ Options passed to `createWorld()` take precedence over the environment variables above. Export the configured World from a module and point `WORKFLOW_TARGET_WORLD` at that module:
66
72
 
67
- const world = createLocalWorld({
73
+ ```typescript title="my-world.ts" lineNumbers
74
+ import { createWorld } from "@workflow/world-local";
75
+
76
+ export default createWorld({
68
77
  dataDir: "./custom-workflow-data",
69
78
  port: 5173,
70
79
  // baseUrl overrides port if set
71
80
  baseUrl: "https://local.example.com:3000",
81
+ recoverActiveRuns: true,
82
+ streamFlushIntervalMs: 10, // overrides WORKFLOW_STREAM_FLUSH_INTERVAL_MS
72
83
  });
73
84
  ```
74
85
 
86
+ ```bash title=".env"
87
+ WORKFLOW_TARGET_WORLD="./my-world.ts"
88
+ ```
89
+
90
+ `createWorld()` also accepts `tag`, which scopes local storage files to a suffix. It is mainly used by test harnesses that share one `.workflow-data` directory.
91
+
75
92
  ## Limitations
76
93
 
77
94
  The local world is designed for development, not production:
@@ -22,6 +22,15 @@ Install the Postgres World package in your workflow project:
22
22
  @workflow/world-postgres
23
23
  ```
24
24
 
25
+ <Callout type="info">
26
+ Use the same release channel for `workflow` and `@workflow/world-postgres`. If
27
+ your app uses a beta or other prerelease Workflow version, install the matching
28
+ prerelease Postgres World package, such as
29
+ `npm install @workflow/world-postgres@beta`. Mismatched versions fail before
30
+ starting a run with an error that says the runtime requires a World with a
31
+ matching spec version.
32
+ </Callout>
33
+
25
34
  Configure the required environment variables to use the world and point it to your PostgreSQL database:
26
35
 
27
36
  ```bash title=".env"
@@ -29,12 +38,44 @@ WORKFLOW_TARGET_WORLD="@workflow/world-postgres"
29
38
  WORKFLOW_POSTGRES_URL="postgres://user:password@host:5432/database"
30
39
  ```
31
40
 
32
- Run the migration script to create the necessary tables in your database. Ensure `WORKFLOW_POSTGRES_URL` is set when running this command:
41
+ Run the migration script to create the necessary tables in your database. Ensure `WORKFLOW_POSTGRES_URL` or `DATABASE_URL` is set when running this command:
42
+
43
+ <Tabs items={["npm", "pnpm", "Yarn", "Bun"]}>
44
+
45
+ <Tab value="npm">
46
+
47
+ ```bash
48
+ npx --package=@workflow/world-postgres bootstrap
49
+ ```
50
+
51
+ </Tab>
52
+
53
+ <Tab value="pnpm">
54
+
55
+ ```bash
56
+ pnpm dlx --package @workflow/world-postgres bootstrap
57
+ ```
58
+
59
+ </Tab>
60
+
61
+ <Tab value="Yarn">
33
62
 
34
63
  ```bash
35
- npx workflow-postgres-setup
64
+ yarn dlx --package @workflow/world-postgres bootstrap
36
65
  ```
37
66
 
67
+ </Tab>
68
+
69
+ <Tab value="Bun">
70
+
71
+ ```bash
72
+ bunx --package @workflow/world-postgres bootstrap
73
+ ```
74
+
75
+ </Tab>
76
+
77
+ </Tabs>
78
+
38
79
  <Callout type="info">
39
80
  The migration is idempotent and can safely be run as a post-deployment lifecycle script.
40
81
  </Callout>
@@ -149,41 +190,66 @@ Learn more in the [Observability](/docs/observability) documentation.
149
190
 
150
191
  All configuration options can be set via environment variables or programmatically via `createWorld()`.
151
192
 
152
- ### `WORKFLOW_POSTGRES_URL` (required)
193
+ ### `WORKFLOW_POSTGRES_URL`
194
+
195
+ PostgreSQL connection string used by the runtime World.
153
196
 
154
- PostgreSQL connection string. Falls back to `DATABASE_URL` if not set.
197
+ Precedence: `WORKFLOW_POSTGRES_URL` > `DATABASE_URL` > `postgres://world:world@localhost:5432/world`
155
198
 
156
- Default: `postgres://world:world@localhost:5432/world`
199
+ The `bootstrap` migration command uses the same precedence.
157
200
 
158
201
  ### `WORKFLOW_POSTGRES_JOB_PREFIX`
159
202
 
160
- Prefix for graphile-worker queue job names. Useful when sharing a database between multiple applications.
203
+ Prefix for graphile-worker queue job names. Useful when sharing a database between multiple applications. Default: `workflow_`
161
204
 
162
205
  ### `WORKFLOW_POSTGRES_WORKER_CONCURRENCY`
163
206
 
164
- Number of concurrent workers polling for jobs. Default: `10`
207
+ Number of concurrent workers polling for jobs. Default: `50`.
208
+
209
+ This value also bounds how many parent→child workflow polls can be in flight simultaneously. Every `await childRun.returnValue` inside a workflow holds a worker slot until the child run terminates — if you expect recursive or highly-fanned-out parent/child workflows, raise this ceiling above the peak number of concurrent polls. With the default of 50, the included `fibonacciWorkflow` e2e test (fib(6), ~24 concurrent polls at peak) passes; deeper recursion or larger fanouts need a correspondingly larger setting.
165
210
 
166
211
  ### `WORKFLOW_POSTGRES_MAX_POOL_SIZE`
167
212
 
168
- Maximum size of the internal `pg.Pool` used when `createWorld()` constructs the pool. Default: `10`
213
+ Maximum size of the internal `pg.Pool` used when `createWorld()` constructs the pool. Default: the `pg` default (`10`).
169
214
 
170
215
  For higher worker concurrency, Graphile Worker recommends setting `maxPoolSize` to `10` or `queueConcurrency + 2`, whichever is larger.
171
216
 
217
+ ### `WORKFLOW_QUEUE_NAMESPACE`
218
+
219
+ Queue topic namespace shared by build output and the Postgres World. Default: unset.
220
+
221
+ For example, `custom` changes the queue topic prefixes from `__wkf_workflow_` and `__wkf_step_` to `__custom_wkf_workflow_` and `__custom_wkf_step_`. The value must be lowercase alphanumeric and start with a letter.
222
+
223
+ ### `WORKFLOW_STREAM_FLUSH_INTERVAL_MS`
224
+
225
+ Flush interval, in milliseconds, for buffered stream writes. Default: `10`.
226
+
172
227
  ### Programmatic configuration
173
228
 
174
229
  {/*@skip-typecheck: incomplete code sample*/}
175
230
 
176
- ```typescript title="workflow.config.ts" lineNumbers
231
+ ```typescript title="my-world.ts" lineNumbers
177
232
  import { createWorld } from "@workflow/world-postgres";
178
233
 
179
- const world = createWorld({
180
- connectionString: "postgres://user:password@host:5432/database",
234
+ export default createWorld({
235
+ connectionString:
236
+ process.env.WORKFLOW_POSTGRES_URL ?? process.env.DATABASE_URL!,
181
237
  jobPrefix: "myapp_",
182
- queueConcurrency: 20,
183
- maxPoolSize: 20, // overrides WORKFLOW_POSTGRES_MAX_POOL_SIZE
238
+ namespace: "myapp",
239
+ queueConcurrency: 50,
240
+ maxPoolSize: 52, // overrides WORKFLOW_POSTGRES_MAX_POOL_SIZE
241
+ streamFlushIntervalMs: 10,
184
242
  });
185
243
  ```
186
244
 
245
+ Options passed to `createWorld()` take precedence over the environment variables above. Export the World from a module and point `WORKFLOW_TARGET_WORLD` at that module:
246
+
247
+ You can also pass an existing `pg.Pool` as `pool` instead of a connection string.
248
+
249
+ ```bash title=".env"
250
+ WORKFLOW_TARGET_WORLD="./my-world.ts"
251
+ ```
252
+
187
253
  ## How It Works
188
254
 
189
255
  The Postgres World uses PostgreSQL as a durable backend:
@@ -89,46 +89,79 @@ Learn more in the [Observability](/docs/observability) documentation.
89
89
 
90
90
  ## Configuration
91
91
 
92
- The Vercel World requires no configuration when deployed to Vercel. For advanced use cases, you can override settings programmatically via `createVercelWorld()`.
92
+ In a Vercel deployment, you do not configure the Vercel World yourself. The platform injects everything the runtime needs, including `VERCEL_DEPLOYMENT_ID`, `VERCEL_PROJECT_ID`, per-request OIDC tokens, and `VERCEL_DEPLOYMENT_KEY` for encryption.
93
+
94
+ Do not set those platform-provided values yourself.
95
+
96
+ Most users never need to set the `WORKFLOW_VERCEL_*` variables below. They are only overrides for tools running outside Vercel, such as your laptop or CI, when those tools need to inspect or test a remote Vercel Workflow project and cannot infer the project, team, token, or target environment automatically.
97
+
98
+ For example, you might set them when running `workflow inspect runs --backend vercel` from CI without an interactive `vercel login`, or when running tests against a specific preview deployment. In normal local development, the CLI infers these values from your `.vercel` directory and Vercel CLI login. In a deployed Vercel function, these variables have no effect on runtime configuration, and the runtime warns if they are set there.
93
99
 
94
100
  ### `WORKFLOW_VERCEL_ENV`
95
101
 
96
- The Vercel environment to use. Options: `production`, `preview`, `development`. Automatically detected.
102
+ The Vercel environment to target. Options: `production`, `preview`. Default: `production`.
97
103
 
98
104
  ### `WORKFLOW_VERCEL_AUTH_TOKEN`
99
105
 
100
- Authentication token for API requests. Automatically detected.
106
+ Vercel API authentication token (secret keep it in your environment, not in code). Falls back to `VERCEL_TOKEN`, then to your Vercel CLI login.
101
107
 
102
108
  ### `WORKFLOW_VERCEL_PROJECT`
103
109
 
104
- Vercel project ID for API requests. Automatically detected.
110
+ Vercel project ID (`prj_...`).
111
+
112
+ ### `WORKFLOW_VERCEL_PROJECT_NAME`
113
+
114
+ Vercel project name/slug, used for dashboard links.
105
115
 
106
116
  ### `WORKFLOW_VERCEL_TEAM`
107
117
 
108
- Vercel team ID for API requests. Automatically detected.
118
+ Vercel team ID.
109
119
 
110
120
  ### `WORKFLOW_VERCEL_BACKEND_URL`
111
121
 
112
- Custom base URL for the Vercel workflow API. Automatically detected.
122
+ Custom base URL for the Vercel workflow API proxy. Default: `https://api.vercel.com/v1/workflow`.
123
+
124
+ ### `VERCEL_WORKFLOW_SERVER_URL`
125
+
126
+ Custom workflow-server URL for direct runtime requests, or for the proxy to forward to via `WORKFLOW_VERCEL_BACKEND_URL`. Default: unset; normal deployments should not need this.
127
+
128
+ ### `VERCEL_QUEUE_MAX_DELAY_SECONDS`
129
+
130
+ Maximum delay, in seconds, that Workflow uses for one Vercel Queues continuation message when implementing `sleep()`. If a workflow sleeps longer than this, the runtime schedules another continuation message when the first one fires, repeating until the sleep's target time is reached. Default: `82800` (23 hours).
131
+
132
+ Vercel Queues can delay messages for up to 7 days, capped by the message TTL. Because the default TTL is 24 hours, Workflow uses a 23-hour continuation hop to stay safely inside that default.
133
+
134
+ ### `WORKFLOW_REQUEST_TIMEOUT_MS`
135
+
136
+ Per-request timeout, in milliseconds, for Vercel World HTTP calls to workflow-server. Default: `60000`. Minimum: `1`.
137
+
138
+ ### `WORKFLOW_MAX_CHUNKS_PER_REQUEST`
139
+
140
+ Maximum stream chunks written in one Vercel World request. Larger batches are split across multiple requests. Default: `1000`. Minimum: `1`.
113
141
 
114
142
  ### Programmatic configuration
115
143
 
144
+ `createWorld()` accepts explicit API configuration. It does not read `WORKFLOW_VERCEL_*` automatically, so pass the environment values yourself when you want a configured World module:
145
+
116
146
  {/*@skip-typecheck: incomplete code sample*/}
117
147
 
118
- ```typescript title="workflow.config.ts" lineNumbers
119
- import { createVercelWorld } from "@workflow/world-vercel";
148
+ ```typescript title="my-world.ts" lineNumbers
149
+ import { createWorld } from "@workflow/world-vercel";
120
150
 
121
- const world = createVercelWorld({
151
+ export default createWorld({
122
152
  token: process.env.WORKFLOW_VERCEL_AUTH_TOKEN,
123
- baseUrl: "https://api.vercel.com/v1/workflow",
124
153
  projectConfig: {
125
- projectId: "my-project",
126
- teamId: "my-team",
154
+ projectId: "prj_...",
155
+ teamId: "team_...",
127
156
  environment: "production",
128
157
  },
129
158
  });
130
159
  ```
131
160
 
161
+ ```bash title=".env"
162
+ WORKFLOW_TARGET_WORLD="./my-world.ts"
163
+ ```
164
+
132
165
  ## Versioning
133
166
 
134
167
  On Vercel, workflow runs are pegged to the deployment that started them. This means:
@@ -139,6 +172,8 @@ On Vercel, workflow runs are pegged to the deployment that started them. This me
139
172
 
140
173
  This ensures long-running workflows complete reliably without being affected by subsequent deployments.
141
174
 
175
+ For the full model, including rerunning on latest and explicit upgrade boundaries, see [Versioning](/docs/foundations/versioning).
176
+
142
177
  ## Security
143
178
 
144
179
  ### Consumer function security
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: abort-signal-timeout-in-workflow
3
+ description: AbortSignal.timeout() cannot be used inside workflow functions because it relies on real timers which break deterministic replay.
4
+ type: troubleshooting
5
+ summary: Use sleep() with AbortController instead of AbortSignal.timeout() in workflow functions.
6
+ prerequisites:
7
+ - /docs/foundations/workflows-and-steps
8
+ related:
9
+ - /docs/foundations/cancellation
10
+ - /docs/api-reference/workflow/sleep
11
+ - /docs/errors/timeout-in-workflow
12
+ ---
13
+
14
+ ## Error
15
+
16
+ ```
17
+ AbortSignal.timeout() is not supported in workflow functions.
18
+ Use sleep() with an AbortController instead.
19
+ ```
20
+
21
+ ## Why This Happens
22
+
23
+ `AbortSignal.timeout()` creates a signal that aborts after a real-time delay using an internal timer. Workflow functions must be [deterministic](/docs/foundations/workflows-and-steps) to support replay — they run the same code multiple times during the workflow's lifecycle, using the [event log](/docs/how-it-works/event-sourcing) to resume execution to the correct point.
24
+
25
+ Real-time timers break this determinism because:
26
+ - On the first execution, the timer might fire after 10 seconds
27
+ - On replay, the timer would fire again, but the event log may have already advanced past that point
28
+ - The timer's behavior depends on wall-clock time, which varies between executions
29
+
30
+ ## How to Fix
31
+
32
+ Use [`sleep()`](/docs/api-reference/workflow/sleep) with an `AbortController` to create a deterministic timeout that cancels in-flight work:
33
+
34
+ **Before (incorrect):**
35
+
36
+ {/* @skip-typecheck: intentionally incorrect example */}
37
+ ```typescript lineNumbers
38
+ export async function workflow() {
39
+ "use workflow";
40
+
41
+ // This will throw an error
42
+ const signal = AbortSignal.timeout(10_000); // [!code highlight]
43
+ const result = await fetchData(signal);
44
+ return result;
45
+ }
46
+ ```
47
+
48
+ **After (correct):**
49
+
50
+ ```typescript lineNumbers
51
+ import { sleep } from "workflow";
52
+
53
+ export async function workflow() {
54
+ "use workflow";
55
+
56
+ const controller = new AbortController(); // [!code highlight]
57
+ void sleep("10s").then(() => controller.abort()); // [!code highlight]
58
+
59
+ return await fetchData(controller.signal);
60
+ }
61
+
62
+ async function fetchData(signal: AbortSignal) {
63
+ "use step";
64
+ const response = await fetch("https://api.example.com/data", { signal });
65
+ return response.json();
66
+ }
67
+ ```
68
+
69
+ The `sleep()` + `AbortController` pattern is the durable equivalent of `AbortSignal.timeout()`. The sleep is recorded in the event log, so it replays deterministically. If `fetchData` finishes within 10 seconds you get the response; if not, the timer fires `controller.abort()`, `fetch` rejects with an `AbortError`, and the step's failure propagates to the workflow as a `FatalError` (no retries — abort is intentional cancellation).
70
+
71
+ <Callout type="info">
72
+ `AbortSignal.timeout()` works normally inside step functions, since steps have full Node.js runtime access and are not replayed.
73
+ </Callout>
74
+
75
+ ## Related
76
+
77
+ - [Cancellation](/docs/foundations/cancellation) — Patterns for cancelling in-flight work
78
+ - [`sleep()` API Reference](/docs/api-reference/workflow/sleep) — Durable sleep primitive
79
+ - [Workflows and Steps](/docs/foundations/workflows-and-steps) — Why workflow functions must be deterministic
80
+ - [`setTimeout` in Workflow](/docs/errors/timeout-in-workflow) — Similar restriction on `setTimeout`
@@ -9,21 +9,21 @@ related:
9
9
  - /docs/foundations/errors-and-retries
10
10
  ---
11
11
 
12
- This error occurs when the Workflow runtime encounters an event in the event log that no registered consumer can process. This means the event log is in an invalid state typically due to duplicate or orphaned events.
12
+ This error occurs when the Workflow runtime repeatedly cannot replay events in the event log. This usually means the event log is in an invalid state, such as duplicate or orphaned events, or that a runtime determinism bug persists across retry attempts.
13
13
 
14
- This is a **workflow-level fatal error**. It cannot be caught or handled inside your workflow code. A corrupted event log immediately fails the entire run without executing any more user code. The run must be retried from outside the workflow.
14
+ This is a **workflow-level fatal error**. It cannot be caught or handled inside your workflow code. The runtime first retries transient replay divergence automatically; it marks the run as failed with this error only after replay still cannot recover.
15
15
 
16
16
  ## Error Message
17
17
 
18
18
  ```
19
- Unconsumed event in event log: eventType=<type>, correlationId=<id>, eventId=<id>. This indicates a corrupted or invalid event log.
19
+ Workflow replay diverged <divergenceCount> times after <maxRecoveryReplays> recovery replays; latest divergent event was <eventId>. Last divergence: <details>
20
20
  ```
21
21
 
22
22
  ## Why This Happens
23
23
 
24
24
  Workflows persist their progress as an ordered event log. During replay, the runtime processes each event in sequence — every event must be consumed by a matching callback (e.g., a step or sleep waiting for its result). When an event has no matching consumer, the runtime cannot advance past it, which would block all subsequent events and hang the workflow indefinitely.
25
25
 
26
- Instead of silently hanging, the runtime raises a `WorkflowRuntimeError` to fail the workflow fast and surface the problem.
26
+ Instead of silently hanging, the runtime retries a divergent replay before failing the workflow and surfacing this terminal error.
27
27
 
28
28
  Common scenarios that produce this error:
29
29
 
@@ -45,7 +45,7 @@ npm install workflow@latest
45
45
 
46
46
  ### 2. Retry the failed run
47
47
 
48
- Since this is a fatal error, the run is automatically marked as `failed`. You can re-run it using the **Re-run** button in the Workflow Dashboard.
48
+ If this error is displayed, automatic replay recovery has already been exhausted and the run has been marked as `failed`. You can re-run it using the **Re-run** button in the Workflow Dashboard.
49
49
 
50
50
  ### 3. Report the issue
51
51
 
@@ -73,9 +73,9 @@ export async function processPayment() {
73
73
  }
74
74
  ```
75
75
 
76
- ## Handling Hook Conflicts in Your Workflow
76
+ ## Handling Hook Conflicts
77
77
 
78
- When a hook conflict occurs, awaiting the hook will throw a `HookConflictError`. You can catch this error to handle the conflict gracefully:
78
+ When a hook conflict occurs, awaiting the hook will throw a `HookConflictError`. The error exposes the token that conflicted and, for current worlds, the run ID that currently owns it. `conflictingRunId` remains optional for compatibility with older persisted events and world implementations, so guard it before delegating:
79
79
 
80
80
  ```typescript lineNumbers
81
81
  import { createHook } from "workflow";
@@ -93,14 +93,64 @@ export async function processPayment(orderId: string) {
93
93
  if (HookConflictError.is(error)) { // [!code highlight]
94
94
  // Another workflow is already processing this order
95
95
  console.log(`Conflicting token: ${error.token}`);
96
- return { success: false, reason: "duplicate-processing" };
96
+ if (error.conflictingRunId) {
97
+ console.log(`Active run: ${error.conflictingRunId}`);
98
+ }
99
+ return {
100
+ success: false,
101
+ reason: "duplicate-processing",
102
+ token: error.token,
103
+ runId: error.conflictingRunId
104
+ };
97
105
  }
98
106
  throw error; // Re-throw other errors
99
107
  }
100
108
  }
101
109
  ```
102
110
 
103
- This pattern is useful when you want to detect and handle duplicate processing attempts instead of letting the workflow fail.
111
+ This pattern is useful when you want to detect duplicate processing inside the workflow. Runtime APIs such as `resumeHook()` and `getRun()` must be called outside workflow functions, for example from an API route or in a step.
112
+
113
+ ### Delegate to the Active Run
114
+
115
+ In idempotency flows, a conflict means another active run already owns the hook token. You can return the duplicate-processing payload from the workflow, resume the active hook to deliver the payload to the existing run, then use `getRun(result.runId)` to wait for, stream, or cancel the active run:
116
+
117
+ ```typescript lineNumbers
118
+ import { getRun, resumeHook, start } from "workflow/api";
119
+ import { processPayment } from "@/workflows/process-payment";
120
+
121
+ type ProcessPaymentResult =
122
+ | { success: true; payment: unknown }
123
+ | {
124
+ success: false;
125
+ reason: "duplicate-processing";
126
+ token: string;
127
+ runId?: string;
128
+ };
129
+
130
+ export async function POST(request: Request) {
131
+ const { orderId, payment } = await request.json();
132
+ const run = await start(processPayment, [orderId]);
133
+ const result = (await run.returnValue) as ProcessPaymentResult;
134
+
135
+ if (
136
+ result.success === false &&
137
+ result.reason === "duplicate-processing" &&
138
+ result.runId
139
+ ) {
140
+ await resumeHook(result.token, payment); // [!code highlight]
141
+ const activeRun = getRun(result.runId); // [!code highlight]
142
+
143
+ return Response.json({
144
+ delegatedToRunId: activeRun.runId,
145
+ result: await activeRun.returnValue
146
+ });
147
+ }
148
+
149
+ return Response.json(result);
150
+ }
151
+ ```
152
+
153
+ If the caller needs live output instead of the final result, return `activeRun.getReadable()` from the same branch. If the duplicate request should replace the active work, call `await activeRun.cancel()` after inspecting the run.
104
154
 
105
155
  ## When Hook Tokens Are Released
106
156
 
@@ -122,4 +172,6 @@ After a workflow completes, its hook tokens become available for reuse by other
122
172
  ## Related
123
173
 
124
174
  - [Hooks](/docs/foundations/hooks) - Learn more about using hooks in workflows
175
+ - [getRun](/docs/api-reference/workflow-api/get-run) - Retrieve or control the active run
176
+ - [resumeHook](/docs/api-reference/workflow-api/resume-hook) - Deliver data to the active hook
125
177
  - [createWebhook](/docs/api-reference/workflow/create-webhook) - Alternative for fixed webhook URLs
@@ -9,41 +9,7 @@ related:
9
9
 
10
10
  Fix common mistakes when creating and executing workflows in the **Workflow SDK**.
11
11
 
12
- <Cards>
13
- <Card href="/docs/errors/fetch-in-workflow" title="fetch-in-workflow">
14
- Learn how to use fetch in workflow functions.
15
- </Card>
16
- <Card href="/docs/errors/hook-conflict" title="hook-conflict">
17
- Learn how to handle hook token conflicts between workflows.
18
- </Card>
19
- <Card href="/docs/errors/node-js-module-in-workflow" title="node-js-module-in-workflow">
20
- Learn how to use Node.js modules in workflows.
21
- </Card>
22
- <Card href="/docs/errors/serialization-failed" title="serialization-failed">
23
- Learn how to handle serialization failures in workflows.
24
- </Card>
25
- <Card href="/docs/errors/start-invalid-workflow-function" title="start-invalid-workflow-function">
26
- Learn how to start an invalid workflow function.
27
- </Card>
28
- <Card href="/docs/errors/timeout-in-workflow" title="timeout-in-workflow">
29
- Learn how to handle timing delays in workflow functions.
30
- </Card>
31
- <Card href="/docs/errors/webhook-invalid-respond-with-value" title="webhook-invalid-respond-with-value">
32
- Learn how to use the correct `respondWith` values for webhooks.
33
- </Card>
34
- <Card href="/docs/errors/webhook-response-not-sent" title="webhook-response-not-sent">
35
- Learn how to send responses when using manual webhook response mode.
36
- </Card>
37
- <Card href="/docs/errors/corrupted-event-log" title="corrupted-event-log">
38
- Learn how to handle corrupted or invalid event logs.
39
- </Card>
40
- <Card href="/docs/errors/step-not-registered" title="step-not-registered">
41
- Resolve step not registered errors caused by deployment mismatches.
42
- </Card>
43
- <Card href="/docs/errors/workflow-not-registered" title="workflow-not-registered">
44
- Resolve workflow not registered errors caused by deployment mismatches.
45
- </Card>
46
- </Cards>
12
+ <AutoCards />
47
13
 
48
14
  ## Learn More
49
15
 
@@ -0,0 +1,27 @@
1
+ ---
2
+ title: replay-divergence
3
+ description: A workflow replay temporarily followed a path that did not match its recorded events.
4
+ type: troubleshooting
5
+ summary: Understand automatic recovery when a workflow replay diverges from its event history.
6
+ prerequisites:
7
+ - /docs/foundations/workflows-and-steps
8
+ related:
9
+ - /docs/errors/corrupted-event-log
10
+ - /docs/foundations/errors-and-retries
11
+ ---
12
+
13
+ A replay divergence occurs when one invocation of a workflow cannot consume the durable event history using the promises, hooks, sleeps, or steps it created during replay.
14
+
15
+ This is an SDK/runtime signal, not an error thrown by your workflow code. It is not catchable inside a workflow function.
16
+
17
+ ## Automatic Recovery
18
+
19
+ A single divergent replay does not prove that persisted history is corrupted. For example, asynchronous delivery ordering may cause one invocation to follow the wrong side of a race while another replay can follow the recorded history correctly.
20
+
21
+ The runtime automatically queues another replay when an invocation reports `REPLAY_DIVERGENCE`. No terminal `run_failed` event is written during these recovery attempts.
22
+
23
+ If recovery replays continue to diverge after the retry budget is exhausted, the runtime marks the run as failed with `CORRUPTED_EVENT_LOG` and records the latest divergent event for diagnosis.
24
+
25
+ ## What To Do
26
+
27
+ Most replay divergence signals recover without action. If a run ultimately fails with `CORRUPTED_EVENT_LOG`, update to the latest `workflow` package and report the run ID and error details if the failure persists.
@@ -0,0 +1,77 @@
1
+ ---
2
+ title: runtime-decryption-failed
3
+ description: The SDK's built-in AES-GCM encryption layer failed to encrypt or decrypt a workflow payload.
4
+ type: troubleshooting
5
+ summary: Resolve runtime decryption failures caused by ciphertext corruption, key mismatch, or malformed envelopes.
6
+ prerequisites:
7
+ - /docs/foundations/workflows-and-steps
8
+ related:
9
+ - /docs/foundations/errors-and-retries
10
+ ---
11
+
12
+ This error occurs when the Workflow SDK's built-in AES-GCM encryption layer fails while encrypting or decrypting a workflow payload. The SDK encrypts step inputs, step outputs, hook payloads, and other event-log data with a per-run AES-256 key whenever encryption is configured for the deployment.
13
+
14
+ This is an **internal SDK failure** — your workflow code never invokes the encryption primitives directly. When this surfaces, it means the ciphertext, nonce, or auth tag the SDK tried to verify is not the bytes that were originally produced. The run is failed with the `RUNTIME_ERROR` classification.
15
+
16
+ ## Error Message
17
+
18
+ ```
19
+ AES-256-GCM decryption failed: The operation failed for an operation-specific reason
20
+ ```
21
+
22
+ The underlying cause is a native Web Crypto [`OperationError`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException#operationerror) — most commonly raised by `AESCipherJob.onDone` in Node's `node:internal/crypto/util` module when the GCM authentication tag does not verify.
23
+
24
+ The thrown `RuntimeDecryptionError` carries a small `context` object with diagnostic fields to help triangulate the source:
25
+
26
+ - `operation` — `'encrypt'` or `'decrypt'`
27
+ - `byteLength` — total byte length of the payload at the failure site
28
+ - `formatPrefix` — the first 4 bytes of the input (`'encr'` for a well-formed encrypted envelope, otherwise a hex dump)
29
+
30
+ ## Why This Happens
31
+
32
+ Common causes, in rough order of likelihood:
33
+
34
+ 1. **Ciphertext mutation or truncation in transit.** The encrypted payload reached the SDK with bytes that differ from what storage holds. Possible sources include a truncated HTTP response from a workflow-server ref endpoint, an edge-cache miss returning a partial 200, or a proxy drop during streaming. A truncated body whose first 4 bytes happen to still spell `encr` produces the exact "auth tag mismatch" symptom.
35
+ 2. **Key resolution mismatch.** The key used to decrypt is not the key that was used to encrypt — e.g. the run's `deploymentId` was not threaded through key resolution and the SDK fell back to the wrong deployment's key material.
36
+ 3. **Malformed encrypted envelope.** The envelope is too short to contain the GCM nonce (12 bytes) and auth tag (16 bytes), so decryption is rejected before it begins.
37
+
38
+ ## What To Do
39
+
40
+ This error indicates an SDK or infrastructure problem — not a bug in your workflow code. Your workflow code does not need to change.
41
+
42
+ ### 1. Upgrade to the latest `workflow` package
43
+
44
+ The underlying issue may have already been identified and fixed:
45
+
46
+ ```bash
47
+ npm install workflow@latest
48
+ ```
49
+
50
+ ### 2. Retry the failed run
51
+
52
+ Since this is a fatal error, the run is automatically marked as `failed`. You can re-run it using the **Re-run** button in the Workflow Dashboard.
53
+
54
+ ### 3. Report the issue
55
+
56
+ If the error persists after upgrading, please [open an issue on GitHub](https://github.com/vercel/workflow/issues/new) so we can investigate. Include:
57
+
58
+ - The version of the `workflow` package you are using
59
+ - The run ID(s) of the affected workflow run(s)
60
+ - The full error message, including the `context` fields (`operation`, `byteLength`, `formatPrefix`)
61
+ - Whether the affected workflows make heavy use of large step inputs/outputs (which may indicate the failure is on the lazy-loaded ref read path)
62
+
63
+ ## This Error Cannot Be Caught
64
+
65
+ Like other `WorkflowRuntimeError` subclasses, a runtime decryption failure is **not catchable** inside your workflow function. The runtime cannot safely continue executing user code when an event-log payload can't be verified, so the entire run fails immediately and is marked as `failed`.
66
+
67
+ To handle this programmatically from outside the workflow, check the run status:
68
+
69
+ ```typescript lineNumbers
70
+ import { getRun } from "workflow/api";
71
+
72
+ const run = getRun("wrun_abc123");
73
+ const status = await run.status;
74
+ if (status === "failed") {
75
+ console.error("Run failed");
76
+ }
77
+ ```