
    
Bj                     x    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y)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y)HookRegistryz
    Discovers, loads, and fires event hooks.

    Usage:
        registry = HookRegistry()
        registry.discover_and_load()
        await registry.emit("agent:start", {"platform": "telegram", ...})
    c                      i | _         g | _        y 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                      y)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           	      f   | j                          t        j                         syt        t        j	                               D ]	  }|j                         s|dz  }|dz  }|j                         r|j                         s@	 t        j                  |j                  d            }|rt        |t              st        d|j                   dd	       |j                  d
|j                        }|j                  dg       }|st        d| dd	       d| }t        j                  j!                  ||      }||j"                  t        d| dd	       t        j                  j%                  |      }	|	t&        j(                  |<   	 |j"                  j+                  |	       t1        |	dd      }
|
t        d| dd	       |D ]-  }| j2                  j5                  |g       j7                  |
       / | j8                  j7                  ||j                  dd      |t;        |      d       t        d| d| d	        y# t,        $ r" t&        j(                  j/                  |d        w xY w# t,        $ r(}t        d|j                   d| d	       Y d}~hd}~ww xY w)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    	$$&!y0023 @	VH??$${2M#l2L '')1D1D1F6V>>-*A*A7*A*STz(D'A-hmm_<OPX\]$LL?	!h3-i[8LMUYZ !-YK8 ~~== <4;;#6-i[8ST\`a"88>+1K(KK++F3
 $FHd;	$-i[8TU]ab $ KENN--eR8??	JK ""))%#+<<r#B$M	+  -i[vhOW[\{@	VR ! KKOOK6,  V3HMM?"QCHPTUUVsL   AI?AI?AI?2I?I- I?A>I?+I<<I??	J0J++J0
event_typec                     t        | j                  j                  |g             }d|v rD|j                  d      d   }| d}|j	                  | j                  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   si     **:r:;*##C(+D"V2;LOODNN..|R@Ar   contextc                    K   |i }| j                  |      D ]+  }	  |||      }t        j                  |      r
| d{    - y7 # t        $ r}t	        d| d| d       Y d}~Rd}~ww xY ww)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 	WBWJ0&&v. LL	W
 ! W6zl#aSIQUVVWs?   A2#A	A A	A2A			A/A*%A2*A//A2c                   K   |i }g }| j                  |      D ]>  }	  |||      }t        j                  |      r
| d{   }||j                  |       @ |S 7 # t        $ r}t        d| d| d       Y d}~fd}~ww xY ww)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 	WBWJ0&&v.#)\F%NN6*	W  *  W6zl#aSIQUVVWs@   B#AAABA	B(B ;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 ( (OVbC DN WS W8DcN3K 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  - '	o or   