Skip to content

Commit 1acf59e

Browse files
committed
docs(ai-chat): clarify lastEventId is sessionId-keyed across run boundaries
Clearing lastEventId on chat.endRun() looks intuitive (Run ended, cursor must be stale) but breaks the next-message subscription. The cursor is sessionId-keyed, not runId-keyed. Clearing it forces the next sendMessages to subscribe from seq_num=0, where it may hit the prior turn's still-durable turn-complete record and close the SSE empty before the new Run's chunks arrive. Spells out the invariant in the frontend transport persistence table and adds a Warning in the chat.endRun() reference.
1 parent c80b85e commit 1acf59e

2 files changed

Lines changed: 5 additions & 1 deletion

File tree

docs/ai-chat/backend.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,10 @@ The current turn streams through normally, `onBeforeTurnComplete` / `onTurnCompl
585585

586586
Use this when the agent knows its work is done (budget exhausted, goal achieved, one-shot response) rather than relying on the idle timeout. Unlike `chat.requestUpgrade()`, no `upgrade-required` signal is sent to the client, so there's no version-migration semantics.
587587

588+
<Warning>
589+
If you persist `lastEventId` to your own storage for cross-page-load resume, **don't clear it on `chat.endRun()`**. The cursor is sessionId-keyed and stays valid across Run boundaries — clearing it forces the next `sendMessages` to subscribe from `seq_num=0`, where it may hit the prior turn's stale `turn-complete` record and close the stream empty before the new Run's chunks arrive.
590+
</Warning>
591+
588592
### Runtime configuration
589593

590594
#### chat.setTurnTimeout()

docs/ai-chat/frontend.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Every chat is backed by a durable Session — the row that owns the chat's runs,
113113
| Field | Type | Notes |
114114
| --- | --- | --- |
115115
| `publicAccessToken` | `string` | Session-scoped JWT (`read:sessions:{chatId} + write:sessions:{chatId}`). Refreshed automatically on 401/403 via `accessToken`. |
116-
| `lastEventId` | `string \| undefined` | Last SSE event received on `.out`. Used to resume mid-stream after a reload. |
116+
| `lastEventId` | `string \| undefined` | Last SSE event received on `.out`. **Valid for the lifetime of the Session** — keep it across `endRun` / `requestUpgrade` / continuation-run boundaries; only clear when the Session itself closes. The cursor lets the next subscription open past the prior turn's stale `turn-complete` record. |
117117
| `isStreaming` | `boolean \| undefined` | **Optional.** The transport sets it internally, but you don't have to persist it — the server decides "nothing is streaming" via the session's [`X-Session-Settled`](/ai-chat/client-protocol#x-session-settled-fast-close-on-idle-reconnects) signal on reconnect. If you do persist it, the transport keeps the fast-path short-circuit. If you drop it, reconnects open the SSE and close fast on settled sessions. |
118118

119119
### Session cleanup (frontend)

0 commit comments

Comments
 (0)