refactor(tools): trim subagent_worktree/terminal_scope/terminal_hints/thread_context/slash_confirm — drop no-op HERMES_HOME override, inline single-use constants, unify probes, compact docstrings

This commit is contained in:
Teknium
2026-09-02 22:00:49 -07:00
parent 113f04616b
commit 922083ab93
5 changed files with 121 additions and 222 deletions
+17 -46
View File
@@ -1,29 +1,13 @@
#!/usr/bin/env python3
"""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, so tool
dispatch inside it silently loses:
* the approval session/platform ContextVars (``tools.approval`` /
``gateway.session_context``) — gateway sessions then fall into
``check_dangerous_command``'s non-interactive auto-approve branch and
dangerous commands run without prompting;
* the thread-local CLI approval/sudo callbacks (``tools.terminal_tool``) —
``prompt_dangerous_approval`` then cannot reach the user (GHSA-qg5c-hvr5-hjgr).
One audited capture/install/clear lifecycle for every place that fans tool
dispatch onto worker threads (``agent.tool_executor``, ``execute_code`` RPC threads).
Call :func:`propagate_context_to_thread` **on the parent thread** (it snapshots
the parent's ContextVars and callbacks at call time) and use the result as the
worker target::
t = threading.Thread(target=propagate_context_to_thread(loop_fn), args=(...))
executor.submit(propagate_context_to_thread(worker_fn), *args)
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.
A bare ``threading.Thread`` / ``ThreadPoolExecutor`` worker starts with an
empty ``contextvars.Context`` and no thread-local approval/sudo callbacks, so
tool dispatch inside it silently loses the approval ContextVars (gateway
sessions then auto-approve dangerous commands) and the CLI approval/sudo
callbacks (``prompt_dangerous_approval`` cannot reach the user,
GHSA-qg5c-hvr5-hjgr). Call :func:`propagate_context_to_thread` **on the parent
thread** (it snapshots at call time) and use the result as the worker target.
Callbacks are installed for the worker's lifetime and always cleared on exit.
"""
from __future__ import annotations
@@ -38,8 +22,8 @@ logger = logging.getLogger(__name__)
def _callback_api():
"""Resolve the terminal_tool callback getters/setters.
Imported lazily: ``tools.terminal_tool`` imports ``tools.approval`` at module
load, so a top-level import would risk a cycle for callers in ``tools.approval``.
Lazy: ``tools.terminal_tool`` imports ``tools.approval`` at module load, so a
top-level import would risk a cycle for callers in ``tools.approval``.
"""
from tools.terminal_tool import (
_get_approval_callback,
@@ -47,22 +31,15 @@ def _callback_api():
set_approval_callback,
set_sudo_password_callback,
)
return (
_get_approval_callback,
_get_sudo_password_callback,
set_approval_callback,
set_sudo_password_callback,
)
return (_get_approval_callback, _get_sudo_password_callback, set_approval_callback, set_sudo_password_callback)
def propagate_context_to_thread(target: Callable) -> Callable:
"""Wrap *target* so it runs on a worker thread with the *current* thread's
ContextVars and approval/sudo callbacks. The wrapper forwards args/kwargs.
"""Wrap *target* to run with the *current* thread's ContextVars and approval/sudo callbacks.
Fail-closed: if callback installation raises, the callbacks stay unset
(``None``) — ``prompt_dangerous_approval`` denies dangerous commands with no
callback in an interactive context, and the gateway approval queue blocks
when its notify callback is absent.
(``None``) — ``prompt_dangerous_approval`` then denies dangerous commands
and the gateway approval queue blocks.
"""
ctx = contextvars.copy_context()
parent_approval_cb = parent_sudo_cb = None
@@ -86,11 +63,8 @@ def propagate_context_to_thread(target: Callable) -> Callable:
if parent_sudo_cb is not None:
set_sudo(parent_sudo_cb)
except Exception:
logger.debug(
"Failed to install propagated approval/sudo callbacks; "
"dangerous-command approval will fail closed",
exc_info=True,
)
logger.debug("Failed to install propagated approval/sudo callbacks; "
"dangerous-command approval will fail closed", exc_info=True)
try:
return target(*args, **kwargs)
finally:
@@ -98,10 +72,7 @@ def propagate_context_to_thread(target: Callable) -> Callable:
set_approval(None)
set_sudo(None)
except Exception:
logger.debug(
"Failed to clear propagated approval/sudo callbacks",
exc_info=True,
)
logger.debug("Failed to clear propagated approval/sudo callbacks", exc_info=True)
return ctx.run(_inner)