
    $jM2                        d dl Z d dlZe j                            dd          Zer$eej        vrej                            d e           d ej        D             e_        d dl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mZmZmZ d dlmZ  ej        e          Zdadd	Zd
ZdefdZdeddfdZ ee	d          r e	j	        e	j         e	j!                    ee	d          r e	j	        e	j"        e            ee	d          r e	j	        e	j#        e           n" ee	d          r e	j	        e	j$        e            ee	d          r e	j	        e	j%        e	j!                   de&ddfdZ'ddeddfdZ(d Z)edk    r e)             dS dS )    NHERMES_PYTHON_SRC_ROOT c                     g | ]}|d v|	S )>   r   . ).0ps     0/usr/local/lib/hermes-agent/tui_gateway/entry.py
<listcomp>r      s"    666!1I#5#5A#5#5#5    )server)
_CRASH_LOGdispatchresolve_skin
write_json)TeeTransportreturnc                      t           j                            d          } | sdS ddlm} t          t          j         ||                     t          _        dS )a  Mirror every dispatcher emit to the dashboard sidebar via WS.

    Activated by `HERMES_TUI_SIDECAR_URL`, set by the dashboard's
    ``/api/pty`` endpoint when a chat tab passes a ``channel`` query param.
    Best-effort: connect failure or runtime drop falls back to stdio-only.
    HERMES_TUI_SIDECAR_URLNr   )WsPublisherTransport)osenvirongettui_gateway.event_publisherr   r   r   _stdio_transport)urlr   s     r
   _install_sidecar_publisherr       sc     *..1
2
2C @@@@@@*!5!5c!:!: Fr   g      ?c                      t           j                            d          pd                                } | st          S 	 t          |           }n# t          $ r
 t          cY S w xY w|dk    r|nt          S )N#HERMES_TUI_GATEWAY_SHUTDOWN_GRACE_Sr   r   )r   r   r   strip_DEFAULT_SHUTDOWN_GRACE_Sfloat
ValueError)rawvalues     r
   _shutdown_grace_secondsr&   >   s    :>>?@@FB
M
M
O
OC )(()c

 ) ) )(((()AII55#<<s   A A"!A"signumc                    i }dD ],}t          t          |d          }|||t          |          <   -|                    | d|            }	 t	          j        t          j                            t                    d           t          t          dd          5 }|
                    d	| d
t          j        d           d           |+|
                    d           t          j        ||           ddl}|j                                        D ]\  }}	|
                    d|	j         d| d           |
                    d                    t          j        t+          j                                        |                                         	 ddd           n# 1 swxY w Y   n# t.          $ r Y nw xY wt1          d| t*          j        d           ddl}dd}
|                    t7                      |
          }d|_        |                                 	 t+          j        d           dS # t>          $ r  w xY w)u  Capture WHICH thread and WHERE a termination signal hit us.

    SIG_DFL for SIGPIPE kills the process silently the instant any
    background thread (TTS playback, beep, voice status emitter, etc.)
    writes to a stdout the TUI has stopped reading.  Without this
    handler the gateway-exited banner in the TUI has no trace — the
    crash log never sees a Python exception because the kernel reaps
    the process before the interpreter runs anything.

    Termination semantics: ``sys.exit(0)`` here used to race the worker
    pool — a thread holding ``_stdout_lock`` mid-flush would block the
    interpreter shutdown indefinitely.  We now log the stack, give the
    process the configured shutdown grace
    (``HERMES_TUI_GATEWAY_SHUTDOWN_GRACE_S``, default
    ``_DEFAULT_SHUTDOWN_GRACE_S``) to drain naturally on a background
    thread, and fall back to ``os._exit(0)`` so a wedged write/flush
    can never strand the process.
    )SIGPIPESIGTERMSIGHUPSIGINTSIGBREAKNzsignal Texist_okautf-8encodingz
=== u    received · %Y-%m-%d %H:%M:%S ===
z&main-thread stack at signal delivery:
)filer   z
--- thread z (id=z) ---
r   z[gateway-signal] r6   flushr   c                  .    t          j        d           d S )Nr   )r   _exitr   r   r
   
_hard_exitz_log_signal.<locals>._hard_exity   s     	r   r   N) getattrsignalintr   r   makedirspathdirnamer   openwritetimestrftime	tracebackprint_stack	threading_activeitemsnamejoinformat_stacksys_current_frames	ExceptionprintstderrTimerr&   daemonstartexit
SystemExit)r'   frame_signal_names_attr_sigrL   f
_threadingtidthr;   timers               r
   _log_signalrb   I   s   * %'MG - -vud++',M#d))$V%7v%7%788D
BGOOJ//$????*cG444 	YGGVVVDM:M,N,NVVV    ABBB%e!4444 +***%-3355 Y YRBBBcBBBCCC	 6s7J7L7L7P7PQT7U7U V VWWXXXXY	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y    	
$d
$
$3:TBBBB""""    466
CCEEL	KKMMM	    	sD   AF2 C>F&F2 &F**F2 -F*.F2 2
F?>F?'H= =I	r)   r*   r+   r-   r,   reasonc                    	 t          j        t           j                            t                    d           t          t          dd          5 }|                    dt          j        d           d|  d	           d
d
d
           n# 1 swxY w Y   n# t          $ r Y nw xY wt          d|  t          j        d           d
S )u  Record why the gateway subprocess is shutting down.

    Three exit paths (startup write fail, parse-error-response write fail,
    dispatch-response write fail, stdin EOF) all collapse into a silent
    sys.exit(0) here.  Without this trail the TUI shows "gateway exited"
    with no actionable clue about WHICH broken pipe or WHICH message
    triggered it — the main reason voice-mode turns look like phantom
    crashes when the real story is "TUI read pipe closed on this event".
    Tr.   r0   r1   r2   u   
=== gateway exit · r4   u    · reason=r5   Nz[gateway-exit] r7   )r   r@   rA   rB   r   rC   rD   rE   rF   rQ   rR   rO   rS   )rc   r]   s     r
   	_log_exitre      s   
BGOOJ//$????*cG444 	GG,7J)K)K , ,#, , ,  	 	 	 	 	 	 	 	 	 	 	 	 	 	 	
    	
$F
$
$3:TBBBBBBs6   AB /B B BB BB 
B%$B%      ?timeoutc                 p    t           }||                                sdS |                    |            dS )a  Briefly block until background MCP discovery finishes, up to ``timeout``.

    MCP discovery runs in a daemon thread spawned at startup (see main()) so a
    slow/dead server can't freeze ``gateway.ready``.  But the agent snapshots
    its tool list ONCE at build time and never re-reads it, so a reachable-but-
    slow server that finishes connecting *after* the first prompt would be
    invisible for the whole session.  Joining with a short bounded timeout
    before the first agent build lets already-spawning fast servers land
    without re-introducing the startup hang: a dead server simply isn't waited
    on beyond ``timeout``.  No-op when no discovery thread was started.
    N)rg   )_mcp_discovery_threadis_aliverM   )rg   threads     r
   wait_for_mcp_discoveryrl      s;     #F~V__..~
KKK     r   c                     t                       	 ddlm}   |             pi                     d          }t	          |t
                    ot          |          dk    }n# t          $ r d}Y nw xY w|r6dd}dd l}|	                    |dd          }|
                                 |at          d	d
ddt                      idd          s#t          d           t          j        d           t          j        D ]}|                                }|s	 t'          j        |          }nN# t&          j        $ r< t          d	dddd d          s#t          d           t          j        d           Y yw xY wt	          |t
                    r|                    d          nd }	t-          |          }
|
6t          |
          s't          d|	d           t          j        d           t          d           d S )Nr   )read_raw_configmcp_serversTr   c                      	 ddl m}   |              d S # t          $ r  t                              dd           Y d S w xY w)Nr   discover_mcp_toolsz$Background MCP tool discovery failedT)exc_info)tools.mcp_toolrr   rQ   loggerwarningrq   s    r
   _discover_mcp_backgroundz&main.<locals>._discover_mcp_background   sx    ======""$$$$$   :T       s    &>>ztui-mcp-discovery)targetrL   rU   z2.0eventzgateway.readyskin)typepayload)jsonrpcmethodparamsz<startup write failed (broken stdout pipe before first event)iDzparse error)codemessage)r}   erroridz6parse-error-response write failed (broken stdout pipe)r~   z!response write failed for method=z (broken stdout pipe)z'stdin EOF (TUI closed the command pipe)r<   )r   hermes_cli.configrn   r   
isinstancedictlenrQ   rI   ThreadrV   ri   r   r   re   rO   rW   stdinr    jsonloadsJSONDecodeErrorr   )rn   _mcp_servers_has_mcp_serversrw   _mcp_threading_mcp_threadr$   linereqr~   resps              r
   mainr      sz      , 555555'))/R44]CC%lD99Sc,>O>ORS>S           ,	 	 	 	 	+***$+++$ , 
 

 	 !,*7OPP    
 	PQQQy  yy{{ 		*T""CC# 	 	 	%6Vc:d:dlpqqrr RSSSH		 '1d&;&;E"""}}d## ]f]]]^^^788888s%   AA A-,A-D**AE54E5__main__r<   )rf   )*r   rO   r   r   	_src_rootrA   insertr   loggingr>   rE   rG   tui_gatewayr   tui_gateway.serverr   r   r   r   tui_gateway.transportr   	getLogger__name__ru   ri   r   r!   r"   r&   r?   rb   hasattrr)   SIG_IGNr*   r+   r-   r,   strre   rl   r   r   r   r
   <module>r      s   				 




 JNN3R88	 "#(**HOOAy!!! 76sx666               M M M M M M M M M M M M . . . . . .		8	$	$
     6   = = = = =E Et E E E El 769 2FM&.&.111
769 /FM&.+...
768 0FM&-----WVZ   0 FM&/;///
768 1FM&-000Cc Cd C C C C,! !E !T ! ! ! !$Q9 Q9 Q9h zDFFFFF r   