feat(gateway): add SimpleX Chat platform plugin (salvages #2558)#26232
Merged
Conversation
SimpleX Chat (https://simplex.chat) is a private, decentralised messenger with no persistent user IDs — every contact is identified by an opaque internal ID generated at connection time. This adds it as a Hermes gateway platform via the plugin system. The adapter connects to a local simplex-chat daemon via WebSocket, listens for inbound messages, and sends replies. Originally proposed in PR #2558 as a core-modifying integration; reshaped here as a self- contained plugin under plugins/platforms/simplex/ with no edits to any core file. Discovery is filesystem-based (scanned by gateway.config), and the platform identity is resolved on demand via Platform("simplex"). Plugin contract: - check_requirements() requires SIMPLEX_WS_URL AND the websockets package - validate_config() / is_connected() accept env or config.yaml input - _env_enablement() seeds PlatformConfig.extra (ws_url + home_channel) - _standalone_send() supports out-of-process cron delivery - interactive_setup() provides a stdin wizard for hermes gateway setup - register() wires the adapter into the registry with required_env, install_hint, cron_deliver_env_var, allowed_users_env, and a platform_hint for the LLM. Lazy dependency: the websockets Python package is imported inside the functions that need it. The plugin is importable and discoverable even when websockets is missing — check_requirements() simply returns False until `pip install websockets` is run. No new pyproject extras are introduced. Environment variables: SIMPLEX_WS_URL WebSocket URL of the daemon (required) SIMPLEX_ALLOWED_USERS Comma-separated allowed contact IDs SIMPLEX_ALLOW_ALL_USERS Set true to allow all contacts SIMPLEX_HOME_CHANNEL Default contact for cron delivery SIMPLEX_HOME_CHANNEL_NAME Human label for the home channel Closes #2557.
- Adds plugins/platforms/simplex docs page to the messaging sidebar between LINE and Open WebUI. - Maps louismichalot@hotmail.com -> Mibayy in scripts/release.py so the attribution check on the salvage PR passes.
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
unresolved-import |
4 |
invalid-argument-type |
3 |
unused-type-ignore-comment |
1 |
First entries
run_agent.py:7482: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
tests/gateway/test_simplex_plugin.py:14: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tests/gateway/test_simplex_plugin.py:61: [unresolved-import] unresolved-import: Cannot resolve imported module `websockets`
run_agent.py:13750: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
tests/gateway/test_simplex_plugin.py:260: [unused-type-ignore-comment] unused-type-ignore-comment: Unused blanket `type: ignore` directive
tests/gateway/test_simplex_plugin.py:313: [unresolved-import] unresolved-import: Cannot resolve imported module `websockets.client`
plugins/platforms/simplex/adapter.py:632: [unresolved-import] unresolved-import: Cannot resolve imported module `websockets`
run_agent.py:13753: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown, Unknown] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 3 union elements`
✅ Fixed issues (3):
| Rule | Count |
|---|---|
invalid-argument-type |
3 |
First entries
run_agent.py:13750: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 3 union elements`
run_agent.py:13753: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown | str, Unknown | str | dict[str, str]] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 3 union elements`
run_agent.py:7482: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 3 union elements`
Unchanged: 4308 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
1 task
|
I've got SimpleX connected and environment variables configured, but I receive no responses when I message my agent. |
19 tasks
linxule
pushed a commit
to linxule/hermes-agent
that referenced
this pull request
Jun 1, 2026
Bundled platform plugin following the LINE precedent (NousResearch#23197) and the more recent SimpleX precedent (NousResearch#26232). KimiClaw is Moonshot AI's agentic bot platform on kimi.com (launched Feb 2026). Distinct from the Moonshot LLM provider (kimi-for-coding profile in plugins/model-providers/kimi-coding/, aliased in hermes_cli/providers.py) — this PR adds a separate chat platform and does not touch that integration. The adapter bridges two channels under one bot identity: - DM via Zed ACP over WebSocket (sentinel session im:kimi:main) - Group rooms via Connect RPC Subscribe server-stream over HTTP/1.1 long-poll (chat_id room:<uuid>); outbound replies via unary Connect RPC SendMessage Deployment model: Moonshot's intended bot deployment uses the official OpenClaw runtime (claw-install.sh, V2026.4.5+) to own the wire connection. This plugin instead speaks the wire protocol directly from Python while sending the OpenClaw-shaped runtime metadata headers kimi.com gates group-room participation on ("OpenClaw 3.13 or above"). Identity layers are honest: User-Agent is hermes-kimi-adapter/1.0, X-Kimi-Claw-ID is prefixed hermes-kimi-, X-Kimi-OpenClaw-Skills is suppressed by default, and X-Kimi-OpenClaw-Version is set to the documented group-gate floor (2026.3.13) — not a "real install" value. All five OpenClaw-shaped headers are overridable via config.extra. See plugins/platforms/kimiclaw/adapter.py:23-28 (module docstring) and :103-112 (_GROUP_GATE_DEFAULTS) for the rationale. Production wear: the same adapter code has run on one Raspberry Pi gateway under daily user traffic since 2026-04-27. The adapter previously shipped (and continues to ship) as an external plugin at linxule/hermes-kimi-plugin; this PR upstreams it at version 1.0.0 — a stable snapshot intentionally decoupled from the external repo's bleeding-edge stream. Files: - plugins/platforms/kimiclaw/{__init__,adapter,plugin}.{py,yaml} - tests/gateway/test_kimiclaw_plugin.py - website/docs/user-guide/messaging/kimiclaw.md (setup + deployment model + known limitations) - website/docs/reference/environment-variables.md (+ KIMI_* table) - website/docs/user-guide/messaging/index.md (capability row) - website/sidebars.ts (sidebar entry) - cli-config.yaml.example (kimiclaw: bridge block) - pyproject.toml + uv.lock (declares websockets==15.0.1 in [messaging] extra for the DM ACP socket) - scripts/release.py (AUTHOR_MAP attribution entry) Internal class names retained as KimiAdapter / check_kimi_requirements — implementation detail; matches the QQAdapter precedent. Env-var names retained as KIMI_* because credentials are issued by kimi.com itself. 261 tests passing (pytest tests/gateway/test_kimiclaw_plugin.py). Includes regression coverage for the tool_only DM-inflight closure path (test_3b_4, test_3b_5), bug-fixes for 429 retryability and upload-path exception handling in the standalone send code path, and the registry-gate (_check_for_registry) requiring KIMI_BOT_TOKEN before auto-enable so messaging-equipped installs don't light up KimiClaw without credentials.
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.
Salvages #2558 (@Mibayy) onto current
main, reshaped from a 12-file core integration into a self-contained platform plugin.What this PR does
Adds SimpleX Chat — a private, decentralised messenger with no persistent user IDs — as a Hermes gateway platform under
plugins/platforms/simplex/. The adapter connects to a localsimplex-chatdaemon via WebSocket and relays messages between SimpleX contacts/groups and the agent.Plugin shape (vs. the original PR)
The original PR (#2558) touched 12 files including 11 core files (gateway enum, run.py adapter factory, prompt builder, cron scheduler, send_message_tool, channel_directory, status, gateway CLI, toolsets). This salvage delivers the same functionality with zero core edits — the platform is discovered by the existing plugin filesystem scan in
gateway/config.pyand wired throughctx.register_platform(), the same contract used byirc,line,google_chat, andteamsplugins.plugins/platforms/simplex/plugin.yaml— declares required/optional env vars;hermes configUI picks them up via the platform-plugin env-var injector.plugins/platforms/simplex/adapter.py—SimplexAdapter+ plugin hooks (check_requirements,validate_config,is_connected,_env_enablement,_standalone_send,interactive_setup,register).plugins/platforms/simplex/__init__.py— exposesregisterfor the plugin loader.Lazy dependency install
The
websocketsPython package is the only external dep. It is imported lazily inside the functions that need it —SimplexAdapter.connect(),_ws_listener(),_send_ws(), and_standalone_send(). The module top imports only stdlib and Hermes' owngateway.config/gateway.platforms.base. This means:websocketsis not installed.check_requirements()returnsFalseuntil bothSIMPLEX_WS_URLis set ANDwebsocketsimports successfully, so the gateway never attempts to construct the adapter without its dependency.install_hint="pip install websockets"is surfaced viahermes setup gatewayandhermes gateway status.Environment variables
SIMPLEX_WS_URLws://127.0.0.1:5225)SIMPLEX_ALLOWED_USERSSIMPLEX_ALLOW_ALL_USERStrueto allow every contact (dev only)SIMPLEX_HOME_CHANNELSIMPLEX_HOME_CHANNEL_NAMEFiles (7)
Tests
27 new tests covering: Platform enum auto-discovery,
check_requirementsgating,validate_config/is_connectedenv+extra precedence,_env_enablementseeding (including home_channel), adapter init defaults, magic-byte file extension detection, correlation ID generation and self-trim, outbound send for DMs and groups, own-echo filtering by corrId prefix, standalone send paths (missing websockets / missing URL), andregister()metadata.Credit
The adapter logic — WebSocket lifecycle, reconnect backoff with jitter, health monitor,
newChatItem/newChatItemsevent handling, DM / group routing,@[contact-id]/#[group-id]command shape, file attachment caching — is @Mibayy's original work, cherry-picked here with their authorship preserved on the feat commit.Closes #2557. Original PR #2558 will be closed pointing to this one.