§
    ‰eîi$w  ã                   ó´  — d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZmZ ddlmZ ddlmZmZmZ ddlmZ ddlmZ  ej        e¦  «        Z e ej        d¦  «        ¦  «        Zere                     ej        ¦  «          ej         ¦   «         Z!d	ee"gdf         dz  d
dfd„Z#d
ee"gdf         dz  fd„Z$de%de"d
dfd„Z&d
efd„Z'dej(        de"d
dfd„Z)	 d$de*e"         de"dz  d
ej(        fd„Z+ded
e%fd„Z,dede%d
dfd„Z-de"d
e.e/e0f         dz  fd„Z1 G d„ de¦  «        Z2 G d„ d¦  «        Z3d e"d
e"fd!„Z4 G d"„ d#e¦  «        Z5dS )%aC  Base class for all Hermes execution environment backends.

Unified spawn-per-call model: every command spawns a fresh ``bash -c`` process.
A session snapshot (env vars, functions, aliases) is captured once at init and
re-sourced before each command. CWD persists via in-band stdout markers (remote)
or a temp file (local).
é    N)ÚABCÚabstractmethod)ÚPath)ÚIOÚCallableÚProtocol)Úget_hermes_home)Úis_interruptedÚHERMES_DEBUG_INTERRUPTÚcbÚreturnc                 ó   — | t           _        dS )z>Register a callback that _wait_for_process fires periodically.N)Ú_activity_callback_localÚcallback)r   s    ú9/home/div/.hermes/hermes-agent/tools/environments/base.pyÚset_activity_callbackr   .   s   € à(*ÕÔ%Ð%Ð%ó    c                  ó.   — t          t          dd ¦  «        S )Nr   )Úgetattrr   © r   r   Ú_get_activity_callbackr   3   s   € ÝÕ+¨Z¸Ñ>Ô>Ð>r   ÚstateÚlabelc                 ó  — t          j        ¦   «         }|                      dd¦  «        }|| d         z
  |k     rdS || d<   	 t          ¦   «         }|r+t	          || d         z
  ¦  «        } ||› d|› d�¦  «         dS dS # t
          $ r Y dS w xY w)a_  Fire the activity callback at most once every ``state['interval']`` seconds.

    *state* must contain ``last_touch`` (monotonic timestamp) and ``start``
    (monotonic timestamp of the operation start).  An optional ``interval``
    key overrides the default 10 s cadence.

    Swallows all exceptions so callers don't need their own try/except.
    Úintervalg      $@Ú
last_touchNÚstartz (z
s elapsed))ÚtimeÚ	monotonicÚgetr   ÚintÚ	Exception)r   r   Únowr   r   Úelapseds         r   Útouch_activity_if_duer%   7   sË   € õ Œ.Ñ
Ô
€CØ�yŠy˜ TÑ*Ô*€HØ
ˆU�<Ô Ñ  8Ò+Ð+ØˆØ€Eˆ,ÑðÝ#Ñ%Ô%ˆØð 	0Ý˜#  g¤Ñ.Ñ/Ô/ˆGØˆB�%Ð.Ð.˜7Ð.Ð.Ð.Ñ/Ô/Ð/Ð/Ð/ð	0ð 	0øõ ð ð ð Øˆˆðøøøs   Á9A> Á>
BÂBc                  ó¢   — t          j        d¦  «        } | rt          | ¦  «        }nt          ¦   «         dz  }|                     dd¬¦  «         |S )zÇReturn the host-side root for all sandbox storage (Docker workspaces,
    Singularity overlays/SIF cache, etc.).

    Configurable via TERMINAL_SANDBOX_DIR. Defaults to {HERMES_HOME}/sandboxes/.
    ÚTERMINAL_SANDBOX_DIRÚ	sandboxesT©ÚparentsÚexist_ok)ÚosÚgetenvr   r	   Úmkdir)ÚcustomÚps     r   Úget_sandbox_dirr1   Q   sT   € õ ŒYÐ-Ñ.Ô.€FØð ,Ý�‰LŒLˆˆåÑÔ Ñ+ˆØ‡G‚G�D 4€GÑ(Ô(Ð(Ø€Hr   ÚprocÚdatac                 óf   ‡ ‡— ˆˆ fd„}t          j        |d¬¦  «                             ¦   «          dS )zMWrite *data* to proc.stdin on a daemon thread to avoid pipe-buffer deadlocks.c                  ó    •— 	 ‰j                              ‰ ¦  «         ‰j                              ¦   «          d S # t          t          f$ r Y d S w xY w©N)ÚstdinÚwriteÚcloseÚBrokenPipeErrorÚOSError)r3   r2   s   €€r   Ú_writez_pipe_stdin.<locals>._writeh   s_   ø€ ð	ØŒJ×Ò˜TÑ"Ô"Ð"ØŒJ×ÒÑÔÐÐÐøÝ¥Ð)ð 	ð 	ð 	ØˆDˆDð	øøøs   ƒ38 ¸AÁAT©ÚtargetÚdaemonN)Ú	threadingÚThreadr   )r2   r3   r<   s   `` r   Ú_pipe_stdinrB   e   sL   øø€ ðð ð ð ð ð õ Ô˜F¨4Ð0Ñ0Ô0×6Ò6Ñ8Ô8Ð8Ð8Ð8r   ÚcmdÚ
stdin_datac                 ó°   — t          j        | ft           j        t           j        |�t           j        nt           j        ddœ|¤Ž}|�t          ||¦  «         |S )a  Spawn a subprocess with standard stdout/stderr/stdin setup.

    If *stdin_data* is provided, writes it asynchronously via :func:`_pipe_stdin`.
    Backends with special Popen needs (e.g. local's ``preexec_fn``) can bypass
    this and call :func:`_pipe_stdin` directly.
    NT)ÚstdoutÚstderrr7   Útext)Ú
subprocessÚPopenÚPIPEÚSTDOUTÚDEVNULLrB   )rC   rD   Úkwargsr2   s       r   Ú_popen_bashrO   r   si   € õ ÔØðåŒÝÔ Ø!+Ð!7�jŒoˆo½ZÔ=OØðð ð ðð €Dð ÐÝ�D˜*Ñ%Ô%Ð%Ø€Kr   Úpathc                 óœ   — |                       ¦   «         r7	 t          j        |                      ¦   «         ¦  «        S # t          $ r Y nw xY wi S )z:Load a JSON file as a dict, returning ``{}`` on any error.)ÚexistsÚjsonÚloadsÚ	read_textr"   )rP   s    r   Ú_load_json_storerV   ˆ   sV   € à‡{‚{�}„}ð ð	Ý”:˜dŸnšnÑ.Ô.Ñ/Ô/Ð/øÝð 	ð 	ð 	ØˆDð	øøøà€Is   –%< ¼
A	ÁA	c                 ó�   — | j                              dd¬¦  «         |                      t          j        |d¬¦  «        ¦  «         dS )z.Write *data* as pretty-printed JSON to *path*.Tr)   é   )ÚindentN)Úparentr.   Ú
write_textrS   Údumps)rP   r3   s     r   Ú_save_json_storer]   ’   sD   € à„K×Ò˜d¨TÐÑ2Ô2Ð2Ø‡O‚O•D”J˜t¨AÐ.Ñ.Ô.Ñ/Ô/Ð/Ð/Ð/r   Ú	host_pathc                 ó„   — 	 t          | ¦  «                             ¦   «         }|j        |j        fS # t          $ r Y dS w xY w)zIReturn ``(mtime, size)`` for cache comparison, or ``None`` if unreadable.N)r   ÚstatÚst_mtimeÚst_sizer;   )r^   Ústs     r   Ú_file_mtime_keyrd   ˜   sO   € ðÝ�)‰_Œ_×!Ò!Ñ#Ô#ˆØ”˜RœZÐ(Ð(øÝð ð ð Øˆtˆtðøøøs   ‚.1 ±
?¾?c                   ó”   — e Zd ZdZdedz  fd„Zd
d„Zddedz  defd„Ze	de
e         dz  fd„¦   «         Ze	dedz  fd	„¦   «         ZdS )ÚProcessHandlezÏDuck type that every backend's _run_bash() must return.

    subprocess.Popen satisfies this natively.  SDK backends (Modal, Daytona)
    return _ThreadedProcessHandle which adapts their blocking calls.
    r   Nc                 ó   — d S r6   r   ©Úselfs    r   ÚpollzProcessHandle.poll­   ó   € € € r   c                 ó   — d S r6   r   rh   s    r   ÚkillzProcessHandle.kill®   rk   r   Útimeoutc                 ó   — d S r6   r   ©ri   rn   s     r   ÚwaitzProcessHandle.wait¯   rk   r   c                 ó   — d S r6   r   rh   s    r   rF   zProcessHandle.stdout±   ó   € Ø(+¨r   c                 ó   — d S r6   r   rh   s    r   Ú
returncodezProcessHandle.returncode´   rs   r   ©r   Nr6   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r!   rj   rm   Úfloatrq   Úpropertyr   ÚstrrF   ru   r   r   r   rf   rf   ¦   sœ   € € € € € ðð ð &�c˜D‘jÐ%Ð%Ð%Ð%ØÐÐÐØ<Ð<˜E D™LÐ<°CÐ<Ð<Ð<Ð<àØ+˜˜3œ $™Ð+Ð+Ð+ñ „XØ+àØ+˜C $™JÐ+Ð+Ð+ñ „XØ+Ð+Ð+r   rf   c                   óÄ   — e Zd ZdZ	 ddeg eeef         f         deg df         dz  fd„Ze	d„ ¦   «         Z
e	dedz  fd„¦   «         Zdedz  fd	„Zd
„ Zddedz  defd„ZdS )Ú_ThreadedProcessHandleal  Adapter for SDK backends (Modal, Daytona) that have no real subprocess.

    Wraps a blocking ``exec_fn() -> (output_str, exit_code)`` in a background
    thread and exposes a ProcessHandle-compatible interface.  An optional
    ``cancel_fn`` is invoked on ``kill()`` for backend-specific cancellation
    (e.g. Modal sandbox.terminate, Daytona sandbox.stop).
    NÚexec_fnÚ	cancel_fnc                 ó8  ‡ ‡— |‰ _         t          j        ¦   «         ‰ _        d ‰ _        d ‰ _        t          j        ¦   «         \  }}t          j        |ddd¬¦  «        ‰ _	        |‰ _
        ˆˆ fd„}t          j        |d¬¦  «        }|                     ¦   «          d S )NÚrúutf-8Úreplace)ÚencodingÚerrorsc                  ó  •— 	  ‰¦   «         \  } }|‰_         	 t          j        ‰j        |                      dd¬¦  «        ¦  «         n# t
          $ r Y nw xY wn%# t          $ r}|‰_        d‰_         Y d }~nd }~ww xY w	 t          j        ‰j        ¦  «         n# t
          $ r Y nw xY w‰j	         
                    ¦   «          d S # 	 t          j        ‰j        ¦  «         n# t
          $ r Y nw xY w‰j	         
                    ¦   «          w xY w)Nr„   r…   ©r‡   é   )Ú_returncoder,   r8   Ú	_write_fdÚencoder;   r"   Ú_errorr9   Ú_doneÚset)ÚoutputÚ	exit_codeÚexcr€   ri   s      €€r   Ú_workerz0_ThreadedProcessHandle.__init__.<locals>._workerÐ   sO  ø€ ð!Ø$+ G¡I¤IÑ!�˜	Ø#,�Ô ðÝ”H˜Tœ^¨V¯]ª]¸7È9¨]Ñ-UÔ-UÑVÔVÐVÐVøÝð ð ð Ø�Dðøøøøøåð %ð %ð %Ø!�”Ø#$�Ô Ð Ð Ð Ð Ð øøøøð%øøøðÝ”H˜Tœ^Ñ,Ô,Ð,Ð,øÝð ð ð Ø�Dðøøøà”
—’Ñ Ô Ð Ð Ð øð	Ý”H˜Tœ^Ñ,Ô,Ð,Ð,øÝð ð ð Ø�Dðøøøà”
—’Ñ Ô Ð Ð øøøsˆ   ƒA ˜/A ÁA Á
AÁA ÁAÁA ÁC Á
A;Á#A6Á1C Á6A;Á;C Á?B Â
B&Â%B&ÃD
ÃC ÃD
Ã 
C-Ã*D
Ã,C-Ã-D
Tr=   )Ú
_cancel_fnr@   ÚEventr�   r‹   rŽ   r,   ÚpipeÚfdopenÚ_stdoutrŒ   rA   r   )ri   r€   r�   Úread_fdÚwrite_fdr”   Úts   ``     r   Ú__init__z_ThreadedProcessHandle.__init__Á   s¤   øø€ ð
 $ˆŒÝ”_Ñ&Ô&ˆŒ
Ø'+ˆÔØ(,ˆŒõ œG™IœIÑˆ�Ý”y ¨#¸È	ÐRÑRÔRˆŒØ!ˆŒð	!ð 	!ð 	!ð 	!ð 	!ð 	!õ& Ô G°DÐ9Ñ9Ô9ˆØ	�Š‰	Œ	ˆ	ˆ	ˆ	r   c                 ó   — | j         S r6   )r™   rh   s    r   rF   z_ThreadedProcessHandle.stdoutæ   s
   € àŒ|Ðr   r   c                 ó   — | j         S r6   )r‹   rh   s    r   ru   z!_ThreadedProcessHandle.returncodeê   s   € àÔÐr   c                 óF   — | j                              ¦   «         r| j        nd S r6   )r�   Úis_setr‹   rh   s    r   rj   z_ThreadedProcessHandle.pollî   s#   € Ø#'¤:×#4Ò#4Ñ#6Ô#6Ð@ˆtÔÐ¸DÐ@r   c                 ód   — | j         r(	 |                       ¦   «          d S # t          $ r Y d S w xY wd S r6   )r•   r"   rh   s    r   rm   z_ThreadedProcessHandle.killñ   sS   € ØŒ?ð 	ðØ—’Ñ!Ô!Ð!Ð!Ð!øÝð ð ð Ø��ðøøøð	ð 	s   ‰ Ÿ
-¬-rn   c                 óF   — | j                              |¬¦  «         | j        S )N©rn   )r�   rq   r‹   rp   s     r   rq   z_ThreadedProcessHandle.waitø   s!   € ØŒ
�Š ˆÑ(Ô(Ð(ØÔÐr   r6   )rw   rx   ry   rz   r   Útupler}   r!   r�   r|   rF   ru   rj   rm   r{   rq   r   r   r   r   r   ¸   s  € € € € € ðð ð 04ð#ð #à˜"˜e C¨ HœoÐ-Ô.ð#ð ˜B ˜HÔ%¨Ñ,ð#ð #ð #ð #ðJ ðð ñ „Xðð ð ˜C $™Jð  ð  ð  ñ „Xð ðA�c˜D‘jð Að Að Að Aðð ð ð ð  ˜E D™Lð  °Cð  ð  ð  ð  ð  ð  r   r   Ú
session_idc                 ó   — d| › d�S )NÚ__HERMES_CWD_Ú__r   )r¦   s    r   Ú_cwd_markerrª     s   € Ø)˜:Ð)Ð)Ð)Ð)r   c                   óš  — e Zd ZU dZdZeed<   dZeed<   defd„Z	d'd	ed
ede
fd„Zddddœdeded
ededz  def
d„Zed„ ¦   «         Zd„ Zed	edefd„¦   «         Zded	edefd„Zedededefd„¦   «         Zd(ded
ede
fd„Zdefd„Zde
fd„Zde
fd„Zd)d „Z	 d*ddd"œded	ed
edz  dedz  de
f
d#„Zd$„ Zd%„ Zdedeeedz  f         fd&„ZdS )+ÚBaseEnvironmenta  Common interface and unified execution flow for all Hermes backends.

    Subclasses implement ``_run_bash()`` and ``cleanup()``.  The base class
    provides ``execute()`` with session snapshot sourcing, CWD tracking,
    interrupt handling, and timeout enforcement.
    r—   Ú_stdin_modeé   Ú_snapshot_timeoutr   c                 ó   — dS )a,  Return the backend temp directory used for session artifacts.

        Most sandboxed backends use ``/tmp`` inside the target environment.
        LocalEnvironment overrides this on platforms like Termux where ``/tmp``
        may be missing and ``TMPDIR`` is the portable writable location.
        z/tmpr   rh   s    r   Úget_temp_dirzBaseEnvironment.get_temp_dir  s	   € ð ˆvr   NÚcwdrn   Úenvc                 óX  — || _         || _        |pi | _        t          j        ¦   «         j        d d…         | _        |                      ¦   «                              d¦  «        pd}|› d| j        › d�| _	        |› d| j        › d�| _
        t          | j        ¦  «        | _        d| _        d S )Né   ú/z/hermes-snap-z.shz/hermes-cwd-z.txtF)r²   rn   r³   ÚuuidÚuuid4ÚhexÚ_session_idr±   ÚrstripÚ_snapshot_pathÚ	_cwd_filerª   Ú_snapshot_ready)ri   r²   rn   r³   Útemp_dirs        r   r�   zBaseEnvironment.__init__"  s±   € ØˆŒØˆŒØ�9˜"ˆŒåœ:™<œ<Ô+¨C¨R¨CÔ0ˆÔØ×$Ò$Ñ&Ô&×-Ò-¨cÑ2Ô2Ð9°cˆØ!)ÐMÐM¸Ô8HÐMÐMÐMˆÔØ$ÐHÐH°$Ô2BÐHÐHÐHˆŒÝ& tÔ'7Ñ8Ô8ˆÔØ$ˆÔÐÐr   Féx   ©Úloginrn   rD   Ú
cmd_stringrÂ   rD   c                óJ   — t          t          | ¦  «        j        › d�¦  «        ‚)z®Spawn a bash process to run *cmd_string*.

        Returns a ProcessHandle (subprocess.Popen or _ThreadedProcessHandle).
        Must be overridden by every backend.
        z must implement _run_bash())ÚNotImplementedErrorÚtyperw   )ri   rÃ   rÂ   rn   rD   s        r   Ú	_run_bashzBaseEnvironment._run_bash2  s$   € õ "¥T¨$¡Z¤ZÔ%8Ð"UÐ"UÐ"UÑVÔVÐVr   c                 ó   — dS )z<Release backend resources (container, instance, connection).Nr   rh   s    r   ÚcleanupzBaseEnvironment.cleanupA  s	   € ð 	ˆr   c                 ó  — d| j         › d| j         › d| j         › d| j         › d| j         › d| j         › d| j        › d| j        › d	| j        › d
�}	 |                      |d| j        ¬¦  «        }|                      || j        ¬¦  «        }d| _        |                      |¦  «         t           	                    d| j
        | j        ¦  «         dS # t          $ r3}t                               d| j
        |¦  «         d| _        Y d}~dS d}~ww xY w)züCapture login shell environment into a snapshot file.

        Called once after backend construction.  On success, sets
        ``_snapshot_ready = True`` so subsequent commands source the snapshot
        instead of running with ``bash -l``.
        úexport -p > z#
declare -f | grep -vE '^_[^_]' >> z
alias -p >> z#
echo 'shopt -s expand_aliases' >> z
echo 'set +e' >> z
echo 'set +u' >> z

pwd -P > z 2>/dev/null || true
printf '\nú%sz\n' "$(pwd -P)"
T)rÂ   rn   r¤   z-Session snapshot created (session=%s, cwd=%s)uL   init_session failed (session=%s): %s â€” falling back to bash -l per commandFN)r¼   r½   rª   rÇ   r¯   Ú_wait_for_processr¾   Ú_update_cwdÚloggerÚinforº   r²   r"   Úwarning)ri   Ú	bootstrapr2   Úresultr“   s        r   Úinit_sessionzBaseEnvironment.init_sessionJ  s¤  € ðU˜4Ô.ð Uð UØ15Ô1DðUð UàÔ.ðUð Uð 26Ô1DðUð Uð !%Ô 3ð	Uð Uð
 !%Ô 3ðUð Uð œðUð Uð Ô*ðUð Uð /3Ô.>ðUð Uð Uð 	ð	)Ø—>’> )°4ÀÔAW�>ÑXÔXˆDØ×+Ò+¨D¸$Ô:PÐ+ÑQÔQˆFØ#'ˆDÔ Ø×Ò˜VÑ$Ô$Ð$Ý�KŠKØ?ØÔ Ø”ñô ð ð ð øõ
 ð 	)ð 	)ð 	)Ý�NŠNð6àÔ Øñ	ô ð ð $)ˆDÔ Ð Ð Ð Ð Ð Ð øøøøð	)øøøs   ÁA;C
 Ã

DÃ(DÄDc                 ó²   — | dk    r| S | dk    rdS |                       d¦  «        rdt          j        | dd…         ¦  «        › �S t          j        | ¦  «        S )z7Quote a ``cd`` target while preserving ``~`` expansion.Ú~z~/z$HOMEz$HOME/rX   N)Ú
startswithÚshlexÚquote)r²   s    r   Ú_quote_cwd_for_cdz!BaseEnvironment._quote_cwd_for_cds  sd   € ð �#Š:ˆ:ØˆJØ�$Š;ˆ;Ø�7Ø�>Š>˜$ÑÔð 	3Ø2�EœK¨¨A¨B¨B¬Ñ0Ô0Ð2Ð2Ð2ÝŒ{˜3ÑÔÐr   Úcommandc                 óZ  — |                      dd¦  «        }g }| j        r|                     d| j        › d�¦  «         |                      |¦  «        }|                     d|› d�¦  «         |                     d|› d�¦  «         |                     d¦  «         | j        r|                     d	| j        › d�¦  «         |                     d
| j        › d�¦  «         |                     d| j        › d| j        › d�¦  «         |                     d¦  «         d                     |¦  «        S )zwBuild the full bash script that sources snapshot, cd's, runs command,
        re-dumps env vars, and emits CWD markers.ú'z'\''zsource z 2>/dev/null || truezbuiltin cd z || exit 126zeval 'z__hermes_ec=$?rË   z	pwd -P > z
printf '\nrÌ   z\n' "$(pwd -P)"zexit $__hermes_ecÚ
)r…   r¾   Úappendr¼   rÚ   r½   rª   Újoin)ri   rÛ   r²   ÚescapedÚpartsÚ
quoted_cwds         r   Ú_wrap_commandzBaseEnvironment._wrap_command~  s]  € ð —/’/ # wÑ/Ô/ˆàˆð Ôð 	NØ�LŠLÐL 4Ô#6ÐLÐLÐLÑMÔMÐMð ×+Ò+¨CÑ0Ô0ˆ
Ø�ŠÐ; :Ð;Ð;Ð;Ñ<Ô<Ð<ð 	�ŠÐ(˜gÐ(Ð(Ð(Ñ)Ô)Ð)Ø�ŠÐ%Ñ&Ô&Ð&ð Ôð 	SØ�LŠLÐQ¨Ô(;ÐQÐQÐQÑRÔRÐRð 	�ŠÐE ¤ÐEÐEÐEÑFÔFÐFð
 	�ŠØR˜$Ô*ÐRÐR¨dÔ.>ÐRÐRÐRñ	
ô 	
ð 	
ð 	�ŠÐ(Ñ)Ô)Ð)à�yŠy˜ÑÔÐr   c                 ób   — dt          j        ¦   «         j        dd…         › �}| › d|› d|› d|› �S )z;Append stdin_data as a shell heredoc to the command string.ÚHERMES_STDIN_Nrµ   z << 'z'
rÞ   )r·   r¸   r¹   )rÛ   rD   Ú	delimiters      r   Ú_embed_stdin_heredocz$BaseEnvironment._embed_stdin_heredoc§  sG   € ð <¥D¤J¡L¤LÔ$4°S°b°SÔ$9Ð;Ð;ˆ	ØÐGÐG 	ÐGÐG¨jÐGÐG¸IÐGÐGÐGr   r2   c           
      ó˜  ‡‡‡— g Š t          j        d¦  «        d¬¦  «        Šˆˆˆfd„}t          j        |d¬¦  «        }|                     ¦   «          t          j        ¦   «         |z   }t          j        ¦   «         }||dœ}t          j        ¦   «         j        }t          ‰dd	¦  «        }	d
}
|}d}t          ¦   «         d	u }t          r/t                               d||	||sdndt          ¦   «         ¦  «         	 ‰                     ¦   «         �€|
dz  }
t          ¦   «         r…t          r8t                               d||	|
t          j        ¦   «         |d         z
  ¦  «         |                      ‰¦  «         |                     d¬¦  «         d                     ‰¦  «        dz   ddœS t          j        ¦   «         |k    r‰t          rt                               d||	|
|¦  «         |                      ‰¦  «         |                     d¬¦  «         d                     ‰¦  «        }d|› d�}|r||z   n|                     ¦   «         ddœS t'          |d¦  «         t          r�t          j        ¦   «         |z
  dk    rvt          ¦   «         d	u }t                               d||	|
t          j        ¦   «         |d         z
  t          ¦   «         |sdnd|r|sdnd¦  «         t          j        ¦   «         }|}t          j        d ¦  «         ‰                     ¦   «         �®n’# t*          t,          f$ r~ t          r8t                               d!||	|
t          j        ¦   «         |d         z
  ¦  «         	 |                      ‰¦  «         |                     d¬¦  «         n# t.          $ r Y nw xY w‚ w xY w|                     d¬¦  «         	 ‰j                             ¦   «          n# t.          $ r Y nw xY wt          r>t                               d"||	|
t          j        ¦   «         |d         z
  ‰j        ¦  «         d                     ‰¦  «        ‰j        dœS )#uþ  Poll-based wait with interrupt checking and stdout draining.

        Shared across all backends â€” not overridden.

        Fires the ``activity_callback`` (if set on this instance) every 10s
        while the process is running so the gateway's inactivity timeout
        doesn't kill long-running commands.

        Also wraps the poll loop in a ``try/finally`` that guarantees we
        call ``self._kill_process(proc)`` if we exit via ``KeyboardInterrupt``
        or ``SystemExit``.  Without this, the local backend (which spawns
        subprocesses with ``os.setsid`` into their own process group) leaves
        an orphan with ``PPID=1`` when python is shut down mid-tool â€” the
        ``sleep 300``-survives-30-min bug Physikal and I both hit.
        r„   r…   r‰   c                  ó¸  •— ‰j                              ¦   «         } d}	 	 	 t          j        | gg g d¦  «        \  }}}n# t          t          f$ r Y nƒw xY w|r\	 t          j        | d¦  «        }n# t          t          f$ r Y nSw xY w|snL‰                     ‰                     |¦  «        ¦  «         d}n ‰ 	                    ¦   «         �|dz  }|dk    rnŒ´	 ‰                     dd¬¦  «        }|r‰                     |¦  «         d S d S # t          $ r Y d S w xY w# 	 ‰                     dd¬¦  «        }|r‰                     |¦  «         w w # t          $ r Y w w xY wxY w)	Nr   Tgš™™™™™¹?é   rŠ   é   r   )Úfinal)rF   ÚfilenoÚselectÚ
ValueErrorr;   r,   Úreadrß   Údecoderj   r"   )	ÚfdÚidle_after_exitÚreadyÚ_ÚchunkÚtailÚdecoderÚoutput_chunksr2   s	         €€€r   Ú_drainz1BaseEnvironment._wait_for_process.<locals>._drainÝ  sÜ  ø€ Ø”×#Ò#Ñ%Ô%ˆBØˆOðð"ðÝ&,¤m°R°D¸"¸bÀ#Ñ&FÔ&F™˜˜q ! !øÝ&­Ð0ð ð ð Ø˜ðøøøàð "ð"Ý$&¤G¨B°Ñ$5Ô$5˜E˜EøÝ *­GÐ4ð "ð "ð "Ø!˜Eð"øøøà$ð "Ø!Ø%×,Ò,¨W¯^ª^¸EÑ-BÔ-BÑCÔCÐCØ*+˜˜ØŸš™œÐ0ð (¨1Ñ,˜Ø*¨aÒ/Ð/Ø!ð)"ð2Ø"Ÿ>š>¨#°T˜>Ñ:Ô:�DØð 3Ø%×,Ò,¨TÑ2Ô2Ð2Ð2Ð2ð3ð 3øå ð ð ð Ø�D�Dðøøøøð	Ø"Ÿ>š>¨#°T˜>Ñ:Ô:�DØð 3Ø%×,Ò,¨TÑ2Ô2Ð2Ð2ð3øå ð ð ð Ø�Dðøøøøøøs†   žD  = ¼D ½AÁD ÁAÁD ÁA- Á,D Á-BÁ>D Â BÂAD Ã.D Ä
DÄDÄEÄ.E	ÅEÅ	
EÅEÅEÅETr=   )r   r   ÚpidNr   Fzg[interrupt-debug] _wait_for_process ENTER tid=%s pid=%s timeout=%ss activity_cb=%s initial_interrupt=%sr�   ÚMISSINGrŠ   ut   [interrupt-debug] _wait_for_process INTERRUPT DETECTED tid=%s pid=%s iter=%d elapsed=%.1fs â€” killing process groupr   rX   r¤   Ú z
[Command interrupted]é‚   )r‘   ru   zM[interrupt-debug] _wait_for_process TIMEOUT tid=%s pid=%s iter=%d timeout=%ssz
[Command timed out after zs]é|   zterminal command runningg      >@zo[interrupt-debug] _wait_for_process HEARTBEAT tid=%s pid=%s iter=%d elapsed=%.0fs interrupt=%s activity_cb=%s%sz (LOST during run)gš™™™™™É?uƒ   [interrupt-debug] _wait_for_process EXCEPTION_EXIT tid=%s pid=%s iter=%d elapsed=%.1fs â€” killing subprocess group before re-raisezd[interrupt-debug] _wait_for_process EXIT (natural) tid=%s pid=%s iter=%d elapsed=%.1fs returncode=%s)ÚcodecsÚgetincrementaldecoderr@   rA   r   r   r   Úcurrent_threadÚidentr   r   Ú_DEBUG_INTERRUPTrÏ   rÐ   r
   rj   Ú_kill_processrà   Úlstripr%   ÚsleepÚKeyboardInterruptÚ
SystemExitr"   rF   r9   ru   )ri   r2   rn   rû   Údrain_threadÚdeadlineÚ_nowÚ_activity_stateÚ_tidÚ_pidÚ_iter_countÚ_last_heartbeatÚ_last_interrupt_stateÚ_cb_was_noneÚpartialÚtimeout_msgÚ_cb_now_nonerù   rú   s    `               @@r   rÍ   z!BaseEnvironment._wait_for_process±  s   øøø€ ð  $&ˆð4 8•&Ô.¨wÑ7Ô7¸yÐIÑIÔIˆð"	ð "	ð "	ð "	ð "	ð "	ð "	õH !Ô'¨v¸dÐCÑCÔCˆØ×ÒÑÔÐÝ”>Ñ#Ô# gÑ-ˆÝŒ~ÑÔˆàØð
ð 
ˆõ Ô'Ñ)Ô)Ô/ˆÝ�t˜U DÑ)Ô)ˆØˆØˆØ %ÐÝ-Ñ/Ô/°4Ð7ˆÝð 	Ý�KŠKðBà�d˜GØ)Ð8��¨yÝÑ Ô ñô ð ðK	Ø—)’)‘+”+Ñ%Ø˜qÑ �Ý!Ñ#Ô#ð Ý'ð ÝŸšð\à  $¨µT´^Ñ5EÔ5EÈÐX_ÔH`Ñ5`ñô ð ð
 ×&Ò& tÑ,Ô,Ð,Ø ×%Ò%¨aÐ%Ñ0Ô0Ð0à"$§'¢'¨-Ñ"8Ô"8Ð;TÑ"TØ&)ðð ð õ ”>Ñ#Ô# hÒ.Ð.Ý'ð ÝŸšð@à  $¨°Wñô ð ð
 ×&Ò& tÑ,Ô,Ð,Ø ×%Ò%¨aÐ%Ñ0Ô0Ð0Ø Ÿgšg mÑ4Ô4�GØ"KÀÐ"KÐ"KÐ"K�Kð #ð#2 '¨KÑ"7Ð"7à(×/Ò/Ñ1Ô1Ø&)ð	ð ð õ & oÐ7QÑRÔRÐRõ
 $ð 0­¬Ñ(8Ô(8¸?Ñ(JÈdÒ(RÐ(RÝ#9Ñ#;Ô#;¸tÐ#C�LÝ—K’Kð8ð ˜d KÝœÑ(Ô(¨?¸7Ô+CÑCÝ&Ñ(Ô(Ø%1Ð@˜˜°yØ0<ÐYÀ\ÐYÐ,Ð,ÐWYñ	ô 	ð 	õ '+¤nÑ&6Ô&6�OØ#/�Lå”
˜3‘”�ðk —)’)‘+”+Ñ%øøõl "¥:Ð.ð 	ð 	ð 	õ  ð Ý—’ðgà˜$ Ý”NÑ$Ô$ °wÔ'?Ñ?ñ	ô ð ðØ×"Ò" 4Ñ(Ô(Ð(Ø×!Ò!¨!Ð!Ñ,Ô,Ð,Ð,øÝð ð ð Ø�ðøøøàð)	øøøð2 	×Ò !ÐÑ$Ô$Ð$ð	ØŒK×ÒÑÔÐÐøÝð 	ð 	ð 	ØˆDð	øøøõ ð 	Ý�KŠKðDà�d˜KÝ”Ñ Ô  ?°7Ô#;Ñ;Ø”ñô ð ð Ÿ'š' -Ñ0Ô0ÀÄÐPÐPÐPsR   Ã9B,L Æ&BL ÉCL ÌAN&Í(+NÎN&Î
N!ÎN&Î N!Î!N&Ï O Ï
O'Ï&O'c                 ól   — 	 |                      ¦   «          dS # t          t          t          f$ r Y dS w xY w)zDTerminate a process. Subclasses may override for process-group kill.N)rm   ÚProcessLookupErrorÚPermissionErrorr;   )ri   r2   s     r   r  zBaseEnvironment._kill_process  sA   € ð	Ø�IŠI‰KŒKˆKˆKˆKøÝ"¥OµWÐ=ð 	ð 	ð 	ØˆDˆDð	øøøs   ‚ ˜3²3rÓ   c                 ó0   — |                       |¦  «         dS )zDExtract CWD from command output. Override for local file-based read.N)Ú_extract_cwd_from_output)ri   rÓ   s     r   rÎ   zBaseEnvironment._update_cwdŠ  s   € à×%Ò% fÑ-Ô-Ð-Ð-Ð-r   c                 óB  — |                      dd¦  «        }| j        }|                     |¦  «        }|dk    rdS t          d|dz
  ¦  «        }|                     |||¦  «        }|dk    s||k    rdS ||t	          |¦  «        z   |…                              ¦   «         }|r|| _        |                     dd|¦  «        }|dk    r|}|                     d|t	          |¦  «        z   ¦  «        }	|	dk    r|	dz   nt	          |¦  «        }	|d|…         ||	d…         z   |d<   dS )	zÙParse the __HERMES_CWD_{session}__ marker from stdout output.

        Updates self.cwd and strips the marker from result["output"].
        Used by remote backends (Docker, SSH, Modal, Daytona, Singularity).
        r‘   rþ   éÿÿÿÿNr   rë   rÞ   rŠ   )r    rª   ÚrfindÚmaxÚlenÚstripr²   Úfind)
ri   rÓ   r‘   ÚmarkerÚlastÚsearch_startÚfirstÚcwd_pathÚ
line_startÚline_ends
             r   r  z(BaseEnvironment._extract_cwd_from_outputŽ  s:  € ð —’˜H bÑ)Ô)ˆØÔ!ˆØ�|Š|˜FÑ#Ô#ˆØ�2Š:ˆ:ØˆFõ ˜1˜d T™kÑ*Ô*ˆØ—’˜V \°4Ñ8Ô8ˆØ�BŠ;ˆ;˜% 4š-˜-ØˆFà˜%¥# f¡+¤+Ñ-°Ð4Ô5×;Ò;Ñ=Ô=ˆØð 	 ØˆDŒHð —\’\ $¨¨5Ñ1Ô1ˆ
Ø˜ÒÐØˆJØ—;’;˜t T­C°©K¬KÑ%7Ñ8Ô8ˆØ#+¨r¢> >�8˜a‘<�<µs¸6±{´{ˆà! + : +Ô.°¸¸	¸	Ô1BÑBˆˆxÑÐÐr   c                 ó   — dS )u>  Hook called before each command execution.

        Remote backends (SSH, Modal, Daytona) override this to trigger
        their FileSyncManager.  Bind-mount backends (Docker, Singularity)
        and Local don't need file sync â€” the host filesystem is directly
        visible inside the container/process.
        Nr   rh   s    r   Ú_before_executezBaseEnvironment._before_execute´  s	   € ð 	ˆr   rþ   )rn   rD   c                óÖ  — |                       ¦   «          |                      |¦  «        \  }}ddlm}  ||¦  «        }|p| j        }|p| j        }	|�|�||z   }
n|�|}
n|}
|
r#| j        dk    r|                      ||
¦  «        }d}
|                      ||	¦  «        }| j	         }|  
                    ||||
¬¦  «        }|                      ||¬¦  «        }|                      |¦  «         |S )z=Execute a command, return {"output": str, "returncode": int}.r   )Ú_rewrite_compound_backgroundNÚheredocrÁ   r¤   )r,  Ú_prepare_commandÚtools.terminal_toolr.  rn   r²   r­   rè   rä   r¾   rÇ   rÍ   rÎ   )ri   rÛ   r²   rn   rD   Úexec_commandÚ
sudo_stdinr.  Úeffective_timeoutÚeffective_cwdÚeffective_stdinÚwrappedrÂ   r2   rÓ   s                  r   ÚexecutezBaseEnvironment.executeÂ  s?  € ð 	×ÒÑÔÐà#'×#8Ò#8¸Ñ#AÔ#AÑ ˆ�jð 	EÐDÐDÐDÐDÐDØ3Ð3°LÑAÔAˆØ#Ð3 t¤|ÐØ˜˜tœxˆð Ð! jÐ&<Ø(¨:Ñ5ˆOˆOØÐ#Ø(ˆOˆOà(ˆOð ð 	#˜tÔ/°9Ò<Ð<Ø×4Ò4°\À?ÑSÔSˆLØ"ˆOà×$Ò$ \°=ÑAÔAˆð Ô(Ð(ˆà�~Š~Ø˜5Ð*;Èð ñ 
ô 
ˆð ×'Ò'¨Ð6GÐ'ÑHÔHˆØ×Ò˜Ñ Ô Ð àˆr   c                 ó.   — |                       ¦   «          dS )z.Alias for cleanup (compat with older callers).N)rÉ   rh   s    r   ÚstopzBaseEnvironment.stopö  s   € à�Š‰Œˆˆˆr   c                 óR   — 	 |                       ¦   «          d S # t          $ r Y d S w xY wr6   )rÉ   r"   rh   s    r   Ú__del__zBaseEnvironment.__del__ú  s:   € ð	Ø�LŠL‰NŒNˆNˆNˆNøÝð 	ð 	ð 	ØˆDˆDð	øøøs   ‚ ˜
&¥&c                 ó$   — ddl m}  ||¦  «        S )z6Transform sudo commands if SUDO_PASSWORD is available.r   )Ú_transform_sudo_command)r1  r>  )ri   rÛ   r>  s      r   r0  z BaseEnvironment._prepare_command   s%   € à?Ð?Ð?Ð?Ð?Ð?à&Ð& wÑ/Ô/Ð/r   r6   )rÀ   rv   )rþ   ) rw   rx   ry   rz   r­   r}   Ú__annotations__r¯   r!   r±   Údictr�   Úboolrf   rÇ   r   rÉ   rÔ   ÚstaticmethodrÚ   rä   rè   rÍ   r  rÎ   r  r,  r8  r:  r<  r¥   r0  r   r   r   r¬   r¬     s  € € € € € € ðð ð €K�ÐÐÑð  Ð�sÐÐÑð˜cð ð ð ð ð
%ð 
%˜Cð 
%¨#ð 
%°Dð 
%ð 
%ð 
%ð 
%ð( ØØ!%ðWð Wð WàðWð ð	Wð
 ðWð ˜$‘JðWð 
ðWð Wð Wð Wð ðð ñ „^ðð#)ð #)ð #)ðR ð ˜sð   sð  ð  ð  ñ „\ð ð#  Sð # ¨sð # °sð # ð # ð # ð # ðR ðH cð H°sð H¸sð Hð Hð Hñ „\ðHðLQð LQ mð LQ¸cð LQÈDð LQð LQð LQð LQð\ -ð ð ð ð ð. $ð .ð .ð .ð .ð C¨tð  Cð  Cð  Cð  CðLð ð ð ð" ð.ð
 #Ø!%ð.ð .ð .àð.ð ð.ð
 �t‘ð.ð ˜$‘Jð.ð 
ð.ð .ð .ð .ðhð ð ðð ð ð0¨ð 0°°c¸3À¹:°oÔ0Fð 0ð 0ð 0ð 0ð 0ð 0r   r¬   r6   )6rz   r  rS   Úloggingr,   rï   rØ   rI   r@   r   r·   Úabcr   r   Úpathlibr   Útypingr   r   r   Úhermes_constantsr	   Útools.interruptr
   Ú	getLoggerrw   rÏ   rA  r-   r  ÚsetLevelÚINFOÚlocalr   r}   r   r   r@  r%   r1   rJ   rB   ÚlistrO   rV   r]   r¥   r{   r!   rd   rf   r   rª   r¬   r   r   r   ú<module>rN     s‡  ððð ð €€€Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø €€€Ø Ð Ð Ð Ø Ð Ð Ð Ø €€€Ø €€€Ø #Ð #Ð #Ð #Ð #Ð #Ð #Ð #Ø Ð Ð Ð Ð Ð Ø )Ð )Ð )Ð )Ð )Ð )Ð )Ð )Ð )Ð )à ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø *Ð *Ð *Ð *Ð *Ð *à	ˆÔ	˜8Ñ	$Ô	$€ð �4˜	˜œ	Ð":Ñ;Ô;Ñ<Ô<Ð àð "ð
 ‡O‚O�G”LÑ!Ô!Ð!ð +˜9œ?Ñ,Ô,Ð ð+˜h¨ u¨d {Ô3°dÑ:ð +¸tð +ð +ð +ð +ð
? ¨#¨°¨Ô 5¸Ñ <ð ?ð ?ð ?ð ?ðØðàðð 
ðð ð ð ð4˜ð ð ð ð ð(
9�jÔ&ð 
9¨cð 
9°dð 
9ð 
9ð 
9ð 
9ð .2ðð Ø	ˆcŒðØ # d¡
ðàÔðð ð ð ð,˜4ð  Dð ð ð ð ð0˜4ð 0 tð 0°ð 0ð 0ð 0ð 0ð˜sð  u¨U°C¨ZÔ'8¸4Ñ'?ð ð ð ð ð,ð ,ð ,ð ,ð ,�Hñ ,ô ,ð ,ð$B ð B ð B ð B ð B ñ B ô B ð B ðT*˜Cð * Cð *ð *ð *ð *ðy0ð y0ð y0ð y0ð y0�cñ y0ô y0ð y0ð y0ð y0r   