diff --git a/gateway/relay/ws_transport.py b/gateway/relay/ws_transport.py index 2f79222a446..f93c3c2cd92 100644 --- a/gateway/relay/ws_transport.py +++ b/gateway/relay/ws_transport.py @@ -120,6 +120,15 @@ def _event_from_wire(raw: Dict[str, Any]) -> MessageEvent: scope_id=src.get("scope_id"), parent_chat_id=src.get("parent_chat_id"), message_id=src.get("message_id"), + # The HERMES profile this event is routed to (multiplex mode). The + # connector stamps it on the wire source when NAS resolves the target + # profile for a Team-Gateway message; absent for a single-profile + # gateway, where it stays None and session keys keep the legacy + # ``agent:main`` namespace (SessionStore._resolve_profile_for_key). + # Consumed by build_session_key's profile namespacing + the per-turn + # config/credential scope — the same field the /p// HTTP + # prefix and per-credential polling adapters already set. + profile=src.get("profile"), # Authentic upstream-trust signal: this event arrived over the # per-instance-authenticated relay WS, so the connector already resolved # it to this instance's owner-bound author. ``platform`` is the diff --git a/tests/gateway/test_relay_upstream_authz.py b/tests/gateway/test_relay_upstream_authz.py index 1fca02b1745..c712331124e 100644 --- a/tests/gateway/test_relay_upstream_authz.py +++ b/tests/gateway/test_relay_upstream_authz.py @@ -243,3 +243,51 @@ def test_event_from_wire_sets_relay_delivery_marker(): ) assert event.source.platform is Platform.DISCORD assert event.source.delivered_via_upstream_relay is True + + +def test_event_from_wire_stamps_routed_profile(): + """A connector-routed profile on the wire source lands on SessionSource. + + In multiplex mode the connector resolves the target HERMES profile for a + Team-Gateway message and stamps ``profile`` on the wire source. The relay + transport must carry it through so build_session_key namespaces the session + and the agent turn resolves that profile's config/credentials. + """ + from gateway.relay.ws_transport import _event_from_wire + + event = _event_from_wire( + { + "text": "hello!", + "source": { + "platform": "discord", + "chat_id": "123", + "chat_type": "dm", + "user_id": "267171776755269633", + "user_name": "rewbs", + "profile": "reviewer", + }, + } + ) + assert event.source.profile == "reviewer" + + +def test_event_from_wire_profile_absent_is_none(): + """No ``profile`` on the wire (single-profile gateway) → None. + + Back-compat: a connector that never sets ``profile`` yields the legacy + behaviour, and session keys stay in the ``agent:main`` namespace. + """ + from gateway.relay.ws_transport import _event_from_wire + + event = _event_from_wire( + { + "text": "hi", + "source": { + "platform": "discord", + "chat_id": "123", + "chat_type": "dm", + "user_id": "1", + }, + } + ) + assert event.source.profile is None