mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
codex/share-error-dehydration
201 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d72c82220f |
Fix bug where the SWC compiler bug prunes step-only imports in the client-mode transformation
* first pass * fix failing test * Update changeset to fix SWC compiler issue Fix bug where the SWC compiler bug prunes step-only imports in the client-mode transformation Signed-off-by: Nathan Rajlich <n@n8.io> * DCO Remediation Commit for nathancolosimo <nathancolosimo@gmail.com> I, nathancolosimo <nathancolosimo@gmail.com>, hereby add my Signed-off-by to this commit: |
||
|
|
54758f30ab |
Remove hard-coded VERCEL_DEPLOYMENT_KEY from nextjs-turbopack workbench (#1319)
Vercel now automatically sets VERCEL_DEPLOYMENT_KEY to a unique value for every deployment, so the hard-coded value is no longer necessary. |
||
|
|
9781afb490 |
test: enable encryption on nextjs-turbopack workbench (#1262)
* test: enable encryption on nextjs-turbopack workbench
Set VERCEL_DEPLOYMENT_KEY to enable e2e encryption for the
nextjs-turbopack workbench project. This allows the e2e test
suite to exercise encrypted workflow runs alongside existing
unencrypted runs for backwards compatibility testing.
* fix: fetch encryption key from API in external contexts (e2e, CLI)
Use VERCEL=1 env var to distinguish serverless runtime (can use local
HKDF derivation) from external contexts like e2e test runners, CLI,
and web dashboard (must fetch derived key from Vercel API).
Previously, when VERCEL_DEPLOYMENT_ID matched but VERCEL_DEPLOYMENT_KEY
was absent (e.g., e2e runner has the deployment ID from CI but not the
key), getEncryptionKeyForRun returned undefined — silently skipping
decryption. Now external contexts always use the API fetch path.
* fix: add 429 retry with exponential backoff to fetchRunKey
The e2e runner calls fetchRunKey for every run, which can exceed the
API rate limit (30/min per team) when many tests run in parallel.
Add retry logic with exponential backoff + jitter (up to 5 retries)
and respect the Retry-After header when present.
* Fix: Missing NaN guard on `retry-after` header parsing causes `setTimeout(r, NaN)` to resolve immediately (0ms), triggering rapid-fire retries against a rate-limited API endpoint.
This commit fixes the issue reported at packages/world-vercel/src/encryption.ts:138
**Bug Analysis:**
In `packages/world-vercel/src/encryption.ts`, the `fetchRunKey` function handles HTTP 429 (rate limited) responses by reading the `retry-after` header and computing a delay. The original code was:
```js
const retryAfter = response.headers.get('retry-after');
const delay = retryAfter
? Number.parseInt(retryAfter, 10) * 1000
: BASE_DELAY_MS * 2 ** attempt + Math.random() * 500;
```
Per RFC 9110, the `Retry-After` header can contain either a number of seconds OR an HTTP date string (e.g., `"Wed, 21 Oct 2015 07:28:00 GMT"`). When the header contains a date string or any non-numeric value, `Number.parseInt(retryAfter, 10)` returns `NaN`. Since the header string is truthy (non-empty), the ternary takes the parseInt path rather than the exponential backoff fallback. `NaN * 1000` evaluates to `NaN`, and `setTimeout(r, NaN)` resolves immediately (~0ms, confirmed by testing). This causes rapid-fire retries (up to 5 iterations with zero delay) against a rate-limited API endpoint, wasting resources and potentially worsening the rate-limiting situation.
**Fix:**
Parse the integer first, then check if the result is NaN before using it. If NaN, fall back to exponential backoff with jitter. This matches the established pattern already used in `packages/world-vercel/src/utils.ts` (lines 309-315) where the same header is properly guarded with `!Number.isNaN(parsed)`.
```js
const retryAfterHeader = response.headers.get('retry-after');
const parsedRetryAfter = retryAfterHeader
? Number.parseInt(retryAfterHeader, 10)
: NaN;
const delay = !Number.isNaN(parsedRetryAfter)
? parsedRetryAfter * 1000
: BASE_DELAY_MS * 2 ** attempt + Math.random() * 500;
```
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: TooTallNate <n@n8.io>
* fix: add --decrypt to e2e CLI inspect calls
With encryption enabled on the nextjs-turbopack workbench, CLI inspect
output shows encrypted data as placeholders. The e2e tests check for
actual output values, so they need --decrypt to see the real data.
* fix: remove manual 429 retry, rely on undici RetryAgent; add changeset
The shared RetryAgent from getDispatcher() already handles 429/5xx
retries with exponential backoff and Retry-After header support.
The manual retry loop in fetchRunKey was redundant.
Also add changeset for the VERCEL=1 external context fix.
* refactor: use @ts-expect-error on dispatcher instead of as any cast
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
|
||
|
|
c71befe8ec |
fix(core): premature suspension when hooks have buffered payloads with concurrent pending entities (#1294)
* test: reproduce hook+sleep promiseQueue regression Add failing tests that reproduce a regression from #1246 where the sleep's WorkflowSuspension fires before all hook payloads are delivered when a hook and sleep run concurrently. Root cause: when the null event fires, the sleep queues a suspension through promiseQueue. After the first hook payload resolves, subsequent hook payload resolutions are queued AFTER the sleep suspension, causing the workflow to terminate prematurely via Promise.race. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: expand tests to isolate bug to hook + pending entity pattern Add control tests proving sequential steps are NOT affected by the promiseQueue regression, isolating the bug to hooks specifically: Failing (hook-based): - hook + sleep: all 3 payloads → step invocation - hook + sleep: 2 payloads → return - hook + incomplete step: 2 payloads → return Passing (step-based controls): - sleep + sequential steps: both step events exist - sleep + sequential steps: only 1st step completed - incomplete step + sequential steps: all step events exist - hook only (no concurrent entity): payloads + step The bug is: any entity that queues a suspension through promiseQueue at null-event time preempts hook payload delivery, because hooks buffer payloads in payloadsQueue and only resolve them one-at-a-time as the workflow code iterates. Steps are unaffected because each step has its own events consumed before null fires. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): move WorkflowSuspension calls back to setTimeout(0) from promiseQueue The promiseQueue refactor (#1246) moved ALL promise resolutions through the queue, including WorkflowSuspension calls. This caused premature termination when hooks had buffered payloads and a concurrent entity (sleep or incomplete step) was pending: 1. Null event fires → all subscribers run 2. Sleep/step queues WorkflowSuspension via promiseQueue.then() 3. Hook's next payload resolution is also queued via promiseQueue.then() 4. Suspension fires first → Promise.race terminates workflow 5. Hook payload never delivered → infinite retry loop Fix: move WorkflowSuspension calls back to setTimeout(0) (macrotask). Suspensions fire AFTER all microtask-based deliveries (promiseQueue resolve/reject for step results and hook payloads) have completed. Non-suspension resolve/reject calls remain on promiseQueue for deterministic ordering of data delivery. Affected paths: - step.ts: null event handler - sleep.ts: null event handler - hook.ts: null event handler, eventLogEmpty suspension, dispose suspension * fix(core): use pendingDeliveries counter + scheduleWhenIdle for suspensions Replace all nested setTimeout/promiseQueue suspension patterns with a clean idle-polling mechanism: 1. pendingDeliveries counter: incremented before async hydration (step results, hook payloads), decremented in finally block after delivery 2. scheduleWhenIdle(ctx, fn): polls via setTimeout(0) → check counter → if > 0, wait for promiseQueue.then() → repeat. Only fires fn when pendingDeliveries reaches 0. This correctly handles: - Sync deserialization (no encryption): counter is 0, fires immediately after first setTimeout(0) - Async deserialization (with encryption): waits for decryption to complete before firing - Multi-round hook payload delivery: each createHookPromise() call increments the counter, preventing premature suspension between delivery rounds Also adds payloadsQueue.length check to hook null handler — don't trigger suspension if buffered payloads can satisfy pending awaits. Tests now run in both sync and async modes (14 total = 7 scenarios x 2). All 164 tests pass. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Nathan Rajlich <n@n8.io> |
||
|
|
36a901d2d2 |
feat: expose workflow and step names in metadata functions (#1285)
* feat: expose workflow and step names in metadata functions Add `workflowName` to `WorkflowMetadata` and `stepName` to `StepMetadata`, making them available via `getWorkflowMetadata()` and `getStepMetadata()`. This is useful for adding context to structured logs, collating logs during investigations, and bucketing errors by workflow/step name. Closes #1103 * add changeset * Apply suggestion from @TooTallNate Signed-off-by: Nathan Rajlich <n@n8.io> --------- Signed-off-by: Nathan Rajlich <n@n8.io> |
||
|
|
456c1aa455 | Add vitest plugin for testing full workflows without setting up a server (#1237) | ||
|
|
30e24d441e |
Merge commit from fork
* Prevent deterministic tokens in createWebhook Remove the `token` option from `WebhookOptions` to prevent unauthorized access to the public webhook endpoint. Webhook tokens are now always randomly generated. Deterministic tokens remain available for `createHook()` with server-side `resumeHook()`. **BREAKING CHANGE**: `createWebhook()` no longer accepts a `token` option. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR feedback: throw on token, revert unnecessary doc changes, replace examples - createWebhook() now throws an error if token is passed at runtime instead of silently stripping it - Revert unnecessary changes to create-hook.mdx (only keep slack_webhook -> slack_messages prefix change) - Replace Slack webhook examples in create-webhook.mdx and hooks.mdx with generic event collector examples that make sense without deterministic tokens Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address andresriancho's feedback: stricter token check, add unit test - Use `token !== undefined` instead of `if (token)` for correctness (empty string should also be rejected) - Add unit test verifying createWebhook({ token: "anything" }) throws the expected error message Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * change changeset to minor * Fix docs typechecker to actually resolve workflow module types The docs typechecker was silently failing to resolve `workflow` imports because: 1. repoRoot was computed 4 levels up instead of 3 2. No path mappings for workspace packages 3. Missing TS lib files for disposable and DOM iterables This meant all imported symbols became `any` and type errors in docs code samples went undetected. Fixes: - Fix repoRoot: '../../../..' -> '../../..' - Add path mappings for workflow, @workflow/core, @workflow/ai, etc. - Add lib.dom.iterable.d.ts and lib.esnext.disposable.d.ts - Fix regex that ate first line of some code blocks - Auto-skip error demonstration code blocks - Add respondWith to global Request for webhook doc patterns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Revert "Fix docs typechecker to actually resolve workflow module types" This reverts commit 02d42ac866e6db1b6d016339739344cdd9538e90. * chore: trigger CI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
a9fea9132e |
Update workbench tests to build and run outside of monorepo (#1230)
* Setup fixes * ci: run local e2e against staged tarball workbenches * ci: update staged workbench tarball setup script * chore: set nextjs workbenches back to next 16.1.6 * update lock * test(e2e): resolve workbench path from WORKBENCH_APP_PATH * fix: address deferred builder issues outside monorepo * ci: stage tarball workbenches only for nextjs local e2e * fix(next): discover deferred steps imported via workflows * test(core): gate deferred step-discovery dev test to canary * test(e2e): cover cross-file imported step in build/start lanes * fix(e2e): use local manifest in local runs and relax dev rebuild timeout * fix(workbench): add imported-step workflow symlink for sveltekit/astro * test(e2e): scope imported-step workflow test to nextjs lanes * fix(next): rebuild deferred entries on discovered file updates * fix(next): watch transitive deferred step deps for dev rebuilds * fix(next): restore socket-driven deferred step rebuilds * add changeset * chore: address review feedback on deferred e2e updates * fix(cli): guard stream flush against closed write streams |
||
|
|
809339ba1c |
fix(builders): enable directive discovery in dot-prefixed files and directories (#1228)
* fix(builders): enable directive discovery in dot-prefixed files and directories The glob-based file scanning in getInputFiles() was using tinyglobby's default behavior which skips dot-prefixed files/directories. This prevented discovering files with 'use step' / 'use workflow' directives inside paths like .config/step.ts or .hidden-workflow.ts. Switch to relative glob patterns with per-directory cwd and dot: true to ensure dot-files are scanned while still respecting the explicit ignore list (.git, .next, .vercel, etc.). * test: add E2E tests for dot-directory directive discovery Add workbench fixtures and E2E tests verifying that 'use step' / 'use workflow' directives inside dot-prefixed directories (.well-known/agent/) are correctly discovered, included in manifests, and executed at runtime. * fix: normalize paths in getInputFiles tests for Windows compatibility tinyglobby returns forward-slash paths even on Windows, while Node's path.join() uses backslashes. Normalize both sides to forward slashes before comparison. * refactor: use dirname() instead of join(.., '..') in test helper Cleaner and more idiomatic way to get the parent directory. * fix: add .nuxt and other build tool dot-directories to ignore list With dot: true enabled, framework build output directories like .nuxt/ are now traversed and their generated files picked up as input files. This caused a circular dependency in the nuxt builder where generated .nuxt/workflow/steps.mjs was being re-discovered as an input file. Add .nuxt, .turbo, .cache, .yarn, and .pnpm-store to the ignore list, matching the directories already ignored by the eager builder's watcher. |
||
|
|
0ecb9872c1 | Disable lazy discovery for turbopack workbench (#1223) | ||
|
|
02681dce4a |
feat(core): add hook.dispose() method to release hook tokens early (#1181)
* feat(core): add hook.dispose() method to release hook tokens early Add a `dispose()` method to the Hook interface that allows workflows to explicitly release hook tokens for reuse by other workflows while the current workflow is still running. This enables handoff patterns where one workflow can transfer a hook token to another workflow. - Add `dispose()` method to Hook interface in create-hook.ts - Implement dispose functionality in workflow/hook.ts - Add HookDisposedInvocationQueueItem to global.ts - Handle hook_disposed events in suspension-handler.ts - Update documentation in hooks.mdx and create-hook.mdx - Add e2e test for hook token reuse after explicit disposal https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * feat(core): implement TypeScript Disposable spec for hooks Add Symbol.dispose to Hook interface to support the TC39 Explicit Resource Management proposal. This allows hooks to be used with the `using` keyword for automatic disposal when exiting scope. https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * docs: prefer `using` keyword for hooks and webhooks Update all documentation and e2e tests to use the `using` keyword as the recommended approach for creating hooks and webhooks. This leverages the TC39 Explicit Resource Management proposal for automatic disposal. - Update e2e tests to use `using` syntax - Update foundational hooks guide to recommend `using` - Update create-hook API reference with `using` examples - Update create-webhook API reference with `using` examples - Update example workflow to use `using` https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * chore: simplify `using` examples in docs and e2e tests Remove unnecessary block scopes and excessive comments about automatic disposal. Block scopes are only used when early disposal is relevant (like in the handoff test). https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * docs: add brief explanation of `using` in first examples Add a one-liner explaining the `using` keyword in the intro examples of the API reference docs, so new users understand the syntax. https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * fix: address PR review comments - Restore code highlights (`[!code highlight]`) that were unintentionally removed from pre-existing doc examples - Move `using` explanation from prose to inline code comment in intro examples - Add `{/* @skip-typecheck */}` to incomplete manual dispose() snippet - Add 409 (conflict/duplicate) error handling for hook_disposed events in suspension handler to handle workflow re-invocation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: polyfill Symbol.dispose in workflow VM context The workflow VM sandbox doesn't have Symbol.dispose/Symbol.asyncDispose available, causing `using` keyword to fail with "Symbol.dispose is not defined" at runtime. Add polyfill in the VM context creation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use VM's Symbol.dispose for hook disposable support The workflow VM sandbox has its own Symbol object with a polyfilled Symbol.dispose. The hook object was using the host's Symbol.dispose, which is a different symbol instance. The SWC-compiled `using` keyword looks up the VM's Symbol.dispose on the object, causing "Object not disposable" errors. Fix by setting Symbol.dispose on the hook object dynamically using the VM's globalThis.Symbol.dispose from the orchestrator context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert webhookWorkflow to create all webhooks upfront The webhookWorkflow e2e test creates 3 webhooks that must all exist before the test sends HTTP requests. Using `using` with sequential creation meant only the first webhook existed at the first suspension point. Revert to `const` since all webhooks need to be created upfront. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: eliminate hook_disposed queue item, use flag pattern for disposal Instead of adding a separate HookDisposedInvocationQueueItem to the queue on dispose, keep the HookInvocationQueueItem throughout the hook lifecycle and track state with flags (hasCreatedEvent, disposed). A closure variable (hasDisposedEvent) makes disposeHook() a pure no-op on replay, avoiding redundant server calls and 409 errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: delete hook from invocations queue on hook_disposed terminal event Match the pattern used by steps (step_completed/step_failed) and waits (wait_completed) where the queue item is removed on the terminal event. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: verify dispose() is safe when called twice after replay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add comprehensive edge case tests for hook disposal - WorkflowSuspension counts disposed hooks separately from active hooks - Dispose after hook_created replay produces correct suspension - Dispose before first suspension (needs both create + dispose) - Multiple hooks where only one is disposed - Dispose on a conflicted hook is safe (no crash) - Symbol.dispose calls disposeHook correctly (using keyword pattern) - Iterator break without dispose keeps hook alive in queue - Await after dispose on first invocation triggers suspension Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove unsafe cast by adding Symbol.dispose to hook object literal Add [Symbol.dispose] directly to the hook object so it satisfies the Hook<T> type without `as unknown as`. The VM's Symbol.dispose is still added separately when it differs from the host's. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address remaining Copilot review comments - Rename test to match new behavior (hooks stay in queue, not removed) - Use neutral "processed" verb in WorkflowSuspension message when mixed item types are present - Remove extends Disposable from Hook interface to avoid requiring lib.esnext.disposable in downstream consumers (explicit [Symbol.dispose]() method is still declared on the interface) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: drain pending promises in disposeHook to prevent orphaned awaits When dispose() is called while a promise is pending (e.g., iterator suspended on yield await this, or direct await hook after dispose), the promise would hang forever since the event consumer will never deliver another hook_received. Now disposeHook() clears the promises array and triggers a WorkflowSuspension so the runtime processes the disposal cleanly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove workflow from changeset, keep only @workflow/core Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
e55c63678b |
Update dependencies (#1217)
Bump zod 4.1→4.3, esbuild 0.25→0.27, turbo 2.5→2.8, biome 2.2→2.4, vitest 3.2→4.0, @vercel/functions 3.1→3.4, @vercel/oidc 3.0→3.2, @vercel/queue 0.0→0.1, @oclif/core 4.0→4.8, AWS SDK 3.609→3.972. Fix duplicate workspace name for nitro/nitro-v3 (turbo 2.8 strictness). Fix vitest 4 constructor mock in world-vercel queue tests. drizzle-orm 0.44→0.45, drizzle-kit 0.31.6→0.31.9, dotenv 16→17, postgres 3.4.7→3.4.8, hono 4.9→4.12, @hono/node-server 1.19.5→1.19.9, enhanced-resolve 5.18→5.19, tinyglobby 0.2.14→0.2.15, @oclif/plugin-help 6.2.31→6.2.37, semver 7.7.3→7.7.4, watchpack 2.4→2.5, @nuxt/kit 4.2→4.3, @nuxt/schema 4.2→4.3, exsolve 1.0.7→1.0.8, shiki 3.13→4.0, streamdown 1.6→2.3, lucide-react 0.469→0.575, tailwind-merge 2.5→3.5, react-virtuoso 4.12→4.18, @xyflow/react 12.9→12.10, @react-router/* 7.13.0→7.13.1, @testcontainers/postgresql 11.7→11.12, lint-staged 16.1→16.2, @changesets/cli 2.29.5→2.29.8 Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
f0823dc79b |
[web-shared][web] Fix trace viewer pagination issues (#1182)
* [web-shared] Fix trace viewer pagination issue * [web-shared] Fix trace viewer pagination issue * [web-shared] Fix trace viewer pagination issue * [workflow o11y] bump package and fix trace viewer pagination issue * [workflow o11y] bump package and fix trace viewer pagination issue |
||
|
|
54879835f3 | Fix pages router default args: use empty array instead of [42] (#1081) | ||
|
|
0946dad01b |
Remove "workflow/internal/serialization" export (#1082)
* Remove "workflow/internal/serialization" export Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there. * Fix: Removing the `workflow/internal/serialization` export from package.json breaks 5 files in `packages/world-testing` that still import `hydrateWorkflowReturnValue` from that path. Co-authored-by: TooTallNate <n@n8.io> * lockfile --------- Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> |
||
|
|
c2b4fe9906 |
fix(core): detect and fatal error on orphaned/invalid events (#1055)
* fix(core): detect and fatal error on orphaned/invalid events in EventsConsumer When an event log has duplicate or invalid events (e.g., 2 wait_completed for a single wait_created), the EventsConsumer gets stuck: the orphaned event has no callback to consume it, so eventIndex never advances, blocking all subsequent events and hanging the workflow forever. This adds deferred orphaned event detection that raises a WorkflowRuntimeError instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * add corrupted-event-log error slug and docs page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * add changeset for orphaned event detection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * address PR feedback on docs and JSDoc - Add JSDoc to EventsConsumer constructor's onUnconsumedEvent parameter - Rewrite docs to clarify this is a SDK/server bug, not user code - Remove cancel/programmatic retry advice; run is already marked failed - Emphasize error is uncatchable inside workflow code - Add steps: upgrade package, retry via dashboard/CLI, report issue on GitHub Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * address PR review: structured options, required callback, reorder docs - Change EventsConsumer constructor to use structured EventsConsumerOptions object - Make onUnconsumedEvent required (all callsites provide it) - Move corrupted-event-log card to last position in errors index - Update all callsites in tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix docs code sample to pass type-checking Use correct imports (getRun from workflow/api) and declare variables to satisfy the docs-typecheck CI job. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * add e2e test for parallel sleep (Promise.all with 10x sleep) Adds parallelSleepWorkflow that does Promise.all with 10 concurrent sleep('1s') calls, and an e2e test asserting it completes or fails within a reasonable time (not hanging). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix parallelSleep e2e test: await returnValue instead of checking status run.status returns immediately and the workflow is still 'running'. Use run.returnValue which polls until completion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix parallelSleep e2e: assert parallel completion under 10s The test verifies 10 concurrent sleep('1s') calls complete in parallel (~1s) rather than serially (10s) or hanging indefinitely. 30s timeout is sufficient since the workflow should finish in ~1-2s. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
f5ae600db4 | fix(e2e): stabilize 5xx step_completed fault injection (#1046) | ||
|
|
30076ecea8 | Enable lazyDiscovery in workbench (#1044) | ||
|
|
8d117cd219 |
Retry 5xx errors from workflow-server in step handler (#1011)
* Retry 5xx errors from workflow-server in step handler Add `withServerErrorRetry` helper that retries world calls on 5xx errors with exponential backoff (500ms, 1s, 2s ≈ 3.5s total). Applied to all `world.events.create` calls in the step handler so transient workflow-server errors don't consume step attempts. If retries are exhausted, the error is thrown to the queue for higher-level retry without burning a step attempt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address PR review comments on 5xx retry handling - Fix misleading `maxAttempts` log field to `maxRetries` in withServerErrorRetry - Update step-handler comment to accurately note that queue retries may still consume step attempts since step_started has already incremented - Add unit tests for withServerErrorRetry (7 tests covering success, retry/backoff, exhaustion, and non-5xx passthrough) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add tests for 429/5xx retry handling Unit tests for withThrottleRetry and withServerErrorRetry helpers, plus an e2e test that exercises the 5xx retry codepath during step execution via run-scoped fault injection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Wrap VQS errors in WorkflowAPIError and add retry to queueMessage VQS throws its own error types (InternalServerError, ConsumerDiscoveryError, ConsumerRegistryNotConfiguredError) that don't match WorkflowAPIError.is(). Wrapping them at the world-vercel boundary enables withServerErrorRetry in queueMessage() to automatically retry transient queue failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update changeset to include @workflow/world-vercel Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove queue retry logic per review feedback Queue retrying will be handled natively by the @vercel/queue client instead. Reverts VQS error wrapping and withServerErrorRetry in queueMessage(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
bb45816f0a | Fix hydration mismatch in Next.js workbench workflow defaults (#1009) | ||
|
|
fcfaf8bbaa | Support step function serialization in client mode (#924) | ||
|
|
5b5b36a03b |
[e2e] Remove /api/hook and /api/test-health-check endpoints, call APIs directly
- Remove debug logging from bench.bench.ts - Remove awaitReturnValue() wrapper, use run.returnValue directly in benchmarks - Add changeset for Nitro builder manifest fix - Refactor hookWorkflow tests to call getHookByToken()/resumeHook() directly (pass hook object to resumeHook to avoid duplicate lookups) - Refactor queue-based health check test to call healthCheck() directly - Assert specific error message for invalid hook token test - Remove /api/hook and /api/test-health-check endpoints from all workbench apps (nextjs-turbopack, nextjs-webpack, vite, hono, express, fastify, sveltekit, astro, nuxt, nitro-v2, nitro-v3, nest, example) |
||
|
|
82c209c669 | Add missing env for tests on deploy (#973) | ||
|
|
86f62f2779 |
Refactor e2e tests to no longer use "trigger" endpoint (#958)
## Summary
Refactors the E2E tests to call `start()` from `workflow/api` directly instead of going through the `/api/trigger` HTTP endpoint in each workbench app. This removes a layer of indirection — the tests now use the same API that users would use to start workflows programmatically.
### Before
```ts
const run = await triggerWorkflow('addTenWorkflow', [123]);
const returnValue = await getWorkflowReturnValue(run.runId);
```
- `triggerWorkflow()` sent an HTTP POST to `/api/trigger` on the workbench app
- The workbench app looked up the workflow function, called `start()`, and returned the run ID
- `getWorkflowReturnValue()` polled `GET /api/trigger?runId=...` until the workflow completed
### After
```ts
const run = await start(await e2e('addTenWorkflow'), [123]);
const returnValue = await run.returnValue;
```
- `e2e()` / `getWorkflowMetadata()` fetches the manifest from `/.well-known/workflow/v1/manifest.json` to look up the correct `workflowId`
- `start()` is called directly from the test process via the configured World
- `run.returnValue` polls for completion via the World (no HTTP polling endpoint needed)
### Changes
**`packages/core/e2e/e2e.test.ts`**
- Removed `triggerWorkflow()` and `getWorkflowReturnValue()` helpers
- Added `fetchManifest()` to fetch and cache the workflow manifest from the deployment
- Added `getWorkflowMetadata(file, fn)` to look up `{ workflowId }` from the manifest
- Added `e2e(fn)` shorthand for the common case of `workflows/99_e2e.ts`
- All tests call `start()` and `run.returnValue` directly
- Error tests use `.catch()` to inspect `WorkflowRunFailedError`
- Output stream tests use `run.getReadable()` directly (skipped on local world where cross-process streaming isn't supported)
- `beforeAll` configures the local World with the correct data directory and base URL
- Pages Router tests use `startWorkflowViaHttp()` to specifically validate the HTTP trigger path
**Workbench apps (hono, express, fastify, nest)**
- Removed `/api/trigger` route handlers
- Kept `/api/hook`, `/api/test-direct-step-call`, `/api/test-health-check` endpoints
- Re-added `_workflows.js` side-effect import for hono/express/fastify to maintain Nitro's HMR dependency graph
**Deleted trigger-only route files** from: nextjs-turbopack, nextjs-webpack, vite, sveltekit, astro, nuxt, nitro-v2, nitro-v3, example
**`.github/workflows/tests.yml`**
- Added `WORKFLOW_PUBLIC_MANIFEST: '1'` to all E2E test jobs
### Dependencies
Stacked on #963 which adds `WORKFLOW_PUBLIC_MANIFEST` support to all framework builders.
|
||
|
|
661724c01e |
Expose workflow manifest via HTTP when WORKFLOW_PUBLIC_MANIFEST=1 (#963)
## Summary
When `WORKFLOW_PUBLIC_MANIFEST=1` is set, each framework builder exposes the workflow manifest at `/.well-known/workflow/v1/manifest.json` via the most appropriate mechanism for the framework:
- **Next.js**: Copies manifest to `public/.well-known/workflow/v1/manifest.json` (served as a static file)
- **SvelteKit**: Copies manifest to `static/.well-known/workflow/v1/manifest.json` (served as a static file)
- **Vercel Build Output API** (example workbench): Copies manifest to `.vercel/output/static/.well-known/workflow/v1/manifest.json` (served as a static file)
- **Nitro** (vite, hono, express, fastify, nuxt, nitro-v2, nitro-v3): Registers a virtual handler that reads and serves the manifest JSON
- **Astro**: Generates a `manifest.json.js` page route that returns the manifest JSON
- **NestJS**: Adds a `@Get('manifest.json')` endpoint on the `WorkflowController` (gated by the env var at runtime)
### Other changes
- `BaseBuilder.createManifest()` now returns the manifest JSON string so framework builders can use it
- Added `shouldExposePublicManifest` getter to `BaseBuilder`
- Fixed Astro `LocalBuilder` which was missing the `createManifest()` call that all other framework builders have
- Removed unused `public/` directory and static file copy from the example workbench build script
- Added `WORKFLOW_PUBLIC_MANIFEST` to `turbo.json` build task env so Vercel builds can access it
- Added generated manifest paths to `.gitignore`
|
||
|
|
fed805a15f |
Bump Next.js and React in workbenches (#944)
* Bump Next.js and React in workbenches - Next.js: 16.0.10 → 16.1.6 in nextjs-turbopack, nextjs-webpack, swc-playground workbenches - Next.js: 16.0.10 → 16.1.6 in @workflow/next devDependencies (for type compatibility) - React/React-DOM: ^19.0.0/19.2.1 → 19.2.4 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add changeset Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> |
||
|
|
ad21f3f608 |
Add configurable moduleSpecifier value for SWC Playground (#936)
|
||
|
|
b895446bc3 | Show git commit sha in swc-playground (#934) | ||
|
|
08e1169061 | Add "swc-playground" workbench app (#930) | ||
|
|
2453b29426 |
Make wf build --manifest-file include steps / classes metadata (#931)
|
||
|
|
6208616d06 |
Add sequential step benchmarks and fix broken benchmark infrastructure (#845)
* Add 50, 100, 500 concurrent step benchmarks Enable Promise.all and Promise.race benchmarks for 50, 100, and 500 concurrent steps (previously 100+ were skipped). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add 50, 100, 500 sequential step benchmarks Extends the sequential step benchmarks to test workflows with 50, 100, and 500 sequential steps in addition to the existing 10 step test. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add full/quick benchmark suite toggle for CI - Add BENCHMARK_FULL_SUITE env var to control which benchmarks run - Quick suite (default for PRs): 10, 25, 50 step benchmarks - Full suite (main branch, manual dispatch): adds 100, 500 step benchmarks - Add workflow_dispatch input to manually trigger full suite from GitHub UI - Skip 100+ sequential and concurrent step benchmarks by default This keeps PR benchmarks fast while allowing full stress testing on demand. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add 25 sequential steps benchmark Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix benchmark API calls to use binary format PR #853 changed the workflow trigger API to expect binary data (application/octet-stream) instead of JSON. The e2e tests were updated but the benchmark file was missed, causing all benchmarks to fail silently since Jan 28. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Only run full benchmark suite on manual dispatch Remove automatic full suite on main branch pushes - only run full suite when manually triggered with full_suite=true. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> |
||
|
|
c1d7c8dbb4 |
Add support for "use step" functions in class instance methods (#777)
Added support for `"use step"` directive in class instance methods, allowing instance methods to be used as workflow steps. ### What changed? - Modified the SWC plugin to recognize and transform instance methods with the "use step" directive - Added registration logic for instance method steps using `ClassName.prototype.methodName` - Implemented proper serialization of class instances to preserve the `this` context across workflow/step boundaries - Added comprehensive end-to-end tests for instance method steps - Updated error handling to allow "use step" in instance methods while still preventing "use workflow" in instance methods ### How to test? The PR includes a new end-to-end test `instanceMethodStepWorkflow` that demonstrates the functionality: 1. Run the e2e tests to verify the new instance method step functionality 2. The test creates a `Counter` class with instance methods marked as steps 3. It verifies that the instance methods can be called as steps with proper serialization of the `this` context 4. It also verifies that multiple instances of the same class can be used independently ### Why make this change? This change enables a more natural object-oriented programming model when working with workflows. Previously, only static methods, standalone functions, and object methods could be marked as steps. Now, developers can create classes with instance methods that are steps, allowing for better encapsulation and more intuitive code organization. This is particularly useful for complex workflows that need to maintain state across multiple step invocations. |
||
|
|
b5296a7a32 |
Add discovered serializable classes in all context modes (#874)
This PR ensures that all classes with custom serialization are automatically included in all bundle contexts (step, workflow, client) to ensure proper serialization/deserialization when crossing execution boundaries: - Classes defined in any context can now be properly serialized when passing data between: - Client → Workflow (when starting workflows) - Workflow → Step (when calling steps) - Step → Workflow (when returning step results) - Workflow → Client (when returning workflow results) - The build system now automatically discovers all files containing serializable classes and includes them in each bundle, regardless of where the class is originally defined. - No manual configuration is required - cross-registration happens automatically during the build process. |
||
|
|
1060f9d04a | Change user input/output to be binary data at the World interface (#853) | ||
|
|
9ec21584d6 | Update docs template (#552) | ||
|
|
f2ab6ee179 |
Fix Nest workbench app build (#865)
* Use proper OpenAI import
```
Error: ../example/workflows/5_hooks.ts(36,22): error TS2351: This expression is not constructable.
Type 'typeof import("/vercel/path0/node_modules/.pnpm/openai@6.1.0_ws@8.18.3_zod@4.1.11/node_modules/openai/index")' has no construct signatures.
```
* .
|
||
|
|
50f50f44d7 | NestJS framework support (#840) | ||
|
|
4966b728a8 |
implement event-sourced architecture (#621)
* perf: implement event-sourced architecture
* Apply suggestions from code review
* Improve invalid event log handling in step/hook/wait
* Handle serialized workflow run errors correctly
* log error in failing test
* Handle queue idempotency in vercel world
* hotfix for error propogation
* Fix: Incorrect HTTP status code 409 should be 410 for terminal run state rejections in postgres storage
* Fix: The code attempts to pass an unsupported `fatal` property when creating a `step_failed` event. The TypeScript schema for `step_failed` events only allows `error` and `stack` properties, so the `fatal` property causes a compilation error.
This commit fixes the issue reported at packages/core/src/runtime/step-handler.ts:133-139
## TypeScript error: Invalid property 'fatal' in step_failed event
**What fails:** TypeScript compilation fails in `packages/core` due to an invalid property in the `step_failed` event creation.
**How to reproduce:**
```bash
cd /vercel/sandbox/primary
pnpm run -F @workflow/core build
```
**Result:**
```
src/runtime/step-handler.ts(133,32): error TS2769: No overload matches this call.
Overload 1 of 2, '(runId: null, data: { eventType: "run_created"; ... }, gave the following error.
Argument of type 'string' is not assignable to parameter of type 'null'.
Overload 2 of 2, '(runId: string, data: CreateEventRequest, params?: CreateEventParams | undefined): Promise<EventResult>', gave the following error.
Object literal may only specify known properties, and 'fatal' does not exist in type '{ error: any; stack?: string | undefined; }'.
```
**Issue:** The code attempted to pass a `fatal: true` property in the `eventData` object when creating a `step_failed` event. However, the event schema defined in `packages/world/src/events.ts` for `StepFailedEventSchema` only allows `error` and `stack` properties - the `fatal` property is not part of the schema.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
* Fix: Code silently skips updating workflowRun if result.run is undefined, causing workflows to be incorrectly skipped instead of throwing an error
* Add hook_conflict event type for duplicate token detection
Implements hook_conflict events across all world implementations to handle
cases where a workflow attempts to use a hook token already claimed by another
workflow. Instead of throwing errors, the system now records hook_conflict
events in the event log, enabling deterministic replay.
- Add HookConflictEvent schema to @workflow/world
- Implement hook_conflict in world-local, world-postgres, and suspension-handler
- Update hook consumer to reject promises with WorkflowRuntimeError on conflict
- Add HOOK_CONFLICT error slug with documentation
- Add e2e and unit tests for conflict scenarios
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add changeset for hook_conflict events
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add unit tests for hook_conflict handling
- Add tests for hook_conflict event in workflow.test.ts
- Fix world-postgres test to not expect removed message field
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Improve hook-conflict.mdx error guide
- Remove redundant third point in 'Why This Happens' section
- Add example showing how to handle WorkflowRuntimeError for hook conflicts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix docs validation: add hook-conflict to errors index
- Fix broken link in hook-conflict.mdx (/docs/foundations/webhooks -> /docs/api-reference/workflow/create-webhook)
- Add hook-conflict to errors index page so it's discoverable by the docs link validator
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix world-local tests for hook_conflict event behavior
Update tests to expect hook_conflict events instead of thrown errors when
duplicate hook tokens are used. This aligns with the new event-sourced
approach where conflicts are recorded as events rather than thrown.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add specVersion property to World interface for backwards compatibility
- Add specVersion property to World interface to track world package version
- Add specVersion to WorkflowRun schema and run_created event data
- World implementations (vercel, local, postgres) set specVersion from npm version
- Server can use specVersion to route operations based on world version
- Add specVersion display to observability UI attribute panel
- Add spec_version column to postgres runs schema
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add migration for spec_version column in postgres schema
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add drizzle migration journal and snapshot for spec_version column
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Regenerate postgres migration using drizzle-kit
- Properly generates migration with drizzle-kit CLI
- Removes deprecated 'paused' status from enum
- Adds spec_version column
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add backwards compatibility for event-sourced runs
- Add RunNotSupportedError for runs requiring newer world versions
- Add semver-based version utilities (isLegacyVersion) to @workflow/world
- World implementations check specVersion and route to legacy handlers
- Legacy runs (< 4.1.0): run_cancelled skips event storage, wait_completed stores event only
- New runs always get current world version (4.1.0-beta.0)
- Make EventResult.event optional for legacy compatibility
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Refactor spec version from semver strings to integers
Replace semver-based version compatibility with explicit integer spec versions:
- SPEC_VERSION_LEGACY (1): pre-event-sourcing runs
- SPEC_VERSION_CURRENT (2): event-sourced architecture
Use branded SpecVersion type to enforce importing from @workflow/world.
Remove semver dependency from world, world-local, and world-postgres.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Store error as CBOR in postgres world for consistency with input/output
- Add error_cbor bytea columns to workflow_runs and workflow_steps tables
- Deprecate text error column, rename to errorJson with fallback parsing
- Remove JSON.stringify from error writes (run_failed, step_failed, step_retrying)
- Add parseErrorJson helper for backwards compatibility with legacy data
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use WorkflowRuntimeError and improve run entity handling in core runtime
- Replace generic Error with WorkflowRuntimeError for runtime assertions
- Add explicit check for run entity in run_created response
- Use run.runId instead of event.runId for consistency
- Use actual run status instead of hardcoded 'pending' in attributes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add specVersion to Step, Hook, and Event entities
- Add specVersion field to Step, Hook, and Event interfaces in @workflow/world
- Add spec_version column to steps, hooks, events tables in postgres schema
- Set specVersion to SPEC_VERSION_CURRENT when creating entities in all worlds
- Update migration to include spec_version columns for all entity tables
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Refactor world-local storage into modular files
Split storage.ts (1041 lines) into smaller, focused modules:
- storage/filters.ts: Data filtering helpers
- storage/helpers.ts: ULID and date utilities
- storage/hooks-storage.ts: Hook CRUD operations
- storage/legacy.ts: Legacy event handling
- storage/runs-storage.ts: Run get/list operations
- storage/steps-storage.ts: Step get/list operations
- storage/events-storage.ts: Event create/list operations
- storage/index.ts: Main composition
Also extracted test helpers to test-helpers.ts for reusability.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove genversion and World.specVersion property
The World.specVersion string property was never actually read - only
the numeric SPEC_VERSION_CURRENT is used for backwards compatibility.
- Remove genversion dependency and generated version.ts from @workflow/world
- Remove specVersion property from World interface and all implementations
- Minor fix: correct error message to reference 'workflow' package
- Minor fix: correct error source priority in world-vercel events.ts
- Minor fix: update comment in runtime.ts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove genversion from world-local, world-postgres, and world-vercel
These packages no longer need genversion since we removed the
World.specVersion property in the previous commit.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove version.ts from .gitignore files
No longer needed after removing genversion.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add legacy/backwards compatibility tests
Tests for world-local and world-postgres covering:
- Legacy runs (specVersion < 2 or null/undefined)
- run_cancelled handling (updates run, no event stored)
- wait_completed handling (stores event only)
- Rejection of unsupported events
- Hook cleanup on cancellation
- Future runs (specVersion > current)
- Rejection with RunNotSupportedError
- Current version runs (normal processing)
- Legacy error parsing (errorJson field parsing)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add hook_received support for legacy runs
When resumeHook() is called on a legacy run (specVersion < 2), the
hook_received event was previously rejected. This adds support for
storing hook_received events on legacy runs without entity mutation,
matching the behavior of wait_completed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix missing genversion in world-vercel
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
* Remove deprecated workflow_completed, workflow_failed, and workflow_started events
Replace with run_completed, run_failed, and run_started equivalents.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add specVersion to EventWithRefsSchema in world-vercel
The manually-created EventWithRefsSchema was missing the specVersion field,
which caused specVersion to be stripped when using lazy (refs) mode for events.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Wire specVersion from client through world backends
- Core runtime now sends specVersion in run_created eventData
- world-local accepts specVersion from eventData (defaults to current)
- world-postgres accepts specVersion from eventData (defaults to current)
This matches workflow-server behavior where v2 endpoints accept
specVersion from the client, while v1 endpoints default to legacy.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Move specVersion to event object level, propagate to entities
- Add specVersion to BaseEventSchema (event level, not eventData)
- Remove specVersion from RunCreatedEventSchema.eventData
- Core runtime sends specVersion on event object
- world-local reads specVersion from event, propagates to run/step/hook entities
- world-postgres reads specVersion from event, propagates to run/step/hook entities
This ensures specVersion flows from client through event to all created entities.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update specVersion to be optional in types for backwards compatibility
- specVersion is optional in all entity schemas (runs, steps, hooks, events)
for backwards compatibility with legacy data in storage
- Runtime always sends specVersion on event requests
- world-local and world-postgres provide fallback to SPEC_VERSION_CURRENT
- Test helpers include specVersion in all event creation calls
- EventWithRefsSchema in world-vercel defaults specVersion to 1 for legacy
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix world-vercel queue tests missing VERCEL_DEPLOYMENT_ID setup
Two tests were calling queue.queue() without setting up
VERCEL_DEPLOYMENT_ID, causing them to fail with "No deploymentId
provided" error before reaching the code they were testing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add specVersion to all event creation calls in core package
The server's CreateEventSchemaV2 requires specVersion on all events,
but only run_created was sending it. This caused 400 Bad Request errors
for all other event types (run_started, run_completed, run_failed,
run_cancelled, step_created, step_started, step_completed, step_failed,
step_retrying, hook_created, hook_received, wait_created, wait_completed).
Now all event creation calls include specVersion: SPEC_VERSION_CURRENT.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
|
||
|
|
0b5cc48140 |
fix(builders): manifest missing workflow-only files (no steps), add tests (#831)
* Fix manifest missing workflow-only files (no steps), add tests Signed-off-by: voyager14 <21mh124@queensu.ca> * Update packages/builders/src/base-builder.ts Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> Signed-off-by: Michael Han <21mh124@queensu.ca> --------- Signed-off-by: voyager14 <21mh124@queensu.ca> Signed-off-by: Michael Han <21mh124@queensu.ca> Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> |
||
|
|
1843704b83 |
Add support for custom class instance serialization (#762)
Added support for custom class instance serialization across workflow/step boundaries.
### What changed?
- Introduced a new `@workflow/serde` package with `WORKFLOW_SERIALIZE` and `WORKFLOW_DESERIALIZE` symbols
- Enhanced the serialization system to handle custom class instances using these symbols
- Updated the SWC plugin to detect classes with serialization methods and register them
- Added class registry mechanism that works in both step and workflow contexts
- Implemented comprehensive tests for various serialization scenarios
### How to test?
The PR includes a new e2e test `customSerializationWorkflow` that demonstrates the feature:
```typescript
import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde';
// Define a class with custom serialization
class Point {
constructor(public x: number, public y: number) {}
static [WORKFLOW_SERIALIZE](instance: Point) {
return { x: instance.x, y: instance.y };
}
static [WORKFLOW_DESERIALIZE](data: { x: number; y: number }) {
return new Point(data.x, data.y);
}
}
// Use in workflow and steps
export async function customSerializationWorkflow(x: number, y: number) {
'use workflow';
const point = new Point(x, y);
const scaled = await transformPoint(point, 2);
// ...
}
```
Run the e2e test to verify that class instances are properly serialized and deserialized.
### Why make this change?
Previously, user-defined class instances couldn't be passed between workflows and steps without losing their prototype chain and methods. This change allows developers to define custom serialization/deserialization logic for their classes, enabling proper reconstruction of instances with their full functionality intact when crossing workflow/step boundaries.
|
||
|
|
344c90ff9f |
Add Next.js pages router entries handling (#792)
* Add Next.js pages router entries handling * update trigger-pages * Apply suggestions from code review Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> Signed-off-by: JJ Kasper <jj@jjsweb.site> --------- Signed-off-by: JJ Kasper <jj@jjsweb.site> Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> |
||
|
|
7906429541 |
Add support for serializing this when invoking step functions (#754)
Added support for serializing `this` context when invoking step functions. ### What changed? - Modified the step function implementation to capture and serialize the `this` context when it's defined and not the global object - Updated the step invocation queue item interface to include an optional `thisVal` property - Enhanced the step handler to apply the hydrated `thisVal` when executing step functions ### Why make this change? This enhancement allows step functions to be invoked with an explicit context object using standard JavaScript methods like `.call()` and `.apply()`. This is particularly useful for step functions that need to access properties from a specific context, enabling more flexible and idiomatic JavaScript patterns within workflows. |
||
|
|
a2fc53a0dc |
Support class static methods with "use step" / "use workflow" (#753)
The SWC compiler plugin had logic to walk through class static methods with "use step" / "use workflow", but no actual transformation was being applied. This fixes that. |
||
|
|
61fdb41e1b |
Add queue-based health check (#743)
* feat: add queue-based health check to bypass Deployment Protection
- Add HealthCheckPayloadSchema and HEALTH_CHECK_STREAM_PREFIX to @workflow/world
- Add healthCheck() method to Queue interface
- Update workflow and step handlers to detect and respond to health check messages
- Implement healthCheck() in world-local, world-vercel, and world-postgres
The queue-based health check sends a message through the queue pipeline,
which bypasses Vercel's Deployment Protection. The handler writes a response
to a stream that the caller reads to confirm health.
This complements the existing HTTP-based ?__health approach which still works
for local development and when bypass headers are available.
* refactor: move healthCheck to core package as utility function
Instead of adding healthCheck to the World interface (which duplicated
the same implementation across all worlds), this is now a utility function
in @workflow/core that takes the World as a parameter.
Usage:
import { healthCheck } from '@workflow/core';
const result = await healthCheck(world, 'workflow');
This is cleaner because:
- Single implementation instead of 3 identical ones
- World implementations remain simple
- No changes needed to the World interface
* .
* refactor: move health check types from world to core
Health check types (HealthCheckPayloadSchema, HealthCheckResult, etc.)
are now defined in @workflow/core since that's where they're used.
The HealthCheckPayloadSchema is still part of QueuePayloadSchema in
world (so the queue accepts health check messages), but it's not
exported from the public API.
* .
* Refactor health check implementation based on code review feedback (#746)
* Initial plan
* Address PR review comments: export types, fix race condition, improve error handling
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
* Add queue-based health check test and document security considerations
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
* Replace 'any' type with proper type guards for health check response
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
* Extract health check queue names as constants and improve type guards
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
* .
* Fix e2e test
* .
* .
* .
* fix(ai): preserve providerMetadata as providerOptions in multi-turn tool calls (#733)
When tool calls are added to the conversation history, map providerMetadata
to providerOptions following the AI SDK convention. This fixes Gemini thinking
models that require thoughtSignature to be preserved across multi-turn tool calls,
preventing the error 'function call is missing a thought_signature'.
Fixes #727
* Local ui cli flag (#744)
* [web] Increase contrast on attribute items in sidebar (#736)
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
* [world] Remove pause and resume events, actions and states (#751)
* Version Packages (beta) (#735)
* .
* .
* Update turbo inputs to include shared config (#752)
* Update turbo inputs to include shared config
* Apply suggestions from code review
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
---------
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
* feat(web): add self-hosted mode for world configuration (#747)
* feat(web): add self-hosted mode for world configuration
When WORKFLOW_TARGET_WORLD env var is set, the web UI operates in
self-hosted mode where the world configuration is locked to server-side
environment variables and cannot be changed via query params or UI.
- Add getHardcodedConfig server action to detect self-hosted mode
- Modify getWorldFromEnv to use server env vars in hardcoded mode
- Create WorldConfigContext to provide config state app-wide
- Update settings sidebar to show locked state with disabled inputs
- Update connection status to show PostgreSQL backend info
- Mask sensitive values (postgres URL) in hardcoded mode UI
* fix: address PR review feedback
- Remove unused ConfigMode type export
- Fix postgres substring to undefined (tooltip has details)
- Extract buildEnvMapFromProcessEnv helper to reduce duplication
- Remove unused EnvMap import from layout-client
- Import HardcodedConfig from web-shared/server instead of re-defining
* Fix: PostgreSQL URL parameter missing from configParsers, causing loss of postgres URL configuration on page reload in dynamic mode
* fix(cli): clear WORKFLOW_TARGET_WORLD when spawning web server
The CLI sets WORKFLOW_TARGET_WORLD as an env var, which the spawned
Next.js server inherits. This caused the web UI to enter self-hosted
mode even when launched via CLI.
Now we explicitly clear WORKFLOW_TARGET_WORLD from the server's
environment so it starts in dynamic mode where config comes from
query params as intended.
* refactor(web): use server-side env vars for world config
BREAKING CHANGE: The web UI no longer supports configuring the world
backend via URL query parameters. Configuration is now read exclusively
from server-side environment variables.
Changes:
- Remove query param parsing from @workflow/web config.ts
- Add ServerConfig interface with non-sensitive display info
- Update all components to use useServerConfig() hook
- Settings sidebar is now read-only
- CLI passes env vars to spawned web server instead of query params
- Server actions use process.env directly (envMap param reserved for future use)
This simplifies the architecture and improves security by never sending
sensitive data (connection strings, auth tokens) to the client.
* fix(web): fix settings sidebar overflow and shorten data dir path
- Add truncate/overflow handling to settings sidebar config values
- Add shortenPath() helper to abbreviate long file paths:
- Replaces home directory with ~
- Shows .../last-two-segments if still too long
- Add title attributes for full path on hover
* Update changeest
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
* Version Packages (beta) (#755)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Update packages/world/src/queue.ts
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
* [web] Tidy wake-up and re-enqueue buttons (#737)
---------
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
* [cli] Use dotenv to resolve .env and .env.local files on startup (#765)
* Use temporary workflow-server deployment URL
* feat: add queue-based health check to bypass Deployment Protection
- Add HealthCheckPayloadSchema and HEALTH_CHECK_STREAM_PREFIX to @workflow/world
- Add healthCheck() method to Queue interface
- Update workflow and step handlers to detect and respond to health check messages
- Implement healthCheck() in world-local, world-vercel, and world-postgres
The queue-based health check sends a message through the queue pipeline,
which bypasses Vercel's Deployment Protection. The handler writes a response
to a stream that the caller reads to confirm health.
This complements the existing HTTP-based ?__health approach which still works
for local development and when bypass headers are available.
* refactor: move healthCheck to core package as utility function
Instead of adding healthCheck to the World interface (which duplicated
the same implementation across all worlds), this is now a utility function
in @workflow/core that takes the World as a parameter.
Usage:
import { healthCheck } from '@workflow/core';
const result = await healthCheck(world, 'workflow');
This is cleaner because:
- Single implementation instead of 3 identical ones
- World implementations remain simple
- No changes needed to the World interface
* .
* refactor: move health check types from world to core
Health check types (HealthCheckPayloadSchema, HealthCheckResult, etc.)
are now defined in @workflow/core since that's where they're used.
The HealthCheckPayloadSchema is still part of QueuePayloadSchema in
world (so the queue accepts health check messages), but it's not
exported from the public API.
* .
* Refactor health check implementation based on code review feedback (#746)
* Initial plan
* Address PR review comments: export types, fix race condition, improve error handling
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
* Add queue-based health check test and document security considerations
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
* Replace 'any' type with proper type guards for health check response
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
* Extract health check queue names as constants and improve type guards
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
* .
* Fix e2e test
* .
* .
* .
* .
* .
* Update packages/world/src/queue.ts
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
* Use temporary workflow-server deployment URL
* .
* .
---------
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Vercel Release Bot <88769842+vercel-release-bot@users.noreply.github.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
||
|
|
dc44dab49d | Biome config tweaks and fixes applied (#741) | ||
|
|
4d6f797274 |
Fix step stack trace propogation and refactor e2e error tests (#720)
* Refactor e2e tests for errors * Improve step error tests to check workflow return value Step error workflows now catch the error and return message/stack, making assertions cleaner. Tests verify both: - Workflow return value (caught error message) - CLI step result (original stack with function names) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add assertion for stack trace in caught step error With the fix in step.ts that propagates original stack traces, we can now verify that caught step errors include function names directly in the workflow return value (not just via CLI). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * logging * Fix: When a step handler is re-invoked after max retries are exhausted, the step_failed event doesn't include a stack trace, breaking stack trace propagation for this edge case. This commit fixes the issue reported at packages/core/src/runtime/step-handler.ts:127-146 ## Stack trace loss in step_failed event when max retries are exceeded **What fails:** Step handler does not include stack property in step_failed event when step is re-invoked after max retries exhausted, causing FatalError in workflow to lose original stack trace **How to reproduce:** 1. Create a step that always fails with an error that includes a stack trace 2. Exhaust all retry attempts (e.g., maxRetries = 3, so 4 total attempts) 3. The step handler is re-invoked (attempt > maxRetries + 1) 4. This triggers the edge case at lines 127-146 in step-handler.ts 5. A step_failed event is created with fatal: true but without stack property 6. When step.ts processes this event (lines 103-104), the stack remains unset since event.eventData.stack is undefined **Result:** FatalError created in workflow has default stack (from step.ts) instead of original error stack from step execution **Expected:** Stack trace should be propagated consistently across all error paths. All other step_failed events with fatal: true include stack property (lines 311-313 and 351-353), but edge case was missing it. **Root cause:** When step handler is re-invoked after max retries exhausted (attempt > maxRetries + 1), the code creates a step_failed event without including step.error?.stack, which contains the previous error information from the last failed attempt. Other code paths capture this correctly. Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> * add withData * Enable source maps for step bundles and validate in e2e tests - Move sourcemap generation from intermediate workflow bundle to steps bundle - Enhance step error tests to validate function names and source files in stack traces - Remove isLocalDeployment() checks since source maps now work in dev mode 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add changeset for step bundle source maps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add changeset for core package e2e test improvements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Enable source maps in CI e2e tests Add NODE_OPTIONS="--enable-source-maps" to all e2e test jobs to ensure stack traces show original source file paths. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix step error source map checks for local prod builds Add hasStepSourceMaps() helper that correctly identifies when source maps are expected to work: - Vercel prod: works (production builds have proper source maps) - Local dev: works (DEV_TEST_CONFIG is set, uses step bundle with inline source maps) - Local prod: doesn't work (nitro/bundler output doesn't preserve source maps) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add hasWorkflowSourceMaps() utility for vite-based framework exception - Add isViteBasedFramework() helper to detect vite, sveltekit, astro apps - Add hasWorkflowSourceMaps() to check if workflow errors have source maps (known issue: vite-based frameworks in local deployments don't preserve them) - Refactor e2e.test.ts to use the new utility instead of inline check 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * prevent sourcemap checks in nextjs and sveltekit (known offenders) * improve source maps matrix check * fix matrix again * ugh more matrix ignoring --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> |
||
|
|
fdc1aa203c |
Revert "Refactor e2e tests for errors"
This reverts commit
|
||
|
|
1bab22cd2a | Refactor e2e tests for errors | ||
|
|
307f4b0e41 | Revert "Add lazy workflow and step discovery in Next.js (#640)" (#712) | ||
|
|
7ff68d1753 |
Pass tsconfig to esbuild for support of "paths" aliases (#705)
|