Skip to content

Update trigger - #4

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

tylerc-govsignals wants to merge 2104 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]

💯

ericallam and others added 29 commits October 2, 2026 20:40
…n turns

```ts
export const dashboardAgent = chat.agent({
  idleTimeoutInSeconds: 300,
  turnTimeout: "7d",
  maxDuration: 86_400,
  // ...
});
```

The dashboard agent now stays warm for 5 minutes after each answer and,
once suspended, waits up to 7 days for the next message. Quick
follow-ups answer from the same live run, returning to a chat days later
resumes it rather than starting a new run, and the chat run's compute
limit is raised to a day so long conversations aren't cut off.

Mono-RevId: 7b7ac10420f36e9cc76b371b40faa9b7245e1b48
… org, project, env and user

```ts
await startChatSession({
  chatId,
  clientData,
  tags: [`org:${org.slug}`, `user:${user.id}`], // the Session's own tags
  triggerConfig: { tags: [`org:${org.slug}`] }, // tags on its runs, as before
});
```

`chat.createStartSessionAction`, `chat.headStart` and
`chat.startHeadStart` now accept `tags` for the Session itself. Until
now they only forwarded `triggerConfig.tags`, which tag the session's
runs, so chat sessions couldn't be filtered on the Sessions page.

The dashboard agent uses this to tag every chat's session and runs by
organization, project, environment and user. Run tags come from the
session's trigger config, so runs the session starts later carry them
too.

Mono-RevId: af4ac8b786f6f6b13b2f5c5aab8a6af4e2ab5113
Prepare the dashboard agent for Claude Sonnet 5.5 without changing its
default model.

```ts
withoutThinking("claude-sonnet-5-5"); // { anthropic: { thinking: { type: "between_tools" } } }
withoutThinking("claude-sonnet-5");   // { anthropic: { thinking: { type: "disabled" } } }

turnProviderOptions("claude-sonnet-5-5");
// { anthropic: { effort: "medium", thinking: { type: "adaptive", display: "updates" } } }
turnProviderOptions("claude-sonnet-5"); // undefined: API defaults, as before
```

Sonnet 5.5 no longer accepts `disabled` thinking, so summaries and
bounded wakes now send each model's own off switch (`between_tools` on
Sonnet 5.5). Main turns and the head-start step send the same per-model
options, so the prefix the head-start caches stays valid for the run.
Sonnet 5.5 gets an explicit effort because its levels are recalibrated
(default `medium`; `DASHBOARD_AGENT_EFFORT` overrides it).

Sonnet 5.5 returns the notes it writes before a tool call as thinking
blocks, and in the dashboard agent those notes are often the answer
itself. The agent now requests `display: "updates"` so they come back
with their text, and the panel renders a reasoning part with text as
part of the answer. Thinking itself still comes back empty and renders
nothing.

The agent's history also stays append-only, which Sonnet 5.5 needs
because it binds each thinking block to the exact history it followed.
Wake turns keep the turn's tools declared and call none of them.
Compaction drops the reasoning from the messages it keeps after a
summary, since that reasoning was bound to the history the summary
replaced.

On Amazon Bedrock the agent now uses the native Anthropic Messages API
instead of Converse, so it takes the same options as the direct
provider, including `tool_choice: none`, which the agent needs to keep a
wake turn's tools declared. A small patch to `@ai-sdk/anthropic` sends
the tools with `tool_choice: none` rather than dropping them.

Also bumps `@ai-sdk/anthropic` to 3.0.125, the first release that knows
Sonnet 5.5, with an override scoped to that version for the
`@ai-sdk/provider-utils` it needs, and adds the Sonnet 5.5 Bedrock
profile and output ceiling.

Mono-RevId: 53dd6f2570ec7fa77434373c918097ccf9bcf447
… user out of that user's history

```ts
await createChat(db, { id, organizationId, userId, createdByUserId: actingUserId });

listChats(db, { organizationId, userId });                  // the owner: their own chats only
listChats(db, { organizationId, userId, actingUserId });    // the acting user: the owner's chats + theirs
```

A dashboard agent chat started by someone acting on a user's behalf is
no longer saved to that user's history. The chat records who created it:
the owner never sees it in their history, unread count, or by id, and
the user who created it sees it alongside the owner's own chats while
acting as the owner. Such a session can't create watches, so the owner
gets no alerts from it, and its messages don't count against the
organization's message quota.

Mono-RevId: b3fb58f14118f2cb507e6396d7511bfaf0a19d2b
The sessions list now shows a session's tags on one row, the same as the
runs list, instead of wrapping them onto several lines and growing the
row.

Mono-RevId: fa044489406a355c2b252d46f15d2df699314aaa
…dpoint route

Every webhook endpoint route now takes either the endpoint's declared id
(the `id` given to `webhooks.endpoint.define`) or its `wh_` id. Before,
only creating a waiter accepted the declared id; getting, enabling,
disabling and setting or rotating an endpoint's secret needed the `wh_`
id.

```bash
curl -X PUT https://api.trigger.dev/api/v1/webhooks/endpoints/payments/secret \
  -H "Authorization: Bearer $TRIGGER_SECRET_KEY" \
  -d '{"secret": "whsec_..."}'
```

A declared id names the endpoint's declared instance in the environment
the key belongs to. Declared ids can no longer start with `wh_`, which
is reserved for generated ids: `trigger dev` and deploys fail with a
clear error for an endpoint declared as `wh_orders`.

Mono-RevId: a3e8dde7e794525cb86651b16d65001b75eccdbd
…oard agent

The dashboard agent can now report problems with its own tools, the
dashboard, or the docs to the Trigger.dev team with a new
`submit_feedback` tool.

```ts
submit_feedback({
  message: "get_queue returned a 500 for a paused queue; expected the paused row",
  toolName: "get_queue",
});
```

The agent uses it when a tool error looks like a bug, the docs
contradict the product, or it lacks a capability it needed, and it tells
the user what it reported. It never reports the user's own failing runs,
and never includes secrets, payloads, or logs.

Reports go through a new `POST /api/v1/dashboard-agent/feedback` route,
which only accepts the agent's delegated token and attributes the report
to the chat's user, organization, project and environment. Reporting is
fire-and-forget so it never slows the agent down: the event is queued
like the rest of the dashboard's analytics, and a report that doesn't go
through (including on an instance without analytics configured) is
dropped quietly instead of surfacing as an error.

Mono-RevId: b9c67c574d621ab1ec365046f401a9b031b386d2
Mono-RevId: 09dc0eb6741c9fc65417c0538f6b2499c5a8d286
Mono-RevId: 7d7a39b64f16278f6014f95e6c1d1596125898bf
Deployments with an unsupported `runtime` now fail with a 400 that names
the supported runtimes, instead of a generic 500.

Mono-RevId: 82fda1dd3e8d4d224323d828c99f06470e469cac
Adds authenticated, weighted multi-queue consumption with separate
dispatch classes and execution phases. Existing consumers retain their
legacy queue selection; requested subscriptions are validated and
authorized before dequeue.

Mono-RevId: 1b2b009af1ec70bc9bc23eb7d1a9a78cb60862e7
## Summary

Stop the duration-format equivalence test from comparing independently
generated observation timestamps. All other timeline fields remain
covered by the equality assertion.

Verified: all 49 tests in tool-curation.test.ts pass.
Mono-RevId: c679c63a9555eaf894d86aa6522026ec3584f3a9
Session creation requires both session-write and task-trigger
permissions. Cached sessions are checked against their stored task
before refreshing their configuration or starting a run. The API key
scope preview now displays session and tagged-run access.

Mono-RevId: a138ef996f92a325c335c061f1b46e7f67bbca4a
Adds opt-in versioned worker-queue assignment and routes checkpoint
resumes to queues matching the saved runtime, preserving the run's
dispatch class, region and channel. Legacy routing remains available,
and task retries that start fresh return to the original assignment.

Mono-RevId: ffc26bd998a3409f27f5011bb11a8bc2b2919db6
…cknowledgement

Hosted webhook endpoints now accept `application/x-www-form-urlencoded`
bodies. A verified form body that isn't JSON is routed as an object of
its fields, so Slack slash commands, Twilio and other form-posting
providers reach `webhook()` subscribers, agent session routes and
waiters like any JSON event:

```ts
export const miphy = webhook({
  id: "miphy-command",
  endpoint: slackCommands,
  filter: "event.command == '/miphy'",
  onEvent: async ({ event }) => {
    // event = { command: "/miphy", text: "a raccoon dancing", user_id: "U...", response_url: "...", ... }
  },
});
```

A repeated field becomes an array. A source's `formPayload` field (Slack
interactivity's `payload=<json>`) still takes precedence, and
verification is unchanged because it runs over the raw bytes first.

A source's response contract can also set `acceptedBody: "empty"` to
acknowledge with an empty 200 instead of the JSON receipt, for providers
such as Slack that show a non-empty reply to the user.

Mono-RevId: 8ab4361a8ae1e4ae19c029d41929ab6b26a03fb8
Mono-RevId: 617d3a597d6df160d2c82a41d1f5fda5a14322f2
Mono-RevId: 06e3c9f4226cb4fc882cccec3e5943341aa2b756
Mono-RevId: ab8173265f690d9c4ddeaff2d2ae12417fafaabb
Mono-RevId: 614115b256b239d0036903d3cea381e2333e08bc
Mono-RevId: 7fd0191ab689a6a51bac2069f8297ff7e996e25a
Remove the dead `maxRunCount` dequeue option
(`TRIGGER_DEQUEUE_MAX_RUN_COUNT`) from the supervisor, the worker
dequeue request schema, the self-hosting docs and the Helm chart.
Server-side dequeue batching was removed in
#2127 and the field has been accepted and
ignored since; older supervisors that still send it keep working.

Mono-RevId: 7904c59a81b5bf4aa0614cd61f0ab21f7fe663b4
Stopping a `chat.agent` turn whose `run()` returns a `streamText` result no longer sends an `error` chunk saying "An unexpected error occurred". This was a regression in 4.7.x. The turn ends quietly again and the run stays alive for the next message.

Mono-RevId: 63cdf4e7ac05e7f2019b878c9bfeaf5e992f09cb
Add a worker action that lets a supervisor report a failed checkpoint
restore, so the run is requeued or failed promptly instead of waiting
out its stall timeouts.

Mono-RevId: 2a28ec932fcd5256c60369d14d7f793693d19142
… ones

The Kubernetes supervisor now follows checkpoint restores more cheaply,
keeps slow restores from timing out, and reports failed restores so runs
are requeued or failed promptly.

Mono-RevId: eb4bb979ad6c27ec04aca0a99a13b64ecec32bf7
… requeued

Fix a stalled run occasionally not being requeued correctly when its
snapshots are stored in Redis.

Mono-RevId: 10d1c8f9d75d4cbd42475a46b68a1ec40831c7d4
…rovider's id

A webhook delivery routed to an agent session now carries both ids on
its envelope:

```ts
onAction: async ({ action }) => {
  action.deliveryId; // "whd_...": the delivery in the dashboard and deliveries API, new on a replay
  action.externalDeliveryId; // the provider's id, e.g. Stripe's "evt_...", the same across replays
},
```

`deliveryId` previously held the provider's id. It now matches the id
`webhook()` tasks, waiters and the deliveries API use.

Mono-RevId: bc4eb9c3b90fc4b3fe15e3084d637819467a24f7
Mono-RevId: 29cc1d473b94a7dfda68aad60e9f9a44bc0f4ac3
Mono-RevId: 86d44825dca4c41deb80a30fd0a4df8f92ee0d0a
Mono-RevId: c4c0a14ad8d9eb58850c8e3a69d05ccb5149f6fd
mintlify Bot and others added 30 commits October 22, 2026 11:29
)

## Summary

The environment variables page now explains `TRIGGER_DASHBOARD_URL`,
with an example that builds a link to the current run from `ctx.run.id`.

Runs now get this variable set to the dashboard's address on every
instance, and a value set on the project still takes precedence.

Based on [feat(webapp): give runs a TRIGGER_DASHBOARD_URL environment
variable](7ee436b13),
which added the variable to the run environment.

Docs only. Mark ready for review to merge.

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
…5048)

## Summary

The runtime list now shows the Node.js versions deploys actually run on:
`node-22` 22.23.4, `node-24` (and the default `node`) 24.22.1, and
`node-26` 26.12.1.

The deploy base images moved to the October 2026 Node.js security
releases, so the old patch versions on the page were out of date.

Based on [fix(base-images): build node 22, 24 and 26 on the October 2026
Node.js security
releases](40f241b34),
which bumped the base images.

Docs only. Mark ready for review to merge.

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
… security releases

Deployed tasks on node-22, node-24 and node-26 run on Node.js 22.23.4,
24.22.1 and 26.12.1, the October 2026 security releases.

Mono-RevId: 35db19e7144d50271973d7bd941b517ec9b7c3fb
Add an admin route to request compute template builds for existing deployments.

Mono-RevId: adae12bcc253c0e7a19eee851da1638977b74052
Agent instructions (AGENTS.md and the test-writing guide) now say webapp
tests go in `apps/webapp/test/`, and packages follow their existing
layout (next to the source or in a `test/` folder).

Mono-RevId: 3ac9cac75ceaff967dc461f695192cdef33fef87
S2 ends a read that fails mid-stream with an `event: error` followed by
a normal end of the response. Trigger.dev's readers ignored that event,
so a failed read looked like the stream had finished. Run streams
(`runs.subscribeToRun` with streams, `useRealtimeRunWithStreams`) and
chat streams stopped early, and server-side record reads returned
partial results as if they were complete.

```ts
if (chunk.event === "error") {
  throw new Error(`Stream read failed: ${chunk.data}`);
}
```

The stream subscription now throws on the event, so its existing retry
path reconnects with `Last-Event-ID` and resumes after the last record
it delivered. This covers every client that reads realtime streams,
including chat reconnects. If retries run out, the stream errors instead
of completing.

Server-side record reads now reject on the event and retry transient
failures. An input stream or session `.wait()` no longer misses a
message sent just before the wait started when the stream read fails
briefly. The client protocol docs now list the `error` event, and the
reference reader handles it.

Mono-RevId: 2bddf6acd0dc4fc56a709eec125251fa97bd064a
The supervisor can now set the `net.ipv4.ip_local_reserved_ports` sysctl
on every run pod it creates, via the new optional
`KUBERNETES_POD_RESERVED_SOURCE_PORTS` (a comma-separated list of ports
or `low-high` ranges, validated at startup), so the kernel never picks
those ports as ephemeral source ports. Unset leaves run pods unchanged.
The sysctl is namespaced and on the Kubernetes safe sysctl list since
1.27 (kernel 3.16+), so no kubelet allowlist is needed on 1.27+
clusters.

Mono-RevId: fdd8c6b08a68c0a669c650fd51499a7a2efe24d1
…I tree

The run trace API (`GET /api/v1/runs/:runId/trace`, unpaged) no longer
reads span attributes from ClickHouse when building the span tree. The
tree never used them, so responses are unchanged while ClickHouse reads
and the data sent to the API shrink, most for large traces.

Mono-RevId: cb52ce09b11e29ad36f09b11caaaf9582e5cbfbf
fix(webapp): improve alert creation, editing, and Slack connection flow

Mono-RevId: a038b8e5f71e7150954feece93ecc862d7d0ef60
Run traces and trace APIs read less from ClickHouse for completed runs,
most for older runs. Every trace read now also bounds `start_time` in
the form of the table's sort key (`toUnixTimestamp(start_time)`), so
ClickHouse skips granules outside the run's window even when it doesn't
derive that from the exact bounds. Results are unchanged.

Mono-RevId: 0776e6cd8797bb3d3777f0a0f1afb1e18e0d07e5
The snapshot store's completed-state retention is now configurable with
RUN_ENGINE_SNAPSHOT_STORE_COMPLETED_TTL_MS (default 4 days).

Mono-RevId: 5d229fefab916dfc1e608023ffb36a512e72b6db
… them

The API keys page now always lists the root API key. A hidden root key
shows masked to its last 4 characters and can be regenerated, but never
revealed or copied. When it's hidden and you have no other keys, the
page prompts you to create an API key for your backend.

"Revoke the previous key immediately" now also stops older root keys
still in their 24-hour grace window.

Mono-RevId: e0466df43d3fae68e32f01da137835ea3dec4858
Alerts are now checked every minute instead of every 5–10 minutes, so
they raise and recover sooner.

Mono-RevId: f147b6afe8616cecb53c38a6da5d3a6758e7caf8
Runs whose deployment has no compute template now go to containers when they are triggered. The task metadata cache carries the deployment's template key, so the trigger reads it with no new database query. The key only keeps a run off compute; it never moves a run that is not migrated.

Mono-RevId: cf013ae7cd32ef2e02711ac9278c11ad222487c1
Mono-RevId: 66417a057fa33dbdea1e4914dcbcea329cb9c5af
## Summary
3 new features, 14 improvements, 4 bug fixes.

## Improvements
- Route verified webhooks into agent sessions with `chat.event({
endpoint, key })`, and connect external chat surfaces with
`chat.channels.custom({ endpoint })`. Both subscribe to a shared webhook
endpoint, so an agent and your other webhook tasks can use the same
provider URL. Channels support reply delivery, session binding,
conversation seeding, and lifecycle reactions, and a failed turn posts a
short line instead of the error (`errorMessage` sets it, as a string or
a function of the failure). Use `chat.createWatchToken()` to watch a
session from another surface.
([`d6955ee9f`](d6955ee))
  
A connector can also stream replies natively (`stream()`), mirror the
session's state into the surface (`lifecycle()`), show tool calls as
they run (`progress()`), stop a running turn from the surface
(`onControl()`), post approvals and tool controls and resume the run
from a verified click, download files only the surface can read
(`download()`), and acknowledge or fold in messages that arrive mid-turn
(`received()`, `pendingMessages`).
- Add hosted webhooks: declare an endpoint once with
`webhooks.endpoint.define({ id, source })` and Trigger.dev hosts its
URL, verifies every request, and routes each event to the endpoint's
subscribers. Subscribe tasks with `webhook({ endpoint, filter, onEvent
})`, each with its own type-safe filter, and pause a run until a
matching webhook arrives with `webhooks.waiter.create()` and
`webhooks.waiter.wait()`.
([`d6955ee9f`](d6955ee))
  
  ```ts
  export const stripe = webhooks.endpoint.define({
  id: "stripe",
  source: webhooks.stripe(),
  });
  
  export const fulfill = webhook({
  id: "fulfill",
  endpoint: stripe,
  filter: "event.type == 'checkout.session.completed'",
  onEvent: async ({ event }) => {},
  });
  
  const waiter = await webhooks.waiter.create(stripe, {
  match: { "event.data.object.metadata.orderId": order.id },
  });
  const { event } = await webhooks.waiter.wait(waiter).unwrap();
  ```
  
Sources cover presets (`webhooks.stripe()`, `webhooks.github()`,
`webhooks.svix()`, `webhooks.discord()`, and others), named providers
(`webhooks.clerk()`, `webhooks.shopify()`, and more) and
`webhooks.custom<T>(config)`, and most carry a setup prompt an AI coding
agent can follow to register the endpoint. Retries are deduplicated on
the provider's own event or delivery id where it sends one. Filters,
keys and waiter `match` paths share the `event.`, `header.` and
`webhook.` namespaces, and filters accept `== null` / `!= null` presence
checks and `event any (...)` over a payload that is an array of events.
`webhook()` takes `task()` options such as `queue`, `machine` and
`retry`. The HTTP API lists endpoints and deliveries, replays
deliveries, and creates and cancels waiters, and both it and the MCP
server can read an endpoint's setup prompt and generate or store its
signing secret.
- The `generate_webhook_secret` and `set_webhook_secret` MCP tools take
an optional `previousSecretTtlSeconds` to keep accepting a webhook
endpoint's old signing secret while the provider switches to the new one
(up to 7 days), and report when the old secret stops working.
([`98adb6662`](98adb66))
- Realtime streams now reconnect and resume when the stream backend
reports a failed read mid-stream, instead of ending early as if the
stream had finished. This affects `runs.subscribeToRun` with streams,
`useRealtimeRunWithStreams`, and chat streams.
([`23730b034`](23730b0))
- HMAC webhook verifier configs accept `additionalSignatureHeaders`:
more headers that can carry a signature, matched as alternatives to
`signatureHeader`. Providers that send a second signature during a
secret rotation (Hookdeck's `x-hookdeck-signature-2`) keep verifying
through the rotation.
([`08d1ddbd3`](08d1ddb))
- Add `webhooks.hookdeck()`, a source for deliveries relayed through a
Hookdeck connection. It verifies the `x-hookdeck-signature` header with
the project's signing secret, keeps verifying while the secret is being
rolled (Hookdeck sends the previous secret's signature in
`x-hookdeck-signature-2`), and deduplicates retries on
`x-hookdeck-eventid`. The event is the original provider's payload, so
pass its type: `webhooks.hookdeck<Stripe.Event>()`. If the Hookdeck
project's header prefix was changed, pass it as `webhooks.hookdeck({
headerPrefix })` so retries keep deduplicating.
([`51b39fbc6`](51b39fb))
- Add `@trigger.dev/linear`: turn a `chat.agent` into a Linear agent.
Assign it an issue or @mention it, and Linear's agent session becomes a
durable Trigger.dev session: the reply posts back to Linear, tool calls
show as activities, `linearPlanTool()` keeps the plan Linear shows as a
checklist, and approvals are Approve and Deny options that resume the
run.
([`1586c4a3f`](1586c4a))
  
  ```ts
  import { linear, webhookSource } from "@trigger.dev/linear";
  
  const linearEvents = webhooks.endpoint.define({
  id: "linear",
  source: webhookSource(),
  });
  
  export const triage = chat.agent({
  id: "triage",
  channels: [
    linear({
      id: "linear",
      endpoint: linearEvents,
      token: process.env.LINEAR_ACCESS_TOKEN!,
    }),
  ],
  run: async ({ messages, signal, streamText }) =>
    streamText({ model, messages, abortSignal: signal }),
  });
  ```
  
The first turn gets the issue's context and earlier comments, images and
PDFs in the issue reach the model, a Stop in Linear winds the turn down,
and `errorMessage` sets the text a failed turn shows.
- Add `@trigger.dev/slack`: turn a `chat.agent` into a native Slack
agent. Each DM or channel thread is a durable session, the reply streams
into the thread with the turn's tool calls shown as a plan, approvals
are Approve and Deny buttons, and Slack's stop button stops the turn.
([`6e715da77`](6e715da))
  
  ```ts
import { appMentions, slack, webhookSource } from "@trigger.dev/slack";
  
  const slackEvents = webhooks.endpoint.define({
  id: "slack",
  source: webhookSource(),
  });
  
  export const support = chat.agent({
  id: "support",
  channels: [
    slack({
      id: "slack",
      endpoint: slackEvents,
      token: process.env.SLACK_BOT_TOKEN!,
      startOn: appMentions(),
    }),
  ],
  run: async ({ messages, signal, streamText }) =>
    streamText({ model, messages, abortSignal: signal }),
  });
  ```
  
`startOn` decides which channel messages start a session
(`appMentions()` answers when the agent is @mentioned). Images and PDFs
people share reach the model, messages sent while the agent is answering
can be folded into the reply, tools can post their own buttons, and
`ownWorkspaceOnly` ignores Slack Connect guests.
- The `streamText` passed to a `chat.agent`'s `run()` now turns AI SDK
telemetry on by default, so each model step and tool call shows up as a
span in the run trace even when the agent doesn't store a prompt with
`chat.prompt.set()`. An agent with `telemetry: { recordUserMessage:
false }` gets these spans without prompt and response content, including
when it stores a prompt (whose variables are left out of the span
metadata). Pass `experimental_telemetry: { isEnabled: false }` at the
call site to turn them off, or `recordInputs: false` / `recordOutputs:
false` to keep them without content.
([`bd7cc3525`](bd7cc35))

## Server changes

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

- When regenerating an environment's API key you can now revoke the
previous key immediately instead of keeping it working for 24 hours. Use
this when a key may have leaked.
- Set `KUBERNETES_POD_RESERVED_SOURCE_PORTS` on the supervisor (for
example `50080,50081`) to stop runs using those ports for outbound
connections, which is useful to avoid port clashes.
- Rotating a webhook endpoint's signing secret can keep the old one
valid for a while, so you can rotate without failing deliveries that are
still signed with it. The rotate and regenerate dialogs let you choose
how long the old secret stays valid (up to 7 days, 24 hours by default).
The `rotate-secret` and `secret` endpoints accept an optional
`previousSecretTtlSeconds` and return `previousSecretExpiresAt`. With no
value, the old secret stops working immediately, as before.
- Alerts are now checked every minute instead of every 5–10 minutes, so
they raise and recover sooner.
- Run traces and trace APIs load faster for older runs.
- The API keys page now always lists your root API key, masked to its
last 4 characters when it's hidden, so you can regenerate it and revoke
the previous key. When the root key is hidden, the page points you to
creating an API key for your backend.
- Runs whose deployment has no compute template now go to containers
when they are triggered, instead of first passing through the compute
worker queue.
- Runs now get a `TRIGGER_DASHBOARD_URL` environment variable with the
dashboard's address, so a task can link to its run
(`${TRIGGER_DASHBOARD_URL}/runs/${ctx.run.id}`) on any Trigger.dev
instance. A `TRIGGER_DASHBOARD_URL` you set on the project still wins.
- Creating an alert works without the dashboard agent.
- Small fixes to creating and editing alerts.
- Alerts sent to Slack channels connected through a removed Slack
installation deliver again.
- The supervisor no longer trusts certificates issued under the
compromised emSign (eMudhra) certificate authorities.

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

# Releases
## trigger.dev@4.8.0

### Minor Changes

- Route verified webhooks into agent sessions with `chat.event({
endpoint, key })`, and connect external chat surfaces with
`chat.channels.custom({ endpoint })`. Both subscribe to a shared webhook
endpoint, so an agent and your other webhook tasks can use the same
provider URL. Channels support reply delivery, session binding,
conversation seeding, and lifecycle reactions, and a failed turn posts a
short line instead of the error (`errorMessage` sets it, as a string or
a function of the failure). Use `chat.createWatchToken()` to watch a
session from another surface.
([`d6955ee9f`](d6955ee))

A connector can also stream replies natively (`stream()`), mirror the
session's state into the surface (`lifecycle()`), show tool calls as
they run (`progress()`), stop a running turn from the surface
(`onControl()`), post approvals and tool controls and resume the run
from a verified click, download files only the surface can read
(`download()`), and acknowledge or fold in messages that arrive mid-turn
(`received()`, `pendingMessages`).

- Add hosted webhooks: declare an endpoint once with
`webhooks.endpoint.define({ id, source })` and Trigger.dev hosts its
URL, verifies every request, and routes each event to the endpoint's
subscribers. Subscribe tasks with `webhook({ endpoint, filter, onEvent
})`, each with its own type-safe filter, and pause a run until a
matching webhook arrives with `webhooks.waiter.create()` and
`webhooks.waiter.wait()`.
([`d6955ee9f`](d6955ee))

  ```ts
  export const stripe = webhooks.endpoint.define({
    id: "stripe",
    source: webhooks.stripe(),
  });

  export const fulfill = webhook({
    id: "fulfill",
    endpoint: stripe,
    filter: "event.type == 'checkout.session.completed'",
    onEvent: async ({ event }) => {},
  });

  const waiter = await webhooks.waiter.create(stripe, {
    match: { "event.data.object.metadata.orderId": order.id },
  });
  const { event } = await webhooks.waiter.wait(waiter).unwrap();
  ```

Sources cover presets (`webhooks.stripe()`, `webhooks.github()`,
`webhooks.svix()`, `webhooks.discord()`, and others), named providers
(`webhooks.clerk()`, `webhooks.shopify()`, and more) and
`webhooks.custom<T>(config)`, and most carry a setup prompt an AI coding
agent can follow to register the endpoint. Retries are deduplicated on
the provider's own event or delivery id where it sends one. Filters,
keys and waiter `match` paths share the `event.`, `header.` and
`webhook.` namespaces, and filters accept `== null` / `!= null` presence
checks and `event any (...)` over a payload that is an array of events.
`webhook()` takes `task()` options such as `queue`, `machine` and
`retry`. The HTTP API lists endpoints and deliveries, replays
deliveries, and creates and cancels waiters, and both it and the MCP
server can read an endpoint's setup prompt and generate or store its
signing secret.

### Patch Changes

- The `generate_webhook_secret` and `set_webhook_secret` MCP tools take
an optional `previousSecretTtlSeconds` to keep accepting a webhook
endpoint's old signing secret while the provider switches to the new one
(up to 7 days), and report when the old secret stops working.
([`98adb6662`](98adb66))
- Updated dependencies:
  - `@trigger.dev/core@4.8.0`
  - `@trigger.dev/build@4.8.0`
  - `@trigger.dev/schema-to-json@4.8.0`
## @trigger.dev/core@4.8.0

### Minor Changes

- Add hosted webhooks: declare an endpoint once with
`webhooks.endpoint.define({ id, source })` and Trigger.dev hosts its
URL, verifies every request, and routes each event to the endpoint's
subscribers. Subscribe tasks with `webhook({ endpoint, filter, onEvent
})`, each with its own type-safe filter, and pause a run until a
matching webhook arrives with `webhooks.waiter.create()` and
`webhooks.waiter.wait()`.
([`d6955ee9f`](d6955ee))

  ```ts
  export const stripe = webhooks.endpoint.define({
    id: "stripe",
    source: webhooks.stripe(),
  });

  export const fulfill = webhook({
    id: "fulfill",
    endpoint: stripe,
    filter: "event.type == 'checkout.session.completed'",
    onEvent: async ({ event }) => {},
  });

  const waiter = await webhooks.waiter.create(stripe, {
    match: { "event.data.object.metadata.orderId": order.id },
  });
  const { event } = await webhooks.waiter.wait(waiter).unwrap();
  ```

Sources cover presets (`webhooks.stripe()`, `webhooks.github()`,
`webhooks.svix()`, `webhooks.discord()`, and others), named providers
(`webhooks.clerk()`, `webhooks.shopify()`, and more) and
`webhooks.custom<T>(config)`, and most carry a setup prompt an AI coding
agent can follow to register the endpoint. Retries are deduplicated on
the provider's own event or delivery id where it sends one. Filters,
keys and waiter `match` paths share the `event.`, `header.` and
`webhook.` namespaces, and filters accept `== null` / `!= null` presence
checks and `event any (...)` over a payload that is an array of events.
`webhook()` takes `task()` options such as `queue`, `machine` and
`retry`. The HTTP API lists endpoints and deliveries, replays
deliveries, and creates and cancels waiters, and both it and the MCP
server can read an endpoint's setup prompt and generate or store its
signing secret.

### Patch Changes

- Realtime streams now reconnect and resume when the stream backend
reports a failed read mid-stream, instead of ending early as if the
stream had finished. This affects `runs.subscribeToRun` with streams,
`useRealtimeRunWithStreams`, and chat streams.
([`23730b034`](23730b0))
- HMAC webhook verifier configs accept `additionalSignatureHeaders`:
more headers that can carry a signature, matched as alternatives to
`signatureHeader`. Providers that send a second signature during a
secret rotation (Hookdeck's `x-hookdeck-signature-2`) keep verifying
through the rotation.
([`08d1ddbd3`](08d1ddb))
- The `generate_webhook_secret` and `set_webhook_secret` MCP tools take
an optional `previousSecretTtlSeconds` to keep accepting a webhook
endpoint's old signing secret while the provider switches to the new one
(up to 7 days), and report when the old secret stops working.
([`98adb6662`](98adb66))
- Add `webhooks.hookdeck()`, a source for deliveries relayed through a
Hookdeck connection. It verifies the `x-hookdeck-signature` header with
the project's signing secret, keeps verifying while the secret is being
rolled (Hookdeck sends the previous secret's signature in
`x-hookdeck-signature-2`), and deduplicates retries on
`x-hookdeck-eventid`. The event is the original provider's payload, so
pass its type: `webhooks.hookdeck<Stripe.Event>()`. If the Hookdeck
project's header prefix was changed, pass it as `webhooks.hookdeck({
headerPrefix })` so retries keep deduplicating.
([`51b39fbc6`](51b39fb))
## @trigger.dev/linear@4.8.0

### Minor Changes

- Add `@trigger.dev/linear`: turn a `chat.agent` into a Linear agent.
Assign it an issue or @mention it, and Linear's agent session becomes a
durable Trigger.dev session: the reply posts back to Linear, tool calls
show as activities, `linearPlanTool()` keeps the plan Linear shows as a
checklist, and approvals are Approve and Deny options that resume the
run.
([`1586c4a3f`](1586c4a))

  ```ts
  import { linear, webhookSource } from "@trigger.dev/linear";

  const linearEvents = webhooks.endpoint.define({
    id: "linear",
    source: webhookSource(),
  });

  export const triage = chat.agent({
    id: "triage",
    channels: [
      linear({
        id: "linear",
        endpoint: linearEvents,
        token: process.env.LINEAR_ACCESS_TOKEN!,
      }),
    ],
    run: async ({ messages, signal, streamText }) =>
      streamText({ model, messages, abortSignal: signal }),
  });
  ```

The first turn gets the issue's context and earlier comments, images and
PDFs in the issue reach the model, a Stop in Linear winds the turn down,
and `errorMessage` sets the text a failed turn shows.

### Patch Changes

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

### Minor Changes

- Add `@trigger.dev/slack`: turn a `chat.agent` into a native Slack
agent. Each DM or channel thread is a durable session, the reply streams
into the thread with the turn's tool calls shown as a plan, approvals
are Approve and Deny buttons, and Slack's stop button stops the turn.
([`6e715da77`](6e715da))

  ```ts
import { appMentions, slack, webhookSource } from "@trigger.dev/slack";

  const slackEvents = webhooks.endpoint.define({
    id: "slack",
    source: webhookSource(),
  });

  export const support = chat.agent({
    id: "support",
    channels: [
      slack({
        id: "slack",
        endpoint: slackEvents,
        token: process.env.SLACK_BOT_TOKEN!,
        startOn: appMentions(),
      }),
    ],
    run: async ({ messages, signal, streamText }) =>
      streamText({ model, messages, abortSignal: signal }),
  });
  ```

`startOn` decides which channel messages start a session
(`appMentions()` answers when the agent is @mentioned). Images and PDFs
people share reach the model, messages sent while the agent is answering
can be folded into the reply, tools can post their own buttons, and
`ownWorkspaceOnly` ignores Slack Connect guests.

### Patch Changes

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

### Minor Changes

- Route verified webhooks into agent sessions with `chat.event({
endpoint, key })`, and connect external chat surfaces with
`chat.channels.custom({ endpoint })`. Both subscribe to a shared webhook
endpoint, so an agent and your other webhook tasks can use the same
provider URL. Channels support reply delivery, session binding,
conversation seeding, and lifecycle reactions, and a failed turn posts a
short line instead of the error (`errorMessage` sets it, as a string or
a function of the failure). Use `chat.createWatchToken()` to watch a
session from another surface.
([`d6955ee9f`](d6955ee))

A connector can also stream replies natively (`stream()`), mirror the
session's state into the surface (`lifecycle()`), show tool calls as
they run (`progress()`), stop a running turn from the surface
(`onControl()`), post approvals and tool controls and resume the run
from a verified click, download files only the surface can read
(`download()`), and acknowledge or fold in messages that arrive mid-turn
(`received()`, `pendingMessages`).

- Add hosted webhooks: declare an endpoint once with
`webhooks.endpoint.define({ id, source })` and Trigger.dev hosts its
URL, verifies every request, and routes each event to the endpoint's
subscribers. Subscribe tasks with `webhook({ endpoint, filter, onEvent
})`, each with its own type-safe filter, and pause a run until a
matching webhook arrives with `webhooks.waiter.create()` and
`webhooks.waiter.wait()`.
([`d6955ee9f`](d6955ee))

  ```ts
  export const stripe = webhooks.endpoint.define({
    id: "stripe",
    source: webhooks.stripe(),
  });

  export const fulfill = webhook({
    id: "fulfill",
    endpoint: stripe,
    filter: "event.type == 'checkout.session.completed'",
    onEvent: async ({ event }) => {},
  });

  const waiter = await webhooks.waiter.create(stripe, {
    match: { "event.data.object.metadata.orderId": order.id },
  });
  const { event } = await webhooks.waiter.wait(waiter).unwrap();
  ```

Sources cover presets (`webhooks.stripe()`, `webhooks.github()`,
`webhooks.svix()`, `webhooks.discord()`, and others), named providers
(`webhooks.clerk()`, `webhooks.shopify()`, and more) and
`webhooks.custom<T>(config)`, and most carry a setup prompt an AI coding
agent can follow to register the endpoint. Retries are deduplicated on
the provider's own event or delivery id where it sends one. Filters,
keys and waiter `match` paths share the `event.`, `header.` and
`webhook.` namespaces, and filters accept `== null` / `!= null` presence
checks and `event any (...)` over a payload that is an array of events.
`webhook()` takes `task()` options such as `queue`, `machine` and
`retry`. The HTTP API lists endpoints and deliveries, replays
deliveries, and creates and cancels waiters, and both it and the MCP
server can read an endpoint's setup prompt and generate or store its
signing secret.

- Add `@trigger.dev/slack`: turn a `chat.agent` into a native Slack
agent. Each DM or channel thread is a durable session, the reply streams
into the thread with the turn's tool calls shown as a plan, approvals
are Approve and Deny buttons, and Slack's stop button stops the turn.
([`6e715da77`](6e715da))

  ```ts
import { appMentions, slack, webhookSource } from "@trigger.dev/slack";

  const slackEvents = webhooks.endpoint.define({
    id: "slack",
    source: webhookSource(),
  });

  export const support = chat.agent({
    id: "support",
    channels: [
      slack({
        id: "slack",
        endpoint: slackEvents,
        token: process.env.SLACK_BOT_TOKEN!,
        startOn: appMentions(),
      }),
    ],
    run: async ({ messages, signal, streamText }) =>
      streamText({ model, messages, abortSignal: signal }),
  });
  ```

`startOn` decides which channel messages start a session
(`appMentions()` answers when the agent is @mentioned). Images and PDFs
people share reach the model, messages sent while the agent is answering
can be folded into the reply, tools can post their own buttons, and
`ownWorkspaceOnly` ignores Slack Connect guests.

- Add `@trigger.dev/linear`: turn a `chat.agent` into a Linear agent.
Assign it an issue or @mention it, and Linear's agent session becomes a
durable Trigger.dev session: the reply posts back to Linear, tool calls
show as activities, `linearPlanTool()` keeps the plan Linear shows as a
checklist, and approvals are Approve and Deny options that resume the
run.
([`1586c4a3f`](1586c4a))

  ```ts
  import { linear, webhookSource } from "@trigger.dev/linear";

  const linearEvents = webhooks.endpoint.define({
    id: "linear",
    source: webhookSource(),
  });

  export const triage = chat.agent({
    id: "triage",
    channels: [
      linear({
        id: "linear",
        endpoint: linearEvents,
        token: process.env.LINEAR_ACCESS_TOKEN!,
      }),
    ],
    run: async ({ messages, signal, streamText }) =>
      streamText({ model, messages, abortSignal: signal }),
  });
  ```

The first turn gets the issue's context and earlier comments, images and
PDFs in the issue reach the model, a Stop in Linear winds the turn down,
and `errorMessage` sets the text a failed turn shows.

### Patch Changes

- The `streamText` passed to a `chat.agent`'s `run()` now turns AI SDK
telemetry on by default, so each model step and tool call shows up as a
span in the run trace even when the agent doesn't store a prompt with
`chat.prompt.set()`. An agent with `telemetry: { recordUserMessage:
false }` gets these spans without prompt and response content, including
when it stores a prompt (whose variables are left out of the span
metadata). Pass `experimental_telemetry: { isEnabled: false }` at the
call site to turn them off, or `recordInputs: false` / `recordOutputs:
false` to keep them without content.
([`bd7cc3525`](bd7cc35))
- Add `webhooks.hookdeck()`, a source for deliveries relayed through a
Hookdeck connection. It verifies the `x-hookdeck-signature` header with
the project's signing secret, keeps verifying while the secret is being
rolled (Hookdeck sends the previous secret's signature in
`x-hookdeck-signature-2`), and deduplicates retries on
`x-hookdeck-eventid`. The event is the original provider's payload, so
pass its type: `webhooks.hookdeck<Stripe.Event>()`. If the Hookdeck
project's header prefix was changed, pass it as `webhooks.hookdeck({
headerPrefix })` so retries keep deduplicating.
([`51b39fbc6`](51b39fb))
- Updated dependencies:
  - `@trigger.dev/core@4.8.0`
## @trigger.dev/build@4.8.0

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

</details>

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Database query spans from the webapp now carry a db.client_type
attribute naming the database client that ran the query, the same name
used on the webapp's database metrics. Query latency can now be broken
down by query type (INSERT, UPDATE, SELECT, ...) for each client.

Mono-RevId: 6dead141fe60243a6f6b7bb9ea4847920dd288fb
Reworked the docs introduction around building agents with `chat.agent`,
webhooks, and running in production, with an expanded FAQ.

Mono-RevId: b79d3f40d91d699c56406a750855012dca05262b
Alerts can now watch the result of a query: raise when a value crosses a
threshold, when matching rows stop appearing, or when new matching rows
appear — with the observed values charted on the alert detail page.

Mono-RevId: 6fef942e47bcf26a8a9d707149d99033858687d2
Fixed an issue where some runs from deployments without a prebuilt
template could stay queued instead of starting.

Mono-RevId: 2a8e5c624437ab80277ea56bb7b97ea6cb932230
The admin template-builds route now reports a deployment as failed, with a reason, when the template key could not be stored.

Mono-RevId: 52d799b6e54eefe3ba3a9c2125092dbf697ee5ed
…d webhooks

Docs updates for 4.8.0: `chat.agent` examples use the `streamText`
passed to `run()`, the webhook secret MCP tools document
`previousSecretTtlSeconds`, self-hosting docs cover turning on webhooks
and `KUBERNETES_POD_RESERVED_SOURCE_PORTS`, and the agents overview
introduces channels.

Mono-RevId: 3eb1ed33ef2e0e21cd1197391b71a3a7c57e9785
…session append route

Internal refactor of the session append endpoint. No behaviour change:
appending to a session's input channel now goes through one shared
server helper that other server code can reuse.

Mono-RevId: c5c64aad40d4a1b19f2e8e383ff359276b97229f
…gs dialog

The admin org flags dialog now shows an org's own value for read-only flags, and says what that value does: it overrides the global value, it's stored but ignored, or it's a server-written stamp. Stored org flags are now read one key at a time, so a single invalid value no longer makes the dialog and the admin API treat the org as having no flags. Saves keep stored values they didn't change, including keys the running version doesn't know yet.

Mono-RevId: 061e76440f3462887e0b63d75b556a70ae02c480
Fixes `trigger deploy` failing on self-hosted instances that set custom
base images with `DEPLOY_BASE_IMAGES` or `DEPLOY_BUILD_BASE_IMAGES`
(#5054). The deploy step that distrusts compromised emSign certificate
authorities assumed the published root Debian images, so it broke on
non-root or non-Debian bases such as Chainguard's.

Stages built on a custom image now skip that step and keep the
certificate settings the image ships with. Stages on the published
images are unchanged. The self-hosting docs now state that a custom base
image owns its own security hardening.

Mono-RevId: 5de0cdda76e9b57d7b076d52c6ab8c763934e2d3
## Summary
1 new feature, 1 bug fix.

## Bug fixes
- Fix `trigger deploy` failing on self-hosted instances with custom base
images. Stages built on a custom image now keep the trust store that
image ships with.
([`b5ed66b74`](b5ed66b))

## Server changes

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

- Alerts can now watch the result of any supported query: raise when a
value crosses a threshold, when matching rows stop appearing, or when
new matching rows appear. Create them from the Query page, the Dashboard
Agent or the API, and see the alert's values charted on its page.

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

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

### Patch Changes

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

### Patch Changes

- Fix `trigger deploy` failing on self-hosted instances with custom base
images. Stages built on a custom image now keep the trust store that
image ships with.
([`b5ed66b74`](b5ed66b))
- Updated dependencies:
  - `@trigger.dev/build@4.8.1`
  - `@trigger.dev/core@4.8.1`
  - `@trigger.dev/schema-to-json@4.8.1`
## @trigger.dev/linear@4.8.1

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

### Patch Changes

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

</details>

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Log why deploy finalisation skipped the template prebuild, with the eligibility inputs it used, and count skips by reason and eligibility.

Mono-RevId: 8cc37b007e0ab7e74c3075ab21628597c5f729ab
Lets one worker group carry container and compute consumers side by
side. Items on an `any` queue lane go to whichever runtime takes them
first, and only runs that compute can start are given an `any` lane.

Mono-RevId: d2ca67b6bfdd7ca8c940aeaa4471b6af4bab4386
…ebuild eligibility

Template prebuild eligibility now resolves a project's default worker group the same way triggering does, including the fallback to the global default group.

Mono-RevId: 8ad53cce26c26034dc834b97f950fc70cfda7f9a
Promoting or rolling back a deployment now updates webhook endpoints to match the deployment that becomes current.

Mono-RevId: dff3d7c34e49b2a8f7e4e5a4b4cc79b145e98888

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.