Skip to content

Commit e4c81d0

Browse files
committed
docs(ai-chat): lead the sessions page with a plain definition
The opening defined a Session as a durable, task-bound, bi-directional I/O channel pair, which reads as jargon and omits run orchestration entirely. The page now leads with the plain mental model (a pair of durable streams: input carries user messages, output carries everything the agent produces) plus the Session's role orchestrating runs, adds a diagram, a minimal runnable example, and a Sessions-and-runs section covering the one-session-many-runs lifecycle.
1 parent d2924d2 commit e4c81d0

1 file changed

Lines changed: 66 additions & 2 deletions

File tree

docs/ai-chat/sessions.mdx

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,81 @@
11
---
22
title: "Sessions"
33
sidebarTitle: "Sessions"
4-
description: "The durable, task-bound, bi-directional I/O primitive that backs chat.agent — sessions.list / open / start / close plus the SessionHandle (in/out) API."
4+
description: "A Session is a pair of durable streams — input carries your users' messages to the agent, output carries everything the agent produces back — plus orchestration of the runs that process them."
55
---
66

77
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
88

99
<RcBanner />
1010

11-
A **Session** is a durable, task-bound, bi-directional I/O channel pair. It outlives any single run: a Session row is keyed on a stable `externalId` (e.g. `chatId`), holds the conversation's identity across run boundaries, and exposes two realtime streams — `.in` (clients → task) and `.out` (task → clients).
11+
**A Session is a pair of durable streams.** The input stream (`.in`) carries incoming user messages to your task. The output stream (`.out`) carries everything the agent produces back to your clients: AI generation parts (text, reasoning, tool calls) and any custom data parts you write.
12+
13+
Sessions also **orchestrate the runs that process those streams**. A Session is keyed on your stable id (`externalId` — for chat, the `chatId`) and owns its current run: when a run suspends, idles out, or hands off to a new version, the Session starts or swaps to a fresh run and the streams carry on. Clients keep sending and reading against the same id; they never know a run changed underneath.
14+
15+
```mermaid
16+
flowchart LR
17+
C[Browser / backend clients] -- "user messages" --> IN([Session .in])
18+
IN --> R["current run<br/>(runs come and go)"]
19+
R -- "text, reasoning, tool calls,<br/>data parts" --> OUT([Session .out])
20+
OUT --> C
21+
```
1222

1323
`chat.agent` is built on Sessions. You can also use them directly for any pattern that needs durable bi-directional streaming across runs: long-lived agent inboxes, multi-step approval flows, server-to-server pipelines that survive worker restarts.
1424

25+
## A minimal example
26+
27+
A task that echoes whatever lands on its input stream, and a backend that starts the session, sends a message, and reads the reply:
28+
29+
```ts trigger/inbox.ts
30+
import { task, sessions } from "@trigger.dev/sdk";
31+
32+
export const inboxAgent = task({
33+
id: "inbox-agent",
34+
run: async (payload: { sessionId: string }) => {
35+
const session = sessions.open(payload.sessionId);
36+
37+
while (true) {
38+
// Suspends the run (no compute billed) until a record arrives.
39+
const next = await session.in.wait<{ text: string }>({ timeout: "1h" });
40+
if (!next.ok) return;
41+
await session.out.append({ type: "reply", text: `echo: ${next.output.text}` });
42+
}
43+
},
44+
});
45+
```
46+
47+
```ts Your backend
48+
import { sessions } from "@trigger.dev/sdk";
49+
50+
// Atomically create the session AND trigger its first run.
51+
await sessions.start({
52+
type: "inbox",
53+
externalId: userId,
54+
taskIdentifier: "inbox-agent",
55+
triggerConfig: { basePayload: { sessionId: userId } },
56+
});
57+
58+
const session = sessions.open(userId);
59+
await session.in.send({ text: "hello" });
60+
61+
const stream = await session.out.read({ signal: AbortSignal.timeout(30_000) });
62+
for await (const chunk of stream) {
63+
console.log(chunk); // { type: "reply", text: "echo: hello" }
64+
}
65+
```
66+
67+
The run can suspend, crash, or be replaced between the `send` and the `read` — the streams are durable, so nothing is lost and the client code doesn't change.
68+
69+
## Sessions and runs
70+
71+
One Session spans many runs over its lifetime. The Session row tracks `currentRunId`; the runs do the work:
72+
73+
- **First run**: created atomically by `sessions.start` (no gap where the session exists but nothing is listening).
74+
- **Idle suspend**: a run blocked on `in.wait` suspends and frees compute. A new record on `.in` wakes it.
75+
- **Continuation**: when a run ends (idle timeout, `chat.endRun`, a crash, a version upgrade), the next incoming record triggers a fresh run against the same Session. The new run picks up the streams where the old one left off.
76+
77+
This is what makes a Session the durable identity for a conversation: runs are an execution detail, the Session (and its `externalId`) is what your clients address. See [How it works](/ai-chat/how-it-works) for how `chat.agent` drives this loop.
78+
1579
## When to reach for Sessions directly
1680

1781
`chat.agent` handles 90% of chat-shaped workloads — message accumulation, the turn loop, stop signals, lifecycle hooks. Use the raw `sessions` API when you need any of:

0 commit comments

Comments
 (0)