
    PmjK                      d Z ddlm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  ej        e          ZddlmZ ddlmZmZmZmZm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#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+ dd
l,m-Z-m.Z.m/Z/ ddl0m1Z1m2Z2 ddl3m4Z4 ddl0m5Z5 ddl6m7Z7m8Z8m9Z9 d`dadZ:dbdZ;dcdZ<dddZ=dedZ>d`dfd"Z?dgd%Z@dhd&ZAdid'ZBdhd(ZCdjd)ZDdkdld+ZEdd,dmd/ZFh d0ZGdnd3ZHdod4ZIdpd7ZJdddd8dqd>ZKdkdrd@ZL	 	 dsdtdDZMdudGZNdudHZOdvdIZPdwdJZQdedKZRdxdLZSddddMdydOZTdodPZUdzdRZVdddSd{dVZWdddSd|dWZXddddXd}dYZYddddXd~dZZZddddd[dd\Z[dd_Z\dS )zGShared runtime provider resolution for CLI, gateway, cron, and helpers.    )annotationsN)urlparse)AnyDictOptional)auth)CredentialPoolPooledCredential credential_pool_matches_providerget_custom_provider_pool_key	load_pool)
get_secret)	AuthErrorDEFAULT_CODEX_BASE_URLDEFAULT_QWEN_BASE_URLDEFAULT_XAI_OAUTH_BASE_URLPROVIDER_REGISTRY_agent_key_is_usable_nous_inference_env_overrideformat_auth_errorresolve_provider resolve_nous_runtime_credentials!resolve_codex_runtime_credentials%resolve_xai_oauth_runtime_credentials resolve_qwen_runtime_credentials$resolve_api_key_provider_credentials-resolve_external_process_provider_credentialshas_usable_secret)get_compatible_custom_providersload_confignormalize_extra_headers)custom_provider_aliasescustom_provider_slug)OPENROUTER_BASE_URL)is_official_openai_host)base_url_host_matchesbase_url_hostnameenv_int namestrdefaultreturnc                .    t          | |          }||n|S )a  Profile-scoped replacement for ``os.getenv`` on credential/provider reads.

    Routes through the secret scope (Workstream A): identical to ``os.getenv``
    when multiplexing is off, scope-aware (and fail-closed on an unscoped read)
    when on. Genuinely-global vars are handled inside ``get_secret`` and still
    read ``os.environ``. Keeps the ``(name, default) -> str`` contract every
    call site here already relies on.
    )_get_secret)r*   r,   vals      A/home/thesage/.hermes/hermes-agent/hermes_cli/runtime_provider.py_getenvr2   3   s!     dG
$
$C/33w.    valuec                v    |                                                                                      dd          S )N -)striplowerreplacer4   s    r1   _normalize_custom_provider_namer<   @   s,    ;;==  ((c222r3   hostboolc                \    | pd                                                     d          }|dv S )Nr)   .>   ::10.0.0.0	localhost	127.0.0.1)r9   rstrip)r=   hs     r1   _loopback_hostnamerG   D   s1    	##C((A<<<r3   cfg_base_urlcfg_providerc                D   |pd                                                                 }| pd                                 }|sdS |dk    rdS 	 ddlm}  ||          dk    rdS n# t          $ r Y nw xY wt          |d          rdS t          t          |                    S )u  Decide whether ``model.base_url`` may back bare ``custom`` runtime resolution.

    GitHub #14676: the model picker can select Custom while ``model.provider`` still reflects a
    previous provider. Reject non-loopback URLs unless the YAML provider is already ``custom``
    (or one of the local-server aliases that resolve to ``custom`` — ollama, vllm, llamacpp, …),
    so a stale OpenRouter/Z.ai base_url cannot hijack local ``custom`` sessions.
    r)   FcustomTr   r   openrouter.ai)r8   r9   hermes_cli.authr   	Exceptionr&   rG   r'   )rH   rI   cfg_provider_normbu_resolve_providers        r1   ,_config_base_url_trustworthy_for_bare_customrS   I   s     &+2244::<<

"	#	#	%	%B uH$$t
IIIIII.//8;;4 <   R11 u/33444s   A$ $
A10A1base_urlOptional[str]c                   | pd                                                                                     d          }t          |           }|dk    rdS t	          |           rdS |dk    rdS t          |          j                            d          }|                    d          s|                    d          rdS |d	k    rd
|v rdS dS )u^  Auto-detect api_mode from the resolved base URL.

    - Direct api.openai.com endpoints need the Responses API for GPT-5.x
      tool calls with reasoning (chat/completions returns 400).
    - Direct api.anthropic.com endpoints must use the native Messages
      API (``/v1/messages``).  Anthropic also exposes an OpenAI-compat
      ``/chat/completions`` shim on the same host, but Pro/Max OAuth
      subscriptions are only billed against the native Messages route;
      hitting the shim accounts against a separate "extra usage" pool
      that is empty by default and surfaces as HTTP 400 "You're out of
      extra usage."  See issue #32243.
    - Third-party Anthropic-compatible gateways (MiniMax, Zhipu GLM,
      LiteLLM proxies, etc.) conventionally expose the native Anthropic
      protocol under a ``/anthropic`` suffix — treat those as
      ``anthropic_messages`` transport instead of the default
      ``chat_completions``.
    - Kimi Code's ``api.kimi.com/coding`` endpoint also speaks the
      Anthropic Messages protocol (the /coding route accepts Claude
      Code's native request shape).
    r)   /zapi.x.aicodex_responsesapi.anthropic.comanthropic_messagesz
/anthropicz/anthropic/v1zapi.kimi.comz/codingN)r8   r9   rE   r'   r%   r   pathendswith)rT   
normalizedhostnamer[   s       r1   _detect_api_mode_for_urlr_   g   s    * .b''))//1188==J **H:  
 x(( !  
 &&&##J$++C00D}}\"" $dmmO&D&D $##>!!i:&=&=##4r3   providermodelc                R    t          |          }|r|S ddlm}  || ||          pdS )u  Resolve api_mode when no explicit/persisted mode applies.

    Precedence: URL detection (host-mandated wire shapes) first, then the
    transport the provider overlay itself declares via
    ``providers.determine_api_mode`` — which already handles host mandates,
    dual-wire providers, and the registry transport map — and only then the
    ``chat_completions`` default for genuinely unknown providers/endpoints.

    Before this helper the runtime paths consulted URL detection ONLY and
    silently landed reasoning providers on ``chat_completions`` whenever the
    hostname wasn't literally recognized. That is how ``openai-api`` pointed
    at OpenAI's data-residency hosts (``us.api.openai.com``) 400'd on every
    tool-calling turn: the provider declares ``codex_responses`` but the
    declaration was never consulted. Same latent class covered the other
    non-chat overlays (MiniMax family, copilot-acp).
    r   )determine_api_modechat_completions)r_   hermes_cli.providersrc   )r`   rT   ra   detectedrc   s        r1   _fallback_api_moderg      sK    " (11H 777777h%88N<NNr3   	model_cfgDict[str, Any]c                    t          |                     d                    }t          |          }|dk    r%|dk    rt                              d|pd           d}|p|pdS )a  Resolve api_mode for legacy/plain ``provider: custom`` endpoints.

    Custom endpoints should stay conservative by default. Only direct OpenAI/xAI
    URLs imply Responses API automatically; named custom providers can opt in via
    their own ``api_mode`` field. This also prevents a stale persisted
    ``model.api_mode: codex_responses`` from forcing generic relays onto the
    Responses path after upgrades or /reset.
    api_moderX   zMIgnoring persisted custom api_mode=codex_responses for non-OpenAI endpoint %sz	(unknown)Nrd   )_parse_api_modegetr_   loggerinfo)rh   rT   configured_modedetected_modes       r1   _resolve_plain_custom_api_moderr      sz     &immJ&?&?@@O,X66M+++AR0R0R[#	
 	
 	
 AmA/AAr3   c                r   t          |           }|sdS t          d |                    d          d         D                       rdS |dv sd|v rdS d |                    d          D             }|r+|d         d	v r!|                    d           |r
|d         d	v !t	          |          d
k     rdS |d         }d                    d |D                                                       }|r|d                                         sdS |dv rdS | d}t          |d          pd	                                S )u_  Look up `<VENDOR>_API_KEY` in the env, derived from the base URL host.

    Examples:
        https://api.deepseek.com/v1   → DEEPSEEK_API_KEY
        https://api.groq.com/openai/v1 → GROQ_API_KEY
        https://api.mistral.ai/v1     → MISTRAL_API_KEY
        https://generativelanguage.googleapis.com/v1beta/openai/ → GOOGLEAPIS_API_KEY

    Returns the env value (stripped) or "". Never returns env vars whose names
    are already explicitly checked elsewhere — those are handled by their own
    host-gated paths (OPENAI/OPENROUTER/OLLAMA).

    The vendor label is the *registrable* portion of the hostname: strip
    ``api.`` / ``www.`` prefixes, then take the second-to-last label
    (``api.deepseek.com`` → ``deepseek``). Falls back to "" for hostnames
    that don't yield a usable vendor label (IPs, loopback, single-label
    hosts).
    r)   c              3  >   K   | ]}|                                 V  d S N)isdigit.0chs     r1   	<genexpr>z(_host_derived_api_key.<locals>.<genexpr>   s*      
:
:B2::<<
:
:
:
:
:
:r3   r@   )rC   :c                    g | ]}||S  r~   )rx   lbls     r1   
<listcomp>z)_host_derived_api_key.<locals>.<listcomp>   s    888cC8c888r3   r   )apiwww   c              3  F   K   | ]}|                                 r|nd V  dS )_N)isalnumrw   s     r1   rz   z(_host_derived_api_key.<locals>.<genexpr>   s3      EEbjjll3EEEEEEr3   )OPENAI
OPENROUTEROLLAMA_API_KEY)
r'   anysplitpoplenjoinupperisalphar2   r8   )rT   r^   labelsvendor	sanitizedenv_names         r1   _host_derived_api_keyr      sv   & !**H r

:
:(.."5"5b"9
:
:
::: r>!!SH__r88X^^C00888F
 VAY.00

1  VAY.00
6{{Qr BZFEEfEEEEEKKMMI IaL0022 r666r%%%HHb!!'R..000r3   c                D   | pd                                 }|sdS t          |          pd                                }|sdS |dk    s*|                    d          s|                    d          rdS |                    d          rdS t	          |          dk    rdS dS )	u  Decide whether a configured ``model.base_url`` may back native Anthropic.

    Native ``provider: anthropic`` resolution honors ``model.base_url`` so users
    can point at Anthropic-compatible endpoints (official Anthropic/Claude hosts,
    Azure Foundry, MiniMax/Zhipu/LiteLLM-style ``/anthropic`` proxies, Kimi's
    ``/coding`` route). But a config can carry a *stale* non-Anthropic URL — e.g.
    ``provider: anthropic`` left with ``base_url: https://openrouter.ai/api/v1``
    after a provider switch — which would route Anthropic OAuth/setup-token
    traffic to an OpenAI-compatible aggregator and 404. Ignore those.

    Returns True only when the URL plausibly speaks the Anthropic Messages
    protocol; otherwise the caller falls back to ``https://api.anthropic.com``.
    r)   FrY   z.anthropic.comz.claude.comTz
.azure.comrZ   )r8   r'   r9   r\   r_   )rT   	candidater^   s      r1   _anthropic_base_url_override_okr      s     R&&((I u!),,299;;H u &&&(*;*;<L*M*M&QYQbQbcpQqQq&t&& t  	**.BBBt5r3   c                   | sdS 	 ddl }|                     d          }|                    d          s|dz  }|                    |dz   d          }|j        r[|                                                    d	g           }t          |          d
k    r |d                             dd          }|r|S n3# t          $ r&}t          	                    d| |           Y d}~nd}~ww xY wdS )zFQuery a local server for its model name when only one model is loaded.r)   r   NrW   z/v1z/models)r      )timeoutdata   idz$Auto-detect model from %s failed: %s)
requestsrE   r\   rm   okjsonr   rO   rn   debug)rT   r   urlrespmodelsmodel_idexcs          r1   _auto_detect_local_modelr   !  s    rLooc""||E"" 	5LC||C)OV|<<7 	$YY[[__VR00F6{{a!!9==r22 $#O L L L 	;XsKKKKKKKKL 2s   B.B6 6
C& C!!C&c                 r   t                      } |                     d          }t          |t                    rt          |          }|                    d          s |                    d          r|d         |d<   |                    d          pd                                }|                    d          pd                                }d|v pd|v }| }|r|r|rt          |          }|r||d<   |S t          |t                    r*|                                rd|                                iS i S )Nra   r,   r)   rT   rC   rD   )r    rm   
isinstancedictr8   r   r+   )configrh   cfgr,   rT   is_localis_fallbackrf   s           r1   _get_model_configr   8  sB   ]]F

7##I)T"" 9oowwy!! 	*cggg&6&6 	* \C	N779%%+2244GGJ''-24466(*EkX.E!k 	* 	* 	*/99H *!)I
)S!! .ioo&7&7 .9??,,--Ir3   configured_providerc                    | pd                                                                 }|pd                                                                 }|sdS |dk    r|dk    p|                    d          S ||k    S )aT  Check whether a persisted api_mode should be honored for a given provider.

    Prevents stale api_mode from a previous provider leaking into a
    different one after a model/provider switch.  Only applies the
    persisted mode when the config's provider matches the runtime
    provider (or when no configured provider is recorded).
    r)   TrK   custom:)r8   r9   
startswith)r`   r   normalized_providernormalized_configureds       r1   $_provider_supports_explicit_api_moder   N  s     $>r002288::06B==??EEGG  th&&$0_4I4T4TU^4_4__ $777r3   target_modelapi_keyr   c                  t          |                     d          pd                                                                          }t	          |                     d                    }|rt          d|          r|S t          |p|                     d          pd                                          }|sdS 	 ddlm}  |||	          S # t          $ r Y dS w xY w)
Nr`   r)   rk   copilotr,   rd   r   )copilot_model_api_mode)r   )	r+   rm   r8   r9   rl   r   hermes_cli.modelsr   rO   )rh   r   r   r   rp   
model_namer   s          r1   _copilot_runtime_api_moder   _  s    immJ77=2>>DDFFLLNN%immJ&?&?@@O ?	K^__  \CY]]9%=%=CDDJJLLJ "!!"<<<<<<%%j'BBBB " " "!!!"s   <C 
CC>   rX   bedrock_converserd   codex_app_serverrZ   rawr   c                    t          | t                    r1|                                                                 }|t          v r|S dS )z@Validate an api_mode value from config. Returns None if invalid.N)r   r+   r8   r9   _VALID_API_MODES)r   r]   s     r1   rl   rl     sD    #s YY[[&&((
)))4r3   c                 "    t                      pdS )uV  Return the trusted Nous runtime base URL override, if configured.

    Delegates to ``auth._nous_inference_env_override`` so every
    ``NOUS_INFERENCE_BASE_URL`` read shares one normalization path
    (trailing-slash stripping, blank → empty). The env source is trusted
    and intentionally bypasses the network host allowlist there.
    r)   )r   r~   r3   r1   !_nous_inference_base_url_overrider     s     ())/R/r3   rk   Optional[Dict[str, Any]]c                    |s|S | dvr|S t          |                    d          pd                                                                          }|dk    rdS |S )u  Optional opt-in: rewrite api_mode → "codex_app_server" for OpenAI/Codex
    providers when the user has explicitly enabled that runtime via
    `model.openai_runtime: codex_app_server` in config.yaml.

    Default behavior is preserved: when the key is unset, "auto", or empty,
    this function is a no-op. Only providers in {"openai", "openai-codex"}
    are eligible — other providers (anthropic, openrouter, etc.) cannot be
    rerouted through codex.

    Returns the (possibly-rewritten) api_mode.>   openaiopenai-codexopenai_runtimer)   r   )r+   rm   r8   r9   )r`   rk   rh   runtimes       r1   %_maybe_apply_codex_app_server_runtimer     sp       111)-- 0117R88>>@@FFHHG$$$!!Or3   )rh   poolr   entryr
   requested_providerr   Optional[CredentialPool]c           	     	   |pt                      }|p|                    d          pd}t          |dd           pt          |dd           pd                    d          }t          |dd           pt          |dd          }d}	| d	k    rd
}	|pt          }n| dk    rd
}	|pt
          }n| dk    rd}	|pt          }n| dk    r%d}	t          j        |           }
|p
|
r|
j        nd}n| dk    rd}	t          |                    d          pd          
                                                                }d}|dk    rZt          |                    d          pd          
                                                    d          }t          |          sd}|p|pd}n| dk    r|pt          }n| dk    rd
}	n| dk    r#ddlm}  ||          }	t!                      p|}n| dk    r7t#          |t          |dd          |          }	|pt          d         j        }nZ| dk    rt          |                    d          pd          
                                                                }|dk    rst          |                    d          pd          
                                                    d          }|r|}t%          |                    d                    }|r|}	|r/|	dk    r)	 ddlm}  ||          }n# t*          $ r d }Y nw xY w|r|}	|	dk    rt-          j        dd|          }nCt          |                    d          pd          
                                                                }t          j        |           }
|
o0|                    d          |
j                            d          k    }|| k    rO|rMt          |                    d          pd          
                                                    d          }|r|}t%          |                    d                    }| dv rddlm}  || |          }	n&|rt3          | |          r|}	nt5          | ||          }	| dv rddlm}  || |	|          }t9          | |	|           }	| d!k    rt;          j        |          }| |	||t          |d"d#          ||d$S )%Nr,   r)   runtime_base_urlrT   rW   runtime_api_keyaccess_tokenrd   r   rX   	xai-oauth
qwen-oauthminimax-oauthrZ   	anthropicr`   https://api.anthropic.com
openrouterxainousr   nous_api_moder   r   azure-foundryrk   azure_foundry_model_api_mode/v1/?$   opencode-goopencode-zenopencode_model_api_modenormalize_opencode_base_url)r`   rk   rh   lmstudiosourcer   )r`   rk   rT   r   r   credential_poolr   )r   rm   getattrrE   r   r   r   r   inference_base_urlr+   r8   r9   r   r$   re   r   r   r   rl   r   r   rO   resubr   r   rg   r   r   auth_mod$_normalize_lmstudio_runtime_base_url)r`   r   r   rh   r   r   effective_modelrT   r   rk   pconfigrI   rH   r   rp   r   inferredr   pool_url_is_defaultr   r   s                        r1    _resolve_runtime_from_pool_entryr     s    0.00I $Ey}}Y'?'?E2O1488bGE:W[<\<\b`bjjknooHe.55[XZ9[9[G!H>!!$55	[	 	 $99	\	!	!%44	_	$	$
 (#'11Ng M : :2	[	 	 '9==44:;;AACCIIKK;&&y}}Z88>B??EEGGNNsSSL2<@@ "!J8J/J	\	!	!22	U		$	V		666666 =11466B(	Y		,E,b11(
 
 

 N0;N	_	$	$9==44:;;AACCIIKK?**y}}Z88>B??EEGGNNsSSL ('-immJ.G.GHHO +*
  	$x+??? JJJJJJ77HH        $#+++viX66H!)--
";";"ArBBHHJJPPRR
 $'11%h(//#*>*>'B\BcBcdgBhBh*h(**/B*y}}Z88>B??EEGGNNsSSL ('))--
*C*CDD666 BAAAAA..xIIHH 	O!EhPc!d!d 	O&HH
 *(HoNNH 222AAAAAA..x8LL 5H	  H :@JJ %6220  s   5L LL	requestedc                   | r:|                                  r&|                                                                  S t                      }|                    d          }t	          |t
                    r:|                                 r&|                                                                 S t          dd                                                                           }|r|S dS )z=Resolve provider request from explicit arg, config, then env.r`   HERMES_INFERENCE_PROVIDERr)   auto)r8   r9   r   rm   r   r+   r2   )r   rh   rI   env_providers       r1   resolve_requested_providerr  K  s     )Y__&& )  &&(((!##I==,,L,$$ ,););)=)= ,!!##))+++ 6;;AACCIIKKL 6r3   provider_labelapi_mode_overrideprovider_namec                P   t          | |          }|sdS 	 t          |          }|                                sdS |                                }|dS t	          |dd          pt	          |dd          }|sdS ||pt          |           pd| |d| |dS # t          $ r Y dS w xY w)	zXCheck if a credential pool exists for a custom endpoint and return a runtime dict if so.r  Nr   r   r)   rd   zpool:)r`   rk   rT   r   r   r   )r   r   has_credentialsselectr   r_   rO   )rT   r  r  r  pool_keyr   r   pool_api_keys           r1   _try_resolve_from_custom_poolr  ^  s     ,HMRRRH t""##%% 	4=4u&7>>d'%Q_acBdBd 	4&)e-Eh-O-OeSe #(h((#
 
 	
    tts"   #B B $B :B 
B%$B%resultNonec                    dD ]:}|                      |          }t          |t                    r|dk    r||d<    dS ;dS )aq  Propagate a per-provider output cap onto the resolved runtime dict.

    Accepts ``max_output_tokens`` or ``max_tokens`` on a ``custom_providers``
    entry so a provider block can pin its own output limit. Gateway and CLI
    map this onto ``AIAgent.max_tokens`` only when the top-level
    ``model.max_tokens`` isn't set, so the documented global key still wins.
    )max_output_tokens
max_tokensr   r  N)rm   r   int)r   r  _k_vs       r1   _lift_max_output_tokensr  ~  sY     2  YYr]]b# 	266*,F&'FF	 r3   c                \    t          |                     d                    }|r||d<   dS dS )zCopy a validated ``extra_headers`` dict from a provider entry.

    SECURITY: header values routinely carry credentials (Cloudflare Access
    service tokens, proxy auth, custom bearer schemes). Never log them.
    extra_headersN)r!   rm   )r   r  r  s      r1   _lift_extra_headersr    s?     ,EIIo,F,FGGM 0"/0 0r3   c           	        t          | pd          }|sd S |dk    rd S |dk    ri|                    d          sT	 t          j        |          }|pd                                                                |k    rd S n# t          $ r Y nw xY wt                      }|                    d          }t          |t                    rAddlm} |                                D ]%\  }}t          |t                    s ||          s(t          |                    dd          pd                                          }|r"t          |d                                          nd}	|	s7t          |                    d	d          pd                                          }	|                    d
d          }
|t!          t          |
p|          t          |                    v r"|                    d          p+|                    d          p|                    d          pd}|r|                    d
|          |                                |	|                    dd          d}|                    d          }t          |t                    rt          |          |d<   t#          ||           t%          |                    d          p|                    d                    }|r||d<   t'          ||           |c S '|                    d          }t          |t                    rt(                              d           d S t-          |          }|sd S |D ]!}t          |t                    s|                    d
          }|                    d          }t          |t                    rt          |t                    snt          |                    dd          pd                                          }|t!          ||          vr|                                |                                t          |                    d	d          pd                                          d}t          |                    dd          pd                                          }|r||d<   |r||d<   |                    d          }t          |t                    rt          |          |d<   t#          ||           t%          |                    d                    }|r||d<   t          |                    dd          pd                                          }|r||d<   t'          ||           |c S d S )Nr)   r   rK   r   	providersr   )is_provider_enabledkey_envr   r*   r   r   rT   default_model)r*   rT   r   ra   
extra_bodyrk   	transportcustom_providerszcustom_providers in config.yaml is a dict, not a list. Each entry must be prefixed with '-' in YAML. Run 'hermes doctor' for details.provider_key)r*   rT   r   ra   )r<   r   r   r   r8   r9   r   r    rm   r   r   hermes_cli.configr  itemsr+   r2   r"   r  rl   r  rn   warningr   )r   requested_norm	canonicalr   r  r  ep_namer   r  resolved_api_keydisplay_namerT   r  r  rk   r  r*   r   r   s                      r1   _get_named_custom_providerr)    s   45G5M2NNN t" t!!.*C*CI*N*N!	 1.AAI R&&((..00NBBt C  	 	 	D	 ]]F 

;''I)T"" /"999999'oo// -	" -	"NGUeT** 
 '&u-- %))Ir228b99??AAG?FNww3399;;;B# O#&uyyB'?'?'E2#F#F#L#L#N#N  99VR00L!8L+G,,G" "  
 !99U++^uyy/?/?^599ZCXCX^\^ " %		&' : :$,NN$4$4#3!&?B!?!?	 F "'<!8!8J!*d33 @/3J/?/?|,'v666  /uyy/D/D/^		R]H^H^__H 6-5z*+E6:::!MMM zz"455"D)) /	
 	
 	

 t6v>> t!  %&& 	yy  99Z(($$$ 	Jx,E,E 	599^R88>B??EEGG!8|!L!LLLJJLL ((599Y339r::@@BB
 

 eii	2..4"55;;== 	( 'F9 	2%1F>"YY|,,
j$'' 	4#'
#3#3F< E6***"599Z#8#899 	*!)F:7B//5266<<>>
 	)(F7Ov...4s   A= =
B
	B
c                H    	 t          |           duS # t          $ r Y dS w xY w)u~  Return True when config defines a custom provider matching the request.

    Thin public wrapper around :func:`_get_named_custom_provider` so other
    modules (e.g. the cronjob tool) can decide whether a provider name will
    actually resolve to a configured ``providers:`` / ``custom_providers:``
    entry — without reaching into a private helper or duplicating the scan.
    NF)r)  rO   )r   s    r1   has_named_custom_providerr+  &  s=    )*<==TII   uus    
!!c           
        t          |           }|sdS 	 t                      }n# t          $ r Y dS w xY w|                    d          }t	          |t
                    r|                                D ]\  }}t	          |t
                    s|                    d          p+|                    d          p|                    d          pd}t          |          |k    r,t          t          |          t          |                    c S 	 t          |          }n# t          $ r d}Y nw xY w|pg D ]}t	          |t
                    s|                    d          }t	          |t                    r|
                                sWt          |                    d                    |k    r5t          |t          |                    dd          pd                    c S dS )	uD  Map an endpoint URL back to its canonical ``custom:<name>`` menu key.

    Returns the ``custom:<normalized-name>`` slug of the first ``providers:``
    / ``custom_providers:`` entry whose base_url matches, or ``None`` when no
    entry owns the URL.

    Session persistence stores the agent's *resolved* provider, and for every
    named custom endpoint that is the literal string ``"custom"`` — the entry
    name is lost, and the api_key is deliberately never persisted. The
    endpoint URL is the one durable fact that survives the round-trip, so
    this reverse lookup lets persist/rebuild paths recover the entry identity
    (and with it key_env/api_key/api_mode resolution via
    :func:`_get_named_custom_provider`) instead of failing with
    ``auth_unavailable`` or silently rebuilding with placeholder credentials.
    Nr  r   r   rT   r)   r*   r   )_normalize_base_url_for_matchr    rO   rm   r   r   r"  r#   r+   r   r8   )	rT   targetr   r  r&  r   	entry_urlr  r*   s	            r1   find_custom_provider_identityr0  4  s'     +844F t   tt 

;''I)T"" H'oo// 	H 	HNGUeT** 		%  SEIIe$4$4S		*8M8MSQS  -Y776AA+CLL#g,,GGGGG B :6BB       !'R 
 
%&& 	yy  $$$ 	DJJLL 	(:)>)>??6II'EIInb117R88     J 4s   $ 
22D   D/.D/c           
     h   t          | pd                                                                          sdS 	 t                      }n# t          $ r Y dS w xY wdfd}|                    d          }t          |t                    rg|                                D ]R\  }}t          |t                    s ||          r,t          t          |          t          |                    c S S	 t          |          }n# t          $ r d}Y nw xY w|pg D ]}t          |t                    s|                    d	          }t          |t                     r|                                sW ||          r5t          |t          |                    d
d          pd                    c S dS )a  Map a model id back to the ``custom:<name>`` entry that serves it.

    Returns the ``custom:<normalized-name>`` slug of the first ``providers:``
    / ``custom_providers:`` entry whose ``model`` / ``default_model`` matches,
    or whose ``models`` catalog (dict or list shape) contains the id.
    ``None`` when no entry serves the model.

    Companion to :func:`find_custom_provider_identity` (URL reverse-lookup)
    for the persistence paths where no base_url survived the round-trip: the
    session row always stores the model name, and a custom endpoint's model
    ids (e.g. an in-house SFT checkpoint) virtually never collide with
    catalog models on built-in providers, so the model is the last durable
    fact that can recover the entry identity.
    r)   Nr   ri   r-   r>   c                (   dD ]Y}|                      |          }t          |t                    r-|                                                                k    r dS Z|                      d          }t          |t
                    r-t          fd|                                D                       S t          |t                    r|D ]}t          |t                    r-|                                                                k    r dS t          |t
                    rl|                     d          p|                     d          }t          |t                    r-|                                                                k    r dS dS )N)ra   r  Tr   c              3     K   | ];}t          |                                                                          k    V  <d S ru   )r+   r8   r9   )rx   midr.  s     r1   rz   zVfind_custom_provider_identity_by_model.<locals>._entry_serves_model.<locals>.<genexpr>  sU        7:C  &&((F2     r3   r   r*   F)	rm   r   r+   r8   r9   r   r   keyslist)r   keyr4   r   itemr4  r.  s         r1   _entry_serves_modelzCfind_custom_provider_identity_by_model.<locals>._entry_serves_model  s   - 	 	CIIcNNE%%% %++--*=*=*?*?6*I*Itt8$$fd## 	    >Dkkmm      fd## 	$ $ $dC((  TZZ\\-?-?-A-AV-K-K44dD)) $((4..<DHHV,<,<C!#s++ $		0A0A0C0Cv0M0M#ttur3   r  r*   r   )r   ri   r-   r>   )r+   r8   r9   r    rO   rm   r   r   r"  r#   r   )	ra   r   r9  r  r&  r   r  r*   r.  s	           @r1   &find_custom_provider_identity_by_modelr:  j  s    "##%%++--F t   tt     ( 

;''I)T"" H'oo// 	H 	HNGUeT** ""5)) H+CLL#g,,GGGGGH :6BB       !'R 
 
%&& 	yy  $$$ 	DJJLL 	u%% 	'EIInb117R88    	 4s#   A 
AA4D DD)rT   config_providerra   r;  c                   | rt          |           }|r|S |rt          |          }|r|S t          |pd                                          }|sV	 t          t	                                          d          pd                                          }n# t          $ r d}Y nw xY w|s2t          j                            dd                                          }t          |          }|r|dv rdS 	 t          |          }|Qt          t          |                    d          pd                    }|r|S |                    d          r|S d| S n# t          $ r Y nw xY wdS )uq  Recover a routable ``custom:<name>`` identity for a bare custom provider.

    The bare string ``"custom"`` is the *resolved billing class* shared by
    every named ``providers:`` / ``custom_providers:`` entry — it is NOT a
    routable provider identity (``resolve_runtime_provider("custom")`` falls
    through to the OpenRouter default URL with no api_key, which surfaces to
    the user as "No LLM provider configured").

    Any code path that persists or restores a session's provider override
    must run the resolved provider through this helper so a bare ``"custom"``
    is upgraded back to its durable ``custom:<name>`` menu key. Three
    recovery sources, in priority order:

    1. ``base_url`` — reverse-lookup the entry that owns the endpoint URL
       (the one fact that always survives the persistence round-trip when a
       URL was recorded).
    2. ``model`` — reverse-lookup the entry that serves the session's model
       (``model``/``default_model``/``models`` catalog). The session row
       always stores the model name, so when no base_url survived (the
       recurring Desktop/TUI regression vector) the model is the last
       session-scoped fact that can recover the entry — and unlike the
       config fallback below it stays correct after the user points their
       global default at a different provider.
    3. ``config_provider`` — the active ``config.model.provider`` (or its
       ``provider``/``HERMES_INFERENCE_PROVIDER`` equivalent). When neither
       a base_url nor a model recovered the entry, the configured provider
       is the only durable identity left, so fall back to it when it names
       a real entry.

    Returns ``custom:<name>`` when a routable identity is recovered, else
    ``None`` (caller keeps whatever it had — bare ``"custom"`` only as a last
    resort, e.g. a genuine ad-hoc endpoint with no config entry).
    r)   r`   r   >   r   rK   r   NrT   r   )r0  r:  r+   r8   r   rm   rO   osenvironr<   r)  r   )rT   r;  ra   identityr   candidate_normr   s          r1   canonical_custom_identityrA    s   P  0:: 	O  9%@@ 	O O)r**0022I 	-//33J??E2FFLLNNII 	 	 	III	 LJNN#>CCIIKK	4Y??N ^/OOOt*955 5S:9N9N9TRT5U5UVVH  ((33 &%%-^---     4s2   AB B#"B#2AE 8E E 
E"!E"c                    t          | pd                                                              d                                          S )Nr)   rW   )r+   r8   rE   r9   r;   s    r1   r-  r-  	  s:    u{!!##**3//55777r3   custom_providerc                    |                      d          }t          |t                    r|si S dt          |          iS )Nr  )rm   r   r   )rC  r  s     r1   "_custom_provider_request_overridesrE    sF     $$\22Jj$'' z 	$z**++r3   explicit_api_keyexplicit_base_urlrG  rH  c           
     	   | pd                                                                 }|r/|dk    r)	 ddlm}  ||          dk    rd}n# t          $ r Y nw xY w|dk    r|r|                                                     d          }t          |dd           }|rd|d<   |S t          |d          pt          |d	          }t          |d
          }|pd                                 |r"t          dd                                           nd|r"t          dd                                           ndt          |          g}	t          d |	D             d          pd}
dt          |          pd||
d| dS t          |           }|sd S |pd                                 p|                    dd                              d          }|sd S t          |d|                    d          |                    d                    }|r|                    d          }|r||d<   t          |                    d          t                    r|d         |d<   t!          |          }|r+i t#          |                    d          pi           ||d<   |                    d          rt#          |d                   |d<   |S t          |d          pt          |d	          }t          |d
          }|pd                                 t%          |                    dd          pd                                           t          t%          |                    dd          pd                                           d                                           |r"t          dd                                           nd|r"t          dd                                           ndt          |          g}	t          d |	D             d          }
d|                    d          pt          |          pd||
pdd|                    d|            d}|                    d          r|d         |d<   t          |                    d          t                    r|d         |d<   |                    d          rt#          |d                   |d<   t!          |          }|r||d<   |S )Nr)   rK   r   rL   rW   zdirect-aliasr   
openai.comopenai.azure.comrM   OPENAI_API_KEYOPENROUTER_API_KEYc              3  8   K   | ]}t          |          |V  d S ru   r   )rx   cs     r1   rz   z0_resolve_named_custom_runtime.<locals>.<genexpr>A  s0      CC1.?.B.BCQCCCCCCr3   no-key-requiredrd   r`   rk   rT   r   r   r   rT   rk   r*   r  ra   r  request_overridesr  r   r  c              3  8   K   | ]}t          |          |V  d S ru   rO  rx   r   s     r1   rz   z0_resolve_named_custom_runtime.<locals>.<genexpr>}  s1      bb)EVW`EaEabIbbbbbbr3   zcustom_provider:r`   rk   rT   r   r   )r8   r9   rN   r   rO   rE   r  r&   r2   r   nextr_   r)  rm   r   r  rE  r   r+   )r   rG  rH  r$  rR   rT   pool_result_da_is_openai_url_da_is_openrouterapi_key_candidatesr   rC  r   rS  _cp_is_openai_url_cp_is_openrouterr  s                    r1   _resolve_named_custom_runtimer^    s    ).B5577==??N .H44	MMMMMM  00H<<!) 	 	 	D	!!&7!$**,,33C88 4HhMM 	$2K!3HlKK  COdem  pB  PC  PC3HoNN#**,,:KSW%r**00222QS:KTW)2..44666RT "(++	
 CC*CCC
 
   	
 !0::P>P $"4
 
 	
 11CDDO t 
	 b'')) 	/z2..fSkk   t 0(ODWDWXbDcDc  tC  tG  tG  HN  tO  tO  P  P  PK  %((11
 	.#-K o))*=>>DD 	T/>?R/SK+,>OO 	0{':;;ArBB0#0K+, // 	R+/0P+Q+QK(/,GG~K`aik}K~K~//JJ		R&&((O	2..4"55;;==O''	266<"==CCEErJJPPRR 7H	P!2	&	&	,	,	.	.	.b6G	P%r	*	*	0	0	2	2	2b 	h'' bb/AbbbdfggG #''
33 #H--//V_%8%8AS%T%TVV F 7## 3)'2w/%%&9::C@@ K&56I&J"# ?++ I"&'G"H"H:?KK 8&7"#Ms   A
 

AAc           	        t                      }t          |                    d          t                    r|                    d          nd}t          |                    d          t                    r|                    d          nd}d}dD ]V}|                    |          }t          |t                    r*|                                r|                                } nW| pd                                                                }	|                                                                }|	r/|	dk    r)	 ddlm}
  |
|	          dk    rd}	n# t          $ r Y nw xY wt          dd                                          }t          d	d                                          }d
}|                                r+|s)|	dk    r|r|dk    rd}n|	dk    rt          ||          rd}|pd                                p"|p |r|                                ndp|pt                              d          }t          |d          }|p(|	dk    o"|p||k    o||pd                    d          k    }|r |t          d          t          d          g}n~t          |d          }t          |d          }t          |d          }||r|nd|rt          d          nd|s|rt          d          nd|rt          d          ndt          |          g}t          d |D             d          }|s|rdnd}|	dk    rdnd}|dk    rA|r?t!          ||t#          |                    d                    |	dk    r| nd           }|r|S |dk    r|s|sd}||dk    rt%          ||          n2t#          |                    d                    pt'          |          pd|||dS )NrT   r)   r`   )r   r   rK   r   rL   r$   CUSTOM_BASE_URLFr   TrW   rM   r   rM  rL  z
ollama.comrJ  rK  OLLAMA_API_KEYc              3  z   K   | ]6}t          |          t          |pd                                           V  7dS )r)   N)r   r+   r8   rU  s     r1   rz   z._resolve_openrouter_runtime.<locals>.<genexpr>  sI      jj)M^_hMiMijY_"			#	#	%	%jjjjjjr3   explicitz
env/configrk   r  rQ  rd   rV  )r   r   rm   r+   r8   r9   rN   r   rO   r2   rS   r$   rE   r&   r   rW  r  rl   rr   r_   )r   rG  rH  rh   rH   rI   cfg_api_keykvr$  rR   env_openrouter_base_urlenv_custom_base_urluse_config_base_urlrT   _is_openrouter_url_is_openrouter_contextr[  _is_ollama_url_is_openai_url_is_openai_azurer   r   effective_providerrX  s                            r1   _resolve_openrouter_runtimerp    s    "##I0:9==;T;TVY0Z0Zb9==,,,`bL0:9==;T;TVY0Z0Zb9==,,,`bLK  MM!a 	!'')) 	''))KE(.B5577==??N%%''--//L  .H44	MMMMMM  00H<<!) 	 	 	D	 &&;R@@FFHH!"3R88>>@@
   '$5 'V## +<6#9#9&*#x'',X,-
 -
' #' 
	 b'')) 		$7?L   R	 #	 fSkk  /xII 0 ,& 	D$K4K(K	D06B>>sCCC 
   
())$%%
 2(LII1(LII1(<NOO /7[[R.<ZW%&&&XZ/=ZAQZW%&&&XZ.@ZW)***XZ
 "(++
 jj7Ijjj
 G
 -T0ATZZF &4x%?%?\ X%%(% 4(/)--
:S:S*T*T0>(0J0J,,PT
 
 
  	X%%g%>P%# ')) 39hGGGY]]:6677 #H--
 
 
s   ?E 
E$#E$)rG  rH  r   c           	        t          |pd                                          }t          |pd                                                              d          }t          |                    d          pd                                                                          }d}d}d}	i }
|dk    rt          |                    d          pd                                                              d          }t          |                    d                    pd}t          |                    d	          pd                                                                          pd}	|                    d
          }t          |t                    r|}
t          |p|                    d          pd                                          }|r/|dk    r)	 ddlm	}  ||          }n# t          $ r d}Y nw xY w|r|}t          dd                                                              d          }|p|p|}|st          d          |dk    rt          j        dd|          }|	dk    r|r|}d}d}n	 ddlm}m}m} n&# t          $ r}t          d| d          |d}~ww xY wt          |
                    d          pd                                          p|}	  ||          } ||          }n/# t&          $ r"}t          t          |                    |d}~ww xY w|}d}d}i }|dk    r=t          |
                    d          pd                                          }|r||d<   d||||||| dS |}|s'	 ddlm}  |d          pd}n# t          $ r d}Y nw xY w|s"t          dd                                          }|st          d          |s|rdnd}d|||d|| d S )!a  Resolve an Azure Foundry runtime entry.

    Reads ``model.base_url`` + ``model.api_mode`` from config.yaml (or
    explicit overrides), pulls the API key from ``.env`` / env var, and
    strips a trailing ``/v1`` for Anthropic-style endpoints because the
    Anthropic SDK appends ``/v1/messages`` internally.

    When ``model.auth_mode == "entra_id"`` (and the model is OpenAI-style),
    the returned ``api_key`` is a zero-arg callable produced by
    :func:`agent.azure_identity_adapter.build_token_provider` rather than
    a string. Downstream code that constructs an OpenAI SDK client passes
    this through unchanged (the SDK accepts ``Callable[[], str]`` for
    ``api_key`` and calls it before every request). Code paths that need
    a string (logging, manual HTTP probes, header injection) must use the
    helpers in ``agent.azure_identity_adapter``.

    Raises :class:`AuthError` when required values are missing.
    r)   rW   r`   rd   r   r   rT   rk   	auth_modeentrar,   rZ   r   r   NAZURE_FOUNDRY_BASE_URLzpAzure Foundry requires a base URL. Set it via 'hermes model' or the AZURE_FOUNDRY_BASE_URL environment variable.r   entra_idrc  )EntraIdentityConfigSCOPE_AI_AZURE_DEFAULTbuild_token_providerzAzure Foundry Entra ID auth requires the 'azure-identity' package. Install it with: pip install azure-identity (import failed: )scope)rz  )r   )r`   rk   rT   r   rr  rs  r   r   )get_env_valueAZURE_FOUNDRY_API_KEYa  Azure Foundry requires an API key. Set AZURE_FOUNDRY_API_KEY in ~/.hermes/.env or run 'hermes model' to configure. To use keyless Microsoft Entra ID auth instead, set model.auth_mode: entra_id in config.yaml (or pick 'Microsoft Entra ID' in 'hermes model').r   )r`   rk   rT   r   rr  r   r   )r+   r8   rE   rm   r9   rl   r   r   r   r   rO   r2   r   r   r   agent.azure_identity_adapterrv  rw  rx  ImportErrorr!  r{  )r   rh   rG  rH  r   explicit_base_url_cleanrI   rH   cfg_api_modecfg_auth_mode	cfg_entra_entrar   r   r   env_base_urlrT   r   r   rr  rv  rw  rx  r   rz  entra_configtoken_providerclean_entraconfigured_scoper{  s                                 r1   _resolve_azure_foundry_runtimer  &  s1   4 +1r2288::!"3"9r::@@BBII#NNy}}Z006B77==??EEGGLL%LM "I&&9==44:;;AACCJJ3OO&y}}Z'@'@AAWEWIMM+66C)DDJJLLRRTTaXaw''fd## 	I ,H)--	*B*BHbIIOOQQO $<+???	FFFFFF33ODDHH 	 	 	HHH	 	$#L3R88>>@@GGLLL&F,F,H 
?
 
 	
 +++6)R22  
"" !	# ,GF!II          
    .'*. . .  	 IMM'**0b117799 *) 322      "6!5\!J!J!J 3 3 3C))s23$GF"I
"""9==#9#9#?R@@FFHH 8'7G$ ($ " "4	
 	
 		
 G 	777777#m$;<<BGG 	 	 	GGG	 ?1266<<>> 
7
 
 	
 -P0APZZF# 0  sT   G& &G54G55
J   
J#
JJ#K8 8
L$LL$N N%$N%c           
     &   t          |pd                                          }t          |pd                                                              d          }|s|sd S | dk    rt          |                    d          pd                                                                          }d}|dk    rZt          |                    d          pd                                                              d          }t          |          sd}|p|pd}|}	|	s!ddlm}
  |
            }	|	st          d	          dd
||	d|dS | dk    r|pt          }|}	d }|	sft                      }|                    dd          }	|                    d          }|s+|                    dd                              d          p|}dd||	d||dS | dk    rddlm} t          j        d          pi }|p`t                      pRt          |                    d          pt          j                                                                      d          }|pct#          |t%          dt'          dd                              r6t          |                    d          pd                                          nd}	|                    d          p|                    d          }|	st)          t+          t-          dd                              }|                    dd          }	|                    d          }|s+|                    dd                              d          p|}d ||p|                    d          pd          ||	d||dS | d k    rt/          ||||!          S t1          j        |           }|r|j        dk    rd}|j        r:t-          |j        d                                                              d          }|}|sF| d"v r9t7          |           }|                    dd                              d          }n	|p|j        }|}	|	sPt7          |           }|                    dd          }	|s)|                    dd                              d          }d#}| d$k    rt;          ||	|%          }n| d&k    rd}nt          |                    d          pd                                                                          }t=          |                    d'                    }|rt?          | |          r|}n'tA          | ||p|                    dd                    }| ||                    d          |	d|dS d S )(Nr)   rW   r   r`   rT   r   r   resolve_anthropic_tokenNo Anthropic credentials found. Set ANTHROPIC_TOKEN or ANTHROPIC_API_KEY, run 'claude setup-token', or authenticate with 'claude /login'.rZ   rc  rR  r   r   last_refreshrX   r`   rk   rT   r   r   r  r   r   r   r   <   HERMES_NOUS_MIN_KEY_TTL_SECONDS  	agent_keyagent_key_expires_at
expires_atHERMES_NOUS_TIMEOUT_SECONDS15timeout_secondsr,   r`   rk   rT   r   r   r  r   r   )r   rh   rG  rH  >   kimi-codingkimi-coding-cnrd   r   r   r   rk   )!r+   r8   rE   rm   r9   r   agent.anthropic_adapterr  r   r   r   re   r   r   get_provider_auth_stater   DEFAULT_NOUS_INFERENCE_URLr   maxr(   r   floatr2   r  r   	auth_typebase_url_env_varr   r   r   rl   r   rg   )r`   r   rh   rG  rH  r   rI   rH   rT   r   r  r  credsr   stater  r   env_urlrk   r   rp   s                        r1   _resolve_explicit_runtimer    su    +1r2288::-344::<<CCCHH $5 t;9==44:;;AACCIIKK;&&y}}Z88>B??EEGGNNsSSL2<@@ "!$SS8S" 	GGGGGG--//G V  
 $,  "4
 
 	
 >!!$>(>" 	M577Eii	2..G 99^44L$ M 99Z44;;C@@LH&)  ("4
 
 	
 6666666088>B o022o599122Yh6YZZ``bbiijmnn 	 # 
#B A4HHII C		+&&,"--33555
  	 YY566Q%))L:Q:Q
 	M4 %g.KT&R&R S S  E ii	2..G<00J$ M 99Z44;;C@@LH%l&TimmI6N6N&TRTUU  $"4
 
 	
 ?""-1-/	
 
 
 	
  #H--G 0
7$	11# 	Pg6;;AACCJJ3OOG$ 	A<<<<XFF 99Z44;;C@@"@g&@" 	A8BBEii	2..G A 99Z44;;C@@%y  0)  HH
 (HH"%immJ&?&?&E2"F"F"L"L"N"N"T"T"V"V-immJ.G.GHHO #GRe#f#f * .h(T	iQS8T8T 
 !  ,, "4
 
 	
 4r3   )r   rG  rH  r   c           
     ^!  ? t          |           }ddlm}m}  |            }t	          |t
                    r|                    d          nd}t	          |t
                    rK|                    |          }	t	          |	t
                    r! ||	          st          d|d| d          |dk    r	dd	d
dd|dS |pd                                }
|dk    rzd|
v rv|pd                                pCt          dd                                          p!t          dd                                          }dd|

                    d          |d|dS |dk    r"t          |t                      |||          }|S |dv rBddlm}  |            \  }}|r|st          d          dd	|
                    d          |d|dS t!          |||          }|r||d<   |S |s|st                      }t#          |                    d          pd                                                                          }t#          |                    d          pd                                          ??r:|d v r6d!}t'          ?fd"|D                       st)          |||          }||d<   |S t+          |||#          }t                      }t-          ||||||$          }|r|S |d%k    }|d%k    rt#          |                    d          pd                                                                          }t#          |                    d          pd                                          ?t          d&d                                          }t          d'd                                          }t/          |p|p|          }?r|d(v rd)}t/          |p|          }|d*v o| o| }	 |rt1          |          nd}n# t2          $ r d}Y nw xY w|r|                                r|                                }d}|"t9          |d+d          pt9          |d,d          }|d-k    rO|Lt;          d.t=          d/d0                    }t9          |d1d          t9          |d2d          t9          |d3d          d4} t?          | |          st@          !                    d5           	 |"                                }!n4# t2          $ r'}"t@          !                    d6|"           d}!Y d}"~"nd}"~"ww xY w|!W|!}t9          |d+d          pt9          |d,d          }t9          |d1d          t9          |d2d          t9          |d3d          d4} |rt?          | |          st@          !                    d7           d}|K|rItG          ||t9          |d8d          pt9          |dd          pd9          rtI          ||||||:          S |d-k    r	 dd;l%m&}# tO          tQ          t          d<d=                    >          }$d- |#|p|                    d?          pd          |$                    dd          
                    d          |$                    d@d          |$                    dAdB          |$                    dC          |dDS # t          $ r$ |dEk    r t@          )                    dF           Y nw xY w|dGk    r	 tU                      }$dGdH|$                    dd          
                    d          |$                    d@d          |$                    dAdI          |$                    dJ          |dKS # t          $ r$ |dEk    r t@          )                    dL           Y nw xY w|dMk    r	 tW                      }$dMdH|$                    d          pd
                    d          ptX          |$                    d@d          |$                    dAdI          |$                    dJ          |dKS # t          $ r$ |dEk    r t@          )                    dN           Y nw xY w|dOk    r	 t[                      }$dOd	|$                    dd          
                    d          |$                    d@d          |$                    dAdP          |$                    dQ          |dRS # t          $ r$ |dEk    r t@          )                    dS           Y nw xY w|dTk    rZt]          j        |          }%|%rD|%j/        dUk    r9ddVl0m1}&  |&            }$|d|$d         |$d@         |$                    dAdW          |dS |dXk    rte          |          }$dXd	|$                    dd          
                    d          |$                    d@d          |$                    dYd          tg          |$                    dZ          pg           |$                    dAd[          |d\S |dk    rt#          |                    d          pd                                                                          }d?|dk    rM|                    d          pd                                
                    d          ?ti          ?          sd??pd]}d|                                v p?od?                                v }'|'rd}d^D ]`}(t#          |                    |(          pd                                          })|)r&t          |)d                                          }|r na|s6t#          |                    d@          pd                                          }|sDt          dd                                          p!t          dd                                          }|st          d_          n!dd`l5m6}*  |*            }|st          da          dd||db|dS |dck    rdddl7m8}+m9},m:}-m;}. |dev }/|/s |+            st          dfdgh           |                                dci           }0|0                    di          pd                                p	 |-            }1 |,            pdj}2|0                    dki           }3d}4|3                    dl          rf|3                    dm          rQ|3dl         |3dm         dn}4|3                    do          r|3do         |4dp<   |3                    dq          r|3dq         |4dq<   t#          |p|                    d?          pd                                          }5t/          tx          j=                            drd                                                    }6 |.|5          r|6sdcdds|1 dtdu|2|1d)|dv}ndcdwds|1 dtdu|2|1|dx}|4r|4|dy<   |S t]          j        |          }%|%r|%j/        d@k    rt}          |          }$t          |$                    d@                    s<dz@                    |%jA                  }7|7rd{|7 d|nd}8t          d}| d~|8 |d          t#          |                    d          pd                                                                          }d?||k    r<|                    d          pd                                
                    d          ??p(|$                    dd          
                    d          }d	}9|dk    r't          ||$                    d@d          |          }9n|dk    rdH}9nt#          |                    d          pd                                                                          }:t          |                    d                    };|dv r+ddlDmE}< |p|                    d?d          }= |<||=          }9n<|;rt          ||:          r|;}9n't          |||p|                    d?d                    }9|dv rddlDmH}>  |>||9|          }|dk    rt          jJ        |          }||9||$                    d@d          |$                    dAdb          |dS t)          |||          }||d<   |S )a)  Resolve runtime provider credentials for agent execution.

    target_model: Optional override for model_cfg.get("default") when
    computing provider-specific api_mode (e.g. OpenCode Zen/Go where different
    models route through different API surfaces). Callers performing an
    explicit mid-session model switch should pass the new model here so
    api_mode is derived from the model they are switching TO, not the stale
    persisted default. Other callers can leave it None to preserve existing
    behavior (api_mode derived from config).
    r   )r  r    r  Nz	provider z" is disabled in config (providers.z.enabled: false)moard   zmoa://localzmoa-virtual-providerrR  r)   r   z	azure.comAZURE_ANTHROPIC_KEYANTHROPIC_API_KEYrZ   rW   zazure-explicitr   )r   rh   rG  rH  r   )vertexzgoogle-vertexz	vertex-aiz
gcp-vertexvertexai)get_vertex_configa  Vertex AI credentials could not be resolved. Vertex uses OAuth2 (not a static API key): provide a service-account JSON via GOOGLE_APPLICATION_CREDENTIALS (or VERTEX_CREDENTIALS_PATH) in ~/.hermes/.env, or run 'gcloud auth application-default login' for ADC. Set the GCP project/region under vertex: in config.yaml if they aren't embedded in the credentials. Run `hermes setup` to install Vertex support.r  zvertex-oauth)r   rG  rH  r   r`   rT   )r   r)   )rM   zanthropic.comrJ  c              3  8   K   | ]}t          |          V  d S ru   )r&   )rx   r=   rH   s     r1   rz   z+resolve_runtime_provider.<locals>.<genexpr>
  sA         &lD99     r3   rF  )r`   r   rh   rG  rH  r   r   OPENAI_BASE_URLr$   >   r   rK   T>   r   r   r   r   r   r  r  r  r  r  rz  )r  r  rz  zINous pool entry agent_key expired/missing, refreshing selected pool entryz"Nous pool entry refresh failed: %szRNous pool entry agent_key still unavailable, falling through to runtime resolutionr   )rT   )r`   r   r   rh   r   r   r   r  r  r  r,   r   r   portalr  r  r   zUAuto-detected Nous provider but credentials failed; falling through to next provider.r   rX   zhermes-auth-storer  r  zVAuto-detected Codex provider but credentials failed; falling through to next provider.r   zZAuto-detected xAI OAuth provider but credentials failed; falling through to next provider.r   zqwen-cliexpires_at_ms)r`   rk   rT   r   r   r  r   z@Qwen OAuth credentials failed; falling through to next provider.r   oauth_minimax))resolve_minimax_oauth_runtime_credentialsoauthzcopilot-acpcommandargsprocess)r`   rk   rT   r   r  r  r   r   r   )r  api_key_envzNo Azure Anthropic API key found. Set AZURE_ANTHROPIC_KEY or ANTHROPIC_API_KEY, or point key_env/api_key_env in your config.yaml model section at a custom env var.r  r  envbedrock)has_aws_credentialsresolve_aws_auth_env_varresolve_bedrock_regionis_anthropic_bedrock_model>   aws-bedrockamazon-bedrockawsamazonr  zNo AWS credentials found for Bedrock. Configure one of:
  - AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
  - AWS_PROFILE (for SSO / named profiles)
  - IAM instance role (EC2, ECS, Lambda)
Or run 'aws configure' to set up credentials.no_aws_credentials)coderegionzaws-sdk-default-chain	guardrailguardrail_identifierguardrail_version)guardrailIdentifierguardrailVersionstream_processing_modestreamProcessingModetraceAWS_BEARER_TOKEN_BEDROCKzhttps://bedrock-runtime.z.amazonaws.comzaws-sdk)r`   rk   rT   r   r   r  bedrock_anthropicr   r   )r`   rk   rT   r   r   r  r   guardrail_configz, z Set r@   z*No usable credentials found for provider 'z'.missing_api_key)r`   r  r   r   r   rk   r   r   r   r   )Kr  r!  r  r    r   r   rm   
ValueErrorr8   r2   rE   r  r   agent.vertex_adapterr  r   r^  r+   r9   r   rp  r   r  r>   r   rO   r  r  r   r  r(   r   rn   r   try_refresh_currentr   r   re   r   r   r  ro   r   r   r   r   r   r  rN   r  r   r6  r   r  r  agent.bedrock_adapterr  r  r  r  r=  r>  r   r   r   api_key_env_varsr   rl   r   r   r   rg   r   r   r   )@r   rG  rH  r   r   r  r    	_full_cfg
_provs_cfg_block	_eff_base
_azure_keyazure_runtimer  tokenrT   custom_runtimerh   rI   _known_cloud_hostsr   r`   explicit_runtimeshould_use_poolenv_openai_base_urlrg  has_custom_endpointhas_runtime_overrider   r   r
  min_ttl
nous_state	refreshedr   r   r  r   r  _is_azure_endpointhint_keyenv_varr  r  r  r  r  is_explicit_bedrock_cfgr  auth_source_grr  _current_model_has_bearer_token	env_nameshintrk   r   rp   r   
_effectiver   rH   s@                                                                  @r1   resolve_runtime_providerr  r  s"   " 4I>> CBBBBBBBI/9)T/J/JT{+++PTJ*d##  233fd## 	,?,?,G,G 	C. C C0C C C  
 U""*%-,"4
 
 	
 #(b//11I[(([I-E-E#**,, 8,b1177998*B//5577 	 $,!((--!&"4
 
 	
 _,,61'))-/%
 
 
  ___::::::++--x 		H 		@   !* ,,$"4
 
 	
 3-)+  N
  /A+,  %5 %''	9==44:;;AACCIIKK9==44:;;AACC 	LL88"
     .     
 6'9%5&7  
 1C,-)+  H
 "##I0-)+!     ,.O<9==44:;;AACCIIKK9==44:;;AACC%&7<<BBDD")*?"D"D"J"J"L"L" '"'&
 

  	'L,>>>"&#$4$I8IJJ"88 )'')(( 	&5?y"""4    >$$&& >0$77 65."55  v%"3"g&GNNOOG$UK>>(/7Mt(T(T 66 J
 (
G<< &hiii% $ 8 8 : :II  % % %LL!EsKKK $IIIIII% (%E'8$?? >"5."== !
 &-UK%F%F07?UW[0\0\!(!>!>" "J
 $ &+?
G+T+T &LL!uvvv#%L 0E#5t<< uj$77    4!#5#)    6	=::::::4 %g.KT&R&R S S  E #)M,*X)--	:R:R*XVXYY!IIj"55<<SAA 99Y33))Hh77#ii55&8    	= 	= 	=!V++ KK < = = = = =	= >!!	=577E*-!IIj"55<<SAA 99Y33))H.ABB %		. 9 9&8    	= 	= 	=!V++ KK < = = = = =	= ;	=9;;E'-"YYz228b@@EEcIc 99Y33))H.ABB %		. 9 9&8    	= 	= 	=!V++KK < = = = = =	= <	=466E(.!IIj"55<<SAA 99Y33))Hj99!&?!;!;&8    	= 	= 	=!V++KK < = = = = =	= ?""#'11 
	w(O;;QQQQQQ==??E$0!*- +))Hg66&8   =  =hGG%*		*b1188==yyB//yyB//6**0b11ii)44"4	
 	
 		
 ; 9==44:;;AACCIIKK;&&%MM*55;BBDDKKCPPL2<@@ "!>#> )HNN,<,<< 
@[L,>,>,@,@@ 	  %	 E6  immH55;<<BBDD #GR006688E   DIMM)44:;;AACC 1266<<>> @2B77==??   E   HGGGGG++--E V  
 $, "4
 
 	
 9	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 ),ii 	#6#6#8#8 	@
 *    #{}}((B77""8,,299;;W?U?U?W?W..00K4K{B//77)** 	9sww7J/K/K 	9'*+A'B$'(;$<    ww/00 Y;>?W;X !78www 9,/L ) \KY]]9-E-EKLLRRTT 0JB!O!O!U!U!W!WXX%%n55 	>O 	 &0MvMMM$% %)&8	 	GG &.MvMMM$% &8 G  	;*:G&'  #H--G H
7$	114X>> !9!5!566 			'":;;I+4<'9''''"DOXOOOO!&    9==44:;;AACCIIKK8##%MM*55;BBDDKKCPPLH599Z#<#<#C#CC#H#H%y  0		)R(()  HH
 (HH"%immJ&?&?&E2"F"F"L"L"N"N"T"T"V"V-immJ.G.GHHO::: FEEEEE)IY]]9b-I-I
228ZHH  %I(Tg%h%h *
 .h(T	iQS8T8T  666EEEEEE228XxPPHz!!DXNNH   yyB//ii%00"4
 
 	
 *-)+  G
 %7G !Nsv   'O; ;P
	P
%S: :
T+T&&T+	B<[ +[43[4>A9]8 8+^&%^&0B`2 2+a a *A9c$ $+dderrorrO   c                h    t          | t                    rt          |           S t          |           S ru   )r   r   r   r+   )r  s    r1   format_runtime_provider_errorr    s.    %## ( '''u::r3   )r)   )r*   r+   r,   r+   r-   r+   )r4   r+   r-   r+   )r=   r+   r-   r>   )rH   r+   rI   r+   r-   r>   )rT   r+   r-   rU   )r`   r+   rT   r+   ra   r+   r-   r+   )rh   ri   rT   r+   r-   r+   )rT   r+   r-   r+   )rT   r+   r-   r>   )r-   ri   ru   )r`   rU   r   rU   r-   r>   )rh   ri   r   r+   r   rU   r-   r+   )r   r   r-   rU   )r-   r+   )r`   r+   rk   r+   rh   r   r-   r+   )r`   r+   r   r
   r   r+   rh   r   r   r   r   rU   r-   ri   )r   rU   r-   r+   )NN)
rT   r+   r  r+   r  rU   r  rU   r-   r   )r   ri   r  ri   r-   r  )r   r+   r-   r   )r   r+   r-   r>   )ra   r+   r-   rU   )rT   rU   r;  rU   ra   rU   r-   rU   )rC  ri   r-   ri   )r   r+   rG  rU   rH  rU   r-   r   )r   r+   rG  rU   rH  rU   r-   ri   )r   r+   rh   ri   rG  rU   rH  rU   r   rU   r-   ri   )r`   r+   r   r+   rh   ri   rG  rU   rH  rU   r   rU   r-   r   )
r   rU   rG  rU   rH  rU   r   rU   r-   ri   )r  rO   r-   r+   )]__doc__
__future__r   loggingr=  r   urllib.parser   typingr   r   r   	getLogger__name__rn   
hermes_clir   r   agent.credential_poolr	   r
   r   r   r   agent.secret_scoper   r/   rN   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r!  r   r    r!   re   r"   r#   hermes_constantsr$   r%   utilsr&   r'   r(   r2   r<   rG   rS   r_   rg   rr   r   r   r   r   r   r   r   rl   r   r   r   r  r  r  r  r)  r+  r0  r:  rA  r-  rE  r^  rp  r  r  r  r  r~   r3   r1   <module>r     s   M M " " " " " "  				 				 ! ! ! ! ! ! & & & & & & & & & &		8	$	$ ' ' ' ' ' '              9 8 8 8 8 8                                   $         
 O N N N N N N N 0 0 0 0 0 0 8 8 8 8 8 8 C C C C C C C C C C
/ 
/ 
/ 
/ 
/3 3 3 3= = = =
5 5 5 5<* * * *ZO O O O O2B B B B,61 61 61 61r" " " "J   .   ,8 8 8 8 8* #'	" " " " " ":      0 0 0 0   > +/%)"&P P P P P Pf    , (,#'	    @   0 0 0 0K K K K\   3 3 3 3lC C C CP #%)	V V V V V Vr8 8 8 8, , , , '+'+	A A A A A AN '+'+	K K K K K Kd '+'+"&f f f f f f\ '+'+"&` ` ` ` ` `J  $&*'+"&\	 \	 \	 \	 \	 \	~     r3   