You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Bring the AI Chat documentation in line with the Sessions-as-run-manager
release. Public surface (chat.agent({...}), useTriggerChatTransport,
AgentChat, chat.store / chat.defer / chat.history) is unchanged in
shape; the wiring docs around it changed enough that every transport
example, every server-action snippet, and the auth/session model needed
updating.
New:
- ai-chat/upgrade-guide.mdx — step-by-step migration for prerelease
customers (replace getStartToken / getChatToken with startSession +
accessToken, drop runId from ChatSession persistence, etc.).
Updated transport-shape group (these all share the same callback
shape and ship together):
- quick-start, frontend, backend, server-chat, types, reference
Updated peripherals:
- overview, changelog, client-protocol, features, error-handling,
testing, patterns/version-upgrades, patterns/database-persistence
Outside ai-chat:
- realtime/backend/{streams,input-streams}: callout cross-references
no longer point at the deleted Sessions docs.
Removed:
- docs/sessions/{overview,quick-start,channels,reference}.mdx — the
standalone-Sessions surface predates the task-bound model and was
giving stale guidance. We'll re-introduce Sessions docs once the
primitive ships a non-chat.agent customer flow worth documenting.
- The "Sessions" group in docs.json's AI nav.
Every `chat.agent` conversation is backed by a [Session](/sessions/overview) — `externalId` is your `chatId`, `type` is `"chat.agent"`. The session outlives any single run, which is why chats can resume across days or deploys without losing identity. You rarely need to touch the session directly (`chat.stream`, `chat.messages`, `chat.stopSignal` wrap everything), but `payload.sessionId` is available if you want to reach in — e.g. `sessions.open(payload.sessionId)` to write from a sub-agent or from outside the turn loop.
16
+
Every `chat.agent` conversation is backed by a durable Session — `externalId` is your `chatId`, `type` is `"chat.agent"`, `taskIdentifier` is the agent's task ID. The session is the run manager: it owns the chat's runs, persists across run lifecycles, and orchestrates handoffs (idle continuation, `chat.requestUpgrade`). You rarely need to touch the session directly (`chat.stream`, `chat.messages`, `chat.stopSignal` wrap everything), but `payload.sessionId` is available if you want to reach in — e.g. `sessions.open(payload.sessionId)` to write from a sub-agent or from outside the turn loop.
Copy file name to clipboardExpand all lines: docs/ai-chat/changelog.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ description: "Pre-release updates for AI chat agents."
8
8
9
9
## `chat.agent` now runs on Sessions
10
10
11
-
Every chat is backed by a [Session](/sessions/overview) — a new public, durable, bidirectional I/O primitive that outlives any single run. `externalId` = your chat ID, `type` = `"chat.agent"`. Under the hood:
11
+
Every chat is backed by a durable Session row that outlives any single run. `externalId` = your chat ID, `type` = `"chat.agent"`. Under the hood:
12
12
13
13
- Output chunks stream on `session.out` (was a run-scoped `streams.writer("chat")`).
14
14
- Client messages and stops land on `session.in` as a [`ChatInputChunk`](/ai-chat/reference#chatinputchunk) tagged union (was two run-scoped `streams.input` definitions).
@@ -21,7 +21,7 @@ Public surface (`chat.agent()`, `TriggerChatTransport`, `AgentChat`, `chat.strea
21
21
-**`TriggerChatTaskResult.sessionId`** + **`ChatTaskRunPayload.sessionId`** — you can reach into the raw session via `sessions.open(payload.sessionId)` for advanced cases (writing from a sub-agent, custom transport).
22
22
-**Dashboard Agent tab** resolves via `sessionId` and stays in sync with the live stream across runs.
23
23
24
-
See the new [Sessions docs](/sessions/overview) for the underlying primitive.
24
+
The full wire-level protocol (session create, channel routes, JWT scopes) is documented in [Client Protocol](/ai-chat/client-protocol).
25
25
26
26
## `X-Session-Settled` — fast reconnect on idle chats
Copy file name to clipboardExpand all lines: docs/ai-chat/client-protocol.mdx
+4-8Lines changed: 4 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,7 +12,7 @@ This page documents the protocol that chat clients use to communicate with `chat
12
12
13
13
## Overview
14
14
15
-
`chat.agent` is built on [Sessions](/sessions/overview) — a durable, bidirectional I/O primitive that outlives a single run. A conversation is one session; a session can host many runs over its lifetime.
15
+
`chat.agent` is built on a durable Session row — the unit of state that owns the chat's runs across their full lifecycle. A conversation is one session; a session can host many runs over its lifetime.
16
16
17
17
The protocol has four parts:
18
18
@@ -75,7 +75,7 @@ Response:
75
75
76
76
`id` is the `session_*` friendly ID — persist it alongside your chat state. `isCached: true` means the server returned an existing session for this `externalId` (safe to ignore).
77
77
78
-
See [`POST /api/v1/sessions`](/sessions/reference#create) for the full request / response schema.
78
+
`POST /api/v1/sessions` is documented inline in the wire-protocol section below.
79
79
80
80
## Step 2: Trigger a run
81
81
@@ -485,17 +485,13 @@ A client needs to track per-conversation:
485
485
| Create session (`POST /api/v1/sessions`) | Secret API key or JWT with `write:sessions`|
486
486
| Close session (`POST /api/v1/sessions/{id}/close`) | Secret API key or JWT with `admin:sessions:{id}`|
487
487
| Trigger task | Secret API key or JWT with `write:tasks`|
488
-
|`.in` append | JWT with `write:sessions:{id}`(or `write:sessions:{id}:in`) |
488
+
|`.in` append | JWT with `write:sessions:{id}`|
489
489
|`.out` subscribe | JWT with `read:sessions:{id}`|
490
490
491
-
The transport-facing `publicAccessToken` returned from the trigger response carries both `read:sessions:{id}` and `write:sessions:{id}`for the session, plus `read:runs:{runId}` + `write:inputStreams:{runId}`for the run. Use it for all session operations.
492
-
493
-
See [Session reference — Token scopes](/sessions/reference#token-scopes) for the full scope list.
491
+
The transport-facing `publicAccessToken` returned from `POST /api/v1/sessions` carries both `read:sessions:{id}` and `write:sessions:{id}`for the session — use it for all session operations. A token minted for either the externalId form or the friendlyId form authorizes both URL forms on every read and write route.
494
492
495
493
## See also
496
494
497
-
- [Sessions Overview](/sessions/overview) — The durable primitive chat.agent is built on
498
-
- [Sessions Reference](/sessions/reference) — Full `sessions.*` API and wire endpoints
499
495
- [`TriggerChatTransport`](/ai-chat/frontend) — Built-in browser transport (implements this protocol)
const { messages, error, sendMessage } =useChat({ transport });
305
310
306
311
return (
@@ -344,6 +349,30 @@ uiMessageStreamOptions: {
344
349
For richer error structures, use [`chat.response.write()`](/ai-chat/features#custom-data-parts) with a custom `data-error` part type. This lets you ship structured error metadata (codes, retry hints, etc.) instead of stringly-typed messages.
345
350
</Tip>
346
351
352
+
### Errors from `accessToken` / `startSession`
353
+
354
+
If your `accessToken` or `startSession` callback throws (auth failure, DB write failure, network error), the rejection surfaces through `useChat`'s `error` state — same as a stream error. The transport doesn't retry the callback automatically; the customer is responsible for handling it.
355
+
356
+
```tsx
357
+
const transport =useTriggerChatTransport({
358
+
task: "my-chat",
359
+
accessToken: async ({ chatId }) => {
360
+
try {
361
+
returnawaitmintChatAccessToken(chatId);
362
+
} catch (err) {
363
+
// Customer's server action failed (e.g. user lost auth).
364
+
// Re-throw to surface as a useChat error, or return a sentinel
365
+
// your UI can detect and prompt re-auth.
366
+
thrownewError(`AUTH_REFRESH: ${err.message}`);
367
+
}
368
+
},
369
+
startSession: ({ chatId, taskId, clientData }) =>
370
+
startChatSession({ chatId, taskId, clientData }),
371
+
});
372
+
```
373
+
374
+
`startSession` failures most commonly mean the customer's authorization layer rejected the request (no plan, quota exceeded, user not allowed to chat with this agent). The customer's server should produce a meaningful error message; the transport propagates it verbatim to `useChat`'s `error` state.
375
+
347
376
## Run-level retries
348
377
349
378
`chat.agent` uses `retry: { maxAttempts: 1 }` — the run **never retries** on unhandled failure. This is intentional: each turn is conversation-preserving, so a true run failure is severe and shouldn't silently retry (which could send duplicate API calls or mutate state twice).
0 commit comments