
    ri^7                        d 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mZmZmZmZmZmZmZ ddlmZ d Z G d d          Zd	 ZddefdZd ZddefdZdefdZddZddededefdZdefdZ defdZ!de"fdZ#dS ) 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                     g } t          j        d          D ]v}|j                            d          r	 t                              |j                  r'|                                                                g# t          $ r Y sw xY wt          5  t          j                    D ]Dt          fd| D                       s'|                                                                E	 ddd           n# 1 swxY w Y   |                     d d           t          j        t#          j        | dd	
          d           dS )z5Rebuild the session index file for O(1) future reads.*.json_c              3   :   K   | ]}|d          j         k    V  dS )
session_idNr   ).0ess     '/home/ubuntu/hermes-webui/api/models.py	<genexpr>z'_write_session_index.<locals>.<genexpr>   s.      HH1q!,6HHHHHH    Nc                     | d         S )N
updated_at r   s    r   <lambda>z&_write_session_index.<locals>.<lambda>    s
    q r   TkeyreverseF   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''  6S!!+8	QV$$A-'..--- 	 	 	D		 , ,"" 	, 	,AHHHHHHHHH ,qyy{{+++	,, , , , , , , , , , , , , , , LL..L===!$*W5QR"S"S"S^effffffs%   AA??
BBAC<<D D c                       e Zd Zdd ee          edddddddddddddfdededed	ed
edededefdZe	d             Z
ddZed             ZdefdZdS )r,   NUntitledFr    r   titlepinnedarchived
project_idinput_tokensoutput_tokensreasoning_effortc                    |pt          j                    j        d d         | _        || _        t          t          |                                                                                    | _	        || _
        |pg | _        |pg | _        |pt          j                    | _        |pt          j                    | _        t!          |	          | _        t!          |
          | _        |pd | _        || _        |pd| _        |pd| _        || _        || _        t          |pd                                                                          | _        d S )N   r   r=   )uuiduuid4hexr   r>   strr   
expanduserresolve	workspacemodelmessages
tool_callstime
created_atr   boolr?   r@   rA   profilerB   rC   estimated_costpersonalitystriplowerrD   )selfr   r>   rM   rN   rO   rR   r   rP   r?   r@   rA   rT   rB   rC   rU   rV   rD   kwargss                      r   __init__zSession.__init__%   s#    %=
(8"(=
T)__7799AACCDD
 B$*$3	$3	6llX$,(-A*/a,& #$4$: ; ; A A C C I I K Kr   c                 &    t           | j         dz  S )N.json)r   r   rY   s    r   pathzSession.path?   s    66666r   returnc                     t          j                     | _        | j                            t	          j        | j        dd          d           t                       d S )NFr"   r#   r&   r'   )rQ   r   r_   r5   r6   r7   __dict__r:   r^   s    r   savezSession.saveC   s[    )++	Jt}5CCC 	 	
 	
 	
 	r   c           	          t           | dz  }|                                sd S  | di t          j        |                    d                    S )Nr]   r&   r'   r   )r   existsr6   loads	read_text)clssidr9   s      r   r-   zSession.loadK   sU    S---'xxzz 	4s??TZW = =>>???r   c                     i d| j         d| j        d| j        d| j        dt	          | j                  d| j        d| j        d| j        d	| j	        d
| j
        d| j        d| j        d| j        d| j        d| j        d| j        S )Nr   r>   rM   rN   message_countrR   r   r?   r@   rA   rT   rB   rC   rU   rV   rD   )r   r>   rM   rN   lenrO   rR   r   r?   r@   rA   rT   rB   rC   rU   rV   rD   r^   s    r   r0   zSession.compactR   s    
$/
TZ
 
 TZ	

 S//
 $/
 $/
 dk
 
 $/
 t|
 D-
 T/
 d1
 4+
   5!
 	
r   r`   N)__name__
__module____qualname__rJ   r	   r
   rS   intr[   propertyr_   rc   classmethodr-   dictr0   r   r   r   r,   r,   $   s"       '+
30114D uU!%t#$4!L L3 LC L +/L AEL !	L
  #L
 69L 69L L L L4 7 7 X7    @ @ [@
 
 
 
 
 
 
r   r,   c                     t           5  | t          v r-t          j        |            t          |          cd d d            S 	 d d d            n# 1 swxY w Y   t                              |           }|rt           5  |t          | <   t          j        |            t          t                    t          k    r2t          j        d           t          t                    t          k    2d d d            n# 1 swxY w Y   |S t          |           )NFlast)	r   r   move_to_endr,   r-   rl   r   popitemKeyError)ri   r   s     r   get_sessionr{   f   s   	 ! !(?? %%%C=! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 	SA  	- 	-HSM %%%h--,.. e,,,, h--,..	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	-
 
3--s$   *AAA:A.C44C8;C8r=   rD   c           	      :   	 ddl m}  |            }n# t          $ r d }Y nw xY wt          | pt	                      |pt
          j        |t          j        |pt
          j        |                    }t          5  |t          |j
        <   t          j        |j
                   t          t                    t          k    r2t          j        d           t          t                    t          k    2d d d            n# 1 swxY w Y   |                                 |S )Nr   get_active_profile_name)rM   rN   rT   rD   Frv   )api.profilesr~   ImportErrorr,   r   _cfgr
   clamp_reasoning_effortr   r   r   rx   rl   r   ry   rc   )rM   rN   rD   r~   _profiler   s         r   new_sessionr   u   sd   888888**,,   3133)t)4'T'
 
		 	 	A 
 ) )!"Q\***(mml**%(((( (mml**) ) ) ) ) ) ) ) ) ) ) ) ) ) )
 FFHHHHs    ""8A8C<<D D c                     t          j                    r	 t          j        t          j        d                    } d | D             }t
          5  t          j                    D ]                                |j	        <   	 d d d            n# 1 swxY w Y   t          |                                d d          }d |D             }|D ]                    d          sd	d<   |S # t          $ r Y nw xY wg }t          j        d
          D ]d}|j                            d          r	 t"                              |j                  r|                               U# t          $ r Y aw xY wt          j                    D ]2t+          fd|D                       r|                               3|                    d d           d |D             }|D ]                    d          sd	d<   |S )Nr&   r'   c                      i | ]}|d          |S r   r   r   r   s     r   
<dictcomp>z all_sessions.<locals>.<dictcomp>   s    ;;;<!;;;r   c                 >    |                      dd          | d         fS )Nr?   Fr   getr   s    r   r   zall_sessions.<locals>.<lambda>   s     quuXu?U?UWXYeWf>g r   Tr   c                 z    g | ]8}|                     d d          dk    r|                     dd          dk    6|9S )r>   r<   rk   r   r   r   s     r   
<listcomp>z all_sessions.<locals>.<listcomp>   sN    wwwAgj0I0I:0U0UZ[Z_Z_`opqZrZrtuZuZuaZuZuZur   rT   defaultr   r   c              3   8   K   | ]}j         |j         k    V  d S )Nr   )r   xr   s     r   r   zall_sessions.<locals>.<genexpr>   s,      99q|q|+999999r   c                 2    t          | dd          | j        fS )Nr?   F)getattrr   r   s    r   r   zall_sessions.<locals>.<lambda>   s    GAx77F r   c                 |    g | ]9}|j         d k    rt          |j                  dk    %|                                :S )r<   r   )r>   rl   rO   r0   r   s     r   r   z all_sessions.<locals>.<listcomp>   s?    [[[aqw
/B/Bs1:XYGYGYaiikkGYGYGYr   )r   re   r6   rf   rg   r   r   r2   r0   r   sortedr   r1   r   r)   r*   r+   r,   r-   r.   r/   allr4   )index	index_mapresultoutr9   r   s        @r   all_sessionsr      s    "" 	J1;WMMMNNE;;U;;;I : :!** : :A./iikkIal++:: : : : : : : : : : : : : : : I,,..4g4gquvvvFwwwwwF  - -uuY'' -#,AiLM 	 	 	D	 Ch''  6S!!+8	QV$$A#**Q--- 	 	 	D	_ I I9999S99999H3::a===HHFFPTHUUU[[3[[[F % %uuY 	%$AiLMsH   :C- 3BC- BC- BAC- -
C:9C:16E((
E54E5r<   fallbackc                 4   | D ]}|                     d          dk    ry|                     dd          }t          |t                    rd                    d |D                       }t	          |                                          }|r|dd         c S |S )	z3Derive a session title from the first user message.roleusercontentr=    c              3      K   | ]H}t          |t                    |                    d           dk    0|                    dd          V  IdS )typetextr=   N)
isinstancert   r   )r   r9   s     r   r   ztitle_from.<locals>.<genexpr>   sZ      mm1Z4=P=PmUVUZUZ[aUbUbflUlUlQUU62..UlUlUlUlmmr   N@   )r   r   listjoinrJ   rW   )rO   r   mcr   s        r   
title_fromr      s     ! !55==F""i$$A!T"" nHHmmmmmmmq66<<>>D !CRCy   Or   r`   c                      t          j                    sg S 	 t          j        t          j        d                    S # t
          $ r g cY S w xY w)z;Load project list from disk. Returns list of project dicts.r&   r'   )r   re   r6   rf   rg   r1   r   r   r   load_projectsr      s_    !! 	z-17CCCDDD   			s   &> AAc                 \    t          j        t          j        | dd          d           dS )zWrite project list to disk.Fr"   r#   r&   r'   N)r   r5   r6   r7   )projectss    r   save_projectsr      s0    TZuQOOOZabbbbbbr   unknownr   r>   rN   c                 p    t          | |t                      |||          }|                                 |S )z\Create a new WebUI session populated with CLI messages.
    Returns the Session object.
    )r   r>   rM   rN   rO   rT   )r,   r   rc   )r   r>   rO   rN   rT   r   s         r   import_cli_sessionr      sD     	$&&	 	 	A FFHHHHr   c                  <   ddl } g }	 ddl}n# t          $ r |cY S w xY w	 ddlm} t           |                                                                                      }nl# t          $ r_ t          | 	                    dt          t          dz                                                                                                }Y nw xY w|dz  }|                                s|S 	 ddlm}  |            }n# t          $ r d}Y nw xY w	 |                    t          |                    5 }|j        |_        |                                }	|	                    d           |	                                D ]v}
|
d	         }|
d
         p|
d         }|}|                    ||
d         pdt          t+                                |
d         pd|
d         pd|
d         |ddd|ddd           w	 ddd           n# 1 swxY w Y   n# t          $ r g cY S w xY w|S )aF  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.
    r   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>   zCLI SessionrN   r   rk   FcliT)r   r>   rM   rN   rk   rR   r   r?   r@   rA   rT   
source_tagis_cli_session)ossqlite3r   r   r   r   rK   rL   r1   getenvrJ   r   re   r~   connectRowrow_factorycursorexecutefetchallr/   r   )r   cli_sessionsr   r   hermes_homedb_pathr~   _cli_profileconncurrowri   raw_tsrT   s                 r   get_cli_sessionsr      s    IIIL   c777777113344??AAIIKK c c c299]Cy8H4I4IJJKKVVXX``bbc J&G>> 888888..00   &__S\\** "	d&{D++--CKK 	 	 	 	 ||~~  $i_-B\1B '##"% \:]!$%7%9%9!:!: \6Y%(%9%>Q"%l"3"(# %"&&"'&*% %    "	 "	 "	 "	 "	 "	 "	 "	 "	 "	 "	 "	 "	 "	 "	F    			 sh    AA" "A&C
C*C; ;D
	D
"H
 0CG>2H
 >HH
 HH
 
HHc           
      f   ddl }	 ddl}n# t          $ r g cY S w xY w	 ddlm} t           |                                                                                      }nl# t          $ r_ t          |	                    dt          t          dz                                                                                                }Y nw xY w|dz  }|                                sg S 	 |                    t          |                    5 }|j        |_        |                                }|                    d| f           g }|                                D ]-}	|                    |	d         |	d	         |	d
         d           .	 ddd           n# 1 swxY w Y   n# t          $ r g cY S w xY w|S )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.
    r   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   r   r   r   rK   rL   r1   r   rJ   r   re   r   r   r   r   r   r   r/   )
ri   r   r   r   r   r   r   r   msgsr   s
             r   get_cli_session_messagesr   8  s@   
 III   			c777777113344??AAIIKK c c c299]Cy8H4I4IJJKKVVXX``bbcJ&G>> 	__S\\** 	d&{D++--CKK 
    D||~~  K"9~!$[!1     	 	 	 	 	 	 	 	 	 	 	 	 	 	 	     			KsV    AA   A&C	C	("F 
A<FF FF FF F.-F.c           
      *   ddl }	 ddl}n# t          $ r Y dS w xY w	 ddlm} t           |                                                                                      }nl# t          $ r_ t          |	                    dt          t          dz                                                                                                }Y nw xY w|dz  }|                                sdS 	 |                    t          |                    5 }|                                }|                    d| f           |                    d	| f           |                                 |j        dk    cddd           S # 1 swxY w Y   dS # t          $ r Y dS w xY w)
z{Delete a CLI session from state.db (messages + session row).
    Returns True if deleted, False if not found or error.
    r   NFr   r   r   r   z)DELETE FROM messages WHERE session_id = ?z!DELETE FROM sessions WHERE id = ?)r   r   r   r   r   r   rK   rL   r1   r   rJ   r   re   r   r   r   commitrowcount)ri   r   r   r   r   r   r   r   s           r   delete_cli_sessionr   b  s     III   uuc777777113344??AAIIKK c c c299]Cy8H4I4IJJKKVVXX``bbcJ&G>> u__S\\** 	$d++--CKKCcVLLLKK;cVDDDKKMMM<!#	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$    uusV    
AA A&CC'"F 	A!E7*F 7E;;F >E;?F 
FF)NNr=   )r<   rm   )r   N)$__doc__collectionsr6   rQ   rG   pathlibr   
api.configconfigr   r   r   r   r   r   r	   r
   r   r   api.workspacer   r:   r,   r{   rJ   r   r   r   r   r   r   r   r   r   rS   r   r   r   r   <module>r      sC                                            - , , , , ,g g g$@
 @
 @
 @
 @
 @
 @
 @
D   c    0% % %P
 
3 
 
 
 
t    c c c c
 3 s S    $Q$ Q Q Q Qh'T ' ' ' 'Tt      r   