Commit Graph

351 Commits

Author SHA1 Message Date
Peter Wielander 1471f252fa [core] Gate event creation on the loaded event count and restart replays in-process (#3145) 2026-07-31 14:27:43 -07:00
Nathan Rajlich 438eaa6a59 Make resumeHook() resilient to transient hook_received event write failures (#1834)
* Make resumeHook() resilient to transient hook_received event write failures

When events.create('hook_received') fails with a retryable error (429/5xx),
resumeHook() now dispatches the queue message with a `hookInput` payload
carrying the dehydrated hook payload. The workflow runtime materializes the
missing hook_received event from that payload on its next delivery, mirroring
the existing resilient-start behavior of start() / run_created / run_started.

Returned Hook carries a new `resilientResume: true` flag when the fallback
path was taken. Both write paths share a client-minted `resumeId` as an
idempotency key so the runtime can dedup if the direct write actually
committed but the client saw a transient error.

Uses a sequential write-then-queue flow (not parallel) to avoid a dedup race
on the happy path: hook_received events have no entity-level conflict guard
(unlike run_created), so a duplicate written before the direct write commits
would double-deliver the payload to the workflow.

* Fix resilient resume: use local payload in materialized hook_received event

The server returns a 'lazy' response for hook_received event creation,
where eventData.payload may be a RefDescriptor (when the payload
exceeded the inline size and was offloaded to blob storage) rather
than the raw bytes. Pushing this directly to the in-memory events
array caused the workflow VM to fail with 'Invalid input' when trying
to deserialize the RefDescriptor as a Uint8Array.

Substitute the eventData we already have locally so the in-memory
event matches what getWorkflowRunEvents would return after
client-side ref hydration.

* Gate resilient resume on target runtime capability; carry hook token; export ResumedHook; docs

- Only take the resilient path when the target run's recorded
  @workflow/core version understands hookInput on the queue payload.
  Runs keep executing on the deployment they were created on (skew
  protection), and older runtimes parse the queue message with a schema
  that silently strips unknown fields - the resume payload would be
  lost while resumeHook() reported success. Fail fast (propagate the
  original event-write error) for such runs instead, preserving the
  caller's ability to retry.
- Carry the hook token on hookInput and write it into the materialized
  hook_received event so it gets the same replay-divergence guard as a
  directly written event (#2030 parity).
- Export ResumedHook from @workflow/core/runtime and workflow/api.
- Add changelog page and update resumeHook() API reference docs.

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

* Address review: correct capability cutoff, drop own-version escape hatch, replay-side resumeId dedup

Review fixes for the resilient-resume capability gate and dedup:

- Bump the supportsQueueHookInput cutoff to 5.0.0-beta.39: 5.0.0-beta.38 is
  published WITHOUT this feature (its queue-payload schema strips hookInput),
  so classifying it as capable would silently lose resume payloads. The
  cutoff is now a single exported constant (QUEUE_HOOK_INPUT_MIN_VERSION)
  with a TODO(release) requiring re-verification at merge time.
- Remove the own-version exact-match escape hatch entirely: version strings
  do not identify builds (a published beta.38 and a main-built tarball can
  share a version string while differing in content), so the check could
  declare a featureless published deployment capable. Pre-release builds now
  fall back to fail-fast until the version is bumped past the cutoff — the
  safe direction. Tests simulate a capable target explicitly.
- Make duplicate suppression authoritative at the replay boundary: replay
  now dedups hook_received events sharing a resumeId (same resume attempt),
  so even when concurrent redelivery of the same queue message
  double-materializes the event (no World enforces uniqueness on
  hook_received), the payload reaches workflow code exactly once. This is a
  pure function of the persisted log, keeping replay deterministic. The
  runtime's snapshot check remains as best-effort write suppression, with
  its comment corrected to say so; the EntityConflictError catch is kept as
  the forward-compatible signal for planned server-side (runId, resumeId)
  uniqueness, with its comment corrected to say it is defensive today.
- Stamp materialized hook_received events with occurredAt decoded from the
  resumeId ULID so resiliently-resumed hooks are timestamped at resume time
  rather than after the queue round-trip.
- Pin the cross-version compat contract in a test: the direct write is
  resumeId-only (no digest or negotiation fields), which later server-side
  idempotency work must keep accepting.
- Exercise the published boundary (5.0.0-beta.38) in fail-fast tests, and
  make the capability tests self-check against the exported cutoff constant
  instead of restating literals.
- Docs: changelog date June -> July 2026, dash consistency, and document the
  replay-side dedup guarantee.

* Encode release-gate and successor-rebase contracts into code comments

Comment-only changes capturing the review agreements so they survive the
parallel-resume successor rebase (no behavior change):

- capabilities.ts: the QUEUE_HOOK_INPUT_MIN_VERSION re-verification point
  is the actual combined SDK release (after the successor lands and its
  server-side dedup is deployed), not source-merge time — this PR merges
  source-only and no SDK is published from it alone. Every Version
  Packages merge in between moves the earliest possible carrier.
- workflow/hook.ts + runtime.ts: scope the replay-side resumeId dedup
  honestly as defense-in-depth over the persisted log, not a
  cross-invocation exactly-once guarantee — concurrent invocations
  replaying pre-duplicate snapshots each see only their own row; the
  storage-level (runId, resumeId) constraint in the successor work is the
  correctness boundary. The set stays useful post-constraint for logs
  written before it deployed.
- runtime.ts: document the EntityConflictError swallow's known gap while
  the branch is defensive (this invocation's local log lacks the payload;
  progress relies on the other writer's delivery or redelivery) and pin
  the rebase contract for when the constraint makes it live: a matching
  claim must append the canonical event locally and succeed; a real
  conflict must rethrow for redelivery.
- resume-hook-resilient.test.ts: reframe the wire-shape pin as a tripwire
  rather than a permanent contract — the successor deliberately widens it
  (ID/digest pair + attestation) before any SDK release, so the
  resumeId-only shape never ships as a published server contract.

---------

Co-authored-by: Peter Wielander <peter.wielander@vercel.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 20:45:54 +00:00
christopherkindl 3c7875ad73 [docs] upgrade @vercel/geistdocs to 1.19.0 (#3170)
* chore: upgrade @vercel/geistdocs to 1.17.1

Picks up the new footer (Footer no longer takes a config prop), the
heading font-weight change to 450, and the tightened navbar OSS-menu
marks. Also switches the site's own navbar logo from the vendored
geistcn LogoWorkflow fallback to the package's LogoWorkflowSdk, using
its new tuned default height instead of a hardcoded 15.

Fixes a resulting regression: navbarOssProducts entries lacked an `id`,
so resolveOssProducts' `product.id !== activeProduct` filter evaluated
to `undefined !== undefined` (false) for every entry and emptied the
OSS flyout. Added stable `id`/`label` values to each entry.

* refactor: use default navbarOssProducts list instead of a custom override

The manual navbarOssProducts array (with local logo imports/heights) is
no longer needed now that the package's DEFAULT_OSS_PRODUCTS list
already includes all these SDKs with proper id/label/section values.
navbarActiveProduct: 'workflow-sdk' now handles self-exclusion instead.

* fix: use bg-background-200 for docs surfaces to match the template

The docs, cookbook, and v5 route layouts plus the shared DocsLayout
container hardcoded bg-background-100 (pure white), so /docs/* pages
rendered on a lighter surface than the rest of the site. Switch them to
bg-background-200, matching the geistdocs template's page background.

* style: adopt package text-heading-* utilities for homepage headings

The marketing homepage headings hardcoded font-semibold (weight 600)
plus manual responsive sizes/tracking, so they rendered heavier than
the docs headings that now use Geist's 450 heading weight. Swap each
display heading to the package's text-heading-* utilities, which bundle
the 450 weight, line-height, and tracking, mapped across breakpoints to
the nearest design-system size. Inline label/emphasis spans keep their
own weight.

* style: adopt package text-heading-* utilities for worlds headings

Extends the homepage heading change to the /worlds section: the world
listing, detail, compare, and building-a-world pages plus their
components hardcoded font-semibold display headings. Swap each to the
package's text-heading-* utilities (450 weight + line-height +
tracking), mapped across breakpoints to the nearest design-system size.
Mono stat numbers, per-benchmark item labels, and the dialog title keep
their own weight.

* style: remove the bordered grid framing from the homepage

The homepage sections were wrapped in a grid divide-y border-y sm:border-x
container, drawing side borders and divider lines between every section.
Drop that framing so the sections flow with whitespace separation.

* style: remove vertical column dividers from homepage sections

Drop the divide-x column dividers still drawn inside the use-cases
(3-col), feature-grid (2-col), and templates sections, so no vertical
lines remain after the section-grid removal. Section padding keeps the
columns visually separated.

* style: make the homepage "Get started" CTA button rounded-full

* style: use text-heading-* for the feature-grid paragraph text

The two 2-col feature blurbs ("Deep integration with AI SDK.",
"Durable agents by default.") hardcoded their size/leading/tracking
plus font-medium/font-semibold weights. Those manual sizes already
equal text-heading-20/24, so swap to text-heading-20 lg:text-heading-24
— same sizes, but the Geist 450 heading weight (lead drops 600 -> 500
via the utility's [&>strong] rule). The lead stays gray-1000 for
emphasis; body stays gray-900.

* style: fade out the run-anywhere provider logos at the left/right edges

Add linear-gradient masks to the flanking cloud-provider logo groups in
the "Run anywhere, no lock-in" viz so they fade to transparent toward
the outer edges, leaving the centered code block untouched.

* style: widen the right-edge fade on the Vercel dashboard viz

The "Workflow SDK on Vercel" dashboard is offset off the right edge, so
the existing to_left black_10% mask fell off-screen and the visible
right edge hard-clipped. Widen it to black_40% so the dashboard fades
out gradually at the visible right edge.

* style: add spacing between the Vercel, use-cases, and templates sections

Wrap the UseCases and Templates sections with a top margin so there's
clear separation between "Workflow SDK on Vercel", "Build anything with
AI Agents", and "Get started" now that the section dividers are gone.

* style: widen the homepage layout from 1080px to 1200px

* style: align use-cases code block and templates cards with the Vercel section

Switch the "Build anything with" and "Get started" sections from
grid-cols-3 / [1fr_2fr] to [1fr_1.5fr], matching the "Workflow SDK on
Vercel" section above so their code block and cards share the same
right-hand column. The wider text column also lets "Build anything with"
sit on one line. Normalize both to outer padding + column gap so the
code block and cards line up exactly.

* style: extend use-cases/templates content to the right layout edge

Drop the right padding at md+ (md:pr-0) so the code block and template
cards reach the same right edge as the "Workflow SDK on Vercel"
dashboard above, which bleeds to the container edge. Mobile keeps its
padding.

* style: remove the divider between the two feature cards

Drop divide-y/lg:divide-y-0 from the feature grid so no border shows
between "Deep integration with AI SDK" and "Durable agents by default".

* refactor: position homepage sections on a shared 12-col grid

Replace the ad-hoc [1fr_1.5fr] + md:pr-0 + lg:pl-* positioning on the
Vercel, use-cases, and templates sections with a shared grid-cols-12
layout (text col-span-5, visual col-span-7), matching the vercel.com
marketing grid convention. The Vercel dashboard becomes a proper grid
cell instead of an absolutely-offset right-bleed, so all three
sections' visuals align by the grid columns with no magic values.

* style: align homepage width with the navbar content

Widen the homepage container from max-w-[1200px] to the site's
max-w-[1448px] (matching the navbar/footer) and reduce the section
gutters from sm:px-12 to sm:px-6, so section content lines up with the
navbar's content edges (right edge flush at the same column as the
navbar and footer). Also convert the "Reliability-as-code" section to
the shared grid-cols-12 layout (col-span-5 text / col-span-7 code
example), replacing its lg:grid-cols-[330px_1fr] magic values.

* refactor: handle homepage horizontal padding at the root container

Move the mobile/desktop gutter (px-4 sm:px-6) onto the homepage root
container and remove the horizontal padding from every section
component. Section content still aligns with the navbar/footer content
edges, but the gutter is now defined once instead of repeated per
section. Inner-element padding (tab buttons, visual internals) is
unchanged.

* style: left-align content sections on mobile + fix run-anywhere/o11y viz

- Left-align the centered content sections on mobile only (FeatureCardWide,
  TweetWall heading, Frameworks, Run-anywhere heading/buttons), restoring
  their centered layout at sm and up.
- Make the "Inspect every run" timeline span edge-to-edge by shifting its
  gantt from a 14-col grid (content in cols 2-13) to a flush 12-col grid.
- Constrain the run-anywhere viz cluster to the code block width so the
  provider cards (AWS/Docker/etc.) overlap behind the code block again.

* style: anchor run-anywhere provider cards to overlap the code block

Position the flanking provider-card groups relative to the centered
code block (right/left calc(50%+140px)) instead of the section edges,
so the cards sit behind and overlap the code block regardless of the
section width.

* style: make the reliability-as-code example fill its column to the right edge

Drop max-w-3xl mx-auto from the workflow/non-workflow code examples so
they fill the col-span-7 cell, aligning the code block's right edge with
the layout's right content edge (matching the tabs and other sections).

* style: split feature-card copy into a title + description

Break the AI SDK / durable-agents feature blurbs into a heading and a
separate muted description with a gap (matching the other sections)
instead of one inline paragraph, and drop the trailing periods from the
feature titles so they read as headings.

* update

* update

* style: give the tweet cards a bg-background-100 surface

* fix(swc-playground): pin monaco-editor to 0.55.1

The lockfile refresh resolved the unpinned `monaco-editor: "latest"` from
0.55.1 to 0.56.0, breaking the workflow-swc-playground Turbopack build.

0.56.0 rewrote its exports map to reroot subpaths under `esm/vs/`
("./*": "./esm/vs/*.js"). monaco-vim@0.4.4 deep-imports
`monaco-editor/esm/vs/editor/editor.api` and
`.../common/commands/shiftCommand`, which now map to
`esm/vs/esm/vs/...` — a path that does not exist. Under 0.55.1
("./*": "./*") both specifiers resolve to real files.

monaco-vim 0.4.4 is the latest published release, so pinning
monaco-editor is the only available fix.

* update

---------

Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com>
2026-07-31 10:21:15 -07:00
Pranay Prakash 11dc036854 ci: stop deploying changeset-release/main, run its e2e against production (#3243)
* ci: stop deploying changeset-release/main, run its e2e against production

The changesets action force-pushes `changeset-release/main`, and it can
point at exactly main's HEAD SHA. Vercel keeps one commit status per
project per SHA, so when both a production deployment (from main) and a
preview deployment (from changeset-release/main) are built for the same
commit, whichever finishes last owns the status. On 2026-07-30 the
preview finished last, so `vercel/wait-for-deployment-action` — which
reads the deployment ID out of that status — handed production e2e runs
a preview deployment ID and forked runs across environments.

Disable git deployments for that branch in every Vercel project rooted
in this repo, and give the changeset PR's Vercel e2e lanes a deployment
to test that actually exists: main's production deployment for the PR's
base SHA, resolved by SHA so a mid-flight production build is waited out
rather than silently replaced by an older one.

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

* ci: resolve changeset-release e2e deployments with the wait action, tokenless

Per review: with changeset-release/main no longer deployed, main SHAs
can never again be deployed to a second environment of these projects,
so the per-SHA commit status the action reads is unambiguous for
exactly this lane. Reuse vercel/wait-for-deployment-action with
environment: production and sha pinned to the PR base SHA instead of
the Vercel-API polling script, drop the script and its VERCEL_TOKEN
usage, and inherit the action's inactive/skipped-build handling.

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

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
2026-07-31 10:09:36 -07:00
Andrew Barba 2677653759 fix(world-local): bound stalled queue deliveries (#3255)
Signed-off-by: Andrew Barba <barba@hey.com>
2026-07-31 08:27:35 -07:00
Nathan Rajlich 32ac8e73fd Fix Biome lint violations and add Biome CI check (#3222)
* Fix Biome lint violations and add Biome CI check

Biome was not configured to respect .gitignore, so ~92% of the 13,355
reported diagnostics came from gitignored build artifacts. Enable VCS
integration (useIgnoreFile), apply safe auto-fixes across the repo, fix
the remaining mechanical errors by hand, downgrade judgment-call a11y /
dangerouslySetInnerHTML rules to warnings, and add a 'biome ci' job to
the Lint workflow so violations block PRs going forward.

* Use an empty changeset (no behavior change, no release needed)
2026-07-30 22:32:12 +00:00
Peter Wielander 34975f6b7d [world-vercel] Make HTTP/2 actually multiplex on the events path (#3190) 2026-07-29 14:38:17 -07:00
Peter Wielander a09d00135b Revert "Statically inject workflow world target" (#2752) (#3142) 2026-07-29 08:55:29 -07:00
Pranay Prakash 7d7effd49f docs: correct the workflow ID claim in publishing libraries (#3153)
* docs: correct the workflow ID claim in publishing libraries

The consumer re-export file does not relocate a library's workflow and
step IDs into the consumer's source tree. An ID is derived from where the
file lives, so any export-reachable package file keeps a name@version ID
whether or not it is re-exported.

Rename the section to describe what the re-export actually does — put the
package's directive files on the compiler's discovery graph and give the
entry point a resolvable address — and add the upgrade guidance that
follows from the real behavior: a package version bump renames every
workflow and step it ships, so in-flight runs must drain first.

The wrong claim also appeared in the page summary and the CopyPrompt, so
it is corrected in all three places, in both the v4 and v5 copies.

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

* docs: describe deployment pinning instead of drain guidance

Runs are pinned to the deployment that recorded their step IDs, so a
library version bump does not strand in-flight runs: new runs execute
the new version, in-flight runs keep replaying on their original
deployment. Replaces the incorrect drain-before-upgrade advice.

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

* docs: qualify deployment pinning as world-dependent

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

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 11:42:15 -07:00
Nathan Colosimo 7959acc8cf Remove deprecated setAttributes aliases (#3128) 2026-07-28 18:36:22 +00:00
Peter Wielander 49276f2d0b [utils] Fix vercel world not being selected when running build on external CI (#3144) 2026-07-28 11:26:30 -07:00
Pranay Prakash 7dba3ae722 docs: redirect retired migration-guides URLs to comparisons (#3127)
* docs: redirect retired migration-guides URLs to comparisons

The Migration Guides section was replaced by Comparisons in #2676
without redirects, 404ing the previously indexed
/docs/migration-guides/* URLs. Add permanent redirects mapping each
migrating-from-* page to its workflow-sdk-vs-* comparison, plus a
temporary catch-all onto the comparisons index.

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

* docs: redirect migration guide markdown URLs

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Karthik Kalyanaraman <karthik.kalyanaraman@vercel.com>
2026-07-27 13:45:39 -07:00
Nathan Colosimo d813fb8ee8 feat(core): deterministic sandbox hardening (#3045)
* feat(core): deterministic sandbox hardening

- crypto.subtle.digest computes synchronously via node:crypto:
  byte-identical values, promise settles on a deterministic microtask,
  full BufferSource validation (internal-slot view reads, SAB rejection)
- Atomics.waitAsync (a wall-clock timer), async WebAssembly compilation,
  WeakRef, and FinalizationRegistry are removed from the sandbox — wall
  clock and GC observation are unreplayable; sync WebAssembly
  constructors remain
- freezeSerializationIntrinsics pins the universal dispatch surfaces:
  Object.prototype/Array.prototype/Function.prototype are frozen (every
  missed property read and hasInstance lookup terminates there) and
  serialization-referenced global bindings are non-writable. Value-type
  prototypes and constructor statics stay patchable so polyfills
  (Temporal's Date.prototype.toTemporalInstant, core-js Set.prototype
  .union / Object.groupBy) keep working — the retained-input gate
  verifies the members serialization executes per boundary instead.

Groundwork for retained-VM replay (#2990).

* Drop serialization intrinsic freezing from the sandbox

The retained-VM passivity design moved from pinning/verifying the
sandbox surfaces serialization dispatches on to injecting hardened
operations into devalue itself (with taint-based de-opt), so freezing
Object/Array/Function prototypes and pinning global bindings is no
longer needed. Keep only the determinism hardening (sync digest,
removal of wall-clock/GC-observing APIs).

* Document and lock in why async crypto.subtle methods cannot break quiescence

The remaining async subtle methods reject immediately through the crypto
proxy (brand check — the receiver is not a real SubtleCrypto), so they can
never mint a host-timing promise. Narrow the quiescence comment to what the
code actually enforces and add a test so the unreachability is not silently
"fixed" later.

* simplify sandbox hardening: lean digest input conversion, async digest, explicit subtle throwers

* mark sandbox API removals as a major change

---------

Co-authored-by: Nathan Rajlich <n@n8.io>
2026-07-27 09:23:16 -07:00
Nathan Colosimo 62d570ed4b Remove retired v1 step route plumbing (#3061) 2026-07-24 23:50:55 +00:00
Nathan Rajlich 706b6c41a7 fix: upgrade postcss to >=8.5.18 to address GHSA-r28c-9q8g-f849 (#3102) 2026-07-24 15:21:58 -07:00
Peter Wielander 3069b4918e [next] Respect .gitignore in dev watcher to avoid EMFILE on large monorepos (#3085) 2026-07-24 14:30:52 -07:00
Karthik Kalyan fc81f4502f perf(core): immediate leading-edge dispatch for idle streams (flush window default 0) (#3088)
* perf(core): immediate leading-edge dispatch for idle streams (flush window default 0)

Production producer-rate data (24h of client flush spans): most agents
average 1.03-1.21 chunks per flush with 87-98% single-chunk flushes and
>70% of chunks arriving more than 10ms after the previous request had
already settled — a fixed 10ms leading window batches almost nothing
for them while adding ~20% to isolated-chunk publish latency (~50ms
median RTT). The one bursty producer (avg ~4-8 chunks/flush) gets its
batching from in-flight accumulation, which does not depend on the
window at all.

The leading chunk of an idle sink now dispatches immediately by
default (window 0): first chunk goes out at once, chunks arriving
during its request coalesce into the next group, and each settle
dispatches the accumulated group immediately — path-independent
batching with no fixed tax on slow producers. A positive
WORKFLOW_STREAM_FLUSH_INTERVAL_MS (or world.streamFlushIntervalMs,
applying from the second group) opts into a windowed leading edge for
slow-but-steady producers that prefer larger groups over first-chunk
latency. Early-ack, the durability drain barrier, wire caps, and
backpressure bounds are unchanged.

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

* Update packages/world/src/interfaces.ts

Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Karthik Kalyan <105607645+karthikscale3@users.noreply.github.com>

* review: env var overrides world streamFlushIntervalMs; world option governs the leading edge too

WORKFLOW_STREAM_FLUSH_INTERVAL_MS, when set, now takes precedence over
world.streamFlushIntervalMs; otherwise the world option applies from the
very first chunk (no more second-group lazy quirk). Deciding waits for
the world when needed, which adds no latency: sendGroup awaits the same
promise before any request can leave.

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

---------

Signed-off-by: Karthik Kalyan <105607645+karthikscale3@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-07-24 12:29:28 -07:00
Karthik Kalyan b610c46f81 perf(core): path-independent stream write batching (group commit in the server writable) (#3078)
* perf(core): move stream write batching into WorkflowServerWritableStream (group commit)

Batching previously lived in flushablePipe's coalescing loop, so it only
engaged on paths that used flushablePipe (getWritable). A raw
ReadableStream crossing a workflow/step boundary is piped with native
pipeTo(), which does not pull chunk N+1 until write(chunk N) resolves —
and write() resolved only after the flush timer AND the server round
trip, so the buffer never held more than one chunk and every token
became its own server request.

The sink now group-commits:
- write() resolves when the chunk enters a bounded client buffer; the
  bound counts buffered AND in-request chunks
  (WORKFLOW_STREAM_MAX_INFLIGHT_CHUNKS, preserving its documented
  meaning) plus a byte bound (WORKFLOW_STREAM_MAX_BUFFERED_BYTES, new,
  default 8 MiB, documented in runtime-tuning). A full buffer applies
  backpressure until a group lands durably.
- The flush interval is a real group-commit window; chunks arriving
  while a request is in flight accumulate and form the next writeMulti
  group. One request in flight at a time preserves chunk order.
- Per-request wire limits (1,000 chunks / 1 MiB) split groups exactly
  as the coalescing pipe did; an oversized single chunk goes alone.
- Durability moved to an explicit barrier (STREAM_DRAIN_SYMBOL):
  close() drains before closing; flushablePipe adopts the barrier so
  lock-release completion (step completion) still means 'everything
  written is durable'; abort() DRAINS the accepted prefix (never
  closing) so a producer error after acked writes cannot lose data —
  native pipeTo aborts the sink on source failure; and a failed pipe
  drains before settling so a step failure is not persisted ahead of
  the emitted prefix. A dispatch failure retains the group, poisons
  the sink, and surfaces at the next write/close/drain.

flushablePipe is now a plain per-chunk pump responsible only for
lock-release completion and durability tracking; its coalescing
machinery and STREAM_WRITE_BATCH_SYMBOL are removed.

Covered: native-pipeTo batching (the regression), awaited per-chunk
loops coalescing into one writeMulti, in-flight accumulation, wire-cap
splits (count/byte/oversized), in-flight-inclusive backpressure for
both bounds, sequential fallback without writeMulti, source-error
prefix delivery through abort, failed-pipe drain-before-reject,
early-ack sticky errors, turbo run-ready barrier gating (incl. dwell
telemetry), drain-barrier adoption/rejection, and group-level flush
spans. 1,572 core unit tests pass; e2e tier requires a deployment and
was not run here.

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

* fix(core): re-dispatch a chunk buffered in the request settle gap

Review (bot): a write landing between the dispatch loop's empty-buffer
exit and the reaction clearing the in-flight marker armed no timer
(scheduleGroupCommit saw the marker set) and was never dispatched on an
open stream — only a later write/close/drain would pick it up. The
settle reaction now re-dispatches when the buffer is non-empty, treating
the chunk as an in-request arrival; drain waiters settle with the new
chain. Regression test aims a write at the settle gap and asserts both
chunks flush without a close.

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

* docs(core): document abort-drain boundedness and terminal-run conflict handling

Review note: the abort-path drain is deliberately un-timeboxed (a bound
would drop acked chunks); its worst case is owned by the World
transport's finite timeout/retry budget, and a teardown-driven drain
into an already-terminal run rejects into the existing catch.

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

* test(core): poll instead of fixed sleeps for dispatch assertions

The native-pipeTo batching test flaked on a slow CI runner: a fixed
25ms wait raced the 10ms commit window plus scheduler jitter. All
'dispatch has happened' assertions now poll the expectation (bounded);
intentional negatives keep their fixed windows.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 09:02:35 -07:00
Joey Hotz cdb3db4049 fix(world-postgres): abort stalled HTTP delivery on shutdown (#3064)
Signed-off-by: Joey Hotz <joeyhotz1@gmail.com>
2026-07-23 19:53:12 -07:00
Peter Wielander cfe7570d67 [builders] Add opt-out for discovering workflows in node_modules (#3054) 2026-07-23 22:57:26 +00:00
Nathan Rajlich f11e9fe56f fix: upgrade next to 16.2.11 to address CVE-2026-64641 (#3071) 2026-07-23 15:18:11 -07:00
Nathan Rajlich 9216556bf5 fix: upgrade postcss to >=8.5.12 to address CVE-2026-45623 (#3067)
* fix: upgrade postcss to >=8.5.12 to address CVE-2026-45623

* fix: override transitive postcss <8.5.12 to patched version
2026-07-23 12:47:36 -07:00
Peter Wielander 97a53550a4 docs: fix stale/incorrect v5 API reference details (#3017) 2026-07-21 11:43:13 -07:00
Peter Wielander eb8fdb9797 Default WORKFLOW_PRECONDITION_GUARD on (#2946) 2026-07-21 10:18:47 -07:00
Pranay Prakash 918a2c558c docs: replace migration guides with a Comparisons section (#2676)
* docs: replace migration guides with a Comparisons section

Add a Comparisons section (v4 + v5) with an index/snapshot across all frameworks and deep-dive pages for Temporal, Cloudflare Workflows, AWS Step Functions, AWS Bedrock AgentCore, Inngest, and trigger.dev. Remove the old migration-guides section, folding its concept-mapping and migration content into the relevant comparison pages, and repoint top-level nav in both versions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: refresh comparison pages with current facts (July 2026)

Re-verified each comparison against the vendor's current public docs and
updated what changed since the June 2026 snapshot:

- Temporal: Worker Versioning is now GA; Serverless Workers (AWS Lambda,
  pre-release) scale to zero, so soften the blanket "no scale-to-zero";
  drop the unsubstantiated "Uber" customer claim (Uber is Cadence's origin,
  not a Temporal customer).
- Cloudflare Workflows: note the new per-step billing dimension (500K/mo
  included, then $0.80/100K) landing no earlier than Aug 10, 2026; note the
  50K concurrency ceiling was raised from 4,500 at GA.
- AWS Bedrock AgentCore: add newer GA modules (Harness, Policy, Evaluations);
  correct compliance (SOC/PCI/ISO under internal assessment, audits pending;
  FedRAMP not yet authorized; drop GovCloud claim); refresh languages
  (@aws/agentcore CLI scaffolds TS or Python); "some modules preview" is stale.
- Inngest: Pro pricing $75 -> $99/mo; encryption middleware now TS + Python;
  AgentKit/Realtime are Developer Preview and Connect is public beta; self-host
  is community/best-effort (not "unsupported"); Free-tier run duration 30 days
  vs 366 on Pro; soften funding to ~$30M+.
- AWS Step Functions & trigger.dev: facts re-confirmed; date stamp only.

Bumped every "as of June 2026" stamp to July 2026. v4 and v5 kept identical.

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

* docs: present comparisons in the present tense; v5-only; Pro usage-based pricing

Follow-up pass on the comparison pages:

- Remove all date references (past and future). Anything that lands on a
  date is stated as already in effect: Cloudflare's per-step billing, Temporal
  Serverless Workers and GA Worker Versioning, AgentCore's Harness/Policy/
  Evaluations modules. Dropped "as of July 2026" stamps, founding/GA years,
  funding round dates, and roadmap/"being added" phrasing.
- Workflow SDK: reference v5 only and treat it as GA (was "v4 GA / v5 beta").
- Pricing and limits: quote the Pro/paid tier only and usage-based rates only;
  drop plan-included quotas and free-tier allowances (Step Functions 4K/mo free,
  Cloudflare 500K steps/mo included, Inngest 50K free execs, Inngest/Free 30-day
  run cap, Temporal $100/mo plan minimum).

v4 and v5 kept identical.

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

* docs: tighten comparison maturity/status wording

- Drop dateless "upcoming change" phrasing: AgentCore compliance now states
  current facts only (no "self-assessed"/audit-pending implication); remove
  Inngest's "SSPL → Apache after 3 yrs" license-conversion note.
- Don't label the Workflow SDK "GA" — non-beta is the default; also drop bare
  "GA" where it only meant "not beta" (Temporal "7 SDKs", Inngest "TypeScript",
  competitor maturity cells).
- Maturity cells no longer cite version numbers; they describe backing/track
  record instead (e.g. "Built and maintained by Vercel", "Backed by AWS").

v4 and v5 kept identical.

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

* docs: make "features without a 1:1 equivalent" sections directional

Rename each heading to name the competitor that has the feature (e.g.
"Temporal features without a direct Workflow SDK equivalent") and add a
lead-in clarifying these are the competitor's capabilities the Workflow SDK
doesn't replicate one-to-one, with how to cover each on the Workflow SDK side.

v4 and v5 kept identical.

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

* docs: point world links at the /worlds routes

The comparison pages linked to /docs/deploying/world/* and
/docs/deploying/building-a-world, which no longer exist in the docs
trees (the Docs Links check rejects them on v5 pages, where /docs hrefs
are render-rewritten and skip the legacy redirects). Link the canonical
/worlds/* routes directly, in both body links and frontmatter refs.

v4 and v5 kept identical.

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

* docs: address toolbar review feedback on the comparison pages

- Drop the Maturity row from every at-a-glance table
- Add a "what the limits mean in practice" paragraph to each comparison,
  spelling out what the competitor's caps mean for long-running AI
  workloads, and link Vercel World limits to the pricing doc
- Security cells: lead with zero-config per-run E2E encryption and note
  platform security is per-World, instead of the VM-sandbox framing
- Temporal: drop the throughput sentence and the still-in-preview
  Serverless Workers mention from the performance cell
- Cloudflare: end the recommendation on "already all-in on Cloudflare"
- Convert the "features without a direct equivalent" bullet lists into
  two-column tables so it's unambiguous which product owns each feature
- Fix the Inngest page's "no step cap" cell (Vercel World caps runs at
  10K steps per the pricing doc)

v4 and v5 kept identical.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Wielander <peter.wielander@vercel.com>
2026-07-21 18:21:15 +07:00
Nathan Colosimo a5e6f1167a feat(core): add experimental Hook minimum retention (#2865)
* feat(core): add hook token retention contract

* refactor(core): constrain hook retention options

* fix(core): preserve boolean hook visibility options

* revert(core): preserve HookOptions interface

* docs(core): clarify retained conflict ownership

* docs(core): retain newest-wins conflict pattern

* docs(core): simplify hook retention guidance

* docs(core): explain retained token cleanup

* docs(core): simplify idempotency guidance

* docs(core): clarify retained token results

* refactor(core): rename hook token expiration option

* chore(core): name hook expiration changeset

* docs(core): simplify Hook expiration language

* docs(core): clarify Hook expiration deadline

* docs(core): remove Hook deadline caveat

* refactor(core): align Hook expiration field names

* docs(core): narrow Hook expiration documentation

* docs(core): clarify hook expiration availability

* Update packages/core/src/workflow/hook.ts

Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Nathan Colosimo <110621881+NathanColosimo@users.noreply.github.com>

* docs(core): clarify Hook token expiration behavior

* docs(core): explain active Hook expiration behavior

* feat(world): advertise hook ttl capability

* fix(core): validate hook ttl capability after main merge

* refactor(core): rename hook expiry to minimum retention

* docs: keep hook retention guidance on v5

* docs: define retained run availability

* fix(core): validate Hook retention at creation

* feat(core): define retained Hook lookup semantics

* refactor(core): simplify hook retention checks

* docs(core): simplify retained conflict example

* docs(core): flatten forward-to-owner example

---------

Signed-off-by: Nathan Colosimo <110621881+NathanColosimo@users.noreply.github.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-07-21 15:35:55 +07:00
Peter Wielander 96719d8220 [ci] Enable NestJS e2e-vercel-prod and add to docs as "experimental" (#3011) 2026-07-20 14:21:17 -07:00
Peter Wielander aafc3fd7f4 docs: fall back to first child page for sidebar folders without an index (#3009) 2026-07-20 13:44:33 -07:00
Peter Wielander 542138dc0b [nest] Fix NestJS Vercel build output (#2988) 2026-07-20 12:14:09 -07:00
Rich Haines 21448a8ab3 chore(docs): update @vercel/geistdocs to 1.14.0 (#3002)
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-07-20 11:23:08 -07:00
Rich Haines e892e8b3c5 fix(docs): add version-switcher fallback redirects for pages missing in one version (#3003) 2026-07-20 10:54:51 -07:00
Peter Wielander 6353c8c6cf fix(core): batch stream writes via writeMulti (#2995) 2026-07-20 09:19:03 -07:00
Peter Wielander bb773e9507 Enable additional perf optimizations when correctness guarantees are met (#2970) 2026-07-17 14:02:14 -07:00
christopherkindl 5554c253ae chore(docs): update @vercel/geistdocs to 1.11.4 (#2955)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 09:27:40 -07:00
Pranay Prakash 8a872529fe docs: make /worlds the canonical home for World docs (#2934)
* docs: make /worlds the canonical home for World docs

The world pages (Local/Postgres/Vercel) and Building a World were
duplicated inside the v4 and v5 docs trees while /worlds/[id] rendered
the v4 copy — hiding v5-only content like multi-region and leaving two
diverging sources of truth.

- Move world docs to an unversioned docs/content/worlds/ collection
  (based on the v5 copies, with inline 4.x callouts for factory naming
  and 5.x-only env vars), rendered at /worlds/*
- Add /worlds/building-a-world; flatten the docs Deploying section to a
  single intro page and drop its Rocket icon
- Point every link, frontmatter ref, and worlds-manifest docs field at
  /worlds/*; add redirects for the removed v5 and building-a-world URLs
- Keep world docs on agent-facing surfaces: search, llms.txt,
  sitemap.md/.xml, and .md exports now serve the worlds collection
- Extend the docs link linter to validate worlds pages (with heading
  anchors) and their outgoing links

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

* docs: version the world docs like the docs trees (v4/v5 switcher)

Instead of a single unversioned copy, world docs now follow the same
versioning strategy as the docs pages: content/worlds/v4 is served at
/worlds/* (current) and content/worlds/v5 at /v5/worlds/*, restoring the
original per-version content. Each world detail page (and Building a
World) renders the docs version switcher — the worlds listing page has
no natural home for it, so it lives on the world pages themselves.

- Render-time href rewriting on v5 pages now covers /worlds/... links
  (shared rewriteHrefForVersion helper, also used by the v5 docs and
  cookbook routes), and the markdown-export rewrite does the same
- v5 world pages are noindexed with a canonical to /worlds/<id>;
  community worlds stay unversioned (/v5/worlds/<id> redirects)
- /v5/docs/deploying/world/* redirects now land on /v5/worlds/*;
  /v5/worlds and /v5/worlds/compare redirect to the unversioned pages
- Link linter models the versioned worlds URL spaces (v5 pages resolve
  /worlds hrefs against the v5 collection); sitemap.md and the .md
  export routes cover /v5/worlds/*

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

* docs: fix v4 multi-region anchor and tighten version-prefix matching

Address PR review:
- The v4 Deploying page linked /worlds/vercel#multi-region, but the
  Multi-region section only exists on the v5 world page; use the
  explicit cross-version /v5/worlds/vercel#multi-region link (this was
  the Docs Links CI failure)
- rewriteHrefForVersion now uses the boundary-checked hasPathPrefix
  (shared leaf module lib/geistdocs/path-prefix.ts, also used by
  source.ts) instead of bare startsWith
- buildVersionUrl's shared-route fast path is segment-based rather than
  substring includes()

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

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:15:07 +07:00
Roey D. Chasman 6b8efd58ce feat: cross-run lineage via reserved run attributes (#2153)
Signed-off-by: Roey D. Chasman <rchasman@gmail.com>
2026-07-15 11:41:31 -07:00
Ricardo Gonzalez d07bbf2ef7 update python docs to no longer mention experimentalServices (#2935) 2026-07-15 12:38:49 +00:00
Peter Wielander a00d169470 Add stateUpdatedAt precondition guard to event creation (#2266) 2026-07-15 02:11:14 +00:00
Nathan Colosimo 35899580bd Fix Nitro cleanup for React Router and add setup guides (#2908)
* fix(nitro): support React Router Vite builds

* refactor(nitro): simplify React Router cleanup

* docs(react-router): specify cleanup version

* fix(nitro): close temporary Vite servers
2026-07-14 13:34:43 -07:00
Elliot Dauber 2588e53c02 [docs] fix code highlight in child workflows cookbook (#2922) 2026-07-14 13:32:33 -07:00
Karthik Kalyan f72184dc83 feat(world-local): add WORKFLOW_LOCAL_RECOVER_ACTIVE_RUNS env var (#2914)
The recoverActiveRuns factory option had no environment variable, so
disabling startup re-enqueueing of pending/running runs required a custom
world module via WORKFLOW_TARGET_WORLD. Wire an env fallback
(0/false disables, 1/true enables, explicit factory option wins) and
document it in the worlds configuration reference and local world guide.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 12:45:59 -07:00
Karthik Kalyan 9242ddb02c telemetry: move client stream spans from world-vercel to core (#2901)
* fix(deps): dedupe @opentelemetry/api to a single workspace instance

The lockfile resolved both 1.9.0 and 1.9.1, so the copy that registers
the tracer provider (via @vercel/otel in the app) and the copy a package
imports could differ. The API's global-registration version check rejects
a consumer newer than the registered copy and silently hands back a noop
tracer — which is why world-vercel's spans (workflow.stream.write/
chunk_rtt, read.connect, its http spans) never reached Datadog from
deployed apps while core's spans flowed in the same process. Root-caused
via the DEBUG=workflow:* run on #2900: import succeeds, no warn, spans
dropped.

Pin a single version via a workspace override so every bundle shares one
API instance.

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

* debug: one-shot OTEL runtime diagnostic in core + world-vercel; DEBUG on turbopack workbench

The dedupe alone did not restore world-vercel span emission (verified on
this PR's own preview: stream traffic flowed, zero workflow.stream.write
spans). Under DEBUG=workflow:*, both packages now log once how their
module instance of @opentelemetry/api sees the world — global
registration version, provider/delegate/tracer/probe constructor names,
and whether a probe span is recording. Diffing the core line (spans work)
against the world-vercel line (spans dropped) in one deployment's logs
pinpoints the divergence.

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

* debug: log span identity for named world-vercel spans; namespace otel probes per package

Diag round 1 showed world-vercel's tracer records and instrumentedFetch
handles the stream PUTs, yet the named spans are unfindable in the
backend. Round 2: log traceId/spanId/isRecording for every named
instrumentedFetch span under DEBUG so export can be checked for a
specific span id, and split the probe span names (.core /
.world_vercel) so per-package export is attributable.

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

* telemetry: emit stream RPC latencies from core (chunk_rtt, connect_ms, close span)

world-vercel's instrumentedFetch spans never export from deployed apps
(root cause still open — see PR discussion), so the operationally
needed client-side latency signals move one layer up to core, whose
spans are proven to export:

- workflow.stream.write.chunk_rtt on the workflow.stream.flush span:
  the World write RPC duration, network included (same attribute key as
  world-vercel's per-request span so queries are layer-agnostic).
- workflow.stream.read.connect_ms on the workflow.stream.read span:
  the world.streams.get await (read dispatch -> stream handle).
- new workflow.stream.close span: the close RPC round trip.

Bonus: measured at the World interface, these cover world-local and
world-postgres too, not just Vercel deployments.

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

* telemetry: emit read-completion span (total duration, chunks, bytes)

Completes the read-side picture: workflow.stream.read.complete is
back-dated to the read dispatch so its duration is the total read, with
chunk/byte counts for throughput. Cancelled reads emit nothing.

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

* chore: drop DEBUG from turbopack workbench; tighten changeset

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

* telemetry: cover createReconnectingFramedStream in read telemetry

Ordinary serialized streams read through createReconnectingFramedStream
(which calls world.streams.get directly), so connect_ms / ttfc /
read.complete never fired for that path — only WorkflowServerReadableStream
was instrumented. Wire the same helpers into the framed reader: first-
connect duration, first-frame TTFC, and completion totals — plus
workflow.stream.read.reconnects, which only this path can know.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 19:34:01 -07:00
Karthik Kalyan 200845d885 docs: clarify Vercel-specific analytics notes and align sidebar chevrons (#2909)
- analytics: attribute the serving path to the Vercel observability data
  pipeline instead of naming the ClickHouse store, drop the Vercel-only
  "Plan-bounded lookback" bullet, and mark the pageInfo lookback ceiling
  and upgradeAvailable comments as Vercel-specific.
- sidebar: folder rows without an index page render as <button>s, which
  don't stretch to the row width like the <a> folder links, leaving their
  ms-auto chevron hugging the label. Stretch those triggers to full width
  so every chevron sits at the end of the row.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:51:00 -07:00
Nathan Rajlich 6bbb5e264e docs: fix stale and broken vercel.com links (#2910)
Audited every vercel.com link in docs/content (20 unique URLs,
HTTP-validated including anchor fragments):

- project-configuration#regions (2x): the #regions anchor no longer
  exists on that page — content moved to the vercel-json subpage; now
  links project-configuration/vercel-json#regions
- gateway/api-reference/overview (2x): hard 404; the AI Gateway docs
  restructured — 'get an API key' context now points at
  ai-gateway/authentication
- observability/otel-overview (1x): redirects to
  tracing/instrumentation; link the final URL
- docs/workflow and docs/workflow/python (8x): redirect to the plural
  docs/workflows paths; link the final URLs (also drops a redundant
  ?language=py param that the redirect discards)

All other links (queues, queues/pricing + anchors, plans/hobby,
limits, regions, sandbox, workflows/pricing#storage-retention,
cli/project-linking, audit-log, home, help, blog) verified 200 with
live anchors.
2026-07-13 16:49:10 -07:00
Karthik Kalyan f2be954bb7 Add attribute discovery and filtering to world.analytics (#2903)
* Add attribute discovery and filtering to world.analytics

- analytics.attributes.list() — distinct attribute keys observed on runs
  in the window, with run counts and first/last seen timestamps
  (GET /v2/analytics/attributes).
- analytics.attributes.listValues({ key }) — distinct values for one key
  with latest-write-wins run counts (GET /v2/analytics/attributes/values).
- analytics.runs.list({ attributes: { key: value } }) — restrict the runs
  listing to runs whose latest attribute snapshot matches every provided
  pair (JSON-encoded query param, up to 8 pairs; $-prefixed framework
  keys allowed in read filters).

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

* Shorten changeset; document world.analytics in the World SDK reference

The analytics namespace was previously undocumented. Adds a full
reference page (runs, attributes, steps/events/hooks/waits, lookback
windows and pageInfo), links it from the World SDK index and meta, and
replaces the stale 'in the future you'll be able to search by
attributes' line in the attributes guide with a filtering section.
Extends the docs-typecheck ambient world global with the analytics
namespace so reference snippets typecheck.

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

* Drop analytics.attributes.listValues

Not a derived requirement: the agent-runs UI filters by known constant
values and reads per-run values via batch attribute fetches; it never
enumerates distinct values for a key.

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

* Feature-detect world.analytics in the attributes guide example

The snippet dereferenced the optional analytics namespace without the
runtime check its skip-typecheck annotation claimed, and would throw on
local/Postgres/custom Worlds. Guard it and drop the annotation — the
block is now genuinely typechecked.

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

* Fix stale 'querying attributes not available' bullet in the guide

The Behavior list still claimed a query API was only planned, directly
contradicting the Searching and filtering section above it. State what
is implemented: attributes are readable on run objects everywhere, and
key discovery / key=value run filtering are available through the
optional Analytics API.

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

* Surface the analytics namespace on the API reference index pages

The Analytics page sits under workflow/runtime > World SDK, but neither
the API reference index card, the workflow/runtime World SDK card, nor
the World SDK overview mentioned analytics — making the new reference
effectively undiscoverable from /docs/api-reference. Mention it at each
level of the path.

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

* Bump changeset to minor

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:06:23 -07:00
Nathan Rajlich 2b5522020c docs: document multi-region support in the Vercel World (#2895)
* docs: document multi-region support in the Vercel World

The Vercel World Limitations section still described the backend as
iad1-only, which is no longer true — it now runs in every Vercel
Function region, with each run pinned to a single region at creation.

- Replace the single-region / iad1 data-residency limitations with a
  Multi-region section: automatic pinning from the creating function's
  region (single- and multi-region deployments), explicit per-run
  selection via start(..., { region }), routing semantics for readers,
  and 4.x/pre-existing-run behavior (iad1, no migration).
- Version requirement called out as workflow 5.x beta with a TODO to
  pin the exact minimum version once released; 4.x will not support
  region pinning.
- Limitations now lists the one that remains: a run's region is fixed
  at creation (no migration).
- /docs/deploying: add a Multi-region bullet to the Vercel World
  feature list, deferring details to /worlds/vercel.

* docs: clarify region option scope — data + queue dispatch, not code placement

The start({ region }) option pins where the run's data is stored and
where its queue messages dispatch from; it does not deploy code.
Execution happens in the regions the application is deployed to, so
region-local execution requires deploying the app to the desired
region (vercel.json regions or the project's Function Regions
setting). Adds a warning callout to the explicit-selection section and
tightens the intro to only claim region-local execution for the
automatic case.

* docs(v4): point default-version pages at the v5 multi-region docs

The v4 docs are the default version, so readers landing on
/worlds/vercel (which renders v4 content) or /docs/deploying would
never discover multi-region exists behind the v5 switcher.

- v4 Vercel World Limitations: add a callout that multi-region ships
  with workflow 5.x (deep link to the v5 Multi-region section), and
  scope the iad1-only statements to the 4.x release line — they remain
  true there; 4.x will not gain multi-region.
- v4 /docs/deploying: matching callout after the Vercel World blurb.

* docs(v5): surface the world pages in the Deploying sidebar

The v5 world pages (vercel-world, postgres-world, local-world) were
orphaned: the Deploying sidebar only listed 'Building a World', and
the dedicated /worlds/:id route renders v4 content — so there was no
navigation path to the v5 Vercel World page (and its new Multi-region
section) at all.

- v5/deploying/meta.json: add the world folder to the sidebar
  (Worlds group between the section index and Building a World)
- world/meta.json: group title 'World' -> 'Worlds'
- v5 /docs/deploying callout: link to the v5 world page (with a
  multi-region deep link) instead of /worlds/vercel, which silently
  drops the reader into v4-rendered content

Verified via dev server: the Worlds group (all three pages) renders in
the v5 sidebar and the Multi-region section renders on the page.

Note for docs owners: other v5 content still links to the versionless
/worlds/* routes, which render v4 content — probably worth a broader
pass or making /worlds version-aware.

* docs(v4): surface the world pages in the Deploying sidebar

Same fix as v5: the v4 Deploying sidebar only listed 'Building a
World', leaving the world pages reachable only via the /worlds/:id
routes with no sidebar path. Adds the Worlds group (Vercel World,
Postgres World, Local World) between the section index and Building a
World, and pluralizes the group title.

Verified via dev server: the group renders, and the sidebar items land
on /worlds/:id through the existing permanent redirects (same v4
content).

* docs: pin multi-region minimum version to workflow 5.0.0-beta.33

vercel/workflow#1981 merged; the first release carrying multi-region
support is 5.0.0-beta.33. Replace the '5.x (currently in beta)'
placeholders (and the TODO marker) with the exact minimum version on
all four touched pages (v5 world page + deploying bullet, v4
breadcrumb callouts).

* docs: note hook-token data residency in the multi-region section

Hook tokens carry no region information, so the token-to-run mapping
behind getHookByToken()/resumeHook() is currently stored in iad1 for
every run regardless of its region. Hook payloads are unaffected —
a received payload lands on the run's event log in the run's region
like all other run data. Noted as potentially becoming a
project-level setting in the future.

* docs: address review — execution-locality wording + dedupe limitation

- /docs/deploying multi-region bullet: 'execution' -> 'queuing'; the
  region option pins data/queue/streams, while execution follows the
  app's deployed regions (matching the v4 callout and the world page's
  own warning callout)
- v5 world page: drop 'region is fixed for its entire lifetime' from
  the Good to know bullet — the Limitations section already owns that
  statement; the bullet now covers only routing semantics
2026-07-13 14:28:36 -07:00
Peter Wielander a4d8de03e6 [world-vercel] [builders] Add WORKFLOW_SEQUENTIAL_REPLAYS option to limit flow route concurrency to one (#2193) 2026-07-13 20:57:38 +00:00
Karthik Kalyan 4a43e39fec telemetry: stream flush span + otel load diagnostics (#2891)
* telemetry: emit client-observed workflow.stream.write span per flush batch

Complements the existing workflow.stream.read TTFC span: each flushed
batch emits a back-dated CLIENT span covering the app-perceived write
latency (buffer dwell + RPC), with buffer_dwell_ms / chunks / bytes
attributes so client-side batching cost (flush timer, turbo run-ready
barrier) can be told apart from network/server time. Failed flushes
keep the batch's original t0 so a retried batch reports its full dwell.

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

* chore: tighten changeset

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

* telemetry: rename flush span to workflow.stream.flush; DEBUG-log world-vercel OTEL load failure

- workflow.stream.write is taken by world-vercel's per-request RPC span
  (#2857, chunk_rtt); the per-batch flush span gets its own name so the
  two stay distinguishable in trace queries. Attributes move to
  workflow.stream.flush.{buffer_dwell_ms,chunks,bytes}, operation=flush.
- world-vercel's @opentelemetry/api load failure was silently latched as
  null, which also swallows bundler/resolution failures in apps that DO
  register a tracer (observed in production: workbench apps emit core
  spans but none of world-vercel's). Log the reason under
  DEBUG=workflow:* so the failure mode is diagnosable.
- Document workflow.stream.flush in the tracing docs.

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

* chore: tighten changeset

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:58:05 -07:00
Rich Haines aa93cc5e69 Migrate docs to package-backed geistdocs (#2222)
* Migrate docs to package-backed geistdocs

* update agent install cmd on home page

* add copy prompt component usage

* update docs test for sitemap inclusion

* cut unused components

* address docs migration review feedback

* address stale review feedback: geistdocs 1.8.2, version icons, cookbook prompts

* drop Workflow from OSS products dropdown (self-link)

* bump @vercel/geistdocs to 1.11.0

* fix: resolve pnpm-lock.yaml conflict marker from main merge

---------

Co-authored-by: Peter Wielander <peter.wielander@vercel.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-13 18:44:11 +02:00
Peter Wielander 0b956f65cb Rename experimental_setAttributes to setAttributes (#2882) 2026-07-11 10:17:37 -07:00