@trigger.dev/sdk 4.5.16 → 4.6.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.
Files changed (159) hide show
  1. package/dist/commonjs/imports/ai-runtime-cjs.cjs.map +1 -1
  2. package/dist/commonjs/imports/ai-runtime.js +2 -0
  3. package/dist/commonjs/v3/ai-shared.d.ts +15 -0
  4. package/dist/commonjs/v3/ai-shared.js +35 -1
  5. package/dist/commonjs/v3/ai-shared.js.map +1 -1
  6. package/dist/commonjs/v3/ai.d.ts +311 -25
  7. package/dist/commonjs/v3/ai.js +1633 -380
  8. package/dist/commonjs/v3/ai.js.map +1 -1
  9. package/dist/commonjs/v3/auth.d.ts +10 -4
  10. package/dist/commonjs/v3/auth.js.map +1 -1
  11. package/dist/commonjs/v3/chat-client.d.ts +11 -2
  12. package/dist/commonjs/v3/chat-client.js +227 -111
  13. package/dist/commonjs/v3/chat-client.js.map +1 -1
  14. package/dist/commonjs/v3/chat-react.d.ts +88 -0
  15. package/dist/commonjs/v3/chat-react.js +130 -1
  16. package/dist/commonjs/v3/chat-react.js.map +1 -1
  17. package/dist/commonjs/v3/chat-server.d.ts +42 -6
  18. package/dist/commonjs/v3/chat-server.js +52 -7
  19. package/dist/commonjs/v3/chat-server.js.map +1 -1
  20. package/dist/commonjs/v3/chat.d.ts +92 -10
  21. package/dist/commonjs/v3/chat.js +328 -73
  22. package/dist/commonjs/v3/chat.js.map +1 -1
  23. package/dist/commonjs/v3/chatSnapshotIo.d.ts +2 -0
  24. package/dist/commonjs/v3/chatSnapshotIo.js +173 -0
  25. package/dist/commonjs/v3/chatSnapshotIo.js.map +1 -0
  26. package/dist/commonjs/v3/chatVersionSkew.d.ts +12 -0
  27. package/dist/commonjs/v3/chatVersionSkew.js +30 -0
  28. package/dist/commonjs/v3/chatVersionSkew.js.map +1 -0
  29. package/dist/commonjs/v3/externalDeploymentId.d.ts +23 -0
  30. package/dist/commonjs/v3/externalDeploymentId.js +43 -0
  31. package/dist/commonjs/v3/externalDeploymentId.js.map +1 -0
  32. package/dist/commonjs/v3/retry.d.ts +4 -0
  33. package/dist/commonjs/v3/retry.js +22 -13
  34. package/dist/commonjs/v3/retry.js.map +1 -1
  35. package/dist/commonjs/v3/sessions.d.ts +15 -2
  36. package/dist/commonjs/v3/sessions.js +12 -1
  37. package/dist/commonjs/v3/sessions.js.map +1 -1
  38. package/dist/commonjs/v3/shared.js +30 -36
  39. package/dist/commonjs/v3/shared.js.map +1 -1
  40. package/dist/commonjs/v3/test/index.d.ts +1 -0
  41. package/dist/commonjs/v3/test/index.js +3 -1
  42. package/dist/commonjs/v3/test/index.js.map +1 -1
  43. package/dist/commonjs/v3/test/mock-chat-agent.d.ts +47 -4
  44. package/dist/commonjs/v3/test/mock-chat-agent.js +116 -10
  45. package/dist/commonjs/v3/test/mock-chat-agent.js.map +1 -1
  46. package/dist/commonjs/v3/test/test-session-handle.js +6 -0
  47. package/dist/commonjs/v3/test/test-session-handle.js.map +1 -1
  48. package/dist/commonjs/v3/test/transcript-storage-tests.d.ts +40 -0
  49. package/dist/commonjs/v3/test/transcript-storage-tests.js +258 -0
  50. package/dist/commonjs/v3/test/transcript-storage-tests.js.map +1 -0
  51. package/dist/commonjs/v3/transcriptStorage.d.ts +261 -0
  52. package/dist/commonjs/v3/transcriptStorage.js +374 -0
  53. package/dist/commonjs/v3/transcriptStorage.js.map +1 -0
  54. package/dist/commonjs/version.js +1 -1
  55. package/dist/esm/imports/ai-runtime.d.ts +2 -2
  56. package/dist/esm/imports/ai-runtime.js +2 -2
  57. package/dist/esm/imports/ai-runtime.js.map +1 -1
  58. package/dist/esm/v3/ai-shared.d.ts +15 -0
  59. package/dist/esm/v3/ai-shared.js +33 -0
  60. package/dist/esm/v3/ai-shared.js.map +1 -1
  61. package/dist/esm/v3/ai.d.ts +311 -25
  62. package/dist/esm/v3/ai.js +1625 -377
  63. package/dist/esm/v3/ai.js.map +1 -1
  64. package/dist/esm/v3/auth.d.ts +10 -4
  65. package/dist/esm/v3/auth.js.map +1 -1
  66. package/dist/esm/v3/chat-client.d.ts +11 -2
  67. package/dist/esm/v3/chat-client.js +228 -112
  68. package/dist/esm/v3/chat-client.js.map +1 -1
  69. package/dist/esm/v3/chat-react.d.ts +88 -0
  70. package/dist/esm/v3/chat-react.js +127 -1
  71. package/dist/esm/v3/chat-react.js.map +1 -1
  72. package/dist/esm/v3/chat-server.d.ts +42 -6
  73. package/dist/esm/v3/chat-server.js +53 -8
  74. package/dist/esm/v3/chat-server.js.map +1 -1
  75. package/dist/esm/v3/chat.d.ts +92 -10
  76. package/dist/esm/v3/chat.js +329 -74
  77. package/dist/esm/v3/chat.js.map +1 -1
  78. package/dist/esm/v3/chatSnapshotIo.d.ts +2 -0
  79. package/dist/esm/v3/chatSnapshotIo.js +165 -0
  80. package/dist/esm/v3/chatSnapshotIo.js.map +1 -0
  81. package/dist/esm/v3/chatVersionSkew.d.ts +12 -0
  82. package/dist/esm/v3/chatVersionSkew.js +27 -0
  83. package/dist/esm/v3/chatVersionSkew.js.map +1 -0
  84. package/dist/esm/v3/externalDeploymentId.d.ts +23 -0
  85. package/dist/esm/v3/externalDeploymentId.js +38 -0
  86. package/dist/esm/v3/externalDeploymentId.js.map +1 -0
  87. package/dist/esm/v3/retry.d.ts +4 -0
  88. package/dist/esm/v3/retry.js +14 -8
  89. package/dist/esm/v3/retry.js.map +1 -1
  90. package/dist/esm/v3/sessions.d.ts +15 -2
  91. package/dist/esm/v3/sessions.js +11 -1
  92. package/dist/esm/v3/sessions.js.map +1 -1
  93. package/dist/esm/v3/shared.js +17 -23
  94. package/dist/esm/v3/shared.js.map +1 -1
  95. package/dist/esm/v3/test/index.d.ts +1 -0
  96. package/dist/esm/v3/test/index.js +1 -0
  97. package/dist/esm/v3/test/index.js.map +1 -1
  98. package/dist/esm/v3/test/mock-chat-agent.d.ts +47 -4
  99. package/dist/esm/v3/test/mock-chat-agent.js +118 -12
  100. package/dist/esm/v3/test/mock-chat-agent.js.map +1 -1
  101. package/dist/esm/v3/test/test-session-handle.js +6 -0
  102. package/dist/esm/v3/test/test-session-handle.js.map +1 -1
  103. package/dist/esm/v3/test/transcript-storage-tests.d.ts +40 -0
  104. package/dist/esm/v3/test/transcript-storage-tests.js +255 -0
  105. package/dist/esm/v3/test/transcript-storage-tests.js.map +1 -0
  106. package/dist/esm/v3/transcriptStorage.d.ts +261 -0
  107. package/dist/esm/v3/transcriptStorage.js +360 -0
  108. package/dist/esm/v3/transcriptStorage.js.map +1 -0
  109. package/dist/esm/version.js +1 -1
  110. package/docs/ai-chat/actions.mdx +55 -23
  111. package/docs/ai-chat/anatomy.mdx +47 -27
  112. package/docs/ai-chat/backend.mdx +125 -48
  113. package/docs/ai-chat/background-injection.mdx +122 -19
  114. package/docs/ai-chat/client-protocol.mdx +5 -4
  115. package/docs/ai-chat/compaction.mdx +13 -7
  116. package/docs/ai-chat/custom-agents.mdx +23 -0
  117. package/docs/ai-chat/fast-starts.mdx +27 -20
  118. package/docs/ai-chat/frontend.mdx +19 -14
  119. package/docs/ai-chat/how-it-works.mdx +1 -1
  120. package/docs/ai-chat/lifecycle-hooks.mdx +6 -2
  121. package/docs/ai-chat/migrating-from-a-route-handler.mdx +16 -14
  122. package/docs/ai-chat/migrating-from-hydrate-messages.mdx +299 -0
  123. package/docs/ai-chat/patterns/database-persistence.mdx +24 -9
  124. package/docs/ai-chat/patterns/native-compaction.mdx +310 -0
  125. package/docs/ai-chat/patterns/persistence-and-replay.mdx +34 -23
  126. package/docs/ai-chat/patterns/skills.mdx +7 -10
  127. package/docs/ai-chat/patterns/version-upgrades.mdx +79 -6
  128. package/docs/ai-chat/pending-messages.mdx +3 -3
  129. package/docs/ai-chat/prompt-caching.mdx +23 -25
  130. package/docs/ai-chat/quick-start.mdx +11 -11
  131. package/docs/ai-chat/reference.mdx +69 -10
  132. package/docs/ai-chat/sessions.mdx +6 -1
  133. package/docs/ai-chat/side-channels.mdx +1 -1
  134. package/docs/ai-chat/testing.mdx +2 -1
  135. package/docs/ai-chat/tools.mdx +18 -13
  136. package/docs/ai-chat/transcript-storage.mdx +247 -0
  137. package/docs/ai-chat/upgrade-guide.mdx +2 -2
  138. package/docs/apikeys.mdx +27 -45
  139. package/docs/cli-env-commands.mdx +114 -0
  140. package/docs/cli-projects-commands.mdx +62 -0
  141. package/docs/cli-runs-commands.mdx +94 -0
  142. package/docs/config/extensions/syncEnvVars.mdx +6 -0
  143. package/docs/deploy-environment-variables.mdx +23 -2
  144. package/docs/deployment/overview.mdx +4 -8
  145. package/docs/deployment/preview-branches.mdx +4 -4
  146. package/docs/deployment/version-skew-protection.mdx +62 -0
  147. package/docs/introduction.mdx +58 -152
  148. package/docs/manual-setup.mdx +7 -7
  149. package/docs/mcp-tools.mdx +9 -0
  150. package/docs/quick-start.mdx +3 -3
  151. package/docs/realtime/auth.mdx +1 -1
  152. package/docs/reports.mdx +157 -0
  153. package/docs/self-hosting/security.mdx +17 -0
  154. package/docs/tasks/scheduled.mdx +24 -0
  155. package/docs/triggering.mdx +1 -1
  156. package/docs/troubleshooting-zod.mdx +158 -0
  157. package/package.json +4 -4
  158. package/skills/trigger-authoring-chat-agent/SKILL.md +41 -29
  159. package/skills/trigger-chat-agent-advanced/SKILL.md +54 -19
@@ -0,0 +1,157 @@
1
+ ---
2
+ title: "Project health report"
3
+ sidebarTitle: "Health report"
4
+ description: "Get a deterministic verdict on your project's health — split into Flow, Execution, and Liveness — from the CLI, an MCP tool or prompt, or the HTTP API."
5
+ ---
6
+
7
+ **The health report answers one question — "is my project healthy, and if not, is the problem my code or the platform?" — with a deterministic verdict, not a dashboard.** The server computes the verdict from your telemetry; no LLM interprets it, so the same numbers always produce the same answer.
8
+
9
+ Every report reads a live window of your project's data and returns a headline verdict, the evidence behind it, and a single suggested next action. The only report today is `health`.
10
+
11
+ ## What the report measures
12
+
13
+ The report splits a project into three dimensions, each with its own verdict:
14
+
15
+ | Dimension | Question | What a bad verdict means |
16
+ | --- | --- | --- |
17
+ | **Flow** | Is work starting? | Runs are queuing but not being picked up — a concurrency limit, a throttle, or a trigger spike. |
18
+ | **Execution** | Are the runs that start succeeding? | Runs are failing or slowing more than your baseline — usually your code. |
19
+ | **Liveness** | Is the telemetry fresh? | The data behind the report is stale, so the verdict can't be trusted. |
20
+
21
+ Splitting Flow from Execution is the point: a project can be failing to _start_ work (Flow) while every run that does start _completes_ fine (Execution), or the reverse. The report names which one, so you know whether to look at your concurrency settings or your task code.
22
+
23
+ Each dimension carries a severity — healthy, degraded, or failing — and the report leads with the worst one as its headline. The headline finding is expanded with its supporting metrics; the others collapse to a one-line verdict.
24
+
25
+ ## Reading the report
26
+
27
+ Here is `trigger report health` for a project whose runs are backing up against the environment concurrency limit:
28
+
29
+ ```text
30
+ /report health prod · last 1h · vs 7d normal
31
+
32
+ ✕ Flow stalled — at your env concurrency limit for the last 40 min
33
+
34
+ concurrency 100/100 ▄▅▆▇████ 40 min at limit
35
+ pending 1,240 ↑ 6× ▁▂▄▅▆▇██ (normal ~120)
36
+ start latency p95 8.2s ↑ 4× ▁▂▃▄▆▇██ (normal ~2s)
37
+
38
+ why: runs are finishing at ~90/min
39
+ nothing dead-lettered
40
+
41
+ ✓ EXECUTION runs are executing normally
42
+
43
+ ✓ LIVENESS fresh — telemetry current, updated 12s ago
44
+
45
+ read: limit saturated → incoming work exceeds capacity → backlog grows
46
+
47
+ → Raise the env concurrency limit
48
+ Read concurrency docs
49
+ or do nothing — backlog drains in ~26.7 min once triggers ease
50
+ ```
51
+
52
+ The verdict reads top to bottom:
53
+
54
+ - **Headline** — the worst finding, stated as a verdict with the breach window. Here Flow is stalled because concurrency has been pinned at its limit for 40 minutes.
55
+ - **Evidence** — the metrics behind the headline, each with a sparkline over the window, its movement against your baseline (`↑ 6×` means six times normal), and an annotation (`40 min at limit`) or baseline (`normal ~120`).
56
+ - **`why:`** — the facts that support the verdict, including what the problem _isn't_ (runs are still finishing, nothing is dead-lettered — so it's not your code).
57
+ - **Sibling findings** — Execution and Liveness, each a one-line verdict.
58
+ - **`read:`** — the causal chain in one line.
59
+ - **Next steps** — the primary action first (`→`), then any alternatives.
60
+
61
+ ### The verdict vocabulary
62
+
63
+ Every surface carries meaning in glyphs, never color alone, so the report is legible in a monochrome terminal or a plain-text MCP host:
64
+
65
+ | Glyph | Meaning |
66
+ | --- | --- |
67
+ | `✓` | Healthy |
68
+ | `⚠` | Degraded |
69
+ | `✕` | Failing |
70
+ | `○` | Genuinely unknown — neither good nor bad |
71
+ | `⚑` | The data behind the report can't be trusted |
72
+ | `↑` `↓` `→` | Above, below, or flat against the baseline |
73
+
74
+ <Note>
75
+ The markdown surface swaps the status glyphs for traffic-light emoji (`🟢` `🟡` `🔴` `⚪` `🚩`) — the one color cue a chat host that renders neither ANSI nor HTML can show. The glyph and the emoji mean the same thing.
76
+ </Note>
77
+
78
+ ## Surfaces
79
+
80
+ The same report renders four ways.
81
+
82
+ ### CLI: `trigger report`
83
+
84
+ Print an interpreted report for an environment. The command defaults to the `health` report and the `prod` environment — a manual production check.
85
+
86
+ ```bash
87
+ npx trigger.dev@latest report health --env prod --period 24h
88
+ ```
89
+
90
+ | Flag | Default | Description |
91
+ | --- | --- | --- |
92
+ | `[key]` | `health` | The report to render (positional argument). |
93
+ | `-e, --env` | `prod` | The environment: `dev`, `staging`, `prod`, or `preview`. |
94
+ | `-b, --branch` | — | The preview branch. Required when `--env preview`. |
95
+ | `--period` | `1h` | The live window: `30m`, `1h`, `24h`, `7d`. Minutes to weeks, max 90 days. |
96
+ | `-p, --project-ref` | from config | The project ref (`proj_…`). Required if there is no `trigger.config.ts`. |
97
+ | `-c, --config` | — | Path to the config file. |
98
+
99
+ The command prints only the report — no banner — so it pipes cleanly. Output is ANSI-colored in a real terminal and plain markdown when piped:
100
+
101
+ ```bash
102
+ trigger report health > health.md # markdown, no escape codes
103
+ ```
104
+
105
+ Color follows the [supports-color](https://github.com/chalk/supports-color) convention: `NO_COLOR` (any value) or `FORCE_COLOR=0` disables color outright; `FORCE_COLOR` set to anything else force-enables it. Both win over TTY detection — which matters for agents spawned on a PTY, where `isTTY` is `true` but escape codes would be garbled.
106
+
107
+ ### MCP tool: `get_report`
108
+
109
+ Fetch the report from an AI coding tool through the [Trigger.dev MCP server](/mcp-introduction). The tool returns plain markdown by default, or ANSI when `color` is set (for hosts that display escapes in tool output). It is read-only.
110
+
111
+ | Parameter | Default | Description |
112
+ | --- | --- | --- |
113
+ | `key` | — | The report to render. Currently only `health`. |
114
+ | `environment` | `dev` | The environment. The MCP server is dev-centric, so an unspecified environment reports on `dev` — pass `environment` explicitly to report on production. |
115
+ | `period` | `1h` | Time window shorthand, e.g. `1h`, `24h`, `7d`. |
116
+ | `color` | `false` | Return ANSI-colored text instead of markdown. |
117
+ | `projectRef` | auto-detected | The project ref. Detected from `trigger.config.ts` if present. |
118
+ | `branch` | — | The preview branch, for preview or branchable dev environments. |
119
+
120
+ ### MCP prompt: `/report`
121
+
122
+ The MCP server also registers a `report` prompt, which hosts that support MCP prompts surface as a slash command (Claude Code renders it as `/mcp__trigger__report`). Running `/report health` calls `get_report` for you and shows the result verbatim in a fenced code block, preserving the monospace alignment and sparklines.
123
+
124
+ ### HTTP API: `GET /api/v1/reports/:key`
125
+
126
+ The endpoint behind every surface. It accepts a `period` and a `format`, and authenticates with a token scoped to read the report's query tables (`read:query`).
127
+
128
+ | Query param | Default | Description |
129
+ | --- | --- | --- |
130
+ | `period` | `1h` | The live window, same grammar as the CLI. |
131
+ | `format` | `markdown` | `markdown`, `ansi`, or `json`. |
132
+
133
+ `format=markdown` and `format=ansi` return the rendered text. `format=json` returns the `ReportViewModel` — the semantic payload behind the render, where verdicts are codes rather than prose, for building your own surface on top of the report.
134
+
135
+ ```bash
136
+ curl "https://api.trigger.dev/api/v1/reports/health?period=24h&format=json" \
137
+ -H "Authorization: Bearer $TRIGGER_ACCESS_TOKEN"
138
+ ```
139
+
140
+ An unknown report key returns `404` with the list of available keys.
141
+
142
+ ## Next steps
143
+
144
+ <CardGroup cols={2}>
145
+ <Card title="MCP server" icon="plug" href="/mcp-introduction">
146
+ Connect your AI coding tool to Trigger.dev.
147
+ </Card>
148
+ <Card title="MCP tools" icon="wrench" href="/mcp-tools">
149
+ Every tool the MCP server exposes, including `get_report`.
150
+ </Card>
151
+ <Card title="Concurrency & queues" icon="layer-group" href="/queue-concurrency">
152
+ Configure the concurrency limits the Flow verdict checks against.
153
+ </Card>
154
+ <Card title="Query your data" icon="magnifying-glass" href="/observability/query">
155
+ Run TRQL queries against the same telemetry the report reads.
156
+ </Card>
157
+ </CardGroup>
@@ -6,10 +6,27 @@ sidebarTitle: "Security"
6
6
 
7
7
  We take the security of Trigger.dev seriously, for both Cloud and self-hosted deployments. This page covers how to report a vulnerability, what to expect, and how to stay informed about security releases.
8
8
 
9
+ <Note>
10
+ Unlike Trigger.dev Cloud, the self-hosted setup is optimized for single-tenant use, with code and
11
+ users you trust. It is not designed to run untrusted code or untrusted payloads.
12
+ </Note>
13
+
9
14
  <Warning>
10
15
  Do not report security vulnerabilities through public GitHub issues, pull requests, or Discord. Use one of the private channels below.
11
16
  </Warning>
12
17
 
18
+ ## What is in scope
19
+
20
+ A self-hosted deployment is a single trust domain: as above, it is built for code and users you trust. Role separation inside an organization is therefore not a security boundary there. Role-based access control comes from a plugin that is not part of the open-source distribution, and without it the permission layer falls back to a permissive ability for session users and personal access tokens. That is deliberate.
21
+
22
+ **Out of scope for self-hosted:** a member of an organization performing a privileged action inside that same organization, such as renaming or deleting the organization or managing other members. Control who you invite, or use Cloud, if you need that separation.
23
+
24
+ **In scope from any deployment:** reaching data or actions belonging to an organization the caller is not a member of, or bypassing authentication. Organization is a hard boundary on Trigger.dev Cloud, and Cloud runs this same code, so report these even though your own install is single-tenant.
25
+
26
+ Tell us which deployment you tested against. The same report can be out of scope for self-hosting and in scope for Cloud.
27
+
28
+ The [security policy](https://github.com/triggerdotdev/trigger.dev/blob/main/SECURITY.md) is canonical.
29
+
13
30
  ## Reporting a vulnerability
14
31
 
15
32
  <Steps>
@@ -184,6 +184,30 @@ const createdSchedule = await schedules.create({
184
184
  is the assigned time the run will actually start.
185
185
  </Note>
186
186
 
187
+ ### Free-plan minimum window
188
+
189
+ Schedules created while an organization is on a free plan run no more than once per hour and use a minimum 60-minute window.
190
+
191
+ - The minimum applies to the **cron cadence**: creating or deploying a schedule whose cron fires more often than once an hour is rejected with an actionable error. Change the cron expression or upgrade before saving.
192
+ - Omitted, zero (`"0m"` / `"0%"`), or smaller windows are treated as the 60-minute minimum. A larger configured window still wins, subject to the usual cap at the next cron occurrence.
193
+ - The policy applies to **all environment types**, including Development.
194
+ - It is captured when the schedule is created. Existing schedules — and schedules created while paid, even after a later downgrade — are **grandfathered** and keep running unchanged.
195
+ - Upgrading does not immediately rewrite existing schedules. A free-created restriction is cleared the next time the schedule is saved (imperative/dashboard) or redeployed (declarative) while the organization is paying.
196
+ - No runs are ever skipped or coalesced: unsupported high-frequency schedules are rejected at create/update time rather than silently thinned out.
197
+ - Self-hosted deployments, and any case where the billing plan can't be determined, are unrestricted.
198
+
199
+ When a schedule is subject to this policy, the API returns an `appliedSchedulePolicy` object alongside the configured `window`:
200
+
201
+ ```json
202
+ {
203
+ "window": "0m",
204
+ "appliedSchedulePolicy": {
205
+ "minimumWindowSeconds": 3600,
206
+ "reason": "free_schedule"
207
+ }
208
+ }
209
+ ```
210
+
187
211
  ## Supported cron syntax
188
212
 
189
213
  ```
@@ -27,7 +27,7 @@ Trigger tasks **from inside a another task**:
27
27
 
28
28
  ## Triggering from your backend
29
29
 
30
- When you trigger a task from your backend code, you need to set the `TRIGGER_SECRET_KEY` environment variable. If you're [using a preview branch](/deployment/preview-branches), you also need to set the `TRIGGER_PREVIEW_BRANCH` environment variable. You can find the value on the API keys page in the Trigger.dev dashboard. [More info on API keys](/apikeys).
30
+ When you trigger a task from your backend code, create a named API key with **Trigger only** access in the environment you want to target and set it as `TRIGGER_SECRET_KEY`. If you're [using a preview branch](/deployment/preview-branches), also set `TRIGGER_PREVIEW_BRANCH`. [More info on API keys](/apikeys).
31
31
 
32
32
  If a single process needs to trigger across multiple projects, environments, or preview branches, use [`new TriggerClient({...})`](/management/multiple-clients) for each target instead of relying on the global env vars.
33
33
 
@@ -0,0 +1,158 @@
1
+ ---
2
+ title: "Zod"
3
+ description: "Resolve Zod typechecking, test, and deployment errors when upgrading to Trigger.dev v4.6."
4
+ ---
5
+
6
+ **Trigger.dev v4.6 uses Zod 4 by default. Zod 3.25.56 and later 3.x releases remain supported.**
7
+
8
+ Use this guide if typechecking, tests, or deployment fail after upgrading. The supported project dependency range is `^3.25.56 || ^4.0.0`.
9
+
10
+ | Your project's Zod dependency | What to do |
11
+ | --- | --- |
12
+ | No direct Zod dependency | No Zod-specific change is needed. Trigger.dev installs a compatible runtime dependency automatically. |
13
+ | Zod below 3.25.56 | Upgrade before using Trigger.dev v4.6. These versions are unsupported. |
14
+ | Zod 3.25.56 or later in the 3.x release line | Keep using your Zod 3 schemas with supported SDK APIs. Review the cross-major caveats below. |
15
+ | Zod 4.x | Supported and the recommended default. |
16
+
17
+ Passing your own supported Zod 3 schema to [`schemaTask`](/tasks/schemaTask) or `toolTask` remains supported. This does not make Zod 3 and Zod 4 schemas interchangeable when you compose or inspect them yourself.
18
+
19
+ ## Typechecking, tests, or deployment fail after upgrading
20
+
21
+ An older Zod installation can be missing the entry points, types, or schema behavior that Trigger.dev v4.6 requires.
22
+
23
+ Expect TypeScript errors when an unsupported version is resolved, particularly with `skipLibCheck: false`. Tests may also fail when they import or execute schemas. Deployment loads your task code, so an incompatible runtime installation causes deployment errors even if your local tooling skips typechecking. A passing local test suite does not establish that an unsupported version is safe to deploy.
24
+
25
+ Symptoms include missing `zod/v4` or `zod/v4/core` exports, missing Zod types, incompatible generic parameters, and errors while loading tasks. The exact failure depends on the version and dependency tree; not every unsupported version fails at the same stage.
26
+
27
+ ### Check the installed version
28
+
29
+ Inspect the resolved dependencies in the package that contains your tasks, not only the version range in `package.json`:
30
+
31
+ <CodeGroup>
32
+ ```bash npm
33
+ npm ls zod
34
+ ```
35
+
36
+ ```bash pnpm
37
+ pnpm why zod
38
+ ```
39
+
40
+ ```bash bun
41
+ bun pm ls --all
42
+ ```
43
+ </CodeGroup>
44
+
45
+ Check the lockfile and any dependency overrides or resolutions as well. An override can keep an old Zod version installed even after you update a direct dependency.
46
+
47
+ ### Update Zod
48
+
49
+ Choose whether to move your application to Zod 4 or keep its existing Zod 3 schemas:
50
+
51
+ <Tabs>
52
+ <Tab title="Move to Zod 4">
53
+ Install the latest Zod 4 release:
54
+
55
+ <CodeGroup>
56
+ ```bash npm
57
+ npm install zod@4
58
+ ```
59
+
60
+ ```bash pnpm
61
+ pnpm add zod@4
62
+ ```
63
+
64
+ ```bash bun
65
+ bun add zod@4
66
+ ```
67
+ </CodeGroup>
68
+
69
+ Review [Zod's migration guide](https://zod.dev/v4/changelog) for changes to your own schemas and error handling.
70
+ </Tab>
71
+ <Tab title="Stay on Zod 3">
72
+ Install the latest Zod 3 patch rather than pinning the minimum supported version:
73
+
74
+ <CodeGroup>
75
+ ```bash npm
76
+ npm install zod@3
77
+ ```
78
+
79
+ ```bash pnpm
80
+ pnpm add zod@3
81
+ ```
82
+
83
+ ```bash bun
84
+ bun add zod@3
85
+ ```
86
+ </CodeGroup>
87
+
88
+ Your existing `import { z } from "zod"` continues to use Zod 3. Trigger.dev's own schemas use the Zod 4 implementation included in the supported Zod 3 package.
89
+ </Tab>
90
+ </Tabs>
91
+
92
+ Other dependencies can require a higher minimum than Trigger.dev. For example, an AI SDK dependency may require Zod 3.25.76 or Zod 4. Meet those peer requirements too; do not force a lower version across every dependency.
93
+
94
+ ### Verify the update
95
+
96
+ - Commit the updated manifest and lockfile, and make sure CI uses them.
97
+ - Recheck the installed Zod versions in your local and deployment environments.
98
+ - Run your project's TypeScript checks and tests, including code that constructs or inspects schemas.
99
+ - Keep the CLI and SDK versions aligned using the [package upgrade guide](/upgrading-packages), then retry deployment.
100
+
101
+ Do not use `skipLibCheck` or ignored peer-dependency warnings as a compatibility fix. They do not change the runtime package that deployment loads. You do not need to add Zod as a direct dependency if your application does not import it.
102
+
103
+ ## Parsing still fails with a supported Zod 3 version
104
+
105
+ Supporting a Zod 3 schema as an SDK input is different from nesting a Trigger.dev-exported Zod 4 schema inside a Zod 3 object. Cross-major composition can fail during typechecking or parsing.
106
+
107
+ For example, this mixes a Zod 3 object with a Zod 4 `RetryOptions` schema:
108
+
109
+ ```ts incompatible-schemas.ts
110
+ import { z } from "zod"; // Project dependency is Zod 3.
111
+ import { RetryOptions } from "@trigger.dev/core/v3";
112
+
113
+ const schema = z.object({ retry: RetryOptions });
114
+ schema.parse({ retry: {} });
115
+ ```
116
+
117
+ Use Zod 4 for every schema in the composed object. The `zod/v4` entry point is available in both supported Zod 3 packages and Zod 4 packages:
118
+
119
+ ```ts compatible-schemas.ts
120
+ import { z } from "zod/v4";
121
+ import { RetryOptions } from "@trigger.dev/core/v3";
122
+
123
+ const schema = z.object({ retry: RetryOptions });
124
+ schema.parse({ retry: {} });
125
+ ```
126
+
127
+ Alternatively, keep the Zod 3 and Trigger.dev schemas separate and call each schema's parser independently. You do not need to migrate unrelated application schemas to use this approach.
128
+
129
+ ## An `instanceof` check stops matching
130
+
131
+ A Zod 4 error is not an instance of the Zod 3 `ZodError` constructor. A constructor check against your project's Zod 3 import can stop matching errors produced by Trigger.dev's schemas:
132
+
133
+ ```ts mismatched-error-check.ts
134
+ import { z } from "zod"; // Project dependency is Zod 3.
135
+ import { RetryOptions } from "@trigger.dev/core/v3";
136
+
137
+ const result = RetryOptions.safeParse({ maxAttempts: "invalid" });
138
+
139
+ if (!result.success) {
140
+ console.log(result.error instanceof z.ZodError); // false
141
+ }
142
+ ```
143
+
144
+ Use the result returned by the schema you called instead of a constructor from another Zod installation:
145
+
146
+ ```ts schema-error-handling.ts
147
+ import { RetryOptions } from "@trigger.dev/core/v3";
148
+
149
+ const result = RetryOptions.safeParse({ maxAttempts: "invalid" });
150
+
151
+ if (!result.success) {
152
+ console.error(result.error.issues);
153
+ }
154
+ ```
155
+
156
+ The same caveat applies to checks such as `schema instanceof z.ZodObject`. Multiple installed copies can also have different constructors, even within the same major version. Prefer parsing and the returned validation result over inspecting classes or private fields such as `_def`.
157
+
158
+ These examples are not an exhaustive list of cross-major differences. If errors remain after updating, check which Zod implementation creates each schema and which code composes, parses, or inspects it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trigger.dev/sdk",
3
- "version": "4.5.16",
3
+ "version": "4.6.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.5.16",
72
+ "@trigger.dev/core": "4.6.1",
73
73
  "uncrypto": "^0.1.3"
74
74
  },
75
75
  "devDependencies": {
@@ -82,13 +82,13 @@
82
82
  "tshy": "^4.1.3",
83
83
  "tsx": "4.17.0",
84
84
  "typescript": "7.0.2",
85
- "zod": "3.25.76"
85
+ "zod": "4.5.4"
86
86
  },
87
87
  "peerDependencies": {
88
88
  "@ai-sdk/otel": ">=1.0.0-0 <2",
89
89
  "ai": "^5.0.0 || ^6.0.0 || >=7.0.0-canary <8",
90
90
  "react": "^18.0 || ^19.0",
91
- "zod": "^3.0.0 || ^4.0.0"
91
+ "zod": "^3.25.56 || ^4.0.0"
92
92
  },
93
93
  "peerDependenciesMeta": {
94
94
  "@ai-sdk/otel": {
@@ -2,8 +2,8 @@
2
2
  name: trigger-authoring-chat-agent
3
3
  description: >
4
4
  Author and run a durable AI chat agent with chat.agent from @trigger.dev/sdk/ai: the per-turn
5
- run loop, why you MUST spread ...chat.toStreamTextOptions() first, returning a StreamTextResult
6
- vs calling chat.pipe(), the two server actions (chat.createStartSessionAction +
5
+ run loop, why you MUST take streamText from the run argument rather than importing it from ai,
6
+ returning a StreamTextResult vs calling chat.pipe(), the two server actions (chat.createStartSessionAction +
7
7
  auth.createPublicToken), and wiring useChat to useTriggerChatTransport. Load this when building,
8
8
  modifying, or debugging a chat backend (the agent task or its lifecycle hooks) or its React
9
9
  transport, when declaring typed tools or custom data parts, or when migrating a plain AI SDK
@@ -47,10 +47,9 @@ import { anthropic } from "@ai-sdk/anthropic";
47
47
 
48
48
  export const myChat = chat.agent({
49
49
  id: "my-chat",
50
- run: async ({ messages, signal }) =>
50
+ // `streamText` below is the SDK's, from the run argument. See "Common mistakes".
51
+ run: async ({ messages, signal, streamText }) =>
51
52
  streamText({
52
- // Spread this FIRST. See "Common mistakes".
53
- ...chat.toStreamTextOptions(),
54
53
  model: anthropic("claude-sonnet-4-5"),
55
54
  messages,
56
55
  abortSignal: signal,
@@ -121,16 +120,22 @@ inside nested helpers, call `await chat.pipe(result)` from anywhere in the task
121
120
  `run` resolve `void`.
122
121
 
123
122
  ```ts
123
+ import { chat, type ChatStreamText } from "@trigger.dev/sdk/ai";
124
+ import { anthropic } from "@ai-sdk/anthropic";
125
+ import type { ModelMessage } from "ai";
126
+
124
127
  export const agentChat = chat.agent({
125
128
  id: "agent-chat",
126
- run: async ({ messages }) => {
127
- await runAgentLoop(messages); // don't return; pipe inside
129
+ run: async ({ messages, streamText }) => {
130
+ await runAgentLoop(messages, streamText); // don't return; pipe inside
128
131
  },
129
132
  });
130
133
 
131
- async function runAgentLoop(messages: ModelMessage[]) {
134
+ // A loop factored out of `run` takes `streamText` as an argument, so it keeps the
135
+ // managed options. `ChatStreamText` (from `@trigger.dev/sdk/ai`) types the parameter.
136
+ // `chat.toStreamTextOptions()` is the alternative when threading it down is impractical.
137
+ async function runAgentLoop(messages: ModelMessage[], streamText: ChatStreamText) {
132
138
  const result = streamText({
133
- ...chat.toStreamTextOptions(),
134
139
  model: anthropic("claude-sonnet-4-5"),
135
140
  messages,
136
141
  });
@@ -138,10 +143,10 @@ async function runAgentLoop(messages: ModelMessage[]) {
138
143
  }
139
144
  ```
140
145
 
141
- ### 2. Typed tools (declare on config AND spread back)
146
+ ### 2. Typed tools (declare on config AND pass back)
142
147
 
143
148
  Declare tools on `chat.agent({ tools })`, read them back typed from the `run()` payload, and pass
144
- that set to `chat.toStreamTextOptions({ tools })`. One declaration flows everywhere.
149
+ that set as `tools`. One declaration flows everywhere.
145
150
 
146
151
  ```ts
147
152
  import { tool, stepCountIs } from "ai";
@@ -158,11 +163,11 @@ const tools = {
158
163
  export const myChat = chat.agent({
159
164
  id: "my-chat",
160
165
  tools, // so toModelOutput survives across turns
161
- run: async ({ messages, tools, signal }) =>
166
+ run: async ({ messages, tools, signal, streamText }) =>
162
167
  streamText({
163
- ...chat.toStreamTextOptions({ tools }), // same set, handed back typed
164
168
  model: anthropic("claude-sonnet-4-5"),
165
169
  messages,
170
+ tools, // same set, handed back typed
166
171
  abortSignal: signal,
167
172
  stopWhen: stepCountIs(15),
168
173
  }),
@@ -203,8 +208,8 @@ export const myChat = chat
203
208
  onTurnStart: async ({ uiMessages, writer }) => {
204
209
  writer.write({ type: "data-turn-status", data: { status: "preparing" } });
205
210
  },
206
- run: async ({ messages, tools, signal }) =>
207
- streamText({ ...chat.toStreamTextOptions({ tools }), model, messages, abortSignal: signal }),
211
+ run: async ({ messages, tools, signal, streamText }) =>
212
+ streamText({ model, messages, tools, abortSignal: signal }),
208
213
  });
209
214
  ```
210
215
 
@@ -217,8 +222,9 @@ frontend, narrow `useChat` with `InferChatUIMessage<typeof myChat>` from `@trigg
217
222
  `chat.agent` accepts hooks that fire in a fixed per-turn order:
218
223
 
219
224
  ```text
220
- onValidateMessages -> hydrateMessages -> onChatStart (chat's first message only)
221
- -> onTurnStart -> run() -> onBeforeTurnComplete -> onTurnComplete
225
+ onValidateMessages -> storage.loadContext (or the deprecated hydrateMessages)
226
+ -> onChatStart (chat's first message only)
227
+ -> onTurnStart -> run() -> onBeforeTurnComplete -> onTurnComplete -> storage.save
222
228
  ```
223
229
 
224
230
  `onBoot` fires once per worker process (every fresh boot, including continuation runs) and is where
@@ -228,13 +234,13 @@ first message. Suspend/resume use `onChatSuspend` / `onChatResume`. Config optio
228
234
  `uiMessageStreamOptions`, and `exitAfterPreloadIdle`. There is no generic `retry`; `chat.agent`
229
235
  runs with `maxAttempts: 1` internally.
230
236
 
231
- Stop is load-bearing: the `signal` passed to `run` aborts on stop or cancel. Forward it as
237
+ Stop depends on it: the `signal` passed to `run` aborts on stop or cancel. Forward it as
232
238
  `abortSignal` to `streamText`, or the Stop button updates the UI while the model keeps generating
233
239
  server-side.
234
240
 
235
241
  ```ts
236
- run: async ({ messages, signal }) =>
237
- streamText({ ...chat.toStreamTextOptions(), model, messages, abortSignal: signal, stopWhen: stepCountIs(15) });
242
+ run: async ({ messages, signal, streamText }) =>
243
+ streamText({ model, messages, abortSignal: signal, stopWhen: stepCountIs(15) });
238
244
  ```
239
245
 
240
246
  ### 6. Migrating from a plain AI SDK `streamText` route
@@ -243,24 +249,30 @@ There is no API route in this model. The transport replaces the route round-trip
243
249
 
244
250
  - Delete the route handler. Move per-request auth into the two server actions from Setup step 2.
245
251
  - Move the `streamText` call into `run`. It already receives pre-converted `ModelMessage[]`.
246
- - Return the `StreamTextResult` (it auto-pipes) and add `...chat.toStreamTextOptions()` first.
252
+ - Return the `StreamTextResult` (it auto-pipes) and take `streamText` from `run`'s argument, not from `ai`.
247
253
  - On the client, swap the `api` URL for `useTriggerChatTransport`; `useChat` stays the same shape.
248
254
 
249
255
  ## Common mistakes
250
256
 
251
- - **CRITICAL: forgetting `...chat.toStreamTextOptions()`.**
257
+ - **CRITICAL: calling the `streamText` imported from `ai`.**
252
258
  ```ts
253
259
  // Wrong - compaction / steering / background injection silently no-op
254
- return streamText({ model, messages, abortSignal: signal });
255
- // Correct - spread FIRST so explicit overrides win
256
- return streamText({ ...chat.toStreamTextOptions(), model, messages, abortSignal: signal });
260
+ import { streamText } from "ai";
261
+ run: async ({ messages, signal }) => streamText({ model, messages, abortSignal: signal });
262
+ // Correct - the run argument's streamText carries the managed options
263
+ run: async ({ messages, signal, streamText }) => streamText({ model, messages, abortSignal: signal });
257
264
  ```
258
- It wires the `prepareStep` callback behind compaction, mid-turn steering, and background
259
- injection, injects the system prompt from `chat.prompt()`, resolves the registry model, and adds
260
- telemetry. Omitting it makes all of those silently no-op with no error.
265
+ The SDK's one carries the `prepareStep` behind compaction, mid-turn steering and background
266
+ injection, the system prompt from `chat.prompt()` or `chat.agent({ system })`, the registry-resolved
267
+ model, and telemetry. The imported one carries none of it, with no error.
268
+ `...chat.toStreamTextOptions()` does the same job by hand, and is what a custom agent has to use,
269
+ since it has no `run` argument. A `chat.headStart` route gets a bound `streamText` too, and there it
270
+ also owns `messages`, `prompt`, `stopWhen` and `abortSignal`. Spreading it and then re-setting
271
+ `tools` or `prepareStep` replaces the managed ones; the run argument's `streamText` merges `tools`
272
+ and composes `prepareStep` instead.
261
273
 
262
274
  - **Declaring tools only on `streamText`.** Also declare them on `chat.agent({ tools })`, read them
263
- back from `run`, and pass `chat.toStreamTextOptions({ tools })`. Otherwise each tool's
275
+ back from `run`, and pass that set as `tools`. Otherwise each tool's
264
276
  `toModelOutput` runs on turn 1 but is dropped when history is re-converted on later turns.
265
277
 
266
278
  - **Not forwarding `signal` for stop.** Without `abortSignal: signal`, Stop updates the UI but the