@@ -88,6 +88,10 @@ type VcsCache = {
8888 ready : Accessor < boolean >
8989}
9090
91+ type ChildOptions = {
92+ bootstrap ?: boolean
93+ }
94+
9195function createGlobalSync ( ) {
9296 const globalSDK = useGlobalSDK ( )
9397 const platform = usePlatform ( )
@@ -127,8 +131,10 @@ function createGlobalSync() {
127131 } )
128132
129133 const children : Record < string , [ Store < State > , SetStoreFunction < State > ] > = { }
134+ const booting = new Map < string , Promise < void > > ( )
135+ const sessionLoads = new Map < string , Promise < void > > ( )
130136
131- function child ( directory : string ) {
137+ function ensureChild ( directory : string ) {
132138 if ( ! directory ) console . error ( "No directory provided" )
133139 if ( ! children [ directory ] ) {
134140 const cache = runWithOwner ( owner , ( ) =>
@@ -163,7 +169,6 @@ function createGlobalSync() {
163169 message : { } ,
164170 part : { } ,
165171 } )
166- bootstrapInstance ( directory )
167172 }
168173
169174 runWithOwner ( owner , init )
@@ -173,11 +178,23 @@ function createGlobalSync() {
173178 return childStore
174179 }
175180
181+ function child ( directory : string , options : ChildOptions = { } ) {
182+ const childStore = ensureChild ( directory )
183+ const shouldBootstrap = options . bootstrap ?? true
184+ if ( shouldBootstrap && childStore [ 0 ] . status === "loading" ) {
185+ void bootstrapInstance ( directory )
186+ }
187+ return childStore
188+ }
189+
176190 async function loadSessions ( directory : string ) {
177- const [ store , setStore ] = child ( directory )
191+ const pending = sessionLoads . get ( directory )
192+ if ( pending ) return pending
193+
194+ const [ store , setStore ] = child ( directory , { bootstrap : false } )
178195 const limit = store . limit
179196
180- return globalSDK . client . session
197+ const promise = globalSDK . client . session
181198 . list ( { directory, roots : true } )
182199 . then ( ( x ) => {
183200 const nonArchived = ( x . data ?? [ ] )
@@ -208,13 +225,23 @@ function createGlobalSync() {
208225 const project = getFilename ( directory )
209226 showToast ( { title : `Failed to load sessions for ${ project } ` , description : err . message } )
210227 } )
228+
229+ sessionLoads . set ( directory , promise )
230+ promise . finally ( ( ) => {
231+ sessionLoads . delete ( directory )
232+ } )
233+ return promise
211234 }
212235
213236 async function bootstrapInstance ( directory : string ) {
214237 if ( ! directory ) return
215- const [ store , setStore ] = child ( directory )
216- const cache = vcsCache . get ( directory )
217- if ( ! cache ) return
238+ const pending = booting . get ( directory )
239+ if ( pending ) return pending
240+
241+ const promise = ( async ( ) => {
242+ const [ store , setStore ] = ensureChild ( directory )
243+ const cache = vcsCache . get ( directory )
244+ if ( ! cache ) return
218245 const sdk = createOpencodeClient ( {
219246 baseUrl : globalSDK . url ,
220247 fetch : platform . fetch ,
@@ -250,98 +277,105 @@ function createGlobalSync() {
250277 config : ( ) => sdk . config . get ( ) . then ( ( x ) => setStore ( "config" , x . data ! ) ) ,
251278 }
252279
253- try {
254- await Promise . all ( Object . values ( blockingRequests ) . map ( ( p ) => retry ( p ) ) )
255- } catch ( err ) {
256- console . error ( "Failed to bootstrap instance" , err )
257- const project = getFilename ( directory )
258- const message = err instanceof Error ? err . message : String ( err )
259- showToast ( { title : `Failed to reload ${ project } ` , description : message } )
260- setStore ( "status" , "partial" )
261- return
262- }
280+ try {
281+ await Promise . all ( Object . values ( blockingRequests ) . map ( ( p ) => retry ( p ) ) )
282+ } catch ( err ) {
283+ console . error ( "Failed to bootstrap instance" , err )
284+ const project = getFilename ( directory )
285+ const message = err instanceof Error ? err . message : String ( err )
286+ showToast ( { title : `Failed to reload ${ project } ` , description : message } )
287+ setStore ( "status" , "partial" )
288+ return
289+ }
263290
264- if ( store . status !== "complete" ) setStore ( "status" , "partial" )
265-
266- Promise . all ( [
267- sdk . path . get ( ) . then ( ( x ) => setStore ( "path" , x . data ! ) ) ,
268- sdk . command . list ( ) . then ( ( x ) => setStore ( "command" , x . data ?? [ ] ) ) ,
269- sdk . session . status ( ) . then ( ( x ) => setStore ( "session_status" , x . data ! ) ) ,
270- loadSessions ( directory ) ,
271- sdk . mcp . status ( ) . then ( ( x ) => setStore ( "mcp" , x . data ! ) ) ,
272- sdk . lsp . status ( ) . then ( ( x ) => setStore ( "lsp" , x . data ! ) ) ,
273- sdk . vcs . get ( ) . then ( ( x ) => {
274- const next = x . data ?? store . vcs
275- setStore ( "vcs" , next )
276- if ( next ?. branch ) cache . setStore ( "value" , next )
277- } ) ,
278- sdk . permission . list ( ) . then ( ( x ) => {
279- const grouped : Record < string , PermissionRequest [ ] > = { }
280- for ( const perm of x . data ?? [ ] ) {
281- if ( ! perm ?. id || ! perm . sessionID ) continue
282- const existing = grouped [ perm . sessionID ]
283- if ( existing ) {
284- existing . push ( perm )
285- continue
291+ if ( store . status !== "complete" ) setStore ( "status" , "partial" )
292+
293+ Promise . all ( [
294+ sdk . path . get ( ) . then ( ( x ) => setStore ( "path" , x . data ! ) ) ,
295+ sdk . command . list ( ) . then ( ( x ) => setStore ( "command" , x . data ?? [ ] ) ) ,
296+ sdk . session . status ( ) . then ( ( x ) => setStore ( "session_status" , x . data ! ) ) ,
297+ loadSessions ( directory ) ,
298+ sdk . mcp . status ( ) . then ( ( x ) => setStore ( "mcp" , x . data ! ) ) ,
299+ sdk . lsp . status ( ) . then ( ( x ) => setStore ( "lsp" , x . data ! ) ) ,
300+ sdk . vcs . get ( ) . then ( ( x ) => {
301+ const next = x . data ?? store . vcs
302+ setStore ( "vcs" , next )
303+ if ( next ?. branch ) cache . setStore ( "value" , next )
304+ } ) ,
305+ sdk . permission . list ( ) . then ( ( x ) => {
306+ const grouped : Record < string , PermissionRequest [ ] > = { }
307+ for ( const perm of x . data ?? [ ] ) {
308+ if ( ! perm ?. id || ! perm . sessionID ) continue
309+ const existing = grouped [ perm . sessionID ]
310+ if ( existing ) {
311+ existing . push ( perm )
312+ continue
313+ }
314+ grouped [ perm . sessionID ] = [ perm ]
286315 }
287- grouped [ perm . sessionID ] = [ perm ]
288- }
289316
290- batch ( ( ) => {
291- for ( const sessionID of Object . keys ( store . permission ) ) {
292- if ( grouped [ sessionID ] ) continue
293- setStore ( "permission" , sessionID , [ ] )
294- }
295- for ( const [ sessionID , permissions ] of Object . entries ( grouped ) ) {
296- setStore (
297- "permission" ,
298- sessionID ,
299- reconcile (
300- permissions
301- . filter ( ( p ) => ! ! p ?. id )
302- . slice ( )
303- . sort ( ( a , b ) => a . id . localeCompare ( b . id ) ) ,
304- { key : "id" } ,
305- ) ,
306- )
307- }
308- } )
309- } ) ,
310- sdk . question . list ( ) . then ( ( x ) => {
311- const grouped : Record < string , QuestionRequest [ ] > = { }
312- for ( const question of x . data ?? [ ] ) {
313- if ( ! question ?. id || ! question . sessionID ) continue
314- const existing = grouped [ question . sessionID ]
315- if ( existing ) {
316- existing . push ( question )
317- continue
317+ batch ( ( ) => {
318+ for ( const sessionID of Object . keys ( store . permission ) ) {
319+ if ( grouped [ sessionID ] ) continue
320+ setStore ( "permission" , sessionID , [ ] )
321+ }
322+ for ( const [ sessionID , permissions ] of Object . entries ( grouped ) ) {
323+ setStore (
324+ "permission" ,
325+ sessionID ,
326+ reconcile (
327+ permissions
328+ . filter ( ( p ) => ! ! p ?. id )
329+ . slice ( )
330+ . sort ( ( a , b ) => a . id . localeCompare ( b . id ) ) ,
331+ { key : "id" } ,
332+ ) ,
333+ )
334+ }
335+ } )
336+ } ) ,
337+ sdk . question . list ( ) . then ( ( x ) => {
338+ const grouped : Record < string , QuestionRequest [ ] > = { }
339+ for ( const question of x . data ?? [ ] ) {
340+ if ( ! question ?. id || ! question . sessionID ) continue
341+ const existing = grouped [ question . sessionID ]
342+ if ( existing ) {
343+ existing . push ( question )
344+ continue
345+ }
346+ grouped [ question . sessionID ] = [ question ]
318347 }
319- grouped [ question . sessionID ] = [ question ]
320- }
321348
322- batch ( ( ) => {
323- for ( const sessionID of Object . keys ( store . question ) ) {
324- if ( grouped [ sessionID ] ) continue
325- setStore ( "question" , sessionID , [ ] )
326- }
327- for ( const [ sessionID , questions ] of Object . entries ( grouped ) ) {
328- setStore (
329- "question" ,
330- sessionID ,
331- reconcile (
332- questions
333- . filter ( ( q ) => ! ! q ?. id )
334- . slice ( )
335- . sort ( ( a , b ) => a . id . localeCompare ( b . id ) ) ,
336- { key : "id" } ,
337- ) ,
338- )
339- }
340- } )
341- } ) ,
342- ] ) . then ( ( ) => {
343- setStore ( "status" , "complete" )
349+ batch ( ( ) => {
350+ for ( const sessionID of Object . keys ( store . question ) ) {
351+ if ( grouped [ sessionID ] ) continue
352+ setStore ( "question" , sessionID , [ ] )
353+ }
354+ for ( const [ sessionID , questions ] of Object . entries ( grouped ) ) {
355+ setStore (
356+ "question" ,
357+ sessionID ,
358+ reconcile (
359+ questions
360+ . filter ( ( q ) => ! ! q ?. id )
361+ . slice ( )
362+ . sort ( ( a , b ) => a . id . localeCompare ( b . id ) ) ,
363+ { key : "id" } ,
364+ ) ,
365+ )
366+ }
367+ } )
368+ } ) ,
369+ ] ) . then ( ( ) => {
370+ setStore ( "status" , "complete" )
371+ } )
372+ } ) ( )
373+
374+ booting . set ( directory , promise )
375+ promise . finally ( ( ) => {
376+ booting . delete ( directory )
344377 } )
378+ return promise
345379 }
346380
347381 const unsub = globalSDK . event . listen ( ( e ) => {
0 commit comments