mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
workflow-auth-docs
23 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1280163551 |
Use the storage APIs for run detail views (#3944)
* Fix empty trace and events tabs on older runs A run's events were read from a different source than the rest of the run detail view, one with a shorter retention window and a small ingestion delay. Past that window the trace and events tabs came up empty even though the run's data was still retained, and the events tab's own id search would find events the list above it was not showing. Inside the window, a run still executing could show gaps. All the run-scoped reads now come from the same source as the rest of the view. The runs list and hooks list are unchanged: they span runs and are fine where they are. The events tab's id search now stops after fewer pages before reporting a truncated result. Removes the fetchSteps server action and its /api/rpc method. The trace viewer has built its spans from events since the observability data-fetching refactor, which left fetchSteps the only /api/rpc method with no caller. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Describe the read paths without backend internals The comments explaining why these reads moved named the plan field that gates the analytics window, its per-plan day counts, storage TTLs, index choices and page-scan mechanics. This repo is the client SDK, so those belong on the service side, not here — the reason a caller needs is that the analytics namespace is a metadata mirror with a shorter retention window and asynchronous ingestion. Also drops the claim that this listing feeds the graph tab, which is disabled in run-detail-view.tsx. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fbfb9fe869 |
Validate world.analytics arguments up front (#3943)
* Validate world.analytics arguments up front
Every analytics method now checks its arguments before making a request
and throws a RangeError naming the limit it broke: the ids, the
pagination limit against the cap for that listing, and the attribute
filter's pair count, key length and value size. Because analytics is an
optional capability, callers wrap it in a catch, which turned an invalid
argument into what looked like an empty result rather than an error.
Two arguments that used to be dropped silently now fail too. A limit of 0
fell back to the default page size, and a startTime without a matching
endTime turned a listing you meant to bound into an unbounded one that
looked like a normal answer.
Exports ANALYTICS_RUN_SCOPED_PAGE_LIMIT, ANALYTICS_PAGE_LIMIT and
ANALYTICS_MAX_ATTRIBUTE_FILTERS so callers can check the bounds
themselves.
Deprecates analytics.events.listByCorrelationId() in favour of
analytics.events.list({ runId, correlationId }), which issues the same
request and also accepts an eventType filter. It keeps its own
implementation rather than delegating: list() treats correlationId as
optional and skips an empty one, so a delegation would turn an empty id
into an unfiltered listing of the run.
Documents every analytics method in the reference. events.getMany() was
missing entirely, seven methods shared one code block with no parameters
or return shapes, and none of the limits were written down.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Parse attribute key timestamps as UTC
firstSeenAt and lastSeenAt were the only analytics timestamps still on a
plain date coercion. The values arrive without a timezone designator, so
that read them in the process's local zone and every other field in the
namespace read them as UTC — a seven-hour skew on those two fields alone
for anyone running outside UTC.
The added test fails without the fix under TZ=America/Los_Angeles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: drop the deprecated correlation-id listing from the reference
A reference page should describe the API you should reach for. The
deprecation notice lives on the method itself, so editors surface it
where it matters without the page advertising a method nobody should
start using. Also drops it from the page-limit table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Close two gaps in the analytics argument guards
Run ids were validated with workflowRunIdSchema while every other id used
a regex mirroring the backend. Those disagree: z.ulid() accepts a
lowercase body and a first character above 7, and the backend accepts
neither, so the most-used parameter had the leakiest guard and still
produced the 400 this is meant to prevent. Run ids now use the same
pattern as the rest.
A supplied-but-empty filter value was also still being dropped —
correlationId, the optional runId scope on hooks.get, and workflowName
all tested truthiness. Dropping one widens the result set rather than
narrowing it, so an empty correlationId listed the whole run and an
empty workflowName listed every workflow. That is the same failure the
limit and time-window guards were added to prevent, and the comment on
listByCorrelationId already described the hazard. They now compare
against undefined, so an empty id throws and an empty name is forwarded
for the backend to match.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Raise argument rejections as a typed, non-retryable error
The guards threw bare RangeErrors, which left a caller — or an agent
driving this API — parsing English to decide whether to fix the call or
retry it. They now raise WorkflowWorldError with
code: 'INVALID_ARGUMENT', the code the rest of this client already uses
for its transport and throttle failures, so the retry decision is a
field lookup. normalizeEventIds moves with them rather than staying the
one guard that throws a different type.
Also sharpens the four messages that made a caller do the work:
a half-open window now names the bound that is missing rather than
restating the rule, an inverted window prints both ends, and the
attribute-value and event-id batch errors report the size measured
rather than only the bound they broke.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Name the method and the field on an argument rejection
Two things a caller could not get without reading prose. The same guard
runs behind several methods, so `runId must be a workflow run id` was
identical whether it came from events.list or steps.get — fine with a
stack, lossy once the error has crossed a log line. And the offending
argument was only available as the first token of the message, which is
the part most likely to be reworded.
Messages now open with the method, and WorkflowWorldError carries an
optional `field`:
analytics.runs.list: pagination.limit must be an integer between 1
and 100 (received 9999)
→ code: 'INVALID_ARGUMENT', field: 'pagination.limit'
`field` is additive on the error class and set only by these guards, so
nothing that reads the existing properties changes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
371f06e5ac |
feat(web): bulk-cancel selected runs from the runs table (#3349)
* feat(cli): bulk-cancel runs in a single operation Replace the per-run cancel loop in `workflow cancel` with one `cancelRuns` call, validate `--limit` (1-500), print a compact outcome summary with per-run lines for surfaced failures, and exit nonzero only when a run fails. The bulk logic lives in a dependency-injected `performBulkCancel` helper so it is unit-testable without an oclif harness. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): address bulk cancel review feedback * feat(web): bulk-cancel selected runs in a single request Thread a bulkCancelRuns action through the server action, RPC route, rpc-client, and client wrappers, backed by core's cancelRuns. The runs table now cancels the selected pending/running runs in one call, caps a batch at BULK_CANCEL_MAX_RUN_IDS (disabling the button with guidance above the cap), and reports a single outcome-summary toast covering only the categories that occurred. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
de1905f15c | feat(world): require a runId on listByCorrelationId (#3280) | ||
|
|
62d570ed4b | Remove retired v1 step route plumbing (#3061) | ||
|
|
fe327e69e2 |
[world][web][cli] o11y: window-aware runs listing (#2812)
* web: infinite scroll for the runs table Replace Previous/Next cursor paging with front-style infinite scroll: a useInfiniteList hook accumulates cursor pages with per-run dedup and generation-guarded resets, and useLoadMoreOnScroll drives loadMore from an IntersectionObserver sentinel (400px prefetch margin, guarded against double-fetch, observed against the table's scroll container). Footer now shows the loaded count and the analytics lookback window. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * web: back the runs infinite list with SWR so tab switches serve from cache Rewrite useInfiniteList on useSWRInfinite: pages are keyed by [cacheKey, cursor] in SWR's global cache, so unmount/remount (switching tabs) restores fetched pages instantly instead of refetching. Revalidation is conservative because analytics list queries are expensive: revalidateFirstPage and revalidateIfStale are off; freshness comes from the Refresh button and the visibility-change auto-reload, which map to reload() (reset to first page + revalidate). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * world: expose startTime/endTime on analytics runs listing The workflow-server /v2/analytics/runs endpoint has accepted a bounded startTime/endTime window since it shipped, and is significantly faster with one (the window prunes the ClickHouse scan: ~2s for 12h vs ~8s for the default 30-day entitlement window). The world client never exposed the params, so the CLI and web UI could only issue windowless requests. Pass them through so clients can send bounded windows (e.g. a period picker like front's workflows o11y). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * web: front-style period picker for the runs list Add a time-window picker (1h/6h/24h/3d/7d/30d, default 24h, URL-backed via ?period=) that sends an explicit startTime/endTime window through fetchRuns -> world.analytics.runs.list, keeping the ClickHouse scan bounded. The window is frozen per selection/refresh so all cursor pages share the same bounds, and it participates in the SWR cache key. Plan tiers are honored data-driven from the server's pageInfo: presets longer than the plan's observability lookback are disabled in the picker (labeled Observability Plus when an upgrade is available), and a 402 observability-upgrade-required response renders through the existing upgrade-required error handling. The footer now labels the selected window instead of the plan lookback. The runtime (local) fallback path ignores the window since the storage API has no time filter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * web: allow status filtering without a workflow name filter The status dropdown was disabled on Vercel backends until a workflow was selected — a limitation of the runtime DynamoDB API's index design. The runs list now reads via world.analytics, whose ClickHouse query filters derived status independently of workflowName, so drop the guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * cli: time-window flags for runs listing; widen name lookups past the default window The analytics backend now defaults windowless runs listings to the trailing 24h. Replicate the web's window support in the CLI: - 'workflow inspect runs' gains --since/--until (relative durations like 30m/12h/7d/2w, or timestamps) which are sent as an explicit startTime/endTime window. Out-of-plan windows surface through the existing observability-upgrade-required handling; non-analytics backends warn that the flags are ignored. - 'workflow start <name>' resolves the workflow's latest run via a windowless (default-window) listing and now retries across the plan's whole observability window on a miss, so names idle for more than a day keep resolving. - Bulk 'workflow cancel' matches across the plan window up front — a run can sleep or wait on a hook for days without recent events, so the default recent window must not bound cancellation matching. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: tighten changeset descriptions Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * web: persist frozen listing windows across remounts; minimize lockfile diff Address review findings: - The frozen startTime/endTime lived in component state, but RunsTable fully remounts on tab switches, so every remount minted a new SWR cache key — the cached-pages restore never hit and cache entries grew unboundedly (one per key, including every 5s local-backend poll tick). Move the frozen windows to a module-scope store keyed by period: a remount reuses the stored window (same cache key, instant restore), and the window only advances on explicit refresh/reload. Non-analytics backends now send no window at all (the runtime APIs ignore it anyway), which also hides the period picker and window label there. - Regenerate pnpm-lock.yaml from main so the diff contains only the swr addition (plus its own use-sync-external-store dependency), dropping the unrelated docs-importer radix-ui re-resolutions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
49a50e83d9 | Document configuration environment variables (v5) (#2468) | ||
|
|
cdfac39e07 |
web: construct worlds explicitly instead of via static-injection stub (#2804)
* web: construct worlds explicitly instead of via static-injection stub Since #2752, createWorld() from @workflow/core/runtime is a stub that throws unless a framework build plugin aliases it to a world package. The CLI was migrated to construct worlds directly, but @workflow/web still called the stub, so 'workflow web' crashed with 'Workflow target world was not statically injected' for local and postgres backends (the vercel path was unaffected since it constructs the world directly). Mirror the CLI: import the local world statically and resolve any other configured world package from the inspected project's directory. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * web: fall back to default.createWorld for CJS world packages Review feedback: import() of a require-resolved CJS entry relies on cjs-module-lexer to surface named exports; fall back to mod.default.createWorld when detection fails. Mirrored in the CLI in #2806. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ae51f45166 |
web: list hooks from analytics, fetch token on demand (#2652)
* Add workflow analytics world APIs * web: read observability list views from world.analytics when available Route the runs/steps/events/hooks list server actions through the optional world.analytics namespace when the backend provides one, falling back to the runtime storage APIs otherwise. Events listing only uses the analytics path when no payload data is requested. Detail/get actions, streams, and mutations are unchanged. * web: keep events and hooks list reads on the runtime storage API The Events tab and trace viewer derive step names and wait resumeAt from resolved event payloads, and the hooks table needs the secret token and ownerId for its resume/copy-token actions. The metadata-only analytics rows do not carry these, so only the runs and steps list views use world.analytics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * web: read events list from world.analytics with a runtime-shape remap The events list/trace consumers read only top-level eventType, correlationId, and createdAt; event payloads are loaded lazily per event via fetchEvent(..., 'all') on the runtime path. Map the flat analytics event rows into the runtime Event shape (reconstructing eventData.stepName) so fetchEvents and fetchEventsByCorrelationId can use the analytics read path when available. Hooks remain on the runtime path (they need the secret token + ownerId). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * web: list hooks from world.analytics, fetch token on demand The hooks list now reads from the metadata-only world.analytics namespace when the backend provides one (falling back to the runtime storage APIs otherwise). A hook's secret token is no longer shipped in list rows — it is fetched one hook at a time via world.hooks.get only when the user copies the token or resumes the hook, keeping the secret out of bulk list responses. Adds a fetchHookToken server action + RPC, a HookListItem type (Hook without token), and a lazy HookTokenCell for the copy-token affordance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Handle analytics access metadata in web --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
1518c48608 |
web: read observability list views from world.analytics when available (#2647)
* Add workflow analytics world APIs * web: read observability list views from world.analytics when available Route the runs/steps/events/hooks list server actions through the optional world.analytics namespace when the backend provides one, falling back to the runtime storage APIs otherwise. Events listing only uses the analytics path when no payload data is requested. Detail/get actions, streams, and mutations are unchanged. * web: keep events and hooks list reads on the runtime storage API The Events tab and trace viewer derive step names and wait resumeAt from resolved event payloads, and the hooks table needs the secret token and ownerId for its resume/copy-token actions. The metadata-only analytics rows do not carry these, so only the runs and steps list views use world.analytics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * web: read events list from world.analytics with a runtime-shape remap The events list/trace consumers read only top-level eventType, correlationId, and createdAt; event payloads are loaded lazily per event via fetchEvent(..., 'all') on the runtime path. Map the flat analytics event rows into the runtime Event shape (reconstructing eventData.stepName) so fetchEvents and fetchEventsByCorrelationId can use the analytics read path when available. Hooks remain on the runtime path (they need the secret token + ownerId). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Handle analytics access metadata in web --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
ad5c068d7f |
[web-shared][web] Fix events tab search (#2107)
* 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> |
||
|
|
873b4e2bb4 | [core] Refactor getWorld interface to be asynchronous (#942) | ||
|
|
66d49c0db6 | [world] Restructure stream interface, require run ID for all step and stream operations (#1293) | ||
|
|
c488877727 |
[web] Switch web package stream reader to getStreamChunks (#1542)
* fix: switch web stream reader from readFromStream to getStreamChunksix * add decrypt button for stream * add decrypt button for stream * add polling for streams tab * add polling for streams tab * add jsdoc * address review comments |
||
|
|
aee035f944 |
refactor: replace HTTP status code checks with semantic error types (#1342)
* feat: classify run failure error codes and improve error logging - Add RUN_ERROR_CODES (USER_ERROR, RUNTIME_ERROR) to @workflow/errors - Populate errorCode in run_failed events via classifyRunError() - Update web UI StatusBadge to show amber dot for infrastructure errors - Improve world-local queue error logging (concise, no body dump) - Improve schema validation error messages (concise, verbose behind DEBUG) - Add e2e tests for error code flow and infrastructure error retry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add semantic error types to replace HTTP status code checks in runtime Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: classify run failure error codes and improve error logging - Add RUN_ERROR_CODES (USER_ERROR, RUNTIME_ERROR) to @workflow/errors - Populate errorCode in run_failed events via classifyRunError() - Update web UI StatusBadge to show amber dot for infrastructure errors - Improve world-local queue error logging (concise, no body dump) - Improve schema validation error messages (concise, verbose behind DEBUG) - Add e2e tests for error code flow and infrastructure error retry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: classify run failure error codes and improve error logging - Add RUN_ERROR_CODES (USER_ERROR, RUNTIME_ERROR) to @workflow/errors - Populate errorCode in run_failed events via classifyRunError() - Update web UI StatusBadge to show amber dot for infrastructure errors - Improve world-local queue error logging (concise, no body dump) - Improve schema validation error messages (concise, verbose behind DEBUG) - Add e2e tests for error code flow and infrastructure error retry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * address PR review comments - Remove dead `meta` option from TooEarlyError constructor (TooTallNate) - Extract `throwWithTrace` helper to deduplicate span recording in world-vercel makeRequest (TooTallNate) - Restore `maxAttempts` const for stable retry count logging (TooTallNate) - Fix behavioral regression: add WorkflowAPIError 404 fallback in suspension-handler hook disposal to handle world-vercel path where makeRequest doesn't map 404 to HookNotFoundError (TooTallNate) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: translate 404 to HookNotFoundError at the world-vercel boundary Move the 404 → HookNotFoundError translation into world-vercel's createWorkflowRunEvent, where we know the event type context. For hook-related events (hook_created, hook_disposed, hook_received, hook_conflict), a 404 from the server means the hook was not found. This removes the WorkflowAPIError 404 fallback from the runtime's suspension-handler, keeping the runtime fully decoupled from HTTP status codes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: parse Retry-After for 425 responses and narrow hook event set - Parse Retry-After header unconditionally so TooEarlyError gets the server-provided delay instead of always falling back to ~1s - Narrow hookEventsRequiringExistence to only hook_disposed and hook_received (matching world-local's set), since hook_created and hook_conflict don't imply the hook must already exist Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * rename WorkflowAPIError to WorkflowWorldError Breaking change: rename WorkflowAPIError → WorkflowWorldError to better reflect that this error represents world (storage backend) failures, not HTTP API errors specifically. Updated across all packages: errors, core, world-local, world-vercel, world-postgres, workflow, and web. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
887cc2bd55 | [web-shared] [cli] Refactor observability data fetching (#1261) | ||
|
|
5e4ef657cf | [web] Refactor event list view data fetching (#1288) | ||
|
|
97932d3086 |
fix: thread runId through stream inspection for encryption key resolution (#1277)
CLI showStream: - Requires --run with --decrypt for encrypted stream decryption - Warns when --decrypt is used without --run Web stream reading: - readStreamServerAction accepts runId parameter for key resolution - Stream API route reads runId from query param - readStream client function passes runId to the API route - useStreamReader hook accepts and passes runId - run-detail-view passes runId to useStreamReader Removes getRunIdFromStreamId helper (stream IDs don't always share the run's ULID, e.g. streams serialized across step/workflow boundaries). |
||
|
|
bbe40ff00a |
Opt-in decryption for o11y tooling (CLI + web) (#1256)
* 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 |
||
|
|
ea3254e7ce |
Fix projectConfig.projectId containing project name instead of ID (#999)
* Fix projectConfig.projectId containing project name instead of ID * add changeset * Address review: fix changeset description, normalize slug-based project IDs, add clarifying comments |
||
|
|
1c1157340d |
[builders][web-shared] Improvements to o11y and fixes to graph generation code path (#1031)
* [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> |
||
|
|
c56dc3848e |
Extract browser-safe serialization format and move hydration client-side (#1015)
## 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 |
||
|
|
7653e6bfdb |
Migrate @workflow/web from Next.js to React Router v7 (#1005)
## Summary
- Replace Next.js App Router with React Router v7.13.0 framework mode (Vite-based), eliminating the large `next` dependency from the web, CLI, and workflow metapackages
- Serve the web UI in-process from the CLI via Express instead of spawning `next start` as a child process
- Switch RPC transport from JSON to CBOR to preserve binary data types across the wire
- Replace `nuqs` URL state management with React Router's `useSearchParams`
- Replace Next.js server actions with an RPC resource route (`/api/rpc`) and a thin CBOR-based client
## Motivation
The `next` package is ~300MB installed and was the single largest dependency in the monorepo. It also required spawning a separate child process from the CLI to run the o11y web server, adding complexity around process lifecycle management, port readiness polling, and environment variable forwarding.
With React Router framework mode, the web package builds to a standard Express-compatible server bundle that the CLI can import and serve directly in its own process.
## What changed
**Framework swap (`@workflow/web`):**
- `next.config.ts` / `postcss.config.mjs` → `react-router.config.ts` / `vite.config.ts`
- `src/` directory → `app/` directory (React Router convention)
- `src/app/layout.tsx` + `layout-client.tsx` → `app/root.tsx`
- `src/app/page.tsx` → `app/routes/home.tsx`
- `src/app/run/[runId]/page.tsx` → `app/routes/run-detail.tsx`
- Path alias `@/` → `~/`
- Removed all `'use client'` / `'use server'` directives
**Data transport:**
- Server actions → RPC resource route at `/api/rpc` with CBOR encoding
- CBOR preserves `Uint8Array` and other binary types natively (no base64 overhead)
- Stream reading → dedicated `/api/stream/:streamId` resource route
**URL state:**
- `nuqs` (`useQueryState`) → `useSearchParams` from `react-router`
**Fonts:**
- `next/font/google` → Geist `.woff2` files referenced directly from `node_modules/geist` via `@font-face` in CSS
**CLI integration (`@workflow/cli`):**
- `import('@workflow/web/server').then(m => m.startServer(port))`
- No child process, no readiness polling, no cleanup handlers
**Radix UI compatibility:**
- `onSubmit` preventDefault on `AlertDialogContent` and `SheetContent` to prevent Radix's internal `<form method="dialog">` from triggering React Router route actions
- Catch-all action on root route for any stray POSTs
## Dependencies removed
- `next`, `swr`, `nuqs`, `@tailwindcss/postcss`
## Dependencies added
- `react-router` / `@react-router/dev` / `@react-router/node` / `@react-router/express` (all `7.13.0`)
- `express`, `vite`, `@tailwindcss/vite`, `cbor-x`, `isbot`, `cross-env`
- `geist` (devDep)
|