
    PL
j(                    B   U d Z ddlm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  ej        e          Ze G d d                      Z ej                    Zi Zded	<   i Zd
ed<   d+dZd,dZd-dZd.dZd/dZd0dZd1d Zd2d!Zi Zd"ed#<   d3d'Z d4d(Z!d5d*Z"dS )6u  Gateway-side clarify primitive (blocking event-based queue).

The ``clarify`` tool needs to ask the user a question and block the agent
thread until they respond.  In CLI mode this is trivial — ``input()`` is
synchronous.  In gateway mode the agent runs on a worker thread while the
event loop handles the user's reply, so we need a thread-safe primitive
that:

  * stores a pending clarify request (with a generated ``clarify_id``),
  * blocks the agent thread on an ``Event``,
  * resolves the wait when the gateway's button-callback or text-intercept
    fires ``resolve_gateway_clarify(clarify_id, response)``,
  * supports timeouts so a user who never responds does NOT hang the agent
    thread forever (which would also pin the gateway's running-agent guard).

State is module-level (same shape as ``tools.approval``) so platform
adapters can call ``resolve_gateway_clarify`` without holding a back-
reference to the ``GatewayRunner`` instance.

Two delivery paths from the adapter:

  1. **Button UI** — adapters override ``send_clarify`` to render inline
     buttons (e.g. Telegram ``InlineKeyboardMarkup``).  The button
     callback resolves with the chosen string.  A final "Other (type
     answer)" button enters text-capture mode for free-form responses.

  2. **Text fallback** — adapters without rich UI render a numbered list.
     The user replies with a number ("2") or with free text; the gateway's
     ``_handle_message`` intercepts the reply and resolves directly.
    )annotationsN)	dataclassfield)CallableDictListOptionalc                      e Zd ZU dZded<   ded<   ded<   ded<    eej                  Zd	ed
<   dZ	ded<   dZ
ded<   ddZdS )_ClarifyEntryz5One pending clarify request inside a gateway session.str
clarify_idsession_keyquestionOptional[List[str]]choices)default_factoryzthreading.EventeventNOptional[str]responseFboolawaiting_textreturnDict[str, object]c                d    | j         | j        | j        | j        rt	          | j                  nd dS )N)r   r   r   r   )r   r   r   r   list)selfs    9/home/kuhnn/.hermes/hermes-agent/tools/clarify_gateway.py	signaturez_ClarifyEntry.signature:   s:    /+-1\CtDL)))t	
 
 	
    )r   r   )__name__
__module____qualname____doc____annotations__r   	threadingEventr   r   r   r    r   r   r   r   /   s         ??OOOMMM    "U9?CCCECCCC"H""""M
 
 
 
 
 
r   r   zDict[str, _ClarifyEntry]_entrieszDict[str, List[str]]_session_indexr   r   r   r   r   r   r   c           	        t          | |||rt          |          ndt          |                     }t          5  |t          | <   t
                              |g                               |            ddd           n# 1 swxY w Y   |S )zRegister a pending clarify request and return the entry.

    The caller (gateway clarify_callback) will then send the prompt to the
    user and block on ``wait_for_response(clarify_id, timeout)``.
    N)r   r   r   r   r   )r   r   r   _lockr(   r)   
setdefaultappend)r   r   r   r   entrys        r   registerr/   N   s     !(2Wdw--'  E 
 F F$!!+r2299*EEEF F F F F F F F F F F F F F F Ls   9B  BBtimeoutfloatr   c                .   t           5  t                              |           }ddd           n# 1 swxY w Y   |dS 	 ddlm} n# t
          $ r d}Y nw xY wt          j                    t          |d          z   }t          j                    t          j                    d}	 |t          j                    z
  }|dk    rn9|j	        
                    t          d|                    rn| ||d	           Vt           5  t                              | d           t                              |j                  }|r;| |v r7|                    |            |s t                              |j        d           ddd           n# 1 swxY w Y   |j        S )
u  Block on the entry's event until resolved or timeout fires.

    Polls in 1-second slices so the agent's inactivity heartbeat keeps
    firing — without this, ``Event.wait(timeout=600)`` blocks the thread
    for 10 minutes with zero activity touches and the gateway's inactivity
    watchdog kills the agent while the user is still typing.

    Returns the resolved response string, or ``None`` on timeout.
    Nr   )touch_activity_if_dueg        )
last_touchstartTg      ?)r0   z!waiting for user clarify response)r+   r(   gettools.environments.baser3   	Exceptiontime	monotonicmaxr   waitminpopr)   r   remover   )r   r0   r.   r3   deadlineactivity_state	remainingidss           r   wait_for_responserD   g   s8    
 ) )Z(() ) ) ) ) ) ) ) ) ) ) ) ) ) )}t%AAAAAAA % % % $% ~#gs"3"33H$(N$4$4t~?O?OPPNWt~///	>>;CY$7$788 	 ,!!.2UVVVW 
 < <Z&&&  !233 	<:$$JJz""" <""5#4d;;;< < < < < < < < < < < < < < < >s0   /33A AAA8FF	F	r   r   c                    t           5  t                              |           }|	 ddd           dS 	 ddd           n# 1 swxY w Y   |t          |          nd|_        |j                                         dS )zUnblock the agent thread waiting on ``clarify_id``.

    Returns True if an entry was found and resolved, False otherwise
    (already resolved, expired, or never existed).
    NF T)r+   r(   r6   r   r   r   set)r   r   r.   s      r   resolve_gateway_clarifyrH      s     
  Z((=                      '/&:S]]]EN	KOO4s   A  AAOptional[_ClarifyEntry]c                    t           5  t                              |           pg }|D ]6}t                              |          }||j        r|c cddd           S 7	 ddd           dS # 1 swxY w Y   dS )u  Return the OLDEST pending clarify entry for a session, or None.

    Used by the text-fallback intercept in ``_handle_message`` — when a
    clarify is awaiting a free-form text response, the next user message
    in that session is captured as the answer.
    N)r+   r)   r6   r(   r   )r   rC   cidr.   s       r   get_pending_for_sessionrL      s     
    --3 	 	CLL%%E}"                         s   AA,A,,A03A0c                    t           5  t                              |           }|	 ddd           dS d|_        	 ddd           dS # 1 swxY w Y   dS )zFlip an entry into text-capture mode (user picked the 'Other' button).

    Returns True if the entry exists and was flipped, False otherwise.
    NFT)r+   r(   r6   r   )r   r.   s     r   mark_awaiting_textrN      s    
 
  Z((=        #                 s   AAAAc                    t           5  t                              |           pg }t          d |D                       cddd           S # 1 swxY w Y   dS )zEReturn True when this session has at least one pending clarify entry.c              3  N   K   | ] }t                               |          d uV  !d S N)r(   r6   .0rK   s     r   	<genexpr>zhas_pending.<locals>.<genexpr>   s3      @@S8<<$$D0@@@@@@r   N)r+   r)   r6   any)r   rC   s     r   has_pendingrV      s    	 A A  --3@@C@@@@@A A A A A A A A A A A A A A A A A As   5A

AAintc                   t           5  t          t                              | g           pg           }d |D             }ddd           n# 1 swxY w Y   d}|D ]*}|d|_        |j                                         |dz  }+|S )a  Resolve and drop every pending clarify for a session.

    Used by session-boundary cleanup (e.g. ``/new``, gateway shutdown,
    cached-agent eviction) so blocked agent threads don't hang past the
    end of their session.  Returns the number of entries cancelled.
    c                D    g | ]}t                               |d           S rQ   )r(   r>   rR   s     r   
<listcomp>z!clear_session.<locals>.<listcomp>   s&    :::s8<<T**:::r   Nr   rF      )r+   r   r)   r>   r   r   rG   )r   rC   entries	cancelledr.   s        r   clear_sessionr^      s     
 ; ;>%%k266<"==::c:::; ; ; ; ; ; ; ; ; ; ; ; ; ; ; I 	 	=
 Q		s   7AAAc                     	 ddl m}   |             pi }|                    di           pi }t          |                    dd                    S # t          $ r Y dS w xY w)u]  Read the clarify response timeout (seconds) from config.

    Defaults to 600 (10 minutes) — long enough for the user to type a
    thoughtful response, short enough that an abandoned prompt eventually
    unblocks the agent thread instead of pinning the running-agent guard
    forever.

    Reads ``agent.clarify_timeout`` from config.yaml.
    r   )load_configagentclarify_timeoutiX  )hermes_cli.configr`   r6   rW   r8   )r`   cfg	agent_cfgs      r   get_clarify_timeoutrf      s    111111kmm!rGGGR((.B	9==!2C88999   sss   AA 
AAz*Dict[str, Callable[[_ClarifyEntry], None]]_notify_cbscbCallable[[_ClarifyEntry], None]Nonec                Z    t           5  |t          | <   ddd           dS # 1 swxY w Y   dS )zDRegister a per-session notify callback used by ``clarify_callback``.N)r+   rg   )r   rh   s     r   register_notifyrl     sv    	 & &#%K & & & & & & & & & & & & & & & & & &s    $$c                    t           5  t                              | d           ddd           n# 1 swxY w Y   t          |            dS )zLDrop the per-session notify callback and cancel any pending clarify entries.N)r+   rg   r>   r^   r   s    r   unregister_notifyro     s    	 + +T***+ + + + + + + + + + + + + + + +s   044)Optional[Callable[[_ClarifyEntry], None]]c                x    t           5  t                              |           cd d d            S # 1 swxY w Y   d S rQ   )r+   rg   r6   rn   s    r   
get_notifyrr     s{    	 , ,{++, , , , , , , , , , , , , , , , , ,s   /33)
r   r   r   r   r   r   r   r   r   r   )r   r   r0   r1   r   r   )r   r   r   r   r   r   )r   r   r   rI   )r   r   r   r   )r   r   r   r   )r   r   r   rW   )r   rW   )r   r   rh   ri   r   rj   )r   r   r   rj   )r   r   r   rp   )#r#   
__future__r   loggingr%   r9   dataclassesr   r   typingr   r   r   r	   	getLoggerr    loggerr   RLockr+   r(   r$   r)   r/   rD   rH   rL   rN   rV   r^   rf   rg   rl   ro   rr   r'   r   r   <module>rz      s    > # " " " " "       ( ( ( ( ( ( ( ( 1 1 1 1 1 1 1 1 1 1 1 1		8	$	$ 
 
 
 
 
 
 
 
& 		%' ' ' ' '') ) ) ) )   2( ( ( (^      $
 
 
 
A A A A   8   6 ;= < < < <& & & &   , , , , , ,r   