+
    lHàj)+  ã                  ó¶  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	H
t
 ^ RIHt ]P                  ! ]4      t]P                   ! 4       tRsR]R&   ]P(                  ! 4       tRsR]R&   ^ sR	tR
s]! 0 R(m4      tR R ltR)R R lltR R ltR R ltR R lt R R lt!R R lt"R R lt#R R lt$RR
/R R llt%R  R! lt&R" R# lt'R$ R% lt(R& R' lt)R# )*a¯  Local-environment toolchain probe for the system prompt: when the terminal backend
is local, one deterministic line about Python tooling (python3/python versions, missing
pip, pip bound to another Python, PEP 668) so models don't discover it by hitting
walls. Cached per process; "" when clean. Remote backends are skipped (the sandbox has
its own probe in agent/prompt_builder). Toggle: ``agent.environment_probe`` in config.yaml.)ÚannotationsN)ÚOptional)Úwindows_hide_flagsúOptional[str]Ú_CACHED_LINEzOptional[threading.Thread]Ú_PROBE_THREADg      $@Fc               ó    € V ^8„  d   QhRRRR/# )é   ÚbackendÚstrÚreturnÚbool© )Úformats   "Ú4/home/ubuntu/.hermes/hermes-agent/tools/env_probe.pyÚ__annotate__r   *   s   € ÷ 	ñ 	 sð 	¨tñ 	ó    c                ó’   € V '       d   V \         9   g   V R8X  d   R#  ^ RIHp \        V! V RR4      4      #   \         d     R# i ; i)zCWhether a plugin-registered terminal backend is remote (fail-soft).ÚlocalF)Úprovider_flagÚ	is_remote)Ú_REMOTE_BACKENDSÚagent.terminal_env_registryr   r   Ú	Exception)r
   r   s   & r   Ú_plugin_backend_is_remoter   *   sE   € ç�gÔ!1Ô1°WÀÔ5GÙðÝ=ä‘M '¨;¸Ó>Ó?Ð?øÜô Úðús   ž7 ·AÁAc               ó$   € V ^8„  d   QhRRRRRR/# )r	   Úcmdz	list[str]ÚtimeoutÚfloatr   ztuple[int, str, str]r   )r   s   "r   r   r   6   s"   € ÷ )ñ )ˆið ) %ð )Ð2Fñ )r   c                ó:  €  \         P                  ! 4       ;_uu_ 4       p\         P                  ! 4       ;_uu_ 4       p \        P                  ! WW1R\        P                  \        4       R7      pTP                  ^ 4       TP                  ^ 4       TP                  TP                  4       P                  RR4      P                  4       TP                  4       P                  RR4      P                  4       3uuRRR4       uuRRR4       #   \        P                   d    R	u uuRRR4       uuRRR4       # i ; i  + '       g   i     M; iRRR4       R#   + '       g   i     R# ; i  \         d    R
u # \         d   pRRRT 23u Rp?# Rp?ii ; i)a3  Run a short subprocess -> (returncode, stdout, stderr); failures (binary
missing, timeout, OSError) return (-1, "", "<reason>"). Output goes through temp
files, not pipes, so ``timeout`` bounds the *whole* call even on native Windows: a
console-script launcher (``pip.exe``) can spawn a descendant that inherits the
captured handles and outlives its parent; with OS pipes ``communicate()``'s reader
threads block until that grandchild closes the write end (a warm probe could hang
~28 min holding ``_CACHE_LOCK``). Temp files make ``wait()`` cover only the child.F)ÚstdoutÚstderrr   ÚcheckÚstdinÚcreationflagsÚ Nzutf-8Úreplacez	oserror: éÿÿÿÿ)r'   r%   r   )r'   r%   z	not found)ÚtempfileÚTemporaryFileÚ
subprocessÚrunÚDEVNULLr   ÚTimeoutExpiredÚseekÚ
returncodeÚreadÚdecodeÚstripÚFileNotFoundErrorÚOSError)r   r   Úout_fÚerr_fÚresultÚexcs   &&    r   Ú_runr9   6   s;  € ð)Ü×#Ò#×%Ô%¨´×0FÒ0F×0HÔ0HÈEð)Ü#ŸšØ¨eÈEä$×,Ñ,Ô<NÓ<PôR�ð �J‰J�qŒMØ�J‰J�qŒMØ×%Ñ% u§z¡z£|×':Ñ':¸7ÀIÓ'N×'TÑ'TÓ'VØ—J‘J“L×'Ñ'¨°Ó;×AÑAÓCðE÷ 1IÑ0H×%Ò%øô ×,Ñ,ô )Ø(Ñ(÷ 1IÑ0H×%Ò%ð)ú÷ 1I×0Hú×%×%×%Ò%ûô ô #Ø"Ò"Üô )Ø�2˜ 3 %Ð(Ð(Õ(ûð)ús“   ‚E/ ŸE½D>	¿1DÁ0BD>	Ã8
EÄ
E/ ÄD;Ä$D>	Ä%
EÄ/
E/ Ä:D;Ä;D>	Ä>EÅ	EÅE/ ÅE,	Å&E/ Å,E/ Å/FÅ>FÆFÆFÆFÆFc               ó$   € V ^8„  d   QhRRRRRR/# )r	   Úbinaryr   Úargsr   r   r   )r   s   "r   r   r   Q   s!   € ÷ $ñ $�Cð $ ð $¨ñ $r   c                óv   € \         P                  ! V 4      '       g   R# \        V .VO4      w  r#pV^ 8X  d   V# R# )zOstdout of ``<binary> *args`` when the binary is on PATH and exits 0, else None.N)ÚshutilÚwhichr9   )r;   r<   ÚrcÚoutÚ_errs   &*   r   Ú_py_outrC   Q   s9   € ä�<Š<˜×ÒÙÜ˜&˜ 4˜Ó)�M€BˆTØ˜”'ˆ3Ð#˜tÐ#r   c               ó    € V ^8„  d   QhRRRR/# )r	   r;   r   r   r   r   )r   s   "r   r   r   Y   s   € ÷ /ñ /˜sð / }ñ /r   c                ó4   € Rp\        V RV4      ;'       g    R# )zFReturn a short version string like ``3.12.4`` for ``binary``, or None.z;import sys; print('.'.join(map(str, sys.version_info[:3])))ú-cN©rC   ©r;   Úcodes   & r   Ú_python_version_ofrJ   Y   s   € àH€DÜ�6˜4 Ó&×.Ð.¨$Ð.r   c               ó    € V ^8„  d   QhRRRR/# ©r	   r;   r   r   r   r   )r   s   "r   r   r   _   s   € ÷ Añ A˜Cð A Dñ Ar   c                ó"   € \        V RRR4      RJ# )z/True if ``<binary> -m pip --version`` succeeds.z-mÚpipú	--versionNrG   )r;   s   &r   Ú_has_pip_modulerP   _   s   € ä�6˜4 ¨Ó4¸DÐ@Ð@r   c               ó    € V ^8„  d   QhRRRR/# rL   r   )r   s   "r   r   r   d   s   € ÷ @ñ @˜3ð @ 4ñ @r   c                óV   € Rp\        V RV4      ;'       g    RP                  4       R8H  # )zTrue when ``<binary>`` is PEP-668 externally-managed (``EXTERNALLY-MANAGED``
marker next to the stdlib, as Debian/Ubuntu ship).zuimport os; print('yes' if os.path.exists(os.path.join(os.path.dirname(os.__file__), 'EXTERNALLY-MANAGED')) else 'no')rF   r%   Úyes)rC   r2   rH   s   & r   Ú_detect_pep668rT   d   s/   € ðN€Dä�F˜D $Ó'×-Ð-¨2×4Ñ4Ó6¸%Ñ?Ð?r   c               ó   € V ^8„  d   QhRR/# )r	   r   r   r   )r   s   "r   r   r   l   s   € ÷ ñ ˜]ñ r   c                 óÂ   € \        RR4      ;'       g    Rp RV 9   dB   V P                  R4      '       d+   V P                  R^4      ^,          RR P                  4       # R# )uŒ   If ``pip`` is on PATH, the Python version it's bound to â€” the trailing
``(python X.Y)`` of ``pip --version`` (e.g. ``"3.12"``), else None.rN   rO   r%   z(python Ú)Nr'   )rC   ÚendswithÚrsplitr2   )rA   s    r   Ú_pip_python_versionrZ   l   sW   € ô �%˜Ó
%×
+Ð
+¨€CØ�SÔ˜SŸ\™\¨#×.Ò.Ø�z‰z˜* aÓ(¨Õ+¨C¨RÐ0×6Ñ6Ó8Ð8Ùr   c               ó   € V ^8„  d   QhRR/# ©r	   r   r   r   )r   s   "r   r   r   u   s   € ÷ ñ  3ñ r   c                 ó¾   €  ^ RI Hp  V ! R4      ;'       g    RP                  4       P                  4       #   \         d    \
        P                  RRR7        R# i ; i)z@Scope-aware terminal backend name (``local`` when unresolvable).©Úterminal_envÚTERMINAL_ENVr   z"terminal backend resolution failedT)Úexc_info)Útools.terminal_scoper_   r2   Úlowerr   ÚloggerÚdebugr^   s    r   Ú_resolve_terminal_backendrf   u   sR   € ðÝ5á˜^Ó,×7Ð7°×>Ñ>Ó@×FÑFÓHÐHøÜô Ü�‰Ð9ÀDˆÔIÚðús   ‚6 —6 ¶"AÁAc               ó   € V ^8„  d   QhRR/# r\   r   )r   s   "r   r   r   €   s   € ÷ '8ñ '8˜3ñ '8r   c                 óÜ  € \        R4      p \        R4      pV '       d   \        R4      MRp\        4       pV '       d   \        R4      MRp\        P
                  ! R4      RJp\        T;'       d!    T ;'       d    V P                  V4      '       * 4      pV e#   V'       d   V'       g   V'       d	   V'       d   R# . pV '       d'   TP                  RV  2V'       d   RMR,           4       MVP                  R	4       V'       d   W8w  d   VP                  R
V 24       M!V'       g   V '       d   VP                  R4       V'       d<   V'       d   VP                  RV R24       M6V'       g   VP                  RV 24       MV'       g   VP                  R4       V'       d   VP                  R4       V'       d   VP                  R4       RRP                  V4      ,           R,           # )u•   Build the one-liner; "" when nothing notable is detected â€” the goal is to
save the model from an avoidable wall, not narrate a healthy environment.Úpython3ÚpythonFÚuvNr%   zpython3=z (no pip module)zpython3=missingzpython=zpython=missing (use python3)u   pipâ†’pythonz (mismatch)zpip=missingzPEP 668=yes (use venv or uv)zuv=installedzPython toolchain: z, Ú.)
rJ   rP   rZ   rT   r>   r?   r   Ú
startswithÚappendÚjoin)Úpy3_verÚpy_verÚpy3_has_pipÚpip_bound_toÚ
py3_pep668Úhas_uvÚmismatchÚbitss           r   Ú_build_probe_linerx   €   sb  € ô ! Ó+€GÜ Ó)€Fß07”/ )Ô,¸U€KÜ&Ó(€Lß.5” 	Ô*¸5€Jô �\Š\˜$Ó tÐ+€Fä�L×UÐU W×UÐU°W×5GÑ5GÈÓ5UÔ1UÓV€HØÒŸ{·8ÇZ×SYÙà€DßØ�‰�h˜w˜iÐ(·+©BÐCUÕVÕWà�‰Ð%Ô&ß�&Ô#Ø�‰�g˜f˜XÐ&Õ'ßŸà�‰Ð2Ô3ßßØ�K‰K˜, | n°KÐ@ÕAßØ�K‰K˜, | nÐ5Ô6øßà�‰�MÔ"ßØ�‰Ð2Ô3ßØ�‰�NÔ#Ø $§)¡)¨D£/Õ1°CÕ7Ð7r   Úforce_refreshc               ó    € V ^8„  d   QhRRRR/# )r	   ry   r   r   r   r   )r   s   "r   r   r   ª   s   € ÷ !ñ !°ð !À#ñ !r   c                ó²  € V '       d   \        4        \        4       pV\        9   g   \        V4      '       d   R# \        P                  4       '       d   \        ;'       g    R# \        4        \        '       d   RM\        p\        P                  VR7      '       g+   \        '       g   Rs\        P                  R\        4       R# \        ;'       g    R# )u¬  Return the cached probe line (building it on first call); "" when the
environment is clean, so the prompt assembler drops the section. Waits at most
``_PROBE_WAIT_TIMEOUT`` on the single worker, then fails open with "".
``force_refresh`` is for tests.

A wedged probe subprocess (#67964) therefore can never block system-prompt construction â€” at worst the
toolchain line is absent from prompts built while the probe is stuck.
r%   gš™™™™™©?)r   Tzcenv_probe did not finish within %.0fs; building the system prompt without the Python toolchain line)Ú_reset_cache_for_testsrf   r   r   Ú_PROBE_DONEÚis_setr   Ú_ensure_probe_startedÚ_WAIT_ALREADY_TIMED_OUTÚ_PROBE_WAIT_TIMEOUTÚwaitrd   Úwarning)ry   r
   Úwait_timeouts   $  r   Úget_environment_probe_liner…   ª   s¨   € ÷ ÜÔ ô (Ó)€GØÔ"Ô"Ô&?À×&HÒ&HÙÜ×Ñ×ÒÜ×!Ð!˜rÐ!ÜÔß2Ó2‘4Ô8K€LÜ×Ñ LÐ×1Ò1÷ 'Ó&Ø&*Ð#Ü�N‰Nð;Ü<OôQñ Ü×Ð˜2Ðr   c               ó    € V ^8„  d   QhRRRR/# )r	   ÚgenÚintr   ÚNoner   )r   s   "r   r   r   Î   s   € ÷ ñ �sð ˜tñ r   c                ó*  €  \        4       p\        ;_uu_ 4        V \
        8w  d    RRR4       R# Vs\        P                  4        RRR4       R#   \         d#   p\        P                  RT4       Rp Rp?LmRp?ii ; i  + '       g   i     R# ; i)uD   Body of the single probe thread â€” computes and publishes the line.zenv_probe failed: %sr%   N)	rx   r   rd   re   Ú_CACHE_LOCKÚ
_PROBE_GENr   r}   Úset)r‡   Úliner8   s   &  r   Ú_probe_workerr�   Î   sp   € ðÜ Ó"ˆ÷ 
ŽØ”*ÔØ÷ 
‰ð ˆÜ�‰Ô÷	 
‰øô ô Ü�‰Ð+¨SÔ1Ø�ûðú÷ 
�ˆús'   ‚
A šB±BÁA>ÁA9Á9A>ÂB	c               ó   € V ^8„  d   QhRR/# ©r	   r   r‰   r   )r   s   "r   r   r   Ý   s   € ÷ ñ ˜tñ r   c            	     óX  € \         ;_uu_ 4        \        P                  4       '       g#   \        e&   \        P	                  4       '       d    RRR4       R# \
        P                  ! \        \        3RRR7      s\        P                  4        RRR4       R#   + '       g   i     R# ; i)z?Start the probe worker if it isn't running and hasn't finished.Nz	env-probeT)Útargetr<   ÚnameÚdaemon)
r‹   r}   r~   r   Úis_aliveÚ	threadingÚThreadr�   rŒ   Ústartr   r   r   r   r   Ý   sk   € ÷ 
ŽÜ×Ñ×Ò¤MÒ$=Ä-×BXÑBX×BZÒBZØ÷ 
‰ô "×(Ò(Ü ¬
 }¸;ÈtôUˆä×ÑÔ÷ 
��Šús   �<BÁBÁ7BÂB)	c               ó   € V ^8„  d   QhRR/# r‘   r   )r   s   "r   r   r   è   s   € ÷ ñ  dñ r   c                 ó   € \        4        R# )zÜStart the probe in the background so the first system-prompt build doesn't
pay the ~0.5s of subprocess calls on the time-to-first-token path. Idempotent;
``get_environment_probe_line`` waits (bounded) on the same worker.N)r   r   r   r   Úwarm_environment_probe_asyncrœ   è   s
   € ô Ör   c               ó   € V ^8„  d   QhRR/# r‘   r   )r   s   "r   r   r   ï   s   € ÷ (ñ ( ñ (r   c                 óª   € \         ;_uu_ 4        Rs\        P                  4        Rs\
        ^,          sRsRRR4       R#   + '       g   i     R# ; i)u8   Test helper â€” clear the cache between probe scenarios.NF)r‹   r   r}   Úclearr   rŒ   r€   r   r   r   r|   r|   ï   s:   € ÷ 
ŽØˆÜ×ÑÔØˆÜ�a�ˆ
Ø"'Ð÷ 
��Šús   �(AÁA	>   ÚsshÚmodalÚdockerÚdaytonaÚmanaged_modalÚvercel_sandbox)g      @)*Ú__conditional_annotations__Ú__doc__Ú
__future__r   Úloggingr>   r*   r(   r—   Útypingr   Úhermes_cli._subprocess_compatr   Ú	getLoggerÚ__name__rd   ÚLockr‹   r   Ú__annotations__ÚEventr}   r   rŒ   r�   r€   Ú	frozensetr   r   r9   rC   rJ   rP   rT   rZ   rf   rx   r…   r�   r   rœ   r|   )r¦   s   @r   Ú<module>r²      sÙ   øðô_õ #ã Û Û Û Û Ý å <à	×	Ò	˜8Ó	$€ð �nŠnÓ€Ø"€ˆmÓ "Ø�oŠoÓ€Ø,0€Ð)Ó 0Ø€
ØÐ ØÐ ñ ò ó Ð õ	÷)õ6$õ/õAõ
@õõõ'8ðT!¸÷ !õHõõ÷(r   