Skip to content

Upgrading GramIO ​

This is the version-to-version upgrade guide — what changes when you bump gramio and @gramio/* to a newer release. (Coming from another framework? See the migration guides instead.)

How upgrades work ​

GramIO is a family of packages that move together. A few rules make upgrades painless:

  1. Find out what you're on. From your project root:

    bash
    npx gramio-detect-versions --latest
    # or, if you installed the AI skills:
    node skills/gramio-upgrade/detect-versions.mjs --latest

    It lists every gramio / @gramio/* dependency with its installed version, the latest on npm, and a deep link into this page for each one with an upgrade available.

  2. Bump in dependency order. Move the low-level packages first, then the ones that depend on them:

    @gramio/types · @gramio/composer → @gramio/contexts · @gramio/files · @gramio/format → gramio → plugins (scenes, session, views, …) → tooling (@gramio/test).

    A Bot API line (e.g. @gramio/types, contexts, files, format, gramio) should move as one unit — their peer ranges depend on each other.

  3. Don't skip the intermediate hops. Breaking changes accumulate. Going 0.5 → 0.10 means reading every entry in between, not just the endpoints.

  4. Pin around known-bad releases. Where an entry says "upgrade straight to X", do exactly that.

Let your AI assistant do it

If you use the GramIO AI skills, the gramio-upgrade skill automates all of this — it detects your versions, builds the ordered plan from the data below, applies the code edits, and typechecks. Just ask it to "upgrade gramio".

Find your upgrade ​

Pick a package and your current/target versions — or paste the JSON from gramio-detect-versions --latest --json to get the full plan for your whole project at once.

All migrations ​

Everything below is generated from the same data the picker and the CLI use, ordered by dependency layer. Each entry links to the full changelog for that cycle.

@gramio/composer ​

0.3.3 → 0.4.1 · changelog ​

🗑 Deprecated

  • commandsMeta is now unknown-valued — The Telegram-specific shape moved into gramio core. Only matters if you read commandsMeta directly.

✨ New

  • registeredEvents() & EventContextOf<T, E> — registeredEvents() returns the wired-up event names (powers gramio 0.9 auto allowed_updates); EventContextOf extracts global + per-event derives for custom methods.

🐛 Fixes

  • guard() ctx no longer collapses to any — Predicate ctx keeps its type after derive().

0.2.0 → 0.3.3 · changelog ​

✨ New

  • EventContextOf / ContextOf / defineComposerMethods + macro system — Type-safe custom methods that see accumulated derives, plus an Elysia-inspired macro system for declarative per-handler options.
    ts
    bot.macro("adminOnly", {
        preHandler: async (ctx, next) =>
            ctx.from?.id !== ADMIN_ID ? ctx.reply("Admins only") : next(),
    });
    bot.command("ban", banHandler, { adminOnly: true });

🐛 Fixes

  • WeakMap-backed getters in isolation groups — group()/extend() isolation switched from Object.create(ctx) to snapshot/restore, fixing lazy getters (ctx.text, ctx.from) inside isolation groups.

0.1.x → 0.2.0 · changelog ​

✨ New

  • decorate() / when() / inspect() / trace() — decorate() (zero-overhead static context), when() (build-time conditional middleware — props typed Partial), inspect() (read-only metadata), trace() (opt-in instrumentation). createComposer({ methods }) injects typed shorthands. MaybeArray<T> widened to T | readonly T[].

@gramio/schema-parser ​

1.0.1 → 1.1.0 · changelog ​

✨ New

  • Shared-sibling FormattableString detection — Promotes the lone unmarked string field to semanticType: "formattable" when an object has bare parse_mode + bare entities (e.g. InputTextMessageContent).

→ 1.0.1 · changelog ​

✨ New

  • New internal schema engine — Native TypeScript parser of the Telegram Bot API docs that powers @gramio/types (replaces the Rust tg-bot-api crate). Semantic type markers, InputFile | string detection, synthesized Currencies enum with XTR, oneOf unions.

@gramio/types ​

10.2.2 → 10.3.1 · changelog ​

⚠️ Breaking

  • Ephemeral send parameters are nested — Move receiver_user_id, callback_query_id, and replace_callback_query_message into ephemeral_message_parameters. Deprecated top-level aliases are not supported.
    ts
    // Before
    await bot.api.sendMessage({
        chat_id,
        text: "Private reply",
        receiver_user_id: userId,
        callback_query_id: callbackQueryId,
    });
    // After
    await bot.api.sendMessage({
        chat_id,
        text: "Private reply",
        ephemeral_message_parameters: {
            receiver_user_id: userId,
            callback_query_id: callbackQueryId,
            replace_callback_query_message: true,
        },
    });
  • Administrator-right fixtures require welcome-message permission — Constructed ChatAdministratorRights and ChatMemberAdministrator values must include can_send_welcome_messages.

✨ New

  • Complete Bot API 10.3 declaration set — Adds stopped message generation, community chat joined, disabled buttons, force-reply markup, expanded rich-message objects, and new ephemeral-message parameters.

🐛 Fixes

  • Draft stop fields restored — SendMessageDraftParams and SendRichMessageDraftParams both expose can_stop and keep_on_stop, guarded by generation assertions.

10.1.0 → 10.2.0 · changelog ​

✨ New

  • Regenerated for Bot API 10.2 — Additive, non-breaking. Ephemeral Messages: editEphemeralMessageText/Media/Caption/ReplyMarkup + deleteEphemeralMessage, receiverUserId & callbackQueryId params on 13 send methods, Message.receiverUser / ephemeralMessageId, BotCommand.isEphemeral, ReplyParameters.ephemeralMessageId.
  • Communities & subscription updates — Community object, ChatFullInfo.community, community_chat_added / community_chat_removed service messages. New subscription update: Update.subscription + BotSubscriptionUpdated (state: canceled | active | failed) — add "subscription" to allowed_updates to receive it.
  • Rich Messages — structured input — InputRichBlock* write-side blocks (paragraph, list, table, collage, slideshow, details, thinking, math, media blocks, …), InputRichMessage.blocks / media, InputRichMessageMedia, and InputMediaVoiceNote — build rich messages block-by-block instead of only via markdown/html.

10.0.0 → 10.1.0 · changelog ​

✨ New

  • Regenerated for Bot API 10.1 — Additive, non-breaking. Rich Messages (read side): RichText* / RichBlock* structures, Message.richMessage, InputRichMessage + InputRichMessageContent (usable as InputMessageContent in inline / guest / Web App answers), sendRichMessage, sendRichMessageDraft (ephemeral streaming preview), editMessageText.richMessage.
  • Join Request Queries & Polls — User.supportsJoinRequestQueries, ChatFullInfo.guardBot, ChatJoinRequest.queryId, answerChatJoinRequestQuery, sendChatJoinRequestWebApp. Polls: Link + PollMedia.link, InputMediaLink usable as InputPollOptionMedia.

9.6.x → 10.0.0 · changelog ​

⚠️ Breaking

  • Regenerated for Bot API 10.0 — Live photos, guest messages, poll/option media, react-permissions, bot access settings. correctOptionId is now correctOptionIds (array) — update any code reading the singular field.

✨ New

  • New structures — LivePhotoAttachment, BotAccessSettings, SentGuestMessage, poll media / explanationMedia / membersOnly / countryCodes, sendLivePhoto + per-option sendPoll media.

9.5.0 → 9.6.1 · changelog ​

⚠️ Breaking

  • Bot API 9.6 — correctOptionId → correctOptionIds (array) first appears here; managed-bot + poll structures added.

🐛 Fixes

  • Mojibake SendDiceEmoji fixed in 9.6.1 — 9.6.0 briefly shipped corrupted emoji values; 9.6.1 throws on the bad byte sequence and ships clean unicode. Pin ^9.6.1, not 9.6.0.

9.4.2 → 9.5.0 · changelog ​

✨ New

  • Bot API 9.5 types — Member tags, date_time entity, can_manage_tags.

9.4.1 → 9.4.2 · changelog ​

✨ New

  • Migrated to @gramio/schema-parser — Generator moved off the Rust tg-bot-api crate. Precise InputFile | string unions, semantic-typed formattable fields, a Currencies enum (incl. XTR), and the previously-missing APIResponse / APIResponseOk / APIResponseError types.

9.3.0 → 9.4.0 · changelog ​

✨ New

  • Bot API 9.2–9.4 types — VideoQuality, UserProfileAudios, ChatOwnerLeft, ChatOwnerChanged, UniqueGiftModelRarity, button-styling types, and methods getUserProfileAudios / setMyProfilePhoto / removeMyProfilePhoto.

wrappergram ​

1.3.0 → 2.0.0 · changelog ​

Only affects you if you use wrappergram directly — gramio users get bot.api and are unaffected.

⚠️ Breaking

  • Direct results, thrown errors, and opt-in middleware — The Telegram class remains, but API calls now return the Telegram result directly and throw TelegramError by default. @gramio/files is no longer bundled — opt in via @gramio/files/middleware (and @gramio/format/middleware). requestOptions is renamed to fetchOptions.
    ts
    // Before
    const response = await telegram.api.sendMessage({ chat_id, text });
    if (!response.ok) console.error(response.description);
    else console.log(response.result.message_id);
    // After
    import { Telegram, TelegramError } from "wrappergram";
    import { filesMiddleware } from "@gramio/files/middleware";
    
    const telegram = new Telegram(token, { middlewares: [filesMiddleware] });
    const result = await telegram.api.sendMessage({ chat_id, text, suppress: true });
    if (result instanceof TelegramError) console.error(result.code, result.payload);
    else console.log(result.message_id);

✨ New

  • Correct Bot API 10.3 raw API line — Wrappergram exposes the @gramio/types 10.3.1 method surface, including corrected draft fields and strict nested ephemeral_message_parameters.
  • Single Middleware type, TelegramError, suppress — Middleware (ctx, next) => unknown, first-class TelegramError (method/code/payload + real stack), suppress: true (return TelegramError | Result instead of throwing), per-request fetch options.

@gramio/callback-data ​

0.1.1 → 0.2.0 · changelog ​

✨ New

  • Zero-width reply-keyboard payload codec — encode/decode convert strings to and from an invisible UTF-8 suffix; embed/extract attach typed callback payloads to visible reply-keyboard labels and recover them from message text.
    ts
    import { embed, extract } from "@gramio/callback-data";
    const label = embed("⚙️ Settings", navigation.pack({ to: "settings" }));
    const embedded = extract(ctx.text ?? "");
    if (embedded) navigation.safeUnpack(embedded.data);

0.0.11 → 0.1.0 · changelog ​

✨ New

  • safeUnpack() — Never throws on stale buttons; returns a typed discriminated union (SafeUnpackResult<T>). Use it only outside bot.callbackQuery(schema, …) (that path already unpacks into ctx.queryData).
    ts
    const result = data.safeUnpack(ctx.data ?? "");
    if (!result.success) return ctx.answerCallbackQuery({ text: "This button is outdated!" });
  • Optional fields are backward-compatible — Adding optional fields to the end of a schema is now a safe migration — old packed strings unpack with the new fields as undefined. Adding required fields, reordering, or renaming nameId are still breaking.

@gramio/contexts ​

0.11.0 → 0.12.0 · changelog ​

✨ New

  • Native Rich routing — ctx.send(), ctx.reply(), and ctx.editText() accept branded native values from gramio/rich and route them through sendRichMessage/editMessageText.
  • Explicit draft boundary — streamRichMessage() accepts Markdown chunks only and throws for a native structured value instead of dropping its blocks.

🔧 Peer/dep bumps (move together)

  • @gramio/types ^10.3.1

0.10.0 → 0.11.0 · changelog ​

✨ New

  • Bot API 10.3 contexts — MessageGenerationStoppedContext exposes draftId, threadId, chat, chatId, chatType, and send helpers; CommunityChatJoinedContext is registered as a service event.
  • New 10.3 getters — Administrator rights expose canSendWelcomeMessages(); UniqueGiftInfo adds text, wrapped entities, and isPrivate; keyboard wrappers expose forceReply and isDisabled.
  • Rich-message plain-text flattening — Plain-text extraction now includes rich buttons, expandable quotations, document captions, button rows, and credits.

🔧 Peer/dep bumps (move together)

  • @gramio/types ^10.3.1

0.6.1 → 0.7.0 · changelog ​

✨ New

  • Bot API 10 getters & mixins — Message.livePhoto + sendLivePhoto; guest messages (Message.guestQueryId / guestBotCallerUser / guestBotCallerChat, MessageContext.answerGuestQuery(), User.supportsGuestQueries()); deleteReaction / deleteAllReactions, canReactToMessages; managed bot access settings.

0.5.x → 0.6.1 · changelog ​

✨ New

  • Bot API 9.6 — managed bots & polls — managed_bot / managed_bot_created contexts, ManagedBotCreated/Updated, User.canManageBots(), getManagedBotToken()/replaceManagedBotToken(); poll getters (Poll.allowsRevoting/description, PollOption.persistentId/addedByUser, PollAnswer.optionPersistentIds).

🐛 Fixes

  • 0.5.1: AnyBot narrowing — ctx.isPM()/isGroup()/isChannel() no longer collapse to never through AnyBot (contributed by @ttempaa).

0.4.0 → 0.5.0 · changelog ​

✨ New

  • Bot API 9.5 getters — date_time entity getters on MessageEntity (unixTime, dateTimeFormat); ctx.setMemberTag() shorthand; member-tag fields.

0.3.1 → 0.4.0 · changelog ​

✨ New

  • ctx.streamMessage(chunks) — Live-typing drafts via sendMessageDraft, auto-finalizing at 4096 chars, AbortSignal-cancellable. Accepts Iterable/AsyncIterable<MessageDraftPiece>.
    ts
    bot.command("stream", async (ctx) => {
        await ctx.streamMessage(generateTextChunks());
    });
  • 8 new contexts (Bot API 9.2–9.4) — SuggestedPost*Context, GiftUpgradeSentContext, ChatOwnerLeftContext, ChatOwnerChangedContext, VideoAttachment.qualities, User.allowsUsersToCreateTopics().

→ 0.3.1 · changelog ​

✨ New

  • ctx.chatId on callback-query context — No more ctx.message?.chat?.id digging. Contributed by @n08i40k.
  • UniqueGiftInfo TON support — lastResaleCurrency ("XTR" | "TON") + lastResaleAmount; lastResaleStarCount returns a value only when currency is "XTR".

@gramio/files ​

0.8.0 → 0.8.1 · changelog ​

🐛 Fixes

  • Recursive native rich uploads — Multipart detection finds Blob, File, and MediaUpload values when the only upload is inside a nested blockquote, list, collage, slideshow, details block, thumbnail, or cover.

0.7.0 → 0.8.0 · changelog ​

✨ New

  • Recursive rich-message uploads — File extraction traverses references, unions, arrays, and recursive rich blocks, including sendRichMessage rich_message.blocks and rich_message.media.
  • Path descriptors without breaking Extractor — Generated upload metadata adds wildcard path descriptors while preserving the legacy Extractor shape. InputRichBlockDocument uploads are supported; sendRichMessageDraft remains upload-free by Telegram design.

🔧 Peer/dep bumps (move together)

  • @gramio/types ^10.3.1

@gramio/format ​

0.12.0 → 0.12.1 · changelog ​

🐛 Fixes

  • Wrap direct Blob/File media uploads — Native media helpers now distinguish Blob/File MIME types from complete InputMedia objects, so direct uploads serialize correctly.

0.11.0 → 0.12.0 · changelog ​

⚠️ Breaking

  • table() is native — Use markdownTable() when you need the previous Markdown/GFM string. Use blocks([...]) to compose native tables with other structured blocks.

✨ New

  • Upload-friendly media helpers — Native media accepts URLs/file IDs, Blob/File, and complete InputMedia values, including thumbnail and cover options.
  • Native rich composition — Structured values route through sendRichMessage without hand-writing InputRichMessage objects.

0.10.0 → 0.11.0 · changelog ​

✨ New

  • Rich-message builders — Adds quote(content, { expandable, credit }), document({ url, caption }), button(), buttonRow(), and compact tables.
  • Bot API 10.3 mutator coverage — Generated formatting mutation covers rich content in ephemeral edits plus the new document and caption paths.

🔧 Peer/dep bumps (move together)

  • @gramio/types ^10.3.1

0.7.0 → 0.8.0 · changelog ​

✨ New

  • Regenerated for Bot API 10 — Mutators for sendLivePhoto, answerGuestQuery, explanation_media, and per-option poll media.

🔧 Peer/dep bumps (move together)

  • @gramio/types ^10

0.5.0 → 0.7.0 · changelog ​

✨ New

  • formatMiddleware — @gramio/format/middleware exports formatMiddleware for the wrappergram v2 chain (decomposes FormattableString into text+entities before each API call).

🐛 Fixes

  • Markdown block separators preserved — Adjacent blocks (paragraph+list, +blockquote, +code, heading+anything) no longer glue together. Important for LLM-generated content.

0.4.0 → 0.5.0 · changelog ​

✨ New

  • htmlToFormattable() — From @gramio/format/html (peer node-html-parser) — convert HTML to Telegram entities without parse_mode, degrading gracefully to plain text.
    ts
    import { htmlToFormattable } from "@gramio/format/html";
    ctx.send(htmlToFormattable("<b>Bold</b> and <i>italic</i>"));
  • join() array overload — join(items, "\n") instead of join(items, (x) => x, "\n"). Still never use native Array.join() on Formattables (it drops entity offsets).

@gramio/keyboards ​

1.4.0 → 1.5.0 · changelog ​

✨ New

  • Disabled inline buttons — InlineKeyboard.disabled(text, options?) creates Bot API 10.3 disabled buttons.
  • Force reply on keyboard builders — InlineKeyboard and Keyboard now provide forceReply(enabled = true), serialized as force_reply.

🔧 Peer/dep bumps (move together)

  • @gramio/types ^10.3.1

1.3.x → 1.4.0 · changelog ​

✨ New

  • requestManagedBot button — Bot API 9.6 button for picking a managed bot from a Telegram dialog.

→ 1.3.0 · changelog ​

✨ New

  • Button styling — All button methods accept an options arg: style ("danger" | "primary" | "success") and icon_custom_emoji_id. Works on InlineKeyboard and Keyboard.
    ts
    new InlineKeyboard()
        .text("Delete", "delete", { style: "danger" })
        .text("Confirm", "confirm", { style: "success" });

@gramio/storage ​

1.x → 2.0.0 · changelog ​

⚠️ Breaking

  • Storage<Data> now constrains keys to keyof Data — Value types are inferred from the key. storage.get<SomeType>("key") no longer overrides the return type — define your key→value map as the Data type parameter on the constructor instead.
    ts
    // Before
    const v = await storage.get<User>("user:1");
    // After
    type Data = Record<`user:${number}`, { name: string; age: number }>;
    const storage = inMemoryStorage<Data>();
    const user = await storage.get("user:1"); // ✅ { name; age } | undefined

🐛 Fixes

  • inMemoryStorage<Data>() generic flows through — No more implicit any from a dropped generic.

@gramio/storage-redis ​

→ ioredis peer dependency · changelog ​

⚠️ Breaking

  • ioredis is now a peer dependency (install step) — ioredis is no longer bundled — install it yourself. Later Bun's native RedisClient is auto-selected and the ioredis peer became optional; explicit sub-paths /ioredis and /bun were added.
    ts
    npm install @gramio/storage-redis ioredis

@gramio/storage-sqlite ​

→ 1.0.0 · changelog ​

✨ New

  • Node.js support (dual runtime) — 1.0.0 adds node:sqlite (DatabaseSync) alongside Bun's bun:sqlite; the right impl is auto-selected — no code change. (Adapter first landed Bun-only.)

gramio ​

0.15.0 → 0.15.1 · changelog ​

🐛 Fixes

  • Require the fixed native media path — gramio now requires @gramio/format ^0.12.1 so direct Blob/File media helpers are wrapped as InputMedia objects before multipart extraction.

🔧 Peer/dep bumps (move together)

  • @gramio/format ^0.12.1

0.14.0 → 0.15.0 · changelog ​

⚠️ Breaking

  • Native Rich DSL exposed from gramio/rich — table() now creates a native table; replace Markdown calls with markdownTable().

✨ New

  • Native values route through context helpers — ctx.send(), ctx.reply(), and ctx.editText() accept native structured rich values through the updated contexts dependency line.

🔧 Peer/dep bumps (move together)

  • @gramio/contexts ^0.12.0, @gramio/files ^0.8.1, @gramio/format ^0.12.0

0.13.0 → 0.14.0 · changelog ​

✨ New

  • Bot API 10.3 update routing — stopped_message_generation is included in default, all, and handler-derived allowed_updates filters; stopped-message-generation and community-chat-joined updates route to their typed contexts.
  • Strict raw Bot API surface — bot.api.* remains one-to-one with Telegram methods and accepts the Bot API 10.3 nested ephemeral_message_parameters shape only.

🔧 Peer/dep bumps (move together)

  • @gramio/types ^10.3.1, @gramio/contexts ^0.11.0, @gramio/files ^0.8.0, @gramio/format ^0.11.0, @gramio/keyboards ^1.5.0, @gramio/test ^0.8.0

0.9.0 → 0.10.0 · changelog ​

No call-site break — the change is the Bot API 10 dependency line; bump the peers below together.

✨ New

  • bot.guestQuery(trigger?, handler) — Handle the new Bot API 10 guest_message update. Reply with ctx.answerGuestQuery(result) (a single InlineQueryResult), not ctx.send/reply.
    ts
    import { InlineQueryResult, InputMessageContent } from "gramio";
    
    bot.guestQuery(/^help/i, (ctx) =>
        ctx.answerGuestQuery(
            InlineQueryResult.article("help", "Help", InputMessageContent.text("How can I help?")),
        ),
    );
  • bot.chosenInlineResult(callbackData, handler) — Pass a CallbackData schema to filter on result_id and get a typed ctx.queryData (mirrors callbackQuery(schema, …)).
    ts
    import { CallbackData } from "gramio";
    
    const card = new CallbackData("card").number("id");
    bot.chosenInlineResult(card, (ctx) => {
        ctx.queryData.id; // ✅ typed as number
    });
  • No-trigger bot.inlineQuery(handler) overload — Matches any inline query — handy for the auth-redirect 'answer with empty results + login button' pattern.
  • Plugin-author helpers re-exported from gramio — WithDerives, WithEventDerive, WithDecorate, WithExtend, DeriveHandler are now exported from gramio directly.

🐛 Fixes

  • No lost updates when stopping mid-batch — bot.stop() landing mid-getUpdates no longer advances the offset on a dropped batch — Telegram re-delivers it.
  • Typed bots assignable to webhookHandler — Bots with derives/plugins/macros no longer need as any to pass to webhookHandler.

🔧 Peer/dep bumps (move together)

  • @gramio/types ^10, @gramio/contexts ^0.7, @gramio/files ^0.5, @gramio/format ^0.8, @gramio/test ^0.7

0.7.0 → 0.9.0 · changelog ​

Nothing required — existing bot.command(name, handler) and all handlers keep working. The below is opt-in or backward-compatible.

✨ New

  • bot.command(name, meta, handler) + bot.syncCommands() — Optional CommandMeta (description, locales, scopes, hide) between name and handler; syncCommands() flushes the Telegram menu (hash-cached, skips unchanged scopes).
    ts
    const bot = new Bot(process.env.BOT_TOKEN!)
        .command("help", { description: "Show help", locales: { ru: "Помощь" } }, helpHandler)
        .command("debug", { hide: true }, debugHandler);
    
    bot.onStart(() => bot.syncCommands());
  • Plugin shorthand methods — command, callbackQuery, hears, reaction, inlineQuery, chosenInlineResult, startParameter now work directly on a Plugin; Plugin.extend(plugin) propagates middleware/hooks/decorators/errors.
  • AllowedUpdatesFilter — auto allowed_updates — allowed_updates is auto-derived from registered handlers, so chat_member / message_reaction / message_reaction_count stop getting silently dropped. Strict mode via bot.start({ allowedUpdates: "strict" }).
    ts
    await bot.start({
        allowedUpdates: AllowedUpdatesFilter.default.add("chat_member").except("poll"),
    });
  • onStart / onStop receive the bot instance — bot.onStart(({ bot, info }) => …) — call bot.api.* during startup/shutdown without a closure.

🐛 Fixes

  • ctx.isPM()/isGroup()/isChannel() no longer narrow to never — Fixed on AnyBot handlers (contexts 0.5.1, pulled in from 0.8.3+).

🔧 Peer/dep bumps (move together)

  • @gramio/types 9.6.1, @gramio/contexts 0.6.1, @gramio/files 0.4.0, @gramio/format 0.7.0, @gramio/keyboards 1.4.0, @gramio/composer 0.4.1, @gramio/test 0.7.0

0.5.0 → 0.7.0 · changelog ​

✨ New

  • Bot API 9.5 support — setChatMemberTag / ctx.setMemberTag(), member-tag fields on ChatMember, can_manage_tags admin right, date_time message entities.

🔧 Peer/dep bumps (move together)

  • @gramio/types 9.5.0, @gramio/contexts 0.5.0, @gramio/keyboards 1.3.1

0.4.x → 0.5.0 · changelog ​

⚠️ Breaking

  • middleware-io engine removed (mostly internal) — gramio now builds on @gramio/composer. If you imported internals (src/queue.ts / UpdateQueue), they're gone (UpdateQueue → EventQueue from @gramio/composer). Public Bot API is unchanged.

✨ New

  • Shorthand methods moved into Composer — reaction, callbackQuery, chosenInlineResult, inlineQuery, hears, command, startParameter usable on plugins and standalone composers.
  • Bot.extend(composer) / Plugin.extend(composer) — Accept EventComposer instances (promoted to scoped — shared context without duplicate middleware), plus the new decorate()/when()/inspect()/trace() surface.

🔧 Peer/dep bumps (move together)

  • @gramio/types 9.4.1, @gramio/composer 0.2.0

@gramio/auto-answer-callback-query ​

→ 0.0.3 · changelog ​

🐛 Fixes

  • Answers even when the handler throws — The middleware now wraps the handler in try/finally, so answerCallbackQuery always runs — no more stuck spinner.

@gramio/i18n ​

→ 1.5 · changelog ​

✨ New

  • localesFor() — i18n.localesFor(key) returns Record<string, string> of non-primary translations — drops straight into CommandMeta.locales for bot.syncCommands().
    ts
    bot.command("help", { description: i18n.t("en", "cmd.help"), locales: i18n.localesFor("cmd.help") }, helpHandler);

@gramio/jsx ​

0.0.1 → 0.1.0 · changelog ​

✨ New

  • Rich-message JSX — Adds rich <button>, <button-row>, and <document>, expandable credited <blockquote>, and compact <table> support.
  • Bot API 10.3 keyboard JSX — Regular keyboard JSX supports disabled inline buttons and forceReply for inline and reply keyboards.

🔧 Peer/dep bumps (move together)

  • gramio ^0.14.0, @gramio/types ^10.3.1, @gramio/test ^0.8.0

→ date-time element · changelog ​

✨ New

  • <date-time> element — <date-time unixTime={…} format="D" /> backed by the dateTime entity (@gramio/format 0.5+). Formats: r w d D t T wDT Dt, etc.

@gramio/onboarding ​

0.1.0 → 0.2.0 · changelog ​

✨ New

  • Typed build() (type-level only) — createOnboarding({ id }).….build() threads the flow Id, so bot.extend(...) widens ctx.onboarding.<id> automatically — no augmentation or cast. No runtime change.
    ts
    bot.command("start", (ctx) => {
        ctx.onboarding.welcome.start(); // ✅ typed, no augmentation
        return ctx.send("Let's go!");
    });

→ 0.1.0 · changelog ​

✨ New

  • New official plugin — Declarative tutorials with multi-flow concurrency (queue/preempt/parallel), a refusal ladder (next → skip → exit → dismiss → disableAll), scope-aware rendering (renderIn), fire-and-forget ctx.onboarding.*, pluggable @gramio/storage, optional @gramio/views integration.

@gramio/opentelemetry ​

→ new plugin · changelog ​

✨ New

  • OpenTelemetry plugin — opentelemetryPlugin({ recordApiParams }); every update is a root span, every API call a child span. Utilities record(), getCurrentSpan(), setAttributes().

@gramio/rate-limit ​

→ 0.0.1 · changelog ​

⚠️ Breaking

  • Export & package renamed — The plugin export is rateLimit (briefly rateLimitPlugin in the same release — the old name is gone). The npm package was renamed rate-limiter → rate-limit.

✨ New

  • Macro-based per-handler throttling — Sliding-window rate limiting via the macro system — no imperative if (!await ctx.rateLimit()) return. In-memory by default; swap in Redis/SQLite/Cloudflare via storage.
    ts
    import { rateLimit } from "@gramio/rate-limit";
    const bot = new Bot(token).extend(rateLimit({ onLimitExceeded: (ctx) => ctx.is("message") && ctx.reply("Slow down!") }));
    bot.command("pay", payHandler, { rateLimit: { limit: 3, window: 60 } });

@gramio/scenes ​

0.6.0 → 0.7.1 · changelog ​

⚠️ Breaking

  • Scene now extends EventComposer — The full bot-level DSL (.use/.on/.derive/.guard/.command/.callbackQuery/.hears/…) is available on every scene. The classic event-filter step form still works alongside builder steps.

✨ New

  • Builder steps — Each step is its own sub-composer with .enter / .exit / .fallback / .message plus the full event surface. State auto-inferred from ctx.scene.update({...}) — no .state<T>() needed.
    ts
    import { Scene } from "@gramio/scenes";
    
    const checkout = new Scene("checkout")
        .step("ask-name", (c) =>
            c.message("What's your name?").on("message", (ctx) => ctx.scene.update({ name: ctx.text })),
        )
        .step("confirm", (c) =>
            c.enter((ctx) => ctx.send(`${ctx.scene.state.name}, confirm? (yes/no)`)).hears("yes", (ctx) => ctx.scene.exit()),
        );
  • Reusable step modules + onExit — scene.extend(otherScene) pulls in a nameless Scene of steps (collisions throw, numeric steps renumber). New onExit hook fires before storage teardown; scene-level .derive() is visible inside onEnter.

🐛 Fixes

  • Upgrade straight to 0.7.1 — 0.7.0 could run an onEnter-consumed .derive() twice on entry. 0.7.1 restores exactly-once-per-update. If your derive has side effects (counters, spans, DB writes), do not stop at 0.7.0.

0.4.0 → 0.6.0 · changelog ​

⚠️ Breaking

  • Passthrough is now the default (behavior change) — Updates that don't match the current step propagate to outer handlers, so a global /cancel or /help fires mid-scene; the scene keeps its firstTime state. Restore the old greedy behavior with passthrough: false.
    ts
    // Before
    // before: non-matching updates were silently swallowed inside a scene
    // After
    const bot = new Bot(token)
        .extend(scenes([signupScene])) // passthrough: true by default
        .command("cancel", (ctx) => ctx.scene?.exit()); // now actually fires

✨ New

  • Sub-scenes & typed enter params — ctx.scene.enterSub(other, params) / exitSub(data) with a persisted stack and typed .exitData<T>(); scene.reenter(params); scene.enter() type-checks its params tuple.

→ 0.4.x · changelog ​

✨ New

  • EventComposer extend + onInvalidInput — scene.extend() accepts EventComposer instances; bot-level plugins extended before scenes aren't re-applied inside scene chains; ask() gained an onInvalidInput option.

🔧 Peer/dep bumps (move together)

  • gramio >= 0.5.0, @gramio/storage ^2.0.0

→ onEnter · changelog ​

✨ New

  • scene.onEnter(handler) — Run logic once when a scene is entered (awaited before the scene proceeds).

@gramio/sentry ​

→ new plugin · changelog ​

✨ New

  • Sentry plugin — sentryPlugin({ setUser, breadcrumbs, tracing }) with ctx.sentry.captureMessage()/setTag(). Uses @sentry/core (Bun + Node). Rides on the gramio onApiCall hook.

@gramio/session ​

→ 0.2.0 · changelog ​

✨ New

  • Lazy sessions — session({ storage, lazy: true }) defers the storage get until the first ctx.session read — cuts DB reads 50–90% for handlers that don't touch session. Write-back unchanged.

@gramio/views ​

0.2.0 → 0.2.1 · changelog ​

🐛 Fixes

  • Bot API 10.3 media compatibility — ResponseView.media excludes the two-file InputMediaLivePhoto from its one-file abstraction, while supported media keep all media-specific fields during send and edit.

🔧 Peer/dep bumps (move together)

  • gramio ^0.14.0

0.1.1 → 0.2.0 · changelog ​

✨ New

  • Lazy globals via thunk — buildRender accepts Globals | (() => Globals); a thunk runs per render so views see fresh session/scene/locale/onboarding state. The adapter factory re-runs per render too.

0.0.x → 0.1.1 · changelog ​

✨ New

  • sticker / voice / video_note media — Each with its own edit behavior (sticker/video_note are keyboard-only edits). Render methods return typed results instead of void.

→ new package · changelog ​

✨ New

  • Reusable message-view system — Automatic send/edit detection: programmatic adapters (defineAdapter), JSON-driven views (createJsonAdapter, interpolation), filesystem loading (loadJsonViewsDir), and i18n support.

@gramio/test ​

0.7.0 → 0.8.0 · changelog ​

✨ New

  • Stop-generation test actor — user.stopMessageGeneration(draftId, { chat?, messageThreadId? }) delivers a synthetic stopped_message_generation update.
  • Ephemeral send mock responses — Mocked sends resolve nested ephemeral_message_parameters and populate receiver_user plus ephemeral_message_id.

🔧 Peer/dep bumps (move together)

  • gramio ^0.14.0, @gramio/contexts ^0.11.0, @gramio/types ^10.3.1

0.3.0 → 0.7.0 · changelog ​

✨ New

  • lastBotMessage() bubble, payments, typed ApiCall — env.lastBotMessage() auto-tracks edits (options { withReplyMarkup }, { where }); Telegram Payments (sendPreCheckoutQuery/sendShippingQuery/sendSuccessfulPayment); type-safe ApiCall<Method>, lastApiCall(m), filterApiCalls(m).

0.1.0 → 0.3.0 · changelog ​

✨ New

  • 9 new methods — user.editMessage(), forwardMessage(), sendMediaGroup(), pinMessage(), on(msg).clickByText(), sendAudio()/sendAnimation()/sendVideoNote(), ChatObject.post(), env.clearApiCalls()/lastApiCall().

0.0.x → 0.1.0 · changelog ​

✨ New

  • Reactions, inline mode, fluent scopes — user.react()/ReactObject (auto old_reaction), sendInlineQuery()/chooseInlineResult(), and user.in(chat).on(msg).react(). Also env.onApi()/offApi() mocking + apiError().

create-gramio ​

2.2.0 → 2.3.0 · changelog ​

Affects newly generated projects only.

✨ New

  • Bot API 10.3 project template — New projects use gramio ^0.14.0 and @gramio/test ^0.8.0.

→ 2.x · changelog ​

Affects new scaffolds, not existing projects.

✨ New

  • Scaffold features — Generates CLAUDE.md; optional GramIO AI Skills install; @gramio/broadcast plugin choice; full CLI args + presets (minimal/recommended/full); scoped-composer + scene step-inheritance layout (2.2.0).