Move cn to its own lib/cn module that teaches tailwind-merge about the
design-system text-heading/text-label/text-copy/text-button/material
utilities, and import it directly from lib/cn.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* Propagate trace context to vercel-workflow.com in workbench instrumentation
@vercel/otel only propagates W3C trace context to Vercel deployment URLs
by default, so outgoing requests to the workflow-server
(vercel-workflow.com) got a client span with no `traceparent` header —
breaking the APM trace link to workflow-server's spans. Add
`instrumentationConfig.fetch.propagateContextUrls` for the workflow-server
domain in every workbench that uses @vercel/otel: example,
nextjs-turbopack, nextjs-webpack, and sveltekit. The Next.js and SvelteKit
apps already declared @vercel/otel but weren't registering it at all; they
now do.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Also propagate trace context to the Vercel Queue Service (vercel-queue.com)
The workflow-server queue path (@vercel/queue) sends to regional
vercel-queue.com subdomains (e.g. iad1.vercel-queue.com) when not using the
queues proxy, which were missing a `traceparent` header for the same reason
as vercel-workflow.com. Add `/vercel-queue\.com/` to propagateContextUrls in
all four workbench instrumentation configs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Use text-gray-900 for trace shortcut helper text
Co-authored-by: Mitul Shah <mitulxshah@gmail.com>
* Add changeset for trace shortcut helper text color
Co-authored-by: Mitul Shah <mitulxshah@gmail.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* fix(web-shared): stop data inspector duplicating expanded objects
Expanded objects/arrays now render bracket delimiters ({ … } / [ … ])
instead of repeating the inline preview alongside the child tree.
Co-authored-by: Cursor <cursoragent@cursor.com>
* nice
* fix: sync lockfile after dropping react-inspector
The react-inspector removal landed in package.json but the lockfile was
reverted during cleanup, breaking frozen-lockfile installs in CI.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(web-shared): keep data inspector colors theme-aware
Drop the dark-mode color overrides (and the data-theme/useDarkMode
wiring) and rely on the theme-aware --ds-* tokens, matching front:
strings stay green in both light and dark instead of turning blue.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(web-shared): add data inspector tests; tidy comments
Cover collapseRefs ref/typed-array/Map/Set handling and the rendered
tree (keys, value colors, brackets, commas, collapse/expand, empties,
dates, class/Map/Set prefixes) via jsdom + testing-library.
Co-authored-by: Cursor <cursoragent@cursor.com>
* revert(web-shared): drop data inspector tests
Remove the test suite and its jsdom/@testing-library devDependencies to
avoid adding new packages. Keeps the data inspector code unchanged.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(web-shared): extract data inspector styles to a sibling module
Move the class-name map and CSS string out of the component into
data-inspector.styles.ts for readability. Still injected via the
hoistable <style>; no behavior or dependency change.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(web-shared): restore ARIA tree semantics for the data inspector
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(web-shared): render RegExp values as /source/flags
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(web-shared): add unit tests for CopyableDataBlock JSON viewer (#2584)
Export serializeForClipboard and cover its clipboard formatting (strings, primitives, pretty-printed JSON, circular/BigInt fallbacks) plus CopyableDataBlock/EncryptedDataBlock rendering.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Swap the text-based eve placeholder for the real eve wordmark (hard-copied
SVG from @vercel/geistcn-assets, themed via currentColor) and drop Streamdown
so AI Elements is last.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
* Add Platformatic World to worlds-manifest.json
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
* ci fixup
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
* ci: pin platformatic world image to 0.8.1 and harden community-world runner
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
* platforamtic-world version
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
* ci: wire generic docker service-type into community benchmark workflow
The shared community-worlds matrix now emits service-type "docker" for any
world with non-builtin or multiple services (e.g. Platformatic, which needs
postgres + the platformatic/workflow image). tests.yml's e2e-community path
already handles it, but benchmarks.yml's benchmark-community path
(label-gated, non-blocking) did not — so a "community-benchmarks" run would
start no services and fail.
Mirror the e2e "Start Docker services" step, package-version pin, and docker
cleanup into benchmark-community-world.yml, and pass `services`/`version`
through from benchmarks.yml.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
Co-authored-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Section index card grids (e.g. foundations) were hand-written and drifted
from the sidebar (meta.json) and the actual pages. Make them derive from
the fumadocs page tree (single source of truth) and add CI lint so the
card grid and navigation can't fall out of sync again.
- resolveSectionChildren + <AutoCards/>, bound in both v4 and v5 docs
routes (correct /docs vs /v5/docs URL spaces)
- getLLMText expands <AutoCards/> so llms.txt/.md/copy-page keep child links
- manualCards frontmatter opt-out for curated pages (source.config.ts)
- checkSectionCards (card<->nav completeness) + checkMetaEntriesResolve
(dangling meta entries) in scripts/lint.ts
- convert foundations + errors (drift fixes) and v5 observability to AutoCards
- mark deploying + ai as manualCards (intentionally curated)
- remove dangling meta entries: v4 cancellation (x2), root introduction
(x2), v4/internal serializable-abort-controller
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* perf(core): memoize step return value hydration across replays
The inline replay loop re-executes the workflow body and re-consumes the
full event log on every iteration. For each already-completed step, the
step consumer re-decrypted and re-devalue-parsed the serialized result on
every replay — O(N^2) decrypt+parse operations across a single
invocation of a sequential N-step workflow.
Add a per-run memoization cache, owned by the inline loop in runtime.ts
(alongside cachedEvents) so it survives across replay iterations of the
same run but never leaks across runs. It is threaded into runWorkflow and
stored on the orchestrator context, and consulted in the step_completed
path keyed by the persisted event id. This makes a completed step's
hydrated result O(1) on subsequent replays, turning the aggregate cost
into O(N).
Determinism is preserved: the cache lookup happens inside the existing
ctx.promiseQueue slot and still resolves via the same resolve(), so a
cache hit occupies the identical position in the ordered delivery chain a
re-hydrate would have — pendingDeliveries accounting, delivery barriers,
and Promise.race/all replay are untouched.
Identity safety: hydrateStepReturnValue returns a fresh object graph each
call and each replay runs in a fresh VM, so sharing an object reference
across replays could let one replay's mutation leak into the next. Only
primitive results are memoized (immutable, reference-share == re-parse);
non-primitives re-hydrate fresh every replay, exactly as before. Hook,
wait, and abort hydration paths are intentionally left uncached.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* perf(core): bound memoized step-hydration cache by primitive size
Address the review note that the per-run step hydration cache was never
size-bounded: cached entries hold the decrypted/parsed plaintext of a
primitive step result for the whole invocation, on top of the serialized
bytes already retained in cachedEvents, so a long run returning large
strings could roughly double peak retained memory for those results.
Document the cache's memory characteristic (per-invocation, freed when the
invocation ends, bounded by primitive-returning step count) and cap the
only primitive types that can carry a large payload: string/bigint results
longer than MAX_MEMOIZED_PRIMITIVE_LENGTH (4 KiB) fall through to the
existing per-replay re-hydrate path instead of being memoized. Large
payloads are cheap to re-hydrate relative to their footprint, so this caps
the worst case at negligible cost. Other primitives are inherently small
and always memoized.
The cap only ever reduces what is cached, so deterministic replay is
unaffected: oversized values take the already-correct re-hydrate path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
On the first delivery of a run's first invocation, background run_started,
skip the initial event-log load, and force optimistic inline start so the run
reaches its first steps with no preceding network round-trips. Safe because the
first delivery has no concurrent handler to race the step create-claim; turbo
exits the moment a suspension creates a hook or wait, and is a no-op for every
other invocation. On by default; disable with WORKFLOW_TURBO=0.
Wire the existing AI SDK logo into the OSS product switcher (above Flags
SDK) and add a new eve entry (text wordmark + Beta badge, linking to
eve.dev/docs) above it at the top of the list.
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
* otel(world-vercel): inject trace context on v4 event requests
The v4 event path (createEvent / getEvent / listEvents) routes through
fetchV4 → global fetch with a custom undici dispatcher, bypassing both the
makeRequest path (where the explicit W3C trace-context injection lives) and
ambient undici auto-instrumentation. As a result, v4 event traffic from the
flow route carried no traceparent, so workflow-server could not parent its
spans to the invocation — its spans never joined the /flow execution trace,
even though v2/v3 reads/writes (via makeRequest) did join.
fetchV4 now calls injectTraceContextIntoHeaders before fetch, the single
choke point for all v4 create/get/list requests, mirroring makeRequest.
No-op when no OpenTelemetry SDK is registered.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(agents): require trace-context injection on new world-vercel HTTP paths
Codify the guardrail that the v4 regression revealed: any outgoing
world-vercel request must call injectTraceContextIntoHeaders (auto-
instrumentation can't be relied on with the custom dispatcher / global fetch),
with a test in trace-propagation.test.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* changeset: make v4 trace-propagation note concise
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Default source maps to dev-on / prod-off
Inline source maps are embedded in the step bundle and the intermediate
workflow VM bundle, which bloats production function bundles (a problem for
the Vercel 250MB limit) even though maps only help when reading a stack trace.
Make the default environment-aware in @workflow/builders: inline in
development (next dev / nitro dev / Vite-based dev servers, detected via
config.watch or NODE_ENV=development) and off in production. The `sourcemap`
config option and `WORKFLOW_SOURCEMAP` env var still override in either
environment. A production build with no override also drops the
source-map-support shim from the Vercel step function.
Keep runtime stack remapping graceful and fast when maps are absent
(@workflow/core): short-circuit when no frame references the workflow file
and memoize the parsed map (or its absence) per bundle, so production failures
don't rescan the bundle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(e2e): make source-map expectations match dev-on/prod-off default
The e2e error-stack tests gate source-map assertions on hasWorkflowSourceMaps()
and hasStepSourceMaps(). Now that source maps default to off in production
builds, update those helpers:
- hasWorkflowSourceMaps(): false for all production builds (local prod,
postgres, Vercel — keyed off DEV_TEST_CONFIG), and exclude nest in dev (the
Nest integration builds with watch:false / no NODE_ENV=development, so its
bundles have no maps).
- hasStepSourceMaps(): nest now resolves to a production build (maps off) in
both dev and prod.
Add unit cases for the dev-vs-prod and nest behavior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* otel: nest linked-mode invocations under the delivery context
Follow-up to the linked-trace mode shipped in #2363. In linked mode the
queue-delivered workflow.execute / step.execute spans were created as new
trace roots (root: true) with span links to both the delivery context and
the run-origin context. That split a single local invocation across two
traces: the framework route/server span and the workflow execution span
ended up in different traces connected only by a link.
This nests each invocation under its local delivery context instead:
- Drop `root: true` on the queue-delivered workflow.execute / step.execute
spans so they become children of the active context — the framework
route/server span when one exists, otherwise a clean root. One invocation
(route handler, replay, inline steps, event writes) is now a single
bounded trace.
- buildInvocationSpanLinks in linked mode now returns only the run-origin
link; the delivery context is the parent, so it is no longer also a link.
The run-origin context remains a link (never a parent) and re-enqueues still
forward the original carrier unchanged, so a long-running run is still never
stitched into one giant trace across invocations. continuous mode is
unchanged. Everything remains a no-op when no OpenTelemetry SDK is
registered, and there is no dependency on any particular framework: with no
route/server span active, the invocation span is a clean root rather than an
orphan.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Update .changeset/nest-linked-invocations-under-delivery.md
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Karthik Kalyan <105607645+karthikscale3@users.noreply.github.com>
---------
Signed-off-by: Karthik Kalyan <105607645+karthikscale3@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Derive the deterministic RNG seed from `runId:workflowName:deploymentId`
and the VM's initial fixed clock from the ULID timestamp embedded in
`runId` (via the new `runIdCreatedAt` helper). All of these inputs are
available the instant a queue message arrives, so the VM seed and clock
no longer depend on `startedAt` (set only after the `run_started`
round-trip). This is the prerequisite for starting VM initialization
earlier on the critical path.
This changes the seed-derived value sequence for a given run, so the
affected deterministic test fixtures are regenerated accordingly.