
    `gjS                        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 ddlmZmZmZ ddlmZ ddlmZ dd	lmZ  ej8                  e      Z e ej@                  d
            Z!e!rejE                  ejF                          ejH                         Z%dZ& G d d      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e0e(   de(dz  dej\                  fdZ1dede+fdZ2dede+ddfdZ3de(de4e5e6f   dz  fd Z7 G d! d"e      Z8 G d# d$      Z9d%e(de(fd&Z: G d' d(e      Z;y)*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)deque)Path)IOCallableProtocol)get_hermes_home)windows_hide_flags)is_interruptedHERMES_DEBUG_INTERRUPTl    c                   n    e Zd ZdZdefdZedefd       Zedefd       Zde	ddfd	Z
d
dde	de	fdZy)_BoundedOutputCollectorz9Retain a bounded 40/60 head-tail window of streamed text.	max_charsc                 6   t        dt        |            | _        t        | j                  dz        | _        | j                  | j                  z
  | _        g | _        t               | _        d| _        d| _	        d| _
        t        j                         | _        y )N   皙?r   )maxintr   _head_limit_tail_limit_headr   _tail_head_chars_tail_chars_total_chars	threadingLock_lock)selfr   s     J/root/.hermes/venv/lib/python3.12/site-packages/tools/environments/base.py__init__z _BoundedOutputCollector.__init__8   sw    QI/t~~34>>D,<,<< "
!&
^^%
    returnc                 x    | j                   5  | j                  | j                  z   cd d d        S # 1 sw Y   y xY wN)r   r   r   r    s    r!   buffered_charsz&_BoundedOutputCollector.buffered_charsC   s2    ZZ 	7##d&6&66	7 	7 	7s   09c                 ^    | j                   5  | j                  cd d d        S # 1 sw Y   y xY wr&   )r   r   r'   s    r!   total_charsz#_BoundedOutputCollector.total_charsH   s'    ZZ 	%$$	% 	% 	%s   #,textNc                    |sy | j                   5  t        |      }| xj                  |z  c_        d}| j                  | j                  k  rZt        | j                  | j                  z
  |      }|r5| j                  j                  |d |        | xj                  |z  c_        |}||z
  }|dk  s| j                  dk  r
	 d d d        y || j                  k\  r^| j                  j                          | j                  j                  || j                   d         | j                  | _        	 d d d        y ||d  }| j                  j                  |       | xj                  t        |      z  c_        | j                  | j                  kD  r| j                  | j                  z
  }| j                  d   }t        |      |k  r9| j                  j                          | xj                  t        |      z  c_        n'||d  | j                  d<   | xj                  |z  c_        | j                  | j                  kD  rd d d        y # 1 sw Y   y xY w)Nr   )r   lenr   r   r   minr   appendr   r   clearr   popleft)	r    r+   text_lenstarttake	remainingchunkexcessfirsts	            r!   r/   z_BoundedOutputCollector.appendM   s   ZZ  	/4yH)E$"2"224++d.>.>>IJJ%%d5Dk2$$,$ E 5(IA~!1!1Q!6 	/  	/ D,,,

  "

!!$(8(8'8':";<#'#3#3 ' 	/  	/* LEJJe$E
*""T%5%55))D,<,<<

1u:'JJ&&($$E
2$$)&'NDJJqM$$.$ ""T%5%551 	/  	/  	/s   B0I	A$I6DII suffixr;   c          	         | j                   5  t        |      | j                  k\  r|| j                   d cddd       S dj                  | j                        }dj                  | j
                        }| j                  t        |      z
  }| j                  |k  r||z   |z   cddd       S d}t        d      D ]g  }t        d|t        |      z
        }t        |dz        }||z
  }	t        d| j                  |z
  |	z
        }
d|
dd| j                  dd	}||k(  r n|}i t        d|t        |      z
        }t        |dz        }||z
  }	|	r||	 d nd}|d| |d| z   |z   |z   cddd       S # 1 sw Y   yxY w)
zARender within ``max_chars``, preserving a required status suffix.Nr9      r   r   z

... [OUTPUT TRUNCATED - ,z chars omitted out of z total] ...

)
r   r-   r   joinr   r   r   ranger   r   )r    r;   headtail	availablenotice_content_budget
head_chars
tail_charsomittedupdatedrendered_tails                r!   renderz_BoundedOutputCollector.renderr   s   ZZ 	S6{dnn,t~~o/0	S 	S 774::&D774::&DV4I  I-d{V+	S 	S F1X !!$Q	CK(?!@ #!56
+j8
a!2!2Z!?*!LM271+ >"//2/C  f$ ! !IF$;<N^c12J'*4J2<D*."M$vjy'99MIFR9	S 	S 	Ss   (E3A$E3-B<E33E<)__name__
__module____qualname____doc__r   r"   propertyr(   r*   strr/   rL    r#   r!   r   r   6   sv    C	&# 	& 7 7 7 %S % %#/3 #/4 #/J ') S SS Sr#   r   cbr$   c                     | t         _        y)z>Register a callback that _wait_for_process fires periodically.N)_activity_callback_localcallback)rT   s    r!   set_activity_callbackrX      s    (*%r#   c                  $    t        t        dd       S )NrW   )getattrrV   rS   r#   r!   _get_activity_callbackr[      s    +Z>>r#   statelabelc                     t        j                         }| j                  dd      }|| d   z
  |k  ry|| d<   	 t               }|r t	        || d   z
        } || d| d       yy# t
        $ r Y y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_touchNr3   z (z
s elapsed))time	monotonicgetr[   r   	Exception)r\   r]   nowr_   rT   elapseds         r!   touch_activity_if_duerg      s     ..
CyyT*H
U<  8+E,#%#g./G%7):./   s   +A& &	A21A2c                      t        j                  d      } | rt        |       }nt               dz  }|j	                  dd       |S )zReturn 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_dirrs      sA     YY-.FL+GGD4G(Hr#   procdatac                 ^      fd}t        j                  |d      j                          y)u  Write *data* to proc.stdin on a daemon thread to avoid pipe-buffer deadlocks.

    On Windows, text-mode stdin (``text=True`` / ``encoding="utf-8"``)
    translates ``\n`` → ``\r\n`` as the data flows through the pipe —
    which corrupts every write_file / patch call because the bytes that
    land on disk include injected carriage returns.  The file IS created,
    but every subsequent byte-count / content compare against the
    caller's ``\n``-only string fails.

    Workaround: write through ``proc.stdin.buffer`` (the underlying byte
    buffer), encoding to UTF-8 ourselves.  That bypasses Python's
    newline translation entirely on every platform.  No behaviour change
    on POSIX — the byte sequence is identical to what text-mode would
    produce there.
    c                      	 t        t              rj                  d      n} t        j                  dj                        }|j                  |        |j                          y # t        t        f$ r Y y w xY w)Nutf-8buffer)	
isinstancerR   encoderZ   stdinwritecloseBrokenPipeErrorOSError)rawtargetru   rt   s     r!   _writez_pipe_stdin.<locals>._write   se    	 +5T3*?$++g&TCTZZ4::>FLLLLN) 		s   A%A) )A;:A;Tr   daemonN)r   Threadr3   )rt   ru   r   s   `` r!   _pipe_stdinr      s#    " F40668r#   cmd
stdin_datac                    |j                  dt                      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.
    creationflagsT)stdoutstderrr|   r+   )
setdefaultr   
subprocessPopenPIPESTDOUTDEVNULLr   )r   r   kwargsrt   s       r!   _popen_bashr      sx     o'9';<  !+!7jooZ=O=O D D*%Kr#   pathc                     | j                         r&	 t        j                  | j                  d            S i S # t        $ r Y i S w xY w)z:Load a JSON file as a dict, returning ``{}`` on any error.rx   encoding)existsjsonloads	read_textrd   )r   s    r!   _load_json_storer     sJ    {{}	::dnngn>?? I  	I	s   $9 	AAc                     | j                   j                  dd       | j                  t        j                  |d      d       y)z.Write *data* as pretty-printed JSON to *path*.Trk      )indentrx   r   N)parentrp   
write_textr   dumps)r   ru   s     r!   _save_json_storer     s4    KKdT2OODJJtA.OAr#   	host_pathc                     	 t        |       j                         }|j                  |j                  fS # t        $ r Y y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_keyr     s>    )_!!#RZZ(( s   03 	??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y)ProcessHandlezDuck 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                      y r&   rS   r'   s    r!   pollzProcessHandle.poll(      r#   c                      y r&   rS   r'   s    r!   killzProcessHandle.kill)  r   r#   timeoutc                      y r&   rS   r    r   s     r!   waitzProcessHandle.wait*  r   r#   c                      y r&   rS   r'   s    r!   r   zProcessHandle.stdout,      (+r#   c                      y r&   rS   r'   s    r!   
returncodezProcessHandle.returncode/  r   r#   r$   Nr&   )rM   rN   rO   rP   r   r   r   floatr   rQ   r   rR   r   r   rS   r#   r!   r   r   !  s[     &cDj%<EDL<C<+3$+ ++C$J+ +r#   r   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y)_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                 6    | _         t        j                          _        d  _        d  _        t        j                         \  }}t        j                  |ddd       _	        | _
         fd}t        j                  |d      }|j                          y )Nrrx   replace)r   errorsc                  $   	         \  } }|_         	 t        j                  j                  | j	                  dd             	 t        j                  j                         j                  j                          y # t
        $ r Y Fw xY w# t        $ r}|_        d_         Y d }~fd }~ww xY w# t
        $ r Y Yw xY w# 	 t        j                  j                         n# t
        $ r Y nw xY wj                  j                          w xY w)Nrx   r   r   r   )_returncodern   r}   	_write_fdr{   r   rd   _errorr~   _doneset)output	exit_codeexcr   r    s      r!   _workerz0_ThreadedProcessHandle.__init__.<locals>._workerK  s    !$+I!	#, HHT^^V]]79]-UVHHT^^, 

    %!#$  %  HHT^^, 

 s|   B 1B B4 	B
B BB 	B1B,'C ,B11C 4	C ?C DC%$D%	C1.D0C11DTr   )
_cancel_fnr   Eventr   r   r   rn   pipefdopen_stdoutr   r   r3   )r    r   r   read_fdwrite_fdr   ts   ``     r!   r"   z_ThreadedProcessHandle.__init__<  sz    
 $__&
'+(, GGIyy#	R!	!& GD9		r#   c                     | j                   S r&   )r   r'   s    r!   r   z_ThreadedProcessHandle.stdouta  s    ||r#   r$   c                     | j                   S r&   )r   r'   s    r!   r   z!_ThreadedProcessHandle.returncodee  s    r#   c                 R    | j                   j                         r| j                  S d S r&   )r   is_setr   r'   s    r!   r   z_ThreadedProcessHandle.polli  s"    #'::#4#4#6t@D@r#   c                 ^    | j                   r	 | j                          y y # t        $ r Y y w xY wr&   )r   rd   r'   s    r!   r   z_ThreadedProcessHandle.killl  s1    ??!   s     	,,r   c                 R    | j                   j                  |       | j                  S )Nr   )r   r   r   r   s     r!   r   z_ThreadedProcessHandle.waits  s     

(r#   r&   )rM   rN   rO   rP   r   tuplerR   r   r"   rQ   r   r   r   r   r   r   rS   r#   r!   r   r   3  s     04#"eCHo-.# BH%,#J    C$J    AcDj A EDL  C  r#   r   
session_idc                     d|  dS )N__HERMES_CWD___rS   )r   s    r!   _cwd_markerr   }  s    :,b))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fdZded	edefdZedededefd       Z	 d-ddde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d&ded	ed
edz  dedz  d'edede
fd(Zd) Zd* Zdedeeedz  f   fd+Z y)0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                      y)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/tmprS   r'   s    r!   get_temp_dirzBaseEnvironment.get_temp_dir  s     r#   Ncwdr   envc                 t   || _         || _        |xs i | _        t        j                         j
                  d d | _        | j                         j                  d      xs d}| d| j                   d| _	        | d| j                   d| _
        t        | j                        | _        d| _        d| _        y )N   /z/hermes-snap-z.shz/hermes-cwd-z.txtF)r   r   r   uuiduuid4hex_session_idr   rstrip_snapshot_path	_cwd_filer   _snapshot_ready_prefer_nonlogin)r    r   r   r   temp_dirs        r!   r"   zBaseEnvironment.__init__  s    9"::<++CR0$$&--c29c!)
-8H8H7IM$:\$2B2B1C4H&t'7'78$ !&r#   Fx   loginr   r   
cmd_stringr   r   c                D    t        t        |       j                   d      )zSpawn 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typerM   )r    r   r   r   r   s        r!   	_run_bashzBaseEnvironment._run_bash  s"     "T$Z%8%8$99T"UVVr#   c                      y)z<Release backend resources (container, instance, connection).NrS   r'   s    r!   cleanupzBaseEnvironment.cleanup  s     	r#   c                 
   | j                  | j                        }| j                  | j                        }| j                  | j                  dz         dz   }d| d| d| d| d| d| d	| d
| d| d| d| j                   d| j                   d}	 | j                  |d| j                        }| j                  || j                        }t        |j                  d      xs d      dk7  rt        d|j                  d             d| _        | j                  |       t        j                  d| j                  | j                         y# t         $ r'}d| _        t#        |      }d}		 | j                  ddt%        d| j                              }
| j                  |
t%        d| j                              }t        |j                  d      xs d      dk(  }	|	s'|j                  d      xs |j'                         xs |}n# t         $ r}| d| }Y d}~nd}~ww xY w|	| _        |	r"t        j+                  d| j                  |       n&t        j+                  d| j                  |       Y d}~yY d}~yd}~ww xY w)zCapture 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``.
        .tmp.$BASHPIDzumask 077
export -p > z
__hermes_fns=$(declare -F | awk '{print $3}' | grep -vE '^_[^_]') || true
[ -n "$__hermes_fns" ] && declare -f $__hermes_fns >> z! 2>/dev/null || true
alias -p >> z#
echo 'shopt -s expand_aliases' >> z
echo 'set +e' >> z
echo 'set +u' >> z
mv -f  z
 || rm -f z
builtin cd -- z 2>/dev/null || true
printf '\n%sz\n' "$(pwd -P)"
T)r   r   r   r   r   z)snapshot bootstrap failed with exit code z-Session snapshot created (session=%s, cwd=%s)Ftrue   r   z; non-login probe: Nu_   init_session failed (session=%s): %s — login bash unusable; falling back to non-login bash -cuL   init_session failed (session=%s): %s — falling back to bash -l per command)_quote_cwd_for_cdr   _quote_shell_pathr   r   r  r   _wait_for_processr   rc   RuntimeErrorr   _update_cwdloggerinfor   rd   rR   r.   stripr   warning)r    _quoted_cwd_quoted_snap	_snap_tmp	bootstraprt   resultr   detailprefer_nonloginprobeprobe_result	probe_excs                r!   init_sessionzBaseEnvironment.init_session  s     ,,TXX6 --d.A.AB$ **4+>+>+HIJV	$+ &  $+ &11: <  ){ +  ){ + Kqj D(M ***+2d.>.>-??S3U 	8-	>>)4AWAW>XD++D$:P:P+QF6::l+0q1Q6"?

<@X?YZ  $(D V$KK?  
  	#(D 
 XF#OCvUCDLbLbDcd#55eSTMcMcEd5e"%l&6&6|&D&I"Ja"O&*..x8BFIIKUvF C"8#6ykBC %4D!M$$	 :$$	 '	sE   B<E 
JI=0BH
	I=
	H$HI=H$$AI==Jc                     | dk(  r| S | dk(  ry| j                  d      rdt        j                  | dd        S t        j                  |       S )z7Quote a ``cd`` target while preserving ``~`` expansion.~z~/z$HOMEz$HOME/r   N)
startswithshlexquote)r   s    r!   r  z!BaseEnvironment._quote_cwd_for_cdB  sR     #:J$;>>$EKKAB0122{{3r#   r   c                 ,    t        j                  |      S )zQuote *path* for interpolation into a bash script.

        LocalEnvironment overrides this to rewrite native/mixed Windows
        paths to ``/c/...`` before quoting. Remote backends leave paths
        as-is (they already speak POSIX).
        )r$  r%  )r    r   s     r!   r  z!BaseEnvironment._quote_shell_pathM  s     {{4  r#   commandc                    |j                  dd      }| j                  | j                        }| j                  | j                  dz         dz   }g }| j                  r|j	                  d| d       | j                  |      }|j	                  d| d       |j	                  d	| d       |j	                  d
       |j	                  d       | j                  r|j	                  d| d| d| d| d	       |j	                  d| j                   d| j                   d       |j	                  d       dj                  |      S )zwBuild the full bash script that sources snapshot, cd's, runs command,
        re-dumps env vars, and emits CWD markers.'z'\''r  r  zsource z >/dev/null 2>&1 || truezbuiltin cd -- z || exit 126zeval 'z__hermes_ec=$?z	umask 077z{ export -p > z
 && mv -f r	  z; } 2>/dev/null || rm -f z 2>/dev/null || truez
printf '\nr
  z\n' "$(pwd -P)"zexit $__hermes_ec
)r   r  r   r   r/   r  r   r?   )r    r'  r   escapedr  r  parts
quoted_cwds           r!   _wrap_commandzBaseEnvironment._wrap_commandV  s[    //#w/ --d.A.AB **4+>+>+HIJV	 LL,'?@ ++C0
~j\>? 	vgYa()%& 	[! LL!)Jyk<. Q((1{2FH 	$**+2d.>.>-??QR	
 	()yyr#   c                 d    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'  r   	delimiters      r!   _embed_stdin_heredocz$BaseEnvironment._embed_stdin_heredoc  sA     $DJJL$4$4Sb$9#:;	%	{#j\I;GGr#   )bounded_capturert   r3  c          
         |r	 ddl m}  |       }nt        }t	        |       t        j                  d      d      fdfd}t        j                  |d	
      }|j                          t        j                         |z   }t        j                         }	|	|	d}
t        j                         j                  }t        dd      }d}|	}d}t               du }t         r&t"        j%                  d||||sdndt'                      	 d}j)                         |dz  }t'               rot         r1t"        j%                  d|||t        j                         |
d   z
         | j+                         |j-                  d       j/                  d      ddS t        j                         |kD  rt         rt"        j%                  d||||       | j+                         |j-                  d       d| d}j0                  dk(  r j/                  |      j3                         nj/                  |      ddS t5        |
d        t         rt        j                         |z
  d!k\  rht               du }t"        j%                  d"|||t        j                         |
d   z
  t'               |sdnd|r|sd#nd$       t        j                         }|}t        j6                  |       |d%k  rt9        |d&z  d%      }j)                         |j-                  d       	 j>                  jA                          t         r<t"        j%                  d(|||t        j                         |
d   z
  jB                         j/                         jB                  dS # t        $ r d}Y w xY w# t:        t<        f$ rl t         r1t"        j%                  d'|||t        j                         |
d   z
         	 | j+                         |j-                  d        # t        $ r Y  w xY ww xY w# t        $ r Y w xY w))u  Poll-based wait with interrupt checking and stdout draining.

        Shared across all backends — not overridden.

        ``bounded_capture=True`` (foreground terminal-tool path only) retains
        at most ``tool_output.max_bytes`` of output in a head/tail window
        while draining, so a verbose subprocess cannot OOM the process
        (#64435). The default (False) preserves full-fidelity capture for
        internal consumers — file-operation ``cat`` reads feeding the patch
        engine, code-execution RPC reads, log reads — where truncation would
        corrupt data.

        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   )get_max_bytesiP  rx   r   r   c                    	 | D ]P  }|t        |t              r!j                  j                  |             7j                  t	        |             R 	 	 j                  dd      }|rj                  |       y y # t
        $ r Y 4w xY w# t
        $ r Y y w xY w# 	 j                  dd      }|rj                  |       w w # t
        $ r Y w w xY wxY w)Nr#   Tfinal)rz   bytesr/   decoderR   rd   )streampiecerB   decoderr   s      r!   _drain_iterablez:BaseEnvironment._wait_for_process.<locals>._drain_iterable  s    # 2E} !%/gnnU&;<c%j12">>#T>:Dd+    ! 	">>#T>:Dd+   s`   AB B  &B 	BB  BB  	BB C"&C
C
	CCCCc                     j                   } | y t        | dd       }	 t        |      r |       nd }t	        |t
              r|dk  r	 |        y t        j                  dk(  re	 	 t        j                  |d      }|sn!
j                  	j                  |             :	 	 	j                  dd      }|r
j                  |       y d}	 	 	 t        j                  |gg g d      \  }}}|r=	 t        j                  |d      }|sn?
j                  	j                  |             d}nj                         |d	z  }|d
k\  rny	 	j                  dd      }|r
j                  |       y y # t        $ r d }Y Jw xY w# t        t        f$ r Y w xY w# t        $ r Y y w xY w# 	 	j                  dd      }|r
j                  |       w w # t        $ r Y w w xY wxY w# t        t        f$ r Y w xY w# t        t        f$ r Y w xY w# t        $ r Y y w xY w# 	 	j                  dd      }|r
j                  |       w w # t        $ r Y w w xY wxY w)Nfilenor   ntT   r#   r7  g?r      )r   rZ   callablerd   rz   r   rn   namereadr/   r:  
ValueErrorr   selectr   )r;  r@  fdr6   rB   idle_after_exitreadyrE   r>  r=  r   rt   s           r!   _drainz1BaseEnvironment._wait_for_process.<locals>._drain  s    [[F~VXt4F!)&!1VXt b#&"q&' ww$ "D 1$!gnnU&;<	  "
&~~c~>"MM$/ O&,mmRD"b#&Fq! "$&GGB$5E  %!gnnU&;<*+0 (1,*a/!) 2">>#T>:Dd+ m   #G,  % 	&~~c~>"MM$/  $  '0 
 !+G4 "!"* ! 	">>#T>:Dd+   s   E/ %;F  F% "&F H G! +H .G6 AH &H /E>=E>FF% FF% 	F"!F"%G'&GG	GGGG!G30H 2G33H 6HH HH 	HHI&II	IIIITr   )r`   r3   pidNFzg[interrupt-debug] _wait_for_process ENTER tid=%s pid=%s timeout=%ss activity_cb=%s initial_interrupt=%sr   MISSINGg{Gzt?r   ut   [interrupt-debug] _wait_for_process INTERRUPT DETECTED tid=%s pid=%s iter=%d elapsed=%.1fs — killing process groupr3   r   r   z
[Command interrupted]r:      )r   r   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)r9   g?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)"tools.tool_output_limitsr5  rd   _UNBOUNDED_CAPTURE_CHARSr   codecsgetincrementaldecoderr   r   r3   ra   rb   current_threadidentrZ   r[   _DEBUG_INTERRUPTr  r  r   r   _kill_processr?   rL   r*   lstriprg   sleepr.   KeyboardInterrupt
SystemExitr   r~   r   )r    rt   r   r3  r5  capture_limitrL  drain_threaddeadline_now_activity_state_tid_pid_iter_count_last_heartbeat_last_interrupt_state_cb_was_none_poll_sleeptimeout_msg_cb_now_noner>  r=  r   s    `                  @@@r!   r  z!BaseEnvironment._wait_for_process  s#   4 'B - 5M(74 8&..w7yI	4F	P !''vdC>>#g-~~
 '')//tUD) %-/47KKBdG)y U	K))+%q !#'\ $T^^5EX_H`5`
 &&t, %%a%0"(--7P-"Q&)  >>#h.'@ $W
 &&t, %%a%0$?y"KK "--2 #)--{-"C"J"J"L#]]+]>&)	  &o7QR
 $(8?(Jd(R#9#;t#CLKK8 dK(?7+CC&(%1y0<\,WY	 '+nn&6O#/L 

;'$"%kC&7"=K} ))+%p 	!$	KK KKDdK ?7#;; !--/IIW  ' &'D ":. 	  g$NN$w'??	""4(!!!!,   %	:  		sc   N! BN3 #B"N3 CN3 (P1 !N0/N03AP.:#PP.	P*'P.)P**P.1	P=<P=c                 Z    	 |j                          y# t        t        t        f$ r Y yw xY w)zDTerminate a process. Subclasses may override for process-group kill.N)r   ProcessLookupErrorPermissionErrorr   )r    rt   s     r!   rX  zBaseEnvironment._kill_process  s)    	IIK"OW= 		s    **r  c                 &    | j                  |       y)zDExtract CWD from command output. Override for local file-based read.N)_extract_cwd_from_output)r    r  s     r!   r  zBaseEnvironment._update_cwd  s    %%f-r#   c                    |j                  dd      }| j                  }|j                  |      }|dk(  ryt        d|dz
        }|j                  |||      }|dk(  s||k(  ry||t	        |      z   | j                         }|r|| _        |j                  dd|      }|dk(  r|}|j                  d|t	        |      z         }	|	dk7  r|	dz   n
t	        |      }	|d| ||	d z   |d<   y)	zParse 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   r9   Nr   rB  r*  r   )rc   r   rfindr   r-   r  r   find)
r    r  r   markerlastsearch_startr8   cwd_path
line_startline_ends
             r!   ro  z(BaseEnvironment._extract_cwd_from_output  s     Hb)!!||F#2: 1dTk*V\48B;%4-%#f+-5;;=DH \\$51
J;;tTCK%78#+r>8a<s6{!+:.	1BBxr#   c                      y)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.
        NrS   r'   s    r!   _before_executezBaseEnvironment._before_execute  s     	r#   T)r   r   rewrite_compound_backgroundr3  r|  c                   | j                          | j                  |      \  }}|rddlm}	  |	|      }|xs | j                  }
|xs | j
                  }||||z   }n||}n|}|r#| j                  dk(  r| j                  ||      }d}| j                  ||      }| j                   xr | j                   }| j                  |||
|      }| j                  ||
|      }| j                  |       |S )u  Execute a command, return {"output": str, "returncode": int}.

        ``bounded_capture=True`` caps stdout/stderr retention at
        ``tool_output.max_bytes`` WHILE the stream is drained (head/tail
        window) instead of holding the full output in memory (#64435).
        It must only be set by callers whose output is destined for the
        model/tool payload (the foreground terminal tool). Internal
        full-fidelity consumers — file operations ``cat`` reads that feed
        the patch engine, code-execution RPC reads, log reads — MUST leave
        it False: truncating those corrupts data, not just display.
        r   )_rewrite_compound_backgroundNheredocr   )r   r3  )r{  _prepare_commandtools.terminal_toolr~  r   r   r   r2  r.  r   r   r  r  r  )r    r'  r   r   r   r|  r3  exec_command
sudo_stdinr~  effective_timeouteffective_cwdeffective_stdinwrappedr   rt   r  s                    r!   executezBaseEnvironment.execute  s$   * 	#'#8#8#A j 'H7EL#3t||txx !j&<(:5O#(O(O t//9<44\?SL"O$$\=A (((F1F1F-F~~5*;  
 ''+_ ( 
 	 r#   c                 $    | j                          y)z.Alias for cleanup (compat with older callers).N)r  r'   s    r!   stopzBaseEnvironment.stopW  s    r#   c                 D    	 | j                          y # t        $ r Y y w xY wr&   )r  rd   r'   s    r!   __del__zBaseEnvironment.__del__[  s"    	LLN 		s    	c                     ddl m}  ||      S )z6Transform sudo commands if SUDO_PASSWORD is available.r   )_transform_sudo_command)r  r  )r    r'  r  s      r!   r  z BaseEnvironment._prepare_commanda  s    ?&w//r#   r&   )r   r   )r9   )!rM   rN   rO   rP   r   rR   __annotations__r   r   r   dictr"   boolr   r  r   r  r   staticmethodr  r  r.  r2  r  rX  r  ro  r{  r  r  r  r   r  rS   r#   r!   r   r     s,    K  sc &C &# &D &0 !%WW 	W
 W $JW 
W  sr  s  s    !c !c !@ S @ s @ s @ L Hc Hs Hs H H 36jJRWjJ!jJ,/jJKOjJ	jJX	- .$ . Ct  CL" <
 #!%,0 %<< <
 t< $J< &*< < 
<D0 0c3:o0F 0r#   r   r&   )<rP   rS  r   loggingrn   rH  r$  r   r   ra   r   abcr   r   collectionsr   pathlibr   typingr   r   r	   hermes_constantsr
   hermes_cli._subprocess_compatr   tools.interruptr   	getLoggerrM   r  r  ro   rW  setLevelINFOlocalrV   rR  r   rR   rX   r[   r  rg   rs   r   r   listr   r   r   r   r   r   r   r   r   r   r   rS   r#   r!   <module>r     s      	       #   ) ) , < *			8	$ 			":;< 
 OOGLL! +9??,  % ZS ZSz+hud{3d: +t +
?# 5 < ? 
4 (9j&& 9c 9d 9F .2	c #d
.4 D B4 Bt B Bs uUCZ'84'? ,H ,$B  B T*C *C *_0c _0r#   