Skip to content

feat(channels): Telegram channel (custom bot) - #6679

Open
mmabrouk wants to merge 25 commits into
channels/fix-approval-card-on-parkfrom
channels/telegram
Open

feat(channels): Telegram channel (custom bot)#6679
mmabrouk wants to merge 25 commits into
channels/fix-approval-card-on-parkfrom
channels/telegram

Conversation

@mmabrouk

@mmabrouk mmabrouk commented Sep 8, 2026

Copy link
Copy Markdown
Member

What

Adds Telegram as a channel, for a customer's own bot (the custom-bot path), mirroring the Slack adapter. Also fixes two shared delivery bugs surfaced by Telegram.

Why

Telegram is the next platform after Slack. The goal is one agent answering in a chat, with memory, behind the feature flag.

Changes

The Telegram adapter (core/channels/adapters/telegram/)

  • Verifies the bot token with getMe; verifies each webhook with the X-Telegram-Bot-Api-Secret-Token header.
  • Reads the bot from the per-bot URL path, since a Telegram update carries no bot identity.
  • Parses messages and button presses; classifies direct message, group, and forum topic; decides when a group message is addressed to the bot.
  • Sends and edits messages, splits long text, renders inline-keyboard buttons.
  • Declares no history (backfill off) and no chat listing, which the Bot API cannot do.
  • Shows the native "typing…" action at turn start and posts the answer as a fresh message (no placeholder edit).
  • Keys a chat as one conversation, so a chat has memory across messages.

Ingress and activation

  • A per-bot route /telegram/events/{routing_token}/, on the public auth allowlist by prefix. The stored webhook secret is what authorises.
  • A new activate_connection adapter hook runs setWebhook after the row is stored, because that call writes on Telegram's side and cannot run in the pre-store verify step. The webhook secret is minted and vaulted at create time.

Secrets

  • A telegram secret kind and a webhook_secret credential field.

Two shared fixes (affect all channels)

  • Rename the turn indicator to "Thinking…".
  • Make outbox delivery idempotent: turn_ended is published twice, which posted a duplicate message on a post-only channel and a wasted "not modified" edit on Slack. A row already SENT with identical content is now skipped.

Testing

  • 740 channels unit tests pass; ruff clean.
  • Live end-to-end on a real Telegram account: text reply, memory across two messages (asked to remember 42, then recalled it), single reply per message, model answering via the Pi subscription.

Not in this PR

  • Hosted (Agenta-owned) Telegram bot with the QR and account-link flow.
  • Media input (images, voice).
  • The connect UI.

…tion

Adds a Telegram channel for a customer's own bot, mirroring the Slack adapter.

- Adapter: getMe verification, X-Telegram-Bot-Api-Secret-Token webhook check,
  per-bot-path connection locator, update parsing (message and callback_query),
  sendMessage/editMessage, deactivation on kicked/left. No history (backfill
  off) and no chat listing (discover_spaces empty), which Telegram cannot do.
- Ingress: a per-bot route /telegram/events/{routing_token}/, added to the
  public auth allowlist by prefix (the stored webhook secret authorises).
- Activation: a new adapter.activate_connection hook runs setWebhook after the
  row is stored (setWebhook writes on Telegram's side, so it cannot run in the
  pre-store verify step). The webhook secret is minted and vaulted at create.
- Secrets: a telegram ChannelSecretKind and a webhook_secret credential field.
- Tests: unit tests for the adapter and the interface method list.
The placeholder shown while a turn runs now reads "Thinking…" in every channel
(Slack, Telegram, Agenta), from the one shared INDICATOR_TEXT constant.
…ended

turn_ended is published twice (SessionTurnsService.complete_turn and the records
worker post-commit), so on_turn_ended can run twice for one turn. On an edit
channel the second call was a wasted "message is not modified" that overwrote the
SENT row; on a post-only channel (Telegram) it posted a duplicate message the
user saw. _send now skips a row already SENT with identical content.
@vercel

vercel Bot commented Sep 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
agenta-documentation Ready Ready Preview Sep 9, 2026 9:07am UTC

Request Review

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📘 Docs preview

Status ✅ Ready
Preview https://pr-6679-agenta-docs-preview.mahmoud-637.workers.dev/docs
Inspect Actions run
Commit bf4902b6be94542611053820d5f2e3dafcc6b5c9

This comment updates in place on every push.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • release/.*

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Advanced

Run ID: 42ff4483-9f5a-4792-bccc-e2b9e61e859e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Summary

Summary by CodeRabbit

  • New Features

    • Added Telegram as a supported channel for connecting bots and receiving updates.
    • Added Telegram messaging with replies, typing indicators, message editing, inline buttons, and support for private chats and groups.
    • Added webhook security and automatic bot webhook setup.
    • Added channel settings screens for connecting, managing, and disconnecting Slack and Telegram integrations.
    • Added connection status, issue alerts, chat management, and direct-message/group behavior controls.
  • Bug Fixes

    • Prevented duplicate channel messages when events are delivered more than once.
    • Improved protection of bot tokens and webhook credentials in responses.

Walkthrough

The pull request adds a Telegram custom-bot channel adapter with webhook ingress, secret handling, message parsing, outbound delivery, tests, supporting design documents, and a first-pass Slack and Telegram settings interface.

Changes

Telegram channel runtime

Layer / File(s) Summary
Adapter contracts and Telegram mapping
api/oss/src/core/channels/adapters/...
Defines Telegram capabilities, webhook routing, signature checks, message addressing, content rendering, inline keyboards, and Bot API calls.
Webhook runtime and connection activation
api/oss/src/apis/fastapi/channels/ingress.py, api/oss/src/core/channels/service.py, api/oss/src/middlewares/auth.py, api/oss/src/core/secrets/..., api/oss/src/tasks/asyncio/channels/outbox.py
Registers public Telegram ingress, activates per-bot webhooks, persists and redacts Telegram credentials, preserves secondary secrets, and skips duplicate deliveries.
Adapter and runtime validation
api/oss/tests/pytest/unit/channels/..., api/oss/tests/pytest/unit/secrets/...
Tests Telegram parsing, signature validation, rendering, Bot API requests, webhook setup, secret handling, interface behavior, and duplicate delivery prevention.

Channel design and settings UI

Layer / File(s) Summary
Requirements and operational documentation
docs/design/channels-research/v2/*.md
Documents Telegram requirements, hosted-bot planning, media scope, operational setup, progress notes, and adapter review findings.
Channels settings first pass
docs/design/channels-research/v2/ui-firstpass/channels-components/*, docs/design/channels-research/v2/ui-firstpass/channels-connect-ui.patch
Adds shared channel types, connection and management flows, local connection state, icons, helpers, navigation bindings, and Slack and Telegram settings UI wiring.

Priority: ➖ Normal

Estimated code review effort: 5 (Critical) | ~120 minutes

Merge Risk: 🟡 Moderate · up to c86ba

Several reachable Telegram failure and update paths can stop inbound messages or produce duplicate or unintended replies, so these issues should be resolved before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.03% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 93 functions across 30 files. (8 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding Telegram support for customer-owned custom bots. It is concise and specific.
Description check ✅ Passed The description directly explains the Telegram adapter, webhook activation, secrets, shared delivery fixes, testing, and excluded scope. It is fully related to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 29.03% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 93 functions across 30 files. (8 skipped: 8 unsupported.)

✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch channels/telegram

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mmabrouk

mmabrouk commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-pr-6679.up.railway.app/w
Project agenta-oss-clone-spike
Image tag pr-6679-008d526c
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-09-09T09:09:02.585Z

Fixes from the two reviews of PR #6679 (no architecture change):

P1 security/correctness:
- Redact the channel secondary secrets (Slack signing_secret, Telegram
  webhook_secret) in public vault responses; they survived redaction before.
- Preserve those secondary secrets on credential rotation (a token-only update
  no longer drops the webhook secret and breaks inbound verification).
- Roll back a Telegram connection whose setWebhook activation fails, instead of
  leaving a row that looks live but receives nothing.
- Do not deactivate the whole bot connection on a per-chat kick/leave.
- Render the approval card (tool + arguments) as text on Telegram; it was
  dropped, so an approval showed only Approve/Deny with no explanation.
- Stop classifying forum topics and advertising topic support: the chat-only
  thread key would misroute a topic reply. v1 keeps one conversation per chat.

P2:
- Split raw answer text before html-escaping so a split never cuts an entity.
- Translate an invalid-bot-token getMe error to a setup failure, not a 500.
- Match a bot mention by substring (Telegram entity offsets are UTF-16); admit
  a /command only when bare or addressed to this bot.
- Acknowledge a button press (answerCallbackQuery) so Telegram clears its spinner.
- Identify the turn indicator by an explicit marker, not its display text, so a
  real answer equal to the indicator text is never swallowed.

Tests updated and added for each; 844 channels+secrets unit tests pass.
@mmabrouk

mmabrouk commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

Addressed an independent Codex (gpt-astra, xhigh) review and a code-quality review. All P1 and P2 findings are fixed in the latest commit, with no change to JP's architecture: secondary channel-secret redaction and rotation carry-over, failed-activation rollback, per-chat kick no longer deactivates the whole connection, the approval card renders as text, forum topics are not misrouted (one conversation per chat in v1), split-before-escape, invalid-token maps to a setup error, mention-by-substring, callback acknowledgement, and the indicator is marked explicitly rather than by its display text. 844 channels+secrets unit tests pass; live-verified continuity and single-reply on a real Telegram account.

@mmabrouk

mmabrouk commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 12


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Advanced

Run ID: d872753a-3e86-4496-944d-4d868f7130af

📥 Commits

Reviewing files that changed from the base of the PR and between b37c8b2 and c86ba0e.

📒 Files selected for processing (39)
  • api/entrypoints/channel_adapters.py
  • api/oss/src/apis/fastapi/channels/ingress.py
  • api/oss/src/core/channels/adapters/interface.py
  • api/oss/src/core/channels/adapters/slack/adapter.py
  • api/oss/src/core/channels/adapters/telegram/__init__.py
  • api/oss/src/core/channels/adapters/telegram/adapter.py
  • api/oss/src/core/channels/adapters/telegram/capabilities.py
  • api/oss/src/core/channels/adapters/telegram/mapping.py
  • api/oss/src/core/channels/adapters/telegram/signature.py
  • api/oss/src/core/channels/render/dtos.py
  • api/oss/src/core/channels/render/render.py
  • api/oss/src/core/channels/service.py
  • api/oss/src/core/secrets/dtos.py
  • api/oss/src/core/secrets/enums.py
  • api/oss/src/core/secrets/redaction.py
  • api/oss/src/core/secrets/services.py
  • api/oss/src/middlewares/auth.py
  • api/oss/src/tasks/asyncio/channels/outbox.py
  • api/oss/tests/pytest/unit/channels/telegram/__init__.py
  • api/oss/tests/pytest/unit/channels/telegram/test_telegram_adapter.py
  • api/oss/tests/pytest/unit/channels/test_channel_adapter_interface.py
  • api/oss/tests/pytest/unit/channels/test_channels_outbox_worker.py
  • api/oss/tests/pytest/unit/secrets/test_dtos.py
  • api/oss/tests/pytest/unit/secrets/test_write_only.py
  • docs/design/channels-research/v2/design-brief.md
  • docs/design/channels-research/v2/lane3-hosted-telegram-plan.md
  • docs/design/channels-research/v2/media-input.md
  • docs/design/channels-research/v2/ops-todo.md
  • docs/design/channels-research/v2/overnight-progress.md
  • docs/design/channels-research/v2/review-telegram-quality.md
  • docs/design/channels-research/v2/telegram-requirements.md
  • docs/design/channels-research/v2/ui-firstpass/channels-components/ChannelConnectFlow.tsx
  • docs/design/channels-research/v2/ui-firstpass/channels-components/ChannelManagePanel.tsx
  • docs/design/channels-research/v2/ui-firstpass/channels-components/ChannelsPage.tsx
  • docs/design/channels-research/v2/ui-firstpass/channels-components/helpers.ts
  • docs/design/channels-research/v2/ui-firstpass/channels-components/icons.tsx
  • docs/design/channels-research/v2/ui-firstpass/channels-components/index.ts
  • docs/design/channels-research/v2/ui-firstpass/channels-components/types.ts
  • docs/design/channels-research/v2/ui-firstpass/channels-connect-ui.patch

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment thread api/oss/src/core/channels/adapters/telegram/adapter.py
Comment thread api/oss/src/core/channels/adapters/telegram/mapping.py Outdated
Comment thread api/oss/src/core/channels/adapters/telegram/signature.py Outdated
Comment thread api/oss/src/core/channels/service.py
Comment thread api/oss/src/core/secrets/services.py
Comment thread docs/design/channels-research/v2/lane3-hosted-telegram-plan.md Outdated
Comment thread docs/design/channels-research/v2/lane3-hosted-telegram-plan.md Outdated
Comment thread docs/design/channels-research/v2/ops-todo.md
Comment thread docs/design/channels-research/v2/review-telegram-quality.md
Comment thread docs/design/channels-research/v2/telegram-requirements.md
- adapter: best-effort typing and callback calls also swallow httpx transport
  errors, not only Bot API errors, so a timeout cannot block the answer or the
  button routing.
- mapping: match a bot mention on a username boundary, so @agenta_bot does not
  also match @agenta_bot2 and answer an unrequested turn.
- signature: compare the secret token as bytes, so a non-ASCII header raises
  ChannelSignatureInvalid (401) instead of TypeError (500).
- secrets: reject a blank secondary credential (e.g. an empty webhook_secret),
  the same rule as the primary; a blank would break inbound verification.
- service: re-run activation after a Telegram credential rotation, so a rotated
  bot token re-points its webhook; surface a failure instead of a silent break.

Deferred: the outbox atomic-claim finding is a property of JP's shared outbox
worker for all channels, not introduced here; the _send idempotency guard already
prevents the observed duplicate. Left as a separate concern about the worker.
…ferred decision

Group chats need the bot's BotFather group-privacy turned off for Telegram to deliver group
messages; added that to the Telegram setup instructions (Option 1). Recorded Mahmoud's
decision that images/voice are out of scope this release, and that the eventual image flow
uploads via the attachments store (same as the UI), not inline data URLs.
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.

1 participant