+
    lHàjpo  ã                  ó   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	t	^ RI
t
^ RIt^ RIHtHtHtHt ^ RIHtHtHt ^ RIt^ RIHt ^ RIHtHtHt ^ RIHtHt ^ RIH t  ^ R	I!H"t" ]PF                  ! ]$4      t%^t&R
t'Rs(R])R&   Rs*/ t+R])R&   Rs,R])R&   Rs-R R lt.R R lt/R R lt0R R lt1R R lt2R R lt3R R lt4R  R! lt5R" R# lt6RuR$ R% llt7R&R/R' R( llt8R) R* lt9R+ R, lt:R- R. lt;R/ R0 lt</ t=R1])R2&   ]
P|                  ! 4       t?R3 R4 lt@R5 R6 ltARvR7 R8 lltBR9 R: ltCRuR; R< lltDRuR= R> lltERuR? R@ lltFRwRA RB lltGRwRC RD lltHRwRE RF lltIRG RH ltJRI RJ ltKRK RL ltLRM RN ltMRO RP ltNRQ RR ltORuRS RT lltPRU RV ltQRuRW RX lltRRY RZ ltSR[ R\ ltTRxR] R^ lltURuR_ R` lltVRuRa Rb lltWRuRc Rd lltXRuRe Rf lltYRuRg Rh lltZRuRi Rj llt[RuRk Rl llt\Rm Rn lt]Ro Rp lt^RyRq Rr llt_RyRs Rt llt`R# )zuT  Camofox browser backend â€” local anti-detection browser via REST API.

Camofox-browser (https://github.com/jo-inc/camofox-browser) is a self-hosted Node.js
server wrapping Camoufox (Firefox fork with C++ fingerprint spoofing); its REST API maps
1:1 to our browser tool interface (accessibility snapshots with element refs, click/type/
scroll by ref, screenshots). Setup: ``npm start`` in a checkout or ``docker run -p 9377:9377
-e CAMOFOX_PORT=9377 jo-inc/camofox-browser``, then ``CAMOFOX_URL=http://localhost:9377`` in
``~/.hermes/.env`` (Docker: see ``CAMOFOX_REWRITE_LOOPBACK_URLS`` below).
)ÚannotationsN)ÚAnyÚCallableÚDictÚOptional)ÚSplitResultÚurlsplitÚ
urlunsplit)Ú
get_secret)Úcfg_getÚload_configÚread_raw_config)Úget_hermes_home_overrideÚhermes_home_key)Úget_camofox_identity)Ú
tool_errorz0No browser session. Call browser_navigate first.úOptional[str]Ú_vnc_urlFzdict[str, Optional[str]]Ú_vnc_url_by_camofox_urlzOptional[dict[str, int]]Ú_cached_cmd_timeoutc               ó   € V ^8„  d   QhRR/# )é   ÚreturnÚint© )Úformats   "Ú:/home/ubuntu/.hermes/hermes-agent/tools/browser_camofox.pyÚ__annotate__r   1   s   € ÷ ñ ˜cñ ó    c                 óR  € \        4       p \        f   / s\        '       d   V \        9   d   \        V ,          # \        p \	        \        4       RR4      pVe   \        \        V4      ^4      pT\        T &   RsT#   \         d!   p\        P                  RT4        Rp?L3Rp?ii ; i)z^``browser.command_timeout`` (floor 5s, default 30s), cached per profile home after first read.NÚbrowserÚcommand_timeoutz*Could not read browser.command_timeout: %sT)r   r   Ú_cmd_timeout_resolvedÚ_DEFAULT_TIMEOUTr   r   Úmaxr   Ú	ExceptionÚloggerÚdebug)ÚhomeÚtimeoutÚvalÚexcs       r   Ú_get_command_timeoutr,   1   s¡   € ô Ó€DÜÒ"Ø ÐßÓ Ô)<Ô!<Ü" 4Õ(Ð(Ü€GðHÜ”oÓ'¨Ð4EÓFˆØŠ?Üœ#˜c›( AÓ&ˆGð !(Ô˜ÑØ ÐØ€Nøô	 ô HÜ�‰ÐAÀ3×GÑGûðHús   Á .A; Á;B&ÂB!Â!B&c               ó   € V ^8„  d   QhRR/# )r   r   údict[str, str]r   )r   s   "r   r   r   E   s   € ÷ =ñ =�~ñ =r   c                 ól   € \        RR4      ;'       g    RP                  4       p V '       d   RRV  2/# / # )z8Return Authorization header when CAMOFOX_API_KEY is set.ÚCAMOFOX_API_KEYÚ ÚAuthorizationzBearer )r
   Ústrip)Úkeys    r   Ú_auth_headersr5   E   s8   € äÐ'¨Ó,×2Ð2°×
9Ñ
9Ó
;€Cß14ˆO˜w s e˜_Ð-Ð<¸"Ð<r   c               ó   € V ^8„  d   QhRR/# ©r   r   Ústrr   )r   s   "r   r   r   K   s   € ÷ =ñ =˜ñ =r   c                 óL   € \        RR4      ;'       g    RP                  R4      # )z:Return the configured Camofox server URL, or empty string.ÚCAMOFOX_URLr1   Ú/)r
   Úrstripr   r   r   Úget_camofox_urlr=   K   s"   € ä�} bÓ)×/Ð/¨R×7Ñ7¸Ó<Ð<r   c               ó   € V ^8„  d   QhRR/# r7   r   )r   s   "r   r   r   P   s   € ÷ 
ñ 
˜ñ 
r   c                 óú   €  ^ RI Hp  V ! 4       P                  R/ 4      p\        V\        4      '       d4   \        VP                  RR4      ;'       g    R4      P                  4       #  R#   \         d     R# i ; i)u‡   Persistent ``browser.cdp_url`` from config.yaml, or "" (read here, not via
``browser_tool_cdp._get_cdp_override`` â€” circular import).)r   r    Úcdp_urlr1   )Úhermes_cli.configr   ÚgetÚ
isinstanceÚdictr8   r3   r%   )r   Úbrowser_cfgs     r   Ú_config_cdp_urlrF   P   st   € ðÝ5Ù%Ó'×+Ñ+¨I°rÓ:ˆÜ�k¤4×(Ò(Ü�{—‘ y°"Ó5×;Ð;¸Ó<×BÑBÓDÐDð )ñ øô ô ØÙðús   ‚AA+ ÁA+ Á+A:Á9A:c               ó   € V ^8„  d   QhRR/# ©r   r   Úboolr   )r   s   "r   r   r   ]   s   € ÷ #ñ #˜ñ #r   c                 óæ   € ^ RI Hp Hp V! 4       '       g   V ! 4       '       g   \        4       '       d   R#  ^ RIHp V! R4      pVe   VR8H  # \        \        4       4      #   \         d    Rp L+i ; i)a­  True when the Camofox backend is selected and no CDP override is active.

Selection is ``browser.cloud_provider: camofox``; ``CAMOFOX_URL`` is only the address
and never overrides a different stored selection (legacy: with no selection ever
written, a set ``CAMOFOX_URL`` still activates Camofox). A CDP override (``BROWSER_CDP_URL``
env or ``browser.cdp_url``, same precedence as ``browser_tool_cdp._get_cdp_override()``) wins.
)Úenv_cdp_urlÚlive_cdp_overrideF)Úread_selectionr    NÚcamofox)	Útools.browser_tool_cdprK   rL   rF   Útools.tool_backend_helpersrM   r%   rI   r=   )rK   rL   rM   Úselecteds       r   Úis_camofox_moderR   ]   sk   € ÷ FÙ×Ò™kŸmšm¬×/@Ò/@ÙðÝ=Ù! )Ó,ˆð ÒØ˜9Ñ$Ð$Ü”Ó!Ó"Ð"øô	 ô ØŠðús   ¶A  Á A0Á/A0c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úurlr8   Úrespr   r   r   r   )r   s   "r   r   r   r   s!   € ÷ ñ ˜cð ¨ð °ñ r   c                ó  €  VP                  4       P                  R 4      p\        V\        4      '       d9   ^Tu;8:  d   R8:  d*   M R# R\	        V 4      P
                  ;'       g    R RV 2# R#   \        \        3 d     R# i ; i)ÚvncPortiÿÿ  zhttp://Ú	localhostÚ:N)ÚjsonrB   rC   r   r   ÚhostnameÚ
ValueErrorÚKeyError)rT   rU   Úvnc_ports   && r   Ú_vnc_url_from_healthr_   r   s‚   € ðØ—9‘9“;—?‘? 9Ó-ˆÜ�h¤×$Ò$¨¨hÖ)?¸%Ö)?ñ ð œX c›]×3Ñ3×BÐB°{ÐCÀ1ÀXÀJÐOÐOñ øô œÐ!ô ØÙðús   ‚AA1 Á
A1 Á'A1 Á1BÂBc               ó   € V ^8„  d   QhRR/# rH   r   )r   s   "r   r   r   |   s   € ÷ #ñ # ñ #r   c                 óh  € \        4       p V '       g   R#  \        P                  ! V  R2^R7      pTP                  ^È8X  dR   \        4       e   T \        9  d   \        Y4      \        T &   M'\        '       g   \        Y4      ;'       g    \        s	RsTP                  ^È8H  #   \         d     R# i ; i)zDVerify the Camofox server is reachable (and cache its VNC URL once).Fz/health©r)   T)
r=   ÚrequestsrB   r%   Ústatus_coder   r   r_   Ú_vnc_url_checkedr   )rT   rU   s     r   Úcheck_camofox_availablerf   |   s£   € ô Ó
€CßÙðÜ�|Š|˜s˜e 7˜O°QÔ7ˆð ×Ñ˜3ÔÜ#Ó%Ò1ØÔ1Ô1Ü/CÀCÓ/NÔ'¨Ñ,øß!Ó!Ü+¨CÓ6×BÐB¼(ˆHØ#ÐØ×Ñ˜sÑ"Ð"øô ô Úðús   –B" Â"B1Â0B1c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r   �   s   € ÷ 	ñ 	�]ñ 	r   c                 óº   € \        4       e5   \        4       p V \        9  d   \        4        \        P	                  V 4      # \
        '       g   \        4        \        # )z>Return the VNC URL if the Camofox server exposes one, or None.)r   r=   r   rf   rB   re   r   )rT   s    r   Úget_vnc_urlri   �   sD   € äÓ!Ò-ÜÓˆØÔ-Ô-Ü#Ô%Ü&×*Ñ*¨3Ó/Ð/ßÓÜÔ!Ü€Or   c               ó   € V ^8„  d   QhRR/# )r   r   údict[str, Any]r   )r   s   "r   r   r   œ   s   € ÷ @ñ @˜^ñ @r   c                 óì   €  \        4       P                  R/ 4      P                  R/ 4      p \        T \        4      '       d   T # / #   \         d#   p\        P	                  RT4       / u Rp?# Rp?ii ; i)z>Return the ``browser.camofox`` config block, or an empty dict.r    rN   z7camofox config check failed, defaulting to disabled: %sN)r   rB   r%   r&   ÚwarningrC   rD   )Úcamofox_cfgr+   s     r   Ú_get_camofox_configro   œ   sh   € ðÜ!“m×'Ñ'¨	°2Ó6×:Ñ:¸9ÀbÓIˆô % [´$×7Ò7ˆ;Ð?¸RÐ?øô ô Ü�‰ÐPÐRUÔVØ�	ûðús   ‚*A ÁA3ÁA.Á(A3Á.A3c               ó    € V ^8„  d   QhRRRR/# )r   rn   úOptional[dict[str, Any]]r   rI   r   )r   s   "r   r   r   ¦   s    € ÷ lñ lÐ.Fð lÐRVñ lr   c                óR   € \        V f   \        4       MT P                  R4      4      # )z\``browser.camofox.managed_persistence``: stable profile-scoped userId vs random per session.Úmanaged_persistence)rI   ro   rB   )rn   s   &r   Ú_managed_persistence_enabledrt   ¦   s$   € ä¨+Ò*=Ô$Ô&À;×SÑSÐTiÓjÓkÐkr   Úsecretc          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   Úenv_namer8   rn   rk   Úcfg_keyru   rI   r   r   )r   s   "r   r   r   «   s8   € ÷ Nñ N˜#ð N¨Nð NÀSð NÐUYð NÐfiñ Nr   c               ó   € V'       d   \        V R4      M\        P                  ! V R4      pT;'       g    RP                  4       ;'       g3    \	        VP                  V4      ;'       g    R4      P                  4       # )zOEnv/secret-scope value first, then the ``browser.camofox`` config key, else "".r1   )r
   ÚosÚgetenvr3   r8   rB   )rw   rn   rx   ru   Úraws   &&&$ r   Ú_env_or_cfgr}   «   sY   € ç&,Œ*�X˜rÔ
"´"·)²)¸HÀbÓ2I€CØ�IˆI�2×ÑÓ×MÐM¤# k§o¡o°gÓ&>×&DÐ&DÀ"Ó"E×"KÑ"KÓ"MÐMr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Útask_idr   rn   rk   r   zOptional[dict[str, str]]r   )r   s   "r   r   r   ±   s'   € ÷ eñ e¨ð eÀNð eÐWoñ er   c                óš   € \        RVRRR7      pV'       g   R# \        RVRRR7      pRTRT;'       g    RT ;'       g    R	R
,           2/# )zŠExternally configured identity (integrations owning the visible Camofox browser
share a user ID so Hermes uses the same profile), or None.ÚCAMOFOX_USER_IDÚuser_idT)ru   NÚCAMOFOX_SESSION_KEYÚsession_keyÚtask_ÚdefaultºNé   N)r}   )r   rn   r‚   r„   s   &&  r   Ú_camofox_identity_overrider‰   ±   s[   € ô Ð+¨[¸)ÈDÔQ€GßÙÜÐ3°[À-ÐX\Ô]€KØ�w ¨{×/cÐ/cÀÀw×G[ÐG[ÐR[Ð]`ÕFaÐEbÐ>cÐdÐdr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rw   r8   rn   rk   rx   r   rI   r   )r   s   "r   r   r   »   s(   € ÷ 	*ñ 	*�Cð 	* nð 	*¸sð 	*Àtñ 	*r   c                óü   € \         P                  ! V R4      P                  4       P                  4       pVR9   d   R# VR9   d   R# V'       d   \        P                  RW4       \        VP                  V4      4      # )zHBoolean toggle: env var wins when set to a valid value, else config key.r1   TFz"Ignoring invalid boolean env %s=%r>   Ú1ÚonÚyesÚtrue>   Ú0ÚnoÚoffÚfalse)rz   r{   r3   Úlowerr&   r'   rI   rB   )rw   rn   rx   r|   s   &&& r   Ú_flagr•   »   sb   € ä
�)Š)�H˜bÓ
!×
'Ñ
'Ó
)×
/Ñ
/Ó
1€CØ
Ð(Ô(ÙØ
Ð)Ô)Ùß
Ü�‰Ð9¸8ÔIÜ�—‘ Ó(Ó)Ð)r   c               ó    € V ^8„  d   QhRRRR/# )r   r[   r   r   rI   r   )r   s   "r   r   r   Ç   s   € ÷ 	ñ 	 Mð 	°dñ 	r   c                óè   € V '       g   R # V P                  4       P                  R4      P                  4       pVR9   d   R#  \        P                  ! V4      P                  #   \
         d     R # i ; i)Fz[]T>   rX   úlocalhost.localdomain)r3   r”   Ú	ipaddressÚ
ip_addressÚis_loopbackr\   )r[   Úhosts   & r   Ú_is_loopback_hostnamer�   Ç   sb   € ßÙØ�>‰>Ó×!Ñ! $Ó'×-Ñ-Ó/€DØÐ5Ô5ÙðÜ×#Ò# DÓ)×5Ñ5Ð5øÜô Úðús   ÁA" Á"A1Á0A1c               ó    € V ^8„  d   QhRRRR/# )r   rT   r8   r   z$tuple[str, Optional[dict[str, str]]]r   )r   s   "r   r   r   Ó   s   € ÷ tñ t¨3ð tÐ3Wñ tr   c           	     ó   € \        4       p\        RVR4      '       g   V R3#  \        V 4      p\	        RTR4      ;'       g    RpTP
                  R9  g$   \        TP                  4      '       d	   T'       g   T R3# TP                  '       d<   TP                  TP                  '       d   RTP                   2MR,           R	,           MRpRT9   d   TP                  R
4      '       g   R
T R2MTpTP                  '       d   RTP                   2MRp\        \        TP
                  T T T 2TP                  TP                  TP                   4      4      pTRTP                  ;'       g    RRTRT RT/3#   \         d    T R3u # i ; i)aò  Rewrite loopback page URLs for Docker-hosted Camofox, if configured.

``CAMOFOX_URL`` may point at a host-published Docker port, but page URLs are opened by
the browser *inside* the container, where loopback is the container, not the host.
Opt-in (``CAMOFOX_REWRITE_LOOPBACK_URLS`` / config) because non-Docker installs run the
browser on the host. Returns ``(rewritten_url, metadata)``; ``metadata`` is present only
when a rewrite happened so the tool result can disclose the change to the model.
ÚCAMOFOX_REWRITE_LOOPBACK_URLSÚrewrite_loopback_urlsNÚCAMOFOX_LOOPBACK_HOST_ALIASÚloopback_host_aliaszhost.docker.internalrY   r1   Ú@Ú[Ú]ÚfromÚtoÚoriginal_urlÚrewritten_url>   ÚhttpÚhttps)ro   r•   r   r\   r}   Úschemer�   r[   ÚusernameÚpasswordÚ
startswithÚportr	   r   ÚpathÚqueryÚfragment)rT   rn   ÚparsedÚaliasÚuserinfoÚ	host_partÚ	port_partÚ	rewrittens   &       r   Ú!_rewrite_loopback_url_for_camofoxr»   Ó   sv  € ô &Ó'€KÜÐ0°+Ð?V×WÒWØ�DˆyÐðÜ˜#“ˆô Ð5°{ÐDYÓZ×tÐtÐ^t€EØ‡}�}Ð-Ô-Ô5JÈ6Ï?É?×5[Ò5[×chØ�DˆyÐØ_e×_n×_nÐ_n�—‘¸F¿O¿O¸O Q v§¡Ð&7Ñ#8ÐQSÕTÐWZÖZÐtv€HØ # u¤°U×5EÑ5EÀc×5JÒ5J�!�E�7˜!‘ÐPU€IØ%+§[§[ [�!�F—K‘K�=Ñ!°b€IÜÜ�F—M‘M h Z°	¨{¸9¸+Ð#FÈÏÉÐU[×UaÑUaÐci×crÑcrÓsóu€Ià�v˜vŸ™×4Ð4°"°d¸EÀ>ÐSVÐXgÐirÐsÐsÐsøô ô Ø�DˆyÒðús   £E; Å;FÆFzdict[str, dict[str, Any]]Ú	_sessionsc               ó    € V ^8„  d   QhRRRR/# )r   Úsessionrk   r   r   )r   s   "r   r   r   ó   s   € ÷ ñ  ð °Nñ r   c                óD  € V P                  R4      '       g(   V P                  R4      '       d   \        4       '       g   V #  \        R\        V 4      ^R7      P                  R. 4      p\        T\        4      '       d+   T Uu. uF  p\        T\        4      '       g   K  TNK  	  upM. pT Uu. uF+  q3P                  R	4      T P                  R
4      8X  g   K)  TNK-  	  up;'       g    TpT'       d   TR,          P                  R4      MRp\        T\        4      '       d2   T'       d*   Y`R&   \
        P                  RY`P                  R4      4       T #   \         d3   p\
        P                  RT P                  R4      T4       T u Rp?# Rp?ii ; iu upi u upi )z’Rehydrate tab_id from an already-open managed tab: gateway restarts empty the
in-memory cache while Camofox still holds the integration-owned tab.Útab_idÚadopt_existing_tabú/tabs)Úparamsr)   Útabsz&Camofox tab adoption failed for %s: %sr‚   NÚ
listItemIdr„   ÚtabIdz&Adopted existing Camofox tab %s for %séÿÿÿÿ)rB   r=   Ú_getÚ_user_paramsr%   r&   r'   rC   ÚlistrD   r8   )r¾   rÄ   r+   ÚtabÚ	dict_tabsÚ
candidatesrÀ   s   &      r   Ú_adopt_existing_tabrÎ   ó   sE  € ð ‡{�{�8×Ò G§K¡KÐ0D×$EÒ$EÌ_×M^ÒM^ØˆðÜ�G¤L°Ó$9À1ÔE×IÑIÈ&ÐRTÓUˆô CMÈTÔSW×BXÒBX¡Ó>¡˜¬
°3¼×(=—�¡Ò>Ð^`€IÙ!*Ób¡˜#¯g©g°lÓ.CÀwÇ{Á{ÐS`ÓGaÑ.a—#�#¡Ñb×oÐoÐfo€Jß,6ˆZ˜�^×Ñ Ô(¸D€FÜ�&œ#×Ò§6Ø"�ÑÜ�‰Ð=¸vÇ{Á{ÐS\ÓG]Ô^Ø€Nøô ô Ü�‰Ð=¸w¿{¹{È9Ó?UÐWZÔ[Ø�ûðüò ?ùÚbs6   Á'E ÂFÂ FÂ/&FÃFÅFÅ#'FÆ
FÆFc               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   rk   r   )r   s   "r   r   r     s   € ÷ ,ñ ,˜-ð ,¨Nñ ,r   c                ó*  € T ;'       g    Rp \         ;_uu_ 4        V \        9   d!   \        \        V ,          4      uuRRR4       # \        4       p\	        W4      pVf   \        V4      '       d   \        V 4      pVf<   RR\        P                  ! 4       P                  R,           2RRV R,           2/pR	R	rCMR
\        RVR4      rCRVR,          RRRVR,          RVRV/pV\        V &   \        V4      uuRRR4       #   + '       g   i     R# ; i)u»   Get or create the task's session. Identity precedence: external override
(CAMOFOX_USER_ID / config) â†’ profile-scoped identity when managed persistence
is on â†’ random ephemeral userId.r†   Nr‚   Úhermes_:Né
   Nr„   r…   r‡   FTÚCAMOFOX_ADOPT_EXISTING_TABrÁ   rÀ   Úmanaged)Ú_sessions_lockr¼   rÎ   ro   r‰   rt   r   ÚuuidÚuuid4Úhexr•   )r   rn   ÚidentityrÔ   Úadoptr¾   s   &     r   Ú_get_sessionrÛ     s   € ð ×"Ð"˜€Gß	ŽØ”iÔÜ&¤y°Õ'9Ó:÷ 
Šô *Ó+ˆÜ-¨gÓCˆØÒÔ <¸[× IÒ IÜ+¨GÓ4ˆHØÒØ! W¬T¯ZªZ«\×-=Ñ-=¸cÕ-BÐ,CÐ#DÀmÐW\Ð]dÐehÕ]iÐ\jÐUkÐlˆHØ" E‘Uà!¤5Ð)EÀ{ÐThÓ#i�UØ˜h yÕ1°8¸TÀ=ÐRZÐ[hÕRiØ˜gÐ';¸UðDˆà$Œ	�'ÑÜ" 7Ó+÷ 
��‹ús   ›!DÁB/DÄD	c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   rT   r8   r   rk   r   )r   s   "r   r   r     s!   € ÷ ñ ˜ð ¨Sð À^ñ r   c           	     ó¢   € \        V 4      pVR,          '       g5   \        RRVR,          RVR,          RV/4      pVP                  R4      VR&   V# )	z<Ensure a tab exists for the session, creating one if needed.rÀ   rÂ   ÚuserIdr‚   rÅ   r„   rT   rÆ   )rÛ   Ú_postrB   )r   rT   r¾   Údatas   &&  r   Ú_ensure_tabrá     sU   € ä˜7Ó#€GØ�8×ÔÜ�W˜x¨°Õ);¸\È7ÐS`ÕKaÐchÐjmÐnÓoˆØ ŸH™H WÓ-ˆ�ÑØ€Nr   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   rq   r   )r   s   "r   r   r   &  s   € ÷ 9ñ 9˜=ð 9Ð-Eñ 9r   c                óœ   € \         ;_uu_ 4        \        P                  T ;'       g    RR4      uuRRR4       #   + '       g   i     R# ; i)zRemove and return session info.r†   N)rÕ   r¼   Úpop©r   s   &r   Ú_drop_sessionræ   &  s)   € ç	ŽÜ�}‰}˜W×1Ð1¨	°4Ó8÷ 
��‹ús	   � :ºA	c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   rI   r   )r   s   "r   r   r   ,  s   € ÷ 	ñ 	 -ð 	¸4ñ 	r   c                ó¤   € \        4       p\        V4      '       g   \        W4      '       d$   \        V 4       \        P                  RV 4       R# R# )z¾Drop only the local tracking entry (``True``) for managed profiles, which must
survive across agent tasks; ``False`` for ephemeral sessions so the caller falls back
to :func:`camofox_close`.z6Camofox soft cleanup for task %s (managed persistence)TF)ro   rt   r‰   ræ   r&   r'   )r   rn   s   & r   Úcamofox_soft_cleanupré   ,  s?   € ô &Ó'€KÜ# K×0Ò0Ô4NÈw×4dÒ4dÜ�gÔÜ�‰ÐMÈwÔWÙÙr   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   Úmethodr8   r²   r)   úOptional[int]Úkwargsr   r   úrequests.Responser   )r   s   "r   r   r   9  s0   € ÷ ñ �Sð  ð ¨mð Ècð ÐVgñ r   c                ó    € \        \        V 4      ! \        4        V 23R\        4       RVf   \	        4       MT/VB pVP                  4        V# )zTIssue an authenticated request to camofox and return the raised-for-status response.Úheadersr)   )Úgetattrrc   r=   r5   r,   Úraise_for_status)rë   r²   r)   rí   rU   s   &&&, r   Ú_requestró   9  s[   € ä”8˜VÔ$¬Ó(9Ð':¸4¸&Ð%Añ oÌ=Ë?ð oØGNÂÔ-AÔ-CÐ\cðoØgmño€Dà×ÑÔØ€Kr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r²   r8   ÚbodyrD   r)   rì   r   r   )r   s   "r   r   r   A  s(   € ÷ =ñ =�ð =˜4ð =¨-ð =À4ñ =r   c                ó:   € \        R WVR7      P                  4       # )Úpost©rZ   ©ró   rZ   ©r²   rõ   r)   s   &&&r   rß   rß   A  s   € Ü�F˜D°Ô5×:Ñ:Ó<Ð<r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r²   r8   rÃ   údict | Noner)   rì   r   rD   r   )r   s   "r   r   r   E  s/   € ÷ @ñ @ˆsð @˜Kð @¸ð @ÐRVñ @r   c                ó:   € \        R WVR7      P                  4       # )rB   ©rÃ   rù   ©r²   rÃ   r)   s   &&&r   rÈ   rÈ   E  s   € Ü�E˜4°Ô8×=Ñ=Ó?Ð?r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r²   r8   rÃ   rü   r)   rì   r   rî   r   )r   s   "r   r   r   I  s)   € ÷ 9ñ 9�3ð 9 ð 9¸]ð 9ÐVgñ 9r   c                ó   € \        RWVR7      # )z2GET and return the raw response (for binary data).rB   rþ   )ró   rÿ   s   &&&r   Ú_get_rawr  I  s   € ä�E˜4°Ô8Ð8r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r²   r8   rõ   rü   r)   rì   r   rD   r   )r   s   "r   r   r   N  s)   € ÷ ?ñ ?�#ð ?˜[ð ?¸-ð ?ÐSWñ ?r   c                ó:   € \        R WVR7      P                  4       # )Údeleterø   rù   rú   s   &&&r   Ú_deleter  N  s   € Ü�H˜d°$Ô7×<Ñ<Ó>Ð>r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r¾   rk   Úsuffixr8   r   r   )r   s   "r   r   r   S  s!   € ÷ 1ñ 1�~ð 1¨sð 1°sñ 1r   c                ó    € R V R,           RV 2# )z/tabs/rÀ   r;   r   )r¾   r  s   &&r   Ú	_tab_pathr
  S  s   € Ø�G˜HÕ%Ð& a¨ xÐ0Ð0r   c               ó    € V ^8„  d   QhRRRR/# )r   r¾   rk   r   r.   r   )r   s   "r   r   r   W  s   € ÷ *ñ *˜.ð *¨^ñ *r   c                ó   € R V R,          /# ©rÞ   r‚   r   ©r¾   s   &r   rÉ   rÉ   W  s   € Ø�g˜iÕ(Ð)Ð)r   c               ó    € V ^8„  d   QhRRRR/# )r   r¾   rk   r   rD   r   )r   s   "r   r   r   [  s   € ÷ Nñ N˜Nð N¨tñ Nr   c                óB   € \        \        V R 4      \        V 4      R7      # )Úsnapshotrþ   )rÈ   r
  rÉ   r  s   &r   Ú_snapshot_datar  [  s   € Ü”	˜' :Ó.´|ÀGÓ7LÔMÐMr   c               ó    € V ^8„  d   QhRRRR/# )r   r  r8   r   zlist[dict[str, str]]r   )r   s   "r   r   r   _  s   € ÷ ñ  Sð Ð-Añ r   c                ó"  € . pV P                  R4      p\        V4       Fì  w  r4VP                  4       pVP                  R4      '       g   K.  \        P
                  ! RV4      pV^,           \        V4      8  d3   \        P
                  ! RW#^,           ,          P                  4       4      MRpV'       d   VP                  ^4      MRV'       d   VP                  ^4      MRr˜V'       g   V	'       g   K×  VP                  RV	RV/4       Kî  	  V# )	z‘Images from an accessibility snapshot: ``img "alt" [eN]`` entries with the URL on
the following ``/url:`` line (Camofox has no /images endpoint).Ú
zimg\s+"([^"]*)"z/url:\s*(\S+)Nr1   ÚsrcÚalt)z- img zimg )	ÚsplitÚ	enumerater3   r°   ÚreÚsearchÚlenÚgroupÚappend)
r  ÚimagesÚlinesÚiÚlineÚstrippedÚ	alt_matchÚ	url_matchr  r  s
   &         r   Ú_parse_snapshot_imagesr&  _  sÔ   € ð €FØ�N‰N˜4Ó €EÜ˜UÖ#‰ˆØ—:‘:“<ˆØ×"Ñ"Ð#5×6Ò6ÙÜ—I’IÐ0°(Ó;ˆ	ØIJÈQÍÔQTÐUZÓQ[ÔI[”B—I’IÐ.°¸!µeµ×0BÑ0BÓ0DÔEÐaeˆ	ß*3�I—O‘O AÔ&¸×U^¸y¿¹ÈqÔ?QÐdfˆSß—#‘#Ø�M‰M˜5 # u¨cÐ2Ö3ñ $ð €Mr   c               ó    € V ^8„  d   QhRRRR/# )r   r¾   rk   r   ztuple[str, int]r   )r   s   "r   r   r   p  s   € ÷ 
.ñ 
.˜^ð 
.°ñ 
.r   c                ó¸   € ^ RI Hp ^ RIHp \	        V 4      pVP                  RR4      V! 4       rT\        V4      V8”  d
   V! WER7      pWCP                  R^ 4      3# )zÅ``(snapshot_text, refs_count)`` truncated like the main browser tool (line boundaries,
full tree stored to cache/web, read_file pointer appended). Lazy import: ``browser_tool``
imports this module.)Ú_truncate_snapshot)Úget_browser_snapshot_thresholdr  r1   )Ú	max_charsÚ	refsCount)Útools.browser_tool_snapshotr)  Útools.browser_toolr*  r  rB   r  )r¾   r)  r*  rà   r  Ú	thresholds   &     r   Ú_fetch_snapshotr0  p  sT   € õ ?ÝAÜ˜'Ó"€DØŸ(™( :¨rÓ2Ñ4RÓ4TˆiÜ
ˆ8ƒ}�yÔ Ù% hÔDˆØ—X‘X˜k¨1Ó-Ð-Ð-r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   Úbrowser_urlr8   r   ztuple[dict[str, Any], dict]r   )r   s   "r   r   r   }  s'   € ÷ Oñ O˜=ð O°sð OÐ?Zñ Or   c                ó‚  € \        V 4      pVR,          '       d(    \        \        VR4      RVR,          RV/^<R7      pW#3# \        W4      R
RRV/3#   \        P                   dW   pTP
                  e   TP
                  P                  R8w  d   h \        P                  R	TR,          4       RTR&    Rp?LwRp?ii ; i)uÍ   Open ``browser_url`` in the task's tab (creating it if missing) and return
``(session, navigate_response)``. A 404 on the existing tab means the server
garbage-collected it â€” recreate instead of failing.rÀ   ÚnavigaterÞ   r‚   rT   rb   Ni”  uP   Camofox tab %s returned 404 â€” tab was garbage collected. Creating a fresh tab.ÚokT)
rÛ   rß   r
  rc   Ú	HTTPErrorÚresponserd   r&   rm   rá   )r   r2  r¾   rà   Úes   &&   r   Ú_navigate_tabr9  }  s¾   € ô ˜7Ó#€GØˆx×Ôð	%Üœ 7¨JÓ7¸(ÀGÈIÕDVÐX]Ð_jÐ9kÐuwÔxˆDØ�=Ð ô �wÓ,¨t°T¸5À+Ð.NÐNÐNøô ×!Ñ!ô 	%Ø�z‰zÒ! Q§Z¡Z×%;Ñ%;¸sÔ%BØÜ�N‰NÐmØ" 8Õ,ô.à $ˆG�HÖûð	%ús   œ%A ÁB>Á(AB9Â9B>c               ó$   € V ^8„  d   QhRRRRRR/# )r   rT   r8   r   r   r   r   )r   s   "r   r   r   �  s!   € ÷ 1ñ 1˜#ð 1¨ð 1Àñ 1r   c           	     óº  €  \        V 4      w  r#\        W4      w  rERRRVP                  RV4      RVP                  RR4      /pV'       d$   WuVR&   VR&   RVR	,           R
VR,           2VR&   \        4       pV'       d
   WvR&   RVR&    \	        V4      w  VR&   VR&   \        P                  ! V4      #   \
         d     L#i ; i  \        P                   d   p\        RT 2RR7      u Rp?# Rp?i\        P                   d*    \        P                  ! RRRR\        4        R2/4      u # \
         d!   p\        \        T4      RR7      u Rp?# Rp?ii ; i)zNavigate to a URL via Camofox.ÚsuccessTrT   Útitler1   Úrequested_urlÚurl_rewritez0Rewrote loopback URL for Docker-hosted Camofox: r§   z -> r¨   rm   Úvnc_urlz]Browser is visible via VNC. Share this link with the user so they can watch the browser live.Úvnc_hintr  Úelement_countzNavigation failed: F©r<  NÚerrorzCannot connect to Camofox at z�. Is the server running? Start with: npm start (in camofox-browser dir) or: docker run -p 9377:9377 -e CAMOFOX_PORT=9377 jo-inc/camofox-browser)r»   r9  rB   ri   r0  r%   rZ   Údumpsrc   r6  r   ÚConnectionErrorr=   r8   )	rT   r   r2  Úrewrite_infor¾   rà   ÚresultÚvncr8  s	   &&       r   Úcamofox_navigaterJ  �  s{  € ð1Ü$EÀcÓ$JÑ!ˆÜ% gÓ;‰ˆØ˜T 5¨$¯(©(°5¸+Ó*FÈÐQU×QYÑQYÐZaÐceÓQfÐgˆßØ=@Ð:ˆF�?Ñ# V¨MÑ%:Ø!SØ$0°Õ$8Ð#9¸¸lÈ4Õ>PÐ=Qð"SˆF�9Ñä‹mˆßØ #�9Ñð#fˆF�:Ñð	Ü:IÈ'Ó:RÑ7ˆF�:Ñ  Ñ 7ô �zŠz˜&Ó!Ð!øô ô 	Ùð	ûô ×Ñô DÜÐ/°¨sÐ3¸UÔCÕCûÜ×#Ñ#ô YÜ�zŠz˜9 e¨WØ+¬OÓ,=Ð+>ð ?Vð VðXó Yò 	Yô ô 1Üœ#˜a›&¨%Ô0Õ0ûð1úsf   ‚BC ÂB5 ÂC Â5CÃ C ÃCÃC ÃEÃC0Ã*EÃ0EÄ'EÄ0EÄ9EÄ:EÅEÅEc               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r¾   rk   r   r   Úactionr8   r   r   )r   s   "r   r   r   ®  s0   € ÷ kñ k¨ð kÀ-ð kÐY\ð kÐanñ kr   c           
     óÎ   € ^ RI HpHp T! T;'       g    R4      '       g   R# V! V R,          V R,          4      pV'       g   R# \        P                  ! RRRR	V R
V R2/RR7      # )a™  Blocked payload when the current page is private/internal, else None.

Mirrors the ``_camofox_eval`` guard in browser_tool.py: page-state reads on a non-local
backend can leak an intranet/metadata page the terminal can't reach. Only active when
the SSRF guard applies (non-local backend, not a local sidecar, ``allow_private_urls``
unset); fail-open on probe failure like sibling guards. Lazy import (cycle).
)Ú!_camofox_current_page_private_urlÚ_eval_ssrf_guard_activer†   NrÀ   r‚   r<  FrD  z9Blocked: page URL targets a private or internal address (z). Refusing to z# on this page in this browser mode.)Úensure_ascii)Útools.browser_tool_eval_policyrN  rO  rZ   rE  )r¾   r   rL  rN  rO  Úblocked_urls   &&&   r   Ú_camofox_private_page_blockrS  ®  su   € ÷ jÙ" 7×#7Ð#7¨i×8Ò8ÙÙ3°G¸HÕ4EÀwÈyÕGYÓZ€KßÙÜ�:Š:�y %¨ð	Øˆ=˜¨ xÐ/Rð	TðVàdiôkð kr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   rL  r   z$tuple[dict[str, Any], Optional[str]]r   )r   s   "r   r   r   Á  s'   € ÷ lñ l˜-ð l°ð lÐJnñ lr   c                ó„   € \        V 4      pVR,          '       g   V\        \        RR7      3# Y!e   \        W V4      3# R3# )zkReturn ``(session, error_payload)``: error when no tab exists or, if ``action`` given, the page is private.rÀ   FrC  N)rÛ   r   Ú_NO_SESSION_ERRORrS  )r   rL  r¾   s   && r   Ú_require_tabrW  Á  sI   € ä˜7Ó#€GØ�8×ÔØœ
Ô#4¸eÔDÐDÐDØÒN`Ô0°À6ÓJÐkÐkÐfjÐkÐkr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r   r   Úguard_actionrõ   zCallable[[dict[str, Any]], str]r   r8   r   )r   s   "r   r   r   É  s*   € ÷ 	1ñ 	1�}ð 	1°Mð 	1ÐIhð 	1Ðmpñ 	1r   c                óž   €  \        W4      w  r4V'       d   V# V! V4      #   \         d!   p\        \        T4      RR7      u Rp?# Rp?ii ; i)z†Require a tab (+ private-page guard when ``guard_action`` is set), then run ``body(session)``;
any exception becomes a ``tool_error``.FrC  N)rW  r%   r   r8   )r   rY  rõ   r¾   Úblockedr8  s   &&&   r   Ú	_with_tabr\  É  sG   € ð1Ü'¨Ó>ÑˆßØˆNÙ�G‹}ÐøÜô 1Üœ#˜a›&¨%Ô0Õ0ûð1ús   ‚! ™! ¡A¬AÁAÁAc               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   r   r   rY  r  r8   rõ   rk   rH  zCallable[[dict], dict]r   r   )r   s   "r   r   r   Õ  sA   € ÷ \ñ \˜ð \°mð \ÈSð \Ø$ð\Ø.Dð\ØILñ\r   c                ó,   aaa€ \        WVVV3R l4      # )zNSimple tab action: POST ``body`` to ``/tabs/<id>/<suffix>``, build the result.c           
     óz   <€ \         P                  ! S! \        \        V S4      R V R,          /SC4      4      4      # r  )rZ   rE  rß   r
  )r¾   rõ   rH  r  s   &€€€r   Ú<lambda>Ú_tab_action.<locals>.<lambda>Ø  s5   ø€ ¼D¿JºJÙŒu”Y˜w¨Ó/°(¸GÀIÕ<NÐ1WÐRVÐ1WÓXÓYô=[r   ©r\  )r   rY  r  rõ   rH  s   &&fffr   Ú_tab_actionrc  Õ  s   ú€ ô �Wö -[ó \ð \r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   ÚfullrI   r   r   Ú	user_taskr   r8   r   )r   s   "r   r   r   Ü  s*   € ÷ <ñ <˜4ð <°-ð <ÐS`ð <Ðloñ <r   c                ó"   € R p\        VRV4      # )uŽ   Accessibility tree snapshot. ``user_task`` is deprecated and ignored â€”
oversized snapshots always truncate-and-store (no LLM summarization).c                óT   € \        V 4      w  r\        P                  ! R RRVRV/4      # )r<  Tr  rB  )r0  rZ   rE  )r¾   r  Ú
refs_counts   &  r   rõ   Úcamofox_snapshot.<locals>.bodyß  s,   € Ü.¨wÓ7ÑˆÜ�zŠz˜9 d¨J¸À/ÐS]Ð^Ó_Ð_r   zread a page snapshotrb  )re  r   rf  rõ   s   &&& r   Úcamofox_snapshotrk  Ü  s   € ò`ô �WÐ4°dÓ;Ð;r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úrefr8   r   r   r   r   )r   s   "r   r   r   å  s&   € ÷ iñ i�sð i ]ð i¸cñ ir   c                óP   a€ V P                  R4      o\        VRRRS/V3R l4      # )z$Click an element by ref via Camofox.r¤   Úclickrm  c           	     ó4   <€ R RRSRV P                  RR4      /# )r<  TÚclickedrT   r1   ©rB   )rà   Ú	clean_refs   &€r   r`  Úcamofox_click.<locals>.<lambda>é  s"   ø€  Y°°iÀÈEÐSW×S[ÑS[Ð\aÐceÓSfÑ$gr   )Úlstriprc  )rm  r   rs  s   &&@r   Úcamofox_clickrv  å  s/   ø€ à—
‘
˜3“€IÜ�w ¨°5¸)Ð2DÜgóið ir   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rm  r8   Útextr   r   r   r   )r   s   "r   r   r   ì  s.   € ÷ ^ñ ^�cð ^ ð ^¨}ð ^Èñ ^r   c           	     ó¢  €  \        VR4      w  r4V'       d   V# T P                  R4      p\        \        VR4      RVR,          RVRV/4       ^ RIHpHp V! RRV/4      ;'       g    / R,          pR	R
RVRV/p	\        P                  ! V! W‘4      4      #   \         d.   p
^ RIHp \        T! \        T
4      T4      RR7      u Rp
?
# Rp
?
ii ; i)z-Type text into an element by ref via Camofox.Útyper¤   rÞ   r‚   rm  rx  )Ú%redact_browser_typed_text_for_displayÚredact_tool_args_for_displayÚbrowser_typer<  TÚtypedÚelement)r{  FrC  N)rW  ru  rß   r
  Úagent.displayr{  r|  rZ   rE  r%   r   r8   )rm  rx  r   r¾   r[  rs  r{  r|  Údisplay_textr7  r8  s   &&&        r   Úcamofox_typer‚  ì  sÌ   € ð^Ü'¨°Ó8ÑˆßØˆNØ—J‘J˜s“Oˆ	ÜŒi˜ Ó(¨8°W¸YÕ5GÈÐPYÐ[aÐcgÐ*hÔißeñ 5°^ÀfÈdÀ^ÓT×ZÐZÐXZÐ\bÕcˆØ˜t W¨l¸IÀyÐQˆÜ�zŠzÑ?ÀÓOÓPÐPøÜô ^ÝGÜÑ?ÄÀAÃÈÓMÐW\Ô]Õ]ûð^ús)   ‚B šAB Á(-B ÂCÂ!"C	ÃCÃ	Cc               ó$   € V ^8„  d   QhRRRRRR/# )r   Ú	directionr8   r   r   r   r   )r   s   "r   r   r      s&   € ÷ Nñ N˜cð N¨Mð NÀSñ Nr   c                ó.   a € \        VRRRS /V 3R l4      # )zScroll the page via Camofox.NÚscrollr„  c                ó   <€ R RRS/# )r<  TÚscrolledr   )rà   r„  s   &€r   r`  Ú camofox_scroll.<locals>.<lambda>  s   ø€  Y°°jÀ)Ñ$Lr   ©rc  )r„  r   s   f&r   Úcamofox_scrollr‹     s"   ø€ ä�w  h°¸iÐ0HÜLóNð Nr   c               ó    € V ^8„  d   QhRRRR/# ©r   r   r   r   r8   r   )r   s   "r   r   r     s   € ÷ nñ n˜-ð n°3ñ nr   c                ó"   € \        V RR/ R 4      # )zNavigate back via Camofox.NÚbackc                ó.   € R RRV P                  RR4      /# )r<  TrT   r1   rr  )rà   s   &r   r`  Úcamofox_back.<locals>.<lambda>  s   € À	È4ÐQVÐX\×X`ÑX`ÐafÐhjÓXkÑ?lr   rŠ  rå   s   &r   Úcamofox_backr’    s   € ä�w  f¨bÑ2lÓmÐmr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r4   r8   r   r   r   r   )r   s   "r   r   r     s&   € ÷ pñ p�sð p ]ð p¸cñ pr   c                ó.   a € \        VRRRS /V 3R l4      # )z!Press a keyboard key via Camofox.Úpressr4   c                ó   <€ R RRS/# )r<  TÚpressedr   )rà   r4   s   &€r   r`  Úcamofox_press.<locals>.<lambda>  s   ø€ ÈiÐY]Ð_hÐjmÑMnr   rŠ  )r4   r   s   f&r   Úcamofox_pressr™    s   ø€ ä�w ¨°5¸#°,Ô@nÓoÐor   c               ó    € V ^8„  d   QhRRRR/# r�  r   )r   s   "r   r   r     s   € ÷ Pñ P˜=ð P°Cñ Pr   c                ó  €  \        V 4      pV'       d   \        RVR,           24       \        P                  ! RRRR/4      #   \         d0   p\        P                  ! RRRRR\        T4      /4      u Rp?# Rp?ii ; i)z&Close the browser session via Camofox.z
/sessions/r‚   r<  TÚclosedrm   N)ræ   r  rZ   rE  r%   r8   )r   r¾   r8  s   &  r   Úcamofox_closer�    st   € ðPÜ Ó(ˆßÜ�j ¨Õ!3Ð 4Ð5Ô6Ü�zŠz˜9 d¨H°dÐ;Ó<Ð<øÜô PÜ�zŠz˜9 d¨H°d¸IÄsÈ1ÃvÐNÓOÕOûðPús   ‚AA ÁA>Á$A9Á3A>Á9A>c               ó    € V ^8„  d   QhRRRR/# r�  r   )r   s   "r   r   r     s   € ÷ ;ñ ; ð ;¸ñ ;r   c                ó"   € R p\        V RV4      # )zFGet images on the current page via Camofox (parsed from the snapshot).c           
     ó”   € \        \        V 4      P                  R R4      4      p\        P                  ! RRRVR\        V4      /4      # )r  r1   r<  Tr  Úcount)r&  r  rB   rZ   rE  r  )r¾   r  s   & r   rõ   Ú camofox_get_images.<locals>.body  s?   € Ü'¬°wÓ(?×(CÑ(CÀJÐPRÓ(SÓTˆÜ�zŠz˜9 d¨H°f¸gÄsÈ6Ã{ÐSÓTÐTr   zextract page imagesrb  )r   rõ   s   & r   Úcamofox_get_imagesr£    s   € òUô �WÐ3°TÓ:Ð:r   c               ó   € V ^8„  d   QhRR/# )r   r   ztuple[float, float]r   )r   s   "r   r   r   #  s   € ÷ ñ Ð1ñ r   c                 óÄ   €  \        \        4       RR/ R7      p \        V P                  R^x4      4      \        V P                  RR4      4      3#   \         d    Ru # i ; i)zY``auxiliary.vision`` ``(timeout, temperature)``; defaults 120s / 0.1 on any config error.Ú	auxiliaryÚvision)r†   r)   Útemperatureçš™™™™™¹?)g      ^@r©  )r   r   ÚfloatrB   r%   )Úcfgs    r   Ú_vision_llm_settingsr¬  #  sY   € ðÜ”k“m [°(ÀBÔGˆÜ�S—W‘W˜Y¨Ó,Ó-¬u°S·W±W¸]ÈCÓ5PÓ/QÐQÐQøÜô ØÒðús   ‚AA ÁAÁAc               ó    € V ^8„  d   QhRRRR/# )r   ÚcontentÚbytesr   r8   r   )r   s   "r   r   r   ,  s   € ÷ ñ ˜eð ¨ñ r   c                óL  € ^ RI Hp V! 4       R,          pVP                  RRR7       \        VR\        P
                  ! 4       P                  R,           R2,          4      p\        VR4      ;_uu_ 4       pVP                  V 4       R	R	R	4       V#   + '       g   i     T# ; i)
zOWrite PNG bytes under ``$HERMES_HOME/browser_screenshots`` and return the path.)Úget_hermes_homeÚbrowser_screenshotsT)ÚparentsÚexist_okÚbrowser_screenshot_:Né   Nz.pngÚwbN)	Úhermes_constantsr±  Úmkdirr8   rÖ   r×   rØ   ÚopenÚwrite)r®  r±  Úscreenshots_dirÚscreenshot_pathÚfs   &    r   Ú_save_screenshotr¿  ,  sˆ   € å0Ù%Ó'Ð*?Õ?€OØ×Ñ $°ÐÔ6Ü˜/Ð.AÄ$Ç*Â*Ã,×BRÑBRÐSUÕBVÐAWÐW[Ð,\Õ\Ó]€OÜ	ˆo˜t×	$Ô	$¨Ø	�‰�Ô÷ 
%àÐ÷ 
%Ö	$àÐús   Á6BÂB#	c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   Úquestionr8   ÚannotaterI   r   r   r   r   )r   s   "r   r   r   7  s)   € ÷ <ñ <˜Sð <¨Dð <À=ð <Ð\_ñ <r   c                ó.   a a€ VV 3R lp\        VRV4      # )z<Take a screenshot and analyze it with vision AI via Camofox.c                óÄ  <€ \        \        V R 4      \        V 4      R7      p\        VP                  4      p\
        P                  ! VP                  4      P                  R4      pRpS'       d)    \        V 4      P                  RR4      pRVR,           2p^ RIHp ^ RIHp R	S V! V4       2p\        4       w  ršV! R
RRRRRV/RRRRRV 2//./.RW©R7      pVP                   '       d@   VP                   ^ ,          P"                  P                  ;'       g    RP%                  4       MRp\&        P(                  ! RRRV! V4      RV/4      #   \         d     LÆi ; i)Ú
screenshotrþ   zutf-8r1   r  z5

Accessibility tree (element refs for interaction):
:Ni¸  N)Úredact_sensitive_text)Úcall_llmz,Analyze this browser screenshot and answer: ÚroleÚuserr®  rz  rx  Ú	image_urlrT   zdata:image/png;base64,r§  )ÚmessagesÚtaskr¨  r)   r<  TÚanalysisr½  )r  r
  rÉ   r¿  r®  Úbase64Ú	b64encodeÚdecoder  rB   r%   Úagent.redactrÆ  Úagent.auxiliary_clientrÇ  r¬  ÚchoicesÚmessager3   rZ   rE  )r¾   rU   r½  Úimg_b64Úannotation_contextr  rÆ  rÇ  Úvision_promptr)   r¨  r7  rÍ  rÂ  rÁ  s   &            €€r   rõ   Úcamofox_vision.<locals>.body9  sv  ø€ Üœ	 '¨<Ó8ÄÈgÓAVÔWˆÜ*¨4¯<©<Ó8ˆÜ×"Ò" 4§<¡<Ó0×7Ñ7¸Ó@ˆØÐßðÜ)¨'Ó2×6Ñ6°zÀ2ÓF�Ø'_Ð`hÐinÕ`oÐ_pÐ%qÐ"õ
 	7Ý3ØFÀxÀjÑQfÐgyÓQzÐP{Ð|ˆÜ3Ó5ÑˆÙØ˜v yØ˜ ¨Ð7Ø˜ k°EÐ=SÐT[ÐS\Ð;]Ð3^Ð_ð3að bð cð  {ô	Eˆð
 KS×JZ×JZÐJZ�H×$Ñ$ QÕ'×/Ñ/×7Ñ7×=Ð=¸2×DÑDÔFÐ`bˆä�zŠz˜9 d¨JÑ8MÈhÓ8WÐYjÐl{Ð|Ó}Ð}øô ô Ùðús   Á1'E ÅEÅEzcapture a screenshotrb  )rÁ  rÂ  r   rõ   s   ff& r   Úcamofox_visionrÙ  7  s   ù€ ö~ô2 �WÐ4°dÓ;Ð;r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   ÚclearrI   r   r   r   r8   r   )r   s   "r   r   r   U  s&   € ÷ Rñ R˜4ð R°-ð RÈ3ñ Rr   c                óF   € \         P                  ! RRR. R. R^ R^ RR/4      # )	zZConsole output is not exposed by the Camofox REST API; return an empty result with a note.r<  TÚconsole_messagesÚ	js_errorsÚtotal_messagesÚtotal_errorsÚnotez|Console log capture is not available with the Camofox backend. Use browser_snapshot or browser_vision to inspect page state.)rZ   rE  )rÛ  r   s   &&r   Úcamofox_consolerâ  U  s>   € ä�:Š:Ø�4Ð+¨R°¸bÐBRÐTUÐWeÐghØð PðQó Rð Rr   )N)zabout:blank)NN)FNN)FN)aÚ__conditional_annotations__Ú__doc__Ú
__future__r   rÎ  r™   rZ   Úloggingrz   r  Ú	threadingrÖ   Útypingr   r   r   r   Úurllib.parser   r   r	   rc   Úagent.secret_scoper
   rA   r   r   r   r¸  r   r   Útools.browser_camofox_stater   Útools.registryr   Ú	getLoggerÚ__name__r&   r#   rV  r   Ú__annotations__re   r   r   r"   r,   r5   r=   rF   rR   r_   rf   ri   ro   rt   r}   r‰   r•   r�   r»   r¼   ÚLockrÕ   rÎ   rÛ   rá   ræ   ré   ró   rß   rÈ   r  r  r
  rÉ   r  r&  r0  r9  rJ  rS  rW  r\  rc  rk  rv  r‚  r‹  r’  r™  r�  r£  r¬  r¿  rÙ  râ  )rã  s   @r   Ú<module>rñ     sÃ  øðôõ #ã Û Û Û Û 	Û 	Û Û ß 0Ó 0ß :Ñ :ã å )ß CÑ Cß FÝ <Ý %à	×	Ò	˜8Ó	$€ð Ð ØFÐ Ø€ˆ-Ó ØÐ ð 57Ð Ð1Ó 6ð 15Ð Ð-Ó 4ØÐ õõ(=õ=õ

õ#õ*õ#õ(	õ@÷lð
NÐ\a÷ Nõeõ	*õ	õtð8 (*€	Ð$Ó )Ø—’Ó!€õõ&,÷.õ9÷	÷÷=÷@÷9÷
?õ
1õ*õNõõ"
.õO÷$1õ>k÷&lõ	1õ\÷<÷i÷^÷(N÷n÷
p÷
P÷;õõ÷<÷<Rñ Rr   