§
    ‰eîiHW  ã                  ó~  — U d Z ddlmZ ddlZddlZddlZddlZddlmZm	Z	m
Z
 ddlmZmZ  ej        e¦  «        ZdZ	 ddlZddlmZ dZn# e$ r	 dZeZd	ZY nw xY wd
„ Zd6d„Zd7d„Zd8d„Z	 	 	 	 	 d9d:d„Zdde› d�d d!d"d#œd d$i dd%œd!d&d#œd!d'd#œd(d)d*d+œd,œdgd-œd.œZded/<   d;d1„Z ej         dd2ed3„ ed4¬5¦  «         dS )<uÏ  
Raw Chrome DevTools Protocol (CDP) passthrough tool.

Exposes a single tool, ``browser_cdp``, that sends arbitrary CDP commands to
the browser's DevTools WebSocket endpoint.  Works when a CDP URL is
configured â€” either via ``/browser connect`` (sets ``BROWSER_CDP_URL``) or
``browser.cdp_url`` in ``config.yaml`` â€” or when a CDP-backed cloud provider
session is active.

This is the escape hatch for browser operations not covered by the main
browser tool surface (``browser_navigate``, ``browser_click``,
``browser_console``, etc.) â€” handling native dialogs, iframe-scoped
evaluation, cookie/network control, low-level tab management, etc.

Method reference: https://chromedevtools.github.io/devtools-protocol/
é    )ÚannotationsN)ÚAnyÚDictÚOptional)ÚregistryÚ
tool_errorz3https://chromedevtools.github.io/devtools-protocol/)ÚWebSocketExceptionTFc                óz  — 	 t          j        ¦   «         }n# t          $ r d}Y nw xY w|r|                     ¦   «         rkddl}|j                             d¬¦  «        5 }|                     t           j        | ¦  «        }| 	                    ¦   «         cddd¦  «         S # 1 swxY w Y   t          j        | ¦  «        S )zJRun an async coroutine from a sync handler, safe inside or outside a loop.Nr   é   )Úmax_workers)
ÚasyncioÚget_running_loopÚRuntimeErrorÚ
is_runningÚconcurrent.futuresÚfuturesÚThreadPoolExecutorÚsubmitÚrunÚresult)ÚcoroÚloopÚ
concurrentÚpoolÚfutures        ú8/home/div/.hermes/hermes-agent/tools/browser_cdp_tool.pyÚ
_run_asyncr   3   s  € ðÝÔ'Ñ)Ô)ˆˆøÝð ð ð Øˆˆˆðøøøð ð #�—’Ñ!Ô!ð #Ø!Ð!Ð!Ð!àÔ×2Ò2¸qÐ2ÑAÔAð 	#ÀTØ—[’[¥¤¨dÑ3Ô3ˆFØ—=’=‘?”?ð	#ð 	#ð 	#ð 	#ñ 	#ô 	#ð 	#ð 	#ð 	#ð 	#ð 	#ð 	#øøøð 	#ð 	#ð 	#ð 	#õ Œ;�tÑÔÐs   ‚ –%¤%Á4BÂB"Â%B"ÚreturnÚstrc                 ó²   — 	 ddl m}   | ¦   «         pd                     ¦   «         S # t          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w)aR  Return the normalized CDP WebSocket URL, or empty string if unavailable.

    Delegates to ``tools.browser_tool._get_cdp_override`` so precedence stays
    consistent with the rest of the browser tool surface:

    1. ``BROWSER_CDP_URL`` env var (live override from ``/browser connect``)
    2. ``browser.cdp_url`` in ``config.yaml``
    r   )Ú_get_cdp_overrideÚ z/browser_cdp: failed to resolve CDP endpoint: %sN)Útools.browser_toolr!   ÚstripÚ	ExceptionÚloggerÚdebug)r!   Úexcs     r   Ú_resolve_cdp_endpointr)   H   sz   € ðØ8Ð8Ð8Ð8Ð8Ð8à!Ð!Ñ#Ô#Ð) r×0Ò0Ñ2Ô2Ð2øÝð ð ð Ý�ŠÐFÈÑLÔLÐLØˆrˆrˆrˆrˆrøøøøðøøøs   ‚#& ¦
A°AÁAÚws_urlÚmethodÚparamsúDict[str, Any]Ú	target_idúOptional[str]ÚtimeoutÚfloatc           
   ƒ  óÔ  K  — t           €J ‚t          j        | d|dd¬¦  «        4 ƒd{V —†	 }d}d}|�rW|}|dz  }|                     t          j        |d|ddœdœ¦  «        ¦  «        ƒ d{V —† t          j        ¦   «                              ¦   «         |z   }		 |	t          j        ¦   «                              ¦   «         z
  }
|
d	k    rt          d
|› �¦  «        ‚t          j	        | 
                    ¦   «         |
¬¦  «        ƒ d{V —†}t          j        |¦  «        }|                     d¦  «        |k    rWd|v rt          d|d         › �¦  «        ‚|                     di ¦  «                             d¦  «        }|st          d¦  «        ‚nŒó|}|dz  }|||pi dœ}|r||d<   |                     t          j        |¦  «        ¦  «        ƒ d{V —† t          j        ¦   «                              ¦   «         |z   }		 |	t          j        ¦   «                              ¦   «         z
  }
|
d	k    rt          d|› �¦  «        ‚t          j	        | 
                    ¦   «         |
¬¦  «        ƒ d{V —†}t          j        |¦  «        }|                     d¦  «        |k    rDd|v rt          d|d         › �¦  «        ‚|                     di ¦  «        cddd¦  «        ƒd{V —† S Œà# 1 ƒd{V —†swxY w Y   dS )uÔ  Make a single CDP call, optionally attaching to a target first.

    When ``target_id`` is provided, we call ``Target.attachToTarget`` with
    ``flatten=True`` to multiplex a page-level session over the same
    browser-level WebSocket, then send ``method`` with that ``sessionId``.
    When ``target_id`` is None, ``method`` is sent at browser level â€” which
    works for ``Target.*``, ``Browser.*``, ``Storage.*`` and a few other
    globally-scoped domains.
    Né   )Úmax_sizeÚopen_timeoutÚclose_timeoutÚping_intervalr   zTarget.attachToTargetT)ÚtargetIdÚflatten)Úidr+   r,   r   zTimed out attaching to target ©r0   r:   ÚerrorzTarget.attachToTarget failed: r   Ú	sessionIdz0Target.attachToTarget did not return a sessionIdz"Timed out waiting for response to zCDP error: )Ú
websocketsÚconnectÚsendÚjsonÚdumpsr   Úget_event_loopÚtimeÚTimeoutErrorÚwait_forÚrecvÚloadsÚgetr   )r*   r+   r,   r.   r0   ÚwsÚnext_idÚ
session_idÚ	attach_idÚdeadlineÚ	remainingÚrawÚmsgÚcall_idÚreqs                  r   Ú	_cdp_callrT   _   s8  è è € õ  Ð!Ð!Ð!åÔ!ØØØØØðñ ô ð E-ð E-ð E-ð E-ð E-ð E-ð E-ð E-ð 
ØˆØ$(ˆ
ð ñ 	ØˆIØ�q‰LˆGØ—'’'Ý”
à'Ø"9Ø/8ÀTÐ"JÐ"Jðð ñô ñô ð ð ð ð ð ð ð õ Ô-Ñ/Ô/×4Ò4Ñ6Ô6¸Ñ@ˆHðØ$¥wÔ'=Ñ'?Ô'?×'DÒ'DÑ'FÔ'FÑF�	Ø ’>�>Ý&ØD¸ÐDÐDñô ð õ $Ô,¨R¯WªW©Y¬YÀ	ÐJÑJÔJÐJÐJÐJÐJÐJÐJ�Ý”j ‘o”o�Ø—7’7˜4‘=”= IÒ-Ð-Ø #�~�~Ý*ØK¸SÀ¼\ÐKÐKñô ð ð "%§¢¨°2Ñ!6Ô!6×!:Ò!:¸;Ñ!GÔ!G�JØ%ð Ý*ØNñô ð ð ð%ð, ˆØ�1‰ˆàØØ�l ð
ð 
ˆð
 ð 	*Ø)ˆC�ÑØ�gŠg•d”j ‘o”oÑ&Ô&Ð&Ð&Ð&Ð&Ð&Ð&Ð&åÔ)Ñ+Ô+×0Ò0Ñ2Ô2°WÑ<ˆð	-Ø ¥7Ô#9Ñ#;Ô#;×#@Ò#@Ñ#BÔ#BÑBˆIØ˜AŠ~ˆ~Ý"ØA¸ÐAÐAñô ð õ  Ô(¨¯ª©¬¸IÐFÑFÔFÐFÐFÐFÐFÐFÐFˆCÝ”*˜S‘/”/ˆCØ�wŠw�t‰}Œ} Ò'Ð'Ø˜c�>�>Ý&Ð'C°S¸´\Ð'CÐ'CÑDÔDÐDØ—w’w˜x¨Ñ,Ô,ðKE-ð E-ð E-ð E-ñ E-ô E-ð E-ð E-ð E-ð E-ð E-ð E-ð E-ð E-ðt	-ðuE-ð E-ð E-ð E-ð E-ð E-ð E-ð E-ð E-ð E-øøøð E-ð E-ð E-ð E-ð E-ð E-s   ¬JKËKË
K!Ë$K!Útask_idÚframe_idúOptional[Dict[str, Any]]c                óÌ  ‡‡‡‡‡— 	 ddl m} n*# t          $ r}t          d|› d�¦  «        cY d}~S d}~ww xY w|                     | ¦  «        Š‰€t          d| ›d�¦  «        S ‰                     ¦   «         }|j                             d¦  «        }d}	|r|                     d	¦  «        |k    r|}	n=|j                             d
g ¦  «        pg D ]}
|
                     d	¦  «        |k    r|
}	 nŒ |	€O‰j        5  ‰j                             |¦  «        }ddd¦  «         n# 1 swxY w Y   |�| 	                    ¦   «         }	|	€t          d|›d�¦  «        S |	                     d¦  «        Š‰st          d|›d�¦  «        S ddl
}‰j        }|�|                     ¦   «         st          d¦  «        S ˆˆˆˆˆfd„}	 |                      |¦   «         |¦  «        }|                     ‰dz   ¬¦  «        }nE# t          $ r8}t          dt          |¦  «        j        › d|› �t"          ¬¦  «        cY d}~S d}~ww xY wd‰|‰|                     di ¦  «        dœ}t%          j        |d¬¦  «        S )a\  Route a CDP call through the live supervisor session for an OOPIF frame.

    Looks up the frame in the supervisor's snapshot, extracts its child
    ``cdp_session_id``, and dispatches ``method`` with that sessionId via
    the supervisor's already-connected WebSocket (using
    ``asyncio.run_coroutine_threadsafe`` onto the supervisor loop).
    r   )ÚSUPERVISOR_REGISTRYz!CDP supervisor is not available: zp. frame_id routing requires a running supervisor attached via /browser connect or an active Browserbase session.Nz'No CDP supervisor is attached for task=zª. Call browser_navigate or /browser connect first so the supervisor can attach. Once attached, browser_snapshot will populate frame_tree with frame_ids you can pass here.ÚtoprV   Úchildrenz	frame_id zP not found in supervisor state. Call browser_snapshot to see current frame_tree.rL   zò is not an out-of-process iframe (no dedicated CDP session). For same-origin iframes, use `browser_cdp(method='Runtime.evaluate', params={'expression': "document.querySelector('iframe').contentDocument.title"})` at the top-level page instead.zKCDP supervisor loop is not running. Try reconnecting with /browser connect.c               “  óJ   •K  — ‰                      ‰‰pi ‰ ‰¬¦  «        ƒ d {V —†S )N)rL   r0   )Ú_cdp)Ú	child_sidr+   r,   Ú
supervisorr0   s   €€€€€r   Ú_do_cdpz,_browser_cdp_via_supervisor.<locals>._do_cdp  sP   øè è € Ø—_’_ØØˆL�bØ Øð	 %ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
ó    é   r;   z CDP call via supervisor failed: ú: ©Úcdp_docsTr   )Úsuccessr+   rV   rL   r   F©Úensure_ascii)Útools.browser_supervisorrY   r%   r   rI   ÚsnapshotÚ
frame_treeÚ_state_lockÚ_framesÚto_dictr   Ú_loopr   Úrun_coroutine_threadsafer   ÚtypeÚ__name__ÚCDP_DOCS_URLrA   rB   )rU   rV   r+   r,   r0   rY   r(   ÚsnaprZ   Ú
frame_infoÚchildrP   Ú_asyncior   r`   ÚfutÚ
result_msgÚpayloadr^   r_   s     ```             @@r   Ú_browser_cdp_via_supervisorr{   ¿   s½  øøøøø€ ð
Ø@Ð@Ð@Ð@Ð@Ð@Ð@øÝð 
ð 
ð 
Ýð$°ð $ð $ð $ñ
ô 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
øøøøð
øøøð %×(Ò(¨Ñ1Ô1€JØÐÝð<°gð <ð <ð <ñ
ô 
ð 	
ð ×ÒÑ Ô €Dà
Œ/×
Ò
˜eÑ
$Ô
$€CØ+/€JØ
ð ˆs�wŠw�zÑ"Ô" hÒ.Ð.Øˆ
ˆ
à”_×(Ò(¨°RÑ8Ô8Ð>¸Bð 	ð 	ˆEØ�yŠy˜Ñ$Ô$¨Ò0Ð0Ø"�
Ø�ð 1ð ÐàÔ#ð 	3ð 	3ØÔ$×(Ò(¨Ñ2Ô2ˆCð	3ð 	3ð 	3ñ 	3ô 	3ð 	3ð 	3ð 	3ð 	3ð 	3ð 	3øøøð 	3ð 	3ð 	3ð 	3àˆ?ØŸš™œˆJàÐÝð@˜ð @ð @ð @ñ
ô 
ð 	
ð
 —’˜|Ñ,Ô,€IØð 

õ ð.˜ð .ð .ð .ñ
ô 
ð 	
ð ÐÐÐØÔ€DØ€|˜4Ÿ?š?Ñ,Ô,€|Ýð ñ
ô 
ð 	
ð

ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð
Ø×/Ò/°°±	´	¸4Ñ@Ô@ˆØ—Z’Z¨°!©�ZÑ4Ô4ˆ
ˆ
øÝð 
ð 
ð 
ÝØJ­t°C©y¬yÔ/AÐJÐJÀSÐJÐJÝ!ð
ñ 
ô 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
øøøøð
øøøð ØØØØ—.’. ¨2Ñ.Ô.ðð €Gõ Œ:�g¨EÐ2Ñ2Ô2Ð2sD   ‡ Ž
5˜0ª5°5Ã6DÄD!Ä$D!Æ77G/ Ç/
H1Ç9-H,È&H1È,H1ç      >@c           	     ó  — |rt          |pd|| ||¬¦  «        S ~| rt          | t          ¦  «        st          dt          ¬¦  «        S t
          st          d¦  «        S t          ¦   «         }|st          dt          ¬¦  «        S |                     d¦  «        st          d|›d	�¦  «        S |pi }t          |t          ¦  «        s$t          d
t          |¦  «        j
        › �¦  «        S 	 |rt          |¦  «        nd}n# t          t          f$ r d}Y nw xY wt          dt          |d¦  «        ¦  «        }	 t!          t#          || |||¦  «        ¦  «        }	�n# t$          j        $ r!}
t          d|› d|
› �| ¬¦  «        cY d}
~
S d}
~
wt&          $ r(}
t          t          |
¦  «        | ¬¦  «        cY d}
~
S d}
~
wt(          $ r(}
t          t          |
¦  «        | ¬¦  «        cY d}
~
S d}
~
wt*          $ r"}
t          d|› d|
› d�| ¬¦  «        cY d}
~
S d}
~
wt,          $ rM}
t.                               d¦  «         t          dt          |
¦  «        j
        › d|
› �| ¬¦  «        cY d}
~
S d}
~
ww xY wd| |	dœ}|r||d<   t3          j        |d¬¦  «        S )un  Send a raw CDP command.  See ``CDP_DOCS_URL`` for method documentation.

    Args:
        method: CDP method name, e.g. ``"Target.getTargets"``.
        params: Method-specific parameters; defaults to ``{}``.
        target_id: Optional target/tab ID for page-level methods.  When set,
            we first attach to the target (``flatten=True``) and send
            ``method`` with the resulting ``sessionId``.  Uses a fresh
            stateless CDP connection.
        frame_id: Optional cross-origin (OOPIF) iframe ``frame_id`` from
            ``browser_snapshot.frame_tree.children[]``.  When set (and the
            frame is an OOPIF with a live session tracked by the CDP
            supervisor), routes the call through the supervisor's existing
            WebSocket â€” which is how you Runtime.evaluate *inside* an
            iframe on backends where per-call fresh CDP connections would
            hit signed-URL expiry (Browserbase) or expensive reattach.
        timeout: Seconds to wait for the call to complete.
        task_id: Task identifier for supervisor lookup.  When ``frame_id``
            is set, this identifies which task's supervisor to use; the
            handler will default to ``"default"`` otherwise.

    Returns:
        JSON string ``{"success": True, "method": ..., "result": {...}}`` on
        success, or ``{"error": "..."}`` on failure.
    Údefault)rU   rV   r+   r,   r0   z/'method' is required (e.g. 'Target.getTargets')rd   zfThe 'websockets' Python package is required but not installed. Install it with: pip install websocketsz¶No CDP endpoint is available. Run '/browser connect' to attach to a running Chrome, or set 'browser.cdp_url' in config.yaml. The Camofox backend is REST-only and does not expose CDP.)zws://zwss://z%CDP endpoint is not a WebSocket URL: u™   . Expected ws://... or wss://... â€” the /browser connect resolver should have rewritten this. Check that Chrome is actually listening on the debug port.z%'params' must be an object/dict, got r|   g      ð?g     Àr@zCDP call timed out after zs: )r+   Nz"WebSocket error talking to CDP at rc   uE   . The browser may have disconnected â€” try '/browser connect' again.zbrowser_cdp unexpected errorzUnexpected error: T)rf   r+   r   r.   Frg   )r{   Ú
isinstancer   r   rs   Ú_WS_AVAILABLEr)   Ú
startswithÚdictrq   rr   r1   Ú	TypeErrorÚ
ValueErrorÚmaxÚminr   rT   r   rE   r   r	   r%   r&   Ú	exceptionrA   rB   )r+   r,   r.   rV   r0   rU   ÚendpointÚcall_paramsÚsafe_timeoutr   r(   rz   s               r   Úbrowser_cdpr‹   (  sì  € ðD ð 
Ý*ØÐ(˜yØØØØð
ñ 
ô 
ð 	
ð 	àð 
� F­CÑ0Ô0ð 
ÝØ=Ý!ð
ñ 
ô 
ð 	
õ
 ð 
Ýð6ñ
ô 
ð 	
õ
 %Ñ&Ô&€HØð 
ÝðHõ "ð	
ñ 
ô 
ð 	
ð ×ÒÐ2Ñ3Ô3ð 
Ýð4°Hð 4ð 4ð 4ñ
ô 
ð 	
ð #) ,¨B€KÝ�k¥4Ñ(Ô(ð 
ÝØPµD¸Ñ4EÔ4EÔ4NÐPÐPñ
ô 
ð 	
ðØ)0Ð:•u˜W‘~”~�~°dˆˆøÝ•zÐ"ð ð ð Øˆˆˆðøøøå�s�C ¨eÑ4Ô4Ñ5Ô5€Lð
ÝÝ�h ¨°YÀÑMÔMñ
ô 
ˆ‰øõ Ôð 
ð 
ð 
ÝØ>¨Ð>Ð>¸Ð>Ð>Øð
ñ 
ô 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
øøøøõ ð 3ð 3ð 3Ý�#˜c™(œ(¨6Ð2Ñ2Ô2Ð2Ð2Ð2Ð2Ð2Ð2øøøøÝð 3ð 3ð 3Ý�#˜c™(œ(¨6Ð2Ñ2Ô2Ð2Ð2Ð2Ð2Ð2Ð2øøøøÝð 
ð 
ð 
ÝðN°ð Nð N¸Sð Nð Nð Nàð
ñ 
ô 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
øøøøõ
 ð 
ð 
ð 
Ý×ÒÐ7Ñ8Ô8Ð8ÝØ<¥ c¡¤Ô!3Ð<Ð<°sÐ<Ð<Øð
ñ 
ô 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
øøøøð
øøøð ØØðð €Gð
 ð )Ø(ˆ�ÑÝŒ:�g¨EÐ2Ñ2Ô2Ð2sy   Ã)C= Ã=DÄDÄ5 E ÅI&Å&FÅ<I&ÆI&ÆF2Æ,I&Æ2I&Æ?G"ÇI&Ç"I&Ç/HÈI&ÈI&ÈAI!ÉI&É!I&r‹   uz  Send a raw Chrome DevTools Protocol (CDP) command. Escape hatch for browser operations not covered by browser_navigate, browser_click, browser_console, etc.

**Requires a reachable CDP endpoint.** Available when the user has run '/browser connect' to attach to a running Chrome, or when 'browser.cdp_url' is set in config.yaml. Not currently wired up for cloud backends (Browserbase, Browser Use, Firecrawl) â€” those expose CDP per session but live-session routing is a follow-up. Camofox is REST-only and will never support CDP. If the tool is in your toolset at all, a CDP endpoint is already reachable.

**CDP method reference:** uœ   â€” use web_extract on a method's URL (e.g. '/tot/Page/#method-handleJavaScriptDialog') to look up parameters and return shape.

**Common patterns:**
- List tabs: method='Target.getTargets', params={}
- Handle a native JS dialog: method='Page.handleJavaScriptDialog', params={'accept': true, 'promptText': ''}, target_id=<tabId>
- Get all cookies: method='Network.getAllCookies', params={}
- Eval in a specific tab: method='Runtime.evaluate', params={'expression': '...', 'returnByValue': true}, target_id=<tabId>
- Set viewport for a tab: method='Emulation.setDeviceMetricsOverride', params={'width': 1280, 'height': 720, 'deviceScaleFactor': 1, 'mobile': false}, target_id=<tabId>

**Usage rules:**
- Browser-level methods (Target.*, Browser.*, Storage.*): omit target_id and frame_id.
- Page-level methods (Page.*, Runtime.*, DOM.*, Emulation.*, Network.* scoped to a tab): pass target_id from Target.getTargets.
- **Cross-origin iframe scope** (Runtime.evaluate inside an OOPIF, Page.* targeting a frame target, etc.): pass frame_id from the browser_snapshot frame_tree output. This routes through the CDP supervisor's live connection â€” the only reliable way on Browserbase where stateless CDP calls hit signed-URL expiry.
- Each stateless call (without frame_id) is independent â€” sessions and event subscriptions do not persist between calls. For stateful workflows, prefer the dedicated browser tools or use frame_id routing.ÚobjectÚstringz]CDP method name, e.g. 'Target.getTargets', 'Runtime.evaluate', 'Page.handleJavaScriptDialog'.)rq   ÚdescriptionzaMethod-specific parameters as a JSON object. Omit or pass {} for methods that take no parameters.)rq   rŽ   Ú
propertiesÚadditionalPropertiesz©Optional. Target/tab ID from Target.getTargets result (each entry's 'targetId'). Use for page-level methods at the top-level tab scope. Mutually exclusive with frame_id.aà  Optional. Out-of-process iframe (OOPIF) frame_id from browser_snapshot.frame_tree.children[] where is_oopif=true. When set, routes the call through the CDP supervisor's live session for that iframe. Essential for Runtime.evaluate inside cross-origin iframes, especially on Browserbase where fresh per-call CDP connections can't keep up with signed URL rotation. For same-origin iframes, use parent contentWindow/contentDocument from Runtime.evaluate at the top-level page instead.Únumberz)Timeout in seconds (default 30, max 300).é   )rq   rŽ   r~   )r+   r,   r.   rV   r0   )rq   r�   Úrequired)ÚnamerŽ   Ú
parametersÚBROWSER_CDP_SCHEMAÚboolc                 óÂ   — 	 ddl m} m} n3# t          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w |¦   «         sdS t           | ¦   «         ¦  «        S )u*  Availability check for browser_cdp.

    The tool is only offered when the Python side can actually reach a CDP
    endpoint right now â€” meaning a static URL is set via ``/browser connect``
    (``BROWSER_CDP_URL``) or ``browser.cdp_url`` in ``config.yaml``.

    Backends that do *not* currently expose CDP to us â€” Camofox (REST-only),
    the default local agent-browser mode (Playwright hides its internal CDP
    port), and cloud providers whose per-session ``cdp_url`` is not yet
    surfaced â€” are gated out so the model doesn't see a tool that would
    reliably fail.  Cloud-provider CDP routing is a follow-up.

    Kept in a thin wrapper so the registration statement stays at module top
    level (the tool-discovery AST scan only picks up top-level
    ``registry.register(...)`` calls).
    r   )r!   Úcheck_browser_requirementsz1browser_cdp check: browser_tool import failed: %sNF)r#   r!   r™   ÚImportErrorr&   r'   r—   )r!   r™   r(   s      r   Ú_browser_cdp_checkr›     s§   € ð"ð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
øõ ð ð ð Ý�ŠÐHÈ#ÑNÔNÐNØˆuˆuˆuˆuˆuøøøøðøøøð &Ð%Ñ'Ô'ð ØˆuÝÐ!Ð!Ñ#Ô#Ñ$Ô$Ð$s   ‚ ‹
;•6¶;zbrowser-cdpc           
     ó  — t          |                      dd¦  «        |                      d¦  «        |                      d¦  «        |                      d¦  «        |                      dd¦  «        |                     d¦  «        ¬	¦  «        S )
Nr+   r"   r,   r.   rV   r0   r|   rU   )r+   r,   r.   rV   r0   rU   )r‹   rI   )ÚargsÚkws     r   ú<lambda>rŸ   *  ss   € �{Ø�xŠx˜ "Ñ%Ô%Ø�xŠx˜Ñ!Ô!Ø—(’(˜;Ñ'Ô'Ø—’˜*Ñ%Ô%Ø—’˜ DÑ)Ô)Ø—’�yÑ!Ô!ð ñ  ô  € ra   u   ðŸ§ª)r”   ÚtoolsetÚschemaÚhandlerÚcheck_fnÚemoji)r   r   )r*   r   r+   r   r,   r-   r.   r/   r0   r1   r   r-   )rU   r   rV   r   r+   r   r,   rW   r0   r1   r   r   )NNNr|   N)r+   r   r,   rW   r.   r/   rV   r/   r0   r1   rU   r/   r   r   )r   r—   )!Ú__doc__Ú
__future__r   r   rA   ÚloggingÚosÚtypingr   r   r   Útools.registryr   r   Ú	getLoggerrr   r&   rs   r>   Úwebsockets.exceptionsr	   r€   rš   r%   r   r)   rT   r{   r‹   r–   Ú__annotations__r›   Úregister© ra   r   ú<module>r°      s¨  ððð ð ð  #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø €€€Ø 	€	€	€	Ø &Ð &Ð &Ð &Ð &Ð &Ð &Ð &Ð &Ð &à /Ð /Ð /Ð /Ð /Ð /Ð /Ð /à	ˆÔ	˜8Ñ	$Ô	$€àD€ð
ØÐÐÐØ8Ð8Ð8Ð8Ð8Ð8à€M€MøØð ð ð Ø€JØ"ÐØ€M€M€Mðøøøðð ð ð*ð ð ð ð.W-ð W-ð W-ð W-ð@f3ð f3ð f3ð f3ðV (,Ø#Ø"ØØ!ðv3ð v3ð v3ð v3ð v3ð~ ð%	ð &2ð%	ð %	ð %	ðP ð !ðIðð ð !ðCð !Ø(,ðð ð !ð ðð ð !ð	5ðð ð  !à?àðð ðS0
ð 0
ðb �Jðg4ð 4ðU_&ð _&Ð ð _ð _ð _ñ _ðD%ð %ð %ð %ð< €Ô Ø	ØØðð ð  Ø
ðñ ô ð ð ð s   ¿A ÁAÁA