Originally created by: SergiioB
The Telegram remote-control channel now posts an editable "Thinking…" bubble the moment an owner message lands, updates it live with the turn's activity, and removes it when the reply is ready.
Today the only feedback is the native chatAction: "typing" dots. They are easy to miss and disappear the instant a fast reply lands, so a short answer gives no visible sign the message was received. This adds a clear, persistent acknowledgement that also surfaces what the agent is doing on longer turns.
🤔 Thinking….⚙️ Working… (step N), 🔧 <tool> (from tool_call_parsed), ✅ <step summary> (from step_finished), 🔁 Repeating <tool> (×N) (from loop_detected).TelegramProgressIndicator in inbound-handler.ts, wired into dispatchToRuntime through the existing eventHook.deleteMessage to the TelegramApi surface (grammy's bot.api already provides it).remove() before the initial sendMessage resolves still cleans up the just-posted message.telegram.* flag if you prefer.vitest run src/channels/telegram/), including the 23 inbound-handler tests.serve deployment against a remote llama-server.Happy to add a dedicated unit test for the indicator lifecycle and/or put it behind a config flag if that helps merge.
Originally posted by: sosidudku1
Thanks @SergiioB, and sorry it took four days to get to you. The chain-serialised start/update/remove is genuinely careful work, I traced the fast-turn race you describe in the comments and it holds up.
One thing I do not think you had in view.
progressLabelechoesstep_finished.summaryinto the bubble, and for a single-tool step that summary is not a label, it is the tail of the tool's raw output.compressToolResultbuilds it from the last non-blank lines of whatever the tool printed, so anos.fs.readon a config file, a grep hit, or a stack trace ends up in the Telegram message. Your 80 char clip bounds the length but not the content, and 80 chars is plenty for a file path or the start of a key. Telegram history is forwardable and outlives the session, so I would rather the bubble stayed on things you construct yourself,🔧 <tool>and the step counter, and dropped the summary echo.Second, lighter: the
TelegramApifake ininbound-handler.test.tsonly implementssendMessage, so everyeditMessageText?.()anddeleteMessage?.()in your class silently no-ops under test. The suite stays green whether the indicator works or not. If you add the lifecycle test you offered, adding those two to the fake would make it meaningful.Originally posted by: Ooooze
+1 on the summary-echo concern @sosidudku1 raised — traced it myself:
progressLabel'sstep_finishedbranch forwardsevent.summarystraight fromcompressToolResult's tail-of-raw-output, and the 80-char clip bounds length, not content. Telegram history is forwardable and outlives the session, so agreed this should drop to just🔧 <tool>+ the step counter, no summary text.Two more before this can land:
inbound-handler.tswas already at 347 lines pre-PR (over the repo's 300-line guideline) and this pushes it to 510. Could you pullTelegramProgressIndicator+progressLabel+truncateForProgressinto their own file, e.g.telegram-progress-indicator.ts?TelegramApifake ininbound-handler.test.tsonly implementssendMessage—editMessageText/deleteMessageare optional and no-op when absent, so every call the indicator makes to them is untested. Since you offered to add the lifecycle test, adding both methods to the fake would make it actually exercise start/update/remove (including the fast-turn race you handled carefully in the code).Happy to take another look once those land — the throttling/chain-serialization design itself is solid.
Originally posted by: sosidudku1
Following up with three additions from a deeper pass, on top of the summary-echo drop, the file split and the real fake-API tests already requested above:
disable_notification: true. Right now every owner message produces two push notifications, one for the Thinking bubble and one for the reply. On fast turns the bubble also flashes and disappears before it can be read.retry_afterwhen Telegram returns 429. The practical per-chat limit is about one message per second and 700 ms sits above it on chatty turns. The risk is not the bubble itself: a flood-wait earned by edits applies to the whole chat and can delay or drop the final reply, and edits currently swallow the 429 and keep going.telegram.progressIndicator, defaulttrue, plumbed the same wayparseModewas. Always-on needs a kill switch in production.Non-blocking: the throttle drops the newest label instead of deferring it, so the last meaningful state (for example the tool name) can be lost until the next event. Fine as a follow-up.
The chain serialization and best-effort isolation still hold up on the deeper pass. Once these plus the earlier items land, this is good to go from my side.
Ticket changed by: Ooooze