refactor(terminal): probe NOPASSWD only when a prompt would fire; drop the host-only probe

With BaseEnvironment supplying a backend-scoped probe to every production
caller, the module-level host-only `_sudo_nopasswd_works` (and its
TERMINAL_ENV gate) had no callers left; the `or` fallback and the outer
try/except around a callback that already fails closed were dead too.

Move the probe under `should_prompt_for_sudo`: headless callers (gateway,
cron, delegated children) reach `(command, None)` whether or not the probe
runs, so the extra backend round trip — an ssh exec on the SSH backend —
was pure waste on every headless sudo command.

test_subagent_sudo_prompt no longer needs to patch the host probe out:
bare `_transform_sudo_command(cmd)` calls have no probe by construction.
This commit is contained in:
kshitijk4poor
2026-09-11 10:35:27 +05:30
committed by kshitij
parent 39abca492d
commit 4fc64b6730
3 changed files with 18 additions and 48 deletions
+11 -32
View File
@@ -13,8 +13,7 @@ import subprocess
import sys
import threading
import time
from typing import Callable
from collections.abc import Iterator
from collections.abc import Callable, Iterator
from utils import env_var_enabled
@@ -363,23 +362,6 @@ def _count_real_sudo_invocations(command: str) -> int:
return _rewrite_real_sudo_invocations(command)[1]
def _sudo_nopasswd_works() -> bool:
"""True when local sudo currently works without prompting. Local backend only — Docker/SSH/
Modal must not inherit host sudo state. Re-probes every call (no cache) so an expired sudo
timestamp can't make a later command silently block waiting for a password."""
from tools.terminal_tool import _tenv
if (_tenv("TERMINAL_ENV", "local").strip().lower() or "local") != "local":
return False
try:
probe = subprocess.run(
["sudo", "-n", "true"], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, timeout=3, check=False,
)
return probe.returncode == 0
except Exception:
return False
def _rewrite_compound_background(command: str) -> str:
"""Wrap `A && B &` (or `A || B &`) to `A && { B & }` at depth 0. Bash binds `&&` tighter
than `&`, so `A && B &` backgrounds a subshell that runs B in the foreground and waits for
@@ -447,7 +429,9 @@ def _transform_sudo_command(
password in the command string themselves. With no password available the command is
returned unchanged and ``sudo_stdin`` is None, so it fails gracefully with "sudo: a password
is required". Password sources, in order: configured SUDO_PASSWORD, the session cache, then
an interactive prompt (45s timeout, cached on success) when a UI is reachable."""
an interactive prompt (45s timeout, cached on success) when a UI is reachable.
``sudo_nopasswd_check`` (supplied by ``BaseEnvironment``) runs ``sudo -n true`` inside the
selected backend; a True result skips the prompt and the ``-S`` rewrite entirely."""
from tools.terminal_tool import _get_sudo_password_callback
if command is None:
return None, None
@@ -465,24 +449,19 @@ def _transform_sudo_command(
has_configured_password = _configured_password is not None
sudo_password = _configured_password if has_configured_password else _get_cached_sudo_password()
# sudoers NOPASSWD must not be forced through the prompt or the -S pipe. BaseEnvironment
# supplies a probe scoped to the selected backend; direct callers keep the local-host
# fallback. Re-probed every call so an expired sudo timestamp cannot silently block.
if not has_configured_password and not sudo_password:
nopasswd_check = sudo_nopasswd_check or _sudo_nopasswd_works
try:
if nopasswd_check():
return command, None
except Exception:
pass
# delegate_task children inherit HERMES_INTERACTIVE=1 (and possibly a stale thread-local
# callback on a recycled worker) but have no user on the other side — always headless;
# configured password, session cache and the NOPASSWD probe still apply.
# configured password and session cache still apply.
should_prompt_for_sudo = (
env_var_enabled("HERMES_INTERACTIVE") or _get_sudo_password_callback() is not None
) and not _in_delegated_child_context()
if not has_configured_password and not sudo_password and should_prompt_for_sudo:
# sudoers NOPASSWD must not be forced through the prompt or the -S pipe. The probe is
# a round trip on the selected backend (an ssh exec for SSH), so it only runs when a
# prompt would otherwise fire: headless callers end up at ``(command, None)`` either
# way. Re-probed every call so an expired sudo timestamp cannot silently block.
if sudo_nopasswd_check is not None and sudo_nopasswd_check():
return command, None
sudo_password = _prompt_for_sudo_password(timeout_seconds=45)
if sudo_password:
_set_cached_sudo_password(sudo_password)