+
    lHàjl7  ã                  óÄ  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIHt ^ RIH	t	H
t
Ht ^ RIHt ]P                  ! ]4      tRtRt0 R6mtRR	/R
 R lltR7RR	/R R llltR R ltR R lt]tR R ltRR/R R lltR R ltR R ltR R ltR8R R lltR R  ltR! R" lt R#t!R$R9/t"R%R:R$R;/t#R<t$R& R' lt%R( R) lt&R=R* R+ llt'R, R- lt(/ t)R.]*R/&   R0 R1 lt+R2 R3 lt,R4 R5 lt-R# )>z*Shared helpers for tool backend selection.)ÚannotationsN)ÚPath)ÚAnyÚDictÚOptional)Úis_truthy_valueÚlocalÚautoÚforce_freshFc               ó    € V ^8„  d   QhRRRR/# )é   r
   ÚboolÚreturn© )Úformats   "Ú?/home/ubuntu/.hermes/hermes-agent/tools/tool_backend_helpers.pyÚ__annotate__r      s   € ÷ ñ ¨tð Àñ ó    c                ó¶   €  ^ RI Hp V '       d
   V! RR7      MV! 4       p\        VP                  4      ;'       d    VP                  #   \
         d     R# i ; i)u	  Coarse gate: entitled to the Nous Tool Gateway (paid Portal access OR a live free
pool). Fails closed on unknown/error â€” never blocks startup. Callers narrow per category
via ``tool_gateway_entitled_for``; ``force_fresh`` is for flows needing a just-bought grant.)Úget_nous_portal_account_infoT©r
   F)Úhermes_cli.nous_accountr   r   Ú	logged_inÚtool_gateway_entitledÚ	Exception)r
   r   Úaccount_infos   $  r   Úmanaged_nous_tools_enabledr      sQ   € ðÝHçJUÑ4ÀÕFÙ9Ó;ð 	ä�L×*Ñ*Ó+×RÐR°×0RÑ0RÐRøÜô Úðús   ‚A	 �+A	 ¼A	 Á	AÁAc               ó$   € V ^8„  d   QhRRRRRR/# )r   Ú
capabilityÚstrr
   r   r   r   )r   s   "r   r   r       s#   € ÷ 5ñ 5°cð 5Ø7;ð5ØHKñ5r   c               óz   €  ^ RI HpHp V! V! VR7      V RR7      pV'       d   V#  V  R2#   \         d     Li ; i)zHReturn account-aware guidance for an unavailable Nous Tool Gateway path.)Ú&format_nous_portal_entitlement_messager   r   T)r   Úin_chatzY is unavailable. Run `hermes model` to refresh your Nous Portal login and billing status.)r   r!   r   r   )r   r
   r!   r   Úmessages   &$   r   Ú%nous_tool_gateway_unavailable_messager$       s]   € ð	÷	Rá8Ù(°[ÔAÈjØôˆ÷ ØˆNð ð ˆlð 4ð 4ð 5øô ô Ùðús   ‚!, ¤, ¬:¹:c               ó    € V ^8„  d   QhRRRR/# ©r   Úvalueúobject | Noner   r   r   )r   s   "r   r   r   1   s   € ÷ 1ñ 1¨Mð 1¸cñ 1r   c                óŒ   € \        T ;'       g    \        4      P                  4       P                  4       pT;'       g    \        # )z)Return a normalized browser provider key.)r   Ú_DEFAULT_BROWSER_PROVIDERÚstripÚlower)r'   Úproviders   & r   Ú normalize_browser_cloud_providerr.   1   s6   € ä�5×5Ð5Ô5Ó6×<Ñ<Ó>×DÑDÓF€HØ×0Ð0Ô0Ð0r   c               ó    € V ^8„  d   QhRRRR/# r&   r   )r   s   "r   r   r   7   s   € ÷ Gñ G˜]ð G¨sñ Gr   c                ó’   € \        T ;'       g    \        4      P                  4       P                  4       pV\        9   d   V# \        # )z=Return the requested modal mode when valid, else the default.)r   Ú_DEFAULT_MODAL_MODEr+   r,   Ú_VALID_MODAL_MODES)r'   Úmodes   & r   Úcoerce_modal_moder4   7   s;   € äˆu×+Ð+Ô+Ó,×2Ñ2Ó4×:Ñ:Ó<€DØÔ-Ô-ˆ4ÐFÔ3FÐFr   c               ó   € V ^8„  d   QhRR/# ©r   r   r   r   )r   s   "r   r   r   @   s   € ÷ 	ñ 	 dñ 	r   c                 óÄ   € \        R4      '       d   \        R4      '       d   R#  \        P                  ! 4       R,          P                  4       #   \         d     R# i ; i)zíReturn True when direct Modal credentials/config are available. The token pair is a
profile credential: read it through the secret scope so the default profile's Modal
account never selects the direct backend for a multiplexed secondary.ÚMODAL_TOKEN_IDÚMODAL_TOKEN_SECRETTz.modal.tomlF)Ú_scoped_credentialr   ÚhomeÚexistsÚOSErrorr   r   r   Úhas_direct_modal_credentialsr>   @   sP   € ô Ð*×+Ò+Ô0BÐCW×0XÒ0XÙðÜ—	’	“˜mÕ+×3Ñ3Ó5Ð5øÜô Úðús   ¦)A ÁAÁAÚmanaged_enabledc          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   Ú
modal_moder(   Ú
has_directr   Úmanaged_readyr?   zbool | Noner   zdict[str, Any]r   )r   s   "r   r   r   L   s2   € ÷ 2ñ 2¨Mð 2È$ð 2Ø/3ð2à1<ð2àHVñ2r   c               ó
  € \        V 4      pVf   \        4       pT;'       d    TpRV'       d   RMRRV'       d   RMR/pTP                  YE'       d   RMV'       d   RMR4      pRTRTRTRTRVR8H  ;'       d    V'       * R	V/# )
z„Resolve direct vs managed Modal backend: ``direct``/``managed`` are exclusive; ``auto``
prefers managed when available, else direct.NÚmanagedÚdirectÚrequested_moder3   rB   rC   Úmanaged_mode_blockedÚselected_backend)r4   r   Úget)rA   rB   rC   r?   rG   Ú
managed_okÚ	exclusiverI   s   &$$$    r   Úresolve_modal_backend_staterM   L   s˜   € ô
 ' zÓ2€NØÒÜ4Ó6ˆØ ×2Ð2 ]€JØ¯™I¸Ø§z™8°tð=€Ià —}‘}Ø¦Z™	Ç±XÐQUóWÐà˜n¨f°nÀlÐT^Ø˜]Ø" N°iÑ$?×$WÐ$WÈÔDWØÐ 0ð2ð 2r   c               ó    € V ^8„  d   QhRRRR/# )r   Únamer   r   r   )r   s   "r   r   r   _   s   € ÷ 3ñ 3˜Sð 3 Sñ 3r   c                óÒ   €  ^ RI Hp V! V R4      ;'       g    RP                  4       #   \         d3    \        P
                  ! T R4      ;'       g    RP                  4       u # i ; i)zªRead a credential env var under the active profile secret scope; raw env fallback only
if ``agent.secret_scope`` cannot import (a packaging edge must never lose the key).)Ú
get_secretÚ )Úagent.secret_scoperQ   r+   r   ÚosÚgetenv)rO   rQ   s   & r   r:   r:   _   sY   € ð3Ý1Ù˜4 Ó$×*Ð*¨×1Ñ1Ó3Ð3øÜô 3Ü—	’	˜$ Ó#×)Ð) r×0Ñ0Ó2Ò2ð3ús   ‚) ˜) ©(A&ÁA&Á%A&c               ó    € V ^8„  d   QhRRRR/# )r   Úenv_varr   r   r   )r   s   "r   r   r   i   s   € ÷ ñ ˜3ð  3ñ r   c                ó†   €  ^ RI Hp \        V! V 4      ;'       g    R4      P                  4       #   \         d     R# i ; i)zQ``.env`` value via ``hermes_cli.config.get_env_value`` (``""`` when unavailable).)Úget_env_valuerR   )Úhermes_cli.configrY   r   r+   r   )rW   rY   s   & r   Ú_dotenv_valuer[   i   s=   € ðÝ3Ü‘= Ó)×/Ð/¨RÓ0×6Ñ6Ó8Ð8øÜô Úðús   ‚1 œ1 ±A ¿A c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úprovider_idr   rW   r   r   r   )r   s   "r   r   r   r   s!   € ÷ 
ñ 
¨ð 
°cð 
¸dñ 
r   c                ó`   € V '       g   R#  ^ RI Hp V! V RV 24      #   \         d     R# i ; i)a  ``hermes auth remove <provider>`` records ``env:<VAR>`` in ``suppressed_sources`` and promises
the variable is ignored until ``hermes auth add``; a value still living in a long-running
gateway's process environment (inherited across update restarts) must honor that too.F)Úis_source_suppressedzenv:)Úhermes_cli.authr_   r   )r]   rW   r_   s   && r   Ú_env_source_suppressedra   r   s8   € ÷ ÙðÝ8Ù# K°4¸°yÐ1AÓBÐBøÜô Úðús   Œ ž-¬-c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rW   r   r]   Úconfig_valuer   r   )r   s   "r   r   r      s)   € ÷ (ñ ( Sð (°sð (È#ð (Ø03ñ(r   c                ó<  € \        T;'       g    R4      P                  4       pV'       d   V# \        W4      pV'       g   \        V 4      ;p'       d   V#  ^ RIHp V! 4       '       d   R#  V'       g=   V'       d*   \        V! V 4      ;'       g    R4      P                  4       M
\        V 4      pV'       g	   V'       g   V#  ^ RIH	p VRV 23 F†  pV! V4      p	V	e'   V	P                  4       '       d   V	P                  4       MRp
\        \        V
RR4      ;'       g    \        V
RR4      ;'       g    R4      P                  4       pV'       g   K„  Vu # 	  R#   \         d     Lýi ; i  \         d#   p\        P                  RYT4        Rp?R# Rp?ii ; i)	u�  Resolve a voice-provider API key (single owner for STT/TTS lookup). Order: explicit
``config_value`` -> profile secret scope / env -> ``.env`` via ``env_getter`` (or
``hermes_cli.config.get_env_value``) -> credential pool for ``provider_id``. Under an
active multiplex turn the profile scope is authoritative: a miss returns ``""`` rather
than borrowing another profile's env or pool. Never raises. The env/.env tier is skipped
entirely while ``env:<env_var>`` is suppressed for ``provider_id`` (#116155).

Resolution order (fixes #68003 â€” keys added via ``hermes auth add <provider>`` were invisible to the
voice tools, which only consulted env/.env):
rR   )Úis_multiplex_active)Ú	load_poolzcustom:NÚruntime_api_keyÚaccess_tokenz,Could not read %s credential pool for %s: %s)r   r+   ra   r:   rS   re   r   r[   Úagent.credential_poolrf   Úhas_credentialsÚpeekÚgetattrÚloggerÚdebug)rW   r]   rc   Ú
env_getterÚkeyÚenv_suppressedre   rf   Úpool_keyÚpoolÚentryÚexcs   &&&&        r   Úresolve_provider_secretrv      sv  € ô ˆl× Ð ˜bÓ
!×
'Ñ
'Ó
)€Cß
Øˆ
Ü+¨KÓA€NßÔ&8¸Ó&AÐA˜sÖAØˆ
ðÝ:Ù× Ò Ùð !÷ ß8BŒc‘*˜WÓ%×+Ð+¨Ó,×2Ñ2Ô4ÌÐV]ÓH^ˆß
—+Øˆ
ð`Ý3à$¨°¨}Ð&=Ó>ˆHÙ˜XÓ&ˆDØ#'Ò#3¸×8LÑ8L×8NÒ8N�D—I‘I”KÐTXˆEÜ”g˜eÐ%6¸Ó;÷ ð ¼wÀuÈnÐ^`Ó?a÷ ð Øóß"™U›Wð ç‰sØ’
ñ ?ñ øô% ô Ùðûô  ô `Ü�‰ÐCÀ[Ð[^×_Ð_Ùûð`úsH   ÁE Ã1E. Ã6*E. Ä!E. Ä6E. ÅE. ÅE. ÅE+Å*E+Å.FÅ9FÆFc               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r   ª   s   € ÷ 
Hñ 
H cñ 
Hr   c                 óB   € \        RR4      ;'       g    \        RR4      # )uß  Prefer VOICE_TOOLS_OPENAI_KEY, else OPENAI_API_KEY (scope-aware, pool fallback for the
latter). Must go through the secret scope: a raw ``os.environ`` read could bill another
profile's account under multiplex.

Outside a multiplexed turn, ``OPENAI_API_KEY`` additionally falls back to the credential pool (``hermes
auth add openai-api``) via ``resolve_provider_secret`` â€” same #68003 fix as the other voice providers.
The dedicated voice-tools override remains env/scope-only.
ÚVOICE_TOOLS_OPENAI_KEYrR   ÚOPENAI_API_KEYz
openai-api)rv   r   r   r   Úresolve_openai_audio_api_keyr{   ª   s,   € ô $Ð$<¸bÓA÷ Gð GÜ&Ð'7¸ÓFðHr   c               ó    € V ^8„  d   QhRRRR/# )r   Úconfig_sectionr   r   r   r   )r   s   "r   r   r   ·   s   € ÷ ñ  Cð ¨Dñ r   c                óØ   €  ^ RI Hp V! 4       ;'       g    / P                  V 4      p\        V\        4      ;'       d    \        VP                  R4      4      #   \         d     R# i ; i)zHTrue when ``<section>.use_gateway`` is set in config.yaml. Never raises.)Úload_configÚuse_gatewayF)rZ   r   rJ   Ú
isinstanceÚdictr   r   )r}   r   Úsections   &  r   Úprefers_gatewayr„   ·   sY   € ðÝ1Ù“=×&Ð& B×+Ñ+¨NÓ;ˆÜ˜'¤4Ó(×XÐX¬_¸W¿[¹[ÈÓ=WÓ-XÐXøÜô Úðús   ‚A –(A ¿A ÁA)Á(A)ÚnousÚwebÚbrowserc               ó    € V ^8„  d   QhRRRR/# )r   rƒ   r   r   zdict[str, Any] | Noner   )r   s   "r   r   r   Ì   s   € ÷ ñ ˜#ð Ð"7ñ r   c                óÜ   €  ^ RI Hp V! 4       ;'       g    / p\        V\        4      '       d   VP	                  V 4      MRp\        V\        4      '       d   V# R#   \
         d     R# i ; i)z@The RAW (unmerged) config.yaml mapping for ``section``, or None.)Úread_raw_config_readonlyN)rZ   rŠ   r�   r‚   rJ   r   )rƒ   rŠ   ÚcfgÚraws   &   r   Ú_raw_sectionr�   Ì   s_   € ðÝ>Ù&Ó(×.Ð.¨BˆÜ",¨S´$×"7Ò"7ˆc�g‰g�gÔ¸TˆÜ  ¤d×+Ò+ˆsÐ5°Ð5øÜô Úðús   ‚A –AA ÁA ÁA+Á*A+c               ó    € V ^8„  d   QhRRRR/# )r   rƒ   r   r   ú
str | Noner   )r   s   "r   r   r   ×   s   € ÷ ñ ˜Cð  Jñ r   c                ód  € \        V 4      pVf   R# \        VP                  R4      4      '       d   \        # \        P                  V \
        4       FZ  pVP                  V4      e7   \        VP                  V4      4      P                  4       P                  4       MRpV'       g   KX  Vu # 	  R# )aè  THE single runtime read of the persisted `hermes tools` selection: ``"nous"`` (managed
gateway row), a vendor name (direct, own credentials), or ``None`` (never configured ->
legacy autodetect allowed). Reads the RAW config.yaml so key presence means "actually
written", not "schema default"; a raw ``local`` is therefore a real user selection.
Legacy shim: ``use_gateway: true`` was only ever written by the managed row, so it maps
to ``"nous"`` regardless of the name key. Never raises.Nr€   rR   )	r�   r   rJ   ÚNOUS_MANAGED_PROVIDERÚ_SELECTION_NAME_KEYSÚ_DEFAULT_NAME_KEYSr   r+   r,   )rƒ   rŒ   rp   Útexts   &   r   Úread_selectionr•   ×   sŒ   € ô �wÓ
€CØ
‚{ÙÜ�s—w‘w˜}Ó-×.Ò.Ü$Ð$Ü#×'Ñ'¨Ô1CÖDˆØ47·G±G¸C³LÒ4LŒs�3—7‘7˜3“<Ó ×&Ñ&Ó(×.Ñ.Ô0ÐRTˆß‰4ØŠKñ Eñ r   c               ó    € V ^8„  d   QhRRRR/# )r   r   úOptional[str]r   r�   r   )r   s   "r   r   r   ì   s   € ÷ 
!ñ 
!¨mð 
!Àzñ 
!r   c                óì   € V '       dc   \        R4      ;'       g    / p\        VP                  V  R24      ;'       g    R4      P                  4       P	                  4       pV'       d   V# \        R4      # )a<  Stored selection that decides ONE web capability (``"search"`` / ``"extract"``):
``web.<capability>_backend`` when set (``"nous"`` = managed gateway, a vendor name =
that vendor direct), else the shared :func:`read_selection`. Lets search and extract
each choose between the user's own key and the Nous Tool Gateway.r†   Ú_backendrR   )r�   r   rJ   r+   r,   r•   )r   rŒ   Úpins   &  r   Úread_web_capability_selectionr›   ì   s`   € ÷
 Ü˜5Ó!×'Ð' RˆÜ�#—'‘'˜Z˜L¨Ð1Ó2×8Ð8°bÓ9×?Ñ?ÓA×GÑGÓIˆßØˆJÜ˜%Ó Ð r   c               ó    € V ^8„  d   QhRRRR/# )r   rƒ   r   r   r   r   )r   s   "r   r   r   ù   s   € ÷ Uñ U˜cð U dñ Ur   c                ó  a€ \        V 4      e   R# \        P                  V R4      pV'       d   \        V 4      MRoSRJ;'       d;    \        ;QJ d    V3R lV 4       F  '       g   K   R# 	  R# ! V3R lV 4       4      # )zTrue when ANY selection signal was ever written for the section (wider than
read_selection: per-capability web keys count too).NTc              3  óˆ   <"  € T F7  p\        SP                  V4      ;'       g    R 4      P                  4       x € K9  	  R# 5i©rR   N)r   rJ   r+   )Ú.0rp   rŒ   s   & €r   Ú	<genexpr>Ú#selection_exists.<locals>.<genexpr>   s2   øé € Ð"TÉeÀs¤3 s§w¡w¨s£|×'9Ð'9°rÓ#:×#@Ñ#@×#BÐ#BËeùs
   ƒ!A¥AFr   )r•   Ú_EXTRA_SELECTION_KEYSrJ   r�   Úany)rƒ   ÚextrarŒ   s   & @r   Úselection_existsr¦   ù   sh   ø€ ô �gÓÒ*ÙÜ!×%Ñ% g¨rÓ2€Eß#(Œ,�wÔ
¨d€CØ�dˆ?×TÐTŸs›sÔ"TÉeÓ"TŸsœsÐTšsÐT˜sÔ"TÉeÓ"TÓTÐTr   zdict[str, dict[str, str]]ÚREMOVED_BACKENDSc               ó$   € V ^8„  d   QhRRRRRR/# )r   rƒ   r   rO   r   r—   r   )r   s   "r   r   r     s&   € ÷ \ñ \ #ð \¨Sð \°]ñ \r   c                ó¶   € \         P                  V / 4      P                  T;'       g    RP                  4       P                  R4      P                  4       4      # )zƒExplanation for a backend that used to ship in-tree, or None. ``name`` tolerates the
quoted form callers pass to selection_error().rR   z'")r§   rJ   r+   r,   )rƒ   rO   s   &&r   Úremoved_backend_noterª     sE   € ô ×Ñ ¨Ó,×0Ñ0°$·*°*¸"×1CÑ1CÓ1E×1KÑ1KÈEÓ1R×1XÑ1XÓ1ZÓ[Ð[r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rƒ   r   Úselection_nameÚfailurer   r   )r   s   "r   r   r     s.   € ÷ Hñ H˜Sð H°#ð HÀð HÈñ Hr   c                óB   € \        W4      ;'       g    TpV  RV RV R2# )zEThe uniform honest-error contract for a selected-but-broken provider.z is configured to use z (set via hermes tools), but z". Run 'hermes tools' to change it.)rª   )rƒ   r¬   r­   s   &&&r   Úselection_errorr¯     s:   € ä" 7Ó;×FÐF¸w€GØˆiÐ-¨nÐ-=ð >Ø"˜)Ð#EðGð Hr   c               ó   € V ^8„  d   QhRR/# r6   r   )r   s   "r   r   r      s   € ÷ Kñ K˜tñ Kr   c                 óP   € \        \        R4      ;'       g    \        R4      4      # )z§True when FAL_KEY is set (scope/env, else ``.env`` for CLI paths that run before dotenv
loads) to a non-whitespace value, so tool-side and CLI setup-time checks agree.ÚFAL_KEY)r   r:   r[   r   r   r   Úfal_key_is_configuredr³      s"   € ô Ô" 9Ó-×IÐI´¸yÓ1IÓJÐJr   >   r	   rF   rE   )zthe Nous Tool GatewayrŸ   )Úsearch_backendÚextract_backend)Úcloud_provider)Úbackend)r-   r·   r¶   )N).Ú__conditional_annotations__Ú__doc__Ú
__future__r   ÚloggingrT   Úpathlibr   Útypingr   r   r   Úutilsr   Ú	getLoggerÚ__name__rm   r*   r1   r2   r   r$   r.   r4   Únormalize_modal_moder>   rM   r:   r[   ra   rv   r{   r„   r‘   r£   r’   r“   r�   r•   r›   r¦   r§   Ú__annotations__rª   r¯   r³   )r¸   s   @r   Ú<module>rÃ      s  øðÜ 0å "ã Û 	Ý ß &Ñ &å !à	×	Ò	˜8Ó	$€Ø#Ð ØÐ Ú2Ð ð°e÷ ñ5Ø>C÷5ð 5õ"1õGð )Ð õ	ð2à?C÷2õ&3õõ
÷(õV
Hõð Ð àÐ EÐFÐ ð "Ð#6¸¸|ÐLÐ Ø>Ð õõ÷*
!õUð /1Ð Ð+Ó 0õ\õH÷Kr   