Ë
    âß²jYz  ã                  ó~  — 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mZ ddlmZmZmZmZmZmZmZmZ ddlmZ  ej4                  d«      Zh d£Zd	ed
<   dhZd	ed<   dhZd	ed<   dZ dZ!dZ"d1d„Z# e$dh«      Z%dZ&dZ'dZ(dZ) e	jT                  d«      Z+dZ,dZ-dZ.d2d„Z/d3d„Z0d4d„Z1dZ2dZ3d Z4 e5«       Z6 ed!¬"«       G d#„ d$«      «       Z7 ed!¬"«       G d%„ d&«      «       Z8 ed!¬"«       G d'„ d(«      «       Z9 ed!¬"«       G d)„ d*«      «       Z:d+Z;d,Z< e5«       Z=d5d-„Z>d6d.„Z? G d/„ d0«      Z@y)7zåPlugin hook / middleware / event-bus / system-prompt-section dispatch.

Mixed into :class:`hermes_cli.plugins.PluginManager`. ``_resolve_hook_callback_timeout`` stays on
the origin (tests patch it there) and is looked up lazily.
é    )ÚannotationsN)Ú	dataclass)ÚAnyÚCallableÚDictÚListÚMappingÚOptionalÚSetÚUnion)ÚOBSERVER_SCHEMA_VERSIONzhermes_cli.plugins>   Ú
pre_verifyÚpre_llm_callÚpost_llm_callÚon_session_endÚpost_tool_callÚpre_api_requestÚon_session_startÚpost_api_requestÚapi_request_errorÚpre_auxiliary_callÚpost_auxiliary_callÚtransform_llm_outputÚtransform_tool_resultÚtransform_terminal_outputzSet[str]Ú_HOOK_TIMEOUT_BOUNDED_HOOKSÚpre_tool_callÚ_HOOK_TIMEOUT_FAIL_CLOSED_HOOKSÚsubagent_stopÚ_HOOK_CALLER_THREAD_HOOKSg      N@é   z;pre_tool_call plugin callback timed out or is still runningc           
     óŒ   — t        |dt        |«      «      }d| › d|› dt        |«      j                  › dt	        |«      dd › �dœS )	uý   Block directive for a fail-closed hook whose callback raised: names the callback and the
    error (truncated â€” a hook that embeds tool args in its exception must not grow the tool
    result) so the operator can tell a crashing guard from a slow one.Ú__name__Úblockz plugin callback z raised z: NéÈ   ©ÚactionÚmessage)ÚgetattrÚreprÚtyper#   Ústr)Ú	hook_nameÚcbÚexcÚcallback_names       ú@/home/ubuntu/.hermes/hermes-agent/hermes_cli/plugins_dispatch.pyÚ_policy_error_block_directiver2   ;   s^   € ô ˜B 
¬D°«HÓ5€MØØ#˜Ð$5°m°_ÀHÌTÐRUËY×M_ÑM_ÐL`Ð`bÔcfÐgjÓckÐlpÐmpÐcqÐbrÐsñuð uó    Úafter_memoryi   é    i@  z^[a-z0-9][a-z0-9._-]{0,127}$z## Plugin Context: z%<!-- hermes-plugin-sections:start -->z#<!-- hermes-plugin-sections:end -->c                ób   — t        | t        «      xr t        t        j	                  | «      «      S )zDReturn whether *value* is a stable, heading-safe section identifier.)Ú
isinstancer,   ÚboolÚ_SYSTEM_PROMPT_SECTION_ID_REÚ	fullmatch)Úvalues    r1   Ú!is_valid_system_prompt_section_idr<   O   s&   € ä�eœSÓ!ÒY¤dÔ+G×+QÑ+QÐRWÓ+XÓ&YÐYr3   c                ó4   — t         › | › dt        |«      › d|› �S )zJRender an auditable, length-framed block recoverable from the full prompt.z"
<!-- hermes-plugin-section-chars:z -->

)Ú%_SYSTEM_PROMPT_SECTION_HEADING_PREFIXÚlen)Ú
section_idÚcontents     r1   Úformat_system_prompt_sectionrB   T   s.   € ô 1Ð
1°*°ð >,Ü,/°«L¨>¸À'Àð	LðMr3   c                ó¶   — | sy| D �cg c]"  }t        |j                  |j                  «      ‘Œ$ }}t        › d�dj	                  |«      z   dt
        › �z   S c c}w )z=Render the canonical container used for persistence recovery.Ú ú
z

)rB   ÚidrA   ÚPLUGIN_SECTIONS_STARTÚjoinÚPLUGIN_SECTIONS_END)ÚsectionsÚitemÚblockss      r1   Úformat_system_prompt_sectionsrM   [   s[   € áØØNVÖWÀdÔ*¨4¯7©7°D·L±LÕAÐW€FÐWÜ#Ð$ BÐ'¨&¯+©+°fÓ*=Ñ=À"ÔEXÐDYÐ@ZÑZÐZùò Xs   ˆ'AÚhermesé   é@   T)Úfrozenc                  óD   — e Zd ZU dZded<   ded<   ded<   ded<   ded	<   y
)ÚPluginSystemPromptSectionz:A plugin-owned section rendered once for each new session.r,   rF   z.Union[str, Callable[[Mapping[str, Any]], str]]rA   ÚpositionÚintÚ	max_charsÚpluginN©r#   Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__© r3   r1   rS   rS   l   s   … áDàƒGØ;Ó;ØƒMØƒNØ„Kr3   rS   c                  ó:   — e Zd ZU dZded<   ded<   ded<   ded<   y)Ú!RenderedPluginSystemPromptSectionz4Validated prompt bytes frozen on the owning AIAgent.r,   rF   rA   rT   rW   NrX   r]   r3   r1   r_   r_   w   s   … á>àƒGØƒLØƒMØ„Kr3   r_   c                  ó&   — e Zd ZU dZded<   ded<   y)Ú_EventSubscriptionz%Host-owned subscription ledger entry.r,   Úownerr   ÚcallbackNrX   r]   r3   r1   ra   ra   �   s   … á/àƒJØÔr3   ra   c                  óN   — e Zd ZU dZded<   ded<   ded<   ded	<   ded
<   ded<   y)Ú_QueuedPluginEventz9Immutable dispatch envelope consumed by the event worker.r,   ÚeventúDict[str, Any]Úpayloadztuple[_EventSubscription, ...]ÚsubscriptionsrU   ÚdepthÚ
generationzcontextvars.ContextÚcontextNrX   r]   r3   r1   re   re   ‰   s(   … áCàƒJØÓØ1Ó1ØƒJØƒOð !Ô r3   re   g      >@g     À‚@c                ób   — dD ]*  }| j                  |«      }t        |t        «      sŒ%|sŒ(|c S  y)uü  Identity of the call this callback fires for, or ``None`` when the event has none.

    Concurrent invocations of the same tool in one session must not collapse into one
    gate key: they are different work, and treating the second as a duplicate drops the
    hook as if a callback had timed out (upstream #98382). The identity is already in the
    payload; nothing new is plumbed. Deliberately not ``api_request_id`` â€” one API request
    carries many tool calls, which would re-collapse the keys.
    )Útool_call_idÚturn_idN)Úgetr7   r,   )ÚkwargsÚfieldr;   s      r1   Ú_hook_call_identityrs   ž   s7   € ð -ò ˆØ—
‘
˜5Ó!ˆÜ�eœSÕ!¢eØŠLðð r3   c                óB   — |dk  s| t         v ry| t        v xs | t        v S )zCWhether *hook_name* should run under the non-blocking timeout path.r   F)r    r   r   )r-   Útimeouts     r1   Ú_hook_uses_callback_timeoutrv   ®   s+   € à�!‚|�yÔ$=Ñ=ØØÔ3Ð3Òc°yÔDcÐ7cÐcr3   c                  ó  — e Zd Zedd„«       Zedd„«       Zdd„Zddœ	 	 	 	 	 	 	 	 	 	 	 dd„Z	 	 	 	 	 	 	 	 	 	 dd„Z	dd„Z
dd	„Zdd
„Zd d„Zd!d„Zd"d„Zd#d$d„Zd%d„Zd&d„Zdd„Zd'd„Z	 	 	 	 d(d„Ze	 	 	 	 	 	 d)d„«       Zd*d„Zd+d„Zy),ÚPluginDispatchMixinc                ó¾  — 	 t        j                  | «      j                  }t        d„ |j                  «       D «       «      rt        |«      S t         j                  j                  t         j                  j                  h}|j                  «       D ��ci c]  \  }}||v r||   j                  |v r||“Œ c}}S # t        t        f$ r t        |«      cY S w xY wc c}}w )z¢The slice of *payload* a callback accepts: everything for ``**kwargs`` (or
        un-introspectable) callbacks, only declared names for narrow legacy signatures.c              3  ój   K  — | ]+  }|j                   t        j                  j                  k(  –— Œ- y ­w©N)ÚkindÚinspectÚ	ParameterÚVAR_KEYWORD)Ú.0Úps     r1   ú	<genexpr>z<PluginDispatchMixin._hook_callback_kwargs.<locals>.<genexpr>¾   s%   è ø€ ÒT¸1ˆq�v‰vœ×*Ñ*×6Ñ6Õ6ÑTùs   ‚13)r}   Ú	signatureÚ
parametersÚ	TypeErrorÚ
ValueErrorÚdictÚanyÚvaluesr~   ÚPOSITIONAL_OR_KEYWORDÚKEYWORD_ONLYÚitemsr|   )rc   rh   r„   Úkeyword_kindsÚnamer;   s         r1   Ú_hook_callback_kwargsz)PluginDispatchMixin._hook_callback_kwargs¶   sÉ   € ð	!Ü ×*Ñ*¨8Ó4×?Ñ?ˆJô ÑTÀ
×@QÑ@QÓ@SÔTÔTÜ˜“=Ð Ü ×*Ñ*×@Ñ@Ä'×BSÑBS×B`ÑB`Ðaˆà+2¯=©=«?÷
Ù'˜D %Ø�zÑ! j°Ñ&6×&;Ñ&;¸}Ñ&Lð �%‰Kó
ð 	
øô œ:Ð&ò 	!Ü˜“=Ò ð	!üó

s   ‚B9 Â"CÂ9CÃCc           
     óJ   — ddl m}  | |di | j                  ||«      ¤Ž«      S )aB  Invoke a hook while withholding additive fields from narrow legacy callbacks.

        An ``async def`` callback returns a coroutine; resolve it the way plugin slash commands
        are (loop-safe), otherwise the bare coroutine object is appended to the results and the
        plugin's body never runs (#12449).
        r   ©Úresolve_plugin_command_resultr]   )Úhermes_cli.pluginsr’   r�   )Úclsrc   rh   r’   s       r1   Ú_invoke_hook_callbackz)PluginDispatchMixin._invoke_hook_callbackÆ   s*   € õ 	EÙ,©XÑ-e¸×8QÑ8QÐRZÐ\cÓ8dÑ-eÓfÐfr3   c           	     ó  — ddl m} |dk7  r|j                  dt        «       g } |«       }t	        ||«      }|t
        v }| j                  j                  |g «      D ]a  }	 |r7| j                  ||||«      }	|	t        u r-|r|j                  dt        dœ«       Œ=| j                  ||«      }	|	�|j                  |	«       Œc |S # t        t        f$ r<}
| j                  ||||
«       |r|j                  t!        |||
«      «       Y d}
~
Œ¬d}
~
ww xY w)a>  Call all callbacks for *hook_name*; return their non-``None`` results.

        Payloads evolve additively: ``**kwargs`` callbacks get everything, narrow signatures only
        what they declare. Each callback is isolated. Bounded hooks and ``pre_tool_call`` run under
        ``plugins.hook_callback_timeout`` (worker abandoned, never joined); ``pre_tool_call`` fails
        closed with a block directive, others skip. ``_HOOK_CALLER_THREAD_HOOKS`` always run on the
        caller thread. ``pre_llm_call`` may return ``{"context": "..."}`` (or a str) to inject.
        r   ©Ú_resolve_hook_callback_timeoutÚgateway_platform_eventÚtelemetry_schema_versionr$   r&   N)r“   r˜   Ú
setdefaultr   rv   r   Ú_hooksrp   Ú_run_hook_callback_boundedÚ_HOOK_SKIPPEDÚappendÚ$_PRE_TOOL_CALL_TIMEOUT_BLOCK_MESSAGEr•   Ú	ExceptionÚ
SystemExitÚ_report_hook_failurer2   )Úselfr-   rq   r˜   Úresultsru   Úuse_timeoutÚfail_closedr.   Úretr/   s              r1   Úinvoke_hookzPluginDispatchMixin.invoke_hookÑ   s  € õ 	Fð Ð0Ò0Ø×ÑÐ8Ô:QÔRØˆÙ0Ó2ˆÜ1°)¸WÓEˆØÔ#BÐBˆØ—+‘+—/‘/ )¨RÓ0ò 	VˆBðVÙØ×9Ñ9¸)ÀRÈÐQXÓY�CØœmÑ+Ù&Ø#ŸN™N°gÔJnÑ+oÔpØ à×4Ñ4°R¸Ó@�CØ�?Ø—N‘N 3Ô'øð	Vð  ˆøô	 œzÐ*ò VØ×)Ñ)¨)°R¸ÀÔEÙØ—N‘NÔ#@ÀÈBÐPSÓ#TÔUÿøðVús   Á8CÂ%CÃDÃ2DÄDÚHook©Úsurfacec               ó¸  — t        |dt        |«      «      }|t        |dd«      t        |d|«      t        |«      j                  t	        |«      dd f}|| j
                  v rt        j                  d||||«       y| j
                  j                  |«       t        j                  d|||||j                  «       d	j                  t        |«      «      xs d
«       y)uâ  One WARNING per distinct (hook, callback, error); identical repeats at DEBUG.

        A callback whose signature names a parameter the hook never sends (``tool_data`` instead
        of ``tool_name``/``args``) fails identically on every tool call â€” ~1700 WARNING lines an
        hour that bury real signals (#111922). The first report names the fields the hook does
        provide so the plugin author can fix the signature. The key names the callback by
        module/qualname (not ``id()``, which CPython recycles across plugin reloads) and
        truncates the message so a hook that embeds tool args in its error cannot grow the set
        per call; the set is cleared on unload alongside the timeout-suppression map.
        r#   rY   rD   rZ   Nr%   z$%s '%s' callback %s raised again: %szd%s '%s' callback %s raised: %s (%s provides: %s; identical failures are logged at DEBUG from now on)z, z	no fields)r)   r*   r+   r#   r,   Ú_hook_failures_reportedÚloggerÚdebugÚaddÚwarningÚlowerrH   Úsorted)r¤   r-   r.   rq   r/   r¬   r0   Úkeys           r1   r£   z(PluginDispatchMixin._report_hook_failureõ   sÃ   € ô    J´°R³Ó9ˆØœ' " l°BÓ7¼ÀÀ^ÐUbÓ9cÜ�C‹y×!Ñ!¤3 s£8¨D¨S >ð3ˆà�$×.Ñ.Ñ.Ü�L‰LÐ?ÀÈ)ÐUbÐdgÔhØØ×$Ñ$×(Ñ(¨Ô-Ü�‰ØrØ�Y ¨s°G·M±M³OÀTÇYÁYÌvÐV\Ë~ÓE^ÒEmÐbmõ	or3   c                ó   ‡ ‡‡‡‡‡‡‡‡‡‡— t        ‰dt        ‰«      «      }|t        ‰«      fŠg ‰¢t        ‰«      ‘­Št	        «       Š‰ j
                  5  ‰ j                  j                  ‰«      }‰‰ j                  v s|�=|t        j                  «       kD  r&t        j                  d||«       t        cddd«       S ‰ j                  j                  ‰«      }|rNt        |«      t         k\  r<t        j                  d||t        ‰dd«      t        |«      «       t        cddd«       S |�‰ j                  j#                  ‰d«       ‰‰ j                  ‰<   ddd«       t%        j&                  «       Št)        j*                  «       Ši Ši Šdˆˆ ˆˆfd„Šdˆˆˆˆˆˆˆˆ fd„}t)        j,                  |d	|› �dd
 d¬«      }		 |	j/                  «        ‰j3                  |¬«      s±‰ j
                  5  t        j                  «       ‰ j4                  z   ‰ j                  ‰<   ‰ j                  j                  ‰«      ‰u r3‰ j                  j7                  ‰t9        «       «      j;                  ‰«       ddd«       t        j                  d|||«       t        S d‰v r‰d   ‚‰j                  d«      S # 1 sw Y   �ŒXxY w# t0        $ r/}
 ‰«        t        j                  d|||
«       t        cY d}
~
S d}
~
ww xY w# 1 sw Y   Œ‰xY w)a  Run one callback on a daemon worker with a wall-clock cap; ``_HOOK_SKIPPED`` when
        suppressed, still running for this call id, over the abandoned-worker cap, timed out
        (worker abandoned, never joined), or the worker could not be started. Exceptions
        propagate.r#   NzKHook '%s' callback %s skipped after previous timeout or while still runningu“   Hook '%s' callback %s (%s) skipped: %d abandoned worker(s) still running â€” the plugin is hung; fix or disable it (retried when a worker finishes)rY   zunknown pluginc                 óR  •— ‰j                   5  ‰j                  j                  ‰«      ‰u rh‰j                  j                  ‰d «       ‰j                  j                  ‰«      } | �/| j                  ‰«       | s‰j                  j                  ‰d «       d d d «       y # 1 sw Y   y xY wr{   )Ú_hook_timeout_lockÚ_hook_running_callbacksrp   ÚpopÚ_hook_abandonedÚdiscard)Ú	abandonedÚgate_keyr¤   Úsuppression_keyÚtokens    €€€€r1   Ú_release_tokenzFPluginDispatchMixin._run_hook_callback_bounded.<locals>._release_token8  s—   ø€ Ø×(Ñ(ñ LØ×/Ñ/×3Ñ3°HÓ=ÀÑFØ×0Ñ0×4Ñ4°X¸tÔDØ $× 4Ñ 4× 8Ñ 8¸Ó I�IØ Ð,Ø!×)Ñ)¨(Ô3Ù(Ø ×0Ñ0×4Ñ4°_ÀdÔK÷L÷ Lñ Lús   ŽBBÂB&c                 óä   •— 	 ‰j                  ‰j                  ‰‰«      ‰d<    ‰«        ‰j                  «        y # t        $ r} | ‰d<   Y d } ~ Œ+d } ~ ww xY w#  ‰«        ‰j                  «        w xY w)Nr;   r/   )Úrunr•   ÚBaseExceptionÚset)	r/   rÁ   r.   rl   ÚdoneÚfailurerq   Úoutcomer¤   s	    €€€€€€€€r1   Ú_runnerz?PluginDispatchMixin._run_hook_callback_bounded.<locals>._runnerB  sc   ø€ ðØ#*§;¡;¨t×/IÑ/IÈ2ÈvÓ#V�˜Ñ ñ Ô Ø—‘•
øô	 !ò %Ø!$�˜–ûð%ûñ Ô Ø—‘•
ús'   ƒ ; »	AÁAÁ	A ÁAÁA ÁA/zhermes-hook-é(   T)ÚtargetrŽ   Údaemonu=   Hook '%s' callback %s worker failed to start: %s â€” skipping©ru   u6   Hook '%s' callback %s timed out after %gs â€” skippingr/   r;   ©ÚreturnÚNone)r)   r*   rF   rs   Úobjectr¸   Ú_hook_timeout_suppressed_untilrp   r¹   ÚtimeÚ	monotonicr¯   r²   rž   r»   r?   Ú_HOOK_MAX_ABANDONED_WORKERSrº   ÚcontextvarsÚcopy_contextÚ	threadingÚEventÚThreadÚstartÚRuntimeErrorÚwaitÚ!_hook_timeout_suppression_secondsr›   rÅ   r±   )r¤   r-   r.   rq   ru   r0   Úsuppressed_untilr½   rÉ   Úthreadr/   rÁ   rl   rÆ   rÇ   r¾   rÈ   r¿   rÀ   s   ` ``       @@@@@@@@r1   r�   z.PluginDispatchMixin._run_hook_callback_bounded  sÔ  ÿú€ ô    J´°R³Ó9ˆð %¤b¨£fÐ-ˆØB�_ÐBÔ&9¸&Ó&AÑBˆÜ“ˆØ×$Ñ$ñ 	;Ø#×BÑB×FÑFÀÓWÐØ˜D×8Ñ8Ñ8Ø(Ð4Ð9IÌDÏNÉNÓL\Ò9\Ü—‘ð5Ø6?ÀôPô %÷	;ñ 	;ð ×,Ñ,×0Ñ0°ÓAˆIÙœS ›^Ô/JÒJÜ—‘ð]à˜}¬g°b¸,ÐHXÓ.YÔ[^Ð_hÓ[iôkô %÷)	;ñ 	;ð*  Ð+Ø×3Ñ3×7Ñ7¸ÈÔNØ5:ˆD×(Ñ(¨Ñ2÷/	;ô2 ×*Ñ*Ó,ˆÜ�‰Ó ˆØ"$ˆØ,.ˆ÷	Lð 	L÷	ô 	ô ×!Ñ!¨¸ÀmÀ_Ð7UÐVYÐWYÐ7ZÐcgÔhˆð	!Ø�L‰LŒNð �y‰y ˆyÔ)Ø×(Ñ(ñ Zô —N‘NÓ$ t×'MÑ'MÑMð ×3Ñ3°OÑDð
 ×/Ñ/×3Ñ3°HÓ=ÀÑFØ×(Ñ(×3Ñ3°OÄSÃUÓK×OÑOÐPXÔY÷Zô �N‰NØHÈ)ÐUbÐdkômä Ð Ø�GÑØ˜%‘.Ð Ø�{‰{˜7Ó#Ð#÷S	;ñ 	;ûôh ò 	!ÙÔÜ�N‰NØOØ˜=¨#ô/ô !Õ ûð	!ú÷Zð ZúsE   ÁAJ,Â>A!J,Ä)-J,Æ?J9 Ç-A?K4Ê,J6Ê9	K1Ë$K,Ë&K1Ë,K1Ë4K=c                óÜ   — t        |«      st        d«      ‚| j                  5  | j                  j	                  |g «      j                  t        ||«      «       ddd«       y# 1 sw Y   yxY w)z=Add an owner-tagged event subscription in registration order.z*Event subscriber callback must be callableN)Úcallabler…   Ú_event_lockÚ_subscriptionsr›   rŸ   ra   )r¤   rb   rf   rc   s       r1   Ú_subscribe_eventz$PluginDispatchMixin._subscribe_evente  sc   € ä˜Ô!ÜÐHÓIÐIØ×Ññ 	bØ×Ñ×*Ñ*¨5°"Ó5×<Ñ<Ô=OÐPUÐW_Ó=`Ôa÷	b÷ 	bñ 	bús   £6A"Á"A+c                ó\  — d}| j                   5  t        | j                  «      D ]i  }| j                  |   }|D �cg c]  }|j                  |k7  sŒ|‘Œ }}|t	        |«      t	        |«      z
  z  }|r|| j                  |<   Œ]| j                  |= Œk 	 ddd«       |S c c}w # 1 sw Y   |S xY w)aÝ  Remove every subscription owned by *owner*; return the count. Queued envelopes re-check
        membership per callback, so this also cancels already-snapshotted deliveries.

        TODO(#64229): when the central plugin ownership ledger / registration handles land, route this
        owner-tagged bookkeeping through that ledger so per-plugin unload cancels event subscriptions
        alongside every other registration surface. This method is the integration seam.
        r   N)rã   Úlisträ   rb   r?   )r¤   rb   Úremovedrf   ÚentriesÚentryÚretaineds          r1   Ú_remove_plugin_subscriptionsz0PluginDispatchMixin._remove_plugin_subscriptionsl  s¶   € ð ˆØ×Ññ 	3Ü˜d×1Ñ1Ó2ò 3�Ø×-Ñ-¨eÑ4�Ø/6ÖO e¸%¿+¹+ÈÓ:NšEÐO�ÐOØœ3˜w›<¬#¨h«-Ñ7Ñ7�ÙØ19�D×'Ñ'¨Ò.à×+Ñ+¨EÑ2ñ3÷	3ð ˆùò P÷	3ð ˆús"   �,B!»BÁBÁ=B!ÂB!Â!B+c                óÌ   — | j                   }|�|j                  «       ry t        j                  | j                  | j
                  fdd¬«      }|| _         |j                  «        y )Nzhermes-plugin-eventsT)rË   ÚargsrŽ   rÌ   )Ú_event_workerÚis_aliverØ   rÚ   Ú_event_worker_loopÚ_event_queuerÛ   )r¤   Úworkers     r1   Ú_ensure_event_worker_lockedz/PluginDispatchMixin._ensure_event_worker_locked€  s\   € Ø×#Ñ#ˆØÐ &§/¡/Ô"3ØÜ×!Ñ!Ø×*Ñ*°$×2CÑ2CÐ1EÐLbØô
ˆð $ˆÔØ�‰�r3   c                óš  — 	 |j                  «       }	 |t        u r5	 |t        ur| j                  |j                  «       |j	                  «        y | j                  |«       |t        ur| j                  |j                  «       |j	                  «        Œ“# |t        ur| j                  |j                  «       |j	                  «        w xY wr{   )rp   Ú_EVENT_WORKER_STOPÚ_mark_event_donerk   Ú	task_doneÚ_deliver_event)r¤   Údispatch_queuerK   s      r1   rñ   z&PluginDispatchMixin._event_worker_loop‹  s´   € ØØ!×%Ñ%Ó'ˆDð+ØÔ-Ñ-Øð Ô1Ñ1Ø×)Ñ)¨$¯/©/Ô:Ø×(Ñ(Õ*ð	 ×#Ñ# DÔ)àÔ1Ñ1Ø×)Ñ)¨$¯/©/Ô:Ø×(Ñ(Ô*ð øð Ô1Ñ1Ø×)Ñ)¨$¯/©/Ô:Ø×(Ñ(Õ*ús   “B ÁB Â5C
c                óà   — | j                   5  | j                  j                  |d«      }|dkD  r|dz
  | j                  |<   | j                   j                  «        d d d «       y # 1 sw Y   y xY w)Nr   é   )Ú_event_idleÚ_event_pending_by_generationrp   Ú
notify_all)r¤   rk   Úpendings      r1   r÷   z$PluginDispatchMixin._mark_event_done—  se   € Ø×Ññ 	*Ø×7Ñ7×;Ñ;¸JÈÓJˆGØ˜Š{Ø@GÈ!Á�×1Ñ1°*Ñ=Ø×Ñ×'Ñ'Ô)÷		*÷ 	*ñ 	*ús   �AA$Á$A-c           
     óÈ  ‡— ddl m} | j                  5  |j                  | j                  k7  r
	 ddd«       y	 ddd«       t        | j                  dd«      }|j                  | j                  _        	 |j                  D ]Ù  Š| j                  5  |j                  | j                  k7  r	 ddd«        n¨t        ˆfd„| j                  j                  |j                  g «      D «       «      s
	 ddd«       Œv	 ddd«       ‰j                  }	  | |j                  j!                  «       j"                  |fi t!        j$                  |j&                  «      ¤Ž«       ŒÛ || j                  _        y# 1 sw Y   �Œ7xY w# 1 sw Y   Œ…xY w# t(        t*        f$ r5}| j-                  |j                  ||j&                  |d¬«       Y d}~�ŒFd}~ww xY w# || j                  _        w xY w)z9Deliver one queued event on the host-owned worker thread.r   r‘   Nr;   c              3  ó&   •K  — | ]  }|‰u –— Œ
 y ­wr{   r]   )r€   ÚcurÚsubscriptions     €r1   r‚   z5PluginDispatchMixin._deliver_event.<locals>.<genexpr>¬  s   øè ø€ Òf°s˜s lÔ2Ñfùs   ƒrÙ   r«   )r“   r’   rã   rk   Ú_event_generationr)   Ú_emit_depthrj   r;   ri   rˆ   rä   rp   rf   rc   rl   ÚcopyrÃ   Údeepcopyrh   r¡   r¢   r£   )r¤   rK   r’   Úprevious_depthrc   r/   r  s         @r1   rù   z"PluginDispatchMixin._deliver_eventž  s¯  ø€ åDØ×Ññ 	Ø�‰ $×"8Ñ"8Ò8Ø÷	ð 	Ø8÷	ô ! ×!1Ñ!1°7¸AÓ>ˆØ!%§¡ˆ×ÑÔð	4Ø $× 2Ñ 2ò h�Ø×%Ñ%ñ !Ø—‘¨$×*@Ñ*@Ò@Ø÷!ñ !ô Óf¸d×>QÑ>Q×>UÑ>UÐVZ×V`ÑV`ÐbdÓ>eÔfÔfØ ÷!ð !ð g÷	!ð (×0Ñ0�ðhá1Ø/˜Ÿ™×)Ñ)Ó+×/Ñ/°ÑX¼D¿M¹MÈ$Ï,É,Ó<WÑXõZðhð  &4ˆD×ÑÕ"÷-	ñ 	ú÷!ð !ûô "¤:Ð.ò hà×-Ñ-¨d¯j©j¸(ÀDÇLÁLÐRUÐ_fÐ-×gÒgûðhûð &4ˆD×ÑÕ"úsl   ”E.Á4G ÂE;Â*
G Â49E;Ã-	G Ã7G ÄAFÅG Å.E8Å;F	Æ G ÆGÆ*GÇ G ÇGÇG ÇG!c                ó¤   ‡ ‡— ‰ j                   5  ‰ j                  Š‰ j                   j                  ˆˆ fd„|¬«      cddd«       S # 1 sw Y   yxY w)zCWait for the current event generation to become idle (test helper).c                 óB   •— ‰j                   j                  ‰ d«      dk(  S )Nr   )rþ   rp   )rk   r¤   s   €€r1   ú<lambda>z>PluginDispatchMixin._wait_for_event_dispatch.<locals>.<lambda>¾  s    ø€ ˜×9Ñ9×=Ñ=¸jÈ!ÓLÐPQÑQ€ r3   rÍ   N)rý   r  Úwait_for)r¤   ru   rk   s   ` @r1   Ú_wait_for_event_dispatchz,PluginDispatchMixin._wait_for_event_dispatch¹  sP   ù€ à×Ññ 	dØ×/Ñ/ˆJØ×#Ñ#×,Ñ,ÜQÐ[bð -ó d÷	d÷ 	dò 	dús   �-AÁAc           
     ó8  — t        | j                  dd«      }|t        k\  rt        j	                  dt        |«       yd}| j
                  5  t        | j                  j                  |g «      «      }|s
	 ddd«       y| j                  }| j                  j                  |d«      }|t        k\  r%t        j	                  |t        |«       	 ddd«       yt        |t        |«      ||dz   |t        j                  «       ¬«      }	 | j                   j#                  |«       |dz   | j                  |<   | j)                  «        t+        |«      cddd«       S # t$        j&                  $ r& t        j	                  |t        |«       Y ddd«       yw xY w# 1 sw Y   yxY w)zµQueue *event* without blocking; return the subscriber count scheduled. Pending work is
        bounded per generation so a blocking subscriber costs one worker and later emits drop.r;   r   uo   Event bus recursion cap (%d) exceeded while dispatching '%s' â€” dropping this emit to prevent an infinite loopuU   Event bus pending budget (%d) exhausted while dispatching '%s' â€” dropping this emitNrü   )rf   rh   ri   rj   rk   rl   )r)   r  Ú_EVENT_EMIT_DEPTH_CAPr¯   r²   rã   Útuplerä   rp   r  rþ   Ú_EVENT_PENDING_CAPre   r‡   rÖ   r×   rò   Ú
put_nowaitÚqueueÚFullrô   r?   )	r¤   rf   rh   rj   Ú
budget_msgri   rk   r   rK   s	            r1   Ú_dispatch_eventz#PluginDispatchMixin._dispatch_eventÀ  s†  € ô ˜×(Ñ(¨'°1Ó5ˆØÔ)Ò)Ü�N‰NðEÜF[Ð]bôdð Ølˆ
Ø×Ññ 	&Ü! $×"5Ñ"5×"9Ñ"9¸%ÀÓ"DÓEˆMÙ Ø÷	&ð 	&ð ×/Ñ/ˆJØ×7Ñ7×;Ñ;¸JÈÓJˆGØÔ,Ò,Ü—‘˜zÔ+=¸uÔEØ÷	&ð 	&ô &Ø¤T¨'£]À-ÐW\Ð_`ÑW`Ø%¬{×/GÑ/GÓ/IôKˆDðØ×!Ñ!×,Ñ,¨TÔ2ð =DÀa¹KˆD×-Ñ-¨jÑ9Ø×,Ñ,Ô.Ü�}Ó%÷'	&ñ 	&øô —:‘:ò Ü—‘˜zÔ+=¸uÔEØ÷!	&ð 	&ðú÷	&ð 	&ús=   Á)FÁ=AFÃ/FÄEÄ,FÅ.FÆFÆFÆFÆFc                óJ   — t        | j                  j                  |«      «      S )z@Return True when at least one callback is registered for a hook.)r8   rœ   rp   ©r¤   r-   s     r1   Úhas_hookzPluginDispatchMixin.has_hookß  s   € ä�D—K‘K—O‘O IÓ.Ó/Ð/r3   c           	   ‹  óì  K  — ddl m} |dk7  r|j                  dt        «       g } |«       }t	        ||«      }|t
        v }| j                  j                  |g «      D ]{  }t        |dt        |«      «      }		  |d
i | j                  ||«      ¤Ž}
t        j                  |
«      r"|rt        j                  |
|«      n|
ƒ d{  –—† }
|
�|j                  |
«       Œ} |S 7 Œ# t        j                   $ r5 t"        j%                  d||	|«       |r|j                  dt&        d	œ«       Y ŒÉt(        t*        f$ r=}| j-                  ||||«       |r|j                  t/        |||«      «       Y d}~�Œd}~ww xY w­w)ué  :meth:`invoke_hook` for callers that are already on an event loop.

        Same payload narrowing, per-callback isolation and result contract. The difference is
        where an ``async def`` callback runs: here it is awaited on the caller's own loop, so a
        callback that awaits anything scheduled on that loop can make progress. Through the
        sync path it runs on a helper thread while the caller blocks in ``done.wait()`` â€” on the
        gateway that stalls the whole event loop for the callback's duration. Sync callbacks
        run inline. Bounded hooks keep ``plugins.hook_callback_timeout`` via ``asyncio.wait_for``
        (the coroutine is cancelled, not abandoned); a timed-out ``pre_tool_call`` fails closed.
        r   r—   r™   rš   r#   Nz+Hook '%s' callback %s timed out after %.0fsr$   r&   r]   )r“   r˜   r›   r   rv   r   rœ   rp   r)   r*   r�   r}   ÚisawaitableÚasyncior  rŸ   ÚTimeoutErrorr¯   r²   r    r¡   r¢   r£   r2   )r¤   r-   rq   r˜   r¥   ru   r¦   r§   r.   r0   r¨   r/   s               r1   Úainvoke_hookz PluginDispatchMixin.ainvoke_hookã  sg  è ø€ õ 	FØÐ0Ò0Ø×ÑÐ8Ô:QÔRØˆÙ0Ó2ˆÜ1°)¸WÓEˆØÔ#BÐBˆØ—+‘+—/‘/ )¨RÓ0ò 	VˆBÜ# B¨
´D¸³HÓ=ˆMðVÙÑB˜4×5Ñ5°b¸&ÓAÑB�Ü×&Ñ& sÔ+ÙCN¤×!1Ñ!1°#°wÔ!?ÐTW×X�CØ�?Ø—N‘N 3Ô'øð	Vð$ ˆð Yùô ×'Ñ'ò iÜ—‘ÐLÈiÐYfÐhoÔpÙØ—N‘N¨gÔBfÑ#gÔhùÜœzÐ*ò Vð ×)Ñ)¨)°R¸ÀÔEÙØ—N‘NÔ#@ÀÈBÐPSÓ#TÔUÿùðVüsP   ‚A4E4Á7A
CÃCÃCÃE4ÃCÃAE1Ä$E4Ä&E1Ä42E,Å&E4Å,E1Å1E4c                óL   — t        | j                  j                  |d«      «      S )z<Return a stable snapshot of callbacks registered for a hook.r]   )r  rœ   rp   r  s     r1   Úiter_hook_callbacksz'PluginDispatchMixin.iter_hook_callbacks	  s   € ä�T—[‘[—_‘_ Y°Ó3Ó4Ð4r3   c           
     óF  — t        j                  t        |«      «      }g }t        t        «      t        t
        «      z   dz   }t        | j                  j                  «       «      D �]7  \  }}t        |«      t        k\  r&t        j                  d|j                  t        «       Œ?| j                  ||«      }|€ŒTt        t        |j                  |«      «      }|r|dz  }||z   t        kD  r1t        j                  d|j                  |j                   t        «       Œ·|j#                  t%        |j                  ||j&                  |j                   ¬«      «       ||z  }t        j)                  d|j                  |j                   |j&                  t        |«      «       �Œ: |S )z?Render all registered sections deterministically and fail open.é   zVPlugin system prompt section %s exceeded the section-count budget (%d) and was skippedzePlugin system prompt section %s (%s) exceeded the aggregate session budget (%d chars) and was skipped)rF   rA   rT   rW   zCSession plugin prompt section: id=%s plugin=%s position=%s chars=%d)ÚtypesÚMappingProxyTyper‡   r?   rG   rI   r´   Ú_system_prompt_sectionsrŒ   ÚMAX_SYSTEM_PROMPT_SECTIONSr¯   r²   rF   Ú_render_prompt_section_textrB   Ú&MAX_SYSTEM_PROMPT_SECTIONS_TOTAL_CHARSrW   rŸ   r_   rT   Úinfo)	r¤   Úsession_infoÚfrozen_infoÚrenderedÚtotal_charsÚ_section_idÚsectionÚtextÚrendered_charss	            r1   Úrender_system_prompt_sectionsz1PluginDispatchMixin.render_system_prompt_sections  sq  € ô ×,Ñ,¬T°,Ó-?Ó@ˆØ<>ˆÜÔ/Ó0´3Ô7JÓ3KÑKÈaÑOˆÜ$*¨4×+GÑ+G×+MÑ+MÓ+OÓ$Pó 	=Ñ ˆK˜Ü�8‹}Ô :Ò:Ü—‘ð2Ø3:·:±:Ô?Yô[ð Ø×3Ñ3°G¸[ÓIˆDØˆ|ØÜ Ô!=¸g¿j¹jÈ$Ó!OÓPˆNÙØ !Ñ#�Ø˜^Ñ+Ô.TÒTÜ—‘ð@ØAHÇÁÈWÏ^É^Ü:ô<ð Ø�O‰OÜ1Ø—z‘z¨4¸'×:JÑ:JÐSZ×SaÑSaôcôdð ˜>Ñ)ˆKÜ�K‰KØUÐW^×WaÑWaØ—‘ × 0Ñ 0´#°d³)ö=ð-	=ð2 ˆr3   c                óâ  ‡ — dˆ fd„}	 t        ‰ j                  «      r‰ j                  |«      n‰ j                  }t	        |t
        «      s |dt        |«      j                  «       y|j                  «       }|syt        |v st        |v r	 |d«       yt        |«      ‰ j                  kD  r |dt        |«      ‰ j                  «       y|S # t        t        f$ r} |d|«       Y d}~yd}~ww xY w)zUEvaluate one section; return its stripped text or None (with a warning) when skipped.c                ód   •— t        j                  d| z   ‰j                  ‰j                  g|¢­Ž  y )Nz%Plugin system prompt section %s (%s) )r¯   r²   rF   rW   )Údetailrî   r0  s     €r1   Ú_skipz>PluginDispatchMixin._render_prompt_section_text.<locals>._skip4  s-   ø€ Ü�N‰NØ7¸&Ñ@À'Ç*Á*ÈgÏnÉnðeØ_côer3   zraised and was skipped: %sNzreturned %s, not str; skippedz7contained a reserved persistence marker and was skippedz,exceeded max_chars (%d > %d) and was skipped)r6  r,   rî   r   rÏ   rÐ   )râ   rA   r¡   r¢   r7   r,   r+   r#   ÚstriprG   rI   r?   rV   )r0  r,  r7  r;   r/   r1  s   `     r1   r(  z/PluginDispatchMixin._render_prompt_section_text/  s×   ø€ õ
	eð	Ü4<¸W¿_¹_Ô4M�G—O‘O KÔ0ÐSZ×SbÑSbˆEô ˜%¤Ô%ÙÐ1´4¸³;×3GÑ3GÔHØØ�{‰{‹}ˆÙØÜ  DÑ(Ô,?À4Ñ,GÙÐKÔLØÜˆt‹9�w×(Ñ(Ò(ÙÐ@Ä#ÀdÃ)ÈW×M^ÑM^Ô_ØØˆøô œ:Ð&ò 	ÙÐ.°Ô4Üûð	ús   ‰2C ÃC.Ã	C)Ã)C.c                óJ   — t        | j                  j                  |«      «      S )zDReturn True when at least one callback is registered for middleware.)r8   Ú_middlewarerp   )r¤   r|   s     r1   Úhas_middlewarez"PluginDispatchMixin.has_middlewareK  s   € ä�D×$Ñ$×(Ñ(¨Ó.Ó/Ð/r3   c           	     óè   — g }| j                   j                  |g «      D ]  }	  |di |¤Ž}|�|j                  |«       Œ  |S # t        t        f$ r }| j                  ||||d¬«       Y d}~ŒMd}~ww xY w)zRCall middleware callbacks for *kind* (each isolated); return non-``None`` results.NÚ
Middlewarer«   r]   )r:  rp   rŸ   r¡   r¢   r£   )r¤   r|   rq   r¥   r.   r¨   r/   s          r1   Úinvoke_middlewarez%PluginDispatchMixin.invoke_middlewareO  sŠ   € àˆØ×"Ñ"×&Ñ& t¨RÓ0ò 	WˆBðWÙ‘l˜6‘l�Ø�?Ø—N‘N 3Ô'øð		Wð ˆøô œzÐ*ò Wà×)Ñ)¨$°°F¸CÈÐ)×VÑVûðWús   £AÁA1ÁA,Á,A1N)rc   r   rh   rg   rÏ   rg   )rc   r   rh   rg   rÏ   r   )r-   r,   rq   r   rÏ   ú	List[Any])r-   r,   r.   r   rq   rg   r/   rÄ   r¬   r,   rÏ   rÐ   )
r-   r,   r.   r   rq   rg   ru   ÚfloatrÏ   r   )rb   r,   rf   r,   rc   r   rÏ   rÐ   )rb   r,   rÏ   rU   rÎ   )rú   zqueue.Queue[Any]rÏ   rÐ   )rk   rU   rÏ   rÐ   )rK   re   rÏ   rÐ   )g       @)ru   r@  rÏ   r8   )rf   r,   rh   rg   rÏ   rU   )r-   r,   rÏ   r8   )r-   r,   rÏ   ztuple[Callable, ...])r+  úMapping[str, Any]rÏ   z'List[RenderedPluginSystemPromptSection])r0  rS   r,  rA  rÏ   úOptional[str])r|   r,   rÏ   r8   )r|   r,   rq   r   rÏ   r?  )r#   rY   rZ   Ústaticmethodr�   Úclassmethodr•   r©   r£   r�   rå   rì   rô   rñ   r÷   rù   r  r  r  r  r!  r3  r(  r;  r>  r]   r3   r1   rx   rx   µ   s-  „ Øò
ó ð
ð ògó ðgó"ðJ kqñoØðoØ"*ðoØ4BðoØIVðoØdgðoà	óoð0V$ØðV$Ø"*ðV$Ø4BðV$ØMRðV$à	óV$ópbóó(	ó
+ó*ó4ô6dó&ó>0ó$óL5ð Ø-ð à	0ó ðD ðØ*ðØ9Jðà	òó ðó60ôr3   rx   )r-   r,   r.   r   r/   rÄ   rÏ   zDict[str, str])r;   r   rÏ   r8   )r@   r,   rA   r,   rÏ   r,   )rJ   rç   rÏ   r,   )rq   rg   rÏ   rB  )r-   r,   ru   r@  rÏ   r8   )Ar[   Ú
__future__r   r  rÖ   r  r}   Úloggingr  ÚrerØ   rÓ   r$  Údataclassesr   Útypingr   r   r   r   r	   r
   r   r   Úhermes_cli.middlewarer   Ú	getLoggerr¯   r   r\   r   r    Ú!_HOOK_TIMEOUT_SUPPRESSION_SECONDSrÕ   r    r2   Ú	frozensetÚSYSTEM_PROMPT_SECTION_POSITIONSÚ'DEFAULT_SYSTEM_PROMPT_SECTION_MAX_CHARSÚMAX_SYSTEM_PROMPT_SECTION_CHARSr'  r)  Úcompiler9   r>   rG   rI   r<   rB   rM   ÚHERMES_EVENT_NAMESPACEr  r  rÑ   rö   rS   r_   ra   re   Ú_HOOK_CALLBACK_TIMEOUT_SECSÚ_MAX_HOOK_CALLBACK_TIMEOUT_SECSrž   rs   rv   rx   r]   r3   r1   ú<module>rU     s²  ðòõ #ã Û Û Û Û Û Û 	Û Û Û Ý !ß K× KÓ Kå 9à	ˆ×	Ñ	Ð/Ó	0€ò$)Ð ˜Xó ð .=Ð,=Ð  Ó =à'6Ð&7Ð ˜8Ó 7à$(Ð !àÐ Ø'dÐ $óuñ #,¨^Ð,<Ó"=Ð Ø*/Ð 'Ø"'Ð ØÐ Ø).Ð &Ø)˜rŸz™zÐ*IÓJÐ Ø(=Ð %Ø?Ð Ø;Ð óZó
Mó[ð "Ð àÐ àÐ Ù“XÐ ñ �$Ô÷ð ó ðñ �$Ô÷ð ó ðñ �$Ô÷ð ó ðñ �$Ô÷
!ð 
!ó ð
!ð #Ð Ø"'Ð Ù“€óó d÷eò er3   