Ë
    ãß²j§  ã                   óº  — d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZ ddlmZmZmZmZmZmZ ddlmZ ddlmZ  ej0                  e«      ZdZdZ ej:                  d	«      Zd
ZdZ dZ!dZ"dZ#dZ$ ej:                  d«      Z% ej:                  dejL                  «      Z' ej:                  dejL                  «      Z(dZ)dndeg ef   dede*defd„Z+de*de*dede*def
d„Z,dnde*de-fd„Z.de-fd„Z/d e0d!e*ddfd"„Z1d e0de-fd#„Z2d e0d$ee*gef   d%e*d&ee3ge*f   d'e*dee*   fd(„Z4d)e*dee*   fd*„Z5de0fd+„Z6d,e*de*fd-„Z7de0fd.„Z8d/e0de-fd0„Z9de*fd1„Z:d/e0de-fd2„Z;de-fd3„Z<dee*   fd4„Z=de*fd5„Z>deee*      fd6„Z?dod7e@dee-e*f   fd8„ZAd9ee*   dee*   fd:„ZBd;e*d<eCdee*   fd=„ZDd>ee*ef   d,e*deee*ef      fd?„ZEde*fd@„ZFdnd9ee*   dAe*de*fdB„ZGdnd e0d9ee*   dAe*dee*   fdC„ZHdnd e0d9ee*   dAe*dee*   fdD„ZIdnd e0d9ee*   dAe*dee*   fdE„ZJdnd e0d9ee*   dAe*dee*   fdF„ZKd e0dGe-dee*   fdH„ZLd e0d9ee*   ddfdI„ZMd e0dJe*d9ee*   dKe-dee*   f
dL„ZNde0fdM„ZOd7ede@fdN„ZPdOZQdpdP„ZRdQ„ ZSde!ddRfd)e*dJe*d7e@d9ee*   dKe-f
dS„ZTdTZUdUZVdVZWdWZXdXZYde*fdY„ZZde0fdZ„Z[d[eUeYz   d\z   d]d^d_d`œd^dad`œdbe!dce!› dde#› de�dfœdgœd)gdhœdiœZ\ddjl]m^Z^  e^j¾                  d[de\dk„ e<e[dl¬m«       y)qz»Use the Browser Use CLI 3.0 (https://browser-use.com) for browser automation

When browser.backend is "browser-use", the model gets ``browser_exec`` tool
instead of default browser tools
é    N)ÚPath)ÚAnyÚCallableÚDictÚListÚOptionalÚTuple)Úget_hermes_home)Úis_truthy_valueúbrowser-useÚoffz ^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$Ú_HERMES_BU_PRIVATE_BROWSERaù  # hermes: pin this named session to its own tab (once per daemon process)
def _hermes_ensure_own_tab():
    import os as _os, tempfile as _tf
    _name = _os.environ.get("BU_NAME", "default")
    try:
        # Key the marker by the daemon's pid so a daemon restart (which
        # re-attaches to the first shared page) re-pins automatically,
        # while agent-driven tab switches mid-session are left alone.
        from browser_harness import _ipc as _bipc
        _dpid = _bipc.pid_path(_name).read_text().strip() or "0"
    except Exception:
        _dpid = "0"
    _uid = _os.getuid() if hasattr(_os, "getuid") else 0
    _marker = _os.path.join(
        _tf.gettempdir(), "hermes-bu-owntab-%s-%s-%s" % (_uid, _name, _dpid)
    )
    if _os.path.exists(_marker):
        return
    try:
        # Force a fresh target: new_tab() would REUSE a blank current tab,
        # which is exactly the tab a sibling daemon may also hold.
        _tid = cdp("Target.createTarget", url="about:blank").get("targetId")
        if _tid:
            switch_tab(_tid)
    except Exception:
        pass  # best-effort: worst case is pre-fix behavior
    try:
        open(_marker, "w").close()
    except OSError:
        pass
_hermes_ensure_own_tab()
del _hermes_ensure_own_tab
i,  é   i  i   z[^A-Za-z0-9._-]+z4((?:[A-Za-z]:[\\/]|/)[^\s\"']+?\.(?:png|jpe?g|webp))zhttps?://[^\s'\"\\)]+)z/usr/local/sbinz/usr/local/binz	/usr/sbinz/usr/binz/sbinz/binÚ ÚfnÚdefaultÚ
log_prefixÚreturnc                 ór   — 	  | «       S # t         $ r$}|rt        j                  d||«       Y d}~|S d}~ww xY w)zT``fn()``, or ``default`` on any exception (debug-logged when ``log_prefix`` is set).z%s: %sN)Ú	ExceptionÚloggerÚdebug)r   r   r   Úes       ú:/home/ubuntu/.hermes/hermes-agent/tools/browser_use_cli.pyÚ_quietr   W   s7   € ð2Ù‹tˆøÜò 2ÙÜ�L‰L˜ :¨qÔ1ûØ€Nûð2ús   ‚	 ‰	6’1±6ÚmoduleÚnamec                 ó(   ‡ ‡— t        ˆ ˆfd„||«      S )z™Call ``module.name()`` resolved at call time (tests stub the module / patch the
    attribute); on any failure log ``log_prefix`` and return ``default``.c                  óL   •—  t        t        j                  ‰ «      ‰«      «       S ©N)ÚgetattrÚ	importlibÚimport_module)r   r   s   €€r   ú<lambda>z_lazy_call.<locals>.<lambda>d   s   ø€ ÐHœ'¤)×"9Ñ"9¸&Ó"AÀ4ÓHÓJ€ ó    )r   )r   r   r   r   s   ``  r   Ú
_lazy_callr&   a   s   ù€ ô ÔJÈGÐU_Ó`Ð`r%   Úcontextc                 ó$   — t        dddd| › �«      S )Nztools.browser_camofoxÚis_camofox_modeFzCamofox activity check failed©r&   )r'   s    r   Ú_camofox_activer+   g   s   € ÜÐ-Ð/@À%ÐKhÐipÐhqÐIrÓsÐsr%   c                  ó   — t        dddd«      S )Nztools.browser_tool_cloudÚ_use_real_profileFz"real-profile consent lookup failedr*   © r%   r   Ú_real_profile_consentedr/   k   s   € ÜÐ0Ð2EÀuÐNrÓsÐsr%   ÚenvÚcdpc                 ó8   — || |j                  d«      rd<   yd<   y)uM   Export a CDP endpoint under the BU_CDP_* contract (http(s) â†’ URL, else WS).)zhttp://zhttps://Ú
BU_CDP_URLÚ	BU_CDP_WSN)Ú
startswith)r0   r1   s     r   Ú_set_cdp_envr6   o   s   € àTW€C˜Ÿ™Ð'>Ô?ˆÒQÀ[ÒQr%   c                 ó\   — t        | j                  d«      xs | j                  d«      «      S )Nr4   r3   )ÚboolÚget)r0   s    r   Ú_has_cdp_envr:   t   s$   € Ü�—‘˜Ó$Ò=¨¯©°Ó(=Ó>Ð>r%   Úget_session_infoÚ	cache_keyÚfail_msgÚ
no_cdp_msgc                 ó²   — 	 t         ||«      xs i j                  d«      xs d«      }|s|S t        | |«       y# t        $ r} ||«      cY d}~S d}~ww xY w)z_Export the CDP endpoint from ``get_session_info(cache_key)``; error string on failure / no CDP.Úcdp_urlr   N)Ústrr9   r   r6   )r0   r;   r<   r=   r>   r1   r   s          r   Ú_export_session_cdprB   x   s`   € ðÜÑ# IÓ.Ò4°"×9Ñ9¸)ÓDÒJÈÓKˆñ ØÐÜ��cÔØøô ò Ù˜‹{Õûðús   ‚(; »	AÁAÁAÁAÚcodec           
      óv   — ddl m} t        d„ t        |t        j                  | xs d«      «      D «       d«      S )zFReturn an error if a URL literal fails the built-in navigation checks.r   )Úevaluate_url_safetyc              3   óF   K  — | ]  }|sŒ|j                  d d«      –— Œ y­w)ÚerrorzBlocked: unsafe URLN)r9   )Ú.0Úerrs     r   ú	<genexpr>z'_blocked_url_in_code.<locals>.<genexpr>ˆ   s)   è ø€ ò  B¸Sò  ~A�—‘˜Ð"7×8ñ  Bùs   ‚!Š!r   N)Útools.browser_toolrE   ÚnextÚmapÚ_URL_REÚfindall)rC   rE   s     r   Ú_blocked_url_in_coderP   …   sD   € å6Üñ  BÄCÐH[Ô]d×]lÑ]lÐmqÒmwÐuwÓ]xÓDyô  Bð  DHó  Ið  Ir%   c                  óÈ   — ddl m}   | «       }|j                  dd «       |j                  dd «       t        |j	                  dd«      «      |d<   |j                  dd«       |S )	Nr   )Ú_build_browser_envÚ
PYTHONPATHÚ
PYTHONHOMEÚPATHr   ÚANONYMIZED_TELEMETRYÚfalse)rK   rR   ÚpopÚ_floor_subprocess_pathr9   Ú
setdefault)rR   r0   s     r   Ú_base_subprocess_envr[   ‹   sX   € Ý5Ù
Ó
€Cð ‡G�GˆL˜$ÔØ‡G�GˆL˜$ÔÜ(¨¯©°¸Ó)<Ó=€Cˆ�KØ‡N�NÐ)¨7Ô3Ø€Jr%   Úpathc           
      óð  — t         j                  dk(  r| S t        j                  t        «      5  ddlm}  || xs d«      cddd«       S # 1 sw Y   nxY w| xs dj                  t         j                  «      D �cg c]  }|sŒ|‘Œ	 nc c}w }}t         j                  j                  |t        D �cg c]2  }|t        |«      vsŒt         j                  j                  |«      sŒ1|‘Œ4 nc c}w c}z   «      S )a‰  Guarantee core system dirs on the CLI subprocess PATH: profile workers (kanban bots, cron) can inherit
    a PATH of only version-manager dirs, and the uv binary's POSIX sh trampoline resolves ``dirname``/``realpath``
    via PATH (exit 127 without /usr/bin). Reuses browser_tool's ``_merge_browser_path`` floor, else appends
    FHS bin dirs. Windows .cmd shims don't trampoline: no-op there.Úntr   )Ú_merge_browser_pathr   N)Úosr   Ú
contextlibÚsuppressr   Útools.browser_tool_installr_   ÚsplitÚpathsepÚjoinÚ_FHS_BIN_DIRSÚsetr\   Úisdir)r\   r_   ÚpÚpartsÚds        r   rY   rY   œ   s¹   € ô
 
‡w�w�$‚ØˆÜ	×	Ñ	œYÓ	'ñ /ÝBÙ" 4¢:¨2Ó.÷/÷ /ò /úð š ×*Ñ*¬2¯:©:Ó6Ö<�1º!ŠQÑ<ùÒ<€EÐ<Ü�:‰:�?‰?˜5¬}Ö#i¨!ÀÌÈUËÒ@SÔXZ×X_ÑX_×XeÑXeÐfgÕXh¢AÑ#iùÔ#iÑiÓjÐjs)   ¯AÁAÁ<B	ÂB	Â2C*
ÃC*
Ã%C*
c                  ó¸   — 	 ddl m} m}  |  |«       di ¬«      }t        |t        «      r|S i S # t
        $ r"}t        j                  d|«       i cY d}~S d}~ww xY w)z=Return the ``browser:`` config section, or {} on any failure.r   )Úcfg_getÚread_raw_configÚbrowser©r   z)Could not read browser config section: %sN)Úhermes_cli.configrn   ro   Ú
isinstanceÚdictr   r   r   )rn   ro   Úcfgr   s       r   Ú_read_browser_cfgrv   ª   sS   € ðß>Ù‘oÓ'¨¸BÔ?ˆÜ  ¤dÔ+ˆsÐ3°Ð3øÜò Ü�‰Ð@À!ÔDØ�	ûðús   ‚). ¬. ®	A·AÁAÁAÚbrowser_cfgc                 óâ   — t        | j                  d«      d¬«      ry	 ddlm} t        | j                  d«      xs d«      j                  «       j                  «       |k(  S # t        $ r Y yw xY w)	u[  True when the browser section selects the Nous Tool Gateway â€” by the current ``hermes tools``
    picker row (``cloud_provider: nous``) or the pre-picker ``use_gateway: true`` flag. Reading only
    the legacy flag missed every picker-configured gateway, and the direct-API branch it fell into
    holds no credentials in managed mode (#108310).Úuse_gatewayFrq   Tr   )ÚNOUS_MANAGED_PROVIDERÚcloud_providerr   )r   r9   Útools.tool_backend_helpersrz   r   rA   ÚstripÚlower)rw   rz   s     r   Ú_use_gatewayr   µ   sj   € ô
 �{—‘ }Ó5¸uÕEØðÝDô ˆ{�‰Ð/Ó0Ò6°BÓ7×=Ñ=Ó?×EÑEÓGÐK`Ñ`Ð`øô ò Ùðús   ŸA" Á"	A.Á-A.c                  óÂ   — t        «       j                  d«      } t        | t        «      r| du rt        S dS t        | xs d«      j                  «       j                  «       S )u›   Configured browser backend key ("" = unset â†’ default). YAML 1.1 parses an
    unquoted ``off`` as False â€” that must mean BACKEND_DISABLED, not "unset".ÚbackendFr   )rv   r9   rs   r8   ÚBACKEND_DISABLEDrA   r}   r~   )Úraws    r   Úget_browser_backendr„   Ã   sV   € ô Ó
×
!Ñ
! )Ó
,€CÜ9CÀCÌÔ9N  u¡ÔÐr°"ÐrÔTWÐX[ÒXaÐ_aÓTb×ThÑThÓTj×TpÑTpÓTrÐrr%   c                 ó   — t        | t        «      r| j                  d«      ryt        | j                  d«      xs d«      j	                  «       j                  «       }|dvst        | «      st        d«      ryddlm	} t         |d	d«      «      S )
zîTrue for pre-CLI direct-API Browser Use cloud configs. An explicit backend or
    a non-Browser-Use cloud_provider wins; Camofox is selected via env var, not
    cloud_provider, so a Camofox user with a stray BROWSER_USE_API_KEY keeps it.r�   Fr{   r   >   r   r   z during migrationr   )Ú
get_secretÚBROWSER_USE_API_KEY)rs   rt   r9   rA   r}   r~   r   r+   Úagent.secret_scoper†   r8   )rw   Úproviderr†   s      r   Ú"is_legacy_browser_use_cloud_configrŠ   Ê   sx   € ô �k¤4Ô(¨K¯O©O¸IÔ,FØÜ�;—?‘?Ð#3Ó4Ò:¸Ó;×AÑAÓC×IÑIÓK€HØÐ*Ñ*¬l¸;Ô.GÌ?Ð[nÔKoØå-Ü‘
Ð0°"Ó5Ó6Ð6r%   c                  ó„   — t        «       ryt        «       } | r	| t        k(  S t        t	        «       «      xs t        «       duS )az  True when the Browser Use CLI replaces the built-in browser stack. Browser Use mode is the DEFAULT:
    unset ``browser.backend`` ("") enables it whenever the CLI is runnable (installed binary or uvx);
    ``browser.backend: off`` keeps the built-in browser_* tools. Camofox always falls back to the built-in
    tools (Firefox, custom HTTP API, no CDP surface for the harness).FN)r+   r„   Ú_BACKEND_KEYrŠ   rv   Ú	_find_cli)r�   s    r   Úis_browser_use_cli_moderŽ   Ø   sQ   € ô
 ÔØÜ!Ó#€GÙ&-ˆ7”lÑ"ð  HÔ4VÔWhÓWjÓ4kò  5GÔoxÓozð  CGð  pGð  Hr%   c                  ój  — 	 t        «       st        «       s
t        «       �yt        t	        «       «      dz  dz  } t        j                  t        «      5  dt        j                  «       | j                  «       j                  z
  cxk  rdk  rn n
	 ddd«       yddd«       t        j                  t        «      5  | j                  j                  dd¬«       | j                  «        ddd«       y# 1 sw Y   ŒYxY w# 1 sw Y   yxY w# t        $ r }t        j!                  d	|«       Y d}~yd}~ww xY w)
z±One-line notice when ``browser.backend`` is unset but the CLI is not runnable, so
    the session fell back to the built-in tools. Rate-limited to once per 24h via a stamp file.NÚcachez.browser_use_default_noticer   i€Q T©ÚparentsÚexist_okuÀ   Browser Use CLI not found â€” using the built-in browser tools. Run `hermes tools` (Browser Automation â†’ Browser Use) to install it, or `browser.backend: off` in config.yaml to silence this.z'browser-use downgrade notice failed: %s)r„   r+   r�   r   r
   ra   rb   ÚOSErrorÚtimeÚstatÚst_mtimeÚparentÚmkdirÚtouchr   r   r   )Ústampr   s     r   Údefault_downgrade_noticerœ   ã   sõ   € ðÜÔ ¤OÔ$5¼»Ð9PØÜ”_Ó&Ó'¨'Ñ1Ð4QÑQˆÜ× Ñ ¤Ó)ñ 	Ø”D—I‘I“K %§*¡*£,×"7Ñ"7Ñ7ÔC¸)ÕCØ÷	÷ 	ð 	ô × Ñ ¤Ó)ñ 	Ø�L‰L×Ñ t°dÐÔ;Ø�K‰KŒM÷	ð@÷	ð 	ú÷	ð@ûäò Ü�‰Ð>ÀÔBÜûðúsX   ‚D	 ¡2D	 Á=C1ÂD	 Â!D	 Â:.C=Ã(D	 Ã1C:Ã6D	 Ã=DÄD	 ÄD	 Ä		D2ÄD-Ä-D2c                  ó@   — t        t        t        «       «      dz  «      S )uV   $HERMES_HOME/bin â€” where install.sh puts uv/uvx and install_cli() links browser-use.Úbin)rA   r   r
   r.   r%   r   Ú_managed_bin_dirrŸ   ÷   s   € äŒt”OÓ%Ó&¨Ñ.Ó/Ð/r%   c                  óØ  — t         j                  dk(  r>t         j                  j                  d«      } | rt	        t        | «      dz  dz  «      nd}n7t	        t        t         j                  j                  d«      «      dz  dz  «      }t        «       d|fD �cg c]	  }|�|sŒ|‘Œ }}dd	„ fd
d„ ffD ]1  \  }}|D ]'  }t        j                  ||¬«      }|sŒ ||«      c c S  Œ3 yc c}w )u‘  Locate the browser-use CLI, or None when it can't be run. MANAGED-FIRST: Hermes' own ``$HERMES_HOME/bin``
    copy always wins so every session drives one Hermes-controlled binary; PATH and the user-level tool dir
    (~/.local/bin, or uv's %APPDATA%/uv/bin on Windows â€” Desktop/TUI workers may start with a minimal PATH
    that omits it) are fallbacks; uvx zero-install (same probe order) is last.r^   ÚAPPDATAÚuvrž   NÚ~z.localr   c                 ó   — | gS r    r.   ©Úbs    r   r$   z_find_cli.<locals>.<lambda>  s   € °!°€ r%   Úuvxc                 ó
   — | dgS )Nr   r.   r¥   s    r   r$   z_find_cli.<locals>.<lambda>  s   € È!È]ÐI[€ r%   ©r\   )r`   r   Úenvironr9   rA   r   r\   Ú
expanduserrŸ   ÚshutilÚwhich)ÚappdataÚuser_binrj   Úprobe_pathsr   ÚargvÚ
probe_pathÚfounds           r   r�   r�   ü   sã   € ô
 
‡w�w�$‚Ü—*‘*—.‘. Ó+ˆÙ8?”3”t˜G“} tÑ+¨eÑ3Ô4ÀT‰ä”tœBŸG™G×.Ñ.¨sÓ3Ó4°xÑ?À%ÑGÓHˆÜ/Ó1°4¸ÐBÖU˜ÀaÀiÒST’1ÐU€KÐUØ%¡}Ð5¸Ñ?[Ð7\Ð]ò #‰
ˆˆdØ%ò 	#ˆJÜ—L‘L ¨JÔ7ˆEÚÙ˜E“{Ô"ñ	#ð#ð
 ùò Vs   Â	C'Â"C'Ú	timeout_sc           
      ót  — t        «       }t        j                  d|¬«      }|rdd|› d�fS dt        t           fd„}t        |dd	«      xs t        j                  d
«      }|syi t        j                  ¥ddi¥}	 t        |«      j                  dd¬«       ||d<   	 t        j                  |dddgdddd|| t        j                  ¬«      }|j$                  dk7  rSdj'                  |j(                  xs |j*                  xs dj-                  «       j/                  «       dd «      }dd|› �fS t1        «       }	|	rt3        |	«      dk7  ry dd!|	d   › d�fS # t        $ r!}t        j                  d||«       Y d}~ŒÝd}~ww xY w# t        j                   $ r dd| › d�fcY S t"        $ r}dd|› �fcY d}~S d}~ww xY w)"u»  Install the browser-use CLI via ``uv tool install`` (managed uv via ``ensure_uv`` â†’ uv on PATH), linking
    the binary into ``$HERMES_HOME/bin`` (``UV_TOOL_BIN_DIR``) so ``_find_cli()`` resolves it for every profile.
    Returns ``(ok, message)``; never raises. MANAGED-FIRST: only the managed copy short-circuits â€” a browser-use
    on PATH is a user-level side install and must not block provisioning the canonical copy (version drift).r   r©   Tz#browser-use CLI already installed (ú)r   c                  ó>   — ddl m}  t         | «       xs d«      xs d S )Nr   ©Ú	ensure_uvr   )Úhermes_cli.managed_uvr¹   rA   r¸   s    r   Ú_managed_uvz install_cli.<locals>._managed_uv  s   € Ý3Ü‘9“;Ò$ "Ó%Ò-¨Ð-r%   Nz Managed uv bootstrap unavailabler¢   )Fz�uv is not available and could not be bootstrapped. Install uv (https://docs.astral.sh/uv/) and run `uv tool install browser-use`.ÚUV_NO_CONFIGÚ1r‘   ÚUV_TOOL_BIN_DIRzCould not prepare %s: %sÚtoolÚinstallúutf-8Úreplace)Úcapture_outputÚtextÚencodingÚerrorsr0   ÚtimeoutÚstdinFz.`uv tool install browser-use` timed out after Úsz-Failed to run `uv tool install browser-use`: r   Ú
r   éýÿÿÿz&`uv tool install browser-use` failed:
é   )Fuz   install reported success but the browser-use binary is still not resolvable â€” run `uv tool install browser-use` manuallyzbrowser-use CLI installed ()rŸ   r¬   r­   r   rA   r   r`   rª   r   r™   r”   r   r   Ú
subprocessÚrunÚDEVNULLÚTimeoutExpiredr   Ú
returncoderf   ÚstderrÚstdoutr}   Ú
splitlinesr�   Úlen)
r´   Úbin_dirÚmanagedr»   Úuv_binr0   r   ÚresultÚtailr³   s
             r   Úinstall_clirÛ     sä  € ô
 Ó €GÜ�l‰l˜=¨wÔ7€GÙØÐ:¸7¸)À1ÐEÐEÐEð.œ¤#™ó .ô �K Ð'IÓJÒ`ÌfÏlÉlÐ[_ÓN`€FÙð^à
-ŒR�Z‰ZÐ
-˜¨Ñ
-€Cð=ÜˆW‹×Ñ D°4ÐÔ8Ø!(ˆÐÑðJÜ—‘ ¨°¸MÐ JÐ[_ÐfjÐu|Ø'0°cÀ9ÔT^×TfÑTfôhˆð ×Ñ˜AÒØ�y‰y˜&Ÿ-™-Ò>¨6¯=©=Ò>¸B×EÑEÓG×RÑRÓTÐUWÐUXÐYÓZˆØÐ?À¸vÐFÐFÐFÜ‹K€EÙ”C˜“J !’OðEàÐ.¨u°Q©x¨j¸Ð:Ð:Ð:øô% ò =Ü�‰Ð/°¸!×<Ñ<ûð=ûô ×$Ñ$ò TØÐFÀyÀkÐQRÐSÐSÒSÜò JØÐEÀaÀSÐIÐIÕIûðJús<   Á;!E Â/F Å	E>ÅE9Å9E>ÆF7ÆF7Æ&F2Æ,F7Æ2F7Útask_idc                 óŽ  — t         j                  j                  d«      rt         j                  d   S 	 t        j	                  dt        | xs d«      «      dd xs d}t        t        «       «      dz  dz  dz  |z  }|j                  d	d	¬
«       t        |«      S # t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)zCStable per-task scratch dir that persists across browser_exec callsÚBH_AGENT_WORKSPACEÚ_r   NéP   r�   r   Ú	workspaceTr‘   z&browser_exec workspace unavailable: %s)r`   rª   r9   Ú_TASK_ID_SAFE_REÚsubrA   r   r
   r™   r   r   r   )rÜ   Úsafer\   r   s       r   Ú_workspace_dirrå   9  s®   € ä	‡z�z‡~�~Ð*Ô+Ü�z‰zÐ.Ñ/Ð/ðÜ×#Ñ# C¬¨WÒ-A¸	Ó)BÓCÀCÀRÐHÒUÈIˆÜ”OÓ%Ó&¨Ñ0°=Ñ@À;ÑNÐQUÑUˆØ�
‰
˜4¨$ˆ
Ô/Ü�4‹yÐøÜò Ü�‰Ð=¸qÔAÜûðús   ´A&B Â	CÂ$B?Â?CrÓ   Úsincec                 ó  — t        t        j                  | xs d«      «      D ]K  }	 t        j                  j                  |«      r)t        j                  j                  |«      |dz
  k\  r|c S ŒM y# t        $ r Y ŒZw xY w)znLast screenshot path printed during this exec that exists and was written after
    the exec started, or None.r   rÌ   N)ÚreversedÚ_IMAGE_PATH_RErO   r`   r\   ÚisfileÚgetmtimer”   )rÓ   ræ   r\   s      r   Ú_find_screenshotrì   G  su   € ô œ×/Ñ/°²¸"Ó=Ó>ò ˆð	Ü�w‰w�~‰~˜dÔ#¬¯©×(8Ñ(8¸Ó(>À%È!Á)Ò(KØ’øðð øô ò 	Ùð	ús   §AA2Á2	A>Á=A>rÙ   c                 ó"  — 	 ddl m}m}m} ddlm}  |«       sy |t        |«      d |«       |d¬«      }t        j                  | d¬	«      }|d
z   }d||ddœd|dœdd|idœgdœS # t        $ r }	t        j                  d|	«       Y d}	~	yd}	~	ww xY w)z?Build a multimodal tool result attaching path for vision modelsr   )Ú_EMBED_MAX_DIMENSIONÚ_resize_image_for_visionÚ#_should_use_native_vision_fast_path)Úresolve_embed_target_bytesNz	image/pngT)Ú	mime_typeÚmax_base64_bytesÚmax_dimensionÚ
force_jpegF)Úensure_asciiuS   

The screenshot from this call is attached â€” inspect it with your native vision.)Úscreenshot_pathÚnative_visionrÄ   )ÚtyperÄ   Ú	image_urlÚurl)rù   rú   )Ú_multimodalÚtext_summaryÚmetaÚcontentz:Native screenshot attach failed (falling back to text): %s)Útools.vision_toolsrî   rï   rð   Ú!tools.vision_tools_history_budgetrñ   r   ÚjsonÚdumpsr   r   r   )
rÙ   r\   rî   rï   rð   rñ   Údata_urlrÄ   Úattachedr   s
             r   Ú_native_screenshot_resultr  S  s³   € ð÷	gñ 	gåPÙ2Ô4Øñ ,¬D°«JÀ+Ù=WÓ=YØ:NÐ[_ôaˆô �z‰z˜&¨uÔ5ˆØÐqÑqˆØ#°TÐW[ÐnrÑCsØ%+°XÑ>ÈÐdiÐksÐctÑ@uÐvñxð 	xøäò Ü�‰ÐQÐSTÔUÜûðús   ‚A% šA
A% Á%	BÁ.B	Â	Bc                  ó2   — ddl m} m}  | «       €dS  |«       S )u  ``""`` outside a served-profile scope (every legacy key stays byte-identical); under a
    multiplexed turn, the routed profile's home key â€” one profile's browser must never be handed
    to another that happens to use the same session name or task id (#110032).r   ©Úget_hermes_home_overrideÚhermes_home_keyr   )Úhermes_constantsr	  r
  r  s     r   Ú_served_profile_tagr  i  s   € ÷ KÙ)Ó+Ð3ˆ2ÐJ¹Ó9JÐJr%   Úsession_namec                 óF   — |rd|› �n| xs d}t        «       }|r|› d|› �S |S )zeSession-cache key for a backend browser: named sessions get their own; served profiles get their own.z	bu-named-zbrowser-exec-defaultú@)r  )rÜ   r  ÚkeyÚtags       r   Ú_backend_cache_keyr  q  s7   € á(4ˆI�l�^Ñ
$¸7Ò;\ÐF\€CÜ
Ó
€CÙ ˆcˆU�!�C�5ˆ>Ð) cÐ)r%   c                 óÔ   — 	 ddl m} ddlm}  |«       sy	 t        | |t        ||«      d„ d«      }|€	d| t        <   |S # t        $ r }t
        j                  d|«       Y d}~yd}~ww xY w)	a(  Point the harness at a Hermes-spawned ``lightpanda serve`` (``browser.engine: lightpanda`` and
    nothing of higher precedence claimed the session). Each cache key gets its own process via the
    legacy ``_get_session_info()`` (cache, reaper, atexit): private browser, own-tab preamble skipped.r   ©Ú_get_session_info)Ú_using_lightpanda_engineNz browser engine lookup failed: %sc                 ó   — d| › d�S )Nz!Lightpanda could not be started: un    Set browser.engine to auto to use local Chrome, or switch backends via `hermes tools` â†’ Browser Automation.r.   )r   s    r   r$   z)_resolve_lightpanda_cdp.<locals>.<lambda>†  s   € Ð6°q°cð :hð h€ r%   z\Lightpanda session returned no CDP endpoint. Set browser.engine to auto to use local Chrome.r½   )
Útools.browser_tool_sessionr  Ú&tools.browser_tool_lightpanda_fallbackr  r   r   r   rB   r  Ú_PRIVATE_BROWSER_SENTINEL)r0   rÜ   r  r  r  r   rI   s          r   Ú_resolve_lightpanda_cdpr  x  s{   € ðÝ@ÝSÙ'Ô)Øð *ô
 ØÐÔ 2°7¸LÓ Iñ	iàfó	€Cð €{Ø),ˆÔ%Ñ&Ø€Jøô ò Ü�‰Ð7¸Ô;Üûðús   ‚> ¾	A'ÁA"Á"A'c                 ó²  — 	 ddl m} ddlm}  |t        ||«      ddg |d¬	«      ¬
«      }|xs i j                  d«      r5t        |xs i j                  d«      xs i j                  d«      xs d«      nd}|sd|xs i j                  d«      xs d› d�S t        | |«       d| t        <   y# t        $ r }t
        j                  d|«       Y d}~yd}~ww xY w)uÌ  Point the harness at Hermes' packaged Chromium, launched through agent-browser for this cache key â€”
    the same browser the built-in tools drive. Left alone, the harness discovers the user's INSTALLED
    Chrome on its default profile, which needs the chrome://inspect toggle + an Allow popup per run and
    is blocked outright on Chrome >=136; on a headless box it just reports ``chrome-not-running``.
    ``get cdp-url`` runs through ``_run_browser_command`` (legacy cache, inactivity reaper, atexit, Chromium
    preflight/auto-install) on EVERY call: it launches the browser cold, follows a relaunch, and refreshes
    the agent-browser daemon's idle timer, which never sees the harness's direct CDP traffic.r   )Ú_run_browser_command)Ú_get_open_command_timeoutz+managed chromium resolution unavailable: %sNr9   zcdp-urlT)Ú
first_open©rÇ   ÚsuccessÚdataÚcdpUrlr   z(The local browser could not be started: rG   z&agent-browser returned no CDP endpointuW    Run `hermes tools` â†’ Browser Automation to (re)install Chromium, or switch backends.r½   )r  r  rK   r  r   r   r   r  r9   rA   r6   r  )r0   rÜ   r  r  r  r   Úresr1   s           r   Ú_resolve_managed_chromium_cdpr%  �  sã   € ðÝCÝ@ñ Ô1°'¸<ÓHÈ%ÐR[ÐQ\Ù'@ÈDÔ'QôS€CàGJÂyÈbÇoÁoÐV_ÔF`Œ#�’	�r�‰˜vÓ&Ò,¨"×1Ñ1°(Ó;ÒA¸rÔ
BÐfh€CÙØ:¸CºIÀ2¿?¹?È7Ó;SÒ;ÐWð  ;Að Aið ið 	jä��cÔØ%(€CÔ!Ñ"Øøô ò Ü�‰ÐBÀAÔFÜûðús   ‚B- Â-	CÂ6CÃCc                 óT   — t        | ||«      }|st        | «      r|S t        | ||«      S )z_Local engine (no provider / override): ``browser.engine: lightpanda`` or the packaged Chromium.)r  r:   r%  )r0   rÜ   r  rI   s       r   Ú_resolve_local_engine_cdpr'  ¨  s.   € ä
! # w°Ó
=€CÙ
Œl˜3ÔØˆ
Ü(¨¨g°|ÓDÐDr%   c           	      óH  ‡— t        | «      ry	 ddlm} ddlm} ddlm} t        |d«      }|rt        | |«       yt        |dd«      }|€t        | ||«      S t        t        |d	d«      xs d«      j                  «       j!                  «       }	|	t"        k(  rt%        t'        «       «      s
d
| t(        <   yt+        |«      j,                  Št/        | |t1        ||«      ˆfd„d‰› d�«      }
|
€|r	d
| t(        <   |
S # t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)u-  Point the harness at the configured backend's CDP endpoint; error string on failure.

    Precedence: (1) ``BU_CDP_WS``/``BU_CDP_URL`` already in env (operator override); (2) ``BROWSER_CDP_URL``
    env / ``browser.cdp_url`` (``/browser connect``); (3) a cloud provider via the legacy ``_get_session_info()``
    so browser_exec shares the SAME session machinery (per-task cache, expiry, reaper, atexit);
    (4) the local engine â€” ``browser.engine: lightpanda`` or Hermes' packaged Chromium via agent-browser
    (never the harness's own discovery of the user's installed Chrome); (5) BU direct-API configs â†’ None:
    the CLI reaches BU cloud natively (BU_AUTOSPAWN). ``session_name`` (BU_NAME) keys the session cache so
    each name gets its OWN browser â€” what makes named sessions concurrent-safe.
    Nr   ©Ú_get_cloud_providerr  )Ú_get_cdp_overridez/browser_tool backend resolution unavailable: %sr   zCloud provider lookup failedr   r½   c                 ó   •— d‰› d| › d�S )NúCloud browser provider z failed to provide a session: u^   . Fix the provider configuration or switch backends via `hermes tools` â†’ Browser Automation.r.   )r   Úprovider_names    €r   r$   z&_resolve_backend_cdp.<locals>.<lambda>Ø  s"   ø€ Ð,¨]¨OÐ;YÐZ[ÐY\ð ]rð r€ r%   r-  zw returned no CDP endpoint, so Browser Use mode cannot drive it. Switch to the built-in browser tools for this provider.)r:   Útools.browser_tool_cloudr*  r  r  Útools.browser_tool_cdpr+  r   r   r   r   r6   r'  rA   r!   r}   r~   rŒ   r   rv   r  rù   Ú__name__rB   r  )r0   rÜ   r  r*  r  r+  r   Úoverrider‰   Úprovider_keyrI   r.  s              @r   Ú_resolve_backend_cdpr4  °  s/  ø€ ô �CÔØðÝ@Ý@Ý<ô Ð'¨Ó,€HÙÜ�S˜(Ô#ØÜÐ)¨4Ð1OÓP€HØÐÜ(¨¨g°|ÓDÐDô ”w˜x¨°Ó4Ò:¸Ó;×AÑAÓC×IÑIÓK€LØ”|Ò#¬LÔ9JÓ9LÔ,MØ),ˆÔ%Ñ&Øä˜“N×+Ñ+€MÜ
ØÐÔ 2°7¸LÓ Ió	sà
! - ð 1Sð 	Só	€Cð €{‘|Ø),ˆÔ%Ñ&Ø€JøôA ò Ü�‰ÐFÈÔJÜûðús   �C8 Ã8	D!ÄDÄD!Úforce_localc                 óZ  — t        «       rt        | «      ry	 ddlm} ddlm} ddlm} t        |d«      ry|s(t        |t        «       «      €t        t        «       «      ry |«       \  }}|r|st        | |«       |xs dS # t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)uJ  Point the harness at the user's real-profile copy-browser (a SNAPSHOT of their default Chromium
    profile, hermes_cli.browser_connect) when consented. Two ways in: the effective backend is already local
    (no provider, CDP override, or legacy BU cloud config) â†’ silent upgrade; or ``force_local`` (consent-gated
    ``local`` arg) â†’ the user's browser even under a cloud backend. Operator overrides (BU_CDP_* env,
    /browser connect, ``browser.cdp_url``) own the session either way. Fail closed: a launch error is
    returned so a consented user is never silently downgraded.Nr   )Ú_get_cdp_override_rawr)  )Ú_real_profile_cdpz/real-profile backend resolution unavailable: %sr   )r/   r:   r0  r7  r/  r*  Útools.browser_tool_real_profiler8  r   r   r   r   ÚobjectrŠ   rv   r6   )r0   r5  r7  r*  r8  r   r1   rI   s           r   Ú_resolve_real_profile_cdpr;  ä  sž   € ô #Ô$¬°SÔ(9ØðÝ@Ý@ÝEô Ð# RÔ(Øñ œFÐ#6¼»ÓAÐMÜAÔBSÓBUÔVØÙ Ó"�H€CˆÙ
‘3Ü�S˜#ÔØŠ;�$Ðøô ò Ü�‰ÐFÈÔJÜûðús   ˜B Â	B*Â
B%Â%B*c                 ó  — | j                  d«      xs | j                  d«      }|sy	 ddlm} ddlm}m}  |«       \  }}|j                  |xs d ||«      ||¬«       y# t        $ r }t        j                  d	|«       Y d}~yd}~ww xY w)
a1  Attach the per-task CDP supervisor to the browser this exec drives so ``browser_vault_fill`` has
    a secret-capable WebSocket (never argv) into the SAME browser. Only CDP-routed backends expose an
    endpoint; BU direct-cloud (BU_AUTOSPAWN) does not, and the vault tools report ``supervisor_required``.r4   r3   Nr   )ÚSUPERVISOR_REGISTRY)Ú_get_dialog_policy_configÚ_resolve_cdp_overrider   )rÜ   r@   Údialog_policyÚdialog_timeout_sz:browser_exec: CDP supervisor attach failed (non-fatal): %s)
r9   Útools.browser_supervisorr=  r0  r>  r?  Úget_or_startr   r   r   )	r0   rÜ   r1   r=  r>  r?  Úpolicyr´   Úexcs	            r   Ú_attach_vault_supervisorrF    sŽ   € ð �'‰'�+Ó
Ò
7 #§'¡'¨,Ó"7€CÙØðXÝ@ß[Ù5Ó7Ñˆ�	Ø×(Ñ(°Ò1E¸IÑOdÐehÓOiØ7=ÐPYð 	)õ 	[øäò XÜ�‰ÐQÐSV×WÑWûðXús   ©7A! Á!	B
Á*BÂB
ÚsessionÚlocalc                 óp   — t        | |¬«      }|r|S |rt        | «      st        «       syt        | ||¬«      S )as  Resolve where the harness connects; returns an error string or None. Real-profile consent runs
    BEFORE provider resolution so a hit short-circuits the cloud path via the BU_CDP_* env contract. Named
    sessions compose with the backend: BU_NAME namespaces the harness daemon (IPC socket, log, pid) and on
    provider backends additionally keys its own cloud browser.)r5  u°   local=true was requested but browser.use_real_profile is off. Enable it in config.yaml (browser.use_real_profile: true) or the desktop Settings â†’ Browser section, then retry.)r  )r;  r:   r/   r4  )r0   rG  rÜ   rH  Úrp_errs        r   Ú_route_backendrK    s=   € ô
 ' s¸Ô>€FÙØˆá”\ #Ô&Ô/FÔ/Hðlä  W¸7ÔCÐCr%   c                  ó^   — dt         fd„} t        j                  dk(  rt        | i d«      S ddiS )a  Popen kwargs starting the CLI in its own process group (a new session on POSIX) so a
    timeout can take down every process that inherited the capture pipes, not just the CLI
    child. Windows also hides the console the .cmd shim would flash (as browser_tool does).r   c                  ó´   — ddl m}  t        j                  «       }|xj                  t        j
                  z  c_         | «       t        t        dd«      z  |dœS )Nr   ©Úwindows_hide_flagsÚCREATE_NEW_PROCESS_GROUP)ÚcreationflagsÚstartupinfo)Úhermes_cli._subprocess_compatrO  rÍ   ÚSTARTUPINFOÚdwFlagsÚSTARTF_USESHOWWINDOWr!   )rO  Úsis     r   Ú_flagsz#_group_popen_kwargs.<locals>._flags%  sJ   € ÝDÜ×#Ñ#Ó%ˆØ
�
Š
”j×5Ñ5Ñ5�
Ù!3Ó!5¼Ä
ÐLfÐhiÓ8jÑ!jØ!ñ#ð 	#r%   r^   zWindows hide-flags unavailableÚstart_new_sessionT)rt   r`   r   r   )rX  s    r   Ú_group_popen_kwargsrZ  !  s8   € ð#”Dó #ô DFÇ7Á7ÈdÂ?Œ6�&˜"Ð>Ó?ÐsÐYlÐnrÐXsÐsr%   c                 óˆ   — 	 t        t        t        t        | «      t        «      «      S # t
        t        f$ r	 t        cY S w xY wr    )ÚmaxÚ_MIN_TIMEOUT_SÚminÚintÚ_MAX_TIMEOUT_SÚ	TypeErrorÚ
ValueErrorÚ_DEFAULT_TIMEOUT_S)r´   s    r   Ú_clamp_timeoutrd  .  s9   € ð"Ü”>¤3¤s¨9£~´~Ó#FÓGÐGøÜ”zÐ"ò "Ü!Ò!ð"ús   ‚&) ©AÁ Ag      $@c                 ó  — t         j                  dk(  r�ddlm} t	        j
                  t        t        j                  «      5  t        j                  ddddt        | j                  «      gt        j                  ddd	d
dd |«       ¬«	       ddd«       yt	        j
                  t        t        «      5  t        j                  | j                  t         j"                  «       ddd«       y# 1 sw Y   yxY w# 1 sw Y   yxY w)u¹   SIGKILL the CLI's whole process group (POSIX; ``start_new_session`` made pgid == pid) or,
    on Windows, its process tree via ``taskkill /T /F`` â€” the only group-wide kill it offers.r^   r   rN  Útaskkillz/Tz/Fz/PIDTrÁ   rÂ   é
   F)rÈ   rÃ   rÄ   rÅ   rÆ   rÇ   ÚcheckrQ  N)r`   r   rS  rO  ra   rb   r”   rÍ   ÚSubprocessErrorrÎ   rA   ÚpidrÏ   ÚProcessLookupErrorÚPermissionErrorÚkillpgÚsignalÚSIGKILL)ÚprocrO  s     r   Ú_kill_cli_process_grouprq  :  sÇ   € ô 
‡w�w�$‚ÝDÜ× Ñ ¤¬*×*DÑ*DÓEñ 	LÜ�N‰N˜J¨¨d°F¼CÀÇÁ»MÐJÔR\×RdÑRdØ*.°TÀGÐT]ÐgiØ!&Ñ6HÓ6JõL÷	Lð 	Ü	×	Ñ	Ô/´Ó	Añ ,Ü
�	‰	�$—(‘(œFŸN™NÔ+÷,ð ,÷	Lð 	ú÷,ð ,ús   ÁA
C+Â3/C7Ã+C4Ã7D c                 óö  — t        j                  | ft         j                  t         j                  t         j                  ddd|dœt        «       ¤Ž}	 |j	                  ||¬«      \  }}t        j                  | |j                  ||«      S # t         j
                  $ r[ t        |«       t        j                  t         j
                  «      5  |j	                  t        ¬«       ddd«       ‚ # 1 sw Y   ‚ xY ww xY w)uÈ  Run the CLI in its own process group and kill the whole group on timeout.

    ``subprocess.run`` only kills the direct child on ``TimeoutExpired``; a grandchild that
    inherited the stdout/stderr pipes (browser_harness daemon / Chrome helper) is orphaned
    still holding them, and on Windows ``run()``'s unbounded post-kill ``communicate()`` then
    blocks on pipe EOF forever â€” so the tool call, plus its activity heartbeat, wedges (#106244).
    TrÁ   rÂ   )rÈ   rÓ   rÒ   rÄ   rÅ   rÆ   r0   )ÚinputrÇ   r   N)rÍ   ÚPopenÚPIPErZ  ÚcommunicaterÐ   rq  ra   rb   Ú_POST_KILL_DRAIN_SÚCompletedProcessrÑ   )ÚcmdrC   r0   rÇ   rp  rÓ   rÒ   s          r   Ú_run_cli_killing_process_grouprz  H  sÙ   € ô ×ÑØðÜ—?‘?¬:¯?©?Ä:Ç?Á?Ø˜G¨I¸3ñäBUÓBWñ€DðØ×)Ñ)°¸gÐ)ÓF‰ˆ�ô ×&Ñ& s¨D¯O©O¸VÀVÓLÐLøô ×$Ñ$ò Ü Ô%Ü× Ñ ¤×!:Ñ!:Ó;ñ 	9Ø×ÑÔ%7ÐÔ8÷	9à÷	9àúð	ús%   ÁB
 Â
AC8ÃC+Ã"	C8Ã+C4	Ã0C8Fc                 ój  — ddl m} ddlm}m} | r| j                  «       s |d«      S t        | «      }|r ||«      S t        «       }	|	s |d«      S t        «       }
|r&t        j                  |«      s |d|›d�«      S ||
d<   t        |
||t        |«      «      }|r ||«      S t        |
|«       |
j                  t        d	«      }|r|s	t         | z   } t#        |«      }|r||
d
<   d|
vrt%        t'        «       «      rd|
d<   t)        |«      }t+        j*                  «       }	 t-        |	| |
|«      }|j6                  dk(  |j6                   ||j8                  d¬«      dœ}|r||d<   |r||d<    ||j:                  xs dj                  «       d¬«      }t=        |«      t>        kD  r|d	t>         dz   }|r||d<   tA        |j8                  |«      }|r||d<   tC        ||«      }|�|S  ||«      S # t.        j0                  $ r  |d|› dt2        › d�«      cY S t4        $ r} |d|› �«      cY d	}~S d	}~ww xY w)zBRun Python code through the browser-use CLI, and return its outputr   )Úredact_sensitive_text)Ú
tool_errorÚtool_resultz~No code provided. Pass Python that uses the pre-imported helpers, e.g. new_tab("https://example.com") then print(page_info()).zÕbrowser-use CLI not found on PATH, and uvx is unavailable for a zero-install run. Install it with `uv tool install browser-use` (or `pipx install browser-use`), then run `browser-use --doctor` to verify the setup.zInvalid session name zA: use 1-64 letters, digits, dashes, or underscores (e.g. 'r7k2').ÚBU_NAMENrÞ   ÚBU_AUTOSPAWNr½   z!browser-use exec timed out after zGs. The daemon may still be working; retry with a larger timeout_s (max u‚   ), or split the work into several calls that append to workspace files â€” anything already written to the workspace is preserved.z"Failed to launch browser-use CLI: T)Úforce)r!  Ú	exit_codeÚoutputrá   rG  r   u   
â€¦ (stderr truncated)rÒ   r÷   )"Úagent.redactr|  Útools.registryr}  r~  r}   rP   r�   r[   Ú_SESSION_REÚmatchrK  r8   rF  rX   r  Ú_OWN_TAB_PREAMBLErå   rŠ   rv   rd  r•   rz  rÍ   rÐ   r`  r”   rÑ   rÓ   rÒ   rÕ   Ú_STDERR_CAP_CHARSrì   r  )rC   rG  r´   rÜ   rH  r|  r}  r~  Úblockedry  r0   Ú	route_errÚprivate_browserrá   rÇ   Ústartedrp  r   rÙ   rÒ   Ú
screenshotÚnatives                         r   Úbrowser_execr�  ^  s’  € õ 3ß6Ù�t—z‘z”|Ùð  ]ó  ^ð  	^ä" 4Ó(€GÙÙ˜'Ó"Ð"ä
‹+€CÙÙð Qó Rð 	Rô Ó
 €CÙÜ× Ñ  Ô)ÙÐ 5°g°[ð AFð Fó Gð Gà ˆˆI‰Ü˜s G¨W´d¸5³kÓB€IÙÙ˜)Ó$Ð$Ü˜S 'Ô*ð —g‘gÔ7¸Ó>€OÙ‘Ü  4Ñ'ˆä˜wÓ'€IÙØ$-ˆÐ Ñ!ð ˜SÑ Ô%GÔHYÓH[Ô%\Ø!ˆˆNÑä˜YÓ'€GÜ�i‰i‹k€GðDÜ-¨c°4¸¸gÓFˆð —?‘? aÑ'Ø—_‘_Ù'¨¯©¸4Ô@ñ€Fñ
 Ø'ˆˆ{ÑÙØ#ˆˆyÑÙ" D§K¡KÒ$5°2×#<Ñ#<Ó#>ÀdÔK€FÜ
ˆ6ƒ{Ô&Ò&ØÐ*Ô*Ð+Ð.HÑHˆÙØ!ˆˆxÑÜ! $§+¡+¨wÓ7€JÙØ$.ˆÐ Ñ!Ü*¨6°:Ó>ˆØÐØˆMÙ�vÓÐøô; ×$Ñ$ò sÙÐ=¸g¸Yð G:Ü:HÐ9Ið Jrðró sò 	sô ò DÙÐ>¸q¸cÐBÓCÕCûðDús$   Ä"G- Ç-&H2ÈH2È
H-È'H2È-H2u|  Drive a real web browser via the Browser Use CLI: `code` runs as full Python (stdlib available) with pre-imported browser helpers; stdout comes back in the result. Start `code` with a one-line comment describing the step for the user in plain language, max 60 chars (e.g. `# Searching Amazon for paper towels`) â€” the UI shows it as the step label.

STATE: the browser session and workspace persist across calls; Python variables do NOT (fresh interpreter each call). The workspace dir is $BH_AGENT_WORKSPACE (also `workspace` in every result); functions defined in agent_helpers.py there are auto-imported into every call. For multi-item tasks ('all N products / every entry'), append each batch to a JSON/CSV file in the workspace, then read it back and aggregate in code â€” dedupe/count/sort with Python, not in your head â€” and verify the collected count against what was asked before answering.

Batch each sub-procedure (navigate, wait, extract, act) into one call â€” do not spend a call per action â€” but for long extractions prefer several medium calls that append to workspace files over one giant call, so progress survives timeouts.u   Screenshots are attached to your context automatically: when the exec output contains a capture_screenshot() path, the image arrives with this tool's result and you inspect it directly with your own vision â€” never send browser screenshots to a separate vision tool.u   Your model cannot view images, so work text-first: page_info() for state, js() for reading/extracting DOM text, fill_input(selector, text) for inputs, and js("document.querySelector('â€¦').click()") for clicks â€” skip the screenshot-driven workflow described below.u   The local engine is Lightpanda (no graphical renderer, one page per session): capture_screenshot() is unavailable, so work text-first; navigate with new_tab(url) exactly once, then goto_url(url) for every later navigation â€” a second new_tab() fails with TargetAlreadyLoaded.uÖ  

HELPERS (pre-imported): new_tab(url) opens/navigates (use for the FIRST navigation), goto_url(url) navigates the current tab, wait_for_load() after navigation, page_info() summarizes the current page state, js(expr) evaluates a JS expression and returns its value (js('document.title'); wrap function bodies as js('(() => {...})()') â€” a bare '() => {...}' returns the function itself, uncalled), fill_input(selector, text) types into inputs, click_at_xy(x, y) clicks viewport coordinates, capture_screenshot() saves and prints a screenshot path, cdp('Domain.method', **kwargs) is raw CDP â€” cdp('Accessibility.getFullAXTree')['nodes'] lists every element's role/name/backendDOMNodeId (filter in Python before printing; it is thousands of nodes), then cdp('DOM.getBoxModel', backendNodeId=n) gives click coordinates. ensure_real_tab() recovers from a stale/internal tab. Login walls: never guess credentials; see the vault note below if present, otherwise stop and ask the user.c                  ó–   — t        dddd«      d   rt        t        z   t        z   S t        dddd	«      } t        | rt        z   S t        z   S )
zCHeader tailored to whether the active model can see images nativelyz&tools.browser_tool_lightpanda_fallbackÚlightpanda_engine_status)Fr   z$lightpanda engine status unavailabler   ztools.vision_toolsrð   Fr   )r&   Ú_HEADER_BASEÚ_HEADER_TEXT_ONLYÚ_HEADER_LIGHTPANDAÚ_HEADER_VISION)Úvisions    r   Ú_description_headerr˜  ä  sZ   € äÐ:Ð<VÐXcØ8ó:Ø:;ò=äÔ/Ñ/Ô2DÑDÐDÜÐ,Ð.SÐUZÐ\^Ó_€FÜ©Vœ>ÑKÐKÔ9JÑKÐKr%   c                  ó    — dt        «       t        z   i} t        «       r0t        t        d   d   «      }ddddœ|d<   i t        d   ¥d|i¥| d<   | S )	NÚdescriptionÚ
parametersÚ
propertiesÚbooleanFu3  Drive the user's own local browser (a Hermes-managed copy of their real default-Chromium profile, logins/cookies included) instead of the configured cloud browser backend. Use when the user asks to act as themselves â€” their accounts, their sessions. No-op when the backend is already local. Default false.©rù   r   rš  rH  )r˜  Ú_HELPERS_DIGESTr/   rt   ÚBROWSER_EXEC_SCHEMA)Ú	overridesÚpropss     r   Ú_dynamic_schema_overridesr£  í  sn   € Ø$Ô&9Ó&;¼oÑ&MÐN€Iô Ô ÜÔ(¨Ñ6°|ÑDÓEˆà¨%ðpñ
ˆˆg‰ð #]Ô%8¸Ñ%FÐ"\ÈÐV[Ñ"\ˆ	�,ÑØÐr%   r�  z\

(The browser-use CLI is not installed yet. Install it with `uv tool install browser-use`.)r:  ÚstringziPython code to execute using the pre-imported browser helpers. Use print(...) for any data you need back.)rù   rš  uÌ   Named isolated browser session â€” its own daemon and (on cloud backends) own browser, so concurrent tasks don't share tabs. Reuse the same name on every related call; omit for the shared default session.Úintegerz4Max seconds to wait for the code to finish (default z, max z).rž  )rC   rG  r´   )rù   rœ  Úrequired)r   rš  r›  )Úregistryc                 óâ   — t        | j                  dd«      | j                  dd«      xs d| j                  dt        «      |j                  d«      t        | j                  dd«      «      ¬«      S )	NrC   r   rG  r´   rÜ   rH  F)rC   rG  r´   rÜ   rH  )r�  r9   rc  r8   )ÚargsÚkws     r   r$   r$     sX   € œ|Ø�X‰X�f˜bÓ!¨4¯8©8°I¸rÓ+BÒ+HÀbØ—(‘(˜;Ô(:Ó;ÀRÇVÁVÈIÓEVÜ�4—8‘8˜G UÓ+Ó,ô € r%   u   ðŸŒ�)r   ÚtoolsetÚschemaÚhandlerÚcheck_fnÚdynamic_schema_overridesÚemoji)r   )iX  )r   N)`Ú__doc__ra   r"   r  Úloggingr`   Úrer¬   rn  rÍ   r•   Úpathlibr   Útypingr   r   r   r   r   r	   r  r
   Úutilsr   Ú	getLoggerr1  r   rŒ   r‚   Úcompiler†  r  rˆ  rc  r]  r`  r‰  râ   Ú
IGNORECASEré   rN   rg   rA   r   r&   r8   r+   r/   rt   r6   r:   r   rB   rP   r[   rY   rv   r   r„   rŠ   rŽ   rœ   rŸ   r�   r_  rÛ   rå   Úfloatrì   r  r  r  r  r%  r'  r4  r;  rF  rK  rZ  rd  rw  rq  rz  r�  r“  r–  r”  r•  rŸ  r˜  r£  r   r…  r§  Úregisterr.   r%   r   ú<module>r¼     sò  ðñó Û Û Û Û 	Û 	Û Û Û Û Ý ß =× =å ,Ý !à	ˆ×	Ñ	˜8Ó	$€à€ØÐ ð ˆb�j‰jÐ<Ó=€ð 9Ð ð!Ð ðF Ð Ø€Ø€ØÐ à�2—:‘:Ð1Ó2Ð à�—‘ÐSÐUW×UbÑUbÓc€à
ˆ"�*‰*Ð-¨r¯}©}Ó
=€Ø_€ñˆx˜˜C˜Ñ ð ¨3ð ¸Cð Èó ða�sð a #ð a°ð aÀð aÈó añt˜Sð t¨$ó tðt ó tðX�dð X ð X¨ó Xð
?�dð ?˜tó ?ð
˜Tð 
°X¸s¸eÀS¸jÑ5Ið 
ÐVYð 
Ø"*¨I¨;¸Ð+;Ñ"<ð
ØJMð
ØRZÐ[^ÑR_ó
ðI˜sð I x°¡}ó Ið˜dó ð"k ð k¨ó kð˜4ó ða˜dð a tó aðs˜Só sð7°Dð 7¸Tó 7ðH ó Hð (¨3¡-ó ð(0˜#ó 0ð
�8˜D ™IÑ&ó ñ&';˜3ð ';¨¨t°S¨yÑ)9ó ';ðT˜H S™Mð ¨h°s©mó ð	˜Sð 	¨ð 	°8¸C±=ó 	ð d¨3°¨8¡nð ¸Cð ÀHÈTÐRUÐWZÐRZÉ^ÑD\ó ð,K˜Só Kñ* ¨¡ð *¸Sð *È#ó *ñ ð °¸±ð ÈSð ÐZbÐcfÑZgó ñ. tð °h¸s±mð ÐSVð Ð`hÐilÑ`mó ñ2E 4ð E°(¸3±-ð EÈsð EÐ\dÐehÑ\ió Eñ1˜dð 1¨X°c©]ð 1È#ð 1ÐW_Ð`cÑWdó 1ðh 4ð °dð ¸xÈ¹}ó ð:X $ð X°¸#±ð XÀ4ó Xð"D˜ð D sð D°X¸c±]ð DÈ4ð DÐT\Ð]`ÑTaó Dð
t˜Tó 
tð"˜cð " có "ð Ð ó,òMð, ,.Ð@RØ*.¸eñM�sð M Sð M¸#ð MØ" 3™-ðMØ7;óMðb5ð ð"Yð ðxð ðTð ð	Yð ðL˜Só Lð 4ó ð& à  ?Ñ2Øvñwð à%ð  7bñ  cØ (ð  :Hñ  IØ"+Ð8JØ+_Ð`rÐ_sÐsyð  {Ið  zJð  JLð  *MñNñ
ð �Hñ	ñÐ õ* $à €× Ñ Ø	ØØñð
 %Ø6Ø
ör%   