Skip to content

Commit 0288aac

Browse files
committed
subagents and AgentChat docs
1 parent 4c34098 commit 0288aac

File tree

3 files changed

+495
-0
lines changed

3 files changed

+495
-0
lines changed

docs/ai-chat/server-chat.mdx

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
---
2+
title: "Server-Side Chat"
3+
sidebarTitle: "Server-Side Chat"
4+
description: "Use AgentChat to interact with chat agents from server-side code — tasks, webhooks, scripts, or other agents."
5+
---
6+
7+
`AgentChat` lets you chat with agents from server-side code. It works inside tasks (agent-to-agent), request handlers, webhook processors, and scripts.
8+
9+
```ts
10+
import { AgentChat } from "@trigger.dev/sdk/chat";
11+
12+
const chat = new AgentChat({ agent: "my-agent" });
13+
const stream = await chat.sendMessage("Hello!");
14+
const text = await stream.text();
15+
await chat.close();
16+
```
17+
18+
## Type-safe client data
19+
20+
Pass `typeof yourAgent` as a type parameter and `clientData` is automatically typed from the agent's `withClientData` schema:
21+
22+
```ts
23+
import { AgentChat } from "@trigger.dev/sdk/chat";
24+
import type { myAgent } from "./trigger/my-agent";
25+
26+
const chat = new AgentChat<typeof myAgent>({
27+
agent: "my-agent",
28+
clientData: { userId: "user_123" }, // ← typed from agent definition
29+
});
30+
```
31+
32+
## Conversation lifecycle
33+
34+
Each `AgentChat` instance represents one conversation. The conversation ID is auto-generated or can be set explicitly:
35+
36+
```ts
37+
// Auto-generated ID
38+
const chat = new AgentChat({ agent: "my-agent" });
39+
40+
// Explicit ID — useful for persistence or finding the run later
41+
const chat = new AgentChat({ agent: "my-agent", id: `review-${prNumber}` });
42+
```
43+
44+
### Sending messages
45+
46+
`sendMessage()` triggers a new run on the first call, then reuses the same run for subsequent messages via input streams:
47+
48+
```ts
49+
// First message — triggers a new run
50+
const stream1 = await chat.sendMessage("Review PR #42");
51+
const review = await stream1.text();
52+
53+
// Follow-up — same run, agent has full context
54+
const stream2 = await chat.sendMessage("Can you fix the main bug?");
55+
const fix = await stream2.text();
56+
```
57+
58+
### Preloading (optional)
59+
60+
If you want the agent to initialize before the first message (e.g., load data, authenticate), call `preload()`. This is optional — `sendMessage()` triggers the run automatically if needed.
61+
62+
```ts
63+
await chat.preload();
64+
// Agent's onPreload hook fires now, before user types anything
65+
const stream = await chat.sendMessage("Hello");
66+
```
67+
68+
### Closing
69+
70+
Signal the agent to exit its loop gracefully:
71+
72+
```ts
73+
await chat.close();
74+
```
75+
76+
Without `close()`, the agent exits on its own when its idle/suspend timeout expires.
77+
78+
## Reading responses
79+
80+
`sendMessage()` returns a `ChatStream` — a typed wrapper around the response.
81+
82+
### Get the full text
83+
84+
```ts
85+
const stream = await chat.sendMessage("What is Trigger.dev?");
86+
const text = await stream.text();
87+
```
88+
89+
### Get structured results
90+
91+
```ts
92+
const stream = await chat.sendMessage("Research this topic");
93+
const { text, toolCalls, toolResults } = await stream.result();
94+
95+
for (const tc of toolCalls) {
96+
console.log(`Tool: ${tc.toolName}, Input: ${JSON.stringify(tc.input)}`);
97+
}
98+
```
99+
100+
### Stream chunks in real-time
101+
102+
```ts
103+
const stream = await chat.sendMessage("Write a report");
104+
105+
for await (const chunk of stream) {
106+
if (chunk.type === "text-delta") {
107+
process.stdout.write(chunk.delta);
108+
}
109+
if (chunk.type === "tool-input-available") {
110+
console.log(`Using tool: ${chunk.toolName}`);
111+
}
112+
}
113+
```
114+
115+
## Stateless request handlers
116+
117+
In a stateless environment (HTTP handler, serverless function), you need to persist and restore the session across requests.
118+
119+
`AgentChat` provides a `session` option and two callbacks for this:
120+
121+
```ts
122+
import { AgentChat } from "@trigger.dev/sdk/chat";
123+
124+
export async function POST(req: Request) {
125+
const { chatId, message, runId, lastEventId } = await req.json();
126+
127+
const chat = new AgentChat({
128+
agent: "my-agent",
129+
id: chatId,
130+
// Restore from previous request
131+
session: runId ? { runId, lastEventId } : undefined,
132+
// Persist when a new run starts
133+
onTriggered: async ({ runId, chatId }) => {
134+
await db.sessions.upsert({ chatId, runId });
135+
},
136+
// Persist after each turn for stream resumption
137+
onTurnComplete: async ({ lastEventId, chatId }) => {
138+
await db.sessions.update({ chatId, lastEventId });
139+
},
140+
});
141+
142+
const stream = await chat.sendMessage(message);
143+
const text = await stream.text();
144+
145+
return Response.json({ text, runId: chat.run?.runId });
146+
}
147+
```
148+
149+
## Sub-agent tool pattern
150+
151+
`AgentChat` can be used inside an AI SDK tool to delegate work to a durable sub-agent. The sub-agent's response streams as preliminary tool results:
152+
153+
```ts
154+
import { tool } from "ai";
155+
import { AgentChat } from "@trigger.dev/sdk/chat";
156+
import { z } from "zod";
157+
158+
const researchTool = tool({
159+
description: "Delegate research to a specialist agent.",
160+
inputSchema: z.object({ topic: z.string() }),
161+
execute: async function* ({ topic }, { abortSignal }) {
162+
const chat = new AgentChat({ agent: "research-agent" });
163+
const stream = await chat.sendMessage(topic, { abortSignal });
164+
yield* stream.messages();
165+
await chat.close();
166+
},
167+
toModelOutput: ({ output: message }) => {
168+
const lastText = message?.parts?.findLast(
169+
(p: { type: string }) => p.type === "text"
170+
) as { text?: string } | undefined;
171+
return { type: "text", value: lastText?.text ?? "Done." };
172+
},
173+
});
174+
```
175+
176+
This supports single-turn delegation, multi-turn LLM-driven conversations with persistent sub-agents, and cross-turn state that survives snapshot/restore.
177+
178+
See the [Sub-Agents guide](/ai-chat/sub-agents) for the full pattern including multi-turn conversations, cleanup, and what the frontend sees.
179+
180+
## Additional methods
181+
182+
### Steering
183+
184+
Send a message during an active stream without interrupting it:
185+
186+
```ts
187+
await chat.steer("Focus on security issues specifically");
188+
```
189+
190+
### Stop generation
191+
192+
Abort the current `streamText` call without ending the run:
193+
194+
```ts
195+
await chat.stop();
196+
```
197+
198+
### Raw messages
199+
200+
For full control over the UIMessage shape:
201+
202+
```ts
203+
const rawStream = await chat.sendRaw([
204+
{
205+
id: "msg-1",
206+
role: "user",
207+
parts: [
208+
{ type: "text", text: "Hello" },
209+
{ type: "file", url: "https://...", mediaType: "image/png" },
210+
],
211+
},
212+
]);
213+
```
214+
215+
### Reconnect
216+
217+
Resume a stream subscription after a disconnect:
218+
219+
```ts
220+
const stream = await chat.reconnect();
221+
```
222+
223+
## AgentChat options
224+
225+
| Option | Type | Default | Description |
226+
|---|---|---|---|
227+
| `agent` | `string` | required | The agent task ID to trigger |
228+
| `id` | `string` | `crypto.randomUUID()` | Conversation ID for tagging and correlation |
229+
| `clientData` | typed from agent | `undefined` | Client data included in every request |
230+
| `session` | `{ runId: string; lastEventId?: string }` | `undefined` | Restore a previous session |
231+
| `onTriggered` | `(event) => void` | `undefined` | Called when a new run is created |
232+
| `onTurnComplete` | `(event) => void` | `undefined` | Called when a turn's stream ends |
233+
| `streamKey` | `string` | `"chat"` | Output stream key |
234+
| `streamTimeoutSeconds` | `number` | `120` | SSE timeout in seconds |
235+
| `triggerOptions` | `object` | `undefined` | Tags, queue, machine, priority |
236+
237+
## ChatStream methods
238+
239+
| Method | Returns | Description |
240+
|---|---|---|
241+
| `text()` | `Promise<string>` | Consume stream, return accumulated text |
242+
| `result()` | `Promise<ChatStreamResult>` | Consume stream, return `{ text, toolCalls, toolResults }` |
243+
| `messages()` | `AsyncGenerator<UIMessage>` | Yield accumulated UIMessage snapshots (sub-agent pattern) |
244+
| `[Symbol.asyncIterator]` | `UIMessageChunk` | Iterate over typed stream chunks |
245+
| `.stream` | `ReadableStream<UIMessageChunk>` | Raw stream for AI SDK utilities |

0 commit comments

Comments
 (0)