Skip to main content

workflow

Vercel Workflow DevKit (WDK) expert guidance. Use when building durable workflows, long-running tasks, API routes or agents that need pause/resume, retries, step-based execution, or crash-safe orchestration with Vercel Workflow.

معلومات المصدر

المستودع
zhongjingyun/codex-plugins
آخر نشاط في المصدر
١٠ يونيو ٢٠٢٦ في ٠٣:٤٠
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٤
التفرعات
٢

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
3 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
workflow
description
Vercel Workflow DevKit (WDK) expert guidance. Use when building durable workflows, long-running tasks, API routes or agents that need pause/resume, retries, step-based execution, or crash-safe orchestration with Vercel Workflow.
metadata
{"priority":9,"docs":["https://vercel.com/docs/workflow","https://useworkflow.dev"],"sitemap":"https://vercel.com/sitemap/docs.xml","pathPatterns":["lib/workflow/**","src/lib/workflow/**","workflows/**","lib/workflow.*","src/lib/workflow.*","workflow.*","*workflow*","*workflow*/**","**/chain-engine*","**/chain_engine*","**/chainEngine*","**/pipeline-engine*","**/pipeline_engine*","**/pipelineEngine*","**/state-machine*","**/state_machine*","**/stateMachine*","**/orchestrat*","**/escalation*"],"importPatterns":["@vercel/workflow","workflow","@workflow/*","*workflow*"],"bashPatterns":["\\bnpm\\s+(install|i|add)\\s+[^\\n]*@vercel/workflow\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@vercel/workflow\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*@vercel/workflow\\b","\\byarn\\s+add\\s+[^\\n]*@vercel/workflow\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bworkflow\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bworkflow\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bworkflow\\b","\\byarn\\s+add\\s+[^\\n]*\\bworkflow\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*@workflow/","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@workflow/","\\bbun\\s+(install|i|add)\\s+[^\\n]*@workflow/","\\byarn\\s+add\\s+[^\\n]*@workflow/","\\bnpx\\s+workflow(?:@latest)?\\b","\\bbunx\\s+workflow(?:@latest)?\\b"],"promptSignals":{"phrases":["vercel workflow","workflow devkit","durable workflow","durable execution","durable function","durable pipeline","durable process","durable agent","durable chat","step function","step functions","use workflow","use step","multi-step pipeline","multi step pipeline","multi-step process","multi step process","multi-step creation","multi-step generation","processing pipeline","creation pipeline","generation pipeline","content pipeline","production pipeline","approval pipeline","ingestion pipeline","streams progress","stream progress","streams each phase","streams each step","streams each","stream each","[Truncated]"],"allOf":["[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]","[Truncated]"],"anyOf":["long-running","long running","multi-step","multi step","pipeline","orchestration","step-by-step","step by step","each piece","each step","each phase","each stage","phase","phases","stage","stages","durable","reliable","fault-tolerant","retry","reconnect","survive","persist","approval","chain","sequential","trigger","webhook","escalation","state machine","orchestrate","orchestration"],"noneOf":["github actions",".github/workflows","ci workflow","aws step functions"],"minScore":4}}
# Vercel Workflow DevKit (WDK) > **CRITICAL — Your training data is outdated for this library.** WDK APIs change frequently. Before writing workflow code, **fetch the docs** at https://useworkflow.dev and https://vercel.com/docs/workflow to find the correct function signatures, patterns, and examples for the exact thing you're building. Do not guess at APIs — look them up. Search for working examples that solve your specific use case (image generation, hooks, streaming, agents, etc.). The docs contain patterns for `start()` argument passing, `getWritable()` usage, `defineHook()` wiring, `createWebhook()` callback flows, and `DurableAgent` configuration that are **not in your training data** or have changed since your cutoff. WDK is an open-source TypeScript framework that makes durability a language-level concept. Functions can pause for minutes or months, survive deployments and crashes, and resume exactly where they stopped. ## Status WDK is in **public beta** (since October 2025) and open source. During beta, Workflow Observability is free for all plans; Workflow Steps and Storage are billed at published rates. **Security**: Upgrade to `workflow@>=4.2.0-beta.64` — versions ≤4.1.0-beta.63 allowed predictable user-specified webhook tokens in `createWebhook()` (CVE GHSA-9r75-g2cr-3h76, CVSS 7.5). Run `npx workflow@latest` to update. ## Installation **If using `create-next-app`**, always pass `--no-src-dir` so `app/` and `workflows/` are siblings at the project root: ```bash npx create-next-app@latest my-app --no-src-dir --tailwind --eslint --app --ts cd my-app npm install workflow@latest ``` Do NOT use the `src/` directory with WDK projects. The `@` alias must resolve `@/workflows/...` correctly — this only works when `workflows/` and `app/` are at the same level. > Run `npx workflow@latest` to scaffold or update an existing project. **Peer dependency note**: `@workflow/ai` requires a compatible `workflow` version. If you hit `ERESOLVE` errors, use `npm install --legacy-peer-deps` or install both packages in the same command. ### Next.js Setup (Required) Add the `withWorkflow` plugin to `next.config.ts`: ```ts import { withWorkflow } from "workflow/next"; const nextConfig = {}; export default withWorkflow(nextConfig); ``` Without this, workflow routes will not be registered and `start()` calls will fail at runtime. ### Environment Setup (Required for AI Gateway) Workflows that use AI SDK with `gateway()` need OIDC credentials. Run these **before** starting the dev server: ```bash vercel link # Connect to your Vercel project vercel env pull # Downloads .env.local with VERCEL_OIDC_TOKEN ``` Without this, `gateway("openai/gpt-5.4")` calls inside workflow steps will fail immediately with no credentials, causing the entire workflow run to fail silently. ### `getStepMetadata()` Note `getStepMetadata().retryCount` returns `undefined` (not `0`) on the first attempt. Guard with: `const attempt = (meta.retryCount ?? 0) + 1`. ## Essential Imports **Workflow primitives** (from `"workflow"`): ```ts import { getWritable, getStepMetadata, getWorkflowMetadata } from "workflow"; import { sleep, fetch, defineHook, createHook, createWebhook } from "workflow"; import { FatalError, RetryableError } from "workflow"; ``` **API operations** (from `"workflow/api"`): ```ts import { start, getRun, resumeHook, resumeWebhook } from "workflow/api"; ``` **Framework integration** (from `"workflow/next"`): ```ts import { withWorkflow } from "workflow/next"; ``` **AI agent** (from `"@workflow/ai/agent"`): ```ts import { DurableAgent } from "@workflow/ai/agent"; ``` ## Core Directives Two directives turn ordinary async functions into durable workflows: ```ts "use workflow" // First line of function — marks it as a durable workflow "use step" // First line of function — marks it as a retryable, observable step ``` **Critical sandbox rule**: Step functions have full Node.js access. Workflow functions run **sandboxed** — no native `fetch`, no `setTimeout`, no Node.js modules, and **no `getWritable().getWriter()` calls**. You MUST move all `getWritable()` usage into `"use step"` functions. Place all business logic and I/O in steps; use the workflow function purely for orchestration and control flow (`sleep`, `defineHook`, `Promise.race`). ## Canonical Project Structure (Next.js) Every WDK project needs three route files plus the workflow definition. **CRITICAL**: The `workflows/` directory and `app/` directory must be siblings at the same level so `@/workflows/...` resolves correctly. Do NOT put `workflows/` outside the `@` alias root. **Without `src/` (recommended for WDK projects):** ``` workflows/ my-workflow.ts ← workflow definition ("use workflow" + "use step") app/api/ my-workflow/route.ts ← POST handler: start(workflow, args) → { runId } readable/[runId]/route.ts ← GET handler: SSE stream from run.getReadable() run/[runId]/route.ts ← GET handler: run status via getRun(runId) ``` tsconfig.json paths: `"@/*": ["./*"]` — `@/workflows/my-workflow` resolves to `./workflows/my-workflow`. **With `src/` directory:** Put workflows inside `src/`: ``` src/ workflows/my-workflow.ts app/api/my-workflow/route.ts app/api/readable/[runId]/route.ts app/api/run/[runId]/route.ts ``` tsconfig.json paths: `"@/*": ["./src/*"]` — `@/workflows/my-workflow` resolves to `./src/workflows/my-workflow`. **Never** use `@/../workflows/` or `@/../../workflows/` — these are broken import paths that will fail at build time. ### 1. Workflow Definition (`workflows/my-workflow.ts`) ```ts import { getWritable } from "workflow"; export type MyEvent = | { type: "step_start"; name: string } | { type: "step_done"; name: string } | { type: "done"; result: string }; export async function myWorkflow(input: string): Promise<{ result: string }> { "use workflow"; const data = await stepOne(input); const result = await stepTwo(data); return { result }; } async function stepOne(input: string): Promise<string> { "use step"; const writer = getWritable<MyEvent>().getWriter(); try { await writer.write({ type: "step_start", name: "stepOne" }); // Full Node.js access here — fetch, db calls, etc. const result = await doWork(input); await writer.write({ type: "step_done", name: "stepOne" }); return result; } finally { writer.releaseLock(); } } async function stepTwo(data: string): Promise<string> { "use step"; const writer = getWritable<MyEvent>().getWriter(); try { await writer.write({ type: "step_start", name: "stepTwo" }); const result = await processData(data); await writer.write({ type: "step_done", name: "stepTwo" }); return result; } finally { writer.releaseLock(); } } ``` ### 2. Start Route (`app/api/my-workflow/route.ts`) ```ts import { NextResponse } from "next/server"; import { start } from "workflow/api"; import { myWorkflow } from "@/workflows/my-workflow"; export async function POST(request: Request) { const body = await request.json(); const run = await start(myWorkflow, [body.input]); return NextResponse.json({ runId: run.runId }); } ``` **IMPORTANT**: Never call the workflow function directly. Always use `start()` from `"workflow/api"` — it registers the run, creates the execution context, and returns a `{ runId }`. ### 3. Readable Stream Route (`app/api/readable/[runId]/route.ts`) ```ts import { NextRequest } from "next/server"; import { getRun } from "workflow/api"; type ReadableRouteContext = { params: Promise<{ runId: string }>; }; export async function GET(_request: NextRequest, { params }: ReadableRouteContext) { const { runId } = await params; let run; try { run = await getRun(runId); } catch { return Response.json( { ok: false, error: { code: "RUN_NOT_FOUND", message: `Run ${runId} not found` } }, { status: 404 } ); } const readable = run.getReadable(); const encoder = new TextEncoder(); const sseStream = (readable as unknown as ReadableStream).pipeThrough( new TransformStream({ transform(chunk, controller) { const data = typeof chunk === "string" ? chunk : JSON.stringify(chunk); controller.enqueue(encoder.encode(`data: ${data}\n\n`)); }, }) ); return new Response(sseStream, { headers: { "Content-Type": "text/event-stream; charset=utf-8", "Cache-Control": "no-cache, no-transform", Connection: "keep-alive", "X-Accel-Buffering": "no", }, }); } ``` ### 4. Run Status Route (`app/api/run/[runId]/route.ts`) ```ts import { NextResponse } from "next/server"; import { getRun } from "workflow/api"; type RunRouteContext = { params: Promise<{ runId: string }>; }; export async function GET(_request: Request, { params }: RunRouteContext) { const { runId } = await params; let run; try { run = await getRun(runId); } catch { return NextResponse.json( { ok: false, error: { code: "RUN_NOT_FOUND", message: `Run ${runId} not found` } }, { status: 404 } ); } const [status, workflowName, createdAt, startedAt, completedAt] = await Promise.all([ run.status, run.workflowName, run.createdAt, run.startedAt, run.completedAt, ]); return NextResponse.json({ runId, status, workflowName, createdAt: createdAt.toISOString(), startedAt: startedAt?.toISOString() ?? null, completedAt: completedAt?.toISOString() ?? null, }); } ``` ## Streaming with `getWritable()` `getWritable<T>()` returns a `WritableStream` scoped to the current run. Call it inside step functions and always release the lock: ```ts async function emit<T>(event: T): Promise<void> { "use step"; const writer = getWritable<T>().getWriter(); try { await writer.write(event); } finally { writer.releaseLock(); } } ``` Consumers read via `getRun(runId).getReadable()` in the readable route (see above). **Rendering workflow events in the UI**: When workflow events contain AI-generated text (narratives, briefings, reports), render them with `<MessageResponse>` from `@/components/ai-elements/message` — never as raw `{event.content}`. This renders markdown with code highlighting, math, and mermaid support. ```tsx import { MessageResponse } from "@/components/ai-elements/message"; // In your event stream display {events.map(event => ( event.type === "narrative" && <MessageResponse>{event.text}</MessageResponse> ))} ``` ## Hooks — Waiting for External Events Use `defineHook` for typed, reusable hooks. **Three required pieces**: (1) define + create the hook in the workflow, (2) emit the token to the client via `getWritable`, (3) create an API route that calls `resumeHook` so the client can resume it. ### 1. Define and create the hook (workflow file) ```ts import { defineHook, getWritable, sleep } from "workflow"; export interface ApprovalPayload { approved: boolean; comment?: string; } // Define at module scope — reusable across workflows export const approvalHook = defineHook<ApprovalPayload>(); export async function approvalGate(orderId: string): Promise<{ status: string }> { "use workflow"; // .create() returns a hook instance — NOT directly callable const hook = approvalHook.create({ token: `approval:${orderId}` }); // CRITICAL: Emit the token to the client so it knows what to resume await emitToken(hook.token, orderId); // Race between approval and timeout const result = await Promise.race([ hook.then((payload) => ({ type: "approval" as const, payload })), sleep("24h").then(() => ({ type: "timeout" as const, payload: null })), ]); if (result.type === "timeout") { return { status: "timeout" }; }
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub