
    nSj                     T   U d Z ddlZddlZddlZ ej        e          Z e ej        d                    Z	e	re
                    ej                    e            Zee         ed<    ej                    Zddededz  ddfdZdefd	Zdd
Z G d d          Z e            ZdS )uy  Per-thread interrupt signaling for all tools.

Provides thread-scoped interrupt tracking so that interrupting one agent
session does not kill tools running in other sessions.  This is critical
in the gateway where multiple agents run concurrently in the same process.

The agent stores its execution thread ID at the start of run_conversation()
and passes it to set_interrupt()/clear_interrupt().  Tools call
is_interrupted() which checks the CURRENT thread — no argument needed.

Usage in tools:
    from tools.interrupt import is_interrupted
    if is_interrupted():
        return {"output": "[interrupted]", "returncode": 130}
    NHERMES_DEBUG_INTERRUPT_interrupted_threadsactive	thread_idreturnc                    ||nt          j                    j        }t          5  | rt                              |           nt                              |           t          rt          t                    nd}ddd           n# 1 swxY w Y   t          r6t          
                    d| |t          j                    j        |           dS dS )zSet or clear interrupt for a specific thread.

    Args:
        active: True to signal interrupt, False to clear it.
        thread_id: Target thread ident.  When None, targets the
                   current thread (backward compat for CLI/tests).
    Nz[[interrupt-debug] set_interrupt(active=%s, target_tid=%s) called_from_tid=%s current_set=%s)	threadingcurrent_threadident_lockr   adddiscard_DEBUG_INTERRUPTsetloggerinfo)r   r   tid	_snapshots       1/home/ice/.hermes/hermes-agent/tools/interrupt.pyset_interruptr   '   s#    !,)))2J2L2L2RC	 L L 	. $$S)))) ((---1AKC,---t	L L L L L L L L L L L L L L L  
0C13399	
 	
 	
 	
 	

 
s   ABB	B	c                      t          j                    j        } t          5  | t          v cddd           S # 1 swxY w Y   dS )u   Check if an interrupt has been requested for the current thread.

    Safe to call from any thread — each thread only sees its own
    interrupt state.
    N)r	   r
   r   r   r   )r   s    r   is_interruptedr   >   s     
"
$
$
*C	 + +**+ + + + + + + + + + + + + + + + + +s   	6::c                  $    t          d           dS )a  Clear any interrupt bit on the CURRENT thread.

    Gives a user-approved command a clean interrupt slate immediately before
    it spawns its child process, so a stale bit that landed on this thread
    during the blocking approval-wait cannot SIGINT the just-approved run
    (exit 130 + "[Command interrupted]").  Single-thread ordering on this tid
    keeps the DO-NOT-BREAK invariant intact: a *genuine* interrupt arriving
    after this call re-sets the bit on the same thread and is still observed by
    the executor's poll loop.  Call this directly, never via the
    _interrupt_event proxy (its .clear() binds to whatever thread runs it).
    FNr        r   clear_current_thread_interruptr   I   s     %r   c                   F    e Zd ZdZdefdZd	dZd	dZd
dedz  defdZ	dS )_ThreadAwareEventProxyzDDrop-in proxy that maps threading.Event methods to per-thread state.r   c                     t                      S N)r   selfs    r   is_setz_ThreadAwareEventProxy.is_setc   s    r   Nc                 $    t          d           d S )NTr   r"   s    r   r   z_ThreadAwareEventProxy.setf   s    dr   c                 $    t          d           d S )NFr   r"   s    r   clearz_ThreadAwareEventProxy.cleari   s    er   timeoutc                 *    |                                  S )u:   Not truly supported — returns current state immediately.)r$   )r#   r(   s     r   waitz_ThreadAwareEventProxy.waitl   s    {{}}r   r   Nr!   )
__name__
__module____qualname____doc__boolr$   r   r'   floatr*   r   r   r   r   r   `   s        NN                EDL D      r   r   r!   r+   )r/   loggingosr	   	getLoggerr,   r   r0   getenvr   setLevelINFOr   r   int__annotations__Lockr   r   r   r   r   _interrupt_eventr   r   r   <module>r<      sO       				    		8	$	$ 4		":;;<<  " OOGL!!! "% c#h & & &	
 
$ 
3: 
 
 
 
 
.+ + + + +   .       " *)++   r   