@@ -274,33 +274,54 @@ async def get_or_create_booter(
274274 self .session_booter [target_sandbox_id ] = client
275275 break
276276
277- self .registry .touch_sandbox (target_sandbox_id )
278- self .registry .update_sandbox_status (target_sandbox_id , "running" )
279- self .registry .set_current_sandbox_id (session_id , target_sandbox_id )
277+ await self ._finalize_created_booter (
278+ provider , target_sandbox_id , session_id = session_id , idle_timeout = idle_timeout
279+ )
280+ return self .session_booter [target_sandbox_id ]
281+
282+ async def _finalize_created_booter (
283+ self ,
284+ provider : SandboxProvider ,
285+ sandbox_id : str ,
286+ * ,
287+ session_id : str | None = None ,
288+ idle_timeout : float ,
289+ ) -> None :
290+ """Common post-creation steps: persist, idle cleanup, skill sync, hooks."""
291+ self .registry .touch_sandbox (sandbox_id )
292+ self .registry .update_sandbox_status (sandbox_id , "running" )
293+ if session_id is not None :
294+ self .registry .set_current_sandbox_id (session_id , sandbox_id )
280295 await self .save_registry_async ()
281- self .schedule_idle_cleanup (target_sandbox_id , idle_timeout )
296+ self .schedule_idle_cleanup (sandbox_id , idle_timeout )
282297
283- # Auto-sync skills unless the provider opts out.
298+ # Auto-sync skills unless the provider opts out. Best-effort: a sync
299+ # failure is logged but does not destroy the already-created sandbox.
284300 if getattr (provider , "auto_sync_skills" , True ):
285- booter = self .session_booter [target_sandbox_id ]
286- if hasattr (booter , "shell" ):
287- await self ._sync_skills_to_booter (booter )
301+ booter = self .session_booter .get (sandbox_id )
302+ if booter is not None and hasattr (booter , "shell" ):
303+ try :
304+ await self ._sync_skills_to_booter (booter )
305+ except Exception as sync_err :
306+ logger .warning (
307+ "[Computer] Auto skill sync failed for %s: %s" ,
308+ sandbox_id ,
309+ sync_err ,
310+ )
288311
289312 # Optional lifecycle hook.
290313 if hasattr (provider , "on_sandbox_created" ):
291314 try :
292315 await provider .on_sandbox_created (
293- self .registry .get_sandbox (target_sandbox_id ) or {}
316+ self .registry .get_sandbox (sandbox_id ) or {}
294317 )
295318 except Exception as hook_err :
296319 logger .warning (
297320 "[Computer] on_sandbox_created hook failed for %s: %s" ,
298- target_sandbox_id ,
321+ sandbox_id ,
299322 hook_err ,
300323 )
301324
302- return self .session_booter [target_sandbox_id ]
303-
304325 async def create_sandbox_uncontrolled (
305326 self ,
306327 context : Context ,
@@ -335,10 +356,9 @@ async def create_sandbox_uncontrolled(
335356 raise
336357 setattr (client , "sandbox_id" , sandbox_id )
337358 self .session_booter [sandbox_id ] = client
338- self .registry .touch_sandbox (sandbox_id )
339- self .registry .update_sandbox_status (sandbox_id , "running" )
340- await self .save_registry_async ()
341- self .schedule_idle_cleanup (sandbox_id , idle_timeout )
359+ await self ._finalize_created_booter (
360+ provider , sandbox_id , session_id = None , idle_timeout = idle_timeout
361+ )
342362 return self .registry .get_sandbox (sandbox_id ) or record
343363
344364 async def create_sandbox (
0 commit comments