- decompress() now gates only on DecompressionStream (read path), not
CompressionStream — reads work in decompress-only runtimes (Copilot).
- Cross-deployment writes (start({deploymentId}), resumeHook) restrict to
the portable gzip codec via a new compressionPortableOnly flag. zstd
decode needs node:zlib >= 22.15, a property of the reader's runtime that
the SDK (engines: Node 18+) can't guarantee for a different deployment;
same-deployment writes still use zstd since reader == writer (vercel bot).
- Browser zstd WASM is now vendored into web-shared dist and referenced via
a relative new URL('./zstd.wasm', import.meta.url) — a bare package
specifier was left unrewritten by Vite and 404'd. Verified the Vite build
emits the asset (karthikscale3).
- hydrateResourceIOWithKey accepts an optional key and always registers the
zstd decoder, so unencrypted compressed payloads (e.g. local world) are
inflated; web no-key hydration paths now route through it (karthikscale3).
- Clarify the sync-decompress doc contract (best-effort via
process.getBuiltinModule, not "always on Node") (Copilot).
Tests: cross-deployment gzip fallback, unencrypted compressed web
hydration, and zstd WASM ↔ node:zlib compatibility.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Add server-backed exact ID search to the Events tab.
Replace client-side substring filtering with API lookups for full correlation and event IDs so searches work beyond the first loaded page.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix exact ID search dimming and support wrun_ correlation IDs.
Disable group dimming for server search results and accept run IDs in the exact ID parser so run-level correlation search works.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix dimmed row when searching by event ID for run-level events.
Map selectedGroupKey to __run__ for run-level search results so the matched row is treated as related instead of dimmed.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Remove run ID search from Events tab exact ID lookup.
Workflow-server only accepts step, wait, and hook correlation IDs — not wrun_. Update the search placeholder and validation toast accordingly.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Harden exact ID search UX and correlation fetch limits.
Normalize lowercase ULIDs, scope Enter toasts to ID-like input, abort stale searches, disable search when unavailable, expand parser tests, and cap correlation pagination in workflow web.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix search clear race and surface truncated correlation results.
Guard successful exact-ID search against aborted requests, invalidate in-flight work when the input clears, and return truncation metadata from correlation pagination.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Differentiate exact ID search errors from not-found results.
Return a discriminated union from onExactIdSearch and show search errors in the Events tab instead of mislabeling them as missing IDs.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Apply suggestion from @VaguelySerious
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
---------
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
* web: configure vercelPreset() for Vercel deployments
Enables per-route bundle splitting, function-level configuration, and an
accurate Deployment Summary for the packages/web Vercel deployment.
Gated on WORKFLOW_WEB_VERCEL_BUILD (rather than the ambient VERCEL var)
so that the standard build layout consumed by server.js (self-hosted
deployments and the CLI's in-process server via @workflow/web/server) is
still produced when the package is packed as a tarball by the docs
Vercel deployment. Existing VERCEL-based checks in vite.config.ts have
been migrated to this same variable for consistency.
* web: allow WORKFLOW_WEB_VERCEL_BUILD through Turborepo
Turborepo strips environment variables not declared in turbo.json, which
prevented WORKFLOW_WEB_VERCEL_BUILD (set on the Vercel project) from
reaching the build. Declare it in the env list and also include the
.vercel/ directory (where the preset writes react-router-build-result.json)
in the build outputs.
* web: narrow Turbo output to react-router-build-result.json
* web: address PR review feedback
- Check WORKFLOW_WEB_VERCEL_BUILD === '1' explicitly (rejects '0', 'false',
etc.) in both react-router.config.ts and vite.config.ts
- Use double-quoted package name in changeset frontmatter to match the
repo's dominant convention
* wip
* context
* cleanup
* wip
* wip
* Update trace-viewer.tsx
* adjust hovers
* time marker
* tweak
* Update event-list.tsx
* header
* Update utils.ts
* search
* Update timeline.tsx
* ship
* Update timeline.tsx
* Update event-list.tsx
* Create icons.tsx
* Update trace-viewer.tsx
* Update utils.ts
* new trace viewrr
* Update workflow-trace-view.tsx
* Fix: Runtime crash when `trace` is `undefined`: `NewTraceViewerComponent` receives `undefined` (cast as `Trace`) and immediately accesses `trace.spans`, causing "Cannot read properties of undefined".
This commit fixes the issue reported at packages/web-shared/src/components/trace-viewer-new.tsx:28
**Bug Analysis:**
In `trace-viewer-new.tsx`, the `buildTrace` function returns `undefined` when `!run?.runId`. The result is stored in `trace` which has type `TraceWithMeta | undefined`. However, on line 28, `trace` is passed directly to `NewTraceViewerComponent` with a type assertion `trace as Trace`, which silences the TypeScript error but does not prevent the runtime crash.
Inside `NewTraceViewerComponent` (in `new-trace-viewer/trace-viewer.tsx` line 98), the component immediately accesses `trace.spans`:
```tsx
<ActiveSpanProvider spans={trace.spans}>
```
When `trace` is `undefined`, this produces: `TypeError: Cannot read properties of undefined (reading 'spans')`.
This happens whenever the component renders before `run.runId` is available — a normal scenario during initial loading.
The old `WorkflowTraceViewer` component (in `workflow-trace-view.tsx` line 953) correctly handles this with a `if (!trace)` guard that renders a loading skeleton. The new component lacks this guard.
**Fix:**
Added a null guard in `trace-viewer-new.tsx` that checks `if (!trace)` before rendering `NewTraceViewerComponent`. When trace is undefined, a simple loading placeholder is rendered instead. This prevents the crash and follows the same pattern as the existing `WorkflowTraceViewer`. The `as Trace` cast on line 28 is now safe because `trace` is guaranteed to be defined after the guard.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: mitul-s <mitulxshah@gmail.com>
* fixed edge view
* Update timeline.tsx
* ship it
* cleanup
* Update trace-viewer.tsx
* wip
* wip
* Add detail pane to the new trace viewer + cleanup (#1714)
* detail pane
* cleanup
* cleanup
* cleanu
* fix
* Update workflow-trace-view.tsx
* cleanup
* Update entity-detail-panel.tsx
* move sidebar provider into export
* dead code
* cleaning up more
* remove decorative indenting & output loader
* fix bars
* Update inspector-theme.ts
* cleanups
* Update copyable-data-block.tsx
* cleanup
* chonky
* Update timeline.tsx
* bug fxi
* marker lines
* wip
* changes
* Update event-list.tsx
* rounded
* Update event-list.tsx
* middle truncate component
* cleanup
* height updatres
* colour fixes
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
* 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>
* [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)
* feat: add clickable Run reference rendering in observability UI
When a serialized Run object appears in step input/output data, it is
now rendered as a clickable purple badge showing the runId. Clicking
navigates to the target run's detail page.
Changes:
- serialization-format.ts: Add RunRef type, isRunRef(), serializedRunToRunRef(),
and 'Run' entry in observabilityRevivers
- data-inspector.tsx: Add RunRefInline component (purple badge), RunClickContext,
collapseRefs() to make refs non-expandable in ObjectInspector
- attribute-panel.tsx: Thread onRunClick prop, wrap in RunClickContext.Provider
- entity-detail-panel.tsx: Thread onRunClick prop
- run-trace-view.tsx: Thread onRunClick prop
- workflow-trace-view.tsx: Thread onRunClick prop, reset selected span on run change
- trace-span-construction.ts: Show step name for builtin steps instead of empty string
- hydration.ts: Re-export RunRef types
- run-detail-view.tsx: Add handleRunRefClick that navigates to /run/{targetRunId}
* fix: guard collapseRefs against class instances and memoize result
Only recurse into plain objects (prototype is Object.prototype or null)
to avoid stripping class instances like Date, Error, Map, etc. that
have their own rendering in NodeRenderer. Also memoize the collapsed
result to avoid recomputing on every render.
* fix: detect Run instances in Instance reviver instead of fake Run serde key
The Run class goes through the standard Instance serialization pipeline
(WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE), not a dedicated 'Run' key.
Move the RunRef detection into serializedInstanceToRef() which checks
if the className is 'Run' and the data contains a runId string, then
returns a RunRef instead of a generic ClassInstanceRef.
* fix: use serializedInstanceToRef in web and CLI Instance revivers
Both the web and CLI hydration layers override the observabilityRevivers
Instance handler with their own implementation (for react-inspector
named constructors and CLI inspect.custom respectively), bypassing the
RunRef detection in serializedInstanceToRef. Fix by calling
serializedInstanceToRef first and returning a RunRef when detected.
* Make encrypted markers clickable to trigger decryption and detect encryption at run level before span selection
* Make encrypted markers clickable to trigger decryption and detect encryption at run level before span selection
* Make encrypted markers clickable to trigger decryption and detect encryption at run level before span selection
* Make encrypted markers clickable to trigger decryption and detect encryption at run level before span selection
* Make encrypted markers clickable to trigger decryption and detect encryption at run level before span selection
The previous pre-release versions (4.x.y-beta.N) caused two issues:
- semver.inc('4.0.0-beta.N', 'major') returns 4.0.0, not 5.0.0
- Pre-release numbers carried over (beta.61 -> beta.62 instead of beta.0)
Setting all versions to 4.0.0 (non-pre-release) ensures a clean major
bump to 5.0.0-beta.0. Also removes @workflow/swc-playground-wasm from
the changeset and pre.json since it is a private package.
* feat: export semantic error types and add API reference documentation
Add missing error exports (HookNotFoundError, EntityConflictError,
RunExpiredError, TooEarlyError, ThrottleError, RunNotSupportedError,
WorkflowWorldError) to workflow/internal/errors. Create new error
classes for world-level semantics. Tighten TSDoc comments on all
error classes. Add API reference docs for all error types.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use @setup declarations, workflow/errors import, and errors/ doc section
- Replace @skip-typecheck with proper `declare` + `// @setup` lines
so code samples are typechecked but setup lines hidden from readers
- Add `workflow/errors` export to package.json (public API, replaces
`workflow/internal/errors` in docs)
- Add `workflow/errors` path mapping in docs-typecheck type-checker
- Add HookConflictError to re-export list
- Move all error docs under api-reference/workflow/errors/ subdirectory
- Update all internal cross-references and links
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: move error docs to top-level workflow-errors section
- Move semantic error docs to api-reference/workflow-errors/ (matching
the workflow/errors import path, like workflow-api for workflow/api)
- Keep FatalError and RetryableError in api-reference/workflow/ since
they're imported from workflow, not workflow/errors
- Fix all cross-reference links
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update HTTP debug logger JSDoc to clarify scope
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: make TooEarlyError.retryAfter a number (seconds) matching WorkflowWorldError
TooEarlyError.retryAfter is now seconds (number) instead of a Date,
consistent with ThrottleError and WorkflowWorldError. The conversion
from seconds to Date is done at the consumer site (step-handler) rather
than at construction time.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review feedback on docs accuracy
- WorkflowWorldError docs: add status, code, url, retryAfter properties
to TSDoc; clarify that .is() only matches direct instances (not
subclasses); use instanceof in catch-all example
- TooEarlyError/ThrottleError docs: mark retryAfter as optional (?)
to match actual type definitions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 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>