Ë
    ãß²jô©  ã                   óÚ  — d Z ddlZddlZddlZddlZddlmZmZ ddlmZm	Z	m
Z
mZmZ ddlmZ dZdZdZd	Zd
Z ej(                  dej*                  «      ZddddœZdZdZeZddœdefd„Zd„ Zdeeee df   de fd„Z!de de"fd„Z#de	e ef   de	e ef   fd„Z$de fd„Z%de defd „Z&de de'e ef   fd!„Z(de de fd"„Z)d#ee    dee   fd$„Z*d#edee   fd%„Z+d&ee   d'ee   d(ee   defd)„Z,d*ede
e    fd+„Z-d,e
e    de.e    fd-„Z/d.e d/e defd0„Z0de defd1„Z1dee	e ef      fd2„Z2d3ee	e ef      defd4„Z3defd5„Z4	 	 dŠd6e	e ef   d7ee   d8ee   de	e ef   fd9„Z5d‹de d:e de fd;„Z6d<ee    de	e ef   fd=„Z7d>e d?ee    dee	e ef      fd@„Z8d>e dAe dBe9de fdC„Z:dDe	e ef   dEe de
e	e ef      fdF„Z;d<e dGe	e ef   dHe dee	e ef      fdI„Z<	 	 	 dŒd>e dJee
e       dKedLee    dAe dMe dNe d'ee   d(ee   d,ee
e       de fdO„Z=d:e fdP„Z>d�de dQedRedNe de f
dS„Z?dTe d:ee    de fdU„Z@dŠdKedMe dNe de fdV„ZAdWedXedYedefdZ„ZBd[e dMe defd\„ZC	 	 dŽde d^ed_edMe de f
d`„ZD	 d�de fda„ZE	 	 	 	 d�d>e dJe dKedMe de d^ed_edLe d:e dAe dde dee d,ee
e       de fdf„ZFdefdg„ZGdhdidjdkdldmœdndodbdpœdkdqdrgdsdtœdkdcdugdvdcdwœdkdxdmœdkdydmœdzd{dkid|d}œdkd~dmœdnddmœdnd€d]dpœdkd�dmœdkd‚dmœdƒœg d„œd…œZHdd†lImJZJmKZK  eJj˜                  dhdheHd‡„ eGdˆ¬‰«       y)‘u·  Session Search Tool - long-term conversation recall over the SQLite session DB.

Single-shape tool; the mode is inferred from the args: DISCOVERY (``query``;
FTS5 deduped by lineage, adaptive detail hydrates only the top result),
SCROLL (``session_id`` + ``around_message_id``; Â±window around the anchor),
READ (``session_id`` alone; whole session or head/tail), BROWSE (no args).
No LLM calls â€” every shape returns actual DB messages.
é    N)ÚdatetimeÚtimezone)ÚAnyÚDictÚListÚOptionalÚUnion)Ú_BOUNDARY_END_REASONS)ÚkanbanÚsubagentÚtool)ÚcroniÐ  i,  é   z^(\d+)\s*(h|d|w)$i  i€Q i€:	 )ÚhÚdÚw)ÚidÚ
session_idÚroleÚsnippetÚsourceÚmodelÚsession_started)z[CONTEXT COMPACTIONz[CONTEXT SUMMARY]:F©Úwith_excr   c                ó„   — 	  | «       S # t         $ r-}t        j                  |g|r||fz   n|¢­ddiŽ |cY d}~S d}~ww xY w)zS``fn()``, or *default* after debug-logging *msg* (+ the exception when *with_exc*).Úexc_infoTN)Ú	ExceptionÚloggingÚdebug)ÚfnÚdefaultÚmsgr   Úlog_argsÚes         ú>/home/ubuntu/.hermes/hermes-agent/tools/session_search_tool.pyÚ_quietr'   :   sD   € ðÙ‹tˆøÜò Ü�‰�cÐW±˜X¨¨š_¸hÒWÐRVÒWØ�ûðús   ‚	 ‰	?’":´?º?c                 ó    — 	  | «       dfS # t         $ r9}t        j                  |g|¢|‘­ddiŽ dt        |› d|› �d¬«      fcY d}~S d}~ww xY w)u€   ``(fn(), None)``, or ``(None, tool_error_json)`` after an error-level log â€” for DB
    calls whose failure the model must see.Nr   Tz: F©Úsuccess)r   r   ÚerrorÚ
tool_error)r!   Úlog_msgÚerror_prefixr$   r%   s        r&   Ú_loudr/   C   s^   € ðGÙ‹t�TˆzÐøÜò GÜ�‰�gÐ; Ð;¨!Ò;°dÒ;Ø”Z < .°°1°#Ð 6ÀÔFÐFÕFûðGús   ‚ ‹	A”.AÁAÁAÚtsÚreturnc                 óÆ   ‡ — ‰ €yt        ‰ t        «      r2‰ j                  dd«      j                  dd«      j                  «       s‰ S t	        ˆ fd„t        ‰ «      d‰ d¬«      S )	zNUnix timestamp -> readable date; ISO strings pass through; "unknown" for None.Úunknownú.Ú Ú-c                  ó^   •— t        j                  t        ‰ «      «      j                  d«      S )Nz%B %d, %Y at %I:%M %p)r   ÚfromtimestampÚfloatÚstrftime©r0   s   €r&   ú<lambda>z#_format_timestamp.<locals>.<lambda>S   s"   ø€ œ(×0Ñ0´°r³Ó;×DÑDÐE\Ó]€ ó    z!Failed to format timestamp %s: %sTr   )Ú
isinstanceÚstrÚreplaceÚisdigitr'   r;   s   `r&   Ú_format_timestamprB   M   s_   ø€ à	€zØÜ�"”cÔ 2§:¡:¨c°2Ó#6×#>Ñ#>¸sÀBÓ#G×#OÑ#OÔ#QØˆ	ÜÓ]Ô_bÐceÓ_fØ5°rÀDôJð Jr=   r   c                 ó6   ‡ ‡— t        ˆ ˆfd„dd‰d¬«      xs i S )z4``db.get_session`` that degrades to ``{}`` on error.c                  ó&   •— ‰ j                  ‰«      S ©N©Úget_session©Údbr   s   €€r&   r<   z#_get_session_meta.<locals>.<lambda>Y   s   ø€ ˜"Ÿ.™.¨Ó4€ r=   Nzget_session failed for %s: %sTr   )r'   rH   s   ``r&   Ú_get_session_metarJ   W   s'   ù€ äÔ4°dØ1°:ÈôNò TØQSðTr=   Úmetac                 óš   — t        | j                  d«      «      | j                  d«      | j                  d«      | j                  d«      dœS )NÚ
started_atr   r   Útitle)Úwhenr   r   rN   )rB   Úget)rK   s    r&   Ú_session_meta_blockrQ   ]   sB   € Ü% d§h¡h¨|Ó&<Ó=ÈÏÉÐRZÓI[Ø—X‘X˜gÓ&°·±¸'Ó1BñDð Dr=   c                  ó8   — t        j                  ddi| ¥d¬«      S )Nr*   TF)Úensure_ascii)ÚjsonÚdumps)Úpayloads    r&   Ú_okrW   b   s   € Ü�:‰:�y $Ð2¨'Ð2ÀÔGÐGr=   Úcontentc                 ób   — t        | «      xr# | j                  «       j                  t        «      S rE   )ÚboolÚlstripÚ
startswithÚ_COMPACTION_PREFIXES)rX   s    r&   Ú_is_compaction_summaryr^   f   s$   € Ü�‹=ÒN˜WŸ^™^Ó-×8Ñ8Ô9MÓNÐNr=   c                 óà   — t        «       }|d}}|r[||vrW|j                  |«       t        | |«      }|xs |j                  d«      dk(  }|j                  d«      s	 ||fS |d   }|r||vrŒW||fS )zØWalk parent_session_id to the root -> ``(root_id, has_compression_hop)``; the flag
    separates a compression-split lineage (parent summarised away) from a delegation
    lineage (child still visible to the parent).FÚ
end_reasonÚcompressionÚparent_session_id)ÚsetÚaddrJ   rP   )rI   r   ÚvisitedÚcurÚhas_compressionÚss         r&   Ú_resolve_to_parentri   j   s�   € ô ›€GØ% uˆ€CÙ
�#˜WÑ$Ø�‰�CÔÜ˜b #Ó&ˆØ)ÒQ¨Q¯U©U°<Ó-@ÀMÑ-QˆØ�u‰uÐ(Ô)Øà�ÐÐð Ð#Ñ$ˆñ �#˜WÒ$ð �ÐÐr=   c                 ó    — t        | |«      d   S )Nr   )ri   rH   s     r&   Ú_resolve_lineagerk   z   s   € Ü˜b *Ó-¨aÑ0Ð0r=   Úvaluec                 ó<  — | €yt        | «      j                  «       }|syt        j                  |«      x}r_t	        t        j
                  «       «      t	        |j                  d«      «      t        |j                  d«      j                  «          z  z
  S 	 t        j                  |j                  dd«      «      }|j                  € |j                  t        j                  ¬«      }t	        |j!                  «       «      S # t        $ r t        d| ›d�«      d‚w xY w)	a³  Parse an ISO date/datetime OR a relative duration into a UTC unix timestamp.

    ISO: a date-only value (``YYYY-MM-DD``) is midnight UTC on that day (the SQL
    ``before`` predicate is exclusive, so ``before=2026-07-01`` keeps June and drops
    July 1 00:00). Relative: ``"7d"``, ``"24h"``, ``"2w"`` = now minus N
    hours/days/weeks, so ``after="7d"`` is the last week and ``before="7d"`` is
    everything older than a week.
    Né   é   ÚZz+00:00zinvalid time bound: zV (expected ISO date/datetime like 2026-07-01, or a relative duration like 7d, 24h, 2w))Útzinfo)r?   ÚstripÚ_RELATIVE_BOUND_REÚmatchÚintÚtimeÚgroupÚ_RELATIVE_UNIT_SECONDSÚlowerr   Úfromisoformatr@   Ú
ValueErrorrq   r   ÚutcÚ	timestamp)rl   ÚtextÚrelÚparseds       r&   Ú_parse_iso_boundr�   ~   s  € ð €}ØÜˆu‹:×ÑÓ€DÙØÜ ×&Ñ& tÓ,Ð,€sÐ,Ü”4—9‘9“;Ó¤# c§i¡i°£lÓ"3Ô6LÈSÏYÉYÐWXË\×M_ÑM_ÓMaÑ6bÑ"bÑbÐbð[Ü×'Ñ'¨¯©°S¸(Ó(CÓDˆð ‡}�}ÐØ—‘¤x§|¡|�Ó4ˆÜˆv×ÑÓ!Ó"Ð"øô ò [ÜÐ/°¨yð 9Pð Pó QØVZð	[ð[ús   Â%D ÄDc                 ó0  — | �| dk(  st        | t        «      ryt        | t        t        f«      rt        | «      S t	        | «      j                  «       }	 t        t        |«      «      S # t        t        f$ r Y nw xY w	 t        |«      S # t        $ r Y yw xY w)zY``sessions.started_at`` as an int unix timestamp (numeric or ISO text); None if unusable.Nr5   )	r>   rZ   ru   r9   r?   rr   Ú	TypeErrorr{   r�   )rl   r~   s     r&   Ú_coerce_started_tsr„   ˜   s�   € à€}˜ š¤z°%¼Ô'>ØÜ�%œ#œu˜Ô&Ü�5‹zÐÜˆu‹:×ÑÓ€DðÜ”5˜“;ÓÐøÜ”zÐ"ò ÙðúðÜ Ó%Ð%øÜò Ùðús$   ÁA( Á(A:Á9A:Á>
B	 Â		BÂBÚ
started_tsÚafter_tsÚ	before_tsc                 óB   — |€|€y| €y|du xs | |k\  xr |du xs | |k  S )zI``after_ts <= started_ts < before_ts``; an unknown start fails any bound.NTF© )r…   r†   r‡   s      r&   Ú_in_time_windowrŠ   ©   sD   € àÐ˜IÐ-ØØÐØØ˜ÐÒ6 
¨hÑ 6Òi¸YÈ$Ð=NÒ=hÐR\Ð_hÑRhÐir=   Úrawc                 ó   — t        | t        «      r| gn"t        | t        t        f«      rt        | «      ng }g }|D ]P  }t        |t        «      r|j	                  «       nd}|r||vr|j                  |«       t        |«      t        k\  sŒO |S  |S )zYDeduped, stripped session ids from a str or list, capped at ``_EXCLUDE_SESSION_IDS_CAP``.r5   )r>   r?   ÚlistÚtuplerr   ÚappendÚlenÚ_EXCLUDE_SESSION_IDS_CAP)r‹   ÚitemsÚoutÚitemÚsids        r&   Ú_normalize_exclude_session_idsr–   ²   sƒ   € ä ¤SÔ)ˆS‰E¼JÀsÌTÔSXÈMÔ<Z¬t°C¬yÐ`b€EØ€CØò ˆÜ(¨¬sÔ3ˆd�j‰jŒl¸ˆÙ�3˜c‘>Ø�J‰J�sŒOÜˆs‹8Ô/Ó/ØØ€Jðð €Jr=   Úexclude_session_idsc                 óˆ   — t        «       }|D ]2  }|j                  |«       |j                  t        | |«      xs |«       Œ4 |S )zUThe excluded ids plus their lineage roots, so a child id also hides its parent chain.)rc   rd   rk   )rI   r—   Úrootsr•   s       r&   Ú_excluded_lineage_rootsrš   ¿   sC   € ä“e€EØ"ò 4ˆØ�	‰	�#ŒØ�	‰	Ô" 2 sÓ+Ò2¨sÕ3ð4ð €Lr=   ÚaÚbc                 óR   — t        | |«      }t        |xr |t        | |«      k(  «      S rE   )rk   rZ   )rI   r›   rœ   Úa_roots       r&   Ú_same_lineagerŸ   È   s+   € Ü˜b !Ó$€FÜ�Ò<˜6Ô%5°b¸!Ó%<Ñ<Ó=Ð=r=   c                 óf   — |xr t        | |«      j                  d«      xs d}|dk(  xs |t        v S )a>  True when the transcript left everyone's live context: ``compression``
    (summarised into the child) or a fresh reset (child starts empty). Live delegation
    children (``end_reason is None``) and ``branched`` parents (copied verbatim into
    the branch) ARE the current context, so they stay excluded from recall.r`   Nra   )rJ   rP   Ú_FRESH_RESET_END_REASONS)rI   r   r`   s      r&   Ú_session_left_live_contextr¢   Í   s=   € ð
 ÒTÔ!2°2°zÓ!B×!FÑ!FÀ|Ó!TÒ]ÐY]€JØ˜Ñ&ÒP¨*Ô8PÐ*PÐPr=   c                 óT   ‡ ‡— ˆ ˆfd„}‰xr t        |dd‰«      }|rt        |«      S dS )zMOwning session and visibility flags for *message_id* (None if missing/error).c                  óž   •— ‰ j                   5  ‰ j                  j                  d‰f«      j                  «       cd d d «       S # 1 sw Y   y xY w)Nz?SELECT session_id, active, compacted FROM messages WHERE id = ?)Ú_lockÚ_connÚexecuteÚfetchone©rI   Ú
message_ids   €€r&   Ú_lookupz+_get_message_storage_state.<locals>._lookupØ   sH   ø€ Ø�X‰Xñ 	mØ—8‘8×#Ñ#ØQÐT^ÐS`óbßbjÑbjÓbl÷	m÷ 	mò 	mús   Ž+AÁANz*message storage-state lookup failed for %s)r'   Údict)rI   rª   r«   Úrows   ``  r&   Ú_get_message_storage_stater®   Ö   s3   ù€ õmð Ò
hœ ¨Ð/[Ð]gÓh€CÙŒ4�‹9Ð% Ð%r=   Ústatec                 ó2   — | duxr | d   dk(  xr | d   dk(  S )z{Compaction archives are ``active=0, compacted=1``; rewind/undo rows are
    ``active=0, compacted=0`` and must stay hidden.NÚactiver   Ú	compactedrn   r‰   )r¯   s    r&   Ú_is_compacted_stater³   à   s-   € ð ˜ÐÒQ  x¡°AÑ!5ÒQ¸%ÀÑ:LÐPQÑ:QÐQr=   c                 ó,   — t        t        | |«      «      S )zƒTrue for a compaction-archived row: no longer in live context, so discoverable
    even on the current session. False on any error.)r³   r®   r©   s     r&   Ú_is_compacted_messagerµ   æ   s   € ô Ô9¸"¸jÓIÓJÐJr=   ÚmÚ	anchor_idÚmax_content_lenc           	      ó`  — | j                  d«      }t        |t        «      rd|v rddlm}  ||«      }| j                  d«      | j                  d«      || j                  d«      dœ}|j                  d	D �ci c]&  }| j                  |«      sŒ|| j                  |«      “Œ( c}«       |�| j                  d«      |k(  rd|d<   |r3|r1t        |«      |kD  r#|j                  |d
| dz   dt        |«      ¬«       |j                  «       D ��ci c]  \  }}|€|dk(  sŒ||“Œ c}}S c c}w c c}}w )zMSlim a message row; keeps ``content`` even when empty (tool-call-only turns).rX   úr   )Ú
strip_ansir   r   r}   )r   r   rX   r}   )Ú	tool_nameÚ
tool_callsÚtool_call_idNTÚanchoru   â€¦)rX   Úcontent_truncatedÚoriginal_content_chars)rP   r>   r?   Útools.ansi_stripr»   Úupdater�   r’   )r¶   r·   r¸   rX   r»   ÚentryÚkÚvs           r&   Ú_shape_messagerÇ   ì   s  € ð �e‰e�IÓ€GÜ�'œ3Ô F¨gÑ$5Ý/Ù˜WÓ%ˆØ—5‘5˜“;¨¯©¨f«À'ÐXY×X]ÑX]Ð^iÓXjÑk€EØ	‡L�LÐ'RÖ_ !ÐVW×V[ÑV[Ð\]ÕV^�!�Q—U‘U˜1“X‘+Ò_Ô`ØÐ §¡ t£°	Ò!9Øˆˆh‰Ù™7¤s¨7£|°oÒ'EØ�‰˜WÐ%5 oÐ6¸Ñ>ÐRVÜ,/°«Lð 	ô 	:à"Ÿ[™[›]×N‘T�Q˜¨a¨m¸qÀI»~ˆAˆq‰DÓNÐNùò `ùó Os   Á7D%ÂD%ÄD*ÄD*Úprofilec                 ól   — d„ }|xs dj                  «       xs t        |dd«      }|rd|› d| › �S d| › �S )u÷   The reference the agent writes for a session â€” same value the desktop composer
    emits, so it renders as a titled link. The profile segment is omitted when it
    can't be named confidently (a bare id still resolves, just not across profiles).c                  ó.   — ddl m}   | «       }|dk(  rdS |S )Nr   )Úget_active_profile_nameÚcustomr5   )Úhermes_cli.profilesrË   )rË   Úresolveds     r&   Ú_activez_session_link.<locals>._active  s   € Ý?Ù*Ó,ˆØ Ò)ˆrÐ7¨xÐ7r=   r5   z/get_active_profile_name failed for session linkz	@session:ú/)rr   r'   )r   rÈ   rÏ   Únames       r&   Ú_session_linkrÒ   ý   sK   € ò8ð ŠM�r× Ñ Ó"Òl¤f¨W°bÐ:kÓ&l€DÙ.2ˆY�t�f˜A˜j˜\Ð*ÐP¸)ÀJÀ<Ð8PÐPr=   Úlineage_rootc                 óT   — dD �ci c]  }|||   “Œ
 }}| r| |d   k7  r| |d<   |S c c}w )zMCanonical key order; ``parent_session_id`` set when the hit lives in a child.©r   rO   r   r   rN   Úmatched_roleÚmatch_message_idr   Úbookend_startÚmessagesÚbookend_endÚmessages_beforeÚmessages_afterÚdetailr   rb   r‰   )rÓ   ÚfieldsrÅ   rÄ   s       r&   Ú_discovery_entryrß   	  sP   € ð$cö d˜aˆQ��q‘	‰\ð d€Eð dñ ˜¨¨lÑ(;Ò;Ø%1ˆÐ!Ñ"Ø€Lùòds   …%ÚqueryÚcurrent_lineage_rootc                 ó@  ‡ ‡‡‡	‡
‡— |j                  «       j                  d«      Š
‰
xr t        ˆ ˆ
fd„dd‰
«      Š	‰	syt        ‰ ‰	«      Š|r‰|k(  rt        ‰ ‰	«      syt        ˆ ˆˆ	fd„dd‰	«      xs i }|j	                  d«      t
        v ryt        ˆ ˆ	fd„g d	‰	«      }|r|d
   j	                  d«      ndŠ‰€i nt        ˆˆ ˆ	fd„i d‰	‰«      Š|j	                  d«      xs ‰
}d"ˆfd„	}i t        ‰‰	t        |j	                  d«      «      |j	                  dd«      |j	                  d«      xs d|d‰d|› � |d|dd «       |d|dd ‰d¬«       |d|dd «      ‰j	                  dd
«      ‰j	                  dt        t        |«      dz
  d
«      «      d¬ «      ¥d!‰i¥S )#zJDiscovery-shaped result when the query matches a session title, else None.z`'"c                  ó&   •— ‰ j                  ‰«      S rE   )Úresolve_session_by_title)rI   Útitle_querys   €€r&   r<   z%_title_match_result.<locals>.<lambda>  s   ø€ °×0KÑ0KÈKÓ0X€ r=   Nz&resolve_session_by_title failed for %rc                  óL   •— ‰ j                  ‰«      xs ‰ j                  ‰«      S rE   rF   )rI   rÓ   r   s   €€€r&   r<   z%_title_match_result.<locals>.<lambda>  s   ø€  "§.¡.°Ó">Ò"\À"Ç.Á.ÐQ[ÓB\€ r=   z%get_session failed for title match %sr   c                  ó&   •— ‰ j                  ‰«      S rE   ©Úget_messagesrH   s   €€r&   r<   z%_title_match_result.<locals>.<lambda>#  ó   ø€ ˜bŸo™o¨jÓ9€ r=   z&get_messages failed for title match %sr   r   c                  ó.   •— ‰j                  ‰‰ dd¬«      S )Né   é   ©ÚwindowÚbookend)Úget_anchored_view)r·   rI   r   s   €€€r&   r<   z%_title_match_result.<locals>.<lambda>&  s   ø€ �×$Ñ$ Z°À1ÈaÐ$ÓP€ r=   z.get_anchored_view failed for title match %s/%srN   c                 ól   •— ‰j                  | «      xs |D �cg c]  }t        |||¬«      ‘Œ c}S c c}w )N©r·   r¸   )rP   rÇ   )ÚkeyÚfallbackr¿   r¸   r¶   Úviews        €r&   Úshapez"_title_match_result.<locals>.shape*  s9   ø€ àŸ(™( 3›-Ò3¨8ö6Øô ˜q¨FÀOÖTò 6ð 	6ùò 6s   ™1rM   r3   r   Úsession_titlezSession title matched: rØ   rí   rï   rì   é   ©r¸   rÚ   éýÿÿÿrÛ   rÜ   ÚfullrÕ   Ú_lineage_root)Né°  )
rr   r'   rk   r¢   rP   Ú_HIDDEN_SESSION_SOURCESrß   rB   Úmaxr�   )rI   rà   rá   Úsession_metarÙ   rN   r÷   r·   rÓ   r   rå   rö   s   `      @@@@@r&   Ú_title_match_resultr    sÙ  ý€ à—+‘+“-×%Ñ% fÓ-€KØò _¤Ô(XÐZ^Ø(PÐR]ó"_€JáØÜ# B¨
Ó3€Lñ  Ð0DÒ DÔMgÐhjÐlvÔMwØÜÕ\Ð^bØAÀ:óOò UØRTð à×Ñ˜Ó!Ô%<Ñ<ØÜÔ9¸2Ð?gÐisÓt€HÙ)1�˜‘—‘ Ô%°t€IØÐ"‰2¬ÝPÐRTØ8¸*Àió)Q€Dð ×Ñ˜WÓ%Ò4¨€Eõ6ð	'ÔØ Ô2CÀL×DTÑDTÐUaÓDbÓ2cØ×Ñ ¨)Ó4¸L×<LÑ<LÈWÓ<UÒ<bÐYbØ /ÀIØ)¨%¨Ð1Ù˜O¨X°b°q¨\Ó:Ù�x ¨"¨1 ¨yÈ$ÔOÙ˜-¨°"°#¨Ó7ÈÏÉÐRcÐefÓIgØ—x‘xÐ 0´#´c¸(³mÀaÑ6GÈÓ2KÓLÐU[ô]ð 	'ð 	˜ñ	'ð 	'r=   rÝ   Úresultsc           
      ó®   — t        | j                  dd«      }|€i nd|d   d|d   › d|d   d›d	|d
   d›d�dœi}t        dd|||t        |«      dœ|¤|¤ŽS )zƒDiscovery response; notes FTS backfill progress so the agent can explain thin
    results instead of treating them as ground truth.Nzfts_rebuild_status failedÚindex_rebuildÚpercentz2The search index is rebuilding in the background (z% done, Úindexedú,z of ÚtotalzL messages). Results from older messages may be incomplete until it finishes.)r  ÚnoteÚdiscover)Úmoderà   rÝ   r  Úcountr‰   )r'   Úfts_rebuild_statusrW   r�   )rI   rà   rÝ   r  ÚextraÚstatusÚrebuilds          r&   Ú_discover_payloadr  9  s“   € ô �B×)Ñ)¨4Ð1LÓM€FØ�N‰b¨ÀfÈYÑFWØ
<¸VÀIÑ=NÐ<OÈxØ�)Ñ˜QÐ
˜t F¨7¡O°AÐ#6ð 7/ð	0ñ;2ð )3€Gô Ðt�J e°FÀGÔSVÐW^ÓS_ÑtÐchÐtÐlsÑtÐtr=   rö   rô   c           	      óž   — | j                  |«      xs g D �cg c]*  }t        |j                  dd«      «      st        |d¬«      ‘Œ, c}S c c}w )NrX   r5   rþ   rú   )rP   r^   rÇ   )rö   rô   r¶   s      r&   Ú_bookendr  D  sL   € Ø>B¿h¹hÀs»mÒ>QÈrö A¸Ü)¨!¯%©%°	¸2Ó*>Ô?ô ˜1¨dÖ3ò Að Aùò As   ˜/A
Ú
match_infoÚresult_detailc                 óŠ  — |j                  d«      xs ||j                  d«      }}	 | j                  ||dd¬«      }t        | |«      |d
k(  }	}t        ||t        |j                  d«      xs |j                  d«      «      |j                  d«      xs |j                  dd«      |j                  d«      xs |j                  d«      xs d|j                  d«      xs d	|j                  d«      ||j                  d«      xs d|	rt        |d«      ng |j                  d«      xs g D �
cg c]&  }
|	s|
j                  d«      |k(  rt        |
|d¬«      ‘Œ( c}
|	rt        |d«      ng |j                  dd«      |j                  dd«      |¬«      S # t        $ r$}t        j                  d|||d¬«       Y d	}~y	d	}~ww xY wc c}
w )zQDiscovery result from a surviving FTS row; None (dropped) if the view can't load.r   r   rì   rí   rî   z&get_anchored_view failed for %s/%s: %sT)r   Nrü   rM   r   r   r3   r   rN   r   r   r5   rØ   rï   rù   ró   rÚ   rÛ   r   rÜ   rÕ   )
rP   rñ   r   r   ÚwarningrJ   rß   rB   r  rÇ   )rI   rÓ   r  r  Úhit_sidÚmsg_idrö   r%   r  rü   r¶   s              r&   Ú_hydrate_hitr  I  s«  € à —n‘n \Ó2ÒB°lÀJÇNÁNÐSWÓDXˆV€GðØ×#Ñ# G¨V¸AÀqÐ#ÓIˆô +¨2¨|Ó<¸mÈvÑ>U�$€LÜØ Ü˜|×/Ñ/°Ó=ÒbÀÇÁÐPaÓAbÓcØ×Ñ Ó)ÒP¨Z¯^©^¸HÀiÓ-PØ×Ñ˜wÓ'ÒO¨:¯>©>¸'Ó+BÒOÀiØ×Ñ˜wÓ'Ò/¨4¸j¿n¹nÈVÓ>TØ¨¯©¸	Ó)BÒ)HÀbÙ9=”h˜t _Ô5À2à ŸH™H XÓ.Ò4°"öXØ¹ÀÇÁÀtÃÐPVÒAVô ! ¨fÀdÖKò Xá59”H˜T =Ô1¸rØŸ™Ð!2°AÓ6ÀtÇxÁxÐP`ÐbcÓGdØôð øô	 ò Ü�‰Ð@À'È6ÐSTÐ_cÕdÜûðüòXs   ¨F Ä++G Æ	F=ÆF8Æ8F=Úrole_filterÚlimitÚsortÚcurrent_session_idÚlink_profilec           	      ó  ‡ ‡‡‡‡‡	— |rt        ‰ |«      nd}t        ‰ |
xs g «      }t        ‰ ‰|«      }|re|d   |j                  d«      xs |d   }}t	        t        ‰ |«      xs t        ‰ |«      j                  d«      «      }||h|z  st        |‰‰	«      sd}t        ˆˆ	ˆ ˆˆˆfd„dd«      \  }}|r|S t        |d„ ¬	«      }|s|st        ‰ ‰|g d
¬«      S i }|r|gng }|r|j                  dd«      x}rddi||<   |D ]‚  }t        |«      |k\  r nr|d   t        ‰ |d   «      }}||v s||v rŒ0t        ‰ |j                  d«      «      }|r||k(  rt        ‰ |«      s|sŒa|r||k(  r|sŒk|j                  |i |¥d|i¥«       Œ„ |j                  «       D ]B  \  }}|j                  d«      rŒt!        ‰ |||dk(  s|sdnd«      }|€Œ2|j#                  |«       ŒD |D ]  }t%        |d   |«      |d<   Œ t        ‰ ‰||t        |«      d¬«      S )z=Discovery shape: FTS5 plus adaptive or full result hydration.Nr   rý   rM   c                  ón   •— ‰j                  ‰‰xs ddgt        t        «      t        d‰t        ‰ ‰¬«	      S )NÚuserÚ	assistantr   )	rà   r  Úexclude_sourcesr  Úoffsetr  rÞ   r†   r‡   )Úsearch_messagesr�   rÿ   Ú_DISCOVER_SCAN_LIMITÚ_DISCOVER_SEARCH_FIELDS)r†   r‡   rI   rà   r  r  s   €€€€€€r&   r<   z_discover.<locals>.<lambda>p  s@   ø€  R×%7Ñ%7Ø Ò!E°¸Ð0EÜÔ4Ó5Ô=QÐZ[ÐbfÜ&°ÀYð &8ó &P€ r=   zFTS5 search failed: %szSearch failedc                 ó8   — | j                  d«      xs dt        v S )Nr   r5   )rP   Ú_DEMOTED_SESSION_SOURCES)Úrs    r&   r<   z_discover.<locals>.<lambda>y  s   € °Q·U±U¸8³_Ò5JÈÔOgÐ4g€ r=   )rô   u´   No matching sessions found. FTS5 ANDs all terms by default â€” broaden with OR (`alpha OR beta`), exact-match with quoted phrases, exclude with NOT, or prefix-match with `deploy*`.)ÚmessageÚ_title_onlyTr   rü   ÚcompactÚlinku=  When referring the user to a session, write its `link` value verbatim inline mid-sentence (it renders as a titled link) â€” never as markdown, in backticks, on its own line, or next to the title/id/date. To read more around a compact result, scroll: session_search(session_id=..., around_message_id=match_message_id).)Úsessions_searchedÚ	link_hint)rk   rš   r  rP   r„   rJ   rŠ   r/   Úsortedr  Úpopr�   rµ   r¢   Ú
setdefaultr’   r  r�   rÒ   )rI   rà   r  r  r  rÝ   r  r   r†   r‡   r—   rá   Úexcluded_rootsÚtitle_resultÚ	title_sidÚ
title_rootÚtitle_startedÚraw_resultsÚerrÚseen_sessionsr  Útitle_lineager,  Úraw_sidÚresolved_sidÚis_compacted_hitrÓ   r  rÄ   s   ``` `   ``                   r&   Ú	_discoverrB  a  s¯  ý€ ñ
 HZÔ+¨BÐ0BÔCÐ_cÐÜ,¨RÐ1DÒ1JÈÓK€NÜ& r¨5Ð2FÓG€Lñ Ø ,¨\Ñ :¸L×<LÑ<LÈ_Ó<]Ò<{ÐamÐnzÑa{�:ˆ	Ü*Ô,=¸bÀ*Ó,MÒ,qÔQbÐceÐgpÓQq×+vÑ+vð  xDó  ,Eó  FˆØ�zÐ" ^Ò3¼?È=ÐZbÐdmÔ;nØˆLÜ÷ Pð Pð RjÐkzó|Ñ€K�ñ Øˆ
ô ˜Ñ*gÔh€Ká™|Ü   U¨F°BðIôKð 	Kð 02€MÙ ,ˆ|‰n°"€GÙ¨,×*:Ñ*:¸?ÈDÓ*QÐQ˜ÐQØ(5°tÐ'<ˆ�mÑ$ð ò UˆÜˆ}Ó Ò&ÙØ ! ,¡Ô1AÀ"ÀaÈÁoÓ1V�ˆØ�nÑ$¨¸Ñ(FØô 1°°Q·U±U¸4³[ÓAÐÙ LÐ4HÒ$HÜ*¨2¨wÔ7Ñ;KØÙ 'Ð-?Ò"?ÑHXØØ× Ñ  Ð/S°!Ð/S°_ÀlÑ/SÕTð-Uð. %2×$7Ñ$7Ó$9ò "Ñ ˆ�jØ�>‰>˜-Ô(Øä˜R ¨zÀVÈvÒEUÑ]d¹6ÐjsÓtˆØÑØ�N‰N˜5Õ!ð"ð ò IˆÜ% e¨LÑ&9¸<ÓHˆˆfŠðIä˜R ¨°Ì3È}ÓK]ð	NôPð Pr=   c                 ó  — | �t        | «      j                  «       syddlm} ddlm} |j                  | «      }|j                  |«       |j                  |«      st        d|› d�«      ‚ ||j                  |«      dz  d¬	«      S )
zTAnother profile's ``state.db`` opened read-only (safe on a live DB); None = current.Nr   )Úprofiles)Ú	SessionDBú	profile 'z' does not existzstate.dbT)Údb_pathÚ	read_only)r?   rr   Ú
hermes_clirD  Úhermes_staterE  Únormalize_profile_nameÚvalidate_profile_nameÚprofile_existsr{   Úget_profile_dir)rÈ   Úprofiles_modrE  Úcanons       r&   Ú_resolve_profile_dbrQ  ±  s~   € à€œc '›l×0Ñ0Ô2ØÝ3Ý&Ø×/Ñ/°Ó8€EØ×&Ñ& uÔ-Ø×&Ñ& uÔ-Ü˜9 U GÐ+;Ð<Ó=Ð=Ù˜\×9Ñ9¸%Ó@À:ÑMÐY]Ô^Ð^r=   ÚheadÚtailc                 ó~  ‡ ‡— t        ‰ ‰«      }|st        d‰› �d¬«      S t        ˆ ˆfd„dd‰«      \  }}|r|S |D �cg c]  }t        |t        ¬«      ‘Œ }	}t        |	«      t        |	«      ||z   kD  }}
t        dd‰t        ‰|«      t        |«      |
||r|	d	| |	| d	 z   n|	d
œ|rdd|
› d|› d|› d�i¤ŽS i ¤ŽS c c}w )zQRead shape: whole session, or ``head`` + ``tail`` messages with a scroll pointer.úsession_id not found: Fr)   c                  ó&   •— ‰ j                  ‰«      S rE   rè   rH   s   €€r&   r<   z_read_session.<locals>.<lambda>Ã  rê   r=   zget_messages failed for %s: %szfailed to load sessionrú   ÚreadN)r  r   r0  r  Úmessage_countÚ	truncatedrÙ   r-  zSession has z messages; showing first z + last z=. Pass around_message_id (any id above) to scroll the middle.r‰   )	rJ   r,   r/   rÇ   Ú_READ_MAX_CONTENTr�   rW   rÒ   rQ   )rI   r   rR  rS  r   rK   Úrowsr<  r¶   Úshapedr	  rY  s   ``          r&   Ú_read_sessionr]  ¾  s  ù€ ä˜R Ó,€DÙÜÐ2°:°,Ð?ÈÔOÐOÜÔ9Ð;[Ð]uØ ó"�I€Dˆ#á
Øˆ
ØLPÖQÀqŒn˜QÔ0AÖBÐQ€FÐQÜ˜6“{¤C¨£K°$¸±+Ñ$=ˆ9€EÜð v�F z¼ÀjÐR^Ó8_Ü/°Ó5ÀUÐV_Ù:C˜˜u ˜¨°¨u¨v¨Ò6Èñvñ clð  ¨e¨WÐ4MÈdÈVÐS[Ð\`Ð[að b]ð  ]ð _ñvð vð rtñ	vð vùò Rs   ¾B:r•   c                 ó”   — t        | ||¬«      }t        j                  |«      j                  d«      dus|r|S t	        d|› d�d¬«      S )u�  Read shape scoped to ONE store: the caller's profile, or the profile it named.

    A miss is a miss. Profiles are isolated islands, so a bare id never falls through to
    a scan of every other profile's ``state.db`` â€” that returned another profile's full
    transcript to any caller holding the id (#106761). The hint tells the model how to
    ask properly: ``@session:<profile>/<id>`` or ``profile=``.
    ©r   r*   Fz&session_id not found in this profile: z^. If it belongs to another profile, pass profile=<name> (or the @session:<profile>/<id> link).r)   )r]  rT   ÚloadsrP   r,   )rI   r•   rÈ   Úresults       r&   Ú_read_scopedrb  Ð  s[   € ô ˜2˜s°Ô9€FÜ‡z�z�&Ó×Ñ˜iÓ(°Ñ5¹ØˆÜÐ>¸s¸eð D\ð \Øejôlð lr=   c                 óF   ‡ ‡‡‡— ˆˆ ˆˆfd„}t        |dd«      \  }}|xs |S )zFBrowse shape: metadata for the most recent sessions (no LLM, no FTS5).c                  óŽ  •— t        ‰
dd «      } | €t        d«      ‚ | ‰dz   t        t        «      d¬«      }‰	rt	        ‰
‰	«      nd\  }}‰	|r|r|nd h}|D �cg c]  }|j                  dd«      |vsŒ|‘Œ c}d ‰ D ��cg c]‹  }|j                  dd«      t        |j                  dd«      ‰«      |j                  d	«      xs d d
œdD �ci c]  }||j                  |d«      “Œ c}¥|j                  dd«      |j                  dd«      dœ¥‘Œ� }}}t        d|t        |«      dt        |«      › d�¬«      S c c}w c c}w c c}}w )NÚlist_recent_sessions_boundedz?session database does not support bounded recent-session browseé   g      @)r  r%  Útimeout_seconds)NFr   r5   rN   )r   r0  rN   )r   rM   Úlast_activerX  r   Úpreview)rX  ri  ÚbrowsezShowing zZ most recent sessions. Pass a query= to search, or session_id+around_message_id to scroll.)r  r  r  r-  )	ÚgetattrÚRuntimeErrorr�   rÿ   ri   rP   rÒ   rW   r�   )Úbounded_listÚsessionsÚcurrent_rootÚhas_compression_hopÚhiddenÚxrh   rÅ   r  r  rI   r  r   s            €€€€r&   Ú_browsez&_list_recent_sessions.<locals>._browseá  sv  ø€ ô ˜rÐ#AÀ4ÓHˆØÐÜÐ`ÓaÐaÙØ˜"‘*Ü Ô!8Ó9È3ôPˆñ ;MÔ˜rÐ#5Ô6ÐR_ñ 	*ˆÐ)ð %Ñ6IÉl¡lÐ`dÐeˆð
 "*ÖK˜A¨Q¯U©U°4¸«_ÀFÒ-J’aÒKÈFÈUÐS÷	Uð ð Ÿ%™%  b›/´=ÀÇÁÀtÈRÃÐR^Ó3_Ø—U‘U˜7“^Ò+ tñYàIpÖ/qÀA°°1·5±5¸¸B³<±Ò/qðYð ŸU™U ?°AÓ6À1Ç5Á5ÈÐTVÓCWôYð Uˆñ Uô
 ˜¨'¼¸W»Ø”s˜7“|�nð %9ð 9ô;ð 	;ùò Lùò 0rùóUs%   ÁD7Á2D7Á?AEÃ
D<Ã$+EÄ<Ez!Error listing recent sessions: %szFailed to list recent sessions)r/   )rI   r  r  r   rs  r“   r<  s   ````   r&   Ú_list_recent_sessionsrt  ß  s(   û€ ÷;ô6 �WÐAÐCcÓd�H€CˆØŠ:�#Ðr=   r"   ÚloÚhic                 ót   — 	 t        | «      } t        |t	        | |«      «      S # t        t        f$ r |} Y Œ)w xY wrE   )ru   rƒ   r{   r   Úmin)rl   r"   ru  rv  s       r&   Ú
_clamp_intry     s@   € ðÜ�E“
ˆô ˆr”3�u˜b“>Ó"Ð"øô ”zÐ"ò ØŠðús   ‚# £7¶7Ú
anchor_sidc                 ón   — t        | ||«      rt        |«      ry|duxr |d   dk(  xs t        | |«       S )aO  True when the scroll anchor is still in the caller's active context (reject).
    Same-lineage history that LEFT live context (compacted rows, compression-ended
    parents, /new-reset predecessors) passes, so scroll never rejects a discovery result.
    Rewind/undo rows (active=0, compacted!=1) never count as out-of-context history.FNr±   r   )rŸ   r³   r¢   )rI   Úanchor_staterz  r  s       r&   Ú_anchor_in_live_contextr}    sI   € ô
 ˜˜ZÐ);Ô<Ô@SÐT`Ô@aØØ Ð$ÒD¨°hÑ)?À1Ñ)DÒwÔMgÐhjÐlvÓMwÐIwÐwr=   rì   Úaround_message_idrï   c                 óZ  ‡ ‡‡‡‡— 	 t        ‰«      Št	        ‰ddd«      Št        ‰ ‰«      }|xs i j                  d«      Š|rt        ‰ |‰xs ‰|«      rt        dd¬«      S t        ‰ ‰«      }|st        d	‰› �d¬«      S t        ˆˆ ˆˆfd
„dd«      \  }}|r|S |j                  d«      xs g }	i }
|	sg‰re‰‰k7  r`t        ‰ ‰‰«      xr t        ˆˆ ˆˆfd„ddd¬«      }|r:|j                  d«      r)d‰› d‰› d‰› d�|
d<   ||d   ‰c}}	Št        ‰ ‰«      xs |}|	st        d‰› d‰› �d¬«      S t        d d‰‰t        |«      ‰|	D �cg c]  }t        |‰¬«      ‘Œ c}|j                  dd«      |j                  dd«      ddœ	|
¤ŽS # t        t        f$ r t        dd¬«      cY S w xY wc c}w )!zDScroll shape: a window centered on an anchor (no FTS5, no bookends).z)scroll requires integer around_message_idFr)   rì   rn   r   r   z]scroll rejected: anchor lives in the current session lineage (already in your active context)rU  c                  ó,   •— ‰j                  ‰‰ ‰¬«      S ©N)rï   ©Úget_messages_around)r~  rI   r   rï   s   €€€€r&   r<   z_scroll.<locals>.<lambda>"  s   ø€ ˜b×4Ñ4°ZÐARÐ[aÐ4Ób€ r=   zget_messages_around failed: %szfailed to load messagesrï   c                  ó,   •— ‰j                  ‰‰ ‰¬«      S r�  r‚  )r~  rI   Úowningrï   s   €€€€r&   r<   z_scroll.<locals>.<lambda>,  s   ø€ �B×*Ñ*¨6Ð3DÈVÐ*ÓT€ r=   Nz%rebind get_messages_around failed: %sTr   zaround_message_id z
 lives in z (child of z); rebound transparentlyr  z not in session_id Úscroll)r·   rÛ   r   rÜ   zóScroll forward: re-call with around_message_id = the LAST message's id; backward: the FIRST message's id (the boundary message repeats as an orientation marker). messages_before/messages_after < window means you've hit that end of the session.)	r  r   r~  r  rï   rÙ   rÛ   rÜ   Úhintr‰   )ru   rƒ   r{   r,   ry  r®   rP   r}  rJ   r/   rŸ   r'   rW   rQ   rÇ   )rI   r   r~  rï   r  r|  r  rö   r<  rÙ   r  Úrebind_viewr¶   r…  s   ````         @r&   Ú_scrollr‰    s*  ü€ ðVÜÐ 1Ó2Ðô ˜  1 bÓ)€Fä-¨bÐ2CÓD€LØÒ ˜b×%Ñ% lÓ3€FÙÔ5°b¸,ÈÒH\ÐR\Ð^pÔqÜÐyð  DIô  Jð  	JÜ$ R¨Ó4€LÙÜÐ2°:°,Ð?ÈÔOÐOÜÖbØ6Ð8QóS�I€Dˆ#á
Øˆ
Ø�x‰x˜Ó!Ò' R€HØ€EÙ™ 6¨ZÒ#7ô $ B¨
°FÓ;ò JÄÞTØÐ9ÀDôAJˆñ ˜;Ÿ?™?¨8Ô4Ø"4Ð5FÐ4GÀzÐRXÐQYð Z-Ø-7¨LÐ8Pð!RˆE�)Ñà)4°kÀ(Ñ6KÈVÐ&ˆD�(˜JÜ,¨R°Ó8ÒH¸LˆLÙÜÐ.Ð/@Ð.AÐATÐU_ÐT`ÐaÐkpÔqÐqÜð EØ *Ð@QÜ(¨Ó6¸vØJRÖSÀQ”. Ð.?Ö@ÒSØŸ™Ð!2°AÓ6ÀtÇxÁxÐP`ÐbcÓGdð:ñEð ?DñEð Eøô= ”zÐ"ò VÜÐEÈuÔUÒUðVüòB Ts   ‡F ÅF(ÆF%Æ$F%c                 óÄ  — t        |t        «      r<d|v r8|j                  d«      \  }}}|r!|}|r|	�t        |	«      j                  «       s|}		 t	        |	«      }|�|d}}|j                  |«       t        |t        «      rJ|j                  «       r:|�t        ||j                  «       |||«      S t        ||j                  «       |	«      S t        |ddd	«      }| r t        | t        «      r| j                  «       st        ||||	¬
«      S t        |t        «      r|j                  «       j                  «       nd}	 t        |«      t        |«      }}t        || j                  «       ||dv r|ndt        |t        «      rB|j!                  d«      D �cg c]#  }|j                  «       sŒ|j                  «       ‘Œ% c}xs dndt        |
t        «      r#|
j                  «       j                  «       dk(  rdnd||	||t#        |«      ¬«      S # t
        $ r}t        d|	› d|› �d¬«      cY d}~S d}~ww xY w# t        $ r }t        t        |«      d¬«      cY d}~S d}~ww xY wc c}w )z™Mode dispatch (see module docstring); scroll wins when an anchor is set.
    Profile DBs opened here are appended to *owned_dbs* for the caller to close.rÐ   NrF  z': Fr)   rí   rn   é
   r_  )ÚnewestÚoldestr  rü   Úadaptive)rI   rà   r  r  r  rÝ   r  r   r†   r‡   r—   )r>   r?   Ú	partitionrr   rQ  r   r,   r�   r‰  rb  ry  rt  ry   r�   r{   rB  Úsplitr–   )rà   r  r  rI   r  r   r~  rï   r  rÈ   rÝ   Ú	owned_dbsÚafterÚbeforer—   Úemb_profileÚ_Úemb_idÚ
profile_dbr%   Ú	sort_normr†   r‡   r,  s                           r&   Ú	_dispatchr™  @  s6  € ô �*œcÔ" s¨jÑ'8Ø!+×!5Ñ!5°cÓ!:Ñˆ�Q˜ÙØˆJÙ  ´s¸7³|×7IÑ7IÔ7KØ%�ðFÜ(¨Ó1ˆ
ð ÐØ!+¨TÐˆØ×Ñ˜Ô$Ü�*œcÔ" z×'7Ñ'7Ô'9ØÐ(Ü˜2˜z×/Ñ/Ó1Ð3DÀfÐN`ÓaÐaÜ˜B 
× 0Ñ 0Ó 2°GÓ<Ð<Ü�u˜a  BÓ'€EÙœ
 5¬#Ô.°e·k±k´mÜ$ R¨Ð0BÐQXÔYÐYÜ(2°4¼Ô(=�—
‘
“×"Ñ"Ô$À4€Ið1Ü.¨uÓ5Ô7GÈÓ7O�)ˆô Ø�U—[‘[“]¨%À9ÐPdÑCd±iÐjnÜZdÐepÔruÔZv¨×):Ñ):¸3Ó)?ÖM AÀ1Ç7Á7Å9�a—g‘g•iÒMÒUÑQUð  }AÜ# F¬CÔ0°V·\±\³^×5IÑ5IÓ5KÈvÒ5U‰vÐ[eØ-¸GÈhÐbkÜ:Ð;NÓOôQð Qøô# ò FÜ˜I g Y¨c°!°Ð5¸uÔEÕEûðFûô ò 1Üœ#˜a›&¨%Ô0Õ0ûð1üò NsH   ÁH Ä:H1 ÆIÆ&IÈ	H.ÈH)È#H.È)H.È1	IÈ:IÉIÉIrí   rŽ  r’  r“  c                 óR  ‡‡— ddl m} ddlm}mŠ g }|€2t        |dd«      }|€t         |«       d¬«      S |j                  |«       	 t        | |||||||||	|
||||¬«      t        |«      D ]  Št        ˆˆfd	„dd
«       Œ S # t        |«      D ]  Št        ˆˆfd	„dd
«       Œ w xY w)zŠRun session search, closing DBs opened here. Positional order is frozen for old callers;
    new parameters are appended after ``detail``.r   )Úformat_session_db_unavailable)ÚacquireÚrelease_or_closeNz(SessionDB unavailable for session_searchFr)   )r’  r“  r—   c                  ó   •—  ‰‰ «      S rE   r‰   )Úowned_dbr�  s   €€r&   r<   z session_search.<locals>.<lambda>~  s   ø€ Ñ+¨HÓ5€ r=   z(Failed to close session_search SessionDB)
rJ  r›  Úhermes_state_registryrœ  r�  r'   r,   r�   r™  Úreversed)rà   r  r  rI   r  r   r~  rï   r  rÈ   rÝ   r’  r“  r—   r›  rœ  r‘  rŸ  r�  s                    @@r&   Úsession_searchr¢  j  sÄ   ù€ õ ;ß?Ø€IØ	€zÜ�G˜TÐ#MÓNˆØˆ:ÜÑ;Ó=ÀuÔMÐMØ×Ñ˜ÔðiÜ˜ ¨U°BÐ8JÈJØ*¨F°D¸'À6È9Ø$¨VÐI\ô^ô ! Ó+ò 	iˆHÜÔ5°tÐ=gÕhñ	iøœ Ó+ò 	iˆHÜÔ5°tÐ=gÕhñ	iús   ÁB Â#B&c                  ól   — 	 ddl m}   | «       j                  j                  «       S # t        $ r Y yw xY w)z#Requires the SQLite state database.r   ©Ú_default_db_pathF)rJ  r¥  ÚparentÚexistsÚImportErrorr¤  s    r&   Ú!check_session_search_requirementsr©  �  s4   € ðÝ1ÙÓ!×(Ñ(×/Ñ/Ó1Ð1øÜò Ùðús   ‚$' §	3²3r¢  u[  Recall past conversations: search or read old Hermes sessions (FTS5), or scroll inside one. Four shapes, picked by args: `query` = discovery (top-N matching sessions, top result fully hydrated); `session_id` + `around_message_id` = scroll (window of messages around an anchor); `session_id` alone = read a whole session â€” how you resolve an `@session:<profile>/<id>` link (split on '/' into profile + id); no args = browse recent sessions. Results are actual DB messages, no LLM. Searches conversation history ONLY â€” when the user gave a direct source (URL, file, contact, live system), inspect that first; never conclude 'not found' from history alone. Use for questions about past conversations: 'what did we do about X', 'where did we leave Y'. When referring the user to a session, write its `link` value verbatim inline (it renders as a titled link).ÚobjectÚstringzÇSearch 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�  z®Discovery shape only. Temporal bias on top of FTS5 ranking: omit for relevance-only (exploratory recall), 'newest' for "where did we leave X", 'oldest' for "how did X start".)r¬  Úenumr­  rü   zÞDiscovery shape only. 'adaptive' (default) fully hydrates the top-ranked result and returns only the exact anchor message for lower-ranked results. 'full' returns bookends and the complete anchored window for every result.)r¬  r¯  r­  r"   zîDiscovery shape only. Inclusive lower bound on session start time. ISO date/datetime (e.g. 2026-06-01) or relative duration (7d, 24h, 2w = within the last N). Use only when the user names a time frame. sort is a ranking bias, not a bound.zÛDiscovery shape only. Exclusive upper bound on session start time. ISO date/datetime (a date-only value is midnight UTC that day) or relative duration (7d = older than a week). Use only when the user names a time frame.Úarrayr¬  z°Discovery shape only. Session ids already inspected this task. Those sessions and their lineage are omitted so a later query explores instead of repeating the same hit. Cap 20.)r¬  r’   r­  z…Scroll shape. Session to read inside. Use the session_id returned from a prior discovery call. Must be paired with around_message_id.u�   Scroll shape. Message id to center the window on â€” use match_message_id from a discovery result, or any id from a prior window.z€Scroll shape only. Messages to return on each side of the anchor (anchor itself always included). Clamped to [1, 20]. Default 5.zåOptional. 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.zãOptional. 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—   r   r~  rï   r  rÈ   )r¬  Ú
propertiesÚrequired)rÑ   r­  Ú
parameters)Úregistryr,   c                 ó.  — t        d| j                  d«      xs d| j                  dd«      | j                  dd«      | j                  dd«      |j                  d	«      |j                  d
«      dœdD �ci c]  }|| j                  |«      “Œ c}¤ŽS c c}w )Nrà   r5   r  rí   rï   rì   rÝ   rŽ  rI   r  )rà   r  rï   rÝ   rI   r  )r  r   r~  r  rÈ   r’  r“  r—   r‰   )r¢  rP   )ÚargsÚkwrÅ   s      r&   r<   r<     s•   € œ~ð  PØ�h‰h�wÓÒ% 2¨T¯X©X°g¸qÓ-AÈ$Ï(É(ÐS[Ð]^ÓJ_Ø�x‰x˜ *Ó-°"·&±&¸³,ÐSU×SYÑSYÐZnÓSoñ Pð$Nö O˜aˆ1ˆd�h‰h�q‹k‰>ò Oñ P€ ùòOs   Á4Bu   ðŸ”�)rÑ   ÚtoolsetÚschemaÚhandlerÚcheck_fnÚemoji)NNrE   )NNNNN)r   r‹  N)rì   N)NNN)r5   Nrí   NNNNrì   NNrŽ  NNN)MÚ__doc__rT   r   Úrerv   r   r   Útypingr   r   r   r   r	   Úhermes_state_commonr
   rÿ   r+  rZ  r(  r‘   ÚcompileÚ
IGNORECASErs   rx   r)  r]   r¡   rZ   r'   r/   ru   r9   r?   rB   r¬   rJ   rQ   rW   r^   rŽ   ri   rk   r�   r„   rŠ   r–   rc   rš   rŸ   r¢   r®   r³   rµ   rÇ   rÒ   rß   r  r�   r  r  r  rB  rQ  r]  rb  rt  ry  r}  r‰  r™  r¢  r©  ÚSESSION_SEARCH_SCHEMAÚtools.registryr´  r,   Úregisterr‰   r=   r&   ú<module>rÆ     s[  ðñó Û Û 	Û ß 'ß 3Õ 3å 5ð 9Ð ð %Ð ð Ð ð Ð àÐ à�R—Z‘ZÐ 4°b·m±mÓDÐ Ø#¨%°fÑ=Ð àgÐ àDÐ ð 1Ð ð :?ò °$ó òGðJ˜%  U¨C°Ð 5Ñ6ð J¸3ó JðT cð T¨dó TðD˜d 3¨ 8™nð D°°c¸3°h±ó Dð
H�có HðO Cð O¨Dó Oð  sð  ¨u°S¸$°YÑ/?ó  ð 1 Sð 1¨Só 1ð#˜H S™Mð #¨h°s©mó #ð4˜cð  h¨s¡mó ð"j ¨¡ð j¸À#¹ð jÐS[Ð\_ÑS`ð jÐeió jð
¨ð 
°°S±	ó 
ð°T¸#±Yð À3ÀsÁ8ó ð>˜ð > ð >¨ó >ð
Q¨sð Q°tó Qð&°(¸4ÀÀSÀ¹>Ñ2Jó &ðR˜x¨¨S°#¨X©Ñ7ð R¸Dó RðK¨Tó Kð BFØ48ñO�d˜3 ˜8‘nð O°¸#±ð OØ$,¨S¡MðOØ=AÀ#ÀsÀ(¹^óOñ"	Q˜cð 	Q¨Cð 	Q¸3ó 	Qð 8¨C¡=ð ¸tÀCÈÀH¹~ó ð#' 3ð #'¸hÀs¹mð #'ÐPXÐY]Ð^aÐcfÐ^fÑYgÑPhó #'ðLu ð u¨cð u¸Dð uÈcó uðA�4˜˜S˜‘>ð A¨ð A°°T¸#¸s¸(±^Ñ0Dó Að
 3ð °D¸¸c¸±Nð ÐSVð Ð[cÐdhÐilÐnqÐiqÑdrÑ[só ð2 PTØIMØ9=ñMP˜ð MP¨8°D¸±IÑ+>ð MPÀsð MPÐRZÐ[^ÑR_ð MPØðMPØ/2ðMPØILðMPà  ™ðMPà9AÀ#¹ðMPð $,¨D°©IÑ#6ðMPð CFóMPð`
_ ó 
_ñv #ð v¨Sð v¸Sð vÐUXð vÐdgó vð$l˜#ð l¨°©ð l¸#ó lñ Sð ¸cð ÐX[ð Ðgjó ðB#˜sð #¨ð #°ð #¸ó #ðx¸#ð xÐSVð xÐ[_ó xð HIØ&*ñ+E˜Cð +E°Cð +EÀð +EØ #ð+EØ/2ó+Eð` <@ñ'QàDGó'QðT QUØdhØYcØeiñi˜#ð i°ð iÀCð iØ'*ðiØ?BðiØ^aðiàðià*-ðià?BðiàSVðið ðið /2ðið PXÐX\Ð]`ÑXaÑObðið oróið.¨4ó ð ð	0ð ð !ð&ñð "ðAð ñð !Ø! 8Ð,ðRñ	ð !Ø# VÐ,ð8ð
 &ñ
ð !ðIñð !ð8ñð  Ø  (Ð+ðJñ	$ð !ð)ñð "ð$ñ"ð "ðVð ñð !ðGñð !ð7ññGl
ðZ ñ_pñ#BÐ ÷J 0à €× Ñ Ø	ØØ ñPð
 /Ø
ö
r=   