+
    Ӄi!?                     b   R t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	 ^ RI
HtHtHt ^ RIHt ^ RIHtHtHt ]P(                  ! ]4      t]! 4       R,          tRtR	 R
 ltR R ltR R ltR R ltR R ltRR R lltR R lt ! R R4      t ] ! R R4      4       t! ! R R]4      t"R# )zModal cloud execution environment using the native Modal SDK directly.

Uses ``Sandbox.create()`` + ``Sandbox.exec()`` instead of the older runtime
wrapper, while preserving Hermes' persistent snapshot behavior across sessions.
N)	dataclass)Path)AnyDictOptional)get_hermes_home)BaseModalExecutionEnvironmentModalExecStartPreparedModalExeczmodal_snapshots.jsondirectc                F    V ^8  d   QhR\         \        \        3,          /# )   returnr   str)formats   "5/home/ubuntu/hermes-agent/tools/environments/modal.py__annotate__r      s      c3h     c                     \         P                  4       '       d*    \        P                  ! \         P	                  4       4      # / #   \
         d     / # i ; i)z#Load snapshot ID mapping from disk.)_SNAPSHOT_STOREexistsjsonloads	read_text	Exception r   r   _load_snapshotsr      sN    	::o779:: I  	I	s   'A AAc                J    V ^8  d   QhR\         \        \        3,          RR/# )r   datar   Nr   )r   s   "r   r   r   '   s"     ; ;$sCx. ;T ;r   c                    \         P                  P                  RRR7       \         P                  \        P
                  ! V ^R7      4       R# )z$Persist snapshot ID mapping to disk.T)parentsexist_ok)indentN)r   parentmkdir
write_textr   dumps)r   s   &r   _save_snapshotsr(   '   s4       =tzz$q9:r   c                0    V ^8  d   QhR\         R\         /# )r   task_idr   r   )r   s   "r   r   r   -   s     5 5# 5# 5r   c                     \          R V  2# ):)_DIRECT_SNAPSHOT_NAMESPACE)r*   s   &r   _direct_snapshot_keyr/   -   s    ()7)44r   c                `    V ^8  d   QhR\         R\        \         R,          \        3,          /# )r   r*   r   N)r   tuplebool)r   s   "r   r   r   1   s(      S U3:t;K5L r   c                    \        4       p\        V 4      pVP                  V4      p\        V\        4      '       d   V'       d   VR3# VP                  V 4      p\        V\        4      '       d   V'       d   VR3# R# )zDReturn a snapshot id and whether it came from the legacy key format.FT)NF)r   r/   get
isinstancer   )r*   	snapshotsnamespaced_keysnapshot_idlegacy_snapshot_ids   &    r   _get_snapshot_restore_candidater:   1   sm    !I)'2N--/K+s##E!!"w/$c**/A!4''r   c                4    V ^8  d   QhR\         R\         RR/# )r   r*   r8   r   Nr+   )r   s   "r   r   r   A   s!      C c d r   c                n    \        4       pW\        V 4      &   VP                  V R4       \        V4       R# )z@Persist the direct Modal snapshot id under the direct namespace.N)r   r/   popr(   )r*   r8   r6   s   && r   _store_direct_snapshotr>   A   s.    !I/:"7+,MM'4 Ir   c                B    V ^8  d   QhR\         R\         R,          RR/# )r   r*   r8   Nr   r+   )r   s   "r   r   r   I   s%     # #S #sTz #T #r   c                    \        4       pRp\        V 4      V 3 F:  pVP                  V4      pVf   K  Ve	   WQ8X  g   K&  VP                  VR4       RpK<  	  V'       d   \	        V4       R# R# )zGRemove direct Modal snapshot entries for a task, including legacy keys.FNT)r   r/   r4   r=   r(   )r*   r8   r6   updatedkeyvalues   &&    r   _delete_direct_snapshotrD   I   si    !IG$W-w7c"=%"6MM#t$G 8 	" r   c                0    V ^8  d   QhR\         R\         /# )r   
image_specr   r   )r   s   "r   r   r   Z   s      S S r   c                    ^ RI p\        V \        4      '       g   V # V P                  R4      '       d   VP                  P                  V 4      # VP                  P                  V R.R7      # )zEConvert registry references or snapshot ids into Modal image objects.Nzim-zRUN rm -rf /usr/local/lib/python*/site-packages/pip* 2>/dev/null; python -m ensurepip --upgrade --default-pip 2>/dev/null || true)setup_dockerfile_commands)modalr5   r   
startswithImagefrom_idfrom_registry)rF   _modals   & r   _resolve_modal_imagerP   Z   sh    j#&&U##||##J//<<%%N#
 &  r   c                   F   a  ] tR t^mt o RtR tR tR tR	R ltR t	Rt
V tR# )
_AsyncWorkerzEBackground thread with its own event loop for async-safe Modal calls.c                V    R V n         R V n        \        P                  ! 4       V n        R # N)_loop_thread	threadingEvent_startedselfs   &r   __init___AsyncWorker.__init__p   s    :>
37!)r   c                    \         P                  ! V P                  R R7      V n        V P                  P	                  4        V P
                  P                  ^R7       R# )TtargetdaemontimeoutN)rW   Thread	_run_looprV   startrY   waitrZ   s   &r   rf   _AsyncWorker.startu   s?     ''t~~dK2&r   c                    \         P                  ! 4       V n        \         P                  ! V P                  4       V P                  P                  4        V P                  P                  4        R # rT   )asyncionew_event_looprU   set_event_looprY   setrun_foreverrZ   s   &r   re   _AsyncWorker._run_loopz   sD    ++-
tzz*

 r   c                    V P                   e!   V P                   P                  4       '       d   \        R4      h\        P                  ! WP                   4      pVP                  VR7      # )NzAsyncWorker loop is not runningrb   )rU   	is_closedRuntimeErrorrj   run_coroutine_threadsaferesult)r[   cororc   futures   &&& r   run_coroutine_AsyncWorker.run_coroutine   sO    ::!5!5!7!7@AA11$

C}}W}--r   c                (   V P                   '       dP   V P                   P                  4       '       d0   V P                   P                  V P                   P                  4       V P                  '       d   V P                  P                  ^
R7       R# R# )
   rb   N)rU   
is_runningcall_soon_threadsafestoprV   joinrZ   s   &r   r}   _AsyncWorker.stop   sZ    :::$**//11JJ++DJJOO<<<<LLb) r   )rU   rY   rV   N)iX  )__name__
__module____qualname____firstlineno____doc__r\   rf   re   rw   r}   __static_attributes____classdictcell____classdict__s   @r   rR   rR   m   s&     O*
'
!.* *r   rR   c                   ,   a  ] tR t^t o V 3R ltRtV tR# )_DirectModalExecHandlec                \   < V ^8  d   Qh/ S[ P                  ;R&   S[S[S[3,          ;R&   # )r   threadresult_holder)rW   rd   r   r   r   )r   r   s   "r   r   #_DirectModalExecHandle.__annotate__   s+       S>! r   r   N)r   r   r   r   __annotate_func__r   r   r   s   @r   r   r      s      r   r   c                      a a ] tR t^t oRtRtRtRtRtRV3R lV 3R lllt	V3R lR	 lt
V3R
 lR ltV3R lR ltV3R lR ltV3R lR ltV3R lR ltR tRtVtV ;t# )ModalEnvironmentz1Modal cloud execution via native Modal sandboxes.heredocg?z0[Command interrupted - Modal sandbox terminated]zModal execution errorc                d   < V ^8  d   QhRS[ RS[ RS[RS[S[S[ S[3,          ,          RS[RS[ /# )r   imagecwdrc   modal_sandbox_kwargspersistent_filesystemr*   )r   intr   r   r   r2   )r   r   s   "r   r   ModalEnvironment.__annotate__   sd     JG JGJG JG 	JG
 'tCH~6JG  $JG JGr   c                  <aaa \         SV `  W#R 7       WPn        W`n        Wn        RV n        RV n        \        4       V n        / V n	        \        T;'       g    / 4      oRpRpV P                  '       d=   \        V P                  4      w  rxV'       d   \        P                  RVR,          4       ^ RIo. o ^ RIHp	Hp
Hp V	! 4        Fb  pSP'                  SP(                  P+                  VR,          VR,          R7      4       \        P                  R	VR,          VR,          4       Kd  	  V
! 4       pV F=  pSP'                  SP(                  P+                  VR,          VR,          R7      4       K?  	  V'       d    \        P                  R
\-        V4      4       V! 4       pV F=  pSP'                  SP(                  P+                  VR,          VR,          R7      4       K?  	  V'       d    \        P                  R\-        V4      4       T P                  P3                  4        R TTT3R llp T;'       g    Tp \5        T4      pT P                  P7                  T! T4      RR7      w  T n        T n        T'       d?   T'       d7   \9        T P                  T4       \        P                  RT P                  4       \        P                  RT P                  4       R#   \.         d!   p\        P1                  RT4        Rp?LRp?ii ; i  \.         d   pT'       g   h \        P;                  RTR,          T4       \=        T P                  T4       \5        T4      pT P                  P7                  T! T4      RR7      w  T n        T n         Rp?LRp?ii ; i  \.         d    T P                  P?                  4        h i ; i))r   rc   NFz!Modal: restoring from snapshot %sN   Nget_credential_file_mountsiter_skills_filesiter_cache_files	host_pathcontainer_path)remote_pathz#Modal: mounting credential %s -> %szModal: mounting %d skill fileszModal: mounting %d cache filesz0Modal: could not load credential file mounts: %sc                $    V ^8  d   QhR\         /# )r   rF   rG   )r   s   "r   r   /ModalEnvironment.__init__.<locals>.__annotate__   s     	  	 c 	 r   c                   <"   SP                   P                  P                  R RR7      G Rj  xL
 p\        S4      pS'       d1   \	        VP                  R. 4      4      pVP                  S4       W2R&   SP                  P                  P                  ! R	RV RVR\        VP                  RR4      4      /VB G Rj  xL
 pW3#  L L	5i)
zhermes-agentT)create_if_missingNmountsr   apprc   i  )sleepinfinity)
Applookupaiodictlistr=   extendSandboxcreater   )rF   r   create_kwargsexisting_mountssandboxrO   cred_mountssandbox_kwargss   &    r   _create_sandbox2ModalEnvironment.__init__.<locals>._create_sandbox   s     

))--nPT-UUC 0M"&}'8'82'F"G&&{3*9h'"NN1155  ! 	
 M--i>?   G < Vs"   +C
CBC
>C?C
C
i,  rb   z1Modal: migrated legacy snapshot entry for task %szBModal: failed to restore snapshot %s, retrying with base image: %sz Modal: sandbox created (task=%s)) superr\   _persistent_task_id_base_image_sandbox_apprR   _worker_synced_filesr   r:   loggerinforJ   tools.credential_filesr   r   r   appendMountfrom_local_filelenr   debugrf   rP   rw   r>   warningrD   r}   )r[   r   r   rc   r   r   r*   restored_snapshot_idrestored_from_legacy_keyr   r   r   mount_entryskills_filesentrycache_fileser   target_image_speceffective_imageexc
base_imagerO   r   r   	__class__s   &&&&&&&               @@@r   r\   ModalEnvironment.__init__   s]    	S20 	#~/1288b9##( =\>:  $?AUVYAZ[.	P   :;""LL00#K0$/0@$A 1  9, 01  < -.L%""LL00k*$)*:$; 1  & <c,>OP
 +,K$""LL00k*$)*:$; 1  % <c+>NO 		  	 "$	 4 = =
 #77H"I+/<<+E+E#O4 ,F ,(	4=( (,D*4==:NOKKK 	6Fy  	PLLKQOO	PD  +X(-
 (7KL1%8
+/<<+E+E#J/ ,F ,(	4==,  	LL	si   9CK( A0K( 3K( 8N& N& ;L  N& 6N& (L3LLN#!A8NN& N##N& &'Oc                ,   < V ^8  d   QhRS[ RS[ RS[/# )r   r   r   r   )r   r2   )r   r   s   "r   r   r   '  s"      s C D r   c                  a a \        V4      p VP                  4       pVP                  VP                  3pS P
                  P                  T4      T8X  d   R#  TP                  4       p^ RI	pTP                  T4      P                  R4      p\        \        T4      P                  4      p	R\        P                  ! T	4       R\        P                  ! T4       R\        P                  ! T4       2oTT 3R lp
S P                   P#                  T
! 4       ^R7       TS P
                  T&   R	#   \         d     R# i ; i  \         d     R# i ; i)
zGPush a single file into the sandbox if changed. Returns True if synced.FNasciiz	mkdir -p z	 && echo z | base64 -d > c                     <"   SP                   P                  P                  R RS4      G Rj  xL
 p V P                  P                  4       G Rj  xL
  R#  L( L5i)bash-cN)r   execr   rg   )proccmdr[   s    r   _write6ModalEnvironment._push_file_to_sandbox.<locals>._write@  sC     ++//cBBD))--/!! C!s!   +AA!AAAArb   T)r   statst_mtimest_sizeOSErrorr   r4   
read_bytesr   base64	b64encodedecoder   r$   shlexquoter   rw   )r[   r   r   hpr   file_keycontentr   b64container_dirr   r   s   f&&        @r   _push_file_to_sandbox&ModalEnvironment._push_file_to_sandbox'  s+   )_	779Dt||4H !!.1X=	mmoG 	w'..w7D0778M23 4KK$%_U[[5P4QS 	
	" 	""68R"8-5>*3  		  		s#   (D- D? -D<;D<?EEc                   < V ^8  d   QhRR/# r   r   Nr   )r   r   s   "r   r   r   H  s     ; ;T ;r   c                l    ^ RI HpHpHp V! 4        FH  pV P	                  VR,          VR,          4      '       g   K+  \
        P                  RVR,          4       KJ  	  V! 4        FH  pV P	                  VR,          VR,          4      '       g   K+  \
        P                  RVR,          4       KJ  	  V! 4        FH  pV P	                  VR,          VR,          4      '       g   K+  \
        P                  RVR,          4       KJ  	  R#   \         d"   p\
        P                  RT4        Rp?R# Rp?ii ; i)	u1  Push credential, skill, and cache files into the running sandbox.

Runs before each command. Uses mtime+size caching so only changed
files are pushed (~13μs overhead in the no-op case).  Cache files
are especially important here — new uploads/screenshots may appear
mid-session after sandbox creation.
r   r   r   zModal: synced credential %szModal: synced skill file %szModal: synced cache file %szModal: file sync failed: %sN)r   r   r   r   r   r   r   r   )r[   r   r   r   r   r   s   &     r   _sync_filesModalEnvironment._sync_filesH  s    	;  45--eK.@%HXBYZZLL!>FV@WX 6 +,--eK.@%HXBYZZLL!>FV@WX - *+--eK.@%HXBYZZLL!>FV@WX ,  	;LL6::	;s*   7D AD AD $!D D3D..D3c                   < V ^8  d   QhRR/# r   r   )r   r   s   "r   r   r   e  s       r   c                &    V P                  4        R # rT   )r   rZ   s   &r   _before_execute ModalEnvironment._before_executee  s    r   c                &   < V ^8  d   QhRS[ RS[/# )r   preparedr   )r
   r	   )r   r   s   "r   r   r   h  s      "d "d*; "d "dr   c                  a aaa R \         P                  ! SP                  4       RSP                   2oRRRR/oVVVV 3R lp\        P
                  ! VRR7      pVP                  4        \        \        VSR7      R	7      # )
zcd z && rC   Nerrorc                     <  VVV3R  lp SP                   P                  V ! 4       SP                  ^,           R7      SR&   R#   \         d   pTSR&    Rp?R# Rp?ii ; i)c                    <"   SP                   P                  P                  R RSSP                  R7      G Rj  xL
 p V P                  P
                  P                  4       G Rj  xL
 pV P                  P
                  P                  4       G Rj  xL
 pV P                  P                  4       G Rj  xL
 p\        V\        4      '       d   VP                  RRR7      p\        V\        4      '       d   VP                  RRR7      pTpV'       d   V'       d   V RV 2MTpSP                  WC4      #  L L L L5i)r   r   rb   Nzutf-8replace)errors
)r   r   r   rc   stdoutreadstderrrg   r5   bytesr   _result)processr  r	  	exit_codeoutputfull_commandr   r[   s        r   _do_executeEModalEnvironment._start_modal_exec.<locals>._run.<locals>._do_executen  s    $(MM$6$6$:$:$ ( 0 0	 %; % G $+>>#6#6#:#:#<<F#*>>#6#6#:#:#<<F&-ll&6&6&8 8I!&%00!'wy!I!&%00!'wy!I#F:@F82fX!6f<<::! =< 8sR   7D?D7+D?&D9'+D?D;!D?4D=5AD?D?D?9D?;D?=D?rb   rC   r  N)r   rw   rc   r   )r  r   r  r   r   r[   s     r   _run0ModalEnvironment._start_modal_exec.<locals>._runl  s]    +;& *.)C)CM$,,r1 *D *g&  +)*g&+s   >A AAATr_   )r   r   )handle)	r   r   r   commandrW   rd   rf   r	   r   )r[   r   r  tr  r   s   ff  @@r   _start_modal_exec"ModalEnvironment._start_modal_exech  ss    U[[67tH<L<L;MN $6	+ 	+8 D6		%;1Ta%bccr   c                4   < V ^8  d   QhRS[ RS[R,          /# r   r  r   N)r   r   )r   r   s   "r   r   r     s      - -'= -$+ -r   c                    VP                   P                  4       '       d   R # VP                  R,          '       d&   V P                  RVP                  R,           24      # VP                  R,          # )Nr  zModal execution error: rC   )r   is_aliver   _error_resultr[   r  s   &&r   _poll_modal_exec!ModalEnvironment._poll_modal_exec  s`    ==!!##((%%(?@T@TU\@]?^&_``##G,,r   c                $   < V ^8  d   QhRS[ RR/# r  )r   )r   r   s   "r   r   r     s     
 
)? 
D 
r   c                    V P                   P                  V P                  P                  P	                  4       ^R7       R# )   rb   N)r   rw   r   	terminater   r  s   &&r   _cancel_modal_exec#ModalEnvironment._cancel_modal_exec  s2    ""MM##'') 	# 	
r   c                  a  S P                   f   R# S P                  '       dq    V 3R lp S P                  P                  V! 4       ^<R7      pV'       d?   \        S P                  V4       \        P                  RVR,          S P                  4        S P                  P                  S P                   P                  P                  4       ^R7       S P                  P                  4        RS n         RS n        R#   \         d    Rp Li ; i  \         d!   p\        P                  RT4        Rp?LRp?ii ; i  \         d     Lxi ; i  S P                  P                  4        RS n         RS n        i ; i)z>Snapshot the filesystem (if persistent) then stop the sandbox.Nc                     <"   SP                   P                  P                  4       G R j  xL
 p V P                  #  L5irT   )r   snapshot_filesystemr   	object_id)imgr[   s    r   	_snapshot+ModalEnvironment.cleanup.<locals>._snapshot  s1      $ A A E E GGC==( Hs   (><>rb   z/Modal: saved filesystem snapshot %s for task %sr   z%Modal: filesystem snapshot failed: %s)r   r   r   rw   r   r>   r   r   r   r   r$  r   r}   r   )r[   r,  r8   r   s   f   r   cleanupModalEnvironment.cleanup  s=   == K)'"&,,"<"<Y[RT"<"UK *4==+FKKI#C(
	LL&&''++- '  LL DMDI/ ! '"&K'  KFJJK  		 LL DMDIsd   D "C> D >D ?D? >DD DD D<D77D<?E
E EE *E:)r   r   r   r   r   r   r   )z/root<   NTdefault)r   r   r   r   r   _stdin_mode_poll_interval_seconds_interrupt_output_unexpected_error_prefixr\   r   r   r   r  r  r%  r.  r   r   __classcell__)r   r   s   @@r   r   r      sx     ;K J6JG JGX B; ;: "d "dH- -
 
$ $r   r   rT   )#r   rj   r   loggingr   rW   dataclassesr   pathlibr   typingr   r   r   hermes_constantsr   tools.environments.modal_commonr   r	   r
   	getLoggerr   r   r   r.   r   r(   r/   r:   r>   rD   rP   rR   r   r   r   r   r   <module>r>     s         !  & & ,  
		8	$!#&<<% ;5 #"&* *@ " " "
j4 jr   