+
    „WÝj0  ã                   óÄ  a € R t(0 t R t^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIH	t	 ^ RI
HtHtHt ]'       d   ^ RIHt ]P                   ! R4      t^t^t^t^@tRt]P.                  ! ]4      t^ s]P4                  ! 4       tRs] ^ k R R ltR	 R
 ltR R ltR R lt R R lt! ! R R4      t"]PF                  ! 4       t$] ^k ]P4                  ! 4       t%R R lt&R R lt'R# )a"  Read-connection budgeting for SessionDB's WAL read path: per-file and
process-wide permit ceilings plus a descriptor-headroom gate, so N handles on
one state.db cannot walk the process into EMFILE. Idle pooled connections keep
their permit and are reclaimable across peers on the same path.N)ÚPath)ÚTYPE_CHECKINGÚIteratorÚOptional)Ú	SessionDBÚhermes_stateg      Ð?c                óh   € V ^8„  d   QhR\         R\        \        \        \        3,          ,          /# )é   ÚpidÚreturn)Úintr   ÚtupleÚstr)Úformats   "ÚIC:\Users\johnn\AppData\Local\hermes\hermes-agent\hermes_state_readpool.pyÚ__annotate__r   H   s'   € ÷ 	ñ 	œ#ð 	¤(¬5´´c°­?Õ";ñ 	ó    c              #  ó¼   "  € RV  R2p\         P                  ! V4       F%  pV RV 2p \         P                  ! V4      V3x € K'  	  R#   \         d     K9  i ; i5i)z Yield ``(readlink target, fd path)`` for every entry in /proc/<pid>/fd (unreadable
links skipped). Raises OSError when the fd directory itself cannot be listed.z/proc/z/fdÚ/N)ÚosÚlistdirÚreadlinkÚOSError)r
   Úfd_dirÚfdÚfd_paths   &   r   Ú_proc_fd_targetsr   H   sd   é € ð �c�U˜#Ð€FÜ�jŠj˜Ö ˆØ�H˜A˜b˜TÐ"ˆð	Ü—+’+˜gÓ&¨Ð/Ô/ó !øô ô 	Úð	üs(   ‚'AªA
ÁAÁ
AÁAÁAÁAc                ó:   € V ^8„  d   QhR\         \        ,          /# ©r	   r   ©r   r   )r   s   "r   r   r   T   s   € ÷ 	ñ 	œ¤�ñ 	r   c                 óú   € R F#  p  \        \        P                  ! V 4      4      u # 	  R#   \         dD   pTP                  \        P
                  \        P                  39   d
   Ru Rp?u #  Rp?Kr  Rp?ii ; i)z™Open descriptors in THIS process; None when unmeasurable (Windows: no fd
dir, correctly inert); -1 when the probe itself hit EMFILE/ENFILE (no headroom).N)z/proc/self/fdz/dev/fdéÿÿÿÿ)Úlenr   r   r   ÚerrnoÚEMFILEÚENFILE)r   Úexcs     r   Ú_open_fd_countr'   T   sa   € ó /ˆð	Ü”r—z’z &Ó)Ó*Ò*ñ /ñ øô ô 	Ø�y‰yœUŸ\™\¬5¯<©<Ð8Ô8Ø—	õ 9ûð	ús   ‡,¬A:·0A5Á'A:Á5A:c                ó:   € V ^8„  d   QhR\         \        ,          /# r   r   )r   s   "r   r   r   `   s   € ÷ ñ œ¤�ñ r   c                 óä   €  ^ RI p  T P                  T P                  4      w  rYP                  R39   d   R# \        T4      #   \         d     R# i ; i  \        \
        \        3 d     R# i ; i)zFThe process's soft RLIMIT_NOFILE, or None when there is no usable one.Nr!   )	ÚresourceÚImportErrorÚ	getrlimitÚRLIMIT_NOFILEÚAttributeErrorr   Ú
ValueErrorÚRLIM_INFINITYr   )r*   ÚsoftÚ_hards      r   Ú_fd_soft_limitr3   `   su   € ðÛðØ×(Ñ(¨×)?Ñ)?Ó@‰ˆð ×&Ñ&¨Ð+Ô+ÙÜˆt‹9Ðøô ô Úðûô œG¤ZÐ0ô Úðús"   ‚A ‡A ÁAÁAÁA/Á.A/c                ó$   € V ^8„  d   QhR\         /# r   ©Úbool)r   s   "r   r   r   o   s   € ÷ Bñ Bœñ Br   c                 ó¨  € \        4       p V f   R# \        P                  ! 4       p\        ;_uu_ 4        \        w  r#VRJ;'       d    W,
          \
        8  pRRR4       X'       g%   \        4       p\        ;_uu_ 4        W3sRRR4       Xf   R# V^ 8¬  ;'       d    W,
          \        8„  #   + '       g   i     L^; i  + '       g   i     LE; i)zÙCan the process spare a descriptor for a new read connection? Fails OPEN
when unmeasurable (refusing every read would be a self-inflicted convoy);
fails CLOSED only on evidence (measured shortfall or a starved probe).NT)r3   ÚtimeÚ	monotonicÚ_fd_usage_lockÚ_fd_usage_cacheÚ_FD_USAGE_CACHE_SECONDSr'   Ú_FD_HEADROOM_RESERVE)r1   ÚnowÚstampÚcachedÚfreshs        r   Ú_fd_headroom_okrB   o   s›   € ô Ó€DØ‚|Ùä
�.Š.Ó
€Cß	ŽÜ'‰ˆØ˜dÐ"×NÐN¨­Ô7NÑ'Nˆ÷ 
÷ ÜÓ!ˆßŽ^Ø"˜mˆO÷ à‚~ÙØ�Q‰;×AÐA˜D�MÔ-AÑAÐA÷ 
�ú÷
 �^ús   ´$B.Â CÂ.B>	ÃC	c                ó$   € V ^8„  d   QhR\         /# r   r5   )r   s   "r   r   r   „   s   € ÷ <ñ <¬$ñ <r   c                 óø   € \         ;_uu_ 4        \        \        P                  4       4      p RRR4       \        ;QJ d    R X  4       F  '       g   K   R# 	  R# ! R X  4       4      #   + '       g   i     LD; i)z–Close one idle read connection on ANY path: the process ceiling is shared
across files, so a quiet profile must not hold descriptors a busy one needs.Nc              3   ó@   "  € T F  qP                  4       x € K  	  R # 5i©N)Úreclaim_idle)Ú.0Úbudgets   & r   Ú	<genexpr>Ú3_reclaim_idle_read_conn_anywhere.<locals>.<genexpr>‰   s   é € Ð;±7¨×"Ñ"×$Ð$³7ùó   ‚TF)Ú_read_budgets_lockÚlistÚ_read_budgetsÚvaluesÚany)Úbudgetss    r   Ú _reclaim_idle_read_conn_anywhererS   „   sT   € ÷ 
Ö	Ü”}×+Ñ+Ó-Ó.ˆ÷ 
ç‹3Ñ;±7Ó;�3Œ3Ð;Š3Ð;ˆ3Ñ;±7Ó;Ó;Ð;÷ 
×	ús   �A)Á)A9	c                   ó¸   a € ] tR t^Œt o RtV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR	 ltV 3R
 lR lt	V 3R lR lt
V 3R lR ltRV 3R lR lltRtV tR# )Ú_PathReadBudgetuü  Read-connection permits for ONE database file, shared process-wide
(per-instance semaphores let N SessionDBs peak at N x (1 + MAX)). An idle
pooled connection keeps its permit, so a permit miss first reclaims an IDLE
connection from a peer on the same path.

``_READ_POOL_MAX`` used to be enforced by a ``BoundedSemaphore`` owned by each SessionDB, which bounded
the wrong noun: the descriptors are spent on a *file*, so N SessionDB objects on one state.db each got
their own allowance and peak scaled as ``N x (1 + _READ_POOL_MAX)``. A long-lived gateway holds at least
two (``SessionStore`` and ``GatewayRunner`` open independent handles per profile path) and the count
grows with the profile count, which is how a healthy process walked into EMFILE â€” #98573.
c                ó   <€ V ^8„  d   QhRR/# ©r	   r   N© )r   Ú__classdict__s   "€r   r   Ú_PathReadBudget.__annotate__™   s   ø€ ÷ /ñ /˜$ñ /r   c                óº   € \         P                  ! \        4      V n        \         P                  ! 4       V n        \        P                  ! 4       V n        R V n	        R# )FN)
Ú	threadingÚBoundedSemaphoreÚ_READ_POOL_MAXÚpermitsÚLockÚ_lockÚweakrefÚWeakSetÚ_membersÚ_duplicate_handles_warned©Úselfs   &r   Ú__init__Ú_PathReadBudget.__init__™   s8   € Ü ×1Ò1´.ÓAˆŒÜ—^’^Ó%ˆŒ
ä4;·O²OÓ4EˆŒØ).ˆÖ&r   c                ó"   <€ V ^8„  d   QhRRRR/# ©r	   Údbr   r   NrX   )r   rY   s   "€r   r   rZ       s   ø€ ÷ ñ ˜;ð ¨4ñ r   c                óî  € V P                   ;_uu_ 4        V P                  P                  V4       \        R  V P                   4       4      pV\        8„  ;'       d    V P
                  '       * pV'       d3   RV n        RP                  \        R V P                   4       4      4      pRRR4       X'       d*   \        P                  RXVP                  \        X4       R# R#   + '       g   i     LC; i)c              3   ón   "  € T F+  pVP                   '       d   K  VP                  f   K'  ^x € K-  	  R # 5irF   )Ú	read_onlyÚ_conn©rH   Úmembers   & r   rJ   Ú+_PathReadBudget.register.<locals>.<genexpr>¦   s-   é € ð Ù -�fØ×'Õ'ô Ø,2¯L©L÷ ’£ùs   ‚5›5«
5Tz, c              3   ó`   "  € T F$  qP                   '       d   K  VP                  x € K&  	  R # 5irF   )ro   Ú_creation_siterq   s   & r   rJ   rs   ¯   s"   é € ð 2Ù8E¨f×M]ÕM]Ô)�F×)Ö)»ùs   ‚.š.NzÍ%d live SessionDB handles on %s in this process; each holds its own writer connection (read connections are capped at %d for the file). A long-lived process should share one handle per path. Created at: %s)ra   rd   ÚaddÚsumÚ_HANDLES_PER_PATH_WARNre   ÚjoinÚsortedÚloggerÚwarningÚdb_pathr^   )rg   rl   ÚhandlesÚwarnÚcreation_sitess   &&   r   ÚregisterÚ_PathReadBudget.register    sÄ   € Ø�Z�Z‹ZØ�M‰M×Ñ˜bÔ!ô ñ Ø $§¢óó ˆGð Ô4Ñ4×[Ð[¸T×=[Ñ=[Ô9[ˆDßØ15�Ô.ð "&§¡¬6ñ 2Ø8<¿ºó2ó ,ó "�÷ ÷" ô �N‰Nð
!ð ˜Ÿ™¤^°^ö
ñ ÷# �Zús   •A!C$Á72C$Ã$C4	c                ó"   <€ V ^8„  d   QhRRRR/# rk   rX   )r   rY   s   "€r   r   rZ   Á   s   ø€ ÷ &ñ &˜[ð &¨Tñ &r   c                óž   € V P                   ;_uu_ 4        V P                  P                  V4       RRR4       R#   + '       g   i     R# ; i)zERemove a closed writer from duplicate-handle diagnostics immediately.N)ra   rd   Údiscard)rg   rl   s   &&r   Ú
unregisterÚ_PathReadBudget.unregisterÁ   s)   € à�Z�Z‹ZØ�M‰M×!Ñ! "Ô%÷ �Z�ZŠZús	   •;»A	c                ó$   <€ V ^8„  d   QhRRRS[ /# ©r	   Ú	requesterr   r   r5   )r   rY   s   "€r   r   rZ   Æ   s   ø€ ÷ ñ  ð ±ñ r   c                ó$  € \        4       '       g'   \        ;_uu_ 4        \        ^,          sRRR4       R# V P                  4       '       g   R# V P	                  V4      '       d   R# \
        P                  4        R#   + '       g   i     R# ; i)z¯Take a permit for a new read connection, or refuse (caller degrades to the
locked writer connection). Gates, broadest first: fd headroom, process
ceiling, this file's ceiling.NFT)rB   rM   Ú_read_open_denied_fd_headroomÚ_acquire_process_permitÚ_acquire_path_permitÚ_process_read_permitsÚrelease©rg   rŠ   s   &&r   ÚacquireÚ_PathReadBudget.acquireÆ   sg   € ô × Ò ç#Ö#Ü-°Õ2Ð-÷ $áØ×+Ñ+×-Ò-ÙØ×$Ñ$ Y×/Ò/ÙÜ×%Ñ%Ô'Ù÷ $Ö#áús   ŸA>Á>B	c                ó   <€ V ^8„  d   QhRR/# rW   rX   )r   rY   s   "€r   r   rZ   Ö   s   ø€ ÷ (ñ (˜ñ (r   c                ób   € V P                   P                  4        \        P                  4        R# )zCReturn one connection's permits. Pairs with a successful acquire().N)r_   r�   r�   rf   s   &r   r�   Ú_PathReadBudget.releaseÖ   s   € à�‰×ÑÔÜ×%Ñ%Ö'r   c                ó    <€ V ^8„  d   QhRS[ /# r   r5   )r   rY   s   "€r   r   rZ   Û   s   ø€ ÷ 
ñ 
©ñ 
r   c                óŽ   € \         P                  R R7      ;'       g)    \        4       ;'       d    \         P                  R R7      # )F©Úblocking)r�   r’   rS   rf   s   &r   r�   Ú'_PathReadBudget._acquire_process_permitÛ   s@   € ä$×,Ñ,°eÐ,Ó<÷ 
ð 
Ü,Ó.×`Ð`Ô3H×3PÑ3PÐZ_Ð3PÓ3`ð	
r   c                ó$   <€ V ^8„  d   QhRRRS[ /# r‰   r5   )r   rY   s   "€r   r   rZ   á   s   ø€ ÷ 
ñ 
¨kð 
¹dñ 
r   c                ó¶   € V P                   P                  R R7      ;'       g7    V P                  VR7      ;'       d    V P                   P                  R R7      # )Fr™   )Úexclude)r_   r’   rG   r‘   s   &&r   rŽ   Ú$_PathReadBudget._acquire_path_permitá   sR   € Ø�|‰|×#Ñ#¨UÐ#Ó3÷ 
ð 
Ø×Ñ iÐÓ0×YÐY°T·\±\×5IÑ5IÐSXÐ5IÓ5Yð	
r   Nc                ó$   <€ V ^8„  d   QhRRRS[ /# )r	   rž   zOptional[SessionDB]r   r5   )r   rY   s   "€r   r   rZ   æ   s    ø€ ÷ Mñ MÐ$9ð MÁTñ Mr   c                ó  € V P                   ;_uu_ 4        V P                   Uu. uF  q"VJg   K
  VNK  	  ppRRR4       \        ;QJ d    R X 4       F  '       g   K   R# 	  R# ! R X 4       4      # u upi   + '       g   i     LI; i)z�Close one idle pooled connection held by a member; True if one went.
Its release() returns both permits, so both ceilings reclaim through here.Nc              3   ó@   "  € T F  qP                  4       x € K  	  R # 5irF   )Ú_evict_one_idle_read_connrq   s   & r   rJ   Ú/_PathReadBudget.reclaim_idle.<locals>.<genexpr>ë   s   é € ÐLÁG¸&×3Ñ3×5Ð5ÃGùrL   TF)ra   rd   rQ   )rg   rž   rl   Úmemberss   &&  r   rG   Ú_PathReadBudget.reclaim_idleæ   sh   € ð �Z�Z‹ZØ$(§M¢MÓG¡M˜b¸wÐ5F—r�r¡MˆGÐG÷ ç‹sÑLÁGÓL�sŒsÐLŠsÐLˆsÑLÁGÓLÓLÐLùò H÷ �Zús    •A9¤A4°A4¶A9Á4A9Á9B		)re   ra   rd   r_   rF   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__rh   r�   r†   r’   r�   r�   rŽ   rG   Ú__static_attributes__Ú__classdictcell__)rY   s   @r   rU   rU   Œ   sc   ø‡ € ñ
÷/ð /÷ð ÷B&ð &÷
ð ÷ (ð (÷

ð 
÷
ð 
÷
M÷ Mð Mr   rU   c                ó$   € V ^8„  d   QhR\         /# r   )r   )r   s   "r   r   r   ô   s   € ÷ ñ ¤ñ r   c                ó€   €  \        \        V 4      P                  4       4      #   \         d    \        T 4      u # i ; i)z9Canonicalise a db path so two spellings share one budget.)r   r   Úresolver   )r}   s   &r   Ú_read_budget_keyr±   ô   s8   € ðÜ”4˜“=×(Ñ(Ó*Ó+Ð+øÜô Ü�7‹|Òðús   ‚!$ ¤=¼=c                ó$   € V ^8„  d   QhR\         /# r   )rU   )r   s   "r   r   r   ü   s   € ÷ ñ ¤ñ r   c                 óÎ   € \        V 4      p\        ;_uu_ 4        \        P                  V4      pVf   \	        4       pV\        V&   VuuR R R 4       #   + '       g   i     R # ; irF   )r±   rM   rO   ÚgetrU   )r}   ÚkeyrI   s   &  r   Ú_read_budget_forr¶   ü   sK   € Ü
˜7Ó
#€Cß	Ö	Ü×"Ñ" 3Ó'ˆØŠ>Ü$Ó&ˆFØ!'ŒM˜#ÑØ÷ 
×	×	Ó	ús   š.AÁA$	c                óÖ   € V ^8„  d   Qh/ ^ \         9   d&   \        \        \        \        ,          3,          ;R&   ^\         9   d%   \
        P                  \        \        3,          ;R&   # )r	   r;   rO   )	Ú__conditional_annotations__r   Úfloatr   r   rb   ÚWeakValueDictionaryr   rU   )r   s   "r   r   r      sW   € ÷ Cñ C÷H ;Ò :””uœh¤s�mÐ+Õ,Ñ :ñIC÷^ cÒ bŒw×*Ñ*¬3´Ð+?Õ@Ñ bò_Cr   )g        N))r¸   r«   r#   Úloggingr   r\   r8   rb   Úpathlibr   Útypingr   r   r   r   r   Ú	getLoggerr{   r^   Ú_READ_POOL_PROCESS_MAXrx   r=   r<   r]   r�   rŒ   r`   r:   r;   r   r'   r3   rB   rS   rU   rº   rO   rM   r±   r¶   r   )r¸   s   @r   Ú<module>rÀ      së   øðöCó
 Û Û 	Û Û Û Ý ß 4Ñ 4çÝ&ð 
×	Ò	˜>Ó	*€ð  €ð Ð ð Ð ð Ð ð Ð à!×2Ò2Ð3IÓJÐ ð !"Ð à—’Ó!€Ø/:€Ó :õ	õ	õõBõ*<÷_Mñ _MðH EL×D_ÒD_ÓDa€Ó bØ—^’^Ó%Ð õ÷r   