
    `gj)                    v   d Z ddl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m	Z	 ddl
mZ ddlmZmZ ddlmZ  ej"                  e      ZdddZdd	Zd d
Zd!dZd!dZd!dZ G d d      Zd"dZd#dZd$dZd%dZd&d'dZd(dZ e	 G d d             Z!dd	 	 	 	 	 	 	 	 	 d)dZ"d*dZ#dd	 	 	 	 	 	 	 d+dZ$d,dZ%y)-zCross-process active chat session leases.

The session database records persisted conversations.  This module records
currently open chat surfaces, including idle CLI/TUI sessions that have not
written a transcript row yet.
    )annotationsN)	dataclass)Path)AnyOptional)get_hermes_homec                   | yt        | t              rt        j                  d||        y	 t        | t              r'| j                         st        |       t        |       }n6t        | t              rt        | j                         d      }nt        |       }|dk  ry|S # t        t        f$ r t        j                  d||        Y yw xY w)z=Return a positive integer cap, or None when disabled/invalid.NzEIgnoring invalid %s=%r (expected a positive integer; 0/null disables)
   r   )
isinstanceboolloggerwarningfloat
is_integer
ValueErrorintstrstrip	TypeError)valuekeyparseds      M/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/active_sessions.pycoerce_max_concurrent_sessionsr      s    }%S	

 eU###% ''ZFs#+FZF {M z" S	

 s   A-B" "&C
Cc                    d}d}t        | t              rKd| v r| j                  d      }nB| j                  d      }t        |t              r!|j                  d      }d}nt        | dd      }t	        ||      S )zBResolve top-level max_concurrent_sessions with gateway.* fallback.Nmax_concurrent_sessionsgatewayzgateway.max_concurrent_sessions)r   )r   dictgetgetattrr   )configrawr   gateway_cfgs       r   resolve_max_concurrent_sessionsr$   8   su    C
#C&$$.**67C **Y/K+t,!oo&?@7f7>)#377    c                    d|  d| dS )Nz'Hermes is at the active session limit (/z+). Try again when another session finishes. )active_countmax_sessionss     r   active_session_limit_messager+   I   s    
1,q O3 	3r%   c                 .    t        t                     dz  S )Nruntime)r   r   r(   r%   r   
_state_dirr.   P   s    !"Y..r%   c                     t               dz  S )Nzactive_sessions.jsonr.   r(   r%   r   _state_pathr1   T       <000r%   c                     t               dz  S )Nzactive_sessions.lockr0   r(   r%   r   
_lock_pathr4   X   r2   r%   c                       e Zd ZddZd Zd Zy)	_FileLockc                     || _         d | _        y N)path_fh)selfr9   s     r   __init__z_FileLock.__init__]   s    	r%   c                   | j                   j                  j                  dd       t        | j                   d      | _        t
        j                  dk(  rW	 dd l}| j                  j                  d       |j                  | j                  j                         |j                  d       | S 	 dd l}|j!                  | j                  j                         |j"                         | S # t        $ r2}| j                  j                          d | _        t        d      |d }~ww xY w# t        $ r2}| j                  j                          d | _        t        d      |d }~ww xY w)NTparentsexist_okza+bntr      z$active session file lock unavailable)r9   parentmkdiropenr:   osnamemsvcrtseeklockingfilenoLK_LOCK	ExceptioncloseRuntimeErrorfcntlflockLOCK_EX)r;   rH   excrP   s       r   	__enter__z_FileLock.__enter__a   s   		td;		5)77d?Ta txx0&..!D TDHHOO-u}}=
   T "#IJPSST  T "#IJPSSTs1   AC( .8D& (	D#1-DD#&	E!/-EE!c                   | j                   y t        j                  dk(  rV	 dd l}| j                   j	                  d       |j                  | j                   j                         |j                  d       n9	 dd l	}|j                  | j                   j                         |j                         	 | j                   j                          d | _         y # t        $ r Y .w xY w# t        $ r Y =w xY w# d | _         w xY w)NrA   r   rB   )r:   rF   rG   rH   rI   rJ   rK   LK_UNLCKrM   rP   rQ   LOCK_UNrN   )r;   exc_typerS   tbrH   rP   s         r   __exit__z_FileLock.__exit__y   s    8877d?a txx0&//1EDHHOO-u}}=	HHNNDH    
 DHs0   AC 88C" 1C1 	CC"	C.-C.1	C:N)r9   r   )__name__
__module____qualname__r<   rT   rZ   r(   r%   r   r6   r6   \   s    0r%   r6   c                   	 t        | dd      5 }t        j                  |      }d d d        t        t              r|j                  d      n|}t        |t              sg S |D cg c]  }t        |t              s| c}S # 1 sw Y   _xY w# t        $ r g cY S t        $ r t
        j                  d|        g cY S w xY wc c}w )Nrutf-8encodingz.Ignoring corrupt active session registry at %sentries)rE   jsonloadFileNotFoundErrorrM   r   r   r   r   r   list)r9   fhdatarc   entrys        r   _read_entriesrk      s    $g. 	!"99R=D	! &0d%;dhhy!Ggt$	&Be*UD*AEBB	! 	! 	 GN	 Cs9   B BB 'C=CB	B C  C ?C c                   | j                   j                  dd       | j                  | j                   dt	        j
                          dt        j                         j                   d      }t        |dd      5 }t        j                  d|i|d	       d d d        t	        j                  ||        y # 1 sw Y    xY w)
NTr>   .z.tmpwr`   ra   rc   )	sort_keys)rC   rD   	with_namerG   rF   getpiduuiduuid4hexrE   rd   dumpreplace)r9   rc   tmprh   s       r   _write_entriesrx      s    KKdT2
..DII;a		}Adjjl6F6F5GtL
MC	c3	) <R		9g&d;<JJsD< <s   >B88Cc                z    	 dd l }t        |j                  |       j                               S # t        $ r Y y w xY w)Nr   )psutilr   Processcreate_timerM   )pidrz   s     r   _process_start_timer~      s9    V^^C(44677 s   +. 	::c                T    | | dk(  ry 	 t        |       S # t        t        f$ r Y y w xY w)N )r   r   r   )r   s    r   _optional_floatr      s6    }U|z" s   
 ''c                    	 t        |       }|dk  ry	 ddlm} t         ||            }|syt        |      }|yt        |      }|yt        ||z
        dk  S # t        t        f$ r Y yw xY w# t        $ r Y yw xY w)NFr   )_pid_existsTgMbP?)
r   r   r   gateway.statusr   r   rM   r   r~   abs)r}   process_start_timepid_intr   existsexpected_startcurrent_starts          r   
_pid_aliver      s    c( !|.k'*+ $%78N'0M}~-.66% z"   s"   A A0 A-,A-0	A<;A<c           	         | D cg c].  }t        |j                  d      |j                  d            r|0 c}S c c}w )Nr}   r   )r   r   )rc   rj   s     r   _prune_deadr      sA     eii&		2F(GH 	  s   3;c                  P    e Zd ZU ded<   ded<   ded<   dZded<   dZded	<   dd
Zy)ActiveSessionLeaser   lease_id
session_idsurfaceTr   enabledFreleasedc                L    | j                   s| j                  sy t        |        y r8   )r   r   release_active_session)r;   s    r   releasezActiveSessionLease.release   s    ==t$r%   N)returnNone)r[   r\   r]   __annotations__r   r   r   r(   r%   r   r   r      s*    MOLGTHd%r%   r   )metadatac                   t        |      }t        j                         j                  }|t	        || |d      dfS t        j
                         }|t        |       t        |      t        j                         t        t        j                               ||d}|rA|j                         D 	ci c]"  \  }}	t        |t              st        |      |	$ c}	}|d<   t               }
t        t                     5  t        |
      }t!        |      }t#        |      t#        |      z
  }|rt$        j'                  d|       t#        |      }||k\  r;t)        |
|       t$        j'                  d|||       dt+        ||      fcddd       S |j-                  |       t)        |
|       ddd       t	        |t        |       t        |            dfS c c}	}w # 1 sw Y   1xY w)	zAcquire an active-session slot.

    Returns ``(lease, None)`` on success.  When the cap is disabled, the lease is
    a no-op object so callers can unconditionally call ``release()``.
    NF)r   r   r   r   )r   r   r   r}   r   
started_at
updated_atr   z'Pruned %d stale active session lease(s)z9Active session limit reached: active=%d max=%d surface=%s)r   r   r   )r$   rr   rs   rt   r   timer   rF   rq   r~   itemsr   r1   r6   r4   rk   r   lenr   inforx   r+   append)r   r   r!   r   r*   r   nowrj   kv
state_pathraw_entriesrc   prunedr)   s                  r   try_acquire_active_sessionr      s    36:Lzz|H!!	

  	 ))+C*ow<yy{1"))+>E "*.."2
!QjC6HCFAI
j J	:<	  ,#J/k*[!CL0KKA6J7|<':w/KKK	 5lLQQ, ,  	uz7+#,& z?G 	 1

, ,s   /GG:BGGG!c           	     |   t               }	 t        t                     5  t        t	        |            }|D cg c]/  }t        |j                  d      xs d      | j                  k7  r|1 }}t        |      t        |      k7  rt        ||       d d d        d| _
        y c c}w # 1 sw Y   xY w# d| _
        w xY w)Nr   r   T)r1   r6   r4   r   rk   r   r   r   r   rx   r   )leaser   rc   rj   kepts        r   r   r   *  s    Jz|$ 	1!-
";<G %uyy,23u~~E D 
 4yCL(z40	1 	1 	1 s3   B2 B&4B!,%B&B2 !B&&B/+B2 2	B;c          
     ^   t        |xs d      }|sy| j                  ry| j                  s|| _        yt	               }t        t                     5  t        t        |            }d}|D ]  }t        |j                  d      xs d      | j                  k7  r/||d<   t        j                         |d<   |rA|j                         D 	ci c]"  \  }}	t        |t               st        |      |	$ c}	}|d<   d} n |rt        ||       || _        |cddd       S c c}	}w # 1 sw Y   yxY w)	zEMove an existing lease to a new session id without dropping the slot.r   FTr   r   r   r   N)r   r   r   r   r1   r6   r4   r   rk   r   r   r   r   r   rx   )
r   r   r   new_session_idr   rc   updatedrj   r   r   s
             r   transfer_active_sessionr   :  s!    )r*N~~==)J	:<	  mJ78 
	E599Z(.B/5>>A"0E,"&))+E,*2..*:%"&!QjC>PCFAI%j! G
	 :w/-E# % s%   A9D#
D
#D
1"D#D##D,c                     t               } t        t                     5  t        t	        |             }t        | |       |cddd       S # 1 sw Y   yxY w)z@Return the pruned active-session registry for diagnostics/tests.N)r1   r6   r4   r   rk   rx   )r   rc   s     r    active_session_registry_snapshotr   _  sD    J	:<	  mJ78z7+  s   "A

A)r   )r   r   r   r   r   Optional[int])r!   r   r   r   )r)   r   r*   r   r   r   )r   r   )r9   r   r   list[dict[str, Any]])r9   r   rc   r   r   r   )r}   r   r   Optional[float])r   r   r   r   r8   )r}   r   r   r   r   r   )rc   r   r   r   )
r   r   r   r   r!   r   r   Optional[dict[str, Any]]r   z2tuple[Optional[ActiveSessionLease], Optional[str]])r   r   r   r   )r   r   r   r   r   r   r   r   )r   r   )&__doc__
__future__r   rd   loggingrF   r   rr   dataclassesr   pathlibr   typingr   r   hermes_constantsr   	getLoggerr[   r   r   r$   r+   r.   r1   r4   r6   rk   rx   r~   r   r   r   r   r   r   r   r   r(   r%   r   <module>r      s   #   	   !    ,			8	$@8"/112 2jC70 
% 
% 
%$ *.== = 	=
 '= 8=@( *.	"" " '	"
 
"Jr%   