NousResearch/hermes-agent

Feishu group chat WebSocket event delivery fixes

Cluster 50656 · gateway. Data as of

Nothing open on GitHub. Fix merged in #61770 on 10 Jul 2026. Check it covers your case.

The short answer

0open issues
0open PRs
3items grouped
Known?
Yes. 3 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #61770. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #61770. That is separate from the fix event.
Still open
Nothing open on GitHub.

Multiple bug reports and fix attempts addressing Feishu/Lark group chat messages and @mentions not being received or routed through the WebSocket gateway. A minor unrelated gateway fix for file path rejection is also present.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #61770 fix(feishu): restore group events over WebSocket

    merged to main

    files: infographic/feishu-group-events/infographic.png, plugins/platforms/feishu/adapter.py, pyproject.toml, scripts/release.py, tests/gateway/test_feishu.py, tools/lazy_deps.py

    Show the diff
    • plugins/platforms/feishu/adapter.py
      @@ -4730,6 +4730,12 @@ async def _connect_websocket(self) -> None:
                   log_level=lark.LogLevel.INFO,
                   event_handler=self._event_handler,
                   domain=domain,
      +            # Channel SDK signaling tag: without this UA tag the Feishu
      +            # server does not push group @mention events over the WebSocket
      +            # transport.  The tag tells the server to use the Channel protocol
      +            # which enables group-message routing in addition to P2P DM.
      +            # See https://github.com/NousResearch/hermes-agent/issues/50656
      +            extra_ua_tags=["channel"],
               )
               self._ws_future = loop.run_in_executor(
                   None,
    • pyproject.toml
      @@ -249,7 +249,7 @@ termux-all = [
         "hermes-agent[web]",
       ]
       dingtalk = ["dingtalk-stream==0.24.3", "alibabacloud-dingtalk==2.2.42", "qrcode==7.4.2"]
      -feishu = ["lark-oapi==1.5.3", "qrcode==7.4.2"]
      +feishu = ["lark-oapi==1.6.8", "qrcode==7.4.2"]
       google = [
         # Required by the google-workspace skill (Gmail, Calendar, Drive, Contacts,
         # Sheets, Docs).  Declared here so packagers (Nix, Homebrew) ship them with
    • scripts/release.py
      @@ -46,7 +46,7 @@
       # Auto-extracted from noreply emails + manual overrides
       AUTHOR_MAP = {
           "[email protected]": "lemonwan",  # PR #59430 sibling salvage (adapter reconnect contract guard)
      -    "[redacted:email]": "luxuguang-leo",  # PR #52966 salvage (QQBot reconnect contract)
      +    "[redacted:email]": "luxuguang-leo",  # PR #52966 + #52908 salvage (QQBot reconnect + Feishu Channel signaling)
           "[redacted:email]": "evelynburger",  # PR #57544 salvage (gateway: webhook payload filters + route scripts; commit under unlinked identity)
           "[redacted:email]": "SiteupAgencia",  # PR #57435 salvage (tui_gateway: back off notification poller when session is busy; #55578)
           "[email protected]": "rainbowgore",  # PR #59405 salvage (mcp: bound stdio initialize handshake to stop subprocess/FD leak; #59349)
    • tests/gateway/test_feishu.py
      @@ -173,6 +173,16 @@ def test_normalize_interactive_card_preserves_title_body_and_actions(self):
       
       
       class TestFeishuAdapterMessaging(unittest.TestCase):
      +    @unittest.skipUnless(_HAS_LARK_OAPI, "lark-oapi not installed")
      +    def test_websocket_sdk_accepts_channel_ua_tag(self):
      +        """The shipped SDK must support the Channel signaling argument."""
      +        import inspect
      +
      +        from lark_oapi.ws import Client as FeishuWSClient
      +
      +        signature = inspect.signature(FeishuWSClient)
      +        self.assertIn("extra_ua_tags", signature.parameters)
      +
           @patch.dict(os.environ, {
               "FEISHU_APP_ID": "cli_app",
               "FEISHU_APP_SECRET": "[redacted:secret-assignment]",
      @@ -414,6 +424,65 @@ def is_closed(self):
               self.assertEqual(sleeps, [1])
               self.assertEqual(fake_loop.calls, 2)
       
      +    @patch.dict(os.environ, {
      +        "FEISHU_APP_ID": "cli_app",
      +        "FEISHU_APP_SECRET": "[redacted:secret-assignment]",
      +    }, clear=True)
      +    def test_connect_websocket_sets_channel_ua_tag(self):
      +        """Verify that FeishuWSClient receives extra_ua_tags=["channel"].
      +
      +        Without this UA tag the Feishu server does not push group @mention
      +        events over the WebSocket transport.  See
      +        https://github.com/NousResearch/hermes-agent/issues/50656
      +        """
      +        from gateway.config import PlatformConfig
      +        from plugins.platforms.feishu.adapter import FeishuAdapter
      +
      +        adapter = FeishuAdapter(PlatformConfig())
      +        ws_client = SimpleNamespace()
      +
      +        with (
      +            patch("plugins.platforms.feishu.adapter.FEISHU_AVAILABLE", True),
      +            patch("plugins.platforms.feishu.adapter.FEISHU_WEBSOCKET_AVAILABLE", True),
      +            patch("plugins.platforms.feishu.adapter.lark",
      +                  SimpleNamespace(LogLevel=SimpleNamespace(INFO="INFO", WARNING="WARNING"))),
      +            patch("plugins.platforms.feishu.adapter.EventDispatcherHandler") as mock_handler_class,
      +            patch("plugins.platforms.feishu.adapter.FeishuWSClient") as mock_ws_client,
      +            patch("plugins.platforms.feishu.adapter._run_official_feishu_ws_client"),
      +            patch("plugins.platforms.feishu.adapter.acquire_scoped_lock", return_value=(True, None)),
      +            patch("plugins.platforms.feishu.adapter.release_scoped_lock"),
      +            patch.object(adapter, "_hydrate_bot_identity", new=AsyncMock()),
      +            patch.object(adapter, "_build_lark_client", return_value=SimpleNamespace()),
      +        ):
      +            _mock_event_dispatcher_builder(mock_handler_class)
      +
      +            loop = asyncio.new_event_loop()
      +            future = loop.create_future()
      +            future.set_result(None)
      +
      +            class _Loop:
      +                def run_in_executor(self, *_args, **_kwargs):
      +                    return future
      +                def is_closed(self):
      +                    return False
      +
      +            try:
      +                with patch("plugins.platforms.feishu.adapter.asyncio.get_running_loop",
      +                           return_value=_Loop()):
      +                    connected = asyncio.run(adapter.connect())
      +            finally:
      +                loop.close()
      +
      +        self.assertTrue(connected)
      +        # Verify the Channel SDK UA tag is present — this is the fix for
      +        # group @mention message delivery over WebSocket.
      +        mock_ws_client.assert_called_once()
      +        call_kwargs = mock_ws_client.call_args.kwargs
      +        self.assertIn("extra_ua_tags", call_kwargs,
      +                      "FeishuWSClient must receive extra_ua_tags for group @mention delivery")
      +        self.assertEqual(call_kwargs["extra_ua_tags"], ["channel"],
      +                         "extra_ua_tags must be ['channel'] to enable group event routing")
      +
           @patch.dict(os.environ, {}, clear=True)
           def test_edit_message_updates_existing_feishu_message(self):
               from gateway.config import PlatformConfig
    • tools/lazy_deps.py
      @@ -188,7 +188,7 @@
               "qrcode==7.4.2",
           ),
           "platform.feishu": (
      -        "lark-oapi==1.5.3",
      +        "lark-oapi==1.6.8",
               "qrcode==7.4.2",
           ),
           # WeCom callback-mode adapter — parses untrusted XML POST bodies. Pulls
    • uv.lock
      @@ -1794,7 +1794,7 @@ requires-dist = [
           { name = "honcho-ai", marker = "extra == 'honcho'", specifier = "==2.0.1" },
           { name = "httpx", extras = ["socks"], specifier = "==0.28.1" },
           { name = "jinja2", specifier = "==3.1.6" },
      -    { name = "lark-oapi", marker = "extra == 'feishu'", specifier = "==1.5.3" },
      +    { name = "lark-oapi", marker = "extra == 'feishu'", specifier = "==1.6.8" },
           { name = "markdown", specifier = "==3.10.2" },
           { name = "mautrix", extras = ["encryption"], marker = "extra == 'matrix'", specifier = "==0.21.0" },
           { name = "mcp", marker = "extra == 'computer-use'", specifier = "==1.26.0" },
      @@ -2184,7 +2184,7 @@ wheels = [
       
       [[package]]
       name = "lark-oapi"
      -version = "1.5.3"
      +version = "1.6.8"
       source = { registry = "https://pypi.org/simple" }
       dependencies = [
           { name = "httpx" },
      @@ -2194,7 +2194,7 @@ dependencies = [
           { name = "websockets" },
       ]
       wheels = [
      -    { url = "https://files.pythonhosted.org/packages/bf/ff/2ece5d735ebfa2af600a53176f2636ae47af2bf934e08effab64f0d1e047/lark_oapi-1.5.3-py3-none-any.whl", hash = "sha256:fda6b32bb38d21b6bdaae94979c600b94c7c521e985adade63a54e4b3e20cc36", size = 6993016, upload-time = "2026-01-27T08:21:49.307Z" },
      +    { url = "https://files.pythonhosted.org/packages/4a/ad/1ab04db5d549ad1a7a2cd33682b1c38dee1d65019cb24fd7a23270e6337d/lark_oapi-1.6.8-py3-none-any.whl", hash = "sha256:9b443a5d47a7d204dd42dc40896c8b75087cc35788e45c48c140806d7df7e5e8", size = 7798300, upload-time = "2026-06-02T07:40:05.492Z" },
       ]
       
       [[package]]

1 PR closed without merging

From the close comments: merged via another change 1

  • #52908 fix(feishu): add extra_ua_tags=["channel"] to FeishuWSClient for group @mention delivery · merged via another change → #61770
Closed issues · 1
  • Closed#50656[Bug]: Feishu/Lark group @mention messages not delivered — WebSocket never receives group events
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
gateway
Merged PRs
#61770
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
10 Jul 2026

For your agent

The same facts, typed.