Summary
When Claude Code calls AskUserQuestion, the user clicks "Other (type it out)" and replies with text, the bot's "↩️ Answered: …" confirmation message in Telegram is truncated to exactly 100 characters with no ellipsis. The agent receives the full untruncated text — the bug is display-only.
Reported by user on @hetz_lba1_bot (v0.35.3rc, lba-1 staging), affects all prior versions back to when this code path was introduced.
Reproduction
- On any Claude Code chat with
@untether_dev_bot or @hetz_lba1_bot, trigger an AskUserQuestion call (or just send a question the agent will ask back).
- Click the "Other (type it out)" option (or just reply with text if
ask mode is configured for text replies).
- Type a reply longer than 100 characters (e.g. "You tell me, please - please list all of the next tasks we can begin implementing/running now here in the chat").
- Observe the bot's confirmation:
↩️ Answered: You tell me, please - please list all of the next tasks we can begin i — cut off mid-word, no ellipsis.
Screenshot evidence (from reporter): user's typed reply "… now here in the chat" vs the "Answered:" echo which ends at "… begin i".
Impact
- Cosmetic / UX only. Agent still receives the complete reply and responds correctly.
- User has no visual confirmation that the full message was sent — looks like Untether dropped half their reply.
- Affects all engines that use Untether's
AskUserQuestion flow (Claude Code today; any future engine wired through this path).
Root cause
Two hardcoded text[:100] slices in src/untether/telegram/loop.py:
loop.py:2378 — multi-question / options flow (final answer reply):
await reply(text=f"↩️ Answered: {text[:100]}")
loop.py:2391 — single-question flow:
await reply(text=f"↩️ Answered: {text[:100]}")
No constant, no ellipsis, no word-boundary handling.
Agent path is unaffected (full text reaches Claude)
For reviewer reference — the full reply IS sent to the agent:
loop.py:2357 stores the full text in flow.answers[question_key] (multi-question flow).
loop.py:2389 passes the full text to answer_ask_question(ask_req_id, text).
runners/claude.py:3248-3263 (answer_ask_question) embeds the complete answer in deny_message.
runners/claude.py:3282-3297 (answer_ask_question_with_options) ships the full flow.answers dict via updatedInput.
runners/claude.py:1917-1953 (write_control_response) writes the untruncated JSONL line to Claude Code's stdin.
Proposed fix
Drop the [:100] slice on both lines — these confirmations are ephemeral courtesy echoes and shouldn't surprise the user by hiding their input. Telegram's 4096-char per-message limit is well above any reasonable typed reply, and render_markdown() / split_markdown_body() already exist for the rare overflow case.
# loop.py:2378 and :2391
await reply(text=f"↩️ Answered: {text}")
If keeping a short echo is preferred (e.g. for groups where mostly-blank confirmations clutter the timeline), use an ellipsis and a larger bound:
echo = text if len(text) <= 300 else text[:297] + "…"
await reply(text=f"↩️ Answered: {echo}")
Regression test
tests/test_ask_user_question.py covers the deny-message construction (agent path) but nothing exercises the loop.py confirmation path — which is why this slipped through. Add a test that:
- Registers a pending ask request and routes a reply >100 chars through the loop handler.
- Asserts the FakeTransport
sent log contains the full reply text in the "↩️ Answered:" message.
Reference .claude/rules/testing-conventions.md for stub patterns (FakeTransport, anyio, etc.).
Affected files
src/untether/telegram/loop.py (lines 2378, 2391)
tests/test_ask_user_question.py (or new test file targeting the loop path)
CHANGELOG.md (v0.35.4 entry, ### fixes subsection with this issue link)
Summary
When Claude Code calls
AskUserQuestion, the user clicks "Other (type it out)" and replies with text, the bot's "↩️ Answered: …" confirmation message in Telegram is truncated to exactly 100 characters with no ellipsis. The agent receives the full untruncated text — the bug is display-only.Reported by user on
@hetz_lba1_bot(v0.35.3rc, lba-1 staging), affects all prior versions back to when this code path was introduced.Reproduction
@untether_dev_botor@hetz_lba1_bot, trigger anAskUserQuestioncall (or just send a question the agent will ask back).ask modeis configured for text replies).↩️ Answered: You tell me, please - please list all of the next tasks we can begin i— cut off mid-word, no ellipsis.Screenshot evidence (from reporter): user's typed reply "… now here in the chat" vs the "Answered:" echo which ends at "… begin i".
Impact
AskUserQuestionflow (Claude Code today; any future engine wired through this path).Root cause
Two hardcoded
text[:100]slices insrc/untether/telegram/loop.py:loop.py:2378— multi-question / options flow (final answer reply):loop.py:2391— single-question flow:No constant, no ellipsis, no word-boundary handling.
Agent path is unaffected (full text reaches Claude)
For reviewer reference — the full reply IS sent to the agent:
loop.py:2357stores the fulltextinflow.answers[question_key](multi-question flow).loop.py:2389passes the fulltexttoanswer_ask_question(ask_req_id, text).runners/claude.py:3248-3263(answer_ask_question) embeds the completeanswerindeny_message.runners/claude.py:3282-3297(answer_ask_question_with_options) ships the fullflow.answersdict viaupdatedInput.runners/claude.py:1917-1953(write_control_response) writes the untruncated JSONL line to Claude Code's stdin.Proposed fix
Drop the
[:100]slice on both lines — these confirmations are ephemeral courtesy echoes and shouldn't surprise the user by hiding their input. Telegram's 4096-char per-message limit is well above any reasonable typed reply, andrender_markdown()/split_markdown_body()already exist for the rare overflow case.If keeping a short echo is preferred (e.g. for groups where mostly-blank confirmations clutter the timeline), use an ellipsis and a larger bound:
Regression test
tests/test_ask_user_question.pycovers the deny-message construction (agent path) but nothing exercises theloop.pyconfirmation path — which is why this slipped through. Add a test that:sentlog contains the full reply text in the "↩️ Answered:" message.Reference
.claude/rules/testing-conventions.mdfor stub patterns (FakeTransport, anyio, etc.).Affected files
src/untether/telegram/loop.py(lines 2378, 2391)tests/test_ask_user_question.py(or new test file targeting the loop path)CHANGELOG.md(v0.35.4 entry,### fixessubsection with this issue link)