
    cj                     &   d Z ddlZddlZddlmZ ddlmZ ddlmZ  ej        e	          Z
 e            dz  Zedz  Z	 	 	 	 dd
ededededee         dee         dedefdZ	 	 dd
ededee         dee         dee         f
dZdededdfdZdS )a}  
Session mirroring for cross-platform message delivery.

When a message is sent to a platform (via send_message or cron delivery),
this module appends a "delivery-mirror" record to the target session's
transcript so the receiving-side agent has context about what was sent.

Standalone -- works from CLI, cron, and gateway contexts without needing
the full SessionStore machinery.
    N)datetime)Optional)get_hermes_homesessionszsessions.jsoncli	assistantplatformchat_idmessage_textsource_label	thread_iduser_idrolereturnc           	         	 t          | t          |          ||          }|s t                              d| |||           dS ||t	          j                                                    d|d}t          ||           t                              d||           dS # t          $ r*}	t                              d| ||||	           Y d}	~	dS d}	~	ww xY w)	u[  
    Append a delivery-mirror message to the target session's transcript.

    Finds the gateway session that matches the given platform + chat_id,
    then writes a mirror entry to both the JSONL transcript and SQLite DB.

    ``role`` defaults to ``"assistant"`` — correct for the interactive
    ``send_message`` mirror, where the mirrored text is the agent's own
    outgoing reply (a genuine assistant turn). Callers mirroring text that is
    NOT the agent speaking — e.g. a cron brief delivered out-of-band — must
    pass ``role="user"``: the ``mirror``/``mirror_source`` metadata is dropped
    at the SQLite boundary (only role+content persist), so on replay an
    assistant-role mirror is indistinguishable from a real assistant turn and
    produces ``assistant → assistant`` pairs that break strict-alternation
    providers (issue #2221). A user-role mirror collapses safely via
    ``repair_message_sequence``'s consecutive-user merge on every provider.

    Returns True if mirrored successfully, False if no matching session or error.
    All errors are caught -- this is never fatal.
    )r   r   z(Mirror: no session found for %s:%s:%s:%sFT)r   content	timestampmirrormirror_sourcez%Mirror: wrote to session %s (from %s)z!Mirror failed for %s:%s:%s:%s: %sN)	_find_session_idstrloggerdebugr   now	isoformat_append_to_sqlite	Exception)
r	   r
   r   r   r   r   r   
session_id
mirror_msges
             -/usr/local/lib/hermes-agent/gateway/mirror.pymirror_to_sessionr"      s   :'%LL	
 
 

  	LL:   5 #!1133)
 

 	*j111<j,WWWt 	 	 	/	
 	
 	
 uuuuu	s   A B AB 
C'CCc                    	 ddl m}  |            }	 t          |dd          }t          |          r4 || ||          }|r#t	          |          |                                 S |                                 n# |                                 w xY wn2# t          $ r%}t                              d|           Y d}~nd}~ww xY wt          
                                sdS 	 t          t          d          5 }	t          j        |	          }
ddd           n# 1 swxY w Y   n# t          $ r Y dS w xY w|                                 }g }|
                                D ]\  }}t	          |                              d	          st#          |t$                    s>|                    d
          pi }|                    d          p|                    dd                                          }||k    rt	          |                    dd                    }|t	          |          k    rP|                    d          }|$t	          |pd          t	          |          k    r
|                    |           !|sdS r)fd|D             }|r|}nJt+          |          dk    rdS n4t+          |          dk    r!d |D             }t+          |          dk    rdS t-          |d           }|                    d          S )a3  
    Find the active session_id for a platform + chat_id pair.

    Queries state.db gateway session rows (primary source since #9006);
    falls back to scanning sessions.json for pre-migration databases.
    DM session keys don't embed the chat_id (e.g. "agent:main:telegram:dm"),
    so we match on the persisted chat origin, not the key.

    When *user_id* is provided, prefer exact sender matches. If multiple
    same-chat candidates exist and none matches the user, return None instead
    of guessing and contaminating another participant's session.
    r   	SessionDBfind_session_by_originN)r	   r
   r   r   z)Mirror state.db session lookup failed: %szutf-8)encoding_originr	    r
   r   c                     g | ]N}t          |                    d           pi                     d          pd          t                    k    L|OS r)   r   r*   )r   get).0entryr   s     r!   
<listcomp>z$_find_session_id.<locals>.<listcomp>   s`     
 
 
EIIh''-2229==CDDGTT TTT       c                 :   h | ]}t          |                    d           pi                     d          pd                                          Mt          |                    d           pi                     d          pd                                          S r,   )r   r-   strip)r.   r/   s     r!   	<setcomp>z#_find_session_id.<locals>.<setcomp>   s     
 
 
EIIh''-2229==CDDJJLL
8$$*//	::@bAAGGII
 
 
r1   c                 .    |                      dd          S )N
updated_atr*   )r-   )r/   s    r!   <lambda>z"_find_session_id.<locals>.<lambda>   s    599\23N3N r1   )keyr   )hermes_stater%   getattrcallabler   closer   r   r   _SESSIONS_INDEXexistsopenjsonloadloweritems
startswith
isinstancedictr-   appendlenmax)r	   r
   r   r   r%   dbfinderr   r    fdataplatform_lower
candidates_keyr/   r)   entry_platformorigin_chat_idorigin_thread_idexact_user_matchesdistinct_user_ids
best_entrys      `                  r!   r   r   `   s   &E******Y[[	R!94@@F +#V%#'#	  
  +z??HHJJJJBHHJJJJBHHJJJJJ E E E@!DDDDDDDDE !!## t/G444 	 9Q<<D	  	  	  	  	  	  	  	  	  	  	  	  	  	  	    tt ^^%%NJzz|| % %e t99$$ 	Jud,C,C 	8$$* **Z00MEIIj"4M4MTTVV^++VZZ	26677S\\))%zz+66$-=-C)D)DI)V)Ve$$$ t 
 
 
 
)
 
 
  	+JJ__q  4 !	Z1		
 
#
 
 

  !!A%%4Z%N%NOOOJ>>,'''sj   B ?A= B (B =BB 
C!CC%D( ;DD( D  D( #D $D( (
D65D6r   messagec                 x   d}	 ddl m}  |            }|                    | |                    dd          |                    d                     n2# t          $ r%}t
                              d|           Y d}~nd}~ww xY w||                                 dS dS # ||                                 w w xY w)	z0Append a message to the SQLite session database.Nr   r$   r   r   r   )r   r   r   zMirror SQLite write failed: %s)r:   r%   append_messager-   r   r   r   r=   )r   rX   rK   r%   r    s        r!   r   r      s    	B******Y[[
!V[11KK	** 	 	
 	
 	
 	

  : : :5q99999999: >HHJJJJJ >2>HHJJJJ s0   AA B  
BA>9B  >BB   B9)r   NNr   )NN)__doc__rA   loggingr   typingr   hermes_cli.configr   	getLogger__name__r   _SESSIONS_DIRr>   r   boolr"   r   rG   r    r1   r!   <module>rd      s  	 	               - - - - - -		8	$	$!!J./1 #!D DDD D 	D
 }D c]D D 
D D D DT  $!	[( [([([( }[( c]	[(
 c][( [( [( [(~#        r1   