
    sj!"             !          U d Z ddlZddlZddlZddlZddlmZ ddlmZ 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dd	
          Zee         e d<   ed             Z! e"            Z#e"e d<   defdZ$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(# e0$ r Z1e2                    de1           Y dZ1[1ndZ1[1ww xY w ej3                    Z4ee5e5f         e d<    ej6                    Z7ee5e8f         e d<   g a9ee5         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e<eee5ef                  f         e d$<    e
j'                    Z=d%Z>ddd&Z?	 	 	 	 ded'eee5                  d(eee5                  d)ed*edeee5ef                  f
d+Z@	 	 	 	 ded'eee5                  d(eee5                  d)ed*edeee5ef                  f
d,ZAdeBfd-ZCh d.ZDd/d0hZE ejF        d1ejG                  ZH ejF        d2ejI                  ZJ ejF        d3ejI                  ZK ejF        d4ejL                  ZMdd5lmNZO d6e5de5fd7ZPd8e5d9ee5ef         dee5ef         fd:ZQd;ed<e5defd=ZRd>ed;edefd?ZSdfd>e5d;e8dz  fd@ZTd;e8dz  defdAZUd>e5dBeVfdCZWdgd>e5dDefdEZXd>e5fdFZYd8e5dGede<e5ee5         ee5         f         fdHZZdddddddddddI
dJe5dKee5ef         dGedLee5         dMee5         dNee5         dOee5         dPee5         dQeBdRee5         dSee5         dTee5         dUeeee5ef                           ddfdVZ[	 	 	 	 	 	 	 	 	 	 	 	 	 dhdJe5dKee5ef         dLee5         dNee5         dMee5         dOee5         dPee5         dWee5         dXeee5                  dYedZed[ed\eeee5ef                           d'eee5                  d(eee5                  de5f d]Z\dee5         fd^Z]d8e5dee5         fd_Z^dee5e8f         fd`Z_dee5ef         fdaZ`dgdbedeee5         ee8         f         fdcZadS )ia  
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)contextmanager)
ContextVar)DictAnyListOptionalTuple)CHECK_FN_CACHE_BYPASScheck_fn_cache_scopediscover_builtin_toolsregistry
tool_error)resolve_toolsetvalidate_toolsetpost_tool_call_hook_suppressedF)default_post_tool_call_hook_suppressedc               #      K   t                               d          } 	 dV  t                               |            dS # t                               |            w xY w)z7Let an outer executor own the terminal post-tool event.TN)r   setreset)tokens    //home/agent/.hermes/hermes-agent/model_tools.pysuppress_post_tool_call_hookr   2   s^       ,//55E5'--e44444'--e4444s	   > A_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    r   _is_delegated_child_contextr!   @   sJ    GGGGGG))+++   uu    
  c                  F    	 ddl m}   |             S # t          $ r Y dS w xY w)zFalse when HERMES_KANBAN_* is present but this execution does not own it
    (delegate_task child, or a cron job fired in-process from a worker).r   "is_dispatcher_owned_worker_contextT)r   r%   r    r$   s    r   _is_dispatcher_owned_workerr&   I   sJ    OOOOOO11333   ttr"   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/   ]   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_loopr1   )r1   s    r   _get_worker_loopr5   l   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,   r   r4   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_contextrF   submitresultshutdownTimeoutErrorrH   r:   call_soon_threadsafer;   current_threadmain_threadr5   r9   r/   )r@   r1   
concurrentrC   poolrF   futurer?   	tool_looprA   rB   s   `        @@r   
_run_asyncr]      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                  x    t           5  t                                           ddd           dS # 1 swxY w Y   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)_tool_defs_cache_lockr   clearr-   r.   r   _clear_tool_defs_cacher   ;  s     
 ! !   ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !s   /33enabled_toolsetsdisabled_toolsets
quiet_modeskip_tool_search_assemblyc           
      t   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    rt          j                    | t          |           nd|rt          |          ndt          j        |t          t          j                            d                    t          |          t%                      t'                      |	f
}t(          5  |t*                              |          nd}
ddd           n# 1 swxY w Y   |
d |
D             at/          |
          S t1          | |||          }|r|t(          5  t*                              |          }
|
bt3          t*                    t4          k    r9t*                              t9          t;          t*                                         |t*          |<   |}
ddd           n# 1 swxY w Y   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-   .0r?   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
   r   current_scope_key	frozenset_generationboolosenvirongetr!   r&   r   r   ra   list_compute_tool_definitionslen_TOOL_DEFS_CACHE_MAXpopnextiter)r   r   r   r   	cache_keyr   cfg_pathcfg_statcfg_fpprofile_scopecachedrS   s               r   get_tool_definitionsr   C  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 # 	X 	X8A8M%)))444SWF	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X )P(O(O(O(O% <<&'79JJAZ\ \ \F i+ # 	  	  &)))44F~'((,@@@$((d3C.D.D)E)EFFF.4 +	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  F|| F||Ms4   2: AAD<<E E A?HHHc                 p  & t                      }| 5t          |           }t          j                            d          r5t                      s't                      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	|            t3          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!} tE          ||&                   } |            }n# tF          $ 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{d&|v r%d'|vr!d( |D             }|$                    d&           |sS|rBd) |D             }t          d*tM          |           d+d                    |                      nt          d,           d- |D             a'	 d
d.l(m)}  ||          }n2# tF          $ r%}t.          *                    d/|           Y d}~nd}~ww xY w	 d
d0l+m,} m-}!  |!            }"|s|"j.        d1k    r}t_                      }# | ||#|"2          }$|$j0        rS|sQd3d4d5d6d7d8}%t          d9|$j1         d:|$j2         d;|$j3         d<|%                    |$j4        |$j4                   d=	           |$j5        }n2# tF          $ 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   r?   discord_tool_names     r   r   z-_compute_tool_definitions.<locals>.<listcomp>%  sK     " " "uuZ,,0088<MMM MMMr.   rj   rb   rc   description zV For simple information retrieval, prefer web_search or web_extract (faster, cheaper).browser_execrd   c                 l    g | ]1}|                     d i                                d          dk    /|2S )r   r   r   r   )r   tds     r   r   z-_compute_tool_definitions.<locals>.<listcomp>M  sI     
 
 
vvj"%%))&11^CC CCCr.   c                 *    g | ]}|d          d         S r   r-   r   s     r   r   z-_compute_tool_definitions.<locals>.<listcomp>U  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)6r   r   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   r2   r    discardreplacer   ra   tools.schema_sanitizerr   warningtools.tool_searchr   r   enabled_resolve_active_context_length	activatedtierdeferred_countdeferred_tokenslisting_form	tool_defs)'r   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ir   _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     sz	     EE#%)*:%;%;"JNN/00	8/11	8 ,--	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( ---*DX2X2X
 
'
 
 
 	$$^444 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;;P
	P
"V4 4
W#>WW#'BZ 
Z3Z..Z3c            	         	 ddl m}   |             pi }t          |                    d          t                    r|                    d          ni }t          |t                    si }|                    d          p|                    d          pd}t          |t                    rddl m}  ||          \  }}t          |                                          }|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|	C|rA	 ddlm}  |||          }t          |t                    r|dk    r|S n# t          $ r Y n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   modelr   r   )split_model_config_default)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)get_cached_context_length)r  r  config_context_lengthr  z+Could not resolve active context length: %s)r   r   
isinstancer   dictr  strstripagent.model_metadatar  inthermes_cli.runtime_providerr  r    r   debugr  )_loadcfg	model_cfg_raw_model_idr  _model_idr  raw_ctx
config_ctxr  r  r  r  rtrt_excr  
cached_ctxr  s                      r   r   r     s   G::::::eggm(237773C3CT(J(JRCGGG$$$PR	)T** 	I!g..P)--	2J2JPbmT** 	IDDDDDD99-HHM1}%%++-- 	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         (JJJJJJ66xJJ
j#.. &:>>%%   ++",
 
 
    	    BAFFFqqqqqsn   C J* $B*J* BH J* 
IH>9J* >IJ* .I; :J* ;
JJ* J!J* *
K4KK>   todomemorydelegate_tasksession_searchru   rx   zR</?(?:tool_call|function_call|result|response|output|input|system|assistant|user)>z%^\s*```(?:json|xml|html|markdown)?\s*z
\s*```\s*$z<!\[CDATA\[.*?\]\]>)_MAX_TOOL_ERROR_CHARS	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)r7  	sanitizeds     r   _sanitize_tool_errorrA  	  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   rG  r    r   itemstupler!  _coerce_valuer"  r   r   r   r   r   __name__"_normalize_json_strings_for_schema_schema_allows_null)
rB  rC  rJ  rF  rG  keyvalueprop_schemaexpectedcoerceds
             r   coerce_tool_argsrY    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rJ  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   brZ  s     r   	<genexpr>z'_schema_accepts_kind.<locals>.<genexpr>  s?       .
 .
./ D)).
 .
 .
 .
 .
 .
r.   )r  r   r   r   any)rJ  rZ  r?   	union_keybranchess    `   r   ra  ra    s     fd## u

6ADyyZ4((yTQYYt0  	::i((h%% 	# .
 .
 .
 .
3;.
 .
 .
 +
 +
 	 445r.   rU  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).
    rH  rL  rK  {rN  FrF  T)r  r   r!  r"  ra  r   jsonloads
ValueError	TypeErrorr   r   rR  r   rN  )rU  rJ  trimmedexpects_arrayexpects_objectparseditems_schemachangedoutitemnxtpropskrV  s                 r   rR  rR    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.
    nullNrI  >   numberintegerr{  )integer_onlybooleanrH  rL  )
rS  r"  lowerr  r   rP  _coerce_number_coerce_boolean_coerce_jsonr   )rU  expected_typerJ  r?   rS   s        r   rP  rP    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   ry  Tnullable)r\  r]  )r  r   r   r   )rJ  schema_typere  variantsvariants        r   rS  rS    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)
ri  rj  rk  rl  r   r   rQ  r  r&  r   )rU  r  rp  excs       r   r  r     s    E""	"   W )	
 	
 	

  &.// C )	
 	
 	
 
NNVV%  
 Ls    A"A
AAr|  c                    	 t          |           }n# t          t          f$ r | cY S w xY w||k    s&|t          d          k    s|t          d          k    r| S |t          |          k    rt          |          S |r| S |S )zFTry to parse *value* as a number.  Returns original string on failure.infz-inf)floatrk  OverflowErrorr$  )rU  r|  fs      r   r  r  ?  s    %LL&    	AvveEll""a5==&8&8CFF{{1vv Hs    ((c                 r    |                                                                  }|dk    rdS |dk    rdS | S )zGTry to parse *value* as a boolean.  Returns original string on failure.trueTfalseF)r"  r~  )rU  lows     r   r  r  Q  s<    
++--



C
f}}t
g~~uLr.   rS   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dlm}  || |          \  }}|r,dd|	                                	                    d          pd fS n# t          $ r Y nw xY wdS )Nerrorr   r   )_detect_tool_failurez[])okNN)
r  r!  ri  rj  r   r   r    agent.displayr  r"  )rB  rS   parsed_resultr  failedsuffixs         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   666666--i@@ 	ML&,,..*>*>t*D*D*LLL	M   s%   A9A= =
B
	B
AC 
CC)
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                 ^   t                                           rdS 	 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).
    Nr   has_hookinvoke_hookpost_tool_callr   )rB  rC  rS   r  r  r  r  r  r  r  r  r  r  zpost_tool_call hook error: %s)
r   r   hermes_cli.lifecycler  r  r  r   r    r   r&  )r  r  rS   r  r  r  r  r  r  r  r  r  r  r  r  	_hook_errs                   r   _emit_post_tool_call_hookr  p  s4   2 '**,, A>>>>>>>>x()) 	F>0L1 1-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                    345 t                     t          t                    si t          |pg           4t	          j                    3dt          dt          f34 f	d}d}	 ddlm} n# t          $ r d}Y nw xY w|`|
                               rJ	 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          4          ||          S t                    }|
sk	 ddlm}  | pdpdpdpdpd          }|j        |j        }|j        4n2# t          $ r%}t6                              d|           Y d}~nd}~ww xY w	  t:          v rt%            d          S |	sd}	 ddlm}  | pdpdpdpdpdt          4                    \  }}||n2# t          $ r%}t6                              d|           Y d}~nd}~ww xY w|9t%          |          }tA           |dd|t          4                     |S 	 ddl!m"}   |            }!|!)tA           |!ddt          4                     |!S ns# t          $ rf}"t6                              d|"            dv r=t%          d          }tA           |ddt          4                     |cY d}"~"S Y d}"~"nd}"~"ww xY w tF          vr%	 dd l$m%}#  |#pd!           n# t          $ r Y nw xY wt	          j                    3d}$	 dd"l&m'}%m(}&  |&pdpdpd#          }$n# t          $ r d}%Y nw xY w	  d$k    r5||ntR          5d%tT          tV          t          f         dt          f 5fd&}'n)d%tT          tV          t          f         dt          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[          t	          j                    3z
  d*z            })tA           ||)t          4          +
  
         	 dd,l.m/}*m0}+  |*d-          rTtc           |          \  },}-}. |+d- |pdpdpdpdpd|)|,|-|..          }/|/D ]}0t          |0tV                    r|0} nn2# t          $ r%}t6                              d/|           Y d}~nd}~ww xY w|S # t          $ r}1d0  d1tW          |1           }2t6          2                    |2           t%          tg          |2                    }3&t[          t	          j                    3z
  d*z            nd})tA           ||)d2ti          |1          j5        tW          |1          t          4          3           |cY d}1~1S d}1~1ww xY w)4a  
    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.
    rS   r   c                    	 t          | 	t          t          j                    z
  dz            t	                    
  
         | S )N  
r  r  rS   r  r  r  r  r  r  r  )r  r$  time	monotonicr   )
rS   _dispatch_start_tool_middleware_tracer  r  r  r  r  r  r  s
    r   _return_bridge_resultz3handle_function_call.<locals>._return_bridge_result  sb    !''!%)T^--?4GHH!"899	
 	
 	
 	
 r.   Nr   )tool_searchT)r   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  r  r  r   r   )apply_tool_request_middlewarer   )r  r  r  r  r  z!tool_request middleware error: %sz" must be handled by the agent loop)_dispatch_pre_tool_call_hooks)r  r  r  r  r  r  zpre_tool_call hook error: %sblockedplugin_block)r  r  rS   r  r  r  r  r  r  r  r  r  )maybe_require_edit_approvaledit_approval_denied)r  r  rS   r  r  r  r  r  r  r  r  z!ACP edit approval guard error: %s>   rw   rv   z+Edit approval denied: approval guard failededit_approval_error)notify_other_tool_callr   )#reset_current_observability_context!set_current_observability_context)r  r  r  r   	next_argsc                 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>._dispatch  s+    #,%y '#-"+	   r.   )run_tool_execution_middleware)original_argsr  r  r  r  r  r  r  r  transform_tool_result)rB  rC  rS   r  r  r  r  r  r  r  r  r  z$transform_tool_result hook error: %szError executing z: r  )r  r  rS   r  r  r  r  r  r  r  r  r  r  )6rY  r  r   r   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  tools.approvalr  r  ra   r   r!  r  r$  r  r  r  r  	exceptionrA  r   rQ  )6r  r  r  r  r  r  r  r  r  r  r  r  r  r   r   r  _ts_modcurrent_defsunderlying_nameunderlying_argserr_scoped_deferrable
_probe_err_tool_original_argsr  _tool_request_mw_mw_errblock_messager  modified_argsr  rS   r  edit_block_message_edit_approval_errr  _approval_tokensr  r  r  r  r  r  r  r  r  r  hook_resultshook_resultr  r7  r  r  r  s6   ````````                                           @@@r   r  r    s   T %]MBBMmT** !"?"E2FF n&&Oc c               G0000000    w55mDD	 0!1"34    	 L
  	 	 	LLL	G444((,,!'R&2 -     G666((..!'R&2 /     G2224;4S4STaTgeg4h4h1O_c / ,,sG&GHH   ")!@!@!N!N&888,,FO F F F    !<<_o^^J%,,Z888 (--)%-#+(?-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s---RRRSSS ' #	+/MHLLLLLL/L/L!!#Mr)/R!-!3#Mr#1#7R%)*@%A%A	0 	0 	0,} !,$1M H H H;YGGGGGGGGH (#M22)"/"/!#)!-##1$-"/%)*@%A%A    
$	MMMMMM!<!<]M!Z!Z!-)"/"/-#)!-##1$5%)*@%A%A    *) .  	 	 	LL<>PQQQ 777#$QRR)"/"/!#)!-##1$4%)*@%A%A     87777	*  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 4P!5 5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@@	###0;;<< * !!O3t;<<< 	
 	"''!%)#Aww'a&&!"899	
 	
 	
 	
 1s  6A= =BB)B? ?CC7H< <
I+I&&I+/X" 
X" 7K X" 
K6K1,X" 1K66=X" 5<M3 2X" 3
O#=AOO#X" X" O##X" 0P X" 
PX" PX" *Q X" QX" QX" BT 0X" 5T  X" 
TX" TX" T6T$#T6$
T1.T60T11T66AX" A'W. -X" .
X8XX" XX" "
[5,B>[0*[50[5c                  (    t          j                    S )z!Return all registered tool names.)r   get_all_tool_namesr-   r.   r   r  r  S  s    &(((r.   c                 *    t          j        |           S )z%Return the toolset a tool belongs to.)r   get_toolset_for_tool)rB  s    r   r  r  X  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  b  s    .000r.   r   c                 ,    t          j        |           S )z.Return (available_toolsets, unavailable_info).r   )r   check_tool_availabilityr   s    r   r  r  g  s    +%8888r.   )r   N)NNFFr`  )F)NNNNNNNFFFNNN)b__doc__r   ri  rer+   
contextlibr   contextvarsr   loggingrM   r  typingr   r   r   r   r	   tools.registryr
   r   r   r   r   r   r   r   	getLoggerrQ  r   r   r   __annotations__r   r   r   r!   r&   r)   Lockr(   localr3   r/   r5   r]   r  r^   r    r  r&  get_tool_to_toolset_mapr_   r!  get_toolset_requirementsr`   r   ra   r   r   rO  r   r   r   r   r   r$  r   r  r  compile
IGNORECASEr:  	MULTILINEr<  r=  DOTALLr>  r6  r?  rA  rY  ra  rR  rP  rS  r   r  r  r  r  r  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              7 6 6 6 6 6 6 6		8	$	$4>J$e5 5 5 D!1   
 5 5 5 !$ # % % %T    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&+	[ [tCy)[S	*[ [  $	[
 
$sCx.[ [ [ [~ -1-1&+	k ktCy)kS	*k k  $	k
 
$sCx.k k k k\M M M M Mp JII !>2 & %"*YM   'BJ'OQSQ]^^ 'RZr|DD !rz"8")DD 
 H G G G G G'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     3x},-   4 " $"&!$(  $#'7;5A 5A 5A5A S>5A 	5A
 c]5A 5A 3-5A c]5A SM5A 5A SM5A 5A C=5A tDcN345A 
5A 5A 5A 5Av ""& $!$(#)-$)).+0DH,0-1d ddS>d c]d 3-	d
 d c]d SMd }d DI&d "d #'d %)d $,Dc3h,@#Ad tCy)d  S	*d  	!d d d dV)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   C# #D(DD