Files
hermes-agent/gateway/session_clarify_batch.py
Teknium 433046b399 refactor(gateway): move the owner's server-request registry out of session_sudo_prompt
The registry behind sudo, secret, batch clarify and the Desktop UI
requests (_Pending, _open, _ask, resolve, lock_answer, may_settle,
owned_by, ask_viewer, ask_viewer_result) lived in session_sudo_prompt.py
because sudo was its first user. It now has its own module,
gateway/session_server_requests.py, the owner-side counterpart of the
sidecar's tui_gateway/server_requests.py. session_sudo_prompt.py keeps
only ask_sudo and sudo_prompt_scope. Every caller and test imports from
the new module directly, with no re-export shim. No behaviour change.

Review finding by @alt-glitch on #140472
Refs #134889 (TUI-18)
Refs #140337
2026-10-17 08:03:51 -07:00

43 lines
2.6 KiB
Python

"""The clarify tool's whole question batch for a classic-CLI viewer's turn, in one request.
Main's HermesCLI answered ``clarify_callback(questions)`` with its batch panel: every question shown
at once, one active, answers revisitable until all are locked, ``{answers: {qid: raw | None},
outcome}``. The messaging path asks one card per question. For a classic-CLI local viewer the owner
sends the batch as the existing ``clarify`` server→client request (``ClarifyRequestParams`` →
``ClarifyResult``) through the same request/response machinery as the sudo prompt, so main's own panel
answers it unchanged, and blocks until it answers, the turn is interrupted, or the clarify timeout
(the same config both sides read) passes.
"""
from __future__ import annotations
_QUESTION_KEYS = ("qid", "question", "choices", "multi_select")
def ask_clarify_batch(turn, questions) -> dict:
from gateway.session_server_requests import ask_viewer_result
from tools.clarify_gateway import get_clarify_timeout
authority, session_id, _generation = turn._approval_owner
live = authority.sessions.get(session_id)
agent = (getattr(turn._ctx, "agent_holder", None) or [None])[0]
interrupted = (lambda: bool(getattr(agent, "_interrupt_requested", False))) if agent is not None else (
lambda: False)
if live is None:
return {"answers": {}, "outcome": "cancelled"}
wire = [{key: entry.get(key) for key in _QUESTION_KEYS if entry.get(key) is not None} for entry in questions]
timeout = get_clarify_timeout()
# ``qids``: the viewer's batch panel locks each answer with ``clarify.lock`` (#140400).
result = ask_viewer_result(live.event_stream, session_id, "clarify", {"questions": wire},
cancelled=interrupted, timeout=timeout if timeout > 0 else None,
qids=[entry["qid"] for entry in questions])
if result is None:
return {"answers": {}, "outcome": "cancelled" if interrupted() else "timed_out"}
answers = result.get("answers")
if not isinstance(answers, dict):
return {"answers": {}, "outcome": "cancelled"}
known = {entry["qid"] for entry in questions}
clean = {qid: (value if isinstance(value, str) else None) for qid, value in answers.items() if qid in known}
# ``session_server_requests`` settles a batch with its terminal outcome (an absent outcome on a frame
# that carried answers is ``submitted``; a cancel-all or error is ``cancelled``).
outcome = result.get("outcome") if result.get("outcome") in ("submitted", "timed_out", "cancelled") else "cancelled"
return {"answers": clean, "outcome": outcome}