
    jV                    `    d Z ddlmZ ddlZddlZddlmZ  ej        e          Z	d Z
d
d	ZdS )u  Propagate agent-turn context into worker threads that dispatch Hermes tools.

A bare ``threading.Thread`` / ``ThreadPoolExecutor`` worker starts with an
empty ``contextvars.Context`` and no thread-local approval/sudo callbacks.
Tool dispatch inside such a thread therefore silently loses:

  * the approval *session/platform* ContextVars (``tools.approval`` /
    ``gateway.session_context``) — so gateway sessions fall into
    ``check_dangerous_command``'s non-interactive auto-approve branch and
    dangerous commands run without prompting (#33057, #30882);
  * the thread-local CLI approval/sudo callbacks (``tools.terminal_tool``) —
    so ``prompt_dangerous_approval`` cannot reach the user
    (GHSA-qg5c-hvr5-hjgr, #15216).

This helper factors out that capture/install/clear lifecycle so the several
places that fan tool dispatch onto worker threads (``agent.tool_executor`` and
the ``execute_code`` RPC threads) share one audited implementation instead of
divergent copies.

Usage — call :func:`propagate_context_to_thread` **on the parent thread**
(it snapshots the parent's ContextVars and callbacks at call time) and use the
returned callable as the worker's target::

    t = threading.Thread(target=propagate_context_to_thread(loop_fn), args=(...))
    # or
    executor.submit(propagate_context_to_thread(worker_fn), *args)

Approval/sudo callbacks are installed for the worker's lifetime and **always
cleared on exit**, so a recycled thread never holds a stale reference to a
disposed CLI instance.
    )annotationsN)Callablec                 &    ddl m} m}m}m} | |||fS )zResolve the terminal_tool callback getters/setters.

    Imported lazily: ``tools.terminal_tool`` imports ``tools.approval`` at
    module load, so a top-level import here would risk an import cycle for
    callers that live in ``tools.approval``.
    r   _get_approval_callback_get_sudo_password_callbackset_approval_callbackset_sudo_password_callback)tools.terminal_toolr   r   r	   r
   r   s       9/home/ubuntu/.hermes/hermes-agent/tools/thread_context.py_callback_apir   +   sV                	#"	     targetr   returnc                   	 t          j                    dxd		 t                      \  }}}} |             |            ||f	n,# t          $ r t                              dd           Y nw xY w	 fd}|S )u  Wrap *target* for execution on a worker thread with the *current*
    thread's ContextVars and approval/sudo callbacks propagated.

    Call this on the parent thread; pass the returned callable as the
    thread/executor target.  The returned callable forwards its positional
    and keyword arguments to *target* and returns its result.

    Fail-closed: if callback installation raises, the callbacks are left
    unset (``None``).  That is the safe outcome — ``prompt_dangerous_approval``
    denies dangerous commands when no callback is registered in an interactive
    context, and the gateway approval queue blocks when its notify callback is
    absent.
    Nz0Could not capture parent approval/sudo callbacksTexc_infoc                 F      fd}                     |          S )Nc                    M\  } }	  |             |           n,# t           $ r t                              dd           Y nw xY w	  i I\  } }	  | d             |d            S # t           $ r t                              dd           Y S w xY wS # I\  } }	  | d             |d            w # t           $ r t                              dd           Y w w xY ww xY w)NzaFailed to install propagated approval/sudo callbacks; dangerous-command approval will fail closedTr   z2Failed to clear propagated approval/sudo callbacks)	Exceptionloggerdebug)set_approvalset_sudoargskwargsparent_approval_cbparent_sudo_cbsettersr   s     r   _innerz<propagate_context_to_thread.<locals>._runner.<locals>._innerZ   s   ")0&h
)5$%7888%1 000    LLF!% !     vt.v..&-4*L($T*** $   P%) %      '7&-4*L($T*** $   P%) %      'sQ   % &AAB% !A88&B! B!%C2.CC2&C.+C2-C..C2)run)r   r   r    ctxr   r   r   r   s   `` r   _runnerz,propagate_context_to_thread.<locals>._runnerY   sI    	 	 	 	 	 	 	 	 	 	8 wwvr   )contextvarscopy_contextr   r   r   r   )
r   get_approvalget_sudor   r   r#   r"   r   r   r   s
   `     @@@@r   propagate_context_to_threadr(   @   s     
"
$
$C*..GX9F6hh)\^^!* X X XGRVWWWWWX        > Ns   +A &A54A5)r   r   r   r   )__doc__
__future__r   r$   loggingtypingr   	getLogger__name__r   r   r(    r   r   <module>r0      s    @ # " " " " "           		8	$	$  *8 8 8 8 8 8r   