Skip to content

fix(channels): support Telegram private bot topics#3211

Open
overlord-letta wants to merge 4 commits into
mainfrom
overlord/LET-9433-telegram-private-topics
Open

fix(channels): support Telegram private bot topics#3211
overlord-letta wants to merge 4 commits into
mainfrom
overlord/LET-9433-telegram-private-topics

Conversation

@overlord-letta

@overlord-letta overlord-letta commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes LET-9433.

Telegram private bot topics send message_thread_id even 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

  • Added a shared inbound route lookup helper that falls Telegram direct-topic messages back to the root direct route only when there is no exact topic route.
  • Wired inbound delivery, slash-command status/cancel routing, and raw pause/resume route lookup through that helper so private bot topics use the existing DM route consistently.
  • Passed inbound direct-reply options for allowlist, pairing, unbound-route, and unbound-Telegram-bot setup replies so those replies can target the same private bot topic.
  • Allowed Telegram outbound helpers to send an explicit message_thread_id for private bot topics.
  • Kept ordinary root private DM sends from inheriting stale route-level thread ids by filtering direct-route thread ids in MessageChannel; only inbound turn-source topic ids or explicit request thread ids are sent.

Behavior matrix

  • Telegram private topic inbound with only the root DM route: falls back to the root route and preserves the inbound topic id on the turn source.
  • Telegram private topic inbound with an exact topic route: uses the exact route; a disabled exact route remains authoritative and does not silently fall back to root.
  • Telegram root private DM outbound: no message_thread_id unless a real request/turn-source thread id is present.
  • Telegram group/forum topics: existing negative chat-id/topic behavior remains supported.
  • Non-Telegram route lookup: unchanged because the fallback predicate is restricted to channel === "telegram", chatType === "direct", and a non-empty threadId.

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 check
  • bun 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

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>
@sarahwooders
sarahwooders requested review from carenthomas and just-cameron and removed request for just-cameron July 3, 2026 22:19
@overlord-letta

Copy link
Copy Markdown
Contributor Author

@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 message_thread_id handling.

lettamate and others added 2 commits July 10, 2026 17:11
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 amelia-letta left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants