use std::collections::HashMap; use matrix_sdk::{ authentication::matrix::MatrixSession, ruma::{OwnedEventId, OwnedRoomId, OwnedUserId}, }; use serde::{Deserialize, Serialize}; /// One prior version of an edited message. #[derive(Debug, Clone, Serialize)] pub struct EditRecord { pub body: String, pub ts: i64, pub ts_human: String, } /// Serializable shape for one timeline event, used both in matrix_turn JSON /// (input to the shard) and tool response JSON (get_room_history, /// fetch_event). #[derive(Debug, Serialize)] #[serde(tag = "kind", rename_all = "lowercase")] pub enum WireEvent { Message { event_id: String, event_id_short: String, sender: String, is_self: bool, ts: i64, ts_human: String, body: String, in_reply_to: Option, read_by: Vec, /// Prior versions of this message, oldest first. Empty when the /// message has never been edited. edit_history: Vec, }, Reaction { sender: String, is_self: bool, ts: i64, ts_human: String, target_event_id: String, target_event_id_short: String, key: String, }, Edit { sender: String, is_self: bool, ts: i64, ts_human: String, target_event_id: String, target_event_id_short: String, old_body: String, new_body: String, }, /// Synthetic event from the daemon (not a Matrix event). Currently used /// to tell the shard "you were rate-limited; events held for X seconds." Notice { text: String, ts: i64, ts_human: String, }, } #[derive(Debug, Serialize)] pub struct RoomInfo { pub room_id: String, pub name: String, } #[derive(Debug, Serialize)] pub struct MemberInfo { pub user_id: String, pub display_name: String, } #[derive(Debug, Serialize)] pub struct FetchEventResult { pub event: Option, pub context_before: Vec, pub earlier_handle: Option, } pub const DEFAULT_MODEL: &str = "claude-sonnet-4-6"; pub const DEFAULT_MAX_HISTORY: usize = 20; pub const DEFAULT_RATE_LIMIT_PER_MIN: u32 = 1; pub const DEFAULT_RATE_BURST_CAPACITY: u32 = 3; pub const DEFAULT_SESSION_IDLE_MINUTES: u64 = 10; pub const DEFAULT_SESSION_MAX_EVENTS: u32 = 100; #[derive(Debug, Deserialize)] pub struct Config { pub homeserver: String, pub username: String, pub password: String, pub rate_limit_per_min: Option, pub rate_burst_capacity: Option, pub model: Option, pub max_history: Option, pub session_idle_minutes: Option, pub session_max_events: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct PersistedSession { pub homeserver: String, pub db_path: std::path::PathBuf, pub user_session: MatrixSession, #[serde(skip_serializing_if = "Option::is_none")] pub sync_token: Option, } #[derive(Clone, Debug)] pub enum TimelineItem { Message { event_id: OwnedEventId, sender: OwnedUserId, /// Latest version of the message body. If the user edited this message, /// this is the most recent edit's content. body: String, is_self: bool, /// Unix seconds. 0 if unknown. ts: i64, in_reply_to: Option, /// Prior versions of this message (oldest first), excluding the /// current `body`. Empty when the message has never been edited. edit_history: Vec, }, Reaction { sender: OwnedUserId, target_event_id: OwnedEventId, key: String, is_self: bool, ts: i64, }, /// A single edit applied to a message. Surfaced as its own chronological /// event so the shard sees edits as they happen. The target message also /// has its `edit_history` updated for at-a-glance reference. Edit { sender: OwnedUserId, target_event_id: OwnedEventId, old_body: String, new_body: String, is_self: bool, ts: i64, }, } impl TimelineItem { pub fn ts(&self) -> i64 { match self { Self::Message { ts, .. } | Self::Reaction { ts, .. } | Self::Edit { ts, .. } => *ts, } } pub fn event_id(&self) -> Option<&OwnedEventId> { match self { Self::Message { event_id, .. } => Some(event_id), Self::Reaction { .. } | Self::Edit { .. } => None, } } pub fn sender(&self) -> &OwnedUserId { match self { Self::Message { sender, .. } | Self::Reaction { sender, .. } | Self::Edit { sender, .. } => sender, } } pub fn is_self(&self) -> bool { match self { Self::Message { is_self, .. } | Self::Reaction { is_self, .. } | Self::Edit { is_self, .. } => *is_self, } } } pub struct DaemonState { pub own_user_id: OwnedUserId, /// Per-room: the latest event_id that's been "shown" to Claude. Events /// after this are "new" on the next invocation. Cleared on daemon restart. pub last_shown: HashMap, /// Rooms with unprocessed events. The Instant is when the room first /// entered the queue (or last became empty, then refilled). Used to /// surface rate-limit delays to the shard via a synthetic notice event. pub pending_rooms: Vec<(OwnedRoomId, std::time::Instant)>, pub rate_budget: f64, pub rate_limit_per_min: u32, pub rate_burst_capacity: u32, pub last_rate_check: std::time::Instant, pub model: String, pub max_history: usize, pub session_idle_minutes: u64, pub session_max_events: u32, }