
    epj?             !       *   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mZmZmZ ddlmZmZ  ej        e          Z e            Zeed<   defd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/                    Z0e	e.e1f         ed<   g a2ee.         ed<   ddgdgdgdgg dg ddgg ddgd	Z3i Z4e	e5ee	e.e
f                  f         ed<   dZ6d]dZ7	 	 	 	 d^d eee.                  d!eee.                  d"ed#edee	e.e
f                  f
d$Z8	 	 	 	 d^d eee.                  d!eee.                  d"ed#edee	e.e
f                  f
d%Z9de:fd&Z;h d'Z<d(d)hZ= ej>        d*ej?                  Z@ ej>        d+ejA                  ZB ej>        d,ejA                  ZC ej>        d-ejD                  ZEd.ZFd/e.de.fd0ZGd1e.d2e	e.e
f         de	e.e
f         fd3ZHd4e
d5e.defd6ZId7e
d4e
de
fd8ZJd_d7e.d4e1dz  fd9ZKd4e1dz  defd:ZLd7e.d;eMfd<ZNd`d7e.d=efd>ZOd7e.fd?ZPd@e
de5e.ee.         ee.         f         fdAZQdddddddddddB
dCe.dDe	e.e
f         d@e
dEee.         dFee.         dGee.         dHee.         dIee.         dJe:dKee.         dLee.         dMee.         dNeee	e.e
f                           ddfdOZR	 	 	 	 	 	 	 	 	 	 	 	 	 dadCe.dDe	e.e
f         dEee.         dGee.         dFee.         dHee.         dIee.         dPee.         dQeee.                  dRedSedTedUeee	e.e
f                           d eee.                  d!eee.                  de.f dVZSdee.         fdWZTd1e.dee.         fdXZUde	e.e1f         fdYZVde	e.ef         fdZZWd`d[edeee.         ee1         f         fd\ZXdS )ba  
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)CHECK_FN_CACHE_BYPASScheck_fn_cache_scopediscover_builtin_toolsregistry
tool_error)resolve_toolsetvalidate_toolset_WARNED_DISABLED_BUNDLESreturnc                  F    	 ddl m}   |             S # t          $ r Y dS w xY w)Nr   is_delegated_child_contextF)agent.delegation_contextr   	Exceptionr   s    1/home/thesage/.hermes/hermes-agent/model_tools.py_is_delegated_child_contextr   0   sJ    GGGGGG))+++   uus    
  c                      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     r   _get_tool_loopr    B   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&   Q   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gatherr   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_contextr8   submitresultshutdownTimeoutErrorr:   r,   call_soon_threadsafer-   current_threadmain_threadr&   r+   r    )r2   r"   
concurrentr5   poolr8   futurer1   	tool_loopr3   r4   s   `        @@r   
_run_asyncrO   g   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   c                  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)ru   clearr   r   r   _clear_tool_defs_cachery     s     r   Fenabled_toolsetsdisabled_toolsets
quiet_modeskip_tool_search_assemblyc           	      ~   d}|r 	 ddl m}  |            }|                                }|j        |j        f}n# t
          t          t          f$ r d}Y nw xY wt                      }	|	t          k    ry| t          |           nd|rt          |          ndt          j        |t          t          j                            d                    t          |          t#                      |	f}|t$                              |          nd}
|
d |
D             at)          |
          S t+          | |||          }|rq|ot-          t$                    t.          k    r9t$                              t3          t5          t$                                         |t$          |<   t)          |          S |r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.
    Nr   )get_config_pathHERMES_KANBAN_TASKc                 *    g | ]}|d          d         S functionnamer   .0r1   s     r   
<listcomp>z(get_tool_definitions.<locals>.<listcomp>`  s!    (O(O(O1:v)>(O(O(Or   )r}   )hermes_cli.configr   statst_mtime_nsst_sizeFileNotFoundErrorOSErrorImportErrorr	   r   	frozensetr   _generationboolosenvirongetr   ru   rS   list_compute_tool_definitionslen_TOOL_DEFS_CACHE_MAXpopnextiter)rz   r{   r|   r}   	cache_keyr   cfg_pathcfg_statcfg_fpprofile_scopecachedrE   s               r   get_tool_definitionsr   &  s   @ I  	999999&((H}}H*H,<=FF!7K8 	 	 	FFF	,..111/?/K	*+++QU0AK	+,,,t$RZ^^$899::.//+--	I 5>4I!%%i000t )P(O(O(O(O% <<&'79JJAZ\ \ \F i+   $888  d+;&<&<!=!=>>>&,#F|| F||Ms   2: AAc                   & t                      }| 't          |           }t          j                            d          r't                      s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	|            t1          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 } tC          ||&                   } |            }n# tD          $ 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'tK          |           d(d                    |                      nt          d)           d* |D             a&	 d
d+l'm(}  ||          }n2# tD          $ r%}t,          )                    d,|           Y d}~nd}~ww xY w	 d
d-l*m+} m,}!  |!            }"|s|"j-        d.k    r}t]                      }# | ||#|"/          }$|$j/        rS|sQd0d1d2d3d4d5}%t          d6|$j0         d7|$j1         d8|$j2         d9|%                    |$j3        |$j3                   d:	           |$j4        }n2# tD          $ r%}t,          )                    d;|           Y d}~nd}~ww xY w|S )<z8Uncached implementation of :func:`get_tool_definitions`.Nr   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 r   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 r   )r   )r   r1   discord_tool_names     r   r   z-_compute_tool_definitions.<locals>.<listcomp>  sK     " " "uuZ,,0088<MMM MMMr   r\   rT   rU   description zV For simple information retrieval, prefer web_search or web_extract (faster, cheaper).c                 *    g | ]}|d          d         S r   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 r   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configzcatalog listing embeddedznames-only listing embeddedz/listing embedded (oversized servers summarized)z/server summary embedded (search-only discovery)zno listing (search-only))fullnamesmixedgroupsr   u   🔎 Tool Search (tier z): z MCP/plugin tools deferred (~u.    tokens) behind tool_search/describe/call — .z Tool search assembly skipped: %s)5r*   r   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   rS   tools.schema_sanitizerr   warningtools.tool_searchr   r   enabled_resolve_active_context_length	activatedtierdeferred_countdeferred_tokenslisting_form	tool_defs)'rz   r{   r|   r}   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   assembly_formsr   s'                                         @r   r   r   {  s	     EE#%)*:%;%;"JNN/00
	8/11
	8  ::: '--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
 ! 
* 
"<#@#T$U"<	> >
 qhm q q@W q q3;3Kq q5;ZZ@UW_Wl5m5mq q q  
 &/N > > >91========> sC   &O--O<;O<+U= =
V,V''V,0BY 
Y<Y77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} |                    d          }t          |t                    r|dk    r|nd}t          |                    d	          pd                                          }t          |                    d
          pd                                          }d}	|r	 ddl
m}
  |
||          pi }t          |                    d
          p|pd                                          }t          |                    d          pd                                          }	n3# t          $ r&}t                              d||           Y d}~nd}~ww xY w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_lengthr   Nproviderbase_url)resolve_runtime_provider)	requestedtarget_modelapi_keyup   Runtime credential resolution failed for tool-search context gate (provider=%s): %s — using config values only)r  r	  config_context_lengthr  z+Could not resolve active context length: %s)r   r   
isinstancer   dictstripagent.model_metadatar  intstrhermes_cli.runtime_providerr  r   r   debug)_loadcfg	model_cfgmodel_idr  raw_ctx
config_ctxr  r  r	  r  rtrt_excr   s                 r   r   r   Y  s   1::::::eggm(237773C3CT(J(JRCGGG$$$PR	)T** 	IMM'**LimmI.F.FL"SSUU 	1AAAAAA
 -- 011 *7C 8 8RWq[[WWd
 y}}Z006B77==??y}}Z006B77==?? 	PPPPPP--&X     rvvj11CXCDDJJLLbffY//5266<<>>   Rf        ++",
 
 
    	    BAFFFqqqqqsJ   B(H- ,B*H- BG H- 
H%HH- H!H- -
I7II>   todomemorydelegate_tasksession_searchrg   rj   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 	 ddlm}  ||                    d          |          }n# t          $ r Y nw xY w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   )unrename_tool_argsr   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   r/  r   r   itemstupler  _coerce_valuer  r   r   r   r   r   __name__"_normalize_json_strings_for_schema_schema_allows_null)
r*  r+  r2  r.  r/  keyvalueprop_schemaexpectedcoerceds
             r   coerce_tool_argsrA    s   $  z$--  ++F **\**0b55lCCJ ======!!&**\":":DAA    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	Ks   !%B 
BBr2  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   brB  s     r   	<genexpr>z'_schema_accepts_kind.<locals>.<genexpr>G  s?       .
 .
./ D)).
 .
 .
 .
 .
 .
r   )r  r  r   r   any)r2  rB  r1   	union_keybranchess    `   r   rI  rI  9  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).
    r0  r4  r3  {r6  Fr.  T)r  r  r  r  rI  r   jsonloads
ValueError	TypeErrorr   r   r:  r   r6  )r=  r2  trimmedexpects_arrayexpects_objectparseditems_schemachangedoutitemnxtpropskr>  s                 r   r:  r:  N  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.
    nullNr1  >   numberintegerrc  )integer_onlybooleanr0  r4  )
r;  r  lowerr  r   r8  _coerce_number_coerce_boolean_coerce_jsonr  )r=  expected_typer2  r1   rE   s        r   r8  r8    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   ra  Tnullable)rD  rE  )r  r  r   r   )r2  schema_typerM  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)
rQ  rR  rS  rT  r   r   r9  r  r  r   )r=  rp  rX  excs       r   ri  ri    s    E""	"   W )	
 	
 	

  &.// C )	
 	
 	
 
NNVV%  
 Ls    A"A
AArd  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)floatrS  OverflowErrorr  )r=  rd  fs      r   rg  rg    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  rf  )r=  lows     r   rh  rh    s<    
++--



C
f}}t
g~~uLr   rE   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errorr   )okNN)r  r  rQ  rR  r  r   r   )rE   parsed_results     r   _tool_result_observer_fieldsr    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_argsr  r  r  r  r  r  r  r  r  r  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+  rE   r  r  r  r  r  r  r  r  r  r  zpost_tool_call hook error: %s)hermes_cli.lifecycler  r  r  r   r   r   r  )r  r  rE   r  r  r  r  r  r  r  r  r  r  r  r  	_hook_errs                   r   _emit_post_tool_call_hookr    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skip_tool_execution_middlewaretool_request_middleware_tracec                    3 t           |          }t          |t                    si }t          |pg           }d}	 ddlm} n# t          $ r d}Y nw xY w|1|                               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          |pd          S |                    |          }||vrt          d| d	          S |                    ||          }||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            d          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|9t          |          }t;           |||||dd|t          |                     |S 	 ddlm}  | |          } | | S nJ# t          $ r=}!t0                              d|!            dv rt          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          3dtR          tT          tV          f         d tV          f 3fd!}'n)dtR          tT          tV          f         d tV          f fd"}'|r |'|          }n$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/}*m0}+  |*d(          rStc          |          \  },}-}. |+d( ||pdpd|pd|pd|pd|)|,|-|.)          }/|/D ]}0t          |0tT                    r|0} nn2# t          $ r%}t0                              d*|           Y d}~nd}~ww xY w|S # t          $ rU}1d+  d,tU          |1           }2t0          2                    |2           t          tg          |2                    cY d}1~1S d}1~1w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.
        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)rz   r{   r|   r}   )current_tool_defsztool_call could not be resolved'zO' is not available in this session. Use tool_search to find tools you can call.)r  r  r  r  r  r  r  r  r  r  r  rz   r{   )apply_tool_request_middlewarer   )r  r  r  r  r  z!tool_request middleware error: %sz" must be handled by the agent loop)resolve_pre_tool_block)r  r  r  r  r  r  zpre_tool_call hook error: %sblockedplugin_block)r  r  rE   r  r  r  r  r  r  r  r  r  )maybe_require_edit_approvalz!ACP edit approval guard error: %s>   ri   rh   z+Edit approval denied: approval guard failed)notify_other_tool_callr  )#reset_current_observability_context!set_current_observability_context)r  r  r   	next_argsr   c                 6    t          j        |           S )N)r  r  r  r   dispatch)r  r  r   r  r  s    r   	_dispatchz'handle_function_call.<locals>._dispatch<  s+    #,%y '#-&5	   r   c                 6    t          j        |           S )N)r  r  r  r  )r  r  r  r  r  s    r   r  z'handle_function_call.<locals>._dispatchD  s+    #,%y '#-"+	   r   )run_tool_execution_middleware)original_argsr  r  r  r  r  i  )
r  r  rE   r  r  r  r  r  r  r  r  transform_tool_result)r*  r+  rE   r  r  r  r  r  r  r  r  r  z$transform_tool_result hook error: %szError executing z: )4rA  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_callr   scoped_deferrable_namesvalidate_deferred_call_argshandle_function_callhermes_cli.middlewarer  payloadoriginal_payloadtracer   r  _AGENT_LOOP_TOOLShermes_cli.pluginsr  r  acp_adapter.edit_approvalr  _READ_SEARCH_TOOLStools.file_toolsr  time	monotonictools.approvalr  r  rS   r   r  r   r  r  r  r  r  r  	exceptionr)  )4r  r  r  r  r  r  r  r  r  r  r  r  r  rz   r{   _tool_middleware_trace_ts_modcurrent_defsunderlying_nameunderlying_argserr_scoped_deferrable
_probe_err_tool_original_argsr  _tool_request_mw_mw_errblock_messager  r  rE   r  edit_block_message_edit_approval_errr  _dispatch_start_approval_tokensr  r  r  r  r  r  r  r  r  r  hook_resultshook_resultr   r  r   s4   ` ` `  `                                           @r   r  r  H  s	   T %]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 L/ L!#"J)JKKK ")!@!@!N!N&888!B B B B   !<<_o^^J%!! (--)%#+(?-I/M.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};---RRRSSS ' !	+/MHEEEEEE 6 6!!#Mr)/R!-!3#Mr#1#7R%)*@%A%A	! 	! 	!  H H H;YGGGGGGGGH (#M22)"/"/!#)!-##1$-"/%)*@%A%A    
		QMMMMMM!<!<]M!Z!Z!-)) . 	Q 	Q 	QLL<>PQQQ 777!"OPPPPPPPP 87777	Q  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          . "=11OOOOOO66!!"5#Mr)/R!-!3#Mr#1#7R
 
 
  +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BBBBBBBB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  ; ; ;@}@@A@@	###.y99::::::::;s  A AA4B
 
BB 7G 
H"HHU# &U# +0I U# 
J&JU# J=U# 
K!  U# !
L(+-L#L(U# U# #L((U# 5M	 U# 	
MU# MU# /N	 U# 	NU# NU# BQ 2U# 7Q U# 
QU# QU# Q8Q&%Q8&
Q30Q82Q33Q88AU# A&T/ .U# /
U9UU# UU# #
W-A
V=7W=Wc                  (    t          j                    S )z!Return all registered tool names.)r   get_all_tool_namesr   r   r   r  r    s    &(((r   c                 *    t          j        |           S )z%Return the toolset a tool belongs to.)r   get_toolset_for_tool)r*  s    r   r  r    s    (333r   c                  (    t          j                    S )z0Return toolset availability info for UI display.)r   get_available_toolsetsr   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    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   )r   N)NNFFrH  )F)NNNNNNNFFFNNN)Y__doc__r   rQ  rer   loggingr?   r  typingr   r   r   r   r   tools.registryr   r	   r
   r   r   r   r   r   	getLoggerr9  r   r*   r   __annotations__r   r   r   Lockr   localr$   r    r&   rO   r  rP   r   r   r  get_tool_to_toolset_maprQ   r  get_toolset_requirementsrR   r  rS   r   ru   r7  r   ry   r   r   r  r   r  r  compile
IGNORECASEr"  	MULTILINEr$  r%  DOTALLr&  r'  r)  rA  rI  r:  r8  r;  r   ri  rg  rh  r  r  r  r  r  r  r  r  r   r   r   <module>r     sj	    * 
			  				        3 3 3 3 3 3 3 3 3 3 3 3 3 3              7 6 6 6 6 6 6 6		8	$	$ !$ # % % %T     
 ).""&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&+	R RtCy)RS	*R R  $	R
 
$sCx.R R R Rl -1-1&+	[ [tCy)[S	*[ [  $	[
 
$sCx.[ [ [ [|7 7 7 7 7D JII !>2 & %"*YM   'BJ'OQSQ]^^ 'RZr|DD !rz"8")DD  'C 'C ' ' ' '(i i4S> id38n i i i iX 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 ""& $!$(#)-$)).+0DH,0-1O; O;O;S>O; c]O; 3-	O;
 O; c]O; SMO; }O; DI&O; "O; #'O; %)O; $,Dc3h,@#AO; tCy)O;  S	*O;  	!O; O; O; O;l
)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3CC