fix(hermes-adapter): widen _ACTIVE_CLIENTS key with owner_id to prevent intra-process bridge fight - #2292
Conversation
…nt intra-process bridge fight
When the Hermes gateway runs multiple concurrent sessions (email threads,
cron, subagents) in one process, each gets its own MemTensorProvider
instance. Every MemosBridgeClient() construction closes whatever client
holds the per-process (agent, no_viewer, runtime_home) singleton slot in
_ACTIVE_CLIENTS — which may belong to a DIFFERENT sessions provider.
This causes a mutual kill loop: session A creates a bridge, session B
creates a bridge and closes As bridge, session As keepalive respawns
and closes Bs bridge. Signature: gateway holds 2+ bridge.mjs children,
PIDs churn every 2-3s while the dashboards single bridge is rock-stable.
Fix: widen the singleton key to (agent, no_viewer, runtime_home, owner_id)
so concurrent provider instances in one process coexist. Each construction
site passes owner_id=f"provider-{id(self)}", giving every provider
instance a stable lifetime-scoped identity.
Related: MemTensor#1910 (original bridge-process leak guard), MemTensor#1927 (initialize
leak), MemTensor#1985 (HTTP transport — the architectural end-state)
🤖 Open Code ReviewTarget: PR #2292 🔍 OpenCodeReview found 1 issue(s) in this PR. 1.
|
|
|
|
Fix pushed. The mangled |
|
cc @Wang-Daoji — the earlier syntax issue from the initial patch was a regex overmatch that inserted duplicate parameter lines into
Both files compile. This is the last remaining patch we carry locally — merging it would let us drop our fork. |
|
Thanks for the automated testing. Status check on both results: 1. The earlier syntax error is fixed. I've verified 2. The latest ENV ISSUE is runner-side, not PR-side. The failure is Could the automated test be re-run against the current head? The branch is ready for review — the diff is the intended 10-line owner-keying change plus the signature repair. |
Description
When the Hermes gateway runs multiple concurrent sessions (email threads, cron jobs, subagents) in one process, each session gets its own
MemTensorProviderinstance. EveryMemosBridgeClient()construction closes whatever client holds the per-process(agent, no_viewer, runtime_home)singleton slot in_ACTIVE_CLIENTS— which belongs to a different session's provider.Root cause
The module-level singleton tracker
_ACTIVE_CLIENTSinbridge_client.pywas keyed by(agent, no_viewer, runtime_home)— one slot per process. This was correct as a guard against issue #1910 (bridge process leak where one provider kept spawning new bridges per turn), but fatal when N provider instances coexist in one gateway process.Evidence chain:
bridge.mjs --no-viewerchildren, replacing one every 2-3s (PIDs churn constantly)bridge.cjs --daemon, systemd, :18800) stable throughoutFix
Widen the singleton key to
(agent, no_viewer, runtime_home, owner_id)so concurrent provider instances in one process coexist instead of fighting.Changes:
tuple[str, bool, str]->tuple[str, bool, str, str];owner_id: str | Noneparameter added to__init__;_singleton_ownerfield set fromowner_id or f"anon-{id(self)}"; key construction in both_register_activeand_unregister_activeupdatedinitialize()and_reconnect_bridge()) in both shared-bridge and legacy modes passowner_id=f"provider-{id(self)}"Expected steady state after fix: daemon + 1 bridge per host process = 3 total; reconnect counts -> ~0.
Alternatives
Type of change
How Has This Been Tested?
bridge client is closedstorms since deployment (was thousands/day)Checklist