
    (Gj                        U 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mZ  ej        e          Zdej        defdZde	defd	Zdd
ee	         dee         fdZ G d d          ZdZdZi Zeeeeef         f         ed<   i Z eeef         ed<    ej!                    Z"dedefdZ#ddZ$ G d d          Z% e%            Z&defdZ'ddefdZ(dS )aV  Central registry for all hermes-agent tools.

Each tool file calls ``registry.register()`` at module level to declare its
schema, handler, toolset membership, and availability check.  ``model_tools.py``
queries the registry instead of maintaining its own parallel data structures.

Import chain (circular-import safe):
    tools/registry.py  (no imports from model_tools or tool files)
           ^
    tools/*.py  (import from tools.registry at module level)
           ^
    model_tools.py  (imports tools.registry + all tool modules)
           ^
    run_agent.py, cli.py, batch_runner.py, etc.
    N)Path)CallableDictListOptionalSetnodereturnc                 8   t          | t          j                  rt          | j        t          j                  sdS | j        j        }t          |t          j                  o9|j        dk    o.t          |j        t          j                  o|j        j	        dk    S )zHReturn True when *node* is a ``registry.register(...)`` call expression.Fregisterregistry)

isinstanceastExprvalueCallfunc	AttributeattrNameid)r	   r   s     4/home/rurouni/.hermes/hermes-agent/tools/registry.py_is_registry_register_callr      s    dCH%% Z
CH-M-M u:?D4'' 	(I#	(tz38,,	( JMZ'	    module_pathc                     	 |                      d          }t          j        |t          |                     }n# t          t
          f$ r Y dS w xY wt          d |j        D                       S )zReturn True when the module contains a top-level ``registry.register(...)`` call.

    Only inspects module-body statements so that helper modules which happen
    to call ``registry.register()`` inside a function are not picked up.
    zutf-8)encoding)filenameFc              3   4   K   | ]}t          |          V  d S N)r   ).0stmts     r   	<genexpr>z*_module_registers_tools.<locals>.<genexpr>7   s+      FFD)$//FFFFFFr   )	read_textr   parsestrOSErrorSyntaxErroranybody)r   sourcetrees      r   _module_registers_toolsr-   +   s    &&&88y#k*:*:;;;[!   uu FFDIFFFFFFs   9< AA	tools_dirc                    | t          |           n*t          t                                                    j        }d t	          |                    d                    D             }g }|D ]_}	 t          j        |           |                    |           -# t          $ r&}t                              d||           Y d}~Xd}~ww xY w|S )zLImport built-in self-registering tool modules and return their module names.Nc                 R    g | ]$}|j         d vt          |          d|j         %S )>   __init__.pymcp_tool.pyregistry.pyztools.)namer-   stem)r!   paths     r   
<listcomp>z*discover_builtin_tools.<locals>.<listcomp>=   sK       9III#D)) J 	IIIr   z*.pyz#Could not import tool module %s: %s)r   __file__resolveparentsortedglob	importlibimport_moduleappend	Exceptionloggerwarning)r.   
tools_pathmodule_namesimportedmod_namees         r   discover_builtin_toolsrH   :   s    $-$9itH~~?U?U?W?W?^J :??62233  L H  O O	O#H---OOH%%%% 	O 	O 	ONN@(ANNNNNNNN	OOs   0)B
C
$CC
c                        e Zd ZdZdZ	 ddZdS )	ToolEntryz&Metadata for a single registered tool.r4   toolsetschemahandlercheck_fnrequires_envis_asyncdescriptionemojimax_result_size_charsdynamic_schema_overridesNc                     || _         || _        || _        || _        || _        || _        || _        || _        |	| _        |
| _	        || _
        d S r    rK   )selfr4   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   s               r   __init__zToolEntry.__init__W   s^     	 ( &
%:" )A%%%r   )NN)__name__
__module____qualname____doc__	__slots__rX    r   r   rJ   rJ   N   sB        00I GKA A A A A Ar   rJ   g      >@g      N@_check_fn_cache_check_fn_last_goodfnc           	          t          j                    }t          5  t                              |           }|!|\  }}||z
  t
          k     r|cddd           S ddd           n# 1 swxY w Y   d}	 t           |                       }n# t          $ r d}d}Y nw xY wt          5  |r$|t          | <   |dft          | <   	 ddd           dS t                              |           }|Q||z
  t          k     rCt                              dt          | d|           |rdndt                     	 ddd           dS t                              dt          | d|           |rdnd           |dft          | <   	 ddd           dS # 1 swxY w Y   dS )	a  Return bool(fn()), TTL-cached across calls.

    Exceptions are swallowed as False. A transient False/exception within
    ``_CHECK_FN_FAILURE_GRACE_SECONDS`` of the last True is suppressed (the
    last-good True is returned and the failure is NOT cached, so the next call
    re-probes) to keep flaky external checks (Docker daemon busy, socket
    contention, probe timeout) from silently stripping tools mid-session.
    NFTzicheck_fn %s failed (%s) within %.0fs of last success; treating as transient and keeping tool(s) availabler[   raisedzreturned Falsez=check_fn %s %s; dependent tools will be unavailable this turn)time	monotonic_check_fn_cache_lockr_   get_CHECK_FN_TTL_SECONDSboolr@   r`   _CHECK_FN_FAILURE_GRACE_SECONDSrA   rB   getattr)ra   nowcachedtsr   rc   	last_goods          r   _check_fn_cachedrp      s    .

C	   $$R((IBRx///                       FRRTT

    
   	&)##&+OB	        (++B//	 S9_7V%V%V NNFNB//"8(8/   %       , 	KB++4HH$4	
 	
 	

  #El9                 sB   1A%%A),A)3B BB&FA F:<FF
Fc                      t           5  t                                           t                                           ddd           dS # 1 swxY w Y   dS )zDrop all cached ``check_fn`` results. Call after config changes that
    affect tool availability (e.g. ``hermes tools enable``).N)rf   r_   clearr`   r^   r   r   invalidate_check_fn_cachers      s     
 $ $!!###$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $s   3AAAc                   4   e Zd ZdZd Zdeee         ee	e
f         f         fdZdee         fdZde	dee         defdZd	e	dee         fd
Zdee	         fdZde	dee	         fdZde	de	ddfdZdee	e	f         fdZde	dee	         fdZde	deddfdZde
dee	         fdZede	fd            Z	 	 	 	 	 	 	 	 d7d	e	de	dede
de
dedede	de	d eez  dz  d!e
d"efd#Zd	e	ddfd$Zd8d%e e	         d&edee         fd'Z!d	e	d(ede	fd)Z"d9d	e	d*eez  dz  deez  fd+Z#dee	         fd,Z$d	e	dee         fd-Z%d	e	dee	         fd.Z&d:d	e	d*e	de	fd0Z'dee	e	f         fd1Z(de	defd2Z)dee	ef         fd3Z*dee	ef         fd4Z+dee	ef         fd5Z,d8d&efd6Z-dS );ToolRegistryzISingleton registry that collects tool schemas + handlers from tool files.c                 |    i | _         i | _        i | _        i | _        t	          j                    | _        d| _        d S )Nr   )_tools_plugin_override_policy_toolset_checks_toolset_aliases	threadingRLock_lock_generationrW   s    r   rX   zToolRegistry.__init__   sD    ,. 9;$4602 _&&
 !"r   r
   c                     | j         5  t          | j                                                  t	          | j                  fcddd           S # 1 swxY w Y   dS )zBReturn a coherent snapshot of registry entries and toolset checks.N)r}   listrw   valuesdictry   r   s    r   _snapshot_statezToolRegistry._snapshot_state   s    Z 	J 	J**,,--tD4H/I/II	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	Js   :AAAc                 6    |                                  d         S )z4Return a stable snapshot of registered tool entries.r   )r   r   s    r   _snapshot_entrieszToolRegistry._snapshot_entries   s    ##%%a((r   rL   entriesc                     i }|D ]M}|j         |k    r|j        s dS |j        |vrt          |j                  ||j        <   ||j                 r dS NdS )a{  Return True when at least one tool in *toolset* would be exposed.

        Mirrors :meth:`get_tool_definitions` per-tool filtering so doctor,
        banners, and other toolset-level surfaces agree with runtime exposure.
        Mixed toolsets (e.g. ``terminal`` plus desktop-only ``read_terminal``)
        must not be gated solely by the first registered ``check_fn``.
        TF)rL   rO   rp   )rW   rL   r   check_resultsentrys        r   _toolset_has_exposable_toolsz)ToolRegistry._toolset_has_exposable_tools   s|     /1 	 	E}''> tt~]220@0P0Pen-U^, ttur   r4   c                 x    | j         5  | j                            |          cddd           S # 1 swxY w Y   dS )z0Return a registered tool entry by name, or None.N)r}   rw   rg   )rW   r4   s     r   	get_entryzToolRegistry.get_entry	  s    Z 	) 	);??4((	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	)   /33c                 X    t          d |                                 D                       S )z;Return sorted unique toolset names present in the registry.c                     h | ]	}|j         
S r^   rL   r!   r   s     r   	<setcomp>z<ToolRegistry.get_registered_toolset_names.<locals>.<setcomp>  s    KKKu}KKKr   r;   r   r   s    r   get_registered_toolset_namesz)ToolRegistry.get_registered_toolset_names  s+    KK$2H2H2J2JKKKLLLr   c                 ^    t          fd|                                 D                       S )z:Return sorted tool names registered under a given toolset.c              3   <   K   | ]}|j         k    |j        V  d S r    )rL   r4   )r!   r   rL   s     r   r#   z:ToolRegistry.get_tool_names_for_toolset.<locals>.<genexpr>  s>       
 
 }'' J''''
 
r   r   )rW   rL   s    `r   get_tool_names_for_toolsetz'ToolRegistry.get_tool_names_for_toolset  sG     
 
 
 
$($:$:$<$<
 
 
 
 
 	
r   aliasNc                     | j         5  | j                            |          }|r#||k    rt                              d|||           || j        |<   | xj        dz  c_        ddd           dS # 1 swxY w Y   dS )z8Register an explicit alias for a canonical toolset name.z4Toolset alias collision: '%s' (%s) overwritten by %s   N)r}   rz   rg   rA   rB   r~   )rW   r   rL   existings       r   register_toolset_aliasz#ToolRegistry.register_toolset_alias  s    Z 	" 	",0077H H//J8W   ,3D!%(!	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	"s   AA//A36A3c                 l    | j         5  t          | j                  cddd           S # 1 swxY w Y   dS )z=Return a snapshot of ``{alias: canonical_toolset}`` mappings.N)r}   r   rz   r   s    r   get_registered_toolset_aliasesz+ToolRegistry.get_registered_toolset_aliases%  s|    Z 	/ 	/-..	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/s   )--c                 x    | j         5  | j                            |          cddd           S # 1 swxY w Y   dS )z8Return the canonical toolset name for an alias, or None.N)r}   rz   rg   )rW   r   s     r   get_toolset_alias_targetz%ToolRegistry.get_toolset_alias_target*  s    Z 	4 	4(,,U33	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4r   module_namespaceallowedc                 t    | j         5  t          |          | j        |<   ddd           dS # 1 swxY w Y   dS )a  Bind a plugin module namespace to its operator opt-in for built-in
        override. Called once per plugin at load time. Durable: never cleared,
        so later (even threaded/delayed) register() calls from that module are
        still gated by the same policy.
        N)r}   ri   rx   )rW   r   r   s      r   register_plugin_override_policyz,ToolRegistry.register_plugin_override_policy3  s     Z 	K 	K=A']]D()9:	K 	K 	K 	K 	K 	K 	K 	K 	K 	K 	K 	K 	K 	K 	K 	K 	K 	Ks   -11rN   c                     	 |j                             dd          }n# t          $ r Y dS w xY w|| j        v r|S t	          |t
                    r|                    d          r|S dS )aS  Return the plugin module namespace that defined *handler*, or None
        if it was not defined in a loaded plugin module.

        Authorization is bound to where the handler was DEFINED
        (``handler.__globals__["__name__"]``), which is fixed at definition
        time and cannot drift with the call site, thread, or timing. Lambdas
        and nested functions inherit the defining module's globals, so a
        plugin cannot launder an override through a callback. Built-in/MCP
        handlers live outside the plugin namespace and return None (unchanged
        behavior).
        rY    Nhermes_plugins.)__globals__rg   AttributeErrorrx   r   r&   
startswith)rW   rN   mods      r   _plugin_owner_ofzToolRegistry._plugin_owner_of<  s    	%))*b99CC 	 	 	44	$...J c3 	CNN3D$E$E 	Jts    
,,c                      	 t          j        d          } | j                            dd          pdS # t          $ r Y dS w xY w)u  Best-effort module name of whoever called the registry method that
        invoked this helper (two frames up: this helper, then the registry
        method itself, then the actual caller).

        ``deregister()`` takes only a tool name — unlike ``register()`` it has
        no handler argument to bind authorization to via ``_plugin_owner_of``.
        Frame inspection is the only way to know who is asking.
           rY   r   )sys	_getframe	f_globalsrg   r@   )frames    r   _caller_modulezToolRegistry._caller_moduleT  sU    	M!$$E?&&z266<"< 	 	 	22	s   03 
A AFr   rM   rO   rP   rQ   rR   rS   rT   rU   overridec                 |   | j         5  | j                            |          }|r|j        |k    r|j                            d          o|                    d          }|r#t
                              d|||j                   n|r|                     |          }|S| j                            |d          s8t
          	                    d|||j                   t          d|d|d          t
                              d	|||j                   n0t
          	                    d
|||j                   	 ddd           dS t          ||||||pg ||p|                    dd          |	|
|          | j        |<   |r|| j        vr
|| j        |<   | xj        dz  c_        ddd           dS # 1 swxY w Y   dS )a  Register a tool.  Called at module-import time by each tool file.

        ``override=True`` is an explicit opt-in for plugins that intend to
        replace an existing built-in tool implementation (e.g. swap the
        default browser tool for a headed-Chrome CDP backend). Without it,
        registrations that would shadow an existing tool from a different
        toolset are rejected to prevent accidental overwrites.
        mcp-z8Tool '%s': MCP toolset '%s' overwriting MCP toolset '%s'NFzTool registration REJECTED: plugin %r attempted to override built-in tool %r (existing toolset %r) without operator opt-in. Set plugins.entries.<plugin_id>.allow_tool_override: true in config.yaml to allow it.Plugin module z cannot override built-in tool z/ without operator opt-in (allow_tool_override).zOTool '%s': toolset '%s' overriding existing toolset '%s' (override=True opt-in)zTool registration REJECTED: '%s' (toolset '%s') would shadow existing tool from toolset '%s'. Pass override=True to register() if the replacement is intentional, or deregister the existing tool first.rR   r   rK   r   )r}   rw   rg   rL   r   rA   debugr   rx   errorPermissionErrorinforJ   ry   r~   )rW   r4   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   r   r   both_mcp_owners                   r   r   zToolRegistry.registerd  s   . Z E	" E	"{t,,H -H,77 $//77 3**622   &LLRgx'7     !!227;;F)$2N2R2RSY[`2a2a):
 #D(*:   .6V 6 6$(6 6 6   KK1gx'7    LLN gx'7   _E	" E	" E	" E	" E	" E	" E	" E	"` !*!)/R!'H6::mR+H+H&;)A! ! !DK&  9G4+???08$W-!KE	" E	" E	" E	" E	" E	" E	" E	" E	" E	" E	" E	" E	" E	" E	" E	" E	" E	"s   D0F1AF11F58F5c           
      f   | j         5  | j                            |          	 ddd           dS j                            d          s|                                 }|                     j                  }d                    |	                    d          dd                   }|r0d                    |	                    d          dd                   nd}t          |o||k              }|                    d          r^|s\| j                            |d          sAt                              d||j        |           t          d	|d
|dj        d          | j        |= t          fd| j                                        D                       }|sJ| j                            j        d           fd| j                                        D             | _        | xj        dz  c_        ddd           n# 1 swxY w Y   t                              d|           dS )uT  Remove a tool from the registry.

        Also cleans up the toolset check if no other tools remain in the
        same toolset.  Used by MCP dynamic tool discovery to nuke-and-repave
        when a server sends ``notifications/tools/list_changed``.

        Gated by the same operator opt-in policy ``register(override=True)``
        enforces. Without this, a plugin could bypass that gate entirely by
        deregistering a tool it doesn't own and then calling plain
        ``register()`` over the now-empty slot — ``register()`` only runs its
        override check when an ``existing`` entry is present, so removing it
        first skips the check altogether. MCP toolsets (``mcp-*``) are exempt:
        dynamic tool discovery legitimately nukes-and-repaves its own tools on
        every refresh and has no plugin-override concept.
        Nr   .r   r   r   FzTool deregistration REJECTED: plugin %r attempted to remove tool %r (toolset %r) it does not own, without operator opt-in. Set plugins.entries.%s.allow_tool_override: true in config.yaml to allow it.r   z cannot deregister tool z
 (toolset z0) without operator opt-in (allow_tool_override).c              3   8   K   | ]}|j         j         k    V  d S r    r   )r!   rG   r   s     r   r#   z*ToolRegistry.deregister.<locals>.<genexpr>  s=       ' '/0	U]*' ' ' ' ' 'r   c                 2    i | ]\  }}|j         k    ||S r^   r   )r!   r   targetr   s      r   
<dictcomp>z+ToolRegistry.deregister.<locals>.<dictcomp>  s6     ) ) )%v.. 6...r   r   zDeregistered tool: %s)r}   rw   rg   rL   r   r   r   rN   joinsplitri   rx   rA   r   r   r)   r   ry   poprz   itemsr~   r   )	rW   r4   
caller_modownercaller_root
owner_rootsame_plugintoolset_still_existsr   s	           @r   
deregisterzToolRegistry.deregister  s     Z 0	" 0	"KOOD))E}0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" =++F33 !0022
--em<< "hhz'7'7'<'<RaR'@AA?DLSXXekk#&6&6rr&:;;;"
"5#F[J-FGG))*;<<' !8<<[%PP
 LL3
 #D%-   *9 9 99 9-2]9 9 9  
 D! $' ' ' ' '48K4F4F4H4H' ' ' $ $  ( $((===) ) ) ))-)>)D)D)F)F) ) )%
 !a0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	"b 	,d33333s   HGHHH
tool_namesquietc                    g }i }d |                                  D             }t          |          D ]}|                    |          }|s|j        rP|j        |vrt	          |j                  ||j        <   ||j                 s|st
                              d|           ri |j        d|j        i}|j	        s	 |	                                }	t          |	t                    r|                    |	           n3# t          $ r&}
t
                              d||
           Y d}
~
nd}
~
ww xY w|                    d|d           |S )a"  Return OpenAI-format tool schemas for the requested tool names.

        Only tools whose ``check_fn()`` returns True (or have no check_fn)
        are included. ``check_fn()`` results are cached for ~30 s via
        :func:`_check_fn_cached` to amortize repeat probes (check_terminal_
        requirements probes modal/docker, browser checks probe playwright,
        etc.); TTL chosen so env-var changes (``hermes tools enable foo``)
        still take effect in near-real-time without forcing a full cache
        flush on every call.
        c                     i | ]
}|j         |S r^   r4   r   s     r   r   z0ToolRegistry.get_definitions.<locals>.<dictcomp>  s    SSS5:uSSSr   z"Tool %s unavailable (check failed)r4   NzCdynamic_schema_overrides for tool %s raised %s; using static schemafunction)typer   )r   r;   rg   rO   rp   rA   r   rM   r4   rU   r   r   updater@   rB   r?   )rW   r   r   resultr   entries_by_namer4   r   schema_with_name	overridesexcs              r   get_definitionszToolRegistry.get_definitions	  s     /1SS$:P:P:R:RSSS:&& 	N 	ND#''--E ~ >664DU^4T4TM%.1$U^4   Q%I4PPPC%,C
CC -9	 % > > @ @I!)T22 ;(//	:::    NN.c        MM:;KLLMMMMs   >>C==
D-D((D-argsc                    |                      |          }|st          j        dd| i          S 	 |j        rddlm}  | |j        |fi |          S  |j        |fi |S # t          $ r{}t          	                    d||           dt          |          j         d| }	 ddlm}  ||          }	n# t          $ r |}	Y nw xY wt          j        d|	i          cY d	}~S d	}~ww xY w)
zExecute a tool handler by name.

        * Async handlers are bridged automatically via ``_run_async()``.
        * All exceptions are caught and returned as ``{"error": "..."}``
          for consistent error format.
        r   zUnknown tool: r   )
_run_asynczTool %s dispatch error: %szTool execution failed: z: )_sanitize_tool_errorN)r   jsondumpsrQ   model_toolsr   rN   r@   rA   	exceptionr   rY   r   )
rW   r4   r   kwargsr   r   rG   rawr   	sanitizeds
             r   dispatchzToolRegistry.dispatch>  sf    t$$ 	B:w(?(?(?@AAA	4~ A222222!z-%-"?"?"?"?@@@ 5=00000 	4 	4 	494CCC DDGG,<CCCCC <<<<<<0055		      			 :w	233333333	4sG   #A$ A$ $
C).6C$%B76C$7CC$CC$C)$C)defaultc                 d    |                      |          }|r|j        |j        S ||S ddlm} |S )zBReturn per-tool max result size, or *default* (or global default).Nr   )DEFAULT_RESULT_SIZE_CHARS)r   rT   tools.budget_configr   )rW   r4   r   r   r   s        r   get_max_result_sizez ToolRegistry.get_max_result_size^  sO    t$$ 	/U0<..NAAAAAA((r   c                 X    t          d |                                 D                       S )z0Return sorted list of all registered tool names.c              3   $   K   | ]}|j         V  d S r    r   r   s     r   r#   z2ToolRegistry.get_all_tool_names.<locals>.<genexpr>j  s$      GGUejGGGGGGr   r   r   s    r   get_all_tool_nameszToolRegistry.get_all_tool_namesh  s+    GGd.D.D.F.FGGGGGGr   c                 B    |                      |          }|r|j        ndS )u   Return a tool's raw schema dict, bypassing check_fn filtering.

        Useful for token estimation and introspection where availability
        doesn't matter — only the schema content does.
        N)r   rM   rW   r4   r   s      r   
get_schemazToolRegistry.get_schemal  s&     t$$$.u||$.r   c                 B    |                      |          }|r|j        ndS )z.Return the toolset a tool belongs to, or None.N)r   rL   r   s      r   get_toolset_for_toolz!ToolRegistry.get_toolset_for_toolu  s$    t$$ %/u}}4/r      ⚡c                 P    |                      |          }|r|j        r|j        n|S )z3Return the emoji for a tool, or *default* if unset.)r   rS   )rW   r4   r   r   s       r   	get_emojizToolRegistry.get_emojiz  s+    t$$$AA'Br   c                 >    d |                                  D             S )z?Return ``{tool_name: toolset_name}`` for every registered tool.c                 (    i | ]}|j         |j        S r^   )r4   rL   r   s     r   r   z8ToolRegistry.get_tool_to_toolset_map.<locals>.<dictcomp>  s    PPPe
EMPPPr   )r   r   s    r   get_tool_to_toolset_mapz$ToolRegistry.get_tool_to_toolset_map  s"    PPt7M7M7O7OPPPPr   c                 \    |                                  \  }}|                     ||          S )zCheck if a toolset has at least one exposable tool.

        Returns False (rather than crashing) when a per-tool check raises
        an unexpected exception (e.g. network error, missing import, bad config).
        )r   r   )rW   rL   r   _s       r   is_toolset_availablez!ToolRegistry.is_toolset_available  s/     ))++
00'BBBr   c                                                        \  }t          d D                       } fd|D             S )z7Return ``{toolset: available_bool}`` for every toolset.c                     h | ]	}|j         
S r^   r   r   s     r   r   z:ToolRegistry.check_toolset_requirements.<locals>.<setcomp>  s    >>>U5=>>>r   c                 >    i | ]}|                     |          S r^   )r   )r!   rL   r   rW   s     r   r   z;ToolRegistry.check_toolset_requirements.<locals>.<dictcomp>  s;     
 
 
 T66wHH
 
 
r   )r   r;   )rW   r   toolsetsr   s   `  @r   check_toolset_requirementsz'ToolRegistry.check_toolset_requirements  se    ))++
>>g>>>??
 
 
 
 
#
 
 
 	
r   c                 d   i }|                                  \  }}|D ]}|j        }||vr|                     ||          g dg d||<   ||         d                             |j                   |j        r;|j        D ]3}|||         d         vr!||         d                             |           4|S )z'Return toolset metadata for UI display.r   )	availabletoolsrR   requirementsr  r  )r   rL   r   r?   r4   rP   )rW   r  r   r   r   rn   envs          r   get_available_toolsetsz#ToolRegistry.get_available_toolsets  s    $&))++
 	A 	AEB!!!%!B!B2w!O!O#%$&	    RL!((444! A - A AC(2,~">>> ^4;;C@@@r   c                    i }|                                  \  }}|D ]}|j        }||vr|g |                    |          dg d||<   |j        ||         d         vr&||         d                             |j                   |j        D ]3}|||         d         vr!||         d                             |           4|S )zABuild a TOOLSET_REQUIREMENTS-compatible dict for backward compat.N)r4   env_varsrO   	setup_urlr  r  r  )r   rL   rg   r4   r?   rP   )rW   r   r   toolset_checksr   rn   r	  s          r   get_toolset_requirementsz%ToolRegistry.get_toolset_requirements  s    "$"&"6"6"8"8 	7 	7EB " . 2 22 6 6!% r
 zG!444r
7#**5:666) 7 7fRj4442Jz*11#6667 r   c                 R   g }g }|                                  \  }}t          d |D                       D ]nfd|D             }|                     |          r|                               <|                    |r|d         j        ng d |D             d           o||fS )zDReturn (available_toolsets, unavailable_info) like the old function.c                     h | ]	}|j         
S r^   r   r   s     r   r   z7ToolRegistry.check_tool_availability.<locals>.<setcomp>  s    ===E%-===r   c                 *    g | ]}|j         k    |S r^   r   )r!   r   rn   s     r   r7   z8ToolRegistry.check_tool_availability.<locals>.<listcomp>  s%    LLLE8K8K%8K8K8Kr   r   c                     g | ]	}|j         
S r^   r   r   s     r   r7   z8ToolRegistry.check_tool_availability.<locals>.<listcomp>  s    AAAUejAAAr   )r4   r  r  )r   r;   r   r?   rP   )rW   r   r  unavailabler   r   
ts_entriesrn   s          @r   check_tool_availabilityz$ToolRegistry.check_tool_availability  s    	))++
==W===>> 		 		BLLLLWLLLJ00W==   $$$$"">H P
1 : :bAAjAAA$ $    
 +%%r   )NNFr   r   NNF)Fr    )r   ).rY   rZ   r[   r\   rX   tupler   rJ   r   r&   r   r   r   ri   r   r   r   r   r   r   r   r   r   r   staticmethodr   r   r   intfloatr   r   r   r   r   r   r   r   r   r   r   r   r  r
  r  r  r^   r   r   ru   ru      s        SS" " "*JtIS(]8K'K!L J J J J
)4	? ) ) ) ) i 
	   0)c )hy&9 ) ) ) )
Md3i M M M M
# 
$s) 
 
 
 

"C 
"# 
"$ 
" 
" 
" 
"/S#X / / / /
4c 4hsm 4 4 4 4K Kd KW[ K K K K Xc]    0 C    \* "!48-1\" \"\" \" 	\"
 \" \" \" \" \" \"  #U{T1\" #+\" \" \" \" \"|A4s A4t A4 A4 A4 A4N/ /#c( /4 /DQUJ / / / /j4S 4 43 4 4 4 4@) ) )cEkD6H )TWZ_T_ ) ) ) )HDI H H H H/s /x~ / / / /0 0# 0 0 0 0
C Cc CC CC C C C C
Qc3h Q Q Q QCC CD C C C C
DdO 
 
 
 
S$Y    ($sDy/    *& &T & & & & & &r   ru   c                 ~    dt          |           i}|r|                    |           t          j        |d          S )zReturn a JSON error string for tool handlers.

    >>> tool_error("file not found")
    '{"error": "file not found"}'
    >>> tool_error("bad input", success=False)
    '{"error": "bad input", "success": false}'
    r   Fensure_ascii)r&   r   r   r   )messageextrar   s      r   
tool_errorr     sC     s7||$F e:f51111r   c                 ^    | t          j        | d          S t          j        |d          S )a  Return a JSON result string for tool handlers.

    Accepts a dict positional arg *or* keyword arguments (not both):

    >>> tool_result(success=True, count=42)
    '{"success": true, "count": 42}'
    >>> tool_result({"key": "value"})
    '{"key": "value"}'
    NFr  )r   r   )datar   s     r   tool_resultr#    s4     z$U3333:f51111r   r    )r
   N))r\   r   r=   r   loggingr   r{   rd   pathlibr   typingr   r   r   r   r   	getLoggerrY   rA   ASTri   r   r-   r&   rH   rJ   rh   rj   r_   r  r  __annotations__r`   Lockrf   rp   rs   ru   r   r   r#  r^   r   r   <module>r+     sl      


       



            6 6 6 6 6 6 6 6 6 6 6 6 6 6		8	$	$
SW 
 
 
 
 
G G$ G G G G htn S	    (A A A A A A A Ap   #' 68heTk 223 8 8 8-/ T(E/* / / /%y~'' 4 4d 4 4 4 4n$ $ $ $}& }& }& }& }& }& }& }&B <>>&2C 2 2 2 22 2 2 2 2 2 2 2r   