fix(telegram): file round-trip + clickable permission prompts (#84)#85
Merged
fix(telegram): file round-trip + clickable permission prompts (#84)#85
Conversation
Two missing code paths in bridges/telegram.py produced both halves of issue #84: the bridge had only `_tg_send` (text via sendMessage), so (1) the agent claimed to "send a file" but only emitted text — even with --accept-all there was no sendDocument helper, no inbound `document` handler, and no path for the agent to emit a file; and (2) permission prompts arrived as text containing `[y/N/a(ccept-all)]` that looked like buttons but weren't, because the poll loop only listened for `message` updates and there was no inline-keyboard render. bridges/telegram.py - _tg_send_document(token, chat_id, file_path, caption=None) — manual multipart/form-data upload, 49 MB cap (Telegram hard limit is 50 MB; headroom for encoding overhead). Six failure modes report specific errors to chat: missing, stat failure, empty, oversize, network exception, API ok=false. - Inbound `document` handler in _tg_poll_loop — getFile download, filename sanitized to [A-Za-z0-9._-]_, saved to /workspace if mounted else tempfile.gettempdir(), path echoed to chat, then a path-aware prompt submitted to the agent (caption-overridable). - !sendfile <absolute_path> user command — explicit on-demand send in a daemon thread so the poll loop does not block. - Auto-send on Write — _on_tool_start records the in-flight file_path; _on_tool_end mails it (FIFO-paired). Skipped on Error: / Denied: results, de-duplicated per turn via _sent_files. - _tg_send_keyboard(...) — sendMessage with reply_markup inline_keyboard. Three-layer fallback: Markdown -> no parse_mode same keyboard -> plain text no keyboard. - _handle_callback_query(...) — extracted for testability. Auth check (chat_id), answerCallbackQuery to clear click spinner, prompt_id validation (stale clicks dropped silently), editMessageText appends "✓ Selected: <value>" for scroll-back, fires tg_input_event with the value to unblock the agent. - Poll loop allowed_updates widened to ["message", "callback_query"]. tools/interaction.py - ask_input_interactive grows options=[(label, value), ...] parameter. Telegram branch renders inline_keyboard when options non-empty; Slack / WeChat / terminal ignore options and keep using the text path. Event + prompt_id are registered before the keyboard send so a fast click cannot race in. cheetahclaws.py - ask_permission_interactive passes [("✅ Approve","y"), ("❌ Reject","n"), ("✅✅ Accept all","a")]. runtime.py - RuntimeContext adds tg_callback_prompt_id: str and tg_callback_message_id: int. tests/test_telegram_bridge.py - 27 new pytest cases. urllib.request.urlopen and _tg_api are mocked; threading.Thread is monkeypatched to a synchronous stub for the auto-send hook; an end-to-end test drives ask_input_interactive(options=...) from a worker thread, simulates a click via _handle_callback_query, and asserts the worker returns the clicked value. docs/guides/bridges.md, docs/news.md, README.md, docs/README.CN.MD - New "Permission prompts (clickable buttons)" and "File support" sections; news entry covering both halves of #84. Tests: 696 passed in 43s (was 669 before this change). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two missing code paths in bridges/telegram.py produced both halves of issue #84: the bridge had only
_tg_send(text via sendMessage), so (1) the agent claimed to "send a file" but only emitted text — even with --accept-all there was no sendDocument helper, no inbounddocumenthandler, and no path for the agent to emit a file; and (2) permission prompts arrived as text containing[y/N/a(ccept-all)]that looked like buttons but weren't, because the poll loop only listened formessageupdates and there was no inline-keyboard render.bridges/telegram.py
documenthandler in tg_poll_loop — getFile download, filename sanitized to [A-Za-z0-9.-]_, saved to /workspace if mounted else tempfile.gettempdir(), path echoed to chat, then a path-aware prompt submitted to the agent (caption-overridable).tools/interaction.py
cheetahclaws.py
runtime.py
tests/test_telegram_bridge.py
docs/guides/bridges.md, docs/news.md, README.md, docs/README.CN.MD
Tests: 696 passed in 43s (was 669 before this change).