"""Minimal message bus for gateway-style host integration.""" from __future__ import annotations import asyncio from dataclasses import dataclass, field from datetime import datetime, timezone from typing import Any from uuid import uuid4 @dataclass(slots=True) class ChannelIdentity: """Normalized channel routing identity. `channel_id` is the Beaver adapter instance id, not the platform kind. """ channel_id: str kind: str account_id: str peer_id: str thread_id: str | None = None peer_type: str = "unknown" user_id: str | None = None message_id: str | None = None def validation_error(self) -> str | None: if not self.channel_id.strip(): return "channel_id is required" if not self.account_id.strip(): return "account_id is required" if not self.peer_id.strip(): return "peer_id is required" return None def session_id(self) -> str: parts = [self.channel_id, self.account_id, self.peer_id] if self.thread_id: parts.append(self.thread_id) return ":".join(_clean_session_part(part) for part in parts) def dedupe_key(self) -> str | None: if not self.message_id: return None return f"{self.session_id()}:{_clean_session_part(self.message_id)}" def _clean_session_part(value: str) -> str: cleaned = str(value).strip() if not cleaned: return "unknown" return cleaned.replace(":", "_") @dataclass(slots=True) class InboundMessage: """A minimal inbound message accepted by the gateway bridge.""" channel: str content: str content_type: str = "text" channel_identity: ChannelIdentity | None = None session_id: str | None = None user_id: str | None = None title: str | None = None execution_context: str | None = None model: str | None = None provider_name: str | None = None embedding_model: str | None = None message_id: str = field(default_factory=lambda: str(uuid4())) metadata: dict[str, Any] = field(default_factory=dict) timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) @dataclass(slots=True) class OutboundMessage: """A minimal outbound message produced by the gateway bridge.""" channel: str content: str session_id: str | None finish_reason: str content_type: str = "text" channel_identity: ChannelIdentity | None = None message_id: str = field(default_factory=lambda: str(uuid4())) run_id: str | None = None provider_name: str | None = None model: str | None = None usage: dict[str, Any] = field(default_factory=dict) metadata: dict[str, Any] = field(default_factory=dict) timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) class MessageBus: """Minimal async message bus with inbound/outbound queues.""" def __init__(self) -> None: self.inbound: asyncio.Queue[InboundMessage] = asyncio.Queue() self.outbound: asyncio.Queue[OutboundMessage] = asyncio.Queue() async def publish_inbound(self, message: InboundMessage) -> None: await self.inbound.put(message) async def consume_inbound(self) -> InboundMessage: return await self.inbound.get() async def publish_outbound(self, message: OutboundMessage) -> None: await self.outbound.put(message) async def consume_outbound(self) -> OutboundMessage: return await self.outbound.get() @property def inbound_size(self) -> int: return self.inbound.qsize() @property def outbound_size(self) -> int: return self.outbound.qsize()