§
    ‰eîi5´  ã                  ó  — 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	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 ddlmZ ddlmZ 	 ddlZn# e$ r dZY nw xY w ej        e¦  «        Zh d	£Zd
e d<   dZ!dZ"d>d„Z#d?d„Z$d@d„Z%h d£Z&d
e d<   e
 G d„ d¦  «        ¦   «         Z'e
 G d„ d¦  «        ¦   «         Z( G d„ d¦  «        Z) G d„ d ¦  «        Z*da+d!e d"<   dAd#„Z,dBdCd'„Z-dDd,„Z.	 	 	 dEdFd5„Z/dBdGd6„Z0d7„ Z1dHd9„Z2dId;„Z3dJd=„Z4dS )Ku“  
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ÚOptionalÚSetÚUnion)Úget_hermes_home©Úenv_var_enabled>   Úpre_llm_callÚpost_llm_callÚpre_tool_callÚsubagent_stopÚon_session_endÚpost_tool_callÚpre_api_requestÚon_session_resetÚon_session_startÚpost_api_requestÚon_session_finalizeÚpre_gateway_dispatchÚtransform_tool_resultÚtransform_terminal_outputzSet[str]ÚVALID_HOOKSzhermes_agent.pluginsÚhermes_pluginsÚnameÚstrÚreturnÚboolc                ó    — t          | ¦  «        S )z<Return True when an env var is set to a truthy opt-in value.r   )r!   s    ú4/home/div/.hermes/hermes-agent/hermes_cli/plugins.pyÚ_env_enabledr'   Y   s   € å˜4Ñ Ô Ð ó    Úsetc                 ó  — 	 ddl m}   | ¦   «         }|                     di ¦  «                             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_configÚpluginsÚdisabled)Úhermes_cli.configr,   ÚgetÚ
isinstanceÚlistr)   Ú	Exception)r,   Úconfigr.   s      r&   Ú_get_disabled_pluginsr5   ^   s’   € ðØ1Ð1Ð1Ð1Ð1Ð1Ø�‘”ˆØ—:’:˜i¨Ñ,Ô,×0Ò0°¸RÑ@Ô@ˆÝ *¨8µTÑ :Ô :ÐE�s�8‰}Œ}ˆ}ÅÁÄÐEøÝð ð ð Ý‰uŒuˆˆˆðøøøs   ‚A+A. Á.B	ÂB	ú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   r+   r-   NÚenabled)r/   r,   r0   r1   Údictr2   r)   r3   )r,   r4   Úplugins_cfgr8   s       r&   Ú_get_enabled_pluginsr;   n   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Ú	exclusiveÚ
standaloneÚ_VALID_PLUGIN_KINDSc                  óæ   — 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S )ÚPluginManifestz0Parsed representation of a plugin.yaml manifest.r"   r!   Ú ÚversionÚdescriptionÚauthor©Údefault_factoryz List[Union[str, Dict[str, Any]]]Úrequires_envú	List[str]Úprovides_toolsÚprovides_hooksÚsourceNúOptional[str]Úpathr>   ÚkindÚkey)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__rC   rD   rE   r   r2   rH   rJ   rK   rL   rN   rO   rP   © r(   r&   rA   rA   “   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€M€Mr(   rA   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<   dZded<   dZded<   dS )ÚLoadedPluginz)Runtime state for a single loaded plugin.rA   ÚmanifestNzOptional[types.ModuleType]ÚmodulerF   rI   Útools_registeredÚhooks_registeredÚcommands_registeredFr$   r8   rM   Úerror)rQ   rR   rS   rT   rU   rZ   r   r2   r[   r\   r]   r8   r^   rV   r(   r&   rX   rX   ³   s´   € € € € € € à3Ð3àÐÐÑØ)-€FÐ-Ð-Ð-Ñ-Ø"' %¸Ð"=Ñ"=Ô"=ÐÐ=Ð=Ð=Ñ=Ø"' %¸Ð"=Ñ"=Ô"=ÐÐ=Ð=Ð=Ñ=Ø%* U¸4Ð%@Ñ%@Ô%@ÐÐ@Ð@Ð@Ñ@Ø€GÐÐÐÑØ€EÐÐÐÑÐÐr(   rX   c                  ó€   — e Zd ZdZd1d„Z	 	 	 	 	 d2d3d„Zd4d5d„Z	 	 d6d7d#„Z	 	 d8d9d%„Zd:d(„Z	d;d)„Z
d;d*„Zd<d-„Z	 d=d>d0„ZdS )?ÚPluginContextz=Facade given to plugins so they can register tools and hooks.rY   rA   Úmanagerú'PluginManager'c                ó"   — || _         || _        d S ©N)rY   Ú_manager)ÚselfrY   ra   s      r&   Ú__init__zPluginContext.__init__Ç   s   € Ø ˆŒØˆŒˆˆr(   NFrB   r!   r"   ÚtoolsetÚschemar9   Úhandlerr   Úcheck_fnúCallable | NonerH   úlist | NoneÚis_asyncr$   rD   Úemojir#   ÚNonec
                óØ   — ddl m}
 |
                     |||||||||	¬¦	  «	         | j        j                             |¦  «         t                               d| j        j	        |¦  «         dS )zKRegister a tool in the global registry **and** track it as plugin-provided.r   ©Úregistry)	r!   rh   ri   rj   rk   rH   rn   rD   ro   zPlugin %s registered tool: %sN)
Útools.registryrs   Úregisterre   Ú_plugin_tool_namesÚaddÚloggerÚdebugrY   r!   )rf   r!   rh   ri   rj   rk   rH   rn   rD   ro   rs   s              r&   Úregister_toolzPluginContext.register_toolÍ   s‹   € ð 	,Ð+Ð+Ð+Ð+Ð+à×ÒØØØØØØ%ØØ#Øð 	ñ 
	
ô 
	
ð 
	
ð 	ŒÔ(×,Ò,¨TÑ2Ô2Ð2Ý�ŠÐ4°d´mÔ6HÈ$ÑOÔOÐOÐOÐOr(   Ú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)re   Ú_cli_refrx   ÚwarningÚgetattrÚ_interrupt_queueÚputÚ_pending_input)rf   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Š   rD   r‹   rŒ   Úpluginz$Plugin %s registered CLI command: %sN)rY   r!   re   Ú_cli_commandsrx   ry   )rf   r!   rŠ   r‹   rŒ   rD   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.
        ú/ú ú-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 commandrB   )rj   rD   rŽ   r‘   z!Plugin %s registered command: /%s)ÚlowerÚstripÚlstripÚreplacerx   r‚   rY   r!   Úhermes_cli.commandsr–   r3   re   Ú_plugin_commandsry   )rf   r!   rj   rD   r‘   Úcleanr–   s          r&   Úregister_commandzPluginContext.register_command!  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?Ú	tool_nameÚ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   rr   Úparent_agentÚagentN)rt   rs   re   r�   rƒ   Údispatch)rf   rŸ   r    Úkwargsrs   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)
re   Ú_context_enginerx   r‚   rY   r!   Úagent.context_enginer¨   r1   Úinfo)rf   Úenginer¨   s      r&   Úregister_context_enginez%PluginContext.register_context_enginex  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°   r1   rx   r‚   rY   r!   r«   )rf   Úproviderr¯   r°   s       r&   Úregister_image_gen_providerz)PluginContext.register_image_gen_provider˜  s¥   € ð 	>Ð=Ð=Ð=Ð=Ð=Ø>Ð>Ð>Ð>Ð>Ð>å˜(Ð$4Ñ5Ô5ð 	Ý�NŠNð?à”Ô"ñô ð ð
 ˆFØÐ˜(Ñ#Ô#Ð#Ý�ŠØ;ØŒMÔ ¤ñ	
ô 	
ð 	
ð 	
ð 	
r(   Ú	hook_nameÚcallbackc           
     ó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)ú, zPlugin %s registered hook: %sN)r   rx   r‚   rY   r!   ÚjoinÚsortedre   Ú_hooksÚ
setdefaultÚappendry   )rf   rµ   r¶   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(   rN   r   c                ó¤  — ddl m} d|v r t          d|› d| j        j        › d�¦  «        ‚|r|                     |¦  «        st          d|› d�¦  «        ‚|                     ¦   «         st          d	|› �¦  «        ‚| j        j        › d|› �}|| j        j        ||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 )rN   rŽ   Ú	bare_namerD   zPlugin %s registered skill: %sN)Úagent.skill_utilsrÀ   Ú
ValueErrorrY   r!   ÚmatchÚexistsÚFileNotFoundErrorre   Ú_plugin_skillsrx   ry   )rf   r!   rN   rD   rÀ   Ú	qualifieds         r&   Úregister_skillzPluginContext.register_skillÆ  s0  € ð$ 	4Ð3Ð3Ð3Ð3Ð3à�$ˆ;ˆ;Ýð:˜tð :ð :à”MÔ&ð:ð :ð :ñô ð ð
 ð 	˜=×.Ò.¨tÑ4Ô4ð 	ÝØJ tÐJÐJÐJñô ð ð �{Š{‰}Œ}ð 	EÝ#Ð$C¸TÐ$CÐ$CÑDÔDÐDà”}Ô)Ð2Ð2¨DÐ2Ð2ˆ	àØ”mÔ(ØØ&ð	3
ð 3
ˆŒÔ$ YÑ/õ 	�ŠØ,ØŒMÔ 	ñ	
ô 	
ð 	
ð 	
ð 	
r(   )rY   rA   ra   rb   )NNFrB   rB   )r!   r"   rh   r"   ri   r9   rj   r   rk   rl   rH   rm   rn   r$   rD   r"   ro   r"   r#   rp   )r{   )r|   r"   r}   r"   r#   r$   )NrB   )r!   r"   rŠ   r"   r‹   r   rŒ   rl   rD   r"   r#   rp   )rB   rB   )
r!   r"   rj   r   rD   r"   r‘   r"   r#   rp   )rŸ   r"   r    r9   r#   r"   ©r#   rp   )rµ   r"   r¶   r   r#   rp   )rB   )r!   r"   rN   r   rD   r"   r#   rp   )rQ   rR   rS   rT   rg   rz   r‰   r�   rž   r¦   r­   r´   r¾   rÊ   rV   r(   r&   r`   r`   Ä   s7  € € € € € ØGÐGð ð  ð  ð  ð %)Ø$(ØØØðPð Pð Pð Pð Pð>ð ð ð ð ðB '+ØðWð Wð Wð Wð Wð: Øð4Uð 4Uð 4Uð 4Uð 4Uðp<ð <ð <ð <ð>
ð 
ð 
ð 
ð@
ð 
ð 
ð 
ð6Uð Uð Uð Uð. ð	+
ð +
ð +
ð +
ð +
ð +
ð +
r(   r`   c                  óˆ   — e Zd ZdZd/d„Zd0d1d„Z	 d2d3d„Zd4d„Zd5d„Zd6d„Z	d7d„Z
d8d„Zd8d „Zd9d%„Zd:d'„Zd;d*„Zd<d-„Zd=d.„Zd	S )>ÚPluginManagerz;Central manager that discovers, loads, and invokes plugins.r#   rp   c                óœ   — i | _         i | _        t          ¦   «         | _        i | _        d | _        i | _        d| _        d | _        i | _	        d S )NF)
Ú_pluginsr»   r)   rv   r�   r©   rœ   Ú_discoveredr�   rÈ   )rf   s    r&   rg   zPluginManager.__init__û  sQ   € Ø13ˆŒØ13ˆŒÝ,/©E¬EˆÔØ.0ˆÔØ#ˆÔØ13ˆÔØ!&ˆÔØˆŒà9;ˆÔÐÐr(   FÚforcer$   c           	     ó~  — | j         r|sdS |r�| j                             ¦   «          | j                             ¦   «          | j                             ¦   «          | j                             ¦   «          | j                             ¦   «          | j                             ¦   «          d| _        d| _         g }t          t          ¦  «                             ¦   «         j        j        dz  }|                     |                      |dddh¬¦  «        ¦  «         t          ¦   «         dz  }|                     |                      |d¬	¦  «        ¦  «         t!          d
¦  «        rCt          j        ¦   «         dz  dz  }|                     |                      |d¬	¦  «        ¦  «         |                     |                      ¦   «         ¦  «         t'          ¦   «         }t)          ¦   «         }i }|D ]}	|	||	j        p|	j        <   Œ|                     ¦   «         D �]K}	|	j        p|	j        }
|
|v s	|	j        |v r>t1          |	d¬¦  «        }d|_        || j        |
<   t4                               d|
¦  «         Œ\|	j        dk    r>t1          |	d¬¦  «        }d|_        || j        |
<   t4                               d|
¦  «         Œ¥|	j        dk    r!|	j        dk    r|                      |	¦  «         ŒÑ|duo|
|v p|	j        |v }|sRt1          |	d¬¦  «        }d                     |
¦  «        |_        || j        |
<   t4                               d|
¦  «         �Œ6|                      |	¦  «         �ŒM|r^t4                                dtC          | j        ¦  «        tE          d„ | j                             ¦   «         D ¦   «         ¦  «        ¦  «         dS dS )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.
        NTr-   ÚbundledÚmemoryÚcontext_engine)rL   Ú
skip_namesr{   )rL   ÚHERMES_ENABLE_PROJECT_PLUGINSz.hermesÚprojectF)rY   r8   zdisabled via configzSkipping disabled plugin '%s'r=   u<   exclusive plugin â€” activate via <category>.provider configz8Skipping '%s' (exclusive, handled by category discovery)r<   zBnot 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)r8   )Ú.0Úps     r&   ú	<genexpr>z2PluginManager.discover_and_load.<locals>.<genexpr>ˆ  s)   è è € ÐCÐC˜!¸¼ÐC�AÐCÐCÐCÐCÐCÐCr(   )#rÐ   rÏ   Úclearr»   rv   r�   rœ   rÈ   r©   r   Ú__file__ÚresolveÚparentÚextendÚ_scan_directoryr   r'   ÚcwdÚ_scan_entry_pointsr5   r;   rP   r!   ÚvaluesrX   r^   rx   ry   rO   rL   Ú_load_pluginÚformatr«   ÚlenÚsum)rf   rÑ   Ú	manifestsÚrepo_pluginsÚuser_dirÚproject_dirr.   r8   ÚwinnersrY   Ú
lookup_keyÚloadedÚ
is_enableds                r&   Údiscover_and_loadzPluginManager.discover_and_load  s  € ð Ôð 	 Eð 	ØˆFØð 	(ØŒM×ÒÑ!Ô!Ð!ØŒK×ÒÑÔÐØÔ#×)Ò)Ñ+Ô+Ð+ØÔ×$Ò$Ñ&Ô&Ð&ØÔ!×'Ò'Ñ)Ô)Ð)ØÔ×%Ò%Ñ'Ô'Ð'Ø#'ˆDÔ ØˆÔà*,ˆ	õ �H‘~”~×-Ò-Ñ/Ô/Ô6Ô=À	ÑIˆØ×ÒØ× Ò ØØ Ø$Ð&6Ð7ð !ñ ô ñ	
ô 	
ð 	
õ #Ñ$Ô$ yÑ0ˆØ×Ò˜×-Ò-¨h¸vÐ-ÑFÔFÑGÔGÐGõ Ð7Ñ8Ô8ð 	RÝœ(™*œ* yÑ0°9Ñ<ˆKØ×Ò˜T×1Ò1°+ÀiÐ1ÑPÔPÑQÔQÐQð 	×Ò˜×0Ò0Ñ2Ô2Ñ3Ô3Ð3õ )Ñ*Ô*ˆÝ&Ñ(Ô(ˆØ-/ˆØ!ð 	>ð 	>ˆHØ5=ˆG�H”LÐ1 H¤MÑ2Ð2ØŸšÑ(Ô(ð 6	(ñ 6	(ˆ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Øñô ð ð ð Œ} 	Ò)Ð)¨h¬oÀÒ.JÐ.JØ×!Ò! (Ñ+Ô+Ð+Øð ˜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(   NrN   r   rL   r"   rÖ   úOptional[Set[str]]úList[PluginManifest]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).
        rB   r   ©rÖ   ÚprefixÚdepth)Ú_scan_directory_level)rf   rN   rL   rÖ   s       r&   rã   zPluginManager._scan_directory�  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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.ymlNrÚ   z/Skipping %s (no plugin.yaml, depth cap reached)r“   r÷   )Úis_dirrº   Úiterdirr!   rÆ   Ú_parse_manifestr½   rx   ry   râ   rú   )rf   rN   rL   rÖ   rø   rù   rë   ÚchildÚmanifest_filerY   Ú
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Ñ.Ô.Ð.Øð
 ˜ŠzˆzÝ—’ÐNÐPUÑVÔVÐVØà5;ÐK˜FÐ1Ð1 U¤ZÐ1Ð1Ð1ÀÄˆJØ×ÒØ×*Ò*ØØØ#Ø%Ø !™)ð +ñ ô ñô ð ð ð Ðr(   r  Ú
plugin_dirúOptional[PluginManifest]c                óâ  — 	 t           €t                               d|¦  «         dS t          j        |                     ¦   «         ¦  «        pi }|                     d|j        ¦  «        }|r|› d|j        › �n|}|                     dd¦  «        }t          |t          ¦  «        sd}| 	                    ¦   «          
                    ¦   «         }	|	t          vrDt                               d||d                     t          t          ¦  «        ¦  «        ¦  «         d}	|	dk    rrd|vrn|d	z  }
|
                     ¦   «         rU	 |
                     d
¬¦  «        dd…         }d|v sd|v rd}	t                               d|¦  «         n# t           $ r Y nw xY wt#          |t          |                     dd¦  «        ¦  «        |                     dd¦  «        |                     dd¦  «        |                     dg ¦  «        |                     dg ¦  «        |                     dg ¦  «        |t          |¦  «        |	|¬¦  «        S # t           $ r'}t                               d||¦  «         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!   r“   rO   r>   zBPlugin %s: unknown kind '%s' (valid: %s); treating as 'standalone'r¸   ú__init__.pyrš   )Úerrorsi    Úregister_memory_providerÚMemoryProviderr=   zAPlugin %s: detected memory provider, treating as kind='exclusive'rC   rB   rD   rE   rH   rJ   rK   )r!   rC   rD   rE   rH   rJ   rK   rL   rN   rO   rP   zFailed to parse %s: %s)Úyamlrx   r‚   Ú	safe_loadÚ	read_textr0   r!   r1   r"   r˜   r—   r?   r¹   rº   rÆ   ry   r3   rA   )rf   r  r  rL   rø   Údatar!   rP   Úraw_kindrO   Ú	init_fileÚsource_textÚexcs                r&   rÿ   zPluginManager._parse_manifestà  sŸ  € ð;	Ýˆ|Ý—’ÐHÈ-ÑXÔXÐXØ�tÝ”> -×"9Ò"9Ñ";Ô";Ñ<Ô<ÐBÀˆ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ÀÐ&9Ñ&KÔ&KÈEÈTÈEÔ&R˜à6¸+ÐEÐEØ/°;Ð>Ð>à#.˜DÝ"ŸLšLð!?à #ñô ð øøõ
 %ð ð ð Ø˜ðøøøõ "ØÝ˜DŸHšH Y°Ñ3Ô3Ñ4Ô4Ø ŸHšH ]°BÑ7Ô7Ø—x’x ¨"Ñ-Ô-Ø!ŸXšX n°bÑ9Ô9Ø#ŸxšxÐ(8¸"Ñ=Ô=Ø#ŸxšxÐ(8¸"Ñ=Ô=ØÝ˜‘_”_ØØðñ ô ð øõ ð 	ð 	ð 	Ý�NŠNÐ3°]ÀCÑHÔHÐHØ�4�4�4�4�4øøøøð	øøøsC   ‚"H= ¦DH= Ä=AF Æ H= Æ
FÆH= ÆFÆB.H= È=
I.ÉI)É)I.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 rV   ©r  ÚENTRY_POINTS_GROUP©rÛ   Úeps     r&   ú
<listcomp>z4PluginManager._scan_entry_points.<locals>.<listcomp>7  s%   € ÐPÐPÐP B°´Õ=OÒ1OÐ1O˜RÐ1OÐ1OÐ1Or(   Ú
entrypoint)r!   rL   rN   rP   zEntry-point scan failed: %sN)Ú	importlibÚmetadataÚentry_pointsÚhasattrr  r  r1   r9   r0   rA   r!   Úvaluer½   r3   rx   ry   )rf   rë   ÚepsÚ	group_epsr  rY   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rY   rA   c                ó6  ‡ ‡— t          ‰¬¦  «        }	 ‰j        dv r‰                      ‰¦  «        }n‰                      ‰¦  «        }||_        t          |dd¦  «        }|€(d|_        t                               d‰j	        ¦  «         n¬t          ‰‰ ¦  «        } ||¦  «         ˆ fd„‰ j        D ¦   «         |_        t          d„ ‰ j                             ¦   «         D ¦   «         d	„ ‰ j                             ¦   «         D ¦   «         z
  ¦  «        |_        ˆˆ fd
„‰ j        D ¦   «         |_        d|_        nL# t*          $ r?}t-          |¦  «        |_        t                               d‰j	        |¦  «         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.)rY   )r{   rØ   rÓ   ru   Nzno register() functionz&Plugin '%s' has no register() functionc                ó^   •— g | ])}|d „ ‰j                              ¦   «         D ¦   «         v¯'|‘Œ*S )c                ó,   — h | ]\  }}|j         D ]}|’ŒŒS rV   )r[   )rÛ   r!   rÜ   Úns       r&   ú	<setcomp>z8PluginManager._load_plugin.<locals>.<listcomp>.<setcomp>`  sF   € ð !ð !ð !á#˜D !Ø!"Ô!3ð!ð !ð ð ð!ð !ð !ð !r(   )rÏ   Úitems)rÛ   Útrf   s     €r&   r  z.PluginManager._load_plugin.<locals>.<listcomp>^  s`   ø€ ð +ð +ð +ØØð !ð !à'+¤}×':Ò':Ñ'<Ô'<ð!ñ !ô !ð ð ð ðð ð r(   c                ó   — h | ]	\  }}|¯|’Œ
S rV   rV   )rÛ   ÚhÚcbss      r&   r(  z-PluginManager._load_plugin.<locals>.<setcomp>g  s2   € ð ð ð á"˜A˜sØðØðð ð r(   c                ó,   — h | ]\  }}|j         D ]}|’ŒŒS rV   )r\   )rÛ   r!   rÜ   r,  s       r&   r(  z-PluginManager._load_plugin.<locals>.<setcomp>l  sF   € ð ð ð á#˜D !Ø!"Ô!3ðð ð ð ðð ð ð r(   c                óf   •— g | ]-}‰j         |                              d ¦  «        ‰j        k    ¯+|‘Œ.S )rŽ   )rœ   r0   r!   )rÛ   ÚcrY   rf   s     €€r&   r  z.PluginManager._load_plugin.<locals>.<listcomp>r  sF   ø€ ð .ð .ð .ØØÔ,¨QÔ/×3Ò3°HÑ=Ô=ÀÄÒNÐNð ØNÐNÐNr(   TzFailed to load plugin '%s': %s)rX   rL   Ú_load_directory_moduleÚ_load_entrypoint_modulerZ   rƒ   r^   rx   r‚   r!   r`   rv   r[   r2   r»   r)  rÏ   r\   rœ   r]   r8   r3   r"   rP   )rf   rY   rñ   rZ   Úregister_fnÚctxr  s   ``     r&   rç   zPluginManager._load_pluginJ  sý  øø€ å xÐ0Ñ0Ô0ˆð,	QØŒÐ"@Ð@Ð@Ø×4Ò4°XÑ>Ô>��à×5Ò5°hÑ?Ô?�à"ˆFŒMõ " &¨*°dÑ;Ô;ˆKØÐ"Ø7�”Ý—’ÐGÈÌÑWÔWÐWÐWå# H¨dÑ3Ô3�Ø�˜CÑ Ô Ð ð+ð +ð +ð +Ø#Ô6ð+ñ +ô +�Ô'õ +/ðð à&*¤k×&7Ò&7Ñ&9Ô&9ðñ ô ð
ð à'+¤}×':Ò':Ñ'<Ô'<ðñ ô ñ	ñ+ô +�Ô'ð.ð .ð .ð .ð .Ø#Ô4ð.ñ .ô .�Ô*ð "&�”øøåð 	Qð 	Qð 	QÝ˜s™8œ8ˆFŒLÝ�NŠNÐ;¸X¼]ÈCÑPÔPÐPÐPÐPÐPÐPÐPøøøøð	Qøøøð 8>ˆŒ�h”lÐ3 h¤mÑ4Ð4Ð4s   ”D"D7 Ä7
F Å5E;Å;F ú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•   Ú_ú.)Úsubmodule_search_locationsNzCannot create module spec for )r   rN   rÆ   rÇ   Ú
_NS_PARENTÚsysÚmodulesÚtypesÚ
ModuleTypeÚ__path__Ú__package__rP   r!   rš   r  ÚutilÚspec_from_file_locationr"   ÚloaderÚImportErrorÚmodule_from_specÚexec_module)
rf   rY   r  r  Úns_pkgrP   ÚslugÚmodule_nameÚspecrZ   s
             r&   r1  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 rV   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 'ú')r  r  r  r   r  r  r1   r9   r0   r!   ÚloadrE  )rf   rY   r"  r#  r  s        r&   r2  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                ó*  — | 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.
        Nz Hook '%s' callback %s raised: %srQ   rV   )r»   r0   r½   r3   rx   r‚   rƒ   Úrepr)rf   rµ   r¥   Ú	callbacksÚresultsÚcbÚretr  s           r&   Úinvoke_hookzPluginManager.invoke_hookº  sÎ   € ð( ”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ú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        ¦  «        |j        dœ¦  «         Œ¯|S )z7Return a list of info dicts for all discovered plugins.)r!   rP   rO   rC   rD   rL   r8   ÚtoolsÚhooksÚcommandsr^   )rº   rÏ   r)  r½   rY   r!   rP   rO   rC   rD   rL   r8   ré   r[   r\   r]   r^   )rf   ÚresultrP   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Ô$>Ñ ?Ô ?Ø#œ\ðð ñô ð ð ð ˆ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``.rN   N)rÈ   r0   )rf   r_  Úentrys      r&   Úfind_plugin_skillzPluginManager.find_plugin_skillû  s+   € àÔ#×'Ò'¨Ñ7Ô7ˆØ %Ð/ˆu�VŒ}ˆ}¨4Ð/r(   Úplugin_namerI   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Û   ÚqnÚerø   s      €r&   rÝ   z3PluginManager.list_plugin_skills.<locals>.<genexpr>  sQ   øè è € ð 
ð 
á��AØ�}Š}˜VÑ$Ô$ð
ØˆkŒNð
ð 
ð 
ð 
ð 
ð 
r(   )rº   rÈ   r)  )rf   rd  rø   s     @r&   Úlist_plugin_skillsz PluginManager.list_plugin_skills   sX   ø€ àÐ"Ð"Ð"ˆÝð 
ð 
ð 
ð 
àÔ,×2Ò2Ñ4Ô4ð
ñ 
ô 
ñ 
ô 
ð 	
r(   c                ó<   — | j                              |d¦  «         dS )z>Remove a stale registry entry (silently ignores missing keys).N)rÈ   Úpop)rf   r_  s     r&   Úremove_plugin_skillz!PluginManager.remove_plugin_skill	  s!   € àÔ×Ò °Ñ5Ô5Ð5Ð5Ð5r(   rË   ©F©rÑ   r$   r#   rp   rd   )rN   r   rL   r"   rÖ   rô   r#   rõ   )rN   r   rL   r"   rÖ   rô   rø   r"   rù   rû   r#   rõ   )
r  r   r  r   rL   r"   rø   r"   r#   r  )r#   rõ   )rY   rA   r#   rp   )rY   rA   r#   r5  ©rµ   r"   r¥   r   r#   rP  )r#   rX  )r_  r"   r#   r`  )rd  r"   r#   rI   )r_  r"   r#   rp   )rQ   rR   rS   rT   rg   ró   rã   rú   rÿ   rå   rç   r1  r2  rW  r^  rc  rj  rm  rV   r(   r&   rÍ   rÍ   ø  sP  € € € € € ØEÐEð
<ð 
<ð 
<ð 
<ð ~ð ~ð ~ð ~ð ~ðP *.ð	
ð 
ð 
ð 
ð 
ð26ð 6ð 6ð 6ðpFð Fð Fð FðXð ð ð ð<2>ð 2>ð 2>ð 2>ðh$ð $ð $ð $ðL
ð 
ð 
ð 
ð,"ð "ð "ð "ðPð ð ð ð2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.)rq  rÍ   rV   r(   r&   Úget_plugin_managerrs    s   € õ ÐÝ'™/œ/ˆÝÐr(   FrÑ   rp   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©rs  ró   ru  s    r&   Údiscover_pluginsrw    s'   € õ ÑÔ×*Ò*°Ð*Ñ7Ô7Ð7Ð7Ð7r(   rµ   r¥   r   rP  c                ó6   —  t          ¦   «         j        | fi |¤ŽS )z|Invoke a lifecycle hook on all loaded plugins.

    Returns a list of non-``None`` return values from plugin callbacks.
    )rs  rW  )rµ   r¥   s     r&   rW  rW  &  s&   € ð
 ,ÕÑÔÔ+¨IÐ@Ð@¸Ð@Ð@Ð@r(   rB   rŸ   r    úOptional[Dict[str, Any]]Útask_idÚ
session_idÚtool_call_idrM   c                ó(  — t          d| t          |t          ¦  «        r|ni |||¬¦  «        }|D ]b}t          |t          ¦  «        sŒ|                     d¦  «        dk    rŒ2|                     d¦  «        }t          |t          ¦  «        r|r|c S ŒcdS )aÂ  Check ``pre_tool_call`` hooks for a blocking directive.

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

        {"action": "block", "message": "Reason the tool was blocked"}

    from their ``pre_tool_call`` callback.  The first valid block
    directive wins.  Invalid or irrelevant hook return values are
    silently ignored so existing observer-only hooks are unaffected.
    r   )rŸ   r    rz  r{  r|  ÚactionÚblockÚmessageN)rW  r1   r9   r0   r"   )rŸ   r    rz  r{  r|  Úhook_resultsr]  r€  s           r&   Úget_pre_tool_call_block_messager‚  /  s¶   € õ$ ØØÝ ¥dÑ+Ô+Ð3ˆTˆT°ØØØ!ðñ ô €Lð ð ð ˆÝ˜&¥$Ñ'Ô'ð 	ØØ�:Š:�hÑÔ 7Ò*Ð*ØØ—*’*˜YÑ'Ô'ˆÝ�g�sÑ#Ô#ð 	¨ð 	ØˆNˆNˆNøàˆ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.
    ru  rv  )rÑ   ra   s     r&   Ú_ensure_plugins_discoveredr„  V  s+   € õ
 !Ñ"Ô"€GØ×Ò EÐÑ*Ô*Ð*Ø€Nr(   c                 ó(   — t          ¦   «         j        S )z5Return the plugin-registered context engine, or None.)r„  r©   rV   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``.rj   N)r„  rœ   r0   )r!   rb  s     r&   Úget_plugin_command_handlerr‰  e  s3   € å&Ñ(Ô(Ô9×=Ò=¸dÑCÔC€EØ$Ð.ˆ5�ÔÐ¨$Ð.r(   ú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œ   rV   r(   r&   Úget_plugin_commandsrŒ  k  s   € õ &Ñ'Ô'Ô8Ð8r(   úList[tuple]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   rr   u   ðŸ”Œ r8  r”   r¸   )rs  rv   rt   rs   r3   Ú	get_entryrh   r¼   r½   r!   rÏ   r)  r[   rº   r0   rš   ÚtitlerY   rD   r¹   )ra   rs   Útoolset_toolsÚtoolset_pluginrŸ   rb  ÚtsÚ_namerñ   r]  Úts_keyrŽ   ÚlabelÚdescs                 r&   Úget_plugin_toolsetsr˜  t  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#   r6   )r#   rÍ   rn  ro  rp  )rB   rB   rB   )rŸ   r"   r    ry  rz  r"   r{  r"   r|  r"   r#   rM   )rÑ   r$   r#   rÍ   )r!   r"   r#   r‡  )r#   rŠ  )r#   r�  )5rT   Ú
__future__r   r  Úimportlib.metadataÚimportlib.utilÚloggingr<  r>  Údataclassesr   r   Úpathlibr   Útypingr   r   r	   r
   r   r   r   Úhermes_constantsr   Úutilsr   r
  rE  Ú	getLoggerrQ   rx   r   rU   r  r;  r'   r5   r;   r?   rA   rX   r`   rÍ   rq  rs  rw  rW  r‚  r„  r†  r‰  rŒ  r˜  rV   r(   r&   ú<module>r£     ss  ððð ð ðB #Ð "Ð "Ð "Ð "Ð "à Ð Ð Ð Ø Ð Ð Ð Ø Ð Ð Ð Ø €€€Ø 
€
€
€
Ø €€€Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (Ø Ð Ð Ð Ð Ð Ø BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ BÐ Bà ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø !Ð !Ð !Ð !Ð !Ð !ðØ€K€K€K€KøØð ð ð Ø€D€D€Dðøøøð 
ˆÔ	˜8Ñ	$Ô	$€ðð ð €ð ð ð ñ ð0 ,Ð à€
ð!ð !ð !ð !ð
ð ð ð ð ð ð ð ðD !GÐ FÐ FÐ Ð FÐ FÐ FÑ Fð ðð ð ð ð ñ ô ñ „ðð> ð	 ð 	 ð 	 ð 	 ð 	 ñ 	 ô 	 ñ „ð	 ð m
ð m
ð m
ð m
ð m
ñ m
ô m
ð m
ðh	S6ð S6ð S6ð S6ð S6ñ S6ô S6ð S6ðt ,0€Ð /Ð /Ð /Ñ /ðð ð ð ð8ð 8ð 8ð 8ð 8ðAð Að Að Að ØØð$ð $ð $ð $ð $ðNð ð ð ð ð8ð 8ð 8ð
/ð /ð /ð /ð9ð 9ð 9ð 9ð*ð *ð *ð *ð *ð *s   ÁA ÁAÁA