@trigger.dev/sdk 4.5.0-rc.6 → 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 (191) hide show
  1. package/dist/commonjs/v3/ai.d.ts +171 -5
  2. package/dist/commonjs/v3/ai.js +309 -22
  3. package/dist/commonjs/v3/ai.js.map +1 -1
  4. package/dist/commonjs/v3/chat-server.d.ts +8 -0
  5. package/dist/commonjs/v3/chat-server.js +32 -10
  6. package/dist/commonjs/v3/chat-server.js.map +1 -1
  7. package/dist/commonjs/v3/chat-server.test.js +51 -0
  8. package/dist/commonjs/v3/chat-server.test.js.map +1 -1
  9. package/dist/commonjs/v3/createStartSessionAction.test.js +30 -0
  10. package/dist/commonjs/v3/createStartSessionAction.test.js.map +1 -1
  11. package/dist/commonjs/v3/sessions.d.ts +3 -2
  12. package/dist/commonjs/v3/sessions.js +3 -2
  13. package/dist/commonjs/v3/sessions.js.map +1 -1
  14. package/dist/commonjs/version.js +1 -1
  15. package/dist/esm/v3/ai.d.ts +171 -5
  16. package/dist/esm/v3/ai.js +309 -22
  17. package/dist/esm/v3/ai.js.map +1 -1
  18. package/dist/esm/v3/chat-server.d.ts +8 -0
  19. package/dist/esm/v3/chat-server.js +32 -10
  20. package/dist/esm/v3/chat-server.js.map +1 -1
  21. package/dist/esm/v3/chat-server.test.js +51 -0
  22. package/dist/esm/v3/chat-server.test.js.map +1 -1
  23. package/dist/esm/v3/createStartSessionAction.test.js +30 -0
  24. package/dist/esm/v3/createStartSessionAction.test.js.map +1 -1
  25. package/dist/esm/v3/sessions.d.ts +3 -2
  26. package/dist/esm/v3/sessions.js +3 -2
  27. package/dist/esm/v3/sessions.js.map +1 -1
  28. package/dist/esm/version.js +1 -1
  29. package/docs/ai/prompts.mdx +430 -0
  30. package/docs/ai-chat/actions.mdx +115 -0
  31. package/docs/ai-chat/anatomy.mdx +71 -0
  32. package/docs/ai-chat/backend.mdx +817 -0
  33. package/docs/ai-chat/background-injection.mdx +221 -0
  34. package/docs/ai-chat/changelog.mdx +850 -0
  35. package/docs/ai-chat/chat-local.mdx +174 -0
  36. package/docs/ai-chat/client-protocol.mdx +1081 -0
  37. package/docs/ai-chat/compaction.mdx +411 -0
  38. package/docs/ai-chat/custom-agents.mdx +364 -0
  39. package/docs/ai-chat/error-handling.mdx +415 -0
  40. package/docs/ai-chat/fast-starts.mdx +672 -0
  41. package/docs/ai-chat/frontend.mdx +580 -0
  42. package/docs/ai-chat/how-it-works.mdx +230 -0
  43. package/docs/ai-chat/lifecycle-hooks.mdx +530 -0
  44. package/docs/ai-chat/mcp.mdx +101 -0
  45. package/docs/ai-chat/overview.mdx +90 -0
  46. package/docs/ai-chat/patterns/branching-conversations.mdx +284 -0
  47. package/docs/ai-chat/patterns/code-sandbox.mdx +126 -0
  48. package/docs/ai-chat/patterns/database-persistence.mdx +414 -0
  49. package/docs/ai-chat/patterns/human-in-the-loop.mdx +275 -0
  50. package/docs/ai-chat/patterns/large-payloads.mdx +169 -0
  51. package/docs/ai-chat/patterns/oom-resilience.mdx +120 -0
  52. package/docs/ai-chat/patterns/persistence-and-replay.mdx +211 -0
  53. package/docs/ai-chat/patterns/recovery-boot.mdx +230 -0
  54. package/docs/ai-chat/patterns/skills.mdx +221 -0
  55. package/docs/ai-chat/patterns/sub-agents.mdx +383 -0
  56. package/docs/ai-chat/patterns/tool-result-auditing.mdx +148 -0
  57. package/docs/ai-chat/patterns/trusted-edge-signals.mdx +337 -0
  58. package/docs/ai-chat/patterns/version-upgrades.mdx +172 -0
  59. package/docs/ai-chat/pending-messages.mdx +343 -0
  60. package/docs/ai-chat/prompt-caching.mdx +206 -0
  61. package/docs/ai-chat/quick-start.mdx +161 -0
  62. package/docs/ai-chat/reference.mdx +909 -0
  63. package/docs/ai-chat/server-chat.mdx +263 -0
  64. package/docs/ai-chat/sessions.mdx +333 -0
  65. package/docs/ai-chat/testing.mdx +682 -0
  66. package/docs/ai-chat/tools.mdx +191 -0
  67. package/docs/ai-chat/types.mdx +242 -0
  68. package/docs/ai-chat/upgrade-guide.mdx +515 -0
  69. package/docs/apikeys.mdx +54 -0
  70. package/docs/building-with-ai.mdx +261 -0
  71. package/docs/bulk-actions.mdx +49 -0
  72. package/docs/changelog.mdx +6 -0
  73. package/docs/cli-deploy-commands.mdx +9 -0
  74. package/docs/cli-dev-commands.mdx +9 -0
  75. package/docs/cli-dev.mdx +8 -0
  76. package/docs/cli-init-commands.mdx +58 -0
  77. package/docs/cli-introduction.mdx +25 -0
  78. package/docs/cli-list-profiles-commands.mdx +42 -0
  79. package/docs/cli-login-commands.mdx +33 -0
  80. package/docs/cli-logout-commands.mdx +33 -0
  81. package/docs/cli-preview-archive.mdx +59 -0
  82. package/docs/cli-promote-commands.mdx +9 -0
  83. package/docs/cli-switch.mdx +43 -0
  84. package/docs/cli-update-commands.mdx +42 -0
  85. package/docs/cli-whoami-commands.mdx +33 -0
  86. package/docs/community.mdx +6 -0
  87. package/docs/config/config-file.mdx +602 -0
  88. package/docs/config/extensions/additionalFiles.mdx +38 -0
  89. package/docs/config/extensions/additionalPackages.mdx +40 -0
  90. package/docs/config/extensions/aptGet.mdx +34 -0
  91. package/docs/config/extensions/audioWaveform.mdx +20 -0
  92. package/docs/config/extensions/custom.mdx +380 -0
  93. package/docs/config/extensions/emitDecoratorMetadata.mdx +29 -0
  94. package/docs/config/extensions/esbuildPlugin.mdx +31 -0
  95. package/docs/config/extensions/ffmpeg.mdx +45 -0
  96. package/docs/config/extensions/lightpanda.mdx +56 -0
  97. package/docs/config/extensions/overview.mdx +67 -0
  98. package/docs/config/extensions/playwright.mdx +195 -0
  99. package/docs/config/extensions/prismaExtension.mdx +1014 -0
  100. package/docs/config/extensions/puppeteer.mdx +30 -0
  101. package/docs/config/extensions/pythonExtension.mdx +182 -0
  102. package/docs/config/extensions/syncEnvVars.mdx +291 -0
  103. package/docs/context.mdx +235 -0
  104. package/docs/database-connections.mdx +213 -0
  105. package/docs/deploy-environment-variables.mdx +435 -0
  106. package/docs/deployment/atomic-deployment.mdx +172 -0
  107. package/docs/deployment/overview.mdx +257 -0
  108. package/docs/deployment/preview-branches.mdx +224 -0
  109. package/docs/errors-retrying.mdx +379 -0
  110. package/docs/github-actions.mdx +222 -0
  111. package/docs/github-integration.mdx +136 -0
  112. package/docs/github-repo.mdx +8 -0
  113. package/docs/help-email.mdx +6 -0
  114. package/docs/help-slack.mdx +11 -0
  115. package/docs/hidden-tasks.mdx +56 -0
  116. package/docs/how-it-works.mdx +454 -0
  117. package/docs/how-to-reduce-your-spend.mdx +217 -0
  118. package/docs/idempotency.mdx +504 -0
  119. package/docs/introduction.mdx +223 -0
  120. package/docs/limits.mdx +241 -0
  121. package/docs/logging.mdx +195 -0
  122. package/docs/machines.mdx +952 -0
  123. package/docs/manual-setup.mdx +632 -0
  124. package/docs/mcp-agent-rules.mdx +41 -0
  125. package/docs/mcp-introduction.mdx +385 -0
  126. package/docs/mcp-tools.mdx +273 -0
  127. package/docs/migrating-from-v3.mdx +334 -0
  128. package/docs/observability/dashboards.mdx +102 -0
  129. package/docs/observability/query.mdx +585 -0
  130. package/docs/open-source-contributing.mdx +16 -0
  131. package/docs/open-source-self-hosting.mdx +541 -0
  132. package/docs/private-networking/aws-console-setup.mdx +304 -0
  133. package/docs/private-networking/overview.mdx +144 -0
  134. package/docs/private-networking/troubleshooting.mdx +78 -0
  135. package/docs/queue-concurrency.mdx +354 -0
  136. package/docs/quick-start.mdx +97 -0
  137. package/docs/realtime/auth.mdx +208 -0
  138. package/docs/realtime/backend/overview.mdx +45 -0
  139. package/docs/realtime/backend/streams.mdx +418 -0
  140. package/docs/realtime/backend/subscribe.mdx +225 -0
  141. package/docs/realtime/how-it-works.mdx +94 -0
  142. package/docs/realtime/overview.mdx +63 -0
  143. package/docs/realtime/react-hooks/overview.mdx +73 -0
  144. package/docs/realtime/react-hooks/streams.mdx +449 -0
  145. package/docs/realtime/react-hooks/subscribe.mdx +674 -0
  146. package/docs/realtime/react-hooks/swr.mdx +87 -0
  147. package/docs/realtime/react-hooks/triggering.mdx +194 -0
  148. package/docs/realtime/react-hooks/use-wait-token.mdx +34 -0
  149. package/docs/realtime/run-object.mdx +174 -0
  150. package/docs/replaying.mdx +72 -0
  151. package/docs/request-feature.mdx +6 -0
  152. package/docs/roadmap.mdx +6 -0
  153. package/docs/run-tests.mdx +20 -0
  154. package/docs/run-usage.mdx +113 -0
  155. package/docs/runs/heartbeats.mdx +38 -0
  156. package/docs/runs/max-duration.mdx +139 -0
  157. package/docs/runs/metadata.mdx +734 -0
  158. package/docs/runs/priority.mdx +31 -0
  159. package/docs/runs.mdx +396 -0
  160. package/docs/self-hosting/docker.mdx +458 -0
  161. package/docs/self-hosting/env/supervisor.mdx +74 -0
  162. package/docs/self-hosting/env/webapp.mdx +276 -0
  163. package/docs/self-hosting/kubernetes.mdx +601 -0
  164. package/docs/self-hosting/overview.mdx +108 -0
  165. package/docs/skills.mdx +85 -0
  166. package/docs/tags.mdx +120 -0
  167. package/docs/tasks/overview.mdx +697 -0
  168. package/docs/tasks/scheduled.mdx +382 -0
  169. package/docs/tasks/schemaTask.mdx +413 -0
  170. package/docs/tasks/streams.mdx +884 -0
  171. package/docs/triggering.mdx +1320 -0
  172. package/docs/troubleshooting-alerts.mdx +385 -0
  173. package/docs/troubleshooting-debugging-in-vscode.mdx +8 -0
  174. package/docs/troubleshooting-github-issues.mdx +6 -0
  175. package/docs/troubleshooting-uptime-status.mdx +6 -0
  176. package/docs/troubleshooting.mdx +398 -0
  177. package/docs/upgrading-packages.mdx +80 -0
  178. package/docs/vercel-integration.mdx +207 -0
  179. package/docs/versioning.mdx +56 -0
  180. package/docs/video-walkthrough.mdx +23 -0
  181. package/docs/wait-for-token.mdx +540 -0
  182. package/docs/wait-for.mdx +42 -0
  183. package/docs/wait-until.mdx +53 -0
  184. package/docs/wait.mdx +18 -0
  185. package/docs/writing-tasks-introduction.mdx +33 -0
  186. package/package.json +8 -5
  187. package/skills/trigger-authoring-chat-agent/SKILL.md +296 -0
  188. package/skills/trigger-authoring-tasks/SKILL.md +254 -0
  189. package/skills/trigger-chat-agent-advanced/SKILL.md +368 -0
  190. package/skills/trigger-cost-savings/SKILL.md +116 -0
  191. package/skills/trigger-realtime-and-frontend/SKILL.md +276 -0
@@ -0,0 +1,909 @@
1
+ ---
2
+ title: "API Reference"
3
+ sidebarTitle: "API Reference"
4
+ description: "Complete API reference for the AI Agents SDK — backend options, events, frontend transport, and hooks."
5
+ ---
6
+
7
+ import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
8
+
9
+ <RcBanner />
10
+
11
+ ## Compatibility
12
+
13
+ | Dependency | Supported | Notes |
14
+ |---|---|---|
15
+ | `@trigger.dev/sdk` | `>=4.5.0-rc.0` | The chat agent surface lives in this SDK release. Install with `@trigger.dev/sdk@rc`. |
16
+ | `ai` (Vercel AI SDK) | `^5.0.0 \|\| ^6.0.0 \|\| >=7.0.0-canary <8` | Declared as a peer. v6 is what we develop against day to day; v5 and v7 work too (v7 is in canary/beta upstream). Your installed `ai` major drives the chat surface's types. |
17
+ | `@ai-sdk/otel` | `1.x` (v7 only) | Optional. AI SDK 7 moved model-call span emission out of `ai` core into this adapter. Install it alongside `ai@7` and the SDK auto-registers it, so your model calls show up as spans in the run trace. Not needed on v5/v6, where `ai` core emits spans. See [AI SDK 7 telemetry](#ai-sdk-7-telemetry) below. |
18
+ | `@ai-sdk/react` | matches your `ai` major | Pulled in by `useChat`. The transport works with whichever React hook ships in the same major as your `ai` version. |
19
+ | `react` | `^18.0 \|\| ^19.0` | Required only if you use `@trigger.dev/sdk/chat/react` (the frontend transport). Server-only consumers can skip React entirely. |
20
+ | Node.js | `>=18.20.0` | The SDK's engine constraint. The chat agent itself works on any version the SDK supports. |
21
+ | Provider packages (`@ai-sdk/openai`, `@ai-sdk/anthropic`, etc.) | versions that target your `ai` major | Pick a provider package whose `ai` peer matches yours. The chat agent doesn't depend on any specific provider — pass whatever model you want into `streamText`. |
22
+
23
+ The `ai` peer is **optional** — server-only setups that don't call `streamText` (raw `task()` with chat primitives) can skip the AI SDK entirely.
24
+
25
+ ### AI SDK 7 telemetry
26
+
27
+ On **AI SDK 7**, model-call spans are emitted by `@ai-sdk/otel` rather than `ai` core. Install it alongside `ai@7`:
28
+
29
+ ```bash
30
+ npm install @ai-sdk/otel
31
+ ```
32
+
33
+ The SDK registers it once per worker at chat agent boot, so the `experimental_telemetry` config wired up by `chat.toStreamTextOptions()` keeps producing spans in your run trace with no extra setup. On v5 and v6 nothing changes: `ai` core emits the spans and `@ai-sdk/otel` isn't needed.
34
+
35
+ If you (or a library you import) already register `@ai-sdk/otel` yourself, the SDK detects the existing integration and skips its own registration, so you won't get duplicate spans. To opt out of the auto-registration entirely, set `TRIGGER_AI_SDK_OTEL_AUTOREGISTER=0`.
36
+
37
+ ## ChatAgentOptions
38
+
39
+ Options for `chat.agent()`.
40
+
41
+ | Option | Type | Default | Description |
42
+ | ----------------------------- | ----------------------------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------- |
43
+ | `id` | `string` | required | Task identifier |
44
+ | `run` | `(payload: ChatTaskRunPayload) => Promise<unknown>` | required | Handler for each turn |
45
+ | `clientDataSchema` | `TaskSchema` | — | Schema for validating and typing `clientData` |
46
+ | `onBoot` | `(event: BootEvent) => Promise<void> \| void` | — | Fires once per worker process — initial, preloaded, AND reactive continuation. Use for `chat.local` init and per-process resources. See [onBoot](/ai-chat/lifecycle-hooks#onboot). |
47
+ | `onRecoveryBoot` | `(event: RecoveryBootEvent) => Promise<RecoveryBootResult \| void> \| RecoveryBootResult \| void` | — | Fires on a continuation boot when the dead predecessor left recovered state (partial assistant or in-flight users). Override the smart default — drop partial, synthesize tool results, emit a recovery banner. See [Recovery boot](/ai-chat/patterns/recovery-boot). |
48
+ | `onPreload` | `(event: PreloadEvent) => Promise<void> \| void` | — | Fires on preloaded runs before the first message |
49
+ | `onChatStart` | `(event: ChatStartEvent) => Promise<void> \| void` | — | Fires once per chat, on the very first user message. Does NOT fire on continuation runs or OOM-retries — see [onChatStart](/ai-chat/lifecycle-hooks#onchatstart). |
50
+ | `onValidateMessages` | `(event: ValidateMessagesEvent) => UIMessage[] \| Promise<UIMessage[]>` | — | Validate/transform UIMessages before model conversion. See [onValidateMessages](/ai-chat/lifecycle-hooks#onvalidatemessages) |
51
+ | `hydrateMessages` | `(event: HydrateMessagesEvent) => UIMessage[] \| Promise<UIMessage[]>` | — | Load message history from backend, replacing the linear accumulator. See [hydrateMessages](/ai-chat/lifecycle-hooks#hydratemessages) |
52
+ | `actionSchema` | `TaskSchema` | — | Schema for validating custom actions sent via `transport.sendAction()`. See [Actions](/ai-chat/actions) |
53
+ | `onAction` | `(event: ActionEvent) => Promise<unknown> \| unknown` | — | Handle custom actions. Actions are not turns — only `hydrateMessages` + `onAction` fire. Return a `StreamTextResult` (or `string` / `UIMessage`) for a model response; return `void` for side-effect-only. See [Actions](/ai-chat/actions) |
54
+ | `onTurnStart` | `(event: TurnStartEvent) => Promise<void> \| void` | — | Fires every turn before `run()` |
55
+ | `onBeforeTurnComplete` | `(event: BeforeTurnCompleteEvent) => Promise<void> \| void` | — | Fires after response but before stream closes. Includes `writer`. |
56
+ | `onTurnComplete` | `(event: TurnCompleteEvent) => Promise<void> \| void` | — | Fires after each turn completes (stream closed) |
57
+ | `onCompacted` | `(event: CompactedEvent) => Promise<void> \| void` | — | Fires when compaction occurs. Includes `writer`. See [Compaction](/ai-chat/compaction) |
58
+ | `compaction` | `ChatAgentCompactionOptions` | — | Automatic context compaction. See [Compaction](/ai-chat/compaction) |
59
+ | `pendingMessages` | `PendingMessagesOptions` | — | Mid-execution message injection. See [Pending Messages](/ai-chat/pending-messages) |
60
+ | `prepareMessages` | `(event: PrepareMessagesEvent) => ModelMessage[]` | — | Transform model messages before use (cache breaks, context injection, etc.) |
61
+ | `tools` | `ToolSet \| ((event: ResolveToolsEvent) => ToolSet \| Promise<ToolSet>)` | — | Tools for this agent. Threads each tool's `toModelOutput` through cross-turn history re-conversion, and hands the resolved set back on the run payload. Static set or per-turn function. See [Tools](/ai-chat/tools). |
62
+ | `maxTurns` | `number` | `100` | Max conversational turns per run |
63
+ | `turnTimeout` | `string` | `"1h"` | How long to wait for next message |
64
+ | `idleTimeoutInSeconds` | `number` | `30` | Seconds to stay idle before suspending |
65
+ | `chatAccessTokenTTL` | `string` | `"1h"` | How long the scoped access token remains valid |
66
+ | `preloadIdleTimeoutInSeconds` | `number` | Same as `idleTimeoutInSeconds` | Idle timeout after `onPreload` fires |
67
+ | `preloadTimeout` | `string` | Same as `turnTimeout` | Suspend timeout for preloaded runs |
68
+ | `uiMessageStreamOptions` | `ChatUIMessageStreamOptions` | — | Default options for `toUIMessageStream()`. Per-turn override via `chat.setUIMessageStreamOptions()` |
69
+ | `onChatSuspend` | `(event: ChatSuspendEvent) => Promise<void> \| void` | — | Fires right before the run suspends. See [onChatSuspend](/ai-chat/lifecycle-hooks#onchatsuspend--onchatresume) |
70
+ | `onChatResume` | `(event: ChatResumeEvent) => Promise<void> \| void` | — | Fires right after the run resumes from suspension |
71
+ | `exitAfterPreloadIdle` | `boolean` | `false` | Exit run after preload idle timeout instead of suspending. See [exitAfterPreloadIdle](/ai-chat/lifecycle-hooks#exitafterpreloadidle) |
72
+ | `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) |
73
+
74
+ 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`).
75
+
76
+ ## Task context (`ctx`)
77
+
78
+ All **`chat.agent`** lifecycle events (**`onBoot`**, **`onPreload`**, **`onChatStart`**, **`onTurnStart`**, **`onBeforeTurnComplete`**, **`onTurnComplete`**, **`onCompacted`**) and the object passed to **`run`** include **`ctx`**: the same **`TaskRunContext`** shape as the `ctx` in `task({ run: (payload, { ctx }) => ... })`.
79
+
80
+ <Note>
81
+ **`onValidateMessages`** does not include `ctx` — it fires before message accumulation and is designed for pure validation/transformation of incoming messages.
82
+ </Note>
83
+
84
+ Use **`ctx`** for run metadata, tags, parent links, or any API that needs the full run record. The chat-specific string **`runId`** on events is always **`ctx.run.id`**; both are provided for convenience.
85
+
86
+ ```ts
87
+ import type { TaskRunContext } from "@trigger.dev/sdk";
88
+ // Equivalent alias (same type):
89
+ import type { Context } from "@trigger.dev/sdk";
90
+ ```
91
+
92
+ <Note>
93
+ Prefer `import type { TaskRunContext } from "@trigger.dev/sdk"` in application code. Do not depend on `@trigger.dev/core` directly.
94
+ </Note>
95
+
96
+ ## ChatTaskRunPayload
97
+
98
+ The payload passed to the `run` function.
99
+
100
+ | Field | Type | Description |
101
+ | -------------- | ------------------------------------------ | -------------------------------------------------------------------- |
102
+ | `ctx` | `TaskRunContext` | Full task run context — same as `task` `run`’s `{ ctx }` |
103
+ | `messages` | `ModelMessage[]` | Model-ready messages — pass directly to `streamText` |
104
+ | `tools` | `ToolSet` | Resolved tools declared on the agent config (empty object when none). Pass straight to `streamText`. See [Tools](/ai-chat/tools). |
105
+ | `chatId` | `string` | Your conversation ID (the session's `externalId`) |
106
+ | `sessionId` | `string` | Friendly ID of the backing Session (`session_*`). Use with `sessions.open()` for advanced cases. Always set — every chat.agent run is bound to a Session. |
107
+ | `trigger` | `"submit-message" \| "regenerate-message"` | What triggered the request |
108
+ | `messageId` | `string \| undefined` | Message ID (for regenerate) |
109
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend (typed when schema is provided) |
110
+ | `continuation` | `boolean` | Whether this run is continuing an existing chat (previous run ended) |
111
+ | `signal` | `AbortSignal` | Combined stop + cancel signal |
112
+ | `cancelSignal` | `AbortSignal` | Cancel-only signal |
113
+ | `stopSignal` | `AbortSignal` | Stop-only signal (per-turn) |
114
+ | `previousTurnUsage` | `LanguageModelUsage \| undefined` | Token usage from the previous turn (undefined on turn 0) |
115
+ | `totalUsage` | `LanguageModelUsage` | Cumulative token usage across completed turns so far |
116
+
117
+ ## BootEvent
118
+
119
+ Passed to the `onBoot` callback.
120
+
121
+ | Field | Type | Description |
122
+ | ----------------- | --------------------------- | ---------------------------------------------------------------------------------------------------- |
123
+ | `ctx` | `TaskRunContext` | Full task run context — see [Task context](#task-context-ctx) |
124
+ | `chatId` | `string` | Chat session ID |
125
+ | `runId` | `string` | The Trigger.dev run ID for this run boot |
126
+ | `chatAccessToken` | `string` | Scoped access token for this run |
127
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
128
+ | `continuation` | `boolean` | `true` when this run is taking over from a prior dead run (cancel / crash / `endRun` / OOM retry) |
129
+ | `previousRunId` | `string \| undefined` | Public id of the prior run when `continuation` is true |
130
+ | `preloaded` | `boolean` | Whether this run was triggered as a preload |
131
+
132
+ ## RecoveryBootEvent
133
+
134
+ Passed to the `onRecoveryBoot` callback. See [Recovery boot](/ai-chat/patterns/recovery-boot) for the full guide.
135
+
136
+ | Field | Type | Description |
137
+ | ------------------ | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
138
+ | `ctx` | `TaskRunContext` | Full task run context — see [Task context](#task-context-ctx) |
139
+ | `chatId` | `string` | Chat session ID |
140
+ | `runId` | `string` | The Trigger.dev run ID for this run boot |
141
+ | `previousRunId` | `string` | Public id of the prior run that died |
142
+ | `cause` | `"cancelled" \| "crashed" \| "unknown"` | Best-effort cause. Currently always `"unknown"` — forward-looking, don't branch on it |
143
+ | `settledMessages` | `TUIMessage[]` | Chain persisted by the predecessor's last `onTurnComplete` |
144
+ | `inFlightUsers` | `TUIMessage[]` | User messages on `session.in` past the cursor — the message(s) the predecessor never acknowledged |
145
+ | `partialAssistant` | `TUIMessage \| undefined` | The trailing assistant message whose stream never received `finish` |
146
+ | `pendingToolCalls` | [`RecoveryPendingToolCall[]`](#recoverypendingtoolcall) | Tool calls in `input-available` state extracted from `partialAssistant` |
147
+ | `writer` | [`ChatWriter`](#chatwriter) | Lazy session.out writer — emit a recovery banner / signal here |
148
+
149
+ ## RecoveryBootResult
150
+
151
+ Return value of `onRecoveryBoot`. Every field is optional — omit to accept the smart default.
152
+
153
+ | Field | Type | Description |
154
+ | ---------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
155
+ | `chain` | `TUIMessage[]` | Replaces the seed chain. Default: `[...settledMessages, firstInFlightUser, partialAssistant]` when both present; `settledMessages` otherwise. |
156
+ | `recoveredTurns` | `TUIMessage[]` | User messages to dispatch as fresh turns. Default: `inFlightUsers.slice(1)` when smart-default fires; `inFlightUsers` otherwise. |
157
+ | `beforeBoot` | `() => Promise<void>` | Runs after the writer flushes and before the first recovered turn fires. Use for blocking persistence work. |
158
+
159
+ ## RecoveryPendingToolCall
160
+
161
+ | Field | Type | Description |
162
+ | ------------- | --------- | ------------------------------------------------------------ |
163
+ | `toolCallId` | `string` | The AI SDK tool call id |
164
+ | `toolName` | `string` | The tool name (the `tool-${name}` suffix on the part type) |
165
+ | `input` | `unknown` | The input the model produced for the call |
166
+ | `partIndex` | `number` | Index into `partialAssistant.parts` for in-place edits |
167
+
168
+ ## PreloadEvent
169
+
170
+ Passed to the `onPreload` callback.
171
+
172
+ | Field | Type | Description |
173
+ | ----------------- | --------------------------- | -------------------------------------------------------------- |
174
+ | `ctx` | `TaskRunContext` | Full task run context — see [Task context](#task-context-ctx) |
175
+ | `chatId` | `string` | Chat session ID |
176
+ | `runId` | `string` | The Trigger.dev run ID |
177
+ | `chatAccessToken` | `string` | Scoped access token for this run |
178
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
179
+ | `writer` | [`ChatWriter`](#chatwriter) | Stream writer for custom chunks. Lazy — no overhead if unused. |
180
+
181
+ ## ChatStartEvent
182
+
183
+ Passed to the `onChatStart` callback.
184
+
185
+ | Field | Type | Description |
186
+ | ----------------- | --------------------------- | -------------------------------------------------------------- |
187
+ | `ctx` | `TaskRunContext` | Full task run context — see [Task context](#task-context-ctx) |
188
+ | `chatId` | `string` | Chat session ID |
189
+ | `messages` | `ModelMessage[]` | Initial model-ready messages |
190
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
191
+ | `runId` | `string` | The Trigger.dev run ID |
192
+ | `chatAccessToken` | `string` | Scoped access token for this run |
193
+ | `continuation` | `boolean` | Whether this run is continuing an existing chat |
194
+ | `previousRunId` | `string \| undefined` | Previous run ID (only when `continuation` is true) |
195
+ | `preloaded` | `boolean` | Whether this run was preloaded before the first message |
196
+ | `writer` | [`ChatWriter`](#chatwriter) | Stream writer for custom chunks. Lazy — no overhead if unused. |
197
+
198
+ ## ValidateMessagesEvent
199
+
200
+ Passed to the `onValidateMessages` callback.
201
+
202
+ | Field | Type | Description |
203
+ | --------- | --------------------------------------------------------------- | ---------------------------------------- |
204
+ | `messages` | `UIMessage[]` | Incoming UI messages for this turn |
205
+ | `chatId` | `string` | Chat session ID |
206
+ | `turn` | `number` | Turn number (0-indexed) |
207
+ | `trigger` | `"submit-message" \| "regenerate-message" \| "preload" \| "close"` | The trigger type for this turn |
208
+
209
+ ## ResolveToolsEvent
210
+
211
+ Passed to the `tools` function form on `chat.agent`, once per turn, to resolve the tool set for that turn. See [Tools](/ai-chat/tools#static-or-per-turn-tools).
212
+
213
+ | Field | Type | Description |
214
+ | -------------- | --------------------------- | ------------------------------------------------- |
215
+ | `chatId` | `string` | Chat session ID |
216
+ | `turn` | `number` | Turn number (0-indexed) |
217
+ | `continuation` | `boolean` | Whether this run is continuing an existing chat |
218
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
219
+
220
+ ## HydrateMessagesEvent
221
+
222
+ Passed to the `hydrateMessages` callback. See [hydrateMessages](/ai-chat/lifecycle-hooks#hydratemessages).
223
+
224
+ | Field | Type | Description |
225
+ | ------------------ | ----------------------------------------------------- | --------------------------------------------------------- |
226
+ | `chatId` | `string` | Chat session ID |
227
+ | `turn` | `number` | Turn number (0-indexed) |
228
+ | `trigger` | `"submit-message" \| "regenerate-message" \| "action"` | The trigger type for this turn |
229
+ | `incomingMessages` | `UIMessage[]` | Validated wire messages from the frontend (empty for actions) |
230
+ | `previousMessages` | `UIMessage[]` | Accumulated UI messages before this turn (`[]` on turn 0) |
231
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
232
+ | `continuation` | `boolean` | Whether this run is continuing an existing chat |
233
+ | `previousRunId` | `string \| undefined` | Previous run ID (only when `continuation` is true) |
234
+
235
+ ## ActionEvent
236
+
237
+ Passed to the `onAction` callback. See [Actions](/ai-chat/actions).
238
+
239
+ | Field | Type | Description |
240
+ | ------------ | --------------------------- | -------------------------------------------------------- |
241
+ | `action` | Typed by `actionSchema` | The parsed and validated action payload |
242
+ | `chatId` | `string` | Chat session ID |
243
+ | `turn` | `number` | Turn number (0-indexed) |
244
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
245
+ | `uiMessages` | `UIMessage[]` | Accumulated UI messages (after hydration, if set) |
246
+ | `messages` | `ModelMessage[]` | Accumulated model messages (after hydration, if set) |
247
+
248
+ ## TurnStartEvent
249
+
250
+ Passed to the `onTurnStart` callback.
251
+
252
+ | Field | Type | Description |
253
+ | ----------------- | --------------------------- | -------------------------------------------------------------- |
254
+ | `ctx` | `TaskRunContext` | Full task run context — see [Task context](#task-context-ctx) |
255
+ | `chatId` | `string` | Chat session ID |
256
+ | `messages` | `ModelMessage[]` | Full accumulated conversation (model format) |
257
+ | `uiMessages` | `UIMessage[]` | Full accumulated conversation (UI format) |
258
+ | `turn` | `number` | Turn number (0-indexed) |
259
+ | `runId` | `string` | The Trigger.dev run ID |
260
+ | `chatAccessToken` | `string` | Scoped access token for this run |
261
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
262
+ | `continuation` | `boolean` | Whether this run is continuing an existing chat |
263
+ | `previousRunId` | `string \| undefined` | Previous run ID (only when `continuation` is true) |
264
+ | `preloaded` | `boolean` | Whether this run was preloaded |
265
+ | `writer` | [`ChatWriter`](#chatwriter) | Stream writer for custom chunks. Lazy — no overhead if unused. |
266
+
267
+ ## TurnCompleteEvent
268
+
269
+ Passed to the `onTurnComplete` callback.
270
+
271
+ | Field | Type | Description |
272
+ | -------------------- | --------------------------------- | ---------------------------------------------------- |
273
+ | `ctx` | `TaskRunContext` | Full task run context — see [Task context](#task-context-ctx) |
274
+ | `chatId` | `string` | Chat session ID |
275
+ | `messages` | `ModelMessage[]` | Full accumulated conversation (model format) |
276
+ | `uiMessages` | `UIMessage[]` | Full accumulated conversation (UI format) |
277
+ | `newMessages` | `ModelMessage[]` | Only this turn's messages (model format) |
278
+ | `newUIMessages` | `UIMessage[]` | Only this turn's messages (UI format) |
279
+ | `responseMessage` | `UIMessage \| undefined` | The assistant's response for this turn |
280
+ | `rawResponseMessage` | `UIMessage \| undefined` | Raw response before abort cleanup |
281
+ | `turn` | `number` | Turn number (0-indexed) |
282
+ | `runId` | `string` | The Trigger.dev run ID |
283
+ | `chatAccessToken` | `string` | Scoped access token for this run |
284
+ | `lastEventId` | `string \| undefined` | Stream position for resumption |
285
+ | `stopped` | `boolean` | Whether the user stopped generation during this turn |
286
+ | `continuation` | `boolean` | Whether this run is continuing an existing chat |
287
+ | `usage` | `LanguageModelUsage \| undefined` | Token usage for this turn |
288
+ | `totalUsage` | `LanguageModelUsage` | Cumulative token usage across all turns |
289
+ | `finishReason` | `FinishReason \| undefined` | Why the LLM stopped (`"stop"`, `"tool-calls"`, `"error"`, …) |
290
+ | `error` | `unknown` | Set when the turn threw; `responseMessage` is then undefined or partial |
291
+
292
+ ## BeforeTurnCompleteEvent
293
+
294
+ Passed to the `onBeforeTurnComplete` callback. Same fields as `TurnCompleteEvent` (including **`ctx`**) plus a `writer`.
295
+
296
+ | Field | Type | Description |
297
+ | -------------------------------- | --------------------------- | ----------------------------------------------------------------------------- |
298
+ | _(all TurnCompleteEvent fields)_ | | See [TurnCompleteEvent](#turncompleteevent) (includes `ctx`) |
299
+ | `writer` | [`ChatWriter`](#chatwriter) | Stream writer — the stream is still open so chunks appear in the current turn |
300
+
301
+ ## ChatSuspendEvent
302
+
303
+ Passed to the `onChatSuspend` callback. A discriminated union on `phase`.
304
+
305
+ | Field | Type | Description |
306
+ | ------------ | --------------------------- | -------------------------------------------------------- |
307
+ | `phase` | `"preload" \| "turn"` | Whether this is a preload or post-turn suspension |
308
+ | `ctx` | `TaskRunContext` | Full task run context |
309
+ | `chatId` | `string` | Chat session ID |
310
+ | `runId` | `string` | The Trigger.dev run ID |
311
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
312
+ | `turn` | `number` | Turn number (**`"turn"` phase only**) |
313
+ | `messages` | `ModelMessage[]` | Accumulated model messages (**`"turn"` phase only**) |
314
+ | `uiMessages` | `UIMessage[]` | Accumulated UI messages (**`"turn"` phase only**) |
315
+
316
+ ## ChatResumeEvent
317
+
318
+ Passed to the `onChatResume` callback. Same discriminated union shape as `ChatSuspendEvent`.
319
+
320
+ | Field | Type | Description |
321
+ | ------------ | --------------------------- | -------------------------------------------------------- |
322
+ | `phase` | `"preload" \| "turn"` | Whether this is a preload or post-turn resumption |
323
+ | `ctx` | `TaskRunContext` | Full task run context |
324
+ | `chatId` | `string` | Chat session ID |
325
+ | `runId` | `string` | The Trigger.dev run ID |
326
+ | `clientData` | Typed by `clientDataSchema` | Custom data from the frontend |
327
+ | `turn` | `number` | Turn number (**`"turn"` phase only**) |
328
+ | `messages` | `ModelMessage[]` | Accumulated model messages (**`"turn"` phase only**) |
329
+ | `uiMessages` | `UIMessage[]` | Accumulated UI messages (**`"turn"` phase only**) |
330
+
331
+ ## ChatWriter
332
+
333
+ A stream writer passed to lifecycle callbacks. Write custom `UIMessageChunk` parts (e.g. `data-*` parts) to the chat stream.
334
+
335
+ The writer is lazy — no stream is opened unless you call `write()` or `merge()`, so there's zero overhead for callbacks that don't use it.
336
+
337
+ | Method | Type | Description |
338
+ | --------------- | -------------------------------------------------- | -------------------------------------------------- |
339
+ | `write(part)` | `(part: UIMessageChunk) => void` | Write a single chunk to the chat stream |
340
+ | `merge(stream)` | `(stream: ReadableStream<UIMessageChunk>) => void` | Merge another stream's chunks into the chat stream |
341
+
342
+ ```ts
343
+ onTurnStart: async ({ writer }) => {
344
+ // Write a custom data part — render it on the frontend
345
+ writer.write({ type: "data-status", data: { loading: true } });
346
+ },
347
+ onBeforeTurnComplete: async ({ writer, usage }) => {
348
+ // Stream is still open — these chunks arrive before the turn ends
349
+ writer.write({ type: "data-usage", data: { tokens: usage?.totalTokens } });
350
+ },
351
+ ```
352
+
353
+ ## ChatAgentCompactionOptions
354
+
355
+ Options for the `compaction` field on `chat.agent()`. See [Compaction](/ai-chat/compaction) for usage guide.
356
+
357
+ | Option | Type | Required | Description |
358
+ | ---------------------- | ---------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------- |
359
+ | `shouldCompact` | `(event: ShouldCompactEvent) => boolean \| Promise<boolean>` | Yes | Decide whether to compact. Return `true` to trigger |
360
+ | `summarize` | `(event: SummarizeEvent) => Promise<string>` | Yes | Generate a summary from the current messages |
361
+ | `compactUIMessages` | `(event: CompactMessagesEvent) => UIMessage[] \| Promise<UIMessage[]>` | No | Transform UI messages after compaction. Default: preserve all |
362
+ | `compactModelMessages` | `(event: CompactMessagesEvent) => ModelMessage[] \| Promise<ModelMessage[]>` | No | Transform model messages after compaction. Default: replace all with summary |
363
+
364
+ ## CompactMessagesEvent
365
+
366
+ Passed to `compactUIMessages` and `compactModelMessages` callbacks.
367
+
368
+ | Field | Type | Description |
369
+ | --------------- | -------------------- | ---------------------------------------------------- |
370
+ | `summary` | `string` | The generated summary text |
371
+ | `uiMessages` | `UIMessage[]` | Current UI messages (full conversation) |
372
+ | `modelMessages` | `ModelMessage[]` | Current model messages (full conversation) |
373
+ | `chatId` | `string` | Chat session ID |
374
+ | `turn` | `number` | Current turn (0-indexed) |
375
+ | `clientData` | `unknown` | Custom data from the frontend |
376
+ | `source` | `"inner" \| "outer"` | Whether compaction is between steps or between turns |
377
+
378
+ ## CompactedEvent
379
+
380
+ Passed to the `onCompacted` callback.
381
+
382
+ | Field | Type | Description |
383
+ | -------------- | --------------------------- | ------------------------------------------------- |
384
+ | `ctx` | `TaskRunContext` | Full task run context — see [Task context](#task-context-ctx) |
385
+ | `summary` | `string` | The generated summary text |
386
+ | `messages` | `ModelMessage[]` | Messages that were compacted (pre-compaction) |
387
+ | `messageCount` | `number` | Number of messages before compaction |
388
+ | `usage` | `LanguageModelUsage` | Token usage from the triggering step/turn |
389
+ | `totalTokens` | `number \| undefined` | Total token count that triggered compaction |
390
+ | `inputTokens` | `number \| undefined` | Input token count |
391
+ | `outputTokens` | `number \| undefined` | Output token count |
392
+ | `stepNumber` | `number` | Step number (-1 for outer loop) |
393
+ | `chatId` | `string \| undefined` | Chat session ID |
394
+ | `turn` | `number \| undefined` | Current turn |
395
+ | `writer` | [`ChatWriter`](#chatwriter) | Stream writer for custom chunks during compaction |
396
+
397
+ ## PendingMessagesOptions
398
+
399
+ Options for the `pendingMessages` field. See [Pending Messages](/ai-chat/pending-messages) for usage guide.
400
+
401
+ | Option | Type | Required | Description |
402
+ | -------------- | --------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------- |
403
+ | `shouldInject` | `(event: PendingMessagesBatchEvent) => boolean \| Promise<boolean>` | No | Decide whether to inject the batch between tool-call steps. If absent, no injection. |
404
+ | `prepare` | `(event: PendingMessagesBatchEvent) => ModelMessage[] \| Promise<ModelMessage[]>` | No | Transform the batch before injection. Default: convert each via `convertToModelMessages`. |
405
+ | `onReceived` | `(event: PendingMessageReceivedEvent) => void \| Promise<void>` | No | Called when a message arrives during streaming (per-message). |
406
+ | `onInjected` | `(event: PendingMessagesInjectedEvent) => void \| Promise<void>` | No | Called after a batch is injected via prepareStep. |
407
+
408
+ ## PendingMessagesBatchEvent
409
+
410
+ Passed to `shouldInject` and `prepare` callbacks.
411
+
412
+ | Field | Type | Description |
413
+ | --------------- | ------------------ | ----------------------------- |
414
+ | `messages` | `UIMessage[]` | All pending messages (batch) |
415
+ | `modelMessages` | `ModelMessage[]` | Current conversation |
416
+ | `steps` | `CompactionStep[]` | Completed steps so far |
417
+ | `stepNumber` | `number` | Current step (0-indexed) |
418
+ | `chatId` | `string` | Chat session ID |
419
+ | `turn` | `number` | Current turn (0-indexed) |
420
+ | `clientData` | `unknown` | Custom data from the frontend |
421
+
422
+ ## PendingMessagesInjectedEvent
423
+
424
+ Passed to `onInjected` callback.
425
+
426
+ | Field | Type | Description |
427
+ | ----------------------- | ---------------- | ------------------------------------- |
428
+ | `messages` | `UIMessage[]` | All injected UI messages |
429
+ | `injectedModelMessages` | `ModelMessage[]` | The model messages that were injected |
430
+ | `chatId` | `string` | Chat session ID |
431
+ | `turn` | `number` | Current turn |
432
+ | `stepNumber` | `number` | Step where injection occurred |
433
+
434
+ ## UsePendingMessagesReturn
435
+
436
+ Return value of `usePendingMessages` hook. See [Pending Messages — Frontend](/ai-chat/pending-messages#frontend-usependingmessages-hook).
437
+
438
+ | Property/Method | Type | Description |
439
+ | ----------------------- | -------------------------------------- | --------------------------------------------------------------- |
440
+ | `pending` | `PendingMessage[]` | Current pending messages with mode and injection status |
441
+ | `steer` | `(text: string) => void` | Send a steering message (or normal message when not streaming) |
442
+ | `queue` | `(text: string) => void` | Queue for next turn (or send normally when not streaming) |
443
+ | `promoteToSteering` | `(id: string) => void` | Convert a queued message to steering |
444
+ | `isInjectionPoint` | `(part: unknown) => boolean` | Check if an assistant message part is an injection confirmation |
445
+ | `getInjectedMessageIds` | `(part: unknown) => string[]` | Get message IDs from an injection point |
446
+ | `getInjectedMessages` | `(part: unknown) => InjectedMessage[]` | Get messages (id + text) from an injection point |
447
+
448
+ ## ChatSessionOptions
449
+
450
+ Options for `chat.createSession()`.
451
+
452
+ | Option | Type | Default | Description |
453
+ | ---------------------- | --------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
454
+ | `signal` | `AbortSignal` | required | Run-level cancel signal |
455
+ | `idleTimeoutInSeconds` | `number` | `30` | Seconds to stay idle between turns |
456
+ | `timeout` | `string` | `"1h"` | Duration string for suspend timeout |
457
+ | `maxTurns` | `number` | `100` | Max turns before ending |
458
+ | `compaction` | `ChatAgentCompactionOptions`| `undefined` | Automatic context [compaction](/ai-chat/compaction) — same options as `chat.agent({ compaction })` |
459
+ | `pendingMessages` | `PendingMessagesOptions` | `undefined` | Mid-execution [message injection](/ai-chat/pending-messages) — same options as `chat.agent({ pendingMessages })` |
460
+
461
+ ## ChatTurn
462
+
463
+ Each turn yielded by `chat.createSession()`.
464
+
465
+ | Field | Type | Description |
466
+ | ------------------- | --------------------------------- | ---------------------------------------------------------- |
467
+ | `number` | `number` | Turn number (0-indexed) |
468
+ | `chatId` | `string` | Chat session ID |
469
+ | `trigger` | `string` | What triggered this turn |
470
+ | `clientData` | `unknown` | Client data from the transport |
471
+ | `messages` | `ModelMessage[]` | Full accumulated model messages |
472
+ | `uiMessages` | `UIMessage[]` | Full accumulated UI messages |
473
+ | `signal` | `AbortSignal` | Combined stop+cancel signal (fresh each turn) |
474
+ | `stopped` | `boolean` | Whether the user stopped generation this turn |
475
+ | `continuation` | `boolean` | Whether this is a continuation run |
476
+ | `previousTurnUsage` | `LanguageModelUsage \| undefined` | Token usage from the previous turn (undefined on turn 0) |
477
+ | `totalUsage` | `LanguageModelUsage` | Cumulative token usage across all completed turns |
478
+ | `handover` | `{ isFinal: boolean } \| null` | The [`chat.headStart`](/ai-chat/fast-starts#handover-with-custom-agents) handover for this turn (turn 0 only); `null` otherwise |
479
+
480
+ | Method | Returns | Description |
481
+ | ------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------ |
482
+ | `complete(source?)` | `Promise<UIMessage \| undefined>` | Pipe, capture, accumulate, cleanup, and signal turn-complete. Call with no source on a final head-start handover (`handover.isFinal`), where the warm partial is already the response |
483
+ | `done()` | `Promise<void>` | Signal turn-complete (when you've piped manually) |
484
+ | `addResponse(response)` | `Promise<void>` | Add response to accumulator manually |
485
+ | `setMessages(uiMessages)`| `Promise<void>` | Replace the accumulated messages (continuation seeding, compaction) |
486
+ | `prepareStep()` | `function \| undefined` | `prepareStep` callback wiring compaction + injection — pass to `streamText` when not using `chat.toStreamTextOptions()` |
487
+
488
+ ## HeadStartHandlerOptions
489
+
490
+ Options for [`chat.headStart()`](/ai-chat/fast-starts#head-start), the warm-server first-turn handler (`@trigger.dev/sdk/chat-server`).
491
+
492
+ | Option | Type | Default | Description |
493
+ | ---------------------- | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- |
494
+ | `agentId` | `string` | required | The `chat.agent` / `chat.customAgent` id to hand off to |
495
+ | `run` | `(args: HeadStartRunArgs) => Promise<StreamTextResult>` | required | First-turn callback. Call `streamText` and spread `chat.toStreamTextOptions({ tools })` |
496
+ | `idleTimeoutInSeconds` | `number` | `60` | How long the agent waits for the handover signal |
497
+ | `triggerConfig` | `Partial<SessionTriggerConfig>` | `undefined` | Run options (tags, queue, machine, maxAttempts, maxDuration, region, lockToVersion) for the auto-triggered handover-prepare run. The `chat:{chatId}` tag is prepended automatically |
498
+
499
+ `chat.headStart(options)` returns the handler `(req: Request) => Promise<Response>`. The `run` callback receives `HeadStartRunArgs`: `{ messages: UIMessage[], signal: AbortSignal, chat: HeadStartChatHelper }`, where the helper exposes `chat.toStreamTextOptions({ tools })` and a `chat.session` escape hatch. See [Head Start](/ai-chat/fast-starts#head-start) for the full guide.
500
+
501
+ ## chat namespace
502
+
503
+ All methods available on the `chat` object from `@trigger.dev/sdk/ai`.
504
+
505
+ | Method | Description |
506
+ | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
507
+ | `chat.agent(options)` | Create a chat agent |
508
+ | `chat.createSession(payload, options)` | Create an async iterator for chat turns |
509
+ | `chat.pipe(source, options?)` | Pipe a stream to the frontend (from anywhere inside a task) |
510
+ | `chat.pipeAndCapture(source, options?)` | Pipe and capture the response `UIMessage` |
511
+ | `chat.writeTurnComplete(options?)` | Signal the frontend that the current turn is complete |
512
+ | `chat.createStopSignal()` | Create a managed stop signal wired to the stop input stream |
513
+ | `chat.messages` | Input stream for incoming messages — use `.waitWithIdleTimeout()` |
514
+ | `chat.local<T>({ id })` | Create a per-run typed local (see [`chat.local`](/ai-chat/chat-local)) |
515
+ | `chat.createStartSessionAction(taskId, options?)` | Returns a server action that creates a chat Session + triggers the first run + returns a session-scoped PAT. Idempotent on `(env, externalId)`. |
516
+ | `chat.waitForHandover(options)` | Wait for a [`chat.headStart`](/ai-chat/fast-starts#handover-with-custom-agents) handover signal in a custom loop. Returns the signal or `null`. `chat.MessageAccumulator` wraps this as `consumeHandover()` / `applyHandover()` |
517
+ | `chat.requestUpgrade()` | End the current run after this turn so the next message starts on the latest agent version. Server-orchestrated handoff. |
518
+ | `chat.setTurnTimeout(duration)` | Override turn timeout at runtime (e.g. `"2h"`) |
519
+ | `chat.setTurnTimeoutInSeconds(seconds)` | Override turn timeout at runtime (in seconds) |
520
+ | `chat.setIdleTimeoutInSeconds(seconds)` | Override idle timeout at runtime |
521
+ | `chat.setUIMessageStreamOptions(options)` | Override `toUIMessageStream()` options for the current turn |
522
+ | `chat.defer(promise)` | Run background work in parallel with streaming, awaited before `onTurnComplete` |
523
+ | `chat.isStopped()` | Check if the current turn was stopped by the user |
524
+ | `chat.cleanupAbortedParts(message)` | Remove incomplete parts from a stopped response message |
525
+ | `chat.response.write(chunk)` | Write a data part that streams to the frontend AND persists in `onTurnComplete`'s `responseMessage` |
526
+ | `chat.stream` | Raw chat output stream — use `.writer()`, `.pipe()`, `.append()`, `.read()`. Chunks are NOT accumulated into the response. |
527
+ | `chat.history.all()` | Read the current accumulated UI messages (returns a copy). See [chat.history](/ai-chat/backend#chat-history) |
528
+ | `chat.history.set(messages)` | Replace all accumulated messages (same as `chat.setMessages()`) |
529
+ | `chat.history.remove(messageId)` | Remove a specific message by ID |
530
+ | `chat.history.rollbackTo(messageId)` | Keep messages up to and including the given ID (undo/rollback) |
531
+ | `chat.history.replace(messageId, message)` | Replace a specific message by ID (edit) |
532
+ | `chat.history.slice(start, end?)` | Keep only messages in the given range |
533
+ | `chat.MessageAccumulator` | Class that accumulates conversation messages across turns |
534
+ | `chat.withUIMessage(config?)` | Returns a [ChatBuilder](/ai-chat/types#chatbuilder) with a fixed `UIMessage` subtype. See [Types](/ai-chat/types) |
535
+ | `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) |
536
+
537
+ ## `chat.withUIMessage`
538
+
539
+ Returns a [`ChatBuilder`](/ai-chat/types#chatbuilder) with a fixed `UIMessage` subtype. Chain `.withClientData()`, hook methods, and `.agent()`.
540
+
541
+ ```ts
542
+ chat.withUIMessage<TUIM>(config?: ChatWithUIMessageConfig<TUIM>): ChatBuilder<TUIM>;
543
+ ```
544
+
545
+ | Parameter | Type | Description |
546
+ | ---------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
547
+ | `config.streamOptions` | `ChatUIMessageStreamOptions<TUIM>` | Optional defaults for `toUIMessageStream()`. Shallow-merged with `uiMessageStreamOptions` on the inner `.agent({ ... })` (agent wins on key conflicts). |
548
+
549
+ Use this when you need [`InferChatUIMessage`](#inferchatuimessage) / typed `data-*` parts / `InferUITools` to line up across backend hooks and `useChat`. Full guide: [Types](/ai-chat/types).
550
+
551
+ ## `chat.withClientData`
552
+
553
+ Returns a [`ChatBuilder`](/ai-chat/types#chatbuilder) with a fixed client data schema. All hooks and `run` get typed `clientData` without passing `clientDataSchema` in `.agent()` options.
554
+
555
+ ```ts
556
+ chat.withClientData<TSchema>({ schema: TSchema }): ChatBuilder<UIMessage, TSchema>;
557
+ ```
558
+
559
+ | Parameter | Type | Description |
560
+ | --------- | ------------ | -------------------------------------------------- |
561
+ | `schema` | `TaskSchema` | Zod, ArkType, Valibot, or any supported schema lib |
562
+
563
+ Full guide: [Typed client data](/ai-chat/types#typed-client-data-with-chatwithclientdata).
564
+
565
+ ## `ChatWithUIMessageConfig`
566
+
567
+ | Field | Type | Description |
568
+ | --------------- | ---------------------------------- | --------------------------------------------------------------------- |
569
+ | `streamOptions` | `ChatUIMessageStreamOptions<TUIM>` | Default `toUIMessageStream()` options for agents created via `.agent()` |
570
+
571
+ ## `InferChatUIMessage`
572
+
573
+ Type helper: extracts the `UIMessage` subtype from a chat agent’s wire payload.
574
+
575
+ ```ts
576
+ import type { InferChatUIMessage } from "@trigger.dev/sdk/ai";
577
+ // Use the /chat/react re-export when you're already importing other React helpers.
578
+
579
+ type Msg = InferChatUIMessage<typeof myChat>;
580
+ ```
581
+
582
+ Use with `useChat<Msg>({ transport })` when using [`chat.withUIMessage`](/ai-chat/types). For agents defined with plain `chat.agent()` (no custom generic), this resolves to the base `UIMessage`.
583
+
584
+ ## `InferChatUIMessageFromTools`
585
+
586
+ Type helper: derives the chat `UIMessage` type (with typed `tool-${name}` parts) directly from a tool set. Shorthand for `UIMessage<unknown, UIDataTypes, InferUITools<typeof tools>>`.
587
+
588
+ ```ts
589
+ import type { InferChatUIMessageFromTools } from "@trigger.dev/sdk/ai";
590
+
591
+ const tools = { search, readFile };
592
+ type ChatUiMessage = InferChatUIMessageFromTools<typeof tools>;
593
+ ```
594
+
595
+ Pin it on the agent with [`chat.withUIMessage<ChatUiMessage>()`](/ai-chat/types) and reuse it on the client. See [Tools](/ai-chat/tools#typing-messages-from-your-tools).
596
+
597
+ ## AI helpers (`ai` from `@trigger.dev/sdk/ai`)
598
+
599
+ | Export | Status | Description |
600
+ | ----------------------------------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
601
+ | `ai.toolExecute(task)` | **Preferred** | Returns the `execute` function for AI SDK `tool()`. Runs the task via `triggerAndSubscribe` and attaches tool/chat metadata (same behavior the deprecated wrapper used internally). |
602
+ | `ai.tool(task, options?)` | **Deprecated** | Wraps `tool()` / `dynamicTool()` and the same execute path. Migrate to `tool({ ..., execute: ai.toolExecute(task) })`. See [Task-backed AI tools](/tasks/schemaTask#task-backed-ai-tools). |
603
+ | `ai.toolCallId`, `ai.chatContext`, `ai.chatContextOrThrow`, `ai.currentToolOptions` | Supported | Work for any task-backed tool execute path, including `ai.toolExecute`. |
604
+
605
+ ## ChatUIMessageStreamOptions
606
+
607
+ Options for customizing `toUIMessageStream()`. Set as static defaults via `uiMessageStreamOptions` on `chat.agent()`, or override per-turn via `chat.setUIMessageStreamOptions()`. See [Stream options](/ai-chat/backend#stream-options) for usage examples.
608
+
609
+ Derived from the AI SDK's `UIMessageStreamOptions` with `onFinish` and `originalMessages` omitted (managed internally — `onFinish` for response capture, `originalMessages` for cross-turn message ID reuse).
610
+
611
+ | Option | Type | Default | Description |
612
+ | ------------------- | --------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
613
+ | `onError` | `(error: unknown) => string` | Raw error message | Called on LLM errors and tool execution errors. Return a sanitized string — sent as `{ type: "error", errorText }` to the frontend. |
614
+ | `sendReasoning` | `boolean` | `true` | Send reasoning parts to the client |
615
+ | `sendSources` | `boolean` | `false` | Send source parts to the client |
616
+ | `sendFinish` | `boolean` | `true` | Send the finish event. Set to `false` when chaining multiple `streamText` calls. |
617
+ | `sendStart` | `boolean` | `true` | Send the message start event. Set to `false` when chaining. |
618
+ | `messageMetadata` | `(options: { part }) => metadata` | — | Extract message metadata to send to the client. Called on `start` and `finish` events. |
619
+ | `generateMessageId` | `() => string` | AI SDK's `generateId` | Custom message ID generator for response messages (e.g. UUID-v7). IDs are shared between frontend and backend via the stream's `start` chunk. |
620
+
621
+ ## TriggerChatTransport options
622
+
623
+ Options for the frontend transport constructor and `useTriggerChatTransport` hook.
624
+
625
+ | Option | Type | Default | Description |
626
+ | ---------------------- | -------------------------------------------------------------------- | --------------------------- | --------------------------------------------------------------------------- |
627
+ | `task` | `string` | required | Task ID the transport's session is bound to. Threaded into `startSession`'s params. |
628
+ | `accessToken` | `(params: AccessTokenParams) => string \| Promise<string>` | required | Pure refresh — mints a fresh session-scoped PAT. Called on 401/403. See [callback shape](#accesstoken-callback). |
629
+ | `startSession` | `(params: StartSessionParams<TClientData>) => Promise<StartSessionResult>` | optional | Creates the chat Session and returns the session-scoped PAT. Called on `transport.preload(chatId)` and lazily on the first `sendMessage` for any chatId without a cached PAT. See [callback shape](#startsession-callback). |
630
+ | `baseURL` | `string \| (ctx: { endpoint: "in" \| "out"; chatId: string }) => string` | `"https://api.trigger.dev"` | API base URL. String form applies to every endpoint; function form lets you pick per endpoint — e.g. route `.in/append` through a trusted edge proxy while keeping `.out` SSE direct (see [Trusted edge signals](/ai-chat/patterns/trusted-edge-signals)). |
631
+ | `fetch` | `(url: string, init: RequestInit, ctx: { endpoint: "in" \| "out"; chatId: string }) => Promise<Response>` | — | Per-request fetch override. Invoked for both `.in/append` POSTs and the `.out` SSE GET. Use for header injection (tracing), custom retries, or proxy rewrites beyond what `baseURL` can express. |
632
+ | `headers` | `Record<string, string>` | — | Extra headers for API requests |
633
+ | `streamTimeoutSeconds` | `number` | `120` | How long to wait for stream data |
634
+ | `clientData` | Typed by `clientDataSchema` | — | Default client data merged into per-turn `metadata` and threaded through `startSession`'s params (so the first run's `payload.metadata` matches per-turn `metadata`). Live-updated when the option value changes. |
635
+ | `sessions` | `Record<string, ChatSession>` | — | Restore sessions from storage. See [ChatSession](#chatsession). |
636
+ | `onSessionChange` | `(chatId, session \| null) => void` | — | Fires when session state changes. `session` is the full `ChatSession` or `null` when the run ends. |
637
+ | `multiTab` | `boolean` | `false` | Enable multi-tab claim coordination via `BroadcastChannel`. See [Frontend → multi-tab](/ai-chat/frontend#multi-tab-coordination). |
638
+ | `watch` | `boolean` | `false` | Read-only watcher mode — keep the SSE subscription open across `trigger:turn-complete` so a viewer sees turns 2, 3, … through one long-lived stream. |
639
+ | `headStart` | `string` | — | URL of a [`chat.headStart`](/ai-chat/fast-starts#head-start) route handler. When set, the FIRST message of a brand-new chat POSTs to this URL so step 1's LLM call runs in your warm process while the agent run boots in parallel. Subsequent turns bypass it. |
640
+
641
+ ### `accessToken` callback
642
+
643
+ The transport invokes `accessToken` whenever it needs a *fresh* session-scoped PAT — initial use after no PAT is cached, or after a 401/403 from any session-PAT-authed request. The callback's job is to **return a token, not to start a run.**
644
+
645
+ `AccessTokenParams`:
646
+
647
+ | Field | Type | Description |
648
+ | --- | --- | --- |
649
+ | `chatId` | `string` | The conversation id. |
650
+
651
+ Customer implementation typically wraps `auth.createPublicToken` server-side:
652
+
653
+ ```ts
654
+ "use server";
655
+ import { auth } from "@trigger.dev/sdk";
656
+
657
+ export async function mintChatAccessToken(chatId: string) {
658
+ return auth.createPublicToken({
659
+ scopes: { read: { sessions: chatId }, write: { sessions: chatId } },
660
+ expirationTime: "1h",
661
+ });
662
+ }
663
+ ```
664
+
665
+ ```ts
666
+ const transport = useTriggerChatTransport({
667
+ task: "my-chat",
668
+ accessToken: ({ chatId }) => mintChatAccessToken(chatId),
669
+ });
670
+ ```
671
+
672
+ ### `startSession` callback
673
+
674
+ The transport invokes `startSession` when it needs to *create* the session — on `transport.preload(chatId)`, and lazily on the first `sendMessage` for any chatId without a cached PAT. Concurrent and repeat calls dedupe via an in-flight promise, and the customer's wrapped helper is idempotent on `(env, externalId)` so two tabs / two `preload` calls converge on the same session.
675
+
676
+ `StartSessionParams<TClientData>`:
677
+
678
+ | Field | Type | Description |
679
+ | --- | --- | --- |
680
+ | `taskId` | `string` | The transport's `task` value. |
681
+ | `chatId` | `string` | The conversation id (the session's `externalId`). |
682
+ | `clientData` | `TClientData` | The transport's current `clientData` option. Pass through to `triggerConfig.basePayload.metadata` so the first run's `payload.metadata` matches per-turn `metadata`. |
683
+
684
+ Customer implementation wraps `chat.createStartSessionAction(taskId)`:
685
+
686
+ ```ts
687
+ "use server";
688
+ import { chat } from "@trigger.dev/sdk/ai";
689
+
690
+ export const startChatSession = chat.createStartSessionAction("my-chat");
691
+ ```
692
+
693
+ ```ts
694
+ const transport = useTriggerChatTransport({
695
+ task: "my-chat",
696
+ startSession: ({ chatId, clientData }) =>
697
+ startChatSession({ chatId, clientData }),
698
+ });
699
+ ```
700
+
701
+ `startSession` is optional only when the customer fully manages the session lifecycle externally (e.g. by hydrating `sessions: { [chatId]: ... }` and never calling `preload`). Most customers should provide it.
702
+
703
+ ### multiTab
704
+
705
+ Enable multi-tab coordination. When `true`, only one browser tab can send messages to a given chatId at a time. Other tabs enter read-only mode with real-time message updates via `BroadcastChannel`.
706
+
707
+ ```ts
708
+ const transport = useTriggerChatTransport({
709
+ task: "my-chat",
710
+ accessToken,
711
+ multiTab: true,
712
+ });
713
+ ```
714
+
715
+ No-op when `BroadcastChannel` is unavailable (SSR, Node.js). See [Multi-tab coordination](/ai-chat/frontend#multi-tab-coordination).
716
+
717
+ ### Trigger configuration
718
+
719
+ Trigger config (machine, queue, tags, maxAttempts, idleTimeoutInSeconds) lives server-side in `chat.createStartSessionAction(taskId, options?)`. The transport doesn't accept these options directly — pass them when wrapping the action:
720
+
721
+ ```ts
722
+ "use server";
723
+ import { chat } from "@trigger.dev/sdk/ai";
724
+
725
+ export const startChatSession = chat.createStartSessionAction("my-chat", {
726
+ triggerConfig: {
727
+ machine: "small-1x",
728
+ queue: "chat-queue",
729
+ tags: ["user:123"],
730
+ maxAttempts: 3,
731
+ idleTimeoutInSeconds: 60,
732
+ },
733
+ });
734
+ ```
735
+
736
+ A `chat:{chatId}` tag is automatically added to every run.
737
+
738
+ For per-call values that vary by chatId (e.g. plan-tier-driven machine), accept extra params on the customer's server action and pass them into `chat.createStartSessionAction(...)`'s options at call time.
739
+
740
+ ### transport.stopGeneration()
741
+
742
+ Stop the current generation for a chat session. Sends a stop signal to the backend task and closes the active SSE connection.
743
+
744
+ ```ts
745
+ transport.stopGeneration(chatId: string): Promise<boolean>
746
+ ```
747
+
748
+ Returns `true` if the stop signal was sent, `false` if there's no active session. Works for both initial connections and reconnected streams (after page refresh with `resume: true`).
749
+
750
+ Use alongside `useChat`'s `stop()` for a complete stop experience:
751
+
752
+ ```tsx
753
+ const { stop: aiStop } = useChat({ transport });
754
+
755
+ const stop = useCallback(() => {
756
+ transport.stopGeneration(chatId);
757
+ aiStop();
758
+ }, [transport, chatId, aiStop]);
759
+ ```
760
+
761
+ See [Stop generation](/ai-chat/frontend#stop-generation) for full details.
762
+
763
+ ### transport.sendAction()
764
+
765
+ Send a custom action to the agent. Actions wake the agent from suspension and fire `onAction`. They are not turns — `run()` and turn lifecycle hooks do not fire. If `onAction` returns a `StreamTextResult`, the response is auto-piped to the frontend.
766
+
767
+ ```ts
768
+ transport.sendAction(chatId: string, action: unknown): Promise<ReadableStream<UIMessageChunk>>
769
+ ```
770
+
771
+ The action payload is validated against the agent's `actionSchema` on the backend.
772
+
773
+ ```tsx
774
+ // Undo button
775
+ <button onClick={() => transport.sendAction(chatId, { type: "undo" })}>
776
+ Undo
777
+ </button>
778
+ ```
779
+
780
+ See [Actions](/ai-chat/actions) for backend setup and [Sending actions](/ai-chat/frontend#sending-actions) for frontend usage.
781
+
782
+ ### transport.preload()
783
+
784
+ Eagerly trigger a run before the first message.
785
+
786
+ ```ts
787
+ transport.preload(chatId): Promise<void>
788
+ ```
789
+
790
+ No-op if a session already exists for this chatId. The preload idle window is set by `preloadIdleTimeoutInSeconds` on the agent, not by this call. See [Preload](/ai-chat/fast-starts#preload) for full details.
791
+
792
+ ## useTriggerChatTransport
793
+
794
+ React hook that creates and memoizes a `TriggerChatTransport` instance. Import from `@trigger.dev/sdk/chat/react`.
795
+
796
+ ```tsx
797
+ import { useTriggerChatTransport } from "@trigger.dev/sdk/chat/react";
798
+ import type { myChat } from "@/trigger/chat";
799
+
800
+ const transport = useTriggerChatTransport<typeof myChat>({
801
+ task: "my-chat",
802
+ accessToken: ({ chatId }) => mintChatAccessToken(chatId),
803
+ startSession: ({ chatId, clientData }) =>
804
+ startChatSession({ chatId, clientData }),
805
+ sessions: savedSessions,
806
+ onSessionChange: handleSessionChange,
807
+ });
808
+ ```
809
+
810
+ The transport is created once on first render and reused across re-renders. Pass a type parameter for compile-time validation of the task ID.
811
+
812
+ ## AgentChat options
813
+
814
+ Options for the server-side chat client constructor. Import `AgentChat` from `@trigger.dev/sdk/chat`.
815
+
816
+ | Option | Type | Default | Description |
817
+ | ---------------------- | -------------------------------------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------- |
818
+ | `agent` | `string` | required | Task ID of the chat agent to converse with. |
819
+ | `id` | `string` | `crypto.randomUUID()` | Conversation ID. Used as the Session `externalId` and for tagging runs. |
820
+ | `clientData` | Typed by `clientDataSchema` | — | Client data included in every request. Same shape as the agent's `clientDataSchema`. |
821
+ | `session` | `ChatSession` | — | Restore a previous session (pass `lastEventId` to resume SSE). |
822
+ | `triggerConfig` | `Partial<SessionTriggerConfig>` | — | Default trigger config used when starting a new session (machine, tags, etc.). |
823
+ | `streamTimeoutSeconds` | `number` | `120` | SSE timeout in seconds. |
824
+ | `onTriggered` | `(event) => void \| Promise<void>` | — | Fires when a new run is triggered for this session. |
825
+ | `onTurnComplete` | `(event) => void \| Promise<void>` | — | Fires when a turn completes. Persist `event.lastEventId` for stream resumption. |
826
+ | `baseURL` | `string \| (ctx: { endpoint: "in" \| "out"; chatId: string }) => string` | `apiClientManager.baseURL` | API base URL. String form applies to every endpoint; function form picks per endpoint. Defaults to whatever `@trigger.dev/sdk` was configured with (typically `TRIGGER_API_URL`). |
827
+ | `fetch` | `(url: string, init: RequestInit, ctx: { endpoint: "in" \| "out"; chatId: string }) => Promise<Response>` | — | Per-request fetch override. Invoked for both `.in/append` POSTs and the `.out` SSE GET. Use for header injection, custom retries, or proxy rewrites. |
828
+
829
+ ## createStartSessionAction options
830
+
831
+ Second argument to `chat.createStartSessionAction(taskId, options?)`. Controls how the server-mediated session-create call reaches the trigger.dev API.
832
+
833
+ | Option | Type | Default | Description |
834
+ | ---------------- | ------------------------------------------------------------------------------------ | ----------------------------- | --------------------------------------------------------------------------- |
835
+ | `tokenTTL` | `string \| number \| Date` | `"1h"` | TTL for the session-scoped public access token returned to the browser. |
836
+ | `triggerConfig` | `Partial<SessionTriggerConfig>` | — | Default trigger config (machine, tags, queue, etc.). Per-call config shallow-merges on top. |
837
+ | `baseURL` | `string \| (ctx: { endpoint: "sessions" \| "auth"; chatId: string }) => string` | `apiClientManager.baseURL` | API base URL. `endpoint` is `"sessions"` for `POST /api/v1/sessions` or `"auth"` for `POST /api/v1/auth/jwt/claims` (only fires when `tokenTTL` is set). |
838
+ | `fetch` | `(url: string, init: RequestInit, ctx: { endpoint: "sessions" \| "auth"; chatId: string }) => Promise<Response>` | — | Per-request fetch override. Use to route session-create through a trusted edge proxy so `basePayload.metadata` is rewritten before reaching `api.trigger.dev`. |
839
+
840
+ ## useMultiTabChat
841
+
842
+ React hook for multi-tab message coordination. Import from `@trigger.dev/sdk/chat/react`.
843
+
844
+ ```tsx
845
+ import { useMultiTabChat } from "@trigger.dev/sdk/chat/react";
846
+
847
+ const { isReadOnly } = useMultiTabChat(transport, chatId, messages, setMessages);
848
+ ```
849
+
850
+ | Parameter | Type | Description |
851
+ |-----------|------|-------------|
852
+ | `transport` | `TriggerChatTransport` | Transport instance with `multiTab: true` |
853
+ | `chatId` | `string` | The chat session ID |
854
+ | `messages` | `UIMessage[]` | Current messages from `useChat` |
855
+ | `setMessages` | `(messages) => void` | Message setter from `useChat` |
856
+
857
+ **Returns:** `{ isReadOnly: boolean }` — `true` when another tab is actively sending to this chatId.
858
+
859
+ The hook handles:
860
+ - Tracking read-only state from the transport's `BroadcastChannel` coordinator
861
+ - Broadcasting messages when this tab is the active sender
862
+ - Receiving messages from other tabs and updating via `setMessages`
863
+
864
+ See [Multi-tab coordination](/ai-chat/frontend#multi-tab-coordination).
865
+
866
+ ## ChatSession
867
+
868
+ Persistable session state for the frontend `TriggerChatTransport` and the server-side `AgentChat`. The underlying Session row is keyed on `chatId` (durable across runs); the persistable shape is just the SSE resume cursor and a refresh token.
869
+
870
+ | Field | Type | Description |
871
+ | --- | --- | --- |
872
+ | `publicAccessToken` | `string` | Session-scoped JWT (`read:sessions:{chatId} + write:sessions:{chatId}`). Refreshed automatically on 401/403 via the transport's `accessToken` callback. |
873
+ | `lastEventId` | `string \| undefined` | Last SSE event received on `.out`. Used to resume mid-stream after a disconnect. |
874
+ | `isStreaming` | `boolean \| undefined` | Optional. If persisted, `reconnectToStream` uses it as a fast-path short-circuit. If omitted, the server decides via the session's [`X-Session-Settled`](/ai-chat/client-protocol#x-session-settled-fast-close-on-idle-reconnects) response header. |
875
+
876
+ ## ChatInputChunk
877
+
878
+ The wire shape for records sent on `.in`. Consumed by `chat.agent` internally — you typically don't write these yourself; `transport.sendMessage`, `transport.stopGeneration`, and `transport.sendAction` all serialize into this shape.
879
+
880
+ ```ts
881
+ type ChatInputChunk<TMessage = UIMessage, TMetadata = unknown> =
882
+ | { kind: "message"; payload: ChatTaskWirePayload<TMessage, TMetadata> }
883
+ | { kind: "stop"; message?: string };
884
+ ```
885
+
886
+ | Variant | When | Payload |
887
+ | --- | --- | --- |
888
+ | `kind: "message"` | New message, action, approval response, or close | `payload` is a full `ChatTaskWirePayload` — its `trigger` field (`"submit-message"` / `"action"` / `"close"`) determines the agent's dispatch |
889
+ | `kind: "stop"` | Client aborted the active turn | Optional `message` surfaces in the stop handler |
890
+
891
+ For the raw wire format, see [Client Protocol — ChatInputChunk](/ai-chat/client-protocol#chatinputchunk).
892
+
893
+ ## Session token scopes
894
+
895
+ Tokens minted for `TriggerChatTransport` and `AgentChat` are session-scoped — keyed on the chat's `externalId` (the `chatId` you assign).
896
+
897
+ | Scope | Grants |
898
+ | --- | --- |
899
+ | `read:sessions:<chatId>` | Subscribe to `.out`, HEAD probe the stream, retrieve the session row |
900
+ | `write:sessions:<chatId>` | Append to `.in`, close the session, end-and-continue, update metadata |
901
+
902
+ Tokens are produced by `auth.createPublicToken({ scopes: { read: { sessions: chatId }, write: { sessions: chatId } } })` (used by the customer's `accessToken` server action) or returned automatically from `chat.createStartSessionAction` / `POST /api/v1/sessions`. Either form authorizes both URL forms (`/sessions/{chatId}/...` and `/sessions/session_*/...`) on every read and write route.
903
+
904
+ ## Related
905
+
906
+ - [Realtime Streams](/tasks/streams) — How streams work under the hood
907
+ - [Using the Vercel AI SDK](/guides/examples/vercel-ai-sdk) — Basic AI SDK usage with Trigger.dev
908
+ - [Realtime React Hooks](/realtime/react-hooks/overview) — Lower-level realtime hooks
909
+ - [Authentication](/realtime/auth) — Public access tokens and trigger tokens