3c7069bdcb
authz_mixin, browser_control_broker, delivery, delivery_ledger, display_config, drain_control, hosted_room_links/peer/policy_checkpoint, hosted_rooms, platform_registry, relay/__init__, relay/ws_transport, run.py and slash_commands.py (comments), session_context, session_state, streaming_tts_consumer, turn_lease and small modules. - HostedRoomPolicyCheckpoint._apply_event -> per-kind handler table - WebSocketRelayTransport._handle_frame -> frame-handler table - GatewayAuthorizationMixin: unified adapter setting/flag/extra readers - dead symbols removed (verified zero references): RoomLinkProbe/select_room_link, relay_bot_username, is_restart_loop_tripped, debug_rows, DeadTargetRegistry.all_dead, BrowserControlBroker.detach_owner/_prune_tickets, StreamingTTSConsumer.started/_enqueue_done/ _iter_stream_chunks/_next_stream_chunk, RecoverableHandleCache.status_for, _auth_env, _copy_default_catalog, _parse_timestamp_prefix, _present_* helpers, _send_result_error_kind, _truthy_env, SessionFieldView/TurnLeaseTokenView dunder shims, and their orphaned tests. - lost WHY/invariant text from the earlier compaction restored compactly (541 hunks audited)
115 lines
5.1 KiB
Python
115 lines
5.1 KiB
Python
"""Relay transport protocol — the gateway<->connector wire contract. EXPERIMENTAL.
|
|
|
|
The ``RelayAdapter`` (gateway side) delegates all wire I/O to a ``RelayTransport``.
|
|
The gateway dials OUT to the connector, so a production transport is a WebSocket
|
|
client (``ws_transport.py``); in tests it is an in-memory stub
|
|
(``tests/gateway/relay/stub_connector.py``). This module defines the protocol
|
|
surface only: lifecycle (connect/disconnect), handshake (the advertised
|
|
``CapabilityDescriptor``), inbound (``set_inbound_handler``) and outbound
|
|
(``send_outbound`` / ``get_chat_info`` / ``send_interrupt``).
|
|
|
|
EXPERIMENTAL: may change without a deprecation cycle until >=2 Class-1 platforms
|
|
validate it. See docs/relay-connector-contract.md.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Awaitable, Callable, Dict, Optional, Protocol, runtime_checkable
|
|
|
|
from gateway.platforms.base import MessageEvent
|
|
from gateway.relay.descriptor import CapabilityDescriptor
|
|
|
|
# Callback the transport invokes for each inbound normalized event.
|
|
InboundHandler = Callable[[MessageEvent], Awaitable[None]]
|
|
|
|
# Callback for each forwarded passthrough request (§5.1): ``(forward, buffer_id)``.
|
|
# ``forward`` is a ws_transport.PassthroughForward, typed Any here because
|
|
# ws_transport imports FROM this module; ``buffer_id`` (§5.3 buffered flip) is
|
|
# acked by the handler after durable handoff.
|
|
PassthroughHandler = Callable[[Any, Optional[str]], Awaitable[None]]
|
|
|
|
|
|
@runtime_checkable
|
|
class RelayTransport(Protocol):
|
|
"""Full gateway<->connector transport contract."""
|
|
|
|
async def connect(self) -> bool:
|
|
"""Open the connection to the connector; return True on success."""
|
|
...
|
|
|
|
async def disconnect(self) -> None:
|
|
"""Close the connection."""
|
|
...
|
|
|
|
async def handshake(self) -> CapabilityDescriptor:
|
|
"""Return the capability descriptor the connector advertises."""
|
|
...
|
|
|
|
def set_inbound_handler(self, handler: InboundHandler) -> None:
|
|
"""Register the callback invoked with each inbound MessageEvent."""
|
|
...
|
|
|
|
def set_passthrough_handler(self, handler: "PassthroughHandler") -> None:
|
|
"""Register the callback for each forwarded passthrough request (§5.1).
|
|
|
|
The connector answers the provider's edge ACK itself, then forwards the
|
|
real request over this same outbound socket (a hosted gateway has no
|
|
public inbound port). Optional on a transport (a stub may not implement it).
|
|
"""
|
|
...
|
|
|
|
async def send_outbound(
|
|
self, action: Dict[str, Any], *, platform: Optional[str] = None
|
|
) -> Dict[str, Any]:
|
|
"""Carry an outbound action (send/edit/typing) to the connector.
|
|
|
|
Returns a result dict; for ``op == "send"`` it carries ``success`` and
|
|
optionally ``message_id`` / ``error``. ``platform`` tags WHICH fronted
|
|
platform this reply targets (the transport resolves the matching botId);
|
|
omitted ⇒ the connector uses the session's default platform.
|
|
"""
|
|
...
|
|
|
|
async def get_chat_info(self, chat_id: str) -> Dict[str, Any]:
|
|
"""Proxy a chat-info lookup to the connector."""
|
|
...
|
|
|
|
async def send_interrupt(self, session_key: str, reason: Optional[str] = None) -> None:
|
|
"""Route a mid-turn /stop to the connector for ``session_key``.
|
|
|
|
The connector forwards it down the socket owned by the gateway instance
|
|
running that session. This is the OUTBOUND direction; the actual task
|
|
cancellation happens when the connector echoes an interrupt inbound.
|
|
"""
|
|
...
|
|
|
|
async def go_idle(self, timeout_s: float = 10.0) -> bool:
|
|
"""Ask the connector to flip this instance to buffered-only (§5.3).
|
|
|
|
Sends ``going_idle`` and awaits the connector-authoritative
|
|
``going_idle_ack`` (live delivery stopped; inbound now buffers for replay
|
|
on reconnect). Returns True on ack, False on timeout / not-connected —
|
|
the caller closes regardless. Optional on a transport. Emitted as part of
|
|
the gateway's EXISTING drain transition -- not a new idle path.
|
|
"""
|
|
...
|
|
|
|
async def send_follow_up(
|
|
self, action: Dict[str, Any], *, platform: Optional[str] = None
|
|
) -> Dict[str, Any]:
|
|
"""Act on a shared-identity capability bound to a session (A2 outbound).
|
|
|
|
Some platforms hand the connector a credential acting on the SHARED bot
|
|
identity (e.g. a Discord interaction follow-up token). Under A2 it NEVER
|
|
reaches the gateway: the connector binds it in its capability vault keyed
|
|
by session, and the gateway issues a SEMANTIC action against that session.
|
|
|
|
Action dict: ``op == "follow_up"``, ``session_key``, ``kind`` (e.g.
|
|
``"discord.interaction_token"``), ``content``, optional ``metadata``.
|
|
The connector resolves the capability, enforces the tenant match, and
|
|
egresses. Returns ``{success, message_id?, error?}``; ``success`` is False
|
|
when the capability is absent/expired or the tenant mismatches — nothing to
|
|
retry with (by design: a leaked gateway holds zero capability material).
|
|
"""
|
|
...
|