fix(approval): classic-CLI MCP/vault consent prompts reach the approval panel
request_elicitation_consent's CLI branch called prompt_dangerous_approval without the agent thread's approval callback. Under prompt_toolkit that fails closed before any panel renders, so an untrusted MCP tool, MCP sampling or a vault card fill was auto-cancelled in hermes chat without the user ever being asked (and observers saw pre/post with no human wait). Pass the registered panel callback, as the protected-write prompt does.
This commit is contained in:
@@ -378,9 +378,8 @@ class TestClassicCliPromptsFireGatewayTwinHooks:
|
||||
approval_module.unregister_gateway_notify(isolated_session)
|
||||
assert result == approved
|
||||
|
||||
# Classic CLI: the panel callback (protected write) or stdin (consent) answers "once".
|
||||
# Classic CLI: the prompt_toolkit panel callback registered on the agent thread answers "once".
|
||||
monkeypatch.delenv("HERMES_GATEWAY_SESSION")
|
||||
monkeypatch.setattr("builtins.input", lambda _prompt="": "o")
|
||||
set_approval_callback(lambda *_a, **_kw: "once")
|
||||
try:
|
||||
result, cli_hooks = _capture_hooks(run)
|
||||
|
||||
@@ -326,7 +326,6 @@ def request_elicitation_consent(message: str, description: str, *,
|
||||
return "cancel" # nobody answered (timeout / prompt withdrawn) — not a user refusal
|
||||
return _consent(decision.get("choice"), "decline")
|
||||
|
||||
# allow_permanent=False: elicitation is a per-call confirmation — no pattern to remember.
|
||||
# Same observer payload as the gateway branch (#131876); post fires in a finally so the
|
||||
# wait settles for observers on the early fail-closed return too.
|
||||
hook_kwargs = dict(command=message, description=description, pattern_key="mcp_elicitation",
|
||||
@@ -334,8 +333,12 @@ def request_elicitation_consent(message: str, description: str, *,
|
||||
_ctx._fire_approval_hook("pre_approval_request", **hook_kwargs)
|
||||
hook_choice = "cancelled"
|
||||
try:
|
||||
from tools.terminal_tool import _get_approval_callback
|
||||
# allow_permanent=False: elicitation is a per-call confirmation — no pattern to remember.
|
||||
# Pass the agent thread's panel callback: without it prompt_toolkit fails the prompt closed unseen.
|
||||
choice = prompt_dangerous_approval(message, description, timeout_seconds=timeout_seconds,
|
||||
allow_permanent=False, title=title)
|
||||
allow_permanent=False, title=title,
|
||||
approval_callback=_get_approval_callback())
|
||||
hook_choice = choice
|
||||
except Exception as exc:
|
||||
logger.error("Elicitation CLI prompt failed: %s", exc, exc_info=True)
|
||||
|
||||
Reference in New Issue
Block a user