§
    øžyj’¥  ã                  ó¨  — d Z ddlmZ ddlZddlZddlZddlZddlmZm	Z	 ddl
mZmZmZmZmZmZ ddlmZ  ej        d¦  «        ZdZd	Zd
Z eeeeh¦  «        ZdZ ed¬¦  «         G d„ d¦  «        ¦   «         Zdqd„Zdrd„Zdsd„Zdtd„Zdud„Z dvd"„Z!dwd$„Z"dxd)„Z#dyd*„Z$e G d+„ d,¦  «        ¦   «         Z% ej&        d-¦  «        Z'dzd0„Z(d{d3„Z)d|d5„Z*d}d7„Z+	 d~ddD„Z,d€d�dI„Z- ej&        dJ¦  «        Z.d‚dƒdN„Z/d„dP„Z0dQdRœd…dV„Z1dQdRœd†dX„Z2	 	 d‡dˆd]„Z3e G d^„ d_¦  «        ¦   «         Z4ddd`œd‰db„Z5dudc„Z6dŠde„Z7d‹df„Z8ddgœdŒdj„Z9d�dk„Z:dŽdl„Z;d�dm„Z<d�do„Z=g dp¢Z>dS )‘uq  Progressive tool disclosure ("tool search") for Hermes Agent.

When enabled, MCP and non-core plugin tools are replaced in the model-visible
tools array by three bridge tools â€” ``tool_search``, ``tool_describe``,
``tool_call`` â€” and surfaced on demand. Core Hermes tools never defer.

Design constraints this module is built around (see ``openclaw-tool-search-report``
for the full rationale):

* Core tools defined in ``toolsets._HERMES_CORE_TOOLS`` are *never* deferred.
  Always-load means always-load. No exceptions.
* Tiered disclosure (July 2026 plan): the moment ANY deferrable (MCP/plugin)
  tools are present, they hide behind the bridge. What scales with catalog
  size is the *listing*, not the activation decision:
    - Tier 0 â€” no MCP/plugin tools: pure passthrough, everything eager.
    - Tier 1 â€” deferred tools whose catalog listing fits the listing budget
      (``min(threshold_pct`` of context â€” default 5% â€” ``, listing_max_tokens)``):
      bridge + skills-style listing (name + short description per tool),
      degrading to a names-only listing when the full form is over budget.
    - Tier 2 â€” per-tool listing over budget even names-only (e.g.
      Cloudflare's flat API surface, ~3,300 tools whose names alone are
      ~32K tokens): bare bridge + a one-line-per-server summary (server
      name + tool count) so the model still knows WHICH domains are
      reachable; individual tools are discoverable only via ``tool_search``.
* The catalog is stateless across turns and tools-array assemblies. It is
  rebuilt from the current tool-defs list every time. This is the lesson
  from OpenClaw's cron regression (openclaw/openclaw#84141): a session-keyed
  catalog that drifts out of sync with the live tool registry produces
  silent tool dropouts.
* Bridge tools route through ``model_tools.handle_function_call`` exactly
  like a direct call, so guardrails, plugin pre/post hooks, approval flows,
  and tool-result truncation all fire identically.
* Display and trajectory unwrap is implemented here so the user (CLI activity
  feed, gateway, saved trajectories) always sees the underlying tool, not
  the bridge.
é    )ÚannotationsN)Ú	dataclassÚfield)ÚAnyÚDictÚIterableÚListÚOptionalÚTuple)Ú
tool_errorztools.tool_searchÚtool_searchÚtool_describeÚ	tool_callg      @T)Úfrozenc                  óp   — e Zd ZU dZded<   ded<   ded<   ded<   d	Zded
<   dZded<   edd„¦   «         ZdS )ÚToolSearchConfigzDResolved, validated tool-search configuration for a single assembly.ÚstrÚenabledÚfloatÚthreshold_pctÚintÚsearch_default_limitÚmax_search_limitÚautoÚlistingé   Úlisting_max_tokensÚrawr   Úreturnú'ToolSearchConfig'c                ó,  — |du r | dddd¬¦  «        S |du r | dddd¬¦  «        S t          |t          ¦  «        s | dddd¬¦  «        S t          |                     d	d¦  «        ¦  «                             ¦   «                              ¦   «         }|d
v rd}n|dv rd}n	|dv r|}nd}t          |                     d¦  «        d¦  «        }t          dt          d|¦  «        ¦  «        }t          dt          dt          |                     d¦  «        d¦  «        ¦  «        ¦  «        }t          dt          |t          |                     d¦  «        d¦  «        ¦  «        ¦  «        }t          |                     dd¦  «        ¦  «                             ¦   «                              ¦   «         }|d
v rd}n|dv rd}n	|dv r|}nd}t          dt          dt          |                     d¦  «        d¦  «        ¦  «        ¦  «        }	 | ||||||	¬¦  «        S )ay  Build a config from a raw dict / bool / None.

        Accepts the legacy bool shape (``tools.tool_search: true``) and the
        dict shape (``tools.tool_search: {enabled: auto, ...}``). Validates
        and clamps every numeric field; unknown values fall back to safe
        defaults rather than raising, so a typo in user config does not
        break the agent.
        Tr   g      @é   é   )r   r   r   r   FÚoffr   )ÚtrueÚ1ÚyesÚon)ÚfalseÚ0Úno)r   r(   r$   r   ç        ç      Y@é   é2   r   r   r   éÈ   i`ê  r   r   )r   r   r   r   r   r   )
Ú
isinstanceÚdictr   ÚgetÚstripÚlowerÚ_safe_floatÚmaxÚminÚ	_safe_int)
Úclsr   Úenabled_rawr   r   r   r   Úlisting_rawr   r   s
             ú7/home/ragecks/.hermes/hermes-agent/tools/tool_search.pyÚfrom_rawzToolSearchConfig.from_rawd   s„  € ð �$ˆ;ˆ;Ø�3˜v°SØ,-ÀðDñ Dô Dð Dà�%ˆ<ˆ<Ø�3˜u°CØ,-ÀðDñ Dô Dð Då˜#�tÑ$Ô$ð 	DØ�3˜v°SØ,-ÀðDñ Dô Dð Dõ ˜#Ÿ'š' )¨VÑ4Ô4Ñ5Ô5×;Ò;Ñ=Ô=×CÒCÑEÔEˆØÐ.Ð.Ð.ØˆGˆGØÐ0Ð0Ð0ØˆGˆGØÐ1Ð1Ð1Ø!ˆGˆGàˆGå# C§G¢G¨OÑ$<Ô$<¸cÑBÔBˆÝ˜C¥ U¨MÑ!:Ô!:Ñ;Ô;ˆå˜q¥# b­)°C·G²GÐ<NÑ4OÔ4OÐQSÑ*TÔ*TÑ"UÔ"UÑVÔVÐÝ" 1¥cÐ*:Ý*3°C·G²GÐ<RÑ4SÔ4SÐUVÑ*WÔ*Wñ'Yô 'Yñ  Zô  ZÐõ ˜#Ÿ'š' )¨VÑ4Ô4Ñ5Ô5×;Ò;Ñ=Ô=×CÒCÑEÔEˆØÐ.Ð.Ð.ØˆGˆGØÐ0Ð0Ð0ØˆGˆGØÐ1Ð1Ð1Ø!ˆGˆGàˆGÝ  ¥c¨%µ¸3¿7º7ÐCWÑ;XÔ;XÐZ^Ñ1_Ô1_Ñ&`Ô&`ÑaÔaÐàˆsØØ'Ø!5Ø-ØØ1ð
ñ 
ô 
ð 	
ó    N)r   r   r   r    )	Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__r   r   Úclassmethodr>   © r?   r=   r   r   K   s�   € € € € € € àNÐNà€L€L�Lð ÐÐÑØÐÐÑØÐÐÑð €GÐÐÐÑð #ÐÐ"Ð"Ð"Ñ"àð6
ð 6
ð 6
ñ „[ð6
ð 6
ð 6
r?   r   Úvaluer   Úfallbackr   r   c                óT   — 	 t          | ¦  «        S # t          t          f$ r |cY S w xY w©N)r   Ú	TypeErrorÚ
ValueError©rG   rH   s     r=   r9   r9   ž   s<   € ðÝ�5‰zŒzÐøÝ•zÐ"ð ð ð Øˆˆˆðøøøó   ‚ ‘'¦'r   c                óT   — 	 t          | ¦  «        S # t          t          f$ r |cY S w xY wrJ   )r   rK   rL   rM   s     r=   r6   r6   ¥   s<   € ðÝ�U‰|Œ|ÐøÝ•zÐ"ð ð ð ØˆˆˆðøøørN   c                 óÆ  — 	 ddl m}   | ¦   «         pi }t          |                     d¦  «        t          ¦  «        r|                     d¦  «        ni }t          |t          ¦  «        si }t
                               |                     d¦  «        ¦  «        S # t          $ r?}t           	                    d|¦  «         t
                               d¦  «        cY d}~S d}~ww xY w)z2Load tool-search config from the user config file.r   )Úload_configÚtoolsr   z%Failed to load tool-search config: %sN)
Úhermes_cli.configrQ   r1   r3   r2   r   r>   Ú	ExceptionÚloggerÚdebug)Ú_loadÚcfgÚ	tools_cfgÚes       r=   rQ   rQ   ¬   sè   € ð	/Ø:Ð:Ð:Ð:Ð:Ð:Øˆe‰gŒgˆm˜ˆÝ(2°3·7²7¸7Ñ3CÔ3CÅTÑ(JÔ(JÐR�C—G’G˜GÑ$Ô$Ð$ÐPRˆ	Ý˜)¥TÑ*Ô*ð 	ØˆIÝ×(Ò(¨¯ª°}Ñ)EÔ)EÑFÔFÐFøÝð /ð /ð /Ý�ŠÐ<¸aÑ@Ô@Ð@Ý×(Ò(¨Ñ.Ô.Ð.Ð.Ð.Ð.Ð.Ð.øøøøð/øøøs   ‚BB Â
C Â!4CÃC ÃC úfrozenset[str]c                 ój   — 	 ddl m}  t          | ¦  «        S # t          $ r t          ¦   «         cY S w xY w)z¬Return the set of tool names that must NEVER be deferred.

    Imported lazily because ``toolsets`` imports from ``tools.registry``
    and we don't want a hard cycle.
    r   ©Ú_HERMES_CORE_TOOLS)Útoolsetsr^   Ú	frozensetrT   r]   s    r=   Ú_core_tool_namesra   ¿   sS   € ðØ/Ð/Ð/Ð/Ð/Ð/ÝÐ+Ñ,Ô,Ð,øÝð ð ð Ý‰{Œ{ÐÐÐðøøøs   ‚ —2±2Únamer   Úboolc                óÚ   — | t           v rdS | t          ¦   «         v rdS 	 ddlm} |                     | ¦  «        }|€dS |j                             d¦  «        rdS dS # t          $ r Y dS w xY w)aF  Return True if a tool with this name is *eligible* for deferral.

    A tool is deferrable iff it is registered with an MCP toolset prefix
    OR it is not in ``_HERMES_CORE_TOOLS``. Core tools are never deferred
    even when their toolset is technically plugin-provided (this protects
    against accidental shadowing).
    Fr   ©ÚregistryNúmcp-T)ÚBRIDGE_TOOL_NAMESra   Útools.registryrf   Ú	get_entryÚtoolsetÚ
startswithrT   ©rb   rf   Úentrys      r=   Úis_deferrable_tool_namero   Ì   s¤   € ð Õ Ð Ð ØˆuØÕÑ!Ô!Ð!Ð!Øˆuð
Ø+Ð+Ð+Ð+Ð+Ð+Ø×"Ò" 4Ñ(Ô(ˆØˆ=Ø�5ØŒ=×#Ò# FÑ+Ô+ð 	Ø�4àˆtøÝð ð ð Øˆuˆuðøøøs   ŸA ¾A Á
A*Á)A*Ú	tool_defsúList[Dict[str, Any]]ú1Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]c                óþ   — g }g }| D ]s}|                      d¦  «        pi }|                      dd¦  «        }|t          v rŒ9t          |¦  «        r|                     |¦  «         Œ^|                     |¦  «         Œt||fS )zöSplit a tool-defs list into (visible, deferrable).

    ``visible`` retains every tool that must stay in the model-facing array:
    every core tool, plus any tool we can't classify. ``deferrable`` is the
    candidate set for catalog entry.
    Úfunctionrb   Ú )r3   rh   ro   Úappend)rp   ÚvisibleÚ
deferrableÚtdÚfnrb   s         r=   Úclassify_toolsr{   æ   sž   € ð %'€GØ')€JØð 
ð 
ˆØ�VŠV�JÑÔÐ% 2ˆØ�vŠv�f˜bÑ!Ô!ˆØÕ$Ð$Ð$ð Ý" 4Ñ(Ô(ð 	Ø×Ò˜bÑ!Ô!Ð!Ð!à�NŠN˜2ÑÔÐÐØ�JÐÐr?   úIterable[Dict[str, Any]]c           	     ó  — d}| D ]`}	 |t          t          j        |dd¬¦  «        ¦  «        z  }Œ+# t          t          f$ r" |t          t          |¦  «        ¦  «        z  }Y Œ]w xY wt          t          j        |t          z  ¦  «        ¦  «        S )uS  Estimate the token cost of a tool-defs list via the chars/4 rule.

    Cheap and stable across providers. The number doesn't need to be exact â€”
    it gates the activate/skip decision, and a typical 200K context with a
    10% threshold means the decision flips around 20K tokens of schema.
    Order-of-magnitude precision is fine.
    r   F)Ú,ú:)Úensure_asciiÚ
separators)
ÚlenÚjsonÚdumpsrK   rL   r   r   ÚmathÚceilÚCHARS_PER_TOKEN)rp   Útotal_charsry   s      r=   Úestimate_tokens_from_schemasr‰     s™   € ð €KØð (ð (ˆð	(Ø�3�tœz¨"¸5ÈZÐXÑXÔXÑYÔYÑYˆKˆKøÝ�:Ð&ð 	(ð 	(ð 	(Ø�3�s 2™wœw™<œ<Ñ'ˆKˆKˆKð	(øøøå�tŒy˜¥Ñ6Ñ7Ô7Ñ8Ô8Ð8s   ˆ'0°0A#Á"A#ÚconfigÚdeferrable_tokensÚcontext_lengthúOptional[int]c                ó0   — | j         dk    rdS |dk    rdS dS )uE  Decide whether tool search should activate for the current assembly.

    ``"off"`` skips unconditionally. ``"on"`` and ``"auto"`` activate whenever
    at least one deferrable tool exists (there's no point swapping a no-op).

    Tiered-disclosure semantics (July 2026): the presence of ANY MCP/plugin
    tool activates the bridge â€” schemas always defer. What the threshold now
    controls is the *listing budget* (see :func:`listing_token_budget`), not
    activation. ``context_length`` is retained in the signature for
    backward compatibility with existing callers.
    r$   Fr   T)r   )rŠ   r‹   rŒ   s      r=   Úshould_activater�     s+   € ð  „~˜ÒÐØˆuØ˜AÒÐØˆuØˆ4r?   c                ó’   — |r!|dk    rt          || j        dz  z  ¦  «        }nd}t          dt          | j        |¦  «        ¦  «        S )zóEffective token budget for the embedded catalog listing.

    ``min(listing_max_tokens, threshold_pct% of context)``. Without a known
    context size, the percentage leg falls back to a fixed 10K cutoff
    (5% of a typical 200K window).
    r   r-   i'  )r   r   r7   r8   r   )rŠ   rŒ   Úpct_legs      r=   Úlisting_token_budgetr’   *  sT   € ð ð ˜.¨1Ò,Ð,Ý�n¨Ô(<¸uÑ(DÑEÑFÔFˆˆàˆÝˆq•#�fÔ/°Ñ9Ô9Ñ:Ô:Ð:r?   c                  óh   — e Zd ZU dZded<   ded<   ded<   ded<   ded<    ee¬	¦  «        Zd
ed<   dS )ÚCatalogEntryzEOne deferrable tool, in a form the bridge tools can search and serve.r   rb   ÚdescriptionúDict[str, Any]ÚschemaÚsourceÚsource_name)Údefault_factoryú	List[str]Ú_tokensN)r@   rA   rB   rC   rD   r   Úlistrœ   rF   r?   r=   r”   r”   @  sn   € € € € € € àOÐOà€I€I�IØÐÐÑØÐÐÑØ€K€K�KØÐÐÑð ˜¨tÐ4Ñ4Ô4€GÐ4Ð4Ð4Ñ4Ð4Ð4r?   r”   z[A-Za-z0-9]+Útextr›   c                óR   — | sg S d„ t                                | ¦  «        D ¦   «         S )Nc                ó6   — g | ]}|                      ¦   «         ‘ŒS rF   )r5   )Ú.0Úts     r=   ú
<listcomp>z_tokenize.<locals>.<listcomp>T  s    € Ð7Ð7Ð7˜!ˆA�GŠG‰IŒIÐ7Ð7Ð7r?   )Ú	_TOKEN_REÚfindall)rž   s    r=   Ú	_tokenizer¦   Q  s1   € Øð Øˆ	Ø7Ð7�y×0Ò0°Ñ6Ô6Ð7Ñ7Ô7Ð7r?   ry   r–   c                óê  — |                       d¦  «        pi }|                      dd¦  «        }|                      dd¦  «        pd}|                      d¦  «        pi                       d¦  «        pi }d                     |                     ¦   «         ¦  «        }|                     dd¦  «                             d	d¦  «                             d
d¦  «                             dd¦  «        }|› d|› d|› �S )uX  Build the search-text blob for a deferrable tool.

    Includes the tool name (with underscores broken into words so BM25 can
    match against query terms), the description, and the names of the
    top-level parameters. Schema bodies are deliberately excluded â€”
    indexing them adds noise without improving recall in our measurement.
    rt   rb   ru   r•   Ú
parametersÚ
propertiesÚ Ú_ú.Ú-r   )r3   ÚjoinÚkeysÚreplace)ry   rz   rb   ÚdescÚparamsÚparam_namesÚ
name_wordss          r=   Ú_entry_search_textrµ   W  sè   € ð 
�Š�
Ñ	Ô	Ð	!˜r€BØ�6Š6�&˜"ÑÔ€DØ�6Š6�- Ñ$Ô$Ð*¨€DØ�vŠv�lÑ#Ô#Ð) r×.Ò.¨|Ñ<Ô<ÐBÀ€FØ—(’(˜6Ÿ;š;™=œ=Ñ)Ô)€Kà—’˜c 3Ñ'Ô'×/Ò/°°SÑ9Ô9×AÒAÀ#ÀsÑKÔK×SÒSÐTWÐY\Ñ]Ô]€JØÐ/Ð/˜4Ð/Ð/ +Ð/Ð/Ð/r?   úTuple[str, str]c                ó¼   — 	 ddl m} |                     | ¦  «        }|€dS |j                             d¦  «        r	d|j        fS d|j        fS # t
          $ r Y dS w xY w)z=Return (source_kind, source_name) for a registered tool name.r   re   N)Úotherru   rg   ÚmcpÚplugin)ri   rf   rj   rk   rl   rT   rm   s      r=   Ú_classify_sourcer»   i  sŠ   € ð	Ø+Ð+Ð+Ð+Ð+Ð+Ø×"Ò" 4Ñ(Ô(ˆØˆ=Ø �=ØŒ=×#Ò# FÑ+Ô+ð 	*Ø˜5œ=Ð)Ð)Ø˜%œ-Ð(Ð(øÝð ð ð Øˆ}ˆ}ðøøøs   ‚A ¡"A ÁA Á
AÁAúList[CatalogEntry]c                óP  — g }| D ] }|                      d¦  «        pi }|                      dd¦  «        }|sŒ2|                      dd¦  «        pd}t          |¦  «        \  }}t          |||||t          t	          |¦  «        ¦  «        ¬¦  «        }|                     |¦  «         Œ¡|S )z±Build the deferred-tool catalog from a tool-defs list.

    Caller is expected to pass only the deferrable subset (``classify_tools``
    returns it as the second element).
    rt   rb   ru   r•   )rb   r•   r—   r˜   r™   rœ   )r3   r»   r”   r¦   rµ   rv   )	rp   Úcatalogry   rz   rb   r±   r˜   r™   rn   s	            r=   Úbuild_catalogr¿   w  sÉ   € ð #%€GØð ð ˆØ�VŠV�JÑÔÐ% 2ˆØ�vŠv�f˜bÑ!Ô!ˆØð 	ØØ�vŠv�m RÑ(Ô(Ð.¨BˆÝ.¨tÑ4Ô4Ñˆ�ÝØØØØØ#ÝÕ0°Ñ4Ô4Ñ5Ô5ð
ñ 
ô 
ˆð 	�Š�uÑÔÐÐØ€Nr?   ç      ø?ç      è?Úquery_tokensÚ
doc_tokensÚdoc_lengthsú	List[int]Úavg_dlÚdoc_freqúDict[str, int]Ún_docsÚk1Úbc           
     ó   — |sdS d}t          |¦  «        }	i }
|D ]}|
                     |d¦  «        dz   |
|<   Œ| D ]’}|                     |d¦  «        }|dk    rŒt          j        d||z
  dz   |dz   z  z   ¦  «        }|
                     |d¦  «        }|dk    rŒ_||dz   z  ||d|z
  ||	z  t	          |d¦  «        z  z   z  z   z  }|||z  z  }Œ“|S )u  Standard BM25 score for one query against one document.

    Inlined small implementation rather than adding a dependency. Performance
    is fine â€” the catalog is bounded by N (tools) typically < 500, and we
    score against the in-memory tokens list.
    r,   r   r.   g      à?g      ð?)r‚   r3   r…   Úlogr7   )rÂ   rÃ   rÄ   rÆ   rÇ   rÉ   rÊ   rË   ÚscoreÚdlÚdoc_tfr¢   ÚqÚdfÚidfÚtfÚnorms                    r=   Ú_bm25_scorerÖ   ‘  s  € ð ð ØˆsØ€EÝ	ˆZ‰Œ€Bà€FØð )ð )ˆØ—J’J˜q !Ñ$Ô$ qÑ(ˆˆq‰	ˆ	Øð 	ð 	ˆØ�\Š\˜!˜QÑÔˆØ�Š7ˆ7ØÝŒh�q˜F R™K¨#Ñ-°"°s±(Ñ;Ñ;Ñ<Ô<ˆØ�ZŠZ˜˜1ÑÔˆØ�Š7ˆ7ØØ�R˜!‘V‰}  R¨1¨q©5°1°r±6½CÀÈÑ<LÔ<LÑ3LÑ+LÑ%MÑ MÑNˆØ��t‘ÑˆˆØ€Lr?   r"   r¾   ÚqueryÚlimitc           	     ó°  — | r|dk    rg S t          |¦  «        }|sg S d„ | D ¦   «         }t          |¦  «        t          t          |¦  «        d¦  «        z  }i }| D ]7}t	          |j        ¦  «        }|D ]}	|                     |	d¦  «        dz   ||	<   ŒŒ8t          | ¦  «        }
g }| D ]8}t          ||j        ||||
¦  «        }|dk    r|                     ||f¦  «         Œ9|sK| 	                    ¦   «         }| D ]4}||j
         	                    ¦   «         v r|                     d|f¦  «         Œ5|                     d„ d¬¦  «         d„ |d	|…         D ¦   «         S )
u’  Return the top-``limit`` catalog entries for ``query`` by BM25.

    Falls back to a stable name-substring match when BM25 yields no hits
    above zero. That ensures a query like ``"github"`` against a catalog
    where every tool is named ``github_*`` still returns results â€” BM25
    can underperform when query and document share only one token that
    appears in every document (zero IDF).
    r   c                ó6   — g | ]}t          |j        ¦  «        ‘ŒS rF   )r‚   rœ   )r¡   rZ   s     r=   r£   z"search_catalog.<locals>.<listcomp>À  s    € Ð3Ð3Ð3 a•3�q”y‘>”>Ð3Ð3Ð3r?   r.   gš™™™™™¹?c                ó   — | d         S )Nr   rF   )Úxs    r=   ú<lambda>z search_catalog.<locals>.<lambda>×  s
   € ˜a œd€ r?   T)ÚkeyÚreversec                ó   — g | ]\  }}|‘ŒS rF   rF   )r¡   r«   rZ   s      r=   r£   z"search_catalog.<locals>.<listcomp>Ø  s   € Ð)Ð)Ð)‘$�!�QˆAÐ)Ð)Ð)r?   N)r¦   Úsumr7   r‚   Úsetrœ   r3   rÖ   rv   r5   rb   Úsort)r¾   r×   rØ   rÂ   rÄ   rÆ   rÇ   rZ   Úseenr¢   rÉ   Úscoredrn   ÚsÚqls                  r=   Úsearch_catalogrè   °  s¤  € ð ð �e˜q’j�jØˆ	Ý˜UÑ#Ô#€LØð Øˆ	ð 4Ð3¨7Ð3Ñ3Ô3€KÝ�ÑÔ¥¥C¨Ñ$4Ô$4°aÑ 8Ô 8Ñ8€FØ!€HØð 1ð 1ˆÝ�1”9‰~Œ~ˆØð 	1ð 	1ˆAØ"Ÿ,š, q¨!Ñ,Ô,¨qÑ0ˆH�Q‰KˆKð	1å�‰\Œ\€Fà/1€FØð &ð &ˆÝ˜ e¤m°[À&Ø  &ñ*ô *ˆàˆqŠ5ˆ5Ø�MŠM˜1˜e˜*Ñ%Ô%Ð%øàð ,à�[Š[‰]Œ]ˆØð 	,ð 	,ˆEØ�U”Z×%Ò%Ñ'Ô'Ð'Ð'Ø—’˜s E˜lÑ+Ô+Ð+øà
‡K‚K�N�N¨D€KÑ1Ô1Ð1Ø)Ð)˜&  % œ.Ð)Ñ)Ô)Ð)r?   z[.!?\n]é<   r•   Ú	max_charsc                óÀ  — d                      | pd                     ¦   «         ¦  «        }|sdS t                               |¦  «        }|r?|d|                     ¦   «         ||                     ¦   «                  dk    rdndz   …         }t          |¦  «        |k    r|S |d|…         }d|v r|                     dd¦  «        d         }|                     d¦  «        dz   S )	a  First sentence of a tool description, clipped to ``max_chars``.

    Mirrors the skills-listing convention: one terse line per capability.
    Whitespace is collapsed; a hard clip never cuts mid-word unless the
    first word itself exceeds the budget.
    rª   ru   Nr¬   r.   r   z,;: u   â€¦)r®   ÚsplitÚ_SENTENCE_END_REÚsearchÚstartr‚   ÚrsplitÚrstrip)r•   rê   rž   ÚmÚclippeds        r=   Ú_short_descrô   ã  sâ   € ð �8Š8�[Ð& B×-Ò-Ñ/Ô/Ñ0Ô0€DØð ØˆrÝ×Ò Ñ%Ô%€AØð GØÐE�Q—W’W‘Y”Y t¨A¯GªG©I¬I¤¸#Ò'=Ð'= ! !À1ÑEÐEÔFˆÝ
ˆ4�y„y�IÒÐØˆØ�:�I�:Ô€GØ
ˆg€~€~Ø—.’.  aÑ(Ô(¨Ô+ˆØ�>Š>˜&Ñ!Ô! EÑ)Ð)r?   r™   c                óL   — | pd}|                      d¦  «        r
|dd…         }|S )zLHuman-facing group heading for a toolset, e.g. ``mcp-github`` -> ``github``.r¸   rg   é   N)rl   )r™   Úlabels     r=   Ú_listing_group_labelrø   ø  s4   € àÐ"˜7€EØ×Ò˜ÑÔð Ø�a�b�b”	ˆØ€Lr?   r   ©Ú
max_tokensrx   rú   úOptional[str]c               ó.   — t          | |¬¦  «        \  }}|S )uN  Render a skills-style manifest of the deferred catalog.

    One line per tool â€” ``name: short description`` â€” grouped under a
    heading per source (MCP server / plugin toolset), exactly like the
    bundled-skills listing in the system prompt:

        github tools: (44)
        - create_issue: Open a new issue in a GitHub repository.
        - merge_pull_request: Merge an open pull request.
        ...

    Ordering is deterministic (groups and tools sorted by name) so the
    rendered block is byte-stable across assemblies of the same catalog â€”
    this keeps the request prefix cacheable across turns.

    Token-budget fallbacks (cheap chars/4 estimate, same rule as the
    activation gate):
      1. full listing (names + short descriptions)
      2. names-only listing, still grouped
      3. server-level summary â€” one line per MCP server / plugin toolset
         (name + tool count), so the model always knows WHICH domains are
         reachable through the bridge even when per-tool names don't fit
      4. ``None`` â€” only when the summary itself exceeds the budget
    rù   )Úbuild_catalog_listing_with_form)rx   rú   rž   Ú_forms       r=   Úbuild_catalog_listingrÿ      s   € õ: 2°*ÈÐTÑTÔT�K€Dˆ%Ø€Kr?   úTuple[Optional[str], str]c          
     óX  ‡‡‡‡— | sdS i Š| D ]§}|                      d¦  «        pi }|                      dd¦  «        }|sŒ2t          |¦  «        \  }}t          |dk    r|nd¦  «        }‰                     |g ¦  «                             |t          |                      dd¦  «        ¦  «        f¦  «         Œ¨‰sdS dˆfd„Šdt          › dt          › d�Šd ˆˆˆfd„}d!ˆfd„}	d„ ‰D ¦   «         }
 |	 ||
¦  «        ¦  «        r ||
¦  «        dfS d„ ‰D ¦   «         }
 |	 ||
¦  «        ¦  «        r ||
¦  «        dfS t          ‰ˆfd„¬¦  «        }|D ]Y}d|
|<    |	 ||
¦  «        ¦  «        r>t          d„ |
 
                    ¦   «         D ¦   «         ¦  «        rdnd} ||
¦  «        |fc S ŒZdS )"uÚ  Like :func:`build_catalog_listing` but also reports the form used.

    Returns ``(text, form)`` where ``form`` is ``"full"`` (names + short
    descriptions), ``"names"`` (names-only fallback), ``"mixed"`` (per-server
    degradation: small servers keep per-tool lines, oversized servers
    collapse to a name + tool-count summary line), ``"groups"`` (every
    server summarized), or ``"none"`` (over budget in every form).

    Degradation is PER SERVER, not global: one huge server (Cloudflare's
    3,320 flat tools) must not cost a small co-attached server (Linear's 24)
    its listing. Greedy fit, smallest rendered group first, is deterministic
    for a given catalog â€” byte-stable across assemblies, cache-safe.
    )NÚnonert   rb   ru   r¸   r•   r÷   r   Úmoder   c                ó�  •— t          ‰|          ¦  «        }|dk    r| › dt          |¦  «        › dt          › d�S | › dt          |¦  «        › d�g}|dk    r+|D ]'\  }}|                     |rd|› d	|› �nd|› �¦  «         Œ(n2|                     d
                     d„ |D ¦   «         ¦  «        ¦  «         d                     |¦  «        S )z>Render one server's block. mode: 'full' | 'names' | 'summary'.Úsummaryz (u+    tools â€” names not listed; discover via `z`)z tools (z):Úfullz- z: ú, c              3  ó    K  — | ]	\  }}|V — Œ
d S rJ   rF   )r¡   rb   r«   s      r=   ú	<genexpr>zHbuild_catalog_listing_with_form.<locals>.render_group.<locals>.<genexpr>N  s&   è è € Ð"=Ð"=©G¨D°! 4Ð"=Ð"=Ð"=Ð"=Ð"=Ð"=r?   Ú
)Úsortedr‚   ÚTOOL_SEARCH_NAMErv   r®   )r÷   r  rR   Úlinesrb   r±   Úgroupss         €r=   Úrender_groupz5build_catalog_listing_with_form.<locals>.render_groupC  s  ø€ å�v˜e”}Ñ%Ô%ˆØ�9ÒÐØð :ð :¥ E¡
¤
ð :ð :Ý%5ð:ð :ð :ð ;àÐ1Ð1¥3 u¡:¤:Ð1Ð1Ð1Ð2ˆØ�6Š>ˆ>Ø#ð Kð K‘
��dØ—’°DÐIÐ0 $Ð0Ð0¨$Ð0Ð0Ð0¸kÀ4¸k¸kÑJÔJÐJÐJðKð �LŠL˜ŸšÐ"=Ð"=°uÐ"=Ñ"=Ô"=Ñ=Ô=Ñ>Ô>Ð>Ø�yŠy˜ÑÔÐr?   z)Deferred tool catalog (call schemas via `z`, invoke via `z`):ÚmodesúDict[str, str]c                ól   •‡ — d                      ‰gˆ ˆfd„t          ‰¦  «        D ¦   «         z   ¦  «        S )Nr
  c                ó4   •— g | ]} ‰|‰|         ¦  «        ‘ŒS rF   rF   )r¡   Úlblr  r  s     €€r=   r£   zEbuild_catalog_listing_with_form.<locals>.assemble.<locals>.<listcomp>U  s?   ø€ ð %@ð %@ð %@Ø),ð &2 \°#°u¸S´zÑ%BÔ%Bð %@ð %@ð %@r?   )r®   r  )r  r  Úheaderr  s   `€€€r=   Úassemblez1build_catalog_listing_with_form.<locals>.assembleT  s`   øø€ Ø�yŠy˜&˜ð %@ð %@ð %@ð %@ð %@Ý06°v±´ð%@ñ %@ô %@ñ @ñ Aô Að 	Ar?   rž   rc   c                ó^   •— t          j        t          | ¦  «        t          z  ¦  «        ‰k    S rJ   )r…   r†   r‚   r‡   )rž   rú   s    €r=   Úfitsz-build_catalog_listing_with_form.<locals>.fitsX  s#   ø€ ÝŒy�˜T™œ¥_Ñ4Ñ5Ô5¸ÒCÐCr?   c                ó   — i | ]}|d “ŒS )r  rF   ©r¡   r  s     r=   ú
<dictcomp>z3build_catalog_listing_with_form.<locals>.<dictcomp>\  s   € Ð+Ð+Ð+˜SˆS�&Ð+Ð+Ð+r?   r  c                ó   — i | ]}|d “ŒS )ÚnamesrF   r  s     r=   r  z3build_catalog_listing_with_form.<locals>.<dictcomp>a  s   € Ð,Ð,Ð,˜cˆS�'Ð,Ð,Ð,r?   r  c                ó<   •— t           ‰| d¦  «        ¦  «         | fS )Nr  )r‚   )r  r  s    €r=   rÝ   z1build_catalog_listing_with_form.<locals>.<lambda>i  s$   ø€ ­s°<°<ÀÀWÑ3MÔ3MÑ/NÔ/NÐ.NÐPSÐ-T€ r?   )rÞ   r  c              3  ó"   K  — | ]
}|d k    V — ŒdS )r  NrF   )r¡   rò   s     r=   r	  z2build_catalog_listing_with_form.<locals>.<genexpr>m  s&   è è € Ð"JÐ"J°a 1¨	¢>Ð"JÐ"JÐ"JÐ"JÐ"JÐ"Jr?   r  Úmixed)r÷   r   r  r   r   r   )r  r  r   r   )rž   r   r   rc   )r3   r»   rø   Ú
setdefaultrv   rô   ÚTOOL_DESCRIBE_NAMEÚTOOL_CALL_NAMEr  ÚallÚvalues)rx   rú   ry   rz   rb   r˜   r™   r÷   r  r  r  Úby_sizer  Úformr  r  r  s    `            @@@r=   rý   rý   !  s¡  øøøø€ ð$ ð Øˆ|à/1€FØð \ð \ˆØ�VŠV�JÑÔÐ% 2ˆØ�vŠv�f˜bÑ!Ô!ˆØð 	ØÝ.¨tÑ4Ô4Ñˆ�Ý$°F¸gÒ4EÐ4E [ [È7ÑSÔSˆØ×Ò˜% Ñ$Ô$×+Ò+¨Tµ;¸r¿vºvÀmÐUWÑ?XÔ?XÑ3YÔ3YÐ,ZÑ[Ô[Ð[Ð[àð Øˆ|ð ð  ð  ð  ð  ð  ðIÝ$ðIð IÝ5CðIð Ið I€FðAð Að Að Að Að Að Að AðDð Dð Dð Dð Dð Dð ,Ð+ FÐ+Ñ+Ô+€EØ€tˆHˆH�U‰OŒOÑÔð 'Øˆx˜‰Œ Ð&Ð&ð -Ð, VÐ,Ñ,Ô,€EØ€tˆHˆH�U‰OŒOÑÔð (Øˆx˜‰Œ Ð'Ð'õ �VÐ!TÐ!TÐ!TÐ!TÐUÑUÔU€GØð )ð )ˆØˆˆc‰
Øˆ4��˜‘”Ñ Ô ð 	)Ý"Ð"JÐ"J¸5¿<º<¹>¼>Ð"JÑ"JÔ"JÑJÔJÐW�8�8ÐPWˆDØ�8˜E‘?”? DÐ(Ð(Ð(Ð(ð	)ð
 ˆ<r?   ru   Údeferred_countr   Úlisting_formc           
     ó”  — d| › dt           › dt          › d�}|r|dk    r	|d|z   z  }n4|r2|dt           › dt          › d	�z  }|d
k    r|dt          › d�z  }|d|z   z  }dt          › dt          › d�}dt           › d�}dt          |ddddœdddœdœdgdœdœdœdt           |dddd dœidgdœdœdœdt          |ddd!dœdd"dœd#œdd$gdœdœdœgS )%uk  Build the bridge tool schemas to inject in place of deferred tools.

    The schemas are intentionally short â€” every byte added here is a byte
    the user pays on every turn. Descriptions are tuned to be unambiguous
    about the call sequence the model should follow.

    When ``listing`` is provided (see :func:`build_catalog_listing`), it is
    embedded in the ``tool_search`` description so every deferred capability
    stays *visible* by name â€” the skills-listing pattern â€” closing the
    "model doesn't know what it doesn't know" gap while full parameter
    schemas remain deferred. ``listing_form`` selects the framing: per-tool
    forms ("full"/"names") tell the model it may skip the search when it
    sees the exact name; the server-summary form ("groups") tells it which
    DOMAINS are reachable and that search is mandatory for tool discovery.
    zSearch zu additional tools that are loaded on demand. Returns up to ``limit`` matches with name and description. Follow with `z0` to load a tool's full parameter schema, then `zs` to invoke it. Tools listed at the top of this system prompt are already available and do not need to be searched.r  u  

The servers below are connected and their tools ARE available through this bridge. For any request in these domains, search here FIRST â€” do not claim the capability is unavailable and do not substitute a generic tool (terminal/browser) without searching.

u{   

Every deferred capability is listed below. If a tool name appears here, do NOT claim it is unavailable â€” load it with `z	` (skip `z'` when you already see the exact name).r   uP    For servers marked 'names not listed', the tools exist too â€” find them with `z(` before concluding anything is missing.z

z4Load the full JSON schema for one tool returned by `z`. Required before `z'` if the tool's parameters are unknown.zhInvoke a deferred tool by name with the given arguments. Argument shape matches the tool's schema (see `zM`). Policy, hooks, and approvals run exactly as for any directly-listed tool.rt   ÚobjectÚstringzIKeywords describing the capability you need (e.g. 'create github issue').)Útyper•   Úintegerz/Maximum number of results to return. Default 5.)r×   rØ   r×   )r-  r©   Úrequired©rb   r•   r¨   )r-  rt   rb   z-Exact tool name (as returned by tool_search).zExact tool name to invoke.z,Arguments for the tool, matching its schema.)rb   Ú	argumentsr1  )r"  r#  r  )r(  r   r)  Údesc_searchÚdesc_describeÚ	desc_calls         r=   Úbridge_tool_schemasr5  t  sQ  € ð*	N�.ð 	Nð 	Nå#ð	Nð 	Nõ  ð	Nð 	Nð 	Nð ð ð (�< 8Ò+Ð+Øðð  'ñ	'ñ	
ˆˆð 
ð (Øð+å"ð+ð +å-=ð+ð +ð +ñ	
ˆð ˜7Ò"Ð"Øð2Ý+;ð2ð 2ð 2ñˆKð
 	�v Ñ'Ñ'ˆð	TÕ?Oð 	Tð 	TÝ*ð	Tð 	Tð 	Tð ð
	EÝ+=ð	Eð 	Eð 	Eð ð å(Ø*à$ð %-Ø+vð"ð "ð
 %.Ø+\ð"ð "ð	#ð 	#ð ") 	ðð ðð ð	
ð 	
ð, å*Ø,à$àØ$,Ø+Zð!ð !ð#ð "( ð	ð 	ðð ð	
ð 	
ð$ å&Ø(à$ð %-Ø+Gð!ð !ð
 %-Ø+Yð&ð &ð	#ð 	#ð "(¨Ð 5ðð ðð ð	
ð 	
ðO<ð <r?   c                  ón   — e Zd ZU dZded<   ded<   dZded<   dZded	<   dZded
<   dZded<   dZ	ded<   dS )ÚAssemblyResultz<Outcome of one assembly. Useful for tests and observability.rq   rp   rc   Ú	activatedr   r   r(  Údeferred_tokensÚthreshold_tokensÚtierr  r   r)  N)
r@   rA   rB   rC   rD   r(  r9  r:  r;  r)  rF   r?   r=   r7  r7  ó  s�   € € € € € € àFÐFà#Ð#Ð#Ñ#Ø€O€O�OØ€NÐÐÐÑØ€OÐÐÐÑØÐÐÐÐÑð
 €D€M€M€M�MØ€LÐÐÐÑÐÐr?   r7  )rŒ   rŠ   úOptional[ToolSearchConfig]c               ó´  — |€t          ¦   «         }d„ | D ¦   «         }t          |¦  «        \  }}|st          |d¬¦  «        S t          |¦  «        }t	          |||¦  «        s<t          |dt          |¦  «        |t          |pd|j        dz  z  ¦  «        d¬¦  «        S d}d}t          ||¦  «        }	|j	        d	k    rt          ||	¬
¦  «        \  }}t          t          |¦  «        ||¬¦  «        }
||
z   }|dv rdnd}t                               d|t          |¦  «        t          |¦  «        |||	¦  «         t          |dt          |¦  «        ||	||¬¦  «        S )a#  Return the tool-defs list the model should actually see.

    When tool search is inactive (off, no deferrable tools, or below
    threshold), this is a passthrough. When active, MCP and plugin tools
    are stripped from the visible list and replaced with the three bridge
    tools. Core tools are *never* deferred regardless of config.

    Idempotent: calling with bridge tools already in the input is a no-op
    (they classify as non-core/non-deferrable but their names are reserved,
    so they are filtered out of the deferrable set).
    Nc                ót   — g | ]5}|                      d ¦  «        pi                       d¦  «        t          v¯3|‘Œ6S )rt   rb   )r3   rh   )r¡   ry   s     r=   r£   z&assemble_tool_defs.<locals>.<listcomp>  sO   € ð Tð Tð T�rØ—F’F˜:Ñ&Ô&Ð,¨"×1Ò1°&Ñ9Ô9ÕARÐRÐRð ØRÐRÐRr?   F)rp   r8  r   r-   )rp   r8  r(  r9  r:  r;  r  r$   rù   )r   r)  )r  r  r   r.   é   zutool_search activated (tier %d): %d core/visible tools kept, %d deferred (~%d tokens), listing %s (budget ~%d tokens)T)rp   r8  r(  r9  r:  r;  r)  )rQ   r{   r7  r‰   r�   r‚   r   r   r’   r   rý   r5  rU   Úinfo)rp   rŒ   rŠ   Úincomingrw   rx   r‹   r   r)  Úlisting_budgetÚbridgeÚresultr;  s                r=   Úassemble_tool_defsrE    sÂ  € ð" €~Ý‘”ˆðTð T˜Yð Tñ Tô T€Hõ )¨Ñ2Ô2Ñ€GˆZØð CÝ¨¸EÐBÑBÔBÐBå4°ZÑ@Ô@ÐÝ˜6Ð#4°nÑEÔEð 
ÝØØÝ˜z™?œ?Ø-Ý  .Ð"5°A¸&Ô:NÐQVÑ:VÑ!WÑXÔXØð
ñ 
ô 
ð 	
ð €GØ€LÝ)¨&°.ÑAÔA€NØ„~˜ÒÐÝ ?Ø >ð!3ñ !3ô !3Ñˆ�å ¥ Z¡¤¸'Ø.:ð<ñ <ô <€Fà�vÑ€Fð
 Ð :Ð:Ð:ˆ1ˆ1À€Då
‡K‚Kð	7à�c�'‰lŒl�C 
™OœOÐ->Ø�nñ	ô ð õ ØØÝ˜:‘”Ø)Ø'ØØ!ðñ ô ð r?   c                ó   — | t           v S rJ   )rh   )rb   s    r=   Úis_bridge_toolrG  R  s   € ØÕ$Ð$Ð$r?   rn   c                óL   — | j         | j        | j        | j        pdd d…         dœS )Nru   i�  ©rb   r˜   r™   r•   rI  )rn   s    r=   Ú_format_search_hitrJ  V  s5   € à”
Ø”,ØÔ(àÔ)Ð/¨R°°#°Ô6ðð ð r?   c                ó¨   ‡— i Š| D ]2}t          |j        ¦  «        }‰                     |d¦  «        dz   ‰|<   Œ3ˆfd„t          ‰¦  «        D ¦   «         S )aY  Return a compact, deterministic summary of connected deferred sources.

    Included only when search returns no matches. This gives the model enough
    evidence to retry with a source/action query instead of treating a lexical
    miss as proof that the capability is unavailable, without adding anything
    to the fixed per-turn prompt.
    r   r.   c                ó&   •— g | ]}|‰|         d œ‘ŒS ))rb   Ú
tool_countrF   )r¡   rb   Úcountss     €r=   r£   z-_available_source_summary.<locals>.<listcomp>n  s5   ø€ ð ð ð àð  V¨D¤\Ð2Ð2ðð ð r?   )rø   r™   r3   r  )r¾   rn   r÷   rN  s      @r=   Ú_available_source_summaryrO  `  sz   ø€ ð  €FØð 1ð 1ˆõ % UÔ%6Ñ7Ô7ˆØŸ
š
 5¨!Ñ,Ô,¨qÑ0ˆˆu‰ˆðð ð ð å˜6‘N”Nðñ ô ð r?   )rŠ   ÚargsÚcurrent_tool_defsc          
     ó\  — |€t          ¦   «         }t          |                      d¦  «        pd¦  «                             ¦   «         }|st	          d¦  «        S |                      d¦  «        }|€|j        }n6t          dt          |j        t          ||j        ¦  «        ¦  «        ¦  «        }t          |¦  «        \  }}t          |¦  «        }t          |||¬¦  «        }	|t          |¦  «        d„ |	D ¦   «         d	œ}
|	s|rt          |¦  «        |
d
<   d|
d<   t          j        |
d¬¦  «        S )z?Execute the ``tool_search`` bridge tool. Returns a JSON string.Nr×   ru   zquery is requiredrØ   r.   )rØ   c                ó,   — g | ]}t          |¦  «        ‘ŒS rF   )rJ  )r¡   Úhs     r=   r£   z(dispatch_tool_search.<locals>.<listcomp>‹  s!   € Ð8Ð8Ð8¨aÕ& qÑ)Ô)Ð8Ð8Ð8r?   )r×   Útotal_availableÚmatchesÚavailable_sourceszÛNo lexical match was found, but the sources above are connected and their tools remain available. Retry tool_search with the service name plus a concrete action or object before concluding the capability is unavailable.ÚhintF©r€   )rQ   r   r3   r4   r   r   r7   r8   r   r9   r{   r¿   rè   r‚   rO  rƒ   r„   )rP  rQ  rŠ   r×   Ú	raw_limitrØ   r«   rx   r¾   ÚhitsrD  s              r=   Údispatch_tool_searchr\  t  sB  € ð
 €~Ý‘”ˆÝ�—’˜Ñ!Ô!Ð' RÑ(Ô(×.Ò.Ñ0Ô0€EØð /ÝÐ-Ñ.Ô.Ð.à—’˜Ñ!Ô!€IØÐØÔ+ˆˆå�A•s˜6Ô2µI¸iÈÔIdÑ4eÔ4eÑfÔfÑgÔgˆå"Ð#4Ñ5Ô5�M€A€zÝ˜JÑ'Ô'€GÝ˜' 5°Ð6Ñ6Ô6€DàÝ˜w™<œ<Ø8Ð8°4Ð8Ñ8Ô8ðð €Fð
 ð 
�Gð 
Ý&?ÀÑ&HÔ&HˆÐ"Ñ#ð-ð 	ˆv‰õ Œ:�f¨5Ð1Ñ1Ô1Ð1r?   c          	     ó  — t          |                      d¦  «        pd¦  «                             ¦   «         }|st          d¦  «        S t	          |¦  «        st          d|› d�¦  «        S t          |¦  «        \  }}|D ]v}|                     d¦  «        pi }|                     d¦  «        |k    rDt          j        ||                     dd¦  «        |                     di ¦  «        d	œd
¬¦  «        c S Œwt          d|› d�¦  «        S )zAExecute the ``tool_describe`` bridge tool. Returns a JSON string.rb   ru   zname is requiredú'zˆ' is not a deferrable tool. If you see it in the tools list already, call it directly; otherwise check the spelling against tool_search.rt   r•   r¨   r0  FrY  z<' is not currently available. Re-run tool_search to refresh.)r   r3   r4   r   ro   r{   rƒ   r„   )rP  rQ  rb   r«   rx   ry   rz   s          r=   Údispatch_tool_describer_  ˜  sM  € õ ˆt�xŠx˜ÑÔÐ% 2Ñ&Ô&×,Ò,Ñ.Ô.€DØð .ÝÐ,Ñ-Ô-Ð-Ý" 4Ñ(Ô(ð 
Ýð[�ð [ð [ð [ñ
ô 
ð 	
õ #Ð#4Ñ5Ô5�M€A€zØð #ð #ˆØ�VŠV�JÑÔÐ% 2ˆØ�6Š6�&‰>Œ>˜TÒ!Ð!Ý”:ØØ!Ÿvšv m°RÑ8Ô8Ø Ÿfšf \°2Ñ6Ô6ðð ð "ð	#ñ #ô #ð #ð #ð #ð "õ ØNˆDÐNÐNÐNñô ð r?   c                óè   — t          ¦   «         }| D ]S}|                     d¦  «        pi                      dd¦  «        }|r$t          |¦  «        r|                     |¦  «         ŒTt	          |¦  «        S )ae  Return the set of deferrable tool names present in ``tool_defs``.

    ``tool_defs`` is expected to be the *pre-assembly* tool list for the
    current session's toolset scope (i.e. what
    ``get_tool_definitions(skip_tool_search_assembly=True)`` returns for the
    session's enabled/disabled toolsets). The resulting set is the universe of
    tools the session may legitimately reach through ``tool_call``. Used as a
    scoping gate by both the ``model_tools`` bridge dispatch and the
    ``tool_executor`` unwrap so a restricted-toolset session can never invoke
    an out-of-scope tool via the bridge.
    rt   rb   ru   )râ   r3   ro   Úaddr`   )rp   r  ry   rb   s       r=   Úscoped_deferrable_namesrb  ²  sx   € õ ‘e”e€EØð ð ˆØ—’�zÑ"Ô"Ð( b×-Ò-¨f°bÑ9Ô9ˆØð 	Õ+¨DÑ1Ô1ð 	Ø�IŠI�d‰OŒOˆOøÝ�UÑÔÐr?   c                ó„  ‡— 	 ddl m} |                     | ¦  «        }t          |t          ¦  «        sdS |                     d¦  «        dk    r|                     d¦  «        n|}t          |t          ¦  «        sdS |                     d¦  «        }t          |t          ¦  «        sdS |                     d¦  «        }t          |t          ¦  «        r|sdS ˆfd„|D ¦   «         }|sdS t          d	| › d
d                     |¦  «        › d�|d¬¦  «        S # t          $ r! t                               d| d¬¦  «         Y dS w xY w)u»  Probe-validate ``tool_call`` arguments against the deferred tool's schema.

    A deferred tool's parameter schema is invisible to the model until it
    calls ``tool_describe`` â€” so models routinely invoke deferred tools
    "blind" by name alone, omitting required arguments. Dispatching such a
    call produces an opaque downstream failure (``KeyError: 'document_id'``)
    that tells the model nothing about what the tool expects, and cheap
    models loop on it until the iteration budget dies.

    Port of the describe-first probe-validation fix from nearai/ironclaw#5149:
    when required arguments are missing, return the tool's parameter schema
    instead of dispatching blind â€” the model repairs the call in one
    round-trip. Valid calls (and any call we can't confidently validate)
    dispatch untouched, so this can never block a legitimate invocation.

    Only *key absence* of schema-``required`` fields counts as invalid.
    No type checking, no null rejection â€” nullable/typed edge cases are the
    tool's own business, and ``coerce_tool_args`` already handles type repair
    downstream. Returns a JSON error string when invalid, ``None`` when the
    call should dispatch.
    r   re   Nr-  rt   r¨   r/  c                óF   •— g | ]}t          |t          ¦  «        ¯|‰v¯|‘ŒS rF   )r1   r   )r¡   ÚrrP  s     €r=   r£   z/validate_deferred_call_args.<locals>.<listcomp>ê  s-   ø€ ÐOÐOÐO˜­*°Q½Ñ*<Ô*<ÐOÀÈ$ÀÀ�1ÀÀÀr?   ztool_call to 'z#' is missing required argument(s): r  z. The tool was NOT invoked.zFRetry tool_call with 'arguments' matching the parameters schema above.)r¨   rX  z)validate_deferred_call_args failed for %sT)Úexc_info)ri   rf   Ú
get_schemar1   r2   r3   r�   r   r®   rT   rU   rV   )rb   rP  Ú	_registryr—   rz   r²   r/  Úmissings    `      r=   Úvalidate_deferred_call_argsrj  Æ  s�  ø€ ð,Ø8Ð8Ð8Ð8Ð8Ð8Ø×%Ò% dÑ+Ô+ˆÝ˜&¥$Ñ'Ô'ð 	Ø�4Ø'-§z¢z°&Ñ'9Ô'9¸ZÒ'GÐ'GˆV�ZŠZ˜
Ñ#Ô#Ð#ÈVˆÝ˜"�dÑ#Ô#ð 	Ø�4Ø—’˜Ñ%Ô%ˆÝ˜&¥$Ñ'Ô'ð 	Ø�4Ø—:’:˜jÑ)Ô)ˆÝ˜(¥DÑ)Ô)ð 	°ð 	Ø�4ØOÐOÐOÐO˜hÐOÑOÔOˆØð 	Ø�4Ýð?˜Tð ?ð ?Ø�yŠy˜Ñ!Ô!ð?ð ?ð ?àð ð
ñ 
ô 
ð 	
øõ ð ð ð Ý�ŠÐ@À$ÐQUˆÑVÔVÐVØˆtˆtðøøøs/   ƒ0D µAD Á<*D Â(,D ÃD Ã(+D Ä'D?Ä>D?ú3Tuple[Optional[str], Dict[str, Any], Optional[str]]c                óÞ  — t          |                      d¦  «        pd¦  «                             ¦   «         }|sdi dfS |t          v r	di d|› d�fS |                      d¦  «        }|€i }t	          |t           ¦  «        r:	 t          j        |¦  «        }n$# t
          j        $ r}di d|› �fcY d}~S d}~ww xY wt	          |t          ¦  «        sdi d	fS t          |¦  «        s	di d
|› d�fS ||dfS )a?  Parse a ``tool_call`` invocation into (underlying_name, args, error_msg).

    Used by:
    * the dispatcher in ``model_tools.handle_function_call``,
    * the display layer (so the activity feed shows the underlying tool),
    * the trajectory recorder.

    On parse error, returns ``(None, {}, error_message)``.
    rb   ru   Nz$tool_call requires a 'name' argumentztool_call cannot invoke 'z' (it is itself a bridge tool)r1  z)tool_call 'arguments' is not valid JSON: z'tool_call 'arguments' must be an objectr^  z|' is not a deferrable tool. If it appears in the model-facing tools list already, call it directly instead of via tool_call.)
r   r3   r4   rh   r1   rƒ   ÚloadsÚJSONDecodeErrorr2   ro   )rP  rb   Úraw_argsrZ   s       r=   Úresolve_underlying_callrp  û  sd  € õ ˆt�xŠx˜ÑÔÐ% 2Ñ&Ô&×,Ò,Ñ.Ô.€DØð @Ø�RÐ?Ð?Ð?ØÕ Ð Ð Ø�RÐY°TÐYÐYÐYÐYÐYØ�xŠx˜Ñ$Ô$€HØÐØˆÝ�(�CÑ Ô ð Mð	MÝ”z (Ñ+Ô+ˆHˆHøÝÔ#ð 	Mð 	Mð 	MØ˜ÐLÈÐLÐLÐLÐLÐLÐLÐLÐLÐLøøøøð	Møøøå�h¥Ñ%Ô%ð CØ�RÐBÐBÐBÝ" 4Ñ(Ô(ð 
Ø�RðG�ð Gð Gð Gð
ð 	
ð �˜4ÐÐs   Á?B ÂB5Â#B0Â*B5Â0B5)r  r"  r#  rh   r   r”   r7  rQ   ro   r{   r‰   r�   r¿   rÿ   rý   r’   rè   r5  rE  rG  r\  r_  rp  rb  rj  )rG   r   rH   r   r   r   )rG   r   rH   r   r   r   )r   r   )r   r[   )rb   r   r   rc   )rp   rq   r   rr   )rp   r|   r   r   )rŠ   r   r‹   r   rŒ   r�   r   rc   )rŠ   r   rŒ   r�   r   r   )rž   r   r   r›   )ry   r–   r   r   )rb   r   r   r¶   )rp   rq   r   r¼   )rÀ   rÁ   )rÂ   r›   rÃ   r›   rÄ   rÅ   rÆ   r   rÇ   rÈ   rÉ   r   rÊ   r   rË   r   r   r   )r"   )r¾   r¼   r×   r   rØ   r   r   r¼   )ré   )r•   r   rê   r   r   r   )r™   r   r   r   )rx   rq   rú   r   r   rû   )rx   rq   rú   r   r   r   )Nru   )r(  r   r   rû   r)  r   r   rq   )rp   rq   rŒ   r�   rŠ   r<  r   r7  )rn   r”   r   r–   )r¾   r¼   r   rq   )rP  r–   rQ  rq   rŠ   r<  r   r   )rP  r–   rQ  rq   r   r   )rp   rq   r   r[   )rb   r   rP  r–   r   rû   )rP  r–   r   rk  )?rC   Ú
__future__r   rƒ   Úloggingr…   ÚreÚdataclassesr   r   Útypingr   r   r   r	   r
   r   ri   r   Ú	getLoggerrU   r  r"  r#  r`   rh   r‡   r   r9   r6   rQ   ra   ro   r{   r‰   r�   r’   r”   Úcompiler¤   r¦   rµ   r»   r¿   rÖ   rè   rí   rô   rø   rÿ   rý   r5  r7  rE  rG  rJ  rO  r\  r_  rb  rj  rp  Ú__all__rF   r?   r=   ú<module>ry     s•  ðð#ð #ðJ #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø €€€Ø 	€	€	€	Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (Ø =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =à %Ð %Ð %Ð %Ð %Ð %à	ˆÔ	Ð.Ñ	/Ô	/€ð !Ð Ø$Ð Ø€à�IÐ/Ð1CÀ^ÐTÑUÔUÐ ð €ð €�$ÐÑÔðO
ð O
ð O
ð O
ð O
ñ O
ô O
ñ ÔðO
ðdð ð ð ðð ð ð ð/ð /ð /ð /ð&
ð 
ð 
ð 
ðð ð ð ð4ð ð ð ð89ð 9ð 9ð 9ð"ð ð ð ð.;ð ;ð ;ð ;ð, ð
5ð 
5ð 
5ð 
5ð 
5ñ 
5ô 
5ñ „ð
5ð ˆBŒJ�Ñ'Ô'€	ð8ð 8ð 8ð 8ð0ð 0ð 0ð 0ð$ð ð ð ðð ð ð ð: -1ðð ð ð ð ð>(*ð (*ð (*ð (*ð (*ð` �2”:˜jÑ)Ô)Ð ð*ð *ð *ð *ð *ð*ð ð ð ð ðð ð ð ð ð ðH ðPð Pð Pð Pð Pð Pðj "Øðwð wð wð wð wð~ ðð ð ð ð ñ ô ñ „ðð& %)Ø)-ð	Fð Fð Fð Fð Fð Fð\%ð %ð %ð %ðð ð ð ðð ð ð ð. ?Cð!2ð !2ð !2ð !2ð !2ð !2ðHð ð ð ð4ð ð ð ð(2ð 2ð 2ð 2ðj ð  ð  ð  ðBð ð €€€r?   