Commit Graph

1132 Commits

Author SHA1 Message Date
github-actions[bot] 015452a70c perf(core): drain consumable replay events synchronously (#2473) (#2504)
* docs(core): document scheduleWhenIdle macrotask is load-bearing

Revert the synchronous consume-loop drain optimization: it caused a
replay divergence (ReplayDivergenceError on step_started →
CorruptedEventLogError) in the world-testing inline-batches parallel
workflow on the Windows CI runner. The per-event `process.nextTick` in
the consume loop is load-bearing — it guarantees at most one event is
consumed per macrotask, letting the cross-VM `resolve → workflow VM body
→ subscribe()` chain register the next operation's consumer before the
drain advances. A synchronous drain races ahead of that registration.

What remains is a documentation comment on `scheduleWhenIdle` capturing
why its initial `setTimeout(0)` must not be downgraded to a microtask
(empirically: queueMicrotask breaks hook/sleep Promise.race ordering →
CorruptedEventLogError). No behavior change.



* docs(core): use empty changeset for comment-only macrotask doc

The scheduleWhenIdle change is a pure code comment with no consumer-facing
effect, so it does not warrant a patch bump / changelog entry. Replace the
patch changeset with an empty one to satisfy the changeset-bot convention
without claiming a release. Per the PR template's `pnpm changeset --empty`
guidance for non-releasing changes.




* perf(core): drain consumable replay events synchronously

The EventsConsumer rescheduled `process.nextTick(this.consume)` after every
consumed event, so replaying N already-consumable events (structural
lifecycle events, step_created/step_started, completed deliveries) cost N
macrotask hops — O(N) per consume wave across a sequential replay.

Drain consecutively consumable events within a single synchronous pass
instead. This is safe because callbacks only ever consume events with a
consumer that is already registered; new consumers are registered by
workflow VM body code that runs asynchronously off ctx.promiseQueue after a
delivery resolve(). When the next event's consumer is not yet registered,
no callback consumes it and we fall through to the existing cross-VM-safe
deferred unconsumed-event check, exactly as before. A null end-of-events
sentinel never continues the drain, so it cannot spin past end-of-log.

scheduleWhenIdle is intentionally left unchanged: its initial setTimeout(0)
is load-bearing for cross-VM propagation (pendingDeliveries is already 0
between a delivery resolve() and the VM body registering its next
subscriber). Replacing it with queueMicrotask empirically breaks hook/sleep
Promise.race ordering (CorruptedEventLogError); a comment now records this.

Re-validated after a premature revert: the windows-unit flake that prompted
the revert reproduces on unmodified main at the same rate (local 8-way
harness: opt 4/80 vs main 7/80; main historical windows-unit ~13%), so it is
a pre-existing flake, not a regression from this change.



---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 09:18:50 -07:00
github-actions[bot] 170ea9659a perf(core): cache compiled workflow-bundle vm.Script across replays (#2471) (#2506)
* perf(core): cache compiled workflow-bundle vm.Script across replays

The inline replay loop calls runWorkflow on every iteration, and each call
re-parsed the entire workflow bundle string via vm.runInContext. For a bundle
containing many workflow definitions (the production shape: one workflow called
per replay), this re-scans every definition on every replay.

Cache the compiled vm.Script per process, keyed by (workflowCode, filename),
and run it against the fresh context instead of recompiling. Compilation is a
pure function of (code, filename), so the result is byte-identical to the
previous re-parse-every-time behaviour — determinism is preserved. filename is
part of the key because it drives source attribution in stack traces (consumed
by remapErrorStack).

Measured per-replay savings scale with bundle size (and multiply by replay
count): ~34% for a 50-workflow app, ~59% for 155 workflows, ~80% for 400.



* perf(core): bound script cache with LRU; soften determinism claim; add tests

Addresses review on #2471:

- Bound `scriptCache` to a small LRU (cap 8 bundle versions). Production
  serves one bundle per process so the bound is never reached; it exists for
  dev/watch mode, where each edit produces a new bundle string that would
  otherwise be pinned forever (~0.8MB/edit, monotonic). Touch-on-access keeps
  the latest bundle hot; evicting a `code` entry drops its per-filename scripts
  together, restoring pre-cache GC behaviour.
- Document precisely why keying includes `filename` (intentional: drives
  stack-trace attribution via `remapErrorStack`; NOT a dedupe key), and that
  the whole bundle is compiled once per distinct filename.
- Soften the "byte-identical including thrown errors" claim to
  same-workflow-function + same-`filename`-attribution, noting the one caveat:
  a lookup-expression error's line number shifts to line 1 of the separate
  lookup Script. Updated in both the code comment and the PR description.
- Add tests: cache-is-bounded regression (eviction past the cap), LRU recency
  (hot bundle survives churn), and a realistic multi-workflow collision test
  (distinct code/filename never returns the wrong Script, results carry their
  own bundle marker).



---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 09:18:06 -07:00
github-actions[bot] ab24408244 Bump undici to 7.28.0 (#2534) (#2535)
Signed-off-by: Nathan Colosimo <110621881+NathanColosimo@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-19 09:13:41 -07:00
github-actions[bot] db8a2c4982 [core] Send workflowName with step events (#2511) (#2518) 2026-06-18 13:08:09 -07:00
github-actions[bot] b06fa657f5 Stamp run IDs on world spans (#2508) (#2513)
Co-authored-by: Karthik Kalyan <105607645+karthikscale3@users.noreply.github.com>
2026-06-18 10:42:58 -07:00
github-actions[bot] 578f7e1f1b Backport #2455: Fix Next workflow module specifier root (#2488) 2026-06-18 08:49:10 -07:00
github-actions[bot] 0dd2db8810 fix(web-shared): use solid gray for queued trace segment (#2474) (#2481)
Co-authored-by: Mitul Shah <mitulxshah@gmail.com>
2026-06-17 14:47:34 -07:00
github-actions[bot] 37ed6a583f test: support Vercel protection bypass secret in e2e headers (#2458) (#2479)
Co-authored-by: Nathan Rajlich <n@n8.io>
2026-06-17 14:45:12 -07:00
github-actions[bot] b3571378b8 fix(swc-plugin): count destructuring-default references in DCE usage analysis (#2398) (#2465)
The DCE usage collector skipped the entire variable name pattern when
visiting a `VarDeclarator` (to avoid marking the binding name as "used").
But default-value initializers inside destructuring patterns live in that
pattern — e.g. the `TTL` in `const { ttl = TTL } = options;` — so those
references were invisible to the collector. A module-scope `const`
referenced only through such a default was treated as unused and stripped,
while the surviving code kept reading it, producing a runtime
`ReferenceError` when the default fired.

Traverse the default-value initializer expressions (and computed keys)
within destructuring patterns while still not marking the binding names
themselves, so the referenced declaration is preserved. Function-parameter
defaults were already covered (params are visited in full).

Fixes #2396.

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 18:54:44 -07:00
github-actions[bot] e3771083e0 fix(deps): upgrade hono to 4.12.25 to resolve CVE-2026-54290 (#2462) (#2466)
hono <4.12.25 is vulnerable to CVE-2026-54290 (GHSA-88fw-hqm2-52qc):
the CORS middleware reflects any request Origin with
Access-Control-Allow-Credentials: true when credentials are enabled and
origin is left at the default wildcard, exposing cookie-authenticated
endpoints to arbitrary origins.

- packages/world-testing: hono 4.12.21 -> 4.12.25 (the flagged manifest)
- workbench/hono: ^4.12.8 -> ^4.12.25, clearing the also-vulnerable
  4.12.9 from the lockfile

Neither app uses hono's CORS middleware, so neither was exploitable, but
the bump clears the vulnerable code from the dependency tree. Only the
core Hono class is imported in world-testing; build and typecheck pass.

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 18:54:25 -07:00
github-actions[bot] 20a6d73a0a fix(core): make deploymentId 'latest' a no-op in non-Vercel worlds (#2397) (#2464)
* fix(core): make deploymentId 'latest' a no-op in non-Vercel worlds

Previously, start({ deploymentId: 'latest' }) threw a WorkflowRuntimeError
in any World that doesn't implement resolveLatestDeploymentId() (local dev,
Postgres). That meant a workflow which opts into 'latest' on Vercel would
fail outright in local development.

Resolving 'latest' only means something in worlds with atomic, immutable
deployments. In other worlds there is nothing to resolve between, so instead
of throwing we now log a warning and fall back to the current deployment,
making 'latest' an effective no-op there.

- start.ts: warn + fall back to currentDeploymentId instead of throwing
- start.test.ts: replace the "should throw" test with a warn + fallback test
- e2e.test.ts: assert 'latest' completes (no-op) on non-Vercel worlds
- docs: note the no-op behavior in v4 + v5 start.mdx



* fix(core): warn once for deploymentId 'latest' no-op; harden test cleanup

Address PR review:
- Gate the 'latest'-has-no-effect warning behind a once-per-process guard
  (mirrors the warnOnce pattern in constants.ts) so a workflow that hardcodes
  'latest' for Vercel doesn't flood local/Postgres dev logs on every run.
  Exposes _resetLatestNoOpWarnForTests() (@internal) for unit tests.
- start.test.ts: reset the guard in beforeEach and restore spies in afterEach
  via vi.restoreAllMocks() so a throwing assertion can't leak the
  runtimeLogger.warn spy into later tests; drop the manual mockRestore().
- Add a test asserting the warning fires exactly once across repeated
  'latest' starts while every run still falls back to the current deployment.



---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 18:54:07 -07:00
github-actions[bot] 3bb5de1669 feat(cli): print run deep links with --url, fix dashboard route (#2467) (#2469)
Add a `--url` flag to `inspect`/`web` that prints a run's observability
dashboard deep link to stdout and exits — no browser, no local server —
so scripts and agents can share a link instead of opening a UI.

Fix the Vercel dashboard URL to the current
`…/workflows/runs/<id>?environment=<env>` route (drop the legacy
`/observability` segment) and respect `--env`. Apply the same route fix
to the e2e helpers, CI aggregation scripts, and the nextjs-turbopack
workbench. Document deep-linking in the workflow skill and observability
docs.

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 18:52:47 -07:00
github-actions[bot] c4ed84f163 fix(deps): upgrade astro to 6.4.6 to resolve CVE-2026-54299 (#2457) (#2461)
Astro <6.4.6 is vulnerable to CVE-2026-54299 (GHSA-2pvr-wf23-7pc7, host
header SSRF in prerendered error page fetch). The fix only exists in the
6.x line — there is no 5.x backport — so this bumps:

- workbench/astro: astro ^6.4.6, @astrojs/node 10.1.4, @astrojs/vercel ^10.0.8
- packages/astro: astro devDependency 6.4.6 (typecheck only, not shipped)

Removes both vulnerable astro@5.16.3 and astro@5.18.0 from the lockfile.
Verified the example app builds under both the node and vercel adapters.

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 22:39:04 +00:00
github-actions[bot] 161431b1d0 Backport #2328: feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2392)
* feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2328)

* feat(web-shared): RelativeTimeCard with shared ContextCard provider

Add a ContextCard provider/trigger and rebuild the timestamp tooltip as a
RelativeTimeCard, giving animated, collision-aware morphing hover cards.
Mount the shared provider in EventListView and AttributePanel.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Match vercel/front timestamp format for run/activity fields

Render absolute Created/Started/Completed (and sibling) timestamps using
date-fns in vercel/front's request/activity format (e.g.
"JUN 10 10:16:02.69 GMT-4") via the shared formatLocalMillisecondTime helper.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web-shared): register dark-theme/light-theme Tailwind variants

The context-card arrow tip stroke uses `dark-theme:[--context-card-tip-stroke:#252525]`,
but Tailwind v4 has no built-in `dark-theme` variant, so the utility was silently
dropped and the stroke fell back to its light `#DBDBDB` value — rendering as a white
caret in dark mode. Register the `dark-theme`/`light-theme` custom variants in
styles.css (mirroring vercel/front's geistcn tailwind.css, extended to match the
`.dark`/`[data-theme="dark"]` selectors this package and next-themes use).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web-shared): match context card shadow to vercel/front

The --ds-shadow-tooltip token was guessed when added standalone, producing
an oversized/heavy drop shadow. Reproduce front's exact resolved value for
both light and dark themes (including the background-border layer).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web-shared): bridge context card hover gap to stop flicker

The card is positioned `sideOffset` away from the trigger, leaving a
transparent un-hoverable gap that caused the hover card to flicker
(open → close → open) when moving the cursor onto it. Add a transparent
hover bridge inside the floating wrapper that extends the hover surface
by `sideOffset` to meet the trigger edge, keeping the visual spacing
while making the hover surface continuous.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Revert "fix(web-shared): bridge context card hover gap to stop flicker"

This reverts commit 2b961b1610.

* docs: simplify changeset description

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: remove vercel/front references from comments

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web-shared): only clear active context card when the active trigger unmounts

The unmount cleanup had an inverted guard: an unmounting inactive trigger
would clear the shared active card, hiding another trigger's card (and an
unmounting active trigger left a stale card). Guard on === id instead.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: remove theme-variant comment

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Mitul Shah <mitulxshah@gmail.com>

* fix(web): correct Geist tokens and context card styling in observability UI (#2435)

* fix(web): register dark-theme/light-theme custom variants in globals.css

* fix(web): register missing Geist color tokens (background-100, gray-1000, gray-alpha-400)

* chore(web): drop explanatory comments in globals.css

* fix(web): map full Geist gray scale to theme-aware Tailwind tokens

* fix(web): correct --ds-* neutral palette to canonical Geist values

* fix(web): add missing --ds-shadow-tooltip token for context card border/shadow

* fix(web-shared): match context card arrow stroke to border via --ds-gray-alpha-400

* docs(changeset): shorten web changeset

* docs(changeset): consolidate into a single changeset

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* Apply suggestions from code review

Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>

* Apply suggestion from @VaguelySerious

Signed-off-by: Peter Wielander <mittgfu@gmail.com>

---------

Signed-off-by: Mitul Shah <mitulxshah@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Mitul Shah <mitulxshah@gmail.com>
2026-06-16 16:55:34 -04:00
Peter Wielander 32e83e8091 Prevent peer dependency-only major bumps (backport #2437) (#2447)
Backport of #2437 to the stable (v4) release line.

- Add publish-time release-major guard (.github/scripts/check-release-major.js)
  run in release.yml before install, gated on RELEASE_VERSION: 4.
- Realign @workflow/nest from 0.0.10 to 4.0.11 so the v4 stable line has no
  out-of-line package majors (nest skipped straight to 5.0.0-beta on main and
  had no 4.x version). Required for the guard to pass on stable.

The .changeset/config.json onlyUpdatePeerDependentsWhenOutOfRange option from
#2437 was already present on stable.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 10:34:29 -07:00
github-actions[bot] 8d1f2993cb Add .swc gitignore handling to builder (#2427) (#2429)
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2026-06-16 10:56:30 +02:00
Pranay Prakash 80ccca023d fix(ai): recover @workflow/ai onto the 4.x stable line (#2440)
@workflow/ai was accidentally published to `latest` as 5.0.0, 6.0.0,
and 7.0.0. Root cause: it peer-depends on `workflow`, and changesets'
`onlyUpdatePeerDependentsWhenOutOfRange` defaults to false, so every
`workflow` minor on `stable` force-bumped ai a full major despite no
breaking changes.

- Add `onlyUpdatePeerDependentsWhenOutOfRange: true` so ai only majors
  when a new `workflow` version actually leaves its `^4` peer range.
- Reset packages/ai/package.json from 7.0.0 back to 4.1.2; a patch
  changeset re-releases it as 4.1.3 on the 4.x line (carries the
  inline-sourcemaps packaging change that was trapped in 6.0.0).

Verified via `changeset status`: with pending `workflow` minors,
@workflow/ai now bumps patch, not major.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 22:56:52 +00:00
github-actions[bot] 977a4a29d9 [next] Clarify serverExternalPackages warning (#2417) (#2431) 2026-06-15 19:36:11 +02:00
github-actions[bot] f2ad7261b1 Backport #1853: [core] Add wire-level framing for byte streams (#2380)
Co-authored-by: Peter Wielander <peter.wielander@vercel.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
2026-06-15 17:07:17 +02:00
github-actions[bot] f9119d4b6a fix(world-local,world-postgres): make duplicate hook_created idempotent (#2295) (#2374)
Co-authored-by: Nathan Rajlich <n@n8.io>
Co-authored-by: Peter Wielander <peter.wielander@vercel.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-06-15 15:20:32 +02:00
github-actions[bot] 48a19d948a Version Packages (#2352) workflow@4.5.0 2026-06-15 14:36:43 +02:00
github-actions[bot] 7dfc82b5c9 [ci] Increase dev.test.ts cleanup hook timeout (#2416) (#2420) 2026-06-15 11:28:26 +02:00
github-actions[bot] ffa053ecb5 Backport #2387: test: e2e coverage for run-idempotency conflict-handling strategies (#2402)
* test: e2e coverage for run-idempotency conflict-handling strategies (#2387)

* test: e2e coverage for run-idempotency conflict-handling strategies

Covers the patterns documented in foundations/idempotency:
- claim-only hook mutex: token claimed and held with no payload data,
  duplicate identifies the owner, token released after completion
- adopt the owner's result via conflict.returnValue
- signal the owner: duplicate forwards its payload via resumeHook
- supersede: duplicate cancels the owner and reclaims the token
- route-side resume-or-start retry pattern reaching the started run

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: fix adopt-owner-result race — gate owner completion on observed conflict

On slow runtimes the duplicate's first invocation could land after the
owner completed and released the token, making the duplicate a fresh
owner that waits forever for a payload (90s timeout across CI matrices).
Poll the duplicate's event log for hook_conflict before resuming the
owner, and widen the test timeout for the added gate budget.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* review: assert superseded owner's returnValue rejection; empty changeset

- Await run1.returnValue and assert WorkflowRunCancelledError so the
  cancellation is verified end-to-end and no rejection leaks from the
  supersede test.
- Test-only PR: use an empty changeset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci: retrigger preview deployments (turbopack deployment for 2e9d000 wedged in esbuild hang)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci: bust poisoned turbo cache entry for nextjs-turbopack build

The 2e9d000 deployment's next build crashed in an esbuild hang but its
task (70724907c9dd3a29) was recorded into the turbo remote cache anyway,
so every subsequent build with the same input hash replays the broken
artifact (missing routes-manifest). Change a build input to force a
fresh execution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* fix(backport): adapt conflict-handling tests to stable's getConflict/getRun API

The backport of #2387 used APIs that only exist on `main`, breaking the
nextjs-turbopack/webpack builds and the e2e suite on `stable`:

- `hookAdoptOwnerResultWorkflow`/`hookSupersedeOwnerWorkflow` read
  `conflict.returnValue`/`conflict.cancel()`, but on `stable`
  `getConflict()` resolves with `{ runId }`. Resolve the owning run via
  `getRun(conflict.runId)` inside a step (the documented stable pattern)
  to await its result / cancel it.
- Import `resumeHook` from `workflow/api` in 99_e2e.ts (was used by
  `forwardPayloadToOwner` but never imported).
- Convert the backported `waitForHook(token, { runId })` call sites to
  `waitForHookState(token, predicate)`; `waitForHook` does not exist on
  `stable` (#2405 standardized on `waitForHookState`).

Both workbench builds and `biome check` pass locally.

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

* fix(backport): import HookNotFoundError in e2e test

The backported conflict tests call `HookNotFoundError.is()` in
`hookClaimOnlyMutexWorkflow` (token-release wait) and the resume-or-start
route test, but the import was never carried into the stable test file —
causing a runtime `ReferenceError: HookNotFoundError is not defined`.
Import it from `@workflow/errors` (matches `main`).

Verified locally against nextjs-turbopack: the two previously-failing
tests plus the adopt/signal/supersede rewrites all pass (5/5).

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

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
2026-06-14 01:00:20 -07:00
Peter Wielander 570293370f [test] Fix stable e2e hookGetConflict test to use waitForHookState (#2405) 2026-06-13 13:50:41 -07:00
github-actions[bot] b5569a8590 Update queue client to 0.3.1 (#2399) (#2401)
Co-authored-by: Peter Wielander <peter.wielander@vercel.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-06-13 20:30:04 +02:00
github-actions[bot] 15507bdccf fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395) (#2404)
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
2026-06-13 20:18:58 +02:00
github-actions[bot] 296b785db0 Replace hook.hasConflict with hook.getConflict() returning the conflicting Run (#2373) (#2382)
* feat: replace hook.hasConflict with hook.getConflict (Promise<Run | null>)

hasConflict's boolean didn't expose WHICH run owns the token, so the
duplicate run couldn't act on the conflict. getConflict resolves with
null once registration commits, or with a Run handle for the conflicting
run — letting the workflow return/log the owner's runId, inspect its
status, await its result, or cancel it and continue, all in code.

The workflow-mode create-hook module exposes the bundle's compiled Run
class (durable step-proxy methods) on a well-known symbol so the host-
side hook consumer can construct the conflicting run inside the VM.
Contexts without the class (plain unit tests) fall back to a { runId }
object, which is also the documented v4 shape (no native Run
serialization in v4).



* fix: never resolve getConflict with a non-Run fallback shape

getConflict's contract is Promise<Run | null>. In the degenerate cases
where a real Run cannot be constructed — a hook_conflict event persisted
by an old world without conflictingRunId, or a context that never loaded
the workflow-mode create-hook module — reject with HookConflictError
instead of resolving with a { runId }-shaped impostor.

Test harnesses now register the Run class on the (VM) globalThis like
real bundles do.



* refactor: make getConflict a method — hook.getConflict()

A property getter that triggers registration/suspension reads as passive
state; a method makes the side effect explicit. Update implementation,
types, tests, e2e workflows, docs, and changeset.



* review: guard Run class registration, fix anchors, clarify changeset

- Only register WORKFLOW_RUN_CLASS when the workflow runtime is present
  (WORKFLOW_CREATE_HOOK installed on globalThis), so host imports of the
  workflow-mode module neither mutate the host global nor expose the
  non-step-proxy host Run.
- Drop #run-idempotency link fragments — that section lands in the
  stacked docs PR (#2011), which restores the anchored links.
- Note in docs that getConflict() rejects with HookConflictError for
  legacy hook_conflict events lacking the owner's run ID.
- Changeset now calls out the hasConflict -> getConflict() replacement.



* refactor: resolve the conflicting Run through the serialization class registry

Replace the bespoke WORKFLOW_RUN_CLASS global with the registry the
serialization pipeline already uses to revive Run instances:

- The SWC plugin already auto-registers the workflow bundle's compiled
  Run in globalThis[workflow-class-registry], but under a path-derived
  classId the host cannot know statically. The workflow-mode create-hook
  module now aliases it under a stable id (class//workflow//Run) via a
  new aliasSerializationClass() helper (a plain registry entry —
  registerSerializationClass cannot be reused since the plugin's IIFE
  already defined the non-configurable classId property).

- createConflictingRun() looks the class up with
  getSerializationClass(RUN_CLASS_ID, ctx.globalThis) and constructs
  through its WORKFLOW_DESERIALIZE hook, exactly as the Instance reviver
  would for a serialized Run crossing from a step into the workflow.

- Because the registry is keyed per-global, no environment guard is
  needed: a stray host-side import registers the host Run on the host
  registry, which is the correct class for that context. The
  WORKFLOW_CREATE_HOOK guard, the ??=, and the WORKFLOW_RUN_CLASS symbol
  are all deleted.

Verified: 1156 core unit tests; compiled workbench bundle contains the
stable alias alongside the plugin's path-derived registration with zero
WORKFLOW_RUN_CLASS references; all 5 hookGetConflict e2e tests pass
against a local nextjs-turbopack dev server, including conflict
resolution reading conflict.status through a durable step.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
2026-06-12 11:11:57 -07:00
github-actions[bot] 0fd1ba5e9f docs: move World SDK and getWorld under workflow/runtime, split out workflow/observability (#2375) (#2379)
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-12 10:49:45 -07:00
github-actions[bot] 3a1a88f6f0 Add hook.hasConflict for early hook conflict detection (#2015) (#2372)
* feat: add hook ready promise

* test: cover hook ready continuation scheduling

* feat: replace hook.ready with hook.hasConflict (Promise<boolean>)

- hook.hasConflict resolves true when the token is owned by another
  active hook, false once registration is committed — no throw, so
  workflows can branch on conflicts early. Awaiting it suspends the
  workflow to commit the hook registration (createHook alone does not).
- Chain the already-created fast-path through promiseQueue so
  resolution order matches event-log order (review feedback).
- Skip inline step execution when a suspension has an awaited hook
  creation so the hasConflict continuation can advance independently
  of step execution (review feedback).
- Update unit tests, e2e tests, workbench workflows, and v4/v5 docs.



* docs: fix inconsistent hasConflict bullet in create-webhook reference

State both resolution values explicitly (true = token already owned,
false = registered) instead of a parenthetical that only described the
false case.

* docs: require docs preview links in PR descriptions for docs changes



* docs: restore SWC Plugin heading in AGENTS.md



---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
2026-06-11 15:49:57 -07:00
Peter Wielander a86f3bd7dd [ci] Fix stable CI flakes: windows unit timeouts, matrix-job cache failures (#2358) 2026-06-11 14:09:03 +02:00
github-actions[bot] 38511ad632 Fix e2e failure reporting under vitest 4 and preserve fetch error causes (#2355) (#2357) 2026-06-11 12:39:59 +02:00
github-actions[bot] 253eeeae0f Prevent local tests from hanging (#2338) (#2342)
Signed-off-by: Justin Xu <xu.justin.j@gmail.com>
Co-authored-by: Justin Xu <xu.justin.j@gmail.com>
2026-06-11 12:21:50 +02:00
github-actions[bot] 4763e06eb7 [docs] Add "Step executed multiple times" error page (#2310) (#2333)
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-06-11 11:32:30 +02:00
github-actions[bot] 4291c5950d [core] Fix process crash from rejected waitUntil promises (#2336) (#2354)
Co-authored-by: Nathan Rajlich <n@n8.io
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-06-11 11:25:53 +02:00
github-actions[bot] 2100fc3a36 [core] Remove duplicate waitUntil for suspension handler async operations (#2345) (#2353)
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-06-11 10:55:07 +02:00
Peter Wielander 73975ada17 [core] Move stream reconnect logic to getReadable level (#1847) 2026-06-11 10:35:34 +02:00
Nathan Rajlich 32bebaffc7 Fix backport of #2305 to stable, correcting lockfile (#2344) 2026-06-11 09:47:54 +02:00
github-actions[bot] 9c667e05c2 feat(core): add optional namespace for queue topic prefix (#2305) (#2341)
* feat(core): add optional namespace for queue prefix

* fix(world-postgres): job queue prefix validation

* fix: changeset description




* fix: add world-postgres to changeset

* fix: world-postgres handle namespaced job queue names

* fix: resolve namespace via env var in core runtime

* fix: world-postgres job queue name task handler

* fix(world-postgres): honor namespace on consumer side

* Fix namespaced queue routing reliability (#2340)

* Fix namespaced queue routing reliability

* Inline queue namespace in generated routes

* Avoid loading Vercel functions during runtime import

---------

Signed-off-by: Will Sather <56037657+willsather@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2026-06-10 21:28:27 -07:00
github-actions[bot] 7f2050641e Version Packages (#2221)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
workflow@4.4.0
2026-06-10 17:24:42 -07:00
github-actions[bot] 326d93a4ae Backport #2012: Expose conflicting run id on hook conflicts (#2016)
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Peter Wielander <peter.wielander@vercel.com>
2026-06-10 17:06:26 +02:00
github-actions[bot] 8bc206522e feat(nitro): add /_workflow web ui during dev (#2110) (#2224)
Signed-off-by: Rihan Arfan <me@file.properties>
Co-authored-by: Rihan Arfan <me@file.properties>
2026-06-10 09:30:13 +02:00
Pranay Prakash b93a071796 Patch vulnerable package dependencies (#2302)
* chore: patch package dependency vulnerabilities

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* Prefer direct dependency upgrades for security fixes

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
2026-06-08 16:29:13 -07:00
Pranay Prakash bc2cf32eb2 [stable] Fix Next deferred step registration under Turbopack (#2298)
* fix(next): preserve lazy step registration on stable

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* refactor(next): reuse deferred step directory constant

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* test: gate deferred dev checks explicitly

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2026-06-08 21:29:08 +00:00
github-actions[bot] a84a293a22 [codex] Fix partial world-local exclusive writes (#2296) (#2299)
* fix(world-local): publish exclusive files atomically

* fix(world-local): guard temp cleanup

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-08 13:43:01 -07:00
github-actions[bot] 9acd46af13 Backport #2257: [codex] Handle lazy stream key request failures (#2293)
* Handle lazy stream key request failures (#2257)

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* fix(core): remove main-only world import

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
2026-06-08 12:49:47 -07:00
github-actions[bot] 8fde11c051 [world-vercel] Validate ref resolve responses before use (#2035) (#2297)
* [world-vercel] Validate ref resolve responses before use

When workflow-server returns a ref body to the SDK, the bytes are
fed into the workflow runtime's event log and deserialized via
`decodeFormatPrefix`. The SDK always writes ref payloads with at
least a 4-byte format prefix (see `encodeWithFormatPrefix` in
`@workflow/core`), so a zero-byte response — or one whose length
disagrees with `Content-Length` — is never a valid stored value.

Before this change, `resolveRefDescriptor` had no validation: a
200 with an empty body would be passed downstream as a zero-length
Uint8Array, which then failed deep inside replay with:

    Data too short to contain format prefix: expected at least 4 bytes, got 0

By that point the workflow's in-memory event snapshot is already
poisoned with the empty payload, so every subsequent replay
deterministically reproduces the same failure, downstream
`resumeHook()` calls surface as `Hook not found`, and the run
only unsticks when stale-run cleanup terminates the sandbox.

This catches the failure at the transport boundary instead, where
it can be retried as a `WorkflowWorldError`. Both an empty body
and a length mismatch (truncated streaming response) are rejected.

This is the SDK-side companion to vercel/workflow-server#432, which
adds the same validation on the server side.

* Address review: reject <4-byte bodies, handle malformed Content-Length

Three review changes:

1. Reject any body shorter than the 4-byte format-prefix length, not
   just zero-byte bodies. The SDK guarantees every stored ref payload
   starts with a 4-byte format prefix (FORMAT_PREFIX_LENGTH in
   @workflow/core), so a 1-3 byte body would also fail downstream
   replay with the same 'Data too short to contain format prefix'
   error this PR exists to prevent.

2. Parse Content-Length safely with parseInt + Number.isFinite +
   non-negative checks instead of bare Number(). A non-numeric value
   like 'abc' would otherwise produce NaN and silently surface as a
   'truncated' error, masking the real cause. Malformed values are
   treated as absent; the minimum-length check still defends against
   actual truncation in that case.

3. Add tests for the truncated-body-without-Content-Length case
   (chunked transfer where Content-Length validation can't see the
   truncation), and for a malformed Content-Length header that should
   be ignored rather than misreported as truncation.

The validation logic also moves into a small assertValidRefBody
helper to keep the inner trace function under the noExcessiveCognitiveComplexity limit.

* Address review: scope 4-byte minimum to binary refs, strict Content-Length parsing

- Only apply the 4-byte format-prefix minimum to application/octet-stream
  payloads; CBOR refs can legitimately be 1-byte primitives (true/0/null).
- Require Content-Length to be a plain run of digits before comparing;
  parseInt would otherwise accept numeric-prefixed garbage ('12junk' -> 12).
- Make the changeset succinct.

* Address review: skip Content-Length check for compressed responses

fetch/undici transparently decompresses gzip/br bodies but leaves
Content-Length describing the encoded (compressed) size, so comparing it
against the decompressed byteLength would reject valid compressed refs as
a phantom 'ref-body-length-mismatch'. Skip the comparison when a
non-identity Content-Encoding is present; an absent or 'identity' encoding
is still validated. Adds regression tests for both cases.

Signed-off-by: Nathan Rajlich <n@n8.io>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-08 12:49:25 -07:00
github-actions[bot] 2ab46de461 fix: bump devalue to 5.8.1 (#2292) (#2294)
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-08 12:07:14 -07:00
github-actions[bot] a7bb78ba04 fix(docs): declare Nitro auto-import globals for code samples (#2290) (#2291)
The nitro-native-build changelog sample calls useStorage() (a Nitro
server-side auto-import) from a step, which the docs type-checker
couldn't resolve and failed with TS2552. Add liberal global
declarations for useStorage/useDatabase/useRuntimeConfig so Nitro
auto-imports type-check in docs samples.

Signed-off-by: Nathan Rajlich <n@n8.io>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-08 12:05:40 -07:00
github-actions[bot] a06206ef41 fix(next): always apply turbopack content condition regardless of builder mode (#2253) (#2281)
* fix(next): always apply turbopack content condition regardless of builder mode

When lazy discovery is enabled (deferred builder), shouldApplyTurboCondition
was false, so turbopack.rules were added with no content filter — causing the
workflow loader to run on every JS/TS file. Apply the content condition
unconditionally so the loader only fires on files with workflow directives.



* add changeset

---------

Signed-off-by: Will Binns-Smith <wbinnssmith@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2026-06-05 14:45:42 -07:00
github-actions[bot] 4df5cff601 [nest] Use AST-based CommonJS import rewriting (#2080) (#2269)
Signed-off-by: yao <zhangyaoruo@outlook.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: yao <zhangyaoruo@outlook.com>
2026-06-05 12:01:45 -07:00