This cookbook used to describe the route-transition provider. That provider has been removed from the active architecture. The current first-message flow is owned by persistent chat runtimes instead of a temporary route transition. For the full migration plan, seeDocumentation Index
Fetch the complete documentation index at: https://chatjs.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
CHAT_RUNTIME_MIGRATION_PLAN.md at the repo
root.
The Problem
The first-message flow has competing requirements:- A new chat needs a real URL like
/chat/:idor/project/:projectId/chat/:chatId - The submitted message and pending assistant placeholders must survive route changes
- Normal navigation between unrelated chats must not leak state
- Backend queries for persisted chat data must not run before the provisional chat is confirmed
- Streams should continue even when the route tree changes
Current Solution
ChatRuntimeRegistryProvider owns live chat runtime entries. A provisional chat
starts by registering a runtime with:
- the generated chat id
- an owned chat store
- the submitted user message
- pending primary submission data
- parallel request specs
persistenceStatus: "provisional"
MountedChatRuntimes renders controllers outside the route tree, so
streaming continues while navigation changes which chat is visible.
ChatRouteHost is now a view resolver. It decides whether the visible route is
backed by a live runtime store or by persisted query data. Persisted chat and
message queries are enabled only when there is no live runtime or when the live
runtime is confirmed.
Parts Involved
| Part | Responsibility |
|---|---|
parseChatIdFromPathname | Classifies /, /chat/:id, /project/:projectId, /project/:projectId/chat/:chatId, /share/:id, and passthrough routes |
ChatRuntimeRegistryProvider | Owns live runtime entries and provisional/confirmed persistence state |
useChatRuntimeApi | Provides the public runtime boundary for lookup, provisional startup, confirmation, and runtime-targeted sends |
MountedChatRuntimes | Keeps runtime controllers mounted outside route transitions |
useDraftChatId / resetDraftChatId | Generates per-home and per-project draft ids and advances them after promotion |
useStartProvisionalChat | Creates the provisional runtime, adds optimistic messages, and updates browser history |
ChatRouteHost | Chooses live runtime data or persisted query data for the visible route |
ChatSystem | Renders the chat tree against either a route-owned store or an external runtime store |
ChatSync | Owns the transport for a mounted controller and reports data-chatConfirmed |
RuntimeConfirmationController | Invalidates persisted queries and runs secondary parallel requests after confirmation |
/api/chat | Idempotently creates the chat/user/assistant records, streams the primary response, and emits data-chatConfirmed |
/api/chat/prepare | Prepares secondary parallel requests against an already-created chat |
Flow
ChatRouteHostrenders/or/project/:projectIdwith a draft id.MultimodalInputorSuggestedActionscallsbuildDraftChatSubmission.useStartProvisionalChatregisters a provisional runtime, adds the user and pending assistant messages, and callswindow.history.pushState.- The destination route re-renders through
ChatRouteHost. - If a live runtime exists for the destination chat id,
ChatRouteHostuses its store and suppresses route-ownedChatSync. - The mounted runtime’s
ChatSyncsends the primary request and receives stream data. - On
data-chatConfirmed,ChatSyncmarks the runtime confirmed. RuntimeConfirmationControllerinvalidates persisted chat queries and runs secondary parallel requests.- Later route navigation only swaps the visible runtime or persisted data source; it does not own stream lifetime.
Runtime Keys
Draft routes include the draft id in the base key:ChatSystem uses the runtime id
instead. That keeps the visible tree attached to the runtime-owned store without
reintroducing a separate transition key model.
Query Gating
Persisted chat queries should only run when one of these is true:- there is no live runtime for the route chat id
- the live runtime has
persistenceStatus: "confirmed"
chat.getChatById or
chat.getChatMessages before the server has confirmed the chat.
Parallel Responses
The runtime entry carries all parallel response metadata:- the first request is sent by the mounted runtime controller
- pending assistant placeholders are added when the provisional runtime starts
- after confirmation, secondary request specs run from
RuntimeConfirmationController - failed secondary requests are converted from pending placeholders into empty assistant messages with
activeStreamId: null
Related
- URL Routing for the supported route shapes
- Parallel Responses for multi-model response behavior
- Resumable Streams for reconnecting to active streams after refresh