internal-dev-workbench
Spin up a portless + tmux dev session for the Workflow SDK that gives each git worktree isolated `<branch>.<name>.localhost` URLs for the Next.js workbench and…
Upgrades an app from Workflow SDK 4.x to 5.0. Use when bumping the `workflow` / `@workflow/*` dependencies to v5, or when hitting removed v4 APIs — `runStep`, `stepEntrypoint`, `workflow/internal/private`, `@workflow/core/private`, `writeToStream` / `closeStream` /
$ npx -y skills add vercel/workflow --skill migrating-workflow-v4-to-v5 --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/migrating-workflow-v4-to-v5Context preview
The summary Claude sees to decide when to auto-load this skill.
Upgrades an app from Workflow SDK 4.x to 5.0. Use when bumping the `workflow` / `@workflow/*` dependencies to v5, or when hitting removed v4 APIs — `runStep`, `stepEntrypoint`, `workflow/internal/private`, `@workflow/core/private`, `writeToStream` / `closeStream` /
name: migrating-workflow-v4-to-v5
description: >-
Upgrades an app from Workflow SDK 4.x to 5.0. Use when bumping the `workflow` / `@workflow/*` dependencies to v5, or when hitting removed v4 APIs — `runStep`, `stepEntrypoint`, `workflow/internal/private`, `@workflow/core/private`, `writeToStream` / `closeStream` / `readFromStream` on a World, `world.steps.get` without a runId, `hook.getConflict()` returning `{ runId }`, `hook.metadata` read synchronously off a `getHookByToken()` result, `experimental_setAttributes`, `createLocalWorld` / `createVercelWorld`, `NestLocalBuilder` imported from `@workflow/nest`, or an SWC transform invoked with `mode: 'client'`.
metadata:
author: Vercel Inc.
version: '0.2.10'Workflow SDK 5.0 keeps the programming model from 4.x. `"use workflow"` / `"use step"`, `start()`, `getRun()`, hooks, webhooks, streams, `sleep()`, retries, and the event log are unchanged, so most application code compiles as-is.
The breaking changes are concentrated in three places:
1. **Runtime entrypoints** (`workflow/api`, `workflow/runtime`) — two exports removed. 2. **The `World` interface** — only relevant if the app implements a custom World or calls `getWorld()` directly. 3. **Build integrations** — `@workflow/nest` subpaths, and private compiler subpaths that were never public.
Do not rewrite workflow or step bodies. If you find yourself restructuring business logic, you have gone outside this migration.
Before editing, establish:
1. **Which packages are installed.** Read `package.json` for `workflow` and every `@workflow/*` dependency. 2. **Whether the app touches the runtime.** Grep for `getWorld`, `createWorld`, `getWorldHandlers`, `writeToStream`, `readFromStream`, `closeStream`, `listStreamsByRunId`, `getStreamChunks`, `world.steps`, `listByCorrelationId`, `runStep`, `stepEntrypoint`, `internal/private`, `core/private`. 3. **Whether the app implements a custom World.** Grep for `implements World`, `: World`, `createLocalWorld`, `createVercelWorld`, `startWorkflowWorld`. 4. **Which framework integration is in use.** `@workflow/next`, `@workflow/nest`, `@workflow/nitro`, `@workflow/sveltekit`, `@workflow/vite`, `@workflow/nuxt`, `@workflow/astro`, or the CLI. 5. **Whether `hook.getConflict()` is used.** Grep for `getConflict`. 6. **Whether hook metadata is read outside a workflow.** Grep for `getHookByToken` and `resumeHook`, then for `.metadata` on their results. 7. **Whether the app calls the compiler directly.** Grep for `mode: 'client'`, `transformSync`, `swc-plugin-workflow`. Only custom build integrations do this. 8. **Whether `experimental_setAttributes` is used.** Grep for `experimental_setAttributes`.
Report anything in 2–8 that the app does not use as "not applicable" rather than silently skipping it.
Move every `workflow` and `@workflow/*` dependency to `^5.0.0`. They are released together and must not be mixed across majors — a 4.x `@workflow/next` against a 5.x `workflow` will fail at build time.
{
"dependencies": {
"workflow": "^5.0.0",
"@workflow/next": "^5.0.0"
}
}Then reinstall and rebuild so the compiler regenerates the workflow/step bundles and the generated routes under `.well-known/workflow/v1/`. Never hand-edit generated output.
Node requirements are unchanged: `^18 || ^20 || ^22 || ^24`.
Apply each rule only where the pattern actually appears.
// v4 const world = getWorld(); // v5 const world = await getWorld();
This also applies to `getWorldHandlers()`. Awaiting was already correct in 4.x, so this edit is safe to make before the dependency bump. Propagate `async` up the call chain rather than wrapping in `.then()` chains.
First-party World packages now expose a single `createWorld()` factory. The arguments are unchanged — this is a rename only.
// v4
import { createLocalWorld } from '@workflow/world-local';
const world = createLocalWorld({ dataDir });
// v5
import { createWorld } from '@workflow/world-local';
const world = createWorld({ dataDir });The same applies to `createVercelWorld` from `@workflow/world-vercel`.
Call the step function directly. The compiler routes the call through the step runtime.
// v4
import { runStep } from 'workflow/api';
const result = await runStep(chargeCard, [orderId]);
// v5
const result = await chargeCard(orderId);Framework integrations generate step routes themselves — delete hand-written step routes that existed only to call `stepEntrypoint`. For a custom host, serve the handlers from `getWorldHandlers()` instead.
These subpaths were never public API. Remove the imports; if generated build output still references them, it is stale — reinstall and rebuild rather than restoring the imports.
| v4 | v5 | | --- | --- | | `world.writeToStream(name, runId, chunk)` | `world.streams.write(runId, name, chunk)` | | `world.writeToStreamMulti(name, runId, chunks)` | `world.streams.writeMulti(runId, name, chunks)` | | `world.closeStream(name, runId)` | `world.streams.close(runId, name)` | | `world.readFromStream(name, startIndex?)` | `world.streams.get(runId, name, startIndex?)` | | `world.getStreamChunks(name, runId, options?)` | `world.streams.getChunks(runId, name, options?)` | | `world.listStreamsByRunId(runId)` | `world.streams.list(runId)` |
The argument order flipped, so a rename alone silently passes a stream name where a run ID is expected. Swap the arguments at every call site. `readFromStream` had no `runId` parameter at all — `streams.get` requires one, so thread t
Workflow SDK: Build durable, reliable, and observable apps and AI Agents in TypeScript
Repo: vercel/workflow
Spin up a portless + tmux dev session for the Workflow SDK that gives each git worktree isolated `<branch>.<name>.localhost` URLs for the Next.js workbench and…
Migrates Temporal, Inngest, Trigger.dev, and AWS Step Functions workflows to the Workflow SDK. Use when porting Activities, Workers, Signals, step.run(),…
Upgrades a custom Workflow SDK World implementation from the v4 spec to v5. Use when a package implements the `World` interface from `@workflow/world` and is…
Install and configure Vercel Workflow SDK before it exists in node_modules. Use when the user asks to "install workflow", "set up workflow", "add durable…
Creates durable, resumable workflows using Vercel's Workflow SDK. Use when building workflows that need to survive restarts, pause for external events, retry…