
    Jj`                        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 e            Zeed<   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g dg ddgg ddgd	Z.i Z/e	e0ee	e)e
f                  f         ed<   dZ1d[dZ2	 	 	 	 d\deee)                  d eee)                  d!e3d"e3dee	e)e
f                  f
d#Z4	 	 	 	 d\deee)                  d eee)                  d!e3d"e3dee	e)e
f                  f
d$Z5de6fd%Z7h d&Z8d'd(hZ9 ej:        d)ej;                  Z< ej:        d*ej=                  Z> ej:        d+ej=                  Z? ej:        d,ej@                  ZAd-ZBd.e)de)fd/ZCd0e)d1e	e)e
f         de	e)e
f         fd2ZDd3e
d4e)de3fd5ZEd6e
d3e
de
fd7ZFd]d6e)d3e,dz  fd8ZGd3e,dz  de3fd9ZHd6e)d:eIfd;ZJd^d6e)d<e3fd=ZKd6e)fd>ZLd?e
de0e)ee)         ee)         f         fd@ZMdddddddddddA
dBe)dCe	e)e
f         d?e
dDee)         dEee)         dFee)         dGee)         dHee)         dIe6dJee)         dKee)         dLee)         dMeee	e)e
f                           ddfdNZN	 	 	 	 	 	 	 	 	 	 	 	 d_dBe)dCe	e)e
f         dDee)         dFee)         dEee)         dGee)         dHee)         dOee)         dPeee)                  dQe3dRe3dSeee	e)e
f                           deee)                  d eee)                  de)fdTZOdee)         fdUZPd0e)dee)         fdVZQde	e)e,f         fdWZRde	e)e3f         fdXZSd^dYe3deee)         ee,         f         fdZZTdS )`a  
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_toolset_WARNED_DISABLED_BUNDLESc                      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     1/home/rurouni/.hermes/hermes-agent/model_tools.py_get_tool_loopr   3   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   B   sV     '66D|t~~''|%''t$$$$(!Kr   c                    	
 	 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l	m
} |                     ||                    }	 |                    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_worker|   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_workers)propagate_context_to_threadi,  )timeoutF)waitg      ?)r   get_running_loopRuntimeError
is_runningconcurrent.futures	threadingEventfuturesThreadPoolExecutortools.thread_contextr/   submitresultshutdownTimeoutErrorr1   r"   call_soon_threadsafer#   current_threadmain_threadr   r!   r   )r)   r   
concurrentr,   poolr/   futurer(   	tool_loopr*   r+   s   `        @@r   
_run_asyncrF   X   s7   ('))     8&!! 8& 	"!!!;?_&&
	$ 	$ 	$ 	$ 	$ 	$ 	$, !444CC 	EDDDDD88HHII	&===--  MMuM%%%% !. 
	 
	 
	 s++ 0G$.{;; C C#88BBBBC#   D 
	 MMuM%%%% !!)>)@)@@@&((--d333  I''---sH    ((#C 'D;71D)(D;)
D63D;5D66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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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)rl   clearr   r   r   _clear_tool_defs_cacherq     s     r   Fenabled_toolsetsdisabled_toolsets
quiet_modeskip_tool_search_assemblyc           	         |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                    t          |          f}t                              |          }	|	d |	D             at#          |	          S t%          | |||          }
|rot'          t                    t(          k    r9t                              t-          t/          t                                         |
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.
        skip_tool_search_assembly: When True, return the pre-assembly tool list
            (raw schemas for every enabled tool). Used internally by the
            tool_search / tool_describe bridge handlers so they can read the
            real catalog, not the already-collapsed one. Public callers should
            leave this False.

    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>L  s!    (O(O(O1:v)>(O(O(Or   )ru   )hermes_cli.configrw   statst_mtime_nsst_sizeFileNotFoundErrorOSErrorImportError	frozensetr	   _generationboolosenvirongetrl   rJ   list_compute_tool_definitionslen_TOOL_DEFS_CACHE_MAXpopnextiter)rr   rs   rt   ru   rw   cfg_pathcfg_statcfg_fp	cache_keycachedr<   s              r   get_tool_definitionsr     s   @   	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% <<&'79JJAZ\ \ \F    $888  d+;&<&<!=!=>>>&,#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          |          r.d
dlm}m} |                    d          s  ||          pi                     d          r ||          }|                    |           t%          |          }|sk|                    d          rV|t&          vrMt&                              |           t*                              d||rd
                    |          nd           n$t          |          }|                    |           |s,t          d| d|rd
                    |          nd            A|t          v rNt          |         }|                    |           |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} tA          ||%                   } |            }n# tB          $ 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'tI          |           d(d
                    |                      nt          d)           d* |D             a%	 d
d+l&m'}  ||          }n2# tB          $ r%}t*          (                    d,|           Y d}~nd}~ww xY w	 d
d-l)m*} m+}!  |!            }"|s_|"j,        d.k    rTt[                      }# | ||#|"/          }$|$j.        r*|s(t          d0|$j/         d1|$j0         d2|$j1         d3           |$j2        }n2# tB          $ r%}t*          (                    d4|           Y d}~nd}~ww xY w|S )5z8Uncached implementation of :func:`get_tool_definitions`.Nrx   kanbanu   ✅ Enabled toolset 'z': z, zno toolsu   ✅ Enabled legacy toolset 'u   ⚠️  Unknown toolset: r   )get_all_toolsets)bundle_non_core_toolsget_toolsetzhermes-posturezagent.disabled_toolsets contains platform-bundle name '%s'; core tools are preserved and only its platform-specific tools (%s) are removed. Bundle names usually belong in `toolsets:`, not `disabled_toolsets` (#33924).noneu   🚫 Disabled toolset 'u   🚫 Disabled legacy toolset 'quietc                 *    h | ]}|d          d         S rz   r   r}   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)moder{   r|   )typer{   get_dynamic_schema_coreget_dynamic_schema_admin)discorddiscord_admin)discord_toolc                 n    g | ]1}|                     d i                                d          k    /|2S rz   )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   rS   rK   rL   description zV For simple information retrieval, prefer web_search or web_extract (faster, cheaper).c                 *    g | ]}|d          d         S rz   r   r}   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 rz   r   r}   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)assemble_tool_defsload_configoff)context_lengthconfigu   🔎 Tool Search: z MCP/plugin tools deferred (~z6 tokens) behind tool_search/describe/call. Threshold ~z tokens.z Tool search assembly skipped: %s)3r    r   r   r   r   appendr   r
   updateprintjoin_LEGACY_TOOLSET_MAPtoolsetsr   r   r   
startswithdifference_updatesortedr   addloggerinfor	   get_definitionstools.code_execution_toolr   r   r   	enumeratetoolsr   r   r%   discardreplacer   rJ   tools.schema_sanitizerr   warningtools.tool_searchr   r   enabled_resolve_active_context_length	activateddeferred_countdeferred_tokensthreshold_tokens	tool_defs)&rr   rs   rt   ru   tools_to_includeeffective_enabled_toolsetstoolset_nameresolvedlegacy_toolsr   ts_namer   r   	to_remove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   _load_ts_configts_cfgr   assemblyr   s&                                        @r   r   r   e  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GGGGGGGG**955 A++l:S:S:YWY9^9^_h9i9i A !6 5l C CI$66yAAA%i00H& <+B+B9+M+M  ,4L L L044\BBB<
 )3;GDIIh///    /|<<H$66x@@@! 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!<<<<<<<<=>XXXXXXXX ""( 	0V^u-D-D;==N))-  H
 ! * F)@ F F!1F F"*";F F F  
 &/N > > >91========> sC   8&OO.-O.U/ /
V9VV"A3X 
Y Y  Yc                     	 ddl m}   |             pi }t          |                    d          t                    r|                    d          ni }t          |t                    si }|                    d          p|                    d          pd                                }|sdS ddlm} t           ||          pd          S # t          $ r&}t                              d|           Y d}~dS d}~ww xY w)	u   Look up the active model's context length for the tool-search gate.

    Returns 0 when the model can't be resolved — ``should_activate`` falls
    back to a fixed token cutoff in that case.
    r   )r   modeldefaultr   )get_model_context_lengthz+Could not resolve active context length: %sN)r   r   
isinstancer   dictstripagent.model_metadatar   intr%   r   debug)_loadcfg	model_cfgmodel_idr   r   s         r   r   r   :  s#   ::::::eggm(237773C3CT(J(JRCGGG$$$PR	)T** 	IMM'**LimmI.F.FL"SSUU 	1AAAAAA++H55:;;;   BAFFFqqqqqs   B(C ,C 
C<C77C<>   todomemorydelegate_tasksession_searchr^   ra   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  v  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 ]\  }}|                    |          }|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                    sf|dk    r0t          |t
          t          f          rt#          ||          ||<   n.|dk    r(t          |t                    rt#          ||          ||<   |st%          |          st          |||          }||ur:|||<   t          |t
          t          t          f          rt#          ||          ||<   |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object)r   r   r	   
get_schemar   r   itemstuplestr_coerce_valuer   r   r   r   r   r   __name__"_normalize_json_strings_for_schema_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::<<(( @U @U
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   %%% 	 7""z%$'G'G">ukRRS		X%%*UD*A*A%>ukRRS	 	 3K @ @ 	xDDD%DI 'D%#677 U>wTTS	Kr   r  kindc                 <   t          | t                    sdS |                     d          }|k    st          |t                    r|v rdS dD ]J}|                     |          }t          |t                    rt	          fd|D                       r dS KdS )u  Return True when *schema* permits a value of JSON type *kind*.

    Looks at ``type`` (string or list) and recurses through
    ``anyOf``/``oneOf``/``allOf`` branches — matching the JSON-Schema shapes
    open-weight models emit against. ``kind`` is ``"array"`` or ``"object"``.
    Fr   T)anyOfoneOfallOfc              3   8   K   | ]}t          |          V  d S N)_schema_accepts_kind)r~   br%  s     r   	<genexpr>z'_schema_accepts_kind.<locals>.<genexpr>  s?       .
 .
./ D)).
 .
 .
 .
 .
 .
r   )r   r   r   r   any)r  r%  r(   	union_keybranchess    `   r   r,  r,    s     fd## u

6ADyyZ4((yTQYYt0  	::i((h%% 	# .
 .
 .
 .
3;.
 .
 .
 +
 +
 	 445r   r   c                 L   t          |t                    s| S t          | t                    r|                                 }t	          |d          }t	          |d          }|r|                    d          s|rz|                    d          re	 t          j        |          }n# t          t          f$ r | cY S w xY wt          |t                    r|r|} nt          |t                    r|r|} n| S | S t          | t                    rf|                    d          }t          |t                    s| S d}g }| D ]-}	t          |	|          }
|p|
|	u}|                    |
           .|r|n| S t          | t                    r|                    d          }t          |t                    s| S d}t          |           }|                                D ]F\  }}|| vst          |t                    st          | |         |          }
|
| |         ur|
||<   d}G|r|n| S | S )	u  Recursively parse JSON-encoded string values that a schema expects to
    be arrays or objects, including nested array items and object properties.

    Open-weight models (DeepSeek, Qwen, GLM, and others) sometimes emit a
    structured field — or an *element* of a structured field — as a
    JSON-encoded string instead of a native value. The top-level
    :func:`coerce_tool_args` pass repairs the outermost value; this helper
    walks the rest of the tree so cases like::

        {"todos": ["{\"id\": \"1\", \"content\": \"x\"}"]}

    (a list whose elements are JSON strings) and nested object sub-fields are
    repaired too. Parsing is schema-guided: a string is only parsed when the
    matching schema position actually expects an array or object, so
    legitimate JSON-looking string fields (``type: string``) are preserved.

    Ported from cline/cline#11803, adapted to hermes-agent's coercion layer.
    Returns the original value object when nothing changed (identity preserved
    so callers can cheaply detect no-ops).
    r  r  r  {r  Fr  T)r   r   r  r   r,  r   jsonloads
ValueError	TypeErrorr   r   r  r   r  )r   r  trimmedexpects_arrayexpects_objectparseditems_schemachangedoutitemnxtpropskr!  s                 r   r  r    sv   * fd##  % ++--,VW==-fh?? 	g0055 		&11#66	G,,	*   &$'' M FD)) n L % 
)zz'**,-- 	L 	 	D4T<HHC2#T/GJJsOOOO(ss5( % )

<((%&& 	L5kk#kkmm 	 	NA{~~ZT%B%B~4U1X{KKC%(""A(ss5(Ls   B% %B;:B;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integerrF  )integer_onlybooleanr  r  )
r  r   lowerr   r   r  _coerce_number_coerce_boolean_coerce_jsonr   )r   expected_typer  r(   r<   s        r   r  r  N  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   rD  Tnullable)r'  r(  )r   r   r   r   )r  schema_typer0  variantsvariants        r   r  r  k  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)
r4  r5  r6  r7  r   r   r  r   r   r   )r   rS  r;  excs       r   rL  rL    s    E""	"   W )	
 	
 	

  &.// C )	
 	
 	
 
NNVV%  
 Ls    A"A
AArG  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)floatr6  OverflowErrorr   )r   rG  fs      r   rJ  rJ    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   rI  )r   lows     r   rK  rK    s<    
++--



C
f}}t
g~~uLr   r<   c                    	 t          | t                    rt          j        |           n| }t          |t                    r:|                    d          r%ddt          |                    d                    fS n# t          $ r Y nw xY wdS )Nerror
tool_error)okNN)r   r  r4  r5  r   r   r%   )r<   parsed_results     r   _tool_result_observer_fieldsrd    s    .8.E.EQ
6***6mT** 	J}/@/@/I/I 	JL#m.?.?.H.H*I*III   s   A9A= =
B
	B
)
task_id
session_idtool_call_idturn_idapi_request_idduration_msstatus
error_typeerror_messagemiddleware_tracefunction_namefunction_argsre  rf  rg  rh  ri  rj  rk  rl  rm  rn  c                 &   	 ddl m}m}  |d          sdS |	t          |          \  }	}
} |d| |||pd|pd|pd|pd|pd||	|
|t	          |pg                      dS # t
          $ r&}t                              d|           Y d}~dS d}~ww xY w)u  Emit the ``post_tool_call`` observer hook.

    No-ops cheaply when no plugin has registered for ``post_tool_call`` —
    the ``has_hook`` gate skips both the result-field derivation and the
    payload dispatch so the no-listener path costs one dict lookup.  When
    ``status`` is not supplied, the ok/error fields are derived from the
    result *after* the gate (parsing the result is only worth it when a
    listener will actually consume it).
    r   has_hookinvoke_hookpost_tool_callNr   )r  r  r<   re  rf  rg  rh  ri  rj  rk  rl  rm  rn  zpost_tool_call hook error: %s)hermes_cli.pluginsrs  rt  rd  r   r%   r   r   )ro  rp  r<   re  rf  rg  rh  ri  rj  rk  rl  rm  rn  rs  rt  	_hook_errs                   r   _emit_post_tool_call_hookrx    s   2A<<<<<<<<x()) 	F>0LV0T0T-FJ#Mr!'R%+Mr)/R#!'!"2"8b99	
 	
 	
 	
 	
 	
   A A A4i@@@@@@@@@As   A  AA   
B*BB	user_taskenabled_toolsskip_pre_tool_call_hookskip_tool_request_middlewaretool_request_middleware_tracec                    1 t           |          }t          |t                    si }t          |pg           }d}	 ddlm} n# t          $ r d}Y nw xY w|(|                               r	 t          ||dd          pg }n# t          $ r g }Y nw xY w |j	        k    r|
                    |pi |          S  |j        k    r|                    |pi |          S  |j        k    r|                    |pi           \  }}}|s|st          j        d|pdid	
          S |                    |          }||vrt          j        dd| did	
          S t%          |||||	|
t          |          ||          S t          |          }|
sk	 ddlm}  | |pdpd|pd|pd|pd          }|j        }|j        }|j        }n2# t          $ r%}t0                              d|           Y d}~nd}~ww xY w	  t4          v rt          j        d  di          S |	sd}	 ddlm}  | |pdpd|pd|pd|pdt          |                    }n2# t          $ r%}t0                              d|           Y d}~nd}~ww xY w|Bt          j        d|id	
          }t;           |||||dd|t          |                     |S 	 ddlm}  | |          }||S nS# t          $ rF}t0                              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tG          j$                    }!d}"	 ddl%m&}#m'}$  |$|pd|pd           }"n# t          $ r d}#Y nw xY w	  d!k    r5||ntP          1d"tR          tT          tV          f         d#tV          f 1fd$}%n)d"tR          tT          tV          f         d#tV          f fd%}%dd&lm,}&  |& ||%|pdpd|pd|pd|pd'	  	        }|"|#	  |#|"           n8# t          $ r Y n,w xY wn'# |"|#	  |#|"           w # t          $ r Y w w xY ww w xY wt[          tG          j$                    |!z
  d(z            }'t;           ||||||'t          |          )
  
         	 dd*lm.}(m/})  |(d+          rSta          |          \  }*}+}, |)d+ ||pdpd|pd|pd|pd|'|*|+|,,          }-|-D ]}.t          |.tT                    r|.} nn2# t          $ r%}t0                              d-|           Y d}~nd}~ww xY w|S # t          $ r^}/d.  d/tU          |/           }0t0          1                    |0           t          j        dte          |0          id	
          cY d}/~/S d}/~/ww xY w)0a  
    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.
        enabled_toolsets: The session's enabled toolsets.  Used to scope the
                       Tool Search bridge catalog so ``tool_search`` /
                       ``tool_describe`` / ``tool_call`` only see and invoke
                       tools the session was actually granted.  ``None`` means
                       "no restriction" (the caller scopes to every toolset),
                       matching ``get_tool_definitions`` semantics.
        disabled_toolsets: The session's disabled toolsets, applied as a
                       subtraction when scoping the bridge catalog.

    Returns:
        Function result as a JSON string.
    Nr   )tool_searchT)rr   rs   rt   ru   )current_tool_defsr`  ztool_call could not be resolvedF)ensure_ascii'zO' is not available in this session. Use tool_search to find tools you can call.)ro  rp  re  rg  rf  ry  rz  r{  r|  r}  rr   rs   )apply_tool_request_middlewarer   )re  rf  rg  rh  ri  z!tool_request middleware error: %sz" must be handled by the agent loop)get_pre_tool_call_block_message)re  rf  rg  rh  ri  rn  zpre_tool_call hook error: %sblockedplugin_block)ro  rp  r<   re  rf  rg  rh  ri  rk  rl  rm  rn  )maybe_require_edit_approvalz!ACP edit approval guard error: %s>   r`   r_   z+Edit approval denied: approval guard failed)notify_other_tool_callr   )#reset_current_observability_context!set_current_observability_context)rh  rg  r   	next_argsrn   c                 6    t          j        |           S )N)re  rf  rz  r	   dispatch)r  ro  r   rf  re  s    r   	_dispatchz'handle_function_call.<locals>._dispatch  s+    #,%y '#-&5	   r   c                 6    t          j        |           S )N)re  rf  ry  r  )r  ro  rf  re  ry  s    r   r  z'handle_function_call.<locals>._dispatch  s+    #,%y '#-"+	   r   )run_tool_execution_middleware)original_argsre  rf  rg  rh  ri  i  )
ro  rp  r<   re  rf  rg  rh  ri  rj  rn  rr  transform_tool_result)r  r  r<   re  rf  rg  rh  ri  rj  rk  rl  rm  z$transform_tool_result hook error: %szError executing z: )3r$  r   r   r   r   r  r%   is_bridge_toolr   TOOL_SEARCH_NAMEdispatch_tool_searchTOOL_DESCRIBE_NAMEdispatch_tool_describeTOOL_CALL_NAMEresolve_underlying_callr4  dumpsscoped_deferrable_nameshandle_function_callhermes_cli.middlewarer  payloadoriginal_payloadtracer   r   _AGENT_LOOP_TOOLSrv  r  rx  acp_adapter.edit_approvalr  _READ_SEARCH_TOOLStools.file_toolsr  time	monotonictools.approvalr  r  rJ   r   r  r   r  r   rs  rt  rd  	exceptionr  )2ro  rp  re  rg  rf  rh  ri  ry  rz  r{  r|  r}  rr   rs   _tool_middleware_trace_ts_modcurrent_defsunderlying_nameunderlying_argserr_scoped_deferrable_tool_original_argsr  _tool_request_mw_mw_errblock_messager  rw  r<   r  edit_block_message_edit_approval_errr  _dispatch_start_approval_tokensr  r  r  r  rj  rs  rt  rk  rl  rm  hook_resultshook_resultr   r  r   s2   ` ` `  `                                         @r   r  r    s
   R %]MBBMmT** !"?"E2FF G0000000    w55mDD	 0!1"34    	 L
  	 	 	LLL	G444//0CBN 0 P P PG66611-2E2DP 2 R R RG2224;4S4STaTgeg4h4h1O_c 6/ 6z7C,T3T"U/46 6 6 6 ")!@!@!N!N&888zFO F F F#
 !&' ' ' ' (--)%#+(?-I.23I.J.J!1"3    }--' G	GKKKKKK<<2%+)/R2-3      -4M"2"C%5%;"" 	G 	G 	GLL<gFFFFFFFF	GyZ---:w=(\(\(\]^^^ ' !	+/MHNNNNNN ? ?!!#Mr)/R!-!3#Mr#1#7R%)*@%A%A	! 	! 	!  H H H;YGGGGGGGGH (Wm$<5QQQ)"/"/!#)!-##1$-"/%)*@%A%A    
		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';)<<<<    .**
	7         A@2)/R       	7 	7 	726///	7&	.. 4A3L--Rkc3h C          c3h C          LKKKKK2212%+)/R2-3
 
 
F  +0S0_778HIIII    D  +0S0_778HIIII    D ,0_
 4>++o=EFF!''!%)#!"899	
 	
 	
 	
*	L@@@@@@@@x/00 4PQW4X4X1
M*{++&!#Mr)/R!-!3#Mr#1#7R +!)"/      $0  K!+s33 !,  	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  A AA4B
 
BB7G 
G>G99G>!U% $U% )0I U% 
J	$J?U% J		AU% K( 'U% (
L826L3(L8)U% .U% 3L88U% M U% 
M&#U% %M&&U% ?N U% N(%U% 'N((U% ,BQ 4U% 9Q U% 
QU% QU% Q:Q('Q:(
Q52Q:4Q55Q::AU% 
A&T1 0U% 1
U ;UU% U  U% %
W/AWWWc                  (    t          j                    S )z!Return all registered tool names.)r	   get_all_tool_namesr   r   r   r  r  H  s    &(((r   c                 *    t          j        |           S )z%Return the toolset a tool belongs to.)r	   get_toolset_for_tool)r  s    r   r  r  M  s    (333r   c                  (    t          j                    S )z0Return toolset availability info for UI display.)r	   get_available_toolsetsr   r   r   r  r  R  s    *,,,r   c                  (    t          j                    S )z>Return {toolset: available_bool} for every registered toolset.)r	   check_toolset_requirementsr   r   r   r  r  W  s    .000r   r   c                 ,    t          j        |           S )z.Return (available_toolsets, unavailable_info).r   )r	   check_tool_availabilityr   s    r   r  r  \  s    +%8888r   )rn   N)NNFFr+  )F)NNNNNNNFFNNN)U__doc__r   r4  rer   loggingr6   r  typingr   r   r   r   r   tools.registryr   r	   r   r
   r   	getLoggerr  r   r    r   __annotations__r   Lockr   localr   r   r   rF   rv  rG   r%   r   r   get_tool_to_toolset_maprH   r  get_toolset_requirementsrI   r   rJ   r   rl   r  r   rq   r   r   r   r   r   r  r  compile
IGNORECASEr  	MULTILINEr  r  DOTALLr	  r
  r  r$  r,  r  r  r  r   rL  rJ  rK  rd  rx  r  r  r  r  r  r  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((     ,]. ]. ].H      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"#  B 79 $ud4S>223 8 8 8      -1-1&+	K KtCy)KS	*K K  $	K
 
$sCx.K K K K^ -1-1&+	R RtCy)RS	*R R  $	R
 
$sCx.R R R Rj    < JII !>2 & %"*YM   'BJ'OQSQ]^^ 'RZr|DD !rz"8")DD  'C 'C ' ' ' '(_ _4S> _d38n _ _ _ _D C D    *Jc J3 J3 J J J JZ  TD[    :t     0 4    > # T    $3     sHSM8TW=7X1Y     " $"&!$(  $#'7;0A 0A 0A0A S>0A 	0A
 c]0A 0A 3-0A c]0A SM0A 0A SM0A 0A C=0A tDcN340A 
0A 0A 0A 0Al ""& $!$(#)-$)).DH,0-1FZ FZFZS>FZ c]FZ 3-	FZ
 FZ c]FZ SMFZ }FZ DI&FZ "FZ #'FZ $,Dc3h,@#AFZ tCy)FZ  S	*FZ 	FZ FZ FZ FZ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" "C'CC