//! SessionStream: uni-directional sink trait. Archive backends use //! ArchiveBackend; live-write sinks like Langfuse use SessionStream. use async_trait::async_trait; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use thiserror::Error; use uuid::Uuid; use super::bus_event::BusEvent; #[async_trait] pub trait SessionStream: Send + Sync { fn summary(&self) -> StreamSummary; fn scope(&self) -> StreamScope; async fn on_event(&self, event: &BusEvent) -> StreamOutcome; async fn shutdown(&self); } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StreamSummary { pub name: String, pub kind: StreamKind, pub target: String, // human-readable ("langfuse: https://…", "matrix: #room:server") pub active_since: DateTime, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum StreamKind { Matrix, Langfuse, Slack, Webhook, Custom, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case", tag = "kind")] pub enum StreamScope { Session { scroll_id: Uuid }, Connector { connector_uid: Uuid }, ArchiveWide { acknowledged: bool }, } #[derive(Debug)] pub enum StreamOutcome { Ok, Skipped, Failed(StreamError), } #[derive(Debug, Error)] pub enum StreamError { #[error("transport: {0}")] Transport(String), #[error("serialisation: {0}")] Serialisation(String), #[error("rejected: {0}")] Rejected(String), #[error("shutdown")] Shutdown, }