* Add isWebhook flag to prevent hooks from being resumed via public webhook endpoint
Hooks created with createHook() are now non-resumable via the public webhook
endpoint by default (isWebhook=false). Only hooks created with createWebhook()
set isWebhook=true, allowing them to be resumed via the public URL.
Also adds HookNotFoundError thrown by all world backends when a webhook
token doesn't match any hook, and an e2e test for the new behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix world-local: default isWebhook to false and fix test assertions
- Default isWebhook to false at write time in events-storage
- Default isWebhook to false at read time in hooks-storage (for old data)
- Update test assertions to match HookNotFoundError message
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix: default isWebhook to true for backwards compat, add postgres migration
- Revert read-side default to `isWebhook ?? true` in world-local for
backwards compatibility with existing hooks that predate the field
- Add postgres migration 0009 to add `is_webhook` column with default true
- Update drizzle schema to match
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 browser-compatible AES-GCM to core and HKDF key derivation to world-vercel
* update changeset
* Move HKDF key derivation server-side: API returns per-run derived key
* Refactor encrypt/decrypt to accept CryptoKey, export importKey for callers to import once per run
* Overload getEncryptionKeyForRun: accept context for start(), fetch WorkflowRun in resume-hook
* Split changeset into per-package descriptions for world, world-vercel, and core
* Remove unnecessary Uint8Array.from() wrapper around Buffer.from()
* Use zod to parse Vercel API response
* fix: restore world-vercel files to main versions
The rebase incorrectly picked up older versions of these files from
early encryption branch commits. The main versions are correct and
up-to-date.
* fix: add type cast for hydrateStepReturnValue return in hook.ts
* Make decryption an explicit opt-in for o11y tooling
* Restore encrypted data handling in o11y hydration layer
* Use EncryptedDataRef with util.inspect.custom for CLI encrypted data display
* Fix Decrypt button crash: use correct 'refresh' callback from useWorkflowResourceData
* Implement client-side decryption for web o11y with getEncryptionKeyForRun RPC
* Fix CLI decrypt: fetch WorkflowRun for key resolution, cache per runId
* Use named constructor pattern for encrypted data display in web o11y
* Decrypt event data when encryption key is available after Decrypt button click
* Lift encryption key to run-level state, auto-decrypt on fetch, fix field pollution
* Re-load expanded event data when encryption key becomes available
* Consolidate Decrypt to title bar Button, remove sidebar decrypt card
* Add hover tooltip to Decrypt button explaining scope and state
* Show flat Encrypted label for encrypted fields, use Lucide Lock icon in DataInspector
* Render eventData subfields individually to avoid encrypted markers in collapsed preview
* Revert: render eventData subfields individually
* Fix Lock icon vertical alignment in DataInspector encrypted label
* update changeset
* Update CLI, web, and stream callers for CryptoKey: importKey at resolution sites
* Pass teamId to the get-key endpoint
* fix: remove unused DataInspector import in events-list.tsx
* fix: restore world-vercel files to base branch versions
Cherry-pick conflict resolution incorrectly took the older opt-in-decrypt
versions of these files, reverting improvements from main (dispatcher,
createGetEncryptionKeyForRun extraction, nullable key response).
* fix: address PR review feedback
- Remove duplicate AttributePanel/EventsList rendering in entity-detail-panel.tsx.
Thread encryptionKey into the existing EventsList render instead.
- Restore missing re-exports (isClassInstanceRef, isStreamId, isStreamRef)
in web-shared/src/index.ts to maintain backwards compatibility.
- Add 'error' to replaceEncryptedWithMarkers field list in web-shared
hydration.ts to match the decrypt path.
- Extend CLI hydration eventData decrypt/placeholder to cover all known
serialized fields (output, metadata, payload) not just result/input.
- Add 'error' to CLI replaceEncryptedWithRef field list.
- Remove invalid encryptionKey option from useWorkflowResourceData call
(hook doesn't support it yet), add TODO.
- Add 4 unit tests for hydrateDataWithKey in serialization-format.test.ts:
encrypted+key decrypts, encrypted+noKey returns raw, non-encrypted
hydrates normally, non-Uint8Array legacy data passes through.
* feat: thread encryptionKey through useWorkflowResourceData hook
Instead of leaving a TODO, implement the encryptionKey support directly:
- Add optional encryptionKey to useWorkflowResourceData options
- When key is available, use hydrateResourceIOWithKey (async decrypt)
instead of hydrateResourceIO for all resource types
- Remove redundant hydrateResourceIO from fetchResourceWithCorrelationId
* fix: address comprehensive review feedback on PR #1256
High priority:
- Gate showStream key fetch on --decrypt flag, warn when --decrypt
used without --run
- Fix workflow-server-actions.server.ts missing cryptoKey params
(undefined for both getExternalRevivers and getDeserializeStream)
- Add hydration + decryption to listEvents (was completely missing)
- Fix error/eventData display: check isEncryptedMarker before
hasDisplayContent so encrypted markers don't silently disappear
Medium priority:
- handleDecrypt: use toast.error() instead of console.error for
user-visible feedback on key fetch failures
- CLI maybeDecryptFields: add try/catch with graceful fallback to
encrypted placeholders + warning, also decrypt error field
- use-resource-data: wrap hook/sleep hydrate() in try/catch to
prevent stuck loading state on decryption errors
- Decrypt button: also check run.error and step input/output for
encrypted markers, not just run.input/output
Low priority:
- event-list-view: add .catch() to re-load useEffect promise
- Export ENCRYPTED_DISPLAY_NAME from hydration.ts and import in
data-inspector.tsx instead of raw 'Encrypted' string
* Bring back sitemap
* Bring back sitemap
* Add pre commit hooks for sitemap
* Add pre commit hooks for sitemap
* Fix failing queue test
* Revert queue test failure
* Add unit tests
* Fix sleep state in side panel
* Side panel improvements
* Side panel improvements
* Side panel improvements
* Side panel improvements
* Side panel improvements
Replaces the Chrome DevTools color scheme with one inspired by
Node.js util.inspect() ANSI stylizing: green strings, yellow
numbers/booleans, grey undefined, magenta dates, cyan functions,
and neutral property names. Adds an extended theme context to
color Date instances (react-inspector has no built-in theme key
for dates). Removes color-hash dependency which is no longer used.
Consolidate all ObjectInspector usage into a single DataInspector component
with shared theming, custom nodeRenderer for StreamRef/ClassInstanceRef, and
stream click context. Update all consumers (attribute-panel, events-list,
event-list-view, stream-viewer, conversation-view) to use DataInspector
instead of direct ObjectInspector or JSON.stringify. Fix class instance
hydration to create objects with dynamically-named constructors so
react-inspector shows proper class names via constructor.name.
* [workflow o11y] rebase on latest main and keep targeted UI/builders changes
Rebase the branch intent onto latest main by preserving web-shared UI refactors and builders base-builder updates while taking main for hydration and data-fetching behavior elsewhere.
Co-authored-by: Cursor <cursoragent@cursor.com>
* [workflow o11y] align web-shared hydration revivers with main
Revert the hydration reviver delta for URL, URLSearchParams, and Headers so web-shared matches main behavior while keeping the targeted UI/builders-only scope on this branch.
Co-authored-by: Cursor <cursoragent@cursor.com>
* [workflow o11y] restore PR #1017 detail-panel decoupling
Bring the web-shared trace/detail panel files back in sync with main so PR #1017 behavior is preserved and not regressed on this branch.
Co-authored-by: Cursor <cursoragent@cursor.com>
* [workflow o11y] restore PR #1018 react-inspector sidebar updates
Bring web-shared o11y rendering files back in sync with main so ObjectInspector-based sidebar rendering and related UI behavior from PR #1018 remain intact on this branch.
Co-authored-by: Cursor <cursoragent@cursor.com>
* [workflow o11y] keep custom viewers and apply inspector rendering
Preserve the branch-specific event list and stream viewer UX while applying react-inspector rendering to payload/chunk data so complex hydrated values render correctly without reverting custom UI behavior.
Co-authored-by: Cursor <cursoragent@cursor.com>
* [workflow o11y] trace viewer UX improvements and Geist alignment
- Add live tick animation, context menu, and cancel run support from PR #984
- Decouple side panel styling to use Geist design tokens (inline styles)
- Fix sleep span detail panel showing events instead of wait entity attributes
- Fix stream viewer flickering by removing unstable object deps
- Remove Chunks/Output toggle from stream viewer, show only chunks
- Add resolve hook modal, wake-up sleep, and cancel run plumbing
- Add loading skeleton for stream viewer
Co-authored-by: Cursor <cursoragent@cursor.com>
* Bug fixes
* Bug fixes
* Bug fixes
* Bug fixes
* Bug fixes
* Bug fixes
* Bug fixes
* Bug fixes
* Bug fixes
* Bug fixes
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
## Summary
Replace the custom `JSON.stringify`-based data renderer with [`react-inspector`](https://www.npmjs.com/package/react-inspector)'s `ObjectInspector` component for proper display of all JavaScript types in the o11y sidebar panel.
## Before
Data was rendered via `JSON.stringify(value, null, 2)` in a `<pre>` block. Non-plain-object types like `Map`, `Set`, `URLSearchParams`, `Date`, `Error`, `RegExp`, and typed arrays all rendered as `{}` or lost their type information.
## After
Uses `react-inspector` (the same rendering engine as Chrome DevTools) to display data as expandable tree views with proper type labels:
- `URLSearchParams {size: 12}` with entries visible
- `Map(3)` / `Set(5)` with contents
- `Date "2026-02-11T..."`
- `Error {message: "...", stack: "..."}`
- `Uint8Array(32)` with byte values
## Implementation
- Replace `JsonBlock` function with `DataInspector` component using `ObjectInspector`
- Custom theme with transparent background and design system CSS variables (`--ds-gray-*`, `--font-mono`)
- Dark/light mode support via `useDarkMode` hook
- `StreamRef` and `ClassInstanceRef` objects still render as custom components (clickable stream links and styled class cards)
- Expand level defaults to 2 for a good overview without overwhelming
## Dependencies
- Added `react-inspector@9` to `@workflow/web-shared`
## Summary
Render the `EntityDetailPanel` (step/hook/event detail sidebar) outside the trace viewer's `TraceViewerContextProvider`, so hydrated data no longer passes through the web worker's `postMessage` structured clone boundary.
## Problem
The trace viewer uses a web worker for span layout calculations, sending data via `postMessage` which requires structured cloning. After moving hydration client-side, the hydrated data (containing `URLSearchParams`, `Headers`, and other non-structurally-cloneable types) was embedded in trace span attributes and sent to the worker, causing `DOMException: URLSearchParams object could not be cloned`.
## Solution
Render the detail panel as a **sibling** to the trace timeline instead of nested inside the context:
```
Before: After:
TraceViewerContext [flex row]
└─ Timeline ├─ TraceViewerContext
└─ Panel (inside worker) │ └─ Timeline (worker)
└─ Panel (direct props)
```
- `WorkflowTraceViewer`: flex layout with timeline + panel as siblings
- `SelectionBridge`: lightweight component inside context that reads the selected span and passes it up via callback
- `EntityDetailPanel`: no longer imports `useTraceViewer()`, receives selected span info via `selectedSpan` prop
- Panel has independent width/resize state (no longer coupled to the trace viewer context)
- Strip `input`/`output`/`error` from trace span attributes to keep worker data lightweight
## Also fixes
- Stale `customPanelComponent` bug — the panel was stored in context state during initialization and never updated, causing data to be lost on re-renders
## Summary
Split the serialization/deserialization logic into environment-specific layers so data hydration can happen client-side in the browser. This is a prerequisite for e2e encryption where decryption keys are only available in the browser.
## Architecture
### Layer 1: `@workflow/core/serialization-format` (new, browser-safe)
- Format prefix encoding/decoding (`devl`, future `encr`, etc.)
- Generic `hydrateData()` dispatch — handles Uint8Array (v2 binary), legacy arrays (v1), and plain values
- `hydrateResourceIO(resource, revivers)` resource-type dispatcher (step/hook/event/workflow field mapping)
- `ClassInstanceRef` (plain data class, no `node:util` dependency)
- `StreamRef`, type guards (`isStreamRef`, `isStreamId`, `isClassInstanceRef`), utility functions (`extractStreamIds`, `truncateId`)
- Shared `observabilityRevivers` for stream/class/step display overrides
- 36 unit tests covering all of the above
### Layer 2: Environment-specific revivers
- **`@workflow/web-shared`** (`lib/hydration.ts`) — browser-safe revivers using `atob()` for base64, real `URLSearchParams`/`Headers`/`URL` instances, `ClassInstanceRef` for UI rendering
- **`@workflow/cli`** (`lib/inspect/hydration.ts`) — Node.js revivers using `Buffer.from()` for base64, `CLIClassInstanceRef` with `util.inspect.custom` for CLI output
Each module exports a pre-bound `hydrateResourceIO(resource)` that uses its environment's revivers.
### Removed: `@workflow/core/observability`
- Deleted `observability.ts` and `observability.test.ts` entirely (no remaining consumers)
- Removed `"./observability"` export from `@workflow/core/package.json`
- Removed the `workflow` package's `internal/observability.ts` re-export
- All functionality has been split between `serialization-format.ts` (shared types/utilities) and the environment-specific hydration modules
## Web package changes
- Server passes raw world data through without hydration (CBOR preserves `Uint8Array`)
- Client calls `hydrateResourceIO` from `@workflow/web-shared` after receiving CBOR-decoded data
- No Vite `node:*` stubs needed since `@workflow/core/serialization-format` is browser-safe
- Optimized: event hydration finds the matching event before hydrating (instead of hydrating all)
- Reduced server log noise from handled API errors (4xx errors no longer logged)
## Packages affected
- `@workflow/core` — new `serialization-format` export (with tests), removed `observability` export
- `@workflow/web-shared` — new `lib/hydration.ts` with browser-safe revivers
- `@workflow/cli` — new `lib/inspect/hydration.ts` with Node.js revivers, updated `output.ts` import
- `@workflow/web` — client-side hydration, removed server-side hydration
- `workflow` — removed `internal/observability.ts` re-export
* Refactor web-shared to separate UI components from server concerns
- Move UI components into src/components/ subdirectory
- Move server actions and API client to @workflow/web
- Add world-actions/ module with shared helpers (recreateRun, cancelRun, etc.)
- Add runtime subpath exports to @workflow/core
- Components now accept callbacks as props for data fetching
* add support for streams
* update lock file
* Migrate helpers from web-shared to core
* Migrate helpers from web-shared to core
* Migrate helpers from web-shared to core
* Migrate helpers from web-shared to core
* Fix claude's comments
* Fix server actions bug for web
* fix copilot bugs
* Add SDK version to workflow run executionContext for observability
Wire `@workflow/core` package version through the `executionContext` field
when creating workflow runs. Display the version in the observability UI
attribute panel. Uses genversion for build-time version injection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix workflowCoreVersion extraction and add turbo.json for caching
- Extract workflowCoreVersion from executionContext in hydrateResourceIO
before stripping the context (addresses Copilot review comment)
- Add turbo.json to include src/version.ts in build outputs for caching
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update workflowCoreVersion display name to @workflow/core version
Use a display name mapping to render the attribute as "@workflow/core version"
in the observability UI for better readability.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Added `WORKFLOW_SERVER_URL_OVERRIDE` configuration to the Vercel world adapter and removed the deprecated `WORKFLOW_VERCEL_SKIP_PROXY` and `WORKFLOW_VERCEL_BACKEND_URL` environment variables.
### What changed?
- Added a changeset for a patch release across multiple packages
- Removed `WORKFLOW_VERCEL_SKIP_PROXY` environment variable from GitHub workflow tests
- Removed `WORKFLOW_VERCEL_BACKEND_URL` from environment variables in CLI and core packages
- Simplified the URL resolution logic in the Vercel world adapter
- Added support for a `WORKFLOW_SERVER_URL_OVERRIDE` constant for testing against different workflow-server versions
- Added the `x-vercel-workflow-api-url` header when the URL override is set
### How to test?
1. Verify that Vercel deployments continue to work without the removed environment variables
2. Test with a custom workflow server URL by setting the `WORKFLOW_SERVER_URL_OVERRIDE` constant in the world-vercel package
### Why make this change?
This change simplifies the configuration for the Vercel world adapter by removing deprecated environment variables and standardizing on a cleaner approach for specifying the workflow API URL. The new implementation automatically determines whether to use the proxy based on project configuration, making it more intuitive and reducing the need for explicit configuration.