@@ -65,6 +65,8 @@ export type UseLoadTranscriptOptions = {
6565 * the loaded transcript, so the live subscription opens just past the
6666 * persisted history instead of replaying it. Only applies once the
6767 * transport knows the session (from `sessions` or after `start`).
68+ * For a blocked session, a fresh load with a newer saved cursor also clears
69+ * the transcript reload requirement. A stale result leaves sends blocked.
6870 */
6971 transport ?: TriggerChatTransport ;
7072 /** Page size passed to the action. */
@@ -77,14 +79,17 @@ export type UseLoadTranscriptOptions = {
7779 * history. Applied to the session now if it exists, otherwise held by the
7880 * transport until the session is created, so a load that resolves before the
7981 * session exists still moves the cursor. A no-op when the transcript carries
80- * no cursor. Returns whether a cursor was provided.
82+ * no cursor. A captured recovery callback replaces ordinary cursor seeding.
83+ * Returns whether the cursor was accepted.
8184 */
8285export function seedTranscriptCursor (
8386 transport : Pick < TriggerChatTransport , "seedResumeCursor" > ,
8487 chatId : string ,
85- cursors : { lastOutEventId ?: string } | undefined
88+ cursors : { lastOutEventId ?: string } | undefined ,
89+ completeRecovery ?: ( lastEventId : string | undefined ) => boolean
8690) : boolean {
8791 const lastEventId = cursors ?. lastOutEventId ;
92+ if ( completeRecovery ) return completeRecovery ( lastEventId ) ;
8893 if ( ! lastEventId ) return false ;
8994 transport . seedResumeCursor ( chatId , lastEventId ) ;
9095 return true ;
@@ -130,8 +135,7 @@ export function useLoadTranscript<TUIMessage extends UIMessage = UIMessage>(
130135
131136 const loadRef = useRef ( load ) ;
132137 loadRef . current = load ;
133- const transportRef = useRef ( options ?. transport ) ;
134- transportRef . current = options ?. transport ;
138+ const transport = options ?. transport ;
135139 const limit = options ?. limit ;
136140
137141 useEffect ( ( ) => {
@@ -140,13 +144,17 @@ export function useLoadTranscript<TUIMessage extends UIMessage = UIMessage>(
140144 return ;
141145 }
142146 let cancelled = false ;
147+ const completeRecovery = transport ?. prepareTranscriptRecovery ( chatId ) ;
143148 setState ( { chatId, messages : [ ] , isLoading : true , error : undefined , nextCursor : undefined } ) ;
144149 loadRef
145150 . current ( { chatId, ...( limit !== undefined ? { limit } : { } ) } )
146151 . then ( ( result ) => {
147152 if ( cancelled ) return ;
148- if ( transportRef . current ) {
149- seedTranscriptCursor ( transportRef . current , chatId , result . cursors ) ;
153+ if ( transport ) {
154+ const seeded = seedTranscriptCursor ( transport , chatId , result . cursors , completeRecovery ) ;
155+ if ( completeRecovery && ! seeded ) {
156+ throw new Error ( "The loaded transcript is not current. Reload the chat again." ) ;
157+ }
150158 }
151159 setState ( {
152160 chatId,
@@ -164,7 +172,7 @@ export function useLoadTranscript<TUIMessage extends UIMessage = UIMessage>(
164172 return ( ) => {
165173 cancelled = true ;
166174 } ;
167- } , [ chatId , limit ] ) ;
175+ } , [ chatId , limit , transport ] ) ;
168176
169177 return {
170178 messages : state . chatId === chatId ? state . messages : [ ] ,
0 commit comments