§
    øžyj/Š  ã                  óz  — U d Z ddlm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mZ ddlmZmZmZ ddlZddlmZ ddlmZmZmZ ddlmZ dd	lmZ  ej        e¦  «        Zd
ZdZ da!de"d<   da#da$de"d<   da%dYd„Z&dZd„Z'd[d„Z(d[d„Z)d\d„Z*d\d„Z+d]d„Z,d^d„Z-d\d„Z.d_d#„Z/d`d&„Z0dad'„Z1dad(„Z2dbd)„Z3dcd+„Z4ddd.„Z5i Z6d/e"d0<    ej7        ¦   «         Z8ded2„Z9dfd3„Z:dgdhd5„Z;did7„Z<djdkd8„Z=djdld=„Z>dmdnd?„Z?dmdodA„Z@dmdldB„ZAdjdpdC„ZBdqdE„ZC	 	 drdsdH„ZDdjdtdJ„ZEdjdudL„ZFdjdvdN„ZGdjdwdO„ZHdjdxdQ„ZIdjdwdR„ZJdjdwdS„ZK	 	 dydzdV„ZLdyd{dX„ZMdS )|u   Camofox browser backend â€” local anti-detection browser via REST API.

Camofox-browser is a self-hosted Node.js server wrapping Camoufox (Firefox
fork with C++ fingerprint spoofing).  It exposes a REST API that maps 1:1
to our browser tool interface: accessibility snapshots with element refs,
click/type/scroll by ref, screenshots, etc.

When ``CAMOFOX_URL`` is set (e.g. ``http://localhost:9377``), the browser
tools route through this module instead of the ``agent-browser`` CLI.

Setup::

    # Option 1: npm
    git clone https://github.com/jo-inc/camofox-browser && cd camofox-browser
    npm install && npm start   # downloads Camoufox (~300MB) on first run

    # Option 2: Docker
    docker run -p 9377:9377 -e CAMOFOX_PORT=9377 jo-inc/camofox-browser

Then set ``CAMOFOX_URL=http://localhost:9377`` in ``~/.hermes/.env``.
For Docker Camofox, optionally set ``CAMOFOX_REWRITE_LOOPBACK_URLS=true``
so page URLs like ``http://127.0.0.1:3000`` are opened inside the
container as ``http://host.docker.internal:3000``.
é    )ÚannotationsN)ÚAnyÚDictÚOptional)ÚSplitResultÚurlsplitÚ
urlunsplit)Ú
get_secret)Úcfg_getÚload_configÚread_raw_config)Úget_camofox_identity)Ú
tool_erroré   i€8 úOptional[str]Ú_vnc_urlFúOptional[int]Ú_cached_cmd_timeoutÚreturnÚintc                 ó  — t           rt          S da t          } 	 t          ¦   «         }t	          |dd¦  «        }|�t          t          |¦  «        d¦  «        } n2# t          $ r%}t           	                    d|¦  «         Y d}~nd}~ww xY w| a| S )a  Return ``browser.command_timeout`` from config, falling back to 30s.

    Mirrors :func:`tools.browser_tool._get_command_timeout` so both the
    local browser path and the Camofox path honour the same config knob.
    Result is cached after the first call.
    TÚbrowserÚcommand_timeoutNé   z*Could not read browser.command_timeout: %s)
Ú_cmd_timeout_resolvedr   Ú_DEFAULT_TIMEOUTr   r   Úmaxr   Ú	ExceptionÚloggerÚdebug)ÚresultÚcfgÚvalÚexcs       ú;/home/ragecks/.hermes/hermes-agent/tools/browser_camofox.pyÚ_get_command_timeoutr&   <   s­   € õ ð #Ý"Ð"à ÐÝ€FðHÝÑÔˆÝ�c˜9Ð&7Ñ8Ô8ˆØˆ?Ý�˜S™œ 1Ñ%Ô%ˆFøøÝð Hð Hð HÝ�ŠÐAÀ3ÑGÔGÐGÐGÐGÐGÐGÐGøøøøðHøøøà ÐØ€Ms   ™>A Á
BÁ"BÂBúDict[str, str]c                 ó`   — t          dd¦  «        pd                     ¦   «         } | rdd| › �iS i S )z8Return Authorization header when CAMOFOX_API_KEY is set.ÚCAMOFOX_API_KEYÚ ÚAuthorizationzBearer )r
   Ústrip)Úkeys    r%   Ú_auth_headersr.   T   sA   € åÐ'¨Ñ,Ô,Ð2°×
9Ò
9Ñ
;Ô
;€CØ
ð 2Ø ¨3  Ð1Ð1Ø€Ió    Ústrc                 óL   — t          dd¦  «        pd                     d¦  «        S )z:Return the configured Camofox server URL, or empty string.ÚCAMOFOX_URLr*   ú/)r
   Úrstrip© r/   r%   Úget_camofox_urlr6   \   s%   € å�} bÑ)Ô)Ð/¨R×7Ò7¸Ñ<Ô<Ð<r/   c                 ó
  — 	 ddl m}   | ¦   «                              di ¦  «        }t          |t          ¦  «        r7t          |                     dd¦  «        pd¦  «                             ¦   «         S n# t          $ r Y nw xY wdS )a.  Persistent ``browser.cdp_url`` from config.yaml, or empty string.

    Read here (instead of importing ``browser_tool._get_cdp_override`` to avoid
    a circular import) so Camofox can yield to a config-based CDP override the
    same way it already yields to the ``BROWSER_CDP_URL`` env override.
    r   )r   r   Úcdp_urlr*   )Úhermes_cli.configr   ÚgetÚ
isinstanceÚdictr0   r,   r   )r   Úbrowser_cfgs     r%   Ú_config_cdp_urlr>   a   s¡   € ðØ5Ð5Ð5Ð5Ð5Ð5à%�oÑ'Ô'×+Ò+¨I°rÑ:Ô:ˆÝ�k¥4Ñ(Ô(ð 	EÝ�{—’ y°"Ñ5Ô5Ð;¸Ñ<Ô<×BÒBÑDÔDÐDð	Eøåð ð ð Øˆðøøøàˆ2s   ‚A/A3 Á3
B Á?B Úboolc                 óª   — t          j        dd¦  «                             ¦   «         rdS t          ¦   «         rdS t	          t          ¦   «         ¦  «        S )u™  True when Camofox backend is configured and no CDP override is active.

    A CDP override takes priority over Camofox so the browser tools operate on
    the real CDP browser (and a CDP backend is treated as non-local for SSRF
    checks) instead of being silently routed to Camofox. The override may come
    from the ``BROWSER_CDP_URL`` env var (set by ``/browser connect``) OR a
    persistent ``browser.cdp_url`` in config.yaml â€” both are honored, matching
    ``browser_tool._get_cdp_override()``'s precedence. (Previously only the env
    var suppressed Camofox, so ``CAMOFOX_URL`` + a config CDP override still
    routed navigation through Camofox.)
    ÚBROWSER_CDP_URLr*   F)ÚosÚgetenvr,   r>   r?   r6   r5   r/   r%   Úis_camofox_moderD   s   sQ   € õ 
„yÐ" BÑ'Ô'×-Ò-Ñ/Ô/ð ØˆuÝÑÔð ØˆuÝ•Ñ!Ô!Ñ"Ô"Ð"r/   c                 óÌ  — t          ¦   «         } | sdS 	 t          j        | › d�d¬¦  «        }|j        dk    r’t          s‹	 |                     ¦   «         }|                     d¦  «        }t          |t          ¦  «        r2d|cxk    rdk    r%n n"d	d
lm	}  || ¦  «        }|j
        pd}d|› d|› �an# t          t          f$ r Y nw xY wda|j        dk    S # t          $ r Y dS w xY w)z'Verify the Camofox server is reachable.Fz/healthr   ©ÚtimeoutéÈ   ÚvncPorté   iÿÿ  r   )ÚurlparseÚ	localhostzhttp://ú:T)r6   Úrequestsr:   Ústatus_codeÚ_vnc_url_checkedÚjsonr;   r   Úurllib.parserK   Úhostnamer   Ú
ValueErrorÚKeyErrorr   )ÚurlÚrespÚdataÚvnc_portrK   ÚparsedÚhosts          r%   Úcheck_camofox_availabler\   †   sA  € õ Ñ
Ô
€CØð ØˆuðÝŒ|˜s˜O˜O˜O°QÐ7Ñ7Ô7ˆØÔ˜sÒ"Ð"Õ+;Ð"ð	Ø—y’y‘{”{�ØŸ8š8 IÑ.Ô.�Ý˜h­Ñ,Ô,ð ;°°hÐ1GÐ1GÒ1GÐ1GÀ%Ò1GÐ1GÐ1GÐ1GÐ1GØ5Ð5Ð5Ð5Ð5Ð5Ø%˜X c™]œ]�FØ!œ?Ð9¨k�DØ:¨Ð:Ð:°Ð:Ð:�HøøÝ¥Ð)ð ð ð Ø�ðøøøà#ÐØÔ 3Ò&Ð&øÝð ð ð Øˆuˆuðøøøs6   ”+C Á A0B1 Â0C Â1CÃC ÃCÃC Ã
C#Ã"C#c                 ó:   — t           st          ¦   «          t          S )z>Return the VNC URL if the Camofox server exposes one, or None.)rP   r\   r   r5   r/   r%   Úget_vnc_urlr^   Ÿ   s   € åð "ÝÑ!Ô!Ð!Ý€Or/   úDict[str, Any]c                 ó  — 	 t          ¦   «                              di ¦  «                             di ¦  «        } n4# t          $ r'}t                               d|¦  «         i cY d}~S d}~ww xY wt          | t          ¦  «        r| ni S )z>Return the ``browser.camofox`` config block, or an empty dict.r   Úcamofoxz7camofox config check failed, defaulting to disabled: %sN)r   r:   r   r   Úwarningr;   r<   )Úcamofox_cfgr$   s     r%   Ú_get_camofox_configrd   ¦   s‘   € ðÝ!‘m”m×'Ò'¨	°2Ñ6Ô6×:Ò:¸9ÀbÑIÔIˆˆøÝð ð ð Ý�ŠÐPÐRUÑVÔVÐVØˆ	ˆ	ˆ	ˆ	ˆ	ˆ	øøøøðøøøõ % [µ$Ñ7Ô7Ð?ˆ;ˆ;¸RÐ?s   ‚69 ¹
A*ÁA%ÁA*Á%A*c                 ó^   — t          t          ¦   «                              d¦  «        ¦  «        S )al  Return whether Hermes-managed persistence is enabled for Camofox.

    When enabled, sessions use a stable profile-scoped userId so the
    Camofox server can map it to a persistent browser profile directory.
    When disabled (default), each session gets a random userId (ephemeral).

    Controlled by ``browser.camofox.managed_persistence`` in config.yaml.
    Úmanaged_persistence)r?   rd   r:   r5   r/   r%   Ú_managed_persistence_enabledrg   °   s(   € õ Õ#Ñ%Ô%×)Ò)Ð*?Ñ@Ô@ÑAÔAÐAr/   Útask_idrc   úOptional[Dict[str, str]]c                óš  — t          dd¦  «        pd                     ¦   «         p5t          |                     d¦  «        pd¦  «                             ¦   «         }|sdS t          dd¦  «        pd                     ¦   «         pDt          |                     d¦  «        pd¦  «                             ¦   «         pd| pddd	…         › �}||d
œS )züReturn an externally configured Camofox identity, if one is set.

    Integrations that own the visible Camofox browser can set a shared user ID
    so Hermes operates in the same browser profile instead of creating a
    separate private session.
    ÚCAMOFOX_USER_IDr*   Úuser_idNÚCAMOFOX_SESSION_KEYÚsession_keyÚtask_Údefaulté   )rl   rn   )r
   r,   r0   r:   )rh   rc   rl   rn   s       r%   Ú_camofox_identity_overriderr   ¼   så   € õ 
Ð% rÑ	*Ô	*Ð	0¨b×7Ò7Ñ9Ô9ð 	9Ýˆ{�Š˜yÑ)Ô)Ð/¨RÑ0Ô0×6Ò6Ñ8Ô8ð ð ð Øˆtõ 
Ð)¨2Ñ	.Ô	.Ð	4°"×;Ò;Ñ=Ô=ð 	1Ýˆ{�Š˜}Ñ-Ô-Ð3°Ñ4Ô4×:Ò:Ñ<Ô<ð	1à0�GÐ(˜y¨#¨2¨#Ô.Ð0Ð0ð ð
 ¨{Ð;Ð;Ð;r/   ÚnameúOptional[bool]c                óÐ   — t          j        | d¦  «                             ¦   «                              ¦   «         }|sd S |dv rdS |dv rdS t                               d| |¦  «         d S )Nr*   >   Ú1ÚonÚyesÚtrueT>   Ú0ÚnoÚoffÚfalseFz"Ignoring invalid boolean env %s=%r)rB   rC   r,   Úlowerr   r    )rs   Úraws     r%   Ú	_env_flagr€   Ò   sv   € Ý
Œ)�D˜"Ñ
Ô
×
#Ò
#Ñ
%Ô
%×
+Ò
+Ñ
-Ô
-€CØð ØˆtØ
Ð(Ð(Ð(ØˆtØ
Ð)Ð)Ð)ØˆuÝ
‡L‚LÐ5°t¸SÑAÔAÐAØˆ4r/   c                ól   — t          d¦  «        }|�|S t          |                      d¦  «        ¦  «        S )z@Return whether Hermes should recover an existing Camofox tab ID.ÚCAMOFOX_ADOPT_EXISTING_TABNÚadopt_existing_tab©r€   r?   r:   ©rc   Ú	env_values     r%   Ú_adopt_existing_tab_enabledr‡   Þ   s7   € åÐ6Ñ7Ô7€IØÐØÐÝ�—’Ð 4Ñ5Ô5Ñ6Ô6Ð6r/   c                ól   — t          d¦  «        }|�|S t          |                      d¦  «        ¦  «        S )aL  Return whether loopback navigation URLs should be rewritten for Docker.

    ``CAMOFOX_URL`` itself often points at a host-published Docker port such as
    ``http://127.0.0.1:9377``.  That is correct for Hermes talking to the
    Camofox control API, but a page URL like ``http://127.0.0.1:3000`` is opened
    by the browser *inside* the Docker container.  In that context loopback
    points at the container, not the host running the web app.

    The rewrite is opt-in because non-Docker Camofox installs run the browser on
    the host, where loopback URLs are already correct.
    ÚCAMOFOX_REWRITE_LOOPBACK_URLSNÚrewrite_loopback_urlsr„   r…   s     r%   Ú_loopback_rewrite_enabledr‹   æ   s9   € õ Ð9Ñ:Ô:€IØÐØÐÝ�—’Ð 7Ñ8Ô8Ñ9Ô9Ð9r/   c                óÀ   — t          j        dd¦  «                             ¦   «         p7t          |                      d¦  «        pd¦  «                             ¦   «         pdS )z=Return the host alias used when rewriting loopback page URLs.ÚCAMOFOX_LOOPBACK_HOST_ALIASr*   Úloopback_host_aliaszhost.docker.internal)rB   rC   r,   r0   r:   )rc   s    r%   Ú_loopback_rewrite_hostr�   ø   s[   € õ 	Œ	Ð/°Ñ4Ô4×:Ò:Ñ<Ô<ð 	"Ýˆ{�ŠÐ4Ñ5Ô5Ð;¸Ñ<Ô<×BÒBÑDÔDð	"à!ðr/   rS   c                óè   — | sdS |                       ¦   «                               d¦  «                             ¦   «         }|dv rdS 	 ddl}|                     |¦  «        j        S # t
          $ r Y dS w xY w)z:Return True for localhost/127.0.0.0/8/::1-style hostnames.Fz[]>   úlocalhost.localdomainrL   Tr   N)r,   r~   Ú	ipaddressÚ
ip_addressÚis_loopbackrT   )rS   r[   r’   s      r%   Ú_is_loopback_hostnamer•     s“   € àð ØˆuØ�>Š>ÑÔ×!Ò! $Ñ'Ô'×-Ò-Ñ/Ô/€DØÐ5Ð5Ð5ØˆtðØÐÐÐà×#Ò# DÑ)Ô)Ô5Ð5øÝð ð ð Øˆuˆuðøøøs   ÁA# Á#
A1Á0A1rV   ú$tuple[str, Optional[Dict[str, str]]]c           	     óH  — t          ¦   «         }t          |¦  «        s| dfS 	 t          | ¦  «        }n# t          $ r | dfcY S w xY w|j        dvst          |j        ¦  «        s| dfS t          |¦  «        }|s| dfS d}|j        r |j        }|j	        r|d|j	        › �z  }|dz  }d|v r| 
                    d¦  «        sd|› d�n|}|j        r
d|j        › �nd}t          t          |j        |› |› |› �|j        |j        |j        ¦  «        ¦  «        }||j        pd|| |dœfS )	zèRewrite loopback page URLs for Docker-hosted Camofox, if configured.

    Returns ``(rewritten_url, metadata)``.  ``metadata`` is present only when a
    rewrite happened so the tool result can disclose the change to the model.
    N>   ÚhttpÚhttpsr*   rM   ú@Ú[Ú])ÚfromÚtoÚoriginal_urlÚrewritten_url)rd   r‹   r   rT   Úschemer•   rS   r�   ÚusernameÚpasswordÚ
startswithÚportr	   r   ÚpathÚqueryÚfragment)rV   rc   rZ   ÚaliasÚuserinfoÚ	host_partÚ	port_partÚ	rewrittens           r%   Ú!_rewrite_loopback_url_for_camofoxr®     sŸ  € õ &Ñ'Ô'€KÝ$ [Ñ1Ô1ð Ø�DˆyÐðÝ˜#‘”ˆˆøÝð ð ð Ø�DˆyÐÐÐðøøøð „}Ð-Ð-Ð-Õ5JÈ6Ì?Ñ5[Ô5[Ð-Ø�DˆyÐå" ;Ñ/Ô/€EØð Ø�DˆyÐà€HØ„ð Ø”?ˆØŒ?ð 	.ØÐ-˜FœOÐ-Ð-Ñ-ˆHØ�C‰ˆØ # u  °U×5EÒ5EÀcÑ5JÔ5J ��E����ÐPU€IØ%+¤[Ð8Ð!�F”KÐ!Ð!Ð!°b€IÝÝ�F”M hÐ#F°	Ð#F¸9Ð#FÐ#FÈÌÐU[ÔUaÐciÔcrÑsÔsñô €Ið Ø”Ð% 2ØØØ"ð	ð ð ð s   £3 ³AÁAzDict[str, Dict[str, Any]]Ú	_sessionsÚsessionc                ó
  ‡— |                       d¦  «        s|                       d¦  «        s| S t          ¦   «         s| S 	 t          dd| d         id¬¦  «                              dg ¦  «        }nH# t          $ r;}t                               d	|                       d¦  «        |¦  «         | cY d
}~S d
}~ww xY wt          |t          ¦  «        r|s| S |                       d¦  «        Šˆfd„|D ¦   «         }|pd„ |D ¦   «         }|r|d         nd
}t          |t          ¦  «        r|                      d¦  «        nd
}t          |t          ¦  «        r6|r4|| d<   t                               d||                       d¦  «        ¦  «         | S )a0  Attach process-local state to an already-open managed Camofox tab.

    Some integrations own the visible Camofox tab outside Hermes. Gateway
    restarts can leave this module's in-memory session cache empty even though
    Camofox still has that tab, so rehydrate tab_id before creating a new tab.
    Útab_idrƒ   ú/tabsÚuserIdrl   r   )ÚparamsrG   Útabsz&Camofox tab adoption failed for %s: %sNrn   c                óp   •— g | ]2}t          |t          ¦  «        r|                     d ¦  «        ‰k    ¯0|‘Œ3S )Ú
listItemId)r;   r<   r:   )Ú.0Útabrn   s     €r%   ú
<listcomp>z'_adopt_existing_tab.<locals>.<listcomp>X  sN   ø€ ð ð ð àÝ�c�4Ñ Ô ðð &)§W¢W¨\Ñ%:Ô%:¸kÒ%IÐ%Ið 	à%IÐ%IÐ%Ir/   c                ó<   — g | ]}t          |t          ¦  «        ¯|‘ŒS r5   )r;   r<   )r¹   rº   s     r%   r»   z'_adopt_existing_tab.<locals>.<listcomp>]  s'   € Ð"PÐ"PÐ"P¨3½*ÀSÍ$Ñ:OÔ:OÐ"P 3Ð"PÐ"PÐ"Pr/   éÿÿÿÿÚtabIdz&Adopted existing Camofox tab %s for %s)
r:   r6   Ú_getr   r   r    r;   Úlistr<   r0   )r°   r¶   r$   Úmatching_tabsÚ
candidatesÚlatestr²   rn   s          @r%   Ú_adopt_existing_tabrÄ   A  sÇ  ø€ ð ‡{‚{�8ÑÔð  G§K¢KÐ0DÑ$EÔ$Eð ØˆåÑÔð ØˆðÝ�G X¨w°yÔ/AÐ$BÈAÐNÑNÔN×RÒRÐSYÐ[]Ñ^Ô^ˆˆøÝð ð ð Ý�ŠÐ=¸w¿{º{È9Ñ?UÔ?UÐWZÑ[Ô[Ð[Øˆˆˆˆˆˆøøøøðøøøõ �d�DÑ!Ô!ð ¨ð Øˆà—+’+˜mÑ,Ô,€Kðð ð ð àðñ ô €Mð
 ÐPÐ"PÐ"P°$Ð"PÑ"PÔ"P€JØ)Ð3ˆZ˜Œ^ˆ^¨t€FÝ$.¨vµtÑ$<Ô$<ÐFˆV�ZŠZ˜Ñ Ô Ð À$€FÝ�&�#ÑÔð _ 6ð _Ø"ˆ�ÑÝ�ŠÐ=¸vÀwÇ{Â{ÐS\ÑG]ÔG]Ñ^Ô^Ð^à€Ns   ¿.A. Á.
B3Á80B.Â(B3Â.B3c                ón  — | pd} t           5  | t          v r&t          t          |          ¦  «        cddd¦  «         S t          ¦   «         }t	          | |¦  «        }|r"|d         d|d         dt          |¦  «        dœ}n‡t          |                     d¦  «        ¦  «        r1t          | ¦  «        }|d         d|d         dt          |¦  «        dœ}n4dt          j
        ¦   «         j        dd	…         › �dd
| dd…         › �dddœ}|t          | <   t          |¦  «        cddd¦  «         S # 1 swxY w Y   dS )zÿGet or create a camofox session for the given task.

    When managed persistence is enabled, uses a deterministic userId
    derived from the Hermes profile so the Camofox server can map it
    to the same persistent browser profile across restarts.
    rp   Nrl   rn   T)rl   r²   rn   Úmanagedrƒ   rf   Úhermes_é
   ro   rq   F)Ú_sessions_lockr¯   rÄ   rd   rr   r‡   r?   r:   r   ÚuuidÚuuid4Úhex)rh   rc   Úidentity_overrider°   Úidentitys        r%   Ú_get_sessionrÏ   g  sÉ  € ð Ð"˜€GÝ	ð  ,ð  ,Ø•iÐÐÝ&¥y°Ô'9Ñ:Ô:ð ,ð  ,ð  ,ð  ,ñ  ,ô  ,ð  ,ð  ,õ *Ñ+Ô+ˆÝ6°wÀÑLÔLÐØð 	à,¨YÔ7ØØ0°Ô?ØÝ&AÀ+Ñ&NÔ&Nðð ˆGˆGõ �+—/’/Ð"7Ñ8Ô8Ñ9Ô9ð 	Ý+¨GÑ4Ô4ˆHà# IÔ.ØØ'¨Ô6ØÝ&AÀ+Ñ&NÔ&Nðð ˆGˆGð =¥T¤Z¡\¤\Ô%5°c°r°cÔ%:Ð<Ð<ØØ5 w¨s°¨s¤|Ð5Ð5Ø Ø&+ðð ˆGð %�	�'ÑÝ" 7Ñ+Ô+ðA ,ð  ,ð  ,ð  ,ñ  ,ô  ,ð  ,ð  ,ð  ,ð  ,ð  ,ð  ,øøøð  ,ð  ,ð  ,ð  ,ð  ,ð  ,s   Œ#D*¼C!D*Ä*D.Ä1D.úabout:blankc                óZ  — t          | ¦  «        }|d         r|S t          ¦   «         }t          j        |› d�|d         |d         |dœt	          ¦   «         t          ¦   «         ¬¦  «        }|                     ¦   «          |                     ¦   «         }|                     d¦  «        |d<   |S )z<Ensure a tab exists for the session, creating one if needed.r²   r³   rl   rn   )r´   r¸   rV   ©rQ   rG   Úheadersr¾   )	rÏ   r6   rN   Úpostr&   r.   Úraise_for_statusrQ   r:   )rh   rV   r°   ÚbaserW   rX   s         r%   Ú_ensure_tabr×   ’  s¶   € å˜7Ñ#Ô#€GØˆxÔð ØˆÝÑÔ€DÝŒ=Øˆˆˆà˜iÔ(Ø! -Ô0Øð
ð 
õ
 %Ñ&Ô&Ý‘”ð	ñ 	ô 	€Dð 	×ÒÑÔÐØ�9Š9‰;Œ;€DØŸš Ñ)Ô)€GˆHÑØ€Nr/   úOptional[Dict[str, Any]]c                ó‚   — | pd} t           5  t                               | d¦  «        cddd¦  «         S # 1 swxY w Y   dS )zRemove and return session info.rp   N)rÉ   r¯   Úpop)rh   s    r%   Ú_drop_sessionrÛ   ¨  s‡   € àÐ"˜€GÝ	ð ,ð ,Ý�}Š}˜W dÑ+Ô+ð,ð ,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,øøøð ,ð ,ð ,ð ,ð ,ð ,s   Œ4´8»8c                óÞ   — t          ¦   «         }t          |                     d¦  «        ¦  «        st          | |¦  «        r,t	          | ¦  «         t
                               d| ¦  «         dS dS )aœ  Release the in-memory session without destroying the server-side context.

    When managed persistence is enabled the browser profile (and its cookies)
    must survive across agent tasks.  This helper drops only the local tracking
    entry and returns ``True``.  When managed persistence is *not* enabled it
    does nothing and returns ``False`` so the caller can fall back to
    :func:`camofox_close`.
    rf   z6Camofox soft cleanup for task %s (managed persistence)TF)rd   r?   r:   rr   rÛ   r   r    )rh   rc   s     r%   Úcamofox_soft_cleanuprÝ   ¯  sm   € õ &Ñ'Ô'€KÝˆK�OŠOÐ1Ñ2Ô2Ñ3Ô3ð Õ7QÐRYÐ[fÑ7gÔ7gð Ý�gÑÔÐÝ�ŠÐMÈwÑWÔWÐWØˆtØˆ5r/   r¦   Úbodyr<   rG   c                óÞ   — |€t          ¦   «         }t          ¦   «         › | › �}t          j        |||t	          ¦   «         ¬¦  «        }|                     ¦   «          |                     ¦   «         S )z0POST JSON to camofox and return parsed response.NrÒ   )r&   r6   rN   rÔ   r.   rÕ   rQ   ©r¦   rÞ   rG   rV   rW   s        r%   Ú_postrá   Ä  sd   € à€Ý&Ñ(Ô(ˆÝÑÔÐ
& Ð
&Ð
&€CÝŒ=˜ 4°Å-Á/Ä/ÐRÑRÔR€DØ×ÒÑÔÐØ�9Š9‰;Œ;Ðr/   rµ   c                óÞ   — |€t          ¦   «         }t          ¦   «         › | › �}t          j        |||t	          ¦   «         ¬¦  «        }|                     ¦   «          |                     ¦   «         S )z,GET from camofox and return parsed response.N©rµ   rG   rÓ   )r&   r6   rN   r:   r.   rÕ   rQ   ©r¦   rµ   rG   rV   rW   s        r%   r¿   r¿   Î  sd   € à€Ý&Ñ(Ô(ˆÝÑÔÐ
& Ð
&Ð
&€CÝŒ<˜ F°GÅ]Á_Ä_ÐUÑUÔU€DØ×ÒÑÔÐØ�9Š9‰;Œ;Ðr/   úrequests.Responsec                óº   — |€t          ¦   «         }t          ¦   «         › | › �}t          j        |||t	          ¦   «         ¬¦  «        }|                     ¦   «          |S )z;GET from camofox and return raw response (for binary data).Nrã   )r&   r6   rN   r:   r.   rÕ   rä   s        r%   Ú_get_rawrç   Ø  s[   € à€Ý&Ñ(Ô(ˆÝÑÔÐ
& Ð
&Ð
&€CÝŒ<˜ F°GÅ]Á_Ä_ÐUÑUÔU€DØ×ÒÑÔÐØ€Kr/   c                óÞ   — |€t          ¦   «         }t          ¦   «         › | › �}t          j        |||t	          ¦   «         ¬¦  «        }|                     ¦   «          |                     ¦   «         S )z-DELETE to camofox and return parsed response.NrÒ   )r&   r6   rN   Údeleter.   rÕ   rQ   rà   s        r%   Ú_deleterê   â  sd   € à€Ý&Ñ(Ô(ˆÝÑÔÐ
& Ð
&Ð
&€CÝŒ?˜3 T°7ÅMÁOÄOÐTÑTÔT€DØ×ÒÑÔÐØ�9Š9‰;Œ;Ðr/   c                óú  — 	 t          | ¦  «        \  }}t          |¦  «        }|d         st          ||¦  «        }d|dœ}n—	 t          d|d         › d�|d         |dœd¬	¦  «        }np# t          j        $ r^}|j        �L|j        j        dk    r<t           	                    d|d         ¦  «         d
|d<   t          ||¦  «        }d|dœ}n‚ Y d
}~nd
}~ww xY wd| 
                    d|¦  «        | 
                    dd¦  «        dœ}|r!| |d<   ||d<   d|d         › d|d         › �|d<   t          ¦   «         }|r
||d<   d|d<   	 t          d|d         › d�d|d         i¬¦  «        }	|	 
                    dd¦  «        }
dd lm}m} t!          |
¦  «        |k    r ||
¦  «        }
|
|d<   |	 
                    d!d¦  «        |d"<   n# t"          $ r Y nw xY wt%          j        |¦  «        S # t          j        $ r}t)          d#|› �d$¬%¦  «        cY d
}~S d
}~wt          j        $ r* t%          j        d$d&t-          ¦   «         › d'�d(œ¦  «        cY S t"          $ r(}t)          t/          |¦  «        d$¬%¦  «        cY d
}~S d
}~ww xY w))zNavigate to a URL via Camofox.r²   T)ÚokrV   ú/tabs/z	/navigaterl   )r´   rV   é<   rF   Ni”  uP   Camofox tab %s returned 404 â€” tab was garbage collected. Creating a fresh tab.rV   Útitler*   )ÚsuccessrV   rï   Úrequested_urlÚurl_rewritez0Rewrote loopback URL for Docker-hosted Camofox: r�   z -> rž   rb   Úvnc_urlz]Browser is visible via VNC. Share this link with the user so they can watch the browser live.Úvnc_hintú	/snapshotr´   ©rµ   Úsnapshotr   )ÚSNAPSHOT_SUMMARIZE_THRESHOLDÚ_truncate_snapshotÚ	refsCountÚelement_countzNavigation failed: F©rð   z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ð   Úerror)r®   rÏ   r×   rá   rN   Ú	HTTPErrorÚresponserO   r   rb   r:   r^   r¿   Útools.browser_toolrø   rù   Úlenr   rQ   Údumpsr   ÚConnectionErrorr6   r0   )rV   rh   Úbrowser_urlÚrewrite_infor°   rX   Úer!   ÚvncÚ	snap_dataÚsnapshot_textrø   rù   s                r%   Úcamofox_navigater  ð  sÄ  € ðL1Ý$EÀcÑ$JÔ$JÑ!ˆ�\Ý˜wÑ'Ô'ˆØ�xÔ ð 	å! '¨;Ñ7Ô7ˆGØ {Ð3Ð3ˆDˆDðÝØ9˜W XÔ.Ð9Ð9Ð9Ø& yÔ1¸+ÐFÐFØðñ ô ��øõ
 Ô%ð ð ð Ø”:Ð)¨a¬jÔ.DÈÒ.KÐ.KÝ—N’Nð0à Ô)ñô ð ð
 )-�G˜HÑ%Ý)¨'°;Ñ?Ô?�GØ"&¨{Ð;Ð;�D�Dàð �D�D�D�Døøøøðøøøð Ø—8’8˜E ;Ñ/Ô/Ø—X’X˜g rÑ*Ô*ð
ð 
ˆð
 ð 	Ø&)ˆF�?Ñ#Ø$0ˆF�=Ñ!ðBØ Ô'ðBð BØ-9¸$Ô-?ðBð Bð �9Ñõ ‰mŒmˆØð 	Ø #ˆF�9ÑðTð �:Ñð	ÝØ5˜ Ô*Ð5Ð5Ð5Ø  '¨)Ô"4Ð5ðñ ô ˆIð &ŸMšM¨*°bÑ9Ô9ˆMðð ð ð ð ð ð ð õ �=Ñ!Ô!Ð$@Ò@Ð@Ø 2Ð 2°=Ñ AÔ A�Ø!.ˆF�:ÑØ&/§m¢m°KÀÑ&CÔ&CˆF�?Ñ#Ð#øÝð 	ð 	ð 	ØˆDð	øøøõ Œz˜&Ñ!Ô!Ð!øÝÔð Dð Dð DÝÐ3°Ð3Ð3¸UÐCÑCÔCÐCÐCÐCÐCÐCÐCøøøøÝÔ#ð ð ð ÝŒzØð_µ_Ñ5FÔ5Fð _ð _ð _ð
ð 
ñ ô ð 	ð 	ð 	õ ð 1ð 1ð 1Ý�#˜a™&œ&¨%Ð0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs†   ‚?G& Á%A( Á'G& Á(CÁ7ACÃG& ÃCÃA.G& ÅA=G ÇG& Ç
GÇG& ÇGÇG& Ç&I:Ç5HÈI:È9I:É		I:ÉI5É/I:É5I:Úactionc                ó¨   — ddl m}m}  ||pd¦  «        sdS  || d         | d         ¦  «        }|sdS t          j        dd|› d	|› d
�dœd¬¦  «        S )uó  Return a blocked payload when the current Camofox page is private/internal.

    Mirrors the eval-path guard added for ``_camofox_eval`` (browser_tool.py):
    Camofox snapshot / vision / image-extraction all read current page state, so
    on a non-local backend they can leak the content of an intranet/metadata
    page the terminal itself can't reach.  The gate matches ``browser_snapshot``
    / ``browser_vision`` â€” only active when the SSRF guard applies (non-local
    backend, not a local sidecar, ``allow_private_urls`` unset).  Fail-open on
    probe failure, matching the sibling guards.

    Imports are deferred to call time because ``browser_tool`` imports this
    module; importing it at module load would create a circular import.
    r   )Ú!_camofox_current_page_private_urlÚ_eval_ssrf_guard_activerp   Nr²   rl   Fz9Blocked: page URL targets a private or internal address (z). Refusing to z# on this page in this browser mode.rý   )Úensure_ascii)r  r  r  rQ   r  )r°   rh   r  r  r  Úblocked_urls         r%   Ú_camofox_private_page_blockr  A  sÁ   € ðð ð ð ð ð ð ð ð
 #Ð" 7Ð#7¨iÑ8Ô8ð ØˆtØ3Ð3°G¸HÔ4EÀwÈyÔGYÑZÔZ€KØð ØˆtÝŒ:ØðØðð Ø,2ðð ð ðð ð ðñ ô ð r/   ÚfullÚ	user_taskc                ó$  — 	 t          |¦  «        }|d         st          dd¬¦  «        S t          ||d¦  «        }|r|S t          d|d         › d�d|d	         i¬
¦  «        }|                     dd¦  «        }|                     dd¦  «        }ddlm}m}	m}
 t          |¦  «        |k    r|r |	||¦  «        }n |
|¦  «        }t          j        d||dœ¦  «        S # t          $ r(}t          t          |¦  «        d¬¦  «        cY d}~S d}~ww xY w)z-Get accessibility tree snapshot from Camofox.r²   ú0No browser session. Call browser_navigate first.Frü   zread a page snapshotrí   rõ   r´   rl   rö   r÷   r*   rú   r   )rø   Ú_extract_relevant_contentrù   T)rð   r÷   rû   N)rÏ   r   r  r¿   r:   r  rø   r  rù   r  rQ   r  r   r0   )r  rh   r  r°   ÚblockedrX   r÷   Ú
refs_countrø   r  rù   r  s               r%   Úcamofox_snapshotr  c  s”  € ð$1Ý˜wÑ'Ô'ˆØ�xÔ ð 	aÝÐPÐZ_Ð`Ñ`Ô`Ð`å-¨g°wÐ@VÑWÔWˆØð 	ØˆNåØ1�W˜XÔ&Ð1Ð1Ð1Ø˜g iÔ0Ð1ð
ñ 
ô 
ˆð
 —8’8˜J¨Ñ+Ô+ˆØ—X’X˜k¨1Ñ-Ô-ˆ
ð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
õ ˆx‰=Œ=Ð7Ò7Ð7Øð 8Ø4Ð4°X¸yÑIÔI��à-Ð-¨hÑ7Ô7�åŒzØØ Ø'ð
ð 
ñ ô ð 	øõ
 ð 1ð 1ð 1Ý�#˜a™&œ&¨%Ð0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs(   ‚'C ªC ¿BC Ã
DÃ'D
ÄDÄ
DÚrefc                ó°  — 	 t          |¦  «        }|d         st          dd¬¦  «        S t          ||d¦  «        }|r|S |                      d¦  «        }t	          d|d         › d�|d	         |d
œ¦  «        }t          j        d||                     dd¦  «        dœ¦  «        S # t          $ r(}t          t          |¦  «        d¬¦  «        cY d}~S d}~ww xY w)z$Click an element by ref via Camofox.r²   r  Frü   Úclickrš   rí   z/clickrl   )r´   r  TrV   r*   )rð   ÚclickedrV   N)
rÏ   r   r  Úlstriprá   rQ   r  r:   r   r0   )r  rh   r°   r  Ú	clean_refrX   r  s          r%   Úcamofox_clickr!  �  s  € ð1Ý˜wÑ'Ô'ˆØ�xÔ ð 	aÝÐPÐZ_Ð`Ñ`Ô`Ð`å-¨g°wÀÑHÔHˆØð 	ØˆNð —J’J˜s‘O”Oˆ	åØ.�W˜XÔ&Ð.Ð.Ð.Ø˜yÔ)°)Ð<Ð<ñ
ô 
ˆõ ŒzØØ Ø—8’8˜E 2Ñ&Ô&ð
ð 
ñ ô ð 	øõ
 ð 1ð 1ð 1Ý�#˜a™&œ&¨%Ð0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs(   ‚'B# ªB# ¿A#B# Â#
CÂ-CÃ
CÃCÚtextc                ó  — 	 t          |¦  «        }|d         st          dd¬¦  «        S t          ||d¦  «        }|r|S |                      d¦  «        }t	          d|d         › d�|d	         ||d
œ¦  «         ddlm}m}  |dd|i¦  «        pi d         }d||dœ}	 ||	|¦  «        }	t          j	        |	¦  «        S # t          $ r8}
ddlm} t           |t          |
¦  «        |¦  «        d¬¦  «        cY d}
~
S d}
~
ww xY w)z-Type text into an element by ref via Camofox.r²   r  Frü   Útyperš   rí   z/typerl   )r´   r  r"  r   )Ú%redact_browser_typed_text_for_displayÚredact_tool_args_for_displayÚbrowser_typer"  T)rð   ÚtypedÚelement)r%  N)rÏ   r   r  r  rá   Úagent.displayr%  r&  rQ   r  r   r0   )r  r"  rh   r°   r  r   r%  r&  Údisplay_textr   r  s              r%   Úcamofox_typer,  ¨  s‘  € ð$^Ý˜wÑ'Ô'ˆØ�xÔ ð 	aÝÐPÐZ_Ð`Ñ`Ô`Ð`å-¨g°wÀÑGÔGˆØð 	ØˆNà—J’J˜s‘O”Oˆ	åØ-�W˜XÔ&Ð-Ð-Ð-Ø˜yÔ)°)ÀTÐJÐJñ	
ô 	
ð 	
ð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð
 5Ð4°^ÀfÈdÀ^ÑTÔTÐZÐXZÐ\bÔcˆð ð
 "Ø ð
ð 
ˆð 9Ð8¸À4ÑHÔHˆÝŒz˜(Ñ#Ô#Ð#øÝð ^ð ^ð ^ØGÐGÐGÐGÐGÐGåÐ?Ð?ÅÀAÁÄÈÑMÔMÐW\Ð]Ñ]Ô]Ð]Ð]Ð]Ð]Ð]Ð]øøøøð^øøøs(   ‚'B< ªB< ¿A<B< Â<
C>Ã-C9Ã3C>Ã9C>Ú	directionc                ó2  — 	 t          |¦  «        }|d         st          dd¬¦  «        S t          d|d         › d�|d         | dœ¦  «         t          j        d	| d
œ¦  «        S # t
          $ r(}t          t          |¦  «        d¬¦  «        cY d}~S d}~ww xY w)zScroll the page via Camofox.r²   r  Frü   rí   z/scrollrl   )r´   r-  T)rð   ÚscrolledN)rÏ   r   rá   rQ   r  r   r0   )r-  rh   r°   r  s       r%   Úcamofox_scrollr0  Ñ  sÍ   € ð1Ý˜wÑ'Ô'ˆØ�xÔ ð 	aÝÐPÐZ_Ð`Ñ`Ô`Ð`åØ/�W˜XÔ&Ð/Ð/Ð/Ø˜yÔ)¸	ÐBÐBñ	
ô 	
ð 	
õ Œz d¸	ÐBÐBÑCÔCÐCøÝð 1ð 1ð 1Ý�#˜a™&œ&¨%Ð0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs"   ‚'A$ ª9A$ Á$
BÁ.BÂBÂBc                óX  — 	 t          | ¦  «        }|d         st          dd¬¦  «        S t          d|d         › d�d|d         i¦  «        }t          j        d	|                     d
d¦  «        dœ¦  «        S # t          $ r(}t          t          |¦  «        d¬¦  «        cY d}~S d}~ww xY w)zNavigate back via Camofox.r²   r  Frü   rí   z/backr´   rl   TrV   r*   )rð   rV   N)rÏ   r   rá   rQ   r  r:   r   r0   )rh   r°   rX   r  s       r%   Úcamofox_backr2  á  sÕ   € ð1Ý˜wÑ'Ô'ˆØ�xÔ ð 	aÝÐPÐZ_Ð`Ñ`Ô`Ð`åØ-�W˜XÔ&Ð-Ð-Ð-Ø�w˜yÔ)Ð*ñ
ô 
ˆõ Œz d°4·8²8¸EÀ2Ñ3FÔ3FÐGÐGÑHÔHÐHøÝð 1ð 1ð 1Ý�#˜a™&œ&¨%Ð0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs#   ‚'A7 ªAA7 Á7
B)ÂB$ÂB)Â$B)r-   c                ó\  — 	 t          |¦  «        }|d         st          dd¬¦  «        S t          ||d¦  «        }|r|S t          d|d         › d�|d         | d	œ¦  «         t	          j        d
| dœ¦  «        S # t          $ r(}t          t          |¦  «        d¬¦  «        cY d}~S d}~ww xY w)z!Press a keyboard key via Camofox.r²   r  Frü   Úpressrí   z/pressrl   )r´   r-   T)rð   ÚpressedN)rÏ   r   r  rá   rQ   r  r   r0   )r-   rh   r°   r  r  s        r%   Úcamofox_pressr6  ñ  së   € ð1Ý˜wÑ'Ô'ˆØ�xÔ ð 	aÝÐPÐZ_Ð`Ñ`Ô`Ð`å-¨g°wÀÑHÔHˆØð 	ØˆNåØ.�W˜XÔ&Ð.Ð.Ð.Ø˜yÔ)°#Ð6Ð6ñ	
ô 	
ð 	
õ Œz d°sÐ;Ð;Ñ<Ô<Ð<øÝð 1ð 1ð 1Ý�#˜a™&œ&¨%Ð0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs'   ‚'A9 ªA9 ¿9A9 Á9
B+ÂB&Â B+Â&B+c                ó*  — 	 t          | ¦  «        }|st          j        dddœ¦  «        S t          d|d         › �¦  «         t          j        dddœ¦  «        S # t          $ r/}t          j        ddt          |¦  «        dœ¦  «        cY d}~S d}~ww xY w)z&Close the browser session via Camofox.T)rð   Úclosedz
/sessions/rl   )rð   r8  rb   N)rÛ   rQ   r  rê   r   r0   )rh   r°   r  s      r%   Úcamofox_closer9    sÄ   € ð
PÝ Ñ(Ô(ˆØð 	AÝ”:¨$¸$Ð?Ð?Ñ@Ô@Ð@åØ-˜ Ô+Ð-Ð-ñ	
ô 	
ð 	
õ Œz d°dÐ;Ð;Ñ<Ô<Ð<øÝð Pð Pð PÝŒz d°dÅsÈ1ÁvÄvÐNÐNÑOÔOÐOÐOÐOÐOÐOÐOøøøøðPøøøs"   ‚'A ª.A Á
BÁ#$BÂBÂBc                ó¬  — 	 t          | ¦  «        }|d         st          dd¬¦  «        S t          || d¦  «        }|r|S ddl}t	          d|d         › d	�d
|d         i¬¦  «        }|                     dd¦  «        }g }|                     d¦  «        }t          |¦  «        D ]Ù\  }}	|	                     ¦   «         }
|
 	                    d¦  «        r«| 
                    d|
¦  «        }|r|                     d¦  «        nd}d}|dz   t          |¦  «        k     rH| 
                    d||dz                                 ¦   «         ¦  «        }|r|                     d¦  «        }|s|r|                     ||dœ¦  «         ŒÚt          j        d|t          |¦  «        dœ¦  «        S # t           $ r(}t          t#          |¦  «        d¬¦  «        cY d}~S d}~ww xY w)z¶Get images on the current page via Camofox.

    Extracts image information from the accessibility tree snapshot,
    since Camofox does not expose a dedicated /images endpoint.
    r²   r  Frü   zextract page imagesr   Nrí   rõ   r´   rl   rö   r÷   r*   Ú
)z- img zimg zimg\s+"([^"]*)"rJ   z/url:\s*(\S+))ÚsrcÚaltT)rð   ÚimagesÚcount)rÏ   r   r  Úrer¿   r:   ÚsplitÚ	enumerater,   r¤   ÚsearchÚgroupr  ÚappendrQ   r  r   r0   )rh   r°   r  r@  rX   r÷   r>  ÚlinesÚiÚlineÚstrippedÚ	alt_matchr=  r<  Ú	url_matchr  s                   r%   Úcamofox_get_imagesrL    s)  € ð*1Ý˜wÑ'Ô'ˆØ�xÔ ð 	aÝÐPÐZ_Ð`Ñ`Ô`Ð`å-¨g°wÐ@UÑVÔVˆØð 	ØˆNàˆ	ˆ	ˆ	åØ1�W˜XÔ&Ð1Ð1Ð1Ø˜g iÔ0Ð1ð
ñ 
ô 
ˆð —8’8˜J¨Ñ+Ô+ˆð
 ˆØ—’˜tÑ$Ô$ˆÝ  Ñ'Ô'ð 	<ð 	<‰GˆAˆtØ—z’z‘|”|ˆHØ×"Ò"Ð#5Ñ6Ô6ð 
<ØŸIšIÐ&8¸(ÑCÔC�	Ø,5Ð=�i—o’o aÑ(Ô(Ð(¸2�à�Ø�q‘5�3˜u™:œ:Ò%Ð%Ø "§	¢	Ð*:¸EÀ!ÀaÁ%¼L×<NÒ<NÑ<PÔ<PÑ QÔ Q�IØ ð 1Ø'Ÿošo¨aÑ0Ô0˜Øð <˜#ð <Ø—M’M¨#°cÐ":Ð":Ñ;Ô;Ð;øåŒzØØÝ˜‘[”[ð
ð 
ñ ô ð 	øõ
 ð 1ð 1ð 1Ý�#˜a™&œ&¨%Ð0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs(   ‚'F! ªF! ¿E!F! Æ!
GÆ+GÇGÇGÚquestionÚannotatec                óª  — 	 t          |¦  «        }|d         st          dd¬¦  «        S t          ||d¦  «        }|r|S t          d|d         › d�d|d	         i¬
¦  «        }ddlm}  |¦   «         dz  }|                     dd¬¦  «         t          |dt          j	        ¦   «         j
        dd…         › d�z  ¦  «        }t          |d¦  «        5 }	|	                     |j        ¦  «         ddd¦  «         n# 1 swxY w Y   t          j        |j        ¦  «                             d¦  «        }
d}|rV	 t#          d|d         › d�d|d	         i¬
¦  «        }d|                     dd¦  «        dd…         › �}n# t&          $ r Y nw xY wddlm}  ||¦  «        }ddlm} d| › |› �}	 t1          ¦   «         }t3          |ddi ¬ ¦  «        }t5          |                     d!d"¦  «        ¦  «        }t5          |                     d#d$¦  «        ¦  «        }n# t&          $ r d%}d$}Y nw xY w |d&d'|d(œd)d*d+|
› �id,œgd-œgd||¬.¦  «        }|j        r+|j        d         j        j        pd                     ¦   «         nd}ddlm}  ||¦  «        }t=          j        d||d/œ¦  «        S # t&          $ r(}t          t          |¦  «        d¬¦  «        cY d}~S d}~ww xY w)0z<Take a screenshot and analyze it with vision AI via Camofox.r²   r  Frü   zcapture a screenshotrí   z/screenshotr´   rl   rö   r   )Úget_hermes_homeÚbrowser_screenshotsT)ÚparentsÚexist_okÚbrowser_screenshot_Né   z.pngÚwbzutf-8r*   rõ   z5

Accessibility tree (element refs for interaction):
r÷   i¸  )Úredact_sensitive_text)Úcall_llmz,Analyze this browser screenshot and answer: Ú	auxiliaryÚvision)rp   rG   éx   Útemperaturegš™™™™™¹?g      ^@Úuserr"  )r$  r"  Ú	image_urlrV   zdata:image/png;base64,)r$  r^  )ÚroleÚcontent)ÚmessagesÚtaskr\  rG   )rð   ÚanalysisÚscreenshot_path) rÏ   r   r  rç   Úhermes_constantsrP  Úmkdirr0   rÊ   rË   rÌ   ÚopenÚwriter`  Úbase64Ú	b64encodeÚdecoder¿   r:   r   Úagent.redactrW  Úagent.auxiliary_clientrX  r   r   ÚfloatÚchoicesÚmessager,   rQ   r  )rM  rN  rh   r°   r  rW   rP  Úscreenshots_dirrd  ÚfÚimg_b64Úannotation_contextr	  rW  rX  Úvision_promptÚ_cfgÚ_vision_cfgÚ_vision_timeoutÚ_vision_temperaturer   rc  r  s                          r%   Úcamofox_visionrz  G  s[  € ð[1Ý˜wÑ'Ô'ˆØ�xÔ ð 	aÝÐPÐZ_Ð`Ñ`Ô`Ð`å-¨g°wÐ@VÑWÔWˆØð 	ØˆNõ Ø3�W˜XÔ&Ð3Ð3Ð3Ø˜g iÔ0Ð1ð
ñ 
ô 
ˆð 	5Ð4Ð4Ð4Ð4Ð4Ø)˜/Ñ+Ô+Ð.CÑCˆØ×Ò d°TÐÑ:Ô:Ð:Ý˜oÐ0`ÅdÄjÁlÄlÔFVÐWYÐXYÐWYÔFZÐ0`Ð0`Ð0`Ñ`ÑaÔaˆå�/ 4Ñ(Ô(ð 	"¨AØ�GŠG�D”LÑ!Ô!Ð!ð	"ð 	"ð 	"ñ 	"ô 	"ð 	"ð 	"ð 	"ð 	"ð 	"ð 	"øøøð 	"ð 	"ð 	"ð 	"õ Ô" 4¤<Ñ0Ô0×7Ò7¸Ñ@Ô@ˆð  ÐØð 	ðÝ Ø9˜W XÔ.Ð9Ð9Ð9Ø$ g¨iÔ&8Ð9ðñ ô �	ð &GÐ`i×`mÒ`mÐnxÐz|Ñ`}Ô`}ð  Dð  @Dð  Dô  aEð  &Gð  &GÐ"Ð"øÝð ð ð Ø�ðøøøð 	7Ð6Ð6Ð6Ð6Ð6Ø2Ð2Ð3EÑFÔFÐð 	4Ð3Ð3Ð3Ð3Ð3ð$¸8ð $Ø!ð$ð $ð 	ð
	&Ý‘=”=ˆDÝ! $¨°XÀrÐJÑJÔJˆKÝ# K§O¢O°I¸sÑ$CÔ$CÑDÔDˆOÝ"'¨¯ª¸ÀsÑ(KÔ(KÑ"LÔ"LÐÐøÝð 	&ð 	&ð 	&Ø#ˆOØ"%ÐÐÐð	&øøøð �8àà#¨]Ð;Ð;à +à!Ð#E¸GÐ#EÐ#Eð&ðð ððð ð ð Ø+Ø#ð
ñ 
ô 
ˆð" KSÔJZÐb�HÔ$ QÔ'Ô/Ô7Ð=¸2×DÒDÑFÔFÐFÐ`bˆð 	7Ð6Ð6Ð6Ð6Ð6Ø(Ð(¨Ñ2Ô2ˆåŒzØØ Ø.ð
ð 
ñ ô ð 	øõ
 ð 1ð 1ð 1Ý�#˜a™&œ&¨%Ð0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs—   ‚'J  ªJ  ¿BJ  ÃC7Ã+J  Ã7C;Ã;J  Ã>C;Ã?3J  Ä3AE8 Å7J  Å8
FÆJ  ÆFÆ!J  Æ'A'H ÈJ  ÈH ÈJ  ÈH È A?J  Ê 
KÊ*KËKËKÚclearc           	     ó8   — t          j        dg g ddddœ¦  «        S )u¤   Get console output â€” limited support in Camofox.

    Camofox does not expose browser console logs via its REST API.
    Returns an empty result with a note.
    Tr   z|Console log capture is not available with the Camofox backend. Use browser_snapshot or browser_vision to inspect page state.)rð   Úconsole_messagesÚ	js_errorsÚtotal_messagesÚtotal_errorsÚnote)rQ   r  )r{  rh   s     r%   Úcamofox_consoler‚  ¨  s7   € õ Œ:ØØØØØðPðð ñ ô ð r/   )r   r   )r   r'   )r   r0   )r   r?   )r   r   )r   r_   )rh   r   rc   r_   r   ri   )rs   r0   r   rt   )rc   r_   r   r?   )rc   r_   r   r0   )rS   r   r   r?   )rV   r0   r   r–   )r°   r_   r   r_   )rh   r   r   r_   )rÐ   )rh   r   rV   r0   r   r_   )rh   r   r   rØ   )N)rh   r   r   r?   )r¦   r0   rÞ   r<   rG   r   r   r<   )NN)r¦   r0   rµ   r<   rG   r   r   r<   )r¦   r0   rµ   r<   rG   r   r   rå   )rV   r0   rh   r   r   r0   )r°   r_   rh   r   r  r0   r   r   )FNN)r  r?   rh   r   r  r   r   r0   )r  r0   rh   r   r   r0   )r  r0   r"  r0   rh   r   r   r0   )r-  r0   rh   r   r   r0   )rh   r   r   r0   )r-   r0   rh   r   r   r0   )FN)rM  r0   rN  r?   rh   r   r   r0   )r{  r?   rh   r   r   r0   )NÚ__doc__Ú
__future__r   ri  rQ   ÚloggingrB   Ú	threadingrÊ   Útypingr   r   r   rR   r   r   r	   rN   Úagent.secret_scoper
   r9   r   r   r   Útools.browser_camofox_stater   Útools.registryr   Ú	getLoggerÚ__name__r   r   Ú_SNAPSHOT_MAX_CHARSr   Ú__annotations__rP   r   r   r&   r.   r6   r>   rD   r\   r^   rd   rg   rr   r€   r‡   r‹   r�   r•   r®   r¯   ÚLockrÉ   rÄ   rÏ   r×   rÛ   rÝ   rá   r¿   rç   rê   r  r  r  r!  r,  r0  r2  r6  r9  rL  rz  r‚  r5   r/   r%   ú<module>r�     sç  ððð ð ð2 #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø €€€Ø 	€	€	€	Ø Ð Ð Ð Ø €€€Ø &Ð &Ð &Ð &Ð &Ð &Ð &Ð &Ð &Ð &Ø :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :à €€€à )Ð )Ð )Ð )Ð )Ð )Ø CÐ CÐ CÐ CÐ CÐ CÐ CÐ CÐ CÐ CØ <Ð <Ð <Ð <Ð <Ð <Ø %Ð %Ð %Ð %Ð %Ð %à	ˆÔ	˜8Ñ	$Ô	$€ð Ð ØÐ Ø€Ð Ð Ð Ñ ØÐ ð &*Ð Ð )Ð )Ð )Ñ )ØÐ ðð ð ð ð0ð ð ð ð=ð =ð =ð =ð
ð ð ð ð$#ð #ð #ð #ð&ð ð ð ð2ð ð ð ð@ð @ð @ð @ð	Bð 	Bð 	Bð 	Bð<ð <ð <ð <ð,	ð 	ð 	ð 	ð7ð 7ð 7ð 7ð:ð :ð :ð :ð$ð ð ð ðð ð ð ð&ð &ð &ð &ðZ (*€	Ð )Ð )Ð )Ñ )Ø�”Ñ!Ô!€ð#ð #ð #ð #ðL(,ð (,ð (,ð (,ðVð ð ð ð ð,,ð ,ð ,ð ,ðð ð ð ð ð*ð ð ð ð ðð ð ð ð ðð ð ð ð ðð ð ð ð ðN1ð N1ð N1ð N1ð N1ðbð ð ð ðD CGØ04ð'1ð '1ð '1ð '1ð '1ðT1ð 1ð 1ð 1ð 1ð6&^ð &^ð &^ð &^ð &^ðR1ð 1ð 1ð 1ð 1ð 1ð 1ð 1ð 1ð 1ð 1ð 1ð 1ð 1ð 1ð(Pð Pð Pð Pð Pð01ð 01ð 01ð 01ð 01ðf 49Ø,0ð^1ð ^1ð ^1ð ^1ð ^1ðBð ð ð ð ð ð r/   