feat: pluggable gateway platform interface — any messaging platform as a drop-in plugin#7942
Closed
teknium1 wants to merge 6 commits into
Closed
feat: pluggable gateway platform interface — any messaging platform as a drop-in plugin#7942teknium1 wants to merge 6 commits into
teknium1 wants to merge 6 commits into
Conversation
Adds a platform adapter plugin interface so anyone can create new gateway
platforms (IRC, Viber, Line, etc.) as drop-in plugins without modifying
core gateway code.
## Platform Registry (gateway/platform_registry.py)
- PlatformEntry dataclass: name, label, adapter_factory, check_fn,
validate_config, required_env, install_hint, source
- PlatformRegistry singleton with register/unregister/create_adapter
- _create_adapter() in gateway/run.py checks registry first, falls
through to existing if/elif chain for built-in platforms
## Dynamic Platform Enum (gateway/config.py)
- Platform._missing_() accepts unknown string values, creating cached
pseudo-members so Platform('irc') is Platform('irc') holds true
- GatewayConfig.from_dict() now parses plugin platform names from
config.yaml without rejecting them
- get_connected_platforms() delegates to registry for unknown platforms
## Plugin Registration (hermes_cli/plugins.py)
- PluginContext.register_platform() for plugin authors
- Mirrors the existing register_tool() / register_hook() pattern
## IRC Reference Plugin (plugins/platforms/irc/)
- Full async IRC adapter using stdlib asyncio (zero external deps)
- Connects via TLS, handles PING/PONG, nick collision, NickServ auth
- Channel messages require addressing (nick: msg), DMs always dispatch
- Markdown stripping for IRC-clean output, message splitting for
512-byte line limit
- Config via config.yaml extra dict or IRC_* env vars
## Tests (55 new tests)
- Platform enum dynamic members (identity stability, case normalization)
- PlatformRegistry (register, unregister, create, validation, factory)
- GatewayConfig integration (from_dict parsing, get_connected_platforms)
- IRC adapter (init, send, protocol parsing, markdown, requirements)
No existing platform adapters were migrated — the if/elif chain is
untouched. This is Phase 1: prove the interface with a real plugin.
Extends the platform plugin interface from Phase 1 to cover every touchpoint where built-in platforms have hardcoded behavior. ## PlatformEntry extended fields - allowed_users_env / allow_all_env: per-platform auth env vars - max_message_length: smart-chunking for send_message tool - pii_safe: session PII redaction flag - emoji: CLI/gateway display - allow_update_command: /update access control ## Functional fixes (Tier 1) send_message tool (tools/send_message_tool.py): - Replaced hardcoded platform_map dict with Platform() call - Added _send_via_adapter() for plugin platforms — routes through live gateway adapter when available - Registry-aware max message length for smart chunking Cron delivery (cron/scheduler.py): - Replaced hardcoded 15-entry platform_map with Platform() call - Plugin platforms now work as cron delivery targets User authorization (gateway/run.py _is_user_authorized): - Registry fallback: checks PlatformEntry.allowed_users_env and allow_all_env when platform not in hardcoded maps - Plugin platforms get per-platform auth support ## Integration fixes (Tier 2) _UPDATE_ALLOWED_PLATFORMS: checks registry allow_update_command flag Channel directory: includes plugin platforms in session enumeration Orphaned config warning: descriptive message when plugin platform is in config but no plugin registered it Gateway weakref: _gateway_runner_ref for cross-module adapter access ## UX completeness hermes status: shows plugin platforms with (plugin) tag hermes gateway setup: plugin platforms appear in menu with setup hints hermes_cli/platforms.py: get_all_platforms() merges with registry, platform_label() falls back to registry for plugin names ## Tests - 8 new tests (extended fields, cron resolution, platforms merge) - Updated 3 tests for new Platform() based resolution - 2829 passed, 24 pre-existing failures, zero new failures
PII redaction: build_session_context_prompt() now checks the plugin registry's pii_safe flag in addition to the hardcoded _PII_SAFE_PLATFORMS frozenset. Plugin platforms that set pii_safe=True (e.g. phone-based messaging bridges) get their user IDs redacted before LLM context. Token empty warnings: the empty-token diagnostic at config load now checks the plugin registry's required_env when a platform isn't in the hardcoded _token_env_names dict. Catches 'enabled but empty' for plugin platforms too.
# Conflicts: # cron/scheduler.py # tools/send_message_tool.py
…, docs
Closes remaining functional gaps and adds documentation.
## Functional fixes
webhook.py: Cross-platform delivery now checks the plugin registry
for unknown platform names instead of hardcoding 15 names in a tuple.
Plugin platforms can receive webhook-routed deliveries.
prompt_builder: Platform hints (system prompt LLM guidance) now fall
back to the plugin registry's platform_hint field. Plugin platforms
can tell the LLM 'you're on IRC, no markdown.'
PlatformEntry: Added platform_hint field for LLM guidance injection.
IRC adapter: Added acquire_scoped_lock/release_scoped_lock in
connect/disconnect to prevent two profiles from using the same IRC
identity. Added platform_hint for IRC-specific LLM guidance.
Removed dead token-empty-warning extension for plugin platforms
(plugin adapters handle their own env vars via check_fn).
## Documentation
website/docs/developer-guide/adding-platform-adapters.md:
- Added 'Plugin Path (Recommended)' section with full code examples,
PLUGIN.yaml template, config.yaml examples, and a table showing all
18 integration points the plugin system handles automatically
- Renamed built-in checklist to clarify it's for core contributors
gateway/platforms/ADDING_A_PLATFORM.md:
- Added Plugin Path section pointing to the reference implementation
and full docs guide
- Clarified built-in path is for core contributors only
Plugin platforms now get full toolset support without any entries in toolsets.py. tools_config._get_platform_tools(): Falls back to 'hermes-<name>' when the platform isn't in the static PLATFORMS dict. No more KeyError for plugin platforms. toolsets.resolve_toolset(): Auto-generates a toolset for plugin platforms (hermes-<name>) containing _HERMES_CORE_TOOLS plus any tools the plugin registered into a matching toolset name. This means a plugin can call ctx.register_tool(toolset='irc', ...) and those tools will be included in the hermes-irc toolset automatically. webhook.py: Registry-aware cross-platform delivery. run_agent.py: Platform hints from plugin registry. IRC adapter: Token lock + platform hint. Removed dead token-empty-warning extension. Updated docs.
Collaborator
|
superseded by #17664 |
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
Adds a complete plugin interface for gateway platform adapters. Anyone can now add a new messaging platform (IRC, Viber, Line, custom bridges, etc.) by dropping a plugin directory into
~/.hermes/plugins/— zero core code changes needed. The plugin uses the samePluginContextas existing plugins, so a single plugin can register a platform adapter AND custom tools, hooks, and CLI commands.Includes a full IRC adapter as the reference implementation to prove the interface end-to-end.
If all 19 built-in platforms were migrated to plugins, ~48,000 lines would leave the core codebase (~24,500 adapter code + ~21,300 tests + ~2,600 hardcoded glue).
Architecture
A plugin platform is just a regular plugin that also calls
ctx.register_platform():What Changed (25 files, +2080 / -105)
Core Infrastructure
gateway/platform_registry.pygateway/config.pygateway/run.pyhermes_cli/plugins.pytoolsets.pyhermes_cli/tools_config.pyIntegration Points (18 total — full parity with built-ins)
hermes statushermes gateway setuphermes tools/hermes skillsIRC Reference Plugin (
plugins/platforms/irc/)extradict or IRC_* env varsDocumentation
website/docs/developer-guide/adding-platform-adapters.md— full "Plugin Path (Recommended)" section with code template, config examples, and 18-row integration tablegateway/platforms/ADDING_A_PLATFORM.md— plugin path section with pointer to reference implementationTests
How to Use (Plugin Author)
mkdir -p ~/.hermes/plugins/my-platformCreate
PLUGIN.yaml+adapter.py(seeplugins/platforms/irc/or the developer guide).Configure in
config.yaml:That's it. The gateway discovers the plugin, creates the adapter, and routes messages through it.
Design Decisions
_missing_()creates cached pseudo-members soPlatform("irc") is Platform("irc")holds true. No enum modification needed.resolve_toolset("hermes-irc")returns _HERMES_CORE_TOOLS + any tools registered withtoolset="irc". No toolsets.py entry needed.