Repository navigation
Hermes: one bridge per gateway, shared by its chats - #154
Open
zaydiscold wants to merge 1 commit into
Open
zaydiscold wants to merge 1 commit into
zaydiscold wants to merge 1 commit into
Conversation
A Hermes gateway serving many chats ran one `reason-network bridge` per conversation: a process and a daemon connection each. They now share one. - The daemon lets a Hermes connection carry many sessions: registering one no longer ends the others (`agent::shares_connection`), and a reconnect registers every open one again. - `end_session` ends one conversation on a shared connection. Only the connection that registered it can end it, and a reconnect leaves it out. - An oversized or non-UTF-8 frame fails that call (id 0) instead of ending the bridge, since the bridge now carries every chat. The plugin refuses such a frame before sending it. - The plugin keeps one bridge, routes deliveries by key, ends a closed conversation with `end_session`, and registers each conversation again on a new bridge if the old one stops. Tests: three chats on one connection with only the registering connection able to end one (daemon), reconnect memory keeps every Hermes chat (client), and e2e for the shared adapter, a restarted adapter, an ended conversation staying gone after a daemon restart, held mail arriving when a conversation comes back, and an oversized frame failing only its call.
Member
|
Thanks, and for rebasing hermes-agent#143310. Reviewing now, including the full suite and the Hermes e2e against #143310. I will report back here. |
Member
|
Sorry for the churn: main moved quite a bit in the last two days (v0.12.14 to v0.12.17, with changes in |
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.
Follow-up to #119, answering the question there: one bridge per gateway works, so the plugin now runs one per Hermes process and shares it across its conversations.
Why
I run two gateways. One serves a single long Telegram chat; the other has several desktop tabs open plus delegated subagents, and today it started dozens of conversations, many within the same minute. Each one started its own
reason-network bridgeand daemon connection, and a conversation that was never finalized kept both. For the single-chat gateway nothing changes: it's one bridge either way.What changes
agent::shares_connection()(the multiplexed MCP agents, plushermes) replacesmultiplexed()in the two places that assume one conversation per connection:registerno longer ends the other Hermes sessions on that connection (moved_on), and the client's reconnect memory keeps all of them, so a daemon restart registers every open chat again.multiplexed()itself is unchanged, so Hermes's MCP-server path behaves as before.end_session. A new request ends one conversation on a shared connection. Only the connection that registered it can end it, and the client forgets it so a reconnect doesn't bring it back. The bridge drops that conversation'sreceivedset at the same time, so a long-running gateway doesn't keep every old chat's ids.BridgeonNetwork, with deliveries routed by key.finalizesendsend_sessioninstead of closing a process. If the bridge stops, the next turn or tool call starts a new one and that conversation registers again as itself. The per-conversation locks are as before, and nothing holds_lockacross I/O.Tradeoffs
Tests
cargo test255 + 2, clippy, fmt.hermes.py. 25/25, against both hermes-agent#143310 as installed and #143310 rebased onto current main. New: conversations share one adapter; a killed adapter is replaced and both conversations register again; an ended conversation stays gone afterreason-network stop; mail held for a conversation reaches it when it comes back; an oversized frame fails only its call. Updated: the three tests that asserted a closed process now assert the conversation left the directory, and the stand-in engine answers concurrently, as the real bridge does.hooks.py74/74,reconnect.py41/41,names.py32/32.mixed_versions.pypasses 21/23, and the same 2 fail on main when the "old" binary is 0.12.13, which can already readrn1links.registerinstead of by stored sessions: the daemon's push arrived after the plugin stored the session either way, so I kept the map as a guard and don't claim a race fix for it.I haven't run it on native Windows yet; #119's Windows checks passed, and nothing here is platform-specific beyond the existing pipe transport.
One thing I hit while testing on my own machines: a plugin from 0.12.13 with an engine from 0.12.12 or earlier (no
bridgesubcommand) logs only "Reason adapter connection closed" on every turn. Runningreason updateand thenreason network setupfixed it on both. A clearer message for that case could be a small separate PR if you'd like one.