Trigger.dev v4.7.4

Per-key concurrency overrides, much faster run traces for large and live runs, and runs.replay() options to change the payload and trigger settings on replay.

4 improvements and 14 server changes.

Highlights

Per-key concurrency overrides

You can now raise or lower a named concurrency limit's perKey cap for a single concurrencyKey, without touching the limit itself or any other key. Give one tenant, user, or chat session more (or fewer) concurrent runs than everyone else, straight from your code:


import { concurrencyLimits } from "@trigger.dev/sdk";
// this session may run 3 at once under "subagents", for the next 7 days
await concurrencyLimits.override("subagents", {
concurrencyKey: sessionId,
perKey: 3,
ttl: "7d",
});
// read it back: the key's bound, the override, and when it expires
const view = await concurrencyLimits.retrieve("subagents", { concurrencyKey: sessionId });
console.log(view.perKey.override, view.perKey.expiresAt, view.running);
// clear it early
await concurrencyLimits.reset("subagents", { concurrencyKey: sessionId });

Per-key overrides are temporary: they expire after ttl (7 days by default, 30 at most), and calling override again extends them. The override applies to every task holding the limit, other limits the run holds still apply on top, and perKey: 0 blocks the key entirely. Lowering a cap takes effect at the next run to start, so runs already executing continue. (c49fd05f4)

Read more in the concurrency docs.

Faster run traces for large and live runs

The run page now handles much bigger traces, and stays fast while they grow:

  • Large traces load in chunks, showing up to 150k spans, instead of all at once.
  • Live runs update incrementally. The page fetches only the spans written since its last read and merges them into the tree, rather than reloading the whole trace on every update.
  • Child run pages read only the child's part of the trace, with the same progressive loading and live updates as root runs.
  • Scrolling the trace tree stays smooth as the trace grows.
  • One trace status next to Cancel and Replay tells you whether the run is live, still loading, partially shown, or failed to load (with a retry). Search, filters, and run actions share a single full-width toolbar.

The run trace API (GET /api/v1/runs/:runId/trace) can page large traces too: pass page[size] and page[after] to read every span of a run. Requests without these parameters are unchanged.

runs.replay() with options

runs.replay() now accepts an options object, so you can override the payload, metadata, tags, queue, machine, version, delay, and other trigger settings for the replay. Anything not specified carries over from the original run. Payload values are serialized the same way as trigger(), so types like Date and BigInt are preserved.


import { runs } from "@trigger.dev/sdk";
await runs.replay("run_1234", {
payload: { userId: "user_456" },
tags: ["replayed"],
delay: "10m",
});

The MCP server also gets a replay_run tool so your AI assistant can replay runs with the same options. It is hidden in --readonly mode. (ff2aa236e)

Improvements

  • Self-hosted instances can require custom deploy base images per runtime via the DEPLOY_BASE_IMAGES and DEPLOY_BUILD_BASE_IMAGES webapp settings. The CLI builds on the images the instance specifies, and older CLIs receive an upgrade message. (#5005)

Server changes

These changes are included in the v4.7.4 Docker image and are already live on Trigger.dev Cloud:

  • Recent deployments 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.
  • The API key dialog's access summary now shows Errors and Waitpoints access. API keys with waitpoint access can create, list, and retrieve waitpoint tokens.
  • Log search uses whole-word matching for ASCII alphabetic terms and substring matching for others, and incrementally searches older logs without blocking the page.
  • Magic links now only sign you in from the browser that requested them. A link opened elsewhere (including by an email security scanner) no longer logs anyone in. Self-hosted instances can opt out with MAGIC_LINK_SAME_BROWSER_REQUIRED=false.
  • The MFA login page now focuses the code input automatically, accepts pasted codes, and submits as soon as all six digits are entered.
  • Dashboard tabs left in the background pause their live updates after 30 seconds and resume when you return.
  • Preview branch auto-archiving is now always available without a feature flag.
  • Blank Vercel imports no longer overwrite existing values when the default-off empty environment variable feature is enabled.
  • Errors on the login page (such as expired or invalid magic links) no longer disappear moments after the page loads.
  • Magic-link sign-in emails now always link to LOGIN_ORIGIN, regardless of which host the request came in on.
  • Parent and root metadata updates from child tasks no longer stall behind heavily-updated parent runs, and can be buffered in Redis to survive a webapp restart.
  • Revoking a personal access token is now permanent. The CLI issues a new token on next login instead of reinstating the revoked one, so you can rotate a leaked token with trigger logout then trigger login.

How to upgrade

Update the trigger.dev/* packages to v4.7.4 using your package manager:


npx trigger.dev@latest update # npm
pnpm dlx trigger.dev@latest update # pnpm
yarn dlx trigger.dev@latest update # yarn
bunx trigger.dev@latest update # bun

Self-hosted users: update your Docker image to ghcr.io/triggerdotdev/trigger.dev:v4.7.4.

Deploy your first agent today

Get set up in minutes, either on your own or with your coding agent.

Get started for free