Creates durable, resumable workflows using Vercel's Workflow DevKit. Use when building workflows that need to survive restarts, pause for external events, retry on failure, or coordinate multi-step operations over time. Triggers on mentions of "workflow", "durable functions", "resumable", "workflow devkit", "queue", "event", "push", "subscribe", or step-based orchestration.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
Creates durable, resumable workflows using Vercel's Workflow DevKit. Use when building workflows that need to survive restarts, pause for external events, retry on failure, or coordinate multi-step operations over time. Triggers on mentions of "workflow", "durable functions", "resumable", "workflow devkit", "queue", "event", "push", "subscribe", or step-based orchestration.
metadata
{"author":"Vercel Inc.","version":"1.4"}
CRITICAL: Always Use Correct workflow Documentation
Your knowledge of workflow is outdated.
The workflow documentation outlined below matches the installed version of the Workflow DevKit.
Follow these instructions before starting on any workflow-related tasks:
Search the bundled documentation in node_modules/workflow/docs/:
"use workflow" functions run in a sandboxed VM. "use step" functions have full Node.js access. Put your logic in steps and use the workflow function purely for orchestration.
// Steps have full Node.js and npm accessasyncfunctionfetchUserData(userId: string) {
"use step";
const response = awaitfetch(`https://api.example.com/users/${userId}`);
return response.json();
}
asyncfunctionprocessWithAI(data: any) {
"use step";
// AI SDK works in steps without workaroundsreturnawaitgenerateText({
model: openai("gpt-4"),
prompt: `Process: ${JSON.stringify(data)}`,
});
}
// Workflow orchestrates steps - no sandbox issuesexportasyncfunctiondataProcessingWorkflow(userId: string) {
"use workflow";
const data = awaitfetchUserData(userId);
const processed = awaitprocessWithAI(data);
return { success: true, processed };
}
Benefits: Steps have automatic retry, results are persisted for replay, and no sandbox restrictions.
Workflow Sandbox Limitations
When you need logic directly in a workflow function (not in a step), these restrictions apply:
Limitation
Workaround
No fetch()
import { fetch } from "workflow" then globalThis.fetch = fetch
No setTimeout/setInterval
Use sleep("5s") from "workflow"
No Node.js modules (fs, crypto, etc.)
Move to a step function
Example - Using fetch in workflow context:
import { fetch } from"workflow";
exportasyncfunctionmyWorkflow() {
"use workflow";
globalThis.fetch = fetch; // Required for AI SDK and HTTP libraries// Now generateText() and other libraries work
}
Note:DurableAgent from @workflow/ai handles the fetch assignment automatically.
DurableAgent — AI Agents in Workflows
Use DurableAgent to build AI agents that maintain state and survive interruptions. It handles the workflow sandbox automatically (no manual globalThis.fetch needed).
getWritable<UIMessageChunk>() streams output to the workflow run's default stream
Tool execute functions that need Node.js/npm access should use "use step"
Tool execute functions that use workflow primitives (sleep(), createHook()) should NOT use "use step" — they run at the workflow level
maxSteps limits the number of LLM calls (default is unlimited)
Multi-turn: pass result.messages plus new user messages to subsequent agent.stream() calls
For more details on DurableAgent, check the AI docs in node_modules/@workflow/ai/docs/.
Starting Workflows & Child Workflows
Use start() to launch workflows from API routes. start() cannot be called directly in workflow context — wrap it in a step function.
import { start } from"workflow/api";
// From an API route — works directlyexportasyncfunctionPOST() {
const run = awaitstart(myWorkflow, [arg1, arg2]);
returnResponse.json({ runId: run.runId });
}
// No-args workflowconst run = awaitstart(noArgWorkflow);
Starting child workflows from inside a workflow — must use a step:
import { start } from"workflow/api";
// Wrap start() in a step functionasyncfunctiontriggerChild(data: string) {
"use step";
const run = awaitstart(childWorkflow, [data]);
return run.runId;
}
exportasyncfunctionparentWorkflow() {
"use workflow";
const childRunId = awaittriggerChild("some data"); // Fire-and-forget via stepawaitsleep("1h");
}
start() returns immediately — it doesn't wait for the workflow to complete. Use run.returnValue to await completion.
Hooks — Pause & Resume with External Events
Hooks let workflows wait for external data. Use createHook() inside a workflow and resumeHook() from API routes. Deterministic tokens are for createHook() + resumeHook() (server-side) only. createWebhook() always generates random tokens — do not pass a token option to createWebhook().
Not supported: Functions, class instances, Symbols, WeakMap/WeakSet. Pass data, not callbacks.
Streaming
Use getWritable() to stream data from workflows. getWritable() can be called in both workflow and step contexts, but you cannot interact with the stream (call getWriter(), write(), close()) directly in a workflow function. The stream must be passed to step functions for actual I/O, or steps can call getWritable() themselves.
Use getWritable({ namespace: 'name' }) to create multiple independent streams for different types of data. This is useful for separating logs from primary output, different log levels, agent outputs, metrics, or any distinct data channels. Long-running workflows benefit from namespaced streams because you can replay only the important events (e.g., final results) while keeping verbose logs in a separate stream.
import { start, getRun } from"workflow/api";
import { agentWorkflow } from"./workflows/agent";
exportasyncfunctionPOST(request: Request) {
const run = awaitstart(agentWorkflow, ["process data"]);
// Access specific streams by namespaceconst results = run.getReadable({ namespace: undefined }); // Default stream (important results)const infoLogs = run.getReadable({ namespace: "logs:info" });
const debugLogs = run.getReadable({ namespace: "logs:debug" });
const thoughts = run.getReadable({ namespace: "agent:thoughts" });
// Return only important results for most clientsreturnnewResponse(results, {
headers: { "Content-Type": "application/json" },
});
}
// Resume from a specific point (useful for long sessions)exportasyncfunctionGET(request: Request) {
const { searchParams } = newURL(request.url);
const runId = searchParams.get("runId")!;
const startIndex = parseInt(searchParams.get("startIndex") || "0", 10);
const run = getRun(runId);
// Resume only the important stream, skip verbose debug logsconst stream = run.getReadable({ startIndex });
returnnewResponse(stream);
}
Pro tip: For very long-running sessions (50+ minutes), namespaced streams help manage replay performance. Put verbose/debug output in separate namespaces so you can replay just the important events quickly.
Debugging
# Check workflow endpoints are reachable
npx workflow health
npx workflow health --port 3001 # Non-default port# Visual dashboard for runs
npx workflow web
npx workflow web <run_id>
# CLI inspection (use --json for machine-readable output, --help for full usage)
npx workflow inspect runs
npx workflow inspect run <run_id>
# For Vercel-deployed projects, specify backend and project
npx workflow inspect runs --backend vercel --project <project-name> --team <team-slug>
npx workflow inspect run <run_id> --backend vercel --project <project-name> --team <team-slug>
# Open Vercel dashboard in browser for a specific run
npx workflow inspect run <run_id> --web
npx workflow web <run_id> --backend vercel --project <project-name> --team <team-slug>
# Cancel a running workflow
npx workflow cancel <run_id>
npx workflow cancel <run_id> --backend vercel --project <project-name> --team <team-slug>
# --env defaults to "production"; use --env preview for preview deployments
Debugging tips:
Use --json (-j) on any command for machine-readable output
Use --web to open the Vercel Observability dashboard in your browser
Use --help on any command for full usage details
Only import workflow APIs you actually use. Unused imports can cause 500 errors.
Testing Workflows
Workflow DevKit provides a Vitest plugin for testing workflows in-process — no running server required.
Unit testing steps: Steps are just functions; without the compiler, "use step" is a no-op. Test them directly: