
    .cj9                      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j4        e5          Z6dfd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!          ZLdgd'ZMe G d( d)                      ZNdhd+ZOdid/ZPdjd2ZQdkd5ZRdld8ZSdmd:ZTdnd>ZUdod@ZVdpdBZWdqdrdDZXdqdsdHZYdtdJZZdudLZ[dvdMZ\ddNdwdPZ]dQZ^dxdUZ_ G dV dW          Z`dydYZadzdZZbd{d\Zcd{d]Zdd^d_d|dcZed}ddZfd~deZgdS )z<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_poolreturnOptional[dict]c                 F    	 ddl m}   |             S # t          $ r Y dS 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    7/home/ice/.hermes/hermes-agent/agent/credential_pool.py_load_config_safer+   -   sJ    ::::::##%%%   tt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_atproviderstrtokenr
   	auth_typec                    | dk    r1t          |t                    r|                    d          rt          S t          |pt                    S )zHInfer pool auth metadata for token formats with one unambiguous meaning.	anthropicz
sk-ant-oat)
isinstancerK   
startswithAUTH_TYPE_OAUTHAUTH_TYPE_API_KEY)rJ   rL   rM   s      r*   _normalize_pool_auth_typerT      sS     	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d
S )/PooledCredentialrK   rJ   idlabelrM   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                l    | j         i | _         t          | j        | j        | j                  | _        d S N)rq   rT   rJ   r]   rM   selfs    r*   __post_init__zPooledCredential.__post_init__   s6    :DJ2MN
 
rU   namec                    |t           v r| j                            |          S t          dt	          |           j         d|          )N'z' object has no attribute )_EXTRA_KEYSrq   getAttributeErrortype__name__)ru   rw   s     r*   __getattr__zPooledCredential.__getattr__   sJ    ;:>>$'''Xd!4XXPTXXYYYrU   payloadr#   'PooledCredential'c                   d t          |           D             }fd|D             }d|v r3t          |d         t                    rt          |d                   |d<   fdt          D             }||d<   |                    dt          j                    j        d d                    |                    d	                    d	|                     |                    d
t                     |                    dd           |                    d	t                     |                    dd            | dd|i|S )Nc                2    h | ]}|j         d k    |j         S )rJ   rw   .0fs     r*   	<setcomp>z-PooledCredential.from_dict.<locals>.<setcomp>   s&    KKK!af
6J6Jqv6J6J6JrU   c                D    i | ]}|v |                     |          S  )r{   r   kr   s     r*   
<dictcomp>z.PooledCredential.from_dict.<locals>.<dictcomp>   s*    GGGa!w,,7;;q>>,,,rU   rb   c                :    i | ]}|v |         ||         S rs   r   r   s     r*   r   z.PooledCredential.from_dict.<locals>.<dictcomp>   s1    ___1qG||PQ
H^GAJH^H^H^rU   rq   rX      rY   r\   rM   r[   r   r]    rJ   r   )r   rP   rK   _parse_absolute_timestamprz   
setdefaultuuiduuid4hexr{   rS   SOURCE_MANUAL)clsrJ   r   field_namesdatarq   s     `   r*   	from_dictzPooledCredential.from_dict   sD   KKvc{{KKKGGGG;GGGt##
48H3I3(O(O#%>tDT?U%V%VD!"_______Wdjll.rr2333Xx!@!@AAA%6777
A&&&-000+++s--H----rU   c                   h d}i }t          |           D ]6}|j        dv rt          | |j                  }|	|j        |v r
|||j        <   7| j                                        D ]\  }}||||<   t          || j                  S )N>   r`   rb   rd   re   rf   rg   >   rq   rJ   )r   rw   getattrrq   itemsr   rJ   )ru   _ALWAYS_EMITresult	field_defvaluer   vs          r*   to_dictzPooledCredential.to_dict   s    
 
 
 "$ 	/ 	/I~!666D).11E INl$B$B).y~&J$$&& 	 	DAq}q	3FDMJJJrU   c           	     V   | j         dk    r| j        | j        f| j        | j        ffD ]j\  }}t          |t                    rP|                                r<t          j	        |t          | dd           |          r|                                c S kdS t          | j        pd          S )Nnousr>   )r>   ri   r   )rJ   rm   rn   r]   ri   rP   rK   stripauth_mod_nous_invoke_jwt_is_usabler   )ru   rL   ri   s      r*   runtime_api_keyz PooledCredential.runtime_api_key   s    =F"" !:;"DO4& ) )!z
 uc**	)	) !;%dGT::#-  	) !;;==(((24$*+++rU   c                B    | j         dk    r| j        p| j        S | j        S )Nr   )rJ   rl   rh   rt   s    r*   runtime_base_urlz!PooledCredential.runtime_base_url  s'    =F""*;dm;}rU   )rw   rK   )rJ   rK   r   rp   r#   r   )r#   rp   )r#   rK   )r#   r^   )r~   
__module____qualname____annotations__r_   r`   rb   rd   re   rf   rg   rh   ri   rj   rk   rl   rm   rn   ro   rq   rv   r   classmethodr   r   propertyr   r   r   rU   r*   rW   rW      s        MMMGGGJJJNNNMMMKKK#'M''''!%K%%%%&*N****%)O))))'+++++(,,,,,+/////"H"""" $J$$$$#'M''''"&L&&&&(,,,,,#I####*.....M E    
 
 
Z Z Z Z
 . . . [. K K K K* , , , X,*    X  rU   rW   fallbackc                    t          |           }dD ]V}|                    |          }t          |t                    r*|                                r|                                c S W|S )N)emailpreferred_usernameupn)r   r{   rP   rK   r   )rL   r   claimskeyr   s        r*   label_from_tokenr     sh    &&F5 ! !

3eS!! 	!ekkmm 	!;;==   OrU   entriesList[PooledCredential]rZ   c                >    t          d | D             d          dz   S )Nc              3  $   K   | ]}|j         V  d S rs   r[   r   entrys     r*   	<genexpr>z!_next_priority.<locals>.<genexpr>  s$      445444444rU   )default   )max)r   s    r*   _next_priorityr     s)    44G444bAAAAEErU   r\   boolc                    | pd                                                                 }|t          k    p|                    t           d          S )Nr   :)r   lowerr   rQ   )r\   
normalizeds     r*   _is_manual_sourcer     sJ    ,B%%''--//J&T**?*?=@S@S@S*T*TTrU   
error_coderc   c                D    | 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)r   s    r*   _exhausted_ttlr   !  s*    S((S((((rU   r   ra   c                   | | dk    rdS t          | t          t          f          r$t          |           }|dk    rdS |dk    r|dz  n|S t          | t                    r|                                 }|sdS 	 t          |          }n# t
          $ r d}Y nw xY w||dk    r|dz  n|S 	 t          j        |                    dd                    	                                S # t
          $ r Y dS w xY wdS )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)
rP   rZ   floatrK   r   
ValueErrorr   fromisoformatr   	timestamp)r   numericraws      r*   r   r   *  s;    }t%#u&& L,,a<<4#*->#>#>wGK% kkmm 	4	CjjGG 	 	 	GGG	'.1B'B'B7V##O	)#++c8*D*DEEOOQQQ 	 	 	44	4s$   9B	 	BB+9C% %
C32C3messagec                x   | sd S t          j        d| t           j                  }|rTt          |                    d                    }|                    d                                          dk    r|dz  n|S t          j        d| t           j                  }|r"t          |                    d                    S t          j        d| t           j                  }|rKt          |                    d                    dz  t          |                    d                    d	z  z   S t          j        d
| t           j                  }|r%t          |                    d                    dz  S t          j        d| t           j                  }|r%t          |                    d                    d	z  S d S )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   rZ   )r   delay_matchr   	sec_matchhr_min_matchhr_only_matchmin_only_matchs          r*   _extract_retry_delay_secondsr   H  s    t)KWVXVcddK Qk''**++!,!2!21!5!5!;!;!=!=!E!Euv~~5P	\^egigtuuI )Y__Q''(((9GRTR_``L S<%%a(())D03|7I7I!7L7L3M3MPR3RRRI<gr}UUM 2=&&q))**T11Y>WWN 1>''**++b004rU   error_contextOptional[Dict[str, Any]]rp   c                   t          | t                    si S i }|                     d          }t          |t                    r+|                                r|                                |d<   |                     d          }t          |t                    r+|                                r|                                |d<   |                     d          p)|                     d          p|                     d          }t          |          }|<t          |t                    r't          |          }|t          j                    |z   }|||d<   |S )Nreasonr   reset_at	resets_atretry_until)rP   dictr{   rK   r   r   r   time)r   r   r   r   r   parsed_reset_atretry_delay_secondss          r*   _normalize_error_contextr   _  sK   mT** 	!#Jx((F&# .6<<>> .%||~~
8	**G'3 0GMMOO 0 '
9*%% 	,[))	,]++ 
 099O:gs#;#;:7CC*"ikk,??O"!0
:rU   r   c                    | j         t          k    rd S t          t          | dd                     }||S | j        r| j        t          | j                  z   S d S )Nrg   )r`   STATUS_EXHAUSTEDr   r   rb   r   rd   )r   r   s     r*   _exhausted_untilr   x  se    ,,,t(8Mt)T)TUUH L#nU5J&K&KKK4rU   rw   c                v    |                                                                                      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11rU   configc              #    K   | t                      } | dS |                     d          }t          |t                    s$	 ddlm}  ||           }n# t          $ r Y dS w xY w|sdS |D ]V}t          |t                    s|                    d          }t          |t                    sCt          |          |fV  WdS )zJYield (normalized_name, entry_dict) for each valid custom_providers entry.Ncustom_providersr   )get_compatible_custom_providersrw   )
r+   r{   rP   listr(   r  r)   r   rK   r   )r   r   r  r   rw   s        r*   _iter_custom_providersr    s     ~"$$~zz"455&-- 	IIIIII>>vFF 	 	 	FF	 ! 7 7%&& 	yy  $$$ 	)$//666667 7s   A 
A"!A"rh   r^   provider_namec                   | sdS |                                                      d          }|r6t          |          }t                      D ]\  }}||k    rt           | c S t                      D ]c\  }}t          |                    d          pd                                                               d          }|r||k    rt           | c S ddS )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/rh   r   )r   rstripr   r  CUSTOM_POOL_PREFIXrK   r{   )rh   r  normalized_urlnormalized_name	norm_namer   	entry_urls          r*   get_custom_provider_pool_keyr    s	     t^^%%,,S11N
  :5mDD 6 8 8 	: 	:IuO++,9i99999 , 344 6 6	5		*--344::<<CCCHH	 	6n44(5)555554rU   	List[str]c                 X     t          d           t           fd D                       S )z?Return all 'custom:*' pool keys that have entries in auth.json.Nc              3     K   | ]P}|                     t                    t                              |          t                    D|         L|V  Qd S rs   )rQ   r  rP   r{   r  )r   r   	pool_datas     r*   r   z-list_custom_pool_providers.<locals>.<genexpr>  sx        >>,-- y}}S))400 cN	     rU   )r!   sorted)r  s   @r*   list_custom_pool_providersr    sH    $T**I          rU   pool_keyc                    |                      t                    sdS | t          t                    d         }t                      D ]\  }}||k    r|c S dS )zWReturn the custom_providers config entry matching a pool key like 'custom:together.ai'.N)rQ   r  lenr  )r  suffixr  r   s       r*   _get_custom_provider_configr    so    122 tc,--../F244  	5LLL 4rU   c                H   t                      }|t          S |                    d          }t          |t                    st          S t          |                    | d          pd                                                                          }|t          v r|S t          S )z8Return the configured selection strategy for a provider.Ncredential_pool_strategiesr   )	r+   STRATEGY_FILL_FIRSTr{   rP   r   rK   r   r   SUPPORTED_POOL_STRATEGIES)rJ   r   
strategiesstrategys       r*   get_pool_strategyr    s      F~""899Jj$'' #"":>>(B//5266<<>>DDFFH,,,rU   rh   pool_or_providerc               J   t          | dd          }|t          | t                    r| }ndS t          |pd                                                                          }t          |pd                                                                          }|r|sdS ||k    rdS |dk    s|                    t                    sdS 	 t          |pd          }n# t          $ r Y dS w xY wt          |pd                                                                          |k    S )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.
    rJ   NTr   Fcustom)	r   rP   rK   r   r   rQ   r  r  r)   )r!  rJ   rh   raw_pool_providerpool_providerprovider_normmatched_pools          r*    credential_pool_matches_providerr(    sF      0*dCC &,, 	 0
 4)/R006688>>@@MB''--//5577M  u%%t  (@(@AS(T(T u3HNCC   uu|!r""((**0022mCCs   	C 
C)(C)r   provider_idstateNonec                &   	 t          j                    }n# t          $ r Y dS w xY w|dS t          j                            d          rzt          j                            dd          }|rXt          |          dz  dz  }	 |                    d          |                    d          k    rdS n# t          $ r Y dS w xY w	 t          j        | ||d	           dS # t          $ r'}t          
                    d
| |           Y d}~dS 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environr{   r	   resolve _persist_provider_state_to_storeloggerdebug)r)  r*  global_pathreal_home_env	real_rootexcs         r*   ,_write_through_provider_state_to_global_rootr=    s   (577   
 
z~~+,, 
vr22 	]++i7+EI&&e&44	8I8IQV8I8W8WWWF X   
1		
 	
 	
 	
 	
 	
  
 
 
F	
 	
 	
 	
 	
 	
 	
 	
 	

s6    
$$.B3 3
C CC 
D)DDc                  Z   e Zd ZdNdZdOdZdOd	ZdPd
ZdQdZdQdZdRdZ	dddSdZ
dTdZ	 dUdddVd#ZdWd$ZdWd%ZdWd&ZdWd'ZdWd(ZdXd)ZdYd+ZdZd-ZdYd.Zd[d/Zd[d0ZdQd1Zd2d2d3d\d6Zd]d7Zdd8d^d9ZdQd:Zddd;d_d>ZdUd`d@ZdadAZdQdBZ 	 dUdbdCZ!dQdDZ"dcdFZ#dddHZ$dedLZ%dWdMZ&dS )fCredentialPoolrJ   rK   r   r   c                    || _         t          |d           | _        d | _        t	          |          | _        t          j                    | _        i | _	        t          | _        d | _        d S )Nc                    | j         S rs   r   r   s    r*   <lambda>z)CredentialPool.__init__.<locals>.<lambda>I  s    %. rU   r   )rJ   r  _entries_current_idr  	_strategy	threadingLock_lock_active_leases%DEFAULT_MAX_CONCURRENT_PER_CREDENTIAL_max_concurrent_last_no_entries_log_at)ru   rJ   r   s      r*   __init__zCredentialPool.__init__G  sg     w,H,HIII*.*844^%%
.0D 9=$$$rU   r#   r   c                l    | j         5  t          | j                  cd d d            S # 1 swxY w Y   d S rs   )rJ  r   rE  rt   s    r*   has_credentialszCredentialPool.has_credentialsV  {    Z 	' 	'&&	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	'   )--c                    | j         5  t          |                                           cddd           S # 1 swxY w Y   dS )zCTrue if at least one entry is not currently in exhaustion cooldown.N)rJ  r   _available_entriesrt   s    r*   has_availablezCredentialPool.has_availableZ  s     Z 	3 	3//1122	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3s   !6::c                l    | j         5  t          | j                  cd d d            S # 1 swxY w Y   d S rs   )rJ  r  rE  rt   s    r*   r   zCredentialPool.entriesd  rR  rS  Optional[PooledCredential]c                X      j         sd S t           fd j        D             d           S )Nc              3  <   K   | ]}|j         j        k    |V  d S rs   )rX   rF  r   r   ru   s     r*   r   z3CredentialPool._current_unlocked.<locals>.<genexpr>k  s2      VVuTEU9U9UU9U9U9U9UVVrU   )rF  nextrE  rt   s   `r*   _current_unlockedz CredentialPool._current_unlockedh  s;     	4VVVVVVVX\]]]rU   c                l    | j         5  |                                 cd d d            S # 1 swxY w Y   d S rs   )rJ  r]  rt   s    r*   currentzCredentialPool.currentm  s}    Z 	, 	,))++	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	,rS  oldrW   newr+  c                t    t          | j                  D ]"\  }}|j        |j        k    r|| j        |<    dS #dS )z4Swap an entry in-place by id, preserving sort order.N)	enumeraterE  rX   )ru   r`  ra  idxr   s        r*   _replace_entryzCredentialPool._replace_entryq  sP    #DM22 	 	JCx36!!%(c" "	 	rU   Nremoved_idsrg  Optional[List[str]]c               R    t          | j        d | j        D             |           d S )Nc                6    g | ]}|                                 S r   r   r   s     r*   
<listcomp>z+CredentialPool._persist.<locals>.<listcomp>{  s     888U]]__888rU   rf  )r"   rJ   rE  )ru   rg  s     r*   _persistzCredentialPool._persistx  s>    M88$-888#	
 	
 	
 	
 	
 	
rU   status_coderc   normalized_errorrp   c                    |dk    rdS |                     d          }t          |t                    sdS |                                                                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   )r{   rP   rK   r   r   _TERMINAL_AUTH_REASONS)ru   rn  ro  r   s       r*   _is_terminal_auth_failurez(CredentialPool._is_terminal_auth_failure  s^      #5!%%h//&#&& 	5||~~##%%)???rU   Tpersistr   r   r   rt  c                  t          |          }|                     ||          rt          }nt          }t	          ||t          j                    ||                    d          |                    d          |                    d                    }|                     ||           |r|                                  |S )Nr   r   r   r`   rb   rd   re   rf   rg   )	r   rr  STATUS_DEADr   r   r   r{   re  rm  )ru   r   rn  r   rt  ro  terminal_statusupdateds           r*   _mark_exhaustedzCredentialPool._mark_exhausted  s     4MBB ))+7GHH 	/)OO.O'9;;'.228<</33I>> 0 4 4Z @ @
 
 
 	E7+++ 	MMOOOrU   c                   | j         dk    s|j        dk    r|S 	 ddlm}  |            }|s|S |                    dd          }|                    dd          }|                    dd          }|j        pd}|j        pd}|s|r|r||k    s|r||k    rzt                              d	|j	                   t          ||p|j        |p|j        |p|j        d
d
d
d
d
d

  
        }	|                     ||	           |                                  |	S n2# t          $ r%}
t                              d|
           Y d
}
~
nd
}
~
ww xY w|S )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.
        rO   claude_coder   )read_claude_code_credentialsrefreshTokenr   accessToken	expiresAtzDPool entry %s: syncing tokens from credentials file (tokens changed)N)	r]   r_   rj   r`   rb   rd   re   rf   rg   z(Failed to sync from credentials file: %s)rJ   r\   agent.anthropic_adapterr}  r{   r]   r_   r7  r8  rX   r   rj   re  rm  r)   )ru   r   r}  credsfile_refreshfile_accessfile_expiresentry_accessentry_refreshry  r<  s              r*   +_sync_anthropic_entry_from_credentials_filez:CredentialPool._sync_anthropic_entry_from_credentials_file  s    =K''5<=+H+HL'	JLLLLLL0022E  99^R88L))M266K 99[!44L !-3L!/52M | !,!<!<  "=%1]%B%BZH   "!,!B0B"."E%2E"."E%2E $#'$(&*'+(,   ##E7333 	J 	J 	JLLCSIIIIIIII	Js   D C!D 
E D;;E c           	     ^   | j         dk    s|j        dk    r|S 	 t                      5  t                      }t	          |d          }ddd           n# 1 swxY w Y   t          |t                    s|S |                    d          }t          |t                    s|S |                    dd          }|                    dd          }|j        pd}|j	        pd}|r||k    s|r||k    rt                              d|j                   ||p|j	        ddddddd	}	|                    d
          r|d
         |	d
<   t          |fi |	}
|                     ||
           |                                  |
S n2# t           $ r%}t                              d|           Y d}~nd}~ww xY w|S )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`   rb   rd   re   rf   rg   rk   z-Failed to sync Codex entry from auth.json: %srJ   r\   r   r   r   rP   r   r{   r]   r_   r7  r8  rX   r   re  rm  r)   ru   r   
auth_storer*  r  store_accessstore_refreshr  r  field_updatesry  r<  s               r*   !_sync_codex_entry_from_auth_storez0CredentialPool._sync_codex_entry_from_auth_store  s[   " =N**elm.K.KL*	O!## I I-//
,ZHHI I I I I I I I I I I I I I I eT** YYx((Ffd++ !::nb99L"JJ;;M !-3L!/52M ,,! -&3}&D&D5H   %1%2%Ie6I#'&*'+)-*.+/	1 	1 99^,, J49.4IM.1!%99=99##E7333 	O 	O 	OLLH#NNNNNNNN	OG   E; AE; AE; AE; 5+E; !CE; ;
F*F%%F*c           	     ^   | j         dk    s|j        dk    r|S 	 t                      5  t                      }t	          |d          }ddd           n# 1 swxY w Y   t          |t                    s|S |                    d          }t          |t                    s|S |                    dd          }|                    dd          }|j        pd}|j	        pd}|r||k    s|r||k    rt                              d|j                   ||p|j	        ddddddd	}	|                    d
          r|d
         |	d
<   t          |fi |	}
|                     ||
           |                                  |
S n2# t           $ r%}t                              d|           Y d}~nd}~ww xY w|S )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-oauthr  Nr  r]   r   r_   zUPool entry %s: syncing xAI OAuth tokens from auth.json (refreshed by another process)r  rk   z1Failed to sync xAI OAuth entry from auth.json: %sr  r  s               r*   %_sync_xai_oauth_entry_from_auth_storez4CredentialPool._sync_xai_oauth_entry_from_auth_store,  sY    =K''5<=+H+HL'	S!## F F-//
,ZEEF F F F F F F F F F F F F F F eT** YYx((Ffd++ !::nb99L"JJ;;M -3L!/52M ,,! -&3}&D&D5H   %1%2%Ie6I#'&*'+)-*.+/	1 	1 99^,, J49.4IM.1!%99=99##E7333 	S 	S 	SLLLcRRRRRRRR	Sr  c                   | j         dk    rS 	 t          fdt          | j                   D             d          }t          |t                    sS t
                              | j         |          }|j        j        k    s|j        j        k    r8t          
                    dj                   |                     |           |S n2# t          $ r%}t          
                    d|           Y d}~nd}~ww xY wS )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   | ]9}t          |t                    r"|                    d           j        k    5|V  :dS )rX   N)rP   r   r{   rX   )r   r   r   s     r*   r   zGCredentialPool._sync_xai_oauth_entry_from_pool_store.<locals>.<genexpr>u  s\        !'400 6=[[5F5F%(5R5R 5R5R5R5R rU   NzIPool entry %s: adopting xAI OAuth tokens rotated by another pool instancez7Failed to sync xAI OAuth entry from credential pool: %s)rJ   r\  r!   rP   r   rW   r   r]   r_   r7  r8  rX   re  r)   )ru   r   	persistedstoredr<  s    `   r*   %_sync_xai_oauth_entry_from_pool_storez4CredentialPool._sync_xai_oauth_entry_from_pool_storef  sB    =K''L	Y   #7#F#F  
  I i.. %//yIIF#u'999'5+>>>_H   ##E6222 ?  	Y 	Y 	YLLRTWXXXXXXXX	Ys   AC A7C 
C=C88C=c                   | j         dk    sj        dk    rS 	 t                      5  t                      }t	          |d          }ddd           n# 1 swxY w Y   |sS |                    dd          }|                    dd          }|||                    d          |                    d          |                    d	          |                    d
          d}t          fd|                                D                       }|r't          	                    dj
                   ddddddd}|r||d<   |r||d<   |                    d          r|d         |d<   |                    d          r|d         |d<   |                    d	          r|d	         |d	<   |                    d
          r|d
         |d
<   t          j                  }	dD ]}
|                    |
          }|||	|
<   t          fd|	i|}|                     |           |                                  |S n2# t           $ r%}t          	                    d|           Y d}~nd}~ww xY wS )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   r  Nr_   r   r]   ri   rm   rn   rl   )r]   r_   ri   rm   rn   rl   c              3  P   K   | ] \  }}|d vot          |d          |k    V  !dS )Nr   N)r   )r   r   r   r   s      r*   r   zBCredentialPool._sync_nous_entry_from_auth_store.<locals>.<genexpr>  sV        C Z'NGE3,E,E,N     rU   z0Pool entry %s: syncing Nous state from auth.jsonrv  rB   r@   rC   rH   rF   rI   rq   z,Failed to sync Nous entry from auth.json: %s)rJ   r\   r   r   r   r{   anyr   r7  r8  rX   r   rq   r   re  rm  r)   )ru   r   r  r*  r  r  comparable_updatesshould_syncr  extra_updates	extra_keyvalry  r<  s    `            r*    _sync_nous_entry_from_auth_storez/CredentialPool._sync_nous_entry_from_auth_store  s^    =F""elm&C&CL9	N!## A A-//
,Z@@A A A A A A A A A A A A A A A  !IIor::M 99^R88L ,!.#ii55"YY{33(-		2H(I(I&+ii0D&E&E" "     "4":":"<"<    K  #FH  
 $(&*'+)-*.+/1 1   A4@M.1  C5BM/299\** F272EM,/99[)) D16{1CM+.99344 Z<ABX<YM"8999122 V:?@T:UM"67 $U[ 1 1"; 7 7I  ))I..C36i0!%NN}NNN##E7333G#H  	N 	N 	NLLGMMMMMMMM	NsA   H< AH< AH< AH< #GH< <
I+I&&I+c                   |j         dk    rdS 	 t                      5  t                      }dddd                    | j                  }t          |          oVt          |                    d          t                    o-t          |d                             |          t                     }| j        dk    rt          |d          }|	 ddd           dS |j	        |d<   |j
        r
|j
        |d	<   |j        r
|j        |d
<   |j        r
|j        |d<   |j        r
|j        |d<   dD ]#}|j                            |          }||||<   $|j        r
|j        |d<   t!          |d|d           nz| j        dk    rt          |d          }t          |t                    s	 ddd           dS |                    d          }t          |t                    s	 ddd           dS |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           dS |                    d          }t          |t                    s	 ddd           dS |j	        |d<   |j
        r
|j
        |d	<   |j        r
|j        |d<   t!          |d|d           n	 ddd           dS t%          |           |r|rt'          ||           ddd           dS # 1 swxY w Y   dS # t(          $ r,}	t*                              d| j        |	           Y d}	~	dS 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.
        r  Nr   r  r  )r   r  r  	providersr]   r_   ri   rm   rn   r  rl   Fr0  r  rk   z3Failed to sync %s pool entry back to auth store: %s)r\   r   r   r{   rJ   r   rP   r   r   r]   r_   ri   rm   rn   rq   rl   r    rk   r   r=  r)   r7  r8  )
ru   r   r  _wt_provider_idwrite_through_to_rootr*  r  r  r  r<  s
             r*   %_sync_device_code_entry_to_auth_storez4CredentialPool._sync_device_code_entry_to_auth_store  sZ   4 <=((FU	d!## R R-//
 #$2!,# # #dm$$	  
 )-_(=(= )z~~k::DAA "";/33ODDd C% =F**0VDDE}7R R R R R R R R8 -2,>E.)* E161Do.' ?.3.>l+ =-2_k*1 S8=8R45&? 3 3	 $kooi88?/2E),/ O6;6N23)*fePUVVVVV]n440^LLE%eT22 eR R R R R R R Rf #YYx00F%fd33 kR R R R R R R Rl .3-?F>** F272E/) C050Bn-)*neX]^^^^^]k110[IIE%eT22 AR R R R R R R RB #YYx00F%fd33 GR R R R R R R RH .3-?F>** F272E/) C050Bn-)*k5UZ[[[[[ YR R R R R R R R\ !,,,( _ @'  aR R R R R R R R R R R R R R R R R Rf  	d 	d 	dLLNPTP]_bccccccccc	ds   L, B1LL, CL&L, 3+LL, +A1LL, )+LL, !AL"L, /#LL, L##L, &L#'L, ,
M"6!MM"forcec                  |j         t          k    s|j        s|r|                     |d            d S | j        dv r| j        dk    r| j        n| j        }t          |                                           5   ||          }| j        dk    rN||ur'|}|s#| 	                    |          s|cd d d            S | 
                    ||          cd d d            S |j        |j        k    s|j        |j        k    r|cd d d            S | 
                    ||          cd d d            S # 1 swxY w Y   | 
                    ||          S )N)r  r  r  )timeout_secondsr  )rM   rR   r_   rz  rJ   r  r  r    _single_use_refresh_lock_timeout_entry_needs_refresh_refresh_entry_implr]   )ru   r   r  
sync_entrysynceds        r*   _refresh_entryzCredentialPool._refresh_entryH  sQ   ?o--U5H- 2$$UD1114 =999 =N22 66? 
 " $ E E G G   E E $E**=N22U** &$ )T-F-Fu-M-M )#(E E E E E E E E  33E3GGE E E E E E E E '5+===+u/BBB!E E E E E E E E //e/DDE E E E E E E E E E E E E E E E  ''U';;;s$   65D,8D,!D,	D,,D03D0r   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.
        r  $HERMES_CODEX_REFRESH_TIMEOUT_SECONDS"HERMES_XAI_REFRESH_TIMEOUT_SECONDS   g      @)rJ   r   	env_floatr   r   AUTH_LOCK_TIMEOUT_SECONDS)ru   env_varrefresh_timeout_secondss      r*   r  z/CredentialPool._single_use_refresh_lock_timeouto  sg     }.. 325 	
 #+"4Wb"A"A(455)**S0
 
 	
rU   c          
        	 | j         dk    rddlm}  ||j        |j                            d                    }t          ||d         |d         |d         	          }|j        d
k    r[	 ddlm}  ||d         |d         |d                    nx# t          $ r&}t          
                    d|           Y d }~nMd }~ww xY wnC| j         dk    rm|                     |          }||ur|}t          j        |j        |j                  }t          ||d         |d         |                    d                    }n| j         dk    rm|                     |          }||ur|}t          j        |j        |j                  }t          ||d         |d         |                    d                    }nS| j         dk    rF|                     |          }||ur|}t          j        |           |                     |          }n|S 
ny# t          $ 
rk}	t          
                    d| j         |j        |	           | j         dk    r|j        d
k    r|                     |          }|j        |j        k    r4t          
                    d           	 ddlm}  ||j        |j                            d                    }t          ||d         |d         |d         t,          d d           }|                     ||           |                                  	 ddlm}  ||d         |d         |d                    n2# t          $ r%}t          
                    d|           Y d }~nd }~ww xY w|cY d }	~	S # t          $ r%}
t          
                    d|
           Y d }
~
n>d }
~
ww xY w|                     |          s!t          
                    d           |cY d }	~	S | j         dk    r|                     |          }|j        |j        k    rft          
                    d           t          |t,          d d d d d           }|                     ||           |                                  |cY d }	~	S t          j        |	          r>t          
                    d           	 t7                      5  t9                      }t;          |d          pi }t=          |t>                    r:|                    d          pi }t=          |t>                    rtA          |                    d          pd          !                                }tA          |j        pd          !                                }|r||k    r|"                    dd            |"                    dd            ||d<   dtG          |	dd          tA          |	          d d!tI          j%        tL          j'                  (                                d"|d#<   tS          |d|           tU          |           d d d            n# 1 swxY w Y   n2# t          $ r%}t          
                    d$|           Y d }~nd }~ww xY wd% | j+        D             }d& | j+        D             | _+        | j,        |j        k    rd | _,        |                     |'           Y d }	~	d S | j         dk    r|                     |          }|j        |j        k    rft          
                    d(           t          |t,          d d d d d           }|                     ||           |                                  |cY d }	~	S t          j-        |	          r>t          
                    d)           	 t7                      5  t9                      }t;          |d          pi }t=          |t>                    r:|                    d          pi }t=          |t>                    rtA          |                    d          pd          !                                }tA          |j        pd          !                                }|r||k    r|"                    dd            |"                    dd            ||d<   dtG          |	dd          tA          |	          d d!tI          j%        tL          j'                  (                                d"|d#<   tS          |d|           tU          |           d d d            n# 1 swxY w Y   n2# t          $ r%}t          
                    d*|           Y d }~nd }~ww xY wd+ | j+        D             }d, | j+        D             | _+        | j,        |j        k    rd | _,        |                     |'           Y d }	~	d S | j         dk    r|                     |          }|j        |j        k    r{t          
                    d-           t          |t,          d d d d d           }|                     ||           |                                  | .                    |           |cY d }	~	S t          j/        |	          rt          
                    d.           	 t7                      5  t9                      }t;          |d          p&|j0        |j1        |j2        |j3        |j4        |j5        d/}tA          |                    d          pd          !                                }tA          |j        pd          !                                }|r||k    rNt          j6        ||	d 0           t          j7        ||	d 0           tS          |d|           tU          |           d d d            n# 1 swxY w Y   n2# t          $ r%}t          
                    d1|           Y d }~nd }~ww xY wt          j8        d2t          j8         hfd3| j+        D             }fd4| j+        D             | _+        | j,        |j        k    rd | _,        |                     |'           Y d }	~	d S | 9                    |d            Y d }	~	d S d }	~	ww xY wt          |t,          d d d d d           }|                     ||           |                                  | .                    |           |S )5NrO   r   )refresh_anthropic_oauth_purehermes_pkce)use_jsonr]   r_   rj   )r]   r_   rj   r|  )_write_claude_code_credentialsz7Failed to write refreshed token to credentials file: %sr  rk   )r]   r_   rk   r  r   )force_refreshz'Credential refresh failed for %s/%s: %sz8Retrying refresh with synced token from credentials file)r]   r_   rj   r`   rb   rd   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 — adoptingrv  zIxAI OAuth refresh token is terminally invalid; clearing local token stater  r   codeunknowncredential_pool_refresh_failureT)rJ   r  r   r   relogin_requiredatlast_auth_errorz,Failed to clear terminal xAI OAuth state: %sc                2    g | ]}|j         d k    |j        S r  r\   rX   r   items     r*   rl  z6CredentialPool._refresh_entry_impl.<locals>.<listcomp>5  1     # # #$(;-77 777rU   c                (    g | ]}|j         d k    |S r  r\   r  s     r*   rl  z6CredentialPool._refresh_entry_impl.<locals>.<listcomp>9  /     % % %!%;-77 777rU   rf  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: %sc                2    g | ]}|j         d k    |j        S r  r  r  s     r*   rl  z6CredentialPool._refresh_entry_impl.<locals>.<listcomp>{  r  rU   c                (    g | ]}|j         d k    |S r  r  r  s     r*   rl  z6CredentialPool._refresh_entry_impl.<locals>.<listcomp>  r  rU   u?   Nous refresh failed but auth.json has newer tokens — adoptingzDNous refresh token is terminally invalid; clearing local token state)r?   rE   rl   rA   r>   r=   )r   z-Failed to clear terminal Nous OAuth state: %szmanual:c                0    g | ]}|j         v |j        S r   r  r   r  singleton_sourcess     r*   rl  z6CredentialPool._refresh_entry_impl.<locals>.<listcomp>  s3     # # #$(;*;;; ;;;rU   c                &    g | ]}|j         v|S r   r  r  s     r*   rl  z6CredentialPool._refresh_entry_impl.<locals>.<listcomp>  s1     % % %!%;.??? ???rU   ):rJ   r  r  r_   r\   endswithr   r  r)   r7  r8  r  r   refresh_codex_oauth_purer]   r{   r  refresh_xai_oauth_purer   resolve_nous_runtime_credentialsrX   r  	STATUS_OKre  rm  r  $_is_terminal_xai_oauth_refresh_errorr   r   r   rP   r   rK   r   popr   r   nowr   utc	isoformatr   r   rE  rF  &_is_terminal_codex_oauth_refresh_errorr  _is_terminal_nous_refresh_errorr?   rE   rl   rA   r>   r=   _quarantine_nous_oauth_state_quarantine_nous_pool_entriesNOUS_DEVICE_CODE_SOURCErz  )ru   r   r  r  	refreshedry  r  wexcr  r<  	retry_excr  r*  r  r  r  	clear_excrg  r  s                     @r*   r  z"CredentialPool._refresh_entry_impl  s   F	}++PPPPPP88'"\22=AA  	 "!*>!:"+O"<"+O"<	   <=00fZZZZZZ66%n5%o6%o6   
 % f f f%^`deeeeeeeef 1 .00
 ??FF&&"E$=&' 	 "!*>!:"+O"<!*~!>!>	   +-- CCEJJ&&"E$;&' 	 "!*>!:"+O"<!*~!>!>	   &((>>uEE&&"E9"'    ??FF |	 |	 |	LLBDMSXS[]`aaa }++0M0MII%PP'5+>>>LL![\\\QXXXXXX$@$@"0%+]%;%;M%J%J% % %	 #*")2>)B*3O*D*3O*D(1+/,0# # # ++FG<<<w^^^^^^:: ). 9 )/ : )/ :   
  ) w w w"LL)oquvvvvvvvvw&$ Q Q Q%DiPPPPPPPPQ226:: "LL!Z[[[!MMMMMM }++CCEJJ'5+>>>LL^   &$-'+(,*.+/,0  G ''888MMOOO"NNNNNN @EE * LLc  -// E E)9););J$8[$Q$Q$WUWE)%66 E).8)<)<)B#-fd#;#; !E47

?8S8S8YWY4Z4Z4`4`4b4bM478K8Qr4R4R4X4X4Z4ZM+8 %EM]<Z<Z(.

>4(H(H(H(.

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

?8S8S8YWY4Z4Z4`4`4b4bM478K8Qr4R4R4X4X4Z4ZM+8 %EM]<Z<Z(.

>4(H(H(H(.

?D(I(I(I:@h8F4;C4S4S7:3xx6W@D2:,x|2L2L2V2V2X2XD* D*.?(@ )=ZY^(_(_(_(8(D(D(D+E E E E E E E E E E E E E E E, %   Li       # #,0M# # #K% %)-% % %DM '5833+/(MMkM:::44444 }&&>>uEE'5+>>>LL!bccc%$-'+(,*.+/,0  G ''888MMOOO>>wGGG"NNNNNN;C@@ . LL!ghhha-// = =)9););J$8V$L$L %-2_383H6;6N.3.>).',yQ QE -0		/0J0J0Pb,Q,Q,W,W,Y,YM,/0C0Ir,J,J,P,P,R,RM#0 =M]4R4R ( E$)$'+L!" !" !" !"
 !) F$.$'+L!" !" !" !"
 !5Z O O O 0 < < <1= = = = = = = = = = = = = = =2 % a a a%TV_````````a !8D("BDD)%# # # #,0M# # #K% % % %)-% % %DM '5833+/(MMkM:::44444  ---44444y|	| ! "# $
 
 
 	E7+++ 	227;;;s  A+H /%B H 
C C;H CEH rA?rBN#%M	N	
M8M3.N3M88N<r
N1N,'r,N113r$r*Br;r/r1Y?E1X<0Y<Y 	 YY 	Yr
Y7Y2-r2Y77ArBr%r+/rd2)E1d&d2&d*	*d2-d*	.d21r2
e!<ere!!Ar>B&r$r*/rn:(C:n."n:.n2	2n:5n2	6n:9r:
o)o$r$o))A6r%rrc                P   | j         dk    s|j        t          k    rdS t          j        |j        |j        |j                  sdS |j        pd}|sdS 	 t          t          j
        ||j                            S # t          $ r  t                              dd           Y dS w xY w)u  Live-check whether an exhausted Codex entry's quota reset early.

        A Codex 429 persists a ``last_error_reset_at`` that can be days in
        the future (weekly windows), but the upstream window can reopen
        before then — the user redeems a banked rate-limit reset via the
        Codex CLI / ChatGPT UI, upgrades their plan, or OpenAI resets the
        window.  Without this check the pool keeps the credential frozen
        until the stale timestamp elapses even though the account is
        usable (issue #43747).

        Only fires for openai-codex entries frozen by a 429/quota-shaped
        error.  The underlying probe is throttled per token (5 min) so this
        is safe on the hot selection path.
        r  Fr   r   z!Codex quota-restored probe failedT)exc_info)rJ   r`   r   r   _is_codex_rate_limit_shapedrd   re   rf   r]   r   _probe_codex_quota_restoredrh   r)   r7  r8  )ru   r   rL   s      r*   _codex_quota_restored_upstreamz-CredentialPool._codex_quota_restored_upstream  s     =N**e.?CS.S.S53!#$
 
 	
 5"(b 	5		4"^      	 	 	LL<tLLLL55	s   'A; ;&B%$B%c                   |j         t          k    rdS | j        dk    rE|j        dS t	          |j                  t	          t          j                    dz            dz   k    S | j        dk    rt          |j        t                    S | j        dk    r1t          j
        |j        t          j        |j                            S | j        dk    rdS dS )NFrO     i r  r  r   )rM   rR   rJ   rj   rZ   r   r   r]   r   r   _xai_access_token_is_expiring#_xai_proactive_refresh_skew_secondsru   r   s     r*   r  z#CredentialPool._entry_needs_refresh  s    ?o--5=K''"*uu*++s49;;3E/F/F/PPP=N**2"7   =K''9"<U=OPP   =F"" 5urU   c                l    | j         5  |                                 cd d d            S # 1 swxY w Y   d S rs   )rJ  _select_unlockedrt   s    r*   selectzCredentialPool.select  s}    Z 	+ 	+((**	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+rS  Fclear_expiredrefreshr  r  c          
        t          j                     }d}g }g }| j        D ]}|j        t          k    r|j        s| j        dk    r=|j        dk    r2|j        t          t          hv r| 
                    |          }||ur|}d}| j        dk    r=|j        dk    r2|j        t          t          hv r|                     |          }||ur|}d}| j        dk    r=|j        dk    r2|j        t          t          hv r|                     |          }||ur|}d}| j        dk    r=|j        dk    r2|j        t          t          hv r|                     |          }||ur|}d}|j        t          k    rt          |j                  r{|j        pd	}	|	rp||	z
  t           k    rb|j        p|j        d
d         }
t&                              d|
|j        pd||	z
  dz  | j                   |                    |j                   d}|j        t          k    rgt/          |          }|||k     r|r|                     |          s|r5t3          |t4          d
d
d
d
d
          }|                     ||           |}d}|r2|                     |          r|                     |d          }||}|                    |           |r't=          |          fd| j        D             | _        |r|                     |           |S )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).
        FrO   r|  Tr   r  r  r  r   N   uh   credential pool: pruning DEAD manual entry %s (reason=%s, age=%.1fh) — re-add via `hermes auth add %s`r  g      @rv  r  c                &    g | ]}|j         v|S r   rX   )r   e
pruned_idss     r*   rl  z5CredentialPool._available_entries.<locals>.<listcomp>  s%    PPP1Z9O9OQ9O9O9OrU   rf  ) r   rE  rM   rS   r   rJ   r\   r`   r   rw  r  r  r  r  r   rb   DEAD_MANUAL_PRUNE_TTL_SECONDSrY   rX   r7  warningre   appendr   r  r   r  re  r  r  setrm  )ru   r  r  r  cleared_anyentries_to_prune	availabler   r  dead_at_labelexhausted_untilclearedr  r  s                 @r*   rU  z!CredentialPool._available_entries  s    ikk&(,.	] p	$ p	$E "333E<Q3 ,,1N1N).>-LLLII%PP&&"E"&K
 ''55).>-LLL>>uEE&&"E"&K //55).>-LLL??FF&&"E"&K
 ,,55).>-LLLCCEJJ&&"E"&K K// %U\22 +#27aG +3=3P#P#P!&!<!Y"!3@y 7]f4 M   )//999&*  $444"25"9"9".33H3H &! ??FF! !  '%$-'+(,*.+/,0  G ''w777#E"&K "444U;; " //U/CC	$!U#### 	Q-..JPPPPPPPDM 	8MM&6M777rU   c                    t          j                    }| j        }|||z
  t          k     rdS || _        t                              d           dS )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   	monotonicrN  )NO_AVAILABLE_ENTRIES_LOG_THROTTLE_SECONDSr7  info)ru   r  lasts      r*   _log_no_available_entriesz(CredentialPool._log_no_available_entries  sT     n+t/X X XF'*$TUUUUUrU   r  c                  |                      d|          }|sd | _        |                                  d S d | _        | j        t
          k    r"t          j        |          j        | _        S | j        t          k    rbt          |          dk    rOt          |d           t          j        dz             }|                     |           j        | _        |S | j        t          k    rt          |          dk    r|d         fd| j        D             }|                    t          t          | j                  dz
  	                     d
 t%          |          D             | _        |                                  j        | _        |                                 pS |d         j        | _        S )NTr  r   c                    | j         S rs   ro   )r  s    r*   rC  z1CredentialPool._select_unlocked.<locals>.<lambda>  s     rU   rD  r  r   c                4    g | ]}|j         j         k    |S r   r  )r   	candidater   s     r*   rl  z3CredentialPool._select_unlocked.<locals>.<listcomp>  s)    \\\Y9<SXS[C[C[yC[C[C[rU   r   c                6    g | ]\  }}t          ||           S r   r   )r   rd  r  s      r*   rl  z3CredentialPool._select_unlocked.<locals>.<listcomp>  s)    ggg.#yWY===gggrU   )rU  rF  r  rN  rG  STRATEGY_RANDOMr:   choicerX   STRATEGY_LEAST_USEDr  minr   ro   re  STRATEGY_ROUND_ROBINrE  r  rc  rm  r]  )ru   r  r
  ry  rotatedr   s        @r*   r  zCredentialPool._select_unlocked  s   ++$+PP	 	#D**,,,4
 (,$>_,,M),,E$xDL>000S^^a5G5G	'@'@AAAEe53F3JKKKGw///$xDN>111c)nnq6H6HaLE\\\\$-\\\GNN753t}3E3E3IJJJKKKggT]^eTfTfgggDMMMOOO$xD))++4u4! 8rU   c                    | j         5  |                                 }||cd d d            S |                                 }|r|d         nd cd d d            S # 1 swxY w Y   d S Nr   )rJ  r]  rU  )ru   r_  r
  s      r*   peekzCredentialPool.peek  s     Z 	7 	7,,..G"	7 	7 	7 	7 	7 	7 	7 	7 //11I#,69Q<<$	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7s   AAA A)r   api_key_hintr'  r^   c               ,  
 | j         5  d 
rjt          fd| j        D             d           

Gt                              d| j                   d | _        |                                 cd d d            S 
(|                                 p|                                 

	 d d d            d S 
j	        p
j
        d d         }|                     
||           rYd}| j        D ]9}|j
        
j
        k    r|j        k    r|                     |||d           d}:|r|                                  t          
fd| j        D             
          }|j        t          k    r%t                              d|||j        pd	           nt                              d
||           d | _        |                                 }|r1|j	        p|j
        d d         }	t                              d|	           |cd d d            S # 1 swxY w Y   d S )Nc              3  2   K   | ]}|j         k    |V  d S rs   r   )r   r  r'  s     r*   r   z;CredentialPool.mark_exhausted_and_rotate.<locals>.<genexpr>  s0      SS11Bl1R1RQ1R1R1R1RSSrU   zgcredential pool: failed key hint matched no %s entry; rotating without marking any credential exhaustedr  Frs  Tc              3  <   K   | ]}|j         j         k    |V  d S rs   r  )r   r  r   s     r*   r   z;CredentialPool.mark_exhausted_and_rotate.<locals>.<genexpr>  s1      >>qQTUX-=-=-=-=-=-=>>rU   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)rJ  r\  rE  r7  r  rJ   rF  r  r]  rY   rX   rz  r   rm  r`   rw  r  re   )ru   rn  r   r'  r  siblings_markedsiblingupdated_entry
next_entry_next_labelr   s      `      @r*   mark_exhausted_and_rotatez(CredentialPool.mark_exhausted_and_rotate  s&    Z J	 J	E 3
 SSSSSSS  = KKL  
 (,D$00221J	 J	 J	 J	 J	 J	 J	 J	2 }..00KD4I4I4K4K}9J	 J	 J	 J	 J	 J	 J	 J	: [0EHRaRLF  ]CCC  $"'#} / /GzUX-- .,>>,,#[- -    +/" $MMOOO >>>>DM>>> M (K77SK)H)UI    QK    $D..00J K(.C*-2C<kJJJUJ	 J	 J	 J	 J	 J	 J	 J	 J	 J	 J	 J	 J	 J	 J	 J	 J	 J	s   A"H	9-H	3E	H		HHcredential_idc                     j         5  |r; j                            |d          dz    j        |<   | _        |cddd           S                      dd          }|s	 ddd           dS  fd|D             }|r|n|}t          | fd          } j                            |j        d          dz    j        |j        <   |j         _        |j        cddd           S # 1 swxY w Y   dS )	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                f    g | ]-}j                             |j        d           j        k     +|.S )r   )rK  r{   rX   rM  r[  s     r*   rl  z0CredentialPool.acquire_lease.<locals>.<listcomp>E  sF       &**58Q77$:NNN NNNrU   c                R    j                             | j        d          | j        fS r%  )rK  r{   rX   r[   )r   ru   s    r*   rC  z.CredentialPool.acquire_lease.<locals>.<lambda>L  s#    4#6#:#:58Q#G#G"X rU   rD  )rJ  rK  r{   rF  rU  r!  rX   )ru   r2  r
  	below_cap
candidateschosens   `     r*   acquire_leasezCredentialPool.acquire_lease3  s    Z 	 	 %595H5L5L]\]5^5^ab5b#M2#0 $		 	 	 	 	 	 	 	 //dD/QQI 	 	 	 	 	 	 	 	   #,  I '0>YJXXXX  F .2-@-D-DVYPQ-R-RUV-VD	*%yD9+	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   1C%C%.A*C%%C),C)c                    | j         5  | j                            |d          }|dk    r| j                            |d           n|dz
  | j        |<   ddd           dS # 1 swxY w Y   dS )z/Release a previously acquired credential lease.r   r   N)rJ  rK  r{   r  )ru   r2  counts      r*   release_leasezCredentialPool.release_leaseR  s    Z 	? 	?'++M1==Ezz#''t<<<<5:QY#M2	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	?s   AA  A$'A$c                l    | j         5  |                                 cd d d            S # 1 swxY w Y   d S rs   )rJ  _try_refresh_current_unlockedrt   s    r*   try_refresh_currentz"CredentialPool.try_refresh_current[  s}    Z 	8 	85577	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8rS  c                F   | j         5  d}r"t          fd| j        D             d          }n*|                                 p|                     d          }|	 ddd           dS |j        | _        |                                 cddd           S # 1 swxY w Y   dS )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  2   K   | ]}|j         k    |V  d S rs   r*  )r   r  r'  s     r*   r   z6CredentialPool.try_refresh_matching.<locals>.<genexpr>n  s=        %$4DD "DDDD rU   Fr  )rJ  r\  rE  r]  r  rX   rF  r>  )ru   r'  r   s    ` r*   try_refresh_matchingz#CredentialPool.try_refresh_matching_  s2    Z 	8 	8E 
Y   )-  
   ..00XD4I4IRW4I4X4X}	8 	8 	8 	8 	8 	8 	8 	8  %xD5577!	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8s   AB*BBBc                    |                                  }|d S |                     |d          }||j        | _        |S )NTr  )r]  r  rX   rF  )ru   r   r  s      r*   r>  z,CredentialPool._try_refresh_current_unlocked|  sK    &&((=4''T'::	 (|DrU   rZ   c                P   | j         5  d}g }| j        D ][}|j        s|j        s|j        r/|                    t          |d d d d d d                      |dz  }F|                    |           \|r|| _        |                                  |cd d d            S # 1 swxY w Y   d S )Nr   rv  r   )rJ  rE  r`   rb   rd   r  r   rm  )ru   r;  new_entriesr   s       r*   reset_statuseszCredentialPool.reset_statuses  s*   Z 	 	EK . .$ .(< .@U .&&!(,+/,0.2/304  
 
 
 QJEE&&u----   +-	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   BBB"Bindexc                   | j         5  |dk     s|t          | j                  k    r	 d d d            d S | j                            |dz
            }d t	          | j                  D             | _        t          | j        d | j        D             |j        g           | j        |j        k    rd | _        |cd d d            S # 1 swxY w Y   d S )Nr   c                6    g | ]\  }}t          ||           S r  r  )r   new_priorityr   s      r*   rl  z/CredentialPool.remove_index.<locals>.<listcomp>  s9       'L% 555  rU   c                6    g | ]}|                                 S r   rk  r   s     r*   rl  z/CredentialPool.remove_index.<locals>.<listcomp>  s     <<<U<<<rU   rf  )	rJ  r  rE  r  rc  r"   rJ   rX   rF  )ru   rG  removeds      r*   remove_indexzCredentialPool.remove_index  sP   Z 	 	qyyEC$6$666	 	 	 	 	 	 	 	 m''	22G +4T]+C+C  DM "<<dm<<<$ZL   
 7:--#' 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s    CBCC
C
targetr
   ?Tuple[Optional[int], Optional[PooledCredential], Optional[str]]c                   t          |pd                                          sdS | j        5  t          | j        d          D ]#\  }}|j        k    r||d fc cd d d            S $fdt          | j        d          D             }t          |          dk    r)|d         d         |d         d         d fcd d d            S t          |          dk    rd d d dfcd d d            S                                 ret                    }d|cxk    rt          | j                  k    r"n n|| j        |dz
           d fcd d d            S d d d	| d
fcd d d            S d d d dfcd d d            S # 1 swxY w Y   d S )Nr   )NNzNo credential target provided.r   )startc                    g | ]J\  }}|j                                                                                                         k    F||fKS r   )rY   r   r   )r   rd  r   r   s      r*   rl  z1CredentialPool.resolve_target.<locals>.<listcomp>  sX       C;$$&&,,..#))++== e===rU   r   zAmbiguous credential label "z-". Use the numeric index or entry id instead.zNo credential #.zNo credential matching "z".)	rK   r   rJ  rc  rE  rX   r  isdigitrZ   )ru   rN  rd  r   label_matchesrG  r   s         @r*   resolve_targetzCredentialPool.resolve_target  s   &,B%%'' 	@??Z 	B 	B'Q??? , ,
U8s??t+++	B 	B 	B 	B 	B 	B 	B 	B"   "+DM"C"C"C  M
 =!!Q&&$Q'*M!,<Q,?E	B 	B 	B 	B 	B 	B 	B 	B =!!A%%T#t##t#t#tt	B 	B 	B 	B 	B 	B 	B 	B {{}} >C3333T]!3!333333 $-	":D@#	B 	B 	B 	B 	B 	B 	B 	B$ T#=U#=#=#==%	B 	B 	B 	B 	B 	B 	B 	B& A#AAAA'	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	Bs1   -E?*AE?	E?1AE?E?*E??FFc                    | j         5  t          |t          | j                            }| j                            |           |                                  |cd d d            S # 1 swxY w Y   d S )Nr   )rJ  r   r   rE  r  rm  r  s     r*   	add_entryzCredentialPool.add_entry  s    Z 	 	EN4=,I,IJJJEM  '''MMOOO		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   AA((A,/A,)rJ   rK   r   r   )r#   r   )r#   r   )r#   rX  )r`  rW   ra  rW   r#   r+  )rg  rh  r#   r+  )rn  rc   ro  rp   r#   r   rs   )
r   rW   rn  rc   r   r   rt  r   r#   rW   )r   rW   r#   rW   )r   rW   r#   r+  )r   rW   r  r   r#   rX  )r#   r   r   rW   r#   r   )r  r   r  r   r#   r   )r#   r+  )r  r   r#   rX  )rn  rc   r   r   r'  r^   r#   rX  )r2  r^   r#   r^   )r2  rK   r#   r+  )r'  r^   r#   rX  )r#   rZ   )rG  rZ   r#   rX  )rN  r
   r#   rO  )'r~   r   r   rO  rQ  rV  r   r]  r_  re  rm  rr  rz  r  r  r  r  r  r  r  r  r  r  r  r  rU  r  r  r&  r1  r9  r<  r?  rB  r>  rF  rM  rV  rX  r   rU   r*   r?  r?  F  s       = = = =' ' ' '3 3 3 3' ' ' '^ ^ ^ ^
, , , ,    >B 
 
 
 
 
 
@ @ @ @6 37	             D2 2 2 2h> > > >@8 8 8 8t% % % %NF F F FPqd qd qd qdf%< %< %< %<N
 
 
 
&Z Z Z Zx
# # # #J   0+ + + + ;@QV A A A A A AFV V V V 37 $ $ $ $ $ $L7 7 7 7 37&*Q Q Q Q Q Qf    >? ? ? ?8 8 8 8
 -18 8 8 8 8:      2   $B B B B4     rU   r?  r   c                |   g }t          |           D ]%\  }}|j        |k    r|                    |           &|r|d         nd }t          |dd                    r fdt          |           D             | d d <   ||                    dt          j                    j        d d                    |                    dt          |                      |                    d|	                    d          p|           |                     t                              ||                     dS | |         }i }	i }
d	 t          |          D             }|                                D ]d\  }}|d
v s||dk    r|j        r||v rt          ||          |k    r||	|<   8|t           v r#|j        	                    |          |k    r||
|<   e|	s|
r[|
ri |j        |
|	d<   t%          |fi |	}|| |<   t'                    p)|                                |                                k    S t'                    S )Nr   r   c                "    g | ]\  }}|v	|S r   r   )r   rd  r   duplicate_indicess      r*   rl  z!_upsert_entry.<locals>.<listcomp>  s(    aaa
UCO`D`D`eD`D`D`rU   rX   r   r[   rY   Tc                    h | ]	}|j         
S r   r   r   s     r*   r   z _upsert_entry.<locals>.<setcomp>  s    555qAF555rU   >   rX   r[   rq   )rc  r\   r  r  r   r   r   r   r   r{   rW   r   r   r   rY   r   rz   rq   r   r   r   )r   rJ   r\   r   matching_indicesrd  r   existing_idxexistingr  r  _field_namesr   r   ry  r\  s                  @r*   _upsert_entryrb    s   (( ) )
U<6!!##C(((*:D#A&&L,QRR011 baaaai.@.@aaa
4!1"1"!5666:~g'>'>???7GKK$8$8$BFCCC'11(GDDEEEt|$HMM55F8$4$4555Lmmoo 
+ 
+
U$$$'>>hn>,x%%..%*c"K~!!#&&%//%*c" R R 	I%H%H-%HM'"(44m44 ' %&&Q(*:*:*<*<@Q@Q*QQ!"""rU   c                `  	 | dk    rdS dddddd	t          d	 |D             d
           }t          d |D             	fd          }g ||}d t          |          D             }d}t          |          D ]1\  }}|j        |k    r!t          ||          |||j                 <   d}2|S )NrO   Fr   r   r         )zenv:ANTHROPIC_TOKENzenv:CLAUDE_CODE_OAUTH_TOKENr  r|  zenv:ANTHROPIC_API_KEYc              3  B   K   | ]}t          |j                  |V  d S rs   r   r\   r   s     r*   r   z-_normalize_pool_priorities.<locals>.<genexpr>  s2      GG5'8'F'FGGGGGGGrU   c                    | j         S rs   r   rB  s    r*   rC  z,_normalize_pool_priorities.<locals>.<lambda>  s    %. rU   rD  c              3  B   K   | ]}t          |j                  |V  d S rs   rg  r   s     r*   r   z-_normalize_pool_priorities.<locals>.<genexpr>  s2      KK5+<U\+J+JKKKKKKKrU   c                n                         | j        t                              | j        | j        fS rs   )r{   r\   r  r[   rY   )r   source_ranks    r*   rC  z,_normalize_pool_priorities.<locals>.<lambda>  s/    OOEL#k*:*:;;NK
 rU   c                $    i | ]\  }}|j         |S r   r  )r   rd  r   s      r*   r   z._normalize_pool_priorities.<locals>.<dictcomp>  s     DDD:33DDDrU   r   T)r  rc  r[   r   rX   )
rJ   r   manual_entriesseeded_entriesordered	id_to_idxchangedrJ  r   rk  s
            @r*   _normalize_pool_prioritiesrr    s   ;u  !'(!" K GGGGGG((  N KKGKKK
 
 
 
  N 100GDD71C1CDDDIG(11  e>\))+25<+P+P+PGIeh'(GNrU   Tuple[bool, Set[str]]c                  % d}t                      }t                      }	 ddlm} n# t          $ r d }Y nw xY w| dk    r	 ddlm}  |d          s||fS n# t          $ r Y nw xY wt                      %dP%fd
} |d          } |d          p
 |d          }	t          |o|	           }
|
r9d |D             }t          |          t          |          k    r	||d d <   d}||fS ddl	m
}m} d |            fd |            ffD ]\  }}|r|                    d          r || |          r)|                    |           |t          || ||t          |                    dd          |                    d          |                    d          t!          |                    dd          |          d          z  }	n| dk    rt#          |d          }t          t%          |t&                    okt)          |                    d          pd                                          p5t)          |                    d          pd                                                    }|r7|s5d |D             }t          |          t          |          k    r	||d d <   d}|rB|r? || d          s2|                    d           t)          |                    d          pd                                          }|p#t!          |                    dd          d          }|t          || di dddt          d|                    dd          d |                    d           d!|                    d!          d"|                    d"          d#|                    d#          d$|                    d$          d%|                    d%          d&|                    d&          d|                    d          d'|                    d'          d(|                    d(          d)|                    d)          d*|                    d*          d+|                    d+          d,|                    d,          |                    d-          t%          |                    d.          t&                    r|                    d.          nd |d/          z  }n| d0k    r	 dd1lm}m}  |            \  }}|r ||          \  }}d2|                                v rd3nd4| } || |          sV|                    |           t5          j        |           }|p
|r|j        nd}|t          || ||t8          |||d5          z  }n1# t:          $ r&}t<                              d6|           Y d }~nd }~ww xY w| d7k    r	 dd8lm }  |d9          }|                    d:d          }|r|                    dd;          } || |          sq|                    |           |t          || ||t          ||                    d<          |                    d=d          |                    d>|          d?          z  }n8# t:          $ r&}t<                              d@|           Y d }~nd }~ww xY w| dAk    r	 ddBlm!}  |dA          }|rP|                    d          r:dC} || |          s+|                    |           d }	 ddDl"m"}  |                    d!d          }!|!r7tG          | $                    |!          %                                dEz            }n# t:          $ r d }Y nw xY wt)          |                    d&d          pd          &                    dF          }"|t          || ||t          |d         |                    d           ||"|                    dd          p#t!          |                    dd          |          dG          z  }n# t:          $ r&}t<                              dH|           Y d }~nld }~ww xY w| dIk    rC || d          r||fS t#          |dI          }t%          |t&                    r|                    dJ          nd }#t%          |#t&                    r|#                    d          r|                    d           t)          |                    d          pd                                          }|t          || ddt          |#                    dd          |#                    d           dK|                    dL          |p#t!          |#                    dd          d          dM          z  }n| dNk    rt#          |dN          }t%          |t&                    r|                    dJ          nd }#t%          |#t&                    r|#                    d          rd} || |          r||fS |                    |           ddOlm'}$ |$}"|t          || ||t          |#                    dd          |#                    d           |"|                    dL          t!          |#                    dd          |          dM          z  }||fS )QNFr   is_source_suppressedc                    dS NFr   _p_ss     r*   _is_suppressedz-_seed_from_singletons.<locals>._is_suppressed+      5rU   rO   )!is_provider_explicitly_configuredr   rK   r#   c                v                         |           pt          | d          pd                                S r  )r{   _get_secretr   )r   	_env_files    r*   _env_valz'_seed_from_singletons.<locals>._env_valL  s5    MM#&&D+c2*>*>D"KKMMMrU   ANTHROPIC_API_KEYANTHROPIC_TOKENCLAUDE_CODE_OAUTH_TOKENc                $    g | ]}|j         d v|S )>   r|  r  r  r   s     r*   rl  z)_seed_from_singletons.<locals>.<listcomp>[  s0       <'EEE EEErU   T)r}  read_hermes_oauth_credentialsr  r|  r  r   r~  r  )r\   rM   r]   r_   rj   rY   r   r]   rm   c                $    g | ]}|j         d v|S )>   manual:device_coder  r  r   s     r*   rl  z)_seed_from_singletons.<locals>.<listcomp>  s0       <'LLL LLLrU   r  rY   r\   rM   r_   ri   rA   r>   r?   rE   rl   rn   rB   r@   rC   rH   rF   rI   r=   )rI   r=   rY   copilot)resolve_copilot_tokenget_copilot_api_tokenghgh_clienv:r\   rM   r]   rh   rY   zCopilot token seed failed: %sz
qwen-oauth) resolve_qwen_runtime_credentials)refresh_if_expiringr6   zqwen-clirj   rh   	auth_file)r\   rM   r]   rj   rh   rY   z Qwen OAuth token seed failed: %szminimax-oauth)get_provider_auth_stater5   )r   r  r  )r\   rM   r]   r_   rj   rh   rY   z#MiniMax OAuth token seed failed: %sr  r  z%https://chatgpt.com/backend-api/codexrk   )r\   rM   r]   r_   rh   rk   rY   r  )DEFAULT_XAI_OAUTH_BASE_URLr   rK   r#   rK   )(r  r   hermes_cli.authrv  ImportErrorr~  r   r   r  r  r}  r  r{   addrb  rR   r   r   rP   r   rK   r   hermes_cli.copilot_authr  r  r   r   rl   rS   r)   r7  r8  r  r  r   rZ   r   r   r  r  )&rJ   r   rq  active_sourcesr  r|  r~  r  anthropic_api_keyanthropic_oauth_envapi_key_path_explicitretainedr}  r  source_namer  r*  has_runtime_materialcustom_labelseeded_labelr  r  rL   r\   	api_tokenenterprise_base_urlpconfigeffective_base_urlr<  r  r  rj   _dtr   rh   r  r  r  s&                                        @r*   _seed_from_singletonsr  !  sb   G"uuN!##JJJJJJJJ   	 	 	 	 	 ;
	IIIIII44[AA /../ 	 	 	D	& JJ		N 	N 	N 	N 	N 	N %H%899H&''N884M+N+N 	 !%%6%R?R;R S S  	+ #*  H 8}}G,,%
N**gggggggg 99;;<88::;#
 	 	K  =11 !>(K88 "";///="-%4(-		-(D(D).>)B)B).;)?)?!1%))M22N2NP[!\!\ 	  	, 
V		$Z88#ud## EIIn--344::<< =uyy--344::<<	 
  
  	- 	 #*  H 8}}G,,%
 *	) *	..=2Y2Y *	}---
 uyy117R88>>@@L' +;		."--}, ,L }m #EIInb$A$A $UYY%?%?	
 !%))L"9"9 !%))L"9"9 UYYw//  ;!7!7 &uyy1B'C'C )%))4H*I*I  ;!7!7 +EII6L,M,M& "599]#;#;'( !%))L"9"9)* #EIIn$=$=+, +EII6L,M,M-. '		2D(E(E/0 .3YY7N-O-O/9%))E:J:JD/Q/Q[599U+++W[)5  	     GD 
Y			?\\\\\\\\1133ME6 1F1Fu1M1M.	.*.&,,..*@*@hhoVoo%~h<< "&&{333/3H==G *= *6=E222 ' } #&1):,5(:%+ 	    G  	? 	? 	?LL8#>>>>>>>>	? 
\	!	!	BHHHHHH44OOOEIIi,,E #ii*==%~h<< "&&{333} #&1)8,1-2YY-G-G(-		*b(A(A%*YY{K%H%H 	    G  	B 	B 	BLL;SAAAAAAAA	B 
_	$	$!	E??????++O<<E >22 %%~h<< "&&{333$(M-<<<<<<#iib99 [,/0A0A#0F0F0P0P0R0RUY0Y,Z,ZM$ - - -(,-"599-A2#F#F#L"MMTTUXYYH} #&1)8,1.,A-2YY-G-G-:(0%*YYw%;%; &?O %		." = ={@ @
 
	    G   	E 	E 	ELL>DDDDDDDD	E 
^	#	#
 >(M22 	+N**$Z@@(25$(?(?I8$$$T fd## 	

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

>(B(B 	 #F~h// /..v&&&BBBBBB1H}$!0$*JJ~r$B$B%+ZZ%@%@ ($)IIn$=$=-fjj.L.LfUU 	  G N""s   ( 88A 
A&%A&5B$V 
W%WWB=Z 
[Z??[A`5  A]6 5`5 6^`5 ^B.`5 5
a%?a  a%c                |   d}t                      }dd}	 ddlm} n# t          $ r d }Y nw xY wddt          dd fd}| dk    r_ |d          }|rNd} || |          r||fS |                    |           |t          || | ||d|t                              z  }||fS t          j	        |           }	|	r|	j
        t          k    r||fS d}
|	j        r# ||	j                                      d          }
t          |	j                  }| dk    rg d}|D ]} ||          }|sd| } || |          r"|                    |           |
p|	j        }| dk    rt!          ||	j        |
          }n| dk    rt#          ||	j        |
          }|t          || | |||||                    z  }||fS )!NFr   rK   r#   c                    t                      }|                    | d                                          }t          | d          pd                                }|                    d          r|r|S |p|S )Nr   zop://)r   r{   r   r  rQ   )r   env_filer   scoped_values       r*   _get_env_prefer_dotenvz._seed_from_env.<locals>._get_env_prefer_dotenvp	  sx    ::ll3##))++#C,,299;; >>'"" 	 | 	 "l"rU   r   ru  c                    dS rx  r   ry  s     r*   _is_source_suppressedz-_seed_from_env.<locals>._is_source_suppressed	  r}  rU   r  r^   c                    	 ddl m}  ||           }n# t          $ r d }Y nw xY w|r!t          |                                          nd S )Nr   )get_secret_source)hermes_cli.env_loaderr  r)   rK   r   )r  r  source_labels      r*   _secret_source_for_envz._seed_from_env.<locals>._secret_source_for_env	  ss    	 ??????,,W55LL 	  	  	 LLL	 ,8Bs<  &&(((dBs    ##)rM   r\   rL   rh   rM   rp   c                <    | ||||d} |          }|r||d<   |S )Nr  rD   r   )r\   r  rL   rh   rM   r   rD   r  s          r*   _env_payloadz$_seed_from_env.<locals>._env_payload	  sI     "! #
 #
 /.w77 	5'4GO$rU   
openrouterOPENROUTER_API_KEYzenv:OPENROUTER_API_KEY)r\   r  rL   rh   r   r  rO   )r  r  r  r  zkimi-codingzair  )r  rK   r#   r^   )r\   rK   r  rK   rL   rK   rh   rK   rM   rK   r#   rp   )r  r  rv  r  rS   r  rb  r   r   r{   rM   base_url_env_varr  r  api_key_env_varsrl   r   r   )rJ   r   rq  r  r  r  r  rL   r\   r  env_urlenv_varsr  rh   r  s                 @r*   _seed_from_envr  h	  s   G"uuN# # # #.QQQQQQQ   	 	 	 	 	C C C C +       ( <&&';<< 	-F$$Xv66 /..v&&&}!00	  	
 
 
G &&#H--G 'g'+<<<&&G O(()ABBII#NNG,--H;
 
 
  
 
&&w// 	!!!  622 	6"""8g8}$$-eW5OQXYYHH,UG4NPWXXH=L!	  	

 

 
	
 N""s    ..Tprune_env_sourcesr  Set[str]r  c                   d	fdfd| D             }t          |          t          |           k    rdS || d d <   dS )
Nr   rW   r#   r   c                    | j                             d          rS t          | j         | j                  p
| j         dk    S )Nr  r  )r\   rQ   r   rJ   )r   r  s    r*   _is_prunablez1_prune_stale_seeded_entries.<locals>._is_prunable	  sJ     <""6** 	%$$ *%,GG -|},	
rU   c                d    g | ],}t          |j                  s|j        v s |          *|-S r   rg  )r   r   r  r  s     r*   rl  z/_prune_stale_seeded_entries.<locals>.<listcomp> 
  sX       U\** <>))|E"" * 	 *))rU   FTrY  )r  )r   r  r  r  r  s    `` @r*   _prune_stale_seeded_entriesr  	  s    
 
 
 
 
 
       H 8}}G$$uGAAAJ4rU   c                   d}t                      }	 ddlm} n# t          $ r d }Y nw xY wt	          |           }|rt          |                    d          pd                                          }t          |                    d          pd                                                              d          }t          |                    d	          pd                                          }|rHd
| }	 || |	          s7|	                    |	           |t          || |	|	t          |||p|	d          z  }	 t                      }
|
r|
                    d          nd}t          |t                    rNt          |                    d          pd                                                                          }t          |                    d          pd                                                              d          }d}dD ]V}|                    |          }t          |t
                    r*|                                r|                                } nW|dk    r\|rZ|rXt!          |          }|| k    rCd}	 || |	          s5|	                    |	           |t          || |	|	t          ||dd          z  }n# t"          $ r Y nw xY w||fS )zJSeed a custom endpoint pool from custom_providers config and model config.Fr   ru  c                    dS rx  r   ry  s     r*   r|  z)_seed_custom_pool.<locals>._is_suppressed
  r}  rU   r6   r   rh   r  rw   zconfig:r  modelNrJ   )r6   apir#  model_config)r  r  rv  r  r  rK   r{   r   r  r  rb  rS   r+   rP   r   r   r  r)   )r  r   rq  r  r|  	cp_configr6   rh   rw   r\   r   	model_cfgmodel_providermodel_base_urlmodel_api_keyr   r   matched_keys                     r*   _seed_custom_poolr  
  sA   G"uuNJJJJJJJ   	 	 	 	 	
 ,H55I immI..4"55;;==y}}Z006B77==??FFsKK9==((.B//5577 	%t%%F!>(F33 ""6***="(%6(/$,!% 	   "$$+1;FJJw'''t	i&& 	 z!:!:!@bAAGGIIOOQQN z!:!:!@bAAGGIIPPQTUUNM'  MM!$$a%% !'')) $%GGIIME))n)):>JJ(**+F)>(F;; &**6222=#$"*0->0=,:)7 	$ $      N""s    ))=FK	 	
KKc                     pd                                                                  t                     }d |D             }t           fd|D                       } fd|D             }t           fd|D                       }|r\t	                                          d          }t          |t                    r|                               nd }t          |          } 	                    t                    r-t           |          \  }}	|p|p|}
|
t          ||	          z  }
nYt           |          \  }}t           |          \  }}|p|p|p|}
|
t          |||z  d          z  }
|
t           |          z  }
|
r;d	 |D             }t!           d
 t#          |d           D             ||z
             t%           |          S )Nr   c                    h | ]A}t          |t                    |                    d           ,|                    d           BS r  )rP   r   r{   r   s     r*   r   zload_pool.<locals>.<setcomp>Y
  sV       eT"" (-yy		$  rU   c              3  j   K   | ]-}t          |t                    ot          |          |k    V  .d S rs   )rP   r   r   r   r   rJ   s     r*   r   zload_pool.<locals>.<genexpr>^
  s[       ! !  	7D!! 	O0(CCwN! ! ! ! ! !rU   c                F    g | ]}t                               |          S r   )rW   r   r  s     r*   rl  zload_pool.<locals>.<listcomp>c
  s*    XXX))(G<<XXXrU   c           	   3     K   | ]s}t          |t                    oYt          |                    d           |                    dt                              |                    dt                    k    V  tdS )r]   rM   N)rP   r   rT   r{   rS   r  s     r*   r   zload_pool.<locals>.<genexpr>d
  s       ' '  	7D!! 	9%KK''KK%677
 
 [[&788	9' ' ' ' ' 'rU   credential_poolFr  c                    h | ]	}|j         
S r   r  r   s     r*   r   zload_pool.<locals>.<setcomp>
  s    11158111rU   c                6    g | ]}|                                 S r   rk  r   s     r*   rl  zload_pool.<locals>.<listcomp>
  s     ZZZU]]__ZZZrU   c                    | j         S rs   r   )r  s    r*   rC  zload_pool.<locals>.<lambda>
  s    4= rU   rD  rf  )r   r   r!   r  r   r{   rP   r   r   rQ   r  r  r  r  r  rr  r"   r  r?  )rJ   raw_entriesdisk_idsraw_needs_sanitizationr   raw_needs_auth_normalizationactive_poolactive_entriescustom_changedcustom_sourcesrq  singleton_changedr  env_changedenv_sourcesnew_idss   `               r*   	load_poolr  V
  s   B%%''--//H&x00K    H
 ! ! ! ! ! #! ! !  
 YXXXKXXXG#& ' ' ' ' #' ' ' $ $  $ < '((,,->??6@d6S6S]222Y]'+N';';$-.. A):8W)M)M&(Z,HZN.wGGG/DXw/W/W,,#1(G#D#D [" +  	 	 	.+#
 
 
 	

 	-h@@@ 
11111ZZ&>X>X*Y*Y*YZZZ 7*	
 	
 	
 	

 (G,,,rU   )r#   r$   )rJ   rK   rL   r
   rM   r
   r#   rK   )rL   rK   r   rK   r#   rK   )r   r   r#   rZ   )r\   rK   r#   r   )r   rc   r#   rZ   )r   r
   r#   ra   )r   rK   r#   ra   )r   r   r#   rp   )r   rW   r#   ra   )rw   rK   r#   rK   rs   )r   r$   )rh   r^   r  r^   r#   r^   )r#   r  )r  rK   r#   r   )rJ   rK   r#   rK   )r!  r
   rJ   r^   rh   r^   r#   r   )r)  rK   r*  rp   r#   r+  )
r   r   rJ   rK   r\   rK   r   rp   r#   r   )rJ   rK   r   r   r#   r   )rJ   rK   r   r   r#   rs  )r   r   r  r  r  r   r#   r   )r  rK   r   r   r#   rs  )rJ   rK   r#   r?  )h__doc__
__future__r   loggingr3  r:   rH  r   r   r   dataclassesr   r   r   r   r   pathlibr	   typingr
   r   r   r   r   r   hermes_constantsr   r(   r   agent.secret_scoper   r  agent.credential_persistencer   r   r  authr   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   	getLoggerr~   r7  r+   r  r   rw  	frozensetrq  r  rR   rS   r   SOURCE_MANUAL_DEVICE_CODEr  r"  r  r   r  r   r   r   r  r  rz   rT   rW   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r(  rL  r=  r?  rb  rr  r  r  r  r  r  r   rU   r*   <module>r     s   B B " " " " " "  				        				 2 2 2 2 2 2 2 2 2 2 ' ' ' ' ' ' ' '       8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0 0 0 & & & & & & 8 8 8 8 8 8        # " " " " "                               " 
	8	$	$   ( 	  
 # $ $ $   ( !-  ,::: " $ " 	  # #  '  -1 )
   i     / / / / g g g g g g g gT   F F F FU U U U
) ) ) )   <   .   2   2 2 2 2
7 7 7 7 74    <         ( #	#D #D #D #D #D #DL )* %4
 4
 4
 4
nI I I I I I I IX,*# *# *# *#Z   DD# D# D# D#N
# # # #L #	           FF# F# F# F#R?- ?- ?- ?- ?- ?-rU   