Commit Graph

192 Commits

Author SHA1 Message Date
Nathan Rajlich 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.
2026-05-01 18:39:01 +00:00
Karthik Kalyan 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>
2026-04-29 09:22:05 -07:00
Karthik Kalyan 56ba32feb8 Bump vite (#1827) 2026-04-22 14:05:38 -07:00
Nathan Rajlich 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.
2026-04-21 00:44:49 -07:00
Pranay Prakash 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>
2026-04-20 13:27:48 -07:00
Pranay Prakash 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>
2026-04-17 07:40:25 -07:00
Nathan Rajlich 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.
2026-04-16 22:50:46 +00:00
Nathan Rajlich 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)
2026-04-15 18:01:48 -07:00
Nathan Rajlich 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.
2026-04-13 12:30:30 -07:00
John Lindquist 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>
2026-04-11 17:40:07 -07:00
Nathan Rajlich 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
2026-04-09 15:10:45 -07:00
Peter Wielander 873b4e2bb4 [core] Refactor getWorld interface to be asynchronous (#942) 2026-04-09 13:54:32 -07:00
Gregor Martynus 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: b1930b307d

Signed-off-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>

---------

Signed-off-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 16:24:51 +00:00
Nathan Rajlich 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
2026-04-09 00:46:03 +00:00
Nathan Rajlich 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
2026-04-07 23:35:55 +00:00
Nathan Rajlich 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
2026-04-03 21:03:59 +00:00
Peter Wielander d8aaf27c79 [core] Enforce single ALS context to protect against duplicate module and caching issues (#1591) 2026-04-03 13:47:19 -07:00
Nathan Rajlich 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 cdf90d5a38 (#1541)

* Fix missing </h1> closing tag and add article 'the' before 'Workflow SDK' in docs
2026-04-02 19:39:02 +00:00
Peter Wielander d119c740d0 [builders] Fix import.meta.url missing when using CJS (#1509) 2026-03-30 12:18:39 -07:00
Nathan Rajlich 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.
2026-03-30 18:17:53 +00:00
Harpreet 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>
2026-03-29 16:05:39 -07:00
Matan Kushner 52db376c39 Support projects with Node.js step dependencies in vitest plugin (#1524)
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-03-26 23:05:05 +00:00
Pranay Prakash 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>
2026-03-24 10:04:42 -07:00
Pranay Prakash 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>
2026-03-23 21:25:28 -07:00
Pranay Prakash 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>
2026-03-23 20:58:29 -07:00
Pranay Prakash 8366fae439 chore: bump nuxt ecosystem dependencies (#1500) 2026-03-23 20:32:58 -07:00
Pranay Prakash 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>
2026-03-23 20:08:35 -07:00
Pranay Prakash 61d40ff4cf chore: bump express and fastify in workbenches (#1499)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:57:08 -07:00
Pranay Prakash 730c95bfb9 chore: bump @nestjs/* to ^11.1.17 (#1497)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:32:30 -07:00
Pranay Prakash db394c43db chore: bump hono to ^4.12.8 (#1495)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:30:01 -07:00
JJ Kasper 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 a2c0c7e6d9.

* fix(next): stabilize deferred discovery on canary builders

* revert canary bump

* fix(next): stabilize local e2e discovery for webpack and turbopack

* fix(next): avoid copying sdk builtins in deferred step routes

* fix(next): restore deferred sdk step registration on canary

* fix(next): stabilize deferred discovery under canary watch
2026-03-23 10:56:30 -07:00
Peter Wielander 823f58e5c6 Revert "Add support for calling start() inside workflow functions (#1133)" (#1475)
This reverts commit e889860984.
2026-03-20 17:04:28 -07:00
Pranay Prakash 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>
2026-03-20 13:00:17 -07:00
Pranay Prakash afa3931a59 Add stress benchmarks: 1000-step, data payload, and stream tests (#1214) 2026-03-19 16:59:09 -07:00
Pranay Prakash 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>
2026-03-18 10:47:46 -07:00
Pranay Prakash 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>
2026-03-17 12:29:26 -07:00
Peter Wielander 2c80ec7217 DurableAgent: various compatibility fixes (#1385) 2026-03-17 09:47:04 -07:00
Nathan Rajlich 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.
2026-03-16 15:49:17 -07:00
Pranay Prakash 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>
2026-03-13 15:20:06 -07:00
Nathan Rajlich 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
2026-03-13 08:34:22 +00:00
Nathan Rajlich 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
2026-03-13 00:39:20 +00:00
Nathan Colosimo d72c82220f Fix bug where the SWC compiler bug prunes step-only imports in the client-mode transformation
* first pass

* fix failing test

* Update changeset to fix SWC compiler issue

Fix bug where the SWC compiler bug prunes step-only imports in the client-mode transformation

Signed-off-by: Nathan Rajlich <n@n8.io>

* DCO Remediation Commit for nathancolosimo <nathancolosimo@gmail.com>

I, nathancolosimo <nathancolosimo@gmail.com>, hereby add my Signed-off-by to this commit: 150d175e7d
I, nathancolosimo <nathancolosimo@gmail.com>, hereby add my Signed-off-by to this commit: 429747fcc3

Signed-off-by: nathancolosimo <nathancolosimo@gmail.com>

---------

Signed-off-by: Nathan Rajlich <n@n8.io>
Signed-off-by: nathancolosimo <nathancolosimo@gmail.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
2026-03-11 09:11:19 -07:00
Nathan Rajlich 54758f30ab Remove hard-coded VERCEL_DEPLOYMENT_KEY from nextjs-turbopack workbench (#1319)
Vercel now automatically sets VERCEL_DEPLOYMENT_KEY to a unique value
for every deployment, so the hard-coded value is no longer necessary.
2026-03-11 01:42:51 +00:00
Nathan Rajlich 9781afb490 test: enable encryption on nextjs-turbopack workbench (#1262)
* test: enable encryption on nextjs-turbopack workbench

Set VERCEL_DEPLOYMENT_KEY to enable e2e encryption for the
nextjs-turbopack workbench project. This allows the e2e test
suite to exercise encrypted workflow runs alongside existing
unencrypted runs for backwards compatibility testing.

* fix: fetch encryption key from API in external contexts (e2e, CLI)

Use VERCEL=1 env var to distinguish serverless runtime (can use local
HKDF derivation) from external contexts like e2e test runners, CLI,
and web dashboard (must fetch derived key from Vercel API).

Previously, when VERCEL_DEPLOYMENT_ID matched but VERCEL_DEPLOYMENT_KEY
was absent (e.g., e2e runner has the deployment ID from CI but not the
key), getEncryptionKeyForRun returned undefined — silently skipping
decryption. Now external contexts always use the API fetch path.

* fix: add 429 retry with exponential backoff to fetchRunKey

The e2e runner calls fetchRunKey for every run, which can exceed the
API rate limit (30/min per team) when many tests run in parallel.
Add retry logic with exponential backoff + jitter (up to 5 retries)
and respect the Retry-After header when present.

* Fix: Missing NaN guard on `retry-after` header parsing causes `setTimeout(r, NaN)` to resolve immediately (0ms), triggering rapid-fire retries against a rate-limited API endpoint.

This commit fixes the issue reported at packages/world-vercel/src/encryption.ts:138

**Bug Analysis:**

In `packages/world-vercel/src/encryption.ts`, the `fetchRunKey` function handles HTTP 429 (rate limited) responses by reading the `retry-after` header and computing a delay. The original code was:

```js
const retryAfter = response.headers.get('retry-after');
const delay = retryAfter
  ? Number.parseInt(retryAfter, 10) * 1000
  : BASE_DELAY_MS * 2 ** attempt + Math.random() * 500;
```

Per RFC 9110, the `Retry-After` header can contain either a number of seconds OR an HTTP date string (e.g., `"Wed, 21 Oct 2015 07:28:00 GMT"`). When the header contains a date string or any non-numeric value, `Number.parseInt(retryAfter, 10)` returns `NaN`. Since the header string is truthy (non-empty), the ternary takes the parseInt path rather than the exponential backoff fallback. `NaN * 1000` evaluates to `NaN`, and `setTimeout(r, NaN)` resolves immediately (~0ms, confirmed by testing). This causes rapid-fire retries (up to 5 iterations with zero delay) against a rate-limited API endpoint, wasting resources and potentially worsening the rate-limiting situation.

**Fix:**

Parse the integer first, then check if the result is NaN before using it. If NaN, fall back to exponential backoff with jitter. This matches the established pattern already used in `packages/world-vercel/src/utils.ts` (lines 309-315) where the same header is properly guarded with `!Number.isNaN(parsed)`.

```js
const retryAfterHeader = response.headers.get('retry-after');
const parsedRetryAfter = retryAfterHeader
  ? Number.parseInt(retryAfterHeader, 10)
  : NaN;
const delay = !Number.isNaN(parsedRetryAfter)
  ? parsedRetryAfter * 1000
  : BASE_DELAY_MS * 2 ** attempt + Math.random() * 500;
```

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: TooTallNate <n@n8.io>

* fix: add --decrypt to e2e CLI inspect calls

With encryption enabled on the nextjs-turbopack workbench, CLI inspect
output shows encrypted data as placeholders. The e2e tests check for
actual output values, so they need --decrypt to see the real data.

* fix: remove manual 429 retry, rely on undici RetryAgent; add changeset

The shared RetryAgent from getDispatcher() already handles 429/5xx
retries with exponential backoff and Retry-After header support.
The manual retry loop in fetchRunKey was redundant.

Also add changeset for the VERCEL=1 external context fix.

* refactor: use @ts-expect-error on dispatcher instead of as any cast

---------

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2026-03-09 14:00:25 -07:00
Pranay Prakash c71befe8ec fix(core): premature suspension when hooks have buffered payloads with concurrent pending entities (#1294)
* test: reproduce hook+sleep promiseQueue regression

Add failing tests that reproduce a regression from #1246 where the
sleep's WorkflowSuspension fires before all hook payloads are delivered
when a hook and sleep run concurrently.

Root cause: when the null event fires, the sleep queues a suspension
through promiseQueue. After the first hook payload resolves, subsequent
hook payload resolutions are queued AFTER the sleep suspension, causing
the workflow to terminate prematurely via Promise.race.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: expand tests to isolate bug to hook + pending entity pattern

Add control tests proving sequential steps are NOT affected by the
promiseQueue regression, isolating the bug to hooks specifically:

Failing (hook-based):
- hook + sleep: all 3 payloads → step invocation
- hook + sleep: 2 payloads → return
- hook + incomplete step: 2 payloads → return

Passing (step-based controls):
- sleep + sequential steps: both step events exist
- sleep + sequential steps: only 1st step completed
- incomplete step + sequential steps: all step events exist
- hook only (no concurrent entity): payloads + step

The bug is: any entity that queues a suspension through promiseQueue
at null-event time preempts hook payload delivery, because hooks
buffer payloads in payloadsQueue and only resolve them one-at-a-time
as the workflow code iterates. Steps are unaffected because each step
has its own events consumed before null fires.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(core): move WorkflowSuspension calls back to setTimeout(0) from promiseQueue

The promiseQueue refactor (#1246) moved ALL promise resolutions through
the queue, including WorkflowSuspension calls. This caused premature
termination when hooks had buffered payloads and a concurrent entity
(sleep or incomplete step) was pending:

1. Null event fires → all subscribers run
2. Sleep/step queues WorkflowSuspension via promiseQueue.then()
3. Hook's next payload resolution is also queued via promiseQueue.then()
4. Suspension fires first → Promise.race terminates workflow
5. Hook payload never delivered → infinite retry loop

Fix: move WorkflowSuspension calls back to setTimeout(0) (macrotask).
Suspensions fire AFTER all microtask-based deliveries (promiseQueue
resolve/reject for step results and hook payloads) have completed.
Non-suspension resolve/reject calls remain on promiseQueue for
deterministic ordering of data delivery.

Affected paths:
- step.ts: null event handler
- sleep.ts: null event handler
- hook.ts: null event handler, eventLogEmpty suspension, dispose suspension

* fix(core): use pendingDeliveries counter + scheduleWhenIdle for suspensions

Replace all nested setTimeout/promiseQueue suspension patterns with a
clean idle-polling mechanism:

1. pendingDeliveries counter: incremented before async hydration (step
   results, hook payloads), decremented in finally block after delivery
2. scheduleWhenIdle(ctx, fn): polls via setTimeout(0) → check counter →
   if > 0, wait for promiseQueue.then() → repeat. Only fires fn when
   pendingDeliveries reaches 0.

This correctly handles:
- Sync deserialization (no encryption): counter is 0, fires immediately
  after first setTimeout(0)
- Async deserialization (with encryption): waits for decryption to
  complete before firing
- Multi-round hook payload delivery: each createHookPromise() call
  increments the counter, preventing premature suspension between
  delivery rounds

Also adds payloadsQueue.length check to hook null handler — don't
trigger suspension if buffered payloads can satisfy pending awaits.

Tests now run in both sync and async modes (14 total = 7 scenarios x 2).
All 164 tests pass.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
2026-03-07 11:02:08 -08:00
Nathan Rajlich 36a901d2d2 feat: expose workflow and step names in metadata functions (#1285)
* feat: expose workflow and step names in metadata functions

Add `workflowName` to `WorkflowMetadata` and `stepName` to `StepMetadata`,
making them available via `getWorkflowMetadata()` and `getStepMetadata()`.
This is useful for adding context to structured logs, collating logs during
investigations, and bucketing errors by workflow/step name.

Closes #1103

* add changeset

* Apply suggestion from @TooTallNate

Signed-off-by: Nathan Rajlich <n@n8.io>

---------

Signed-off-by: Nathan Rajlich <n@n8.io>
2026-03-06 21:51:16 +00:00
Peter Wielander 456c1aa455 Add vitest plugin for testing full workflows without setting up a server (#1237) 2026-03-05 16:08:48 -08:00
Pranay Prakash 30e24d441e Merge commit from fork
* Prevent deterministic tokens in createWebhook

Remove the `token` option from `WebhookOptions` to prevent unauthorized
access to the public webhook endpoint. Webhook tokens are now always
randomly generated. Deterministic tokens remain available for
`createHook()` with server-side `resumeHook()`.

**BREAKING CHANGE**: `createWebhook()` no longer accepts a `token` option.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address PR feedback: throw on token, revert unnecessary doc changes, replace examples

- createWebhook() now throws an error if token is passed at runtime
  instead of silently stripping it
- Revert unnecessary changes to create-hook.mdx (only keep
  slack_webhook -> slack_messages prefix change)
- Replace Slack webhook examples in create-webhook.mdx and hooks.mdx
  with generic event collector examples that make sense without
  deterministic tokens

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address andresriancho's feedback: stricter token check, add unit test

- Use `token !== undefined` instead of `if (token)` for correctness
  (empty string should also be rejected)
- Add unit test verifying createWebhook({ token: "anything" }) throws
  the expected error message

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* change changeset to minor

* Fix docs typechecker to actually resolve workflow module types

The docs typechecker was silently failing to resolve `workflow` imports
because:
1. repoRoot was computed 4 levels up instead of 3
2. No path mappings for workspace packages
3. Missing TS lib files for disposable and DOM iterables

This meant all imported symbols became `any` and type errors in docs
code samples went undetected.

Fixes:
- Fix repoRoot: '../../../..' -> '../../..'
- Add path mappings for workflow, @workflow/core, @workflow/ai, etc.
- Add lib.dom.iterable.d.ts and lib.esnext.disposable.d.ts
- Fix regex that ate first line of some code blocks
- Auto-skip error demonstration code blocks
- Add respondWith to global Request for webhook doc patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Revert "Fix docs typechecker to actually resolve workflow module types"

This reverts commit 02d42ac866e6db1b6d016339739344cdd9538e90.

* chore: trigger CI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-05 10:33:53 -08:00
JJ Kasper a9fea9132e Update workbench tests to build and run outside of monorepo (#1230)
* Setup fixes

* ci: run local e2e against staged tarball workbenches

* ci: update staged workbench tarball setup script

* chore: set nextjs workbenches back to next 16.1.6

* update lock

* test(e2e): resolve workbench path from WORKBENCH_APP_PATH

* fix: address deferred builder issues outside monorepo

* ci: stage tarball workbenches only for nextjs local e2e

* fix(next): discover deferred steps imported via workflows

* test(core): gate deferred step-discovery dev test to canary

* test(e2e): cover cross-file imported step in build/start lanes

* fix(e2e): use local manifest in local runs and relax dev rebuild timeout

* fix(workbench): add imported-step workflow symlink for sveltekit/astro

* test(e2e): scope imported-step workflow test to nextjs lanes

* fix(next): rebuild deferred entries on discovered file updates

* fix(next): watch transitive deferred step deps for dev rebuilds

* fix(next): restore socket-driven deferred step rebuilds

* add changeset

* chore: address review feedback on deferred e2e updates

* fix(cli): guard stream flush against closed write streams
2026-03-03 11:17:39 -08:00
Nathan Rajlich 809339ba1c fix(builders): enable directive discovery in dot-prefixed files and directories (#1228)
* fix(builders): enable directive discovery in dot-prefixed files and directories

The glob-based file scanning in getInputFiles() was using tinyglobby's
default behavior which skips dot-prefixed files/directories. This prevented
discovering files with 'use step' / 'use workflow' directives inside paths
like .config/step.ts or .hidden-workflow.ts.

Switch to relative glob patterns with per-directory cwd and dot: true to
ensure dot-files are scanned while still respecting the explicit ignore
list (.git, .next, .vercel, etc.).

* test: add E2E tests for dot-directory directive discovery

Add workbench fixtures and E2E tests verifying that 'use step' / 'use
workflow' directives inside dot-prefixed directories (.well-known/agent/)
are correctly discovered, included in manifests, and executed at runtime.

* fix: normalize paths in getInputFiles tests for Windows compatibility

tinyglobby returns forward-slash paths even on Windows, while Node's
path.join() uses backslashes. Normalize both sides to forward slashes
before comparison.

* refactor: use dirname() instead of join(.., '..') in test helper

Cleaner and more idiomatic way to get the parent directory.

* fix: add .nuxt and other build tool dot-directories to ignore list

With dot: true enabled, framework build output directories like .nuxt/
are now traversed and their generated files picked up as input files.
This caused a circular dependency in the nuxt builder where generated
.nuxt/workflow/steps.mjs was being re-discovered as an input file.

Add .nuxt, .turbo, .cache, .yarn, and .pnpm-store to the ignore list,
matching the directories already ignored by the eager builder's watcher.
2026-03-02 14:36:12 -08:00