Skip to main content

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.

1

Define a chat agent

Use chat.agent from @trigger.dev/sdk/ai to define an agent that handles chat messages. The run function receives ModelMessage[] (already converted from the frontend’s UIMessage[]) — pass them directly to streamText.If you return a StreamTextResult, it’s automatically piped to the frontend.
trigger/chat.ts
import { chat } from "@trigger.dev/sdk/ai";
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";

export const myChat = chat.agent({
  id: "my-chat",
  run: async ({ messages, signal }) => {
    // messages is ModelMessage[] — pass directly to streamText
    // signal fires on stop or run cancel
    return streamText({
      model: openai("gpt-4o"),
      messages,
      abortSignal: signal,
    });
  },
});
For a custom UIMessage subtype (typed data-* parts, tool map, etc.), define the agent with chat.withUIMessage<...>().agent({...}) instead of chat.agent.
2

Add two server actions

On your server (e.g. as Next.js server actions), expose two helpers the transport will call: one that creates the chat session, and one that mints a fresh session-scoped access token for refresh.
app/actions.ts
"use server";

import { auth } from "@trigger.dev/sdk";
import { chat } from "@trigger.dev/sdk/ai";

// Creates the Session row + triggers the first run, returns the
// session PAT. Idempotent on (env, chatId) so concurrent calls
// converge to the same session.
export const startChatSession = chat.createStartSessionAction("my-chat");

// Pure mint — fresh session-scoped PAT for an existing session.
// The transport calls this on 401/403 to refresh.
export async function mintChatAccessToken(chatId: string) {
  return auth.createPublicToken({
    scopes: {
      read: { sessions: chatId },
      write: { sessions: chatId },
    },
    expirationTime: "1h",
  });
}
The browser never holds your environment’s secret key — both helpers run on your server, where customer-side authorization (per-user, per-plan, etc.) lives alongside any DB writes you want to pair with session creation.
3

Use in the frontend

Use the useTriggerChatTransport hook from @trigger.dev/sdk/chat/react to create a memoized transport instance, then pass it to useChat. Wire both server actions into the transport’s accessToken and startSession callbacks:
app/components/chat.tsx
"use client";

import { useChat } from "@ai-sdk/react";
import { useTriggerChatTransport } from "@trigger.dev/sdk/chat/react";
import type { myChat } from "@/trigger/chat";
import { mintChatAccessToken, startChatSession } from "@/app/actions";

export function Chat() {
  const transport = useTriggerChatTransport<typeof myChat>({
    task: "my-chat",
    accessToken: ({ chatId }) => mintChatAccessToken(chatId),
    startSession: ({ chatId, taskId, clientData }) =>
      startChatSession({ chatId, taskId, clientData }),
  });

  const { messages, sendMessage, stop, status } = useChat({ transport });

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>
          <strong>{m.role}:</strong>
          {m.parts.map((part, i) =>
            part.type === "text" ? <span key={i}>{part.text}</span> : null
          )}
        </div>
      ))}

      <form
        onSubmit={(e) => {
          e.preventDefault();
          const input = e.currentTarget.querySelector("input");
          if (input?.value) {
            sendMessage({ text: input.value });
            input.value = "";
          }
        }}
      >
        <input placeholder="Type a message..." />
        <button type="submit" disabled={status === "streaming"}>
          Send
        </button>
        {status === "streaming" && (
          <button type="button" onClick={stop}>
            Stop
          </button>
        )}
      </form>
    </div>
  );
}

Next steps

  • Backend — Lifecycle hooks, persistence, session iterator, raw task primitives
  • Frontend — Session management, client data, reconnection
  • Typeschat.withUIMessage, InferChatUIMessage, and related typing
  • Features — Per-run data, deferred work, streaming, subtasks