Skip to content

fix(gateway,telegram): wire clarify_callback through gateway agent and Telegram adapter#21517

Closed
Tranquil-Flow wants to merge 1 commit into
NousResearch:mainfrom
Tranquil-Flow:fix/21032-clarify-callback-gateway
Closed

fix(gateway,telegram): wire clarify_callback through gateway agent and Telegram adapter#21517
Tranquil-Flow wants to merge 1 commit into
NousResearch:mainfrom
Tranquil-Flow:fix/21032-clarify-callback-gateway

Conversation

@Tranquil-Flow

Copy link
Copy Markdown
Contributor

What does this PR do?

After upgrade, every clarify call from the agent in gateway/Telegram mode returned {"error": "Clarify tool is not available in this execution context."}. Inline keyboard buttons never appeared. The tool worked in CLI and TUI — gateway-only regression.

Root cause: AIAgent requires clarify_callback to wire the tool to the platform. CLI (hermes_cli/main.py) and TUI (tui_gateway/server.py) both pass it during agent construction. The messaging gateway path in gateway/run.py was never assigning it, so agent.clarify_callback stayed None and tools/clarify_tool.py:57 returned the not-available error. Additionally, the Telegram adapter had none of the primitives needed to actually display the choices (no _clarify_state, no send_clarify_prompt, no clarify: callback handler).

This PR:

  1. Wires agent.clarify_callback on the messaging gateway path, right next to the existing agent.status_callback = _status_callback_sync line. The bridge closure (_clarify_callback_sync) posts an async send_clarify_prompt coroutine on the running loop and parks on a threading.Event (120s timeout, matching CLI behaviour).
  2. Adds send_clarify_prompt, _clarify_state, and the clarify:id:idx|skip callback handler on TelegramAdapter.
  3. Authorizes clarify callbacks via the existing _is_callback_user_authorized helper so unauthorized users in shared chats can't steer the waiting agent turn (security: same gate that already protects slash-confirm and update-prompt callbacks).
  4. Other gateway adapters (Slack, Discord, Matrix, Feishu, …) lack send_clarify_prompt/_clarify_state. The bridge detects this and returns "" so the clarify tool produces an empty user response on those platforms instead of crashing — same UX as before this PR for those adapters. Wiring them up is follow-up work.

Related Issue

Closes #21032

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • gateway/run.py — added _clarify_callback_sync bridge closure (sync→async via run_coroutine_threadsafe, 120s wait, threading.Event resolution); assigned agent.clarify_callback = _clarify_callback_sync next to the existing agent.status_callback = _status_callback_sync.
  • gateway/platforms/telegram.py — added _clarify_state: Dict[str, Dict[str, Any]] initializer; added send_clarify_prompt(...) method (inline keyboard with one button per choice + a Skip row, uses existing _metadata_thread_id/_message_thread_id_for_send helpers, button labels truncated to 60 chars, parse_mode=None for the agent-supplied question); added the clarify:id:idx|skip branch to _handle_callback_query with an _is_callback_user_authorized guard up front, before any state lookup.
  • tests/gateway/test_telegram_clarify_buttons.py — new file. Adapter-side tests for send_clarify_prompt (4 tests covering inline keyboard with choices, skip-only for open-ended, thread routing, not-connected guard) and the callback handler (5 tests including the authorization guard, choice resolution, skip behaviour, unknown id graceful response, invalid index rejection without resolving the event, no-clobber on ea:/update_prompt: routes).
  • tests/gateway/test_runner_clarify_callback.py — new file. End-to-end tests for the gateway closure: posts a coroutine on a real asyncio loop in a background thread, asserts blocking + resolution + cleanup; verifies graceful "" return when the adapter lacks send_clarify_prompt; static guard on the wiring line in gateway/run.py.

How to Test

  1. python -m pytest tests/gateway/test_telegram_clarify_buttons.py tests/gateway/test_runner_clarify_callback.py -q → 13 passed.
  2. Surrounding telegram suite stays green: python -m pytest tests/gateway/test_telegram_format.py tests/gateway/test_telegram_topic_*.py tests/gateway/test_telegram_caption*.py tests/gateway/test_telegram_approval*.py tests/gateway/test_telegram_clarify_buttons.py tests/gateway/test_runner_clarify_callback.py -q → 161 passed.
  3. To exercise the bug manually: configure a Telegram gateway, send a message that triggers the clarify tool. Without this PR, the agent receives the not-available error and proceeds without disambiguation. With this PR, an inline keyboard appears in Telegram; tapping a button resolves the agent's wait.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run the relevant test suites and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15 (Darwin 24.6.0); end-to-end against a live Telegram bot was not exercised (unit-level only, via real asyncio loop in a background thread + mocked adapter send_message)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

$ python -m pytest tests/gateway/test_telegram_clarify_buttons.py tests/gateway/test_runner_clarify_callback.py -q
.............
13 passed in 0.91s

Notes

  • Telegram-only in this PR. Other gateway adapters (Slack, Discord, Matrix, Feishu, ...) still lack send_clarify_prompt/_clarify_state. The bridge detects that and returns "" for those (graceful degradation, same UX as before this PR). Wiring them up is follow-up work.
  • The _is_callback_user_authorized guard runs before any state lookup or mutation, so unauthorized users get a "not authorized" answer with no observable effect on the waiting agent turn. Pattern matches the existing slash-confirm and update-prompt callback flows.
  • Wait timeout (120s) matches CLI clarify behaviour (CLI_CONFIG["clarify"]["timeout"]).
  • Button labels are truncated to 60 chars (Telegram inline-button label limit defensive guard).
  • parse_mode=None for the agent-supplied question text — agent prompts aren't trusted Markdown.

…d Telegram adapter

In the messaging gateway, AIAgent.clarify_callback was never set, so the
clarify tool always returned "Clarify tool is not available in this
execution context." for users on Telegram. CLI and TUI paths set this
callback during agent construction; gateway/run.py was missing the
equivalent wiring.

Bridges the sync callback contract (question, choices) -> str into an
async send_clarify_prompt coroutine on the platform adapter. The agent
thread parks on a threading.Event with a 120s timeout; the inline-button
click handler in the adapter resolves the event with the chosen string.

The bridge degrades gracefully: adapters that don't define
send_clarify_prompt / _clarify_state return "" so the agent falls back
to its own judgement instead of crashing. Only the Telegram adapter
gets the inline-keyboard primitives in this PR.

Closes NousResearch#21032
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/telegram Telegram bot adapter labels May 7, 2026
@Tranquil-Flow

Copy link
Copy Markdown
Contributor Author

Closing — clarify in gateway/Telegram mode now works on main via a different architecture.

Main has a dedicated tools/clarify_gateway.py module (resolve_gateway_clarify, mark_awaiting_text, get_pending_for_session) wired into:

  • gateway/run.py:6577–6594 — intercepts text replies and resolves pending clarifies
  • gateway/platforms/telegram.py:459/2465/2518send_clarify() renders inline buttons with cl:<id>:<idx> callback data, _clarify_state tracks pending IDs per session

The protocol shape differs from this PR's (cl: prefix + Dict[str, str] session-key state vs this PR's clarify: prefix + threading.Event), but the user-visible outcome is the same: clarify buttons appear and resolve correctly in gateway+Telegram mode. The original gateway-only regression is fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clarify tool breaks in gateway/Telegram mode — missing callback wiring

2 participants