workflow 5.0.0-beta.5 → 5.0.0-beta.50

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 (256) hide show
  1. package/README.md +68 -23
  2. package/dist/api-workflow.d.ts +1 -1
  3. package/dist/api-workflow.d.ts.map +1 -1
  4. package/dist/api-workflow.js +1 -1
  5. package/dist/api.d.ts +3 -3
  6. package/dist/api.d.ts.map +1 -1
  7. package/dist/api.js +5 -7
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +6 -1
  11. package/dist/internal/builtins.d.ts +20 -3
  12. package/dist/internal/builtins.d.ts.map +1 -1
  13. package/dist/internal/builtins.js +68 -4
  14. package/dist/internal/errors.d.ts +1 -1
  15. package/dist/internal/errors.d.ts.map +1 -1
  16. package/dist/internal/errors.js +2 -2
  17. package/dist/nest-builder.d.ts +2 -0
  18. package/dist/nest-builder.d.ts.map +1 -0
  19. package/dist/nest-builder.js +2 -0
  20. package/dist/nest-vercel-builder.d.ts +2 -0
  21. package/dist/nest-vercel-builder.d.ts.map +1 -0
  22. package/dist/nest-vercel-builder.js +2 -0
  23. package/dist/observability.d.ts +1 -1
  24. package/dist/observability.js +2 -2
  25. package/dist/runtime.d.ts +2 -1
  26. package/dist/runtime.d.ts.map +1 -1
  27. package/dist/runtime.js +4 -1
  28. package/docs/ai/chat-session-modeling.mdx +29 -26
  29. package/docs/ai/defining-tools.mdx +6 -7
  30. package/docs/ai/human-in-the-loop.mdx +11 -11
  31. package/docs/ai/index.mdx +50 -45
  32. package/docs/ai/message-queueing.mdx +16 -16
  33. package/docs/ai/meta.json +1 -0
  34. package/docs/ai/resumable-streams.mdx +40 -28
  35. package/docs/ai/sleep-and-delays.mdx +10 -10
  36. package/docs/ai/streaming-updates-from-tools.mdx +6 -6
  37. package/docs/api-reference/index.mdx +24 -0
  38. package/docs/api-reference/meta.json +8 -0
  39. package/docs/api-reference/vitest/index.mdx +9 -15
  40. package/docs/api-reference/workflow/create-hook.mdx +89 -10
  41. package/docs/api-reference/workflow/create-webhook.mdx +16 -15
  42. package/docs/api-reference/workflow/define-hook.mdx +35 -33
  43. package/docs/api-reference/workflow/fatal-error.mdx +30 -8
  44. package/docs/api-reference/workflow/fetch.mdx +14 -10
  45. package/docs/api-reference/workflow/get-step-metadata.mdx +2 -2
  46. package/docs/api-reference/workflow/get-workflow-metadata.mdx +3 -3
  47. package/docs/api-reference/workflow/get-writable.mdx +7 -7
  48. package/docs/api-reference/workflow/index.mdx +4 -1
  49. package/docs/api-reference/workflow/retryable-error.mdx +1 -1
  50. package/docs/api-reference/workflow/set-attributes.mdx +61 -0
  51. package/docs/api-reference/workflow/sleep.mdx +4 -4
  52. package/docs/api-reference/workflow-ai/durable-agent.mdx +48 -86
  53. package/docs/api-reference/workflow-ai/index.mdx +3 -3
  54. package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +67 -24
  55. package/docs/api-reference/workflow-api/get-hook-by-token.mdx +26 -12
  56. package/docs/api-reference/workflow-api/get-run.mdx +43 -8
  57. package/docs/api-reference/workflow-api/index.mdx +6 -10
  58. package/docs/api-reference/workflow-api/resume-hook.mdx +73 -12
  59. package/docs/api-reference/workflow-api/resume-webhook.mdx +11 -9
  60. package/docs/api-reference/workflow-api/start.mdx +60 -13
  61. package/docs/api-reference/workflow-astro/index.mdx +18 -0
  62. package/docs/api-reference/workflow-astro/meta.json +4 -0
  63. package/docs/api-reference/workflow-astro/workflow.mdx +45 -0
  64. package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
  65. package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +60 -0
  66. package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
  67. package/docs/api-reference/workflow-errors/index.mdx +88 -0
  68. package/docs/api-reference/workflow-errors/meta.json +6 -0
  69. package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +68 -0
  70. package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
  71. package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
  72. package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +5 -5
  73. package/docs/api-reference/workflow-errors/throttle-error.mdx +2 -2
  74. package/docs/api-reference/workflow-errors/too-early-error.mdx +2 -2
  75. package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
  76. package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +5 -6
  77. package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +6 -6
  78. package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +5 -5
  79. package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
  80. package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
  81. package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
  82. package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
  83. package/docs/api-reference/workflow-globals.mdx +14 -10
  84. package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +33 -0
  85. package/docs/api-reference/workflow-nest/index.mdx +31 -0
  86. package/docs/api-reference/workflow-nest/meta.json +9 -0
  87. package/docs/api-reference/workflow-nest/nest-local-builder.mdx +64 -0
  88. package/docs/api-reference/workflow-nest/workflow-controller.mdx +40 -0
  89. package/docs/api-reference/workflow-nest/workflow-module.mdx +74 -0
  90. package/docs/api-reference/workflow-next/with-workflow.mdx +39 -17
  91. package/docs/api-reference/workflow-nitro/index.mdx +60 -0
  92. package/docs/api-reference/workflow-nuxt/index.mdx +48 -0
  93. package/docs/api-reference/workflow-observability/hydrate-data.mdx +35 -0
  94. package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +62 -0
  95. package/docs/api-reference/workflow-observability/index.mdx +62 -0
  96. package/docs/api-reference/workflow-observability/meta.json +11 -0
  97. package/docs/api-reference/workflow-observability/observability-revivers.mdx +50 -0
  98. package/docs/api-reference/workflow-observability/parse-class-name.mdx +41 -0
  99. package/docs/api-reference/workflow-observability/parse-step-name.mdx +40 -0
  100. package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +55 -0
  101. package/docs/api-reference/workflow-runtime/create-world.mdx +39 -0
  102. package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +44 -0
  103. package/docs/api-reference/{workflow-api → workflow-runtime}/get-world.mdx +11 -14
  104. package/docs/api-reference/workflow-runtime/health-check.mdx +50 -0
  105. package/docs/api-reference/workflow-runtime/index.mdx +41 -0
  106. package/docs/api-reference/workflow-runtime/meta.json +12 -0
  107. package/docs/api-reference/workflow-runtime/set-world.mdx +51 -0
  108. package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +43 -0
  109. package/docs/api-reference/workflow-runtime/world/analytics.mdx +315 -0
  110. package/docs/api-reference/workflow-runtime/world/index.mdx +60 -0
  111. package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
  112. package/docs/api-reference/workflow-runtime/world/queue.mdx +88 -0
  113. package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +98 -34
  114. package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +8 -8
  115. package/docs/api-reference/workflow-serde/index.mdx +1 -2
  116. package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +3 -4
  117. package/docs/api-reference/workflow-serde/workflow-serialize.mdx +8 -8
  118. package/docs/api-reference/workflow-sveltekit/index.mdx +18 -0
  119. package/docs/api-reference/workflow-sveltekit/meta.json +4 -0
  120. package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +42 -0
  121. package/docs/api-reference/workflow-vite/index.mdx +18 -0
  122. package/docs/api-reference/workflow-vite/meta.json +4 -0
  123. package/docs/api-reference/workflow-vite/workflow.mdx +48 -0
  124. package/docs/changelog/attributes-mvp.mdx +380 -0
  125. package/docs/changelog/batched-event-writes.mdx +79 -0
  126. package/docs/changelog/eager-processing.mdx +110 -436
  127. package/docs/changelog/index.mdx +4 -2
  128. package/docs/changelog/lazy-event-creation.md +127 -0
  129. package/docs/changelog/lazy-hook-resume.mdx +78 -0
  130. package/docs/changelog/meta.json +11 -1
  131. package/docs/changelog/resilient-resume.mdx +32 -0
  132. package/docs/changelog/resilient-start.mdx +33 -285
  133. package/docs/changelog/step-message-ownership.mdx +360 -0
  134. package/docs/changelog/turbo-mode.md +87 -0
  135. package/docs/comparisons/index.mdx +66 -0
  136. package/docs/comparisons/meta.json +11 -0
  137. package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +55 -0
  138. package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +111 -0
  139. package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +71 -0
  140. package/docs/comparisons/workflow-sdk-vs-inngest.mdx +102 -0
  141. package/docs/comparisons/workflow-sdk-vs-temporal.mdx +123 -0
  142. package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +104 -0
  143. package/docs/configuration/build-and-diagnostics.mdx +70 -0
  144. package/docs/configuration/cli-and-web-ui.mdx +241 -0
  145. package/docs/configuration/framework-options.mdx +165 -0
  146. package/docs/configuration/index.mdx +32 -0
  147. package/docs/configuration/meta.json +12 -0
  148. package/docs/configuration/runtime-tuning.mdx +376 -0
  149. package/docs/configuration/worlds.mdx +313 -0
  150. package/docs/cookbook/advanced/child-workflows.mdx +211 -264
  151. package/docs/cookbook/advanced/meta.json +6 -1
  152. package/docs/cookbook/advanced/publishing-libraries.mdx +65 -56
  153. package/docs/cookbook/advanced/serializable-steps.mdx +28 -20
  154. package/docs/cookbook/advanced/upgrading-workflows.mdx +199 -0
  155. package/docs/cookbook/agent-patterns/agent-cancellation.mdx +27 -19
  156. package/docs/cookbook/agent-patterns/durable-agent.mdx +14 -142
  157. package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +30 -22
  158. package/docs/cookbook/common-patterns/batching.mdx +18 -14
  159. package/docs/cookbook/common-patterns/idempotency.mdx +41 -53
  160. package/docs/cookbook/common-patterns/rate-limiting.mdx +8 -4
  161. package/docs/cookbook/common-patterns/saga.mdx +23 -19
  162. package/docs/cookbook/common-patterns/scheduling.mdx +34 -22
  163. package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +29 -25
  164. package/docs/cookbook/common-patterns/timeouts.mdx +26 -21
  165. package/docs/cookbook/common-patterns/webhooks.mdx +10 -6
  166. package/docs/cookbook/common-patterns/workflow-composition.mdx +30 -27
  167. package/docs/cookbook/index.mdx +22 -21
  168. package/docs/cookbook/integrations/ai-sdk.mdx +85 -47
  169. package/docs/cookbook/integrations/chat-sdk.mdx +50 -33
  170. package/docs/cookbook/integrations/sandbox.mdx +62 -45
  171. package/docs/deploying.mdx +95 -0
  172. package/docs/errors/abort-signal-timeout-in-workflow.mdx +16 -12
  173. package/docs/errors/corrupted-event-log.mdx +29 -18
  174. package/docs/errors/deployment-mismatch.mdx +71 -0
  175. package/docs/errors/fetch-in-workflow.mdx +11 -7
  176. package/docs/errors/hook-conflict.mdx +69 -13
  177. package/docs/errors/index.mdx +2 -36
  178. package/docs/errors/node-js-module-in-workflow.mdx +9 -5
  179. package/docs/errors/replay-divergence.mdx +27 -0
  180. package/docs/errors/run-expired.mdx +85 -0
  181. package/docs/errors/runtime-decryption-failed.mdx +77 -0
  182. package/docs/errors/serialization-failed.mdx +44 -12
  183. package/docs/errors/start-invalid-workflow-function.mdx +9 -5
  184. package/docs/errors/step-executed-multiple-times.mdx +23 -0
  185. package/docs/errors/step-not-registered.mdx +6 -6
  186. package/docs/errors/timeout-in-workflow.mdx +12 -8
  187. package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
  188. package/docs/errors/webhook-response-not-sent.mdx +20 -16
  189. package/docs/errors/workflow-not-registered.mdx +5 -5
  190. package/docs/foundations/cancellation.mdx +31 -32
  191. package/docs/foundations/errors-and-retries.mdx +42 -11
  192. package/docs/foundations/hooks.mdx +64 -35
  193. package/docs/foundations/idempotency.mdx +244 -12
  194. package/docs/foundations/index.mdx +1 -23
  195. package/docs/foundations/meta.json +2 -1
  196. package/docs/foundations/serialization.mdx +21 -22
  197. package/docs/foundations/starting-workflows.mdx +106 -30
  198. package/docs/foundations/streaming.mdx +107 -59
  199. package/docs/foundations/versioning.mdx +263 -0
  200. package/docs/foundations/workflows-and-steps.mdx +9 -9
  201. package/docs/getting-started/astro.mdx +22 -18
  202. package/docs/getting-started/express.mdx +15 -11
  203. package/docs/getting-started/fastify.mdx +15 -11
  204. package/docs/getting-started/hono.mdx +15 -11
  205. package/docs/getting-started/index.mdx +10 -3
  206. package/docs/getting-started/meta.json +3 -1
  207. package/docs/getting-started/nestjs.mdx +87 -20
  208. package/docs/getting-started/next.mdx +22 -16
  209. package/docs/getting-started/nitro.mdx +22 -18
  210. package/docs/getting-started/nuxt.mdx +15 -11
  211. package/docs/getting-started/python.mdx +135 -40
  212. package/docs/getting-started/react-router/index.mdx +33 -0
  213. package/docs/getting-started/react-router/meta.json +5 -0
  214. package/docs/getting-started/react-router/v7.mdx +237 -0
  215. package/docs/getting-started/react-router/v8.mdx +232 -0
  216. package/docs/getting-started/sveltekit.mdx +20 -16
  217. package/docs/getting-started/tanstack-start.mdx +17 -13
  218. package/docs/getting-started/vite.mdx +15 -11
  219. package/docs/how-it-works/cancellation.mdx +63 -63
  220. package/docs/how-it-works/code-transform.mdx +83 -67
  221. package/docs/how-it-works/encryption.mdx +30 -26
  222. package/docs/how-it-works/event-sourcing.mdx +98 -34
  223. package/docs/how-it-works/framework-integrations.mdx +96 -337
  224. package/docs/how-it-works/understanding-directives.mdx +22 -22
  225. package/docs/internal/index.mdx +6 -4
  226. package/docs/internal/meta.json +6 -1
  227. package/docs/internal/nitro-native-build.mdx +38 -0
  228. package/docs/internal/nitro-web-ui.mdx +24 -0
  229. package/docs/internal/serializable-abort-controller.mdx +7 -7
  230. package/docs/meta.json +3 -2
  231. package/docs/observability/attributes.mdx +134 -0
  232. package/docs/observability/index.mdx +32 -10
  233. package/docs/observability/meta.json +1 -1
  234. package/docs/observability/retention.mdx +93 -0
  235. package/docs/observability/tracing.mdx +124 -0
  236. package/docs/testing/index.mdx +36 -36
  237. package/docs/testing/server-based.mdx +10 -10
  238. package/docs/whats-new.mdx +186 -0
  239. package/package.json +17 -14
  240. package/docs/api-reference/workflow-api/world/index.mdx +0 -58
  241. package/docs/api-reference/workflow-api/world/meta.json +0 -4
  242. package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
  243. package/docs/api-reference/workflow-api/world/queue.mdx +0 -86
  244. package/docs/deploying/building-a-world.mdx +0 -251
  245. package/docs/deploying/index.mdx +0 -95
  246. package/docs/deploying/meta.json +0 -4
  247. package/docs/deploying/world/local-world.mdx +0 -84
  248. package/docs/deploying/world/meta.json +0 -4
  249. package/docs/deploying/world/postgres-world.mdx +0 -224
  250. package/docs/deploying/world/vercel-world.mdx +0 -179
  251. package/docs/migration-guides/index.mdx +0 -34
  252. package/docs/migration-guides/meta.json +0 -9
  253. package/docs/migration-guides/migrating-from-aws-step-functions.mdx +0 -363
  254. package/docs/migration-guides/migrating-from-inngest.mdx +0 -314
  255. package/docs/migration-guides/migrating-from-temporal.mdx +0 -318
  256. package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -337
@@ -21,7 +21,7 @@ This page explores how directives enable this execution model and the design pri
21
21
 
22
22
  To understand how directives work, let's first understand what workflows and steps are in the Workflow SDK.
23
23
 
24
- ## Workflows and Steps Primer
24
+ ## Workflows and steps primer
25
25
 
26
26
  The Workflow SDK has two types of functions:
27
27
 
@@ -58,7 +58,7 @@ export async function onboardUser(userId: string) {
58
58
  **The key insight:** Workflows resume from suspension by replaying their code using cached step results from the [event log](/docs/how-it-works/event-sourcing). When a step like `await fetchUserData(userId)` is called:
59
59
 
60
60
  - **If already executed:** Returns the cached result immediately from the event log
61
- - **If not yet executed:** Suspends the workflow, enqueues the step for background execution, and resumes later with the result
61
+ - **If not yet executed:** Suspends the workflow, executes the step (usually inline in the same invocation, or through the queue when the invocation runs out of inline budget or nears its timeout), then resumes with the result
62
62
 
63
63
  This replay mechanism requires deterministic code. If `Math.random()` weren't seeded, the first execution might return `0.7` (sending the email) but replay might return `0.3` (skipping it), thus breaking resumption. The Workflow SDK sandbox provides seeded `Math.random()` and `Date` to ensure consistent behavior across replays.
64
64
 
@@ -66,9 +66,9 @@ This replay mechanism requires deterministic code. If `Math.random()` weren't se
66
66
  For a deeper dive into workflows and steps, see [Workflows and Steps](/docs/foundations/workflows-and-steps).
67
67
  </Callout>
68
68
 
69
- ## The Core Challenge
69
+ ## The core challenge
70
70
 
71
- This execution model enables powerful durability features - workflows can suspend for days, survive restarts, and resume from any point. However, it also requires a semantic boundary in the code that tells **the compiler, runtime, and developer** that execution semantics have changed.
71
+ This execution model provides durability: workflows can suspend for days, survive restarts, and resume from any point. However, it also requires a semantic boundary in the code that tells **the compiler, runtime, and developer** that execution semantics have changed.
72
72
 
73
73
  The challenge: how do we mark this boundary in a way that:
74
74
 
@@ -77,7 +77,7 @@ The challenge: how do we mark this boundary in a way that:
77
77
  3. Allows static analysis of workflow structure
78
78
  4. Feels natural to JavaScript developers
79
79
 
80
- Let's look at where directives have been used before, and the alternatives we considered:
80
+ Directives have prior uses, and we considered several alternatives:
81
81
 
82
82
  ## Prior art on directives
83
83
 
@@ -104,11 +104,11 @@ The `"use workflow"` directive is also used by the Language Server Plugin shippe
104
104
 
105
105
  But we didn't get here immediately. This took some discovery to arrive at:
106
106
 
107
- ## Alternatives We Explored
107
+ ## Alternatives we explored
108
108
 
109
- Before settling on directives, we prototyped several other approaches. Each had significant limitations that made them unsuitable for production use.
109
+ Before settling on directives, we prototyped several other approaches. Each had limitations that made them unsuitable for production use.
110
110
 
111
- ### Runtime-Only "Suspense" API
111
+ ### Runtime-only "Suspense" API
112
112
 
113
113
  Our first proof of concept used a wrapper-based API without a build step:
114
114
 
@@ -140,7 +140,7 @@ export const myWorkflow = workflow(async () => {
140
140
  });
141
141
  ```
142
142
 
143
- This was verbose and easy to forget. Moreover, if a developer forgot to wrap something innocent like using `Date.now()`, it led to unstable runtime behavior.
143
+ This was verbose and developers could forget it. If a developer forgot to wrap something like `Date.now()`, it led to unstable runtime behavior.
144
144
 
145
145
  For example:
146
146
 
@@ -200,7 +200,7 @@ export const myWorkflow = workflow(async () => {
200
200
  });
201
201
  ```
202
202
 
203
- ### Generator-Based API
203
+ ### Generator-based API
204
204
 
205
205
  We explored using generators for explicit suspension points, inspired by libraries like Effect.ts:
206
206
 
@@ -220,7 +220,7 @@ We're big fans of [Effect.ts](https://effect.website/) and the power of generato
220
220
 
221
221
  **1. Syntax felt more like a DSL than JavaScript**
222
222
 
223
- Generators require a custom mental model that differs significantly from familiar async/await patterns. The `yield*` syntax and generator delegation were unfamiliar to many developers:
223
+ Generators require a custom mental model that differs from familiar async/await patterns. The `yield*` syntax and generator delegation were unfamiliar to many developers:
224
224
 
225
225
  {/* @skip-typecheck: incomplete code sample */}
226
226
  ```typescript lineNumbers
@@ -267,7 +267,7 @@ export const myWorkflow = workflow(function*() {
267
267
 
268
268
  The generator syntax addressed suspension but didn't solve the fundamental sandboxing problem.
269
269
 
270
- ### File System-Based Conventions
270
+ ### File system-based conventions
271
271
 
272
272
  We explored using file system conventions to identify workflows and steps, similar to how modern frameworks handle routing (Next.js, Hono, Nitro, SvelteKit):
273
273
 
@@ -282,7 +282,7 @@ We explored using file system conventions to identify workflows and steps, simil
282
282
  </Folder>
283
283
  </Files>
284
284
 
285
- With this approach, any function in the `workflows/` directory would be transformed as a workflow, and any function in `steps/` would be a step. No directives needed, just file locations.
285
+ With this approach, any function in the `workflows/` directory would be transformed as a workflow, and any function in `steps/` would be a step. File locations would replace directives.
286
286
 
287
287
  **Why this could work:**
288
288
 
@@ -308,9 +308,9 @@ The directive approach solved all these issues: it works in any project structur
308
308
 
309
309
  ### Decorators
310
310
 
311
- We considered decorators, but they presented significant challenges both technical and ergonomic.
311
+ We considered decorators, but they presented technical and ergonomic challenges.
312
312
 
313
- **Decorators are non-yet-standard and class-focused**
313
+ **Decorators are not-yet-standard and class-focused**
314
314
 
315
315
  Decorators are not yet a standard syntax ([TC39 proposal](https://github.com/tc39/proposal-decorators)) and they currently only work with classes. A class decorator approach could look like this:
316
316
 
@@ -349,7 +349,7 @@ While decorators can be handled at compile-time with build tool support, they pr
349
349
  See the [Macro Wrapper](#macro-wrapper-approach) section below for a deeper dive into why this approach breaks down with concrete examples.
350
350
  </Callout>
351
351
 
352
- ### Macro Wrapper Approach
352
+ ### Macro wrapper approach
353
353
 
354
354
  We also explored compile-time macro approaches - using a compiler to transform wrapper functions or decorators into directive-based code:
355
355
 
@@ -385,7 +385,7 @@ export const processOrder = async (orderId: string) => {
385
385
  };
386
386
  ```
387
387
 
388
- The benefit is that macros could enforce types and provide "Go To Definition" or other LSP features out of the box.
388
+ The benefit is that macros could enforce types and provide "Go To Definition" or other LSP features without additional configuration.
389
389
 
390
390
  However, **the core problem remains: Workflows aren't runtime values**
391
391
 
@@ -431,7 +431,7 @@ To detect that `processOrder` is actually a workflow, the compiler would need wh
431
431
 
432
432
  This level of cross-function analysis is impractical for build tools - it would require analyzing every function call chain in your entire codebase and all dependencies. The compiler can only reliably detect direct `useWorkflow` calls, not calls hidden behind abstractions.
433
433
 
434
- ## How Directives Solve These Problems
434
+ ## How directives solve these problems
435
435
 
436
436
  Directives address all the issues we encountered with previous approaches:
437
437
 
@@ -527,7 +527,7 @@ export async function processOrder(orderId: string) {
527
527
 
528
528
  The `"use step"` directive maintains consistency. While steps run in the full Node.js runtime and *could* work without a directive, they need some way to signal to the workflow runtime that they're steps.
529
529
 
530
- We could have used a function wrapper just for steps:
530
+ We could have used a function wrapper for steps:
531
531
 
532
532
  {/* @skip-typecheck: incomplete code sample */}
533
533
  ```typescript lineNumbers
@@ -584,7 +584,7 @@ By requiring explicit `"use step"` directives, developers have fine-grained cont
584
584
  To understand how directives are transformed at compile time, see [How the Code Transform Works](/docs/how-it-works/code-transform).
585
585
  </Callout>
586
586
 
587
- ## What Directives Enable
587
+ ## What directives enable
588
588
 
589
589
  Because `"use workflow"` defines a compile-time semantic boundary, we can provide:
590
590
 
@@ -603,7 +603,7 @@ Because `"use workflow"` defines a compile-time semantic boundary, we can provid
603
603
  </Card>
604
604
  </Cards>
605
605
 
606
- ## Directives as a JavaScript Pattern
606
+ ## Directives as a JavaScript pattern
607
607
 
608
608
  Directives in JavaScript have always been contracts between the developer and the execution environment. `"use strict"` made this pattern familiar - it's a string literal that changes how code is interpreted.
609
609
 
@@ -611,7 +611,7 @@ While JavaScript doesn't yet have first-class support for custom directives (lik
611
611
 
612
612
  As TC39 members, we at Vercel are actively working with the standards body and broader ecosystem to explore formal specifications for pragma-like syntax or macro annotations that can express execution semantics.
613
613
 
614
- ## Closing Thoughts
614
+ ## Closing thoughts
615
615
 
616
616
  Directives aren't about syntax preference, they're about expressing semantic boundaries. `"use workflow"` tells the compiler, developer, and runtime that this code is deterministic, resumable, and sandboxed.
617
617
 
@@ -8,12 +8,14 @@ type: overview
8
8
  This page is only visible on preview deployments and local development. It does not appear in production.
9
9
  </Callout>
10
10
 
11
- ## Preview Package
11
+ ## Preview package
12
12
 
13
13
  <PreviewInstall />
14
14
 
15
- ## Draft Changelogs
15
+ ## Draft changelogs
16
16
 
17
- Changelog entries staged here for review before publishing to the Vercel website.
17
+ Review these changelog entries before publishing them to the Vercel website.
18
18
 
19
- - [Serializable AbortController and AbortSignal](/docs/internal/serializable-abort-controller) March 12, 2026
19
+ - [Local web UI in Nitro dev](/docs/internal/nitro-web-ui): Unreleased (ships in 5.0.0)
20
+ - [Native Nitro v3 bundling for workflows](/docs/internal/nitro-native-build): May 22, 2026
21
+ - [Serializable AbortController and AbortSignal](/docs/internal/serializable-abort-controller): March 12, 2026
@@ -1,5 +1,10 @@
1
1
  {
2
2
  "title": "Internal",
3
- "pages": ["index", "serializable-abort-controller"],
3
+ "pages": [
4
+ "index",
5
+ "nitro-web-ui",
6
+ "nitro-native-build",
7
+ "serializable-abort-controller"
8
+ ],
4
9
  "defaultOpen": false
5
10
  }
@@ -0,0 +1,38 @@
1
+ ---
2
+ title: Native Nitro v3 bundling for workflows
3
+ description: Workflow routes are now bundled by Nitro v3, so steps run in your app's runtime and can call any server-side Nitro API.
4
+ type: overview
5
+ ---
6
+
7
+ # Native Nitro v3 bundling for workflows
8
+
9
+ <span className="text-sm text-fd-muted-foreground">May 22, 2026</span>
10
+
11
+ Workflow routes are now handled as Nitro v3 handlers and bundled by Nitro, rather than built separately for the Vercel Build Output API. Your workflows are part of the same bundle as the rest of your app.
12
+
13
+ ## What's new
14
+
15
+ - **Call any server-side Nitro API from a step.** Workflow steps now run inside the same bundled runtime as the rest of your Nitro app, so you can call `useStorage()`, `useDatabase()`, `useRuntimeConfig()`, virtual imports, and any other server-side Nitro API directly from a `"use step"` function.
16
+ - **Workflows are part of your Nitro bundle.** Workflow routes are bundled by Nitro alongside your application code, instead of being built into a separate output. There's nothing extra to configure.
17
+ - **Minimal runtime output.** During bundling, Nitro automatically traces native dependencies and tree-shakes unused code to produce a minimal runtime output.
18
+ - **Nitro v2 is unchanged.** This applies to Nitro v3. Apps on Nitro v2 keep their existing behavior.
19
+
20
+ ## Using Nitro APIs from a step
21
+
22
+ Because steps run inside your Nitro runtime, server-side utilities work directly inside a `"use step"` function:
23
+
24
+ ```typescript
25
+ export async function cacheResult(key: string, value: string) {
26
+ "use step";
27
+
28
+ const storage = useStorage("cache");
29
+ await storage.setItem(key, value);
30
+
31
+ return { cached: true };
32
+ }
33
+ ```
34
+
35
+ ## Learn more
36
+
37
+ - [Nitro](/docs/getting-started/nitro): Set up Workflow SDK in a Nitro v3 app
38
+ - [Deploying](/docs/deploying): How workflow bundles are deployed
@@ -0,0 +1,24 @@
1
+ ---
2
+ title: Local web UI in Nitro dev
3
+ description: Inspect, monitor, and debug your workflow runs from the /_workflow route during Nitro development.
4
+ type: overview
5
+ ---
6
+
7
+ # Local web UI in Nitro dev
8
+
9
+ {/* TODO: unreleased. Changeset .changeset/nitro-dashboard-route.md is pending; ships in @workflow/nitro 5.0.0. Update this date on publish. */}
10
+ <span className="text-sm text-fd-muted-foreground">June 2, 2026</span>
11
+
12
+ The Workflow SDK web UI is now built into the Nitro dev server. During development, open `/_workflow` in your browser to inspect, monitor, and debug your workflow runs.
13
+
14
+ ## What's new
15
+
16
+ - **Built-in `/_workflow` route in development.** The route starts the local web UI and redirects to it without requiring a separate command or process.
17
+ - **Inspect runs in place.** Inspect, monitor, and debug your workflow runs directly from the dev server you're already running.
18
+
19
+ ![Workflow SDK web UI on the /_workflow route](/local-web-ui.png)
20
+
21
+ ## Learn more
22
+
23
+ - [Observability](/docs/observability): Inspect runs with the web UI and CLI
24
+ - [Nitro](/docs/getting-started/nitro): Set up Workflow SDK in a Nitro v3 app
@@ -8,7 +8,7 @@ type: overview
8
8
 
9
9
  <span className="text-sm text-fd-muted-foreground">March 12, 2026</span>
10
10
 
11
- `AbortController` and `AbortSignal` now work natively in workflow functions. Create a controller, pass its signal to steps, and call `abort()` no special imports or wrapper functions needed.
11
+ `AbortController` and `AbortSignal` now work natively in workflow functions. Create a controller, pass its signal to steps, and call `abort()`; no special imports or wrapper functions needed.
12
12
 
13
13
  ## What's new
14
14
 
@@ -17,8 +17,8 @@ type: overview
17
17
  - **Cooperative cancellation.** Steps receive the abort in real time and can respond by checking `signal.aborted`, calling `signal.throwIfAborted()`, or passing the signal to APIs like `fetch`.
18
18
  - **Abort errors skip retries.** When a step throws due to an abort (e.g., `fetch` throws `AbortError`), the error is automatically wrapped in `FatalError` so it skips retries and bubbles up immediately.
19
19
  - **`AbortSignal.timeout()` blocked in workflow VM.** Because it relies on real-time timers that break deterministic replay, `AbortSignal.timeout()` throws a helpful error pointing to the `sleep()` + `AbortController` pattern instead.
20
- - **`Request.signal` preserved when it carries abort state.** A `Request`'s `.signal` is serialized when it's already aborted (so the cancellation that happened pre-serialization is preserved) or when it's a workflow-managed signal (so its hook + stream backing carries through). Plain non-aborted native signals including the auto-generated signal on `new Request(url)` are dropped to avoid minting stream infrastructure for every `Request`. To get cross-boundary cancellation through a `Request`, build it with the signal from a workflow-context `AbortController`.
21
- - **Pending queue items drain on completion.** If you call `abort()` (or `dispose` a hook, or kick off a `void sleep('1d')`, or fire a `void someStep()`) without a suspension point between that call and the workflow's return, the runtime now treats end-of-run as a final suspension and commits all pending operations before the run is marked terminal. This matches normal JS semantics `setTimeout` etc. continue running after the surrounding function returns. The most important case: `controller.abort()` called as the last statement of a workflow now actually propagates to in-flight steps on other compute instances.
20
+ - **`Request.signal` preserved when it carries abort state.** A `Request`'s `.signal` is serialized when it's already aborted (so the cancellation that happened pre-serialization is preserved) or when it's a workflow-managed signal (so its hook + stream backing carries through). Plain non-aborted native signals (including the auto-generated signal on `new Request(url)`) are dropped to avoid minting stream infrastructure for every `Request`. To get cross-boundary cancellation through a `Request`, build it with the signal from a workflow-context `AbortController`.
21
+ - **Pending queue items drain on completion.** If you call `abort()` (or `dispose` a hook, or kick off a `void sleep('1d')`, or fire a `void someStep()`) without a suspension point between that call and the workflow's return, the runtime now treats end-of-run as a final suspension and commits all pending operations before the run is marked terminal. This matches normal JS semantics: `setTimeout` etc. continue running after the surrounding function returns. The most important case: `controller.abort()` called as the last statement of a workflow now actually propagates to in-flight steps on other compute instances.
22
22
 
23
23
  ## Timeout with cancellation
24
24
 
@@ -106,7 +106,7 @@ export async function userCancellableWorkflow(jobId: string) {
106
106
 
107
107
  ## Step-initiated abort
108
108
 
109
- A step can receive the full `AbortController` and call `abort()` to cancel parallel work useful for watchdog patterns like quota monitoring:
109
+ A step can receive the full `AbortController` and call `abort()` to cancel parallel work, useful for watchdog patterns like quota monitoring:
110
110
 
111
111
  ```typescript
112
112
  declare function processData(url: string, signal: AbortSignal): Promise<{ processed: boolean }>; // @setup
@@ -143,6 +143,6 @@ async function monitorQuota(userId: string, controller: AbortController) {
143
143
 
144
144
  ## Learn more
145
145
 
146
- - [Cancellation](/docs/foundations/cancellation) Full guide with all usage patterns
147
- - [How Cancellation Works](/docs/how-it-works/cancellation) Hook and stream internals
148
- - [AbortSignal.timeout() in Workflow](/docs/errors/abort-signal-timeout-in-workflow) Why `AbortSignal.timeout()` is blocked and what to use instead
146
+ - [Cancellation](/docs/foundations/cancellation): Full guide with all usage patterns
147
+ - [How Cancellation Works](/docs/how-it-works/cancellation): Hook and stream internals
148
+ - [AbortSignal.timeout() in Workflow](/docs/errors/abort-signal-timeout-in-workflow): Why `AbortSignal.timeout()` is blocked and what to use instead
package/docs/meta.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "pages": [
3
- "introduction",
4
3
  "---",
4
+ "whats-new",
5
5
  "getting-started",
6
6
  "foundations",
7
7
  "how-it-works",
@@ -10,7 +10,8 @@
10
10
  "testing",
11
11
  "deploying",
12
12
  "errors",
13
- "migration-guides",
13
+ "comparisons",
14
+ "configuration",
14
15
  "api-reference"
15
16
  ]
16
17
  }
@@ -0,0 +1,134 @@
1
+ ---
2
+ title: Attributes
3
+ description: Attach metadata to workflow runs for observability.
4
+ type: reference
5
+ summary: Add string attributes to a workflow run.
6
+ prerequisites:
7
+ - /docs/foundations/workflows-and-steps
8
+ related:
9
+ - /docs/observability
10
+ - /docs/api-reference/workflow/set-attributes
11
+ - /docs/api-reference/workflow-errors/workflow-world-error
12
+ ---
13
+
14
+ [`setAttributes`](/docs/api-reference/workflow/set-attributes) attaches plaintext string metadata to the current workflow run. These attributes appear in the Workflow CLI and web UI, and you can search and filter runs by them from either the [CLI](#from-the-cli) or the [Analytics API](/docs/api-reference/workflow-runtime/world/analytics).
15
+
16
+ You can also seed any attributes directly when starting a run:
17
+
18
+ {/* @skip-typecheck: abbreviated usage; orderWorkflow is defined below */}
19
+ ```typescript lineNumbers
20
+ const run = await start(orderWorkflow, ["ord_123"], {
21
+ attributes: { source: "checkout" }, // [!code highlight]
22
+ })
23
+ ```
24
+
25
+ ```typescript lineNumbers
26
+ import { setAttributes } from "workflow"
27
+
28
+ export async function orderWorkflow(orderId: string) {
29
+ "use workflow"
30
+
31
+ await setAttributes({ // [!code highlight]
32
+ phase: "received", // [!code highlight]
33
+ orderId, // [!code highlight]
34
+ }) // [!code highlight]
35
+
36
+ // ...work...
37
+
38
+ await setAttributes({ phase: "complete" }) // [!code highlight]
39
+ }
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ Call [`setAttributes`](/docs/api-reference/workflow/set-attributes) from a `"use workflow"` function or a `"use step"` function. Plain application code is not supported because there is no active workflow run to attach attributes to.
45
+
46
+ Values must be strings. Pass `undefined` to remove a key:
47
+
48
+ ```typescript lineNumbers
49
+ import { setAttributes } from "workflow"
50
+
51
+ export async function cleanupAttributes() {
52
+ "use workflow"
53
+
54
+ await setAttributes({ staleKey: undefined }) // [!code highlight]
55
+ }
56
+ ```
57
+
58
+ Attribute keys must be 1-256 characters, values must be strings up to 256 bytes, and each run can have up to 64 attributes. Keys that start with `$` are reserved for framework and library code.
59
+
60
+ ## Reserved keys
61
+
62
+ When `start()` is called from inside a running workflow or step, the new run is automatically tagged with two reserved attributes:
63
+
64
+ - `$parentRunId`: the run that started it.
65
+ - `$rootRunId`: the root of the chain. It is inherited, so every run in a daisy chain or fan-out shares one root id.
66
+
67
+ Top-level runs (started outside any workflow or step) are not tagged.
68
+
69
+ ## Viewing attributes
70
+
71
+ The run details panel in the observability UI shows the run's current attributes as key-value rows. Reserved `$`-prefixed keys are marked with a badge and sorted after user keys:
72
+
73
+ ![Run details panel showing the Attributes card with reserved keys badged](/screenshots/attributes/run-details-attributes.png)
74
+
75
+ Each `setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:
76
+
77
+ ![Trace timeline with attr_set diamond markers on the run row](/screenshots/attributes/trace-timeline.png)
78
+
79
+ Expanding an `attr_set` event (in the run sidebar or the Events tab) shows the changed keys, removed keys, and whether the write came from the workflow body or a step (with the attempt number):
80
+
81
+ ![Expanded attr_set events showing changes and the writer](/screenshots/attributes/run-details-attr-set-events.png)
82
+
83
+ ## Searching and filtering by attributes
84
+
85
+ ### From the CLI
86
+
87
+ `workflow inspect attributes` lists the keys recorded on this project's runs,
88
+ with how many runs carry each and when it was first and last seen:
89
+
90
+ ```bash
91
+ workflow inspect attributes
92
+ ```
93
+
94
+ Pass one or more `--attribute key=value` pairs to `inspect runs` to list the
95
+ runs carrying them. Repeatable up to 8 times:
96
+
97
+ ```bash
98
+ workflow inspect runs --attribute phase=received --status running
99
+ ```
100
+
101
+ Both require a backend with the analytics read path. `--attribute` is ignored
102
+ with a warning on backends without one, and `inspect attributes` reports that
103
+ it is unavailable.
104
+
105
+ ### From the Analytics API
106
+
107
+ The [Analytics API](/docs/api-reference/workflow-runtime/world/analytics) can discover which attribute keys exist and filter run listings by them. The `analytics` namespace is optional on `World`, so feature-detect it before use; it is absent on local, Postgres, and other custom Worlds:
108
+
109
+ ```typescript lineNumbers
110
+ import { getWorld } from "workflow/runtime";
111
+
112
+ const world = await getWorld();
113
+ if (!world.analytics) {
114
+ throw new Error("This World does not support analytics queries"); // [!code highlight]
115
+ }
116
+
117
+ // Which attribute keys exist, and on how many runs?
118
+ const keys = await world.analytics.attributes.list();
119
+
120
+ // List runs whose latest attributes match every pair
121
+ const stuck = await world.analytics.runs.list({
122
+ attributes: { phase: "received" }, // [!code highlight]
123
+ });
124
+ ```
125
+
126
+ Matching is latest-write-wins: once the run above writes `phase: "complete"`, it stops matching `phase: "received"`.
127
+
128
+ ## Behavior
129
+
130
+ - Attributes require a World implementing spec version 4 or later.
131
+ - Writes from workflow and step bodies append native `attr_set` events and immediately materialize `run.attributes`.
132
+ - Storage errors surface rather than being silently ignored: transient errors on workflow-body writes are retried, and a write the World rejects as invalid (for example, exceeding the per-run attribute cap across multiple calls) fails the run with the validation error.
133
+ - Step-body storage errors throw from `setAttributes` like any other step-side network write. Catch the error inside the step if the attribute is best-effort.
134
+ - Reading and querying: each run's current attributes are returned on the run objects from the [Storage](/docs/api-reference/workflow-runtime/world/storage) and [Analytics](/docs/api-reference/workflow-runtime/world/analytics) APIs, and the Analytics API supports discovering attribute keys and filtering run listings by key=value pairs (see [Searching and filtering by attributes](#searching-and-filtering-by-attributes)). On Worlds without the optional `analytics` namespace, attributes are readable on run objects but not searchable.
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  title: Observability
3
- description: Inspect, monitor, and debug workflows through the CLI and Web UI with powerful observability tools.
4
- type: guide
5
- summary: Inspect and debug workflow runs using the CLI and Web UI.
3
+ description: Inspect, monitor, and debug workflows through the CLI and web UI.
4
+ type: overview
5
+ summary: Inspect and debug workflow runs using the CLI and web UI.
6
6
  prerequisites:
7
7
  - /docs/foundations
8
8
  related:
@@ -10,15 +10,15 @@ related:
10
10
  - /docs/how-it-works/encryption
11
11
  ---
12
12
 
13
- Workflow SDK provides powerful tools to inspect, monitor, and debug your workflows through the CLI and Web UI. These tools allow you to inspect workflow runs, steps, webhooks, [events](/docs/how-it-works/event-sourcing), and stream output.
13
+ Workflow SDK provides a Workflow CLI and web UI to inspect, monitor, and debug workflows. You can inspect workflow runs, steps, webhooks, [events](/docs/how-it-works/event-sourcing), and stream output.
14
14
 
15
- ## Quick Start
15
+ ## Quick start
16
16
 
17
17
  ```bash
18
18
  npx workflow
19
19
  ```
20
20
 
21
- The CLI comes pre-installed with the Workflow SDK and registers the `workflow` command. If the `workflow` package is not already installed, `npx workflow` will download and run the CLI temporarily, or use the local installed version if available.
21
+ The CLI comes pre-installed with the Workflow SDK and registers the `workflow` command. If the `workflow` package is not already installed, `npx workflow` will download and run the CLI temporarily, or use the locally installed version if available.
22
22
 
23
23
  Get started inspecting your local workflows:
24
24
 
@@ -33,15 +33,33 @@ npx workflow inspect runs
33
33
  ## Web UI
34
34
 
35
35
  Workflow SDK ships with a local web UI for inspecting your workflows. The CLI
36
- will locally serve the Web UI when using the `--web` flag.
36
+ serves the web UI locally when you use the `--web` flag.
37
37
 
38
38
  ```bash
39
- # Launch Web UI for visual exploration
39
+ # Launch the web UI for visual exploration
40
40
  npx workflow inspect runs --web
41
41
  ```
42
42
 
43
43
  ![Workflow SDK Web UI](/o11y-ui.png)
44
44
 
45
+ On [Nitro](/docs/getting-started/nitro), the dev server has the web UI built
46
+ in: open `/_workflow` while `nitro dev` is running. No separate command is required.
47
+
48
+ In the runs table, select one or more runs and choose **Cancel** to cancel the batch in a single request. Runs that fail with a retryable error stay selected so you can retry them.
49
+
50
+ To share a link to a specific run without opening a browser, use the `--url`
51
+ flag. It prints the dashboard deep link to stdout and exits (no browser, no
52
+ local server), which is useful for scripts, PR comments, or automation. Add `--json` to
53
+ get `{ "url": "..." }`.
54
+
55
+ ```bash
56
+ # Print the deep-link URL for a run (no browser, no server)
57
+ npx workflow inspect run <run_id> --url
58
+
59
+ # Vercel runs: add the backend (and --env preview for preview deployments)
60
+ npx workflow inspect run <run_id> --backend vercel --url
61
+ ```
62
+
45
63
  ## Backends
46
64
 
47
65
  The Workflow SDK CLI can inspect data from any [World](/docs/deploying). By default, it inspects data in your local development environment. For example, if you are using Next.js to develop workflows locally, the
@@ -53,9 +71,9 @@ If you're deploying workflows to a production environment, but want to inspect t
53
71
  Backends might require additional configuration. If you're missing environment variables, the World package should provide instructions on how to configure it.
54
72
  </Callout>
55
73
 
56
- ### Vercel Backend
74
+ ### Vercel backend
57
75
 
58
- To inspect workflows running on Vercel, ensure you're logged in to the Vercel CLI and have linked your project. See [Vercel CLI authentication and project linking docs](https://vercel.com/docs/cli/project-linking) for more information. Then, simply specify the backend as `vercel`.
76
+ To inspect workflows running on Vercel, ensure you're logged in to the Vercel CLI and have linked your project. See [Vercel CLI authentication and project linking docs](https://vercel.com/docs/cli/project-linking) for more information. Then, specify the backend as `vercel`.
59
77
 
60
78
  ```bash
61
79
  # Inspect workflows running on Vercel
@@ -63,3 +81,7 @@ npx workflow inspect runs --backend vercel
63
81
  ```
64
82
 
65
83
  When deployed to Vercel, workflow data is [encrypted end-to-end](/docs/how-it-works/encryption). Encrypted fields display as locked placeholders until you choose to decrypt them using the **Decrypt** button in the web UI or the `--decrypt` flag in the CLI.
84
+
85
+ ## More observability features
86
+
87
+ <AutoCards />
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "title": "Observability",
3
- "pages": ["observability"]
3
+ "pages": ["tracing", "attributes", "retention"]
4
4
  }
@@ -0,0 +1,93 @@
1
+ ---
2
+ title: Data retention
3
+ description: Control how long a run's data is kept after it finishes.
4
+ type: reference
5
+ summary: Control how long a run's data is kept after the run ends.
6
+ prerequisites:
7
+ - /docs/foundations/workflows-and-steps
8
+ related:
9
+ - /docs/observability
10
+ - /docs/api-reference/workflow-api/start
11
+ ---
12
+
13
+ A finished run leaves data behind: the inputs and outputs of the workflow and
14
+ each of its steps, the payloads on its event log, and anything written to its
15
+ streams. How long that data is kept is decided by the World you are running
16
+ on, not by the SDK.
17
+
18
+ `experimental_retention` on [`start()`](/docs/api-reference/workflow-api/start)
19
+ lets a run ask for a specific retention period, rather than the World's default.
20
+
21
+ ## Deleting a run's data as soon as it ends
22
+
23
+ {/* @skip-typecheck: abbreviated usage; processDocumentWorkflow is the reader's own workflow */}
24
+ ```typescript lineNumbers
25
+ const run = await start(processDocumentWorkflow, [documentId], {
26
+ experimental_retention: 0, // [!code highlight]
27
+ })
28
+ ```
29
+
30
+ `0` asks the World to delete the run's **user data** the moment the run
31
+ completes or fails, rather than keeping it for the World's default window.
32
+
33
+ Two values are accepted today:
34
+
35
+ | Value | Meaning |
36
+ | --- | --- |
37
+ | `0` | Delete user data as soon as the run reaches a terminal state. |
38
+ | `'default'` | Use the World's default. Identical to omitting the option. |
39
+
40
+ <Callout type="warn">
41
+ The option is prefixed `experimental_` because both its name and the set of
42
+ values it accepts are expected to change.
43
+ </Callout>
44
+
45
+ ## What is deleted, and what is not
46
+
47
+ **Deleted:** the run's input, output and error; every step's input, output and
48
+ error; the payloads on the event log; and stream contents.
49
+
50
+ **Kept:** the run, step and event records themselves — their ids, timestamps,
51
+ status, step names, and any [attributes](/docs/observability/attributes) you
52
+ set. They are kept for the World's default period so the run stays visible in
53
+ the CLI and web UI. A purged run is still listed and still traceable; its
54
+ payloads simply read back as expired.
55
+
56
+ Inspecting a purged run shows it as expired rather than failing. The Workflow
57
+ CLI renders the run's own input, output and error as `<data expired>`:
58
+
59
+ ```bash
60
+ workflow inspect runs wrun_...
61
+ ```
62
+
63
+ Step, hook and event payloads read back empty. On the Vercel World they also
64
+ render as `<data expired>`; on Worlds that clear the stored value outright
65
+ they simply show as empty. Either way the data is gone — the difference is
66
+ only in how the absence is labelled.
67
+
68
+ <Callout type="warn">
69
+ **You cannot read the return value of a run started with
70
+ `experimental_retention: 0`.** The deletion races your own read of the
71
+ result and generally wins, so `await run.returnValue` throws
72
+ [`RunExpiredError`](/docs/errors/run-expired) instead of resolving.
73
+
74
+ This is a known limitation. If you need the result, send it somewhere you
75
+ control, e.g. a step that writes it to your own store, rather than reading it back off the run.
76
+ </Callout>
77
+
78
+ `RunExpiredError` is not specific to `experimental_retention: 0`. Any run read
79
+ after its retention window has passed throws it, and the error carries
80
+ `runId`, `runStatus` and `expiredAt` when the World still has them — so a
81
+ caller can tell a successful run whose result is gone from a failed one whose
82
+ error is gone. If the run's metadata is gone too, the World reports the run as
83
+ missing and you get `WorkflowRunNotFoundError` instead.
84
+
85
+ ## Retention is implemented by the World
86
+
87
+ The SDK records your preference; it does not enforce it. `start()` writes the
88
+ value onto the run as the reserved `$retention` attribute, and the World
89
+ decides what to do when the run ends.
90
+ A World that does not implement retention
91
+ **keeps the data**. If you need certainty that a specific World deletes your data,
92
+ confirm it against that World's own documentation rather than the presence of this
93
+ option.