§
    ÷žyjžN  ã                  óŽ  — 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	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mZ ddlmZ ddlmZmZmZmZmZmZ ddlmZ dd	lmZ   ej!        e"¦  «        Z#d
Z$dZ%dZ&dZ'dZ(ddhZ)h d£Z* e+¦   «         Z,de-d<    ej.        ¦   «         Z/ e	j0        e(¬¦  «        Z1de-d<    ej.        ¦   «         Z2da3de-d<   e G d„ d¦  «        ¦   «         Z4dCd„Z5dCd „Z6dDdEd%„Z7dFd'„Z8dGd*„Z9dHd.„Z:dId1„Z;dJd5„Z<dKd9„Z=dLd;„Z>dMd=„Z?dFd>„Z@dFd?„ZA G d@„ dAe jB        ¦  «        ZC e jD        eC¦  «        ZEdMdB„ZFdS )Nuß
  
Outbound webhook notifications.

Reads the ``hooks.outbound:`` list from ``config.yaml`` and registers
notify-only callbacks on the existing plugin hook manager, so every
``invoke_hook()`` site can push lifecycle events to external HTTP
endpoints â€” CI systems, dashboards, other agents â€” with zero changes to
call sites and zero polling on the receiving end.

This is the outbound mirror of the inbound webhook platform
(``gateway/platforms/webhook.py``): inbound wakes Hermes when the world
changes; outbound tells the world when Hermes does something.

Design notes
------------
* Delivery is fire-and-forget through a bounded in-process queue and a
  single daemon worker thread.  ``invoke_hook()`` runs inside the agent
  loop, so callbacks must never block on network I/O â€” they serialize,
  enqueue, and return ``None`` immediately.  Outbound targets can never
  block a tool call, inject context, or otherwise influence agent flow.
* Payloads are signed with HMAC-SHA256 (GitHub-style
  ``X-Hermes-Signature-256: sha256=<hexdigest>`` over the raw body) when
  a secret is configured.  Receivers verify exactly like they verify
  GitHub webhooks.
* No consent prompt: unlike shell hooks, an outbound target executes no
  code on this machine â€” it POSTs JSON to a URL the user themselves put
  in config.  ``HERMES_SAFE_MODE=1`` still skips registration, matching
  plugins / MCP / shell hooks.
* Registration is idempotent â€” safe to invoke from both the CLI entry
  point and the gateway entry point.

Config schema (``~/.hermes/config.yaml``)::

    hooks:
      outbound:
        - url: https://ci.example.com/hermes-events
          events: [on_session_end, subagent_stop]
          # secret literal (discouraged) or env var name (preferred):
          secret_env: HERMES_OUTBOUND_WEBHOOK_SECRET
          # optional regex, honored for pre/post_tool_call only:
          matcher: "terminal|delegate_task"
          timeout: 10       # per-attempt seconds, clamped to [1, 60]
          name: ci-notify   # optional label for logs / `hermes hooks list`

Wire format (POST body)::

    {
        "hook_event_name": "on_session_end",
        "tool_name":       null,
        "tool_input":      null,
        "session_id":      "sess_abc123",
        "cwd":             "/home/user/project",
        "extra":           {...},          # event-specific kwargs
        "delivery_id":     "3f2c...",      # uuid4, unique per POST
        "timestamp":       "2026-07-22T14:00:00Z"
    }

Headers::

    Content-Type:            application/json
    User-Agent:              Hermes-Agent-Outbound-Webhook
    X-Hermes-Event:          <hook event name>
    X-Hermes-Delivery:       <delivery_id>
    X-Hermes-Signature-256:  sha256=<hmac hexdigest>   # only when secret set
é    )ÚannotationsN)Ú	dataclassÚfield)ÚdatetimeÚtimezone)ÚPath)ÚAnyÚDictÚListÚOptionalÚSetÚTuple)Úerror)Úrequesté
   é<   é   g      ð?é   Úpre_tool_callÚpost_tool_call>   ÚargsÚ	tool_nameÚ
session_idÚparent_session_idzSet[Tuple[str, str]]Ú_registered)Úmaxsizez''queue.Queue[Optional[Dict[str, Any]]]'Ú_delivery_queuezOptional[threading.Thread]Ú_workerc                  ó¬   — e Zd ZU dZded<   ded<   dZded<   dZd	ed
<   dZd	ed<   eZ	ded<    e
dd¬¦  «        Zded<   dd„Zedd„¦   «         Zdd„ZdS )ÚWebhookTargetzDParsed and validated representation of one ``hooks.outbound`` entry.ÚstrÚurlz	List[str]ÚeventsÚ ÚnameNúOptional[str]ÚsecretÚmatcherÚintÚtimeoutF)ÚdefaultÚreprzOptional[re.Pattern]Úcompiled_matcherÚreturnÚNonec                ó\  — t          | j        t          ¦  «        r$| j                             ¦   «         }|r|nd | _        | j        rf	 t	          j        | j        ¦  «        | _        d S # t          j        $ r3}t           	                    d| j        |¦  «         d | _        Y d }~d S d }~ww xY wd S )NuL   outbound webhook matcher %r is invalid (%s) â€” treating as literal equality)
Ú
isinstancer(   r!   ÚstripÚreÚcompiler-   r   ÚloggerÚwarning)ÚselfÚstrippedÚexcs      ú=/home/ragecks/.hermes/hermes-agent/agent/outbound_webhooks.pyÚ__post_init__zWebhookTarget.__post_init__|   sË   € Ý�d”l¥CÑ(Ô(ð 	:Ø”|×)Ò)Ñ+Ô+ˆHØ'/Ð9˜8˜8°TˆDŒLØŒ<ð 	-ð-Ý(*¬
°4´<Ñ(@Ô(@�Ô%Ð%Ð%øÝ”8ð -ð -ð -Ý—’ð*Ø+/¬<¸ñô ð ð )-�Ô%Ð%Ð%Ð%Ð%Ð%Ð%øøøøð-øøøð	-ð 	-s   ÁA' Á'B)Á6(B$Â$B)c                ó   — | j         p| j        S ©N)r%   r"   )r7   s    r:   ÚlabelzWebhookTarget.labelŠ   s   € àŒyÐ$˜DœHÐ$ó    r   Úboolc                óx   — | j         sdS |€dS | j        �| j                             |¦  «        d uS || j         k    S )NTF)r(   r-   Ú	fullmatch)r7   r   s     r:   Úmatches_toolzWebhookTarget.matches_toolŽ   sN   € ØŒ|ð 	Ø�4ØÐØ�5ØÔ Ð,ØÔ(×2Ò2°9Ñ=Ô=ÀTÐIÐIØ˜DœLÒ(Ð(r?   ©r.   r/   )r.   r!   )r   r&   r.   r@   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__r%   r'   r(   ÚDEFAULT_TIMEOUT_SECONDSr*   r   r-   r;   Úpropertyr>   rC   © r?   r:   r    r    p   sÚ   € € € € € € àNÐNà€H€H�HØÐÐÑØ€D€N€N€N�NØ €FÐ Ð Ð Ñ Ø!€GÐ!Ð!Ð!Ñ!Ø*€GÐ*Ð*Ð*Ñ*Ø-2¨U¸4ÀeÐ-LÑ-LÔ-LÐÐLÐLÐLÑLð-ð -ð -ð -ð ð%ð %ð %ñ „Xð%ð)ð )ð )ð )ð )ð )r?   r    ÚcfgúOptional[Dict[str, Any]]r.   úList[WebhookTarget]c           
     ó  — t          | t          ¦  «        sg S ddlm}  |d¦  «        rt                               d¦  «         g S |                      d¦  «        }t          t          |t          ¦  «        r|                     d¦  «        nd¦  «        }|sg S ddlm	}  |¦   «         }g }t          5  |D ]½}d	}|j        D ]š}	|	|j        f}
|
t          v rŒ|j                             |	g ¦  «                             t#          |	|¦  «        ¦  «         t                               |
¦  «         d
}t                               d|	|j        |j        |j        ¦  «         Œ›|r|                     |¦  «         Œ¾	 ddd¦  «         n# 1 swxY w Y   |S )uª  Register every configured outbound webhook on the plugin manager.

    ``cfg`` is the full parsed config dict.  Missing, empty, or malformed
    ``hooks.outbound`` is treated as zero targets â€” config parsing never
    raises, because a broken webhook entry must not crash the agent.

    Returns the targets that ended up wired (deduplicated across repeat
    calls, so the CLI and gateway can both invoke this safely).
    r   )Úenv_var_enabledÚHERMES_SAFE_MODEu<   HERMES_SAFE_MODE=1 â€” outbound webhook registration skippedÚhooksÚoutboundN)Úget_plugin_managerFTz?outbound webhook registered: %s -> %s (matcher=%s, timeout=%ds))r1   ÚdictÚutilsrQ   r5   ÚinfoÚgetÚ_parse_outbound_blockÚhermes_cli.pluginsrU   Ú_registered_lockr#   r"   r   Ú_hooksÚ
setdefaultÚappendÚ_make_callbackÚaddr>   r(   r*   )rM   rQ   Ú	hooks_cfgÚtargetsrU   ÚmanagerÚ
registeredÚtargetÚ	wired_anyÚeventÚkeys              r:   Úregister_from_configrj   œ   s  € õ �c�4Ñ Ô ð Øˆ	à%Ð%Ð%Ð%Ð%Ð%à€Ð)Ñ*Ô*ð Ý�ŠÐRÑSÔSÐSØˆ	à—’˜Ñ Ô €IÝ#Ý%/°	½4Ñ%@Ô%@ÐJˆ	�Š�jÑ!Ô!Ð!Àdñô €Gð ð Øˆ	à5Ð5Ð5Ð5Ð5Ð5à Ð Ñ"Ô"€Gà&(€JÝ	ð *ð *Øð 	*ð 	*ˆFØˆIØœð ð �Ø˜fœjÐ)�Ø�+Ð%Ð%ØØ”×)Ò)¨%°Ñ4Ô4×;Ò;Ý" 5¨&Ñ1Ô1ñô ð õ —’ Ñ$Ô$Ð$Ø �	Ý—’ð#à˜6œ<¨¬¸¼ñô ð ð ð
 ð *Ø×!Ò! &Ñ)Ô)Ð)øð#	*ð*ð *ð *ñ *ô *ð *ð *ð *ð *ð *ð *øøøð *ð *ð *ð *ð( Ðs   Â0CE>Å>FÆFc                óÌ   — t          | t          ¦  «        sg S |                      d¦  «        }t          t          |t          ¦  «        r|                     d¦  «        nd¦  «        S )zYParse ``hooks.outbound`` without registering anything.
    Used by ``hermes hooks list``.rS   rT   N)r1   rV   rY   rZ   )rM   rb   s     r:   Úiter_configured_targetsrl   Ò   sa   € õ �c�4Ñ Ô ð Øˆ	Ø—’˜Ñ Ô €IÝ Ý%/°	½4Ñ%@Ô%@ÐJˆ	�Š�jÑ!Ô!Ð!Àdñô ð r?   ç      @r*   Úfloatr@   c                ó¤  — t          j        ¦   «         | z   }t          j        ¦   «         |k     rnt          j        5  t          j        dk    r	 ddd¦  «         dS 	 ddd¦  «         n# 1 swxY w Y   t          j        d¦  «         t          j        ¦   «         |k     °nt          j        5  t          j        dk    cddd¦  «         S # 1 swxY w Y   dS )zŒBlock until all queued deliveries are done (or *timeout* elapses).
    Returns ``True`` when the queue fully drained.  Test/shutdown helper.r   NTg{®Gáz”?)ÚtimeÚ	monotonicr   Úall_tasks_doneÚunfinished_tasksÚsleep)r*   Údeadlines     r:   Úflushrv   Ý   sd  € õ Œ~ÑÔ 'Ñ)€HÝ
Œ.Ñ
Ô
˜XÒ
%Ð
%ÝÔ+ð 	ð 	ÝÔ/°1Ò4Ð4Øð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	Ø4ð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	õ 	Œ
�4ÑÔÐõ	 Œ.Ñ
Ô
˜XÒ
%Ð
%õ
 
Ô	'ð 5ð 5ÝÔ/°1Ò4ð5ð 5ð 5ð 5ñ 5ô 5ð 5ð 5ð 5ð 5ð 5ð 5øøøð 5ð 5ð 5ð 5ð 5ð 5s#   ºA&Á&A*Á-A*Â(CÃC	ÃC	r/   c                 ó  — t           5  t                               ¦   «          ddd¦  «         n# 1 swxY w Y   	 	 t                               ¦   «          t                               ¦   «          Œ3# t          j        $ r Y dS w xY w)zAClear the idempotence set and drain the queue.  Test-only helper.N)r\   r   Úclearr   Ú
get_nowaitÚ	task_doneÚqueueÚEmptyrL   r?   r:   Úreset_for_testsr}   ê   sÈ   € å	ð ð Ý×ÒÑÔÐðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ðð	(Ý×&Ò&Ñ(Ô(Ð(Ý×%Ò%Ñ'Ô'Ð'ð	(øõ Œ;ð ð ð Øˆˆðøøøs   ˆ.®2µ2º4A. Á.BÂ BÚrawr	   c                ó  — | €g S t          | t          ¦  «        s/t                               dt	          | ¦  «        j        ¦  «         g S g }t          | ¦  «        D ],\  }}t          ||¦  «        }|�|                     |¦  «         Œ-|S )Nz8hooks.outbound must be a list of webhook targets; got %s)	r1   Úlistr5   r6   ÚtyperE   Ú	enumerateÚ_parse_single_targetr_   )r~   rc   ÚiÚentryrf   s        r:   rZ   rZ   ú   s•   € Ø
€{Øˆ	Ý�c�4Ñ Ô ð Ý�ŠØFÝ�‰IŒIÔñ	
ô 	
ð 	
ð ˆ	à#%€GÝ˜c‘N”Nð #ð #‰ˆˆ5Ý% a¨Ñ/Ô/ˆØÐØ�NŠN˜6Ñ"Ô"Ð"øØ€Nr?   Úindexr)   úOptional[WebhookTarget]c                ó:  — ddl m} t          |t          ¦  «        s0t                               d| t          |¦  «        j        ¦  «         d S |                     d¦  «        }t          |t          ¦  «        r| 
                    ¦   «         st                               d| ¦  «         d S | 
                    ¦   «         }|                     ¦   «                              d¦  «        st                               d| |¦  «         d S |                     ¦   «                              d¦  «        rt                               d	| ¦  «         |                     d
¦  «        }t          |t          ¦  «        r|s>t                               d| d                     t          |¦  «        ¦  «        ¦  «         d S g }|D ]Y}||v r|                     |¦  «         Œt                               d| |d                     t          |¦  «        ¦  «        ¦  «         ŒZ|st                               d| ¦  «         d S |                     d¦  «        }|�2t          |t          ¦  «        st                               d| ¦  «         d }|�7t#          d„ |D ¦   «         ¦  «        st                               d| |¦  «         d }|                     dt$          ¦  «        }	 t'          |¦  «        }	n@# t(          t*          f$ r, t                               d| |t$          ¦  «         t$          }	Y nw xY wt-          dt/          |	t0          ¦  «        ¦  «        }	t3          | |¦  «        }
|                     d¦  «        }t          |t          ¦  «        sd}t5          ||| 
                    ¦   «         |
||	¬¦  «        S )Nr   )ÚVALID_HOOKSzIhooks.outbound[%d] must be a mapping with 'url' and 'events' keys; got %sr"   z/hooks.outbound[%d] is missing a non-empty 'url')úhttp://zhttps://u:   hooks.outbound[%d].url must be http(s); got %r â€” skippedrŠ   up   hooks.outbound[%d].url uses plain http:// â€” payloads (including tool inputs) travel unencrypted. Prefer https.r#   z>hooks.outbound[%d] needs a non-empty 'events' list (valid: %s)z, z8hooks.outbound[%d]: unknown event %r ignored (valid: %s)u2   hooks.outbound[%d] has no valid events â€” skippedr(   z;hooks.outbound[%d].matcher must be a string regex; ignoringc              3  ó(   K  — | ]}|t           v V — Œd S r=   )Ú_TOOL_SCOPED_EVENTS)Ú.0Úes     r:   ú	<genexpr>z'_parse_single_target.<locals>.<genexpr>D  s(   è è € Ð&PÐ&PÀA qÕ,?Ð'?Ð&PÐ&PÐ&PÐ&PÐ&PÐ&Pr?   um   hooks.outbound[%d].matcher=%r will be ignored â€” matcher is only honored for pre_tool_call / post_tool_call.r*   zEhooks.outbound[%d].timeout must be an int (got %r); using default %dsé   r%   r$   )r"   r#   r%   r'   r(   r*   )r[   r‰   r1   rV   r5   r6   r�   rE   rY   r!   r2   ÚlowerÚ
startswithr€   ÚjoinÚsortedr_   ÚanyrJ   r)   Ú	TypeErrorÚ
ValueErrorÚmaxÚminÚMAX_TIMEOUT_SECONDSÚ_resolve_secretr    )r†   r~   r‰   r"   Ú
events_rawr#   Úevr(   Útimeout_rawr*   r'   r%   s               r:   rƒ   rƒ     sµ  € Ø.Ð.Ð.Ð.Ð.Ð.å�c�4Ñ Ô ð Ý�ŠðØ!¥4¨¡9¤9Ô#5ñ	
ô 	
ð 	
ð ˆtà
�'Š'�%‰.Œ.€CÝ�c�3ÑÔð  s§y¢y¡{¤{ð Ý�ŠÐHÈ%ÑPÔPÐPØˆtØ
�)Š)‰+Œ+€CØ�9Š9‰;Œ;×!Ò!Ð"9Ñ:Ô:ð Ý�ŠØHØ�3ñ	
ô 	
ð 	
ð ˆtØ
‡y‚y�{„{×Ò˜iÑ(Ô(ð 
Ý�Šð=Ø>Cñ	
ô 	
ð 	
ð
 —’˜Ñ"Ô"€JÝ�j¥$Ñ'Ô'ð ¨zð Ý�ŠØLØ�4—9’9�V KÑ0Ô0Ñ1Ô1ñ	
ô 	
ð 	
ð ˆtØ€FØð ð ˆØ�ÐÐØ�MŠM˜"ÑÔÐÐå�NŠNØJØ�r˜4Ÿ9š9¥V¨KÑ%8Ô%8Ñ9Ô9ñô ð ð ð ð Ý�ŠØ@À%ñ	
ô 	
ð 	
ð ˆtà�gŠg�iÑ Ô €GØÐ¥:¨gµsÑ#;Ô#;ÐÝ�ŠØIØñ	
ô 	
ð 	
ð ˆØÐ¥3Ð&PÐ&PÈÐ&PÑ&PÔ&PÑ#PÔ#PÐÝ�Šð:Ø;@À'ñ	
ô 	
ð 	
ð ˆà—'’'˜)Õ%<Ñ=Ô=€Kð*Ý�kÑ"Ô"ˆˆøÝ•zÐ"ð *ð *ð *Ý�ŠðØ  +Õ/Fñ	
ô 	
ð 	
õ *ˆˆˆð*øøøõ �!•S˜Õ"5Ñ6Ô6Ñ7Ô7€Gå˜U CÑ(Ô(€Fà�7Š7�6‰?Œ?€DÝ�d�CÑ Ô ð ØˆåØØØ�ZŠZ‰\Œ\ØØØðñ ô ð s   ËK Ë:LÌLúDict[str, Any]r&   c                óª  — |                      d¦  «        }t          |t          ¦  «        rz|                     ¦   «         rft          j                              |                     ¦   «         d¦  «        }|r|S t                               d| |                     ¦   «         ¦  «         dS |                      d¦  «        }t          |t          ¦  «        r|r|S dS )zE``secret_env`` (env var name, preferred) wins over inline ``secret``.Ú
secret_envr$   u^   hooks.outbound[%d].secret_env=%r is not set in the environment â€” deliveries will be UNSIGNEDNr'   )rY   r1   r!   r2   ÚosÚenvironr5   r6   )r†   r~   r¡   Úvaluer'   s        r:   r›   r›   f  sÍ   € à—’˜Ñ&Ô&€JÝ�*�cÑ"Ô"ð  z×'7Ò'7Ñ'9Ô'9ð Ý”
—’˜z×/Ò/Ñ1Ô1°2Ñ6Ô6ˆØð 	ØˆLÝ�Šð.Ø/4°j×6FÒ6FÑ6HÔ6Hñ	
ô 	
ð 	
ð ˆtØ�WŠW�XÑÔ€FÝ�&�#ÑÔð  6ð ØˆØˆ4r?   rh   r!   rf   c                óV   ‡ ‡— d	ˆ ˆfd„}d‰ › d‰j         › d�|_        |j        |_        |S )
zABuild the notify-only closure ``invoke_hook()`` calls per firing.Úkwargsr	   r.   r/   c                 ój  •— ‰t           v r*‰                     |                      d¦  «        ¦  «        sd S t          j        ¦   «         j        }	 t          ‰| |¦  «        }n4# t          $ r' t           	                    d‰‰j
        d¬¦  «         Y d S w xY wt          t          ‰‰||¦  «        ¦  «         d S )Nr   zBoutbound webhook payload serialization failed (event=%s target=%s)T©Úexc_info)rŒ   rC   rY   ÚuuidÚuuid4ÚhexÚ_serialize_payloadÚ	Exceptionr5   r6   r>   Ú_enqueueÚ_build_delivery)r¦   Údelivery_idÚbodyrh   rf   s      €€r:   Ú	_callbackz!_make_callback.<locals>._callback  sÌ   ø€ ØÕ'Ð'Ð'Ø×&Ò& v§z¢z°+Ñ'>Ô'>Ñ?Ô?ð Ø�tÝ”j‘l”lÔ&ˆð	Ý% e¨V°[ÑAÔAˆDˆDøÝð 	ð 	ð 	Ý�NŠNðØ# V¤\¸Dð ñ ô ð ð �4�4ð	øøøõ 	• ¨°°kÑBÔBÑCÔCÐCØˆts   ÁA  Á -BÂBzoutbound_webhook[ú:Ú])r¦   r	   r.   r/   )r>   rE   rG   )rh   rf   r³   s   `` r:   r`   r`   |  sX   øø€ ðð ð ð ð ð ð ð  E¨UÐDÐD°V´\ÐDÐDÐD€IÔØ&Ô/€IÔØÐr?   r¦   r±   Úbytesc                ó”  — d„ |                      ¦   «         D ¦   «         }	 t          t          j        ¦   «         ¦  «        }n# t          $ r d}Y nw xY w| |                     d¦  «        t          |                     d¦  «        t          ¦  «        r|                     d¦  «        nd|                     d¦  «        p|                     d¦  «        pd|||t          j	        t          j        ¬¦  «                             ¦   «                              d	d
¦  «        dœ}t          j        |dt          ¬¦  «                             d¦  «        S )ue  Render the POST body.  Same top-level shape as shell hooks' stdin
    (documented in :mod:`agent.shell_hooks`), plus delivery metadata.

    ``delivery_id`` is shared with the ``X-Hermes-Delivery`` header so
    receivers can dedupe on either â€” and since it (plus ``timestamp``)
    lives inside the HMAC-signed body, it doubles as replay protection.
    c                ó,   — i | ]\  }}|t           v¯||“ŒS rL   )Ú_TOP_LEVEL_PAYLOAD_KEYS)r�   ÚkÚvs      r:   ú
<dictcomp>z&_serialize_payload.<locals>.<dictcomp>ž  s)   € ÐRÐRÐR‘t�q˜!°Õ:QÐ1QÐ1Qˆa�Ð1QÐ1QÐ1Qr?   r$   r   r   Nr   r   )Útzz+00:00ÚZ)Úhook_event_namer   Ú
tool_inputr   ÚcwdÚextrar±   Ú	timestampF)Úensure_asciir+   úutf-8)Úitemsr!   r   rÁ   ÚOSErrorrY   r1   rV   r   Únowr   ÚutcÚ	isoformatÚreplaceÚjsonÚdumpsÚencode)rh   r¦   r±   ÚextrasrÁ   Úpayloads         r:   r­   r­   ”  s-  € ð SÐR˜vŸ|š|™~œ~ÐRÑRÔR€FðÝ•$”(‘*”*‰oŒoˆˆøÝð ð ð Øˆˆˆðøøøð !Ø—Z’Z Ñ,Ô,Ý,6°v·z²zÀ&Ñ7IÔ7IÍ4Ñ,PÔ,PÐZ�f—j’j Ñ(Ô(Ð(ÐVZØ—j’j Ñ.Ô.ÐW°&·*²*Ð=PÑ2QÔ2QÐWÐUWØØØ"Ý”\¥X¤\Ð2Ñ2Ô2ß	Š‰Œß	Š�˜3Ñ	Ô	ðð €Gõ Œ:�g¨E½3Ð?Ñ?Ô?×FÒFÀwÑOÔOÐOs     A ÁAÁAr²   c                óò   — dd| |dœ}|j         rRt          j        |j                              d¦  «        |t          j        ¦  «                             ¦   «         }d|› �|d<   |j        |j        | |||j	        dœS )Nzapplication/jsonzHermes-Agent-Outbound-Webhook)zContent-Typez
User-AgentzX-Hermes-EventzX-Hermes-DeliveryrÅ   zsha256=zX-Hermes-Signature-256)r"   r>   rh   r²   Úheadersr*   )
r'   ÚhmacÚnewrÎ   ÚhashlibÚsha256Ú	hexdigestr"   r>   r*   )rh   rf   r²   r±   rÒ   Údigests         r:   r°   r°   ²  sš   € ð +Ø5ØØ(ð	ð €Gð „}ð ?Ý”ØŒM× Ò  Ñ)Ô)¨4µ´ñ
ô 
ç
Š)‰+Œ+ð 	ð -?°fÐ,>Ð,>ˆÐ(Ñ)àŒzØ”ØØØØ”>ðð ð r?   Údeliveryc                óà   — t          ¦   «          	 t                               | ¦  «         d S # t          j        $ r2 t
                               dt          | d         | d         ¦  «         Y d S w xY w)NuE   outbound webhook queue full (%d pending) â€” dropping %s event for %srh   r>   )Ú_ensure_workerr   Ú
put_nowaitr{   ÚFullr5   r6   ÚQUEUE_MAX_SIZE©rÙ   s    r:   r¯   r¯   Ê  s…   € ÝÑÔÐð
Ý×"Ò" 8Ñ,Ô,Ð,Ð,Ð,øÝŒ:ð 
ð 
ð 
Ý�ŠðÝ$ h¨wÔ&7¸À'Ô9Jñ	
ô 	
ð 	
ð 	
ð 	
ð 	
ð
øøøs   �, ¬=A-Á,A-c                 ó†  — t           �t                                ¦   «         rd S t          5  t           �'t                                ¦   «         r	 d d d ¦  «         d S t          j        t
          dd¬¦  «        a t                                ¦   «          t          j        t          d¬¦  «         d d d ¦  «         d S # 1 swxY w Y   d S )Nzoutbound-webhooksT)rf   r%   Údaemonrm   ©r*   )
r   Úis_aliveÚ_worker_lockÚ	threadingÚThreadÚ_worker_loopÚstartÚatexitÚregisterrv   rL   r?   r:   rÛ   rÛ   Õ  s  € åÐ�w×/Ò/Ñ1Ô1ÐØˆÝ	ð ,ð ,ÝÐ¥7×#3Ò#3Ñ#5Ô#5ÐØð,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,õ Ô"ÝÐ&9À$ð
ñ 
ô 
ˆõ 	�Š‰Œˆõ 	Œ� sÐ+Ñ+Ô+Ð+ð,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,øøøð ,ð ,ð ,ð ,ð ,ð ,s   ª"B6ÁAB6Â6B:Â=B:c                 ó|  — 	 t                                ¦   «         } 	 | �t          | ¦  «         nW# t          $ rJ t                               dt          | t          ¦  «        r|                      d¦  «        ndd¬¦  «         Y nw xY wt                                ¦   «          n# t                                ¦   «          w xY wŒ¼)NTz-outbound webhook delivery crashed (target=%s)r>   ú?r¨   )	r   rY   Ú_deliverr®   r5   r6   r1   rV   rz   rß   s    r:   rç   rç   è  sÏ   € ð(Ý"×&Ò&Ñ(Ô(ˆð
	(ØÐ#Ý˜Ñ"Ô"Ð"øøÝð 	ð 	ð 	Ý�NŠNØ?Ý)3°H½dÑ)CÔ)CÐL�—’˜WÑ%Ô%Ð%ÈØð ñ ô ð ð ð ð	øøøõ ×%Ò%Ñ'Ô'Ð'Ð'ø�O×%Ò%Ñ'Ô'Ð'Ð'øøøð(s'   œ. ­B ®ABÁ?B ÂBÂB ÂB:c                  ó   — e Zd ZdZd„ ZdS )Ú_NoRedirectHandleruD  Refuse to follow redirects.

    urllib's default handler converts a redirected POST into a body-less
    GET â€” the signed payload would be silently dropped and the headers
    re-sent to a location the user never configured.  Treat any 3xx as a
    delivery failure instead (surfaced as HTTPError by returning None).
    c                ó   — d S r=   rL   )r7   ÚreqÚfpÚcodeÚmsgrÒ   Únewurls          r:   Úredirect_requestz#_NoRedirectHandler.redirect_request  s   € Øˆtr?   N)rE   rF   rG   rH   rö   rL   r?   r:   rï   rï   ø  s-   € € € € € ðð ðð ð ð ð r?   rï   c                ó^  — d}t          dt          dz   ¦  «        D �]á}t          j        | d         | d         | d         d¬¦  «        }	 t                               || d         ¬	¦  «        5 }t          |d
d¦  «        }ddd¦  «         n# 1 swxY w Y   d|cxk    rdk     r/n n,t                               d| d         | d         |¦  «          dS d|› �}nÿ# t          j
        $ r¸}d|j        › �}d|j        cxk    rdk     rMn nJt                               d| d         | d         ||j                             dd¦  «        ¦  «         Y d}~ dS d|j        cxk    rdk     r3n n0t                               d| d         | d         |¦  «         Y d}~ dS Y d}~n=d}~wt          $ r-}t!          |¦  «        pt#          |¦  «        j        }Y d}~nd}~ww xY w|t          k     rt'          j        t*          |z  ¦  «         �Œãt                               dt          | d         | d         |¦  «         dS )u×   POST with bounded retries.  Retries on connection errors and 5xx;
    4xx is the receiver telling us the request itself is wrong â€” no retry.
    3xx redirects are never followed (misconfiguration â€” fix the URL).r$   r�   r"   r²   rÒ   ÚPOST)ÚdatarÒ   Úmethodr*   râ   ÚstatuséÈ   Ni,  z.outbound webhook delivered: %s -> %s (HTTP %d)rh   r>   zHTTP i�  u{   outbound webhook target redirected (event=%s target=%s): %s -> %s â€” redirects are not followed; update the configured urlÚLocationrì   iô  uC   outbound webhook rejected (event=%s target=%s): %s â€” not retryingzMoutbound webhook delivery failed after %d attempt(s) (event=%s target=%s): %s)ÚrangeÚMAX_DELIVERY_ATTEMPTSÚ
urlrequestÚRequestÚ_openerÚopenÚgetattrr5   ÚdebugÚurlerrorÚ	HTTPErrorró   r6   rÒ   rY   r®   r!   r�   rE   rp   rt   ÚRETRY_BACKOFF_SECONDS)rÙ   Ú
last_errorÚattemptrñ   Úresprû   r9   s          r:   rí   rí     sâ  € ð €JÝ˜Õ1°AÑ5Ñ6Ô6ð &8ñ &8ˆÝÔ Ø�UŒOØ˜&Ô!Ø˜YÔ'Øð	
ñ 
ô 
ˆð	8Ý—’˜c¨8°IÔ+>�Ñ?Ô?ð 6À4Ý   x°Ñ5Ô5�ð6ð 6ð 6ñ 6ô 6ð 6ð 6ð 6ð 6ð 6ð 6øøøð 6ð 6ð 6ð 6à�fÐ"Ð"Ò"Ð"˜sÒ"Ð"Ð"Ð"Ð"Ý—’ØDØ˜WÔ% x°Ô'8¸&ñô ð ð ��Ø) Ð)Ð)ˆJˆJøÝÔ!ð 	ð 	ð 	Ø+ ¤Ð+Ð+ˆJØ�c”hÐ$Ð$Ò$Ð$ Ò$Ð$Ð$Ð$Ð$Ý—’ð%à&.¨wÔ&7¸À'Ô9JØ ¤§¢°
¸CÑ @Ô @ñ	ô ð ð ������Ø�c”hÐ$Ð$Ò$Ð$ Ò$Ð$Ð$Ð$Ð$Ý—’ð#Ø$,¨WÔ$5°xÀÔ7HØñô ð ð
 ������øøøøøøøøøÝð 	8ð 	8ð 	8Ý˜S™œÐ7¥T¨#¡Y¤YÔ%7ˆJˆJˆJˆJˆJˆJøøøøð	8øøøð Õ*Ò*Ð*ÝŒJÕ,¨wÑ6Ñ7Ô7Ð7ùå
‡N‚Nð	å˜x¨Ô0°(¸7Ô2CÀZñô ð ð ð sU   Á	"CÁ+B	Á=CÂ	B	ÂCÂB	Â<CÃCÃGÃ%A"FÅ>FÆGÆ%#GÇG)rM   rN   r.   rO   )rm   )r*   rn   r.   r@   rD   )r~   r	   r.   rO   )r†   r)   r~   r	   r.   r‡   )r†   r)   r~   rŸ   r.   r&   )rh   r!   rf   r    )rh   r!   r¦   rŸ   r±   r!   r.   r¶   )
rh   r!   rf   r    r²   r¶   r±   r!   r.   rŸ   )rÙ   rŸ   r.   r/   )GrH   Ú
__future__r   ré   rÕ   rÓ   rÌ   Úloggingr¢   r{   r3   rå   rp   rª   Údataclassesr   r   r   r   Úpathlibr   Útypingr	   r
   r   r   r   r   Úurllibr   r  r   r   Ú	getLoggerrE   r5   rJ   rš   rÿ   r  rÞ   rŒ   r¹   Úsetr   rI   ÚLockr\   ÚQueuer   rä   r   r    rj   rl   rv   r}   rZ   rƒ   r›   r`   r­   r°   r¯   rÛ   rç   ÚHTTPRedirectHandlerrï   Úbuild_openerr  rí   rL   r?   r:   ú<module>r     s–  ðð@ð @ð @ðD #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø €€€Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø 	€	€	€	Ø Ð Ð Ð Ø €€€Ø €€€Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (Ø 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ø Ð Ð Ð Ð Ð Ø 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ø $Ð $Ð $Ð $Ð $Ð $Ø (Ð (Ð (Ð (Ð (Ð (à	ˆÔ	˜8Ñ	$Ô	$€àÐ ØÐ ØÐ ØÐ Ø€ð 'Ð(8Ð9Ð ð SÐRÐRÐ ð %( C¡E¤E€Ð )Ð )Ð )Ñ )Ø!�9”>Ñ#Ô#Ð à;F¸5¼;Øð<ñ <ô <€ð ð ð ñ ð ˆyŒ~ÑÔ€Ø&*€Ð *Ð *Ð *Ñ *ð ð$)ð $)ð $)ð $)ð $)ñ $)ô $)ñ „ð$)ðV3ð 3ð 3ð 3ðlð ð ð ð
5ð 
5ð 
5ð 
5ð 
5ð	ð 	ð 	ð 	ð ð ð ð ð$Wð Wð Wð Wðtð ð ð ð,ð ð ð ð0Pð Pð Pð Pð<ð ð ð ð0
ð 
ð 
ð 
ð,ð ,ð ,ð ,ð&(ð (ð (ð (ð 
ð 
ð 
ð 
ð 
˜Ô7ñ 
ô 
ð 
ð "ˆ*Ô
!Ð"4Ñ
5Ô
5€ð1ð 1ð 1ð 1ð 1ð 1r?   