
    sj&                    (   U d Z ddlmZ ddlZddlZddlmZmZmZ ddl	m
Z
 ddlmZ  ej        e          Zi Zded<   i Zd	ed
<   dai Zded<    ej                    Zddd&dZddd'dZddd(dZddd(dZddd)dZddd*d!Zd"Zd+d$Zd,d%ZdS )-u  
Browser Provider Registry
=========================

Central map of registered cloud browser providers. Populated by plugins at
import-time via :meth:`PluginContext.register_browser_provider`; consumed by
:func:`tools.browser_tool._get_cloud_provider` to route each cloud-mode
``browser_*`` tool call to the active backend.

Active selection
----------------
The active provider is chosen by configuration with this precedence:

1. ``browser.cloud_provider`` in ``config.yaml`` (explicit override).
2. Legacy preference order — ``browser-use`` → ``browserbase`` — filtered by
   availability. Matches the historic auto-detect order in
   :func:`tools.browser_tool._get_cloud_provider` (Browser Use checked first
   because it covers both the managed Nous gateway and direct API key path;
   Browserbase as the older direct-credentials fallback). ``firecrawl`` is
   intentionally NOT in the legacy walk — users only get Firecrawl as a
   cloud browser when they explicitly set ``browser.cloud_provider:
   firecrawl``, matching pre-migration behaviour where Firecrawl was never
   auto-selected.
3. Otherwise ``None`` — the dispatcher falls back to local browser mode.

The explicit-config branch (rule 1) intentionally ignores ``is_available()``
so the dispatcher surfaces a typed "X_API_KEY is not set" error to the user
instead of silently switching backends. Matches the legacy
:func:`tools.browser_tool._get_cloud_provider` behaviour for configured names.

Note: there is no "capability" split here (unlike the web subsystem, which
has search/extract/crawl). Every browser provider implements the full
:class:`agent.browser_provider.BrowserProvider` lifecycle; the registry's
job is purely selection, not capability routing.
    )annotationsN)DictListOptional)BrowserProvider)hermes_home_keyzDict[str, BrowserProvider]
_providersz%Dict[str, Dict[str, BrowserProvider]]_scoped_providerszDict[str, int]_scoped_generations)scopeproviderr   r   Optional[str]returnNonec                  t          | t                    s$t          dt          |           j                   | j        }t          |t                    r|                                st          d          |                                }t          5  |t          nt                              |i           }|                    |          }| ||<   |t          dz  an&t                              |d          dz   t          |<   ddd           n# 1 swxY w Y   |0t                               d|t          |          j                   dS t                               d|t          |           j                   dS )u   Register a cloud browser provider.

    Re-registration (same ``name``) overwrites the previous entry and logs
    a debug message — makes hot-reload scenarios (tests, dev loops) behave
    predictably.
    z<register_provider() expects a BrowserProvider instance, got z1Browser provider .name must be a non-empty stringN   r   z,Browser provider '%s' re-registered (was %r)z%Registered browser provider '%s' (%s))
isinstancer   	TypeErrortype__name__namestrstrip
ValueError_lockr	   r
   
setdefaultget_generationr   loggerdebug)r   r   raw_namer   targetexistings         :/home/agent/.hermes/hermes-agent/agent/browser_registry.pyregister_providerr%   8   s    h00 
->>*- -
 
 	
 }Hh$$ NHNN,<,< NLMMM>>D	 O O$}2C2N2NuVX2Y2Y::d##t=1KK)<)@)@)J)JQ)N&O O O O O O O O O O O O O O O :$x..)	
 	
 	
 	
 	

 	3$x..)	
 	
 	
 	
 	
s   A2DDDList[BrowserProvider]c                F   t           5  t          t                    }|                    t                              | pt                      i                      t          |                                          }ddd           n# 1 swxY w Y   t          |d           S )z0Return all registered providers, sorted by name.Nc                    | j         S N)r   )ps    r$   <lambda>z list_providers.<locals>.<lambda>c   s    qv     )key)
r   dictr	   updater
   r   r   listvaluessorted)r   mergeditemss      r$   list_providersr5   ]   s    	 & &j!!'++E,F_5F5FKKLLLV]]__%%& & & & & & & & & & & & & & & %--....s   A2BB
B
r   r   Optional[BrowserProvider]c               F   t          | t                    sdS t          5  |                                 }t                              |pt                      i                               |          pt                              |          cddd           S # 1 swxY w Y   dS )z5Return the provider registered under *name*, or None.N)r   r   r   r   r
   r   r   r	   )r   r   r-   s      r$   get_providerr8   f   s    dC   t	 e ejjll $$U%?o.?.?DDHHMMdQ[Q_Q_`cQdQde e e e e e e e e e e e e e e e e es   A*BBBc                   t           5  |t          nt                              |i           }|                    |                                           cd d d            S # 1 swxY w Y   d S r)   )r   r	   r
   r   r   )r   r   r"   s      r$   snapshot_registrationr:   o   s     
 ( ($}2C2G2Gr2R2Rzz$**,,''( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (s   AA  A$'A$tuple[int, int]c                    | pt                      }t          5  t          t                              |d          fcddd           S # 1 swxY w Y   dS )z?Return a cache fingerprint for the global base and one profile.r   N)r   r   r   r   r   )r   active_scopes     r$   registry_generationr>   w   s    -O--L	 E E/33L!DDDE E E E E E E E E E E E E E E E E Es   "AAAcurrentpreviousboolc                  |                                  }t          5  |t          nt                              |i           }|                    |          |ur	 ddd           dS ||                    |d           n|||<   |t          dz  anCt                              |d          dz   t          |<   |st                              |d           ddd           n# 1 swxY w Y   dS )zERestore a plugin registration only when *current* is still installed.NFr   r   T)	r   r   r	   r
   r   r   popr   r   )r   r?   r@   r   r-   r"   s         r$   restore_registrationrD   ~   sI    **,,C	 3 3$}2C2N2NuVX2Y2Y::c??'))3 3 3 3 3 3 3 3 JJsD!!!!"F3K=1KK)<)@)@)J)JQ)N& 3!%%eT2223 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4s   =C &A.C  C$'C$)zbrowser-usebrowserbase
configuredc                   t           5  t          t                    }|                    t                              t                      i                      ddd           n# 1 swxY w Y   d	d}| dk    rdS | r4|                    |           }||S t                              d|            t          D ](}|                    |          }| ||          r|c S )dS )
u  Resolve the active browser provider.

    Resolution rules (in order):

    1. **Explicit "local".** Returns None — the dispatcher disables cloud
       mode entirely. Mirrors legacy short-circuit in
       :func:`tools.browser_tool._get_cloud_provider`.
    2. **Explicit config wins, ignoring availability.** If ``configured``
       names a registered provider, return it even if its
       :meth:`is_available` returns False — the dispatcher will surface a
       precise "X_API_KEY is not set" error instead of silently routing
       somewhere else.
    3. **Legacy preference walk, filtered by availability.** Walk
       :data:`_LEGACY_PREFERENCE` (``browser-use`` → ``browserbase``) looking
       for a provider whose ``is_available()`` is True.

    There is intentionally NO "single-eligible shortcut" rule here (unlike
    :func:`agent.web_search_registry._resolve`). Pre-migration, the
    auto-detect branch in ``tools.browser_tool._get_cloud_provider`` only
    considered Browser Use and Browserbase; Firecrawl was reachable only
    via an explicit ``browser.cloud_provider: firecrawl`` config key.
    Preserving that gate matters because Firecrawl shares its API key with
    the *web* extract plugin (``plugins/web/firecrawl/``), so users who set
    ``FIRECRAWL_API_KEY`` for web extract must NOT get silently routed to a
    paid cloud browser on a fresh install. Third-party browser-provider
    plugins added under ``~/.hermes/plugins/browser/<vendor>/`` are subject
    to the same gate — they must be explicitly configured to take effect.

    Returns None when no provider is configured AND no available provider
    matches the legacy preference; the dispatcher then falls back to local
    browser mode.
    Nr*   r   r   rA   c                    	 t          |                                           S # t          $ r.}t                              d| j        |d           Y d}~dS d}~ww xY w)zDWrap ``is_available()`` so a buggy provider doesn't kill resolution.uH   Browser provider %s.is_available() raised %s — treating as unavailableT)exc_infoNF)rA   is_available	Exceptionr   warningr   )r*   excs     r$   _is_available_safez$_resolve.<locals>._is_available_safe   st    	(())) 	 	 	NNZd     55555	s    # 
A#AAlocalzVbrowser cloud_provider '%s' configured but not registered; falling back to auto-detect)r*   r   r   rA   )
r   r.   r	   r/   r
   r   r   r   r    _LEGACY_PREFERENCE)rF   snapshotrN   r   legacys        r$   _resolverS      sY   B 
 F F
##)--o.?.?DDEEEF F F F F F F F F F F F F F F	 	 	 	 Wt
  
<<
++O*	
 	
 	
 %  <<''$6$6x$@$@OOO4s   AA##A'*A'c                     t           5  t                                           t                                           t                                           t
          dz  addd           dS # 1 swxY w Y   dS )z"Clear the registry. **Test-only.**r   N)r   r	   clearr
   r   r    r,   r$   _reset_for_testsrW      s     
  !!!!!###q	                 s   AA++A/2A/)r   r   r   r   r   r   )r   r   r   r&   )r   r   r   r   r   r6   )r   r   r   r;   )
r   r   r?   r   r@   r6   r   r   r   rA   )rF   r   r   r6   )r   r   ) __doc__
__future__r   logging	threadingtypingr   r   r   agent.browser_providerr   hermes_constantsr   	getLoggerr   r   r	   __annotations__r
   r   r   Lockr   r%   r5   r8   r:   r>   rD   rP   rS   rW   rV   r,   r$   <module>rb      s  " " "H # " " " " "      ' ' ' ' ' ' ' ' ' ' 2 2 2 2 2 2 , , , , , ,		8	$	$ *,
 + + + +;=  = = = =&(  ( ( ( (	 LP "
 "
 "
 "
 "
 "
J .2 / / / / / / 7; e e e e e e *.( ( ( ( ( ( 37 E E E E E E       J J J J JZ     r,   