+
    iHàjÚ2  ã                   óš   € R t ^ RIt^ RIt^ RIHt ^ RIHt ]P                  ! R4      tR R lt	R R lt
R	 R
 ltR R lt ! R R4      tR# )a  Rate-limit / credits header capture and low-credit notices for ``AIAgent``.

Parses provider response headers into ``_rate_limit_state`` / ``_credits_state`` and emits sticky notices.
Extracted from ``run_agent.py``; every method resolves through ``AIAgent``'s MRO unchanged.
N©ÚAny)Úis_truthy_valueÚ	run_agentc                ó$   € V ^8„  d   QhR\         /# ©é   Úreturn©Úbool)Úformats   "Ú=/home/ubuntu/.hermes/hermes-agent/agent/rate_limit_credits.pyÚ__annotate__r      s   € ÷ Rñ RœTñ Ró    c                óú   € \        V RR4      ;'       g    RP                  4       P                  4       R8X  d   R# ^ RIHp \        \        V RR4      ;'       g    R4      p\        V4      ;'       d
    V! VR4      # )zCTrue for the Nous provider (or a route to a nousresearch.com host).ÚproviderÚ ÚnousT)Úbase_url_host_matchesÚbase_urlznousresearch.com)ÚgetattrÚstripÚlowerÚutilsr   Ústrr   )Úagentr   r   s   &  r   Ú_is_nous_agentr      si   € ä��z 2Ó&×,Ð,¨"×3Ñ3Ó5×;Ñ;Ó=ÀÔGÙÝ+Ü”7˜5 *¨bÓ1×7Ð7°RÓ8€HÜ�‹>×QÐQÑ3°HÐ>PÓQÐQr   c                ó$   € V ^8„  d   QhR\         /# )r   Úhttp_responser   )r   s   "r   r   r      s   € ÷ Zñ Z¤Sñ Zr   c                ó(   € V e   \        V RR4      # R# )z>Headers of a response, or None when there is nothing to parse.NÚheaders)r   )r   s   &r   Ú_response_headersr!      s   € à6CÒ6OŒ7�= )¨TÓ2ÐYÐUYÐYr   c                ó$   € V ^8„  d   QhR\         /# r   )r   )r   s   "r   r   r      s   € ÷ Lñ L”cñ Lr   c                 ó.   € V e   RV ^d,          ,          # R# )Nz%.0f%%ún/a© )Úfractions   &r   Ú_pctr'      s   € Ø,4Ò,@ˆH˜ 3�Õ'ÐKÀeÐKr   c                ó   € V ^8„  d   QhRR/# ©r   r	   Nr%   )r   s   "r   r   r   "   s   € ÷ Eñ E¨$ñ Er   c                óT   € Wn         V P                  f   VP                  V n        R# R# )zcRetain-last-known: overwrite state and latch session-start remaining on the first header ever seen.N)Ú_credits_stateÚ_credits_session_start_microsÚremaining_micros)r   Ústates   &&r   Ú_adopt_credits_stater/   "   s'   € à ÔØ×*Ñ*Ò2Ø.3×.DÑ.DˆÖ+ñ 3r   c                   ó¼   a € ] tR t^)t o RtV 3R lR ltR tV 3R lR ltV 3R lR ltR	R
/V 3R lR llt	V 3R lR lt
V 3R lR ltR tR tV 3R lR ltRtV tR# )ÚRateLimitCreditsMixinzGRate-limit + credits header capture and notices (see module docstring).c                ó$   <€ V ^8„  d   QhRS[ RR/# ©r   r   r	   Nr   )r   Ú__classdict__s   "€r   r   Ú"RateLimitCreditsMixin.__annotate__,   s   ø€ ÷ ñ ±#ð ¸$ñ r   c                ó    € \        V4      pV'       g   R#  ^ RIHp V! W P                  R7      pVe	   W@n        R# R#   \
         d     R# i ; i)zUParse x-ratelimit-* headers from an HTTP response and cache the state (never raises).N)Úparse_rate_limit_headers©r   )r!   Úagent.rate_limit_trackerr7   r   Ú_rate_limit_stateÚ	Exception)Úselfr   r    r7   r.   s   &&   r   Ú_capture_rate_limitsÚ*RateLimitCreditsMixin._capture_rate_limits,   sK   € ä# MÓ2ˆßÙð	ÝIÙ,¨W¿}¹}ÔMˆEØÒ Ø).Ö&ñ !øäô 	Úð	ús   —#> ¾AÁAc                ó   € V P                   # )z1Return the last captured RateLimitState, or None.)r:   ©r<   s   &r   Úget_rate_limit_stateÚ*RateLimitCreditsMixin.get_rate_limit_state9   s   € à×%Ñ%Ð%r   c                ó$   <€ V ^8„  d   QhRS[ RR/# r3   r   )r   r4   s   "€r   r   r5   =   s   ø€ ÷ ñ ¹ð Àñ r   c                ór   € \        V4      pV'       g   R#  ^ RIHp V! W4       R#   \         d     R# i ; i)zïRecord the Nous gateway's ``x-nous-model-switch`` header (a named account asked for the
free tier's model; the gateway served its backing model and named it). Applied between
calls by ``hermes_cli.anon_auth.apply_model_switch``. Fail-open.N)Únote_model_switch)r!   Úhermes_cli.anon_authrE   r;   )r<   r   r    rE   s   &&  r   Ú_capture_nous_model_switchÚ0RateLimitCreditsMixin._capture_nous_model_switch=   s6   € ô $ MÓ2ˆßÙð	Ý>Ù˜dÖ,øÜô 	Úð	ús   —' §6µ6c                ó$   <€ V ^8„  d   QhRS[ RR/# r3   r   )r   r4   s   "€r   r   r5   J   s   ø€ ÷ -ñ -Áð -Èñ -r   c                óJ   € V P                  V4       V P                  V4       R# )z…Capture rate-limit + credits state from Anthropic Messages response headers (the SDK's
aggregated ``Message`` drops them). Fail-open.N)r=   Ú_capture_credits)r<   r   s   &&r   Ú#_capture_anthropic_response_headersÚ9RateLimitCreditsMixin._capture_anthropic_response_headersJ   s    € ð 	×!Ñ! -Ô0Ø×Ñ˜mÖ,r   ÚrefusedFc                ó*   <€ V ^8„  d   QhRS[ RS[RR/# )r   r   rN   r	   N)r   r   )r   r4   s   "€r   r   r5   P   s(   ø€ ÷ F%ñ F%©cð F%¹tð F%ÐPTñ F%r   c               óâ  €  ^ RI Hp V! 4       pVe¥   \        W4       \	        V RR4      p\        V\        4      '       d   RVR&   \        P                  RVP                  VP                  ;'       g    RVP                  VP                  \        VP                  4      4       V P                  4        R# \!        V4      pV'       g   R# \#        \$        P&                  P)                  R4      4      p ^ R	I Hp V! W`P,                  R
7      p	T	f    T'       d   \        P                  R4       R# T	P.                  '       d   \1        T 4      '       g   R# T	P.                  '       dw   \2        ;QJ d    R T 4       F  '       g   K   RM	  RM! R T 4       4      '       g=   \	        T RR4      p
T
e,   ^ RIpTP7                  YšP8                  T
P:                  R7      p	T	P.                  '       d.   T'       g&   T	P<                  f   ^ RIpTP7                  T	RR7      p	\        Y	4        ^ RIH p T! T	4       T'       dÒ   T PE                  4       p\        P                  RT	P                  T	P                  ;'       g    RT	P                  T	P                  \        T	P                  4      Te   RTR,          ,          MRT	PF                  \I        R4      8w  d   T	PF                  R R2MRT	PJ                  '       d   RT	PJ                   2MR4	       T P                  4        R#   \         d    Rp EL#i ; i  \         d     R# i ; i  \         d    \        PC                  RRR7        EL6i ; i)u  Parse x-nous-credits-* headers, cache CreditsState, fire threshold notices.

The PARSE is swallowed (miss â†’ keep last-known); notice EVALUATION WARNS on failure so a
depletion-notice bug cannot vanish silently. HERMES_DEV_CREDITS_FIXTURE injects a chosen state instead.
)Údev_fixture_credits_stateNÚ_credits_latchTÚseen_below_90u    credits â–¸ [FIXTURE] remaining=%d (%s) Â· paid=%s Â· denom=%s Â· used=%s (real headers bypassed â€” `echo clear` / unset HERMES_DEV_CREDITS_FIXTURE to restore)Ú?ÚHERMES_DEV_CREDITS)Úparse_credits_headersr8   uo   credits â–¸ response had no valid x-nous-credits-* headers (miss â€” producer off / non-Nous path / >TTL stale)c              3   óF   "  € T F  qP                  4       R 8H  x € K  	  R# 5i)zx-nous-credits-purchased-microsN)r   )Ú.0Úks   & r   Ú	<genexpr>Ú9RateLimitCreditsMixin._capture_credits.<locals>.<genexpr>{   s   é € Ð,mÑelÐ`a¯W©W«YÐ:[Ö-[Óelùs   ‚!Fr+   )Úpurchased_microsÚpurchased_usd)Ú	unmetered)Úsync_plan_snapshotzPlan snapshot sync failed©Úexc_infouY   credits â–¸ remaining=%d (%s) Â· paid=%s Â· denom=%s Â· used=%s Â· Î”spent=%s Â· age=%s%su   %.1fÂ¢i'  r$   Úinfz.0fÚsu    Â· disabled=r   )&Úagent.credits_trackerrQ   r;   r/   r   Ú
isinstanceÚdictÚloggerÚinfor-   Úremaining_usdÚpaid_accessÚdenominator_kindr'   Úused_fractionÚ_emit_credits_noticesr!   r   ÚosÚenvironÚgetrV   r   Úis_usage_windowr   ÚanyÚdataclassesÚreplacer\   r]   Úspend_sourceÚagent.plan_snapshot_syncr_   ÚdebugÚget_credits_spent_microsÚage_secondsÚfloatÚdisabled_reason)r<   r   rN   rQ   ÚfixtureÚlatchr    ÚdevrV   r.   Úpriorrs   r_   Úspents   &&$           r   rK   Ú&RateLimitCreditsMixin._capture_creditsP   sã  € ð	ÝGÙ/Ó1ˆGð ÒÜ  Ô/Ü˜DÐ"2°DÓ9ˆEÜ˜%¤×&Ò&à)-��oÑ&Ü�K‰Kðià×(Ñ(¨'×*?Ñ*?×*FÐ*FÀ3È×H[ÑH[Ð]d×]uÑ]uÜ�W×*Ñ*Ó+ô	-ð
 ×&Ñ&Ô(ÙÜ# MÓ2ˆßÙÜœbŸj™jŸn™nÐ-AÓBÓCˆð	ÝCÙ)¨'¿M¹MÔJˆEð Š=ßÜ—‘ð Sô Táà× × Ð ¬¸×)=Ò)=ÙØ× × Ð ¯«Ñ,mÑelÓ,m¯¯ªÑ,mÑelÓ,m×)mÒ)mä˜DÐ"2°DÓ9ˆEØÒ Û"Ø#×+Ñ+¨E×DZÑDZØ:?×:MÑ:Mð ,ó O�à× × Ð ¯°U×5GÑ5GÒ5OÛØ×'Ñ'¨¸Ð'Ó>ˆEÜ˜TÔ)ð	Eõ DÙ˜uÔ%÷ à×1Ñ1Ó3ˆEÜ�K‰KØkØ×&Ñ&¨×(;Ñ(;×(BÐ(B¸sÀE×DUÑDUÐW\×WmÑWmÜ�U×(Ñ(Ó)È5ÒK\¨H¸À½Ö,FÐbgØ16×1BÑ1BÄeÈEÃlÔ1R�E×%Ñ% cÐ*¨!Ñ,ÐX]Ø=B×=R×=RÐ=R�= ×!6Ñ!6Ð 7Ñ8ÐXZô\ð 	×"Ñ"Ö$øô{ ô 	Ø‹Gð	ûô0 ô 	Úð	ûô4 ô 	EÜ�L‰LÐ4¸tˆL×Dð	Eús5   ‚L" Ã5L6 È)M Ì"L3Ì2L3Ì6MÍMÍ"M.Í-M.c                ó   <€ V ^8„  d   QhRR/# r)   r%   )r   r4   s   "€r   r   r5   ˜   s   ø€ ÷ 6Sñ 6S tñ 6Sr   c           
     óœ  € \        V RR4      f   \        V RR4      f   R# \        V RR4      pV P                  4       '       d   Vf   R#  ^ RIHpHpHpHpHp \        V RR4      pVf   V! 4       ;qpn        T! \        V RR4      ;'       g    R\        V R	R4      ;'       g    R4      pVP                  ;'       g!    VP                  ;'       g    VP                  p	V	'       d   V'       g   V! V 4      '       d   R# R
p
VP                  '       dJ   V'       gB   ^ RIHpHp T! \        V RR4      ;'       g    RT! \        V R	R4      ;'       g    R4      4      p
\        V RR4      pVP                  '       d   ^ RIHp V! W×4       V! WWŠR7      w  ppVP                  '       d   ^ RIHp V! W×4       MV'       d   ^ RIHp V! V4       V F  pV P'                  V4       K  	  V F  pV P)                  V4       K  	  V! \        V RR4      VP+                  R4      4       R#   \,         d    \.        P1                  RRR7        R# i ; i)zÀRun the threshold policy and emit notices (shared by the warm path and the cold-start seed).

Runs only when a notice consumer is bound; WARNS on failure; clears FIRST so depleted lands last.
Únotice_callbackNÚnotice_clear_callbackr+   )Ú_remember_shown_bandÚevaluate_credits_noticesÚis_free_tier_modelÚnew_credits_latchÚ%rewarm_pricing_before_depleted_noticerR   Úmodelr   r   F)Úcached_pricingÚis_top_tierÚ
session_id)Úrestore_window_notices)Úmodel_is_freeÚmodel_is_top_tier)Úremember_window_notices)Úforget_window_noticesÚ
usage_bandz%credits notice evaluation/emit failedTr`   )r   Ú_credits_notices_enabledrd   r†   r‡   rˆ   r‰   rŠ   rR   ÚdepletedÚwindow_exhaustedÚtop_tier_exhaustedrq   Úagent.plan_limitsrŒ   r�   r�   r’   r“   Ú_emit_notice_clearÚ_emit_noticerp   r;   rg   Úwarning)r<   r.   r†   r‡   rˆ   r‰   rŠ   r}   r�   Úblockingr‘   rŒ   r�   rŽ   r�   Úto_showÚto_clearr’   r“   ÚkeyÚnotices   &                    r   rm   Ú+RateLimitCreditsMixin._emit_credits_notices˜   s  € ô
 �4Ð*¨DÓ1Ò9¼gÀdÐLcÐeiÓ>jÒ>rÙÜ˜Ð.°Ó5ˆØ×,Ñ,×.Ò.°%²-Ùð,	S÷õ ô ˜DÐ"2°DÓ9ˆEØŠ}Ù.?Ó.AÐA�Ô+á.¬w°t¸WÀbÓ/I×/OÐ/OÈRÔQXÐY]Ð_iÐkmÓQn×QtÐQtÐrtÓuˆMð —~‘~×[Ð[¨×)?Ñ)?×[Ð[À5×C[ÑC[ˆHß§Ñ2WÐX\×2]Ò2]ÙØ %ÐØ×$×$Ð$¯]çIÙ$/´¸¸gÀrÓ0J×0PÐ0PÈbÙ0>¼wÀtÈZÐY[Ó?\×?bÐ?bÐ`bÓ0có%eÐ!ä   |°TÓ:ˆJØ×$×$Ð$ÝHÙ& zÔ9Ù 8Ø¨Mô!`ÑˆG�Xà×$×$Ð$ÝIÙ'¨
Õ:ßÝGÙ% jÔ1Û�Ø×'Ñ'¨Ö,ñ  ã!�Ø×!Ñ! &Ö)ñ "ñ !¤¨¨|¸TÓ!BÀEÇIÁIÈlÓD[Ö\øÜô 	SÜ�N‰NÐBÈTˆN×Rð	Sús]   ÁAH% ÂH% Â#H% Â=H% ÃH% Ã&H% Ã.H% Ã>H% ÄH% ÄH% Ä8H% ÅA-H% Æ>A%H% È%"IÉ
Ic                ó    <€ V ^8„  d   QhRS[ /# r   r
   )r   r4   s   "€r   r   r5   Ð   s   ø€ ÷ ñ ©$ñ r   c                ó
  € \        V RR4      pVe   V# Rp ^ RIHp V! 4       ;'       g    / P                  R4      p\	        V\
        4      '       d   RV9   d   \        VR,          4      pY n        T#   \         d     Li ; i)zh``display.credits_notices``, read once per agent and cached (UI noise, not correctness); fail-open True.Ú_credits_notices_enabled_cacheNT)Úload_configÚdisplayÚcredits_notices)	r   Úhermes_cli.configr¦   rp   re   rf   r   r;   r¥   )r<   ÚcachedÚenabledr¦   r§   s   &    r   r•   Ú.RateLimitCreditsMixin._credits_notices_enabledÐ   s‰   € ä˜Ð?ÀÓFˆØÒØˆMØˆð	Ý5Ù"“}×*Ð*¨×/Ñ/°	Ó:ˆGÜ˜'¤4×(Ò(Ð->À'Ô-IÜ˜wÐ'8Õ9Ó:�ð /6Ô+Øˆøô ô 	Ùð	ús   —A4 «AA4 Á4BÂBc                ó   € V P                   # )z/Return the last captured CreditsState, or None.)r+   r@   s   &r   Úget_credits_stateÚ'RateLimitCreditsMixin.get_credits_stateà   s   € à×"Ñ"Ð"r   c                óŒ   € V P                   e   V P                  f   R# V P                   V P                  P                  ,
          # )z\Session-cumulative micros spent = first_seen_remaining - current_remaining. None if no data.N)r,   r+   r-   r@   s   &r   rx   Ú.RateLimitCreditsMixin.get_credits_spent_microsä   s9   € à×-Ñ-Ò5¸×9LÑ9LÒ9TÙØ×1Ñ1°D×4GÑ4G×4XÑ4XÕXÐXr   c                ó$   <€ V ^8„  d   QhRS[ RR/# r3   r   )r   r4   s   "€r   r   r5   ê   s   ø€ ÷ ñ ¹Cð ÀDñ r   c                ót  € \        V4      pV'       g   R#  VP                  R4      pV'       g   R# VP                  4       R8X  d<   V ;P                  ^,          un        \        P                  RV P                  4       R# \        P                  RVP                  4       4       R#   \         d     R# i ; i)zNLog X-OpenRouter-Cache-Status; HITs count in ``_or_cache_hits``. Never raises.Nzx-openrouter-cache-statusÚHITz)OpenRouter response cache HIT (total: %d)zOpenRouter response cache %s)r!   rp   ÚupperÚ_or_cache_hitsrg   rh   rw   r;   )r<   r   r    Ústatuss   &&  r   Ú_check_openrouter_cache_statusÚ4RateLimitCreditsMixin._check_openrouter_cache_statusê   s‡   € ä# MÓ2ˆßÙð
	Ø—[‘[Ð!<Ó=ˆFßÙØ�|‰|‹~ Ô&Ø×#Ò# qÕ(Õ#Ü—‘ÐGÈ×I\ÑI\Ö]ä—‘Ð;¸V¿\¹\»^ÖLøÜô 	Úð	ús   —B( ²AB( Â$B( Â(B7Â6B7)rR   r¥   r:   N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r=   rA   rG   rL   rK   rm   r•   r®   rx   r¸   Ú__static_attributes__Ú__classdictcell__)r4   s   @r   r1   r1   )   sl   ø‡ € ÙQ÷ð ò&÷ð ÷-ð -ðF%Àe÷ F%ò F%÷P6Sð 6S÷pð ò #òY÷ö r   r1   )r¾   Úloggingrn   Útypingr   r   r   Ú	getLoggerrg   r   r!   r'   r/   r1   r%   r   r   Ú<module>rÄ      sN   ðñó
 Û 	Ý å !ð 
×	Ò	˜;Ó	'€õRõZõ
LõE÷Pó Pr   