+
    ri^7                        R t ^ RIt^ RI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HtHtHt ^ RIHt R t ! R R4      tR tRR	 R
 lltR tRR R lltR R ltR R ltRR R lltR R ltR R ltR R lt R# )z=
Hermes Web UI -- Session model and in-memory session store.
N)Path)	SESSION_DIRSESSION_INDEX_FILESESSIONSSESSIONS_MAXLOCKDEFAULT_WORKSPACEDEFAULT_MODELPROJECTS_FILEHOME)get_last_workspacec                   a . p \         P                  ! R4       Fo  pVP                  P                  R4      '       d   K&   \        P                  VP                  4      oS'       d"   V P                  SP                  4       4       Ko  Kq  	  \        ;_uu_ 4        \        P                  ! 4        Fd  o\        ;QJ d    V3R lV  4       F  '       g   K   RM	  RM! V3R lV  4       4      '       d   KE  V P                  SP                  4       4       Kf  	  RRR4       V P                  R RR7       \        P                   ! \"        P$                  ! V R^R	7      R
R7       R#   \         d     EK[  i ; i  + '       g   i     Lh; i)z5Rebuild the session index file for O(1) future reads.*.json_c              3   N   <"   T F  qR ,          SP                   8H  x  K  	  R# 5i)
session_idNr   ).0ess   & '/home/ubuntu/hermes-webui/api/models.py	<genexpr>'_write_session_index.<locals>.<genexpr>   s     H1!,,6s   "%TFNc                     V R ,          # )
updated_at r   s   &r   <lambda>&_write_session_index.<locals>.<lambda>    s    q    keyreverseensure_asciiindentutf-8encoding)r   globname
startswithSessionloadstemappendcompact	Exceptionr   r   valuesanysortr   
write_textjsondumps)entriespr   s     @r   _write_session_indexr:      s    Gh'66S!!8	QVV$A'..-q	 ( 
"A3HH333HHHHqyy{+ # 
 LL.L=!!$**W5QR"S^ef  			s0   &E&(E&8E9E96#E9&E65E69F		c                      a  ] tR t^$t o RR]! ]4      ]RRRRRRRR^ ^ RRR3V 3R lR llt]R 4       t	V 3R lR	 lt
]R
 4       tV 3R lR ltRtV tR# )r,   NUntitledF c                J   < V ^8  d   QhRS[ RS[ RS[RS[RS[ RS[RS[RS[ /# )	   r   titlepinnedarchived
project_idinput_tokensoutput_tokensreasoning_effort)strboolint)format__classdict__s   "r   __annotate__Session.__annotate__%   sc     L L3 LC L +/L AEL !	L
  #L
 69L 69Lr   c                   T;'       g'    \         P                  ! 4       P                  R ,          V n        W n        \        \        V4      P                  4       P                  4       4      V n	        W@n
        T;'       g    . V n        T;'       g    . V n        T;'       g    \        P                  ! 4       V n        T;'       g    \        P                  ! 4       V n        \!        V	4      V n        \!        V
4      V n        T;'       g    RV n        Wn        T;'       g    ^ V n        T;'       g    ^ V n        Wn        VV n        \        T;'       g    R4      P3                  4       P5                  4       V n        R# ):N   NNr=   )uuiduuid4hexr   r@   rG   r   
expanduserresolve	workspacemodelmessages
tool_callstime
created_atr   rH   rA   rB   rC   profilerD   rE   estimated_costpersonalitystriplowerrF   )selfr   r@   rU   rV   rW   rZ   r   rX   rA   rB   rC   r[   rD   rE   r\   r]   rF   kwargss   &&&&&&&&&&&&&&&&&&,r   __init__Session.__init__%   s    %==

(8(8(=
T)_779AACD
 B$**$33		$33		6lX$,,(--A*//a,& #$4$:$: ; A A C I I Kr   c                6    \         V P                   R 2,          # ).json)r   r   r`   s   &r   pathSession.path?   s    0666r   c                   < V ^8  d   QhRR/# r?   returnNr   )rJ   rK   s   "r   rL   rM   C   s      d r   c                    \         P                   ! 4       V n        V P                  P                  \        P
                  ! V P                  R ^R7      RR7       \        4        R# )Fr#   r&   r'   N)rY   r   rg   r5   r6   r7   __dict__r:   rf   s   &r   saveSession.saveC   sE    ))+		JJt}}5C 	 	
 	r   c           
         \         V R 2,          pVP                  4       '       g   R# V ! R/ \        P                  ! VP	                  RR7      4      B # )re   Nr&   r'   r   )r   existsr6   loads	read_text)clssidr9   s   && r   r-   Session.loadK   sB    SE-'xxzz?TZZW =>??r   c                    < V ^8  d   QhRS[ /# r?   rk   )dict)rJ   rK   s   "r   rL   rM   R   s     
 
 
r   c                   / R V P                   bRV P                  bRV P                  bRV P                  bR\	        V P
                  4      bRV P                  bRV P                  bRV P                  bRV P                  bR	V P                  bR
V P                  bRV P                  bRV P                  bRV P                  bRV P                  bRV P                   b# )r   r@   rU   rV   message_countrZ   r   rA   rB   rC   r[   rD   rE   r\   r]   rF   )r   r@   rU   rV   lenrW   rZ   r   rA   rB   rC   r[   rD   rE   r\   r]   rF   rf   s   &r   r0   Session.compactR   s   
$//
TZZ
 
 TZZ	

 S/
 $//
 $//
 dkk
 
 $//
 t||
 D--
 T//
 d11
 4++
   5 5!
 	
r   )rB   rZ   r\   rD   rW   rV   rE   r]   rA   r[   rC   rF   r   r@   rX   r   rU   )__name__
__module____qualname____firstlineno__rG   r   r	   rb   propertyrg   rn   classmethodr-   r0   __static_attributes____classdictcell__)rK   s   @r   r,   r,   $   s~     '+
014D uU!%t#$4!L L4 7 7  @ @
 
r   r,   c                    \         ;_uu_ 4        V \        9   d.   \        P                  ! V 4       \        V ,          uuR R R 4       #  R R R 4       \        P	                  V 4      pV'       dk   \         ;_uu_ 4        V\        V &   \        P                  ! V 4       \        \        4      \        8  d   \        P                  ! RR7       K1   R R R 4       V# \        V 4      h  + '       g   i     L; i  + '       g   i     T# ; i)NFlast)	r   r   move_to_endr,   r-   r|   r   popitemKeyError)ru   r   s   & r   get_sessionr   f   s    	(?  %C= 
 
 	SATHSM  %h-,.  e, / 
 
3- 
 T
 s   .C#<AC6#C3	6D	c                $    V ^8  d   QhR\         /# )r?   rF   rG   )rJ   s   "r   rL   rL   u   s      c r   c           	      T    ^ RI Hp V! 4       p\        T ;'       g    \	        4       T;'       g    \
        P                  T\
        P                  ! T;'       g    \
        P                  V4      R7      p\        ;_uu_ 4        V\        VP                  &   \        P                  ! VP                  4       \        \        4      \        8  d   \        P                  ! RR7       K1   RRR4       VP                  4        V#   \         d    Rp ELi ; i  + '       g   i     L6; i)    get_active_profile_nameN)rU   rV   r[   rF   Fr   )api.profilesr   ImportErrorr,   r   _cfgr	   clamp_reasoning_effortr   r   r   r   r|   r   r   rn   )rU   rV   rF   r   _profiler   s   &&&   r   new_sessionr   u   s    8*, 	3313))t))44''T''
		A 
!"Q\\*(ml*%( + 

 FFHH#   
s   D A%DDDD'	c                    a \         P                  ! 4       '       Ed    \        P                  ! \         P                  ! R R7      4      p V  Uu/ uF  qR,          VbK  	  pp\
        ;_uu_ 4        \        P                  ! 4        F   oSP                  4       VSP                  &   K"  	  RRR4       \        VP                  4       R RR7      pV Uu. uF4  qP                  RR4      R8X  d   VP                  R	^ 4      ^ 8X  d   K2  VNK6  	  ppV F!  oSP                  R
4      '       d   K  RSR
&   K#  	  V# . p\        P                  ! R4       Fa  pVP                  P!                  R4      '       d   K&   \"        P%                  VP&                  4      oS'       d   VP)                  S4       Ka  Kc  	  \        P                  ! 4        FV  o\*        ;QJ d    V3R lV 4       F  '       d   K   RM	  RM! V3R lV 4       4      '       g   KE  VP)                  S4       KX  	  VP-                  R RR7       V Uu. uF?  qP.                  R8X  d   \1        VP2                  4      ^ 8X  d   K/  VP                  4       NKA  	  ppV F!  oSP                  R
4      '       d   K  RSR
&   K#  	  V# u upi   + '       g   i     EL; iu upi   \         d     ELi ; i  \         d     EK  i ; iu upi )r&   r'   r   Nc                 8    V P                  R R4      V R,          3# )rA   Fr   )getr   s   &r   r   all_sessions.<locals>.<lambda>   s    quuXu?UWXYeWf>gr   Tr    r@   r<   r{   r[   defaultr   r   c              3   V   <"   T F  pSP                   VP                   8g  x  K   	  R # 5i)Nr   )r   xr   s   & r   r   all_sessions.<locals>.<genexpr>   s     9Sq||q||+Ss   &)Fc                 4    \        V R R4      V P                  3# )rA   F)getattrr   r   s   &r   r   r      s    GAx7Fr   )r   rq   r6   rr   rs   r   r   r2   r0   r   sortedr   r1   r   r)   r*   r+   r,   r-   r.   r/   allr4   r@   r|   rW   )indexr   	index_mapresultoutr9   s    `    r   all_sessionsr      sD     ""	JJ1;;WMNE5:;U<!+UI;!*A./iikIall+ +  I,,.4gquvF!'wAgj0I:0UZ[Z_Z_`opqZrtuZuaaFw uuY''#,AiL  M Ch'66S!!8	QVV$A#**Q-q	 ( __39S93339S9993::a= HHFPTHU#&[3aww
/Bs1::XYGYkaiik3F[uuY$AiL  M? <
 x  		  		
 \sw   /K
 J, K
 0:J1*)K
 /KKK
 -
K
 7&KK<,K/-K/,K
 1K	<K
 
KKK,+K,c                $    V ^8  d   QhR\         /# )r?   fallbackr   )rJ   s   "r   rL   rL      s     
 
3 
r   c                    V  F  pVP                  R4      R8X  g   K  VP                  RR4      p\        V\        4      '       d   RP                  R V 4       4      p\	        V4      P                  4       pV'       g   K~  VR,          u # 	  V# )z3Derive a session title from the first user message.roleusercontentr=    c              3      "   T FG  p\        V\        4      '       g   K  VP                  R 4      R8X  g   K3  VP                  RR4      x  KI  	  R# 5i)typetextr=   N)
isinstancery   r   )r   r9   s   & r   r   title_from.<locals>.<genexpr>   sB     m1Z4=P.UVUZUZ[aUbflUl.QUU62..s   AAA:N@   N)r   r   listjoinrG   r^   )rW   r   mcr   s   &&   r   
title_fromr      sr    55=F"i$A!T""HHmmmq6<<>DtCy   Or   c                $    V ^8  d   QhR\         /# rx   r   )rJ   s   "r   rL   rL      s      t r   c                     \         P                  ! 4       '       g   . #  \        P                  ! \         P                  ! RR7      4      #   \
         d    . u # i ; i)z;Load project list from disk. Returns list of project dicts.r&   r'   )r
   rq   r6   rr   rs   r1   r   r   r   load_projectsr      sI    !!	zz-117CDD 	s   *A
 
AAc                    V ^8  d   QhRR/# rj   r   )rJ   s   "r   rL   rL      s     c ct cr   c                d    \         P                  ! \        P                  ! V R^R7      RR7       R# )zWrite project list to disk.Fr#   r&   r'   N)r
   r5   r6   r7   )projectss   &r   save_projectsr      s     TZZuQOZabr   c                <    V ^8  d   QhR\         R\         R\         /# )r?   r   r@   rV   r   )rJ   s   "r   rL   rL      s!      3 s S r   c           	     X    \        V V\        4       VVVR7      pVP                  4        V# )zTCreate a new WebUI session populated with CLI messages.
Returns the Session object.
)r   r@   rU   rV   rW   r[   )r,   r   rn   )r   r@   rW   rV   r[   r   s   &&&&& r   import_cli_sessionr      s3     	$&	A FFHHr   c                $    V ^8  d   QhR\         /# rx   r   )rJ   s   "r   rL   rL      s     Q Q$ Qr   c                 V   ^ RI p . p ^ RIp ^ RIHp \        T! 4       4      P                  4       P                  4       pTR,          pTP                  4       '       g   T#  ^ RIHp T! 4       p TP                  \        T4      4      ;_uu_ 4       pTP                  Tn        TP#                  4       p	T	P%                  R4       T	P'                  4        F  p
T
R,          pT
R	,          ;'       g
    T
R
,          pTpTP)                  RTRT
R,          ;'       g    RR\        \+        4       4      RT
R,          ;'       g    RRT
R,          ;'       g    ^ RT
R
,          RTRRRRRRRTRRRR/4       K  	  RRR4       T#   \         d    Tu # i ; i  \         dP    \        T P                  R\        \        R,          4      4      4      P                  4       P                  4       p ELi ; i  \         d    Rp ELi ; i  + '       g   i     T# ; i  \         d    . u # i ; i)a2  Read CLI sessions from the agent's SQLite store and return them as
dicts in a format the WebUI sidebar can render alongside local sessions.

Returns empty list if the SQLite DB is missing, the sqlite3 module is
unavailable, or any error occurs -- the bridge is purely additive and never
crashes the WebUI.
Nget_active_hermes_homeHERMES_HOME.hermesstate.dbr   a  
                SELECT s.id, s.title, s.model, s.message_count,
                       s.started_at, s.source,
                       MAX(m.timestamp) AS last_activity
                FROM sessions s
                LEFT JOIN messages m ON m.session_id = s.id
                GROUP BY s.id
                ORDER BY COALESCE(MAX(m.timestamp), s.started_at) DESC
                LIMIT 200
            idlast_activity
started_atr   r@   zCLI SessionrU   rV   unknownr{   rZ   r   rA   FrB   rC   r[   
source_tagcliis_cli_sessionT)ossqlite3r   r   r   r   rS   rT   r1   getenvrG   r   rq   r   connectRowrow_factorycursorexecutefetchallr/   r   )r   cli_sessionsr   r   hermes_homedb_pathr   _cli_profileconncurrowru   raw_tsr[   s                 r   get_cli_sessionsr      s    Lc7134??AIIK J&G>>8.0&__S\**d&{{D++-CKK 	 	 ||~$i_-BB\1B '## #S\::]%7%9!:S\66Y#S%9%>%>Q #l"3 &e $w %$d%  & +N K    c299]Cy8H4IJKVVX``bc   +*N 	  	s~   F  2F !G0 /"H AH0)H%H H#H6H  FFAG-,G-0H HH	H H H('H(c                $    V ^8  d   QhR\         /# rx   r   )rJ   s   "r   rL   rL   8  s     ' 'T 'r   c                8   ^ RI p ^ RIp ^ RIHp \        T! 4       4      P                  4       P                  4       pTR,          pTP                  4       '       g   . #  TP                  \        T4      4      ;_uu_ 4       pTP                  Tn        TP!                  4       pTP#                  RT 34       . pTP%                  4        F/  p	TP'                  RT	R,          RT	R,          R	T	R	,          /4       K1  	  RRR4       T#   \         d    . u # i ; i  \         dP    \        TP                  R\        \        R,          4      4      4      P                  4       P                  4       p EL4i ; i  + '       g   i     X# ; i  \         d    . u # i ; i)
zRead messages for a single CLI session from the SQLite store.
Returns a list of {role, content, timestamp} dicts.
Returns empty list on any error.
Nr   r   r   r   z
                SELECT role, content, timestamp
                FROM messages
                WHERE session_id = ?
                ORDER BY timestamp ASC
            r   r   	timestamp)r   r   r   r   r   r   rS   rT   r1   r   rG   r   rq   r   r   r   r   r   r   r/   )
ru   r   r   r   r   r   r   r   msgsr   s
   &         r   get_cli_session_messagesr   8  sm   
 c7134??AIIK J&G>>	__S\**d&{{D++-CKK 
  D||~CKs9~[!1  & +$ K?  	  c299]Cy8H4IJKVVX``bc +*$ K  	sT   D 2D "F	 A:E5;F	 DDAE21E25F	 F	 F	 	FFc                $    V ^8  d   QhR\         /# rx   )rH   )rJ   s   "r   rL   rL   b  s      t r   c           
        ^ RI p ^ RIp ^ RIHp \        T! 4       4      P                  4       P                  4       pTR,          pTP                  4       '       g   R#  TP                  \        T4      4      ;_uu_ 4       pTP                  4       pTP                  RT 34       TP                  RT 34       TP!                  4        TP"                  ^ 8  uuRRR4       #   \         d     R# i ; i  \         dP    \        TP                  R\        \        R,          4      4      4      P                  4       P                  4       p ELi ; i  + '       g   i     R# ; i  \         d     R# i ; i)	zsDelete a CLI session from state.db (messages + session row).
Returns True if deleted, False if not found or error.
NFr   r   r   r   z)DELETE FROM messages WHERE session_id = ?z!DELETE FROM sessions WHERE id = ?)r   r   r   r   r   r   rS   rT   r1   r   rG   r   rq   r   r   r   commitrowcount)ru   r   r   r   r   r   r   r   s   &       r   delete_cli_sessionr   b  s.    c7134??AIIK J&G>>__S\**d++-CKKCcVLKK;cVDKKM<<!# +*    c299]Cy8H4IJKVVX``bc +**  sT   C! 2C3 "E$ AE
E$ !C0/C03AEEE!	E$ !E$ $E32E3)NNr=   )r<   )r   N)!__doc__collectionsr6   rY   rP   pathlibr   
api.configconfigr   r   r   r   r   r   r   r	   r
   r   api.workspacer   r:   r,   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>r      s|            -g$@
 @
D0%P
c
$Qh'Tr   