fix(bluebubbles): skip webhook server bind in standalone send path (salvage of #12439 by @Wonham) - #51763
fix(bluebubbles): skip webhook server bind in standalone send path (salvage of #12439 by @Wonham)#51763Bartok9 wants to merge 3 commits into
Conversation
Salvage of #12439 by @Wonham, re-targeted onto current |
f91589c to
5e8f955
Compare
5e8f955 to
0a744e5
Compare
|
I think this still needs one more lifecycle guard before merge. This PR skips the local aiohttp bind/register path for That means a standalone send-only adapter can still clean up gateway-owned state: it uses the same BlueBubbles config/webhook URL as the gateway adapter, so I hit the original Suggested shape:
That keeps outbound REST sends separate from the gateway-owned inbound webhook lifecycle. |
|
Good catch — fixed in e073b6958c. You're right that the standalone sender shared the gateway's webhook lifecycle. Two changes:
Added two regression tests:
|
|
Plate-clear 2026-07-11: |
d68ce67 to
ea64957
Compare
|
@whyy9527 reconfirming after rebase onto current main. Lifecycle guard remains in place:
Regression coverage:
tests/gateway/test_bluebubbles.py: 63 passed on |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for preserving the adapter-level design and for incorporating the lifecycle feedback from the discussion. The standalone-send premise is confirmed on current main: tools/send_message_tool.py:1805-1815 creates a separate adapter and invokes default connect(), while gateway/platforms/bluebubbles.py:281-295 binds and registers the inbound webhook. The PR keeps the default gateway path intact while introducing the narrowly scoped outbound-only path.
Problems
- The changed call site at
tools/send_message_tool.py:1806is not directly covered. The new adapter tests validateconnect(send_only=True), but no test invokes_send_bluebubbles()and asserts that the tool layer supplies the flag.
Suggested changes
- Add a focused
tests/tools/regression test using a fake adapter to assert_send_bluebubbles()callsconnect(send_only=True), then sends and disconnects. The existing adapter tests should remain the lifecycle contract coverage.
All required PR CI checks passed. This is an automated hermes-sweeper review.
| @@ -1803,7 +1803,7 @@ async def _send_bluebubbles(extra, chat_id, message): | |||
| from gateway.config import PlatformConfig | |||
| pconfig = PlatformConfig(extra=extra) | |||
| adapter = BlueBubblesAdapter(pconfig) | |||
| connected = await adapter.connect() | |||
| connected = await adapter.connect(send_only=True) | |||
There was a problem hiding this comment.
Please add a tool-level regression test that invokes _send_bluebubbles() with a fake adapter and asserts this call receives send_only=True. The new adapter tests cover the mode itself, but not the delivery-path wiring changed here.
GottZ
left a comment
There was a problem hiding this comment.
This was generated by AI during triage.
Summary
Two PRs address the same BlueBubbles standalone-send failure: #12439 introduces an outbound-only connect path to avoid binding the gateway-owned webhook port, while #51763 salvages that design on current main and additionally prevents send-only disconnects from unregistering the gateway webhook or changing gateway runtime status. #51763 also adds adapter-level lifecycle regression coverage, but the changed tool-layer call site remains untested.
Related pull requests
- #12439 [closed]
duplicate— (+10/-3) — superseded by #51763: This closed PR identified the root cause and introducedconnect(send_only=True)in the standalone sender, but its version marks the send-only adapter connected and lacks the later disconnect ownership guards, so it remains relevant as the original implementation and attribution source rather than the merge candidate. - #51763
related— (+128/-8) — keep open pending one focused regression test: The diff avoids the outbound-only webhook bind and correctly gates disconnect cleanup on_runner, preventing deletion of gateway-owned webhook and status state. Consistent with the keep_open review on #51763, add a tool-layer test proving_send_bluebubbles()callsconnect(send_only=True), then sends and disconnects; the current tests cover only the adapter contract.
Duplicates
#12439 and #51763 implement substantially the same connect(send_only=True) fix; #51763 is the current-main salvage with the necessary lifecycle guards and expanded regression coverage.
Suggested consolidation
Keep #51763 open until the focused _send_bluebubbles() wiring regression requested by the keep_open review is added; then merge #51763 as the complete current-main implementation. #12439 is already closed and should remain closed as superseded by #51763, with its original authorship preserved through the documented co-author attribution.
Complex graph
flowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
subgraph Dup12439 ["PRs duplicating each other"]
P12439["PR #12439 (closed)"]
P51763["PR #51763 (open)"]
end
class P12439 closed
class P51763 open
class P51763 target
click P12439 "https://github.com/NousResearch/hermes-agent/pull/12439"
click P51763 "https://github.com/NousResearch/hermes-agent/pull/51763"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed or no verify verdict yet (state tag in the node label).
Cross-PR triage: Reviewed 2 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 10 kB of PR diffs, 4 kB of issue/PR text, 7 kB of discussion (11 comments), 1 verify verdict. verdicts reflect diff content, not PR titles. Part of an automated triage batch.
…alvage of NousResearch#12439 by @Wonham) Rebuilt on latest main (Bartok9 hygiene 2026-08-01). Original: NousResearch#51763
|
Rebuilt onto latest — Bartok9 public PR hygiene 2026-08-01 |
ea64957 to
cf0ca28
Compare
…tok9 Per-PR attribution so check-attribution passes on this branch (Teknium).
GottZ
left a comment
There was a problem hiding this comment.
This was generated by AI during triage.
Delta since our previous triage comment
@Bartok9 rebuilt #51763 onto the latest main via patch re-apply and force-pushed a new head. The refreshed diff preserves the lifecycle fix, but it still does not add the focused tool-layer regression test requested in the visible keep_open review; no new CI result is included in the delta.
Changed pull requests
- #51763
related— (+29/-8) — keep open: The rebuilt diff still passes send_only=True from _send_bluebubbles() and gates disconnect cleanup on runner ownership, but despite the keep_open review on #51763, it still lacks a direct test proving that the tool call site connects with that flag, sends, and disconnects.
Suggested consolidation
Recommendation unchanged: keep #51763 open pending the requested tool-layer regression test and fresh CI on the force-pushed head.
Complex graph unchanged since our previous triage comment.
Cross-PR triage: Reviewed 2 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 6 kB of PR diffs, 4 kB of issue/PR text, 7 kB of discussion (12 comments), 0 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.
|
Added the focused tool-layer regression requested in the keep_open review.
Local: |
Add focused tool-layer regression asserting connect(send_only=True), send, and disconnect for the standalone BlueBubbles path (NousResearch#51763).
Summary
connect(send_only=True)to the BlueBubbles adapter so the standalonesend_messagepath no longer tries to bind the local webhook server.send_messagetool) stop failing withOSError(EADDRINUSE)when the gateway is already running and holding the webhook port.Motivation
Salvage of #12439 by @Wonham, re-targeted onto current
main.tools/send_message_tool._send_bluebubbles()builds its ownBlueBubblesAdapterand callsadapter.connect(), which unconditionally starts an aiohttpAppRunner/TCPSiteonself.webhook_port. When the gateway process already owns that port, the bind raisesEADDRINUSEand the send aborts — even though an outbound-only caller never needs to receive inbound webhooks.Fix
connect(*, send_only: bool = False): after the REST ping/server-info handshake,send_only=Truecalls_mark_connected()and returns without starting the webhook server or registering it.send_only=True. Gateway startup keeps the default (send_only=False) and still binds + registers exactly as before.Verification
.venv/bin/python -m pytest tests/gateway/test_bluebubbles.py— 58 passedReal behavior proof
.venv(CPython 3.11).test_connect_send_only_skips_webhook_bind(stubsAppRunner/TCPSite/_register_webhookand asserts none run) andtest_connect_default_still_binds_webhook(asserts the default path still binds + registers).EADDRINUSEcollision against a running gateway (the bind path is stubbed; the test asserts the code path is skipped).Salvage credit: original fix by @Wonham (#12439).