§
    øžyjY» ã                  ó   — U 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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 ddlmZ ddlmZmZmZmZmZmZmZmZ ddlmZ ddlmZm Z  dd	l!m"Z" dd
l#m$Z$m%Z% dtd„Z&	 ddl'Z'n# e($ r dZ'Y nw xY w G d„ de)¦  «        Z* e	j+        e,¦  «        Z- e
j.        dd¦  «         /                    ¦   «          0                    ¦   «         dv a1da2dudvd„Z3 e3¦   «          h d£Z4de5d<   dZ6dZ7dwd„Z8dxd!„Z9dyd#„Z:h d$£Z;de5d%<   dzd'„Z<d{d*„Z=e G d+„ d,¦  «        ¦   «         Z>e G d-„ d.¦  «        ¦   «         Z? G d/„ d0¦  «        Z@ G d1„ d2¦  «        ZAdaBd3e5d4<   d|d5„ZCd}d8„ZDdudvd9„ZEd~d>„ZFdd@„ZGd€dA„ZHd�dB„ZI ejJ        ¦   «         ZK edC¬D¦  «         G dE„ dF¦  «        ¦   «         ZL	 d‚dƒdK„ZMd„dL„ZN	 	 	 	 	 	 d…d†dW„ZO	 	 	 	 	 	 d…d‡dY„ZP	 	 	 	 	 	 d…dˆd[„ZQ	 	 	 	 	 	 d…dˆd\„ZRdddddddd]œd‰df„ZSdudŠdg„ZTdh„ ZUd‹dj„ZVdkZWdŒdm„ZXd�do„ZYdŽdq„ZZd�ds„Z[dS )�u“  
Hermes Plugin System
====================

Discovers, loads, and manages plugins from four sources:

1. **Bundled plugins** â€“ ``<repo>/plugins/<name>/`` (shipped with hermes-agent;
   ``memory/`` and ``context_engine/`` subdirs are excluded â€” they have their
   own discovery paths)
2. **User plugins**   â€“ ``~/.hermes/plugins/<name>/``
3. **Project plugins** â€“ ``./.hermes/plugins/<name>/`` (opt-in via
   ``HERMES_ENABLE_PROJECT_PLUGINS``)
4. **Pip plugins**     â€“ packages that expose the ``hermes_agent.plugins``
   entry-point group.

Later sources override earlier ones on name collision, so a user or project
plugin with the same name as a bundled plugin replaces it.

Each directory plugin must contain a ``plugin.yaml`` manifest **and** an
``__init__.py`` with a ``register(ctx)`` function.

Lifecycle hooks
---------------
Plugins may register callbacks for any of the hooks in ``VALID_HOOKS``.
The agent core calls ``invoke_hook(name, **kwargs)`` at the appropriate
points.

Tool registration
-----------------
``PluginContext.register_tool()`` delegates to ``tools.registry.register()``
so plugin-defined tools appear alongside the built-in tools.
é    )ÚannotationsN)Ú	dataclassÚfield)ÚPath)ÚAnyÚCallableÚDictÚListÚMappingÚOptionalÚSetÚUnion)Úget_hermes_home)Úenv_var_enabledÚfast_safe_load)Úcfg_get)ÚOBSERVER_SCHEMA_VERSIONÚVALID_MIDDLEWAREÚreturnr   c                 ó²   — t          j        d¦  «        } | rt          | ¦  «        S t          t          ¦  «                             ¦   «         j        j        dz  S )zóLocate the bundled ``plugins/`` directory.

    Honours ``HERMES_BUNDLED_PLUGINS`` (set by the Nix wrapper / packaged
    installs) so read-only store paths are consulted first.  Falls back to
    the in-repo path used during development.
    ÚHERMES_BUNDLED_PLUGINSÚplugins)ÚosÚgetenvr   Ú__file__ÚresolveÚparent)Úenv_overrides    ú8/home/ragecks/.hermes/hermes-agent/hermes_cli/plugins.pyÚget_bundled_plugins_dirr    8   sM   € õ ”9Ð5Ñ6Ô6€LØð "Ý�LÑ!Ô!Ð!Ý•‰>Œ>×!Ò!Ñ#Ô#Ô*Ô1°IÑ=Ð=ó    c                  ó   — e Zd ZdZdS )ÚPluginToolOverrideErrorz“Raised when a plugin attempts to override a built-in tool without
    operator opt-in via ``plugins.entries.<plugin_id>.allow_tool_override``.
    N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© r!   r   r#   r#   J   s   € € € € € ðð ð ð r!   r#   ÚHERMES_PLUGINS_DEBUGÚ >   Ú1ÚonÚyesÚtrueFÚforceÚboolÚNonec                ó4  — | r;t          j        dd¦  «                             ¦   «                              ¦   «         dv at          rt
          rdS t          j        t          j	        ¦  «        }| 
                    t          j        ¦  «         |                     t          j        d¦  «        ¦  «         t                               |¦  «         t           
                    t          j        ¦  «         dt          _        dat                               d¦  «         dS )zÝWhen HERMES_PLUGINS_DEBUG is on, tee plugin logs to stderr at DEBUG.

    Idempotent: only attaches the handler once per process unless ``force``
    is passed. Does not touch the root logger or other Hermes loggers.
    r)   r*   >   r+   r,   r-   r.   Nz#[plugins] %(levelname)s %(message)sTuC   HERMES_PLUGINS_DEBUG=1 â€” verbose plugin discovery logging enabled)r   r   ÚstripÚlowerÚ_PLUGINS_DEBUGÚ_DEBUG_HANDLER_INSTALLEDÚloggingÚStreamHandlerÚsysÚstderrÚsetLevelÚDEBUGÚsetFormatterÚ	FormatterÚloggerÚ
addHandlerÚ	propagateÚdebug)r/   Úhandlers     r   Ú_install_plugin_debug_handlerrD   g   sû   € ð ð 
ÝœÐ#9¸2Ñ>Ô>×DÒDÑFÔF×LÒLÑNÔNð S
ð 
ˆõ ð Õ5ð ØˆÝÔ#¥C¤JÑ/Ô/€GØ×Ò•W”]Ñ#Ô#Ð#Ø×Ò�Ô*Ð+PÑQÔQÑRÔRÐRÝ
×Ò�gÑÔÐÝ
‡O‚O•G”MÑ"Ô"Ð"ð …FÔØ#ÐÝ
‡L‚LØMñô ð ð ð r!   >   Ú
pre_verifyÚpre_llm_callÚpost_llm_callÚpre_tool_callÚsubagent_stopÚon_session_endÚpost_tool_callÚsubagent_startÚpre_api_requestÚon_session_resetÚon_session_startÚpost_api_requestÚapi_request_errorÚon_skill_lifecycleÚkanban_task_blockedÚkanban_task_claimedÚon_session_finalizeÚpre_approval_requestÚpre_gateway_dispatchÚtransform_llm_outputÚkanban_task_completedÚtransform_tool_resultÚpost_approval_responseÚtransform_terminal_outputzSet[str]ÚVALID_HOOKSzhermes_agent.pluginsÚhermes_pluginsÚnameÚstrc                ó    — t          | ¦  «        S )z<Return True when an env var is set to a truthy opt-in value.)r   )r_   s    r   Ú_env_enabledrb   â   s   € å˜4Ñ Ô Ð r!   Úsetc                 óê   — 	 ddl m}   | ¦   «         }t          |ddg ¬¦  «        }t          |t          ¦  «        rt          |¦  «        nt          ¦   «         S # t          $ r t          ¦   «         cY S w xY w)zÓRead the disabled plugins list from config.yaml.

    Kept for backward compat and explicit deny-list semantics. A plugin
    name in this set will never load, even if it appears in
    ``plugins.enabled``.
    r   ©Úload_configr   Údisabled)Údefault)Úhermes_cli.configrf   r   Ú
isinstanceÚlistrc   Ú	Exception)rf   Úconfigrg   s      r   Ú_get_disabled_pluginsrn   ç   s…   € ðØ1Ð1Ð1Ð1Ð1Ð1Ø�‘”ˆÝ˜6 9¨jÀ"ÐEÑEÔEˆÝ *¨8µTÑ :Ô :ÐE�s�8‰}Œ}ˆ}ÅÁÄÐEøÝð ð ð Ý‰uŒuˆˆˆðøøøs   ‚AA ÁA2Á1A2úOptional[set]c                 ó   — 	 ddl m}   | ¦   «         }|                     d¦  «        }t          |t          ¦  «        sdS d|vrdS |                     d¦  «        }t          |t
          ¦  «        sdS t          |¦  «        S # t          $ r Y dS w xY w)uL  Read the enabled-plugins allow-list from config.yaml.

    Plugins are opt-in by default â€” only plugins whose name appears in
    this set are loaded. Returns:

    * ``None`` â€” the key is missing or malformed. Callers should treat
      this as "nothing enabled yet" (the opt-in default); the first
      ``migrate_config`` run populates the key with a grandfathered set
      of currently-installed user plugins so existing setups don't
      break on upgrade.
    * ``set()`` â€” an empty list was explicitly set; nothing loads.
    * ``set(...)`` â€” the concrete allow-list.
    r   re   r   NÚenabled)ri   rf   Úgetrj   Údictrk   rc   rl   )rf   rm   Úplugins_cfgrq   s       r   Ú_get_enabled_pluginsru   ÷   s²   € ðØ1Ð1Ð1Ð1Ð1Ð1Ø�‘”ˆØ—j’j Ñ+Ô+ˆÝ˜+¥tÑ,Ô,ð 	Ø�4Ø˜KÐ'Ð'Ø�4Ø—/’/ )Ñ,Ô,ˆÝ˜'¥4Ñ(Ô(ð 	Ø�4Ý�7‰|Œ|ÐøÝð ð ð Øˆtˆtðøøøs"   ‚:A? ¾A? Á*A? Á0A? Á?
BÂB>   ÚbackendÚplatformÚ	exclusiveÚ
standaloneúmodel-providerÚ_VALID_PLUGIN_KINDSÚkeyc                ó$  — d                      d„ |                      ¦   «         D ¦   «         ¦  «        }|                     d¦  «        pd}t          j        |                      d¦  «        ¦  «                             ¦   «         dd…         }d|› d	|› �S )
zGReturn a readable, collision-resistant namespace for a portable plugin.r*   c              3  óv   K  — | ]4}|                      ¦   «         r|                     ¦   «         s|d v r|ndV — Œ5dS )z_-ú-N)ÚisasciiÚisalnum)Ú.0Úchs     r   ú	<genexpr>z,_portable_skill_namespace.<locals>.<genexpr>  s\   è è € ð ð àð �jŠj‰lŒlÐD §
¢
¡¤ÐD°°d°
°
ˆˆÀðð ð ð ð ð r!   z-_Úpluginúutf-8Né   zagent-plugin-r   )Újoinr4   r3   ÚhashlibÚsha256ÚencodeÚ	hexdigest)r|   ÚslugÚdigests      r   Ú_portable_skill_namespacer�     s™   € ð �7Š7ð ð à—)’)‘+”+ðñ ô ñ ô €Dð �:Š:�dÑÔÐ'˜x€DÝŒ^˜CŸJšJ wÑ/Ô/Ñ0Ô0×:Ò:Ñ<Ô<¸R¸a¸RÔ@€FØ*˜4Ð*Ð* &Ð*Ð*Ð*r!   ÚvalueÚobjectc                ó–   ‡ — t          ‰ t          ¦  «        r!d                     ˆ fd„dD ¦   «         ¦  «        S ‰ €dnt          ‰ ¦  «        S )zFNormalize a manifest author value for the string PluginManifest field.ú, c              3  ól   •K  — | ].}‰                      |¦  «        ¯t          ‰|         ¦  «        V — Œ/d S ©N)rr   r`   )r‚   r   r�   s     €r   r„   z"_display_author.<locals>.<genexpr>+  sU   øè è € ð 
ð 
àØ�yŠy˜ÑÔð
Ý��e”ÑÔð
ð 
ð 
ð 
ð 
ð 
r!   )r_   ÚemailÚurlNr*   )rj   r   rˆ   r`   )r�   s   `r   Ú_display_authorr˜   (  sg   ø€ å�%�Ñ!Ô!ð 
Ø�yŠyð 
ð 
ð 
ð 
à1ð
ñ 
ô 
ñ 
ô 
ð 	
ð
 �ˆ2ˆ2¥C¨¡J¤JÐ.r!   c                  ó  — e Zd ZU dZded<   dZded<   dZded<   dZded<    ee	¬¦  «        Z
d	ed
<    ee	¬¦  «        Zded<    ee	¬¦  «        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Zded<   dZded<   dS )ÚPluginManifestz0Parsed representation of a plugin.yaml manifest.r`   r_   r*   ÚversionÚdescriptionÚauthor©Údefault_factoryz List[Union[str, Dict[str, Any]]]Úrequires_envú	List[str]Úprovides_toolsÚprovides_hooksÚsourceNúOptional[str]Úpathry   Úkindr|   Fr0   ÚportableÚskill_namespace)r$   r%   r&   r'   Ú__annotations__r›   rœ   r�   r   rk   r    r¢   r£   r¤   r¦   r§   r|   r¨   r©   r(   r!   r   rš   rš   3  s  € € € € € € à:Ð:à€I€I�IØ€GÐÐÐÑØ€KÐÐÐÑØ€FÐÐÐÑØ5:°UÈ4Ð5PÑ5PÔ5P€LÐPÐPÐPÑPØ % °dÐ ;Ñ ;Ô ;€NÐ;Ð;Ð;Ñ;Ø % °dÐ ;Ñ ;Ô ;€NÐ;Ð;Ð;Ñ;Ø€FÐÐÐÑØ€DÐÐÐÑð  €DÐÐÐÑð €C€M€M€M�MØ€HÐÐÐÑØ€OÐÐÐÑÐÐr!   rš   c                  óÞ   — e Zd ZU dZded<   dZded<    ee¬¦  «        Zded	<    ee¬¦  «        Z	ded
<    ee¬¦  «        Z
ded<    ee¬¦  «        Zded<   dZded<   dZded<   dZded<   dS )ÚLoadedPluginz)Runtime state for a single loaded plugin.rš   ÚmanifestNzOptional[types.ModuleType]Úmodulerž   r¡   Útools_registeredÚhooks_registeredÚmiddleware_registeredÚcommands_registeredFr0   rq   r¥   ÚerrorÚdeferred)r$   r%   r&   r'   rª   r®   r   rk   r¯   r°   r±   r²   rq   r³   r´   r(   r!   r   r¬   r¬   Z  sæ   € € € € € € à3Ð3àÐÐÑØ)-€FÐ-Ð-Ð-Ñ-Ø"' %¸Ð"=Ñ"=Ô"=ÐÐ=Ð=Ð=Ñ=Ø"' %¸Ð"=Ñ"=Ô"=ÐÐ=Ð=Ð=Ñ=Ø', u¸TÐ'BÑ'BÔ'BÐÐBÐBÐBÑBØ%* U¸4Ð%@Ñ%@Ô%@ÐÐ@Ð@Ð@Ñ@Ø€GÐÐÐÑØ€EÐÐÐÑð €HÐÐÐÑÐÐr!   r¬   c                  ó:  — e Zd ZdZdQd„ZedRd	„¦   «         ZedRd
„¦   «         ZedSd„¦   «         Z	 	 	 	 	 	 dTdUd „Z	dVd"„Z
dWdXd&„Z	 	 dYdZd*„Z	 	 d[d\d,„Zd]d.„Zd^d/„Zd^d0„Zd^d1„Zd^d2„Zd^d3„Zd^d4„Zd^d5„Zd^d6„Zd^d7„Z	 	 	 d_d`d>„ZdadA„ZddBœdbdG„ZdcdI„ZdddK„Z	 	 dedfdP„ZdS )gÚPluginContextz=Facade given to plugins so they can register tools and hooks.r­   rš   Úmanagerú'PluginManager'c                ó>   — || _         || _        d | _        d | _        d S r•   )r­   Ú_managerÚ_llmÚ_subagent_lifecycle)Úselfr­   r·   s      r   Ú__init__zPluginContext.__init__s  s%   € Ø ˆŒØˆŒàˆŒ	Ø(,ˆÔ Ð Ð r!   r   r   c                ó|   — | j         €/ddlm} | j        j        p| j        j        } ||¬¦  «        | _         | j         S )aÌ  Return the plugin's :class:`agent.plugin_llm.PluginLlm` facade.

        Lets trusted plugins run host-owned chat or structured completions
        against the user's active model and auth without bringing their
        own provider keys. Override capability (model, agent id, auth
        profile) is fail-closed by default and gated through
        ``plugins.entries.<plugin_id>.llm.*`` config keys.

        See :mod:`agent.plugin_llm` for the full surface.Nr   )Ú	PluginLlm)Ú	plugin_id)r»   Úagent.plugin_llmrÀ   r­   r|   r_   )r½   rÀ   rÁ   s      r   ÚllmzPluginContext.llm|  sN   € ð Œ9ÐØ2Ð2Ð2Ð2Ð2Ð2ØœÔ)Ð?¨T¬]Ô-?ˆIØ!˜	¨IÐ6Ñ6Ô6ˆDŒIØŒyÐr!   c                óN   — | j         €ddlm}m}  ||¦  «        | _         | j         S )a(  Return the public, plugin-safe subagent lifecycle service.

        The service only resolves the active host-owned parent agent when a
        child is launched. Plugins receive serializable handles and immutable
        snapshots; they never receive a live agent or a private registry.
        Nr   )ÚSubagentLifecycleServiceÚget_active_subagent_parent)r¼   Úagent.subagent_lifecyclerÅ   rÆ   )r½   rÅ   rÆ   s      r   Úsubagent_lifecyclez PluginContext.subagent_lifecycle�  s[   € ð Ô#Ð+ðð ð ð ð ð ð ð ð (@Ð'?Ø*ñ(ô (ˆDÔ$ð Ô'Ð'r!   r`   c                óF   — 	 ddl m}  |¦   «         S # t          $ r Y dS w xY w)uX  Return the active Hermes profile name (e.g. ``"default"``).

        Derived from ``HERMES_HOME`` via
        :func:`hermes_cli.profiles.get_active_profile_name`, so it works in
        every execution context â€” interactive CLI, gateway, and
        kanban-spawned worker sessions alike â€” without depending on
        ``_cli_ref`` (which is ``None`` outside an interactive CLI run).

        Returns ``"default"`` for the default profile, the profile id when
        running under ``~/.hermes/profiles/<name>``, or ``"custom"`` when
        ``HERMES_HOME`` points somewhere unrecognized.
        r   )Úget_active_profile_namerh   )Úhermes_cli.profilesrÊ   rl   )r½   rÊ   s     r   Úprofile_namezPluginContext.profile_name¡  sJ   € ð	ØCÐCÐCÐCÐCÐCØ*Ð*Ñ,Ô,Ð,øÝð 	ð 	ð 	Ø�9�9ð	øøøs   ‚ ’
 Ÿ NFr*   r_   ÚtoolsetÚschemars   rC   r   Úcheck_fnúCallable | Noner    úlist | NoneÚis_asyncr0   rœ   ÚemojiÚoverrider1   c                óˆ  — |
rP|                       |¦  «        s;| j        j        p| j        j        }t	          d| j        j        ›d|›d|› d�¦  «        ‚ddlm} |                     |||||||||	|
¬¦
  «
         | j        j	         
                    |¦  «         t                               d| j        j        ||
rd	nd
¦  «         dS )uS  Register a tool in the global registry **and** track it as plugin-provided.

        Pass ``override=True`` to replace an existing built-in tool with the
        same name (e.g. swap the default ``browser_navigate`` for a custom
        CDP-backed implementation). Without it, attempting to register a name
        already claimed by a different toolset is rejected.

        ``override=True`` against a built-in tool requires the operator to
        opt in via ``plugins.entries.<plugin_id>.allow_tool_override: true``
        in config.yaml â€” mirrors the trust gate pattern used for
        ``ctx.llm`` provider/model overrides (#23194). Without that gate,
        any enabled plugin could silently replace a privileged built-in
        like ``shell_exec`` or ``write_file`` and exfiltrate everything
        the model invokes through it.
        zPlugin z cannot override built-in tool z. Set plugins.entries.zY.allow_tool_override: true in config.yaml to allow this plugin to replace built-in tools.r   ©Úregistry)
r_   rÍ   rÎ   rC   rÏ   r    rÒ   rœ   rÓ   rÔ   zPlugin %s registered tool: %s%sz (override)r*   N)Ú_tool_override_allowedr­   r|   r_   r#   Útools.registryr×   Úregisterrº   Ú_plugin_tool_namesÚaddr?   rB   )r½   r_   rÍ   rÎ   rC   rÏ   r    rÒ   rœ   rÓ   rÔ   rÁ   r×   s                r   Úregister_toolzPluginContext.register_tool·  s&  € ð8 ð 	˜D×7Ò7¸Ñ=Ô=ð 	ØœÔ)Ð?¨T¬]Ô-?ˆIÝ)ðR˜$œ-Ô,ð Rð RØðRð Rà#,ðRð Rð Rñô ð ð 	,Ð+Ð+Ð+Ð+Ð+à×ÒØØØØØØ%ØØ#ØØð 	ñ 	
ô 	
ð 	
ð 	ŒÔ(×,Ò,¨TÑ2Ô2Ð2Ý�ŠØ-ØŒMÔ °xÐ&G m mÀRñ	
ô 	
ð 	
ð 	
ð 	
r!   Ú	tool_namec                óˆ  — t          | j        dd¦  «        pd}|dk    rdS 	 ddlm}  |¦   «         pi }n# t          $ r Y dS w xY w| j        j        p| j        j        }|                     d¦  «        pi                      d	¦  «        pi }|                     |¦  «        pi }t          |                     d
d¦  «        ¦  «        S )u›  Return True if this plugin is configured to override built-in tools.

        Bundled plugins (shipped with Hermes core) are trusted by default â€”
        an override there is a deliberate maintainer choice, not a third-party
        plugin trying to elevate privilege. For every other source, require
        ``allow_tool_override: true`` under
        ``plugins.entries.<plugin_id>`` in config.yaml.
        r¤   r*   ÚbundledTr   re   Fr   ÚentriesÚallow_tool_override)	Úgetattrr­   ri   rf   rl   r|   r_   rr   r0   )r½   rÞ   r¤   rf   ÚcfgrÁ   rá   Úentrys           r   rØ   z$PluginContext._tool_override_allowedò  së   € õ ˜œ¨°"Ñ5Ô5Ð;¸ˆØ�YÒÐØ�4ð	Ø5Ð5Ð5Ð5Ð5Ð5Ø�+‘-”-Ð% 2ˆCˆCøÝð 	ð 	ð 	ð �5�5ð	øøøð ”MÔ%Ð;¨¬Ô);ˆ	Ø—7’7˜9Ñ%Ô%Ð+¨×0Ò0°Ñ;Ô;ÐA¸rˆØ—’˜IÑ&Ô&Ð,¨"ˆÝ�E—I’IÐ3°UÑ;Ô;Ñ<Ô<Ð<s   ¢5 µ
AÁAÚuserÚcontentÚrolec                ó  — | j         j        }|€t                               d¦  «         dS |dk    r|nd|› d|› �}t	          |dd¦  «        r|j                             |¦  «         n|j                             |¦  «         dS )	a¤  Inject a message into the active conversation.

        If the agent is idle (waiting for user input), this starts a new turn.
        If the agent is running, this interrupts and injects the message.

        This enables plugins (e.g. remote control viewers, messaging bridges)
        to send messages into the conversation from external sources.

        Returns True if the message was queued successfully.
        Nz@inject_message: no CLI reference (not available in gateway mode)Fræ   Ú[z] Ú_agent_runningT)rº   Ú_cli_refr?   Úwarningrã   Ú_interrupt_queueÚputÚ_pending_input)r½   rç   rè   ÚcliÚmsgs        r   Úinject_messagezPluginContext.inject_message  sš   € ð ŒmÔ$ˆØˆ;Ý�NŠNÐ]Ñ^Ô^Ð^Ø�5à š˜ˆgˆgÐ-B°Ð-BÐ-B¸Ð-BÐ-Bˆå�3Ð(¨%Ñ0Ô0ð 	(àÔ ×$Ò$ SÑ)Ô)Ð)Ð)ð Ô×"Ò" 3Ñ'Ô'Ð'Øˆtr!   ÚhelpÚsetup_fnÚ
handler_fnc                ó’   — |||||| j         j        dœ| j        j        |<   t                               d| j         j        |¦  «         dS )a  Register a CLI subcommand (e.g. ``hermes honcho ...``).

        The *setup_fn* receives an argparse subparser and should add any
        arguments/sub-subparsers.  If *handler_fn* is provided it is set
        as the default dispatch function via ``set_defaults(func=...)``.)r_   rô   rœ   rõ   rö   r…   z$Plugin %s registered CLI command: %sN)r­   r_   rº   Ú_cli_commandsr?   rB   )r½   r_   rô   rõ   rö   rœ   s         r   Úregister_cli_commandz"PluginContext.register_cli_command(  sV   € ð ØØ&Ø Ø$Ø”mÔ(ð-
ð -
ˆŒÔ# DÑ)õ 	�ŠÐ;¸T¼]Ô=OÐQUÑVÔVÐVÐVÐVr!   Ú	args_hintc                ó<  — |                      ¦   «                              ¦   «                              d¦  «                             dd¦  «        }|s't                               d| j        j        ¦  «         dS 	 ddlm	}  ||¦  «        �(t                               d| j        j        |¦  «         dS n# t          $ r Y nw xY w||pd	| j        j        |pd
                     ¦   «         dœ| j        j        |<   t                               d| j        j        |¦  «         dS )uš  Register a slash command (e.g. ``/lcm``) available in CLI and gateway sessions.

        The handler signature is ``fn(raw_args: str) -> str | None``.
        It may also be an async callable â€” the gateway dispatch handles both.

        Unlike ``register_cli_command()`` (which creates ``hermes <subcommand>``
        terminal commands), this registers in-session slash commands that users
        invoke during a conversation.

        ``args_hint`` is an optional short string (e.g. ``"<file>"`` or
        ``"dias:7 formato:json"``) used by gateway adapters to surface the
        command with an argument field â€” for example Discord's native slash
        command picker. Plugin commands without ``args_hint`` register as
        parameterless in Discord and still accept trailing text when invoked
        as free-form chat.

        Names conflicting with built-in commands are rejected with a warning.
        ú/Ú r   z;Plugin '%s' tried to register a command with an empty name.Nr   )Úresolve_commandz^Plugin '%s' tried to register command '/%s' which conflicts with a built-in command. Skipping.zPlugin commandr*   )rC   rœ   r…   rú   z!Plugin %s registered command: /%s)r4   r3   ÚlstripÚreplacer?   rí   r­   r_   Úhermes_cli.commandsrþ   rl   rº   Ú_plugin_commandsrB   )r½   r_   rC   rœ   rú   Úcleanrþ   s          r   Úregister_commandzPluginContext.register_commandA  sH  € ð2 —
’
‘”×"Ò"Ñ$Ô$×+Ò+¨CÑ0Ô0×8Ò8¸¸cÑBÔBˆØð 	Ý�NŠNØMØ”Ô"ñô ð ð ˆFð
	Ø;Ð;Ð;Ð;Ð;Ð;Øˆ˜uÑ%Ô%Ð1Ý—’ð9à”MÔ&¨ñô ð ð
 �ð 2øõ ð 	ð 	ð 	ØˆDð	øøøð Ø&Ð:Ð*:Ø”mÔ(Ø#˜/ r×0Ò0Ñ2Ô2ð	1
ð 1
ˆŒÔ& uÑ-õ 	�ŠÐ8¸$¼-Ô:LÈeÑTÔTÐTÐTÐTs   Á87B2 Â2
B?Â>B?Úargsc                ó„   — ddl m} d|vr(| j        j        }|rt	          |dd¦  «        nd}|�||d<    |j        ||fi |¤ŽS )uÆ  Dispatch a tool call through the registry, with parent agent context.

        This is the public interface for plugin slash commands that need to call
        tools like ``delegate_task`` without reaching into framework internals.
        The parent agent (if available) is resolved automatically â€” plugins never
        need to access the agent directly.

        Args:
            tool_name: Registry name of the tool (e.g. ``"delegate_task"``).
            args: Tool arguments dict (same as what the model would pass).
            **kwargs: Extra keyword args forwarded to the registry dispatch.

        Returns:
            JSON string from the tool handler (same format as model tool calls).
        r   rÖ   Úparent_agentÚagentN)rÙ   r×   rº   rì   rã   Údispatch)r½   rÞ   r  Úkwargsr×   rñ   r  s          r   Údispatch_toolzPluginContext.dispatch_tooly  su   € ð  	,Ð+Ð+Ð+Ð+Ð+ð
  Ð'Ð'Ø”-Ô(ˆCØ36Ð@•G˜C ¨$Ñ/Ô/Ð/¸DˆEØÐ Ø).��~Ñ&à ˆxÔ  ¨DÐ;Ð;°FÐ;Ð;Ð;r!   c                óT  — | j         j        �'t                               d| j        j        ¦  «         dS ddlm} t          ||¦  «        s't                               d| j        j        ¦  «         dS || j         _        t           	                    d| j        j        |j        ¦  «         dS )a%  Register a context engine to replace the built-in ContextCompressor.

        Only one context engine plugin is allowed. If a second plugin tries
        to register one, it is rejected with a warning.

        The engine must be an instance of ``agent.context_engine.ContextEngine``.
        NzyPlugin '%s' tried to register a context engine, but one is already registered. Only one context engine plugin is allowed.r   )ÚContextEnginezbPlugin '%s' tried to register a context engine that does not inherit from ContextEngine. Ignoring.z)Plugin '%s' registered context engine: %s)
rº   Ú_context_enginer?   rí   r­   r_   Úagent.context_enginer  rj   Úinfo)r½   Úenginer  s      r   Úregister_context_enginez%PluginContext.register_context_engine˜  sÁ   € ð Œ=Ô(Ð4Ý�NŠNðQà”Ô"ñô ð ð
 ˆFà6Ð6Ð6Ð6Ð6Ð6Ý˜& -Ñ0Ô0ð 	Ý�NŠNð8à”Ô"ñô ð ð
 ˆFØ(.ˆŒÔ%Ý�ŠØ7ØŒMÔ ¤ñ	
ô 	
ð 	
ð 	
ð 	
r!   c                óø   — ddl m} ddlm} t	          ||¦  «        s't
                               d| j        j        ¦  «         dS  ||¦  «         t
           	                    d| j        j        |j        ¦  «         dS )a=  Register an image generation backend.

        ``provider`` must be an instance of
        :class:`agent.image_gen_provider.ImageGenProvider`. The
        ``provider.name`` attribute is what ``image_gen.provider`` in
        ``config.yaml`` matches against when routing ``image_generate``
        tool calls.
        r   )ÚImageGenProvider©Úregister_providerzjPlugin '%s' tried to register an image_gen provider that does not inherit from ImageGenProvider. Ignoring.Nz-Plugin '%s' registered image_gen provider: %s)
Úagent.image_gen_providerr  Úagent.image_gen_registryr  rj   r?   rí   r­   r_   r  )r½   Úproviderr  r  s       r   Úregister_image_gen_providerz)PluginContext.register_image_gen_provider¸  s¥   € ð 	>Ð=Ð=Ð=Ð=Ð=Ø>Ð>Ð>Ð>Ð>Ð>å˜(Ð$4Ñ5Ô5ð 	Ý�NŠNð?à”Ô"ñô ð ð
 ˆFØÐ˜(Ñ#Ô#Ð#Ý�ŠØ;ØŒMÔ ¤ñ	
ô 	
ð 	
ð 	
ð 	
r!   c           
     óª  — ddl m}m} t          ||¦  «        s't                               d| j        j        ¦  «         dS 	  ||¦  «         nU# t          t          f$ rA}t                               d| j        j        t          |dd¦  «        |¦  «         Y d}~dS d}~ww xY wt                               d| j        j        |j        |j        ¦  «         dS )	u$  Register a dashboard authentication provider.

        ``provider`` must be an instance of
        :class:`hermes_cli.dashboard_auth.DashboardAuthProvider`. Used by
        the dashboard OAuth auth gate, which engages when the dashboard
        binds to a non-loopback host without ``--insecure``.

        Misbehaving providers (wrong type, duplicate name) are logged at
        WARNING and silently ignored â€” never raised â€” so a broken plugin
        cannot crash the host. Same convention as
        ``register_image_gen_provider``.
        r   )ÚDashboardAuthProviderr  zsPlugin '%s' tried to register a dashboard-auth provider that does not inherit from DashboardAuthProvider. Ignoring.Nz=Plugin '%s' failed to register dashboard-auth provider %r: %sr_   ú?z7Plugin '%s' registered dashboard-auth provider: %s (%s))Úhermes_cli.dashboard_authr  r  rj   r?   rí   r­   r_   Ú	TypeErrorÚ
ValueErrorrã   r  Údisplay_name)r½   r  r  r  Úes        r   Ú register_dashboard_auth_providerz.PluginContext.register_dashboard_auth_providerÓ  s  € ð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
õ ˜(Ð$9Ñ:Ô:ð 	Ý�NŠNðNà”Ô"ñô ð ð
 ˆFð	ØÐ˜hÑ'Ô'Ð'Ð'øÝ�:Ð&ð 	ð 	ð 	Ý�NŠNðà”Ô"¥G¨H°f¸cÑ$BÔ$BÀAñô ð ð
 ˆFˆFˆFˆFˆFøøøøð	øøøõ 	�ŠØEØŒMÔ ¤¨xÔ/Dñ	
ô 	
ð 	
ð 	
ð 	
s   ÁA ÁBÁ6BÂBc                óø   — ddl m} ddlm} t	          ||¦  «        s't
                               d| j        j        ¦  «         dS  ||¦  «         t
           	                    d| j        j        |j        ¦  «         dS )a<  Register a video generation backend.

        ``provider`` must be an instance of
        :class:`agent.video_gen_provider.VideoGenProvider`. The
        ``provider.name`` attribute is what ``video_gen.provider`` in
        ``config.yaml`` matches against when routing ``video_generate``
        tool calls.
        r   )ÚVideoGenProviderr  ziPlugin '%s' tried to register a video_gen provider that does not inherit from VideoGenProvider. Ignoring.Nz-Plugin '%s' registered video_gen provider: %s)
Úagent.video_gen_providerr%  Úagent.video_gen_registryr  rj   r?   rí   r­   r_   r  )r½   r  r%  Ú_register_video_providers       r   Úregister_video_gen_providerz)PluginContext.register_video_gen_providerû  s¥   € ð 	>Ð=Ð=Ð=Ð=Ð=ØZÐZÐZÐZÐZÐZå˜(Ð$4Ñ5Ô5ð 	Ý�NŠNð?à”Ô"ñô ð ð
 ˆFØ Ð  Ñ*Ô*Ð*Ý�ŠØ;ØŒMÔ ¤ñ	
ô 	
ð 	
ð 	
ð 	
r!   c                óø   — ddl m} ddlm} t	          ||¦  «        s't
                               d| j        j        ¦  «         dS  ||¦  «         t
           	                    d| j        j        |j        ¦  «         dS )a‚  Register a web search/extract backend.

        ``provider`` must be an instance of
        :class:`agent.web_search_provider.WebSearchProvider`. The
        ``provider.name`` attribute is what ``web.search_backend`` /
        ``web.extract_backend`` / ``web.backend`` in ``config.yaml``
        matches against when routing ``web_search`` / ``web_extract``
        tool calls.
        r   )ÚWebSearchProviderr  zdPlugin '%s' tried to register a web provider that does not inherit from WebSearchProvider. Ignoring.Nz'Plugin '%s' registered web provider: %s)
Úagent.web_search_providerr+  Úagent.web_search_registryr  rj   r?   rí   r­   r_   r  )r½   r  r+  Ú_register_web_providers       r   Úregister_web_search_providerz*PluginContext.register_web_search_provider  s§   € ð 	@Ð?Ð?Ð?Ð?Ð?ØYÐYÐYÐYÐYÐYå˜(Ð$5Ñ6Ô6ð 	Ý�NŠNð@à”Ô"ñô ð ð
 ˆFØÐ˜xÑ(Ô(Ð(Ý�ŠØ5ØŒMÔ ¤ñ	
ô 	
ð 	
ð 	
ð 	
r!   c                óø   — ddl m} ddlm} t	          ||¦  «        s't
                               d| j        j        ¦  «         dS  ||¦  «         t
           	                    d| j        j        |j        ¦  «         dS )uQ  Register a cloud browser backend.

        ``provider`` must be an instance of
        :class:`agent.browser_provider.BrowserProvider`. The
        ``provider.name`` attribute is what ``browser.cloud_provider`` in
        ``config.yaml`` matches against when routing cloud-mode
        ``browser_*`` tool calls.

        Mirrors :meth:`register_web_search_provider` exactly â€” same
        registration shape, same gating, same logging. The browser
        subsystem's dispatcher (:func:`tools.browser_tool._get_cloud_provider`)
        consults the registry built up by these calls.
        r   )ÚBrowserProviderr  zfPlugin '%s' tried to register a browser provider that does not inherit from BrowserProvider. Ignoring.Nz+Plugin '%s' registered browser provider: %s)
Úagent.browser_providerr1  Úagent.browser_registryr  rj   r?   rí   r­   r_   r  )r½   r  r1  Ú_register_browser_providers       r   Úregister_browser_providerz'PluginContext.register_browser_provider2  s¤   € ð 	;Ð:Ð:Ð:Ð:Ð:ØZÐZÐZÐZÐZÐZå˜( OÑ4Ô4ð 	Ý�NŠNð>à”Ô"ñô ð ð
 ˆFØ"Ð" 8Ñ,Ô,Ð,Ý�ŠØ9ØŒMÔ ¤ñ	
ô 	
ð 	
ð 	
ð 	
r!   c                óü   — ddl m} ddlm} t	          ||¦  «        s't
                               d| j        j        ¦  «         dS  ||¦  «        r-t
           	                    d| j        j        |j        ¦  «         dS dS )u  Register an external secret-manager backend.

        ``source`` must be an instance of
        :class:`agent.secret_sources.base.SecretSource`.  Registered
        sources run during ``load_hermes_dotenv()`` startup â€” after
        ``~/.hermes/.env`` loads, before Hermes reads credentials â€” when
        their ``secrets.<source.name>`` config section is enabled.  The
        orchestrator (``agent.secret_sources.registry.apply_all``) owns
        ordering, mapped-vs-bulk precedence, conflict warnings, and
        provenance; the source only fetches.

        NOTE ON TIMING: plugin discovery happens later in startup than
        the first ``load_hermes_dotenv()`` call, so a plugin-registered
        source is not consulted by the initial env load of the process
        that discovers it.  It IS consulted by every subsequently
        spawned Hermes process (gateway children, cron sessions,
        subagents), and immediately after a
        ``reset_secret_source_cache()`` re-pull.  Plugin sources are
        therefore best for supplying credentials to the running fleet;
        the bundled sources cover first-process bootstrap.

        Contract requirements (rejected with a warning otherwise):
        inherit from ``SecretSource``, ``api_version`` matching
        ``SECRET_SOURCE_API_VERSION``, lowercase unique ``name``,
        ``shape`` of ``"mapped"`` or ``"bulk"``, unique ``scheme`` (when
        set), and a ``fetch()`` that never raises and never prompts.
        See the base-module docstring for the full contract.
        r   )ÚSecretSource)Úregister_sourcez`Plugin '%s' tried to register a secret source that does not inherit from SecretSource. Ignoring.Nz(Plugin '%s' registered secret source: %s)
Úagent.secret_sources.baser7  Úagent.secret_sources.registryr8  rj   r?   rí   r­   r_   r  )r½   r¤   r7  r8  s       r   Úregister_secret_sourcez$PluginContext.register_secret_sourceR  s¯   € ð: 	;Ð:Ð:Ð:Ð:Ð:ØAÐAÐAÐAÐAÐAå˜& ,Ñ/Ô/ð 	Ý�NŠNð;à”Ô"ñô ð ð
 ˆFØˆ?˜6Ñ"Ô"ð 	Ý�KŠKØ:Ø”Ô" F¤Kñô ð ð ð ð	ð 	r!   c                óø   — ddl m} ddlm} t	          ||¦  «        s't
                               d| j        j        ¦  «         dS  ||¦  «         t
           	                    d| j        j        |j        ¦  «         dS )u|  Register a text-to-speech backend.

        ``provider`` must be an instance of
        :class:`agent.tts_provider.TTSProvider`. The ``provider.name``
        attribute is what ``tts.provider`` in ``config.yaml`` matches
        against when routing ``text_to_speech`` tool calls â€” **but
        only when**:

        1. ``provider.name`` is NOT a built-in TTS provider name
           (``edge``, ``openai``, ``elevenlabs``, â€¦). Built-ins always
           win â€” the registry rejects shadowing names with a warning.
        2. There is NO ``tts.providers.<name>: type: command`` entry
           with the same name. Command-providers (PR #17843) win on
           name collision because config is more local than plugin
           install.

        Coexists with the command-provider registry rather than
        replacing it â€” see issue #30398 for the full design rationale.
        r   )ÚTTSProviderr  z^Plugin '%s' tried to register a TTS provider that does not inherit from TTSProvider. Ignoring.Nz'Plugin '%s' registered TTS provider: %s)
Úagent.tts_providerr=  Úagent.tts_registryr  rj   r?   rí   r­   r_   r  )r½   r  r=  Ú_register_tts_providers       r   Úregister_tts_providerz#PluginContext.register_tts_provider�  s¤   € ð( 	3Ð2Ð2Ð2Ð2Ð2ØRÐRÐRÐRÐRÐRå˜( KÑ0Ô0ð 	Ý�NŠNð:à”Ô"ñô ð ð
 ˆFØÐ˜xÑ(Ô(Ð(Ý�ŠØ5ØŒMÔ ¤ñ	
ô 	
ð 	
ð 	
ð 	
r!   c                óø   — ddl m} ddlm} t	          ||¦  «        s't
                               d| j        j        ¦  «         dS  ||¦  «         t
           	                    d| j        j        |j        ¦  «         dS )uË  Register a speech-to-text backend.

        ``provider`` must be an instance of
        :class:`agent.transcription_provider.TranscriptionProvider`.
        The ``provider.name`` attribute is what ``stt.provider`` in
        ``config.yaml`` matches against when routing
        :func:`tools.transcription_tools.transcribe_audio` calls â€”
        **but only when**:

        1. ``provider.name`` is NOT a built-in STT provider name
           (``local``, ``local_command``, ``groq``, ``openai``,
           ``mistral``, ``xai``). Built-ins always win â€” the registry
           rejects shadowing names with a warning.
        2. There is NO ``stt.providers.<name>: type: command`` entry
           with the same name. Command-providers win on name
           collision because config is more local than plugin install
           â€” same precedence rule as TTS.

        Coexists with the in-tree dispatcher and the STT
        command-provider registry rather than replacing them. The 6
        built-in STT backends keep their native implementations in
        ``tools/transcription_tools.py``; this hook is for *new* Python
        engines (OpenRouter, SenseAudio, Gemini-STT, custom proprietary
        backends).
        r   )ÚTranscriptionProviderr  zrPlugin '%s' tried to register a transcription provider that does not inherit from TranscriptionProvider. Ignoring.Nz1Plugin '%s' registered transcription provider: %s)
Úagent.transcription_providerrC  Úagent.transcription_registryr  rj   r?   rí   r­   r_   r  )r½   r  rC  Ú_register_stt_providers       r   Úregister_transcription_providerz-PluginContext.register_transcription_provider§  s§   € ð4 	GÐFÐFÐFÐFÐFØ\Ð\Ð\Ð\Ð\Ð\å˜(Ð$9Ñ:Ô:ð 	Ý�NŠNðIà”Ô"ñô ð ð
 ˆFØÐ˜xÑ(Ô(Ð(Ý�ŠØ?ØŒMÔ ¤ñ	
ô 	
ð 	
ð 	
ð 	
r!   ÚlabelÚadapter_factoryÚvalidate_configÚrequired_envÚinstall_hintÚentry_kwargsc                ó0  — ddl m}	m}
 |                     d| j        j        ¦  «          |
d||||||pg |ddœ|¤Ž}|	                     |¦  «         | j        j         	                    |¦  «         t                               d| j        j        |¦  «         dS )	uª  Register a gateway platform adapter.

        The adapter_factory receives a ``PlatformConfig`` and returns a
        ``BasePlatformAdapter`` subclass instance.

        ``check_fn`` is a PASSIVE dependency probe â€” "are deps importable
        right now?".  It must never install anything: status displays and
        config loading call it freely.  If your platform's SDK is
        lazy-installable, pass the ACTIVE installer separately as
        ``ensure_deps_fn`` (forwarded via ``entry_kwargs``); the gateway
        calls it from ``create_adapter()`` when ``check_fn`` is False,
        right before connecting the platform.

        Extra keyword arguments are forwarded to ``PlatformEntry`` (e.g.
        ``setup_fn``, ``emoji``, ``allowed_users_env``, ``platform_hint``,
        ``ensure_deps_fn``).  Unknown keys raise TypeError from the
        dataclass constructor.

        Example::

            ctx.register_platform(
                name="irc",
                label="IRC",
                adapter_factory=lambda cfg: IRCAdapter(cfg),
                check_fn=lambda: True,
                emoji="ðŸ’¬",
                setup_fn=irc_interactive_setup,
            )
        r   )Úplatform_registryÚPlatformEntryÚplugin_namer…   )r_   rH  rI  rÏ   rJ  rK  rL  r¤   z!Plugin %s registered platform: %sNr(   )Úgateway.platform_registryrO  rP  Ú
setdefaultr­   r_   rÚ   rº   Ú_plugin_platform_namesrÜ   r?   rB   )r½   r_   rH  rI  rÏ   rJ  rK  rL  rM  rO  rP  rå   s               r   Úregister_platformzPluginContext.register_platformÓ  sØ   € ðP 	OÐNÐNÐNÐNÐNÐNÐNà×Ò ¨t¬}Ô/AÑBÔBÐBØ�ð 

ØØØ+ØØ+Ø%Ð+¨Ø%Øð

ð 

ð ð

ð 

ˆð 	×"Ò" 5Ñ)Ô)Ð)ØŒÔ,×0Ò0°Ñ6Ô6Ð6Ý�ŠØ/ØŒMÔØñ	
ô 	
ð 	
ð 	
ð 	
r!   Ú	action_idÚcallbackc                ó’  — t          |¦  «        st          d| j        j        › d�¦  «        ‚|�)t	          |t
          ¦  «        r1|                     ¦   «         st          d| j        j        › d�¦  «        ‚| j        j         	                    ||| j        j        f¦  «         t                               d| j        j        |¦  «         dS )uò  Register a Slack Block Kit action handler from a plugin.

        Hermes' Slack adapter wires registered handlers into its
        ``slack_bolt.AsyncApp`` at connect time. The callback is invoked
        when a user clicks a button (or interacts with another Block Kit
        action element) whose ``action_id`` matches.

        Callback signature follows the slack_bolt convention::

            async def handler(ack, body, action) -> None:
                await ack()  # required, within 3 seconds
                ...

        Args:
            action_id: Whatever ``slack_bolt.App.action()`` accepts â€”
                a literal ``action_id`` string, a compiled ``re.Pattern``
                for matching multiple ids, or a constraint dict
                (e.g. ``{"action_id": "...", "block_id": "..."}``).
            callback: Async callable receiving ``(ack, body, action)``.

        Raises:
            ValueError: if ``callback`` is not callable, or ``action_id``
                is empty/None.

        Example::

            async def _on_approve(ack, body, action):
                await ack()
                # apply some workflow keyed on action["value"]

            ctx.register_slack_action_handler("inbox_sweep_approve", _on_approve)
        úPlugin 'zH' tried to register a Slack action handler with a non-callable callback.NzC' tried to register a Slack action handler with an empty action_id.z-Plugin %s registered Slack action handler: %s)Úcallabler   r­   r_   rj   r`   r3   rº   Ú_slack_action_handlersÚappendr?   rB   )r½   rV  rW  s      r   Úregister_slack_action_handlerz+PluginContext.register_slack_action_handler  s÷   € õJ ˜Ñ!Ô!ð 	Ýð@˜4œ=Ô-ð @ð @ð @ñô ð ð Ð¥¨IµsÑ!;Ô!;ÐÀIÇOÂOÑDUÔDUÐÝð;˜4œ=Ô-ð ;ð ;ð ;ñô ð ð 	ŒÔ,×3Ò3Ø˜ $¤-Ô"4Ð5ñ	
ô 	
ð 	
õ 	�ŠØ;ØŒMÔØñ	
ô 	
ð 	
ð 	
ð 	
r!   )Údefaultsr|   r!  r^  úOptional[Dict[str, Any]]c               óD  — |rt          |t          ¦  «        st          d| j        j        › d|›�¦  «        ‚t          d„ |D ¦   «         ¦  «        s t          d| j        j        › d|›d�¦  «        ‚ddlm} d„ |D ¦   «         }||v r0t          d| j        j        › d	|›d
| j        j        › d|› d�	¦  «        ‚| j        j	         
                    |¦  «        }|�Y| 
                    d¦  «        | j        j        k    r6t          d| j        j        › d	|›d| 
                    d¦  «        › d�¦  «        ‚dddddi dœ}|r|                     ¦   «         D ]
\  }	}
|
||	<   Œ||||| j        j        dœ| j        j	        |<   t                               d| j        j        ||¦  «         dS )uâ  Register a plugin-defined auxiliary LLM task.

        Auxiliary tasks are LLM-backed side jobs (vision analysis, web extraction,
        compression, smart-approval, etc.) that route through ``auxiliary_client.py``.
        Each task has its own ``auxiliary.<key>`` config block where users can
        pin a provider/model independent of the main chat model.

        Plugins use this to declare their own auxiliary tasks without touching
        core files. After registration, the task:

          - Appears in the ``hermes model â†’ Configure auxiliary models`` picker
          - Has its provider/model/base_url/api_key bridged from config.yaml to
            ``AUXILIARY_<KEY_UPPER>_*`` env vars at gateway startup
          - Gets default routing fields (provider="auto", model="", etc.) merged
            into loaded configs so ``cfg.get("auxiliary", {}).get(key)`` works

        Args:
            key: stable task key (snake_case). Used in config ``auxiliary.<key>``
                and env vars ``AUXILIARY_<KEY_UPPER>_*``. Must not shadow a
                built-in task key (vision, compression, web_extract, approval,
                mcp, title_generation, skills_hub, curator).
            display_name: human-readable name shown in the picker.
            description: short one-line description shown next to the name.
            defaults: optional dict of default routing fields. Recognized keys:
                ``provider`` (default "auto"), ``model`` (default ""),
                ``base_url`` (default ""), ``api_key`` (default ""),
                ``timeout`` (default 60), ``extra_body`` (default {}),
                plus any task-specific extras (e.g. ``download_timeout``).
                Unknown keys are preserved verbatim â€” the plugin owns the
                schema for its own task.

        Raises:
            ValueError: if *key* is empty, contains invalid characters, or
                shadows a built-in auxiliary task key.

        Example:
            ctx.register_auxiliary_task(
                key="memory_retain_filter",
                display_name="Memory retain filter",
                description="hindsight pre-retain dedup/extract",
                defaults={"provider": "auto", "timeout": 30},
            )
        rY  z4' tried to register auxiliary task with invalid key c              3  óJ   K  — | ]}|                      ¦   «         p|d k    V — ŒdS )Ú_N)r�   )r‚   Úcs     r   r„   z8PluginContext.register_auxiliary_task.<locals>.<genexpr>ˆ  s3   è è € Ð8Ð8¨q�1—9’9‘;”;Ð* ! s¢(Ð8Ð8Ð8Ð8Ð8Ð8r!   z' auxiliary task key z: must contain only alphanumeric characters and underscoresr   )Ú
_AUX_TASKSc                ó   — h | ]\  }}}|’Œ	S r(   r(   )r‚   ÚkÚ_nameÚ_descs       r   ú	<setcomp>z8PluginContext.register_auxiliary_task.<locals>.<setcomp>‘  s   € ÐDÐDÐD™o˜a ¨˜ÐDÐDÐDr!   z!' cannot register auxiliary task uS    â€” that key is reserved for a built-in task. Pick a plugin-namespaced key (e.g. 'rb  z').Nr…   u#    â€” already registered by plugin 'ú'Úautor*   é<   )r  ÚmodelÚbase_urlÚapi_keyÚtimeoutÚ
extra_body)r|   r!  rœ   r^  r…   z,Plugin %s registered auxiliary task: %s (%s))rj   r`   r   r­   r_   ÚallÚhermes_cli.mainrd  rº   Ú
_aux_tasksrr   Úitemsr?   rB   )r½   r|   r!  rœ   r^  Ú_BUILTIN_AUX_TASKSÚbuiltin_keysÚexistingÚmerged_defaultsrf  Úvs              r   Úregister_auxiliary_taskz%PluginContext.register_auxiliary_taskO  s‹  € ðh ð 	�* S­#Ñ.Ô.ð 	Ýð,˜4œ=Ô-ð ,ð ,Ø$'ð,ð ,ñô ð õ Ð8Ð8°CÐ8Ñ8Ô8Ñ8Ô8ð 	ÝðM˜4œ=Ô-ð Mð MÀCð Mð Mð Mñô ð ð 	EÐDÐDÐDÐDÐDàDÐDÐ1CÐDÑDÔDˆØ�,ÐÐÝðU˜4œ=Ô-ð Uð UØðUð Uà7;´}Ô7IðUð UàLOðUð Uð Uñô ð ð ”=Ô+×/Ò/°Ñ4Ô4ˆØÐ H§L¢L°Ñ$:Ô$:¸d¼mÔ>PÒ$PÐ$PÝð.˜4œ=Ô-ð .ð .Øð.ð .à—L’L Ñ*Ô*ð.ð .ð .ñô ð ð ØØØØØð+
ð +
ˆð ð 	'Ø ŸšÑ(Ô(ð 'ð '‘��1Ø%&� Ñ"Ð"ð Ø(Ø&Ø'Ø”mÔ(ð)
ð )
ˆŒÔ  Ñ%õ 	�ŠØ:ØŒMÔØØñ		
ô 	
ð 	
ð 	
ð 	
r!   Ú	hook_namec           
     ób  — |t           vrLt                               d| j        j        |d                     t          t           ¦  «        ¦  «        ¦  «         | j        j         	                    |g ¦  «         
                    |¦  «         t                               d| j        j        |¦  «         dS )z¢Register a lifecycle hook callback.

        Unknown hook names produce a warning but are still stored so
        forward-compatible plugins don't break.
        z4Plugin '%s' registered unknown hook '%s' (valid: %s)r“   zPlugin %s registered hook: %sN)r]   r?   rí   r­   r_   rˆ   Úsortedrº   Ú_hooksrS  r\  rB   )r½   r|  rW  s      r   Úregister_hookzPluginContext.register_hook¾  s™   € ð �KÐ'Ð'Ý�NŠNðà”Ô"ØØ—	’	�&¥Ñ-Ô-Ñ.Ô.ñô ð ð 	ŒÔ×'Ò'¨	°2Ñ6Ô6×=Ò=¸hÑGÔGÐGÝ�ŠÐ4°d´mÔ6HÈ)ÑTÔTÐTÐTÐTr!   r§   c           
     ób  — |t           vrLt                               d| j        j        |d                     t          t           ¦  «        ¦  «        ¦  «         | j        j         	                    |g ¦  «         
                    |¦  «         t                               d| j        j        |¦  «         dS )aS  Register a behavior-changing middleware callback.

        Middleware is separate from observer hooks: request middleware may
        rewrite the effective payload, and execution middleware may wrap the
        real callback. Unknown kinds are stored for forward compatibility but
        warned so plugin authors can catch typos.
        z:Plugin '%s' registered unknown middleware '%s' (valid: %s)r“   z#Plugin %s registered middleware: %sN)r   r?   rí   r­   r_   rˆ   r~  rº   Ú_middlewarerS  r\  rB   )r½   r§   rW  s      r   Úregister_middlewarez!PluginContext.register_middlewareÑ  sœ   € ð Õ'Ð'Ð'Ý�NŠNðà”Ô"ØØ—	’	�&Õ!1Ñ2Ô2Ñ3Ô3ñô ð ð 	ŒÔ!×,Ò,¨T°2Ñ6Ô6×=Ò=¸hÑGÔGÐGÝ�ŠÐ:¸D¼MÔ<NÐPTÑUÔUÐUÐUÐUr!   r¦   r   ÚfrontmatterúOptional[Mapping[str, Any]]c                ó&  — ddl m} d|v r t          d|› d| j        j        › d�¦  «        ‚|r|                     |¦  «        st          d|› d�¦  «        ‚|                     ¦   «         st          d	|› �¦  «        ‚| j        j        p| j        j        }|› d|› �}| j        j	        r!|| j
        j        v rt          d
|› d�¦  «        ‚||||t          |pi ¦  «        dœ| j
        j        |<   t                               d| j        j        |¦  «         dS )u  Register a read-only skill provided by this plugin.

        The skill becomes resolvable as ``'<plugin_name>:<name>'`` via
        ``skill_view()``.  It does **not** enter the flat
        ``~/.hermes/skills/`` tree and is **not** listed in the system
        prompt's ``<available_skills>`` index â€” plugin skills are
        opt-in explicit loads only.

        Raises:
            ValueError: if *name* contains ``':'`` or invalid characters.
            FileNotFoundError: if *path* does not exist.
        r   )Ú_NAMESPACE_REú:zSkill name 'zG' must not contain ':' (the namespace is derived from the plugin name 'z' automatically).zInvalid skill name 'z'. Must match [a-zA-Z0-9_-]+.zSKILL.md not found at zPlugin skill 'z' is already registered)r¦   r…   Ú	bare_namerœ   r„  zPlugin %s registered skill: %sN)Úagent.skill_utilsr‡  r   r­   r_   ÚmatchÚexistsÚFileNotFoundErrorr©   r¨   rº   Ú_plugin_skillsrs   r?   rB   )r½   r_   r¦   rœ   r„  r‡  Ú	namespaceÚ	qualifieds           r   Úregister_skillzPluginContext.register_skillæ  s�  € ð& 	4Ð3Ð3Ð3Ð3Ð3à�$ˆ;ˆ;Ýð:˜tð :ð :à”MÔ&ð:ð :ð :ñô ð ð
 ð 	˜=×.Ò.¨tÑ4Ô4ð 	ÝØJ tÐJÐJÐJñô ð ð �{Š{‰}Œ}ð 	EÝ#Ð$C¸TÐ$CÐ$CÑDÔDÐDà”MÔ1ÐG°T´]Ô5Gˆ	Ø Ð)Ð) 4Ð)Ð)ˆ	ØŒ=Ô!ð 	R i°4´=Ô3OÐ&OÐ&OÝÐP¨iÐPÐPÐPÑQÔQÐQàØØØ&Ý Ð 1¨rÑ2Ô2ð3
ð 3
ˆŒÔ$ YÑ/õ 	�ŠØ,ØŒMÔ 	ñ	
ô 	
ð 	
ð 	
ð 	
r!   )r­   rš   r·   r¸   )r   r   )r   r`   )NNFr*   r*   F)r_   r`   rÍ   r`   rÎ   rs   rC   r   rÏ   rÐ   r    rÑ   rÒ   r0   rœ   r`   rÓ   r`   rÔ   r0   r   r1   )rÞ   r`   r   r0   )ræ   )rç   r`   rè   r`   r   r0   )Nr*   )r_   r`   rô   r`   rõ   r   rö   rÐ   rœ   r`   r   r1   )r*   r*   )
r_   r`   rC   r   rœ   r`   rú   r`   r   r1   )rÞ   r`   r  rs   r   r`   ©r   r1   )NNr*   )r_   r`   rH  r`   rI  r   rÏ   r   rJ  rÐ   rK  rÑ   rL  r`   rM  r   r   r1   )rV  r   rW  r   r   r1   )
r|   r`   r!  r`   rœ   r`   r^  r_  r   r1   )r|  r`   rW  r   r   r1   )r§   r`   rW  r   r   r1   )r*   N)
r_   r`   r¦   r   rœ   r`   r„  r…  r   r1   )r$   r%   r&   r'   r¾   ÚpropertyrÃ   rÈ   rÌ   rÝ   rØ   ró   rù   r  r  r  r  r#  r)  r/  r5  r;  rA  rG  rU  r]  r{  r€  rƒ  r‘  r(   r!   r   r¶   r¶   p  sº  € € € € € ØGÐGð-ð -ð -ð -ð ðð ð ñ „Xðð  ð(ð (ð (ñ „Xð(ð& ðð ð ñ „Xðð6 %)Ø$(ØØØØð7
ð 7
ð 7
ð 7
ð 7
ðv=ð =ð =ð =ð4ð ð ð ð ðB '+ØðWð Wð Wð Wð Wð: Øð4Uð 4Uð 4Uð 4Uð 4Uðp<ð <ð <ð <ð>
ð 
ð 
ð 
ð@
ð 
ð 
ð 
ð6$
ð $
ð $
ð $
ðP
ð 
ð 
ð 
ð6
ð 
ð 
ð 
ð8
ð 
ð 
ð 
ð@+ð +ð +ð +ð^"
ð "
ð "
ð "
ðL(
ð (
ð (
ð (
ðd ,0Ø$(Øð<
ð <
ð <
ð <
ð <
ð@6
ð 6
ð 6
ð 6
ðD .2ðm
ð m
ð m
ð m
ð m
ð m
ð^Uð Uð Uð Uð&Vð Vð Vð Vð2 Ø37ð0
ð 0
ð 0
ð 0
ð 0
ð 0
ð 0
r!   r¶   c                  óð   — e Zd ZdZdCd„ZdDdEd„ZdCd	„ZdFd„ZdGd„Z	 dHdId„Z	dJd„Z
dKd„ZdFd„ZdLd"„ZdMd#„ZdMd$„ZdNd'„ZdOd)„ZdOd*„ZdPd/„ZdQd0„ZdRd2„ZdSd3„ZdTd5„ZdUd7„ZdVd:„ZdWd=„ZdUd>„ZdXd@„ZdYdA„ZdZdB„ZdS )[ÚPluginManagerz;Central manager that discovers, loads, and invokes plugins.r   r1   c                óú   — i | _         i | _        i | _        t          ¦   «         | _        t          ¦   «         | _        i | _        d | _        i | _        d| _	        d | _
        i | _        i | _        i | _        g | _        d S )NF)Ú_pluginsr  r‚  rc   rÛ   rT  rø   r  r  Ú_discoveredrì   rŽ  Ú_portable_mcp_serversrt  r[  ©r½   s    r   r¾   zPluginManager.__init__   s€   € Ø13ˆŒØ13ˆŒØ68ˆÔÝ,/©E¬EˆÔÝ03±´ˆÔ#Ø.0ˆÔØ#ˆÔØ13ˆÔØ!&ˆÔØˆŒà9;ˆÔØ@BˆÔ"ð 68ˆŒð 46ˆÔ#Ð#Ð#r!   Fr/   r0   c                ó  — | j         r|sdS t          d¦  «        r#t                               d¦  «         d| _         dS |�r| j                             ¦   «          | j                             ¦   «          | j                             ¦   «          | j                             ¦   «          | j	                             ¦   «          | j
                             ¦   «          | j                             ¦   «          | j                             ¦   «          | j                             ¦   «          | j                             ¦   «          | j                             ¦   «          d| _        d| _         	 |                      ¦   «          dS # t$          $ r	 d| _         ‚ w xY w)a  Scan all plugin sources and load each plugin found.

        When ``force`` is true, clear cached discovery state first so config
        changes or newly-added bundled backends become visible in long-lived
        sessions without requiring a full agent restart.
        NÚHERMES_SAFE_MODEu/   HERMES_SAFE_MODE=1 â€” plugin discovery skippedTF)r˜  r   r?   r  r—  Úclearr  r‚  rÛ   rT  rø   r  rŽ  r™  rt  r[  r  Ú_discover_and_load_innerÚBaseException)r½   r/   s     r   Údiscover_and_loadzPluginManager.discover_and_load=  s�  € ð Ôð 	 Eð 	ØˆFÝÐ-Ñ.Ô.ð 	Ý�KŠKÐIÑJÔJÐJØ#ˆDÔØˆFØñ 	(ØŒM×ÒÑ!Ô!Ð!ØŒK×ÒÑÔÐØÔ×"Ò"Ñ$Ô$Ð$ØÔ#×)Ò)Ñ+Ô+Ð+ØÔ'×-Ò-Ñ/Ô/Ð/ØÔ×$Ò$Ñ&Ô&Ð&ØÔ!×'Ò'Ñ)Ô)Ð)ØÔ×%Ò%Ñ'Ô'Ð'ØÔ&×,Ò,Ñ.Ô.Ð.ØŒO×!Ò!Ñ#Ô#Ð#ØÔ'×-Ò-Ñ/Ô/Ð/Ø#'ˆDÔ ð  ˆÔð	Ø×)Ò)Ñ+Ô+Ð+Ð+Ð+øÝð 	ð 	ð 	Ø$ˆDÔØð	øøøs   Å#E9 Å9Fc           	     óš  — |                       ¦   «         }|                      ¦   «         }t                               dt	          |¦  «        ¦  «         |                     |¦  «         t          ¦   «         }t          ¦   «         }i }|D ]}|||j        p|j	        <   Œ| 
                    ¦   «         D �]»}|j        p|j	        }||v s	|j	        |v r>t          |d¬¦  «        }d|_        || j        |<   t                               d|¦  «         Œ\|j        dk    r>t          |d¬¦  «        }d|_        || j        |<   t                               d|¦  «         Œ¥|j        d	k    r7t          |d
¬¦  «        }|| j        |<   t                               d|¦  «         Œç|j        dk    r"|j        dk    r|                      |¦  «         �Œ|j        dk    r"|j        dk    r|                      |¦  «         �ŒA|duo||v p|j	        |v }	|	sRt          |d¬¦  «        }d                     |¦  «        |_        || j        |<   t                               d|¦  «         �Œ¦|                      |¦  «         �Œ½|r^t                               dt	          | j        ¦  «        t)          d„ | j         
                    ¦   «         D ¦   «         ¦  «        ¦  «         dS dS )u=   The actual discovery sweep â€” see :meth:`discover_and_load`.z  entrypoints: %d manifest(s)F©r­   rq   zdisabled via configzSkipping disabled plugin '%s'rx   u<   exclusive plugin â€” activate via <category>.provider configz8Skipping '%s' (exclusive, handled by category discovery)rz   Tz?Skipping '%s' (model-provider, handled by providers/ discovery)rà   rv   rw   NzBnot enabled in config (run `hermes plugins enable {}` to activate)z&Skipping '%s' (not in plugins.enabled)z/Plugin discovery complete: %d found, %d enabledc              3  ó(   K  — | ]}|j         ¯	d V — ŒdS )é   N)rq   )r‚   Úps     r   r„   z9PluginManager._discover_and_load_inner.<locals>.<genexpr>Õ  s)   è è € ÐCÐC˜!¸¼ÐC�AÐCÐCÐCÐCÐCÐCr!   )Ú_collect_directory_manifestsÚ_scan_entry_pointsr?   rB   ÚlenÚextendrn   ru   r|   r_   Úvaluesr¬   r³   r—  r§   r¤   Ú_load_pluginÚ_register_deferred_platformÚformatr  Úsum)
r½   Ú	manifestsÚep_manifestsrg   rq   Úwinnersr­   Ú
lookup_keyÚloadedÚ
is_enableds
             r   rž  z&PluginManager._discover_and_load_innerd  s0  € à*.×*KÒ*KÑ*MÔ*Mˆ	ð
 ×.Ò.Ñ0Ô0ˆÝ�ŠÐ4µc¸,Ñ6GÔ6GÑHÔHÐHØ×Ò˜Ñ&Ô&Ð&õ )Ñ*Ô*ˆÝ&Ñ(Ô(ˆØ-/ˆØ!ð 	>ð 	>ˆHØ5=ˆG�H”LÐ1 H¤MÑ2Ð2ØŸšÑ(Ô(ð T	(ñ T	(ˆHØ!œÐ6¨¬ˆJð ˜XÐ%Ð%¨¬¸(Ð)BÐ)BÝ%¨xÀÐGÑGÔG�Ø4�”Ø,2�”˜jÑ)Ý—’Ð<¸jÑIÔIÐIØð
 Œ} Ò+Ð+Ý%¨xÀÐGÑGÔG�àRð ”ð -3�”˜jÑ)Ý—’ØNØñô ð ð ð Œ}Ð 0Ò0Ð0Ý%¨xÀÐFÑFÔF�Ø,2�”˜jÑ)Ý—’ØUØñô ð ð ð Œ )Ò+Ð+°´ÀÒ0JÐ0JØ×!Ò! (Ñ+Ô+Ð+Ùð Œ )Ò+Ð+°´ÀÒ0KÐ0KØ×0Ò0°Ñ:Ô:Ð:Ùð ˜tÐ#ð HØ 7Ð*ÐF¨h¬m¸wÐ.Fð ð ð 
Ý%¨xÀÐGÑGÔG�àXß’V˜JÑ'Ô'ð ”ð -3�”˜jÑ)Ý—’Ø<¸jñô ð ñ Ø×Ò˜hÑ'Ô'Ð'Ñ'àð 	Ý�KŠKØAÝ�D”MÑ"Ô"ÝÐCÐC˜tœ}×3Ò3Ñ5Ô5ÐCÑCÔCÑCÔCñô ð ð ð ð	ð 	r!   úList[PluginManifest]c                ó  — g }t          ¦   «         }t                               d|¦  «         |                      |dh d£¬¦  «        }t                               dt	          |¦  «        ¦  «         |                     |¦  «         |                      |dz  d¬¦  «        }t                               dt	          |¦  «        ¦  «         |                     |¦  «         t          ¦   «         d	z  }t                               d
|¦  «         |                      |d¬¦  «        }t                               dt	          |¦  «        ¦  «         |                     |¦  «         t          d¦  «        r‰t          j	        ¦   «         dz  d	z  }t                               d|¦  «         |                      |d¬¦  «        }t                               dt	          |¦  «        ¦  «         |                     |¦  «         nt                               d¦  «         |S )a£  Collect directory manifests in the same order as full discovery.

        This method only reads manifests. It does not load native plugin
        modules, register deferred platforms, or otherwise mutate manager
        registries. Keeping the source ordering and scanner calls here lets
        startup probes share the exact precedence and containment rules used
        by :meth:`_discover_and_load_inner`.
        zScanning bundled plugins: %srà   >   úmodel-providersÚmemoryÚ	platformsÚcontext_engine)r¤   Ú
skip_namesz%  bundled (top-level): %d manifest(s)r¹  )r¤   z#  bundled/platforms: %d manifest(s)r   zScanning user plugins: %sræ   z  user: %d manifest(s)ÚHERMES_ENABLE_PROJECT_PLUGINSz.hermeszScanning project plugins: %sÚprojectz  project: %d manifest(s)zHProject plugins disabled (set HERMES_ENABLE_PROJECT_PLUGINS=1 to enable))
r    r?   rB   Ú_scan_directoryr¨  r©  r   rb   r   Úcwd)	r½   r¯  Úrepo_pluginsrà   Úbundled_platformsÚuser_dirÚuser_manifestsÚproject_dirÚproject_manifestss	            r   r¦  z*PluginManager._collect_directory_manifestsØ  sü  € ð +-ˆ	õ
 /Ñ0Ô0ˆÝ�ŠÐ3°\ÑBÔBÐBØ×&Ò&ØØØSÐSÐSð 'ñ 
ô 
ˆõ
 	�ŠÐ<½cÀ'¹l¼lÑKÔKÐKØ×Ò˜Ñ!Ô!Ð!Ø ×0Ò0Ø˜;Ñ&¨yð 1ñ 
ô 
Ðõ 	�ŠÐ:½CÐ@QÑ<RÔ<RÑSÔSÐSØ×ÒÐ*Ñ+Ô+Ð+õ #Ñ$Ô$ yÑ0ˆÝ�ŠÐ0°(Ñ;Ô;Ð;Ø×-Ò-¨h¸vÐ-ÑFÔFˆÝ�ŠÐ-­s°>Ñ/BÔ/BÑCÔCÐCØ×Ò˜Ñ(Ô(Ð(õ Ð7Ñ8Ô8ð 		Ýœ(™*œ* yÑ0°9Ñ<ˆKÝ�LŠLÐ7¸ÑEÔEÐEØ $× 4Ò 4°[ÈÐ 4Ñ SÔ SÐÝ�LŠLÐ4µcÐ:KÑ6LÔ6LÑMÔMÐMØ×ÒÐ.Ñ/Ô/Ð/Ð/å�LŠLØZñô ð ð Ðr!   Ú
raw_configúMapping[str, Any]c                ó*  — t          d¦  «        rdS |                     d¦  «        }t          |t          ¦  «        sdS |                     d¦  «        }t          |t          ¦  «        sdS d„ |D ¦   «         }|                     dg ¦  «        }t          |t          ¦  «        rd„ |D ¦   «         nt          ¦   «         }|sdS i }|                      ¦   «         D ]}|||j        p|j        <   Œ| 	                    ¦   «         D ]•}|j
        sŒ
|j        p|j        }	|	|v s	|j        |v rŒ&|	|vr
|j        |vrŒ4	 dd	lm}
  |
t          |j        ¦  «        t          ¦   «         d
z  |j        p|	z  g d¬¦  «        r dS Œy# t"          t$          t&          f$ r Y Œ’w xY wdS )a*  Probe enabled portable MCP packages without loading plugins.

        The directory manifest collection is shared with full discovery, so
        native ``plugin.yaml`` precedence, source ordering, depth limits, and
        project-plugin gating cannot diverge between startup and runtime.
        rœ  Fr   rq   c                ó<   — h | ]}t          |t          ¦  «        ¯|’ŒS r(   ©rj   r`   ©r‚   r�   s     r   ri  z9PluginManager.has_enabled_portable_mcp.<locals>.<setcomp>  s'   € ÐNÐNÐN˜UµzÀ%ÍÑ7MÔ7MÐN�5ÐNÐNÐNr!   rg   c                ó<   — h | ]}t          |t          ¦  «        ¯|’ŒS r(   rÊ  rË  s     r   ri  z9PluginManager.has_enabled_portable_mcp.<locals>.<setcomp>  s'   € ÐIÐIÐI�uµ*¸UÅCÑ2HÔ2HÐIˆUÐIÐIÐIr!   r   )Ú_discover_mcpúplugin-data)Úcreate_dataT)rb   rr   rj   rs   rk   rc   r¦  r|   r_   rª  r¨   Úhermes_cli.agent_pluginsrÍ  r   r¦   r   r©   ÚOSErrorÚRuntimeErrorr   )r½   rÆ  Úplugins_configÚenabled_valuerq   Údisabled_valuerg   r±  r­   r²  rÍ  s              r   Úhas_enabled_portable_mcpz&PluginManager.has_enabled_portable_mcp  s  € õ Ð*Ñ+Ô+ð 	Ø�5à#Ÿš¨	Ñ2Ô2ˆÝ˜.­$Ñ/Ô/ð 	Ø�5Ø&×*Ò*¨9Ñ5Ô5ˆÝ˜-­Ñ.Ô.ð 	Ø�5ØNÐN mÐNÑNÔNˆØ'×+Ò+¨J¸Ñ;Ô;ˆõ ˜.­$Ñ/Ô/ðÐIÐI ÐIÑIÔIÐIå‘”ð 	ð
 ð 	Ø�5à-/ˆØ×9Ò9Ñ;Ô;ð 	>ð 	>ˆHØ5=ˆG�H”LÐ1 H¤MÑ2Ð2àŸšÑ(Ô(ð 	ð 	ˆHØÔ$ð ØØ!œÐ6¨¬ˆJØ˜XÐ%Ð%¨¬¸(Ð)BÐ)BØØ Ð(Ð(¨X¬]À'Ð-IÐ-IØðØBÐBÐBÐBÐBÐBà �=Ý˜œÑ'Ô'Ý#Ñ%Ô%Ø#ñ$àÔ/Ð=°:ñ?ð Ø %ðñ ô ð  ð  ˜4˜4ð øõ �\­:Ð6ð ð ð ð �ðøøøð ˆus   Ä2A E6Å6FÆFNr¦   r   r¤   r`   r»  úOptional[Set[str]]c                ó6   — |                       |||dd¬¦  «        S )uÅ  Read ``plugin.yaml`` manifests from subdirectories of *path*.

        Supports two layouts, mixed freely:

        * **Flat** â€” ``<root>/<plugin-name>/plugin.yaml``. Key is
          ``<plugin-name>`` (e.g. ``disk-cleanup``).
        * **Category** â€” ``<root>/<category>/<plugin-name>/plugin.yaml``,
          where the ``<category>`` directory itself has no ``plugin.yaml``.
          Key is ``<category>/<plugin-name>`` (e.g. ``image_gen/openai``).
          Depth is capped at two segments.

        *skip_names* is an optional allow-list of names to ignore at the
        top level (kept for back-compat; the current call sites no longer
        pass it now that categories are first-class).
        r*   r   ©r»  ÚprefixÚdepth)Ú_scan_directory_level)r½   r¦   r¤   r»  s       r   r¾  zPluginManager._scan_directoryG  s,   € ð* ×)Ò)Ø�& Z¸À!ð *ñ 
ô 
ð 	
r!   rÚ  rÛ  Úintc               óö  — g }|                      ¦   «         s|S t          |                     ¦   «         ¦  «        D �]=}|                      ¦   «         sŒ|dk    r|r
|j        |v rŒ*|dz  }|                     ¦   «         s|dz  }|                     ¦   «         r0|                      ||||¦  «        }	|	�|                     |	¦  «         ŒŒ|dz  }
|
                     ¦   «         s|
                     ¦   «         �r	 ddlm	}  ||¦  «        \  }}|D ]#}t                               d||j        ¦  «         Œ$|r|› d|j        › �n|d	         }|                     t          |d	         |                     d
d¦  «        |                     dd¦  «        t          |                     dd¦  «        ¦  «        |t!          |¦  «        |dt#          |¦  «        ¬¦	  «	        ¦  «         n3# t$          $ r&}t                               d|
|¦  «         Y d}~nd}~ww xY w�ŒÕ|dk    rt                               d|¦  «         �Œø|r|› d|j        › �n|j        }|                     |                      ||d||dz   ¬¦  «        ¦  «         �Œ?|S )a  Recursive implementation of :meth:`_scan_directory`.

        ``prefix`` is the category path already accumulated ("" at root,
        "image_gen" one level in). ``depth`` is the recursion depth; we
        cap at 2 so ``<root>/a/b/c/`` is ignored.
        r   zplugin.yamlz
plugin.ymlNzplugin.json)Úread_agent_plugin_manifestzAgent Plugin '%s': %srü   r_   r›   r*   rœ   r�   T)	r_   r›   rœ   r�   r¤   r¦   r|   r¨   r©   úFailed to parse %s: %sr¤  z/Skipping %s (no plugin.yaml, depth cap reached)rÙ  )Úis_dirr~  Úiterdirr_   rŒ  Ú_parse_manifestr\  Ú
is_symlinkrÐ  rß  r?   rí   Úmessagerš   rr   r˜   r`   r�   rl   rB   r©  rÜ  )r½   r¦   r¤   r»  rÚ  rÛ  r¯  ÚchildÚmanifest_filer­   Úportable_filerß  ÚdataÚdiagnosticsÚ
diagnosticr|   ÚexcÚ
sub_prefixs                     r   rÜ  z#PluginManager._scan_directory_level`  s  € ð +-ˆ	Ø�{Š{‰}Œ}ð 	ØÐå˜DŸLšL™NœNÑ+Ô+ð ?	ñ ?	ˆEØ—<’<‘>”>ð ØØ˜Šzˆz˜jˆz¨U¬Z¸:Ð-EÐ-EØØ! MÑ1ˆMØ ×'Ò'Ñ)Ô)ð 5Ø %¨Ñ 4�à×#Ò#Ñ%Ô%ð Ø×/Ò/Ø! 5¨&°&ñô �ð Ð'Ø×$Ò$ XÑ.Ô.Ð.Øà! MÑ1ˆMØ×#Ò#Ñ%Ô%ð ¨×)AÒ)AÑ)CÔ)Cñ ðQØSÐSÐSÐSÐSÐSà(BÐ(BÀ5Ñ(IÔ(IÑ%�D˜+Ø&1ð ð ˜
ÝŸšØ3Ø!Ø&Ô.ñô ð ð ð
 7=ÐN˜VÐ2Ð2 e¤jÐ2Ð2Ð2À$ÀvÄ,�CØ×$Ò$Ý&Ø!% f¤Ø$(§H¢H¨Y¸Ñ$;Ô$;Ø(,¯ª°ÀÑ(CÔ(CÝ#2°4·8²8¸HÀbÑ3IÔ3IÑ#JÔ#JØ#)Ý!$ U¡¤Ø #Ø%)Ý,EÀcÑ,JÔ,Jð
ñ 
ô 
ñô ð ð øõ !ð Qð Qð QÝ—N’NÐ#;¸]ÈCÑPÔPÐPÐPÐPÐPÐPÐPøøøøðQøøøáð
 ˜ŠzˆzÝ—’ÐNÐPUÑVÔVÐVÙà5;ÐK˜FÐ1Ð1 U¤ZÐ1Ð1Ð1ÀÄˆJØ×ÒØ×*Ò*ØØØ#Ø%Ø !™)ð +ñ ô ñô ð ñ ð Ðs   Ã5C$GÇ
H
Ç$HÈH
rç  Ú
plugin_dirúOptional[PluginManifest]c                ó�  — 	 t           €t                               d|¦  «         dS t          |                     d¬¦  «        ¦  «        pi }|                     d|j        ¦  «        }|r|› d|j        › �n|}|                     dd¦  «        }t          |t          ¦  «        sd}| 	                    ¦   «          
                    ¦   «         }	|	t          vrDt                               d	||d
                     t          t          ¦  «        ¦  «        ¦  «         d}	|	dk    r™d|vr•|dz  }
|
                     ¦   «         r|	 |
                     dd¬¦  «        dd…         }d|v sd|v rd}	t                               d|¦  «         n%d|v r!d|v rd}	t                               d|¦  «         n# t           $ r Y nw xY wt                               d|||	||¦  «         t#          |t          |                     dd¦  «        ¦  «        |                     dd¦  «        t%          |                     dd¦  «        ¦  «        |                     dg ¦  «        |                     dg ¦  «        |                     dg ¦  «        |t          |¦  «        |	|¬¦  «        S # t           $ r.}t                               d ||t&          ¬!¦  «         Y d}~dS d}~ww xY w)"z„Parse a single ``plugin.yaml`` into a :class:`PluginManifest`.

        Returns ``None`` on parse failure (logs a warning).
        Nu'   PyYAML not installed â€“ cannot load %sr†   )Úencodingr_   rü   r§   ry   zBPlugin %s: unknown kind '%s' (valid: %s); treating as 'standalone'r“   ú__init__.pyr   )Úerrorsrñ  i    Úregister_memory_providerÚMemoryProviderrx   zAPlugin %s: detected memory provider, treating as kind='exclusive'r  ÚProviderProfilerz   zEPlugin %s: detected model provider, treating as kind='model-provider'z9Parsed manifest: key=%s name=%s kind=%s source=%s path=%sr›   r*   rœ   r�   r    r¢   r£   )r_   r›   rœ   r�   r    r¢   r£   r¤   r¦   r§   r|   rà  ©Úexc_info)Úyamlr?   rí   r   Ú	read_textrr   r_   rj   r`   r3   r4   r{   rˆ   r~  rŒ  rB   rl   rš   r˜   r5   )r½   rç  rî  r¤   rÚ  ré  r_   r|   Úraw_kindr§   Ú	init_fileÚsource_textrì  s                r   rã  zPluginManager._parse_manifest¶  s*  € ðN	Ýˆ|Ý—’ÐHÈ-ÑXÔXÐXØ�tÝ! -×"9Ò"9À7Ð"9Ñ"KÔ"KÑLÔLÐRÐPRˆDà—8’8˜F J¤OÑ4Ô4ˆDØ39ÐC�VÐ/Ð/˜jœoÐ/Ð/Ð/¸tˆCà—x’x ¨Ñ5Ô5ˆHÝ˜h­Ñ,Ô,ð (Ø'�Ø—>’>Ñ#Ô#×)Ò)Ñ+Ô+ˆDØÕ.Ð.Ð.Ý—’ØXØ˜ 4§9¢9­VÕ4GÑ-HÔ-HÑ#IÔ#Iñô ð ð $�ð �|Ò#Ð#¨°dÐ(:Ð(:Ø&¨Ñ6�	Ø×#Ò#Ñ%Ô%ð ðØ&/×&9Ò&9ÀÐU\Ð&9Ñ&]Ô&]Ð^cÐ_cÐ^cÔ&d˜à6¸+ÐEÐEØ/°;Ð>Ð>à#.˜DÝ"ŸLšLð!?à #ñô ð ð ð 0°;Ð>Ð>Ø 1°[Ð @Ð @ð
 $4˜DÝ"ŸLšLð!Dà #ñô ð øøõ
 %ð ð ð Ø˜ðøøøõ �LŠLØKØ�T˜4 ¨ñô ð õ "ØÝ˜DŸHšH Y°Ñ3Ô3Ñ4Ô4Ø ŸHšH ]°BÑ7Ô7Ý& t§x¢x°¸"Ñ'=Ô'=Ñ>Ô>Ø!ŸXšX n°bÑ9Ô9Ø#ŸxšxÐ(8¸"Ñ=Ô=Ø#ŸxšxÐ(8¸"Ñ=Ô=ØÝ˜‘_”_ØØðñ ô ð øõ ð 	ð 	ð 	Ý�NŠNØ(¨-¸Å~ð ñ ô ð ð �4�4�4�4�4øøøøð		øøøsC   ‚"J ¦DJ Ä:A*F% Æ$J Æ%
F2Æ/J Æ1F2Æ2CJ Ê
KÊ#K Ë Kc                óø  — g }	 t           j                             ¦   «         }t          |d¦  «        r|                     t
          ¬¦  «        }n=t          |t          ¦  «        r|                     t
          g ¦  «        }nd„ |D ¦   «         }|D ]9}t          |j
        d|j        |j
        ¬¦  «        }|                     |¦  «         Œ:n2# t          $ r%}t                               d|¦  «         Y d}~nd}~ww xY w|S )z7Check ``importlib.metadata`` for pip-installed plugins.Úselect©Úgroupc                ó2   — g | ]}|j         t          k    ¯|‘ŒS r(   ©r  ÚENTRY_POINTS_GROUP©r‚   Úeps     r   ú
<listcomp>z4PluginManager._scan_entry_points.<locals>.<listcomp>   s%   € ÐPÐPÐP B°´Õ=OÒ1OÐ1O˜RÐ1OÐ1OÐ1Or!   Ú
entrypoint)r_   r¤   r¦   r|   zEntry-point scan failed: %sN)Ú	importlibÚmetadataÚentry_pointsÚhasattrrÿ  r  rj   rs   rr   rš   r_   r�   r\  rl   r?   rB   )r½   r¯  ÚepsÚ	group_epsr  r­   rì  s          r   r§  z PluginManager._scan_entry_points  s%  € à*,ˆ	ð	=ÝÔ$×1Ò1Ñ3Ô3ˆCå�s˜HÑ%Ô%ð QØŸJšJÕ-?˜JÑ@Ô@�	�	Ý˜C¥Ñ&Ô&ð QØŸGšGÕ$6¸Ñ;Ô;�	�	àPÐP¨#ÐPÑPÔP�	àð +ð +�Ý)ØœØ'ØœØœð	ñ ô �ð × Ò  Ñ*Ô*Ð*Ð*ð+øõ ð 	=ð 	=ð 	=Ý�LŠLÐ6¸Ñ<Ô<Ð<Ð<Ð<Ð<Ð<Ð<øøøøð	=øøøð Ðs   „CC Ã
C7ÃC2Ã2C7r­   rš   c                ó²   — |j         pd}|                     d¦  «        r|dt          d¦  «         …         S |j        rt	          |j        ¦  «        j         S |S )a¤  Derive the gateway platform name (e.g. ``feishu``) for a platform plugin.

        The platform name registered via ``register_platform(name=...)`` lives
        inside the adapter module (which we are explicitly trying NOT to import
        early). It is not carried in ``plugin.yaml``. Across every bundled
        platform plugin the manifest name is ``<platform>-platform`` and the
        plugin directory basename is ``<platform>``, so we derive the name
        without importing: strip a trailing ``-platform`` from the manifest
        name, falling back to the directory basename. This is also a sensible
        convention for third-party platform plugins.
        r*   z	-platformN)r_   Úendswithr¨  r¦   r   )r½   r­   r_   s      r   Ú_platform_name_from_manifestz*PluginManager._platform_name_from_manifest3  sb   € ð Œ}Ð" ˆØ�=Š=˜Ñ%Ô%ð 	-ØÐ+�3˜{Ñ+Ô+Ð+Ð+Ô,Ð,ØŒ=ð 	,Ý˜œÑ&Ô&Ô+Ð+Øˆr!   c                óš  ‡ — |j         p|j        }‰                      |¦  «        }t          |d¬¦  «        }d|_        |‰ j        |<   |fdˆ fd„}	 dd	lm} |                     ||¦  «         t           
                    d
||¦  «         dS # t          $ r6 t           
                    d|d¬¦  «         ‰                      |¦  «         Y dS w xY w)a¾  Register a lazy loader for a bundled platform plugin.

        The platform adapter module is imported only when the gateway / cron /
        setup / send_message path first asks the ``platform_registry`` for this
        platform. Until then we record a lightweight ``LoadedPlugin`` so
        ``hermes plugins list`` still shows the platform as available, and we
        hand the registry a loader that runs the normal eager-load path.
        Tr¢  Ú	_manifestrš   r   r1   c                ó2   •— ‰                      | ¦  «         d S r•   )r«  )r  r½   s    €r   Ú_loaderz:PluginManager._register_deferred_platform.<locals>._loaderY  s   ø€ Ø×Ò˜iÑ(Ô(Ð(Ð(Ð(r!   r   )rO  z3Registered deferred platform loader: %s (plugin=%s)z=Deferred platform registration failed for '%s'; eager-loadingr÷  N)r  rš   r   r1   )r|   r_   r  r¬   r´   r—  rR  rO  Úregister_deferredr?   rB   rl   r«  )r½   r­   r²  Úplatform_namer³  r  rO  s   `      r   r¬  z)PluginManager._register_deferred_platformF  s+  ø€ ð ”\Ð2 X¤]ˆ
Ø×9Ò9¸(ÑCÔCˆõ
  x¸Ð>Ñ>Ô>ˆØˆŒØ$*ˆŒ�jÑ!à08ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð	(ØCÐCÐCÐCÐCÐCà×/Ò/°¸wÑGÔGÐGÝ�LŠLØEØØñô ð ð ð øõ
 ð 	(ð 	(ð 	(õ �LŠLØOØØð ñ ô ð ð
 ×Ò˜hÑ'Ô'Ð'Ð'Ð'Ð'ð	(øøøs   Á8B
 Â
<C
Ã	C
c                ó°  ‡ ‡‡
‡‡— t          ‰¬¦  «        }t                               d‰j        p‰j        ‰j        ‰j        ‰j        ¦  «         ‰j        r‰  	                    ‰|¦  «         dS ddl
m} ‰j        p‰j        }|                     dd¦  «                             dd	¦  «        }|                     t          › d
|› �t          ‰‰ ¦  «                             d¦  «        ¦  «         	 ‰j        dv r‰                      ‰¦  «        }n‰                      ‰¦  «        }||_        t)          |dd¦  «        }|€)d|_        t                               d‰j        ¦  «         �n‡t          ‰‰ ¦  «        }t/          ‰ j        ¦  «        Šd„ ‰ j                             ¦   «         D ¦   «         Š
d„ ‰ j                             ¦   «         D ¦   «         Š ||¦  «         ˆfd„‰ j        D ¦   «         |_        ˆ
fd„‰ j                             ¦   «         D ¦   «         |_        ˆfd„‰ j                             ¦   «         D ¦   «         |_        ˆˆ fd„‰ j        D ¦   «         |_         d|_!        t                               dtE          |j        ¦  «        tE          |j        ¦  «        tE          |j        ¦  «        tE          |j         ¦  «        tG          ˆˆ fd„‰ j$        D ¦   «         ¦  «        ¦  «         nS# tJ          $ rF}	tM          |	¦  «        |_        t                               d‰j        |	tN          ¬¦  «         Y d}	~	nd}	~	ww xY w|‰ j(        ‰j        p‰j        <   dS )z?Import a plugin module and call its ``register(ctx)`` function.)r­   z1Loading plugin '%s' (source=%s, kind=%s, path=%s)Nr   rÖ   rü   Ú__r   rb  ú.r*   >   ræ   rà   r½  rÚ   zno register() functionz&Plugin '%s' has no register() functionc                ó4   — i | ]\  }}|t          |¦  «        “ŒS r(   ©r¨  )r‚   ÚhÚcbss      r   ú
<dictcomp>z.PluginManager._load_plugin.<locals>.<dictcomp>™  s1   € ð 'ð 'ð 'Ù$* A s�A•s˜3‘x”xð'ð 'ð 'r!   c                ó4   — i | ]\  }}|t          |¦  «        “ŒS r(   r  )r‚   r§   r  s      r   r  z.PluginManager._load_plugin.<locals>.<dictcomp>œ  s1   € ð %ð %ð %Ù'0 t¨S�D�#˜c™(œ(ð%ð %ð %r!   c                ó   •— g | ]}|‰v¯|‘Œ	S r(   r(   )r‚   ÚtÚ_tools_befores     €r   r  z.PluginManager._load_plugin.<locals>.<listcomp>   s.   ø€ ð +ð +ð +ØØ Ð-Ð-ð Ø-Ð-Ð-r!   c                óh   •— g | ].\  }}t          |¦  «        ‰                     |d ¦  «        k    ¯,|‘Œ/S ©r   ©r¨  rr   )r‚   r  r  Ú_hook_counts_befores      €r   r  z.PluginManager._load_plugin.<locals>.<listcomp>¤  sI   ø€ ð +ð +ð +á˜˜3Ý˜3‘x”xÐ"5×"9Ò"9¸!¸QÑ"?Ô"?Ò?Ð?ð à?Ð?Ð?r!   c                óh   •— g | ].\  }}t          |¦  «        ‰                     |d ¦  «        k    ¯,|‘Œ/S r%  r&  )r‚   r§   r  Ú_mw_counts_befores      €r   r  z.PluginManager._load_plugin.<locals>.<listcomp>©  sI   ø€ ð 0ð 0ð 0á!˜˜cÝ˜3‘x”xÐ"3×"7Ò"7¸¸aÑ"@Ô"@Ò@Ð@ð à@Ð@Ð@r!   c                óf   •— g | ]-}‰j         |                              d ¦  «        ‰j        k    ¯+|‘Œ.S )r…   )r  rr   r_   ©r‚   rc  r­   r½   s     €€r   r  z.PluginManager._load_plugin.<locals>.<listcomp>®  sF   ø€ ð .ð .ð .ØØÔ,¨QÔ/×3Ò3°HÑ=Ô=ÀÄÒNÐNð ØNÐNÐNr!   Tz[  registered: %d tool(s), %d hook(s), %d middleware, %d slash command(s), %d CLI command(s)c              3  ón   •K  — | ]/}‰j         |                              d ¦  «        ‰j        k    ¯+dV — Œ0dS )r…   r¤  N)rø   rr   r_   r+  s     €€r   r„   z-PluginManager._load_plugin.<locals>.<genexpr>¹  sR   øè è € ð ð ØØÔ-¨aÔ0×4Ò4°XÑ>Ô>À(Ä-ÒOÐOð ØOÐOÐOÐOðð r!   zFailed to load plugin '%s': %sr÷  ))r¬   r?   rB   r|   r_   r¤   r§   r¦   r¨   Ú_load_portable_pluginrÙ   r×   r   Úregister_plugin_override_policyÚ
_NS_PARENTr¶   rØ   Ú_load_directory_moduleÚ_load_entrypoint_moduler®   rã   r³   rí   rc   rÛ   r  ru  r‚  r¯   r°   r±   r  r²   rq   r¨  r®  rø   rl   r`   r5   r—  )r½   r­   r³  Ú	_registryÚ
_plugin_idÚ_slugr®   Úregister_fnÚctxrì  r'  r)  r#  s   ``        @@@r   r«  zPluginManager._load_plugino  sù  øøøøø€ å xÐ0Ñ0Ô0ˆÝ�ŠØ?ØŒLÐ)˜HœM¨8¬?¸H¼MÈ8Ì=ñ	
ô 	
ð 	
ð
 Ôð 	Ø×&Ò& x°Ñ8Ô8Ð8ØˆFà8Ð8Ð8Ð8Ð8Ð8Ø”\Ð2 X¤]ˆ
Ø×"Ò" 3¨Ñ-Ô-×5Ò5°c¸3Ñ?Ô?ˆØ×1Ò1ÝÐ#Ð#˜EÐ#Ð#Ý˜( DÑ)Ô)×@Ò@ÀÑDÔDñ	
ô 	
ð 	
ðB	ØŒÐ"@Ð@Ð@Ø×4Ò4°XÑ>Ô>��à×5Ò5°hÑ?Ô?�à"ˆFŒMõ " &¨*°dÑ;Ô;ˆKØÐ"Ø7�”Ý—’ÐGÈÌÑWÔWÐWÑWå# H¨dÑ3Ô3�õ !$ DÔ$;Ñ <Ô <�ð'ð 'Ø.2¬k×.?Ò.?Ñ.AÔ.Að'ñ 'ô 'Ð#ð%ð %Ø48Ô4D×4JÒ4JÑ4LÔ4Lð%ñ %ô %Ð!ð �˜CÑ Ô Ð ð+ð +ð +ð +Ø#Ô6ð+ñ +ô +�Ô'ð+ð +ð +ð +à"&¤+×"3Ò"3Ñ"5Ô"5ð+ñ +ô +�Ô'ð
0ð 0ð 0ð 0à%)Ô%5×%;Ò%;Ñ%=Ô%=ð0ñ 0ô 0�Ô,ð
.ð .ð .ð .ð .Ø#Ô4ð.ñ .ô .�Ô*ð "&�”Ý—’ØqÝ˜Ô/Ñ0Ô0Ý˜Ô/Ñ0Ô0Ý˜Ô4Ñ5Ô5Ý˜Ô2Ñ3Ô3Ýð ð ð ð ð Ø#'Ô#5ðñ ô ñ ô ñ
ô 
ð 
øøõ ð 	ð 	ð 	Ý˜s™8œ8ˆFŒLÝ�NŠNØ0Ø”˜s­^ð ñ ô ð ð ð ð ð ð øøøøð	øøøð 8>ˆŒ�h”lÐ3 h¤mÑ4Ð4Ð4s   Ã.G>K- Ë-
L=Ë7<L8Ì8L=r³  r¬   c                ó~  — |j         p|j        }	 ddlm}  |t	          |j        ¦  «        t          ¦   «         dz  |j        z  ¦  «        }t          || ¦  «        }|j	        D ])}t                               d||j        |j        ¦  «         Œ*|j        D ]h}	 |                     |j        |j        |j        |j        ¦  «         Œ0# t&          $ r,}	t                               d||j        |	¦  «         Y d}	~	Œad}	~	ww xY w|j                             ¦   «         D ]N\  }
}|j        › d|
› �}|| j        v rt                               d||¦  «         Œ7t/          |¦  «        | j        |<   ŒOd	|_        nG# t&          $ r:}	t3          |	¦  «        |_        t                               d
||	¦  «         Y d}	~	nd}	~	ww xY w|| j        |<   dS )zALoad validated portable components without importing Python code.r   )Úload_agent_pluginrÎ  zAgent Plugin '%s' [%s]: %sz(Agent Plugin '%s' skill '%s' skipped: %sNr  z*Agent Plugin '%s' MCP server collision: %sTz$Failed to load Agent Plugin '%s': %s)r|   r_   rÐ  r8  r   r¦   r   r©   r¶   rê  r?   rí   Úscoperå  Úskillsr‘  Úskill_mdrœ   r„  rl   Úmcp_serversru  r™  rs   rq   r`   r³   r—  )r½   r­   r³  r²  r8  Úpackager6  rë  Úskillrì  Úserver_namerm   Úinternal_names                r   r-  z#PluginManager._load_portable_pluginÇ  sP  € ð
 ”\Ð2 X¤]ˆ
ð+	TØBÐBÐBÐBÐBÐBà'Ð'Ý�X”]Ñ#Ô#ÝÑ!Ô! MÑ1°HÔ4LÑLñô ˆGõ   ¨$Ñ/Ô/ˆCØ%Ô1ð ð �
Ý—’Ø0ØØÔ$ØÔ&ñ	ô ð ð ð !œð ð �ðØ×&Ò&Øœ
ØœØÔ)ØÔ)ñ	ô ð ð øõ !ð ð ð Ý—N’NØBØ"Øœ
Øñ	ô ð ð ð ð ð ð øøøøðøøøð (/Ô':×'@Ò'@Ñ'BÔ'Bð 	Ið 	IÑ#�˜VØ#+Ô#;Ð LÐ L¸{Ð LÐ L�Ø  DÔ$>Ð>Ð>Ý—N’NØDØ"Ø%ñô ð ð
 Ý<@À¹L¼L�Ô*¨=Ñ9Ð9Ø!ˆFŒNˆNøÝð 	Tð 	Tð 	TÝ˜s™8œ8ˆFŒLÝ�NŠNÐAÀ:ÈsÑSÔSÐSÐSÐSÐSÐSÐSøøøøð	Tøøøð %+ˆŒ�jÑ!Ð!Ð!sC   �BE, Â,CÃE, Ã
C9Ã"C4Ã/E, Ã4C9Ã9A2E, Å,
F0Å60F+Æ+F0útypes.ModuleTypec                ó  — t          |j        ¦  «        }|dz  }|                     ¦   «         st          d|› �¦  «        ‚t          t
          j        vr@t          j        t          ¦  «        }g |_	        t          |_
        |t
          j        t          <   |j        p|j        }|                     dd¦  «                             dd¦  «        }t          › d|› �}t          j                             ||t#          |¦  «        g¬¦  «        }|�|j        €t'          d
|› �¦  «        ‚t          j                             |¦  «        }	||	_
        t#          |¦  «        g|	_	        |	t
          j        |<   |j                             |	¦  «         |	S )a/  Import a directory-based plugin as ``hermes_plugins.<slug>``.

        The module slug is derived from ``manifest.key`` so category-namespaced
        plugins (``image_gen/openai``) import as
        ``hermes_plugins.image_gen__openai`` without colliding with any
        future ``tts/openai``.
        rò  zNo __init__.py in rü   r  r   rb  r  )Úsubmodule_search_locationsNzCannot create module spec for )r   r¦   rŒ  r�  r/  r9   ÚmodulesÚtypesÚ
ModuleTypeÚ__path__Ú__package__r|   r_   r   r	  ÚutilÚspec_from_file_locationr`   ÚloaderÚImportErrorÚmodule_from_specÚexec_module)
r½   r­   rî  rü  Úns_pkgr|   r�   Úmodule_nameÚspecr®   s
             r   r0  z$PluginManager._load_directory_moduleû  sn  € õ ˜(œ-Ñ(Ô(ˆ
Ø Ñ.ˆ	Ø×ÒÑ!Ô!ð 	GÝ#Ð$E¸Ð$EÐ$EÑFÔFÐFõ �Sœ[Ð(Ð(ÝÔ%¥jÑ1Ô1ˆFØ ˆFŒOÝ!+ˆFÔØ&,�CŒK�
Ñ#àŒlÐ+˜hœmˆØ�{Š{˜3 Ñ%Ô%×-Ò-¨c°3Ñ7Ô7ˆÝ#Ð,Ð, dÐ,Ð,ˆÝŒ~×5Ò5ØØÝ(+¨J©¬Ð'8ð 6ñ 
ô 
ˆð
 ˆ<˜4œ;Ð.ÝÐJ¸yÐJÐJÑKÔKÐKå”×0Ò0°Ñ6Ô6ˆØ(ˆÔÝ˜z™?œ?Ð+ˆŒØ#)�Œ�KÑ ØŒ×Ò Ñ'Ô'Ð'Øˆr!   c                ó¦  — t           j                             ¦   «         }t          |d¦  «        r|                     t
          ¬¦  «        }n=t          |t          ¦  «        r|                     t
          g ¦  «        }nd„ |D ¦   «         }|D ](}|j	        |j	        k    r| 
                    ¦   «         c S Œ)t          d|j	        › dt
          › d�¦  «        ‚)z:Load a pip-installed plugin via its entry-point reference.rÿ  r   c                ó2   — g | ]}|j         t          k    ¯|‘ŒS r(   r  r  s     r   r  z9PluginManager._load_entrypoint_module.<locals>.<listcomp>)  s%   € ÐLÐLÐL ¨R¬XÕ9KÒ-KÐ-K˜Ð-KÐ-KÐ-Kr!   zEntry point 'z' not found in group 'rj  )r	  r
  r  r  rÿ  r  rj   rs   rr   r_   ÚloadrL  )r½   r­   r  r  r  s        r   r1  z%PluginManager._load_entrypoint_module!  sß   € åÔ ×-Ò-Ñ/Ô/ˆÝ�3˜Ñ!Ô!ð 	MØŸ
š
Õ);˜
Ñ<Ô<ˆIˆIÝ˜�TÑ"Ô"ð 	MØŸšÕ 2°BÑ7Ô7ˆIˆIàLÐL cÐLÑLÔLˆIàð 	!ð 	!ˆBØŒw˜(œ-Ò'Ð'Ø—w’w‘y”yÐ Ð Ð ð (õ ØV˜HœMÐVÐVÕASÐVÐVÐVñ
ô 
ð 	
r!   r|  r
  r   ú	List[Any]c                ó`  — |                      dt          ¦  «         | j                             |g ¦  «        }g }|D ]r}	  |di |¤Ž}|�|                     |¦  «         Œ## t
          $ rC}t                               d|t          |dt          |¦  «        ¦  «        |¦  «         Y d}~Œkd}~ww xY w|S )u   Call all registered callbacks for *hook_name*.

        Each callback is wrapped in its own try/except so a misbehaving
        plugin cannot break the core agent loop.

        Returns a list of non-``None`` return values from callbacks.

        For ``pre_llm_call``, callbacks may return a dict describing
        context to inject into the current turn's user message::

            {"context": "recalled text..."}
            "recalled text..."          # plain string, equivalent

        Context is ALWAYS injected into the user message, never the
        system prompt.  This preserves the prompt cache prefix â€” the
        system prompt stays identical across turns so cached tokens
        are reused.  All injected context is ephemeral â€” never
        persisted to session DB.
        Útelemetry_schema_versionNz Hook '%s' callback %s raised: %sr$   r(   )
rS  r   r  rr   r\  rl   r?   rí   rã   Úrepr)r½   r|  r
  Ú	callbacksÚresultsÚcbÚretrì  s           r   Úinvoke_hookzPluginManager.invoke_hook7  sæ   € ð( 	×ÒÐ4Õ6MÑNÔNÐNØ”K—O’O I¨rÑ2Ô2ˆ	ØˆØð 	ð 	ˆBð
Ø�b�l�l˜6�l�l�Ø�?Ø—N’N 3Ñ'Ô'Ð'øøÝð ð ð Ý—’Ø6ØÝ˜B 
­D°©H¬HÑ5Ô5Øñ	ô ð ð ð ð ð ð øøøøðøøøð ˆs   ¾AÁ
B+Á(9B&Â&B+c                óP   — t          | j                             |¦  «        ¦  «        S )z@Return True when at least one callback is registered for a hook.)r0   r  rr   )r½   r|  s     r   Úhas_hookzPluginManager.has_hook\  s   € å�D”K—O’O IÑ.Ô.Ñ/Ô/Ð/r!   r§   c                óP   — t          | j                             |¦  «        ¦  «        S )zDReturn True when at least one callback is registered for middleware.)r0   r‚  rr   )r½   r§   s     r   Úhas_middlewarezPluginManager.has_middleware`  s!   € å�DÔ$×(Ò(¨Ñ.Ô.Ñ/Ô/Ð/r!   c                ó*  — | j                              |g ¦  «        }g }|D ]r}	  |di |¤Ž}|�|                     |¦  «         Œ## t          $ rC}t                               d|t          |dt          |¦  «        ¦  «        |¦  «         Y d}~Œkd}~ww xY w|S )a	  Call registered middleware callbacks for *kind*.

        Each callback is isolated so one plugin cannot break the base runtime
        path. Middleware that wants to change behavior must return the shape
        documented by the caller-specific contract.
        Nz&Middleware '%s' callback %s raised: %sr$   r(   )r‚  rr   r\  rl   r?   rí   rã   rX  )r½   r§   r
  rY  rZ  r[  r\  rì  s           r   Úinvoke_middlewarezPluginManager.invoke_middlewared  sÑ   € ð Ô$×(Ò(¨¨rÑ2Ô2ˆ	ØˆØð 	ð 	ˆBð
Ø�b�l�l˜6�l�l�Ø�?Ø—N’N 3Ñ'Ô'Ð'øøÝð ð ð Ý—’Ø<ØÝ˜B 
­D°©H¬HÑ5Ô5Øñ	ô ð ð ð ð ð ð øøøøðøøøð ˆs   £AÁ
BÁ9BÂBúList[tuple]c                ó*   — t          | j        ¦  «        S )aa  Return the list of plugin-registered Slack action handlers.

        Each entry is a ``(action_id, callback, plugin_name)`` tuple.
        Consumed by the Slack adapter at connect time to wire callbacks
        into its ``slack_bolt.AsyncApp``.

        Plugins register handlers via
        :meth:`PluginContext.register_slack_action_handler`.
        )rk   r[  rš  s    r   Úget_slack_action_handlersz'PluginManager.get_slack_action_handlers  s   € õ �DÔ/Ñ0Ô0Ð0r!   úList[Dict[str, Any]]c                óÚ  — g }t          | j                             ¦   «         ¦  «        D ]Á\  }}|                     |j        j        |j        j        p|j        j        |j        j        |j        j        |j        j	        |j        j
        |j        t          |j        ¦  «        t          |j        ¦  «        t          |j        ¦  «        t          |j        ¦  «        |j        dœ¦  «         ŒÂ|S )z7Return a list of info dicts for all discovered plugins.)r_   r|   r§   r›   rœ   r¤   rq   ÚtoolsÚhooksÚ
middlewareÚcommandsr³   )r~  r—  ru  r\  r­   r_   r|   r§   r›   rœ   r¤   rq   r¨  r¯   r°   r±   r²   r³   )r½   Úresultr|   r³  s       r   Úlist_pluginszPluginManager.list_plugins�  sØ   € à')ˆÝ! $¤-×"5Ò"5Ñ"7Ô"7Ñ8Ô8ð 	ð 	‰KˆC�Ø�MŠMà"œOÔ0Ø!œ?Ô.ÐF°&´/Ô2FØ"œOÔ0Ø%œÔ6Ø#)¤?Ô#>Ø$œoÔ4Ø%œ~Ý  Ô!8Ñ9Ô9Ý  Ô!8Ñ9Ô9Ý"% fÔ&BÑ"CÔ"CÝ # FÔ$>Ñ ?Ô ?Ø#œ\ðð ñô ð ð ð  ˆr!   Úqualified_nameúOptional[Path]c                óN   — | j                              |¦  «        }|r|d         ndS )z>Return the ``Path`` to a plugin skill's SKILL.md, or ``None``.r¦   N)rŽ  rr   )r½   ro  rå   s      r   Úfind_plugin_skillzPluginManager.find_plugin_skill©  s+   € àÔ#×'Ò'¨Ñ7Ô7ˆØ %Ð/ˆu�VŒ}ˆ}¨4Ð/r!   rQ  r¡   c                ór   ‡— |› d�Št          ˆfd„| j                             ¦   «         D ¦   «         ¦  «        S )zCReturn sorted bare names of all skills registered by *plugin_name*.rˆ  c              3  óX   •K  — | ]$\  }}|                      ‰¦  «        ¯|d          V — Œ%dS )r‰  N)Ú
startswith)r‚   Úqnr"  rÚ  s      €r   r„   z3PluginManager.list_plugin_skills.<locals>.<genexpr>±  sQ   øè è € ð 
ð 
á��AØ�}Š}˜VÑ$Ô$ð
ØˆkŒNð
ð 
ð 
ð 
ð 
ð 
r!   ©r~  rŽ  ru  )r½   rQ  rÚ  s     @r   Úlist_plugin_skillsz PluginManager.list_plugin_skills®  sX   ø€ àÐ"Ð"Ð"ˆÝð 
ð 
ð 
ð 
àÔ,×2Ò2Ñ4Ô4ð
ñ 
ô 
ñ 
ô 
ð 	
r!   c                ób   — d„ t          | j                             ¦   «         ¦  «        D ¦   «         S )zDReturn progressive-disclosure metadata for registered plugin skills.c                ó¦   — g | ]N\  }}|t          |                     d d¦  «        ¦  «        dt          |                     di ¦  «        ¦  «        dœ‘ŒOS )rœ   r*   r…   r„  )r_   rœ   Úcategoryr„  )r`   rr   rs   )r‚   r�  rå   s      r   r  z<PluginManager.list_plugin_skill_metadata.<locals>.<listcomp>º  sm   € ð 
ð 
ð 
ñ !�	˜5ð "Ý" 5§9¢9¨]¸BÑ#?Ô#?Ñ@Ô@Ø$Ý# E§I¢I¨m¸RÑ$@Ô$@ÑAÔAð	ð ð
ð 
ð 
r!   rw  rš  s    r   Úlist_plugin_skill_metadataz(PluginManager.list_plugin_skill_metadata·  s<   € ð
ð 
õ %+¨4Ô+>×+DÒ+DÑ+FÔ+FÑ$GÔ$Gð
ñ 
ô 
ð 	
r!   úDict[str, Dict[str, Any]]c                óH   — d„ | j                              ¦   «         D ¦   «         S )z?Return a defensive copy of enabled portable MCP server configs.c                ó4   — i | ]\  }}|t          |¦  «        “ŒS r(   )rs   )r‚   r_   rm   s      r   r  z:PluginManager.get_portable_mcp_servers.<locals>.<dictcomp>Ç  s4   € ð 
ð 
ð 
á��fð •$�v‘,”,ð
ð 
ð 
r!   )r™  ru  rš  s    r   Úget_portable_mcp_serversz&PluginManager.get_portable_mcp_serversÄ  s2   € ð
ð 
à $Ô :× @Ò @Ñ BÔ Bð
ñ 
ô 
ð 	
r!   c                ó*   — t          | j        ¦  «        S r•   )r0   r™  rš  s    r   Úhas_portable_mcp_serversz&PluginManager.has_portable_mcp_serversÌ  s   € Ý�DÔ.Ñ/Ô/Ð/r!   c                ó<   — | j                              |d¦  «         dS )z>Remove a stale registry entry (silently ignores missing keys).N)rŽ  Úpop)r½   ro  s     r   Úremove_plugin_skillz!PluginManager.remove_plugin_skillÏ  s!   € àÔ×Ò °Ñ5Ô5Ð5Ð5Ð5r!   r’  ©F©r/   r0   r   r1   )r   rµ  ©rÆ  rÇ  r   r0   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   r1   )r­   rš   r³  r¬   r   r1   )r­   rš   r   rA  ©r|  r`   r
  r   r   rU  ©r|  r`   r   r0   ©r§   r`   r   r0   ©r§   r`   r
  r   r   rU  ©r   rd  ©r   rg  )ro  r`   r   rp  )rQ  r`   r   r¡   )r   r}  )r   r0   )ro  r`   r   r1   )r$   r%   r&   r'   r¾   r   rž  r¦  rÖ  r¾  rÜ  rã  r§  r  r¬  r«  r-  r0  r1  r]  r_  ra  rc  rf  rn  rr  rx  r|  r€  r‚  r…  r(   r!   r   r•  r•    s`  € € € € € ØEÐEð6ð 6ð 6ð 6ð:%ð %ð %ð %ð %ðNrð rð rð rðh1ð 1ð 1ð 1ðf6ð 6ð 6ð 6ð@ *.ð	
ð 
ð 
ð 
ð 
ð2Tð Tð Tð TðlYð Yð Yð Yð~ð ð ð ð<ð ð ð ð&'(ð '(ð '(ð '(ðRV>ð V>ð V>ð V>ðp2+ð 2+ð 2+ð 2+ðh$ð $ð $ð $ðL
ð 
ð 
ð 
ð,#ð #ð #ð #ðJ0ð 0ð 0ð 0ð0ð 0ð 0ð 0ðð ð ð ð6
1ð 
1ð 
1ð 
1ð ð ð ð ð40ð 0ð 0ð 0ð

ð 
ð 
ð 
ð
ð 
ð 
ð 
ð
ð 
ð 
ð 
ð0ð 0ð 0ð 0ð6ð 6ð 6ð 6ð 6ð 6r!   r•  zOptional[PluginManager]Ú_plugin_managerc                 ó:   — t           €t          ¦   «         a t           S )z>Return (and lazily create) the global PluginManager singleton.)r�  r•  r(   r!   r   Úget_plugin_managerr‘  Û  s   € õ ÐÝ'™/œ/ˆÝÐr!   rÆ  rÇ  c                óD   — t          ¦   «                              | ¦  «        S )zåReturn whether config enables a portable package with MCP servers.

    A fresh manager performs manifest-only scanning, so this startup gate does
    not mutate the process-wide plugin registry or import native plugin code.
    )r•  rÖ  )rÆ  s    r   Úhas_enabled_agent_plugin_mcpr“  ã  s   € õ ‰?Œ?×3Ò3°JÑ?Ô?Ð?r!   c                óJ   — t          ¦   «                              | ¬¦  «         dS )z¤Discover and load all plugins.

    Default behavior is idempotent. Pass ``force=True`` to rescan plugin
    manifests and reload state in the current process.
    ©r/   N©r‘  r   r•  s    r   Údiscover_pluginsr—  ì  s'   € õ ÑÔ×*Ò*°Ð*Ñ7Ô7Ð7Ð7Ð7r!   r|  r
  r   rU  c                ó6   —  t          ¦   «         j        | fi |¤ŽS )zxInvoke a lifecycle hook on loaded plugins.

    Returns a list of non-``None`` return values from plugin callbacks.
    )r‘  r]  )r|  r
  s     r   r]  r]  õ  s&   € ð
 ,ÕÑÔÔ+¨IÐ@Ð@¸Ð@Ð@Ð@r!   r§   c                ó6   —  t          ¦   «         j        | fi |¤ŽS )zyInvoke registered middleware callbacks.

    Returns a list of non-``None`` return values from middleware callbacks.
    )r‘  rc  )r§   r
  s     r   rc  rc  ý  s&   € ð
 2ÕÑÔÔ1°$ÐAÐA¸&ÐAÐAÐAr!   c                óð   — t          ¦   «         }t          |dd¦  «        }t          |¦  «        rt           || ¦  «        ¦  «        S t          t          |di ¦  «                             | ¦  «        ¦  «        S )zBReturn True when middleware callbacks are registered for ``kind``.ra  Nr‚  )r‘  rã   rZ  r0   rr   )r§   r·   Úmethods      r   ra  ra  	  sm   € å Ñ"Ô"€GÝ�WÐ.°Ñ5Ô5€FÝ�ÑÔð "Ý�F�F˜4‘L”LÑ!Ô!Ð!Ý•˜ °Ñ3Ô3×7Ò7¸Ñ=Ô=Ñ>Ô>Ð>r!   c                óD   — t          ¦   «                              | ¦  «        S )z0Return True when a loaded plugin handles a hook.)r‘  r_  )r|  s    r   r_  r_  	  s   € åÑÔ×(Ò(¨Ñ3Ô3Ð3r!   T)Úfrozenc                  ó:   — e Zd ZU dZded<   dZded<   dZded<   dS )Ú_PreToolCallDirectiveNr¥   Úactionrå  Úrule_key)r$   r%   r&   r   rª   rå  r¡  r(   r!   r   rŸ  rŸ  	  sE   € € € € € € à €FÐ Ð Ð Ñ Ø!€GÐ!Ð!Ð!Ñ!Ø"€HÐ"Ð"Ð"Ñ"Ð"Ð"r!   rŸ  ú>Tool '{tool_name}' denied: not in this thread's tool whitelistÚallowedr×  Údeny_msg_fmtc                ó6   — | t           _        |t           _        d S r•   )Ú_thread_tool_whitelistr£  Úfmt)r£  r¤  s     r   Úset_thread_tool_whitelistr¨  	  s   € ð &-ÕÔ"Ø!-ÕÔÐÐr!   c                 ó   — d t           _        d S r•   )r¦  r£  r(   r!   r   Úclear_thread_tool_whitelistrª  %	  s   € Ø%)ÕÔ"Ð"Ð"r!   rÞ   r  r_  Útask_idÚ
session_idÚtool_call_idÚturn_idÚapi_request_idÚmiddleware_traceúOptional[List[Dict[str, Any]]]c                óæ  — t          t          dd¦  «        }|�?| |vr;t          t          dd¦  «        }	t          d|	                     | ¬¦  «        ¬¦  «        S dd	lm}
  |
d
| t          |t          ¦  «        r|ni |||||t          |pg ¦  «        ¬¦	  «	        }|D ]Ê}t          |t          ¦  «        sŒ| 	                    d¦  «        }|dvrŒ2| 	                    d¦  «        }t          |t          ¦  «        r|r|nd}|dk    r|sŒk|dk    r| 	                    d¦  «        nd}t          |t          ¦  «        r|                     ¦   «         nd}|sd}t          |||¬¦  «        c S t          ¦   «         S )u@  Check ``pre_tool_call`` hooks for a blocking or approval directive.

    Plugins that need to enforce policy (rate limiting, security
    restrictions, approval workflows) can return one of::

        {"action": "block",   "message": "Reason the tool was blocked"}
        {"action": "approve", "message": "Why this needs human confirmation"}
        {"action": "approve", "message": "...", "rule_key": "write_file:ssh"}

    from their ``pre_tool_call`` callback.

    - ``block`` vetoes the tool call outright (the message becomes the tool
      result the model sees).
    - ``approve`` ESCALATES to the existing human-approval gate
      (``prompt_dangerous_approval`` on CLI, the approval callback on the
      gateway) â€” the same mechanism Tier-2 dangerous shell patterns use.
      This lets a plugin require a human ``[o]nce/[s]ession/[a]lways/[d]eny``
      decision on ANY tool, not just terminal command strings. The caller is
      responsible for invoking the gate (see
      :func:`tools.approval.request_tool_approval`).
    - ``rule_key`` is optional and only honored for ``approve`` directives. It
      lets plugins choose the allowlist grain for `[a]lways` approvals.

    The first valid directive wins. Invalid or irrelevant hook return values
    are silently ignored so existing observer-only hooks are unaffected.
    r£  Nr§  zTool '{tool_name}' deniedÚblock)rÞ   )r   rå  r   )r]  rH   )rÞ   r  r«  r¬  r­  r®  r¯  r°  r   )r³  Úapproverå  r´  r¡  )r   rå  r¡  )rã   r¦  rŸ  r­  Úhermes_cli.lifecycler]  rj   rs   rk   rr   r`   r3   )rÞ   r  r«  r¬  r­  r®  r¯  r°  r£  r§  Úinvoke_lifecycle_hookÚhook_resultsrm  r   rå  r¡  s                   r   Ú$_get_pre_tool_call_directive_detailsr¸  )	  sÏ  € õH Õ,¨i¸Ñ>Ô>€GØÐ˜y°Ð7Ð7ÝÕ,¨eÐ5PÑQÔQˆÝ$ØØ—J’J¨�JÑ3Ô3ð
ñ 
ô 
ð 	
ð
 JÐIÐIÐIÐIÐIà(Ð(ØØÝ ¥dÑ+Ô+Ð3ˆTˆT°ØØØ!ØØ%ÝÐ.Ð4°"Ñ5Ô5ð
ñ 
ô 
€Lð ð Xð XˆÝ˜&¥$Ñ'Ô'ð 	ØØ—’˜HÑ%Ô%ˆØÐ-Ð-Ð-ØØ—*’*˜YÑ'Ô'ˆÝ'¨µÑ5Ô5ÐK¸'ÐK�'�'Àtˆð �WÒÐ WÐØØ-3°yÒ-@Ð-@�6—:’:˜jÑ)Ô)Ð)ÀdˆÝ'1°(½CÑ'@Ô'@ÐJ�8—>’>Ñ#Ô#Ð#ÀdˆØð 	ØˆHÝ$¨F¸GÈhÐWÑWÔWÐWÐWÐWå Ñ"Ô"Ð"r!   ú#tuple[Optional[str], Optional[str]]c           
     óL   — t          | |||||||¬¦  «        }|j        |j        fS )aD  Check ``pre_tool_call`` hooks for a blocking or approval directive.

    Backward-compatible public helper: returns ``(directive, message)`` where
    ``directive`` is ``"block"``, ``"approve"``, or ``None``. Internal callers
    that need approve-specific metadata use
    :func:`_get_pre_tool_call_directive_details`.
    ©r«  r¬  r­  r®  r¯  r°  )r¸  r   rå  )	rÞ   r  r«  r¬  r­  r®  r¯  r°  Údetailss	            r   Úget_pre_tool_call_directiver½  x	  s=   € õ" 3Ø�4 °ZØ!¨7Ø%Ð8Hðñ ô €Gð
 ŒN˜GœOÐ,Ð,r!   r¥   c           
     óJ   — t          | |||||||¬¦  «        \  }}	|dk    r|	ndS )aZ  Back-compat shim: return only a ``block`` message (or ``None``).

    Deprecated in favor of :func:`get_pre_tool_call_directive`, which also
    surfaces the ``approve`` escalation directive. Kept so any external caller
    importing the old name keeps working; ``approve`` directives are invisible
    to this shim (it only reports blocks).
    r»  r³  N)r½  )
rÞ   r  r«  r¬  r­  r®  r¯  r°  Ú	directiverå  s
             r   Úget_pre_tool_call_block_messagerÀ  ‘	  sG   € õ" 5Ø�4 °ZØ!¨7Ø%Ð8Hðñ ô Ñ€Iˆwð
   7Ò*Ð*ˆ7ˆ7°Ð4r!   c           
     ó,  — t          | |||||||¬¦  «        }|j        dk    r|j        S |j        dk    rß	 ddlm}	m}
m} d}	  |||¬¦  «        }n# t          $ r Y nw xY w	  |	| |j        pd|j        p| ¬	¦  «        }|�	  |
|¦  «         n5# t          $ r Y n)w xY wn$# |�	  |
|¦  «         w # t          $ r Y w w xY ww xY wn# t          $ r d
| › �cY S w xY w| 	                    d¦  «        s't          | 	                    d¦  «        pd| › �¦  «        S dS )aÝ  Resolve the pre_tool_call directive to a final block message (or None).

    Single entry point for every tool-dispatch site: fetches the plugin
    directive and, for an ``approve`` escalation, invokes the human-approval
    gate (:func:`tools.approval.request_tool_approval`). Returns the message
    the tool result should carry when the call is blocked, or ``None`` when
    the call may proceed.

    Centralizing this keeps the security-critical fail-closed logic in ONE
    place instead of copy-pasted across the concurrent/sequential/helper
    dispatch paths: an ``approve`` directive whose gate errors, denies, or
    times out is fail-closed to a block; ``block`` blocks with its message;
    anything else proceeds.
    r»  r³  r´  r   )Úrequest_tool_approvalÚ#reset_current_observability_contextÚ!set_current_observability_contextN)r®  r­  r*   )r¡  z)BLOCKED: plugin approval gate failed for Úapprovedrå  z&BLOCKED: plugin approval required for )r¸  r   rå  Útools.approvalrÂ  rÃ  rÄ  rl   r¡  rr   r`   )rÞ   r  r«  r¬  r­  r®  r¯  r°  r¼  rÂ  rÃ  rÄ  Úapproval_tokensrm  s                 r   Úresolve_pre_tool_blockrÈ  ª	  s  € õ0 3Ø�4 °ZØ!¨7Ø%Ð8Hðñ ô €Gð
 „~˜Ò Ð ØŒÐØ„~˜Ò"Ð"ð	Kðð ð ð ð ð ð ð ð ð ð #ˆOðØ"CÐ"CØ#Ø!-ð#ñ #ô #��øõ ð ð ð Ø�ðøøøðØ.Ð.ØØ”OÐ) rØ$Ô-Ð:°ðñ ô �ð #Ð.ðØ;Ð;¸OÑLÔLÐLÐLøÝ$ð ð ð Ø˜ðøøøð /ø�?Ð.ðØ;Ð;¸OÑLÔLÐLÐLøÝ$ð ð ð Ø˜ðøøøð /øøøøøõ
 ð 	Kð 	Kð 	Kð K¸yÐJÐJÐJÐJÐJð	Køøøð �zŠz˜*Ñ%Ô%ð 	ÝØ—
’
˜9Ñ%Ô%ð HØG¸IÐGÐGñô ð ð ˆ4s•   ¶C ÁA ÁC Á
AÁC ÁAÁC Á"B Á>C ÂB ÂC Â
BÂC ÂBÂC ÂB?Â"B.Â-B?Â.
B;Â8B?Â:B;Â;B?Â?C ÃCÃC©r¬  rw   rm  ÚcodingÚattemptÚfinal_responseÚchanged_pathsrw   rm  rÊ  rË  rÝ  rÌ  rÍ  úOptional[List[str]]c                ó  — t          d| |||||t          |pg ¦  «        ¬¦  «        }|D ]ã}t          |t          ¦  «        sŒt	          |                     d¦  «        p|                     d¦  «        pd¦  «                             ¦   «                              ¦   «         }	|	dvrŒz|                     d¦  «        p|                     d¦  «        }
t          |
t          ¦  «        r*|
                     ¦   «         r|
                     ¦   «         c S Œäd	S )
u9  Check user ``pre_verify`` hooks for a directive to keep the agent going.

    Fired once per turn when the agent edited code and is about to verify/finish.
    A hook keeps the turn going (run a check, defer it, tidy the diff) by
    returning::

        {"action": "continue", "message": "<follow-up for the model>"}

    The Claude-Code Stop shape ``{"decision": "block", "reason": "..."}`` (block
    the stop == keep going) is accepted too. The first directive carrying a
    non-empty message wins; any other return lets the turn finish. Mirrors
    :func:`get_pre_tool_call_block_message` â€” the call site stays a one-liner.

    ``coding`` / ``attempt`` let a hook scope itself (``if not coding`` â€¦) and
    self-throttle (``if attempt`` â€¦), the same way a ``pre_tool_call`` hook
    scopes on ``tool_name``.
    rE   rÉ  r   Údecisionr*   )Úcontinuer³  rå  ÚreasonN)r]  rk   rj   rs   r`   rr   r3   r4   )r¬  rw   rm  rÊ  rË  rÌ  rÍ  r·  rm  r   rå  s              r   Úget_pre_verify_continue_messagerÓ  ñ	  s  € õ6 ØØØØØØØ%Ý˜=Ð.¨BÑ/Ô/ð	ñ 	ô 	€Lð ð #ð #ˆÝ˜&¥$Ñ'Ô'ð 	ØÝ�V—Z’Z Ñ)Ô)ÐI¨V¯ZªZ¸
Ñ-CÔ-CÐIÀrÑJÔJ×PÒPÑRÔR×XÒXÑZÔZˆØÐ.Ð.Ð.ØØ—*’*˜YÑ'Ô'Ð?¨6¯:ª:°hÑ+?Ô+?ˆÝ�g�sÑ#Ô#ð 	#¨¯ª©¬ð 	#Ø—=’=‘?”?Ð"Ð"Ð"øàˆ4r!   c                óN   — t          ¦   «         }|                     | ¬¦  «         |S )z‚Return the global manager after ensuring plugin discovery has run.

    Pass ``force=True`` to rescan in the current process.
    r•  r–  )r/   r·   s     r   Ú_ensure_plugins_discoveredrÕ  $
  s+   € õ
 !Ñ"Ô"€GØ×Ò EÐÑ*Ô*Ð*Ø€Nr!   c                 ó(   — t          ¦   «         j        S )z5Return the plugin-registered context engine, or None.)rÕ  r  r(   r!   r   Úget_plugin_context_enginer×  .
  s   € å%Ñ'Ô'Ô7Ð7r!   úOptional[Callable]c                óf   — t          ¦   «         j                             | ¦  «        }|r|d         ndS )zFReturn the handler for a plugin-registered slash command, or ``None``.rC   N)rÕ  r  rr   )r_   rå   s     r   Úget_plugin_command_handlerrÚ  3
  s3   € å&Ñ(Ô(Ô9×=Ò=¸dÑCÔC€EØ$Ð.ˆ5�ÔÐ¨$Ð.r!   g      >@rm  c                óè  ‡ ‡‡‡— t          j        ‰ ¦  «        s‰ S 	 t          j        ¦   «          n$# t          $ r t          j        ‰ ¦  «        cY S w xY wi Ši Št          j        ¦   «         Šdˆˆˆˆ fd„}t          j        |dd¬¦  «        }| 	                    ¦   «          ‰ 
                    t          ¬¦  «        st          dt          d	›d
�¦  «        ‚d‰v r‰d         ‚‰                     d¦  «        S )aî  Resolve a plugin command return value, awaiting async handlers when needed.

    Sync CLI/TUI dispatch sites call plugin handlers from plain functions.
    If a handler is async, await it directly when no loop is running; if
    we're already inside an active loop, run it in a helper thread with its
    own loop so the caller still gets a concrete result synchronously. The
    threaded path is bounded by a 30s timeout so a hung async handler cannot
    wedge the terminal indefinitely.
    r   r1   c                 óÌ   •— 	 t          j        ‰¦  «        ‰d<   n# t          $ r} | ‰d<   Y d } ~ nd } ~ ww xY w‰                     ¦   «          d S # ‰                     ¦   «          w xY w)Nr�   rì  )ÚasyncioÚrunrŸ  rc   )rì  ÚdoneÚfailureÚoutcomerm  s    €€€€r   Ú_runnerz.resolve_plugin_command_result.<locals>._runnerR
  sv   ø€ ð	Ý&œ{¨6Ñ2Ô2ˆG�GÑÐøÝð 	!ð 	!ð 	!Ø ˆG�E‰NˆNˆNˆNˆNˆNøøøøð	!øøøð �HŠH‰JŒJˆJˆJˆJøˆD�HŠH‰JŒJˆJˆJøøøs%   ƒ šA ›
4¥/ªA ¯4´A ÁA#zhermes-plugin-command-awaitT)Útargetr_   Údaemon)rp  z5Plugin command async handler did not complete within z.0fÚsrì  r�   r’  )ÚinspectÚisawaitablerÝ  Úget_running_looprÒ  rÞ  Ú	threadingÚEventÚThreadÚstartÚwaitÚ"_PLUGIN_COMMAND_AWAIT_TIMEOUT_SECSÚTimeoutErrorrr   )rm  râ  Úthreadrß  rà  rá  s   `  @@@r   Úresolve_plugin_command_resultrñ  <
  sK  øøøø€ õ Ô˜vÑ&Ô&ð Øˆð#ÝÔ Ñ"Ô"Ð"Ð"øÝð #ð #ð #ÝŒ{˜6Ñ"Ô"Ð"Ð"Ð"ð#øøøð !€GØ(*€GÝŒ?ÑÔ€Dðð ð ð ð ð ð ð ð õ ÔØØ*Øðñ ô €Fð
 ‡L‚L�N„N€NØ�9Š9Õ?ˆ9Ñ@Ô@ð 
Ýð9Ý1Ð8ð9ð 9ð 9ñ
ô 
ð 	
ð �ÐÐØ�eŒnÐØ�;Š;�wÑÔÐs   œ0 °AÁAúDict[str, dict]c                 ó(   — t          ¦   «         j        S )uÒ   Return the full plugin commands dict (name â†’ {handler, description, plugin}).

    Triggers idempotent plugin discovery so callers can use plugin commands
    before any explicit discover_plugins() call.
    )rÕ  r  r(   r!   r   Úget_plugin_commandsrô  j
  s   € õ &Ñ'Ô'Ô8Ð8r!   rg  c                 ó`   ‡ — t          ¦   «         Š ˆ fd„t          ‰ j        ¦  «        D ¦   «         S )a¯  Return all plugin-registered auxiliary tasks as a stable-ordered list.

    Each entry is the registration dict from
    :meth:`PluginContext.register_auxiliary_task`:
    ``{key, display_name, description, defaults, plugin}``.

    Triggers idempotent plugin discovery so callers can read the registry
    before any explicit ``discover_plugins()`` call. Sorted by ``key`` for
    deterministic ordering in pickers and tests.
    c                ó*   •— g | ]}‰j         |         ‘ŒS r(   )rt  )r‚   rf  r·   s     €r   r  z.get_plugin_auxiliary_tasks.<locals>.<listcomp>
  s!   ø€ ÐFÐFÐF aˆGÔ˜qÔ!ÐFÐFÐFr!   )rÕ  r~  rt  )r·   s   @r   Úget_plugin_auxiliary_tasksr÷  s
  s5   ø€ õ )Ñ*Ô*€GØFÐFÐFÐF­6°'Ô2DÑ+EÔ+EÐFÑFÔFÐFr!   rd  c                 óD  — t          ¦   «         } | j        sg S 	 ddlm} n# t          $ r g cY S w xY wi }i }| j        D ]O}|                     |¦  «        }|sŒ|j        }|                     |g ¦  «                             |j	        ¦  «         ŒP| j
                             ¦   «         D ]J\  }}|j        D ]=}|                     |¦  «        }|r$|j        |v r|                     |j        |¦  «         Œ>ŒKg }	t          |¦  «        D ]�}
|                     |
¦  «        }d|
                     dd¦  «                             ¦   «         › �}|r|j        j        r|j        j        }n(d                     t          ||
         ¦  «        ¦  «        }|	                     |
||f¦  «         Œž|	S )zÕReturn plugin toolsets as ``(key, label, description)`` tuples.

    Used by the ``hermes tools`` TUI so plugin-provided toolsets appear
    alongside the built-in ones and can be toggled on/off per platform.
    r   rÖ   u   ðŸ”Œ rb  rý   r“   )r‘  rÛ   rÙ   r×   rl   Ú	get_entryrÍ   rS  r\  r_   r—  ru  r¯   r~  rr   r   Útitler­   rœ   rˆ   )r·   r×   Útoolset_toolsÚtoolset_pluginrÞ   rå   Útsrg  r³  rm  Úts_keyr…   rH  Údescs                 r   Úget_plugin_toolsetsr   ‚
  s  € õ !Ñ"Ô"€GØÔ%ð Øˆ	ðØ+Ð+Ð+Ð+Ð+Ð+Ð+øÝð ð ð Øˆ	ˆ	ˆ	ðøøøð +-€MØ.0€NØÔ/ð <ð <ˆ	Ø×"Ò" 9Ñ-Ô-ˆØð 	ØØŒ]ˆØ× Ò   RÑ(Ô(×/Ò/°´
Ñ;Ô;Ð;Ð;ð !Ô)×/Ò/Ñ1Ô1ð Að A‰ˆˆvØÔ0ð 	Að 	AˆIØ×&Ò& yÑ1Ô1ˆEØð A˜œ¨-Ð7Ð7Ø×)Ò)¨%¬-¸Ñ@Ô@Ð@øð	Að
 €FÝ˜Ñ'Ô'ð -ð -ˆØ×#Ò# FÑ+Ô+ˆØ:˜Ÿš s¨CÑ0Ô0×6Ò6Ñ8Ô8Ð:Ð:ˆØð 	<�f”oÔ1ð 	<Ø”?Ô.ˆDˆDà—9’9�V M°&Ô$9Ñ:Ô:Ñ;Ô;ˆDØ�Š�v˜u dÐ+Ñ,Ô,Ð,Ð,à€Ms   ™   /®/)r   r   r†  r‡  )r_   r`   r   r0   )r   rc   )r   ro   )r|   r`   r   r`   )r�   r‘   r   r`   )r   r•  rˆ  r‰  rŒ  r‹  rŠ  )r¢  )r£  r×  r¤  r`   r   r1   r’  )r*   r*   r*   r*   r*   N)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`   r¬  r`   r­  r`   r®  r`   r¯  r`   r°  r±  r   r¥   )r¬  r`   rw   r`   rm  r`   rÊ  r0   rË  rÝ  rÌ  r`   rÍ  rÎ  r   r¥   )r/   r0   r   r•  )r_   r`   r   rØ  )rm  r   r   r   )r   rò  rŽ  r�  )\r'   Ú
__future__r   rÝ  r‰   Úimportlib.metadatar	  Úimportlib.utilræ  r7   r   r9   ré  rE  Údataclassesr   r   Úpathlibr   Útypingr   r   r	   r
   r   r   r   r   Úhermes_constantsr   Úutilsr   r   ri   r   Úhermes_cli.middlewarer   r   r    rù  rL  ÚPermissionErrorr#   Ú	getLoggerr$   r?   r   r3   r4   r5   r6   rD   r]   rª   r  r/  rb   rn   ru   r{   r�   r˜   rš   r¬   r¶   r•  r�  r‘  r“  r—  r]  rc  ra  r_  Úlocalr¦  rŸ  r¨  rª  r¸  r½  rÀ  rÈ  rÓ  rÕ  r×  rÚ  rî  rñ  rô  r÷  r   r(   r!   r   ú<module>r     sŒ  ððð ð ðB #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø Ð Ð Ð Ø Ð Ð Ð Ø €€€Ø €€€Ø 	€	€	€	Ø 
€
€
€
Ø Ð Ð Ð Ø €€€Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (Ø Ð Ð Ð Ð Ð Ø KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ KÐ Kà ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø 1Ð 1Ð 1Ð 1Ð 1Ð 1Ð 1Ð 1Ø %Ð %Ð %Ð %Ð %Ð %Ø KÐ KÐ KÐ KÐ KÐ KÐ KÐ Kð
>ð 
>ð 
>ð 
>ðØ€K€K€K€KøØð ð ð Ø€D€D€Dðøøøðð ð ð ð ˜oñ ô ð ð 
ˆÔ	˜8Ñ	$Ô	$€ð" �”Ð1°2Ñ6Ô6×<Ò<Ñ>Ô>×DÒDÑFÔFð Kð €ð !Ð ðð ð ð ð ð6 Ð Ñ Ô Ð ðSð Sð S€ð Sð Sð Sñ Sðj ,Ð à€
ð!ð !ð !ð !ð
ð ð ð ð ð ð ð ðD !eÐ dÐ dÐ Ð dÐ dÐ dÑ dð	+ð 	+ð 	+ð 	+ð/ð /ð /ð /ð ð#ð #ð #ð #ð #ñ #ô #ñ „ð#ðL ðð ð ð ð ñ ô ñ „ðð*f
ð f
ð f
ð f
ð f
ñ f
ô f
ð f
ðZt6ð t6ð t6ð t6ð t6ñ t6ô t6ð t6ðv ,0€Ð /Ð /Ð /Ñ /ðð ð ð ð@ð @ð @ð @ð8ð 8ð 8ð 8ð 8ðAð Að Að AðBð Bð Bð Bð?ð ?ð ?ð ?ð4ð 4ð 4ð 4ð
 )˜œÑ*Ô*Ð ð €�$ÐÑÔð#ð #ð #ð #ð #ñ #ô #ñ Ôð#ð Yð.ð .ð .ð .ð .ð*ð *ð *ð *ð ØØØØØ7;ðL#ð L#ð L#ð L#ð L#ðd ØØØØØ7;ð-ð -ð -ð -ð -ð8 ØØØØØ7;ð5ð 5ð 5ð 5ð 5ð8 ØØØØØ7;ðDð Dð Dð Dð DðR ØØØØØØ)-ð0ð 0ð 0ð 0ð 0ð 0ðfð ð ð ð ð8ð 8ð 8ð
/ð /ð /ð /ð &*Ð "ð+ ð + ð + ð + ð\9ð 9ð 9ð 9ðGð Gð Gð Gð*ð *ð *ð *ð *ð *s   Á5A: Á:BÂB