Skip to content

fix: improve platform gateway reliability — multi-process protection, hook chat_id, and Feishu event queueing - #4789

Closed
milkoor wants to merge 2 commits into
NousResearch:mainfrom
milkoor:fix/feishu-gateway-reliability
Closed

fix: improve platform gateway reliability — multi-process protection, hook chat_id, and Feishu event queueing#4789
milkoor wants to merge 2 commits into
NousResearch:mainfrom
milkoor:fix/feishu-gateway-reliability

Conversation

@milkoor

@milkoor milkoor commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Problem

Platform gateways (Feishu, Telegram, etc.) suffer from three reliability issues:

  1. Silent message drops: When the Feishu WebSocket callback fires before the main event loop is ready, inbound messages are dropped with a warning log but never delivered.
  2. Concurrent gateway processes: Multiple gateway processes (e.g. hermes run + hermes gateway) share the lark_oapi global state, meaning only the last-started process receives events. This causes silent disconnections where Feishu shows connected but messages go unanswered.
  3. Missing chat_id in hook context: Gateway hooks (e.g. card-streaming hooks) receive session_id but not chat_id, preventing platform adapters from creating platform-specific cards/messages without using internal session IDs.

Changes

gateway/run.py

  • Add chat_id to hook_ctx for agent:start events, enabling hook handlers to target the correct platform destination

hermes_cli/gateway.py

  • Prevent concurrent gateway instances by checking for existing gateway processes before starting
  • When an existing instance is detected, the new instance aborts with a helpful message (hermes gateway run --replace to override)

gateway/platforms/feishu.py

  • Queue inbound Feishu WebSocket events when the adapter loop is not yet ready, then replay them once the loop is available
  • Previously these events were silently dropped, causing users to lose their first message after gateway restart

Testing

  • ✅ All three patches apply cleanly against latest main (cc54818)
  • ✅ Gateway syntax check passes for all modified files
  • ✅ Tested on live Feishu gateway — messages flow correctly after restart

… hook context chat_id, and Feishu event queueing

- Add chat_id to agent:start hook context for platform adapters to create
  platform-specific cards without using internal session IDs
- Prevent concurrent gateway instances from sharing lark_oapi global
  state (causes event delivery failures and silent disconnections)
- Queue Feishu WebSocket events when adapter loop is not yet ready
  instead of silently dropping inbound messages
- Add --force-restart and --force-kill options to monitor_gateway.py
  with stale state detection and append-only logging

Co-authored-by: Hermes Agent
@britrik

britrik commented Apr 3, 2026

Copy link
Copy Markdown

Code Review: PR #4789

Summary

The PR introduces three reliability improvements:

  1. Feishu event queueing - queues events when the adapter loop isn't ready
  2. chat_id hook addition - adds chat_id to the agent:start hook context
  3. Multi-process protection - prevents duplicate gateway instances

Issues & Suggestions

1. Feishu Event Queueing (gateway/platforms/feishu.py)

Issue: Thread spawning per event is wasteful

Every inbound message event before the loop is ready spawns a new thread. If 10 events arrive quickly, that's 10 threads. Consider using a single background worker or batching.

Issue: No bounds checking on _pending_events
If the loop never becomes ready (e.g., prolonged startup issue), the list grows unbounded, potentially causing memory exhaustion. Consider adding a max queue size.

Issue: Silent failure in _drain_pending_events
The call in has no done_callback to log failures. If an event fails to process, it silently disappears.

Suggestion: Add a try/except around the coroutine call:


2. Multi-Process Protection (hermes_cli/gateway.py)

Issue: Fragile environment variable check

This could be bypassed if another process sets this env var. Consider using a lock file or a more robust mechanism like PID files with validation.

Positive: The find_gateway_pids() function is solid and handles both Linux/macOS and Windows.


3. chat_id Hook Addition (gateway/run.py)

Looks good: Straightforward addition. The is used extensively elsewhere in the codebase (lines 1717, 1727, 2477, etc.), so this should work correctly.


Overall

The changes address real reliability issues. The Feishu queueing could be improved with a single worker thread instead of per-event threads, and failure handling could be better. The multi-process protection and chat_id hook are solid.

…event SDK error loops

The Lark SDK emits persistent errors for unhandled
im.chat.access_event.bot_p2p_chat_entered_v1 events (triggered
when users open the bot DM chat). These errors eventually cause
the WebSocket connection to drop.

Register a no-op handler to suppress the errors.

Fixes: #4789
teknium1 added a commit that referenced this pull request Apr 17, 2026
Inbound Feishu messages arriving during brief windows when the adapter
loop is unavailable (startup/restart transitions, network-flap reconnect)
were silently dropped with a WARNING log. This matches the symptom in
issue #5499 — and users have reported seeing only a subset of their
messages reach the agent.

Fix: queue pending events in a thread-safe list and spawn a single
drainer thread that replays them once the loop becomes ready. Covers
these scenarios:

  * Queue events instead of dropping when loop is None/closed
  * Single drainer handles the full queue (not thread-per-event)
  * Thread-safe with threading.Lock on the queue and schedule flag
  * Handles mid-drain bursts (new events arrive while drainer is working)
  * Handles RuntimeError if loop closes between check and submit
  * Depth cap (1000) prevents unbounded growth during extended outages
  * Drops queue cleanly on disconnect rather than holding forever
  * Safety timeout (120s) prevents infinite retention on broken adapters

Based on the approach proposed in #4789 by milkoor, rewritten for
thread-safety and correctness.

Test plan:
  * 5 new unit tests (TestPendingInboundQueue) — all passing
  * E2E test with real asyncio loop + fake WS thread: 10-event burst
    before loop ready → all 10 delivered in order
  * E2E concurrent burst test: 20 events queued, 20 more arrive during
    drainer dispatch → all 40 delivered, no loss, no duplicates
  * All 111 existing feishu tests pass

Related: #5499, #4789

Co-authored-by: milkoor <milkoor@users.noreply.github.com>
teknium1 added a commit that referenced this pull request Apr 17, 2026
…#11372)

Inbound Feishu messages arriving during brief windows when the adapter
loop is unavailable (startup/restart transitions, network-flap reconnect)
were silently dropped with a WARNING log. This matches the symptom in
issue #5499 — and users have reported seeing only a subset of their
messages reach the agent.

Fix: queue pending events in a thread-safe list and spawn a single
drainer thread that replays them once the loop becomes ready. Covers
these scenarios:

  * Queue events instead of dropping when loop is None/closed
  * Single drainer handles the full queue (not thread-per-event)
  * Thread-safe with threading.Lock on the queue and schedule flag
  * Handles mid-drain bursts (new events arrive while drainer is working)
  * Handles RuntimeError if loop closes between check and submit
  * Depth cap (1000) prevents unbounded growth during extended outages
  * Drops queue cleanly on disconnect rather than holding forever
  * Safety timeout (120s) prevents infinite retention on broken adapters

Based on the approach proposed in #4789 by milkoor, rewritten for
thread-safety and correctness.

Test plan:
  * 5 new unit tests (TestPendingInboundQueue) — all passing
  * E2E test with real asyncio loop + fake WS thread: 10-event burst
    before loop ready → all 10 delivered in order
  * E2E concurrent burst test: 20 events queued, 20 more arrive during
    drainer dispatch → all 40 delivered, no loss, no duplicates
  * All 111 existing feishu tests pass

Related: #5499, #4789

Co-authored-by: milkoor <milkoor@users.noreply.github.com>
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
…search#5499) (NousResearch#11372)

Inbound Feishu messages arriving during brief windows when the adapter
loop is unavailable (startup/restart transitions, network-flap reconnect)
were silently dropped with a WARNING log. This matches the symptom in
issue NousResearch#5499 — and users have reported seeing only a subset of their
messages reach the agent.

Fix: queue pending events in a thread-safe list and spawn a single
drainer thread that replays them once the loop becomes ready. Covers
these scenarios:

  * Queue events instead of dropping when loop is None/closed
  * Single drainer handles the full queue (not thread-per-event)
  * Thread-safe with threading.Lock on the queue and schedule flag
  * Handles mid-drain bursts (new events arrive while drainer is working)
  * Handles RuntimeError if loop closes between check and submit
  * Depth cap (1000) prevents unbounded growth during extended outages
  * Drops queue cleanly on disconnect rather than holding forever
  * Safety timeout (120s) prevents infinite retention on broken adapters

Based on the approach proposed in NousResearch#4789 by milkoor, rewritten for
thread-safety and correctness.

Test plan:
  * 5 new unit tests (TestPendingInboundQueue) — all passing
  * E2E test with real asyncio loop + fake WS thread: 10-event burst
    before loop ready → all 10 delivered in order
  * E2E concurrent burst test: 20 events queued, 20 more arrive during
    drainer dispatch → all 40 delivered, no loss, no duplicates
  * All 111 existing feishu tests pass

Related: NousResearch#5499, NousResearch#4789

Co-authored-by: milkoor <milkoor@users.noreply.github.com>
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/feishu Feishu / Lark adapter labels May 1, 2026
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…search#5499) (NousResearch#11372)

Inbound Feishu messages arriving during brief windows when the adapter
loop is unavailable (startup/restart transitions, network-flap reconnect)
were silently dropped with a WARNING log. This matches the symptom in
issue NousResearch#5499 — and users have reported seeing only a subset of their
messages reach the agent.

Fix: queue pending events in a thread-safe list and spawn a single
drainer thread that replays them once the loop becomes ready. Covers
these scenarios:

  * Queue events instead of dropping when loop is None/closed
  * Single drainer handles the full queue (not thread-per-event)
  * Thread-safe with threading.Lock on the queue and schedule flag
  * Handles mid-drain bursts (new events arrive while drainer is working)
  * Handles RuntimeError if loop closes between check and submit
  * Depth cap (1000) prevents unbounded growth during extended outages
  * Drops queue cleanly on disconnect rather than holding forever
  * Safety timeout (120s) prevents infinite retention on broken adapters

Based on the approach proposed in NousResearch#4789 by milkoor, rewritten for
thread-safety and correctness.

Test plan:
  * 5 new unit tests (TestPendingInboundQueue) — all passing
  * E2E test with real asyncio loop + fake WS thread: 10-event burst
    before loop ready → all 10 delivered in order
  * E2E concurrent burst test: 20 events queued, 20 more arrive during
    drainer dispatch → all 40 delivered, no loss, no duplicates
  * All 111 existing feishu tests pass

Related: NousResearch#5499, NousResearch#4789

Co-authored-by: milkoor <milkoor@users.noreply.github.com>
@milkoor milkoor closed this by deleting the head repository May 17, 2026
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…search#5499) (NousResearch#11372)

Inbound Feishu messages arriving during brief windows when the adapter
loop is unavailable (startup/restart transitions, network-flap reconnect)
were silently dropped with a WARNING log. This matches the symptom in
issue NousResearch#5499 — and users have reported seeing only a subset of their
messages reach the agent.

Fix: queue pending events in a thread-safe list and spawn a single
drainer thread that replays them once the loop becomes ready. Covers
these scenarios:

  * Queue events instead of dropping when loop is None/closed
  * Single drainer handles the full queue (not thread-per-event)
  * Thread-safe with threading.Lock on the queue and schedule flag
  * Handles mid-drain bursts (new events arrive while drainer is working)
  * Handles RuntimeError if loop closes between check and submit
  * Depth cap (1000) prevents unbounded growth during extended outages
  * Drops queue cleanly on disconnect rather than holding forever
  * Safety timeout (120s) prevents infinite retention on broken adapters

Based on the approach proposed in NousResearch#4789 by milkoor, rewritten for
thread-safety and correctness.

Test plan:
  * 5 new unit tests (TestPendingInboundQueue) — all passing
  * E2E test with real asyncio loop + fake WS thread: 10-event burst
    before loop ready → all 10 delivered in order
  * E2E concurrent burst test: 20 events queued, 20 more arrive during
    drainer dispatch → all 40 delivered, no loss, no duplicates
  * All 111 existing feishu tests pass

Related: NousResearch#5499, NousResearch#4789

Co-authored-by: milkoor <milkoor@users.noreply.github.com>
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…search#5499) (NousResearch#11372)

Inbound Feishu messages arriving during brief windows when the adapter
loop is unavailable (startup/restart transitions, network-flap reconnect)
were silently dropped with a WARNING log. This matches the symptom in
issue NousResearch#5499 — and users have reported seeing only a subset of their
messages reach the agent.

Fix: queue pending events in a thread-safe list and spawn a single
drainer thread that replays them once the loop becomes ready. Covers
these scenarios:

  * Queue events instead of dropping when loop is None/closed
  * Single drainer handles the full queue (not thread-per-event)
  * Thread-safe with threading.Lock on the queue and schedule flag
  * Handles mid-drain bursts (new events arrive while drainer is working)
  * Handles RuntimeError if loop closes between check and submit
  * Depth cap (1000) prevents unbounded growth during extended outages
  * Drops queue cleanly on disconnect rather than holding forever
  * Safety timeout (120s) prevents infinite retention on broken adapters

Based on the approach proposed in NousResearch#4789 by milkoor, rewritten for
thread-safety and correctness.

Test plan:
  * 5 new unit tests (TestPendingInboundQueue) — all passing
  * E2E test with real asyncio loop + fake WS thread: 10-event burst
    before loop ready → all 10 delivered in order
  * E2E concurrent burst test: 20 events queued, 20 more arrive during
    drainer dispatch → all 40 delivered, no loss, no duplicates
  * All 111 existing feishu tests pass

Related: NousResearch#5499, NousResearch#4789

Co-authored-by: milkoor <milkoor@users.noreply.github.com>
prmartinow pushed a commit to prmartinow/hermes-agent that referenced this pull request Aug 26, 2026
…search#5499) (NousResearch#11372)

Inbound Feishu messages arriving during brief windows when the adapter
loop is unavailable (startup/restart transitions, network-flap reconnect)
were silently dropped with a WARNING log. This matches the symptom in
issue NousResearch#5499 — and users have reported seeing only a subset of their
messages reach the agent.

Fix: queue pending events in a thread-safe list and spawn a single
drainer thread that replays them once the loop becomes ready. Covers
these scenarios:

  * Queue events instead of dropping when loop is None/closed
  * Single drainer handles the full queue (not thread-per-event)
  * Thread-safe with threading.Lock on the queue and schedule flag
  * Handles mid-drain bursts (new events arrive while drainer is working)
  * Handles RuntimeError if loop closes between check and submit
  * Depth cap (1000) prevents unbounded growth during extended outages
  * Drops queue cleanly on disconnect rather than holding forever
  * Safety timeout (120s) prevents infinite retention on broken adapters

Based on the approach proposed in NousResearch#4789 by milkoor, rewritten for
thread-safety and correctness.

Test plan:
  * 5 new unit tests (TestPendingInboundQueue) — all passing
  * E2E test with real asyncio loop + fake WS thread: 10-event burst
    before loop ready → all 10 delivered in order
  * E2E concurrent burst test: 20 events queued, 20 more arrive during
    drainer dispatch → all 40 delivered, no loss, no duplicates
  * All 111 existing feishu tests pass

Related: NousResearch#5499, NousResearch#4789

Co-authored-by: milkoor <milkoor@users.noreply.github.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 P2 Medium — degraded but workaround exists platform/feishu Feishu / Lark adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants