§
    øžyj
#  ã                   ó†   — 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.

Context dict passed to ``agent:start`` / ``agent:end`` handlers:
  platform     -- source platform name (e.g. "telegram", "matrix", "slack")
  user_id      -- platform user id of the sender
  chat_id      -- platform chat id (group/DM identifier)
  thread_id    -- Telegram forum-topic id / thread root id (string; empty
                  when not in a thread / topic)
  chat_type    -- "dm" | "group" | "forum" (empty if unknown)
  session_id   -- Hermes session id
  message      -- inbound message text (truncated to 500 chars)

``agent:end`` adds:
  response     -- agent response text (truncated to 500 chars)
  model        -- model name that handled the turn
  provider     -- provider that handled the turn

Handlers posting a follow-up into the same Telegram forum-topic should
include ``message_thread_id=int(thread_id)`` when ``chat_type == "forum"``
and ``thread_id`` is non-empty.
é    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    ú3/home/ragecks/.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_hooksE   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_hooksJ   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_loadS   s~  € ð 	×$Ò$Ñ&Ô&Ð&å×ÒÑ!Ô!ð 	ØˆFå�y×0Ò0Ñ2Ô2Ñ3Ô3ð @	Vñ @	VˆHØ—?’?Ñ$Ô$ð Øà$ {Ñ2ˆMØ# lÑ2ˆLà ×'Ò'Ñ)Ô)ð °×1DÒ1DÑ1FÔ1Fð Øð6VÝœ>¨-×*AÒ*AÈ7Ð*AÑ*SÔ*SÑTÔT�Øð ¥z°(½DÑ'AÔ'Að ÝÐP¨h¬mÐPÐPÐPÐX\Ð]Ñ]Ô]Ð]Øà$ŸLšL¨°´Ñ?Ô?�	Ø!Ÿš h°Ñ3Ô3�Øð ÝÐM¨iÐMÐMÐMÐUYÐZÑZÔZÐZØð 9¨YÐ8Ð8�Ý ”~×=Ò=Ø ñô �ð �< 4¤;Ð#6ÝÐT¨iÐTÐTÐTÐ\`ÐaÑaÔaÐaÙå"œ×8Ò8¸Ñ>Ô>�Ø+1•”˜KÑ(ðØ”K×+Ò+¨FÑ3Ô3Ð3Ð3øÝ ð ð ð Ý”K—O’O K°Ñ6Ô6Ð6Øðøøøõ $ F¨H°dÑ;Ô;�	ØÐ$ÝÐU¨iÐUÐUÐUÐ]aÐbÑbÔbÐbÙð $ð Kð K�EØ”N×-Ò-¨e°RÑ8Ô8×?Ò?À	ÑJÔJÐJÐJàÔ"×)Ò)Ø%Ø#+§<¢<°¸rÑ#BÔ#BØ$Ý ™MœMð	+ð +ñ ô ð õ ÐO¨iÐOÐOÀvÐOÐOÐW[Ð\Ñ\Ô\Ð\Ñ\øåð Vð Vð VÝÐH°H´MÐHÐHÀQÐHÐHÐPTÐ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 )z¹Return 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Ø�OŠO˜DœN×.Ò.¨|¸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Ñ.Ô.ð !Ø �L�L�L�L�L�L�LøøÝð Wð Wð WÝÐI°zÐIÐIÀaÐIÐIÐQUÐ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ØÐ%Ø—N’N 6Ñ*Ô*Ð*øøÝð Wð Wð WÝÐI°zÐIÐIÀaÐIÐIÐQUÐ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   6   s>  € € € € € ðð ð,ð ,ð ,ð
 ð(˜d 4œjð (ð (ð (ñ „Xð(ðð ð ð ðOVð OVð OVð OVðb¨Cð °D¸´Nð ð ð ð ðWð W Sð W°8¸DÀÀcÀ¼NÔ3Kð WÐW[ð Wð Wð Wð Wð8 -1ðð àðð ˜$˜s C˜xœ.Ô)ðð 
ˆ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Å   ðð&ð &ðP €€€Ø Ð Ð Ð Ø 
€
€
€
Ø 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   