Skip to content

feat(plugins): native platform handler registration for every gateway platform - #59217

Merged
teknium1 merged 3 commits into
mainfrom
feat/generic-platform-handlers
Aug 27, 2026
Merged

feat(plugins): native platform handler registration for every gateway platform#59217
teknium1 merged 3 commits into
mainfrom
feat/generic-platform-handlers

Conversation

@teknium1

@teknium1 teknium1 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Every gateway platform now supports plugin-registered native handlers via ctx.register_platform_handler(platform, factory) — the generalization of #59159's Telegram-only hook to all 27 connectable adapters.

Stacks on #59159 (includes its commit); this PR's own change is the generic surface + fleet-wide wiring.

How it works

  • Plugins queue (factory, plugin_name) per platform; the platform's adapter invokes factories at connect() time with (native, adapter).
  • native is the platform's real client object where one exists — PTB Application, discord.py commands.Bot, slack_bolt AsyncApp, Teams App, DingTalkStreamClient, lark client, aiohttp web.Application (line / api_server / msgraph_webhook, wired before the router freezes) — and None for adapters with no separate native object (signal, whatsapp bridge, irc, email, sms, ntfy, wecom, weixin, bluebubbles, yuanbao, ...), where the adapter handle is the surface.
  • Ordering respected where dispatch tables are first-match: telegram/slack/teams/aiohttp routers run plugin factories before core handlers register, so pattern-scoped plugin handlers take precedence and everything else falls through unchanged.
  • Fault-isolated in BasePlatformAdapter._wire_plugin_handlers(): a raising factory is logged and the platform still connects.
  • register_telegram_handler / get_telegram_handler_factories retained as thin back-compat aliases.

What this enables (as plugins, zero core edits)

Discord reaction/member-event bots, Slack event/shortcut/command listeners beyond Block Kit, Matrix custom event types, Teams card flows, custom webhook routes on the aiohttp platforms, Telegram Business secretary bots (https://github.com/NousResearch/hermes-telegram-business), and any platform-native interactivity a plugin wants to own under its own scoped namespace.

Changes

  • hermes_cli/plugins.py: register_platform_handler() + keyed factory store + get_platform_handler_factories(); telegram methods become aliases.
  • gateway/platforms/base.py: shared _wire_plugin_handlers(native) helper.
  • 27 adapters (19 plugin platforms + 8 gateway/platforms modules): one wiring call each at the correct point in connect().
  • tests/gateway/test_platform_plugin_handlers.py (renamed from the telegram-only file): 16 tests — validation, normalization, per-platform scoping, alias, copy semantics, force-clear, invocation args, fault isolation, native=None path, plus a source invariant asserting every connectable adapter calls the hook.
  • Docs: plugin guide section rewritten as "Register native platform handlers (any platform)" with the per-platform native table + Telegram/Discord examples.

Validation

tests/gateway/ + tests/hermes_cli/test_plugins.py (443 files) 8,923 / 8,923 passed
Surface tests 16 / 16
E2E (real PluginManager + real TelegramAdapter + SignalAdapter) per-platform scoping, alias routing, native vs None all verified
ruff clean

Infographic

platform-plugin-handlers

@alt-glitch alt-glitch added type/feature New feature or request comp/plugins Plugin system and bundled plugins comp/gateway Gateway runner, session dispatch, delivery sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages P3 Low — cosmetic, nice to have labels Jul 5, 2026
…ister_telegram_handler

Mirrors the Slack precedent (register_slack_action_handler): plugins queue
a factory at register() time; the Telegram adapter invokes each factory
with (application, adapter) at connect() time, before the core handlers
register, so pattern-scoped plugin handlers take precedence for their own
updates while everything else falls through unchanged. Factories are
isolated — a raising plugin cannot prevent Telegram from connecting.

Unblocks standalone plugins that need PTB update types the core adapter
doesn't route (Telegram Business API secretary bots, custom callback
prefixes, chat-member events) without touching core files.
…ry gateway platform

ctx.register_platform_handler(platform, factory) — the generic surface for
plugins to wire native handlers into any platform adapter at connect()
time. Factories receive (native, adapter): the platform's client/app
object (PTB Application, discord.py Bot, slack_bolt AsyncApp, Teams App,
DingTalkStreamClient, aiohttp web.Application) or None for adapters with
no separate native object.

- BasePlatformAdapter._wire_plugin_handlers(native): shared, isolated
  invocation helper — a raising plugin cannot block a platform connect.
- All 27 connectable adapters call it: telegram/slack/teams/line/
  api_server/msgraph_webhook wire before their dispatch tables freeze;
  the rest hook at connect success.
- register_telegram_handler and get_telegram_handler_factories retained
  as thin back-compat aliases over the telegram bucket.
- Source-invariant test guarantees every adapter with connect() keeps
  calling the hook.
…t adapters

Platforms added to main after the original branch was cut; keeps the
source invariant (every connectable adapter calls _wire_plugin_handlers)
true, and adds qqbot to the invariant test's gateway list.
@teknium1
teknium1 force-pushed the feat/generic-platform-handlers branch from 11647ec to 2d3afd7 Compare August 27, 2026 14:20
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 2d3afd7 — feat(plugins): wire plugin platform handlers into a2a, buzz,

⚠️ Warnings

OSV vulnerability scan · View job

6 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 4m33s vs 4m16s (+6.6%). 4 job(s) slower, 6 faster, 3 unchanged.

  • Python tests / Run tests: -37.0s
  • OS-specific tests / Windows-only tests: -12.0s
  • Check contributors / check-attribution: -11.0s
  • Python tests / e2e: +6.0s
  • OS-specific tests / macOS-only tests: +4.0s

@teknium1
teknium1 merged commit 34393c3 into main Aug 27, 2026
35 checks passed
@teknium1
teknium1 deleted the feat/generic-platform-handlers branch August 27, 2026 14:51
vKongv added a commit to pebble-tech/hermes-agent that referenced this pull request Sep 6, 2026
Callback registration already landed in NousResearch#59217. Keep the outbound
send() reply_markup surface so plugins do not bypass adapter policy.
cursor Bot pushed a commit to pebble-tech/hermes-agent that referenced this pull request Sep 7, 2026
…h#59217

Callback registration landed upstream in NousResearch#59217; feat/telegram-plugin-callback-handlers
now only carries send() reply_markup. The old test_telegram_plugin_callback_handlers
file is not on the rebased feature branch, so sync focused-tests failed with
'file or directory not found'.

Co-authored-by: Kong Ka Weng <mgongzai@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants