
    j                        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	m
Z
mZmZmZ ddlmZmZ ddlmZmZ  ej        e          Zda ej                    Z ej                    Zd Zd Zd Z e             	 dd	lm Z   e              n(# e!$ r Z"e#                    d
e"           Y dZ"["ndZ"["ww xY w ej$                    Z%e	e&e&f         e'd<    ej(                    Z)e	e&e*f         e'd<   g a+ee&         e'd<   ddgdgdgdgdgg dg ddgg ddgd
Z,i Z-e	e.ee	e&e
f                  f         e'd<   dHdZ/	 	 	 dIdee&         dee&         d e0dee	e&e
f                  fd!Z1	 	 	 dIdee&         dee&         d e0dee	e&e
f                  fd"Z2h d#Z3d$d%hZ4 ej5        d&ej6                  Z7 ej5        d'ej8                  Z9 ej5        d(ej8                  Z: ej5        d)ej;                  Z<d*Z=d+e&de&fd,Z>d-e&d.e	e&e
f         de	e&e
f         fd/Z?dJd0e&d1e*dz  fd2Z@d1e*dz  de0fd3ZAd0e&d4eBfd5ZCdKd0e&d6e0fd7ZDd0e&fd8ZE	 	 	 	 	 	 dLd9e&d:e	e&e
f         d;ee&         d<ee&         d=ee&         d>ee&         d?eee&                  d@e0de&fdAZFdee&         fdBZGd-e&dee&         fdCZHde	e&e*f         fdDZIde	e&e0f         fdEZJdKdFe0deee&         ee*         f         fdGZKdS )Ma  
Model Tools Module

Thin orchestration layer over the tool registry. Each tool file in tools/
self-registers its schema, handler, and metadata via tools.registry.register().
This module triggers discovery (by importing all tool modules), then provides
the public API that run_agent.py, cli.py, batch_runner.py, and the RL
environments consume.

Public API (signatures preserved from the original 2,400-line version):
    get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode) -> list
    handle_function_call(function_name, function_args, task_id, user_task) -> str
    TOOL_TO_TOOLSET_MAP: dict          (for batch_runner.py)
    TOOLSET_REQUIREMENTS: dict         (for cli.py, doctor.py)
    get_all_tool_names() -> list
    get_toolset_for_tool(name) -> str
    get_available_toolsets() -> dict
    check_toolset_requirements() -> dict
    check_tool_availability(quiet) -> tuple
    N)DictAnyListOptionalTuple)discover_builtin_toolsregistry)resolve_toolsetvalidate_toolsetc                      t           5  t          t                                          rt          j                    at          cddd           S # 1 swxY w Y   dS )a^  Return a long-lived event loop for running async tool handlers.

    Using a persistent loop (instead of asyncio.run() which creates and
    *closes* a fresh loop every time) prevents "Event loop is closed"
    errors that occur when cached httpx/AsyncOpenAI clients attempt to
    close their transport on a dead loop during garbage collection.
    N)_tool_loop_lock
_tool_loop	is_closedasyncionew_event_loop     */usr/local/lib/hermes-agent/model_tools.py_get_tool_loopr   /   s     
  !5!5!7!7 /11J                 s   :AAAc                      t          t          dd          } | |                                 r3t          j                    } t          j        |            | t          _        | S )u  Return a persistent event loop for the current worker thread.

    Each worker thread (e.g., delegate_task's ThreadPoolExecutor threads)
    gets its own long-lived loop stored in thread-local storage.  This
    prevents the "Event loop is closed" errors that occurred when
    asyncio.run() was used per-call: asyncio.run() creates a loop, runs
    the coroutine, then *closes* the loop — but cached httpx/AsyncOpenAI
    clients remain bound to that now-dead loop and raise RuntimeError
    during garbage collection or subsequent use.

    By keeping the loop alive for the thread's lifetime, cached clients
    stay valid and their cleanup runs on a live loop.
    loopN)getattr_worker_thread_localr   r   r   set_event_loopr   )r   s    r   _get_worker_loopr   >   sV     '66D|t~~''|%''t$$$$(!Kr   c                 l   	 	 t          j                    }n# t          $ r d}Y nw xY w|r|                                rddl}d	t          j                     	fd}|j                            d          }|	                    |          }	 |
                    d          |                    d	           S # |j        j        $ r]                     d
          rE	C	 t          j        	          D ]}	                    |j                   n# t          $ r Y nw xY w w xY w# |                    d	           w xY wt          j                    t          j                    ur#t'                      		                               S t+                      }|                               S )a  Run an async coroutine from a sync context.

    If the current thread already has a running event loop (e.g., inside
    the gateway's async stack or Atropos's event loop), we spin up a
    disposable thread so asyncio.run() can create its own loop without
    conflicting.

    For the common CLI path (no running loop), we use a persistent event
    loop so that cached async clients (httpx / AsyncOpenAI) remain bound
    to a live loop and don't trigger "Event loop is closed" on GC.

    When called from a worker thread (parallel tool execution), we use a
    per-thread persistent loop to avoid both contention with the main
    thread's shared loop AND the "Event loop is closed" errors caused by
    asyncio.run()'s create-and-destroy lifecycle.

    This is the single source of truth for sync->async bridging in tool
    handlers. Each handler is self-protecting via this function.
    Nr   c            	         t          j                                                     	 t          j                                                 	 t          j                  } | D ]}|                                 | r$                    t          j        | ddi           n# t          $ r Y nw xY w	                                 S # 	 t          j                  } | D ]}|                                 | r$                    t          j        | ddi           n# t          $ r Y nw xY w	                                 w xY w)Nreturn_exceptionsT)
r   r   setr   run_until_complete	all_taskscancelgather	Exceptionclose)pendingtcoro
loop_readyworker_loops     r   _run_in_workerz"_run_async.<locals>._run_in_workerx   s   !022KNN$&{333"55d;; &/<<G$ # #



 #66#NGLtLL   !   D!!#### &/<<G$ # #



 #66#NGLtLL   !   D!!####sC   (C AB''
B43B4EAD"!E"
D/,E.D//E   )max_workersi,  )timeoutF)waitg      ?)r   get_running_loopRuntimeError
is_runningconcurrent.futures	threadingEventfuturesThreadPoolExecutorsubmitresultshutdownTimeoutErrorr/   r!   call_soon_threadsafer"   current_threadmain_threadr   r    r   )
r(   r   
concurrentr+   poolfuturer'   	tool_loopr)   r*   s
   `       @@r   
_run_asyncrC   T   s   ('))     4&!! 4& 	"!!!;?_&&
	$ 	$ 	$ 	$ 	$ 	$ 	$, !444CC^,,	&===--  MMuM%%%% !. 
	 
	 
	 s++ 0G$.{;; C C#88BBBBC#   D 
	 MMuM%%%% !!)>)@)@@@&((--d333  I''---sH    ((C   'D,(1DD,
D'$D,&D''D,,D/ /E)discover_pluginszPlugin discovery failed: %sTOOL_TO_TOOLSET_MAPTOOLSET_REQUIREMENTS_last_resolved_tool_names
web_searchweb_extractterminalvision_analyzemixture_of_agentsimage_generate)skills_list
skill_viewskill_manage)
browser_navigatebrowser_snapshotbrowser_clickbrowser_typebrowser_scrollbrowser_backbrowser_pressbrowser_get_imagesbrowser_visionbrowser_consolecronjob)	read_file
write_filepatchsearch_filestext_to_speech)
	web_toolsterminal_toolsvision_tools	moa_toolsimage_toolsskills_toolsbrowser_toolscronjob_tools
file_tools	tts_tools_tool_defs_cachereturnc                  8    t                                            dS )zDrop memoized get_tool_definitions() results. Called when dynamic
    schema dependencies change (e.g. discord capability cache reset,
    execute_code sandbox reconfigured).N)rk   clearr   r   r   _clear_tool_defs_cachero     s     r   Fenabled_toolsetsdisabled_toolsets
quiet_modec           	      0   |r	 ddl m}  |            }|                                }|j        |j        f}n# t
          t          t          f$ r d}Y nw xY w| t          |           nd|rt          |          ndt          j
        |t          t          j                            d                    f}t                              |          }|d |D             at#          |          S t%          | ||          }	|r|	t          |<   t#          |	          S |	S )a  
    Get tool definitions for model API calls with toolset-based filtering.

    All tools must be part of a toolset to be accessible.

    Args:
        enabled_toolsets: Only include tools from these toolsets.
        disabled_toolsets: Exclude tools from these toolsets (if enabled_toolsets is None).
        quiet_mode: Suppress status prints.

    Returns:
        Filtered list of OpenAI-format tool definitions.
    r   )get_config_pathNHERMES_KANBAN_TASKc                 *    g | ]}|d          d         S functionnamer   .0r'   s     r   
<listcomp>z(get_tool_definitions.<locals>.<listcomp>6  s!    (O(O(O1:v)>(O(O(Or   )hermes_cli.configrt   statst_mtime_nsst_sizeFileNotFoundErrorOSErrorImportError	frozensetr	   _generationboolosenvirongetrk   rG   list_compute_tool_definitions)
rp   rq   rr   rt   cfg_pathcfg_statcfg_fp	cache_keycachedr9   s
             r   get_tool_definitionsr     sK   4   	999999&((H}}H*H,<=FF!7K8 	 	 	FFF	 ,<+GI&'''T,=GI'(((4  45566
	 "%%i00 )P(O(O(O(O% <<&'79JJWWF 	 '-#F||Ms   27 AAc           	      	   t                      }| t          |           }t          j                            d          rd|vr|                    d           |D ]}t          |          rSt          |          }|                    |           |s,t          d| d|rd
                    |          nd            d|t          v rMt          |         }|                    |           |s(t          d| dd
                    |                      |st          d	|            n5d
dlm}  |            D ]$}	|                    t          |	                     %|r|D ]}t          |          rSt          |          }|                    |           |s,t          d| d|rd
                    |          nd            d|t          v rMt          |         }|                    |           |s(t          d| dd
                    |                      |st          d	|            t          j        ||          }
d |
D             }d|v rpd
dlm}m}m} ||z  } || |                      }t+          |
          D ]<\  }}|                    di                               d          dk    r
d|d|
|<    n=ddd}|D ]|v r	 d
dlm} t1          ||                   } |            }n# t2          $ r d}Y nw xY w|$fd|
D             }
|                               ft+          |
          D ]<\  }}|                    di                               d          k    r
d|d|
|<    n=d|v rddh|z  }|st+          |
          D ]z\  }}|                    di                               d          dk    rH|d                             dd          }|                    d d          }di |d         d|id|
|<    n{|sS|
rBd! |
D             }t          d"t9          |
           d#d
                    |                      nt          d$           d% |
D             a	 d
d&lm}  ||
          }
n2# t2          $ r%}t@          !                    d'|           Y d}~nd}~ww xY w|
S )(z8Uncached implementation of :func:`get_tool_definitions`.Nru   kanbanu   ✅ Enabled toolset 'z': z, zno toolsu   ✅ Enabled legacy toolset 'u   ⚠️  Unknown toolset: r   )get_all_toolsetsu   🚫 Disabled toolset 'u   🚫 Disabled legacy toolset 'quietc                 *    h | ]}|d          d         S rw   r   rz   s     r   	<setcomp>z,_compute_tool_definitions.<locals>.<setcomp>  s!    JJJaAjM&1JJJr   execute_code)SANDBOX_ALLOWED_TOOLSbuild_execute_code_schema_get_execution_mode)moderx   ry   )typerx   get_dynamic_schema_coreget_dynamic_schema_admin)discorddiscord_admin)discord_toolc                 n    g | ]1}|                     d i                                d          k    /|2S rw   )r   )r{   r'   discord_tool_names     r   r|   z-_compute_tool_definitions.<locals>.<listcomp>  sK     " " "uuZ,,0088<MMM MMMr   rQ   rH   rI   description zV For simple information retrieval, prefer web_search or web_extract (faster, cheaper).c                 *    g | ]}|d          d         S rw   r   rz   s     r   r|   z-_compute_tool_definitions.<locals>.<listcomp>  s!    HHHA!J-/HHHr   u   🛠️  Final tool selection (z	 tools): u<   🛠️  No tools selected (all filtered out or unavailable)c                 *    g | ]}|d          d         S rw   r   rz   s     r   r|   z-_compute_tool_definitions.<locals>.<listcomp>  s!     O O O1:v!6 O O Or   )sanitize_tool_schemaszSchema sanitization skipped: %s)"r   r   r   r   r   appendr   r
   updateprintjoin_LEGACY_TOOLSET_MAPtoolsetsr   difference_updater	   get_definitionstools.code_execution_toolr   r   r   	enumeratetoolsr   r   r$   discardreplacelenrG   tools.schema_sanitizerr   loggerwarning)rp   rq   rr   tools_to_includeeffective_enabled_toolsetstoolset_nameresolvedlegacy_toolsr   ts_namefiltered_toolsavailable_tool_namesr   r   r   sandbox_enableddynamic_schemaitd_discord_schema_fns_dt	schema_fndynamicweb_tools_availabledesc
tool_namesr   er   s                               @r   r   r   I  s     EE#%)*:%;%;":>>.// 	8HD^4^4^ '--h7776 	B 	BL-- B*<88 ''111! vt,ttZbCr499XCVCVCVhrttuuu!4442<@ ''555! eccc$))T`JaJaccddd B@,@@AAA	B 	.-----'')) 	> 	>G##OG$<$<====  B- 	B 	BL-- B*<88 228<<<! xvLvv\dEtTYYxEXEXEXjtvvwww!4442<@ 22<@@@! ge<eeDIIVbLcLceefff B@,@@AAA -.>jQQQN KJ>JJJ ---ssssssssss/2FF22?I\I\I^I^___~.. 	 	EArvvj"%%))&11^CC-7^$T$Tq! D -3  1   444555555#C)<=N)OPP	#)++   " " " "-" " " %,,->????&~66  EArvvj"--11&99=NNN5?W,U,Uq) O 111+];>RR" 	">22  266*b))--f559KKKj>--mR@@D<<p D
 !+$Kr*~$K}d$K$K) )N1% E L  R 	RHHHHHJiC4G4GiiRVR[R[\fRgRgiijjjjPQQQ !P O O O O=@@@@@@..~>> = = =8!<<<<<<<<= s*   &K??LL=R 
R>R99R>>   todomemorydelegate_tasksession_searchr\   r_   zR</?(?:tool_call|function_call|result|response|output|input|system|assistant|user)>z%^\s*```(?:json|xml|html|markdown)?\s*z
\s*```\s*$z<!\[CDATA\[.*?\]\]>i  	error_msgc                 F   | sdS t                               d|           }t                              d|          }t                              d|          }t                              d|          }t          |          t          k    r|dt          dz
           dz   }d| S )zStrip structural framing tokens from a tool error before showing it to the model.

    See _TOOL_ERROR_ROLE_TAG_RE docstring above for rationale.
    z[TOOL_ERROR] r   N   z...)_TOOL_ERROR_ROLE_TAG_REsub_TOOL_ERROR_FENCE_OPEN_RE_TOOL_ERROR_FENCE_CLOSE_RE_TOOL_ERROR_CDATA_REr   _TOOL_ERROR_MAX_LEN)r   	sanitizeds     r   _sanitize_tool_errorr     s    
  '++B	::I)--b)<<I*..r9==I$((Y77I
9~~+++62Q667%?	&9&&&r   	tool_nameargsc                    |rt          |t                    s|S t          j        |           }|s|S |                    d          pi                     d          }|s|S t          |                                          D ]j\  }}|                    |          }|s|                    d          }|dk    r|t          |t
          t          f          st          |t                    rt          |||          }||ur|||<   |
                                                    d          rt                              d| |           |g||<   t                              d	| |           |g||<   t                              d
t          |          j        | |           %t          |t                    s<|st#          |          sOt          |||          }||ur|||<   l|S )a  Coerce tool call arguments to match their JSON Schema types.

    LLMs frequently return numbers as strings (``"42"`` instead of ``42``)
    and booleans as strings (``"true"`` instead of ``true``).  This compares
    each argument value against the tool's registered JSON Schema and attempts
    safe coercion when the value is a string but the schema expects a different
    type.  Original values are preserved when coercion fails.

    Handles ``"type": "integer"``, ``"type": "number"``, ``"type": "boolean"``,
    and union types (``"type": ["integer", "string"]``).

    Also wraps bare scalar values in a single-element list when the schema
    declares ``"type": "array"``.  Open-weight models (DeepSeek, Qwen, GLM)
    sometimes emit ``{"urls": "https://a.com"}`` when the tool expects
    ``{"urls": ["https://a.com"]}``; wrapping here avoids a confusing tool
    failure on what is otherwise a well-formed call.
    
parameters
propertiesr   arrayNschema[u   coerce_tool_args: %s.%s looks like a JSON array string but could not be parsed — model may have emitted a JSON-encoded string instead of a native array. Falling back to single-element list.z7coerce_tool_args: wrapped bare string in list for %s.%sz3coerce_tool_args: wrapped bare %s in list for %s.%s)
isinstancedictr	   
get_schemar   r   itemstuplestr_coerce_valuestrip
startswithr   r   infor   __name___schema_allows_null)	r   r   r   r   keyvalueprop_schemaexpectedcoerceds	            r   coerce_tool_argsr   !  s/   $  z$--  ++F **\**0b55lCCJ 4::<<(( 2  2 
U nnS)) 	??6** w5#4ZPTV[}=]=]#4%%% 'xLLL%'' !(DI ;;==++C00 NN? "3   #GS	Ms   DIKKEU$i   %%% 	 	 3K @ @ 	xDDD%DIKr   r   r   c                    t          |          r,|                                                                 dk    rdS t          |t                    r!|D ]}t          | ||          }|| ur|c S | S |dv rt          | |dk              S |dk    rt          |           S |dk    rt          | t                    S |d	k    rt          | t                    S |dk    r,|                                                                 dk    rdS | S )
zAttempt to coerce a string *value* to *expected_type*.

    Returns the original string when coercion is not applicable or fails.
    nullNr   >   numberintegerr   )integer_onlybooleanr   object)
r   r   lowerr   r   r   _coerce_number_coerce_boolean_coerce_jsonr   )r   expected_typer   r'   r9   s        r   r   r   u  s0   
 6"" u{{}}':':'<'<'F'Ft-&&  	 	A"5!F;;;FU"" #---e=I3MOOOO	!!u%%%E4(((  E4(((5;;==#6#6#8#8F#B#BtLr   c                    t          | t                    sdS |                     d          }|dk    rdS t          |t                    rd|v rdS |                     d          du rdS dD ]d}|                     |          }t          |t                    s-|D ]4}t          |t                    r|                    d          dk    r  dS 5edS )z@Return True when a JSON Schema fragment explicitly permits null.Fr   r   Tnullable)anyOfoneOf)r   r   r   r   )r   schema_type	union_keyvariantsvariants        r   r   r     s    fd## u**V$$Kft+t$$ ;)>)>tzz*%%t'  	::i(((D)) 	 	 	G'4(( W[[-@-@F-J-Jttt	 5r   expected_python_typec                 ~   	 t          j        |           }nA# t          t          f$ r-}t                              d|j        |           | cY d}~S d}~ww xY wt          ||          r"t                              d|j                   |S t                              dt          |          j        |j                   | S )aJ  Parse *value* as JSON when the schema expects an array or object.

    Handles model output drift where a complex oneOf/discriminated-union schema
    causes the LLM to emit the array/object as a JSON string instead of a native
    structure.  Returns the original string if parsing fails or yields the wrong
    Python type.
    zIcoerce_tool_args: failed to parse string as JSON for expected type %s: %sNz5coerce_tool_args: coerced string to %s via json.loadsuL   coerce_tool_args: JSON-parsed value is %s, expected %s — skipping coercion)
jsonloads
ValueError	TypeErrorr   r   r   r   debugr   )r   r  parsedexcs       r   r  r    s    E""	"   W )	
 	
 	

  &.// C )	
 	
 	
 
NNVV%  
 Ls    A"A
AAr   c                    	 t          |           }n# t          t          f$ r | cY S w xY w||k    s&|t          d          k    s|t          d          k    r| S |t          |          k    rt          |          S |r| S |S )zFTry to parse *value* as a number.  Returns original string on failure.infz-inf)floatr  OverflowErrorint)r   r   fs      r   r  r    s    %LL&    	AvveEll""a5==&8&8CFF{{1vv Hs    ((c                 r    |                                                                  }|dk    rdS |dk    rdS | S )zGTry to parse *value* as a boolean.  Returns original string on failure.trueTfalseF)r   r  )r   lows     r   r  r    s<    
++--



C
f}}t
g~~uLr   function_namefunction_argstask_idtool_call_id
session_id	user_taskenabled_toolsskip_pre_tool_call_hookc           
         t          | |          }	 | t          v rt          j        d|  di          S |sld}	 ddlm}	  |	| ||pd|pd|pd          }n2# t          $ r%}
t                              d|
           Y d}
~
nd}
~
ww xY w|t          j        d|id	
          S 	 ddl	m
}  || |          }||S nS# t          $ rF}t                              d|           | dv rt          j        ddid	
          cY d}~S Y d}~nd}~ww xY w| t          vr%	 ddlm}  ||pd           n# t          $ r Y nw xY wt          j                    }| dk    r$||nt           }t#          j        | |||          }nt#          j        | |||          }t'          t          j                    |z
  dz            }	 ddlm}  |d| |||pd|pd|pd|           n2# t          $ r%}
t                              d|
           Y d}
~
nd}
~
ww xY w	 ddlm}  |d| |||pd|pd|pd|          }|D ]}t+          |t,                    r|} nn2# t          $ r%}
t                              d|
           Y d}
~
nd}
~
ww xY w|S # t          $ r^}d|  dt-          |           }t                              |           t          j        dt1          |          id	
          cY d}~S d}~ww xY w)a  
    Main function call dispatcher that routes calls to the tool registry.

    Args:
        function_name: Name of the function to call.
        function_args: Arguments for the function.
        task_id: Unique identifier for terminal/browser session isolation.
        user_task: The user's original task (for browser_snapshot context).
        enabled_tools: Tool names enabled for this session.  When provided,
                       execute_code uses this list to determine which sandbox
                       tools to generate.  Falls back to the process-global
                       ``_last_resolved_tool_names`` for backward compat.

    Returns:
        Function result as a JSON string.
    errorz" must be handled by the agent loopNr   )get_pre_tool_call_block_messager   )r#  r%  r$  zpre_tool_call hook error: %sF)ensure_ascii)maybe_require_edit_approvalz!ACP edit approval guard error: %s>   r^   r]   z+Edit approval denied: approval guard failed)notify_other_tool_calldefaultr   )r#  r'  )r#  r&  i  )invoke_hookpost_tool_call)r   r   r9   r#  r%  r$  duration_mszpost_tool_call hook error: %stransform_tool_resultz$transform_tool_result hook error: %szError executing z: )r   _AGENT_LOOP_TOOLSr  dumpshermes_cli.pluginsr+  r$   r   r  acp_adapter.edit_approvalr-  _READ_SEARCH_TOOLStools.file_toolsr.  time	monotonicrG   r	   dispatchr  r0  r   r   	exceptionr   )r!  r"  r#  r$  r%  r&  r'  r(  block_messager+  	_hook_errr-  edit_block_message_edit_approval_errr.  _dispatch_startr   r9   r2  r0  hook_resultshook_resultr   r   s                           r   handle_function_callrE    s   6 %]MBBM|Z---:w=(\(\(\]^^^ ' 	P+/M
HNNNNNN ? ?!!#Mr)/R!-!3! ! !  H H H;YGGGGGGGGH (z7M":OOOO
		pMMMMMM!<!<]M!Z!Z!-)) . 	p 	p 	pLL<>PQQQ 777z7,Y"Zinooooooooo 87777	p  222CCCCCC&&w';)<<<<    .**N** 0=/HmmNgO&}-  FF &}#  F
 4>++o=EFF	E666666K '"2%+)/R'	 	 	 	 	  	E 	E 	ELL8)DDDDDDDD	E	L666666&;''"2%+)/R'	 	 	L  ,  k3// (FE  	L 	L 	LLL?KKKKKKKK	L  Z Z Z@}@@A@@	###z7$8$C$CDSXYYYYYYYYYZs   !I< I< A I< 
B B ;I<  BI< #B: 9I< :
D
6D:D
;I<  I< D

I< D+ *I< +
D85I< 7D88A>I< 7G I< 
H!H<I< HI< 
=I I< 
I7I2-I< 2I77I< <
K$AKK$K$c                  (    t          j                    S )z!Return all registered tool names.)r	   get_all_tool_namesr   r   r   rG  rG    s    &(((r   c                 *    t          j        |           S )z%Return the toolset a tool belongs to.)r	   get_toolset_for_tool)r   s    r   rI  rI    s    (333r   c                  (    t          j                    S )z0Return toolset availability info for UI display.)r	   get_available_toolsetsr   r   r   rK  rK    s    *,,,r   c                  (    t          j                    S )z>Return {toolset: available_bool} for every registered toolset.)r	   check_toolset_requirementsr   r   r   rM  rM    s    .000r   r   c                 ,    t          j        |           S )z.Return (available_toolsets, unavailable_info).r   )r	   check_tool_availabilityr   s    r   rO  rO    s    +%8888r   )rl   N)NNF)N)F)NNNNNF)L__doc__r   r  rer   loggingr4   r:  typingr   r   r   r   r   tools.registryr   r	   r   r
   r   	getLoggerr   r   r   Lockr   localr   r   r   rC   r6  rD   r$   r   r  get_tool_to_toolset_maprE   r   __annotations__get_toolset_requirementsrF   r   rG   r   rk   r   ro   r   r   r   r4  r8  compile
IGNORECASEr   	MULTILINEr   r   DOTALLr   r   r   r   r   r   r   r  r  r  rE  rG  rI  rK  rM  rO  r   r   r   <module>r_     s    * 
			  				        3 3 3 3 3 3 3 3 3 3 3 3 3 3 ; ; ; ; ; ; ; ; 6 6 6 6 6 6 6 6		8	$	$ 
 ).""&y((     ,Y. Y. Y.@      3333333 3 3 3
LL.222222223 'Gh&F&H&H T#s(^ H H H(I(I(K(K d39o K K K (* 49 ) ) ) .!l%&%&$%AAA    [FFF"#  D 79 $ud4S>223 8 8 8    #'#'> >3i>Cy> > 
$sCx.	> > > >D #'#'[ [3i[Cy[ [ 
$sCx.	[ [ [ [L JII !>2 & %"*YM   'BJ'OQSQ]^^ 'RZr|DD !rz"8")DD  'C 'C ' ' ' '(Q Q4S> Qd38n Q Q Q Qh  TD[    :t     0 4    > # T    $3     ""& $#)-$)YZ YZYZS>YZ c]YZ 3-	YZ
 YZ }YZ DI&YZ "YZ 	YZ YZ YZ YZ@)DI ) ) ) )
4C 4HSM 4 4 4 4
-S$Y - - - -
1DdO 1 1 1 1
9 94 9E$s)T$Z:O4P 9 9 9 9 9 9s   B B8B33B8