
    i                         d Z ddlZddlZddlZddlmZmZmZm	Z	m
Z
 ddlZddlmZ  e            dz  Z G d d          ZdS )a  
Event Hook System

A lightweight event-driven system that fires handlers at key lifecycle points.
Hooks are discovered from ~/.hermes/hooks/ directories, each containing:
  - HOOK.yaml  (metadata: name, description, events list)
  - handler.py (Python handler with async def handle(event_type, context))

Events:
  - gateway:startup     -- Gateway process starts
  - session:start       -- New session created (first message of a new session)
  - session:end         -- Session ends (user ran /new or /reset)
  - session:reset       -- Session reset completed (new session entry created)
  - agent:start         -- Agent begins processing a message
  - agent:step          -- Each turn in the tool-calling loop
  - agent:end           -- Agent finishes processing
  - command:*           -- Any slash command executed (wildcard match)

Errors in hooks are caught and logged but never block the main pipeline.
    N)AnyCallableDictListOptional)get_hermes_homehooksc                       e Zd ZdZd Zedee         fd            ZddZ	ddZ
dedee         fd	Zdded
eeeef                  ddfdZ	 dded
eeeef                  dee         fdZdS )HookRegistryz
    Discovers, loads, and fires event hooks.

    Usage:
        registry = HookRegistry()
        registry.discover_and_load()
        await registry.emit("agent:start", {"platform": "telegram", ...})
    c                 "    i | _         g | _        d S N)	_handlers_loaded_hooksselfs    2/home/ubuntu/.hermes/hermes-agent/gateway/hooks.py__init__zHookRegistry.__init__-   s    46)+    returnc                 *    t          | j                  S )z'Return metadata about all loaded hooks.)listr   r   s    r   loaded_hookszHookRegistry.loaded_hooks2   s     D&'''r   Nc                     dS )u   Register built-in hooks that are always active.

        Currently empty — no shipped built-in hooks. Kept as the extension
        point for future always-on gateway hooks so they drop in without
        re-plumbing discover_and_load().
        N r   s    r   _register_builtin_hooksz$HookRegistry._register_builtin_hooks7   s	     	r   c           	         |                                   t                                          sdS t          t                                                    D ]}|                                s|dz  }|dz  }|                                r|                                sK	 t          j        |                    d                    }|rt          |t                    st          d|j         dd	           |                    d
|j                  }|                    dg           }|st          d| dd	           d| }t          j                            ||          }||j        t          d| dd	           4t          j                            |          }	|	t&          j        |<   	 |j                            |	           n/# t,          $ r" t&          j                            |d            w xY wt1          |	dd          }
|
t          d| dd	           |D ]0}| j                            |g                               |
           1| j                            ||                    dd          |t;          |          d           t          d| d| d	           c# t,          $ r'}t          d|j         d| d	           Y d}~d}~ww xY wdS )aI  
        Scan the hooks directory for hook directories and load their handlers.

        Also registers built-in hooks that are always active.

        Each hook directory must contain:
          - HOOK.yaml with at least 'name' and 'events' keys
          - handler.py with a top-level 'handle' function (sync or async)
        Nz	HOOK.yamlz
handler.pyzutf-8)encodingz[hooks] Skipping z: invalid HOOK.yamlTflushnameeventsz: no events declaredhermes_hook_z: could not load handler.pyhandlez: no 'handle' function founddescription )r    r$   r!   pathz[hooks] Loaded hook 'z' for events: z[hooks] Error loading hook z: )r   	HOOKS_DIRexistssortediterdiris_diryaml	safe_load	read_text
isinstancedictprintr    get	importlibutilspec_from_file_locationloadermodule_from_specsysmodulesexec_module	Exceptionpopgetattrr   
setdefaultappendr   str)r   hook_dirmanifest_pathhandler_pathmanifest	hook_namer!   module_namespecmodule	handle_fneventes                r   discover_and_loadzHookRegistry.discover_and_load@   s~    	$$&&&!! 	Fy002233 @	V @	VH??$$ ${2M#l2L '')) 1D1D1F1F 6V>-*A*A7*A*S*STT z(D'A'A PhmPPPX\]]]]$LL??	!h33 MiMMMUYZZZZ 9Y88 ~==  <4;#6TiTTT\`aaaa"88>>+1K(K++F3333    KOOK666 $FHd;;	$UiUUU]abbbb $ K KEN--eR88??	JJJJ"))%#+<<r#B#B$MM	+ +    OiOOvOOW[\\\\\ V V VHHMHHQHHPTUUUUUUUUUV@	V @	VsL   "AJ9<AJ9AJ9
.J99GJ9,H  +J9-B
J99
K*K%%K*
event_typec                     t          | j                            |g                     }d|v rN|                    d          d         }| d}|                    | j                            |g                      |S )zReturn all handlers that should fire for ``event_type``.

        Exact matches fire first, followed by wildcard matches (e.g.
        ``command:*`` matches ``command:reset``).
        :r   z:*)r   r   r2   splitextend)r   rM   handlersbasewildcard_keys        r   _resolve_handlerszHookRegistry._resolve_handlers   sz     **:r::;;*##C((+D";;;LOODN..|R@@AAAr   contextc                    K   |i }|                      |          D ]Y}	  |||          }t          j        |          r| d{V  ,# t          $ r!}t	          d| d| d           Y d}~Rd}~ww xY wdS )a  
        Fire all handlers registered for an event, discarding return values.

        Supports wildcard matching: handlers registered for "command:*" will
        fire for any "command:..." event. Handlers registered for a base type
        like "agent" won't fire for "agent:start" -- only exact matches and
        explicit wildcards.

        Args:
            event_type: The event identifier (e.g. "agent:start").
            context:    Optional dict with event-specific data.
        N[hooks] Error in handler for '': Tr   )rU   asyncioiscoroutiner;   r1   )r   rM   rV   fnresultrK   s         r   emitzHookRegistry.emit   s       ?G((44 	W 	WBWJ00&v.. ! LLLLLLL W W WIzIIaIIQUVVVVVVVVVW	W 	Ws   (A
A3A..A3c                 "  K   |i }g }|                      |          D ]p}	  |||          }t          j        |          r| d{V }||                    |           C# t          $ r!}t          d| d| d           Y d}~id}~ww xY w|S )a  Fire handlers and return their non-None return values in order.

        Like :meth:`emit` but captures each handler's return value. Used for
        decision-style hooks (e.g. ``command:<name>`` policies that want to
        allow/deny/rewrite the command before normal dispatch).

        Exceptions from individual handlers are logged but do not abort the
        remaining handlers.
        NrX   rY   Tr   )rU   rZ   r[   r?   r;   r1   )r   rM   rV   resultsr\   r]   rK   s          r   emit_collectzHookRegistry.emit_collect   s       ?G((44 	W 	WBWJ00&v.. *#)\\\\\\F%NN6*** W W WIzIIaIIQUVVVVVVVVVWs   ?A!!
B+BB)r   Nr   )__name__
__module____qualname____doc__r   propertyr   r0   r   r   rL   r@   r   rU   r   r   r   r^   ra   r   r   r   r   r   #   s>        , , ,
 (d4j ( ( ( X(   OV OV OV OVbC DN    W WS W8DcN3K WW[ W W W W8 -1  $sCx.) 
c	     r   r   )re   rZ   importlib.utilr3   r8   typingr   r   r   r   r   r,   hermes_cli.configr   r'   r   r   r   r   <module>rj      s    *      



 6 6 6 6 6 6 6 6 6 6 6 6 6 6  - - - - - - O'	o o o o o o o o o or   