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 | |
|---|---|---|---|---|
|
|
ab639a1cd6 |
Add dev-tmux skill for portless+tmux local Workflow SDK dev (#1916)
* [e2e] Add step-vs-sleep race tests + dev-tmux skill Adds two race workflows (sleepWinsRaceWorkflow, stepWinsRaceWorkflow) that exercise Promise.race between a step function and a sleep call. The current `sleepWinsRaceWorkflow` test fails — surfacing how the replay engine resolves a previously-completed step instantly while sleep still has to elapse. Also adds a `dev-tmux` skill that documents the 3-pane tmux + portless setup for testing workflows interactively in a worktree alongside the observability UI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [workbench/nextjs-turbopack] Allow *.turbopack.localhost in dev Adds allowedDevOrigins entries so portless-style worktree-prefixed .localhost URLs (e.g. https://<branch>.turbopack.localhost) can hit HMR and dev-only endpoints without Next's cross-origin protection flooding the logs with warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop duplicate race workflows after merging main PR #1924 added the same sleep/step race workflows directly to main while this branch was open. The textual concat from `git merge` left both copies in 99_e2e.ts; this drops the duplicate set so the file matches origin/main verbatim and the e2e tests pick up the upstream definitions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [skills/dev-tmux] Narrow activation, robust pane IDs, statusline helper - Tighten activation phrases so the skill only fires for the specific portless+tmux setup it documents, not the generic "start the dev server" task. Addresses #1916 review. - Capture pane IDs at split time (-P -F '#{pane_id}') so the snippet works under both pane-base-index 0 and 1. Addresses Copilot review. - Add `statusline.sh` that filters `portless list` to the current worktree's routes and emits a one-line summary, plus instructions for wiring it into Claude Code's `statusLine.command`. - Bump version to 1.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [skills/dev-tmux] Recommend primary-checkout path for statusline Worktrees get deleted, so wiring the statusline to a worktree path breaks the moment the worktree is removed. Update the skill and the script header to recommend pointing `statusLine.command` at the primary checkout (`$HOME/github/vercel/workflow/...`). The script itself is already worktree-aware via Claude's `workspace.current_dir` stdin JSON, so the same invocation surfaces routes for whichever worktree the session is in. Bump version to 1.2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [skills/dev-tmux] OSC 8 link statusline + worktree-named tmux session - Statusline overlay now renders `[dev] · [obs] · tmux:<prefix>`, with the bracketed labels emitted as OSC 8 hyperlinks (clickable in any modern terminal) styled cyan + underline so they stand out. Replaces the old long-URL form that was hard to scan and click. - Add a tmux-session indicator: shown when a session named exactly the worktree prefix exists (uses `tmux has-session -t =<prefix>` for exact matching). - Change the skill's tmux session naming convention from the fixed `workflow-dev` to `<worktree-prefix>` (basename of the branch — same string portless uses as the subdomain prefix). This lets the statusline locate the session deterministically and lets multiple worktrees run dev sessions concurrently without manual disambiguation. - Bump skill to v1.3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [skills/dev-tmux] Statusline: print full `tmux attach -t <name>` command Replaces the abbreviated `tmux:<prefix>` indicator with the full copy-paste-ready `tmux attach -t <prefix>` invocation. Saves a step when grabbing the session from another shell. Bump skill to v1.4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [skills/dev-tmux] Brighter statusline + Nerd Font icons - Drop the dim styling that made the overlay hard to read; use bold bright cyan + underline for links and bold bright green for the tmux command. - Add Nerd Font glyphs: for dev, for obs, for the tmux copy-paste hint. Falls back to box-drawing if the font lacks Nerd Font ranges; layout is unaffected. - Visual differentiation: cyan + underline = clickable hyperlink; green = copy this command. Bump skill to v1.5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [skills/dev-tmux] Restore Nerd Font icons via Unicode escapes The copy glyph in `emit_tmux` was a literal Nerd Font byte embedded in the printf string and got stripped during a prior rewrite. Promote all three icons (rocket / graph / copy) to top-level shell variables that use \uHHHH-equivalent UTF-8 escapes, so the source survives editor round-trips that don't preserve Private Use Area code points. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [skills/internal-dev-workbench] Rename from dev-tmux, set author + reset version - Rename `skills/dev-tmux/` → `skills/internal-dev-workbench/` to make the name self-explanatory about the skill's scope (an internal contributor's local dev workbench, not a generic tmux helper). - Author: Pranay Prakash. Version: 0.1 (first release of the skill). - Update internal references in SKILL.md and statusline.sh accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3535caf449 | [core] Skip inline step execution when suspension also has a wait (#1924) | ||
|
|
5f22832675 |
Serialize run_failed/step_failed errors through serialization pipeline (#1851)
* Serialize run_failed/step_failed errors through serialization pipeline
Switch run_failed, step_failed, and step_retrying events to persist
the full thrown value via the workflow serialization pipeline (as
SerializedData / Uint8Array) instead of a lossy { message, stack, code }
StructuredError shape. Consumers hydrate via hydrateRunError /
hydrateStepError to reconstruct the original thrown value, preserving
Error subclass identity, cause chains, and custom properties.
- WorkflowRun.error and Step.error are now SerializedData
- WorkflowRun gains a top-level errorCode plaintext field
- WorkflowRunFailedError.cause is now the hydrated thrown value
- Adds world-postgres migration 0010_add_error_code.sql
- Legacy pre-pipeline errorJson records surface as undefined on read
* Update Next.js workbenches for new WorkflowRunFailedError.cause type
cause is now `unknown` (the hydrated thrown value) rather than
`Error & { code }`. Defensively extract Error-shaped fields when the
hydrated value is an Error, otherwise round-trip the raw value, and
expose the new `errorCode` classification field.
* Update docs for WorkflowRunFailedError.cause: unknown
The hydrated `cause` is now `unknown` (the original thrown value
through the serialization pipeline) and the error classification has
moved to the top-level `errorCode` property. Update the two affected
docs pages and the `TSDoc` interface to reflect the new shape, and
narrow `cause` with `instanceof Error` before accessing fields.
* Expand test coverage for the run/step error serialization pipeline
Unit tests:
- 19 new dehydrate/hydrate{Step,Run}Error round-trip tests covering
FatalError, plain Error, built-in Error subclasses, non-Error thrown
values (string, plain object), cause chains, encryption round-trip,
the binary format prefix contract, and the unserializable / unknown-
format error paths.
- 5 new tests for Run.returnValue when the run is failed: hydrated
FatalError + cause as cause, plain Error preservation, non-Error
thrown values surfaced verbatim, cross-class cause chains, and the
hydration-failure fallback that still surfaces errorCode.
E2E tests (new, in 99_e2e.ts + e2e.test.ts):
- Step throw → workflow catch round-trips a FatalError with a TypeError
cause chain, asserting class identity, fatal marker, and cause name +
message all survive the step_failed event pipeline.
- Workflow throw → run_failed reaches status with the new
top-level errorCode metadata exposed (cause-shape coverage lives at
the unit level, since the SWC plugin's class registration is not
invoked in the plain-Node e2e runner).
- Workflow throw of a non-Error value round-trips that value verbatim
as WorkflowRunFailedError.cause.
Adjustments to existing assertions:
- error.cause is now ; tests narrow with
and use the new top-level field instead of .
- step.error / run.error from CLI --withData are now hydrated payloads:
unregistered class instances surface as Instance refs whose
carries the original message + stack.
Observability hydration:
- hydrateStepIO / hydrateWorkflowIO in serialization-format.ts now
hydrate the field via hydrateData, so the CLI and web UI
continue to surface readable run/step error messages and stacks.
* Tighten error serialization changeset description
* Trim error serialization changeset to a single sentence
* Resolve FatalError/RetryableError revivers via cross-realm registry
When a workflow runs in a Node `vm` context, its bundled
`@workflow/errors` is a different module instance than the host's
import (separate prototype chains, separate class identity). Calling
`new FatalError(...)` from the host-side reviver produces a
host-realm instance that fails `err instanceof FatalError` checks
in the workflow code — even when the serialized payload was correctly
tagged via the dedicated `FatalError` reducer.
Surfaced by the local-prod e2e "step throw round-trips FatalError"
test on Next.js Turbopack: each route gets its own bundled chunk, so
the flow handler's `@workflow/errors` and the workflow VM bundle's
`@workflow/errors` are two distinct copies of the same module.
Fix:
- Each bundled copy of `@workflow/errors` self-registers its
`FatalError` and `RetryableError` classes on `globalThis` via
`Symbol.for("@workflow/errors//FatalError")` /
`Symbol.for("@workflow/errors//RetryableError")`. First load wins
per realm; the descriptor is non-writable / non-configurable to make
accidental clobbering loud.
- The revivers in `@workflow/core`'s common reducers module read the
consumer's `globalThis` (passed in as `global`) to pick up the
realm-local class, falling back to the host-imported class when no
registration is present (e.g. in the CLI / test runner).
* Use `types.isNativeError` to remap workflow stacks across VM realms
The runtime's run-failure path computes a source-map-remapped stack
and then assigns it back onto the thrown value via `if (err
instanceof Error) err.stack = errorStack`. Workflows run inside a
Node `vm` context, so a workflow-thrown error is an instance of the
VM realm's `Error` — `instanceof` against the host realm's
`Error` returns `false`, the assignment is skipped, and the
serialized `run_failed` event carries the un-remapped (bundled-line-
number) stack instead of the source-mapped one.
Switch the gate to `types.isNativeError`, which uses V8's internal
type tag and works across realms — same approach already in place
for the serialization reducers.
Caught by the local-prod e2e "nested function calls preserve message
and stack trace" and "cross-file imports preserve message and stack
trace" tests, which assert that the persisted run-error stack
contains `99_e2e.ts` / `helpers.ts`.
* Sync CLI revivers with core + add toJSON shim for Error subclasses
Two issues with the CLI's hand-rolled reviver list:
1. It hadn't been updated for the new first-class Error subclass
reducers (`TypeError`, `RangeError`, `FatalError`, `RetryableError`,
etc.). devalue throws "Unknown type X" when it encounters a
reduced value with no matching reviver, and `hydrateResourceIO`
swallows that error and surfaces the raw `Uint8Array` payload —
so `step.error` / `run.error` showed up as raw byte dumps in
`workflow inspect` output.
2. Even with all the right revivers, `Error.prototype`'s `message`
/ `stack` / `cause` are non-enumerable, so `JSON.stringify`
(used by `workflow inspect --json`) drops them — leaving the
subclass-specific enumerable fields (e.g. `FatalError.fatal`)
visible but the actual error data missing.
Fix:
- Build the CLI reviver set on top of `getCommonRevivers()` from
`@workflow/core` so the CLI stays in sync with the runtime's
reducer set automatically. New core reducers/revivers will Just
Work without any CLI-side change.
- Wrap each Error reviver from the common set with a thin shim that
attaches a non-enumerable `toJSON` method to the produced
`Error` instance. `JSON.stringify` calls `toJSON` and gets a
full object (`name` + `message` + `stack` + `cause` + any
enumerable subclass fields like `fatal` / `retryAfter` /
`errors`); `util.inspect` ignores `toJSON` and renders the
canonical `Error: msg\\n at ...` format. Best of both worlds for
CLI output without compromising the runtime hydration path.
Caught by the local-prod e2e "basic step error preserves" and
"cross-file step error preserves" tests, which read
`failedStep.error.message` / `.stack` from the CLI's JSON output.
* Clarify parseErrorJson JSDoc to match its always-null return
The previous JSDoc described preserving legacy values "for best-effort
hydration" which contradicted the implementation, where legacy errors
are intentionally surfaced as absent (the pre-pipeline shapes can't be
hydrated by the new error revivers). Rewrite the comment so the contract
matches behavior. Also rename the now-unused parameter to `_errorJson`
to reflect that the function ignores it.
Caught by a code review on #1851.
* Refine error-handler ergonomics on the step / run hot paths
Three review-driven adjustments that all touch the queue handlers and
their interaction with the error serialization pipeline:
1. Memoize the per-run encryption key fetch. The step handler used to
eagerly fetch + import the key at the top of every step delivery so
the value would be in scope for every potential dehydrateStepError
path. That pessimized step-started early-return cases (the fetch
happens unconditionally even when the step never reaches user code)
and required duplicating the same boilerplate at four call sites in
runtime.ts. Introduce `memoizeEncryptionKey(world, run)` in
runtime/helpers.ts that returns a lazy, single-fetch accessor;
step-handler / runtime call sites use `await getEncryptionKey()`
instead. The first caller pays the fetch cost, subsequent callers
await the cached promise, and steps that fail before any
encryption-aware work happens skip the fetch entirely.
2. Preserve the prior attempt's serialized error as the cause on the
defensive max-retries-exceeded `step_failed` re-invocation guard.
The existing comment explicitly opted out of cause attachment, but
the symmetric post-failure path below already does this and the
reviewer is right that consumers shouldn't have to walk the
step_retrying event history to recover the underlying error. Best-
effort: if hydration of the prior `step.error` throws, fall back
to a FatalError without cause rather than letting the event write
itself fail.
3. Document the intentional `unflatten` throw in
`hydrateStepError` / `hydrateRunError` for non-Uint8Array input.
SDK version is pinned per workflow run via skew protection so the
non-binary branch is dead in production; if a misshapen value
reaches it, surfacing the throw via the surrounding o11y try/catch
is more debuggable than masking it. Add a comment so future
reviewers don't reach for a defensive fallback.
A standalone `falls back to plaintext` suggestion on the run_failed
key fetch was rejected: when encryption is configured we should fail
loudly rather than silently emit plaintext error data. The queue's
redelivery semantics will retry the key fetch; persistent KMS outages
get logged with the existing "persistent error preventing the run from
being terminated" message rather than a security regression.
* Hydrate `event.eventData.error` in event listings
`hydrateEventData` enumerated the per-event fields that need
hydration (`result`, `input`, `output`, `metadata`, `payload`)
but omitted the new `error` field on `step_failed`,
`step_retrying`, and `run_failed` events. Without this branch,
o11y tools that list events (e.g. `workflow inspect events`) surface
the raw `Uint8Array` payload instead of a hydrated
`{ name, message, stack, … }` object even though the entity-level
`Run.error` / `Step.error` paths already hydrate.
Mirrors the existing per-field branches; the `try/catch` leaves the
field un-hydrated on parse failure rather than failing the whole
event view. Adds a unit test.
* Use `.is()` static checks in `classifyRunError` for cross-realm safety
Workflows execute inside a separate `vm` realm: the
`WorkflowRuntimeError` class bundled into the workflow code and the
host-imported one are distinct constructors, so an
`err instanceof WorkflowRuntimeError` check on a VM-thrown error
returns `false` and we'd misclassify genuine runtime errors (corrupted
event log, missing timestamps, workflow/step not registered) as user
errors.
Switch to each subclass's `.is()` static (a name-based duck check that
works across realms). Since `WorkflowRuntimeError.is` only matches its
own concrete name, enumerate every concrete subclass we want to
recognize (`StepNotRegisteredError`, `WorkflowNotRegisteredError`)
in a `RUNTIME_ERROR_CHECKS` table; keep that table in sync with the
class hierarchy in `@workflow/errors`.
Existing `classify-error.test.ts` already covers `WorkflowRuntimeError`
and `WorkflowNotRegisteredError` cases — both still pass.
* Add e2e coverage for step throws of non-Error values
We had `errorWorkflowThrowNonErrorValue` (workflow body throws a plain
object — round-trips verbatim as `WorkflowRunFailedError.cause`) but
no symmetric coverage for the step-throw side. Step-throw goes through
a different code path: non-Error values aren't recognized as
`FatalError` (no `name === 'FatalError'`) nor `RetryableError`,
so they take the transient retry path. After max retries the runtime
wraps the original thrown value as `cause` on a fresh `FatalError`
which the workflow's catch block then sees.
Add a workflow that throws a recognizable plain object from a step
with `maxRetries = 0` (so we exhaust on first attempt and avoid a
long test wait) and a workflow that asserts the wrapped FatalError
shape: `isFatal`, `instanceof FatalError`, message includes the
original object's serialized form, `cause` is the original non-Error
object verbatim with structure preserved.
Documents the current retry-then-wrap behavior so any future change
to "non-Error throws skip retries" semantics has to update the test.
* Note legacy postgres error-data loss in the run/step error changeset
Pre-upgrade failed runs that wrote into world-postgres's deprecated
`error` text column can't be hydrated through the new pipeline (the
shape is incompatible with the new revivers). The new runtime
intentionally surfaces them as `error: undefined` on read; the
original payload is still readable directly from the `errorJson`
column for manual inspection. Add a one-sentence note to the
changeset's migration text so consumers upgrading don't get blindsided
by suddenly-empty error fields on historical runs.
|
||
|
|
00a011dee4 |
Add stable Next.js eager and lazy test coverage (#1747)
* Add stable Next.js eager and lazy test coverage * Address PR review feedback * Fix eager Next step route builds * Fix eager Next manifest refreshes * Fix eager Next e2e stack assertions * Externalize native step bundle bindings * Lazy load Vercel world runtime * Fix Next dev step sourcemap assertions * Consolidate eager build changesets * Fix Vercel world tracing in Next deployments * Externalize Vercel world in Next builds * Fix webpack tracing for Vercel world deps * Fix eager workflow route bundling * Rely on Next server externals |
||
|
|
8ea1532e48 | [core] Combine flow+step bundle and process steps eagerly (#1338) | ||
|
|
1203dae70c |
Friendlier workflow errors (consolidated) (#1849)
* Introduce structured context-violation errors + Ansi renderer Phase 1: Add Ansi rendering helpers (frame, hint, note, help, code, inline) to @workflow/errors, and a chalk mock for readable snapshot tests. Phase 2: Add four context-violation error classes to @workflow/core (NotInWorkflowContextError, NotInStepContextError, NotInWorkflowOrStepContextError, UnavailableInWorkflowContextError) and apply them to all twelve user-facing throw sites so errors now include docs links and a structured "what/why/fix" frame. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Address review: tighten changeset, implement ansifyName, harden Ansi - Tighten phase 1 changeset to a single sentence (per pranaygp review) and switch to double-quoted frontmatter (per Copilot + repo convention). - Implement `ansifyName` to actually apply dim styling to workflow/ / step/ prefixes; add an `Ansi.dim` helper to `@workflow/errors` so callers don't need to import chalk directly. - Remove the `void getWorkflowMetadata;` workaround in context-errors.ts by dropping the unused value import (we only needed the type and symbol). - Render the plain-Error throw in `workflow/get-workflow-metadata.ts` with `Ansi.frame` + docs link so the VM path matches the structured-class styling from the sibling step path (still uses a plain Error to avoid the module-init cycle). - Guard `buildUnderline` against zero-length markers so a stray empty token can't produce a negative `String.repeat` count. * Structured runtime logger metadata + fold in replay-timeout logging Adds a `.child()` and `.forRun(runId, workflowName)` child-logger API to the structured logger so runtime/step code doesn't have to repeat `workflowRunId`/`workflowName`/`stepId` on every call. Normalizes error metadata to structured `errorName` / `errorMessage` / `errorStack` fields instead of ad-hoc `error: err.message` strings, and adds comments to silent catches that swallow expected idempotency conflicts. Also folds in the pending changes from #1812 so that PR can be closed: - Standardize the console prefix to `[workflow-sdk]`. - Split the replay-timeout log into a warn-while-retrying vs. error-when-giving-up, and surface the underlying error when we can't mark a timed-out run as failed. - Include the error stack in the "Fatal runtime error during workflow setup" log and in the top-level user-code workflow error log so the stack surfaces in flattened log drains. - Drop the `[Workflows] "<runId>" - ` prefix from `buildWorkflowSuspensionMessage` — the structured logger now attaches run context. Supersedes #1812. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Use double-quoted changeset frontmatter per repo convention * Add SerializationError + apply to user-facing serialization sites Phase 4 of friendlier errors: introduce a `SerializationError` class with an optional `hint` and a docs link (workflow-sdk.dev/err/serialization-failed), and adopt it at every user-facing serialization boundary in @workflow/core: - Locked ReadableStream at a workflow boundary - Unregistered class / missing `classId` / missing `WORKFLOW_DESERIALIZE` - Attempting to return step functions to clients or call workflow functions directly - Webhook `respondWith()` called outside a step - `dehydrate*` / `getSerializeStream` failures (workflow args/return, step args/return, stream chunks) Internal invariants (format prefix length checks, unknown format bytes, missing `STREAM_NAME_SYMBOL`, encryption key/size guards, etc.) now throw `WorkflowRuntimeError` instead of plain `Error` so the classifier and logger treat them consistently. `formatSerializationError` now returns `{ message, hint }` so the hint fragment can be rendered with the standard SerializationError framing instead of being baked into the message string. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Use double-quoted changeset frontmatter per repo convention * Presentation-only user vs SDK error attribution Add describeError() that derives attribution and class-aware hints from existing error classes + RUN_ERROR_CODES — no event data changes. Wire into step failures, max-delivery exhaustion, run failures, and fatal setup errors so terminal logs include errorAttribution and a hint for known error types. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Address review: describeError accepts precomputed errorCode + instanceof - `describeError(err, errorCode?)` now accepts an optional precomputed `RunErrorCode`. `classifyRunError(err)` only narrows to USER_ERROR / RUNTIME_ERROR, so the REPLAY_TIMEOUT and MAX_DELIVERIES_EXCEEDED branches were previously unreachable from the step / run failure log sites. Callers that know the failure category (runtime.ts for replay timeout and max-deliveries exhaustion) now pass the code in. - Context-violation checks use `instanceof` against the actual classes from context-errors.ts instead of a name-string set. Type-safe + survives class renames. - Wire the new hints through to the REPLAY_TIMEOUT and MAX_DELIVERIES_EXCEEDED log sites so those branches actually render a hint now. - 3 new tests cover the reachable code paths + precomputed-code override. - Changeset frontmatter switched to double quotes per repo convention. * Cosmetic consistency pass on remaining bare throws Internal invariants now use WorkflowRuntimeError so describeError attributes them to the SDK: missing startedAt, VM generateKey, closure-vars outside step context, ENOTSUP. defineHook().resume() formats schema validation failures as a readable list instead of a JSON blob. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Use double-quoted changeset frontmatter per repo convention * Data-driven describeRunError + expose via @workflow/core/describe-error Observability renderers read persisted run_failed / step_failed event data, not live Error instances. describeRunError takes { errorCode, errorName } and returns the same { attribution, hint } shape as describeError, so the CLI and web UI can derive user-vs-SDK framing from the event log directly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Friendlier build-time errors: WorkflowBuildError class + applications Add `WorkflowBuildError` class in `@workflow/errors` with optional `hint` for an actionable next step, and apply it in `@workflow/builders` at user-facing sites: failed esbuild phases, unresolved built-in steps, and empty esbuild output now throw `WorkflowBuildError` with a hint pointing at the likely fix. Runtime invariants remain plain `Error`. * Polish friendlier-errors rendering: drop functionName leak, simplify docs link, redirect stack - Drop the readonly `functionName` param-property on context-error classes so util.inspect no longer prints a trailing `{ functionName: 'foo()' }` block. - Replace the `DocLink` ("label: https://…") shape with a plain `DocsUrl` template-literal type. Error output now renders a single clean line: `docs: https://…` (new `Ansi.docs` helper) instead of the noisier "note: Read more about foo(): https://…". - Add throw helpers (`throwNotInWorkflowContext`, etc.) that call `Error.captureStackTrace(err, stackStartFn)` on V8 engines so the top frame of the thrown error points at the user's call site instead of at the gate function inside the framework. Callers pass themselves as the boundary. - Refactor `defineHook()` (both root and `/workflow`) to use named function closures rather than `this.create`/`this.resume`, since the stack redirect relies on a stable function identity that survives destructuring. - Update context-errors.test.ts to snapshot the new `docs:` framing and to add a regression test asserting the top stack frame is the user call site. * Consolidate friendlier-errors stack: fix ANSI leak + non-retry semantics Addresses PR review feedback across the 8-phase friendlier-errors stack and fixes issues surfaced by manual testing (createHook() inside a step): - ANSI no longer leaks into .message / .stack. Context-violation errors now store plain text on .message and render the colored framed form lazily via [util.inspect.custom] / toString(). Structured logs, log drains, CBOR-serialized events, and JSON payloads no longer contain raw \x1B[...m bytes. - Context violations are now fatal. ContextViolationError sets fatal = true; FatalError.is(err) recognizes any error with a fatal: true own property. Calling createHook() from a step no longer burns three retry attempts on a guaranteed-to-fail context violation. - Ansi helpers moved to @workflow/errors/ansi subpath so imports from @workflow/errors no longer pull chalk into consumers that only want error classes (addresses reviewer VaguelySerious). - Shared redirectStackToCaller helper in packages/core/src/capture-stack.ts, used by both context-errors.ts and workflow/get-workflow-metadata.ts (addresses Copilot review on #1849). - Structured framed content: ContextViolationError now takes a structured FramedContent (title segments + detail branches) and renders plain/pretty from the same source of truth. Tightens the eight existing phase changesets to 1-2 sentences each and adds four new scoped changesets (errors-ansi-subpath, context-errors-plain-message, context-errors-fatal, capture-stack-shared) for the followup fixes, so the final changelog history stays readable. * test: update step-handler mocks for scoped forRun() logger The runtime logger now uses .forRun(runId, name, {stepId, stepName}) to attach scope context, so 409-handling log calls no longer repeat {workflowRunId, stepId} in every metadata bag — those live on the scoped logger instance. Update the mock to return itself from forRun() and tighten assertions to check both the log args (errorName/errorMessage) and the forRun() scope. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Mark SerializationError fatal + route dehydration through step-failure path SerializationError now carries readonly fatal = true. Step-return dehydration is wrapped inside the user-code try/catch so that the resulting error flows through userCodeFailed → step_failed → FatalError.is() short-circuit instead of bubbling up as HTTP 500 and triggering a queue retry loop. Retrying a step that returned a non-POJO is guaranteed to fail the same way, so this saves ~20s and 3 near- identical error blocks per serialization failure. * Add logging snapshot tests + manual-test artifacts Snapshot tests lock in the exact shape of: - describeError() payloads (attribution, errorCode, hint) for every classification — plain Error, SerializationError, context-violation, WorkflowRuntimeError, REPLAY_TIMEOUT, MAX_DELIVERIES_EXCEEDED. - The scoped-logger call signature for the two canonical runtime failure paths (fatal-bubble and hit-max-retries), so refactors of forRun() / child() metadata merging can't silently change what users see in their log drains. SerializationError now also has a direct test for readonly fatal=true + FatalError.is() recognition. pr-artifacts/ contains real log-output snapshots from running the nextjs-turbopack workbench against five error scenarios. These are reference material for reviewers and are flagged to be removed before merge. * Readable step-fatal logs: inline stack + friendly step/workflow names The step-level fatal-error log used to embed the full stack trace inside an `errorStack` string field in the metadata object, so util.inspect rendered it as a quote-escaped, line-continuation blob when the log hit the terminal — unreadable in practice. Move framing + stack into the log *message* (matching the workflow-level log in runtime.ts) and keep the metadata object compact with only the indexable structured fields (`errorAttribution`, `errorName`, `errorMessage`, `hint`, IDs). Log drains still get the same keys; humans now see a readable stack trace. Also introduce `formatStepName` / `formatWorkflowName` in `@workflow/utils` that render machine names (`step//./workflows/1_simple//add`) as `add (./workflows/1_simple)` in log framings, using the existing `parseStepName` / `parseWorkflowName` parsers. Applied to step-fatal, hit-max-retries, exceeded-max-retries, and workflow-threw log sites. Artifacts in pr-artifacts/ updated to show the new output shape, and renamed .log → .md since they're Markdown and IDE previews are nicer that way. * Opinionated pretty formatter for runtime structured-log metadata Replace util.inspect's default object dump (which quote-escapes multi-line stacks and paragraph hints into a single-line JSON-y blob) with a workflow-aware formatter that composes the entire log line into a single string passed to console.error / console.warn. Highlights of the new output: - Per-run / per-step IDs render with their parsed friendly names so users see `wrun_… · simple (./workflows/1_simple)` instead of just the raw `workflowName: 'workflow//./workflows/1_simple//simple'`. - Color-coded attribution badge (user error red / sdk error magenta) paired with the error class in bold. - Hints render as a paragraph under `hint:` rather than a backslash- `\n`-escaped string. - Drops redundant fields (errorStack always; errorMessage when it's already in the parent message) to avoid double-printing. - Unknown fields fall through as a sorted `key value` tail so we never silently drop log information. @workflow/errors/ansi gains bold/red/magenta helpers used by the formatter. The web / web-shared packages don't consume stderr — they read structured event payloads from the World event log — so this is presentation-only at the runtime layer. * ci(benchmarks): disable pnpm cache for getCommunityWorldsMatrix The job never runs `pnpm install` (it just calls `node` against a checked-in script), so the pnpm store path never exists. The post-job `actions/setup-node@v4` cache-save then fails with `Path Validation Error: Path(s) specified in the action for caching do(es) not exist` and red-X's the entire job even though the matrix step succeeded. The setup-workflow-dev composite already has a `cache-pnpm` opt-out input for this exact case — wire it through here. * Address PR review comments: inspect dedup, cause leak, retry-loop tests - ContextViolationError: util.inspect(err) duplicated every framed detail line because the stack-tail strip only sliced the first message line. V8's Error.stack reads `Name: messageLine1\n messageLine2\n at ...`, so for our multi-line `title\n╰▶ docs: …` messages every detail line was getting prepended twice (once in the pretty form, once via the unsliced message tail). Count the actual message lines and slice past all of them. Repro test asserts `╰▶ docs:` appears exactly once. - WorkflowError: stop assigning `cause: undefined` as an enumerable own property when no cause is provided. Subclasses (every error in this PR) inherit the parent constructor; the unconditional assignment polluted `util.inspect(err)` output with `{ cause: undefined, … }` on every no-cause instance. The `super(...)` call already conditionally sets `.cause` non-enumerably when `options.cause` is provided. - step-handler.test.ts: add a regression-gate suite that exercises the fatal-vs-retryable retry-loop wiring directly. Asserts that an error with `fatal: true` produces exactly one `step_failed` event with no `step_retrying`, and that a non-fatal `Error` retries via `step_retrying` on early attempts and emits `step_failed` once the retry budget is exhausted. Catches the silent-regression case where `fatal = true` is removed from a context-violation error class but the `FatalError.is()` unit tests stay green. * Consolidate changesets + remove pr-artifacts Address review feedback to drastically shorten the changesets — fold the 15 file-by-file entries into a single user-facing changeset for @workflow/core / errors / builders / utils. Also drop the pr-artifacts/ folder (reviewer-only log captures, no longer needed). * Polish runtime error logging: layout, stack trim, hint consolidation Five user-driven fixes from manual smoke-testing of #1849: 1. Logger layout. composeLogLine() now puts the structured-fields block (attribution badge, run/step IDs, error code) **between** the framing line and the stack body, instead of after it where 30+ lines of stack buried the most useful information. The framing stays at the top, stack at the bottom, structured info readable at a glance. 2. Stack trim. Drops framework-internal frames (`node_modules/.pnpm/`, `node:internal/`, Turbopack-bundled `node_modules__pnpm_*` chunks, `_next_dist_*` chunks) and caps the surviving frame count at 6 so the stack stays compact even on heavy async wrappers. Suppressed runs emit one summary line so users know the trim happened. 3. Wrapper-route noise. The nextjs-turbopack workbench's start route was catching `WorkflowRunFailedError` rejection on `Promise.race([readLoop(), run.returnValue])` and re-logging it via `console.error('Error in workflow stream:', error)` plus `controller.error(error)` — which then triggered Next.js's `⨯ failed to pipe response` overlay. The SDK already logs the failure cleanly upstream and the runId is on the response header, so the wrapper now closes the SSE stream cleanly on WorkflowRunFailedError. 4. Consistent framed `╰▶ hint:` / `╰▶ docs:` layout for all errors that carry a hint or docs slug. WorkflowError, SerializationError, and WorkflowBuildError now share one `appendFramedDetails` helper matching the box-drawing structure that ContextViolationError already used. Was: blank-line-separated `Learn more: <url>`. Now: one tree, indistinguishable from context-violation rendering. 5. Drop the duplicate logger-side `hint` field. Hints now live on the error message only — actionable hints get serialized into the event log, rehydrated on the workflow side, and shown in observability automatically. The previous logger-only hint duplicated stderr but never made it past the step boundary. Updated SerializationError hint to point at the foundations doc ("Ensure you're returning workflow serializable types. Check the serialization docs to see what's serializable: https://workflow-sdk.dev/docs/foundations/serialization") instead of the hardcoded `(plain objects, arrays, primitives, …)` list, which drifted out of sync as the supported types grew. Same hint reuses for step args, workflow args/return, stream messages, and any other site that goes through `formatSerializationError`. Also retitled the retry summary `3 retries` → `3 max retries` since "3 retries" next to "4 attempts" was ambiguous (already-happened vs. budget). * Trim error-card title + drop machine step name from persisted error - ErrorStackBlock (web observability): show just the first non-empty trimmed line of the error message in the card title with single-line truncation. Multi-line messages (`Failed to serialize step return value\n╰▶ hint: …`) were rendering the entire framed body in the title, pushing the copy button off-screen and burying the scannability of the headline. Full message stays in the body via the stack (V8 prepends `Name: message` to `Error.stack`), so no information is lost; hover-tooltip exposes the full title text. - Persisted error message: drop the `Step "step//./.../foo"` machine name from `Step failed after N retries: …` and `Step exceeded max retries (…)` strings. Observability already attributes the event to a specific step via the UI tree, and the CLI logger emits the friendly `Step foo (./...) hit max retries` framing on its own line. Embedding the raw `step//./...` machine name in the persisted message text was duplicate noise. * Update .changeset/friendlier-errors.md Co-authored-by: Peter Wielander <mittgfu@gmail.com> Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * Update .changeset/pretty-log-format.md Co-authored-by: Peter Wielander <mittgfu@gmail.com> Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * Update SerializationError snapshot tests for slug-less message The class no longer attaches a slug-based `╰▶ docs:` line — the foundations URL is embedded directly in the hint via the `formatSerializationError` helper in @workflow/core. Update the test expectations accordingly: - bare-title case is now a single line (no docs link) - hint case renders one `╰▶ hint: …` branch (no second branch) * Update serialization.test.ts hint assertions for foundations URL Four `should throw error for an unsupported type` cases were still asserting on the old hardcoded type list. Update to the new hint phrasing that points at the foundations doc, matching the change in `formatSerializationError` (`packages/core/src/serialization/errors.ts`). --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
a7071bf3d4 | Fix pnpm type issue after tanstack PR (#1907) | ||
|
|
8202663857 | [workbench] Add TanStack Start workbench and tests (#1875) | ||
|
|
072e86bbef |
Add additional tests for event consumer fixes for hook/sleep/step race conditions (#1528)
Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
e7ea0684f4 |
Add first-class serialization for built-in Error subclasses (#1511)
* Add first-class serialization for built-in Error subclasses Replace the generic Error reducer/reviver with specific handlers for TypeError, RangeError, SyntaxError, URIError, ReferenceError, EvalError, and AggregateError. Preserve the cause property on all Error types, only serializing it when present to maintain absence vs presence semantics. * Refactor Error subclass reducers/revivers with helper functions Extract repetitive reducer/reviver logic into makeErrorSubclassReducer and makeErrorSubclassReviver helpers. Reduces ~120 lines of duplicated code to ~50 while preserving identical behavior. AggregateError remains as a wrapping extension since it needs to preserve the errors array. * Bump error-subclass-serialization changeset to minor Adding new serialization support for built-in Error subclasses is a feature, not a bug fix. |
||
|
|
9ea125427f |
Decode UTF-8 stream chunks (#1852)
* Decode typed array stream chunks * Render decoded stream bytes with raw view * Render decoded bytes in data inspector * Use generic byte inspector for streams * review feedback: narrow stream-display exports, fix tab a11y, add collapseRefs tests - Remove unused formatStreamChunkForDisplay/sanitizeStreamChunkForDisplay exports; keep only the formatArrayBufferViewForDisplay path actually used by DataInspector. - Replace broken role=tablist/role=tab on the Decoded/Bytes switcher with aria-pressed toggle-button semantics. - Export collapseRefs/isBytesDisplay and add regression tests covering typed-array detection (top-level, nested in object/array/Map/Set, DataView exclusion). * Replace eval with JSON.parse in serialization revive helper (#1848) * Replace eval with JSON.parse in serialization revive helper devalue.stringify() always produces valid JSON — special values (undefined, NaN, Infinity, -0) are encoded as negative integer sentinels. JSON.parse yields the same flattened array form that unflatten() expects, without the eval anti-pattern (VULN-918). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Drop redundant workflow package from changeset Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> * Add e2e test for UTF-8 parseable stream chunks Emits Uint8Array chunks containing multi-byte UTF-8 (Latin Extended, CJK, emoji, RTL Arabic) plus a UTF-8 encoded JSON document, and asserts each chunk round-trips through TextDecoder({ fatal: true }). Exercises the same decode path the web inspector relies on for typed-array stream values. Made-with: Cursor --------- Co-authored-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
56ba32feb8 | Bump vite (#1827) | ||
|
|
b163860fe4 |
Guard fibonacciWorkflow against non-finite n (#1814)
Throw a FatalError at the top of fibonacciWorkflow if n is not a finite number. Prevents a runaway recursion if the workflow is ever invoked without its numeric argument — NaN - 1 === NaN, NaN <= 1 === false, so the base case would never fire and every descendant would spawn two more children. |
||
|
|
b27921985c |
Add distributed abort controller guide and implementation (#1811)
* feat: add distributed abort controller guide and implementation Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * feat: implement abort signal step function and refactor workflows to use it Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * feat: enhance distributed abort controller with user-provided ID Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * feat: enhance distributed abort controller with TTL and reconnection logic Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * feat: add grace period to abort controller workflow Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * feat: add distributed abort controller workflow and tests Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * add the pattern to the sidebar * fix: correct start() call signature and run.id → run.runId start() takes args as a positional second argument, not an options object property. The Run object exposes runId, not id. Made-with: Cursor * fix: address PR review comments on distributed abort controller - Make abort() idempotent by catching hook-not-found/expired errors - Add error handling to signal getter's stream reader IIFE - Fix tests: use instance methods instead of non-existent static methods, pass required ttlMs/graceMs args, include expired field in assertions Made-with: Cursor * fix: add missing import to Custom TTL docs code sample The docs typecheck runs each code block in isolation. The Custom TTL example was missing the DistributedAbortController import. Made-with: Cursor * fix: only sleep grace period on TTL expiration, not manual abort Manual aborts now complete immediately instead of sleeping through the full TTL + grace period. This fixes vitest timeouts where all 3 distributed-abort-controller tests exceeded the 30s limit. Made-with: Cursor * fix: wait for hook registration before aborting in test The DistributedAbortController instance test was timing out because abort() was called before the workflow had registered the hook. The idempotent catch silently swallowed the "not found" error, leaving the workflow running forever. - Make runId readonly (was private) so tests can access it - Add waitForHook(getRun(controller.runId)) before controller.abort() Made-with: Cursor * fix: apply conditional grace period to E2E workflow copy The distributedAbortControllerWorkflow in 99_e2e.ts had the same unconditional grace sleep bug, causing the reconnect test to timeout at 60s while waiting ~66s for the grace period after manual abort. Made-with: Cursor --------- Co-authored-by: v0 <v0[bot]@users.noreply.github.com> Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> Co-authored-by: Karthik Kalyanaraman <karthik.kalyanaraman@vercel.com> |
||
|
|
e295bae417 |
feat: allow start() to be called directly inside workflow functions (#1491)
* feat: allow start() to be called directly inside workflow functions
Add 'use step' to start() so it can be called directly from workflow
code. The SWC compiler strips the function body in workflow mode and
replaces it with a step proxy. When called from a workflow:
1. The workflow function reference is serialized via WorkflowFunction
reducer (serializes { workflowId })
2. start() executes in the step context with full Node.js access
3. The returned Run is serialized via WORKFLOW_SERIALIZE and deserialized
back in the workflow VM
4. Run getters (.status, .returnValue, etc.) are 'use step' getters
that each execute as separate steps
Also re-exports start from @workflow/core/runtime/start in api-workflow.ts
instead of using a throwing stub, adds e2e tests for startFromWorkflow
(with hook communication) and fibonacciWorkflow (recursive composition).
* fix(next): don't copy package step files in deferred builder to avoid duplicate classes
Files belonging to packages (detected by walking up to find a
package.json with a name field) are imported via relative path
instead of being copied to __workflow_step_files__/. Copying creates
a second module instance which breaks JS native private field (#)
brand checks when the runtime creates instances from one copy and
the step handler accesses fields from the other.
* fix(next): only skip copying package files that are serde classes, not all package step files
Regular package step files (like fetch) must still be copied to ensure
the SWC loader registers them. Only serde class files from packages are
excluded from copying since those define classes with JS native private
fields (#) that break when duplicated.
* fix(next): generate thin wrappers for package serde step files instead of full copies
For package files that define serde classes (like Run), generate a thin
wrapper that imports the original class and registers steps/classes from
the manifest. This avoids duplicating the class definition (which breaks
JS native private field brand checks) while still registering all step
functions and the class in the serialization registry.
Regular package step files (like fetch) are still copied as before.
* fix(next): use forceStepModeFiles to transform package serde files in step mode
Instead of copying package serde+step files (which creates duplicate
classes with #private brand check issues) or generating fragile wrappers,
add the original file paths to a shared forceStepModeFiles set. The
loader checks this set and transforms those files in step mode directly,
so the SWC plugin generates proper step registrations on the original
class — no duplication, no reimplemented registration logic.
* fix(next): use step mode for all files with step/serde patterns, not just copies
The loader now selects step mode for any file that has 'use step'
directives or serde patterns, regardless of whether it's a deferred
step copy. Step mode is a superset of client mode — the only addition
is step registry IIFEs, which are harmless for non-step consumers.
This means package serde+step files (like Run) no longer need to be
copied to get step registrations. They're imported directly in the
step route and the loader transforms the original file in step mode.
One class instance, no duplication, no wrapper generation.
---------
Co-authored-by: Nathan Rajlich <n@n8.io>
|
||
|
|
417c4930be |
refactor(swc-plugin): remove client transform mode, merge into step mode (#1686)
* refactor(swc-plugin): remove client transform mode, merge into step mode Remove the `client` transform mode from the SWC compiler plugin. The `client` and `step` modes were nearly identical — both preserved step function bodies, replaced workflow bodies with throw stubs, and emitted the same JSON manifest. Step mode now absorbs all client-mode behaviors: - Dead code elimination (previously only workflow + client) - Hoisted variable references for object property steps - All integrations use mode: 'step' instead of 'client' BREAKING CHANGE: The `client` value for the SWC plugin `mode` option is no longer accepted. Use `step` instead. * fix(nitro): force-inline workflow packages in dev mode for serde classId registration In dev mode, Nitro's Rollup externalizes npm packages like @workflow/core, so the SWC transform plugin never processes files like run.js. This means serde classes (e.g. Run) never get the classId registration IIFE, causing serialization failures when step functions return Run instances. Uses a Rollup resolveId hook to force workflow SDK packages to be bundled (non-external) while leaving all other dependencies external. This is more targeted than noExternals=true which bundles everything and causes TDZ errors from circular imports in packages like vue-bundle-renderer/h3. The Nitro module now also ignores .nitro/workflow/** in watchOptions so writing generated workflow bundles does not retrigger Nitro's own dev bundle rebuild loop. Also wraps dev:reload workflow rebuilds and makes LocalBuilder.build() atomic (writes to temp files, renames on success) to avoid partial output state during HMR. For Nuxt, also configures Vite's ssr.noExternal to bundle workflow packages in the SSR context. * fix(nitro,nuxt): address review feedback on dev-mode classId fix - nitro builders: use crypto.randomUUID() for temp file suffix instead of Date.now() to avoid collisions under rapid/concurrent build() calls, and serialize concurrent build() calls through an internal queue so two overlapping dev rebuilds cannot clobber each other's temp outputs. - nitro index: use fileURLToPath() to convert file:// URLs to filesystem paths, which correctly handles Windows paths (file:///C:/... -> C:\...) and percent-decoding, instead of relying on new URL(...).pathname. - nuxt module: normalize vite.ssr.noExternal to an array (preserving any existing string/RegExp/array entry) before appending workflow package matchers, so the force-bundle behavior is not a no-op when noExternal is already set to a non-array value. |
||
|
|
173756dc4d |
[docs] Rename workflowdevkit to workflowsdk and useworkflow.dev to workflow-sdk.dev (#1759)
* [docs] Rename workflowdevkit references to workflowsdk * [docs] Rename useworkflow.dev to workflow-sdk.dev * [chore] Add changeset for domain rename * [docs] Revert sitemap rewrite to useworkflow.dev (crawled-sitemap not yet available for new domain) |
||
|
|
1fa6397241 |
Move swc-playground-wasm into workbench/swc-playground (#1704)
* move swc-playground-wasm into workbench/swc-playground Move the Rust/WASM source from packages/swc-playground-wasm into workbench/swc-playground/wasm/ so it is no longer built as part of the packages/* turbo filter in CI. The WASM build now runs as part of the playground's own prebuild/dev scripts, avoiding Rust toolchain failures in unrelated CI jobs. * fix: ensure default rustup toolchain in wasm build The Vercel build environment has rustup installed but no default toolchain configured. Add a check for this and install stable if needed. Also check for VERCEL env var in addition to CI. * fix: ensure cargo bin dir is in PATH on Vercel After cargo install wasm-pack, the binary lives in $CARGO_HOME/bin which may not be in PATH on the Vercel build environment. Always add the cargo bin directory to PATH after ensuring the toolchain. |
||
|
|
851a530164 |
Add cookbook with 50 workflow pattern recipes (#1564)
* Add cookbook section with 50 workflow pattern recipes
Migrate the "Workflow API Explorer" decision tree concept from
workflow-campaign-demos into useworkflow.dev docs as a Cookbook.
Infrastructure:
- docs/lib/cookbook-tree.ts: decision tree data, 50 recipe metadata entries, slug-to-category mapping
- docs/components/geistdocs/cookbook-explorer.tsx: interactive "I want to..." decision tree UI with breadcrumb navigation
- docs/content/docs/cookbook/index.mdx: landing page rendering CookbookExplorer component
- docs/content/docs/cookbook/meta.json + 8 category meta.json files for sidebar nav
- docs/content/docs/meta.json: added cookbook to docs nav between foundations and how-it-works
- docs/app/[lang]/docs/[[...slug]]/page.tsx: registered CookbookExplorer component
50 recipe MDX files across 8 categories (payments, approvals, resilience,
notifications, webhooks, data-processing, routing, observability), each with:
- Frontmatter (title, description, type: guide, summary with use-case scenario)
- Simplified code snippet (core pattern only, stripped of demo UI concerns)
- Full implementation code snippet (exact source from campaign demos)
- Key APIs section with links to API reference docs
* ploop: iteration 1 checkpoint
Automated checkpoint commit.
Ploop-Iter: 1
* fix: polish cookbook public routing
Keep the cookbook surface canonical at /cookbooks so docs navigation, sitemap output, and AI/chat entry points stop leaking the legacy /docs/cookbook paths.
Correct the approval-chain example so the docs teach the intended sequential approval semantics instead of implying the workflow approves after the first successful level. This keeps the cookbook aligned with the docs quality bar and avoids misleading readers with inconsistent behavior.
Ploop-Iter: 2
* docs: canonicalize cookbook doc routes
Align cookbook-facing docs outputs with the new public route so
redirects, sitemap entries, and LLM-facing exports stay consistent.
This keeps the polished cookbook section discoverable at its canonical
location while trimming the last demo-heavy recipe examples toward the
same concise style as the rest of the docs.
Ploop-Iter: 3
* ploop: iteration 4 checkpoint
Automated checkpoint commit.
Ploop-Iter: 4
* fix(docs): finalize cookbook route split
Keep cookbook content discoverable after moving it to a first-class /cookbooks surface so navigation, canonical metadata, and markdown consumers resolve the new public URLs consistently.
Avoid serving the legacy /docs/cookbook tree as if it were still part of the docs section, which reduces duplicate navigation paths and prevents stale static output from competing with the new route structure.
Ploop-Iter: 5
* docs: improve cookbook discovery
The cookbook landing page needs to work for both exploratory users and users who already know the pattern they want. This keeps the guided decision tree while adding shared category metadata and a searchable browse mode so recipe discovery feels faster and more consistent with the rest of the docs experience.
Ploop-Iter: 6
* docs: refine cookbook pattern examples
Tighten the simplified cookbook recipes so the examples teach the intended workflow semantics clearly and consistently. The changes keep the documentation focused on the core control-flow patterns reviewers called out, while removing ambiguity around partial arrivals, deadlines, and first-success behavior.
Ploop-Iter: 7
* docs: decouple cookbook sidebar tree
Separate cookbook navigation from the docs page tree so the standalone /cookbooks experience stays stable after the route move and the main docs sidebar no longer leaks cookbook entries.\n\nThis keeps cookbook navigation driven by explicit recipe metadata, which avoids duplicated section titles and makes the docs and cookbook surfaces easier to evolve independently.\n\nPloop-Iter: 8
* fix(docs): align cookbook public nav
Keep cookbook pages on their public /cookbooks surface so metadata and copied markdown do not leak legacy /docs/cookbook paths.\n\nSimplify sidebar rendering to trust the injected page tree, which avoids route-specific filtering and keeps cookbook navigation consistent with the active layout tree.\n\nPloop-Iter: 9
* refactor(docs): decouple cookbook routing
Move cookbook rendering off the shared docs route so cookbook pages can behave like a first-class docs surface without leaking cookbook-specific UI into the main docs experience.
Centralizing cookbook tree filtering keeps sidebar behavior consistent in one place and avoids duplicate cookbook navigation state across layouts.
Ploop-Iter: 10
* docs: improve cookbook explorer accessibility
Improve the cookbooks entrypoint so loading and keyboard navigation
are usable without visual cues, and keep guided and browse modes
resilient while the route hydrates.
Ploop-Iter: 11
* ploop: iteration 12 checkpoint
Automated checkpoint commit.
Ploop-Iter: 12
* docs: rename cookbooks route to cookbook (singular)
* docs: add 5 cookbook overview design variations
Add getRecipeHref, getRecipesByCategory, and collectSlugs helpers to
cookbook-tree.ts, then create 5 distinct overview page variations at
/cookbook/v1 through /cookbook/v5 for side-by-side comparison:
- v1: Category Grid (zero-JS scannable overview)
- v2: Search-First (real-time filtering with category pills)
- v3: Accordion Catalog (expandable category sections)
- v4: Decision Wizard (step-by-step guided questions)
- v5: Problem-Solution Table (master-detail by scenario)
Also adds .impeccable.md with project design context.
* docs: remove unrelated package.json bumps and generated artifacts
* docs: restructure cookbook from 50 recipes into 27 consolidated pages
Restructure the cookbook based on team meeting feedback, toolbar comments,
mux-ai pattern analysis, and Vercel org code search. Consolidates duplicates,
adds missing patterns, ensures all examples have proper directives and
type-check against the real workflow SDK.
New structure:
- common-patterns/ (9): saga, batching, rate-limiting, fan-out,
scheduling, idempotency, webhooks, content-router, child-workflows
- agent-patterns/ (5): durable-agent, tool-streaming,
human-in-the-loop, tool-orchestration, stop-workflow
- integrations/ (3): ai-sdk, sandbox, chat-sdk
- advanced/ (6): serializable-steps, durable-objects,
isomorphic-packages, secure-credentials, custom-serialization,
publishing-libraries
All 92 code snippets pass docs-typecheck against real workflow SDK types.
Deleted 8 old category folders. Updated cookbook-tree.ts, explorer, nav.
* docs: add workflow migration guides
Help teams evaluating the GA launch translate existing durable workflow systems into Vercel Workflow without reverse-engineering concept parity from product docs alone.
The guides focus on real migration decisions and concrete TypeScript examples so adoption can be driven by implementation clarity rather than platform marketing.
Ploop-Iter: 1
* ploop: iteration 2 checkpoint
Automated checkpoint commit.
Ploop-Iter: 2
* docs: refine workflow migration guides
Clarify the migration narrative for GA so teams evaluating a move from Temporal, Inngest, or Step Functions get examples that are realistic enough to trust and pricing guidance that matches the current platform model.
The Inngest guide needed a complete order-saga flow so the durable orchestration, compensation, and streaming patterns read as a credible migration target instead of a partial sketch. The pricing language across all three guides also needed to be aligned with current Workflow and Vercel compute billing semantics to avoid creating the wrong cost expectations during evaluation.
Ploop-Iter: 3
* ploop: iteration 1 checkpoint
Automated checkpoint commit.
Ploop-Iter: 1
* docs(skill): refine workflow migration guidance
Clarify migration rules so agents choose the correct resume primitive, keep streaming guidance aligned with runtime behavior, and avoid implying Vercel-managed execution for self-hosted targets.
This reduces avoidable migration mistakes in generated guidance and keeps the skill consistent with the acceptance criteria used to evaluate it.
Ploop-Iter: 2
* ploop: iteration 3 checkpoint
Automated checkpoint commit.
Ploop-Iter: 3
* docs(skills): refine workflow migration guidance
Reduce the migration skill entry point to the decision surface agents need\nso they can select the correct resume pattern without carrying duplicate\nexamples in the initial context.\n\nClarify framework precedence so prompts that explicitly ask for\nframework-agnostic boundaries do not get Hono- or Next-specific route\nshapes, while preserving framework-specific examples when requested.\n\nCentralize canonical resume examples in the shared patterns reference to\nkeep the guidance consistent across migration paths and reduce drift.\n\nPloop-Iter: 4
* docs: add workflow deep-dive references
Add a cohesive set of deep-dive reference articles so the GA launch
has architecture-level documentation grounded in the current SDK
implementation. This gives readers verified explanations of runtime,
replay, streaming, compiler, and cost-model behavior while linking the
series together for easier navigation.
Ploop-Iter: 1
* ploop: iteration 5 checkpoint
Automated checkpoint commit.
Ploop-Iter: 5
* docs: normalize deep-dive related links
Keep the new deep-dive reference pages cross-linked so readers can move between adjacent runtime concepts without depending on older how-it-works pages alone.
This preserves navigational consistency across the GA launch docs set and reduces the chance that architectural explanations drift into isolated pages that are harder to discover and maintain.
Ploop-Iter: 2
* docs(skill): refine workflow migration routing
Tighten the migration guidance so agents choose the correct resume surface and runtime boundary earlier, reducing incorrect mixed patterns in generated migrations.
Add explicit fast paths for self-hosted targets and Step Functions task-token callbacks so the skill stays consistent on callback URL vs deterministic resume decisions.
Ploop-Iter: 6
* docs: refine workflow migration skill guidance
Clarify route selection so the migration skill composes resume, runtime, and app-boundary concerns deterministically. Add a canonical Step Functions self-hosted Hono callback recipe so migrations produce the correct callback-url pattern without mixing incompatible hook surfaces.\n\nPloop-Iter: 7
* docs: checkpoint deep-dive drafts
Preserve the verified GA launch deep-dive drafts in git so the campaign work can continue from a stable checkpoint.
Capture the reviewed documentation progress now to reduce risk of drift between source-backed research and the publishable drafts.
Ploop-Iter: 3
* docs(skills): tighten workflow migration routing
Clarify the migration skill's route-selection rules so generated guidance stays consistent across resume surfaces, runtime targets, and named framework boundaries.
This reduces ambiguous outputs where agents might mix framework syntaxes or invent callback routes for webhook-based flows, which leads to migration guidance that does not match the user's runtime model.
Ploop-Iter: 8
* docs: finalize workflow deep dives
Clarify the runtime mechanics behind the GA deep-dive series so launch content stays aligned with the implementation and existing docs. Tightening these explanations reduces the risk of readers internalizing inaccurate mental models about replay, compilation, and workflow execution.\n\nPloop-Iter: 4
* docs(skills): refine workflow migration routing
Clarify route selection so migrations choose the correct resume surface and app boundary patterns for the target runtime and framework. Strengthen verification guidance to reject invented callback routes in URL-based flows and keep examples aligned with the documented migration rules.
Ploop-Iter: 9
* docs: sync compiler deep-dive runtime details
Align the compiler deep-dive trio with the actual Workflow runtime so launch materials describe the same execution model users rely on. This keeps the GA narrative accurate around deterministic replay, step queue triggers, and runtime bundle responsibilities, reducing the risk of docs teaching an architecture the SDK does not implement.
Ploop-Iter: 5
* docs(skill): tighten workflow migration guidance
Reduce hot-path skill context so migration routing stays easier to select and
verify during activation.
Trimmed examples and converted long invalid samples into concise failure rules
so the skill points agents to on-demand references instead of loading bulky
worked code by default.
Ploop-Iter: 10
* docs: tighten workflow migration guidance
Clarify route-key planning and resume-surface defaults so migration outputs
stay deterministic when prompts underspecify callback behavior.
Strengthen the deep-dive docs to trace runtime handoffs more directly,
which reduces ambiguity about how the compiler split maps to durable
execution behavior.
Ploop-Iter: 11
* docs: refine streaming and cost deep dives
Clarify the operational model behind durable streaming and zero-cost suspension so launch materials stay source-accurate for readers comparing workflow runtimes.
The updates make the workflow-step boundary, persistence path, and queue-driven cost story more explicit, reducing ambiguity around where stream I/O is allowed and why long waits do not consume compute.
Ploop-Iter: 6
* ploop: iteration 12 checkpoint
Automated checkpoint commit.
Ploop-Iter: 12
* docs: tighten deep-dive streaming accuracy
Align the launch deep dives with the current runtime so campaign content does not misstate suspension behavior or streaming backend capabilities.
These edits clarify the distinct resume paths for step suspension versus timed waits and document the backend-specific streaming guarantees now available across local, Vercel, and Postgres worlds, reducing the risk that readers build incorrect mental models from launch materials.
Ploop-Iter: 7
* docs(skills): refine callback resume taxonomy
Clarify when migrations should use deterministic internal resume versus generated callback URLs so skill outputs stay consistent across frameworks and hosting targets. Distinguish default webhook responses from manual-response flows to prevent ambiguous guidance and keep the shared callback references directly inspectable.\n\nPloop-Iter: 13
* docs: refine deep-dive runtime wording
Clarify the runtime semantics behind suspension and durable streaming so the GA launch materials stay aligned with the source of truth.
These edits tighten descriptions around wake-up paths, backend behavior, and stream lifecycle details to reduce ambiguity for readers comparing the docs to the implementation.
Ploop-Iter: 8
* docs: clarify webhook response mode defaults
Clarify when migrations should use the default webhook behavior versus
manual responses so agents make the same callback choice across the
skill entrypoint, shared patterns, and API reference.
This reduces avoidable ambiguity for callback-url prompts and makes the
default 202 behavior explicit unless a prompt requires custom response
semantics.
Ploop-Iter: 14
* docs: align cost model wake-up semantics
Prevent the GA launch materials from teaching an incorrect mental model about how suspended runs wake back up. The updated wording keeps the blog, social, and reference variants anchored to the real runtime paths so readers understand which transitions are queue-delayed, which are step-driven re-enqueues, and why that distinction matters for the cost story.
Ploop-Iter: 9
* ploop: iteration 10 checkpoint
Automated checkpoint commit.
Ploop-Iter: 10
* docs: clarify webhook resume choices
Explain the resume-surface decision points so migration and API guidance steer authors toward the correct webhook or hook pattern for the prompt.
Reduce common callback-routing mistakes early in the docs and skill so agents make fewer wrong assumptions during workflow migrations.
Ploop-Iter: 15
* docs: tighten cost model deep dives
Clarify the cost-model narrative so launch materials make source-verifiable
claims about suspension, wake-up paths, and polling behavior.
This keeps the GA messaging aligned with the runtime's actual control
flow and avoids overclaiming where the implementation has narrower
semantics than the original copy suggested.
Ploop-Iter: 11
* docs(skills): tighten resume routing guidance
Keep the migration skill entrypoint small so agents load the routing contract only when the source actually pauses for external resume. Clarify the public webhook docs around the default callback flow to reduce accidental use of lower-level runtime APIs.\n\nPloop-Iter: 16
* docs: tighten deep-dive runtime claims
Align the launch materials with the current runtime semantics so the
cost-model and execution-model narrative stays defensible against the
actual implementation.
This keeps the GA campaign focused on claims we can support directly from
source, especially around suspension, re-enqueue behavior, and the
difference between orchestration compute and client-side polling helpers.
Ploop-Iter: 12
* docs: correct cost model deep dive claims
Align the cost-model launch content with the runtime's actual suspension and re-entry mechanics so GA messaging does not overstate identical-cost waits or imply residency that the queue-based engine does not have.
This keeps the public explanation consistent with source-backed behavior around timed wake-ups, explicit workflow re-queue after step completion, and the distinction between idle worker residency and boundary I/O.
Ploop-Iter: 13
* chore: remove non-cookbook files from cookbook branch
Remove deep-dive articles, migration guides/skill, vercel-toolbar skill,
workflow-skills test fixtures, and misc artifacts that belong in separate
branches (deep-dives, migration-guides). Revert create-webhook.mdx,
getting-started/meta.json, and code-transform.mdx to main versions.
* docs: address toolbar feedback on cookbook pages
Address Vercel toolbar comments from Nathan Rajlich, Peter Wielander, Pranay Prakash, and Karthik Kalyanaraman on the cookbook branch.
In saga.mdx, remove the unnecessary `if (!(error instanceof FatalError)) throw error;` guard in the catch block — compensations should always unwind regardless of error type. Replace the `while/pop()!()` loop with a cleaner `for...of reverse()` to avoid the non-null assertion.
In ai-sdk.mdx, split the "Using Different Providers" section into two subsections: "Vercel Gateway (string model IDs)" clarifying that all string model IDs route through Gateway, and "Direct Provider Access" showing how to import from provider packages like `@workflow/ai/openai` to bypass Gateway. Change the "Tool Functions as Steps" section to "Tool Functions with Steps" and reword to explain that tool execute functions can optionally include steps via "use step" but don't have to — when they aren't steps, they run in workflow context and can modify workflow state directly.
In sandbox.mdx, rewrite the page to reflect that `@vercel/sandbox` now has first-class Workflow SDK support. Replace all `declare function` stubs and `// TODO` placeholders with real `import { Sandbox } from "@vercel/sandbox"`. Remove the four separate "use step" wrapper functions (provisionSandbox, runCommand, teardownSandbox, saveSandboxSnapshot) and show direct `Sandbox.create()`, `sandbox.runCommand()`, and `sandbox.destroy()` calls in the workflow function since these implicitly run as steps. Simplify the agent tool example to use inline execute functions that call Sandbox methods directly with an `activeSandbox` variable for workflow state.
Across all cookbook files, replace "Workflow DevKit" with "Workflow SDK" (8 instances in 5 files: publishing-libraries.mdx, secure-credentials.mdx, ai-sdk.mdx, chat-sdk.mdx, sandbox.mdx).
* docs: simplify cookbook index to plain MDX listing
Replace the interactive CookbookExplorer (726-line decision tree + browse
component) with a simple MDX page that lists recipes grouped by category
with linked titles and descriptions. Remove v1-v5 design variations and
trim cookbook-tree.ts to sidebar-only metadata.
* fix type checks
---------
Co-authored-by: Karthik Kalyanaraman <karthik.kalyanaraman@vercel.com>
|
||
|
|
71d39d2f8d |
fix: serialize Run across runtime and workflow VM (#1616)
* feat: serialize Run via custom class serialization with "use step" Single Run class implementation: - Add WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE to core Run class - Mark all runtime-dependent methods/getters with "use step" so SWC strips their bodies from the workflow bundle - Remove duplicate Run stub from api-workflow.ts; re-export core Run - SWC auto-registers the class (no manual registerSerializationClass) - Add e2e test for Run serialization across workflow/step boundaries - Add unit tests for serde roundtrip * chore: bump changeset to minor (new feature) * fix: include resilientStart in Run serde payload and add readable getter comment * . * fix: remove eager getWorld() from constructor, rely on lazy getter |
||
|
|
873b4e2bb4 | [core] Refactor getWorld interface to be asynchronous (#942) | ||
|
|
4d31619eb7 |
fix(ai): preserve provider tool identity across step boundaries (#1663)
* fix(ai): preserve provider tool identity across step boundaries
Port of vercel/ai#14229. Provider tools (e.g. anthropic.tools.webSearch)
were converted to plain function tools in toolsToModelTools, stripping
type, id, and args fields. This caused providers like Anthropic Gateway
to not recognize them as provider-executed tools.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* DCO Remediation Commit for Gregor Martynus <39992+gr2m@users.noreply.github.com>
I, Gregor Martynus <39992+gr2m@users.noreply.github.com>, hereby add my Signed-off-by to this commit:
|
||
|
|
ec517fa225 |
Add features.encryption to WorkflowMetadata (#1652)
* Add features.encryption to WorkflowMetadata Expose a `features` object on `getWorkflowMetadata()` so library authors can detect at runtime whether encryption is enabled for the current workflow run, allowing them to conditionally handle sensitive data serialization. * Improve docs example: show workflow-level encryption check Step inputs are serialized before the step body runs, so the encryption check should happen in the workflow function before passing data to steps. * Fix docs example: show encryption check on step return value Step return values are serialized to the event log after the step body runs, so this is where the check is actually useful - controlling what data gets persisted as output. * Fix docs typecheck: declare external function in code sample * minor |
||
|
|
bab8cddf98 |
Support getter functions with "use step" directive (#1630)
* Support getter functions with "use step" directive Add SWC compiler plugin support for JavaScript getters marked with "use step", enabling patterns like `await obj.prop` where the getter triggers a step function invocation. - Handle Prop::Getter in object literals and MethodKind::Getter in classes - Emit Object.getOwnPropertyDescriptor registration in step mode - Emit hoisted proxy + Object.defineProperty in workflow mode - Emit error for getters with "use workflow" - Fix @vercel/workflow -> @workflow/serde imports in existing fixtures - Update spec.md with getter transformation documentation * Add changeset for getter step support * Add e2e test for getter step functions * Add static getter support, sanitize hoisted var identifiers Address PR review feedback: - Support static getters with "use step" using ClassName (not .prototype) - Add sanitize_ident_part() to produce valid JS identifiers from getter names that may contain special characters (e.g. string literal keys) - Add static-getter-step test fixture - Update spec.md with static getter transformation documentation * Remove duplicate getter workflow error in visit_mut_prop_or_spread |
||
|
|
f5d2aef58f |
Add serde compliance tooling and improve custom class serialization DX (#1552)
* Add serde compliance tooling and update skill documentation - Add serde compliance checker library to @workflow/builders - Add build-time warnings for serde classes with Node.js imports in workflow bundle - Add 'workflow transform' CLI command for inspecting SWC output - Implement 'workflow validate' CLI command with serde compliance checks - Add serde analysis panel to SWC playground - Update workflow skill with custom class serialization documentation * Fix --json + --strict: use process.exitCode so JSON output is returned before exit * Address code review feedback - Fix --strict exit code: honor process.exitCode in BaseCommand.finally() - Remove unused --module-specifier flag from transform command - Add missing Node.js builtins (e.g. test) to playground detection list - De-dupe build-time serde warnings by grouping identical issues across classes - Make Serde Analysis panel collapsible like the output panels * Use .gitignore for validate file discovery; fix changeset wording |
||
|
|
d8aaf27c79 | [core] Enforce single ALS context to protect against duplicate module and caching issues (#1591) | ||
|
|
7e33c62736 |
Rename 'Workflow Development Kit' / 'DevKit' to 'Workflow SDK' (#1595)
* Rename 'Workflow Development Kit' / 'DevKit' to 'Workflow SDK' across docs, code, and config
Follow-up to
|
||
|
|
d119c740d0 | [builders] Fix import.meta.url missing when using CJS (#1509) | ||
|
|
781d64b737 |
Move SWC playground to client-side WASM and add Monaco type intellisense (#1553)
* Move SWC playground transform from server action to client-side WASM
Create a new swc-playground-wasm crate that bundles swc_ecma_parser,
swc_ecma_codegen, and the swc_workflow transform visitor into a single
WASM binary via wasm-bindgen/wasm-pack. This runs the code
transformation entirely in the browser, eliminating the server action
round-trip and serverless function cold starts on every keystroke.
- New packages/swc-playground-wasm Rust crate targeting wasm32-unknown-unknown
- Exposes transform() and transformAll() functions via wasm-bindgen
- Client loads WASM + JS glue from public/wasm/ as static assets
- Removed @swc/core and @workflow/swc-plugin server-side dependencies
- Removed serverExternalPackages/outputFileTracingIncludes Next.js config
- Added WASM loading indicator and error state in the UI
- Reduced transform debounce from 500ms to 300ms (no network latency)
* Add missing extends key to swc-playground-wasm turbo.json
* Fix build: ensure rustup is available for wasm32-unknown-unknown target
The Vercel build environment has a system Rust without rustup, so
the wasm32-unknown-unknown target can't be managed. Now the build
script checks for rustup specifically (not just cargo) and installs
it when missing, matching the pattern in swc-plugin-workflow/build.js.
* Add workflow type definitions to Monaco editor for intellisense
Auto-generate type declarations from built .d.ts files of workflow,
@workflow/core, @workflow/errors, @workflow/world, and @workflow/utils
packages. Register them with Monaco's TypeScript language service via
addExtraLib() so imports like 'workflow' resolve with full types,
eliminating red squiggles and enabling autocomplete/hover info.
* Fix Monaco type resolution: register root index.d.ts for each package
Monaco's NodeJs module resolution looks for index.d.ts at the package
root, not just in dist/. Register the main entry .d.ts content at both
paths (dist/ and root) so bare imports resolve with full type info.
Also remove the 2307 diagnostic suppression so non-existent imports
correctly show errors.
* Fix Monaco tooltip overflow by enabling fixedOverflowWidgets
* Fix hydration mismatch, Monaco type resolution paths, and WASM init warning
- Fix hydration mismatch: gate Reset button disabled state on isHydrated
so server and client render consistently during hydration
- Fix Monaco types: use bare node_modules/ paths instead of file:///
URIs for addExtraLib, which is what Monaco's NodeJs resolver expects
- Remove virtual package.json entries (Monaco doesn't use them)
- Fix wasm-bindgen init deprecation: pass object { module_or_path }
instead of bare string argument
* Fix Monaco module resolution: set model URI to file:/// and match addExtraLib paths
Monaco's TypeScript NodeJs resolver needs the editor model and the
addExtraLib entries to share the same URI scheme. Set the input editor
model path to file:///src/input.tsx and register all type declarations
at file:///node_modules/... so resolution of bare imports like
'workflow' correctly finds the virtual node_modules.
Also register a root index.d.ts for packages like @workflow/world that
lack an explicit 'types' field in their package.json exports.
* Use declare module ambient declarations for reliable Monaco type resolution
Replace the virtual node_modules filesystem approach with declare module
ambient declarations. This is the standard approach used by TypeScript
Playground and StackBlitz — it works regardless of Monaco's internal URI
scheme and module resolution quirks.
The generation script now:
- Registers all .d.ts files at file:///node_modules/<pkg>/dist/... paths
- Generates a global ambient declarations file with declare module blocks
that map bare import specifiers to their .d.ts entry points
- Includes @workflow/serde and workflow sub-exports (api, errors, observability)
- Supports configurable sub-export mappings per package
* Inline types into declare module blocks for full Monaco type support
Replace the export-from-file approach with fully inlined declare module
blocks. The script now reads each .d.ts entry point, recursively inlines
all relative imports, strips external import statements (resolved via
other declare module blocks), and produces a single ambient declarations
string.
This correctly handles:
- unique symbol exports (@workflow/serde)
- cross-package re-exports (workflow re-exporting from @workflow/core)
- JSDoc comments preserved for hover documentation
- Sub-path exports (workflow/api, workflow/errors, workflow/observability)
- Added @workflow/serde package
* Add workspace packages as dependencies so Turbo builds their types
The generate-monaco-types script reads .d.ts files from the built
dist/ directories of workflow, @workflow/core, @workflow/errors, etc.
On Vercel, Turbo only builds explicit dependencies — without these
workspace references, the packages were never built and the dist/
directories didn't exist, resulting in 0 type modules generated.
* Add @types/node declarations to Monaco editor
Register all @types/node .d.ts files via addExtraLib so Node.js
built-in modules (crypto, fs, path, etc.) are available in the
playground editor with full type information.
* Collect @types/node .d.ts files recursively to include subpath modules
The previous non-recursive scan missed subdirectory files like
fs/promises.d.ts, stream/web.d.ts, dns/promises.d.ts, etc., causing
'Cannot find module node:fs/promises' errors.
|
||
|
|
cdf90d5a38 |
Rename Workflow DevKit to Workflow SDK, remove beta badge, add tweet wall (#1541)
* Rename Workflow DevKit to Workflow SDK, remove beta badge, add tweet wall - Rename "Workflow DevKit" to "Workflow SDK" across all files (~108 files) - Rename standalone "WDK" references to "Workflow SDK" - Remove beta badge from homepage hero - Add tweet wall component to homepage with 4 builder testimonials Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Rename Workflow DevKit to Workflow SDK, remove beta badge, add tweet wall - Rename "Workflow DevKit" to "Workflow SDK" across all files (~108 files) - Rename standalone "WDK" references to "Workflow SDK" - Remove beta badge from homepage hero - Add tweet wall component to homepage with 4 builder testimonials Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Harpreet Arora <harpreet.txt@gmail.com> * Address review: fix missed trigger phrase renames and bump skill versions - Rename "workflow devkit" to "workflow sdk" in trigger phrases for both skill files - Bump workflow-init SKILL.md version to 1.1 - Bump workflow SKILL.md version to 1.5 - Note: CLAUDE.md is a symlink to AGENTS.md, already renamed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Harpreet Arora <harpreet.txt@gmail.com> * link correct tweet --------- Signed-off-by: Harpreet Arora <harpreet.txt@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Karthik Kalyanaraman <karthik.kalyanaraman@vercel.com> |
||
|
|
52db376c39 |
Support projects with Node.js step dependencies in vitest plugin (#1524)
Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
672d9195a4 |
Fix step/workflow not found errors to fail gracefully instead of queue retry (#1452)
* feat: enhance error handling for missing workflow functions Slack-Thread: https://vercel.slack.com/archives/C09G3EQAL84/p1773856370214769?thread_ts=1773856370.214769&cid=C09G3EQAL84 Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * fix: update step not found handling to match FatalError pattern Move step function validation after step_started and call step_failed directly if not found. Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * changes Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * feat: add StepNotRegisteredError and WorkflowNotRegisteredError semantic errors Introduce dedicated error types for when step/workflow functions are not registered in the current deployment, replacing generic WorkflowRuntimeError. These are infrastructure errors (not user code errors) with proper error slugs, docs pages, and a new FUNCTION_NOT_REGISTERED error code. Step not found fails the step (like FatalError) so the workflow can handle it gracefully. Workflow not found fails the run. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review comments - Remove FUNCTION_NOT_REGISTERED error code, use RUNTIME_ERROR instead - Use .is() instead of instanceof for WorkflowRuntimeError check in runtime.ts - Remove non-working example from WorkflowNotRegisteredError docs (custom errors not serialized yet) - Update all references from FUNCTION_NOT_REGISTERED to RUNTIME_ERROR Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add e2e tests for step/workflow not registered errors and fix docs typecheck E2E tests: - WorkflowNotRegisteredError: start a run with a fake workflowId, verify the run fails with RUNTIME_ERROR - StepNotRegisteredError (caught): workflow catches the step failure, verify workflow completes and step is marked failed - StepNotRegisteredError (uncaught): verify the run fails when workflow doesn't catch the error Step not registered is tested by manually invoking useStep with a non-existent step ID in the workflow VM — this is the same pattern the SWC transform generates for real step calls. Also fix docs typecheck by using declare/\@setup pattern instead of \@skip-typecheck for code samples. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: cast globalThis to any for Symbol index access in e2e workflow TypeScript's strict mode doesn't allow using a symbol to index globalThis. Cast to any since this runs in the workflow VM where the symbol is defined. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: classify WorkflowNotRegisteredError as RUNTIME_ERROR The .is() check uses name-based matching, so WorkflowNotRegisteredError (name='WorkflowNotRegisteredError') doesn't match WorkflowRuntimeError.is(). Add explicit check in classifyRunError so the error code is RUNTIME_ERROR instead of USER_ERROR. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use instanceof for WorkflowRuntimeError checks, improve docs Address PR review feedback: 1. Revert .is() checks back to instanceof WorkflowRuntimeError in runtime.ts and classify-error.ts. instanceof catches all subclasses (current and future), which is the correct behavior for these catch blocks. 2. Remove duplicated try/catch example from step-not-registered-error API reference (troubleshooting page already has it). 3. Add Callout in API reference docs clarifying that .is() works in server-side Node.js code but not inside "use workflow" functions where errors arrive deserialized from the event log. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * changes Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: v0 <v0[bot]@users.noreply.github.com> Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
ef4ca00b77 |
chore: bump next to 16.2.1 and fix deferred build (#1496)
* chore: bump next to 16.2.1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: run deferred Next dev e2e assertions on stable Bump Next.js to 16.2.1 in docs and swc-playground and update lockfile. * fix(next): copy all deferred step sources for step-mode transforms --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: JJ Kasper <jj@jjsweb.site> Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
1b4a3abbf1 |
chore: bump nitropack to ^2.13.1 (#1501)
* chore: bump nitropack to ^2.13.1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * c Signed-off-by: Peter Wielander <mittgfu@gmail.com> --------- Signed-off-by: Peter Wielander <mittgfu@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
8366fae439 | chore: bump nuxt ecosystem dependencies (#1500) | ||
|
|
362978be55 |
chore: bump sveltekit ecosystem (#1498)
Bump SvelteKit-related dependencies in the sveltekit workbench: - @sveltejs/kit: ^2.43.2 -> ^2.55.0 - svelte: ^5.39.5 -> ^5.54.0 - @sveltejs/adapter-node: ^5.4.0 -> ^5.5.4 - @sveltejs/adapter-vercel: ^6.1.1 -> ^6.3.3 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
61d40ff4cf |
chore: bump express and fastify in workbenches (#1499)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
730c95bfb9 |
chore: bump @nestjs/* to ^11.1.17 (#1497)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
db394c43db |
chore: bump hono to ^4.12.8 (#1495)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
5010ebe7c5 |
fix(next): stabilize deferred canary e2e in nextjs workbenches (#1468)
* Revert "[ci] Fix nextjs lazy disocvery impacting e2e tests, disable experimental DurableAgent tests (#1400)"
This reverts commit
|
||
|
|
823f58e5c6 |
Revert "Add support for calling start() inside workflow functions (#1133)" (#1475)
This reverts commit
|
||
|
|
e889860984 |
Add support for calling start() inside workflow functions (#1133)
* Add support for calling `start()` directly inside workflow functions Enable `start()` to work in workflow context by routing through an internal step (`__workflow_start`), reusing existing step infrastructure with no new event types or server changes needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address PR review feedback - Use typeof check instead of truthiness for WORKFLOW_START symbol - Validate start() options in workflow context (reject unsupported options like world) - Set maxRetries=0 on __workflow_start step to prevent orphaned child runs - Add unit tests for createStart factory (6 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Make Run serializable in workflow context with step-backed methods - Add Run serialization via __serializable marker + custom Run reducer/reviver in the serialization module (avoids SWC plugin injecting class-serialization imports) - Create WorkflowRun class factory (packages/core/src/workflow/run.ts) with step-backed methods: cancel(), status, returnValue, workflowName, createdAt, startedAt, completedAt, exists - Register 8 built-in steps (__run_cancel, __run_status, etc.) in step-handler - Update __workflow_start to return full Run object (serialized → WorkflowRun in VM) - Update createStart to pass through step result directly - Update docs to reflect full Run support in workflow context Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix start() in workflow VM by delegating from api-workflow stub The workflow VM loads api-workflow.ts (via the "workflow" export condition) which stubs all runtime functions. The start stub needs to check for the injected WORKFLOW_START symbol and delegate to it, otherwise start() throws "doesn't allow this runtime usage" in the workflow context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR review: fix stale WORKFLOW_SERIALIZE comments and register Run in host registry - Update comments in step-handler.ts and start.ts to reference the actual serialization mechanism (Run reducer with __serializable marker) instead of the stale WORKFLOW_SERIALIZE reference - Register Run class in the host's class registry from step-handler.ts so the Run reviver can deserialize Run/WorkflowRun instances in step context Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add docs for recursive/repeating workflows and deploymentId: "latest" - Document using start() for self-chaining workflows to avoid large event logs - Add examples for batch processing and cron-like repeating patterns - Document deploymentId: "latest" option with type safety warning - Update skill file with same patterns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Return full Run object from startFromWorkflow e2e workflow Update the e2e workflow to return the childRun object directly instead of just childRun.runId, exercising Run serialization across the workflow boundary. Update e2e test assertions to match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add recursive fibonacci e2e test for start() in workflow Demonstrates recursive workflow composition: fibonacciWorkflow starts new instances of itself via start() + Promise.all to compute fib(6)=8, fanning out across independent workflow runs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Move Run method steps to builtins with "use step" directives Refactor: instead of manually registering Run method steps via registerStepFunction in step-handler.ts, define them as proper "use step" functions in builtins.ts with __builtin_ prefix. This leverages the existing SWC plugin infrastructure — functions starting with "__builtin" get stable bare-name step IDs. - Add __builtin_run_{cancel,status,return_value,...} to both builtins files - Use dynamic import() for getRun inside step bodies to avoid pulling Node.js modules into the workflow bundle - Remove manual registerStepFunction calls from step-handler.ts - Update WorkflowRun step references to __builtin_run_* names - Fix step name display in web observability: fall back to raw name instead of "?" for built-in steps that don't follow step//module//fn format - Add fibonacciWorkflow default args for nextjs-turbopack workbench UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Render Run objects as clickable links in web observability UI - Add RunRef type and Run reviver to observabilityRevivers so serialized Run objects are hydrated as RunRef instead of showing raw Uint8Array - Add RunRefInline component (purple badge with run ID) that navigates to the target run on click, matching the StreamRef pattern - Thread onRunClick callback through the component chain: WorkflowTraceViewer → EntityDetailPanel → AttributePanel → DataInspector - Wire up navigation in the web app's run-detail-view - Add startFromWorkflow default args for workbench UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Throw error instead of silent fallback when Run class not in registry Address PR review: the Run reviver now throws if the class isn't found in the registry, instead of silently returning a plain { runId } object that would break the assumption of getting a valid Run instance. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix e2e failures: allow retries on Run getter steps, fix docs code samples - Remove maxRetries=0 from read-only Run getter steps (status, returnValue, workflowName, etc.) — these are safe to retry and need retries when the child workflow hasn't completed within the step timeout. Only cancel keeps maxRetries=0. - Fix docs code samples: use correct import path (workflow/api not workflow), add declare statements for helper functions used in examples. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use standard step//module//function naming for built-in steps Update the SWC plugin's __builtin_ special case to generate proper step//@workflow/core//{name} IDs instead of bare function names. This makes parseStepName work correctly for built-in steps, showing: - StepName: "Run#returnValue" (not "__builtin_run_return_value") - ModuleSpecifier: "@workflow/core" (not the raw function name) Convention: __builtin_Run_cancel → step//@workflow/core//Run#cancel (uppercase prefix + underscore → instance method # notation) - Move __workflow_start to builtins.ts as __builtin_start - Rename __builtin_run_* to __builtin_Run_* for proper # notation - Update WorkflowRun step refs to use full step// IDs - Remove manual registerStepFunction from step-handler.ts - Update SWC spec.md with new naming examples Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove SWC __builtin special case, use standard step naming for builtins Remove the SWC plugin's __builtin_ special case so built-in steps get standard step//{module}@{version}//{fn} IDs like any other step. This makes parseStepName work correctly, showing proper StepName and ModuleSpecifier in observability. The VM reconstructs the same IDs via builtinStepId() which uses the @workflow/core version to build: step//workflow/internal/builtins@{v}//{fn} - Remove __builtin special case from SWC plugin (revert to original) - Add builtinStepId() helper shared by workflow.ts, start.ts, run.ts - Rename Run steps: __builtin_Run_cancel → Run_cancel, etc. - Rename start step: __builtin_start → start - Move start step from manual registerStepFunction to builtins.ts - Keep __builtin_response_* names unchanged (pre-existing) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use static class methods for Run steps to get Run.method naming Refactor Run method steps from standalone functions (Run_cancel) to static methods on a Run class, so the SWC plugin generates step IDs with the standard static method convention: Run.cancel, Run.returnValue, Run.status, etc. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR review: tests, docs warnings, skill fix - Add TODO on Run.returnValue about polling blocking (replace with system hooks once AbortSignal/AbortController PR lands) - Add docs callout warning about returnValue holding workers alive - Fix SKILL.md contradiction that said start() can't be used in workflows - Enhance suspension test to assert step arguments are forwarded - Add WorkflowRun unit tests: serializable marker, runId, registry, delegation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix response builtins: adopt this-serialization from PR #1413 The rebase onto main didn't fully adopt PR #1413's refactor of response builtins to use `this` instead of explicit parameters. The old pattern (resJson(this) wrappers) passed `this` as an argument, but the step functions now expect `this` to be set via method call context. Switch to Object.defineProperties on Request/Response prototypes, matching main's approach. Also document WORKFLOW_PUBLIC_MANIFEST=1 for local e2e testing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address docs review: returnValue polling is temporary, link to start() API ref - Update returnValue warning to note this is a temporary implementation that will be replaced with internal hooks - Replace inline deploymentId: "latest" docs with link to the existing start() API reference which already covers it comprehensively Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix e2e tests: replace collectedRunIds with trackRun API PR #1426 replaced the manual collectedRunIds array with a trackRun() helper. The start() wrapper already auto-tracks, so just remove the manual push calls and add trackRun for the child run. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
afa3931a59 | Add stress benchmarks: 1000-step, data payload, and stream tests (#1214) | ||
|
|
7d535bd680 |
test: fix flaky promiseAnyWorkflow e2e test (#1436)
* test: fix flaky promiseAnyWorkflow e2e test Widen the delay gap between step b (100ms) and step c (6000ms) so that b reliably wins the Promise.any race regardless of server/network jitter. Previously the gap was only 1s vs 3s which was insufficient in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix flaky promiseAnyWorkflow e2e test Widened the step delay gap to stabilize the test. Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
3f2ebb2f90 |
test: validate sleep() works correctly inside loops (#1415)
* test: add e2e test proving sleep() works correctly inside loops Validates a user-reported concern that sleep() inside a loop with step calls fires all iterations instantly. The test confirms sleep is honored on replay — 3 iterations with 3s sleeps take ~6s+ total elapsed time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review comments - Fix misleading "3s per iteration" comment to "3s between iterations" - Update test comment to reflect 2.5s jitter-tolerant threshold - Add per-iteration delta assertions to ensure each individual sleep fires Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Apply suggestion from @TooTallNate Co-authored-by: Nathan Rajlich <n@n8.io> Signed-off-by: Peter Wielander <mittgfu@gmail.com> --------- Signed-off-by: Peter Wielander <mittgfu@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Peter Wielander <mittgfu@gmail.com> Co-authored-by: Nathan Rajlich <n@n8.io> |
||
|
|
2c80ec7217 | DurableAgent: various compatibility fixes (#1385) | ||
|
|
1959f2b8e7 |
Use relative import for _workflows in webpack trigger-pages (#1405)
esbuild's discovery build cannot resolve @/ path aliases when the tsconfig does not have baseUrl set. Use a relative import so the discovery can trace through _workflows.ts to find all workflow files. |
||
|
|
74aea7b0af |
Add DurableAgent compat tests, e2e tests, and migrate to AI SDK v6 (#1362)
* Add DurableAgent compat tests, e2e agent tests, and migrate to AI SDK v6 - Port ToolLoopAgent test suite as DurableAgent compatibility spec (34 tests, all expected to fail — each maps to a feature gap to implement) - Add e2e workflow definitions using mock LLM providers (no API keys needed) - Add e2e test file for DurableAgent workflows - Migrate all AI SDK types from V2 to V3 (LanguageModelV2 → V3, etc.) - Drop AI SDK v5 support: ai peer dep ^5||^6 → ^6, @ai-sdk/provider ^2||^3 → ^3 - Update ai catalog version from 5.0.104 to 6.0.116 - Simplify CompatibleLanguageModel to just LanguageModelV3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR review feedback - Remove providerExecuted guard on tool-result stream parts (V3: all tool-results are provider-executed by definition) - Remove providerExecuted spread from tool-output-available UI chunks - Replace inline MockLanguageModelV3 with import from ai/test (works without msw in AI SDK v6) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove streamTextIterator mock from compat tests, use it.fails for gaps Tests now exercise the real DurableAgent code path instead of mocking the core iterator. 5 tests pass (features DurableAgent already has), 29 are marked it.fails() for known API gaps that will alert when fixed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Implement Tier 1+2 gaps, add @workflow/ai/test mock provider, wire e2e in CI DurableAgent API additions: - Add `instructions` (string | SystemModelMessage | SystemModelMessage[]) as alias for deprecated `system` on constructor - Add `onStepFinish` and `onFinish` on constructor, merged with stream options (constructor first, then stream — matching ToolLoopAgent) - Add `timeout` on stream options (converted to AbortSignal) - Add `text`, `finishReason`, `totalUsage` to onFinish event Test infrastructure: - Add @workflow/ai/test export with `mockModel()` wrapper that wraps MockLanguageModelV3 from ai/test as an async step function - E2e workflows now use mockModel() + convertArrayToReadableStream from @workflow/ai/test instead of inline V2 mock models - Add e2e-agent.test.ts to test:e2e script so it runs in CI - Flip 6 compat tests from it.fails → it (now passing) Score: 11 passing / 23 it.fails (was 5/29) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove e2e agent tests — mock models can't serialize across step boundary The workflow runtime serializes step arguments, and function closures (like mock model doStream callbacks) aren't serializable. Mock models only work in unit tests where 'use step' is a no-op. Real e2e agent tests would need either a mock HTTP server or real provider credentials. Also removes 'use step' from mockModel wrapper (closures aren't serializable) and reverts test:e2e script and example workbench dep. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add working e2e agent tests with mock model step factories Mock model factories use the same 'use step' pattern as real providers (anthropic, openai). Closure variables are bound to locals at the step body level so the SWC plugin detects them via __private_getClosureVars. All 4 e2e tests pass against local dev server: - agentBasicE2e: text response (11s) - agentToolCallE2e: single tool call + text (11s) - agentMultiStepE2e: 3 sequential tool calls (12s) - agentErrorToolE2e: FatalError recovery (11s) Also adds e2e-agent.test.ts to test:e2e script for CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use @workflow/ai/test package imports for e2e mock models Split mock provider into two files to work around SWC constructor closure bug: mock-create.ts has the model creation logic, mock.ts has the 'use step' wrappers that capture only serializable args (strings, plain object arrays). Exports mockTextModel(text) and mockSequenceModel(responses) — same 'use step' pattern as real providers (anthropic, openai, etc.). E2e workflows now import directly from @workflow/ai/test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Simplify mock provider, add comprehensive e2e tests for all features + gaps Mock provider: - Replace mock-create.ts with mock-function-wrapper.ts that simply wraps MockLanguageModelV3 constructor in a function (SWC class closure bug) - mockTextModel/mockSequenceModel use mockProvider() from wrapper file - Bind closure vars at step body level (_text = text) for SWC detection - Fix AbortController not available in workflow VM sandbox E2e tests (13 total, all passing): - Core: basic text, tool call, multi-step, error recovery (4) - Callbacks: onStepFinish constructor+stream, onFinish constructor+stream (2) - Features: instructions, timeout (2) - GAPs documented: onStart, onStepStart, onToolCallStart, onToolCallFinish, prepareCall (5 — complete but callbacks not called) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add tool approval (needsApproval) gap tests, fix SWC closure var binding Unit tests: 2 new it.fails() tests for tool approval - needsApproval: true should pause agent (pending tool call, no result) - needsApproval as function should receive tool input E2e tests: 1 new test for tool approval gap - Documents that needsApproval is currently ignored (tool executes anyway) Also fixes: - Bind closure vars at step body level in mock provider (_text = text, _responses = responses) so SWC plugin detects them - Guard AbortController usage in workflow VM (not available in sandbox) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add default args for agent e2e workflows in UI definitions The nextjs-turbopack UI calls workflows with hardcoded default args. Without these entries, agent workflows were called with no args, causing prompt=undefined → ModelMessage validation failure. Also removes debug logging. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add DurableAgent chat UI with tools, update docs for AI SDK v6 Chat UI: - Tab-based layout with Workflows (existing) and DurableAgent Chat tabs - Chat powered by DurableAgent + WorkflowChatTransport + ai-elements - Tools: getWeather (fake data), calculate (math expressions) - Uses createUIMessageStreamResponse for proper stream serialization - Reconnect route at /api/chat/[runId]/stream - ai-elements components: conversation, message, prompt-input, tool - onStepFinish + onFinish callbacks with console logging Docs (AI SDK v6 migration): - system → instructions in DurableAgent constructor examples (10 places) - LanguageModelV2Prompt → LanguageModelV3Prompt in type references (3 places) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix tool rendering, add reasoning support, model picker, observability links - Fix tool part rendering: use `input`/`output` props (not `args`/`result`) and `tool-{name}` part type (AI SDK v6 format) - Add reasoning support for Opus 4.5 via providerOptions - Model picker: Haiku 4.5, Sonnet 4, Opus 4.5 (reasoning), GPT-5.2, GPT-5.3 - Fix observability links: localhost:3456 for local, Vercel dashboard for prod - Add suggestions above prompt input - Add MessageParts component handling text, tool, reasoning, step-start - Add loading spinner for submitted state - Update docs: system → instructions, LanguageModelV2Prompt → V3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix tool output rendering: use input/output props on ToolInput/ToolOutput Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix: Documentation for `PrepareStepInfo` and `PrepareStepResult` interfaces references the obsolete `LanguageModelV2` type while the codebase has fully migrated to `LanguageModelV3`. This commit fixes the issue reported at docs/content/docs/ai/message-queueing.mdx:36 **Bug explanation:** The codebase migrated from AI SDK V2 to V3. In `packages/ai/src/agent/types.ts`, `CompatibleLanguageModel` is defined as `LanguageModelV3` (from `@ai-sdk/provider`). The actual TypeScript interfaces in `packages/ai/src/agent/durable-agent.ts` use `string | (() => Promise<CompatibleLanguageModel>)` which resolves to `LanguageModelV3`. However, the documentation in `docs/content/docs/ai/message-queueing.mdx` at lines 36 and 43 still referenced `LanguageModelV2` for the `model` field in both `PrepareStepInfo` and `PrepareStepResult`. This is inconsistent because: 1. The `messages` fields in the same interfaces were correctly updated to `LanguageModelV3Prompt` 2. The actual source code uses `LanguageModelV3` via `CompatibleLanguageModel` 3. There is no `LanguageModelV2` type anywhere in the codebase This would mislead developers reading the documentation into using the wrong type. **Fix explanation:** Changed both `LanguageModelV2` references to `LanguageModelV3` on lines 36 and 43 of the documentation file, matching the actual codebase types. Verified no other stale `LanguageModelV2` references remain in the docs directory. Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: pranaygp <pranay.gp@gmail.com> * Fix instructions tests: flip from it.fails to it, update snapshots The 3 instructions tests (string, SystemModelMessage, array) now pass. The snapshots include the assistant reply message from the agent loop, which is a behavioral difference from ToolLoopAgent (DurableAgent captures the prompt after the full loop iteration). Score: 14 passing / 22 it.fails (was 11/25) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix getReadable call: pass startIndex as options object Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix docs type errors and turbopack build Docs: - Add await to convertToModelMessages() calls (now async in AI SDK v6) - Change LanguageModelV3Prompt → ModelMessage[] in type references - Change LanguageModelV3 → LanguageModel in PrepareStepInfo - Update docs-globals.d.ts convertToModelMessages return type - Add LanguageModel to import inference map DurableAgent: - Update OutputSpecification to match AI SDK v6 Output interface (type→name, parsePartial→parsePartialOutput, parseOutput→parseCompleteOutput, responseFormat now PromiseLike) Turbopack build: - Remove streamdown plugins from MessageResponse (plugins prop API changed in streamdown 2.4.0, causing type mismatch in CI) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix pnpm-workspace.yaml: use double quotes for catalog entries The stage-workbench-with-tarballs.mjs script only strips double quotes when parsing catalog keys. Single-quoted @-scoped entries (e.g., '@types/node') weren't matched, causing "unresolved catalog dependencies" errors in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CI build failures, add changeset - Remove streamdown plugins from reasoning.tsx (same CI type mismatch) - Cast ToolHeader type prop and WorkflowChatTransport to fix type errors - Fix pnpm-workspace.yaml single→double quotes for staging script - Add minor changeset for @workflow/ai (breaking: AI SDK v6 migration) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Sync webpack workbench with turbopack: add chat UI deps and symlinks - Symlink app-shell.tsx, chat-client.tsx, agent_chat workflow, chat API routes into nextjs-webpack - Add matching deps: streamdown, @streamdown/*, shiki, cmdk, nanoid, motion, @radix-ui/react-use-controllable-state Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix circular symlink: restore chat-client.tsx as real file in turbopack The previous commit accidentally converted turbopack's chat-client.tsx into a circular symlink pointing to itself. Webpack's symlink to it then couldn't resolve, breaking both builds on Vercel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add missing deps to webpack: use-stick-to-bottom, radix-ui, @vercel/blob Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix model picker: merge body params via prepareSendMessagesRequest WorkflowChatTransport sends { messages } by default, ignoring the body option from ChatRequestOptions. Use prepareSendMessagesRequest to merge { messages, ...body } so the model selection reaches the API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix WorkflowChatTransport: forward body/headers from ChatRequestOptions The transport hardcoded body: undefined when calling prepareSendMessagesRequest, so extra body params (like model selection) from sendMessage({ body: { model } }) were silently dropped. Now forwards options.body and options.headers to both prepareSendMessagesRequest and the default request body. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix model IDs: use real AI Gateway model names gpt-5.2 and gpt-5.3 don't exist in the AI Gateway. Replace with gpt-4o and gpt-4o-mini. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use correct AI Gateway model IDs: Opus 4.5, GPT-5.2, GPT-5.3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Enable reasoning for all model providers - Anthropic: thinking.type='enabled' with 10k token budget - OpenAI: reasoningEffort='high' - Instructions kept for all models (no longer conditionally removed) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix OpenAI reasoning: use 'medium' effort (GPT-5.3 doesn't support 'high') Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address all PR review comments - Change changeset from minor to patch (repo convention) - Use ?? instead of || for system/instructions fallback - Clean up timeout: store ID, clearTimeout in finally, { once: true } listeners - Update class docstring example to use instructions - Map unrecognized finish reasons to 'other' with validation - Fix duplicate test, align assertion for unrecognized type - Support ^ exponentiation in calculate tool - Remove debug console.log from chat client - Fix ReactNode/ComponentProps type imports in UI components - Remove unused MockLanguageModelV3 re-exports from mock.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove accidentally created empty mock2.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Change changeset back to minor for breaking AI SDK v6 migration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix agent e2e tests: add Vercel world setup for CI The agent e2e tests only configured the local filesystem world but not the Vercel world backend. On CI (Vercel prod tests), this caused VercelOidcTokenError because the world wasn't initialized. Now matches the setup pattern from e2e.test.ts: configures Vercel world with OIDC token and project config from CI environment variables. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Deduplicate e2e test utilities: extract shared code to utils.ts Extract manifest fetching, workflow lookup, world setup, and types into shared utils.ts. Both e2e.test.ts and e2e-agent.test.ts now import from the same source, eliminating ~200 lines of duplication. Shared utilities: - WorkflowManifest interface - fetchManifest() with caching - getWorkflowMetadata() with retry and fallback - setupWorld() handling local/Vercel/Postgres backends Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix missing deploymentUrl args in e2e.test.ts after utils refactor Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add @workflow/ai dep to all workbenches for agent e2e tests All workbenches now have @workflow/ai as a dependency and the 100_durable_agent_e2e.ts symlink, so agent e2e tests run everywhere. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix missing imports in e2e.test.ts: add fetchManifest and sleep The utils refactor removed these imports but they're still used: - fetchManifest: used in stepFunctionAsStartArgWorkflow test - sleep (setTimeout): used in webhookWorkflow test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> |
||
|
|
b8865edaea |
swc-playground: resizable panels, collapsible outputs, vim mode, and dep cleanup (#1370)
* swc-playground: add resizable panels, collapsible outputs, vim mode toggle, and clean up deps - Add resizable split between input editor and output panels using react-resizable-panels - Add collapsible accordion-style output views (workflow/step/client) with chevron toggles - Add vim keybindings toggle (persisted to localStorage) using monaco-vim - Add shadcn Switch and Resizable UI components - Remove unused individual @radix-ui/react-* packages in favor of unified radix-ui package - Remove unused bulk shadcn init dependencies (recharts, sonner, vaul, zod, cmdk, etc.) * Address PR review: fix vim mode race condition, mount timing, and add aria attributes - Add cancellation flag to async monaco-vim import to prevent stale callbacks - Add .catch() for import failure handling - Track editor mount state so vim mode initializes when persisted as enabled - Add aria-expanded and aria-controls to collapsible output panel toggle buttons |
||
|
|
7df13854f8 |
fix: separate infrastructure vs user code error handling (#1339)
* fix: separate infrastructure vs user code error handling in runtime and step handler Transient network errors (ECONNRESET, etc.) during infrastructure calls (event listing, event creation) were caught by a shared try/catch that also handles user code errors, incorrectly marking runs as run_failed or steps as step_failed instead of letting the queue redeliver. - runtime.ts: Move infrastructure calls outside the user-code try/catch so errors propagate to the queue handler for automatic retry - step-handler.ts: Same structural separation — only stepFn.apply() is wrapped in the try/catch that produces step_failed/step_retrying - helpers.ts: Add isTransientNetworkError() and update withServerErrorRetry to retry network errors in addition to 5xx responses - helpers.test.ts: Add tests for network error detection and retry * add changeset * remove withServerErrorRetry and isTransientNetworkError Redundant with undici RetryAgent which already handles 5xx retries and network error retries at the HTTP dispatcher level. * address review feedback: move getEncryptionKeyForRun out of user-code try/catch, re-add 5xx/410 safety net in step handler, relax e2e test assertion * remove serverError5xxRetryWorkflow e2e test This test validated withServerErrorRetry's in-process retry behavior, which was removed. Queue-level retry with process-scoped fault injection is unreliable across serverless instances and too slow for e2e timeouts. * remove serverError5xxRetryWorkflow and fault injection helpers from e2e workflows * remove inline comment about deleted test |