mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
fix/ack-after-step-enqueue-stable
140 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b93a071796 |
Patch vulnerable package dependencies (#2302)
* chore: patch package dependency vulnerabilities Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * Prefer direct dependency upgrades for security fixes --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> |
||
|
|
bc2cf32eb2 |
[stable] Fix Next deferred step registration under Turbopack (#2298)
* fix(next): preserve lazy step registration on stable Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * refactor(next): reuse deferred step directory constant Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * test: gate deferred dev checks explicitly --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: JJ Kasper <jj@jjsweb.site> |
||
|
|
9092640013 |
feat(core): support passing parent WritableStream to child workflow via start() (#2059) (#2070)
* test(e2e): cover WritableStream passed as start() argument Adds an e2e workflow + test where a parent workflow gets a WritableStream via getWritable(), forwards it through start() to a child workflow, and the child step writes raw bytes to it. Asserts the external reader on the parent's stream observes the exact bytes the child wrote. * fix(core): avoid double-framing when WritableStream is forwarded via start() When a workflow's getWritable() handle is passed across start() to a child workflow, the parent step's reviver wraps it in a serialize transform that pipes into a workflow server stream. Until now, getExternalReducers.WritableStream then installed a second serialize transform on top of that — so every chunk the child step wrote got devalue-framed twice but only deframed once on the reader side, and external consumers saw the inner frame instead of the original bytes. Fix: tag every user-visible writable that's already backed by a workflow server stream with its (runId, name). When the external reducer recognizes those tags during dehydration, it bridges bytes straight from the new child-side server stream to the original server stream instead of piping through the user's writable. That leaves the producer-side serialize transform (installed once by the child's step reviver) as the only framing layer in the chain. * fix(core): forward (runId, name) when a tagged WritableStream crosses start() Replaces the previous in-process bridge with first-class writable forwarding at the descriptor level. When a parent workflow's getWritable() handle is passed as an argument to a child workflow, the dehydrated descriptor now carries the original (runId, name). The child run's step-side reviver opens the writable against the parent's server stream directly and resolves the parent run's encryption key (encrypt-only) via getEncryptionKeyForRun. This removes the architectural limitation that the bridge could only stay alive for the duration of the parent step process — on Vercel that capped forwarding at ~15 minutes regardless of the child run's lifetime, dropping any writes the child made after the parent step process exited. importKey() now accepts a usages parameter, defaulting to ['encrypt', 'decrypt']. The cross-run forwarding path imports with ['encrypt'] only so a compromised child run cannot decrypt any existing data on the parent's stream — only contribute new writes. * test: rename writable-forwarded workflows and cover step-context getWritable() Addresses PR review: - Rename writableForwardedToChildChildWorkflow → writableForwardedChildWorkflow (drops the duplicated 'Child' segment). - Split writableForwardedToChildWorkflow into two variants covered by a test.each: writableForwardedFromWorkflowWorkflow (workflow-context getWritable, the original test) and writableForwardedFromStepWorkflow (step-context getWritable passed directly into start() from the same step that called getWritable()). - Terser changeset description. Signed-off-by: Nathan Rajlich <n@n8.io> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Nathan Rajlich <n@n8.io> |
||
|
|
c0e4abf47c |
[e2e] Add event-log-race-repro label for triggering CI stress-test (#2159) (#2173)
|
||
|
|
ac83cd4bd5 |
fix(workbench): cache .vercel/output for Next workbench builds (#2095) (#2096)
Turborepo replays nextjs-turbopack:build from cache without restoring the Vercel diagnostics manifest (.vercel/output/diagnostics/workflows-manifest.json), which causes the Vercel deployment to fail post-build. Add .vercel/output/** to the workbench's Turbo outputs so it is persisted and replayed. Applies to both nextjs-turbopack and nextjs-webpack (whose turbo.json is a symlink). Signed-off-by: Nathan Rajlich <n@n8.io> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
6aabd6fd2e |
[swc-plugin] Capture lexical this for nested arrow step functions (#1935) (#1945)
* [swc-plugin] Capture lexical `this` for nested arrow step functions When a nested arrow `"use step"` references the enclosing function/method's `this`, plumb that `this` through the workflow runtime so the step body sees the correct receiver. - Workflow mode wraps the step proxy with `.bind(this)`, so invoking the proxy captures the caller's `this` as `thisVal` on the queue item. - Step mode hoists the body as a regular `function` (not an arrow) so the runtime's `stepFn.apply(thisVal, args)` rebinds `this` inside the hoisted body. Detection only fires for arrows, since arrows inherit `this` lexically. Nested non-arrow functions/methods/getters/setters introduce their own `this`, so the detector stops at those boundaries. The runtime already supported `thisVal` for instance-method steps; this PR is purely a compiler change to feed the existing pipeline. Caveat: capture works at runtime only when the captured value is serializable across the workflow->step boundary (i.e. the enclosing class implements `WORKFLOW_SERIALIZE`/`WORKFLOW_DESERIALIZE`). Refs vercel/workflow#1865 * Address PR review: preserve step proxy metadata + tighter `this` detection - core: Override `.bind` on step proxies so the bound function retains `stepId` and `__closureVarsFn`. Without this, a bound proxy that flows through workflow serialization (e.g. as a step argument) would be treated as a non-serializable plain function by `getStepFunctionReducer`. - swc-plugin: Detector now also walks `arrow.params` so `this` references in default values / destructuring initializers (e.g. `(x = this.foo) => ...`) trigger the `.bind(this)` path. - swc-plugin: Class bodies inside the arrow body are now treated as `this`-binding boundaries — `this` inside class field initializers, methods, etc. is bound to the class instance, not the outer arrow. The detector still walks `extends` clauses and computed property keys because those are evaluated in the surrounding scope. - spec.md: Sharpen the note about `this` in step bodies — it's syntactically allowed but only meaningful for instance-method steps and lexical-`this` arrow steps; other shapes compile but `this` will be whatever the caller of the step proxy passes. - Add `lexical-this-detector-edge-cases` fixture covering both the default-param positive case and the inner-class false-positive guard. - Strengthen the runtime test to assert `stepId` / `__closureVarsFn` survive `.bind(...)`. * [swc-plugin] Fix `arguments` closure-var capture; drop dead `this`/`arguments` checks - Add `arguments` to `is_global_identifier` so it's not captured as a closure variable. Previously a nested `function`-form step like function step() { 'use step'; return arguments[0]; } was hoisted with `const { arguments } = ...` (a strict-mode syntax error) and the body's `arguments[0]` resolved against the destructured binding instead of the function's intrinsic `arguments` object. - Remove dead `ForbiddenExpression` checks for `this` and `arguments` in `visit_mut_this_expr` / `visit_mut_ident`. The `'use step'` / `'use workflow'` directives are stripped during the module-level traversal before children are visited, so `in_step_function` / `in_workflow_function` are never observed as true here in practice. The existing `step-with-this-arguments-super` fixture explicitly documents that all three identifiers are allowed in step bodies. - Tighten the spec note about `arguments` accordingly: it works in `function`-form steps (reflecting positional args) but is not captured for arrow-form steps; use `...args` for that case. - Add `nested-step-arguments` fixture pinning down the new behavior. Signed-off-by: Nathan Rajlich <n@n8.io> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Nathan Rajlich <n@n8.io> |
||
|
|
478a9c7618 |
Generate local gitignore when using public workflow manifests (#1683) (#2085)
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Peter Wielander <peter.wielander@vercel.com> Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
05bf938983 |
Fix Next workbench cached outputs (#2071) (#2072)
Signed-off-by: JJ Kasper <jj@jjsweb.site> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
4bc53fc7a9 |
Remove instrumentation from workbench (#1959) (#1963)
* Remove instrumentation from workbench * bump Signed-off-by: JJ Kasper <jj@jjsweb.site> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
bb910f2571 |
[backport] [workbench] Add TanStack Start workbench (#1875, #1907) (#1914)
* [workbench] Add TanStack Start workbench and tests (#1875) (cherry picked from commit |
||
|
|
077eb2a0ad |
[backport] Bump vite + enable step source-map assertions for vite local dev (#1919)
* Bump vite (#1827) * test: enable step source-map assertions for vite local dev (#1862) * test: enable step source-map assertions for vite local dev Vite ^7.3.2 (bumped in #1827) preserves step bundle source maps in dev mode, so stack traces now contain original file paths. Update hasStepSourceMaps() so vite returns true in local dev and stays false only in local prod, fixing the consistently failing 'basic step error' and 'cross-file step error' e2e tests. * chore: drop body from empty changeset * address review: drop redundant vite local-prod guard The default `!DEV_TEST_CONFIG` fall-through already returns false for vite local prod, so the vite-specific guard is dead code. Just remove the vite block entirely now that vite local dev matches the default 'has source maps' behavior. --------- Co-authored-by: Karthik Kalyan <105607645+karthikscale3@users.noreply.github.com> |
||
|
|
fdac33cda6 |
Move swc-playground-wasm into workbench/swc-playground (#1704) (#1772)
* move swc-playground-wasm into workbench/swc-playground Move the Rust/WASM source from packages/swc-playground-wasm into workbench/swc-playground/wasm/ so it is no longer built as part of the packages/* turbo filter in CI. The WASM build now runs as part of the playground's own prebuild/dev scripts, avoiding Rust toolchain failures in unrelated CI jobs. * fix: ensure default rustup toolchain in wasm build The Vercel build environment has rustup installed but no default toolchain configured. Add a check for this and install stable if needed. Also check for VERCEL env var in addition to CI. * fix: ensure cargo bin dir is in PATH on Vercel After cargo install wasm-pack, the binary lives in $CARGO_HOME/bin which may not be in PATH on the Vercel build environment. Always add the cargo bin directory to PATH after ensuring the toolchain. Co-authored-by: Nathan Rajlich <n@n8.io> |
||
|
|
7faedb7967 |
fix(ai): preserve provider tool identity across step boundaries (#1663)
* fix(ai): preserve provider tool identity across step boundaries
Port of vercel/ai#14229. Provider tools (e.g. anthropic.tools.webSearch)
were converted to plain function tools in toolsToModelTools, stripping
type, id, and args fields. This caused providers like Anthropic Gateway
to not recognize them as provider-executed tools.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* DCO Remediation Commit for Gregor Martynus <39992+gr2m@users.noreply.github.com>
I, Gregor Martynus <39992+gr2m@users.noreply.github.com>, hereby add my Signed-off-by to this commit:
|
||
|
|
f5d2aef58f |
Add serde compliance tooling and improve custom class serialization DX (#1552)
* Add serde compliance tooling and update skill documentation - Add serde compliance checker library to @workflow/builders - Add build-time warnings for serde classes with Node.js imports in workflow bundle - Add 'workflow transform' CLI command for inspecting SWC output - Implement 'workflow validate' CLI command with serde compliance checks - Add serde analysis panel to SWC playground - Update workflow skill with custom class serialization documentation * Fix --json + --strict: use process.exitCode so JSON output is returned before exit * Address code review feedback - Fix --strict exit code: honor process.exitCode in BaseCommand.finally() - Remove unused --module-specifier flag from transform command - Add missing Node.js builtins (e.g. test) to playground detection list - De-dupe build-time serde warnings by grouping identical issues across classes - Make Serde Analysis panel collapsible like the output panels * Use .gitignore for validate file discovery; fix changeset wording |
||
|
|
d8aaf27c79 | [core] Enforce single ALS context to protect against duplicate module and caching issues (#1591) | ||
|
|
7e33c62736 |
Rename 'Workflow Development Kit' / 'DevKit' to 'Workflow SDK' (#1595)
* Rename 'Workflow Development Kit' / 'DevKit' to 'Workflow SDK' across docs, code, and config
Follow-up to
|
||
|
|
d119c740d0 | [builders] Fix import.meta.url missing when using CJS (#1509) | ||
|
|
781d64b737 |
Move SWC playground to client-side WASM and add Monaco type intellisense (#1553)
* Move SWC playground transform from server action to client-side WASM
Create a new swc-playground-wasm crate that bundles swc_ecma_parser,
swc_ecma_codegen, and the swc_workflow transform visitor into a single
WASM binary via wasm-bindgen/wasm-pack. This runs the code
transformation entirely in the browser, eliminating the server action
round-trip and serverless function cold starts on every keystroke.
- New packages/swc-playground-wasm Rust crate targeting wasm32-unknown-unknown
- Exposes transform() and transformAll() functions via wasm-bindgen
- Client loads WASM + JS glue from public/wasm/ as static assets
- Removed @swc/core and @workflow/swc-plugin server-side dependencies
- Removed serverExternalPackages/outputFileTracingIncludes Next.js config
- Added WASM loading indicator and error state in the UI
- Reduced transform debounce from 500ms to 300ms (no network latency)
* Add missing extends key to swc-playground-wasm turbo.json
* Fix build: ensure rustup is available for wasm32-unknown-unknown target
The Vercel build environment has a system Rust without rustup, so
the wasm32-unknown-unknown target can't be managed. Now the build
script checks for rustup specifically (not just cargo) and installs
it when missing, matching the pattern in swc-plugin-workflow/build.js.
* Add workflow type definitions to Monaco editor for intellisense
Auto-generate type declarations from built .d.ts files of workflow,
@workflow/core, @workflow/errors, @workflow/world, and @workflow/utils
packages. Register them with Monaco's TypeScript language service via
addExtraLib() so imports like 'workflow' resolve with full types,
eliminating red squiggles and enabling autocomplete/hover info.
* Fix Monaco type resolution: register root index.d.ts for each package
Monaco's NodeJs module resolution looks for index.d.ts at the package
root, not just in dist/. Register the main entry .d.ts content at both
paths (dist/ and root) so bare imports resolve with full type info.
Also remove the 2307 diagnostic suppression so non-existent imports
correctly show errors.
* Fix Monaco tooltip overflow by enabling fixedOverflowWidgets
* Fix hydration mismatch, Monaco type resolution paths, and WASM init warning
- Fix hydration mismatch: gate Reset button disabled state on isHydrated
so server and client render consistently during hydration
- Fix Monaco types: use bare node_modules/ paths instead of file:///
URIs for addExtraLib, which is what Monaco's NodeJs resolver expects
- Remove virtual package.json entries (Monaco doesn't use them)
- Fix wasm-bindgen init deprecation: pass object { module_or_path }
instead of bare string argument
* Fix Monaco module resolution: set model URI to file:/// and match addExtraLib paths
Monaco's TypeScript NodeJs resolver needs the editor model and the
addExtraLib entries to share the same URI scheme. Set the input editor
model path to file:///src/input.tsx and register all type declarations
at file:///node_modules/... so resolution of bare imports like
'workflow' correctly finds the virtual node_modules.
Also register a root index.d.ts for packages like @workflow/world that
lack an explicit 'types' field in their package.json exports.
* Use declare module ambient declarations for reliable Monaco type resolution
Replace the virtual node_modules filesystem approach with declare module
ambient declarations. This is the standard approach used by TypeScript
Playground and StackBlitz — it works regardless of Monaco's internal URI
scheme and module resolution quirks.
The generation script now:
- Registers all .d.ts files at file:///node_modules/<pkg>/dist/... paths
- Generates a global ambient declarations file with declare module blocks
that map bare import specifiers to their .d.ts entry points
- Includes @workflow/serde and workflow sub-exports (api, errors, observability)
- Supports configurable sub-export mappings per package
* Inline types into declare module blocks for full Monaco type support
Replace the export-from-file approach with fully inlined declare module
blocks. The script now reads each .d.ts entry point, recursively inlines
all relative imports, strips external import statements (resolved via
other declare module blocks), and produces a single ambient declarations
string.
This correctly handles:
- unique symbol exports (@workflow/serde)
- cross-package re-exports (workflow re-exporting from @workflow/core)
- JSDoc comments preserved for hover documentation
- Sub-path exports (workflow/api, workflow/errors, workflow/observability)
- Added @workflow/serde package
* Add workspace packages as dependencies so Turbo builds their types
The generate-monaco-types script reads .d.ts files from the built
dist/ directories of workflow, @workflow/core, @workflow/errors, etc.
On Vercel, Turbo only builds explicit dependencies — without these
workspace references, the packages were never built and the dist/
directories didn't exist, resulting in 0 type modules generated.
* Add @types/node declarations to Monaco editor
Register all @types/node .d.ts files via addExtraLib so Node.js
built-in modules (crypto, fs, path, etc.) are available in the
playground editor with full type information.
* Collect @types/node .d.ts files recursively to include subpath modules
The previous non-recursive scan missed subdirectory files like
fs/promises.d.ts, stream/web.d.ts, dns/promises.d.ts, etc., causing
'Cannot find module node:fs/promises' errors.
|
||
|
|
cdf90d5a38 |
Rename Workflow DevKit to Workflow SDK, remove beta badge, add tweet wall (#1541)
* Rename Workflow DevKit to Workflow SDK, remove beta badge, add tweet wall - Rename "Workflow DevKit" to "Workflow SDK" across all files (~108 files) - Rename standalone "WDK" references to "Workflow SDK" - Remove beta badge from homepage hero - Add tweet wall component to homepage with 4 builder testimonials Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Rename Workflow DevKit to Workflow SDK, remove beta badge, add tweet wall - Rename "Workflow DevKit" to "Workflow SDK" across all files (~108 files) - Rename standalone "WDK" references to "Workflow SDK" - Remove beta badge from homepage hero - Add tweet wall component to homepage with 4 builder testimonials Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Harpreet Arora <harpreet.txt@gmail.com> * Address review: fix missed trigger phrase renames and bump skill versions - Rename "workflow devkit" to "workflow sdk" in trigger phrases for both skill files - Bump workflow-init SKILL.md version to 1.1 - Bump workflow SKILL.md version to 1.5 - Note: CLAUDE.md is a symlink to AGENTS.md, already renamed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Harpreet Arora <harpreet.txt@gmail.com> * link correct tweet --------- Signed-off-by: Harpreet Arora <harpreet.txt@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Karthik Kalyanaraman <karthik.kalyanaraman@vercel.com> |
||
|
|
52db376c39 |
Support projects with Node.js step dependencies in vitest plugin (#1524)
Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
672d9195a4 |
Fix step/workflow not found errors to fail gracefully instead of queue retry (#1452)
* feat: enhance error handling for missing workflow functions Slack-Thread: https://vercel.slack.com/archives/C09G3EQAL84/p1773856370214769?thread_ts=1773856370.214769&cid=C09G3EQAL84 Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * fix: update step not found handling to match FatalError pattern Move step function validation after step_started and call step_failed directly if not found. Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> * changes Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * feat: add StepNotRegisteredError and WorkflowNotRegisteredError semantic errors Introduce dedicated error types for when step/workflow functions are not registered in the current deployment, replacing generic WorkflowRuntimeError. These are infrastructure errors (not user code errors) with proper error slugs, docs pages, and a new FUNCTION_NOT_REGISTERED error code. Step not found fails the step (like FatalError) so the workflow can handle it gracefully. Workflow not found fails the run. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review comments - Remove FUNCTION_NOT_REGISTERED error code, use RUNTIME_ERROR instead - Use .is() instead of instanceof for WorkflowRuntimeError check in runtime.ts - Remove non-working example from WorkflowNotRegisteredError docs (custom errors not serialized yet) - Update all references from FUNCTION_NOT_REGISTERED to RUNTIME_ERROR Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add e2e tests for step/workflow not registered errors and fix docs typecheck E2E tests: - WorkflowNotRegisteredError: start a run with a fake workflowId, verify the run fails with RUNTIME_ERROR - StepNotRegisteredError (caught): workflow catches the step failure, verify workflow completes and step is marked failed - StepNotRegisteredError (uncaught): verify the run fails when workflow doesn't catch the error Step not registered is tested by manually invoking useStep with a non-existent step ID in the workflow VM — this is the same pattern the SWC transform generates for real step calls. Also fix docs typecheck by using declare/\@setup pattern instead of \@skip-typecheck for code samples. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: cast globalThis to any for Symbol index access in e2e workflow TypeScript's strict mode doesn't allow using a symbol to index globalThis. Cast to any since this runs in the workflow VM where the symbol is defined. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: classify WorkflowNotRegisteredError as RUNTIME_ERROR The .is() check uses name-based matching, so WorkflowNotRegisteredError (name='WorkflowNotRegisteredError') doesn't match WorkflowRuntimeError.is(). Add explicit check in classifyRunError so the error code is RUNTIME_ERROR instead of USER_ERROR. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use instanceof for WorkflowRuntimeError checks, improve docs Address PR review feedback: 1. Revert .is() checks back to instanceof WorkflowRuntimeError in runtime.ts and classify-error.ts. instanceof catches all subclasses (current and future), which is the correct behavior for these catch blocks. 2. Remove duplicated try/catch example from step-not-registered-error API reference (troubleshooting page already has it). 3. Add Callout in API reference docs clarifying that .is() works in server-side Node.js code but not inside "use workflow" functions where errors arrive deserialized from the event log. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * changes Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: v0 <v0[bot]@users.noreply.github.com> Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
ef4ca00b77 |
chore: bump next to 16.2.1 and fix deferred build (#1496)
* chore: bump next to 16.2.1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: run deferred Next dev e2e assertions on stable Bump Next.js to 16.2.1 in docs and swc-playground and update lockfile. * fix(next): copy all deferred step sources for step-mode transforms --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: JJ Kasper <jj@jjsweb.site> Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
1b4a3abbf1 |
chore: bump nitropack to ^2.13.1 (#1501)
* chore: bump nitropack to ^2.13.1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * c Signed-off-by: Peter Wielander <mittgfu@gmail.com> --------- Signed-off-by: Peter Wielander <mittgfu@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Peter Wielander <mittgfu@gmail.com> |
||
|
|
8366fae439 | chore: bump nuxt ecosystem dependencies (#1500) | ||
|
|
362978be55 |
chore: bump sveltekit ecosystem (#1498)
Bump SvelteKit-related dependencies in the sveltekit workbench: - @sveltejs/kit: ^2.43.2 -> ^2.55.0 - svelte: ^5.39.5 -> ^5.54.0 - @sveltejs/adapter-node: ^5.4.0 -> ^5.5.4 - @sveltejs/adapter-vercel: ^6.1.1 -> ^6.3.3 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
61d40ff4cf |
chore: bump express and fastify in workbenches (#1499)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
730c95bfb9 |
chore: bump @nestjs/* to ^11.1.17 (#1497)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
db394c43db |
chore: bump hono to ^4.12.8 (#1495)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
5010ebe7c5 |
fix(next): stabilize deferred canary e2e in nextjs workbenches (#1468)
* Revert "[ci] Fix nextjs lazy disocvery impacting e2e tests, disable experimental DurableAgent tests (#1400)"
This reverts commit
|
||
|
|
823f58e5c6 |
Revert "Add support for calling start() inside workflow functions (#1133)" (#1475)
This reverts commit
|
||
|
|
e889860984 |
Add support for calling start() inside workflow functions (#1133)
* Add support for calling `start()` directly inside workflow functions Enable `start()` to work in workflow context by routing through an internal step (`__workflow_start`), reusing existing step infrastructure with no new event types or server changes needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address PR review feedback - Use typeof check instead of truthiness for WORKFLOW_START symbol - Validate start() options in workflow context (reject unsupported options like world) - Set maxRetries=0 on __workflow_start step to prevent orphaned child runs - Add unit tests for createStart factory (6 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Make Run serializable in workflow context with step-backed methods - Add Run serialization via __serializable marker + custom Run reducer/reviver in the serialization module (avoids SWC plugin injecting class-serialization imports) - Create WorkflowRun class factory (packages/core/src/workflow/run.ts) with step-backed methods: cancel(), status, returnValue, workflowName, createdAt, startedAt, completedAt, exists - Register 8 built-in steps (__run_cancel, __run_status, etc.) in step-handler - Update __workflow_start to return full Run object (serialized → WorkflowRun in VM) - Update createStart to pass through step result directly - Update docs to reflect full Run support in workflow context Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix start() in workflow VM by delegating from api-workflow stub The workflow VM loads api-workflow.ts (via the "workflow" export condition) which stubs all runtime functions. The start stub needs to check for the injected WORKFLOW_START symbol and delegate to it, otherwise start() throws "doesn't allow this runtime usage" in the workflow context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR review: fix stale WORKFLOW_SERIALIZE comments and register Run in host registry - Update comments in step-handler.ts and start.ts to reference the actual serialization mechanism (Run reducer with __serializable marker) instead of the stale WORKFLOW_SERIALIZE reference - Register Run class in the host's class registry from step-handler.ts so the Run reviver can deserialize Run/WorkflowRun instances in step context Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add docs for recursive/repeating workflows and deploymentId: "latest" - Document using start() for self-chaining workflows to avoid large event logs - Add examples for batch processing and cron-like repeating patterns - Document deploymentId: "latest" option with type safety warning - Update skill file with same patterns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Return full Run object from startFromWorkflow e2e workflow Update the e2e workflow to return the childRun object directly instead of just childRun.runId, exercising Run serialization across the workflow boundary. Update e2e test assertions to match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add recursive fibonacci e2e test for start() in workflow Demonstrates recursive workflow composition: fibonacciWorkflow starts new instances of itself via start() + Promise.all to compute fib(6)=8, fanning out across independent workflow runs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Move Run method steps to builtins with "use step" directives Refactor: instead of manually registering Run method steps via registerStepFunction in step-handler.ts, define them as proper "use step" functions in builtins.ts with __builtin_ prefix. This leverages the existing SWC plugin infrastructure — functions starting with "__builtin" get stable bare-name step IDs. - Add __builtin_run_{cancel,status,return_value,...} to both builtins files - Use dynamic import() for getRun inside step bodies to avoid pulling Node.js modules into the workflow bundle - Remove manual registerStepFunction calls from step-handler.ts - Update WorkflowRun step references to __builtin_run_* names - Fix step name display in web observability: fall back to raw name instead of "?" for built-in steps that don't follow step//module//fn format - Add fibonacciWorkflow default args for nextjs-turbopack workbench UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Render Run objects as clickable links in web observability UI - Add RunRef type and Run reviver to observabilityRevivers so serialized Run objects are hydrated as RunRef instead of showing raw Uint8Array - Add RunRefInline component (purple badge with run ID) that navigates to the target run on click, matching the StreamRef pattern - Thread onRunClick callback through the component chain: WorkflowTraceViewer → EntityDetailPanel → AttributePanel → DataInspector - Wire up navigation in the web app's run-detail-view - Add startFromWorkflow default args for workbench UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Throw error instead of silent fallback when Run class not in registry Address PR review: the Run reviver now throws if the class isn't found in the registry, instead of silently returning a plain { runId } object that would break the assumption of getting a valid Run instance. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix e2e failures: allow retries on Run getter steps, fix docs code samples - Remove maxRetries=0 from read-only Run getter steps (status, returnValue, workflowName, etc.) — these are safe to retry and need retries when the child workflow hasn't completed within the step timeout. Only cancel keeps maxRetries=0. - Fix docs code samples: use correct import path (workflow/api not workflow), add declare statements for helper functions used in examples. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use standard step//module//function naming for built-in steps Update the SWC plugin's __builtin_ special case to generate proper step//@workflow/core//{name} IDs instead of bare function names. This makes parseStepName work correctly for built-in steps, showing: - StepName: "Run#returnValue" (not "__builtin_run_return_value") - ModuleSpecifier: "@workflow/core" (not the raw function name) Convention: __builtin_Run_cancel → step//@workflow/core//Run#cancel (uppercase prefix + underscore → instance method # notation) - Move __workflow_start to builtins.ts as __builtin_start - Rename __builtin_run_* to __builtin_Run_* for proper # notation - Update WorkflowRun step refs to use full step// IDs - Remove manual registerStepFunction from step-handler.ts - Update SWC spec.md with new naming examples Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove SWC __builtin special case, use standard step naming for builtins Remove the SWC plugin's __builtin_ special case so built-in steps get standard step//{module}@{version}//{fn} IDs like any other step. This makes parseStepName work correctly, showing proper StepName and ModuleSpecifier in observability. The VM reconstructs the same IDs via builtinStepId() which uses the @workflow/core version to build: step//workflow/internal/builtins@{v}//{fn} - Remove __builtin special case from SWC plugin (revert to original) - Add builtinStepId() helper shared by workflow.ts, start.ts, run.ts - Rename Run steps: __builtin_Run_cancel → Run_cancel, etc. - Rename start step: __builtin_start → start - Move start step from manual registerStepFunction to builtins.ts - Keep __builtin_response_* names unchanged (pre-existing) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use static class methods for Run steps to get Run.method naming Refactor Run method steps from standalone functions (Run_cancel) to static methods on a Run class, so the SWC plugin generates step IDs with the standard static method convention: Run.cancel, Run.returnValue, Run.status, etc. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR review: tests, docs warnings, skill fix - Add TODO on Run.returnValue about polling blocking (replace with system hooks once AbortSignal/AbortController PR lands) - Add docs callout warning about returnValue holding workers alive - Fix SKILL.md contradiction that said start() can't be used in workflows - Enhance suspension test to assert step arguments are forwarded - Add WorkflowRun unit tests: serializable marker, runId, registry, delegation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix response builtins: adopt this-serialization from PR #1413 The rebase onto main didn't fully adopt PR #1413's refactor of response builtins to use `this` instead of explicit parameters. The old pattern (resJson(this) wrappers) passed `this` as an argument, but the step functions now expect `this` to be set via method call context. Switch to Object.defineProperties on Request/Response prototypes, matching main's approach. Also document WORKFLOW_PUBLIC_MANIFEST=1 for local e2e testing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address docs review: returnValue polling is temporary, link to start() API ref - Update returnValue warning to note this is a temporary implementation that will be replaced with internal hooks - Replace inline deploymentId: "latest" docs with link to the existing start() API reference which already covers it comprehensively Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix e2e tests: replace collectedRunIds with trackRun API PR #1426 replaced the manual collectedRunIds array with a trackRun() helper. The start() wrapper already auto-tracks, so just remove the manual push calls and add trackRun for the child run. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
afa3931a59 | Add stress benchmarks: 1000-step, data payload, and stream tests (#1214) | ||
|
|
7d535bd680 |
test: fix flaky promiseAnyWorkflow e2e test (#1436)
* test: fix flaky promiseAnyWorkflow e2e test Widen the delay gap between step b (100ms) and step c (6000ms) so that b reliably wins the Promise.any race regardless of server/network jitter. Previously the gap was only 1s vs 3s which was insufficient in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix flaky promiseAnyWorkflow e2e test Widened the step delay gap to stabilize the test. Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
3f2ebb2f90 |
test: validate sleep() works correctly inside loops (#1415)
* test: add e2e test proving sleep() works correctly inside loops Validates a user-reported concern that sleep() inside a loop with step calls fires all iterations instantly. The test confirms sleep is honored on replay — 3 iterations with 3s sleeps take ~6s+ total elapsed time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review comments - Fix misleading "3s per iteration" comment to "3s between iterations" - Update test comment to reflect 2.5s jitter-tolerant threshold - Add per-iteration delta assertions to ensure each individual sleep fires Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Apply suggestion from @TooTallNate Co-authored-by: Nathan Rajlich <n@n8.io> Signed-off-by: Peter Wielander <mittgfu@gmail.com> --------- Signed-off-by: Peter Wielander <mittgfu@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Peter Wielander <mittgfu@gmail.com> Co-authored-by: Nathan Rajlich <n@n8.io> |
||
|
|
2c80ec7217 | DurableAgent: various compatibility fixes (#1385) | ||
|
|
1959f2b8e7 |
Use relative import for _workflows in webpack trigger-pages (#1405)
esbuild's discovery build cannot resolve @/ path aliases when the tsconfig does not have baseUrl set. Use a relative import so the discovery can trace through _workflows.ts to find all workflow files. |
||
|
|
74aea7b0af |
Add DurableAgent compat tests, e2e tests, and migrate to AI SDK v6 (#1362)
* Add DurableAgent compat tests, e2e agent tests, and migrate to AI SDK v6 - Port ToolLoopAgent test suite as DurableAgent compatibility spec (34 tests, all expected to fail — each maps to a feature gap to implement) - Add e2e workflow definitions using mock LLM providers (no API keys needed) - Add e2e test file for DurableAgent workflows - Migrate all AI SDK types from V2 to V3 (LanguageModelV2 → V3, etc.) - Drop AI SDK v5 support: ai peer dep ^5||^6 → ^6, @ai-sdk/provider ^2||^3 → ^3 - Update ai catalog version from 5.0.104 to 6.0.116 - Simplify CompatibleLanguageModel to just LanguageModelV3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR review feedback - Remove providerExecuted guard on tool-result stream parts (V3: all tool-results are provider-executed by definition) - Remove providerExecuted spread from tool-output-available UI chunks - Replace inline MockLanguageModelV3 with import from ai/test (works without msw in AI SDK v6) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove streamTextIterator mock from compat tests, use it.fails for gaps Tests now exercise the real DurableAgent code path instead of mocking the core iterator. 5 tests pass (features DurableAgent already has), 29 are marked it.fails() for known API gaps that will alert when fixed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Implement Tier 1+2 gaps, add @workflow/ai/test mock provider, wire e2e in CI DurableAgent API additions: - Add `instructions` (string | SystemModelMessage | SystemModelMessage[]) as alias for deprecated `system` on constructor - Add `onStepFinish` and `onFinish` on constructor, merged with stream options (constructor first, then stream — matching ToolLoopAgent) - Add `timeout` on stream options (converted to AbortSignal) - Add `text`, `finishReason`, `totalUsage` to onFinish event Test infrastructure: - Add @workflow/ai/test export with `mockModel()` wrapper that wraps MockLanguageModelV3 from ai/test as an async step function - E2e workflows now use mockModel() + convertArrayToReadableStream from @workflow/ai/test instead of inline V2 mock models - Add e2e-agent.test.ts to test:e2e script so it runs in CI - Flip 6 compat tests from it.fails → it (now passing) Score: 11 passing / 23 it.fails (was 5/29) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove e2e agent tests — mock models can't serialize across step boundary The workflow runtime serializes step arguments, and function closures (like mock model doStream callbacks) aren't serializable. Mock models only work in unit tests where 'use step' is a no-op. Real e2e agent tests would need either a mock HTTP server or real provider credentials. Also removes 'use step' from mockModel wrapper (closures aren't serializable) and reverts test:e2e script and example workbench dep. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add working e2e agent tests with mock model step factories Mock model factories use the same 'use step' pattern as real providers (anthropic, openai). Closure variables are bound to locals at the step body level so the SWC plugin detects them via __private_getClosureVars. All 4 e2e tests pass against local dev server: - agentBasicE2e: text response (11s) - agentToolCallE2e: single tool call + text (11s) - agentMultiStepE2e: 3 sequential tool calls (12s) - agentErrorToolE2e: FatalError recovery (11s) Also adds e2e-agent.test.ts to test:e2e script for CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use @workflow/ai/test package imports for e2e mock models Split mock provider into two files to work around SWC constructor closure bug: mock-create.ts has the model creation logic, mock.ts has the 'use step' wrappers that capture only serializable args (strings, plain object arrays). Exports mockTextModel(text) and mockSequenceModel(responses) — same 'use step' pattern as real providers (anthropic, openai, etc.). E2e workflows now import directly from @workflow/ai/test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Simplify mock provider, add comprehensive e2e tests for all features + gaps Mock provider: - Replace mock-create.ts with mock-function-wrapper.ts that simply wraps MockLanguageModelV3 constructor in a function (SWC class closure bug) - mockTextModel/mockSequenceModel use mockProvider() from wrapper file - Bind closure vars at step body level (_text = text) for SWC detection - Fix AbortController not available in workflow VM sandbox E2e tests (13 total, all passing): - Core: basic text, tool call, multi-step, error recovery (4) - Callbacks: onStepFinish constructor+stream, onFinish constructor+stream (2) - Features: instructions, timeout (2) - GAPs documented: onStart, onStepStart, onToolCallStart, onToolCallFinish, prepareCall (5 — complete but callbacks not called) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add tool approval (needsApproval) gap tests, fix SWC closure var binding Unit tests: 2 new it.fails() tests for tool approval - needsApproval: true should pause agent (pending tool call, no result) - needsApproval as function should receive tool input E2e tests: 1 new test for tool approval gap - Documents that needsApproval is currently ignored (tool executes anyway) Also fixes: - Bind closure vars at step body level in mock provider (_text = text, _responses = responses) so SWC plugin detects them - Guard AbortController usage in workflow VM (not available in sandbox) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add default args for agent e2e workflows in UI definitions The nextjs-turbopack UI calls workflows with hardcoded default args. Without these entries, agent workflows were called with no args, causing prompt=undefined → ModelMessage validation failure. Also removes debug logging. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add DurableAgent chat UI with tools, update docs for AI SDK v6 Chat UI: - Tab-based layout with Workflows (existing) and DurableAgent Chat tabs - Chat powered by DurableAgent + WorkflowChatTransport + ai-elements - Tools: getWeather (fake data), calculate (math expressions) - Uses createUIMessageStreamResponse for proper stream serialization - Reconnect route at /api/chat/[runId]/stream - ai-elements components: conversation, message, prompt-input, tool - onStepFinish + onFinish callbacks with console logging Docs (AI SDK v6 migration): - system → instructions in DurableAgent constructor examples (10 places) - LanguageModelV2Prompt → LanguageModelV3Prompt in type references (3 places) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix tool rendering, add reasoning support, model picker, observability links - Fix tool part rendering: use `input`/`output` props (not `args`/`result`) and `tool-{name}` part type (AI SDK v6 format) - Add reasoning support for Opus 4.5 via providerOptions - Model picker: Haiku 4.5, Sonnet 4, Opus 4.5 (reasoning), GPT-5.2, GPT-5.3 - Fix observability links: localhost:3456 for local, Vercel dashboard for prod - Add suggestions above prompt input - Add MessageParts component handling text, tool, reasoning, step-start - Add loading spinner for submitted state - Update docs: system → instructions, LanguageModelV2Prompt → V3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix tool output rendering: use input/output props on ToolInput/ToolOutput Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix: Documentation for `PrepareStepInfo` and `PrepareStepResult` interfaces references the obsolete `LanguageModelV2` type while the codebase has fully migrated to `LanguageModelV3`. This commit fixes the issue reported at docs/content/docs/ai/message-queueing.mdx:36 **Bug explanation:** The codebase migrated from AI SDK V2 to V3. In `packages/ai/src/agent/types.ts`, `CompatibleLanguageModel` is defined as `LanguageModelV3` (from `@ai-sdk/provider`). The actual TypeScript interfaces in `packages/ai/src/agent/durable-agent.ts` use `string | (() => Promise<CompatibleLanguageModel>)` which resolves to `LanguageModelV3`. However, the documentation in `docs/content/docs/ai/message-queueing.mdx` at lines 36 and 43 still referenced `LanguageModelV2` for the `model` field in both `PrepareStepInfo` and `PrepareStepResult`. This is inconsistent because: 1. The `messages` fields in the same interfaces were correctly updated to `LanguageModelV3Prompt` 2. The actual source code uses `LanguageModelV3` via `CompatibleLanguageModel` 3. There is no `LanguageModelV2` type anywhere in the codebase This would mislead developers reading the documentation into using the wrong type. **Fix explanation:** Changed both `LanguageModelV2` references to `LanguageModelV3` on lines 36 and 43 of the documentation file, matching the actual codebase types. Verified no other stale `LanguageModelV2` references remain in the docs directory. Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: pranaygp <pranay.gp@gmail.com> * Fix instructions tests: flip from it.fails to it, update snapshots The 3 instructions tests (string, SystemModelMessage, array) now pass. The snapshots include the assistant reply message from the agent loop, which is a behavioral difference from ToolLoopAgent (DurableAgent captures the prompt after the full loop iteration). Score: 14 passing / 22 it.fails (was 11/25) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix getReadable call: pass startIndex as options object Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix docs type errors and turbopack build Docs: - Add await to convertToModelMessages() calls (now async in AI SDK v6) - Change LanguageModelV3Prompt → ModelMessage[] in type references - Change LanguageModelV3 → LanguageModel in PrepareStepInfo - Update docs-globals.d.ts convertToModelMessages return type - Add LanguageModel to import inference map DurableAgent: - Update OutputSpecification to match AI SDK v6 Output interface (type→name, parsePartial→parsePartialOutput, parseOutput→parseCompleteOutput, responseFormat now PromiseLike) Turbopack build: - Remove streamdown plugins from MessageResponse (plugins prop API changed in streamdown 2.4.0, causing type mismatch in CI) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix pnpm-workspace.yaml: use double quotes for catalog entries The stage-workbench-with-tarballs.mjs script only strips double quotes when parsing catalog keys. Single-quoted @-scoped entries (e.g., '@types/node') weren't matched, causing "unresolved catalog dependencies" errors in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CI build failures, add changeset - Remove streamdown plugins from reasoning.tsx (same CI type mismatch) - Cast ToolHeader type prop and WorkflowChatTransport to fix type errors - Fix pnpm-workspace.yaml single→double quotes for staging script - Add minor changeset for @workflow/ai (breaking: AI SDK v6 migration) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Sync webpack workbench with turbopack: add chat UI deps and symlinks - Symlink app-shell.tsx, chat-client.tsx, agent_chat workflow, chat API routes into nextjs-webpack - Add matching deps: streamdown, @streamdown/*, shiki, cmdk, nanoid, motion, @radix-ui/react-use-controllable-state Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix circular symlink: restore chat-client.tsx as real file in turbopack The previous commit accidentally converted turbopack's chat-client.tsx into a circular symlink pointing to itself. Webpack's symlink to it then couldn't resolve, breaking both builds on Vercel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add missing deps to webpack: use-stick-to-bottom, radix-ui, @vercel/blob Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix model picker: merge body params via prepareSendMessagesRequest WorkflowChatTransport sends { messages } by default, ignoring the body option from ChatRequestOptions. Use prepareSendMessagesRequest to merge { messages, ...body } so the model selection reaches the API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix WorkflowChatTransport: forward body/headers from ChatRequestOptions The transport hardcoded body: undefined when calling prepareSendMessagesRequest, so extra body params (like model selection) from sendMessage({ body: { model } }) were silently dropped. Now forwards options.body and options.headers to both prepareSendMessagesRequest and the default request body. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix model IDs: use real AI Gateway model names gpt-5.2 and gpt-5.3 don't exist in the AI Gateway. Replace with gpt-4o and gpt-4o-mini. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use correct AI Gateway model IDs: Opus 4.5, GPT-5.2, GPT-5.3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Enable reasoning for all model providers - Anthropic: thinking.type='enabled' with 10k token budget - OpenAI: reasoningEffort='high' - Instructions kept for all models (no longer conditionally removed) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix OpenAI reasoning: use 'medium' effort (GPT-5.3 doesn't support 'high') Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address all PR review comments - Change changeset from minor to patch (repo convention) - Use ?? instead of || for system/instructions fallback - Clean up timeout: store ID, clearTimeout in finally, { once: true } listeners - Update class docstring example to use instructions - Map unrecognized finish reasons to 'other' with validation - Fix duplicate test, align assertion for unrecognized type - Support ^ exponentiation in calculate tool - Remove debug console.log from chat client - Fix ReactNode/ComponentProps type imports in UI components - Remove unused MockLanguageModelV3 re-exports from mock.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove accidentally created empty mock2.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Change changeset back to minor for breaking AI SDK v6 migration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix agent e2e tests: add Vercel world setup for CI The agent e2e tests only configured the local filesystem world but not the Vercel world backend. On CI (Vercel prod tests), this caused VercelOidcTokenError because the world wasn't initialized. Now matches the setup pattern from e2e.test.ts: configures Vercel world with OIDC token and project config from CI environment variables. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Deduplicate e2e test utilities: extract shared code to utils.ts Extract manifest fetching, workflow lookup, world setup, and types into shared utils.ts. Both e2e.test.ts and e2e-agent.test.ts now import from the same source, eliminating ~200 lines of duplication. Shared utilities: - WorkflowManifest interface - fetchManifest() with caching - getWorkflowMetadata() with retry and fallback - setupWorld() handling local/Vercel/Postgres backends Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix missing deploymentUrl args in e2e.test.ts after utils refactor Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add @workflow/ai dep to all workbenches for agent e2e tests All workbenches now have @workflow/ai as a dependency and the 100_durable_agent_e2e.ts symlink, so agent e2e tests run everywhere. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix missing imports in e2e.test.ts: add fetchManifest and sleep The utils refactor removed these imports but they're still used: - fetchManifest: used in stepFunctionAsStartArgWorkflow test - sleep (setTimeout): used in webhookWorkflow test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> |
||
|
|
b8865edaea |
swc-playground: resizable panels, collapsible outputs, vim mode, and dep cleanup (#1370)
* swc-playground: add resizable panels, collapsible outputs, vim mode toggle, and clean up deps - Add resizable split between input editor and output panels using react-resizable-panels - Add collapsible accordion-style output views (workflow/step/client) with chevron toggles - Add vim keybindings toggle (persisted to localStorage) using monaco-vim - Add shadcn Switch and Resizable UI components - Remove unused individual @radix-ui/react-* packages in favor of unified radix-ui package - Remove unused bulk shadcn init dependencies (recharts, sonner, vaul, zod, cmdk, etc.) * Address PR review: fix vim mode race condition, mount timing, and add aria attributes - Add cancellation flag to async monaco-vim import to prevent stale callbacks - Add .catch() for import failure handling - Track editor mount state so vim mode initializes when persisted as enabled - Add aria-expanded and aria-controls to collapsible output panel toggle buttons |
||
|
|
7df13854f8 |
fix: separate infrastructure vs user code error handling (#1339)
* fix: separate infrastructure vs user code error handling in runtime and step handler Transient network errors (ECONNRESET, etc.) during infrastructure calls (event listing, event creation) were caught by a shared try/catch that also handles user code errors, incorrectly marking runs as run_failed or steps as step_failed instead of letting the queue redeliver. - runtime.ts: Move infrastructure calls outside the user-code try/catch so errors propagate to the queue handler for automatic retry - step-handler.ts: Same structural separation — only stepFn.apply() is wrapped in the try/catch that produces step_failed/step_retrying - helpers.ts: Add isTransientNetworkError() and update withServerErrorRetry to retry network errors in addition to 5xx responses - helpers.test.ts: Add tests for network error detection and retry * add changeset * remove withServerErrorRetry and isTransientNetworkError Redundant with undici RetryAgent which already handles 5xx retries and network error retries at the HTTP dispatcher level. * address review feedback: move getEncryptionKeyForRun out of user-code try/catch, re-add 5xx/410 safety net in step handler, relax e2e test assertion * remove serverError5xxRetryWorkflow e2e test This test validated withServerErrorRetry's in-process retry behavior, which was removed. Queue-level retry with process-scoped fault injection is unreliable across serverless instances and too slow for e2e timeouts. * remove serverError5xxRetryWorkflow and fault injection helpers from e2e workflows * remove inline comment about deleted test |
||
|
|
d72c82220f |
Fix bug where the SWC compiler bug prunes step-only imports in the client-mode transformation
* first pass * fix failing test * Update changeset to fix SWC compiler issue Fix bug where the SWC compiler bug prunes step-only imports in the client-mode transformation Signed-off-by: Nathan Rajlich <n@n8.io> * DCO Remediation Commit for nathancolosimo <nathancolosimo@gmail.com> I, nathancolosimo <nathancolosimo@gmail.com>, hereby add my Signed-off-by to this commit: |
||
|
|
54758f30ab |
Remove hard-coded VERCEL_DEPLOYMENT_KEY from nextjs-turbopack workbench (#1319)
Vercel now automatically sets VERCEL_DEPLOYMENT_KEY to a unique value for every deployment, so the hard-coded value is no longer necessary. |
||
|
|
9781afb490 |
test: enable encryption on nextjs-turbopack workbench (#1262)
* test: enable encryption on nextjs-turbopack workbench
Set VERCEL_DEPLOYMENT_KEY to enable e2e encryption for the
nextjs-turbopack workbench project. This allows the e2e test
suite to exercise encrypted workflow runs alongside existing
unencrypted runs for backwards compatibility testing.
* fix: fetch encryption key from API in external contexts (e2e, CLI)
Use VERCEL=1 env var to distinguish serverless runtime (can use local
HKDF derivation) from external contexts like e2e test runners, CLI,
and web dashboard (must fetch derived key from Vercel API).
Previously, when VERCEL_DEPLOYMENT_ID matched but VERCEL_DEPLOYMENT_KEY
was absent (e.g., e2e runner has the deployment ID from CI but not the
key), getEncryptionKeyForRun returned undefined — silently skipping
decryption. Now external contexts always use the API fetch path.
* fix: add 429 retry with exponential backoff to fetchRunKey
The e2e runner calls fetchRunKey for every run, which can exceed the
API rate limit (30/min per team) when many tests run in parallel.
Add retry logic with exponential backoff + jitter (up to 5 retries)
and respect the Retry-After header when present.
* Fix: Missing NaN guard on `retry-after` header parsing causes `setTimeout(r, NaN)` to resolve immediately (0ms), triggering rapid-fire retries against a rate-limited API endpoint.
This commit fixes the issue reported at packages/world-vercel/src/encryption.ts:138
**Bug Analysis:**
In `packages/world-vercel/src/encryption.ts`, the `fetchRunKey` function handles HTTP 429 (rate limited) responses by reading the `retry-after` header and computing a delay. The original code was:
```js
const retryAfter = response.headers.get('retry-after');
const delay = retryAfter
? Number.parseInt(retryAfter, 10) * 1000
: BASE_DELAY_MS * 2 ** attempt + Math.random() * 500;
```
Per RFC 9110, the `Retry-After` header can contain either a number of seconds OR an HTTP date string (e.g., `"Wed, 21 Oct 2015 07:28:00 GMT"`). When the header contains a date string or any non-numeric value, `Number.parseInt(retryAfter, 10)` returns `NaN`. Since the header string is truthy (non-empty), the ternary takes the parseInt path rather than the exponential backoff fallback. `NaN * 1000` evaluates to `NaN`, and `setTimeout(r, NaN)` resolves immediately (~0ms, confirmed by testing). This causes rapid-fire retries (up to 5 iterations with zero delay) against a rate-limited API endpoint, wasting resources and potentially worsening the rate-limiting situation.
**Fix:**
Parse the integer first, then check if the result is NaN before using it. If NaN, fall back to exponential backoff with jitter. This matches the established pattern already used in `packages/world-vercel/src/utils.ts` (lines 309-315) where the same header is properly guarded with `!Number.isNaN(parsed)`.
```js
const retryAfterHeader = response.headers.get('retry-after');
const parsedRetryAfter = retryAfterHeader
? Number.parseInt(retryAfterHeader, 10)
: NaN;
const delay = !Number.isNaN(parsedRetryAfter)
? parsedRetryAfter * 1000
: BASE_DELAY_MS * 2 ** attempt + Math.random() * 500;
```
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: TooTallNate <n@n8.io>
* fix: add --decrypt to e2e CLI inspect calls
With encryption enabled on the nextjs-turbopack workbench, CLI inspect
output shows encrypted data as placeholders. The e2e tests check for
actual output values, so they need --decrypt to see the real data.
* fix: remove manual 429 retry, rely on undici RetryAgent; add changeset
The shared RetryAgent from getDispatcher() already handles 429/5xx
retries with exponential backoff and Retry-After header support.
The manual retry loop in fetchRunKey was redundant.
Also add changeset for the VERCEL=1 external context fix.
* refactor: use @ts-expect-error on dispatcher instead of as any cast
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
|
||
|
|
c71befe8ec |
fix(core): premature suspension when hooks have buffered payloads with concurrent pending entities (#1294)
* test: reproduce hook+sleep promiseQueue regression Add failing tests that reproduce a regression from #1246 where the sleep's WorkflowSuspension fires before all hook payloads are delivered when a hook and sleep run concurrently. Root cause: when the null event fires, the sleep queues a suspension through promiseQueue. After the first hook payload resolves, subsequent hook payload resolutions are queued AFTER the sleep suspension, causing the workflow to terminate prematurely via Promise.race. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: expand tests to isolate bug to hook + pending entity pattern Add control tests proving sequential steps are NOT affected by the promiseQueue regression, isolating the bug to hooks specifically: Failing (hook-based): - hook + sleep: all 3 payloads → step invocation - hook + sleep: 2 payloads → return - hook + incomplete step: 2 payloads → return Passing (step-based controls): - sleep + sequential steps: both step events exist - sleep + sequential steps: only 1st step completed - incomplete step + sequential steps: all step events exist - hook only (no concurrent entity): payloads + step The bug is: any entity that queues a suspension through promiseQueue at null-event time preempts hook payload delivery, because hooks buffer payloads in payloadsQueue and only resolve them one-at-a-time as the workflow code iterates. Steps are unaffected because each step has its own events consumed before null fires. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): move WorkflowSuspension calls back to setTimeout(0) from promiseQueue The promiseQueue refactor (#1246) moved ALL promise resolutions through the queue, including WorkflowSuspension calls. This caused premature termination when hooks had buffered payloads and a concurrent entity (sleep or incomplete step) was pending: 1. Null event fires → all subscribers run 2. Sleep/step queues WorkflowSuspension via promiseQueue.then() 3. Hook's next payload resolution is also queued via promiseQueue.then() 4. Suspension fires first → Promise.race terminates workflow 5. Hook payload never delivered → infinite retry loop Fix: move WorkflowSuspension calls back to setTimeout(0) (macrotask). Suspensions fire AFTER all microtask-based deliveries (promiseQueue resolve/reject for step results and hook payloads) have completed. Non-suspension resolve/reject calls remain on promiseQueue for deterministic ordering of data delivery. Affected paths: - step.ts: null event handler - sleep.ts: null event handler - hook.ts: null event handler, eventLogEmpty suspension, dispose suspension * fix(core): use pendingDeliveries counter + scheduleWhenIdle for suspensions Replace all nested setTimeout/promiseQueue suspension patterns with a clean idle-polling mechanism: 1. pendingDeliveries counter: incremented before async hydration (step results, hook payloads), decremented in finally block after delivery 2. scheduleWhenIdle(ctx, fn): polls via setTimeout(0) → check counter → if > 0, wait for promiseQueue.then() → repeat. Only fires fn when pendingDeliveries reaches 0. This correctly handles: - Sync deserialization (no encryption): counter is 0, fires immediately after first setTimeout(0) - Async deserialization (with encryption): waits for decryption to complete before firing - Multi-round hook payload delivery: each createHookPromise() call increments the counter, preventing premature suspension between delivery rounds Also adds payloadsQueue.length check to hook null handler — don't trigger suspension if buffered payloads can satisfy pending awaits. Tests now run in both sync and async modes (14 total = 7 scenarios x 2). All 164 tests pass. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Nathan Rajlich <n@n8.io> |
||
|
|
36a901d2d2 |
feat: expose workflow and step names in metadata functions (#1285)
* feat: expose workflow and step names in metadata functions Add `workflowName` to `WorkflowMetadata` and `stepName` to `StepMetadata`, making them available via `getWorkflowMetadata()` and `getStepMetadata()`. This is useful for adding context to structured logs, collating logs during investigations, and bucketing errors by workflow/step name. Closes #1103 * add changeset * Apply suggestion from @TooTallNate Signed-off-by: Nathan Rajlich <n@n8.io> --------- Signed-off-by: Nathan Rajlich <n@n8.io> |
||
|
|
456c1aa455 | Add vitest plugin for testing full workflows without setting up a server (#1237) | ||
|
|
30e24d441e |
Merge commit from fork
* Prevent deterministic tokens in createWebhook Remove the `token` option from `WebhookOptions` to prevent unauthorized access to the public webhook endpoint. Webhook tokens are now always randomly generated. Deterministic tokens remain available for `createHook()` with server-side `resumeHook()`. **BREAKING CHANGE**: `createWebhook()` no longer accepts a `token` option. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR feedback: throw on token, revert unnecessary doc changes, replace examples - createWebhook() now throws an error if token is passed at runtime instead of silently stripping it - Revert unnecessary changes to create-hook.mdx (only keep slack_webhook -> slack_messages prefix change) - Replace Slack webhook examples in create-webhook.mdx and hooks.mdx with generic event collector examples that make sense without deterministic tokens Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address andresriancho's feedback: stricter token check, add unit test - Use `token !== undefined` instead of `if (token)` for correctness (empty string should also be rejected) - Add unit test verifying createWebhook({ token: "anything" }) throws the expected error message Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * change changeset to minor * Fix docs typechecker to actually resolve workflow module types The docs typechecker was silently failing to resolve `workflow` imports because: 1. repoRoot was computed 4 levels up instead of 3 2. No path mappings for workspace packages 3. Missing TS lib files for disposable and DOM iterables This meant all imported symbols became `any` and type errors in docs code samples went undetected. Fixes: - Fix repoRoot: '../../../..' -> '../../..' - Add path mappings for workflow, @workflow/core, @workflow/ai, etc. - Add lib.dom.iterable.d.ts and lib.esnext.disposable.d.ts - Fix regex that ate first line of some code blocks - Auto-skip error demonstration code blocks - Add respondWith to global Request for webhook doc patterns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Revert "Fix docs typechecker to actually resolve workflow module types" This reverts commit 02d42ac866e6db1b6d016339739344cdd9538e90. * chore: trigger CI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
a9fea9132e |
Update workbench tests to build and run outside of monorepo (#1230)
* Setup fixes * ci: run local e2e against staged tarball workbenches * ci: update staged workbench tarball setup script * chore: set nextjs workbenches back to next 16.1.6 * update lock * test(e2e): resolve workbench path from WORKBENCH_APP_PATH * fix: address deferred builder issues outside monorepo * ci: stage tarball workbenches only for nextjs local e2e * fix(next): discover deferred steps imported via workflows * test(core): gate deferred step-discovery dev test to canary * test(e2e): cover cross-file imported step in build/start lanes * fix(e2e): use local manifest in local runs and relax dev rebuild timeout * fix(workbench): add imported-step workflow symlink for sveltekit/astro * test(e2e): scope imported-step workflow test to nextjs lanes * fix(next): rebuild deferred entries on discovered file updates * fix(next): watch transitive deferred step deps for dev rebuilds * fix(next): restore socket-driven deferred step rebuilds * add changeset * chore: address review feedback on deferred e2e updates * fix(cli): guard stream flush against closed write streams |
||
|
|
809339ba1c |
fix(builders): enable directive discovery in dot-prefixed files and directories (#1228)
* fix(builders): enable directive discovery in dot-prefixed files and directories The glob-based file scanning in getInputFiles() was using tinyglobby's default behavior which skips dot-prefixed files/directories. This prevented discovering files with 'use step' / 'use workflow' directives inside paths like .config/step.ts or .hidden-workflow.ts. Switch to relative glob patterns with per-directory cwd and dot: true to ensure dot-files are scanned while still respecting the explicit ignore list (.git, .next, .vercel, etc.). * test: add E2E tests for dot-directory directive discovery Add workbench fixtures and E2E tests verifying that 'use step' / 'use workflow' directives inside dot-prefixed directories (.well-known/agent/) are correctly discovered, included in manifests, and executed at runtime. * fix: normalize paths in getInputFiles tests for Windows compatibility tinyglobby returns forward-slash paths even on Windows, while Node's path.join() uses backslashes. Normalize both sides to forward slashes before comparison. * refactor: use dirname() instead of join(.., '..') in test helper Cleaner and more idiomatic way to get the parent directory. * fix: add .nuxt and other build tool dot-directories to ignore list With dot: true enabled, framework build output directories like .nuxt/ are now traversed and their generated files picked up as input files. This caused a circular dependency in the nuxt builder where generated .nuxt/workflow/steps.mjs was being re-discovered as an input file. Add .nuxt, .turbo, .cache, .yarn, and .pnpm-store to the ignore list, matching the directories already ignored by the eager builder's watcher. |
||
|
|
0ecb9872c1 | Disable lazy discovery for turbopack workbench (#1223) | ||
|
|
02681dce4a |
feat(core): add hook.dispose() method to release hook tokens early (#1181)
* feat(core): add hook.dispose() method to release hook tokens early Add a `dispose()` method to the Hook interface that allows workflows to explicitly release hook tokens for reuse by other workflows while the current workflow is still running. This enables handoff patterns where one workflow can transfer a hook token to another workflow. - Add `dispose()` method to Hook interface in create-hook.ts - Implement dispose functionality in workflow/hook.ts - Add HookDisposedInvocationQueueItem to global.ts - Handle hook_disposed events in suspension-handler.ts - Update documentation in hooks.mdx and create-hook.mdx - Add e2e test for hook token reuse after explicit disposal https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * feat(core): implement TypeScript Disposable spec for hooks Add Symbol.dispose to Hook interface to support the TC39 Explicit Resource Management proposal. This allows hooks to be used with the `using` keyword for automatic disposal when exiting scope. https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * docs: prefer `using` keyword for hooks and webhooks Update all documentation and e2e tests to use the `using` keyword as the recommended approach for creating hooks and webhooks. This leverages the TC39 Explicit Resource Management proposal for automatic disposal. - Update e2e tests to use `using` syntax - Update foundational hooks guide to recommend `using` - Update create-hook API reference with `using` examples - Update create-webhook API reference with `using` examples - Update example workflow to use `using` https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * chore: simplify `using` examples in docs and e2e tests Remove unnecessary block scopes and excessive comments about automatic disposal. Block scopes are only used when early disposal is relevant (like in the handoff test). https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * docs: add brief explanation of `using` in first examples Add a one-liner explaining the `using` keyword in the intro examples of the API reference docs, so new users understand the syntax. https://claude.ai/code/session_01AkvrXduyFTbtV2joTPHrDH * fix: address PR review comments - Restore code highlights (`[!code highlight]`) that were unintentionally removed from pre-existing doc examples - Move `using` explanation from prose to inline code comment in intro examples - Add `{/* @skip-typecheck */}` to incomplete manual dispose() snippet - Add 409 (conflict/duplicate) error handling for hook_disposed events in suspension handler to handle workflow re-invocation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: polyfill Symbol.dispose in workflow VM context The workflow VM sandbox doesn't have Symbol.dispose/Symbol.asyncDispose available, causing `using` keyword to fail with "Symbol.dispose is not defined" at runtime. Add polyfill in the VM context creation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use VM's Symbol.dispose for hook disposable support The workflow VM sandbox has its own Symbol object with a polyfilled Symbol.dispose. The hook object was using the host's Symbol.dispose, which is a different symbol instance. The SWC-compiled `using` keyword looks up the VM's Symbol.dispose on the object, causing "Object not disposable" errors. Fix by setting Symbol.dispose on the hook object dynamically using the VM's globalThis.Symbol.dispose from the orchestrator context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert webhookWorkflow to create all webhooks upfront The webhookWorkflow e2e test creates 3 webhooks that must all exist before the test sends HTTP requests. Using `using` with sequential creation meant only the first webhook existed at the first suspension point. Revert to `const` since all webhooks need to be created upfront. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: eliminate hook_disposed queue item, use flag pattern for disposal Instead of adding a separate HookDisposedInvocationQueueItem to the queue on dispose, keep the HookInvocationQueueItem throughout the hook lifecycle and track state with flags (hasCreatedEvent, disposed). A closure variable (hasDisposedEvent) makes disposeHook() a pure no-op on replay, avoiding redundant server calls and 409 errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: delete hook from invocations queue on hook_disposed terminal event Match the pattern used by steps (step_completed/step_failed) and waits (wait_completed) where the queue item is removed on the terminal event. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: verify dispose() is safe when called twice after replay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add comprehensive edge case tests for hook disposal - WorkflowSuspension counts disposed hooks separately from active hooks - Dispose after hook_created replay produces correct suspension - Dispose before first suspension (needs both create + dispose) - Multiple hooks where only one is disposed - Dispose on a conflicted hook is safe (no crash) - Symbol.dispose calls disposeHook correctly (using keyword pattern) - Iterator break without dispose keeps hook alive in queue - Await after dispose on first invocation triggers suspension Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove unsafe cast by adding Symbol.dispose to hook object literal Add [Symbol.dispose] directly to the hook object so it satisfies the Hook<T> type without `as unknown as`. The VM's Symbol.dispose is still added separately when it differs from the host's. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address remaining Copilot review comments - Rename test to match new behavior (hooks stay in queue, not removed) - Use neutral "processed" verb in WorkflowSuspension message when mixed item types are present - Remove extends Disposable from Hook interface to avoid requiring lib.esnext.disposable in downstream consumers (explicit [Symbol.dispose]() method is still declared on the interface) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: drain pending promises in disposeHook to prevent orphaned awaits When dispose() is called while a promise is pending (e.g., iterator suspended on yield await this, or direct await hook after dispose), the promise would hang forever since the event consumer will never deliver another hook_received. Now disposeHook() clears the promises array and triggers a WorkflowSuspension so the runtime processes the disposal cleanly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove workflow from changeset, keep only @workflow/core Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |