Ë
    ãß²j�4  ã                  ón  — U d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	m
Z
 ddlmZ  ej                  e«      ZdZdZh d	£Zd
dœd(d„Zd)d
dœ	 	 	 d*d„Zd+d„Zd+d„ZeZd,d„Zddœ	 	 	 	 	 d-d„Zd.d„Zd/d„Zd0d„Z	 	 d1	 d2d„Zd3d„Zd4d„ZdZ ddiZ!dddœZ"dZ#d5d „Z$d6d!„Z%d7d"„Z&i Z'd#e(d$<   d8d%„Z)d9d&„Z*d,d'„Z+y):z*Shared helpers for tool backend selection.é    )ÚannotationsN)ÚPath)ÚAnyÚDictÚOptional)Úis_truthy_valueÚlocalÚauto>   r
   ÚdirectÚmanagedF©Úforce_freshc                ó˜   — 	 ddl m} | r	 |d¬«      n |«       }t        |j                  «      xr |j                  S # t
        $ r Y yw xY w)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.r   )Úget_nous_portal_account_infoTr   F)Úhermes_cli.nous_accountr   ÚboolÚ	logged_inÚtool_gateway_entitledÚ	Exception)r   r   Úaccount_infos      ú?/home/ubuntu/.hermes/hermes-agent/tools/tool_backend_helpers.pyÚmanaged_nous_tools_enabledr      sN   € ðÝHÙJUÑ4ÀÕFÙ9Ó;ð 	ä�L×*Ñ*Ó+ÒR°×0RÑ0RÐRøÜò Ùðús   ‚:= ½	A	ÁA	c               ój   — 	 ddl m}m}  | ||¬«      | d¬«      }|r|S 	 | › d�S # t        $ r Y Œw xY w)zHReturn account-aware guidance for an unavailable Nous Tool Gateway path.r   )Ú&format_nous_portal_entitlement_messager   r   T)Ú
capabilityÚ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   ‚& ¦	2±2c                ót   — t        | xs t        «      j                  «       j                  «       }|xs t        S )z)Return a normalized browser provider key.)ÚstrÚ_DEFAULT_BROWSER_PROVIDERÚstripÚlower)ÚvalueÚproviders     r   Ú normalize_browser_cloud_providerr&   0   s0   € ä�5Ò5Ô5Ó6×<Ñ<Ó>×DÑDÓF€HØÒ0Ô0Ð0ó    c                ó€   — t        | xs t        «      j                  «       j                  «       }|t        v r|S t        S )z=Return the requested modal mode when valid, else the default.)r    Ú_DEFAULT_MODAL_MODEr"   r#   Ú_VALID_MODAL_MODES)r$   Úmodes     r   Úcoerce_modal_moder,   6   s8   € äˆuÒ+Ô+Ó,×2Ñ2Ó4×:Ñ:Ó<€DØÔ-Ñ-ˆ4ÐFÔ3FÐFr'   c                 óš   — t        d«      rt        d«      ry	 t        j                  «       dz  j                  «       S # t        $ r Y yw xY w)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ÚOSError© r'   r   Úhas_direct_modal_credentialsr5   ?   sJ   € ô Ð*Ô+Ô0BÐCWÔ0XØðÜ—	‘	“˜mÑ+×3Ñ3Ó5Ð5øÜò Ùðús   ™$> ¾	A
Á	A
)Úmanaged_enabledc               ó¬   — t        | «      }|€
t        «       }|xr |}|rdnd|rdnddœ}|j                  ||rdn|rdnd«      }|||||dk(  xr | |dœS )zˆResolve direct vs managed Modal backend: ``direct``/``managed`` are exclusive; ``auto``
    prefers managed when available, else direct.Nr   r   )r   r   )Úrequested_moder+   Ú
has_directÚmanaged_readyÚmanaged_mode_blockedÚselected_backend)r,   r   Úget)Ú
modal_moder9   r:   r6   r8   Ú
managed_okÚ	exclusiver<   s           r   Úresolve_modal_backend_staterA   K   s�   € ô
 ' zÓ2€NØÐÜ4Ó6ˆØ Ò2 ]€JÙ)3™I¸Ù'1™8°tñ=€Ià —}‘}Ø¡Z™	Á±XÐQUóWÐà,°nÐT^Ø*Ø$2°iÑ$?Ò$WÈÐDWØ 0ñ2ð 2r'   c                ó´   — 	 ddl m}  || d«      xs dj                  «       S # t        $ r+ t	        j
                  | d«      xs dj                  «       cY S w xY w)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).r   )Ú
get_secretÚ )Úagent.secret_scoperC   r"   r   ÚosÚgetenv)ÚnamerC   s     r   r0   r0   ^   sS   € ð3Ý1Ù˜4 Ó$Ò*¨×1Ñ1Ó3Ð3øÜò 3Ü—	‘	˜$ Ó#Ò) r×0Ñ0Ó2Ò2ð3ús   ‚ # £1AÁAc                ót   — 	 ddl m} t         || «      xs d«      j                  «       S # t        $ r Y yw xY w)zQ``.env`` value via ``hermes_cli.config.get_env_value`` (``""`` when unavailable).r   )Úget_env_valuerD   )Úhermes_cli.configrJ   r    r"   r   )Úenv_varrJ   s     r   Ú_dotenv_valuerM   h   s:   € ðÝ3Ü‘= Ó)Ò/¨RÓ0×6Ñ6Ó8Ð8øÜò Ùðús   ‚(+ «	7¶7c                óL   — | sy	 ddl m}  || d|› �«      S # t        $ r Y yw xY w)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.Fr   )Úis_source_suppressedzenv:)Úhermes_cli.authrO   r   )Úprovider_idrL   rO   s      r   Ú_env_source_suppressedrR   q   s8   € ñ ØðÝ8Ù# K°4¸°yÐ1AÓBÐBøÜò Ùðús   … —	#¢#c                ó„  — t        |xs d«      j                  «       }|r|S t        || «      }|st        | «      x}r|S 	 ddlm}  |«       ry	 |s0|r#t         || «      xs d«      j                  «       n
t        | «      }|s|s|S 	 ddlm	} |d|› �fD ]k  } ||«      }	|	� |	j                  «       r|	j                  «       nd}
t        t        |
dd«      xs t        |
dd«      xs d«      j                  «       }|sŒi|c S  	 y# t        $ r Y ŒÁw xY w# t        $ r"}t        j                  d	|| |«       Y d}~yd}~ww xY w)
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):
    rD   r   )Úis_multiplex_active)Ú	load_poolzcustom:NÚruntime_api_keyÚaccess_tokenz,Could not read %s credential pool for %s: %s)r    r"   rR   r0   rE   rT   r   rM   Úagent.credential_poolrU   Úhas_credentialsÚpeekÚgetattrÚloggerÚdebug)rL   rQ   Úconfig_valueÚ
env_getterÚkeyÚenv_suppressedrT   rU   Úpool_keyÚpoolÚentryÚexcs               r   Úresolve_provider_secretrf   ~   sh  € ô ˆ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�D—I‘I”KÐTXˆEÜ”g˜eÐ%6¸Ó;ò ¼wÀuÈnÐ^`Ó?aò Øóß"™U›Wð âØ’
ñ	ð øô% ò Ùðûô  ò `Ü�‰ÐCÀ[ÐRYÐ[^×_Ð_Øûð`ús7   Á D ÂA5D Ã>D ÄD Ä	DÄDÄ	D?ÄD:Ä:D?c                 ó6   — t        dd«      xs t        dd«      S )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_KEYrD   ÚOPENAI_API_KEYz
openai-api)rf   r4   r'   r   Úresolve_openai_audio_api_keyrj   ©   s&   € ô $Ð$<¸bÓAò GÜ&Ð'7¸ÓFðHr'   c                óº   — 	 ddl m}  |«       xs i j                  | «      }t        |t        «      xr t        |j                  d«      «      S # t        $ r Y yw xY w)zHTrue when ``<section>.use_gateway`` is set in config.yaml. Never raises.r   )Úload_configÚuse_gatewayF)rK   rl   r=   Ú
isinstanceÚdictr   r   )Úconfig_sectionrl   Úsections      r   Úprefers_gatewayrr   ¶   sS   € ðÝ1Ù“=Ò& B×+Ñ+¨NÓ;ˆÜ˜'¤4Ó(ÒX¬_¸W¿[¹[ÈÓ=WÓ-XÐXøÜò Ùðús   ‚AA Á	AÁAÚnousÚweb)Úsearch_backendÚextract_backend)Úcloud_provider)Úbackend)Úbrowserrt   )r%   rx   rw   c                ó²   — 	 ddl m}  |«       xs i }t        |t        «      r|j	                  | «      nd}t        |t        «      r|S dS # t
        $ r Y yw xY w)z@The RAW (unmerged) config.yaml mapping for ``section``, or None.r   )Úread_raw_config_readonlyN)rK   r{   rn   ro   r=   r   )rq   r{   ÚcfgÚraws       r   Ú_raw_sectionr~   Ë   sV   € ðÝ>Ù&Ó(Ò.¨BˆÜ",¨S´$Ô"7ˆc�g‰g�gÔ¸TˆÜ  ¤dÔ+ˆsÐ5°Ð5øÜò Ùðús   ‚AA
 ÁA
 Á
	AÁAc                ó<  — t        | «      }|€yt        |j                  d«      «      rt        S t        j                  | t
        «      D ]Q  }|j                  |«      �6t        |j                  |«      «      j                  «       j                  «       nd}|sŒO|c S  y)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.Nrm   rD   )	r~   r   r=   ÚNOUS_MANAGED_PROVIDERÚ_SELECTION_NAME_KEYSÚ_DEFAULT_NAME_KEYSr    r"   r#   )rq   r}   r`   Ú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ˆÚØŠKðð r'   c                óš   ‡— t        | «      �yt        j                  | d«      }|rt        | «      ndŠ‰duxr t	        ˆfd„|D «       «      S )zƒTrue when ANY selection signal was ever written for the section (wider than
    read_selection: per-capability web keys count too).NTr4   c              3  óv   •K  — | ]0  }t        ‰j                  |«      xs d «      j                  «       –— Œ2 y­w©rD   N)r    r=   r"   )Ú.0r`   r}   s     €r   ú	<genexpr>z#selection_exists.<locals>.<genexpr>ò   s,   øè ø€ Ò"TÀs¤3 s§w¡w¨s£|Ò'9°rÓ#:×#@Ñ#@×#BÑ"Tùs   ƒ69)r„   Ú_EXTRA_SELECTION_KEYSr=   r~   Úany)rq   Úextrar}   s     @r   Úselection_existsr�   ë   sM   ø€ ô �gÓÐ*ØÜ!×%Ñ% g¨rÓ2€EÙ#(Œ,�wÔ
¨d€CØ�dˆ?ÒTœsÓ"TÈeÔ"TÓTÐTr'   zDict[str, Dict[str, str]]ÚREMOVED_BACKENDSc                óª   — t         j                  | i «      j                  |xs dj                  «       j                  d«      j                  «       «      S )z‡Explanation for a backend that used to ship in-tree, or None. ``name`` tolerates the
    quoted form callers pass to selection_error().rD   z'")rŽ   r=   r"   r#   )rq   rH   s     r   Úremoved_backend_noter�     sC   € ô ×Ñ ¨Ó,×0Ñ0°$²*¸"×1CÑ1CÓ1E×1KÑ1KÈEÓ1R×1XÑ1XÓ1ZÓ[Ð[r'   c                ó8   — t        | |«      xs |}| › d|› d|› d�S )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�   )rq   Úselection_nameÚfailures      r   Úselection_errorr”     s9   € ä" 7¨NÓ;ÒF¸w€GØˆiÐ-¨nÐ-=ð >Ø"˜)Ð#EðGð Hr'   c                 óD   — t        t        d«      xs t        d«      «      S )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   r0   rM   r4   r'   r   Úfal_key_is_configuredr—     s   € ô Ô" 9Ó-ÒI´¸yÓ1IÓJÐJr'   )r   r   Úreturnr   )zthe Nous Tool Gateway)r   r    r   r   r˜   r    )r$   úobject | Noner˜   r    )r˜   r   )
r>   r™   r9   r   r:   r   r6   zbool | Noner˜   zDict[str, Any])rH   r    r˜   r    )rL   r    r˜   r    )rQ   r    rL   r    r˜   r   r‡   )rL   r    rQ   r    r^   r    r˜   r    )r˜   r    )rp   r    r˜   r   )rq   r    r˜   zDict[str, Any] | None)rq   r    r˜   z
str | None)rq   r    r˜   r   )rq   r    rH   r    r˜   zOptional[str])rq   r    r’   r    r“   r    r˜   r    ),Ú__doc__Ú
__future__r   ÚloggingrF   Úpathlibr   Útypingr   r   r   Úutilsr   Ú	getLoggerÚ__name__r\   r!   r)   r*   r   r   r&   r,   Únormalize_modal_moder5   rA   r0   rM   rR   rf   rj   rr   r€   rŠ   r�   r‚   r~   r„   r�   rŽ   Ú__annotations__r�   r”   r—   r4   r'   r   ú<module>r¤      s-  ðÚ 0å "ã Û 	Ý ß &Ñ &å !à	ˆ×	Ñ	˜8Ó	$€Ø#Ð ØÐ Ú2Ð ð 7<õ 
ð5Ø>Cñ5Ø7;ð5ØHKó5ó"1óGð )Ð ó	ð @Dñ2Ø/3ð2à1<ð2àHVó2ó&3óó
ð QSØ'+ð(Ø03ó(óV
Hóð Ð àÐ EÐFÐ ð $7¸|ÑLÐ Ø>Ð óóó*Uð /1Ð Ð+Ó 0ó\óHôKr'   