@@ -393,6 +393,7 @@ export class TriggerChatTransport implements ChatTransport<UIMessage> {
393393
394394 private sessions : Map < string , ChatSessionState > = new Map ( ) ;
395395 private activeStreams : Map < string , AbortController > = new Map ( ) ;
396+ private pendingPreloads : Map < string , Promise < void > > = new Map ( ) ;
396397
397398 constructor ( options : TriggerChatTransportOptions ) {
398399 this . taskId = options . task ;
@@ -800,26 +801,38 @@ export class TriggerChatTransport implements ChatTransport<UIMessage> {
800801 // Don't preload if session already exists
801802 if ( this . sessions . get ( chatId ) ?. runId ) return ;
802803
803- const mergedMetadata =
804- this . defaultMetadata || options ?. metadata
805- ? { ...( this . defaultMetadata ?? { } ) , ...( options ?. metadata ?? { } ) }
806- : undefined ;
804+ // Deduplicate concurrent preload calls (e.g. React strict mode double-firing effects)
805+ const pending = this . pendingPreloads . get ( chatId ) ;
806+ if ( pending ) return pending ;
807+
808+ const doPreload = async ( ) => {
809+ const mergedMetadata =
810+ this . defaultMetadata || options ?. metadata
811+ ? { ...( this . defaultMetadata ?? { } ) , ...( options ?. metadata ?? { } ) }
812+ : undefined ;
813+
814+ const payload = {
815+ messages : [ ] as never [ ] ,
816+ chatId,
817+ trigger : "preload" as const ,
818+ metadata : mergedMetadata ,
819+ ...( options ?. idleTimeoutInSeconds !== undefined
820+ ? { idleTimeoutInSeconds : options . idleTimeoutInSeconds }
821+ : { } ) ,
822+ } ;
807823
808- const payload = {
809- messages : [ ] as never [ ] ,
810- chatId,
811- trigger : "preload" as const ,
812- metadata : mergedMetadata ,
813- ...( options ?. idleTimeoutInSeconds !== undefined
814- ? { idleTimeoutInSeconds : options . idleTimeoutInSeconds }
815- : { } ) ,
816- } ;
824+ const { runId, publicAccessToken } = await this . triggerNewRun ( chatId , payload , "preload" ) ;
817825
818- const { runId, publicAccessToken } = await this . triggerNewRun ( chatId , payload , "preload" ) ;
826+ const newSession : ChatSessionState = { runId, publicAccessToken } ;
827+ this . sessions . set ( chatId , newSession ) ;
828+ this . notifySessionChange ( chatId , newSession ) ;
829+ } ;
819830
820- const newSession : ChatSessionState = { runId, publicAccessToken } ;
821- this . sessions . set ( chatId , newSession ) ;
822- this . notifySessionChange ( chatId , newSession ) ;
831+ const promise = doPreload ( ) . finally ( ( ) => {
832+ this . pendingPreloads . delete ( chatId ) ;
833+ } ) ;
834+ this . pendingPreloads . set ( chatId , promise ) ;
835+ return promise ;
823836 }
824837
825838 private async resolveAccessToken ( params : ResolveChatAccessTokenParams ) : Promise < string > {
0 commit comments