+
    jHàjñ-  ã            	      ó²  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIH	t	H
t
HtHt ^ RIHt ^ RIHt ]P$                  ! ]4      tR5RR/R R	 llltR
 tRsR]R&   R R ltR6R R lltR tR6RR/R R llltR R ltR R ltR R ltRR R 3R R! ] 3R"R# R$ 3R%R& ]PB                  3/t"R']R(&   R) R* lt#R+RR,R/R- R. llt$R/ R0 lt%R1 R2 lt&R3 R4 lt'R# )7zÉCross-adapter helpers shared by gateway/platforms/* and plugins/platforms/*.

Kept dependency-light (stdlib + ``agent.secret_scope``) so every adapter can
import it at module top level without cycles.
)ÚannotationsN)ÚAnyÚCallableÚIterableÚOptional)ÚUnscopedSecretError)Ú
get_secretÚexternal_fallbackFc               ó(   € V ^8„  d   QhRRRRRRRR/# )é   ÚnameÚstrÚdefaultr   r	   ÚboolÚreturn© )Úformats   "Ú>/home/ubuntu/.hermes/hermes-agent/gateway/platforms/_shared.pyÚ__annotate__r      s)   € ÷ /ñ /˜Cð /¨#ð /ÈDð /Ð]`ñ /ó    c               óÜ   €  \        V R4      pVf.   V'       d&   \	        4       f   \        4       P                  V 4      pVe   V# T#   \         d    \        P                  ! T 4      p L\i ; i)aÌ  Scope-aware credential read with the default-profile startup fallback.

An installed profile secret scope is authoritative: a scoped miss returns
``default`` (never borrow another profile's value from ``os.environ``).
The DEFAULT profile constructs and sends *unscoped* under multiplexing,
where a bare ``get_secret`` raises ``UnscopedSecretError``; there
``os.environ`` is that profile's own value, so fall back to it.

``external_fallback`` adds one rung for startup gates that run before any
scope exists: an unscoped miss consults a one-shot build of the profile's
own secret scope, so externally managed credentials (Bitwarden etc., only
``BWS_ACCESS_TOKEN`` in ``.env``) are visible to ``check_requirements``.
N)Ú_scoped_get_secretÚ_UnscopedSecretErrorÚosÚgetenvÚ_current_scopeÚ_unscoped_profile_secretsÚget)r   r   r	   Úvals   &&$ r   Úget_scoped_secretr      se   € ðÜ   tÓ,ˆð ‚{×(¬^Ó-=Ò-EÜ'Ó)×-Ñ-¨dÓ3ˆØ’/ˆ3Ð. wÐ.øô	  ô Ü�iŠi˜‹oŠðús   ‚A Á!A+Á*A+c                 ó   € ^ RI Hp  V ! 4       # )é    ©Úcurrent_secret_scope)Úagent.secret_scoper#   r"   s    r   r   r   -   s   € Ý7ÙÓ!Ð!r   úOptional[dict]Ú_UNSCOPED_PROFILE_SECRETSc               ó   € V ^8„  d   QhRR/# )r   r   Údictr   )r   s   "r   r   r   5   s   € ÷ %ñ % 4ñ %r   c                 óÎ   € \         f*    ^ RIHp  ^ RIHp \        V ! V! 4       4      4      s \         # \         #   \         d"    \        P                  RRR7       / s  \         # i ; i)z\Process-cached profile secret mapping (external resolvers are slow); failures degrade to {}.)Úbuild_profile_secret_scope)Úget_hermes_homezyCould not build the profile secret scope; externally managed credentials will not be visible to the startup gate (#95216)T)Úexc_info)	r&   r$   r*   Úhermes_constantsr+   r(   Ú	ExceptionÚloggerÚwarning)r*   r+   s     r   r   r   5   sp   € ô !Ò(ð	+ÝEÝ8Ü(,Ñ-GÉÓHYÓ-ZÓ([Ð%ô %Ð$Ô$Ð$øô ô 	+Ü�N‰Nð7ØAEð ô Gð )+Ñ%Ü$Ð$ð	+ús   Š"8 ¸$A$Á#A$Ú c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   r   r   r   )r   s   "r   r   r   E   s!   € ÷ 0ñ 0˜Cð 0¨#ð 0°sñ 0r   c                ó¢  € V '       g   V# \         P                  ! \        4      ;_uu_ 4        ^ RIHpHp V! 4       pVeH   V! 4       '       d;   VP                  V 4      pVf   TM\        V4      P                  4       uuRRR4       # RRR4       \        P                  ! V 4      ;'       g    TP                  4       #   + '       g   i     L>; i)u˜  Allow/deny gate env read with per-profile isolation, always stripped.

With a profile secret scope installed AND multiplexing active, a scoped miss returns ``default``
instead of falling through to ``os.environ``, which may hold ANOTHER profile's first-writer
bridged value (the YAMLâ†’env bridges are first-writer-wins; allowlist leak, #72348).
Single-profile deployments behave exactly like ``os.getenv``.
©r#   Úis_multiplex_activeN)Ú
contextlibÚsuppressr.   r$   r#   r5   r   r   Ústripr   r   )r   r   r#   r5   Úscoper   s   &&    r   Úplatform_gate_envr:   E   s�   € ÷ ØˆÜ	×	Ò	œY×	'Õ	'ßPá$Ó&ˆØÒÑ!4×!6Ò!6Ø—)‘)˜D“/ˆCØ!šk‘7¬s°3«x¯~©~Ó/?÷ 
(×	'Ò	'ô �IŠI�d‹O×&Ð&˜w×-Ñ-Ó/Ð/÷ 
(×	'ús   ­ B>Á/B>Â>C	c                óî   € \        V \        4      '       dL   V P                  4       R,          R8X  d0    \        P                  ! V 4      p\        T\        4      '       d   T# V #   \
         d    T u # i ; i)a2  Decode a JSON-encoded allowlist written by ``hermes config set``.

String-typed defaults keep list literals verbatim on write (``allowed_chats`` is
declared as ``""``), so the config can hold ``'["-100","-200"]'`` as a string.
Malformed JSON passes through unchanged and keeps the legacy comma-split path.
:Né   NÚ[)Ú
isinstancer   ÚlstripÚjsonÚloadsÚ
ValueErrorÚlist)ÚrawÚloadeds   & r   Údecode_json_list_literalrF   Y   sd   € ô �#”s×Ò §
¡
£¨RÕ 0°CÔ 7ð	Ü—Z’Z “_ˆFô �fœd×#Ò#ØˆMØ€Jøô	 ô 	ØŠJð	ús   ´A$ Á$A4Á3A4Úblank_is_unsetTc               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   Úextrar%   Úkeyr   Úenvr   r   rG   r   r   r   )r   s   "r   r   r   j   s8   € ÷ ñ ˜>ð °ð ¸#ð Èð Ø'+ðØ7:ñr   c               ó"  € V'       d2   \        VR4      pVe"   \        V4      P                  4       '       d   V# T ;'       g    / P                  V4      pVe5   V'       d/   \	        V\        4      '       d   VP                  4       '       g   V# V# )uâ  The ONE per-profile setting reader: explicit env ``env`` â†’ the profile's YAML
``config.extra[key]`` â†’ ``default``.

The env rung is the owning profile's, read through ``get_scoped_secret``: a secondary
multiplex profile sees its own ``.env`` and a miss falls to ITS YAML, never to the launch
process's ``os.environ`` (which holds the default profile's bridged values); single-profile
and default-profile installs read ``os.environ`` there, keeping the documented env-over-YAML
contract (an explicit ``DISCORD_ALLOW_MENTION_EVERYONE=false`` beats ``everyone: true``,
#108440; ``TELEGRAM_REACTIONS=true`` beats the stock ``reactions: false``, #109032). A blank
env value is unset. An explicit ``False``/``0`` in YAML is a real value (``require_mention:
false`` must not fall to ``default``). A blank YAML string is unset by default; readers whose
YAML key means "clear it" (``allowed_channels: "" `` = no whitelist) pass
``blank_is_unset=False`` so only a missing/``None`` key falls through.
N)r   r   r8   r   r>   )rI   rJ   rK   r   rG   Ú	env_valueÚvalues   &&&&$  r   Úextra_or_secretrO   j   sn   € ÷  Ü% c¨4Ó0ˆ	ØÒ ¤S¨£^×%9Ñ%9×%;Ò%;ØÐØ�[ˆ[�b×Ñ˜cÓ"€EØ‚}Ÿ¬J°u¼c×,BÒ,BÈ5Ï;É;Ï=Ê=ØˆØ€Lr   c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r   „   s   € ÷ ñ ˜ñ r   c                 óz   €  ^ RI Hp Hp \        V! 4       ;'       d
    V ! 4       RJ4      #   \         d     R# i ; i)a[  True when running inside a multiplexed secondary profile's scope.

Secondary-profile adapters are constructed/connected inside
``_profile_runtime_scope`` (secret scope installed + multiplex active).
The DEFAULT profile under multiplexing runs unscoped and keeps the legacy
``os.environ`` precedence, so YAML->env bridges must skip only when True.
r4   NF)r$   r#   r5   r   r.   r4   s     r   Úprofile_scopedrR   „   s<   € ðßPÜÑ'Ó)×PÐPÑ.BÓ.DÈDÐ.PÓQÐQøÜô Úðús   ‚+ �+ «:¹:c               ó   € V ^8„  d   QhRR/# )r   r   zCallable[[str, Any], None]r   )r   s   "r   r   r   —   s   € ÷ ñ Ð3ñ r   c                 ó.   a€ \        4       oR V3R llp V # )uV  ``set_env(name, value)`` for ``apply_yaml_config_fn`` hooks: writes ``os.environ[name]`` only
when the var is unset (explicit env wins over YAML) and NEVER while a multiplexed secondary
profile's scope is active â€” the gateway loads every secondary's config inside
``_profile_runtime_scope``, so a write there would pin that profile's policy process-wide and the
default profile's adapters would read it as their own (first-writer-wins poisoning, #80099).
Hooks seed the same values into the returned ``extra`` so each profile's adapter reads its own.
Lists are comma-joined; ``None`` is skipped.
c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   rN   r   r   ÚNoner   )r   s   "r   r   Ú%yaml_env_setter.<locals>.__annotate__¢   s&   € ÷ fñ f�cð f #ð f¨$ñ fr   c                óð   <€ Ve%   S'       g   \         P                  ! V 4      '       d   R # \        V\        4      '       d   RP	                  R V 4       4      M
\        V4      \         P                  V &   R # )NÚ,c              3  ó8   "  € T F  p\        V4      x € K  	  R # 5i©N)r   )Ú.0Úvs   & r   Ú	<genexpr>Ú3yaml_env_setter.<locals>.set_env.<locals>.<genexpr>¥   s   é € Ð#:±E¨q¤C¨§F F³Eùs   ‚)r   r   r>   rC   Újoinr   Úenviron)r   rN   Úskips   &&€r   Úset_envÚ yaml_env_setter.<locals>.set_env¢   sL   ø€ ØŠ=ŸD¤B§I¢I¨d§O¢OÙÜ>HÈÔPT×>UÒ>U˜3Ÿ8™8Ñ#:±EÓ#:Ô:Ô[^Ð_dÓ[eŒ�
‰
�4Ór   )rR   )rc   rb   s    @r   Úyaml_env_setterre   —   s    ø€ ô Ó€D÷fð fð
 €Nr   c               ó    € V ^8„  d   QhRRRR/# )r   Úmessager   r   r(   r   )r   s   "r   r   r   ª   s   € ÷  ñ  ˜ð   ñ  r   c                ó0   € ^ RI Hp V! \        V 4      4      # )zÆStandalone-sender failure envelope with vendor exception text redacted (the same helper
``send_message`` uses), so a token or signed URL in an httpx/aiohttp error never reaches the
model transcript.)Ú_error)Útools.send_message_sendersri   r   )rg   ri   s   & r   Ú
send_errorrk   ª   s   € õ 2Ù”#�g“,ÓÐr   Úlowerc                ó
   € W9   # r[   r   ©ÚcfgrJ   s   &&r   Ú<lambda>rp   ¶   s   € ˜sšzr   c                ó4   € \        V 4      P                  4       # r[   )r   rl   ©r]   s   &r   rp   rp   ¶   s   € ´S¸³V·\±\´^r   r   c                ó*   € V P                  V4      R9  # )N)Nr1   ©r   rn   s   &&r   rp   rp   ·   s   € ˜SŸW™W S›\°Ò;r   Úcsvc                ó(   € V P                  V4      R J# r[   rt   rn   s   &&r   rp   rp   ¸   s   € ˜SŸW™W S›\°Ñ5r   c                ó   € V # r[   r   rr   s   &r   rp   rp   ¸   s   € Ár   r@   c                ó
   € W9   # r[   r   rn   s   &&r   rp   rp   ¹   s   € ˜cšjr   zCdict[str, tuple[Callable[[dict, str], bool], Callable[[Any], Any]]]Ú_YAML_KINDSc               ó$   € V ^8„  d   QhRRRRRR/# )r   ro   r(   ÚspeczIterable[tuple[str, str, str]]r   zdict | Noner   )r   s   "r   r   r   ½   s"   € ÷ ñ ˜4ð Ð'Eð È+ñ r   c                óÄ   € \        4       p/ pV FC  w  rEp\        V,          w  rxV! W4      '       g   K%  W,          W4&   V! WX! W,          4      4       KE  	  T;'       g    R# )a  Table-driven ``apply_yaml_config_fn`` body: for each ``(yaml_key, ENV_VAR, kind)`` row seed the
original YAML value into the returned ``extra`` and bridge it to env through ``yaml_env_setter``
(env wins; skipped under a secondary profile's scope). ``None`` when nothing matched.
N)re   ry   )	ro   r{   rc   ÚseededrJ   rK   ÚkindÚappliesÚencodes	   &&       r   Úapply_yaml_bridger�   ½   s^   € ô
 Ó€GØ€FÛ‰ˆ�$Ü% dÕ+‰ˆÙ�3×ÔØ�(ˆF‰KÙ�C˜ ¥Ó)Ö*ñ	 ð
 �>ˆ>�TÐr   Úhome_envÚhome_defaultc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r{   z6Iterable[tuple[str, str, Callable[[str], Any] | None]]r‚   z
str | Nonerƒ   r   r   r(   r   )r   s   "r   r   r   Ð   s,   € ÷ ñ ÐTð Ø",ðØCFðØPTñr   c               óð  € / pV  F|  w  rEp\        \        VR4      ;'       g    R4      P                  4       pV'       g   K=  \        P                  ! \
        4      ;_uu_ 4        V'       d	   V! V4      MTW5&   RRR4       K~  	  V'       dV   \        \        VR4      ;'       g    R4      P                  4       ;'       g    TpV'       d   RVR\        V R2R4      /VR&   V#   + '       g   i     Kð  ; i)u  Table-driven ``env_enablement_fn`` body: for each ``(ENV_VAR, extra_key, conv)`` row read the
profile-scoped value, skip it when blank or when ``conv`` raises ``ValueError``, else seed
``conv(value)`` (``None`` conv keeps the stripped string).

``home_env`` seeds ``home_channel`` as ``{"chat_id", "name"}`` from ``<home_env>`` (else
``home_default``) with ``<home_env>_NAME`` naming it â€” the core hook lifts that dict into a
``HomeChannel``. The name falls back to the literal ``"Home"``, the same rule the built-in
platforms use.
r1   NÚchat_idr   Ú_NAMEÚHomeÚhome_channel)r   r   r8   r6   r7   rB   )	r{   r‚   rƒ   ÚseedrK   rJ   ÚconvrD   Úhomes	   &$$      r   Úseed_extra_from_envr�   Ð   sÒ   € ð €DÛ‰ˆ�$ÜÔ# C¨Ó,×2Ð2°Ó3×9Ñ9Ó;ˆßÙÜ× Ò ¤×,Õ,ß%)™˜Sœ	¨sˆD‰I÷ -Ñ,ñ	 ÷ ÜÔ$ X¨rÓ2×8Ð8°bÓ9×?Ñ?ÓA×QÐQÀ\ˆßØ$-¨t°VÔ=NÐRZÐQ[Ð[`ÐOaÐciÓ=jÐ#kˆD�Ñ Ø€K÷ -×,Ð,ús   Á$C$Ã$C5c               ó    € V ^8„  d   QhRRRR/# )r   Únamesr   r   úCallable[[Any], bool]r   )r   s   "r   r   r   é   s   € ÷ 	ñ 	˜Sð 	Ð%:ñ 	r   c                 ó   a € R V 3R llpV# )zù``is_connected`` for platforms whose only configuration is env: True when every ``names`` var is
non-blank. Resolves ``hermes_cli.gateway.get_env_value`` at call time (scope-aware, .env-backed) so
setup-status tests that patch it see the same value.c               ó    € V ^8„  d   QhRRRR/# ©r   Úconfigr   r   r   r   )r   s   "r   r   Ú&env_is_connected.<locals>.__annotate__î   s   € ÷ Vñ V˜Sð V Tñ Vr   c                ó†   <a€ ^ RI Ho \        ;QJ d    V3R lS 4       F  '       d   K   R# 	  R# ! V3R lS 4       4      # )r!   Nc              3  óv   <"  € T F.  pSP                  V4      ;'       g    R P                  4       x € K0  	  R# 5i)r1   N)Úget_env_valuer8   )r\   r   Úgateway_mods   & €r   r^   Ú9env_is_connected.<locals>.is_connected.<locals>.<genexpr>ð   s0   øé € ÐUÉuÀt�K×-Ñ-¨dÓ3×9Ð9°r×@Ñ@×BÐBËuùs   ƒ9 9FT)Úhermes_cli.gatewayÚgatewayÚall)r”   r™   r�   s   &@€r   Úis_connectedÚ&env_is_connected.<locals>.is_connectedî   s/   ù€ Ý0ß‹sÔUÉuÓU�sŒsÐUŠsÐUˆsÔUÉuÓUÓUÐUr   r   )r�   rž   s   j r   Úenv_is_connectedr    é   s   ø€ ÷
Vð Vð Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   Úkeysr   r   r�   r   )r   s   "r   r   r   õ   s   € ÷ ñ  #ð Ð*?ñ r   c                 ó   a € R V 3R llpV# )z±``PlatformEntry.config_connected_fn``: connected when the merged PlatformConfig carries a
token/api_key or every ``extra`` key (no env fallback; that is ``is_connected``'s job).c               ó    € V ^8„  d   QhRRRR/# r“   r   )r   s   "r   r   Ú,config_connected_extra.<locals>.__annotate__ù   s   € ÷ ^ñ ^ ð ^¨ñ ^r   c                óÜ   <a € \        S P                  ;'       g    S P                  4      ;'       g;    \        ;QJ d    V 3R  lS 4       F  '       d   K   R# 	  R# ! V 3R  lS 4       4      # )c              3  óZ   <"  € T F   pSP                   P                  V4      x € K"  	  R # 5ir[   )rI   r   )r\   Úkr”   s   & €r   r^   ÚCconfig_connected_extra.<locals>.config_connected.<locals>.<genexpr>ú   s&   øé € Ð:]ÑX\ÐST¸6¿<¹<×;KÑ;KÈA×;NÐ;NÓX\ùs   ƒ(+FT)r   ÚtokenÚapi_keyr�   )r”   r¢   s   f€r   Úconfig_connectedÚ0config_connected_extra.<locals>.config_connectedù   sK   ù€ Ü�F—L‘L×2Ð2 F§N¡NÓ3×]Ð]·s³sÔ:]ÑX\Ó:]·s´sÐ]²sÐ]°sÔ:]ÑX\Ó:]Ó7]Ð]r   r   )r¢   r¬   s   j r   Úconfig_connected_extrar®   õ   s   ø€ ÷^ð ^ð Ðr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rN   r   r   Úintr   r   )r   s   "r   r   r   ÿ   s!   € ÷ ñ �sð  Sð ¨Sñ r   c                óV   €  \        V 4      #   \        \        \        3 d    Tu # i ; i)zU``int(value)`` or ``default`` when unparseable (incl. non-finite YAML like ``.inf``).)r°   Ú	TypeErrorrB   ÚOverflowError)rN   r   s   &&r   Úcoerce_portr´   ÿ   s,   € ðÜ�5‹zÐøÜ”z¤=Ð1ô ØŠðús   ‚
 �(§(r[   )r1   )(Ú__conditional_annotations__Ú__doc__Ú
__future__r   r6   r@   Úloggingr   Útypingr   r   r   r   r$   r   r   r   r   Ú	getLoggerÚ__name__r/   r   r   r&   Ú__annotations__r   r:   rF   rO   rR   re   rk   r   Údumpsry   r�   r�   r    r®   r´   )rµ   s   @r   Ú<module>r¾      sû   øðôõ #ã Û Û Û 	ß 4Ó 4õ KÝ ?à	×	Ò	˜8Ó	$€ñ/ÐSX÷ /ð /ò."ð
 -1Ð ˜>Ó 0õ%÷ 0ò(ñ"Ø.2÷ð õ4õ&õ& ð Ñ)Ñ+CÐDØ	Ñ;¸SÐAØ	Ñ5±{ÐCØ
Ñ(¨$¯*©*Ð5ð	T€ÐPó õð&Ø/3ðØIK÷õ2	õ÷r   