Skip to content

workflow: support using a shared sandbox for all workflow invocations - #310

Merged
msullivan merged 2 commits into
mainfrom
sandbox-share
Aug 20, 2026
Merged

msullivan merged 2 commits into
mainfrom
sandbox-share

Conversation

@msullivan

Copy link
Copy Markdown
Contributor

Importing everything on every workflow invocation is quite slow,
especially when using pydantic or similar.

I am planning to add automatic support for using pydantic to
serialize/validate arguments, and regenerating the pydantic validators
on every invocation will make it even more expensive. Using a shared
sandbox eliminates that problem.

Using a shared sandbox means that we still get all of the benefits of
preventing nondeterminstic calls, but that modifications to global
variables will stay visible.
But I think a good workflow shouldn't really be using globals
anyway, so...

I think I'm planning to basically immediately make a PR that will
switch the default to share_sandboxes=True, but for cleanliness I'm
separating it.

To do this, I:

  • Add a Sandbox class entered with enter()
  • Add a policy flag for it
  • Make Workflows responsible for producing a sandbox
  • Hold a sandbox-scoped lock while importing the module, to avoid
    weird cyclic init races.
  • A bunch of tweaks to serde's registries

Questions:

  • Are we nervous about sharing a sandbox for genuinely concurrent
    runs? If so, we can maintain a pool of unused sandboxes and create
    a new one when necessary.

Importing everything on every workflow invocation is quite slow,
especially when using pydantic or similar.

I am planning to add automatic support for using pydantic to
serialize/validate arguments, and regenerating the pydantic validators
on every invocation will make it even more expensive. Using a shared
sandbox eliminates that problem.

Using a shared sandbox means that we still get all of the benefits of
preventing nondeterminstic calls, but that modifications to global
variables will stay visible.
But I think a good workflow shouldn't really be using globals
*anyway*, so...

I think I'm planning to basically immediately make a PR that will
switch the default to `share_sandboxes=True`, but for cleanliness I'm
separating it.

To do this, I:
 * Add a Sandbox class entered with enter()
 * Add a policy flag for it
 * Make `Workflows` responsible for producing a sandbox
 * Hold a sandbox-scoped lock while *importing* the module, to avoid
   weird cyclic init races.
 * A bunch of tweaks to serde's registries

Questions:
 * Are we nervous about sharing a sandbox for genuinely concurrent
   runs? If so, we can maintain a pool of unused sandboxes and create
   a new one when necessary.
@fantix

fantix commented Aug 20, 2026

Copy link
Copy Markdown
Member

Are we nervous about sharing a sandbox for genuinely concurrent runs? If so, we can maintain a pool of unused sandboxes and create a new one when necessary.

Each workflow run in the shared sandbox is sequential and serialized, right? Like workflow A starts an event loop, run to suspension, shutdown that loop, THEN workflow B can only start a new event loop after that, and do B things? I think I'm okay with shared sandboxes for this case.

Comment thread src/vercel-workflow/vercel/workflow/_internal/py_sandbox.py
@msullivan msullivan changed the title workflow: support to using a shared sandbox for all workflow invocations workflow: support using a shared sandbox for all workflow invocations Aug 20, 2026
@msullivan

Copy link
Copy Markdown
Contributor Author

Are we nervous about sharing a sandbox for genuinely concurrent runs? If so, we can maintain a pool of unused sandboxes and create a new one when necessary.

Each workflow run in the shared sandbox is sequential and serialized, right? Like workflow A starts an event loop, run to suspension, shutdown that loop, THEN workflow B can only start a new event loop after that, and do B things? I think I'm okay with shared sandboxes for this case.

We will run multiple workflows in separate threads, so they can be genuinely concurrent. A and B in the same sandbox at the same time.

If we are nervous about that, we can make it so that a sandbox is in use by one thread at a time, and can then get reused later.

@fantix

fantix commented Aug 20, 2026

Copy link
Copy Markdown
Member

We will run multiple workflows in separate threads, so they can be genuinely concurrent. A and B in the same sandbox at the same time.

If we are nervous about that, we can make it so that a sandbox is in use by one thread at a time, and can then get reused later.

I feel like it's probably fine with the asyncio Runner thread-local thing, as far as each event loop is utilized sequentially for all runs within each thread.

@msullivan

Copy link
Copy Markdown
Contributor Author

We will run multiple workflows in separate threads, so they can be genuinely concurrent. A and B in the same sandbox at the same time.
If we are nervous about that, we can make it so that a sandbox is in use by one thread at a time, and can then get reused later.

I feel like it's probably fine with the asyncio Runner thread-local thing, as far as each event loop is utilized sequentially for all runs within each thread.

Yeah, the event loop isn't shared.

@msullivan
msullivan merged commit c825c35 into main Aug 20, 2026
14 checks passed
@msullivan
msullivan deleted the sandbox-share branch August 20, 2026 19:11
@scotttrinh scotttrinh mentioned this pull request Aug 26, 2026
scotttrinh added a commit that referenced this pull request Aug 26, 2026
vercel-internal-core
--------------------

0.1.3 - 2026-08-26
------------------

Internal
--------

- Support disabling HTTP timeouts for selected SDK operations while preserving the client default elsewhere. (#307)

vercel-connect
--------------

0.1.1 - 2026-08-26
------------------

- Update dependencies.

vercel-queue
------------

0.8.1 - 2026-08-26
------------------

Documentation
-------------

- Remove documentation and examples for `asgi_app` in preparation for its removal. (#309)

vercel-sandbox
--------------

0.5.0 - 2026-08-26
------------------

Features
--------

- Add sync and async `fork_sandbox(...)` support for creating a sandbox from an existing named sandbox with optional configuration overrides. (#257)
- Add `region` and `failover_regions` configuration for sandbox creation, forks, and updates, plus multi-region snapshot availability reporting. (#308)

Bug Fixes
---------

- Allow Sandbox process waits and log streams to remain idle longer than the session HTTP timeout. (#307)

vercel-workflow
---------------

0.10.0 - 2026-08-26
-------------------

Breaking Changes
----------------

- Use type annotations on workflows and step to allow passing Pydantic models and dataclasses. (#317)
- This is a breaking change, because type annotations will now be enforced. Passing a `dict` when the declaration expects a `list` will fail. (#317)
- Pydantic models and dataclasses can no longer be passed to `@serializable` or `register_serializable()`. Annotate the workflow or step parameter or return value with their type instead. (#317)

Features
--------

- `get_workflow_metadata()` returns the current run's `WorkflowInfo` (run id, workflow name, start time, deployment URL, and feature flags), callable from a workflow body or a step body — mirroring the JS SDK's `getWorkflowMetadata()`. (#320)
- One current limitation is that `started_at` is `None` from inside a step. (#320)
- `BaseHook.wait()` accepts `metadata` to record on the hook, and `get_hook_by_token()` reads it back for a resumer. (#301)
- A step can raise `RetryableError` to control when its next attempt runs. (#302)
- Accept `specVersion` 7 sealed noop event logs. (#319)
- A workflow or step can attach plaintext metadata to its run with `set_attributes()`. (#303)
- Add a `share_sandboxes` parameter to `SandboxPolicy` to enable reusing already created sandboxes instead of creating a new one on each invocation. This speeds up workflows but means that modifications to global state may persist between invocations. (#310)
- Expose unstable API to serve workflow HTTP endpoint from your own web framework. (#294)
- Added semi-internal manifest API for TS tools and e2e test. (#296)

Bug Fixes
---------

- Fix failing or even crashing cipher calls inside the workflow sandbox. (#305)
- Support resuming hooks with payload in the queue message. (#300)
- Fixed nulls rejected by server, requiring Pydantic 2.12 or newer. (#321)
- Fixed workflow and step calls with both positional-or-keyword parameters and `*args` failing during replay because their arguments were recorded in an unbindable shape. (#312)

Internal
--------

- Construct the protocol models by Python field name. (#322)

vercel
------

0.11.0 - 2026-08-26
-------------------

Features
--------

- Expose `get_deadline()` for reading the current Function invocation deadline. (#306)
- Answer workflow health checks for both queue-based transport and HTTP. (#292)
- Add support to read the sealed (`encp`) workflow payloads (X25519 + AES-GCM) an outside writer addresses to a run, under the `encryption` extra. (#297)

Bug Fixes
---------

- Remove upper bounds on aggregate Sandbox and Workflow dependencies so sibling releases cannot make the `vercel` package un-installable. (#334)
- Start a workflow run even when its queue message arrives before the `run_created` event has landed. (#284)

Internal
--------

- The Workflows implementation now ships in the separate `vercel-workflow` distribution, which `vercel` depends on, so `vercel.workflow` imports keep working without installing anything extra. (#299)

vercel-apscheduler
------------------

0.3.0 - 2026-08-26
------------------

Breaking Changes
----------------

- The managed Redis backend was removed. The integration now always runs on its managed job store (Vercel Runtime Cache); a configured default `RedisJobStore` is rejected at import, `VERCEL_APSCHEDULER_BACKEND` accepts only `cache`, and the `redis` dependency is gone. The scheduler's durable identity now always derives from the builder-assigned subscriber id (previously the Redis `jobs_key`); the `scheduler_id` option still pins an identity explicitly. (#286)

vercel-celery
-------------

0.7.5 - 2026-08-26
------------------

- Update dependencies.

vercel-django-tasks
-------------------

0.7.0 - 2026-08-26
------------------

Features
--------

- Add a Vercel Queues backend for Django Tasks and use it by default when no task backends are configured. (#291)

vercel-dramatiq
---------------

0.7.4 - 2026-08-26
------------------

- Update dependencies.
scotttrinh added a commit that referenced this pull request Aug 31, 2026
vercel-headers
--------------

0.7.2 - 2026-08-31
------------------

Bug Fixes
---------

- Accept request objects with concrete header implementations in the IP address and geolocation type annotations. (#337)

vercel-internal-core
--------------------

0.1.3 - 2026-08-31
------------------

Internal
--------

- Support disabling HTTP timeouts for selected SDK operations while preserving the client default elsewhere. (#307)

vercel-oidc
-----------

0.8.1 - 2026-08-31
------------------

- Update dependencies.

vercel-connect
--------------

0.1.1 - 2026-08-31
------------------

- Update dependencies.

vercel-internal-telemetry
-------------------------

0.7.3 - 2026-08-31
------------------

- Update dependencies.

vercel-queue
------------

0.8.1 - 2026-08-31
------------------

Bug Fixes
---------

- Force embedded development servers to exit when graceful shutdown stalls. (#351)

Documentation
-------------

- Remove documentation and examples for `asgi_app` in preparation for its removal. (#309)

vercel-sandbox
--------------

0.5.0 - 2026-08-31
------------------

Features
--------

- Add sync and async `fork_sandbox(...)` support for creating a sandbox from an existing named sandbox with optional configuration overrides. (#257)
- Add `region` and `failover_regions` configuration for sandbox creation, forks, and updates, plus multi-region snapshot availability reporting. (#308)
- Forward private ``__``-prefixed parameters to the Sandbox API. (#350)

Bug Fixes
---------

- Allow Sandbox process waits and log streams to remain idle longer than the session HTTP timeout. (#307)
- Expose Linux process signals consistently on every SDK host platform. (#352)

vercel-cache
------------

0.7.3 - 2026-08-31
------------------

- Update dependencies.

vercel-workflow
---------------

0.10.0 - 2026-08-31
-------------------

Breaking Changes
----------------

- Make sleep() and retry delays treat numbers as seconds, not ms (#346)
- This matches Python standard library APIs. (#346)
- Use type annotations on workflows and step to allow passing Pydantic models and dataclasses. (#317)
- This is a breaking change, because type annotations will now be enforced. Passing a `dict` when the declaration expects a `list` will fail. (#317)
- Pydantic models and dataclasses can no longer be passed to `@serializable` or `register_serializable()`. Annotate the workflow or step parameter or return value with their type instead. (#317)

Features
--------

- Support `call_later`, `call_at`, and `now` in the event loop implementation. (#343)
- This enables use of `asyncio.sleep()` as well as `asyncio.timeout` and the `timeout` parameter of `asyncio.wait_for`. (#343)
- `get_workflow_metadata()` returns the current run's `WorkflowInfo` (run id, workflow name, start time, deployment URL, and feature flags), callable from a workflow body or a step body — mirroring the JS SDK's `getWorkflowMetadata()`. (#320)
- One current limitation is that `started_at` is `None` from inside a step. (#320)
- `BaseHook.wait()` accepts `metadata` to record on the hook, and `get_hook_by_token()` reads it back for a resumer. (#301)
- A step can raise `RetryableError` to control when its next attempt runs. (#302)
- Accept `specVersion` 7 sealed noop event logs. (#319)
- Failed run and step events now preserve serialized error classes, messages, stacks, and causes. Failed runs also expose a plaintext `errorCode`. (#304)
- A workflow or step can attach plaintext metadata to its run with `set_attributes()`. (#303)
- Add a `share_sandboxes` parameter to `SandboxPolicy` to enable reusing already created sandboxes instead of creating a new one on each invocation. This speeds up workflows but means that modifications to global state may persist between invocations. (#310)
- Support `timedelta` arguments for workflow `sleep()` and retry delays. (#342)
- Expose unstable API to serve workflow HTTP endpoint from your own web framework. (#294)
- Added semi-internal manifest API for TS tools and e2e test. (#296)

Bug Fixes
---------

- Fix failing or even crashing cipher calls inside the workflow sandbox. (#305)
- Fail a workflow run with `HookConflictError` when another run already owns its hook token instead of leaving it running indefinitely. (#327)
- Support resuming hooks with payload in the queue message. (#300)
- Fix some bugs involving hooks arriving when the workflow was not yet blocked on them. (#339)
- Fixed nulls rejected by server, requiring Pydantic 2.12 or newer. (#321)
- Prevent workflows from having side effects while suspending. (#332)
- `hook.dispose()` will now work properly in a `finally` block.  (That is, the hook will be disposed only when the workflow is actually terminating, and not every time it gets replayed.) (#332)
- More reliably fail runs whose replay diverges from the event log. (#347)
- Runs will now fail even in the case where the main thread of execution is not directly blocked on the suspension that is erroring. (#347)
- Fixed workflow and step calls with both positional-or-keyword parameters and `*args` failing during replay because their arguments were recorded in an unbindable shape. (#312)

Internal
--------

- Remove a just-added return from a finally block. (#344)
- Correct internal workflow type annotations found by checking untyped function bodies. (#337)
- Refactored event replay. (#341)
- Construct the protocol models by Python field name. (#322)

vercel
------

0.11.0 - 2026-08-31
-------------------

Features
--------

- Expose `get_deadline()` for reading the current Function invocation deadline. (#306)
- Answer workflow health checks for both queue-based transport and HTTP. (#292)
- Add support to read the sealed (`encp`) workflow payloads (X25519 + AES-GCM) an outside writer addresses to a run, under the `encryption` extra. (#297)

Bug Fixes
---------

- Remove upper bounds on aggregate Sandbox and Workflow dependencies so sibling releases cannot make the `vercel` package un-installable. (#334)
- Start a workflow run even when its queue message arrives before the `run_created` event has landed. (#284)

Internal
--------

- The Workflows implementation now ships in the separate `vercel-workflow` distribution, which `vercel` depends on, so `vercel.workflow` imports keep working without installing anything extra. (#299)

vercel-apscheduler
------------------

0.3.0 - 2026-08-31
------------------

Breaking Changes
----------------

- The managed Redis backend was removed. The integration now always runs on its managed job store (Vercel Runtime Cache); a configured default `RedisJobStore` is rejected at import, `VERCEL_APSCHEDULER_BACKEND` accepts only `cache`, and the `redis` dependency is gone. The scheduler's durable identity now always derives from the builder-assigned subscriber id (previously the Redis `jobs_key`); the `scheduler_id` option still pins an identity explicitly. (#286)

vercel-celery
-------------

0.7.5 - 2026-08-31
------------------

- Update dependencies.

vercel-django-tasks
-------------------

0.7.0 - 2026-08-31
------------------

Features
--------

- Add a Vercel Queues backend for Django Tasks and use it by default when no task backends are configured. (#291)

vercel-dramatiq
---------------

0.7.4 - 2026-08-31
------------------

- Update dependencies.
scotttrinh added a commit that referenced this pull request Aug 31, 2026
vercel-headers
--------------

0.7.2 - 2026-08-31
------------------

Bug Fixes
---------

- Accept request objects with concrete header implementations in the IP address and geolocation type annotations. (#337)

vercel-internal-core
--------------------

0.1.3 - 2026-08-31
------------------

Internal
--------

- Support disabling HTTP timeouts for selected SDK operations while preserving the client default elsewhere. (#307)

vercel-oidc
-----------

0.8.1 - 2026-08-31
------------------

- Update dependencies.

vercel-connect
--------------

0.1.1 - 2026-08-31
------------------

- Update dependencies.

vercel-internal-telemetry
-------------------------

0.7.3 - 2026-08-31
------------------

- Update dependencies.

vercel-queue
------------

0.8.1 - 2026-08-31
------------------

Bug Fixes
---------

- Force embedded development servers to exit when graceful shutdown stalls. (#351)

Documentation
-------------

- Remove documentation and examples for `asgi_app` in preparation for its removal. (#309)

vercel-sandbox
--------------

0.5.0 - 2026-08-31
------------------

Features
--------

- Add sync and async `fork_sandbox(...)` support for creating a sandbox from an existing named sandbox with optional configuration overrides. (#257)
- Add `region` and `failover_regions` configuration for sandbox creation, forks, and updates, plus multi-region snapshot availability reporting. (#308)
- Forward private ``__``-prefixed parameters to the Sandbox API. (#350)

Bug Fixes
---------

- Allow Sandbox process waits and log streams to remain idle longer than the session HTTP timeout. (#307)
- Expose Linux process signals consistently on every SDK host platform. (#352)

vercel-cache
------------

0.7.3 - 2026-08-31
------------------

- Update dependencies.

vercel-workflow
---------------

0.10.0 - 2026-08-31
-------------------

Breaking Changes
----------------

- Make `await hook` never return `None`
- Raises a new `HookDisposedError` instead of returning `None` when the hook has been disposed. It is now typed to return `T` instead of `T | None`. `async for` over a hook will stop iterating on disposal, still.
- Make sleep() and retry delays treat numbers as seconds, not ms (#346)
- This matches Python standard library APIs. (#346)
- Use type annotations on workflows and step to allow passing Pydantic models and dataclasses. (#317)
- This is a breaking change, because type annotations will now be enforced. Passing a `dict` when the declaration expects a `list` will fail. (#317)
- Pydantic models and dataclasses can no longer be passed to `@serializable` or `register_serializable()`. Annotate the workflow or step parameter or return value with their type instead. (#317)

Features
--------

- Support `call_later`, `call_at`, and `now` in the event loop implementation. (#343)
- This enables use of `asyncio.sleep()` as well as `asyncio.timeout` and the `timeout` parameter of `asyncio.wait_for`. (#343)
- `get_workflow_metadata()` returns the current run's `WorkflowInfo` (run id, workflow name, start time, deployment URL, and feature flags), callable from a workflow body or a step body — mirroring the JS SDK's `getWorkflowMetadata()`. (#320)
- One current limitation is that `started_at` is `None` from inside a step. (#320)
- Make `HookEvent` an async context manager
- This matches TS, which supports `using`. ``` # disposes the hook on block exit async with SomeHook.wait(...) as hook: res = await hook ```
- `BaseHook.wait()` accepts `metadata` to record on the hook, and `get_hook_by_token()` reads it back for a resumer. (#301)
- A step can raise `RetryableError` to control when its next attempt runs. (#302)
- Accept `specVersion` 7 sealed noop event logs. (#319)
- Failed run and step events now preserve serialized error classes, messages, stacks, and causes. Failed runs also expose a plaintext `errorCode`. (#304)
- A workflow or step can attach plaintext metadata to its run with `set_attributes()`. (#303)
- Add a `share_sandboxes` parameter to `SandboxPolicy` to enable reusing already created sandboxes instead of creating a new one on each invocation. This speeds up workflows but means that modifications to global state may persist between invocations. (#310)
- Support `timedelta` arguments for workflow `sleep()` and retry delays. (#342)
- Expose unstable API to serve workflow HTTP endpoint from your own web framework. (#294)
- Added semi-internal manifest API for TS tools and e2e test. (#296)

Bug Fixes
---------

- Fix failing or even crashing cipher calls inside the workflow sandbox. (#305)
- Fail a workflow run with `HookConflictError` when another run already owns its hook token instead of leaving it running indefinitely. (#327)
- Support resuming hooks with payload in the queue message. (#300)
- Fix some bugs involving hooks arriving when the workflow was not yet blocked on them. (#339)
- Fixed nulls rejected by server, requiring Pydantic 2.12 or newer. (#321)
- Prevent workflows from having side effects while suspending. (#332)
- `hook.dispose()` will now work properly in a `finally` block.  (That is, the hook will be disposed only when the workflow is actually terminating, and not every time it gets replayed.) (#332)
- More reliably fail runs whose replay diverges from the event log. (#347)
- Runs will now fail even in the case where the main thread of execution is not directly blocked on the suspension that is erroring. (#347)
- Fixed workflow and step calls with both positional-or-keyword parameters and `*args` failing during replay because their arguments were recorded in an unbindable shape. (#312)

Internal
--------

- Remove a just-added return from a finally block. (#344)
- Correct internal workflow type annotations found by checking untyped function bodies. (#337)
- Refactored event replay. (#341)
- Construct the protocol models by Python field name. (#322)

vercel
------

0.11.0 - 2026-08-31
-------------------

Features
--------

- Expose `get_deadline()` for reading the current Function invocation deadline. (#306)
- Answer workflow health checks for both queue-based transport and HTTP. (#292)
- Add support to read the sealed (`encp`) workflow payloads (X25519 + AES-GCM) an outside writer addresses to a run, under the `encryption` extra. (#297)

Bug Fixes
---------

- Remove upper bounds on aggregate Sandbox and Workflow dependencies so sibling releases cannot make the `vercel` package un-installable. (#334)
- Start a workflow run even when its queue message arrives before the `run_created` event has landed. (#284)

Internal
--------

- The Workflows implementation now ships in the separate `vercel-workflow` distribution, which `vercel` depends on, so `vercel.workflow` imports keep working without installing anything extra. (#299)

vercel-apscheduler
------------------

0.3.0 - 2026-08-31
------------------

Breaking Changes
----------------

- The managed Redis backend was removed. The integration now always runs on its managed job store (Vercel Runtime Cache); a configured default `RedisJobStore` is rejected at import, `VERCEL_APSCHEDULER_BACKEND` accepts only `cache`, and the `redis` dependency is gone. The scheduler's durable identity now always derives from the builder-assigned subscriber id (previously the Redis `jobs_key`); the `scheduler_id` option still pins an identity explicitly. (#286)

vercel-celery
-------------

0.7.5 - 2026-08-31
------------------

- Update dependencies.

vercel-django-tasks
-------------------

0.7.0 - 2026-08-31
------------------

Features
--------

- Add a Vercel Queues backend for Django Tasks and use it by default when no task backends are configured. (#291)

vercel-dramatiq
---------------

0.7.4 - 2026-08-31
------------------

- Update dependencies.
scotttrinh added a commit that referenced this pull request Sep 1, 2026
vercel-headers
--------------

0.7.2 - 2026-09-01
------------------

Bug Fixes
---------

- Accept request objects with concrete header implementations in the IP address and geolocation type annotations. (#337)

vercel-internal-core
--------------------

0.1.3 - 2026-09-01
------------------

Internal
--------

- Support disabling HTTP timeouts for selected SDK operations while preserving the client default elsewhere. (#307)

vercel-oidc
-----------

0.8.1 - 2026-09-01
------------------

- Update dependencies.

vercel-connect
--------------

0.1.1 - 2026-09-01
------------------

- Update dependencies.

vercel-internal-telemetry
-------------------------

0.7.3 - 2026-09-01
------------------

- Update dependencies.

vercel-queue
------------

0.8.1 - 2026-09-01
------------------

Bug Fixes
---------

- Force embedded development servers to exit when graceful shutdown stalls. (#351)

Documentation
-------------

- Remove documentation and examples for `asgi_app` in preparation for its removal. (#309)

vercel-sandbox
--------------

0.5.0 - 2026-09-01
------------------

Features
--------

- Add sync and async `fork_sandbox(...)` support for creating a sandbox from an
  existing named sandbox with optional configuration overrides. (#257)

- Add `region` and `failover_regions` configuration for sandbox creation, forks,
  and updates, plus multi-region snapshot availability reporting. (#308)

- Forward private ``__``-prefixed parameters to the Sandbox API. (#350)

Bug Fixes
---------

- Allow Sandbox process waits and log streams to remain idle longer than the session HTTP timeout. (#307)
- Expose Linux process signals consistently on every SDK host platform. (#352)

vercel-cache
------------

0.7.3 - 2026-09-01
------------------

- Update dependencies.

vercel-workflow
---------------

0.10.0 - 2026-09-01
-------------------

Breaking Changes
----------------

- Make `await hook` never return `None` (#353)

  Raises a new `HookDisposedError` instead of returning `None` when the hook has been disposed. It is now typed to return `T` instead of `T | None`. `async for` over a hook will stop iterating on disposal, still.

- Make sleep() and retry delays treat numbers as seconds, not ms (#346)

  This matches Python standard library APIs.

- Use type annotations on workflows and step to allow passing Pydantic models and dataclasses. (#317)

  This is a breaking change, because type annotations will now be
  enforced. Passing a `dict` when the declaration expects a `list` will
  fail.

  Pydantic models and dataclasses can no longer be passed to
  `@serializable` or `register_serializable()`. Annotate the workflow or step
  parameter or return value with their type instead.

Features
--------

- Support `call_later`, `call_at`, and `now` in the event loop implementation. (#343)

  This enables use of `asyncio.sleep()` as well as `asyncio.timeout` and
  the `timeout` parameter of `asyncio.wait_for`.

- Add opt-in cancellable steps: `@workflows.step(cancellable=True)`.

  When `cancel()` is called on a cancellable step, we send a message on
  a stream that the step will listen for. If it gets a message, it will
  exit.

  Note that like regular asyncio tasks, `cancel()` does not cause the
  step to immediately become "cancelled". Waiting on it will still wait
  for the step to actually terminate.

  If there are still cancellable steps running when a workflow function
  completes, they will be cancelled and the workflow will wait for them
  to finish before terminating.

- `get_workflow_metadata()` returns the current run's `WorkflowInfo` (run id,
  workflow name, start time, deployment URL, and feature flags), callable from a
  workflow body or a step body — mirroring the JS SDK's `getWorkflowMetadata()`. (#320)

  One current limitation is that `started_at` is `None` from inside a step.

- Make `HookEvent` an async context manager (#354)

  This matches TS, which supports `using`.
  ```
  # disposes the hook on block exit
  async with SomeHook.wait(...) as hook:
      res = await hook
  ```

- Add `HookEvent.get_conflict()` to check for a token conflict without waiting for hook data.
- `BaseHook.wait()` accepts `metadata` to record on the hook, and `get_hook_by_token()` reads it back for a resumer. (#301)
- A step can raise `RetryableError` to control when its next attempt runs. (#302)
- Accept `specVersion` 7 sealed noop event logs. (#319)
- Failed run and step events now preserve serialized error classes, messages, stacks, and causes. Failed runs also expose a plaintext `errorCode`. (#304)
- A workflow or step can attach plaintext metadata to its run with `set_attributes()`. (#303)
- Add a `share_sandboxes` parameter to `SandboxPolicy` to enable reusing already created sandboxes instead of creating a new one on each invocation. This speeds up workflows but means that modifications to global state may persist between invocations. (#310)
- Support `timedelta` arguments for workflow `sleep()` and retry delays. (#342)
- Expose unstable API to serve workflow HTTP endpoint from your own web framework. (#294)
- Added semi-internal manifest API for TS tools and e2e test. (#296)

Bug Fixes
---------

- Fix failing or even crashing cipher calls inside the workflow sandbox. (#305)
- Fail a workflow run with `HookConflictError` when another run already owns its hook token instead of leaving it running indefinitely. (#327)
- Fix a bug that caused reusing a hook token after disposing the previous hook to conflict with the same workflow run.
- Support resuming hooks with payload in the queue message. (#300)
- Prevent recursive workflow sandbox imports on Windows and decode Node CLI output as UTF-8.

- Fix some bugs involving hooks arriving when the workflow was not yet
  blocked on them. (#339)

- Fixed nulls rejected by server, requiring Pydantic 2.12 or newer. (#321)

- Prevent workflows from having side effects while suspending. (#332)

  `hook.dispose()` will now work properly in a `finally` block.  (That
  is, the hook will be disposed only when the workflow is actually
  terminating, and not every time it gets replayed.)

- Fail an unregistered workflow step permanently and resume its workflow with a
  clear error instead of returning a `KeyError` and redelivering forever.

- More reliably fail runs whose replay diverges from the event log. (#347)

  Runs will now fail even in the case where the main thread of execution
  is not directly blocked on the suspension that is erroring.

- Fixed workflow and step calls with both positional-or-keyword parameters and `*args` failing during replay because their arguments were recorded in an unbindable shape. (#312)

Internal
--------

- Remove a just-added return from a finally block. (#344)
- Correct internal workflow type annotations found by checking untyped function bodies. (#337)
- Refactored event replay. (#341)
- Construct the protocol models by Python field name. (#322)

vercel
------

0.11.0 - 2026-09-01
-------------------

Features
--------

- Expose `get_deadline()` for reading the current Function invocation deadline. (#306)
- Answer workflow health checks for both queue-based transport and HTTP. (#292)
- Add support to read the sealed (`encp`) workflow payloads (X25519 + AES-GCM) an outside writer addresses to a run, under the `encryption` extra. (#297)

Bug Fixes
---------

- Remove upper bounds on aggregate Sandbox and Workflow dependencies so sibling releases cannot make the `vercel` package un-installable. (#334)

- Start a workflow run even when its queue message arrives before the
  `run_created` event has landed. (#284)

Internal
--------

- The Workflows implementation now ships in the separate `vercel-workflow`
  distribution, which `vercel` depends on, so `vercel.workflow` imports keep
  working without installing anything extra. (#299)

vercel-apscheduler
------------------

0.3.0 - 2026-09-01
------------------

Breaking Changes
----------------

- The managed Redis backend was removed. The integration now always runs on its
  managed job store (Vercel Runtime Cache); a configured default `RedisJobStore`
  is rejected at import, `VERCEL_APSCHEDULER_BACKEND` accepts only `cache`, and
  the `redis` dependency is gone. The scheduler's durable identity now always
  derives from the builder-assigned subscriber id (previously the Redis
  `jobs_key`); the `scheduler_id` option still pins an identity explicitly. (#286)

vercel-celery
-------------

0.7.5 - 2026-09-01
------------------

- Update dependencies.

vercel-django-tasks
-------------------

0.7.0 - 2026-09-01
------------------

Features
--------

- Add a Vercel Queues backend for Django Tasks and use it by default when no
  task backends are configured. (#291)

vercel-dramatiq
---------------

0.7.4 - 2026-09-01
------------------

- Update dependencies.

This branch was previously deployed

1 inactive deployment
ci — bbdb1818 Deployed Aug 20, 2026 by msullivan via test (py3.11) #1192
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants