
    `gjx                        U d Z ddlZddlZddlZddlZddlZddlZddlZddlm	Z	m
Z
mZmZmZ ddlmZmZ ddlmZmZ  ej(                  e      Z e       Zeed<   da ej6                         Z ej:                         Zd Zd Z d	 Z! e        	 dd
l"m#Z#  e#         ejN                         Z(e	e)e)f   ed<    ejT                         Z+e	e)e,f   ed<   g a-ee)   ed<   ddgdgdgdgg dg ddgg ddgd	Z.i Z/e	e0ee	e)e
f      f   ed<   dZ1dZdZ2	 	 	 	 d[deee)      deee)      d e3d!e3dee	e)e
f      f
d"Z4	 	 	 	 d[deee)      deee)      d e3d!e3dee	e)e
f      f
d#Z5de6fd$Z7h d%Z8d&d'hZ9 ejt                  d(ejv                        Z< ejt                  d)ejz                        Z> ejt                  d*ejz                        Z? ejt                  d+ej                        ZAd,ZBd-e)de)fd.ZCd/e)d0e	e)e
f   de	e)e
f   fd1ZDd2e
d3e)de3fd4ZEd5e
d2e
de
fd6ZFd\d5e)d2e,dz  fd7ZGd2e,dz  de3fd8ZHd5e)d9eIfd:ZJd]d5e)d;e3fd<ZKd5e)fd=ZLd>e
de0e)ee)   ee)   f   fd?ZMddddddddddd@
dAe)dBe	e)e
f   d>e
dCee)   dDee)   dEee)   dFee)   dGee)   dHe6dIee)   dJee)   dKee)   dLeee	e)e
f         ddfdMZN	 	 	 	 	 	 	 	 	 	 	 	 d^dAe)dBe	e)e
f   dCee)   dEee)   dDee)   dFee)   dGee)   dNee)   dOeee)      dPe3dQe3dReee	e)e
f         deee)      deee)      de)fdSZOdee)   fdTZPd/e)dee)   fdUZQde	e)e,f   fdVZRde	e)e3f   fdWZSd]dXe3deee)   ee,   f   fdYZTy# e$$ rZ%ejM                  de%       Y dZ%[%dZ%[%ww xY w)_a  
Model Tools Module

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

Public API (signatures preserved from the original 2,400-line version):
    get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode) -> list
    handle_function_call(function_name, function_args, task_id, user_task) -> str
    TOOL_TO_TOOLSET_MAP: dict          (for batch_runner.py)
    TOOLSET_REQUIREMENTS: dict         (for cli.py, doctor.py)
    get_all_tool_names() -> list
    get_toolset_for_tool(name) -> str
    get_available_toolsets() -> dict
    check_toolset_requirements() -> dict
    check_tool_availability(quiet) -> tuple
    N)DictAnyListOptionalTuple)discover_builtin_toolsregistry)resolve_toolsetvalidate_toolset_WARNED_DISABLED_BUNDLESc                      t         5  t        t        j                         rt        j                         at        cddd       S # 1 sw Y   yxY w)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     >/root/.hermes/venv/lib/python3.12/site-packages/model_tools.py_get_tool_loopr   3   s>     
 !5!5!7 //1J  s   4AAc                      t        t        dd      } | | j                         r4t        j                         } t        j
                  |        | t        _        | S )u  Return a persistent event loop for the current worker thread.

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

    By keeping the loop alive for the thread's lifetime, cached clients
    stay valid and their cleanup runs on a live loop.
    loopN)getattr_worker_thread_localr   r   r   set_event_loopr   )r   s    r   _get_worker_loopr   B   sK     '6D|t~~'%%'t$$(!Kr   c                 R   	
 	 t        j                         }|r|j                         rddl}d
t        j                         	 	
fd}|j                  j                  d      }ddl	m
} |j                   ||            }	 |j                  d      |j                  d	
       S t        j&                         t        j(                         urt+               

j-                         S t/               }|j-                         S # t        $ r d}Y w xY w# |j                  j                  $ r] 	j                  d      rI
G	 t        j                   
      D ]  }
j#                  |j$                          	  # t        $ r Y  w xY w w xY w# |j                  d	
       w xY w)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            	      n   t        j                         j                          	 t        j                         j	                        	 t        j
                        } | D ]  }|j                           | r$j	                  t        j                  | ddi       j                          S # t        $ r Y w xY w# 	 t        j
                        } | D ]  }|j                           | r$j	                  t        j                  | ddi       n# t        $ r Y nw xY wj                          w xY w)Nreturn_exceptionsT)
r   r   setr   run_until_complete	all_taskscancelgather	Exceptionclose)pendingtcoro
loop_readyworker_loops     r   _run_in_workerz"_run_async.<locals>._run_in_worker|   s!   !002KNN$&&{3"55d; &//<G$ #
##66#NNGLtL
 !!# !  &//<G$ #
##66#NNGLtL ! !!#sC   %B? AB00	B<;B<?D4ADD4	D D4D  D4   )max_workers)propagate_context_to_threadi,  )timeoutF)waitg      ?)r   get_running_loopRuntimeError
is_runningconcurrent.futures	threadingEventfuturesThreadPoolExecutortools.thread_contextr/   submitresultshutdownTimeoutErrorr1   r"   call_soon_threadsafer#   current_threadmain_threadr   r!   r   )r)   r   
concurrentr,   poolr/   futurer(   	tool_loopr*   r+   s   `        @@r   
_run_asyncrF   X   s   ('') ! 	";?__&
	$, !!444C 	E8HI	&===-  MMuM% !)>)>)@@&(--d33 I''--O  X !!.. 
	 s+0G$..{; C#88BC
  $  
	 MMuM%sM   D D DD-F5E>=F>	F
F	F

FF F&)discover_pluginszPlugin discovery failed: %sTOOL_TO_TOOLSET_MAPTOOLSET_REQUIREMENTS_last_resolved_tool_names
web_searchweb_extractterminalvision_analyzeimage_generate)skills_list
skill_viewskill_manage)
browser_navigatebrowser_snapshotbrowser_clickbrowser_typebrowser_scrollbrowser_backbrowser_pressbrowser_get_imagesbrowser_visionbrowser_consolecronjob)	read_file
write_filepatchsearch_filestext_to_speech)		web_toolsterminal_toolsvision_toolsimage_toolsskills_toolsbrowser_toolscronjob_tools
file_tools	tts_tools_tool_defs_cache   returnc                  ,    t         j                          y)zDrop memoized get_tool_definitions() results. Called when dynamic
    schema dependencies change (e.g. discord capability cache reset,
    execute_code sandbox reconfigured).N)rl   clearr   r   r   _clear_tool_defs_cacherq     s     r   enabled_toolsetsdisabled_toolsets
quiet_modeskip_tool_search_assemblyc           	         |r	 ddl m}  |       }|j                         }|j                  |j                  f}| t        |       nd|rt        |      ndt        j                  |t        t        j                  j                  d            t        |      f}t        j                  |      }	|	!|	D 
cg c]
  }
|
d   d    c}
at#        |	      S t%        | |||      }|rUt'        t              t(        k\  r+t        j+                  t-        t/        t                           |t        <   t#        |      S |S # t
        t        t        f$ r d}Y w xY wc c}
w )a  
    Get tool definitions for model API calls with toolset-based filtering.

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

    Args:
        enabled_toolsets: Only include tools from these toolsets.
        disabled_toolsets: Exclude tools from these toolsets (if enabled_toolsets is None).
        quiet_mode: Suppress status prints.
        skip_tool_search_assembly: When True, return the pre-assembly tool list
            (raw schemas for every enabled tool). Used internally by the
            tool_search / tool_describe bridge handlers so they can read the
            real catalog, not the already-collapsed one. Public callers should
            leave this False.

    Returns:
        Filtered list of OpenAI-format tool definitions.
    r   )get_config_pathNHERMES_KANBAN_TASKfunctionname)ru   )hermes_cli.configrw   statst_mtime_nsst_sizeFileNotFoundErrorOSErrorImportError	frozensetr	   _generationboolosenvirongetrl   rJ   list_compute_tool_definitionslen_TOOL_DEFS_CACHE_MAXpopnextiter)rr   rs   rt   ru   rw   cfg_pathcfg_statcfg_fp	cache_keycachedr(   r<   s               r   get_tool_definitionsr     sL   @ 	9&(H}}H**H,<,<=F ,<+GI&'T,=I'(4   456*+
	 "%%i0 IO(O1:v)>(O% <&'79JJAZ\F  $88  d+;&<!=>&,#F|MK "7K8 	F	 )Ps   5D8 3E8EEc           	         t               }| t        |       }t        j                  j	                  d      rd|vr|j                  d       |D ]  }t        |      rDt        |      }|j                  |       |r-t        d| d|rdj                  |      nd        R|t        v r>t        |   }|j                  |       |rwt        d| ddj                  |              |rt        d	|         n,d
dlm}	  |	       D ]  }
|j                  t        |
              |rf|D ]`  }t        |      rd
dlm}m} |j!                  d      s ||      xs i j	                  d      r ||      }|j#                  |       t%        |      }|su|j!                  d      rd|t&        vr\t&        j)                  |       t*        j-                  d||rdj                  |      nd       nt        |      }|j#                  |       |rt        d| d|rdj                  |      nd        |t        v r@t        |   }|j#                  |       |r-t        d| ddj                  |              O|rSt        d	|        c t/        j0                  ||      }|D ch c]
  }|d   d    }}d|v r_d
dlm}m}m} ||z  } || |             }t;        |      D ]3  \  }}|j	                  di       j	                  d      dk(  s+d|d||<    n ddd}|D ]  }||v s	 d
dlm} tA        |||         } |       }|E|D cg c](  }|j	                  di       j	                  d      |k7  r|* }}|jE                  |       lt;        |      D ]3  \  }}|j	                  di       j	                  d      |k(  s+d|d||<      d|v rzdd h|z  }|sqt;        |      D ]c  \  }}|j	                  di       j	                  d      dk(  s+|d   j	                  d!d"      }|jG                  d#d"      }di |d   d!|id||<    n |sL|r?|D cg c]
  }|d   d    }}t        d$tI        |       d%dj                  |              nt        d&       |D cg c]
  }|d   d    c}a%	 d
d'l&m'}   | |      }	 d
d)l)m*}"m+}#  |#       }$|sq|$jX                  d*k7  rbt[               }% |"||%|$+      }&|&j\                  r5|s3t        d,|&j^                   d-|&j`                   d.|&jb                   d/       |&jd                  }|S c c}w # tB        $ r d}Y w xY wc c}w c c}w c c}w # tB        $ r }!t*        jQ                  d(|!       Y d}!~!d}!~!ww xY w# tB        $ r!}!t*        jQ                  d0|!       Y d}!~!|S d}!~!ww xY w)1z8Uncached implementation of :func:`get_tool_definitions`.Nrx   kanbanu   ✅ Enabled toolset 'z': z, zno toolsu   ✅ Enabled legacy toolset 'u   ⚠️  Unknown toolset: r   )get_all_toolsets)bundle_non_core_toolsget_toolsetzhermes-posturezagent.disabled_toolsets contains platform-bundle name '%s'; core tools are preserved and only its platform-specific tools (%s) are removed. Bundle names usually belong in `toolsets:`, not `disabled_toolsets` (#33924).noneu   🚫 Disabled toolset 'u   🚫 Disabled legacy toolset 'quietry   rz   execute_code)SANDBOX_ALLOWED_TOOLSbuild_execute_code_schema_get_execution_mode)mode)typery   get_dynamic_schema_coreget_dynamic_schema_admin)discorddiscord_admin)discord_toolrS   rK   rL   description zV For simple information retrieval, prefer web_search or web_extract (faster, cheaper).u   🛠️  Final tool selection (z	 tools): u<   🛠️  No tools selected (all filtered out or unavailable))sanitize_tool_schemaszSchema sanitization skipped: %s)assemble_tool_defsload_configoff)context_lengthconfigu   🔎 Tool Search: z MCP/plugin tools deferred (~z6 tokens) behind tool_search/describe/call. Threshold ~z tokens.z Tool search assembly skipped: %s)3r    r   r   r   r   appendr   r
   updateprintjoin_LEGACY_TOOLSET_MAPtoolsetsr   r   r   
startswithdifference_updatesortedr   addloggerinfor	   get_definitionstools.code_execution_toolr   r   r   	enumeratetoolsr   r   r%   discardreplacer   rJ   tools.schema_sanitizerr   warningtools.tool_searchr   r   enabled_resolve_active_context_length	activateddeferred_countdeferred_tokensthreshold_tokens	tool_defs)'rr   rs   rt   ru   tools_to_includeeffective_enabled_toolsetstoolset_nameresolvedlegacy_toolsr   ts_namer   r   	to_removefiltered_toolsr(   available_tool_namesr   r   r   sandbox_enableddynamic_schemaitd_discord_schema_fnsdiscord_tool_name_dt	schema_fndynamicweb_tools_availabledesc
tool_namesr   er   _load_ts_configts_cfgr   assemblys'                                          r   r   r   e  sS     E#%)*:%;"::>>./HD^4^ '--h76 	BL-*<8 ''1!1,sZb499XCVhrBstu!442<@ ''5!8c$))T`JaIbcd1,@A	B 	.') 	>G##OG$<=	> - $	BL-G**95+l:S:YWY9^9^_h9i !6l CI$66yA%i0H&<+B+B9+M ,4L L044\B<
 )3;DIIh/  /|<H$66x@!3L>\dTYYxEXjtDuvw!442<@ 22<@!:<.DIIVbLcKdef1,@AI$	BZ --.>jQN <JJaAjM&1JJ --ss/2FF2?I\I^_~. 	EArvvj"%))&1^C-7^$Tq!	 -3 1  445#C)<=N)OP	#+ -"uuZ,008<MM " " %,,->?&~6 EArvvj"-11&9=NN5?W,Uq)0 11+];>RR"">2 266*b)--f59KKj>--mR@D<<pD
 !+$Kr*~$K}d$K)N1%  9GHA!J-/HJH3C4G3H	RVR[R[\fRgQhijPQ AO O1:v!6 O=@.~>>X "(V^^u-D;=N)-H
 !!*()@)@(A B!112 3""*";";!<HF
 &//N i K<  "B I !P  =8!<<=<  >91==>s[   *U
.U-U!U&$U+7U0 BV UU0	V9VV	W%WWc                     	 ddl m}   |        xs i }t        |j                  d      t              r|j                  d      ni }t        |t              si }|j                  d      xs |j                  d      xs dj                         }|syddlm} |j                  d      }t        |t              r|dkD  r|nd}t         |||	      xs d      S # t        $ r }t        j                  d
|       Y d}~y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)config_context_lengthz+Could not resolve active context length: %s)r{   r   
isinstancer   dictstripagent.model_metadatar   intr%   r   debug)_loadcfg	model_cfgmodel_idr   raw_ctx
config_ctxr   s           r   r   r   :  s    :gm(237773CT(JCGGG$PR	)T*IMM'*LimmI.FL"SSUA
 -- 01 *7C 8Wq[Wd
+HJW\[\]] BAFs   BC AC 	D  C;;D >   todomemorydelegate_tasksession_searchr^   ra   zR</?(?:tool_call|function_call|result|response|output|input|system|assistant|user)>z%^\s*```(?:json|xml|html|markdown)?\s*z
\s*```\s*$z<!\[CDATA\[.*?\]\]>i  	error_msgc                    | syt         j                  d|       }t        j                  d|      }t        j                  d|      }t        j                  d|      }t        |      t        kD  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7I
9~++62Q67%?	9+&&r   	tool_nameargsc                 @   |rt        |t              s|S t        j                  |       }|s|S |j	                  d      xs i j	                  d      }|s|S t        |j                               D ]  \  }}|j	                  |      }|s|j	                  d      }|dk(  r|t        |t
        t        f      st        |t              rlt        |||      }||ur|||<   q|j                         j                  d      rt        j                  d| |       |g||<   t        j                  d| |       |g||<   t        j                  d	t        |      j                   | |       t        |t              sQ|dk(  r&t        |t
        t        f      rt#        ||      ||<   n$|d
k(  rt        |t              rt#        ||      ||<   X|st%        |      sgt        |||      }||us{|||<   t        |t
        t        t        f      s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   arrayschema[u   coerce_tool_args: %s.%s looks like a JSON array string but could not be parsed — model may have emitted a JSON-encoded string instead of a native array. Falling back to single-element list.z7coerce_tool_args: wrapped bare string in list for %s.%sz3coerce_tool_args: wrapped bare %s in list for %s.%sobject)r   r   r	   
get_schemar   r   itemstuplestr_coerce_valuer   r   r   r   r   r   __name__"_normalize_json_strings_for_schema_schema_allows_null)	r  r  r  r
  keyvalueprop_schemaexpectedcoerceds	            r   coerce_tool_argsr    s   $ z$-  +F**\*0b55lCJ4::<( @U
U nnS)??6* w5#4ZPTV[}=]%%'xL%' !(DI ;;=++C0NN? "3 #GS	Ms DIKKEU$$i %% 7"z%$'G>ukRS	X%*UD*A>ukRS	 3K @xD%DI 'D%#67>wTS	A@UD Kr   r  kindc                     t        | t              sy| j                  d      }|k(  st        |t              r|v rydD ]:  }| j                  |      }t        |t              s%t	        fd|D              s: y y)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   6   K   | ]  }t        |        y wN)_schema_accepts_kind).0br  s     r   	<genexpr>z'_schema_accepts_kind.<locals>.<genexpr>   s      .
./ D).
s   )r   r   r   r   any)r  r  r(   	union_keybranchess    `   r   r%  r%    s~     fd#

6ADyZ4(TQY0 	::i(h%# .
3;.
 +
  r   r  c                    t        |t              s| S t        | t              r| j                         }t	        |d      }t	        |d      }|r|j                  d      s|rS|j                  d      rB	 t        j                  |      }t        |t              r|r|} nt        |t              r|r|} n| S | S t        | t              rY|j                  d      }t        |t              s| S d}g }| D ]'  }	t        |	|      }
|xs |
|	u}|j                  |
       ) |r|S | S t        | t              r|j                  d      }t        |t              s| S d}t        |       }|j                         D ]8  \  }}|| vst        |t              st        | |   |      }
|
| |   us2|
||<   d}: |r|S | S | S # t        t        f$ r | cY S w xY w)	u  Recursively parse JSON-encoded string values that a schema expects to
    be arrays or objects, including nested array items and object properties.

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

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

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

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

<(%&L5k#kkm 	NA{~ZT%B4U1X{KC%("A	 s(5(LQ 	* s   2F1 1GGc                    t        |      r"| j                         j                         dk(  ryt        |t              r|D ]  }t        | ||      }|| us|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"| j                         j                         dk(  ry| S )
zAttempt to coerce a string *value* to *expected_type*.

    Returns the original string when coercion is not applicable or fails.
    nullNr  >   numberintegerr@  )integer_onlybooleanr  r  )
r  r   lowerr   r   r  _coerce_number_coerce_boolean_coerce_jsonr   )r  expected_typer  r(   r<   s        r   r  r  T  s    
 6"u{{}':':'<'F-& 	A"5!F;FU"	 --e=I3MOO	!u%%E4(( E4((5;;=#6#6#8F#BLr   c                 V   t        | t              sy| j                  d      }|dk(  ryt        |t              rd|v ry| j                  d      du rydD ]S  }| j                  |      }t        |t              s%|D ]*  }t        |t              s|j                  d      dk(  s)  y U y)z@Return True when a JSON Schema fragment explicitly permits null.Fr   r>  Tnullable)r   r!  )r   r   r   r   )r  schema_typer*  variantsvariants        r   r  r  q  s    fd#**V$Kf+t$;)>zz*%' 	::i((D) 	G'4(W[[-@F-J		 r   expected_python_typec                 t   	 t        j                  |       }t        ||      r"t        j                  d|j                         |S t        j                  dt        |      j                  |j                         | S # t        t        f$ r-}t        j                  d|j                  |       | cY d}~S d}~ww xY w)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)
r.  r/  r0  r1  r   r   r  r   r   r   )r  rM  r5  excs       r   rF  rF    s    E" &./C ))	
 
NNVV%%
 L% 	" W ))	

 s   A; ;B7
"B2,B72B7rA  c                     	 t        |       }||k7  s|t        d      k(  s|t        d      k(  r| S |t        |      k(  rt        |      S |r| S |S # t        t        f$ r | cY S w xY w)zFTry to parse *value* as a number.  Returns original string on failure.infz-inf)floatr0  OverflowErrorr   )r  rA  fs      r   rD  rD    sp    %L 	AveEl"a5=&8CF{1vH & s   A A#"A#c                 Z    | j                         j                         }|dk(  ry|dk(  ry| S )zGTry to parse *value* as a boolean.  Returns original string on failure.trueTfalseF)r   rC  )r  lows     r   rE  rE    s/    
++-


C
f}
g~Lr   r<   c                     	 t        | t              rt        j                  |       n| }t        |t              r.|j                  d      rddt        |j                  d            fS y# t        $ r Y yw xY w)Nerror
tool_error)okNN)r   r  r.  r/  r   r   r%   )r<   parsed_results     r   _tool_result_observer_fieldsr^    sp    .8.E

6*6mT*}/@/@/IL#m.?.?.H*III   s   A$A( (	A43A4)
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`  ra  rb  rc  rd  re  rf  rg  rh  c                    	 ddl m}m}  |d      sy|	t        |      \  }	}
} |d| |||xs d|xs d|xs d|xs d|xs d||	|
|t	        |xs g              y# t
        $ r }t        j                  d|       Y d}~yd}~ww xY w)u  Emit the ``post_tool_call`` observer hook.

    No-ops cheaply when no plugin has registered for ``post_tool_call`` —
    the ``has_hook`` gate skips both the result-field derivation and the
    payload dispatch so the no-listener path costs one dict lookup.  When
    ``status`` is not supplied, the ok/error fields are derived from the
    result *after* the gate (parsing the result is only worth it when a
    listener will actually consume it).
    r   has_hookinvoke_hookpost_tool_callNr   )r  r  r<   r_  r`  ra  rb  rc  rd  re  rf  rg  rh  zpost_tool_call hook error: %s)hermes_cli.pluginsrm  rn  r^  r   r%   r   r   )ri  rj  r<   r_  r`  ra  rb  rc  rd  re  rf  rg  rh  rm  rn  	_hook_errs                   r   _emit_post_tool_call_hookrr    s    2A<()>0LV0T-FJ#Mr!'R%+Mr)/R#!'!"2"8b9	
   A4i@@As   A AA 	B%B  B	user_taskenabled_toolsskip_pre_tool_call_hookskip_tool_request_middlewaretool_request_middleware_tracec                    1 t         |      }t        |t              si }t        |xs g       }d}	 ddlm} ||j                         r	 t        ||dd      xs g } |j                  k(  r|j                  |xs i |      S  |j                  k(  r|j                  |xs i |      S  |j                  k(  r|j                  |xs i       \  }}}|s|st        j                   d|xs did	
      S |j#                  |      }||vrt        j                   dd| did	
      S t%        |||||	|
t        |      ||      S t        |      }|
sN	 ddlm}  | |xs dxs d|xs d|xs d|xs d      }|j*                  }|j,                  }|j.                  }	  t4        v rt        j                   d  di      S |	ssd}	 ddlm}  | |xs dxs d|xs d|xs d|xs dt        |            }|;t        j                   d|id	
      }t;         |||||dd|t        |             |S 	 ddlm}  | |      }||S 	  t@        vr	 ddl!m"}   | xs d       tG        jH                         }!d}"	 ddl%m&}#m'}$  |$|xs d|xs d       }"	  d!k(  r-||ntP        1d"tR        tT        tV        f   d#tV        f 1fd$}%n"d"tR        tT        tV        f   d#tV        f fd%}%dd&lm,}&  |& ||%|xs dxs d|xs d|xs d|xs d'	      }|"|#		  |#|"       t[        tG        jH                         |!z
  d(z        }'t;         ||||||'t        |      )
       	 dd*lm.}(m/})  |(d+      rTta        |      \  }*}+}, |)d+ ||xs dxs d|xs d|xs d|xs d|'|*|+|,,      }-|-D ]  }.t        |.tT              s|.} |S  |S # t        $ r d}Y w xY w# t        $ r g }Y w xY w# t        $ r!}t0        j3                  d|       Y d}~wd}~ww xY w# t        $ r!}t0        j3                  d|       Y d}~Id}~ww xY w# t        $ rC}t0        j3                  d|        dv rt        j                   ddid	
      cY d}~S Y d}~Fd}~ww xY w# t        $ r Y ?w xY w# t        $ r d}#Y  w xY w# t        $ r Y w xY w# |"|#	  |#|"       w # t        $ r Y w w xY ww w xY w# t        $ r!}t0        j3                  d-|       Y d}~|S d}~ww xY w# t        $ rR}/d.  d/tU        |/       }0t0        jc                  |0       t        j                   dte        |0      id	
      cY d}/~/S d}/~/ww xY w)0a  
    Main function call dispatcher that routes calls to the tool registry.

    Args:
        function_name: Name of the function to call.
        function_args: Arguments for the function.
        task_id: Unique identifier for terminal/browser session isolation.
        user_task: The user's original task (for browser_snapshot context).
        enabled_tools: Tool names enabled for this session.  When provided,
                       execute_code uses this list to determine which sandbox
                       tools to generate.  Falls back to the process-global
                       ``_last_resolved_tool_names`` for backward compat.
        enabled_toolsets: The session's enabled toolsets.  Used to scope the
                       Tool Search bridge catalog so ``tool_search`` /
                       ``tool_describe`` / ``tool_call`` only see and invoke
                       tools the session was actually granted.  ``None`` means
                       "no restriction" (the caller scopes to every toolset),
                       matching ``get_tool_definitions`` semantics.
        disabled_toolsets: The session's disabled toolsets, applied as a
                       subtraction when scoping the bridge catalog.

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