Ë
    ãß²jÑN  ã            	       óÔ  — 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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mZ ddlmZ d	Zd
Z G d„ d«      Zdedefd„Zdedede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" G d„ de«      Z# G d„ d«      Z$d&de#dedd ddfd!„Z%d&d"ededdfd#„Z&d&d"ededdfd$„Z'	 d&de#dedd dejP                  fd%„Z)y)'a  Process-handle plumbing for ``tools.environments.base``.

Bounded output capture (head/tail window + disk spill), the ``ProcessHandle``
duck type, the SDK adapter ``_ThreadedProcessHandle``, stdin piping, and the
stdout drain thread used by ``BaseEnvironment._wait_for_process``.
é    N)Údeque)ÚPath)ÚIOÚCallableÚProtocol)Úget_hermes_home)Úhead_tail_splitÚtruncation_notice)Úwindows_hide_flagsl   ÿÿÿÿ i€:	 c                   ó�   — e Zd ZdZdZddeddfd„Zded	dfd
„Zd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)Ú_BoundedOutputCollectorzýRetain a bounded 40/60 head-tail window of streamed text. When ``spill_path`` is set,
    the FULL stream is also teed to that file once eviction begins (up to ``_SPILL_CAP_CHARS``)
    so a truncated foreground result is recoverable without re-running.i@KL NÚ	max_charsÚ
spill_pathzPath | Nonec                 ón  — t        dt        |«      «      | _        t        | j                  dz  «      | _        | j                  | j                  z
  | _        g | _        t        «       | _        d| _        d| _	        d| _
        t        j                  «       | _        || _        d | _        d| _        d| _        y )Né   gš™™™™™Ù?r   F)ÚmaxÚintr   Ú_head_limitÚ_tail_limitÚ_headr   Ú_tailÚ_head_charsÚ_tail_charsÚ_total_charsÚ	threadingÚLockÚ_lockÚ_spill_pathÚ	_spill_fhÚ_spill_charsÚ_spill_capped)Úselfr   r   s      úC/home/ubuntu/.hermes/hermes-agent/tools/environments/base_output.pyÚ__init__z _BoundedOutputCollector.__init__%   s–   € Ü˜Q¤ I£Ó/ˆŒÜ˜tŸ~™~°Ñ3Ó4ˆÔØŸ>™>¨D×,<Ñ,<Ñ<ˆÔØ "ˆŒ
Ü!&£ˆŒ
ØˆÔØˆÔØˆÔÜ—^‘^Ó%ˆŒ
Ø%ˆÔØ)-ˆŒØˆÔØ"ˆÕó    ÚtextÚreturnc                 ó   — | j                   �| j                  ry	 | j                  €¢ddlm}m}  || j                   j                  d¬«        || j                   dd¬«      | _        dj                  | j                  «      dj                  | j                  «      z   }| j                  j                  |«       t        |«      | _        | j                  | j                  z
  }|dk  st        |«      |kD  rK| j                  j                  |dt        d|«       «       | j                  j                  d	«       d| _        n| j                  j                  |«       | xj                  t        |«      z  c_        y# t        $ r
 d| _        Y yw xY w)
zATee ``text`` to the spill file (opened lazily on first overflow).Nr   )Úensure_spill_dirÚopen_exclusiveT)ÚprivateÚreplace)r+   ÚerrorsÚ z+
... [spill capped at 5,000,000 chars] ...
)r   r!   r   Útools.spill_safetyr)   r*   ÚparentÚjoinr   r   ÚwriteÚlenr    Ú_SPILL_CAP_CHARSr   ÚOSError)r"   r&   r)   r*   ÚbacklogÚbudgets         r#   Ú_maybe_spillz$_BoundedOutputCollector._maybe_spill4   s<  € à×ÑÐ# t×'9Ò'9Øð	&Ø�~‰~Ð%ßOñ ! ×!1Ñ!1×!8Ñ!8À$ÕGÙ!/°×0@Ñ0@È$ÐW`Ô!a�”àŸ'™' $§*¡*Ó-°·±¸¿
¹
Ó0CÑC�Ø—‘×$Ñ$ WÔ-Ü$'¨£L�Ô!Ø×*Ñ*¨T×->Ñ->Ñ>ˆFØ˜Š{œc $›i¨&Ò0Ø—‘×$Ñ$ T¨/¬3¨q°&«>Ð%:Ô;Ø—‘×$Ñ$Ð%TÔUØ%)�Õ"à—‘×$Ñ$ TÔ*Ø×Ò¤ T£Ñ*ÖøÜò 	&à!%ˆDÖð	&ús   ›EE: Å:FÆFc                 óþ   — | j                   5  | j                  €
	 ddd«       y	 | j                  j                  «        d| _        t	        | j
                  «      cddd«       S # t        $ r Y Œ0w xY w# 1 sw Y   yxY w)z8Close the spill file and return its path if it was used.N)r   r   Úcloser5   Ústrr   ©r"   s    r#   Úclose_spillz#_BoundedOutputCollector.close_spillO   sz   € à�Z‰Zñ 	)Ø�~‰~Ð%Ø÷	)ð 	)ðØ—‘×$Ñ$Ô&ð "ˆDŒNÜ�t×'Ñ'Ó(÷	)ñ 	)øô
 ò Ùðú÷	)ð 	)ús-   �A3¥A$¿A3Á$	A0Á-A3Á/A0Á0A3Á3A<c                 ó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_chars[   s2   € à�Z‰Zñ 	7Ø×#Ñ# d×&6Ñ&6Ñ6÷	7÷ 	7ò 	7ús   �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_chars`   s'   € à�Z‰Zñ 	%Ø×$Ñ$÷	%÷ 	%ò 	%ús   �#£,c                 ó  — |sy | j                   5  t        |«      }| j                  �9| j                  €| j                  |z   | j
                  kD  r| j                  |«       | 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                  z
  x}dkD  rœ| j                  d   }t        |«      |k  r9| j                  j!                  «        | xj                  t        |«      z  c_        n'||d  | j                  d<   | xj                  |z  c_        | j                  | j                  z
  x}dkD  rŒœd d d «       y # 1 sw Y   y xY w)Nr   )r   r3   r   r   r   r   r8   r   r   Úminr   Úappendr   r   Úclearr   Úpopleft)	r"   r&   Útext_lenÚstartÚtakeÚ	remainingÚchunkÚexcessÚfirsts	            r#   rE   z_BoundedOutputCollector.appende   s<  € ÙØØ�Z‰Zñ #	/Ü˜4“yˆHà×ÑÐ+Ø—‘Ð*¨d×.?Ñ.?À(Ñ.JÈTÏ^É^Ò.[Ø×!Ñ! $Ô'Ø×Ò Ñ)ÕØˆEà×Ñ $×"2Ñ"2Ò2Ü˜4×+Ñ+¨d×.>Ñ.>Ñ>ÀÓI�ÙØ—J‘J×%Ñ% d¨5¨D kÔ2Ø×$Ò$¨Ñ,Õ$Ø �Eà  5Ñ(ˆIØ˜AŠ~ ×!1Ñ!1°QÒ!6Ø÷%#	/ð #	/ð& ˜D×,Ñ,Ò,Ø—
‘
× Ñ Ô"Ø—
‘
×!Ñ! $¨×(8Ñ(8Ð'8Ð':Ð";Ô<Ø#'×#3Ñ#3�Ô Ø÷/#	/ð #	/ð2 ˜˜�LˆEØ�J‰J×Ñ˜eÔ$Ø×Ò¤ E£
Ñ*ÕØ!×-Ñ-°×0@Ñ0@Ñ@Ð@�6ÀAÒEØŸ
™
 1™�Ü�u“: Ò'Ø—J‘J×&Ñ&Ô(Ø×$Ò$¬¨E«
Ñ2Ö$à$)¨&¨' N�D—J‘J˜q‘MØ×$Ò$¨Ñ.Õ$ð "×-Ñ-°×0@Ñ0@Ñ@Ð@�6ÀAÓE÷9#	/÷ #	/ñ #	/ús   �C5I<ÄA$I<Å;C7I<É<Jr.   )ÚsuffixrO   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 ]P  }t        d| j                  t        d|t        |«      z
  «      z
  «      }t        || j                  «      }||k(  r n|}ŒR t        d|t        |«      z
  «      }	t        |	«      \  }
}|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.Nr.   é   r   )r   r3   r   r1   r   r   r   Úranger   r
   r	   )r"   rO   ÚheadÚtailÚ	availableÚnoticeÚ_ÚomittedÚupdatedÚcontent_budgetÚ
head_charsÚ
tail_charsÚrendered_tails                r#   Úrenderz_BoundedOutputCollector.render�   sh  € à�Z‰Zñ 	SÜ�6‹{˜dŸn™nÒ,Ø˜tŸ~™~˜oÐ/Ð0÷	Sñ 	Sð —7‘7˜4Ÿ:™:Ó&ˆDØ—7‘7˜4Ÿ:™:Ó&ˆDØŸ™¬¨V«Ñ4ˆIØ× Ñ  IÒ-Ø˜d‘{ VÑ+÷	Sñ 	Sð ˆFÜ˜1“Xò !�Ü˜a ×!2Ñ!2´S¸¸IÌÈFËÑ<SÓ5TÑ!TÓU�Ü+¨G°T×5FÑ5FÓG�Ø˜fÒ$ÙØ ‘ð!ô !  I´°F³Ñ$;Ó<ˆNÜ%4°^Ó%DÑ"ˆJ˜
Ù2<˜D *  Ñ.À"ˆMØ˜˜Ð$ v¨j¨yÐ'9Ñ9¸MÑIÈFÑR÷/	S÷ 	Sò 	Sús   �(E¿A$EÂ-B EÅE r?   )r'   z
str | None)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r4   r   r$   r;   r8   r=   Úpropertyr@   rB   rE   r^   © r%   r#   r   r      s    „ ñKð
 !Ðñ# #ð #°=ó #ð& ð &¨ó &ó6
)ð ð7 ò 7ó ð7ð ð%˜Sò %ó ð%ð&/˜3ð &/ 4ó &/ðP ')ò S ð S¨Sô Sr%   r   Úbounded_capturer'   c           	      óR  — |st        t        «      S 	 ddlm}  |«       }d}	 t        «       dz  dz  }|dt        t        j                  «       «      › dt        j                  «       › dt        | «      d	z  d
›d�z  }|j                  «       r_t        j                  «       t        z
  }|j                  d«      D ]0  }	 |j                  «       j                  |k  r|j!                  «        Œ2 t        ||¬«      S # t        $ r d}Y Œåw xY w# t"        $ r Y Œ\w xY w# t        $ r d}Y Œ:w xY w)a²  Build the collector for one ``_wait_for_process`` call. ``bounded_capture`` (foreground
    terminal path only) caps retention at ``tool_output.max_bytes`` and tees overflow to a
    spill file under ``$HERMES_HOME/cache/terminal-output`` (created only on actual overflow;
    spills older than 7 days are pruned opportunistically). Otherwise the collector is
    effectively unbounded so internal consumers keep full-fidelity output.r   )Úget_max_bytesiPÃ  NÚcachezterminal-outputzout-Ú-iÿÿ  Úxz.logz	out-*.log)r   )r   Ú_UNBOUNDED_CAPTURE_CHARSÚtools.tool_output_limitsrg   Ú	Exceptionr   r   ÚtimeÚosÚgetpidÚidÚis_dirÚ_SPILL_MAX_AGE_SÚglobÚstatÚst_mtimeÚunlinkr5   )Úprocre   rg   Úcapture_limitr   Ú	spill_dirÚcutoffÚolds           r#   Ú_new_output_collectorr}   ©   s$  € ñ Ü&Ô'?Ó@Ð@ðÝ:Ù%›ˆð €JðÜ#Ó%¨Ñ/Ð2CÑCˆ	Ø 4¬¬D¯I©I«KÓ(8Ð'9¸¼2¿9¹9»;¸-ÀqÌÈDËÐTZÑIZÐ[\ÐH]Ð]aÐ!bÑbˆ
Ø×ÑÔÜ—Y‘Y“[Ô#3Ñ3ˆFØ —~‘~ kÓ2ò �ðØ—x‘x“z×*Ñ*¨VÒ3ØŸ
™
œøðô # =¸ZÔHÐHøô! ò ØŠðûô ò Ùðûäò ØŠ
ðúsG   “C8 £BD Â<-D	Ã)D Ã8DÄDÄ		DÄD ÄDÄD ÄD&Ä%D&Ú	collectorÚrenderedÚ
returncodec                 ó\   — ||dœ}| j                  «       }|r| j                  |d<   ||d<   |S )zHAssemble a wait result, attaching spill metadata when overflow occurred.)Úoutputr€   Úoutput_total_charsÚfull_output_path)r=   rB   )r~   r   r€   ÚresultÚspills        r#   Ú_finalize_wait_resultr‡   Ç   s>   € à °
Ñ;€FØ×!Ñ!Ó#€EÙØ'0×'<Ñ'<ˆÐ#Ñ$Ø%*ˆÐ!Ñ"Ø€Mr%   rx   Údatac                 ó†   ‡ ‡‡— g Š‰‰ _         ˆˆˆ fd„}t        j                  |d¬«      }|‰ _        |j	                  «        y)aQ  Write *data* to proc.stdin on a daemon thread to avoid pipe-buffer deadlocks.
    Writes go through ``proc.stdin.buffer`` as UTF-8 bytes we encode ourselves: Windows
    text-mode stdin would translate ``\n`` -> ``\r\n`` and corrupt every write_file/patch
    payload. Encoding uses ``surrogateescape`` (exact inverse of the read-side decode);
    surrogates outside U+DC80-U+DCFF raise, the error is recorded on
    ``proc._hermes_stdin_errors`` (surfaced by ``_wait_for_process`` as ``stdin_error``) and
    stdin is still closed in ``finally`` so the child sees EOF instead of hanging.
    c                  óN  •— ‰j                   €‰j                  t        d«      «       y t        ‰j                   d‰j                   «      } 	 t	        ‰t
        «      r‰j                  dd«      n‰}| j                  |«      }|t        |«      k7  rt        d|› dt        |«      › d�«      ‚	 	 | j                  «        y # t        t        f$ r Y Œ#t        $ r}‰j                  |«       Y d }~ŒAd }~ww xY w# t        $ r Y y w xY w# 	 | j                  «        w # t        $ r Y w w xY wxY w)Nzprocess stdin unavailableÚbufferúutf-8Úsurrogateescapezshort stdin write: z of z bytes)ÚstdinrE   ÚRuntimeErrorÚgetattrÚ
isinstancer;   Úencoder2   r3   ÚBrokenPipeErrorr5   rm   r:   )ÚtargetÚrawÚwrittenÚexcrˆ   r-   rx   s       €€€r#   Ú_writez_pipe_stdin.<locals>._writeÞ   s  ø€ Ø�:‰:ÐØ�M‰Mœ,Ð'BÓCÔDØô ˜Ÿ™ X¨t¯z©zÓ:ˆð	Ü=GÈÌcÔ=R�$—+‘+˜gÐ'8Ô9ÐX\ˆCØ—l‘l 3Ó'ˆGØœ#˜c›(Ò"ä"Ð%8¸¸	ÀÄcÈ#ÃhÀZÈvÐ#VÓWÐWð #ðØ—‘•øô  ¤Ð)ò 	ÙÜò 	à�M‰M˜#×Ñûð	ûô ò ÙðûðØ—‘•øÜò Ùðýss   ÁAB< Â)D Â+C3 Â<C0ÃD ÃC0ÃC+Ã&D Ã+C0Ã0D Ã3	C?Ã>C?ÄD$ÄDÄD$Ä	D!ÄD$Ä D!Ä!D$T©r”   ÚdaemonN)Ú_hermes_stdin_errorsr   ÚThreadÚ_hermes_stdin_threadrI   )rx   rˆ   r˜   Úthreadr-   s   ``  @r#   Ú_pipe_stdinrŸ   Ò   s<   ú€ ð #%€FØ &€DÔöô0 ×Ñ V°DÔ9€FØ &€DÔØ
‡L�L…Nr%   ÚcmdÚ
stdin_datac           
      ó  — |j                  dt        «       «       t        j                  | ft        j                  t        j
                  |�t        j                  nt        j                  ddddœ|¤Ž}|�t        ||«       |S )zúSpawn a subprocess with standard stdout/stderr/stdin setup; *stdin_data* is written
    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.ÚcreationflagsTrŒ   r,   )ÚstdoutÚstderrrŽ   r&   Úencodingr-   )Ú
setdefaultr   Ú
subprocessÚPopenÚPIPEÚSTDOUTÚDEVNULLrŸ   )r    r¡   Úkwargsrx   s       r#   Ú_popen_bashr®   û   s|   € ð ×Ñ�oÔ'9Ó';Ô<Ü×ÑØðä�‰Ü× Ñ Ø!+Ð!7Œj�oŠo¼Z×=OÑ=OØ˜G¨Iñð ñ€Dð ÐÜ�D˜*Ô%Ø€Kr%   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)ÚProcessHandlez�Duck type every backend's _run_bash() must return. subprocess.Popen satisfies this
    natively; SDK backends (Modal, Daytona) return _ThreadedProcessHandle.r'   Nc                  ó   — y r?   rd   r<   s    r#   ÚpollzProcessHandle.poll  ó   � r%   c                  ó   — y r?   rd   r<   s    r#   ÚkillzProcessHandle.kill  r³   r%   Útimeoutc                  ó   — y r?   rd   ©r"   r¶   s     r#   ÚwaitzProcessHandle.wait  r³   r%   c                  ó   — y r?   rd   r<   s    r#   r¤   zProcessHandle.stdout  ó   € Ø(+r%   c                  ó   — y r?   rd   r<   s    r#   r€   zProcessHandle.returncode  r»   r%   )r'   Nr?   )r_   r`   ra   rb   r   r²   rµ   Úfloatr¹   rc   r   r;   r¤   r€   rd   r%   r#   r°   r°     s\   „ ñNð &�c˜D‘jÓ%ÛÙ<˜E D™LÐ<°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)Ú_ThreadedProcessHandlea  Adapter for SDK backends (Modal, Daytona) that have no real subprocess: runs a blocking
    ``exec_fn() -> (output_str, exit_code)`` on a background thread behind a ProcessHandle
    interface. ``cancel_fn`` is invoked on ``kill()`` for backend-specific cancellation.NÚexec_fnÚ	cancel_fnc                 ó$  ‡ ‡— |‰ _         t        j                  «       ‰ _        d ‰ _        t        j                  «       \  }}t        j                  |ddd¬«      ‰ _        |‰ _	        ˆˆ fd„}t        j                  |d¬«      j                  «        y )NÚrrŒ   r,   )r¦   r-   c                  ó  •— 	  ‰«       \  } }|‰_         	 t        j                  ‰j                  | j	                  dd¬«      «       	 t        j                  ‰j                  «       ‰j                  j                  «        y # t
        $ r Y ŒFw xY w# t        $ r
 d‰_         Y Œ\w xY w# t
        $ r Y ŒKw xY w# 	 t        j                  ‰j                  «       n# t
        $ r Y nw xY w‰j                  j                  «        w xY w)NrŒ   r,   ©r-   r   )
Ú_returncodero   r2   Ú	_write_fdr’   r5   rm   r:   Ú_doneÚset)r‚   Ú	exit_coderÀ   r"   s     €€r#   Ú_workerz0_ThreadedProcessHandle.__init__.<locals>._worker+  sØ   ø€ ð!Ù$+£IÑ!�˜	Ø#,�Ô ðÜ—H‘H˜TŸ^™^¨V¯]©]¸7È9¨]Ó-UÔVðÜ—H‘H˜TŸ^™^Ô,ð —
‘
—‘Õ øô ò Ùðûäò %Ø#$�Ö ð%ûô
 ò ÙðûðÜ—H‘H˜TŸ^™^Õ,øÜò Ùðúà—
‘
—‘Õ úsv   ƒB •1B ÁB& Â	BÂ
B ÂBÂB ÂB#Â B5 Â"B#Â#B5 Â&	B2Â1B2Â5DÂ7CÃDÃ	C#Ã DÃ"C#Ã#DTr™   )Ú
_cancel_fnr   ÚEventrÈ   rÆ   ro   ÚpipeÚfdopenÚ_stdoutrÇ   rœ   rI   )r"   rÀ   rÁ   Úread_fdÚwrite_fdrË   s   ``    r#   r$   z_ThreadedProcessHandle.__init__!  so   ù€ Ø#ˆŒÜ—_‘_Ó&ˆŒ
Ø'+ˆÔô ŸG™G›IÑˆ�Ü—y‘y ¨#¸È	ÔRˆŒØ!ˆŒõ	!ô" 	×Ñ °Ô5×;Ñ;Õ=r%   c                 ó   — | j                   S r?   )rÐ   r<   s    r#   r¤   z_ThreadedProcessHandle.stdout>  s   € à�|‰|Ðr%   r'   c                 ó   — | j                   S r?   )rÆ   r<   s    r#   r€   z!_ThreadedProcessHandle.returncodeB  s   € à×ÑÐr%   c                 óR   — | j                   j                  «       r| j                  S d S r?   )rÈ   Úis_setrÆ   r<   s    r#   r²   z_ThreadedProcessHandle.pollF  s"   € Ø#'§:¡:×#4Ñ#4Ô#6ˆt×ÑÐ@¸DÐ@r%   c                 ó^   — | j                   r	 | j                  «        y y # t        $ r Y y w xY wr?   )rÌ   rm   r<   s    r#   rµ   z_ThreadedProcessHandle.killI  s1   € Ø�?Š?ðØ—‘Õ!ð øô ò Ùðús   Ž   	,«,r¶   c                 óR   — | j                   j                  |¬«       | j                  S )N)r¶   )rÈ   r¹   rÆ   r¸   s     r#   r¹   z_ThreadedProcessHandle.waitP  s    € Ø�
‰
�‰ ˆÔ(Ø×ÑÐr%   r?   )r_   r`   ra   rb   r   Útupler;   r   r$   rc   r¤   r€   r²   rµ   r½   r¹   rd   r%   r#   r¿   r¿     s    „ ñ\ñ> ¨¨U°3¸°8©_Ð)<Ñ =ð >È(ÐSUÐW[ÐS[ÑJ\Ð_cÑJcó >ð: ñó ðð ð ˜C $™Jò  ó ð ðA�c˜D‘jó Aòñ ˜E D™Lð  °Cô  r%   r¿   r‚   Ústopzthreading.Event | Nonec                 ó¾  — | j                   }|€y t        j                  d«      d¬«      }	 |j                  «       }	 t        |t        «      r|dk  rF|D ]@  }|€Œ|j                  t        |t        «      r|j                  |«      n
t        |«      «       ŒB n2t        j                  dk(  rt        | ||||«       nt        | ||||«       	 |j                  dd¬	«      }|r|j                  |«       yy# t        $ r d}Y ŒÄw xY w# t        $ r Y ŒEw 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)
ux  Drain ``proc.stdout`` into *output* until EOF or shortly after exit.
    ``for line in proc.stdout`` would block on ``readline()`` until EOF, and a backgrounded
    grandchild (``cmd &``, ``setsid cmd & disown``) inherits the pipe's write end â€” so the
    tool would hang for the grandchild's lifetime. Instead we ``select()`` with a short poll
    and stop ~300ms after bash exits even if the pipe has not EOF'd. Raw 4096-byte ``os.read``
    chunks can split a multibyte UTF-8 sequence, so an incremental decoder with
    ``errors="replace"`` buffers partial sequences across chunks. Streams without a real
    integer ``fileno()`` (mocks, in-memory adapters) are iterated to EOF instead â€” otherwise
    the thread would die silently and lose all output. ``select()`` does not work on pipe fds
    on Windows, so :func:`_drain_fd_windows` polls ``PeekNamedPipe`` there instead.
    NrŒ   r,   rÅ   r   Úntr%   T)Úfinal)r¤   ÚcodecsÚgetincrementaldecoderÚfilenorm   r‘   r   rE   ÚbytesÚdecoder;   ro   ÚnameÚ_drain_fd_windowsÚ_drain_fd_select)rx   r‚   rÚ   ÚstreamÚdecoderÚfdÚpiecerT   s           r#   Ú_drain_stdoutrê   V  sb  € ð �[‰[€FØ€~Øð 4Œf×*Ñ*¨7Ó3¸9ÔE€GðØ�]‰]‹_ˆðÜ˜"œcÔ" b¨1¢fØò e�ØÑ$Ø—M‘M¼:ÀeÌUÔ;S '§.¡.°Ô"7ÔY\Ð]bÓYcÕdñeô �W‰W˜Š_Ü˜d B¨°¸Õ>ä˜T 2 v¨w¸Ô=ð
	Ø—>‘> #¨T�>Ó2ˆDÙØ—‘˜dÕ#ð øô# ò ØŠðûô ò Ùðûô ò 	Ùð	ûð		Ø—>‘> #¨T�>Ó2ˆDÙØ—‘˜dÕ#ð øäò 	Ùð	ýsq   ­C4 ¾D ÁA0D Ã&D Ã4DÄDÄ	DÄD# ÄDÄD# Ä	D ÄD Ä#EÄ%&EÅEÅ	EÅEÅEÅErè   c                 ót  — d}	 |�|j                  «       ry	 t        j                  |gg g d«      \  }}}|r=	 t	        j
                  |d«      }|sy|j                  |j                  |«      «       d}n| j                  «       �|dz  }|dk\  ryŒŒ# t        t        f$ r Y yw xY w# t        t        f$ r Y yw xY w)u°   POSIX drain: select() poll, stopping ~300ms after bash exits with the pipe idle, or
    when *stop* is set (the pipe is being handed to another reader â€” yield-to-background).r   Nçš™™™™™¹?é   r   é   )	rÖ   ÚselectÚ
ValueErrorr5   ro   ÚreadrE   râ   r²   )	rx   rè   r‚   rç   rÚ   Úidle_after_exitÚreadyrW   rL   s	            r#   rå   rå   �  sÓ   € ð €OØ
ØÐ §¡¤Øð	Ü Ÿ-™-¨¨¨b°"°cÓ:‰KˆE�1�añ ðÜŸ™  DÓ)�ñ ØØ�M‰M˜'Ÿ.™.¨Ó/Ô0Ø‰OØ�Y‰Y‹[Ð$ð ˜qÑ ˆOØ !Ò#Øð+ øô
 œGÐ$ò 	Ùð	ûô
 ¤Ð(ò Ùðús"   ˜B ¸B% ÂB"Â!B"Â%B7Â6B7c           	      óÂ  — ddl }ddl}	 |j                  |«      }|dv ry|j	                  |«      }|j
                  j                  }	|j                  d«      }
d}	 |�|j                  «       ry	 |	j                  |ddd|j                  |
«      d«      }|sy|
j                  rZ	 t        j                  |t        t        |
j                  «      d«      «      }|sy|j#                  |j%                  |«      «       d}Œ£| j'                  «       �|dz  }|dk\  ryt)        j*                  d«       ŒÓ# t        $ r Y yw xY w# t        $ r Y yw xY w# t         t        f$ r Y yw xY w)aw  Windows drain: poll ``PeekNamedPipe`` because ``select`` cannot poll pipes.

    ``os.read`` on a Windows pipe blocks until data is available or every writer
    closes the pipe.  A backgrounded grandchild can retain a writer indefinitely,
    so use the Win32 pipe API to check availability before reading and apply the
    same post-exit idle bound as the POSIX drain.
    r   N)éÿÿÿÿr   rí   r   rî   rì   )ÚctypesÚmsvcrtÚget_osfhandler5   Úc_void_pÚwindllÚkernel32Úc_ulongrÖ   ÚPeekNamedPipeÚbyrefÚvaluero   rñ   rD   r   rð   rE   râ   r²   rn   Úsleep)rx   rè   r‚   rç   rÚ   rö   r÷   Ú
raw_handleÚhandlerû   rU   rò   ÚokrL   s                 r#   rä   rä   ©  sb  € ó ÛðØ×)Ñ)¨"Ó-ˆ
ð �WÑØà�_‰_˜ZÓ(€FØ�}‰}×%Ñ%€HØ—‘˜qÓ!€IØ€OØ
ØÐ §¡¤Øð	Ø×'Ñ'¨°°a¸¸v¿|¹|ÈIÓ?VÐX\Ó]ˆBñ ØØ�?Š?ðÜŸ™ ¤C¬¨I¯O©OÓ(<¸dÓ$CÓD�ñ ØØ�M‰M˜'Ÿ.™.¨Ó/Ô0ØˆOØØ�9‰9‹;Ð"Ø˜qÑ ˆOØ !Ò#ØÜ�
‰
�3Œð/ øô ò Ùðûô ò 	Ùð	ûô ¤Ð(ò Ùðús5   ŠD. Á/%D= Â$3E Ä.	D:Ä9D:Ä=	E	ÅE	ÅEÅEc                 ód   — t        j                  t        | ||fd¬«      }|j                  «        |S )zLStart the daemon thread running :func:`_drain_stdout`; *stop* ends it early.T)r”   Úargsrš   )r   rœ   rê   rI   )rx   r‚   rÚ   rž   s       r#   Ú_start_drain_threadr  Ù  s.   € ô ×Ñ¤]¸$ÀÈÐ9MÐVZÔ[€FØ
‡L�L„NØ€Mr%   r?   )*rb   rÞ   ro   rï   r¨   r   rn   Úcollectionsr   Úpathlibr   Útypingr   r   r   Úhermes_constantsr   Útools.tool_output_truncater	   r
   Úhermes_cli._subprocess_compatr   rk   rs   r   Úboolr}   r;   r   Údictr‡   r©   rŸ   Úlistr®   r°   r¿   rê   rå   rä   rœ   r  rd   r%   r#   ú<module>r     s�  ðñó Û 	Û Û Û Û Ý Ý ß )Ñ )å ,ß IÝ <ð %Ð àÐ ÷ISñ ISðXI°ð IÐ:Qó Ið<Ð%<ð Èð ÐY\Ð_cÑYcð Ðhló ð&�j×&Ñ&ð &¨cð &°dó &ñR�T˜#‘Yð ¨C°$©Jð ÈJ×L\ÑL\ó ô$,�Hô ,÷6 ñ 6 ñt4˜ð 4Ð/Fð 4ÐNfð 4Ðrvó 4ñn˜sð Ð,Cð Ð\`ó ñ8- ð -Ð-Dð -Ð]aó -ðb `dñØðØ%<ðØD\ðà×Ñôr%   