Repository navigation
feat: Messaging Channels 1, the seam, Linked identities and Telegram (#1044) - #1064
Merged
Merged
Conversation
Defines the messaging adapter interface (check, listen, send, editButtons, download, deepLink, limits) and the channel_account table, with encrypted credentials, the channels.accounts list/save/remove RPCs, one listener per active account under a session advisory lock across instances, needs-attention on a credentials rejection, key rotation, the fake messaging adapter and the Bot header's Channels popover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The committed listeners were an older copy: the version stayed a number while listenerAccount returns the ciphertext, which broke check-types, and the review's session, stop and rejection fixes were missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1050) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ates, retry_after (#1050) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
bin/botsdk.mjs loads the sources with Node's strip-only TypeScript, which refuses constructor parameter properties; declare the field instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1050) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds the linked_identity and link_code tables, the channels.identities list, unlink and linkCode RPCs, linking an unknown sender by a link code with a system notice, user delete and export, the Settings section and the Channels popover's Now link yourself. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…de (#1046) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…to feat/1046-linked-identities
…ance that stops them (#1046) Adds an optional debug method to the SDK Logger and logs each dropped messaging update without its text or code. The channel plugin made its listeners in init, so composing the plugin into a second instance after start left the started listeners unreachable by stop and the pool open. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A private-chat message from the user's Linked identity becomes a user Message plus a Trigger in the Bot's Conversation, recording the platform as its Channel; files are kept as Attachments. The Trigger's channel is now text (migration), the hand-in shared with email, and the web app marks user Messages from a messaging Channel "via <platform>". Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… only when kept (#1047) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A Turn's reply goes back to the messaging Channel of the latest Trigger it took, and the notification routes can name telegram (refused unless linked; unhonourable routes fall back to the defaults). Delivery extends the channel.deliver job with a messaging payload; a credentials rejection marks the Channel account needs_attention. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…l rules (#1048) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
#1051) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A choice and an Approval go out as buttons that a press or a typed reply answers through the app's own paths, a Secret request as a link only, files as documents or a link to the app. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… too (#1044) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…plugin (#1044) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… unique-violation helper (#1044) A link code takes 5 wrong guesses on any of the user's Bots, then is void (attempts column folded into the unreleased linked_identity migration). Presses, typed answers and link codes act once per source id per Channel account. linkFromCode takes the owner its caller already read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ages, webhook Approvals link to the app (#1044) A delivery keeps where a Message's buttons were sent (buttonsOn on its parts), so a typed answer edits them as a press does. A typed answer the app refuses (a paused Bot) goes on as an ordinary Message. The Other wait is per SDK instance and expires after 10 minutes. Approving a webhook Routine on a Channel says so and links to the app for the URL, never the token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…el, never to one without an adapter (#1044) A reply follows the Turn's latest Trigger to any messaging adapter the Host installed. A route or Trigger whose platform has no adapter can't be honoured, so the defaults decide. The default "the Channel the user last wrote from" includes a messaging Channel, and the Email popover's nextChannel names it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A Turn whose latest user Trigger came from a messaging Channel that can't carry the reply now follows the notification routes, so an urgent Message isn't lost. - Only a paused Bot or an already answered choice lets a typed answer go on as a Message. Other refusals are dropped with their reason. - A failed send of the webhook-approval notice is logged, not thrown, so the approval isn't redone as "Already answered". - An update's sourceId is remembered before it is handled, so concurrent copies act once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Spec 1 of the Messaging Channels Wayfinder. A user can now talk to their Bot on Telegram.
channelplugin, with Channel accounts (a platform bot's token per Bot, encrypted under the ADR 0010 keyring). Listeners run one per account across instances, held by a Postgres advisory lock.telegram) reach Telegram.@botsdk/channel-telegram: long polling with plainfetch, so no new dependency.Closes #1044. Tickets #1045–#1050 are closed. #1051's Host wiring is merged, and the ticket stays open for the owner's manual checklist with a real @Botfather bot. ADR 0033 and the CONTEXT terms Channel account and Linked identity come in with this branch.
How it was tested
fakeMessagingAdapterin@botsdk/testingon real Postgres:channel-accounts,linked-identities,channel-messaging-inbound,channel-messaging-outandchannel-messaging-controls;Decisions I made
bot_trigger.channelchanged from an enum totext, so any adapter id can be recorded.notification_routegainedtelegram. Approved on Linked messages wake the Bot #1047 and on One platform bot per Bot, or one per Host #1036.link_code.attemptscolumn inside the Linked identities and link codes #1046 migration.Loggergains an optionaldebug?. The silent drops of strangers and bad codes log there.nextChanneloutput widened from"web" | "email"to a string, which may be a platform id such astelegram.getMe,getUpdates,deleteWebhook). One user blocking the bot doesn't mark the account needs attention.Risk
botmigrations:channel_account,linked_identity(withlink_code),trigger_channel_text(drops thebot_trigger_channelenum type) androute_telegram.rotateKeyscovers them.channelplugin option.🤖 Generated with Claude Code