Ë
    áß²jÏ? ã                  óØ  — U d Z ddlmZ ddlmZ ddlmZ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mZ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+ ddl,m-Z-m.Z. ddl/m0Z0m1Z1m2Z2 ddl3m4Z5 ddl3m6Z6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z<m=Z=m>Z>m?Z?m@Z@mAZAmBZBmCZCmDZDmEZEmFZFmGZG  ej�                  eI«      ZJdŒd„ZKd�d„ZLdZMdZNdZO ePh d£«      ZQdZRdZSdZTdZUdZVeV› d�ZWdZXdZYd ZZd!Z[eXeYeZe[hZ\d"Z]d#Z^d#Z_d$Z`d%Zad&Zbd'Zcd(Zd ePh d)£«      Zed*Zfddddddd+œZgd,ehd-<   i eg¥d.eMi¥Zid,ehd/<   dŽd0„Zje G d1„ d2«      «       Zkd�d3„Zld�d4„Zmd‘d5„Znd’d6„Zod“d7„Zpd8dd9œ	 	 	 	 	 	 	 d”d:„Zqd•d;„Zrd–d<„Zsd—d=„Ztd8d>œd˜d?„Zud™d@„ZvdšdA„Zwd›dœdB„Zxd�dC„ZydždD„ZzdŸdE„Z{	 d›	 	 	 	 	 d dF„Z|d›d¡dG„Z}d¢dH„Z~d£dI„Zd¤dJ„Z€	 	 	 	 	 	 	 	 d¥dK„Z�	 	 	 	 	 	 	 	 d¦dL„Z‚d§dM„ZƒddNœ	 	 	 	 	 	 	 d¨dO„Z„d©dP„Z…dQZ†dªdR„Z‡	 	 	 	 	 	 d«dS„Zˆd¬dT„Z‰d­dU„ZŠd®dV„Z‹ddWœ	 	 	 	 	 	 	 	 	 d¯dX„ZŒdddYœ	 	 	 	 	 	 	 	 	 d°dZ„Z�d[d\d]œZŽd^ehd_<    ePd`daheŽ£«      Z�dbZ�dcddd]œZ‘dedfdfdfdgœZ’ G dh„ die“«      Z” G dj„ dkee«      Z•d±dl„Z–ddQdmdndodpœZ—d²dq„Z˜d³ds„Z™ G dt„ du«      Zšd´dv„Z›dµdw„Zœ e�«       Zždrehdx<   d¶dy„ZŸd·dz„Z d´d{„Z¡d´d|„Z¢d´d}„Z£dµd~„Z¤d¸d„Z¥d¹d€„Z¦ e�«       Z§drehd�<   dºd‚„Z¨d»dƒ„Z©e@eAd„œZªd¼d…„Z«d¸d†„Z¬d‡dˆœ	 	 	 	 	 	 	 d½d‰„Z­d¾dŠ„Z®d¿d‹„Z¯y)Àz<Persistent multi-credential pool for same-provider failover.é    )Úannotations)ÚCredentialPoolAdminMixin)Ú CredentialPoolModelCooldownMixinÚmodel_cooldown_untilN)Ú	dataclassÚfieldsÚreplace)ÚdatetimeÚtimezone)ÚPath)ÚAnyÚCallableÚDictÚIterableÚListÚOptionalÚSetÚTuple)ÚOPENROUTER_BASE_URL)Úload_env)Ú
get_secretÚget_secret_str)Úreset_delay_from_message)Úplugin_refresh_hook)Úapply_plugin_refresh_resultÚrecover_failed_plugin_refresh)Úfingerprint_secret_valueÚis_borrowed_credential_sourceÚ$sanitize_borrowed_credential_payload)Ú'CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDSÚPROVIDER_REGISTRYÚ!SINGLE_USE_REFRESH_POOL_PROVIDERSÚ_auth_store_lockÚ_codex_access_token_is_expiringÚ_decode_jwt_claimsÚ_global_auth_file_pathÚ_load_auth_storeÚ_load_provider_stateÚ _load_provider_state_with_sourceÚ_resolve_kimi_base_urlÚ_resolve_zai_base_urlÚ
_same_pathÚ_save_auth_storeÚ_save_provider_stateÚ_store_provider_stateÚread_credential_poolÚwrite_credential_poolc                 ó<   — 	 ddl m}   | «       S # t        $ r Y yw xY w)a  Load config.yaml read-only, returning None on any error.

    ``load_config_readonly()`` skips the deepcopy ``load_config()`` pays per
    call; the picker calls ``load_pool()`` once per provider row, which made
    that copy the dominant cost of ``model.options``.
    r   ©Úload_config_readonlyN)Úhermes_cli.configr4   Ú	Exceptionr3   s    ú:/home/ubuntu/.hermes/hermes-agent/agent/credential_pool.pyÚ_load_config_safer8   8   s%   € ðÝ:á#Ó%Ð%øÜò Ùðús   ‚ �	šc                 ó8   — 	 ddl m}  | S # t        $ r d„ cY S w xY w)zO``hermes_cli.auth.is_source_suppressed`` (late-bound), or an always-False stub.r   ©Úis_source_suppressedc                 ó   — y©NF© )Ú_pÚ_ss     r7   ú<lambda>z*_is_source_suppressed_fn.<locals>.<lambda>M   s   � ó    )Úhermes_cli.authr;   ÚImportErrorr:   s    r7   Ú_is_source_suppressed_fnrE   G   s%   € ð$Ý8Ø#Ð#øÜò $Ù#Ò#ð$ús   ‚
 Š˜ÚokÚ	exhaustedÚdead>   Úinvalid_grantÚinvalid_tokenÚtoken_revokedÚtoken_invalidatedÚunauthorized_clientÚrefresh_token_reusedÚcredential_persist_failedi€Q ÚoauthÚapi_keyÚmanualz:device_codeÚ
fill_firstÚround_robinÚrandomÚ
least_usedi,  i  é<   ÚbillingÚbilling_unverifiedg      N@zcustom:>   ÚtlsÚscopeÚorg_idÚuser_idÚ	client_idÚ
anon_tokenÚ
expires_inÚ
token_typeÚauth_methodÚobtained_atÚaccount_tierÚagent_key_idÚsecret_sourceÚfailure_reasonÚportal_base_urlÚagent_key_reusedÚsecret_fingerprintÚagent_key_expires_inÚagent_key_obtained_at)rc   r`   re   rk   ri   rl   rb   rd   r_   r]   r\   )Úlast_statusÚlast_status_atÚlast_error_codeÚlast_error_reasonÚlast_error_messageÚlast_error_reset_atúDict[str, Any]Ú_CLEAR_STATUSrm   Ú_MARK_OKc                ó€   — | dk(  r't        |t        «      r|j                  d«      rt        S t        |xs t        «      S )zHInfer pool auth metadata for token formats with one unambiguous meaning.Ú	anthropicz
sk-ant-oat)Ú
isinstanceÚstrÚ
startswithÚAUTH_TYPE_OAUTHÚAUTH_TYPE_API_KEY)ÚproviderÚtokenÚ	auth_types      r7   Ú_normalize_pool_auth_typer€   Ê   s7   € à�;Ò¤:¨e´SÔ#9¸e×>NÑ>NÈ|Ô>\ÜÐÜˆyÒ-Ô-Ó.Ð.rB   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ed <   d
Zd!ed"<   d#„ Zd)d$„Zed*d%„«       Zd+d&„Zed,d'„«       Zed-d(„«       Zy
).ÚPooledCredentialry   r}   ÚidÚlabelr   ÚintÚpriorityÚsourceÚaccess_tokenNúOptional[str]Úrefresh_tokenrm   úOptional[float]rn   úOptional[int]ro   rp   rq   rr   Ústatus_cleared_atÚbase_urlÚ
expires_atÚexpires_at_msÚlast_refreshÚinference_base_urlÚ	agent_keyÚagent_key_expires_atr   Úrequest_countzOptional[Dict[str, float]]Úmodel_cooldownsrs   Úextrac                óŠ   — | j                   €i | _         t        | j                  | j                  | j                  «      | _        y ©N)r—   r€   r}   rˆ   r   ©Úselfs    r7   Ú__post_init__zPooledCredential.__post_init__ò   s4   € Ø�:‰:ÐØˆDŒJÜ2°4·=±=À$×BSÑBSÐUY×UcÑUcÓdˆ�rB   c                ó�   — |t         v r| j                  j                  |«      S t        dt	        | «      j
                  › d|›�«      ‚)Nú'z' object has no attribute )Ú_EXTRA_KEYSr—   ÚgetÚAttributeErrorÚtypeÚ__name__)r›   Únames     r7   Ú__getattr__zPooledCredential.__getattr__÷   sB   € Ø”;ÑØ—:‘:—>‘> $Ó'Ð'Ü˜q¤ d£×!4Ñ!4Ð 5Ð5OÐPTÈxÐXÓYÐYrB   c                óò  — t        | «      D �ch c]  }|j                  dk7  sŒ|j                  ’Œ  }}|D �ci c]  }||v sŒ||j                  |«      “Œ }}t        |j                  d«      t        «      rt        |d   «      |d<   |j                  «       D ��ci c]  \  }}||vsŒ|dk7  sŒ|€Œ||“Œ c}}|d<   |j                  dt        j                  «       j                  d d «       |j                  d|j                  d|«      «       |j                  dt        «       |j                  d	d
«       |j                  dt        «       |j                  dd«        | dd|i|¤ŽS c c}w c c}w c c}}w )Nr}   rn   r—   rƒ   é   r„   r‡   r   r†   r   rˆ   Ú r>   )r   r¤   r    rx   ry   Ú_parse_absolute_timestampÚitemsÚ
setdefaultÚuuidÚuuid4Úhexr|   ÚSOURCE_MANUAL)Úclsr}   ÚpayloadÚfÚfield_namesÚkÚdataÚvs           r7   Ú	from_dictzPooledCredential.from_dictü   sJ  € ä'-¨c£{ÖK !°a·f±fÀ
Ó6J�q—v“vÐKˆÐKØ+6ÖG a¸!¸wº,��7—;‘;˜q“>Ñ!ÐGˆÐGä�d—h‘hÐ/Ó0´#Ô6Ü%>¸tÐDTÑ?UÓ%VˆDÐ!Ñ"ð %Ÿ]™]›_÷
Ù�Q˜°¸Ò0DÈÈjËÐ]^Ñ]jˆAˆq‰Dó
ˆˆW‰ð 	�‰˜œdŸj™j›l×.Ñ.¨r°Ð2Ô3Ø�‰˜ §¡¨X°xÓ!@ÔAØ�‰˜Ô%6Ô7Ø�‰˜
 AÔ&Ø�‰˜¤-Ô0Ø�‰˜¨Ô+ÙÑ-˜HÐ-¨Ñ-Ð-ùò' LùÚGùó
s-   ŽE)£E)·	E.ÁE.ÂE3Â'E3Â-E3Â0E3c                ó8  — i }t        | «      D ]K  }|j                  dv rŒt        | |j                  «      }|€|j                  t        v sŒ=|||j                  <   ŒM | j                  j                  «       D ]  \  }}|€Œ	|||<   Œ t        || j                  «      S )N>   r—   r}   )r   r¤   Úgetattrrt   r—   rª   r   r}   )r›   ÚresultÚ	field_defÚvaluer´   r¶   s         r7   Úto_dictzPooledCredential.to_dict  sœ   € Ø!#ˆÜ ›ò 	/ˆIØ�~‰~Ð!6Ñ6ØÜ˜D )§.¡.Ó1ˆEØÐ  I§N¡N´mÒ$CØ).��y—~‘~Ò&ð	/ð —J‘J×$Ñ$Ó&ò 	‰DˆAˆqØ‰}Ø��q’	ð	ô 4°F¸D¿M¹MÓJÐJrB   c           	     ór  — | j                   dk(  r�| j                  | j                  f| j                  | j                  ffD ]\  \  }}t        |t        «      sŒ|j                  «       sŒ(t        j                  |t        | dd «      |¬«      sŒL|j                  «       c S  yt        | j                  xs d«      S )NÚnousr[   )r[   r�   r¨   )r}   r“   r”   rˆ   r�   rx   ry   ÚstripÚauth_modÚ_nous_invoke_jwt_is_usabler¹   )r›   r~   r�   s      r7   Úruntime_api_keyz PooledCredential.runtime_api_key   s¦   € à�=‰=˜FÒ"ð —‘ ×!:Ñ!:Ð;Ø×"Ñ" D§O¡OÐ4ð&ò )Ñ!��zô
 ˜u¤cÕ*ØŸ™�Ü ×;Ñ;Ø¤W¨T°7¸DÓ%AÈjöð !Ÿ;™;›=Ò(ð)ð Ü�4×$Ñ$Ò*¨Ó+Ð+rB   c                óø   — | j                   dk(  r| j                  xs | j                  S | j                   dk(  r7t        dd«      j	                  «       j                  d«      xs | j                  S | j                  S )Nr¿   úopenai-codexÚHERMES_CODEX_BASE_URLr¨   ú/)r}   r’   rŽ   r   rÀ   Úrstriprš   s    r7   Úruntime_base_urlz!PooledCredential.runtime_base_url4  si   € à�=‰=˜FÒ"Ø×*Ñ*Ò;¨d¯m©mÐ;Ø�=‰=˜NÒ*ô "Ð"9¸2Ó>×DÑDÓF×MÑMÈcÓRÒcÐVZ×VcÑVcÐcØ�}‰}ÐrB   )r¤   ry   )r}   ry   r±   rs   Úreturnú'PooledCredential')rÊ   rs   )rÊ   ry   )rÊ   r‰   )r£   Ú
__module__Ú__qualname__Ú__annotations__rŠ   rm   rn   ro   rp   rq   rr   r�   rŽ   r�   r�   r‘   r’   r“   r”   r•   r–   r—   rœ   r¥   Úclassmethodr·   r½   ÚpropertyrÃ   rÉ   r>   rB   r7   r‚   r‚   Ñ   s3  … àƒMØƒGØƒJØƒNØƒMØƒKØÓØ#'€M�=Ó'Ø!%€K�Ó%Ø&*€N�OÓ*Ø%)€O�]Ó)Ø'+Ð�}Ó+Ø(,Ð˜Ó,Ø+/Ð˜Ó/ð *.Ð�Ó-Ø"€HˆmÓ"Ø $€J�Ó$Ø#'€M�=Ó'Ø"&€L�-Ó&Ø(,Ð˜Ó,Ø#€Iˆ}Ó#Ø*.Ð˜-Ó.Ø€M�3Óð 37€OÐ/Ó6Ø €Eˆ>Ó òeó
Zð
 ò.ó ð.ó,Kð ò,ó ð,ð& òó ñrB   r‚   c                ó²   — t        | «      }dD ]F  }|j                  |«      }t        |t        «      sŒ%|j	                  «       sŒ6|j	                  «       c S  |S )N)ÚemailÚpreferred_usernameÚupn)r%   r    rx   ry   rÀ   )r~   ÚfallbackÚclaimsÚkeyr¼   s        r7   Úlabel_from_tokenrØ   @  sM   € Ü Ó&€FØ5ò !ˆØ—
‘
˜3“ˆÜ�eœSÕ! e§k¡k¥mØ—;‘;“=Ò ð!ð €OrB   c                ó¬  — t        | «      }t        |t        «      r|j                  d«      nd}t        |t        «      r|j                  d«      nd}t        |t        «      r|j                  d«      nd}t        |t        «      r0|j                  «       r t        |t        «      r|j                  «       sy|j                  «       |j                  «       fS )až  ``(chatgpt_account_id, sub)`` of a Codex access token, or None when either claim is missing.

    Decoded without signature verification: this only decides whether two credentials Hermes
    already holds belong to the same principal, never whether a token is valid. Both claims are
    required because members of one ChatGPT workspace share ``chatgpt_account_id`` yet have their
    own subjects and quotas.
    zhttps://api.openai.com/authNÚchatgpt_account_idÚsub)r%   rx   Údictr    ry   rÀ   )rˆ   rÖ   Úauth_claimsÚ
account_idÚsubjects        r7   Ú_codex_principal_identityrà   I  s§   € ô   Ó-€FÜ?IÈ&ÔRVÔ?W�&—*‘*Ð:Ô;Ð]a€KÜ:DÀ[ÔRVÔ:W�—‘Ð!5Ô6Ð]a€JÜ#-¨f´dÔ#;ˆf�j‰j˜ÔÀ€GÜ�z¤3Ô'¨J×,<Ñ,<Ô,>Ä:ÈgÔWZÔC[Ð`g×`mÑ`mÔ`oØØ×ÑÓ˜wŸ}™}›Ð.Ð.rB   c                óº   — | j                   dk(  ry| j                   t        k7  ryt        | j                  «      }|duxr |t        |j	                  d«      «      k(  S )uë  Whether a Codex pool entry may adopt the auth.json singleton's token pair.

    ``device_code`` IS the singleton. ``manual:device_code`` is ambiguous: a legacy alias of the
    singleton (same account, must follow its rotations) or an independent account added with
    ``hermes auth add openai-codex`` (must never be overwritten â€” adopting turned two logins into
    one account, both hitting the same usage limit). Same principal proves the alias; unknown
    identity fails closed.
    Údevice_codeTFNrˆ   )r‡   ÚSOURCE_MANUAL_DEVICE_CODErà   rˆ   r    )ÚentryÚsingleton_tokensÚentry_identitys      r7   Ú_codex_entry_tracks_singletonrç   Z  s[   € ð ‡|�|�}Ò$ØØ‡|�|Ô0Ò0ØÜ.¨u×/AÑ/AÓB€NØ Ð%Ò{¨.Ô<UÐVf×VjÑVjÐkyÓVzÓ<{Ñ*{Ð{rB   c                ó0   — t        d„ | D «       d¬«      dz   S )Nc              3  ó4   K  — | ]  }|j                   –— Œ y ­wr™   ©r†   ©Ú.0rä   s     r7   ú	<genexpr>z!_next_priority.<locals>.<genexpr>l  s   è ø€ Ò4 5�—•Ñ4ùs   ‚éÿÿÿÿ)Údefaulté   )Úmax)Úentriess    r7   Ú_next_priorityró   k  s   € ÜÑ4¨GÔ4¸bÔAÀAÑEÐErB   c                óŒ   — | xs dj                  «       j                  «       }|t        k(  xs |j                  t        › d�«      S )Nr¨   ú:)rÀ   Úlowerr¯   rz   )r‡   Ú
normalizeds     r7   Ú_is_manual_sourcerø   o  s@   € Ø’,˜B×%Ñ%Ó'×-Ñ-Ó/€JØœÑ&ÒT¨*×*?Ñ*?Ä=À/ÐQRÐ@SÓ*TÐTrB   F©Úsole_credentialrg   c               óÂ   — | dk(  rt         S | dk(  rt        nt        }|t        k(  r| dk7  rt	        |t
        «      S | dk(  xs	 |t        k(  }|r|st	        |t
        «      S |S )aÞ  Return cooldown seconds based on the HTTP status that caused exhaustion.

    *sole_credential*: the pool has nothing to rotate to, so transient
    throttles (429 and the catch-all default covering 403/5xx/unknown) are
    capped to a brief cooldown; 401 keeps its own already-short TTL.

    *failure_reason* is the classifier verdict: an OpenRouter ``key limit
    exceeded`` and an xAI spending block both arrive as 403 but are billing,
    and a 60s retry on a spent account just re-fails. Billing keeps the full
    bench regardless of status; 402 is billing by definition.
    Unverified billing (#82154) gets the short cooldown regardless of pool
    size (the credential may be healthy), unless the status is a true 402.
    é‘  i­  i’  )ÚEXHAUSTED_TTL_401_SECONDSÚEXHAUSTED_TTL_429_SECONDSÚEXHAUSTED_TTL_DEFAULT_SECONDSÚ!FAILURE_REASON_BILLING_UNVERIFIEDÚminÚ%EXHAUSTED_TTL_SOLE_CREDENTIAL_SECONDSÚFAILURE_REASON_BILLING)Ú
error_coderú   rg   ÚbaseÚ
is_billings        r7   Ú_exhausted_ttlr  t  sm   € ð& �SÒÜ(Ð(Ø(2°cÒ(9Õ$Ô?\€DØÔ:Ò:¸zÈSÒ?PÜ�4Ô>Ó?Ð?Ø˜sÑ"ÒN nÔ8NÑ&N€JÙ™zÜ�4Ô>Ó?Ð?Ø€KrB   c                ó”  — | �| dk(  ryt        | t        t        f«      rt        | «      }|dk  ry|dkD  r|dz  S |S t        | t        «      r+| j	                  «       }|sy	 t        |«      }|dkD  r|dz  S |S y# t
        $ r Y nw xY w	 t        j                  |j                  dd«      «      j                  «       S # t
        $ r Y yw xY w)zLBest-effort parse of epoch seconds / epoch ms / ISO-8601 into epoch seconds.Nr¨   r   l    J)£g     @�@ÚZz+00:00)
rx   r…   Úfloatry   rÀ   Ú
ValueErrorr
   Úfromisoformatr	   Ú	timestamp)r¼   ÚnumericÚraws      r7   r©   r©   ’  sß   € à€}˜ šØÜ�%œ#œu˜Ô&Ü˜“,ˆØ�aŠ<ØØ#*Ð->Ò#>ˆw˜ÑÐKÀGÐKÜ�%œÔØ�k‰k‹mˆÙØð	Ü˜C“jˆGØ'.Ð1BÒ'B�7˜VÑ#ÐOÈÐOð øô ò 	Ùð	úð	Ü×)Ñ)¨#¯+©+°c¸8Ó*DÓE×OÑOÓQÐQøÜò 	Ùð	ús*   Á A8 Á5A8 Á8	BÂBÂ2B; Â;	CÃCc                óš   — t        |j                  «      }|€yt        t        | t        «      r| j	                  d«      nd«      }|€y||k  S )aD  True only when the auth.json singleton is PROVABLY older than *entry*.

    Both sides stamp ``last_refresh`` on every successful rotation. When
    either side lacks a parseable stamp this returns False (cannot prove),
    which keeps the historical adopt-on-difference behavior (#70111) intact
    for legacy writers.
    NFr‘   )r©   r‘   rx   rÜ   r    )Ústaterä   Úentry_tsÚstate_tss       r7   Ú_singleton_predates_entryr  «  sR   € ô )¨×);Ñ);Ó<€HØÐØÜ(ÄjÐQVÔX\ÔF]¨¯©°>Ô)BÐcgÓh€HØÐØØ�hÑÐrB   c                óè  — t        | t        «      si S i }dD ]H  }| j                  |«      }t        |t        «      sŒ%|j	                  «       sŒ6|j	                  «       ||<   ŒJ | j                  d«      xs$ | j                  d«      xs | j                  d«      }t        |«      }| j                  d«      }|€4t        |t        «      r$t        |«      }|�t        j                  «       |z   }|�||d<   |S )N©ÚreasonÚmessageÚreset_atÚ	resets_atÚretry_untilr  )rx   rÜ   r    ry   rÀ   r©   r   Útime)Úerror_contextr÷   r×   r¼   r  Úparsed_reset_atr  Úretry_delay_secondss           r7   Ú_normalize_error_contextr   ¼  sô   € Ü�m¤TÔ*Øˆ	Ø!#€JØ$ò ,ˆØ×!Ñ! #Ó&ˆÜ�eœSÕ! e§k¡k¥mØ#Ÿk™k›mˆJ�sŠOð,ð
 	×Ñ˜*Ó%ò 	,Ø×Ñ˜[Ó)ò	,à×Ñ˜]Ó+ð ô
 0°Ó9€OØ×Ñ 	Ó*€GØÐ¤:¨g´sÔ#;Ü6°wÓ?ÐØÐ*Ü"Ÿi™i›kÐ,?Ñ?ˆOØÐ"Ø!0ˆ
�:ÑØÐrB   ©rú   c               óÔ   — | j                   t        k7  ry t        | j                  «      }|�|S | j                  r/| j                  t        | j                  || j                  ¬«      z   S y )Nrù   )rm   ÚSTATUS_EXHAUSTEDr©   rr   rn   r  ro   rg   )rä   rú   r  s      r7   Ú_exhausted_untilr$  Ô  sl   € Ø×ÑÔ,Ò,ØÜ(¨×)BÑ)BÓC€HØÐØˆØ×ÒØ×#Ñ#¤nØ×!Ñ!Ø+Ø ×/Ñ/ô'
ñ 
ð 	
ð
 rB   c                ó^   — | j                  «       j                  «       j                  dd«      S )z>Normalize a custom provider name for use as a pool key suffix.ú ú-)rÀ   rö   r	   )r¤   s    r7   Ú_normalize_custom_pool_namer(  æ  s$   € à�:‰:‹<×ÑÓ×'Ñ'¨¨SÓ1Ð1rB   c                óZ   — t        | xs d«      j                  «       j                  d«      S )Nr¨   rÇ   )ry   rÀ   rÈ   )Úurls    r7   Ú	_norm_urlr+  ë  s$   € ÜˆsŠy�b‹>×ÑÓ!×(Ñ(¨Ó-Ð-rB   c              #  ó  K  — | €
t        «       } | €y	 ddlm}  || «      }|xs dD ]E  }t	        |t
        «      sŒt	        |j                  d«      t        «      sŒ4t        |d   «      |f–— ŒG y# t        $ r Y yw xY w­w)zOYield ``(normalized_name, entry)`` from the merged custom-provider config view.Nr   )Úget_compatible_custom_providersr>   r¤   )	r8   r5   r-  r6   rx   rÜ   r    ry   r(  )Úconfigr-  Úcustom_providersrä   s       r7   Ú_iter_custom_providersr0  ï  s‰   è ø€ à€~Ü"Ó$ˆØ€~ØðÝEá:¸6ÓBÐð "Ò' Rò DˆÜ�eœTÕ"¤z°%·)±)¸FÓ2CÄSÕ'IÜ-¨e°F©mÓ<¸eÐCÓCñDøô ò Ùðüs2   ‚A?“A0 ¡A?»A?ÁA?Á0	A<Á9A?Á;A<Á<A?c                ó€   — | h}t        t        |j                  d«      xs d«      «      }|r|j                  |«       |S )NÚprovider_keyr¨   )r(  ry   r    Úadd)Ú	norm_namerä   Úaliasesr2  s       r7   Ú_custom_entry_name_aliasesr6     s;   € Øˆk€GÜ.¬s°5·9±9¸^Ó3LÒ3RÐPRÓ/SÓT€LÙØ�‰�LÔ!Ø€NrB   c                óÀ   — t        | «      }|r|hn	t        «       }|j                  t        «      r.t        |t	        t        «      d  «      }|r|j                  |«       |S r™   )r(  Úsetrz   ÚCUSTOM_POOL_PREFIXÚlenr3  )Úprovider_namer÷   r5  Úsuffixs       r7   Ú_requested_custom_name_aliasesr=    sT   € Ü,¨]Ó;€JÙ(ˆz‰l¬c«e€GØ×ÑÔ/Ô0Ü,¨Z¼Ô<NÓ8OÐ8PÐ-QÓRˆÙØ�K‰K˜ÔØ€NrB   c                óð   — g }t        t        |j                  d«      xs d«      «      }|| r
t        › | › �ndfD ]9  }|j	                  «       j                  «       }|sŒ$||vsŒ)|j                  |«       Œ; |S )zFDurable ``providers.<key>`` slug first, then legacy ``custom:<name>``.r2  r¨   )r(  ry   r    r9  rÀ   rö   Úappend)r4  rä   Úkeysr2  r×   r÷   s         r7   Ú_pool_keys_for_custom_entryrA    sz   € à€DÜ.¬s°5·9±9¸^Ó3LÒ3RÐPRÓ/SÓT€LØÁIÔ!3Ð 4°Y°KÑ@ÐSUÐVò $ˆØ—Y‘Y“[×&Ñ&Ó(ˆ
Ú˜*¨DÒ0Ø�K‰K˜
Õ#ð$ð €KrB   c                ó8  — | sg S t        | «      }|rt        |«      n	t        «       }|r/t        «       D ]"  \  }}|t	        ||«      z  sŒt        ||«      c S  t        «       D ]5  \  }}t        |j                  d«      «      }|sŒ#||k(  sŒ)t        ||«      c S  g S )a†  Return pool keys to try for a custom endpoint.

    ``hermes auth add <key>`` stores ``providers.<key>`` credentials under the
    durable config slug; older rows and legacy ``custom_providers:`` entries
    live under ``custom:<display-name>``. Try the slug first, then the legacy
    namespace, so a populated pool is not skipped in favour of the
    ``no-key-required`` placeholder.
    rŽ   )r+  r=  r8  r0  r6  rA  r    )rŽ   r;  Únormalized_urlÚrequested_aliasesr4  rä   Ú	entry_urls          r7   Ú#custom_provider_pool_key_candidatesrF    s¯   € ñ Øˆ	Ü˜xÓ(€NÙIVÔ6°}ÔEÔ\_Ó\aÐáÜ 6Ó 8ò 	EÑˆI�uØ Ô#=¸iÈÓ#OÓOÜ2°9¸eÓDÒDð	Eô 3Ó4ò AÑˆ	�5Ü˜eŸi™i¨
Ó3Ó4ˆ	Ú˜ nÓ4Ü.¨y¸%Ó@Ò@ðAð €IrB   c                ó,   — t        | |«      }|r|d   S dS )zÍPreferred pool key for a custom provider: durable slug, else ``custom:<name>``.

    When provider_name is given, match by name first so two custom providers
    sharing a base_url keep separate keys.
    r   N)rF  )rŽ   r;  Ú
candidatess      r7   Úget_custom_provider_pool_keyrI  :  s!   € ô 5°X¸}ÓM€JÙ&ˆ:�a‰=Ð0¨DÐ0rB   c                 óB   ‡ — t        d«      Š t        ˆ fd„‰ D «       «      S )z?Return all 'custom:*' pool keys that have entries in auth.json.Nc              3  ó”   •K  — | ]?  }|j                  t        «      r(t        ‰j                  |«      t        «      r	‰|   r|–— ŒA y ­wr™   )rz   r9  rx   r    Úlist)rì   r×   Ú	pool_datas     €r7   rí   z-list_custom_pool_providers.<locals>.<genexpr>G  s@   øè ø€ ò ØØ�>‰>Ô,Ô-Ü�y—}‘} SÓ)¬4Ô0Ø�cŠNô 	ñùs   ƒAA)r0   Úsorted)rM  s   @r7   Úlist_custom_pool_providersrO  D  s'   ø€ ä$ TÓ*€IÜó Ø ôó ð rB   c                óŽ   ‡— | j                  t        «      sy| t        t        «      d Št        ˆfd„t	        «       D «       d«      S )zWReturn the custom_providers config entry matching a pool key like 'custom:together.ai'.Nc              3  ó4   •K  — | ]  \  }}|‰k(  sŒ|–— Œ y ­wr™   r>   )rì   r4  rä   r<  s      €r7   rí   z._get_custom_provider_config.<locals>.<genexpr>T  s   øè ø€ Ò_Ñ+˜9 eÈ9ÐX^ÓK^”Ñ_ùó   ƒ‘)rz   r9  r:  Únextr0  )Úpool_keyr<  s    @r7   Ú_get_custom_provider_configrU  O  s@   ø€ à×ÑÔ1Ô2ØØ”cÔ,Ó-Ð.Ð/€FÜÓ_Ô/EÓ/GÔ_ÐaeÓfÐfrB   c                ó  — t        «       }|r|j                  d«      nd}t        |t        «      st        S t        |j                  | d«      xs d«      j                  «       j                  «       }|t        v r|S t        S )z8Return the configured selection strategy for a provider.Úcredential_pool_strategiesNr¨   )	r8   r    rx   rÜ   ÚSTRATEGY_FILL_FIRSTry   rÀ   rö   ÚSUPPORTED_POOL_STRATEGIES)r}   r.  Ú
strategiesÚstrategys       r7   Úget_pool_strategyr\  W  sm   € äÓ €FÙ=C�—‘Ð8Ô9È€JÜ�j¤$Ô'Ü"Ð"Ü�:—>‘> (¨BÓ/Ò5°2Ó6×<Ñ<Ó>×DÑDÓF€HØÔ#<Ñ<ˆ8ÐUÔBUÐUrB   c                óÎ  — t        |«      }|sy	 t        «       D ]¹  \  }}t        t        |j	                  d«      xs d«      «      }|| k7  rŒ3t        ||«      }|j                  t        › |› �«       |r|j                  t        › |› �«       t        |j	                  d«      «      }|dk(  r||k(  c S t        |«      }	t        |	|z  «      xr ||k(  c S  	 y# t        $ r Y yw xY w)zDMatch a durable ``providers.<key>`` pool against runtime identities.Fr2  r¨   rŽ   Úcustom)r+  r0  r(  ry   r    r6  r3  r9  r=  Úboolr6   )
Úpool_providerÚprovider_normrŽ   Úruntime_urlÚnormalized_namerä   r2  r5  Úconfigured_urlÚruntime_aliasess
             r7   Ú_keyed_custom_pool_matchesrf  a  sü   € ô ˜HÓ%€KÙØðÜ&<Ó&>ò 	UÑ"ˆO˜UÜ6´s¸5¿9¹9À^Ó;TÒ;ZÐXZÓ7[Ó\ˆLØ˜}Ò,ØÜ0°À%ÓHˆGØ�K‰KÔ-Ð.¨Ð.?Ð@ÔAÙØ—‘Ô1Ð2°<°.ÐAÔBÜ& u§y¡y°Ó'<Ó=ˆNØ Ò(Ø" nÑ4Ò4Ü<¸]ÓKˆOÜ˜¨'Ñ1Ó2ÒT°{ÀnÑ7TÒTñ	Uð øô ò Ùðús   �B"C Â3!C ÃC Ã	C$Ã#C$c           	     óx  — 	 t        «       D �]  \  }}t        › |› �| k7  rŒ|h}|j                  d«      |j                  d«      fD ]e  }t        t	        |xs d«      «      }|sŒ|j                  |«       |j                  t        «      sŒE|j                  |t        t        «      d «       Œg t        |j                  d«      «      }t        |«      h}	|j                  t        «      r*|	j                  t        |t        t        «      d «      «       t        |	|z  «      xr ||k(  c S  	 y# t        $ r Y yw xY w)zGMatch a legacy ``custom:<name>`` pool against a named runtime identity.r¤   r2  r¨   NrŽ   F)r0  r9  r    r(  ry   r3  rz   r:  r+  r_  r6   )
r`  ra  rb  rc  rä   r5  r¼   Úaliasrd  re  s
             r7   Ú_legacy_custom_pool_matchesri  }  s7  € ðÜ&<Ó&>ó 	UÑ"ˆO˜UÜ$Ð% oÐ%6Ð7¸=ÒHØØ&Ð'ˆGØŸ)™) FÓ+¨U¯Y©Y°~Ó-FÐGò E�Ü3´C¸ºÀÓ4DÓE�ÚØ—K‘K Ô&Ø×'Ñ'Ô(:Õ;ØŸ™ E¬#Ô.@Ó*AÐ*BÐ$CÕDðEô ' u§y¡y°Ó'<Ó=ˆNÜ:¸=ÓIÐJˆOØ×'Ñ'Ô(:Ô;Ø×#Ñ#Ô$?ÀÌcÔRdÓNeÐNfÐ@gÓ$hÔiÜ˜¨'Ñ1Ó2ÒT°{ÀnÑ7TÒTñ	Uð" øô ò Ùðús%   ‚A!D- Á$&D- ÂBD- Ä*D- Ä-	D9Ä8D9c                ó´   — t        |t        «      r|syt        | dd«      xs t        | dd«      }t        |t        «      r|syddlm}  ||«       ||«      k(  S )u¬  Whether a pooled credential may be bound to a session running at ``base_url``. ``_swap_credential``
    adopts the entry's base_url too, so a same-provider entry for another endpoint (public OpenAI vs. an
    Azure resource) would send the session's requests â€” and the entry's key â€” to the wrong host (#68237).
    Entries or sessions without endpoint metadata (legacy adapters, test doubles) cannot rebind and are accepted.TrÉ   NrŽ   r   )Únormalize_route_base_url)rx   ry   r¹   Úhermes_cli.route_identityrk  )rä   rŽ   rE  rk  s       r7   Ú%credential_pool_entry_serves_endpointrm  ˜  sY   € ô
 �h¤Ô$©HØÜ˜Ð1°4Ó8Ò\¼GÀEÈ:ÐW[Ó<\€IÜ�i¤Ô%©YØÝBÙ# IÓ.Ñ2JÈ8Ó2TÑTÐTrB   ©rŽ   c               ó¾  — t        | dd«      }|€t        | t        «      sy| }t        |xs d«      j                  «       j	                  «       }t        |xs d«      j                  «       j	                  «       }|r|sy|j                  t        «      s||k(  ryt        |||«      S |dk(  r…	 t        |xs d«      }t        |xs d«      j                  «       j	                  «       |k(  ryt        |xs d«      }||D �ch c])  }t        |«      j                  «       j	                  «       ’Œ+ c}v S t        |«      }	|	syt        |||	«      S # t        $ r Y yw xY wc c}w )aI  Return whether a pool belongs to the requested runtime provider.

    Named custom endpoints may use three identities: the live agent can retain
    the configured name/provider key, newer runtime paths normalize it to
    ``custom``, and the pool may be keyed as the durable ``providers.<key>``
    slug or as legacy ``custom:<name>``. Accept those aliases only when the
    runtime endpoint belongs to the same configured custom provider. Empty
    identities fail closed. Legacy pool adapters without a ``provider``
    attribute remain compatible; production pools are scoped.
    r}   NTr¨   Fr^  )r¹   rx   ry   rÀ   rö   rz   r9  rf  rI  rF  r6   r+  ri  )
Úpool_or_providerr}   rŽ   Úraw_pool_providerr`  ra  Úmatched_poolrH  r×   rb  s
             r7   Ú credential_pool_matches_providerrs  ¦  sT  € ô   Ð 0°*¸dÓCÐØÐ ÜÐ*¬CÔ0ð Ø,ÐÜÐ)Ò/¨RÓ0×6Ñ6Ó8×>Ñ>Ó@€MÜ˜š BÓ'×-Ñ-Ó/×5Ñ5Ó7€MÙ¡ØØ×#Ñ#Ô$6Ô7Ø˜MÒ)ØÜ)¨-¸ÈÓQÐQØ˜Ò ð	Ü7¸ºÀBÓGˆLÜ�<Ò% 2Ó&×,Ñ,Ó.×4Ñ4Ó6¸-ÒGØÜ<¸Xº^ÈÓLˆJð ÀZÖ P¸c¤ S£§¡Ó!1×!7Ñ!7Õ!9Ò PÐPÐPä˜HÓ%€KÙØÜ& }°mÀ[ÓQÐQøô ò 	Ùð	üâ Ps   Â,=E Ã*E Ã>.EÅ	EÅEc                óz  ‡‡— t        | xs d«      j                  «       j                  «       Š‰sydˆˆfd„}	 ‰dk(  r<t        ‰«      }|r` ||«      rXt        |«      j                  «       j                  «       S t	        «       D ]$  \  }}t        ||«      D ]  } ||«      sŒ|c c S  Œ& ‰S # t        $ r Y ‰S w xY w)a–  Resolve the credential-pool key for a runtime provider identity.

    Named custom runtimes retain their configured alias while their pool may
    be stored under the durable ``providers.<key>`` slug or legacy
    ``custom:<name>``. Return that scoped key only when the canonical
    provider/endpoint boundary accepts it; otherwise preserve the normalized
    runtime identity so callers fail closed.
    r¨   c                ó    •— t        | ‰‰¬«      S )Nrn  )rs  )Ú	candidaterŽ   ra  s    €€r7   Ú_acceptsz*resolve_runtime_pool_key.<locals>._acceptsâ  s   ø€ Ü/°	¸=ÐS[Ô\Ð\rB   r^  )rv  ry   rÊ   r_  )ry   rÀ   rö   rI  r0  rA  r6   )r}   rŽ   rw  rv  rc  rä   ra  s    `    @r7   Úresolve_runtime_pool_keyrx  Õ  sÎ   ù€ ô ˜š BÓ'×-Ñ-Ó/×5Ñ5Ó7€MÙØö]ðØ˜HÒ$Ü4°XÓ>ˆIÙ™X iÔ0Ü˜9“~×+Ñ+Ó-×3Ñ3Ó5Ð5ô
 +AÓ*Bò )Ñ&� Ü!<¸_ÈeÓ!Tò )�IÙ 	Õ*Ø(Ô(ñ)ð)ð Ðøô ò ØØÐðús$   ¹A B- Á:'B- Â"B- Â(B- Â-	B:Â9B:rð   c                ó"  — | €yt         j                  j                  d«      r]t         j                  j                  dd«      }|r;t        |«      dz  dz  }	 | j	                  d¬«      |j	                  d¬«      k(  ry	 | S | S # t
        $ r Y yw xY w)	aš  Apply the pytest seat belt to a resolved global-root auth.json path.

    ``None`` means classic mode (profile == root) or "refuse": under pytest,
    never write the real user's ``~/.hermes/auth.json`` even when HERMES_HOME
    points at a profile path (mirrors the read-side guard in
    ``_load_global_auth_store``). Uses the unmodified HOME env, not
    ``Path.home()`` which fixtures may monkeypatch.
    NÚPYTEST_CURRENT_TESTÚHOMEr¨   z.hermesz	auth.jsonF)Ústrict)ÚosÚenvironr    r   Úresolver6   )Úglobal_pathÚreal_home_envÚ	real_roots      r7   Ú_guarded_global_rootrƒ  ý  s�   € ð ÐØÜ	‡z�z‡~�~Ð+Ô,ÜŸ
™
Ÿ™ v¨rÓ2ˆÙÜ˜]Ó+¨iÑ7¸+ÑEˆIðØ×&Ñ&¨eÐ&Ó4¸	×8IÑ8IÐQVÐ8IÓ8WÒWØð Xð Ðˆ;Ðøô ò Ùðús   Á%B Â	BÂBc                óò   — 	 t        t        j                  «       «      }|€y	 t        j                  | ||d¬«       y# t        $ r Y yw xY w# t        $ r!}t
        j                  d| |«       Y d}~yd}~ww xY w)ue  Persist a rotated OAuth ``state`` into the global-root auth.json.

    Best-effort write-through for the multi-profile rotation hazard: 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 revoked
    refresh token and every other profile 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 root-stale and must never break the
    profile's own successful save. Mirrors
    ``hermes_cli.auth._write_through_xai_oauth_to_global_root``.

    See #48415.
    NF©Ú
set_activez8%s pool refresh: write-through to global root failed: %s)rƒ  rÁ   r&   r6   Ú _persist_provider_state_to_storeÚloggerÚdebug)Úprovider_idr  r€  Úexcs       r7   Ú,_write_through_provider_state_to_global_rootrŒ    s}   € ð(Ü*¬8×+JÑ+JÓ+LÓMˆð ÐØðcÜ×1Ñ1°+¸uÀkÐ^cÖdøô ò Ùðûô ò cÜ�‰ÐOÐQ\Ð^a×bÑbûðcús&   ‚= £A ½	A	ÁA	Á	A6ÁA1Á1A6c                óŽ   — |j                   dk7  s|j                  t        | dd«      vry	 ddlm}  |«       S # t
        $ r Y yw xY w)zURoot ``.anthropic_oauth.json`` when *entry* is a borrowed hermes_pkce row, else None.Úhermes_pkceÚ_borrowed_root_idsr>   Nr   )Ú_root_hermes_oauth_file)r‡   rƒ   r¹   Úagent.anthropic_credentialsr�  r6   )Úpoolrä   r�  s      r7   Ú_singleton_target_for_entryr“  4  sJ   € à‡|�|�}Ò$¨¯©¼ÀÐFZÐ\^Ó8_Ñ(_ØðÝGÙ&Ó(Ð(øÜò Ùðús   «8 ¸	AÁAc                óÔ   — 	 t        «       j                  d«      }t        |t        «      r|j                  | «      nd}t        |t
        «      xr t        |«      S # t        $ r Y yw xY w)zÊTrue when the ACTIVE auth.json has its own rows for *provider*.

    Named profiles with no local rows read the provider through the
    ``read_credential_pool`` global-root fallback ("borrowing").
    Úcredential_poolTN)r'   r    r6   rx   rÜ   rL  r_  )r}   r’  rò   s      r7   Ú_profile_owns_pool_providerr–  ?  s`   € ðÜÓ!×%Ñ%Ð&7Ó8ˆô %/¨t´TÔ$:ˆd�h‰h�xÔ À€GÜ�gœtÓ$Ò6¬¨g«Ð6øô ò Ùðús   ‚A Á	A'Á&A'c                 óH   — 	 t        t        «       «      S # t        $ r Y yw xY w)zåGlobal-root auth.json when persisting a BORROWED single-use pool, else None.

    ``None`` means "persist to the active store as usual": classic mode
    (profile == root), or the profile owns its own rows for this provider.
    N)rƒ  r&   r6   r>   rB   r7   Ú_borrowed_single_use_pool_rootr˜  M  s(   € ðÜ#Ô$:Ó$<Ó=Ð=øÜò Ùðús   ‚ •	! !©Ústatus_cleared_idsc               óú  — t        |¬«      5  t        |«      }|j                  d«      }t        |t        «      si }||d<   |j                  | «      }t        |t
        «      r|ng }|D �ci c]7  }t        |t        «      sŒ|j                  d«      sŒ&|j                  d«      |“Œ9 }	}|xs dD �
ch c]  }
|
sŒ|
’Œ	 }}
g }d}|D ]ƒ  }t        |t        «      r|j                  d«      nd}|r|	j                  |«      nd}|€|j                  |«       ŒOt        j                  |||v rdn|| «      }||k7  rd}|j                  |«       Œ… |r||| <   t        ||¬«       ddd«       yc c}w c c}
w # 1 sw Y   yxY w)u‡  UPDATE-ONLY merge of *payloads* into the root store's rows for *provider*.

    A borrower may refresh the root's rows (rotation, cooldown state) but
    never add or delete them â€” the root owns their lifecycle. In particular a
    profile's singleton-prune (it has no ``.anthropic_oauth.json`` of its own)
    must not delete the root grant, so ``removed_ids`` is ignored by callers.
    )Útarget_pathr•  rƒ   r>   FNT)
r#   r'   r    rx   rÜ   rL  r?  rÁ   Ú_merge_disk_cooldown_stater-   )r}   Úpayloadsr€  rš  Ústorer’  ÚexistingÚexisting_listÚpÚincoming_by_idÚcidÚclearedÚmergedÚchangedÚ
disk_entryÚdidÚincomingÚupdateds                     r7   Ú_update_root_pool_rowsr¬  Y  s~  € ô 
 kÔ	2ñ =Ü  Ó-ˆØ�y‰yÐ*Ó+ˆÜ˜$¤Ô%ØˆDØ'+ˆEÐ#Ñ$Ø—8‘8˜HÓ%ˆÜ$.¨x¼Ô$>™ÀBˆØ2:Öb¨Q¼jÈÌDÕ>QÐVW×V[ÑV[Ð\`ÕVa˜!Ÿ%™% ›+ q™.ÐbˆÐbØ#5Ò#;¸ÖD˜3Â’3ÐDˆÐDØ')ˆØˆØ'ò 	#ˆJÜ*4°ZÄÔ*F�*—.‘. Ô&ÈDˆCÙ25�~×)Ñ)¨#Ô.¸4ˆHØÐØ—‘˜jÔ)Øä×9Ñ9Ø #¨¡.™$°jÀ(óˆGð ˜*Ò$Ø�Ø�M‰M˜'Õ"ð	#ñ Ø#ˆD�‰NÜ˜U°Õ<÷7=ð =ùò cùÚD÷=ð =ús=   �AE1Á*E'Â E'ÂE'Â&
E1Â0E,Â8E,Â<B"E1Å'
E1Å1E:©Úremoved_idsrš  c               óÜ   — | t         v r(t        | «      st        «       }|�	 t        | |||¬«       yt        | |||¬«       y# t        $ r!}t
        j                  d| |«       Y d}~yd}~ww xY w)a@  Persist a provider's pool rows to the store that OWNS them.

    A named profile that sees a single-use-refresh provider (Anthropic,
    Codex, xAI OAuth) only through the global-root fallback must not
    materialize a local ``credential_pool.<provider>`` copy: that copy forks
    the single-use refresh token, the first profile to rotate commits the new
    pair only to its own file, and root plus every sibling die with
    ``invalid_grant`` (#100339). Such rows are written back to the root store
    (under the root lock); everything else goes to the active store.
    Nr™  za%s pool: write-through of borrowed root grant failed (%s); not materializing a profile-local copyr­  )r"   r–  r˜  r¬  r6   rˆ  Úwarningr1   )r}   rž  r®  rš  r€  r‹  s         r7   Úpersist_pool_entriesr±  ‚  s†   € ð" Ô4Ñ4Ô=XÐYaÔ=bÜ4Ó6ˆØÐ"ðÜ&Ø˜h¨Ø'9õð ÜØ�(¨ÐHZöøô ò ô —‘ð=à˜c÷ð ð
 ûðús   ¡A Á	A+Á
A&Á&A+)ÚCodexr²  Úrefresh_codex_oauth_pureÚ&_is_terminal_codex_oauth_refresh_error)z	xAI OAuthÚxAIÚrefresh_xai_oauth_pureÚ$_is_terminal_xai_oauth_refresh_error)rÅ   ú	xai-oauthz$Dict[str, Tuple[str, str, str, str]]Ú_TOKENS_SINGLETON_PROVIDERSrw   r¿   )rÅ   r¸  rw   Ú$HERMES_CODEX_REFRESH_TIMEOUT_SECONDSÚ"HERMES_XAI_REFRESH_TIMEOUT_SECONDSÚclaude_coderâ   )rw   r¿   rÅ   r¸  c                  ó$   ‡ — e Zd ZdZdˆ fd„Zˆ xZS )Ú_RefreshDonez\Raised inside a provider refresher to short-circuit ``_refresh_entry_impl`` with ``result``.c                ó0   •— t         ‰| �  «        || _        y r™   )ÚsuperÚ__init__rº   )r›   rº   Ú	__class__s     €r7   rÁ  z_RefreshDone.__init__Ñ  s   ø€ Ü‰ÑÔØˆ�rB   )rº   zOptional['PooledCredential'])r£   rÌ   rÍ   Ú__doc__rÁ  Ú__classcell__)rÂ  s   @r7   r¾  r¾  Î  s   ø„ Ùf÷ñ rB   r¾  c                  óÆ  — e Zd ZdBd„ZdCd„ZddœdDd„ZddœdEd„ZdFd„ZdCd„ZdGd	„Z	dHd
„Z
dHd„ZdIdJd„ZdKd„Zdddœ	 	 	 	 	 dLd„ZddœdMd„ZdNd„Z	 	 	 	 	 	 dOd„Z	 dIdddœ	 	 	 	 	 	 	 	 	 	 	 dPd„ZdQd„ZdQd„ZeZdQd„ZdQd„ZdRd„ZdSd„ZdTd„Zd„ Z	 	 	 	 	 	 	 	 dUd„ZdVd „Z	 	 	 	 	 	 dWd!„ZdQd"„ZdQd#„Z dTd$„Z!dXd%„Z"dYd&„Z#dYd'„Z$dYd(„Z%dZd)„Z&dZd*„Z'ddœd[d+„Z(ddœd\d,„Z)d]d-„Z*d^d.„Z+dQd/„Z,d0d0dd1œ	 	 	 	 	 	 	 d_d2„Z-d`d3„Z.dddd4œ	 	 	 	 	 	 	 dad5„Z/dHd6„Z0ddœdbd7„Z1	 	 	 	 	 	 dcd8„Z2dHd9„Z3dddddd:œ	 	 	 	 	 	 	 	 	 	 	 	 	 ddd;„Z4dIded<„Z5	 	 	 	 dfd=„Z6dgd>„Z7dHd?„Z8	 	 dh	 	 	 	 	 did@„Z9dHdA„Z:y)jÚCredentialPoolc                óö   — || _         t        |d„ ¬«      | _        d | _        t	        «       | _        t        |«      | _        t        j                  «       | _
        i | _        t        | _        d | _        d| _        y )Nc                ó   — | j                   S r™   rê   ©rä   s    r7   rA   z)CredentialPool.__init__.<locals>.<lambda>Ù  s
   € ¸%¿.¹.€ rB   ©r×   r   )r}   rN  Ú_entriesÚ_current_idr8  r�  r\  Ú	_strategyÚ	threadingÚRLockÚ_lockÚ_active_leasesÚ%DEFAULT_MAX_CONCURRENT_PER_CREDENTIALÚ_max_concurrentÚ_last_no_entries_log_atÚ_unmatched_rotation_streak©r›   r}   rò   s      r7   rÁ  zCredentialPool.__init__×  sl   € Ø ˆŒÜ˜wÑ,HÔIˆŒØ*.ˆÔô -0«EˆÔÜ*¨8Ó4ˆŒô
 —_‘_Ó&ˆŒ
Ø.0ˆÔÜDˆÔð 9=ˆÔ$ð 01ˆÕ'rB   c                óp   — | j                   5  t        | j                  «      cd d d «       S # 1 sw Y   y xY wr™   )rÐ  r_  rË  rš   s    r7   Úhas_credentialszCredentialPool.has_credentialsó  ó*   € Ø�Z‰Zñ 	'Ü˜Ÿ™Ó&÷	'÷ 	'ò 	'úó   �,¬5N©Úmodelc               ó†   — | j                   5  | j                  |¬«      \  }}t        |«      cddd«       S # 1 sw Y   yxY w)a+  True if at least one entry is not currently in exhaustion cooldown.

        ``_available_entries`` is not read-only (it prunes aged-out DEAD
        manual entries and persists), so it must run under ``self._lock``
        like every other caller or a probe can race a concurrent rotation.
        rÛ  N)rÐ  Ú_available_entriesr_  )r›   rÜ  Ú	availableÚ_pendings       r7   Úhas_availablezCredentialPool.has_available÷  s?   € ð �Z‰Zñ 	#Ø"&×"9Ñ"9ÀÐ"9Ó"FÑˆI�xÜ˜	“?÷	#÷ 	#ò 	#ús	   � 7·A c               ód  ‡‡— | j                   5  | j                  ‰¬«      \  }}|r
	 ddd«       y| j                  «       Šˆfd„| j                  D «       D �cg c]  }|�|‘Œ }}|j	                  ˆfd„| j                  D «       «       |rt        |«      ndcddd«       S c c}w # 1 sw Y   yxY w)aq  Earliest epoch time (seconds) any entry re-enters rotation.

        ``None`` when an entry is available now, or when no exhausted entry
        carries a usable recovery time (empty pool, or only ``STATUS_DEAD``
        entries). Callers must treat ``None`` as "no wait information".
        Runs under ``self._lock`` for the same reason as ``has_available``.
        rÛ  Nc              3  ó^   •K  — | ]$  }|j                   t        k(  rt        |‰¬ «      –— Œ& y­w)r!  N)rm   r#  r$  )rì   rä   rú   s     €r7   rí   z3CredentialPool.next_available_at.<locals>.<genexpr>  s2   øè ø€ ò àØ×(Ñ(Ô,<Ò<ô % U¸O×LÐLñùs   ƒ*-c              3  ód   •K  — | ]'  }|j                   t        k7  rt        |‰«      }|�|–— Œ) y ­wr™   )rm   ÚSTATUS_DEADr   )rì   rä   ÚuntilrÜ  s      €r7   rí   z3CredentialPool.next_available_at.<locals>.<genexpr>  s9   øè ø€ ò àØ×$Ñ$¬Ò3Ü2°5¸%Ó@�EØÐ$ô	 ñùs   ƒ-0)rÐ  rÞ  Ú_is_sole_credentialrË  Úextendr  )r›   rÜ  rß  rà  ræ  rH  rú   s    `    @r7   Únext_available_atz CredentialPool.next_available_at  sÄ   ù€ ð �Z‰Zñ 	;Ø"&×"9Ñ"9ÀÐ"9Ó"FÑˆI�xÙØ÷	;ð 	;ð #×6Ñ6Ó8ˆOóà!%§¡ôöàð
 Ð$ò ðˆJð ð ×Ñó à!Ÿ]™]ôô ñ '1”3�z”?°d÷1	;ñ 	;ùò÷	;ð 	;ús"   �B&±'B&ÁB!Á#4B&Â!B&Â&B/c                óp   — | j                   5  t        | j                  «      cd d d «       S # 1 sw Y   y xY wr™   )rÐ  rL  rË  rš   s    r7   rò   zCredentialPool.entries$  rÙ  rÚ  c                ó@   — t        d„ | j                  D «       «      dk  S )zWDEAD entries never re-enter rotation, so <=1 non-DEAD entry means nothing to rotate to.c              3  óH   K  — | ]  }|j                   t        k7  sŒd –— Œ y­w)rð   N)rm   rå  )rì   Úes     r7   rí   z5CredentialPool._is_sole_credential.<locals>.<genexpr>*  s   è ø€ ÒL˜¨q¯}©}ÄÓ/K”1ÑLùs   ‚"›"rð   )ÚsumrË  rš   s    r7   rç  z"CredentialPool._is_sole_credential(  s   € äÑL˜dŸm™mÔLÓLÐPQÑQÐQrB   c                óB   ‡— t        ˆfd„| j                  D «       d «      S )Nc              3  ó4   •K  — | ]  } ‰|«      sŒ|–— Œ y ­wr™   r>   )rì   rí  Ú	predicates     €r7   rí   z'CredentialPool._find.<locals>.<genexpr>-  s   øè ø€ Ò>˜1±¸1µ”QÑ>ùrR  )rS  rË  )r›   rñ  s    `r7   Ú_findzCredentialPool._find,  s   ø€ ÜÓ> §¡Ô>ÀÓEÐErB   c                óF   ‡ — ‰ j                   sy ‰ j                  ˆ fd„«      S )Nc                ó6   •— | j                   ‰j                  k(  S r™   )rƒ   rÌ  )rí  r›   s    €r7   rA   z2CredentialPool._current_unlocked.<locals>.<lambda>2  s   ø€  A§D¡D¨D×,<Ñ,<Ñ$<€ rB   )rÌ  rò  rš   s   `r7   Ú_current_unlockedz CredentialPool._current_unlocked/  s   ø€ Ø×ÒØØ�z‰zÓ<Ó=Ð=rB   c                óf   — | j                   5  | j                  «       cd d d «       S # 1 sw Y   y xY wr™   )rÐ  rõ  rš   s    r7   ÚcurrentzCredentialPool.current4  s*   € Ø�Z‰Zñ 	,Ø×)Ñ)Ó+÷	,÷ 	,ò 	,úó   �'§0c                óh  — | j                   5  | j                  «       }|�&|�|j                  |k(  r|j                  cddd«       S |€
	 ddd«       y| j                  D �cg c]  }|j                  |k(  sŒ|‘Œ }}t        |«      dk(  r|d   j                  ndcddd«       S c c}w # 1 sw Y   yxY w)zÎStable id for the runtime credential in use.

        Prefer the current selection when it still supplies ``api_key_hint``;
        if the cursor was cleared, fall back to an unambiguous key match.
        Nrð   r   )rÐ  rõ  rÃ   rƒ   rË  r:  )r›   Úapi_key_hintr÷  rí  Úmatchess        r7   Úentry_id_for_api_keyz#CredentialPool.entry_id_for_api_key8  s¿   € ð �Z‰Zñ 	@Ø×,Ñ,Ó.ˆGØÐ"¨Ð(<À×@WÑ@WÐ[gÒ@gØ—z‘z÷	@ñ 	@ð Ð#Ø÷	@ð 	@ð #'§-¡-ÖU˜Q°1×3DÑ3DÈÓ3T’qÐUˆGÐUÜ$'¨£L°AÒ$5�7˜1‘:—=’=¸4÷	@ñ 	@ùò V÷	@ð 	@ús/   �/B(ÁB(ÁB(Á B#Á5B#Á9 B(Â#B(Â(B1c                óæ   — | j                   5  t        | j                  «      D ]7  \  }}|j                  |j                  k(  sŒ || j                  |<    ddd«       y 	 ddd«       y# 1 sw Y   yxY w)u÷   Swap an entry in-place by id, preserving sort order.

        Self-locking (RLock) so the deferred refresh path â€” which runs outside
        the pool lock â€” cannot tear ``self._entries`` against a concurrent
        select()/rotation.
        N)rÐ  Ú	enumeraterË  rƒ   )r›   ÚoldÚnewÚidxrä   s        r7   Ú_replace_entryzCredentialPool._replace_entryI  sg   € ð �Z‰Zñ 	Ü'¨¯©Ó6ò ‘
��UØ—8‘8˜sŸv™vÓ%Ø),�D—M‘M #Ñ&Ø÷		ð 	ñ÷	÷ 	ñ 	ús   �5A'ÁA'ÁA'Á'A0r­  c          	     óÎ   — | j                   5  t        | j                  | j                  D �cg c]  }|j	                  «       ‘Œ c}||¬«       d d d «       y c c}w # 1 sw Y   y xY w)Nr­  )rÐ  r±  r}   rË  r½   )r›   r®  rš  rä   s       r7   Ú_persistzCredentialPool._persistV  sW   € ð �Z‰Zñ 	Ü Ø—‘Ø.2¯m©mÖ< U�—‘•Ò<Ø'Ø#5õ	÷	ð 	ùò =÷	ð 	ús   �A¬AÁ
AÁAÁA$T)Úpersistc               óf   — t        |fi |¤Ž}| j                  ||«       |r| j                  «        |S )zI``replace(entry, **updates)``, swap it into the pool, optionally persist.)r	   r  r  )r›   rä   r  Úupdatesr«  s        r7   Ú_adoptzCredentialPool._adopte  s2   € ä˜%Ñ+ 7Ñ+ˆØ×Ñ˜E 7Ô+ÙØ�M‰MŒOØˆrB   c                ó~  — | j                   5  | j                  D �cg c]  }|j                  |v sŒ|j                  ‘Œ }}| j                  D �cg c]  }|j                  |vsŒ|‘Œ c}| _        | j                  |j                  k(  rd| _        | j                  |¬«       ddd«       yc c}w c c}w # 1 sw Y   yxY w)a  Drop every entry seeded from *sources* and persist the removal.

        Atomic read-modify-write of ``self._entries``: this runs on the
        DEFERRED refresh path (outside the pool lock), so take the RLock here;
        still-locked callers re-enter safely.
        N©r®  )rÐ  rË  r‡   rƒ   rÌ  r  )r›   rä   ÚsourcesÚitemr®  s        r7   Ú_quarantine_sourcesz"CredentialPool._quarantine_sourcesm  s›   € ð �Z‰Zñ 	3Ø/3¯}©}ÖW tÀÇÁÈwÒ@V˜4Ÿ7›7ÐWˆKÐWØ.2¯m©mÖZ d¸t¿{¹{ÐRYÒ?YšTÒZˆDŒMØ×Ñ 5§8¡8Ò+Ø#'�Ô Ø�M‰M kˆMÔ2÷	3ð 	3ùÚWùÚZ÷	3ð 	3ús2   �B3œB)°B)¾B3ÁB.Á"B.Á&:B3Â)
B3Â3B<c                ó¶   — |j                  d«      }t        |t        «      r|j                  «       j	                  «       nd}|t
        k(  ry|dk(  xr |t        v S )aÆ  Detect upstream-permanent OAuth failures that won't recover on TTL.

        Only 401s whose reason is a known terminal OAuth state count;
        token_expired (refreshable) and reason-less 401s (possible glitch)
        stay transient, as do 429/402. The one status-independent case is
        ``CREDENTIAL_PERSIST_FAILED_REASON``: no upstream response is involved,
        the rotated pair never became durable and only a re-auth recovers it.
        r  r¨   Trü   )r    rx   ry   rÀ   rö   Ú CREDENTIAL_PERSIST_FAILED_REASONÚ_TERMINAL_AUTH_REASONS)r›   Ústatus_codeÚnormalized_errorÚ
raw_reasonr  s        r7   Ú_is_terminal_auth_failurez(CredentialPool._is_terminal_auth_failure}  sY   € ð &×)Ñ)¨(Ó3ˆ
Ü/9¸*ÄcÔ/J�×!Ñ!Ó#×)Ñ)Ô+ÐPRˆØÔ5Ò5ØØ˜cÑ!ÒF fÔ0FÐ&FÐFrB   ©r  rg   c               ód  — t        |«      }| j                  ||«      }t        |j                  «      }|r||d<   n|j	                  dd «       | j                  |||rt        nt        t        j                  «       ||j                  d«      |j                  d«      |j                  d«      |¬«	      S )Nrg   r  r  r  )r  rm   rn   ro   rp   rq   rr   r—   )
r   r  rÜ   r—   Úpopr  rå  r#  r  r    )	r›   rä   r  r  r  rg   r  ÚterminalÚupdated_extras	            r7   Ú_mark_exhaustedzCredentialPool._mark_exhausted�  s°   € ô 4°MÓBÐð ×1Ñ1°+Ð?OÓPˆô ˜UŸ[™[Ó)ˆÙØ.<ˆMÐ*Ò+à×ÑÐ.°Ô5Ø�{‰{ØØÙ'/�Ô5EÜŸ9™9›;Ø'Ø.×2Ñ2°8Ó<Ø/×3Ñ3°IÓ>Ø 0× 4Ñ 4°ZÓ @Øð ó 

ð 
	
rB   c           	     óR  — | j                   dk7  s|j                  dk7  r|S 	 ddlm}  |«       }|s|S |j	                  dd«      }|j	                  dd«      }|s|rŸ|r||j
                  xs dk7  s|rˆ||j                  xs dk7  rut        j                  d|j                  «        | j                  |f|xs |j
                  |xs |j                  |j	                  d	d«      xs |j                  d
œt        ¤ŽS |S # t        $ r!}t        j                  d|«       Y d}~|S d}~ww xY w)zKSync a claude_code entry from ~/.claude/.credentials.json if tokens differ.rw   r¼  r   )Úread_claude_code_credentialsÚrefreshTokenr¨   ÚaccessTokenzDPool entry %s: syncing tokens from credentials file (tokens changed)Ú	expiresAt©rˆ   rŠ   r�   z(Failed to sync from credentials file: %sN)r}   r‡   r‘  r  r    rˆ   rŠ   rˆ  r‰  rƒ   r  r�   rt   r6   )r›   rä   r  ÚcredsÚfile_refreshÚfile_accessr‹  s          r7   Ú+_sync_anthropic_entry_from_credentials_filez:CredentialPool._sync_anthropic_entry_from_credentials_fileº  s*  € à�=‰=˜KÒ'¨5¯<©<¸=Ò+HØˆLð	JÝPÙ0Ó2ˆEÙØ�Ø Ÿ9™9 ^°RÓ8ˆLØŸ)™) M°2Ó6ˆKñ ™|Ù °×1CÑ1CÒ1IÀrÒ!JÙ  \°e×6IÑ6IÒ6OÈRÒ%Pä—‘ÐcÐej×emÑemÔnØ"�t—{‘{Øðà!,Ò!B°×0BÑ0BØ".Ò"E°%×2EÑ2EØ"'§)¡)¨K¸Ó";Ò"R¸u×?RÑ?Rñ	ô
 $ñð ð ˆøô ò 	JÜ�L‰LÐCÀS×IÐIØˆûð	Jús   ¢C< ³CC< Ã<	D&ÄD!Ä!D&c                óŠ  ‡— | j                   dvrt        | j                   «      €‰S | j                   dk(  }| j                   dk(  }dddœj                  | j                   | j                   «      }|r"t        ‰j                  | j                   «      r‰S 	 t        ˆfd„t        | j                   «      D «       d«      }t        |t        «      s‰S t        j                  | j                   |«      }|s>|j                  xs dj                  «       s |j                  xs dj                  «       s‰S |j                  ‰j                  k7  s|j                  ‰j                  k7  r5t        j                  d	‰j                   |«       | j#                  ‰|«       |S 	 ‰S # t$        $ r"}t        j                  d
||«       Y d}~‰S d}~ww xY w)uD  Adopt a token pair rotated by another pool instance (anthropic, xai-oauth).

        Re-reads the exact persisted row from the credential-pool store while
        the shared cross-process auth-store lock is held. Direct integrations
        load a fresh ``CredentialPool`` per request, so in-memory locks cannot
        protect a single-use refresh token across requests or processes.

        Anthropic borrowed sources (``claude_code``) are excluded: they are
        reference-only rows whose secrets are stripped before reaching
        auth.json, so re-reading yields empty tokens that would be adopted as
        a "rotation" â€” blanking a usable credential. The singleton file, not
        the pool store, is token authority for those sources; a row with no
        token material at all is refused for the same reason.
        )rw   r¸  Nrw   r¸  Ú	Anthropicrµ  c              3  ó‚   •K  — | ]6  }t        |t        «      sŒ|j                  d «      ‰j                  k(  sŒ3|–— Œ8 y­w©rƒ   N©rx   rÜ   r    rƒ   ©rì   r¢  rä   s     €r7   rí   z=CredentialPool._sync_entry_from_pool_store.<locals>.<genexpr>ð  s6   øè ø€ Òs�qÄ:ÈaÔQUÕCVÐ[\×[`Ñ[`ÐaeÓ[fÐjo×jrÑjrÓ[r”Ñsùs   ƒ?™?¸?r¨   zHPool entry %s: adopting %s OAuth tokens rotated by another pool instancez6Failed to sync %s OAuth entry from credential pool: %s)r}   r   r    r   r‡   rS  r0   rx   rÜ   r‚   r·   rˆ   rÀ   rŠ   rˆ  r‰  rƒ   r  r6   )r›   rä   Úis_anthropicÚis_xaiÚdisplayÚ	persistedÚstoredr‹  s    `      r7   Ú_sync_entry_from_pool_storez*CredentialPool._sync_entry_from_pool_storeØ  s—  ø€ ð �=‰=Ð :Ñ:Ô?RÐSW×S`ÑS`Ó?aÐ?iØˆLØ—}‘}¨Ñ3ˆØ—‘ +Ñ-ˆØ +¸%Ñ@×DÑDÀTÇ]Á]ÐTX×TaÑTaÓbˆÙÔ9¸%¿,¹,ÈÏÉÔVØˆLð	aÜÛsÔ0°·±Ó?ÔsØóˆIô ˜i¬Ô.Ø�Ü%×/Ñ/°·±¸yÓIˆFñ  6×#6Ñ#6Ò#<¸"×"CÑ"CÔ"EÈv×OcÑOcÒOiÐgi×NpÑNpÔNrØ�Ø×"Ñ" e×&8Ñ&8Ò8¸F×<PÑ<PÐTY×TgÑTgÒ<gÜ—‘Ø^Ø—H‘H˜gôð ×#Ñ# E¨6Ô2Ø�ð =hð ˆøô ò 	aÜ�L‰LÐQÐSZÐ\_×`Ð`Øˆûð	aús&   Â9F ÃAF Ä-A&F Æ	GÆ F=Æ=Gc                óÈ  — t         j                  | j                  «      }|€|S |d   }| j                  dk(  }|rdnd}|j                  |vr|S 	 t	        «       5  t        t        «       | j                  «      }ddd«       t        t        «      r|j                  d«      nd}t        |t        «      s|S |rt        ||«      s|S |j                  dd«      }|j                  d	d«      }	|j                  xs d}
t        |«      xr ||j                  xs dk7  xs	 |	xr |	|
k7  }|s-|r+|	r)|	|
k7  r$|s"t        j                  d
|j                  «       d}|r[t!        ||«      rOt        j                  d|j                  t        |t        «      r|j                  d«      nd|j"                  «       d}|rtt        j%                  d|j                  |«       |xs |j                  |	xs |j                  dœt&        ¥}|j                  d«      r|d   |d<    | j(                  |fi |¤ŽS 	 |S # 1 sw Y   �Œ³xY w# t*        $ r"}t        j%                  d||«       Y d}~|S d}~ww xY w)uø  Sync a Codex / xAI device_code entry from auth.json ``providers.<id>.tokens``.

        A fresh ``hermes model`` / ``hermes auth`` login writes new tokens
        under ``_auth_store_lock`` while the pool entry may sit frozen behind
        a ``last_error_reset_at`` hours in the future; without this sync every
        request fails with "no available entries" despite fresh credentials on
        disk. Only singleton-seeded entries apply â€” env/API-key rows have no
        auth.json shadow.
        Nr   rÅ   )râ   úmanual:device_code)râ   Útokensrˆ   r¨   rŠ   z~Pool entry %s: auth.json has newer refresh_token but no access_token; adopting refresh_token to avoid replaying consumed tokenTzšPool entry %s: auth.json singleton predates this entry's rotation (last_refresh %s < %s); keeping pool chain to avoid replaying the consumed refresh tokenr‘   FzNPool entry %s: syncing %s tokens from auth.json (refreshed by another process))rˆ   rŠ   z*Failed to sync %s entry from auth.json: %s)r¹  r    r}   r‡   r#   r(   r'   rx   rÜ   rç   rŠ   r_  rˆ   rˆ  Úinforƒ   r  r‘   r‰  rt   r  r6   )r›   rä   Úspecr-  Úis_codexr  r  r3  Ústore_accessÚstore_refreshÚentry_refreshÚshould_adoptÚfield_updatesr‹  s                 r7   Ú_sync_entry_from_auth_storez*CredentialPool._sync_entry_from_auth_store  sr  € ô +×.Ñ.¨t¯}©}Ó=ˆØˆ<ØˆLØ�q‘'ˆØ—=‘= NÑ2ˆÙ;CÑ7ÐIYˆØ�<‰<˜wÑ&ØˆLð;	UÜ!Ó#ñ PÜ,Ô-=Ó-?ÀÇÁÓO�÷Pä,6°u¼dÔ,C�U—Y‘Y˜xÔ(ÈˆFÜ˜f¤dÔ+Ø�ÙÔ =¸eÀVÔ LØ�Ø!Ÿ:™: n°bÓ9ˆLØ"ŸJ™J ¸Ó;ˆMØ!×/Ñ/Ò5°2ˆMô   Ó-ò Ø ×!3Ñ!3Ò!9°rÑ:ò FØ!ÒD m°}Ñ&Dð ñ  ¡H±À=ÐTaÒCaÑjvô —‘ð5ð —H‘Hô	ð  $�ÙÔ 9¸%ÀÔ Gô —‘ðAð —H‘HÜ1;¸EÄ4Ô1H�E—I‘I˜nÔ-ÈdØ×&Ñ&ôð  %�ÙÜ—‘ØdØ—H‘H˜gôð
 %1Ò$F°E×4FÑ4FØ%2Ò%I°e×6IÑ6Iñ1ô $ð1�ð
 —9‘9˜^Ô,Ø49¸.Ñ4I�M .Ñ1Ø"�t—{‘{ 5Ñ:¨MÑ:Ð:ð ð ˆ÷wPñ Pûôr ò 	UÜ�L‰LÐEÀwÐPS×TÐTØˆûð	Uús=   Á
H6 ÁH)Á8<H6 Â5H6 ÃE H6 È)H3È.H6 È6	I!È?IÉI!c                óF  ‡— | j                   dk7  s‰j                  dk7  r‰S 	 t        «       5  t        t	        «       d«      }ddd«       s‰S dD �ci c]  }||j                  |«      “Œ }}t        ˆfd„|j                  «       D «       «      s‰S t        j                  d‰j                  «       t        t        «      }|j                  |j                  «       D ��ci c]  \  }}|sŒ	||“Œ c}}«       t        ‰j                  «      }|j                  t        D �ci c]  }|j                  |«      €Œ|||   “Œ c}«        | j                   ‰fd|i|¤ŽS # 1 sw Y   �ŒxY wc c}w c c}}w c c}w # t"        $ r!}	t        j                  d|	«       Y d}	~	‰S d}	~	ww xY w)	a3  Sync a Nous device_code entry from auth.json ``providers.nous`` if state differs.

        Another process refreshing via ``resolve_nous_runtime_credentials``
        writes fresh tokens under ``_auth_store_lock``; adopting them avoids a
        "refresh token reuse" revocation on the Nous Portal.
        r¿   râ   N)rˆ   rŠ   r�   r“   r”   r’   c              3  óP   •K  — | ]  \  }}|d vxr t        ‰|d«      |k7  –— Œ y­w)©Nr¨   N)r¹   )rì   r´   r¶   rä   s      €r7   rí   zBCredentialPool._sync_nous_entry_from_auth_store.<locals>.<genexpr>l  s3   øè ø€ ÒkÑPTÐPQÐST�q 
Ð*ÒK¬w°u¸aÀÓ/FÈ!Ñ/KÓKÑkùs   ƒ#&z0Pool entry %s: syncing Nous state from auth.jsonr—   z,Failed to sync Nous entry from auth.json: %s)r}   r‡   r#   r(   r'   r    Úanyrª   rˆ  r‰  rƒ   rÜ   rt   Úupdater—   Ú_NOUS_EXTRA_STATE_KEYSr  r6   )
r›   rä   r  r×   Ú
comparabler;  r´   r¶   Úextra_updatesr‹  s
    `        r7   Ú _sync_nous_entry_from_auth_storez/CredentialPool._sync_nous_entry_from_auth_storeW  s‰  ø€ ð �=‰=˜FÒ" e§l¡l°mÒ&CØˆLð	NÜ!Ó#ñ IÜ,Ô-=Ó-?ÀÓH�÷IáØ�ðöàð �U—Y‘Y˜s“^Ñ#ðˆJð ô ÓkÐXb×XhÑXhÓXjÔkÔkØ�Ü�L‰LÐKÈUÏXÉXÔVÜ,0´Ó,?ˆMØ× Ñ °:×3CÑ3CÓ3E×!K©4¨1¨aÊ ! Q¡$Ó!KÔLÜ  §¡Ó-ˆMØ× Ñ Ü&<ÖY ÀÇ	Á	È!ÃÑ@X��E˜!‘H‘ÒYôð �4—;‘;˜uÑK¨MÐK¸]ÑKÐK÷)Iñ Iüòùó "Lùò Zøô ò 	NÜ�L‰LÐGÈ×MÐMØˆûð	Núsk   £
E6 ­EÁE6 ÁE6 ÁE&Á+%E6 ÂAE6 Ã
E+
Ã)E+
Ã.0E6 ÄE1Ä5E1Ä=E6 ÅE#ÅE6 Å6	F Å?FÆF c                ó€  — |j                   dk7  s| j                  dgt        ¢­vry	 t        «       5  t	        «       }t        || j                  «      \  }}t        |t        «      s
	 ddd«       yt        «       }t        |duxr |duxr t        ||«      «      }| j                  ||«      s
	 ddd«       y|rt        | j                  |«       n$t        || j                  |d¬«       t        |«       ddd«       y# 1 sw Y   yxY w# t        $ r+}t         j#                  d| j                  |«       Y d}~yd}~ww xY w)u#  Write refreshed pool entry tokens back to auth.json ``providers.<id>``.

        Otherwise the next ``load_pool()`` re-seeds the stale singleton state
        over the fresh entry â€” potentially a consumed single-use refresh
        token. Applies to Nous, OpenAI Codex and xAI OAuth singletons.

        ``set_active=False`` everywhere: a sync-back is a token-rotation side
        effect, not the user choosing a provider; ``_save_provider_state``
        would flip ``active_provider`` to whichever provider refreshed last.

        #74339: decide the root write-through on WHERE the state resolved
        from (``_load_provider_state_with_source``), not on whether the
        profile has a ``providers.<id>`` key â€” ``_store_provider_state``
        creates that key unconditionally, which self-sealed the check after
        the first refresh. When the grant came from the global root, write
        back to root ONLY and skip the profile store so it never accrues a
        shadowing key that blocks both the fallback and the write-through.
        râ   r¿   NFr…  z3Failed to sync %s pool entry back to auth store: %s)r‡   r}   r¹  r#   r'   r)   rx   rÜ   r&   r_  r,   Ú_apply_entry_to_singleton_staterŒ  r/   r-   r6   rˆ  r‰  )r›   rä   Ú
auth_storer  Úsource_pathÚglobal_rootÚis_from_rootr‹  s           r7   Ú%_sync_device_code_entry_to_auth_storez4CredentialPool._sync_device_code_entry_to_auth_storez  s0  € ð* �<‰<˜=Ò(¨D¯M©MÀ&ÐAgÔKfÑAgÑ,gØð	dÜ!Ó#ñ 1Ü-Ó/�
Ü%EÀjÐRV×R_ÑR_Ó%`Ñ"��{Ü! %¬Ô.Ø÷	1ð 1ô
 5Ó6�Ü#Ø tÐ+Òp°À4Ð0GÒpÌJÐWbÐdoÓLpó �ð ×;Ñ;¸EÀ5ÔIØ÷1ð 1ñ  Ü@ÀÇÁÐPUÕVä)¨*°d·m±mÀUÐW\Õ]Ü$ ZÔ0÷1÷ 1ñ 1ûô  ò 	dÜ�L‰LÐNÐPT×P]ÑP]Ð_b×cÑcûð	dúsL   ¨
D	 ²5C=Á'D	 Á0>C=Â.D	 Â7=C=Ã4D	 Ã=DÄD	 ÄD	 Ä		D=Ä!D8Ä8D=c                óè  — | j                   dk(  r||j                  |d<   dD ]  }t        ||«      sŒt        ||«      ||<   Œ  t        D ]%  }|j                  j                  |«      }|€Œ!|||<   Œ' |j                  r|j                  |d<   y|j                  d«      }t        |t        «      sy|j                  |d<   |j                  r|j                  |d<   |j                  r|j                  |d	<   y)
zGCopy *entry*'s tokens into the provider's auth.json ``state`` in place.r¿   rˆ   )rŠ   r�   r“   r”   r’   Tr3  FrŠ   r‘   )r}   rˆ   r¹   rB  r—   r    r’   rx   rÜ   rŠ   r‘   )r›   rä   r  r×   Ú	extra_keyÚvalr3  s          r7   rG  z.CredentialPool._apply_entry_to_singleton_state¥  sø   € à�=‰=˜FÒ"Ø$)×$6Ñ$6ˆE�.Ñ!Ø[ò 5�Ü˜5 #Õ&Ü!(¨°Ó!4�E˜#’Jð5ô 4ò +�	Ø—k‘k—o‘o iÓ0�Ø‘?Ø'*�E˜)Ò$ð+ð ×'Ò'Ø.3×.FÑ.F�Ð*Ñ+ØØ—‘˜8Ó$ˆÜ˜&¤$Ô'ØØ!&×!3Ñ!3ˆˆ~ÑØ×ÒØ&+×&9Ñ&9ˆF�?Ñ#Ø×ÒØ$)×$6Ñ$6ˆE�.Ñ!ØrB   c               óø  — |j                   t        k7  s|j                  s|r| j                  |d «       y | j                  t
        vr(t        | j                  «      €| j                  ||¬«      S t        | j                  «       ¬«      5  | j                  dk(  rO| j                  |«      }||ur|s| j                  |«      s|cd d d «       S | j                  ||¬«      cd d d «       S | j                  |«      }| j                  dk(  rƒ|j                  dk(  rt| j                  «       5  | j                  |«      }|j                  |j                  k7  r|cd d d «       cd d d «       S | j                  ||¬«      cd d d «       cd d d «       S |j                   |j                   k7  s|j                  |j                  k7  r|cd d d «       S | j                  ||¬«      cd d d «       S # 1 sw Y   ŒbxY w# 1 sw Y   y xY w)N©Úforce)Útimeout_secondsrÅ   rw   r¼  )r   r{   rŠ   r  r}   Ú_SINGLE_USE_REFRESH_PROVIDERSr   Ú_refresh_entry_implr#   Ú _single_use_refresh_lock_timeoutr<  Ú_entry_needs_refreshr0  r‡   Ú_claude_code_credentials_lockr$  rˆ   )r›   rä   rR  Úsynceds       r7   Ú_refresh_entryzCredentialPool._refresh_entry¿  sý  € Ø�?‰?œoÒ-°U×5HÒ5HÙØ×$Ñ$ U¨DÔ1Øð �=‰=Ô =Ñ=ÔBUÐVZ×VcÑVcÓBdÐBlØ×+Ñ+¨E¸Ð+Ó?Ð?ô ¨d×.SÑ.SÓ.UÔVñ 	AØ�}‰} Ò.Ø×9Ñ9¸%Ó@�Ø Ñ&©u¸T×=VÑ=VÐW]Ô=^Ø!÷		Añ 	Að
 ×/Ñ/°¸eÐ/ÓD÷	Añ 	Að ×5Ñ5°eÓ<ˆFØ�}‰} Ò+°·±ÀÒ0Nð ×7Ñ7Ó9ñ IØ!×MÑMÈfÓU�FØ×+Ñ+¨u×/BÑ/BÒBØ%÷Ið I÷#	Añ 	Að*  ×3Ñ3°FÀ%Ð3ÓH÷	Ið I÷#	Añ 	Að, ×"Ñ" e×&8Ñ&8Ò8¸F×<PÑ<PÐTY×TgÑTgÒ<gØ÷/	Añ 	Að0 ×+Ñ+¨F¸%Ð+Ó@÷1	Añ 	A÷"Ið Iú÷#	Að 	AúsH   Â	9G0ÃG0Ã(?G0Ä',G$Å	G0Å&G$Å8	G0Æ3G0ÇG0Ç$G-	Ç)G0Ç0G9c                óN   — ddl m} t        | j                  «        |«       ¬«      S )zâCross-process lock keyed to the shared claude_code credentials file.

        Unlike the per-profile ``_auth_store_lock()`` this serializes every
        profile and process that might refresh a ``claude_code`` entry.
        r   )Úclaude_code_credentials_path)rS  rœ  )r‘  r\  r#   rV  )r›   r\  s     r7   rX  z,CredentialPool._claude_code_credentials_lockë  s'   € õ 	MäØ ×AÑAÓCÙ4Ó6ô
ð 	
rB   c          	     óD  — t         j                  d|j                  ||«       	 ddlm}m}  ||j                  |j                   ||j                  «      ¬«       | j                  |dt        d	|› d
|› �dœ«       y# t        $ r t         j                  dd¬«       Y ŒCw xY w)u›  Quarantine an entry whose rotated pair never reached its store.

        For ``claude_code`` / ``hermes_pkce`` the singleton file â€” not
        auth.json â€” is authoritative: ``_seed_from_singletons()`` re-reads it
        on every ``load_pool()``. When the refresh POST succeeded but the
        singleton write failed, the replacement pair exists only in memory
        while the consumed pair survives on disk and would be re-seeded over
        any row we persisted; the next refresh would replay the spent token.
        So never expose or persist the rotated pair; mark the entry terminally
        so it surfaces as an explicit re-auth requirement.
        u£   Anthropic %s refresh rotated the single-use token but could not commit it to %s (%s) â€” failing closed and quarantining the credential; re-authenticate to recoverr   )Ú"mark_rotation_consumed_uncommittedÚspent_rotation_source_path©rI  z/Failed to record consumed rotation fingerprintsT©Úexc_infoNz.rotated credential was not durably written to z: r  )rˆ  Úerrorr‡   r‘  r^  r_  rˆ   rŠ   r6   r‰  r  r  )r›   rä   r‹  rŸ  r^  r_  s         r7   Ú!_fail_closed_unpersisted_rotationz0CredentialPool._fail_closed_unpersisted_rotationø  s°   € ô$ 	�‰ð)ð �L‰L˜% ô		
ð	[÷ñ /Ø×"Ñ"Ø×#Ñ#Ù6°u·|±|ÓDõð 	×ÑØØä:ØKÈEÈ7ÐRTÐUXÐTYÐZñô	
ð øô ò 	[Ü�L‰LÐJÐUYˆLÖZð	[ús   ¤7A< Á< BÂBc                óÌ   — t         j                  | j                  d«      }t        j                  |d«      }t        t        t        j                  «      t        |«      dz   «      S )z^Configured refresh POST timeout plus margin, so a slow token endpoint cannot starve the flock.Ú(HERMES_ANTHROPIC_REFRESH_TIMEOUT_SECONDSé   g      @)Ú_REFRESH_TIMEOUT_ENV_VARSr    r}   rÁ   Ú	env_floatrñ   r
  ÚAUTH_LOCK_TIMEOUT_SECONDS)r›   Úenv_varÚrefresh_timeout_secondss      r7   rV  z/CredentialPool._single_use_refresh_lock_timeout,  sP   € ä+×/Ñ/°·±Ð?iÓjˆÜ"*×"4Ñ"4°W¸bÓ"AÐÜ”5œ×;Ñ;Ó<¼eÐD[Ó>\Ð_bÑ>bÓcÐcrB   c                ól  — |j                   dk(  rd}n|j                   dk(  rd}ny	 ddlm} |d   |d	   |d
   f}|j                   dk(  r! |j                  |d|j                  xs diŽ y |j
                  |dt        | |«      iŽ y# t        $ r"}t        | j                  |||¬«      «      ‚d}~ww xY w)a)  Write a rotated Anthropic pair to its authoritative singleton, or fail closed.

        claude_code -> ~/.claude/.credentials.json (so the fallback resolver
        and other profiles see it). hermes_pkce -> ~/.hermes/.anthropic_oauth.json
        (``_seed_from_singletons`` re-seeds it every load; a borrowed row commits
        to the ROOT's file, never a new profile-local copy, #100339). Not
        ``endswith``: manual:hermes_pkce is pool-owned and a singleton for it
        would be a second authority for the same refresh-token family.
        r¼  z~/.claude/.credentials.jsonrŽ  z~/.hermes/.anthropic_oauth.jsonNr   )Úanthropic_credentialsrˆ   rŠ   r�   Úspent_refresh_tokenr¨   Útarget©rŸ  )
r‡   Úagentrn  Ú_write_claude_code_credentialsrŠ   Ú_write_hermes_oauth_credentialsr“  r6   r¾  rd  )r›   rä   Ú	refreshedrŸ  ÚacÚargsÚwexcs          r7   Ú_commit_anthropic_rotationz)CredentialPool._commit_anthropic_rotation2  sÌ   € ð �<‰<˜=Ò(Ø1‰EØ�\‰\˜]Ò*Ø5‰Eàð	aÝ9Ø˜nÑ-¨y¸Ñ/IÈ9ÐUdÑKeÐfˆDØ�|‰|˜}Ò,Ø1�×1Ñ1°4ÐgÈU×M`ÑM`ÒMfÐdfÓgà2�×2Ñ2°DÐjÔA\Ð]aÐchÓAiÓjøÜò 	aô ˜t×EÑEÀeÈTÐY^ÐEÓ_Ó`Ð`ûð		aús   §AB Á+B Â	B3ÂB.Â.B3c           
     ó¢  — ddl m}m}m}  ||j                  «      } ||j
                  |¬«      s ||j                  |¬«      r3t        | j                  |t        d«      t        |xs d«      ¬«      «      ‚ ||j
                  |j                  j                  d«      ¬«      }t        ||d	   |d
   |d   ¬«      }| j                  ||«       |S )z\POST the Anthropic refresh, commit to the singleton, return the rotated (unpersisted) entry.r   )Ú is_rotation_consumed_uncommittedÚrefresh_anthropic_oauth_purer_  r`  zpcredential pair was rotated by another process but the rotation never committed (spent-rotation sidecar verdict)zcredential storerq  rŽ  ©Úuse_jsonrˆ   rŠ   r�   r   )r‘  r{  r|  r_  r‡   rŠ   rˆ   r¾  rd  ÚRuntimeErrorry   Úendswithr	   ry  )r›   rä   r{  r|  r_  rI  ru  r«  s           r7   Ú_refresh_anthropicz!CredentialPool._refresh_anthropicQ  sÝ   € ÷	
ñ 	
ñ 1°·±Ó>ˆÙ+¨E×,?Ñ,?È[ÕYÙ,¨U×-?Ñ-?È[ÕYä˜t×EÑEØÜðPóô ˜+Ò;Ð);Ó<ð  Fó  ó ð ñ 1°×1DÑ1DÈuÏ|É|×OdÑOdÐerÓOsÔtˆ	ÜØØ" >Ñ2Ø# OÑ4Ø# OÑ4ô	
ˆð 	×'Ñ'¨¨yÔ9ØˆrB   c                óÌ   — t         | j                     d   } t        t        |«      |j                  |j
                  «      }t        ||d   |d   |j                  d«      ¬«      S )zICodex / xAI: POST the refresh and return the rotated (unpersisted) entry.é   rˆ   rŠ   r‘   ©rˆ   rŠ   r‘   )r¹  r}   r¹   rÁ   rˆ   rŠ   r	   r    )r›   rä   Úrefresh_fn_nameru  s       r7   Ú_post_tokens_refreshz#CredentialPool._post_tokens_refreshq  sb   € ä5°d·m±mÑDÀQÑGˆØ6”GœH oÓ6°u×7IÑ7IÈ5×K^ÑK^Ó_ˆ	ÜØØ" >Ñ2Ø# OÑ4Ø"Ÿ™ ~Ó6ô	
ð 	
rB   c               ó"  — 	 | j                   dk(  r| j                  |«      }�n*| j                   t        v r#| j                  |«      }| j	                  |«      }nõt        | j                   «      x}�" ||«      }|st        d«      ‚t        ||«      }n¼| j                   dk(  r«|j                  xs |j                  xs |j                  }| j                  |«      }||urA|}|r=|j                  r1|j                  |k7  r"t        j                  d|j                  «       |S t        j                   ||xs d ¬«       | j                  |«      }n|S t+        |fi t,        ¤Ž}| j/                  ||«       | j1                  |j                  g¬«       | j3                  |«       |S # t"        $ r}|j$                  cY d }~S d }~wt&        $ rH}	t        j                  d| j                   |j                  |	«       | j)                  ||	«      cY d }	~	S d }	~	ww xY w)Nrw   z6provider refresh_credential returned no rotated fieldsr¿   z<Nous entry %s: adopting peer-rotated token, skipping refresh)Úforce_refreshÚstale_access_tokenz'Credential refresh failed for %s/%s: %sr™  )r}   r�  r¹  r<  r†  r   r  r   rÃ   r“   rˆ   rE  rˆ  r‰  rƒ   rÁ   Ú resolve_nous_runtime_credentialsr¾  rº   r6   Ú_recover_failed_refreshr	   ru   r  r  rL  )
r›   rä   rR  r«  Úplugin_refreshÚrotatedÚ	stale_keyrY  Údoner‹  s
             r7   rU  z"CredentialPool._refresh_entry_impl|  sØ  € ð	<Ø�}‰} Ò+Ø×1Ñ1°%Ó8’Ø—‘Ô"=Ñ=Ø×8Ñ8¸Ó?�Ø×3Ñ3°EÓ:‘Ü$7¸¿¹Ó$FÐF�.ÐSÙ(¨Ó/�Ùô 'Ð'_Ó`Ð`Ü5°e¸WÓE‘Ø—‘ &Ò(Ø!×1Ñ1ÒZ°U·_±_ÒZÈ×HZÑHZ�	Ø×>Ñ>¸uÓE�Ø Ñ&Ø"�Eñ  ×!6Ò!6¸5×;PÑ;PÐT]Ò;]ÜŸ™Ð%cÐej×emÑemÔnØ$˜Ü×9Ñ9ÈÐbkÒbsÐosÕtØ×?Ñ?ÀÓF‘à�ô ˜'Ñ.¤XÑ.ˆØ×Ñ˜E 7Ô+ð 	�‰¨'¯*©*¨ˆÔ6ð 	×2Ñ2°7Ô;Øˆøô ò 	Ø—;‘;ÕûÜò 	<Ü�L‰LÐBÀDÇMÁMÐSX×S[ÑS[Ð]`ÔaØ×/Ñ/°°sÓ;Õ;ûð	<ús6   ‚DF  Ä.F  Æ 	HÆ)F:Æ4HÆ:HÇ=H	ÈHÈ	Hc           	     ón  — | j                   dk(  �r‹|j                  dk(  rÒ| j                  |«      }|j                  |j                  k7  r€t        j                  d«       	 ddlm}  ||j                  |j                  j                  d«      ¬«      }| j                  ||«       | j                  ||d   |d	   |d
   t        dd¬«      S | j                  |«      smt        j                  d«       |S | j!                  |«      }|j                  |j                  k7  r,t        j                  d«        | j                  |fi t"        ¤ŽS ddlm}  ||«      �rƒt        j'                  d|j(                  xs |j*                  dd |«       | j-                  ||«       y| j                   t.        v ràt.        | j                      \  }	}
}	}| j1                  |«      }|j                  |j                  k7  r-t        j                  d|
«        | j                  |fi t"        ¤ŽS  t3        t4        |«      |«      �r¦t        j'                  d|
|| j                   «       | j7                  ||«       | j9                  |dh«       | j-                  ||«       y| j                   dk(  �r| j;                  |«      }|j                  |j                  k7  r?t        j                  d«        | j                  |fi t"        ¤Ž}| j=                  |«       |S t?        |t@        «      rt        j                  d«       |S t5        jB                  |«      r—t        j'                  d|«       | jE                  ||«       | j9                  |t4        jF                  dt4        jF                  › �h«       | j-                  ||«       ytI        | j                   «      �tK        | ||«      \  }}|r|S | jM                  |d«       y# t        $ r}|j                  cY d}~S d}~wt        $ r!}t        j                  d|«       Y d}~�Œçd}~ww xY w)a7  After a failed refresh POST: adopt a peer's rotation, quarantine a dead grant, or bench.

        Another process may have consumed the refresh token between our
        pre-POST sync and the HTTP call; re-read the provider's token
        authority once more and adopt fresher tokens before giving up.
        rw   r¼  z8Retrying refresh with synced token from credentials filer   )r|  rŽ  r}  rˆ   rŠ   r�   N)rˆ   rŠ   r�   rm   rn   ro   zRetry refresh also failed: %sz7Credentials file has valid token, using without refreshuK   Anthropic OAuth refresh failed but pool store has newer tokens â€” adopting)Ú#is_terminal_anthropic_refresh_errorz•Anthropic OAuth refresh token for %s is terminally invalid (%s); the credential leaves rotation. Re-run 'hermes auth add anthropic' to sign in again.é   uC   %s OAuth refresh failed but auth.json has newer tokens â€” adoptingz|%s OAuth refresh token is terminally invalid (%s); clearing local token state. Re-run 'hermes auth add %s' to sign in again.râ   r¿   u?   Nous refresh failed but auth.json has newer tokens â€” adoptingz>Nous refresh skipped: auth store lock busy; not benching entryzzNous refresh token is terminally invalid (%s); clearing local token state. Re-run 'hermes auth add nous' to sign in again.zmanual:)'r}   r‡   r$  rŠ   rˆ  r‰  r‘  r|  r€  ry  r  Ú	STATUS_OKr¾  rº   r6   rW  r0  ru   r‘  r°  r„   rƒ   Ú_mark_dead_refresh_grantr¹  r<  r¹   rÁ   Ú_clear_terminal_tokens_stater  rE  rL  rx   ÚTimeoutErrorÚ_is_terminal_nous_refresh_errorÚ_clear_terminal_nous_stateÚNOUS_DEVICE_CODE_SOURCEr   r   r  )r›   rä   r‹  rY  r|  ru  r�  Ú	retry_excr‘  Ú_r-  Úterminal_fn_namer«  Úhandledrº   s                  r7   r‹  z&CredentialPool._recover_failed_refresh¬  sÌ  € ð �=‰=˜KÓ'Ø�|‰|˜}Ò,Ø×IÑIÈ%ÓP�Ø×'Ñ'¨5×+>Ñ+>Ò>Ü—L‘LÐ![Ô\ðQÝ\Ù$@Ø"×0Ñ0¸6¿=¹=×;QÑ;QÐR_Ó;`ô%˜	ð ×7Ñ7¸À	ÔJØ#Ÿ{™{Ø"Ø)2°>Ñ)BØ*3°OÑ*DØ*3°OÑ*DÜ(1Ø+/Ø,0ð  +ó  ð ð ×2Ñ2°6Ô:Ü—L‘LÐ!ZÔ[Ø!�Mð ×9Ñ9¸%Ó@�Ø×'Ñ'¨5×+>Ñ+>Ò>Ü—L‘LÐ!nÔoØ&˜4Ÿ;™; vÑ:´Ñ:Ð:ÝWÙ2°3Õ7ô —‘ð\à—K‘KÒ/ 5§8¡8¨B¨Q <°ô6ð ×-Ñ-¨e°SÔ9ØØ�]‰]Ô9Ñ9Ü.IÈ$Ï-É-Ñ.XÑ+ˆAˆw˜Ð+Ø×5Ñ5°eÓ<ˆFØ×#Ñ# u×':Ñ':Ò:Ü—‘ÐbÐdkÔlØ"�t—{‘{ 6Ñ6¬XÑ6Ð6ð
 3Œw”xÐ!1Ó2°3Õ7ô —‘ðDØELÈcÐSW×S`ÑS`ôbð ×1Ñ1°%¸Ô=Ø×(Ñ(¨°°Ô@Ø×-Ñ-¨e°SÔ9ØØ�]‰]˜fÓ$Ø×:Ñ:¸5ÓAˆFØ×#Ñ# u×':Ñ':Ò:Ü—‘Ð^Ô_Ø%˜$Ÿ+™+ fÑ9´Ñ9�Ø×:Ñ:¸7ÔCØ�Ü˜#œ|Ô,ô
 —‘Ð]Ô^Ø�Ü×7Ñ7¸Ô<Ü—‘ðFØGJôLð ×/Ñ/°°sÔ;Ø×(Ñ(ØÜ×5Ñ5¸Ä×AaÑAaÐ@bÐ7cÐdôð ×-Ñ-¨e°SÔ9ØÜ  §¡Ó/Ð;Ü;¸DÀ%ÈÓM‰OˆG�VÙØ�Ø×Ñ˜U DÔ)ØøôW (ò +Ø#Ÿ{™{Õ*ûÜ$ò QÜŸ™Ð%DÀi×PÒPûðQús+   Á A)O- Ï-	P4Ï6PÐP4ÐP4ÐP/Ð/P4c                ó`  ‡— | j                   5  t        ˆfd„| j                  D «       d«      }|�|j                  t        k(  r
	 ddd«       y| j                  |t        t        j                  «       dt        t        |dd«      xs d«      t        |«      ¬«       ddd«       y# 1 sw Y   yxY w)a»  Mark a row whose refresh token was terminally rejected DEAD, if the quarantine kept it.

        ``_quarantine_sources`` drops only singleton-seeded rows; an independent ``manual:*`` login
        (``hermes auth add``) survives, and an unmarked survivor re-enters rotation and re-fires the
        terminal WARNING on every later refresh attempt. DEAD leaves rotation until a write-side
        re-auth sync clears it (never via TTL).
        c              3  óV   •K  — | ]   }|j                   ‰j                   k(  sŒ|–— Œ" y ­wr™   ©rƒ   )rì   r  rä   s     €r7   rí   z:CredentialPool._mark_dead_refresh_grant.<locals>.<genexpr>   s    øè ø€ ÒR T¸d¿g¹gÈÏÉÓ>QœDÑRùs   ƒ)¢)NÚcoderI   )rm   rn   ro   rp   rq   )	rÐ  rS  rË  rm   rå  r  r  ry   r¹   )r›   rä   r‹  r÷  s    `  r7   r”  z'CredentialPool._mark_dead_refresh_grant  s™   ø€ ð �Z‰Zñ 	ÜÓR¨T¯]©]ÔRÐTXÓYˆGØˆ '×"5Ñ"5¼Ò"DØ÷	ð 	ð �K‰KØÜ'Ü#Ÿy™y›{Ø $Ü"%¤g¨c°6¸4Ó&@Ò&SÀOÓ"TÜ#& s£8ð ô ÷		÷ 	ñ 	ús   Ž6B$ÁAB$Â$B-c           	     óx  — t         | j                     d   }	 t        «       5  t        «       }t	        || j                  «      xs i }t        |t        «      r|j                  d«      xs i nd}t        |t        «      rüt        |j                  d«      xs d«      j                  «       }|r*|t        |j                  xs d«      j                  «       k(  r¤|j                  dd«       |j                  dd«       ||d<   | j                  t        |dd«      t        |«      d	d
t        j                  t        j                   «      j#                  «       dœ|d<   t%        || j                  |«       t'        |«       ddd«       y# 1 sw Y   yxY w# t(        $ r!}t*        j-                  d||«       Y d}~yd}~ww xY w)zSDrop the dead Codex/xAI token pair from auth.json unless a peer already rotated it.rð   r3  NrŠ   r¨   rˆ   r¡  ÚunknownÚcredential_pool_refresh_failureT)r}   r¡  r  r  Úrelogin_requiredÚatÚlast_auth_errorz+Failed to clear terminal %s OAuth state: %s)r¹  r}   r#   r'   r(   rx   rÜ   r    ry   rÀ   rŠ   r  r¹   r
   Únowr   ÚutcÚ	isoformatr.   r-   r6   rˆ  r‰  )	r›   rä   r‹  r-  rH  r  r3  r8  Ú	clear_excs	            r7   r•  z+CredentialPool._clear_terminal_tokens_state,  sn  € ä-¨d¯m©mÑ<¸QÑ?ˆð	\Ü!Ó#ñ 5Ü-Ó/�
Ü,¨Z¸¿¹ÓGÒMÈ2�Ü8BÀ5Ì$Ô8O˜%Ÿ)™) HÓ-Ò3±ÐUY�Ü˜f¤dÔ+Ü$'¨¯
©
°?Ó(CÒ(IÀrÓ$J×$PÑ$PÓ$R�MÙ(¨M¼SÀ×ATÑATÒAZÐXZÓ=[×=aÑ=aÓ=cÒ,cØŸ
™
 >°4Ô8ØŸ
™
 ?°DÔ9Ø*0˜˜h™à(,¯©Ü$+¨C°¸Ó$CÜ'*¨3£xØ&GØ04Ü"*§,¡,¬x¯|©|Ó"<×"FÑ"FÓ"Hñ4˜Ð/Ñ0ô -¨Z¸¿¹ÈÔNÜ(¨Ô4÷'5÷ 5ñ 5ûô( ò 	\Ü�L‰LÐFÈÐQZ×[Ñ[ûð	\ús5   ˜
F ¢EFÅ:F ÆFÆF ÆF Æ	F9ÆF4Æ4F9c                ó–  — 	 t        «       5  t        «       }t        |d«      xsE |j                  |j                  |j
                  |j                  |j                  |j                  dœ}t        |j                  d«      xs d«      j                  «       }|r*|t        |j                  xs d«      j                  «       k(  rHt        j                  ||d¬«       t        j                  ||d¬«       t!        |d|«       t#        |«       d d d «       y # 1 sw Y   y xY w# t$        $ r }t&        j)                  d|«       Y d }~y d }~ww xY w)Nr¿   )r^   rh   r’   ra   r[   rZ   rŠ   r¨   r¤  )r  z-Failed to clear terminal Nous OAuth state: %s)r#   r'   r(   r^   rh   r’   ra   r[   rZ   ry   r    rÀ   rŠ   rÁ   Ú_quarantine_nous_oauth_stateÚ_quarantine_nous_pool_entriesr.   r-   r6   rˆ  r‰  )r›   rä   r‹  rH  r  r8  r«  s          r7   r˜  z)CredentialPool._clear_terminal_nous_stateG  s  € ð	UÜ!Ó#ñ 1Ü-Ó/�
Ü,¨Z¸Ó@ò Ø!&§¡Ø',×'<Ñ'<Ø*/×*BÑ*BØ"'×"2Ñ"2Ø"Ÿ[™[Ø Ÿ9™9ñE�ô !$ E§I¡I¨oÓ$>Ò$DÀ"Ó E× KÑ KÓ M�Ù$¨¼¸U×=PÑ=PÒ=VÐTVÓ9W×9]Ñ9]Ó9_Ò(_Ü×9Ñ9¸%ÀÐMnÕoÜ×:Ñ:¸:ÀsÐStÕuÜ(¨°V¸UÔCÜ$ ZÔ0÷1÷ 1ñ 1ûô  ò 	UÜ�L‰LÐHÈ)×TÑTûð	Uús5   ‚
D ŒC>DÄ
D ÄDÄD ÄD Ä	EÄ(EÅEc                ór  — | j                   dk7  s|j                  t        k7  ryt        j                  |j
                  |j                  |j                  «      sy|j                  xs d}|sy	 t        j                  ||j                  «      }|rY| j                  ||d   |d   |j                  d«      xs |j                  ¬«      }| j                  |«       |j                  xs |}t        t        j                   ||j"                  ¬«      «      S # t$        $ r t&        j)                  d	d
¬«       Y y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 out
        (weekly windows), but the window can reopen before then (redeemed
        reset, plan upgrade, OpenAI reset) â€” issue #43747. Only fires for
        429/quota-shaped errors; the probe is throttled per token (5 min) so
        it is safe on the hot selection path.
        rÅ   Fr¨   rˆ   rŠ   r‘   r„  rn  z!Codex quota-restored probe failedTra  )r}   rm   r#  rÁ   Ú_is_codex_rate_limit_shapedro   rp   rq   rˆ   Ú"_refresh_expired_codex_probe_tokenrŠ   r  r    r‘   rL  r_  Ú_probe_codex_quota_restoredrŽ   r6   rˆ  r‰  )r›   rä   r~   Úfreshs       r7   Ú_codex_quota_restored_upstreamz-CredentialPool._codex_quota_restored_upstream\  s"  € ð �=‰=˜NÒ*¨e×.?Ñ.?ÔCSÒ.SØÜ×3Ñ3Ø×!Ñ! 5×#:Ñ#:¸E×<TÑ<Tô
ð Ø×"Ñ"Ò( bˆÙØð	ô ×?Ñ?ÀÀu×GZÑGZÓ[ˆEÙð
 Ÿ™Ø¨¨nÑ(=ÈUÐSbÑMcØ!&§¡¨>Ó!:Ò!P¸e×>PÑ>Pð $ó �ð ×:Ñ:¸5ÔAØ×*Ñ*Ò3¨e�Üœ×<Ñ<¸UÈUÏ^É^Ô\Ó]Ð]øÜò 	Ü�L‰LÐ<ÀtˆLÔLÙð	ús   Á.B$D Ä D6Ä5D6c                óÀ  — |j                   t        k7  ry| j                  dk(  rF|j                  €yt	        |j                  «      t	        t        j
                  «       dz  «      dz   k  S | j                  dk(  rt        |j                  t        «      S | j                  dk(  r=t        j                  |j                  t        j                  |j                  «      «      S y)NFrw   éè  iÀÔ rÅ   r¸  )r   r{   r}   r�   r…   r  r$   rˆ   r    rÁ   Ú_xai_access_token_is_expiringÚ#_xai_proactive_refresh_skew_seconds)r›   rä   s     r7   rW  z#CredentialPool._entry_needs_refresh‚  s¸   € Ø�?‰?œoÒ-ØØ�=‰=˜KÒ'Ø×"Ñ"Ð*ØÜ�u×*Ñ*Ó+¬s´4·9±9³;ÀÑ3EÓ/FÈÑ/PÑPÐPØ�=‰=˜NÒ*Ü2°5×3EÑ3EÔGnÓoÐoØ�=‰=˜KÒ'Ü×9Ñ9Ø×"Ñ"¤H×$PÑ$PÐQV×QcÑQcÓ$dóð ð
 rB   c               ó–   — | j                  |¬«      \  }}|r(| j                  |«       |€| j                  |¬«      \  }}|�d| _        |S )NrÛ  r   )Ú_select_under_lockÚ_refresh_pending_entriesrÕ  )r›   rÜ  rä   Úpending_refreshr›  s        r7   ÚselectzCredentialPool.select•  sZ   € Ø!%×!8Ñ!8¸uÐ!8Ó!EÑˆˆÙØ×)Ñ)¨/Ô:àˆ}Ø×2Ñ2¸Ð2Ó?‘��qØÐØ./ˆDÔ+ØˆrB   c               ój   — | j                   5  | j                  |¬«      cd d d «       S # 1 sw Y   y xY w)NrÛ  )rÐ  Ú_select_unlocked)r›   rÜ  s     r7   rº  z!CredentialPool._select_under_lock   s/   € Ø�Z‰Zñ 	6Ø×(Ñ(¨uÐ(Ó5÷	6÷ 	6ò 	6ús   �)©2c                ó8   — |D ]  }| j                  |d¬«       Œ y)a  Refresh deferred single-use-token entries OUTSIDE the pool lock.

        Each refresh takes the cross-process ``_auth_store_lock`` (20+ s
        possible) and merges into the pool through the self-locking mutation
        primitives; failures are silently skipped.
        FrQ  N)rZ  )r›   Úpendingrä   s      r7   r»  z'CredentialPool._refresh_pending_entries¤  s&   € ð ò 	4ˆEØ×Ñ ¨UÐÕ3ñ	4rB   c                ó2  ‡— 	 t        ˆfd„t        | j                  «      D «       d«      }t        |xs i j	                  d«      «      }|r|‰j                  xs dkD  r|S dS # t
        $ r+}t        j                  d‰j                  |«       Y d}~yd}~ww xY w)z`Epoch of a ``hermes auth reset`` persisted by another process AFTER *entry*'s status, else None.c              3  ó~   •K  — | ]4  }t        |t        «      r"|j                  d «      ‰j                  k(  r|–— Œ6 y­wr(  r)  r*  s     €r7   rí   z6CredentialPool._reset_cleared_after.<locals>.<genexpr>±  s7   øè ø€ ò L˜aÜ% a¬Ô.°1·5±5¸³;À%Ç(Á(Ò3Jô ñ Lùs   ƒ:=Nr�   z.Pool entry %s: could not read reset marker: %sg        )
rS  r0   r}   r©   r    r6   rˆ  r‰  rƒ   rn   )r›   rä   Úrowr¥  r‹  s    `   r7   Ú_reset_cleared_afterz#CredentialPool._reset_cleared_after®  s“   ø€ ð	Üó LÔ#7¸¿¹Ó#Fô LØMQóSˆCä/°²¸·±Ð@SÓ0TÓUˆGñ " g°×1EÑ1EÒ1LÈÒ&MˆwÐWÐSWÐWøô ò 	Ü�L‰LÐIÈ5Ï8É8ÐUXÔYÜûð	ús   ƒAA" Á"	BÁ+!BÂBc                ó”  — |j                   t        t        hvr|S | j                  |«      }|� | j                  |fddit
        ¤d|i¤ŽS |j                  t        j                  | j                  «      k7  r|S | j                  dk(  r| j                  |«      S | j                  dk(  r| j                  |«      S | j                  |«      S )aÞ  Re-read an exhausted/DEAD singleton-seeded entry from its token authority.

        The user may have re-authed (``hermes model`` / ``hermes auth``, the
        Claude Code CLI, another profile) leaving fresh tokens on disk while
        the pool entry is frozen behind ``last_error_reset_at``. A ``hermes auth
        reset`` run from another process while this pool is live is honoured the
        same way (#89415): the in-memory cooldown would otherwise outlive it.
        r  Fr�   rw   r¿   )rm   r#  rå  rÅ  r  ru   r‡   Ú_RESYNC_SOURCEr    r}   r$  rE  r<  )r›   rä   Ú
cleared_ats      r7   Ú_resync_stale_entryz"CredentialPool._resync_stale_entry¹  s¿   € ð ×ÑÔ%5´{Ð$CÑCØˆLØ×.Ñ.¨uÓ5ˆ
ØÐ!Ø�4—;‘;˜uÑ^¨eÐ^´xÑ^ÐS]Ò^Ð^Ø�<‰<œ>×-Ñ-¨d¯m©mÓ<Ò<ØˆLØ�=‰=˜KÒ'Ø×CÑCÀEÓJÐJØ�=‰=˜FÒ"Ø×8Ñ8¸Ó?Ð?Ø×/Ñ/°Ó6Ð6rB   F©Úclear_expiredÚrefreshrÜ  c               ó  — t        j                   «       }d}g }g }g }| j                  «       }	| j                  D �]î  }
|
j                  t        k(  r|
j
                  sŒ$| j                  |
«      }||
ur|}
d}|
j                  t        k(  r£t        |
j                  «      r�|
j                  xs d}|r{||z
  t        kD  rot        j                  d|
j                  xs |
j                   dd |
j"                  xs d||z
  dz  | j$                  «       |j'                  |
j                   «       d}Œót)        |
|«      ��Œ|
j                  t*        k(  rFt-        |
|	¬	«      }|�||k  r|r| j/                  |
«      s�Œ=|r | j0                  |
fd
dit2        ¤Ž}
d}|rO| j5                  |
«      r>| j$                  t6        v r|j'                  |
«       �Œ’| j9                  |
d¬«      }|€�Œ©|}
|
j                  t:        k(  r |
j<                  xs dj?                  «       s�ŒÞ|j'                  |
«       �Œñ |r9tA        |«      }| j                  D �cg c]  }|j                   |vsŒ|‘Œ c}| _        |r| jC                  |¬«       ||fS c c}w )u  Return (available, pending_refresh) for entries not in cooldown.

        *clear_expired* resets elapsed cooldowns to STATUS_OK and persists.
        *refresh* refreshes entries needing a token refresh (skipped on
        failure) â€” except single-use-token providers (openai-codex,
        xai-oauth), which are returned as *pending_refresh* so the caller
        refreshes them outside the lock instead of stalling every pool
        consumer during cross-process flock acquisition + OAuth network I/O.
        FTr   uh   credential pool: pruning DEAD manual entry %s (reason=%s, age=%.1fh) â€” re-add via `hermes auth add %s`Nr’  r£  g      ¬@r!  r  rQ  r¨   r
  )"r  rç  rË  r   r|   rÃ   rÉ  rm   rå  rø   r‡   rn   ÚDEAD_MANUAL_PRUNE_TTL_SECONDSrˆ  r°  r„   rƒ   rp   r}   r?  r   r#  r$  r´  r  ru   rW  r¹  rZ  r{   rˆ   rÀ   r8  r  )r›   rË  rÌ  rÜ  r¨  Úcleared_anyÚentries_to_prunerß  r¼  rú   rä   rY  Údead_atÚexhausted_untilru  Ú
pruned_idsrí  s                    r7   rÞ  z!CredentialPool._available_entriesÏ  so  € ô �i‰i‹kˆØˆØ&(ÐØ,.ˆ	Ø24ˆØ×2Ñ2Ó4ˆØ—]‘]ó 9	$ˆEð �‰Ô"3Ò3¸E×<QÒ<QØØ×-Ñ-¨eÓ4ˆFØ˜UÑ"Ø�Ø"�Ø× Ñ ¤KÒ/ô
 % U§\¡\Ô2Ø#×2Ñ2Ò7°a�GÙ 3¨¡=Ô3PÒ#PÜŸ™ðYà!ŸK™KÒ7¨5¯8©8°B°Q¨<Ø!×3Ñ3Ò@°yØ  7™]¨fÑ4Ø ŸM™Môð )×/Ñ/°·±Ô9Ø&*˜ØÜ# E¨5Ó1Ð=ÙØ× Ñ Ô$4Ò4Ü"2°5È/Ô"Z�ð $Ð/Ø˜oÒ-Ù*¨t×/RÑ/RÐSXÔ/YáÙ Ø'˜DŸK™K¨ÑI°uÐIÄÑI�EØ"&�KÙ˜4×4Ñ4°UÔ;Ø—=‘=Ô$?Ñ?Ø#×*Ñ*¨5Ô1ÙØ ×/Ñ/°¸UÐ/ÓC�	ØÐ$ÙØ!�Ø�‰¤/Ò1¸5×;MÑ;MÒ;SÐQS×:ZÑ:ZÔ:\ñ Ø×Ñ˜UÖ#ðs9	$ñt ÜÐ-Ó.ˆJØ(,¯©ÖP 1¸¿¹ÀZÒ9OšQÒPˆDŒMÙØ�M‰MÐ&6ˆMÔ7Ø˜/Ð)Ð)ùò Qs   ÉI>ÉI>c                óš   — t        j                  «       }| j                  }|�||z
  t        k  ry|| _        t        j                  d«       y)z?Emit the empty-pool INFO line at most once per throttle window.Nz>credential pool: no available entries (all exhausted or empty))r  Ú	monotonicrÔ  Ú)NO_AVAILABLE_ENTRIES_LOG_THROTTLE_SECONDSrˆ  r4  )r›   r¨  Úlasts      r7   Ú_log_no_available_entriesz(CredentialPool._log_no_available_entries"  sC   € ä�n‰nÓˆØ×+Ñ+ˆØÐ  t¡Ô/XÒ XØØ'*ˆÔ$Ü�‰ÐTÕUrB   )rÌ  ÚcountrÜ  c               ót  ‡	— | j                  d||¬«      \  }}|sd| _        | j                  «        d|fS d| _        | j                  t
        k(  rt        j                  |«      Š	n5| j                  t        k(  rt        |«      dkD  rt        |d„ ¬«      Š	n|d   Š	|r!| j                  ‰	d‰	j                  dz   ¬	«      Š	| j                  t        k(  rÉt        |«      dkD  r»| j                  D �cg c]  }|j                  ‰	j                  k7  sŒ|‘Œ  }}|j!                  t#        ‰	t        | j                  «      dz
  ¬
«      «       t%        |«      D ��cg c]  \  }}t#        ||¬
«      ‘Œ c}}| _        | j'                  «        | j)                  ˆ	fd„«      xs ‰	Š	‰	j                  | _        ‰	|fS c c}w c c}}w )zèSelect the best available entry; returns ``(entry, pending_refresh)``.

        ``count=False`` skips the ``request_count`` bump for selections that are
        not going to serve a request (a forced-refresh target lookup).
        TrÊ  Nrð   c                ó   — | j                   S r™   )r•   )rí  s    r7   rA   z1CredentialPool._select_unlocked.<locals>.<lambda>@  s
   € °·±€ rB   rÊ  r   F)r  r•   rê   c                ó6   •— | j                   ‰j                   k(  S r™   r   )rv  rä   s    €r7   rA   z1CredentialPool._select_unlocked.<locals>.<lambda>M  s   ø€ °·±ÀÇÁÑ1I€ rB   )rÞ  rÌ  rØ  rÔ  rÍ  ÚSTRATEGY_RANDOMrU   ÚchoiceÚSTRATEGY_LEAST_USEDr:  r  r  r•   ÚSTRATEGY_ROUND_ROBINrË  rƒ   r?  r	   rþ  r  rò  )
r›   rÌ  rÙ  rÜ  rß  r¼  rv  r�  r  rä   s
            @r7   r¿  zCredentialPool._select_unlocked+  s~  ø€ ð &*×%<Ñ%<È4ÐY`ÐhmÐ%<Ó%nÑ"ˆ	�?ÙØ#ˆDÔØ×*Ñ*Ô,Ø˜Ð(Ð(ð (,ˆÔ$à�>‰>œ_Ò,Ü—M‘M )Ó,‰EØ�^‰^Ô2Ò2´s¸9³~ÈÒ7IÜ˜	Ñ'@ÔA‰Eà˜a‘LˆEñ Ø—K‘K ¨uÀE×DWÑDWÐZ[ÑD[�KÓ\ˆEØ�>‰>Ô1Ò1´c¸)³nÀqÒ6HØ26·-±-Ö\ YÀ9Ç<Á<ÐSX×S[ÑS[ÓC[’yÐ\ˆGÐ\Ø�N‰Nœ7 5´3°t·}±}Ó3EÈÑ3IÔJÔKÜT]Ð^eÓTf×gÁ.À#ÀyœW Y¸Ö=ÓgˆDŒMØ�M‰MŒOØ—J‘JÓIÓJÒSÈeˆEØ Ÿ8™8ˆÔØ�oÐ%Ð%ùò ]ùãgs   Ã-F/ÄF/ÅF4c                ó¸   — | j                   5  | j                  «       }|�|cd d d «       S | j                  «       \  }}|r|d   nd cd d d «       S # 1 sw Y   y xY w©Nr   )rÐ  rõ  rÞ  )r›   r÷  rß  rà  s       r7   ÚpeekzCredentialPool.peekQ  s_   € Ø�Z‰Zñ 	7Ø×,Ñ,Ó.ˆGØÐ"Ø÷	7ñ 	7ð #'×"9Ñ"9Ó";ÑˆI�xÙ#,�9˜Q’<°$÷	7÷ 	7ò 	7ús   �A«AÁAc               óž  ‡— | j                   5  | j                  dd|¬«      \  }}ddd«       t        ˆfd„D «       «      r[| j                  |D �cg c]  }|j                  ‰k(  sŒ|‘Œ c}«       | j                   5  | j                  dd|¬«      \  }}ddd«       t        ˆfd„D «       d«      S # 1 sw Y   Œ�xY wc c}w # 1 sw Y   Œ/xY w)a  Entry *credential_id* once its cooldown has lifted (cleared and token-refreshed the way
        ``select`` would), else ``None``. Never bumps ``request_count`` or round-robin order: a
        live session asking "may I go back?" every turn is not a request.TrÊ  Nc              3  ó<   •K  — | ]  }|j                   ‰k(  –— Œ y ­wr™   r   ©rì   rí  Úcredential_ids     €r7   rí   z)CredentialPool.reclaim.<locals>.<genexpr>_  s   øè ø€ Ò6¨ˆq�t‰t�}Õ$Ñ6ùs   ƒc              3  óB   •K  — | ]  }|j                   ‰k(  sŒ|–— Œ y ­wr™   r   ræ  s     €r7   rí   z)CredentialPool.reclaim.<locals>.<genexpr>c  s   øè ø€ ÒC˜1¨Q¯T©T°]Ó-B”QÑCùs   ƒ˜)rÐ  rÞ  r@  r»  rƒ   rS  )r›   rç  rÜ  rß  rÁ  rí  rà  s    `     r7   ÚreclaimzCredentialPool.reclaimY  sË   ø€ ð �Z‰Zñ 	hØ!%×!8Ñ!8ÀtÐUYÐafÐ!8Ó!gÑˆI�w÷	häÓ6¨gÔ6Ô6Ø×)Ñ)°gÖ*W°ÀÇÁÈÓAVª1Ò*WÔXØ—‘ñ mØ&*×&=Ñ&=ÈDÐZ^ÐfkÐ&=Ó&lÑ#�	˜8÷mäÓC 	ÔCÀTÓJÐJ÷	hð 	hüò +X÷mð mús#   ŽB2ÁB>Á&B>Á=CÂ2B;ÃCc                óZ  ‡‡— d}‰rŒ| j                  ˆfd„«      }|�v‰rt|j                  ‰k7  re| j                  ˆfd„«      }|�Mt        j                  d|j                  xs |j
                  dd |j                  xs |j
                  dd «       |}|€‰r| j                  ˆfd„«      }|S )zJResolve the entry that issued a failed request from its supplied identity.Nc                ó"   •— | j                   ‰k(  S r™   r   ©rí  rç  s    €r7   rA   z7CredentialPool._identify_failed_entry.<locals>.<lambda>m  ó   ø€ ¨¯©°Ñ)>€ rB   c                ó"   •— | j                   ‰k(  S r™   ©rÃ   ©rí  rú  s    €r7   rA   z7CredentialPool._identify_failed_entry.<locals>.<lambda>s  s   ø€ °!×2CÑ2CÀ|Ñ2S€ rB   z‡credential pool: credential_id %s runtime key does not match api_key_hint; attributing failure to key-matched entry %s instead (#79156)r’  c                ó"   •— | j                   ‰k(  S r™   rï  rð  s    €r7   rA   z7CredentialPool._identify_failed_entry.<locals>.<lambda>ƒ  s   ø€ ¨×):Ñ):¸lÑ)J€ rB   )rò  rÃ   rˆ  r4  r„   rƒ   )r›   rç  rú  rä   Ú
hint_entrys    ``  r7   Ú_identify_failed_entryz%CredentialPool._identify_failed_entryg  s¯   ù€ ð ˆÙØ—J‘JÓ>Ó?ˆEð
 Ð ¡\°e×6KÑ6KÈ|Ò6[Ø!ŸZ™ZÓ(SÓT�
ØÐ)Ü—K‘KðCð Ÿ™Ò4¨¯©°°!¨Ø#×)Ñ)Ò>¨Z¯]©]¸2¸AÐ->ôð #�Øˆ=™\ð —J‘JÓJÓKˆEØˆrB   c                óè  — | xj                   dz  c_         t        | j                  «       d   «      }| j                   t        |d«      kD  r;t        j                  d| j                  | j                   |«       d| _         d| _        yt        j                  d| j                  «       d| _        | j                  d¬«      \  }}|�.t        | j                  «       d   «      dk(  rd| _         d| _        y|S )a`  Rotate without marking anything when the failed identity matches no entry.

        Falling through to current()/_select_unlocked() would bench an
        innocent healthy key for the full TTL. But this must be BOUNDED
        (#70401): with OAuth-token auth the 401's key hint never matches any
        ``runtime_api_key``, so every retry lands here, nothing is marked, and
        the caller retries the same dead token forever (~6/sec, starving the
        event loop). Cap consecutive no-mark rotations at one lap of the
        available entries, then surface the error; no cooldown is written.
        rð   r   u�   credential pool: failed credential identity matched no %s entry for %d consecutive rotations (pool size %d) â€” surfacing the error instead of rotating againNzrcredential pool: failed credential identity matched no %s entry; rotating without marking any credential exhaustedF©rÌ  )
rÕ  r:  rÞ  rñ   rˆ  r°  r}   rÌ  r4  r¿  )r›   Úavailable_countÚ
next_entryrà  s       r7   Ú_rotate_unmatchedz CredentialPool._rotate_unmatched†  sò   € ð 	×'Ò'¨1Ñ,Õ'Ü˜d×5Ñ5Ó7¸Ñ:Ó;ˆØ×*Ñ*¬S°À!Ó-DÒDÜ�N‰Nð@ð —‘˜t×>Ñ>Àô	ð /0ˆDÔ+Ø#ˆDÔØÜ�‰ðGà�M‰Mô	
ð
  ˆÔØ#×4Ñ4¸UÐ4ÓCÑˆ
�HØÐ!¤c¨$×*AÑ*AÓ*CÀAÑ*FÓ&GÈ1Ò&Lð /0ˆDÔ+Ø#ˆDÔØØÐrB   )r  rú  rç  rg   rÜ  c          	     ót  ‡— | j                   5  t        |xs |«      }| j                  ||«      Š‰€|r| j                  «       cd d d «       S d| _        ‰€'| j                  «       xs | j                  d¬«      d   Š‰€
	 d d d «       y ‰j                  xs ‰j                  d d }| j                  |||«      rT| j                  ‰|||¬«       t        j                  d||«       d | _        | j                  d|¬«      \  }	}
|	cd d d «       S | j                  ‰|||¬«       ‰j                  }|rt|rr| j                   D �cg c].  }|j                  ‰j                  k7  sŒ|j                  |k(  sŒ-|‘Œ0 }}|D ]  }| j                  |||d|¬«       Œ |r| j#                  «        | j%                  ˆfd	„«      xs ‰}|j&                  t(        k(  r't        j+                  d
|||j,                  xs d«       nt        j                  d||«       d | _        | j                  d¬«      \  }	}
|	�@|	j                  ‰j                  k(  r't        j+                  d|«       d | _        	 d d d «       y |	r1t        j                  d|	j                  xs |	j                  d d «       |	cd d d «       S c c}w # 1 sw Y   y xY w)Nr   Frõ  r’  )rg   zIcredential pool: %s unavailable for model %s; other models stay available)rÌ  rÜ  r  c                ó6   •— | j                   ‰j                   k(  S r™   r   )rí  rä   s    €r7   rA   z:CredentialPool.mark_exhausted_and_rotate.<locals>.<lambda>ß  s   ø€ °·±¸¿¹Ñ1A€ rB   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), rotatinguo   credential pool: rotation returned the just-marked entry %s â€” treating as no-recovery so the failure surfaceszcredential pool: rotated to %s)rÐ  r_  ró  rø  rÕ  rõ  r¿  r„   rƒ   Ú_is_model_scoped_failureÚ_cool_down_modelrˆ  r4  rÌ  r  rÃ   rË  r  rò  rm   rå  r°  rp   )r›   r  r  rú  rç  rg   rÜ  Úidentity_suppliedÚ_labelr÷  rà  Úfailed_runtime_keyÚsÚsiblingsÚsiblingÚupdated_entryrä   s                   @r7   Úmark_exhausted_and_rotatez(CredentialPool.mark_exhausted_and_rotate­  sä  ø€ ð �Z‰Zñ B	Ü $ ]Ò%B°lÓ CÐØ×/Ñ/°¸|ÓLˆEØˆ}Ñ!2Ø×-Ñ-Ó/÷	B	ñ B	ð /0ˆDÔ+Øˆ}Ø×.Ñ.Ó0Ò[°D×4IÑ4IÐRWÐ4IÓ4XÐYZÑ4[�Øˆ}Ø÷B	ð B	ð —[‘[Ò0 E§H¡H¨R¨a LˆFØ×,Ñ,¨[¸%ÀÔPð ×%Ñ% e¨U°MÐR`Ð%ÔaÜ—‘ÐgÐioÐqvÔwØ#'�Ô Ø'+×'<Ñ'<ÀUÐRWÐ'<Ó'XÑ$�
˜HØ!÷)B	ñ B	ð* × Ñ  ¨°]ÐSaÐ Ôbð "'×!6Ñ!6ÐÙ Ñ%7à#Ÿ}™}öØ°·±¸¿¹Ó0@ÀQ×EVÑEVÐZlÓEl’Að�ð ð  (ò �GØ×(Ñ(Ø ¨mÀUÐ[ið )õ ðñ Ø—M‘M”Oà ŸJ™JÓ'AÓBÒKÀeˆMØ×(Ñ(¬KÒ7Ü—‘ðSà˜K¨×)HÑ)HÒ)UÈIõô —‘ÐYÐ[aÐcnÔoØ#ˆDÔØ#'×#8Ñ#8ÀÐ#8Ó#GÑ ˆJ˜ØÐ%¨*¯-©-¸5¿8¹8Ò*Cô —‘ðFØGMôð $(�Ô Ø÷B	ð B	ñ@ Ü—‘Ð<¸j×>NÑ>NÒ>cÐR\×R_ÑR_Ð`bÐabÐRcÔdØ÷EB	ñ B	ùò<÷=B	ð B	úsC   Ž5J.Á3J.Â	A:J.Ä3J.Å J)ÅJ)Å/J)Å3C/J.É+4J.Ê)J.Ê.J7c                ó€   — | j                  |«      \  }}|r'| j                  |«       |€| j                  |«      \  }}|S )a3  Acquire a soft lease on a credential.

        With *credential_id*, lease that entry directly. Otherwise prefer the
        least-leased available credential (priority as tie-breaker); when
        every credential is at the soft cap, still return the least-leased
        one instead of blocking.
        )Ú_acquire_lease_under_lockr»  )r›   rç  Ú	chosen_idr¼  r›  s        r7   Úacquire_leasezCredentialPool.acquire_leaseý  sL   € ð &*×%CÑ%CÀMÓ%RÑ"ˆ	�?ÙØ×)Ñ)¨/Ô:ð Ð Ø#×=Ñ=¸mÓL‘�	˜1ØÐrB   c                óŒ  ‡ — ‰ j                   5  |r@‰ j                  j                  |d«      dz   ‰ j                  |<   |‰ _        |g fcd d d «       S ‰ j	                  dd¬«      \  }}|sd |fcd d d «       S |D �cg c]8  }‰ j                  j                  |j
                  d«      ‰ j                  k  sŒ7|‘Œ: }}t        |xs |ˆ fd„¬«      }‰ j                  j                  |j
                  d«      dz   ‰ j                  |j
                  <   |j
                  ‰ _        |j
                  |fcd d d «       S c c}w # 1 sw Y   y xY w)Nr   rð   T)rË  rÌ  c                óh   •— ‰j                   j                  | j                  d«      | j                  fS râ  )rÑ  r    rƒ   r†   )rä   r›   s    €r7   rA   z:CredentialPool._acquire_lease_under_lock.<locals>.<lambda>	  s'   ø€  4×#6Ñ#6×#:Ñ#:¸5¿8¹8ÀQÓ#GÈÏÉÐ"X€ rB   rÊ  )rÐ  rÑ  r    rÌ  rÞ  rƒ   rÓ  r  )r›   rç  rß  r¼  rí  Ú	below_capÚchosens   `      r7   r  z(CredentialPool._acquire_lease_under_lock	  s@  ø€ ð �Z‰Zñ 	.ÙØ59×5HÑ5H×5LÑ5LÈ]Ð\]Ó5^ÐabÑ5b�×#Ñ# MÑ2Ø#0�Ô Ø$ bÐ(÷		.ñ 	.ð *.×)@Ñ)@ÈtÐ]aÐ)@Ó)bÑ&ˆI�ÙØ˜_Ð,÷	.ñ 	.ð %.Öi˜q°×1DÑ1D×1HÑ1HÈÏÉÈqÓ1QÐTX×ThÑThÓ1hšÐiˆIÐiÜØÒ&˜YÛXôˆFð .2×-@Ñ-@×-DÑ-DÀVÇYÁYÐPQÓ-RÐUVÑ-VˆD×Ñ §	¡	Ñ*Ø%Ÿy™yˆDÔØ—9‘9˜oÐ-÷#	.ñ 	.ùò j÷	.ð 	.ús0   Ž9D:ÁD:Á6D:Á:8D5Â3D5Â7A4D:Ä5D:Ä:Ec                óæ   — | j                   5  | j                  j                  |d«      }|dk  r| j                  j                  |d «       n|dz
  | j                  |<   d d d «       y # 1 sw Y   y xY w)Nr   rð   )rÐ  rÑ  r    r  )r›   rç  rÙ  s      r7   Úrelease_leasezCredentialPool.release_lease%	  sg   € Ø�Z‰Zñ 	?Ø×'Ñ'×+Ñ+¨M¸1Ó=ˆEØ˜ŠzØ×#Ñ#×'Ñ'¨°tÕ<à5:¸Q±Y�×#Ñ# MÑ2÷	?÷ 	?ñ 	?ús   �AA'Á'A0c                óf   — | j                   5  | j                  «       cd d d «       S # 1 sw Y   y xY wr™   )rÐ  Ú_try_refresh_current_unlockedrš   s    r7   Útry_refresh_currentz"CredentialPool.try_refresh_current/	  s*   € Ø�Z‰Zñ 	8Ø×5Ñ5Ó7÷	8÷ 	8ò 	8úrø  c                óV  ‡‡— | j                   5  ‰r| j                  ˆfd„«      nd}|€?‰r| j                  ˆfd„«      }n(| j                  «       xs | j                  dd¬«      d   }|€
	 ddd«       y|j                  | _        | j                  «       cddd«       S # 1 sw Y   yxY w)ae  Force-refresh the entry that supplied the failed request.

        Direct integrations may reload the pool after a request failed, so
        ``current_id`` cannot identify the issuing credential. With no hint,
        select WITHOUT the normal proactive refresh: the forced refresh below
        must consume a rotating refresh token exactly once.
        c                ó"   •— | j                   ‰k(  S r™   r   rì  s    €r7   rA   z5CredentialPool.try_refresh_matching.<locals>.<lambda>@	  rí  rB   Nc                ó"   •— | j                   ‰k(  S r™   rï  rð  s    €r7   rA   z5CredentialPool.try_refresh_matching.<locals>.<lambda>C	  s   ø€ °×1BÑ1BÀlÑ1R€ rB   F)rÌ  rÙ  r   )rÐ  rò  rõ  r¿  rƒ   rÌ  r  )r›   rú  rç  rä   s    `` r7   Útry_refresh_matchingz#CredentialPool.try_refresh_matching3	  s£   ù€ ð �Z‰Zñ 
	8ÙCP�D—J‘JÓ>Ô?ÐVZˆEØˆ}ÙØ ŸJ™JÓ'RÓS‘Eà ×2Ñ2Ó4Òl¸×8MÑ8MÐV[ÐchÐ8MÓ8iÐjkÑ8l�EØˆ}Ø÷
	8ð 
	8ð  %Ÿx™xˆDÔØ×5Ñ5Ó7÷
	8÷ 
	8ò 
	8ús   �ABÁ5 BÂB(c                óx   — | j                  «       }|€y | j                  |d¬«      }|�|j                  | _        |S )NTrQ  )rõ  rZ  rƒ   rÌ  )r›   rä   ru  s      r7   r  z,CredentialPool._try_refresh_current_unlockedK	  sE   € Ø×&Ñ&Ó(ˆØˆ=ØØ×'Ñ'¨°TÐ'Ó:ˆ	ØÐ Ø(Ÿ|™|ˆDÔØÐrB   ©r}   ry   rò   úList[PooledCredential])rÊ   r_  )rÜ  r‰   rÊ   r_  )rÜ  r‰   rÊ   r‹   )rÊ   r  )rñ  z"Callable[[PooledCredential], bool]rÊ   úOptional[PooledCredential])rÊ   r  r™   )rú  r   rÊ   r‰   )rÿ  r‚   r   r‚   rÊ   ÚNone)r®  úOptional[List[str]]rš  r  rÊ   r  )rä   r‚   r  r_  r  r   rÊ   r‚   )rä   r‚   r  úSet[str]rÊ   r  )r  rŒ   r  rs   rÊ   r_  )rä   r‚   r  rŒ   r  úOptional[Dict[str, Any]]r  r_  rg   r‰   rÊ   r‚   )rä   r‚   rÊ   r‚   )rä   r‚   rÊ   r  )rä   r‚   r  rs   rÊ   r_  )rä   r‚   rR  r_  rÊ   r  )rä   r‚   r‹  ÚBaseExceptionrŸ  ry   rÊ   r  )rÊ   r
  )rä   r‚   ru  rs   rÊ   r  )rä   r‚   r‹  r6   rÊ   r  )rä   r‚   r‹  r6   rÊ   r  ©rä   r‚   rÊ   r_  )rÜ  r‰   rÊ   r  )rÜ  r‰   rÊ   ú9Tuple[Optional[PooledCredential], List[PooledCredential]])rÁ  r  rÊ   r  )rä   r‚   rÊ   r‹   )rË  r_  rÌ  r_  rÜ  r‰   rÊ   z5Tuple[List[PooledCredential], List[PooledCredential]]©rÊ   r  )rÌ  r_  rÙ  r_  rÜ  r‰   rÊ   r   )rç  ry   rÜ  r‰   rÊ   r  )rç  r‰   rú  r‰   rÊ   r  )r  rŒ   r  r  rú  r‰   rç  r‰   rg   r‰   rÜ  r‰   rÊ   r  )rç  r‰   rÊ   r‰   )rç  r‰   rÊ   z,Tuple[Optional[str], List[PooledCredential]])rç  ry   rÊ   r  )NN)rú  r‰   rç  r‰   rÊ   r  );r£   rÌ   rÍ   rÁ  rØ  rá  ré  rò   rç  rò  rõ  r÷  rü  r  r  r  r  r  r  r$  r0  Ú%_sync_anthropic_entry_from_pool_storer<  rE  rL  rG  rZ  rX  rd  rV  ry  r�  r†  rU  r‹  r”  r•  r˜  r´  rW  r½  rº  r»  rÅ  rÉ  rÞ  rØ  r¿  rã  ré  ró  rø  r  r  r  r  r  r  r  r>   rB   r7   rÆ  rÆ  Ö  s6  „ ó1ó8'ð 7;õ 	#ð ;?õ  ;óD'óRóFó>ó
,ô@ó"ð  ,0Ø26ñ	ð )ðð 0ð	ð
 
óð BFõ ó3ð Gà"ðGð )ðGð 
ó	Gð. 37ð	 
ð Ø(,ñ 
àð 
ð #ð 
ð 0ð	 
ð ð 
ð &ð 
ð 
ó 
óTó<+ðZ -HÐ)óNó`!óF)dóVó4*AòX
ð2àð2ð ð2ð
 ð2ð 
ó2óhdðaØ%ðaØ2@ðaà	óaó>ó@	
ó.ó`ióVó*\ó6Uó*$óLð& 04õ 	ð <@õ 6ó4ó	Xó7ð. (-¸eÐ\`ñQ*Ø $ðQ*Ø7;ðQ*ØLYðQ*à	>óQ*ófVð "&°TÐRVñ$&Øð$&Ø.2ð$&ØBOð$&à	Bó$&óL7ð EIõ 
KðØ*ðØ:Gðà	#óó>%ðV 37Ø&*Ø'+Ø(,Ø#ñLð #ðLð 0ð	Lð
 $ðLð %ðLð &ðLð ðLð 
$óLô`ð$.Ø*ð.à	5ó.ó,?ó8ð '+Ø'+ð8à#ð8ð %ð8ð 
$ó	8ô0rB   rÆ  c                ó\  — t        | «      D ��cg c]  \  }}|j                  |k(  sŒ|‘Œ }}}|r|d   nd }t        |dd  «      }|r&t        | «      D ��cg c]  \  }}||vsŒ|‘Œ c}}| d d  |€—|j                  dt	        j
                  «       j                  d d «       |j                  dt        | «      «       |j                  d|j                  d«      xs |«       | j                  t        j                  ||«      «       y| |   }	i }
i }t        |	«      D �ch c]  }|j                  ’Œ }}|j                  d«      }|d uxr ||	j                  k7  }|rG|	j                  s;|	j                  j                  d	«      }t!        |t"        «      r|rt%        |«      |k7  }|j'                  «       D ]d  \  }}|d
v s|�|dk(  r|	j(                  rŒ||v rt+        |	|«      |k7  sŒ2||
|<   Œ8|t,        v sŒA|	j                  j                  |«      |k7  sŒ`|||<   Œf |r!|	j.                  �|
j1                  t2        «       |
s|rT|ri |	j                  ¥|¥|
d<   t5        |	fi |
¤Ž}|| |<   t7        |«      xs! |	j9                  «       |j9                  «       k7  S t7        |«      S c c}}w c c}}w c c}w )Nr   rð   rƒ   r§   r†   r„   Trˆ   rj   >   rƒ   r†   r—   )rþ  r‡   r8  r«   r¬   r­   r®   ró   r    r?  r‚   r·   r   r¤   rˆ   r—   rx   ry   r   rª   r„   r¹   rŸ   rm   rA  rt   r	   r_  r½   )rò   r}   r‡   r±   r  rä   Úmatching_indicesÚexisting_idxÚduplicate_indicesr   r;  rD  r²   Ú_field_namesÚincoming_tokenÚtoken_changedÚknown_fingerprintr×   r¼   r«  s                       r7   Ú_upsert_entryr+  Y	  s¢  € Ü.7¸Ó.@×[¡
  UÀEÇLÁLÐTZÓDZšÐ[ÐÑ[Ù*:Ð# AÒ&À€LÜÐ,¨Q¨RÐ0Ó1ÐÙÜ.7¸Ó.@×a¡
  UÀCÐO`ÒD`’eÓaˆ‘ˆ
àÐØ×Ñ˜4¤§¡£×!1Ñ!1°"°1Ð!5Ô6Ø×Ñ˜:¤~°gÓ'>Ô?Ø×Ñ˜7 G§K¡K°Ó$8Ò$B¸FÔCØ�‰Ô'×1Ñ1°(¸GÓDÔEØà�|Ñ$€HØ$&€MØ$&€MÜ$*¨8Ó$4Ö5˜q�A—F“FÐ5€LÐ5Ø—[‘[ Ó0€NØ"¨$Ð.ÒZ°>ÀX×EZÑEZÑ3Z€MÙ˜X×2Ò2ð %ŸN™N×.Ñ.Ð/CÓDÐÜÐ'¬Ô-Ñ2CÜ4°^ÓDÐHYÑYˆMØ—m‘m“oò '‰
ˆˆUØÐ$Ñ$¨¨¸#Àº.ÈXÏ^Ê^ØØ�,ÑÜ�x Ó%¨Ó.Ø%*�˜cÒ"Ø”KÒ H§N¡N×$6Ñ$6°sÓ$;¸uÓ$DØ!&ˆM˜#Òð'ñ ˜×-Ñ-Ð9Ø×Ñœ]Ô+Ù™ÙØ%H¨¯©Ð%H¸-Ð%HˆM˜'Ñ"Ü˜(Ñ4 mÑ4ˆØ 'ˆ�Ñô Ð%Ó&ÒQ¨(×*:Ñ*:Ó*<ÀÇÁÓ@QÑ*QÐQÜÐ!Ó"Ð"ùóa \ùó bùò 6s   �J§JÁJ#Á"J#ÄJ)rƒ  é   é   )zenv:ANTHROPIC_TOKENzenv:CLAUDE_CODE_OAUTH_TOKENrŽ  r¼  zenv:ANTHROPIC_API_KEYc                óR  — | dk7  ryt        d„ |D «       d„ ¬«      }t        d„ |D «       d„ ¬«      }t        |«      D ��ci c]  \  }}|j                  |“Œ }}}d}t        g |¢|¢«      D ]4  \  }}|j                  |k7  sŒt	        ||¬«      |||j                     <   d	}Œ6 |S c c}}w )
Nrw   Fc              3  óL   K  — | ]  }t        |j                  «      sŒ|–— Œ y ­wr™   ©rø   r‡   rë   s     r7   rí   z-_normalize_pool_priorities.<locals>.<genexpr>š	  s   è ø€ ÒG�5Ô'8¸¿¹Õ'FŒÑGùó   ‚$�$c                ó   — | j                   S r™   rê   rÉ  s    r7   rA   z,_normalize_pool_priorities.<locals>.<lambda>›	  s
   € ˜%Ÿ.™.€ rB   rÊ  c              3  óL   K  — | ]  }t        |j                  «      rŒ|–— Œ y ­wr™   r0  rë   s     r7   rí   z-_normalize_pool_priorities.<locals>.<genexpr>ž	  s   è ø€ ÒK�5Ô+<¸U¿\¹\Õ+JŒÑKùr1  c                óŠ   — t         j                  | j                  t        t         «      «      | j                  | j
                  fS r™   )Ú_ANTHROPIC_SOURCE_RANKr    r‡   r:  r†   r„   rÉ  s    r7   rA   z,_normalize_pool_priorities.<locals>.<lambda>Ÿ	  s1   € Ü"×&Ñ& u§|¡|´SÔ9OÓ5PÓQØ�N‰NØ�K‰Kð
€ rB   rê   T)rN  rþ  rƒ   r†   r	   )	r}   rò   Úmanual_entriesÚseeded_entriesr  rä   Ú	id_to_idxr§  Únew_prioritys	            r7   Ú_normalize_pool_prioritiesr:  –	  sÂ   € Ø�;ÒØÜÙG˜GÔGÙ(ô€Nô ÙK˜GÔKñ
ô€Nô 2;¸7Ó1C×D¡: 3¨�—‘˜3‘ÐD€IÑDØ€GÜ(Ð)K¨>Ð)K¸NÐ)KÓLò Ñˆ�eØ�>‰>˜\Ó)Ü+2°5À<Ô+PˆG�I˜eŸh™hÑ'Ñ(Ø‰Gðð €Nùó Es   ¿B#r  c                ó„   — | D �cg c]  }|j                   |vsŒ|‘Œ }}t        |«      t        | «      k(  ry|| dd yc c}w )zGRemove entries whose source is in *drop*; True if anything was removed.FNT)r‡   r:  )rò   Údroprä   Úretaineds       r7   Ú_retain_sources_not_inr>  ®	  sE   € à#*ÖG˜%¨e¯l©lÀ$Ò.F’ÐG€HÐGÜ
ˆ8ƒ}œ˜G›Ò$ØØ€G‰A€JØùò	 Hs   …=™=c                  ó2   — e Zd ZdZdd„Zdd„Zedd„«       Zy)	Ú_SeederzIAccumulates ``_upsert_entry`` results for one ``load_pool`` seeding pass.c                ój   — || _         || _        d| _        t        «       | _        t        «       | _        y r=   )r}   rò   r§  r8  Úactive_sourcesrE   Úis_suppressedrÖ  s      r7   rÁ  z_Seeder.__init__º	  s,   € Ø ˆŒØˆŒØˆŒÜ(+«ˆÔÜ5Ó7ˆÕrB   c                óì   — | j                  | j                  |«      ry| j                  j                  |«       t	        | j
                  | j                  |d|i|¥«      }| xj                  |z  c_        |S )zPUpsert unless suppressed (``hermes auth remove`` must stay stable across loads).Fr‡   )rC  r}   rB  r3  r+  rò   r§  )r›   r‡   r±   Úingesteds       r7   Úupsertz_Seeder.upsertÁ	  sd   € à×Ñ˜dŸm™m¨VÔ4ØØ×Ñ×Ñ Ô'Ü  §¡¨t¯}©}¸fÀxÐQWÐFcÐ[bÐFcÓdˆØ�Š˜Ñ �ØˆrB   c                ó2   — | j                   | j                  fS r™   )r§  rB  rš   s    r7   rº   z_Seeder.resultÊ	  s   € à�|‰|˜T×0Ñ0Ð0Ð0rB   Nr  )r‡   ry   r±   rs   rÊ   r_  )rÊ   úTuple[bool, Set[str]])r£   rÌ   rÍ   rÃ  rÁ  rF  rÐ   rº   r>   rB   r7   r@  r@  ·	  s#   „ ÙSó8óð ò1ó ñ1rB   r@  c                óÂ  ‡
— 	 ddl m}  |d«      sy 	 t        «       Š
dˆ
fd„} |d«      xs  |d«      } |d«      r.|s,| xj                  t        | j                  dd	h«      z  c_        y dd
lm}m	} ddl
m} d |«       fg} |«       r|j                  d	 |«       f«       n*| xj                  t        | j                  d	h«      z  c_        |D ]~  \  }}	|	sŒ	|	j                  d«      sŒ| j                  |t        |	j                  dd«      |	j                  d«      |	j                  d«      t!        |	j                  dd«      |«      dœ«       Œ€ y # t        $ r Y �ŒJw xY w)Nr   ©Ú!is_provider_explicitly_configuredrw   c                óf   •— ‰j                  | «      xs t        | d«      xs dj                  «       S r?  )r    Ú_get_secretrÀ   )r×   Ú	_env_files    €r7   Ú_env_valz,_seed_anthropic_singletons.<locals>._env_valã	  s,   ø€ Ø—‘˜cÓ"Ò@¤k°#°rÓ&:Ò@¸b×GÑGÓIÐIrB   ÚANTHROPIC_TOKENÚCLAUDE_CODE_OAUTH_TOKENÚANTHROPIC_API_KEYrŽ  r¼  )r  Úread_hermes_oauth_credentials)Úadopt_external_logins_enabledr  r¨   r  r  )r   rˆ   rŠ   r�   r„   ©r×   ry   rÊ   ry   )rC   rK  rD   r   r§  r>  rò   r‘  r  rS  Úagent.credential_sourcesrT  r?  r    rF  r{   rØ   )ÚseedrK  rO  Úanthropic_oauth_envr  rS  rT  r  Úsource_namer!  rN  s             @r7   Ú_seed_anthropic_singletonsrZ  Ï	  sP  ø€ ðÝEÙ0°Ô=Øð >ô “
€IõJñ #Ð#4Ó5Ò\¹ÐB[Ó9\ÐÙÐ#Ô$Ñ-@ð 	�ŠÔ.¨t¯|©|¸mÈ]Ð=[Ó\Ñ\�Ø÷õ GàÑ<Ó>Ð?Ð@€GÙ$Ô&Ø�‰˜Ñ'CÓ'EÐFÕGð 	�ŠÔ.¨t¯|©|¸m¸_ÓMÑM�Ø%ò Ñˆ�UÚ�U—Y‘Y˜}Õ-Ø�K‰K˜Ü,Ø %§	¡	¨-¸Ó <Ø!&§¡¨>Ó!:Ø!&§¡¨;Ó!7Ü)¨%¯)©)°MÀ2Ó*FÈÓTñ&õ ñøôG ò Úðús   ƒE Å	EÅEc                ó0  — t        |d«      }t        t        |t        «      xrZ t	        |j                  d«      xs d«      j                  «       xs, t	        |j                  d«      xs d«      j                  «       «      }|r-|s+| xj                  t        | j                  ddh«      z  c_        |r|sy t	        |j                  d«      xs d«      j                  «       }| j                  dt        |j                  dd«      |j                  d«      |j                  d	«      |j                  d
«      |j                  d«      |j                  d«      |j                  d«      |j                  d«      |j                  d«      |j                  d«      dœt        D �ci c]  }||j                  |«      “Œ c}¥t        |j                  d«      t        «      r|j                  d«      nd |xs t        |j                  dd«      d«      dœ¥«       y c c}w )Nr¿   rˆ   r¨   r“   râ   r2  r„   rŠ   r�   ra   r[   r^   rh   r’   r”   )r   rˆ   rŠ   r�   ra   r[   r^   rh   r’   r“   r”   rZ   )rZ   r„   )r(   r_  rx   rÜ   ry   r    rÀ   r§  r>  rò   rF  r{   rB  rØ   )rW  rH  r  Úhas_runtime_materialÚcustom_labelr×   s         r7   Ú_seed_nous_singletonr^  
  s½  € Ü  ¨VÓ4€EÜÜ�5œ$Óò 	hÜ�—‘˜>Ó*Ò0¨bÓ1×7Ñ7Ó9Òf¼SÀÇÁÈ;ÓAWÒA]Ð[]Ó=^×=dÑ=dÓ=fóÐñ Ñ)Ø�ŠÔ.¨t¯|©|¸mÐMaÐ=bÓcÑc�ÙÑ*Øô �u—y‘y Ó)Ò/¨RÓ0×6Ñ6Ó8€LØ‡K�K�Ü$ØŸ	™	 .°"Ó5ØŸ™ ?Ó3Ø—i‘i Ó-Ø—i‘i Ó-Ø—‘˜7Ó#Ø—Y‘Y˜{Ó+Ø Ÿ9™9Ð%6Ó7Ø#Ÿi™iÐ(<Ó=Ø—Y‘Y˜{Ó+Ø %§	¡	Ð*@Ó Añ ô +AÖ
A 3ˆ3�—	‘	˜#“ÑÒ
Að ô  $.¨e¯i©i¸Ó.>ÄÔ#Eˆu�y‰y˜ÔÈ4ØÒ_Ô!1°%·)±)¸NÈBÓ2OÐQ^Ó!_ò# õ ùò Bs   ÆHÚ_COPILOT_RAW_DEGRADATION_WARNEDc                óˆ   — t        | «      xs d}|t        v ryt        j                  |«       t        j	                  d«       y)zVWARN once per token per process when Copilot exchange degrades to raw token (#114740).r£  Nz¤Copilot token exchange degraded to RAW token (exchange unavailable); enterprise-only models may 400 with model_not_available_for_integrator until exchange recovers.)r   r_  r3  rˆ  r°  )r~   Úfingerprints     r7   Ú"_warn_copilot_raw_degradation_oncerb  +
  s=   € ä*¨5Ó1Ò>°Y€KØÔ5Ñ5ØÜ#×'Ñ'¨Ô4Ü
‡N�Nð	FõrB   c                 ó,   — t         j                  «        y)z9Clear the degradation warning cache (for test isolation).N)r_  Úclearr>   rB   r7   Ú%_reset_copilot_raw_degradation_warnedre  8
  s   € ä#×)Ñ)Õ+rB   c                óN  ‡ — 	 ddl m}m}m} dg|D �cg c]  }d|› �‘Œ	 c}z   }t	        ˆ fd„|D «       «      ry  |«       \  }}|sy |dk(  rdnd|› �}‰ j                  ‰ j                  |«      ry ddlm}	  |	‰ j                  «      s|d }}
n ||«      \  }
}|
|k(  r|st        |«       t        j                  ‰ j                  «      }‰ j                  |t        |
|xs |r|j                  nd|d	œ«       y c c}w # t        $ r }t         j#                  d
|«       Y d }~y d }~ww xY w)Nr   )ÚCOPILOT_ENV_VARSÚresolve_copilot_tokenÚget_copilot_api_tokenÚgh_cliúenv:c              3  óV   •K  — | ]   }‰j                  ‰j                  |«      –— Œ" y ­wr™   )rC  r}   )rì   r   rW  s     €r7   rí   z*_seed_copilot_singleton.<locals>.<genexpr>K
  s"   øè ø€ ÒM¸ˆt×!Ñ! $§-¡-°×3ÑMùs   ƒ&)zgh auth tokenrJ  r¨   ©r   rˆ   rŽ   r„   zCopilot token seed failed: %s)Úhermes_cli.copilot_authrg  rh  ri  ÚallrC  r}   rC   rK  rb  r!   r    rF  r|   r’   r6   rˆ  r‰  )rW  rg  rh  ri  r¶   Úcopilot_sourcesr~   r‡   rY  rK  Ú	api_tokenÚenterprise_base_urlÚpconfigr‹  s   `             r7   Ú_seed_copilot_singletonrt  =
  s0  ø€ ð-;÷	
ñ 	
ð $˜*Ð<LÖ'M°q¨$¨q¨cª
Ò'MÑMˆÜÓM¸_ÔMÔMØÙ-Ó/‰ˆˆvÙØð #)¨OÒ";‘hÀ4ÈÀxÀˆà×Ñ˜dŸm™m¨[Ô9ØÝEÙ0°·±Ô?ð
 .3°DÐ*‰Iá-BÀ5Ó-IÑ*ˆIÐ*ð
 ˜EÒ!Ñ*=Ü2°5Ô9Ü#×'Ñ'¨¯©Ó6ˆØ�‰�KÜ*Ø%Ø+Ò^Ég°×0JÒ0JÐ[]Øñ	"
õ 	ùò9 (NøôD ò ;Ü�‰Ð4°c×:Ñ:ûð;ús9   ƒC; “C6ŸC; ¹C; Á(C; Á/BC; Ã6C; Ã;	D$ÄDÄD$c                ó`  — 	 ddl m}  |d¬«      }|j                  dd«      }|r^|j                  dd«      }| j                  |t        ||j                  d	«      |j                  d
d«      |j                  d|«      dœ«       y y # t
        $ r }t        j                  d|«       Y d }~y d }~ww xY w)Nr   )Ú resolve_qwen_runtime_credentialsF)Úrefresh_if_expiringrQ   r¨   r‡   zqwen-clir�   rŽ   Ú	auth_file)r   rˆ   r�   rŽ   r„   z Qwen OAuth token seed failed: %s)rC   rv  r    rF  r{   r6   rˆ  r‰  )rW  rv  r!  r~   rY  r‹  s         r7   Ú_seed_qwen_singletonry  p
  s¡   € ð>ÝDÙ0ÀUÔKˆØ—	‘	˜) RÓ(ˆÙØŸ)™) H¨jÓ9ˆKØ�K‰K˜Ü,Ø %Ø!&§¡¨?Ó!;Ø!ŸI™I j°"Ó5ØŸ™ ;°Ó<ñ&õ ð øô ò >Ü�‰Ð7¸×=Ñ=ûð>ús   ‚B B Â	B-ÂB(Â(B-c                ób  — 	 ddl m}  |d«      }|r|j                  d«      sy d }	 |j                  dd«      }|r/t        t	        j
                  |«      j                  «       dz  «      }| j                  dt        |d   |j                  d	«      |t        |j                  d
d«      xs d«      j                  d«      |j                  dd«      xs t        |j                  dd«      d«      dœ«       y # t        $ r d }Y Œ—w xY w# t        $ r }t        j                  d|«       Y d }~y d }~ww xY w)Nr   )Úget_provider_auth_stateúminimax-oauthrˆ   r�   r¨   r¶  rP   rŠ   r’   rÇ   r„   )r   rˆ   rŠ   r�   rŽ   r„   z#MiniMax OAuth token seed failed: %s)rC   r{  r    r…   r
   r  r  r6   rF  r{   ry   rÈ   rØ   rˆ  r‰  )rW  r{  r  r�   r  r‹  s         r7   Ú_seed_minimax_singletonr}  „
  s  € ðAÝ;Ù'¨Ó8ˆÙ˜%Ÿ)™) NÔ3ØØˆð	!Ø—)‘)˜L¨"Ó-ˆCÙÜ #¤H×$:Ñ$:¸3Ó$?×$IÑ$IÓ$KÈdÑ$RÓ S�ð 	�‰�GÜ(Ø! .Ñ1Ø"ŸY™Y Ó7Ø*Ü˜EŸI™IÐ&:¸BÓ?ÒEÀ2ÓF×MÑMÈcÓRØ—Y‘Y˜w¨Ó+ÒgÔ/?ÀÇ	Á	È.ÐZ\Ó@]Ð_fÓ/gñ
õ 	øô ò 	!Ø ŠMð	!ûô ò AÜ�‰Ð:¸C×@Ñ@ûðAúsA   ‚!D ¤D §AC4 Á*B	D Ã4DÃ?D ÄDÄD Ä	D.ÄD)Ä)D.c                óH  — t        || j                  «      }t        |t        «      r|j	                  d«      nd}t        |t        «      r|j	                  d«      sy| j                  dk(  r=t
        j                  }t        |j	                  d«      xs d«      j                  «       }nt
        j                  }d}| j                  dt        |j	                  dd«      |j	                  d«      ||j	                  d	«      |xs t        |j	                  dd«      d«      d
œ«       y)au  Codex / xAI: surface the auth.json ``providers.<id>.tokens`` singleton as ``device_code``.

    Hermes owns its own Codex auth state and does NOT auto-import
    ~/.codex/auth.json: refresh tokens are single-use, so sharing them with
    Codex CLI / VS Code causes refresh_token_reused races. Adoption is an
    explicit one-time prompt via `hermes auth openai-codex`.
    r3  Nrˆ   rÅ   r„   r¨   râ   rŠ   r‘   )r   rˆ   rŠ   rŽ   r‘   r„   )r(   r}   rx   rÜ   r    rÁ   ÚDEFAULT_CODEX_BASE_URLry   rÀ   ÚDEFAULT_XAI_OAUTH_BASE_URLrF  r{   rØ   )rW  rH  r  r3  rŽ   r]  s         r7   Ú_seed_tokens_singletonr�  Ÿ
  sè   € ô ! ¨T¯]©]Ó;€EÜ$.¨u´dÔ$;ˆU�Y‰Y�xÔ À€FÜ�vœtÔ$¨¯©°NÔ)CØØ‡}�}˜Ò&Ü×2Ñ2ˆÜ˜5Ÿ9™9 WÓ-Ò3°Ó4×:Ñ:Ó<‰ä×6Ñ6ˆØˆØ‡K�K�Ü$ØŸ
™
 >°2Ó6ØŸ™ OÓ4ØØŸ	™	 .Ó1ØÒ`Ô!1°&·*±*¸^ÈRÓ2PÐR_Ó!`ñ õ rB   c                óÎ  — t        | |«      }t        «       }| dk(  rt        |«       |j                  S | dk(  rt        ||«       |j                  S | dk(  rt	        |«       |j                  S | dk(  rt        |«       |j                  S | dk(  rt        |«       |j                  S | t        v r/| dk(  r|j                  | d«      r|j                  S t        ||«       |j                  S )Nrw   r¿   Úcopilotz
qwen-oauthr|  rÅ   râ   )r@  r'   rZ  r^  rt  ry  r}  r¹  rC  rº   r�  )r}   rò   rW  rH  s       r7   Ú_seed_from_singletonsr„  »
  sî   € Ü�8˜WÓ%€DÜ!Ó#€JØ�;ÒÜ" 4Ô(ð �;‰;Ðð 
�VÒ	Ü˜T :Ô.ð �;‰;Ðð 
�YÒ	Ü Ô%ð �;‰;Ðð 
�\Ò	!Ü˜TÔ"ð �;‰;Ðð 
�_Ò	$Ü Ô%ð �;‰;Ðð 
Ô0Ñ	0ð �~Ò%¨$×*<Ñ*<¸XÀ}Ô*UØ—;‘;ÐÜ˜t ZÔ0Ø�;‰;ÐrB   c                óÈ   — t        «       }|j                  | d«      j                  «       }t        | d«      xs dj                  «       }|j	                  d«      r|r|S |xs |S )a  Resolve a credential env var, preferring ~/.hermes/.env over os.environ.

    The user's config file is authoritative; stale env vars from parent
    processes (Codex CLI, test scripts) must not override deliberate .env
    changes. load_env() memoizes on mtime, so per-call reads cost a stat().
    An unresolved ``op://`` reference in .env yields to the already-resolved
    value from the active secret scope (set by apply_onepassword_secrets());
    otherwise every provider auth attempt would receive a URL instead of a key.
    r¨   zop://)r   r    rÀ   rM  rz   )r×   Úenv_filer  Úscoped_values       r7   Úget_env_prefer_dotenvrˆ  Ñ
  s]   € ô ‹z€HØ
�,‰,�s˜BÓ
×
%Ñ
%Ó
'€CÜ  RÓ(Ò.¨B×5Ñ5Ó7€LØ
‡~�~�gÔ¡<ØÐØÒ�,ÐrB   Ú_ENV_INGESTION_WARNEDc                óŒ   — | t         v ryt         j                  | «       t        j                  d|| | dk(  rd| «       y| | «       y)uR  WARN once per process per provider when an env credential is ingested into a paid pool.

    Auto-ingesting OPENROUTER_API_KEY is what ARMS silent OpenRouter spend â€”
    every downstream auto-detect keys off the pool having credentials.
    Ingestion stays allowed (an exported key is arguable intent) but must
    never be silent.
    Nu�   Ingested %s from environment into the %s credential pool â€” this enables %s spend. Remove the key or run hermes auth remove %s <n> to suppress.Ú
openrouterÚ
OpenRouter)r‰  r3  rˆ  r°  )r}   rk  s     r7   Ú_warn_env_ingestion_oncer�  è
  sQ   € ð Ô(Ñ(ØÜ×Ñ˜hÔ'Ü
‡N�Nð	1ð 	ØØ  LÒ0ˆØõð 7?ØõrB   c                ó¤   — t         ||| dœ}	 ddlm}  || «      }|rt	        |«      j                  «       nd }|r||d<   |S # t        $ r d }Y Œ3w xY w)Nrm  r   )Úget_secret_sourcerf   )r|   Úhermes_cli.env_loaderr�  r6   ry   rÀ   )rk  r~   rŽ   r±   r�  Úsource_labelrf   s          r7   Ú_env_payloadr’  þ
  sh   € ä&ØØØñ	€GðÝ;Ù(¨Ó1ˆñ 2>”C˜Ó%×+Ñ+Ô-À4€MÙØ#0ˆ�Ñ Ø€Nøô ò ØŠðús   �A ÁAÁA)zkimi-codingÚzaic                óz  — t        | «      }| D ]A  }d}t        |› d|› �«      sŒ|j                  |› d|› �«       |dz  }t        |› d|› �«      rŒ,ŒC |D ]d  }|j                  j	                  d«      sŒ|j                  j                  dd«      d   j                  «       }|sŒO||vsŒT|j                  |«       Œf |S )a]  *env_vars*, their numbered siblings, and the ``env:VAR`` names already persisted.

    ``VAR_2``, ``VAR_3``, ... are tried for every declared VAR until the first
    one that does not resolve, so a `.env` or secret-manager project can back a
    whole rotation pool with no config: setting ``NVIDIA_API_KEY_2`` is the
    whole opt-in (#76593).

    Env-backed rows are written to auth.json without their secret and
    re-hydrated on every load; a row whose VAR the registry does not
    declare would otherwise stay empty forever and be silently dropped
    from rotation by ``_available_entries``.
    rƒ  r›  rð   rk  rõ   )rL  rˆ  r?  r‡   rz   ÚsplitrÀ   )Úenv_varsrò   Únamesr  Únrä   Úenv_names          r7   Ú_env_key_var_candidatesrš    sÊ   € ô �‹N€EØò ˆØˆÜ# t f¨A¨a¨S MÕ2Ø�L‰L˜D˜6  1 #˜Ô'Ø�‰FˆAô $ t f¨A¨a¨S MÖ2ðð
 ò 'ˆØ�<‰<×"Ñ" 6Õ*Ø—|‘|×)Ñ)¨#¨qÓ1°!Ñ4×:Ñ:Ó<ˆHÚ˜H¨EÒ1Ø—‘˜XÕ&ð	'ð
 €LrB   c           	     ó   — t        | |«      }| dk(  r|j                  S | dk(  r^t        dg|«      D ]B  }t        |«      }|sŒ|j	                  d|› �t        ||t        ¬«      «      sŒ7t        | |«       ŒD |j                  S t        j                  | «      }|r|j                  t        k7  r|j                  S d}|j                  r$t        |j                  «      j                  d«      }t        |j                  «      }| dk(  rg d	¢}t        ||«      }t         j                  | «      }|D ]W  }t        |«      }|sŒ|xs |j"                  }	|� |||j"                  |«      }	|j	                  d|› �t        |||	¬«      «       ŒY |j                  S )
Nrƒ  r‹  ÚOPENROUTER_API_KEYrk  )rk  r~   rŽ   r¨   rÇ   rw   )rP  rQ  rR  )r@  rº   rš  rˆ  rF  r’  r   r�  r!   r    r   r|   Úbase_url_env_varrÈ   rL  Úapi_key_env_varsÚ_ENV_BASE_URL_RESOLVERSr’   )
r}   rò   rW  rk  r~   rs  Úenv_urlr–  Úresolve_base_urlrŽ   s
             r7   Ú_seed_from_envr¢  2  s�  € Ü�8˜WÓ%€Dð
 �9ÒØ�{‰{Ðà�<ÒÜ.Ð0DÐ/EÀwÓOò 	<ˆGÜ)¨'Ó2ˆEÚ˜Ÿ™Ø�w�iÐ Ü W°EÔDWÔXõô )¨°7Õ;ð	<ð �{‰{Ðä×#Ñ# HÓ-€GÙ�g×'Ñ'Ô+<Ò<Ø�{‰{Ðà€GØ×ÒÜ'¨×(@Ñ(@ÓA×HÑHÈÓMˆä�G×,Ñ,Ó-€HØ�;ÒÚVˆÜ& x°Ó9€Hä.×2Ñ2°8Ó<ÐØò eˆÜ% gÓ.ˆÙØØÒ8˜g×8Ñ8ˆØÐ'Ù'¨¨w×/IÑ/IÈ7ÓSˆHØ�‰�d˜7˜)Ð$¤l¸7È%ÐZbÔ&cÕdðeð �;‰;ÐrB   T©Úprune_env_sourcesc               óÊ   ‡— dˆfd„}| D �cg c]/  }t        |j                  «      s|j                  |v s ||«      s|‘Œ1 }}t        |«      t        | «      k(  ry|| d d  yc c}w )Nc                ó    •— | j                   j                  d«      r‰S t        | j                   | j                  «      xs | j                   dk(  S )Nrk  rŽ  )r‡   rz   r   r}   )rä   r¤  s    €r7   Ú_is_prunablez1_prune_stale_seeded_entries.<locals>._is_prunabled  sA   ø€ ð
 �<‰<×"Ñ" 6Ô*Ø$Ð$ä,¨U¯\©\¸5¿>¹>ÓJÒkÈeÏlÉlÐ^kÑNkÐkrB   FTr  )rø   r‡   r:  )rò   rB  r¤  r§  rä   r=  s     `   r7   Ú_prune_stale_seeded_entriesr¨  ^  sn   ø€ õlð öàÜ˜UŸ\™\Ô*¨e¯l©l¸nÑ.LÑT`ÐafÔTgò 	ð€Hð ô
 ˆ8ƒ}œ˜G›Ò$ØØ€G‰A€JØùòs   Œ4A c           
     ó¶  ‡— t        | |«      }t        | «      }|r—t        |j                  d«      xs d«      j	                  «       }t        |j                  d«      xs d«      j	                  «       }|r=|j                  d|› �t        |t        |j                  d«      «      |xs d|› �dœ«       	 t        «       }|r|j                  d«      ndŠt        ‰t        «      rÎt        ‰j                  d	«      xs d«      j	                  «       j                  «       }t        ‰j                  d«      «      }t        ˆfd
„dD «       d«      }	|dk(  r`|r^|	r\t        |«      D �
ch c])  }
t        |
«      j	                  «       j                  «       ’Œ+ }}
| |v r|j                  dt        |	|ddœ«       |j                  S c c}
w # t        $ r Y |j                  S w xY w)zJSeed a custom endpoint pool from custom_providers config and model config.rQ   r¨   r¤   zconfig:rŽ   rm  rÜ  Nr}   c              3  ó¤   •K  — | ]G  }‰j                  |«      }t        |t        «      sŒ%|j                  «       sŒ6|j                  «       –— ŒI y ­wr™   )r    rx   ry   rÀ   )rì   r´   r¶   Ú	model_cfgs      €r7   rí   z$_seed_custom_pool.<locals>.<genexpr>‘  s<   øè ø€ Òx˜qÀÇÁÈqÓAQ¸1ÔWaÐbcÔehÕWiÐno×nuÑnuÕnw�—‘—Ñxùs   ƒ&AªA»A)rQ   Úapir^  Úmodel_config)r@  rU  ry   r    rÀ   rF  r|   r+  r8   rx   rÜ   rö   rS  rF  r6   rº   )rT  rò   rW  Ú	cp_configrQ   r¤   r.  Úmodel_providerÚmodel_base_urlÚmodel_api_keyr×   Úmatched_keysr«  s               @r7   Ú_seed_custom_poolr³  y  sº  ø€ ä�8˜WÓ%€Dä+¨HÓ5€IÙÜ�i—m‘m IÓ.Ò4°"Ó5×;Ñ;Ó=ˆÜ�9—=‘= Ó(Ò.¨BÓ/×5Ñ5Ó7ˆÙØ�K‰K˜' $ Ð(Ü.Ø 'Ü% i§m¡m°JÓ&?Ó@ØÒ1 7¨4¨&Ð!1ñ	+ô ðÜ"Ó$ˆÙ+1�F—J‘J˜wÔ'°tˆ	Ü�i¤Ô&Ü  §¡¨zÓ!:Ò!@¸bÓA×GÑGÓI×OÑOÓQˆNÜ& y§}¡}°ZÓ'@ÓAˆNÜ ÛxÐ$6ÔxØóˆMð  Ò)©nÁô 9\Ð\jÓ8kö Ø14”C˜“H—N‘NÓ$×*Ñ*Õ,ð �ð  ð ˜|Ñ+Ø—K‘K Ü%6Ø(5Ø$2Ø!/ñ	1ô ð �;‰;Ðùò øô ò Øà�;‰;Ððús%   Â3B.G Å!.F<Æ!G Æ<G Ç	GÇGc                ó�  ‡ — ‰ xs dj                  «       j                  «       Š ‰ t        v rt        j                  ‰ «       t        ‰ «      }|D �ch c]6  }t        |t        «      sŒ|j                  d«      sŒ&|j                  d«      ’Œ8 }}t        ˆ fd„|D «       «      }|D �cg c]  }t        j                  ‰ |«      ‘Œ }}t        ˆ fd„|D «       «      }|rJt        «       j                  d«      }t        |t        «      r|j                  ‰ «      nd }	|t        |	«      z  }‰ j                  t        «      r$t!        ‰ |«      \  }
}||
z  }|t#        ||«      z  }nÈt%        ‰ |«      \  }}t'        ‰ |«      \  }}||xs |z  }‰ t        v xr t        |«      xr t)        ‰ «       }|rY|D �cg c]  }|j*                  |v sŒ|‘Œ }}|D �cg c]  }|j*                  |vsŒ|‘Œ }}|t#        |||z  d¬«      z  }||z   |d d  n|t#        |||z  d¬«      z  }|t-        ‰ |«      z  }|rR|D �ch c]  }|j*                  ’Œ }}t/        ‰ t1        |d„ ¬	«      D �cg c]  }|j3                  «       ‘Œ c}||z
  ¬
«       t5        ‰ |«      }‰ t        v rt)        ‰ «      st7        |«      |_        |S c c}w c c}w c c}w c c}w c c}w c c}w )Nr¨   rƒ   c              3  ó`   •K  — | ]%  }t        |t        «      xr t        |‰«      |k7  –— Œ' y ­wr™   )rx   rÜ   r   ©rì   r±   r}   s     €r7   rí   zload_pool.<locals>.<genexpr>±  s7   øè ø€ ò àô 	�7œDÓ!ÒhÔ&JÈ7ÐT\Ó&]ÐahÑ&hÓhñùs   ƒ+.c           	   3  óÐ   •K  — | ]]  }t        |t        «      xrG t        ‰|j                  d «      |j                  dt        «      «      |j                  dt        «      k7  –— Œ_ y­w)rˆ   r   N)rx   rÜ   r€   r    r|   r¶  s     €r7   rí   zload_pool.<locals>.<genexpr>¶  sd   øè ø€ ò 'ð
 ô	 	�7œDÓ!ò 	9Ü%Ø�g—k‘k .Ó1°7·;±;¸{ÔL]Ó3^ó
à�[‰[˜Ô&7Ó8ñ9ó	9ñ'ùs   ƒA#A&r•  Fr£  c                ó   — | j                   S r™   rê   )r  s    r7   rA   zload_pool.<locals>.<lambda>é  s
   € È4Ï=É=€ rB   rÊ  r
  )rÀ   rö   r"   rÁ   Ú#heal_forked_single_use_oauth_grantsr0   rx   rÜ   r    r@  r‚   r·   r'   r_  rz   r9  r³  r¨  r„  r¢  r–  rƒ   r:  r±  rN  r½   rÆ  r8  r�  )r}   Úraw_entriesrí  Údisk_idsr§  r±   rò   Úraw_needs_auth_normalizationÚactive_poolÚactive_entriesÚcustom_changedÚcustom_sourcesÚsingleton_changedÚsingleton_sourcesÚenv_changedÚenv_sourcesÚborrowing_root_grantÚborrowedÚothersrä   Únew_idsr’  s   `                     r7   Ú	load_poolrÉ  ©  sé  ø€ Ø’˜B×%Ñ%Ó'×-Ñ-Ó/€HØÔ4Ñ4ô 	×4Ñ4°XÔ>Ü& xÓ0€KØ%0ÖX ´J¸qÄ$Õ4GÈAÏEÉEÐRVÍK�—‘�d•ÐX€HÐXÜó à"ôó €Gð MXÖXÀÔ×)Ñ)¨(°GÕ<ÐX€GÐXÜ#&ó 'ð
 #ô'ó $Ð ñ $ô 'Ó(×,Ñ,Ð->Ó?ˆÜ6@ÀÌdÔ6S˜Ÿ™¨Ô2ÐY]ˆØ”4˜Ó'Ñ'ˆà×ÑÔ-Ô.Ü):¸8ÀWÓ)MÑ&ˆ˜Ø�>Ñ!ˆØÔ.¨w¸ÓGÑG‰ä/DÀXÈwÓ/WÑ,ÐÐ,Ü#1°(¸GÓ#DÑ ˆ�[ØÐ$Ò3¨Ñ3ˆð Ô9Ð9ò :Ü�X“ò:ä/°Ó9Ð9ð 	ñ
  ð
 $+Ö?˜a¨a¯d©d°hÒ.>šÐ?ˆHÐ?Ø!(ÖA˜A¨A¯D©D¸Ò,@’aÐAˆFÐAØÔ2ØÐ)¨KÑ7È5ôñ ˆGð " FÑ*ˆG‘A‰JàÔ2ØÐ*¨[Ñ8ÈEôñ ˆGð 	Ô-¨h¸Ó@Ñ@ˆáØ)0Ö1 �5—8“8Ð1ˆÐ1ÜØÜ*0°Ñ>XÔ*YÖZ ˆU�]‰]�_ÒZØ  7Ñ*õ	
ô
 ˜( GÓ,€Dð Ô4Ñ4Ô=XÐYaÔ=bÜ"% h£-ˆÔØ€KùòC Yùò
 YùòH @ùÚAùò 2ùò [s<   ÁJ*Á&J*Á8J*Â%J/Æ-J4ÇJ4ÇJ9ÇJ9È+J>ÉK
)rÊ   úOptional[dict])rÊ   zCallable[[str, str], bool])r}   ry   r~   r   r   r   rÊ   ry   )r~   ry   rÕ   ry   rÊ   ry   )rˆ   r   rÊ   zOptional[Tuple[str, str]])rä   r‚   rå   rs   rÊ   r_  )rò   r  rÊ   r…   )r‡   ry   rÊ   r_  )r  rŒ   rú   r_  rg   r‰   rÊ   r…   )r¼   r   rÊ   r‹   )r  r   rä   rË   rÊ   r_  )r  r  rÊ   rs   )rä   r‚   rú   r_  rÊ   r‹   )r¤   ry   rÊ   ry   )r*  r   rÊ   ry   r™   )r.  rÊ  )r4  ry   rä   rs   rÊ   r8  )r;  ry   rÊ   r8  )r4  ry   rä   rs   rÊ   ú	List[str])rŽ   r‰   r;  r‰   rÊ   rË  )rŽ   r‰   r;  r‰   rÊ   r‰   )rÊ   rË  )rT  ry   rÊ   r  )r}   ry   rÊ   ry   )r`  ry   ra  ry   rŽ   r‰   rÊ   r_  )r`  ry   ra  ry   rb  ry   rÊ   r_  )rä   r   rŽ   r   rÊ   r_  )rp  r   r}   r‰   rŽ   r‰   rÊ   r_  )r}   r‰   rŽ   r‰   rÊ   ry   )r€  úOptional[Path]rÊ   rÌ  )rŠ  ry   r  rs   rÊ   r  )r’  z'CredentialPool'rä   rË   rÊ   rÌ  )r}   ry   rÊ   r_  )rÊ   rÌ  )
r}   ry   rž  úList[Dict[str, Any]]r€  r   rš  úOptional[Iterable[str]]rÊ   r  )
r}   ry   rž  rÍ  r®  rÎ  rš  rÎ  rÊ   r  )
rò   r  r}   ry   r‡   ry   r±   rs   rÊ   r_  )r}   ry   rò   r  rÊ   r_  )rò   r  r<  r  rÊ   r_  )rW  r@  rÊ   r  )rW  r@  rH  rs   rÊ   r  )r~   ry   rÊ   r  r!  )r}   ry   rò   r  rÊ   rH  rU  )r}   ry   rk  ry   rÊ   r  )rk  ry   r~   ry   rŽ   ry   rÊ   rs   )r–  rË  rò   r  rÊ   rË  )rò   r  rB  r  r¤  r_  rÊ   r_  )rT  ry   rò   r  rÊ   rH  )r}   ry   rÊ   rÆ  )°rÃ  Ú
__future__r   Úagent.credential_pool_adminr   Ú%agent.credential_pool_model_cooldownsr   r   Úloggingr}  rU   rÎ  r  r¬   ÚreÚdataclassesr   r   r	   r
   r   Úpathlibr   Útypingr   r   r   r   r   r   r   r   Úhermes_constantsr   r5   r   Úagent.secret_scoper   rM  r   Úagent.retry_utilsr   Ú hermes_cli.auth_plugin_providersr   Úagent.credential_pool_pluginr   r   Úagent.credential_persistencer   r   r   rC   ÚauthrÁ   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   Ú	getLoggerr£   rˆ  r8   rE   r“  r#  rå  Ú	frozensetr  r  rÎ  r{   r|   r¯   rã   rX  rà  rÝ  rß  rY  rý   rþ   rÿ   r  r  r   rÖ  r9  rŸ   rB  rt   rÎ   ru   r€   r‚   rØ   rà   rç   ró   rø   r  r©   r  r   r$  r(  r+  r0  r6  r=  rA  rF  rI  rO  rU  r\  rf  ri  rm  rs  rx  rÒ  rƒ  rŒ  r“  r–  r˜  r¬  r±  r¹  ÚREFRESHABLE_OAUTH_PROVIDERSrT  rh  rÇ  r6   r¾  rÆ  r+  r5  r:  r>  r@  rZ  r^  r8  r_  rb  re  rt  ry  r}  r�  r„  rˆ  r‰  r�  r’  rŸ  rš  r¢  r¨  r³  rÉ  r>   rB   r7   ú<module>rá     s,  ðÚ Bå "å @ß hã Û 	Û Û Û Û Û 	ß 2Ñ 2ß 'Ý ß L× LÓ Lå 0Ý &ß HÝ 6Ý @ß c÷ñ õ
 #÷÷ ÷ ÷ ÷ ð* 
ˆ×	Ñ	˜8Ó	$€óó$ð €	ØÐ ð
 €ñ #ò $ó Ð ð $?Ð  ð !-Ð à€ØÐ à€Ø,˜o¨\Ð:Ð à"Ð Ø$Ð Ø€Ø"Ð àØØØð	Ð ð #Ð Ø#Ð Ø 'Ð ð )+Ð %ð #Ð ð %9Ð !ð -1Ð )ð Ð ñ ò ó €ðÐ ð ØØØØØñ!€ˆ~ó ð G˜mÐF¨]¸IÑF€ˆ.Ó Fó/ð ÷kð kó ðkó\ó/ó"|ó"FóUð "Ø$(ñ	Øðð ðð "ð	ð
 	óó<ó2ó"ð0 JOõ ó$2ó
.ôDó"óóð $(ðØðà ðð óô:1óógóVðØðàðð ðð 
ó	ð8Øðàðð ðð 
ó	ó6Uð$ #ñ	,RØð,Ràð,Rð ð	,Rð
 
ó,Ró^ðD )*Ð %óð.cØðcØ+ðcà	ócó@ó7ó	ð 6:ñ&=Øð&=Ø1ð&=Ø@Dð&=à2ð&=ð 
ó&=ðZ ,0Ø26ñ%Øð%à"ð%ð )ð	%ð
 0ð%ð 
ó%ð^ mØgñEÐ ÐAó ñ (¨°fÐ([Ð?ZÐ([Ó\Ð ð !KÐ ð ;Ø5ñÐ ð ØØ!Øñ	€ô�9ô ô|Ð-Ð/Oô |óF,1#ðj Ø#$ØØØñÐ óó0÷1ñ 1ó03ólñF -0«EÐ  Ó 1ó
ó,ó
0;óf>ó(Aó6ó8ó,ñ( #&£%Ð �xÓ 'óó,ð( *Ø ñÐ óó6)ð` #ñ	Ø#ðàðð ð	ð
 
óó6-ô`HrB   