@trigger.dev/sdk 4.8.0 → 4.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = "4.8.0";
4
+ exports.VERSION = "4.8.1";
5
5
  //# sourceMappingURL=version.js.map
@@ -1,2 +1,2 @@
1
- export const VERSION = "4.8.0";
1
+ export const VERSION = "4.8.1";
2
2
  //# sourceMappingURL=version.js.map
@@ -219,8 +219,9 @@ Prompts integrate with `chat.agent()` via `chat.prompt` — a run-scoped store f
219
219
  ```ts
220
220
  import { chat } from "@trigger.dev/sdk/ai";
221
221
  import { prompts } from "@trigger.dev/sdk";
222
- import { streamText, createProviderRegistry } from "ai";
222
+ import { createProviderRegistry, stepCountIs } from "ai";
223
223
  import { anthropic } from "@ai-sdk/anthropic";
224
+ import { z } from "zod";
224
225
 
225
226
  const registry = createProviderRegistry({ anthropic });
226
227
 
@@ -234,13 +235,14 @@ const systemPrompt = prompts.define({
234
235
 
235
236
  export const myChat = chat.agent({
236
237
  id: "my-chat",
238
+ registry,
239
+ clientDataSchema: z.object({ name: z.string() }),
237
240
  onChatStart: async ({ clientData }) => {
238
241
  const resolved = await systemPrompt.resolve({ name: clientData.name });
239
242
  chat.prompt.set(resolved);
240
243
  },
241
- run: async ({ messages, signal }) => {
244
+ run: async ({ messages, signal, streamText }) => {
242
245
  return streamText({
243
- ...chat.toStreamTextOptions({ registry }),
244
246
  messages,
245
247
  abortSignal: signal,
246
248
  stopWhen: stepCountIs(15),
@@ -249,9 +251,11 @@ export const myChat = chat.agent({
249
251
  });
250
252
  ```
251
253
 
254
+ The `streamText` passed to `run()` applies the stored prompt: its system text, its model (resolved through the agent's `registry`), its `config`, and telemetry that links each generation to the prompt version. Options you pass at the call site win, apart from `system`, which throws when a prompt already sets one.
255
+
252
256
  ### chat.toStreamTextOptions()
253
257
 
254
- Returns an options object ready to spread into `streamText()`. When a prompt is stored via `chat.prompt.set()`, it includes:
258
+ Inside a `chat.agent` you don't need this: the `streamText` passed to `run()` applies the same options. Use `chat.toStreamTextOptions()` when you call `streamText` yourself, as in a [custom agent](/ai-chat/custom-agents). It returns an options object ready to spread into `streamText()`. When a prompt is stored via `chat.prompt.set()`, it includes:
255
259
 
256
260
  - `system` — the compiled prompt text
257
261
  - `model` — resolved via the `registry` when provided
@@ -277,14 +281,13 @@ const options = chat.toStreamTextOptions();
277
281
  Access the stored prompt from anywhere in the run:
278
282
 
279
283
  ```ts
280
- run: async ({ messages, signal }) => {
284
+ run: async ({ messages, signal, streamText }) => {
281
285
  const prompt = chat.prompt(); // Throws if not set
282
286
  console.log(prompt.text); // The compiled prompt
283
287
  console.log(prompt.model); // "anthropic:claude-sonnet-4-5"
284
288
  console.log(prompt.version); // 3
285
289
 
286
290
  return streamText({
287
- ...chat.toStreamTextOptions({ registry }),
288
291
  messages,
289
292
  abortSignal: signal,
290
293
  stopWhen: stepCountIs(15),
@@ -16,7 +16,7 @@ Declare locals at module level with a unique `id`, then initialize them inside a
16
16
 
17
17
  ```ts
18
18
  import { chat } from "@trigger.dev/sdk/ai";
19
- import { streamText, tool, stepCountIs } from "ai";
19
+ import { tool, stepCountIs } from "ai";
20
20
  import { anthropic } from "@ai-sdk/anthropic";
21
21
  import { z } from "zod";
22
22
  import { db } from "@/lib/db";
@@ -44,7 +44,7 @@ export const myChat = chat.agent({
44
44
  messageCount: user.messageCount,
45
45
  });
46
46
  },
47
- run: async ({ messages, signal }) => {
47
+ run: async ({ messages, signal, streamText }) => {
48
48
  userContext.messageCount++;
49
49
 
50
50
  return streamText({
@@ -88,7 +88,7 @@ When you use `ai.toolExecute()` inside AI SDK `tool()` to expose a subtask, chat
88
88
  ```ts
89
89
  import { chat, ai } from "@trigger.dev/sdk/ai";
90
90
  import { schemaTask } from "@trigger.dev/sdk";
91
- import { streamText, tool } from "ai";
91
+ import { tool, stepCountIs } from "ai";
92
92
  import { anthropic } from "@ai-sdk/anthropic";
93
93
  import { z } from "zod";
94
94
 
@@ -115,7 +115,7 @@ export const myChat = chat.agent({
115
115
  onBoot: async ({ clientData }) => {
116
116
  userContext.init({ name: "Alice", plan: "pro" });
117
117
  },
118
- run: async ({ messages, signal }) => {
118
+ run: async ({ messages, signal, streamText }) => {
119
119
  return streamText({
120
120
  model: anthropic("claude-sonnet-4-5"),
121
121
  messages,
@@ -80,7 +80,7 @@ export const myChat = chat.agent({
80
80
  if (preloaded) return; // Already initialized in onPreload
81
81
  // ... fallback initialization for non-preloaded runs
82
82
  },
83
- run: async ({ messages, signal }) => {
83
+ run: async ({ messages, signal, streamText }) => {
84
84
  return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
85
85
  },
86
86
  });
@@ -12,12 +12,12 @@ A `chat.agent` task takes `messages`, calls `streamText`, and returns the result
12
12
 
13
13
  ```ts trigger/chat.ts
14
14
  import { chat } from "@trigger.dev/sdk/ai";
15
- import { streamText, stepCountIs } from "ai";
15
+ import { stepCountIs } from "ai";
16
16
  import { anthropic } from "@ai-sdk/anthropic";
17
17
 
18
18
  export const myChat = chat.agent({
19
19
  id: "my-chat",
20
- run: async ({ messages, signal }) =>
20
+ run: async ({ messages, signal, streamText }) =>
21
21
  streamText({
22
22
  model: anthropic("claude-sonnet-4-5"),
23
23
  messages,
@@ -56,11 +56,12 @@ See [Quick Start](/ai-chat/quick-start) for the matching server actions and a ru
56
56
 
57
57
  ## How it fits together
58
58
 
59
- Three primitives, related but distinct:
59
+ Four primitives, related but distinct:
60
60
 
61
61
  - **Chat agents** — the SDK surface you define with [`chat.agent()`](/ai-chat/backend#chat-agent). Owns the turn loop, lifecycle hooks, and the response stream.
62
62
  - **Sessions** — the durable, bi-directional channel keyed on `chatId` that holds the conversation across run boundaries. A chat agent runs *on top of* a [Session](/ai-chat/sessions).
63
63
  - **Sub-agents** — Delegate work from one agent to another via [`AgentChat`](/ai-chat/patterns/sub-agents). The sub-agent runs as its own durable agent on its own session; its response streams back through the parent as preliminary tool results, so the frontend sees the sub-agent working inside the parent's tool card.
64
+ - **Channels** connect an agent to a surface other than your frontend. [`slack()`](/webhooks/slack) and [`linear()`](/webhooks/linear) turn an agent into a Slack or Linear agent, and [`chat.channels.custom()`](/webhooks/channels) connects any chat surface that sends webhooks. Each conversation there is still a durable session, and the reply posts back to the surface.
64
65
 
65
66
  ## Next steps
66
67
 
@@ -226,7 +226,7 @@ export const myChat = chat.agent({
226
226
  }),
227
227
  ]);
228
228
  },
229
- run: async ({ messages, signal }) => {
229
+ run: async ({ messages, signal, streamText }) => {
230
230
  return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
231
231
  },
232
232
  });
@@ -254,7 +254,7 @@ End-to-end implementation across the three files involved: agent task, server ac
254
254
  <CodeGroup>
255
255
  ```ts trigger/chat.ts
256
256
  import { chat } from "@trigger.dev/sdk/ai";
257
- import { streamText, stepCountIs } from "ai";
257
+ import { stepCountIs } from "ai";
258
258
  import { anthropic } from "@ai-sdk/anthropic";
259
259
  import { z } from "zod";
260
260
  import { db } from "@/lib/db";
@@ -296,7 +296,7 @@ export const myChat = chat.agent({
296
296
  }),
297
297
  ]);
298
298
  },
299
- run: async ({ messages, signal }) => {
299
+ run: async ({ messages, signal, streamText }) => {
300
300
  return streamText({
301
301
  model: anthropic("claude-sonnet-4-5"),
302
302
  messages,
@@ -31,7 +31,7 @@ The fix: read the `appliedEdits` counts as they stream in `onStepFinish`, then a
31
31
 
32
32
  ```ts /trigger/native-persist.ts
33
33
  import { chat } from "@trigger.dev/sdk/ai";
34
- import { streamText, stepCountIs, tool, type UIMessage } from "ai";
34
+ import { stepCountIs, tool, type UIMessage } from "ai";
35
35
  import { anthropic } from "@ai-sdk/anthropic";
36
36
  import { z } from "zod";
37
37
 
@@ -75,7 +75,7 @@ export const nativePersist = chat.agent({
75
75
  id: "native-persist",
76
76
  idleTimeoutInSeconds: 120,
77
77
  tools: { fetchRecord },
78
- run: async ({ messages, chatId, tools, signal }) => {
78
+ run: async ({ messages, chatId, tools, signal, streamText }) => {
79
79
  return streamText({
80
80
  model: anthropic("claude-sonnet-4-5"),
81
81
  messages,
@@ -132,7 +132,7 @@ OpenAI's `store: true` keeps the thread server-side and returns a `responseId`.
132
132
 
133
133
  ```ts /trigger/openai-store.ts
134
134
  import { chat } from "@trigger.dev/sdk/ai";
135
- import { streamText, stepCountIs, type ModelMessage } from "ai";
135
+ import { stepCountIs, type ModelMessage } from "ai";
136
136
  import { openai } from "@ai-sdk/openai";
137
137
 
138
138
  // Persist the stored-response handle between turns. Replace with your database.
@@ -151,7 +151,7 @@ function messagesSinceLastAssistant(messages: ModelMessage[]): ModelMessage[] {
151
151
  export const openaiStore = chat.agent({
152
152
  id: "openai-store",
153
153
  idleTimeoutInSeconds: 120,
154
- run: async ({ messages, chatId, signal }) => {
154
+ run: async ({ messages, chatId, signal, streamText }) => {
155
155
  const native = nativeStore.get(chatId);
156
156
  const outbound = native ? messagesSinceLastAssistant(messages) : messages;
157
157
 
@@ -186,7 +186,7 @@ A native handle is a per-provider cache. An OpenAI `previousResponseId` means no
186
186
 
187
187
  ```ts /trigger/resilient-chat.ts
188
188
  import { chat } from "@trigger.dev/sdk/ai";
189
- import { streamText, generateText, stepCountIs, generateId, type ModelMessage } from "ai";
189
+ import { generateText, stepCountIs, generateId, type ModelMessage } from "ai";
190
190
  import { anthropic } from "@ai-sdk/anthropic";
191
191
  import { openai } from "@ai-sdk/openai";
192
192
 
@@ -241,7 +241,7 @@ export const resilientChat = chat.agent({
241
241
  if (chatId) nativeStore.delete(chatId);
242
242
  },
243
243
 
244
- run: async ({ messages, chatId, signal }) => {
244
+ run: async ({ messages, chatId, signal, streamText }) => {
245
245
  let lastError: unknown;
246
246
  for (const providerId of FALLBACK_ORDER) {
247
247
  const native = nativeStore.get(chatId);
@@ -275,7 +275,7 @@ const tools = { research: researchTool };
275
275
  export const myAgent = chat.agent({
276
276
  id: "orchestrator",
277
277
  tools, // declare here so toModelOutput survives across turns
278
- run: async ({ messages, tools, stopSignal }) => {
278
+ run: async ({ messages, tools, stopSignal, streamText }) => {
279
279
  return streamText({
280
280
  model: anthropic("claude-sonnet-4-6"),
281
281
  messages,
@@ -100,7 +100,7 @@ On the agent side, declare which versions the current code supports:
100
100
 
101
101
  ```ts
102
102
  import { chat } from "@trigger.dev/sdk/ai";
103
- import { streamText } from "ai";
103
+
104
104
  import { anthropic } from "@ai-sdk/anthropic";
105
105
 
106
106
  // The set of frontend protocol versions this agent code supports.
@@ -121,7 +121,7 @@ export const myChat = chat
121
121
  chat.requestUpgrade();
122
122
  }
123
123
  },
124
- run: async ({ messages, signal }) => {
124
+ run: async ({ messages, signal, streamText }) => {
125
125
  return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
126
126
  },
127
127
  });
@@ -183,7 +183,7 @@ export const myChat = chat
183
183
  chat.requestUpgrade();
184
184
  }
185
185
  },
186
- run: async ({ messages, signal }) => {
186
+ run: async ({ messages, signal, streamText }) => {
187
187
  return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
188
188
  },
189
189
  });
@@ -38,12 +38,12 @@ Inside a `chat.agent` run, `chat.channel(...)` opens a channel on the current ru
38
38
 
39
39
  ```ts /trigger/browser-agent.ts
40
40
  import { chat } from "@trigger.dev/sdk/ai";
41
- import { streamText } from "ai";
41
+
42
42
  import { screenshots } from "./channels";
43
43
 
44
44
  export const browserAgent = chat.agent({
45
45
  id: "browser-agent",
46
- run: async ({ messages, signal }) => {
46
+ run: async ({ messages, signal, streamText }) => {
47
47
  const frames = chat.channel(screenshots);
48
48
 
49
49
  frames.in.on((control) => setPaused(control.paused)); // control: ViewportControl
@@ -56,7 +56,7 @@ Call `withUIMessage` **once**, then chain `.agent({ ... })` instead of `chat.age
56
56
 
57
57
  ```ts
58
58
  import { chat } from "@trigger.dev/sdk/ai";
59
- import { streamText, tool } from "ai";
59
+ import { tool } from "ai";
60
60
  import { anthropic } from "@ai-sdk/anthropic";
61
61
  import { z } from "zod";
62
62
  import type { MyChatUIMessage } from "./my-chat-types";
@@ -90,7 +90,7 @@ export const myChat = chat
90
90
  data: { status: "preparing" },
91
91
  });
92
92
  },
93
- run: async ({ messages, tools, signal }) => {
93
+ run: async ({ messages, tools, signal, streamText }) => {
94
94
  // `tools` is myTools, typed, handed back on the payload
95
95
  return streamText({
96
96
  model: anthropic("claude-sonnet-4-5"),
@@ -156,7 +156,7 @@ export const myChat = chat
156
156
  // clientData is typed as { userId: string; model?: string }
157
157
  await initUser(clientData.userId);
158
158
  },
159
- run: async ({ messages, clientData, signal }) => {
159
+ run: async ({ messages, clientData, signal, streamText }) => {
160
160
  return streamText({
161
161
  model: getModel(clientData.model),
162
162
  messages,
@@ -193,7 +193,7 @@ export const myChat = chat
193
193
  })
194
194
  .agent({
195
195
  id: "my-chat",
196
- run: async ({ messages, signal }) => {
196
+ run: async ({ messages, signal, streamText }) => {
197
197
  return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
198
198
  },
199
199
  });
@@ -218,7 +218,7 @@ chat
218
218
  // Runs second — task-specific logic
219
219
  await createChatRecord(event.chatId);
220
220
  },
221
- run: async ({ messages, signal }) => {
221
+ run: async ({ messages, signal, streamText }) => {
222
222
  return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
223
223
  },
224
224
  });
@@ -176,13 +176,13 @@ A chat agent runs one long-lived worker per conversation and suspends between me
176
176
 
177
177
  ```ts /trigger/chat.ts
178
178
  import { chat } from "@trigger.dev/sdk/ai";
179
- import { streamText } from "ai";
179
+
180
180
  import { openai } from "@ai-sdk/openai";
181
181
  import { prisma } from "@/lib/db";
182
182
 
183
183
  export const myChat = chat.agent({
184
184
  id: "my-chat",
185
- run: async ({ messages, clientData, signal }) => {
185
+ run: async ({ messages, clientData, signal, streamText }) => {
186
186
  const user = await prisma.user.findUnique({ where: { id: clientData.userId } });
187
187
  // The connection is back in the pool before the model stream starts.
188
188
  return streamText({
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: "Trigger.dev docs"
3
3
  sidebarTitle: "Introduction"
4
- description: "Build and run durable AI agents and workflows in TypeScript. Long-running tasks with retries, queues, observability, and elastic scaling. Open source and self-hostable."
4
+ description: "Build and run durable AI agents and workflows in TypeScript. Long-running tasks with retries, queues, webhooks, observability, and elastic scaling. Open source and self-hostable."
5
5
  mode: "center"
6
6
  ---
7
7
 
@@ -11,8 +11,8 @@ import AgentSetupPrompt from "/snippets/agent-setup-prompt.mdx";
11
11
  <Card title="Quick start" icon="rocket" href="/quick-start" color="#A8FF53">
12
12
  Go from zero to your first task in 3 minutes.
13
13
  </Card>
14
- <Card title="Build AI agents" icon="robot" href="/guides/ai-agents/overview" color="#a855f7">
15
- Agent patterns, worked examples, and the tools to run them in production.
14
+ <Card title="Build AI agents" icon="robot" href="/ai-chat/overview" color="#a855f7">
15
+ Durable, multi-turn agents with `chat.agent`, from your frontend to Slack and Linear.
16
16
  </Card>
17
17
  <Card title="AI coding assistants" icon="wand-magic-sparkles" href="/building-with-ai" color="#3B82F6">
18
18
  Let Claude Code, Cursor and Windsurf write correct Trigger.dev code with the MCP server, Skills and rules.
@@ -28,48 +28,124 @@ import AgentSetupPrompt from "/snippets/agent-setup-prompt.mdx";
28
28
 
29
29
  ## What is Trigger.dev?
30
30
 
31
- Trigger.dev is the open source platform for building and running durable AI agents and workflows in TypeScript. Run them for hours, configure automatic retries, control concurrency, and manage queues. There's no execution timeout, and no queue or worker infrastructure for you to build.
31
+ Trigger.dev is the open source platform for building and running durable AI agents and workflows in TypeScript. You write tasks and agents in your existing codebase, and they run on Trigger.dev with retries, queues, waits, full tracing, and elastic scaling. There's no execution timeout, and no queue or worker infrastructure for you to build.
32
32
 
33
- You write agents and tasks in your existing codebase using our CLI and SDK, and get durable execution, queues, waits, full tracing, and elastic scaling without building any of it yourself.
33
+ Trigger.dev's core is Apache-2.0 licensed. [Self-host it](/self-hosting/overview) on your own infrastructure, or let [Trigger.dev Cloud](https://cloud.trigger.dev) run it for you.
34
34
 
35
- Trigger.dev's core is Apache-2.0 licensed, a genuine OSI license. [Self-host it](/self-hosting/overview) on your own infrastructure, or let [Trigger.dev Cloud](https://cloud.trigger.dev) run it for you.
35
+ ## Build agents with `chat.agent`
36
36
 
37
- ## Core concepts
37
+ A `chat.agent` runs each conversation as one durable task. It wakes when a message arrives and idles between turns, and its state survives page refreshes, deploys, and crashes. Turn lifecycle, resuming a stream mid-reply, and approvals are handled for you, and your frontend connects with the AI SDK's `useChat`, with no API routes to write.
38
+
39
+ ```ts trigger/chat.ts
40
+ import { chat } from "@trigger.dev/sdk/ai";
41
+ import { anthropic } from "@ai-sdk/anthropic";
42
+
43
+ export const supportAgent = chat.agent({
44
+ id: "support-agent",
45
+ run: async ({ messages, signal, streamText }) =>
46
+ streamText({
47
+ model: anthropic("claude-sonnet-4-5"),
48
+ messages,
49
+ abortSignal: signal,
50
+ }),
51
+ });
52
+ ```
53
+
54
+ <CardGroup cols={3}>
55
+ <Card title="Agent quick start" icon="rocket" href="/ai-chat/quick-start">
56
+ Build a chat agent and its frontend end to end.
57
+ </Card>
58
+ <Card title="Sessions" icon="layer-group" href="/ai-chat/sessions">
59
+ Each conversation's durable state and two-way stream, across all its runs.
60
+ </Card>
61
+ <Card title="Tools" icon="screwdriver-wrench" href="/ai-chat/tools">
62
+ Declare tools on your agent and get them back typed in `run()`.
63
+ </Card>
64
+ <Card title="Human in the loop" icon="hand" href="/ai-chat/patterns/human-in-the-loop">
65
+ Pause a tool call for approval and resume when a person decides.
66
+ </Card>
67
+ <Card title="Slack agents" icon="slack" href="/webhooks/slack">
68
+ Turn an agent into a native Slack agent with `@trigger.dev/slack`.
69
+ </Card>
70
+ <Card title="Linear agents" icon="list-check" href="/webhooks/linear">
71
+ Assign Linear issues to an agent with `@trigger.dev/linear`.
72
+ </Card>
73
+ </CardGroup>
74
+
75
+ Version your [prompts](/ai/prompts) in code and change them from the dashboard, and see every model step and tool call, with tokens, cost and latency, in [AI observability](/ai/observability).
76
+
77
+ ## Run work
38
78
 
39
79
  <CardGroup cols={2}>
40
80
  <Card title="Writing tasks" icon="pen-to-square" href="/tasks/overview" color="#A8FF53">
41
- The durable functions at the core of Trigger.dev. Write them in your existing codebase, with retries and no timeouts.
81
+ The durable functions at the core of Trigger.dev, written in your existing codebase.
42
82
  </Card>
43
83
  <Card title="Triggering tasks" icon="bullseye-pointer" href="/triggering" color="#3b82f6">
44
- Trigger a task from your backend, your frontend, a schedule, or another task.
84
+ Trigger a task from your backend, your frontend, or another task.
45
85
  </Card>
46
86
  <Card title="Runs" icon="play" href="/runs" color="#28bf5c">
47
- Every execution of a task is a run, with its status, retries, and logs recorded.
87
+ Every execution of a task is a run, with its status, retries, logs and trace.
88
+ </Card>
89
+ <Card title="Realtime" icon="bolt" href="/realtime/overview" color="#F43F5E">
90
+ Stream run status and LLM output to your frontend with React hooks.
91
+ </Card>
92
+ </CardGroup>
93
+
94
+ ## React to events
95
+
96
+ <CardGroup cols={2}>
97
+ <Card title="Webhooks" icon="webhook" href="/webhooks/overview" color="#a855f7">
98
+ Hosted, verified endpoints for Stripe, GitHub and more, delivered to your tasks and agents.
99
+ </Card>
100
+ <Card title="Scheduled tasks" icon="clock" href="/tasks/scheduled" color="#f6cd46">
101
+ Run a task on a cron schedule, with timezone support.
102
+ </Card>
103
+ <Card title="Webhook waiters" icon="bell" href="/webhooks/waiters" color="#3b82f6">
104
+ Pause a run until a matching webhook arrives, like a payment confirmation.
48
105
  </Card>
49
- <Card title="Idempotency" icon="fingerprint" href="/idempotency" color="#F43F5E">
50
- Use an idempotency key so a retried or duplicated trigger runs once, not twice.
106
+ <Card title="Waits" icon="hourglass" href="/wait" color="#28bf5c">
107
+ Pause a run for a duration, until a date, or until a token is completed. A waiting run doesn't use compute.
51
108
  </Card>
52
109
  </CardGroup>
53
110
 
54
- ## Build with agents
111
+ ## Run in production
55
112
 
56
- Build durable, multi-turn agents with the [chat agent](/ai-chat/overview): one long-running task per conversation, with the tools it can call, streaming, and resumability handled for you. Add human-in-the-loop approval by pausing a run on a [waitpoint](/wait-for-token) until a person signs off, and a waiting run doesn't consume compute. Stream run status and LLM output straight to your frontend with the [Realtime API](/realtime/overview) and React hooks, and record your Vercel AI SDK calls as spans with the model, token counts, cost and latency using [AI observability](/ai/observability). Version your [prompts](/ai/prompts) in code, then edit the text or swap the model from the dashboard without redeploying.
113
+ <CardGroup cols={2}>
114
+ <Card title="Queues & concurrency" icon="layer-group" href="/queues" color="#3b82f6">
115
+ Control how many runs execute at once, globally or per tenant.
116
+ </Card>
117
+ <Card title="Machines" icon="microchip" href="/machines" color="#a855f7">
118
+ Choose the CPU and memory each task runs on.
119
+ </Card>
120
+ <Card title="Deployment" icon="cloud-arrow-up" href="/deployment/overview" color="#28bf5c">
121
+ Atomic, versioned deploys, with preview branches and environments.
122
+ </Card>
123
+ <Card title="Errors & retries" icon="arrows-rotate" href="/errors-retrying" color="#F43F5E">
124
+ Retry failed attempts automatically, and decide what happens when a task throws.
125
+ </Card>
126
+ <Card title="Logs, traces & metrics" icon="chart-line" href="/logging" color="#f6cd46">
127
+ A full trace of every run, with your logs and spans.
128
+ </Card>
129
+ <Card title="Query & dashboards" icon="table" href="/observability/query" color="#A8FF53">
130
+ Query your runs with TRQL, and build dashboards from the results.
131
+ </Card>
132
+ </CardGroup>
57
133
 
58
- ## Scale and scheduling
134
+ ## Frequently asked questions
59
135
 
60
- Set how many runs of a task execute at once, globally or per tenant, with [concurrency](/concurrency) and [queues](/queues). Run a task on a [cron schedule](/tasks/scheduled) with timezone support, choose the CPU and memory each task runs on with [machines](/machines), and control what happens when a task throws with [errors and retries](/errors-retrying).
136
+ **Can I use Trigger.dev for background jobs, not just AI?** Yes. The same durable tasks, queues, retries, and [cron schedules](/tasks/scheduled) run any background job or workflow, with or without AI.
61
137
 
62
- ## Self-hosting
138
+ **How long can a run take?** As long as it needs. Runs have no maximum duration by default, and you can set a [`maxDuration`](/runs/max-duration) for a project, a task, or a single run to cap it.
63
139
 
64
- Trigger.dev is open source and you can self-host. Read the [overview guide](/self-hosting/overview), or how to do it with [Docker](/self-hosting/docker) or [Kubernetes](/self-hosting/kubernetes).
140
+ **Do my serverless function timeouts apply?** No. Tasks run on Trigger.dev, not inside your serverless functions, so your platform's function timeout isn't in the execution path. Your backend triggers the task and returns.
65
141
 
66
- ## Frequently asked questions
142
+ **Can I receive webhooks without running a server?** Yes. Declare a [webhook endpoint](/webhooks/overview) in your code, and Trigger.dev hosts its URL, verifies each request, and delivers the event to your tasks and agents.
67
143
 
68
- **How do I add human-in-the-loop approval to an agent?** Pause the run on a [waitpoint](/wait-for-token) until a person approves the action. A waiting run doesn't consume compute, so it can wait minutes or days.
144
+ **How do I add human-in-the-loop approval?** Pause the run on a [waitpoint](/wait-for-token) until a person approves, or use a [chat agent's approvals](/ai-chat/patterns/human-in-the-loop) for tool calls. A waiting run doesn't use compute, so it can wait minutes or days.
69
145
 
70
146
  **Does Trigger.dev support tool calling and MCP?** Yes. Agents call the tools you define, and the [MCP server](/building-with-ai) lets AI coding assistants trigger tasks, deploy, and read run traces.
71
147
 
72
- **Can I use Trigger.dev for background jobs, not just AI?** Yes. The same durable tasks, queues, retries, and [cron schedules](/tasks/scheduled) run any background job or workflow, with or without AI.
148
+ **Can I self-host?** Yes. Trigger.dev is open source. Read the [self-hosting overview](/self-hosting/overview), or set it up with [Docker](/self-hosting/docker) or [Kubernetes](/self-hosting/kubernetes).
73
149
 
74
150
  ## Getting help
75
151
 
@@ -85,7 +161,7 @@ We'd love to hear from you or give you a hand getting started. Here are some way
85
161
  Our Discord is the best place to get help with any questions about Trigger.dev.
86
162
  </Card>
87
163
  <Card
88
- title="Follow us on X (Twitter)"
164
+ title="Follow us on X"
89
165
  icon={
90
166
  <svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 0 512 512">
91
167
  <path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" />
@@ -112,7 +188,7 @@ We'd love to hear from you or give you a hand getting started. Here are some way
112
188
  href="https://github.com/triggerdotdev/trigger.dev"
113
189
  color="#fbbf24"
114
190
  >
115
- Check us out our GitHub repo and give us a star if you like what we're doing.
191
+ Check out our GitHub repo, and give us a star if you like what we're doing.
116
192
  </Card>
117
193
  </CardGroup>
118
194
 
@@ -122,12 +198,16 @@ We'd love to hear from you or give you a hand getting started. Here are some way
122
198
 
123
199
  **Current major version:** v4 (`@trigger.dev/sdk` 4.x). Documentation under `/docs/v2` and `/docs/v3` describes superseded versions and should not be used.
124
200
 
125
- **Execution model:** tasks run on Trigger.dev infrastructure, not inside your serverless functions, so a platform function timeout is not in the execution path. A task runs until it finishes; you can cap runtime with `maxDuration` (minimum 5 seconds).
201
+ **Execution model:** tasks run on Trigger.dev infrastructure, not inside your serverless functions, so a platform function timeout is not in the execution path. Runs have no maximum duration by default; `maxDuration` caps a run's compute time for a project, a task, or a single run, and `timeout.None` removes the cap.
126
202
 
127
203
  **Deploys:** each deploy is an atomic version. A run already in progress is not affected by code shipped after it started.
128
204
 
129
205
  **Durability and retries:** retries are automatic and configurable. A failed attempt is retried according to the task's retry settings; once retries are exhausted the run ends terminally as `FAILED`, and some crashes (for example out of memory) end the run as `CRASHED` without a retry. Idempotency keys deduplicate the task triggers and waits you key, so the same key runs once instead of again.
130
206
 
207
+ **Agents:** `chat.agent` is imported from `@trigger.dev/sdk/ai`, not from `@trigger.dev/sdk`. Inside `run()`, call the `streamText` passed in the run options. It records each model step and tool call as a span by default. Slack and Linear agents use the `@trigger.dev/slack` and `@trigger.dev/linear` packages, and `slack()` requires a `startOn` filter (`appMentions()` answers @mentions).
208
+
209
+ **Webhooks:** `webhooks.endpoint.define()` declares a hosted, verified endpoint that syncs on `trigger dev` and on deploy; `webhook()` subscribes a task with a filter, and `webhooks.waiter` pauses a run until a matching event arrives. Both come from `@trigger.dev/sdk`.
210
+
131
211
  **License:** the platform core is Apache-2.0 and the `@trigger.dev/sdk` package is MIT, permissive open source, not fair-code or source-available. Some features are gated, but the open source core is not relicensed or held back.
132
212
 
133
213
  **LLM observability coverage:** only Vercel AI SDK calls (`generateText`, `streamText`, `generateObject`) are recorded as spans. Raw `fetch` calls and provider SDKs used directly are not captured.
@@ -411,6 +411,10 @@ Generate a new signing secret for an endpoint and return it once, for providers
411
411
 
412
412
  **Parameters:**
413
413
  - `endpointId` (required): the endpoint's declared id (such as `payments`) or `wh_` id
414
+ - `previousSecretTtlSeconds` (optional): keep accepting the replaced secret for this many seconds (up to 604800, 7 days) while the provider switches to the new one. Omit it, or pass `0`, to replace the secret immediately, for example when it leaked. The response says when the old secret stops working.
415
+
416
+ **Example usage:**
417
+ - `"Rotate the GitHub endpoint's secret and keep the old one working for a day"`
414
418
 
415
419
  ### set_webhook_secret
416
420
 
@@ -419,6 +423,7 @@ Store a signing secret or public key the provider issued, such as Stripe's `whse
419
423
  **Parameters:**
420
424
  - `endpointId` (required): the endpoint's declared id (such as `payments`) or `wh_` id
421
425
  - `secret` (required): the secret or public key
426
+ - `previousSecretTtlSeconds` (optional): keep accepting the replaced secret for this many seconds (up to 604800, 7 days) while the provider switches to the new one. Omit it, or pass `0`, to replace the secret immediately, for example when it leaked. The response says when the old secret stops working.
422
427
 
423
428
  <Callout type="warning">
424
429
  `replay_webhook_delivery`, `generate_webhook_secret` and `set_webhook_secret` are write operations and are not available in readonly mode.
@@ -52,6 +52,7 @@ mode: "wide"
52
52
  | `KUBERNETES_IMAGE_PULL_SECRETS` | No | — | Image pull secrets (CSV). |
53
53
  | `KUBERNETES_EPHEMERAL_STORAGE_SIZE_LIMIT` | No | 10Gi | Ephemeral storage size limit. Applies to all runs. |
54
54
  | `KUBERNETES_EPHEMERAL_STORAGE_SIZE_REQUEST` | No | 2Gi | Ephemeral storage size request. Applies to all runs. |
55
+ | `KUBERNETES_POD_RESERVED_SOURCE_PORTS` | No | — | Ports runs never use as the source port of an outbound connection, so they can't clash with services on those ports. CSV of ports or ranges, e.g. `50080,50081` or `50080-50090`. Sets the pod's `net.ipv4.ip_local_reserved_ports` sysctl. |
55
56
  | **Metrics** | | | |
56
57
  | `METRICS_ENABLED` | No | true | Enable metrics. |
57
58
  | `METRICS_COLLECT_DEFAULTS` | No | true | Collect default metrics. |
@@ -192,6 +192,18 @@ mode: "wide"
192
192
  | `RUN_ENGINE_DEFAULT_MAX_TTL` | No | — | Maximum TTL for all runs (e.g. "14d"). Runs without a TTL use this as default; runs with a larger TTL are clamped. |
193
193
  | `MAXIMUM_DEV_QUEUE_SIZE` | No | — | Maximum queued runs per queue in development environments. |
194
194
  | `MAXIMUM_DEPLOYED_QUEUE_SIZE` | No | — | Maximum queued runs per queue in deployed (staging/prod) environments. |
195
+ | **Webhooks** | | | |
196
+ | `WEBHOOK_ENABLED` | No | 0 | Set to `1` to run hosted webhooks: ingest, delivery and waiters. See [Webhooks](#webhooks). |
197
+ | `WEBHOOK_REDIS_HOST` | No | localhost | A node of the Redis cluster webhooks use for their queue and waiters. It must run in cluster mode. |
198
+ | `WEBHOOK_REDIS_PORT` | No | 6379 | Port of that node. |
199
+ | `WEBHOOK_REDIS_USERNAME` | No | — | Webhooks Redis username. |
200
+ | `WEBHOOK_REDIS_PASSWORD` | No | — | Webhooks Redis password. |
201
+ | `WEBHOOK_REDIS_TLS_DISABLED` | No | `REDIS_TLS_DISABLED` | Set to `true` to connect to the webhooks Redis without TLS. |
202
+ | `WEBHOOK_INGRESS_ORIGIN` | No | `API_ORIGIN` | Public origin of the webhook URLs, for when you serve them from their own host. |
203
+ | `WEBHOOK_INGRESS_BODY_SIZE_LIMIT_MB` | No | 1 | Largest webhook request body accepted, in MB. |
204
+ | `WEBHOOK_DATABASE_URL` | No | `DATABASE_URL` | A separate PostgreSQL database for webhook endpoints and deliveries. |
205
+ | `WEBHOOK_DELIVERY_RETENTION_DAYS` | No | 30 | How many days of deliveries the dashboard and API show. It doesn't delete anything: deliveries are kept for `WEBHOOK_DELIVERY_STORAGE_DAYS`, or longer when this is higher. |
206
+ | `WEBHOOK_DELIVERY_STORAGE_DAYS` | No | 30 | How long deliveries are stored before they're deleted. Must be one of `3`, `7`, `30`, `90`, `180` or `365`; any other value stops the webapp from starting. |
195
207
  | **Misc** | | | |
196
208
  | `TRIGGER_TELEMETRY_DISABLED` | No | — | Disable telemetry. |
197
209
  | `NODE_MAX_OLD_SPACE_SIZE` | No | 8192 | Maximum memory allocation for Node.js heap in MiB (e.g. "4096" for 4GB). |
@@ -202,6 +214,18 @@ mode: "wide"
202
214
  | `ADMIN_DASHBOARD_ENABLED` | No | 1 | Set to anything other than `1` or `true` to disable the admin dashboard and user impersonation on this instance. |
203
215
  | `EVENT_LOOP_MONITOR_ENABLED` | No | 1 | Node.js event loop lag monitor. |
204
216
 
217
+ ## Webhooks
218
+
219
+ Hosted webhooks are off on a self-hosted instance until you turn them on:
220
+
221
+ 1. Run a Redis (or Valkey) cluster for webhooks and point `WEBHOOK_REDIS_HOST` and `WEBHOOK_REDIS_PORT` at one of its nodes. The webhook engine connects in cluster mode, so a standalone Redis won't work.
222
+ 2. Set `WEBHOOK_ENABLED=1` on the webapp.
223
+ 3. Give organizations access with the `hasWebhooksAccess` feature flag: turn it on for everyone on the admin dashboard's **Global Feature Flags** page, or for one organization from its feature flags on the **Organizations** page. An organization without it doesn't see Webhooks in the dashboard, and its deploys don't sync endpoints.
224
+
225
+ Webhook URLs are served from `WEBHOOK_INGRESS_ORIGIN`, which defaults to `API_ORIGIN`, so providers need to reach that origin from the internet.
226
+
227
+ The Docker Compose setup only passes the variables listed under the webapp service's `environment` in `hosting/docker/webapp/docker-compose.yml`, so add the webhook variables there. With the Helm chart, set them in `webapp.extraEnvVars`.
228
+
205
229
  ## Multi-Provider Object Storage
206
230
 
207
231
  The object storage system supports multiple S3-compatible providers (R2, S3, GCS, MinIO, etc.) using protocol prefixes. This enables migrating between providers without breaking existing runs.
@@ -121,6 +121,8 @@ You own a custom base image. It must provide:
121
121
 
122
122
  A `DEPLOY_BUILD_BASE_IMAGES` image needs everything the base image provides, plus `python3`, `make` and `g++`. Build extensions that add image instructions are replayed on it. Without an entry, the build stage uses the published build image, except for projects whose build extensions add image instructions: those build from your base image and install the toolchain with `apt-get`, so that base must be Debian-based. To avoid the published images entirely, set both variables.
123
123
 
124
+ A custom image is yours to harden. Deploys on the published images remove trust in publicly compromised certificate authorities and point Node.js and Python at the OS trust store. Stages built on a custom image get neither, and run with the certificate settings the image ships with. Keeping a custom image patched, trust store included, is your responsibility.
125
+
124
126
  <Warning>
125
127
  `image.pkgs` and build extensions that run `apt-get` (such as `aptGet` and `playwright`) assume a
126
128
  Debian base. On other distributions, install those packages in your base image instead.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trigger.dev/sdk",
3
- "version": "4.8.0",
3
+ "version": "4.8.1",
4
4
  "description": "trigger.dev Node.JS SDK",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -69,7 +69,7 @@
69
69
  "dependencies": {
70
70
  "@opentelemetry/api": "1.9.1",
71
71
  "@opentelemetry/semantic-conventions": "1.41.1",
72
- "@trigger.dev/core": "4.8.0",
72
+ "@trigger.dev/core": "4.8.1",
73
73
  "uncrypto": "^0.1.3"
74
74
  },
75
75
  "devDependencies": {