+
    jHàj   ã                   óÞ   € R t ^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	H
t
HtHtHt ^ RIt^ RIHt ^ RIHt ]! 4       R,          t]tR R ltR	 R
 ltR R lt ! R R4      t ! R R4      tR# )av  Event hook system: fires handlers at gateway lifecycle points.

Hooks live in ~/.hermes/hooks/<name>/ with HOOK.yaml (name, description, events) and
handler.py (``def handle(event_type, context)``, sync or async); errors never block
the pipeline.  Events: gateway:startup, session:start/end/reset, agent:start,
agent:step (each tool-loop turn), agent:end, command:* (wildcard).  agent:* context:
platform, user_id, chat_id, thread_id ("" outside a thread), chat_type
("dm"|"group"|"forum"|""), session_id, message (500 chars); agent:end adds response,
model, provider.  Forum follow-ups pass ``message_thread_id=int(thread_id)``.
N©ÚPath)ÚAnyÚCallableÚDictÚListÚOptional)Úget_hermes_home)Úhermes_home_keyÚhooksc                ó$   € V ^8„  d   QhR\         /# ©é   Úreturnr   )Úformats   "Ú2/home/ubuntu/.hermes/hermes-agent/gateway/hooks.pyÚ__annotate__r      s   € ÷ ]ñ ]œDñ ]ó    c                 ó\   € \        \        4      p V \        8w  d   V # \        4       R,          # )a¢  Active profile's hooks dir at call time: the patched ``HOOKS_DIR`` when a test changed it,
else ``get_hermes_home()/hooks``. The import-time constant is the LAUNCH profile's; under
``gateway.multiplex_profiles`` every served profile has its own ``hooks/``, and a registry
loaded from the launch home would run the default profile's handlers (arbitrary Python) on
every other profile's messages, responses and user ids.r   )r   Ú	HOOKS_DIRÚ_HOOKS_DIR_AT_IMPORTr	   )Ú
configureds    r   Ú_resolve_hooks_dirr      s)   € ô ”i“€JØ#Ô';Ô;ˆ:Ð\ÄÓARÐU\ÕA\Ð\r   c                ó4   € V ^8„  d   QhR\         R\         RR/# )r   ÚnameÚreasonr   N)Ústr)r   s   "r   r   r   '   s!   € ÷ <ñ <”ð <œSð < Tñ <r   c                 ó,   € \        R V  RV 2RR7       R# )z[hooks] Skipping ú: T©ÚflushN)Úprint)r   r   s   &&r   Ú_skipr"   '   s   € Ü	Ð˜d˜V 2 f XÐ
.°d×;r   c                óF   € V ^8„  d   QhR\         R\        \        ,          /# )r   Úhook_dirr   )r   r   Útuple)r   s   "r   r   r   +   s"   € ÷ Iñ IœTð I¤h¬u¥oñ Ir   c                óš  € V R,          V R,          r!VP                  4       '       d   VP                  4       '       g   R# \        P                  ! VP                  RR7      4      pV'       d   \	        V\
        4      '       g   \        V P                  R4      # VP                  RV P                  4      pVP                  R. 4      pV'       g   \        VR	4      # R
V 2p\        P                  P                  Wb4      pVe   VP                  f   \        VR4      # \        P                  P                  V4      pV\        P                  V&    VP                  P!                  V4       \'        TRR4      p	T	f   \        TR4      # YEY“P                  RR4      3#   \"         d#    \        P                  P%                  TR4       h i ; i)z\``(name, events, handle_fn, description)`` for a valid hook dir, else None (reason printed).z	HOOK.yamlz
handler.pyNz	utf-8-sig)Úencodingzinvalid HOOK.yamlr   Úeventszno events declaredÚhermes_hook_zcould not load handler.pyÚhandlezno 'handle' function foundÚdescriptionÚ )ÚexistsÚyamlÚ	safe_loadÚ	read_textÚ
isinstanceÚdictr"   r   ÚgetÚ	importlibÚutilÚspec_from_file_locationÚloaderÚmodule_from_specÚsysÚmodulesÚexec_moduleÚ	ExceptionÚpopÚgetattr)
r$   Úmanifest_pathÚhandler_pathÚmanifestÚ	hook_namer(   Úmodule_nameÚspecÚmoduleÚ	handle_fns
   &         r   Ú_load_hook_dirrG   +   s|  € à"*¨[Õ"8¸(À\Õ:Q�<Ø×Ñ×!Ò!¨×)<Ñ)<×)>Ò)>ÙÜ�~Š~˜m×5Ñ5¸{Ð5ÓKÓL€Hßœ: h´×5Ò5Ü�X—]‘]Ð$7Ó8Ð8Ø—‘˜V X§]¡]Ó3€IØ�\‰\˜( BÓ'€FßÜ�YÐ 4Ó5Ð5ð !  Ð,€KÜ�>‰>×1Ñ1°+ÓL€DØ‚|�t—{‘{Ò*Ü�YÐ ;Ó<Ð<Ü�^‰^×,Ñ,¨TÓ2€FØ%„C‡K�K�ÑðØ�‰×Ñ Ô'ô ˜ ¨$Ó/€IØÒÜ�YÐ <Ó=Ð=Ø˜i¯©°mÀRÓ)HÐHÐHøô ô Ü�‰�‰˜ TÔ*Øðús   ÅF Æ-G
c                   ó¨   a € ] tR t^Kt o RtR t]V 3R lR l4       tV 3R lR ltV 3R lR lt	V 3R	 lR
 lt
RV 3R lR lltRV 3R lR lltRtV tR# )ÚHookRegistryz(Discovers, loads, and fires event hooks.c                ó"   € / V n         . V n        R # ©N©Ú	_handlersÚ_loaded_hooks©Úselfs   &r   Ú__init__ÚHookRegistry.__init__N   s   € Ø46ˆŒØ)+ˆÖr   c                ó0   <€ V ^8„  d   QhRS[ S[,          /# r   ©Úlistr2   )r   Ú__classdict__s   "€r   r   ÚHookRegistry.__annotate__S   s   ø€ ÷ (ñ (™d¡4�jñ (r   c                ó,   € \        V P                  4      # rK   )rU   rN   rO   s   &r   Úloaded_hooksÚHookRegistry.loaded_hooksR   s   € ä�D×&Ñ&Ó'Ð'r   c                ó   <€ V ^8„  d   QhRR/# ©r   r   N© )r   rV   s   "€r   r   rW   V   s   ø€ ÷ Tñ T¨ñ Tr   c                ó   € R# )zEExtension point for always-on built-in hooks; currently none shipped.Nr]   rO   s   &r   Ú_register_builtin_hooksÚ$HookRegistry._register_builtin_hooksV   s   ‚ r   c                ó   <€ V ^8„  d   QhRR/# r\   r]   )r   rV   s   "€r   r   rW   Y   s   ø€ ÷ Yñ Y 4ñ Yr   c                óZ  € V P                  4        \        4       pVP                  4       '       g   R# \        VP	                  4       4       F¥  pVP                  4       '       g   K   \        V4      pTf   K-  Tw  rVrxT F.  p	T P                  P                  T	. 4      P                  T4       K0  	  T P                  P                  RTRTRTR	\        T4      /4       \        R
T RT 2RR7       K§  	  R#   \         d)   p\        RTP                   RT 2RR7        Rp?KÙ  Rp?ii ; i)z^Register built-in hooks, then load every valid hook dir under the active profile's ``hooks/``.Nz[hooks] Error loading hook r   Tr   r   r+   r(   Úpathz[hooks] Loaded hook 'z' for events: )r_   r   r-   ÚsortedÚiterdirÚis_dirrG   r<   r!   r   rM   Ú
setdefaultÚappendrN   r   )
rP   Ú	hooks_dirr$   ÚloadedÚerB   r(   rF   r+   Úevents
   &         r   Údiscover_and_loadÚHookRegistry.discover_and_loadY   s  € à×$Ñ$Ô&Ü&Ó(ˆ	Ø×Ñ×!Ò!ÙÜ˜y×0Ñ0Ó2Ö3ˆHØ—?‘?×$Ò$ÙðÜ'¨Ó1�ð Š~ÙØ8>Ñ5ˆI˜yÛ�Ø—‘×)Ñ)¨%°Ó4×;Ñ;¸IÖFñ  à×Ñ×%Ñ%Ø˜ M°;ÀÈ&ÐRXÔZ]Ð^fÓZgÐhôô Ð)¨)¨°NÀ6À(ÐKÐSW×Xó! 4øô
 ô ÜÐ3°H·M±M°?À"ÀQÀCÐHÐPTÕUÝûðús   Á(C7Ã7D*ÄD%Ä%D*c                ó6   <€ V ^8„  d   QhRS[ RS[S[,          /# )r   Ú
event_typer   )r   rU   r   )r   rV   s   "€r   r   rW   q   s   ø€ ÷ ñ ©Cð ±D¹µNñ r   c                óæ   € \        V P                  P                  V. 4      4      pRV9   dE   VP                  V P                  P                  VP	                  R4      ^ ,           R2. 4      4       V# )u£   Exact-match handlers first, then ``<base>:*`` wildcards.  A bare base type
("agent") does NOT fire for "agent:start" â€” only exact matches and explicit wildcards.Ú:z:*)rU   rM   r3   ÚextendÚsplit)rP   rp   Úhandlerss   && r   Ú_resolve_handlersÚHookRegistry._resolve_handlersq   sb   € ô ˜Ÿ™×*Ñ*¨:°rÓ:Ó;ˆØ�*ÔØ�O‰O˜DŸN™N×.Ñ.°*×2BÑ2BÀ3Ó2GÈÕ2JÐ1KÈ2Ð/NÐPRÓSÔTØˆr   Nc                óP   <€ V ^8„  d   QhRS[ RS[S[S[ S[3,          ,          RR/# ©r   rp   Úcontextr   N©r   r   r2   r   )r   rV   s   "€r   r   rW   y   s0   ø€ ÷ 5ñ 5¡Sð 5±8¹DÁÁcÀ½NÕ3Kð 5ÐW[ñ 5r   c              ƒ  óD   "  € V P                  W4      G Rj  x€L
  R#  L5i)z9Fire all handlers for an event, discarding return values.N)Úemit_collect©rP   rp   rz   s   &&&r   ÚemitÚHookRegistry.emity   s   é € à×Ñ 
Ó4×4Ô4ùs   ‚ —˜ c                ób   <€ V ^8„  d   QhRS[ RS[S[S[ S[3,          ,          RS[S[,          /# ©r   rp   rz   r   ©r   r   r2   r   rU   )r   rV   s   "€r   r   rW   }   s6   ø€ ÷ ñ ©Sð ¹8ÁDÉÉcÈÅNÕ;Sð Ñ_cÑdgÕ_hñ r   c              ƒ  ó:  "  € Vf   / p. pV P                  V4       FK  p V! W4      p\        P                  ! V4      '       d   VG Rj  x€L
 MTpVe   VP                  V4       KK  KM  	  V#  L#  \         d   p\        RT RT 2RR7        Rp?Kw  Rp?ii ; i5i)z¡Fire handlers and return their non-None return values in order (decision-style
hooks, e.g. ``command:<name>`` policies).  A failing handler is logged, not fatal.Nz[hooks] Error in handler for 'z': Tr   )rv   ÚasyncioÚiscoroutinerh   r<   r!   )rP   rp   rz   ÚresultsÚfnÚresultrk   s   &&&    r   r}   ÚHookRegistry.emit_collect}   s¨   é € ð Š?ØˆGØˆØ×(Ñ(¨Ö4ˆBðWÙ˜JÓ0�Ü)0×)<Ò)<¸V×)DÒ)D˜vŸ˜È&�ØÒ%Ø—N‘N 6Ö*ñ &ñ	 5ð ˆñ &øô ô WÜÐ6°z°lÀ#ÀaÀSÐIÐQU×VÓVûðWüs@   ‚B )A/Á	A-Á
A/Á%BÁ-A/Á/BÁ:BÂBÂBÂBrL   rK   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__rQ   ÚpropertyrY   r_   rm   rv   r   r}   Ú__static_attributes__Ú__classdictcell__©rV   s   @r   rI   rI   K   s\   ø‡ € Ù2ò,ð ÷(ó ð(÷Tð T÷Yð Y÷0ð ÷5ò 5÷÷ ð r   rI   c                   ó–   a € ] tR t^Žt o RtR tV 3R lR lt]V 3R lR l4       tV 3R lR lt	RV 3R
 lR llt
RV 3R lR lltRtV tR	# )ÚProfileHookRegistriesaz  ``HookRegistry`` per served profile home, picked at emit time from the active HERMES_HOME.

The gateway holds ONE of these. Every hook emit already runs inside the routed profile's
``_profile_runtime_scope`` (message handlers, /new, turn wiring), so resolving the registry by
``get_hermes_home()`` there gives each profile its own ``hooks/`` and keeps the default
profile's handlers from seeing other profiles' messages. Each home's registry is loaded on its
first emit, i.e. inside that profile's scope (handler imports see its HERMES_HOME); multiplexing off
means a single entry for the launch home, i.e. exactly the old behaviour.
c                óH   € / V n         \        P                  ! 4       V n        R # rK   )Ú_by_homeÚ	threadingÚLockÚ_lockrO   s   &r   rQ   ÚProfileHookRegistries.__init__™   s   € Ø13ˆŒÜ—^’^Ó%ˆŽ
r   c                ó    <€ V ^8„  d   QhRS[ /# r   )rI   )r   rV   s   "€r   r   Ú"ProfileHookRegistries.__annotate__�   s   ø€ ÷ 
ñ 
™ñ 
r   c                ó^  € \        \        4       4      pV P                  P                  V4      pVfg   V P                  ;_uu_ 4        V P                  P                  V4      pVf)   \        4       pVP                  4        W P                  V&   R R R 4       V# V#   + '       g   i     T# ; irK   )r
   r	   r—   r3   rš   rI   rm   )rP   ÚkeyÚregistrys   &  r   Ú_activeÚProfileHookRegistries._active�   sŠ   € ÜœoÓ/Ó0ˆØ—=‘=×$Ñ$ SÓ)ˆØÒØ——“ØŸ=™=×,Ñ,¨SÓ1�ØÒ#Ü+›~�HØ×.Ñ.Ô0Ø)1—M‘M #Ñ&÷ ð ˆˆxˆ÷ –ð ˆús   ÁABÂB,	c                ó0   <€ V ^8„  d   QhRS[ S[,          /# r   rT   )r   rV   s   "€r   r   r�   ª   s   ø€ ÷ +ñ +™d¡4�jñ +r   c                ó6   € V P                  4       P                  # rK   )r¡   rY   rO   s   &r   rY   Ú"ProfileHookRegistries.loaded_hooks©   s   € à�|‰|‹~×*Ñ*Ð*r   c                ó   <€ V ^8„  d   QhRR/# r\   r]   )r   rV   s   "€r   r   r�   ­   s   ø€ ÷ ñ  4ñ r   c                ó&   € V P                  4        R# )zTLoad the active home's hooks now (startup, or a secondary profile's scoped startup).N)r¡   rO   s   &r   rm   Ú'ProfileHookRegistries.discover_and_load­   s   € à�‰Žr   Nc                óP   <€ V ^8„  d   QhRS[ RS[S[S[ S[3,          ,          RR/# ry   r{   )r   rV   s   "€r   r   r�   ±   s0   ø€ ÷ 7ñ 7¡Sð 7±8¹DÁÁcÀ½NÕ3Kð 7ÐW[ñ 7r   c              ƒ  ó`   "  € V P                  4       P                  W4      G R j  x€L
  R #  L5irK   )r¡   r   r~   s   &&&r   r   ÚProfileHookRegistries.emit±   s   é € Ø�l‰l‹n×!Ñ! *Ó6×6Ô6ùs   ‚#.¥,¦.c                ób   <€ V ^8„  d   QhRS[ RS[S[S[ S[3,          ,          RS[S[,          /# r‚   rƒ   )r   rV   s   "€r   r   r�   ´   s;   ø€ ÷ Fñ F©Sð F¹8ÁDÉÉcÈÅNÕ;Sð FÑ_cÑdgÕ_hñ Fr   c              ƒ  ó\   "  € V P                  4       P                  W4      G R j  x€L
 #  L5irK   )r¡   r}   r~   s   &&&r   r}   Ú"ProfileHookRegistries.emit_collect´   s"   é € Ø—\‘\“^×0Ñ0°ÓE×EÐEÑEùs   ‚#,¥*¦,)r—   rš   rK   )r‹   rŒ   r�   rŽ   r�   rQ   r¡   r�   rY   rm   r   r}   r‘   r’   r“   s   @r   r•   r•   Ž   sS   ø‡ € ñò&÷
ð 
ð ÷+ó ð+÷ð ÷7ò 7÷F÷ Fð Fr   r•   )r�   r…   Úimportlib.utilr4   r9   r˜   Úpathlibr   Útypingr   r   r   r   r   Úhermes_yamlr.   Úhermes_cli.configr	   Úhermes_constantsr
   r   r   r   r"   rG   rI   r•   r]   r   r   Ú<module>rµ      sj   ðñ	ó Û Û 
Û Ý ß 6Õ 6ã å -Ý ,ñ Ó Õ'€	Ø Ð õ]õ<õI÷@@ñ @÷F'Fó 'Fr   