The Agent Finished, but Telegram Is Down: A Delivery Queue in Rust's agent-hub
When the agent has already finished its work but Telegram is unavailable, you can't just repeat the request to the model: it costs tokens and may modify files again. The author of agent-hub, written in Rust, separated obtaining the result from delivering it. The final text is first written to disk, and a separate task sends it in parts and retries after a failure or restart.
AI-processed from Habr AI; edited by Hamidun News
A familiar situation: the agent read the code, fixed the files, verified the result and wrote an excellent reply. And at that moment Telegram is unavailable. For the agent the work is done; for the user nothing has happened. Running the task again is unappealing: it costs time, tokens and risks the agent changing something once more. This is exactly what the author of agent-hub ran into: a Rust application that runs AI agents locally and communicates with them through topics of a Telegram group.
Separate the Result from Delivery
The main idea is simple: receiving the reply and sending it are two different events. When an agent's turn finishes successfully, the hub first saves the final text to a local file, topics.results.json, and puts it in a queue, and only then contacts Telegram. A separate task handles sending and retries. After a restart it reads the state from disk and continues delivery without a new model call.
What goes into the queue is not the whole conversation, but the consecutive text blocks after the last displayed tool call. Attachments, intermediate messages, questions and permission buttons are not repeated automatically.
An important detail: the hub stores already prepared parts, not the original Markdown. If the markup conversion changes after an app update, message boundaries stay the same, and the saved number of the next part won't drift to a different piece of text.
How It Survives Failures
The cycle goes like this: find a ready entry, reserve it for 60 seconds and save that to disk, send the current part, record the acknowledgment, move on to the next one. The reservation is needed in case the process dies in the middle of a network request: the entry won't stay locked forever. A request to Telegram is limited to 20 seconds, and sending proceeds at no more than one part per second.
On a temporary error the pauses grow: 5, 15, 30, 60, 120, 300, 600 seconds. After eight consecutive failures, delivery stops until a manual retry. If Telegram itself asks to wait (RetryAfter), that pause is stored for the whole group's queue and is shortened neither by a manual retry nor by a restart. If Telegram rejected the HTML, the hub tries to send plain text, and if that fails too, it waits for human intervention.
You can manage the queue from a topic: /last shows the last saved reply without calling the model, /outbox shows pending deliveries with "Retry" and "Delete" buttons. If reading the storage fails, the interface shows "unknown" rather than zero, so a disk failure doesn't look like an empty queue.
Where the Guarantees End
The author honestly does not promise exactly-once delivery. Telegram may have accepted the message while the acknowledgment was lost; or the hub failed to write it to disk before stopping. Then the part will be sent again. That is why each part begins with a turn ID and a number like "part 2/3": the label doesn't remove the duplicate, but shows that it is a repeat, not a new reply.
There are also hard limits: up to 50 pending replies and 8 MiB in the queue, retention of up to 7 days, up to 64 KiB per text. If the queue is full, old entries are not evicted, and the new reply is sent without automatic retry and with a warning. The file is created with 0600 permissions, but it contains the agent's replies, so keep backups in mind.
Tests ran on Linux with a mocked Telegram and temporary storage: unavailable network, restart after partial sending, lost acknowledgment, RetryAfter, HTML error, overflow, group change. Power loss was not simulated.
Takeaways
The pattern is universal for any agent tool: save the result before delivery, store progress per part, give retries identifiers, and don't promise exactly-once where it doesn't exist. It is essentially a small outbox pattern, familiar from backend work, only for agents, where a rerun is expensive and unsafe. The code is open under the MIT license.
- Nov 12, 2026Claude in Chrome requires approval for every click on category 3 sites
- Nov 12, 2026Norwest raises record $4 billion fund, betting on agents, voice and physical AI
- Nov 12, 2026AWS shows how 100% retrieval precision can hide an AI agent's failure
- Nov 12, 2026Garage Eight dropped peer approvals for 69 MRs in an internal service using AI review
Key Questions Answered
What are the key details on Separate the Result from Delivery?
The main idea is simple: receiving the reply and sending it are two different events. When an agent's turn finishes successfully, the hub first saves the final text to a local file, topics.results.json, and puts it in a queue, and only then contacts Telegram. A separate task handles sending and retries. After a restart it reads the state from disk and continues delivery without a new model call.
How It Survives Failures?
The cycle goes like this: find a ready entry, reserve it for 60 seconds and save that to disk, send the current part, record the acknowledgment, move on to the next one. The reservation is needed in case the process dies in the middle of a network request: the entry won't stay locked forever. A request to Telegram is limited to 20 seconds, and sending proceeds at no more than one part per second. On a temporary error the pauses grow: 5, 15, 30, 60, 120, 300, 600 seconds.
Where the Guarantees End?
The author honestly does not promise exactly-once delivery. Telegram may have accepted the message while the acknowledgment was lost; or the hub failed to write it to disk before stopping. Then the part will be sent again. That is why each part begins with a turn ID and a number like "part 2/3": the label doesn't remove the duplicate, but shows that it is a repeat, not a new reply. There are also hard limits: up to 50 pending replies and 8 MiB in the queue, retention of up to 7 days, up to 64 KiB per text.
Test Frontier LLMs & Autonomous Agents in One Workspace
Run Claude Opus 5.5, GPT-6, Gemini 3.1, and DeepSeek side-by-side in Nomad AI (nomadnet.ai) — unified model routing, deep research, and multimodal workflows under one subscription.
The AI world, distilled — once a week
Seven stories that actually mattered, hand-picked. No noise, no reposts, no press releases.
Done! Check your inbox for a confirmation.