@trigger.dev/sdk 4.7.4 → 4.7.5

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.
Files changed (48) hide show
  1. package/dist/commonjs/v3/ai.d.ts +26 -0
  2. package/dist/commonjs/v3/ai.js +7 -5
  3. package/dist/commonjs/v3/ai.js.map +1 -1
  4. package/dist/commonjs/v3/query.d.ts +434 -1
  5. package/dist/commonjs/v3/query.js +115 -0
  6. package/dist/commonjs/v3/query.js.map +1 -1
  7. package/dist/commonjs/version.js +1 -1
  8. package/dist/esm/v3/ai.d.ts +26 -0
  9. package/dist/esm/v3/ai.js +7 -5
  10. package/dist/esm/v3/ai.js.map +1 -1
  11. package/dist/esm/v3/query.d.ts +434 -1
  12. package/dist/esm/v3/query.js +115 -0
  13. package/dist/esm/v3/query.js.map +1 -1
  14. package/dist/esm/version.js +1 -1
  15. package/docs/ai-chat/actions.mdx +6 -1
  16. package/docs/ai-chat/backend.mdx +47 -0
  17. package/docs/ai-chat/patterns/redacting-telemetry.mdx +117 -0
  18. package/docs/ai-chat/pending-messages.mdx +2 -0
  19. package/docs/ai-chat/reference.mdx +53 -2
  20. package/docs/ai-chat/transcript-storage.mdx +1 -1
  21. package/docs/apikeys.mdx +6 -4
  22. package/docs/cli-runs-commands.mdx +2 -2
  23. package/docs/cli-tasks-commands.mdx +95 -0
  24. package/docs/config/config-file.mdx +58 -0
  25. package/docs/config/extensions/prismaExtension.mdx +4 -4
  26. package/docs/deploy-environment-variables.mdx +4 -0
  27. package/docs/deployment/atomic-deployment.mdx +3 -3
  28. package/docs/deployment/overview.mdx +1 -1
  29. package/docs/deployment/preview-branches.mdx +1 -1
  30. package/docs/github-actions.mdx +3 -3
  31. package/docs/limits.mdx +6 -0
  32. package/docs/manual-setup.mdx +1 -1
  33. package/docs/mcp-tools.mdx +99 -3
  34. package/docs/observability/dashboards.mdx +62 -0
  35. package/docs/runs/max-duration.mdx +4 -0
  36. package/docs/self-hosting/env/supervisor.mdx +1 -0
  37. package/docs/webhooks/channels.mdx +158 -0
  38. package/docs/webhooks/connect.mdx +84 -0
  39. package/docs/webhooks/deliveries.mdx +103 -0
  40. package/docs/webhooks/filters.mdx +144 -0
  41. package/docs/webhooks/human-in-the-loop.mdx +162 -0
  42. package/docs/webhooks/linear.mdx +190 -0
  43. package/docs/webhooks/overview.mdx +119 -0
  44. package/docs/webhooks/session-routing.mdx +145 -0
  45. package/docs/webhooks/slack.mdx +397 -0
  46. package/docs/webhooks/sources.mdx +221 -0
  47. package/docs/webhooks/waiters.mdx +194 -0
  48. package/package.json +3 -2
@@ -0,0 +1,117 @@
1
+ ---
2
+ title: "Redact chat content from traces"
3
+ sidebarTitle: "Redact chat content"
4
+ description: "Stop a chat agent recording user messages and clientData on its spans, turn off AI SDK prompt and response recording, or redact span content with a span processor before it leaves the worker."
5
+ ---
6
+
7
+ A chat agent's run trace records conversation content by default: the user's message and the `clientData` your frontend sends. When AI SDK telemetry is on, it also records the prompts and responses of each model call. This page covers the three controls that change that, and what they don't cover.
8
+
9
+ ## What the run trace records
10
+
11
+ | Span | Content | Control |
12
+ | --- | --- | --- |
13
+ | `chat turn N` | The last user message (`chat.user_message`) and its first 80 characters as the span preview | `telemetry.recordUserMessage` |
14
+ | `chat turn N` | The raw `clientData` (`chat.client_data`) | `telemetry.recordClientData` |
15
+ | `pending message injected` | Messages sent mid-turn (`pending.messages`) | `telemetry.recordUserMessage` |
16
+ | AI SDK model call spans, when AI SDK telemetry is on | Prompt messages, response text, tool calls | `experimental_telemetry` on the model call |
17
+ | Any span | Everything above, plus your own attributes and span events | A span processor |
18
+
19
+ ## Stop recording chat content
20
+
21
+ Set `telemetry` on the agent to stop the chat runtime recording the user's message and `clientData` on its spans. Both default to `true`.
22
+
23
+ ```ts /trigger/support-agent.ts
24
+ import { chat } from "@trigger.dev/sdk/ai";
25
+ import { anthropic } from "@ai-sdk/anthropic";
26
+
27
+ export const supportAgent = chat.agent({
28
+ id: "support-agent",
29
+ telemetry: { recordUserMessage: false, recordClientData: false },
30
+ run: async ({ messages, signal, streamText }) =>
31
+ streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal }),
32
+ });
33
+ ```
34
+
35
+ The spans still appear in the trace with their timings and token usage. The turn spans have no preview text.
36
+
37
+ ## Stop the AI SDK recording prompts and responses
38
+
39
+ AI SDK model call spans appear when AI SDK telemetry is on: [`chat.prompt.set()`](/ai-chat/backend#using-prompts) turns it on for you, or you pass `experimental_telemetry` yourself. Those spans include prompts and responses unless you turn that off with `recordInputs` and `recordOutputs`:
40
+
41
+ ```ts /trigger/support-agent.ts
42
+ import { chat } from "@trigger.dev/sdk/ai";
43
+ import { anthropic } from "@ai-sdk/anthropic";
44
+
45
+ export const supportAgent = chat.agent({
46
+ id: "support-agent",
47
+ telemetry: { recordUserMessage: false, recordClientData: false },
48
+ run: async ({ messages, signal, streamText }) =>
49
+ streamText({
50
+ model: anthropic("claude-sonnet-4-5"),
51
+ messages,
52
+ abortSignal: signal,
53
+ // Replaces any telemetry config from chat.prompt.set(), so keep isEnabled on
54
+ experimental_telemetry: { isEnabled: true, recordInputs: false, recordOutputs: false },
55
+ }),
56
+ });
57
+ ```
58
+
59
+ If you use `chat.prompt.set()`, your `experimental_telemetry` replaces the one it adds, including the metadata that links each generation to its prompt version. To keep that link, redact with a span processor instead.
60
+
61
+ ## Redact instead of dropping
62
+
63
+ To keep the content but strip sensitive values from it, add a span processor in `trigger.config.ts`. It runs on every span before export, so one processor covers the chat runtime's spans, the AI SDK spans and your own.
64
+
65
+ ```ts trigger.config.ts
66
+ import { defineConfig } from "@trigger.dev/sdk";
67
+ import type { AttributeValue, Attributes } from "@opentelemetry/api";
68
+ import type { SpanProcessor } from "@opentelemetry/sdk-trace-base";
69
+
70
+ const EMAIL = /[\w.+-]+@[\w-]+\.[\w.]+/g;
71
+
72
+ function redact(value: AttributeValue | undefined): AttributeValue | undefined {
73
+ if (typeof value === "string") return value.replace(EMAIL, "[email]");
74
+ // Some attributes are string arrays, like the messages sent mid-turn
75
+ if (Array.isArray(value)) {
76
+ return value.map((item) =>
77
+ typeof item === "string" ? item.replace(EMAIL, "[email]") : item
78
+ ) as AttributeValue;
79
+ }
80
+ return value;
81
+ }
82
+
83
+ function scrub(attributes: Attributes) {
84
+ for (const key of Object.keys(attributes)) attributes[key] = redact(attributes[key]);
85
+ }
86
+
87
+ const redactEmails: SpanProcessor = {
88
+ onStart: () => {},
89
+ onEnding: (span) => {
90
+ scrub(span.attributes);
91
+ for (const event of span.events) scrub(event.attributes ?? {});
92
+ },
93
+ onEnd: () => {},
94
+ forceFlush: async () => {},
95
+ shutdown: async () => {},
96
+ };
97
+
98
+ export default defineConfig({
99
+ project: "<project ref>",
100
+ telemetry: { spanProcessors: [redactEmails] },
101
+ });
102
+ ```
103
+
104
+ Scrubbing every string value, rather than matching attribute names, also catches the turn preview text. See [Span processors](/config/config-file#span-processors) for when each hook runs and which attributes are kept.
105
+
106
+ ## What spans don't cover
107
+
108
+ These controls change the run trace only. Conversation content also reaches the platform in two other places:
109
+
110
+ - **Session streams.** Each message your frontend sends is written to the session's `session.in` stream, and the agent's output to `session.out`. They hold message content for their retention window, and the session view in the dashboard reads from them. See [transcript storage limits](/ai-chat/transcript-storage#guarantees-and-limits).
111
+ - **The run payload.** A chat agent run's payload includes `clientData` as `metadata`, and every `session.in` record carries it too. Send an id in `clientData` and look up the rest in `onPreload` or `run` instead of sending the values themselves.
112
+
113
+ To keep a value off the platform entirely, redact or tokenize it in your frontend or backend before it's sent. The model then sees the redacted value too, unless your tools resolve the tokens.
114
+
115
+ ## Verify it
116
+
117
+ Send a message containing a marker value (an email address, for example), then open the run in the dashboard. Select the `chat turn 1` span and check its properties: the marker is gone, or shows as `[email]` with the span processor above. The span list shows the same redacted preview.
@@ -12,6 +12,8 @@ By default (without `pendingMessages`), a message sent while the agent is respon
12
12
 
13
13
  The `pendingMessages` option enables steering instead, injecting user messages between tool-call steps via the AI SDK's `prepareStep`. Messages that arrive during streaming are queued and injected at the next step boundary. A message that is not injected becomes the next turn instead, whether that is because `shouldInject` returned `false` or because there were no more step boundaries (single-step response or final text generation). The backend handles that, so no client-side re-send is involved.
14
14
 
15
+ Messages from a [channel](/webhooks/channels) such as [Slack](/webhooks/slack) are offered the same way, with `metadata.source` set to `"channel"` so `shouldInject` can tell them apart. A channel's stop control and approval clicks are never offered. The connector acknowledges a channel message as soon as it arrives (Slack reacts with 👀, Linear posts a thought), before `shouldInject` decides, so the acknowledgement is the same whether the message is folded in or answered next. Channels are part of the [webhooks private beta](https://trigger.dev/webhooks-beta).
16
+
15
17
  Use the `streamText` passed to your agent's `run` callback. It wires up pending-message injection automatically. If you import `streamText` directly from `ai`, spread `chat.toStreamTextOptions()` into its options to connect injection.
16
18
 
17
19
  ## How it works
@@ -47,7 +47,9 @@ Options for `chat.agent()`.
47
47
  | `storage` | `TranscriptStorage` | `defaultStorage` | Where the conversation is persisted and read back. The platform snapshot by default; bring your own to write each change to your database. See [Transcript storage](/ai-chat/transcript-storage) |
48
48
  | `hydrateMessages` | `(event: HydrateMessagesEvent) => UIMessage[] \| Promise<UIMessage[]>` | — | **Deprecated.** Load message history from backend, replacing the linear accumulator. Use `loadContext` on a `storage` instead; cannot be combined with `storage`. See [hydrateMessages](/ai-chat/lifecycle-hooks#hydratemessages) |
49
49
  | `actionSchema` | `TaskSchema` | — | Schema for validating custom actions sent via `transport.sendAction()`. See [Actions](/ai-chat/actions) |
50
- | `onAction` | `(event: ActionEvent) => Promise<void \| ActionTurn> \| void \| ActionTurn` | — | Handle custom actions. Actions are state edits: only `hydrateMessages` (or a storage's `loadContext`) + `onAction` fire. Return `chat.turn()` to run a turn on the edited history, or nothing for an edit only. See [Actions](/ai-chat/actions) |
50
+ | `onAction` | `(event: ActionEvent) => Promise<void \| ActionTurn> \| void \| ActionTurn` | — | Handle custom actions. Actions are state edits: only `hydrateMessages` (or a storage's `loadContext`) + `onAction` fire. Return `chat.turn()` to run a turn on the edited history (`chat.turn(message)` to answer a new user message), or nothing for an edit only. See [Actions](/ai-chat/actions) |
51
+ | `events` | `ChatEvent[]` | — | Webhook event descriptors (from `chat.event()`) whose verified deliveries are routed to this agent as actions and handled in `onAction`. See [session routing](/webhooks/session-routing). Part of the [webhooks private beta](https://trigger.dev/webhooks-beta). |
52
+ | `channels` | `ChannelConnector[]` | — | Channel connectors (for example `slack()`) that turn an external chat surface into a frontend for the agent: inbound messages run as turns and the reply posts back. See [channels](/webhooks/channels). Part of the [webhooks private beta](https://trigger.dev/webhooks-beta). |
51
53
  | `onTurnStart` | `(event: TurnStartEvent) => Promise<void> \| void` | — | Fires every turn before `run()` |
52
54
  | `onBeforeTurnComplete` | `(event: BeforeTurnCompleteEvent) => Promise<void> \| void` | — | Fires after response but before stream closes. Includes `writer`. |
53
55
  | `onTurnComplete` | `(event: TurnCompleteEvent) => Promise<void> \| void` | — | Fires after each turn completes (stream closed) |
@@ -71,6 +73,7 @@ Options for `chat.agent()`.
71
73
  | `onChatResume` | `(event: ChatResumeEvent) => Promise<void> \| void` | — | Fires right after the run resumes from suspension |
72
74
  | `exitAfterPreloadIdle` | `boolean` | `false` | Exit run after preload idle timeout instead of suspending. See [exitAfterPreloadIdle](/ai-chat/lifecycle-hooks#exitafterpreloadidle) |
73
75
  | `oomMachine` | `MachinePresetName` | — | Fallback machine when an attempt fails with OOM. Setting it enables a single OOM retry on the larger machine. See [OOM resilience](/ai-chat/patterns/oom-resilience) |
76
+ | `telemetry` | `{ recordUserMessage?: boolean; recordClientData?: boolean }` | both `true` | What the chat runtime records on its spans. `recordUserMessage: false` drops the user message and turn preview (`chat.user_message`, `pending.messages`); `recordClientData: false` drops `chat.client_data`. See [Redact chat content from traces](/ai-chat/patterns/redacting-telemetry) |
74
77
 
75
78
  Plus most standard [TaskOptions](/tasks/overview) — `queue`, `machine`, `maxDuration`, **`onWait`**, **`onResume`**, **`onComplete`**, and other lifecycle hooks. Generic `retry` is **not** exposed on `chat.agent`; use `oomMachine` for OOM recovery, or drop down to a raw [`task()`](/ai-chat/custom-agents) if you need richer retry semantics. Standard hooks use the same parameter shapes as on a normal `task()` (including `ctx`).
76
79
 
@@ -232,7 +235,7 @@ The persistence adapter set through `chat.agent({ storage })`. See [Transcript s
232
235
  | Type | Shape |
233
236
  | --- | --- |
234
237
  | `TranscriptScope<TClientData>` | `{ chatId: string; clientData: TClientData }` |
235
- | `TranscriptStorageContext<TClientData>` | `TranscriptScope` plus `{ turn: number; trigger: "submit-message" \| "regenerate-message" \| "action"; runId: string; ctx: TaskRunContext }` |
238
+ | `TranscriptStorageContext<TClientData>` | `TranscriptScope` plus `{ turn: number; trigger: "submit-message" \| "regenerate-message" \| "action" \| "action-turn"; source?: "client" \| "channel" \| "webhook"; connectorId?: string; runId: string; ctx: TaskRunContext }`. `action-turn` is the turn `chat.turn()` requested; `source` names the surface that drove the write |
236
239
  | `TranscriptChange` | `{ op: "put"; message: UIMessage; final?: boolean }` \| `{ op: "remove"; id: string }` \| `{ op: "truncateAfter"; afterId: string }` \| `{ op: "state"; value: unknown \| null }` |
237
240
  | `TranscriptChangeset` | `{ reason: "turn-complete" \| "turn-error" \| "action" \| "compaction" \| "recovery"; changes: TranscriptChange[]; transcript: TranscriptState; cursors?: TranscriptCursors }` |
238
241
  | `TranscriptState` | `{ entries: Array<{ id: string; final: boolean; message: UIMessage }>; state: unknown \| null }`: the whole conversation after the changeset's changes, for stores that write one document |
@@ -275,6 +278,7 @@ Passed to the `onAction` callback. See [Actions](/ai-chat/actions).
275
278
  | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
276
279
  | `uiMessages` | `UIMessage[]` | Accumulated UI messages (after hydration, if set) |
277
280
  | `messages` | `ModelMessage[]` | Accumulated model messages (after hydration, if set) |
281
+ | `channel` | `{ connectorId, provider, event, deliveryId }` | For a channel action (a button the agent posted, such as a Slack `slackButton()`): the verified click event. Absent otherwise |
278
282
 
279
283
  ## TurnStartEvent
280
284
 
@@ -538,6 +542,8 @@ All methods available on the `chat` object from `@trigger.dev/sdk/ai`.
538
542
  | Method | Description |
539
543
  | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
540
544
  | `chat.agent(options)` | Create a chat agent |
545
+ | `chat.event(options)` | Declare an inbound webhook event descriptor an agent claims via `chat.agent({ events })`. See [session routing](/webhooks/session-routing). Part of the [webhooks private beta](https://trigger.dev/webhooks-beta). |
546
+ | `chat.channels.custom(options)` | Create a generic chat-frontend channel over any verified webhook source (you supply the egress). The `slack()` preset ships in `@trigger.dev/slack`. See [channels](/webhooks/channels). Part of the [webhooks private beta](https://trigger.dev/webhooks-beta). |
541
547
  | `chat.createSession(payload, options)` | Create an async iterator for chat turns |
542
548
  | `chat.pipe(source, options?)` | Pipe a stream to the frontend (from anywhere inside a task) |
543
549
  | `chat.pipeAndCapture(source, options?)` | Pipe and capture the response; returns `{ message, status, error }` |
@@ -570,6 +576,51 @@ All methods available on the `chat` object from `@trigger.dev/sdk/ai`.
570
576
  | `chat.withUIMessage(config?)` | Returns a [ChatBuilder](/ai-chat/types#chatbuilder) with a fixed `UIMessage` subtype. See [Types](/ai-chat/types) |
571
577
  | `chat.withClientData({ schema })` | Returns a [ChatBuilder](/ai-chat/types#chatbuilder) with a fixed client data schema. See [Types](/ai-chat/types#typed-client-data-with-chatwithclientdata) |
572
578
 
579
+ ## `chat.event`
580
+
581
+ Declare an inbound webhook event that an agent claims via [`events`](#chatagentoptions) on `chat.agent()`. It is a descriptor only, with no handler: it names the [endpoint](/webhooks/overview) it subscribes to, a `key` template that resolves each delivery to a durable [session](/ai-chat/sessions), and an optional `type` label (defaults to the descriptor `id`). Verified deliveries are routed to that session and arrive at `onAction` as a `{ type, event, source, headers, deliveryId, endpoint }` envelope, not as a chat turn. See [session routing](/webhooks/session-routing).
582
+
583
+ ```ts
584
+ import { webhooks } from "@trigger.dev/sdk";
585
+ import { chat } from "@trigger.dev/sdk/ai";
586
+
587
+ export const stripe = webhooks.endpoint.define({ id: "stripe", source: webhooks.stripe() });
588
+
589
+ export const orderEvents = chat.event({
590
+ id: "order-events",
591
+ endpoint: stripe,
592
+ key: "{event.data.object.customer}",
593
+ // invoices always carry a customer; events without one would have no session to go to
594
+ filter: "event.type startsWith 'invoice.'",
595
+ type: "order.event",
596
+ });
597
+ ```
598
+
599
+ ## `chat.channels.custom`
600
+
601
+ Create a generic chat-frontend channel on any [endpoint](/webhooks/overview), claimed via [`channels`](#chatagentoptions) on `chat.agent()`. You supply the session `key`, the `inbound` map from event to turn message, and your own `send` egress that posts the reply back, so the whole round-trip is under your control. Inbound messages run as normal turns and the reply is posted back. The `slack()` preset ships in `@trigger.dev/slack` and wires the egress for you. See [channels](/webhooks/channels), and the interactive approvals layer at [human-in-the-loop](/webhooks/human-in-the-loop).
602
+
603
+ ```ts
604
+ import { webhooks } from "@trigger.dev/sdk";
605
+ import { chat } from "@trigger.dev/sdk/ai";
606
+
607
+ const surface = webhooks.endpoint.define({
608
+ id: "my-surface",
609
+ source: webhooks.custom<MyEvent>({ /* verifier config */ }),
610
+ });
611
+
612
+ export const mySurface = chat.channels.custom({
613
+ id: "my-surface",
614
+ endpoint: surface,
615
+ key: "{event.conversationId}",
616
+ inbound: (event) => event.text,
617
+ send: async (message, ctx) => {
618
+ const ref = await postToMySurface(ctx.event, message.text, ctx.previousRef);
619
+ return { ref };
620
+ },
621
+ });
622
+ ```
623
+
573
624
  ## `chat.withUIMessage`
574
625
 
575
626
  Returns a [`ChatBuilder`](/ai-chat/types#chatbuilder) with a fixed `UIMessage` subtype. Chain `.withClientData()`, hook methods, and `.agent()`.
@@ -226,7 +226,7 @@ A few things to get right:
226
226
  ## Guarantees and limits
227
227
 
228
228
  - Crash recovery of a half-written answer is runtime-owned in every configuration. It comes from the durable session stream, which no application database can reconstruct. A storage holds settled turns; the runtime overlays the recovered tail and hands it to `save` like any other change.
229
- - Bringing your own database does not remove platform custody. Session streams still hold message content for their retention window.
229
+ - Bringing your own database does not remove platform custody. Session streams still hold message content for their retention window. To keep content out of the run trace, see [Redact chat content from traces](/ai-chat/patterns/redacting-telemetry).
230
230
  - The default storage rewrites the whole conversation each turn. A row-per-message storage writes only what changed. That is the reason to plug in your own.
231
231
 
232
232
  ## Migrating from hydrateMessages
package/docs/apikeys.mdx CHANGED
@@ -81,8 +81,10 @@ Access presets define what a key can do. On the Free and Hobby plans only the **
81
81
  | --- | --- |
82
82
  | **Trigger only** | Trigger runs and batches for all or selected tasks. Trigger responses include scoped public access tokens for the runs and batches they create |
83
83
  | **Task operator** | Trigger all or selected tasks and inspect or operate on their runs |
84
- | **Observer** | Read runs, tasks, batches, logs, traces, and queues |
85
- | **Operator** | Observe and operate on runs and queues, and trigger tasks |
84
+ | **Observer** | Read runs, tasks, batches, logs, traces, queues, errors, and webhooks |
85
+ | **Operator** | Observe and operate on runs, queues, errors, and webhooks, and trigger tasks |
86
+ | **Webhooks only** | Read and write [webhooks](/webhooks/overview), which are in private beta. Replaying deliveries that trigger tasks and creating webhook waiters require task or waitpoint scopes |
87
+ | **Waitpoints only** | Create, list, and retrieve [waitpoint tokens](/wait-for-token). No task or run access |
86
88
  | **Deploy only** | Deploy versions, sync environment variables, and manage Preview branches |
87
89
  | **Variables only** | Read and write environment variables in this environment |
88
90
  | **No restrictions** | Full access to the environment |
@@ -96,11 +98,11 @@ Set a key in `TRIGGER_ACCESS_TOKEN` to authenticate `trigger deploy` without log
96
98
  <CodeGroup>
97
99
 
98
100
  ```bash npm
99
- TRIGGER_ACCESS_TOKEN="tr_prod_sk_…" npx trigger.dev@latest deploy
101
+ TRIGGER_ACCESS_TOKEN="tr_prod_sk_…" npx trigger.dev@latest deploy --env prod
100
102
  ```
101
103
  </CodeGroup>
102
104
 
103
- The key must belong to the target environment. Use a Production key for the default deployment, a Staging key with `--env staging`, or a key from the Preview environment with `--env preview`. A Preview deployment key can create and archive Preview branches and sync their environment variables.
105
+ The key must belong to the target environment. Use a Production key with `--env prod`, a Staging key with `--env staging`, or a key from the Preview environment with `--env preview`. If you leave out `--env`, the CLI deploys to the key's environment, and a `--env` that doesn't match the key is an error. A Preview deployment key can create and archive Preview branches and sync their environment variables.
104
106
 
105
107
  <Note>
106
108
  The deploy CLI reads environment API keys from `TRIGGER_ACCESS_TOKEN`, not
@@ -84,8 +84,8 @@ npx trigger.dev@latest runs cancel run_abc123
84
84
 
85
85
  <ConfigFileOptions />
86
86
  <ProjectRefOptions />
87
- <ParamField body="Preview branch" type="--branch | -b">
88
- Required when using `--env preview`. Specify the branch, e.g. `--branch my-branch` or `-b my-branch`.
87
+ <ParamField body="Branch" type="--branch | -b">
88
+ Required when using `--env preview`. Specify the branch, e.g. `--branch my-branch` or `-b my-branch`. With `--env dev`, it defaults to the same [dev branch](/deployment/dev-branches) as the dev command.
89
89
  </ParamField>
90
90
 
91
91
  ### Common options
@@ -0,0 +1,95 @@
1
+ ---
2
+ title: "CLI tasks commands"
3
+ sidebarTitle: "tasks"
4
+ description: "Use these commands to trigger tasks from your terminal."
5
+ ---
6
+
7
+ import CommonOptions from "/snippets/cli-options-common.mdx";
8
+ import ConfigFileOptions from "/snippets/cli-options-config-file.mdx";
9
+ import ProjectRefOptions from "/snippets/cli-options-project-ref.mdx";
10
+
11
+ ## tasks trigger
12
+
13
+ Triggers a task and prints the run ID and its dashboard URL. It runs in the `dev` environment unless you pass `--env`, so start the [dev command](/cli-dev-commands) first.
14
+
15
+ <CodeGroup>
16
+
17
+ ```bash npm
18
+ npx trigger.dev@latest tasks trigger hello-world --payload '{"name":"Ada"}'
19
+ ```
20
+
21
+ ```bash pnpm
22
+ pnpm dlx trigger.dev@latest tasks trigger hello-world --payload '{"name":"Ada"}'
23
+ ```
24
+
25
+ ```bash yarn
26
+ yarn dlx trigger.dev@latest tasks trigger hello-world --payload '{"name":"Ada"}'
27
+ ```
28
+
29
+ </CodeGroup>
30
+
31
+ Add `--wait` to wait for the run to finish. It prints the run's output, or its error and exits with code 1 if the run failed, which makes it useful in scripts and for coding agents testing a task:
32
+
33
+ ```bash
34
+ npx trigger.dev@latest tasks trigger hello-world --payload '{"name":"Ada"}' --wait
35
+ ```
36
+
37
+ The command fails without creating a run if the current version has no task with that ID. In `dev`, it warns you when the dev command isn't running, because the run waits in the queue until it starts.
38
+
39
+ <ParamField body="Payload" type="--payload">
40
+ The payload as JSON, e.g. `--payload '{"name":"Ada"}'`. Without it, the task gets no payload.
41
+ </ParamField>
42
+
43
+ <ParamField body="Payload file" type="--payload-file">
44
+ Read the payload from a JSON file instead.
45
+ </ParamField>
46
+
47
+ <ParamField body="Wait" type="--wait">
48
+ Wait for the run to finish, then print its output or error.
49
+ </ParamField>
50
+
51
+ <ParamField body="Timeout" type="--timeout">
52
+ With `--wait`, stop waiting after this many seconds and exit with code 1.
53
+ </ParamField>
54
+
55
+ <ParamField body="Tag" type="--tag">
56
+ A tag to add to the run. Repeat it to add more than one, e.g. `--tag cli --tag test`.
57
+ </ParamField>
58
+
59
+ <ParamField body="Idempotency key" type="--idempotency-key">
60
+ If a run with this key already exists, return it instead of creating a new one. See [idempotency](/idempotency).
61
+ </ParamField>
62
+
63
+ <ParamField body="Delay" type="--delay">
64
+ Wait before running, e.g. `30s`, `5m` or an ISO 8601 date.
65
+ </ParamField>
66
+
67
+ <ParamField body="TTL" type="--ttl">
68
+ Expire the run if it hasn't started within this time, e.g. `10m`.
69
+ </ParamField>
70
+
71
+ <ParamField body="Queue" type="--queue">
72
+ The queue to run in, instead of the queue set on the task.
73
+ </ParamField>
74
+
75
+ <ParamField body="Machine" type="--machine">
76
+ The machine to run on, e.g. `small-2x`. See [machines](/machines). Has no effect in `dev`.
77
+ </ParamField>
78
+
79
+ ## Options
80
+
81
+ <ParamField body="Environment" type="--env | -e">
82
+ The environment to use: `dev`, `prod`, `staging` or `preview`. Defaults to `dev`.
83
+ </ParamField>
84
+
85
+ <ConfigFileOptions />
86
+ <ProjectRefOptions />
87
+ <ParamField body="Branch" type="--branch | -b">
88
+ The branch to use. Required with `--env preview`. In `dev`, it defaults to the same [dev branch](/deployment/dev-branches) as the dev command.
89
+ </ParamField>
90
+
91
+ ### Common options
92
+
93
+ These options are available on most commands.
94
+
95
+ <CommonOptions />
@@ -267,6 +267,64 @@ export default defineConfig({
267
267
  });
268
268
  ```
269
269
 
270
+ ### Span processors
271
+
272
+ Span processors run on every span before it is exported, to Trigger.dev and to any `exporters` you configure. Use them to redact or drop span content before it reaches the run trace.
273
+
274
+ ```ts trigger.config.ts
275
+ import { defineConfig } from "@trigger.dev/sdk";
276
+ import type { AttributeValue, Attributes } from "@opentelemetry/api";
277
+ import type { SpanProcessor } from "@opentelemetry/sdk-trace-base";
278
+
279
+ const EMAIL = /[\w.+-]+@[\w-]+\.[\w.]+/g;
280
+
281
+ function redact(value: AttributeValue | undefined): AttributeValue | undefined {
282
+ if (typeof value === "string") return value.replace(EMAIL, "[email]");
283
+ // Some attributes are string arrays, like the messages sent mid-turn
284
+ if (Array.isArray(value)) {
285
+ return value.map((item) =>
286
+ typeof item === "string" ? item.replace(EMAIL, "[email]") : item
287
+ ) as AttributeValue;
288
+ }
289
+ return value;
290
+ }
291
+
292
+ function scrub(attributes: Attributes) {
293
+ for (const key of Object.keys(attributes)) attributes[key] = redact(attributes[key]);
294
+ }
295
+
296
+ const redactEmails: SpanProcessor = {
297
+ onStart: () => {},
298
+ // The span is complete here and still writable
299
+ onEnding: (span) => {
300
+ scrub(span.attributes);
301
+ for (const event of span.events) scrub(event.attributes ?? {});
302
+ },
303
+ onEnd: () => {},
304
+ forceFlush: async () => {},
305
+ shutdown: async () => {},
306
+ };
307
+
308
+ export default defineConfig({
309
+ project: "<project ref>",
310
+ telemetry: {
311
+ spanProcessors: [redactEmails],
312
+ },
313
+ });
314
+ ```
315
+
316
+ Your processors run ahead of ours, so they also see the in-progress copy of each span that the dashboard shows while a span is open. Redact in `onEnding`:
317
+
318
+ - `onStart` runs before we add the run context, so it sees only the attributes the span was started with.
319
+ - In `onEnd` the span is read-only and `setAttribute` does nothing. Writing to `span.attributes` directly still works.
320
+ - Content can also sit in span events (exceptions, for example). Events are on `span.events`.
321
+
322
+ After each call we restore the few attributes we use to link a span to its run, such as the run, task and attempt ids. Everything else can be redacted, including run tags and idempotency keys. If a processor throws, the error is reported and the span is still exported.
323
+
324
+ <Note>
325
+ Span processors only change span content. Run payloads and outputs, and the messages on a chat agent's session streams, are stored as sent. For chat agents, see [Redact chat content from traces](/ai-chat/patterns/redacting-telemetry).
326
+ </Note>
327
+
270
328
  ## Runtime
271
329
 
272
330
  If you don't set a `runtime`, your tasks run on the current Node.js LTS. Setting `runtime: "node"` does the same thing — see the Node.js versions section below to pin a specific version instead.
@@ -189,7 +189,7 @@ prismaExtension({
189
189
  "scripts": {
190
190
  "prebuild": "prisma generate",
191
191
  "dev": "trigger dev",
192
- "deploy": "trigger deploy"
192
+ "deploy": "trigger deploy --env prod"
193
193
  }
194
194
  }
195
195
  ```
@@ -331,7 +331,7 @@ steps:
331
331
  - name: Generate Prisma client
332
332
  run: npx prisma@6.19.0 generate
333
333
  - name: Deploy Trigger.dev
334
- run: npx trigger.dev@4.1.1 deploy
334
+ run: npx trigger.dev@4.1.1 deploy --env prod
335
335
  env:
336
336
  TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
337
337
  ```
@@ -902,7 +902,7 @@ datasource db {
902
902
  "scripts": {
903
903
  "generate": "prisma generate",
904
904
  "dev": "pnpm generate && trigger dev",
905
- "deploy": "trigger deploy"
905
+ "deploy": "trigger deploy --env prod"
906
906
  }
907
907
  }
908
908
  ```
@@ -975,7 +975,7 @@ export default defineConfig({
975
975
  "scripts": {
976
976
  "generate": "prisma generate",
977
977
  "dev": "pnpm generate && trigger dev",
978
- "deploy": "trigger deploy"
978
+ "deploy": "trigger deploy --env prod"
979
979
  }
980
980
  }
981
981
  ```
@@ -3,6 +3,8 @@ title: "Environment Variables"
3
3
  description: "Any environment variables used in your tasks need to be added so the deployed code will run successfully."
4
4
  ---
5
5
 
6
+ import { RollingOut } from "/snippets/feature-status.jsx";
7
+
6
8
  An environment variable in Node.js is accessed in your code using `process.env.MY_ENV_VAR`.
7
9
 
8
10
  We deploy your tasks and scale them up and down when they are triggered. So any environment variables you use in your tasks need to accessible to us so your code will run successfully.
@@ -43,6 +45,8 @@ When creating an environment variable, you can mark it as a **Secret**. Secret v
43
45
 
44
46
  You can edit an environment variable's values. You cannot edit the key name, you must delete and create a new one.
45
47
 
48
+ <RollingOut flag="allowEmptyEnvironmentVariableValues" />
49
+
46
50
  <Note>
47
51
  An empty string is a valid value. Saving a variable with an empty value stores it as an empty
48
52
  string (your task sees the variable set to `""`), which is different from deleting the variable
@@ -60,7 +60,7 @@ The id is free-form and opaque to Trigger.dev — a commit SHA, a release tag, a
60
60
  <Step title="Deploy your tasks with an external id">
61
61
 
62
62
  ```bash
63
- npx trigger.dev@latest deploy --external-id "$(git rev-parse HEAD)"
63
+ npx trigger.dev@latest deploy --env prod --external-id "$(git rev-parse HEAD)"
64
64
  ```
65
65
 
66
66
  </Step>
@@ -240,7 +240,7 @@ Read the commit in the place where it *is* available, and write it into both hal
240
240
 
241
241
  ```bash
242
242
  # 1. Deploy side — name the deployment after the commit.
243
- npx trigger.dev@latest deploy --external-id "$(git rev-parse HEAD)"
243
+ npx trigger.dev@latest deploy --env prod --external-id "$(git rev-parse HEAD)"
244
244
 
245
245
  # 2. Application side — give the running app the same value.
246
246
  TRIGGER_EXTERNAL_DEPLOYMENT_ID=<the-same-commit-sha>
@@ -385,7 +385,7 @@ If you filter deploys by path, derive the id from the same filter on both sides
385
385
 
386
386
  ```bash
387
387
  TASK_ID=$(git log -1 --format=%H -- trigger/)
388
- npx trigger.dev@latest deploy --external-id "$TASK_ID"
388
+ npx trigger.dev@latest deploy --env prod --external-id "$TASK_ID"
389
389
  ```
390
390
 
391
391
  Compute the same value where you set your app's variable. In GitHub Actions this needs `fetch-depth: 0` on `actions/checkout` — the default shallow clone can't see far enough back.
@@ -162,7 +162,7 @@ To keep your app and tasks in sync without coordinating promotion at all, see [a
162
162
 
163
163
  ## Staging deploys
164
164
 
165
- By default, the `deploy` command will deploy to the `prod` environment. If you want to deploy to a different environment, you can use the `--env` flag:
165
+ Use the `--env` flag to choose the environment you deploy to. If you don't pass it, the `deploy` command deploys to the `prod` environment, unless `TRIGGER_ACCESS_TOKEN` holds an API key for another environment. To deploy to staging:
166
166
 
167
167
  ```bash
168
168
  npx trigger.dev deploy --env staging
@@ -147,7 +147,7 @@ Creating and deploying a preview branch manually is easy:
147
147
  npx trigger.dev@latest deploy --env preview
148
148
  ```
149
149
 
150
- This will create and deploy a preview branch, automatically detecting the git branch. If for some reason the auto-detection doesn't work it will let you know and tell you do this:
150
+ This will create and deploy a preview branch, automatically detecting the git branch. When your CI checks out a detached HEAD, the branch is read from the CI provider's branch variable instead (GitLab, Bitbucket, CircleCI, Buildkite, Travis CI, Jenkins and Azure Pipelines are supported). If for some reason the auto-detection doesn't work it will let you know and tell you do this:
151
151
 
152
152
  ```bash
153
153
  npx trigger.dev@latest deploy --env preview --branch your-branch-name
@@ -47,7 +47,7 @@ jobs:
47
47
  env:
48
48
  TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
49
49
  run: |
50
- npx trigger.dev@latest deploy --external-id ${{ github.sha }}
50
+ npx trigger.dev@latest deploy --env prod --external-id ${{ github.sha }}
51
51
  ```
52
52
 
53
53
  ```yaml .github/workflows/release-trigger-staging.yml
@@ -184,7 +184,7 @@ Tip: add the `trigger.dev` CLI to your `devDependencies` and the deploy command
184
184
  ```json
185
185
  {
186
186
  "scripts": {
187
- "deploy:trigger-prod": "trigger deploy",
187
+ "deploy:trigger-prod": "trigger deploy --env prod",
188
188
  "deploy:trigger": "trigger deploy --env staging"
189
189
  },
190
190
  "devDependencies": {
@@ -254,7 +254,7 @@ jobs:
254
254
  TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
255
255
  TRIGGER_API_URL: ${{ secrets.TRIGGER_API_URL }}
256
256
  run: |
257
- npx trigger.dev@latest deploy
257
+ npx trigger.dev@latest deploy --env prod
258
258
  ```
259
259
 
260
260
  </CodeGroup>
package/docs/limits.mdx CHANGED
@@ -12,6 +12,12 @@ You can view your current limits, quotas, and rate limit usage in real-time by v
12
12
  [Billing limits and alerts](/billing-limits).
13
13
  </Note>
14
14
 
15
+ ## Run duration
16
+
17
+ Runs have no maximum duration. You set a default `maxDuration` for your project and can override it per task or per run, or set it to `timeout.None` to let a run execute indefinitely.
18
+
19
+ Trigger is built for long running AI agents and workflows and we have seen Trigger.dev users with runs lasting more than six months.
20
+
15
21
  ## Concurrency limits
16
22
 
17
23
  Concurrency is allocated **per environment**, not as a single figure for the whole organization. Each plan sets a separate default for production, staging, preview and development:
@@ -109,7 +109,7 @@ Then add scripts to your `package.json`:
109
109
  {
110
110
  "scripts": {
111
111
  "dev:trigger": "trigger dev",
112
- "deploy:trigger": "trigger deploy"
112
+ "deploy:trigger": "trigger deploy --env prod"
113
113
  }
114
114
  }
115
115
  ```