Skip to content

Update trigger - #4

Open
tylerc-govsignals wants to merge 1996 commits into
GovSignals:ConProgramming/two-phase-deployfrom
triggerdotdev:main
Open

tylerc-govsignals wants to merge 1996 commits into
GovSignals:ConProgramming/two-phase-deployfrom
triggerdotdev:main

Conversation

@tylerc-govsignals

Copy link
Copy Markdown
Collaborator

Closes #

✅ Checklist

  • I have followed every step in the contributing guide
  • The PR title follows the convention.
  • I ran and tested the code works

Testing

[Describe the steps you took to test this change]


Changelog

[Short description of what has changed]


Screenshots

[Screenshots]

💯

carderne and others added 29 commits September 16, 2026 14:45
Mono-RevId: d8b73d93e54f38fa91e02d9eacf172665dde4d0a
Mono-RevId: ae5e805754715be9aa073ac27678447a7ecb30d6
Mono-RevId: 10c7939163dff42cf5389eebd02793f84c069356
Sending a message to a chat session that had been idle for a while could
fail. The session's realtime streams are created empty and auto-deleted
once they have been empty for the basin's delete-on-empty age, and a
message that arrived during the brief window while a stream was being
deleted failed with a conflict.

The default delete-on-empty age
(`REALTIME_STREAMS_BASIN_DELETE_ON_EMPTY_MIN_AGE`) is raised from 1 hour
to 30 days, matching the stream retention age, so an idle session's
streams are no longer removed out from under it. This age is now also
applied when a basin is reconfigured, not only when it is first created.
As an extra guard, a stream append that races the deletion is retried
with bounded backoff and recreates the stream instead of surfacing an
error.

Mono-RevId: de345da6508411a22df4179671e7161161d8b26b
Keep assistant steps and tool results replaced by inner compaction out
of subsequent `chat.agent()` model context. Turn completion now appends
only response steps generated after compaction, while preserving the
full visible conversation. Persistence hooks retain the complete
response, including same-ID replacements for approvals and handovers.

Track the completed-step boundary and trim the captured response before
model-message conversion. Same-ID handover continuations also skip their
original assistant prefix, avoiding a fallback that rebuilds uncompacted
model history.

Mono-RevId: 55a2d4354c501682b159c8853255b1f08a13d3cb
Mono-RevId: 93c150de793b922c3be2d58f2f147658d3d0cb8c
Mono-RevId: 507cf22bb577a9a495820c9028bc7cd7a0c7fede
Mono-RevId: 65c2b9b96bc9a32a351ab93a70eb65773b6ede6e
## Summary
3 improvements.

## Improvements
- Show how to override the default cron window in schedule policy
warnings.
([`630eb75d1`](630eb75))
- Thrown error `cause` chains are now captured and shown. When a task
throws an error that wraps another one, the run's error in the
dashboard, the CLI dev output, and failure alerts all carry the chain
instead of only the outermost message.
([`4f36f614b`](4f36f61))
  
  ```ts
throw new Error("Could not sync the customer", { cause: originalError
});
  ```
  
The chain is flattened outermost first, capped at five causes, and cycle
safe. It also rides on the `error` of API and realtime run records as a
`causes` array, and `triggerAndWait` and `triggerAndSubscribe` rebuild
it as a native `cause` on the error they hand back, so `err.cause` works
in your own catch blocks.
- Keep summarized assistant steps and tool results out of future
`chat.agent()` model context after inner compaction, while preserving
the full visible conversation.
([`fc77bfc9a`](fc77bfc))

<details>
<summary>Raw changeset output</summary>

# Releases
## @trigger.dev/build@4.6.3

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.6.3`
## trigger.dev@4.6.3

### Patch Changes

- Show how to override the default cron window in schedule policy
warnings.
([`630eb75d1`](630eb75))
- Thrown error `cause` chains are now captured and shown. When a task
throws an error that wraps another one, the run's error in the
dashboard, the CLI dev output, and failure alerts all carry the chain
instead of only the outermost message.
([`4f36f614b`](4f36f61))

  ```ts
throw new Error("Could not sync the customer", { cause: originalError
});
  ```

The chain is flattened outermost first, capped at five causes, and cycle
safe. It also rides on the `error` of API and realtime run records as a
`causes` array, and `triggerAndWait` and `triggerAndSubscribe` rebuild
it as a native `cause` on the error they hand back, so `err.cause` works
in your own catch blocks.

- Updated dependencies:
  - `@trigger.dev/core@4.6.3`
  - `@trigger.dev/build@4.6.3`
  - `@trigger.dev/schema-to-json@4.6.3`
## @trigger.dev/core@4.6.3

### Patch Changes

- Thrown error `cause` chains are now captured and shown. When a task
throws an error that wraps another one, the run's error in the
dashboard, the CLI dev output, and failure alerts all carry the chain
instead of only the outermost message.
([`4f36f614b`](4f36f61))

  ```ts
throw new Error("Could not sync the customer", { cause: originalError
});
  ```

The chain is flattened outermost first, capped at five causes, and cycle
safe. It also rides on the `error` of API and realtime run records as a
`causes` array, and `triggerAndWait` and `triggerAndSubscribe` rebuild
it as a native `cause` on the error they hand back, so `err.cause` works
in your own catch blocks.
## @trigger.dev/python@4.6.3

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/sdk@4.6.3`
  - `@trigger.dev/core@4.6.3`
  - `@trigger.dev/build@4.6.3`
## @trigger.dev/react-hooks@4.6.3

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.6.3`
## @trigger.dev/redis-worker@4.6.3

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.6.3`
## @trigger.dev/rsc@4.6.3

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.6.3`
## @trigger.dev/schema-to-json@4.6.3

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.6.3`
## @trigger.dev/sdk@4.6.3

### Patch Changes

- Thrown error `cause` chains are now captured and shown. When a task
throws an error that wraps another one, the run's error in the
dashboard, the CLI dev output, and failure alerts all carry the chain
instead of only the outermost message.
([`4f36f614b`](4f36f61))

  ```ts
throw new Error("Could not sync the customer", { cause: originalError
});
  ```

The chain is flattened outermost first, capped at five causes, and cycle
safe. It also rides on the `error` of API and realtime run records as a
`causes` array, and `triggerAndWait` and `triggerAndSubscribe` rebuild
it as a native `cause` on the error they hand back, so `err.cause` works
in your own catch blocks.

- Keep summarized assistant steps and tool results out of future
`chat.agent()` model context after inner compaction, while preserving
the full visible conversation.
([`fc77bfc9a`](fc77bfc))
- Updated dependencies:
  - `@trigger.dev/core@4.6.3`

</details>

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Adds support for environment variables with empty-string values, rolling
out gradually. Once enabled for your organization, an empty value stays
stored and available to tasks, including through environment-variable
imports and Vercel sync. Deleting a variable remains a separate action.
Also fixes local dev environment precedence so freshly resolved project
values reach task processes correctly.

Mono-RevId: 25dea78a2c72a984a9bd9ac432c11026f220576c
Use Node.js 24 for dashboard agent task deployments.

Mono-RevId: a0d27501dc1ede88a89955bed4dc2f346bed062f
Mono-RevId: e42ec673635842461d2261b89345b9a2cc847db4
Add opt-in preview branch auto-archiving after a configurable period
without deployments. Projects can protect named branches and preview the
effect of their settings before saving. Availability is controlled by an
organization or global feature flag and is disabled by default.

Mono-RevId: 96ef71472cc849ed8fffbef47fc4503987ba6288
Each server process opened Redis pub/sub subscriptions for every
socket.io namespace at startup, including namespaces it would never
serve a connection on. Every broadcast was therefore delivered to every
process, and all but the few holding a relevant connection discarded it.

Subscriptions are now opened per namespace the first time that process
serves a connection on it, which keeps broadcast fan-out to the
processes that can act on it. Behaviour is unchanged for a single
process deployment, where the first worker or dev worker connection
opens the same subscriptions as before.

Mono-RevId: c14a7b60cd4303f463a24c6e044b4d48cc25e981
Balance internal test files across packages and split the workload
across two runners to reduce CI waits. Preserve package test
configuration, refresh duration estimates, and combine all shard reports
for timing updates.

Mono-RevId: f43f740e7a8db6a812a157a8a02b2a15a7ac66fa
When the server returns `200` without records, chat subscriptions can
retry indefinitely. The client stall timer fires after 60 seconds,
before S2 closes the response at its 120-second timeout, so the existing
EOF budget never applies. Terminal failures also leave persisted
`isStreaming` state active, so a reload starts another subscription.

Normal chat subscriptions now permit five reconnects after stall
timeouts. A separate stall counter preserves unlimited retries for
retryable connection failures, fetch timeouts, and browser wakeups.
Decoded records restore the stall budget. Terminal failures clear state
only for the owning subscription, and watch subscriptions remain
unlimited.

The existing stall timer already ignores keepalives because the parser
drops them before the timer reset. The comment correction does not
change that behavior.

## Checklist

- [x] The PR title follows the contribution convention.
- [x] The changes include tests and a changeset.
- [x] Local package builds, formatting, lint, and knip passed.
- [ ] Maintainer CI and reference-project validation.

## Testing

- Regression tests reproduce timeout exhaustion, indefinite stalls,
stale terminal state, and recovery beyond five connection failures.
- Full suites at `8149b96`: 695 SDK tests and 1,122 core tests passed.
- Final error-message and constructor changes: all 43 stream tests
passed.
- Core and SDK builds passed.
- Repository formatting, lint, and knip passed.
- General and security review passes found no remaining defects.
- The debug-marker check reports existing markers in
`apps/webapp/app/services/previewAutoArchive.server.ts`; the changed
files contain none.

The tests use local HTTP servers. They cover separate stall limits,
fetch timeouts, body failures, keepalives, progress resets, mixed
failures, wakeups, cancellation, watch recovery, and replacement
ownership. Core tests exercise stall exhaustion with short timers. The
SDK's six-minute silence window needs reference-project validation.

## Changelog

Silent chat subscriptions now report `Stream stalled: no records
received` after five stall retries. Network failures retain automatic
recovery, and watch subscriptions remain unlimited.

## Risk

| Condition | Result |
| --- | --- |
| Retryable connection or fetch failure | No finite retry deadline;
exponential backoff continues |
| Repeated connected silence | The sixth 60-second stall ends the
subscription |
| Browser wake or `online` event | Reconnect without consuming or
restoring the stall budget |

With immediate response headers, six silent attempts take 368.5–377
seconds, about 6.1–6.3 minutes. Network delays extend this window. A
healthy tool call with no records can also reach this limit: silence
does not prove that the run is dead. The terminal error stops automatic
client resumption but does not cancel the server-side run.

Shared core consumers retain their configured retry limits. Repeated
successful responses without records now increase backoff until a
decoded record arrives. Caller cancellation and token-refresh limits
remain unchanged.
…, overrides, metrics, and management API

Queues get richer concurrency controls: a total concurrency limit across
all concurrency keys of a queue (alongside the existing per-key limit),
named concurrency limits declared in code and shared across tasks, and
trigger-time selection between them. The dashboard gains a Concurrency
page with per-queue metrics charts and an override dialog for per-key
and total bounds, plus a management API for listing, retrieving,
overriding and resetting concurrency limits. Chat sessions can set
concurrency options when triggering. Run admission gates in the engine
enforce the new limits and are off by default behind
RUN_ENGINE_QUEUE_GATES_ENABLED.

Mono-RevId: b8b2c98fae12cf9abb09b3f1349fd828d053ab70
…run() (#4952)

## Summary

Idempotency keys deduplicate task triggers (`trigger()`,
`triggerAndWait()`, `batchTrigger()`). They do not make the code inside
a task's `run()` idempotent: when a run retries, `run()` executes again
from the top, and any API call inside it happens again unless that call
is itself idempotent.

The idempotency page listed "avoiding double-charging customers" as a
use case, but every example on the page only ever deduplicated a child
trigger, so a reader could reasonably assume a payment call made
directly inside a retryable `run()` was protected. It is not, and no
Trigger.dev-side mechanism can protect it: only the payment provider can
deduplicate its own API call, so the provider's idempotency key has to
be passed too.

This is a docs-only change. No SDK changes.

## What changed

- The intro now states the boundary up front: the key applies to the
trigger call and nothing else.
- The use-case bullets say "trigger the X task once" rather than
implying the side effect itself is deduplicated, and the payments bullet
points at the new section.
- A new "Side effects inside `run()`" section explains why moving the
call into a child task does not close the gap either, and shows a Stripe
refund passing Stripe's `idempotencyKey` alongside a Trigger.dev key for
the follow-up email trigger.

Addresses the documentation side of
[#4627](#4627).
Supersedes
[#4650](#4650), which
was auto-closed by the vouch gate.
Keep steering messages in context across later agent steps and preserve
their original position in saved conversations and subsequent turns.
Persist managed custom response data in stream order and retain prepared
steering input when a conversation resumes in a new worker.

Mono-RevId: 0c642ea561d632dabb68ad024aaff4e7e708d114
Mono-RevId: 5997ba1b23b730bd390acfc3ddd3c8c60f06e00a
)

## Summary

Follow-up to
[#4952](#4952). The new
"Side effects inside `run()`" section told readers to derive the
provider's idempotency key from a payload ID, but the more general
answer is the run ID: `ctx.run.id` is stable across every attempt of a
run, is always available, and is the same value the default `run` scope
already mixes into a Trigger.dev idempotency key.

The Stripe refund example now keys the provider call on `ctx.run.id`,
and a short paragraph explains when a business ID from the payload is
the better choice (when the same task can be triggered more than once
for the same order and you want deduplication across separate runs).

Docs only.
Mono-RevId: b32e17efc094e8641bd7e2ccc544a9c9aeae470f
Add a CI guard that checks new Prisma migrations for idempotent,
lock-safe DDL: creates must use IF NOT EXISTS, drops IF EXISTS, CREATE
TYPE, ADD CONSTRAINT and RENAME must sit in a guarded DO block, INSERTs
need ON CONFLICT, and indexes on existing tables must be built
CONCURRENTLY in a single-statement migration. The enforced cutoff date
is pinned in the `guard:migrations` script in
`apps/webapp/package.json`; `-- --all` audits the whole history locally.
A `-- migration-guard: allow <reason>` comment opts a single statement
out.

Mono-RevId: 7c937700bc4eae8b5b51fecdb60b620bf3c9807b
Mono-RevId: c77f5700f7d4a98e7e2a86685f4a7285960e82cd
…erify tokens

## Summary

Hosted webhook sources can now describe how the provider expects to be
answered, and the ingress honors it. This is the server side for
providers like Discord and WhatsApp, whose SDK sources follow in a later
release.

- A verifier artifact can declare a response contract as data: the
status code for a handshake answer (`respondStatus`), and the codes
returned for accepted deliveries and rejected signatures
(`response.acceptedStatus`, `response.rejectedStatus`). The ingress and
the dashboard's send action map every outcome through the same helper,
so a source that needs 204 on success and 401 on a bad signature gets
exactly that.
- A verifier artifact can declare a GET verification flow
(`getHandshake`) for providers that confirm a callback URL with a
challenge, such as Meta's `hub.challenge`. The ingress answers GET on
the endpoint URL against a dedicated verify token, which you generate
and reveal from the endpoint's Connect panel. The token is its own
credential, separate from the signing secret.
- HMAC verifiers can read the signed timestamp from a body field. The
Linear provider uses it for a 60 second replay window on
`webhookTimestamp`; deliveries are deduplicated on the signed request
itself, so a replay with a different unsigned delivery header is still
recognised as a duplicate.
- The dashboard's test-send re-signs a recorded sample with the current
timestamp, so sources with a body-timestamp replay window accept it.
- An admin API action bootstraps delivery partitions in the configured
webhook database before enablement. It is safe to repeat and preserves
existing partitions. Bootstrap and daily maintenance can use
`WEBHOOK_DATABASE_DIRECT_URL` with separate owner credentials while
application queries use `WEBHOOK_DATABASE_URL`. When the direct URL is
unset, partition operations reuse the webhook writer.
- The index worker protocol gains a message for duplicate webhook ids,
so the CLI can report which files define the same id. The CLI side of
this ships with the SDK release that adds `webhook()`.

Mono-RevId: 54862ef81ec5adc311aa42783020fedc4b8c3ba9
Explain when to collect a tool result with `addToolOutput` and when to
combine `needsApproval` with `execute`. Cover choosing from search
results in the human-in-the-loop guide, including server-side selection
checks and stable operation IDs for retries.

Clarify transcript restoration and the limits of tool-result filtering
for external side effects. Update the frontend approval examples and
link them to the selection guidance.

Mono-RevId: 6c6be1ef71ea4846ba9929179edf48eee77a2af7
Mono-RevId: c893ea0934c69e8fe8ec6f45eaf7be8487e4886b
Clarify chat.agent setup and recovery with examples that check chat
ownership before starting sessions or refreshing tokens, use managed
streamText for steering, and preserve partial responses after failures.
Explain how to load transcripts before resuming the frontend and stop a
resumed generation.

Update the branching guide to use transcript storage and an explicit
active branch, and add checks for transport behavior, error recovery,
and branch isolation.

Mono-RevId: 071c63091f9605d84806381d97311f2fb1583eea
Mono-RevId: b882af2b96296eb4b1010903fe41af0b2b612572
ericallam and others added 30 commits October 14, 2026 09:32
Cuts real-time waits from the slowest container test suites.

- The schedule engine lifecycle test fakes `Date` (timers stay real, so
the worker keeps polling), waits for each next run to be enqueued, and
moves the clock to its due time, so it no longer waits two real cron
minutes.
- Run queue and engine tests that expect an empty dequeue use a 1s
blocking pop in place of the 10s default.
- Webhook job queue retry tests use a 20ms retry base delay, like the
other webhook suites.
- Lock timeout tests hold the blocking lock only about twice as long as
the retry window.

```ts
vi.useFakeTimers({ toFake: ["Date"], shouldAdvanceTime: true });

const job = await engine.getJob(`scheduled-task-instance:${instanceId}`);
vi.setSystemTime(job.timestamp);
```

Mono-RevId: f4c095985459517532f65f2945a124aad77373f3
Runs webapp unit test files in parallel (half the available cores) and
cuts that CI matrix from 24 shards to 6. Webapp e2e files run four at a
time on one shard instead of one at a time on two.

Mono-RevId: e62ad92a646140b8f97d75ed6978d4f698e14238
)

## Summary

Supervisor images built from `main` are only published under the mutable
`main` tag, so there is no way to pin a deployment to the exact commit a
build came from. The webapp publish already pushes a full commit-sha tag
next to `main` for this reason. This adds the same block to the
supervisor publish, so every main build is also available as
`ghcr.io/triggerdotdev/supervisor:<sha>`.

Tag builds (`re2-*`, `build-*`, `v4.x.y`) are unchanged.
Build env vars of `--local-bundle` deploys are no longer cleared when a deployment finishes. They are kept for `DEPLOYMENTS_REDEPLOY_WINDOW_DAYS`, then cleared by an hourly sweep that uses a new partial index on `WorkerDeployment.createdAt`. Finished deployments still never get their build env vars served.

Mono-RevId: 5ccba57214529ad4809bf997c4a5d3ff548fecd5
Keep blank Vercel imports from overwriting existing values when the
default-off empty environment variable feature is enabled.

Mono-RevId: 46b3866fb5682306bce50a89af04a21178cd0214
…ead of 14

Finished runs' execution snapshot state in the Redis snapshot store now
expires after 4 days instead of 14, keeping the store's memory bounded
to a few days of completions. Runs that are still active are unaffected,
and runs that finished before this change keep their existing expiry.

Mono-RevId: 6609ff96d5f3872e964e73072f0465e5fba4f1db
… state as already finished

Once a finished run's execution snapshot state has aged out of the Redis
snapshot store, operations that only needed to know the run had finished
were failing instead of completing: cancelling the run, an idempotent
`triggerAndWait` that hit the cached run, a late waitpoint completion,
the queue repair tool, the dev TTL job, and a stale queue message. The
store now reports expiry with a distinct, non-retriable error, and each
of those paths treats a finished run whose snapshot state has expired as
already finished, using the run record. Expired state on a run that is
not finished still fails closed.

Mono-RevId: ee089985af8a7c4e6bc1ecbac38554dcd7ed53b2
… to client timeouts and store outages

Runs on the Redis execution snapshot store are now resilient to
client-side timeouts and short store outages. A transition whose Redis
publish timed out after the database had committed is proved or retried
instead of reported as a failure. Snapshot reads retry briefly on
transient client errors. Heartbeats keep the stall timer armed while the
store is unavailable, so an outage no longer ends in runs being crashed
as out of memory. Redis-resident reads do less work on the database per
read.

Mono-RevId: 457df0f19243b52ddcb6685ee679f5f052c1c86f
…tes mid-build

Fix a test fixture race where the shared Postgres test container could
drop a template database while it was still being built.

Mono-RevId: 242d60bf90ab9b07773be392a3feb66ce8f8ddaf
## Summary
5 new features, 12 improvements, 6 bug fixes.

## Improvements
- Self-hosted instances can require custom deploy base images per
runtime via the new `DEPLOY_BASE_IMAGES` and `DEPLOY_BUILD_BASE_IMAGES`
webapp settings. The CLI builds on the images the instance specifies,
and older CLIs are rejected with an upgrade message.
([#5005](#5005))
- Add a `replay_run` tool to the MCP server, so your AI assistant can
replay a run, optionally with a new payload, metadata, tags, machine,
version, delay or other trigger options. It is hidden in `--readonly`
mode.
([`ff2aa236e`](ff2aa23))
- Override a concurrency limit's `perKey` for a single `concurrencyKey`:
`concurrencyLimits.override(name, { concurrencyKey, perKey, ttl })` caps
that one key under that one limit for a while (7 days by default, 30 at
most), `reset(name, { concurrencyKey })` clears it, and `retrieve(name,
{ concurrencyKey })` shows the key's bound and when the override
expires.
([`c49fd05f4`](c49fd05))
- `runs.replay()` now takes options, so you can replay a run with a
different payload, metadata, tags, queue, machine, version, delay and
other trigger options. Anything you leave out comes from the original
run, as before, and the payload is serialized like `trigger()`, so
values like `Date` and `BigInt` keep their types.
([`ff2aa236e`](ff2aa23))
  
  ```ts
  import { runs } from "@trigger.dev/sdk";
  
  await runs.replay("run_1234", {
  payload: { userId: "user_456" },
  tags: ["replayed"],
  delay: "10m",
  });
  ```

## Server changes

These changes affect the self-hosted Docker image and Trigger.dev Cloud:

- Self-hosted instances can add a Cloudflare Turnstile bot check to the
magic-link sign-in form by setting `TURNSTILE_SITE_KEY` and
`TURNSTILE_SECRET_KEY`. It's off unless both are set.
- Recent deployments started from the GitHub integration or a native CLI
deploy can be redeployed from the deployments list, rebuilding the same
commit or upload as a new deployment.
- Each webhook endpoint's request rate limit now comes from the
organization's plan and can be raised per organization, and the limits
page shows it.
- Each environment's webhook endpoints now share a rate limit that
counts larger request bodies as more than one request, set by the
organization's plan and shown on the limits page.
- Webhooks now appear in the dashboard side menu with a Beta badge, and
organizations without access can request beta access from the Webhooks
page. The New badges on Sessions, Prompts and Models are gone.
- The new API key dialog's access summary now shows Errors, Webhooks and
Waitpoints access, and API keys with waitpoint access can create, list
and retrieve waitpoint tokens.
- Log search uses whole-word matching for ASCII alphabetic terms, keeps
substring matching for other terms, and incrementally searches older
logs without blocking the page.
- Magic links now only sign you in from the browser that requested them,
so a link opened elsewhere (including by an email security scanner) no
longer logs anyone in. Self-hosted instances can turn this off with
`MAGIC_LINK_SAME_BROWSER_REQUIRED=false`.
- The multi-factor authentication login page now focuses the code input,
accepts pasted codes, and submits as soon as all six digits are entered.
- Dashboard tabs left in the background now pause their live updates
after 30 seconds and catch up as soon as you return, so idle tabs stop
reloading data nobody is looking at.
- Preview branch auto-archiving is now always available, with no feature
flag to enable first.
- Large run traces now load in chunks and update live without reloading
the page, on child run pages as well as root runs.
- The run page shows one clear trace status next to Cancel and Replay:
whether the run is live, still loading, only partially shown, or failed
to load (with a retry). Search, filters and the run actions now share a
single full-width toolbar.
- Keep blank Vercel imports from overwriting existing values when the
default-off empty environment variable feature is enabled.
- Errors on the login page, such as an expired or invalid magic link, no
longer disappear a moment after the page loads in a new browser.
- Magic-link sign-in emails now always link to `LOGIN_ORIGIN`, whatever
host the request came in on.
- Parent and root metadata updates from child tasks no longer stall
behind a parent run that is being updated by many children at once, and
can optionally be buffered in Redis so they survive a webapp restart.
- Revoking a personal access token is now permanent. Logging in with the
CLI again issues a new token instead of reinstating the revoked one, so
you can rotate a leaked CLI token by revoking it and running `trigger
logout` then `trigger login`.
- Webhook deliveries from a provider's shared IP addresses are no longer
throttled by other customers' traffic or by a spoofed `X-Forwarded-For`
header; the per-IP limit now applies only to requests for endpoints that
don't exist, and every ingest 429 includes `Retry-After`.

<details>
<summary>Raw changeset output</summary>

# Releases
## @trigger.dev/build@4.7.4

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.7.4`
## trigger.dev@4.7.4

### Patch Changes

- Self-hosted instances can require custom deploy base images per
runtime via the new `DEPLOY_BASE_IMAGES` and `DEPLOY_BUILD_BASE_IMAGES`
webapp settings. The CLI builds on the images the instance specifies,
and older CLIs are rejected with an upgrade message.
([#5005](#5005))
- Add a `replay_run` tool to the MCP server, so your AI assistant can
replay a run, optionally with a new payload, metadata, tags, machine,
version, delay or other trigger options. It is hidden in `--readonly`
mode.
([`ff2aa236e`](ff2aa23))
- Updated dependencies:
  - `@trigger.dev/core@4.7.4`
  - `@trigger.dev/build@4.7.4`
  - `@trigger.dev/schema-to-json@4.7.4`
## @trigger.dev/core@4.7.4

### Patch Changes

- Override a concurrency limit's `perKey` for a single `concurrencyKey`:
`concurrencyLimits.override(name, { concurrencyKey, perKey, ttl })` caps
that one key under that one limit for a while (7 days by default, 30 at
most), `reset(name, { concurrencyKey })` clears it, and `retrieve(name,
{ concurrencyKey })` shows the key's bound and when the override
expires.
([`c49fd05f4`](c49fd05))
- Self-hosted instances can require custom deploy base images per
runtime via the new `DEPLOY_BASE_IMAGES` and `DEPLOY_BUILD_BASE_IMAGES`
webapp settings. The CLI builds on the images the instance specifies,
and older CLIs are rejected with an upgrade message.
([#5005](#5005))
- `runs.replay()` now takes options, so you can replay a run with a
different payload, metadata, tags, queue, machine, version, delay and
other trigger options. Anything you leave out comes from the original
run, as before, and the payload is serialized like `trigger()`, so
values like `Date` and `BigInt` keep their types.
([`ff2aa236e`](ff2aa23))

  ```ts
  import { runs } from "@trigger.dev/sdk";

  await runs.replay("run_1234", {
    payload: { userId: "user_456" },
    tags: ["replayed"],
    delay: "10m",
  });
  ```
## @trigger.dev/python@4.7.4

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/sdk@4.7.4`
  - `@trigger.dev/core@4.7.4`
  - `@trigger.dev/build@4.7.4`
## @trigger.dev/react-hooks@4.7.4

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.7.4`
## @trigger.dev/redis-worker@4.7.4

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.7.4`
## @trigger.dev/rsc@4.7.4

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.7.4`
## @trigger.dev/schema-to-json@4.7.4

### Patch Changes

- Updated dependencies:
  - `@trigger.dev/core@4.7.4`
## @trigger.dev/sdk@4.7.4

### Patch Changes

- Override a concurrency limit's `perKey` for a single `concurrencyKey`:
`concurrencyLimits.override(name, { concurrencyKey, perKey, ttl })` caps
that one key under that one limit for a while (7 days by default, 30 at
most), `reset(name, { concurrencyKey })` clears it, and `retrieve(name,
{ concurrencyKey })` shows the key's bound and when the override
expires.
([`c49fd05f4`](c49fd05))
- `runs.replay()` now takes options, so you can replay a run with a
different payload, metadata, tags, queue, machine, version, delay and
other trigger options. Anything you leave out comes from the original
run, as before, and the payload is serialized like `trigger()`, so
values like `Date` and `BigInt` keep their types.
([`ff2aa236e`](ff2aa23))

  ```ts
  import { runs } from "@trigger.dev/sdk";

  await runs.replay("run_1234", {
    payload: { userId: "user_456" },
    tags: ["replayed"],
    delay: "10m",
  });
  ```

- Updated dependencies:
  - `@trigger.dev/core@4.7.4`

</details>

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Oskar Otwinowski <oskar.otwinowski@gmail.com>
Support threads sent from the dashboard contact form now carry the
organization they were sent from: the org page in the submitted path, or
the user's only org when sent from outside an org page. The lookup is
scoped to the user's own memberships, and a failed lookup sends the
message without an org instead of dropping it.

Mono-RevId: 7e276dffb2951e420a26f437bffa29a6a8e9e909
…g to

Fix `batchTriggerAndWait` parents that could stay executing forever
after all of their child runs finished. When creating the batch was
retried after the first attempt had already blocked the parent, the
parent was later resumed with its child results labelled with the
abandoned batch's id, so the SDK never matched them. The resume now
takes the batch from the child results themselves.

Mono-RevId: 147329aad61a4e6b4b81a14dd0fca68ac49f2559
…available

Docs pages for features that are in private beta or rolling out can now
show a status callout, and a check makes sure each callout references an
existing feature flag.

Mono-RevId: 65fe0273a31f568f7731682172d8eb400d4efaea
Mono-RevId: bb6728e6f219c8de7fd58500bb0629e311aecea4
Webhooks private beta docs: pnpm-first install steps, and how to exempt
the beta packages from a package manager's minimum release age policy.

Mono-RevId: bef46032aba4ee4d7a250410e2cb5e5be89b1cb5
Mono-RevId: 743b73b64a31d3c8940df5075086791f2c7d3cf1
The natural-language field on the schedule form called
`gpt-3.5-turbo-1106`, which OpenAI has retired, so every request
returned a 500. It now uses `gpt-4o-mini`, the same model as the
webapp's other AI features.

Mono-RevId: 677615f12c210b122bdd15512ac0d117519f5bb9
Fixes [#5030](#5030).

Aborting a run subscription's signal after breaking out of the loop, or
after the stream had ended or errored, threw an uncaught `TypeError:
Controller is already closed` that crashed Node. Breaking out also left
the subscription polling for updates until the signal aborted.

Fix:
- `createAsyncIterableReadable` tracks whether the stream is closed and
only enqueues or closes while it is open. It handles an already aborted
signal, swallows `reader.cancel()` rejections, removes its abort
listener, and implements `cancel()`.
- Cancelling a `RunSubscription`, or its `withStreams()` stream, aborts
the subscription's internal controller, so a `break` stops the shape
stream from polling.
- When a subscription ends, its `withStreams()` background pipes stop,
its delayed stop timer is cleared so Node can exit, and its listener on
the caller's signal is removed.
- `subscribeToRun`, `subscribeToRunsWithTag` and `subscribeToBatch` on
`ApiClient` accept and forward a custom `fetchClient`.

Verified with the repro from the issue against a local build: it now
exits cleanly instead of crashing, and a subscription that broke out of
its loop makes no more requests.

Mono-RevId: 9e75d7d873d67e401be0f78a27c77e71d9f3eeeb
Add `billing` to the `/_` deeplinks. `/_/billing` now redirects to the
billing page of the user's current org, the same way `/_/projects`
already works. Org-level deeplinks now all resolve under the org
settings path, so adding another one is a one-line change.

Mono-RevId: 2080269e9a03554890acdf1a8cd6c6858cb6c9f4
…tion

Webhook verification can read timestamps written as ISO 8601 date-times
or HTTP dates, not only unix numbers, so a source can enforce a replay
window for providers that send dates. A header value can also list
fallback headers, read in order when the primary header is missing or
empty.

```ts
timestamp: {
  source: { from: "header", name: "x-metronome-date", fallback: ["date"] },
  format: "http-date",
  toleranceSeconds: 300,
}
```

`format` is `unix` (the default, using `unit`), `iso8601` or
`http-date`. When the dashboard signs a test delivery, it writes the
current time in the same format, so test sends verify under the window.

Mono-RevId: d68d3cd4824313c145b9281fa416e4ecd3caa0fc
Recent `--local-bundle` deployments can now be redeployed from the dashboard. A redeploy copies the encrypted build env vars onto the new deployment and rebuilds the same bundle. Bundle deploys now always store build env vars, even when there are none, so every new bundle deploy can be redeployed.

Mono-RevId: 0d4b32618fccc9347fd990e5eb70394a67f751ce
…ime says OOM

Runs whose process is killed are only reported as out of memory when the machine actually ran out of memory. Previously any SIGKILL (exit code 137), e.g. a node shutting down, an eviction or a manual kill, was reported as TASK_PROCESS_OOM_KILLED after the heartbeat timeout. The runtime's own reason is now used, so a real out of memory kill still fails with TASK_PROCESS_OOM_KILLED and retries on a larger machine if configured, while any other kill fails as TASK_PROCESS_EXITED_WITH_NON_ZERO_CODE and is retried using the task's retry settings on the same machine.

Mono-RevId: dc88e94b2835521837b6c8f2c0a4f19d198fac68
Mono-RevId: 474404593850c009e5386a2b72442f4d28e8c729
…3 at 12.11.1

Floor better-sqlite3 at 12.11.1. better-sqlite3 11.x has no prebuilt
binary for Node 24, so `pnpm install` on the repo's Node version
compiled it from source, which took about two minutes. Nothing in the
repo loads it, and the floor also removes its stale lockfile entry.

Mono-RevId: d734572eec38d0d7e249723fa96777835448ba73
… and chat.agent telemetry options

Span content can now be redacted before it leaves the worker.

```ts
// trigger.config.ts
export default defineConfig({
  telemetry: { spanProcessors: [redactSpans] },
});

// chat agent
chat.agent({
  id: "support",
  telemetry: { recordUserMessage: false, recordClientData: false },
  run: async ({ messages, signal, streamText }) =>
    streamText({ model, messages, abortSignal: signal }),
});
```

`telemetry.spanProcessors` runs your processors ahead of every span
export, both to Trigger.dev and to any `telemetry.exporters`. Before
this, the only hook was an extra exporter, which received its own copy
of each span, so a redacting exporter could not change what the run
trace showed.

User processors are wrapped so they can't break the trace:

- They run before the processor that emits partial spans (the
in-progress copy shown while a span is open), and partial spans pass
through them too, so redaction covers both the partial and the final
span.
- After each call, the attributes used to link a span to its run and to
pair partial and final spans are restored: run, task, attempt,
environment, organization, project, machine and worker ids (bare and
under `$metadata`), the conversation id, partial span ids, entity type
and id, icon and variant. Everything else stays redactable, including
run tags, idempotency keys, the span preview text and `$output`.
- `onStart`, `onEnding` and `onEnd` are all forwarded. An exception
thrown by a user processor goes to the OTel global error handler and
does not escape into `startSpan` or `span.end()`.

Redact in `onEnding`: the span is complete and `setAttribute` still
works. `onStart` runs before run context is added, and spans are
read-only by `onEnd`. Content in span events lives on `span.events`. The
`spanProcessors` JSDoc has an example that scrubs attributes and events.

`chat.agent({ telemetry })` turns off recording on the chat runtime's
own spans. `recordUserMessage: false` drops `chat.user_message`, the
turn span's preview text and `pending.messages` (messages sent
mid-turn). `recordClientData: false` drops `chat.client_data`. Both
default to `true`. The AI SDK's own spans are still controlled by
`experimental_telemetry`.

Docs: a Span processors section on the config file page, the `telemetry`
option in the AI Agents reference, and a new AI Agents pattern page,
Redact chat content from traces, covering what a chat agent's trace
records, each control, and what session streams and the run payload
still hold.
Mono-RevId: 8056b1c53d7525ae05814caabb97b05e0bac86d8
Mono-RevId: 8184a644047f2e4ac9e2441344de4748f087960d
… Pro plans

Show the $500/month price for the HIPAA BAA add-on on the Hobby and Pro
plans in the plan picker. The Enterprise card stays unpriced.

Mono-RevId: 7f0e1cdc0f122349a1e3dfe87ba37ad3d369dead
Org admin provisioning endpoints (runs enable/disable, concurrency, staging environments) now queue background jobs and return 202 instead of updating every environment inside the request, and skip archived preview branches. The billing page shows whether a plan change is still being applied, is active, or failed.

Mono-RevId: 4fed1b88f5a4086c0a4fc1b8c0486c4340c2908f
The Schedules row on the Limits page now has a "Manage" button that lets
you purchase or reduce extra schedules directly, using the same dialog
as the scheduled task page.

Mono-RevId: 465b118b16c3d669e0a5a8c902e93a4df05e62c9
Generating a cron expression from natural language in the schedule side
panel now fills in the cron field. Previously the panel reloaded after
generating and reset the form, so the result was lost. The generator
also now handles requests like "the last Friday of the month" and "the
second Tuesday of the month", which it previously refused.

Mono-RevId: fc8969d083839c65b04ba20a0fefe7c05e6f8bf0

This branch has not been deployed

No deployments
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.