fix(channels): support Telegram private bot topics#3211
Conversation
Letta Code (agent-c2adbf5c-8419-4211-8cd8-3740db164974) LET-9433 Route Telegram private bot topic messages through existing DM bindings while preserving the topic id for replies. Keep ordinary root DM sends from inheriting stale route-level thread ids. 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta Code <noreply@letta.com>
|
@carenthomas quick nudge when you have a chance: this is green and waiting on review. The change is scoped to Telegram channel routing/reply behavior for private bot topics, with regression tests for root DMs and private-topic |
Preserve Telegram private bot topic ids on inbound routing and outbound replies while keeping stale direct-route thread ids out of MessageChannel sends. 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta Code <noreply@letta.com>
Compact a guard after formatting expanded the Telegram private-topic repair enough to trip the source file-size check. 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta Code <noreply@letta.com>
amelia-letta
left a comment
There was a problem hiding this comment.
buildDirectReplyOptions (used here and in registry-routes.ts) sets replyToMessageId to msg.threadId ?? msg.messageId, which means for private bot topics it uses the thread ID as the reply target. This creates a reply to the thread creation message instead of the user's message.
For "not on allowed list", pairing instructions, and unbound route instructions, you probably want to reply to the user's actual message (use messageId), not anchor to the thread root.
The function in registry-presentation.ts should likely be:
replyToMessageId: msg.messageId ?? undefined,Or if you want no reply anchor for these system messages, just omit replyToMessageId entirely and only pass threadId.
👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta Code <noreply@letta.com>
| await adapter.sendDirectReply( | ||
| msg.chatId, | ||
| "You are not on the allowed users list for this bot.", | ||
| buildDirectReplyOptions(msg), |
There was a problem hiding this comment.
buildDirectReplyOptions sets replyToMessageId to threadId ?? messageId, so for private bot topics this replies to the thread creation message instead of the user's message. For "not on allowed list", pairing instructions, and unbound route messages, you probably want to reply to the actual user message (use messageId), not anchor to the thread root.
Summary
Fixes LET-9433.
Telegram private bot topics send
message_thread_ideven though the chat id is a positive private-chat id. The existing Telegram adapter preserved the inbound field, but the route lookup treated that topic id as a separate unbound DM route, and outbound helpers discarded thread ids for positive chat ids. Result: private bot topic messages either got unbound-route replies or lost the topic id on response.What changed
message_thread_idfor private bot topics.MessageChannel; only inbound turn-source topic ids or explicit request thread ids are sent.Behavior matrix
message_thread_idunless a real request/turn-source thread id is present.channel === "telegram",chatType === "direct", and a non-emptythreadId.Limits
This does not create one route per Telegram private bot topic. Private bot topics intentionally reuse the root private DM route unless an exact topic route already exists. It also does not change Telegram API behavior for invalid topic ids; bad thread targets still surface through the existing send failure path.
Validation
bun run checkbun test src/channels/registry-route-lookup.test.ts src/channels/telegram-adapter-ingress.test.ts src/channels/telegram-adapter-runtime.test.ts src/channels/telegram-adapter-lifecycle.test.ts src/channels/telegram-adapter-typing.test.ts src/channels/telegram-registry.test.ts src/channels/message-channel-telegram.test.ts src/tools/impl/message-channel.test.ts src/channels/registry-command-routing.test.ts src/channels/registry-controls.test.ts src/channels/registry-pairing.test.ts src/channels/registry-presentation.test.ts src/channels/registry-lifecycle.test.ts