@trigger.dev/sdk 4.5.0-rc.5 → 4.5.0-rc.7

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 (213) hide show
  1. package/dist/commonjs/v3/ai.d.ts +178 -5
  2. package/dist/commonjs/v3/ai.js +603 -119
  3. package/dist/commonjs/v3/ai.js.map +1 -1
  4. package/dist/commonjs/v3/chat-client.js +3 -0
  5. package/dist/commonjs/v3/chat-client.js.map +1 -1
  6. package/dist/commonjs/v3/chat-react.js +10 -7
  7. package/dist/commonjs/v3/chat-react.js.map +1 -1
  8. package/dist/commonjs/v3/chat-server.d.ts +8 -0
  9. package/dist/commonjs/v3/chat-server.js +32 -10
  10. package/dist/commonjs/v3/chat-server.js.map +1 -1
  11. package/dist/commonjs/v3/chat-server.test.js +51 -0
  12. package/dist/commonjs/v3/chat-server.test.js.map +1 -1
  13. package/dist/commonjs/v3/chat.js +34 -6
  14. package/dist/commonjs/v3/chat.js.map +1 -1
  15. package/dist/commonjs/v3/chat.test.js +53 -0
  16. package/dist/commonjs/v3/chat.test.js.map +1 -1
  17. package/dist/commonjs/v3/createStartSessionAction.test.js +30 -0
  18. package/dist/commonjs/v3/createStartSessionAction.test.js.map +1 -1
  19. package/dist/commonjs/v3/sessions.d.ts +11 -6
  20. package/dist/commonjs/v3/sessions.js +10 -5
  21. package/dist/commonjs/v3/sessions.js.map +1 -1
  22. package/dist/commonjs/v3/test/mock-chat-agent.d.ts +6 -0
  23. package/dist/commonjs/v3/test/mock-chat-agent.js +1 -0
  24. package/dist/commonjs/v3/test/mock-chat-agent.js.map +1 -1
  25. package/dist/commonjs/version.js +1 -1
  26. package/dist/esm/v3/ai.d.ts +178 -5
  27. package/dist/esm/v3/ai.js +603 -120
  28. package/dist/esm/v3/ai.js.map +1 -1
  29. package/dist/esm/v3/chat-client.js +3 -0
  30. package/dist/esm/v3/chat-client.js.map +1 -1
  31. package/dist/esm/v3/chat-react.js +10 -7
  32. package/dist/esm/v3/chat-react.js.map +1 -1
  33. package/dist/esm/v3/chat-server.d.ts +8 -0
  34. package/dist/esm/v3/chat-server.js +32 -10
  35. package/dist/esm/v3/chat-server.js.map +1 -1
  36. package/dist/esm/v3/chat-server.test.js +51 -0
  37. package/dist/esm/v3/chat-server.test.js.map +1 -1
  38. package/dist/esm/v3/chat.js +34 -6
  39. package/dist/esm/v3/chat.js.map +1 -1
  40. package/dist/esm/v3/chat.test.js +53 -0
  41. package/dist/esm/v3/chat.test.js.map +1 -1
  42. package/dist/esm/v3/createStartSessionAction.test.js +30 -0
  43. package/dist/esm/v3/createStartSessionAction.test.js.map +1 -1
  44. package/dist/esm/v3/sessions.d.ts +11 -6
  45. package/dist/esm/v3/sessions.js +10 -5
  46. package/dist/esm/v3/sessions.js.map +1 -1
  47. package/dist/esm/v3/test/mock-chat-agent.d.ts +6 -0
  48. package/dist/esm/v3/test/mock-chat-agent.js +1 -0
  49. package/dist/esm/v3/test/mock-chat-agent.js.map +1 -1
  50. package/dist/esm/version.js +1 -1
  51. package/docs/ai/prompts.mdx +430 -0
  52. package/docs/ai-chat/actions.mdx +115 -0
  53. package/docs/ai-chat/anatomy.mdx +71 -0
  54. package/docs/ai-chat/backend.mdx +817 -0
  55. package/docs/ai-chat/background-injection.mdx +221 -0
  56. package/docs/ai-chat/changelog.mdx +850 -0
  57. package/docs/ai-chat/chat-local.mdx +174 -0
  58. package/docs/ai-chat/client-protocol.mdx +1081 -0
  59. package/docs/ai-chat/compaction.mdx +411 -0
  60. package/docs/ai-chat/custom-agents.mdx +364 -0
  61. package/docs/ai-chat/error-handling.mdx +415 -0
  62. package/docs/ai-chat/fast-starts.mdx +672 -0
  63. package/docs/ai-chat/frontend.mdx +580 -0
  64. package/docs/ai-chat/how-it-works.mdx +230 -0
  65. package/docs/ai-chat/lifecycle-hooks.mdx +530 -0
  66. package/docs/ai-chat/mcp.mdx +101 -0
  67. package/docs/ai-chat/overview.mdx +90 -0
  68. package/docs/ai-chat/patterns/branching-conversations.mdx +284 -0
  69. package/docs/ai-chat/patterns/code-sandbox.mdx +126 -0
  70. package/docs/ai-chat/patterns/database-persistence.mdx +414 -0
  71. package/docs/ai-chat/patterns/human-in-the-loop.mdx +275 -0
  72. package/docs/ai-chat/patterns/large-payloads.mdx +169 -0
  73. package/docs/ai-chat/patterns/oom-resilience.mdx +120 -0
  74. package/docs/ai-chat/patterns/persistence-and-replay.mdx +211 -0
  75. package/docs/ai-chat/patterns/recovery-boot.mdx +230 -0
  76. package/docs/ai-chat/patterns/skills.mdx +221 -0
  77. package/docs/ai-chat/patterns/sub-agents.mdx +383 -0
  78. package/docs/ai-chat/patterns/tool-result-auditing.mdx +148 -0
  79. package/docs/ai-chat/patterns/trusted-edge-signals.mdx +337 -0
  80. package/docs/ai-chat/patterns/version-upgrades.mdx +172 -0
  81. package/docs/ai-chat/pending-messages.mdx +343 -0
  82. package/docs/ai-chat/prompt-caching.mdx +206 -0
  83. package/docs/ai-chat/quick-start.mdx +161 -0
  84. package/docs/ai-chat/reference.mdx +909 -0
  85. package/docs/ai-chat/server-chat.mdx +263 -0
  86. package/docs/ai-chat/sessions.mdx +333 -0
  87. package/docs/ai-chat/testing.mdx +682 -0
  88. package/docs/ai-chat/tools.mdx +191 -0
  89. package/docs/ai-chat/types.mdx +242 -0
  90. package/docs/ai-chat/upgrade-guide.mdx +515 -0
  91. package/docs/apikeys.mdx +54 -0
  92. package/docs/building-with-ai.mdx +261 -0
  93. package/docs/bulk-actions.mdx +49 -0
  94. package/docs/changelog.mdx +6 -0
  95. package/docs/cli-deploy-commands.mdx +9 -0
  96. package/docs/cli-dev-commands.mdx +9 -0
  97. package/docs/cli-dev.mdx +8 -0
  98. package/docs/cli-init-commands.mdx +58 -0
  99. package/docs/cli-introduction.mdx +25 -0
  100. package/docs/cli-list-profiles-commands.mdx +42 -0
  101. package/docs/cli-login-commands.mdx +33 -0
  102. package/docs/cli-logout-commands.mdx +33 -0
  103. package/docs/cli-preview-archive.mdx +59 -0
  104. package/docs/cli-promote-commands.mdx +9 -0
  105. package/docs/cli-switch.mdx +43 -0
  106. package/docs/cli-update-commands.mdx +42 -0
  107. package/docs/cli-whoami-commands.mdx +33 -0
  108. package/docs/community.mdx +6 -0
  109. package/docs/config/config-file.mdx +602 -0
  110. package/docs/config/extensions/additionalFiles.mdx +38 -0
  111. package/docs/config/extensions/additionalPackages.mdx +40 -0
  112. package/docs/config/extensions/aptGet.mdx +34 -0
  113. package/docs/config/extensions/audioWaveform.mdx +20 -0
  114. package/docs/config/extensions/custom.mdx +380 -0
  115. package/docs/config/extensions/emitDecoratorMetadata.mdx +29 -0
  116. package/docs/config/extensions/esbuildPlugin.mdx +31 -0
  117. package/docs/config/extensions/ffmpeg.mdx +45 -0
  118. package/docs/config/extensions/lightpanda.mdx +56 -0
  119. package/docs/config/extensions/overview.mdx +67 -0
  120. package/docs/config/extensions/playwright.mdx +195 -0
  121. package/docs/config/extensions/prismaExtension.mdx +1014 -0
  122. package/docs/config/extensions/puppeteer.mdx +30 -0
  123. package/docs/config/extensions/pythonExtension.mdx +182 -0
  124. package/docs/config/extensions/syncEnvVars.mdx +291 -0
  125. package/docs/context.mdx +235 -0
  126. package/docs/database-connections.mdx +213 -0
  127. package/docs/deploy-environment-variables.mdx +435 -0
  128. package/docs/deployment/atomic-deployment.mdx +172 -0
  129. package/docs/deployment/overview.mdx +257 -0
  130. package/docs/deployment/preview-branches.mdx +224 -0
  131. package/docs/errors-retrying.mdx +379 -0
  132. package/docs/github-actions.mdx +222 -0
  133. package/docs/github-integration.mdx +136 -0
  134. package/docs/github-repo.mdx +8 -0
  135. package/docs/help-email.mdx +6 -0
  136. package/docs/help-slack.mdx +11 -0
  137. package/docs/hidden-tasks.mdx +56 -0
  138. package/docs/how-it-works.mdx +454 -0
  139. package/docs/how-to-reduce-your-spend.mdx +217 -0
  140. package/docs/idempotency.mdx +504 -0
  141. package/docs/introduction.mdx +223 -0
  142. package/docs/limits.mdx +241 -0
  143. package/docs/logging.mdx +195 -0
  144. package/docs/machines.mdx +952 -0
  145. package/docs/manual-setup.mdx +632 -0
  146. package/docs/mcp-agent-rules.mdx +41 -0
  147. package/docs/mcp-introduction.mdx +385 -0
  148. package/docs/mcp-tools.mdx +273 -0
  149. package/docs/migrating-from-v3.mdx +334 -0
  150. package/docs/observability/dashboards.mdx +102 -0
  151. package/docs/observability/query.mdx +585 -0
  152. package/docs/open-source-contributing.mdx +16 -0
  153. package/docs/open-source-self-hosting.mdx +541 -0
  154. package/docs/private-networking/aws-console-setup.mdx +304 -0
  155. package/docs/private-networking/overview.mdx +144 -0
  156. package/docs/private-networking/troubleshooting.mdx +78 -0
  157. package/docs/queue-concurrency.mdx +354 -0
  158. package/docs/quick-start.mdx +97 -0
  159. package/docs/realtime/auth.mdx +208 -0
  160. package/docs/realtime/backend/overview.mdx +45 -0
  161. package/docs/realtime/backend/streams.mdx +418 -0
  162. package/docs/realtime/backend/subscribe.mdx +225 -0
  163. package/docs/realtime/how-it-works.mdx +94 -0
  164. package/docs/realtime/overview.mdx +63 -0
  165. package/docs/realtime/react-hooks/overview.mdx +73 -0
  166. package/docs/realtime/react-hooks/streams.mdx +449 -0
  167. package/docs/realtime/react-hooks/subscribe.mdx +674 -0
  168. package/docs/realtime/react-hooks/swr.mdx +87 -0
  169. package/docs/realtime/react-hooks/triggering.mdx +194 -0
  170. package/docs/realtime/react-hooks/use-wait-token.mdx +34 -0
  171. package/docs/realtime/run-object.mdx +174 -0
  172. package/docs/replaying.mdx +72 -0
  173. package/docs/request-feature.mdx +6 -0
  174. package/docs/roadmap.mdx +6 -0
  175. package/docs/run-tests.mdx +20 -0
  176. package/docs/run-usage.mdx +113 -0
  177. package/docs/runs/heartbeats.mdx +38 -0
  178. package/docs/runs/max-duration.mdx +139 -0
  179. package/docs/runs/metadata.mdx +734 -0
  180. package/docs/runs/priority.mdx +31 -0
  181. package/docs/runs.mdx +396 -0
  182. package/docs/self-hosting/docker.mdx +458 -0
  183. package/docs/self-hosting/env/supervisor.mdx +74 -0
  184. package/docs/self-hosting/env/webapp.mdx +276 -0
  185. package/docs/self-hosting/kubernetes.mdx +601 -0
  186. package/docs/self-hosting/overview.mdx +108 -0
  187. package/docs/skills.mdx +85 -0
  188. package/docs/tags.mdx +120 -0
  189. package/docs/tasks/overview.mdx +697 -0
  190. package/docs/tasks/scheduled.mdx +382 -0
  191. package/docs/tasks/schemaTask.mdx +413 -0
  192. package/docs/tasks/streams.mdx +884 -0
  193. package/docs/triggering.mdx +1320 -0
  194. package/docs/troubleshooting-alerts.mdx +385 -0
  195. package/docs/troubleshooting-debugging-in-vscode.mdx +8 -0
  196. package/docs/troubleshooting-github-issues.mdx +6 -0
  197. package/docs/troubleshooting-uptime-status.mdx +6 -0
  198. package/docs/troubleshooting.mdx +398 -0
  199. package/docs/upgrading-packages.mdx +80 -0
  200. package/docs/vercel-integration.mdx +207 -0
  201. package/docs/versioning.mdx +56 -0
  202. package/docs/video-walkthrough.mdx +23 -0
  203. package/docs/wait-for-token.mdx +540 -0
  204. package/docs/wait-for.mdx +42 -0
  205. package/docs/wait-until.mdx +53 -0
  206. package/docs/wait.mdx +18 -0
  207. package/docs/writing-tasks-introduction.mdx +33 -0
  208. package/package.json +10 -6
  209. package/skills/trigger-authoring-chat-agent/SKILL.md +296 -0
  210. package/skills/trigger-authoring-tasks/SKILL.md +254 -0
  211. package/skills/trigger-chat-agent-advanced/SKILL.md +368 -0
  212. package/skills/trigger-cost-savings/SKILL.md +116 -0
  213. package/skills/trigger-realtime-and-frontend/SKILL.md +276 -0
@@ -0,0 +1,515 @@
1
+ ---
2
+ title: "Upgrade Guide: prerelease → Sessions-as-run-manager"
3
+ sidebarTitle: "Sessions Upgrade Guide"
4
+ description: "Migrating chat.agent code from the prerelease API to the Sessions-as-run-manager release."
5
+ ---
6
+
7
+ import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
8
+
9
+ <RcBanner />
10
+
11
+ This guide is for customers who tried `chat.agent` during the prerelease period.
12
+ The public surface of `chat.agent({...})`, `useTriggerChatTransport`,
13
+ `AgentChat`, `chat.defer`, and `chat.history` is largely
14
+ unchanged — but the transport's auth callbacks and the server-side helpers
15
+ that feed them were reshaped, so most prerelease apps need a small wiring
16
+ update.
17
+
18
+ ## TL;DR
19
+
20
+ <CodeGroup>
21
+
22
+ ```ts before.ts
23
+ // Single accessToken callback, dispatches on purpose
24
+ accessToken: async ({ chatId, purpose }) => {
25
+ if (purpose === "trigger") {
26
+ return chat.createAccessToken<typeof myChat>("my-chat");
27
+ }
28
+ // purpose === "preload" — same call, same trigger token
29
+ return chat.createAccessToken<typeof myChat>("my-chat");
30
+ };
31
+ ```
32
+
33
+ ```ts after.ts
34
+ // Two callbacks: pure refresh + server action that creates the session
35
+ accessToken: ({ chatId }) => mintChatAccessToken(chatId),
36
+ startSession: ({ chatId, clientData }) =>
37
+ startChatSession({ chatId, clientData }),
38
+ ```
39
+
40
+ </CodeGroup>
41
+
42
+ What changed:
43
+
44
+ - `accessToken` is now a **pure session-PAT mint** — called only on 401/403
45
+ to refresh. It must return a token scoped to the session, not a
46
+ `trigger:tasks` JWT.
47
+ - `startSession` is a **new callback** that wraps a server action calling
48
+ `chat.createStartSessionAction(taskId)`. The transport invokes it on
49
+ `transport.preload(chatId)` and lazily on the first `sendMessage` for
50
+ any chatId without a cached PAT.
51
+ - `ChatSession` persistable state drops `runId` — store only
52
+ `{publicAccessToken, lastEventId?}`.
53
+ - Per-call options on `transport.preload(chatId, ...)` are gone. Trigger
54
+ config (machine, idleTimeoutInSeconds, tags, queue, maxAttempts) lives
55
+ server-side in `chat.createStartSessionAction(taskId, options)`.
56
+
57
+ <Note>
58
+ The architectural shift is that `chat.agent` no longer rolls its own
59
+ per-run streams. It runs on top of a durable **Session** row that owns
60
+ its current run, persists across run lifecycles, and orchestrates
61
+ upgrades server-side. The customer-facing surface is similar; the wire
62
+ path beneath it changed completely.
63
+ </Note>
64
+
65
+ ## Step 1: Replace your access-token server action with two server actions
66
+
67
+ The old pattern was a single helper that minted a trigger token:
68
+
69
+ ```ts app/actions.ts (before)
70
+ "use server";
71
+
72
+ import { chat } from "@trigger.dev/sdk/ai";
73
+ import type { myChat } from "@/trigger/chat";
74
+
75
+ export const getChatToken = () =>
76
+ chat.createAccessToken<typeof myChat>("my-chat");
77
+ ```
78
+
79
+ Replace with two helpers — one for session creation, one for PAT refresh:
80
+
81
+ ```ts app/actions.ts (after)
82
+ "use server";
83
+
84
+ import { auth } from "@trigger.dev/sdk";
85
+ import { chat } from "@trigger.dev/sdk/ai";
86
+
87
+ // Server-side wrapper for session creation. Idempotent on (env, chatId).
88
+ // The customer's server is the only entry point that creates Session rows;
89
+ // the browser never holds a `trigger:tasks` JWT.
90
+ export const startChatSession = chat.createStartSessionAction("my-chat");
91
+
92
+ // Pure session-PAT mint for the transport's 401/403 retry path.
93
+ export async function mintChatAccessToken(chatId: string) {
94
+ return auth.createPublicToken({
95
+ scopes: {
96
+ read: { sessions: chatId },
97
+ write: { sessions: chatId },
98
+ },
99
+ expirationTime: "1h",
100
+ });
101
+ }
102
+ ```
103
+
104
+ `chat.createStartSessionAction(taskId)` returns a server action that:
105
+
106
+ 1. Creates the Session row for `chatId` (idempotent on the
107
+ `(env, externalId)` unique pair).
108
+ 2. Triggers the agent task's first run with
109
+ `basePayload: {messages: [], trigger: "preload"}` defaults plus any
110
+ overrides you pass.
111
+ 3. Returns `{sessionId, runId, publicAccessToken}` to the browser.
112
+
113
+ ## Step 2: Update the transport wiring
114
+
115
+ The transport now takes two callbacks instead of one:
116
+
117
+ ```tsx app/components/chat.tsx (after)
118
+ "use client";
119
+
120
+ import { useChat } from "@ai-sdk/react";
121
+ import { useTriggerChatTransport } from "@trigger.dev/sdk/chat/react";
122
+ import type { myChat } from "@/trigger/chat";
123
+ import { mintChatAccessToken, startChatSession } from "@/app/actions";
124
+
125
+ export function Chat() {
126
+ const transport = useTriggerChatTransport<typeof myChat>({
127
+ task: "my-chat",
128
+ accessToken: ({ chatId }) => mintChatAccessToken(chatId),
129
+ startSession: ({ chatId, clientData }) =>
130
+ startChatSession({ chatId, clientData }),
131
+ });
132
+
133
+ const { messages, sendMessage, status } = useChat({ transport });
134
+ // ...
135
+ }
136
+ ```
137
+
138
+ The transport calls them in two distinct flows:
139
+
140
+ | Trigger | Callback fired |
141
+ |---|---|
142
+ | `transport.preload(chatId)` | `startSession` |
143
+ | First `sendMessage` for a chatId with no cached PAT | `startSession` (auto) |
144
+ | Any 401/403 from `.in/append`, `.out` SSE, or `end-and-continue` | `accessToken` |
145
+ | Page hydrates with `sessions: { [chatId]: ... }` | Neither (uses hydrated PAT) |
146
+
147
+ `startSession` is deduped via an in-flight promise — concurrent
148
+ `preload` + `sendMessage` calls converge to one server action invocation.
149
+
150
+ ## Step 3: Drop transport-level trigger config
151
+
152
+ The prerelease transport accepted `triggerConfig`, `triggerOptions`, and
153
+ per-call options on `preload`. All of that moved server-side:
154
+
155
+ ```ts before
156
+ const transport = useTriggerChatTransport({
157
+ task: "my-chat",
158
+ accessToken: getChatToken,
159
+ triggerConfig: { basePayload: { /* ... */ } },
160
+ triggerOptions: { tags: [...], machine: "small-1x", maxAttempts: 3 },
161
+ });
162
+
163
+ transport.preload(chatId, { idleTimeoutInSeconds: 60, metadata: { ... } });
164
+ ```
165
+
166
+ ```ts after
167
+ // Trigger config now lives in chat.createStartSessionAction
168
+ export const startChatSession = chat.createStartSessionAction("my-chat", {
169
+ triggerConfig: {
170
+ machine: "small-1x",
171
+ maxAttempts: 3,
172
+ tags: ["my-tag"],
173
+ idleTimeoutInSeconds: 60,
174
+ },
175
+ });
176
+
177
+ // Browser side
178
+ const transport = useTriggerChatTransport<typeof myChat>({
179
+ task: "my-chat",
180
+ accessToken: ({ chatId }) => mintChatAccessToken(chatId),
181
+ startSession: ({ chatId, clientData }) =>
182
+ startChatSession({ chatId, clientData }),
183
+ });
184
+
185
+ transport.preload(chatId); // no second arg
186
+ ```
187
+
188
+ For metadata that varies per chat, use `clientData` on the transport (see
189
+ the next step) — it's typed and threaded through `startSession` automatically.
190
+
191
+ ## Step 4: Use `clientData` for typed payload metadata
192
+
193
+ If your agent uses `withClientData({schema})`, the transport's `clientData`
194
+ option is now the canonical place to set it. The same value:
195
+
196
+ - Is passed to your `startSession` callback as `params.clientData`, where
197
+ you forward it into `chat.createStartSessionAction`'s
198
+ `triggerConfig.basePayload.metadata`. The agent's first run sees it in
199
+ `payload.metadata` (visible to `onPreload` / `onChatStart`).
200
+ - Merges into per-turn `metadata` on every `.in/append` chunk
201
+ (visible to `onTurnStart` / inside `run` via `turn.clientData`).
202
+
203
+ ```tsx
204
+ const transport = useTriggerChatTransport<typeof myChat>({
205
+ task: "my-chat",
206
+ accessToken: ({ chatId }) => mintChatAccessToken(chatId),
207
+ startSession: ({ chatId, clientData }) =>
208
+ startChatSession({ chatId, clientData }),
209
+ clientData: {
210
+ userId: currentUser.id,
211
+ plan: currentUser.plan,
212
+ },
213
+ });
214
+ ```
215
+
216
+ The `clientData` value is live-updated when the option changes (the hook
217
+ calls `setClientData` under the hood), so dynamic values work without
218
+ reconstructing the transport.
219
+
220
+ <Tip>
221
+ Server-side authorization can still override or augment the
222
+ browser-claimed `clientData` inside `startSession` — never trust the
223
+ browser's identity claim. A typical pattern: the server action looks up
224
+ the user from the request session, then merges the trusted server fields
225
+ on top of `params.clientData`.
226
+ </Tip>
227
+
228
+ ## Step 5: Update your `ChatSession` persistence
229
+
230
+ If you persist session state across page loads, drop the `runId` field:
231
+
232
+ ```ts before
233
+ type ChatSession = {
234
+ runId: string;
235
+ publicAccessToken: string;
236
+ lastEventId?: string;
237
+ };
238
+ ```
239
+
240
+ ```ts after
241
+ type ChatSession = {
242
+ publicAccessToken: string;
243
+ lastEventId?: string;
244
+ };
245
+ ```
246
+
247
+ If your DB has a `runId` column, you can drop it (the transport doesn't
248
+ read it) or keep it for telemetry. The current run ID lives on the
249
+ Session row server-side now.
250
+
251
+ Hydration on page reload is unchanged:
252
+
253
+ ```tsx
254
+ const transport = useTriggerChatTransport<typeof myChat>({
255
+ // ...
256
+ sessions: persistedSession
257
+ ? { [chatId]: persistedSession }
258
+ : {},
259
+ });
260
+ ```
261
+
262
+ ## `chat.requestUpgrade()`: same call, faster handoff
263
+
264
+ Calling `chat.requestUpgrade()` inside `onTurnStart` /
265
+ `onValidateMessages` still ends the current run so the next message starts
266
+ on the latest version. What changed is the mechanism:
267
+
268
+ - **Before:** the agent emitted a `trigger:upgrade-required` chunk on
269
+ `.out`; the transport consumed it browser-side and triggered a new run.
270
+ - **After:** the agent calls `endAndContinueSession` server-to-server;
271
+ the webapp triggers a new run and atomically swaps `Session.currentRunId`
272
+ via optimistic locking. The browser's existing SSE subscription keeps
273
+ receiving chunks across the swap — no transport-side bookkeeping.
274
+
275
+ The new run is recorded in a `SessionRun` audit row with
276
+ `reason: "upgrade"` for dashboard provenance.
277
+
278
+ ## Hitting raw URLs
279
+
280
+ If your code talks to the realtime API directly instead of going through
281
+ the SDK, the URL shapes changed:
282
+
283
+ | Before | After |
284
+ |---|---|
285
+ | `GET /realtime/v1/streams/{runId}/chat` | `GET /realtime/v1/sessions/{chatId}/out` |
286
+ | `POST /realtime/v1/streams/{runId}/{target}/chat-messages/append` | `POST /realtime/v1/sessions/{chatId}/in/append` (body: `{kind: "message", payload}`) |
287
+ | `POST /realtime/v1/streams/{runId}/{target}/chat-stop/append` | `POST /realtime/v1/sessions/{chatId}/in/append` (body: `{kind: "stop"}`) |
288
+
289
+ The session-scoped PAT
290
+ (`read:sessions:{chatId} + write:sessions:{chatId}`) authorizes both the
291
+ externalId form (`/sessions/my-chat-id/...`) and the friendlyId form
292
+ (`/sessions/session_abc.../...`). The transport always uses the
293
+ externalId form; the friendlyId form is available for dashboard tooling
294
+ and direct API consumers.
295
+
296
+ ## What didn't change
297
+
298
+ - `chat.agent({...})` definition — `id`, `idleTimeoutInSeconds`,
299
+ `clientDataSchema`, `actionSchema`, `hydrateMessages`, `onPreload`,
300
+ `onChatStart`, `onValidateMessages`, `onTurnStart`, `onTurnComplete`,
301
+ `onChatSuspend`, `run`. All callbacks have the same signature and
302
+ fire at the same lifecycle points.
303
+ - `onAction` is still defined the same way, but its semantics changed
304
+ in the [May 6 prerelease](/ai-chat/changelog) — actions are no longer
305
+ turns, and `onAction` returning a `StreamTextResult` produces a model
306
+ response.
307
+ - `chat.customAgent({...})` and the `chat.createSession(payload, ...)`
308
+ helper for building a session loop manually inside a custom agent.
309
+ - `chat.defer` (deferred work) and `chat.history` (imperative history
310
+ mutations from inside `onAction`).
311
+ - `AgentChat` (server-side chat client) — `agent`, `id`, `clientData`,
312
+ `session`, `onTriggered`, `onTurnComplete`, `sendMessage`, `text()`.
313
+ - `useTriggerChatTransport` React semantics (created once, kept in a
314
+ ref, callbacks updated under the hood).
315
+ - Multi-tab coordination (`multiTab: true`),
316
+ [pending messages / steering](/ai-chat/pending-messages),
317
+ [background injection](/ai-chat/background-injection),
318
+ [compaction](/ai-chat/compaction).
319
+ - Per-turn `metadata` flowing through
320
+ `sendMessage({ text }, { metadata })` to `turn.metadata` server-side.
321
+
322
+ ## Verifying the migration
323
+
324
+ After updating, the smoke check is the same as before: send a message,
325
+ confirm the assistant streams a response, reload mid-stream, confirm
326
+ resume.
327
+
328
+ A few new things worth verifying once you've cut over:
329
+
330
+ - **Eager preload.** Click the button (or call `transport.preload(id)`
331
+ programmatically) — your `startSession` callback should fire and a
332
+ Session row + first run should be created before you send a message.
333
+ - **Idle-timeout continuation.** Wait past the agent's
334
+ `idleTimeoutInSeconds` so the run exits, then send another message —
335
+ the transport's `.in/append` should boot a new run on the same
336
+ Session, with a `SessionRun` row of `reason: "continuation"`.
337
+ - **PAT refresh.** Force a stale PAT in your DB (corrupt the signature)
338
+ and reload — the first request should 401, your `accessToken`
339
+ callback should fire, and the retry should succeed.
340
+
341
+ If any of those misfire, check that:
342
+
343
+ - Your `accessToken` callback returns a token minted via
344
+ `auth.createPublicToken({ scopes: { read: { sessions: chatId }, write: { sessions: chatId } } })`, **not**
345
+ `chat.createAccessToken` or `auth.createTriggerPublicToken`. The
346
+ transport rejects trigger tokens now.
347
+ - Your `startSession` callback returns
348
+ `{publicAccessToken: string}` — the result of
349
+ `chat.createStartSessionAction(taskId)({chatId, ...})` already has
350
+ this shape.
351
+ - You haven't left a stale `getStartToken` option on the transport;
352
+ it's not part of `TriggerChatTransportOptions` anymore.
353
+
354
+ ## v4.5 wire format change
355
+
356
+ A second migration lands on top of the Sessions release. v4.5 removes the full-history wire payload — clients now ship at most one new `UIMessage` per `.in/append`, and the agent rebuilds prior history from a durable JSON snapshot in object storage plus a replay of the `session.out` tail.
357
+
358
+ If you use the built-in `TriggerChatTransport` / `AgentChat` and don't reach into the wire shape directly, **most apps need no changes** — the change is below the customer-facing surface. Customers who built custom transports, hit `/realtime/v1/sessions/{id}/in/append` directly, or rely on specific behaviors of `hydrateMessages` / `onChatStart` should read this section.
359
+
360
+ ### Why the change
361
+
362
+ Long chats with heavy tool results were hitting the realtime API's 512 KiB body cap on `/in/append` once the accumulated `UIMessage[]` history (which the wire shipped in full on every send) crossed the limit. The 413 surfaced as a CORS error in browsers and stalled chats around turn 10–30 with tool use.
363
+
364
+ The wire is now **delta-only**: each `.in/append` carries at most one new `UIMessage`. The agent rebuilds prior history at run boot. The 512 KiB ceiling stops being pressure — typical payloads are a few KB regardless of chat length.
365
+
366
+ ### Object-store configuration
367
+
368
+ Snapshot read/write uses Trigger.dev's existing object-store infrastructure — the same presigned-URL routes used for large payloads. Set the standard `OBJECT_STORE_*` env vars on your webapp deployment if you haven't already; MinIO and S3-compatible stores work via `OBJECT_STORE_DEFAULT_PROTOCOL`.
369
+
370
+ | Env var | Purpose |
371
+ |---|---|
372
+ | `OBJECT_STORE_BASE_URL` | Endpoint URL (S3, MinIO, R2, etc.) |
373
+ | `OBJECT_STORE_ACCESS_KEY_ID` | Access key |
374
+ | `OBJECT_STORE_SECRET_ACCESS_KEY` | Secret key |
375
+ | `OBJECT_STORE_DEFAULT_PROTOCOL` | `s3` (default), `minio`, etc. |
376
+
377
+ Snapshots are written under `packets/{projectRef}/{envSlug}/sessions/{sessionId}/snapshot.json`. Each snapshot is small (typically tens of KB) and overwritten every turn — no append-only growth.
378
+
379
+ <Warning>
380
+ **No object store + no `hydrateMessages` = conversations don't survive run boundaries.** With neither piece of state, a continuation boots empty and the agent can't reconstruct prior turns. Either configure an object store or register `hydrateMessages`. The runtime logs a warning at agent registration time when both are missing.
381
+ </Warning>
382
+
383
+ ### Custom transports
384
+
385
+ If you've built your own transport (Slack bot, CLI, native app) against the [Client Protocol](/ai-chat/client-protocol), the `ChatTaskWirePayload` shape changed:
386
+
387
+ ```ts before
388
+ type ChatTaskWirePayload = {
389
+ messages: UIMessage[]; // full history
390
+ chatId: string;
391
+ trigger: "submit-message" | "regenerate-message" | "preload" | "close" | "action";
392
+ // ...
393
+ };
394
+ ```
395
+
396
+ ```ts after
397
+ type ChatTaskWirePayload = {
398
+ message?: UIMessage; // singular, optional
399
+ headStartMessages?: UIMessage[]; // chat.headStart only, "handover-prepare"
400
+ chatId: string;
401
+ trigger:
402
+ | "submit-message"
403
+ | "regenerate-message"
404
+ | "preload"
405
+ | "close"
406
+ | "action"
407
+ | "handover-prepare";
408
+ // ...
409
+ };
410
+ ```
411
+
412
+ What to send per trigger:
413
+
414
+ | Trigger | What to put in the payload |
415
+ |---|---|
416
+ | `submit-message` | The new user message (or a tool-approval-responded assistant message) in `message` |
417
+ | `regenerate-message` | No `message` — the agent trims its own tail |
418
+ | `preload` / `close` / `action` | No `message` |
419
+ | `handover-prepare` (head-start only) | Full prior history in `headStartMessages` (route handler — not on `/in/append`) |
420
+
421
+ The full wire breakdown is in the rewritten [Client Protocol](/ai-chat/client-protocol).
422
+
423
+ ### `hydrateMessages` consumers
424
+
425
+ The hook signature is unchanged. Two behavior tightenings worth knowing:
426
+
427
+ 1. **`incomingMessages` is now consistently 0-or-1-length.** Previously some triggers (`regenerate-message`, continuation) shipped full history; now all triggers ship at most one. If you assumed `incomingMessages` could contain multiple messages and acted on them as a batch, the loop now runs zero or one times. Patterns like the one below work the same — they just iterate fewer messages:
428
+
429
+ ```ts
430
+ hydrateMessages: async ({ incomingMessages }) => {
431
+ for (const msg of incomingMessages) { // 0-or-1 iterations
432
+ for (const r of chat.history.extractNewToolResults(msg)) {
433
+ await auditLog.record({ id: r.toolCallId, output: r.output });
434
+ }
435
+ }
436
+ return await db.getMessages(chatId);
437
+ }
438
+ ```
439
+
440
+ 2. **Registering `hydrateMessages` short-circuits snapshot+replay.** The runtime trusts your hook to be the source of truth, so it doesn't read or write the JSON snapshot or replay `session.out`. Zero object-store traffic. Trade-off: you own persistence end-to-end.
441
+
442
+ ### `onChatStart` is now once-per-chat
443
+
444
+ `onChatStart` no longer fires on continuation runs (post-`endRun`, post-waitpoint-timeout, post-`chat.requestUpgrade`, post-cancel, post-crash) or on OOM-retry attempts. It fires **exactly once per chat**, on the very first user message of the chat's lifetime. The `continuation` and `previousRunId` fields on `ChatStartEvent` are now `@deprecated` (always `false` / `undefined` when the hook fires).
445
+
446
+ This makes once-per-chat setup code (create the Chat DB row, mint chat-scoped resources) safe to write without continuation gates. Drop any `if (continuation) return;` checks from `onChatStart`:
447
+
448
+ ```ts before
449
+ onChatStart: async ({ continuation, chatId, clientData }) => {
450
+ if (continuation) return; // ❌ no longer needed — fires only on first message ever
451
+ await db.chat.create({ /* ... */ });
452
+ }
453
+ ```
454
+
455
+ ```ts after
456
+ onChatStart: async ({ chatId, clientData }) => {
457
+ await db.chat.create({ /* ... */ }); // ✅ guaranteed first-message-of-chat
458
+ }
459
+ ```
460
+
461
+ If you need per-turn setup that **does** run on continuations, move it to [`onTurnStart`](/ai-chat/lifecycle-hooks#onturnstart) — that hook still fires on every turn, including the first turn of a continuation run.
462
+
463
+ ### Move `chat.local` init from `onChatStart` to `onBoot`
464
+
465
+ Because `onChatStart` no longer fires on continuation runs, **`chat.local`** state initialized there will be missing when a continuation run starts — `run()` then crashes with `"chat.local can only be modified after initialization"`. The fix is to move per-process initialization to the new [`onBoot`](/ai-chat/lifecycle-hooks#onboot) hook, which fires once per worker boot (initial, preloaded, AND continuation):
466
+
467
+ ```ts before
468
+ const userContext = chat.local<{ name: string; plan: string }>({ id: "userContext" });
469
+
470
+ onChatStart: async ({ clientData }) => {
471
+ const user = await db.user.findUnique({ where: { id: clientData.userId } });
472
+ userContext.init({ name: user.name, plan: user.plan }); // ❌ never runs on continuation
473
+ }
474
+ ```
475
+
476
+ ```ts after
477
+ const userContext = chat.local<{ name: string; plan: string }>({ id: "userContext" });
478
+
479
+ onBoot: async ({ clientData }) => {
480
+ const user = await db.user.findUnique({ where: { id: clientData.userId } });
481
+ userContext.init({ name: user.name, plan: user.plan }); // ✅ runs on every fresh worker
482
+ }
483
+ ```
484
+
485
+ Anything else that's per-process (DB connection pools, sandbox handles, in-memory caches) belongs in `onBoot` for the same reason. Branch on `continuation` inside `onBoot` if you need to re-load state from your DB on takeover.
486
+
487
+ ### Client-side `setMessages` doesn't round-trip
488
+
489
+ The new wire makes one thing explicit that was implicit before: **mutating `useChat()`'s messages on the client doesn't change the agent's history.** Full-history mutations were silently overwritten by the wire's accumulator before this release; now they aren't even shipped.
490
+
491
+ For history compaction, summarization, or branch-swap, mutate the agent's accumulator inside `onTurnStart` using [`chat.setMessages()`](/ai-chat/backend) or [`chat.history.set()`](/ai-chat/backend#chat-history). The client's `useChat` will reconcile against the next `session.out` payload.
492
+
493
+ ### Verifying the v4.5 migration
494
+
495
+ After updating, the smoke check is the same as for v4.4:
496
+
497
+ - Send a message, confirm the assistant streams a response.
498
+ - Reload mid-stream, confirm resume.
499
+ - Send 30+ turns with tool calls — `.in/append` body sizes stay under ~5 KB the entire time. (Pre-change baseline: payloads grew past 512 KB around turn 10-30.)
500
+ - Idle out a run, send another message — the new run reads the snapshot, replays the tail, and continues seamlessly.
501
+
502
+ If continuations boot empty:
503
+
504
+ - Confirm `OBJECT_STORE_*` env vars are set on the webapp.
505
+ - Confirm the bucket key `packets/{projectRef}/{envSlug}/sessions/{sessionId}/snapshot.json` exists after a successful turn.
506
+ - Or — register `hydrateMessages` and let your DB be the source of truth.
507
+
508
+ ## Reference
509
+
510
+ - [TriggerChatTransport options](/ai-chat/reference#triggerchattransport-options)
511
+ - [`chat.createStartSessionAction`](/ai-chat/reference)
512
+ - [Backend setup](/ai-chat/backend)
513
+ - [Frontend setup](/ai-chat/frontend)
514
+ - [Client Protocol](/ai-chat/client-protocol) — wire format reference
515
+ - [Persistence and replay](/ai-chat/patterns/persistence-and-replay) — snapshot model end-to-end
@@ -0,0 +1,54 @@
1
+ ---
2
+ title: "API keys"
3
+ description: "How to authenticate with Trigger.dev so you can trigger tasks."
4
+ ---
5
+
6
+ ### Authentication and your secret keys
7
+
8
+ When you [trigger a task](/triggering) from your backend code, you need to set the `TRIGGER_SECRET_KEY` environment variable.
9
+
10
+ Each environment has its own secret key. You can find the value on the API keys page in the Trigger.dev dashboard:
11
+
12
+ ![How to find your secret key](/images/api-keys.png)
13
+
14
+ <Note>
15
+ For preview branches, you need to also set the `TRIGGER_PREVIEW_BRANCH` environment variable as
16
+ well. You can find the value on the API keys page when you're on the preview branch.
17
+ </Note>
18
+
19
+ ### Automatically Configuring the SDK
20
+
21
+ To automatically configure the SDK with your secret key, you can set the `TRIGGER_SECRET_KEY` environment variable. The SDK will automatically use this value when calling API methods (like `trigger`).
22
+
23
+ ```bash .env
24
+ TRIGGER_SECRET_KEY="tr_dev_…"
25
+ TRIGGER_PREVIEW_BRANCH="my-branch" # Only needed for preview branches
26
+ ```
27
+
28
+ You can do the same if you are self-hosting and need to change the default URL by using `TRIGGER_API_URL`.
29
+
30
+ ```bash .env
31
+ TRIGGER_API_URL="https://trigger.example.com"
32
+ TRIGGER_PREVIEW_BRANCH="my-branch" # Only needed for preview branches
33
+ ```
34
+
35
+ The default URL is `https://api.trigger.dev`.
36
+
37
+ ### Manually Configuring the SDK
38
+
39
+ If you prefer to manually configure the SDK, you can call the `configure` method:
40
+
41
+ ```ts
42
+ import { configure } from "@trigger.dev/sdk";
43
+ import { myTask } from "./trigger/myTasks";
44
+
45
+ configure({
46
+ secretKey: "tr_dev_1234", // WARNING: Never actually hardcode your secret key like this
47
+ previewBranch: "my-branch", // Only needed for preview branches
48
+ baseURL: "https://mytrigger.example.com", // Optional
49
+ });
50
+
51
+ async function triggerTask() {
52
+ await myTask.trigger({ userId: "1234" }); // This will use the secret key and base URL you configured
53
+ }
54
+ ```