
    ,jD|                     R   d Z ddlZddlZddlmZmZmZmZmZ dZ	dee
eedf         defdZdedefd	Zd=d
eeef         dee
         deeef         fdZdefdZdefdZd>dede
de
defdZd=de
dedefdZ	 	 d?dede
de
dedef
dZ	 d=dedeee                  de
dee         dedefdZ	 	 	 	 	 	 	 	 	 	 d@dedede
dedede
de
dededefd"Zdefd#Zd$d%d&d'd(d)d*d+d!d,d'd-d.gd/d0d'd1d)d*d2d)d*d3dd,d'd4d)d'd5d)d6g d7d8Zdd9lmZmZ  ej        d$d$ed: ed;<           dS )Au  
Session Search Tool - Long-Term Conversation Recall

Single-shape tool with three calling modes (inferred from args, no explicit
mode parameter):

  1. DISCOVERY — pass ``query``. Runs FTS5, dedupes hits by session lineage,
     returns top N sessions each with: snippet, ±5 message window around the
     match, plus bookend_start (first 3 user+assistant msgs of session) and
     bookend_end (last 3). Zero LLM cost.

  2. SCROLL — pass ``session_id`` + ``around_message_id``. Returns a window
     of ±window messages centered on the anchor, no FTS5, no bookends. To
     scroll forward / backward, re-anchor on the last / first message id of
     the returned window.

  3. BROWSE — no args. Returns recent sessions chronologically (titles,
     previews, timestamps).

All three modes operate on the SQLite session DB via the FTS5 index and
the get_anchored_view / get_messages_around primitives in hermes_state.
No LLM calls anywhere — every shape returns actual messages from the DB.

History: PR #20238 (JabberELF) seeded a fast/summary dual-mode split; the
toolkit expansion in PR #26419 (yoniebans) added the anchored drill-down,
bookends, and sort. This module merges all of that into a single calling
shape with no mode parameter, no summary LLM path, and explicit scroll
support.
    N)AnyDictListOptionalUnion)subagenttooltsreturnc                    | dS 	 t          | t          t          f          r0ddlm} |                    |           }|                    d          S t          | t                    r{|                     dd                              dd                                          r=ddlm} |                    t          |                     }|                    d          S | S nf# t          t          t          f$ r"}t          j        d	| |d
           Y d}~n2d}~wt          $ r"}t          j        d| |d
           Y d}~nd}~ww xY wt          |           S )zConvert a Unix timestamp (float/int) or ISO string to a human-readable date.

    Returns "unknown" for None, str(ts) if conversion fails.
    Nunknownr   )datetimez%B %d, %Y at %I:%M %p. -z!Failed to format timestamp %s: %sTexc_infoz,Unexpected error formatting timestamp %s: %s)
isinstanceintfloatr   fromtimestampstrftimestrreplaceisdigit
ValueErrorOSErrorOverflowErrorloggingdebug	Exception)r
   r   dtes       >/home/ubuntu/.hermes/hermes-agent/tools/session_search_tool.py_format_timestampr%   +   s   
 
zy\b3,'' 	8))))))''++B;;6777b# 	zz#r""**333;;== <------++E"II66{{#:;;;I	 / Q Q Q92q4PPPPPPPPP \ \ \Db!VZ[[[[[[[[[\r77Ns1   AC# BC#  C# #E:DE$EE
session_idc                 6   |s|S t                      }|}|r||vr~|                    |           	 |                     |          }|snP|                    d          }|sn8|}n/# t          $ r"}t          j        d||d           Y d}~nd}~ww xY w|r||v~|S )zPWalk parent_session_id chain to the lineage root. Falls back to input on errors.parent_session_idz!Error resolving parent for %s: %sTr   N)setaddget_sessiongetr!   r   r    )dbr&   visitedcursparentr#   s          r$   _resolve_to_parentr2   D   s     eeG
C
 #W$$C
	s##A UU.//F CC 	 	 	M=sAPTUUUUEEEE	  #W$$ Js#   A$ 	A$ !A$ $
B.BBm	anchor_idc                 2   |                      d          |                      d          |                      d          |                      d          d}|                      d          r|                      d          |d<   |                      d          r|                      d          |d<   |                      d          r|                      d          |d<   ||                      d          |k    rd
|d<   d |                                D             S )zFSlim a message row for the tool response. Keeps content even if empty.idrolecontent	timestamp)r6   r7   r8   r9   	tool_name
tool_callstool_call_idNTanchorc                 &    i | ]\  }}||dv ||S )N)r8    ).0kvs      r$   
<dictcomp>z"_shape_message.<locals>.<dictcomp>l   s,    QQQTQamqL?P?PAq?P?P?P    )r,   items)r3   r4   entrys      r$   _shape_messagerG   Z   s
    eeDkkf55##UU;''	 E 	uu[ 0UU;//kuu\ 2eeL11luu^ 6 !n 5 5nt	!9!9h RQU[[]]QQQQrD   profilec                 N   | !t          |                                           sdS ddlm} ddlm} |                    |           }|                    |           |                    |          st          d| d           ||
                    |          dz  d	          S )
u  Open another profile's ``state.db`` read-only, or None for the current one.

    The desktop's ``@session:<profile>/<id>`` links always carry the source
    profile, so a linked session from profile B can be read while the agent
    runs in profile A. ``read_only=True`` (mode=ro) takes no write lock — safe
    to point at a live profile's DB, including our own. Returns None when no
    profile is given (use the caller's default db).
    Nr   profiles	SessionDB	profile 'z' does not existstate.dbTdb_path	read_only)r   strip
hermes_clirK   hermes_staterM   normalize_profile_namevalidate_profile_nameprofile_existsr   get_profile_dir)rH   profiles_modrM   canons       r$   _resolve_profile_dbr\   o   s     c'll0022t333333&&&&&&//88E&&u---&&u-- ><U<<<===9\99%@@:MY]^^^^rD   c                    ddl m} 	 ddlm} ddlm} n# t          $ r Y dS w xY wd|                    d          fg}	 |d |                                D             z  }n&# t          $ r t          j
        dd	
           Y nw xY wt                      }|D ]\  }} ||          dz  }t          |          }	|	|v s|                                s;|                    |	           	  ||d	          }
n# t          $ r Y kw xY w	 |
                    |           r|
|fc S n(# t          $ r t          j
        d| |d	
           Y nw xY w|
                                 dS )u  Scan every profile's ``state.db`` (read-only) for a session id.

    Returns ``(db, profile_name)`` for the first profile that owns the id, or
    ``(None, None)``. Session ids are globally unique (timestamp + random hex),
    so the first hit is authoritative. This is the safety net for linked-session
    reads where the model dropped the owning profile from the link and passed a
    bare id — we find it wherever it actually lives instead of failing.
    r   )PathrJ   rL   )NNdefaultc                 *    g | ]}|j         |j        fS r?   )namepath)r@   infos     r$   
<listcomp>z&_locate_session_db.<locals>.<listcomp>   s!    TTTtTY	*TTTrD   z*list_profiles failed during session locateTr   rO   rP   z%get_session probe failed for %s in %s)pathlibr^   rT   rK   rU   rM   r!   rY   list_profilesr   r    r)   r   existsr*   r+   close)r&   r^   rZ   rM   targetsseenra   homerQ   keypdbs              r$   _locate_session_dbrn      s    777777*******   zz <77	BBCDGSTT|7Q7Q7S7STTTT S S SBTRRRRRRS D  
d$t**z)'ll$;;gnn..;	)Gt<<<CC 	 	 	H		dz** !Dy   ! 	d 	d 	dMA:t^bcccccc	d		:sA    
##!A! ! BB(C66
DDD##"EE   
   headtailc           	         	 |                      |          pi }n1# t          $ r$}t          j        d||d           i }Y d}~nd}~ww xY w|st	          d| d          S 	 |                     |          }nC# t          $ r6}t          j        d||d           t	          d	| d          cY d}~S d}~ww xY wd
 |D             }t          |          }|||z   k    }	|	r|d|         || d         z   n|}
dd|t          |	                    d                    |	                    d          |	                    d          |	                    d          d||	|
d}|	rd| d| d| d|d<   t          j        |d          S )uY  Read shape: dump a whole session by id (head + tail when large).

    Serves the linked-session case — the user dropped an @session reference and
    the agent wants the transcript. Bounded payload: small sessions return in
    full, large ones return the first ``head`` and last ``tail`` messages with a
    pointer to scroll the middle.
    get_session failed for %s: %sTr   Nsession_id not found: Fsuccesszget_messages failed for %s: %szfailed to load session: c                 ,    g | ]}t          |          S r?   rG   r@   r3   s     r$   rd   z!_read_session.<locals>.<listcomp>   s     ...AnQ...rD   read
started_atsourcemodeltitlewhenr}   r~   r   )rw   moder&   session_metamessage_count	truncatedmessageszSession has z messages; showing first z + last z=. Pass around_message_id (any id above) to scroll the middle.messageensure_ascii)r+   r!   r   r    
tool_errorget_messageserrorlenr%   r,   jsondumps)r-   r&   rq   rr   metar#   rowsshapedtotalr   windowresponses               r$   _read_sessionr      s5   ~~j))/R   5z1tTTTT  P?:??OOOOIz** I I I6
APTUUUU8Q88%HHHHHHHHHI /....FKKEt#I/8DVETE]VTEFF^++fF  %dhh|&<&<==hhx((XXg&&XXg&&	
 
  H  
J5 J J4 J J J J J 	 :hU3333s2    
AAA"A8 8
B8+B3-B83B8limitcurrent_session_idc                 X   	 |                      |dz   t          t                    d          }|rt          | |          nd}g }|D ]}|                    dd          }|r||k    s||k    r'|                    d          r=|                    ||                    d          pd|                    d	d          |                    d
d          |                    dd          |                    dd          |                    dd          d           t          |          |k    r nt          j        dd|t          |          dt          |           ddd          S # t          $ r5}t          j        d|d           t          d| d          cY d}~S d}~ww xY w)zEReturn metadata for the most recent sessions (no LLM calls, no FTS5).   T)r   exclude_sourcesorder_by_last_activeNr6   r   r(   r   r}   r|   last_activer   r   preview)r&   r   r}   r|   r   r   r   browsezShowing zZ most recent sessions. Pass a query= to search, or session_id+around_message_id to scroll.)rw   r   resultscountr   Fr   z!Error listing recent sessions: %sr   z Failed to list recent sessions: rv   )list_sessions_richlist_HIDDEN_SESSION_SOURCESr2   r,   appendr   r   r   r!   r   r   r   )	r-   r   r   sessionscurrent_rootr   r0   sidr#   s	            r$   _list_recent_sessionsr      s    &Q((!) !899!% ) 
 
 FXa)".@AAA]a 	 	A%%b//C !4!4?Q8Q8Quu()) NN!w/4%%"--eeL"55 uu]B77!"!:!:55B//     7||u$$ % z\\ K#g,,  K  K  K
 
    	  Q Q Q91tLLLL@Q@@%PPPPPPPPPQs   E'E* *
F)4*F$F)$F)r   around_message_idr   c                 .   t          |t                    r|                                st          dd          S |                                }	 t	                    n(# t
          t          f$ r t          dd          cY S w xY wt          |t                    s*	 t	          |          }n# t
          t          f$ r d}Y nw xY wt          dt          |d                    }|r;t          | |          }t          | |          }|r|r||k    rt          dd          S 	 | 
                    |          pi }n1# t          $ r$}t          j        d	||d
           i }Y d}~nd}~ww xY w|st          d| d          S 	 |                     ||          }	nB# t          $ r5}t          j        d|d
           t          d| d          cY d}~S d}~ww xY w|	                    d          pg }
d}|
sHd}	 t#          | dd          }|5|                    df                                          }|r|d         nd}n0# t          $ r#}t          j        d|d
           d}Y d}~nd}~ww xY w|r||k    rt          | |          }t          | |          }|r|r||k    r	 |                     ||          }|                    d          pg }
|
r9|}	d d| d| d}	 | 
                    |          p|}n# t          $ r Y nw xY w|}n.# t          $ r!}t          j        d|d
           Y d}~nd}~ww xY w|
st          d d| d          S d
d|t)          |                    d                    |                    d          |                    d          |                    d           d!|fd"|
D             |	                    d#d          |	                    d$d          d%	}|r||d&<   t+          j        |d'          S )(u$  Scroll shape: return a window of messages centered on an anchor.

    No FTS5, no bookends — just the slice. The discovery shape's lineage
    fixup is preserved: if the anchor doesn't live in the named session
    but does live in a child session in the same lineage, rebind silently.
    zscroll requires session_idFrv   z)scroll requires integer around_message_idr      ro   z]scroll rejected: anchor lives in the current session lineage (already in your active context)rt   Tr   Nru   )r   zget_messages_around failed: %szfailed to load messages: r   _connz,SELECT session_id FROM messages WHERE id = ?r   z owning-session lookup failed: %szaround_message_id z
 lives in z (child of z); rebound transparentlyz%rebind get_messages_around failed: %sz not in session_id scrollr|   r}   r~   r   r   c                 2    g | ]}t          |           S )r4   ry   )r@   r3   r   s     r$   rd   z_scroll.<locals>.<listcomp>  s'    VVV^A1BCCCVVVrD   messages_beforemessages_after)	rw   r   r&   r   r   r   r   r   r   warningr   )r   r   rS   r   r   	TypeErrorr   maxminr2   r+   r!   r   r    get_messages_aroundr   r,   getattrexecutefetchoner%   r   r   )r-   r&   r   r   r   a_rootc_rootr   r#   viewr   rebind_warningowningconnrowo_rootrebind_viewr   s     `               r$   _scrollr     s    j#&& Gj.>.>.@.@ G6FFFF!!##JV 122z" V V VEuUUUUUUV fc"" 	[[FF:& 	 	 	FFF	COO$$F  #B
33#B(:;; 	f 	6!1!1o   ~~j117R   5z1tTTTT  P?:??OOOOJ%%j2CF%SS J J J6DIIII9a995IIIIIIIIIJ xx!!'RH
 N  ]
	2w--DllB&(  (**  $'0QD 	 	 	M<a$OOOOFFFFFF	  	]f
**'J77F'F33F ]& ]Vv%5%5]"$"8"8AR[a"8"b"bK*x88>BH 
,*N1B N Nf N N)3N N N '!+->>&+A+A+Q\LL( ! ! ! D!%+
  ] ] ]M"I1W[\\\\\\\\\]  
S!2SSzSS
 
 
 	
  .%l&6&6|&D&DEE"&&x00!%%g..!%%g..	
 
 VVVVXVVV88$5q99((#3Q77 H   -,:hU3333s   A! !"BBB/ /CC$D< <
E*E%%E*F 
G'*GGG>AI 
I4I//I4*?L *L L 
LL LL 
M L;;M queryrole_filtersortc                    |r|nddg}	 |                      ||t          t                    dd|          }nB# t          $ r5}t	          j        d|d           t          d	| d
          cY d}~S d}~ww xY w|st          j        dd|g dddd
          S |rt          | |          nd}	i }
|D ]^}|d         }t          | |          }|	r||	k    r#|r||k    r,||
vrt          |          }||d<   ||
|<   t          |
          |k    r n_g }|
                                D ]&\  }}|                    d          p|}|                    d          	 |                     |dd          }n0# t          $ r#}t	          j        d||d           Y d}~ud}~ww xY w	 |                     |          pi }n# t          $ r i }Y nw xY w|t#          |                    d          p|                    d                    |                    d          p|                    dd          |                    d          p|                    d          pd|                    d          pd|                    d          |                    d          pd d! |                    d"          pg D             fd#|                    d$          pg D             d% |                    d&          pg D             |                    d'd          |                    d(d          d)}|r||k    r||d*<   |                    |           (t          j        dd||t          |          t          |
          d+d
          S ),zHDiscovery shape: FTS5 + anchored window + bookends per hit. Single call.user	assistant2   r   )r   r   r   r   offsetr   zFTS5 search failed: %sTr   zSearch failed: Frv   NdiscoverzNo matching sessions found.)rw   r   r   r   r   r   r   r&   _lineage_rootr6   r      )r   bookendz&get_anchored_view failed for %s/%s: %sr|   session_startedr}   r   r~   r   r7   snippetr   c                 ,    g | ]}t          |          S r?   ry   rz   s     r$   rd   z_discover.<locals>.<listcomp>  s     [[[AnQ//[[[rD   bookend_startc                 2    g | ]}t          |           S r   ry   )r@   r3   msg_ids     r$   rd   z_discover.<locals>.<listcomp>  s&    aaaV<<<aaarD   r   c                 ,    g | ]}t          |          S r?   ry   rz   s     r$   rd   z_discover.<locals>.<listcomp>  s     WWW!N1--WWWrD   bookend_endr   r   )r&   r   r}   r~   r   matched_rolematch_message_idr   r   r   r   r   r   r(   )rw   r   r   r   r   sessions_searched)search_messagesr   r   r!   r   r   r   r   r   r2   dictr   rE   r,   get_anchored_viewr   r+   r%   r   )r-   r   r   r   r   r   	role_listraw_resultsr#   current_lineage_rootseen_sessionsrraw_sidresolved_sidr   r   lineage_root
match_infohit_sidr   r   rF   r   s                         @r$   	_discoverr     s     +E0EI@((! !899 ) 
 
  @ @ @.DAAAA/A//?????????@  z4
 
    	 J\e-b2DEEEae
 M  L/)"g66 	L4H$H$H 	'-?"?"?},,q''C#/C *-M,'}&&E ' G$1$7$7$9$9 ! ! j....>,%%	''1'MMDD 	 	 	ODgvWXcghhhhHHHH		>>,77=2LL 	 	 	LLL	 "%  ..S*..AR2S2S  #&&x00WJNN8Y4W4W!%%g..V*..2I2IVY!%%g..6$&NN622 &!~~i006B[[$((?:S:S:YWY[[[aaaatxxPXGYGYG_]_aaaWW8O8O8USUWWW#xx(91=="hh'7;;
 
"  	6LG33)5E%&u:W //     sE   -9 
A8*A3-A83A8E44
F!>FF!%F==GGr   r   c
                 p   |W	 ddl m}
  |
            }nE# t          $ r8 t          j        dd           ddl m} t           |            d	          cY S w xY wt          |t                    rHd
|v rD|	                    d
          \  }}}|r)|}|r%|	!t          |	          
                                s|}	|	ft          |	          
                                rE	 t          |	          }n.# t          $ r!}t          d|	 d| d	          cY d}~S d}~ww xY w||}d}t          |t                    r*|
                                r|t          |||||          S t          |t                    r|
                                r|
                                }t          ||          }t          j        |                              d          r|S t#          |          \  }}|	 t          j        t          ||                    }|                                 n# |                                 w xY w|                    d          r||d<   t          j        |d          S |S t          |t(                    s*	 t)          |          }n# t*          t,          f$ r d}Y nw xY wt/          dt1          |d                    }| r)t          | t                    r| 
                                st3          |||          S d}t          |t                    r3|
                                rd |                    d          D             }d}t          |t                    r,|
                                                                }|dv r|}t9          || 
                                ||||          S )u  Single-shape tool. Mode inferred from which args are set.

    Discovery: pass ``query``.
    Scroll:    pass ``session_id`` + ``around_message_id``.
    Read:      pass ``session_id`` (no anchor) — dumps the whole session.
    Browse:    pass nothing.

    Pass ``profile`` to read another profile's sessions (e.g. resolving an
    ``@session:<profile>/<id>`` link). Scroll wins over read/discovery when an
    anchor is set — the agent has asked for a specific slice.
    Nr   rL   z(SessionDB unavailable for session_searchTr   )format_session_db_unavailableFrv   /rN   z': )r-   r&   r   r   r   rw   rH   r   r   r   rp   c                 ^    g | ]*}|                                 |                                 +S r?   )rS   )r@   r   s     r$   rd   z"session_search.<locals>.<listcomp>X  s-    LLL1!''))LQWWYYLLLrD   ,)newestoldest)r-   r   r   r   r   r   )rU   rM   r!   r   r    r   r   r   r   	partitionrS   r\   r   r   r   loadsr,   rn   rh   r   r   r   r   r   r   r   splitlowerr   )r   r   r   r-   r   r&   r   r   r   rH   rM   r   emb_profile_emb_id
profile_dbr#   r   resultlocatedownerfoundr   	sort_norm	candidates                            r$   session_searchr     sW   4 
z	N......BB 	N 	N 	NMDtTTTTBBBBBB;;==uMMMMMM	N *c"" &sj'8'8!+!5!5c!:!:Q 	&J &s7||7I7I7K7K%
 s7||1133	J,W55JJ 	J 	J 	J9'99a995IIIIIIIII	J!B!% 	:s## 

(8(8(:(: 
@Q@]!/1
 
 
 	
 *c"" z'7'7'9'9   r3'':f!!),, 	M
 ,C00 
=##>#>??yy## =#(i z%e<<<< eS!! 	JJEE:& 	 	 	EEE	3ub>>""E  D
5#.. Dekkmm D$R0BCCC &*I+s## M(9(9(;(; MLL(9(9#(>(>LLL	  $I$ "JJLL&&((	,,,!Ikkmm-   sK    ?AAC+ +
D5DDD)"H   H6J J'&J'c                  d    	 ddl m}  | j                                        S # t          $ r Y dS w xY w)z#Requires the SQLite state database.r   DEFAULT_DB_PATHF)rU   r  r1   rg   ImportErrorr  s    r$   !check_session_search_requirementsr  k  sP    000000%,,...   uus   ! 
//r   u
  Search past sessions stored in the local session DB, or scroll inside one. FTS5-backed retrieval over the SQLite message store. No LLM calls — every shape returns actual messages from the DB.

FOUR CALLING SHAPES

  1) DISCOVERY — pass `query`:
     session_search(query="auth refactor", limit=3)
     Runs FTS5, dedupes hits by session lineage, returns the top N sessions. Each result carries:
       - session_id, title, when, source
       - snippet: FTS5-highlighted match excerpt
       - bookend_start: first 3 user+assistant messages of the session (the goal / kickoff)
       - messages: ±5 messages around the FTS5 match, with the anchor message flagged (the hit in context)
       - bookend_end: last 3 user+assistant messages of the session (the resolution / decisions)
       - match_message_id, messages_before, messages_after
     Bookends + window together let you reconstruct goal → match → resolution without paying for the whole transcript.

  2) SCROLL — pass `session_id` + `around_message_id`:
     session_search(session_id="...", around_message_id=12345, window=10)
     Returns a window of ±`window` messages centered on the anchor. No FTS5, no bookends — just the slice. Use after a discovery call when you need more context than the ±5 default window.
       - To scroll FORWARD: pass messages[-1].id back as around_message_id.
       - To scroll BACKWARD: pass messages[0].id back as around_message_id.
       - The boundary message appears in both windows — orientation marker.
       - When messages_before or messages_after is < window, you're at the start or end of the session.

  3) READ — pass `session_id` only (no around_message_id):
     session_search(session_id="...", profile="work")
     Dumps the whole session by id (first 20 + last 10 messages when large). This is how you resolve an `@session:<profile>/<id>` link the user dropped into the chat: split the value on `/` into profile + id and call session_search(session_id=id, profile=profile).

  4) BROWSE — no args:
     session_search()
     Returns recent sessions chronologically: titles, previews, timestamps. Use when the user asks "what was I working on" without naming a topic.

FTS5 SYNTAX

  AND is the default — multi-word queries require all terms. Use OR explicitly for broader recall (`alpha OR beta OR gamma`), quoted phrases for exact match (`"docker networking"`), boolean (`python NOT java`), or prefix wildcards (`deploy*`).

WHEN TO USE

  Reach for this on any "what did we do about X" / "where did we leave Y" / "find the session where Z" question — before gh, web search, or filesystem inspection. The session DB carries what was said when; external tools show current world state.objectstringzSearch query (discovery shape). Keywords, phrases, or boolean expressions to find in past sessions. Omit to browse recent sessions. Ignored when session_id + around_message_id are set (scroll shape).)typedescriptionintegeru   Discovery shape only. Max sessions to return (default 3, max 10). Bump to 5–10 when the topic likely spans several sessions and you want to pick the right one to scroll into.)r  r  r_   r   r   uH  Discovery shape only. Temporal bias on top of FTS5 ranking. Omit to keep relevance-only ordering (suitable for exploratory recall — "what do we know about X"). Set 'newest' for recency-shaped questions ("where did we leave X"). Set 'oldest' for origin-shaped questions ("how did X start"). Ignored in scroll and browse shapes.)r  enumr  zScroll shape. Session to read inside. Use the session_id returned from a prior discovery call. Must be paired with around_message_id.zScroll shape. Message id to center the window on. From a discovery result use match_message_id, or any id seen in a prior window. To scroll forward pass the last window message's id; to scroll backward pass the first.zScroll shape only. Messages to return on each side of the anchor (anchor itself always included). Clamped to [1, 20]. Default 5.zOptional. Comma-separated roles to include. Discovery defaults to 'user,assistant' (tool output is usually noise). Pass 'user,assistant,tool' to include tool output (debugging tool behaviour) or 'tool' to search tool output only.zOptional. Read sessions from another Hermes profile's database (read-only). Use when resolving an `@session:<profile>/<id>` link: pass the profile segment here with session_id as the id segment. Omit to use the current profile.)r   r   r   r&   r   r   r   rH   )r  
propertiesrequired)ra   r  
parameters)registryr   c                    t          |                     d          pd|                     d          |                     dd          |                     d          |                     d          |                     dd	          |                     d
          |                     d          |                    d          |                    d          
  
        S )Nr   r   r   r   r   r&   r   r   r   r   rH   r-   r   )
r   r   r   r&   r   r   r   rH   r-   r   )r   r,   )argskws     r$   <lambda>r    s    ~hhw%2HH]++hhw""88L))((#677xx!$$XXf##66$<<66"677      rD   u   🔍)ra   toolsetschemahandlercheck_fnemoji)N)ro   rp   )r   N)
r   Nr   NNNNr   NN)__doc__r   r   typingr   r   r   r   r   r   r   r   r   r%   r2   rG   r\   rn   r   r   r   r   r   boolr  SESSION_SEARCH_SCHEMAtools.registryr  r   registerr?   rD   r$   <module>r     s}   <   3 3 3 3 3 3 3 3 3 3 3 3 3 3 / %UC 56 3    2s s    ,R Rd38n R# R$sTWx. R R R R*_ _ _ _ _.)3 ) ) ) )X.4 .4# .4S .4S .4# .4 .4 .4 .4b(Q (QS (Qc (QS (Q (Q (Q (Q^ "y4 y4y4 y4 	y4
 y4 	y4 y4 y4 y4D #b bb $s)$b 	b
 3-b b 	b b b bL "!y yyy y
 y y y y y y 	y y y yx4     0	f  !&  "A   !!8,)	  !)  "/" " "V   !G  !7 CJ
 J
V [N NkD D P 0 / / / / / / /  	   /
#     rD   