+
    jHàj�&  ã                  óD  € R t ^ RIHt ^ RIt^ RIt^ RIHt ^ RIHt ^ RI	H
t
HtHtHtHtHt RtRt^t^tR#t]! 0 R$m4      t]! RR	7       ! R
 R4      4       tR R lt]3R R lltR R ltR R ltR R ltR R ltR R ltR R lt R R lt!R R lt"R ^ /R! R" llt#R# )%u-  Memory-pressure bounds for the gateway's per-session AIAgent cache.

Each cached ``AIAgent`` pins its full live transcript (tens of MB on a tool-heavy
session); the LRU cap counts entries, not bytes, and the idle TTL defers eviction
for busy sessions, so neither sees actual memory use.  This module supplies that
signal â€” own anonymous RSS against a budget derived from the cgroup limit â€” and
``GatewayRunner`` sheds LRU transcripts via soft eviction (rebuilt from the
persisted session next turn).  Pure/read-only; config under ``agent.agent_cache``.
)ÚannotationsN)Ú	dataclass)ÚPath)ÚAnyÚCallableÚIterableÚListÚOptionalÚTuplegÍÌÌÌÌÌä?i   T)Úfrozenc                  ób   € ] tR t^ t$ RtRtR]R&   RtR]R&   RtR]R&   ]	t
R]R	&   ]tR]R
&   RtR# )ÚAgentCacheBoundsz®Operator-facing bounds.  ``max_size``/``idle_ttl_secs`` are ``None`` when unset
so ``gateway/run.py`` keeps its defaults; ``memory_high_mb`` ``None`` = pressure eviction off.NúOptional[int]Úmax_sizezOptional[float]Úidle_ttl_secsÚmemory_high_mbÚintÚmax_evictions_per_passÚprotect_recent© )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r   Ú__annotations__r   r   Ú_DEFAULT_MAX_EVICTIONS_PER_PASSr   Ú_DEFAULT_PROTECT_RECENTr   Ú__static_attributes__r   ó    ÚA/home/ubuntu/.hermes/hermes-agent/gateway/agent_cache_pressure.pyr   r       s=   ‡ ñfð #€HˆmÓ"Ø%)€M�?Ó)Ø$(€N�MÓ(Ø"AÐ˜CÓAØ1€N�C×1r   r   c               ó    € V ^8„  d   QhRRRR/# )é   Úvaluer   ÚreturnÚboolr   )Úformats   "r    Ú__annotate__r'   ,   s   € ÷ Bñ B�3ð B˜4ñ Br   c                ó\   € \        V \        4      ;'       d    \        V \        4      '       * # )N)Ú
isinstancer   r%   )r#   s   &r    Ú_is_intr*   ,   s!   € Ü�eœSÓ!×AÐA¬*°U¼DÓ*AÔ&AÐAr   c               ó$   € V ^8„  d   QhRRRRRR/# )r"   r#   r   ÚcastzCallable[[Any], Any]r$   r   )r&   s   "r    r'   r'   0   s'   € ÷ Añ A�Sð AÐ 4ð A¸sñ Ar   c                óš   €  \        V \        4      '       g   V f   RMV! V 4      pTe
   T^ 8”  d   T# R#   \        \        3 d     R# i ; i)zG``cast(value)`` if it is a positive number (bools rejected), else None.N)r)   r%   Ú	TypeErrorÚ
ValueError)r#   r,   Úparseds   && r    Ú	_positiver1   0   sS   € ðÜ# E¬4×0Ò0°E²M‘ÁtÈEÃ{ˆð Ò'¨F°Q¬Jˆ6Ð@¸DÐ@øô ”zÐ"ô Úðús   ‚$5 µA
Á	A
c               ó    € V ^8„  d   QhRRRR/# )r"   Úpathr   r$   r   r   )r&   s   "r    r'   r'   9   s   € ÷ 4ñ 4˜ð 4 ñ 4r   c                ó®   €  \        V P                  RR7      P                  4       4      p^ Tu;8  d	   R8  d   T#  R#   \        \        3 d     R# i ; i)z‘A cgroup memory limit file's value when it is a real cap; None for unreadable, empty,
``max``, or the v1 near-2^63 sentinel (all mean unlimited).ú	utf-8-sig©ÚencodingNl            )r   Ú	read_textÚstripÚOSErrorr/   )r3   Úlimits   & r    Ú_finite_limitr<   9   sZ   € ðÜ�D—N‘N¨K�NÓ8×>Ñ>Ó@ÓAˆð ˜Ö) Ô)ˆ5Ð3Ð)¨tÐ3øô ”ZÐ ô Úðús   ‚)? ¿AÁAc               ó   € V ^8„  d   QhRR/# ©r"   r$   r   r   )r&   s   "r    r'   r'   C   s   € ÷ ñ ˜]ñ r   c                 óX  € \         P                  R8w  d   R#  ^ RIHp  V ! 4       pV'       d   VR8w  d   RV 2.M. R.,           pV UUu. uF  pR F	  qC RV 2NK  	  K  	  uppR.,            F  p\        \        V4      4      pVf   K  Vu # 	  R#   \         d    Rp L|i ; iu uppi )	u  Memory limit this process runs under, if cgroup-capped.

Prefers v2 ``memory.high`` (the throttling point) over ``memory.max``, then v1.
Own cgroup first (where a systemd unit's ``MemoryHigh=``/``MemoryMax=`` lands â€”
root reads ``max`` there), then root for container-style limits.
ÚlinuxN©Ú_own_cgroup_pathÚ/ú/sys/fs/cgroupz/memory.z+/sys/fs/cgroup/memory/memory.limit_in_bytes©ÚhighÚmax)ÚsysÚplatformÚgateway.cgroup_cleanuprB   Ú	Exceptionr<   r   )rB   ÚownÚrootsÚrÚfÚ	candidater;   s          r    Ú_cgroup_limit_bytesrQ   C   sÆ   € ô ‡|�|�wÔÙðÝ;áÓ ˆ÷ *-°¸´�˜s˜eÐ$Ñ%ÀÐHXÐGYÕY€EÙ16ÔP±¨AÃ¸1˜˜8 A 3Ó'ÁÑ'±ÒPð  UBð  TC÷  Cð  Cˆ	Üœd 9›oÓ.ˆØÔØŠLñ Cñ øô ô ØŠðüó Qs   ™B Á
B&ÂB#Â"B#c               ó   € V ^8„  d   QhRR/# r>   r   )r&   s   "r    r'   r'   Z   s   € ÷ 
ñ 
˜]ñ 
r   c                 ó6  €  \        \        P                  ! R 4      4      \        \        P                  ! R4      4      ,          #   \        \        \
        3 d     Mi ; i ^ RIp \        T P                  4       P                  4      #   \         d     R# i ; i)ÚSC_PAGE_SIZEÚSC_PHYS_PAGESN)
r   ÚosÚsysconfr:   r/   ÚAttributeErrorÚpsutilÚvirtual_memoryÚtotalrK   )rY   s    r    Ú_total_memory_bytesr\   Z   s{   € ðÜ”2—:’:˜nÓ-Ó.´´R·Z²ZÀÓ5PÓ1QÕQÐQøÜ”Z¤Ð0ô ÙðúðÛä�6×(Ñ(Ó*×0Ñ0Ó1Ð1øÜô Úðús$   ‚AA ÁAÁAÁ"&B	 Â	BÂBc               ó    € V ^8„  d   QhRRRR/# )r"   Úsettingr   r$   r   r   )r&   s   "r    r'   r'   g   s   € ÷ ?ñ ? Cð ?¨Mñ ?r   c                óˆ  € \        V \        4      '       d?   V P                  4       P                  4       pVR8w  d   V\        9   d   R# \        V4      # MV RJ d   R# V RJd   \        V 4      # \        4       ;'       g    \        4       pV'       g   R# \        V\        ,          \        ,          4      pV\        8¼  d   V# R# )zžAbsolute MB budget: ``"auto"`` derives from the cgroup limit (or total RAM when
uncapped); a positive number is literal; anything falsy/off disables the pass.ÚautoNFT)r)   Ústrr9   ÚlowerÚ
_OFF_WORDSr1   rQ   r\   r   Ú_AUTO_BUDGET_FRACTIONÚ_BYTES_PER_MBÚ_AUTO_BUDGET_FLOOR_MB)r^   Ú
normalizedr;   Úbudgets   &   r    Úresolve_memory_high_mbri   g   s©   € ô �'œ3×ÒØ—]‘]“_×*Ñ*Ó,ˆ
Ø˜ÔØ%¬Ô3�4ÐN¼À:Ó9NÐNð  à	�EÓ	ÙØ	˜Ó	Ü˜Ó!Ð!ÜÓ!×:Ð:Ô%8Ó%:€EßÙÜ�Ô.Õ.´Õ>Ó?€FØÔ4Ô4ˆ6Ð>¸$Ð>r   c               ó    € V ^8„  d   QhRRRR/# )r"   Úconfigr   r$   r   r   )r&   s   "r    r'   r'   y   s   € ÷ ñ  sð Ð/?ñ r   c           
     óP  € \        V \        4      '       d+   V P                  R4      ;'       g    / P                  R4      MRp\        V\        4      '       g   / pVP                  R4      p\        V4      pVf   \	        V4      '       d
   V^ 8X  d   ^ p\        \        VP                  R4      4      \        VP                  R4      \        4      \        VP                  RR4      4      \        VP                  R	4      4      ;'       g    \        Vf   \        R
7      # TR
7      # )z£Read ``agent.agent_cache`` from the *raw* config: the gateway loader does not
deep-merge ``DEFAULT_CONFIG``, so callers can tell "operator chose 128" from "unset".ÚagentÚagent_cacheNr   r   r   r   r`   r   )r   r   r   r   r   )
r)   ÚdictÚgetr1   r*   r   Úfloatri   r   r   )rk   Úsectionr   Úprotect_parseds   &   r    Úresolve_agent_cache_boundsrt   y   sû   € ô AKÈ6ÔSW×@XÒ@Xˆv�z‰z˜'Ó"×(Ð( b×-Ñ-¨mÔ<Ð^b€GÜ�gœt×$Ò$ØˆØ—[‘[Ð!1Ó2€NÜ˜~Ó.€Nð Ò¤'¨.×"9Ò"9¸nÐPQÔ>QØˆÜÜ˜7Ÿ;™; zÓ2Ó3Ü §¡¨OÓ <¼eÓDÜ-¨g¯k©kÐ:JÈFÓ.SÓTÜ(¨¯©Ð5MÓ)NÓO×rÐrÔSrØ2@Ò2HÔ.ôð ð
 O]ôð r   c               ó   € V ^8„  d   QhRR/# r>   r   )r&   s   "r    r'   r'   Ž   s   € ÷ ñ ˜Mñ r   c                 ó^  a€ \         P                  R8w  d   R#  ^ RIHp  V ! 4       pV'       d   VR8X  d   R# \	        RV 24      o\
        ;QJ d    V3R lR 4       F  '       g   K   RM	  RM! V3R lR 4       4      pV'       d"   SP                  R	4      P                  R
R7      MRpTP                  4        FQ  pTP                  R4      w  rVpTR8X  g   K   TP                  4       P                  4       '       g   KF  \        T4      u # 	  R#   \        \        \        3 d     R# i ; i)uk  Anonymous memory charged to this process's own cgroup v2 (``memory.stat`` ``anon``), or None.

The budget is derived from the same cgroup's ``memory.high``/``memory.max``, and the kernel
charges every process in the unit against it â€” execute_code kernels, terminal children â€” so a
self-only reading under-counts by exactly the children's share (#110549).  Anon, not
``memory.current``: the module's signal is heap, and reclaimable page cache is noise.
Only for a *capped* cgroup: an uncapped one (a plain login session) is the whole user
slice, and its budget is total RAM â€” self RSS stays the right scope there.
r@   NrA   rC   rD   c              3  óN   <"  € T F  p\        SR V 2,          4      x € K  	  R# 5i)zmemory.N)r<   )Ú.0rO   Úroots   & €r    Ú	<genexpr>Ú%_cgroup_anon_bytes.<locals>.<genexpr>¡   s$   øé € ÐRÁ/¸Q”] 4¨G°A°3¨-Õ#7×8Ð8Ã/ùs   ƒ"%TFzmemory.statr5   r6   Ú Ú ÚanonrE   )rH   rI   rJ   rB   r   ÚanyÚjoinpathr8   r:   r/   ÚImportErrorÚ
splitlinesÚ	partitionr9   Úisdigitr   )	rB   rL   ÚcappedÚtextÚlineÚkeyÚ_r#   ry   s	           @r    Ú_cgroup_anon_bytesrŠ   Ž   sì   ø€ ô ‡|�|�wÔÙð
Ý;áÓ ˆß�c˜S”jÙÜ�n S EÐ*Ó+ˆß“ÔRÁ/ÓR——’ÔRÁ/ÓRÓRˆßOUˆt�}‰}˜]Ó+×5Ñ5¸{Ð5ÔKÐ[]ˆð —‘Ö!ˆØŸ™ sÓ+‰ˆ�Ø�&Ž=˜UŸ[™[›]×2Ñ2×4Ô4Ü�u“:Òñ "ñ øô ”Z¤Ð-ô Úðús'   šD ¯D ¸-D Á(D Â#D ÄD,Ä+D,c               ó   € V ^8„  d   QhRR/# r>   r   )r&   s   "r    r'   r'   ¬   s   € ÷ ñ ˜-ñ r   c                 óÔ  € \        4       p V '       d   V \        ,          #  ^ RIHp V! 4       pR F>  pVP	                  V4      p\        V\        4      '       g   K,  V^ 8”  g   K5  VR,          u # 	   ^ RIp\        VP                  \        P                  ! 4       4      P                  4       P                  \        ,          4      #   \         d     Lbi ; i  \         d     R# i ; i)u#  Anonymous memory in MB (where cached transcripts live; file-backed pages are noise),
or None.  Own cgroup's ``memory.stat`` anon first â€” the scope the budget is charged
against, so same-unit child processes count; then ``/proc/self/status``; psutil covers
other platforms (total RSS only).)Úcollect_memory_snapshoti   N)Úrss_anon_kibÚrss_kib)rŠ   re   Úhermes_cli.mem_trimr�   rp   r)   r   rK   rY   ÚProcessrV   ÚgetpidÚmemory_infoÚrss)Úchargedr�   Úsnapshotrˆ   ÚkibrY   s         r    Úread_anon_rss_mbr˜   ¬   s¼   € ô
 !Ó"€GßØœ-Õ'Ð'ð	Ý?á*Ó,ˆÛ.ˆCØ—,‘,˜sÓ#ˆCÜ˜#œs×#Ô#¨¨a®Ø˜d•{Ò"ñ /ðÛä�6—>‘>¤"§)¢)£+Ó.×:Ñ:Ó<×@Ñ@Ä=ÕPÓQÐQøô ô Ùðûô ô Úðús6   ¡6C ÁC Á%
C Á0C Á3AC ÃCÃCÃC'Ã&C'c               ó    € V ^8„  d   QhRRRR/# )r"   rm   r   r$   r%   r   )r&   s   "r    r'   r'   Æ   s   € ÷ 	Xñ 	X¨Cð 	X°Dñ 	Xr   c                ó¦   € \        V RR4      \        V RR4      r!\        V\        4      ;'       d"    \        V4      ;'       d    V\	        V4      8¬  # )a:  True when the live transcript is fully on disk.

Soft eviction rebuilds from the persisted session, so it is only safe once
``_last_flushed_db_idx`` (advanced only on a fully successful write) has caught
up.  Unknown shapes are *not* caught up: a skipped eviction costs memory, a
wrong one costs the conversation.
Ú_session_messagesNÚ_last_flushed_db_idx)Úgetattrr)   Úlistr*   Úlen)rm   ÚmessagesÚflusheds   &  r    Ú transcript_persistence_caught_upr¢   Æ   sK   € ô   Ð':¸DÓAÄ7È5ÐRhÐjnÓCoˆgÜ�h¤Ó%×WÐW¬'°'Ó*:×WÐW¸wÌ#ÈhË-Ñ?WÐWr   r   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r"   Úordered_entrieszIterable[tuple[str, Any]]Úis_evictablezCallable[[str, Any], bool]Úmax_evictionsr   r   r$   zlist[tuple[str, Any]]r   )r&   s   "r    r'   r'   Ò   s6   € ÷ ñ Ø.ðØA[ðàðà(+ðð ñr   c               ó*  € \        V 4      pV^ 8:  g	   V'       g   . # \        \        V^ 4      \        V4      ^,          4      pV'       d   VRV)  p. pV F:  w  rx\        V4      V8¼  d    V# V! Wx4      '       g   K(  VP	                  Wx34       K<  	  V# )uo  Choose which cached sessions to shed, least-recently-used first.

``ordered_entries`` must be LRUâ†’MRU (the cache OrderedDict ``move_to_end``s on
every hit).  The batch is capped so one pass cannot stall the gateway.
``protect_recent`` is clamped to half the cache: a few huge transcripts can
exhaust the budget alone, and a fixed guard would leave nothing to shed.
N)rž   ÚminrG   rŸ   Úappend)	r¤   r¥   r¦   r   ÚentriesÚprotectÚplanrˆ   rm   s	   &$$$     r    Úplan_pressure_evictionsr­   Ò   s“   € ô �?Ó#€GØ˜Ô§Øˆ	Ü”#�n aÓ(¬#¨g«,¸!Õ*;Ó<€GßØ˜)˜G˜8Ð$ˆØ"$€DÛ‰
ˆÜˆt‹9˜Ô%Øð €Kñ ˜×#Ô#Ø�K‰K˜˜Ö%ñ	 ð
 €Kr   i   >   r|   ÚoffÚnoneÚfalseÚdisabled)$r   Ú
__future__r   rV   rH   Údataclassesr   Úpathlibr   Útypingr   r   r   r   r	   r
   rd   rf   r   r   re   Ú	frozensetrc   r   r*   r   r1   r<   rQ   r\   ri   rt   rŠ   r˜   r¢   r­   r   r   r    Ú<module>r·      s·   ðñõ #ã 	Û 
Ý !Ý ß A× Að Ð àÐ Ø"$Ð ð Ð Ø€ÙÒ?Ó@€
ñ �$Ô÷2ð 2ó ð2õBð 8;÷ Aõ4õõ.
õ?õ$õ*õ<õ4	Xðà./÷ñ r   