
    sjױ                      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+m,Z,m-Z-m.Z. dd
l/m0Z0m1Z1m2Z2 ddl3m4Z4m5Z5 ddl6m7Z7 ddl3m8Z8 ddl9m:Z:m;Z;m<Z< d`dadZ=dbdZ>dcdZ?dddZ@dedZAd`dfd"ZBdgd%ZCdhd&ZDdid'ZEdhd(ZFdjd)ZGdkdld+ZHdd,dmd/ZIh d0ZJdnd3ZKdod4ZLdpd7ZMdddd8dqd>ZNdkdrd@ZO	 	 dsdtdDZPdudGZQdudHZRdvdIZSdwdJZTdedKZUdxdLZVddddMdydOZWdodPZXdzdRZYddddSd{dVZZdddWd|dXZ[ddddSd}dYZ\ddddSd~dZZ]d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)ACTUAL_LOCAL_NOAUTH_PLACEHOLDER	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is_actual_local_base_urlnormalize_actual_base_url)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      ?/home/agent/.hermes/hermes-agent/hermes_cli/runtime_provider.py_getenvr5   6   s!     dG
$
$C/33w.    valuec                v    |                                                                                      dd          S )N -)striplowerreplacer7   s    r4   _normalize_custom_provider_namer?   C   s,    ;;==  ((c222r6   hostboolc                \    | pd                                                     d          }|dv S )Nr,   .>   ::10.0.0.0	localhost	127.0.0.1)r<   rstrip)r@   hs     r4   _loopback_hostnamerJ   G   s1    	##C((A<<<r6   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)r;   r<   hermes_cli.authr   	Exceptionr)   rJ   r*   )rK   rL   cfg_provider_normbu_resolve_providers        r4   ,_config_base_url_trustworthy_for_bare_customrV   L   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 |dk    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_responseszapi.meta.aizapi.actual.incapi.anthropic.comanthropic_messagesz
/anthropicz/anthropic/v1zapi.kimi.comz/codingN)r;   r<   rH   r*   r(   r   pathendswith)rW   
normalizedhostnamer^   s       r4   _detect_api_mode_for_urlrb   j   s   * .b''))//1188==J **H:  
 x(( !   =    ###  
 &&&##J$++C00D}}\"" $dmmO&D&D $##>!!i:&=&=##4r6   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)rb   hermes_cli.providersrf   )rc   rW   rd   detectedrf   s        r4   _fallback_api_moderj      sK    " (11H 777777h%88N<NNr6   	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_moder[   zMIgnoring persisted custom api_mode=codex_responses for non-OpenAI endpoint %sz	(unknown)Nrg   )_parse_api_modegetrb   loggerinfo)rk   rW   configured_modedetected_modes       r4   _resolve_plain_custom_api_moderu      sz     &immJ&?&?@@O,X66M+++AR0R0R[#	
 	
 	
 AmA/AAr6   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     r4   	<genexpr>z(_host_derived_api_key.<locals>.<genexpr>   s*      
:
:B2::<<
:
:
:
:
:
:r6   rC   )rF   :c                    g | ]}||S  r   )r{   lbls     r4   
<listcomp>z)_host_derived_api_key.<locals>.<listcomp>   s    888cC8c888r6   r   )apiwww   c              3  F   K   | ]}|                                 r|nd V  dS )_N)isalnumrz   s     r4   r}   z(_host_derived_api_key.<locals>.<genexpr>   s3      EEbjjll3EEEEEEr6   )OPENAI
OPENROUTEROLLAMA_API_KEY)
r*   anysplitpoplenjoinupperisalphar5   r;   )rW   ra   labelsvendor	sanitizedenv_names         r4   _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0r6   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,   Fr\   z.anthropic.comz.claude.comTz
.azure.comr]   )r;   r*   r<   r_   rb   )rW   	candidatera   s      r4   _anthropic_base_url_override_okr     s     R&&((I u!),,299;;H u &&&(*;*;<L*M*M&QYQbQbcpQqQq&t&& t  	**.BBBt5r6   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   NrZ   z/v1z/models)r      )timeoutdata   idz$Auto-detect model from %s failed: %s)
requestsrH   r_   rp   okjsonr   rR   rq   debug)rW   r   urlrespmodelsmodel_idexcs          r4   _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                    t                      } |                     d          }t          |t                    rIt          |          }|                    d          s |                    d          r|d         |d<   |                    d          }t          |t                    r]ddlm}  ||          \  }}|p#t          |                    d          pd          }||d<   |r|                    d          s||d<   |}t          |pd                                          }|                    d          pd                                }t          |          dv }	| }
|	r|
r|rt          |          }|r||d<   |S t          |t                    r*|                                rd|                                iS i S )	Nrd   r/   r   )split_model_config_defaultrc   r,   rW   )rF   rG   )
r#   rp   
isinstancedicthermes_cli.configr   r.   r;   r*   r   )configrk   cfg_defaultr   	cfg_modelrL   r/   rW   is_localis_fallbackri   s               r4   _get_model_configr   C  s   ]]F

7##I)T"" 9oowwy!! 	*cggg&6&6 	* \C	N779%%h%% 	!DDDDDD&@&@&J&J#I|'O3y}}Z/H/H/NB+O+OL&C	N /CGGJ$7$7 /".J Hx~2&&--//GGJ''-24466$X..2LL!k 	* 	* 	*/99H *!)I
)S!! .ioo&7&7 .9??,,--Ir6   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,   TrN   custom:)r;   r<   
startswith)rc   r   normalized_providernormalized_configureds       r4   $_provider_supports_explicit_api_moder   c  s     $>r002288::06B==??EEGG  th&&$0_4I4T4TU^4_4__ $777r6   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)
Nrc   r,   rn   copilotr/   rg   r   )copilot_model_api_mode)r   )	r.   rp   r;   r<   ro   r   hermes_cli.modelsr   rR   )rk   r   r   r   rs   
model_namer   s          r4   _copilot_runtime_api_moder   t  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>   r[   bedrock_converserg   codex_app_serverr]   rawr   c                    t          | t                    r.ddlm}  ||                                           }|t
          v r|S dS )un  Validate an api_mode value from config. Returns None if invalid.

    Legacy/alias spellings (``openai``, ``anthropic``, ``responses``, …) are
    canonicalized via the shared alias map before validation, so configs
    written against older releases keep selecting the transport they named
    instead of silently falling through to hostname-based detection.
    r   )_canonical_api_modeN)r   r.   r   r   r<   _VALID_API_MODES)r   r   r`   s      r4   ro   ro     s[     #s 999999((--3355
)))4r6   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   r6   r4   !_nous_inference_base_url_overrider     s     ())/R/r6   rn   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.   rp   r;   r<   )rc   rn   rk   runtimes       r4   %_maybe_apply_codex_app_server_runtimer     sp       111)-- 0117R88>>@@FFHHG$$$!!Or6   )rk   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        }ng| 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|          }nPt          |                    d          pd          
                                                                }t          j        |           }
|
o0|                    d          |
j                            d          k    }|| k    rO|rMt          |                    d          pd          
                                                    d          }|r|}t%          |                    d                    }ddlm}  ||           ddlm}  || |          }	n&|rt5          | |          r|}	nt7          | ||          }	ddlm}  ||           ddlm}  || |	|          }t;          | |	|           }	| d!k    rt=          j        |          }| |	||t          |d"d#          ||d$S )%Nr/   r,   runtime_base_urlrW   rZ   runtime_api_keyaccess_tokenrg   r   r[   	xai-oauth
qwen-oauthminimax-oauthr]   	anthropicrc   https://api.anthropic.com
openrouterxainousr   nous_api_moder   r   azure-foundryrn   azure_foundry_model_api_mode/v1/?$opencode_provider_familyopencode_model_api_modenormalize_opencode_base_url)rc   rn   rk   lmstudiosourcer   )rc   rn   rW   r   r   credential_poolr   ) r   rp   getattrrH   r   r   r   r   inference_base_urlr.   r;   r<   r   r'   rh   r   r   r   ro   r   r   rR   resubr   r   r   rj   r   r   auth_mod$_normalize_lmstudio_runtime_base_url)rc   r   r   rk   r   r   effective_modelrW   r   rn   pconfigrL   rK   r   rs   r   inferredr   pool_url_is_defaultr   r   r   s                         r4    _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>>>>>>##H--9 BAAAAA..xIIHH 	O!EhPc!d!d 	O&HH
 *(HoNNH ;:::::))5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.rc   HERMES_INFERENCE_PROVIDERr,   auto)r;   r<   r   rp   r   r.   r5   )r  rk   rL   env_providers       r4   resolve_requested_providerr  j  s     )Y__&& )  &&(((!##I==,,L,$$ ,););)=)= ,!!##))+++ 6;;AACCIIKKL 6r6   provider_labelapi_mode_overrideprovider_namec                   t          | |          }|sdS 	 t          |          }|                                sdS |                                }|dS t	          |dd          pt	          |dd          }|sdS t          |          st          t          |                     rd}||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,   no-key-requiredrg   zpool:)rc   rn   rW   r   r   r   )
r   r   has_credentialsselectr   r   rJ   r*   rb   rR   )rW   r	  r
  r  pool_keyr   r   pool_api_keys           r4   _try_resolve_from_custom_poolr  }  s)    ,HMRRRH t""##%% 	4=4u&7>>d'%Q_acBdBd 	4 .. 	-3EFWX`FaFa3b3b 	- -L&)e-Eh-O-OeSe #(h((#
 
 	
    tts#   #C C $C :A	C 
CC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)rp   r   int)r   r  _k_vs       r4   _lift_max_output_tokensr    sY     2  YYr]]b# 	266*,F&'FF	 r6   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$   rp   )r   r  r  s      r4   _lift_extra_headersr    s?     ,EIIo,F,FGGM 0"/0 0r6   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                    rddlm} |                                D ]x\  }}t          |t                    s ||          s(t          |                    d          p|                    d	          pd                                          }|r"t          |d                                          nd}	|	s7t          |                    d
d          pd                                          }	|                    dd          }
|t!          t          |
p|          t          |                    v ra|                    d          p+|                    d          p|                    d          pd}|r|                    d|          |                                |	|                    dd          d}|                    d          }t          |t                    rt          |          |d<   t#          ||           t          |                    dd          pd                                          }|r||d<   t%          |                    d          p|                    d                    }|r||d<   t'          ||           |c S z|                    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  rN   r   	providersr   )is_provider_enabledkey_envapi_key_envr   r-   r   r   rW   default_model)r-   rW   r   rd   
extra_bodykey_cmdrn   	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-   rW   r   rd   )r?   r   r   r   r;   r<   r   r#   rp   r   r   r   r"  itemsr.   r5   r%   r  ro   r  rq   warningr"   )r   requested_norm	canonicalr   r!  r"  ep_namer   r#  resolved_api_keydisplay_namerW   r  r&  r'  rn   r)  r-   r*  r   s                       r4   _get_named_custom_providerr2    s   45G5M2NNN t" t!!.*C*CI*N*N!	 1.AAI R&&((..00NBBt C  	 	 	D	 ]]F 

;''I)T"" 8"999999'oo// 6	" 6	"NGUeT** 
 '&u-- 		)$$F		-(@(@FB egg  @G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
 "%))Ir":":"@bAAGGIIG 4,3y)  /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)r2  rR   )r   s    r4   has_named_custom_providerr4  Z  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   rW   r,   r-   r*  )_normalize_base_url_for_matchr#   rR   rp   r   r   r+  r&   r.   r"   r;   )	rW   targetr   r!  r/  r   	entry_urlr)  r-   s	            r4   find_custom_provider_identityr9  h  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   rl   r0   rA   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)rd   r%  Tr   c              3     K   | ];}t          |                                                                          k    V  <d S rx   )r.   r;   r<   )r{   midr7  s     r4   r}   zVfind_custom_provider_identity_by_model.<locals>._entry_serves_model.<locals>.<genexpr>  sU        7:C  &&((F2     r6   r   r-   F)	rp   r   r.   r;   r<   r   r   keyslist)r   keyr7   r   itemr=  r7  s         r4   _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ur6   r!  r-   r*  )r   rl   r0   rA   )r.   r;   r<   r#   rR   rp   r   r   r+  r&   r"   )	rd   r   rB  r!  r/  r   r)  r-   r7  s	           @r4   &find_custom_provider_identity_by_modelrC    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)rW   config_providerrd   rD  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,   rc   r  >   r  rN   r   NrW   r   )r9  rC  r.   r;   r   rp   rR   osenvironr?   r2  r   )rW   rD  rd   identityr   candidate_normr   s          r4   canonical_custom_identityrJ    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,   rZ   )r.   r;   rH   r<   r>   s    r4   r6  r6  =  s:    u{!!##**3//55777r6   custom_providerc                    |                      d          }t          |t                    r|si S dt          |          iS )Nr&  )rp   r   r   )rL  r&  s     r4   "_custom_provider_request_overridesrN  A  sF     $$\22Jj$'' z 	$z**++r6   )explicit_api_keyexplicit_base_urlr   rO  rP  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          }t%          |                    dd          pd                                           }|r\t'          |pd                                           s9ddlm}  ||t%          |                    d|           p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<   dd lm}  ||           }|U	 dd!lm}  ||                                          d"k    rd#|                                v rd$nd%}n# t          $ r d }Y nw xY w||                    d          sdd&lm}m} t%          |p7|                    d          p"t9                                          d'          pd                                           }|r |||          |d<    |||d         |d                   |d<   |S )(Nr,   rN   r   rO   rZ   zdirect-aliasr   
openai.comopenai.azure.comrP   OPENAI_API_KEYOPENROUTER_API_KEYc              3  8   K   | ]}t          |          |V  d S rx   r   )r{   cs     r4   r}   z0_resolve_named_custom_runtime.<locals>.<genexpr>v  s0      CC1.?.B.BCQCCCCCCr6   r  rg   rc   rn   rW   r   r   r   rW   rn   r-   r  rd   r  request_overridesr  r   r#  c              3  8   K   | ]}t          |          |V  d S rx   rW  r{   r   s     r4   r}   z0_resolve_named_custom_runtime.<locals>.<genexpr>  s1      bb)EVW`EaEabIbbbbbbr6   r'  )build_command_token_providerzcustom_provider:rc   rn   rW   r   r   r   )r*   zopencode.aiz/zen/gozopencode-gozopencode-zen)r   r   r/   )r;   r<   rQ   r   rR   rH   r  r)   r5   r   nextrb   r2  rp   r   r  rN  r   r.   r   agent.command_token_sourcer]  r   r   utilsr*   r   r   r   )r   rO  rP  r   r-  rU   rW   pool_result_da_is_openai_url_da_is_openrouterapi_key_candidatesr   rL  r   rZ  _cp_is_openai_url_cp_is_openrouterr'  r]  token_providerr  r   
_oc_familyr*   r   r   _effective_models                              r4   _resolve_named_custom_runtimerk  H  s2    ).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 /%%i44:;;AACCG %(*:*@b)G)G)I)IJJ %KKKKKK55##F,>??K8LL
 
 %$G #''
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"# ;:::::))*<==J	//////  **0022mCC%.(..2B2B%B%BMM   	 	 	JJJ	o&9&9*&E&E	
 	
 	
 	
 	
 	
 	
 	

  ""7++ ""&&y11 	
 

 %'' 	  	W!8!8EU!V!VF:88z*F:,>
 
z Ms$   A
 

AAAW WWrO  rP  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 )NrW   r,   rc   )r   r   rN   r   rO   r'   CUSTOM_BASE_URLFr  TrZ   rP   r   rU  rT  z
ollama.comrR  rS  OLLAMA_API_KEYc              3  z   K   | ]6}t          |          t          |pd                                           V  7dS )r,   N)r   r.   r;   r\  s     r4   r}   z._resolve_openrouter_runtime.<locals>.<genexpr>i  sI      jj)M^_hMiMijY_"			#	#	%	%jjjjjjr6   explicitz
env/configrn   r  r  rg   r^  )r   r   rp   r.   r;   r<   rQ   r   rR   r5   rV   r'   rH   r)   r   r_  r  ro   ru   rb   )r   rO  rP  rk   rK   rL   cfg_api_keykvr-  rU   env_openrouter_base_urlenv_custom_base_urluse_config_base_urlrW   _is_openrouter_url_is_openrouter_contextre  _is_ollama_url_is_openai_url_is_openai_azurer   r   effective_providerrb  s                            r4   _resolve_openrouter_runtimer~    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$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,   rZ   rc   rg   r   r   rW   rn   	auth_modeentrar/   r]   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_idrq  )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)r  )r   )rc   rn   rW   r   r  r  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   )rc   rn   rW   r   r  r   r   )r.   r;   rH   rp   r<   ro   r   r   r   r   rR   r5   r   r   r   agent.azure_identity_adapterr  r  r  ImportErrorr   r  )r   rk   rO  rP  r   explicit_base_url_cleanrL   rK   cfg_api_modecfg_auth_mode	cfg_entra_entrar   r   r   env_base_urlrW   r   r   r  r  r  r  r   r  entra_configrh  clean_entraconfigured_scoper  s                                 r4   _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    rd}|j        r:t-          |j        d                                                              d          }|}|sF| d"v r9t7          |           }|                    dd                              d          }n	|p|j        }| d#k    rt;          |          }|}	|	set7          |           }|                    dd          }	|s>|                    dd                              d          }| d#k    rt;          |          }d$}| d%k    rt=          ||	|&          }n| d'k    rd}n| d#k    rd}nt          |                    d          pd                                                                          }t?          |                    d(                    }|rtA          | |          r|}n'tC          | ||p|                    dd                    }| d#k    r|	stE          |          rtF          }	| ||                    d          |	d|dS d S ))Nr,   rZ   r   rc   rW   r   r   resolve_anthropic_tokenNo Anthropic credentials found. Set ANTHROPIC_TOKEN or ANTHROPIC_API_KEY, run 'claude setup-token', or authenticate with 'claude /login'.r]   rq  rY  r   r   last_refreshr[   rc   rn   rW   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/   rc   rn   rW   r   r   r  r   r   )r   rk   rO  rP  >   kimi-codingkimi-coding-cnactualrg   r   r   r   rn   )$r.   r;   rH   rp   r<   r   agent.anthropic_adapterr  r   r   r   rh   r   r   get_provider_auth_stater   DEFAULT_NOUS_INFERENCE_URLr   maxr+   r   floatr5   r  r   	auth_typebase_url_env_varr   r   r!   r   ro   r   rj   r    r   )rc   r   rk   rO  rP  r   rL   rK   rW   r   r  r  credsr   stater  r   env_urlrn   r   rs   s                        r4   _resolve_explicit_runtimer  9  s    +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 :
7$	11# 	Pg6;;AACCJJ3OOG$ 	A<<<<XFF 99Z44;;C@@"@g&@x0::H" 	C8BBEii	2..G C 99Z44;;C@@x''8BBH%y  0)  HH
 (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  x4LX4V4V5G !  ,, "4
 
 	
 4r6   )r  rO  rP  r   c           
     Z%  H 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    rt          |
d          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                                          HHr:|d v r6d!}t)          Hfd"|D                       st+          |||#          }||d<   |S t-          |||$          }t                      }dd%lm}m}  ||          bt%          |p+|                    d&          p|                    d'          pd                                          } |||          }|||d<   |S t5          ||||||(          }|r|S |d)k    }|d)k    rt%          |                    d          pd                                                                          }t%          |                    d          pd                                          Ht          d*d                                          }t          d+d                                          }t7          |p|p|          }Hr|d,v rd-}t7          |p|          }|d.v o| o| }	 |rt9          |          nd} n# t:          $ r d} Y nw xY w| r|                                 r|                                 }!d}"|!"tA          |!d/d          ptA          |!d0d          }"|d1k    rO|!LtC          d2tE          d3d4                    }#tA          |!d5d          tA          |!d6d          tA          |!d7d          d8}$tG          |$|#          stH          %                    d9           	 | &                                }%n4# t:          $ r'}&tH          %                    d:|&           d}%Y d}&~&nd}&~&ww xY w|%W|%}!tA          |!d/d          ptA          |!d0d          }"tA          |!d5d          tA          |!d6d          tA          |!d7d          d8}$|"rtG          |$|#          stH          %                    d;           d}"|!K|"rItO          | |tA          |!d<d          ptA          |!dd          pd=          rtQ          ||!||| |>          S |d1k    r	 dd?l)m*}' tW          tY          t          d@dA                    B          }(d1 |'|p|                    d&          pd          |(                    dd                              d          |(                    dCd          |(                    dDdE          |(                    dF          |dGS # t           $ r$ |dHk    r tH          -                    dI           Y nw xY w|dJk    r	 t]                      }(dJdK|(                    dd                              d          |(                    dCd          |(                    dDdL          |(                    dM          |dNS # t           $ r$ |dHk    r tH          -                    dO           Y nw xY w|dPk    r	 t_                      }(dPdK|(                    d          pd                    d          pt`          |(                    dCd          |(                    dDdL          |(                    dM          |dNS # t           $ r$ |dHk    r tH          -                    dQ           Y nw xY w|dRk    r	 tc                      }(dRd	|(                    dd                              d          |(                    dCd          |(                    dDdS          |(                    dT          |dUS # t           $ r$ |dHk    r tH          -                    dV           Y nw xY w|dWk    rZte          j        |          })|)rD|)j3        dXk    r9ddYl4m5}*  |*            }(|d|(d         |(dC         |(                    dDdZ          |dS |d[k    rtm          |          }(d[d	|(                    dd                              d          |(                    dCd          |(                    d\d          to          |(                    d]          pg           |(                    dDd^          |d_S |dk    rt%          |                    d          pd                                                                          }dH|dk    rM|                    d          pd                                                    d          Htq          H          sdHHpd`}t          |d          pHot          Hd          }+|+rd}daD ]`},t%          |                    |,          pd                                          }-|-r&t          |-d                                          }|r na|s6t%          |                    dC          pd                                          }|sDt          dd                                          p!t          dd                                          }|st!          db          n!ddcl9m:}.  |.            }|st!          dd          dd||de|dS |dfk    rddgl;m<}/m=}0m>}1m?}2 |dhv }3|3s |/            st!          didjk           |                                dfi           }4|4                    dl          pd                                p	 |1            }5 |0            pdm}6|4                    dni           }7d}8|7                    do          rf|7                    dp          rQ|7do         |7dp         dq}8|7                    dr          r|7dr         |8ds<   |7                    dt          r|7dt         |8dt<   t%          |p|                    d&          pd                                          }9t7          t          jA                            dud                                                    }: |2|9          r|:sdfddv|5 dwdx|6|5d-|dy}ndfdzdv|5 dwdx|6|5|d{}|8r|8|d|<   |S te          j        |          })|)r'|)j3        dCk    rt          |          }(|d}k    r)t          |(                    dC                    st%          |                    d          pd                                                                          };d}<|;|k    r<|                    d          pd                                                    d          }<t          |<p(|(                    dd                              d                    }=t          |=          r3t          |(          }(t          |(dC<   |(                    dD          pd~|(dD<   t          |(                    dC                    s<dG                    |)jH                  }>|>rd|> dnd}?t!          d| d|? |d          t%          |                    d          pd                                                                          }dH||k    r<|                    d          pd                                                    d          HHp(|(                    dd                              d          }|d}k    rt          |          }d	}@|dk    r't          ||(                    dCd          |          }@n|dk    rdK}@n|d}k    rdK}@nt%          |                    d          pd                                                                          }At          |                    d                    }Bddlm}C  |C|          +ddlmK}D |p|                    d&d          }E |D||E          }@n<|Brt          ||A          r|B}@n't          |||p|                    d&d                    }@ddlm}C  |C|          ddlmN}F  |F||@|          }|dk    rt          jP        |          }|(                    dCd          }G|d}k    r|Gst          |          rt          }G||@||G|(                    dDde          |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)moarg   zmoa://localzmoa-virtual-providerrY  r,   r   z	azure.comAZURE_ANTHROPIC_KEYANTHROPIC_API_KEYr]   rZ   zazure-explicitr   )r   rk   rO  rP  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   rO  rP  r   r   rc   rW   )r  r,   )rP   zanthropic.comrR  c              3  8   K   | ]}t          |          V  d S rx   )r)   )r{   r@   rK   s     r4   r}   z+resolve_runtime_provider.<locals>.<genexpr>  sA         &lD99     r6   )r   rO  rP  rl  )r   opencode_zen_free_runtimer/   rd   )rc   r   rk   rO  rP  r   r   OPENAI_BASE_URLr'   >   r  rN   T>   r  r   r   r   r   r  r  r  r  r  r  )r  r  r  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   )rW   )rc   r   r   rk   r   r   r   r  r  r  r   r   portalr  r  r  zUAuto-detected Nous provider but credentials failed; falling through to next provider.r   r[   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)rc   rn   rW   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)rc   rn   rW   r   r  r  r   r   r   )r#  r$  zNo 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)rc   rn   rW   r   r   r  bedrock_anthropicr   r   )rc   rn   rW   r   r   r  r   guardrail_configr  zlocal-offlinez, z Set rC   z*No usable credentials found for provider 'z'.missing_api_key)rc   r  r   r   r   rn   r   r   r   r   )Qr  r   r"  r#   r   r   rp   
ValueErrorr;   r)   r5   rH   r  r   agent.vertex_adapterr  r   rk  r.   r<   r   r~  r   r   r   r  r  rA   r   rR   r  r  r   r  r+   r   rq   r   try_refresh_currentr   r  rh   r   r   r  rr   r   r   r   r   r   r  rQ   r  r   r?  r   r  r  agent.bedrock_adapterr  r  r  r  rF  rG  r   r   r!   r    r   r   api_key_env_varsr   ro   r   r   rj   r   r   r   )Ir  rO  rP  r   r   r"  r#   	_full_cfg
_provs_cfg_block	_eff_base
_azure_keyazure_runtimer  tokenrW   custom_runtimerk   rL   _known_cloud_hostsr   rc   _oc_family_fn_oc_free_runtime_fn	_oc_model_free_runtimeexplicit_runtimeshould_use_poolenv_openai_base_urlru  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_cfg_provider_cfg_url_effective_url	env_nameshintrn   r   rs   r   r   
_effectiver   r   rK   sI                                                                           @r4   resolve_runtime_providerr
    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[((-B9k-Z-Z(#**,, 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        }X*TIMM)44T	g8N8NTRT
 

%'' 	 ,+Hi@@$2DM./  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<@@ "!>#> 38[II 
M2<MM 	  %	 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 a
7$	114X>>
 x(9%))I:N:N(O(O	j 9 9 ?R@@FFHHNNPPMH((%MM*55;BBDDKKCPP6AEIIj"55<<SAA N (77 IU#Bi "'))H"5"5"Hh !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x0::H%y  0		)R(()  HH
 (HH!!(HH"%immJ&?&?&E2"F"F"L"L"N"N"T"T"V"V-immJ.G.GHHOBBBBBB''11= FEEEEE)IY]]9b-I-I
228ZHH  %I(Tg%h%h *
 .h(T	iQS8T8T  	?>>>>>##H--9EEEEEE228XxPPHz!!DXNNH))Ir**x4LX4V4V5G   ii%00"4
 
 	
 *-)+  G
 %7G !Nsv   )Q= =RR'U< <
V-V((V-B<] +]65]6 A9_: :+`('`(2Bb4 4+c"!c",A9e& &+fferrorrR   c                h    t          | t                    rt          |           S t          |           S rx   )r   r   r   r.   )r  s    r4   format_runtime_provider_errorr  t	  s.    %## ( '''u::r6   )r,   )r-   r.   r/   r.   r0   r.   )r7   r.   r0   r.   )r@   r.   r0   rA   )rK   r.   rL   r.   r0   rA   )rW   r.   r0   rX   )rc   r.   rW   r.   rd   r.   r0   r.   )rk   rl   rW   r.   r0   r.   )rW   r.   r0   r.   )rW   r.   r0   rA   )r0   rl   rx   )rc   rX   r   rX   r0   rA   )rk   rl   r   r.   r   rX   r0   r.   )r   r   r0   rX   )r0   r.   )rc   r.   rn   r.   rk   r   r0   r.   )rc   r.   r   r
   r   r.   rk   r   r   r   r   rX   r0   rl   )r  rX   r0   r.   )NN)
rW   r.   r	  r.   r
  rX   r  rX   r0   r   )r   rl   r  rl   r0   r  )r   r.   r0   r   )r   r.   r0   rA   )rd   r.   r0   rX   )rW   rX   rD  rX   rd   rX   r0   rX   )rL  rl   r0   rl   )
r   r.   rO  rX   rP  rX   r   rX   r0   r   )r   r.   rO  rX   rP  rX   r0   rl   )r   r.   rk   rl   rO  rX   rP  rX   r   rX   r0   rl   )rc   r.   r   r.   rk   rl   rO  rX   rP  rX   r   rX   r0   r   )
r  rX   rO  rX   rP  rX   r   rX   r0   rl   )r  rR   r0   r.   )`__doc__
__future__r   loggingrF  r   urllib.parser   typingr   r   r   	getLogger__name__rq   
hermes_clir   r   agent.credential_poolr	   r
   r   r   r   agent.secret_scoper   r2   rQ   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   r   r"   r#   r$   rh   r%   r&   hermes_constantsr'   r(   ra  r)   r*   r+   r5   r?   rJ   rV   rb   rj   ru   r   r   r   r   r   r   r   ro   r   r   r  r  r  r  r  r2  r4  r9  rC  rJ  r6  rN  rk  r~  r  r  r
  r  r   r6   r4   <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<1 1 1 1h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   > +/%)"&R R R R R Rj    , (,#'	) ) ) ) )X   0 0 0 0T T T Tn   3 3 3 3lC C C CP #%)	V V V V V Vr8 8 8 8, , , , '+'+"&w w w w w wz '+'+	K K K K K Kd '+'+"&f f f f f f\ '+'+"&j j j j j j^  $&*'+"&K
 K
 K
 K
 K
 K
\     r6   