Documentation Index
Fetch the complete documentation index at: https://trigger-docs-tri-7532-ai-sdk-chat-transport-and-chat-task-s.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
ChatAgentOptions
Options forchat.agent().
| Option | Type | Default | Description |
|---|---|---|---|
id | string | required | Task identifier |
run | (payload: ChatTaskRunPayload) => Promise<unknown> | required | Handler for each turn |
clientDataSchema | TaskSchema | — | Schema for validating and typing clientData |
onPreload | (event: PreloadEvent) => Promise<void> | void | — | Fires on preloaded runs before the first message |
onChatStart | (event: ChatStartEvent) => Promise<void> | void | — | Fires on turn 0 before run() |
onValidateMessages | (event: ValidateMessagesEvent) => UIMessage[] | Promise<UIMessage[]> | — | Validate/transform UIMessages before model conversion. See onValidateMessages |
hydrateMessages | (event: HydrateMessagesEvent) => UIMessage[] | Promise<UIMessage[]> | — | Load message history from backend, replacing the linear accumulator. See hydrateMessages |
actionSchema | TaskSchema | — | Schema for validating custom actions sent via transport.sendAction(). See Actions |
onAction | (event: ActionEvent) => Promise<void> | void | — | Handle custom actions. Fires after hydration, before onTurnStart. See Actions |
onTurnStart | (event: TurnStartEvent) => Promise<void> | void | — | Fires every turn before run() |
onBeforeTurnComplete | (event: BeforeTurnCompleteEvent) => Promise<void> | void | — | Fires after response but before stream closes. Includes writer. |
onTurnComplete | (event: TurnCompleteEvent) => Promise<void> | void | — | Fires after each turn completes (stream closed) |
onCompacted | (event: CompactedEvent) => Promise<void> | void | — | Fires when compaction occurs. Includes writer. See Compaction |
compaction | ChatAgentCompactionOptions | — | Automatic context compaction. See Compaction |
pendingMessages | PendingMessagesOptions | — | Mid-execution message injection. See Pending Messages |
prepareMessages | (event: PrepareMessagesEvent) => ModelMessage[] | — | Transform model messages before use (cache breaks, context injection, etc.) |
maxTurns | number | 100 | Max conversational turns per run |
turnTimeout | string | "1h" | How long to wait for next message |
idleTimeoutInSeconds | number | 30 | Seconds to stay idle before suspending |
chatAccessTokenTTL | string | "1h" | How long the scoped access token remains valid |
preloadIdleTimeoutInSeconds | number | Same as idleTimeoutInSeconds | Idle timeout after onPreload fires |
preloadTimeout | string | Same as turnTimeout | Suspend timeout for preloaded runs |
uiMessageStreamOptions | ChatUIMessageStreamOptions | — | Default options for toUIMessageStream(). Per-turn override via chat.setUIMessageStreamOptions() |
onChatSuspend | (event: ChatSuspendEvent) => Promise<void> | void | — | Fires right before the run suspends. See onChatSuspend |
onChatResume | (event: ChatResumeEvent) => Promise<void> | void | — | Fires right after the run resumes from suspension |
exitAfterPreloadIdle | boolean | false | Exit run after preload idle timeout instead of suspending. See exitAfterPreloadIdle |
retry, queue, machine, maxDuration, onWait, onResume, onComplete, and other lifecycle hooks. Those hooks use the same parameter shapes as on a normal task() (including ctx).
Task context (ctx)
All chat.agent lifecycle events (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 }) => ... }).
onValidateMessages does not include ctx — it fires before message accumulation and is designed for pure validation/transformation of incoming messages.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.
Prefer
import type { TaskRunContext } from "@trigger.dev/sdk" in application code. Do not depend on @trigger.dev/core directly.ChatTaskRunPayload
The payload passed to therun function.
| Field | Type | Description |
|---|---|---|
ctx | TaskRunContext | Full task run context — same as task run’s { ctx } |
messages | ModelMessage[] | Model-ready messages — pass directly to streamText |
chatId | string | Your conversation ID (the session’s externalId) |
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. |
trigger | "submit-message" | "regenerate-message" | What triggered the request |
messageId | string | undefined | Message ID (for regenerate) |
clientData | Typed by clientDataSchema | Custom data from the frontend (typed when schema is provided) |
continuation | boolean | Whether this run is continuing an existing chat (previous run ended) |
signal | AbortSignal | Combined stop + cancel signal |
cancelSignal | AbortSignal | Cancel-only signal |
stopSignal | AbortSignal | Stop-only signal (per-turn) |
previousTurnUsage | LanguageModelUsage | undefined | Token usage from the previous turn (undefined on turn 0) |
totalUsage | LanguageModelUsage | Cumulative token usage across completed turns so far |
PreloadEvent
Passed to theonPreload callback.
| Field | Type | Description |
|---|---|---|
ctx | TaskRunContext | Full task run context — see Task context |
chatId | string | Chat session ID |
runId | string | The Trigger.dev run ID |
chatAccessToken | string | Scoped access token for this run |
clientData | Typed by clientDataSchema | Custom data from the frontend |
writer | ChatWriter | Stream writer for custom chunks. Lazy — no overhead if unused. |
ChatStartEvent
Passed to theonChatStart callback.
| Field | Type | Description |
|---|---|---|
ctx | TaskRunContext | Full task run context — see Task context |
chatId | string | Chat session ID |
messages | ModelMessage[] | Initial model-ready messages |
clientData | Typed by clientDataSchema | Custom data from the frontend |
runId | string | The Trigger.dev run ID |
chatAccessToken | string | Scoped access token for this run |
continuation | boolean | Whether this run is continuing an existing chat |
previousRunId | string | undefined | Previous run ID (only when continuation is true) |
preloaded | boolean | Whether this run was preloaded before the first message |
writer | ChatWriter | Stream writer for custom chunks. Lazy — no overhead if unused. |
ValidateMessagesEvent
Passed to theonValidateMessages callback.
| Field | Type | Description |
|---|---|---|
messages | UIMessage[] | Incoming UI messages for this turn |
chatId | string | Chat session ID |
turn | number | Turn number (0-indexed) |
trigger | "submit-message" | "regenerate-message" | "preload" | "close" | The trigger type for this turn |
HydrateMessagesEvent
Passed to thehydrateMessages callback. See hydrateMessages.
| Field | Type | Description |
|---|---|---|
chatId | string | Chat session ID |
turn | number | Turn number (0-indexed) |
trigger | "submit-message" | "regenerate-message" | "action" | The trigger type for this turn |
incomingMessages | UIMessage[] | Validated wire messages from the frontend (empty for actions) |
previousMessages | UIMessage[] | Accumulated UI messages before this turn ([] on turn 0) |
clientData | Typed by clientDataSchema | Custom data from the frontend |
continuation | boolean | Whether this run is continuing an existing chat |
previousRunId | string | undefined | Previous run ID (only when continuation is true) |
ActionEvent
Passed to theonAction callback. See Actions.
| Field | Type | Description |
|---|---|---|
action | Typed by actionSchema | The parsed and validated action payload |
chatId | string | Chat session ID |
turn | number | Turn number (0-indexed) |
clientData | Typed by clientDataSchema | Custom data from the frontend |
uiMessages | UIMessage[] | Accumulated UI messages (after hydration, if set) |
messages | ModelMessage[] | Accumulated model messages (after hydration, if set) |
TurnStartEvent
Passed to theonTurnStart callback.
| Field | Type | Description |
|---|---|---|
ctx | TaskRunContext | Full task run context — see Task context |
chatId | string | Chat session ID |
messages | ModelMessage[] | Full accumulated conversation (model format) |
uiMessages | UIMessage[] | Full accumulated conversation (UI format) |
turn | number | Turn number (0-indexed) |
runId | string | The Trigger.dev run ID |
chatAccessToken | string | Scoped access token for this run |
clientData | Typed by clientDataSchema | Custom data from the frontend |
continuation | boolean | Whether this run is continuing an existing chat |
previousRunId | string | undefined | Previous run ID (only when continuation is true) |
preloaded | boolean | Whether this run was preloaded |
writer | ChatWriter | Stream writer for custom chunks. Lazy — no overhead if unused. |
TurnCompleteEvent
Passed to theonTurnComplete callback.
| Field | Type | Description |
|---|---|---|
ctx | TaskRunContext | Full task run context — see Task context |
chatId | string | Chat session ID |
messages | ModelMessage[] | Full accumulated conversation (model format) |
uiMessages | UIMessage[] | Full accumulated conversation (UI format) |
newMessages | ModelMessage[] | Only this turn’s messages (model format) |
newUIMessages | UIMessage[] | Only this turn’s messages (UI format) |
responseMessage | UIMessage | undefined | The assistant’s response for this turn |
rawResponseMessage | UIMessage | undefined | Raw response before abort cleanup |
turn | number | Turn number (0-indexed) |
runId | string | The Trigger.dev run ID |
chatAccessToken | string | Scoped access token for this run |
lastEventId | string | undefined | Stream position for resumption |
stopped | boolean | Whether the user stopped generation during this turn |
continuation | boolean | Whether this run is continuing an existing chat |
usage | LanguageModelUsage | undefined | Token usage for this turn |
totalUsage | LanguageModelUsage | Cumulative token usage across all turns |
BeforeTurnCompleteEvent
Passed to theonBeforeTurnComplete callback. Same fields as TurnCompleteEvent (including ctx) plus a writer.
| Field | Type | Description |
|---|---|---|
| (all TurnCompleteEvent fields) | See TurnCompleteEvent (includes ctx) | |
writer | ChatWriter | Stream writer — the stream is still open so chunks appear in the current turn |
ChatSuspendEvent
Passed to theonChatSuspend callback. A discriminated union on phase.
| Field | Type | Description |
|---|---|---|
phase | "preload" | "turn" | Whether this is a preload or post-turn suspension |
ctx | TaskRunContext | Full task run context |
chatId | string | Chat session ID |
runId | string | The Trigger.dev run ID |
clientData | Typed by clientDataSchema | Custom data from the frontend |
turn | number | Turn number ("turn" phase only) |
messages | ModelMessage[] | Accumulated model messages ("turn" phase only) |
uiMessages | UIMessage[] | Accumulated UI messages ("turn" phase only) |
ChatResumeEvent
Passed to theonChatResume callback. Same discriminated union shape as ChatSuspendEvent.
| Field | Type | Description |
|---|---|---|
phase | "preload" | "turn" | Whether this is a preload or post-turn resumption |
ctx | TaskRunContext | Full task run context |
chatId | string | Chat session ID |
runId | string | The Trigger.dev run ID |
clientData | Typed by clientDataSchema | Custom data from the frontend |
turn | number | Turn number ("turn" phase only) |
messages | ModelMessage[] | Accumulated model messages ("turn" phase only) |
uiMessages | UIMessage[] | Accumulated UI messages ("turn" phase only) |
ChatWriter
A stream writer passed to lifecycle callbacks. Write customUIMessageChunk parts (e.g. data-* parts) to the chat stream.
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.
| Method | Type | Description |
|---|---|---|
write(part) | (part: UIMessageChunk) => void | Write a single chunk to the chat stream |
merge(stream) | (stream: ReadableStream<UIMessageChunk>) => void | Merge another stream’s chunks into the chat stream |
ChatAgentCompactionOptions
Options for thecompaction field on chat.agent(). See Compaction for usage guide.
| Option | Type | Required | Description |
|---|---|---|---|
shouldCompact | (event: ShouldCompactEvent) => boolean | Promise<boolean> | Yes | Decide whether to compact. Return true to trigger |
summarize | (event: SummarizeEvent) => Promise<string> | Yes | Generate a summary from the current messages |
compactUIMessages | (event: CompactMessagesEvent) => UIMessage[] | Promise<UIMessage[]> | No | Transform UI messages after compaction. Default: preserve all |
compactModelMessages | (event: CompactMessagesEvent) => ModelMessage[] | Promise<ModelMessage[]> | No | Transform model messages after compaction. Default: replace all with summary |
CompactMessagesEvent
Passed tocompactUIMessages and compactModelMessages callbacks.
| Field | Type | Description |
|---|---|---|
summary | string | The generated summary text |
uiMessages | UIMessage[] | Current UI messages (full conversation) |
modelMessages | ModelMessage[] | Current model messages (full conversation) |
chatId | string | Chat session ID |
turn | number | Current turn (0-indexed) |
clientData | unknown | Custom data from the frontend |
source | "inner" | "outer" | Whether compaction is between steps or between turns |
CompactedEvent
Passed to theonCompacted callback.
| Field | Type | Description |
|---|---|---|
ctx | TaskRunContext | Full task run context — see Task context |
summary | string | The generated summary text |
messages | ModelMessage[] | Messages that were compacted (pre-compaction) |
messageCount | number | Number of messages before compaction |
usage | LanguageModelUsage | Token usage from the triggering step/turn |
totalTokens | number | undefined | Total token count that triggered compaction |
inputTokens | number | undefined | Input token count |
outputTokens | number | undefined | Output token count |
stepNumber | number | Step number (-1 for outer loop) |
chatId | string | undefined | Chat session ID |
turn | number | undefined | Current turn |
writer | ChatWriter | Stream writer for custom chunks during compaction |
PendingMessagesOptions
Options for thependingMessages field. See Pending Messages for usage guide.
| Option | Type | Required | Description |
|---|---|---|---|
shouldInject | (event: PendingMessagesBatchEvent) => boolean | Promise<boolean> | No | Decide whether to inject the batch between tool-call steps. If absent, no injection. |
prepare | (event: PendingMessagesBatchEvent) => ModelMessage[] | Promise<ModelMessage[]> | No | Transform the batch before injection. Default: convert each via convertToModelMessages. |
onReceived | (event: PendingMessageReceivedEvent) => void | Promise<void> | No | Called when a message arrives during streaming (per-message). |
onInjected | (event: PendingMessagesInjectedEvent) => void | Promise<void> | No | Called after a batch is injected via prepareStep. |
PendingMessagesBatchEvent
Passed toshouldInject and prepare callbacks.
| Field | Type | Description |
|---|---|---|
messages | UIMessage[] | All pending messages (batch) |
modelMessages | ModelMessage[] | Current conversation |
steps | CompactionStep[] | Completed steps so far |
stepNumber | number | Current step (0-indexed) |
chatId | string | Chat session ID |
turn | number | Current turn (0-indexed) |
clientData | unknown | Custom data from the frontend |
PendingMessagesInjectedEvent
Passed toonInjected callback.
| Field | Type | Description |
|---|---|---|
messages | UIMessage[] | All injected UI messages |
injectedModelMessages | ModelMessage[] | The model messages that were injected |
chatId | string | Chat session ID |
turn | number | Current turn |
stepNumber | number | Step where injection occurred |
UsePendingMessagesReturn
Return value ofusePendingMessages hook. See Pending Messages — Frontend.
| Property/Method | Type | Description |
|---|---|---|
pending | PendingMessage[] | Current pending messages with mode and injection status |
steer | (text: string) => void | Send a steering message (or normal message when not streaming) |
queue | (text: string) => void | Queue for next turn (or send normally when not streaming) |
promoteToSteering | (id: string) => void | Convert a queued message to steering |
isInjectionPoint | (part: unknown) => boolean | Check if an assistant message part is an injection confirmation |
getInjectedMessageIds | (part: unknown) => string[] | Get message IDs from an injection point |
getInjectedMessages | (part: unknown) => InjectedMessage[] | Get messages (id + text) from an injection point |
ChatSessionOptions
Options forchat.createSession().
| Option | Type | Default | Description |
|---|---|---|---|
signal | AbortSignal | required | Run-level cancel signal |
idleTimeoutInSeconds | number | 30 | Seconds to stay idle between turns |
timeout | string | "1h" | Duration string for suspend timeout |
maxTurns | number | 100 | Max turns before ending |
ChatTurn
Each turn yielded bychat.createSession().
| Field | Type | Description |
|---|---|---|
number | number | Turn number (0-indexed) |
chatId | string | Chat session ID |
trigger | string | What triggered this turn |
clientData | unknown | Client data from the transport |
messages | ModelMessage[] | Full accumulated model messages |
uiMessages | UIMessage[] | Full accumulated UI messages |
signal | AbortSignal | Combined stop+cancel signal (fresh each turn) |
stopped | boolean | Whether the user stopped generation this turn |
continuation | boolean | Whether this is a continuation run |
| Method | Returns | Description |
|---|---|---|
complete(source) | Promise<UIMessage | undefined> | Pipe, capture, accumulate, cleanup, and signal turn-complete |
done() | Promise<void> | Signal turn-complete (when you’ve piped manually) |
addResponse(response) | Promise<void> | Add response to accumulator manually |
chat namespace
All methods available on thechat object from @trigger.dev/sdk/ai.
| Method | Description |
|---|---|
chat.agent(options) | Create a chat agent |
chat.createSession(payload, options) | Create an async iterator for chat turns |
chat.pipe(source, options?) | Pipe a stream to the frontend (from anywhere inside a task) |
chat.pipeAndCapture(source, options?) | Pipe and capture the response UIMessage |
chat.writeTurnComplete(options?) | Signal the frontend that the current turn is complete |
chat.createStopSignal() | Create a managed stop signal wired to the stop input stream |
chat.messages | Input stream for incoming messages — use .waitWithIdleTimeout() |
chat.local<T>({ id }) | Create a per-run typed local (see Per-run data) |
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). |
chat.requestUpgrade() | End the current run after this turn so the next message starts on the latest agent version. Server-orchestrated handoff. |
chat.setTurnTimeout(duration) | Override turn timeout at runtime (e.g. "2h") |
chat.setTurnTimeoutInSeconds(seconds) | Override turn timeout at runtime (in seconds) |
chat.setIdleTimeoutInSeconds(seconds) | Override idle timeout at runtime |
chat.setUIMessageStreamOptions(options) | Override toUIMessageStream() options for the current turn |
chat.defer(promise) | Run background work in parallel with streaming, awaited before onTurnComplete |
chat.isStopped() | Check if the current turn was stopped by the user |
chat.cleanupAbortedParts(message) | Remove incomplete parts from a stopped response message |
chat.response.write(chunk) | Write a data part that streams to the frontend AND persists in onTurnComplete’s responseMessage |
chat.stream | Raw chat output stream — use .writer(), .pipe(), .append(), .read(). Chunks are NOT accumulated into the response. |
chat.history.all() | Read the current accumulated UI messages (returns a copy). See chat.history |
chat.history.set(messages) | Replace all accumulated messages (same as chat.setMessages()) |
chat.history.remove(messageId) | Remove a specific message by ID |
chat.history.rollbackTo(messageId) | Keep messages up to and including the given ID (undo/rollback) |
chat.history.replace(messageId, message) | Replace a specific message by ID (edit) |
chat.history.slice(start, end?) | Keep only messages in the given range |
chat.MessageAccumulator | Class that accumulates conversation messages across turns |
chat.withUIMessage(config?) | Returns a ChatBuilder with a fixed UIMessage subtype. See Types |
chat.withClientData({ schema }) | Returns a ChatBuilder with a fixed client data schema. See Types |
chat.withUIMessage
Returns a ChatBuilder with a fixed UIMessage subtype. Chain .withClientData(), hook methods, and .agent().
| Parameter | Type | Description |
|---|---|---|
config.streamOptions | ChatUIMessageStreamOptions<TUIM> | Optional defaults for toUIMessageStream(). Shallow-merged with uiMessageStreamOptions on the inner .agent({ ... }) (agent wins on key conflicts). |
InferChatUIMessage / typed data-* parts / InferUITools to line up across backend hooks and useChat. Full guide: Types.
chat.withClientData
Returns a ChatBuilder with a fixed client data schema. All hooks and run get typed clientData without passing clientDataSchema in .agent() options.
| Parameter | Type | Description |
|---|---|---|
schema | TaskSchema | Zod, ArkType, Valibot, or any supported schema lib |
ChatWithUIMessageConfig
| Field | Type | Description |
|---|---|---|
streamOptions | ChatUIMessageStreamOptions<TUIM> | Default toUIMessageStream() options for agents created via .agent() |
InferChatUIMessage
Type helper: extracts the UIMessage subtype from a chat agent’s wire payload.
useChat<Msg>({ transport }) when using chat.withUIMessage. For agents defined with plain chat.agent() (no custom generic), this resolves to the base UIMessage.
AI helpers (ai from @trigger.dev/sdk/ai)
| Export | Status | Description |
|---|---|---|
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). |
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. |
ai.toolCallId, ai.chatContext, ai.chatContextOrThrow, ai.currentToolOptions | Supported | Work for any task-backed tool execute path, including ai.toolExecute. |
ChatUIMessageStreamOptions
Options for customizingtoUIMessageStream(). Set as static defaults via uiMessageStreamOptions on chat.agent(), or override per-turn via chat.setUIMessageStreamOptions(). See Stream options for usage examples.
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).
| Option | Type | Default | Description |
|---|---|---|---|
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. |
sendReasoning | boolean | true | Send reasoning parts to the client |
sendSources | boolean | false | Send source parts to the client |
sendFinish | boolean | true | Send the finish event. Set to false when chaining multiple streamText calls. |
sendStart | boolean | true | Send the message start event. Set to false when chaining. |
messageMetadata | (options: { part }) => metadata | — | Extract message metadata to send to the client. Called on start and finish events. |
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. |
TriggerChatTransport options
Options for the frontend transport constructor anduseTriggerChatTransport hook.
| Option | Type | Default | Description |
|---|---|---|---|
task | string | required | Task ID the transport’s session is bound to. Threaded into startSession’s params. |
accessToken | (params: AccessTokenParams) => string | Promise<string> | required | Pure refresh — mints a fresh session-scoped PAT. Called on 401/403. See callback shape. |
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. |
baseURL | string | "https://api.trigger.dev" | API base URL (for self-hosted) |
headers | Record<string, string> | — | Extra headers for API requests |
streamTimeoutSeconds | number | 120 | How long to wait for stream data |
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. |
sessions | Record<string, ChatSession> | — | Restore sessions from storage. See ChatSession. |
onSessionChange | (chatId, session | null) => void | — | Fires when session state changes. session is the full ChatSession or null when the run ends. |
multiTab | boolean | false | Enable multi-tab claim coordination via BroadcastChannel. See Frontend → multi-tab. |
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. |
accessToken callback
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.
AccessTokenParams:
| Field | Type | Description |
|---|---|---|
chatId | string | The conversation id. |
auth.createPublicToken server-side:
startSession callback
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.
StartSessionParams<TClientData>:
| Field | Type | Description |
|---|---|---|
taskId | string | The transport’s task value. |
chatId | string | The conversation id (the session’s externalId). |
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. |
chat.createStartSessionAction(taskId):
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.
multiTab
Enable multi-tab coordination. Whentrue, 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.
BroadcastChannel is unavailable (SSR, Node.js). See Multi-tab coordination.
Trigger configuration
Trigger config (machine, queue, tags, maxAttempts, idleTimeoutInSeconds) lives server-side inchat.createStartSessionAction(taskId, options?). The transport doesn’t accept these options directly — pass them when wrapping the action:
chat:{chatId} tag is automatically added to every run.
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.
transport.stopGeneration()
Stop the current generation for a chat session. Sends a stop signal to the backend task and closes the active SSE connection.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).
Use alongside useChat’s stop() for a complete stop experience:
transport.sendAction()
Send a custom action to the agent. Actions wake the agent from suspension, fireonAction, then trigger a normal run() turn.
actionSchema on the backend.
transport.preload()
Eagerly trigger a run before the first message.useTriggerChatTransport
React hook that creates and memoizes aTriggerChatTransport instance. Import from @trigger.dev/sdk/chat/react.
useMultiTabChat
React hook for multi-tab message coordination. Import from@trigger.dev/sdk/chat/react.
| Parameter | Type | Description |
|---|---|---|
transport | TriggerChatTransport | Transport instance with multiTab: true |
chatId | string | The chat session ID |
messages | UIMessage[] | Current messages from useChat |
setMessages | (messages) => void | Message setter from useChat |
{ isReadOnly: boolean } — true when another tab is actively sending to this chatId.
The hook handles:
- Tracking read-only state from the transport’s
BroadcastChannelcoordinator - Broadcasting messages when this tab is the active sender
- Receiving messages from other tabs and updating via
setMessages
ChatSession
Persistable session state for the frontendTriggerChatTransport 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.
| Field | Type | Description |
|---|---|---|
publicAccessToken | string | Session-scoped JWT (read:sessions:{chatId} + write:sessions:{chatId}). Refreshed automatically on 401/403 via the transport’s accessToken callback. |
lastEventId | string | undefined | Last SSE event received on .out. Used to resume mid-stream after a disconnect. |
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 response header. |
ChatInputChunk
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.
| Variant | When | Payload |
|---|---|---|
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 |
kind: "stop" | Client aborted the active turn | Optional message surfaces in the stop handler |
Session token scopes
Tokens minted forTriggerChatTransport and AgentChat are session-scoped — keyed on the chat’s externalId (the chatId you assign).
| Scope | Grants |
|---|---|
read:sessions:<chatId> | Subscribe to .out, HEAD probe the stream, retrieve the session row |
write:sessions:<chatId> | Append to .in, close the session, end-and-continue, update metadata |
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.
Related
- Realtime Streams — How streams work under the hood
- Using the Vercel AI SDK — Basic AI SDK usage with Trigger.dev
- Realtime React Hooks — Lower-level realtime hooks
- Authentication — Public access tokens and trigger tokens

