Skip to content

refactor(gateway): migrate ALL remaining inline adapters (slack, dingtalk, whatsapp, matrix, feishu, telegram, wecom, email, sms) to plugins - #41284

Closed
teknium1 wants to merge 17 commits into
mainfrom
migrate/platforms-to-plugins
Closed

refactor(gateway): migrate ALL remaining inline adapters (slack, dingtalk, whatsapp, matrix, feishu, telegram, wecom, email, sms) to plugins#41284
teknium1 wants to merge 17 commits into
mainfrom
migrate/platforms-to-plugins

Conversation

@teknium1

@teknium1 teknium1 commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrates ALL remaining inline messaging-platform adapters — slack, dingtalk, whatsapp, matrix, feishu, telegram, wecom (+ wecom_callback), email, and sms — out of gateway/platforms/ into self-contained bundled plugins under plugins/platforms/<x>/, discovered via the platform registry instead of hardcoded Platform.<X> touchpoints. Completes the gateway-channel → plugin transition tracked in #41112 (companion to the registry architecture in #3823). With Discord, Home Assistant, Mattermost, Slack (#41160) and the born-as-plugin set already done, this removes the last hardcoded adapter branches from core.

Changes

Each platform follows the established Discord/HA migration shape:

  • Adapter relocated via git mv (history preserved): gateway/platforms/<x>.pyplugins/platforms/<x>/adapter.py, plus __init__.py / plugin.yaml. Satellites travel with their adapter: telegram_network.py → telegram plugin; the three feishu_comment* / feishu_meeting_invite modules → feishu plugin; wecom_crypto.py + wecom_callback.py → wecom plugin (internal imports rewritten).
  • register(ctx) exposes each platform with the hook set: standalone_sender_fn (out-of-process cron delivery), apply_yaml_config_fn (config.yaml → env/extra bridge), setup_fn (interactive wizard), is_connected, check_fn, plus allowed_users_env / allow_all_env / cron_deliver_env_var / max_message_length / emoji.
  • Core touchpoints stripped: gateway/run.py factory branches (telegram's leading if, the rest elifs); gateway/config.py per-platform YAML→env blocks + _PLATFORM_CONNECTED_CHECKERS entries; hermes_cli/gateway.py static _PLATFORMS dicts + setup-map entries; hermes_cli/setup.py _setup_matrix; tools/send_message_tool.py _send_<x> dispatch (now a generic _registry_standalone_send registry hook). Telegram keeps _send_telegram (its plugin delegates to it); matrix keeps _send_matrix_via_adapter (native-media path) with its import repointed.

Deliberately left generic in core (same policy as prior migrations): the Platform.<X> enum literals, the _is_user_authorized allowlist maps, the cron delivery maps, and the bridged-loop shared-key handling.

Fixes uncovered during migration

  • Explicit enabled: false honored for plugin platforms. The registry-driven plugin-enable pass re-enabled any plugin whose check_fn/is_connected passed, ignoring an explicit disable. Generalized the _enabled_explicit marker (was slack-only), made _enable_from_env read (not pop) it, and added an explicit-disable guard.
  • get_connected_platforms() forces plugin discovery before the registry checker lookup so directly-constructed GatewayConfig resolves migrated checkers.
  • Plugin is_connected reads via gateway.get_env_value (telegram/whatsapp/matrix) so setup-status and connected checks observe the same value; telegram/matrix gain explicit is_connected so they aren't enabled on mere SDK presence.
  • hermes logs --component gateway now matches plugins.platforms.* adapter logs (added to COMPONENT_PREFIXES).
  • Matrix stays hidden on Windows in the setup picker for the registry path too.

Validation

Result
Targeted suites (dingtalk/matrix/feishu/telegram/whatsapp/config/send/setup/logs/connected-checkers) passing
Full gateway + tools + cli + logging sweep 19001 passing; the only failures are 4 pre-existing local-only web-provider tests (ddgs installed locally, absent in CI — identical failures on clean main)
Live E2E (real plugin discovery → registry resolves all migrated adapters (5 + wecom/wecom_callback), legacy modules gone, factory branches stripped, env-enable, YAML bridges, standalone dispatch, setup discovery) all 10 platforms register; 625 targeted + full hermetic suites green

Test imports swept across ~68 files (gateway.platforms.<x>plugins.platforms.<x>.adapter); _setup_matrix/_setup_feishu tests retargeted to the plugins' interactive_setup; send-helper tests retargeted to the registry standalone_sender_fn.

Scope

Deliberately not migrated and left in core: webhook, api_server, msgraph_webhook (generic HTTP / change-notification ingress surfaces the #41112 tracker flags as not platform-shaped). signal, qqbot, weixin, yuanbao, bluebubbles are excluded because they have open contributor migration PRs (#41061, #41065, #41101, #40804, #31872) — preserving that work. Slack was folded in from #41160 (now closed) so this is the single PR for the whole transition.

Infographic

Gateway platforms to plugins migration

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: migrate/platforms-to-plugins vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 10036 on HEAD, 10002 on base (🆕 +34)

🆕 New issues (197):

Rule Count
unresolved-attribute 88
unresolved-import 67
invalid-method-override 15
invalid-argument-type 11
invalid-assignment 7
unresolved-reference 3
call-non-callable 2
no-matching-overload 1
not-subscriptable 1
unused-type-ignore-comment 1
invalid-return-type 1
First entries
plugins/platforms/whatsapp/adapter.py:1100: [invalid-method-override] invalid-method-override: Invalid override of method `send_document`: Definition is incompatible with `BasePlatformAdapter.send_document`
plugins/platforms/dingtalk/adapter.py:1209: [unresolved-attribute] unresolved-attribute: Attribute `StreamingUpdateHeaders` is not defined on `None` in union `Unknown | None`
plugins/platforms/wecom/callback_adapter.py:134: [unresolved-attribute] unresolved-attribute: Attribute `AsyncClient` is not defined on `None` in union `Unknown | None`
plugins/platforms/telegram/adapter.py:1618: [unresolved-attribute] unresolved-attribute: Attribute `Document` is not defined on `None` in union `Unknown | None`
plugins/platforms/dingtalk/adapter.py:895: [invalid-assignment] invalid-assignment: Invalid subscript assignment with key of type `str | None` and value of type `str` on object of type `dict[str, str]`
plugins/platforms/dingtalk/adapter.py:1103: [unresolved-attribute] unresolved-attribute: Attribute `DeliverCardRequest` is not defined on `None` in union `Unknown | None`
plugins/platforms/dingtalk/adapter.py:1410: [unresolved-attribute] unresolved-attribute: Attribute `ChatbotHandler` is not defined on `None` in union `Unknown | None`
plugins/platforms/feishu/feishu_comment.py:41: [unresolved-import] unresolved-import: Cannot resolve imported module `lark_oapi.core.model.base_request`
plugins/platforms/matrix/adapter.py:714: [unresolved-import] unresolved-import: Cannot resolve imported module `mautrix.client.state_store`
plugins/platforms/email/adapter.py:374: [invalid-argument-type] invalid-argument-type: Argument to bound method `IMAP4.uid` is incorrect: Expected `str`, found `None`
plugins/platforms/wecom/adapter.py:284: [unresolved-attribute] unresolved-attribute: Attribute `ClientSession` is not defined on `None` in union `Unknown | None`
plugins/platforms/feishu/adapter.py:1368: [unresolved-import] unresolved-import: Cannot resolve imported module `lark_oapi.core`
plugins/platforms/feishu/adapter.py:3535: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_last_chunk_len` on type `MessageEvent`
plugins/platforms/telegram/adapter.py:2076: [unresolved-attribute] unresolved-attribute: Attribute `message_id` is not defined on `None` in union `None | Unknown`
tests/tools/test_send_message_tool.py:873: [invalid-assignment] invalid-assignment: Object of type `AsyncMock` is not assignable to attribute `standalone_sender_fn` on type `PlatformEntry | None`
plugins/platforms/telegram/adapter.py:4317: [unresolved-attribute] unresolved-attribute: Attribute `CHANNEL` is not defined on `None` in union `Unknown | None`
plugins/platforms/matrix/adapter.py:1288: [invalid-method-override] invalid-method-override: Invalid override of method `send_document`: Definition is incompatible with `BasePlatformAdapter.send_document`
plugins/platforms/telegram/adapter.py:3406: [unresolved-attribute] unresolved-attribute: Attribute `PRIVATE` is not defined on `None` in union `Unknown | None`
plugins/platforms/telegram/adapter.py:2937: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to `def group_providers(slugs) -> Unknown`
plugins/platforms/whatsapp/adapter.py:1089: [invalid-method-override] invalid-method-override: Invalid override of method `send_voice`: Definition is incompatible with `BasePlatformAdapter.send_voice`
plugins/platforms/matrix/adapter.py:2952: [unresolved-attribute] unresolved-attribute: Attribute `group` is not defined on `None` in union `Match[str] | None`
plugins/platforms/telegram/adapter.py:5295: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_last_chunk_len` on type `MessageEvent`
plugins/platforms/wecom/callback_adapter.py:25: [unresolved-import] unresolved-import: Cannot resolve imported module `defusedxml.ElementTree`
plugins/platforms/telegram/telegram_network.py:18: [unresolved-import] unresolved-import: Cannot resolve imported module `httpx`
plugins/platforms/dingtalk/adapter.py:1265: [unresolved-attribute] unresolved-attribute: Attribute `RobotRecallEmotionRequest` is not defined on `None` in union `Unknown | None`
... and 172 more

✅ Fixed issues (182):

Rule Count
unresolved-attribute 85
unresolved-import 57
invalid-method-override 15
invalid-argument-type 10
invalid-assignment 7
call-non-callable 2
unresolved-reference 2
no-matching-overload 1
invalid-return-type 1
not-subscriptable 1
unused-type-ignore-comment 1
First entries
gateway/platforms/feishu.py:2595: [no-matching-overload] no-matching-overload: No overload of bound method `dict.get` matches arguments
gateway/platforms/dingtalk.py:1071: [unresolved-attribute] unresolved-attribute: Attribute `create_card_with_options_async` is not defined on `None` in union `Any | None`
gateway/platforms/dingtalk.py:1446: [unresolved-attribute] unresolved-attribute: Attribute `from_dict` is not defined on `None` in union `Unknown | None`
gateway/platforms/matrix.py:810: [unresolved-import] unresolved-import: Cannot resolve imported module `mautrix.util.async_db`
gateway/platforms/feishu.py:4577: [call-non-callable] call-non-callable: Object of type `None` is not callable
gateway/platforms/dingtalk.py:1067: [unresolved-attribute] unresolved-attribute: Attribute `CreateCardHeaders` is not defined on `None` in union `Unknown | None`
gateway/platforms/whatsapp.py:1078: [invalid-method-override] invalid-method-override: Invalid override of method `send_video`: Definition is incompatible with `BasePlatformAdapter.send_video`
gateway/platforms/whatsapp.py:1067: [invalid-method-override] invalid-method-override: Invalid override of method `send_image_file`: Definition is incompatible with `BasePlatformAdapter.send_image_file`
gateway/platforms/email.py:80: [invalid-assignment] invalid-assignment: Object of type `Literal["0"]` is not assignable to `Literal["0.16.0"]`
gateway/platforms/telegram.py:1519: [unresolved-attribute] unresolved-attribute: Attribute `builder` is not defined on `<special-form 'typing.Any'>` in union `Unknown | <special-form 'typing.Any'>`
gateway/platforms/wecom.py:1464: [invalid-method-override] invalid-method-override: Invalid override of method `send_voice`: Definition is incompatible with `BasePlatformAdapter.send_voice`
gateway/platforms/matrix.py:345: [unresolved-import] unresolved-import: Cannot resolve imported module `mautrix.types`
gateway/platforms/matrix.py:2809: [unresolved-import] unresolved-import: Cannot resolve imported module `markdown`
gateway/platforms/telegram.py:137: [unresolved-import] unresolved-import: Cannot resolve imported module `telegram.ext`
gateway/platforms/matrix.py:808: [unresolved-import] unresolved-import: Cannot resolve imported module `mautrix.crypto`
gateway/platforms/dingtalk.py:1276: [unresolved-attribute] unresolved-attribute: Attribute `RobotReplyEmotionRequest` is not defined on `None` in union `Unknown | None`
gateway/platforms/wecom.py:179: [unresolved-attribute] unresolved-attribute: Attribute `ClientWebSocketResponse` is not defined on `None` in union `Unknown | None`
gateway/platforms/dingtalk.py:78: [unresolved-import] unresolved-import: Cannot resolve imported module `alibabacloud_tea_util`
gateway/platforms/telegram.py:1618: [unresolved-attribute] unresolved-attribute: Attribute `Document` is not defined on `None` in union `Unknown | None`
hermes_cli/gateway.py:5177: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to `def qr_register(*, initial_domain: str = "feishu", timeout_seconds: int = 600) -> dict[Unknown, Unknown] | None`
gateway/platforms/dingtalk.py:257: [unresolved-attribute] unresolved-attribute: Attribute `AsyncClient` is not defined on `None` in union `Unknown | None`
hermes_cli/gateway.py:5249: [invalid-argument-type] invalid-argument-type: Argument to function `save_env_value` is incorrect: Expected `str`, found `Unknown | str | None`
gateway/platforms/dingtalk.py:1441: [unresolved-attribute] unresolved-attribute: Attribute `data` is not defined on `None` in union `Unknown | None`
gateway/platforms/telegram.py:2076: [unresolved-attribute] unresolved-attribute: Attribute `message_id` is not defined on `None` in union `None | Unknown`
gateway/platforms/feishu.py:1368: [unresolved-import] unresolved-import: Cannot resolve imported module `lark_oapi.core`
... and 157 more

Unchanged: 5008 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@daimon-nous daimon-nous Bot added type/refactor Code restructuring, no behavior change comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have labels Jun 7, 2026
@teknium1
teknium1 force-pushed the migrate/platforms-to-plugins branch from 1ba0a94 to 908dc9b Compare June 7, 2026 13:18
@teknium1 teknium1 changed the title refactor(gateway): migrate dingtalk, whatsapp, matrix, feishu, telegram adapters to bundled plugins refactor(gateway): migrate dingtalk, whatsapp, matrix, feishu, telegram, wecom adapters to bundled plugins Jun 7, 2026
teknium1 added 12 commits June 7, 2026 06:23
…ugins

WIP toward #41112 — full transition of all gateway channels to plugins.
dingtalk and whatsapp adapters relocated to plugins/platforms/<name>/ with
register() + standalone_sender_fn / apply_yaml_config_fn / setup_fn /
is_connected hooks. Core touchpoints stripped: run.py factory elifs,
config.py YAML bridges + _PLATFORM_CONNECTED_CHECKERS entries, gateway.py
static _PLATFORMS dicts + setup-map entries + _setup_* fns, send_message_tool
dispatch (now via _registry_standalone_send) + _send_* helpers.

matrix, feishu, telegram still to come in this branch.
WIP toward #41112. Matrix adapter relocated to plugins/platforms/matrix/ with
register() + standalone_sender_fn (REST text path) / apply_yaml_config_fn /
setup_fn (full E2EE-aware wizard) hooks. Core touchpoints stripped: run.py
elif, config.py matrix_cfg YAML bridge, gateway.py static dict + setup-map
entry, setup.py _setup_matrix, send_message_tool _send_matrix + dispatch.
_send_matrix_via_adapter (native media path) kept in send_message_tool with
its import repointed to plugins.platforms.matrix.adapter. Matrix uses the
generic token connected-check (no is_connected override needed).

feishu + telegram still to come.
…lugin

WIP toward #41112. Feishu adapter + its feishu_comment / feishu_comment_rules /
feishu_meeting_invite satellites relocated to plugins/platforms/feishu/ with
internal imports rewritten to the new package path. register() exposes the
platform with standalone_sender_fn (adapter-based send incl native media:
images/video/voice/documents), apply_yaml_config_fn (allow_bots bridge),
setup_fn (full QR-register + manual + DM/group-policy wizard), is_connected
(app_id). Core touchpoints stripped: run.py elif, config.py feishu YAML bridge
+ _PLATFORM_CONNECTED_CHECKERS entry, gateway.py static dict + _setup_feishu +
setup-map entry, send_message_tool _send_feishu + text dispatch + native-media
branch (now via registry standalone_sender_fn) + FeishuAdapter MAX_LENGTHS
import (registry max_message_length=8000 covers it).

telegram still to come (the last one).
…undled plugin

WIP toward #41112 — the last and largest gateway channel. Telegram adapter +
telegram_network satellite relocated to plugins/platforms/telegram/ with the
network import rewritten to the new package path. register() exposes the
platform with standalone_sender_fn (delegates to the retained _send_telegram
REST sender), apply_yaml_config_fn (full telegram: YAML→env + extra bridge),
setup_fn (delegates to the managed-bot QR wizard via lazy import), and a
_build_adapter that applies the notification-mode resolution that used to live
in gateway/run.py's factory branch. Core touchpoints stripped: run.py factory
branch (was the leading if), config.py 90-line telegram_cfg bridge, gateway.py
static dict + setup-map entry. send_message_tool keeps _send_telegram (the
plugin delegates to it) with its TelegramAdapter imports repointed to the
plugin path. Generic Platform.TELEGRAM references in core (auth maps, bridged
config-loop special-cases, source-platform behavior checks) intentionally
remain — same 'left generic in core' policy as the merged migrations.
… discovery, telegram extra precedence, prompt_choice import

- Sweep test imports gateway.platforms.{telegram,feishu,matrix,dingtalk,whatsapp}
  (+ telegram_network / feishu satellites) -> plugins.platforms.<x>.adapter
  across ~68 test files; both 'from X import' and 'from gateway.platforms import X'
  forms.
- _is_platform_connected now forces discover_plugins() before the registry
  lookup so get_connected_platforms() works on directly-constructed GatewayConfig
  (fixes dingtalk-recognised tests after its checker moved to the plugin).
- telegram _apply_yaml_config no longer re-emits generic shared-config keys
  (reply_prefix etc.) that _merge_platform_map already merges with top-level
  precedence, while still passing through telegram-specific extras (base_url).
- prompt_choice lives in hermes_cli.setup, not cli_output — fixed the import in
  feishu/dingtalk interactive_setup and the test patch target.
- test_setup_feishu rewritten to drive interactive_setup via the plugin.
…ndalone_sender_fn

The _send_matrix / _send_whatsapp inline helpers moved into their plugins'
_standalone_send (#41112). Updated the three affected tests to patch / call the
plugin registry standalone_sender_fn instead of the removed module-level
helpers; behavior assertions (lightweight text path, bridge routing, room-ID
percent-encoding) preserved.
…hecker invariant + webhook-secret source path

- Restore TELEGRAM_REQUIRE_MENTION env bridge for the top-level require_mention
  shorthand (#3979) in core config.py — keys off the top-level key, not a
  telegram: block, so the plugin hook (which needs a block) can't cover the
  no-telegram-block case.
- test_all_builtins_have_checker_or_generic_token_path now also accepts
  registry-provided is_connected/validate_config (dingtalk/whatsapp/feishu
  checkers moved to their plugins).
- test_telegram_webhook_secret reads the adapter source from the new
  plugins/platforms/telegram/adapter.py path.
…platforms; test path/import updates

- hermes_logging COMPONENT_PREFIXES['gateway'] now includes plugins.platforms
  so migrated adapters' logs route to gateway.log + match
  'hermes logs --component gateway'.
- Generalize the _enabled_explicit marker (was slack-only) to every platform
  with an explicit enabled key, and make _enable_from_env read (not pop) it,
  so the registry-driven plugin-enable pass honors an explicit enabled:false
  for telegram/matrix/etc. instead of re-enabling on token/SDK presence.
- Telegram gets an is_connected (token check) so the enable gate doesn't flip
  it on merely because python-telegram-bot is importable.
- Test updates: windows-native npm-scan path -> plugins/platforms/whatsapp,
  webhook-secret source path -> plugin adapter, logging component tests use
  expanded gateway prefixes, connected-checker invariant accepts registry
  is_connected/validate_config.
… explicit is_connected; setup-flow test stubs

- whatsapp/telegram/matrix _is_connected now read tokens via
  hermes_cli.gateway.get_env_value (not os.getenv) so setup-status callers that
  patch get_env_value — and the connected-platforms check — observe the same
  value (matches the discord/slack plugin pattern). whatsapp keys off
  WHATSAPP_ENABLED instead of returning unconditional True (was making it always
  show 'configured' in hermes setup). matrix/telegram gain explicit is_connected
  so _platform_status reflects real config, not mere SDK presence.
- _all_platforms() hides matrix on Windows for registry-discovered plugins too
  (python-olm has no Windows wheel), not just the legacy _PLATFORMS list.
- setup_gateway tests stub _configure_platform + keep checklist pre-selection so
  migrated plugins' interactive_setup wizards don't read real stdin.
Was an exact-tuple snapshot ('gateway','hermes_plugins'); now asserts the
required prefixes (incl. the new plugins.platforms from #41112) as an
invariant so future gateway-component prefixes don't break it.
… plugin

Completes #41112 — the last unclaimed chat-platform channels. WeCom Smart
Robot (wecom) and callback-mode self-built apps (wecom_callback), sharing the
wecom_crypto satellite, relocated to plugins/platforms/wecom/ (adapter.py +
callback_adapter.py + wecom_crypto.py, internal crypto import rewritten).
register() exposes BOTH platforms via the registry with standalone_sender_fn
(wecom WebSocket send), setup_fn (QR/manual wizard), is_connected (bot_id /
corp_id). Core touchpoints stripped: run.py 2 factory elifs, config.py 2
_PLATFORM_CONNECTED_CHECKERS entries, gateway.py 2 static dicts + _setup_wecom
+ setup-map entry, send_message_tool _send_wecom (now registry dispatch).
Env->PlatformConfig seeding stays in core. 191 wecom-related tests pass.
…_send + restore dingtalk token redaction

CI (test shard 6) caught tests/tools/test_send_message_missing_platforms.py
importing the removed _send_dingtalk/_send_matrix helpers. Added thin
pre-migration-shaped shims around the plugins' _standalone_send (same pattern
mattermost/HA used in this file). Also restored access_token redaction in the
dingtalk plugin's _standalone_send error path (the legacy _send_dingtalk
returned via _error() which redacts URL secrets; the bare plugin return leaked
the webhook access_token in exception text) by reusing send_message_tool._error
via lazy import.
@teknium1
teknium1 force-pushed the migrate/platforms-to-plugins branch from 908dc9b to 91da240 Compare June 7, 2026 13:23
Migrates the remaining unclaimed, genuinely-messaging channels: email (IMAP
poll + SMTP reply) and sms (Twilio REST). Both relocated to
plugins/platforms/{email,sms}/ with register() + standalone_sender_fn +
is_connected. Core touchpoints stripped: run.py 2 factory elifs, config.py 2
_PLATFORM_CONNECTED_CHECKERS entries, gateway.py 2 static dicts,
send_message_tool _send_email/_send_sms (now registry dispatch). Both
standalone senders preserve credential redaction via send_message_tool._error;
sms preserves markdown stripping; both marked pii_safe. env->PlatformConfig
seeding stays in core.

Deliberately NOT migrated: webhook + api_server + msgraph_webhook (generic
HTTP/ingress surfaces the #41112 tracker flags as not platform-shaped); and
signal/qqbot/weixin/yuanbao/bluebubbles (open contributor PRs).
@teknium1 teknium1 changed the title refactor(gateway): migrate dingtalk, whatsapp, matrix, feishu, telegram, wecom adapters to bundled plugins refactor(gateway): migrate dingtalk/whatsapp/matrix/feishu/telegram/wecom/email/sms adapters to bundled plugins Jun 7, 2026
teknium1 added 3 commits June 7, 2026 06:39
…ange (#f6f363662)

Pre-existing main breakage, not migration fallout: f6f3636 made
_component_check_auth fail closed when no allowlist is set but only updated
test_discord_component_auth.py, leaving test_discord_model_picker's
empty-allowlist case asserting events that the now-rejected click never
produces. Give the picker view an allowlist containing the interacting user
({"123"}) so the authorized path — which is what this control-clearing test
actually exercises — runs. Confirmed: fails on current main (a317e54 red),
passes on the stale local clone that lacks f6f3636.
…-closed change (#f6f363662)

Same pre-existing main breakage as the model-picker fix: f6f3636 made
component-button auth fail closed but only updated test_discord_component_auth.py.
test_discord_clarify_buttons' test_choice_falls_back_to_label_text_when_entry_missing
and test_other_flips_entry_to_awaiting_text drove the authorized-proceed path
with an empty allowlist, which now correctly rejects. Give the interacting
user (id 42) an allowlist entry so the proceed path runs (the dedicated
*_unauthorized_* tests still cover rejection). Verified green under the
hermetic per-file runner.
…Error (cryptography version skew)

CI shard (test 1) deterministically failed importing the dingtalk plugin:
alibabacloud_dingtalk transitively imports cryptography and raises
AttributeError ('cryptography.utils' has no attribute 'DeprecatedIn46') when
CI's cryptography is older than the SDK expects. The adapter guarded that SDK
import with 'except ImportError', which does NOT catch AttributeError, so the
whole adapter (and thus plugin discovery via __init__'s 'from .adapter import
register') crashed instead of degrading. Broadened the alibabacloud_dingtalk
guard, the dingtalk_stream guard, and the check_dingtalk_requirements lazy
re-import to 'except Exception' so a broken optional SDK dependency chain
degrades gracefully (CARD_SDK_AVAILABLE/DINGTALK_STREAM_AVAILABLE=False), same
as a missing dep. This was invisible before migration because the inline
adapter was only imported when dingtalk was activated, not at plugin-discovery
time.
teknium1 added a commit that referenced this pull request Jun 7, 2026
…ge (#f6f363662)

Pre-existing main breakage inherited via rebase, not slack-migration fallout:
f6f3636 made discord component-button auth fail closed when no allowlist is
set but only updated test_discord_component_auth.py. test_discord_model_picker
and two test_discord_clarify_buttons tests drove the authorized-proceed path
with an empty allowlist, which now correctly rejects. Give the interacting
user an allowlist entry so the proceed path runs (dedicated *_unauthorized_*
tests still cover rejection). Same fix as #41284; both PRs un-break main's
currently-red CI.
Move gateway/platforms/slack.py into plugins/platforms/slack/ following the
Discord (#24356) and Home Assistant (#40709) migrations. Advances #41112 /
hardcoded Platform.SLACK touchpoints in core.

  - Adapter file renamed via git mv (history preserved).
  - register() exposes the platform via ctx.register_platform() instead of the
    Platform.SLACK elif in gateway/run.py::_create_adapter().
  - _standalone_send() replaces the legacy _send_slack() helper in
    tools/send_message_tool.py; out-of-process cron delivery (deliver=slack)
    now flows through the registry's standalone_sender_fn. mrkdwn formatting
    moved into the plugin (was applied in _send_to_platform before chunking).
  - _apply_yaml_config() owns the config.yaml slack: -> SLACK_* env bridge
    (require_mention, strict_mention, allow_bots, free_response_channels,
    reactions, allowed_channels), replacing the hardcoded block in
    gateway/config.py.
  - interactive_setup() replaces hermes_cli/setup.py::_setup_slack +
    _write_slack_manifest_and_instruct and the static _PLATFORMS["slack"] dict
    in hermes_cli/gateway.py; setup metadata is discovered dynamically.
  - is_connected() probes SLACK_BOT_TOKEN via hermes_cli.gateway.get_env_value.
  - max_message_length=39000 on the PlatformEntry; the registry fallback in
    send_message_tool covers it (dropped the _MAX_LENGTHS entry).

The SLACK_BOT_TOKEN/SLACK_HOME_CHANNEL env->PlatformConfig seeding and the
_is_user_authorized allowlist maps stay in core (same as Discord/HA/Mattermost).

Bug fixed during migration: the registry-driven plugin-enable pass in
_apply_env_overrides re-enabled any plugin platform whose is_connected()
passed, ignoring an explicit enabled: false. Slack is the first plugin with an
enabled-false-wins test, so it exposed this latent bug (Discord had no such
test). Added an explicit-disable guard (_enabled_explicit + enabled=False ->
skip) and changed the slack env-block to read the flag instead of popping it so
the guard can see it; the flag is still cleared in the final per-platform
cleanup. Restores test_explicit_{top_level,platforms}_slack_enabled_false_wins.

Test imports rewritten across 11 files (gateway.platforms.slack ->
plugins.platforms.slack.adapter). The _setup_slack home-channel tests moved to
tests/gateway/test_slack_plugin_setup.py exercising interactive_setup. The
test_send_message_tool slack-formatting tests now patch the registry
standalone_sender_fn (via _patch_slack_standalone_sender) and assert the
mrkdwn-formatted text reaches the wire.

Validation: 706 targeted tests pass (slack/config/setup/registry/send/media
suites); 18/18 live E2E checks pass (real plugin discovery + registry resolves
SlackAdapter, env-only enable, standalone sender wired, YAML bridge, dynamic
setup discovery).
@teknium1 teknium1 changed the title refactor(gateway): migrate dingtalk/whatsapp/matrix/feishu/telegram/wecom/email/sms adapters to bundled plugins refactor(gateway): migrate ALL remaining inline adapters (slack, dingtalk, whatsapp, matrix, feishu, telegram, wecom, email, sms) to plugins Jun 7, 2026
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Reviewed this end-to-end (fetched into a worktree, ran the migrated-platform + shared-surface test suites serially, plus targeted repros). Solid, behavior-preserving migration overall — telegram's full YAML bridge is intact (all keys + the bare-off"off" coercion), matrix's E2EE/device-id handling is untouched, and the feishu/wecom satellite modules all relocated with their cross-imports repointed. Test suite is green modulo known pre-existing flakes (matrix/telegram xdist ordering + the defusedxml-missing wecom_callback tests — all reproduce identically on clean main).

One blocker and a couple of smaller notes:

🔴 HIGH — explicit enabled: false is silently overridden for 6 of the migrated platforms when env credentials are present

The _enabled_explicit guard that prevents the registry enablement pass from re-enabling a platform the user explicitly disabled is wired into the slack block and the _enable_from_env helper (telegram/matrix), but the platforms with their own bespoke env-enablement blocks in _apply_env_overrides still flip enabled = True unconditionally on credential presence, without consulting the marker:

  • email, sms, dingtalk, feishu, wecom, wecom_callback

Repro:

# config.yaml
dingtalk:
  enabled: false
DINGTALK_CLIENT_ID=... DINGTALK_CLIENT_SECRET=***   # in env

load_gateway_config() yields dingtalk.enabled == True. Same with sms + TWILIO_ACCOUNT_SID. The adapter starts against the user's explicit opt-out — the exact regression class this PR's gate work (#41112) is meant to close. The fix is mechanical: mirror the slack block's enabled_was_explicit = bool(extra.get("_enabled_explicit", False)) read before setting enabled = True in each of those six blocks.

🟡 MEDIUM — whatsapp is_connected changed from the prior lambda cfg: True

This looks intentional (the docstring cites #41112 — unconditional True made whatsapp always report "configured"), and it's arguably more correct, but it's a behavior change rather than a 1:1 relocation. Worth a line in the PR description so it's not read as a silent migration artifact.

🟢 LOW (cosmetic, non-blocking)

  • _UPDATE_ALLOWED_PLATFORMS in gateway/run.py still lists the migrated platforms. These are now dead entries — the /update gate falls back to the registry (allow_update_command defaults True), so they can be dropped following the chore(gateway): drop plugin-migrated platforms from /update allowlist #32525 cleanup. Harmless if left.
  • Stale module-path strings in docstrings/usage text: plugins/platforms/feishu/adapter.py:2466, plugins/platforms/feishu/feishu_comment_rules.py:360 (the python -m ... usage line), gateway/run.py:13470. Text-only; runtime imports are correct.

Coordination note

This touches gateway/run.py, gateway/config.py, tools/send_message_tool.py, and tests/gateway/test_platform_connected_checkers.py — the same shared surfaces as the in-flight yuanbao (#40804), signal (#41061), qqbot (#41065), and weixin (#41101) plugin-migration PRs. Whichever lands second will need a small rebase on those files (_create_adapter factory, the connected-checkers guard, the _UPDATE_ALLOWED_PLATFORMS frozenset).

teknium1 added a commit that referenced this pull request Jun 20, 2026
…gram/wecom/email/sms adapters to bundled plugins

Salvage of PR #41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
teknium1 added a commit that referenced this pull request Jun 20, 2026
…gram/wecom/email/sms adapters to bundled plugins

Salvage of PR #41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
@teknium1

Copy link
Copy Markdown
Contributor Author

Merged via #49408 onto current main (commits 5600105 + c329279).

Your branch was ~1340 commits stale, so a plain cherry-pick would have reverted large swaths of unrelated work. The migration was re-applied surgically on current main with your authorship preserved per-commit (rebase merge): the 9 adapters + satellites relocated via git mv, each migration delta re-applied, and the core touchpoints re-stripped against the refactored gateway/run.py / gateway/config.py / hermes_cli/*.

Two things that had drifted since this PR opened were also fixed in the salvage:

  • The matrix plugin's apply_yaml_config_fn gained the 4 keys main added to the inline block in the interim (allowed_users, ignore_user_patterns, process_notices, session_scope) — they would otherwise have been silently dropped.
  • ~12 test files added on main since the base still imported gateway.platforms.* (incl. from … import X as Y and source-by-path reads) — swept and retargeted.

Verified end-to-end before merge: registry resolves all 10 platforms; _create_adapter check_fn gate, auth allowlist map, cron home-channel map, _UPDATE_ALLOWED_PLATFORMS, and max_message_length (slack 39000 / feishu 8000) all preserved; 7539 tests pass locally; CI green and CLEAN.

@teknium1 teknium1 closed this Jun 20, 2026
kpadilha pushed a commit to kpadilha/hermes-agent that referenced this pull request Jun 24, 2026
…gram/wecom/email/sms adapters to bundled plugins

Salvage of PR NousResearch#41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
pai-scaffolde pushed a commit to pai-scaffolde/hermes-agent that referenced this pull request Jun 28, 2026
…gram/wecom/email/sms adapters to bundled plugins

Salvage of PR NousResearch#41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…gram/wecom/email/sms adapters to bundled plugins

Salvage of PR NousResearch#41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
…gram/wecom/email/sms adapters to bundled plugins

Salvage of PR NousResearch#41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…gram/wecom/email/sms adapters to bundled plugins

Salvage of PR NousResearch#41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…gram/wecom/email/sms adapters to bundled plugins

Salvage of PR NousResearch#41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…gram/wecom/email/sms adapters to bundled plugins

Salvage of PR NousResearch#41284 onto current main. Relocates the last 9 inline messaging
adapters (+ satellites: telegram_network, feishu_comment/_rules/meeting_invite,
wecom_crypto, wecom_callback) from gateway/platforms/ into self-contained
bundled plugins under plugins/platforms/<x>/, discovered via the platform
registry. Strips the per-platform core touchpoints from gateway/run.py,
gateway/config.py, hermes_cli/gateway.py, hermes_cli/setup.py, and
tools/send_message_tool.py.

Carries forward the migration fixes (explicit enabled:false honored,
get_connected_platforms forces discovery, plugin is_connected via
gateway.get_env_value, logs --component gateway matches plugins.platforms.*,
matrix hidden on Windows).

Additionally ports config keys main added since the PR base: the matrix
plugin's _apply_yaml_config now also covers allowed_users,
ignore_user_patterns, process_notices, and session_scope (the inline
gateway/config.py matrix block gained these in the 1340 commits the PR sat
open; they would otherwise have been silently dropped on deletion).
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 type/refactor Code restructuring, no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants