Drop the synchronous getModuleSourceUrl prop so hosts maintain one prop
instead of two. resolveModuleSourceUrl now returns its own best-effort
fallback, so the sync variant is unnecessary; the Module row renders as
plain text until resolved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an optional async resolveModuleSourceUrl to the trace-viewer sidebar
data. The details-panel "Module" link prefers it over the synchronous
getModuleSourceUrl (falling back when it's absent or returns nothing), so
hosts can resolve the link to the file's real source path and extension
and never render a broken link.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* 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>
* test: regression coverage for hook.resume() from isolated route bundles (o2flow beta.26 incident)
Reproduces the o2flow v5 upgrade failure (workflow@5.0.0-beta.26, fixed by
#2752 in beta.28): a plain API route importing defineHook() from the root
`workflow` entry and calling .resume() failed with Turbopack's
"Cannot find module as expression is too dynamic" stub, because the world
registration was tree-shaken out of the route bundle and getWorldLazy()'s
dynamic-import fallback got stubbed.
The bug only manifests when a route bundle loads in isolation (a Vercel
lambda): local `next dev`/`next start` evaluates next.config.ts, whose
workflow/next import chain registers the world process-wide and masks it —
which is why no existing server-driven suite caught it.
- route-bundle-isolation.test.ts: production Turbopack build of the
nextjs-turbopack workbench, then loads ONLY the compiled route bundle in a
bare Node subprocess (cold-lambda simulation) and invokes its POST handler.
Fails with the exact incident error on regressed code; passes on main.
Wired into the build-error-messages CI job.
- e2e: plainModuleDoneHook round-trip through a plain API route on the two
Next workbenches (deployed matrix covers real lambda isolation).
- Workbench fixtures mirroring o2flow: a directive-less defineHook module
shared by a workflow (create) and a plain route (resume). The webpack
workbench gets a real route file because `next dev` (webpack) does not
serve directory-symlinked app routes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
* test: authenticate plain hook resume request
* test: address review — marker-based harness output parsing, changeset summary
- route-bundle-isolation: prefix the harness result line with a unique
marker and locate it explicitly instead of JSON.parse()ing the last
stdout line, so stray logging from the route bundle or the world can't
break parsing; failures now include the full subprocess stdout.
- changeset: add a human-readable summary to the (release-less) changeset.
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>
Co-authored-by: Karthik Kalyanaraman <karthik.kalyanaraman@vercel.com>
Co-authored-by: Karthik Kalyan <105607645+karthikscale3@users.noreply.github.com>
* 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>
Hook timeline bars were amber/yellow via RESOURCE_COLORS; passive
spans should be gray to match the event list icons and minimap.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* Give Metadata Token and Hook ID copy + truncation
Add token to the copyable metadata attributes set and constrain
copyable key-value rows so MiddleTruncate can shrink long IDs.
* Remove AttributePanel copy unit tests
The Metadata Token/Hook ID copy change is small enough that the
dedicated panel render tests are unnecessary.
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* 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
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>
* 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>
- 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>
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.