
    jw                        U d Z ddlZddlZddlZddlZddlZddlZddlZddlm	Z	m
Z
mZmZmZ ddlmZmZ ddlmZmZ  ej(                  e      Zda ej0                         Z ej4                         Zd Zd Zd Z e        	 dd	lm Z   e          ejH                         Z%e	e&e&f   e'd<    ejP                         Z)e	e&e*f   e'd<   g a+ee&   e'd<   ddgdgdgdgdgg dg ddgg ddgd
Z,i Z-e	e.ee	e&e
f      f   e'd<   dZ/dWdZ0	 	 	 	 dXdeee&      deee&      d e1d!e1dee	e&e
f      f
d"Z2	 	 	 	 dXdeee&      deee&      d e1d!e1dee	e&e
f      f
d#Z3de4fd$Z5h d%Z6d&d'hZ7 ejp                  d(ejr                        Z: ejp                  d)ejv                        Z< ejp                  d*ejv                        Z= ejp                  d+ej|                        Z?d,Z@d-e&de&fd.ZAd/e&d0e	e&e
f   de	e&e
f   fd1ZBdYd2e&d3e*dz  fd4ZCd3e*dz  de1fd5ZDd2e&d6eEfd7ZFdZd2e&d8e1fd9ZGd2e&fd:ZHd;e
de.e&ee&   ee&   f   fd<ZIddddddddddd=
d>e&d?e	e&e
f   d;e
d@ee&   dAee&   dBee&   dCee&   dDee&   dEe4dFee&   dGee&   dHee&   dIeee	e&e
f         ddfdJZJ	 	 	 	 	 	 	 	 	 	 	 	 d[d>e&d?e	e&e
f   d@ee&   dBee&   dAee&   dCee&   dDee&   dKee&   dLeee&      dMe1dNe1dOeee	e&e
f         deee&      deee&      de&fdPZKdee&   fdQZLd/e&dee&   fdRZMde	e&e*f   fdSZNde	e&e1f   fdTZOdZdUe1deee&   ee*   f   fdVZPy# e!$ rZ"ejG                  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_toolsetc                      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     /home/cube/projects/richard/traning coach/.omo/evidence/nutricoach-v150-combined/st_01a0560c-r63-installed-wheel-first-claim-qa/venv/lib/python3.12/site-packages/model_tools.py_get_tool_loopr   /   s3     
!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   >   sK     '6D|t~~'%%'t$$(!Kr   c                 :   	 	 t        j                         }|r|j                         rsddl}d	t        j                          	fd}|j                  j                  d      }|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_workerx   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_workersi,  )timeoutF)waitg      ?)r   get_running_loopRuntimeError
is_runningconcurrent.futures	threadingEventfuturesThreadPoolExecutorsubmitresultshutdownTimeoutErrorr/   r!   call_soon_threadsafer"   current_threadmain_threadr   r    r   )
r(   r   
concurrentr+   poolfuturer'   	tool_loopr)   r*   s
   `       @@r   
_run_asyncrC   T   s   ('') ! 	";?__&
	$, !!444C^,	&===-  MMuM% !)>)>)@@&(--d33 I''--G  P !!.. 
	 s+0G$..{;#88B <
  $  
	 MMuM%sM   C< :D <D
	D
-F;5E21F2	E>;F=E>>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mixture_of_agentsimage_generate)skills_list
skill_viewskill_manage)
browser_navigatebrowser_snapshotbrowser_clickbrowser_typebrowser_scrollbrowser_backbrowser_pressbrowser_get_imagesbrowser_visionbrowser_consolecronjob)	read_file
write_filepatchsearch_filestext_to_speech)
	web_toolsterminal_toolsvision_tools	moa_toolsimage_toolsskills_toolsbrowser_toolscronjob_tools
file_tools	tts_tools_tool_defs_cache   returnc                  ,    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)rk   clearr   r   r   _clear_tool_defs_cacherp   	  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)rt   )hermes_cli.configrv   statst_mtime_nsst_sizeFileNotFoundErrorOSErrorImportError	frozensetr	   _generationboolosenvirongetrk   rG   list_compute_tool_definitionslen_TOOL_DEFS_CACHE_MAXpopnextiter)rq   rr   rs   rt   rv   cfg_pathcfg_statcfg_fp	cache_keycachedr'   r9   s               r   get_tool_definitionsr     sP   @ 	9&(H}}H**H,<,<=F ,<+GI&'T,=I'(4   456*+
	 "%%i0 IO(O1: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        |
              |r|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	|         t        j                   ||      }|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} t1        |||         } |       }|E|D cg c](  }|j	                  di       j	                  d      |k7  r|* }}|j5                  |       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      }|j7                  dd      }di |d   d|id||<    n |sL|r?|D cg c]
  }|d   d    }}t        dt9        |       d dj                  |              nt        d!       |D cg c]
  }|d   d    c}a	 d
d"lm}  ||      }	 d
d$l"m#}m$}   |        }!|sq|!jJ                  d%k7  rbtM               }" |||"|!&      }#|#jN                  r5|s3t        d'|#jP                   d(|#jR                   d)|#jT                   d*       |#jV                  }|S c c}w # t2        $ r d}Y w xY wc c}w c c}w c c}w # t2        $ r }t@        jC                  d#|       Y d}~d}~ww xY w# t2        $ r!}t@        jC                  d+|       Y d}~|S d}~ww xY w),z8Uncached implementation of :func:`get_tool_definitions`.Nrw   kanbanu   ✅ Enabled toolset 'z': z, zno toolsu   ✅ Enabled legacy toolset 'u   ⚠️  Unknown toolset: r   )get_all_toolsetsu   🚫 Disabled toolset 'u   🚫 Disabled legacy toolset 'quietrx   ry   execute_code)SANDBOX_ALLOWED_TOOLSbuild_execute_code_schema_get_execution_mode)mode)typerx   get_dynamic_schema_coreget_dynamic_schema_admin)discorddiscord_admin)discord_toolrQ   rH   rI   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),r   r   r   r   r   appendr   r
   updateprintjoin_LEGACY_TOOLSET_MAPtoolsetsr   difference_updater	   get_definitionstools.code_execution_toolr   r   r   	enumeratetoolsr   r   r$   discardreplacer   rG   tools.schema_sanitizerr   loggerwarningtools.tool_searchr   r   enabled_resolve_active_context_length	activateddeferred_countdeferred_tokensthreshold_tokens	tool_defs)$rq   rr   rs   rt   tools_to_includeeffective_enabled_toolsetstoolset_nameresolvedlegacy_toolsr   ts_name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   ^  s     E#%)*:%;"::>>./HD^4^ '--h76L-*<8 ''1!1,sZb499XCVhrBstu!442<@ ''5!8c$))T`JaIbcd1,@A 7 	.')G##OG$<= * -L-*<8 228<!3L>\dTYYxEXjtDuvw!442<@ 22<@!:<.DIIVbLcKdef1,@A .* --.>jQN <JJ>aAjM&1>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 ~  " %,,->?&~6EArvvj"-11&9=NN5?W,Uq) 7 10 11+];>RR"">2266*b)--f59KKj>--mR@D<<pD
 !+$Kr*~$K}d$K)N1%  3 9GHA!J-/JH3C4G3H	RVR[R[\fRgQhijPQ AO O1: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[   0R4R-R' R,*R1=R6 BS" R$#R$6	S?SS"	T+TT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} 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_lengthz+Could not resolve active context length: %sN)rz   r   
isinstancer   dictstripagent.model_metadatar   intr$   r   debug)_loadcfg	model_cfgmodel_idr   r   s         r   r   r     s    :gm(237773CT(JCGGG$PR	)T*IMM'*LimmI.FL"SSUA+H5:;; BAFs   BB+ B+ +	C4CC>   todomemorydelegate_tasksession_searchr\   r_   zR</?(?:tool_call|function_call|result|response|output|input|system|assistant|user)>z%^\s*```(?:json|xml|html|markdown)?\s*z
\s*```\s*$z<!\[CDATA\[.*?\]\]>i  	error_msgc                    | 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   W  s    
 '++B	:I)--b)<I*..r9=I$((Y7I
9~++62Q67%?	9+&&r   	tool_nameargsc                 J   |rt        |t              s|S t        j                  |       }|s|S |j	                  d      xs i j	                  d      }|s|S t        |j                               D ]0  \  }}|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              s	|st#        |      st        |||      }||us,|||<   3 |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)r   r   r	   
get_schemar   r   itemstuplestr_coerce_valuer   
startswithr   r   infor   __name___schema_allows_null)	r   r   r   r   keyvalueprop_schemaexpectedcoerceds	            r   coerce_tool_argsr  k  s   $ z$-  +F**\*0b55lCJ4::<(
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 %% 3K @xD%DIe )h Kr   r  r   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   object)
r
  r   lowerr   r   r  _coerce_number_coerce_boolean_coerce_jsonr   )r  expected_typer   r'   r9   s        r   r  r    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)anyOfoneOf)r   r   r   r   )r   schema_type	union_keyvariantsvariants        r   r
  r
    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)
jsonloads
ValueError	TypeErrorr   r   r	  r   r   r   )r  r%  parsedexcs       r   r  r    s    E" &./C ))	
 
NNVV%%
 L% 	" W ))	

 s   A; ;B7
"B2,B72B7r  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)floatr)  OverflowErrorr   )r  r  fs      r   r  r    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   r  )r  lows     r   r  r  %  s/    
++-


C
f}
g~Lr   r9   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$   )r9   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=  r>  r?  r@  rA  rB  rC  rD  rE  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   r9   r<  r=  r>  r?  r@  rA  rB  rC  rD  rE  zpost_tool_call hook error: %s)hermes_cli.pluginsrJ  rK  r;  r   r$   r   r   )rF  rG  r9   r<  r=  r>  r?  r@  rA  rB  rC  rD  rE  rJ  rK  	_hook_errs                   r   _emit_post_tool_call_hookrO  9  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)rq   rr   rs   rt   )current_tool_defsr7  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.)rF  rG  r<  r>  r=  rP  rQ  rR  rS  rT  rq   rr   )apply_tool_request_middlewarer   )r<  r=  r>  r?  r@  z!tool_request middleware error: %sz" must be handled by the agent loop)get_pre_tool_call_block_message)r<  r=  r>  r?  r@  rE  zpre_tool_call hook error: %sblockedplugin_block)rF  rG  r9   r<  r=  r>  r?  r@  rB  rC  rD  rE  )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)r?  r>  r   	next_argsrm   c                 8    t        j                  |       S )N)r<  r=  rQ  r	   dispatch)rb  rF  r   r=  r<  s    r   	_dispatchz'handle_function_call.<locals>._dispatchZ  s$    #,,%y '#-&5	 r   c                 8    t        j                  |       S )N)r<  r=  rP  rd  )rb  rF  r=  r<  rP  s    r   rf  z'handle_function_call.<locals>._dispatchb  s$    #,,%y '#-"+	 r   )run_tool_execution_middleware)original_argsr<  r=  r>  r?  r@  i  )
rF  rG  r9   r<  r=  r>  r?  r@  rA  rE  rI  transform_tool_result)r   r   r9   r<  r=  r>  r?  r@  rA  rB  rC  rD  z$transform_tool_result hook error: %szError executing z: )3r  r   r   r   r   rV  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.middlewarerZ  payloadoriginal_payloadtracer   r   _AGENT_LOOP_TOOLSrM  r[  rO  acp_adapter.edit_approvalr^  _READ_SEARCH_TOOLStools.file_toolsr_  time	monotonictools.approvalr`  ra  rG   r   r  r   rh  r   rJ  rK  r;  	exceptionr   )2rF  rG  r<  r>  r=  r?  r@  rP  rQ  rR  rS  rT  rq   rr   _tool_middleware_trace_ts_modcurrent_defsunderlying_nameunderlying_argserr_scoped_deferrable_tool_original_argsrZ  _tool_request_mw_mw_errblock_messager[  rN  r9   r^  edit_block_message_edit_approval_errr_  _dispatch_start_approval_tokensr`  ra  rf  rh  rA  rJ  rK  rB  rC  rD  hook_resultshook_resultr   r   r   s2   ` ` `  `                                         @r   rt  rt  l  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%;%;"yZ--::w=/9[(\]^^ '+/MHN ?!!#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 +!)"/  $0K!+s3!,  $0 W  .  	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    s    &&((r   c                 ,    t        j                  |       S )z%Return the toolset a tool belongs to.)r	   get_toolset_for_tool)r   s    r   r  r    s    ((33r   c                  *    t        j                         S )z0Return toolset availability info for UI display.)r	   get_available_toolsetsr   r   r   r  r    s    **,,r   c                  *    t        j                         S )z>Return {toolset: available_bool} for every registered toolset.)r	   check_toolset_requirementsr   r   r   r  r    s    ..00r   r   c                 .    t        j                  |       S )z.Return (available_toolsets, unavailable_info).r   )r	   check_tool_availabilityr   s    r   r  r    s    ++%88r   )rm   N)NNFF)N)F)NNNNNNNFFNNN)Q__doc__r   r'  rer   loggingr4   r}  typingr   r   r   r   r   tools.registryr   r	   r   r
   r   	getLoggerr	  r   r   Lockr   localr   r   r   rC   rM  rD   r$   r   r   get_tool_to_toolset_maprE   r  __annotations__get_toolset_requirementsrF   r   rG   r   rk   r  r   rp   r   r   r   r   r   ry  r{  compile
IGNORECASEr   	MULTILINEr   r   DOTALLr   r   r   r  r  r
  r   r  r  r  r;  rO  rt  r  r  r  r  r  r   r   r   <module>r     s1  * 
  	     3 3 ; 6			8	$ 
 ).."&y( ,Y.@   33 'Gh&F&F&H T#s(^ H(I(I(I(K d39o K (* 49 ) .!l%&%&$%A  [F"# D 79 $ud4S>223 8   -1-1&+	KtCy)KS	*K K  $	K
 
$sCx.K^ -1-1&+	ztCy)zS	*z z  $	z
 
$sCx.zz < J !>2 & %"**YMM  'BJJ'OQSQ]Q]^ 'RZZr||D !rzz"8"))D  'C 'C '(Q Q4S> Qd38n Qh 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FZFZS>FZ c]FZ 3-	FZ
 FZ c]FZ SMFZ }FZ DI&FZ "FZ #'FZ $,Dc3h,@#AFZ tCy)FZ  S	*FZ 	FZZ
)DI )
4C 4HSM 4
-S$Y -
1DdO 1
94 9E$s)T$Z:O4P 9M   3
LL.223s   L- -M2M

M