+
    iHàjh  ã                  óâ   € R t ^ RIHt ^ RIt^ RIt^ RIHtHt ^ RIH	t	H
t
HtHtHt ^ RIHt ^ RIHt ]	'       d	   ^ RIHtHt ]P*                  ! ]4      tRtR	 R
 ltR R ltR R ltR R ltR# )uR  Plugin-provider refresh support for the credential pool (#116408).

A model-provider plugin makes its pooled OAuth rows refreshable by shipping
``ProviderProfile.refresh_credential(entry) -> Mapping | None``. The pool owns
the locking, persistence and failure classification around that hook; the
plugin owns only the token POST. This sibling module keeps that logic out of
``agent/credential_pool.py`` (near the size cap).

Contract (documented in website/docs/developer-guide/model-provider-plugin.md):

* the hook returns a mapping of rotated values â€” dataclass field names
  (``access_token``, ``refresh_token``, ``expires_at_ms`` â€¦) replace the row's
  fields, every other key (``expires_in``, ``token_type``, ``scope`` â€” the
  natural token-endpoint shape) lands in ``entry.extra``; ``None``/empty = could not
  rotate and the pool benches the row like a failed refresh POST;
* raising ``AuthError(..., relogin_required=True)`` (or a grant-dead OAuth code)
  is terminal: the row goes DEAD with a WARNING naming ``hermes auth add``;
  any other exception is transient and only benches the row.
)ÚannotationsN)ÚfieldsÚreplace)ÚTYPE_CHECKINGÚAnyÚMappingÚOptionalÚTuple)Ú_OAUTH_GRANT_DEAD_CODES)Ú	AuthError)ÚCredentialPoolÚPooledCredentialiÀÔ c               ó    € V ^8„  d   QhRRRR/# )é   Úentryr   ÚreturnÚbool© )Úformats   "ÚA/home/ubuntu/.hermes/hermes-agent/agent/credential_pool_plugin.pyÚ__annotate__r   '   s   € ÷ uñ uÐ"2ð u°tñ uó    c                óº   € V P                   RJ;'       dG    \        V P                   4      \        \        P                  ! 4       R,          4      \        ,           8*  # )a-  An expiry-stamped OAuth row (a plugin's, or Anthropic's) is due within the skew of ``expires_at_ms``.

The token endpoint's ``expires_in`` is the only clock: many providers issue opaque bearers (Google's
``ya29.*``) with no JWT ``exp`` to decode, so without this a long session sends the dead bearer.
Niè  )Úexpires_at_msÚintÚtimeÚ_EXPIRY_SKEW_MS)r   s   &r   Úplugin_row_is_expiringr   '   sD   € ð ×Ñ dÐ*×tÐt¬s°5×3FÑ3FÓ/GÌ3ÌtÏyÊyË{Ð]aÕOaÓKbÔetÕKtÑ/tÐtr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   Úresultr   r   r   )r   s   "r   r   r   0   s(   € ÷ Gñ GÐ'7ð GÀð GÐIYñ Gr   c                óÚ  € V'       g   V # \        V4      p\        \        V 4      4       Uu0 uF  q3P                  kK  	  upRR0,
          pVP	                  4        UUu/ uF  w  rVWT9   g   K  WVbK  	  pppVP	                  4        UUu/ uF  w  rVWT9  g   K  VR8w  g   K  WVbK  	  pppV'       d   / V P
                  CVCVR&   V'       d   \        V 3/ VB # T # u upi u uppi u uppi )uŸ  Merge a ``refresh_credential`` return value into *entry*.

Field names go through ``dataclasses.replace``; everything else is merged into ``extra``
(mirroring ``PooledCredential.from_dict``). Before this split a token-endpoint-shaped mapping
with ``expires_in`` raised ``TypeError`` inside ``replace`` and the pool benched a row whose
single-use refresh token the server had already rotated â€” the login was lost.
ÚproviderÚextra)Údictr   ÚtypeÚnameÚitemsr"   r   )	r   r   ÚmappingÚfÚfield_namesÚkÚvÚfield_updatesÚextra_updatess	   &&       r   Úapply_plugin_refresh_resultr.   0   sÒ   € ÷ ØˆÜ!% f£€GÜ#)¬$¨u«+Ô#6Ó7Ñ#6˜a—6”6Ñ#6Ñ7¸:ÀwÐ:OÕO€KØ&-§m¡m¤oÔJ¡o™d˜a¸Ñ9I”T�Q’T¡o€MÑJØ&-§m¡m¤oÔb¡o™d˜a¸Ñ9M”TÐRSÐWaÑRa”T�Q’T¡o€MÑbßØ!A E§K¡KÐ!A°=Ð!Aˆ�gÑß.;Œ7�5Ñ*˜MÑ*ÐFÀÐFùò 8ùÛJùÛbs#   ¬CÁ
C!Á.C!Â

C'ÂC'Â"C'c               ó    € V ^8„  d   QhRRRR/# )r   ÚexcÚBaseExceptionr   r   r   )r   s   "r   r   r   C   s   € ÷ 
Uñ 
U¨-ð 
U¸Dñ 
Ur   c                ó¦   € \        V \        4      '       g   R# \        V P                  4      ;'       g    V P                  ;'       g    R\
        9   # )a—  True when retrying the same plugin refresh token cannot succeed.

Plugins have no per-provider code table, so the predicate is the structural one every built-in
flow shares: a structured ``AuthError`` that asks for a re-login, or one carrying a grant-dead
OAuth code. Transport errors, 429/5xx-shaped ``RuntimeError`` and plain ``AuthError`` without
that signal stay transient (EXHAUSTED for one cooldown).
FÚ )Ú
isinstancer   r   Úrelogin_requiredÚcoder
   )r0   s   &r   Ú is_terminal_plugin_refresh_errorr7   C   s=   € ô �cœ9×%Ò%ÙÜ�×$Ñ$Ó%×TÐT¨#¯(©(¯.¨.°bÔ=TÑ)TÐTr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   Úpoolr   r   r   r0   Ú	Exceptionr   z'tuple[bool, Optional[PooledCredential]]r   )r   s   "r   r   r   P   s,   € ÷ ñ Ø
ðØ!1ðØ8Aðà,ñr   c                ó  € ^ RI Hp V P                  V4      pVP                  VP                  8w  d`   VP                  ;'       g    RP                  4       '       d6   \        P                  RV P                  4       RV P                  ! V3/ VB 3# \        V4      '       de   \        P                  RV P                  VP                  ;'       g    VP                  R,          W P                  4       V P                  W4       R# R# )	uc  Recovery for a plugin hook that raised: adopt a peer's rotation, or quarantine a dead grant.

Returns ``(handled, result)``; ``handled=False`` means the caller should bench the row as a
transient failure. A peer process may have rotated the single-use token between our in-lock
sync and the hook's POST â€” adopt that pair before classifying the failure.
)Ú_MARK_OKr3   uB   %s refresh failed but the pool store has newer tokens â€” adoptingTz�%s refresh token for %s is terminally invalid (%s); the credential leaves rotation. Re-run 'hermes auth add %s' to sign in again.:Né   N)TN)FN)Úagent.credential_poolr<   Ú_sync_entry_from_pool_storeÚrefresh_tokenÚaccess_tokenÚstripÚloggerÚdebugr!   Ú_adoptr7   ÚwarningÚlabelÚidÚ_mark_dead_refresh_grant)r9   r   r0   r<   Úsynceds   &&&  r   Úrecover_failed_plugin_refreshrK   P   sÓ   € õ /à×-Ñ-¨eÓ4€FØ×Ñ˜u×2Ñ2Ô2¸×8KÑ8K×8QÐ8QÈr×7XÑ7X×7ZÒ7ZÜ�‰ÐYÐ[_×[hÑ[hÔiØ�T—[’[ Ñ4¨8Ñ4Ð4Ð4Ü'¨×,Ò,ô 	�‰ð<à�M‰M˜5Ÿ;™;×6Ð6¨%¯(©(°2­,¸¿]¹]ô	
ð
 	×%Ñ% eÔ1ØÐØÐr   )Ú__doc__Ú
__future__r   Úloggingr   Údataclassesr   r   Útypingr   r   r   r   r	   Úhermes_cli.authr
   Úhermes_cli.auth_constantsr   r>   r   r   Ú	getLoggerÚ__name__rC   r   r   r.   r7   rK   r   r   r   Ú<module>rU      sV   ðñõ( #ã Û ß 'ß ?Õ ?å 3Ý /çßFà	×	Ò	˜8Ó	$€à€õuõGõ&
U÷r   