Ë
    ãß²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m	Z	 ddl
mZmZmZmZ ddlmZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ  ddlm!Z" dZ#dZ$de#› �Z%de$› �Z&de'fd„Z(dee)e)f   ddfd„Z*de)de)de+e)e)f   fd„Z,de)ddfd„Z-de)de.de)de)de)f
d„Z/d e)de0fd!„Z1d"e)de)d#ee)   dd$fd%„Z2d&ede)dee)ef   fd'„Z3d(e)de)fd)„Z4d*e)dee)e)f   fd+„Z5	 dTd,ee)   d-ee)e)f   d*e)d.e)d/ee6   dd0fd1„Z7d2e)d3ee)   d4ee)ef   dee)ef   fd5„Z8dUd6e)d7e'dee)e)f   fd8„Z9d6e)dee)ef   fd9„Z:d:ee)ef   de'fd;„Z;d6e)d3e)dee)e)f   fd<„Z<d6e)dee)ef   fd=„Z=d6e)d>e'dee)ef   fd?„Z>dTd6ee)   dee)ef   fd@„Z?d6e)d:ee)ef   dAe)ddfdB„Z@dAe)d(e)dee.   fdC„ZAdVdAe)dDeBde'fdE„ZCd6e)d:ee)ef   dAe)ddfdF„ZDd6e)d:ee)ef   dAe)dGe)ddf
dH„ZEd:ee)ef   d&ee)ef   de'fdI„ZFde)de)de)dJe.dee)ef   f
dK„ZGdee)ef   fdL„ZH	 dTd6e)d:ee)ef   dMee)   de)dNe)de.d/ee6   dee)ef   fdO„ZId6e)d:ee)ef   d e)de)d#ee)   de.dPee)   ddQfdR„ZJ	 	 	 dWd6e)de)d#ee)   dee.   dPee)   dee)ef   fdS„ZKy)Xu:  agent-browser session management: daemon spawn, per-backend session creation
(local/lightpanda/cdp/cloud), cached lookup, command execution + output interpretation.

Split out of ``tools/browser_tool.py``. Facade-owned state is read through ``_bt`` (``tools.browser_tool``, resolved per call) â€” no import cycle.
é    N)ÚPath)ÚAnyÚDictÚListÚOptional)Úwindows_hide_flags)Úorigin)Úbrowser_tool_cdp)Úbrowser_tool_cloud)Úbrowser_tool_install)Úbrowser_tool_lifecycle)Ú browser_tool_lightpanda_fallback)Úbrowser_tool_real_profile)Úbrowser_tool_snapshotz4docker pull ghcr.io/nousresearch/hermes-agent:latestzWnpx agent-browser install --with-deps (or: npx playwright install --with-deps chromium)um   Chromium browser is missing. You're running in Docker â€” pull the latest image to get the bundled Chromium: z.Chromium browser is missing. Install it with: Úreturnc                  ó$  — t        t        d«      rt        j                  «       dk(  ryt        j                  «       ry	 t        dd¬«      5 } | j                  «       j                  «       dk(  cddd«       S # 1 sw Y   yxY w# t        $ r Y y	w xY w)
zXTrue when Chromium needs --no-sandbox to start reliably (root, Docker, AppArmor userns).Úgeteuidr   Tz6/proc/sys/kernel/apparmor_restrict_unprivileged_usernsúutf-8©ÚencodingÚ1NF)	ÚhasattrÚosr   Ú_installÚ_running_in_dockerÚopenÚreadÚstripÚOSError)Úfs    ú?/home/ubuntu/.hermes/hermes-agent/tools/browser_tool_session.pyÚ_needs_chromium_sandbox_bypassr"   "   sz   € äŒr�9Ô¤"§*¡*£,°!Ò"3ØÜ×"Ñ"Ô$ØðÜÐJÐU\Ô]ð 	+ÐabØ—6‘6“8—>‘>Ó# sÑ*÷	+÷ 	+ò 	+ûäò Ùðús/   ¿B Á!A7Á-	B Á7B Á<B Â B Â	BÂBÚbrowser_envc                 óv   — d| vr5d| vr0t        «       r%t        j                  j                  d«       d| d<   yyyy)zEAdd required Chromium sandbox flags without overriding user settings.ÚAGENT_BROWSER_ARGSÚAGENT_BROWSER_CHROME_FLAGSuW   browser: sandbox bypass needed (root/docker/AppArmor userns) â€” injecting --no-sandboxz$--no-sandbox,--disable-dev-shm-usageN)r"   Ú_btÚloggerÚdebug)r#   s    r!   Ú_apply_chromium_sandbox_argsr*   /   sE   € à KÑ/Ð4PÐXcÑ4cÜ.Ô0Ü�
‰
×ÑÐrÔsØ,RˆÐ(Ò)ð 1ð 5dÐ/ó    Ústdout_pathÚstderr_pathc                 ó  — g }| |fD ]G  }	 t        |dd¬«      5 }|j                  |j                  «       j                  «       «       ddd«       ŒI |d   |d   fS # 1 sw Y   ŒxY w# t        $ r |j                  d«       Y Œ|w xY w)z;Best-effort read of agent-browser stdout/stderr temp files.Úrr   r   NÚ r   é   )r   Úappendr   r   r   )r,   r-   ÚoutÚpathr    s        r!   Ú_read_command_output_filesr5   7   sŠ   € à
€CØ˜kÐ*ò ˆð	Ü�d˜C¨'Ô2ð -°aØ—
‘
˜1Ÿ6™6›8Ÿ>™>Ó+Ô,÷-øðð ˆq‰6�3�q‘6ˆ>Ð÷	-ð -ûäò 	Ø�J‰J�rŽNð	ús(   ‹A'™.AÁA'ÁA$	Á A'Á'BÂBÚpathsc                  ó\   — | D ]  }	 t        j                  |«       Œ y # t        $ r Y Œ'w xY w©N)r   Úunlinkr   )r6   r4   s     r!   Ú_unlink_command_output_filesr:   C   s5   € Øò ˆð	Ü�I‰I�d�Oñøô ò 	Ùð	ús   ‡Ÿ	+ª+ÚcommandÚtimeoutÚstdoutÚstderrc                 óœ  — d|› d�g}|xs |xs dj                  «       }|r|j                  |dd «       d|› d|› �j                  «       v r|j                  d«       n^| d	k(  rYt        j                  «       rEt        j                  «       r|j                  d
t        › �«       n|j                  dt        › �«       dj                  |«      S )z7Actionable timeout message from captured daemon output.zCommand timed out after z secondsr0   NiÜ  ÚsandboxÚ
z Chromium sandbox launch failed. Set AGENT_BROWSER_ARGS='--no-sandbox,--disable-dev-shm-usage' in your environment, or run: npx agent-browser install --with-depsr   z\The browser daemon may still be starting or Chromium may be missing. Pull the latest image: zlThe browser daemon may still be starting, or Chromium may be missing system libraries. Install/repair with: )
r   r2   ÚlowerÚ_cloudÚ_is_local_moder   r   Ú_DOCKER_PULLÚ_CHROMIUM_INSTALLÚjoin)r;   r<   r=   r>   ÚpartsÚdetails         r!   Ú_format_browser_timeout_errorrJ   K   sß   € ð (¨ y°Ð9Ð:€EØÒ$˜Ò$ "×+Ñ+Ó-€FÙØ�‰�V˜E˜T�]Ô#à�v�h˜b  Ð)×/Ñ/Ó1Ñ1Ø�‰ð Eõ 	Fð 
�FÒ	œv×4Ñ4Ô6Ü×&Ñ&Ô(Ø�L‰Lð <Ü<H¸>ðKõ Lð �L‰Lð KÜK\ÐJ]ð_ô `à�9‰9�UÓÐr+   Úbrowser_cmdc                 óŒ   — t        j                  | «      r-t        j                  «       xs d}|dddt        j                  gS | gS )u¼  Command prefix to invoke agent-browser (concrete binary, or the npx sentinel expanded).

    npx is resolved through the same PATH cascade as ``_find_agent_browser`` (a bare
    ``which("npx")`` would let a broken system npx shadow a healthy managed one); if
    absent the bare name gives a readable ``FileNotFoundError``. ``--ignore-scripts``:
    the spec is a floating range â€” a compromised future patch must not run install scripts.
    Únpxz--ignore-scriptsz--prefer-offlinez-y)r   Ú_is_npx_agent_browser_sentinelÚ_resolve_npx_binr'   ÚAGENT_BROWSER_NPX_SPEC)rK   Ú_npx_bins     r!   Ú_agent_browser_argvrR   b   sF   € ô ×.Ñ.¨{Ô;Ü×,Ñ,Ó.Ò7°%ˆØÐ,Ð.@À$Ì×HbÑHbÐcÐcØˆ=Ðr+   Úargv0Úargsz&tuple[str, List[str], Optional[bytes]]c                 ób  — |r| j                  «       j                  d«      s||dfS |dk(  r>|^}}|dt        j                  |j	                  d«      «      j                  d«      g|¢dfS |dk(  st        d„ |D «       «      s||dfS dg t        j                  |g|¢g«      j	                  d«      fS )	u(  ``(spawn_command, spawn_args, stdin_payload)`` for one CLI command. Arguments that reach a
    ``.cmd``/``.bat`` shim (``npx.cmd``, npm's ``agent-browser.cmd`` on Windows) go through cmd.exe,
    which re-parses the child command line: a newline ends the argument and ``%VAR%`` expands even
    inside quotes, so a multi-line ``eval`` script arrives as its first line only (``SyntaxError:
    Unexpected end of input``) and multi-line ``fill`` text is truncated the same way. ``eval``
    scripts are sent base64-encoded (``agent-browser eval -b``); any other command whose argv carries
    a newline or ``%`` is wrapped as ``batch`` with the command as a JSON array on stdin â€” cmd.exe
    never sees the text (both forms present since the 0.26 floor). Every other spawn gets the raw
    argv and no stdin.)z.cmdz.batNÚevalz--base64r   ÚasciiÚbatchc              3   ó2   K  — | ]  }d D ]  }||v –— Œ
 Œ y­w)z
%N© )Ú.0ÚargÚchs      r!   ú	<genexpr>z"_shim_safe_args.<locals>.<genexpr>   s    è ø€ Ò$Q°3ÈÒ$QÀ2 R¨3¤YÐ$Q YÑ$Qùs   ‚)	rB   ÚendswithÚbase64Ú	b64encodeÚencodeÚdecodeÚanyÚjsonÚdumps)rS   r;   rT   ÚscriptÚrests        r!   Ú_shim_safe_argsri   p   sÀ   € ñ �u—{‘{“}×-Ñ-Ð.>Ô?Ø˜˜dÐ"Ð"Ø�&ÒØˆˆ�Ø˜¤V×%5Ñ%5°f·m±mÀGÓ6LÓ%M×%TÑ%TÐU\Ó%]ÐeÐ`dÐeÐgkÐkÐkØ�'Ò¤Ñ$Q¸$Ô$QÔ!QØ˜˜dÐ"Ð"Ø�BœŸ
™
 WÐ$4¨tÐ$4Ð#5Ó6×=Ñ=¸gÓFÐFÐFr+   Úresultc                 ó,  — t        | t        «      s| S t        | «      dk7  st        | d   t        «      s!dd|› dt	        j
                  | «      dd › �dœS | d   }t        |j                  d	«      «      |j                  d
«      |j                  d«      dœS )z½``batch --json`` prints ``[{command, success, result, error}]``; reshape the single entry
    into the ``{success, data, error}`` dict every other command returns. Error dicts pass through.r1   r   FzUnexpected batch output for 'ú': Né,  ©ÚsuccessÚerrorro   rj   rp   )ro   Údatarp   )Ú
isinstanceÚlistÚlenÚdictre   rf   ÚboolÚget)rj   r;   Úentrys      r!   Ú_unwrap_batch_resultry   „   s—   € ô �fœdÔ#ØˆÜ
ˆ6ƒ{�aÒœz¨&°©)´TÔ:Ø Ð-JÈ7È)ÐSVÔW[×WaÑWaÐbhÓWiÐjnÐknÐWoÐVpÐ+qÑrÐrØ�1‰I€EÜ˜EŸI™I iÓ0Ó1¸5¿9¹9ÀXÓ;NÐY^×YbÑYbÐcjÓYkÑlÐlr+   Úsession_namec                 óÌ   — t         j                  j                  t        j                  «       d| › �«      }t        j
                  |dd¬«       t        j                  || «       |S )uæ   Create the per-session socket dir (parallel workers must not share one) and claim it
    with our PID BEFORE first use â€” another hermes process's orphan reaper rmtree's any
    ownerless agent-browser-* dir in the shared tmpdir.zagent-browser-iÀ  T)ÚmodeÚexist_ok)r   r4   rG   r'   Ú_socket_safe_tmpdirÚmakedirsÚ
_lifecycleÚ_write_owner_pid)rz   Ú
socket_dirs     r!   Ú_prepare_session_socket_dirrƒ   �   sN   € ô —‘—‘œc×5Ñ5Ó7¸>È,ÈÐ9XÓY€JÜ‡K�K�
 °Õ6Ü×Ñ 
¨LÔ9ØÐr+   r‚   c                 óÎ   — t        j                  «       }t        j                  |j	                  dd«      «      |d<   | |d<   d|vrt        t         j                  dz  «      |d<   |S )zóCredential-scrubbed env for one command: PATH fallbacks, the session socket dir, and
    daemon-side idle self-termination (agent-browser 0.24+) mirroring the Python janitor
    unless the user set ``AGENT_BROWSER_IDLE_TIMEOUT_MS`` explicitly.ÚPATHr0   ÚAGENT_BROWSER_SOCKET_DIRÚAGENT_BROWSER_IDLE_TIMEOUT_MSiè  )r'   Ú_build_browser_envr   Ú_merge_browser_pathrw   ÚstrÚ"BROWSER_SESSION_INACTIVITY_TIMEOUT)r‚   Úenvs     r!   Ú_agent_browser_command_envr�   ™   se   € ô ×
 Ñ
 Ó
"€CÜ×.Ñ.¨s¯w©w°v¸rÓ/BÓC€Cˆ�KØ&0€CÐ"Ñ#Ø&¨cÑ1Ü/2´3×3YÑ3YÐ\`Ñ3`Ó/aˆÐ+Ñ,Ø€Jr+   ÚargvrŒ   ÚtagÚstdin_payloadzsubprocess.Popenc                 ó¬  — dD �cg c]m  }t        j                  t         j                  j                  |d|› d|› �«      t         j                  t         j
                  z  t         j                  z  d«      ‘Œo }}t        j                  }|��t         j                  j                  |d|› �«      }t        |d«      5 }	|	j                  |«       ddd«       |j                  t        j                  |t         j                  «      «       |d   }	 i }
t         j                  dk(  rEt        j                  «       }|xj                  t        j                  z  c_        t!        «       d	|d
œ}
t        j"                  | f|d   |d   ||dœ|
¤Ž|D ]  }t        j$                  |«       Œ S c c}w # 1 sw Y   ŒÞxY w# |D ]  }t        j$                  |«       Œ w xY w)a  Spawn agent-browser with stdout/stderr redirected to ``socket_dir/_std{out,err}_<tag>``;
    ``stdin_payload`` (a ``batch`` JSON body) is served from ``_stdin_<tag>`` the same way.

    Temp files, not pipes: the CLI forks a daemon that inherits its fds, so pipes never
    see EOF until the timeout. Windows: CREATE_NO_WINDOW only (CREATE_NEW_PROCESS_GROUP
    cancels asyncio's running task on 3.11), STARTF_USESTDHANDLES + close_fds so the child
    gets ONLY our three handles (leaked console handles kill the Rust daemon grandchild).
    )r=   r>   Ú_i€  NÚ_stdin_ÚwbéÿÿÿÿÚntT)ÚcreationflagsÚ	close_fdsÚstartupinfor   r1   )r=   r>   ÚstdinrŒ   )r   r   r4   rG   ÚO_WRONLYÚO_CREATÚO_TRUNCÚ
subprocessÚDEVNULLÚwriter2   ÚO_RDONLYÚnameÚSTARTUPINFOÚdwFlagsÚSTARTF_USESTDHANDLESr   ÚPopenÚclose)rŽ   rŒ   r‚   r�   r�   ÚslotÚfdsrš   Ú
stdin_pathr    Ú_popen_extraÚ_siÚfds                r!   Ú_popen_agent_browserr®   ¥   sŒ  € ð ,ö-Øô �7‰7”2—7‘7—<‘< 
¨a°¨v°Q°s°eÐ,<Ó=¼r¿{¹{ÌRÏZÉZÑ?WÔZ\×ZdÑZdÑ?dÐfkÕlð -€Cð -ä×#Ñ#€EØÐ Ü—W‘W—\‘\ *°¸°u¨oÓ>ˆ
Ü�*˜dÓ#ð 	# qØ�G‰G�MÔ"÷	#à�
‰
”2—7‘7˜:¤r§{¡{Ó3Ô4Ø�B‘ˆð	ØˆÜ�7‰7�dŠ?Ü×(Ñ(Ó*ˆCØ�KŠKœ:×:Ñ:Ñ:�KÜ-?Ó-AÐPTÐehÑiˆLÜ×Ñ Ði¨S°©V¸CÀ¹FÈ%ÐUXÑiÐ\hÑiàò 	ˆBÜ�H‰H�R�Lñ	ùò#-÷
	#ð 	#ûð ò 	ˆBÜ�H‰H�R�Lñ	ús   …A2F$Â:F)ÄA:F5 Æ)F2Æ5GÚprefixÚcdp_urlÚfeaturesc                 óX   — | › dt        j                  «       j                  dd › �d||dœS )zCFresh session dict with a random ``<prefix>_<hex10>`` session name.r’   Né
   )rz   Úbb_session_idr°   r±   )ÚuuidÚuuid4Úhex)r¯   r°   r±   s      r!   Ú_session_recordr¸   Ä   s6   € à%˜h a¬¯
©
«×(8Ñ(8¸¸"Ð(=Ð'>Ð?ÐRVØ¨Hñ6ð 6r+   Útask_idÚallow_real_profilec                 ó´  — |rot        j                  «       \  }}|rt        |«      ‚|rIt        dt	        j
                  |«      dddœ«      }t        j                  j                  d|d   | «       |S t        j                  «       rt        j                  «       rt        | «      S t        ddddi«      }t        j                  j                  d	|d   | «       |S )
ak  Local Chromium session; consented real-profile CDP attach when allowed.

    Real-profile fails closed on resolver/launch errors (a consented user must never be
    silently downgraded to a throwaway). The hybrid private-URL sidecar passes
    ``allow_real_profile=False``: the user's cookie jar must not reach an arbitrary
    internal host the model chose.
    ÚrpT)ÚlocalÚreal_profilez1Created real-profile local session %s for task %srz   ÚhNr½   z,Created local browser session %s for task %s)Ú_real_profileÚ_real_profile_cdpÚRuntimeErrorr¸   Ú_cdpÚ_resolve_cdp_overrider'   r(   ÚinfoÚ_is_browser_use_cli_modeÚ_lpÚ_using_lightpanda_engineÚ_create_lightpanda_session)r¹   rº   r°   ÚerrrÅ   s        r!   Ú_create_local_sessionrË   Ê   sÂ   € ñ Ü$×6Ñ6Ó8‰ˆ�ÙÜ˜sÓ#Ð#ÙÜ" 4¬×)CÑ)CÀGÓ)LÐX\ÐnrÑNsÓtˆDÜ�J‰J�O‰OÐOÐQUÐVdÑQeÐgnÔoØˆKô ×#Ñ#Ô%¬#×*FÑ*FÔ*HÜ)¨'Ó2Ð2ä˜3  w° oÓ6€DÜ‡J�J‡O�OÐBÀDÈÑDXÐZaÔbØ€Kr+   c                 ó  — ddl m} t        dddddœ«      } ||d   t        j                  «        ¬«      \  }}|rt        |«      ‚|j                  |d	<   t        j                  j                  d
|d   |j                  | «       |S )zCSpawn ``lightpanda serve`` for this session key (Browser Use mode).r   )Úlaunch_lightpandaÚlpNT)r½   Ú
lightpandarz   )Úblock_private_networksr°   z3Created Lightpanda session %s (port %s) for task %s)Útools.browser_lightpandarÍ   r¸   rC   Ú_is_local_backendrÂ   r°   r'   r(   rÅ   Úport)r¹   rÍ   rÅ   ÚserverrÊ   s        r!   rÉ   rÉ   å   s   € å:ä˜4 °ÀTÑ'JÓK€DÙ# D¨Ñ$8ÔU[×UmÑUmÓUoÐQoÔp�K€FˆCÙ
Ü˜3ÓÐØ—n‘n€Dˆ�OÜ‡J�J‡O�OÐIÈ4ÐP^ÑK_Ðag×alÑalÐnuÔvØ€Kr+   Úsession_infoc                 ó¶   — | j                  d«      xs i j                  d«      syddlm}  || j                  dd«      «      }|du xs |j                  «        S )	zATrue for a Lightpanda session whose ``lightpanda serve`` is gone.r±   rÏ   Fr   )Ú
get_serverrz   r0   N)rw   rÑ   r×   Úis_alive)rÕ   r×   rÔ   s      r!   Ú_local_backend_process_deadrÙ   ò   sV   € à×Ñ˜ZÓ(Ò.¨B×3Ñ3°LÔAØÝ3á˜×(Ñ(¨¸Ó<Ó=€FØ�Tˆ>Ò2 §¡Ó!2Ð2Ð2r+   c                 ó”   — t        d|ddi«      }t        j                  j                  d|d   t        j                  |«      | «       |S )z3Session connecting to a user-supplied CDP endpoint.ÚcdpÚcdp_overrideTu1   Created CDP browser session %s â†’ %s for task %srz   )r¸   r'   r(   rÅ   Ú_sanitize_url_for_logs)r¹   r°   rÅ   s      r!   Ú_create_cdp_sessionrÞ   ü   sE   € ä˜5 '¨N¸DÐ+AÓB€DÜ‡J�J‡O�OÐGØ�^Ñ$¤c×&@Ñ&@ÀÓ&IÈ7ôTà€Kr+   c                 ó:  — 	 |j                  | «      }|rt        |t        «      st        d|›�«      ‚|j	                  d«      r/t        |«      }t        j                  t        |d   «      «      |d<   |S # t        $ rœ}t        |«      j                  }t        j                  j                  d||| d¬«       	 t        | «      }n'# t        $ r}t        d|› d|› d|› d	�«      |‚d
}~ww xY wt        |t        «      ri |¥dt        |«      |dœ¥}|cY d
}~S d
}~ww xY w)z¯Cloud session; fall back to local Chromium (marked degraded) on failure. ``cdp_url``
    is resolved here because some providers return an HTTP discovery URL, not a websocket.z)Cloud provider returned invalid session: r°   zPCloud provider %s failed (%s); attempting fallback to local Chromium for task %sT©Úexc_infozCloud provider z	 failed (z") and local fallback also failed (ú)N)Úfallback_from_cloudÚfallback_reasonÚfallback_provider)Úcreate_sessionrr   ru   Ú
ValueErrorrw   rÃ   rÄ   rŠ   Ú	ExceptionÚtypeÚ__name__r'   r(   ÚwarningrË   rÂ   )r¹   ÚproviderrÕ   ÚeÚprovider_nameÚlocal_errors         r!   Ú!_create_cloud_session_or_fallbackrð     sA  € ðØ×.Ñ.¨wÓ7ˆÙ¤:¨l¼DÔ#AÜÐHÈÐHXÐYÓZÐZØ×Ñ˜IÔ&Ü Ó-ˆLÜ&*×&@Ñ&@ÄÀ\ÐR[ÑE\ÓA]Ó&^ˆL˜Ñ#ØÐøÜò Ü˜X›×/Ñ/ˆÜ�
‰
×ÑÐmØ(¨!¨W¸tð 	ô 	Eð	OÜ0°Ó9‰LøÜò 	OÜ °°¸yÈÈð L8Ø8C°}ÀAð Gó HØMNðOûð	Oúô �l¤DÔ)ð@˜lð @À4Ô\_Ð`aÓ\bØ1>ò@ˆLàÕûðúsB   ‚A2A5 Á5	DÁ>9DÂ8CÃDÃ	C(ÃC#Ã#C(Ã('DÄDÄDÚforce_localc                 óÂ   — t        j                  «       }|r|st        | |«      S |rt        | d¬«      S t	        j
                  «       }|€t        | «      S t        | |«      S )z±Fresh session for ``task_id`` (runs OUTSIDE the lock: cloud mode makes a network call).
    Precedence: CDP override > hybrid local sidecar (never real-profile) > cloud > local.F)rº   )rÃ   Ú_get_cdp_overriderÞ   rË   rC   Ú_get_cloud_providerrð   )r¹   rñ   rÜ   rì   s       r!   Ú_create_session_for_keyrõ     s`   € ô ×)Ñ)Ó+€LÙ™KÜ" 7¨LÓ9Ð9ÙÜ$ WÀÔGÐGÜ×)Ñ)Ó+€HØÐÜ$ WÓ-Ð-Ü,¨W°hÓ?Ð?r+   c                 ó¢  ‡ ‡— ‰ €dŠ t        j                  «        t        j                  ‰ «       t        j                  5  t        j
                  j                  ‰ «      Šddd«       dt        t        t        t        f      fˆˆ fd„}‰�“t        j                  ‰ «      j                  «       s |«       }|�|S dŠnbt        j                  ‰«      st        ‰«      s‰S t        j                  j!                  d‰ «       t        j"                  ‰ «        |«       }|�|S t        j$                  ‰ «      }t'        ‰ |«      }t        j                  5  ‰ t        j
                  v rt        j
                  ‰    cddd«       S t)        |«      }|j+                  d‰ «       |j+                  dt        j,                  ‰ «      «       |t        j
                  ‰ <   t        j.                  j1                  ‰ d«       ddd«       |s9|j                  d«      xs i j                  d	«      st3        j4                  ‰ «       |S # 1 sw Y   �ŒÚxY w# 1 sw Y   ŒSxY w)
zÊGet or create session info for a session key (thread-safe); also starts the
    inactivity thread and touches activity. A ``::local`` key forces local Chromium
    even with a cloud provider configured.NÚdefaultr   c                  óÊ   •— t        j                  ‰«       t        j                  5  t        j                  j                  ‰«      } d d d «        �| ‰ur| S d S # 1 sw Y   ŒxY wr8   )r€   Ú_update_session_activityr'   Ú_cleanup_lockÚ_active_sessionsrw   )ÚreplacementÚexisting_sessionr¹   s    €€r!   Ú_replacement_after_teardownz6_get_session_info.<locals>._replacement_after_teardown9  s`   ø€ ô 	×+Ñ+¨GÔ4Ü×Ññ 	<Ü×.Ñ.×2Ñ2°7Ó;ˆK÷	<à)Ð5¸+ÐM]Ñ:]ˆ{ÐgÐcgÐg÷	<ð 	<ús   § AÁA"z5Replacing expired or dead browser session for task %sÚsession_keyÚowner_task_idr±   rÏ   )r€   Ú_start_browser_cleanup_threadrù   r'   rú   rû   rw   r   r   rŠ   r   Ú_browser_session_backendÚensure_healthyÚ_session_has_expiredrÙ   r(   rÅ   Ú_cleanup_single_browser_sessionÚ_is_local_sidecar_keyrõ   ru   Ú
setdefaultÚ_bare_task_id_for_session_keyÚ_suspect_browser_sessionsÚpoprÃ   Ú_ensure_cdp_supervisor)r¹   rþ   rü   rñ   rÕ   rý   s   `    @r!   Ú_get_session_infor  ,  sý  ù€ ð €Øˆä×,Ñ,Ô.Ü×'Ñ'¨Ô0ä	×	Ñ	ñ =Ü×/Ñ/×3Ñ3°GÓ<Ð÷=ðh¬´$´s¼C°x±.Ñ)Aö hð Ð#ô ×+Ñ+¨GÓ4×CÑCÔEÙ5Ó7ˆKØÐ&Ø"Ð"Ø#ÑÜ×0Ñ0Ð1AÔBÔKfÐgwÔKxØ#Ð#ä�J‰J�O‰OÐSÐU\Ô]Ü×6Ñ6°wÔ?Ù5Ó7ˆKØÐ&Ø"Ð"ä×+Ñ+¨GÓ4€KÜ*¨7°KÓ@€Lä	×	Ñ	ñ 9Ø”c×*Ñ*Ñ*Ü×'Ñ'¨Ñ0÷9ñ 9ô ˜LÓ)ˆØ×Ñ ¨wÔ7Ø×Ñ ´×1RÑ1RÐSZÓ1[Ô\Ø(4Œ×Ñ˜WÑ%Ü×%Ñ%×)Ñ)¨'°4Ô8÷9ñ  × 0Ñ 0°Ó <Ò BÀ×GÑGÈÔUÜ×#Ñ# GÔ,àÐ÷[=ñ =ú÷>9ð 9ús   Á  H8Å%IÅ>A5IÈ8IÉIÚtask_socket_dirc                 ó  — t         j                  5  t         j                  j                  | «      |ur
	 ddd«       yt	        j
                  | «       |j                  d«      s|j                  d«      rXt        |«      }dt        j                  «       j                  dd › �|d<   |j                  dd«       |t         j                  | <   n@t         j                  j                  | d«       t         j                  j                  | d«       t        j                  | «      }t         j                  j                  |«      | k(  r t         j                  j                  |d«       ddd«       t        |j                  d«      xs d«      }|rŸt        j                   j#                  t        j                   j%                  ||› d	�«      «      r_t'        ||«      }|€!t         j(                  j+                  d
|«       yt-        j.                  |||«      sy	 ddlm} |j5                  |«       t=        j>                  |d¬«       y# 1 sw Y   ŒàxY w# t6        t8        t:        f$ r# t         j(                  j+                  d
|«       Y yw xY w)zBDrop a stuck client generation without losing cloud cleanup state.Nr´   r°   Úh_r³   rz   Ú
_first_navr0   ú.pidz.Could not kill timed-out browser daemon for %sr   )ÚdeadlineT)Úignore_errors) r'   rú   rû   rw   rÃ   Ú_stop_cdp_supervisorru   rµ   r¶   r·   r
  Ú_session_last_activityr  Ú_last_active_session_keyrŠ   r   r4   ÚisfilerG   Ú_read_browser_daemon_pidr(   r)   r€   Ú_verify_reapable_browser_daemonÚagentr  Úkill_process_treeÚProcessLookupErrorÚPermissionErrorr   ÚshutilÚrmtree)r¹   rÕ   r  rü   Úbare_task_idrz   Ú
daemon_pidÚ	_deadlines           r!   Ú"_discard_timed_out_browser_sessionr#  f  s  € ä	×	Ñ	ñ AÜ×Ñ×#Ñ# GÓ,°LÑ@Ø÷Að Aô 	×!Ñ! 'Ô*Ø×Ñ˜OÔ,°×0@Ñ0@ÀÔ0KÜ˜|Ó,ˆKØ,.¬t¯z©z«|×/?Ñ/?ÀÀÐ/DÐ.EÐ*FˆK˜Ñ'Ø�O‰O˜L¨$Ô/Ø,7ŒC× Ñ  Ò)ä× Ñ ×$Ñ$ W¨dÔ3Ü×&Ñ&×*Ñ*¨7°DÔ9ä×8Ñ8¸ÓAˆÜ×'Ñ'×+Ñ+¨LÓ9¸WÒDÜ×(Ñ(×,Ñ,¨\¸4Ô@÷Aô" �|×'Ñ'¨Ó7Ò=¸2Ó>€LÙœŸ™Ÿ™¤r§w¡w§|¡|°OÈÀ~ÐUYÐEZÓ'[Ô\Ü-¨o¸|ÓLˆ
ØÐÜ�J‰J×ÑÐMÈ|Ô\ØÜ×9Ñ9¸*ÀoÐWcÔdØð	õ 4à×'Ñ'¨
Ô3ô ‡M�M�/°Ö6÷EAð Aûô> #¤O´WÐ=ò 	Ü�J‰J×ÑÐMÈ|Ô\Ùð	ús#   ‘#I½D&IÈI ÉIÉ4JÊJc                 óØ   — t         j                  j                  | |› d�«      }	 t        t	        |«      j                  d¬«      j                  «       «      S # t        t        f$ r Y yw xY w)z>Read the agent-browser daemon PID for a session (best-effort).r  r   r   N)	r   r4   rG   Úintr   Ú	read_textr   r   rç   )r  rz   Úpid_files      r!   r  r  �  s_   € ä�w‰w�|‰|˜O°¨~¸TÐ-BÓC€HðÜ”4˜“>×+Ñ+°WÐ+Ó=×CÑCÓEÓFÐFøÜ”ZÐ ò Ùðús   ¥1A ÁA)Á(A)Úprobe_timeout_sc                 óÐ  — t         j                  dk(  ryddl}t        |d«      sy	 t        j                  | «      }d„ |D «       D ]t  }	 |j                  |j                  |j                  «      5 }|j                  |«       |j                  t         j                  j                  | |«      «       	 ddd«        y y# t
        $ r Y yw xY w# 1 sw Y   nxY wŒ”# t
        $ r Y ŒŸw xY w)u÷   Cheap liveness probe: a connect to the daemon's unix control socket proves the accept
    loop is alive (the command wedged page/CDP-side). Windows named pipes can't be probed â†’
    report unresponsive (tree-kill + respawn is the safe recovery).r–   Fr   NÚAF_UNIXc              3   óD   K  — | ]  }|j                  d «      sŒ|–— Œ y­w)z.sockN)r_   )r[   rí   s     r!   r^   z-_browser_daemon_responsive.<locals>.<genexpr>¤  s   è ø€ Ò<˜¨¯
©
°7Õ(;”!Ñ<ùs   ‚ ™ T)r   r¢   Úsocketr   Úlistdirr   r*  ÚSOCK_STREAMÚ
settimeoutÚconnectr4   rG   )r  r(  Ú
socket_modÚentriesrx   Úss         r!   Ú_browser_daemon_responsiver4  –  så   € ô 
‡w�w�$‚ØÛä�:˜yÔ)ØðÜ—*‘*˜_Ó-ˆñ =˜WÔ<ò ˆð	Ø×"Ñ" :×#5Ñ#5°z×7MÑ7MÓNð ÐRSØ—‘˜_Ô-Ø—	‘	œ"Ÿ'™'Ÿ,™, ¸Ó>Ô?Ø÷ñ ðð øô ò Ùðú÷ð úð øô ò 	Ùð	ús<   §B= Á	&CÁ/ACÂ1CÂ=	C	ÃC	ÃC	ÃCÃ	C%Ã$C%c                 ó€   — |j                  d«      s|j                  d«      rt        | ||«       yt        | ||d«       y)uú   Recover session state after a command timeout.

    Cloud/CDP: no daemon to probe â€” replace the stuck client generation now (same
    ``bb_session_id`` so cloud cleanup works). Local: ``_recycle_local_session``.
    See #68139, #72205, #72206.
    r´   r°   Nz2browser command timed out; session may be poisoned)rw   r#  Ú_recycle_local_session)r¹   rÕ   r  s      r!   Ú_handle_browser_command_timeoutr7  ¯  s=   € ð ×Ñ˜Ô(¨L×,<Ñ,<¸YÔ,GÜ*¨7°LÀ/ÔRØÜ˜7 L°/ÐCwÕxr+   Úreasonc                 ó  — t        j                  | «      j                  |«       t        |j	                  d«      xs d«      }|rt        ||«      nd}|duxr; t        j                  |«      xr$ t        j                  |||«      xr t        |«      }|r"t         j                  j                  d| |«       yt         j                  j                  d| |«       t        | ||«       t         j                  j                  | d«       y)uÝ  Stop handing out a poisoned local session record (timeout or protocol-level failure).

    Daemon alive (PID live, verified as ours, control socket accepts): only the *command*
    wedged â€” mark suspect, recycle at next use through ``ensure_healthy``. Daemon wedged or
    dead: tree-kill and evict now (Chromium children would leak). Both branches
    ``mark_suspect`` first so the poisoned-cache invariant holds even if eviction races
    another thread's replacement.
    rz   r0   Nz\browser daemon for %s is alive (%s); session marked suspect and will be recycled at next usezSbrowser daemon for %s is wedged or dead (%s); tree-killing and evicting the session)r'   r  Úmark_suspectrŠ   rw   r  r€   Ú_pid_existsr  r4  r(   rë   r#  r	  r
  )r¹   rÕ   r  r8  rz   r!  Údaemon_alives          r!   r6  r6  ¼  só   € ô × Ñ  Ó)×6Ñ6°vÔ>ä�|×'Ñ'¨Ó7Ò=¸2Ó>€LÙLXÔ)¨/¸<ÔHÐ^b€Jà˜$Ðò 	8Ü×"Ñ" :Ó.ò	8ä×6Ñ6°zÀ?ÐT`Óaò	8ô ' Ó7ð	 ñ Ü�
‰
×Ñð 2Ø3:¸Fô	Dàä‡J�J×ÑÐlØ ô(ä& w°¸oÔNô ×!Ñ!×%Ñ% g¨tÕ4r+   c                 ó(  — | j                  d«      xs i }|j                  d«      r"|j                  d«      s|j                  d«      ry| j                  d«      s| j                  d«      ry|j                  d«      d	uxr |j                  d
«       S )u¦  True when a finished command failed at the agent-browser level â€” nonzero exit (101 =
    the CLI panicked against a stale session daemon), or empty/non-JSON output from a dead
    daemon â€” on a plain local Chromium session. Parsed-JSON failures carry no ``returncode``
    (the backend answered; the page said no) and must not recycle; cloud/CDP/real-profile/
    Lightpanda sessions have their own recovery (#115184).r±   r½   rÏ   r¾   Fr°   r´   Ú
returncodeNro   )rw   )rÕ   rj   Úfeatss      r!   Ú%_is_recoverable_local_backend_failurer@  Ü  s�   € ð ×Ñ˜ZÓ(Ò.¨B€EØ�9‰9�WÔ §¡¨<Ô!8¸E¿I¹IÀnÔ<UØØ×Ñ˜	Ô" l×&6Ñ&6°Ô&GØØ�:‰:�lÓ#¨4Ð/ÒM¸¿
¹
À9Ó8MÐ4MÐMr+   r>  c           	      óÂ  — |rh|j                  «       rX|dk7  rt        j                  nt        j                  }t        j
                  j                  |d| |j                  «       dd «       |j                  «       }|sˆ|dk7  rB|r|j                  «       nd|› �}t        j
                  j                  d| ||dd «       d||d	œS | t        j                  vr*t        j
                  j                  d
| «       dd| › d�|d	œS di dœS 	 t        j                  |«      }| dk(  re|j%                  d«      rT|j%                  di «      }|j%                  d«      s1|j%                  d«      s t        j
                  j                  d|«       |S # t        j                  $ rÄ |dd }t        j
                  j                  d| ||dd «       | dk(  r†dj                  d„ ||xs dj                  «       fD «       «      }	t        j                  |	«      }
|
rCt        |
«      j!                  «       r*t        j
                  j#                  d|
«       d|
|dœdœcY S dd| › d|› �|d	œcY S w xY w)u  Finished agent-browser process output â†’ result dict. Empty stdout with rc=0 is a
    broken state (stale daemon) reported as failure except for ``_EMPTY_OK_COMMANDS``;
    non-JSON output is an error except ``screenshot``, whose path is recovered from prose.r   zbrowser '%s' stderr: %sNéô  zCommand failed with code zbrowser '%s' failed (rc=%s): %srm   F)ro   rp   r>  z)browser '%s' returned empty output (rc=0)zBrowser command 'z' returned no outputT)ro   rq   iÐ  z1browser '%s' returned non-JSON output (rc=%s): %sÚ
screenshotrA   c              3   ó&   K  — | ]	  }|sŒ|–— Œ y ­wr8   rZ   )r[   Úparts     r!   r^   z4_interpret_browser_command_output.<locals>.<genexpr>  s   è ø€ Ò%e¨tÒ`d¤dÑ%eùs   ‚Šr0   z<browser 'screenshot' recovered file from non-JSON output: %s)r4   Úrawz(Non-JSON output from agent-browser for 'rl   Úsnapshotro   rq   Úrefsz]snapshot returned empty content. Possible stale daemon or CDP connection issue. returncode=%s)r   ÚloggingÚWARNINGÚDEBUGr'   r(   Úlogrë   Ú_EMPTY_OK_COMMANDSre   ÚloadsÚJSONDecodeErrorrG   Ú	_snapshotÚ"_extract_screenshot_path_from_textr   ÚexistsrÅ   rw   )r;   r=   r>   r>  ÚlevelÚstdout_textÚ	error_msgÚparsedrF  Úcombined_textÚrecovered_pathÚ	snap_datas               r!   Ú!_interpret_browser_command_outputrZ  ê  sJ  € ñ �&—,‘,”.Ø#-°¢?”—’¼¿¹ˆÜ�
‰
�‰�uÐ7¸À&Ç,Á,Ã.ÐQUÐRUÐBVÔWà—,‘,“.€KÙØ˜Š?Ù*0˜Ÿ™œÐ8QÐR\ÐQ]Ð6^ˆIÜ�J‰J×ÑÐ@À'È:ÐW`ÐaeÐbeÐWfÔgØ$¨yÈ
ÑSÐSØœ#×0Ñ0Ñ0Ü�J‰J×ÑÐJÈGÔTØ$Ð1BÀ7À)ÐK_Ð/`ÐpzÑ{Ð{Ø¨Ñ,Ð,ðCÜ—‘˜KÓ(ˆð �*Ò §¡¨IÔ!6Ø—J‘J˜v rÓ*ˆ	Ø�}‰}˜ZÔ(°·±¸vÔ1FÜ�J‰J×Ñð  /Ø0:ô<à€Møô# ×Ñò 	CØ˜%˜4Ð ˆÜ�
‰
×ÑÐNÐPWÐYcÐehÐimÐjmÐenÔoØ�lÒ"Ø ŸI™IÑ%e¸ÀfÂlÐPR×EYÑEYÓE[Ð7\Ô%eÓeˆMÜ&×IÑIÈ-ÓXˆNÙ¤$ ~Ó"6×"=Ñ"=Ô"?Ü—
‘
—‘Ð ^Ð`nÔoØ#'¸.ÐQTÑ1UÑVÒVØ Ð-UÐV]ÐU^Ð^aÐbeÐafÐ+gð  xBñ  Cò  	Cð	Cús   ÄF ÆCIÉIÉIc                  ó¬  — 	 t        j                  «       } t        j                  | «      r9t        j                  «       }t        j                  j                  d|«       d|dœS t        j                  «       r„t        j                  «       spt        j                  «       dk7  rYt        j                  «       sEt        j                  «       rt        nt         }t        j                  j                  d|«       d|dœS dd	lm}  |«       rdd
dœS d| iS # t        $ r8}t        j                  j                  d|«       dt        |«      dœcY d}~S d}~ww xY w)u¸   Fail fast before spawning (missing CLI, Termux gap, interrupt, no Chromium in local
    mode â€” else every call hangs for command_timeout). Error result, or ``{"browser_cmd": path}``.zagent-browser CLI not found: %sFrn   Nz%browser command blocked on Termux: %srÏ   zbrowser command blocked: %sr   )Úis_interruptedÚInterruptedrK   )r   Ú_find_agent_browserÚFileNotFoundErrorr'   r(   rë   rŠ   Ú%_requires_real_termux_browser_installÚ_termux_browser_install_errorrC   rD   Ú_chromium_installedÚ_get_browser_engineÚ_maybe_autoinstall_chromiumr   Ú_CHROMIUM_MISSING_DOCKER_HINTÚ_CHROMIUM_MISSING_HINTÚtools.interruptr\  )rK   rí   rp   Úhintr\  s        r!   Ú_browser_command_preflightri    s  € ð3Ü×2Ñ2Ó4ˆô
 ×5Ñ5°kÔBÜ×6Ñ6Ó8ˆÜ�
‰
×ÑÐBÀEÔJØ ¨5Ñ1Ð1ô 	×ÑÔÜ×,Ñ,Ô.Ü×&Ñ&Ó(¨LÒ8Ü×4Ñ4Ô6ä08×0KÑ0KÔ0MÕ,ÔSiˆÜ�
‰
×ÑÐ8¸$Ô?Ø ¨4Ñ0Ð0å.ÙÔØ ¨=Ñ9Ð9Ø˜;Ð'Ð'øô/ ò 3Ü�
‰
×ÑÐ<¸aÔ@Ø ¬3¨q«6Ñ2Õ2ûð3ús   ‚D Ä	EÄ-EÅEÅEÚ	cmd_partsÚenginec           	      óô  — t        |d   «      }t        j                  j                  d|| |t	        |«      «       t        |«      }|dk(  rRdD �	cg c]  }	|j                  |	d«      ‘Œ }
}	t        d„ |
D «       «      r,t        j                  j                  d|«       nt        |«       t        j                  j                  |d|› �«      }t        j                  j                  |d	|› �«      }t        |||||«      }	 |j                  |¬
«       t/        |dd¬«      5 }|j1                  «       }ddd«       t/        |dd¬«      5 }|j1                  «       }ddd«       t%        ||«       t3        ||j4                  «      S c c}	w # t        j                  $ rÃ |j!                  «        |j                  «        t#        ||«      \  }}t%        ||«       t'        | ||«       |rB|j)                  «       r2t        j                  j+                  d||j)                  «       dd «       t        j                  j+                  d||| |«       dt-        ||||«      dœcY S w xY w# 1 sw Y   �Œ3xY w# 1 sw Y   �ŒxY w)zTRun the prepared agent-browser argv once and interpret its output (handles timeout).rz   z/browser cmd=%s task=%s socket_dir=%s (%d chars)rÏ   )r%   r&   Nc              3   ó$   K  — | ]  }|d u–— Œ
 y ­wr8   rZ   )r[   Úvs     r!   r^   z%_spawn_and_collect.<locals>.<genexpr>@  s   è ø€ Ò/ ˆq˜Œ}Ñ/ùs   ‚z�browser: stripped Chromium-only AGENT_BROWSER_ARGS/AGENT_BROWSER_CHROME_FLAGS for Lightpanda command %s (agent-browser rejects them with --engine lightpanda)Ú_stdout_Ú_stderr_)r<   z%browser '%s' stderr after timeout: %srB  z9browser '%s' timed out after %ds (task=%s, socket_dir=%s)Frn   r/   r   r   )rƒ   r'   r(   r)   rt   r�   r
  rd   r*   r   r4   rG   r®   Úwaitrž   ÚTimeoutExpiredÚkillr5   r:   r7  r   rë   rJ   r   r   rZ  r>  )r¹   rÕ   rj  r;   rk  r<   r�   r  r#   ÚkÚstrippedr,   r-   Úprocr=   r>   r    s                    r!   Ú_spawn_and_collectrw  2  s;  € ô
 2°,¸~Ñ2NÓO€OÜ‡J�J×ÑÐFØ˜' ?´C¸Ó4HôJä,¨_Ó=€Kð �ÒØ6jÖk°�K—O‘O A tÕ,ÐkˆÐkÜÑ/ hÔ/Ô/Ü�J‰J×Ñð oà$õ&ô 	% [Ô1ä—'‘'—,‘,˜°(¸7¸)Ð0DÓE€KÜ—'‘'—,‘,˜°(¸7¸)Ð0DÓE€KÜ 	¨;¸ÈÐR_Ó`€DðlØ�	‰	˜'ˆ	Ô"ô 
ˆk˜3¨Ô	1ð °QØ—‘“ˆ÷ä	ˆk˜3¨Ô	1ð °QØ—‘“ˆ÷ä  ¨kÔ:Ü,¨W°f¸fÀdÇoÁoÓVÐVùò= løô ×$Ñ$ò 
lØ�	‰	ŒØ�	‰	ŒÜ3°KÀÓM‰ˆ�Ü$ [°+Ô>Ü'¨°¸ÔOÙ�f—l‘l”nÜ�J‰J×ÑÐFÈÐQW×Q]ÑQ]ÓQ_Ð`dÐadÐQeÔfÜ�
‰
×ÑÐVØ ¨°/ô	Cà Ô+HÈÐRYÐ[aÐciÓ+jÑkÒkð
lú÷ñ ú÷ñ ús1   ÁFÃ>F ÄI ÅI-ÆCIÉIÉ I*É-I7Ú_engine_overrideztuple[str, Dict[str, Any]]c                 ó~  — |dk7  r&|j                  d«      rt        j                  | «       |xs t        j                  «       }|j                  d«      rd|d   g}nLd|d   g}t        j
                  «       r|j                  d«       |dk7  rt        j                  «       s|d|gz  }t        |«      }	t        |	d	   ||«      \  }
}}|	|z   d
|
gz   |z   }	 t        t        | ||||||«      |«      }||fS # t        $ r>}t        j                  j                  d||d¬«       dt!        |«      dœ}Y d}~||fS d}~ww xY w)u[   Build the agent-browser argv for ``session_info`` and run it once â†’ ``(engine, result)``.r§   r°   z--cdpz	--sessionrz   z--headedÚautoz--enginer   z--jsonzbrowser '%s' exception: %sTrà   Frn   N)rw   rÃ   r  rC   rc  Ú_is_headed_moder2   r'   Ú_is_camofox_moderR   ri   ry   rw  rè   r(   rë   rŠ   )r¹   rÕ   rK   r;   rT   r<   rx  rk  Úbackend_argsrŽ   Úspawn_commandÚ
spawn_argsr�   rj  rj   rí   s                   r!   Ú_dispatch_browser_commandr€  `  s\  € ð �'Ò˜l×.Ñ.¨yÔ9Ü×#Ñ# GÔ,ð Ò=¤×!;Ñ!;Ó!=€FØ×Ñ˜	Ô"Ø ¨iÑ!8Ð9‰à# \°.Ñ%AÐBˆÜ×!Ñ!Ô#Ø×Ñ 
Ô+Ø�VÒ¤C×$8Ñ$8Ô$:Ø˜Z¨Ð0Ñ0ˆLä˜{Ó+€DÜ/>¸tÀA¹wÈÐQUÓ/VÑ,€M�:˜}Ø�|Ñ# x°Ð&?Ñ?À*ÑL€Ið5Ü%Ü˜w¨°iÀÈ&ÐRYÐ[hÓiÐkrótˆð
 �6ˆ>Ðøô ò 5Ü�
‰
×ÑÐ7¸À!ÈdÐÔSØ"¬S°«VÑ4ŒØ�6ˆ>Ðûð5ús   ÃC5 Ã5	D<Ã>1D7Ä7D<c                 ó`  — |€t        j                  «       }|xs g }t        «       }d|vr|S |d   }t        d«      D ]š  }	 t	        | «      }t        | ||||||«      \  }
}|dk(  rr|d	k7  rmt        ||«      rat         j                  j                  d
|| |j                  d«      «       t        | |t        |d   «      d|› d|j                  d«      › �«       Œš n t        j                  
|«      }|rqt         j                  j!                  d|| |«       |dk(  rt        j"                  | |xs g |«      }nt        j$                  | |||«      }t        j&                  ||«      S |S # t
        $ r>}	t         j                  j                  d| |	«       ddt        |	«      › �dœcY d}	~	c S d}	~	ww xY w)a  Run one agent-browser CLI command against the task's session; returns its parsed JSON.
    ``timeout=None`` reads ``browser.command_timeout``; ``_engine_override`` forces an engine
    for this call only (Lightpanda fallback retries with Chrome without touching global state).NrK   é   z0Failed to create browser session for task=%s: %sFz"Failed to create browser session: rn   r   r§   zbbrowser '%s' failed at the backend level (task=%s, rc=%s); recycling the session and retrying oncer>  rz   zagent-browser 'z	' exited z<Lightpanda fallback: retrying '%s' with Chrome (task=%s): %srC  )r'   Ú_safe_command_timeoutri  Úranger  rè   r(   rë   rŠ   r€  r@  rw   r6  rƒ   rÇ   Ú_lightpanda_fallback_reasonrÅ   Ú_chrome_fallback_screenshotÚ_run_chrome_fallback_commandÚ_annotate_lightpanda_fallback)r¹   r;   rT   r<   rx  Ú	preflightrK   ÚattemptrÕ   rí   rk  rj   rä   Úfallback_results                 r!   Ú_run_browser_commandrŒ  „  sÔ  € ð €Ü×+Ñ+Ó-ˆØŠ:�2€Dä*Ó,€IØ˜IÑ%ØÐØ˜MÑ*€Kä˜“8ò ˆð	^Ü,¨WÓ5ˆLô 3°7¸LÈ+ÐW^Ð`dÐfmØ3CóE‰ˆ�ð �aŠ<˜G wÒ.Ô3XÐYeÐgmÔ3nÜ�J‰J×Ñð  3Ø4;¸WÀfÇjÁjÐQ]ÓF^ô`ä" 7¨LÔ:UÐVbÐcqÑVrÓ:sØ%4°W°I¸YÀvÇzÁzÐR^ÓG_ÐF`Ð#aôcàÙð'ô. ×5Ñ5°f¸gÀvÓN€OÙÜ�
‰
�‰ÐVÐX_ÐahÐjyÔzØ�lÒ"Ü!×=Ñ=¸gÀtÂzÈrÐSZÓ[‰Oä!×>Ñ>¸wÈÐQUÐW^Ó_ˆOÜ×0Ñ0°À/ÓRÐRà€Møô; ò 	^Ü�J‰J×ÑÐQÐSZÐ\]Ô^Ø$Ð1SÔTWÐXYÓTZÐS[Ð/\Ñ]×]ûð	^ús   ÁE&Å&	F-Å/1F(Æ F-Æ(F-r8   )T)g      ð?)NNN)LÚ__doc__r`   re   rI  r   r  rž   rµ   Úpathlibr   Útypingr   r   r   r   Úhermes_cli._subprocess_compatr   Útools.browser_tool_originr	   r'   Útoolsr
   rÃ   r   rC   r   r   r   r€   r   rÇ   r   rÀ   r   rP  rE   rF   re  rf  rv   r"   rŠ   r*   Útupler5   r:   r%  rJ   rs   rR   ri   ry   rƒ   r�   Úbytesr®   r¸   rË   rÉ   rÙ   rÞ   rð   rõ   r  r#  r  Úfloatr4  r7  r6  r@  rZ  ri  rw  r€  rŒ  rZ   r+   r!   ú<module>r–     sa  ðñó Û Û Û 	Û Û Û Ý ß ,Ó ,å <Ý 3Ý *Ý .Ý 2Ý 6Ý 9Ý <Ý 4àE€ØmÐ ð"AØAMÀð"PÐ àIÐJ[ÐI\Ð]Ð ð
¨ó 
ðS¨d°3¸°8©nð SÀó Sð	¨Cð 	¸cð 	ÀeÈCÐQTÈHÁoó 	ð¨ð °ó ðØðØðØ(+ðØ58ðàóð. Sð ¨Tó ðG˜3ð G¨ð G°D¸±Ið GÐBjó Gð(m ð m¨sð m°t¸CÀ¸H±~ó mð¨cð °có ð	¨3ð 	°4¸¸S¸±>ó 	ð ;?ñ˜t C™yð ¨t°C¸°H©~ð È3ð ÐUXð Ø(0°©ðØCUóð>6˜Cð 6¨(°3©-ð 6À4ÈÈSÈÁ>ð 6ÐVZÐ[^Ð`cÐ[cÑVdó 6ñ 3ð ¸Dð ÈDÐQTÐVYÐQYÉNó ð6
¨ð 
°°S¸#°X±ó 
ð3¨d°3¸°8©nð 3Àó 3ð ð ¨sð °t¸CÀ¸H±~ó ð¨sð ÀÀcÈ3ÀhÁó ð4@ Sð @°tð @ÀÀSÈ#ÀXÁó @ñ7˜x¨™}ð 7¸¸SÀ#¸X¹ó 7ðt$7°ð $7À4ÈÈSÈÁ>ð $7Ðdgð $7Ðlpó $7ðN¨cð Àð ÈÐRUÉó ñ°ð Àeð ÐVZó ð2
y¨Sð 
yÀÀSÈ#ÀXÁð 
yÐadð 
yÐimó 
yð5 Cð 5°t¸CÀ¸H±~ð 5ÐX[ð 5Ðehð 5Ðmqó 5ð@N¸¸SÀ#¸X¹ð NÐPTÐUXÐZ]ÐU]ÑP^ð NÐcgó Nð&¨sð &¸Cð &Èð &ÐZ]ð &ÐbfÐgjÐloÐgoÑbpó &ðR( D¨¨c¨¡Nó (ðB OSñ+WØð+WØ $ S¨# X¡ð+WØ;?À¹9ð+Wàð+Wàð+Wà(+ð+Wà<DÀU¹Oð+Wð 
ˆ#ˆsˆ(�^ó+Wð\!Øð!Ø $ S¨# X¡ð!Ø=@ð!ØKNð!ØVZÐ[^ÑV_ð!àð!à$,¨S¡Mð!ð "ó!ðN Ø!Ø&*ñ3Øð3àð3ð ˆs‰)ð3ð �c‰]ð	3ð
 ˜s‘mð3ð 
ˆ#ˆsˆ(�^ô3r+   