
    `gj                      d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
mZmZmZ ddlmZmZ ddlmZ ddlmZmZmZmZmZmZ ddlmZ dd	lmZ dd
lmZ ddl 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/m0Z0m1Z1m2Z2m3Z3  ejh                  e5      Z6d@dZ7dZ8dZ9dZ: e;h d      Z<dZ=dZ>dZ?dZ@e@ dZAdZBdZCdZDdZEeBeCeDeEhZFdZGdZHdZIdZJdZK e;h d      ZLdAd ZMe G d! d"             ZNdBd#ZOdCd$ZPdDd%ZQdEd&ZRdFd'ZSdGd(ZTdHd)ZUdId*ZVdJd+ZWdKdLd,ZXdKdMd-ZYdNd.ZZdOd/Z[dPd0Z\dd1	 	 	 	 	 	 	 dQd2Z]d3Z^	 	 	 	 	 	 dRd4Z_ G d5 d6      Z`dSd7ZadTd8ZbdUd9ZcdUd:Zdd;d<	 	 	 	 	 	 	 dVd=ZedWd>ZfdXd?Zgy)Yz<Persistent multi-credential pool for same-provider failover.    )annotationsN)	dataclassfieldsreplace)datetimetimezone)Path)AnyDictListOptionalSetTuple)OPENROUTER_BASE_URL)load_env)
get_secret)is_borrowed_credential_source$sanitize_borrowed_credential_payload)'CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDSPROVIDER_REGISTRY_auth_store_lock_codex_access_token_is_expiring_decode_jwt_claims_load_auth_store_load_provider_state_resolve_kimi_base_url_resolve_zai_base_url_save_auth_store_save_provider_state_store_provider_stateread_credential_poolwrite_credential_poolc                 <    	 ddl m}   |        S # t        $ r Y yw xY w)u  Load config.yaml read-only, returning None on any error.

    Uses ``load_config_readonly()``: every consumer in this module only reads
    (``get_pool_strategy``, ``_iter_custom_providers``, the model-config seed),
    and the deepcopy that ``load_config()`` pays per call is what made
    credential-pool checks the dominant cost of ``model.options`` — the picker
    calls ``load_pool()`` once per provider row, each of which loaded (and
    deep-copied) the full config again.
    r   load_config_readonlyN)hermes_cli.configr%   	Exceptionr$   s    H/root/.hermes/venv/lib/python3.12/site-packages/agent/credential_pool.py_load_config_safer)   -   s%    :#%% s    	ok	exhausteddead>   invalid_grantinvalid_tokentoken_revokedtoken_invalidatedunauthorized_clientrefresh_token_reusediQ oauthapi_keymanualz:device_code
fill_firstround_robinrandom
least_usedi,    g      N@zcustom:>   tlsscope	client_id
expires_in
token_typeobtained_atagent_key_idsecret_sourceportal_base_urlagent_key_reusedsecret_fingerprintagent_key_expires_inagent_key_obtained_atc                    | dk(  r't        |t              r|j                  d      rt        S t        |xs t              S )zHInfer pool auth metadata for token formats with one unambiguous meaning.	anthropicz
sk-ant-oat)
isinstancestr
startswithAUTH_TYPE_OAUTHAUTH_TYPE_API_KEY)providertoken	auth_types      r(   _normalize_pool_auth_typerR      s;     	Kuc"\*y--..    c                     e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   ded	<   d
Zded<   d
Zded<   d
Zded<   d
Zded<   d
Zded<   d
Z	ded<   d
Z
ded<   d
Zded<   d
Zded<   d
Zded<   d
Zded<   d
Zded<   d
Zded<   d
Zded<   dZded<   d
Zded<   d  Zd&d!Zed'd"       Zd(d#Zed)d$       Zed*d%       Zy
)+PooledCredentialrK   rO   idlabelrQ   intprioritysourceaccess_tokenNOptional[str]refresh_tokenlast_statusOptional[float]last_status_atOptional[int]last_error_codelast_error_reasonlast_error_messagelast_error_reset_atbase_url
expires_atexpires_at_mslast_refreshinference_base_url	agent_keyagent_key_expires_atr   request_countDict[str, Any]extrac                    | j                   i | _         t        | j                  | j                  | j                        | _        y N)ro   rR   rO   r[   rQ   selfs    r(   __post_init__zPooledCredential.__post_init__   s6    ::DJ2MMNN
rS   c                    |t         v r| j                  j                  |      S t        dt	        |       j
                   d|      )N'z' object has no attribute )_EXTRA_KEYSro   getAttributeErrortype__name__)rs   names     r(   __getattr__zPooledCredential.__getattr__   sB    ;::>>$''qd!4!4 55OPTxXYYrS   c                   t        |       D ch c]  }|j                  dk7  s|j                    }}|D ci c]  }||v s||j                  |       }}d|v r$t        |d   t              rt        |d         |d<   t        D ci c]  }||v s||   |||    }}||d<   |j                  dt        j                         j                  d d        |j                  d|j                  d|             |j                  dt               |j                  d	d
       |j                  dt               |j                  dd        | dd|i|S c c}w c c}w c c}w )NrO   r`   ro   rV      rW   rZ   rQ   rY   r   r[     )r   r|   rx   rJ   rK   _parse_absolute_timestamprw   
setdefaultuuiduuid4hexrN   SOURCE_MANUAL)clsrO   payloadffield_nameskdataro   s           r(   	from_dictzPooledCredential.from_dict   sD   '-c{K!aff
6JqvvKK+6Ga!w,7;;q>!GGt#
48H3I3(O%>tDT?U%VD!"(3_1qG|PQ
H^GAJ__Wdjjl..r23Xx!@A%67
A&-0+-H--- LG `s'   EE	EE	EEEc                8   h d}i }t        |       D ]G  }|j                  dv rt        | |j                        }||j                  |v s9|||j                  <   I | j                  j	                         D ]  \  }}|	|||<    t        || j                        S )N>   r^   r`   rb   rc   rd   re   >   ro   rO   )r   r|   getattrro   itemsr   rO   )rs   _ALWAYS_EMITresult	field_defvaluer   vs          r(   to_dictzPooledCredential.to_dict   s    
 "$ 	/I~~!66D)..1E INNl$B).y~~&	/ JJ$$& 	DAq}q		 4FDMMJJrS   c           	     r   | j                   dk(  r| j                  | j                  f| j                  | j                  ffD ]\  \  }}t        |t              s|j                         s(t        j                  |t        | dd       |      sL|j                         c S  yt        | j                  xs d      S )Nnousr<   )r<   rg   r   )rO   rk   rl   r[   rg   rJ   rK   stripauth_mod_nous_invoke_jwt_is_usabler   )rs   rP   rg   s      r(   runtime_api_keyz PooledCredential.runtime_api_key   s    ==F" !:!:;""DOO4& )!z
 uc* ;;%dGT:#- !;;=() 4$$*++rS   c                l    | j                   dk(  r| j                  xs | j                  S | j                  S )Nr   )rO   rj   rf   rr   s    r(   runtime_base_urlz!PooledCredential.runtime_base_url  s-    ==F"**;dmm;}}rS   )r|   rK   )rO   rK   r   rn   returnz'PooledCredential')r   rn   )r   rK   )r   r\   )r{   
__module____qualname____annotations__r]   r^   r`   rb   rc   rd   re   rf   rg   rh   ri   rj   rk   rl   rm   ro   rt   r}   classmethodr   r   propertyr   r   r   rS   r(   rU   rU      s   MGJNMK#'M='!%K%&*NO*%)O])'+}+(,,+//"Hm" $J$#'M='"&L-&(,,#I}#*.-.M3 E> 
Z
 . . K* , ,*  rS   rU   c                    t        |       }dD ]F  }|j                  |      }t        |t              s%|j	                         s6|j	                         c S  |S )N)emailpreferred_usernameupn)r   rx   rJ   rK   r   )rP   fallbackclaimskeyr   s        r(   label_from_tokenr     sM    &F5 !

3eS!ekkm;;= ! OrS   c                0    t        d | D        d      dz   S )Nc              3  4   K   | ]  }|j                     y wrq   rY   .0entrys     r(   	<genexpr>z!_next_priority.<locals>.<genexpr>  s     454s   )default   )max)entriess    r(   _next_priorityr     s    4G4bAAEErS   c                    | xs dj                         j                         }|t        k(  xs |j                  t         d      S )Nr   :)r   lowerr   rL   )rZ   
normalizeds     r(   _is_manual_sourcer     s@    ,B%%'--/J&T**?*?=/QR@S*TTrS   c                :    | dk(  rt         S | dk(  rt        S t        S )zHReturn cooldown seconds based on the HTTP status that caused exhaustion.  i  )EXHAUSTED_TTL_401_SECONDSEXHAUSTED_TTL_429_SECONDSEXHAUSTED_TTL_DEFAULT_SECONDS)
error_codes    r(   _exhausted_ttlr   !  s$    S((S((((rS   c                   | | dk(  ryt        | t        t        f      rt        |       }|dk  ry|dkD  r|dz  S |S t        | t              ra| j	                         }|sy	 t        |      }||dkD  r|dz  S |S 	 t        j                  |j                  dd            j                         S y# t
        $ r d}Y Pw xY w# t
        $ r Y yw xY w)zBest-effort parse for provider reset timestamps.

    Accepts epoch seconds, epoch milliseconds, and ISO-8601 strings.
    Returns seconds since epoch.
    Nr   r   l    J)     @@Zz+00:00)
rJ   rX   floatrK   r   
ValueErrorr   fromisoformatr   	timestamp)r   numericraws      r(   r   r   *  s     }%#u&,a<#*->#>wKGK%kkm	CjG '.1B'B7V#OO	))#++c8*DEOOQQ   	G	  		s$    B. :2B? .B<;B<?	C
Cc                4   | sy t        j                  d| t         j                        }|rCt        |j	                  d            }|j	                  d      j                         dk(  r|dz  S |S t        j                  d| t         j                        }|rt        |j	                  d            S t        j                  d| t         j                        }|r;t        |j	                  d            dz  t        |j	                  d            d	z  z   S t        j                  d
| t         j                        }|rt        |j	                  d            dz  S t        j                  d| t         j                        }|rt        |j	                  d            d	z  S y )Nz,quotaResetDelay[:\s\"]+(\d+(?:\.\d+)?)(ms|s)r      msr   z?retry\s+(?:after\s+)?(\d+(?:\.\d+)?)\s*(?:sec|secs|seconds|s\b)z'resets?\s+in\s+(\d+)\s*hr\s+(\d+)\s*minr:   <   zresets?\s+in\s+(\d+)\s*hr\bzresets?\s+in\s+(\d+)\s*min\b)research
IGNORECASEr   groupr   rX   )messagedelay_matchr   	sec_matchhr_min_matchhr_only_matchmin_only_matchs          r(   _extract_retry_delay_secondsr   H  sV   ))KWVXVcVcdKk''*+!,!2!21!5!;!;!=!Euv~P5P		\^egigtgtuIY__Q'((99GRTR_R_`L<%%a()D03|7I7I!7L3MPR3RRRII<gr}}UM=&&q)*T11YY>WN>''*+b00rS   c                <   t        | t              si S i }| j                  d      }t        |t              r#|j	                         r|j	                         |d<   | j                  d      }t        |t              r#|j	                         r|j	                         |d<   | j                  d      xs$ | j                  d      xs | j                  d      }t        |      }|4t        |t              r$t        |      }|t        j                         |z   }|||d<   |S )Nreasonr   reset_at	resets_atretry_until)rJ   dictrx   rK   r   r   r   time)error_contextr   r   r   r   parsed_reset_atretry_delay_secondss          r(   _normalize_error_contextr   _  s   mT*	!#Jx(F&#6<<>%||~
8	*G'3GMMO '
9*% 	,[)	,]+ 
 09O:gs#;:7C*"iik,??O"!0
:rS   c                    | j                   t        k7  ry t        t        | dd             }||S | j                  r"| j                  t        | j                        z   S y )Nre   )r^   STATUS_EXHAUSTEDr   r   r`   r   rb   )r   r   s     r(   _exhausted_untilr   x  s[    ,,(8Mt)TUH##nU5J5J&KKKrS   c                ^    | j                         j                         j                  dd      S )z>Normalize a custom provider name for use as a pool key suffix. -)r   r   r   )r|   s    r(   _normalize_custom_pool_namer     s$    ::<''S11rS   c              #  @  K   | 
t               } | y| j                  d      }t        |t              s	 ddlm}  ||       }|sy|D ]D  }t        |t              s|j                  d      }t        |t              s6t        |      |f F y# t        $ r Y yw xY ww)zJYield (normalized_name, entry_dict) for each valid custom_providers entry.Ncustom_providersr   )get_compatible_custom_providersr|   )
r)   rx   rJ   listr&   r   r'   r   rK   r   )configr   r   r   r|   s        r(   _iter_custom_providersr     s     ~"$~zz"45&-	I>vF ! 7%&yy $$)$/667	  		s)   1BB AB	BBBBc                j   | sy| j                         j                  d      }|r.t        |      }t               D ]  \  }}||k(  st         | c S  t               D ]T  \  }}t        |j                  d      xs d      j                         j                  d      }|sD||k(  sJt         | c S  y)a~  Look up the custom_providers list in config.yaml and return 'custom:<name>' for a matching base_url.

    When provider_name is given, prefer matching by name first (solving the case where
    multiple custom providers share the same base_url but have different API keys).
    Falls back to base_url matching when no name match is found.

    Returns None if no match is found.
    N/rf   r   )r   rstripr   r   CUSTOM_POOL_PREFIXrK   rx   )rf   provider_namenormalized_urlnormalized_name	norm_namer   	entry_urls          r(   get_custom_provider_pool_keyr     s     ^^%,,S1N
 5mD 6 8 	:IuO+,-i[99	:
 34 6	5		*-34::<CCCH	n4())556 rS   c                 B     t        d       t         fd D              S )z?Return all 'custom:*' pool keys that have entries in auth.json.Nc              3     K   | ]?  }|j                  t              r(t        j                  |      t              r	|   r| A y wrq   )rL   r   rJ   rx   r   )r   r   	pool_datas     r(   r   z-list_custom_pool_providers.<locals>.<genexpr>  s@      >>,-y}}S)40cN 	s   AA)r!   sorted)r  s   @r(   list_custom_pool_providersr    s'    $T*I    rS   c                    | j                  t              sy| t        t              d }t               D ]  \  }}||k(  s|c S  y)zWReturn the custom_providers config entry matching a pool key like 'custom:together.ai'.N)rL   r   lenr   )pool_keysuffixr   r   s       r(   _get_custom_provider_configr
    sP    12c,-./F24 	5L rS   c                
   t               }|t        S |j                  d      }t        |t              st        S t        |j                  | d      xs d      j                         j                         }|t        v r|S t        S )z8Return the configured selection strategy for a provider.credential_pool_strategiesr   )	r)   STRATEGY_FILL_FIRSTrx   rJ   r   rK   r   r   SUPPORTED_POOL_STRATEGIES)rO   r   
strategiesstrategys       r(   get_pool_strategyr    st     F~""89Jj$'"":>>(B/526<<>DDFH,,rS   )rf   c                  t        | dd      }|t        | t              r| }nyt        |xs d      j                         j	                         }t        |xs d      j                         j	                         }|r|sy||k(  ry|dk7  s|j                  t              sy	 t        |xs d      }t        |xs d      j                         j	                         |k(  S # t        $ r Y yw xY w)a  Return whether a pool belongs to the requested runtime provider.

    Named custom endpoints intentionally use two identities: the live agent is
    ``custom`` while its pool is keyed ``custom:<name>``. Accept that pair only
    when the runtime base URL resolves to the exact same custom pool key.
    Empty string identities fail closed. Legacy pool adapters without a
    ``provider`` attribute remain compatible; production pools are scoped.
    rO   NTr   Fcustom)	r   rJ   rK   r   r   rL   r   r   r'   )pool_or_providerrO   rf   raw_pool_providerpool_providerprovider_normmatched_pools          r(    credential_pool_matches_providerr    s       0*dC &, 0
 )/R0668>>@MB'--/557M% (@(@AS(T3HNC |!r"((*002mCC  s   !C 	C*)C*r   c                   	 t        j                         }|yt        j                  j                  d      r[t        j                  j                  dd      }|r9t        |      dz  dz  }	 |j                  d      |j                  d      k(  ry	 	 t        j                  | ||d	       y# t        $ r Y yw xY w# t        $ r Y yw xY w# t        $ r!}t        j                  d
| |       Y d}~yd}~ww xY w)u  Persist a rotated OAuth ``state`` into the global-root auth.json.

    Best-effort write-through for the multi-profile rotation hazard
    (#48415 / #43589): nous, openai-codex, and xai-oauth rotate the
    refresh_token on refresh, so when a profile pool refresh rotates a grant
    it resolved from the root fallback, the rotated chain must land back in
    root. Otherwise root keeps a now-revoked refresh token and every other
    profile reading the stale root grant dies with ``refresh_token_reused`` /
    ``invalid_grant`` once its access token expires.

    Only updates ``providers.<provider_id>`` in the root store; never touches
    the profile store (the caller already saved that). Swallows all errors — a
    failed write-through degrades to the pre-existing behavior (root stale), it
    must never break the profile's own successful save. Mirrors
    ``hermes_cli.auth._write_through_xai_oauth_to_global_root`` (which covers
    the non-pool xAI refresh path) for the credential-pool refresh path.
    NPYTEST_CURRENT_TESTHOMEr   z.hermesz	auth.jsonF)strict
set_activez8%s pool refresh: write-through to global root failed: %s)r   _global_auth_file_pathr'   osenvironrx   r	   resolve _persist_provider_state_to_storeloggerdebug)provider_idstateglobal_pathreal_home_env	real_rootexcs         r(   ,_write_through_provider_state_to_global_rootr-    s   (557 
 
zz~~+,

vr2]+i7+EI&&e&4	8I8IQV8I8WW X
11		
'       
F	
 	

s;   B. ,%B= C .	B:9B:=	C	C		C6C11C6c                     e Zd Zd*dZd+dZd+dZd,dZd-dZd.dZddd/d	Z		 	 	 	 	 	 d0d
Z
	 d1	 	 	 	 	 	 	 d2dZd3dZd3dZd3dZ	 	 	 	 d3dZd3dZd4dZd5dZd6dZ	 	 	 	 	 	 d5dZd7dZd-dZdddd8dZd9dZddd:dZd-dZddd	 	 	 	 	 	 	 d;d Zd1d<d!Zd=d"Zd-d#Z	 d1	 	 	 d>d$Zd-d%Z d?d&Z!d@d'Z"dAd(Z#d3d)Z$y)BCredentialPoolc                    || _         t        |d       | _        d | _        t	        |      | _        t        j                         | _        i | _	        t        | _        d | _        y )Nc                    | j                   S rq   r   r   s    r(   <lambda>z)CredentialPool.__init__.<locals>.<lambda>I  s
    %.. rS   r   )rO   r  _entries_current_idr  	_strategy	threadingLock_lock_active_leases%DEFAULT_MAX_CONCURRENT_PER_CREDENTIAL_max_concurrent_last_no_entries_log_at)rs   rO   r   s      r(   __init__zCredentialPool.__init__G  sT     w,HI*.*84^^%
.0D 9=$rS   c                ,    t        | j                        S rq   )boolr5  rr   s    r(   has_credentialszCredentialPool.has_credentialsV      DMM""rS   c                4    t        | j                               S )zCTrue if at least one entry is not currently in exhaustion cooldown.)rA  _available_entriesrr   s    r(   has_availablezCredentialPool.has_availableY  s    D++-..rS   c                ,    t        | j                        S rq   )r   r5  rr   s    r(   r   zCredentialPool.entries]  rC  rS   c                \      j                   sy t         fd j                  D        d       S )Nc              3  V   K   | ]   }|j                   j                  k(  s| " y wrq   )rV   r6  )r   r   rs   s     r(   r   z)CredentialPool.current.<locals>.<genexpr>c  s"     VuTEUEU9UUV   )))r6  nextr5  rr   s   `r(   currentzCredentialPool.current`  s'    VVX\]]rS   c                    t        | j                        D ]/  \  }}|j                  |j                  k(  s || j                  |<    y y)z4Swap an entry in-place by id, preserving sort order.N)	enumerater5  rV   )rs   oldnewidxr   s        r(   _replace_entryzCredentialPool._replace_entrye  s?    #DMM2 	JCxx366!%(c"	rS   Nremoved_idsc                   t        | j                  | j                  D cg c]  }|j                          c}|       y c c}w )NrS  )r"   rO   r5  r   )rs   rT  r   s      r(   _persistzCredentialPool._persistl  s/    MM*.--8U]]_8#	
8s   A 
c                    |dk7  ry|j                  d      }t        |t              sy|j                         j	                         t
        v S )uT  Detect upstream-permanent OAuth failures that won't recover on TTL.

        Only fires for 401 responses whose error code/reason matches a known
        terminal OAuth state (token_invalidated, token_revoked, invalid_grant,
        etc.).  Distinguishes permanent failures from transient ones like
        token_expired (refreshable) or generic 401 without a specific reason
        (could be a server-side glitch worth retrying).

        Returns False for non-401 status codes — 429 rate limits and 402
        billing failures are transient by nature and should keep TTL semantics.
        r   Fr   )rx   rJ   rK   r   r   _TERMINAL_AUTH_REASONS)rs   status_codenormalized_errorr   s       r(   _is_terminal_auth_failurez(CredentialPool._is_terminal_auth_failures  sG      #!%%h/&#&||~##%)???rS   c                @   t        |      }| j                  ||      rt        }nt        }t	        ||t        j
                         ||j                  d      |j                  d      |j                  d            }| j                  ||       | j                          |S )Nr   r   r   r^   r`   rb   rc   rd   re   )	r   r[  STATUS_DEADr   r   r   rx   rR  rV  )rs   r   rY  r   rZ  terminal_statusupdateds          r(   _mark_exhaustedzCredentialPool._mark_exhausted  s     4MB ))+7GH)O.O'99;'.228</33I> 0 4 4Z @
 	E7+rS   c                   | j                   dk7  s|j                  dk7  r|S 	 ddlm}  |       }|s|S |j	                  dd      }|j	                  dd      }|j	                  dd      }|j
                  xs d}|j                  xs d}|s|r|r||k7  s|r||k7  rt        j                  d	|j                         t        ||xs |j
                  |xs |j                  |xs |j                  d
d
d
d
d
d

      }	| j                  ||	       | j                          |	S |S # t        $ r!}
t        j                  d|
       Y d
}
~
|S d
}
~
ww xY w)a  Sync a claude_code pool entry from ~/.claude/.credentials.json if tokens differ.

        OAuth refresh tokens are single-use. When something external (e.g.
        Claude Code CLI, or another profile's pool) refreshes the token, it
        writes the new pair to ~/.claude/.credentials.json. The pool entry's
        refresh token becomes stale. This method detects that and syncs.
        rI   claude_coder   )read_claude_code_credentialsrefreshTokenr   accessToken	expiresAtzDPool entry %s: syncing tokens from credentials file (tokens changed)N)	r[   r]   rh   r^   r`   rb   rc   rd   re   z(Failed to sync from credentials file: %s)rO   rZ   agent.anthropic_adapterrd  rx   r[   r]   r%  r&  rV   r   rh   rR  rV  r'   )rs   r   rd  credsfile_refreshfile_accessfile_expiresentry_accessentry_refreshr`  r,  s              r(   +_sync_anthropic_entry_from_credentials_filez:CredentialPool._sync_anthropic_entry_from_credentials_file  sW    ==K'5<<=+HL'	JL02E 99^R8L))M26K 99[!4L !--3L!//52M|!< \]%BZHH "!,!B0B0B"."E%2E2E"."E%2E2E $#'$(&*'+(, ##E73   	JLLCSII	Js   D  C*D   	E
)EE
c           	         | j                   dk7  s|j                  dk7  r|S 	 t               5  t               }t	        |d      }ddd       t        t              s|S |j                  d      }t        |t              s|S |j                  dd      }|j                  dd      }|j                  xs d}|j                  xs d}|r||k7  s|r||k7  rt        j                  d|j                         ||xs |j                  ddddddd	}	|j                  d
      r|d
   |	d
<   t        |fi |	}
| j                  ||
       | j                          |
S |S # 1 sw Y   xY w# t         $ r!}t        j                  d|       Y d}~|S d}~ww xY w)u  Sync a Codex device_code pool entry from auth.json if tokens differ.

        When a Codex OAuth access token expires (or the ChatGPT account hits
        its 5h/weekly quota), the pool entry gets marked ``STATUS_EXHAUSTED``
        with a ``last_error_reset_at`` that can be many hours in the future.
        Meanwhile the user may run ``hermes model`` / ``hermes auth`` which
        performs a fresh device-code login and writes new tokens to
        ``auth.json`` under ``_auth_store_lock``.  Without this sync the pool
        entry stays frozen until ``last_error_reset_at`` elapses — even
        though fresh credentials are sitting on disk — and every request
        fails with "no available entries (all exhausted or empty)".

        Mirrors the Nous/Anthropic resync paths above.  Only applies to
        device_code-sourced entries; env/API-key-sourced entries have no
        auth.json shadow to sync from.
        openai-codexdevice_codeNtokensr[   r   r]   zQPool entry %s: syncing Codex tokens from auth.json (refreshed by another process)r[   r]   r^   r`   rb   rc   rd   re   ri   z-Failed to sync Codex entry from auth.json: %srO   rZ   r   r   r   rJ   r   rx   r[   r]   r%  r&  rV   r   rR  rV  r'   rs   r   
auth_storer(  rs  store_accessstore_refreshrm  rn  field_updatesr`  r,  s               r(   !_sync_codex_entry_from_auth_storez0CredentialPool._sync_codex_entry_from_auth_store  s   " ==N*ellm.KL*	O!# I-/
,ZHI eT*YYx(Ffd+!::nb9L"JJ;M !--3L!//52M,!m}&D5HH %1%2%Ie6I6I#'&*'+)-*.+/	1 99^,49.4IM.1!%9=9##E73 UI IP  	OLLH#NN	O;   
E# EE# "E#  CE# E E# #	F,FFc           	         | j                   dk7  s|j                  dk7  r|S 	 t               5  t               }t	        |d      }ddd       t        t              s|S |j                  d      }t        |t              s|S |j                  dd      }|j                  dd      }|j                  xs d}|j                  xs d}|r||k7  s|r||k7  rt        j                  d|j                         ||xs |j                  ddddddd	}	|j                  d
      r|d
   |	d
<   t        |fi |	}
| j                  ||
       | j                          |
S |S # 1 sw Y   xY w# t         $ r!}t        j                  d|       Y d}~|S d}~ww xY w)a  Sync an xAI OAuth pool entry from auth.json if tokens differ.

        xAI OAuth refresh tokens are single-use.  When another Hermes process
        (or another profile sharing the same auth.json) refreshes the token,
        it writes the new pair to ``providers["xai-oauth"]["tokens"]`` under
        ``_auth_store_lock``.  Without this resync, our in-memory pool entry
        keeps the consumed refresh_token and the next ``_refresh_entry`` call
        would replay it and get a ``refresh_token_reused``-style 4xx.

        Only applies to entries seeded from the singleton (``device_code``);
        manually added entries are independent credentials with their own
        refresh-token lifecycle.
        	xai-oauthrr  Nrs  r[   r   r]   zUPool entry %s: syncing xAI OAuth tokens from auth.json (refreshed by another process)rt  ri   z1Failed to sync xAI OAuth entry from auth.json: %sru  rv  s               r(   %_sync_xai_oauth_entry_from_auth_storez4CredentialPool._sync_xai_oauth_entry_from_auth_store  s    ==K'5<<=+HL'	S!# F-/
,ZEF eT*YYx(Ffd+!::nb9L"JJ;M --3L!//52M,!m}&D5HH %1%2%Ie6I6I#'&*'+)-*.+/	1 99^,49.4IM.1!%9=9##E73 OF FJ  	SLLLcRR	Sr|  c                   | j                   dk7  rS 	 t        fdt        | j                         D        d      }t        |t              sS t
        j                  | j                   |      }|j                  j                  k7  s|j                  j                  k7  r4t        j                  dj                         | j                  |       |S 	 S # t        $ r!}t        j                  d|       Y d}~S d}~ww xY w)a  Adopt a token pair rotated by another pool instance.

        Direct xAI integrations load a fresh ``CredentialPool`` for each
        request. Their in-memory locks therefore cannot protect xAI's
        single-use refresh token across concurrent requests or processes.
        This helper is called while the shared auth-store lock is held and
        re-reads the exact persisted row before a refresh POST is attempted.
        r~  c              3  ~   K   | ]4  }t        |t              r"|j                  d       j                  k(  r| 6 yw)rV   N)rJ   r   rx   rV   )r   r   r   s     r(   r   zGCredentialPool._sync_xai_oauth_entry_from_pool_store.<locals>.<genexpr>f  s7      !'40W[[5F%((5R s   :=NzIPool entry %s: adopting xAI OAuth tokens rotated by another pool instancez7Failed to sync xAI OAuth entry from credential pool: %s)rO   rK  r!   rJ   r   rU   r   r[   r]   r%  r&  rV   rR  r'   )rs   r   	persistedstoredr,  s    `   r(   %_sync_xai_oauth_entry_from_pool_storez4CredentialPool._sync_xai_oauth_entry_from_pool_storeW  s     ==K'L	Y#7#F
 I i.%//yIF##u'9'99''5+>+>>_HH ##E62 ?   	YLLRTWXX	Ys   9C BC 	D C<<Dc                :   | j                   dk7  sj                  dk7  rS 	 t               5  t               }t	        |d      }ddd       sS |j                  dd      }|j                  dd      }|||j                  d      |j                  d      |j                  d	      |j                  d
      d}t        fd|j                         D              }|rt        j                  dj                         ddddddd}|r||d<   |r||d<   |j                  d      r|d   |d<   |j                  d      r|d   |d<   |j                  d	      r|d	   |d	<   |j                  d
      r|d
   |d
<   t        j                        }	dD ]  }
|j                  |
      }|||	|
<    t        fd|	i|}| j                  |       | j                          |S 	 S # 1 sw Y   xY w# t         $ r!}t        j                  d|       Y d}~S d}~ww xY w)a  Sync a Nous pool entry from auth.json if tokens differ.

        Nous OAuth refresh tokens are single-use.  When another process
        (e.g. a concurrent cron) refreshes the token via
        ``resolve_nous_runtime_credentials``, it writes fresh tokens to
        auth.json under ``_auth_store_lock``.  The pool entry's tokens
        become stale.  This method detects that and adopts the newer pair,
        avoiding a "refresh token reuse" revocation on the Nous Portal.
        r   rr  Nr]   r   r[   rg   rk   rl   rj   )r[   r]   rg   rk   rl   rj   c              3  P   K   | ]  \  }}|d vxr t        |d      |k7    yw)Nr   N)r   )r   r   r   r   s      r(   r   zBCredentialPool._sync_nous_entry_from_auth_store.<locals>.<genexpr>  s8      C Z'NGE3,E,NNs   #&z0Pool entry %s: syncing Nous state from auth.jsonr]  r@   r>   rA   rF   rD   rG   ro   z,Failed to sync Nous entry from auth.json: %s)rO   rZ   r   r   r   rx   anyr   r%  r&  rV   r   ro   r   rR  rV  r'   )rs   r   rw  r(  ry  rx  comparable_updatesshould_syncrz  extra_updates	extra_keyvalr`  r,  s    `            r(    _sync_nous_entry_from_auth_storez/CredentialPool._sync_nous_entry_from_auth_store~  sV    ==F"ellm&CL9	N!# A-/
,Z@A !IIor:M 99^R8L ,!.#ii5"YY{3(-		2H(I&+ii0D&E"  "4":":"< K FHH
 $(&*'+)-*.+/1  4@M.1 5BM/299\*272EM,/99[)16{1CM+.9934<ABX<YM"899912:?@T:UM"67 $U[[ 1"; 7I  ))I.C36i07 "%N}NN##E73G L sA An  	NLLGMM	Ns;   
G0 G#G0 EG0 '8G0 #G-(G0 0	H9HHc                   |j                   dk7  ry	 t               5  t               }ddddj                  | j                        }t        |      xrD t        |j                  d      t              xr" t        |d   j                  |      t               }| j                  dk(  rt        |d      }|
	 ddd       y|j                  |d<   |j                  r|j                  |d	<   |j                  r|j                  |d
<   |j                  r|j                  |d<   |j                  r|j                  |d<   dD ]%  }|j                  j                  |      }|!|||<   ' |j                  r|j                  |d<   t!        |d|d       nt| j                  dk(  rt        |d      }t        |t              s
	 ddd       y|j                  d      }t        |t              s
	 ddd       y|j                  |d<   |j                  r|j                  |d	<   |j"                  r|j"                  |d<   t!        |d|d       n| j                  dk(  rt        |d      }t        |t              s
	 ddd       y|j                  d      }t        |t              s
	 ddd       y|j                  |d<   |j                  r|j                  |d	<   |j"                  r|j"                  |d<   t!        |d|d       n
	 ddd       yt%        |       |r|rt'        ||       ddd       y# 1 sw Y   yxY w# t(        $ r+}	t*        j-                  d| j                  |	       Y d}	~	yd}	~	ww xY w)ux  Write refreshed pool entry tokens back to auth.json providers.

        After a pool-level refresh, the pool entry has fresh tokens but
        auth.json's ``providers.<id>`` still holds the pre-refresh state.
        On the next ``load_pool()``, ``_seed_from_singletons()`` reads that
        stale state and can overwrite the fresh pool entry — potentially
        re-seeding a consumed single-use refresh token.

        Applies to any OAuth provider whose singleton lives in auth.json
        (currently Nous, OpenAI Codex, and xAI Grok OAuth).

        ``set_active=False`` on every write: a pool sync-back is a
        token-rotation side effect, not the user choosing a provider.
        Using ``_save_provider_state`` (which sets ``active_provider``)
        here would mean every Nous/Codex/xAI refresh in a multi-provider
        setup silently flips the ``active_provider`` flag — the next
        ``hermes`` invocation that defaults to the active provider
        (e.g. setup wizard, ``hermes auth status``) would land on
        whatever provider happened to refresh last, not whatever the
        user actually chose.
        rr  Nr   rq  r~  )r   rq  r~  	providersr[   r]   rg   rk   rl   r  rj   Fr  rs  ri   z3Failed to sync %s pool entry back to auth store: %s)rZ   r   r   rx   rO   rA  rJ   r   r   r[   r]   rg   rk   rl   ro   rj   r    ri   r   r-  r'   r%  r&  )
rs   r   rw  _wt_provider_idwrite_through_to_rootr(  r  r  rs  r,  s
             r(   %_sync_device_code_entry_to_auth_storez4CredentialPool._sync_device_code_entry_to_auth_store  su   4 <<=(U	d!# R-/
 #$2!,# #dmm$	  
 )-_(= )z~~k:DA "";/33ODdC% ==F*0VDE}7R R8 -2,>,>E.)**161D1Do.''.3.>.>l+-2__k*118=8R8R45&? 3	 $kkooi8?/2E),3 //6;6N6N23)*fePUV]]n40^LE%eT2eR Rf #YYx0F%fd3kR Rl .3-?-?F>***272E2E/))050B0Bn-)*neX]^]]k10[IE%eT2AR RB #YYx0F%fd3GR RH .3-?-?F>***272E2E/))050B0Bn-)*k5UZ[ YR R\ !,(_@'aR R Rf  	dLLNPTP]P]_bcc	ds   
L3 BL'5L3 >BL'AL';L3 "L'&L3 /BL'0L3 9"L'L3 $AL':L3 L'L3 'L0,L3 0L3 3	M'<!M""M'c                  |j                   t        k7  s|j                  s|r| j                  |d        y | j                  dv r| j                  dk(  r| j
                  n| j                  }t        | j                               5   ||      }| j                  dk(  r@||ur |}|s| j                  |      s|cd d d        S | j                  ||      cd d d        S |j                  |j                  k7  s|j                  |j                  k7  r|cd d d        S | j                  ||      cd d d        S | j                  ||      S # 1 sw Y   xY w)N)rq  r~  rq  )timeout_secondsforce)rQ   rM   r]   ra  rO   r{  r  r    _single_use_refresh_lock_timeout_entry_needs_refresh_refresh_entry_implr[   )rs   r   r  
sync_entrysynceds        r(   _refresh_entryzCredentialPool._refresh_entry9  sb   ??o-U5H5H$$UD1 ==99 ==N2 66?? 
 " $ E E G E $E*==N2U* &$T-F-Fu-M#(E E  33E3GE E ''5+=+==++u/B/BB!E E //e/DE E  ''U';;!E Es   2E E3EEEc                    | j                   dk(  rdnd}t        j                  |d      }t        t	        t        j
                        t	        |      dz         S )a0  Lock timeout for single-use-refresh-token providers.

        Covers the configured refresh POST timeout plus a margin so a slow
        token endpoint cannot make the flock give up before the refresh
        resolves.  Reads the provider's ``HERMES_*_REFRESH_TIMEOUT_SECONDS``
        override.
        rq  $HERMES_CODEX_REFRESH_TIMEOUT_SECONDS"HERMES_XAI_REFRESH_TIMEOUT_SECONDS   g      @)rO   r   	env_floatr   r   AUTH_LOCK_TIMEOUT_SECONDS)rs   env_varrefresh_timeout_secondss      r(   r  z/CredentialPool._single_use_refresh_lock_timeout`  s[     }}. 35 	
 #+"4"4Wb"A(445)*S0
 	
rS   c          
     Z   	 | j                   dk(  rwddlm}  ||j                  |j                  j                  d            }t        ||d   |d   |d   	      }|j                  d
k(  rV	 ddlm}  ||d   |d   |d          n:| j                   dk(  rf| j                  |      }||ur|}t        j                  |j                  |j                        }t        ||d   |d   |j                  d            }n| j                   dk(  rf| j!                  |      }||ur|}t        j"                  |j                  |j                        }t        ||d   |d   |j                  d            }nP| j                   dk(  r?| j%                  |      }||ur|}t        j&                  |       | j%                  |      }n|S t        |t,        d d d d d       }| j/                  ||       | j1                          | j]                  |       |S # t        $ r }t        j                  d|       Y d }~od }~ww xY w# t        $ 
rg}	t        j                  d| j                   |j(                  |	       | j                   dk(  rl|j                  d
k(  r\| j+                  |      }|j                  |j                  k7  rt        j                  d       	 ddlm}  ||j                  |j                  j                  d            }t        ||d   |d   |d   t,        d d       }| j/                  ||       | j1                          	 ddlm}  ||d   |d   |d          n,# t        $ r }t        j                  d|       Y d }~nd }~ww xY w|cY d }	~	S # t        $ r }
t        j                  d|
       Y d }
~
n5d }
~
ww xY w| j3                  |      st        j                  d       |cY d }	~	S | j                   dk(  r| j!                  |      }|j                  |j                  k7  rTt        j                  d       t        |t,        d d d d d       }| j/                  ||       | j1                          |cY d }	~	S t        j4                  |	      r@t        j                  d       	 t7               5  t9               }t;        |d      xs i }t=        |t>              r|j                  d      xs i }t=        |t>              rtA        |j                  d      xs d      jC                         }tA        |j                  xs d      jC                         }|r||k(  r|jE                  dd        |jE                  dd        ||d<   dtG        |	dd      tA        |	      d d!tI        jJ                  tL        jN                        jQ                         d"|d#<   tS        |d|       tU        |       d d d        n# 1 sw Y   nxY wn,# t        $ r }t        j                  d$|       Y d }~nd }~ww xY w| jV                  D cg c]  }|j                  d%k(  r|j(                   nc c}w }}| jV                  D cg c]  }|j                  d%k7  r| nc c}w c}| _+        | jX                  |j(                  k(  rd | _,        | j1                  |&       Y d }	~	y | j                   dk(  r| j                  |      }|j                  |j                  k7  rTt        j                  d'       t        |t,        d d d d d       }| j/                  ||       | j1                          |cY d }	~	S t        jZ                  |	      r@t        j                  d(       	 t7               5  t9               }t;        |d      xs i }t=        |t>              r|j                  d      xs i }t=        |t>              rtA        |j                  d      xs d      jC                         }tA        |j                  xs d      jC                         }|r||k(  r|jE                  dd        |jE                  dd        ||d<   dtG        |	dd      tA        |	      d d!tI        jJ                  tL        jN                        jQ                         d"|d#<   tS        |d|       tU        |       d d d        n# 1 sw Y   nxY wn,# t        $ r }t        j                  d)|       Y d }~nd }~ww xY w| jV                  D cg c]  }|j                  d%k(  r|j(                   nc c}w }}| jV                  D cg c]  }|j                  d%k7  r| nc c}w c}| _+        | jX                  |j(                  k(  rd | _,        | j1                  |&       Y d }	~	y | j                   dk(  r| j%                  |      }|j                  |j                  k7  ret        j                  d*       t        |t,        d d d d d       }| j/                  ||       | j1                          | j]                  |       |cY d }	~	S t        j^                  |	      r&t        j                  d+       	 t7               5  t9               }t;        |d      xsE |j`                  |jb                  |jd                  |jf                  |jh                  |jj                  d,}tA        |j                  d      xs d      jC                         }tA        |j                  xs d      jC                         }|r||k(  rHt        jl                  ||	d -       t        jn                  ||	d -       tS        |d|       tU        |       d d d        n# 1 sw Y   nxY wn,# t        $ r }t        j                  d.|       Y d }~nd }~ww xY wt        jp                  d/t        jp                   h}| jV                  D cg c]  }|j                  |v r|j(                   nc c}w }}| jV                  D cg c]  }|j                  |vr| nc c}w c}| _+        | jX                  |j(                  k(  rd | _,        | j1                  |&       Y d }	~	y | js                  |d        Y d }	~	y d }	~	ww xY w)0NrI   r   )refresh_anthropic_oauth_purehermes_pkce)use_jsonr[   r]   rh   )r[   r]   rh   rc  )_write_claude_code_credentialsz7Failed to write refreshed token to credentials file: %srq  ri   )r[   r]   ri   r~  r   )force_refreshz'Credential refresh failed for %s/%s: %sz8Retrying refresh with synced token from credentials file)r[   r]   rh   r^   r`   rb   zDFailed to write refreshed token to credentials file (retry path): %szRetry refresh also failed: %sz7Credentials file has valid token, using without refreshuD   xAI OAuth refresh failed but auth.json has newer tokens — adoptingr]  zIxAI OAuth refresh token is terminally invalid; clearing local token staters  r   codeunknowncredential_pool_refresh_failureT)rO   r  r   r   relogin_requiredatlast_auth_errorz,Failed to clear terminal xAI OAuth state: %srr  rS  uF   Codex OAuth refresh failed but auth.json has newer tokens — adoptingzKCodex OAuth refresh token is terminally invalid; clearing local token statez.Failed to clear terminal Codex OAuth state: %su?   Nous refresh failed but auth.json has newer tokens — adoptingzDNous refresh token is terminally invalid; clearing local token state)r=   rC   rj   r?   r<   r;   )r   z-Failed to clear terminal Nous OAuth state: %szmanual:):rO   rh  r  r]   rZ   endswithr   r  r'   r%  r&  r{  r   refresh_codex_oauth_purer[   rx   r  refresh_xai_oauth_purer   resolve_nous_runtime_credentialsrV   ro  	STATUS_OKrR  rV  r  $_is_terminal_xai_oauth_refresh_errorr   r   r   rJ   r   rK   r   popr   r   nowr   utc	isoformatr   r   r5  r6  &_is_terminal_codex_oauth_refresh_errorr  _is_terminal_nous_refresh_errorr=   rC   rj   r?   r<   r;   _quarantine_nous_oauth_state_quarantine_nous_pool_entriesNOUS_DEVICE_CODE_SOURCEra  )rs   r   r  r  	refreshedr`  r  wexcr  r,  	retry_excrw  r(  rs  ry  rn  	clear_excitemrT  singleton_sourcess                       r(   r  z"CredentialPool._refresh_entry_impls  s   F	}}+P8''"\\22=A	 "!*>!:"+O"<"+O"<	 <<=0fZ6%n5%o6%o6 .0
 ??F&"E$==&&''	 "!*>!:"+O"<!*~!>	 +- CCEJ&"E$;;&&''	 "!*>!:"+O"<!*~!>	 &(>>uE&"E99"' ??F~ ! "# $
 	E7+ 	227;}	 % f%^`deefb  |	LLBDMMSXS[S[]`a }}+0MII%P''5+>+>>LL![\QX$@"00%+]]%;%;M%J%	 #*")2>)B*3O*D*3O*D(1+/,0# ++FG<w^: ). 9 )/ : )/ :
  ) w"LL)oquvvw&$ Q%DiPPQ226:LL!Z[!M }}+CCEJ''5+>+>>LL^ &$-'+(,*.+/,0G ''8MMO"N @@ELLc-/ E)9);J$8[$Q$WUWE)%6).8)<)B#-fd#;47

?8S8YWY4Z4`4`4bM478K8K8Qr4R4X4X4ZM+8M]<Z(.

>4(H(.

?D(I:@h8C4;C4S7:3x6W@D2:,,x||2L2V2V2XD*.?(@ )=ZV[(\(8(D+E E E, % JI 
 -1MM#$(;;-7 # #K #
 *.%!%;;-7 % %DM ''5883+/(MMkM:
 }}.??F''5+>+>>LL` &$-'+(,*.+/,0G ''8MMO"N BB3GLLe-/ E)9);J$8^$T$ZXZE)%6).8)<)B#-fd#;47

?8S8YWY4Z4`4`4bM478K8K8Qr4R4X4X4ZM+8M]<Z(.

>4(H(.

?D(I:@h8F4;C4S7:3x6W@D2:,,x||2L2V2V2XD*.?(@ )=ZY^(_(8(D+E E E, % Li 
 -1MM#$(;;-7 # #K #
 *.%!%;;-7 % %DM ''5883+/(MMkM: }}&>>uE''5+>+>>LL!bc%$-'+(,*.+/,0G ''8MMO>>wG"N;;C@LL!gha-/ =)9);J$8V$L %-2__383H3H6;6N6N.3.>.>).',yyQE -0		/0J0Pb,Q,W,W,YM,/0C0C0Ir,J,P,P,RM#0M]4R ( E E$)$'+L!"
 !) F F$.$'+L!"
 !5Z O 0 <1= = =2 % a%TV_``a !88!("B"B!CD)%
 -1MM#$(;;*;; # #K #
 *.%!%;;.?? % %DM ''5883+/(MMkM:  -y|	s  A*H9 -H D;H9 	H6H1,H9 1H66H9 9
r*Br%A4NMN	N(N>NNNr*	N;N61r%6N;;*r%%r*+Br%3r*9+r%%
X /D;W3*	X 3W<	8X ?r% 	X)	X$r%$X))r%:"Yr%2Z
A r%Br%r*+r%	
c$D;c	c$c 	c$#r%$	d-dr%dr%"e r%e/.A r%3Br%r*+r%>
nD n	nn	nr%	o'o=r%o4r%;!pr%2q
	A r%r%%r*c                   |j                   t        k7  ry| j                  dk(  rF|j                  yt	        |j                        t	        t        j
                         dz        dz   k  S | j                  dk(  rt        |j                  t              S | j                  dk(  r=t        j                  |j                  t        j                  |j                              S | j                  dk(  ryy)NFrI     i rq  r~  r   )rQ   rM   rO   rh   rX   r   r   r[   r   r   _xai_access_token_is_expiring#_xai_proactive_refresh_skew_secondsrs   r   s     r(   r  z#CredentialPool._entry_needs_refresh  s    ??o-==K'""*u**+s499;3E/F/PPP==N*2""7  ==K'99""<<U=O=OP  ==F" rS   c                f    | j                   5  | j                         cd d d        S # 1 sw Y   y xY wrq   )r:  _select_unlockedrr   s    r(   selectzCredentialPool.select  s*    ZZ 	+((*	+ 	+ 	+   '0Fclear_expiredrefreshc          
     V   t        j                          }d}g }g }| j                  D ]  }|j                  t        k(  r|j                  s$| j
                  dk(  r@|j                  dk(  r1|j                  t        t        hv r| j                  |      }||ur|}d}| j
                  dk(  r@|j                  dk(  r1|j                  t        t        hv r| j                  |      }||ur|}d}| j
                  dk(  r@|j                  dk(  r1|j                  t        t        hv r| j                  |      }||ur|}d}| j
                  dk(  r@|j                  dk(  r1|j                  t        t        hv r| j                  |      }||ur|}d}|j                  t        k(  rt        |j                        r|j                  xs d	}	|	r}||	z
  t         kD  rq|j"                  xs |j$                  d
d }
t&        j)                  d|
|j*                  xs d||	z
  dz  | j
                         |j-                  |j$                         d}|j                  t        k(  rBt/        |      }|||k  r@|r,t1        |t2        d
d
d
d
d
      }| j5                  ||       |}d}|r*| j7                  |      r| j9                  |d      }||}|j-                  |        |r9t;        |      }| j                  D cg c]  }|j$                  |vs| c}| _        |r| j=                  |       |S c c}w )a  Return entries not currently in exhaustion cooldown.

        When *clear_expired* is True, entries whose cooldown has elapsed are
        reset to STATUS_OK and persisted.  When *refresh* is True, entries
        that need a token refresh are refreshed (skipped on failure).
        FrI   rc  Tr   rr  rq  r~  r   N   uh   credential pool: pruning DEAD manual entry %s (reason=%s, age=%.1fh) — re-add via `hermes auth add %s`r  g      @r]  r  rS  )r   r5  rQ   rN   r   rO   rZ   r^   r   r^  ro  r  r{  r  r   r`   DEAD_MANUAL_PRUNE_TTL_SECONDSrW   rV   r%  warningrc   appendr   r   r  rR  r  r  setrV  )rs   r  r  r  cleared_anyentries_to_prune	availabler   r  dead_at_labelexhausted_untilclearedr  
pruned_idses                   r(   rE  z!CredentialPool._available_entries  s-    iik&(,.	]] e	$E "33E<Q<Q ,1N)).>-LLII%P&"E"&K
 '5)).>-LL>>uE&"E"&K /5)).>-LL??F&"E"&K
 ,5)).>-LLCCEJ&"E"&K  K/ %U\\2#227aG3=3P#P!&!<!Y"!33@y 7]f4 MM )//9&*   $44"25"9".33H %$-'+(,*.+/,0G ''w7#E"&K444U; //U/C	$!U#Ke	$L -.J(,P1Z9OQPDMMM&6M7 Qs   0L&L&c                    t        j                         }| j                  }|||z
  t        k  ry|| _        t        j                  d       y)a+  Emit the empty-pool INFO line at most once per throttle window.

        Called on every selection while the pool is empty/exhausted. Without
        throttling this storms the Windows cross-process log lock and stalls the
        event loop (see NO_AVAILABLE_ENTRIES_LOG_THROTTLE_SECONDS).
        Nz>credential pool: no available entries (all exhausted or empty))r   	monotonicr>  )NO_AVAILABLE_ENTRIES_LOG_THROTTLE_SECONDSr%  info)rs   r  lasts      r(   _log_no_available_entriesz(CredentialPool._log_no_available_entriesc  sE     nn++t/X X'*$TUrS   Tr  c                  | j                  d|      }|sd | _        | j                          y d | _        | j                  t
        k(  r(t        j                  |      }|j                  | _        |S | j                  t        k(  r[t        |      dkD  rMt        |d       }t        ||j                  dz         }| j                  ||       |j                  | _        |S | j                  t        k(  rt        |      dkD  r|d   }| j                   D cg c]  }|j                  |j                  k7  s|  }}|j#                  t        |t        | j                         dz
               t%        |      D cg c]  \  }}t        ||       c}}| _        | j'                          |j                  | _        | j)                         xs |S |d   }|j                  | _        |S c c}w c c}}w )	NTr  r   c                    | j                   S rq   rm   )r  s    r(   r3  z1CredentialPool._select_unlocked.<locals>.<lambda>  s
     rS   r4  r  r   r   )rE  r6  r  r>  r7  STRATEGY_RANDOMr8   choicerV   STRATEGY_LEAST_USEDr  minr   rm   rR  STRATEGY_ROUND_ROBINr5  r  rN  rV  rL  )rs   r  r  r   r`  	candidaterotatedrQ  s           r(   r  zCredentialPool._select_unlockedq  s   ++$+P	#D**,
 (,$>>_,MM),E$xxDL>>00S^a5G	'@AEe53F3F3JKGw/$xxDN>>11c)nq6HaLE26--\Y9<<SXS[S[C[y\G\NN753t}}3E3IJKT]^eTfg.#yWY=gDMMMO$xxD<<>*U*! 88 ]gs   G$1G$7G)c                \    | j                         }||S | j                         }|r|d   S d S Nr   )rL  rE  )rs   rL  r  s      r(   peekzCredentialPool.peek  s7    ,,.N++-	(y|2d2rS   )r   api_key_hintc                  | j                   5  d rt        fd| j                  D        d       "| j                         xs | j	                         
	 d d d        y j
                  xs j                  d d }| j                  ||       t        fd| j                  D              }|j                  t        k(  r't        j                  d|||j                  xs d       nt        j                  d||       d | _        | j	                         }|r3|j
                  xs |j                  d d }t        j                  d|       |cd d d        S # 1 sw Y   y xY w)Nc              3  B   K   | ]  }|j                   k(  s|  y wrq   r   )r   r  r  s     r(   r   z;CredentialPool.mark_exhausted_and_rotate.<locals>.<genexpr>  s     S11B1Bl1RQSs   r  c              3  V   K   | ]   }|j                   j                   k(  s| " y wrq   )rV   )r   r  r   s     r(   r   z;CredentialPool.mark_exhausted_and_rotate.<locals>.<genexpr>  s      >qQTTUXX-=>rJ  ux   credential pool: marking %s DEAD (status=%s, reason=%s) — permanently failed, will NOT re-enter rotation until re-authr  z;credential pool: marking %s exhausted (status=%s), rotatingzcredential pool: rotated to %s)r:  rK  r5  rL  r  rW   rV   ra  r^   r^  r%  r  rc   r  r6  )	rs   rY  r   r  r  updated_entry
next_entry_next_labelr   s	      `    @r(   mark_exhausted_and_rotatez(CredentialPool.mark_exhausted_and_rotate  sM    ZZ %	E
 SS }A$*?*?*A}%	 %	 [[0EHHRaLF  ]C >DMM>M ((K7SK)H)H)UI QK  $D..0J(..C*--2C<kJK%	 %	 %	s   AE#C-EE#c                z     j                   5  |r> j                  j                  |d      dz    j                  |<   | _        |cddd       S  j	                  dd      }|s
	 ddd       y|D cg c]7  } j                  j                  |j
                  d       j                  k  r|9 }}|r|n|}t        | fd      } j                  j                  |j
                  d      dz    j                  |j
                  <   |j
                   _        |j
                  cddd       S c c}w # 1 sw Y   yxY w)a\  Acquire a soft lease on a credential.

        If a specific credential_id is provided, lease that entry directly.
        Otherwise prefer the least-leased available credential, using priority as
        a stable tie-breaker. When every credential is already at the soft cap,
        still return the least-leased one instead of blocking.
        r   r   NTr  c                h    j                   j                  | j                  d      | j                  fS r  )r;  rx   rV   rY   )r   rs   s    r(   r3  z.CredentialPool.acquire_lease.<locals>.<lambda>  s'    4#6#6#:#:588Q#G"X rS   r4  )r:  r;  rx   r6  rE  rV   r=  r  )rs   credential_idr  r   	below_cap
candidateschosens   `      r(   acquire_leasezCredentialPool.acquire_lease  s:    ZZ 	595H5H5L5L]\]5^ab5b##M2#0 $		 	 //dD/QI	 	 $-&&**588Q7$:N:NN I  '0YJXF .2-@-@-D-DVYYPQ-RUV-VD		*%yyD99+	 		 	s*   7D1D1.D12<D,.A4D1,D11D:c                    | j                   5  | j                  j                  |d      }|dk  r| j                  j                  |d       n|dz
  | j                  |<   ddd       y# 1 sw Y   yxY w)z/Release a previously acquired credential lease.r   r   N)r:  r;  rx   r  )rs   r  counts      r(   release_leasezCredentialPool.release_lease  sg    ZZ 	?''++M1=Ez##''t<5:QY##M2	? 	? 	?s   AA''A0c                f    | j                   5  | j                         cd d d        S # 1 sw Y   y xY wrq   )r:  _try_refresh_current_unlockedrr   s    r(   try_refresh_currentz"CredentialPool.try_refresh_current  s*    ZZ 	8557	8 	8 	8r  c                2   | j                   5  d}r t        fd| j                  D        d      }n$| j                         xs | j	                  d      }|
	 ddd       y|j
                  | _        | j                         cddd       S # 1 sw Y   yxY w)a  Force-refresh the entry that supplied ``api_key_hint``.

        Direct provider integrations may reload the pool after a request has
        already failed, so they cannot rely on ``current_id`` identifying the
        issuing credential. With no hint, select an entry without first doing
        the normal proactive refresh; the forced refresh below must consume a
        rotating refresh token exactly once.
        Nc              3  @   K   | ]  }|j                   k(  r|  y wrq   r  )r   r  r  s     r(   r   z6CredentialPool.try_refresh_matching.<locals>.<genexpr>  s'      %$44D "s   Fr  )r:  rK  r5  rL  r  rV   r6  r  )rs   r  r   s    ` r(   try_refresh_matchingz#CredentialPool.try_refresh_matching  s     ZZ 	8E)-
  N$*?*?*?*N}	8 	8  %xxD557!	8 	8 	8s   AB# BBc                x    | j                         }|y | j                  |d      }||j                  | _        |S )NTr  )rL  r  rV   r6  )rs   r   r  s      r(   r  z,CredentialPool._try_refresh_current_unlocked  sB    =''T':	 (||DrS   c                   d}g }| j                   D ]^  }|j                  s|j                  s|j                  r'|j	                  t        |d d d d d d              |dz  }N|j	                  |       ` |r|| _         | j                          |S )Nr   r]  r   )r5  r^   r`   rb   r  r   rV  )rs   r  new_entriesr   s       r(   reset_statuseszCredentialPool.reset_statuses  s    ]] 	*E  E$8$8E<Q<Q""$('+(,*.+/,0
 
""5)	*  'DMMMOrS   c                   |dk  s|t        | j                        kD  ry | j                  j                  |dz
        }t        | j                        D cg c]  \  }}t	        ||       c}}| _        t        | j                  | j                  D cg c]  }|j                          c}|j                  g       | j                  |j                  k(  rd | _	        |S c c}}w c c}w )Nr   r   rS  )
r  r5  r  rN  r   r"   rO   r   rV   r6  )rs   indexremovednew_priorityr   s        r(   remove_indexzCredentialPool.remove_index6  s    19DMM 22--##EAI. (1'?
#e EL1
 	MM*.--8U]]_8 	

 wzz)#D
 9s   C C&
c                   t        |xs d      j                         }|syt        | j                  d      D ]  \  }}|j                  |k(  s||d fc S  t        | j                  d      D cg c]B  \  }}|j
                  j                         j                         |j                         k(  r||fD }}}t        |      dk(  r|d   d   |d   d   d fS t        |      dkD  r	d d d| dfS |j                         rJt        |      }d|cxk  rt        | j                        k  rn n|| j                  |dz
     d fS d d d| d	fS d d d
| dfS c c}}w )Nr   )NNzNo credential target provided.r   )startr   zAmbiguous credential label "z-". Use the numeric index or entry id instead.zNo credential #.zNo credential matching "z".)
rK   r   rN  r5  rV   rW   r   r  isdigitrX   )rs   targetr   rQ  r   label_matchesr  s          r(   resolve_targetzCredentialPool.resolve_targetG  st   &,B%%'?#DMM; 	(JCxx3E4''	( (Q?
U{{  "((*ciik9 %L
 

 }" #A&a(8(;TAA}!!=cUBoppp;;=HEE/S//dmmEAI6<<q999T5cU"===
s   0AEc                    t        |t        | j                              }| j                  j                  |       | j	                          |S )Nr   )r   r   r5  r  rV  r  s     r(   	add_entryzCredentialPool.add_entry`  s7    t}}(EFU#rS   )rO   rK   r   List[PooledCredential])r   rA  )r   r"  )r   Optional[PooledCredential])rO  rU   rP  rU   r   None)rT  zOptional[List[str]]r   r$  )rY  ra   rZ  rn   r   rA  rq   )r   rU   rY  ra   r   Optional[Dict[str, Any]]r   rU   )r   rU   r   rU   )r   rU   r   r$  )r   rU   r  rA  r   r#  )r   r   r   rU   r   rA  )r  rA  r  rA  r   r"  )r   r$  )r  rA  r   r#  )rY  ra   r   r%  r  r\   r   r#  )r  r\   r   r\   )r  rK   r   r$  )r  r\   r   r#  )r   rX   )r  rX   r   r#  )r  r
   r   z?Tuple[Optional[int], Optional[PooledCredential], Optional[str]])%r{   r   r   r?  rB  rF  r   rL  rR  rV  r[  ra  ro  r{  r  r  r  r  r  r  r  r  r  rE  r  r  r  r  r  r	  r  r  r  r  r  r  r!  r   rS   r(   r/  r/  F  s   =#/#^
 >B 
@"@ )@ 
	@6 37	 # 0	
 
>2h>@8t%%%	%NFPqdf%<N
&Z%Z15Z	#Zx
0+ ;@QV vpV 37 $L3 37&*, #, 0	,
 $, 
$,\>?8
 -18)8	#8:0">2rS   r/  c                @   g }t        |       D ]&  \  }}|j                  |k(  s|j                  |       ( |r|d   nd }t        |dd        }|r&t        |       D cg c]  \  }}||vs| c}}| d d  ||j	                  dt        j                         j                  d d        |j	                  dt        |              |j	                  d|j                  d      xs |       | j                  t        j                  ||             y| |   }	i }
i }t        |	      D ch c]  }|j                   }}|j                         D ]e  \  }}|dv s||dk(  r|	j                  r||v rt!        |	|      |k7  s3||
|<   9|t"        v sB|	j$                  j                  |      |k7  sa|||<   g |
s|rT|ri |	j$                  ||
d	<   t'        |	fi |
}|| |<   t)        |      xs! |	j+                         |j+                         k7  S t)        |      S c c}}w c c}w )
Nr   r   rV   r   rY   rW   T>   rV   rY   ro   )rN  rZ   r  r  r   r   r   r   r   rx   rU   r   r   r|   r   rW   r   rw   ro   r   rA  r   )r   rO   rZ   r   matching_indicesrQ  r   existing_idxduplicate_indicesexistingrz  r  r   _field_namesr   r   r`  s                    r(   _upsert_entryr-  g  s-   ( )
U<<6!##C() +;#A&L,QR01.7.@a
UCO`D`ea
4!1!1"1!56:~g'>?7GKK$8$BFC'11(GDE|$HMM$*8$45qAFF5L5mmo 
+
U$$'>hnn,x%.%*c"K~~!!#&%/%*c"
+ %H%H-%HM'"(4m4 ' %&Q(*:*:*<@Q*QQ!""C b 6s   H+H%Hc                l  
 | dk7  rydddddd
t        d	 |D        d
       }t        d |D        
fd      }g ||}t        |      D ci c]  \  }}|j                  | }}}d}t        |      D ]4  \  }	}|j                  |	k7  st	        ||	      |||j                     <   d}6 |S c c}}w )NrI   Fr   r   r         )zenv:ANTHROPIC_TOKENzenv:CLAUDE_CODE_OAUTH_TOKENr  rc  zenv:ANTHROPIC_API_KEYc              3  L   K   | ]  }t        |j                        s|  y wrq   r   rZ   r   s     r(   r   z-_normalize_pool_priorities.<locals>.<genexpr>  s     G5'8'FG   $$c                    | j                   S rq   r   r2  s    r(   r3  z,_normalize_pool_priorities.<locals>.<lambda>  s
    %.. rS   r4  c              3  L   K   | ]  }t        |j                        r|  y wrq   r2  r   s     r(   r   z-_normalize_pool_priorities.<locals>.<genexpr>  s     K5+<U\\+JKr3  c                |    j                  | j                  t                    | j                  | j                  fS rq   )rx   rZ   r  rY   rW   )r   source_ranks    r(   r3  z,_normalize_pool_priorities.<locals>.<lambda>  s/    OOELL#k*:;NNKK
 rS   r   T)r  rN  rV   rY   r   )rO   r   manual_entriesseeded_entriesorderedrQ  r   	id_to_idxchangedr  r7  s             @r(   _normalize_pool_prioritiesr=    s    ;  !'(!"K GGG(N KGK
N 100G1:71CD:33DIDG(1 e>>\)+25<+PGIehh'(G N Es   B0c                f  & d}t               }t               }	 ddlm} | dk(  rI	 ddlm}  |d      s||fS 	 t               &dM&fd} |d      } |d	      xs  |d
      }	t        |xr |	       }
|
r?|D cg c]  }|j                  dvr| }}t        |      t        |      k7  r||d d  d}||fS ddl
m}m} d |       fd |       ffD ]  \  }}|s	|j                  d      s || |      r%|j                  |       |t        || ||t         |j                  dd      |j                  d      |j                  d      t#        |j                  dd      |      d      z  } ||fS | dk(  rt%        |d      }t        t'        |t(              xrZ t+        |j                  d      xs d      j-                         xs, t+        |j                  d      xs d      j-                               }|r=|s;|D cg c]  }|j                  dvr| }}t        |      t        |      k7  r||d d  d}|r|r || d      s|j                  d       t+        |j                  d      xs d      j-                         }|xs t#        |j                  dd      d      }|t        || di dddt         d|j                  dd      d|j                  d      d|j                  d      d|j                  d      d |j                  d       d!|j                  d!      d"|j                  d"      d#|j                  d#      d|j                  d      d$|j                  d$      d%|j                  d%      d&|j                  d&      d'|j                  d'      d(|j                  d(      d)|j                  d)      |j                  d*      t'        |j                  d+      t(              r|j                  d+      nd |d,      z  }||fS | d-k(  r	 dd.lm}m}  |       \  }}|r ||      \  }}d/|j5                         v rd0nd1| } || |      sU|j                  |       t7        j                  |       }|xs |r|j8                  nd}|t        || ||t:        |||d2      z  }||fS | d4k(  r	 dd5lm!}  |d6      }|j                  d7d      }|rw|j                  dd8      } || |      s\|j                  |       |t        || ||t         ||j                  d9      |j                  d:d      |j                  d;|      d<      z  }||fS | d>k(  r	 dd?lm"}  |d>      }|r |j                  d      rd@} || |      s|j                  |       d } 	 ddAl#m#}! |j                  dd      }"|"r+tI        |!jK                  |"      jM                         dBz        } t+        |j                  d#d      xs d      jO                  dC      }#|t        || ||t         |d   |j                  d      | |#|j                  dd      xs t#        |j                  dd      |      dD      z  }||fS | dFk(  r || d      r||fS t%        |dF      }t'        |t(              r|j                  dG      nd }$t'        |$t(              r|$j                  d      r|j                  d       t+        |j                  d      xs d      j-                         }|t        || ddt         |$j                  dd      |$j                  d      dH|j                  dI      |xs t#        |$j                  dd      d      dJ      z  }||fS | dKk(  rt%        |dK      }t'        |t(              r|j                  dG      nd }$t'        |$t(              r|$j                  d      rd} || |      r||fS |j                  |       ddLlm(}% |%}#|t        || ||t         |$j                  dd      |$j                  d      |#|j                  dI      t#        |$j                  dd      |      dJ      z  }||fS # t        $ r d }Y Ww xY w# t        $ r Y Mw xY wc c}w c c}w # t<        $ r#}t>        jA                  d3|       Y d }~||fS d }~ww xY w# t<        $ r#}t>        jA                  d=|       Y d }~||fS d }~ww xY w# t<        $ r d } Y w xY w# t<        $ r#}t>        jA                  dE|       Y d }~||fS d }~ww xY w)NNFr   is_source_suppressedc                     yNFr   _p_ss     r(   _is_suppressedz-_seed_from_singletons.<locals>._is_suppressed      rS   rI   )!is_provider_explicitly_configuredc                f    j                  |       xs t        | d      xs dj                         S r  )rx   _get_secretr   )r   	_env_files    r(   _env_valz'_seed_from_singletons.<locals>._env_val  s,    MM#&D+c2*>D"KKMMrS   ANTHROPIC_API_KEYANTHROPIC_TOKENCLAUDE_CODE_OAUTH_TOKEN>   rc  r  T)rd  read_hermes_oauth_credentialsr  rc  rf  r   re  rg  )rZ   rQ   r[   r]   rh   rW   r   r[   rk   >   manual:device_coderr  rr  rW   rZ   rQ   r]   rg   r?   r<   r=   rC   rj   rl   r@   r>   rA   rF   rD   rG   r;   )rG   r;   rW   copilot)resolve_copilot_tokenget_copilot_api_tokenghgh_clienv:rZ   rQ   r[   rf   rW   zCopilot token seed failed: %sz
qwen-oauth) resolve_qwen_runtime_credentials)refresh_if_expiringr4   zqwen-clirh   rf   	auth_file)rZ   rQ   r[   rh   rf   rW   z Qwen OAuth token seed failed: %szminimax-oauth)get_provider_auth_stater3   )r   r  r   )rZ   rQ   r[   r]   rh   rf   rW   z#MiniMax OAuth token seed failed: %srq  rs  z%https://chatgpt.com/backend-api/codexri   )rZ   rQ   r[   r]   rf   ri   rW   r~  )DEFAULT_XAI_OAUTH_BASE_URLr   rK   r   rK   ))r  r   hermes_cli.authr@  ImportErrorrH  r   rA  rZ   r  rh  rd  rP  rx   addr-  rM   r   r   rJ   r   rK   r   hermes_cli.copilot_authrS  rT  r   r   rj   rN   r'   r%  r&  rY  r\  r   rX   r   r   r   r]  )'rO   r   r<  active_sourcesrw  rF  rH  rL  anthropic_api_keyanthropic_oauth_envapi_key_path_explicitr   retainedrd  rP  source_nameri  r(  has_runtime_materialcustom_labelseeded_labelrS  rT  rP   rZ   	api_tokenenterprise_base_urlpconfigeffective_base_urlr,  rY  r\  rh   _dtr   rf   rs  r]  rK  s'                                         @r(   _seed_from_singletonsrq    s,
   G"uN!#JJ
 ;
	I4[A.. B* J		N %%89&'N84M+N 	 !%%6%R?R;R S  $+<<'EE H  8}G,%
N**g 9;<8:;#
 	K =1!(K8"";/="-%4(-		-(D).>)B).;)?!1%))M22NP[!\	 	~ N""S 
V	$Z8#ud# EIIn-34::< =uyy-34::<	 
 -#*<<'LL H  8}G,%
).=2Y}-
 uyy17R8>>@L' +;		."-},L }m #EIInb$A $UYY%?	
 !%))L"9 !%))L"9 UYYw/  ;!7 &uyy1B'C )%))4H*I  ;!7 +EII6L,M& "599]#;'( !%))L"9)* #EIIn$=+, +EII6L,M-. '		2D(E/0 .3YY7N-O/9%))E:JD/Q599U+W[)5	   G\ N""Y 
Y		?\13ME61Fu1M.	.*.&,,.*@hVHo%h<"&&{3/33H=G *= *6=222 ' } #&1):,5(:%+	  Gt N""W 
\	!	BH4OEIIi,E#ii*=%h<"&&{3} #&1)8,1-2YY-G(-		*b(A%*YY{K%H	  Gz N""[ 
_	$!	E?+O<E>2%%h<"&&{3$(M-<#iib9,/0A0A#0F0P0P0RUY0Y,ZM  #599-A2#F#L"MTTUXYH} #&1)8,1.,A-2YY-G-:(0%*YYw%; &?O %		." ={@
	  Gl N""G 
^	#
 (M2N**$Z@(25$(?8$T fd#

>(B}-uyy17R8>>@L}+!0$*JJ~r$B%+ZZ%@ G$)IIn$=)l-=fjjY[>\^k-l	 G` N""C 
[	  %Z=(25$(?8$Tfd#

>(B #Fh/..v&B1H}$!0$*JJ~r$B%+ZZ%@ ($)IIn$=-fjj.LfU	 G N""w	  	  		HVb  	?LL8#>>Z N""]	?<  	BLL;SAA^ N""a	B. % -(,-&  	ELL>DDJ N""M	Es   a' a: 9b
=bBb :Bc A d  Ac2 %Bd 'a76a7:	bb	c b;;c 	c/c**c/2d=d  dd 	d0d++d0c                ^   d}t               }dd}	 ddlm} ddt        d	 	 	 	 	 	 	 	 	 	 	 dfd}| d	k(  rM |d
      }|r?d} || |      r||fS |j                  |       |t        || | ||d
|t                    z  }||fS t        j                  |       }	|	r|	j                  t        k7  r||fS d}
|	j                  r! ||	j                        j                  d      }
t        |	j                        }| dk(  rg d}|D ]  } ||      }|sd| } || |      r|j                  |       |
xs |	j                  }| dk(  rt!        ||	j                  |
      }n| dk(  rt#        ||	j                  |
      }|t        || | |||||            z  } ||fS # t        $ r d }Y w xY w)NFc                   t               }|j                  | d      j                         }t        j                  j                  | d      j                         }|j                  d      r|r|S |xs t        | d      xs |S )Nr   zop://)r   rx   r   r!  r"  rL   rJ  )r   env_filer   env_vals       r(   _get_env_prefer_dotenvz._seed_from_env.<locals>._get_env_prefer_dotenv	  sk    :ll3#))+**..b)//1 >>'"wN5k#r*5g5rS   r   r?  c                     yrB  r   rC  s     r(   _is_source_suppressedz-_seed_from_env.<locals>._is_source_suppressed	  rG  rS   c                |    	 ddl m}  ||       }|rt        |      j	                         S d S # t        $ r d }Y *w xY w)Nr   )get_secret_source)hermes_cli.env_loaderrz  r'   rK   r   )r  rz  source_labels      r(   _secret_source_for_envz._seed_from_env.<locals>._secret_source_for_env!	  sG    	 ?,W5L -9s< &&(BdB  	 L	 s   - ;;)rQ   c                6    | ||||d} |      }|r||d<   |S )NrX  rB   r   )rZ   r  rP   rf   rQ   r   rB   r}  s          r(   _env_payloadz$_seed_from_env.<locals>._env_payload)	  s9     "! #
 /w7'4GO$rS   
openrouterOPENROUTER_API_KEYzenv:OPENROUTER_API_KEY)rZ   r  rP   rf   r   r   rI   )rN  rO  rM  rW  zkimi-codingzair^  )r  rK   r   r\   )rZ   rK   r  rK   rP   rK   rf   rK   rQ   rK   r   rn   )r  r_  r@  r`  rN   ra  r-  r   r   rx   rQ   base_url_env_varr   r   api_key_env_varsrj   r   r   )rO   r   r<  rc  rv  rx  r  rP   rZ   rn  env_urlenv_varsr  rf   r}  s                 @r(   _seed_from_envr    s9   G"uN6,Q
C +  	
   
( <&';<-F$Xv6..v&}!00		
 
G &&##H-Gg''+<<&&G()A)ABII#NG,,-H;
  
&w/y! 626"8g88}$-eW5O5OQXYH,UG4N4NPWXH=!		

 
	

2 N""}  	s   F F,+F,Tprune_env_sourcesc                   dfd}| D cg c]/  }t        |j                        s|j                  |v s ||      s|1 }}t        |      t        |       k(  ry|| d d  yc c}w )Nc                    | j                   j                  d      rS t        | j                   | j                        xs | j                   dk(  S )NrW  r  )rZ   rL   r   rO   )r   r  s    r(   _is_prunablez1_prune_stale_seeded_entries.<locals>._is_prunable	  sG     <<""6*$$ *%,,G -||},	
rS   FTr&  )r   rZ   r  )r   rc  r  r  r   rg  s     `   r(   _prune_stale_seeded_entriesr  ~	  sm    
$ U\\*<<>)E"	 	H  8}G$GAJs   4A c                   d}t               }	 ddlm} t	        |       }|rt        |j                  d      xs d      j                         }t        |j                  d      xs d      j                         j                  d      }t        |j                  d	      xs d      j                         }|r>d
| }	 || |	      s0|j                  |	       |t        || |	|	t        |||xs |	d      z  }	 t               }
|
r|
j                  d      nd}t        |t              rt        |j                  d      xs d      j                         j                         }t        |j                  d      xs d      j                         j                  d      }d}dD ]F  }|j                  |      }t        |t
              s%|j                         s6|j                         } n |dk(  rK|rI|rGt!        |      }|| k(  r7d}	 || |	      s,|j                  |	       |t        || |	|	t        ||dd      z  }||fS # t        $ r d }Y 6w xY w# t"        $ r Y ||fS w xY w)zJSeed a custom endpoint pool from custom_providers config and model config.Fr   r?  c                     yrB  r   rC  s     r(   rF  z)_seed_custom_pool.<locals>._is_suppressed	  rG  rS   r4   r   rf   r   r|   zconfig:rX  modelNrO   )r4   apir  model_config)r  r_  r@  r`  r
  rK   rx   r   r   ra  r-  rN   r)   rJ   r   r   r   r'   )r  r   r<  rc  rF  	cp_configr4   rf   r|   rZ   r   	model_cfgmodel_providermodel_base_urlmodel_api_keyr   r   matched_keys                     r(   _seed_custom_poolr  	  s\   G"uNJ ,H5IimmI.4"5;;=y}}Z06B7==?FFsK9==(.B/557tf%F!(F3""6*="(%6(/$,!%	  "$+1FJJw't	i& z!:!@bAGGIOOQN z!:!@bAGGIPPQTUNM' MM!$a%!'')$%GGIM	
 )n:>J(*+F)(F;&**62=#$"*0->0=,:)7	$  N""}  	v  N""s1   H; 5CI I A#I ;I
I	IIc                     xs dj                         j                          t               }|D ch c]4  }t        |t              r"|j                  d      r|j                  d      6 }}t         fd|D              }|D cg c]  }t        j                   |       }}t         fd|D              }|rGt               j                  d      }t        |t              r|j                         nd }	t        |	      } j                  t              r)t         |      \  }
}|xs |xs |
}|t        ||      z  }nOt         |      \  }}t!         |      \  }}|xs
 |xs |xs |}|t        |||z  d      z  }|t#         |      z  }|rR|D ch c]  }|j$                   }}t'         t)        |d 	      D cg c]  }|j+                          c}||z
  
       t-         |      S c c}w c c}w c c}w c c}w )Nr   rV   c              3  `   K   | ]%  }t        |t              xr t        |      |k7   ' y wrq   )rJ   r   r   r   r   rO   s     r(   r   zload_pool.<locals>.<genexpr>	  s=      !  	7D! 	O0(CwN	O!s   +.c           	   3     K   | ]]  }t        |t              xrG t        |j                  d       |j                  dt                    |j                  dt              k7   _ yw)r[   rQ   N)rJ   r   rR   rx   rN   r  s     r(   r   zload_pool.<locals>.<genexpr>	  sh      '  	7D! 	9%KK'KK%67
 [[&78	9	9's   A#A&credential_poolFr  c                    | j                   S rq   r   )r  s    r(   r3  zload_pool.<locals>.<lambda>&
  s
    4== rS   r4  rS  )r   r   r!   rJ   r   rx   r  rU   r   r   rA  rL   r   r  r  rq  r  r=  rV   r"   r  r   r/  )rO   raw_entriesr   disk_idsraw_needs_sanitizationr   r   raw_needs_auth_normalizationactive_poolactive_entriescustom_changedcustom_sourcesr<  singleton_changedr  env_changedenv_sourcesnew_idss   `                 r(   	load_poolr  	  s,   B%%'--/H&x0K !eT"uyy 			$H 
 ! ! #! 
 MXX))(G<XGX#& ' #' $  $ '(,,->?6@d6S2Y]'+N';$-.):8W)M&(Z,HZN.wGG/DXw/W,,#1(G#D [" +  	 	 	.+#
 	

 	-h@@)0158811*0>X*YZU]]_Z 7*	

 (G,,y YX 2 [s   9G/G4G9 G>
)r   Optional[dict])rO   rK   rP   r
   rQ   r
   r   rK   )rP   rK   r   rK   r   rK   )r   r"  r   rX   )rZ   rK   r   rA  )r   ra   r   rX   )r   r
   r   r_   )r   rK   r   r_   )r   r%  r   rn   )r   rU   r   r_   )r|   rK   r   rK   rq   )r   r  )rf   r\   r   r\   r   r\   )r   z	List[str])r  rK   r   r%  )rO   rK   r   rK   )r  r
   rO   r\   rf   r\   r   rA  )r'  rK   r(  rn   r   r$  )
r   r"  rO   rK   rZ   rK   r   rn   r   rA  )rO   rK   r   r"  r   rA  )rO   rK   r   r"  r   Tuple[bool, Set[str]])r   r"  rc  zSet[str]r  rA  r   rA  )r  rK   r   r"  r   r  )rO   rK   r   r/  )h__doc__
__future__r   loggingr!  r8   r8  r   r   r   dataclassesr   r   r   r   r   pathlibr	   typingr
   r   r   r   r   r   hermes_constantsr   r&   r   agent.secret_scoper   rJ  agent.credential_persistencer   r   r_  authr   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   	getLoggerr{   r%  r)   r  r   r^  	frozensetrX  r  rM   rN   r   SOURCE_MANUAL_DEVICE_CODEr  r  r  r  r  r   r   r   r  r   rw   rR   rU   r   r   r   r   r   r   r   r   r   r   r   r  r
  r  r  r<  r-  r/  r-  r=  rq  r  r  r  r  r   rS   r(   <module>r     sC   B "  	     	 2 2 '  8 8 0 & 8 #   " 
		8	$( 	  
 # $  ( !-  ,o\: " $ " 	  # #  '  -1 )
     / g g gTFU
)<.22
74<( #	#D#D#D 	#D
 
#DL )* %4
4
+4
	4
n^ ^B)*#ZDD#N
~#J #	 #   	 
 
 FF#R?-rS   